linux設備驅動(19)設備樹詳解3-u-boot傳輸dts


1 傳遞dtb給內核

對於傳統bootloader提供兩種工作模式:一是啟動加載模式(start loading),一是下載模式(downloading)
工作在啟動加載模式時,bootloader會自動執行bootcmd命令,
比如:bootcmd=“nand read 0x100000 0x80000000 0x300000; bootm 0x80000000”
uboot首先把內核鏡像拷貝到內存地址為0x80000000的地方,然后執行bootm 0x80000000命令。
bootm命令實際上調用的是do_bootm_linux函數:

theKernel (0,bd->bi_arch_number, bd->bi_boot_params);
r0,r1,r2三個寄存器的設置
r0一般設置為0;
r1一般設置為machine id (在使用設備樹時該參數沒有被使用);是讓內核知道是哪個CPU,從而調用對應的初始化函數
r2一般設置ATAGS或DTB的開始地址;

以前沒有使用設備樹時,需要bootloader傳一個machine id給內核,內核啟動的時候會根據這個machine_id來比較內核machine_desc(機器描述結構體)中的.nr,如果相等,就選中了對應的machine_desc(機器描述結構體)),然后調用machine_desc(機器描述結構體)中的.init(初始化函數)。現在使用設備樹的話,這個參數就不需要設置了。
對於我們拿到一個新的bootloader,我們怎么能使代碼支持dtb模式,我們需要配置#define CONFIG_OF_LIBFDT,可讓u-boot支持內核設備樹dts,加載命令如下:

  bootm <uImage_addr> <initrd_addr> <dtb_addr>
//bootm + uImage地址 + ramdisk地址 + 設備樹鏡像地址

如:

1 //1. 下載內核uImage到內存0x30007FC0
2 tftp 0x30007FC0 uImage
3 //2. 下載dtb到內存32000000
4 tftp 0x30001000 s3c2440-smdk2440.dtb
5 //3. - 表示不使用ramdisk加載,如果使用ramdisk則提供其加載地址
6  bootm 0x30007FC0 - 0x30001000

對於我們下載dtb的地址0x32000000,這個地址有什么要求呢?是隨意選的地址就可以,還是要遵循什么原則呢?

(1)不要破壞u-boot本身

(2)不要破壞內核本身: 內核本身的空間不能占用, 內核要用到的內存區域也不能占用

對於該問題,我們拿了一塊2440的地址空間分配圖來說明該問題。

對於dtb的存放,只能存放在空閑區,並且不能與其他區有重合的地方。

2 fdt命令查看設備樹

如果修改設備樹中的led設備引腳,有兩種辦法:

(1)修改dts文件,重新編譯得到dtb並上傳燒寫

(2)使用uboot提供的一些命令來修改dtb文件,修改后再把它保存到板子上,以后就使用這個修改后的dtb文件移動值,也就是通過memmove處理

對於u-boot提供了fdt的相關命令

 1 "addr [-c]  <addr> [<length>]   - Set the [control] fdt location to <addr>\n"
 2     "fdt move   <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active\n"
 3     "fdt resize [<extrasize>]            - Resize fdt to size + padding to 4k addr + some optional <extrasize> if needed\n"
 4     "fdt print  <path> [<prop>]          - Recursive print starting at <path>\n"
 5     "fdt list   <path> [<prop>]          - Print one level starting at <path>\n"
 6     "fdt get value <var> <path> <prop>   - Get <property> and store in <var>\n"
 7     "fdt get name <var> <path> <index>   - Get name of node <index> and store in <var>\n"
 8     "fdt get addr <var> <path> <prop>    - Get start address of <property> and store in <var>\n"
 9     "fdt get size <var> <path> [<prop>]  - Get size of [<property>] or num nodes and store in <var>\n"
