From 9c541dcb6ad2a1469ce76b05270b4ddc12289e66 Mon Sep 17 00:00:00 2001 From: Charly Castes Date: Wed, 30 Oct 2024 18:55:12 +0100 Subject: [PATCH] [test] Create test_helpers crate to group all clint test code The new crate can be extended in the future to hold more test helpers, such as macros to generate proper interrupt handlers. For now it is limited to clint functionalities and assumes the QEMU virt platform (it is not meant to be a general-purpose library). --- Cargo.lock | 9 ++++ Cargo.toml | 2 + crates/test_helpers/Cargo.toml | 6 +++ crates/test_helpers/src/clint.rs | 42 +++++++++++++++++++ crates/test_helpers/src/lib.rs | 8 ++++ firmware/clint_interrupt/Cargo.toml | 1 + firmware/clint_interrupt/main.rs | 27 ++---------- firmware/clint_interrupt_multihart/Cargo.toml | 1 + firmware/clint_interrupt_multihart/main.rs | 36 +++++----------- firmware/clint_interrupt_priority/Cargo.toml | 1 + firmware/clint_interrupt_priority/main.rs | 42 +++---------------- firmware/interrupt/Cargo.toml | 1 + firmware/interrupt/main.rs | 28 +------------ firmware/vectored_mtvec/Cargo.toml | 1 + firmware/vectored_mtvec/main.rs | 18 +------- 15 files changed, 94 insertions(+), 129 deletions(-) create mode 100644 crates/test_helpers/Cargo.toml create mode 100644 crates/test_helpers/src/clint.rs create mode 100644 crates/test_helpers/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 5c6cb710..4d2ebec6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,6 +120,7 @@ version = "0.1.0" dependencies = [ "log", "miralis_abi", + "test_helpers", ] [[package]] @@ -128,6 +129,7 @@ version = "0.1.0" dependencies = [ "log", "miralis_abi", + "test_helpers", ] [[package]] @@ -136,6 +138,7 @@ version = "0.1.0" dependencies = [ "log", "miralis_abi", + "test_helpers", ] [[package]] @@ -250,6 +253,7 @@ version = "0.1.0" dependencies = [ "log", "miralis_abi", + "test_helpers", ] [[package]] @@ -452,6 +456,10 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "test_helpers" +version = "0.1.0" + [[package]] name = "test_protect_payload_firmware" version = "0.1.0" @@ -536,6 +544,7 @@ version = "0.1.0" dependencies = [ "log", "miralis_abi", + "test_helpers", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 37dd9911..e99ca822 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,11 +31,13 @@ members = [ # Payload "payload/hello_world", "payload/test_protect_payload_payload", + # Crates "crates/abi", "crates/core", "crates/config_helpers", "crates/config_select", + "crates/test_helpers", # Tooling "runner", diff --git a/crates/test_helpers/Cargo.toml b/crates/test_helpers/Cargo.toml new file mode 100644 index 00000000..34bea08a --- /dev/null +++ b/crates/test_helpers/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "test_helpers" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/crates/test_helpers/src/clint.rs b/crates/test_helpers/src/clint.rs new file mode 100644 index 00000000..c8d4f4ba --- /dev/null +++ b/crates/test_helpers/src/clint.rs @@ -0,0 +1,42 @@ +//! Client utilities +//! +//! The functions exposed in this modules assumes the CLINT is located at the same addresses as on +//! the QEMU virt platform, which we use for out tests. + +/// Clint base, assuming a QEMU virt-like layout. +const CLINT_BASE: usize = 0x2000000; +const MTIME_OFFSET: usize = 0xBFF8; +const MTIMECMP_OFFSET: usize = 0x4000; + +/// Get the current mtime value +pub fn read_mtime() -> usize { + let mtime_ptr = (CLINT_BASE + MTIME_OFFSET) as *const usize; + unsafe { mtime_ptr.read_volatile() } +} + +/// Set mtimecmp deadline in the future +pub fn set_mtimecmp_deadline(delta: usize, hart: usize) { + let current_mtime = read_mtime(); + let future_time = current_mtime.saturating_add(delta); + + let mtimecmp_ptr = (CLINT_BASE + MTIMECMP_OFFSET + 8 * hart) as *mut usize; + unsafe { + mtimecmp_ptr.write_volatile(future_time); + } +} + +/// Send an MSI to the given hart. +pub fn send_msi(hart: usize) { + let msip_ptr = (CLINT_BASE + 4 * hart) as *mut u32; + unsafe { + msip_ptr.write_volatile(1); + } +} + +/// Clear MSI for the given hart. +pub fn clear_msi(hart: usize) { + let msip_ptr = (CLINT_BASE + 4 * hart) as *mut u32; + unsafe { + msip_ptr.write_volatile(0); + } +} diff --git a/crates/test_helpers/src/lib.rs b/crates/test_helpers/src/lib.rs new file mode 100644 index 00000000..a02bbc9b --- /dev/null +++ b/crates/test_helpers/src/lib.rs @@ -0,0 +1,8 @@ +//! Test helpers +//! +//! Utility functions, macros, and contants for the Miralis integration tests (firmware and +//! payloads). + +#![no_std] + +pub mod clint; diff --git a/firmware/clint_interrupt/Cargo.toml b/firmware/clint_interrupt/Cargo.toml index 4137dd87..19a88fd8 100644 --- a/firmware/clint_interrupt/Cargo.toml +++ b/firmware/clint_interrupt/Cargo.toml @@ -9,4 +9,5 @@ path = "main.rs" [dependencies] miralis_abi = { path = "../../crates/abi" } +test_helpers = { path = "../../crates/test_helpers" } log = { workspace = true } diff --git a/firmware/clint_interrupt/main.rs b/firmware/clint_interrupt/main.rs index f5e8b267..3d6ad45e 100644 --- a/firmware/clint_interrupt/main.rs +++ b/firmware/clint_interrupt/main.rs @@ -6,6 +6,7 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use core::usize; use miralis_abi::{setup_binary, success}; +use test_helpers::clint::{self, set_mtimecmp_deadline}; setup_binary!(main); @@ -16,7 +17,7 @@ setup_binary!(main); /// 4. If the virtual interrupt is not cleared, the firmware will trap on each execution cycle. fn main() -> ! { // Setup a timer deadline - set_mtimecmp_future_value(0); + clint::set_mtimecmp_deadline(0, 0); // Configure trap handler and enable interrupts unsafe { @@ -44,31 +45,9 @@ unsafe fn enable_interrupts() { // ———————————————————————————— Timer Interrupt ————————————————————————————— // -// Clint -const CLINT_BASE: usize = 0x2000000; -const MTIME_OFFSET: usize = 0xBFF8; -const MTIMECMP_OFFSET: usize = 0x4000; - static INTERRUPT_COUNTER: AtomicUsize = AtomicUsize::new(0); const REPETITIONS: usize = 10; -// Get the current mtime value -fn get_current_mtime() -> usize { - let mtime_ptr = (CLINT_BASE + MTIME_OFFSET) as *const usize; - unsafe { mtime_ptr.read_volatile() } -} - -// Set mtimecmp value in the future -fn set_mtimecmp_future_value(value: usize) { - let current_mtime = get_current_mtime(); - let future_time = current_mtime.saturating_add(value); - - let mtimecmp_ptr = (CLINT_BASE + MTIMECMP_OFFSET) as *mut usize; - unsafe { - mtimecmp_ptr.write_volatile(future_time); - } -} - /// This function should be called from the raw trap handler extern "C" fn trap_handler() { let mip: usize; @@ -120,7 +99,7 @@ fn handle_timer_interrupt() { panic!("Expected another timer interrupt, but got nothing"); } else { // We got all the timers we expected! - set_mtimecmp_future_value(usize::MAX); + set_mtimecmp_deadline(usize::MAX, 0); log::trace!("Now timer should clear, re-enabling interrupts"); unsafe { enable_interrupts() }; diff --git a/firmware/clint_interrupt_multihart/Cargo.toml b/firmware/clint_interrupt_multihart/Cargo.toml index b6525a33..bfb41613 100644 --- a/firmware/clint_interrupt_multihart/Cargo.toml +++ b/firmware/clint_interrupt_multihart/Cargo.toml @@ -9,4 +9,5 @@ path = "main.rs" [dependencies] miralis_abi = { path = "../../crates/abi" } +test_helpers = { path = "../../crates/test_helpers" } log = { workspace = true } diff --git a/firmware/clint_interrupt_multihart/main.rs b/firmware/clint_interrupt_multihart/main.rs index 9251611b..94a3578e 100644 --- a/firmware/clint_interrupt_multihart/main.rs +++ b/firmware/clint_interrupt_multihart/main.rs @@ -4,7 +4,8 @@ use core::arch::{asm, global_asm}; use core::usize; -use miralis_abi::{failure, setup_binary, success}; +use miralis_abi::{setup_binary, success}; +use test_helpers::clint; setup_binary!(main); @@ -42,32 +43,19 @@ fn main() -> ! { msie = in(reg) 0x8, ); } - success() + panic!("Expected an MSI interrupt"); } 1 => { - set_msip(0, 1); + clint::send_msi(0); loop { - unsafe { asm!("nop") }; + core::hint::spin_loop(); } } - _ => failure(), + _ => panic!("Invalid hart ID"), } } -// ———————————————————————————— Timer Interrupt ————————————————————————————— // - -const CLINT_BASE: usize = 0x2000000; -const MSIP_WIDTH: usize = 0x4; - -// Set msip bit pending for other hart -fn set_msip(hart: usize, value: u32) { - log::debug!("Set interrupt for hart {:}", hart); - - let msip_ptr = (CLINT_BASE + MSIP_WIDTH * hart) as *mut u32; - unsafe { - msip_ptr.write_volatile(value); - } -} +// —————————————————————————————— Trap Handler —————————————————————————————— // /// This function should be called from the raw trap handler extern "C" fn trap_handler() { @@ -80,17 +68,13 @@ extern "C" fn trap_handler() { } if mcause == 0x8000000000000003 { - set_msip(0, 0); - unsafe { - asm!("mret",); - } + clint::clear_msi(0); + success(); } else { - failure(); + panic!("Unexpected mcause: 0x{:x}", mcause); } } -// —————————————————————————————— Trap Handler —————————————————————————————— // - global_asm!( r#" .text diff --git a/firmware/clint_interrupt_priority/Cargo.toml b/firmware/clint_interrupt_priority/Cargo.toml index 45b78c10..32e69a06 100644 --- a/firmware/clint_interrupt_priority/Cargo.toml +++ b/firmware/clint_interrupt_priority/Cargo.toml @@ -9,4 +9,5 @@ path = "main.rs" [dependencies] miralis_abi = { path = "../../crates/abi" } +test_helpers = { path = "../../crates/test_helpers" } log = { workspace = true } diff --git a/firmware/clint_interrupt_priority/main.rs b/firmware/clint_interrupt_priority/main.rs index e06340a7..94340901 100644 --- a/firmware/clint_interrupt_priority/main.rs +++ b/firmware/clint_interrupt_priority/main.rs @@ -5,6 +5,7 @@ use core::arch::{asm, global_asm}; use core::usize; use miralis_abi::{failure, setup_binary, success}; +use test_helpers::clint; setup_binary!(main); @@ -14,8 +15,8 @@ setup_binary!(main); /// memory-mapped registers. fn main() -> ! { // Set up two interrupts simultaniously - set_msip(0, 1); - set_mtimecmp_future_value(0); + clint::send_msi(0); + clint::set_mtimecmp_deadline(0, 0); // MTIP is not guaranteed to be reflected instantly, so wait for a bit for _ in 1..10000 { @@ -59,39 +60,6 @@ fn main() -> ! { // ———————————————————————————— Timer Interrupt ————————————————————————————— // -const CLINT_BASE: usize = 0x2000000; -const MTIME_OFFSET: usize = 0xBFF8; -const MTIMECMP_OFFSET: usize = 0x4000; -const MSIP_WIDTH: usize = 0x4; - -// Get the current mtime value -fn get_current_mtime() -> usize { - let mtime_ptr = (CLINT_BASE + MTIME_OFFSET) as *const usize; - unsafe { mtime_ptr.read_volatile() } -} - -// Set mtimecmp value in the future -fn set_mtimecmp_future_value(value: usize) { - log::trace!("Updated timer"); - let current_mtime = get_current_mtime(); - let future_time = current_mtime.saturating_add(value); - - let mtimecmp_ptr = (CLINT_BASE + MTIMECMP_OFFSET) as *mut usize; - unsafe { - mtimecmp_ptr.write_volatile(future_time); - } -} - -// Set msip bit pending for other hart -fn set_msip(hart: usize, value: u32) { - log::trace!("Set interrupt for hart {:}", hart); - - let msip_ptr = (CLINT_BASE + MSIP_WIDTH * hart) as *mut u32; - unsafe { - msip_ptr.write_volatile(value); - } -} - /// This function should be called from the raw trap handler extern "C" fn trap_handler() { let mip: usize; @@ -116,10 +84,10 @@ extern "C" fn trap_handler() { if mcause == 0x8000000000000003 { log::trace!("Cleared MSIP"); - set_msip(0, 0); + clint::clear_msi(0); } else if mcause == 0x8000000000000007 { log::trace!("Cleared MTIP"); - set_mtimecmp_future_value(usize::MAX); + clint::set_mtimecmp_deadline(usize::MAX, 0); } else { log::warn!("Received non-interrupt trap, {:x}", mcause); failure(); diff --git a/firmware/interrupt/Cargo.toml b/firmware/interrupt/Cargo.toml index 7ebaa8fb..903ca5a8 100644 --- a/firmware/interrupt/Cargo.toml +++ b/firmware/interrupt/Cargo.toml @@ -11,4 +11,5 @@ path = "main.rs" [dependencies] miralis_abi = { path = "../../crates/abi" } +test_helpers = { path = "../../crates/test_helpers" } log = { workspace = true } diff --git a/firmware/interrupt/main.rs b/firmware/interrupt/main.rs index 068a826c..5214dd1b 100644 --- a/firmware/interrupt/main.rs +++ b/firmware/interrupt/main.rs @@ -4,6 +4,7 @@ use core::arch::{asm, global_asm}; use miralis_abi::{failure, setup_binary, success}; +use test_helpers::clint; setup_binary!(main); @@ -83,31 +84,6 @@ fn test_sie_by_mie() { // ———————————————————————————— Timer Interrupt ————————————————————————————— // -const CLINT_BASE: usize = 0x2000000; -const MTIME_OFFSET: usize = 0xBFF8; -const MTIMECMP_OFFSET: usize = 0x4000; - -// Get the current mtime value -fn get_current_mtime() -> usize { - let mtime_ptr = (CLINT_BASE + MTIME_OFFSET) as *const usize; - unsafe { mtime_ptr.read_volatile() } -} - -// Set mtimecmp value in the future -fn set_mtimecmp_future_value() { - let current_mtime = get_current_mtime(); - let future_time = current_mtime + 10000; - - let mtimecmp_ptr = (CLINT_BASE + MTIMECMP_OFFSET) as *mut usize; // TODO: add support for different harts - unsafe { - mtimecmp_ptr.write_volatile(future_time); - } - - // Read back the value to verify - let read_back = unsafe { mtimecmp_ptr.read_volatile() }; - assert_eq!(read_back, future_time, "mtimecmp set correctly"); -} - #[allow(unreachable_code)] fn test_timer_interrupts() -> ! { // Configure trap handler and enable interrupts @@ -123,7 +99,7 @@ fn test_timer_interrupts() -> ! { } // Setup a timer deadline - set_mtimecmp_future_value(); + clint::set_mtimecmp_deadline(10_000, 0); // Wait for an interrupt loop { diff --git a/firmware/vectored_mtvec/Cargo.toml b/firmware/vectored_mtvec/Cargo.toml index 226283df..cd9506ed 100644 --- a/firmware/vectored_mtvec/Cargo.toml +++ b/firmware/vectored_mtvec/Cargo.toml @@ -11,4 +11,5 @@ path = "main.rs" [dependencies] miralis_abi = { path = "../../crates/abi" } +test_helpers = { path = "../../crates/test_helpers" } log = { workspace = true } diff --git a/firmware/vectored_mtvec/main.rs b/firmware/vectored_mtvec/main.rs index e1842d33..14487827 100644 --- a/firmware/vectored_mtvec/main.rs +++ b/firmware/vectored_mtvec/main.rs @@ -5,6 +5,7 @@ use core::arch::{asm, global_asm}; use core::hint::spin_loop; use miralis_abi::{failure, setup_binary, success}; +use test_helpers::clint; setup_binary!(main); @@ -27,7 +28,7 @@ fn main() -> ! { } // Setup a timer deadline in the past to trap directly - set_mtimecmp_future_value(); + clint::set_mtimecmp_deadline(0, 0); for _ in 0..10_000 { spin_loop() @@ -38,21 +39,6 @@ fn main() -> ! { failure() } -// ———————————————————————————— Timer Interrupt ————————————————————————————— // - -const CLINT_BASE: usize = 0x2000000; -const MTIMECMP_OFFSET: usize = 0x4000; - -// Set mtimecmp value in the future -fn set_mtimecmp_future_value() { - let future_time = 0; - - let mtimecmp_ptr = (CLINT_BASE + MTIMECMP_OFFSET) as *mut usize; // TODO: add support for different harts - unsafe { - mtimecmp_ptr.write_volatile(future_time); - } -} - // —————————————————————————————— Trap Handler —————————————————————————————— // /// This function should be called from the raw trap handler