diff --git a/aarch32-cpu/src/lib.rs b/aarch32-cpu/src/lib.rs index 2ffdb32..8e70587 100644 --- a/aarch32-cpu/src/lib.rs +++ b/aarch32-cpu/src/lib.rs @@ -35,15 +35,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 e3f3f73..869946e 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 793d029..12e5549 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, 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, 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 c8d1888..1cc0b75 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) + pop {{ lr }} // load and restore SPSR using LR + msr spsr, lr // + 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 5264732..005d23f 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, 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 9297ee1..ccd4713 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, r12 // + pop {{ r12 }} // restore R12 + 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() }, diff --git a/aarch32-rt/src/arch_v7/abort.rs b/aarch32-rt/src/arch_v7/abort.rs index f08591e..8a2093d 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 bd3869c..dab9b3c 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 000fa69..f303a48 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 9fb0c6b..03e1312 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 ecf05f6..1393eb1 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 0000000..4055ecd --- /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 0000000..4055ecd --- /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 d4deece..c7ed246 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 47e7471..c774dc6 100644 --- a/examples/mps3-an536/src/bin/svc-t32.rs +++ b/examples/mps3-an536/src/bin/svc-t32.rs @@ -16,51 +16,28 @@ fn main() -> ! { let y = x + 1; let z = (y as f64) * 1.5; println!("x = {}, y = {}, z = {:0.3}", x, y, z); - unsafe { - svc12_from_t32(); - } + do_svc1(); println!("x = {}, y = {}, z = {:0.3}", x, y, z); semihosting::process::exit(0); } /// 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 - unsafe { - svc34_from_t32(); - } + do_svc2(); } + 0 } -// These functions are written in assembly -extern "C" { - fn svc12_from_t32(); - fn svc34_from_t32(); +#[instruction_set(arm::t32)] +fn do_svc1() { + aarch32_cpu::svc!(0x12); } -core::arch::global_asm!( - r#" - // fn svc12_from_t32(); - .thumb - .global svc12_from_t32 - .type svc12_from_t32, %function - svc12_from_t32: - push {{ r7, lr }} - svc 0x12 - pop {{ r7, pc }} - .size svc12_from_t32, . - svc12_from_t32 - - // fn svc34_from_t32(); - .thumb - .global svc34_from_t32 - .type svc34_from_t32, %function - svc34_from_t32: - push {{ r7, lr }} - svc 0x34 - pop {{ r7, pc }} - .size svc34_from_t32, . - svc34_from_t32 -"# -); +#[instruction_set(arm::t32)] +fn do_svc2() { + aarch32_cpu::svc!(0x34); +} diff --git a/examples/mps3-an536/src/bin/syscall.rs b/examples/mps3-an536/src/bin/syscall.rs new file mode 100644 index 0000000..71ca20e --- /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 0000000..4055ecd --- /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 0000000..4055ecd --- /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 0000000..4055ecd --- /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 0000000..4055ecd --- /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 0000000..4055ecd --- /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 0000000..4055ecd --- /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 0000000..4055ecd --- /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 0000000..4055ecd --- /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 0000000..4055ecd --- /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 0000000..4055ecd --- /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 0000000..4055ecd --- /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 0000000..4055ecd --- /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 0000000..4055ecd --- /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 0000000..4055ecd --- /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 b660067..49c4425 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 6b22c17..86017d1 100644 --- a/examples/versatileab/src/bin/svc-t32.rs +++ b/examples/versatileab/src/bin/svc-t32.rs @@ -16,51 +16,28 @@ fn main() -> ! { let y = x + 1; let z = (y as f64) * 1.5; println!("x = {}, y = {}, z = {:0.3}", x, y, z); - unsafe { - svc12_from_t32(); - } + do_svc1(); println!("x = {}, y = {}, z = {:0.3}", x, y, z); semihosting::process::exit(0); } /// 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 - unsafe { - svc34_from_t32(); - } + do_svc2(); } + 0 } -// These functions are written in assembly -extern "C" { - fn svc12_from_t32(); - fn svc34_from_t32(); +#[instruction_set(arm::t32)] +fn do_svc1() { + aarch32_cpu::svc!(0x12); } -core::arch::global_asm!( - r#" - // fn svc12_from_t32(); - .thumb - .global svc12_from_t32 - .type svc12_from_t32, %function - svc12_from_t32: - push {{ r7, lr }} - svc 0x12 - pop {{ r7, pc }} - .size svc12_from_t32, . - svc12_from_t32 - - // fn svc34_from_t32(); - .thumb - .global svc34_from_t32 - .type svc34_from_t32, %function - svc34_from_t32: - push {{ r7, lr }} - svc 0x34 - pop {{ r7, pc }} - .size svc34_from_t32, . - svc34_from_t32 -"# -); +#[instruction_set(arm::t32)] +fn do_svc2() { + aarch32_cpu::svc!(0x34); +} diff --git a/examples/versatileab/src/bin/syscall.rs b/examples/versatileab/src/bin/syscall.rs new file mode 100644 index 0000000..2129e7a --- /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 +} diff --git a/tests.sh b/tests.sh index 2e947c4..20d14d6 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 }