Skip to content

Commit

Permalink
[feat] Configure ROM Watchdog timer with a timeout value from CPTRA_W…
Browse files Browse the repository at this point in the history
…DT_CFG register. (#730)

* [feat] Configure ROM Watchdog timer with a timeout value from CPTRA_WDT_CFG register.

This change uses the value from the CPTRA_WDT_CFG register to program the ROM WDT.
This change also removes the dismisal of the WDT during mailbox command processing.

* Replace WDT NMI bit-twiddling with ureg.

---------

Co-authored-by: Kor Nielsen <[email protected]>
  • Loading branch information
mhatrevi and korran authored Sep 12, 2023
1 parent 43fb6be commit 52e1a39
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 32 deletions.
19 changes: 19 additions & 0 deletions drivers/src/soc_ifc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Abstract:

use caliptra_error::{CaliptraError, CaliptraResult};
use caliptra_registers::soc_ifc::enums::DeviceLifecycleE;
use caliptra_registers::soc_ifc::regs::CptraWdtStatusReadVal;
use caliptra_registers::soc_ifc::{self, SocIfcReg};

use crate::{memory_layout, FuseBank};
Expand Down Expand Up @@ -154,6 +155,18 @@ impl SocIfc {
.write(|w| w.timer1_en(false));
}

/// Get the WDT status.
///
/// This is useful to call from a fatal-error-handling routine.
///
/// # Safety
///
/// This function is safe to call from a trap handler.
pub unsafe fn wdt_status() -> CptraWdtStatusReadVal {
let soc_ifc = SocIfcReg::new();
soc_ifc.regs().cptra_wdt_status().read()
}

pub fn get_cycle_count(&self, seconds: u32) -> CaliptraResult<u64> {
const GIGA_UNIT: u32 = 1_000_000_000;
let clock_period_picosecs = self.soc_ifc.regs().cptra_timer_config().read();
Expand Down Expand Up @@ -207,6 +220,12 @@ impl SocIfc {
.write(|w| w.timer1_restart(true));
}

pub fn wdt1_timeout_cycle_count(&self) -> u64 {
let soc_ifc_regs = self.soc_ifc.regs();
soc_ifc_regs.cptra_wdt_cfg().at(0).read() as u64
| ((soc_ifc_regs.cptra_wdt_cfg().at(1).read() as u64) << 32)
}

pub fn internal_fw_update_reset_wait_cycles(&self) -> u32 {
self.soc_ifc
.regs()
Expand Down
2 changes: 2 additions & 0 deletions error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,8 @@ impl CaliptraError {
pub const ROM_GLOBAL_FAKE_ROM_IN_PRODUCTION: CaliptraError =
CaliptraError::new_const(0x0105000B);

pub const ROM_GLOBAL_WDT_EXPIRED: CaliptraError = CaliptraError::new_const(0x0105000C);

/// ROM KAT Errors
pub const ROM_KAT_SHA256_DIGEST_FAILURE: CaliptraError = CaliptraError::new_const(0x90010001);
pub const ROM_KAT_SHA256_DIGEST_MISMATCH: CaliptraError = CaliptraError::new_const(0x90010002);
Expand Down
16 changes: 16 additions & 0 deletions hw-model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ impl TrngMode {
}
}

const EXPECTED_CALIPTRA_BOOT_TIME_IN_CYCLES: u64 = 40_000_000; // 40 million cycles

pub struct InitParams<'a> {
// The contents of the boot ROM
pub rom: &'a [u8],
Expand Down Expand Up @@ -136,6 +138,8 @@ pub struct InitParams<'a> {

// When None, use the itrng compile-time feature to decide which mode to use.
pub trng_mode: Option<TrngMode>,

pub wdt_timeout_cycles: u64,
}

impl<'a> Default for InitParams<'a> {
Expand Down Expand Up @@ -164,6 +168,7 @@ impl<'a> Default for InitParams<'a> {
itrng_nibbles,
etrng_responses,
trng_mode: Default::default(),
wdt_timeout_cycles: EXPECTED_CALIPTRA_BOOT_TIME_IN_CYCLES,
}
}
}
Expand Down Expand Up @@ -339,6 +344,7 @@ pub trait HwModel {
where
Self: Sized,
{
let wdt_timeout_cycles = run_params.init_params.wdt_timeout_cycles;
let mut hw: Self = HwModel::new_unbooted(run_params.init_params)?;

hw.init_fuses(&run_params.fuses);
Expand All @@ -347,6 +353,16 @@ pub trait HwModel {
.cptra_dbg_manuf_service_reg()
.write(|_| run_params.initial_dbg_manuf_service_reg);

hw.soc_ifc()
.cptra_wdt_cfg()
.at(0)
.write(|_| wdt_timeout_cycles as u32);

hw.soc_ifc()
.cptra_wdt_cfg()
.at(1)
.write(|_| (wdt_timeout_cycles >> 32) as u32);

writeln!(hw.output().logger(), "writing to cptra_bootfsm_go")?;
hw.soc_ifc().cptra_bootfsm_go().write(|w| w.go(true));

Expand Down
8 changes: 1 addition & 7 deletions rom/dev/src/flow/cold_reset/fw_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ Abstract:
#[cfg(feature = "fake-rom")]
use crate::flow::fake::FakeRomImageVerificationEnv;
use crate::fuse::log_fuse_data;
use crate::pcr;
use crate::rom_env::RomEnv;
use crate::run_fips_tests;
use crate::CALIPTRA_ROM_INFO;
use crate::{pcr, wdt};
use caliptra_cfi_derive::cfi_impl_fn;
use caliptra_common::capabilities::Capabilities;
use caliptra_common::fips::FipsVersionCmd;
Expand Down Expand Up @@ -54,9 +54,6 @@ pub struct FirmwareProcessor {}

impl FirmwareProcessor {
pub fn process(env: &mut RomEnv) -> CaliptraResult<FwProcInfo> {
// Disable the watchdog timer during processing mailbox commands.
wdt::stop_wdt(&mut env.soc_ifc);

let mut kats_env = caliptra_kat::KatsEnv {
// SHA1 Engine
sha1: &mut env.sha1,
Expand Down Expand Up @@ -86,9 +83,6 @@ impl FirmwareProcessor {
let mut txn =
Self::process_mailbox_commands(&mut env.soc_ifc, &mut env.mbox, &mut kats_env)?;

// Renable the watchdog timer.
wdt::start_wdt(&mut env.soc_ifc);

// Load the manifest
let manifest = Self::load_manifest(&mut env.persistent_data, &mut txn);
let manifest = okref(&manifest)?;
Expand Down
12 changes: 10 additions & 2 deletions rom/dev/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,6 @@ pub extern "C" fn rom_entry() -> ! {
}

// Stop the watchdog timer.
// [TODO] Reset the watchdog timer and let FMC take ownership of it.
wdt::stop_wdt(&mut env.soc_ifc);

// Lock the datavault registers.
Expand Down Expand Up @@ -237,7 +236,16 @@ extern "C" fn nmi_handler(exception: &exception::ExceptionRecord) {
exception.mepc
);

handle_fatal_error(CaliptraError::ROM_GLOBAL_NMI.into());
// Check if the NMI was due to WDT expiry.
let mut error = CaliptraError::ROM_GLOBAL_NMI;

let wdt_status = unsafe { SocIfc::wdt_status() };
if wdt_status.t1_timeout() || wdt_status.t2_timeout() {
cprintln!("WDT Expired");
error = CaliptraError::ROM_GLOBAL_WDT_EXPIRED;
}

handle_fatal_error(error.into());
}

#[panic_handler]
Expand Down
48 changes: 26 additions & 22 deletions rom/dev/src/wdt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,46 +17,50 @@ Environment:
--*/

use caliptra_cfi_derive::cfi_mod_fn;
use caliptra_common::WdtTimeout;
use caliptra_drivers::SocIfc;
use core::cmp::max;

use crate::cprintln;

const EXPECTED_CALIPTRA_BOOT_TIME_IN_CYCLES: u64 = 20_000_000; // 20 million cycles
const WDT1_TIMEOUT_SECS: u32 = 5;
const WDT2_TIMEOUT_CYCLES: u64 = 1; // Fire immediately after WDT1 expiry

/// Start the Watchdog Timer
/// Note: WDT is configured only if the device is in non-debug mode (i.e debug_locked = 1)
///
/// # Arguments
///
/// * `soc_ifc` - SOC Interface
#[cfg_attr(not(feature = "no-cfi"), cfi_mod_fn)]
pub fn start_wdt(soc_ifc: &mut SocIfc) {
cprintln!("[state] Starting the Watchdog Timer");

let wdt1_timeout_cycles: u64 = match soc_ifc.get_cycle_count(WDT1_TIMEOUT_SECS) {
Ok(cycle_count) => max(EXPECTED_CALIPTRA_BOOT_TIME_IN_CYCLES * 10, cycle_count),
Err(_) => EXPECTED_CALIPTRA_BOOT_TIME_IN_CYCLES * 10,
};

// Set WDT1 period.
soc_ifc.set_wdt1_timeout(wdt1_timeout_cycles);

// Set WDT2 period.
soc_ifc.set_wdt2_timeout(WDT2_TIMEOUT_CYCLES);

// Enable WDT1 only. WDT2 is automatically scheduled (since it is disabled) on WDT1 expiry.
soc_ifc.configure_wdt1(true);
if soc_ifc.debug_locked() {
cprintln!("[state] Starting the Watchdog Timer");
let mut wdt_timeout_cycles = soc_ifc.wdt1_timeout_cycle_count();
if wdt_timeout_cycles == 0 {
wdt_timeout_cycles = 1;
}
caliptra_common::wdt::start_wdt(
soc_ifc,
WdtTimeout::from(core::num::NonZeroU64::new(wdt_timeout_cycles).unwrap()),
);
} else {
cprintln!(
"[state] Watchdog Timer is not started because the device is not locked for debugging"
);
}
}

/// Stop the Watchdog Timer
/// Note: WDT is configured only if the device is in non-debug mode (i.e debug_locked = 1)
///
/// # Arguments
///
/// * `soc_ifc` - SOC Interface
#[cfg_attr(not(feature = "no-cfi"), cfi_mod_fn)]
pub fn stop_wdt(soc_ifc: &mut SocIfc) {
cprintln!("[state] Stopping the Watchdog Timer");
soc_ifc.configure_wdt1(false);
if soc_ifc.debug_locked() {
cprintln!("[state] Stopping the Watchdog Timer");
caliptra_common::wdt::stop_wdt(soc_ifc);
} else {
cprintln!(
"[state] Watchdog Timer is not stopped because the device is not locked for debugging"
);
}
}
18 changes: 18 additions & 0 deletions sw-emulator/app/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ const FW_LOAD_CMD_OPCODE: u32 = 0x4657_4C44;
/// The number of CPU clock cycles it takes to write the firmware to the mailbox.
const FW_WRITE_TICKS: u64 = 1000;

const EXPECTED_CALIPTRA_BOOT_TIME_IN_CYCLES: u64 = 20_000_000; // 20 million cycles

// CPU Main Loop (free_run no GDB)
fn free_run(mut cpu: Cpu<CaliptraRootBus>, trace_path: Option<PathBuf>) {
if let Some(path) = trace_path {
Expand Down Expand Up @@ -144,6 +146,12 @@ fn main() -> io::Result<()> {
.value_parser(value_parser!(String))
.default_value("unprovisioned"),
)
.arg(
arg!(--"wdt-timeout" <U64> "Watchdog Timer Timeout in CPU Clock Cycles")
.required(false)
.value_parser(value_parser!(u64))
.default_value(&(EXPECTED_CALIPTRA_BOOT_TIME_IN_CYCLES.to_string()))
)
.get_matches();

let args_rom = args.get_one::<PathBuf>("rom").unwrap();
Expand All @@ -152,6 +160,7 @@ fn main() -> io::Result<()> {
let args_log_dir = args.get_one::<PathBuf>("log-dir").unwrap();
let args_idevid_key_id_algo = args.get_one::<String>("idevid-key-id-algo").unwrap();
let args_ueid = args.get_one::<u64>("ueid").unwrap();
let wdt_timeout = args.get_one::<u64>("wdt-timeout").unwrap();
let mut mfg_pk_hash = match hex::decode(args.get_one::<String>("mfg-pk-hash").unwrap()) {
Ok(mfg_pk_hash) => mfg_pk_hash,
Err(_) => {
Expand Down Expand Up @@ -348,6 +357,15 @@ fn main() -> io::Result<()> {
soc_ifc.fuse_idevid_cert_attr().write(&cert);
}

// Populate cptra_wdt_cfg
{
soc_ifc.cptra_wdt_cfg().at(0).write(|_| *wdt_timeout as u32);
soc_ifc
.cptra_wdt_cfg()
.at(1)
.write(|_| (*wdt_timeout >> 32) as u32);
}

let cpu = Cpu::new(root_bus, clock);

// Check if Optional GDB Port is passed
Expand Down
5 changes: 4 additions & 1 deletion sw-emulator/lib/periph/src/soc_reg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,9 @@ struct SocRegistersImpl {
#[register_array(offset = 0x0200)]
fuse_uds_seed: [u32; FUSE_UDS_SEED_SIZE / 4],

#[register_array(offset = 0x110)]
cptra_wdt_cfg: [u32; 2],

#[register_array(offset = 0x0230)]
fuse_field_entropy: [u32; FUSE_FIELD_ENTROPY_SIZE / 4],

Expand Down Expand Up @@ -671,10 +674,10 @@ impl SocRegistersImpl {
cptra_wdt_status: ReadOnlyRegister::new(0),
op_wdt_timer1_expired_action: None,
op_wdt_timer2_expired_action: None,

etrng_responses: args.etrng_responses,
pending_etrng_response: None,
op_pending_etrng_response_action: None,
cptra_wdt_cfg: [0x0; 2],
};

regs
Expand Down

0 comments on commit 52e1a39

Please sign in to comment.