【问题标题】:Bootloader Jump Function. How to Jump to the right Address?Bootloader跳转功能。如何跳转到正确的地址?
【发布时间】:2023-02-11 20:30:54
【问题描述】:

我正在尝试创建一个引导加载程序,它跳转到我在 MKE02Z32VFM4(Freescale 的 KEO2 系列)上的应用程序代码。我正在使用 Keil IDE 5 和 Armv6 编译器 v6.16。

在向应用程序起始地址发出跳转指令后,代码跳转到“a”复位处理程序。并且当到达跳转到__main的指令时,它会跳转到bootloader的main。闪存由链接器文件定义如下:

#define m_interrupts_start             0x00000000
#define m_interrupts_size              0x00000200

#define m_flash_config_start           0x00000400
#define m_flash_config_size            0x00000010

#define bootloader_start               0x00000410
#define bootloader_size                0x00000800 //2kb size 0x410+0x800=0xC10 ==> 256 byte aligned => 0xE00

#define ota_part_0_start               0x00000E00 //Vector Table interrupt must be 256 byte aligned
#define ota_part_0_size                0x00003800 //14KB (14336 Byte) 0xE00+0x3800  => 0x4600

#define ota_part_1_start               0x00004600   
#define ota_part_1_size                0x00003800 //14KB (14336 Byte) 0x4600+0x3800 = 0x7E00 || flash_end == 0x0000 7FFF =>  0x100(256) byte frei

#define m_data_start                   0x1FFFFC00 //ram start
#define m_data_size                    0x00001000 //4kb 

应用程序链接器文件(分散文件)使用这些定义:

#define m_interrupts_start             0x00000E00  //Address of the application reset handler
#define m_interrupts_size              0x00000200

#define m_flash_config_start           0x00001000  //some config bytes, defined by manufacturer
#define m_flash_config_size            0x00000010

#define m_text_start                   0x00001010 // start address of application code
#define m_text_size                    0x000035F0

#define m_data_start                   0x1FFFFC00 //ram start
#define m_data_size                    0x00001000 //4kb 

重置处理程序是用汇编程序编写的,我试图注释说明:

Reset_Handler:
    cpsid   i               /* Mask interrupts */
    .equ    VTOR, 0xE000ED08 //.equ is like #define in C. VTOR = predefined ARMv6 label. 0xE000ED08 VectorTableOffsetRegister. 
        ldr     r0, =VTOR    // load word from memory. load value from word at VTOR address to r0. R0 now contains the offset for the vector table. 
    ldr     r1, =__Vectors   // load word from memory. load value of word at __Vectors address to r1. --> the first word at __Vectors is the initial stack pointer
    str     r1, [r0]        //store Register to memory. content of r1 is stored to memory adress in r0(==VTOR) --> initial stack pointer is stored to the first word of the Vector table
    ldr     r2, [r1]        //load word from memory. r2 is set to the value of the word in memory at address in r1. --> r2 is set to the address of the initial stack pointer
    msr     msp, r2         //move to special register. move value of r2 to special register msp (main stack pointer) --> main stack pointer is set to the valjue of the initial stack pointer
    ldr     r0,=SystemInit  //set register 0 to address of SystemInit function. (
    blx     r0              // branch with link ( to address of r0)
    cpsie   i               /* Unmask interrupts */
    ldr   r0,=__main
    bx    r0

    .pool
    .size Reset_Handler, . - Reset_Handler

bootloader代码如下: 第一个测试中的地址是值 0x00000E00(用户应用程序的开始)

