blob: 551dea9225bbb25b2d261e66dcfa8f6f9042dbe3 [file] [log] [blame]
// 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());
}
}