Linux开发四_bootloader启动linux内核

Linux开发四

bootloader启动linux内核

象棋小子    1048272975

不同的CPU具有不同的启动方式,其系统外设等均具有较大的差异。CPU上电启动后,并不具有相应的内核启动环境,需要bootloader先初始化CPU及相应系统外设,加载内核,使之具备内核启动的必要条件。bootloader一般应有下载固化以及加载启动这两个功能,笔者此处就s3c2416基于yaffs文件系统的linux下载固化以及加载启动作一个简单的介绍。

1. 基于linux的bootloader

bootloader的实现流程前面章节有详细的介绍,此处不再细述。bootloader为了能启动linux,往往需要跟内核彼此协调约定,才能联合工作。linux系统为了精简以及便于维护,分成了内核空间以及用户空间。Linux内核由内存管理、进程管理、设备驱动程序、网络管理等组成,它是操作系统的核心,具有很多最基本的功能,决定了系统的性能和稳定性。用户空间的文件系统用来提供管理系统的各种配置,提供相应的应用程序、服务、数据交换等。文件系统作为一种载体,它是用来实现用户与操作系统内核的交互。因此,一个可启动的linux系统必须包含linux内核以及一个根文件系统。

bootloader应该具有把自身、linux内核、根文件系统下载固化进相应的非易失存性储器中,以便目标机能脱离宿主机,单独加载启动运行。bootloader可以通过多种通讯方式,如usb、网络、sd卡、u盘等,把宿主机上开发好的内核、根文件系统等下载进目标机的内存,再固化进相应的存储器中。目标机单独运行时,bootloader首先自启动,然后从固化存储器中加载linux内核到内存,如果根文件系统在ram disk里,bootloader还需加载根文件系统到内存,否则,根文件系统由linux内核根据约定进行挂载。最后,bootloader跳转到内核,把CPU控制器全权交付内核处理。

2. 下载固化

此处假设已经做好了bootloader、linux内核、基于yaffs的根文件系统,把宿主机里的这些镜像文件拷贝到sd卡,bootloader从sd卡读取这些镜像文件到内存,然后再烧录固化进nand flash里,实现从sd卡自更新nand flash里面的bootloader、内核、文件系统。bootloader采用fatfs来支持sd卡的fat32文件系统的访问。

2.1. bootloader固化

bootloader从/image/bootloader.bin读取文件到内存,然后调用nand写函数把代码下载固化进nand flash。对于s3c2416,此时的bootloader在nand flash最开始的位置,如果是nand boot,启动后CPU不通过任何校验直接把nand flash最前面的8k加载进内部ram(stepping stone),如果是irom nand启动,则CPU需要通过ecc校验成功后才会把nand flash里面的8k代码加载进stepping stone。此处bootloader采用ecc检验烧写方式用于同时支持nand boot以及irom nand启动,同时,bootloader从nand flash加载自身时需采用一致的读取方式。

2.2. linux内核固化

bootloader从/image/kernel.bin读取文件到内存,linux内核由bootloader安装以及加载,因此linux内核的储存位置本身与内核无关,由bootloader决定。bootloader可以决定把内核储存进如spi flash、sd卡等存储器,如果储存进nand flash,只需注意linux内核储存位置不与bootloader、根文件系统位置冲突即可。bootloader加载内核时需从安装内核的位置读取即可。

2.3. 根文件系统固化

bootloader从/image/rootfs.bin读取文件到内存,如果根文件系统为initrd,则根文件系统储存位置也与内核无关,由bootloader决定存储及加载。如果根文件系统为initramfs,并且跟linux内核链接在一起,那么bootloader无需再处理根文件系统了,因为linux内核包含了根文件系统,固化加载内核也同时处理了根文件系统。

