一、背景
dtb作為二進制文件被加載到內存中,然后由內核讀取并進行解析,如果對dtb文件的格式不了解,那么在看設備樹解析相關的內核代碼時將會寸步難行,而閱讀源代碼才是了解設備樹最好的方式。
所以,如果需要更透徹的了解設備樹解析的細節,第一步就是需要了解設備樹的格式。
二、dtb的由來
設備樹的一般操作方式是:開發人員根據開發需求編寫dts文件,然后使用dtc將dts編譯成dtb文件。
DTB文件是由DTS文件通過dtc命令編譯生成的二進制文件。DTS文件不能直接被內核解析,需要編譯成DTB文件才可以直接被內核識別并解析使用的。
dts文件是文本格式的文件,而dtb是二進制文件,在linux啟動時被加載到內存中,接下來我們需要來分析設備樹dtb文件的格式。
三、dts和dtb文件的
編譯.dts文件生成DTB文件:
dtc -I dts -O dtb -o output.dtb input.dts
反匯編DTB文件生成.dts文件:換
dtc -I dtb -O dts -o output.dts input.dtb
四、dtb格式總覽
dtb的格式是這樣的:
4.1dtbheader
但凡涉及到數據的記錄,就一定會有一個總的描述部分,就像磁盤的超級塊,書的目錄,dtb當然也不例外,這個描述頭部就是dtb的header部分,通過這個header部分,用戶可以快速地了解到整個dtb的大致信息。header可以用這么一個結構體來描述:
struct fdt_header { fdt32_t magic; /* magic word FDT_MAGIC */ fdt32_t totalsize; /* total size of DT block */ fdt32_t off_dt_struct; /* offset to structure */ fdt32_t off_dt_strings; /* offset to strings */ fdt32_t off_mem_rsvmap; /* offset to memory reserve map */ fdt32_t version; /* format version */ fdt32_t last_comp_version; /* last compatible version */ /* version 2 fields below */ fdt32_t boot_cpuid_phys; /* Which physical CPU id we're booting on */ /* version 3 fields below */ fdt32_t size_dt_strings; /* size of the strings block */ /* version 17 fields below */ fdt32_t size_dt_struct; /* size of the structure block */ };
各字段含義如下:
magic
設備樹的魔數,魔數其實就是一個用于識別的數字,表示設備樹的開始,linuxdtb的魔數為0xd00dfeed.
totalsize
這個設備樹的size,也可以理解為所占用的實際內存空間。
off_dt_struct
offsettodt_struct,表示整個dtb中structure部分所在內存相對頭部的偏移地址
off_dt_strings
offsettodt_string,表示整個dtb中string部分所在內存相對頭部的偏移地址
off_mem_rsvmap
offsettomemoryreservemap,dtb中memoryreservemap所在內存相對頭部的偏移地址,
version
設備樹的版本,截至目前的最新版本為17.
last_comp_version
最新的兼容版本
boot_cpuid_phys
這部分僅在版本2中存在,后續版本不再使用。
size_dt_strings
表示整個dtb中string部分的大小
size_dt_struct
表示整個dtb中struct部分的大小
alignmentgap
中間的alignmentgap部分表示對齊間隙,它并非是必須的,它是否被提供以及大小由具體的平臺對數據對齊和的要求以及數據是否已經對齊來決定。
memoryreservemap
memoryreservemap:描述保留的內存部分,這個map的數據結構是這樣的:
{ uint64_t physical_address; uint64_t size; }
這部分存儲了此結構的列表,整個部分的結尾由一個數據為0的結構來表示(即physical_address和size都為0,總共16字節)。
這一部分的數據并非是節點中的memory子節點,而是在設備開始之前(也就是第一個花括號之前)定義的,例如:
/dts-v1/ /memreserve/ 0x10000000 0x100000 /*在結構提中的表示為 physical_address=0x10000000,size=0x100000 */ { ... }
這一部分的作用是告訴內核哪一些內存空間需要被保留而不應該被系統覆蓋使用,因為在內核啟動時常常需要動態申請大量的內存空間,只有提前進行注冊,用戶需要使用的內存才不會被系統征用而造成數據覆蓋。
值得一提的是,對于設備樹而言,即使不指定保留內存,系統也會默認為設備樹保留相應的內存空間。
同時,這一部分需要64位(8字節)對齊。
4.2device-treestructure
device-treestructure:每個節點都會被描述為一個struct,節點之間可以嵌套,因此也會有嵌套的struct。
structure的的結構是這樣的:
一個node開始信號,OF_DT_BEGIN_NODE,內容為:0x00000001
對于版本1-3而言,這一部分是節點的全路徑,以/開頭,而對于版本16及以上,這部分只是unitname(root除外,它沒有unitname),unitname是以0結尾的字符串
可選的對齊字節
對于每個屬性字段:
如果有子節點,遞歸地對子節點進行描述。
節點結束信號,OF_DT_END_NODE,數據為0x00000002.
每個節點的信息都按照上述結構被描述,需要注意的是,所有用于描述一個特定節點的屬性都必須在任何子節點之前定義,雖然設備樹的層次結構不會因此產生二義性,但是linuxkernel的解析程序要求這么做。
4.3device-treestrings
device-treestrings:在dtb中有大量的重復字符串,比如"model","compatile"等等,為了節省空間,將這些字符串統一放在某個地址,需要使用的時候直接使用索引來查看。
需要注意的是,屬性部分格式為key=value,key部分被放置在strings部分,而value部分的字符串并不會放在這一部分,而是直接放在structure中。
五、dtb文件解析示例
光說不練假把式,下面我就使用一個簡單的示例來剖析dtb的文件格式。
下述示例僅僅是一個演示demo,不針對任何平臺,為了演示方便,編寫了一個非常簡單的dts文件。
/dts-v1/; / { compatible = "hd,test_dts", "hd,test_xxx"; #address-cells = 0x1?>; #size-cells = 0x1?>; model = "HD test dts"; chosen { stdout-path = "/ocp/serial@ffff"; }; memory@80000000 { device_type = "memory"; reg = 0x80000000 0x10000000?>; }; led1:led@2000000 { compatible = "test_led"; #address-cells = 0x1?>; #size-cells = 0x1?>; reg = 0x200 0x4?>; }; };
編譯當前dts文件,獲取對應的dtb文件。
鑒于dtb文件為二進制文件,普通編輯器打開顯示亂碼,我們使用ultraEdit查看,它將數據以16進制形式顯示:
整個頭部為40字節,16進制為0x28,從頭部信息中off_mem_rsvmap部分可以得到,reservememory起始地址為0x28,上文中提到,這一部分使用一個16字節的struct來描述,以一個全為0的struct結尾。
后16字節全為0,可以看出,這里并沒有設置reservememory。
structure部分
上文回顧:每一個屬性都是以key=value的形式來描述,value部分可選。
偏移地址來到0x00000038(0x28+0x10),接下來8個字節為00000003,根據上述structure中的描述,這是OF_DT_PROP,即標示屬性的開始。
接下來4字節為00000018,表明該屬性的value部分size為24字節。
接下來4字節是當前屬性的key在string部分的偏移地址,這里是00000000,由頭部信息中off_dt_strings可以得到,string部分的開始為00000174,偏移地址為0,所以對應字符串為"compatible"。
之后就是value部分,這部分的數據是字符串,可以直接從圖片右側欄看出,總共24字節的字符串"hd,test_dts","hd,test_xxx",因為字符串之間以0結尾,所以程序可以識別出這是兩個字符串。
可以看出,到這里,compatible="hd,test_dts","hd,test_xxx";這個屬性就被描述完了,對于屬性的描述還是非常簡單的。
按照固有的規律,接下來就是對#address-cells=<0x1>的解析,然后是#size-cells=<0x1>...
然后就是遞歸的子節點chosen,memory@80000000等等都是按照上文中提到的structure解析規則來進行解析,最后以00000002結尾。
與根節點不同的是,子節點有一個unitname,即chosen,memory@80000000這些名稱,并非節點中的.name屬性。
而整個結構的結束由00000009來描述。
一般而言,在32位系統中,dtc在編譯dts文件時會自動考慮對齊問題,所以對于設備樹的對齊字節,我們只需要有所了解即可,并不會常接觸到。
文章來源:易百納技術社區
https://www.ebaina.com/articles/140000016352
-
Linux
+關注
關注
87文章
11304瀏覽量
209502
發布評論請先 登錄
相關推薦
評論