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

C ARM代码中未定义的指令异常,c,exception,arm,kernel,bare-metal,C,Exception,Arm,Kernel,Bare Metal,我正在ARM Cortex A53上进行裸机编程(我正在开发内核),SoC BCM2837(换言之,是Raspberry PI 3)。实际上,我正在编写一个负责处理mini-UART(一种helloworld,正如osdevwiki上报道的那样)的软件。因此,我编写了一组函数来处理微型UART,让我们考虑下面的问题,因为对于任何其他函数,问题仍然存在: void miniUartSendByte(unsigned char byte){ // FIFO can accept at least

我正在ARM Cortex A53上进行裸机编程(我正在开发内核),SoC BCM2837(换言之,是Raspberry PI 3)。实际上,我正在编写一个负责处理mini-UART(一种helloworld,正如osdevwiki上报道的那样)的软件。因此,我编写了一组函数来处理微型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

正如您所看到的,当机器执行跳转时,它接收到一个异常并跳转到地址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;
}

void a(){

对于(int j=0;j而言,问题在于访问外围设备寄存器的基址设置错误。一旦将其设置为0x3F000000,将不再引发异常

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

外围设备的物理地址范围为0x3F000000到0x3FFFFFFF 外围设备的总线地址设置为映射到外围总线地址范围 从0x7E000000开始。因此,在总线地址0x7ENNNNN处播发的外围设备是 物理地址为0x3FNNN

但后来有报道说:

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


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

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

外围设备的物理地址范围为0x3F000000到0x3FFFFFFF 外围设备的总线地址设置为映射到外围总线地址范围 从0x7E000000开始。因此,在总线地址0x7ENNNNN处播发的外围设备是 物理地址为0x3FNNN

但后来有报道说:

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


您使用
unsigned int*AUX_____IO_REG
写入一个字节有什么原因吗?这将写入至少16位。结果还会写入什么?未签名的int*指地址寄存器AUX____IO_REG映射到,换句话说,要写入AUX______REG,我需要写入该内存位置,而且acc根据BCM2837数据表,即使寄存器是32位宽,也只有前8位是可访问的,因此我一次只能写入一个字节,其余的都被忽略。此外,我看到的任何代码片段都使用volatile unsigned int*访问寄存器(例如,看看问题中的OsDev链接,函数mmio_write()如果处理器是little-endian,那么我所做的就是(除了使用uint32_t而不是unsigned int)。好的,根据ARM文档()指令总是little-endian。你能解释一下这对执行的影响吗?我的意思是为什么miniUartSendByte()会导致跳转和像a()或b()这样的函数出现异常不要??另外,如果问题出现在寄存器中存储的数据中,我不应该遇到UART行为问题(我不知道,我想发送“c”,但发送的是完全不同的东西)而不是在代码执行上??抱歉,但我有点困惑…我尝试在gcc中使用-mlittle-endian标志进行编译,但结果没有改变。实际上gcc已经以little-endian格式编译(我认为它能够通过查看-mcpu参数来检测目标的endianness)实际上,如果我使用-mbig-endian编译,则返回“编译为大端系统,目标为小端”。我不认为这是问题所在……您使用
无符号int*AUX_____IO_REG
写入一个字节有什么原因吗?这将写入至少16位。结果还会写入什么?未签名int*指的是地址寄存器AUX______IO_REG映射到的地址寄存器,换句话说,写入我需要写入的AUX________IO_REG该内存位置,而且根据BCM2837数据表,即使寄存器为32位宽,也只能访问前8位,因此我一次只能写入一个字节,其余的都被忽略。此外,我看到的任何代码片段都使用易失性无符号int*访问寄存器(以问题中的OsDev链接为例,函数mmio_write()与我所做的完全相同(除了它们使用uint32_t而不是unsigned int),根据ARM文档()指令总是小端的。你能解释一下这是如何影响执行的吗?我的意思是为什么miniUartSendByte()会导致跳转异常,而像a()或b()这样的函数不会?另外,如果问题是存储在寄存器中的数据,我应该不会遇到UART行为问题(我不知道,我想发送'c',但发送的是一个完全不同的东西)而不是在代码执行上??抱歉,但我有点困惑…我尝试在gcc中使用-mlittle-endian标志进行编译,但结果没有改变。实际上gcc已经以little-endian格式编译了(我认为可以通过查看-mcpu参数来检测目标的endianness),事实上,如果我使用-mbig endian编译,则如下所示
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;
}