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