----------------------------------------------------------------------------------------------------------------------------
開發板 :NanoPC-T6
開發板
eMMC
:256GB
LPDDR4
:16GB
螢幕 :15.6
英寸HDMI
介面螢幕
u-boot
:2017.09
linux
:6.1
----------------------------------------------------------------------------------------------------------------------------
在前面的文章我們對Rockhip Linux SDK
進行了深入分析,其中涉及到了SDK
編譯過程、編譯原始碼,具體可以參考:
Rockchip RK3588 - Rockchip Linux SDK
編譯;Rockchip RK3588 - Rockchip Linux SDK Buildroot
檔案系統構建;Rockchip RK3588 - Rockchip Linux SDK
指令碼分析。
此外,我們還是深入分析了Recovery
模式下的系統升級功能,具體可參考:
Rockchip RK3588 - Rockchip Linux Recovery updateEngine
原始碼分析;Rockchip RK3588 - Rockchip Linux Recovery updateEngine
測試。
本節我們將嘗試在NanoPC-T6
開發板實現系統升級功能,當然我們還期望當根檔案系統損壞時,開發板能夠透過按住GPIO
口進入到recovery
系統恢復正常系統。
一、uboot
啟動方式
既然要實現在NanoPC-T6
開發板實現系統升級功能,我們就需要了解uboot
啟動核心的方式,並製作以下分割槽映象;
misc.img
:misc
分割槽是一個沒有檔案系統的分割槽,用於存放一些引導配置引數;recovery.img
:由kernel + dtb + ramdisk
組成,主要用於升級操作;
uboot
會根據misc
分割槽存放的欄位來判斷將要引導的系統是normal
系統還是recovery
系統。
1.1 系統韌體
我們使用的是NanoPC-T6
開發板,這裡我們就去下載官方提供的韌體,關於韌體的下載和燒烤可以參考《Rockchip RK3588
- 移植uboot 2017.09 & linux 6.1
(友善之家指令碼方式)》。
這裡我們選擇debian-bullseye-desktop-arm64-images.tgz
作為測試使用的映象檔案,將debian-bullseye-desktop-arm64-images.tgz
(位於"\03_分割槽映象檔案"目錄下,以實際下載的檔案為準)複製到/work/sambashare/rk3588/friendly/sd-fuse_rk3588
目錄下;
root@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588# ll debian*
-rwxrw-rw- 1 root root 1590466719 Dec 3 01:49 debian-bullseye-desktop-arm64-images.tgz*
-rwxrw-rw- 1 root root 75 Nov 18 19:05 debian-bullseye-desktop-arm64-images.tgz.hash.md5*
root@zhengyang:/work/sambashare/rk3588/friendly/sd-fuse_rk3588# tar -xvzf debian-bullseye-desktop-arm64-images.tgz
解壓得到debian-bullseye-desktop-arm64
資料夾;
root@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588# ll debian-bullseye-desktop-arm64
-rw-r--r-- 1 root root 8072140 May 28 2023 boot.img
-rw-r--r-- 1 root root 1424 May 28 2023 dtbo.img
-rw-r--r-- 1 root root 307200 Sep 8 23:33 idbloader.img
-rw-r--r-- 1 root root 64 Nov 17 10:03 info.conf
-rw-r--r-- 1 root root 35551252 Nov 16 16:17 kernel.img
-rw-r--r-- 1 root root 471488 Sep 8 23:33 MiniLoaderAll.bin
-rw-r--r-- 1 root root 49152 May 28 2023 misc.img
-rw-r--r-- 1 root root 470 Nov 17 10:03 parameter.txt
-rw-r--r-- 1 root root 6227456 Nov 16 16:17 resource.img
-rw-r--r-- 1 root root 3992675220 Nov 17 10:03 rootfs.img
-rw-r--r-- 1 root root 4194304 Sep 8 23:33 uboot.img
-rw-r--r-- 1 root root 159868 Nov 17 10:03 userdata.img
可以看到解壓的檔案已經包含了misc.img
,但是並沒有recovery.img
。
1.1.1 系統分割槽介紹
parameter.txt
儲存著分割槽資訊:
FIRMWARE_VER: 12.0
MACHINE_MODEL: RK3588
MACHINE_ID: 007
MANUFACTURER: RK3588
MAGIC: 0x5041524B
ATAG: 0x00200800
MACHINE: NanoPi6
CHECK_MASK: 0x80
PWR_HLD: 0,0,A,0,1
TYPE: GPT
CMDLINE: mtdparts=rk29xxnand:0x00002000@0x00004000(uboot),0x00002000@0x00006000(misc),0x00002000@0x00008000(dtbo),0x00008000@0x0000a000(resource),0x00014000@0x00012000(kernel),0x00010000@0x00026000(boot),0x00010000@0x00036000(recovery),0x007c0000@0x00046000(rootfs),-@0x00806000(userdata:grow)
解析資訊如下:
Number | 映象檔案 | Start (sector) | Start (sector) End (sector) | Size | Name |
---|---|---|---|---|---|
1 | uboot.img | 0x4000 | 0x5FFF | 4M | uboot |
2 | misc.img | 0x6000 | 0x7FFF | 4M | misc |
3 | dtbo.img | 0x8000 | 0x9FFF | 4M | dtbo |
4 | resource.img | 0xa000 | 0x11FFF | 16MB | resource |
5 | kernel.img | 0x12000 | 0x25FFF | 40MB | kernel |
6 | boot.img | 0x26000 | 0x35FFF | 32MB | boot |
7 | recovery.img | 0x36000 | 0x45FFF | 32MB | recovery |
8 | rootfs.img | 0x46000 | 0x804FFF | 3968GB | rootfs |
9 | userdata.img | 0x806000 | - | userdata |
其中:
uboot
分割槽:供uboot
編譯出來的uboot.img
;misc
分割槽:引導引數分割槽,供misc.img
,給recovery
使用;dtbo
::供kernel
編譯出來的dtbo.img
;resource
:資源分割槽,由裝置樹、圖片資原始檔組成,不包含核心;boot
:供kernel
編譯出來的boot.img
(kernel + dtb
,對於該型別映象uboot
需要採用distro_boo extlinux
引導方式);kernel
:供kernel
編譯出來的kernel.img
(由tools/mkkrnlimg
工具編譯核心映象Image
檔案得到);recovery
分割槽:供recovery
編譯出的recovery.img
(kernel + dtb + ramdisk
);rootfs
分割槽:供buildroot
、debian
或yocto
編出來的rootfs.img
;userdata
分割槽:供APP
臨時生成檔案或給終端使用者使用,掛載在/userdata
目錄下。
從上面我們可以看到這裡有兩個分割槽時存放了核心映象,分別是boot
和kernel
,那問題來了,uboot
啟動到底使用的是哪個核心呢?
1.1.2 燒錄韌體
我們將韌體燒錄到開發板;
給開發板上電,輸出的日誌中有如下內容:
Hit key to stop autoboot('CTRL+C'): 0
## Booting FIT Image FIT: No fit blob
FIT: No FIT image
ANDROID: reboot reason: "(none)"
Not AVB images, AVB skip
No valid android hdr
Android image load failed
Android boot failed, error -1.
## Booting Rockchip Format Image
fdt @ 0x08300000 (0x000421c2)
kernel @ 0x00400000 (0x021c7808)
ramdisk @ 0x0a200000 (0x007b2bc0)
Fdt Ramdisk skip relocation
## Flattened Device Tree blob at 0x08300000
Booting using the fdt blob at 0x08300000
Using Device Tree in place at 0000000008300000, end 00000000083451c1
日誌中說我們採用的核心映象既不是是Android
,也不是FIT uImage
。
接著又輸出
在uboot
命令列檢視啟動命令:
1.2 boot
啟動命令
當我們在uboot
命令列執行了boot
命令時,uboot
會獲取bootcmd
環境變數的內容,然後執行bootcmd
中儲存的啟動命令。
接下來我們來分析一下bootcmd
預設配置,在預設環境變數default_environment
(位於uboot-rockchip/include/env_default.h
)中定義有,其內容大致如下:
const uchar default_environment[] = {
"bootcmd=" CONFIG_BOOTCOMMAND "\0"
"bootdelay=" __stringify(CONFIG_BOOTDELAY) "\0"
"baudrate=" __stringify(CONFIG_BAUDRATE) "\0"
"ipaddr=" __stringify(CONFIG_IPADDR) "\0"
"serverip=" __stringify(CONFIG_SERVERIP) "\0"
"netmask=" __stringify(CONFIG_NETMASK) "\0"
......
};
預設啟動命令CONFIG_BOOTCOMMAND
定義在uboot-rockchip/include/configs/nanopi6.h
,該檔案存放著開發板配置資訊,被uboot-rockchip/include/config.h
檔案引入。
#include <configs/rk3588_common.h>
/* Remove or override few declarations from rk3588-common.h */
#undef CONFIG_BOOTCOMMAND
#undef CONFIG_DISPLAY_BOARDINFO_LATE
#undef RKIMG_DET_BOOTDEV
#undef RKIMG_BOOTCOMMAND
#define CONFIG_SYS_MMC_ENV_DEV 0
#define CONFIG_SYS_MMC_MAX_BLK_COUNT 32768
#define CONFIG_MISC_INIT_R
#define CONFIG_SERIAL_TAG
#ifndef CONFIG_SPL_BUILD
#define ROCKCHIP_DEVICE_SETTINGS \
"stdout=serial,vidconsole\0" \
"stderr=serial,vidconsole\0"
#define RKIMG_DET_BOOTDEV \
"rkimg_bootdev=" \
"if mmc dev 1 && rkimgtest mmc 1; then " \
"setenv devtype mmc; setenv devnum 1; echo Boot from SDcard;" \
"elif mmc dev 0; then " \
"setenv devtype mmc; setenv devnum 0;" \
"elif rksfc dev 1; then " \
"setenv devtype spinor; setenv devnum 1;" \
"fi; \0"
#define RKIMG_BOOTCOMMAND \
"boot_fit;" \
"boot_android ${devtype} ${devnum};" \
"bootrkp;" \
"run distro_bootcmd;"
#define CONFIG_BOOTCOMMAND RKIMG_BOOTCOMMAND
#endif
這裡取消了uboot-rockchip/include/configs/rockchip-common.h
中定義的宏RKIMG_BOOTCOMMAND
,而進行了重定義。
這裡支援了4中啟動方式:
boot_fit
:從eMMC
中boot
分割槽載入FIT uImage
映象檔案(通常由kernel
+dtb
+ramdisk
組成)到記憶體,然後啟動核心 ;boot_android
:啟動Android
核心映象;bootrkp
:通常用於Rockchip
平臺上的特定啟動操作,可能用於啟動特定的韌體或者特殊的操作模式;distro_bootcmd
:執行uboot
環境中定義的distro_bootcmd
,這是一個uboot
環境變數,通常包含了一系列的啟動命令,比如嘗試從網路引導、從儲存裝置引導等;
其中boot_fit
、distro_bootcmd
啟動方式我們在《 Rockchip RK3399
- 移植linux 5.2.8
》中有過介紹。
1.2.1 bootrkp
bootrkp
命令的實現位於uboot-rockchip/cmd/bootrkp.c
,uboot
中使用宏U_BOOT_CMD
來定義命令;
U_BOOT_CMD(
bootrkp, 1, 1, do_boot_rockchip,
"Boot Linux Image from rockchip image type",
"kernel.img: zImage/Image\n"
"boot.img: ramdisk\n"
"resource.img: dtb, u-boot logo, kernel logo"
);
該宏展開後實際上是一個cmd_tbl_t
結構體變數,命令名為bootrkp
,命令處理函式為do_boot_rockchip
。
static int boot_rockchip_image(struct blk_desc *dev_desc,
disk_partition_t *boot_part)
{
disk_partition_t kernel_part;
ulong ramdisk_addr_r;
ulong kernel_addr_r;
ulong fdt_addr_r;
int ramdisk_size;
int kernel_size;
int fdt_size;
int ret;
printf("\n## Booting Rockchip Format Image\n");
ramdisk_addr_r = env_get_ulong("ramdisk_addr_r", 16, 0);
kernel_addr_r = env_get_ulong("kernel_addr_r", 16, 0);
fdt_addr_r = env_get_ulong("fdt_addr_r", 16, 0);
ret = part_get_info_by_name(dev_desc, PART_KERNEL, &kernel_part);
if (ret < 0) {
printf("Could not find kernel partition, ret=%d\n", ret);
return -EINVAL;
}
kernel_size = read_rockchip_image(dev_desc, &kernel_part,
(void *)kernel_addr_r);
if (kernel_size < 0) {
printf("Failed to read kernel image, ret=%d\n", ret);
return -EINVAL;
}
ramdisk_size = read_rockchip_image(dev_desc, boot_part,
(void *)ramdisk_addr_r);
if (ramdisk_size < 0)
ramdisk_size = 0;
if (gd->fdt_blob != (void *)fdt_addr_r) {
fdt_size = rockchip_read_dtb_file((void *)fdt_addr_r);
if (fdt_size < 0) {
printf("Failed to read fdt, ret=%d\n", fdt_size);
return -EINVAL;
}
}
env_set("bootm-no-reloc", "y");
printf("fdt @ 0x%08lx (0x%08x)\n", fdt_addr_r, fdt_totalsize(fdt_addr_r));
printf("kernel @ 0x%08lx (0x%08x)\n", kernel_addr_r, kernel_size);
printf("ramdisk @ 0x%08lx (0x%08x)\n", ramdisk_addr_r, ramdisk_size);
#if defined(CONFIG_ARM64)
char cmdbuf[64];
snprintf(cmdbuf, 64, "booti 0x%lx 0x%lx:0x%x 0x%lx",
kernel_addr_r, ramdisk_addr_r, ramdisk_size, fdt_addr_r);
run_command(cmdbuf, 0);
#else
/* We asume it's always zImage on 32-bit platform */
ulong kaddr_c = env_get_ulong("kernel_addr_c", 16, 0);
ulong kaddr_r, kaddr, ksize;
if (kernel_addr_r && !kaddr_c) {
kaddr_c = kernel_addr_r;
kaddr_r = CONFIG_SYS_SDRAM_BASE;
}
if (!sysmem_free((phys_addr_t)kaddr_c)) {
kaddr = kaddr_r;
ksize = kernel_size * 100 / 45 ; /* Ratio: 45% */
ksize = ALIGN(ksize, dev_desc->blksz);
if (!sysmem_alloc_base(MEM_UNCOMP_KERNEL,
(phys_addr_t)kaddr, ksize))
return -ENOMEM;
}
boot_lmb_init(&images);
images.ep = kernel_addr_r;
images.initrd_start = ramdisk_addr_r;
images.initrd_end = ramdisk_addr_r + ramdisk_size;
images.ft_addr = (void *)fdt_addr_r;
images.ft_len = fdt_totalsize(fdt_addr_r);
do_bootm_linux(0, 0, NULL, &images);
#endif
return 0;
}
二、映象製作
2.1 misc.img
2.2 recovery.img
本節我們將參考Rockchip Linux SDK
嘗試自行製作一個recovery.img
系統映象,並將其燒錄到NanoPC-T6
開發板,用來實現該開發板的OTA
升級功能。
recovery.img
系統映象由kernel + dtb + ramdisk
三部分組成。
2.2.1 Kernrl
2.2.2 dtb
2.2.3 ramdisk
參考文章
[1] Mini2440
之uboot
移植之原始碼分析start.S
(一)]
[2] Mini2440之uboot
移植之裁剪、分割槽與環境變數設定(五)
[3] Rockchip RK3399
- 移植linux 5.2.8
[4] Mini2440
之uboot
移植之原始碼分析命令解析(五)