Skip to content

Conversation

@folkertdev
Copy link

@folkertdev folkertdev commented Nov 24, 2025

Support for the cmse-nonsecure-entry and cmse-nonsecure-call calling conventions on ArmV8m (thumbv8*) targets, and a lint preventing (partially) uninitialized values from crossing the security boundary.

extern "cmse-nonsecure-entry" fn entry(callback: extern "cmse-nonsecure-call" fn(_: u64)) { 
    callback(42);
}

The implementation is tracked in:

Important

When responding to RFCs, try to use inline review comments (it is possible to leave an inline review comment for the entire file at the top) instead of direct comments for normal comments and keep normal comments for procedural matters like starting FCPs.

This keeps the discussion more organized.

@rustbot label +T-lang

Rendered

@rustbot rustbot added the T-lang Relevant to the language team, which will review and decide on the RFC. label Nov 24, 2025
@folkertdev folkertdev changed the title RCF for CMSE calling conventions RFC for CMSE calling conventions Nov 24, 2025
@folkertdev folkertdev force-pushed the cmse-calling-conventions branch from f30d3be to 21f5b58 Compare November 24, 2025 12:14
@folkertdev folkertdev changed the title RFC for CMSE calling conventions CMSE calling conventions Nov 24, 2025
Copy link

@thejpster thejpster left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had one quibble with the architecture terminology, but everything else looks good to me.

Co-authored-by: Jonathan 'theJPster' Pallant <[email protected]>
@folkertdev
Copy link
Author

This has now been reviewed by several people from the rust embedded community and from Arm, so I think this is ready for T-lang to take a look.

@rustbot label +I-lang-nominated

@rustbot rustbot added the I-lang-nominated Indicates that an issue has been nominated for prioritizing at the next lang team meeting. label Nov 30, 2025
@traviscross traviscross added the P-lang-drag-2 Lang team prioritization drag level 2. label Dec 3, 2025

The `cmse-nonsecure-call` ABI can only be used on function pointers. Using it in for a function definition or extern block emits an error. It is invalid to cast to or from `extern "aapcs"`.

The `cmse-nonsecure-entry` ABI is allowed on function definitions, extern blocks and function pointers. It is sound and valid (in some cases even encouraged) to cast such a function to `extern "aapcs"`. Calling the function is valid and will behave as expected in both the secure and non-secure applications. Casting from `extern "aapcs"` to `extern "C"` is invalid.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a number instances where the RFC discusses casting a function, but in Rust the function types cannot be explicitly named at all, meaning there is also no way to cast a function type to have a different calling convention. Can you please clarify the RFC with what sorts of casts you're talking about here? Is it casts of function types to function pointer types with a different ABI? Do we enable nonsecure_entry_function as extern "aapcs" fn(...)? Or just casts between different function pointers requiring a nonsecure_entry_function as extern "cmse-nonsecure-entry" fn(...) as extern "aapcs" fn (...)?

If its latter, please qualify every instance of "function cast" to a "function pointer cast" or similar.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, that language is imprecise.

The cmse calling conventions are a subset of aapcs, which means that in some cases extern "cmse-nonsecure-entry" can be treated as extern "aapcs" (i.e. extern "C" on arm platforms).

My intention was to say that the following is therefore valid:

#[unsafe(no_mangle)]
extern "cmse-nonsecure-entry" fn foo(x: i32) -> i32 { x + x }

unsafe extern "aapcs" {
    #[link_name = "foo"]
    safe fn bar(x: i32) -> i32; 
}

fn main() {
    dbg!(bar());
}

Similarly a transmute between the two types would result in a function that is perfectly sound to call.

I did not mean that we should support using as to cast between these types. This currently does't work either between extern "C" and the target-specific name for the current C abi, e.g.

extern "sysv64" fn foo() {}

fn main() {
    dbg!((foo as extern "C" fn())());
}

fails with

error[E0605]: non-primitive cast: `extern "sysv64" fn() {foo}` as `extern "C" fn()`
 --> src/main.rs:4:10
  |
4 |     dbg!((foo as extern "C" fn())());
  |          ^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

I-lang-nominated Indicates that an issue has been nominated for prioritizing at the next lang team meeting. P-lang-drag-2 Lang team prioritization drag level 2. T-lang Relevant to the language team, which will review and decide on the RFC.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants