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。

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

图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

 

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内核启动流程可以分为两个阶段:第一阶段和第二阶段。 在第一阶段,当系统启动时,Bootloader(如U-Boot)会加载uImage到内存中,并根据头部信息将真正的内核解压到指定的地址。然后,内核启动第一阶段开始执行。在这个阶段,内核会校验Bootloader传递的机器ID等参数,并使能MMU(内存管理单元)等硬件。\[2\] 在第二阶段,内核启动第二阶段开始执行。在这个阶段,内核会解析Bootloader传递的内核启动参数,并进行内核初始化。接着,内核会挂载根文件系统,执行rest_init()函数,然后调用kernel_init()函数进行内核的初始化。最后,内核会运行init进程,通常是/sbin/init,来启动用户空间的进程。\[2\] 需要注意的是,内核源码中的所有函数都是在内核态下执行的,而应用程序必须在用户态下执行。因此,内核启动过程中的init程序是由根文件系统提供的,它是一个独立于内核的用户态程序。\[3\] 总结起来,Linux内核启动流程包括两个阶段:第一阶段是Bootloader加载内核并使能硬件,第二阶段是内核解析启动参数、初始化内核启动用户空间进程。\[2\] #### 引用[.reference_title] - *1* *3* [linux内核启动过程分析](https://blog.csdn.net/linuxweiyh/article/details/83382154)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [linux内核启动流程](https://blog.csdn.net/u010743406/article/details/115053622)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值