Boot Loader开发--Blob分析
本文详细描述Blob的原理、实现方法及开发要点,作为对自己工作的总结。 一、Bootloader简介 Boot Loader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。 通常,Boot Loader 是严重地依赖于硬件而实现的,特别是在嵌入式世界。每种CPU体系结构都有不同的 BootLoader。有些 Boot Loader 也支持多种体系结构的 CPU,比如 U-Boot 就同时支持 ARM 体系结构和MIPS 体系结构。除了依赖于 CPU 的体系结构外,Boot Loader 实际上也依赖于具体的嵌入式板级设备的配置。这也就是说,对于两块不同的嵌入式板而言,即使它们是基于同一种 CPU 而构建的,要想让运行在一块板子上的 Boot Loader 程序也能运行在另一块板子上,通常也都需要修改 Boot Loader 的源程序。 常用的Bootloader有U-Boot,Vivi,Blob,Redboot等。他们通常存储于系统的ROM中。对于嵌入式系统而言,主要是Flash,E2ROM等。一个嵌入式系统的典型存储空间分配如图(1)所示。 图(1)嵌入式系统典型存储空间分配 二、Blob功能 Blob通过串口输出信息,接收从串口的输入,blob的控制台就是通过串口实现的。文件传输也可以通过网口或者USB口进行以提高传输速度。其主要功能如下: ● 使用xModem协议下载文件到内存 ● Nor Flash在线烧写 ● Nand Flash在线烧写 ● Linux内核加载和引导 ● 网络和简单协议(TFTP、PING)支持 ● USB传输支持 ● 在线调试BLOB ???????? 和其他Bootloader一样,blob支持两种工作模式:加载模式和下载模式。分别描述如下: (1)加载模式 也称为“自主”(Autonomous)模式。也即 Boot Loader 从目标机上的某个固态存储设备上将操作系统加载到 RAM 中运行,整个过程并没有用户的介入。这种模式是 Boot Loader 的正常工作模式,因此在嵌入式产品发布的时侯,Boot Loader 显然必须工作在这种模式下。 (2)下载模式 在这种模式下,目标机上的 Boot Loader将通过串口连接或网络连接等通信手段从主机下载文件,比如:下载内核映像和根文件系统映像等。从主机下载的文件通常首先被 Boot Loader 保存到目标机的 RAM 中,然后再被 Boot Loader 写到目标机上的FLASH 类固态存储设备中。Boot Loader 的这种模式通常在第一次安装内核与根文件系统时被使用;此外,以后的系统更新也会使用 Boot Loader 的这种工作模式。工作于这种模式下的 Boot Loader 通常都会向它的终端用户提供一个简单的命令行接口。 ???????? 显然,两种模式的差别仅仅对于开发者才有意义的。在启动的stage 2,将kernel复制到RAM后,Blob在一定时间内会循环检查控制台有无输入,如果此时用户没有按任何按键,blob将自动引导操作系统启动,否则将进入下载模式,输出控制台信息。等待时间的长短在Main.c中设定。 三、Blob的文件组织结构 Blob->include ->blob???? ->arch:与硬件平台相关的头文件其它头文件,如xmodem.h等 ?????? ->src?????? ->blob:??????? ?? 大部分源代码在这个目录 ->diag:?????????? ?? 诊断测试代码,不常用 ->lib?????? :???????????? ??库文件,如串口驱动、led驱动等 ?????? ->tools: ?????? ->utils: 四、Blob空间分配 在../include/blob/arch/lubbock.h中,定义了系统的存储空间分配,包括Bootloader、Linux内核,文件系统等。主要内容如下: /* the baseaddress were BLOB is loaded by the first stage loader */ #define BLOB_ABS_BASE_ADDR???? (0xA0000000) /* where dovarious parts live in RAM */ #define BLOB_RAM_BASE?????????????? (0xA0100000) #define KERNEL_RAM_BASE?????????? (0xA0200000)?????? #define PARAM_RAM_BASE??????????? (0xA0180000) #define RAMDISK_RAM_BASE??????? (0xA0400000) /* and wheredo they live in flash */ #define BLOB_FLASH_BASE??????????? (0x00000000) #define BLOB_FLASH_LEN????????????? (64* 1024) #define PARAM_FLASH_BASE? (BLOB_FLASH_BASE+ BLOB_FLASH_LEN) #define PARAM_FLASH_LEN?????????? (64*1024) #define KERNEL_FLASH_BASE????????????? (0x00020000) #define KERNEL_FLASH_LEN????????? (1920*1024)???????? //1920=2048-128 #define RAMDISK_FLASH_BASE???? (0x00200000) #define RAMDISK_FLASH_LEN????????????? (14* 1024 * 1024) //for rootfs #define JFFS2_FLASH_BASE??????????? (0x01000000)??????? //17M--32M #define JFFS2_FLASH_LEN???????????????????? (4* 1024 * 1024) /* theposition of the kernel boot parameters */ #define BOOT_PARAMS???? ?????? (0xA0000100) ???????? 因此,整个32M的Flash空间分配如图(2)。
图(2)Blob对系统Flash的规划 五、Blob启动分析 Blob被放在Flash的0x0地址起始处,ARM微处理器复位后自动跳到0x0地址执行,因此系统复位时首先运行blob程序。Blob负责完成对系统硬件的初始化、对SDRAM进行测试,然后加载操作系统,并跳到操作系统处开始执行。Blob的启动流程如图(3)所示。 图(3) blob启动流程 Blob支持自烧写,即通过blob更新blob。对于一个空的目标系统,首先是通过JTAG或者其他手段将blob程序烧写到flash中去,之后,如果需要修改blob,则可以直接使用blob提供的工具来完成更新了。要实现这一功能,blob程序必须在SDRAM中运行。因此,blob键程序分成两个段:stage1和stage2,如图(4)所示。 图(4)Blob的两个阶段 ???????? 在Stage 1,基本都是用汇编语言编写。通常要完成以下工作; (1)???初始化硬件; (2)???为Blob的Stage 2准备RAM空间; (3)???复制Stage 2到RAM; (4)???设置好堆栈; (5)???跳到Stage 2 的C程序入口点; 当blob由stage1执行到Stage 2时,程序已经在SDRAM中运行了。下面看一下是如何跳转的。 在../include/blob/arch/lubbock.h中定义了很多宏变量: /* the baseaddress were BLOB is loaded by the first stage loader */ #define BLOB_ABS_BASE_ADDR???? (0xA0000000) /* where dovarious parts live in RAM */ #defineBLOB_RAM_BASE??????? (0xA0100000) /* and wheredo they live in flash */ #define BLOB_FLASH_BASE??????????? (0x00000000) #define BLOB_FLASH_LEN????????????? (64* 1024) ? 在Start.S中,初始化硬件后进行了Stage 2的搬移: BLOB_START:????? .word ? BLOB_ABS_BASE_ADDR …… relocate: ?????? adr?? r0,_start /* blob入口地址到r1,在此相当于基址 */ ? ?????? /* relocate the second stage loader */ ?????? add? r2,r0,#(64 * 1024)?????? ?????? /* blob maximumsize is 64kB */ ?????? add? r0,#0x1000???????????? /* skip first 4k bytes,Only Stage 2 */ ?????? ldr?? r1,BLOB_START???? /* 目的地址 */ ? ?????? /* r0 =source address ?????? ?* r1 = target address ?????? ?* r2 = source end address ?????? ?*/ copy_loop: ?????? ldmia????? r0!,{r3-r10} /* ldmia批量加载,r0指向的地址上的多字数据,保存到r3-r10中,r0的值更新*/ ?????? stmia?????? r1!,{r3-r10}/*将r3-r10中的数据存储到R1指向的地址上,R1值更新*/ ?????? cmp r0,r2?/*源开始地址是否=源结束地址*/ ?????? ble?? copy_loop?/* 不等就继续复制数据 */ …… ???????? 可以看到,Blob的Stage 1将flash地址0x1000开始的60K空间全部搬移到了BLOB_START定义的RAM空间。在搬移过程中,跳过了前4K的空间,因为前4K存放的blob的Stage 1。Blob如何将代码分割为前4k和后60K的,我们下面在讨论。搬移成功后,紧接着是下面的跳转语句: ?????? /*blob is copied to ram,so jump to it */ ?????? ldr?? r0,BLOB_START ?????? mov pc,r0???????????? // 跳转 通过将PC指针指向BLOB_START即实现了跳转。 Stage 2 开始并不是Mani.c文件,而是trampoline.S,意为“跳板”。该文件内容如下: _trampoline: ?????? /*clear the BSS section */ ?????? ldr?? r1,bss_start ?????? ldr?? r0,bss_end ?????? sub? r0,r1 ?????? /* r1 = startaddress */ ?????? /* r0 =#number of bytes */ ?????? mov r2,#0 clear_bss: ?????? stmia?????? r1!,{r2} ?????? subs r0,#4 ?????? bne? clear_bss ?????? /*setup the stack pointer */?????? //设置堆栈 ?????? ldr?? r0,stack_end ?????? sub? sp,#4 ? ?????? /*jump to C code */ ?????? bl??? main???????????????????? // 这里跳转到C程序,即Main.C的main()函数 ?????? /*if main ever returns we just call it again */ ?????? b???? _trampoline bss_start: ?????? .word????? __bss_start bss_end:? ?????? .word????? __bss_end stack_end:????? .word????? __stack_end 这是,Blob才真正开始执行C程序代码了。Blob的Stage 2一般使用C语言编写,便于实现更复杂的功能和取得更好的代码可读性和可移植性。Stage 2一般完成的功能是: (1)初始化本阶段要使用到的硬件设备 (2)检测系统内存映射(memory map) (3)将 kernel 映像和根文件系统映像从 flash 上读到 RAM 空间中 (4)为内核设置启动参数 (5)?调用内核 Stage 2中程序的执行过程如图(5)所示。 图(5)Blob Main函数流程图 启动内核掉用parse_command("boot")完成得,该函数实际执行的函数是在../src/blob/linux.c中的boot_linux(),如下: static int boot_linux(int argc,char *argv[]) { ???????? int i,len; ???????? struct param_struct *params; ???????? void (*theKernel)(int zero,int arch) = (void (*)(int,int))KERNEL_RAM_BASE; ? ???????? /* Initial kernel params */ ???????? params = (struct param_struct *)BOOT_PARAMS; ???????? params->u1.s.page_size = LINUX_PAGE_SIZE; ???????? params->u1.s.nr_pages = (0x04000000 >>LINUX_PAGE_SHIFT); ???????? memcpy(params->commandline,boot_paramsm,strlen(boot_paramsm)); ???????? setup_start_tag();?????????? //传递内核参数 ???????? setup_memory_tags(); ???????? setup_commandline_tag(argc,argv); setup_initrd_tag(); ???????? setup_ramdisk_tag(); ???????? setup_end_tag(); ? ???????? /* we assume that the kernel is in place */ ???????? SerialOutputString("Starting kernel ...n"); ???????? serial_flush_output(); ? ???????? /* disable subsystems that want to be disabled beforekernel boot */ ???????? exit_subsystems(); ? ???????? /* start kernel */ ???????? theKernel(0,ARCH_NUMBER);? // 启动内核 ???????? SerialOutputString("Hey,the kernel returned! Thisshould not happen.n"); ???????? return 0; } 这里面非常重要的就是theKernel()函数指针。这是在C语言中实现程序跳转的很好的方法。theKernel()有两个参数,根据POSIX准则,第一个参数将传递给CPU的R0寄存器,第二个则是R1寄存器。在跳到内核是,必须要满足以下条件: (1) CPU 寄存器的设置: ·R0=0;(theKernel的第一个参数) (2) CPU 模式: ·必须禁止中断(IRQs和FIQs); (3)Cache 和 MMU 的设置: ·MMU 必须关闭; 至此,Blob交出CPU使用权了,一次正常的引导过程完成了。而进入Blob控制台后,blob在死循环中等待用户输入,并执行用户命令。Blob支持简单的命令集,比如,help命令打印帮助信息,xdownload命令下载文件,flash命令将下载的文件烧写到flash中,boot命令引导内核启动等。 六、移植到PXA255平台需要做的工作 Blob支持的平台比较多,因此移植blob到一个已有的平台是比较简单的事情。只需要修改为数不多的几个文件即可。移植到一个新的平台则相对复杂,具体可以参考../doc/porting.txt。下面以第一种情况为例说明需要修改的几个地方。 (1)在../include/blob/arch/lubbock.h修改存储空间分配方案及IO寄存器、Mem控制器的初始值; (2)如果系统由LED,在../src/lib/led.c和../src/blob/ledasm.S中修改led的控制函数; (3)在memsetup-pxa.S中设置CPU工作时钟、内存访问速度等; (4)在Main.c中修改控制台参数; (5)在../src/lib/serial-pxa.c中选择控制台串口,在../src/blob/initcalls.c中设置默认的控制台串口速率。 (6)在../src/blob/flash.c中修改FLASH读写函数(如果不是同一种Flash的话,这里用的是IntelE28F128J3A); (7)对于不同的Linux内核,需要修改../src/blob/linux.c中的参数和boot_linux()函数。 (8)如果需要网卡功能和USB功能支持,则需要修改相应的文件,这里不再详述。 七、其它主要程序分析 1、? Blob链接时如何区分Stage 1 和 Stage 2? 分析Blob的Makefile可以得到答案。../src/blob/Makefile.in文件写得很清楚: # ---- Blob first stage loader--------------------------------------- # WARNING: start.S *must* be the first file,otherwisethe target will # be linked in the wrong order! blob_start_elf32_SOURCES = ???????? start.S ???????? ledasm.S ???????? testmem.S …… blob_start_elf32_DEPENDENCIES = ???????? memsetup-pxa.o ???????? start-ld-script ? # ---- Blob second stage--------------------------------------------- # WARNING: trampoline.S *must* be the first file,otherwise the target # will be linked in the wrong order! blob_rest_elf32_SOURCES = ???????? trampoline.S ???????? flashasm.S ???????? stack.S ???????? testmem2.S ???????? bootldrpart.c ???????? commands.c ???????? flash.c ???????? initcalls.c ???????? linux.c ???????? main.c ???????? memory.c ???????? param_block.c ???????? partition.c ???????? reboot.c ???????? uucodec.c ???????? xmodem.c …… 在../src/blob/start-ld-script和../src/blob/rest-ld-script两个文件中定义了链接时各个代码段的地址。 2、? Blob使用的文件传输协议 Blob使用Xmodem协议传输文件,源代码可以参考../src/blob/xmodem.c。 八、Blob编译说明 使用arm-linux-gcc可以编译blob,这里使用的arm-linux-gcc版本为3.3.2。编译blob时必须指定Linux源码的安装路径,原因在于编译blob时需要寻找在linux.c程序中语句#include<asm-arm/setup.h>包含的头文件,该头文件包含了Linux内核启动参数结构体,不同版本的linux内核可能不一样。 ./configure-pxa255可以自动编译生成blob可执行文件。该脚本实际执行如下指令: make distclean export CC=/usr/local/arm/3.3.2/bin/arm-linux-gcc export OBJCOPY=/usr/local/arm/3.3.2/bin/arm-linux-objcopy ./configure --with-board=lubbock --with-cpu=pxa255--with-linux-prefix=/usr/src/linux-2.6.8.1-sniffer --enable-usb --with-eth=usb--enable-debug Make 各个选项的说明可以参考configure.in文件。更为详细的说明参考blob的README文件。 九、Blob使用说明 参考blob的README文件。Blob的控制台界面如下: 图(6)Blob控制台 原文主要载于 http://blog.163.com/xd_zxw/blog/static/15473901200702101959175/ ?本人稍作编辑修改。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |