Reputation: 73
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
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