1. 程式人生 > >第二課:linux裝置樹的規範(dts和dtb)

第二課:linux裝置樹的規範(dts和dtb)

第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>;
};

其中[email protected]

就表示node-name[@unit-address]其中的unit-address是記憶體首地址用來區分其它同名的裝置
可以把節點理解為目錄,也就是同一目錄下的子目錄名稱不能相同

有哪些需要注意的事項

比如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

  1. 在dtsi檔案裡,我們使用了各種C語言類似的巨集,這些巨集需要在被使用的地方展開;
  2. dtsi和dts檔案中,都是可讀性非常強的程式碼,容易引入錯誤,需要檢測這些錯誤;
  3. 在dts檔案裡,可以包含一個或多個dtsi檔案,這就意味著原始檔有很多,需要將它們編譯成一個唯一的檔案;
  4. dtsi和dts檔案中,後面屬性的值要覆蓋前面同名的屬性的值;

使用dtc工具將dtsi和dts變成dtb檔案時,該工具就自動完成前面的四個操作。
本節視訊的知識來源如下兩個文件,可以閱讀參考:

官方文件: https://www.devicetree.org/specifications/
核心文件: Documentation/devicetree/booting-without-of.txt

DTB檔案佈局

DTB檔案佈局如下:

可以看出整個DTB分為四個部分:struct ftd_headermemory reservation blockstructure blockstrings 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的內容:

  1. 首先是ftd_header結構體中的magic,為0xd00dfeed;
  2. 然後是totalsize,整個DTB檔案的大小;
  3. 再是off_dt_struct,即structure block的偏移地址;
  4. 再是off_dt_strings,即strings block的偏移地址;
  5. 再是off_mem_rsvmap,即memory reservation block的偏移地址;

因此,根據偏移,就能找到DTB每個部分的內容。
structure block儲存節點的資訊,節點的結構,和DTS中節點資訊對應如下:

其中節點資訊結構體如下:

struct {
	uint32_t len;
	uint32_t nameoff;
}

len表示val長度;
nameoff表示在string block的偏移;

總結

最後總結一下:

  1. DTB檔案可以分為四個部分:struct ftd_headermemory reservation blockstructure blockstrings block
  2. 最開始的為struct ftd_header,包含其它三個部分的偏移地址;
  3. memory reservation block記錄保留記憶體資訊;
  4. structure block儲存節點的資訊,節點的結構;
  5. strings block儲存屬性的名字,將屬性名字單獨作為字串儲存;

朋友您好,上面的內容過癮不?如果還想了解更多可以去淘寶搜尋"裝置樹" 進入"韋東山老師個人店"購買裝置樹視訊,29節只要69元,超級划算!