Bootloader重定位APP
一般情况下,不会采用APP重定位自己。假如APP程序存放在外接的SPI Flash或者SD卡上,
SPI Flash和SD卡是不支持XIP的,APP代码无法执行,重定位代码也是APP代码的一部分,何谈重定位自己。
此时就需要要采用Bootloader重定位APP的策略了。
Bootloader重定位APP程序需要知道APP的加载地址和链接地址,但是Bootloader程序和APP一般在是不同Project的程序,无法直接得到加载地址和链接地址。
a、固定地址重定位,可以写死加载地址和链接地址,还要计算APP程序的大,不太可靠的样子
b、给APP程序加上头部,头部包含加载地址、链接地址、长度、CRC校验等,APP可以决定APP的链接地址和加载地址,而不是写死。
Bootloader重定位APP实现
使用u-boot的mkimage给APP加头部
执行./mkimage
可以查看用法,mkimage.exe用window cmd和powershell无法执行,需要使用git bash
使用mkimage工具给APP加头部
opto@DESKTOP-ET42CRG MINGW64 /g/Myself/myself/bootloader_stm32/03_Bootloader_relocate_app/app
$ ./mkimage -C none -a 0x800B000 -e 0x20000000 -d app.bin app_with_header.bin
Image Name:
Created: Mon Jul 18 10:36:44 2022
Image Type: PowerPC Linux Kernel Image (uncompressed)
Data Size: 420 Bytes = 0.41 KiB = 0.00 MiB
Load Address: 0800b000
Entry Point: 20000000
Bootloader解析头部重定位执行APP
mkimage给APP添加的头部信息的格式如下结构体:
u-boot源码include/image.h
中定义
#define IH_NMLEN 32 /* Image Name Length */
typedef struct image_header {
__be32 ih_magic; /* Image Header Magic Number */
__be32 ih_hcrc; /* Image Header CRC Checksum */
__be32 ih_time; /* Image Creation Timestamp */
__be32 ih_size; /* Image Data Size */
__be32 ih_load; /* Data Load Address */
__be32 ih_ep; /* Entry Point Address */
__be32 ih_dcrc; /* Image Data CRC Checksum */
uint8_t ih_os; /* Operating System */
uint8_t ih_arch; /* CPU architecture */
uint8_t ih_type; /* Image Type */
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
} image_header_t;
__be32
表示该字段是使用的是大端字节序,在解析头部时还需要将该字段转换成使用的MCU对应的字节序。
程序实现:
start.s:
PRESERVE8 ; instruct is aligned by 8 bytes 指令集8字节对齐
THUMB ; use Thumb instruction set 使用thumb指令集
AREA RESET, DATA, READONLY ;DATA定义数据段,READONLY只读
EXPORT __Vectors
VECTOR_REG_ADD EQU 0xE000ED08
__Vectors DCD 0 ; CPU自动将该处的值设置给sp,Top of Stack
DCD Reset_Handler ; Reset Handler 指令地址,CPU首先执行此句
AREA |.text|, CODE, READONLY ; CODE表示定义代码段,READONLY只读
; Reset handler
Reset_Handler PROC ; 子程序开始标志
EXPORT Reset_Handler [WEAK] ;
IMPORT main ;
LDR sp, = (0x20000000+0x10000) ;
BL main ; 跳到C语言世界执行
ENDP ;
boot_app PROC ;
EXPORT boot_app
LDR R1, = VECTOR_REG_ADD
STR R0, [R1] ; 向中断向量寄存器写入程序链接地址0x800B000
LDR sp, [R0] ; 取0x800B000地址处的值写入sp即设置栈
;LDR R3, [R1]
LDR R2, [R0, #4] ; 0x20000000 + 4 = 0x20000004,取出0x20000004地址处的值赋值给R2
;ADD R2, R0, #9
BX R2 ; 跳去执行APP
ENDP
END ;汇编文件结
main.c:
#include "usart.h"
typedef unsigned int __be32;
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
#define BOOTLOADER_VERSION "1.4"
#define APP_BIN_ADDR 0x800B000 /* APP在加载地址,自定义,放在哪都无所谓, */
#define IH_MAGIC 0x27051956 /* Image Magic Number */
#define IH_NMLEN 32 /* Image Name Length */
#define __BE32_TO_CPU(x) ( ( (x & 0x000000FF) << 24 ) | ( ( (x & 0x0000FF00) >> 8 ) << 16) | \
( ( (x & 0x00FF0000) >> 16 ) << 8) | ( ( (x & 0xFF000000 ) >> 24 ) ) )
typedef struct image_header {
__be32 ih_magic; /* Image Header Magic Number */
__be32 ih_hcrc; /* Image Header CRC Checksum */
__be32 ih_time; /* Image Creation Timestamp */
__be32 ih_size; /* Image Data Size */
__be32 ih_load; /* Data Load Address */
__be32 ih_ep; /* Entry Point Address */
__be32 ih_dcrc; /* Image Data CRC Checksum */
uint8_t ih_os; /* Operating System */
uint8_t ih_arch; /* CPU architecture */
uint8_t ih_type; /* Image Type */
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
} image_header_t;
static void parse_head(uint32_t bin_addr, uint32_t *load, uint32_t *entry, uint32_t *size)
{
image_header_t *hdr;
hdr = (image_header_t *)bin_addr;
uint32_t _load = __BE32_TO_CPU(hdr->ih_load); /* 加载地址 */
uint32_t _entry = __BE32_TO_CPU(hdr->ih_ep); /* 链接地址 */
uint32_t _size = __BE32_TO_CPU(hdr->ih_size); /* 链接地址 */
*load = bin_addr + sizeof(image_header_t); /* APP真正开始的地址 */
*entry = _entry;
*size = _size;
}
static void relocate_app(char *from, char *to, unsigned int len)
{
while (len--)
{
*to++ = *from++;
}
}
void boot_app(uint32_t start_addr);
int main(void)
{
uint32_t s_addr = APP_BIN_ADDR;
uint32_t entry;
uint32_t load;
uint32_t size;
uart_init();
myputstr("\r\nBootloader: ");
myputstr(BOOTLOADER_VERSION);
myputstr(" ");
myputstr(__DATE__);
myputstr(" ");
myputstr(__TIME__);
parse_head(s_addr, &load, &entry, &size);
relocate_app((char *)load, (char *)entry, size);
boot_app(entry);
return 0;
}
#define APP_BIN_ADDR 0x800B000
, APP在加载地址,自定义,放在哪都无所谓,此处放在STM32内部Flash,如果有外部SPI Flash或SD卡,也可以放在它们之中。__BE32_TO_CPU
,大端字节序转小端字节序parse_head
, 解析头部,获取加载地址、链接地址、程序大小等- relocate_app,重定位APP
- boot_app,跳转执行APP