From f102017b28c27642c50ad04a748d13e05dc35655 Mon Sep 17 00:00:00 2001 From: Anthony Rocha <116300062+rusty1968@users.noreply.github.com> Date: Mon, 21 Oct 2024 18:38:59 -0700 Subject: [PATCH] Enhance PAUSER tests (#1667) * - Modify HwModel trait so it boots with a set of five valid pausers for all supported model types. - Add a negative test to ascertain that , when FpgaRealTime is selected, a SIGBUS is raised if an invalid PAUSER tries to access the mailbox. * Use a Vec to store valid pausers * Add SocManager trait definition to the newly created test --- hw-model/Cargo.toml | 6 + hw-model/src/bin/fpga_realtime_mbox_pauser.rs | 116 ++++++++++++++++++ hw-model/src/lib.rs | 27 ++-- hw-model/tests/model_tests.rs | 48 ++++++++ 4 files changed, 186 insertions(+), 11 deletions(-) create mode 100644 hw-model/src/bin/fpga_realtime_mbox_pauser.rs diff --git a/hw-model/Cargo.toml b/hw-model/Cargo.toml index c16bbd52bf..11e7150b3d 100644 --- a/hw-model/Cargo.toml +++ b/hw-model/Cargo.toml @@ -41,3 +41,9 @@ caliptra-image-types.workspace = true caliptra-builder.workspace = true caliptra-registers.workspace = true caliptra-test-harness-types.workspace = true +nix.workspace = true + +[[bin]] +name = "fpga_realtime_mbox_pauser" +path = "src/bin/fpga_realtime_mbox_pauser.rs" +required-features = ["fpga_realtime", "itrng"] diff --git a/hw-model/src/bin/fpga_realtime_mbox_pauser.rs b/hw-model/src/bin/fpga_realtime_mbox_pauser.rs new file mode 100644 index 0000000000..df1a8b1554 --- /dev/null +++ b/hw-model/src/bin/fpga_realtime_mbox_pauser.rs @@ -0,0 +1,116 @@ +// Licensed under the Apache-2.0 license + +use caliptra_hw_model::{mmio::Rv32GenMmio, HwModel, InitParams}; +use nix::sys::signal; +use nix::sys::signal::{SaFlags, SigAction, SigHandler, SigSet}; +use std::process::exit; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::thread; +use std::time::Duration; + +use caliptra_api::soc_mgr::SocManager; +use caliptra_registers::soc_ifc; + +fn gen_image_hi() -> Vec { + let rv32_gen = Rv32GenMmio::new(); + let soc_ifc = + unsafe { soc_ifc::RegisterBlock::new_with_mmio(0x3003_0000 as *mut u32, &rv32_gen) }; + soc_ifc + .cptra_generic_output_wires() + .at(0) + .write(|_| b'h'.into()); + soc_ifc + .cptra_generic_output_wires() + .at(0) + .write(|_| b'i'.into()); + soc_ifc + .cptra_generic_output_wires() + .at(0) + .write(|_| 0x100 | u32::from(b'i')); + soc_ifc.cptra_generic_output_wires().at(0).write(|_| 0xff); + rv32_gen.into_inner().empty_loop().build() +} + +// Atomic flag to indicate if SIGBUS was received +static SIGBUS_RECEIVED: AtomicBool = AtomicBool::new(false); + +// Signal handler function +extern "C" fn handle_sigbus(_: i32) { + SIGBUS_RECEIVED.store(true, Ordering::SeqCst); +} + +fn main() { + println!("Setup signal handler..."); + // Define the signal action + let sig_action = SigAction::new( + SigHandler::Handler(handle_sigbus), + SaFlags::empty(), + SigSet::empty(), + ); + + // Set the signal handler for SIGBUS + unsafe { + signal::sigaction(signal::Signal::SIGBUS, &sig_action) + .expect("Failed to set SIGBUS handler"); + } + + // Spawn a thread that causes a SIGBUS error + thread::spawn(|| { + // Sleep for a short duration to ensure the main thread is ready + thread::sleep(Duration::from_secs(2)); + + let mut model = caliptra_hw_model::new_unbooted(InitParams { + rom: &gen_image_hi(), + ..Default::default() + }) + .unwrap(); + + model.soc_ifc().cptra_fuse_wr_done().write(|w| w.done(true)); + model.soc_ifc().cptra_bootfsm_go().write(|w| w.go(true)); + + // Set up the PAUSER as valid for the mailbox (using index 0) + model + .soc_ifc() + .cptra_mbox_valid_pauser() + .at(0) + .write(|_| 0x1); + model + .soc_ifc() + .cptra_mbox_pauser_lock() + .at(0) + .write(|w| w.lock(true)); + + // Set the PAUSER to something invalid + model.set_apb_pauser(0x2); + + // The accesses below trigger sigbus + assert!(!model.soc_mbox().lock().read().lock()); + // Should continue to read 0 because the reads are being blocked by valid PAUSER + assert!(!model.soc_mbox().lock().read().lock()); + + // Set the PAUSER back to valid + model.set_apb_pauser(0x1); + + // Should read 0 the first time still for lock available + assert!(!model.soc_mbox().lock().read().lock()); + // Should read 1 now for lock taken + assert!(model.soc_mbox().lock().read().lock()); + + model.soc_mbox().cmd().write(|_| 4242); + + assert_eq!(model.soc_mbox().cmd().read(), 4242); + // Continue with the rest of your program + println!("Continuing execution..."); + }); + + // Simulate some work in the main thread + loop { + if SIGBUS_RECEIVED.load(Ordering::SeqCst) { + println!("Received SIGBUS signal!"); + // Handle the SIGBUS signal here + exit(42); + } + println!("Working..."); + thread::sleep(Duration::from_secs(1)); + } +} diff --git a/hw-model/src/lib.rs b/hw-model/src/lib.rs index c6b851b472..77f2a28f75 100644 --- a/hw-model/src/lib.rs +++ b/hw-model/src/lib.rs @@ -81,6 +81,8 @@ pub type DefaultHwModel = ModelVerilated; #[cfg(feature = "fpga_realtime")] pub type DefaultHwModel = ModelFpgaRealtime; +pub const DEFAULT_APB_PAUSER: u32 = 0x01; + /// Constructs an HwModel based on the cargo features and environment /// variables. Most test cases that need to construct a HwModel should use this /// function over HwModel::new_unbooted(). @@ -252,7 +254,7 @@ pub struct BootParams<'a> { pub initial_dbg_manuf_service_reg: u32, pub initial_repcnt_thresh_reg: Option, pub initial_adaptp_thresh_reg: Option, - pub valid_pauser: u32, + pub valid_pauser: Vec, pub wdt_timeout_cycles: u64, } @@ -264,7 +266,7 @@ impl<'a> Default for BootParams<'a> { initial_dbg_manuf_service_reg: Default::default(), initial_repcnt_thresh_reg: Default::default(), initial_adaptp_thresh_reg: Default::default(), - valid_pauser: 0x1, + valid_pauser: vec![0, 1, 2, 3, 4], wdt_timeout_cycles: EXPECTED_CALIPTRA_BOOT_TIME_IN_CYCLES, } } @@ -514,15 +516,18 @@ pub trait HwModel: SocManager { .write(|_| reg); } - // Set up the PAUSER as valid for the mailbox (using index 0) - self.soc_ifc() - .cptra_mbox_valid_pauser() - .at(0) - .write(|_| boot_params.valid_pauser); - self.soc_ifc() - .cptra_mbox_pauser_lock() - .at(0) - .write(|w| w.lock(true)); + { + for idx in 0..boot_params.valid_pauser.len() { + self.soc_ifc() + .cptra_mbox_valid_pauser() + .at(idx) + .write(|_| boot_params.valid_pauser[idx]); + self.soc_ifc() + .cptra_mbox_pauser_lock() + .at(idx) + .write(|w| w.lock(true)); + } + } writeln!(self.output().logger(), "writing to cptra_bootfsm_go")?; self.soc_ifc().cptra_bootfsm_go().write(|w| w.go(true)); diff --git a/hw-model/tests/model_tests.rs b/hw-model/tests/model_tests.rs index 6732bffd47..25c8ac5cb9 100644 --- a/hw-model/tests/model_tests.rs +++ b/hw-model/tests/model_tests.rs @@ -6,6 +6,27 @@ use caliptra_hw_model::{BootParams, DefaultHwModel, HwModel, InitParams}; use caliptra_hw_model_types::ErrorInjectionMode; use caliptra_test_harness_types as harness; +#[cfg(feature = "fpga_realtime")] +use std::process::{Child, Command}; +#[cfg(feature = "fpga_realtime")] +use std::thread; +#[cfg(feature = "fpga_realtime")] +use std::time::{Duration, Instant}; + +#[cfg(feature = "fpga_realtime")] +fn wait_with_timeout(child: &mut Child, timeout: Duration) -> Option { + let start = Instant::now(); + while start.elapsed() < timeout { + match child.try_wait() { + Ok(Some(status)) => return status.code(), + Ok(None) => thread::sleep(Duration::from_millis(100)), // Check every 100ms + Err(_) => return None, + } + } + let _ = child.kill(); + None +} + fn run_fw_elf(elf: &[u8]) -> DefaultHwModel { let rom = caliptra_builder::elf2rom(elf).unwrap(); let model = caliptra_hw_model::new( @@ -272,3 +293,30 @@ fn test_pcr_extend() { model.step_until_exit_success().unwrap(); } + +#[test] +#[cfg(feature = "fpga_realtime")] +fn test_mbox_pauser_sigbus() { + fn find_binary_path() -> Option<&'static str> { + // Use this path when running on github. + const TEST_BIN_PATH_SQUASHFS:&str = "/tmp/caliptra-test-binaries/target/aarch64-unknown-linux-gnu/release/fpga_realtime_mbox_pauser"; + + const TEST_BIN_PATH: &str = env!("CARGO_BIN_EXE_fpga_realtime_mbox_pauser"); + if std::path::Path::new(TEST_BIN_PATH_SQUASHFS).exists() { + Some(TEST_BIN_PATH_SQUASHFS) + } else if std::path::Path::new(TEST_BIN_PATH).exists() { + Some(TEST_BIN_PATH) + } else { + None + } + } + + let mut child = Command::new(find_binary_path().unwrap()) + .spawn() + .expect("Failed to start mbox_pauser test utility"); + + let exit_code = wait_with_timeout(&mut child, Duration::from_secs(120)); + + // Check if the exit code is 42 + assert_eq!(exit_code, Some(42)); +}