tinker R is based on RK3399PRO chip. The uboot part of Android SDK released by tinker R is based on the official uboot source code modified by RK. This article firstly introduces the compilation method of uboot, then introduces how to burn the modified uboot image to the device, and finally introduces the modification method of Rockchip uboot, for users who don't need to modify uboot, they only need to read the compilation and burning method. For a more in-depth understanding, you can refer to the documentation under SDK at the path RKDocs/common/u-boot.
Before reading the following, you should prepare the following
Assume that the entire SDK has been pulled down via wiki or netdisk and is currently in the SDK installation directory. First go to uboot's directory
cd u-boot
Under u-boot, execute
# The corresponding config file is tinker_edge_r_defconfig
. /make.sh tinker_edge_r
or
# The corresponding config file is rk3399pro_defconfig
. /make.sh rk3399pro
The only difference between the two is that the former uses a VID PID of 0x0b05 0x7820 and the latter uses a VID PID of 0x2207 0x330a in the ums mode later on.
After successful compilation, the following message will be printed
The compiled file will be
u-boot/uboot.img
u-boot/trust.img
rk3399pro_loader_v1.24.119.bin
note: Do not change the directory relationship between u-boot, rkbin and prebuilts in the SDK, otherwise the compilation will fail
There are two download modes for RK's platform, loader mode and maskrom mode. For tinkerR, it also supports UMS mode. This mode is similar to the loader mode, which is to download in uboot. maskrom mode is to enter the forced download mode when the EMMC is empty or to force the EMMC data pin to ground (on tinkerR it is shorted to the recovery header), similar to the 9008 mode on Qualcomm platform.
It is recommended to burn the system under windows system. The burning tool is
RKTools/windows/AndroidTool/AndroidTool_Release_v2.65.zip
Copy this tool to windows system and unzip it, there is an AndroidTool.exe executable program inside.Open the Rockchip burning tool.
Connect the tinkerR to the computer with a typec cable, short it to the position shown in the picture below, and then power it on, at this time tinkerR enters MASKROM mode
note: you have to short the recovery header first, then power it on, after that remove the jumper cap on top of the recovery header
In the case that there are already other parts of the firmware on the board, use AndroidTool.exe to burn the separately compiled uboot image according to the following steps
RK3399PRO's bootloader is divided into two parts, the first part is the loader firmware, including ddr.bin and miniloader.bin, etc., no source code, only through the configuration file to modify its configuration, the second part uboot based on the open source U-Boot 2017.09 version for customization, this part is open source.
The following describes the first level loader and the packaging process of trust, all the processes are reflected in the make.sh script
This function finds the chip type by corresponding to the .config file and assigns this type to the RKCHIP_LOADER RKCHIP_TRUST variable
The target loader firmware is then generated via the boot_merge command with the configuration file ${RKCHIP_LOADER}MINIALL.ini
Therefore, if you want to modify the packaged target file, you can modify this ini file.
To make functional changes to uboot, the debug serial port must be enabled. There is no default debug serial port on the board, because the default debug serial port and SDMMC are multiplexed on the RK3399PRO, and the board has already used it as SDMMC, so you have to choose another serial port as the debug serial port.
The default serial port 0 in the SDK of RK is generally used as Bluetooth communication, like AP6212 such module, which uses serial port for Bluetooth. However, on top of tinkerR, Bluetooth goes USB, so serial port 0 has no other default function to occupy, so choose serial port 0 as the debug serial port
There is also a uart4 on the 40pin header, if you want to select uart4, modify the method similar to
To modify the loader part, you can only do so by changing its configuration file and then generating a copy through rk's tools. The specific steps are as follows
- Go to the rkbin directory, find the ddr.bin currently in use, and make a copy of it.
In the SDK root directory, execute
cd rkbin/bin/rk33
cp rk3399pro_ddr_933MHz_v1.24.bin rk3399pro_ddr_933MHz_v1.24_uart0.bin
- Write the configuration file
For example, the configuration file name ddrbin_param_uart0.txt with the following content
Store this file under rkbin/tools
- Generate ddr firmware
Execute in the SDK root directory
cd rkbin/tools
. /ddrbin_tool ddrbin_param_uart0.txt . /bin/rk33/rk3399pro_ddr_933MHz_v1.24_uart0.bin
This will generate a ddr firmware that uses uart0 to output debug information by default.
- Modify the loader packing configuration.
As described in the previous section, the configuration file used by RK3399PRO is rkbin/RKBOOT/RK3399PROMINIALL.ini
- Compile and verify
Under uboot, execute . /make.sh rk3399pro to output the new loader firmware, its file name is rk3399pro_loader_v1.24.119.bin
After burning as described above, the debug information of the loader part will be printed out
You can see the compile time printed in the uboot section, if it is the same as the actual compile time, it means that the uboot update was successful
The boot of Rockchip uboot also starts by initializing some clocks and peripherals, and this part usually does not need to be modified. After that, if you do not enter command line mode with ctrl+c, the default bootcmd will be executed.
If the configuration of the debug serial port is modified as above, the debug serial port is available at this time, and you can enter the command mode of uboot by pressing ctrl+c when entering uboot.
You can check its default boot command by using the following command
The default boot command is boot_android, if it fails, bootrkp will be executed, and after it fails, distro_bootcmd command will be used. Now the rk platform, no matter android or linux, uses the android format boot.img, so eventually the boot_android command takes effect
The implementation of boot_android is under u-boot/cmd/boot_android.c, you can see that it calls do_boot_android
static int do_boot_android(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[])
{
unsigned long load_address;
int ret = CMD_RET_SUCCESS;
char *addr_arg_endp, *addr_str;
struct blk_desc *dev_desc;
...
if (argc >= 5) {
...
} else {
addr_str = env_get("kernel_addr_r");
...
}
...
dev_desc = blk_get_dev(argv[1], simple_strtoul(argv[2], NULL, 16));
if (!dev_desc) {
printf("Could not get %s %s\n", argv[1], argv[2]);
return CMD_RET_FAILURE;
}
ret = android_bootloader_boot_flow(dev_desc, load_address);
if (ret < 0) {
printf("Android boot failed, error %d.\n", ret);
return CMD_RET_FAILURE;
}
return CMD_RET_SUCCESS;
}
U_BOOT_CMD(
boot_android, 5, 0, do_boot_android,
"..." ,
"..."
);
This function first gets the address specified by kernel_addr_r as the kernel image load address, then gets the memory information in the first and second arguments and assigns it to dev_desc, and finally calls android_bootloader_boot_flow
The implementation of android_bootloader_boot_flow is in u-boot/common/android_bootloader.c
int android_bootloader_boot_flow(struct blk_desc *dev_desc,
unsigned long load_address)
{
enum android_boot_mode mode = ANDROID_BOOT_MODE_NORMAL;
disk_partition_t misc_part_info;
int part_num;
int ret;
char *command_line;
char slot_suffix[3] = {0};
const char *mode_cmdline = NULL;
char *boot_partname = ANDROID_PARTITION_BOOT;
ulong fdt_addr;
/*
* 1. Load MISC partition and determine the boot mode
* clear its value for the next boot if needed.
*/
part_num = part_get_info_by_name(dev_desc, ANDROID_PARTITION_MISC,
&misc_part_info);
if (part_num < 0) {
printf("Could not find misc partition\n");
} else {
mode = android_bootloader_load_and_clear_mode(dev_desc,
&misc_part_info);
#ifdef CONFIG_RKIMG_BOOTLOADER
if (mode == ANDROID_BOOT_MODE_NORMAL) {
if (rockchip_get_boot_mode() == BOOT_MODE_RECOVERY)
mode = ANDROID_BOOT_MODE_RECOVERY;
}
#endif
}
...
switch (mode) {
case ANDROID_BOOT_MODE_NORMAL:
/* In normal mode, we load the kernel from "boot" but append
* "skip_initramfs" to the cmdline to make it ignore the
* recovery initramfs in the boot partition.
*/
...
break;
case ANDROID_BOOT_MODE_RECOVERY:
/* In recovery mode we still boot the kernel from "boot" but
* don't skip the initramfs so it boots to recovery.
*/
boot_partname = ANDROID_PARTITION_RECOVERY;
break;
...
}
...
if (load_android_image(dev_desc, boot_partname,
slot_suffix, &load_address)) {
printf("Android image load failed\n");
return -1;
}
...
/* Assemble the command line */
command_line = android_assemble_cmdline(slot_suffix, mode_cmdline);
env_update("bootargs", command_line);
debug("ANDROID: bootargs: \"%s\"\n", command_line);
...
ret = android_image_get_fdt((void *)load_address, &fdt_addr);
...
android_bootloader_boot_kernel(load_address);
/* TODO: If the kernel doesn't boot mark the selected slot as bad. */
return -1;
}
The approximate flow of this function is to first find the boot command inside the misc partition by part_get_info_by_name and decide whether to boot normally or enter recovery mode, corresponding to the uboot inside is this print
If it is a normal boot, the image inside the boot partition is loaded by the load_android_image function, otherwise the image inside the recovery partition is loaded.
The command line of uboot and the command line inside the kernel device tree can be merged by android_assemble_cmdline function.
With android_image_get_fdt function, find the contents of the device tree from the boot image load address pointed to by load_address and load it to the address pointed to by fdt_addr.
Eventually boot the boot kernel image in load_address through android_bootloader_boot_kernel, which is also the final call to bootm to achieve
Modification suggestions: uboot part after rk years of custom modifications, and its hardware peripherals, partitions and other highly coupled, so you can modify few things. It is suggested that if you want to modify bootcmd, you can directly modify the kernel device tree. If you want to do some peripheral operations before kernel boot, such as writing one or two i2c registers, you can modify the flow of bootcmd and add it before boot_android.