add configuration of external-mux GPIOs and Implement policy locking
diff --git a/target/ast10x0/board/BUILD.bazel b/target/ast10x0/board/BUILD.bazel
index e5baf59..34075da 100644
--- a/target/ast10x0/board/BUILD.bazel
+++ b/target/ast10x0/board/BUILD.bazel
@@ -18,5 +18,6 @@
     deps = [
         "//target/ast10x0/backend/i2c:i2c_backend_ast10x0",
         "//target/ast10x0/peripherals",
+        "@ast1060_pac",
     ],
 )
diff --git a/target/ast10x0/board/src/lib.rs b/target/ast10x0/board/src/lib.rs
index d1005b0..8e85720 100644
--- a/target/ast10x0/board/src/lib.rs
+++ b/target/ast10x0/board/src/lib.rs
@@ -20,7 +20,8 @@
 
 pub use monitor::Ast1060Monitor;
 pub use spim_wiring::{
-    apply_spim_pinctrl, apply_spim_wiring, presets, SpimWiring, SpimWiringError,
+    apply_spim_external_mux, apply_spim_pinctrl, apply_spim_wiring, apply_spim_wiring_with_log,
+    presets, SpimWiring, SpimWiringError,
 };
 
 pub use ast10x0_peripherals::i2c::{I2cConfig, I2cError};
@@ -133,7 +134,7 @@
 /// This is a placeholder; production code should use a proper timer or delay provider.
 /// Spins for approximately `micros` microseconds.
 #[inline]
