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

时间:2019-05-28 15:42:42

标签: c exception arm kernel bare-metal

我正在ARM-Cortex A53,SoC BCM2837(换句话说是Raspberry PI 3)上进行裸机编程(正在开发内核)。我实际上是在编写负责处理迷你UART的软件(这是一种问候世界,如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代码中没有什么不好的,并且汇编代码中的地址似乎还可以...问题出在哪里?为什么会出现未定义指令异常?如果需要更多信息,我可以提供更多详细信息

1 个答案:

答案 0 :(得分:0)

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

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

  

外围设备的物理地址范围从0x3F000000到0x3FFFFFFF。的   设置外设的总线地址以映射到外设总线地址范围   从0x7E000000开始。因此,此处在总线地址0x7Ennnnnn上发布的外围设备为   在物理地址0x3Fnnnnnn处可用。

但后来报道:

  

本文档中指定的外围设备地址是总线地址。直接软件   访问外围设备必须将这些地址转换为物理或虚拟地址