uboot源码分析十二 uboot使用bootz启动linux流程二

41 篇文章 6 订阅
16 篇文章 5 订阅

在这里插入图片描述

bootm_os_get_boot_func 函数

do_bootm_states 会调用 bootm_os_get_boot_func 来查找对应系统的启动函数,此函数定义在文件 common/bootm_os.c 中

493 boot_os_fn *bootm_os_get_boot_func(int os)
494 {
495 #ifdef CONFIG_NEEDS_MANUAL_RELOC
496 static bool relocated;
497
498 if (!relocated) {
499 int i;
500
501 /* relocate boot function table */
502 for (i = 0; i < ARRAY_SIZE(boot_os); i++)
503 if (boot_os[i] != NULL)
504 boot_os[i] += gd->reloc_off;
505
506 relocated = true;
507 }
508 #endif
509 return boot_os[os];
510 }

第 495~508 行是条件编译,在本 uboot 中没有用到,因此这段代码无效,只有 509 行有效。
在 509 行中 boot_os 是个数组,这个数组里面存放着不同的系统对应的启动函数。 boot_os 也定义在文件 common/bootm_os.c 中

435 static boot_os_fn *boot_os[] = {
436 [IH_OS_U_BOOT] = do_bootm_standalone,
437 #ifdef CONFIG_BOOTM_LINUX
438 [IH_OS_LINUX] = do_bootm_linux,
439 #endif
......
465 #ifdef CONFIG_BOOTM_OPENRTOS
466 [IH_OS_OPENRTOS] = do_bootm_openrtos,
467 #endif
468 };

第 438 行就是 Linux 系统对应的启动函数: do_bootm_linux。

do_bootm_linux 函数

do_bootm_linux 就是最终启动 Linux 内核的函数,此函数定义在文件 arch/arm/lib/bootm.c

339 int do_bootm_linux(int flag, int argc, char * const argv[],
340 bootm_headers_t *images)
341 {
342 /* No need for those on ARM */
343 if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
344 return -1;
345
346 if (flag & BOOTM_STATE_OS_PREP) {
347 boot_prep_linux(images);
348 return 0;
349 }
350
351 if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) {
352 boot_jump_linux(images, flag);
353 return 0;
354 }
355
356 boot_prep_linux(images);
357 boot_jump_linux(images, flag);
358 return 0;
359 }

第351行,如果参数flag等于BOOTM_STATE_OS_GO或者BOOTM_STATE_OS_FAKE_GO的话就执行 boot_jump_linux 函数。boot_selected_os 函数在调用 do_bootm_linux 的时候会将 flag设置为 BOOTM_STATE_OS_GO。
第 352 行,执行函数 boot_jump_linux,此函数定义在文件 arch/arm/lib/bootm.c 中

272 static void boot_jump_linux(bootm_headers_t *images, int flag)
273 {
274 #ifdef CONFIG_ARM64
......
292 #else
293 unsigned long machid = gd->bd->bi_arch_number;
294 char *s;
295 void (*kernel_entry)(int zero, int arch, uint params);
296 unsigned long r2;
297 int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
298
299 kernel_entry = (void (*)(int, int, uint))images->ep;
300
301 s = getenv("machid");
302 if (s) {
303 if (strict_strtoul(s, 16, &machid) < 0) {
304 debug("strict_strtoul failed!\n");
305 return;
306 }
307 printf("Using machid 0x%lx from environment\n", machid);
308 }
309
310 debug("## Transferring control to Linux (at address %08lx)" \
311 "...\n", (ulong) kernel_entry);
312 bootstage_mark(BOOTSTAGE_ID_RUN_OS);
313 announce_and_cleanup(fake);
314
315 if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
316 r2 = (unsigned long)images->ft_addr;
317 else
318 r2 = gd->bd->bi_boot_params;
319
......
328 kernel_entry(0, machid, r2);
329 }
330 #endif
331 }

第 274~292 行是 64 位 ARM 芯片对应的代码, Cortex-A7 是 32 位芯片,因此用不到。
第 293 行,变量 machid 保存机器 ID,如果不使用设备树的话这个机器 ID 会被传递给 Linux内核, Linux 内核会在自己的机器 ID 列表里面查找是否存在与 uboot 传递进来的 machid 匹配的项目,如果存在就说 Linux 内核支持这个机器,那么 Linux 就会启动!如果使用设备树的话这个 machid 就无效了,设备树存有一个“兼容性”这个属性, Linux 内核会比较“兼容性”属性的值(字符串)来查看是否支持这个机器。
第 295 行,函数 kernel_entry,看名字“内核_进入”,说明此函数是进入 Linux 内核的.此函数有三个参数: zero, arch, params,第一个参数 zero 同样为 0;第二个参数为机器 ID;第三个参数 ATAGS 或者设备树(DTB)首地址, ATAGS 是传统的方法,用于传递一些命令行信息啥的,如果使用设备树的话就要传递设备树(DTB)。
第 299 行,获取 kernel_entry 函数,函数 kernel_entry 并不是 uboot 定义的,而是 Linux 内核定义的, Linux 内核镜像文件的第一行代码就是函数 kernel_entry,而 images->ep 保存着 Linux内核镜像的起始地址,而起始地址保存的不正是 Linux 内核第一行代码!
第 313 行,调用函数 announce_and_cleanup 来打印一些信息并做一些清理工作,此函数定义在文件 arch/arm/lib/bootm.c 中,
函数内容如下:

72 static void announce_and_cleanup(int fake)
73 {
74 printf("\nStarting kernel ...%s\n\n", fake ?
75 "(fake run for tracing)" : "");
76 bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
......
87 cleanup_before_linux();
88 }

第 87 行调用 cleanup_before_linux 函数做一些清理工作。

继续回到函数 boot_jump_linux,第 315~318 行是设置寄存器 r2 的值?
为什么要设置 r2 的值呢? Linux 内核一开始是汇编代码,因此函数 kernel_entry 就是个汇编函数。向汇编函数传递参数要使用 r0、 r1 和 r2(参数数量不超过 3 个的时候),所以 r2 寄存器就是函数 kernel_entry 的第三个参数。
第 316 行,如果使用设备树的话, r2 应该是设备树的起始地址,而设备树地址保存在 images的 ftd_addr 成员变量中。
第 317 行,如果不使用设备树的话, r2 应该是 uboot 传递给 Linux 的参数起始地址,也就是环境变量 bootargs 的值,
第 328 行,调用 kernel_entry 函数进入 Linux 内核,此行将一去不复返, uboot 的使命也就完成了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值