奇小葩讲设备树(3/5)-- Linux设备树详解(三)u-boot设备树的传递
前面兩節介紹了設備的基本概念、編譯、結構的組成,本章討論的主要內容為
1. 傳遞dtb給內核
對于傳統bootloader提供兩種工作模式:一是啟動加載模式(start loading),一是下載模式(downloading)
工作在啟動加載模式時,bootloader會自動執行bootcmd命令,
比如:bootcmd=“nand read 0x100000 0x80000000 0x300000; bootm 0x80000000”
uboot首先把內核鏡像拷貝到內存地址為0x80000000的地方,然后執行bootm 0x80000000命令。
bootm命令實際上調用的是do_bootm_linux函數:
- 1
r0,r1,r2三個寄存器的設置
以前沒有使用設備樹時,需要bootloader傳一個machine id給內核,內核啟動的時候會根據這個machine_id來比較內核machine_desc(機器描述結構體)中的.nr,如果相等,就選中了對應的machine_desc(機器描述結構體)),然后調用machine_desc(機器描述結構體)中的.init(初始化函數)。現在使用設備樹的話,這個參數就不需要設置了。
對于我們拿到一個新的bootloader,我們怎么能使代碼支持dtb模式,我們需要配置#define CONFIG_OF_LIBFDT,可讓u-boot支持內核設備樹dts,加載命令如下:
比如:
//1. 下載內核uImage到內存0x30007FC0 tftp 0x30007FC0 uImage //2. 下載dtb到內存32000000 tftp 0x30001000 s3c2440-smdk2440.dtb //3. - 表示不使用ramdisk加載,如果使用ramdisk則提供其加載地址bootm 0x30007FC0 - 0x30001000對于我們下載dtb的地址0x32000000,這個地址有什么要求呢?是隨意選的地址就可以,還是要遵循什么原則呢?
對于該問題,我們拿了一塊2440的地址空間分配圖來說明該問題
對于dtb的存放,只能存放在空閑區,并且不能與其他區有重合的地方。
2. fdt命令查看設備樹
如果修改設備樹中的led設備引腳,有兩種辦法
對于u-boot提供了fdt的相關命令
"addr [-c] <addr> [<length>] - Set the [control] fdt location to <addr>\n""fdt move <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active\n""fdt resize [<extrasize>] - Resize fdt to size + padding to 4k addr + some optional <extrasize> if needed\n""fdt print <path> [<prop>] - Recursive print starting at <path>\n""fdt list <path> [<prop>] - Print one level starting at <path>\n""fdt get value <var> <path> <prop> - Get <property> and store in <var>\n""fdt get name <var> <path> <index> - Get name of node <index> and store in <var>\n""fdt get addr <var> <path> <prop> - Get start address of <property> and store in <var>\n""fdt get size <var> <path> [<prop>] - Get size of [<property>] or num nodes and store in <var>\n""fdt set <path> <prop> [<val>] - Set <property> [to <val>]\n""fdt mknode <path> <node> - Create a new node after <path>\n""fdt rm <path> [<prop>] - Delete the node or <property>\n""fdt header - Display header info\n""fdt bootcpu <id> - Set boot cpuid\n""fdt memory <addr> <size> - Add/Update memory node\n""fdt rsvmem print - Show current mem reserves\n""fdt rsvmem add <addr> <size> - Add a mem reserve\n""fdt rsvmem delete <index> - Delete a mem reserves\n""fdt chosen [<start> <end>] - Add/update the /chosen branch in the tree\n"" <start>/<end> - initrd start/end addr\n"實例
nand read.jffs2 32000000 device_tree // 從flash讀出dtb文件到內存(0x32000000) fdt addr 32000000 // 告訴fdt, dtb文件在哪 fdt print /led pin // 打印/led節點的pin屬性 fdt get value XXX /led pin // 讀取/led節點的pin屬性, 并且賦給環境變量XXX print XXX // 打印環境變量XXX的值 fdt set /led pin <0x00050005> // 設置/led節點的pin屬性 fdt print /led pin // 打印/led節點的pin屬性 nand erase device_tree // 擦除flash分區 nand write.jffs2 32000000 device_tree // 把修改后的dtb文件寫入flash分區3. u-boot對dtb的支持
dtb可以以兩種形式編譯到uboot的鏡像中
- 1.dtb和uboot的bin文件分離
現在的uboot已經做得和kernel很像,最主要的一點是,uboot也使用了dtb的方法,將設備樹和代碼分離開來(當然可以通過宏來控制)。
CONFIG_OF_CONTROL=y // 用于表示是否使用了dtb的方式CONFIG_OF_SEPARATE=y // 是否將dtb和uboot分離表一- 2.dtb集成到uboot的bin文件內部
- 3.通過fdtcontroladdr環境變量來指定dtb的地址
4. uboot中如何獲取dtb
在uboot初始化過程中,需要對dtb做兩個操作:
4.1 獲取dtb的地址,并且驗證dtb的合法性
在系統起來的時候,進行一串的初始化函數中,fdtdec_setup會對dtb進行合法性驗證
static const init_fnc_t init_sequence_f[] = { ...setup_mon_len, #ifdef CONFIG_OF_CONTROLfdtdec_setup, #endifreserve_fdt, ... }對應代碼如下: lib/fdtdec.c
int fdtdec_setup(void) { #if CONFIG_IS_ENABLED(OF_CONTROL) # ifdef CONFIG_OF_EMBED/* Get a pointer to the FDT */ // 1. 當使用CONFIG_OF_EMBED的方式時,也就是dtb集成到uboot的bin文件中時,通過__dtb_dt_begin符號來獲取dtb地址gd->fdt_blob = __dtb_dt_begin; # elif defined CONFIG_OF_SEPARATE # ifdef CONFIG_SPL_BUILD/* FDT is at end of BSS unless it is in a different memory region */if (IS_ENABLED(CONFIG_SPL_SEPARATE_BSS))gd->fdt_blob = (ulong *)&_image_binary_end;elsegd->fdt_blob = (ulong *)&__bss_end;# elif defined CONFIG_FIT_EMBEDgd->fdt_blob = locate_dtb_in_fit(&_end);if (gd->fdt_blob == NULL || gd->fdt_blob <= ((void *)&_end)) {puts("Failed to find proper dtb in embedded FIT Image\n");return -1;}# else/* FDT is at end of image */ //2. //當使用CONFIG_OF_SEPARATE的方式時,也就是dtb追加到uboot的bin文件后面時,通過_end符號來獲取dtb地址gd->fdt_blob = (ulong *)&_end; # endif # elif defined(CONFIG_OF_BOARD)/* Allow the board to override the fdt address. */gd->fdt_blob = board_fdt_blob_setup(); # elif defined(CONFIG_OF_HOSTFILE)if (sandbox_read_fdt_from_file()) {puts("Failed to read control FDT\n");return -1;} # endif //3. 可以通過環境變量fdtcontroladdr來指定gd->fdt_blob,也就是指定fdt的地址 # ifndef CONFIG_SPL_BUILD/* Allow the early environment to override the fdt address */gd->fdt_blob = (void *)env_get_ulong("fdtcontroladdr", 16,(uintptr_t)gd->fdt_blob); # endif #endifreturn fdtdec_prepare_fdt(); }該函數主要做了一下幾件事情
4.2 為dtb分配新的內存地址空間
當使用CONFIG_OF_EMBED方式時,也就是dtb集成在uboot中的時候,relocate uboot過程中也會把dtb一起relocate,所以這里就不需要處理。當為分離式要為該dtb在內存中分配一片空間即可
static int reserve_fdt(void) { #ifndef CONFIG_OF_EMBED/** If the device tree is sitting immediately above our image then we* must relocate it. If it is embedded in the data section, then it* will be relocated with other data.*/if (gd->fdt_blob) {gd->fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32);gd->start_addr_sp -= gd->fdt_size;gd->new_fdt = map_sysmem(gd->start_addr_sp, gd->fdt_size);debug("Reserving %lu Bytes for FDT at: %08lx\n",gd->fdt_size, gd->start_addr_sp);} #endifreturn 0; }static int reloc_fdt(void) { #ifndef CONFIG_OF_EMBEDif (gd->flags & GD_FLG_SKIP_RELOC)return 0;if (gd->new_fdt) {memcpy(gd->new_fdt, gd->fdt_blob, gd->fdt_size);gd->fdt_blob = gd->new_fdt;} #endifreturn 0; }5. 參考文檔:
https://blog.csdn.net/kunkliu/article/details/82707282
https://blog.csdn.net/thisway_diy/article/details/84338249
總結
以上是生活随笔為你收集整理的奇小葩讲设备树(3/5)-- Linux设备树详解(三)u-boot设备树的传递的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 奇小葩讲设备树(2/5)-- Linux
- 下一篇: 奇小葩讲设备树(4/5)-- Linux