簡體   English   中英

如何在 Cortex M3 上生成異常?

[英]How to Generate Exceptions on Cortex M3?

我正在嘗試在 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 function 系統工作正常。 我的代碼有什么問題? ARM 是否處理微處理器內部的此類錯誤?

Cortex-M 設備專門使用 Thumb-2 指令集,ARM 使用分支/跳轉/調用地址的最低有效位來確定目標是 Thumb 還是 ARM 代碼,因為 Cortex-M 無法運行 Z47F45E61A5446C1 代碼可以通過創建到偶數地址的跳轉來生成 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 ;
}

從您的評論問題:如何生成任何異常。

這是文檔中的一個:

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

...

Exceptions 
SVCall.

我可以通過搜索 SVCall 找到。

ARM 很好地記錄了異常,有一些方法可以導致您列出的異常而無需中斷總線(需要 sim 和 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 上......

flash.s

.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 工具鏈從幾個版本到現在都可以正常工作,因為我將它們用作編譯器、匯編器和 linker 並且不要弄亂庫或其他從一種口味到另一種口味的東西。

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

所以這里的測試分支到 arm 地址與拇指地址,這會導致使用錯誤。 (也可以在文檔中閱讀有關 BX 指令的 psr.t 位如何以及何時更改等)

支持它的是 stm32 藍色葯丸。 PC13 上有一個 LED,代碼啟用了使用錯誤,將 PC13 配置為 output,閃爍一次,因此我們看到程序已啟動,然后如果遇到使用錯誤處理程序,則它會永遠閃爍。

ra&=(~1);

如果您將其注釋掉,那么它會繼續分支以重置,這會再次緩慢閃爍一次,您會看到它永遠重復。

在自然運行之前,您檢查構建以查看它是否有機會工作:

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

所以它在我的零。

所以添加 fun.c:

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

改成這樣。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 或類似的東西)。 這只是一個可能的庫,現在我想它看起來像 arm 而不是拇指,所以必須 go 尋找它,更容易只看反匯編。

根據您的評論,如果我再次閱讀另一個問題,我假設因為它沒有引發異常,除以零的正確結果是正確簽名的無窮大。 但是如果您切換到 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

也可以(而且你可以通過其他人用你的方式用上帝的方式)。

是的,產生了一個錯誤,這是使用錯誤,但這將是另一個 Stack Overflow 問題,即為什么我沒有被零除。 盲目地使用 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

也會產生所需的故障

所以首先掌握語言(讀讀讀),然后掌握工具鏈(讀讀讀),然后是裸機編程(讀讀讀)。 這完全是關於閱讀,而不是關於編碼。 如上所示,即使在此級別擁有數十年的經驗,您也無法完全預測這些工具會產生什么; 您必須嘗試一下,但最重要的是,因為您一天一次在一台機器上為一種工具解決了這個問題,所以沒有理由對您的假設過於寬泛。 必須嘗試並檢查編譯器生成的內容,重復該過程直到獲得所需的效果。 Push 到了,只需要寫幾行 asm 就可以了。

你沒有看到錯誤,因為你沒有產生任何和/或沒有捕獲它們或兩者兼而有之。 可能的原因列表基於提供的代碼很長,但是這些示例,您應該可以毫無問題地移植到您的平台,應該也可以證明您的硬件也可以工作,然后您可以通過連接來找出軟件無法正常工作的原因執行的代碼和不執行的代碼之間的點。 我所做的只是按照文檔,檢查編譯器的 output,一旦我啟用了最少數量的東西,就會調用故障處理程序。 如果沒有啟用這些,則不會觸發使用錯誤。

BusFault、HardFault、MemmanageFault、UsageFault、SVC Call、NMI 這些是 arm cortex-M 微處理器的內部異常。 這實際上取決於您使用的是哪個處理器,但假設您使用的是 cortex-m3:

  • 默認情況下,所有故障都映射到硬故障處理程序,除非您明確啟用它們以映射到自己的處理程序
  • 設置位:系統處理程序控制中的 USGFAULTENA、BUSFAULTENA、MEMFAULTENA 和 state 寄存器 => 這些故障每個都將映射到其正確的處理程序

要生成故障,您可以嘗試:

  • 訪問未映射 memory 區域 => 這會生成 Busfault
  • 執行無法識別的指令 => 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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM