第二課:linux裝置樹的規範(dts和dtb)
- 轉載請註明文章地址 http://wiki.100ask.org/Linux_devicetree
第01節_DTS格式
dts檔案通過編譯生成dtb格式檔案
屬性的定義
value取值型別
屬性名=值只有三種取值
- 第一種 <1 0x3 0x123> (一個或多個32位資料) arrays of cells
- 第二種 “字串” (用雙引號括起來的值)
- 第三種 [ 00 11 22] (byte string 是16進製表示的一個或者多個位元組)
- 一個 byte string必須用2位16進位制數表示 byte之間的空格可以省略,可組合多種型別的值,之間用逗號分開
示例內容
示例:
a. Arrays of cells : cell就是一個32位的資料interrupts = <17 0xc>;
b. 64bit資料使用2個cell來表示: clock-frequency = <0x00000001 0x00000000>;
c. A null-terminated string (有結束符的字串): compatible = “simple-bus”;
d. A bytestring(位元組序列) :local-mac-address = [00 00 12 34 56 78];
每個byte使用2個16進位制數來表示
e. 可以是各種值的組合, 用逗號隔開:
compatible = “ns16550”, “ns8250”;
example = <0xf00f0000 19>, //“a strange property format”;
##裝置節點如何定義?
[label:] node-name[@unit-address] {
[properties definitions]
[child nodes]
};
比如
[email protected]30000000 {
device_type = "memory";
reg = <0x30000000 0x4000000>;
};
可以把節點理解為目錄,也就是同一目錄下的子目錄名稱不能相同
有哪些需要注意的事項
比如2440裝置樹檔案必須要包含的
model = "SMDK2440";
compatible = "samsung,smdk2440";
#address-cells = <1>;//表示子節點的地址寬度是32位
#size-cells = <1>;//表示子節點的位寬是32位
特殊的、預設的屬性:
a.根節點:
#address-cells // 在它的子節點的reg屬性中, 使用多少個u32整數來描述地址(address)
#size-cells // 在它的子節點的reg屬性中, 使用多少個u32整數來描述大小(size)
compatible // 定義一系列的字串, 用來指定核心中哪個
例如 compatible = “samsung,smdk2440”, “samsung,s3c24xx”;
//它會優先去核心中尋找 samsung,smdk2440,如果沒有則尋找samsung,s3c24xx第二項,
*machine_desc可以支援本裝置
// 即這個板子相容哪些平臺
// uImage : smdk2410 smdk2440 mini2440==> machine_desc
model // 咱這個板子是什麼
// 比如有2款板子配置基本一致, 它們的compatible是一樣的
// 那麼就通過model來分辨這2款板子
b. /memory
device_type = “memory”;
reg // 用來指定記憶體的地址、大小
c. /chosen
bootargs // 核心command line引數, 跟u-boot中設定的bootargs作用一樣
d. /cpus
/cpus結點下有1個或多個cpu子結點, cpu子結點中用reg屬性用來標明自己是哪一個cpu,
*所以 /cpus 中有以下2個屬性:
#address-cells // 在它的子節點的reg屬性中, 使用多少個u32整數來描述地址(address)
#size-cells // 在它的子節點的reg屬性中, 使用多少個u32整數來描述大小(size) 必須設定為0
e. /cpus/cpu*
device_type = “cpu”;
reg // 表明自己是哪一個cpu
引用其他節點:
a. phandle : // 節點中的phandle屬性, 它的取值必須是唯一的(不要跟其他的phandle值一樣)
[email protected]10000000 {
phandle = <1>;
interrupt-controller;
};
another-device-node {
interrupt-parent = <1>; // 使用phandle值為1來引用上述節點
};
b. label:
PIC: [email protected]10000000 {
interrupt-controller;
};
another-device-node {
interrupt-parent = <&PIC>; // 使用label來引用上述節點,
// 使用lable時實際上也是使用phandle來引用,
// 在編譯dts檔案為dtb檔案時, 編譯器dtc會在dtb中插入phandle屬性
};
舉例說明
如果我想在dts中包含dtsi檔案
新建 jz2440.dtsi
拷貝jz2440.dts
dtsi檔案時dts的父節點可以直接引用,語法格式相同,
在dts檔案中引用dtsi,比如想修改某個引腳,但是又不想修改dtsi檔案,則只需要在dts檔案中覆蓋掉原來的的配置即可
#include "jz2440.dtsi"
/{
led {
ping = <S3C2410_GPF(6)>;
}
}
上傳檔案,
設定環境變數,編譯
如果我想反編譯dtb檔案怎麼做?
當前目錄下執行
./scripts/dtc/dtc -I 輸入檔案dtb -O 輸出檔案dts -o tmp.dts(輸出檔名) 指定dtb檔案所在位置
./scripts/dtc/dtc -I dtb -O dts -o tmp.dts arch/arm/boot/dts/jz2440.dtb
發現修改後暫存器值變了
再次修改
在dtsi中的led節點上新增lable
LED:led {
compatible = "jz2440_led";
pin = <S3C2410_GPF(5)>;
};
在dts檔案中覆蓋
&LED{
pin = <S3C2410_GPF(7)>;
};
上傳檔案,
設定環境變數,編譯,反編譯dtb檢視已經變化
官方文件:https://www.devicetree.org/specifications/
還可以檢視核心目錄\linux-4.19-rc3\Documentation\devicetree\usage-model.txt檔案
- Linux uses DT data for three major purposes:
-
- platform identification,
-
- runtime configuration, and
-
- device population.
比如你想保留某塊記憶體,保留記憶體的起始地址以及大小
/memreserve/ 0x33000000 0x10000
這些配置屬於runtime configuration
比如led就屬於device population.
第02節_DTB格式
這節視訊開始講解裝置樹的DTB格式。
DTS變成DTB
- 在dtsi檔案裡,我們使用了各種C語言類似的巨集,這些巨集需要在被使用的地方展開;
- dtsi和dts檔案中,都是可讀性非常強的程式碼,容易引入錯誤,需要檢測這些錯誤;
- 在dts檔案裡,可以包含一個或多個dtsi檔案,這就意味著原始檔有很多,需要將它們編譯成一個唯一的檔案;
- dtsi和dts檔案中,後面屬性的值要覆蓋前面同名的屬性的值;
使用dtc工具將dtsi和dts變成dtb檔案時,該工具就自動完成前面的四個操作。
本節視訊的知識來源如下兩個文件,可以閱讀參考:
官方文件: https://www.devicetree.org/specifications/
核心文件: Documentation/devicetree/booting-without-of.txt
DTB檔案佈局
DTB檔案佈局如下:
可以看出整個DTB分為四個部分:struct ftd_header
、memory reservation block
、structure block
、strings block
;
- struct ftd_header:用來表明各個分部的偏移地址,整個檔案的大小,版本號等;
- memory reservation block:在裝置樹中使用
/memreserve/
定義的保留記憶體資訊; - structure block:儲存節點的資訊,節點的結構;
- strings block:儲存屬性的名字,單獨作為字串儲存;
使用命令make dts
編譯JZ2440的裝置樹檔案,生成DTB檔案,再使用UltraEdit工具開啟,方便檢視16進位制,進行分析dts和dtb的對應關係。
struct ftd_header結構體的定義如下:
struct fdt_header {
uint32_t magic;
uint32_t totalsize;
uint32_t off_dt_struct;
uint32_t off_dt_strings;
uint32_t off_mem_rsvmap;
uint32_t version;
uint32_t last_comp_version;
uint32_t boot_cpuid_phys;
uint32_t size_dt_strings;
uint32_t size_dt_struct;
};
在DTB檔案中,資料的存放格式是大端模式,即數值的高位存放在低地址。
- 補充知識:大端(big endian)小端(little endian)
對於一個值,比如0x12345678,存放方式如下:
注意,大端模式和小端模式只針對數值,對於字串abc
,a在低地址,c在高地址。
分析DTB內容
下面開始分析DTB的內容:
- 首先是ftd_header結構體中的magic,為0xd00dfeed;
- 然後是totalsize,整個DTB檔案的大小;
- 再是off_dt_struct,即structure block的偏移地址;
- 再是off_dt_strings,即strings block的偏移地址;
- 再是off_mem_rsvmap,即memory reservation block的偏移地址;
因此,根據偏移,就能找到DTB每個部分的內容。
structure block儲存節點的資訊,節點的結構,和DTS中節點資訊對應如下:
其中節點資訊結構體如下:
struct {
uint32_t len;
uint32_t nameoff;
}
len表示val長度;
nameoff表示在string block的偏移;
總結
最後總結一下:
- DTB檔案可以分為四個部分:
struct ftd_header
、memory reservation block
、structure block
、strings block
; - 最開始的為struct ftd_header,包含其它三個部分的偏移地址;
- memory reservation block記錄保留記憶體資訊;
- structure block儲存節點的資訊,節點的結構;
- strings block儲存屬性的名字,將屬性名字單獨作為字串儲存;
朋友您好,上面的內容過癮不?如果還想了解更多可以去淘寶搜尋"裝置樹" 進入"韋東山老師個人店"購買裝置樹視訊,29節只要69元,超級划算!