diff --git a/services/fwmanager/api/BUILD.bazel b/services/fwmanager/api/BUILD.bazel new file mode 100644 index 00000000..d78ebb6a --- /dev/null +++ b/services/fwmanager/api/BUILD.bazel @@ -0,0 +1,23 @@ +# Licensed under the Apache-2.0 license +# SPDX-License-Identifier: Apache-2.0 + +load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test") + +rust_library( + name = "fwmanager_api", + srcs = [ + "src/boot_control.rs", + "src/lib.rs", + ], + edition = "2024", + visibility = ["//visibility:public"], + deps = [ + "//hal/blocking", + ], +) + +# Host tests: build on the host platform, no kernel/QEMU. +rust_test( + name = "fwmanager_api_test", + crate = ":fwmanager_api", +) diff --git a/services/fwmanager/api/src/boot_control.rs b/services/fwmanager/api/src/boot_control.rs new file mode 100644 index 00000000..5f33c810 --- /dev/null +++ b/services/fwmanager/api/src/boot_control.rs @@ -0,0 +1,206 @@ +// Licensed under the Apache-2.0 license +// SPDX-License-Identifier: Apache-2.0 + +//! Actuation capability: hold a managed device in reset and release it. + +use openprot_hal_blocking::system_control::ResetControl; + +/// Actuation capability: hold a managed device in reset and release it. +/// +/// Stateless pass-through by design — sequencing discipline (hold before +/// release, release only after verification) belongs to the orchestrator +/// flows, where it is observable behavior. +/// +/// # How the orchestrator uses it +/// +/// During verified release the orchestrator parks a device in +/// reset, verifies its active slot while nothing is running, then releases +/// it to boot the image it just checked: +/// +/// ```ignore +/// // `dev` is this device's BootControl, obtained from the registry. +/// fn verified_release(dev: &mut D) -> Result<(), D::Error> { +/// dev.hold_in_reset()?; // freeze the device; its flash is now safe to inspect +/// verify_active_slot()?; // re-hash + signature check (a separate capability) +/// dev.release()?; // run the just-verified image +/// Ok(()) +/// } +/// ``` +/// +/// In a trial boot the same hold/release pair brackets a +/// watchdog-bounded window; the new slot is committed only if a good boot is +/// observed, otherwise the device falls back to the previous slot: +/// +/// ```ignore +/// dev.hold_in_reset()?; +/// store.set_trial(new_slot)?; // tentative boot selection — not yet committed +/// dev.release()?; // boot the trial image +/// match monitor.await_boot(window)? { +/// Booted => store.commit(new_slot)?, // observed good => make it active +/// Failed | Timeout => { /* nothing committed; previous slot still active */ } +/// } +/// ``` +pub trait BootControl { + /// The error type reported by this device's boot control. + type Error: core::fmt::Debug; + + /// Holds the device in reset. + fn hold_in_reset(&mut self) -> Result<(), Self::Error>; + + /// Releases the device from reset. + fn release(&mut self) -> Result<(), Self::Error>; +} + +/// Binds one reset line of a HAL reset controller to one managed device. +pub struct HalBootControl { + controller: C, + reset_id: C::ResetId, +} + +impl HalBootControl { + /// Creates the binding of `controller`'s line `reset_id` to a device. + pub fn new(controller: C, reset_id: C::ResetId) -> Self { + Self { + controller, + reset_id, + } + } + + /// Read access to the underlying controller. + pub fn controller(&self) -> &C { + &self.controller + } +} + +impl BootControl for HalBootControl { + type Error = C::Error; + + fn hold_in_reset(&mut self) -> Result<(), Self::Error> { + self.controller.reset_assert(&self.reset_id) + } + + fn release(&mut self) -> Result<(), Self::Error> { + self.controller.reset_deassert(&self.reset_id) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use core::time::Duration; + use openprot_hal_blocking::system_control::{Error, ErrorKind, ErrorType}; + + // Normally set in config.rs + const BMC_LINE: u8 = 7; + + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + enum Call { + Assert(u8), + Deassert(u8), + } + + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + struct FakeError(ErrorKind); + + impl Error for FakeError { + fn kind(&self) -> ErrorKind { + self.0 + } + } + + /// Fake HAL reset controller: records every call it receives. + struct FakeResetController { + calls: Vec, + fail: Option, + } + + impl FakeResetController { + fn new() -> Self { + Self { + calls: Vec::new(), + fail: None, + } + } + + fn failing(kind: ErrorKind) -> Self { + Self { + calls: Vec::new(), + fail: Some(kind), + } + } + + fn calls(&self) -> &[Call] { + &self.calls + } + } + + impl ErrorType for FakeResetController { + type Error = FakeError; + } + + impl ResetControl for FakeResetController { + type ResetId = u8; // Reset line is GPIO here. Real driver should use Enum + + fn reset_assert(&mut self, reset_id: &u8) -> Result<(), FakeError> { + if let Some(kind) = self.fail { + return Err(FakeError(kind)); + } + self.calls.push(Call::Assert(*reset_id)); + Ok(()) + } + + fn reset_deassert(&mut self, reset_id: &u8) -> Result<(), FakeError> { + if let Some(kind) = self.fail { + return Err(FakeError(kind)); + } + self.calls.push(Call::Deassert(*reset_id)); + Ok(()) + } + + fn reset_pulse(&mut self, _: &u8, _: Duration) -> Result<(), FakeError> { + panic!( + "BootControl must never pulse: hold and release are distinct orchestrator steps" + ); + } + + fn reset_is_asserted(&self, _: &u8) -> Result { + panic!("BootControl does not query line state"); + } + } + + // `hold_in_reset()` must assert exactly the configured line (BMC = 7) + // and nothing else. + #[test] + fn holding_a_device_in_reset_asserts_its_configured_line() { + let mut bmc = HalBootControl::new(FakeResetController::new(), BMC_LINE); + + bmc.hold_in_reset().expect("hold_in_reset failed"); + + assert_eq!(bmc.controller().calls(), &[Call::Assert(BMC_LINE)]); + } + + #[test] + fn holding_a_device_in_reset_deasserts_its_configured_line() { + let mut bmc = HalBootControl::new(FakeResetController::new(), BMC_LINE); + + bmc.hold_in_reset().expect("hold_in_reset failed"); + bmc.release().expect("release failed"); + + assert_eq!( + bmc.controller().calls(), + &[Call::Assert(BMC_LINE), Call::Deassert(BMC_LINE)] + ); + } + + #[test] + fn controller_error_propagates_through_boot_control() { + let mut bmc = HalBootControl::new( + FakeResetController::failing(ErrorKind::InvalidResetId), + BMC_LINE, + ); + let err = bmc + .hold_in_reset() + .expect_err("expected the controller error to propagate"); + assert_eq!(err.kind(), ErrorKind::InvalidResetId); + } +} diff --git a/services/fwmanager/api/src/lib.rs b/services/fwmanager/api/src/lib.rs new file mode 100644 index 00000000..8ab488a2 --- /dev/null +++ b/services/fwmanager/api/src/lib.rs @@ -0,0 +1,15 @@ +// Licensed under the Apache-2.0 license +// SPDX-License-Identifier: Apache-2.0 + +//! Device-facing capability traits for the Boot Orchestrator. +//! +//! `BootControl` is the actuation capability: the orchestrator drives a +//! single managed device's reset without knowing which controller line it +//! maps to. The binding of a HAL reset controller line to a device happens +//! once, in platform configuration, via [`HalBootControl`]. + +#![cfg_attr(not(test), no_std)] + +mod boot_control; + +pub use boot_control::{BootControl, HalBootControl}; diff --git a/target/ast10x0/board/BUILD.bazel b/target/ast10x0/board/BUILD.bazel index e5baf596..ea878a69 100644 --- a/target/ast10x0/board/BUILD.bazel +++ b/target/ast10x0/board/BUILD.bazel @@ -8,7 +8,7 @@ rust_library( name = "ast10x0_board", srcs = [ "src/lib.rs", - "src/monitor.rs", + "src/spi_monitor.rs", "src/spim_wiring.rs", ], crate_name = "ast10x0_board", diff --git a/target/ast10x0/board/src/lib.rs b/target/ast10x0/board/src/lib.rs index e12c05d7..5db23555 100644 --- a/target/ast10x0/board/src/lib.rs +++ b/target/ast10x0/board/src/lib.rs @@ -14,10 +14,10 @@ use ast10x0_peripherals::scu::{ClockRegisterHalf, ScuRegisterHalf}; use ast10x0_peripherals::scu::{PinctrlPin, ScuRegisters}; -pub mod monitor; +pub mod spi_monitor; pub mod spim_wiring; -pub use monitor::Ast1060Monitor; +pub use spi_monitor::Ast1060SpiMonitor; pub use spim_wiring::{apply_spim_wiring, presets, SpimWiring, SpimWiringError}; pub use ast10x0_peripherals::i2c::{I2cConfig, I2cError}; diff --git a/target/ast10x0/board/src/monitor.rs b/target/ast10x0/board/src/spi_monitor.rs similarity index 78% rename from target/ast10x0/board/src/monitor.rs rename to target/ast10x0/board/src/spi_monitor.rs index ae48623b..c3aef110 100644 --- a/target/ast10x0/board/src/monitor.rs +++ b/target/ast10x0/board/src/spi_monitor.rs @@ -3,7 +3,7 @@ //! Board-level SPI Monitor orchestration. //! -//! `Ast1060Monitor` is the concrete implementation of the `Monitor` trait, orchestrating +//! `Ast1060SpiMonitor` is the concrete implementation of the `Monitor` trait, orchestrating //! both SCU (for external mux control) and SPIPF (for enforcement, filtering, and locks). //! //! This layer ensures no duplication: mux control delegates to SCU routing, and SPIPF @@ -12,9 +12,9 @@ use ast10x0_peripherals::scu::registers::ScuRegisters; use ast10x0_peripherals::scu::types::{ScuExtMuxSelect, SpiMonitorInstance}; use ast10x0_peripherals::spimonitor::registers::SpiMonitorRegisters; -use ast10x0_peripherals::spimonitor::traits::Monitor; +use ast10x0_peripherals::spimonitor::traits::SpiMonitorControl; use ast10x0_peripherals::spimonitor::types::{ - BootError, BootResult, MonitorInstance, MonitorStatus, MuxSelect, PrivilegeDirection, + BootError, BootResult, SpiMonitorId, SpiMonitorStatus, MuxSelect, PrivilegeDirection, PrivilegeOp, }; @@ -29,11 +29,11 @@ use ast10x0_peripherals::spimonitor::types::{ /// ```ignore /// let mut board = Ast1060Board::init(); /// let mut monitor = board.monitor(); -/// monitor.set_mux(MonitorInstance::Spim0, MuxSelect::RotControl)?; +/// monitor.set_mux(SpiMonitorId::Spim0, MuxSelect::RotControl)?; /// monitor.set_address_privilege(/*...*/)?; -/// monitor.lock_policy(MonitorInstance::Spim0)?; +/// monitor.lock_policy(SpiMonitorId::Spim0)?; /// ``` -pub struct Ast1060Monitor<'a> { +pub struct Ast1060SpiMonitor<'a> { scu: &'a mut ScuRegisters, spipf: &'a mut [SpiMonitorRegisters; 4], read_blocked_region_count: u8, @@ -41,7 +41,7 @@ pub struct Ast1060Monitor<'a> { write_blocked_region_count: u8, } -impl<'a> Ast1060Monitor<'a> { +impl<'a> Ast1060SpiMonitor<'a> { /// Create a new board-level Monitor with access to both SCU and SPIPF. pub fn new( scu: &'a mut ScuRegisters, @@ -59,33 +59,33 @@ impl<'a> Ast1060Monitor<'a> { /// Get the register accessor for the specified monitor instance. #[inline] - fn regs(&self, instance: MonitorInstance) -> &SpiMonitorRegisters { + fn regs(&self, instance: SpiMonitorId) -> &SpiMonitorRegisters { match instance { - MonitorInstance::Spim0 => &self.spipf[0], - MonitorInstance::Spim1 => &self.spipf[1], - MonitorInstance::Spim2 => &self.spipf[2], - MonitorInstance::Spim3 => &self.spipf[3], + SpiMonitorId::Spim0 => &self.spipf[0], + SpiMonitorId::Spim1 => &self.spipf[1], + SpiMonitorId::Spim2 => &self.spipf[2], + SpiMonitorId::Spim3 => &self.spipf[3], } } /// Get mutable reference to register accessor (for write operations). #[inline] - fn regs_mut(&mut self, instance: MonitorInstance) -> &mut SpiMonitorRegisters { + fn regs_mut(&mut self, instance: SpiMonitorId) -> &mut SpiMonitorRegisters { match instance { - MonitorInstance::Spim0 => &mut self.spipf[0], - MonitorInstance::Spim1 => &mut self.spipf[1], - MonitorInstance::Spim2 => &mut self.spipf[2], - MonitorInstance::Spim3 => &mut self.spipf[3], + SpiMonitorId::Spim0 => &mut self.spipf[0], + SpiMonitorId::Spim1 => &mut self.spipf[1], + SpiMonitorId::Spim2 => &mut self.spipf[2], + SpiMonitorId::Spim3 => &mut self.spipf[3], } } - /// Map MonitorInstance to SCU SpiMonitorInstance for routing operations. - fn instance_to_scu(instance: MonitorInstance) -> SpiMonitorInstance { + /// Map SpiMonitorId to SCU SpiMonitorInstance for routing operations. + fn instance_to_scu(instance: SpiMonitorId) -> SpiMonitorInstance { match instance { - MonitorInstance::Spim0 => SpiMonitorInstance::Spim0, - MonitorInstance::Spim1 => SpiMonitorInstance::Spim1, - MonitorInstance::Spim2 => SpiMonitorInstance::Spim2, - MonitorInstance::Spim3 => SpiMonitorInstance::Spim3, + SpiMonitorId::Spim0 => SpiMonitorInstance::Spim0, + SpiMonitorId::Spim1 => SpiMonitorInstance::Spim1, + SpiMonitorId::Spim2 => SpiMonitorInstance::Spim2, + SpiMonitorId::Spim3 => SpiMonitorInstance::Spim3, } } @@ -125,8 +125,8 @@ impl<'a> Ast1060Monitor<'a> { } } -impl<'a> Monitor for Ast1060Monitor<'a> { - fn set_mux(&mut self, instance: MonitorInstance, mux: MuxSelect) -> BootResult<()> { +impl<'a> SpiMonitorControl for Ast1060SpiMonitor<'a> { + fn set_mux(&mut self, instance: SpiMonitorId, mux: MuxSelect) -> BootResult<()> { // External mux selection is controlled via SCU0F0 register. // Delegate to SCU routing layer which has the actual register access. let scu_instance = Self::instance_to_scu(instance); @@ -135,14 +135,14 @@ impl<'a> Monitor for Ast1060Monitor<'a> { Ok(()) } - fn read_mux(&self, instance: MonitorInstance) -> BootResult { + fn read_mux(&self, instance: SpiMonitorId) -> BootResult { // Read from SCU0F0 register via SCU routing layer. let scu_instance = Self::instance_to_scu(instance); let scu_mux = self.scu.get_spim_ext_mux(scu_instance); Ok(Self::scu_to_mux(scu_mux)) } - fn soft_reset(&mut self, instance: MonitorInstance) -> BootResult<()> { + fn soft_reset(&mut self, instance: SpiMonitorId) -> BootResult<()> { let regs = self.regs_mut(instance); // Soft reset clears status/logs but preserves policy. // NON-BLOCKING TODO 1: Verify soft reset bit position from AST10x0 datasheet. @@ -157,7 +157,7 @@ impl<'a> Monitor for Ast1060Monitor<'a> { Ok(()) } - fn hardware_reset(&mut self, instance: MonitorInstance) -> BootResult<()> { + fn hardware_reset(&mut self, instance: SpiMonitorId) -> BootResult<()> { let regs = self.regs_mut(instance); // Full hardware reset of all state (SPIPF and related SCU registers). // NON-BLOCKING TODO 1: Verify hardware reset bit and sequence from AST10x0 datasheet. @@ -173,7 +173,7 @@ impl<'a> Monitor for Ast1060Monitor<'a> { fn set_address_privilege( &mut self, - instance: MonitorInstance, + instance: SpiMonitorId, start_addr: u32, end_addr: u32, _direction: PrivilegeDirection, @@ -200,14 +200,14 @@ impl<'a> Monitor for Ast1060Monitor<'a> { Ok(()) } - fn read_region_count(&self, _instance: MonitorInstance) -> BootResult { + fn read_region_count(&self, _instance: SpiMonitorId) -> BootResult { // Region count is tracked in memory (following aspeed-rust pattern). // aspeed-rust stores read_blocked_region_num and write_blocked_region_num as struct fields. // We return the read-blocked region count for now; write-blocked can be exposed via separate method if needed. Ok(self.read_blocked_region_count as u32) } - fn read_status(&self, instance: MonitorInstance) -> BootResult { + fn read_status(&self, instance: SpiMonitorId) -> BootResult { let regs = self.regs(instance); let ctrl = regs.read_ctrl(); let lock_status = regs.read_lock_status(); @@ -217,7 +217,7 @@ impl<'a> Monitor for Ast1060Monitor<'a> { let scu_mux = self.scu.get_spim_ext_mux(scu_instance); let mux = Self::scu_to_mux(scu_mux); - Ok(MonitorStatus { + Ok(SpiMonitorStatus { mux, policy_locked: Self::is_policy_locked(lock_status), enforcement_active: Self::is_enforcement_active(ctrl), @@ -232,7 +232,7 @@ impl<'a> Monitor for Ast1060Monitor<'a> { true } - fn lock_policy(&mut self, instance: MonitorInstance) -> BootResult<()> { + fn lock_policy(&mut self, instance: SpiMonitorId) -> BootResult<()> { if !self.supports_policy_lock() { return Err(BootError::LockedOutFromMonitor); } @@ -246,7 +246,7 @@ impl<'a> Monitor for Ast1060Monitor<'a> { Ok(()) } - fn verify_policy_locked(&self, instance: MonitorInstance) -> BootResult<()> { + fn verify_policy_locked(&self, instance: SpiMonitorId) -> BootResult<()> { if !self.supports_policy_lock() { return Ok(()); } @@ -267,13 +267,13 @@ mod tests { #[test] fn test_enforcement_flag() { - assert!(!Ast1060Monitor::is_enforcement_active(0)); - assert!(Ast1060Monitor::is_enforcement_active(1 << 4)); + assert!(!Ast1060SpiMonitor::is_enforcement_active(0)); + assert!(Ast1060SpiMonitor::is_enforcement_active(1 << 4)); } #[test] fn test_lock_flag() { - assert!(!Ast1060Monitor::is_policy_locked(0)); - assert!(Ast1060Monitor::is_policy_locked(1)); + assert!(!Ast1060SpiMonitor::is_policy_locked(0)); + assert!(Ast1060SpiMonitor::is_policy_locked(1)); } } diff --git a/target/ast10x0/board/src/spim_wiring.rs b/target/ast10x0/board/src/spim_wiring.rs index d33289db..5698acf1 100644 --- a/target/ast10x0/board/src/spim_wiring.rs +++ b/target/ast10x0/board/src/spim_wiring.rs @@ -16,7 +16,7 @@ use ast10x0_peripherals::scu::{ }; use ast10x0_peripherals::smc::SmcController; use ast10x0_peripherals::spimonitor::{ - LockedSpiMonitor, MonitorPolicy, SpiMonitor, SpiMonitorController, SpiMonitorError, + LockedSpiMonitor, SpiMonitorPolicy, SpiMonitor, SpiMonitorController, SpiMonitorError, Uninitialized, }; @@ -96,7 +96,7 @@ impl From for SpimWiringError { /// /// Order: validate → SCU route → passthrough → ext-mux → MISO multi-func → /// SPIPF policy → SPIPF lock. The lock is one-way; an empty -/// `MonitorPolicy::empty()` combined with lock will brick the SPI bus until +/// `SpiMonitorPolicy::empty()` combined with lock will brick the SPI bus until /// reset, so callers should pass a vetted preset (see [`presets`]). /// /// # Safety @@ -106,7 +106,7 @@ pub unsafe fn apply_spim_wiring( scu: &ScuRegisters, controller_id: SmcController, wiring: SpimWiring, - policy: &MonitorPolicy, + policy: &SpiMonitorPolicy, ) -> Result { validate_controller_for_source(controller_id, wiring.source)?; scu.validate_spim_instance(wiring.instance)?; @@ -144,9 +144,9 @@ fn validate_controller_for_source( } } -/// Built-in `MonitorPolicy` presets vetted against the BMC's flash opcode set. +/// Built-in `SpiMonitorPolicy` presets vetted against the BMC's flash opcode set. pub mod presets { - use ast10x0_peripherals::spimonitor::MonitorPolicy; + use ast10x0_peripherals::spimonitor::SpiMonitorPolicy; /// Allow-list for the BMC's normal flash opcodes covering both 3-byte and /// 4-byte addressing variants. Empty `regions` (no address-privilege @@ -159,8 +159,8 @@ pub mod presets { /// `RDSR` (`0x05`), `WREN` (`0x06`), `WRDI` (`0x04`), /// `RDID` (`0x9F`), `RSTEN` (`0x66`), `RST` (`0x99`). #[must_use] - pub const fn bmc_default_policy() -> MonitorPolicy { - let mut p = MonitorPolicy::empty(); + pub const fn bmc_default_policy() -> SpiMonitorPolicy { + let mut p = SpiMonitorPolicy::empty(); p.allow_commands[0] = 0x03; // READ p.allow_commands[1] = 0x0B; // FAST_READ p.allow_commands[2] = 0x0C; // FAST_READ_4B diff --git a/target/ast10x0/peripherals/spimonitor/controller.rs b/target/ast10x0/peripherals/spimonitor/controller.rs index 39b1a1ee..a3c66f28 100644 --- a/target/ast10x0/peripherals/spimonitor/controller.rs +++ b/target/ast10x0/peripherals/spimonitor/controller.rs @@ -7,10 +7,10 @@ use core::marker::PhantomData; use crate::scu::registers::ScuRegisters; use crate::scu::types::{ScuExtMuxSelect, SpiMonitorInstance}; -use crate::spimonitor::policy::{MonitorPolicy, MAX_REGION_SLOTS}; +use crate::spimonitor::policy::{SpiMonitorPolicy, MAX_REGION_SLOTS}; use crate::spimonitor::registers::{SpiMonitorController, SpiMonitorRegisters}; use crate::spimonitor::types::{ - ExtMuxSel, LockState, MonitorState, PassthroughMode, PrivilegeDirection, PrivilegeOp, Result, + ExtMuxSel, LockState, SpiMonitorState, PassthroughMode, PrivilegeDirection, PrivilegeOp, Result, SpiMonitorError, ViolationLogEntry, }; @@ -92,7 +92,7 @@ impl SpiMonitor { /// Returns `Err(InvalidSlot)` if `allow_command_count` exceeds the command /// table length. Returns `Err(InvalidRegion)` if `region_count` exceeds /// `MAX_REGION_SLOTS`. - pub fn apply_policy(self, policy: &MonitorPolicy) -> Result> { + pub fn apply_policy(self, policy: &SpiMonitorPolicy) -> Result> { if policy.allow_command_count > policy.allow_commands.len() { return Err(SpiMonitorError::InvalidSlot); } @@ -128,8 +128,8 @@ impl SpiMonitor { } #[must_use] - pub const fn state(&self) -> MonitorState { - MonitorState::Uninitialized + pub const fn state(&self) -> SpiMonitorState { + SpiMonitorState::Uninitialized } } @@ -233,8 +233,8 @@ impl SpiMonitor { } #[must_use] - pub const fn state(&self) -> MonitorState { - MonitorState::Configured + pub const fn state(&self) -> SpiMonitorState { + SpiMonitorState::Configured } } @@ -300,8 +300,8 @@ impl SpiMonitor { } #[must_use] - pub const fn state(&self) -> MonitorState { - MonitorState::Locked + pub const fn state(&self) -> SpiMonitorState { + SpiMonitorState::Locked } } diff --git a/target/ast10x0/peripherals/spimonitor/mod.rs b/target/ast10x0/peripherals/spimonitor/mod.rs index 8f649e14..5e289972 100644 --- a/target/ast10x0/peripherals/spimonitor/mod.rs +++ b/target/ast10x0/peripherals/spimonitor/mod.rs @@ -14,14 +14,14 @@ pub use controller::{ Configured, ConfiguredSpiMonitor, Locked, LockedSpiMonitor, SpiMonitor, UninitSpiMonitor, Uninitialized, }; -pub use policy::{MonitorPolicy, MAX_CMD_SLOTS, MAX_REGION_SLOTS}; +pub use policy::{SpiMonitorPolicy, MAX_CMD_SLOTS, MAX_REGION_SLOTS}; pub use registers::{ SpiMonitorController, SpiMonitorRegisters, SPIPF1_BASE, SPIPF2_BASE, SPIPF3_BASE, SPIPF4_BASE, SPIPF_REG_SIZE, }; -pub use traits::Monitor; +pub use traits::SpiMonitorControl; pub use types::{ - BootConfig, BootError, BootPhase, BootResult, ExtMuxSel, LockState, MonitorInstance, - MonitorState, MonitorStatus, MuxSelect, PassthroughMode, PrivilegeDirection, PrivilegeOp, + BootConfig, BootError, BootPhase, BootResult, ExtMuxSel, LockState, SpiMonitorId, + SpiMonitorState, SpiMonitorStatus, MuxSelect, PassthroughMode, PrivilegeDirection, PrivilegeOp, RegionPolicy, Result as SpiMonitorResult, SpiMonitorError, ViolationLogEntry, }; diff --git a/target/ast10x0/peripherals/spimonitor/policy.rs b/target/ast10x0/peripherals/spimonitor/policy.rs index fee1082c..2d969062 100644 --- a/target/ast10x0/peripherals/spimonitor/policy.rs +++ b/target/ast10x0/peripherals/spimonitor/policy.rs @@ -13,14 +13,14 @@ pub const MAX_CMD_SLOTS: usize = 32; /// Policy payload applied to a monitor instance. #[derive(Clone, Debug)] -pub struct MonitorPolicy { +pub struct SpiMonitorPolicy { pub allow_commands: [u8; MAX_CMD_SLOTS], pub allow_command_count: usize, pub regions: [Option; MAX_REGION_SLOTS], pub region_count: usize, } -impl MonitorPolicy { +impl SpiMonitorPolicy { #[must_use] pub const fn empty() -> Self { Self { diff --git a/target/ast10x0/peripherals/spimonitor/profile.rs b/target/ast10x0/peripherals/spimonitor/profile.rs index 4e498dfe..458fc9d9 100644 --- a/target/ast10x0/peripherals/spimonitor/profile.rs +++ b/target/ast10x0/peripherals/spimonitor/profile.rs @@ -4,15 +4,15 @@ //! Built-in SPI monitor policy profiles. //! //! Profiles provide command allow-lists only. Region entries are platform -//! policy and must be added by the caller via `MonitorPolicy::add_region` +//! policy and must be added by the caller via `SpiMonitorPolicy::add_region` //! using the PFM or provisioned manifest for the specific device. -use crate::spimonitor::policy::MonitorPolicy; +use crate::spimonitor::policy::SpiMonitorPolicy; /// Runtime profile: read-focused allow-list suitable for steady-state boot/runtime. #[must_use] -pub const fn runtime_read_only() -> MonitorPolicy { - let mut p = MonitorPolicy::empty(); +pub const fn runtime_read_only() -> SpiMonitorPolicy { + let mut p = SpiMonitorPolicy::empty(); p.allow_commands[0] = 0x03; // READ p.allow_commands[1] = 0x0B; // FAST_READ p.allow_commands[2] = 0x9F; // RDID @@ -22,7 +22,7 @@ pub const fn runtime_read_only() -> MonitorPolicy { /// Update profile: expands allow-list for controlled erase/program flows. #[must_use] -pub const fn firmware_update_window() -> MonitorPolicy { +pub const fn firmware_update_window() -> SpiMonitorPolicy { let mut p = runtime_read_only(); p.allow_commands[3] = 0x06; // WREN p.allow_commands[4] = 0x20; // SE diff --git a/target/ast10x0/peripherals/spimonitor/traits.rs b/target/ast10x0/peripherals/spimonitor/traits.rs index af455d6c..a427740d 100644 --- a/target/ast10x0/peripherals/spimonitor/traits.rs +++ b/target/ast10x0/peripherals/spimonitor/traits.rs @@ -7,7 +7,7 @@ //! with SPI monitor hardware during platform initialization. use super::types::{ - BootError, BootResult, MonitorInstance, MonitorStatus, MuxSelect, PrivilegeDirection, + BootError, BootResult, SpiMonitorId, SpiMonitorStatus, MuxSelect, PrivilegeDirection, PrivilegeOp, }; @@ -16,18 +16,18 @@ use super::types::{ /// Implementations of this trait provide register-level access to SPI monitor /// blocks via PAC or other register models. Boot code uses this trait to remain /// independent of the concrete register implementation. -pub trait Monitor { +pub trait SpiMonitorControl { /// Set monitor mux to ROT or Host control. - fn set_mux(&mut self, instance: MonitorInstance, mux: MuxSelect) -> BootResult<()>; + fn set_mux(&mut self, instance: SpiMonitorId, mux: MuxSelect) -> BootResult<()>; /// Read current mux setting. - fn read_mux(&self, instance: MonitorInstance) -> BootResult; + fn read_mux(&self, instance: SpiMonitorId) -> BootResult; /// Soft reset monitor (clears status/logs, preserves policy). - fn soft_reset(&mut self, instance: MonitorInstance) -> BootResult<()>; + fn soft_reset(&mut self, instance: SpiMonitorId) -> BootResult<()>; /// Hardware reset monitor (full reset of all state). - fn hardware_reset(&mut self, instance: MonitorInstance) -> BootResult<()>; + fn hardware_reset(&mut self, instance: SpiMonitorId) -> BootResult<()>; /// Configure an address privilege region. /// @@ -39,7 +39,7 @@ pub trait Monitor { /// * `op` - Allow or Block access fn set_address_privilege( &mut self, - instance: MonitorInstance, + instance: SpiMonitorId, start_addr: u32, end_addr: u32, direction: PrivilegeDirection, @@ -47,10 +47,10 @@ pub trait Monitor { ) -> BootResult<()>; /// Read number of configured address privilege regions. - fn read_region_count(&self, instance: MonitorInstance) -> BootResult; + fn read_region_count(&self, instance: SpiMonitorId) -> BootResult; /// Read monitor status snapshot. - fn read_status(&self, instance: MonitorInstance) -> BootResult; + fn read_status(&self, instance: SpiMonitorId) -> BootResult; /// Check if policy write-lock is supported by this monitor. fn supports_policy_lock(&self) -> bool { @@ -60,7 +60,7 @@ pub trait Monitor { /// Lock policy tables to prevent further modification. /// /// Returns error if not supported by hardware. - fn lock_policy(&mut self, _instance: MonitorInstance) -> BootResult<()> { + fn lock_policy(&mut self, _instance: SpiMonitorId) -> BootResult<()> { if !self.supports_policy_lock() { return Err(BootError::LockedOutFromMonitor); } @@ -70,7 +70,7 @@ pub trait Monitor { /// Verify that policy is locked (if supported). /// /// No-op if policy lock is not supported. - fn verify_policy_locked(&self, instance: MonitorInstance) -> BootResult<()> { + fn verify_policy_locked(&self, instance: SpiMonitorId) -> BootResult<()> { if !self.supports_policy_lock() { return Ok(()); } diff --git a/target/ast10x0/peripherals/spimonitor/types.rs b/target/ast10x0/peripherals/spimonitor/types.rs index 4be2d823..a74a8576 100644 --- a/target/ast10x0/peripherals/spimonitor/types.rs +++ b/target/ast10x0/peripherals/spimonitor/types.rs @@ -52,7 +52,7 @@ pub enum LockState { /// High-level monitor lifecycle stage. #[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum MonitorState { +pub enum SpiMonitorState { Uninitialized, Configured, Locked, @@ -143,7 +143,7 @@ pub type Result = core::result::Result; /// /// Maps to SPIPF1-4 hardware blocks on AST10x0. #[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum MonitorInstance { +pub enum SpiMonitorId { /// SPIPF1 (0x7E79_1000) - typically BMC/SMC flash Spim0, /// SPIPF2 (0x7E79_2000) - typically BMC dual flash @@ -186,7 +186,7 @@ impl From for MuxSelect { /// Monitor status snapshot at a point in time. #[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct MonitorStatus { +pub struct SpiMonitorStatus { /// Current mux routing pub mux: MuxSelect, /// Whether policy tables are write-locked