-fn delay_us(micros: u32) {
+pub(crate) fn delay_us(micros: u32) {
     // Very rough approximation: ~16 cycles per microsecond on Cortex-M4 @ ~50MHz
     // This is calibration-free but inaccurate; improve for production.
     for _ in 0..(micros * 16) {
diff --git a/target/ast10x0/board/src/spim_wiring.rs b/target/ast10x0/board/src/spim_wiring.rs
index 8768562..ed247fd 100644
--- a/target/ast10x0/board/src/spim_wiring.rs
+++ b/target/ast10x0/board/src/spim_wiring.rs
@@ -20,9 +20,10 @@
 };
 use ast10x0_peripherals::smc::SmcController;
 use ast10x0_peripherals::spimonitor::{
-    LockedSpiMonitor, MonitorPolicy, SpiMonitor, SpiMonitorController, SpiMonitorError,
-    Uninitialized,
+    LockedSpiMonitor, MonitorPolicy, PassthroughMode, SpiMonitor, SpiMonitorController,
+    SpiMonitorError, Uninitialized,
 };
+use ast1060_pac as device;
 
 /// Static SPIM wiring for one SPI controller.
 ///
@@ -110,6 +111,63 @@
     scu.apply_pinctrl_group(group);
 }
 
+/// Drive the external mux-select GPIO pair described by the AST1060 DTS.
+///
+/// SPIM1/2 use GPIO A-D pin 12 and SGPIOM pin 0. SPIM3/4 use GPIO E-H
+/// pin 8 and SGPIOM pin 2. Both signals carry the same mux selection.
+pub fn apply_spim_external_mux(instance: SpiMonitorInstance, mux: ScuExtMuxSelect) {
+    let high = matches!(mux, ScuExtMuxSelect::Mux1);
+    let (gpio_group, gpio_mask, sgpio_mask) = match instance {
+        SpiMonitorInstance::Spim0 | SpiMonitorInstance::Spim1 => {
+            (ExternalMuxGpioGroup::Abcd, 1 << 12, 1 << 0)
+        }
+        SpiMonitorInstance::Spim2 | SpiMonitorInstance::Spim3 => {
+            (ExternalMuxGpioGroup::Efgh, 1 << 8, 1 << 2)
+        }
+    };
+
+    let gpio = unsafe { &*device::Gpio::ptr() };
+    match gpio_group {
+        ExternalMuxGpioGroup::Abcd => {
+            gpio.gpio000().modify(|r, w| unsafe {
+                w.bits(update_bit(r.bits(), gpio_mask, high))
+            });
+            gpio.gpio004().modify(|r, w| unsafe {
+                w.bits(r.bits() | gpio_mask)
+            });
+        }
+        ExternalMuxGpioGroup::Efgh => {
+            gpio.gpio020().modify(|r, w| unsafe {
+                w.bits(update_bit(r.bits(), gpio_mask, high))
+            });
+            gpio.gpio024().modify(|r, w| unsafe {
+                w.bits(r.bits() | gpio_mask)
+            });
+        }
+    }
+
+    let sgpio = unsafe { &*device::Sgpiom::ptr() };
+    sgpio.gpio500().modify(|r, w| unsafe {
+        w.bits(update_bit(r.bits(), sgpio_mask, high))
+    });
+
+    crate::delay_us(1_000);
+}
+
+#[derive(Clone, Copy)]
+enum ExternalMuxGpioGroup {
+    Abcd,
+    Efgh,
+}
+
+const fn update_bit(value: u32, mask: u32, set: bool) -> u32 {
+    if set {
+        value | mask
+    } else {
+        value & !mask
+    }
+}
+
 /// Apply static SPIM wiring at controller-init time.
 ///
 /// Order: validate → pinctrl → SCU route → passthrough → ext-mux →
@@ -126,14 +184,33 @@
     wiring: SpimWiring,
     policy: &MonitorPolicy,
 ) -> Result<LockedSpiMonitor, SpimWiringError> {
+    unsafe { apply_spim_wiring_with_log(scu, controller_id, wiring, policy, None) }
+}
+
+/// Apply static SPIM wiring and optionally configure violation-log DMA before
+/// the policy registers are locked.
+///
+/// # Safety
+/// The caller must satisfy [`apply_spim_wiring`] ownership requirements. A
+/// supplied log buffer must remain exclusively owned by the monitor forever.
+pub unsafe fn apply_spim_wiring_with_log(
+    scu: &ScuRegisters,
+    controller_id: SmcController,
+    wiring: SpimWiring,
+    policy: &MonitorPolicy,
+    log_buffer: Option<&'static mut [u32]>,
+) -> Result<LockedSpiMonitor, SpimWiringError> {
     validate_controller_for_source(controller_id, wiring.source)?;
     scu.validate_spim_instance(wiring.instance)?;
 
     apply_spim_pinctrl(scu, wiring.instance);
+    scu.disable_spim_cs_internal_pull_down(wiring.instance);
     scu.set_spim_internal_master_route(wiring.instance, wiring.source);
     scu.set_spim_passthrough(wiring.instance, wiring.passthrough);
     scu.set_spim_ext_mux(wiring.instance, wiring.ext_mux);
+    apply_spim_external_mux(wiring.instance, wiring.ext_mux);
     scu.set_spim_miso_multi_func(wiring.instance, wiring.miso_multi_func);
+    scu.set_spim_filter(wiring.instance, true);
 
     let monitor_controller = match wiring.instance {
         SpiMonitorInstance::Spim0 => SpiMonitorController::Spim0,
@@ -146,6 +223,12 @@
     // instance, mirroring the SCU exclusivity required above.
     let monitor = unsafe { SpiMonitor::<Uninitialized>::new(monitor_controller) };
     let configured = monitor.apply_policy(policy)?;
+    if let Some(buffer) = log_buffer {
+        configured.configure_log(buffer)?;
+    }
+    configured.set_push_pull(true);
+    configured.set_passthrough(PassthroughMode::Disabled);
+    configured.enable();
     let locked = configured.lock()?;
     Ok(locked)
 }
@@ -165,7 +248,9 @@
 
 /// Built-in `MonitorPolicy` presets vetted against the BMC's flash opcode set.
 pub mod presets {
-    use ast10x0_peripherals::spimonitor::MonitorPolicy;
+    use ast10x0_peripherals::spimonitor::{
+        profile, MonitorPolicy, PrivilegeDirection, PrivilegeOp,
+    };
 
     /// Allow-list for the BMC's normal flash opcodes covering both 3-byte and
     /// 4-byte addressing variants. Empty `regions` (no address-privilege
@@ -196,4 +281,18 @@
         p.allow_command_count = 13;
         p
     }
+
+    /// Policy matching the supplied Zephyr SPIM nodes: full command list and
+    /// write protection over flash addresses `0x0000_0000..0x0800_0000`.
+    #[must_use]
+    pub fn zephyr_spim_policy() -> MonitorPolicy {
+        let mut policy = profile::zephyr_default();
+        let _ = policy.add_region(
+            0,
+            0x0800_0000,
+            PrivilegeDirection::Write,
+            PrivilegeOp::Disable,
+        );
+        policy
+    }
 }
