Reputation: 11
I'm trying to get going on bare metal programming with my rapsberry pi 4b and I'm encountering an issue when trying to run it on 64bits. Whenever I try to write to the GPIO SET0 register, the program completly stops. When I use a similar code in 32 bits, it works just fine with the exact same base addresses and offset.
I tried simulating the kernel using qemu and gdb, and it appears that whenever I reach the line that writes into the GPIO SET0 reg, the action doesn't go through and the PC gets stuck at a random address. Maybe I'm using the wrong address (I'm currently using 0xFE20_0000) but I'm fairly sure it's the correct one.
An other source of the issue could be the way I compile the kernel8.img since I am using the aarch64-none-elf toolchain. I then replace the *.img files in the boot section of the SD card with my own kernel8.img and leave the other files (from Raspberry OS).
I wrote another assembly code that I compiled using arm-none-eabi and set the rpi to 32bits and this works but it is very inconsistant. By that I mean that the same code works sometimes and if I try to rerun it, it doesn't.
This is the code that writes into GPIO_SET0 and whenever it reaches that last line it completly stops working
.section ".text.boot"
.global _start
.equ GPIO_BASE, 0xFE200000
.equ GPIO_SET_21_OUT, 1 << 3
.equ GPIO_SEL2, 0x08
.equ GPIO_SET0, 0x1C
.equ GPIO_21, 1 << 21
_start:
ldr x0, =GPIO_BASE
ldr x1, =GPIO_SET_21_OUT
str x1, [x0, #GPIO_SEL2]
ldr x1, =GPIO_21
ldr x1, [x0, #GPIO_SET0] // this line seems to be the issue
loop:
nop
b loop
Upvotes: 1
Views: 67
Reputation: 1082
There are a few potential problems here. First and most importantly, when writing to GPIO registers, you should use str
instead of ldr
for the final GPIO_SET0 operation. You're currently trying to read from the SET0 register rather than write to it. Here's the corrected line:
str x1, [x0, #GPIO_SET0] // Use str to write to the register
The base address 0xFE200000 is correct for the Raspberry Pi 4's peripheral base when using the ARM64 architecture, so that's not the issue.
Your setup code for configuring GPIO 21 as an output looks correct, using GPIO_SEL2 register (GPFSEL2) and setting bits 3-5 for GPIO 21.
When running bare metal code, you need to ensure proper initialization. Here's a more complete version of your code that might help:
.section ".text.boot"
.global _start
.equ GPIO_BASE, 0xFE200000
.equ GPIO_SET_21_OUT, 1 << 3
.equ GPIO_SEL2, 0x08
.equ GPIO_SET0, 0x1C
.equ GPIO_21, 1 << 21
_start:
// Ensure we're in EL1
mrs x0, currentel
lsr x0, x0, #2
cmp x0, #1
bne .
// Disable all interrupts
msr daifset, #0xf
// Initialize stack pointer
ldr x0, =_start
mov sp, x0
// GPIO initialization
ldr x0, =GPIO_BASE
ldr x1, =GPIO_SET_21_OUT
str x1, [x0, #GPIO_SEL2]
ldr x1, =GPIO_21
str x1, [x0, #GPIO_SET0] // Changed to str
loop:
nop
b loop
For your compilation, make sure you're using the correct flags. Here's a suggested compilation process:
aarch64-none-elf-as -c source.s -o source.o
aarch64-none-elf-ld -T linker.ld source.o -o kernel8.elf
aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img
And here's a basic linker script (linker.ld):
SECTIONS
{
. = 0x80000;
.text : { *(.text.boot) *(.text*) }
.rodata : { *(.rodata*) }
.data : { *(.data*) }
.bss : { *(.bss*) }
}
The inconsistency in 32-bit mode might be related to the initialization state of the hardware. The Raspberry Pi's boot process can leave the hardware in different states, which is why proper initialization is important.
Upvotes: 0