uboot启动内核的相关命令详解——bootbootm
Posted 代二毛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了uboot启动内核的相关命令详解——bootbootm相关的知识,希望对你有一定的参考价值。
1、boot和bootm命令的联系
当我们进入uboot的命令终端后,可以利用boot和bootm来启动内核,但是命令的使用方式有区别:
直接输入boot命令就可以启动内核,如果使用bootm命令,后面还需要传入内核在DDR中的地址。
(1)boot命令:完成内核的重定位,将内核从外存加载到内存,然后启动内核;
(2)bootm命令:调用时一般要传入内核在内存中的地址,然后去该地址处启动内核;如果不传入内核在内存中的地址,bootm会用默认地址去启动内核,这样不保证能正确启动内核。
(3)bootm命令可以算作是boot命令的子命令,因为boot命令最终也是调用bootm去启动内核。
补充:uboot命令体系和环境变量是理解下面源码实现的基础,不清楚的可以看博客:《uboot的命令体系详解》和《uboot中环境变量的实现》。
2、boot命令实现源码
#define CONFIG_BOOTCOMMAND "movi read kernel 30008000; movi read rootfs 30B00000 300000; bootm 30008000 30B00000"
int do_bootd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
int rcode = 0;
//run_command是uboot中执行命令的函数,第一个参数就是要执行的命令
#ifndef CFG_HUSH_PARSER
//环境变量bootcmd的值默认就是CONFIG_BOOTCOMMAND
if (run_command (getenv ("bootcmd"), flag) < 0)
rcode = 1;
#else
if (parse_string_outer (getenv ("bootcmd"),
FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP) != 0)
rcode = 1;
#endif
return rcode;
}
//利用U_BOOT_CMD宏注册命令
U_BOOT_CMD(
boot, 1, 1, do_bootd,
"boot - boot default, i.e., run 'bootcmd'\\n",
NULL
);
boot是启动内核的命令,boot命令的内容保存在环境变量bootcmd中,bootcmd的默认值来自于配置文件的CONFIG_BOOTCOMMAND宏定义。在main_loop函数中如果bootdelay时间内没有按下按键就会自动启动内核,可以把boot命令拆分成几个命令,其中包含重定位内核和启动内核。
(1)movi read kernel 30008000:用movi命令将外存的kernel分区读取到内存的0x30008000;
(2)movi read rootfs 30B00000 300000:用movi命令将外存的rootfs读取到内存的0x30B00000,读取长度0x300000
(3)bootm 30008000 30B00000:bootm是直接启动内核的指令,0x30008000是告诉bootm命令内核此时在内存的0x30008000地址处;
3、bootm命令实现源码
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
image_header_t *hdr;
ulong addr;
ulong iflag;
const char *type_name;
uint unc_len = CFG_BOOTM_LEN;
uint8_t comp, type, os;
void *os_hdr;
ulong os_data, os_len;
ulong image_start, image_end;
ulong load_start, load_end;
//判断内核是否是zImage
#ifdef CONFIG_ZIMAGE_BOOT
#define LINUX_ZIMAGE_MAGIC 0x016f2818 //这是一个魔术,用来判断是否是zImage
/* find out kernel image address */
if (argc < 2) {
addr = load_addr;
debug ("* kernel: default image load address = 0x%08lx\\n",
load_addr);
} else {
//argv[1]就是调用bootm命令时指定的内核所在地址
addr = simple_strtoul(argv[1], NULL, 16);
debug ("* kernel: cmdline image address = 0x%08lx\\n", img_addr);
}
//判断镜像头的第37-40字节处是否存放的是LINUX_ZIMAGE_MAGIC
//这是zImage类型内核的特征
if (*(ulong *)(addr + 9*4) == LINUX_ZIMAGE_MAGIC) {
printf("Boot with zImage\\n");
addr = virt_to_phys(addr);//得到内核的物理地址,因为uboot可能开启了虚拟地址映射
hdr = (image_header_t *)addr;//得到内核的头信息
hdr->ih_os = IH_OS_LINUX;//指明该内核是Linux
hdr->ih_ep = ntohl(addr);//得到内核的入口
memmove (&images.legacy_hdr_os_copy, hdr, sizeof(image_header_t));
/* save pointer to image header */
images.legacy_hdr_os = hdr;
images.legacy_hdr_valid = 1; //标志位,如果不为1是用设备树的方式启动内核
goto after_header_check;
}
#endif
#if defined(CONFIG_ZIMAGE_BOOT)
after_header_check:
os = hdr->ih_os;
#endif
//根据操作类型判断调用不同的内核启动函数
switch (os) {
default: /* handled by (original) Linux case */
case IH_OS_LINUX:
#ifdef CONFIG_SILENT_CONSOLE
fixup_silent_linux();
#endif
do_bootm_linux (cmdtp, flag, argc, argv, &images); //启动linux内核
break;
case IH_OS_NETBSD:
do_bootm_netbsd (cmdtp, flag, argc, argv, &images);
break;
}
show_boot_progress (-9);
if (iflag)
enable_interrupts();
return 1;
}
摘抄自uboot的do_bootm函数中zImage内核启动部分的代码,为了便于理解内核启动的过程,其余关于uImage和设备树的代码都已经删除了。bootm函数的主要作用就是解析内核的头信息,区分出内核的类型和调用相应内核的启动函数。do_bootm 函数主要功能:
(1)识别出当前的启动方式:zImage、uImage或者设备树;
(2)根据不同的启动方式去初始化images全局变量和内核头信息;
(3)根据操作系统类型,调用不同的内核启动函数;
补充:常见的linux系统启动函数是do_bootm_linux (),细节请看博客:《do_bootm_linux函数解析》。
以上是关于uboot启动内核的相关命令详解——bootbootm的主要内容,如果未能解决你的问题,请参考以下文章