高通平臺中簡單瞭解DTB
本文將以系統開機的執行順序簡要分析kernel如何通過DTB檔案進行裝置的註冊。
注:本文不涉及裝置樹的語法和使用。
一、kernel如何獲取DTB檔案(ARM64架構)
裝置樹的原始檔為.dts和.dtsi檔案,經過裝置樹專用的編譯器編譯後生成一個二進位制的DTB(Device tree Blob)檔案。在系統啟動時,DTB檔案由bootloader載入進記憶體,此時,記憶體中的DTB成為FDT(Flat Device Tree)。Bootloader啟動kernel時,將FDT的地址傳給Kernel,在Kernel啟動的彙編階段,將FDT地址儲存在“x5”暫存器中,並定義8位元組變數“__fdt_pointer”,用來表示該地址,以供Kernel的C程式碼使用。
二、Kernel解析FDT
以ARM64的晶片為例。核心解析FDT的入口在檔案“arch/arm64/kernel/setup.c”中,由setup_arch(char** cmdline)呼叫,而setup_arch由start_kernel呼叫。程式碼流程如下圖:
圖 1
圖 1的執行順序為 "1"--->"1.1"--->"1.2"--->"2"。
如上圖(圖 1),setup_arch函式以“__fdt_pointer”為引數呼叫setup_machine_fdt函式。從彙編階段傳來的引數"__fdt_pointer"是一個FDT的地址,且是一個實體地址,不宜直接使用。setup_machine_fdt的一個工作(圖1中的1.1)就是將"__fdt_pointer"轉化為頁內偏移地址,並賦值給全域性變數"initial_boot_params",之後所有關於FDT的操作都將基於"initial_boot_params"變數。
/ {
model = "Qualcomm Technologies, Inc. MSM 8996";
compatible = "qcom,msm8996";
qcom,msm-id = <246 0x0>;
qcom,pmic-id = <0x20009 0x2000A 0x0 0x0>;
interrupt-parent = <&intc>;
chosen {
bootargs = "sched_enable_hmp=1 sched_enable_power_aware=1";
};
aliases {
sdhc1 = &sdhc_1;
...
};
cpus {
...
};
soc: soc { };
reserved-memory {
...
};
};
在上圖中我省略了很多子節點的內容,但這影響不大,因為我們只是要簡單認識裝置樹根節點下有哪些直接子節點。為什麼要認識它們?因為我接下來要講的東西和它們有關。
arm平臺的習慣是幾乎把所有外設節點放置在"SOC"節點下面。而跟根節點的直接子節點除了"SOC”節點之外,還有其他一些節點,如上面的示例,這些節點比較特殊,不像外設節點一樣生產device,然後通過匹配流程與驅動進行繫結,它們的屬性可能定義的是kernel啟動引數,比如"choosen"節點,這些資訊在kernel啟動的早期就會用到,而此時裝置模型還沒有初始化完成,因而先先解析這些引數供核心初始化,而其他他裝置節點則等待核心的其他一些模組初始化完成後再解析。
真正的裝置節點是如何別解析的呢?流程見下圖(arm64):
裝置節點的解析是在"arch_initcall_sync"階段,從arm64_device_init開始,呼叫of_platform_populate函式,並傳入裝置樹的根節點作為該函式的實參。之後的核心過程是為裝置樹下根節點下的每個直接子節點呼叫遞迴函式"of_platform_bus_create",該函式通過of_device_is_available判斷指定節點的狀態是否為disable,如果否,則為該節點建立一個platform_device結構體。通過遞迴呼叫,裝置樹根節點下每個非disable狀態的節點建立一個platform_device結構體。