blob: ee0a747dc448dd8a4cba889d4c82086f79e0ef6c [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};
Imre Kisbdf1c702025-09-23 17:24:06 +020020pub use crate::region::{RegionAttributes, RegionIDAccess, SecureAccess, TzcRegion, TzcRegionMut};
Ludovic Mermodbe75da82025-09-17 11:13:28 +020021use 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,
Imre Kis655db812025-09-23 17:36:55 +0200422 address_width_mask: ((1_u64.unbounded_shl(config.address_width() as u32))
423 .wrapping_sub(1)),
Ludovic Mermodbe75da82025-09-17 11:13:28 +0200424 })
425 } else {
426 None
427 }
428 }
429
430 /// Returns a [`TzcRegionMut`] structure to read and write from the region registers at `index`,
431 /// or `None` if `index` is out of bounds.
432 pub fn region_mut<'regs>(&'regs mut self, idx: usize) -> Option<TzcRegionMut<'regs>> {
433 let config = self.build_configuration();
434
435 if idx < config.number_of_regions() {
436 let regions = field!(self.regs, region_registers);
437 regions.take(idx).map(|regs| TzcRegionMut {
438 regs,
439 addresses_writable: idx != 0,
Imre Kis655db812025-09-23 17:36:55 +0200440 address_width_mask: ((1_u64.unbounded_shl(config.address_width() as u32))
441 .wrapping_sub(1)),
Ludovic Mermodbe75da82025-09-17 11:13:28 +0200442 })
443 } else {
444 None
445 }
446 }
447
448 /// Returns the peripheral ID, which is `0x4002BB460`.
449 pub fn peripheral_id(&self) -> u64 {
450 let values = [
451 field_shared!(self.regs, pid0).read(),
452 field_shared!(self.regs, pid1).read(),
453 field_shared!(self.regs, pid2).read(),
454 field_shared!(self.regs, pid3).read(),
455 field_shared!(self.regs, pid4).read(),
456 field_shared!(self.regs, pid5).read(),
457 field_shared!(self.regs, pid6).read(),
458 field_shared!(self.regs, pid7).read(),
459 ];
460
461 values
462 .into_iter()
463 .map(|v| v as u64)
464 .enumerate()
465 .fold(0, |acc, (idx, value)| (value << (8 * idx as u64)) | acc)
466 }
467
468 /// Holds a 32 bit component ID value. Used for automatic BIOS configuration. The result is
469 /// `0xB105F00D`.
470 pub fn component_id(&self) -> u64 {
471 let values = [
472 field_shared!(self.regs, cid0).read(),
473 field_shared!(self.regs, cid1).read(),
474 field_shared!(self.regs, cid2).read(),
475 field_shared!(self.regs, cid3).read(),
476 ];
477
478 values
479 .into_iter()
480 .map(|v| v as u64)
481 .enumerate()
482 .fold(0, |acc, (idx, value)| (value << (8 * idx as u64)) | acc)
483 }
484}
485
486#[cfg(test)]
487pub(crate) mod tests {
488 use safe_mmio::UniqueMmioPointer;
489 use zerocopy::transmute_mut;
490
491 use crate::*;
492
493 const DEFAULT_BUILD_CONFIG: u32 = 0b0000_0001_0000_0000_0010_1111_0000_1000;
494
495 pub(crate) struct FakeTZCRegisters {
496 regs: [u32; 1024],
497 }
498
499 impl FakeTZCRegisters {
500 pub fn new() -> Self {
501 let mut s = Self { regs: [0u32; 1024] };
502 s.reg_write(0x000, DEFAULT_BUILD_CONFIG);
503 s
504 }
505
506 pub fn reg_write(&mut self, offset: usize, value: u32) {
507 self.regs[offset / 4] = value;
508 }
509
510 pub fn reg_read(&self, offset: usize) -> u32 {
511 self.regs[offset / 4]
512 }
513
514 fn get(&mut self) -> UniqueMmioPointer<'_, TzcRegisters> {
515 UniqueMmioPointer::from(transmute_mut!(&mut self.regs))
516 }
517
518 pub fn tzc_for_test(&mut self) -> Tzc<'_> {
519 Tzc::new(self.get())
520 }
521 }
522
523 #[test]
524 fn size() {
525 assert_eq!(size_of::<TzcRegisters>(), 4096);
526 }
527
528 #[test]
529 fn build_config() {
530 let mut regs = FakeTZCRegisters::new();
531
532 let config = regs.tzc_for_test().build_configuration();
533 assert_eq!(config.number_of_filters(), 2);
534 assert_eq!(config.address_width(), 48);
535 assert_eq!(config.number_of_regions(), 9);
536
537 regs.reg_write(0x000, 0b0000_0000_0000_0000_0011_1111_0000_1000);
538 let config = regs.tzc_for_test().build_configuration();
539 assert_eq!(config.number_of_filters(), 1);
540 assert_eq!(config.address_width(), 64);
541 assert_eq!(config.number_of_regions(), 9);
542
543 regs.reg_write(0x000, 0b0000_0011_0000_0000_0010_0111_0000_1000);
544 let config = regs.tzc_for_test().build_configuration();
545 assert_eq!(config.number_of_filters(), 4);
546 assert_eq!(config.address_width(), 40);
547 assert_eq!(config.number_of_regions(), 9);
548
549 regs.reg_write(0x000, 0b0000_0011_0000_0000_0010_0011_0000_1000);
550 let config = regs.tzc_for_test().build_configuration();
551 assert_eq!(config.number_of_filters(), 4);
552 assert_eq!(config.address_width(), 36);
553 assert_eq!(config.number_of_regions(), 9);
554
555 regs.reg_write(0x000, 0b0000_0011_0000_0000_0001_1111_0000_1000);
556 let config = regs.tzc_for_test().build_configuration();
557 assert_eq!(config.number_of_filters(), 4);
558 assert_eq!(config.address_width(), 32);
559 assert_eq!(config.number_of_regions(), 9);
560 }
561
562 #[test]
563 #[should_panic]
564 fn build_config_reserved_filters() {
565 let mut regs = FakeTZCRegisters::new();
566 regs.reg_write(0x000, 0b0000_0010_0000_0000_0010_1111_0000_1000);
567 let config = regs.tzc_for_test().build_configuration();
568
569 config.number_of_filters();
570 }
571
572 #[test]
573 #[should_panic]
574 fn build_config_reserved_address_width() {
575 fn run(address_width: u32, regs: &mut FakeTZCRegisters) {
576 regs.reg_write(
577 0x000,
578 0b0000_0001_0000_0000_0000_0000_0000_1000 | address_width << 8,
579 );
580 regs.tzc_for_test().build_configuration().address_width();
581 }
582
583 let mut regs = FakeTZCRegisters::new();
584
585 run(0b11_0011, &mut regs);
586 run(0b11_1101, &mut regs);
587 run(0b00_0000, &mut regs);
588 }
589
590 #[test]
591 #[should_panic]
592 fn build_config_reserved_regions() {
593 fn run(region_count: u32, regs: &mut FakeTZCRegisters) {
594 regs.reg_write(
595 0x000,
596 0b0000_0001_0000_0000_0011_1111_0000_0000 | region_count,
597 );
598 regs.tzc_for_test()
599 .build_configuration()
600 .number_of_regions();
601 }
602
603 let mut regs = FakeTZCRegisters::new();
604
605 run(0b1_1111, &mut regs);
606 run(0b0_0000, &mut regs);
607 run(0b0_1100, &mut regs);
608 }
609
610 #[test]
Imre Kis655db812025-09-23 17:36:55 +0200611 fn build_config_64_bit_address_region() {
612 let mut regs = FakeTZCRegisters::new();
613
614 // Testing address mask calculation with 64 bit addresses.
615 regs.reg_write(0x000, 0b0000_0000_0000_0000_0011_1111_0000_1000);
616
617 let mut tzc = regs.tzc_for_test();
618
619 let region = tzc.region(1);
620 assert!(region.is_some());
621 assert_eq!(u64::MAX, region.unwrap().address_width_mask);
622
623 let region = tzc.region_mut(1);
624 assert!(region.is_some());
625 assert_eq!(u64::MAX, region.unwrap().address_width_mask);
626 }
627
628 #[test]
Ludovic Mermodbe75da82025-09-17 11:13:28 +0200629 fn get_action() {
630 let mut regs = FakeTZCRegisters::new();
631
632 regs.reg_write(0x004, 0b0000_0000_0000_0000_0000_0000_0000_0000);
633 assert_eq!(regs.tzc_for_test().action(), ActionRegister::TZCINTLOW_OKAY);
634
635 regs.reg_write(0x004, 0b0000_0000_0000_0000_0000_0000_0000_0001);
636 assert_eq!(
637 regs.tzc_for_test().action(),
638 ActionRegister::TZCINTLOW_DECERR
639 );
640
641 regs.reg_write(0x004, 0b0000_0000_0000_0000_0000_0000_0000_0010);
642 assert_eq!(
643 regs.tzc_for_test().action(),
644 ActionRegister::TZCINTHIGH_OKAY
645 );
646
647 regs.reg_write(0x004, 0b0000_0000_0000_0000_0000_0000_0000_0011);
648 assert_eq!(
649 regs.tzc_for_test().action(),
650 ActionRegister::TZCINTHIGH_DECERR
651 );
652 }
653
654 #[test]
655 fn set_action() {
656 let mut regs = FakeTZCRegisters::new();
657 let mut tzc = regs.tzc_for_test();
658
659 tzc.set_action(ActionRegister::TZCINTLOW_OKAY);
660 assert_eq!(tzc.action(), ActionRegister::TZCINTLOW_OKAY);
661
662 tzc.set_action(ActionRegister::TZCINTLOW_DECERR);
663 assert_eq!(tzc.action(), ActionRegister::TZCINTLOW_DECERR);
664
665 tzc.set_action(ActionRegister::TZCINTHIGH_OKAY);
666 assert_eq!(tzc.action(), ActionRegister::TZCINTHIGH_OKAY);
667
668 tzc.set_action(ActionRegister::TZCINTHIGH_DECERR);
669 assert_eq!(tzc.action(), ActionRegister::TZCINTHIGH_DECERR);
670 }
671
672 #[test]
673 fn gate_keeper_oob() {
674 let mut regs = FakeTZCRegisters::new();
675 regs.reg_write(0x008, 0b0000_0000_0000_0010_0000_0000_0000_0000);
676 let mut tzc = regs.tzc_for_test();
677
678 assert!(tzc.gate_keeper(2).is_none());
679 assert!(tzc.gate_keeper(3).is_none());
680 assert!(tzc.gate_keeper(4).is_none());
681 assert!(tzc.gate_keeper(18).is_none());
682
683 assert!(tzc.gate_keeper_mut(2).is_none());
684 assert!(tzc.gate_keeper_mut(3).is_none());
685 assert!(tzc.gate_keeper_mut(4).is_none());
686 assert!(tzc.gate_keeper_mut(18).is_none());
687 }
688
689 #[test]
690 fn gate_keeper_status() {
691 let mut regs = FakeTZCRegisters::new();
692 regs.reg_write(0x008, 0b0000_0000_0000_0010_0000_0000_0000_0000);
693 let tzc = regs.tzc_for_test();
694
695 assert_eq!(
696 tzc.gate_keeper(0).unwrap().status(),
697 GateKeeperStatus::Closed
698 );
699 assert_eq!(
700 tzc.gate_keeper(1).unwrap().status(),
701 GateKeeperStatus::Opened
702 );
703 }
704
705 #[test]
706 fn gate_keeper_request() {
707 let mut regs = FakeTZCRegisters::new();
708 regs.reg_write(0x008, 0b0000_0000_0000_0010_0000_0000_0000_0001);
709 let mut tzc = regs.tzc_for_test();
710
711 tzc.gate_keeper_mut(0)
712 .unwrap()
713 .request(GateKeeperStatus::Closed);
714 tzc.gate_keeper_mut(1)
715 .unwrap()
716 .request(GateKeeperStatus::Opened);
717
718 assert_eq!(
719 regs.reg_read(0x008),
720 0b0000_0000_0000_0010_0000_0000_0000_0010,
721 )
722 }
723
724 #[test]
725 fn get_speculation_control() {
726 fn run(flags: u32, regs: &mut FakeTZCRegisters, value: SpeculationControlRegister) {
727 regs.reg_write(0x00C, flags);
728 let spec = regs.tzc_for_test().speculation_control();
729 assert_eq!(spec, value);
730 }
731
732 let mut regs = FakeTZCRegisters::new();
733
734 run(0b00, &mut regs, SpeculationControlRegister::empty());
735 run(
736 0b01,
737 &mut regs,
738 SpeculationControlRegister::READ_SPEC_DISABLE,
739 );
740 run(
741 0b10,
742 &mut regs,
743 SpeculationControlRegister::WRITE_SPEC_DISABLE,
744 );
745 run(
746 0b11,
747 &mut regs,
748 SpeculationControlRegister::READ_SPEC_DISABLE
749 | SpeculationControlRegister::WRITE_SPEC_DISABLE,
750 );
751 }
752
753 #[test]
754 fn set_speculation_control() {
755 fn run(flags: u32, regs: &mut FakeTZCRegisters, value: SpeculationControlRegister) {
756 regs.tzc_for_test().set_speculation_control(value);
757 assert_eq!(regs.reg_read(0x00C), flags);
758 }
759
760 let mut regs = FakeTZCRegisters::new();
761
762 run(0b00, &mut regs, SpeculationControlRegister::empty());
763 run(
764 0b01,
765 &mut regs,
766 SpeculationControlRegister::READ_SPEC_DISABLE,
767 );
768 run(
769 0b10,
770 &mut regs,
771 SpeculationControlRegister::WRITE_SPEC_DISABLE,
772 );
773 run(
774 0b11,
775 &mut regs,
776 SpeculationControlRegister::READ_SPEC_DISABLE
777 | SpeculationControlRegister::WRITE_SPEC_DISABLE,
778 );
779 }
780
781 #[test]
782 pub fn interrupt_status() {
783 let mut regs = FakeTZCRegisters::new();
784 regs.reg_write(0x010, 0b0000_0000_0000_0010_0000_0001_0000_0010);
785 let mut tzc = regs.tzc_for_test();
786
787 let status = tzc.interrupt_status();
788
789 assert!(!status.overlap(0));
790 assert!(status.overlap(1));
791
792 assert!(status.overrun(0));
793 assert!(!status.overrun(1));
794
795 assert!(!status.status(0));
796 assert!(status.status(1));
797 }
798
799 #[test]
800 pub fn interrupt_clear() {
801 let mut regs = FakeTZCRegisters::new();
802 let mut tzc = regs.tzc_for_test();
803
804 let clear = InterruptClearRegister::new(&[0, 3]);
805 tzc.clear_interrupts(clear);
806
807 assert_eq!(
808 regs.reg_read(0x014),
809 0b0000_0000_0000_0000_0000_0000_0000_1001
810 );
811 }
812
813 #[test]
814 fn peripheral_id() {
815 let mut regs = FakeTZCRegisters::new();
816 regs.reg_write(0xFD0, 0x04);
817 regs.reg_write(0xFD4, 0x00);
818 regs.reg_write(0xFD8, 0x00);
819 regs.reg_write(0xFDC, 0x00);
820 regs.reg_write(0xFE0, 0x60);
821 regs.reg_write(0xFE4, 0xB4);
822 regs.reg_write(0xFE8, 0x2B);
823 regs.reg_write(0xFEC, 0x00);
824
825 let tzc = regs.tzc_for_test();
826 assert_eq!(tzc.peripheral_id(), 0x0000_0004_002B_B460);
827 }
828
829 #[test]
830 fn component_id() {
831 let mut regs = FakeTZCRegisters::new();
832 regs.reg_write(0xFF0, 0x0D);
833 regs.reg_write(0xFF4, 0xF0);
834 regs.reg_write(0xFF8, 0x05);
835 regs.reg_write(0xFFC, 0xB1);
836
837 let tzc = regs.tzc_for_test();
838 assert_eq!(tzc.component_id(), 0xB105F00D);
839 }
840
841 #[test]
842 fn fail_oob() {
843 let mut regs = FakeTZCRegisters::new();
844 let tzc = regs.tzc_for_test();
845
846 assert!(tzc.filter_failure(4).is_none());
847 assert!(tzc.filter_failure(8).is_none());
848 assert!(tzc.filter_failure(123124).is_none());
849 }
850
851 #[test]
852 fn region_oob() {
853 let mut regs = FakeTZCRegisters::new();
854 let tzc = regs.tzc_for_test();
855
856 assert!(tzc.region(9).is_none());
857 assert!(tzc.region(32).is_none());
858 assert!(tzc.region(123124).is_none());
859 }
860
861 #[test]
862 fn region_mut_oob() {
863 let mut regs = FakeTZCRegisters::new();
864 let mut tzc = regs.tzc_for_test();
865
866 assert!(tzc.region_mut(9).is_none());
867 assert!(tzc.region_mut(32).is_none());
868 assert!(tzc.region_mut(123124).is_none());
869 }
870}