分析核心原始碼,裝置樹
U-Boot需要將裝置樹在記憶體中的儲存地址傳給核心。該樹主要由三大部分組成:頭(Header)、結構塊(Structure block)、字串塊(Strings block)。
裝置樹在記憶體中的儲存佈局圖如下
1.1 頭(header)
1.2 結構塊(struct block)
扁平裝置樹結構塊是線性化的樹形結構,和字串塊一起組成了裝置樹的主體,以節點形式儲存目標板的裝置資訊
在結構塊中,節點起始標誌為32位常值巨集OF_DT_BEGIN_NODE,節點結束標誌為巨集OF_DT_END_NODE
子節點定義在節點結束標誌前。一個節點的基本結構如下所示:
(1).
節點起始標誌OF_DT_BEGIN_NODE(即0x0000_0001);
(2). 節點路徑或者節點單元名(version < 3以及節點路徑表示,version > 16時以節點單元名錶示);
(3). 填充位元組保證四位元組對齊;
(4). 節點屬性。
每個屬性以常值巨集OF_DT_PROP 開始,後面依次為屬性值的位元組長度、屬性名在字串塊中的偏移值,屬性值及位元組對齊填充段;
(5).
如果存在子節點,則定義子節點
(6). 節點結束標誌OF_DT_END_NODE(即0x0000_0002)。
歸納起來,一個節點可以概括為以OF_DT_BEGIN_NODE開始,節點路徑、屬性列表、子節點列表以及OF_DT_END_NODE結束的序列,每一個子節點自身也是類似的結構。
1.3 字串塊(Strings block)
為了節省空間,對於那些屬性名,尤其是很多屬性名是重複冗餘出現的,提取出來單獨存放到字串塊。這個塊中包含了很多有結束標誌的屬性名字串。 在裝置樹的結構塊中儲存了這些字串的偏移地址,因為可以很容易的查詢到屬性名字串。字串塊的引入節省嵌入式系統較為緊張的儲存空間。
1.4 裝置樹原始碼 DTS 表示
裝置樹原始碼檔案(.dts)以可讀可編輯的文字形式描述系統硬體配置裝置樹,支援 C/C++方式的註釋,該結構有一個唯一的根節點“/”,每個節點都有自己的名字並可以包含多個子節點。裝置樹的資料格式遵循了 Open Firmware IEEE standard
1275。這個裝置樹中有很多節點,每個節點都指定了節點單元名稱。每一個屬性後面都給出相應的值。以雙引號引出的內容為 ASCII 字串,以尖括號給出的是 32 位的16進位制值。這個樹結構是啟動 Linux 核心所需節點和屬性簡化後的集合,包括了根節點的基本模式資訊、CPU 和實體記憶體佈局,它還包括通過/chosen 節點傳遞給核心的命令列引數資訊。
1.5 machine_desc結構
核心提供了一個重要的結構體struct machine_desc ,這個結構體在核心移植中起到相當重要的作用,核心通過machine_desc結構體來控制系統體系架構相關部分的初始化。machine_desc結構體通過MACHINE_START巨集來初始化,在程式碼中, 通過在mian->setup_arch中呼叫setup_machine_fdt來獲取。
1.6 裝置節點結構體
1.7 屬性結構體
三、裝置樹初始化及解析
分析Linux核心的原始碼,可以看到其對扁平裝置樹的解析流程如下
進入這個setup_arch 其餘程式碼先省略
檢視setup_machine_fdt函式
這裡呼叫了early_init_dt_scan_nodes函式
來看看這個函式
看看這個fdt_next_node函式
上面還有一個很重要的地方
這裡,這裡對這個initial_boot_parems進行賦值,這裡這個指標指向了裝置樹的虛擬起始地址
在這裡進行偏移,詳細資訊看這裡的程式碼
chosen屬性
chosen 節點並不代表一個真實的裝置,只是作為一個為韌體和作業系統之間傳遞資料的地方,比如引導引數。chosen 節點裡的資料也不代表硬體。通常,chosen 節點在.dts 原始檔中為空,並在啟動時填充。在我們的示例系統中,韌體可以往 chosen 節點新增以下資訊: chosen { bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200"; //節點屬性linux,initrd-start = <0x85500000>; //節點屬性
linux,initrd-end = <0x855a3212>; //節點屬性
};
1.2 根節點"/" 裝置樹有且僅有一個根節點,即“/”,根節點下包含很多子節點,例入下圖,根節點為"/",根節點的子節點為"chosen",根節點的屬性包含"compatible","#address-cells","#size-cells","interrupt-parent"等。屬性model指明瞭目標板平臺或模組的名稱,屬性compatible值指明和目標板為同一系列的相容的開發板名稱。對於大多數32位平臺,屬性#address-cells和#size-cells的值一般為1。#address-cells = <1>; 1表示地址32位,2表示地址64位。#size-cells = <1>;1表示rangs的每部分佔一個cell,依此類推 {
compatible = "sprd,spx15";
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&gic>;
chosen {
bootargs = "loglevel=8 console=ttyS1,115200n8 init=/init root=/dev/ram0 rw";
linux,initrd-start = <0x85500000>;
linux,initrd-end = <0x855a3212>;
};
} 所以本函式就是讀取根節點的"#address-cells","#size-cells"屬性
1.3 memory節點
memory節點用於描述目標板上實體記憶體範圍,一般稱作/memory節點,可以有一個或多個。當有多個節點時,需要後跟單元地址予以區分;只有一個單元地址時,可以不寫單元地址,預設為0。此節點包含板上實體記憶體的屬性,一般要指定device_type(固定為"memory")和reg屬性。其中reg的屬性值以<起始地址 空間大小>的形式給出,如下示例中目標板記憶體起始地址為0x80000000,大小為0x20000000位元組。
memory {
device_type = "memory";
reg = <0x80000000 0x20000000>;
};
在看看解析裝置樹這一塊