開發(fā)板 :RK3588 EVB開發(fā)板
eMMC :256GB
LPDDR4 :16GB
顯示屏 :10.1英寸HDMI接口顯示屏
u-boot :2017.09
linux :6.1
----------------------------------------------------------------------------------------------------------------------------
在前面的文章我們對Rockhip Linux SDK進(jìn)行了深入分析,其中涉及到了SDK編譯過程、編譯源碼,具體可以參考:
Rockchip RK3588 - Rockchip Linux SDK編譯;(如需要此文檔的可以聯(lián)系博主獲取)
Rockchip RK3588 - Rockchip Linux SDK Buildroot文件系統(tǒng)構(gòu)建;(如需要此文檔的可以聯(lián)系博主獲取)
Rockchip RK3588 - Rockchip Linux SDK腳本分析。(如需要此文檔的可以聯(lián)系博主獲取)
此外,我們還是深入分析了Recovery模式下的系統(tǒng)升級功能,具體可參考:
Rockchip RK3588 - Rockchip Linux Recovery updateEngine源碼分析;(如需要此文檔的可以聯(lián)系博主獲取)
Rockchip RK3588 - Rockchip Linux Recovery updateEngine測試。 (如需要此文檔的可以聯(lián)系博主獲取)
接下來我們將嘗試在RK3588開發(fā)板實現(xiàn)系統(tǒng)升級功能,當(dāng)然我們還期望當(dāng)根文件系統(tǒng)損壞時,開發(fā)板能夠通過按住GPIO口進(jìn)入到recovery系統(tǒng)恢復(fù)正常系統(tǒng)。
一、uboot啟動方式
既然要實現(xiàn)在開發(fā)板實現(xiàn)系統(tǒng)升級功能,我們就需要了解uboot啟動內(nèi)核的方式,并制作以下分區(qū)鏡像;
misc.img:misc分區(qū)是一個沒有文件系統(tǒng)的分區(qū),用于存放一些引導(dǎo)配置參數(shù);
recovery.img:由kernel + dtb + ramdisk組成,主要用于升級操作;
uboot會根據(jù)misc分區(qū)存放的字段來判斷將要引導(dǎo)的系統(tǒng)是normal系統(tǒng)還是recovery系統(tǒng)。
1.1 系統(tǒng)固件
我們使用的是RK3588開發(fā)板,這里我們就去下載官方提供的固件
這里我們選擇debian-bullseye-desktop-arm64-images.tgz作為測試使用的鏡像文件,將debian-bullseye-desktop-arm64-images.tgz(位于"?3_分區(qū)鏡像文件"目錄下,以實際下載的文件為準(zhǔn))拷貝到/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@ubuntu:/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
可以看到解壓的文件已經(jīng)包含了misc.img,但是并沒有recovery.img。
1.1.1 系統(tǒng)分區(qū)介紹
parameter.txt保存著分區(qū)信息:
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 | Name | 鏡像文件 | Start (sector) | End (sector) | Size |
---|---|---|---|---|---|
1 | uboot | uboot.img | 0x4000(16384) | 0x5FFF | 4M |
2 | misc | misc.img | 0x6000(24576) | 0x7FFF | 4M |
3 | dtbo | dtbo.img | 0x8000(32768) | 0x9FFF | 4M |
4 | resource | resource.img | 0xa000(40960) | 0x11FFF | 16MB |
5 | kernel | kernel.img | 0x12000(73728) | 0x25FFF | 40MB |
6 | boot | boot.img | 0x26000(155648) | 0x35FFF | 32MB |
7 | recovery | recovery.img | 0x36000(221184) | 0x45FFF | 32MB |
8 | rootfs | rootfs.img | 0x46000(286720) | 0x804FFF | 3.968GB |
9 | userdata | userdata.img | 0x806000(8413184) | - |
其中:
uboot分區(qū):供uboot編譯出來的uboot.img;
misc分區(qū):引導(dǎo)參數(shù)分區(qū),供misc.img,給recovery使用;
dtbo::供kernel編譯出來的dtbo.img;
resource:資源分區(qū),由設(shè)備樹、圖片資源文件組成,不包含內(nèi)核;
boot:供kernel編譯出來的boot.img(可能是FIT uImage鏡像格式,也有可能是Android bootimg鏡像格式);
kernel:供kernel編譯出來的kernel.img(由tools/mkkrnlimg工具編譯內(nèi)核鏡像Image文件得到);
recovery分區(qū):供recovery編譯出的recovery.img(kernel + dtb + ramdisk);
rootfs分區(qū):供buildroot、debian或yocto編出來的rootfs.img;
userdata分區(qū):供APP臨時生成文件或給最終用戶使用,掛載在/userdata目錄下。
從上面我們可以看到這里有兩個分區(qū)時存放了內(nèi)核鏡像,分別是boot和kernel,那問題來了,uboot啟動到底使用的是哪個內(nèi)核呢?
1.1.2 生成統(tǒng)一固件
將debian-bullseye-desktop-arm64目錄下的鏡像文件重新打包成SD卡固件:
root@@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588# sudo ./mk-sd-image.sh debian-bullseye-desktop-arm64/ Creating RAW image: out/rk3588-sd-debian-bullseye-desktop-6.1-arm64-20240714.img (7800 MB) --------------------------------- 記錄了0+0 的讀入 記錄了0+0 的寫出 0字節(jié)已復(fù)制,0.0001181 s,0.0 kB/s ---------------------------------------------------------------- [out/rk3588-sd-debian-bullseye-desktop-6.1-arm64-20240714.img] capacity = 7438MB, 7799999488 bytes current out/rk3588-sd-debian-bullseye-desktop-6.1-arm64-20240714.img partition: ---------------------------------------------------------------- parsing ./debian-bullseye-desktop-arm64//parameter.txt: create new GPT 9: ---------------------------------------------------------------- copy from: ./debian-bullseye-desktop-arm64 to out/rk3588-sd-debian-bullseye-desktop-6.1-arm64-20240714.img [RAW. 0]: 300 KB | ./debian-bullseye-desktop-arm64/idbloader.img > 100% : done. [RAW. 1]: 4096 KB | ./debian-bullseye-desktop-arm64/uboot.img > 100% : done. [RAW. 2]: 48 KB | ./debian-bullseye-desktop-arm64/misc.img > 100% : done. [RAW. 3]: 1 KB | ./debian-bullseye-desktop-arm64/dtbo.img > 100% : done. [RAW. 4]: 2518 KB | ./debian-bullseye-desktop-arm64/resource.img > 100% : done. [RAW. 5]: 34590 KB | ./debian-bullseye-desktop-arm64/kernel.img > 100% : done. [RAW. 6]: 7882 KB | ./debian-bullseye-desktop-arm64/boot.img > 100% : done. [RAW. 8]: 3907280 KB | ./debian-bullseye-desktop-arm64/rootfs.img > 100% : done. [RAW. 9]: 156 KB | ./debian-bullseye-desktop-arm64/userdata.img > 100% : done. ---------------------------------------------------------------- --------------------------------- RAW image successfully created (21:09:10). -rw-r--r-- 1 root root 7799999488 7月 14 21:09
該sh腳本內(nèi)部調(diào)用了Rockchip官方提供的打包工具sd_update生成的統(tǒng)一固件,由于打包工具并不開源,所以無法研究源碼。
不過我們大致可以猜測出應(yīng)該就是做了一個鏡像文件,然后按照parameter.txt進(jìn)行劃分分區(qū),并將各個分區(qū)鏡像依次燒錄進(jìn)去。
View Code
1.1.3 制作
SD
啟動卡
我們將SD卡插入PC上,在虛擬機(jī)ubuntu中運行demsg查看新接入的設(shè)備;
[36809.524292] usb 2-1: USB disconnect, device number 2 [36813.382382] usb 2-1: new high-speed USB device number 3 using ehci-pci [36813.657882] usb 2-1: New USB device found, idVendor=14cd, idProduct=1212, bcdDevice= 1.00 [36813.657887] usb 2-1: New USB device strings: Mfr=1, Product=3, SerialNumber=2 [36813.657889] usb 2-1: Product: Mass Storage Device [36813.657890] usb 2-1: Manufacturer: Generic [36813.657891] usb 2-1: SerialNumber: 121220160204 [36813.660529] usb-storage 2-1:1.0: USB Mass Storage device detected [36813.661135] scsi host33: usb-storage 2-1:1.0 [36814.676011] scsi 33:0:0:0: Direct-Access Mass Storage Device 1.00 PQ: 0 ANSI: 0 CCS [36814.677119] sd 33:0:0:0: Attached scsi generic sg2 type 0 [36814.681851] sd 33:0:0:0: [sdb] 62333952 512-byte logical blocks: (31.9 GB/29.7 GiB) [36814.685829] sd 33:0:0:0: [sdb] Write Protect is off [36814.685833] sd 33:0:0:0: [sdb] Mode Sense: 03 00 00 00 [36814.690127] sd 33:0:0:0: [sdb] No Caching mode page found [36814.690132] sd 33:0:0:0: [sdb] Assuming drive cache: write through [36814.713610] sdb: sdb1 [36814.714055] sd 33:0:0:0: [sdb] Attached SCSI removable disk
可以看到SD卡對應(yīng)的設(shè)備節(jié)點為/dev/sdb,對應(yīng)1個分區(qū)sdb1;
root@@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588# sudo ls /dev/sdb* /dev/sdb /dev/sdb1 root@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588# df -hT 文件系統(tǒng) 類型 容量 已用 可用 已用% 掛載點 udev devtmpfs 3.9G 0 3.9G 0% /dev tmpfs tmpfs 791M 3.6M 787M 1% /run /dev/sda5 ext4 98G 69G 24G 75% / tmpfs tmpfs 3.9G 0 3.9G 0% /dev/shm tmpfs tmpfs 5.0M 4.0K 5.0M 1% /run/lock tmpfs tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup /dev/sda1 vfat 511M 4.0K 511M 1% /boot/efi /dev/loop15 squashfs 497M 497M 0 100% /snap/gnome-42-2204/132 tmpfs tmpfs 791M 0 791M 0% /run/user/0 tmpfs tmpfs 791M 36K 791M 1% /run/user/1000 /dev/sdc2 ext4 11G 311M 9.8G 4% /media/zhengyang/userdata /dev/sdc1 ext4 4.5G 4.4G 35M 100% /media/zhengyang/rootfs
開始制作SD啟動卡:
root@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588# sudo dd if=out/rk3588-sd-debian-bullseye-desktop-6.1-arm64-20240714.img of=/dev/sdb bs=4M status=progress
1.2 uboot環(huán)境變量
將SD卡插入到開發(fā)板,并使用準(zhǔn)備好的USB轉(zhuǎn)串口適配器和連接線(需另購),連接開發(fā)板,給開發(fā)板上電。在啟動過程中按下CTRL+C進(jìn)入uboot命令行模式;
View Code
1.2.1 啟動命令行
查看內(nèi)核啟動命令;
=> print bootcmd bootcmd=boot_fit;boot_android ${devtype} ${devnum};bootrkp;run distro_bootcmd;
1.2.2 啟動參數(shù)
查看內(nèi)核啟動參數(shù):
=> pri bootargs bootargs=storagemedia=sd androidboot.storagemedia=sd androidboot.mode=normal androidboot.dtbo_idx=1
1.2.3 資源文件
uboot查看資源文件:
# 切換到SD卡所屬設(shè)備 ==> mmc dev 1 switch to partitions #0, OK mmc1 is current device ==> mmc info Device: mmc@fe2c0000 Manufacturer ID: 3 OEM: 5344 Name: SD32G Timing Interface: Legacy Tran Speed: 52000000 Rd Block Len: 512 SD version 3.0 High Capacity: Yes Capacity: 29.7 GiB Bus Width: 4-bit Erase Group Size: 512 Bytes # 從resource分區(qū)讀取20個扇區(qū)數(shù)據(jù) ==> mmc read 0x10000000 0xa000 20 MMC read: dev # 1, block # 40960, count 32 ... 32 blocks read: OK # 查看前兩個扇區(qū)數(shù)據(jù) ==> md.b 0x10000000 0x400 10000000: 52 53 43 45 00 00 00 00 01 01 01 00 18 00 00 00 RSCE............ 10000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ ...... 10000200: 45 4e 54 52 72 6b 33 33 39 39 2d 6e 61 6e 6f 70 ENTRrk3399-nanop 10000210: 69 34 2d 72 65 76 30 30 2e 64 74 62 00 00 00 00 i4-rev00.dtb.... 10000220: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ ...... 100002e0: 02 8a cd 4f a8 69 32 dd d0 bd de 09 34 59 ad 6e ...O.i2.....4Y.n 100002f0: 7d 42 d6 ac 00 00 00 00 00 00 00 00 00 00 00 00 }B.............. ......
這里我們讀取resource分區(qū)的數(shù)據(jù),也就是resource.img鏡像,可以看到以上輸出內(nèi)容中包含了設(shè)備樹文件的數(shù)據(jù)。
1.2.4 設(shè)備樹
查看設(shè)備樹:
=> print dtb_name dtb_name=rk3588-nano0pi6-rev01.dtb
1.3 啟動內(nèi)核
當(dāng)我們在uboot命令行執(zhí)行了boot命令時,uboot會獲取bootcmd環(huán)境變量的內(nèi)容,然后執(zhí)行bootcmd中保存的啟動命令。
接下來我們來分析一下bootcmd默認(rèn)配置,在默認(rèn)環(huán)境變量default_environment(位于uboot-rockchip/include/env_default.h)中定義有,其內(nèi)容大致如下:
const uchar default_environment[] = { "bootcmd=" CONFIG_BOOTCOMMAND "?" "bootdelay=" __stringify(CONFIG_BOOTDELAY) "?" "baudrate=" __stringify(CONFIG_BAUDRATE) "?" "ipaddr=" __stringify(CONFIG_IPADDR) "?" "serverip=" __stringify(CONFIG_SERVERIP) "?" "netmask=" __stringify(CONFIG_NETMASK) "?" ...... #ifdef CONFIG_EXTRA_ENV_SETTINGS CONFIG_EXTRA_ENV_SETTINGS #endif "?" };
默認(rèn)啟動命令CONFIG_BOOTCOMMAND定義在uboot-rockchip/include/configs/nanopi6.h,該文件存放著開發(fā)板配置信息,被uboot-rockchip/include/config.h文件引入。
#include /* 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?" "stderr=serial,vidconsole?" #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; ?" #define RKIMG_BOOTCOMMAND "boot_fit;" "boot_android ${devtype} ${devnum};" "bootrkp;" "run distro_bootcmd;" #define CONFIG_BOOTCOMMAND RKIMG_BOOTCOMMAND #endif
這里引入了uboot-rockchip/include/configs/rk3588_common.h,而該文件又引入了uboot-rockchip/include/configs/rockchip-common.h。
這里支持了內(nèi)核的4種引導(dǎo)方式:
boot_fit:從eMMC中boot/recovery分區(qū)(如果進(jìn)入的是normal系統(tǒng),則為boot分區(qū);如果進(jìn)入的是recovery系統(tǒng),則為recovery分區(qū))加載FIT uImage鏡像文件(通常由kernel + dtb + ramdisk組成)到內(nèi)存,然后啟動內(nèi)核 ;
boot_android:啟動Android內(nèi)核鏡像;
bootrkp:通常用于Rockchip平臺上的特定啟動操作,可能用于啟動特定的固件或者特殊的操作模式;
distro_bootcmd:運行uboot環(huán)境中定義的 distro_bootcmd,這是一個uboot環(huán)境變量,通常包含了一系列的啟動命令,比如嘗試從網(wǎng)絡(luò)引導(dǎo)、從存儲設(shè)備引導(dǎo)等;
其中boot_fit、distro_bootcmd啟動方式我們在《 Rockchip RK3399 - 移植linux 5.2.8》中有過介紹。
1.3.1 內(nèi)核啟動日志
輸入boot命令啟動內(nèi)核:
=> boot ## Booting FIT Image FIT: No fit blob # 命令boot_fit FIT: No FIT image ANDROID: reboot reason: "(none)" # 命令boot_android Not AVB images, AVB skip No valid android hdr Android image load failed Android boot failed, error -1. ## Booting Rockchip Format Image # 命令bootrkp fdt @ 0x08300000 (0x000421b2) # fdt加載到內(nèi)存的地址 kernel @ 0x00400000 (0x021c7808) # kernel加載到內(nèi)存的地址 ramdisk @ 0x0a200000 (0x007b2bc0) # ramdisk加載到內(nèi)存的地址 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 00000000083451b1 ## reserved-memory: cma: addr=10000000 size=8000000 drm-logo@00000000: addr=edf00000 size=468000 vendor-storage-rm@00000000: addr=ebcd3000 size=10000 ramoops@110000: addr=110000 size=e0000 Adding bank: 0x00200000 - 0x08400000 (size: 0x08200000) Adding bank: 0x09400000 - 0xf0000000 (size: 0xe6c00000) Adding bank: 0x100000000 - 0x3fc000000 (size: 0x2fc000000) Adding bank: 0x3fc500000 - 0x3fff00000 (size: 0x03a00000) Adding bank: 0x4f0000000 - 0x500000000 (size: 0x10000000) Total: 10246.299/11135.828 ms Starting kernel ... [ 11.146608] Booting Linux on physical CPU 0x0000000000 [0x412fd050] [ 11.146631] Linux version 6.1.25 (root@ubuntu) (aarch64-linux-gnu-gcc (Ubuntu 10.5.0-1ubuntu1~20.04) 10.5.0, GNU ld (GNU Binutils for Ubuntu) 2.34) #1 SMP Wed Dec 27 21:53:18 CST 2023 [ 11.153743] Machine model: FriendlyElec NanoPC-T6 ...... [ 11.510154] Kernel command line: storagemedia=sd androidboot.storagemedia=sd androidboot.mode=normal androidboot.dtbo_idx=1 androidboot.verifiedbootstate=orange earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 coherent_pool=1m irqchip.gicv3_pseudo_nmi=0 rw root=/dev/mmcblk0p8 rootfstype=ext4 data=/dev/mmcblk0p9 consoleblank=0 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory swapaccount=1 ...... Debian GNU/Linux 11 NanoPC-T6 ttyFIQ0 NanoPC-T6 login: [ 20.885195] systemd-journald[409]: File /var/log/journal/b9164042f80842f6968af54e1d15c9af/user-1000.journal corrupted or uncleanly shut down, renaming and replacing. [ 21.783657] rk_hdmirx fdee0000.hdmirx-controller: hdmirx_audio_startup: device is no connected or audio is off [ 26.433687] platform mtd_vendor_storage: deferred probe pending NanoPC-T6 login:
(1) 首先執(zhí)行boot_fit命令,對于FIT uImage,其中地址范圍0x00000000~0x00000027表示的是fdt_header結(jié)構(gòu)體的成員信息。
因此會調(diào)用fit_get_blob函數(shù)獲取boot/recovery分區(qū)(如果進(jìn)入的是normal系統(tǒng),則獲取boot分區(qū);如果進(jìn)入的是recovery系統(tǒng),則獲取recovery分區(qū))第一個扇區(qū)數(shù)據(jù),并對fdt_header結(jié)構(gòu)體進(jìn)行校驗判斷是不是FIT uImage。
由于正常情況下我們進(jìn)入的是normal系統(tǒng),則從boot分區(qū)加載boot.img數(shù)據(jù),從輸出的日志信息可以看出我們燒錄的boot.img并不是FIT uIamge。
(2) 接著執(zhí)行boot_android命令,從輸出日志可以看到應(yīng)該也是引導(dǎo)失敗了。
(3) 執(zhí)行bootrkp命令。
(4) 執(zhí)行distro_bootcmd命令。
有關(guān)bootrkp和distro_bootcmd啟動方式,我們接下來詳細(xì)介紹。
1.3.2 加載命令行
不知道你有沒有留意內(nèi)核啟動輸出命令行信息;
[ 11.510154] Kernel command line: storagemedia=sd androidboot.storagemedia=sd androidboot.mode=normal androidboot.dtbo_idx=1 androidboot.verifiedbootstate=orange earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 coherent_pool=1m irqchip.gicv3_pseudo_nmi=0 rw root=/dev/mmcblk0p8 rootfstype=ext4 data=/dev/mmcblk0p9 consoleblank=0 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory swapaccount=1
這里輸出的信息為啥和bootargs環(huán)境變量以及arch/arm64/boot/dts/rockchip/rk3588-nanopi6-common.dtsi內(nèi)容不一樣呢?
=> pri bootargs bootargs=storagemedia=sd androidboot.storagemedia=sd androidboot.mode=normal androidboot.dtbo_idx=1 # 設(shè)備樹設(shè)備節(jié)點內(nèi)容 chosen: chosen { bootargs = "earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 coherent_pool=1m irqchip.gicv3_pseudo_nmi=0"; };
那么我們不得不介紹內(nèi)核啟動后是如何獲取到啟動參數(shù)。對于ARM64來說,uboot在啟動內(nèi)核時會將r2設(shè)置為dtb文件的開始地址。
1.3.2.1 內(nèi)核
bootargs
來源
對于開發(fā)板開發(fā)板而言,r2設(shè)置為了rk3588-nanopi6-rev01.dtb加載到內(nèi)存的地址。
這里我們直接從內(nèi)核start_kernel函數(shù)開始說起,其位于init/main.c文件,函數(shù)調(diào)用棧如下;
#char __initdata boot_command_line[COMMAND_LINE_SIZE]; // 全局變量,定義在init/main.c start_kernel() // init/main.c char *command_line; ....... setup_arch(&command_line); // arch/arm64/kernel/setup.c ...... *cmdline_p = boot_command_line; ...... // __fdt_pointer:dtb所在的物理地址,由bootloader通過x0寄存器傳遞過來 setup_machine_fdt(__fdt_pointer); // arch/arm64/kernel/setup.c // 返回dtb所在的虛擬地址dt_virt void *dt_virt = fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL) if (!dt_virt || !early_init_dt_scan(dt_virt)) { ........ } name = of_flat_dt_get_machine_name(); pr_info("Machine model: %sn", name); machine_desc = mdesc; ......
這里我們重點關(guān)注early_init_dt_scan函數(shù),early_init_dt_scan主要是對dtb進(jìn)行早期的掃描工作,下面是簡要介紹函數(shù)的調(diào)用流程和實現(xiàn)細(xì)節(jié):
early_init_dt_scan(dt_virt) // drivers/of/fdt.c // 對dtb頭進(jìn)行檢查 early_init_dt_verify(dt_virt) early_init_dt_scan_nodes() // 遍歷設(shè)備樹的節(jié)點,解析出重要的信息用于內(nèi)核啟動 /* Retrieve various information from the /chosen node */ of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); /* Initialize {size,address}-cells info */ of_scan_flat_dt(early_init_dt_scan_root, NULL); /* Setup memory, calling early_init_dt_add_memory_arch */ of_scan_flat_dt(early_init_dt_scan_memory, NULL);
of_scan_flat_dt對dtb里面的所有節(jié)點進(jìn)行掃描,用提供的回調(diào)函數(shù)循環(huán)處理節(jié)點信息,回調(diào)函數(shù)返回0繼續(xù)掃描,返回非0結(jié)束掃描,當(dāng)掃描到最后一個節(jié)點也會結(jié)束掃描;
/** * of_scan_flat_dt - scan flattened tree blob and call callback on each. * @it: callback function * @data: context data pointer * * This function is used to scan the flattened device-tree, it is * used to extract the memory information at boot before we can * unflatten the tree */ int __init of_scan_flat_dt(int (*it)(unsigned long node, const char *uname, int depth, void *data), void *data) { //dtb數(shù)據(jù)的地址,也就是根節(jié)點的地址 const void *blob = initial_boot_params; const char *pathp; int offset, rc = 0, depth = -1; if (!blob) return 0; // 從根節(jié)點遍歷dtb中每個節(jié)點,返回的offset就是每個節(jié)點的地址 // offset:表示節(jié)點的地址相對于根節(jié)點的偏移量,也是節(jié)點數(shù)據(jù)所在地址 // depth:代表節(jié)點相對于根節(jié)點的深度,比如根節(jié)點深度是0,/chosen節(jié)點是1 for (offset = fdt_next_node(blob, -1, &depth); offset >= 0 && depth >= 0 && !rc; offset = fdt_next_node(blob, offset, &depth)) { // 解析出節(jié)點名稱 pathp = fdt_get_name(blob, offset, NULL); if (*pathp == '/') pathp = kbasename(pathp); // 回調(diào)函數(shù)解析節(jié)點,it是傳遞進(jìn)來的設(shè)備樹節(jié)點的解析函數(shù),需要解析什么消息就傳遞進(jìn)來相應(yīng)的節(jié)點解析函數(shù) rc = it(offset, pathp, depth, data); } return rc; }
early_init_dt_scan_chosen用于掃描chosen節(jié)點,并把bootargs屬性值拷貝到boot_command_line中,如果內(nèi)核定義了CONFIG_CMDLINE這個宏,則把配置的命令行參數(shù)也拷貝到boot_command_line;
/* * Convert configs to something easy to use in C code */ #if defined(CONFIG_CMDLINE_FORCE) static const int overwrite_incoming_cmdline = 1; static const int read_dt_cmdline; static const int concat_cmdline; #elif defined(CONFIG_CMDLINE_EXTEND) static const int overwrite_incoming_cmdline; static const int read_dt_cmdline = 1; static const int concat_cmdline = 1; #else /* CMDLINE_FROM_BOOTLOADER */ // 走這里 static const int overwrite_incoming_cmdline; static const int read_dt_cmdline = 1; static const int concat_cmdline; #endif #ifdef CONFIG_CMDLINE static const char *config_cmdline = CONFIG_CMDLINE; #else static const char *config_cmdline = ""; #endif int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, int depth, void *data) { int l = 0; const char *p = NULL; char *cmdline = data; // 即boot_command_line const void *rng_seed; pr_debug("search "chosen", depth: %d, uname: %sn", depth, uname); // 節(jié)點的深度要為1,數(shù)據(jù)不能使NULL,同時節(jié)點名字是"chosen"或者"chosen@0" if (depth != 1 || !cmdline || (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) return 0; // 解析initrd相關(guān) early_init_dt_check_for_initrd(node); /* Put CONFIG_CMDLINE in if forced or if data had nothing in it to start */ if (overwrite_incoming_cmdline || !cmdline[0]) // 進(jìn)入 strlcpy(cmdline, config_cmdline, COMMAND_LINE_SIZE); /* Retrieve command line unless forcing */ if (read_dt_cmdline) // 從chosen節(jié)點中解析出bootargs屬性 p = of_get_flat_dt_prop(node, "bootargs", &l); if (p != NULL && l > 0) { if (concat_cmdline) { int cmdline_len; int copy_len; strlcat(cmdline, " ", COMMAND_LINE_SIZE); cmdline_len = strlen(cmdline); copy_len = COMMAND_LINE_SIZE - cmdline_len - 1; copy_len = min((int)l, copy_len); strncpy(cmdline + cmdline_len, p, copy_len); cmdline[cmdline_len + copy_len] = '?'; } else { // 追加bootargs參數(shù)到boot_command_line strlcpy(cmdline, p, min((int)l, COMMAND_LINE_SIZE)); } } pr_debug("Command line is: %sn", (char*)data); rng_seed = of_get_flat_dt_prop(node, "rng-seed", &l); if (rng_seed && l > 0) { add_bootloader_randomness(rng_seed, l); /* try to clear seed so it won't be found. */ fdt_nop_property(initial_boot_params, node, "rng-seed"); /* update CRC check value */ of_fdt_crc32 = crc32_be(~0, initial_boot_params, fdt_totalsize(initial_boot_params)); } /* break now */ return 1; }
如果想查看內(nèi)核debug級別日志可以配置:
# arch/arm64/configs/nanopi6_linux_defconfig Kernel hacking ---> printk and dmesg options ---> (8) Default console loglevel (1-15) # CONFIG_CONSOLE_LOGLEVEL_DEFAULT # 修改drivers/of/fdt.c 即在需要輸出debug級別日志的文件頭部定義如下宏 #define DEBUG
通過追加日志,我們重新編譯并燒錄會發(fā)現(xiàn)啟動命令行的確是如下這個內(nèi)容:
[ 0.000000] OF: fdt: search "chosen", depth: 1, uname: chosen [ 0.000000] OF: fdt: Looking for initrd properties... [ 0.000000] OF: fdt: initrd_start=0xa200000 initrd_end=0xa9b2bc0 [ 0.000000] OF: fdt: Command line is: storagemedia=sd androidboot.storagemedia=sd androidboot.mode=normal androidboot.dtbo_idx=1 androidboot.verifiedbootstate=orange earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 coherent_pool=1m irqchip.gicv3_pseudo_nmi=0 rw root=/dev/mmcblk0p8 rootfstype=ext4 data=/dev/mmcblk0p9 consoleblank=0 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory swapaccount=1
1.3.2.2
dtb
中
bootargs
來源
實際上dtb中的bootargs的來源有如下幾種;
內(nèi)核啟動參數(shù)bootargs保存在dts的chosen節(jié)點的bootargs屬性,這里就是arch/arm64/boot/dts/rockchip/rk3588-nanopi6-common.dtsi這個設(shè)備樹源文件;
bootargs數(shù)據(jù)可以是在dts源文件中定義,也可以是uboot啟動內(nèi)核時傳遞給內(nèi)核;
其中uboot傳遞的bootargs參數(shù)優(yōu)先級高于設(shè)備樹中定義的bootargs,如果是uboot傳遞的bootargs,在內(nèi)核啟動階段就會調(diào)用fdt_chosen函數(shù)將環(huán)境變量中的bootargs參數(shù)寫進(jìn)dtb數(shù)據(jù)中;
既然uboot傳遞了bootargs參數(shù),那么內(nèi)核將會使用uboot傳遞過來的bootargs參數(shù),不過該參數(shù)為何和內(nèi)核啟動輸出的不太一樣呢?為此我們不得不去研究bootrkp啟動是否追加了啟動參數(shù);
boot_rockchip_image(dev_desc, &part) // bootrkp啟動方式 ...... // 設(shè)置內(nèi)核加載地址(Image鏡像) images.ep = kernel_addr_r; images.initrd_start = ramdisk_addr_r; images.initrd_end = ramdisk_addr_r + ramdisk_size; // 設(shè)置設(shè)備樹加載地址 images.ft_addr = (void *)fdt_addr_r; // 設(shè)備樹長度 images.ft_len = fdt_totalsize(fdt_addr_r); do_bootm_linux(0, 0, NULL, &images); // arch/arm/lib/bootm.c boot_prep_linux(images); // arch/arm/lib/bootm.c image_setup_linux(images) // common/image.c ulong of_size = images->ft_len; char **of_flat_tree = &images->ft_addr; struct lmb *lmb = &images->lmb; boot_fdt_add_mem_rsv_regions(lmb, *of_flat_tree); boot_get_cmdline(lmb, &images->cmdline_start, &images->cmdline_end); boot_relocate_fdt(lmb, of_flat_tree, &of_size); image_setup_libfdt(images, *of_flat_tree, of_size, lmb); // common/image-fdt.c ...... ...... # 可以通過如下代碼輸出啟動參數(shù) char *commandline = env_get("bootargs"); printf("%s %d:%sn", __func__, __LINE__, commandline);
重點關(guān)注image_setup_libfdt,定義在common/image-fdt.c;
int image_setup_libfdt(bootm_headers_t *images, void *blob, int of_size, struct lmb *lmb) { ulong *initrd_start = &images->initrd_start; ulong *initrd_end = &images->initrd_end; int ret = -EPERM; int fdt_ret; // 進(jìn)行架構(gòu)特定的設(shè)備樹修正 if (arch_fixup_fdt(blob) < 0) { printf("ERROR: arch-specific fdt fixup failedn"); goto err; } #if defined(CONFIG_PASS_DEVICE_SERIAL_BY_FDT) // 定義 // 配置根節(jié)點 if (fdt_root(blob) < 0) { printf("ERROR: root node setup failedn"); goto err; } #endif // 創(chuàng)建/chosen節(jié)點 if (fdt_chosen(blob) < 0) { printf("ERROR: /chosen node create failedn"); goto err; } /* Update ethernet nodes */ fdt_fixup_ethernet(blob); if (IMAGE_OF_BOARD_SETUP) { fdt_ret = ft_board_setup(blob, gd-?>bd); if (fdt_ret) { printf("ERROR: board-specific fdt fixup failed: %sn", fdt_strerror(fdt_ret)); goto err; } } if (IMAGE_OF_SYSTEM_SETUP) { fdt_ret = ft_system_setup(blob, gd->bd); if (fdt_ret) { printf("ERROR: system-specific fdt fixup failed: %sn", fdt_strerror(fdt_ret)); goto err; } } /* Delete the old LMB reservation */ if (lmb) lmb_free(lmb, (phys_addr_t)(u32)(uintptr_t)blob, (phys_size_t)fdt_totalsize(blob)); ret = fdt_shrink_to_minimum(blob, 0); if (ret < 0) goto err; of_size = ret; if (*initrd_start && *initrd_end) { of_size += FDT_RAMDISK_OVERHEAD; fdt_set_totalsize(blob, of_size); } /* Create a new LMB reservation */ if (lmb) lmb_reserve(lmb, (ulong)blob, of_size); fdt_initrd(blob, *initrd_start, *initrd_end); if (!ft_verify_fdt(blob)) goto err; #if defined(CONFIG_SOC_KEYSTONE) if (IMAGE_OF_BOARD_SETUP) ft_board_setup_ex(blob, gd-?>bd); #endif return 0; err: printf(" - must RESET the board to recover.nn"); return ret; }
這里我們只需要關(guān)注fdt_chosen函數(shù),定義在common/fdt_support.c;其中rk3399和rk3588 SDK的u-boot源碼是不一樣的;
以rk3588為例:
int fdt_chosen(void *fdt) { int nodeoffset; int err; char *str; /* used to set string properties */ // 檢查設(shè)備樹頭部是否有效 err = fdt_check_header(fdt); if (err < 0) { printf("fdt_chosen: %sn", fdt_strerror(err)); return err; } /* find or create "/chosen" node. 查找或創(chuàng)建/chosen節(jié)點 */ nodeoffset = fdt_find_or_add_subnode(fdt, 0, "chosen"); if (nodeoffset < 0) return nodeoffset; // 獲取環(huán)境變量bootargs的值 str = board_fdt_chosen_bootargs(fdt); if (str) { // 設(shè)置設(shè)備樹中的bootargs屬性 err = fdt_setprop(fdt, nodeoffset, "bootargs", str, strlen(str) + 1); if (err < 0) { printf("WARNING: could not set bootargs %s.n", fdt_strerror(err)); return err; } } return fdt_fixup_stdout(fdt, nodeoffset); }
其中board_fdt_chosen_bootargs定義在arch/arm/mach-rockchip/board.c:
char *board_fdt_chosen_bootargs(void *fdt) { /* bootargs_ext is used when dtbo is applied. */ const char *arr_bootargs[] = { "bootargs", "bootargs_ext" }; const char *bootargs; int nodeoffset; int i, dump; char *msg = "kernel"; /* debug */ hotkey_run(HK_INITCALL); dump = is_hotkey(HK_CMDLINE); if (dump) printf("## bootargs(u-boot): %snn", env_get("bootargs")); /* find or create "/chosen" node. */ nodeoffset = fdt_find_or_add_subnode(fdt, 0, "chosen"); if (nodeoffset < 0) return NULL; // 遍歷arr_bootargs,檢查設(shè)備樹中是否已有相關(guān)的bootargs for (i = 0; i < ARRAY_SIZE(arr_bootargs); i++) { // 獲取/chosen節(jié)點的bootargs、bootargs_ext屬性值 bootargs = fdt_getprop(fdt, nodeoffset, arr_bootargs[i], NULL); if (!bootargs) continue; if (dump) printf("## bootargs(%s-%s): %snn", msg, arr_bootargs[i], bootargs); /* * Append kernel bootargs * If use AB system, delete default "root=" which route * to rootfs. Then the ab bootctl will choose the * high priority system to boot and add its UUID * to cmdline. The format is "roo=PARTUUID=xxxx...". */ #ifdef CONFIG_ANDROID_AB env_update_filter("bootargs", bootargs, "root="); #else // 進(jìn)入,更新bootargs環(huán)境變量,追加設(shè)備樹中配置的bootargs env_update("bootargs", bootargs); #endif } #ifdef CONFIG_VENDOR_FRIENDLYELEC // 針對FriendlyELEC板卡的處理,進(jìn)入 char *panel = board_get_panel_name(); // 如果設(shè)置了panel,更新bootargs環(huán)境變量,比如追加lcd=HD702E,213dpi if (panel) { char lcdinfo[128] = { 0 }; strcpy(lcdinfo, "lcd="); strncat(lcdinfo, panel, sizeof(lcdinfo) - 5); env_update("bootargs", lcdinfo); } #endif #if defined(CONFIG_ENVF) || defined(CONFIG_ENV_PARTITION) ...... #endif #ifdef CONFIG_MTD_BLK ...... #endif #ifdef CONFIG_ANDROID_AB ab_update_root_partition(); #endif /* * Initrd fixup: remove unused "initrd=0x...,0x...", * this for compatible with legacy parameter.txt */ env_delete("bootargs", "initrd=", 0); /* * If uart is required to be disabled during * power on, it would be not initialized by * any pre-loader and U-Boot. * * If we don't remove earlycon from commandline, * kernel hangs while using earlycon to putc/getc * which may dead loop for waiting uart status. * (It seems the root cause is baundrate is not * initilalized) * * So let's remove earlycon from commandline. */ if (gd-?>flags & GD_FLG_DISABLE_CONSOLE) env_delete("bootargs", "earlycon=", 0); /* Android header v4+ need this handle */ #ifdef CONFIG_ANDROID_BOOT_IMAGE struct andr_img_hdr *hdr; hdr = (void *)env_get_ulong("android_addr_r", 16, 0); if (hdr && !android_image_check_header(hdr) && hdr->header_version >= 4) { if (env_update_extract_subset("bootargs", "andr_bootargs", "androidboot.")) printf("extract androidboot.xxx errorn"); if (dump) printf("## bootargs(android): %snn", env_get("andr_bootargs")); } #endif bootargs = env_get("bootargs"); if (dump) printf("## bootargs(merged): %snn", bootargs); return (char *)bootargs; }
以rk3399為例:
int fdt_chosen(void *fdt) { /* * "bootargs_ext" is used when dtbo is applied. */ const char *arr_bootargs[] = { "bootargs", "bootargs_ext" }; int nodeoffset; int err; int i; char *str; /* used to set string properties */ int dump; // 判斷HK_CMDLINE是否是熱鍵,返回false dump = is_hotkey(HK_CMDLINE); // 檢查設(shè)備樹頭部是否有效 err = fdt_check_header(fdt); if (err < 0) { printf("fdt_chosen: %sn", fdt_strerror(err)); return err; } /* find or create "/chosen" node. 查找或創(chuàng)建/chosen節(jié)點 */ nodeoffset = fdt_find_or_add_subnode(fdt, 0, "chosen"); if (nodeoffset < 0) return nodeoffset; // 獲取環(huán)境變量bootargs的值 str = env_get("bootargs"); if (str) { // 如果環(huán)境變量配置了bootargs #ifdef CONFIG_ARCH_ROCKCHIP // 針對Rockchip架構(gòu)的處理 const char *bootargs; if (dump) printf("## U-Boot bootargs: %sn", str); // 遍歷arr_bootargs,檢查設(shè)備樹中是否已有相關(guān)的bootargs for (i = 0; i < ARRAY_SIZE(arr_bootargs); i++) { // 獲取/chosen節(jié)點的bootargs、bootargs_ext屬性值 bootargs = fdt_getprop(fdt, nodeoffset, arr_bootargs[i], NULL); // 1. fdt_chosen 389:earlycon=uart8250,mmio32,0xff1a0000 swiotlb=1 coherent_pool=1m // 2. fdt_chosen 389:root=/dev/mmcblk2p8 rw rootfstype=ext4 rootflags=discard data=/dev/mmcblk2p9 console=ttyFIQ0 consoleblank=0 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory swapaccount=1 printf("%s %d:%sn", __func__, __LINE__, bootargs); // 如果存在,更新環(huán)境變量bootargs if (bootargs) { if (dump) printf("## Kernel %s: %sn", arr_bootargs[i], bootargs); /* * Append kernel bootargs * If use AB system, delete default "root=" which route * to rootfs. Then the ab bootctl will choose the * high priority system to boot and add its UUID * to cmdline. The format is "roo=PARTUUID=xxxx...". */ hotkey_run(HK_INITCALL); #ifdef CONFIG_ANDROID_AB // 未定義 env_update_filter("bootargs", bootargs, "root="); #else // 進(jìn)入,更新bootargs環(huán)境變量,追加設(shè)備樹中配置的bootargs env_update("bootargs", bootargs); #endif #ifdef CONFIG_MTD_BLK // 未定義 char *mtd_par_info = mtd_part_parse(); if (mtd_par_info) { if (memcmp(env_get("devtype"), "mtd", 3) == 0) env_update("bootargs", mtd_par_info); } #endif /* * Initrd fixup: remove unused "initrd=0x...,0x...", * this for compatible with legacy parameter.txt */ env_delete("bootargs", "initrd=", 0); /* * If uart is required to be disabled during * power on, it would be not initialized by * any pre-loader and U-Boot. * * If we don't remove earlycon from commandline, * kernel hangs while using earlycon to putc/getc * which may dead loop for waiting uart status. * (It seems the root cause is baundrate is not * initilalized) * * So let's remove earlycon from commandline. */ if (gd-?>flags & GD_FLG_DISABLE_CONSOLE) env_delete("bootargs", "earlycon=", 0); } } #endif #ifdef CONFIG_VENDOR_FRIENDLYELEC // 針對FriendlyELEC板卡的處理,進(jìn)入 char *panel = board_get_panel_name(); // 如果設(shè)置了panel,更新bootargs環(huán)境變量,比如追加lcd=HD702E,213dpi if (panel) { char lcdinfo[128] = { 0 }; strcpy(lcdinfo, "lcd="); strncat(lcdinfo, panel, sizeof(lcdinfo) - 5); env_update("bootargs", lcdinfo); } #endif // 獲取更新后的bootargs環(huán)境變量,并設(shè)置設(shè)備樹中的bootargs屬性 str = env_get("bootargs"); // fdt_chosen 451:storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal androidboot.dtbo_idx=0 earlycon=uart8250,mmio32,0xff1a0000 swiotlb=1 coherent_pool=1m rw root=/dev/mmcblk2p8 rootfstype=ext4 rootflags=discard data=/dev/mmcblk2p9 console=ttyFIQ0 consoleblank=0 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory swapaccount=1 lcd=HD702E,213dpi printf("%s %d:%sn", __func__, __LINE__, str); err = fdt_setprop(fdt, nodeoffset, "bootargs", str, strlen(str) + 1); if (err < 0) { printf("WARNING: could not set bootargs %s.n", fdt_strerror(err)); return err; } } if (dump) printf("## Merged bootargs: %sn", env_get("bootargs")); return fdt_fixup_stdout(fdt, nodeoffset); }
通過分析,可以了解到fdt_chosen 函數(shù)主要完成了以下任務(wù):
確保設(shè)備樹的 /chosen 節(jié)點存在;
從環(huán)境變量中獲取和處理啟動參數(shù) bootargs;
根據(jù)不同的硬件配置(如Rockchip架構(gòu)或FriendlyELEC板卡)調(diào)整啟動參數(shù);
更新設(shè)備樹中的 bootargs 屬性,確保內(nèi)核可以正確獲得啟動參數(shù);
修正標(biāo)準(zhǔn)輸出設(shè)備配置。
在上面代碼執(zhí)行過程中我們輸出了/chosen節(jié)點的bootargs、bootargs_ext屬性值,其中bootargs_ext屬性值哪里來的呢?
root=/dev/mmcblk2p8 rw rootfstype=ext4 rootflags=discard data=/dev/mmcblk2p9 console=ttyFIQ0 consoleblank=0 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory swapaccount=1
這個值實際上配置在dtbo.img鏡像中,具體可以參考android_fdt_overlay_apply函數(shù),這個我們在接下來的內(nèi)容會介紹到。
1.4 uboot編譯和燒錄
1.4.1 編譯
如果我們對uboot源碼有改動,執(zhí)行如下命令進(jìn)行編譯;
root@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588# UBOOT_SRC=$PWD/uboot-rockchip ./build-uboot.sh debian-bullseye-desktop-arm64
編譯完成后debian-bullseye-desktop-arm64目錄下的uboot.img被更新了。
1.4.2
dd
燒錄
由于uboot.img占用的分區(qū)是uboot分區(qū),假設(shè)SD/TF Card設(shè)備節(jié)點為/dev/mmcblk0。
我們在ubuntu開啟http下載服務(wù),或者使用scp將鏡像文件發(fā)送到開發(fā)版;
root@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588/debian-bullseye-desktop-arm64$ python3 -m http.server 8080
開發(fā)板下載uboot.img,然后使用如下命令燒錄;
root@linaro-alip:/opt# sudo wget 192.168.0.200:8080/uboot.img root@linaro-alip:/opt# sudo dd if=uboot.img of=/dev/mmcblk0p1 bs=1M
同樣如果我們修改了resource.img,也可以使用如下命令燒錄;
root@linaro-alip:/opt# sudo wget 192.168.0.200:8080/resource.img root@linaro-alip:/opt# sudo dd if=resource.img of=/dev/mmcblk0p4 bs=1M
1.5 補充
新創(chuàng)云RK3588核心板介紹
1.1 適用范圍
產(chǎn)品采用核心板加底板方式,核心板主要集成四大主件(分別為主控,內(nèi)存,存儲和電源管理),核心板安裝可方便拆卸,客戶可以快速進(jìn)行二次開發(fā),主要應(yīng)用于工業(yè)控制,商顯,智能家居,汽車電子,醫(yī)療設(shè)備,產(chǎn)品形態(tài)有,廣告機(jī),無人值守機(jī)器人,送餐機(jī)器人,工業(yè)應(yīng)用機(jī)器人,平板電腦,學(xué)習(xí)機(jī),匝機(jī)通道,客流統(tǒng)計,車牌識別,數(shù)字標(biāo)牌、智能自助終端、智能零售終端等相關(guān)產(chǎn)品。
1.2 產(chǎn)品概述
核心板采用 ROCKCHIP 八核 RK3588 四核 A76+四核 A55,搭載Android/Linux+QT/Ubuntu 系 統(tǒng) ,A76 主 頻 2.4GHz , A55 主 頻1.8G,采用 Mali-G610 MP4 GPU,內(nèi)置 6T 算力 NPU,內(nèi)存最大支持16GB,支持市面上通用顯示屏接口,支持多屏異顯,核心板接口豐富,引出全部功能引腳,支持多款外設(shè)擴(kuò)展,是您在人機(jī)交互、工控項目上的最佳選擇。
1.3 產(chǎn)品特點
◆ BDB 板對板連接器可拆卸式核心板,引腳高達(dá) 320P,引出全部功能。
◆ 支持 2 路 HDMI 輸出,2 路 MIPI 顯示屏,2 路 EDP 顯示屏,支持多屏異顯。
◆ 內(nèi)置 6T 算力 NPU。
◆ 最大支持 16GB 內(nèi)存。
◆ 支持 Android/Linux+QT/麒麟信安、鴻蒙系統(tǒng)定制,提供系統(tǒng)調(diào)用接口 API 參考代碼,完美支持客戶上層應(yīng)用 APP 開發(fā)及SDK。
主要硬件指標(biāo) | |
CPU | ROCKCHIP RK3588 八核 A76+A55 |
NPU | 6 TOPS |
GPU | Mali G61 MP4 |
DDR | LPDDR4x 可選配 4G/8G/16G |
EMMC | EMMC 5.1 標(biāo)配 8GB 選配 32G/64G/128G |
工作電壓 | 3.4-5.5V 5A 以上 |
工作溫度 | -10 到+60 度 |
連接方式 | 板對板連接器 |
尺寸 | 60mm 長*50mm 寬*4.2mm 高 |
壽命 | 連續(xù)運行壽命大于 5 年以上 |
-
開發(fā)板
+關(guān)注
關(guān)注
25文章
5439瀏覽量
101313 -
Uboot
+關(guān)注
關(guān)注
4文章
126瀏覽量
28797 -
RK3588
+關(guān)注
關(guān)注
7文章
397瀏覽量
5444
發(fā)布評論請先 登錄
香蕉派瑞芯微 Rockchip RK3588 開發(fā)板套件主要硬件規(guī)格

瑞芯微RK3588開發(fā)板RK3588 EVB和RK3588S EVB解讀

RK3588 Android+Linux雙系統(tǒng)方案的實現(xiàn)

RK3588 SDK編譯與固件燒寫步驟
RK3588 Android 12.0 SDK編譯步驟分享
【LGA封裝RK3588核心板】基于RK3588,小而強(qiáng)大的ArmSom-W3 CORE BOARD

評論