Linux设备树详解(三)u-boot设备树的传递

前面两节介绍了设备的基本概念、编译、结构的组成,本章讨论的主要内容为

dtb如何通过Bootloader引导程序加载到内核
bootloader如何解析dbt
bootloader支持哪些dtb的操作
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);
1
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
2
比如:

//1. 下载内核uImage到内存0x30007FC0
tftp 0x30007FC0 uImage
//2. 下载dtb到内存32000000
tftp 0x30001000 s3c2440-smdk2440.dtb
//3. - 表示不使用ramdisk加载,如果使用ramdisk则提供其加载地址
 bootm 0x30007FC0 - 0x30001000
1
2
3
4
5
6
对于我们下载dtb的地址0x32000000,这个地址有什么要求呢?是随意选的地址就可以,还是要遵循什么原则呢?

不要破坏u-boot本身
不要破坏内核本身: 内核本身的空间不能占用, 内核要用到的内存区域也不能占用
对于该问题,我们拿了一块2440的地址空间分配图来说明该问题

对于dtb的存放,只能存放在空闲区,并且不能与其他区有重合的地方。

在这里插入图片描述

2. fdt命令查看设备树
如果修改设备树中的led设备引脚,有两种办法

修改dts文件,重新编译得到dtb并上传烧写
使用uboot提供的一些命令来修改dtb文件,修改后再把它保存到板子上,以后就使用这个修改后的dtb文件移动值,也就是通过memmove处理
对于u-boot提供了fdt的相关命令

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
实例

nand read.jffs2 32000000 device_tree      // 从flash读出dtb文件到内存(0x32000000)
fdt addr 32000000                                      // 告诉fdt, dtb文件在哪
fdt print /led pin                                           // 打印/led节点的pin属性
fdt get value XXX /led pin                           // 读取/led节点的pin属性, 并且赋给环境变量XXX
print XXX                                                    // 打印环境变量XXX的值
fdt set /led pin <0x00050005>                     // 设置/led节点的pin属性
fdt print /led pin                                            // 打印/led节点的pin属性
nand erase device_tree                              // 擦除flash分区
nand write.jffs2 32000000 device_tree      // 把修改后的dtb文件写入flash分区
1
2
3
4
5
6
7
8
9
3. u-boot对dtb的支持
dtb可以以两种形式编译到uboot的镜像中

1.dtb和uboot的bin文件分离
现在的uboot已经做得和kernel很像,最主要的一点是,uboot也使用了dtb的方法,将设备树和代码分离开来(当然可以通过宏来控制)。

CONFIG_OF_CONTROL=y
// 用于表示是否使用了dtb的方式

CONFIG_OF_SEPARATE=y
// 是否将dtb和uboot分离表一
1
2
3
4
5
2.dtb集成到uboot的bin文件内部
3.通过fdtcontroladdr环境变量来指定dtb的地址
4. uboot中如何获取dtb
在uboot初始化过程中,需要对dtb做两个操作:

获取dtb的地址,并且验证dtb的合法性
根据你编译的是集成还是分离,如果是集成的话,需要为dtb预留内存空间并进行relocate
重新获取一次dtb的地址,bootm传递给内核
4.1 获取dtb的地址,并且验证dtb的合法性
在系统起来的时候,进行一串的初始化函数中,fdtdec_setup会对dtb进行合法性验证

static const init_fnc_t init_sequence_f[] = {
...
    setup_mon_len,
#ifdef CONFIG_OF_CONTROL
    fdtdec_setup,
#endif
    reserve_fdt,
...
}
1
2
3
4
5
6
7
8
9
对应代码如下: lib/fdtdec.c

