diff --git a/Cargo.lock b/Cargo.lock index 94eb2c16cd..db66f32c6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -459,22 +459,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "caliptra-fmc-mock-rt" -version = "0.1.0" -dependencies = [ - "caliptra-builder", - "caliptra-cpu", - "caliptra-drivers", - "caliptra-gen-linker-scripts", - "caliptra-registers", - "caliptra_common", - "cfg-if 1.0.0", - "ufmt", - "ureg", - "zerocopy", -] - [[package]] name = "caliptra-gen-linker-scripts" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index b4676b842c..481a49e20e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,6 @@ members = [ "drivers/test-fw", "drivers/test-fw/scripts/vector_gen", "fmc", - "fmc/test-fw/test-rt", "hw/1.0/registers", "hw/latest/registers", "hw/verilated", diff --git a/builder/src/firmware.rs b/builder/src/firmware.rs index 4ec34d497c..bd454d2266 100644 --- a/builder/src/firmware.rs +++ b/builder/src/firmware.rs @@ -339,21 +339,6 @@ pub mod rom_tests { }; } -pub mod fmc_tests { - use super::*; - - pub const MOCK_RT_WITH_UART: FwId = FwId { - crate_name: "caliptra-fmc-mock-rt", - bin_name: "caliptra-fmc-mock-rt", - features: &["emu"], - }; - pub const MOCK_RT_INTERACTIVE: FwId = FwId { - crate_name: "caliptra-fmc-mock-rt", - bin_name: "caliptra-fmc-mock-rt", - features: &["emu", "interactive_test"], - }; -} - pub mod runtime_tests { use super::*; @@ -377,6 +362,11 @@ pub mod runtime_tests { bin_name: "persistent_rt", ..RUNTIME_TEST_FWID_BASE }; + + pub const MOCK_RT_INTERACTIVE: FwId = FwId { + bin_name: "mock_rt_interact", + ..RUNTIME_TEST_FWID_BASE + }; } pub const REGISTERED_FW: &[&FwId] = &[ @@ -434,9 +424,8 @@ pub const REGISTERED_FW: &[&FwId] = &[ &rom_tests::TEST_FMC_INTERACTIVE, &rom_tests::FAKE_TEST_FMC_INTERACTIVE, &rom_tests::TEST_RT_WITH_UART, - &fmc_tests::MOCK_RT_WITH_UART, - &fmc_tests::MOCK_RT_INTERACTIVE, &runtime_tests::BOOT, &runtime_tests::MBOX, &runtime_tests::PERSISTENT_RT, + &runtime_tests::MOCK_RT_INTERACTIVE, ]; diff --git a/fmc/test-fw/test-rt/Cargo.toml b/fmc/test-fw/test-rt/Cargo.toml deleted file mode 100644 index 753c1ee9e8..0000000000 --- a/fmc/test-fw/test-rt/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -# Licensed under the Apache-2.0 license - -[package] -name = "caliptra-fmc-mock-rt" -version = "0.1.0" -edition = "2021" - -[dependencies] -caliptra_common = { workspace = true, default-features = false } -caliptra-cpu.workspace = true -caliptra-drivers = { workspace = true, features = ["no-cfi"] } -caliptra-registers.workspace = true -ufmt.workspace = true -zerocopy.workspace = true -ureg.workspace = true - -[build-dependencies] -cfg-if.workspace = true -caliptra_common = { workspace = true, default-features = false } -caliptra-gen-linker-scripts.workspace = true - -[dev-dependencies] -caliptra-builder.workspace = true - -[features] -default = ["std"] -emu = ["caliptra_common/emu", "caliptra-drivers/emu"] -riscv = ["caliptra-cpu/riscv"] -std = ["ufmt/std", "caliptra_common/std"] -interactive_test = [] diff --git a/fmc/test-fw/test-rt/build.rs b/fmc/test-fw/test-rt/build.rs deleted file mode 100644 index a7670891c1..0000000000 --- a/fmc/test-fw/test-rt/build.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed under the Apache-2.0 license - -fn main() { - cfg_if::cfg_if! { - if #[cfg(not(feature = "std"))] { - use std::env; - use std::fs; - use std::path::PathBuf; - use caliptra_gen_linker_scripts::gen_memory_x; - - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - - fs::write(out_dir.join("memory.x"),gen_memory_x(caliptra_common::RUNTIME_ORG, caliptra_common::RUNTIME_SIZE) - .as_bytes()) - .expect("Unable to generate memory.x"); - - println!("cargo:rustc-link-search={}", out_dir.display()); - - println!("cargo:rerun-if-changed=memory.x"); - println!("cargo:rustc-link-arg=-Tmemory.x"); - - println!("cargo:rustc-link-arg=-Tlink.x"); - println!("cargo:rerun-if-changed=build.rs"); - } - } -} diff --git a/fmc/test-fw/test-rt/src/interactive_test.rs b/fmc/test-fw/test-rt/src/interactive_test.rs deleted file mode 100644 index 857a74a4bb..0000000000 --- a/fmc/test-fw/test-rt/src/interactive_test.rs +++ /dev/null @@ -1,158 +0,0 @@ -// Licensed under the Apache-2.0 license - -use caliptra_common::mailbox_api; -use caliptra_drivers::pcr_log::{PcrLogEntry, PcrLogEntryId}; -use caliptra_drivers::{cprintln, PcrBank, PcrId, PersistentDataAccessor}; -use caliptra_registers::pv::PvReg; -use caliptra_registers::soc_ifc::SocIfcReg; -use ureg::RealMmioMut; - -use core::convert::TryInto; -use zerocopy::AsBytes; - -pub const TEST_CMD_READ_PCR_LOG: u32 = 0x1000_0000; -pub const TEST_CMD_READ_FHT: u32 = 0x1000_0001; -pub const TEST_CMD_READ_PCRS: u32 = 0x1000_0002; -pub const TEST_CMD_PCRS_LOCKED: u32 = 0x1000_0004; -const FW_LOAD_CMD_OPCODE: u32 = mailbox_api::CommandId::FIRMWARE_LOAD.0; - -fn process_mailbox_command( - persistent_data: &PersistentDataAccessor, - mbox: &caliptra_registers::mbox::RegisterBlock, -) { - let cmd = mbox.cmd().read(); - cprintln!("[fmc-test-harness] Received command: 0x{:08X}", cmd); - match cmd { - TEST_CMD_READ_PCR_LOG => { - read_pcr_log(persistent_data, mbox); - } - TEST_CMD_READ_FHT => { - read_fht(persistent_data, mbox); - } - TEST_CMD_READ_PCRS => { - read_pcrs(mbox); - } - FW_LOAD_CMD_OPCODE => { - // Reset the CPU with the firmware-update command in the mailbox - trigger_update_reset(); - } - TEST_CMD_PCRS_LOCKED => { - try_to_reset_pcrs(mbox); - } - _ => { - panic!(); - } - } -} - -pub fn process_mailbox_commands() { - let persistent_data = unsafe { PersistentDataAccessor::new() }; - let mut mbox = unsafe { caliptra_registers::mbox::MboxCsr::new() }; - let mbox = mbox.regs_mut(); - - cprintln!("Waiting for mailbox commands..."); - loop { - if mbox.status().read().mbox_fsm_ps().mbox_execute_uc() { - process_mailbox_command(&persistent_data, &mbox); - } - } -} - -fn read_fht( - persistent_data: &PersistentDataAccessor, - mbox: &caliptra_registers::mbox::RegisterBlock, -) { - send_to_mailbox(mbox, persistent_data.get().fht.as_bytes(), true); -} - -fn send_to_mailbox( - mbox: &caliptra_registers::mbox::RegisterBlock, - data: &[u8], - update_mb_state: bool, -) { - let data_len = data.len(); - let word_size = core::mem::size_of::(); - let remainder = data_len % word_size; - let n = data_len - remainder; - for idx in (0..n).step_by(word_size) { - mbox.datain() - .write(|_| u32::from_le_bytes(data[idx..idx + word_size].try_into().unwrap())); - } - - if remainder > 0 { - let mut last_word = data[n] as u32; - for idx in 1..remainder { - last_word |= (data[n + idx] as u32) << (idx << 3); - } - mbox.datain().write(|_| last_word); - } - - if update_mb_state { - mbox.dlen().write(|_| data_len as u32); - mbox.status().write(|w| w.status(|w| w.data_ready())); - } -} - -fn read_pcr_log( - persistent_data: &PersistentDataAccessor, - mbox: &caliptra_registers::mbox::RegisterBlock, -) { - let mut pcr_entry_count = 0; - loop { - let pcr_entry = persistent_data.get().pcr_log[pcr_entry_count]; - if PcrLogEntryId::from(pcr_entry.id) == PcrLogEntryId::Invalid { - break; - } - - pcr_entry_count += 1; - send_to_mailbox(mbox, pcr_entry.as_bytes(), false); - } - - mbox.dlen().write(|_| { - (core::mem::size_of::() * pcr_entry_count) - .try_into() - .unwrap() - }); - mbox.status().write(|w| w.status(|w| w.data_ready())); -} - -fn read_pcrs(mbox: &caliptra_registers::mbox::RegisterBlock) { - let pcr_bank = unsafe { PcrBank::new(PvReg::new()) }; - const PCR_COUNT: usize = 32; - for i in 0..PCR_COUNT { - let pcr = pcr_bank.read_pcr(PcrId::try_from(i as u8).unwrap()); - let mut pcr_bytes: [u32; 12] = pcr.try_into().unwrap(); - - swap_word_bytes_inplace(&mut pcr_bytes); - send_to_mailbox(mbox, pcr.as_bytes(), false); - } - - mbox.dlen().write(|_| (48 * PCR_COUNT).try_into().unwrap()); - mbox.status().write(|w| w.status(|w| w.data_ready())); -} - -fn swap_word_bytes_inplace(words: &mut [u32]) { - for word in words.iter_mut() { - *word = word.swap_bytes() - } -} - -fn trigger_update_reset() { - unsafe { SocIfcReg::new() } - .regs_mut() - .internal_fw_update_reset() - .write(|w| w.core_rst(true)); -} - -fn try_to_reset_pcrs(mbox: &caliptra_registers::mbox::RegisterBlock) { - let mut pcr_bank = unsafe { PcrBank::new(PvReg::new()) }; - - let res0 = pcr_bank.erase_pcr(caliptra_common::RT_FW_CURRENT_PCR); - let res1 = pcr_bank.erase_pcr(caliptra_common::RT_FW_JOURNEY_PCR); - - if res0.is_err() && res1.is_err() { - mbox.status().write(|w| w.status(|w| w.cmd_complete())); - } else { - mbox.status().write(|w| w.status(|w| w.cmd_failure())); - } -} diff --git a/fmc/test-fw/test-rt/src/main.rs b/fmc/test-fw/test-rt/src/main.rs deleted file mode 100644 index 4a7719a9d6..0000000000 --- a/fmc/test-fw/test-rt/src/main.rs +++ /dev/null @@ -1,111 +0,0 @@ -/*++ - -Licensed under the Apache-2.0 license. - -File Name: - - main.rs - -Abstract: - - File contains main entry point for Caliptra Test Runtime - ---*/ -#![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(feature = "interactive_test", allow(unused_imports))] -mod interactive_test; - -use caliptra_common::cprintln; -use caliptra_cpu::TrapRecord; -use caliptra_drivers::{report_fw_error_non_fatal, Mailbox, PersistentDataAccessor}; -use core::hint::black_box; - -#[cfg(feature = "std")] -pub fn main() {} - -const BANNER: &str = r#" - _____ __ __________ __ - / \ ____ ____ | | __ \______ \_/ |_ - / \ / \ / _ \_/ ___\| |/ / | _/\ __\ -/ Y ( <_> ) \___| < | | \ | | -\____|__ /\____/ \___ >__|_ \ |____|_ / |__| - \/ \/ \/ \/ -"#; - -#[no_mangle] -pub extern "C" fn entry_point() -> ! { - cprintln!("{}", BANNER); - let persistent_data = unsafe { PersistentDataAccessor::new() }; - - if !persistent_data.get().fht.is_valid() { - cprintln!("FHT not loaded"); - caliptra_drivers::ExitCtrl::exit(0xff) - } - - if cfg!(feature = "interactive_test") { - interactive_test::process_mailbox_commands(); - } - caliptra_drivers::ExitCtrl::exit(0) -} - -#[no_mangle] -#[inline(never)] -#[allow(clippy::empty_loop)] -extern "C" fn exception_handler(trap_record: &TrapRecord) { - cprintln!( - "FMC EXCEPTION mcause=0x{:08X} mscause=0x{:08X} mepc=0x{:08X}", - trap_record.mcause, - trap_record.mscause, - trap_record.mepc - ); - - // Signal non-fatal error to SOC - report_error(0xdead); -} - -#[no_mangle] -#[inline(never)] -#[allow(clippy::empty_loop)] -extern "C" fn nmi_handler(trap_record: &TrapRecord) { - cprintln!( - "FMC NMI mcause=0x{:08X} mscause=0x{:08X} mepc=0x{:08X}", - trap_record.mcause, - trap_record.mscause, - trap_record.mepc - ); - - report_error(0xdead); -} - -#[panic_handler] -#[inline(never)] -#[cfg(not(feature = "std"))] -#[allow(clippy::empty_loop)] -fn fmc_panic(_: &core::panic::PanicInfo) -> ! { - cprintln!("RT Panic!!"); - panic_is_possible(); - - // TODO: Signal non-fatal error to SOC - report_error(0xdead); -} - -#[allow(clippy::empty_loop)] -fn report_error(code: u32) -> ! { - cprintln!("RT Error: 0x{:08X}", code); - report_fw_error_non_fatal(code); - loop { - // SoC firmware might be stuck waiting for Caliptra to finish - // executing this pending mailbox transaction. Notify them that - // we've failed. - unsafe { Mailbox::abort_pending_soc_to_uc_transactions() }; - } -} - -#[no_mangle] -#[inline(never)] -fn panic_is_possible() { - black_box(()); - // The existence of this symbol is used to inform test_panic_missing - // that panics are possible. Do not remove or rename this symbol. -} diff --git a/fmc/test-fw/test-rt/tests/test_panic_missing.rs b/fmc/test-fw/test-rt/tests/test_panic_missing.rs deleted file mode 100644 index e61c50cba2..0000000000 --- a/fmc/test-fw/test-rt/tests/test_panic_missing.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed under the Apache-2.0 license - -use caliptra_builder::firmware; - -#[test] -fn test_panic_missing() { - let rt_elf = caliptra_builder::build_firmware_elf(&firmware::APP_WITH_UART).unwrap(); - let symbols = caliptra_builder::elf_symbols(&rt_elf).unwrap(); - if symbols.iter().any(|s| s.name.contains("panic_is_possible")) { - panic!( - "The caliptra RT contains the panic_is_possible symbol, which is not allowed. \ - Please remove any code that might panic." - ) - } -} diff --git a/fmc/tests/fmc_integration_tests/test_hand_off.rs b/fmc/tests/fmc_integration_tests/test_hand_off.rs index 25c04409fb..b231f661dd 100644 --- a/fmc/tests/fmc_integration_tests/test_hand_off.rs +++ b/fmc/tests/fmc_integration_tests/test_hand_off.rs @@ -8,7 +8,7 @@ fn test_hand_off() { let image = caliptra_builder::build_and_sign_image( &firmware::FMC_WITH_UART, - &firmware::fmc_tests::MOCK_RT_WITH_UART, + &firmware::runtime_tests::BOOT, ImageOptions::default(), ) .unwrap(); diff --git a/fmc/tests/fmc_integration_tests/test_rtalias.rs b/fmc/tests/fmc_integration_tests/test_rtalias.rs index e82e1ebe15..3d1b15f299 100644 --- a/fmc/tests/fmc_integration_tests/test_rtalias.rs +++ b/fmc/tests/fmc_integration_tests/test_rtalias.rs @@ -1,6 +1,6 @@ // Licensed under the Apache-2.0 license use caliptra_builder::{ - firmware::{self, fmc_tests::MOCK_RT_INTERACTIVE, FMC_WITH_UART}, + firmware::{self, runtime_tests::MOCK_RT_INTERACTIVE, FMC_WITH_UART}, ImageOptions, }; use caliptra_common::RomBootStatus::*; @@ -40,7 +40,7 @@ fn test_boot_status_reporting() { let image = caliptra_builder::build_and_sign_image( &firmware::FMC_WITH_UART, - &firmware::fmc_tests::MOCK_RT_WITH_UART, + &firmware::runtime_tests::BOOT, ImageOptions::default(), ) .unwrap(); diff --git a/runtime/test-fw/Cargo.toml b/runtime/test-fw/Cargo.toml index a88e82349b..75acc16088 100644 --- a/runtime/test-fw/Cargo.toml +++ b/runtime/test-fw/Cargo.toml @@ -34,6 +34,12 @@ name = "persistent_rt" path = "src/persistent_tests.rs" required-features = ["riscv", "runtime"] +[[bin]] +name = "mock_rt_interact" +path = "src/mock_rt_test_interactive.rs" +required-features = ["riscv", "runtime"] + + [build-dependencies] caliptra_common = { workspace = true, default-features = false } caliptra-gen-linker-scripts.workspace = true diff --git a/runtime/test-fw/src/mock_rt_test_interactive.rs b/runtime/test-fw/src/mock_rt_test_interactive.rs new file mode 100644 index 0000000000..6f36154445 --- /dev/null +++ b/runtime/test-fw/src/mock_rt_test_interactive.rs @@ -0,0 +1,173 @@ +// Licensed under the Apache-2.0 license + +#![no_main] +#![no_std] + +use caliptra_common::{handle_fatal_error, mailbox_api::CommandId}; +use caliptra_drivers::pcr_log::{PcrLogEntry, PcrLogEntryId}; + +use caliptra_drivers::{cprintln, CaliptraError, CaliptraResult}; +use caliptra_drivers::{PcrBank, PcrId, PersistentDataAccessor}; +use caliptra_registers::pv::PvReg; +use caliptra_registers::{mbox::enums::MboxStatusE, soc_ifc::SocIfcReg}; +use caliptra_runtime::{mailbox::Mailbox, Drivers, RtBootStatus}; +use caliptra_test_harness::{runtime_handlers, test_suite}; +use zerocopy::AsBytes; + +const OPCODE_FW_LOAD: u32 = CommandId::FIRMWARE_LOAD.0; + +const BANNER: &str = r#"FMC Tester"#; + +pub const TEST_CMD_READ_PCR_LOG: u32 = 0x1000_0000; +pub const TEST_CMD_READ_FHT: u32 = 0x1000_0001; +pub const TEST_CMD_READ_PCRS: u32 = 0x1000_0002; +pub const TEST_CMD_PCRS_LOCKED: u32 = 0x1000_0004; + +#[no_mangle] +#[allow(clippy::empty_loop)] +fn rt_entry() -> () { + cprintln!("{}", BANNER); + let mut drivers = unsafe { + Drivers::new_from_registers().unwrap_or_else(|e| { + // treat global exception as a fatal error + match e { + CaliptraError::RUNTIME_GLOBAL_EXCEPTION => handle_fatal_error(e.into()), + _ => { + cprintln!("Runtime can't load drivers"); + handle_fatal_error(e.into()); + } + } + }) + }; + + if !drivers.persistent_data.get().fht.is_valid() { + cprintln!("Runtime can't load FHT"); + handle_fatal_error(CaliptraError::RUNTIME_HANDOFF_FHT_NOT_LOADED.into()); + } + cprintln!("[rt] Runtime listening for mailbox commands..."); + if let Err(e) = handle_mailbox_commands(&mut drivers) { + handle_fatal_error(e.into()); + } + return; +} + +pub fn handle_mailbox_commands(drivers: &mut Drivers) -> CaliptraResult<()> { + // Indicator to SOC that RT firmware is ready + drivers.soc_ifc.assert_ready_for_runtime(); + caliptra_drivers::report_boot_status(RtBootStatus::RtReadyForCommands.into()); + + let persistent_data = unsafe { PersistentDataAccessor::new() }; + + loop { + if drivers.is_shutdown { + return Err(CaliptraError::RUNTIME_SHUTDOWN); + } + + if drivers.mbox.is_cmd_ready() { + caliptra_drivers::report_fw_error_non_fatal(0); + match handle_command(drivers, &persistent_data) { + Ok(status) => { + drivers.mbox.set_status(status); + } + Err(e) => { + caliptra_drivers::report_fw_error_non_fatal(e.into()); + drivers.mbox.set_status(MboxStatusE::CmdFailure); + } + } + } + } +} + +pub fn handle_command( + drivers: &mut Drivers, + persistent_data: &PersistentDataAccessor, +) -> CaliptraResult { + loop { + while !drivers.mbox.is_cmd_ready() { + // Wait for a request from the SoC. + } + let cmd = drivers.mbox.cmd(); + let mbox = &mut drivers.mbox; + + match cmd { + CommandId(OPCODE_FW_LOAD) => trigger_update_reset(), + CommandId(TEST_CMD_READ_PCR_LOG) => read_pcr_log(persistent_data, mbox), + CommandId(TEST_CMD_READ_FHT) => read_fht(persistent_data, mbox), + CommandId(TEST_CMD_READ_PCRS) => read_pcrs(mbox), + CommandId(TEST_CMD_PCRS_LOCKED) => try_to_reset_pcrs(mbox), + _ => { + drivers.mbox.set_status(MboxStatusE::CmdFailure); + } + } + } +} + +fn read_pcr_log(persistent_data: &PersistentDataAccessor, mbox: &mut Mailbox) { + let mut pcr_entry_count = 0; + loop { + let pcr_entry = persistent_data.get().pcr_log[pcr_entry_count]; + if PcrLogEntryId::from(pcr_entry.id) == PcrLogEntryId::Invalid { + break; + } + + pcr_entry_count += 1; + mbox.copy_bytes_to_mbox(pcr_entry.as_bytes()).unwrap(); + } + + mbox.set_dlen( + (core::mem::size_of::() * pcr_entry_count) + .try_into() + .unwrap(), + ); + mbox.set_status(MboxStatusE::DataReady); +} + +fn read_fht(persistent_data: &PersistentDataAccessor, mbox: &mut Mailbox) { + mbox.write_response(persistent_data.get().fht.as_bytes()) + .unwrap(); + mbox.set_status(MboxStatusE::DataReady); +} + +fn read_pcrs(mbox: &mut Mailbox) { + let pcr_bank = unsafe { PcrBank::new(PvReg::new()) }; + const PCR_COUNT: usize = 32; + for i in 0..PCR_COUNT { + let pcr = pcr_bank.read_pcr(PcrId::try_from(i as u8).unwrap()); + let mut pcr_bytes: [u32; 12] = pcr.try_into().unwrap(); + + swap_word_bytes_inplace(&mut pcr_bytes); + mbox.copy_bytes_to_mbox(pcr.as_bytes()).unwrap(); + } + mbox.set_dlen((48 * PCR_COUNT).try_into().unwrap()); + mbox.set_status(MboxStatusE::DataReady); +} + +fn swap_word_bytes_inplace(words: &mut [u32]) { + for word in words.iter_mut() { + *word = word.swap_bytes() + } +} + +fn try_to_reset_pcrs(mbox: &mut Mailbox) { + let mut pcr_bank = unsafe { PcrBank::new(PvReg::new()) }; + + let res0 = pcr_bank.erase_pcr(caliptra_common::RT_FW_CURRENT_PCR); + let res1 = pcr_bank.erase_pcr(caliptra_common::RT_FW_JOURNEY_PCR); + + if res0.is_err() && res1.is_err() { + mbox.set_status(MboxStatusE::CmdComplete); + } else { + mbox.set_status(MboxStatusE::CmdComplete); + } +} + +fn trigger_update_reset() { + unsafe { SocIfcReg::new() } + .regs_mut() + .internal_fw_update_reset() + .write(|w| w.core_rst(true)); +} + +test_suite! { + rt_entry, +}