4.1uboot对设备树的支持——传递dtb给内核

本节讲述uboot怎么把设备树(dtb)传给内核。

uboot只要把dtb写到内存,然后在启动内核时,把这块内存的起始地址传给内核(通过r2寄存器)。

uboot中内核启动命令

在uboot中,启动内核的命令是bootm

在有设备树和无设备树时,bootm命令的使用是不同的。

  • bootm <uImage_addr>                                                 //无设备树
  • bootm <uImage_addr> <initrd_addr> <dtb_addr>       //有设备树

比如,没有使用设备树时,通过指令将内核uImage读到内存0x3000 7FC0后,就可以使用bootm命令启动内核。

nand read.jffs2 0x30007FC0 kernel
bootm 0x30007FC0

使用设备树时,则还需要将dtb读到内存,假设读到起始地址为0x3200 0000的内存区域,然后再使用bootm命令启动内核,启动内核的时候,还要将dtb在内存中的起始地址也当做参数写入。

nand read.jffs2 0x30007FC0 kernel
nand read.jffs2 0x32000000 device_tree
bootm 0x30007FC0 - 0x32000000

需要注意的是,此时中间的参数代表的是<initrd_addr>内存文件系统的地址

当没有使用内存文件系统时,可以将它简写为一个减号(-)

bootm命令怎么把dtb_addr写入r2寄存器传给内核?

uboot使用bootm命令启动,那么bootm命令要怎么把dtb_addr写入r2寄存器传给内核呢?

根据ARM程序调用规则ATPCS),当一个函数被调用的时候,它的第一个参数使用r0寄存器来传递,第二个参数使用r1寄存器来传递,传参的数量超过一定值之后(一般来说,该值为4),才使用来传参。

假设,有一个函数:

int __add(int a, int b, int c);

调用这个函数:

add(1, 2, 3);

那么,1,2,3分别会通过r0,r1,r2寄存器来传递。

所以,可以创建一个C函数,传入三个参数,在第三个参数中传入dtb_addr

这样,当这个C函数被调用时,这三个传参就会被写到r0,r1,r2寄存器,dtb_add就会被传递到函数内部。

c_function(p0, p1, p2) // p0 => r0, p1 => r1, p2 => r2

实际上,是定义个一个函数指针th_kernel,指向内核的入口地址

然后,执行 th_kernel(0, machine_id, 0x32000000); 即可(使用设备时,machine_id未使用到,可以随便设置)。

看一下代码,bootm命令最终会调用到do_bootm_linux函数(\u-boot-1.1.6\lib_arm\armlinux.c)。

do_bootm_linux函数中,会定义一个函数指针theKernel,它指向内核的入口地址

函数的简单分析如下,最终通过theKernel调用函数启动内核

dtb_addr可以随便选吗?

问:dtb可以随便存放在内核的任意区域吗?

答:不可以,需要遵循两个原则

  1. 不要破坏uboot本身(dtb放到内存时,uboot还在运行,当然不能被破坏);
  2. 不要和内核使用的内存空间重合(dtb是给内核使用的,当然也不能破坏内核):
    1. 内核本身的空间不能占用;
    2. 内核要用到的内存区域也不能占用;(内核启动时一般会在它所处位置的下边放置页表, 这块空间(一般是0x4000即16K字节)不能被占用)

JZ2440内存使用情况:

                     ------------------------------
  0x33f80000       ->|    u-boot                  |
                     ------------------------------
                     |    u-boot所使用的内存(栈等)|
                     ------------------------------
                     |                            |
                     |                            |
                     |        空闲区域             |
                     |                            |
                     |                            |
                     |                            |
                     |                            |
                     ------------------------------
  0x30008000       ->|      zImage                |
                     ------------------------------  uImage = 64字节的头部+zImage
  0x30007FC0       ->|      uImage头部            |
                     ------------------------------
  0x30004000       ->|      内核创建的页表         |  head.S
                     ------------------------------
                     |                            |
                     |                            |
              -----> ------------------------------
              |
              |
              --- (内存基址 0x30000000)

其中,内核的加载地址可以通过mkimage工具来读取。

根据JZ2440内存使用情况,可以将dtb放到中间的空闲区域,或者0x30000000-0x30004000的空闲区域,只要不会影响到uboot和内核的运行即可。

另外,在内核启动之后,dtb所在的内存区域会被保留,不会有其他程序来使用这块内存区域,即不会有内存泄漏发生。

  • 4
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值