rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
97.41k stars 12.59k forks source link

Tracking Issue for the C-cmse-nonsecure-call ABI #81391

Open hug-dev opened 3 years ago

hug-dev commented 3 years ago

This is a tracking issue for the PR #81346. The feature gate for the issue is #![feature(abi_c_cmse_nonsecure_call)].

Description

The TrustZone-M feature is available for targets with the Armv8-M architecture profile (thumbv8m in their target name). LLVM, the Rust compiler and the linker are providing support for the TrustZone-M feature.

One of the things provided, with this unstable feature, is the C-cmse-nonsecure-call function ABI. This ABI is used on function pointers to non-secure code to mark a non-secure function call (see section 5.5 for details).

With this ABI, the compiler will do the following to perform the call:

To avoid using the non-secure stack, the compiler will constrain the number and type of parameters/return value.

The extern "C-cmse-nonsecure-call" ABI is otherwise equivalent to the extern "C" ABI.

Example

#![no_std]
#![feature(abi_c_cmse_nonsecure_call)]

#[no_mangle]
pub fn call_nonsecure_function(addr: usize) -> u32 {
    let non_secure_function =
        unsafe { core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn() -> u32>(addr) };
    non_secure_function()
}
$ rustc --emit asm --crate-type lib --target thumbv8m.main-none-eabi function.rs

