bootz 启动 kernel

110 篇文章 25 订阅
35 篇文章 0 订阅


活动地址:CSDN21天学习挑战赛

思考

uboot 启动 kernel,这是一个众所周知的事实。但是,你有没有思考过,这两个独立的镜像实体,是如何做到 A 启动 B 的呢?如果是同一份代码中的 func_a() 调用 func_b(),还好理解,因为是在同一份程序中(或者说是同一份指令中),func_a 是知道 func_b() 的地址的,直接跳转过去就行了(设置 PC 指针指向 func_b 的地址)。
但是 uboot 和 kernel 不在同一份指令中,这是两个独立的程序,那么是怎样实现 程序 A “调用” 程序 B 的呢?

bootz

在 uboot 命令行下,可以使用 bootz 命令启动 kernel,我们就以此为切入点,探究 uboot 启动 kernel 的底层原理。

表象

使用如下命令,就能完成 uboot 启动 kernel。

tftp 80800000 zImage
tftp 83000000 imx6ull-14x14-evk.dtb
bootz 80800000 - 83000000

这里简单介绍下命令作用。
前两条是通过 tftp 命令,分别将 kernel 镜像 zImage 和设备树二进制文件下载到内存的 0x80800000 和 0x83000000 位置。
第三条是使用 bootz 命令启动 kernel。

bootz

接下来,就分析下 bootz 启动 kernel 的原理。
通过 uboot 命令行,向 bootz 传递了三个参数 “80800000”、“-”、“83000000”,它们分别是 “kernel 镜像在内存中的首地址”、“无效参数”、“设备树文件首地址”,
bootz 命令对应的执行函数为 do_bootz(),在该函数中,首先设置 kernel 首地址和设备树首地址(其实这就是 uboot 能够启动 kernel 的原因:有了地址就能跳转过去运行)。

images->ep = simple_strtoul(argv[0], NULL, 16);

变量 ep 用来存储 kernel 镜像在内存中的首地址,该参数由 argv[0] 赋值,argv[0] 就是刚刚在命令行传递给 bootz 的第一个参数 80800000

images.ft_addr = argv[2];

变量 ft_addr 用来存储设备树在内存中的首地址,该参数由 argv[2] 赋值,为 83000000

kernel_entry()

do_bootz() 最终会调用 kernel_entry(),kernel_entry 并不是在 uboot 中定义的函数,它是一个地址转换来的,如下:

kernel_entry = (void (*)(void *fdt_addr, void *res0, void *res1,
				void *res2))images->ep;

而 image->ep 就是 kernel 在内存中的首地址,这不就联系上了嘛🤞。

uboot 将设备树传递给 kernel

刚才分析出 images.ft_addr 存放设备树在内存中的首地址,那又是如何将这个地址传递给 kernel 呢?这里不像函数传参,当成实参就传递过去了。
一来 uboot 和 kernel 不是同一份程序,不共用同一个堆栈;二来 kernel 一开始的代码是汇编代码,C 函数也没办法直接将自己的实参传递给汇编函数。
那要怎么才能实现 uboot 中的一个变量传递给 kernel 呢?答案是使用寄存器。
uboot 生命周期的最后一个函数调用如下

r2 = (unsigned long)images->ft_addr;
kernel_entry(0, machid, r2);

使用 r2 寄存器存储设备树文件的首地址。

kernel 接收设备树

再来看 kernel 是怎么接收这个地址的呢?
kernel 的入口函数是 head.S 中的 stext

ENTRY(stext)
	...
	ldr	r13, =__mmap_switched		@ address to jump to after

__mmap_switched:
	...
	str	r2, [r6]			@ Save atags pointer
	cmp	r7, #0
	strne	r0, [r7]			@ Save control register values
	b	start_kernel
ENDPROC(__mmap_switched)


__mmap_switched_data:
	.long	__data_loc			@ r4
	.long	_sdata				@ r5
	.long	__bss_start			@ r6
	.long	_end				@ r7
	.long	processor_id			@ r4
	.long	__machine_arch_type		@ r5
	.long	__atags_pointer			@ r6
...

总体流程是:
stext 调用 __mmap_switched,在 __mmap_switched 中将 r2 的值赋给全局变量 __atags_pointer,然后调用 start_kernel()。

start_kernel() 调用 setup_arch(),在该函数中解析设备树,如下:

mdesc = setup_machine_fdt(__atags_pointer);

总结

uboot 启动 kernel:

uboot 拿到 kernel 在内存中的首地址,就能跳转到该地址运行。

uboot 将设备树传递给 kernel:

通过 r2 寄存器。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Li-Yongjun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值