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:
Hugues de Valon 2021-01-24 17:15:05 +00:00
parent d60b29d1ae
commit ce9818f2b7
35 changed files with 251 additions and 3 deletions

View File

@ -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

View File

@ -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

View File

@ -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"),
);
}
}
}
}

View File

@ -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);

View File

@ -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,

View File

@ -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

View 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.

View File

@ -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
// -------------------------------------------------------------------------

View File

@ -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) {

View File

@ -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,

View File

@ -218,6 +218,7 @@ symbols! {
abi,
abi_amdgpu_kernel,
abi_avr_interrupt,
abi_c_cmse_nonsecure_call,
abi_efiapi,
abi_msp430_interrupt,
abi_ptx,

View File

@ -551,6 +551,7 @@ pub enum Conv {
// Target-specific calling conventions.
ArmAapcs,
CCmseNonSecureCall,
Msp430Intr,

View File

@ -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 },

View File

@ -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

View File

@ -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}
```

View 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);
}

View File

@ -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`.

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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

View File

@ -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]

View File

@ -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`.

View File

@ -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();
}

View File

@ -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`.

View File

@ -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

View File

@ -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