linux設備驅動程序-設備樹(0)-dtb格式

linux設備樹dtb格式

設備樹的通常操做方式是:開發人員根據開發需求編寫dts文件,而後使用dtc將dts編譯成dtb文件。html

dts文件是文本格式的文件,而dtb是二進制文件,在linux啓動時被加載到內存中,接下來咱們須要來分析設備樹dtb文件的格式。node

爲何要了解設備樹dtb文件的格式

dtb做爲二進制文件被加載到內存中,而後由內核讀取並進行解析,若是對dtb文件的格式不瞭解,那麼在看設備樹解析相關的內核代碼時將會步履維艱,而閱讀源代碼纔是瞭解設備樹最好的方式,因此,若是須要更透徹的瞭解設備樹解析的細節,第一步就是須要了解設備樹的格式。linux

注:本文部分參考:官方文檔git

dtb格式總覽

dtb的格式是這樣的:github

dtb header

但凡涉及到數據的記錄,就必定會有一個總的描述部分,就像磁盤的超級塊,書的目錄,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

設備樹的魔數,魔數其實就是一個用於識別的數字,表示設備樹的開始,linux dtb的魔數爲 0xd00dfeed.ui

totalsize

這個設備樹的size,也能夠理解爲所佔用的實際內存空間。code

off_dt_struct

offset to dt_struct,表示整個dtb中structure部分所在內存相對頭部的偏移地址orm

off_dt_strings

offset to dt_string,表示整個dtb中string部分所在內存相對頭部的偏移地址

off_mem_rsvmap

offset to memory reserve map,dtb中memory reserve map所在內存相對頭部的偏移地址,

version

設備樹的版本,截至目前的最新版本爲17.

last_comp_version

最新的兼容版本

boot_cpuid_phys

這部分僅在版本2中存在,後續版本再也不使用。

size_dt_strings

表示整個dtb中string部分的大小

size_dt_struct

表示整個dtb中struct部分的大小

alignment gap

中間的alignment gap部分表示對齊間隙,它並不是是必須的,它是否被提供以及大小由具體的平臺對數據對齊和的要求以及數據是否已經對齊來決定。

memory reserve map

memory reserve map:描述保留的內存部分,這個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字節)對齊。

device-tree structure

device-tree structure:每一個節點都會被描述爲一個struct,節點之間能夠嵌套,所以也會有嵌套的struct。

structure的的結構是這樣的:

  • 一個node開始信號,OF_DT_BEGIN_NODE,數據爲0x00000001
  • 對於版本1-3而言,這一部分是節點的全路徑,以/開頭,而對於版本16及以上,這部分只是unit name(root 除外,它沒有unit name),unit name是以0結尾的字符串
  • 可選的對齊字節
  • 對於每一個屬性字段:
    • 由OF_DT_PROP標識,數據爲0x00000003
    • 32位的數據,表示屬性的size
    • 32位的數據,表示屬性名在string block中的偏移地址
    • 屬性中的value data.
  • 若是有子節點,遞歸地對子節點進行描述。
  • 節點結束信號,OF_DT_END_NODE ,數據爲0x00000002.
    每一個節點的信息都按照上述結構被描述,須要注意的是,全部用於描述一個特定節點的屬性都必須在任何子節點以前定義,雖然設備樹的層次結構不會所以產生二義性,可是linux kernel的解析程序要求這麼作。

device-tree strings

device-tree strings:在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進制形式顯示:

整個dtb文件仍是比較簡單的,圖中的紅色框出的部分爲header部分的數據,能夠看到:

第1個四字節對應magic,數據爲 D00DFEED.
第2四字節對應totalsize,數據爲 000001BC,能夠由整張圖片看出,這個dtb文件的大小由0x0~0x1bb,大小恰好是0x1bc
第3個四字節對應off_dt_struct,數據爲00000038。
第4個四字節對應off_dt_strings,數據爲00000174,能夠由整張圖片看到,從0x174開始恰好是字符串開始的地方
第5個四字節對應off_mem_rsvmap,數據爲00000028
第6個四字節對應version,數據爲00000011,十進制爲17
第7個四字節對應last_comp_version,數據爲00000010,十進制爲16,表示兼容版本16
第8個四字節對應boot_cpuid_phys,數據爲00000000,僅在版本2中使用,這裏爲0
第9個四字節對應size_dt_strings,數據爲00000048,表示字符串總長。
第10個四字節對應size_dt_struct,數據爲0000013c,表示struct部分總長度。

整個頭部爲40字節,16進製爲0x28,從頭部信息中off_mem_rsvmap部分能夠獲得,reserve memory起始地址爲0x28,上文中提到,這一部分使用一個16字節的struct來描述,以一個全爲0的struct結尾。

後16字節全爲0,能夠看出,這裏並無設置reserve memory。

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結尾。

與根節點不一樣的是,子節點有一個unit name,即chosen,memory@80000000這些名稱,並不是節點中的.name屬性。

而整個結構的結束由00000009來描述。

通常而言,在32位系統中,dtc在編譯dts文件時會自動考慮對齊問題,因此對於設備樹的對齊字節,咱們只須要有所瞭解便可,並不會常接觸到。

好了,關於linux設備樹dtb文件格式的討論就到此爲止啦,若是朋友們對於這個有什麼疑問或者發現有文章中有什麼錯誤,歡迎留言

關於linux設備樹在內核啓動時的解析能夠參考個人另外兩篇博客:

linux設備樹的解析--dtb轉換成device_node
linux設備樹的解析--device_node轉換成platform_deviec

原創博客,轉載請註明出處!

祝各位早日實現項目叢中過,bug不沾身.

相關文章
相關標籤/搜索