__attribute__( ( naked, noreturn ) ) void BootJumpASM( uint32_t SP, uint32_t RH )
{
  __asm("MSR      MSP,r0");
  __asm("BX       r1");
}


    
static void BootJump( uint32_t *Address )
{
    if( CONTROL_nPRIV_Msk & __get_CONTROL( ) ) //THIS is from the arm doku, but it is always false in our implementation and skipped.
  {  /* not in privileged mode */
    EnablePrivilegedMode( ) ;
  }
    
    NVIC->ICER[0] = 0xFFFFFFFF ;
    NVIC->ICPR[0] = 0xFFFFFFFF ;

    SysTick->CTRL = 0 ;
    SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk ;
    
    if( CONTROL_SPSEL_Msk & __get_CONTROL( ) ) //THIS is from the arm doku, but it is always false in our implementation and skipped. (only 1 stack pointer used)
    {  /* MSP is not active */
        __set_MSP( __get_PSP( ) ) ;
        __set_CONTROL( __get_CONTROL( ) & ~CONTROL_SPSEL_Msk ) ;
    }
    
    SCB->VTOR = ( uint32_t )Address ; //Setting the Vector Table Offset Register to the start of the user app.
    BootJumpASM( Address[ 0 ], Address[ 1 ] ) ; //This function is taken from the Arm Documentation

}

SCB->VTOR = (uint32_t)Address; // Set VTOR to 0xE00

VTOR 寄存器更新为 0xE00。但是执行函数后:

__attribute__( ( naked, noreturn ) ) void BootJumpASM( uint32_t SP, uint32_t RH )
{
  __asm("MSR      MSP,r0");
  __asm("BX       r1"); //<-- This is the Point where VTOR changes it value to 0x00 again
}

VTOR 再次为 0x00,我在重新处理程序中。这个 resethandler 连接到 bootloader main。所以我假设我在 0x00 处的重置处理程序中,而不是 0xE00 处的重置处理程序中。我检查了闪存,确定向量表位于 0x000 和 0xE00。我确信应用程序的固件也在闪存中的正确位置。

我假设我要么:

定义的内存空间错误。

BootJumpASM 函数跳转到非法位置,MCU 在 0x00 重新启动并重置 VTOR 寄存器。

我不确定为什么 BootJumpASM 函数使用 r0 和 r1 以及它如何处理函数的参数。我只是汇编程序和所有特定编译器属性的新手。上面描述的函数直接复制自:

https://developer.arm.com/documentation/ka002218/latest

虽然我不明白编译器如何设法将函数参数放入寄存器 r0 和 r1,但我确信错误出在我这边,而不是在官方的 arm 文档中。

有人能给我解释一下,为什么在“BootJumpASM”函数的第二条指令后“VTOR”被重置为 0x00? 以及为什么 resethandler,调试器紧随其后,连接到引导加载程序主而不是应用程序主。我如何设法跳转到内存中的正确位置。

谢谢你的时间。我希望这个解释不会太混乱。

【问题讨论】:

  • 我不知道这是否与问题有关,但您的某些闪存区域大小似乎很奇怪。你的第一个链接描述文件有 ota_part_0_start0xE000xE00+0x3800。但是您的应用程序链接描述文件从 0xE00 映射到 0x1010+0x3800。另外,在第一个脚本中,m_flash_config_start 的偏移量是0x400,在第二个脚本中是0x200。也许这是故意的,但对于不完全了解您的设置的人来说看起来很奇怪。
  • 你可以读回 vtor 并看到它不为零吗?
  • 我假设您的矢量和其他馈送到 bx 的地址设置了 msbit?
  • BootJumpASM 参数在跳转至 BootJumpASM 之前加载到 r0 和 r1。当你进入 BootJumpASM 时,r0 和 r1 的值是多少?
  • 抱歉 lsbit 设置

标签: microcontroller bootloader cortex-m keil


【解决方案1】:

问题不是跳转指令,而是Keil IDE 的Debugger。我按照arm和Keil的文档搭建了debug环境,但是在跳出bootloader的代码环境进入应用程序内存区后,Debugger触发了reset。 (Bootloader 是一个单独的 Keil 项目。)

在应用程序项目中启动调试器,在跳转指令之后没有触发此类重置,并且在反汇编视图之后,引导加载程序按预期执行并且跳转指令起作用。

感谢大家花时间尝试和我一起找出错误。

【讨论】:

    猜你喜欢
    • 2021-06-06
    • 2015-05-02
    • 1970-01-01
    • 2021-11-21
    • 2010-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多