diff --git a/target/ast10x0/peripherals/scu/routing.rs b/target/ast10x0/peripherals/scu/routing.rs
index 2ebad91..b99d1bd 100644
--- a/target/ast10x0/peripherals/scu/routing.rs
+++ b/target/ast10x0/peripherals/scu/routing.rs
@@ -197,6 +197,41 @@
         });
     }
 
+    /// Enable or disable the SCU-side SPI monitor filter path. Uses SCU0F0[11:8].
+    pub fn set_spim_filter(&self, instance: SpiMonitorInstance, enable: bool) {
+        self.unlock_write_protection();
+        self.regs().scu0f0().modify(|_, w| match instance {
+            SpiMonitorInstance::Spim0 => w.enbl_filter_of_spipf1().bit(enable),
+            SpiMonitorInstance::Spim1 => w.enbl_filter_of_spipf2().bit(enable),
+            SpiMonitorInstance::Spim2 => w.enbl_filter_of_spipf3().bit(enable),
+            SpiMonitorInstance::Spim3 => w.enbl_filter_of_spipf4().bit(enable),
+        });
+    }
+
+    /// Disable the internal pull-down on the monitor CS output pin.
+    pub fn disable_spim_cs_internal_pull_down(&self, instance: SpiMonitorInstance) {
+        self.unlock_write_protection();
+        match instance {
+            SpiMonitorInstance::Spim0 => {
+                self.regs()
+                    .scu610()
+                    .modify(|r, w| unsafe { w.bits(r.bits() | (1 << 6)) });
+            }
+            SpiMonitorInstance::Spim1 => {
+                self.regs()
+                    .scu610()
+                    .modify(|r, w| unsafe { w.bits(r.bits() | (1 << 20)) });
+            }
+            // SPIM3's corresponding pin cannot have its pull-down disabled.
+            SpiMonitorInstance::Spim2 => {}
+            SpiMonitorInstance::Spim3 => {
+                self.regs()
+                    .scu614()
+                    .modify(|r, w| unsafe { w.bits(r.bits() | (1 << 16)) });
+            }
+        }
+    }
+
     /// Route an internal SPI master through the selected SPI monitor path.
     pub fn set_spim_internal_master_route(
         &self,
diff --git a/target/ast10x0/peripherals/spimonitor/commands.rs b/target/ast10x0/peripherals/spimonitor/commands.rs
index 3f528d9..034b7a3 100644
--- a/target/ast10x0/peripherals/spimonitor/commands.rs
+++ b/target/ast10x0/peripherals/spimonitor/commands.rs
@@ -94,6 +94,7 @@
         0x5a => command(opcode, true, false, true, false, 1, 8, 0, 3, 1),
         0xb7 | 0xe9 => command(opcode, false, false, false, false, 0, 0, 0, 0, 0),
         0xc5 => command(opcode, false, true, false, false, 1, 0, 0, 0, 0),
+        0xc2 => command(opcode, true, true, false, false, 1, 0, 0, 0, 0),
         _ => return None,
     };
     Some(entry)
@@ -136,4 +137,9 @@
         assert_eq!(fixed_slot(0xe9), Some(1));
         assert_eq!(fixed_slot(0xc5), Some(31));
     }
+
+    #[test]
+    fn winbond_die_select_matches_zephyr_encoding() {
+        assert_eq!(table_value(0xc2, false), Some(0x7100_00c2));
+    }
 }
diff --git a/target/ast10x0/peripherals/spimonitor/controller.rs b/target/ast10x0/peripherals/spimonitor/controller.rs
index 7b7e9e5..123f1de 100644
--- a/target/ast10x0/peripherals/spimonitor/controller.rs
+++ b/target/ast10x0/peripherals/spimonitor/controller.rs
@@ -7,7 +7,7 @@
 
 use crate::scu::registers::ScuRegisters;
 use crate::scu::types::{ScuExtMuxSelect, SpiMonitorInstance};
