Bootloader - aman396271/Flagchip_Study GitHub Wiki

概述

内容大部分来自于NXP-AN12323 A bootloader is a piece of firmware that resides in microcontroller’s Nonvolatile Memory (NVM), that handles the updates of the application program or data.Bootlader是驻留在NVM中一块处理应用程序和数据更新的固件 更新流程图 image

如何确认最旧的映像

When the bootloader starts, it searches for the app key in the application header. It first reads the content of the last address of the header one, if nothing is found in that location, then the new address to allocate the upcoming firmware is that location. In case an app key is found in image one header, then the content of the firmware header two is read, if no app key is found the new address to allocate the upcoming firmware is image two location. When an app key is found in both headers, then the firmware’s version is read to determine the oldest one. The location of the oldest firmware is then where the upcoming firmware is allocated. In case there is no update requested by a host within a defined timeout after reset (e.g. 4 seconds), the bootloader jumps to the flash location with the newest firmware. For an eventual case in which neither of the headers has the app key, this means that there is no valid firmware, the timeout is then disregarded, and the bootloader stays waiting for update requests from the host. Figure 11 shows the steps of the boot process. 根据A/B映像的头来确认

Bootloader

何谓“Bootloader”?

2025.9.14 个人初步理解“Bootloader”完全可视作是一个的程序,没有必要因为其称呼的不同而对其望而生畏。Bootloader其特点在于其往往只实现App更新,我们应该将问题重心放在这段程序是怎样的实现原理,而不是被其“靴子”的奇怪称呼所迷惑。 Bootloader有几大功能点: 1、通讯,实现通信才能察觉更新,接收更新数据 2、校验,增加校验流程才能保证新刷入程序的正常执行 3、跳转,Bootloader App内容的更新其实是实现了存储内容的更改,如我使用PFlash作为实验对象,FC4150F1MB也是使用PFlash的AB swap特性才实现OTA的 在得知Bootloader这几大功能点后,要做的便是逐一击破。

Bootloader手搓实验

纯flash跳转方法

通讯

  1. 作为实验Demo,通讯使用了串口来实现,打开串口中断接收方便接收上位机传输的数据,打开串口传输方便向上位机发送receive OK信号
  2. 通讯协议使用SRecord 校验 初代Demo实现功能点,暂且不加入校验机制 跳转 在跳转上,有以下需要注意的点:(个人认为跳转这一块是最有挑战的一部分)
  3. 链接文件(.ld)规定的Flash起始地址及长度大小,这将决定程序从哪里启动,也决定了在Bootloader擦写Flash要从哪里开始及最大允许擦写范围
  4. 汇编语言进行跳转

明晰了Bootloader的需求后,首先学习会使用到的ld文件的基础知识,在知晓其代码内容及效用后,才能更顺利地进行下去。

ld文件

链接脚本 (.ld) 告诉链接器 (ld):

  1. MCU 的存储器地址和大小 (MEMORY)
  2. 哪些段放到哪里 (SECTIONS)
  3. 初始化时需要拷贝哪些数据

关键词解析: 1.isr_vector:中断向量表,必须放在Flash起始位置,一般为“ORIGN+0x0004” 2.text:程序代码、常量字符串、编译器glue代码 3.rodata:const常量 4.data:带初始值的全局变量,Flash会保存初始值,RAM则会从Flash中拷贝使用 5.bss:未初始化的全局变量,储存在RAM,启动时被清零 6.stack(栈):局部变量,存储在RAM 7.heap(堆):malloc分配的空间

对于Flash的操作,基本都是对MEMORY部分的更改,更改ORIGN以更改起始位置,更改LENGTH以更改大小,如1MB的PFlash从0x00000000开始,大小为1MB

/* Specify the memory areas */
MEMORY
{
  /* Flash */
  FLASH                (RX)  : ORIGIN = 0x00000000, LENGTH = 1M

  /* SRAM_L */
  RAM_L                (RW)  : ORIGIN = 0x02000000, LENGTH = 32K
  
  /* SRAM_U */
  RAM_U                (RW)  : ORIGIN = 0x20000000, LENGTH = 96K
}

在本次Demo中,我们想要实现Bootloader+AB分区切换,于是,我们可以将1MB的PFlash分成:64K Bootloader+480K A_APP_Bank+480K B_APP_Bank,Flash分配时要注意对齐,对齐的标准是Sector的倍数,FC4150F1MB的Sector大小为2KB,故OK,于是Flash分配: BOOTLOADER (RX) : ORIGIN = 0x00000000, LENGTH = 64K APP_A_BANK (RX) : ORIGIN = 0x00010000, LENGTH = 480K APP_B_BANK (RX) : ORIGIN = 0x00088000, LENGTH = 480K 在实际操作中,Bootloader程序其Flash区域仍未从0开始的1MB,否则在执行跳转时会超出Flash范围而Fault,A或B的APP代码ld文件需要更改,一般Bootloader还需要单独分配版本号空间和校验内容空间,但目前测试暂且不加

对于.ld文件中SECTION段的详细解读

例如:

.boot_text :                 /* 输出段名称,用户自定义 */
{
    . = ALIGN(1024);         /* 地址对齐,保证向量表按 1KB 边界对齐 */
    __app_vector_start__ = .; /* 定义符号,标记向量表起始地址 */
    KEEP(*(.isr_vector))      /* “.isr_vector”向量表名称,必须与 .S文件 中的内容一致,否则会死机 */
    __app_vector_end__ = .;   /* 定义符号,标记向量表结束地址 */
    *(.text*)                 /* 收集所有输入段“.text”及其子段(代码) */
    *(.rodata*)               /* 收集所有输入段“.rodata”及其子段(常量、字符串) */
    *(.text*)                /* .text* sections (code) */
    *(.rodata*)              /* .rodata* sections (constants, strings, etc.) */
    *(.init)                 /* section used in crti.o files */
    *(.fini)                 /* section used in crti.o files */
    *(.eh_frame)             /* section used in crtbegin.o files */
} > BOOTLOADER                /* 放到 BOOTLOADER 这个内存区域 */

SRec机制

程序更新的代码通讯机制为SRec,可直接在eclipse的Build step中加入arm-none-eabi-objcopy -O srec --srec-forceS3 "${BuildArtifactFileBaseName}.elf" "${BuildArtifactFileBaseName}.srec" 具体的SRec机制可谷歌,在Demo中有我手搓的解析代码

具体执行

Jump Code

typedef void (*pFunc)(void);
bool JumpToApp(uint32_t app_base)
{
    uint32_t *vector_table = (uint32_t *)app_base;
	uint32_t msp = vector_table[0];
	uint32_t reset = vector_table[1];
	pFunc app_reset_handler = (pFunc)(reset | 0x1);		//Thumb 位强制置 1(| 0x1),保证跳转到 App 时 CPU 处于 Thumb 模式

	__disable_irq();
	SCB->VTOR = app_base;
	__set_MSP(msp);
	app_reset_handler();
	while(1);
}

End