Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I assign a value to the stack pointer of an ARM chip in Rust?

Tags:

embedded

rust

I'm trying to write a bootloader for an STM32 in Rust and I can't figure out how to correctly populate the stack pointer. Near as I can tell the code should be:

asm!("MOV SP, $0" :: "0"(stack_pointer));  // set the stack pointer

but the compiler disagrees:

error: invalid operand in inline asm: 'MOV SP, $0'
  --> src/main.rs:38:5
   |
38 |     asm!("MOV SP, $0" :: "0"(stack_pointer));  // set the stack pointer
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: <inline asm>:1:11: error: unexpected token in operand
        MOV SP, 
                ^

  --> src/main.rs:38:5
   |
38 |     asm!("MOV SP, $0" :: "0"(stack_pointer));  // set the stack pointer
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

What am I doing wrong? It seem to be complaining about the dollar sign, but I got that directly from the documentation.

Per the conversation in the comments, I've tried two things, both of which compile(!) but neither of which seem to work (but that could be for any of a billion reasons, still working on it):

Version A:

asm!("MOV R0, #0x0800");
asm!("LSL R0, R0, #16");
asm!("MOV R1, #0x8000");
asm!("ORR R2, R1, R0");
asm!("LDRT R0, [R2]");
asm!("MOV SP, R0");
entry_point()

Version B:

#[inline(never)]
unsafe fn go(_addr: u32, entry_point: fn()->()) {
    asm!("MOV SP, R0");
    entry_point()
}
like image 610
teryret Avatar asked Oct 17 '22 22:10

teryret


1 Answers

It seems to me you're having two problems:

  1. figuring out what the assembly code should look like in the end
  2. how to build it from Rust.

The second part seems easy with global_asm!:

#![feature(global_asm)]

global_asm!(r#"
.global go
go:
    mov sp, r0
    bx r1
"#);
extern "C" {
    pub fn go(addr: u32, entry_point: extern fn());
}

or with normal asm!:

#![feature(asm)]

#[no_mangle]
pub unsafe fn go(addr: u32, entry_point: fn()) {
    asm!("mov sp, r0" :: "{r0}"(addr) : "sp");
    entry_point()
}

I think the "0"(stack_pointer) part didn't work because stack_pointer is not a constant.

Your Version B results in the same ASM code; but it really should mark "sp" as clobbered, and "{r0}"(addr) makes sure the first argument really is in r0.

Clifford seems to prefer passing a pointer to a "vector table", which could look like this:

#[repr(C)]
pub struct VectorTable {
    stack_pointer: u32,
    entry_point: extern fn() -> !,
}

global_asm!(r#"
.global go
go:
    ldr sp, [r0]
    ldr pc, [r0, #4]
"#);
extern "C" {
    pub fn go(vt: &VectorTable) -> !;
}
like image 165
Stefan Avatar answered Oct 20 '22 15:10

Stefan