10     "fdt set    <path> <prop> [<val>]    - Set <property> [to <val>]\n"
11     "fdt mknode <path> <node>            - Create a new node after <path>\n"
12     "fdt rm     <path> [<prop>]          - Delete the node or <property>\n"
13     "fdt header                          - Display header info\n"
14     "fdt bootcpu <id>                    - Set boot cpuid\n"
15     "fdt memory <addr> <size>            - Add/Update memory node\n"
16     "fdt rsvmem print                    - Show current mem reserves\n"
17     "fdt rsvmem add <addr> <size>        - Add a mem reserve\n"
18     "fdt rsvmem delete <index>           - Delete a mem reserves\n"
19     "fdt chosen [<start> <end>]          - Add/update the /chosen branch in the tree\n"
20     "                                        <start>/<end> - initrd start/end addr\n"

實例

1 nand read.jffs2 32000000 device_tree      // 從flash讀出dtb文件到內存(0x32000000)
2 fdt addr 32000000                                      // 告訴fdt, dtb文件在哪
3 fdt print /led pin                                           // 打印/led節點的pin屬性
4 fdt get value XXX /led pin                           // 讀取/led節點的pin屬性, 並且賦給環境變量XXX
5 print XXX                                                    // 打印環境變量XXX的值
6 fdt set /led pin <0x00050005>                     // 設置/led節點的pin屬性
7 fdt print /led pin                                            // 打印/led節點的pin屬性
8 nand erase device_tree                              // 擦除flash分區
9 nand write.jffs2 32000000 device_tree      // 把修改后的dtb文件寫入flash分區

3 u-boot對dtb的支持

dtb可以以兩種形式編譯到uboot的鏡像中

(1)dtb和uboot的bin文件分離

現在的uboot已經做得和kernel很像,最主要的一點是,uboot也使用了dtb的方法,將設備樹和代碼分離開來(當然可以通過宏來控制)。

1 CONFIG_OF_CONTROL=y
2 // 用於表示是否使用了dtb的方式
3 
4 CONFIG_OF_SEPARATE=y
5 // 是否將dtb和uboot分離表一

(2)dtb集成到uboot的bin文件內部;

(3)通過fdtcontroladdr環境變量來指定dtb的地址.

4 u-boot如何獲取dtb

在uboot初始化過程中,需要對dtb做兩個操作:

(1)獲取dtb的地址,並且驗證dtb的合法性

(2)根據你編譯的是集成還是分離,如果是集成的話,需要為dtb預留內存空間並進行relocate

(3)重新獲取一次dtb的地址,bootm傳遞給內核

4.1  獲取dtb的地址,並且驗證dtb的合法性

在系統起來的時候,進行一串的初始化函數中,fdtdec_setup會對dtb進行合法性驗證

1 static const init_fnc_t init_sequence_f[] = {
2 ...
3     setup_mon_len,
4 #ifdef CONFIG_OF_CONTROL
5     fdtdec_setup,
6 #endif
7     reserve_fdt,
8 ...
9 }

對應代碼如下: lib/fdtdec.c

 1 int fdtdec_setup(void)
 2 {
 3 #if CONFIG_IS_ENABLED(OF_CONTROL)
 4 # ifdef CONFIG_OF_EMBED
 5     /* Get a pointer to the FDT */
 6 // 1. 當使用CONFIG_OF_EMBED的方式時,也就是dtb集成到uboot的bin文件中時,通過__dtb_dt_begin符號來獲取dtb地址
 7     gd->fdt_blob = __dtb_dt_begin;
 8 # elif defined CONFIG_OF_SEPARATE
 9 #  ifdef CONFIG_SPL_BUILD
10     /* FDT is at end of BSS unless it is in a different memory region */
11     if (IS_ENABLED(CONFIG_SPL_SEPARATE_BSS))
12         gd->fdt_blob = (ulong *)&_image_binary_end;
13     else
14         gd->fdt_blob = (ulong *)&__bss_end;
15 
16 #  elif defined CONFIG_FIT_EMBED
17     gd->fdt_blob = locate_dtb_in_fit(&_end);
18 
19     if (gd->fdt_blob == NULL || gd->fdt_blob <= ((void *)&_end)) {
20         puts("Failed to find proper dtb in embedded FIT Image\n");
21         return -1;
22     }
23 
24 #  else
25     /* FDT is at end of image */
26 //2. 
27 //當使用CONFIG_OF_SEPARATE的方式時,也就是dtb追加到uboot的bin文件后面時,通過_end符號來獲取dtb地址
28     gd->fdt_blob = (ulong *)&_end;
29 #  endif
30 # elif defined(CONFIG_OF_BOARD)
31     /* Allow the board to override the fdt address. */
32     gd->fdt_blob = board_fdt_blob_setup();
33 # elif defined(CONFIG_OF_HOSTFILE)
34     if (sandbox_read_fdt_from_file()) {
35         puts("Failed to read control FDT\n");
36         return -1;
37     }
38 # endif
39 //3. 可以通過環境變量fdtcontroladdr來指定gd->fdt_blob,也就是指定fdt的地址
40 # ifndef CONFIG_SPL_BUILD
41     /* Allow the early environment to override the fdt address */
42     gd->fdt_blob = (void *)env_get_ulong("fdtcontroladdr", 16,
43                         (uintptr_t)gd->fdt_blob);
44 # endif
45 #endif
46     return fdtdec_prepare_fdt();
47 }

