|
楼主 |
发表于 2011-3-2 12:28:34
|
显示全部楼层
转自网上,全文如下:
揭开BOOT LOAD升级过程的神秘面纱
在没有接触BOOTLOAD之前,看着别人搞这“玩意儿”,觉得是一个很有技术含量的事,当细细的去“品”过后,也就是那么一回事,作技术就是要深专,只要“钻”进去了,在某一时刻就会恍然大悟。今天就将我对BOOTLOAD升级过程的理解和大家分享分享。
BOOTLOAD并不是只有arm中才有的,其它的嵌入式系统甚至PC上都会有bootloader,主要的作用就是引导操作系统。在硬件起动后,硬件设备尚未初始化,直接加载体积较大的系统比较困难,有时甚至无法加载,如系统内核在网络上的情况,所以常常在系统运行前,提供一个体积较小但又具体初始化基本软硬件环境的程序来运行,由它来载入系统并设置系统运行参数,并最终运行系统,这就是bootloader。
它可以分为两大类,一类BOOTLOAD是芯片在出厂时,生产商固化在ROM中的BOOTLOAD;二类BOOTLOAD是用户在设计过程中,根据实际工程的需要设计一小段代码,使新的应用程序从非JATG接口引导到ROM区或RAM区。其实这两类在功能上都差不多。本文讨论二类BOOTLOAD,讨论以Cotex-M3内核的LM3S系列处理器为对象。代码和编译器是基于IAR5.11版本。
我们都知道任何程序都是从复位开始执行起走的,BOOTLOAD也不例外。当芯片复位后(软、硬件复位,掉电复位等),系统产生复位中断,执行复位中断服务程序,复位中断的优先级最高,所以不需要考虑有谁可以打断它(专心做它自己的事)。当然,程序员就可以在复位中断里面打芯片的主意了。BOOTLOAD升级应用程序的点子就是从这里出发的(一发不可收拾“坏透了”),它先把自己的BOOTLOAD复制到RAM区,再零填充.bss段(数据段,主要用于存放那些初始化为0的变量和没有初始的自动变量。位于SRAM区)。然后就让向量表的偏移从RAM的起始地址开始算起,LM3Sxxxx是从0x2000 0000开始偏移,到此,开始在RAM区里执行。
在RAM区里执行时,会对升级信号进行检测,如检测到要升级信号,则开始配置要升级的接口(升级前的准备),准备好了就升级应用程序。如果没有检测到升级信号则执行原有的用户应用程序。我们来看看启动代码分析,就知道是怎么回事了。
//*********************************************************
// 包含配置头文件
//*********************************************************
#include "bl_config.h"
//*********************************************************
// 声明.bbs数据段,此段位于RAM区
//*********************************************************
rseg .bss:DATA(2)
//*********************************************************
// 分配存储堆栈
//*********************************************************
g_pulStack ds8 STACK_SIZE * 4
//*********************************************************
// 声明INTVEC段,此段位于ROM区,用于存放下面这一段向量表
//*********************************************************
rseg INTVEC:CONST(2)
//*********************************************************
// Cortex-M3 处理器的简化向量表,也是必要的,
//*********************************************************
export __vector_table
__vector_table
dcd g_pulStack + (STACK_SIZE * 4) // Offset 00: Initial stack pointer
dcd ResetISR - 0x20000000 // Offset 04: Reset handler
dcd NmiSR // Offset 08: NMI handler
dcd FaultISR // Offset 0C: Hard fault handler
dcd IntDefaultHandler // Offset 10: MPU fault handler
dcd IntDefaultHandler // Offset 14: Bus fault handler
dcd IntDefaultHandler // Offset 18: Usage fault handler
dcd 0 // Offset 1C: Reserved
dcd 0 // Offset 20: Reserved
dcd 0 // Offset 24: Reserved
dcd 0 // Offset 28: Reserved
dcd UpdateHandler - 0x20000000 // Offset 2C: SVCall handler
dcd IntDefaultHandler // Offset 30: Debug monitor handler
dcd 0 // Offset 34: Reserved
dcd IntDefaultHandler // Offset 38: PendSV handler
#if defined(ENET_ENABLE_UPDATE)
import SysTickIntHandler
dcd SysTickIntHandler // Offset 3C: SysTick handler
#else
dcd IntDefaultHandler // Offset 3C: SysTick handler
#endif
#if defined(UART_ENABLE_UPDATE) && defined(UART_AUTOBAUD)
import GPIOIntHandler
dcd GPIOIntHandler // Offset 40: GPIO port A handler
#endif
//*********************************************************
// 声明代码段CODE,用于存放代码段
//*********************************************************
rseg CODE:CODE(2)
thumb
//*********************************************************
// 初始化时,把flasn里面的boot loader拷贝到SRAM区,
// 零填充.bss段并从SRAM区向量表开始执行
//*********************************************************
ProcessorInit
//
// 把flash 的 boot loader拷贝到SRAM区
//
movs r0, #0x00000000
ldr r1, =0x20000000 // SRAM区的起始地址
ldr r2, =SFB(.bss)
copy_loop
ldr r3, [r0], #4
str r3, [r1], #4
cmp r1, r2
blt copy_loop // 复制boot loader代码
//
// 零填充.bss段
//
movs r0, #0x00000000
ldr r2, =SFE(.bss)
zero_loop
str r0, [r1], #4
cmp r1, r2
blt zero_loop
//
// 设置中断向量表相对于SRAM区的起始地址的偏移量
//
ldr r0, =0xe000ed08 // 中断向量的起始地址
ldr r1, =0x20000000
str r1, [r0]
orr lr, lr, #0x20000000 // 设置向量表偏移寄存器为SRAM底部
bx lr // 跳转到SRAM区执行
//*********************************************************
// 复位中断处理程序
//*********************************************************
export ResetISR // 复位中断函数声明
ResetISR
bl ProcessorInit // 调用ProcessorInit汇编代码
//
// 看是否有升级信号
//
import CheckForceUpdate
bl CheckForceUpdate // 调用升级检测程序
cbz r0, CallApplication // 如果检测到没有升级信号.就调用原有的应用程序。
// 如果有则继续向下执行
//
// 处理器相关配置
//
#ifdef ENET_ENABLE_UPDATE // 以太网升级接口配置
import ConfigureEnet
bl ConfigureEnet
#elif defined(CAN_ENABLE_UPDATE) // CAN接口升级配置
import ConfigureCAN
bl ConfigureCAN
#else
import ConfigureDevice // 其它接口升级配置如I2C/UART/SSI
bl ConfigureDevice
#endif
//
// 调用升级程序
//
#ifdef ENET_ENABLE_UPDATE // 调用以太网升级程序
import UpdateBOOTP
b UpdateBOOTP
#elif defined(CAN_ENABLE_UPDATE) // 调用CAN升级程序
import UpdaterCAN
b UpdaterCAN
#else
import Updater // 其它接口升级程序。开始升级处理了
b Updater
#endif
//
// 准备调用应用程序
//
CallApplication // 如果没有升级信号就执行这里调应用程序
ldr r0, =APP_START_ADDRESS // 应用程序的起始地址加载到R0
ldr r1, =0xe000ed08 // 应用程序响量表的地址LM3S系列芯片中断向量是从0Xe000ed08开始的
str r0, [r1]
// 从应用程序响量表中读出堆栈指针
// 堆栈是向量表的一开始位址.LM3S芯片是堆栈的栈顶地址,有些芯片可能是栈底地址,
// 如果是栈底的话,PC指针就等于SP+堆栈大小+4
ldr r1, [r0]
mov sp, r1
//
// 从应用程序响量表中读出PC指针
//
ldr r0, [r0, #4]
bx r0 // 开始执行应用程序
//*********************************************************
// 升级中断处理程序,这段代码和上面这段代码类似,不加分析。
//*********************************************************
UpdateHandler
//
// Initialize the processor.
//
bl ProcessorInit
//
// Load the stack pointer from the vector table.
//
movs r0, #0x00000000
ldr r0, [r0]
mov sp, r0
//
// Branch to the update handler.
//
#ifdef ENET_ENABLE_UPDATE
b UpdateBOOTP
#elif defined(CAN_ENABLE_UPDATE)
import AppUpdaterCAN
b AppUpdaterCAN
#else
b Updater
#endif
//*********************************************************
// 不可屏蔽中断处理程序
//*********************************************************
NmiSR
b . // 死循环
//*********************************************************
// 硬件错误中断处理程序
//*********************************************************
FaultISR
b . // 死循环
//*********************************************************
// 未定义中断处理程序
//*********************************************************
IntDefaultHandler
b . // 死循环
//*********************************************************
// 延时函数
//*********************************************************
export Delay
Delay
subs r0, #1
bne Delay
bx lr
//*********************************************************
// 启动文件结束
//*********************************************************
end |
|