bootloader 编译为 Debug 无法引导 MS-RTOS 问题

一、问题描述

        在智芯 SCM402F 芯片上,bootloader 如果按照 Debug 模式编译则无法成功引导 MS-RTOS,反之按照 Release 模式编译则可以正常引导。引导代码如下所示:

static void ms_boot_os(ms_addr_t *addr)
{
    ms_func_t func = (ms_func_t)addr[1U];

    ms_printk(MS_PK_NOTICE, "Start MS-RTOS...\n");
    
    SysTick->CTRL = 0U;
    __asm__ __volatile__ (
            "MOV  R13, %[sp]\n"
            :
            : [sp] "r" (addr[0])
            : "cc");

    func();
}

        ms_boot_os() 函数传入 MS-RTOS 镜像基址 addr,基址指向向量表,向量表第一位是栈顶地址,第二位为复位入口。代码中首先保存 addr[1] 即复位入口到变量 func 中,再使用一段嵌入汇编将 R13(SP) 赋值为 addr[0]。最后调用 func(),即跳转到 MS-RTOS 的复位入口处。

二、问题分析

上述在逻辑上有一个很大的问题,我们先看一下 Debug 模式下的反汇编代码:

static void ms_boot_os(ms_addr_t *addr)
{
 8007100:	b500      	push	{lr}
 8007102:	b085      	sub	sp, #20
 8007104:	9001      	str	r0, [sp, #4]
    ms_func_t func = (ms_func_t)addr[1U];
 8007106:	9b01      	ldr	r3, [sp, #4]
 8007108:	3304      	adds	r3, #4
 800710a:	681b      	ldr	r3, [r3, #0]
 800710c:	9303      	str	r3, [sp, #12]

    ms_printk(MS_PK_NOTICE, "Start SW-RTOS...\n");
 800710e:	4908      	ldr	r1, [pc, #32]	; (8007130 <ms_boot_os+0x30>)
 8007110:	2005      	movs	r0, #5
 8007112:	f000 fc6d 	bl	80079f0 <ms_printk>

    /*
     * Disable system tick
     */
    SysTick->CTRL = 0U;
 8007116:	4b07      	ldr	r3, [pc, #28]	; (8007134 <ms_boot_os+0x34>)
 8007118:	2200      	movs	r2, #0
 800711a:	601a      	str	r2, [r3, #0]

    __asm__ __volatile__ (
            "MOV  R13, %[sp]\n"
            :
            : [sp] "r" (addr[0])
 800711c:	9b01      	ldr	r3, [sp, #4]
 800711e:	681b      	ldr	r3, [r3, #0]
    __asm__ __volatile__ (
 8007120:	469d      	mov	sp, r3
            : "cc");

    func();
 8007122:	9b03      	ldr	r3, [sp, #12]
 8007124:	4798      	blx	r3
}

        在这段代码的最后,首先读取变量 func 的值到 r3,然后使用 blx 跳转到 r3 中保存的地址。这里因为 func 是局部变量,使用 SP 指针加偏移的方式获取 func 的值,但是在上一步中已经改变了 SP 的值,因此这里无法正确获取 func 变量的值。所以无法成功引导 MS-RTOS,如下图所示。

 

        分析了 Debug 版本下无法引导的原因后又引出了另一个问题,为什么 Release 可以成功引导呢?同样我们先看一下 Release 模式下的反汇编:

   ms_func_t func = (ms_func_t)addr[1U];
 800445e:	4c1b      	ldr	r4, [pc, #108]	; (80044cc <main+0x3a4>)
    ms_printk(MS_PK_NOTICE, "Start SW-RTOS...\n");
 8004460:	4f1b      	ldr	r7, [pc, #108]	; (80044d0 <main+0x3a8>)
    SysTick->CTRL = 0U;
 8004462:	4e1c      	ldr	r6, [pc, #112]	; (80044d4 <main+0x3ac>)
    ms_func_t func = (ms_func_t)addr[1U];
 8004464:	6865      	ldr	r5, [r4, #4]
    ms_printk(MS_PK_NOTICE, "Start SW-RTOS...\n");
 8004466:	4639      	mov	r1, r7
 8004468:	2005      	movs	r0, #5
 800446a:	f000 f9b9 	bl	80047e0 <ms_printk>
    SysTick->CTRL = 0U;
 800446e:	2300      	movs	r3, #0
 8004470:	6033      	str	r3, [r6, #0]
    __asm__ __volatile__ (
 8004472:	6823      	ldr	r3, [r4, #0]
 8004474:	469d      	mov	sp, r3
    func();
 8004476:	47a8      	blx	r5

        从这段反汇编可以看出,Release 版本中编译器识别到了 func 变量除了初始化和读取之外没有其它的操作,因此优化了这个变量。这里直接使用 r4 保存了 MS-RTOS 基址,即 ms_boot_os() 传入的参数,宏 MS_CFG_BOOT_OS_BASE,如下图所示。

 

        紧接着使用 r5 保存基址的第二位,即复位入口。并在更新 SP 后跳转到 r5 中保存的地址上,因为此时获取复位入口的方式并不依赖 SP 指针,因此 Release 版本可以正常跳转到 MS-RTOS。如下图所示。

三、解决方案

        从上述的分析可以看出,无法成功引导的原因是由于局部变量取值依赖 SP 指针,因此在更新 SP 指针后就无法正确读取局部变量的值了。ms_boot_os() 中启动 MS-RTOS 的逻辑是存在问题的,Release 可以成功启动也只是因为优化而产生的巧合而已。这里可以修改为不使用局部变量,直接使用一个寄存器保存复位入口,就可以避免这个问题。如下所示。

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

stone8761

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值