diff --git a/Cargo.lock b/Cargo.lock index 440a898c9..83ee332d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -669,6 +669,7 @@ dependencies = [ name = "task-idle" version = "0.1.0" dependencies = [ + "cortex-m", "userlib", ] diff --git a/demo/src/main.rs b/demo/src/main.rs index 12176db64..4a4b81cfc 100644 --- a/demo/src/main.rs +++ b/demo/src/main.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(llvm_asm)] #[cfg(not(any(feature = "panic-itm", feature = "panic-semihosting")))] compile_error!( diff --git a/kern/src/arch/arm_m.rs b/kern/src/arch/arm_m.rs index f44080d47..aa5db487c 100644 --- a/kern/src/arch/arm_m.rs +++ b/kern/src/arch/arm_m.rs @@ -567,19 +567,16 @@ pub fn start_first_task(task: &task::Task) -> ! { } unsafe { - llvm_asm! { " - msr PSP, $0 @ set the user stack pointer - ldm $1, {r4-r11} @ restore the callee-save registers + asm!(" + msr PSP, {user_sp} @ set the user stack pointer + ldm {task}, {{r4-r11}} @ restore the callee-save registers svc #0xFF @ branch into user mode (svc # ignored) udf #0xad @ should not return - " - : - : "r"(task.save.psp), - "r"(&task.save.r4) - : "memory" - : "volatile" - } - core::hint::unreachable_unchecked() + ", + user_sp = in(reg) task.save.psp, + task = in(reg) &task.save.r4, + options(noreturn), + ) } } @@ -593,7 +590,7 @@ pub unsafe extern "C" fn SVCall() { // of instructions below, though the precise details depend on how complex // of an M-series processor you're targeting -- so I've punted on this for // the time being. - llvm_asm! {" + asm!(" cmp lr, #0xFFFFFFF9 @ is it coming from inside the kernel? beq 1f @ if so, we're starting the first task; @ jump ahead. @@ -608,7 +605,7 @@ pub unsafe extern "C" fn SVCall() { @ fetching into r12 means the order in the stm below is right. mrs r12, PSP @ now, store volatile registers, plus the PSP in r12, plus LR. - stm r1, {r4-r12, lr} + stm r1, {{r4-r12, lr}} @ syscall number is passed in r11. Move it into r0 to pass it as an @ argument to the handler, then call the handler. @@ -620,7 +617,7 @@ pub unsafe extern "C" fn SVCall() { movt r0, #:upper16:CURRENT_TASK_PTR ldr r0, [r0] @ restore volatile registers, plus load PSP into r12 - ldm r0, {r4-r12, lr} + ldm r0, {{r4-r12, lr}} msr PSP, r12 @ resume @@ -636,12 +633,9 @@ pub unsafe extern "C" fn SVCall() { @ return into thread mode, PSP, FP on bx lr @ branch into user mode - " - : - : - : - : "volatile" - } + ", + options(noreturn), + ) } /// Manufacture a mutable/exclusive reference to the task table from thin air @@ -745,7 +739,7 @@ fn pend_context_switch_from_isr() { #[naked] #[no_mangle] pub unsafe extern "C" fn PendSV() { - llvm_asm! {" + asm!(" @ store volatile state. @ first, get a pointer to the current task. movw r0, #:lower16:CURRENT_TASK_PTR @@ -755,7 +749,7 @@ pub unsafe extern "C" fn PendSV() { @ fetching into r12 means the order in the stm below is right. mrs r12, PSP @ now, store volatile registers, plus the PSP in r12, plus LR. - stm r1, {r4-r12, lr} + stm r1, {{r4-r12, lr}} @ syscall number is passed in r11. Move it into r0 to pass it as an @ argument to the handler, then call the handler. @@ -766,17 +760,14 @@ pub unsafe extern "C" fn PendSV() { movt r0, #:upper16:CURRENT_TASK_PTR ldr r0, [r0] @ restore volatile registers, plus load PSP into r12 - ldm r0, {r4-r12, lr} + ldm r0, {{r4-r12, lr}} msr PSP, r12 @ resume bx lr - " - : - : - : - : "volatile" - } + ", + options(noreturn), + ); } /// The Rust side of the PendSV handler, after all volatile registers have been @@ -803,10 +794,11 @@ pub unsafe extern "C" fn DefaultHandler() { // We can cheaply get the identity of the interrupt that called us from the // bottom 9 bits of IPSR. let mut ipsr: u32; - llvm_asm! { - "mrs $0, IPSR" - : "=r"(ipsr) - } + asm!( + "mrs {}, IPSR", + out(reg) ipsr, + options(pure, nomem, preserves_flags, nostack), + ); let exception_num = ipsr & 0x1FF; // The first 16 exceptions are architecturally defined; vendor hardware @@ -882,7 +874,7 @@ pub fn enable_irq(n: u32) { #[no_mangle] #[naked] pub unsafe extern "C" fn MemoryManagement() { - llvm_asm! { " + asm!(" @ Get the exc_return value into an argument register, which is @ difficult to do from higher-level code. mov r0, lr @@ -891,9 +883,9 @@ pub unsafe extern "C" fn MemoryManagement() { movt r1, #:upper16:CURRENT_TASK_PTR ldr r1, [r1] b mem_manage_fault - " - ::::"volatile" - } + ", + options(noreturn), + ); } bitflags::bitflags! { diff --git a/kern/src/lib.rs b/kern/src/lib.rs index dedef8e71..15dfb9134 100644 --- a/kern/src/lib.rs +++ b/kern/src/lib.rs @@ -23,7 +23,7 @@ //! most clever algorithms used in kernels wind up requiring `unsafe`.) #![cfg_attr(target_os = "none", no_std)] -#![feature(llvm_asm)] +#![feature(asm)] #![feature(naked_functions)] #[macro_use] diff --git a/lpc55/src/main.rs b/lpc55/src/main.rs index e00436bfd..75be1f2d9 100644 --- a/lpc55/src/main.rs +++ b/lpc55/src/main.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(llvm_asm)] #[cfg(not(any(feature = "panic-itm", feature = "panic-semihosting")))] compile_error!( diff --git a/rust-toolchain b/rust-toolchain index 1bc243968..4aec5c6b4 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2020-05-01 +nightly-2020-06-10 diff --git a/task-idle/Cargo.toml b/task-idle/Cargo.toml index 72bcf5776..408b7c4cc 100644 --- a/task-idle/Cargo.toml +++ b/task-idle/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" # suppress default features to avoid including panic message collection, to # ensure the idle task remains tiny. userlib = {path = "../userlib", default-features = false} +cortex-m = {version = "0.6", features = ["inline-asm"]} [features] default = ["standalone"] diff --git a/task-idle/src/main.rs b/task-idle/src/main.rs index e1f38bc1e..e9ee90a79 100644 --- a/task-idle/src/main.rs +++ b/task-idle/src/main.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(llvm_asm)] // Make sure we actually link in userlib, despite not using any of it explicitly // - we need it for our _start routine. @@ -9,11 +8,8 @@ extern crate userlib; #[export_name = "main"] fn main() -> ! { loop { - // Safety: asm in general is unsafe, but this instruction is fine. - unsafe { - // Wait For Interrupt to pause the processor until an ISR arrives, - // which could wake some higher-priority task. - llvm_asm!("wfi"::::"volatile"); - } + // Wait For Interrupt to pause the processor until an ISR arrives, + // which could wake some higher-priority task. + cortex_m::asm::wfi(); } } diff --git a/task-ping/src/main.rs b/task-ping/src/main.rs index 0c34677e1..2af13713b 100644 --- a/task-ping/src/main.rs +++ b/task-ping/src/main.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(llvm_asm)] use userlib::*; diff --git a/task-spam2/src/main.rs b/task-spam2/src/main.rs index 6887ad742..68a9d735a 100644 --- a/task-spam2/src/main.rs +++ b/task-spam2/src/main.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(llvm_asm)] // Make sure we actually link in userlib, despite not using any of it explicitly // - we need it for our _start routine. diff --git a/userlib/src/lib.rs b/userlib/src/lib.rs index 3f4491690..1109c5611 100644 --- a/userlib/src/lib.rs +++ b/userlib/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![feature(llvm_asm)] +#![feature(asm)] pub use abi::*; pub use num_derive::{FromPrimitive, ToPrimitive}; @@ -66,21 +66,25 @@ pub fn sys_send( let mut response_code: u32; let mut response_len: usize; unsafe { - llvm_asm! { - "svc #0" - : "={r4}"(response_code), - "={r5}"(response_len) - : "{r4}"(u32::from(target.0) << 16 | u32::from(operation)), - "{r5}"(outgoing.as_ptr()), - "{r6}"(outgoing.len()), - "{r7}"(incoming.as_mut_ptr()), - "{r8}"(incoming.len()), - "{r9}"(leases.as_ptr()), - "{r10}"(leases.len()), - "{r11}"(Sysnum::Send) - : "memory" // TODO probably too conservative? - : "volatile" - } + asm!(" + mov {save11}, r11 + mov r11, {sysnum} + svc #0 + mov r11, {save11} + ", + save11 = out(reg) _, + sysnum = const Sysnum::Send as u32, + + inlateout("r4") u32::from(target.0) << 16 | u32::from(operation) => response_code, + inlateout("r5") outgoing.as_ptr() => response_len, + in("r6") outgoing.len(), + in("r7") incoming.as_mut_ptr(), + in("r8") incoming.len(), + in("r9") leases.as_ptr(), + in("r10") leases.len(), + + options(preserves_flags, nostack), + ); } (response_code, response_len) } @@ -93,20 +97,24 @@ pub fn sys_recv(buffer: &mut [u8], notification_mask: u32) -> RecvMessage { let mut lease_count: usize; unsafe { - llvm_asm! { - "svc #0" - : "={r5}"(sender), - "={r6}"(operation), - "={r7}"(message_len), - "={r8}"(response_capacity), - "={r9}"(lease_count) - : "{r4}"(buffer.as_mut_ptr()), - "{r5}"(buffer.len()), - "{r6}"(notification_mask), - "{r11}"(Sysnum::Recv) - : "r4", "memory" // TODO probably too conservative? - : "volatile" - } + asm!(" + mov {save11}, r11 + mov r11, {sysnum} + svc #0 + mov r11, {save11} + ", + save11 = out(reg) _, + sysnum = const Sysnum::Recv as u32, + + inlateout("r4") buffer.as_mut_ptr() => _, + inlateout("r5") buffer.len() => sender, + inlateout("r6") notification_mask => operation, + lateout("r7") message_len, + lateout("r8") response_capacity, + lateout("r9") lease_count, + + options(preserves_flags, nostack), + ); } RecvMessage { @@ -128,34 +136,49 @@ pub struct RecvMessage { pub fn sys_reply(peer: TaskId, code: u32, message: &[u8]) { unsafe { - llvm_asm! { - "svc #0" - : - : "{r4}"(peer.0 as u32), - "{r5}"(code), - "{r6}"(message.as_ptr()), - "{r7}"(message.len()), - "{r11}"(Sysnum::Reply) - : "r4", "r5" // reserved - : "volatile" - } + asm!(" + mov {save11}, r11 + mov r11, {sysnum} + svc #0 + mov r11, {save11} + ", + save11 = out(reg) _, + sysnum = const Sysnum::Reply as u32, + + // While r4/r5 are not useful outputs at this time, they are + // reserved as clobbered in case we change that. + inlateout("r4") peer.0 as u32 => _, + inlateout("r5") code => _, + in("r6") message.as_ptr(), + in("r7") message.len(), + + // This is NOT readonly because no kernel mechanism prevents this + // task and the caller task from sharing memory, including the + // message buffer! + options(preserves_flags, nostack), + ); } } pub fn sys_set_timer(deadline: Option, notifications: u32) { let raw_deadline = deadline.unwrap_or(0); unsafe { - llvm_asm! { - "svc #0" - : - : "{r4}"(deadline.is_some() as u32), - "{r5}"(raw_deadline as u32), - "{r6}"((raw_deadline >> 32) as u32), - "{r7}"(notifications), - "{r11}"(Sysnum::Timer) - : - : "volatile" - } + asm!(" + mov {save11}, r11 + mov r11, {sysnum} + svc #0 + mov r11, {save11} + ", + save11 = out(reg) _, + sysnum = const Sysnum::Timer as u32, + + in("r4") deadline.is_some() as u32, + in("r5") raw_deadline as u32, + in("r6") (raw_deadline >> 32) as u32, + in("r7") notifications, + + options(nomem, preserves_flags, nostack), + ); } } @@ -168,19 +191,23 @@ pub fn sys_borrow_read( let mut rc: u32; let mut length: usize; unsafe { - llvm_asm! { - "svc #0" - : "={r4}"(rc), - "={r5}"(length) - : "{r4}"(lender.0 as u32), - "{r5}"(index as u32), - "{r6}"(offset as u32), - "{r7}"(dest.as_mut_ptr()), - "{r8}"(dest.len()), - "{r11}"(Sysnum::BorrowRead) - : "memory" - : "volatile" - } + asm!(" + mov {save11}, r11 + mov r11, {sysnum} + svc #0 + mov r11, {save11} + ", + save11 = out(reg) _, + sysnum = const Sysnum::BorrowRead as u32, + + inlateout("r4") lender.0 as u32 => rc, + inlateout("r5") index => length, + in("r6") offset, + in("r7") dest.as_mut_ptr(), + in("r8") dest.len(), + + options(readonly, preserves_flags, nostack), + ); } (rc, length) } @@ -189,24 +216,31 @@ pub fn sys_borrow_write( lender: TaskId, index: usize, offset: usize, - dest: &[u8], + src: &[u8], ) -> (u32, usize) { let mut rc: u32; let mut length: usize; unsafe { - llvm_asm! { - "svc #0" - : "={r4}"(rc), - "={r5}"(length) - : "{r4}"(lender.0 as u32), - "{r5}"(index as u32), - "{r6}"(offset as u32), - "{r7}"(dest.as_ptr()), - "{r8}"(dest.len()), - "{r11}"(Sysnum::BorrowWrite) - : "memory" - : "volatile" - } + asm!(" + mov {save11}, r11 + mov r11, {sysnum} + svc #0 + mov r11, {save11} + ", + save11 = out(reg) _, + sysnum = const Sysnum::BorrowWrite as u32, + + inlateout("r4") lender.0 as u32 => rc, + inlateout("r5") index => length, + in("r6") offset, + in("r7") src.as_ptr(), + in("r8") src.len(), + + // This is NOT readonly because no kernel mechanism prevents this + // task and the caller task from sharing memory, including the + // message buffer! + options(preserves_flags, nostack), + ); } (rc, length) } @@ -216,48 +250,60 @@ pub fn sys_borrow_info(lender: TaskId, index: usize) -> (u32, u32, usize) { let mut atts: u32; let mut length: usize; unsafe { - llvm_asm! { - "svc #0" - : "={r4}"(rc), - "={r5}"(atts), - "={r6}"(length) - : "{r4}"(lender.0 as u32), - "{r5}"(index as u32), - "{r11}"(Sysnum::BorrowInfo) - : - : "volatile" - } + asm!(" + mov {save11}, r11 + mov r11, {sysnum} + svc #0 + mov r11, {save11} + ", + save11 = out(reg) _, + sysnum = const Sysnum::BorrowInfo as u32, + + inlateout("r4") lender.0 as u32 => rc, + inlateout("r5") index => atts, + lateout("r6") length, + + options(nomem, preserves_flags, nostack), + ); } (rc, atts, length) } pub fn sys_irq_control(mask: u32, enable: bool) { unsafe { - llvm_asm! { - "svc #0" - : - : "{r4}"(mask), - "{r5}"(enable as u32), - "{r11}"(Sysnum::IrqControl) - : "r4", "r5" - : "volatile" - } + asm!(" + mov {save11}, r11 + mov r11, {sysnum} + svc #0 + mov r11, {save11} + ", + save11 = out(reg) _, + sysnum = const Sysnum::IrqControl as u32, + + // Though r4/r5 don't have useful outputs right now, we're reserving + // them in case that changes. + inlateout("r4") mask => _, + inlateout("r5") enable as u32 => _, + + options(nomem, preserves_flags, nostack), + ); } } pub fn sys_panic(msg: &[u8]) -> ! { unsafe { - llvm_asm! { - "svc #0 - udf #0xad" - : - : "{r4}"(msg.as_ptr()), - "{r5}"(msg.len()), - "{r11}"(Sysnum::Panic) - : - : "volatile" - } - core::hint::unreachable_unchecked() + asm!(" + mov r6, r11 + mov r11, {sysnum} + svc #0 + udf #0xad + ", + sysnum = const Sysnum::Panic as u32, + + in("r4") msg.as_ptr(), + in("r5") msg.len(), + options(nomem, noreturn, nostack), + ) } }