Add a new ABI to support cmse_nonsecure_call
This commit adds a new ABI to be selected via `extern "C-cmse-nonsecure-call"` on function pointers in order for the compiler to apply the corresponding cmse_nonsecure_call callsite attribute. For Armv8-M targets supporting TrustZone-M, this will perform a non-secure function call by saving, clearing and calling a non-secure function pointer using the BLXNS instruction. See the page on the unstable book for details. Signed-off-by: Hugues de Valon <hugues.devalon@arm.com>
This commit is contained in:
parent
d60b29d1ae
commit
ce9818f2b7
@ -156,6 +156,14 @@ impl<'a> PostExpansionVisitor<'a> {
|
||||
"efiapi ABI is experimental and subject to change"
|
||||
);
|
||||
}
|
||||
"C-cmse-nonsecure-call" => {
|
||||
gate_feature_post!(
|
||||
&self,
|
||||
abi_c_cmse_nonsecure_call,
|
||||
span,
|
||||
"C-cmse-nonsecure-call ABI is experimental and subject to change"
|
||||
);
|
||||
}
|
||||
abi => self
|
||||
.sess
|
||||
.parse_sess
|
||||
|
@ -28,6 +28,7 @@ fn clif_sig_from_fn_abi<'tcx>(
|
||||
Conv::X86_64SysV => CallConv::SystemV,
|
||||
Conv::X86_64Win64 => CallConv::WindowsFastcall,
|
||||
Conv::ArmAapcs
|
||||
| Conv::CCmseNonSecureCall
|
||||
| Conv::Msp430Intr
|
||||
| Conv::PtxKernel
|
||||
| Conv::X86Fastcall
|
||||
|
@ -389,7 +389,7 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
||||
|
||||
fn llvm_cconv(&self) -> llvm::CallConv {
|
||||
match self.conv {
|
||||
Conv::C | Conv::Rust => llvm::CCallConv,
|
||||
Conv::C | Conv::Rust | Conv::CCmseNonSecureCall => llvm::CCallConv,
|
||||
Conv::AmdGpuKernel => llvm::AmdGpuKernel,
|
||||
Conv::AvrInterrupt => llvm::AvrInterrupt,
|
||||
Conv::AvrNonBlockingInterrupt => llvm::AvrNonBlockingInterrupt,
|
||||
@ -546,6 +546,18 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
||||
if cconv != llvm::CCallConv {
|
||||
llvm::SetInstructionCallConv(callsite, cconv);
|
||||
}
|
||||
|
||||
if self.conv == Conv::CCmseNonSecureCall {
|
||||
// This will probably get ignored on all targets but those supporting the TrustZone-M
|
||||
// extension (thumbv8m targets).
|
||||
unsafe {
|
||||
llvm::AddCallSiteAttrString(
|
||||
callsite,
|
||||
llvm::AttributePlace::Function,
|
||||
rustc_data_structures::const_cstr!("cmse_nonsecure_call"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1100,6 +1100,7 @@ extern "C" {
|
||||
// Operations on call sites
|
||||
pub fn LLVMSetInstructionCallConv(Instr: &Value, CC: c_uint);
|
||||
pub fn LLVMRustAddCallSiteAttribute(Instr: &Value, index: c_uint, attr: Attribute);
|
||||
pub fn LLVMRustAddCallSiteAttrString(Instr: &Value, index: c_uint, Name: *const c_char);
|
||||
pub fn LLVMRustAddAlignmentCallSiteAttr(Instr: &Value, index: c_uint, bytes: u32);
|
||||
pub fn LLVMRustAddDereferenceableCallSiteAttr(Instr: &Value, index: c_uint, bytes: u64);
|
||||
pub fn LLVMRustAddDereferenceableOrNullCallSiteAttr(Instr: &Value, index: c_uint, bytes: u64);
|
||||
|
@ -43,6 +43,10 @@ pub fn AddFunctionAttrString(llfn: &'a Value, idx: AttributePlace, attr: &CStr)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn AddCallSiteAttrString(callsite: &Value, idx: AttributePlace, attr: &CStr) {
|
||||
unsafe { LLVMRustAddCallSiteAttrString(callsite, idx.as_uint(), attr.as_ptr()) }
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum AttributePlace {
|
||||
ReturnValue,
|
||||
|
@ -465,6 +465,7 @@ E0777: include_str!("./error_codes/E0777.md"),
|
||||
E0778: include_str!("./error_codes/E0778.md"),
|
||||
E0779: include_str!("./error_codes/E0779.md"),
|
||||
E0780: include_str!("./error_codes/E0780.md"),
|
||||
E0781: include_str!("./error_codes/E0781.md"),
|
||||
;
|
||||
// E0006, // merged with E0005
|
||||
// E0008, // cannot bind by-move into a pattern guard
|
||||
|
12
compiler/rustc_error_codes/src/error_codes/E0781.md
Normal file
12
compiler/rustc_error_codes/src/error_codes/E0781.md
Normal file
@ -0,0 +1,12 @@
|
||||
The `C-cmse-nonsecure-call` ABI can only be used with function pointers.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0781
|
||||
#![feature(abi_c_cmse_nonsecure_call)]
|
||||
|
||||
pub extern "C-cmse-nonsecure-call" fn test() {}
|
||||
```
|
||||
|
||||
The `C-cmse-nonsecure-call` ABI should be used by casting function pointers to
|
||||
specific addresses.
|
@ -628,6 +628,9 @@ declare_features! (
|
||||
|
||||
/// Allows using `pointer` and `reference` in intra-doc links
|
||||
(active, intra_doc_pointers, "1.51.0", Some(80896), None),
|
||||
|
||||
/// Allows `extern "C-cmse-nonsecure-call" fn()`.
|
||||
(active, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391), None),
|
||||
// -------------------------------------------------------------------------
|
||||
// feature-group-end: actual feature gates
|
||||
// -------------------------------------------------------------------------
|
||||
|
@ -216,6 +216,14 @@ extern "C" void LLVMRustAddCallSiteAttribute(LLVMValueRef Instr, unsigned Index,
|
||||
Call->addAttribute(Index, Attr);
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustAddCallSiteAttrString(LLVMValueRef Instr, unsigned Index,
|
||||
const char *Name) {
|
||||
CallBase *Call = unwrap<CallBase>(Instr);
|
||||
Attribute Attr = Attribute::get(Call->getContext(), Name);
|
||||
Call->addAttribute(Index, Attr);
|
||||
}
|
||||
|
||||
|
||||
extern "C" void LLVMRustAddAlignmentCallSiteAttr(LLVMValueRef Instr,
|
||||
unsigned Index,
|
||||
uint32_t Bytes) {
|
||||
|
@ -2650,6 +2650,7 @@ where
|
||||
Win64 => Conv::X86_64Win64,
|
||||
SysV64 => Conv::X86_64SysV,
|
||||
Aapcs => Conv::ArmAapcs,
|
||||
CCmseNonSecureCall => Conv::CCmseNonSecureCall,
|
||||
PtxKernel => Conv::PtxKernel,
|
||||
Msp430Interrupt => Conv::Msp430Intr,
|
||||
X86Interrupt => Conv::X86Intr,
|
||||
|
@ -218,6 +218,7 @@ symbols! {
|
||||
abi,
|
||||
abi_amdgpu_kernel,
|
||||
abi_avr_interrupt,
|
||||
abi_c_cmse_nonsecure_call,
|
||||
abi_efiapi,
|
||||
abi_msp430_interrupt,
|
||||
abi_ptx,
|
||||
|
@ -551,6 +551,7 @@ pub enum Conv {
|
||||
|
||||
// Target-specific calling conventions.
|
||||
ArmAapcs,
|
||||
CCmseNonSecureCall,
|
||||
|
||||
Msp430Intr,
|
||||
|
||||
|
@ -36,6 +36,7 @@ pub enum Abi {
|
||||
EfiApi,
|
||||
AvrInterrupt,
|
||||
AvrNonBlockingInterrupt,
|
||||
CCmseNonSecureCall,
|
||||
|
||||
// Multiplatform / generic ABIs
|
||||
System,
|
||||
@ -81,6 +82,7 @@ const AbiDatas: &[AbiData] = &[
|
||||
name: "avr-non-blocking-interrupt",
|
||||
generic: false,
|
||||
},
|
||||
AbiData { abi: Abi::CCmseNonSecureCall, name: "C-cmse-nonsecure-call", generic: false },
|
||||
// Cross-platform ABIs
|
||||
AbiData { abi: Abi::System, name: "system", generic: true },
|
||||
AbiData { abi: Abi::RustIntrinsic, name: "rust-intrinsic", generic: true },
|
||||
|
@ -42,6 +42,17 @@ pub(super) fn check_abi(tcx: TyCtxt<'_>, span: Span, abi: Abi) {
|
||||
)
|
||||
.emit()
|
||||
}
|
||||
|
||||
// This ABI is only allowed on function pointers
|
||||
if abi == Abi::CCmseNonSecureCall {
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
span,
|
||||
E0781,
|
||||
"the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers."
|
||||
)
|
||||
.emit()
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper used for fns and closures. Does the grungy work of checking a function
|
||||
|
@ -0,0 +1,88 @@
|
||||
# `abi_c_cmse_nonsecure_call`
|
||||
|
||||
The tracking issue for this feature is: [#81391]
|
||||
|
||||
[#81391]: https://github.com/rust-lang/rust/issues/81391
|
||||
|
||||
------------------------
|
||||
|
||||
The [TrustZone-M
|
||||
feature](https://developer.arm.com/documentation/100690/latest/) 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](https://developer.arm.com/documentation/ecm0359818/latest/) 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](https://developer.arm.com/documentation/ecm0359818/latest/) for details).
|
||||
|
||||
With this ABI, the compiler will do the following to perform the call:
|
||||
* save registers needed after the call to Secure memory
|
||||
* clear all registers that might contain confidential information
|
||||
* clear the Least Significant Bit of the function address
|
||||
* branches using the BLXNS instruction
|
||||
|
||||
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.
|
||||
|
||||
<!-- NOTE(ignore) this example is specific to thumbv8m targets -->
|
||||
|
||||
``` rust,ignore
|
||||
#![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()
|
||||
}
|
||||
```
|
||||
|
||||
``` text
|
||||
$ 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}
|
||||
```
|
11
src/test/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.rs
Normal file
11
src/test/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.rs
Normal file
@ -0,0 +1,11 @@
|
||||
// gate-test-abi_c_cmse_nonsecure_call
|
||||
fn main() {
|
||||
let non_secure_function = unsafe {
|
||||
core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn(i32, i32, i32, i32) -> i32>(
|
||||
//~^ ERROR [E0658]
|
||||
0x10000004,
|
||||
)
|
||||
};
|
||||
let mut toto = 5;
|
||||
toto += non_secure_function(toto, 2, 3, 5);
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
error[E0658]: C-cmse-nonsecure-call ABI is experimental and subject to change
|
||||
--> $DIR/gate_test.rs:4:46
|
||||
|
|
||||
LL | core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn(i32, i32, i32, i32) -> i32>(
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #81391 <https://github.com/rust-lang/rust/issues/81391> for more information
|
||||
= help: add `#![feature(abi_c_cmse_nonsecure_call)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
@ -0,0 +1,15 @@
|
||||
// build-pass
|
||||
// compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
|
||||
// only-thumbv8m.main-none-eabi
|
||||
#![feature(abi_c_cmse_nonsecure_call)]
|
||||
#![no_std]
|
||||
|
||||
#[no_mangle]
|
||||
pub fn test(a: u32, b: u32, c: u32, d: u32) -> u32 {
|
||||
let non_secure_function = unsafe {
|
||||
core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32) -> u32>(
|
||||
0x10000004,
|
||||
)
|
||||
};
|
||||
non_secure_function(a, b, c, d)
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
// compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
|
||||
// only-thumbv8m.main-none-eabi
|
||||
#![feature(abi_c_cmse_nonsecure_call)]
|
||||
#![no_std]
|
||||
|
||||
#[no_mangle]
|
||||
pub fn test(a: u32, b: u32, c: u32, d: u32, e: u32) -> u32 {
|
||||
let non_secure_function = unsafe {
|
||||
core::mem::transmute::<
|
||||
usize,
|
||||
extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32>
|
||||
(
|
||||
0x10000004,
|
||||
)
|
||||
};
|
||||
non_secure_function(a, b, c, d, e)
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
error: <unknown>:0:0: in function test i32 (i32, i32, i32, i32, i32): call to non-secure function would require passing arguments on stack
|
||||
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,6 @@
|
||||
// compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
|
||||
// only-thumbv8m.main-none-eabi
|
||||
#![feature(abi_c_cmse_nonsecure_call)]
|
||||
#![no_std]
|
||||
|
||||
pub extern "C-cmse-nonsecure-call" fn test() {} //~ ERROR [E0781]
|
@ -0,0 +1,9 @@
|
||||
error[E0781]: the `"cmse-nonsecure-call"` ABI is only allowed on function pointers.
|
||||
--> $DIR/wrong-abi-location-1.rs:6:1
|
||||
|
|
||||
LL | pub extern "C-cmse-nonsecure-call" fn test() {} //~ ERROR [E0781]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0781`.
|
@ -0,0 +1,8 @@
|
||||
// compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
|
||||
// only-thumbv8m.main-none-eabi
|
||||
#![feature(abi_c_cmse_nonsecure_call)]
|
||||
#![no_std]
|
||||
|
||||
extern "C-cmse-nonsecure-call" { //~ ERROR [E0781]
|
||||
fn test();
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
error[E0781]: the `"C-cmse-nonsecure-call"` ABI is only allowed on function pointers.
|
||||
--> $DIR/wrong-abi-location-2.rs:6:1
|
||||
|
|
||||
LL | / extern "C-cmse-nonsecure-call" {
|
||||
LL | | fn test(); //~ ERROR [E0781]
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0781`.
|
@ -4,7 +4,7 @@ error[E0703]: invalid ABI: found `路濫狼á́́`
|
||||
LL | extern "路濫狼á́́" fn foo() {}
|
||||
| ^^^^^^^^^ invalid ABI
|
||||
|
|
||||
= help: valid ABIs: Rust, C, cdecl, stdcall, fastcall, vectorcall, thiscall, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, system, rust-intrinsic, rust-call, platform-intrinsic, unadjusted
|
||||
= help: valid ABIs: Rust, C, cdecl, stdcall, fastcall, vectorcall, thiscall, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, system, rust-intrinsic, rust-call, platform-intrinsic, unadjusted
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -4,7 +4,7 @@ error[E0703]: invalid ABI: found `invalid-ab_isize`
|
||||
LL | "invalid-ab_isize"
|
||||
| ^^^^^^^^^^^^^^^^^^ invalid ABI
|
||||
|
|
||||
= help: valid ABIs: Rust, C, cdecl, stdcall, fastcall, vectorcall, thiscall, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, system, rust-intrinsic, rust-call, platform-intrinsic, unadjusted
|
||||
= help: valid ABIs: Rust, C, cdecl, stdcall, fastcall, vectorcall, thiscall, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, system, rust-intrinsic, rust-call, platform-intrinsic, unadjusted
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user