Is it possible to branch to a function in another module from inline asm?

Hi all, I'm new to Rust and embedded, doing my first steps and I've got the following question:
(Target: aarch64-unknown-none)

Let's say I have boot init code in main.rs:

mod kernel;

#[no_mangle]
fn _start() {
    unsafe {
        asm!("
                mrs x0, mpidr_el1
                and x0, x0, 3
                cbz x0, 2f
            1:  wfe
                b 1b
            2:  adr x0, _start
                mov sp, x0
            ");
    }
    kernel::main();
}

And kernel.rs:

pub fn main() {
    // do stuff
}

Is it possible to branch to kernel::main from inline assembly block and how?

adr x0, kernel::main
br x0

If this is possible, any reason I should never do this?

Couldn't you mark kernel::main() as #[no_mangle] extern "C" fn main() (removes name mangling and directly exports the main symbol with the C ABI) then execute a jump/call to the main() function like calling any other C function? Then during compilation the linker should make sure your _start calls kernel::main().

As far as I can tell this is a perfectly legitimate thing to do. For example, the entry point to a normal OS kernel is often written in assembly to set up the hardware, then it'll jump to some function written in C (or Rust!) to begin the kernel proper.

Yep, that what I was initially doing. I just wanted to jump to main() from inline assembly block. And I managed to do it -- I just had to add use kernel::main;, so now the asm block looks like this:

mod kernel;
use kernel::main;

#[no_mangle]
fn _start() {
    unsafe {
        asm!("
                mrs x0, mpidr_el1
                and x0, x0, 3
                cbz x0, 2f
            1:  wfe
                b 1b
            2:  adr x0, _start
                mov sp, x0
                adr x0, main    
                br x0                     // jump to kernel::main
            ");
    }
}

The downside -- I started receiving warning: unused import: kernel::main but it can be turned off

Finally I figured out the proper way to do this:

mod kernel;

fn _start() {
    unsafe {
        asm!("
                mrs x0, mpidr_el1
                and x0, x0, 3
                cbz x0, 2f
            1:  wfe
                b 1b
            2:  adr x0, _start
                mov sp, x0
                adr x0, $0
                br x0
            "::"i"(kernel::main as fn()));
    }
}
2 Likes