Stack pointer misalignment in aarch64 EL1

Viewed 187

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"
}
0 Answers
Related