I am working on a low level program in rust on a Raspberry pi. I have been able to use UART and other features when running in EL2. I have been trying to transition to EL1. I am able to blink and LED from EL1 however whenver I call a function that uses the stack pointer, I get error code 0b100110, which indicates a "SP alignment fault exception." I have tried setting the stack pointer from EL1, which works, however I still get the exception. Is there a reason why this is happening?
Here is my start.s:
.section ".text.boot"
.global _start
_start:
// read cpu id, stop slave cores
mrs x1, mpidr_el1
and x1, x1, #3
cbz x1, 2f
// cpu id > 0, stop
1: wfe
b 1b
2: // cpu id == 0
// set stack before our code
ldr x1, =_start
//msr SPSel, xzr //use sp_el0 as the default sp
mov sp, x1
// clear bss
ldr x1, =bss_start
ldr w2, =bss_size
3: cbz w2, 4f
str xzr, [x1], #8
sub w2, w2, #1
cbnz w2, 3b
// set up exception handlers and jump to Rust, should not return
4: ldr x0, =_exception_vector
msr VBAR_EL1, x0
ldr x0, =HEAP_START
b main
Here is start.rs:
#[no_mangle]
pub extern "C" fn main(heap_start: usize) {
if cpu::el() == 2 {
// Counter and Timer Hyp Control
// allow el 1 and 0 access to the timer and counter reigsters
write!("CNTHCTL_EL2", read!("CNTHCTL_EL2") | 0b11);
// set offset to 0
write!("CNTVOFF_EL2", 0);
// allow el1 and 0 to use the fancy SIMD and FP registers (I paid for them, I'm damned well going to use them)
write!("CPTR_EL2", read!("CPTR_EL2") | (0b11 << 20));
write!("CPACR_EL1", read!("CPACR_EL1") | (0b11 << 20));
// set el1 to 64 bit execution
// 31: 64 bit execution, 1: Set/Way Invalidation Override
write!("HCR_EL2", (1 << 31) | (1 << 1));
// Saved program status register
// fake an exception to enter EL1
// 9-6: DAIF
// 5: Res0
// 4: 0b0: AArch64 execution state
// 3-0: SP: 0b0100 = EL1t = sp_el0
write!("SPSR_EL2", 0b1111000100);
// 4: SA0
// 3: SA
//write!("SCTLR_EL1", read!("SCTLR_EL1") | 0b11000);
write!("ELR_el2", init_el1 as *const () as usize);
cpu::eret();
}
}
#[no_mangle]
pub fn init_el1() {
let mmio = MMIOController::default();
let gpio = GPIOController::new(&mmio);
let status_light = StatusLight::init(&gpio);
let timer = Timer::new(&mmio);
blink_sequence(&status_light, &timer, 250);
use_uart();
}
#[inline(never)]
#[no_mangle]
pub fn use_uart() {
let mmio = MMIOController::default();
let gpio = GPIOController::new(&mmio);
let timer = Timer::new(&mmio);
let mut uart = UARTController::init(&gpio, &mmio);
uart.putc('f');
}
pub fn blink_sequence(status_light: &StatusLight, timer: &Timer, interval: u64) {
status_light.set_green(OutputLevel::High);
timer.delay(interval);
status_light.set_green(OutputLevel::Low);
status_light.set_blue(OutputLevel::High);
timer.delay(interval);
status_light.set_blue(OutputLevel::Low);
status_light.set_red(OutputLevel::High);
timer.delay(interval);
status_light.set_red(OutputLevel::Low);
}
and this is the llvm arch specification JSON I have been using:
{
"llvm-target": "aarch64-unknown-none",
"target-endian": "little",
"target-pointer-width": "64",
"target-c-int-width": "32",
"os": "none",
"env": "eabi",
"vendor": "unknown",
"arch": "aarch64",
"linker-flavor": "ld",
"linker": "aarch64-none-elf-ld",
"pre-link-args": {
"ld": [
"-Taarch64-raspi3.ld"
]
},
"data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
"executables": true,
"relocation-model": "static",
"features": "+strict-align,+neon,+fp-armv8"
}