-use crate::spimonitor::commands::{fixed_slot, table_value};
+use crate::spimonitor::commands::{fixed_slot, table_value, LOCKED as COMMAND_LOCKED};
 use crate::spimonitor::policy::{MonitorPolicy, MAX_REGION_SLOTS};
 use crate::spimonitor::registers::{SpiMonitorController, SpiMonitorRegisters};
 use crate::spimonitor::types::{
@@ -38,38 +38,6 @@
 pub type LockedSpiMonitor = SpiMonitor<Locked>;
 
 // ---------------------------------------------------------------------------
-// Encoding helpers (hardware-format definitions)
-// ---------------------------------------------------------------------------
-
-/// Encode one address-filter slot word from a region policy entry.
-///
-/// Hardware slot format (pending datasheet confirmation):
-/// - bits[31:14] : region base address >> 14 (18-bit granule)
-/// - bit[13]     : direction (0 = read, 1 = write)
-/// - bit[12]     : op (0 = enable/allow, 1 = disable/block)
-/// - bits[11:0]  : length in 4 KiB units (length >> 12), clamped to 12 bits
-///
-/// TODO: replace with confirmed SPIPF register field encoding once available.
-fn encode_addr_filter_slot(
-    start: u32,
-    length: u32,
-    direction: PrivilegeDirection,
-    op: PrivilegeOp,
-) -> u32 {
-    let addr_field = (start >> 14) & 0x3_FFFF;
-    let dir_bit: u32 = match direction {
-        PrivilegeDirection::Read => 0,
-        PrivilegeDirection::Write => 1,
-    };
-    let op_bit: u32 = match op {
-        PrivilegeOp::Enable => 0,
-        PrivilegeOp::Disable => 1,
-    };
-    let len_field = (length >> 12) & 0xFFF;
-    (addr_field << 14) | (dir_bit << 13) | (op_bit << 12) | len_field
-}
-
-// ---------------------------------------------------------------------------
 // Uninitialized state
 // ---------------------------------------------------------------------------
 
@@ -100,11 +68,49 @@
         if policy.region_count > MAX_REGION_SLOTS {
             return Err(SpiMonitorError::InvalidRegion);
         }
+        if self.regs.read_lock_status() & LOCK_STATUS_REQUIRED != 0 {
+            return Err(SpiMonitorError::Locked);
+        }
+        for slot in 0..COMMAND_TABLE_SLOTS {
+            if self.regs.read_allow_cmd_slot(slot) & COMMAND_LOCKED != 0 {
+                return Err(SpiMonitorError::Locked);
+            }
+        }
 
-        let mut next_slot = 2usize;
+        let general_slot_limit = if policy.allow_commands[..policy.allow_command_count]
+            .contains(&0xc5)
+        {
+            LAST_GENERAL_COMMAND_SLOT_EXCLUSIVE
+        } else {
+            COMMAND_TABLE_SLOTS
+        };
+        let mut general_command_count = 0usize;
+        for opcode in policy.allow_commands[..policy.allow_command_count]
+            .iter()
+            .copied()
+        {
+            if table_value(opcode, false).is_none() {
+                return Err(SpiMonitorError::UnsupportedCommand(opcode));
+            }
+            if fixed_slot(opcode).is_none() {
+                general_command_count += 1;
+            }
+        }
+        if general_command_count > general_slot_limit - FIRST_GENERAL_COMMAND_SLOT {
+            return Err(SpiMonitorError::NoCommandSlot);
+        }
+        for region in policy.regions[..policy.region_count].iter().flatten() {
+            validate_privilege_region(region.start, region.length)?;
+        }
 
+        for slot in 0..COMMAND_TABLE_SLOTS {
+            self.regs.write_allow_cmd_slot(slot, 0);
+        }
+
+        let mut next_slot = FIRST_GENERAL_COMMAND_SLOT;
         // Slots 0 and 1 are reserved for EN4B and EX4B; slot 31 is reserved
-        // for WREAR. Other commands occupy slots 2 through 30.
+        // for WREAR when it is present. Otherwise slot 31 can hold a generic
+        // command, allowing the supplied 32-command Zephyr policy to fit.
         for i in 0..policy.allow_command_count {
             let opcode = policy.allow_commands[i];
             let value =
@@ -112,7 +118,7 @@
             let slot = match fixed_slot(opcode) {
                 Some(slot) => slot,
                 None => {
-                    if next_slot >= 31 {
+                    if next_slot >= general_slot_limit {
                         return Err(SpiMonitorError::NoCommandSlot);
                     }
                     let slot = next_slot;
@@ -121,18 +127,26 @@
                 }
             };
             self.regs.write_allow_cmd_slot(slot, value);
+            if self.regs.read_allow_cmd_slot(slot) != value {
+                return Err(SpiMonitorError::VerificationFailed);
+            }
         }
 
-        // Program address filter table.
+        // Empty policy means unrestricted access, matching the Zephyr driver:
+        // initialize both 256 MiB privilege maps to all-allowed, then apply
+        // the requested deny/allow regions.
+        initialize_privilege_table(&self.regs, PrivilegeDirection::Read)?;
+        initialize_privilege_table(&self.regs, PrivilegeDirection::Write)?;
+
         for i in 0..policy.region_count {
             if let Some(region) = policy.regions[i] {
-                let word = encode_addr_filter_slot(
+                configure_privilege_region(
+                    &self.regs,
                     region.start,
                     region.length,
                     region.direction,
                     region.op,
-                );
-                self.regs.write_addr_filter_slot(i, word);
+                )?;
             }
         }
 
@@ -148,6 +162,15 @@
     pub const fn state(&self) -> MonitorState {
         MonitorState::Uninitialized
     }
+
+    /// Pulse the SPIPF software-reset bit for at least 5 microseconds.
+    pub fn software_reset(&self) {
+        self.regs
+            .modify_ctrl(|bits| *bits |= CTRL_SW_RESET_BIT);
+        delay_cycles(SW_RESET_DELAY_CYCLES);
+        self.regs
+            .modify_ctrl(|bits| *bits &= !CTRL_SW_RESET_BIT);
+    }
 }
 
 // ---------------------------------------------------------------------------
@@ -231,6 +254,58 @@
         drain_log_impl(&self.regs, buf)
     }
 