call_nonsecure_function:
        .fnstart
        .save   {r7, lr}
        push    {r7, lr}
        .setfp  r7, sp
        mov     r7, sp
        .pad    #16
        sub     sp, #16
        str     r0, [sp, #12]
        ldr     r0, [sp, #12]
        str     r0, [sp, #8]
        b       .LBB0_1
.LBB0_1:
        ldr     r0, [sp, #8]
        push.w  {r4, r5, r6, r7, r8, r9, r10, r11}
        bic     r0, r0, #1
        mov     r1, r0
        mov     r2, r0
        mov     r3, r0
        mov     r4, r0
        mov     r5, r0
        mov     r6, r0
        mov     r7, r0
        mov     r8, r0
        mov     r9, r0
        mov     r10, r0
        mov     r11, r0
        mov     r12, r0
        msr     apsr_nzcvq, r0
        blxns   r0
        pop.w   {r4, r5, r6, r7, r8, r9, r10, r11}
        str     r0, [sp, #4]
        b       .LBB0_2
.LBB0_2:
        ldr     r0, [sp, #4]
        add     sp, #16
        pop     {r7, pc}

Steps

nagisa commented 3 years ago

What's the name of the actual underlying calling convention? Is it AAPCS? I think the extern name should contain it too somehow.

hug-dev commented 3 years ago

What's the name of the actual underlying calling convention?

It will use the C convention ultimately, the current implementation maps it to llvm::CCallConv (I guess similar than AAPCS as this feature is only available on Arm processors?). Do you mean that because it might be possible to have the cmse_nonsecure_call feature available for other ABIs as well?

I think restricting it to only ever use the C ABI is not a bad thing: this is used to switch to functions that are defined in other executable files that could have been written in any programming language.

nagisa commented 3 years ago

Well, what I really want is for the underlying ABI to be explicit in the ABI string, whatever it is, so that it is more obvious that a transmute as given in the example above is… valid. It would also, as you mention, enable us to add non-secure options for other calling conventions if necessary.

So a couple of proposals: C_cmse_nonsecure or cmse_nonsecure_C.

hug-dev commented 3 years ago

Ok makes sense! Will modify the implementation PR and this with the C in front: extern "C-cmse-nonsecure-call".

folkertdev commented 2 months ago

Request for Stabilization

Summary

We propose to stabilize the C-cmse-nonsecure-call ABI. It can only be used in function pointer types, never in an actual *extern "C-cmse-nonsecure-call" {} block. Such function pointers are typically received via FFI.

Usage example

https://godbolt.org/z/KT6hc5Y7W

// `cargo build --target thumbv8m.main-none-eabi`
#![feature(abi_c_cmse_nonsecure_call)]
#![no_std]

#[no_mangle]
pub fn call_nonsecure(f: unsafe extern "C-cmse-nonsecure-call" fn(u8, u16, u32) -> f32) -> f32 {
    unsafe { f(1, 2, 3) }
}

Which produces this LLVM IR:

; Function Attrs: nounwind
define dso_local float @call_nonsecure(ptr %f) unnamed_addr #0 !dbg !5 {
start:
  %_0 = call float %f(i8 zeroext 1, i16 zeroext 2, i32 3) #1, !dbg !10
  ret float %_0, !dbg !11
}

attributes #0 = { nounwind "frame-pointer"="all" "target-cpu"="generic" }
attributes #1 = { nounwind "cmse_nonsecure_call" }

Which produces this assembly:

call_nonsecure:
        push    {r7, lr}
        mov     r7, sp
        mov     r3, r0
        movs    r0, #1
        movs    r1, #2
        movs    r2, #3
        push.w  {r4, r5, r6, r7, r8, r9, r10, r11}
        bic     r3, r3, #1
        sub     sp, #136
        vlstm   sp
        mov     r4, r3 ; <- All unused registers get cleared, to not leak information
        mov     r5, r3
        mov     r6, r3
        mov     r7, r3
        mov     r8, r3
        mov     r9, r3
        mov     r10, r3
        mov     r11, r3
        mov     r12, r3
        msr     apsr_nzcvq, r3
        blxns   r3 ; <- the expected non-secure call instruction
        vlldm   sp
        add     sp, #136
        pop.w   {r4, r5, r6, r7, r8, r9, r10, r11}
        pop     {r7, pc}

Error messages

The calling requirements for this ABI are checked within rustc to produce good error messsages.

#![feature(abi_c_cmse_nonsecure_call)]
#![no_std]

#[no_mangle]
pub fn call_nonsecure(f: unsafe extern "C-cmse-nonsecure-call" fn(u64, u64, u64) -> (u64, u64)) {
    unsafe { f(1, 2, 3) };
}

Produces the following errors:

error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
 --> <source>:5:77
  |
5 | pub fn call_nonsecure(f: unsafe extern "C-cmse-nonsecure-call" fn(u64, u64, u64) -> (u64, u64)) {
  |                                                                             ^^^ this argument doesn't fit in the available registers
  |
  = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers

error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
 --> <source>:5:85
  |
5 | pub fn call_nonsecure(f: unsafe extern "C-cmse-nonsecure-call" fn(u64, u64, u64) -> (u64, u64)) {
  |                                                                                     ^^^^^^^^^^ this type doesn't fit in the available registers
  |
  = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
  = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0798`.

Documentation

Tests

Test cases are in

https://github.com/rust-lang/rust/tree/master/tests/ui/cmse-nonsecure/cmse-nonsecure-call

The tests in the (as yet unmerged) PR for cmse-nonsecure-entry validate the assembly output of this ABI.

C examples

Clang: https://godbolt.org/z/7ch3xcz96 GCC: https://godbolt.org/z/16arxab5x

tdittr commented 2 months ago

This tracking issues seems to be missing some tags compared to #75835

@rustbot label +T-compiler +T-lang +A-codegen

jieyouxu commented 2 months ago

Nominating for both T-lang and T-compiler discussion: there is a request for the stabilization of the C-cmse-nonsecure-call ABI (posted above at https://github.com/rust-lang/rust/issues/81391#issuecomment-2258346708), there's also the seemingly related #[cmse_nonsecure_entry] attribute (#75835). This is mostly nominated for several reasons stated below.

For T-compiler:

For T-lang:

For both T-compiler and T-lang:

@rustbot labels +I-compiler-nominated +I-lang-nominated

apiraino commented 1 month ago

Discussed in t-compiler triage on Zulip. Probably T-lang could be decide if we're ok in principle with having such an abi (the compiler should then just need to pass to LLVM the right info).

@rustbot label -I-compiler-nominated