stm32 BootLoader之检查栈顶地址是否合法(否则无法跳转到APP程序)
stm32 BootLoader之检查栈顶地址是否合法
- Chapter1 stm32 BootLoader之检查栈顶地址是否合法
- 试验结果
- 参考文章
正点原子STM32F407 U盘升级程序(IAP)OTA Bootloader APP USB升级+FATFS+USB Host
Chapter1 stm32 BootLoader之检查栈顶地址是否合法
在stm32 IAP例程中,跳转到APP区的时候,都会检查栈顶地址是否合法,以及reset地址是否正确等信息,那么这些判断具体依据什么原理???
以stm32f407ZGT6为例说明
检查栈顶是否合法代码如下:
/**********************************************************************
**** 函数名: jumpToApp()
**** 功 能: 跳转到appa运行程序
**** 参 数:
**** 返回值: 无
**** 时 间: 2025年3月11日
**** 设 计:
**** 备 注:
**********************************************************************/
void jumpToApp()
{
#if 1// 检查应用程序的栈顶地址是否有效// 应用程序的栈顶地址存储在FLASH_USER_START_ADDR处// 有效栈顶地址的高16位必须是0x2000(即位于SRAM区域)uint32_t app_stack_pointer = *(__IO uint32_t*)FLASH_USER_START_ADDR;printf("应用程序的栈顶地址:0x%08X\n", app_stack_pointer);if ((app_stack_pointer & 0x2FFE0000 ) == 0x20000000)//if (((*(__IO uint32_t*)FLASH_USER_START_ADDR) & 0x2FFE0000 ) == 0x20000000){printf("跳转到应用程序\r\n");// 1. 禁用所有中断__disable_irq(); // 2. 重置和复位时钟HAL_RCC_DeInit(); // 3. 重置所有外设HAL_DeInit(); //复位所有外设// 4. 设置向量表位置SCB->VTOR = FLASH_USER_START_ADDR; //这里可以不用,在APP程序中设置 // 5. 初始化应用程序栈指针// 设置栈指针(MSP)为应用程序的栈顶地址 __set_MSP(*(__IO uint32_t*) FLASH_USER_START_ADDR); //应用程序的栈顶地址存储在FLASH_USER_START_ADDR处// 6. 获取复位处理函数地址JumpAddress = *(__IO uint32_t*) (FLASH_USER_START_ADDR + 4); // 应用程序的入口地址存储在FLASH_USER_START_ADDR + 4处Jump_To_Application = (pFunction) JumpAddress; // 将入口地址转换为函数指针// 7. 跳转到应用程序 Jump_To_Application(); // 跳转到应用程序}printf("ADDR != 0x20000000\r\n"); // 栈顶地址无效printf("跳转到应用程序失败!\r\n");
#endif
}
栈顶地址 和 reset入口地址具体是什么???
从startup_stm32h407xx.s中可以看出,程序第一个地址存放的是__initial_sp,紧接着第二个地址存放的是Reset_Handler;这两个正是所谓的栈顶地址 reset 入口
__Vectors DCD __initial_sp ; Top of StackDCD Reset_Handler ; Reset HandlerDCD NMI_Handler ; NMI HandlerDCD HardFault_Handler ; Hard Fault HandlerDCD MemManage_Handler ; MPU Fault HandlerDCD BusFault_Handler ; Bus Fault HandlerDCD UsageFault_Handler ; Usage Fault HandlerDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD SVC_Handler ; SVCall HandlerDCD DebugMon_Handler ; Debug Monitor HandlerDCD 0 ; ReservedDCD PendSV_Handler ; PendSV HandlerDCD SysTick_Handler ; SysTick Handler
结合map文件和bin文件进行分析
生成map文件中,__initial_sp = 0x20005aa0
data 0x20000000 Data 4 freertos.o(.data)defaultTaskHandle 0x20000004 Data 4 freertos.o(.data)LEDTaskHandle 0x20000008 Data 4 freertos.o(.data)HMITaskHandle 0x2000000c Data 4 freertos.o(.data)myBinarySem01Handle 0x20000010 Data 4 freertos.o(.data)uwTickFreq 0x20000014 Data 1 stm32f4xx_hal.o(.data)uwTickPrio 0x20000018 Data 4 stm32f4xx_hal.o(.data)uwTick 0x2000001c Data 4 stm32f4xx_hal.o(.data)SystemCoreClock 0x20000020 Data 4 system_stm32f4xx.o(.data)pxCurrentTCB 0x20000024 Data 4 tasks.o(.data)__stdout 0x200000a4 Data 4 stdout.o(.data)htim7 0x200000a8 Data 72 tim.o(.bss)huart1 0x200000f0 Data 72 usart.o(.bss)huart2 0x20000138 Data 72 usart.o(.bss)htim6 0x20000180 Data 72 stm32f4xx_hal_timebase_tim.o(.bss)xQueueRegistry 0x200001c8 Data 64 queue.o(.bss)__initial_sp 0x20005aa0 Data 0 startup_stm32f407xx.o(STACK)
另外,从中可以看出
堆栈Stack范围 0x20004aa0 - 0x24005a40 占用4K
ucHeap范围 0x20000e9c - 0x24004a9c 占用15K
和startup_stm32h407xx.s中的定义对应
.bss 0x200007e4 Section 1720 cmsis_os2.o(.bss)Idle_TCB 0x200007e4 Data 92 cmsis_os2.o(.bss)Idle_Stack 0x20000840 Data 512 cmsis_os2.o(.bss)Timer_TCB 0x20000a40 Data 92 cmsis_os2.o(.bss)Timer_Stack 0x20000a9c Data 1024 cmsis_os2.o(.bss).bss 0x20000e9c Section 15360 heap_4.o(.bss)ucHeap 0x20000e9c Data 15360 heap_4.o(.bss)STACK 0x20004aa0 Section 4096 startup_stm32f407xx.o(STACK)Global Symbols
map文件中,Reset_Handler = 0x08010261
PendSV_Handler 0x080101fd Thumb Code 88 port.o(.emb_text)vPortGetIPSR 0x08010259 Thumb Code 6 port.o(.emb_text)Reset_Handler 0x08010261 Thumb Code 8 startup_stm32f407xx.o(.text)
生成bin文件中,用SEGGAR J-Flash程序打开,查看bin文件
第一个数据 a0 5a 00 20 即 0x20005aa0 (__initial_sp)
第二个数据 61 02 01 08 即 0x08010261 (Reset_Handler)
所以跳转的时候,直接跳转到0x08010261地址即可!
#生成bin文件user run命令:
fromelf.exe --bin -o “$L@L.bin” “#L”
所以
(*(uint32_t*)(STM32_APP_BASE) 即 __initial_sp(*(uint32_t*)(STM32_APP_BASE+4) 即 Reset_Handler
试验结果
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************LR_IROM1 0x08010000 0x00070000 { ; load region size_regionER_IROM1 0x08010000 0x00070000 { ; load address = execution address*.o (RESET, +First)*(InRoot$$Sections).ANY (+RO).ANY (+XO)}RW_IRAM1 0x20000000 0x00020000 { ; RW data.ANY (+RW +ZI)}RW_IRAM2 0x10000000 0x00010000 {.ANY (+RW +ZI)}
}
参考文章
stm32 BootLoader之检查栈顶地址是否合法