Boot Loader 就是在linux操作系统内核运行之前运行的一段小程序。
可以分为以下几个步骤:初始化硬件设备、读取内核影像,设置启动参数,启动内核。
你可以通过一直U-boot来得到一个功能强大的boot loader,但是对于实际情况来说,
我们启动linux kernel只需要几个步骤就可以完成,写一个最简单的linux bootloader也是一件很轻松的事情。
这里介绍一个最简单的bootloader。
一、初始化硬件:
1、初始化CPU时钟频率,禁止中断。
2、关闭看门狗。
3、初始化存储控制器。
4、初始化linux需要用到的串口。
5、设置堆栈,CPU 必须 SVC 模式, 禁止mmu和cache,进入C程序入口。
二、读取内核影像:
1、由于内核影像存储在Nand Flash中,所以初始化NAND控制器。
2、读出内核影像数据到(rambase + 0x8000)。
3、因为我们的根文件系统直接在mtd中存在,没有用到ramdisk作为根文件系统,所以不需要加载initrd作为根文件系统。
三、设置linux内核启动参数。
设置启动参数为 cmdline = "console=ttyS0 root=mtdblock0"
1、设置param_struct来传递参数。
void
linux_arg(
char
*
cmdline)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
int i;
struct param_struct *params = (struct param_struct *)(rambase+0x100);
params->u1.s.page_size = 4096;
params->u1.s.nr_pages = (RAM_SIZE) >> 12;
for(i = 0; i < 24; i++)
params->commandline[i] = cmdline[i];
}
其中 param_struct 结构在arch/arm/kernel/compat.c中声明,
/*This is the old deprecated way to pass parameters to the kernel*/
在新的内核中启动参数以标记语言的形式来传递。 不过用以上的方法仍然可以启动现在最新的内核。
2、以标记列表的形式来传递启动参数。
启动参数标记列表以标记 ATAG_CORE 开始,以标记 ATAG_NONE 结束。
每个标记由标识被传递参数的 tag_header 结构以及随后的参数值数据结构来组成。具体可以参考
arch/arm/kernel/compat.c arch/arm/kernel/setup.c等。
这里简单起见,只设置了 ATAG_CORE ATAG_CMDLINE。
#define
tag_next(t) ((struct tag *)((__u32 *)(t) + (t)->hdr.size))
params
=
(
struct
tag
*
)BOOT_PARAMS;
params
->
hdr.tag
=
ATAG_CORE;
params
->
hdr.size
=
tag_size(tag_core);
params
->
u.core.flags
=
0
;
params
->
u.core.pagesize
=
0
;
params
->
u.core.rootdev
=
0
;
params
=
tag_next(
params
);
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
);
params
->
hdr.tag
=
ATAG_NONE;
params
->
hdr.size
=
0
;
四、传递参数,运行内核。
内核调用需要三个参数分别在三个寄存器中 R0, R1, R2。
R0=0;
R1=机器类型 ID;关于 Machine Type Number,可以参见 linux/arch/arm/tools/mach-types。
R2=启动参数标记列表在 RAM 中起始基地址。
可以用以下行数调用来传递参数和执行内核:
void (*theKernel)(int zero, int arch, u32 addr) = (void (*)(int, int, u32))(rambase + 0x8000);
theKernel(0, ARCH, rambase);
五、启动信息:
串口收到"uncompressing linux.................. done, booting the kernel……"
boot loader 已经成功地转起来了。
整个启动程序代码只有3k左右, 可以用ads 或realview编译成功,可以正常启动linux, 在板子上可以很好的运行。
可以分为以下几个步骤:初始化硬件设备、读取内核影像,设置启动参数,启动内核。
你可以通过一直U-boot来得到一个功能强大的boot loader,但是对于实际情况来说,
我们启动linux kernel只需要几个步骤就可以完成,写一个最简单的linux bootloader也是一件很轻松的事情。
这里介绍一个最简单的bootloader。
一、初始化硬件:
1、初始化CPU时钟频率,禁止中断。
2、关闭看门狗。
3、初始化存储控制器。
4、初始化linux需要用到的串口。
5、设置堆栈,CPU 必须 SVC 模式, 禁止mmu和cache,进入C程序入口。
二、读取内核影像:
1、由于内核影像存储在Nand Flash中,所以初始化NAND控制器。
2、读出内核影像数据到(rambase + 0x8000)。
3、因为我们的根文件系统直接在mtd中存在,没有用到ramdisk作为根文件系统,所以不需要加载initrd作为根文件系统。
三、设置linux内核启动参数。
设置启动参数为 cmdline = "console=ttyS0 root=mtdblock0"
1、设置param_struct来传递参数。
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/1327ab569c1ae82736693a50b8e33378.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
其中 param_struct 结构在arch/arm/kernel/compat.c中声明,
/*This is the old deprecated way to pass parameters to the kernel*/
在新的内核中启动参数以标记语言的形式来传递。 不过用以上的方法仍然可以启动现在最新的内核。
2、以标记列表的形式来传递启动参数。
启动参数标记列表以标记 ATAG_CORE 开始,以标记 ATAG_NONE 结束。
每个标记由标识被传递参数的 tag_header 结构以及随后的参数值数据结构来组成。具体可以参考
arch/arm/kernel/compat.c arch/arm/kernel/setup.c等。
这里简单起见,只设置了 ATAG_CORE ATAG_CMDLINE。
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
四、传递参数,运行内核。
内核调用需要三个参数分别在三个寄存器中 R0, R1, R2。
R0=0;
R1=机器类型 ID;关于 Machine Type Number,可以参见 linux/arch/arm/tools/mach-types。
R2=启动参数标记列表在 RAM 中起始基地址。
可以用以下行数调用来传递参数和执行内核:
void (*theKernel)(int zero, int arch, u32 addr) = (void (*)(int, int, u32))(rambase + 0x8000);
theKernel(0, ARCH, rambase);
五、启动信息:
串口收到"uncompressing linux.................. done, booting the kernel……"
boot loader 已经成功地转起来了。
整个启动程序代码只有3k左右, 可以用ads 或realview编译成功,可以正常启动linux, 在板子上可以很好的运行。