AlePalu
AlePalu

Reputation: 73

Undefined Instruction exception in ARM code

I'm doing bare-metal programming (I'm developing a kernel) on an ARM-Cortex A53, SoC BCM2837 (Raspberry PI 3 in other words). I'm actually writing the piece of software responsable to handle the mini UART (a sort of hello world, as reported on OsDev wiki https://wiki.osdev.org/ARM_RaspberryPi_Tutorial_C). So I've written a set of functions to handle the mini UART, let's consider the following, since the problem persists for any of the other functions:

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;
}

where AUX_MU_* is of type volatile unsigned int*. this the disassemble of the code above:

  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

and this the execution as reported by 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 // ??

As you can see, when the machine executes the jump, it receives an exception and jumps to the address 0x200, where the interrupt handler is placed (note, no interrupt handler has been configured, I've not implemented it yet) and gets stucked at address 0x200 executing an infinite loop (default behaviour when no interrupt handler is present). Now, from QEMU I was able to capture the type of exception:

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

I'm compiling with the following command:

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

I've also tried to see if this was a "compiler" problem, trying to execute the following totally useless code:

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;
}

but the execution goes without any problem... now I can't figure out what's going wrong. I mean, there is nothing bad in the C code producing that assembler, and addresses in the assembly code seems ok... any idea where the problem arises?? Why that Undefined Instruction exception?? If more information are needed I can provide more details

Upvotes: 3

Views: 2239

Answers (1)

AlePalu
AlePalu

Reputation: 73

The problem was in the wrong base address set for accessing the peripherals' registers. Once set it to 0x3F000000, no exception is raised anymore.

I had setted as base address 0x7E000000, misleading what reported in the BCM datasheet:

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.

But later is reported:

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

Upvotes: 2

Related Questions