From 37219c527e1cd606d2e032014c3322b3e3d1aace Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sat, 7 Feb 2026 19:26:13 +0000 Subject: [PATCH 1/4] Provide r0-r5 to SVC exception handler, for implementing syscalls. This involves stacking the registers in the opposite order, so that r0-r5 are stacked at the lowest addresses (and that any stack alignment padding is stored above them). We don't pass r6 and r7 because they may be in use (e.g. r7 is the Frame Pointer on Thumb) and it isn't worth the cost of the push/mov/pop we need to use them. I also took the opportunity to move the integer register push/pop code into the asm routines. This meant we could avoid stacking a couple of registers and speed up the routines. It also meant it was a bit easier to ensure the frame was stacked after the AAPCS stack padding and hence in a known location. This commit also add svcX! macros for passing arguments to a syscall. They have to be macros because the syscall number goes into the instruction as a literal and cannot come from a function call. --- aarch32-cpu/src/lib.rs | 204 +++++++++++++++++- aarch32-rt-macros/src/lib.rs | 11 +- aarch32-rt/src/arch_v4/abort.rs | 83 +++---- aarch32-rt/src/arch_v4/interrupt.rs | 43 ++-- aarch32-rt/src/arch_v4/svc.rs | 43 ++-- aarch32-rt/src/arch_v4/undefined.rs | 51 ++--- aarch32-rt/src/arch_v7/abort.rs | 76 ++++--- aarch32-rt/src/arch_v7/interrupt.rs | 41 ++-- aarch32-rt/src/arch_v7/svc.rs | 51 ++--- aarch32-rt/src/arch_v7/undefined.rs | 57 ++--- aarch32-rt/src/lib.rs | 132 +++++------- .../reference/syscall-armv8r-none-eabihf.out | 3 + .../syscall-thumbv8r-none-eabihf.out | 3 + examples/mps3-an536/src/bin/svc-a32.rs | 3 +- examples/mps3-an536/src/bin/svc-t32.rs | 3 +- examples/mps3-an536/src/bin/syscall.rs | 107 +++++++++ .../reference/syscall-armv4t-none-eabi.out | 3 + .../reference/syscall-armv5te-none-eabi.out | 3 + .../reference/syscall-armv6-none-eabi.out | 3 + .../reference/syscall-armv6-none-eabihf.out | 3 + .../reference/syscall-armv7a-none-eabi.out | 3 + .../reference/syscall-armv7a-none-eabihf.out | 3 + .../reference/syscall-armv7r-none-eabi.out | 3 + .../reference/syscall-armv7r-none-eabihf.out | 3 + .../reference/syscall-thumbv4t-none-eabi.out | 3 + .../reference/syscall-thumbv5te-none-eabi.out | 3 + .../reference/syscall-thumbv7a-none-eabi.out | 3 + .../syscall-thumbv7a-none-eabihf.out | 3 + .../reference/syscall-thumbv7r-none-eabi.out | 3 + .../syscall-thumbv7r-none-eabihf.out | 3 + examples/versatileab/src/bin/svc-a32.rs | 3 +- examples/versatileab/src/bin/svc-t32.rs | 3 +- examples/versatileab/src/bin/syscall.rs | 107 +++++++++ 33 files changed, 752 insertions(+), 314 deletions(-) create mode 100644 examples/mps3-an536/reference/syscall-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536/reference/syscall-thumbv8r-none-eabihf.out create mode 100644 examples/mps3-an536/src/bin/syscall.rs create mode 100644 examples/versatileab/reference/syscall-armv4t-none-eabi.out create mode 100644 examples/versatileab/reference/syscall-armv5te-none-eabi.out create mode 100644 examples/versatileab/reference/syscall-armv6-none-eabi.out create mode 100644 examples/versatileab/reference/syscall-armv6-none-eabihf.out create mode 100644 examples/versatileab/reference/syscall-armv7a-none-eabi.out create mode 100644 examples/versatileab/reference/syscall-armv7a-none-eabihf.out create mode 100644 examples/versatileab/reference/syscall-armv7r-none-eabi.out create mode 100644 examples/versatileab/reference/syscall-armv7r-none-eabihf.out create mode 100644 examples/versatileab/reference/syscall-thumbv4t-none-eabi.out create mode 100644 examples/versatileab/reference/syscall-thumbv5te-none-eabi.out create mode 100644 examples/versatileab/reference/syscall-thumbv7a-none-eabi.out create mode 100644 examples/versatileab/reference/syscall-thumbv7a-none-eabihf.out create mode 100644 examples/versatileab/reference/syscall-thumbv7r-none-eabi.out create mode 100644 examples/versatileab/reference/syscall-thumbv7r-none-eabihf.out create mode 100644 examples/versatileab/src/bin/syscall.rs diff --git a/aarch32-cpu/src/lib.rs b/aarch32-cpu/src/lib.rs index 052a2d66..615726c9 100644 --- a/aarch32-cpu/src/lib.rs +++ b/aarch32-cpu/src/lib.rs @@ -37,15 +37,207 @@ pub mod generic_timer; #[cfg(any(test, doc, arm_architecture = "v8-r"))] pub mod pmsav8; -/// Generate an SVC call with the given argument. +/// Generate an SVC call with no parameters. /// -/// Safe to call even in Supervisor (SupervisorCall) mode, as long as your Svc handler -/// saves and restores SPSR_svc correctly. +/// Puts the first argument in the instruction. Gives you back +/// the value left in `r0` by the handler. +/// +/// ```rust,ignore +/// let value = svc!(0xFF); +/// ``` #[macro_export] macro_rules! svc { - ($r0:expr) => { + ($num:expr) => { { + let retval: u32; + unsafe { + core::arch::asm!("svc {arg}", arg = const $num, lateout("r0") retval, out("lr") _); + } + retval + } } +} + +/// Generate an SVC call with 1 parameters +/// +/// Puts the first argument in the instruction, and the parameter in r0. Gives you back +/// the value left in `r0` by the handler. +/// +/// ```rust,ignore +/// const SYSCALL_FOO: u32 = 0x100; +/// let result = svc1!(0x00, SYSCALL_FOO); +/// ``` +#[macro_export] +macro_rules! svc1 { + ($num:expr, $arg0:expr) => { { + let retval: u32; + let arg0: u32 = $arg0; + unsafe { + core::arch::asm!( + // Do the SVCall + "svc {arg}", + arg = const $num, + inout("r0") arg0 => retval, + out("lr") _); + } + retval + } } +} + +/// Generate an SVC call with 2 parameters +/// +/// Puts the first argument in the instruction, and the parameters in r0-r1. Gives you back +/// the value left in `r0` by the handler. +/// +/// ```rust,ignore +/// const SYSCALL_FOO: u32 = 0x100; +/// let result = svc2!(0x00, SYSCALL_FOO, 1); +/// ``` +#[macro_export] +macro_rules! svc2 { + ($num:expr, $arg0:expr, $arg1:expr) => { { + let retval: u32; + let arg0: u32 = $arg0; + let arg1: u32 = $arg1; + unsafe { + core::arch::asm!( + // Do the SVCall + "svc {arg}", + arg = const $num, + inout("r0") arg0 => retval, + in("r1") arg1, + out("lr") _); + } + retval + } } +} + +/// Generate an SVC call with 3 parameters +/// +/// Puts the first argument in the instruction, and the parameters in r0-r2. Gives you back +/// the value left in `r0` by the handler. +/// +/// ```rust,ignore +/// const SYSCALL_FOO: u32 = 0x100; +/// let result = svc3!(0x00, SYSCALL_FOO, 1, 2); +/// ``` +#[macro_export] +macro_rules! svc3 { + ($num:expr, $arg0:expr, $arg1:expr, $arg2:expr) => { { + let retval: u32; + let arg0: u32 = $arg0; + let arg1: u32 = $arg1; + let arg2: u32 = $arg2; + unsafe { + core::arch::asm!( + // Do the SVCall + "svc {arg}", + arg = const $num, + inout("r0") arg0 => retval, + in("r1") arg1, + in("r2") arg2, + out("lr") _); + } + retval + } } +} + +/// Generate an SVC call with 4 parameters +/// +/// Puts the first argument in the instruction, and the parameters in r0-r3. Gives you back +/// the value left in `r0` by the handler. +/// +/// ```rust,ignore +/// const SYSCALL_FOO: u32 = 0x100; +/// let result = svc4!(0x00, SYSCALL_FOO, 1, 2, 3); +/// ``` +#[macro_export] +macro_rules! svc4 { + ($num:expr, $arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr) => { { + let retval: u32; + let arg0: u32 = $arg0; + let arg1: u32 = $arg1; + let arg2: u32 = $arg2; + let arg3: u32 = $arg3; + unsafe { + core::arch::asm!( + // Do the SVCall + "svc {arg}", + arg = const $num, + inout("r0") arg0 => retval, + in("r1") arg1, + in("r2") arg2, + in("r3") arg3, + out("lr") _); + } + retval + } } +} + +/// Generate an SVC call with 5 parameters +/// +/// Puts the first argument in the instruction, and the parameters in r0-r4. Gives you back +/// the value left in `r0` by the handler. +/// +/// ```rust,ignore +/// const SYSCALL_FOO: u32 = 0x100; +/// let result = svc5!(0x00, SYSCALL_FOO, 1, 2, 3, 4); +/// ``` +#[macro_export] +macro_rules! svc5 { + ($num:expr, $arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr) => { { + let retval: u32; + let arg0: u32 = $arg0; + let arg1: u32 = $arg1; + let arg2: u32 = $arg2; + let arg3: u32 = $arg3; + let arg4: u32 = $arg4; + unsafe { + core::arch::asm!( + // Do the SVCall + "svc {arg}", + arg = const $num, + inout("r0") arg0 => retval, + in("r1") arg1, + in("r2") arg2, + in("r3") arg3, + in("r4") arg4, + out("lr") _); + } + retval + } } +} + +/// Generate an SVC call with 6 parameters +/// +/// Puts the first argument in the instruction, and the parameters in r0-r5. Gives you back +/// the value left in `r0` by the handler. +/// +/// ```rust,ignore +/// const SYSCALL_FOO: u32 = 0x100; +/// let result = svc6!(0x00, SYSCALL_FOO, 1, 2, 3, 4, 5); +/// ``` +#[macro_export] +macro_rules! svc6 { + ($num:expr, $arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr) => { { + let retval: u32; + let arg0: u32 = $arg0; + let arg1: u32 = $arg1; + let arg2: u32 = $arg2; + let arg3: u32 = $arg3; + let arg4: u32 = $arg4; + let arg5: u32 = $arg5; unsafe { - core::arch::asm!("svc {arg}", arg = const $r0, out("lr") _); + core::arch::asm!( + // Do the SVCall + "svc {arg}", + arg = const $num, + inout("r0") arg0 => retval, + in("r1") arg1, + in("r2") arg2, + in("r3") arg3, + in("r4") arg4, + in("r5") arg5, + out("lr") _); } - } + retval + } } } diff --git a/aarch32-rt-macros/src/lib.rs b/aarch32-rt-macros/src/lib.rs index e3f3f73e..869946e2 100644 --- a/aarch32-rt-macros/src/lib.rs +++ b/aarch32-rt-macros/src/lib.rs @@ -301,7 +301,8 @@ fn handle_vector(args: TokenStream, input: TokenStream, kind: VectorKind) -> Tok VectorKind::Interrupt => Exception::Irq, }; - let block = f.block; + let func_name = f.sig.ident.clone(); + let block = f.block.clone(); let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); let handler = match exception { @@ -387,7 +388,7 @@ fn handle_vector(args: TokenStream, input: TokenStream, kind: VectorKind) -> Tok ) } } - // extern "C" fn _svc_handler(addr: usize); + // extern "C" fn _svc_handler(arg: u32, args: &Frame) -> u32 Exception::SupervisorCall => { let tramp_ident = Ident::new("__aarch32_rt_svc_handler", Span::call_site()); quote!( @@ -395,8 +396,10 @@ fn handle_vector(args: TokenStream, input: TokenStream, kind: VectorKind) -> Tok #(#attrs)* #[doc(hidden)] #[export_name = "_svc_handler"] - pub unsafe extern "C" fn #tramp_ident(arg: u32) { - #block + pub unsafe extern "C" fn #tramp_ident(arg: u32, frame: &aarch32_rt::Frame) -> u32 { + #f + + #func_name(arg, frame) } ) } diff --git a/aarch32-rt/src/arch_v4/abort.rs b/aarch32-rt/src/arch_v4/abort.rs index 793d029a..f740b201 100644 --- a/aarch32-rt/src/arch_v4/abort.rs +++ b/aarch32-rt/src/arch_v4/abort.rs @@ -14,30 +14,37 @@ core::arch::global_asm!( .global _asm_default_data_abort_handler .type _asm_default_data_abort_handler, %function _asm_default_data_abort_handler: - // Subtract 8 from the stored LR, see p.1214 of the ARMv7-A architecture manual. - subs lr, lr, #8 - // state save from compiled code - stmfd sp!, {{ r0 }} - mrs r0, spsr - stmfd sp!, {{ r0 }} + sub lr, lr, #8 // Subtract 8 from LR, see p.1214 of the ARMv7-A architecture manual. + push {{ r12 }} // Save preserved register R12 - can now use it + mrs r12, spsr // grab SPSR + push {{ r12 }} // save SPSR value + mov r12, sp // align SP down to eight byte boundary using R12 + and r12, r12, 7 // + sub sp, r12 // SP now aligned - only push 64-bit values from here + push {{ r0-r4, r12 }} // push alignment amount, and preserved registers - can now use R0-R3 (R4 is just padding) "#, - crate::save_context!(), + crate::save_fpu_context!(), r#" - // Pass the faulting instruction address to the handler. - mov r0, lr - // call C handler - bl _data_abort_handler - // if we get back here, assume they returned a new LR in r0 - mov lr, r0 + mov r0, lr // Pass the faulting instruction address to the handler. + bl _data_abort_handler // call C handler + mov lr, r0 // if we get back here, assume they returned a new LR in r0 "#, - crate::restore_context!(), + crate::restore_fpu_context!(), r#" - // Return from the asm handler - ldmia sp!, {{ r0 }} - msr spsr, r0 - ldmia sp!, {{ r0 }} - movs pc, lr + pop {{ r0-r4, r12 }} // restore preserved registers, dummy value, and alignment amount + add sp, r12 // restore SP alignment using R12 + pop {{ r12 }} // restore SPSR using R12 + msr spsr_cxsf, r12 // + pop {{ r12 }} // restore R12 + movs pc, lr // return from exception .size _asm_default_data_abort_handler, . - _asm_default_data_abort_handler + "# +); + +core::arch::global_asm!( + r#" + // Work around https://github.com/rust-lang/rust/issues/127269 + .fpu vfp2 // Called from the vector table when we have a prefetch abort. @@ -48,29 +55,29 @@ core::arch::global_asm!( .global _asm_default_prefetch_abort_handler .type _asm_default_prefetch_abort_handler, %function _asm_default_prefetch_abort_handler: - // Subtract 4 from the stored LR, see p.1212 of the ARMv7-A architecture manual. - subs lr, lr, #4 - // state save from compiled code - stmfd sp!, {{ r0 }} - mrs r0, spsr - stmfd sp!, {{ r0 }} + sub lr, lr, #4 // Subtract 4 from LR, see p.1212 of the ARMv7-A architecture manual. + push {{ r12 }} // Save preserved register R12 - can now use it + mrs r12, spsr // grab SPSR + push {{ r12 }} // save SPSR value + mov r12, sp // align SP down to eight byte boundary using R12 + and r12, r12, 7 // + sub sp, r12 // SP now aligned - only push 64-bit values from here + push {{ r0-r4, r12 }} // push alignment amount, and preserved registers - can now use R0-R3 (R4 is just padding) "#, - crate::save_context!(), + crate::save_fpu_context!(), r#" - // Pass the faulting instruction address to the handler. - mov r0, lr - // call C handler - bl _prefetch_abort_handler - // if we get back here, assume they returned a new LR in r0 - mov lr, r0 + mov r0, lr // Pass the faulting instruction address to the handler. + bl _prefetch_abort_handler // call C handler + mov lr, r0 // if we get back here, assume they returned a new LR in r0 "#, - crate::restore_context!(), + crate::restore_fpu_context!(), r#" - // Return from the asm handler - ldmia sp!, {{ r0 }} - msr spsr, r0 - ldmia sp!, {{ r0 }} - movs pc, lr + pop {{ r0-r4, r12 }} // restore preserved registers, dummy value, and alignment amount + add sp, r12 // restore SP alignment using R12 + pop {{ r12 }} // restore SPSR using R12 + msr spsr_cxsf, r12 // + pop {{ r12 }} // restore R12 + movs pc, lr // return from exception .size _asm_default_prefetch_abort_handler, . - _asm_default_prefetch_abort_handler "#, ); diff --git a/aarch32-rt/src/arch_v4/interrupt.rs b/aarch32-rt/src/arch_v4/interrupt.rs index c8d1888b..5a2c3444 100644 --- a/aarch32-rt/src/arch_v4/interrupt.rs +++ b/aarch32-rt/src/arch_v4/interrupt.rs @@ -16,33 +16,28 @@ core::arch::global_asm!( .global _asm_default_irq_handler .type _asm_default_irq_handler, %function _asm_default_irq_handler: - // make sure we jump back to the right place - sub lr, lr, 4 - // save our LR - stmfd sp!, {{ lr }} - // The hardware has copied the interrupted task's CPSR to SPSR_irq - mrs lr, spsr - stmfd sp!, {{ lr }} - // switch to system mode so we can handle another interrupt - // (because if we interrupt irq mode we trash our own shadow registers) - msr cpsr_c, {sys_mode} - // save state to the system stack (adjusting SP for alignment) - "#, - crate::save_context!(), + sub lr, lr, 4 // make sure we jump back to the right place + push {{ lr }} // save adjusted LR to IRQ stack + mrs lr, spsr // The hardware has copied the interrupted task's CPSR to SPSR_irq - grab it and + push {{ lr }} // save it to IRQ stack using LR + msr cpsr_c, {sys_mode} // switch to system mode so we can handle another interrupt (because if we interrupt irq mode we trash our own shadow registers) + mov lr, sp // align SP down to eight byte boundary using LR + and lr, lr, 7 // + sub sp, lr // SP now aligned - only push 64-bit values from here + push {{ r0-r3, r12, lr }} // push alignment amount (in LR) and preserved registers + "#, + crate::save_fpu_context!(), r#" - // call C handler (they may choose to re-enable interrupts) - bl _irq_handler - // restore from the system stack + bl _irq_handler // call C handler (they may choose to re-enable interrupts) "#, - crate::restore_context!(), + crate::restore_fpu_context!(), r#" - // switch back to IRQ mode (with IRQ masked) - msr cpsr_c, {irq_mode} - // load and restore SPSR - ldmia sp!, {{ lr }} - msr spsr, lr - // return - ldmfd sp!, {{ pc }}^ + pop {{ r0-r3, r12, lr }} // restore alignment amount (in LR) and preserved registers + add sp, lr // restore SP alignment using LR + msr cpsr_c, {irq_mode} // switch back to IRQ mode (with IRQ masked) + ldmia sp!, {{ lr }} // load and restore SPSR using LR + msr spsr, lr // + ldmfd sp!, {{ pc }}^ // return from exception .size _asm_default_irq_handler, . - _asm_default_irq_handler "#, // sys mode with IRQ masked diff --git a/aarch32-rt/src/arch_v4/svc.rs b/aarch32-rt/src/arch_v4/svc.rs index 5264732c..e941e8ba 100644 --- a/aarch32-rt/src/arch_v4/svc.rs +++ b/aarch32-rt/src/arch_v4/svc.rs @@ -1,4 +1,4 @@ -//! Abort handler for Armv4 to Armv6 +//! SVC handler for Armv4 to Armv6 #[cfg(target_arch = "arm")] core::arch::global_asm!( @@ -9,36 +9,45 @@ core::arch::global_asm!( // Called from the vector table when we have an software interrupt. // Saves state and calls a C-compatible handler like - // `extern "C" fn _svc_handler(svc: u32);` + // `extern "C" fn _svc_handler(arg: u32, frame: &Frame) -> u32;` .section .text._asm_default_svc_handler .arm .global _asm_default_svc_handler .type _asm_default_svc_handler, %function _asm_default_svc_handler: - stmfd sp!, {{ r0, lr }} - mrs r0, spsr - stmfd sp!, {{ r0 }} + push {{ r12, lr }} // save LR and R12 - can now use R12 (but leave LR alone for SVC code lookup) + mrs r12, spsr // grab SPSR using R12 + push {{ r12 }} // save SPSR value + mov r12, sp // align SP down to eight byte boundary using R12 + and r12, r12, 7 // + sub sp, r12 // SP now aligned - only push 64-bit values from here + push {{ r0-r6, r12 }} // push alignment amount, and stacked SVC argument registers (must be even number of regs for alignment) + mov r12, sp // save SP for integer frame "#, - crate::save_context!(), + crate::save_fpu_context!(), r#" - mrs r0, spsr // Load processor status that was banked on entry - tst r0, {t_bit} // SVC occurred from Thumb state? + mrs r0, spsr // Load processor status that was banked on entry + tst r0, {t_bit} // SVC occurred from Thumb state? beq 1f - ldrh r0, [lr,#-2] // Yes: Load halfword and... - bic r0, r0, #0xFF00 // ...extract comment field + ldrh r0, [lr,#-2] // Yes: Load halfword and... + bic r0, r0, #0xFF00 // ...r0 now contains SVC number b 2f 1: - ldr r0, [lr,#-4] // No: Load word and... - bic r0, r0, #0xFF000000 // ...extract comment field + ldr r0, [lr,#-4] // No: Load word and... + bic r0, r0, #0xFF000000 // ...r0 now contains SVC number 2: - // r0 now contains SVC number + mov r1, r12 // pass the stacked integer registers in r1 bl _svc_handler + mov lr, r0 // move r0 out of the way - restore_fpu_context will trash it "#, - crate::restore_context!(), + crate::restore_fpu_context!(), r#" - ldmfd sp!, {{ r0 }} - msr spsr_cxsf, r0 - ldmfd sp!, {{ r0, pc }}^ + pop {{ r0-r6, r12 }} // restore stacked registers and alignment amount + mov r0, lr // replace R0 with return value from _svc_handler + add sp, r12 // restore SP alignment using R12 + pop {{ lr }} // restore SPSR using LR + msr spsr_cxsf, lr // + ldmfd sp!, {{ r12, pc }}^ // restore R12 and return from exception .size _asm_default_svc_handler, . - _asm_default_svc_handler "#, t_bit = const { crate::Cpsr::new_with_raw_value(0).with_t(true).raw_value() }, diff --git a/aarch32-rt/src/arch_v4/undefined.rs b/aarch32-rt/src/arch_v4/undefined.rs index 9297ee10..02cb09ac 100644 --- a/aarch32-rt/src/arch_v4/undefined.rs +++ b/aarch32-rt/src/arch_v4/undefined.rs @@ -16,39 +16,32 @@ core::arch::global_asm!( .global _asm_default_undefined_handler .type _asm_default_undefined_handler, %function _asm_default_undefined_handler: - // state save from compiled code - stmfd sp!, {{ r0 }} - mrs r0, spsr - stmfd sp!, {{ r0 }} - // First adjust LR for two purposes: Passing the faulting instruction to the C handler, - // and to return to the failing instruction after the C handler returns. - // Load processor status for the calling code - mrs r0, spsr - // Was the code that triggered the exception in Thumb state? - tst r0, {t_bit} - // Subtract 2 in Thumb Mode, 4 in Arm Mode - see p.1206 of the ARMv7-A architecture manual. - ite eq - subeq lr, lr, #4 - subne lr, lr, #2 - // now do our standard exception save (which saves the 'wrong' R0) + push {{ r12 }} // save R12 - can now use it + mrs r12, spsr // grab SPSR using R12 + push {{ r12 }} // save SPSR value + tst r12, {t_bit} // Was the code that triggered the exception in Thumb state? + ite eq // Adjust LR to point to faulting instruction - see p.1206 of the ARMv7-A architecture manual. + subeq lr, lr, #4 // Subtract 4 in Arm Mode + subne lr, lr, #2 // Subtract 2 in Thumb Mode + mov r12, sp // align SP down to eight byte boundary using R12 + and r12, r12, 7 // + sub sp, r12 // SP now aligned - only push 64-bit values from here + push {{ r0-r4, r12 }} // push alignment amount, and preserved registers - can now use R0-R3 (R4 is just padding) "#, - crate::save_context!(), + crate::save_fpu_context!(), r#" - // Pass the faulting instruction address to the handler. - mov r0, lr - // call C handler - bl _undefined_handler - // if we get back here, assume they returned a new LR in r0 - mov lr, r0 - // do our standard restore (with the 'wrong' R0) + mov r0, lr // Pass the faulting instruction address to the handler. + bl _undefined_handler // call C handler + mov lr, r0 // if we get back here, assume they returned a new LR in r0 "#, - crate::restore_context!(), + crate::restore_fpu_context!(), r#" - // Return from the asm handler - ldmia sp!, {{ r0 }} - msr spsr, r0 - ldmia sp!, {{ r0 }} - movs pc, lr + pop {{ r0-r4, r12 }} // restore preserved registers, dummy value, and alignment amount + add sp, r12 // restore SP alignment using R12 + pop {{ r12 }} // restore SPSR using R12 + msr spsr_cxsf, r12 // + pop {{ r12 }} // restore R12 + movs pc, lr // return from exception .size _asm_default_undefined_handler, . - _asm_default_undefined_handler "#, t_bit = const { crate::Cpsr::new_with_raw_value(0).with_t(true).raw_value() }, diff --git a/aarch32-rt/src/arch_v7/abort.rs b/aarch32-rt/src/arch_v7/abort.rs index f08591e9..8a2093d3 100644 --- a/aarch32-rt/src/arch_v7/abort.rs +++ b/aarch32-rt/src/arch_v7/abort.rs @@ -13,57 +13,67 @@ core::arch::global_asm!( .global _asm_default_data_abort_handler .type _asm_default_data_abort_handler, %function _asm_default_data_abort_handler: - // Subtract 8 from the stored LR, see p.1214 of the ARMv7-A architecture manual. - subs lr, lr, #8 - // state save from compiled code - srsfd sp!, #{abt_mode} + sub lr, lr, #8 // Subtract 8 from LR, see p.1214 of the ARMv7-A architecture manual. + srsfd sp!, #{abt_mode} // store return state to ABT stack + push {{ r12 }} // Save preserved register R12 - can now use it + mov r12, sp // align SP down to eight byte boundary using R12 + and r12, r12, 7 // + sub sp, r12 // SP now aligned - only push 64-bit values from here + push {{ r0-r4, r12 }} // push alignment amount, and preserved registers - can now use R0-R3 (R4 is just padding) "#, - crate::save_context!(), + crate::save_fpu_context!(), r#" - // Pass the faulting instruction address to the handler. - mov r0, lr - // call C handler - bl _data_abort_handler - // if we get back here, assume they returned a new LR in r0 - mov lr, r0 + mov r0, lr // Pass the faulting instruction address to the handler. + bl _data_abort_handler // call C handler + mov lr, r0 // if we get back here, assume they returned a new LR in r0 "#, - crate::restore_context!(), + crate::restore_fpu_context!(), r#" - // overwrite the saved LR with the one from the C handler - str lr, [sp] - // Return from the asm handler - rfefd sp! + pop {{ r0-r4, r12 }} // restore preserved registers, dummy value, and alignment amount + add sp, r12 // restore SP alignment using R12 + pop {{ r12 }} // restore R12 + str lr, [sp] // overwrite the saved LR with the one from the C handler + rfefd sp! // return from exception .size _asm_default_data_abort_handler, . - _asm_default_data_abort_handler + "#, + abt_mode = const crate::ProcessorMode::Abt as u8, +); +core::arch::global_asm!( + r#" + // Work around https://github.com/rust-lang/rust/issues/127269 + .fpu vfp3 .section .text._asm_default_prefetch_abort_handler // Called from the vector table when we have a prefetch abort. // Saves state and calls a C-compatible handler like // `extern "C" fn _prefetch_abort_handler(addr: usize);` .global _asm_default_prefetch_abort_handler + .arm .type _asm_default_prefetch_abort_handler, %function _asm_default_prefetch_abort_handler: - // Subtract 4 from the stored LR, see p.1212 of the ARMv7-A architecture manual. - subs lr, lr, #4 - // state save from compiled code - srsfd sp!, #{abt_mode} + sub lr, lr, #4 // Subtract 8 from LR, see p.1212 of the ARMv7-A architecture manual. + srsfd sp!, #{abt_mode} // store return state to ABT stack + push {{ r12 }} // save R12 - can now use it + mov r12, sp // align SP down to eight byte boundary using R12 + and r12, r12, 7 // + sub sp, r12 // SP now aligned - only push 64-bit values from here + push {{ r0-r4, r12 }} // push alignment amount, and preserved registers - can now use R0-R3 (R4 is just padding) "#, - crate::save_context!(), + crate::save_fpu_context!(), r#" - // Pass the faulting instruction address to the handler. - mov r0, lr - // call C handler - bl _prefetch_abort_handler - // if we get back here, assume they returned a new LR in r0 - mov lr, r0 + mov r0, lr // Pass the faulting instruction address to the handler. + bl _prefetch_abort_handler // call C handler + mov lr, r0 // if we get back here, assume they returned a new LR in r0 "#, - crate::restore_context!(), + crate::restore_fpu_context!(), r#" - // overwrite the saved LR with the one from the C handler - str lr, [sp] - // Return from the asm handler - rfefd sp! + pop {{ r0-r4, r12 }} // restore preserved registers, dummy value, and alignment amount + add sp, r12 // restore SP alignment using R12 + pop {{ r12 }} // restore R12 + str lr, [sp] // overwrite the saved LR with the one from the C handler + rfefd sp! // return from exception .size _asm_default_prefetch_abort_handler, . - _asm_default_prefetch_abort_handler - "#, + "#, abt_mode = const crate::ProcessorMode::Abt as u8, ); diff --git a/aarch32-rt/src/arch_v7/interrupt.rs b/aarch32-rt/src/arch_v7/interrupt.rs index bd3869c3..dab9b3c6 100644 --- a/aarch32-rt/src/arch_v7/interrupt.rs +++ b/aarch32-rt/src/arch_v7/interrupt.rs @@ -1,4 +1,4 @@ -//! IRQ handler for Armv7 and higher +//! IRQ handler for for Armv7 and higher #[cfg(target_arch = "arm")] core::arch::global_asm!( @@ -14,33 +14,26 @@ core::arch::global_asm!( .global _asm_default_irq_handler .type _asm_default_irq_handler, %function _asm_default_irq_handler: - // make sure we jump back to the right place - sub lr, lr, 4 - // The hardware has copied CPSR to SPSR_irq and LR to LR_irq for us. - // Now push SPSR_irq and LR_irq to the SYS stack (because that's the - // mode we're in when we pop) - srsfd sp!, #{sys_mode} - // switch to system mode so we can handle another interrupt - // (because if we interrupt irq mode we trash our own shadow registers) - cps #{sys_mode} - // we also need to save LR, so we can be re-entrant - push {{lr}} - // save state to the system stack (adjusting SP for alignment) - "#, - crate::save_context!(), + sub lr, lr, 4 // make sure we jump back to the right place + srsfd sp!, #{sys_mode} // store return state to SYS stack + cps #{sys_mode} // switch to system mode so we can handle another interrupt (because if we interrupt irq mode we trash our own shadow registers) + push {{ lr }} // save adjusted LR to SYS stack + mov lr, sp // align SP down to eight byte boundary using LR + and lr, lr, 7 // + sub sp, lr // SP now aligned - only push 64-bit values from here + push {{ r0-r3, r12, lr }} // push alignment amount (in LR) and preserved registers + "#, + crate::save_fpu_context!(), r#" - // call C handler - bl _irq_handler - // restore from the system stack + bl _irq_handler // call C handler (they may choose to re-enable interrupts) "#, - crate::restore_context!(), + crate::restore_fpu_context!(), r#" - // restore LR - pop {{lr}} - // pop CPSR and LR from the stack (which also restores the mode) - rfefd sp! + pop {{ r0-r3, r12, lr }} // restore alignment amount (in LR) and preserved registers + add sp, lr // restore SP alignment using LR + pop {{ lr }} // restore adjusted LR + rfefd sp! // return from exception .size _asm_default_irq_handler, . - _asm_default_irq_handler - "#, sys_mode = const crate::ProcessorMode::Sys as u8, ); diff --git a/aarch32-rt/src/arch_v7/svc.rs b/aarch32-rt/src/arch_v7/svc.rs index 000fa697..f303a482 100644 --- a/aarch32-rt/src/arch_v7/svc.rs +++ b/aarch32-rt/src/arch_v7/svc.rs @@ -6,42 +6,43 @@ core::arch::global_asm!( // Work around https://github.com/rust-lang/rust/issues/127269 .fpu vfp3 - .section .text._asm_default_svc_handler - // Called from the vector table when we have an software interrupt. // Saves state and calls a C-compatible handler like - // `extern "C" fn _svc_handler(svc: u32);` + // `extern "C" fn _svc_handler(arg: u32, frame: &Frame) -> u32;` + .section .text._asm_default_svc_handler + .arm .global _asm_default_svc_handler .type _asm_default_svc_handler, %function _asm_default_svc_handler: - // state save from compiled code - srsfd sp!, #{svc_mode} + srsfd sp!, #{svc_mode} // store return state to SVC stack + push {{ r12, lr }} // save LR and R12 - can now use R12 (but leave LR alone for SVC code lookup) + mov r12, sp // align SP down to eight byte boundary using R12 + and r12, r12, 7 // + sub sp, r12 // SP now aligned - only push 64-bit values from here + push {{ r0-r6, r12 }} // push alignment amount, and stacked SVC argument registers (must be even number of regs for alignment) + mov r12, sp // save SP for integer frame "#, - crate::save_context!(), + crate::save_fpu_context!(), r#" - mrs r0, spsr // Load processor status that was banked on entry - tst r0, {t_bit} // SVC occurred from Thumb state? - beq 1f - ldrh r0, [lr,#-2] // Yes: Load halfword and... - bic r0, r0, #0xFF00 // ...extract comment field - b 2f - 1: - ldr r0, [lr,#-4] // No: Load word and... - bic r0, r0, #0xFF000000 // ...extract comment field - 2: - // r0 now contains SVC number + mrs r0, spsr // Load processor status that was banked on entry + tst r0, {t_bit} // SVC occurred from Thumb state? + ldrhne r0, [lr,#-2] // Yes: Load halfword and... + bicne r0, r0, #0xFF00 // ...extract comment field + ldreq r0, [lr,#-4] // No: Load word and... + biceq r0, r0, #0xFF000000 // ...extract comment field + mov r1, r12 // pass the stacked integer registers in r1 bl _svc_handler + mov lr, r0 // move r0 out of the way - restore_fpu_context will trash it "#, - crate::restore_context!(), + crate::restore_fpu_context!(), r#" - // Return from the asm handler - rfefd sp! + pop {{ r0-r6, r12 }} // restore stacked registers and alignment amount + mov r0, lr // replace R0 with return value from _svc_handler + add sp, r12 // restore SP alignment using R12 + pop {{ r12, lr }} // restore R12 and LR + rfefd sp! // return from exception .size _asm_default_svc_handler, . - _asm_default_svc_handler "#, svc_mode = const crate::ProcessorMode::Svc as u8, - t_bit = const { - crate::Cpsr::new_with_raw_value(0) - .with_t(true) - .raw_value() - }, + t_bit = const { crate::Cpsr::new_with_raw_value(0).with_t(true).raw_value() }, ); diff --git a/aarch32-rt/src/arch_v7/undefined.rs b/aarch32-rt/src/arch_v7/undefined.rs index 9fb0c6bd..03e13129 100644 --- a/aarch32-rt/src/arch_v7/undefined.rs +++ b/aarch32-rt/src/arch_v7/undefined.rs @@ -15,46 +15,33 @@ core::arch::global_asm!( .global _asm_default_undefined_handler .type _asm_default_undefined_handler, %function _asm_default_undefined_handler: - // state save from compiled code - srsfd sp!, #{und_mode} - // to work out what mode we're in, we need R0 - push {{r0}} - // First adjust LR for two purposes: Passing the faulting instruction to the C handler, - // and to return to the failing instruction after the C handler returns. - // Load processor status for the calling code - mrs r0, spsr - // Was the code that triggered the exception in Thumb state? - tst r0, {t_bit} - // Subtract 2 in Thumb Mode, 4 in Arm Mode - see p.1206 of the ARMv7-A architecture manual. - ite eq - subeq lr, lr, #4 - subne lr, lr, #2 - // now do our standard exception save (which saves the 'wrong' R0) + srsfd sp!, #{und_mode} // store return state to UND stack + push {{ r12 }} // Save preserved register R12 - can now use it + mrs r12, spsr // Read SPSR into R12 + tst r12, {t_bit} // Was the code that triggered the exception in Thumb state? + ite eq // Adjust LR to point to faulting instruction - see p.1206 of the ARMv7-A architecture manual. + subeq lr, lr, #4 // Subtract 4 in Arm Mode + subne lr, lr, #2 // Subtract 2 in Thumb Mode + mov r12, sp // align SP down to eight byte boundary using R12 + and r12, r12, 7 // + sub sp, r12 // SP now aligned - only push 64-bit values from here + push {{ r0-r4, r12 }} // push alignment amount, and preserved registers - can now use R0-R3 (R4 is just padding) "#, - crate::save_context!(), + crate::save_fpu_context!(), r#" - // Pass the faulting instruction address to the handler. - mov r0, lr - // call C handler - bl _undefined_handler - // if we get back here, assume they returned a new LR in r0 - mov lr, r0 - // do our standard restore (with the 'wrong' R0) + mov r0, lr // Pass the faulting instruction address to the handler. + bl _undefined_handler // call C handler + mov lr, r0 // if we get back here, assume they returned a new LR in r0 "#, - crate::restore_context!(), + crate::restore_fpu_context!(), r#" - // get the R0 we saved early - pop {{r0}} - // overwrite the saved LR with the one from the C handler - str lr, [sp] - // Return from the asm handler - rfefd sp! + pop {{ r0-r4, r12 }} // restore preserved registers, dummy value, and alignment amount + add sp, r12 // restore SP alignment using R12 + pop {{ r12 }} // restore R12 + str lr, [sp] // overwrite the saved LR with the one from the C handler + rfefd sp! // return from exception .size _asm_default_undefined_handler, . - _asm_default_undefined_handler "#, und_mode = const crate::ProcessorMode::Und as u8, - t_bit = const { - crate::Cpsr::new_with_raw_value(0) - .with_t(true) - .raw_value() - }, + t_bit = const { crate::Cpsr::new_with_raw_value(0).with_t(true).raw_value() }, ); diff --git a/aarch32-rt/src/lib.rs b/aarch32-rt/src/lib.rs index ecf05f6d..1393eb1f 100644 --- a/aarch32-rt/src/lib.rs +++ b/aarch32-rt/src/lib.rs @@ -193,14 +193,16 @@ //! The symbol `_svc_handler` should be an `extern "C"` function. It is called //! in SVC mode when an [Supervisor Call Exception] occurs. //! -//! [Supervisor CalL Exception]: +//! [Supervisor Call Exception]: //! https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/The-System-Level-Programmers--Model/Exception-descriptions/Supervisor-Call--SVC--exception?lang=en //! //! Returning from this function will cause execution to resume at the function //! the triggered the exception, immediately after the SVC instruction. You //! cannot control where execution resumes. The function is passed the literal //! integer argument to the `svc` instruction, which is extracted from the -//! machine code for you by the default assembly trampoline. +//! machine code for you by the default assembly trampoline, along with +//! registers r0 through r7, in the form of a reference to a `Frame` +//! structure. //! //! Our linker script PROVIDEs a default `_svc_handler` symbol which is an alias //! for the `_default_handler` function. You can override it by defining your @@ -208,8 +210,9 @@ //! //! ```rust //! #[unsafe(no_mangle)] -//! extern "C" fn _svc_handler(svc: u32) { +//! extern "C" fn _svc_handler(arg: u32, frame: &aarch32_rt::Frame) -> u32 { //! // do stuff here +//! todo!() //! } //! ``` //! @@ -220,8 +223,9 @@ //! use aarch32_rt::exception; //! //! #[exception(SupervisorCall)] -//! fn my_svc_handler(arg: u32) { +//! fn svc_handler(arg: u32, frame: &aarch32_rt::Frame) -> u32 { //! // do stuff here +//! todo!() //! } //! ``` //! @@ -546,83 +550,76 @@ core::arch::global_asm!( "# ); -/// This macro expands to code for saving context on entry to an exception -/// handler. It ensures the stack pointer is 8 byte aligned on exit. +/// Arguments stacked on interrupt /// -/// EABI specifies R4 - R11 as callee-save, and so we don't preserve them -/// because any C function we call to handle the exception will -/// preserve/restore them itself as required. +/// This struct is very carefully designed to match the layout of the +/// registers pushed to the stack in our SVC handler. +#[derive(Debug, Clone, PartialEq, Eq)] +#[repr(C)] +pub struct Frame { + pub r0: u32, + pub r1: u32, + pub r2: u32, + pub r3: u32, + pub r4: u32, + pub r5: u32, +} + +/// This macro expands to code for saving FPU context on entry to an exception +/// handler. It pushes a multiple of eight bytes to preserve AAPCS alignment. +/// It may damage R0-R3. /// -/// It should match `restore_context!`. +/// It should match `restore_fpu_context!` /// /// On entry to this block, we assume that we are in exception context. #[cfg(not(any(target_abi = "eabihf", feature = "eabi-fpu")))] #[macro_export] -macro_rules! save_context { +macro_rules! save_fpu_context { () => { - r#" - // save preserved registers (and gives us some working area) - push {{ r0-r3 }} - // align SP down to eight byte boundary - mov r0, sp - and r0, r0, 7 - sub sp, r0 - // push alignment amount, and final preserved register - push {{ r0, r12 }} - "# + "" }; } /// This macro expands to code for restoring context on exit from an exception /// handler. /// -/// It should match `save_context!`. +/// It should match `save_fpu_context!`. #[cfg(not(any(target_abi = "eabihf", feature = "eabi-fpu")))] #[macro_export] -macro_rules! restore_context { +macro_rules! restore_fpu_context { () => { - r#" - // restore alignment amount, and preserved register - pop {{ r0, r12 }} - // restore pre-alignment SP - add sp, r0 - // restore more preserved registers - pop {{ r0-r3 }} - "# + "" }; } -/// This macro expands to code for restoring context on exit from an exception -/// handler. It saves FPU state, assuming 16 DP registers (a 'D16' or 'D16SP' +/// This macro expands to code for saving FPU context on entry to an exception +/// handler. It pushes a multiple of eight bytes to preserve AAPCS alignment. +/// It may damage R0-R3. +/// +/// It should match `restore_fpu_context!` +/// +/// On entry to this block, we assume that we are in exception context. +/// +/// This version saves FPU state, assuming 16 DP registers (a 'D16' or 'D16SP' /// FPU configuration). Note that SP-only FPUs still have DP registers /// - each DP register holds two SP values. /// -/// EABI specifies R4 - R11 and D8-D15 as callee-save, and so we don't +/// EABI specifies D8-D15 as callee-save, and so we don't /// preserve them because any C function we call to handle the exception will /// preserve/restore them itself as required. -/// -/// It should match `restore_context!`. #[cfg(all( any(target_abi = "eabihf", feature = "eabi-fpu"), not(feature = "fpu-d32") ))] #[macro_export] -macro_rules! save_context { +macro_rules! save_fpu_context { () => { r#" - // save preserved registers (and gives us some working area) - push {{ r0-r3 }} // save all D16 FPU context, except D8-D15 vpush {{ d0-d7 }} vmrs r0, FPSCR vmrs r1, FPEXC push {{ r0-r1 }} - // align SP down to eight byte boundary - mov r0, sp - and r0, r0, 7 - sub sp, r0 - // push alignment amount, and final preserved register - push {{ r0, r12 }} "# }; } @@ -631,58 +628,49 @@ macro_rules! save_context { /// handler. It restores FPU state, assuming 16 DP registers (a 'D16' or /// 'D16SP' FPU configuration). /// -/// It should match `save_context!`. +/// It should match `save_fpu_context!`. #[cfg(all( any(target_abi = "eabihf", feature = "eabi-fpu"), not(feature = "fpu-d32") ))] #[macro_export] -macro_rules! restore_context { +macro_rules! restore_fpu_context { () => { r#" - // restore alignment amount, and preserved register - pop {{ r0, r12 }} - // restore pre-alignment SP - add sp, r0 // restore all D16 FPU context, except D8-D15 pop {{ r0-r1 }} vmsr FPEXC, r1 vmsr FPSCR, r0 vpop {{ d0-d7 }} - // restore more preserved registers - pop {{ r0-r3 }} "# }; } -/// This macro expands to code for saving context on entry to an exception -/// handler. It saves FPU state assuming 32 DP registers (a 'D32' FPU +/// This macro expands to code for saving FPU context on entry to an exception +/// handler. It pushes a multiple of eight bytes to preserve AAPCS alignment. +/// It may damage R0-R3. +/// +/// It should match `restore_fpu_context!` +/// +/// On entry to this block, we assume that we are in exception context. +/// +/// This version saves FPU state assuming 32 DP registers (a 'D32' FPU /// configuration). /// -/// EABI specifies R4 - R11 and D8-D15 as callee-save, and so we don't +/// EABI specifies D8-D15 as callee-save, and so we don't /// preserve them because any C function we call to handle the exception will /// preserve/restore them itself as required. -/// -/// It should match `restore_context!`. #[cfg(all(any(target_abi = "eabihf", feature = "eabi-fpu"), feature = "fpu-d32"))] #[macro_export] -macro_rules! save_context { +macro_rules! save_fpu_context { () => { r#" - // save preserved registers (and gives us some working area) - push {{ r0-r3 }} // save all D32 FPU context, except D8-D15 vpush {{ d0-d7 }} vpush {{ d16-d31 }} vmrs r0, FPSCR vmrs r1, FPEXC push {{ r0-r1 }} - // align SP down to eight byte boundary - mov r0, sp - and r0, r0, 7 - sub sp, r0 - // push alignment amount, and final preserved register - push {{ r0, r12 }} "# }; } @@ -691,24 +679,18 @@ macro_rules! save_context { /// handler. It restores FPU state, assuming 32 DP registers (a 'D32' FPU /// configuration). /// -/// It should match `save_context!`. +/// It should match `save_fpu_context!`. #[cfg(all(any(target_abi = "eabihf", feature = "eabi-fpu"), feature = "fpu-d32"))] #[macro_export] -macro_rules! restore_context { +macro_rules! restore_fpu_context { () => { r#" - // restore alignment amount, and preserved register - pop {{ r0, r12 }} - // restore pre-alignment SP - add sp, r0 // restore all D32 FPU context, except D8-D15 pop {{ r0-r1 }} vmsr FPEXC, r1 vmsr FPSCR, r0 vpop {{ d16-d31 }} vpop {{ d0-d7 }} - // restore more preserved registers - pop {{ r0-r3 }} "# }; } diff --git a/examples/mps3-an536/reference/syscall-armv8r-none-eabihf.out b/examples/mps3-an536/reference/syscall-armv8r-none-eabihf.out new file mode 100644 index 00000000..4055ecd0 --- /dev/null +++ b/examples/mps3-an536/reference/syscall-armv8r-none-eabihf.out @@ -0,0 +1,3 @@ +x = 1, y = 2, z = 3.000 +Syscalls all look OK, printing local variables... +x = 1, y = 2, z = 3.000 diff --git a/examples/mps3-an536/reference/syscall-thumbv8r-none-eabihf.out b/examples/mps3-an536/reference/syscall-thumbv8r-none-eabihf.out new file mode 100644 index 00000000..4055ecd0 --- /dev/null +++ b/examples/mps3-an536/reference/syscall-thumbv8r-none-eabihf.out @@ -0,0 +1,3 @@ +x = 1, y = 2, z = 3.000 +Syscalls all look OK, printing local variables... +x = 1, y = 2, z = 3.000 diff --git a/examples/mps3-an536/src/bin/svc-a32.rs b/examples/mps3-an536/src/bin/svc-a32.rs index 6a4076ea..c8774b1a 100644 --- a/examples/mps3-an536/src/bin/svc-a32.rs +++ b/examples/mps3-an536/src/bin/svc-a32.rs @@ -23,12 +23,13 @@ fn main() -> ! { /// This is our SVC exception handler #[exception(SupervisorCall)] -fn svc_handler(arg: u32) { +fn svc_handler(arg: u32, _frame: &aarch32_rt::Frame) -> u32 { println!("In svc_handler, with arg=0x{:06x}", arg); if arg == 0xABCDEF { // test nested SVC calls do_svc2(); } + 0 } #[instruction_set(arm::a32)] diff --git a/examples/mps3-an536/src/bin/svc-t32.rs b/examples/mps3-an536/src/bin/svc-t32.rs index e94c8a9b..d3600778 100644 --- a/examples/mps3-an536/src/bin/svc-t32.rs +++ b/examples/mps3-an536/src/bin/svc-t32.rs @@ -23,12 +23,13 @@ fn main() -> ! { /// This is our SVC exception handler #[exception(SupervisorCall)] -fn svc_handler(arg: u32) { +fn svc_handler(arg: u32, _frame: &aarch32_rt::Frame) -> u32 { println!("In svc_handler, with arg=0x{:06x}", arg); if arg == 0x12 { // test nested SVC calls do_svc2(); } + 0 } #[instruction_set(arm::t32)] diff --git a/examples/mps3-an536/src/bin/syscall.rs b/examples/mps3-an536/src/bin/syscall.rs new file mode 100644 index 00000000..71ca20e5 --- /dev/null +++ b/examples/mps3-an536/src/bin/syscall.rs @@ -0,0 +1,107 @@ +//! Syscall example +//! +//! * Syscall 0xF0 takes no arguments and returns: 0x1000_0000 +//! * Syscall 0xF1 takes one argument and returns: 0x1000_0000 + (arg0) +//! * Syscall 0xF2 takes two arguments and returns: 0x1000_0000 + (arg0) + (arg1 << 4) +//! * Syscall 0xF3 takes three arguments and returns: 0x1000_0000 + (arg0) + (arg1 << 4) + (arg2 << 8) +//! * Syscall 0xF4 takes four arguments and returns: 0x1000_0000 + (arg0) + (arg1 << 4) + (arg2 << 8) + (arg3 << 12) +//! * Syscall 0xF5 takes five arguments and returns: 0x1000_0000 + (arg0) + (arg1 << 4) + (arg2 << 8) + (arg3 << 12) + (arg4 << 16) +//! * Syscall 0xF6 takes six arguments and returns: 0x1000_0000 + (arg0) + (arg1 << 4) + (arg2 << 8) + (arg3 << 12) + (arg4 << 16) + (arg5 << 20) + +#![no_std] +#![no_main] + +use aarch32_rt::{entry, exception}; +use mps3_an536 as _; +use semihosting::println; + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[entry] +fn main() -> ! { + let x = 1; + let y = x + 1; + let z = (y as f64) * 1.5; + println!("x = {}, y = {}, z = {:0.3}", x, y, z); + + let retval = aarch32_cpu::svc6!( + 0xF6, + 0xE000_0001, + 0xE000_0002, + 0xE000_0003, + 0xE000_0004, + 0xE000_0005, + 0xE000_0006 + ); + if retval != 0xF065_4321 { + panic!("Wanted 0xF065_4321, got {:08x}", retval); + } + let retval = aarch32_cpu::svc5!( + 0xF5, + 0xE000_0001, + 0xE000_0002, + 0xE000_0003, + 0xE000_0004, + 0xE000_0005 + ); + if retval != 0xF005_4321 { + panic!("Wanted 0xF005_4321, got {:08x}", retval); + } + let retval = aarch32_cpu::svc4!(0xF4, 0xE000_0001, 0xE000_0002, 0xE000_0003, 0xE000_0004); + if retval != 0xF000_4321 { + panic!("Wanted 0xF000_4321, got {:08x}", retval); + } + let retval = aarch32_cpu::svc3!(0xF3, 0xE000_0001, 0xE000_0002, 0xE000_0003); + if retval != 0xF000_0321 { + panic!("Wanted 0xF000_0321, got {:08x}", retval); + } + let retval = aarch32_cpu::svc2!(0xF2, 0xE000_0001, 0xE000_0002); + if retval != 0xF000_0021 { + panic!("Wanted 0xF000_0021, got {:08x}", retval); + } + let retval = aarch32_cpu::svc1!(0xF1, 0xE000_0001); + if retval != 0xF000_0001 { + panic!("Wanted 0xF000_0001, got {:08x}", retval); + } + let retval = aarch32_cpu::svc!(0xF0); + if retval != 0x1000_0000 { + panic!("Wanted 0x1000_0000, got {:08x}", retval); + } + println!("Syscalls all look OK, printing local variables..."); + println!("x = {}, y = {}, z = {:0.3}", x, y, z); + semihosting::process::exit(0); +} + +/// This is our syscall handler +/// +/// We mix together the arguments given in `frame` according to the rules at the +/// top of the file. The arguments are designed to be easy to see in a debugger +/// and the output is designed to be easy to check. +#[exception(SupervisorCall)] +fn svc_handler(arg: u32, frame: &aarch32_rt::Frame) -> u32 { + // println!("Frame: {:08x?}", frame); + if arg & 0xF8 != 0xF0 { + return 0xDEAD_C0DE; + } + let mut output = 0x1000_0000; + if arg >= 0xF6 { + output += frame.r5 << 20; + } + if arg >= 0xF5 { + output += frame.r4 << 16; + } + if arg >= 0xF4 { + output += frame.r3 << 12; + } + if arg >= 0xF3 { + output += frame.r2 << 8; + } + if arg >= 0xF2 { + output += frame.r1 << 4; + } + if arg >= 0xF1 { + output += frame.r0; + } + output +} diff --git a/examples/versatileab/reference/syscall-armv4t-none-eabi.out b/examples/versatileab/reference/syscall-armv4t-none-eabi.out new file mode 100644 index 00000000..4055ecd0 --- /dev/null +++ b/examples/versatileab/reference/syscall-armv4t-none-eabi.out @@ -0,0 +1,3 @@ +x = 1, y = 2, z = 3.000 +Syscalls all look OK, printing local variables... +x = 1, y = 2, z = 3.000 diff --git a/examples/versatileab/reference/syscall-armv5te-none-eabi.out b/examples/versatileab/reference/syscall-armv5te-none-eabi.out new file mode 100644 index 00000000..4055ecd0 --- /dev/null +++ b/examples/versatileab/reference/syscall-armv5te-none-eabi.out @@ -0,0 +1,3 @@ +x = 1, y = 2, z = 3.000 +Syscalls all look OK, printing local variables... +x = 1, y = 2, z = 3.000 diff --git a/examples/versatileab/reference/syscall-armv6-none-eabi.out b/examples/versatileab/reference/syscall-armv6-none-eabi.out new file mode 100644 index 00000000..4055ecd0 --- /dev/null +++ b/examples/versatileab/reference/syscall-armv6-none-eabi.out @@ -0,0 +1,3 @@ +x = 1, y = 2, z = 3.000 +Syscalls all look OK, printing local variables... +x = 1, y = 2, z = 3.000 diff --git a/examples/versatileab/reference/syscall-armv6-none-eabihf.out b/examples/versatileab/reference/syscall-armv6-none-eabihf.out new file mode 100644 index 00000000..4055ecd0 --- /dev/null +++ b/examples/versatileab/reference/syscall-armv6-none-eabihf.out @@ -0,0 +1,3 @@ +x = 1, y = 2, z = 3.000 +Syscalls all look OK, printing local variables... +x = 1, y = 2, z = 3.000 diff --git a/examples/versatileab/reference/syscall-armv7a-none-eabi.out b/examples/versatileab/reference/syscall-armv7a-none-eabi.out new file mode 100644 index 00000000..4055ecd0 --- /dev/null +++ b/examples/versatileab/reference/syscall-armv7a-none-eabi.out @@ -0,0 +1,3 @@ +x = 1, y = 2, z = 3.000 +Syscalls all look OK, printing local variables... +x = 1, y = 2, z = 3.000 diff --git a/examples/versatileab/reference/syscall-armv7a-none-eabihf.out b/examples/versatileab/reference/syscall-armv7a-none-eabihf.out new file mode 100644 index 00000000..4055ecd0 --- /dev/null +++ b/examples/versatileab/reference/syscall-armv7a-none-eabihf.out @@ -0,0 +1,3 @@ +x = 1, y = 2, z = 3.000 +Syscalls all look OK, printing local variables... +x = 1, y = 2, z = 3.000 diff --git a/examples/versatileab/reference/syscall-armv7r-none-eabi.out b/examples/versatileab/reference/syscall-armv7r-none-eabi.out new file mode 100644 index 00000000..4055ecd0 --- /dev/null +++ b/examples/versatileab/reference/syscall-armv7r-none-eabi.out @@ -0,0 +1,3 @@ +x = 1, y = 2, z = 3.000 +Syscalls all look OK, printing local variables... +x = 1, y = 2, z = 3.000 diff --git a/examples/versatileab/reference/syscall-armv7r-none-eabihf.out b/examples/versatileab/reference/syscall-armv7r-none-eabihf.out new file mode 100644 index 00000000..4055ecd0 --- /dev/null +++ b/examples/versatileab/reference/syscall-armv7r-none-eabihf.out @@ -0,0 +1,3 @@ +x = 1, y = 2, z = 3.000 +Syscalls all look OK, printing local variables... +x = 1, y = 2, z = 3.000 diff --git a/examples/versatileab/reference/syscall-thumbv4t-none-eabi.out b/examples/versatileab/reference/syscall-thumbv4t-none-eabi.out new file mode 100644 index 00000000..4055ecd0 --- /dev/null +++ b/examples/versatileab/reference/syscall-thumbv4t-none-eabi.out @@ -0,0 +1,3 @@ +x = 1, y = 2, z = 3.000 +Syscalls all look OK, printing local variables... +x = 1, y = 2, z = 3.000 diff --git a/examples/versatileab/reference/syscall-thumbv5te-none-eabi.out b/examples/versatileab/reference/syscall-thumbv5te-none-eabi.out new file mode 100644 index 00000000..4055ecd0 --- /dev/null +++ b/examples/versatileab/reference/syscall-thumbv5te-none-eabi.out @@ -0,0 +1,3 @@ +x = 1, y = 2, z = 3.000 +Syscalls all look OK, printing local variables... +x = 1, y = 2, z = 3.000 diff --git a/examples/versatileab/reference/syscall-thumbv7a-none-eabi.out b/examples/versatileab/reference/syscall-thumbv7a-none-eabi.out new file mode 100644 index 00000000..4055ecd0 --- /dev/null +++ b/examples/versatileab/reference/syscall-thumbv7a-none-eabi.out @@ -0,0 +1,3 @@ +x = 1, y = 2, z = 3.000 +Syscalls all look OK, printing local variables... +x = 1, y = 2, z = 3.000 diff --git a/examples/versatileab/reference/syscall-thumbv7a-none-eabihf.out b/examples/versatileab/reference/syscall-thumbv7a-none-eabihf.out new file mode 100644 index 00000000..4055ecd0 --- /dev/null +++ b/examples/versatileab/reference/syscall-thumbv7a-none-eabihf.out @@ -0,0 +1,3 @@ +x = 1, y = 2, z = 3.000 +Syscalls all look OK, printing local variables... +x = 1, y = 2, z = 3.000 diff --git a/examples/versatileab/reference/syscall-thumbv7r-none-eabi.out b/examples/versatileab/reference/syscall-thumbv7r-none-eabi.out new file mode 100644 index 00000000..4055ecd0 --- /dev/null +++ b/examples/versatileab/reference/syscall-thumbv7r-none-eabi.out @@ -0,0 +1,3 @@ +x = 1, y = 2, z = 3.000 +Syscalls all look OK, printing local variables... +x = 1, y = 2, z = 3.000 diff --git a/examples/versatileab/reference/syscall-thumbv7r-none-eabihf.out b/examples/versatileab/reference/syscall-thumbv7r-none-eabihf.out new file mode 100644 index 00000000..4055ecd0 --- /dev/null +++ b/examples/versatileab/reference/syscall-thumbv7r-none-eabihf.out @@ -0,0 +1,3 @@ +x = 1, y = 2, z = 3.000 +Syscalls all look OK, printing local variables... +x = 1, y = 2, z = 3.000 diff --git a/examples/versatileab/src/bin/svc-a32.rs b/examples/versatileab/src/bin/svc-a32.rs index 2b8673cb..8cd11b5f 100644 --- a/examples/versatileab/src/bin/svc-a32.rs +++ b/examples/versatileab/src/bin/svc-a32.rs @@ -23,12 +23,13 @@ fn main() -> ! { /// This is our SVC exception handler #[exception(SupervisorCall)] -fn svc_handler(arg: u32) { +fn svc_handler(arg: u32, _frame: &aarch32_rt::Frame) -> u32 { println!("In svc_handler, with arg=0x{:06x}", arg); if arg == 0xABCDEF { // test nested SVC calls do_svc2(); } + 0 } #[instruction_set(arm::a32)] diff --git a/examples/versatileab/src/bin/svc-t32.rs b/examples/versatileab/src/bin/svc-t32.rs index 700c93df..4b4ac74a 100644 --- a/examples/versatileab/src/bin/svc-t32.rs +++ b/examples/versatileab/src/bin/svc-t32.rs @@ -23,12 +23,13 @@ fn main() -> ! { /// This is our SVC exception handler #[exception(SupervisorCall)] -fn svc_handler(arg: u32) { +fn svc_handler(arg: u32, _frame: &aarch32_rt::Frame) -> u32 { println!("In svc_handler, with arg=0x{:06x}", arg); if arg == 0x12 { // test nested SVC calls do_svc2(); } + 0 } #[instruction_set(arm::t32)] diff --git a/examples/versatileab/src/bin/syscall.rs b/examples/versatileab/src/bin/syscall.rs new file mode 100644 index 00000000..2129e7ae --- /dev/null +++ b/examples/versatileab/src/bin/syscall.rs @@ -0,0 +1,107 @@ +//! Syscall example +//! +//! * Syscall 0xF0 takes no arguments and returns: 0x1000_0000 +//! * Syscall 0xF1 takes one argument and returns: 0x1000_0000 + (arg0) +//! * Syscall 0xF2 takes two arguments and returns: 0x1000_0000 + (arg0) + (arg1 << 4) +//! * Syscall 0xF3 takes three arguments and returns: 0x1000_0000 + (arg0) + (arg1 << 4) + (arg2 << 8) +//! * Syscall 0xF4 takes four arguments and returns: 0x1000_0000 + (arg0) + (arg1 << 4) + (arg2 << 8) + (arg3 << 12) +//! * Syscall 0xF5 takes five arguments and returns: 0x1000_0000 + (arg0) + (arg1 << 4) + (arg2 << 8) + (arg3 << 12) + (arg4 << 16) +//! * Syscall 0xF6 takes six arguments and returns: 0x1000_0000 + (arg0) + (arg1 << 4) + (arg2 << 8) + (arg3 << 12) + (arg4 << 16) + (arg5 << 20) + +#![no_std] +#![no_main] + +use aarch32_rt::{entry, exception}; +use semihosting::println; +use versatileab as _; + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[entry] +fn main() -> ! { + let x = 1; + let y = x + 1; + let z = (y as f64) * 1.5; + println!("x = {}, y = {}, z = {:0.3}", x, y, z); + + let retval = aarch32_cpu::svc6!( + 0xF6, + 0xE000_0001, + 0xE000_0002, + 0xE000_0003, + 0xE000_0004, + 0xE000_0005, + 0xE000_0006 + ); + if retval != 0xF065_4321 { + panic!("Wanted 0xF065_4321, got {:08x}", retval); + } + let retval = aarch32_cpu::svc5!( + 0xF5, + 0xE000_0001, + 0xE000_0002, + 0xE000_0003, + 0xE000_0004, + 0xE000_0005 + ); + if retval != 0xF005_4321 { + panic!("Wanted 0xF005_4321, got {:08x}", retval); + } + let retval = aarch32_cpu::svc4!(0xF4, 0xE000_0001, 0xE000_0002, 0xE000_0003, 0xE000_0004); + if retval != 0xF000_4321 { + panic!("Wanted 0xF000_4321, got {:08x}", retval); + } + let retval = aarch32_cpu::svc3!(0xF3, 0xE000_0001, 0xE000_0002, 0xE000_0003); + if retval != 0xF000_0321 { + panic!("Wanted 0xF000_0321, got {:08x}", retval); + } + let retval = aarch32_cpu::svc2!(0xF2, 0xE000_0001, 0xE000_0002); + if retval != 0xF000_0021 { + panic!("Wanted 0xF000_0021, got {:08x}", retval); + } + let retval = aarch32_cpu::svc1!(0xF1, 0xE000_0001); + if retval != 0xF000_0001 { + panic!("Wanted 0xF000_0001, got {:08x}", retval); + } + let retval = aarch32_cpu::svc!(0xF0); + if retval != 0x1000_0000 { + panic!("Wanted 0x1000_0000, got {:08x}", retval); + } + println!("Syscalls all look OK, printing local variables..."); + println!("x = {}, y = {}, z = {:0.3}", x, y, z); + semihosting::process::exit(0); +} + +/// This is our syscall handler +/// +/// We mix together the arguments given in `frame` according to the rules at the +/// top of the file. The arguments are designed to be easy to see in a debugger +/// and the output is designed to be easy to check. +#[exception(SupervisorCall)] +fn svc_handler(arg: u32, frame: &aarch32_rt::Frame) -> u32 { + // println!("Frame: {:08x?}", frame); + if arg & 0xF8 != 0xF0 { + return 0xDEAD_C0DE; + } + let mut output = 0x1000_0000; + if arg >= 0xF6 { + output += frame.r5 << 20; + } + if arg >= 0xF5 { + output += frame.r4 << 16; + } + if arg >= 0xF4 { + output += frame.r3 << 12; + } + if arg >= 0xF3 { + output += frame.r2 << 8; + } + if arg >= 0xF2 { + output += frame.r1 << 4; + } + if arg >= 0xF1 { + output += frame.r0; + } + output +} From 454948ea20fe65385723099d4f96f31c0994d042 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 30 Jan 2026 21:54:36 +0000 Subject: [PATCH 2/4] Fix tests.sh so it copies over output files even if the file doesn't exist yet. --- tests.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests.sh b/tests.sh index 2e947c4f..20d14d6d 100755 --- a/tests.sh +++ b/tests.sh @@ -38,6 +38,10 @@ my_diff() { # output # - Fix the CRLF line endings in the files on disk, because git adds them to # text files. + if [ "${UPDATE_OUT}" == "1" ]; then + # echo "Copying $file_b to $file_a... in $(pwd)" + cp $file_b $file_a + fi if [ ! -f $1 ]; then echo "File $1 is missing?!" return 1 @@ -47,9 +51,6 @@ my_diff() { else diff <(cat $file_a | tr -d '\r') <(cat $file_b | sed 's~\\\\~/~g') result=$? - if [ "${UPDATE_OUT}" == "1" ]; then - cp $file_b $file_a - fi return $result fi } From c32f74de6c21e50555d94c21c18bc678b8256bb3 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 6 Feb 2026 21:36:52 +0000 Subject: [PATCH 3/4] Clean up assembly code a little Add some extra comments, and replace an ldmia with a pop. --- aarch32-rt/src/arch_v4/abort.rs | 4 ++-- aarch32-rt/src/arch_v4/interrupt.rs | 4 ++-- aarch32-rt/src/arch_v4/svc.rs | 4 ++-- aarch32-rt/src/arch_v4/undefined.rs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/aarch32-rt/src/arch_v4/abort.rs b/aarch32-rt/src/arch_v4/abort.rs index f740b201..12e5549c 100644 --- a/aarch32-rt/src/arch_v4/abort.rs +++ b/aarch32-rt/src/arch_v4/abort.rs @@ -34,7 +34,7 @@ core::arch::global_asm!( pop {{ r0-r4, r12 }} // restore preserved registers, dummy value, and alignment amount add sp, r12 // restore SP alignment using R12 pop {{ r12 }} // restore SPSR using R12 - msr spsr_cxsf, r12 // + msr spsr, r12 // pop {{ r12 }} // restore R12 movs pc, lr // return from exception .size _asm_default_data_abort_handler, . - _asm_default_data_abort_handler @@ -75,7 +75,7 @@ core::arch::global_asm!( pop {{ r0-r4, r12 }} // restore preserved registers, dummy value, and alignment amount add sp, r12 // restore SP alignment using R12 pop {{ r12 }} // restore SPSR using R12 - msr spsr_cxsf, r12 // + msr spsr, r12 // pop {{ r12 }} // restore R12 movs pc, lr // return from exception .size _asm_default_prefetch_abort_handler, . - _asm_default_prefetch_abort_handler diff --git a/aarch32-rt/src/arch_v4/interrupt.rs b/aarch32-rt/src/arch_v4/interrupt.rs index 5a2c3444..1cc0b75b 100644 --- a/aarch32-rt/src/arch_v4/interrupt.rs +++ b/aarch32-rt/src/arch_v4/interrupt.rs @@ -35,9 +35,9 @@ core::arch::global_asm!( pop {{ r0-r3, r12, lr }} // restore alignment amount (in LR) and preserved registers add sp, lr // restore SP alignment using LR msr cpsr_c, {irq_mode} // switch back to IRQ mode (with IRQ masked) - ldmia sp!, {{ lr }} // load and restore SPSR using LR + pop {{ lr }} // load and restore SPSR using LR msr spsr, lr // - ldmfd sp!, {{ pc }}^ // return from exception + ldmfd sp!, {{ pc }}^ // return from exception (^ => restore SPSR to CPSR) .size _asm_default_irq_handler, . - _asm_default_irq_handler "#, // sys mode with IRQ masked diff --git a/aarch32-rt/src/arch_v4/svc.rs b/aarch32-rt/src/arch_v4/svc.rs index e941e8ba..005d23f5 100644 --- a/aarch32-rt/src/arch_v4/svc.rs +++ b/aarch32-rt/src/arch_v4/svc.rs @@ -46,8 +46,8 @@ core::arch::global_asm!( mov r0, lr // replace R0 with return value from _svc_handler add sp, r12 // restore SP alignment using R12 pop {{ lr }} // restore SPSR using LR - msr spsr_cxsf, lr // - ldmfd sp!, {{ r12, pc }}^ // restore R12 and return from exception + msr spsr, lr // + ldmfd sp!, {{ r12, pc }}^ // restore R12 and return from exception (^ => restore SPSR to CPSR) .size _asm_default_svc_handler, . - _asm_default_svc_handler "#, t_bit = const { crate::Cpsr::new_with_raw_value(0).with_t(true).raw_value() }, diff --git a/aarch32-rt/src/arch_v4/undefined.rs b/aarch32-rt/src/arch_v4/undefined.rs index 02cb09ac..ccd47131 100644 --- a/aarch32-rt/src/arch_v4/undefined.rs +++ b/aarch32-rt/src/arch_v4/undefined.rs @@ -39,9 +39,9 @@ core::arch::global_asm!( pop {{ r0-r4, r12 }} // restore preserved registers, dummy value, and alignment amount add sp, r12 // restore SP alignment using R12 pop {{ r12 }} // restore SPSR using R12 - msr spsr_cxsf, r12 // + msr spsr, r12 // pop {{ r12 }} // restore R12 - movs pc, lr // return from exception + movs pc, lr // return from exception (movs => restore SPSR to CPSR) .size _asm_default_undefined_handler, . - _asm_default_undefined_handler "#, t_bit = const { crate::Cpsr::new_with_raw_value(0).with_t(true).raw_value() }, From c25d1f08cfc8d787991477cd7ad30537d4812cc0 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sun, 15 Feb 2026 12:48:30 +0000 Subject: [PATCH 4/4] Don't need the LR clobber when doing an svc instruction. The assembly routine restores all registers correctly. --- aarch32-cpu/src/lib.rs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/aarch32-cpu/src/lib.rs b/aarch32-cpu/src/lib.rs index 615726c9..5303b432 100644 --- a/aarch32-cpu/src/lib.rs +++ b/aarch32-cpu/src/lib.rs @@ -50,7 +50,7 @@ macro_rules! svc { ($num:expr) => { { let retval: u32; unsafe { - core::arch::asm!("svc {arg}", arg = const $num, lateout("r0") retval, out("lr") _); + core::arch::asm!("svc {arg}", arg = const $num, lateout("r0") retval); } retval } } @@ -75,8 +75,7 @@ macro_rules! svc1 { // Do the SVCall "svc {arg}", arg = const $num, - inout("r0") arg0 => retval, - out("lr") _); + inout("r0") arg0 => retval); } retval } } @@ -103,8 +102,7 @@ macro_rules! svc2 { "svc {arg}", arg = const $num, inout("r0") arg0 => retval, - in("r1") arg1, - out("lr") _); + in("r1") arg1); } retval } } @@ -133,8 +131,7 @@ macro_rules! svc3 { arg = const $num, inout("r0") arg0 => retval, in("r1") arg1, - in("r2") arg2, - out("lr") _); + in("r2") arg2); } retval } } @@ -165,8 +162,7 @@ macro_rules! svc4 { inout("r0") arg0 => retval, in("r1") arg1, in("r2") arg2, - in("r3") arg3, - out("lr") _); + in("r3") arg3); } retval } } @@ -199,8 +195,7 @@ macro_rules! svc5 { in("r1") arg1, in("r2") arg2, in("r3") arg3, - in("r4") arg4, - out("lr") _); + in("r4") arg4); } retval } } @@ -235,8 +230,7 @@ macro_rules! svc6 { in("r2") arg2, in("r3") arg3, in("r4") arg4, - in("r5") arg5, - out("lr") _); + in("r5") arg5); } retval } }