+    /// Configure a caller-owned, static DMA buffer for violation logging.
+    ///
+    /// The hardware stores one 32-bit violation record per entry.
+    pub fn configure_log(&self, buffer: &'static mut [u32]) -> Result<()> {
+        if buffer.is_empty() || buffer.len() > MAX_LOG_ENTRIES {
+            return Err(SpiMonitorError::InvalidLogBuffer);
+        }
+        let address = buffer.as_mut_ptr() as usize;
+        if address & 0x3 != 0 || address > u32::MAX as usize {
+            return Err(SpiMonitorError::InvalidLogBuffer);
+        }
+        buffer.fill(0);
+        self.regs
+            .write_log_config(address as u32, buffer.len() as u32);
+        Ok(())
+    }
+
+    /// Use push-pull signaling for the monitor output path.
+    pub fn set_push_pull(&self, enable: bool) {
+        self.regs.modify_ctrl2(|bits| {
+            if enable {
+                *bits |= CTRL2_PUSH_PULL;
+            } else {
+                *bits &= !CTRL2_PUSH_PULL;
+            }
+        });
+    }
+
+    /// Enable command, write, and read violation interrupts in SPIPF004.
+    ///
+    /// Platform code must install and enable the corresponding NVIC handler.
+    pub fn enable_violation_interrupts(&self) {
+        self.regs
+            .modify_ctrl2(|bits| *bits |= CTRL2_VIOLATION_IRQ_ENABLE_MASK);
+    }
+
+    /// Return currently pending command/write/read violation status bits.
+    #[must_use]
+    pub fn pending_violations(&self) -> u32 {
+        self.regs.read_ctrl2() & CTRL2_VIOLATION_STATUS_MASK
+    }
+
+    /// Acknowledge all currently pending violation status bits.
+    pub fn acknowledge_violations(&self) -> u32 {
+        let pending = self.pending_violations();
+        if pending != 0 {
+            self.regs
+                .modify_ctrl2(|bits| *bits |= pending);
+        }
+        pending
+    }
+
     /// Lock monitor policy registers and transition to `Locked`.
     ///
     /// Activates all write-protection bits to prevent further policy changes.
