bootloader 要想启动内核,可以直接跳到内核的第一个指令处,即内核的起始地址,这样便可以完成内核的启动工作了。但是要想启动内核还需要满足一些条件,如下所示:
1、cpu 寄存器设置
* R0 = 0
* R1 = 机器类型 id
* R2 = 启动参数在内存中的起始地址
2、cpu 模式
* 禁止所有中断
* 必须为SVC(超级用户)模式
3、Cache、MMU
* 关闭 MMU
* 指令Cache可以开启或者关闭
* 数据Cache必须关闭
4、设备
* DMA 设备应当停止工作
5、PC为内核的起始地址
这些需求都由 boot loader 实现,在常用的 uboot 中完成一系列的初始化后最后通过 bootm 命令加载 linux 内核。bootm 向将内核映像从各种媒介中读出,存放在指定的位置;然后设置标记列表给内核传递参数;最后跳到内核的入口点去执行。
Uboot版本:u-boot-2013.01
一、bootm命令用法介绍如下:
在 common/cmd_bootm.c 中可以看到bootm 的定义:
可以看到 bootm 命令使调用了do_bootm 函数。
do_bootm 函数
在cmd_bootm.c 第586行可以看到do_bootm函数的定义(为方便阅读,对其中一些代码进行了删减,完整代码请阅读uboot源码):
- /*******************************************************************/
- /* bootm - boot application image from image in memory */
- /*******************************************************************/
- int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
- {
- ulong iflag;
- ulong load_end = 0;
- int ret;
- boot_os_fn *boot_fn;
- if (bootm_start(cmdtp, flag, argc, argv))// 获取镜像信息
- return 1;
- iflag = disable_interrupts(); // 关闭中断
- usb_stop();// 关闭usb设备
- ret = bootm_load_os(images.os, &load_end, 1);//加载内核
- lmb_reserve(&images.lmb, images.os.load, (load_end - images.os.load));
- if (images.os.type == IH_TYPE_STANDALONE) { //如有需要,关闭内核的串口
- if (iflag)
- enable_interrupts();
- /* This may return when 'autostart' is 'no' */
- bootm_start_standalone(iflag, argc, argv);
- return 0;
- }
- boot_fn = boot_os[images.os.os];//获取启动参数
- arch_preboot_os();//启动前准备
- boot_fn(0, argc, argv, &images);//启动,不再返回
- #ifdef DEBUG
- puts("\n## Control returned to monitor - resetting...\n");
- #endif
- do_reset(cmdtp, flag, argc, argv);
- return 1;
- }
a -- 首先通过 bootm_start 函数分析镜像的信息;
b -- 如果满足判定条件则进入 bootm_load_os 函数进行加载;
c -- 加载完成后就可以调用 boot_fn 开始启动。
1、bootm_start
在cmd_bootm.c 第193行可以看到bootm_start函数的定义, 主要作用是填充内核相关信息