c - ARM代码中的未定义指令异常

标签 c exception arm kernel bare-metal

我正在 ARM-Cortex A53、SoC BCM2837(即 Raspberry PI 3)上进行裸机编程(我正在开发内核)。我实际上正在编写负责处理迷你 UART 的软件(一种 hello world,如 OsDev wiki https://wiki.osdev.org/ARM_RaspberryPi_Tutorial_C 所报道)。所以我写了一组函数来处理迷你 UART,让我们考虑以下问题,因为任何其他函数仍然存在问题:

void miniUartSendByte(unsigned char byte){
  // FIFO can accept at least one byte
  while(*AUX_MU_LSR_REG & 0b100000);

  // write byte to buffer
  *AUX_MU_IO_REG = byte;
  return;
}

其中 AUX_MU_* 的类型为 volatile unsigned int*。这是上面代码的反汇编:

  1000ac:       d10043ff        sub     sp, sp, #0x10
  1000b0:       39003fe0        strb    w0, [sp, #15]
  1000b4:       d503201f        nop
  1000b8:       d28a0a80        mov     x0, #0x5054                     // #20564
  1000bc:       f2afc420        movk    x0, #0x7e21, lsl #16
  1000c0:       b9400000        ldr     w0, [x0]
  1000c4:       121b0000        and     w0, w0, #0x20
  1000c8:       7100001f        cmp     w0, #0x0
  1000cc:       54ffff61        b.ne    1000b8 <miniUartSendByte+0xc>   // b.any
  1000d0:       d28a0800        mov     x0, #0x5040                     // #20544
  1000d4:       f2afc420        movk    x0, #0x7e21, lsl #16
  1000d8:       39403fe1        ldrb    w1, [sp, #15]
  1000dc:       b9000001        str     w1, [x0]
  1000e0:       d503201f        nop
  1000e4:       910043ff        add     sp, sp, #0x10
  1000e8:       d65f03c0        ret

这是 QEMU 报告的执行:

----------------
IN: kernel_main
0x00100050:  a9bf7bfd  stp      x29, x30, [sp, #-0x10]!
0x00100054:  910003fd  mov      x29, sp
0x00100058:  52800c60  movz     w0, #0x63
0x0010005c:  94000012  bl       #0x1000a4 // jump to miniUartSendByte

----------------
IN: miniUartSendByte
0x001000a4:  d10043ff  sub      sp, sp, #0x10
0x001000a8:  39003fe0  strb     w0, [sp, #0xf]
0x001000ac:  d503201f  nop      
0x001000b0:  d28a0a80  movz     x0, #0x5054
0x001000b4:  f2afc420  movk     x0, #0x7e21, lsl #16
0x001000b8:  b9400000  ldr      w0, [x0]
0x001000bc:  121b0000  and      w0, w0, #0x20
0x001000c0:  7100001f  cmp      w0, #0
0x001000c4:  54ffff61  b.ne     #0x1000b0

----------------
IN: 
0x00000200:  00000000  .byte    0x00, 0x00, 0x00, 0x00 // ??

可以看到,机器在执行跳转的时候,收到异常,跳转到地址0x200,这里是放置中断处理程序的地方(注意,没有配置中断处理程序,我还没实现)并卡在地址 0x200 执行无限循环(不存在中断处理程序时的默认行为)。现在,我可以从 QEMU 捕获异常类型:

Taking exception 1 [Undefined Instruction]
...from EL3 to EL3
...with ESR 0x0/0x2000000
...with ELR 0x200
...to EL3 PC 0x200 PSTATE 0x3cd

我正在使用以下命令进行编译:

aarch64-elf-gcc -Wall -O0 -ffreestanding -nostdinc -nostdlib -nostartfiles -mcpu=cortex-a53 -g -c ... -o ...

我还尝试查看这是否是“编译器”问题,尝试执行以下完全无用的代码:

void a(){
  for(int j=0; j<10; j++);
  return;
}

void b(char* string){
  for(int i = 0; i<10; i++){
    a();
  }
  return;
}

void kernel_main(){

  a();
  b("test");

  while(1);

  return;
}

但是执行没有任何问题...... 现在我不知道出了什么问题。我的意思是,生成该汇编程序的 C 代码没有任何问题,而且汇编代码中的地址似乎没问题……知道问题出在哪里吗?为什么会出现未定义指令异常??如果需要更多信息,我可以提供更多详细信息

最佳答案

问题出在访问外设寄存器的基址设置错误。一旦将其设置为 0x3F000000,就不会再引发异常。

我将基地址设置为 0x7E000000,误导了 BCM 数据表中的报告:

Physical addresses range from 0x3F000000 to 0x3FFFFFFF for peripherals. The bus addresses for peripherals are set up to map onto the peripheral bus address range starting at 0x7E000000. Thus a peripheral advertised here at bus address 0x7Ennnnnn is available at physical address 0x3Fnnnnnn.

但后来报道:

The peripheral addresses specified in this document are bus addresses. Software directly accessing peripherals must translate these addresses into physical or virtual addresses

关于c - ARM代码中的未定义指令异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56345771/

相关文章:

c# - 核心库的异常架构

c++ - LPC1700不会从深度 sleep 中醒来

c - 禁用 DCache 将阻止设置atomic_flag

C:返回通用寄存器

c - 如何将格式化字符串附加到数组中?

c++ - 创建复制传递给它们的参数的线程

c - 如何在内联汇编中替换32位变量的字节?

c - 前缀和的并行化 (Openmp)

python - 捕获异常和处理异常有什么区别?

exception - 我可以避免在使用 catching(...) 时多余地 throw Throwable 吗?