Skip to main content

arm_tzc/
lib.rs

1// SPDX-FileCopyrightText: Copyright The arm-tzc Contributors.
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4#![no_std]
5#![doc = include_str!("../README.md")]
6#![deny(clippy::undocumented_unsafe_blocks)]
7#![deny(unsafe_op_in_unsafe_fn)]
8
9mod fail;
10mod region;
11mod utils;
12
13use bitflags::bitflags;
14use safe_mmio::{
15    SharedMmioPointer, UniqueMmioPointer, field, field_shared,
16    fields::{ReadOnly, ReadPure, ReadPureWrite, WriteOnly},
17};
18use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
19
20pub use crate::fail::{FailAccessDirection, FailControlRegister, FailIDRegister, TzcFail};
21pub use crate::region::{RegionAttributes, RegionIDAccess, SecureAccess, TzcRegion, TzcRegionMut};
22use crate::{
23    fail::FailRegisters,
24    region::RegionRegisters,
25    utils::{extract_bits, get_bit, set_bit},
26};
27
28macro_rules! for_in_slice {
29    ($var:ident in $slice:expr, $($body:stmt)*) => {
30        let mut i = 0;
31        while i < $slice.len() {
32            let $var = &$slice[i];
33            $($body)*
34            i += 1;
35        }
36    };
37}
38pub(crate) use for_in_slice;
39
40#[derive(Debug, Clone, Copy, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
41/// Provides information about the configuration of the TZC-400.
42#[repr(transparent)]
43pub struct BuildConfigRegister(u32);
44
45impl BuildConfigRegister {
46    // Bit values for number of filters.
47    const NF_1: u32 = 0b00;
48    const NF_2: u32 = 0b01;
49    const NF_4: u32 = 0b11;
50
51    /// Returns the number of filter units in the design implementation.
52    pub const fn number_of_filters(&self) -> usize {
53        match extract_bits(self.0, 25, 24) {
54            Self::NF_1 => 1,
55            Self::NF_2 => 2,
56            Self::NF_4 => 4,
57            _ => panic!("Use of reserved value for number of filters"),
58        }
59    }
60
61    // Bit values for address width.
62    const AW_32: u32 = 0b011111;
63    const AW_36: u32 = 0b100011;
64    const AW_40: u32 = 0b100111;
65    const AW_48: u32 = 0b101111;
66    const AW_64: u32 = 0b111111;
67
68    /// Returns the width of the ACE-Lite address bus.
69    pub const fn address_width(&self) -> usize {
70        match extract_bits(self.0, 13, 8) {
71            Self::AW_32 => 32,
72            Self::AW_36 => 36,
73            Self::AW_40 => 40,
74            Self::AW_48 => 48,
75            Self::AW_64 => 64,
76            _ => panic!("Use of reserved value for address width"),
77        }
78    }
79
80    // Bit values for number of regions.
81    const NR_9: u32 = 0b01000;
82
83    /// Returns the number of regions that the TZC-400 provides.
84    pub fn number_of_regions(&self) -> usize {
85        match extract_bits(self.0, 4, 0) {
86            Self::NR_9 => 9,
87            _ => panic!("Use of reserved value for number of regions"),
88        }
89    }
90}
91
92#[derive(Debug, Clone, Copy, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
93/// Controls the interrupt and bus response signaling behavior of the TZC-400 when region permission
94/// failures occur.
95#[repr(transparent)]
96pub struct ActionRegister(u32);
97
98impl ActionRegister {
99    /// Sets TZCINT LOW and issues an OKAY response.
100    pub const TZCINTLOW_OKAY: ActionRegister = ActionRegister(0b00);
101    /// Sets TZCINT LOW and issues a DECERR response.
102    pub const TZCINTLOW_DECERR: ActionRegister = ActionRegister(0b01);
103    /// Sets TZCINT HIGH and issues an OKAY response.
104    pub const TZCINTHIGH_OKAY: ActionRegister = ActionRegister(0b10);
105    /// Sets TZCINT HIGH and issues a DECERR response.
106    pub const TZCINTHIGH_DECERR: ActionRegister = ActionRegister(0b11);
107}
108
109#[derive(Clone, Copy, Debug, Eq, PartialEq)]
110#[repr(u32)]
111/// See [`GateKeeper::status`].
112pub enum GateKeeperStatus {
113    /// Access to the associated filter is not permitted.
114    Closed = 0,
115    /// Access to the associated filter is permitted.
116    Opened = 1,
117}
118
119#[derive(Debug)]
120/// Provides control and status for the gate keeper in each filter unit implemented.
121///
122/// ## 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.
123pub struct GateKeeper<'reg> {
124    reg: SharedMmioPointer<'reg, ReadPureWrite<u32>>,
125    index: usize,
126}
127
128impl<'reg> GateKeeper<'reg> {
129    /// The current state of the gate keeper in the filter unit at `index`. `index` must be the
130    /// index of a valid filter in the TZ-ASC configuration.
131    pub fn status(&self) -> GateKeeperStatus {
132        if get_bit(self.reg.read(), self.index + 16) {
133            GateKeeperStatus::Opened
134        } else {
135            GateKeeperStatus::Closed
136        }
137    }
138}
139
140#[derive(Debug)]
141/// Provides control and status for the gate keeper in each filter unit implemented.
142///
143/// ## Usage constraints
144///
145/// Closing the gate can cause accesses on the command channels to stall and can inadvertently cause
146/// a bus deadlock. Use this with caution.
147pub struct GateKeeperMut<'reg> {
148    reg: UniqueMmioPointer<'reg, ReadPureWrite<u32>>,
149    index: usize,
150}
151
152impl<'reg> GateKeeperMut<'reg> {
153    /// See [GateKeeper::status].
154    pub fn status(&self) -> GateKeeperStatus {
155        GateKeeper {
156            reg: *self.reg,
157            index: self.index,
158        }
159        .status()
160    }
161
162    /// Request the gate at `index` to be set to `status`. The request will take effect once all
163    /// outstanding accesses through the filter unit are complete.
164    pub fn request(&mut self, status: GateKeeperStatus) {
165        let mut value = self.reg.read();
166
167        set_bit(
168            &mut value,
169            self.index,
170            matches!(status, GateKeeperStatus::Opened),
171        );
172        self.reg.write(value);
173
174        while self.status() != status {}
175    }
176}
177
178#[derive(Debug, Clone, Copy, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
179/// Controls the read access speculation and write access speculation.
180#[repr(transparent)]
181pub struct SpeculationControlRegister(u32);
182
183bitflags! {
184    impl SpeculationControlRegister: u32 {
185        /// Disables read access speculation.
186        ///
187        /// Note: This bit is ignored and assumed to be zero at a filter unit if the corresponding
188        /// QVNENABLE signal is HIGH.
189        const READ_SPEC_DISABLE = 1 << 0;
190
191        /// Disables write access speculation.
192        ///
193        /// Note: This bit is ignored and assumed to be zero at a filter unit if the corresponding
194        /// QVNENABLE signal is HIGH.
195        const WRITE_SPEC_DISABLE = 1 << 1;
196    }
197}
198
199#[derive(Debug, Clone, Copy, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
200/// Contains the status of the interrupt signal, *TZCINT*, that reports access security violations
201/// or region overlap errors.
202#[repr(transparent)]
203pub struct InterruptStatusRegister(u32);
204
205impl InterruptStatusRegister {
206    /// When a bit is set to 1 it indicates a violation of the overlap region configuration rules
207    /// for the associated filter unit. This occurs when an access matches with two enabled regions
208    /// at the same time unless the overlap is only with Region 0.
209    ///
210    /// This bit is set even if the [`ActionRegister`] is set to not drive the interrupt.
211    ///
212    /// When this bit is true, the interrupt status bit is also set to true.
213    ///
214    /// Clear the interrupt status of the associated bit in the [`InterruptClearRegister`] to also
215    /// clear this field.
216    pub const fn overlap(&self, index: usize) -> bool {
217        assert!(index < 4);
218        get_bit(self.0, index + 16)
219    }
220
221    /// When a bit is set to `true`, it indicates the occurrence of two or more region permission or
222    /// region overlapping failures at the associated filter unit after the interrupt was cleared by
223    /// the associated bit in the [`InterruptClearRegister`].
224    ///
225    /// This bit is set even if the [`ActionRegister`] is set to not drive the interrupt.
226    ///
227    /// Clear the interrupt status of the associated bit in the [`InterruptClearRegister`] to also
228    /// clear this field.
229    pub const fn overrun(&self, index: usize) -> bool {
230        assert!(index < 4);
231        get_bit(self.0, index + 8)
232    }
233
234    /// This field indicates the status of the interrupt from the corresponding filter unit as
235    /// follows:
236    /// - `false`: Interrupt is not asserted.
237    /// - `true`: Interrupt is asserted and waiting to be cleared.
238    ///
239    /// This bit is set even if the [`ActionRegister`] is set to not drive the interrupt output
240    /// TZCINT HIGH.
241    ///
242    /// Therefore, the status acts as an indicator that a region permission check failure or an
243    /// overlap error has occurred at a particular filter unit.
244    pub const fn status(&self, index: usize) -> bool {
245        assert!(index < 4);
246        get_bit(self.0, index)
247    }
248}
249
250#[derive(
251    Debug, Clone, Copy, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq, Default,
252)]
253/// Clears the interrupt.
254#[repr(transparent)]
255pub struct InterruptClearRegister(u32);
256
257impl InterruptClearRegister {
258    /// Creates an instance of [`InterruptClearRegister`] requesting to clear all the interrupts
259    /// present in `interrupts`.
260    pub const fn new(interrupts: &[usize]) -> Self {
261        let mut s = Self(0);
262
263        for_in_slice!(int in interrupts, s.0 |= 1 << *int);
264
265        s
266    }
267}
268/// View over the TZC memory.
269#[derive(Debug, Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
270#[repr(C, align(4))]
271pub struct TzcRegisters {
272    /// 0x000: Build configuration register.
273    build_config: ReadPure<BuildConfigRegister>,
274    /// 0x004: Action register.
275    action: ReadPureWrite<ActionRegister>,
276    /// 0x008: Gate keeper register.
277    gate_keeper: ReadPureWrite<u32>,
278    /// 0x00C: Speculation register.
279    speculation_ctrl: ReadPureWrite<SpeculationControlRegister>,
280    /// 0x010: Interrupt status register.
281    int_status: ReadOnly<InterruptStatusRegister>,
282    /// 0x014: Interrupt clear register.
283    int_clear: WriteOnly<InterruptClearRegister>,
284    /// 0x018-0x020: reserved.
285    reserved_18: [u32; 2],
286
287    /// 0x020-0x060: The fail status registers provide information about an access that fails
288    /// because of insufficient permissions or an overlap condition. A separate set of fail status
289    /// registers is provided for each filter unit.
290    fail_registers: [FailRegisters; 4],
291    /// 0x060-0x100: reserved.
292    reserved_60: [u32; 40],
293
294    /// 0x100-0x218: Registers for region 0 to 8 (included).
295    ///
296    /// The region control registers control the address space that each region controls and the
297    /// security attributes to use for that region.
298    region_registers: [RegionRegisters; 9],
299
300    /// 0x220-0xf00: reserved.
301    ///
302    /// Although the specification states that this reserved range should start at 0x218, the 8
303    /// first reserved bytes are actually at the end of the `RegionRegister` entry for Region 9.
304    reserved_220: [u32; 823],
305    padding_f00: [u32; 53],
306
307    /// 0xFD0: Peripheral ID 4 register.
308    pid4: ReadPure<u32>,
309    /// 0xFD4: Peripheral ID 5 register.
310    pid5: ReadPure<u32>,
311    /// 0xFD8: Peripheral ID 6 register.
312    pid6: ReadPure<u32>,
313    /// 0xFDC: Peripheral ID 7 register.
314    pid7: ReadPure<u32>,
315    /// 0xFE0: Peripheral ID 0 register.
316    pid0: ReadPure<u32>,
317    /// 0xFE4: Peripheral ID 1 register.
318    pid1: ReadPure<u32>,
319    /// 0xFE8: Peripheral ID 2 register.
320    pid2: ReadPure<u32>,
321    /// 0xFEC: Peripheral ID 3 register.
322    pid3: ReadPure<u32>,
323
324    /// 0xFF0: Component ID 0 register.
325    cid0: ReadPure<u32>,
326    /// 0xFF4: Component ID 1 register.
327    cid1: ReadPure<u32>,
328    /// 0xFF8: Component ID 2 register.
329    cid2: ReadPure<u32>,
330    /// 0xFFC: Component ID 3 register.
331    cid3: ReadPure<u32>,
332}
333
334/// Interface to manipulate the [TZC-400 TrustZone Address Space Controller](https://developer.arm.com/documentation/ddi0504/latest).
335pub struct Tzc<'a> {
336    regs: UniqueMmioPointer<'a, TzcRegisters>,
337}
338
339impl<'a> Tzc<'a> {
340    /// Creates new TZC instance from a [`UniqueMmioPointer`] that points to the memory region of
341    /// the Controller.
342    pub const fn new(regs: UniqueMmioPointer<'a, TzcRegisters>) -> Self {
343        Self { regs }
344    }
345
346    /// Returns the [`BuildConfigRegister`] for this TZC-400 instance.
347    pub fn build_configuration(&self) -> BuildConfigRegister {
348        field_shared!(self.regs, build_config).read()
349    }
350
351    /// Returns the current [`ActionRegister`] configuration.
352    pub fn action(&self) -> ActionRegister {
353        field_shared!(self.regs, action).read()
354    }
355    /// Sets the [`ActionRegister`] configuration.
356    pub fn set_action(&mut self, value: ActionRegister) {
357        field!((self.regs), action).write(value);
358    }
359
360    /// Returns a [`GateKeeper`] wrapper to read from the Gate Keeper Register.
361    pub fn gate_keeper<'reg>(&'reg self, index: usize) -> Option<GateKeeper<'reg>> {
362        if index < self.build_configuration().number_of_filters() {
363            Some(GateKeeper {
364                reg: field_shared!(self.regs, gate_keeper),
365                index,
366            })
367        } else {
368            None
369        }
370    }
371
372    /// Returns a [`GateKeeperMut`] wrapper to read from/write to the Gate Keeper Register.
373    pub fn gate_keeper_mut<'reg>(&'reg mut self, index: usize) -> Option<GateKeeperMut<'reg>> {
374        if index < self.build_configuration().number_of_filters() {
375            Some(GateKeeperMut {
376                reg: field!(self.regs, gate_keeper),
377                index,
378            })
379        } else {
380            None
381        }
382    }
383
384    /// Returns the current [`SpeculationControlRegister`] configuration.
385    pub fn speculation_control(&self) -> SpeculationControlRegister {
386        field_shared!(self.regs, speculation_ctrl).read()
387    }
388    /// Sets the [`SpeculationControlRegister`] configuration.
389    pub fn set_speculation_control(&mut self, value: SpeculationControlRegister) {
390        field!(self.regs, speculation_ctrl).write(value);
391    }
392
393    /// Returns the current [`InterruptStatusRegister`].
394    pub fn interrupt_status(&mut self) -> InterruptStatusRegister {
395        field!(self.regs, int_status).read()
396    }
397
398    /// Sets the [`InterruptClearRegister`].
399    pub fn clear_interrupts(&mut self, value: InterruptClearRegister) {
400        field!(self.regs, int_clear).write(value);
401    }
402
403    /// Returns a [`TzcFail`] structure to read from the failure register of the filter at `index`,
404    /// or `None` if `index` is out of bounds.
405    pub fn filter_failure<'regs>(&'regs self, idx: usize) -> Option<TzcFail<'regs>> {
406        if idx < self.build_configuration().number_of_filters() {
407            let fails = field_shared!(self.regs, fail_registers);
408            fails.get(idx).map(|regs| TzcFail { regs })
409        } else {
410            None
411        }
412    }
413
414    /// Returns a [`TzcRegion`] structure to read from the region registers at `index`, or `None` if
415    /// `index` is out of bounds.
416    pub fn region<'regs>(&'regs self, idx: usize) -> Option<TzcRegion<'regs>> {
417        let config = self.build_configuration();
418
419        if idx < config.number_of_regions() {
420            let regions = field_shared!(self.regs, region_registers);
421            regions.get(idx).map(|regs| TzcRegion {
422                regs,
423                address_width_mask: ((1_u64.unbounded_shl(config.address_width() as u32))
424                    .wrapping_sub(1)),
425            })
426        } else {
427            None
428        }
429    }
430
431    /// Returns a [`TzcRegionMut`] structure to read and write from the region registers at `index`,
432    /// or `None` if `index` is out of bounds.
433    pub fn region_mut<'regs>(&'regs mut self, idx: usize) -> Option<TzcRegionMut<'regs>> {
434        let config = self.build_configuration();
435
436        if idx < config.number_of_regions() {
437            let regions = field!(self.regs, region_registers);
438            regions.take(idx).map(|regs| TzcRegionMut {
439                regs,
440                addresses_writable: idx != 0,
441                address_width_mask: ((1_u64.unbounded_shl(config.address_width() as u32))
442                    .wrapping_sub(1)),
443            })
444        } else {
445            None
446        }
447    }
448
449    /// Returns the peripheral ID, which is `0x4002BB460`.
450    pub fn peripheral_id(&self) -> u64 {
451        let values = [
452            field_shared!(self.regs, pid0).read(),
453            field_shared!(self.regs, pid1).read(),
454            field_shared!(self.regs, pid2).read(),
455            field_shared!(self.regs, pid3).read(),
456            field_shared!(self.regs, pid4).read(),
457            field_shared!(self.regs, pid5).read(),
458            field_shared!(self.regs, pid6).read(),
459            field_shared!(self.regs, pid7).read(),
460        ];
461
462        values
463            .into_iter()
464            .map(|v| v as u64)
465            .enumerate()
466            .fold(0, |acc, (idx, value)| (value << (8 * idx as u64)) | acc)
467    }
468
469    /// Holds a 32 bit component ID value. Used for automatic BIOS configuration. The result is
470    /// `0xB105F00D`.
471    pub fn component_id(&self) -> u64 {
472        let values = [
473            field_shared!(self.regs, cid0).read(),
474            field_shared!(self.regs, cid1).read(),
475            field_shared!(self.regs, cid2).read(),
476            field_shared!(self.regs, cid3).read(),
477        ];
478
479        values
480            .into_iter()
481            .map(|v| v as u64)
482            .enumerate()
483            .fold(0, |acc, (idx, value)| (value << (8 * idx as u64)) | acc)
484    }
485}
486
487#[cfg(test)]
488pub(crate) mod tests {
489    use safe_mmio::UniqueMmioPointer;
490    use zerocopy::transmute_mut;
491
492    use crate::*;
493
494    const DEFAULT_BUILD_CONFIG: u32 = 0b0000_0001_0000_0000_0010_1111_0000_1000;
495
496    pub(crate) struct FakeTZCRegisters {
497        regs: [u32; 1024],
498    }
499
500    impl FakeTZCRegisters {
501        pub fn new() -> Self {
502            let mut s = Self { regs: [0u32; 1024] };
503            s.reg_write(0x000, DEFAULT_BUILD_CONFIG);
504            s
505        }
506
507        pub fn reg_write(&mut self, offset: usize, value: u32) {
508            self.regs[offset / 4] = value;
509        }
510
511        pub fn reg_read(&self, offset: usize) -> u32 {
512            self.regs[offset / 4]
513        }
514
515        fn get(&mut self) -> UniqueMmioPointer<'_, TzcRegisters> {
516            UniqueMmioPointer::from(transmute_mut!(&mut self.regs))
517        }
518
519        pub fn tzc_for_test(&mut self) -> Tzc<'_> {
520            Tzc::new(self.get())
521        }
522    }
523
524    #[test]
525    fn size() {
526        assert_eq!(size_of::<TzcRegisters>(), 4096);
527    }
528
529    #[test]
530    fn build_config() {
531        let mut regs = FakeTZCRegisters::new();
532
533        let config = regs.tzc_for_test().build_configuration();
534        assert_eq!(config.number_of_filters(), 2);
535        assert_eq!(config.address_width(), 48);
536        assert_eq!(config.number_of_regions(), 9);
537
538        regs.reg_write(0x000, 0b0000_0000_0000_0000_0011_1111_0000_1000);
539        let config = regs.tzc_for_test().build_configuration();
540        assert_eq!(config.number_of_filters(), 1);
541        assert_eq!(config.address_width(), 64);
542        assert_eq!(config.number_of_regions(), 9);
543
544        regs.reg_write(0x000, 0b0000_0011_0000_0000_0010_0111_0000_1000);
545        let config = regs.tzc_for_test().build_configuration();
546        assert_eq!(config.number_of_filters(), 4);
547        assert_eq!(config.address_width(), 40);
548        assert_eq!(config.number_of_regions(), 9);
549
550        regs.reg_write(0x000, 0b0000_0011_0000_0000_0010_0011_0000_1000);
551        let config = regs.tzc_for_test().build_configuration();
552        assert_eq!(config.number_of_filters(), 4);
553        assert_eq!(config.address_width(), 36);
554        assert_eq!(config.number_of_regions(), 9);
555
556        regs.reg_write(0x000, 0b0000_0011_0000_0000_0001_1111_0000_1000);
557        let config = regs.tzc_for_test().build_configuration();
558        assert_eq!(config.number_of_filters(), 4);
559        assert_eq!(config.address_width(), 32);
560        assert_eq!(config.number_of_regions(), 9);
561    }
562
563    #[test]
564    #[should_panic]
565    fn build_config_reserved_filters() {
566        let mut regs = FakeTZCRegisters::new();
567        regs.reg_write(0x000, 0b0000_0010_0000_0000_0010_1111_0000_1000);
568        let config = regs.tzc_for_test().build_configuration();
569
570        config.number_of_filters();
571    }
572
573    #[test]
574    #[should_panic]
575    fn build_config_reserved_address_width() {
576        fn run(address_width: u32, regs: &mut FakeTZCRegisters) {
577            regs.reg_write(
578                0x000,
579                0b0000_0001_0000_0000_0000_0000_0000_1000 | address_width << 8,
580            );
581            regs.tzc_for_test().build_configuration().address_width();
582        }
583
584        let mut regs = FakeTZCRegisters::new();
585
586        run(0b11_0011, &mut regs);
587        run(0b11_1101, &mut regs);
588        run(0b00_0000, &mut regs);
589    }
590
591    #[test]
592    #[should_panic]
593    fn build_config_reserved_regions() {
594        fn run(region_count: u32, regs: &mut FakeTZCRegisters) {
595            regs.reg_write(
596                0x000,
597                0b0000_0001_0000_0000_0011_1111_0000_0000 | region_count,
598            );
599            regs.tzc_for_test()
600                .build_configuration()
601                .number_of_regions();
602        }
603
604        let mut regs = FakeTZCRegisters::new();
605
606        run(0b1_1111, &mut regs);
607        run(0b0_0000, &mut regs);
608        run(0b0_1100, &mut regs);
609    }
610
611    #[test]
612    fn build_config_64_bit_address_region() {
613        let mut regs = FakeTZCRegisters::new();
614
615        // Testing address mask calculation with 64 bit addresses.
616        regs.reg_write(0x000, 0b0000_0000_0000_0000_0011_1111_0000_1000);
617
618        let mut tzc = regs.tzc_for_test();
619
620        let region = tzc.region(1);
621        assert!(region.is_some());
622        assert_eq!(u64::MAX, region.unwrap().address_width_mask);
623
624        let region = tzc.region_mut(1);
625        assert!(region.is_some());
626        assert_eq!(u64::MAX, region.unwrap().address_width_mask);
627    }
628
629    #[test]
630    fn get_action() {
631        let mut regs = FakeTZCRegisters::new();
632
633        regs.reg_write(0x004, 0b0000_0000_0000_0000_0000_0000_0000_0000);
634        assert_eq!(regs.tzc_for_test().action(), ActionRegister::TZCINTLOW_OKAY);
635
636        regs.reg_write(0x004, 0b0000_0000_0000_0000_0000_0000_0000_0001);
637        assert_eq!(
638            regs.tzc_for_test().action(),
639            ActionRegister::TZCINTLOW_DECERR
640        );
641
642        regs.reg_write(0x004, 0b0000_0000_0000_0000_0000_0000_0000_0010);
643        assert_eq!(
644            regs.tzc_for_test().action(),
645            ActionRegister::TZCINTHIGH_OKAY
646        );
647
648        regs.reg_write(0x004, 0b0000_0000_0000_0000_0000_0000_0000_0011);
649        assert_eq!(
650            regs.tzc_for_test().action(),
651            ActionRegister::TZCINTHIGH_DECERR
652        );
653    }
654
655    #[test]
656    fn set_action() {
657        let mut regs = FakeTZCRegisters::new();
658        let mut tzc = regs.tzc_for_test();
659
660        tzc.set_action(ActionRegister::TZCINTLOW_OKAY);
661        assert_eq!(tzc.action(), ActionRegister::TZCINTLOW_OKAY);
662
663        tzc.set_action(ActionRegister::TZCINTLOW_DECERR);
664        assert_eq!(tzc.action(), ActionRegister::TZCINTLOW_DECERR);
665
666        tzc.set_action(ActionRegister::TZCINTHIGH_OKAY);
667        assert_eq!(tzc.action(), ActionRegister::TZCINTHIGH_OKAY);
668
669        tzc.set_action(ActionRegister::TZCINTHIGH_DECERR);
670        assert_eq!(tzc.action(), ActionRegister::TZCINTHIGH_DECERR);
671    }
672
673    #[test]
674    fn gate_keeper_oob() {
675        let mut regs = FakeTZCRegisters::new();
676        regs.reg_write(0x008, 0b0000_0000_0000_0010_0000_0000_0000_0000);
677        let mut tzc = regs.tzc_for_test();
678
679        assert!(tzc.gate_keeper(2).is_none());
680        assert!(tzc.gate_keeper(3).is_none());
681        assert!(tzc.gate_keeper(4).is_none());
682        assert!(tzc.gate_keeper(18).is_none());
683
684        assert!(tzc.gate_keeper_mut(2).is_none());
685        assert!(tzc.gate_keeper_mut(3).is_none());
686        assert!(tzc.gate_keeper_mut(4).is_none());
687        assert!(tzc.gate_keeper_mut(18).is_none());
688    }
689
690    #[test]
691    fn gate_keeper_status() {
692        let mut regs = FakeTZCRegisters::new();
693        regs.reg_write(0x008, 0b0000_0000_0000_0010_0000_0000_0000_0000);
694        let tzc = regs.tzc_for_test();
695
696        assert_eq!(
697            tzc.gate_keeper(0).unwrap().status(),
698            GateKeeperStatus::Closed
699        );
700        assert_eq!(
701            tzc.gate_keeper(1).unwrap().status(),
702            GateKeeperStatus::Opened
703        );
704    }
705
706    #[test]
707    fn gate_keeper_request() {
708        let mut regs = FakeTZCRegisters::new();
709        regs.reg_write(0x008, 0b0000_0000_0000_0010_0000_0000_0000_0001);
710        let mut tzc = regs.tzc_for_test();
711
712        tzc.gate_keeper_mut(0)
713            .unwrap()
714            .request(GateKeeperStatus::Closed);
715        tzc.gate_keeper_mut(1)
716            .unwrap()
717            .request(GateKeeperStatus::Opened);
718
719        assert_eq!(
720            regs.reg_read(0x008),
721            0b0000_0000_0000_0010_0000_0000_0000_0010,
722        )
723    }
724
725    #[test]
726    fn get_speculation_control() {
727        fn run(flags: u32, regs: &mut FakeTZCRegisters, value: SpeculationControlRegister) {
728            regs.reg_write(0x00C, flags);
729            let spec = regs.tzc_for_test().speculation_control();
730            assert_eq!(spec, value);
731        }
732
733        let mut regs = FakeTZCRegisters::new();
734
735        run(0b00, &mut regs, SpeculationControlRegister::empty());
736        run(
737            0b01,
738            &mut regs,
739            SpeculationControlRegister::READ_SPEC_DISABLE,
740        );
741        run(
742            0b10,
743            &mut regs,
744            SpeculationControlRegister::WRITE_SPEC_DISABLE,
745        );
746        run(
747            0b11,
748            &mut regs,
749            SpeculationControlRegister::READ_SPEC_DISABLE
750                | SpeculationControlRegister::WRITE_SPEC_DISABLE,
751        );
752    }
753
754    #[test]
755    fn set_speculation_control() {
756        fn run(flags: u32, regs: &mut FakeTZCRegisters, value: SpeculationControlRegister) {
757            regs.tzc_for_test().set_speculation_control(value);
758            assert_eq!(regs.reg_read(0x00C), flags);
759        }
760
761        let mut regs = FakeTZCRegisters::new();
762
763        run(0b00, &mut regs, SpeculationControlRegister::empty());
764        run(
765            0b01,
766            &mut regs,
767            SpeculationControlRegister::READ_SPEC_DISABLE,
768        );
769        run(
770            0b10,
771            &mut regs,
772            SpeculationControlRegister::WRITE_SPEC_DISABLE,
773        );
774        run(
775            0b11,
776            &mut regs,
777            SpeculationControlRegister::READ_SPEC_DISABLE
778                | SpeculationControlRegister::WRITE_SPEC_DISABLE,
779        );
780    }
781
782    #[test]
783    pub fn interrupt_status() {
784        let mut regs = FakeTZCRegisters::new();
785        regs.reg_write(0x010, 0b0000_0000_0000_0010_0000_0001_0000_0010);
786        let mut tzc = regs.tzc_for_test();
787
788        let status = tzc.interrupt_status();
789
790        assert!(!status.overlap(0));
791        assert!(status.overlap(1));
792
793        assert!(status.overrun(0));
794        assert!(!status.overrun(1));
795
796        assert!(!status.status(0));
797        assert!(status.status(1));
798    }
799
800    #[test]
801    pub fn interrupt_clear() {
802        let mut regs = FakeTZCRegisters::new();
803        let mut tzc = regs.tzc_for_test();
804
805        let clear = InterruptClearRegister::new(&[0, 3]);
806        tzc.clear_interrupts(clear);
807
808        assert_eq!(
809            regs.reg_read(0x014),
810            0b0000_0000_0000_0000_0000_0000_0000_1001
811        );
812    }
813
814    #[test]
815    fn peripheral_id() {
816        let mut regs = FakeTZCRegisters::new();
817        regs.reg_write(0xFD0, 0x04);
818        regs.reg_write(0xFD4, 0x00);
819        regs.reg_write(0xFD8, 0x00);
820        regs.reg_write(0xFDC, 0x00);
821        regs.reg_write(0xFE0, 0x60);
822        regs.reg_write(0xFE4, 0xB4);
823        regs.reg_write(0xFE8, 0x2B);
824        regs.reg_write(0xFEC, 0x00);
825
826        let tzc = regs.tzc_for_test();
827        assert_eq!(tzc.peripheral_id(), 0x0000_0004_002B_B460);
828    }
829
830    #[test]
831    fn component_id() {
832        let mut regs = FakeTZCRegisters::new();
833        regs.reg_write(0xFF0, 0x0D);
834        regs.reg_write(0xFF4, 0xF0);
835        regs.reg_write(0xFF8, 0x05);
836        regs.reg_write(0xFFC, 0xB1);
837
838        let tzc = regs.tzc_for_test();
839        assert_eq!(tzc.component_id(), 0xB105F00D);
840    }
841
842    #[test]
843    fn fail_oob() {
844        let mut regs = FakeTZCRegisters::new();
845        let tzc = regs.tzc_for_test();
846
847        assert!(tzc.filter_failure(4).is_none());
848        assert!(tzc.filter_failure(8).is_none());
849        assert!(tzc.filter_failure(123124).is_none());
850    }
851
852    #[test]
853    fn region_oob() {
854        let mut regs = FakeTZCRegisters::new();
855        let tzc = regs.tzc_for_test();
856
857        assert!(tzc.region(9).is_none());
858        assert!(tzc.region(32).is_none());
859        assert!(tzc.region(123124).is_none());
860    }
861
862    #[test]
863    fn region_mut_oob() {
864        let mut regs = FakeTZCRegisters::new();
865        let mut tzc = regs.tzc_for_test();
866
867        assert!(tzc.region_mut(9).is_none());
868        assert!(tzc.region_mut(32).is_none());
869        assert!(tzc.region_mut(123124).is_none());
870    }
871}