嵌入式BootLoader技术
PC机的引导代码就是BIOS,由主板厂商提供,BIOS的代码一般都是保密的,负责完成硬件检测和资源的分配。完成任务后,会将控制权交给操作系统的BootLoader.
在嵌入式系统中,由于硬件资源的限制,通常没有BIOS那样的固化程序,所以整个系统的加载启动就全部由操作系统的BootLoader来完成处理。一般来说,BootLoader会映射到0x00000000地址片,所以当系统加电后通常会转到该地址片,也就是运行系统的BootLoader。
>> BootLoader的基本概念
BootLoader(引导装载程序),就是运行操作系统内核之前的所运行的一段小程序,通过这段小程序,可以对系统的硬件设备进行初始化、建立内存空间的映射图,从而将系统的软硬件设置为一个合适的环境,以便为最终调用操作系统内核作好正确的准备。
一、 BootLoader所支持的CPU和嵌入式板
一般来说,不同的CPU体系结构需要不同的BootLoader,但有些功能强大的也会同时支持不同体系结构的CPU,U-Boot就同时支持多种体系结构,如X85,ARM,DSP等。除了依赖CPU的体系结构外,也依赖于具体的嵌入式板级设备的配置,如板卡的硬件地址的分配,RAM芯片的类型以及其他外设的类型等。
现有的各种BootLoader(支持的CPU):
LILO(X86)、GRUB(x86)、Blob(ARM),U-Boot(x86,ARM,PowerPC),vivi(ARM)
二、 BootLoader的安装点和启动过程
在嵌入式系统加电或复位,所有CPU从CPU制造商预先安排的地址上取指令,ARM加电或复位后,通常从地址0x00000000取第一条指定。而固态存储设备通常被映射到这个预先安排的地址在,从而在系统加电后,CPU首先执行BootLoader程序。
BootLoader所在的存储设备的空间分配图:
Bootloader | 启动参数 | 内核 | 根文件系统 | 应用程序。。。 |
三、 BootLoader的操作模式
从开发人员角度,Bootloader包含两种操作模式:
1、 启动加载:也叫自主模式,Bootl oad从目标机上的某个存储设备将操作系统加载到RAM中运行,没有用户的介入,也是默认、正常的工作模式。
2、 下载模式:目标机上的Bootload将通过串口连接或网络等通信手机从主机上下载文件,先被Bootload保存到目标机的RAM中,然后再被Bootload写到固态存储设备上。也就是烧写时的工作模式。
四、 BootLoader与主机之间的通信方式
主机和目标机之间一般用串口建立连接,通常会通过串口来进行I/O,比如:输出打印信息,从串口读取用户控制字符等,最常见就是,目标机上的Bootload通过串口与主机之间进行文件传输,传输协议通过Xmodem/ymodem协议。
五、 Bootload的典型结构框架
Bootload的引导和装载分为两部分,一部分依赖于CPU的体系结构,通常放在stag1中,汇编语言来实现;第二部分stag2,通常由C语言来实现,可以复杂的功能,同时有更好的可读性和可移植性。
1、 stag1通常包括以下步骤
(1) 硬件设备初始化
屏蔽所有中断;设备CPU的速度和时钟频率;RAM初始化;LED初始化;关闭CPU内部指令/数据cache
(2) 为加载Bootload的stag2准备RAM空间
(3) 复制Bootload的stag2到RAM空间
(4) 设备好堆栈
(5) 跳转到stag2的C入口点。
2、 stag2通常包括以下步骤:
(1) 初始化本阶段要使用的硬件设备。
至少初始化一个串口,初始化计时器,输出提示信息。
(2) 检测系统内存映像RAM
(3) 将kernel映像和根文件系统映象从Flash上读到RAM空间中。
(4) 为内核设置启动参数
(5) 调用内核
>>引导加载程序:GRUB
4.2.1 LINUX操作系统的启动流程
1、加载BIOS的硬件信息,并获取第一个启动设备的代号。
2、读取第一个启动设备的MBR(主引导记录)的引导加载程序(即lilo,grub,spfdisk等)的启动信息。
3、加载核心操作系统的内核信息,内核开始解压缩,并且尝试驱动所有硬件设备。
4、内核执行init程序(/sbin/init)并获取运行信息。
Init程序所做的工作很多,除了利用设置文件/etc/inittab来获取运行等级之外,还会通过运行等级的设置值启动不同的服务项目。
5、init程序执行/etc/rc.d/rc.sysinit文件。
表示“我开始加载各项系统服务之前,先做好整个系统环境,主要使用该脚本来设置系统环境。”主要工作:
(1) 获取网络环境(/etc/sysconfig/network)与主机类型。
(2) 测试与载入内存设备/proc及USB设备/sys
(3) 决定是否启动SELinux
(4) 接口设备的检测与PNP(即插即用)参数的测试
(5) 用户自定义模块的加载(/etc/sysconfig/modules/*.modules)
(6) 加载内核的相关设置(/etc/sysctl.conf)
(7) 设置系统时间
(8) 设置终端控制台的字型
(9) 设置RAID与LVM等硬盘功能
(10) 以fsck检验磁备文件系统
(11) 进行磁盘配额quoto的转换。
(12) 重新以可读取模式载入系统磁盘
(13) 启动quoto功能
(14) 启动系统随机数设备
(15) 清除启动过程中的临时文件
(16) 将启动相关信息加载到/var/log/dmesg文件中
6、启动系统服务与相关启动设置文件
/etc/rc.d/rc.n与/etc/sysconfig
7、用户自定义引导启动程序/etc/rc.d/rc.local
8、根据/etc/inittab设置加载终端(tty1...)或X-Window界面
4.2.2 Grub
一、该引导加载程序分成阶段来执行:
1、stag1 :第一阶段为主程序,这个主程序必须安装在启动区,即MBR或超级块(第一个扇区)。
MBR是整个硬盘的第一个扇区,大小不可能超过512B.
所以第一阶段通常仅安装引导加载程序的最小主程序,并没有安装加载程序的相关设置文件。
3、 stag2:第二阶段为载入引导加载程序的所有设置与相关环境参数文件,一般在/boot下面。
二、GRUB(Grand Unified Bootloader)
特点:1、支持启动时使用命令行模式
3、 支持MD5加密保护
4、 可从ext2/ext3,JFS,FAT,MINIX,FFS文件系统上启动
5、 其配置为/boot/grub/grub.conf
6、 若硬盘上的MBR被更改过,可使用grub-install /dev/sdax来重新安装grub
例:1、dd if=/dev/sda of=mbr bs=512 count=1
Hexdump –C mbr
把sda的第一个扇区的512B导出
55 aa :硬盘的有效识别标识
例2: stag2:/boot/grub/grub.conf
Root (hd0,0)表示内核文件放置的分区
Kernel:后面接的是内核的文件名,后面的是内核的参数
Initrd:使用initrd制作出的RAM Disk的文件名
例3:破坏MBR,即grub的第一阶段代码
Dd if=/dev/zero of=/dev/sda bs=512 count=1
解决方案:
1、 重启reboot
2、 F12引导 boot:3 rescue
3、 Chroot /mnt/sysimage
4、 Grub-install /dev/sda
例4:破坏第二阶段代码
Mv /boot/grub/grub.conf /mnt
解决:1、grub> root(hd0,0)
2、grub>kernel /vmlinux-2.6.18
3、grub>initrd /initrd-2.6.18.img
4、grub>boot
4.3 bootloader---vivi
>> vivi简介
Vivi是韩国MIZI Research公司为其开发的SMDK2410开发板编写的一款Bootloader,目前的版本是0.1.4,vivi也有两种工作模式,启动加载可以在一段时间后自行启动Linux内核,这是vivi的默认模式;下载模式,vivi为用户提供一个命令行接口,通过接口可以使用vivi提供的一些命令:
1、 load:把二进制文件载入Flash或者RAM。
2、 part:操作MTD分区信息,显示、增加、删除、复位、保存MTD分区。
3、 param 设置参数
4、 boot启动系统
5、 flash 管理Flash,比如删除Flash的数据
4.3.2 vivi源码目录
包括arch,init,lib,drivers和include等几个目录,共有200多个文件:
1、 arch:此目录包括了所有vivi支持的目标板的子目录,如:s3c2410目录
2、 drivers:其中包括了引导内核需要的设备的驱动程序。mtd目录下的maps,nand,nor三个目录分别是内存映射、NAND Flash驱动和NOR Flash驱动。
3、 init:这个目录只有main.c和version.c两个文件,vivi将从main函数开始执行。
4、 lib:一些平台公共的接口代码,比如:time.c里的udelay()
5、 include:头文件的公共目录,其中s3c2410.h定义了该处理器的一些寄存器、NANDFlash的一些寄存器。
Platform/smdk2410.h定义与开发相关的资源配置参数,我们只需修改这个文件就可以配置目标板的参数,如波特率、引导参数、物理内存映射等。
一、 vivi的第一阶段代码
vivi/arch/s3c2410/head.S:
1、 关闭看门狗
2、 禁止所有中断
3、 初始化系统时钟
4、 始始化内存控制寄存器
R1:内存控制器的基地址
R2:参数表的起始地址
R3:参数表的结束地址
5、 检查是否从掉电模式唤醒,若是,则调用WakeupStart函数进行处理。
6、 点亮所有LED
7、 初始化UART0:
设置GPIO,选择UART0使用的引脚;初始化UART0,设置工作方式,波特率115200,8数据位,无检验,1个停止位
8、 将vivi所有代码从nand Flash中复制到SDRAM
9、 跳到init/main.c中的main函数
二、 vivi的第二阶段代码
init/main.c
1、 输出vivi版本信息,用于将内存清零
2、 初始化定时器和设置GPIO引脚功能
3、 初始化MMU,启动MMU
4、 初始化堆栈区域
5、 检测所有的NAND Flash型号,构造MTD设备
6、 传递启动内核的命令参数
7、 命令处理函数
8、 启动vivi_shell,终端,调用OS内核
>>vivi的编写、编译
配置vivi可以使用以下命令:
Make distclean
Make clean
Make menuconfig根据自己的开发板进行适当的配置,保存退出后执行make开始编译。
例1:修改vivi启动时显示信息。
修改init/version.c
修改字符串vivi-banner
例2:添加mytest命令
修改init/main.c 中init_builtin_cmds增加add_command
添加命令功能:
1、 复制文件
Cp –af ./arch/s3c2410/smdk2410_test.c ./lib/my_test.c
2、 编辑./lib/Config_cmd.in文件
Bool ‘built-in my test command’ CONFIG_MY_TEST
3、 编辑./lib/Makefile
Obj -$(CONFIG_MY_TEST)+=my_test.o
4、 编辑my_test.c文件
5、 在./lib/command.c文件中的init_builtin_cmds中添加命令
Extern user_command_t my_test_cmd;
add_command(&my_test_cmd);
user_command_t mytest_cmd={....} ;
3、 编译配置
Make menuconfig添加命令
[ ]built_in my test command
4、编译make