該函數主要做了一下幾件事情:

(1)對於集成的dtb的u-boot,使用__dtb_dt_begin符號來獲取dtb地址,如果是分離式的,通過_end符號來獲取dtb地址,同時也支持通過環境參數fdtcontroladdr來配置
(2)然后通過fdtdec_prepare_fdt來對fdt進行合法性檢查,判斷dtb是否存在,以及是否有四個字節對齊。然后再調用fdt_check_header看看頭部是否正常,fdt_check_header主要是檢查dtb的magic是否正確

 1 int fdtdec_prepare_fdt(void)
 2 {
 3     if (!gd->fdt_blob || ((uintptr_t)gd->fdt_blob & 3) ||
 4         fdt_check_header(gd->fdt_blob)) {
 5 #ifdef CONFIG_SPL_BUILD
 6         puts("Missing DTB\n");
 7 #else
 8         puts("No valid device tree binary found - please append one to U-Boot binary, use u-boot-dtb.bin or define CONFIG_OF_EMBED. For sandbox, use -d <file.dtb>\n");
 9 # ifdef DEBUG
10         if (gd->fdt_blob) {
11             printf("fdt_blob=%p\n", gd->fdt_blob);
12             print_buffer((ulong)gd->fdt_blob, gd->fdt_blob, 4,
13                      32, 0);
14         }
15 # endif
16 #endif
17         return -1;
18     }
19     return 0;
20 }

4.2 為dtb分配新的內存地址

當使用CONFIG_OF_EMBED方式時,也就是dtb集成在uboot中的時候,relocate uboot過程中也會把dtb一起relocate,所以這里就不需要處理。當為分離式要為該dtb在內存中分配一片空間即可

 1 static int reserve_fdt(void)
 2 {
 3 #ifndef CONFIG_OF_EMBED
 4     /*
 5      * If the device tree is sitting immediately above our image then we
 6      * must relocate it. If it is embedded in the data section, then it
 7      * will be relocated with other data.
 8      */
 9     if (gd->fdt_blob) {
10         gd->fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32);
11 
12         gd->start_addr_sp -= gd->fdt_size;
13         gd->new_fdt = map_sysmem(gd->start_addr_sp, gd->fdt_size);
14         debug("Reserving %lu Bytes for FDT at: %08lx\n",
15               gd->fdt_size, gd->start_addr_sp);
16     }
17 #endif
18 
19     return 0;
20 }
21 
22 static int reloc_fdt(void)
23 {
24 #ifndef CONFIG_OF_EMBED
25     if (gd->flags & GD_FLG_SKIP_RELOC)
26         return 0;
27     if (gd->new_fdt) {
28         memcpy(gd->new_fdt, gd->fdt_blob, gd->fdt_size);
29         gd->fdt_blob = gd->new_fdt;
30     }
31 #endif
32 
33     return 0;
34 }

參考博文:

https://blog.csdn.net/kunkliu/article/details/82707282

https://blog.csdn.net/u012489236/article/details/97256928

https://blog.csdn.net/thisway_diy/article/details/84338249


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM