1 傳遞dtb給內核
對於傳統bootloader提供兩種工作模式:一是啟動加載模式(start loading),一是下載模式(downloading)
工作在啟動加載模式時,bootloader會自動執行bootcmd命令,
比如:bootcmd=“nand read 0x100000 0x80000000 0x300000; bootm 0x80000000”
uboot首先把內核鏡像拷貝到內存地址為0x80000000的地方,然后執行bootm 0x80000000命令。
bootm命令實際上調用的是do_bootm_linux函數:
theKernel (0,bd->bi_arch_number, bd->bi_boot_params);
r0,r1,r2三個寄存器的設置
r0一般設置為0;
r1一般設置為machine id (在使用設備樹時該參數沒有被使用);是讓內核知道是哪個CPU,從而調用對應的初始化函數
r2一般設置ATAGS或DTB的開始地址;
以前沒有使用設備樹時,需要bootloader傳一個machine id給內核,內核啟動的時候會根據這個machine_id來比較內核machine_desc(機器描述結構體)中的.nr,如果相等,就選中了對應的machine_desc(機器描述結構體)),然后調用machine_desc(機器描述結構體)中的.init(初始化函數)。現在使用設備樹的話,這個參數就不需要設置了。
對於我們拿到一個新的bootloader,我們怎么能使代碼支持dtb模式,我們需要配置#define CONFIG_OF_LIBFDT,可讓u-boot支持內核設備樹dts,加載命令如下:
bootm <uImage_addr> <initrd_addr> <dtb_addr> //bootm + uImage地址 + ramdisk地址 + 設備樹鏡像地址
如:
1 //1. 下載內核uImage到內存0x30007FC0 2 tftp 0x30007FC0 uImage 3 //2. 下載dtb到內存32000000 4 tftp 0x30001000 s3c2440-smdk2440.dtb 5 //3. - 表示不使用ramdisk加載,如果使用ramdisk則提供其加載地址 6 bootm 0x30007FC0 - 0x30001000
對於我們下載dtb的地址0x32000000,這個地址有什么要求呢?是隨意選的地址就可以,還是要遵循什么原則呢?
(1)不要破壞u-boot本身
(2)不要破壞內核本身: 內核本身的空間不能占用, 內核要用到的內存區域也不能占用
對於該問題,我們拿了一塊2440的地址空間分配圖來說明該問題。
對於dtb的存放,只能存放在空閑區,並且不能與其他區有重合的地方。
2 fdt命令查看設備樹
如果修改設備樹中的led設備引腳,有兩種辦法:
(1)修改dts文件,重新編譯得到dtb並上傳燒寫
(2)使用uboot提供的一些命令來修改dtb文件,修改后再把它保存到板子上,以后就使用這個修改后的dtb文件移動值,也就是通過memmove處理
對於u-boot提供了fdt的相關命令
1 "addr [-c] <addr> [<length>] - Set the [control] fdt location to <addr>\n" 2 "fdt move <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active\n" 3 "fdt resize [<extrasize>] - Resize fdt to size + padding to 4k addr + some optional <extrasize> if needed\n" 4 "fdt print <path> [<prop>] - Recursive print starting at <path>\n" 5 "fdt list <path> [<prop>] - Print one level starting at <path>\n" 6 "fdt get value <var> <path> <prop> - Get <property> and store in <var>\n" 7 "fdt get name <var> <path> <index> - Get name of node <index> and store in <var>\n" 8 "fdt get addr <var> <path> <prop> - Get start address of <property> and store in <var>\n" 9 "fdt get size <var> <path> [<prop>] - Get size of [<property>] or num nodes and store in <var>\n" 10 "fdt set <path> <prop> [<val>] - Set <property> [to <val>]\n" 11 "fdt mknode <path> <node> - Create a new node after <path>\n" 12 "fdt rm <path> [<prop>] - Delete the node or <property>\n" 13 "fdt header - Display header info\n" 14 "fdt bootcpu <id> - Set boot cpuid\n" 15 "fdt memory <addr> <size> - Add/Update memory node\n" 16 "fdt rsvmem print - Show current mem reserves\n" 17 "fdt rsvmem add <addr> <size> - Add a mem reserve\n" 18 "fdt rsvmem delete <index> - Delete a mem reserves\n" 19 "fdt chosen [<start> <end>] - Add/update the /chosen branch in the tree\n" 20 " <start>/<end> - initrd start/end addr\n"
實例
1 nand read.jffs2 32000000 device_tree // 從flash讀出dtb文件到內存(0x32000000) 2 fdt addr 32000000 // 告訴fdt, dtb文件在哪 3 fdt print /led pin // 打印/led節點的pin屬性 4 fdt get value XXX /led pin // 讀取/led節點的pin屬性, 並且賦給環境變量XXX 5 print XXX // 打印環境變量XXX的值 6 fdt set /led pin <0x00050005> // 設置/led節點的pin屬性 7 fdt print /led pin // 打印/led節點的pin屬性 8 nand erase device_tree // 擦除flash分區 9 nand write.jffs2 32000000 device_tree // 把修改后的dtb文件寫入flash分區
3 u-boot對dtb的支持
dtb可以以兩種形式編譯到uboot的鏡像中
(1)dtb和uboot的bin文件分離
現在的uboot已經做得和kernel很像,最主要的一點是,uboot也使用了dtb的方法,將設備樹和代碼分離開來(當然可以通過宏來控制)。
1 CONFIG_OF_CONTROL=y 2 // 用於表示是否使用了dtb的方式 3 4 CONFIG_OF_SEPARATE=y 5 // 是否將dtb和uboot分離表一
(2)dtb集成到uboot的bin文件內部;
(3)通過fdtcontroladdr環境變量來指定dtb的地址.
4 u-boot如何獲取dtb
在uboot初始化過程中,需要對dtb做兩個操作:
(1)獲取dtb的地址,並且驗證dtb的合法性
(2)根據你編譯的是集成還是分離,如果是集成的話,需要為dtb預留內存空間並進行relocate
(3)重新獲取一次dtb的地址,bootm傳遞給內核
4.1 獲取dtb的地址,並且驗證dtb的合法性
在系統起來的時候,進行一串的初始化函數中,fdtdec_setup會對dtb進行合法性驗證
1 static const init_fnc_t init_sequence_f[] = { 2 ... 3 setup_mon_len, 4 #ifdef CONFIG_OF_CONTROL 5 fdtdec_setup, 6 #endif 7 reserve_fdt, 8 ... 9 }
對應代碼如下: lib/fdtdec.c
1 int fdtdec_setup(void) 2 { 3 #if CONFIG_IS_ENABLED(OF_CONTROL) 4 # ifdef CONFIG_OF_EMBED 5 /* Get a pointer to the FDT */ 6 // 1. 當使用CONFIG_OF_EMBED的方式時,也就是dtb集成到uboot的bin文件中時,通過__dtb_dt_begin符號來獲取dtb地址 7 gd->fdt_blob = __dtb_dt_begin; 8 # elif defined CONFIG_OF_SEPARATE 9 # ifdef CONFIG_SPL_BUILD 10 /* FDT is at end of BSS unless it is in a different memory region */ 11 if (IS_ENABLED(CONFIG_SPL_SEPARATE_BSS)) 12 gd->fdt_blob = (ulong *)&_image_binary_end; 13 else 14 gd->fdt_blob = (ulong *)&__bss_end; 15 16 # elif defined CONFIG_FIT_EMBED 17 gd->fdt_blob = locate_dtb_in_fit(&_end); 18 19 if (gd->fdt_blob == NULL || gd->fdt_blob <= ((void *)&_end)) { 20 puts("Failed to find proper dtb in embedded FIT Image\n"); 21 return -1; 22 } 23 24 # else 25 /* FDT is at end of image */ 26 //2. 27 //當使用CONFIG_OF_SEPARATE的方式時,也就是dtb追加到uboot的bin文件后面時,通過_end符號來獲取dtb地址 28 gd->fdt_blob = (ulong *)&_end; 29 # endif 30 # elif defined(CONFIG_OF_BOARD) 31 /* Allow the board to override the fdt address. */ 32 gd->fdt_blob = board_fdt_blob_setup(); 33 # elif defined(CONFIG_OF_HOSTFILE) 34 if (sandbox_read_fdt_from_file()) { 35 puts("Failed to read control FDT\n"); 36 return -1; 37 } 38 # endif 39 //3. 可以通過環境變量fdtcontroladdr來指定gd->fdt_blob,也就是指定fdt的地址 40 # ifndef CONFIG_SPL_BUILD 41 /* Allow the early environment to override the fdt address */ 42 gd->fdt_blob = (void *)env_get_ulong("fdtcontroladdr", 16, 43 (uintptr_t)gd->fdt_blob); 44 # endif 45 #endif 46 return fdtdec_prepare_fdt(); 47 }
該函數主要做了一下幾件事情:
(1)對於集成的dtb的u-boot,使用__dtb_dt_begin符號來獲取dtb地址,如果是分離式的,通過_end符號來獲取dtb地址,同時也支持通過環境參數fdtcontroladdr來配置
(2)然后通過fdtdec_prepare_fdt來對fdt進行合法性檢查,判斷dtb是否存在,以及是否有四個字節對齊。然后再調用fdt_check_header看看頭部是否正常,fdt_check_header主要是檢查dtb的magic是否正確
1 int fdtdec_prepare_fdt(void) 2 { 3 if (!gd->fdt_blob || ((uintptr_t)gd->fdt_blob & 3) || 4 fdt_check_header(gd->fdt_blob)) { 5 #ifdef CONFIG_SPL_BUILD 6 puts("Missing DTB\n"); 7 #else 8 puts("No valid device tree binary found - please append one to U-Boot binary, use u-boot-dtb.bin or define CONFIG_OF_EMBED. For sandbox, use -d <file.dtb>\n"); 9 # ifdef DEBUG 10 if (gd->fdt_blob) { 11 printf("fdt_blob=%p\n", gd->fdt_blob); 12 print_buffer((ulong)gd->fdt_blob, gd->fdt_blob, 4, 13 32, 0); 14 } 15 # endif 16 #endif 17 return -1; 18 } 19 return 0; 20 }
4.2 為dtb分配新的內存地址
當使用CONFIG_OF_EMBED方式時,也就是dtb集成在uboot中的時候,relocate uboot過程中也會把dtb一起relocate,所以這里就不需要處理。當為分離式要為該dtb在內存中分配一片空間即可
1 static int reserve_fdt(void) 2 { 3 #ifndef CONFIG_OF_EMBED 4 /* 5 * If the device tree is sitting immediately above our image then we 6 * must relocate it. If it is embedded in the data section, then it 7 * will be relocated with other data. 8 */ 9 if (gd->fdt_blob) { 10 gd->fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32); 11 12 gd->start_addr_sp -= gd->fdt_size; 13 gd->new_fdt = map_sysmem(gd->start_addr_sp, gd->fdt_size); 14 debug("Reserving %lu Bytes for FDT at: %08lx\n", 15 gd->fdt_size, gd->start_addr_sp); 16 } 17 #endif 18 19 return 0; 20 } 21 22 static int reloc_fdt(void) 23 { 24 #ifndef CONFIG_OF_EMBED 25 if (gd->flags & GD_FLG_SKIP_RELOC) 26 return 0; 27 if (gd->new_fdt) { 28 memcpy(gd->new_fdt, gd->fdt_blob, gd->fdt_size); 29 gd->fdt_blob = gd->new_fdt; 30 } 31 #endif 32 33 return 0; 34 }
參考博文:
https://blog.csdn.net/kunkliu/article/details/82707282