如果根文件系统在nand flash、sd/mmc卡等存储设备中,首先linux内核必须支持相应的设备驱动以及文件系统。此处以nandflash为例说明,bootloader应该与linux内核协调约定nand flash的分区位置,因为此时根文件系统由linux内核挂载,与bootloader无关。bootloader把根文件系统下载固化进相应的分区位置,然后在启动内核时告之内核所在的分区。除此之外,nand flash还有oob区,对于yaffs文件系统,oob区用来存储yaffs的tag数据,坏块标记,ecc校验数据,这需要宿主机制作yaffs镜像工具mkyaffs2image、bootloader、linux内核协调约定,确保bootloader与linux内核有一致的nand layout,即bootloader的坏块标记位置、yaffs的tag数据位置应该与linux内核完全一致,如果linux内核采用了相应的nand ecc,那么bootloader在下载固化根文件系统时也应采用相同的nandecc方式进行填充oob区。任何一点bootloader与linux内核的不一致,将造成linux内核无法挂载根文件系统,引起内核panic。

Linux开发四_bootloader启动linux内核_第1张图片

图2-1 linux内核源码中的nand分区信息

Linux开发四_bootloader启动linux内核_第2张图片

图2-2 linux内核启动时的nand分区打印信息

3. 加载启动

3.1. bootloader硬件初始化

上电启动后,bootloader首先初始化必要的硬件,如内存控制器,整个系统时钟等,包括linux内核默认不初始化直接使用的部分,如外部总线时序、usb时钟等。

3.2. bootloader加载内核

bootloader从内核安装位置加载内核到内存,这个内存位置并没有需要特别注意的地方,一般默认是内存基址+0x8000处,linux内核会自行重定位到执行地址执行。如果采用initrd根文件系统,也由bootloader加载根文件系统到内存。

3.3. linux启动参数

bootloader可以给linux内核传递启动参数以控制其行为。linux内核启动参数位置并没有需要特别注意的地方,一般默认是内存基址+0x100处,bootloader通过标记列表的形式来传递启动参数。

标记列表以标记ATAG_CORE开始

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);

设置内存标记ATAG_MEM,标记内存的位置及大小

params->hdr.tag =ATAG_MEM;

params->hdr.size =tag_size (tag_mem32);

params->u.mem.start =DRAM_BASE;

params->u.mem.size =DRAM_SIZE; // 64M

params = tag_next(params);

设置命令行标记ATAG_CMDLINE,这是一个字符串,用来控制内核的行为。如"noinitrd root=/dev/mtdblock3 rootfstype=yaffs2init=/linuxrc console=ttySAC0",表示根文件系统在MTD3分区上,文件系统为yaffs2,系统启动后执行的第一个程序为linuxrc,控制台为ttySAC0。

params->hdr.tag =ATAG_CMDLINE;            

params->hdr.size =(sizeof(struct tag_header)+strlen(NandBootCmd)+1+4) >> 2;

strcpy(params->u.cmdline.cmdline,NandBootCmd);

params = tag_next(params);

标记列表以标记ATAG_NONE结束

params->hdr.tag =ATAG_NONE;

params->hdr.size = 0;

3.4. 内核启动环境

在交出CPU控制权之前,bootloader需要把CPU恢复成初始的环境,CPU必须禁止IRQ以及FIQ中断,处于特权模式,关闭MMU,使无效并且写回DCache数据到主存,禁止DCache,推荐ICache打开。

IRQ_Disable();

CP15_DisableDCache();

CP15_DisableICache();

CP15_CleanDCache();

CP15_DrainWriteBuffer();

CP15_InvalidateDCache();

CP15_InvalidateICache();

CP15_DisableMMU();

CP15_InvalidateTLB();

CP15_EnableICache();

3.5. 跳转到内核

bootloader跳转到内核时,将传递三个参数给内核,对于ARM为R0、R1、R2这三个寄存器。第一个参数R0必须为0,第二个参数R1为机器码,必须与linux内核一致才能启动,第三个参数为先前设置的内核启动参数地址位置。跳转到内核后,bootloader完全退出,由linux内核全权处理。

Kernel(0, MACH_TYPE,(uint32_t)params);

图3-1 linux内核启动

4. 附录

附录为arm交叉编译工具链下基于newlib的s3c2416 linux启动 bootloader工程,附带了newlib库,sd卡windows下烧录工具,工程直接make即可。

http://pan.baidu.com/s/1czo6N8

mdk下s3c2416 linux启动bootloader工程。

http://pan.baidu.com/s/1dFrGU7R

 

你可能感兴趣的:(嵌入式linux开发)