Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do dynamic value prints panic in my no_std kernel?

I'm trying to print dynamic values off in no_std Rust, specifically on the RISC-V architecture, but whenever I do, my kernel panics, and I can't even print the panic message because printing it panics too. I use a function I made called write that doesn't have any panic macro in it but does use unsafe rust to write directly to registers to do a SBI (supervisor binary interface) call (since the architecture is RISC-V). This function works fine until it has to print off a value that isn't a &'static str. None of my code being called here has any panic! macros, and core::str::from_utf8() should be safe since it checks to see if the utf8 encoding is valid, yet run_utf8_validation() in core::str::from_utf8() seems to be causing undefined behaviour as eventually it puts 0x1 in the return address register ra and the program circles back to the start of the kernel. Note that my kernel isn't loaded in at 0x1.

Even after putting the panicking code at the very start of my kernel without the print about the device tree, it still causes undefined behaviour.

My theory is that something in core::str::from_utf8() causes an exception, which machine mode then deals with by restarting the kernel, the kernel then panics because the registers still have the previous kernels variables in the registers which causes an invalid memory address access when the value in the a1 register is treated as a pointer. If my theory is true, what in the core library could possibly be causing an exception? I have no doubt the core library is correctly implemented, so it must be something to do with my environment that's incorrect.

// Works fine:
let _ = debug_console::write("Device tree found\n");

let bytes = [0x64];

// Panics:
if let Ok(number) = core::str::from_utf8(&bytes) {
    let _ = debug_console::write(number);
}
pub fn write(text: &str) -> SBIResult {
    const DEBUG_CONSOLE_WRITE_ID: usize = 0;
    two_arguments(
        text.len(),
        text.as_ptr() as usize,
        DEBUG_CONSOLE_WRITE_ID,
        DEBUG_CONSOLE_SBI_EXTENSION_ID,
    )
}
pub fn two_arguments(
    argument_1: usize,
    argument_2: usize,
    function_id: usize,
    extension_id: usize,
) -> SBIResult {
    let mut value;
    let mut error_code;

    unsafe {
        asm!(
            "ecall", // Short for "enviroment call", in this case it's a call to the sbi
             lateout("a0") error_code, // Gets the error value from the sbi call
             lateout("a1") value, // Gets the value value from the sbi call
             in("a0") argument_1, // Provides the first argument
             in("a1") argument_2, // Provides the second argument
             in("a2") 0, // Provides the high end of the pointers address which should always be 0
             in("a6") function_id, // Specifies function ID in extension
             in("a7") extension_id, // Specifies SBI extension
             clobber_abi("system")
        );
    }

    match error_code {
        SBI_SUCCESS => Ok(value),
        _ => Err(Error::from(error_code)),
    }
}
like image 392
CocytusDEDI Avatar asked Dec 01 '25 10:12

CocytusDEDI


1 Answers

The stack pointer wasn't being set, so any stack operation was failing.

To setup the stack, use a linker script to reserve a memory region, and then set the stack pointer to the start of that region.

On RISC-V, a few things note are:

  • The 'start' of the stack should be at the highest memory address in the stack region since the stack grows down
  • The stack pointer should be 16 Byte aligned

Linker Script Snippet:

.stack (NOLOAD) : {
    . = ALIGN(16);
    stack_end = .;
    . += STACK_SIZE;
    stack_start = .;
} > kernel_space

Rust Code Snippet:

unsafe extern "C" {
    static stack_start: u8;
}

pub extern "C" fn start() -> ! {
    // Puts the stack pointer at the start of the stack
    unsafe {
        asm!(
            "la sp, {}",
            sym stack_start,
        )
    }
}
like image 142
CocytusDEDI Avatar answered Dec 02 '25 22:12

CocytusDEDI



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!