| // SPDX-FileCopyrightText: Copyright The arm-tzc Contributors. |
| // SPDX-License-Identifier: MIT OR Apache-2.0 |
| #![no_std] |
| #![doc = include_str!("../README.md")] |
| #![deny(clippy::undocumented_unsafe_blocks)] |
| #![deny(unsafe_op_in_unsafe_fn)] |
| |
| mod fail; |
| mod region; |
| mod utils; |
| |
| use bitflags::bitflags; |
| use safe_mmio::{ |
| SharedMmioPointer, UniqueMmioPointer, field, field_shared, |
| fields::{ReadOnly, ReadPure, ReadPureWrite, WriteOnly}, |
| }; |
| use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; |
| |
| pub use crate::fail::{FailAccessDirection, FailControlRegister, FailIDRegister, TzcFail}; |
| pub use crate::region::{RegionAttributes, RegionIDAccess, TzcRegion, TzcRegionMut}; |
| use crate::{ |
| fail::FailRegisters, |
| region::RegionRegisters, |
| utils::{extract_bits, get_bit, set_bit}, |
| }; |
| |
| macro_rules! for_in_slice { |
| ($var:ident in $slice:expr, $($body:stmt)*) => { |
| let mut i = 0; |
| while i < $slice.len() { |
| let $var = &$slice[i]; |
| $($body)* |
| i += 1; |
| } |
| }; |
| } |
| pub(crate) use for_in_slice; |
| |
| #[derive(Debug, Clone, Copy, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)] |
| /// Provides information about the configuration of the TZC-400. |
| #[repr(transparent)] |
| pub struct BuildConfigRegister(u32); |
| |
| impl BuildConfigRegister { |
| // Bit values for number of filters. |
| const NF_1: u32 = 0b00; |
| const NF_2: u32 = 0b01; |
| const NF_4: u32 = 0b11; |
| |
| /// Returns the number of filter units in the design implementation. |
| pub const fn number_of_filters(&self) -> usize { |
| match extract_bits(self.0, 25, 24) { |
| Self::NF_1 => 1, |
| Self::NF_2 => 2, |
| Self::NF_4 => 4, |
| _ => panic!("Use of reserved value for number of filters"), |
| } |
| } |
| |
| // Bit values for address width. |
| const AW_32: u32 = 0b011111; |
| const AW_36: u32 = 0b100011; |
| const AW_40: u32 = 0b100111; |
| const AW_48: u32 = 0b101111; |
| const AW_64: u32 = 0b111111; |
| |
| /// Returns the width of the ACE-Lite address bus. |
| pub const fn address_width(&self) -> usize { |
| match extract_bits(self.0, 13, 8) { |
| Self::AW_32 => 32, |
| Self::AW_36 => 36, |
| Self::AW_40 => 40, |
| Self::AW_48 => 48, |
| Self::AW_64 => 64, |
| _ => panic!("Use of reserved value for address width"), |
| } |
| } |
| |
| // Bit values for number of regions. |
| const NR_9: u32 = 0b01000; |
| |
| /// Returns the number of regions that the TZC-400 provides. |
| pub fn number_of_regions(&self) -> usize { |
| match extract_bits(self.0, 4, 0) { |
| Self::NR_9 => 9, |
| _ => panic!("Use of reserved value for number of regions"), |
| } |
| } |
| } |
| |
| #[derive(Debug, Clone, Copy, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)] |
| /// Controls the interrupt and bus response signaling behavior of the TZC-400 when region permission |
| /// failures occur. |
| #[repr(transparent)] |
| pub struct ActionRegister(u32); |
| |
| impl ActionRegister { |
| /// Sets TZCINT LOW and issues an OKAY response. |
| pub const TZCINTLOW_OKAY: ActionRegister = ActionRegister(0b00); |
| /// Sets TZCINT LOW and issues a DECERR response. |
| pub const TZCINTLOW_DECERR: ActionRegister = ActionRegister(0b01); |
| /// Sets TZCINT HIGH and issues an OKAY response. |
| pub const TZCINTHIGH_OKAY: ActionRegister = ActionRegister(0b10); |
| /// Sets TZCINT HIGH and issues a DECERR response. |
| pub const TZCINTHIGH_DECERR: ActionRegister = ActionRegister(0b11); |
| } |
| |
| #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
| #[repr(u32)] |
| /// See [`GateKeeper::status`]. |
| pub enum GateKeeperStatus { |
| /// Access to the associated filter is not permitted. |
| Closed = 0, |
| /// Access to the associated filter is permitted. |
| Opened = 1, |
| } |
| |
| #[derive(Debug)] |
| /// Provides control and status for the gate keeper in each filter unit implemented. |
| /// |
| /// ## Usage constraints Closing the gate can cause accesses on the command channels to stall and can inadvertently cause a bus deadlock. Use this with caution. |
| pub struct GateKeeper<'reg> { |
| reg: SharedMmioPointer<'reg, ReadPureWrite<u32>>, |
| index: usize, |
| } |
| |
| impl<'reg> GateKeeper<'reg> { |
| /// The current state of the gate keeper in the filter unit at `index`. `index` must be the |
| /// index of a valid filter in the TZ-ASC configuration. |
| pub fn status(&self) -> GateKeeperStatus { |
| if get_bit(self.reg.read(), self.index + 16) { |
| GateKeeperStatus::Opened |
| } else { |
| GateKeeperStatus::Closed |
| } |
| } |
| } |
| |
| #[derive(Debug)] |
| /// Provides control and status for the gate keeper in each filter unit implemented. |
| /// |
| /// ## Usage constraints |
| /// |
| /// Closing the gate can cause accesses on the command channels to stall and can inadvertently cause |
| /// a bus deadlock. Use this with caution. |
| pub struct GateKeeperMut<'reg> { |
| reg: UniqueMmioPointer<'reg, ReadPureWrite<u32>>, |
| index: usize, |
| } |
| |
| impl<'reg> GateKeeperMut<'reg> { |
| /// See [GateKeeper::status]. |
| pub fn status(&self) -> GateKeeperStatus { |
| GateKeeper { |
| reg: *self.reg, |
| index: self.index, |
| } |
| .status() |
| } |
| |
| /// Request the gate at `index` to be set to `status`. The request will take effect once all |
| /// outstanding accesses through the filter unit are complete. |
| pub fn request(&mut self, status: GateKeeperStatus) { |
| let mut value = self.reg.read(); |
| |
| set_bit( |
| &mut value, |
| self.index, |
| matches!(status, GateKeeperStatus::Opened), |
| ); |
| self.reg.write(value); |
| |
| while self.status() != status {} |
| } |
| } |
| |
| #[derive(Debug, Clone, Copy, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)] |
| /// Controls the read access speculation and write access speculation. |
| #[repr(transparent)] |
| pub struct SpeculationControlRegister(u32); |
| |
| bitflags! { |
| impl SpeculationControlRegister: u32 { |
| /// Disables read access speculation. |
| /// |
| /// Note: This bit is ignored and assumed to be zero at a filter unit if the corresponding |
| /// QVNENABLE signal is HIGH. |
| const READ_SPEC_DISABLE = 1 << 0; |
| |
| /// Disables write access speculation. |
| /// |
| /// Note: This bit is ignored and assumed to be zero at a filter unit if the corresponding |
| /// QVNENABLE signal is HIGH. |
| const WRITE_SPEC_DISABLE = 1 << 1; |
| } |
| } |
| |
| #[derive(Debug, Clone, Copy, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)] |
| /// Contains the status of the interrupt signal, *TZCINT*, that reports access security violations |
| /// or region overlap errors. |
| #[repr(transparent)] |
| pub struct InterruptStatusRegister(u32); |
| |
| impl InterruptStatusRegister { |
| /// When a bit is set to 1 it indicates a violation of the overlap region configuration rules |
| /// for the associated filter unit. This occurs when an access matches with two enabled regions |
| /// at the same time unless the overlap is only with Region 0. |
| /// |
| /// This bit is set even if the [`ActionRegister`] is set to not drive the interrupt. |
| /// |
| /// When this bit is true, the interrupt status bit is also set to true. |
| /// |
| /// Clear the interrupt status of the associated bit in the [`InterruptClearRegister`] to also |
| /// clear this field. |
| pub const fn overlap(&self, index: usize) -> bool { |
| assert!(index < 4); |
| get_bit(self.0, index + 16) |
| } |
| |
| /// When a bit is set to `true`, it indicates the occurrence of two or more region permission or |
| /// region overlapping failures at the associated filter unit after the interrupt was cleared by |
| /// the associated bit in the [`InterruptClearRegister`]. |
| /// |
| /// This bit is set even if the [`ActionRegister`] is set to not drive the interrupt. |
| /// |
| /// Clear the interrupt status of the associated bit in the [`InterruptClearRegister`] to also |
| /// clear this field. |
| pub const fn overrun(&self, index: usize) -> bool { |
| assert!(index < 4); |
| get_bit(self.0, index + 8) |
| } |
| |
| /// This field indicates the status of the interrupt from the corresponding filter unit as |
| /// follows: |
| /// - `false`: Interrupt is not asserted. |
| /// - `true`: Interrupt is asserted and waiting to be cleared. |
| /// |
| /// This bit is set even if the [`ActionRegister`] is set to not drive the interrupt output |
| /// TZCINT HIGH. |
| /// |
| /// Therefore, the status acts as an indicator that a region permission check failure or an |
| /// overlap error has occurred at a particular filter unit. |
| pub const fn status(&self, index: usize) -> bool { |
| assert!(index < 4); |
| get_bit(self.0, index) |
| } |
| } |
| |
| #[derive( |
| Debug, Clone, Copy, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq, Default, |
| )] |
| /// Clears the interrupt. |
| #[repr(transparent)] |
| pub struct InterruptClearRegister(u32); |
| |
| impl InterruptClearRegister { |
| /// Creates an instance of [`InterruptClearRegister`] requesting to clear all the interrupts |
| /// present in `interrupts`. |
| pub const fn new(interrupts: &[usize]) -> Self { |
| let mut s = Self(0); |
| |
| for_in_slice!(int in interrupts, s.0 |= 1 << *int); |
| |
| s |
| } |
| } |
| /// View over the TZC memory. |
| #[derive(Debug, Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)] |
| #[repr(C, align(4))] |
| pub struct TzcRegisters { |
| /// 0x000: Build configuration register. |
| build_config: ReadPure<BuildConfigRegister>, |
| /// 0x004: Action register. |
| action: ReadPureWrite<ActionRegister>, |
| /// 0x008: Gate keeper register. |
| gate_keeper: ReadPureWrite<u32>, |
| /// 0x00C: Speculation register. |
| speculation_ctrl: ReadPureWrite<SpeculationControlRegister>, |
| /// 0x010: Interrupt status register. |
| int_status: ReadOnly<InterruptStatusRegister>, |
| /// 0x014: Interrupt clear register. |
| int_clear: WriteOnly<InterruptClearRegister>, |
| /// 0x018-0x020: reserved. |
| reserved_18: [u32; 2], |
| |
| /// 0x020-0x060: The fail status registers provide information about an access that fails |
| /// because of insufficient permissions or an overlap condition. A separate set of fail status |
| /// registers is provided for each filter unit. |
| fail_registers: [FailRegisters; 4], |
| /// 0x060-0x100: reserved. |
| reserved_60: [u32; 40], |
| |
| /// 0x100-0x218: Registers for region 0 to 8 (included). |
| /// |
| /// The region control registers control the address space that each region controls and the |
| /// security attributes to use for that region. |
| region_registers: [RegionRegisters; 9], |
| |
| /// 0x220-0xf00: reserved. |
| /// |
| /// Although the specification states that this reserved range should start at 0x218, the 8 |
| /// first reserved bytes are actually at the end of the `RegionRegister` entry for Region 9. |
| reserved_220: [u32; 823], |
| padding_f00: [u32; 53], |
| |
| /// 0xFD0: Peripheral ID 4 register. |
| pid4: ReadPure<u32>, |
| /// 0xFD4: Peripheral ID 5 register. |
| pid5: ReadPure<u32>, |
| /// 0xFD8: Peripheral ID 6 register. |
| pid6: ReadPure<u32>, |
| /// 0xFDC: Peripheral ID 7 register. |
| pid7: ReadPure<u32>, |
| /// 0xFE0: Peripheral ID 0 register. |
| pid0: ReadPure<u32>, |
| /// 0xFE4: Peripheral ID 1 register. |
| pid1: ReadPure<u32>, |
| /// 0xFE8: Peripheral ID 2 register. |
| pid2: ReadPure<u32>, |
| /// 0xFEC: Peripheral ID 3 register. |
| pid3: ReadPure<u32>, |
| |
| /// 0xFF0: Component ID 0 register. |
| cid0: ReadPure<u32>, |
| /// 0xFF4: Component ID 1 register. |
| cid1: ReadPure<u32>, |
| /// 0xFF8: Component ID 2 register. |
| cid2: ReadPure<u32>, |
| /// 0xFFC: Component ID 3 register. |
| cid3: ReadPure<u32>, |
| } |
| |
| /// Interface to manipulate the [TZC-400 TrustZone Address Space Controller](https://developer.arm.com/documentation/ddi0504/latest). |
| pub struct Tzc<'a> { |
| regs: UniqueMmioPointer<'a, TzcRegisters>, |
| } |
| |
| impl<'a> Tzc<'a> { |
| /// Creates new TZC instance from a [`UniqueMmioPointer`] that points to the memory region of |
| /// the Controller. |
| pub const fn new(regs: UniqueMmioPointer<'a, TzcRegisters>) -> Self { |
| Self { regs } |
| } |
| |
| /// Returns the [`BuildConfigRegister`] for this TZC-400 instance. |
| pub fn build_configuration(&self) -> BuildConfigRegister { |
| field_shared!(self.regs, build_config).read() |
| } |
| |
| /// Returns the current [`ActionRegister`] configuration. |
| pub fn action(&self) -> ActionRegister { |
| field_shared!(self.regs, action).read() |
| } |
| /// Sets the [`ActionRegister`] configuration. |
| pub fn set_action(&mut self, value: ActionRegister) { |
| field!((self.regs), action).write(value); |
| } |
| |
| /// Returns a [`GateKeeper`] wrapper to read from the Gate Keeper Register. |
| pub fn gate_keeper<'reg>(&'reg self, index: usize) -> Option<GateKeeper<'reg>> { |
| if index < self.build_configuration().number_of_filters() { |
| Some(GateKeeper { |
| reg: field_shared!(self.regs, gate_keeper), |
| index, |
| }) |
| } else { |
| None |
| } |
| } |
| |
| /// Returns a [`GateKeeperMut`] wrapper to read from/write to the Gate Keeper Register. |
| pub fn gate_keeper_mut<'reg>(&'reg mut self, index: usize) -> Option<GateKeeperMut<'reg>> { |
| if index < self.build_configuration().number_of_filters() { |
| Some(GateKeeperMut { |
| reg: field!(self.regs, gate_keeper), |
| index, |
| }) |
| } else { |
| None |
| } |
| } |
| |
| /// Returns the current [`SpeculationControlRegister`] configuration. |
| pub fn speculation_control(&self) -> SpeculationControlRegister { |
| field_shared!(self.regs, speculation_ctrl).read() |
| } |
| /// Sets the [`SpeculationControlRegister`] configuration. |
| pub fn set_speculation_control(&mut self, value: SpeculationControlRegister) { |
| field!(self.regs, speculation_ctrl).write(value); |
| } |
| |
| /// Returns the current [`InterruptStatusRegister`]. |
| pub fn interrupt_status(&mut self) -> InterruptStatusRegister { |
| field!(self.regs, int_status).read() |
| } |
| |
| /// Sets the [`InterruptClearRegister`]. |
| pub fn clear_interrupts(&mut self, value: InterruptClearRegister) { |
| field!(self.regs, int_clear).write(value); |
| } |
| |
| /// Returns a [`TzcFail`] structure to read from the failure register of the filter at `index`, |
| /// or `None` if `index` is out of bounds. |
| pub fn filter_failure<'regs>(&'regs self, idx: usize) -> Option<TzcFail<'regs>> { |
| if idx < self.build_configuration().number_of_filters() { |
| let fails = field_shared!(self.regs, fail_registers); |
| fails.get(idx).map(|regs| TzcFail { regs }) |
| } else { |
| None |
| } |
| } |
| |
| /// Returns a [`TzcRegion`] structure to read from the region registers at `index`, or `None` if |
| /// `index` is out of bounds. |
| pub fn region<'regs>(&'regs self, idx: usize) -> Option<TzcRegion<'regs>> { |
| let config = self.build_configuration(); |
| |
| if idx < config.number_of_regions() { |
| let regions = field_shared!(self.regs, region_registers); |
| regions.get(idx).map(|regs| TzcRegion { |
| regs, |
| address_width_mask: ((1 << config.address_width()) - 1), |
| }) |
| } else { |
| None |
| } |
| } |
| |
| /// Returns a [`TzcRegionMut`] structure to read and write from the region registers at `index`, |
| /// or `None` if `index` is out of bounds. |
| pub fn region_mut<'regs>(&'regs mut self, idx: usize) -> Option<TzcRegionMut<'regs>> { |
| let config = self.build_configuration(); |
| |
| if idx < config.number_of_regions() { |
| let regions = field!(self.regs, region_registers); |
| regions.take(idx).map(|regs| TzcRegionMut { |
| regs, |
| addresses_writable: idx != 0, |
| address_width_mask: ((1 << config.address_width()) - 1), |
| }) |
| } else { |
| None |
| } |
| } |
| |
| /// Returns the peripheral ID, which is `0x4002BB460`. |
| pub fn peripheral_id(&self) -> u64 { |
| let values = [ |
| field_shared!(self.regs, pid0).read(), |
| field_shared!(self.regs, pid1).read(), |
| field_shared!(self.regs, pid2).read(), |
| field_shared!(self.regs, pid3).read(), |
| field_shared!(self.regs, pid4).read(), |
| field_shared!(self.regs, pid5).read(), |
| field_shared!(self.regs, pid6).read(), |
| field_shared!(self.regs, pid7).read(), |
| ]; |
| |
| values |
| .into_iter() |
| .map(|v| v as u64) |
| .enumerate() |
| .fold(0, |acc, (idx, value)| (value << (8 * idx as u64)) | acc) |
| } |
| |
| /// Holds a 32 bit component ID value. Used for automatic BIOS configuration. The result is |
| /// `0xB105F00D`. |
| pub fn component_id(&self) -> u64 { |
| let values = [ |
| field_shared!(self.regs, cid0).read(), |
| field_shared!(self.regs, cid1).read(), |
| field_shared!(self.regs, cid2).read(), |
| field_shared!(self.regs, cid3).read(), |
| ]; |
| |
| values |
| .into_iter() |
| .map(|v| v as u64) |
| .enumerate() |
| .fold(0, |acc, (idx, value)| (value << (8 * idx as u64)) | acc) |
| } |
| } |
| |
| #[cfg(test)] |
| pub(crate) mod tests { |
| use safe_mmio::UniqueMmioPointer; |
| use zerocopy::transmute_mut; |
| |
| use crate::*; |
| |
| const DEFAULT_BUILD_CONFIG: u32 = 0b0000_0001_0000_0000_0010_1111_0000_1000; |
| |
| pub(crate) struct FakeTZCRegisters { |
| regs: [u32; 1024], |
| } |
| |
| impl FakeTZCRegisters { |
| pub fn new() -> Self { |
| let mut s = Self { regs: [0u32; 1024] }; |
| s.reg_write(0x000, DEFAULT_BUILD_CONFIG); |
| s |
| } |
| |
| pub fn reg_write(&mut self, offset: usize, value: u32) { |
| self.regs[offset / 4] = value; |
| } |
| |
| pub fn reg_read(&self, offset: usize) -> u32 { |
| self.regs[offset / 4] |
| } |
| |
| fn get(&mut self) -> UniqueMmioPointer<'_, TzcRegisters> { |
| UniqueMmioPointer::from(transmute_mut!(&mut self.regs)) |
| } |
| |
| pub fn tzc_for_test(&mut self) -> Tzc<'_> { |
| Tzc::new(self.get()) |
| } |
| } |
| |
| #[test] |
| fn size() { |
| assert_eq!(size_of::<TzcRegisters>(), 4096); |
| } |
| |
| #[test] |
| fn build_config() { |
| let mut regs = FakeTZCRegisters::new(); |
| |
| let config = regs.tzc_for_test().build_configuration(); |
| assert_eq!(config.number_of_filters(), 2); |
| assert_eq!(config.address_width(), 48); |
| assert_eq!(config.number_of_regions(), 9); |
| |
| regs.reg_write(0x000, 0b0000_0000_0000_0000_0011_1111_0000_1000); |
| let config = regs.tzc_for_test().build_configuration(); |
| assert_eq!(config.number_of_filters(), 1); |
| assert_eq!(config.address_width(), 64); |
| assert_eq!(config.number_of_regions(), 9); |
| |
| regs.reg_write(0x000, 0b0000_0011_0000_0000_0010_0111_0000_1000); |
| let config = regs.tzc_for_test().build_configuration(); |
| assert_eq!(config.number_of_filters(), 4); |
| assert_eq!(config.address_width(), 40); |
| assert_eq!(config.number_of_regions(), 9); |
| |
| regs.reg_write(0x000, 0b0000_0011_0000_0000_0010_0011_0000_1000); |
| let config = regs.tzc_for_test().build_configuration(); |
| assert_eq!(config.number_of_filters(), 4); |
| assert_eq!(config.address_width(), 36); |
| assert_eq!(config.number_of_regions(), 9); |
| |
| regs.reg_write(0x000, 0b0000_0011_0000_0000_0001_1111_0000_1000); |
| let config = regs.tzc_for_test().build_configuration(); |
| assert_eq!(config.number_of_filters(), 4); |
| assert_eq!(config.address_width(), 32); |
| assert_eq!(config.number_of_regions(), 9); |
| } |
| |
| #[test] |
| #[should_panic] |
| fn build_config_reserved_filters() { |
| let mut regs = FakeTZCRegisters::new(); |
| regs.reg_write(0x000, 0b0000_0010_0000_0000_0010_1111_0000_1000); |
| let config = regs.tzc_for_test().build_configuration(); |
| |
| config.number_of_filters(); |
| } |
| |
| #[test] |
| #[should_panic] |
| fn build_config_reserved_address_width() { |
| fn run(address_width: u32, regs: &mut FakeTZCRegisters) { |
| regs.reg_write( |
| 0x000, |
| 0b0000_0001_0000_0000_0000_0000_0000_1000 | address_width << 8, |
| ); |
| regs.tzc_for_test().build_configuration().address_width(); |
| } |
| |
| let mut regs = FakeTZCRegisters::new(); |
| |
| run(0b11_0011, &mut regs); |
| run(0b11_1101, &mut regs); |
| run(0b00_0000, &mut regs); |
| } |
| |
| #[test] |
| #[should_panic] |
| fn build_config_reserved_regions() { |
| fn run(region_count: u32, regs: &mut FakeTZCRegisters) { |
| regs.reg_write( |
| 0x000, |
| 0b0000_0001_0000_0000_0011_1111_0000_0000 | region_count, |
| ); |
| regs.tzc_for_test() |
| .build_configuration() |
| .number_of_regions(); |
| } |
| |
| let mut regs = FakeTZCRegisters::new(); |
| |
| run(0b1_1111, &mut regs); |
| run(0b0_0000, &mut regs); |
| run(0b0_1100, &mut regs); |
| } |
| |
| #[test] |
| fn get_action() { |
| let mut regs = FakeTZCRegisters::new(); |
| |
| regs.reg_write(0x004, 0b0000_0000_0000_0000_0000_0000_0000_0000); |
| assert_eq!(regs.tzc_for_test().action(), ActionRegister::TZCINTLOW_OKAY); |
| |
| regs.reg_write(0x004, 0b0000_0000_0000_0000_0000_0000_0000_0001); |
| assert_eq!( |
| regs.tzc_for_test().action(), |
| ActionRegister::TZCINTLOW_DECERR |
| ); |
| |
| regs.reg_write(0x004, 0b0000_0000_0000_0000_0000_0000_0000_0010); |
| assert_eq!( |
| regs.tzc_for_test().action(), |
| ActionRegister::TZCINTHIGH_OKAY |
| ); |
| |
| regs.reg_write(0x004, 0b0000_0000_0000_0000_0000_0000_0000_0011); |
| assert_eq!( |
| regs.tzc_for_test().action(), |
| ActionRegister::TZCINTHIGH_DECERR |
| ); |
| } |
| |
| #[test] |
| fn set_action() { |
| let mut regs = FakeTZCRegisters::new(); |
| let mut tzc = regs.tzc_for_test(); |
| |
| tzc.set_action(ActionRegister::TZCINTLOW_OKAY); |
| assert_eq!(tzc.action(), ActionRegister::TZCINTLOW_OKAY); |
| |
| tzc.set_action(ActionRegister::TZCINTLOW_DECERR); |
| assert_eq!(tzc.action(), ActionRegister::TZCINTLOW_DECERR); |
| |
| tzc.set_action(ActionRegister::TZCINTHIGH_OKAY); |
| assert_eq!(tzc.action(), ActionRegister::TZCINTHIGH_OKAY); |
| |
| tzc.set_action(ActionRegister::TZCINTHIGH_DECERR); |
| assert_eq!(tzc.action(), ActionRegister::TZCINTHIGH_DECERR); |
| } |
| |
| #[test] |
| fn gate_keeper_oob() { |
| let mut regs = FakeTZCRegisters::new(); |
| regs.reg_write(0x008, 0b0000_0000_0000_0010_0000_0000_0000_0000); |
| let mut tzc = regs.tzc_for_test(); |
| |
| assert!(tzc.gate_keeper(2).is_none()); |
| assert!(tzc.gate_keeper(3).is_none()); |
| assert!(tzc.gate_keeper(4).is_none()); |
| assert!(tzc.gate_keeper(18).is_none()); |
| |
| assert!(tzc.gate_keeper_mut(2).is_none()); |
| assert!(tzc.gate_keeper_mut(3).is_none()); |
| assert!(tzc.gate_keeper_mut(4).is_none()); |
| assert!(tzc.gate_keeper_mut(18).is_none()); |
| } |
| |
| #[test] |
| fn gate_keeper_status() { |
| let mut regs = FakeTZCRegisters::new(); |
| regs.reg_write(0x008, 0b0000_0000_0000_0010_0000_0000_0000_0000); |
| let tzc = regs.tzc_for_test(); |
| |
| assert_eq!( |
| tzc.gate_keeper(0).unwrap().status(), |
| GateKeeperStatus::Closed |
| ); |
| assert_eq!( |
| tzc.gate_keeper(1).unwrap().status(), |
| GateKeeperStatus::Opened |
| ); |
| } |
| |
| #[test] |
| fn gate_keeper_request() { |
| let mut regs = FakeTZCRegisters::new(); |
| regs.reg_write(0x008, 0b0000_0000_0000_0010_0000_0000_0000_0001); |
| let mut tzc = regs.tzc_for_test(); |
| |
| tzc.gate_keeper_mut(0) |
| .unwrap() |
| .request(GateKeeperStatus::Closed); |
| tzc.gate_keeper_mut(1) |
| .unwrap() |
| .request(GateKeeperStatus::Opened); |
| |
| assert_eq!( |
| regs.reg_read(0x008), |
| 0b0000_0000_0000_0010_0000_0000_0000_0010, |
| ) |
| } |
| |
| #[test] |
| fn get_speculation_control() { |
| fn run(flags: u32, regs: &mut FakeTZCRegisters, value: SpeculationControlRegister) { |
| regs.reg_write(0x00C, flags); |
| let spec = regs.tzc_for_test().speculation_control(); |
| assert_eq!(spec, value); |
| } |
| |
| let mut regs = FakeTZCRegisters::new(); |
| |
| run(0b00, &mut regs, SpeculationControlRegister::empty()); |
| run( |
| 0b01, |
| &mut regs, |
| SpeculationControlRegister::READ_SPEC_DISABLE, |
| ); |
| run( |
| 0b10, |
| &mut regs, |
| SpeculationControlRegister::WRITE_SPEC_DISABLE, |
| ); |
| run( |
| 0b11, |
| &mut regs, |
| SpeculationControlRegister::READ_SPEC_DISABLE |
| | SpeculationControlRegister::WRITE_SPEC_DISABLE, |
| ); |
| } |
| |
| #[test] |
| fn set_speculation_control() { |
| fn run(flags: u32, regs: &mut FakeTZCRegisters, value: SpeculationControlRegister) { |
| regs.tzc_for_test().set_speculation_control(value); |
| assert_eq!(regs.reg_read(0x00C), flags); |
| } |
| |
| let mut regs = FakeTZCRegisters::new(); |
| |
| run(0b00, &mut regs, SpeculationControlRegister::empty()); |
| run( |
| 0b01, |
| &mut regs, |
| SpeculationControlRegister::READ_SPEC_DISABLE, |
| ); |
| run( |
| 0b10, |
| &mut regs, |
| SpeculationControlRegister::WRITE_SPEC_DISABLE, |
| ); |
| run( |
| 0b11, |
| &mut regs, |
| SpeculationControlRegister::READ_SPEC_DISABLE |
| | SpeculationControlRegister::WRITE_SPEC_DISABLE, |
| ); |
| } |
| |
| #[test] |
| pub fn interrupt_status() { |
| let mut regs = FakeTZCRegisters::new(); |
| regs.reg_write(0x010, 0b0000_0000_0000_0010_0000_0001_0000_0010); |
| let mut tzc = regs.tzc_for_test(); |
| |
| let status = tzc.interrupt_status(); |
| |
| assert!(!status.overlap(0)); |
| assert!(status.overlap(1)); |
| |
| assert!(status.overrun(0)); |
| assert!(!status.overrun(1)); |
| |
| assert!(!status.status(0)); |
| assert!(status.status(1)); |
| } |
| |
| #[test] |
| pub fn interrupt_clear() { |
| let mut regs = FakeTZCRegisters::new(); |
| let mut tzc = regs.tzc_for_test(); |
| |
| let clear = InterruptClearRegister::new(&[0, 3]); |
| tzc.clear_interrupts(clear); |
| |
| assert_eq!( |
| regs.reg_read(0x014), |
| 0b0000_0000_0000_0000_0000_0000_0000_1001 |
| ); |
| } |
| |
| #[test] |
| fn peripheral_id() { |
| let mut regs = FakeTZCRegisters::new(); |
| regs.reg_write(0xFD0, 0x04); |
| regs.reg_write(0xFD4, 0x00); |
| regs.reg_write(0xFD8, 0x00); |
| regs.reg_write(0xFDC, 0x00); |
| regs.reg_write(0xFE0, 0x60); |
| regs.reg_write(0xFE4, 0xB4); |
| regs.reg_write(0xFE8, 0x2B); |
| regs.reg_write(0xFEC, 0x00); |
| |
| let tzc = regs.tzc_for_test(); |
| assert_eq!(tzc.peripheral_id(), 0x0000_0004_002B_B460); |
| } |
| |
| #[test] |
| fn component_id() { |
| let mut regs = FakeTZCRegisters::new(); |
| regs.reg_write(0xFF0, 0x0D); |
| regs.reg_write(0xFF4, 0xF0); |
| regs.reg_write(0xFF8, 0x05); |
| regs.reg_write(0xFFC, 0xB1); |
| |
| let tzc = regs.tzc_for_test(); |
| assert_eq!(tzc.component_id(), 0xB105F00D); |
| } |
| |
| #[test] |
| fn fail_oob() { |
| let mut regs = FakeTZCRegisters::new(); |
| let tzc = regs.tzc_for_test(); |
| |
| assert!(tzc.filter_failure(4).is_none()); |
| assert!(tzc.filter_failure(8).is_none()); |
| assert!(tzc.filter_failure(123124).is_none()); |
| } |
| |
| #[test] |
| fn region_oob() { |
| let mut regs = FakeTZCRegisters::new(); |
| let tzc = regs.tzc_for_test(); |
| |
| assert!(tzc.region(9).is_none()); |
| assert!(tzc.region(32).is_none()); |
| assert!(tzc.region(123124).is_none()); |
| } |
| |
| #[test] |
| fn region_mut_oob() { |
| let mut regs = FakeTZCRegisters::new(); |
| let mut tzc = regs.tzc_for_test(); |
| |
| assert!(tzc.region_mut(9).is_none()); |
| assert!(tzc.region_mut(32).is_none()); |
| assert!(tzc.region_mut(123124).is_none()); |
| } |
| } |