Linux裝置樹的傳遞及Kernel中對裝置樹的分析
導讀 | 當 U-Boot 將裝置樹載入到記憶體指定位置後,ARM 核心的 SoC 以通用暫存器 r2 來傳遞 dtb 在記憶體中的地址。kernel 獲取到該地址後對 dtb 檔案做進一步的處理。 |
當使用 bootm 載入 kernel 映象時(bootz 是對 bootm 的一種封裝以及功能擴充套件,實質一樣)。U-Boot 跳轉到 kernel 的入口函式是 boot_jump_
這個函式的 C 檔案在 arch/arm/lib 下,說明裝置樹的傳遞的方式是與 SoC 架構相關的。不同的 SoC 在 bring-up 時,這個函式格外重要,這是 U-Boot 與 kernel 之間銜接、互動資訊的一個關鍵 API。U-Boot 的這個函式執行結束後,將 CPU 的控制權完整的交給 kernel。
/* 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 的定義如下,供各種核心的 SoC 使用,每家廠商根據自己 CPU 的特點對各個成員進行不同的例化。
/* * Legacy and FIT format headers used by do_bootm() and do_bootm_<os>() * 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
這種屬於比較古老的一種方式了,目前基本不會採用。bi_boot_params 是一個存放核心啟動引數的地址,通常是在板級初始化中進行指定。
程式碼執行到此處,r2 是否為預期的值,一是可以透過列印的方式、再有使用除錯工具連上去確認。
解析分兩個階段,第一階段進行校驗以及啟動引數的再調整;第二階段完成裝置樹的解壓,也就是將裝置樹由 FDT 變成 EDT,建立 device_node。
kernel 啟動日誌中與裝置樹相關的第一條列印如下,也就是列印出當前硬體裝置的模型名,"OF: fdt: Machine model: V2P-CA9" 。
Booting Linux on physical CPU 0x0 Linux 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 2021 CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr=10c5387d CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache OF: 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/; #include "vexpress-v2m.dtsi" / { model = "V2P-CA9"; ... }
但這並不是 kernel 對裝置樹第一次進行處理的地方。在此之前已有其他的操作。函式呼叫棧如下:
setup_arch(char **cmdline_p) arch/arm/kernel/setup.c atags_vaddr = FDT_VIRT_BASE(__atags_pointer); setup_machine_fdt(void *dt_virt) arch/arm/kernel/devtree.c early_init_dt_verify() of_flat_dt_match_machine() drivers/of/fdt.c early_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: #ifdef CONFIG_XIP_KERNEL #ifndef CONFIG_XIP_DEFLATED_DATA .long _sdata @ r0 .long __data_loc @ r1 .long _edata_loc @ r2 #endif .long __bss_stop @ sp (temporary stack in .bss) #endif .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_type D 更新 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 時依據這個階段完成的工作情況進行對應的裝置註冊,供驅動程式碼使用。
原文來自:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2787004/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 第四課. Uboot對裝置樹的支援boot
- 裝置樹學習
- 裝置樹總結
- 裝置樹的引入與體驗
- 嵌入式Linux中的LED驅動控制(裝置樹方式)Linux
- 裝置樹解析函式函式
- Linux驅動之裝置樹的基礎知識Linux
- 【linux】驅動-9-裝置樹外掛Linux
- 嵌入式Linux中的LED驅動控制(裝置樹方式)(續)Linux
- Linux中的主次裝置Linux
- linux驅動之獲取裝置樹資訊Linux
- 裝置樹下的 LED 驅動實驗
- 【linux】驅動-8-一文解決裝置樹Linux
- 核心與裝置樹編譯與燒寫編譯
- linux mdev實現裝置符重對映/裝置符手動新增Linuxdev
- linux-中塊裝置檔案和字元裝置檔案的本質區別Linux字元
- AIX中的裸裝置AI
- 黑客通過樹莓派裝置攻入 NASA 網路黑客樹莓派
- 騰訊IOT之樹莓派物聯網裝置樹莓派
- Linux裝置節點Linux
- Linux裝置模型(3)Linux模型
- Linux裝置名稱的查詢Linux
- Android與物聯網裝置通訊-資料傳遞的本質Android
- Linux磁碟裝置基礎Linux
- linux檢視裝置命令Linux
- Linux裸裝置總結Linux
- 在Linux中,什麼是裝置驅動程式?如何安裝和解除安裝裝置驅動程式?Linux
- Linux裝置掛載和解除安裝Linux
- Linux配置和管理裝置對映多路徑multipathLinux
- 實用的 IEC61850 裝置裝置模擬器
- Linux 高可用仲裁裝置配置Linux
- Linux裝置模型(一) 概覽Linux模型
- linux udev裸裝置繫結Linuxdev
- IO裝置在OS中的權衡
- Android 與 Chrome OS 中針對大螢幕裝置的更新AndroidChrome
- 5.裝置中斷
- Linux裝置驅動程式學習----1.裝置驅動程式簡介Linux
- Linux 下使用 NetLink 檢測裝置的熱插拔Linux