int fdtdec_setup(void)
{
#if CONFIG_IS_ENABLED(OF_CONTROL)
# ifdef CONFIG_OF_EMBED
    /* Get a pointer to the FDT */
// 1. 当使用CONFIG_OF_EMBED的方式时,也就是dtb集成到uboot的bin文件中时,通过__dtb_dt_begin符号来获取dtb地址
    gd->fdt_blob = __dtb_dt_begin;
# elif defined CONFIG_OF_SEPARATE
#  ifdef CONFIG_SPL_BUILD
    /* FDT is at end of BSS unless it is in a different memory region */
    if (IS_ENABLED(CONFIG_SPL_SEPARATE_BSS))
        gd->fdt_blob = (ulong *)&_image_binary_end;
    else
        gd->fdt_blob = (ulong *)&__bss_end;

#  elif defined CONFIG_FIT_EMBED
    gd->fdt_blob = locate_dtb_in_fit(&_end);

    if (gd->fdt_blob == NULL || gd->fdt_blob <= ((void *)&_end)) {
        puts("Failed to find proper dtb in embedded FIT Image\n");
        return -1;
    }

#  else
    /* FDT is at end of image */
//2. 
//当使用CONFIG_OF_SEPARATE的方式时,也就是dtb追加到uboot的bin文件后面时,通过_end符号来获取dtb地址
    gd->fdt_blob = (ulong *)&_end;
#  endif
# elif defined(CONFIG_OF_BOARD)
    /* Allow the board to override the fdt address. */
    gd->fdt_blob = board_fdt_blob_setup();
# elif defined(CONFIG_OF_HOSTFILE)
    if (sandbox_read_fdt_from_file()) {
        puts("Failed to read control FDT\n");
        return -1;
    }
# endif
//3. 可以通过环境变量fdtcontroladdr来指定gd->fdt_blob,也就是指定fdt的地址
# ifndef CONFIG_SPL_BUILD
    /* Allow the early environment to override the fdt address */
    gd->fdt_blob = (void *)env_get_ulong("fdtcontroladdr", 16,
                        (uintptr_t)gd->fdt_blob);
# endif
#endif
    return fdtdec_prepare_fdt();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
该函数主要做了一下几件事情

对于集成的dtb的u-boot,使用__dtb_dt_begin符号来获取dtb地址,如果是分离式的,通过_end符号来获取dtb地址,同时也支持通过环境参数fdtcontroladdr来配置
然后通过fdtdec_prepare_fdt来对fdt进行合法性检查,判断dtb是否存在,以及是否有四个字节对齐。然后再调用fdt_check_header看看头部是否正常,fdt_check_header主要是检查dtb的magic是否正确
int fdtdec_prepare_fdt(void)
{
    if (!gd->fdt_blob || ((uintptr_t)gd->fdt_blob & 3) ||
        fdt_check_header(gd->fdt_blob)) {
#ifdef CONFIG_SPL_BUILD
        puts("Missing DTB\n");
#else
        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");
# ifdef DEBUG
        if (gd->fdt_blob) {
            printf("fdt_blob=%p\n", gd->fdt_blob);
            print_buffer((ulong)gd->fdt_blob, gd->fdt_blob, 4,
                     32, 0);
        }
# endif
#endif
        return -1;
    }
    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
4.2 为dtb分配新的内存地址空间
当使用CONFIG_OF_EMBED方式时,也就是dtb集成在uboot中的时候,relocate uboot过程中也会把dtb一起relocate,所以这里就不需要处理。当为分离式要为该dtb在内存中分配一片空间即可

static int reserve_fdt(void)
{
#ifndef CONFIG_OF_EMBED
    /*
     * If the device tree is sitting immediately above our image then we
     * must relocate it. If it is embedded in the data section, then it
     * will be relocated with other data.
     */
    if (gd->fdt_blob) {
        gd->fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32);

        gd->start_addr_sp -= gd->fdt_size;
        gd->new_fdt = map_sysmem(gd->start_addr_sp, gd->fdt_size);
        debug("Reserving %lu Bytes for FDT at: %08lx\n",
              gd->fdt_size, gd->start_addr_sp);
    }
#endif

    return 0;
}

static int reloc_fdt(void)
{
#ifndef CONFIG_OF_EMBED
    if (gd->flags & GD_FLG_SKIP_RELOC)
        return 0;
    if (gd->new_fdt) {
        memcpy(gd->new_fdt, gd->fdt_blob, gd->fdt_size);
        gd->fdt_blob = gd->new_fdt;
    }
#endif

    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
5. 参考文档:
https://blog.csdn.net/kunkliu/article/details/82707282
https://blog.csdn.net/thisway_diy/article/details/84338249
————————————————
版权声明:本文为CSDN博主「奇小葩」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u012489236/article/details/97256928

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值