目录
一、作用
二、使用方法
三、环境需求
四、流程分析
五、加载地址一、作用
boot application image stored in memory //启动存放在内存中的镜像(二进制文件),一般就是启动内核镜像。
在使用bootm命令之前,需要先将内核镜像放到内存里边,比如:1.从存储介质中读出 2.用tftp命令下载到指定的内存地址。
二、使用方法
bootm image_addr ramdisk_addr dtb_addr
image_addr: 内核镜像存放在DDR中的位置
ramdisk_addr: ramdisk地址
dtb_addr: 设备树地址
任何一项都可以没有,如果没有,用 “-’代替
用法1:bootm //使用默认的镜像地址启动
用法2:bootm image_addr //指定镜像地址启动。一般是uImage
用法3:bootm image_addr - dtb_addr //指定设备树地址
三、环境需求
arm 架构处理器对 linux 内核启动之前环境的五点需求
1、cpu 寄存器设置
* R0 = 0
* R1 = 板级 id
* R2 = 启动参数在内存中的起始地址
2、cpu 模式
* 禁止所有中断
* 必须为SVC(超级用户)模式
3、缓存、MMU
* 关闭 MMU
* 指令缓存可以开启或者关闭
* 数据缓存必须关闭并且不能包含任何脏数据
4、设备
* DMA 设备应当停止工作
5、boot loader 需要跳转到内核镜像的第一条指令处四、流程分析
bootm.c(cmd)
do_bootm
//如果argc大于1,则进行其他处理
//bootm_headers_t images; //uboot_sdk/common /* pointers to os/initrd/fdt images */
do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START | BOOTM_STATE_FINDOS |
BOOTM_STATE_FINDOTHER | BOOTM_STATE_LOADOS | BOOTM_STATE_OS_PREP |
BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO, &images, 1) bootm.c(common)
images->state |= states;
/* 设置images.state为BOOTM_STATE_START;设置lmb (管理镜像的内存)*/
bootm_start(cmdtp, flag, argc, argv) // bootm.c(uboot_sdk/common)
memset((void *)&images, 0, sizeof(images));
images.verify = getenv_yesno("verify");
boot_start_lmb(&images); //logic memory block,用于管理镜像的内存
mem_start = getenv_bootm_low(); //获得内存sdram的起始地址
mem_size = getenv_bootm_size(); //获得内存sdram的大小
lmb_add(&images->lmb, (phys_addr_t)mem_start, mem_size);
images.state = BOOTM_STATE_START;
/* 根据header进行判断,设置images全局变量成员,打印header的信息 */
bootm_find_os(cmdtp, flag, argc, argv)
const void *os_hdr;
/* images.os.image_start : 有效数据地址 images.os.image_len:总长度(header和有效数据) */
os_hdr = boot_get_kernel(cmdtp, flag, argc, argv, &images, &images.os.image_start,
&images.os.image_len);
/* 确定kernel的地址,如果是0,则用默认地址,如果不是0,则设置为传进来的地址 */
img_addr = genimg_get_kernel_addr_fit(argc < 1 ? NULL : argv[0], &fit_uname_config, &fit_uname_kernel);
/* 如果配置了CONFIG_HAS_DATAFLASH则从dataflash中读出内核。本处,没有配置,一般是先用loadk命令
读出了 */
img_addr = genimg_get_image(img_addr);
*os_data = *os_len = 0; //images.os.image_start = 0; images.os.image_len = 0
buf = map_sysmem(img_addr, 0); //buf = (void *)(unsigned long int)img_addr;
switch (genimg_get_format(buf)) { // 确定magic是否匹配(0x27051956),匹配则返回
// IMAGE_FORMAT_LEGACY
case IMAGE_FORMAT_LEGACY:
hdr = image_get_kernel(img_addr, images->verify)
//image_header_t *hdr = (image_header_t *)img_addr;
检测magic是否是0x27051956
检测头部的crc校验值
打印出head的内容:name,type,data size,load address,entry point
如果是IH_TYPE_MULTI或者IH_TYPE_SCRIPT类型,则打印其偏移值
检测实际数据的crc校验值(如果images->verify不是0,即环境变量verify不是0)
检测架构是否匹配(如果架构类型是0(invalid)则不匹配)
获得镜像的数据起始地址和长度 //先判断类型,后操作:IH_TYPE_KERNEL、
// IH_TYPE_KERNEL_NOLOAD,IH_TYPE_STANDALONE用同一组函数, // IH_TYPE_MULTI用另一组函数
*os_data = image_get_data(hdr); //images.os.image_start = image_get_data(hdr)
((ulong)hdr + image_get_header_size());
*os_len = image_get_data_size(hdr); //images.os.image_len = image_get_data_size(hdr)
(image_get_size(hdr) + image_get_header_size())
/* 把镜像的header拷贝到变量中,从而允许kernel解压时覆盖 */
memmove(&images->legacy_hdr_os_copy, hdr, sizeof(image_header_t));
images->legacy_hdr_os = hdr;
images->legacy_hdr_valid = 1;
return buf
images.os.type = image_get_type(os_hdr); //映像类型
images.os.comp = image_get_comp(os_hdr); //compression 压缩方式
images.os.os = image_get_os(os_hdr); // 操作系统类型
images.os.end = image_get_image_end(os_hdr); // 结束地址
images.os.load = image_get_load(os_hdr); // 头信息中定义的加载地址
images.os.arch = image_get_arch(os_hdr);
/* entry point of os */
images.ep = image_get_ep(&images.legacy_hdr_os_copy);
//如果类型是IH_TYPE_KERNEL_NOLOAD,有额外操作
images.os.start = map_to_sysmem(os_hdr); //(unsiged long)os_hdr 映像起始地址,包括头信息
/*加载可用的ramfs,加载设备树
bootm_find_other(cmdtp, flag, argc, argv)
关中断
/* 将内核解压或者拷贝到加载地址 */
bootm_load_os(images, &load_end, 0); bootm.c(common)
image_info_t os = images->os;
ulong load = os.load;
ulong image_len = os.image_len;load_buf = map_sysmem(load, 0);
image_buf = map_sysmem(os.image_start, image_len);
/* 解压内核 */
/* #define CONFIG_SYS_BOOTM_LEN 0x800000 */
bootm_decomp_image(os.comp, load, os.image_start, os.type, load_buf, image_buf, image_len,
CONFIG_SYS_BOOTM_LEN, load_end);
若压缩类型是:未压缩
若load == image_start则退出,否则进行内存移动(将首地址为os.image_start的数据拷贝到首地址为
os.load)
如果有压缩,则解压到load_buf
/* 清dcache */
flush_cache(load, ALIGN(*load_end - load, ARCH_DMA_MINALIGN));
判断映像文件存放地址和加载地址是否重叠
lmb_reserve(&images->lmb, images->os.load, (load_end - images->os.load));
重定位ramfs
设置设备树区域
/* 获得启动的函数(根据系统类型)。本处是do_bootm_linux */
boot_fn = bootm_os_get_boot_func(images->os.os);
/* 设置启动标记 */
need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE | BOOTM_STATE_OS_BD_T |
BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO);
do_bootm_linux(BOOTM_STATE_OS_PREP, argc, argv, images); //bootm.c(arch/arm/lib)
/* 初始化启动的参数 */
boot_prep_linux(images); //bootm.c(arch/arm/lib)
static struct tag *params;
获得bootargs和dbg环境变量,把这两个变量放到一起(一个数组里)
setup_start_tag (gd->bd); /* 初始化参数列表起始符 */
params = (struct tag *)bd->bi_boot_params;
...
params = tag_next (params);
setup_serial_tag (¶ms); /* 初始化串口参数 */
//传进来参数为tmp
设置params的成员
params = tag_next (params);
*tmp = params;
setup_commandline_tag(gd->bd, commandline); /* 初始化命令参数 */
char *p;
for (p = commandline; *p == ' '; p++);
params->hdr.tag = ATAG_CMDLINE;
params->hdr.size = (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;
strcpy (params->u.cmdline.cmdline, p);
params = tag_next (params);
setup_revision_tag (¶ms); /* 初始化版本参数 */
setup_memory_tags (bd); /* 初始化内存参数 */
setup_initrd_tag(gd->bd, images->initrd_start, images->initrd_end); /* 初始化虚拟磁盘参数 */
setup_board_tags(¶ms); /* 初始化fb参数 */
setup_end_tag(gd->bd); /* 初始化参数列表结束符 */
boot_jump_linux(images, flag); //bootm.c(arch/arm/lib)
unsigned long machid = gd->bd->bi_arch_number;
char *s;
void (*kernel_entry)(int zero, int arch, uint params);
unsigned long r2;
kernel_entry = (void (*)(int, int, uint))images->ep;
s = getenv("machid");
announce_and_cleanup(fake);
cleanup_before_linux()
cleanup_before_linux_select(CBL_ALL)
关中断
关闭dcache
关闭外部cache
使所有dcache无效
关闭icache
使所有icache无效
r2 = gd->bd->bi_boot_params
kernel_entry(0, machid, r2);
五、加载地址
uImage的头部64字节信息,对应image_header_t,有个ih_load成员,代表image实际数据的地址(不包括头部),这个值是编译时就确定的,经过bootm命令处理以后,实际image会放到ih_load指定的地址上。最终会直接到ih_load指定的地址处运行。
对于未压缩的uImage:
1.若运行bootm命令,默认加载地址不是ih_load指定的地址,则将默认加载地址处的数据移动到ih_load指定的位置 (memmov)。
2.若运行bootm image_addr,image_addr不是ih_load指定的地址,则将image_addr处的数据移动到ih_load指定的位置
(memmov)。
对于压缩的uImage:
1.若运行bootm命令,默认加载地址不是ih_load指定的地址,则将默认加载地址处的数据解压到ih_load。
2.若运行bootm image_addr,image_addr不是ih_load指定的地址,则将image_addr处的数据解压到ih_load。
总结:bootm 的image_addr可以是不影响uboot运行、不影响mmu映射、不影响ramdisk和dtb的任意内存地址。
代码流程分析如下:
bootm.c(cmd)
do_bootm
//如果argc大于1,则进行其他处理
//bootm_headers_t images; //uboot_sdk/common /* pointers to os/initrd/fdt images */
do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START | BOOTM_STATE_FINDOS |
BOOTM_STATE_FINDOTHER | BOOTM_STATE_LOADOS | BOOTM_STATE_OS_PREP |
BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO, &images, 1) bootm.c(common)
images->state |= states;
/* 设置images.state为BOOTM_STATE_START;设置lmb (管理镜像的内存)*/
bootm_start(cmdtp, flag, argc, argv) // bootm.c(uboot_sdk/common)
...
/* 根据header进行判断,设置images全局变量成员,打印header的信息 */
bootm_find_os(cmdtp, flag, argc, argv)
const void *os_hdr;
/* images.os.image_start : 有效数据地址 images.os.image_len:总长度(header和有效数据) */
os_hdr = boot_get_kernel(cmdtp, flag, argc, argv, &images, &images.os.image_start, &images.os.image_len);
/* 确定kernel的地址,如果是0,则用默认地址,如果不是0,则设置为传进来的地址 */
img_addr = genimg_get_kernel_addr_fit(argc < 1 ? NULL : argv[0], &fit_uname_config,
&fit_uname_kernel);
buf = map_sysmem(img_addr, 0); // buf = (void *)(unsigned long int)img_addr;
switch (genimg_get_format(buf)) { // 确定magic是否匹配(0x27051956),匹配则返回
// IMAGE_FORMAT_LEGACY
case IMAGE_FORMAT_LEGACY:
hdr = image_get_kernel(img_addr, images->verify)
// image_header_t *hdr = (image_header_t *)img_addr;
images.os.image_start = image_get_data(hdr)
// images.os.image_start = ((ulong)hdr + image_get_header_size());
return buf
images.os.load = image_get_load(os_hdr);
images.os.start = (unsiged long)os_hdr // 映像起始地址,包括头信息
bootm_load_os(images, &load_end, 0);
ulong load = os.load;
load_buf = os.load
image_buf = os.image_start
bootm_decomp_image(os.comp, load, os.image_start, os.type, load_buf, image_buf, image_len,
CONFIG_SYS_BOOTM_LEN, load_end);
若没有压缩
若load == image_start则退出,否则进行内存移动(将os.image_start的数据移动到os.load)
若压缩了
解压到os.load