@@ -239,9 +314,32 @@
     /// - Lock all command table entries
     /// - Write-disable SPIPF000, SPIPF004, SPIPF010, SPIPF014
     pub fn lock(self) -> Result<SpiMonitor<Locked>> {
-        // Placeholder: This single bit write is incomplete.
-        // Full lock requires SPIPF07C write-disable bits per aspeed-rust pattern.
-        self.regs.modify_ctrl(|bits| *bits |= CTRL_LOCK_BIT);
+        for slot in 0..COMMAND_TABLE_SLOTS {
+            let value = self.regs.read_allow_cmd_slot(slot);
+            self.regs
+                .write_allow_cmd_slot(slot, value | COMMAND_LOCKED);
+        }
+
+        self.regs
+            .modify_ctrl(|bits| *bits |= CTRL_BLOCK_FIFO_LOCK | CTRL_SW_RESET_LOCK);
+        self.regs.modify_lock_status(|bits| {
+            *bits |= LOCK_CTRL
+                | LOCK_IRQ_CTRL
+                | LOCK_LOG_BASE
+                | LOCK_LOG_CTRL
+                | LOCK_WRITE_PRIVILEGE
+                | LOCK_READ_PRIVILEGE;
+        });
+
+        let lock_status = self.regs.read_lock_status();
+        if lock_status & LOCK_STATUS_REQUIRED != LOCK_STATUS_REQUIRED {
+            return Err(SpiMonitorError::LockFailed);
+        }
+        for slot in 0..COMMAND_TABLE_SLOTS {
+            if self.regs.read_allow_cmd_slot(slot) & COMMAND_LOCKED == 0 {
+                return Err(SpiMonitorError::LockFailed);
+            }
+        }
 
         Ok(SpiMonitor {
             regs: self.regs,
@@ -262,19 +360,6 @@
 // ---------------------------------------------------------------------------
 
 impl SpiMonitor<Locked> {
-    /// Configure passthrough mode in locked state.
-    ///
-    /// Passthrough is intentionally available post-lock because it is used
-    /// during mux ownership transitions at runtime (e.g., BMC boot-hold/release).
-    pub fn set_passthrough(&self, mode: PassthroughMode) {
-        self.regs.modify_ctrl(|bits| match mode {
-            PassthroughMode::Enabled => {
-                *bits = (*bits & !CTRL_PASSTHROUGH_MASK) | CTRL_SINGLE_PASSTHROUGH_BIT
-            }
-            PassthroughMode::Disabled => *bits &= !CTRL_PASSTHROUGH_MASK,
-        });
-    }
-
     /// Select the external SPI mux routing in locked state.
     ///
     /// Available post-lock for mux ownership transitions at runtime (e.g., BMC boot-hold/release).
@@ -355,14 +440,123 @@
 const CTRL_MONITOR_ENABLE_BIT: u32 = 1 << 2;
 #[allow(dead_code)]
 const CTRL_SW_RESET_BIT: u32 = 1 << 15;
-#[allow(dead_code)]
-const CTRL_LOCK_BIT: u32 = 1 << 31; // PLACEHOLDER - NOT in SPIPF000! See note below.
-                                    //
-                                    // NOTE: CTRL_EXT_MUX_SEL and CTRL_LOCK are NOT in SPIPF000 register:
-                                    // - ExtMux is controlled via SCU0F0 register (ext_mux_select_sig_of_spipfN bits)
-                                    //   See aspeed-rust: spim_ext_mux_config()
-                                    // - Lock is controlled via SPIPF07C write-disable bits and individual command
-                                    //   table entry lock bits. See aspeed-rust: spim_lock_common(), spim_lock_rw_priv_table()
+const CTRL_BLOCK_FIFO_LOCK: u32 = 1 << 22;
+const CTRL_SW_RESET_LOCK: u32 = 1 << 23;
+const CTRL2_VIOLATION_STATUS_MASK: u32 = 0x7;
+const CTRL2_VIOLATION_IRQ_ENABLE_MASK: u32 = 0x7 << 16;
+const CTRL2_PUSH_PULL: u32 = 1 << 31;
+
+const COMMAND_TABLE_SLOTS: usize = 32;
+const FIRST_GENERAL_COMMAND_SLOT: usize = 2;
+const LAST_GENERAL_COMMAND_SLOT_EXCLUSIVE: usize = 31;
+const PRIVILEGE_TABLE_WORDS: usize = 512;
+const PRIVILEGE_BLOCK_SIZE: u32 = 16 * 1024;
+const PRIVILEGE_ADDRESS_LIMIT: u32 = 256 * 1024 * 1024;
+const PRIVILEGE_READ_SELECT: u32 = 0x5200_0000;
+const PRIVILEGE_WRITE_SELECT: u32 = 0x5700_0000;
+const MAX_LOG_ENTRIES: usize = 0x7_ffff;
+const SW_RESET_DELAY_CYCLES: u32 = 1_000;
+
+const LOCK_CTRL: u32 = 1 << 0;
+const LOCK_IRQ_CTRL: u32 = 1 << 1;
+const LOCK_LOG_BASE: u32 = 1 << 4;
+const LOCK_LOG_CTRL: u32 = 1 << 5;
+const LOCK_WRITE_PRIVILEGE: u32 = 1 << 30;
+const LOCK_READ_PRIVILEGE: u32 = 1 << 31;
+const LOCK_STATUS_REQUIRED: u32 = LOCK_CTRL
+    | LOCK_IRQ_CTRL
+    | LOCK_LOG_BASE
+    | LOCK_LOG_CTRL
+    | LOCK_WRITE_PRIVILEGE
+    | LOCK_READ_PRIVILEGE;
+
+fn select_privilege_table(regs: &SpiMonitorRegisters, direction: PrivilegeDirection) {
+    let selection = match direction {
+        PrivilegeDirection::Read => PRIVILEGE_READ_SELECT,
+        PrivilegeDirection::Write => PRIVILEGE_WRITE_SELECT,
+    };
+    regs.modify_ctrl(|bits| *bits = (*bits & 0x00ff_ffff) | selection);
+}
+
+fn initialize_privilege_table(
+    regs: &SpiMonitorRegisters,
+    direction: PrivilegeDirection,
+) -> Result<()> {
+    select_privilege_table(regs, direction);
+    for index in 0..PRIVILEGE_TABLE_WORDS {
+        regs.write_addr_filter_slot(index, u32::MAX);
+    }
+    if regs.read_addr_filter_slot(0) != u32::MAX
+        || regs.read_addr_filter_slot(PRIVILEGE_TABLE_WORDS - 1) != u32::MAX
+    {
+        return Err(SpiMonitorError::VerificationFailed);
+    }
+    Ok(())
+}
+
+fn configure_privilege_region(
+    regs: &SpiMonitorRegisters,
+    start: u32,
+    length: u32,
+    direction: PrivilegeDirection,
+    op: PrivilegeOp,
+) -> Result<()> {
+    validate_privilege_region(start, length)?;
+    let end = start
+        .checked_add(length)
+        .ok_or(SpiMonitorError::InvalidLength)?
+        .min(PRIVILEGE_ADDRESS_LIMIT);
+    let aligned_start = start / PRIVILEGE_BLOCK_SIZE * PRIVILEGE_BLOCK_SIZE;
+    let aligned_end = end
+        .checked_add(PRIVILEGE_BLOCK_SIZE - 1)
+        .ok_or(SpiMonitorError::InvalidLength)?
+        / PRIVILEGE_BLOCK_SIZE
+        * PRIVILEGE_BLOCK_SIZE;
+    let mut block = aligned_start / PRIVILEGE_BLOCK_SIZE;
+    let end_block = aligned_end / PRIVILEGE_BLOCK_SIZE;
+
+    select_privilege_table(regs, direction);
+    while block < end_block {
+        let word_index = (block / 32) as usize;
+        let bit_index = block % 32;
+        let remaining = end_block - block;
+        let updated = if bit_index == 0 && remaining >= 32 {
+            block += 32;
+            match op {
+                PrivilegeOp::Enable => u32::MAX,
+                PrivilegeOp::Disable => 0,
+            }
+        } else {
+            block += 1;
+            let value = regs.read_addr_filter_slot(word_index);
+            match op {
+                PrivilegeOp::Enable => value | (1 << bit_index),
+                PrivilegeOp::Disable => value & !(1 << bit_index),
+            }
+        };
+        regs.write_addr_filter_slot(word_index, updated);
+        if regs.read_addr_filter_slot(word_index) != updated {
+            return Err(SpiMonitorError::VerificationFailed);
+        }
+    }
+    Ok(())
+}
+
+fn validate_privilege_region(start: u32, length: u32) -> Result<()> {
+    if start >= PRIVILEGE_ADDRESS_LIMIT {
+        return Err(SpiMonitorError::InvalidAddress);
+    }
+    if length == 0 || start.checked_add(length).is_none() {
+        return Err(SpiMonitorError::InvalidLength);
+    }
+    Ok(())
+}
+
+fn delay_cycles(cycles: u32) {
+    for _ in 0..cycles {
+        core::hint::spin_loop();
+    }
+}
 
 /// Shared drain-log implementation used by both `Configured` and `Locked`.
 fn drain_log_impl<'a>(
@@ -370,7 +564,7 @@
     buf: &'a mut [ViolationLogEntry],
 ) -> &'a [ViolationLogEntry] {
     let log_base = regs.log_ram_base_addr();
-    let max_entries = regs.read_log_max_sz() as usize / core::mem::size_of::<u32>();
+    let max_entries = regs.read_log_capacity_entries() as usize;
     let write_idx = regs.read_log_idx_reg() as usize;
 
     let available = write_idx.min(max_entries);
diff --git a/target/ast10x0/peripherals/spimonitor/profile.rs b/target/ast10x0/peripherals/spimonitor/profile.rs
index 4e498df..dd6290e 100644
--- a/target/ast10x0/peripherals/spimonitor/profile.rs
+++ b/target/ast10x0/peripherals/spimonitor/profile.rs
@@ -30,3 +30,16 @@
     p.allow_command_count = 6;
     p
 }
+
+/// Full command allow-list used by the AST1060 Zephyr device tree.
+#[must_use]
+pub const fn zephyr_default() -> MonitorPolicy {
+    let mut p = MonitorPolicy::empty();
+    p.allow_commands = [
+        0x03, 0x13, 0x0b, 0x0c, 0x6b, 0x6c, 0x01, 0x05, 0x35, 0x06, 0x04, 0x20, 0x21, 0x9f,
+        0x5a, 0xb7, 0xe9, 0x32, 0x34, 0xd8, 0xdc, 0x02, 0x12, 0x3b, 0x3c, 0x70, 0xbb, 0xbc,
+        0x50, 0xeb, 0xec, 0xc2,
+    ];
+    p.allow_command_count = p.allow_commands.len();
+    p
+}
diff --git a/target/ast10x0/peripherals/spimonitor/registers.rs b/target/ast10x0/peripherals/spimonitor/registers.rs
index b0a6252..a44e308 100644
--- a/target/ast10x0/peripherals/spimonitor/registers.rs
+++ b/target/ast10x0/peripherals/spimonitor/registers.rs
@@ -138,6 +138,17 @@
         self.regs().spipf07c().write(|w| unsafe { w.bits(value) });
     }
 
+    pub fn modify_lock_status<F>(&self, f: F)
+    where
+        F: FnOnce(&mut u32),
+    {
+        self.regs().spipf07c().modify(|r, w| {
+            let mut bits = r.bits();
+            f(&mut bits);
+            unsafe { w.bits(bits) }
+        });
+    }
+
     /// SPIPFWT[n]: Allow-command table entry.
     pub fn read_allow_cmd_slot(&self, index: usize) -> u32 {
         self.regs().spipfwt(index).read().bits()
@@ -164,22 +175,38 @@
     // Violation log registers
     // -----------------------------------------------------------------------
 
-    /// Current violation log write index (number of entries written so far).
-    ///
+    /// Current violation log write index (number of 32-bit entries written).
     pub fn read_log_idx_reg(&self) -> u32 {
-        self.regs().spipf018().read().bits()
+        self.regs()
+            .spipf018()
+            .read()
+            .block_log_dmawr_pointer()
+            .bits()
     }
 
-    /// Maximum violation log capacity in bytes.
-    ///
-    pub fn read_log_max_sz(&self) -> u32 {
-        self.regs().spipf014().read().bits() & 0x0007_ffff
+    /// Maximum violation log capacity in 32-bit entries.
+    pub fn read_log_capacity_entries(&self) -> u32 {
+        self.regs()
+            .spipf014()
+            .read()
+            .size_of_block_log_dmabuffer()
+            .bits()
     }
 
     /// Base address of the violation log RAM region.
-    ///
-    /// Returns a `usize` suitable for casting to `*const u32` by the caller.
     pub fn log_ram_base_addr(&self) -> usize {
         (self.regs().spipf010().read().bits() & !0x3) as usize
     }
+
+    /// Configure the violation-log DMA buffer.
+    pub fn write_log_config(&self, base_addr: u32, entries: u32) {
+        self.regs()
+            .spipf010()
+            .write(|w| unsafe { w.bits(base_addr & !0x3) });
+        self.regs()
+            .spipf014()
+            .write(|w| unsafe { w.bits(LOG_DMA_ENABLE | entries) });
+    }
 }
+
+const LOG_DMA_ENABLE: u32 = 1 << 31;
diff --git a/target/ast10x0/peripherals/spimonitor/types.rs b/target/ast10x0/peripherals/spimonitor/types.rs
index 6471407..3be10bf 100644
--- a/target/ast10x0/peripherals/spimonitor/types.rs
+++ b/target/ast10x0/peripherals/spimonitor/types.rs
@@ -127,10 +127,15 @@
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
 pub enum SpiMonitorError {
     InvalidRegion,
+    InvalidAddress,
+    InvalidLength,
     InvalidSlot,
+    InvalidLogBuffer,
     UnsupportedCommand(u8),
     NoCommandSlot,
     Locked,
+    LockFailed,
+    VerificationFailed,
     InvalidTransition,
 }