嵌入式系统开发原理与实践陈文智等编著清华大学出版社重点大学计算机专业系列教材第四章嵌入式系统的BootLoader技术•1.BootLoader程序的基本概念•2.BootLoader的典型结构框架•3.BootLoader实验–实验一BootLoader应用实验–实验二U-BOOT的分析和移植1.BootLoader程序的基本概念•一个嵌入式Linux系统从软件的角度看通常可以分为四个层次:–1.引导加载程序。•包括固化在固件(firmware)中的boot代码(可选),和BootLoader两大部分。–2.Linux内核。•特定于嵌入式板子的定制内核以及内核的启动参数。–3.文件系统。•包括根文件系统和建立于Flash内存设备之上文件系统。•通常用ramdisk来作为rootfs。–4.用户应用程序。特定于用户的应用程序。•有时在用户应用程序和内核层之间可能还会包括一个嵌入式图形用户界面。常用的嵌入式GUI有:MicroWindows和MiniGUI懂。1.BootLoader程序的基本概念•BootLoader就是在操作系统内核运行之前运行的一段小程序–初始化硬件设备和建立内存空间的映射图–将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境•BootLoader所支持的硬件环境–BootLoader是严重地依赖于硬件而实现的–每种不同的CPU体系结构都有不同的BootLoader•BootLoader的安装地址–系统加电或复位后,所有的CPU通常都从某个由CPU制造商预先安排的地址上取指令。–系统的BootLoader程序通常安排在地址0x00000000处•BootLoader相关的设备和机制–主机和目标机之间一般通过串口建立连接–BootLoader软件在执行时通常会通过串口来进行I/O–超级终端•BootLoader的启动过程–通常多阶段的BootLoader能提供更为复杂的功能,以及更好的可移植性–分为stage1和stage2两部分•BootLoader的操作模式–启动加载模式–下载模式•BootLoader与主机之间的通信设备及协议–最常见的是串口,协议xmodem/ymodem/zmodem–以太网,协议tftp•常用嵌入式Bootloader–U-boot:通用引导程序,ARM/XSCALE平台–Blob:LART等硬件平台的引导程序,StrongARM–RedBoot:基于eCos的引导程序2.BootLoader的典型结构框架•操作系统的角度看,BootLoader的总目标就是正确地调用内核来执行•大多数BootLoader都分为阶段1和阶段2两大部分–阶段1实现依赖于CPU体系结构的代码(汇编)–阶段2实现一些复杂的功能(C语言)2.1BootLoader阶段1介绍•BootLoader的阶段1通常包括以下步骤:•1)硬件设备初始化。–屏蔽所有的中断–设置CPU的速度和时钟频率–RAM初始化–初始化LED–关闭CPU内部指令/数据Cache•2)为加载阶段2准备RAM空间–除了阶段2可执行映象的大小外,还必须把堆栈空间也考虑进来–一般1M就够了•Blob是0xc0200000开始的1MB空间•推荐:(RamEnd–1MB,RamEnd)–标记•Stage2_end=stage2_start+stage2_size•2)为加载阶段2准备RAM空间–必须确保所安排的地址范围的的确确是可读写的RAM空间1.先保存memorypage一开始两个字的内容2.向这两个字中写入任意的数字3.立即将这两个字读回,若不是,则说明不是一段有效的RAM空间4.再向这两个字写入任意数字5.立即将这两个字读回。若不是,则说明不是有效的RAM空间6.恢复这两个字的原始内容,测试完毕。•3)拷贝阶段2到RAM中•复制时要确定两点:–阶段2的可执行映像在固态存储设备的存放其实地址和终止地址–RAM空间的起始地址•4)设置堆栈指针sp–设置堆栈指针是为了执行C语言代码做好准备–通常可以设置为sp=stage2_end–4–此时,BootLoader的阶段2可执行映象刚被拷贝到RAM空间时的系统内存布局,如下图:•5)跳转到阶段2的C入口点–修改PC寄存器为合适地址来实现2.2BootLoader阶段2介绍•trampoline(弹簧床)的概念–进入main()函数•用汇编语言写一段trampoline小程序•在trampoline汇编小程序中用CPU跳转指令跳入main()函数中去执行;而当main()函数返回时,CPU执行路径显然再次回到trampoline程序.text.globl_trampoline_trampoline:blmain/*ifmaineverreturnswejustcallitagain*/b_trampoline2.2BootLoader阶段2介绍•1)初始化本阶段要使用到的硬件设备–初始化至少一个串口,以便和终端用户进行I/O输出信息–初始化计时器等•2)检测系统的内存映射–所谓内存映射就是指在整个4GB物理地址空间中有哪些地址范围被分配用来寻址系统的RAM单元–例如,SA-1100CPU中,从0xC0000000开始的512M–但是,具体嵌入式系统并不实现全部预留地址,可能仅用64M–因此,必须检测整个系统的内存映射情况•2)检测系统的内存映射–可以用如下数据结构来描述RAM地址空间中的一段连续的地址范围:typedefstructmemory_area_struct{u32start;/*内存空间的基址*/u32size;/*内存空间的大小*/intused;/*1已实现0未实现*/}memory_area_t;•2)检测系统的内存映射–整个CPU预留的RAM地址可用数组表示memory_area_tmemory_map[NUM_MEM_AREAS]={[0…(NUM_MEM_AREAS-1)]={.start=0,.size=0;.used=0},};•2)检测系统的内存映射–内存映射的检测memory_area_tmemory_map[NUM_MEM_AREAS]={[0…(NUM_MEM_AREAS-1)]={.start=0,.size=0;.used=0},};•3)加载内核映像和根文件系统映像–规划内存占用的布局•内核映像所占用的内存范围–一般是MEM_START+0X8000到约1MB的空间•根文件系统所占用的内存范围–一般是MEM_START+0x100000开始的地方•3)加载内核映像和根文件系统映像–从Flash上拷贝–由于像ARM这样的嵌入式CPU通常都是在统一内存地址空间中寻址FLASH等固态存储设备,因此从闪存上读取数据与从RAM内存单元中读取数据并没有什么不同while(count){*dest++=*src++;count-=4;}•4)设置内核的启动参数–标记列表(taggedlist)的形式来传递启动参数,启动参数标记列表以标记ATAG_CORE开始,以标记ATAG_NONE结束–嵌入式Linux系统中,通常需要由BootLoader设置的常见启动参数有:ATAG_CORE、ATAG_MEM、ATAG_CMDLINE、ATAG_RAMDISK、ATAG_INITRD•例:设置ATAG_CORE的代码如下:params=(structtag*)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);BOOT_PARAMS表示内核启动参数在内存中的起始基地址,指针params是一个structtag类型的指针。宏tag_next()将以指向当前标记的指针为参数,计算出当前标记的下一个标记的起始地址initrd•initrd的英文含义是bootloaderinitializedRAMdisk,就是由bootloader初始化的内存盘。•在linux内核启动前,bootloader会将存储介质中的initrd文件加载到内存,内核启动时会在访问真正的根文件系统前先访问该内存中的initrd文件系统。•在bootloader配置了initrd的情况下,内核启动被分成了两个阶段,第一阶段先执行initrd文件系统中的某个文件,完成加载驱动模块等任务,第二阶段才会执行真正的根文件系统中的/sbin/init进程。•5)调用内核–CPU寄存器的设置:•R0=0;•R1=机器类型ID;关于机器类型号,可以参见:•linux/arch/arm/tools/mach-types。•R2=启动参数标记列表在RAM中起始基地址;–CPU模式:•必须禁止中断(IRQs和FIQs);•CPU必须SVC模式;–Cache和MMU的设置:•MMU必须关闭;•指令Cache可以打开也可以关闭;•数据Cache必须关闭;•5)调用内核–C语言调用方式void(*theKernel)(intzero,intarch,u32params_addr)=(void(*)(int,int,u32))KERNEL_RAM_BASE;……theKernel(0,ARCH_NUMBER,(u32)kernel_params_start);2.3关于串口终端•向串口终端打印信息也是一个非常重要而又有效的调试手段•如果碰到串口终端显示乱码或根本没有显示的问题,可能是因为:–BootLoader对串口的初始化设置不正确–运行在host端的终端仿真程序对串口的设置不正确•BootLoader启动内核后却无法看到内核的启动输出信息:–确认内核在编译时是否配置了对串口终端的支持,并配置了正确的串口驱动程序–BootLoader对串口的初始化设置是否和内核对串口的初始化设置一致–还要确认BootLoader所用的内核基地址必须和内核映像在编译时所用的运行基地址一致3.BootLoader实验•实验一BootLoader应用实验•实验二U-BOOT的分析和移植实验一BootLoader应用实验(1)•烧写XsBase255的BootLoader–编译生成XsBase255专用的JTAG程序Jflash-XSBase255–编译生成XSBase的BootLoaderx-boot255–正确连线–利用JTAG烧写BootLoader[root@XSBaseJTAG]#./Jflash-XSBase255x-boot255实验一BootLoader应用实验(2)•熟悉使用Bootloader指令,执行各个指令后将其结果与下表的description进行比较UsageHelpDescription对各个指令的简单的说明。ArgumentsNoneExampleX-HYPER255HelpUsageReload[kernel/ramdisk]Description将Flash中纪录的image复制到SDRAM为了复制kernelimage到SDRAM,Autoboot时自动执行Arguments将Kernel–flash的kernelimage复制到SDRAM0xa0008000将Ramdisk–flash的ramdisk复制到SDRAM0xa0800000ExampleX-HYPER255reloadkernelUsageTftp[file][loader/kernel/root/ramdisk]Tftp[file][addr]Description通过Ethernet将Host的映像文件下载到SDRAM中ArgumentsLoader–将接收到的文件储存到loader的SDRAM0xa0000000Kernel–将接收到的文件储存到kernel的SDRAM0xa0008000Root–将接收到的文件储存到0x