當U-Boot將設備樹加載到內存指定位置后,ARM內核的SoC以通用寄存器r2來傳遞dtb在內存中的地址。kernel獲取到該地址后對dtb文件做進一步的處理。
#設備樹的傳遞
當使用bootm加載kernel鏡像時(bootz是對bootm的一種封裝以及功能擴展,實質一樣)。U-Boot跳轉到kernel的入口函數是boot_jump_linux
/*?Subcommand:?GO?*/static void boot_jump_linux(bootm_headers_t *images, int flag){...debug("## Transferring control to Linux (at address %08lx)" \"...\n", (ulong) kernel_entry);bootstage_mark(BOOTSTAGE_ID_RUN_OS);announce_and_cleanup(fake);if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)r2 = (unsigned long)images->ft_addr;else????r2?=?gd->bd->bi_boot_params;...}
r2作為存放設備樹地址的寄存器,其取值有兩種方式,分別是例化bootm_header_t這個數據結構的ft_addr,以及利用U-Boot的板級啟動參數作為設備樹的地址。
##bootm_header_t方式
數據結構bootm_header_t的定義如下,供各種內核的SoC使用,每家廠商根據自己CPU的特點對各個成員進行不同的例化。
/** Legacy and FIT format headers used by do_bootm() and do_bootm_() * routines.*/typedef struct bootm_headers {??...char *ft_addr; /* flat dev tree address */ulong ft_len; /* length of flat device tree */...} bootm_headers_t;
用bootm_header_t的方式,U-Boot需支持設備樹以及文件非空。

ft_len以及ft_addr屬于bootm_header_t,在U-Boot解析鏡像文件時,實例化這兩個成員。函數調用棧如下:
do_bootz(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])-bootz_start()--bootm_find_images(int?flag,?int?argc,?char?*const?argv[],?ulong?start,ulong?size)---boot_get_fdt(flag, argc, argv, IH_ARCH_DEFAULT, &images,&images.ft_addr, &images.ft_len);u-boot-v2021.04/common/image-fdt.c
##gd->bd->bi_boot_params方式
這種屬于比較古老的一種方式了,目前基本不會采用。bi_boot_params是一個存放內核啟動參數的地址,通常是在板級初始化中進行指定。
代碼執行到此處,r2是否為預期的值,一是可以通過打印的方式、再有使用調試工具連上去確認。
#kernel對設備樹的解析
解析分兩個階段,第一階段進行校驗以及啟動參數的再調整;第二階段完成設備樹的解壓,也就是將設備樹由FDT變成EDT,創建device_node。
##第一階段
kernel啟動日志中與設備樹相關的第一條打印如下,也就是打印出當前硬件設備的模型名,"OF: fdt: Machine model: V2P-CA9"
Booting Linux on physical CPU 0x0Linux version 5.4.124 (qemu@qemu) (gcc version 6.5.0 (Linaro GCC 6.5-2018.12)) #3 SMP Fri Jun 25 15:26:02 CST 2021CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr=10c5387dCPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cacheOF: fdt: Machine model: V2P-CA9
這個模型名是在設備樹文件的頭部定義的,定義當前設備的總體名稱。
// SPDX-License-Identifier: GPL-2.0/** ARM Ltd. Versatile Express** CoreTile Express A9x4* Cortex-A9 MPCore (V2P-CA9)** HBI-0191B*//dts-v1/;/ {model = "V2P-CA9";...}
但這并不是kernel對設備樹第一次進行處理的地方。在此之前已有其他的操作。函數調用棧如下:
setup_arch(char **cmdline_p) arch/arm/kernel/setup.catags_vaddr = FDT_VIRT_BASE(__atags_pointer);setup_machine_fdt(void *dt_virt) arch/arm/kernel/devtree.cearly_init_dt_verify()of_flat_dt_match_machine() drivers/of/fdt.cearly_init_dt_scan_nodes();????????__machine_arch_type?=?mdesc->nr;
第2行__atags_pointer是dtb在內存中的地址,這個地址在匯編階段(若鏡像為zImage,那么在解壓縮階段就完成了)便獲取到了。由于執行到setup_arch時mmu已經使能并且4K的段頁表也已經完成了映射,而U-Boot傳遞給kernel的設備樹fdt地址屬于物理地址,因此需要將物理地址轉換成虛擬地址。
head-common.S.align 2.type __mmap_switched_data, %object__mmap_switched_data:.long _sdata @ r0.long __data_loc @ r1.long _edata_loc @ r2.long __bss_stop @ sp (temporary stack in .bss).long __bss_start @ r0.long __bss_stop @ r1.long init_thread_union + THREAD_START_SP @ sp.long processor_id @ r0.long __machine_arch_type @ r1.long __atags_pointer @ r2
第一階段對設備樹的配置主要包括:
A 對dtb文件進行crc32校驗,檢測設備樹文件是否合法early_init_dt_verify()B early_init_dt_scan_nodes()/* 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);C 更新__machine_arch_typeD 更新chosen
上面這個chosen信息可以在kernel起來后再次查看做了哪些修改。
##第二階段
第二階段單純的是將設備樹ABI文件進行解壓縮,由FDT變成EDT,生成相應的device_node結點。
這個階段的函數調用棧如下:
unflatten_device_tree();*__unflatten_device_tree()/* First pass, scan for size */size = unflatten_dt_nodes(blob, NULL, dad, NULL);/* Second pass, do actual unflattening */unflatten_dt_nodes(blob, mem, dad, mynodes);unflatten_dt_nodes()populate_node()
device_nodes結點如下:

device_node創建完成后,kernel創建platform_device時依據這個階段完成的工作情況進行對應的設備注冊,供驅動代碼使用。
?
end
一口Linux?
關注,回復【1024】海量Linux資料贈送
精彩文章合集
文章推薦