• 从0写bootloader — Bootloader重定位APP


    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • __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     ;汇编文件结
    			 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • #define APP_BIN_ADDR 0x800B000, APP在加载地址,自定义,放在哪都无所谓,此处放在STM32内部Flash,如果有外部SPI Flash或SD卡,也可以放在它们之中。
    • __BE32_TO_CPU,大端字节序转小端字节序
    • parse_head, 解析头部,获取加载地址、链接地址、程序大小等
    • relocate_app,重定位APP
    • boot_app,跳转执行APP
  • 相关阅读:
    前端常用库之-JavaScript工具库lodash
    【“在路上”疫情信息检测】——项目基础框架搭建
    Springboot拦截器中注入Bean
    看图就懂|衡量业务增长健康的销售指标如何选择
    【小黑嵌入式系统第三课】嵌入式系统硬件平台(一)——概述、总线、存储设备(RAM&ROM&FLASH)
    iverilog入门教程
    灰色GM(1,1)模型及其在电力负荷预测中的应用附Matlab代码
    html文本格式化标签
    Linux常用基本命令
    YBTOJ 期望分数【第31章 期望问题】
  • 原文地址:https://blog.csdn.net/qq_36413982/article/details/125755588