如何在Cortex M3上生成异常?

pkln4tw6  于 2023-08-03  发布在  其他
关注(0)|答案(3)|浏览(178)

我试图在ARM Cortex-M3上生成总线故障、使用故障等异常。我的启用异常代码:

void EnableExceptions(void)
{
    UINT32 uReg = SCB->SHCSR;

    uReg |= 0x00070000;

    SCB->SHCSR = uReg;

    //Set Configurable Fault Status Register
    SCB->CFSR = 0x0367E7C3;
    //Set to 1 DIV_0_TRP register
    SCB->CCR |= 0x00000010;

    //Set priorities of fault handlers

    NVIC_SetPriority(MemoryManagement_IRQn, 0x01);

    NVIC_SetPriority(BusFault_IRQn, 0x01);

    NVIC_SetPriority(UsageFault_IRQn, 0x01);
}
    
void UsageFault_Handler(void){
    //handle
    //I've set a breakpoint but system does not hit 
}

void BusFault_Handler(void){
    //handle
    //I've set a breakpoint but system does not hit 
}

字符串
我试图生成除零异常,并看到变量值为“无穷大”。但是系统在继续运行时没有生成任何异常。还尝试生成总线故障异常,但发生了相同的情况。
另外,当我注解EnableExceptions函数时,系统正常工作。我的代码有什么问题?ARM是否在微处理器内部处理此类错误?

q3aa0525

q3aa05251#

Cortex-M设备专门使用Thumb-2指令集,ARM使用分支/跳转/调用地址的最低有效位来确定目标是Thumb还是ARM代码,由于Cortex-M无法运行ARM代码,因此可以通过创建到 * 偶数 * 地址的跳转来生成BusFault异常。

int dummy(){ volatile x = 0 ; return x ; }

int main()
{
    typedef void (*fn_t)();
    fn_t foo = (fn_t)(((char*)dummy) - 1) ;

    foo() ;
}

字符串
下面的代码也可以工作,因为调用会在执行任何指令之前失败,所以它不需要指向任何有效的代码。

int main()
{
    typedef void (*fn_t)();
    fn_t foo = (fn_t)(0x8004000) ;    
    foo() ;
}


您可以通过强制 integer 除以零来生成使用错误:

int main()
{
    volatile int x = 0 ;
    volatile int y = 1 / x ;
}

hec6srdp

hec6srdp2#

从您的评论问题:如何生成任何异常。
下面是文档中的一个:

Encoding T1 All versions of the Thumb instruction set.
SVC<c> #<imm8>

字符串
...

Exceptions 
SVCall.


我可以通过搜索SVCall找到它。
ARM对异常有很好的记录,有一些方法可以在不破坏总线的情况下引起你列出的异常(需要一个sim an fpga或创建自己的芯片),你已经知道文档的搜索词来查找busfault和usagefault。
ARM如何处理这些(内部或外部)都有文档记录。在这种情况下,内部意味着锁定或不锁定,否则它们将执行错误处理程序(当然,除非有错误获取错误处理程序)。
大多数你可以在C中创建,而不需要借助汇编语言指令,但你必须小心,它正在生成你认为它正在生成的东西:

void fun ( void )
{
  int x = 3;
  int y = 0;
  int z = x / y;
}

Disassembly of section .text:

00000000 <fun>:
   0:   4770        bx  lr


相反,您需要的是实际生成可能导致错误的指令的东西:

int fun0 ( int x, int y )
{
    return(x/y);
}
void fun1 ( void )
{
    fun0(3,0);
}

00000000 <fun0>:
   0:   fb90 f0f1   sdiv    r0, r0, r1
   4:   4770        bx  lr
   6:   bf00        nop

00000008 <fun1>:
   8:   4770        bx  lr


但如图所示,你必须小心在哪里以及如何调用它。在这种情况下,调用是在同一个文件中完成的,因此优化器可以看到这是一个死代码,并对其进行优化,因此这样的测试将由于多种原因而无法生成错误。
这就是为什么OP需要提供一个完整的最小示例,没有看到故障的原因不是处理器。而是软件和/或测试代码。

