《s3c2440上bootloader代码分析》由会员分享,可在线阅读,更多相关《s3c2440上bootloader代码分析(48页珍藏版)》请在金锄头文库上搜索。
1、Bootloader,Bank 的划分,面对不同速度、总线宽度的外设,通常,嵌入式处理器都把外部总线分成不同的Bank空间,对应不同的地址。 不同的Bank可以有不同的配置,或者对应的功能也不一样,支持的外设也不同。 S3C2440的Bank0-5支持ROM、SRAM等那些线性寻址的存储器或者外设;Bank6-7,还可以支持SDRAM、DRAM等。而且,每一个Bank都可以配置成8-32bit的总线,大端(big endian)或者小端(little endian)等,嵌入式处理器的启动过程(1),通常系统上电(或者复位)以后,程序从地址空间的0x0开始:即PC(程序计数器)指针,指向0x0。
2、从这个地址开始读取指令并运行。通常这个地址对应的是Bank0。,嵌入式处理器的启动过程(2),因为系统是从bank0上引导的,所以,在Bank0上连接保存有启动代码的Flash ROM,这就是我们常说的Boot ROM。,Bootloader要实现的主要工作1,硬件系统自检 配置其他Bank或者端口、外设等工作模式 处理中系统的中断 在不支持remap的处理器中,中断必然要经过Boot ROM的空间,这时,Boot ROM需要处理的任务就是把固定的中断向量映射到一个可编程的中断处理子程序的地址。,Bootloader要实现的主要工作2,引导操作系统 系统配置完成以后, Bootloader需要
3、把操作系统(或者其他程序)装载到SDRAM(就是系统的RAM区),然后,把PC指针指向程序的RAM空间,使操作系统启动,这就是引导。,Bootloader要实现的主要工作3,Flash(Boot ROM)编程 对引导Flash(或者其他的非易失性存储器)编程,通过串口或者以太网口下载编译成功的操作系统或者应用程序,甚至Boot ROM本身。 要想实现Boot ROM的自编程,必须让Boot 程序是在RAM中运行。,Bootloader的制作,Boot ROM使用来配置系统启动的。没有Boot ROM,系统就不能配置,更不能启动。 Boot ROM可以自编程,但是,第一次系统启动用的Boot R
4、OM是如何烧录的呢? 使用插座连接Flash配合编程器 通过ARM的JTAG接口,Bootloader,Bootloader的概念,Boot Loader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。 通常,Boot Loader 是严重地依赖于硬件而实现的,特别是在嵌入式世界。因此,在嵌入式世界里建立一个通用的Boot Loader 几乎是不可能的。尽管如此,我们仍然可以对Boot Loader 归纳出一些通用的概念来,以指导用户特定的Boot
5、Loader 设计与实现。,嵌入式系统离不开Bootloader,每种CPU 体系结构都有不同的BootLoader。 有些Boot Loader 也支持多种体系结构的CPU,比如U-Boot 就同时支持ARM 体系结构和MIPS 体系结构。 除了依赖于CPU 的体系结构外,Boot Loader 实际上也依赖于具体的嵌入式板级设备的配置。这也就是说,对于两块不同的嵌入式板而言,即使它们是基于同一种CPU 而构建的,要想让运行在一块板子上的Boot Loader 程序也能运行在另一块板子上,通常也都需要修改Boot Loader 的源程序。,Boot Loader 的安装媒介,系统加电或复位后
6、,所有的CPU 通常都从某个由CPU 制造商预先安排的地址上取指令。比如,基于ARM7 core 的CPU 在复位时通常都从地址0x00000000 取它的第一条指令。 基于CPU 构建的嵌入式系统通常都有某种类型的固态存储设备(比如:ROM、EEPROM 或FLASH等)被映射到这个预先安排的地址上。因此在系统加电后,CPU 将首先执行Boot Loader 程序。,典型存储空间分配结构,bootloader,启动参数,内核,根文件系统,Blob的存储器区域分配,Bootloader空间结构划分,控制Boot Loader 的设备或机制,主机和目标机之间一般通过串口建立连接,Boot Loa
7、der 软件在执行时通常会通过串口来进行I/O,比如:输出打印信息到串口,从串口读取用户控制字符等,Boot Loader 的操作模式,大多数Boot Loader 都包含两种不同的操作模式:“启动加载”模式和“下载”模式,这种区别仅对于开发人员才有意义。 从最终用户的角度看,Boot Loader 的作用就是用来加载操作系统,而并不存在所谓的启动加载模式与下载工作模式的区别。,启动加载模式,也称为“自主”(Autonomous)模式。也即Boot Loader 从目标机上的某个固态存储设备上将操作系统加载到RAM 中运行,整个过程并没有用户的介入。这种模式是Boot Loader 的正常工作
8、模式,因此在嵌入式产品发布的时侯,Boot Loader 显然必须工作在这种模式下。,下载模式,在这种模式下,目标机上的Boot Loader 将通过串口连接或网络连接等通信手段从主机下载文件,比如:下载内核映像和根文件系统映像等。从主机下载的文件通常首先被Boot Loader 保存到目标机的RAM 中,然后再被Boot Loader 写到目标机上的FLASH 类固态存储设备中。 Boot Loader 的这种模式通常在第一次安装内核与根文件系统时被使用;此外,以后的系统更新也会使用Boot Loader的这种工作模式。工作于这种模式下的Boot Loader 通常都会向它的终端用户提供一个
9、简单的命令行接口。,BootLoader 与主机如何通信?,最常见的情况就是,目标机上的Boot Loader 通过串口与主机之间进行文件传输,传输协议通常是xmodemymodemzmodem 协议中的一种。但是,串口传输的速度比较慢,因此通过以太网连接并借助TFTP 协议来下载文件是个更好的选择。,Boot Loader划分,大多数Boot Loader 都分为stage1 和stage2 两大部分。依赖于CPU 体系结构的代码,比如设备初始化代码等,通常都放在stage1 中,而且通常都用汇编语言来实现,以达到短小精悍的目的。而stage2 则通常用C语言来实现,这样可以实现给复杂的功能
10、,而且代码会具有更好的可读性和可移植性。,Stage1,通常包括以下步骤(以执行的先后顺序): 硬件设备初始化。 为加载Boot Loader 的stage2 准备RAM 空间。 拷贝Boot Loader 的stage2 到RAM 空间中。 设置好堆栈。 跳转到stage2 的C 入口点。,硬件设备初始化,屏蔽所有的中断 为中断提供服务通常是 OS 设备驱动程序的责任,因此在 Boot Loader 的执行全过程中可以不必响应任何中断。中断屏蔽可以通过写 CPU 的中断屏蔽寄存器或状态寄存器(比如 ARM 的 CPSR 寄存器)来完成。 设置 CPU 的速度和时钟频率。 RAM 初始化。 包
11、括正确地设置系统的内存控制器的功能寄存器以及各内存库控制寄存器等。 初始化 LED。 典型地,通过 GPIO 来驱动 LED,其目的是表明系统的状态是 OK 还是 Error。如果板子上没有 LED,那么也可以通过初始化 UART 向串口打印 Boot Loader 的 Logo 字符信息来完成这一点。 关闭 CPU 内部指令数据 cache,为加载 stage2 准备 RAM 空间,由于 stage2 通常是 C 语言执行代码,因此在考虑空间大小时,除了 stage2 可执行映象的大小外,还必须把堆栈空间也考虑进来。 空间大小最好是内存页大小(通常是 4KB)的倍数。 一般而言,1M 的 R
12、AM 空间已经足够了。 具体的地址范围可以任意安排。但是,将 stage2 安排到整个 RAM 空间的最顶 1MB(也即(RamEnd-1MB) - RamEnd)是一种值得推荐的方法,拷贝 stage2 到 RAM 中,需要考虑: stage2 的可执行映象在固态存储设备的存放起始地址和终止地址; RAM 空间的起始地址。,设置堆栈指针,堆栈指针的设置是为了执行 C 语言代码作好准备。,跳转到 stage2 的 C 入口点,Stage2,通常包括以下步骤(以执行的先后顺序): 初始化本阶段要使用到的硬件设备。 检测系统内存映射(memory map)。 将kernel 映像和根文件系统映像从
13、flash 上读到RAM 空间中。 为内核设置启动参数。 调用内核。,Stage2,Stage2的代码一般都是用C语言实现 与一般C语言不同,编译Bootloader时不用使用函数库。,Stage2,stage2 的代码通常用 C 语言来实现,以便于实现更复杂的功能和取得更好的代码可读性和可移植性。 但是与普通 C 语言应用程序不同的是,在编译和链接 boot loader 这样的程序时,不能使用 glibc 库中任何支持函数。,问题Main()的入口?,直接把 main() 函数的起始地址作为整个 stage2 执行映像的入口点或许是最直接的想法。 无法通过main() 函数传递函数参数;
14、无法处理 main() 函数返回的情况,解决 Main()的入口,trampoline(弹簧床) : trampoline小程序来作为 main() 函数的外部包裹(external wrapper) 用汇编语言写一段trampoline 小程序,并将这段 trampoline 小程序来作为 stage2 可执行映象的执行入口点。 在 trampoline 汇编小程序中用 CPU 跳转指令跳入 main() 函数中去执行; 当 main() 函数返回时,CPU 执行路径显然再次回到我们的 trampoline 程序。,trampoline(弹簧床)示例,来自Blob bootloader .t
15、ext .globl _trampoline _trampoline: Bl main /*if main ever return we just call it again*/ B _trampoline,初始化本阶段要使用到的硬件设备,初始化至少一个串口,以便和终端用户进行 I/O 输出信息 初始化计时器等。 设备初始化完成后,可以输出一些打印信息,程序名字字符串、版本号等。,检测系统的内存映射,内存映射就是指在整个4GB 物理地址空间中有哪些地址范围被分配用来寻址系统的 RAM 单元。 通常CPU预留一大段足够的地址空间给系统的RAM。 嵌入式系统却不一定实现CPU预留的全部RAM空间,
16、即嵌入式系统往往只把CPU预留的全部RAM地址空间中的一部分映射到RAM空间,而让剩余部分处于未使用状态。,地址空间描述,Typedef struct memory_area_struct u32 start;/*the base address of the memory region*/ u32 size; /*the byte number of the memory region*/ int used; memory_area_t; Used = 1 说明某段地址被使用 Used = 0 说明某段地址并未被使用,加载内核映像和根文件系统映像,规划内存占用的布局 内核映象所占用的内存范围 一般将其拷贝到(MEM_START+0x8000)地址开始的大约1MB大小的空间,内核一般小于1M 根文件系统所占用的内存范围 一般将其拷贝到(MEM_START+0x0080,0000)地址开始的地方,内存布局,加载内核映像和根文件系统映像,从flash上拷贝 ARM CPU一般从统一的内存地址空间寻址flash等固态存储设备,从flash上读取和从RAM