diff --git a/hw-model/src/lib.rs b/hw-model/src/lib.rs index 2f3fa27fa7..8efbcf5a13 100644 --- a/hw-model/src/lib.rs +++ b/hw-model/src/lib.rs @@ -45,6 +45,7 @@ mod rv32_builder; pub use api::mailbox::mbox_write_fifo; pub use api_types::{DeviceLifecycle, Fuses, SecurityState, U4}; pub use caliptra_emu_bus::BusMmio; +pub use caliptra_emu_cpu::StackInfo; use output::ExitStatus; pub use output::Output; @@ -176,6 +177,10 @@ pub struct InitParams<'a> { // A trace path to use. If None, the CPTRA_TRACE_PATH environment variable // will be used pub trace_path: Option, + + // Information about the stack Caliptra is using. When set the emulator will check if the stack + // overflows. + pub stack_info: Option, } impl<'a> Default for InitParams<'a> { fn default() -> Self { @@ -210,6 +215,7 @@ impl<'a> Default for InitParams<'a> { }), random_sram_puf: true, trace_path: None, + stack_info: None, } } } diff --git a/hw-model/src/model_emulated.rs b/hw-model/src/model_emulated.rs index 881bec4001..710aed3dbe 100644 --- a/hw-model/src/model_emulated.rs +++ b/hw-model/src/model_emulated.rs @@ -187,7 +187,13 @@ impl HwModel for ModelEmulated { dccm_dest.copy_from_slice(params.dccm); } let soc_to_caliptra_bus = root_bus.soc_to_caliptra_bus(); - let cpu = Cpu::new(BusLogger::new(root_bus), clock); + let cpu = { + let mut cpu = Cpu::new(BusLogger::new(root_bus), clock); + if let Some(stack_info) = params.stack_info { + cpu.with_stack_info(stack_info); + } + cpu + }; let mut hasher = DefaultHasher::new(); std::hash::Hash::hash_slice(params.rom, &mut hasher); diff --git a/rom/dev/tests/rom_integration_tests/helpers.rs b/rom/dev/tests/rom_integration_tests/helpers.rs index f960726370..aefffdaef4 100644 --- a/rom/dev/tests/rom_integration_tests/helpers.rs +++ b/rom/dev/tests/rom_integration_tests/helpers.rs @@ -4,24 +4,41 @@ use std::mem; use caliptra_api::SocManager; use caliptra_builder::{firmware, ImageOptions}; -use caliptra_hw_model::{BootParams, Fuses, HwModel, InitParams, SecurityState}; +use caliptra_drivers::memory_layout::{ROM_STACK_ORG, ROM_STACK_SIZE}; +use caliptra_hw_model::{BootParams, Fuses, HwModel, InitParams, SecurityState, StackInfo}; use caliptra_hw_model::{DefaultHwModel, ModelError}; use caliptra_image_types::ImageBundle; pub fn build_hw_model_and_image_bundle( fuses: Fuses, image_options: ImageOptions, +) -> (DefaultHwModel, ImageBundle) { + build_hw_model_and_image_bundle_with_stack_info(fuses, image_options, None) +} + +pub fn build_hw_model_and_image_bundle_with_stack_info( + fuses: Fuses, + image_options: ImageOptions, + stack_info: Option, ) -> (DefaultHwModel, ImageBundle) { let image = build_image_bundle(image_options); - (build_hw_model(fuses), image) + (build_hw_model_with_stack_info(fuses, stack_info), image) } -pub fn build_hw_model(fuses: Fuses) -> DefaultHwModel { +pub fn build_hw_model_with_stack_info( + fuses: Fuses, + stack_info: Option, +) -> DefaultHwModel { let rom = caliptra_builder::build_firmware_rom(firmware::rom_from_env()).unwrap(); + let stack_info = stack_info.unwrap_or(StackInfo::new( + ROM_STACK_ORG + ROM_STACK_SIZE, + ROM_STACK_ORG, + )); caliptra_hw_model::new( InitParams { rom: &rom, security_state: SecurityState::from(fuses.life_cycle as u32), + stack_info: Some(stack_info), ..Default::default() }, BootParams { @@ -32,6 +49,10 @@ pub fn build_hw_model(fuses: Fuses) -> DefaultHwModel { .unwrap() } +pub fn build_hw_model(fuses: Fuses) -> DefaultHwModel { + build_hw_model_with_stack_info(fuses, None) +} + pub fn build_image_bundle(image_options: ImageOptions) -> ImageBundle { caliptra_builder::build_and_sign_image( &firmware::FMC_WITH_UART, diff --git a/rom/dev/tests/rom_integration_tests/test_idevid_derivation.rs b/rom/dev/tests/rom_integration_tests/test_idevid_derivation.rs index cb77cceca2..3f972aee1c 100644 --- a/rom/dev/tests/rom_integration_tests/test_idevid_derivation.rs +++ b/rom/dev/tests/rom_integration_tests/test_idevid_derivation.rs @@ -3,8 +3,9 @@ use caliptra_api::SocManager; use caliptra_builder::{firmware, ImageOptions}; use caliptra_common::mailbox_api::{CommandId, GetLdevCertResp, MailboxReqHeader}; +use caliptra_drivers::memory_layout::{STACK_ORG, STACK_SIZE}; use caliptra_drivers::{IdevidCertAttr, MfgFlags, X509KeyIdAlgo}; -use caliptra_hw_model::{DefaultHwModel, Fuses, HwModel}; +use caliptra_hw_model::{DefaultHwModel, Fuses, HwModel, StackInfo}; use caliptra_image_types::ImageBundle; use openssl::pkey::{PKey, Public}; use openssl::x509::X509; @@ -43,8 +44,11 @@ fn generate_csr(hw: &mut DefaultHwModel, image_bundle: &ImageBundle) -> Vec #[test] fn test_generate_csr() { - let (mut hw, image_bundle) = - helpers::build_hw_model_and_image_bundle(Fuses::default(), ImageOptions::default()); + let (mut hw, image_bundle) = helpers::build_hw_model_and_image_bundle_with_stack_info( + Fuses::default(), + ImageOptions::default(), + Some(StackInfo::new(STACK_ORG + STACK_SIZE, STACK_ORG)), + ); generate_csr(&mut hw, &image_bundle); } @@ -54,8 +58,11 @@ fn test_idev_subj_key_id_algo() { let mut fuses = Fuses::default(); fuses.idevid_cert_attr[IdevidCertAttr::Flags as usize] = algo; - let (mut hw, image_bundle) = - helpers::build_hw_model_and_image_bundle(fuses, ImageOptions::default()); + let (mut hw, image_bundle) = helpers::build_hw_model_and_image_bundle_with_stack_info( + Fuses::default(), + ImageOptions::default(), + Some(StackInfo::new(STACK_ORG + STACK_SIZE, STACK_ORG)), + ); hw.upload_firmware(&image_bundle.to_bytes().unwrap()) .unwrap(); @@ -88,8 +95,11 @@ fn test_generate_csr_stress() { for _ in 0..num_tests { let fuses = fuses_with_random_uds(); - let (mut hw, image_bundle) = - helpers::build_hw_model_and_image_bundle(fuses.clone(), ImageOptions::default()); + let (mut hw, image_bundle) = helpers::build_hw_model_and_image_bundle_with_stack_info( + Fuses::default(), + ImageOptions::default(), + Some(StackInfo::new(STACK_ORG + STACK_SIZE, STACK_ORG)), + ); let csr_bytes = generate_csr(&mut hw, &image_bundle); diff --git a/rom/dev/tests/rom_integration_tests/test_warm_reset.rs b/rom/dev/tests/rom_integration_tests/test_warm_reset.rs index 453f550ac2..1b32dbb6bb 100644 --- a/rom/dev/tests/rom_integration_tests/test_warm_reset.rs +++ b/rom/dev/tests/rom_integration_tests/test_warm_reset.rs @@ -6,9 +6,10 @@ use caliptra_builder::firmware::{APP_WITH_UART, ROM_WITH_UART}; use caliptra_builder::ImageOptions; use caliptra_common::mailbox_api::CommandId; use caliptra_common::RomBootStatus::*; +use caliptra_drivers::memory_layout::{STACK_ORG, STACK_SIZE}; use caliptra_drivers::CaliptraError; use caliptra_hw_model::DeviceLifecycle; -use caliptra_hw_model::{BootParams, Fuses, HwModel, InitParams, SecurityState}; +use caliptra_hw_model::{BootParams, Fuses, HwModel, InitParams, SecurityState, StackInfo}; use caliptra_test::swap_word_bytes_inplace; use openssl::sha::sha384; use zerocopy::AsBytes; @@ -178,8 +179,11 @@ fn test_warm_reset_during_update_reset() { ..Default::default() }; - let (mut hw, image_bundle) = - helpers::build_hw_model_and_image_bundle(fuses, ImageOptions::default()); + let (mut hw, image_bundle) = helpers::build_hw_model_and_image_bundle_with_stack_info( + fuses, + ImageOptions::default(), + Some(StackInfo::new(STACK_ORG + STACK_SIZE, STACK_ORG)), + ); hw.upload_firmware(&image_bundle.to_bytes().unwrap()) .unwrap(); diff --git a/runtime/tests/runtime_integration_tests/common.rs b/runtime/tests/runtime_integration_tests/common.rs index eb7fce430a..1abf0ba0fb 100644 --- a/runtime/tests/runtime_integration_tests/common.rs +++ b/runtime/tests/runtime_integration_tests/common.rs @@ -9,9 +9,14 @@ use caliptra_common::mailbox_api::{ CommandId, GetFmcAliasCertResp, GetRtAliasCertResp, InvokeDpeReq, InvokeDpeResp, MailboxReq, MailboxReqHeader, }; -use caliptra_drivers::MfgFlags; +use caliptra_drivers::{ + memory_layout::{STACK_ORG, STACK_SIZE}, + MfgFlags, +}; use caliptra_error::CaliptraError; -use caliptra_hw_model::{BootParams, DefaultHwModel, Fuses, HwModel, InitParams, ModelError}; +use caliptra_hw_model::{ + BootParams, DefaultHwModel, Fuses, HwModel, InitParams, ModelError, StackInfo, +}; use dpe::{ commands::{Command, CommandHdr}, response::{ @@ -70,6 +75,7 @@ pub fn run_rt_test_lms(args: RuntimeTestArgs, lms_verify: bool) -> DefaultHwMode Some(init_params) => init_params, None => InitParams { rom: &rom, + stack_info: Some(StackInfo::new(STACK_ORG + STACK_SIZE, STACK_ORG)), ..Default::default() }, }; diff --git a/sw-emulator/lib/cpu/src/cpu.rs b/sw-emulator/lib/cpu/src/cpu.rs index 3a54ed14c8..fce50fadfb 100644 --- a/sw-emulator/lib/cpu/src/cpu.rs +++ b/sw-emulator/lib/cpu/src/cpu.rs @@ -22,6 +22,77 @@ use caliptra_emu_types::{RvAddr, RvData, RvException, RvSize}; pub type InstrTracer<'a> = dyn FnMut(u32, RvInstr) + 'a; +/// Describes the shape of Caliptra's stack. +/// +/// Currently only supports one stack region at a time. +pub struct StackInfo { + lowest_stack_address: u32, + current_stack_address: u32, + has_overflowed: bool, + stack_start: u32, + stack_end: u32, +} + +impl StackInfo { + /// Create a new `StackInfo` by describing the start and end of the stack + /// + /// **Note:** `stack_start` MUST be greater than `stack_end`. Caliptra's stack grows + /// to a lower address. + pub fn new(stack_start: u32, stack_end: u32) -> Self { + if stack_start < stack_end { + panic!("Caliptra's stack grows to a lower address!"); + } + + Self { + lowest_stack_address: stack_start, + current_stack_address: stack_start, + has_overflowed: false, + stack_start, + stack_end, + } + } +} + +impl StackInfo { + /// Fetch current stack usage. + pub fn current_stack_usage(&self) -> u32 { + self.stack_start - self.current_stack_address + } + + /// Fetch the largest stack overflow. + /// + /// If the stack never overflowed, returns `None`. + pub fn max_stack_overflow(&self) -> Option { + if self.has_overflowed { + Some(self.stack_end - self.lowest_stack_address) + } else { + None + } + } + + /// Checks if the stack will overflow when pushed to `stack_address`. + /// + /// Returns `true` if the stack will overflow, `false` if it will not overflow. + pub fn check_overflow(&mut self, stack_address: u32) -> bool { + if stack_address == 0 { + // sp is initialized to 0. + return false; + } + + self.current_stack_address = stack_address; + if self.current_stack_address < self.lowest_stack_address { + self.lowest_stack_address = self.current_stack_address; + } + + if self.current_stack_address < self.stack_end { + self.has_overflowed = true; + return true; + } + + false + } +} + #[derive(Clone)] pub struct CodeCoverage { rom_bit_vec: BitVec, @@ -159,6 +230,20 @@ pub struct Cpu { pub(crate) watch_ptr_cfg: WatchPtrCfg, pub code_coverage: CodeCoverage, + stack_info: Option, +} + +impl Drop for Cpu { + fn drop(&mut self) { + if let Some(stack_info) = &self.stack_info { + if stack_info.has_overflowed { + panic!( + "[EMU] Fatal: Caliptra's stack overflowed by {} bytes!", + stack_info.max_stack_overflow().unwrap() + ) + } + } + } } /// Cpu instruction step action @@ -197,9 +282,14 @@ impl Cpu { // TODO: Pass in code_coverage from the outside (as caliptra-emu-cpu // isn't supposed to know anything about the caliptra memory map) code_coverage: CodeCoverage::new(ROM_SIZE, ICCM_SIZE), + stack_info: None, } } + pub fn with_stack_info(&mut self, stack_info: StackInfo) { + self.stack_info = Some(stack_info); + } + /// Read the RISCV CPU Program counter /// /// # Return @@ -262,7 +352,25 @@ impl Cpu { /// /// * `RvException` - Exception with cause `RvExceptionCause::IllegalRegister` pub fn write_xreg(&mut self, reg: XReg, val: RvData) -> Result<(), RvException> { - self.xregs.write(reg, val) + // XReg::X2 is the sp register. + if reg == XReg::X2 { + self.check_stack(val); + } + self.xregs.write(reg, val)?; + Ok(()) + } + + // Check if the stack overflows at the requested address. + fn check_stack(&mut self, val: RvData) { + if let Some(stack_info) = &mut self.stack_info { + if stack_info.check_overflow(val) { + eprintln!( + "[EMU] Caliptra's stack overflowed by {} bytes at pc 0x{:x}.", + stack_info.max_stack_overflow().unwrap(), + self.pc + ); + } + } } /// Read the specified configuration status register diff --git a/sw-emulator/lib/cpu/src/lib.rs b/sw-emulator/lib/cpu/src/lib.rs index 5d0a73f57e..62e7fb3d60 100644 --- a/sw-emulator/lib/cpu/src/lib.rs +++ b/sw-emulator/lib/cpu/src/lib.rs @@ -22,7 +22,7 @@ pub mod xreg_file; pub use cpu::StepAction; pub use cpu::WatchPtrHit; pub use cpu::WatchPtrKind; -pub use cpu::{CoverageBitmaps, Cpu, InstrTracer}; +pub use cpu::{CoverageBitmaps, Cpu, InstrTracer, StackInfo}; pub use csr_file::CsrFile; pub use pic::{IntSource, Irq, Pic, PicMmioRegisters}; pub use types::RvInstr;