编辑

一个完整的最小示例,除了一个GNU工具链(没有。这是在一个stm32蓝色药丸一个stm32f103.
闪光

.cpu cortex-m3
.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset /* 1 Reset */
.word hang  /* 2 NMI */
.word hang  /* 3 HardFault */
.word hang  /* 4 MemManage */
.word hang  /* 5 BusFault */
.word usagefault  /* 6 UsageFault */
.word hang  /* 7 Reserved */
.word hang  /* 8 Reserved */
.word hang  /* 9 Reserved */
.word hang  /*10 Reserved */
.word hang  /*11 SVCall */
.word hang  /*12 DebugMonitor */
.word hang  /*13 Reserved */
.word hang  /*14 PendSV */
.word hang  /*15 SysTick */
.word hang  /* External interrupt 1 */
.word hang  /* External interrupt 2 */

.thumb_func
reset:
    bl notmain
    b hang
.thumb_func
hang:   b .

.thumb_func
.globl PUT32
PUT32:
    str r1,[r0]
    bx lr

.thumb_func
.globl GET32
GET32:
    ldr r0,[r0]
    bx lr

.thumb_func
.globl dummy
dummy:
    bx lr

.thumb_func
.globl dosvc
dosvc:
    svc 1

.thumb_func
.globl hop
hop:
    bx r0


flash.ld

MEMORY
{
    rom : ORIGIN = 0x08000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > rom
    .rodata : { *(.rodata*) } > rom
    .bss : { *(.bss*) } > ram
}


fun.c

void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
void dummy ( unsigned int );
void hop ( unsigned int );

#define GPIOCBASE   0x40011000
#define RCCBASE     0x40021000
#define SHCSR       0xE000ED24

void usagefault ( void )
{
    unsigned int ra;

    while(1)
    {
        PUT32(GPIOCBASE+0x10,1<<(13+0));
        for(ra=0;ra<100000;ra++) dummy(ra);
        PUT32(GPIOCBASE+0x10,1<<(13+16));
        for(ra=0;ra<100000;ra++) dummy(ra);
    }
}

int notmain ( void )
{
    unsigned int ra;

    ra=GET32(SHCSR);
    ra|=1<<18; //usagefault
    PUT32(SHCSR,ra);

    ra=GET32(RCCBASE+0x18);
    ra|=1<<4; //enable port c
    PUT32(RCCBASE+0x18,ra);
    ra=GET32(GPIOCBASE+0x04);
    ra&=(~(3<<20));   //PC13
    ra|=  (1<<20) ;   //PC13
    ra&=(~(3<<22));   //PC13
    ra|=  (0<<22) ;   //PC13
    PUT32(GPIOCBASE+0x04,ra);

    PUT32(GPIOCBASE+0x10,1<<(13+0));
    for(ra=0;ra<200000;ra++) dummy(ra);
    PUT32(GPIOCBASE+0x10,1<<(13+16));
    for(ra=0;ra<200000;ra++) dummy(ra);

    ra=GET32(0x08000004);
    ra&=(~1);
    hop(ra);

    return(0);
}


建造

arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m3 flash.s -o flash.o
arm-none-eabi-gcc -Wall -O2 -ffreestanding -mcpu=cortex-m3 -mthumb -c so.c -o so.o
arm-none-eabi-ld -nostdlib -nostartfiles -T flash.ld flash.o so.o -o so.elf
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy -O binary so.elf so.bin


所有这些命令行选项都不是必需的arm-linux-gnueabi-和其他版本的gnu工具链从几个版本到现在都工作得很好,因为我把它们用作编译器,汇编器和链接器,而不会弄乱库或其他不同版本的东西。

UsageFault The UsageFault fault handles non-memory related faults
caused by instruction execution. 

A number of different situations  cause usage faults, including: 

 • Undefined Instruction. 
 • Invalid state on instruction execution. 
 • Error on exception return. 
 • Attempting to access a disabled or unavailable coprocessor. 

 The following can cause usage faults when the processor is configured to 
 report them: 
  • A word or halfword memory accesses to an unaligned address. 
  • Division by zero. 

Software can disable this fault. If it does, a UsageFault escalates to HardFault. UsageFault has a configurable priority.


...

Instruction execution with EPSR.T set to 0 causes the invalid state UsageFault


所以这里的测试分支到手臂地址和拇指地址,这导致了usagefault。(也可以在文档中阅读有关BX指令psr.t位如何以及何时更改等内容)
支持这一点的是stm32蓝色药丸。PC 13上有一个led,代码启用usagefault,将PC 13配置为输出, Flink 一次,这样我们就可以看到程序启动,然后如果它命中usagefault处理程序,它就会永远 Flink 。

ra&=(~1);


如果你注解掉这个,它会继续分支到重置,重置会再次执行所有操作,一个缓慢的 Flink ,你会看到永远重复。
在自然运行之前,您检查构建以查看它是否有可能工作:

Disassembly of section .text:

08000000 <_start>:
 8000000:   20001000
 8000004:   08000049
 8000008:   0800004f
 800000c:   0800004f
 8000010:   0800004f
 8000014:   0800004f
 8000018:   08000061
 800001c:   0800004f
 8000020:   0800004f
 8000024:   0800004f
 8000028:   0800004f
 800002c:   0800004f
 8000030:   0800004f
 8000034:   0800004f
 8000038:   0800004f
 800003c:   0800004f
 8000040:   0800004f
 8000044:   0800004f

08000048 <reset>:
 8000048:   f000 f82a   bl  80000a0 <notmain>
 800004c:   e7ff        b.n 800004e <hang>

0800004e <hang>:
 800004e:   e7fe        b.n 800004e <hang>

...

08000060 <usagefault>:
 8000060:   b570        push    {r4, r5, r6, lr}


向量表是正确的,正确的向量指向正确的地方.

0xE000ED28 CFSR RW 0x00000000


HFSR是CFSR的高位

> halt
target halted due to debug-request, current mode: Handler UsageFault
xPSR: 0x81000006 pc: 0x0800008a msp: 0x20000fc0
> mdw 0xE000ED28
0xe000ed28: 00020000


那就是

INVSTATE, bit[1] 
0 EPSR.T bit and EPSR.IT bits are valid for instruction execution.
1 Instruction executed with invalid EPSR.T or EPSR.IT field.


现在开始

Using the CCR, see Configuration and Control Register, CCR on page B3-604, software can enable or disable:
• Divide by zero faults, alignment faults and some features of processor operation.
• BusFaults at priority -1 and higher.


CCR的重置值是IMPLEMENTATION DEFINED,因此它可能只是为您启用或不启用,可能需要查看Cortex-m3 TRM或只需读取它:

> mdw 0xE000ED14          
0xe000ed14: 00000000


所以它的0和我的一样
所以增加乐趣。c:

unsigned int fun ( unsigned int x, unsigned int y)
{
    return(x/y);
}


更改so.c:

void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
void dummy ( unsigned int );
unsigned int fun ( unsigned int, unsigned int);

#define GPIOCBASE   0x40011000
#define RCCBASE     0x40021000
#define SHCSR       0xE000ED24

#define CCR         0xE000ED14

void usagefault ( void )
{
    unsigned int ra;

    while(1)
    {
        PUT32(GPIOCBASE+0x10,1<<(13+0));
        for(ra=0;ra<100000;ra++) dummy(ra);
        PUT32(GPIOCBASE+0x10,1<<(13+16));
        for(ra=0;ra<100000;ra++) dummy(ra);
    }
}

int notmain ( void )
{
    unsigned int ra;

    ra=GET32(SHCSR);
    ra|=1<<18; //usagefault
    PUT32(SHCSR,ra);

    ra=GET32(CCR);
    ra|=1<<4; //div by zero
    PUT32(CCR,ra);

    ra=GET32(RCCBASE+0x18);
    ra|=1<<4; //enable port c
    PUT32(RCCBASE+0x18,ra);
    ra=GET32(GPIOCBASE+0x04);
    ra&=(~(3<<20));   //PC13
    ra|=  (1<<20) ;   //PC13
    ra&=(~(3<<22));   //PC13
    ra|=  (0<<22) ;   //PC13
    PUT32(GPIOCBASE+0x04,ra);

    PUT32(GPIOCBASE+0x10,1<<(13+0));
    for(ra=0;ra<200000;ra++) dummy(ra);
    PUT32(GPIOCBASE+0x10,1<<(13+16));
    for(ra=0;ra<200000;ra++) dummy(ra);

    fun(3,0);

    return(0);
}


建造

arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m3 flash.s -o flash.o
arm-none-eabi-gcc -Wall -O2 -ffreestanding -mcpu=cortex-m3 -mthumb -c so.c -o so.o
arm-none-eabi-gcc -Wall -O2 -ffreestanding -mcpu=cortex-m3 -mthumb -c fun.c -o fun.o
arm-none-eabi-ld -nostdlib -nostartfiles -T flash.ld flash.o so.o fun.o -o so.elf
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy -O binary so.elf so.bin


确认实际上有一个除法指令,我们将命中

800011e:   2100        movs    r1, #0
 8000120:   2003        movs    r0, #3
 8000122:   f000 f80f   bl  8000144 <fun>
...
08000144 <fun>:
 8000144:   fbb0 f0f1   udiv    r0, r0, r1
 8000148:   4770        bx  lr


加载并运行,并调用处理程序。

target halted due to debug-request, current mode: Handler UsageFault
xPSR: 0x81000006 pc: 0x08000086 msp: 0x20000fc0
> mdw 0xE000ED28                
0xe000ed28: 02000000

这表示它被零除。
所以你需要知道/做的一切都在文档中,一个文档。
99.999%的裸机编程是阅读或做实验来验证读取的内容,几乎没有工作是编写最终的应用程序,这只是工作的一小部分。
在你开始裸机编程之前,你必须掌握工具链,否则什么都不会工作。掌握工具链可以在没有任何目标硬件的情况下完成,使用免费工具,所以这只是一个坐下来做的问题。
如果你想在一个没有硬件除零功能的内核上进行浮点除零,你需要看看软浮点数,例如libgcc:

ARM_FUNC_START divsf3
ARM_FUNC_ALIAS aeabi_fdiv divsf3
    CFI_START_FUNCTION

    @ Mask out exponents, trap any zero/denormal/INF/NAN.
    mov ip, #0xff
    ands    r2, ip, r0, lsr #23
    do_it   ne, tt
    COND(and,s,ne)  r3, ip, r1, lsr #23
    teqne   r2, ip
    teqne   r3, ip
    beq LSYM(Ldv_s)
LSYM(Ldv_x):

...

    @ Division by 0x1p*: let''s shortcut a lot of code.
LSYM(Ldv_1):
    and ip, ip, #0x80000000
    orr r0, ip, r0, lsr #9
    adds    r2, r2, #127
    do_it   gt, tt
    COND(rsb,s,gt)  r3, r2, #255

and so on

这应该在反汇编中可见,我没有立即看到强制异常(故意未定义的指令,swi/svc或类似的东西)。这只是一个可能的库,现在我想它看起来像手臂而不是拇指,所以必须去寻找,更容易只看拆卸。
根据你的评论,如果我再读一遍另一个问题,我假设,因为它没有引发异常,除以零的正确结果是一个正确的有符号的无穷大。但如果你切换到cortex-m4或m7,那么你可能会触发硬件异常,但....阅读文档来找出答案。

编辑2

void fun ( void )
{
    int a = 3;
    int b = 0;
    volatile int c = a/b;
}

fun.c:6:18: warning: unused variable ‘c’ [-Wunused-variable]
    6 |     volatile int c = a/b;
      |                  ^

08000140 <fun>:
 8000140:   deff        udf #255    ; 0xff
 8000142:   bf00        nop

> halt                          
target halted due to debug-request, current mode: Handler UsageFault
xPSR: 0x01000006 pc: 0x08000076 msp: 0x20000fc0
> mdw 0xE000ED28                
0xe000ed28: 00010000

这意味着

The processor has attempted to execute an undefined instruction

因此volatile未能使用gcc产生预期的结果

arm-none-eabi-gcc --version
arm-none-eabi-gcc (GCC) 10.1.0
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

arm-linux-gnueabi-gcc --version
arm-linux-gnueabi-gcc (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

产生

Disassembly of section .text:

00000000 <fun>:
   0:   deff        udf #255    ; 0xff
   2:   bf00        nop

你也可以通过别人的方式来实现你的目标。
是的,产生了一个错误,这是使用错误,但这将是另一个堆栈溢出问题,为什么我没有得到一个除以零。盲目地使用volatile来划分是行不通的。
使这三个都变得不稳定

void fun ( void )
{
    volatile int a = 3;
    volatile int b = 0;
    volatile int c = a/b;
}

Disassembly of section .text:

00000000 <fun>:
   0:   2203        movs    r2, #3
   2:   2300        movs    r3, #0
   4:   b084        sub sp, #16
   6:   9201        str r2, [sp, #4]
   8:   9302        str r3, [sp, #8]
   a:   9b01        ldr r3, [sp, #4]
   c:   9a02        ldr r2, [sp, #8]
   e:   fb93 f3f2   sdiv    r3, r3, r2
  12:   9303        str r3, [sp, #12]
  14:   b004        add sp, #16
  16:   4770        bx  lr

将产生所需的故障。
而且没有任何优化

00000000 <fun>:
   0:   b480        push    {r7}
   2:   b085        sub sp, #20
   4:   af00        add r7, sp, #0
   6:   2303        movs    r3, #3
   8:   60fb        str r3, [r7, #12]
   a:   2300        movs    r3, #0
   c:   60bb        str r3, [r7, #8]
   e:   68fa        ldr r2, [r7, #12]
  10:   68bb        ldr r3, [r7, #8]
  12:   fb92 f3f3   sdiv    r3, r2, r3
  16:   607b        str r3, [r7, #4]
  18:   bf00        nop
  1a:   3714        adds    r7, #20
  1c:   46bd        mov sp, r7
  1e:   bc80        pop {r7}
  20:   4770        bx  lr

也会产生所需的故障

所以首先掌握语言(读),然后掌握工具链(读),然后是裸机编程(读)。这一切都是关于阅读,而不是编码。如上所示,即使在这个级别上有几十年的经验,您也不能完全预测这些工具将产生什么;你必须尝试一下,但最重要的是,因为你一天一次在一台机器上为一个工具弄明白了没有理由在你的假设中变得太宽泛。必须尝试它并检查编译器产生的内容,重复该过程,直到获得所需的效果。到了紧要关头,写几行asm就可以了。
您没有看到错误,因为您没有生成任何错误和/或没有捕获它们,或者两者都没有。根据提供的代码,可能的原因列表很长,但是这些例子,你应该没有问题移植到你的平台,也应该证明你的硬件工作,然后你可以通过连接代码和代码之间的点来找出为什么你的软件没有。我所做的就是遵循文档,并检查编译器的输出,一旦我启用了最少数量的东西,就调用了错误处理程序。如果未启用,则不会触发使用故障。

r8xiu3jd

r8xiu3jd3#

总线故障、硬故障、MemmanageFault、UsageFault、SVC调用、NMI,这些是arm cortex-M微处理器的内部异常。这实际上取决于您使用的处理器,但让我们假设您使用的是cortex-m3:

  • 默认情况下,所有错误都Map到硬错误处理程序,除非您显式地启用它们以Map到它们自己的处理程序
  • 设置位:系统处理程序控制和状态寄存器中的USGFAULTENA、BUSFAULTENA、MEMFAULTENA =>这些故障中的每一个将被Map到其适当的处理程序

要生成故障,您可以尝试:

  • 访问未Map的内存区域=>这会生成总线故障
  • 执行无法识别的指令=> UsageFault
  • 通过设置以下位之一来明确触发其中一个故障:

USGFAULTACT、BUSFAULTACT、MEMFAULTACT在系统处理程序控制和状态寄存器=>中,这肯定会生成异常
详情请参阅:https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/system-control-block/system-handler-control-and-state-register?lang=en

相关问题