1use super::registers::CapReg;
12use super::registers::EcapReg;
13use super::registers::TranslationTableMode;
14use bitfield_struct::bitfield;
15use inspect::Inspect;
16use open_enum::open_enum;
17use zerocopy::FromBytes;
18use zerocopy::Immutable;
19use zerocopy::IntoBytes;
20use zerocopy::KnownLayout;
21
22#[derive(Debug, Copy, Clone, FromBytes, IntoBytes, Immutable, KnownLayout)]
24#[repr(C)]
25pub struct InvalidationDescriptor {
26 pub dw0: u32,
28 pub dw1: u32,
30 pub dw2: u32,
32 pub dw3: u32,
34}
35
36impl InvalidationDescriptor {
37 pub fn descriptor_type(&self) -> DescriptorType {
39 DescriptorType(((self.dw0 & 0xf) | ((self.dw0 >> 5) & 0x70)) as u8)
40 }
41
42 pub fn low(&self) -> u64 {
44 u64::from(self.dw0) | (u64::from(self.dw1) << 32)
45 }
46
47 pub fn high(&self) -> u64 {
49 u64::from(self.dw2) | (u64::from(self.dw3) << 32)
50 }
51}
52
53#[derive(Debug, Copy, Clone, FromBytes, IntoBytes, Immutable, KnownLayout)]
55#[repr(C)]
56pub struct InvalidationDescriptor256 {
57 pub lower: InvalidationDescriptor,
59 pub upper: [u64; 2],
61}
62
63#[derive(Debug, Copy, Clone, PartialEq, Eq)]
65pub enum DescriptorWidth {
66 Bits128,
68 Bits256,
70}
71
72impl DescriptorWidth {
73 pub const fn bytes(self) -> usize {
75 match self {
76 Self::Bits128 => 16,
77 Self::Bits256 => 32,
78 }
79 }
80
81 pub(crate) fn checked(
82 dw: bool,
83 mode: TranslationTableMode,
84 ecap: EcapReg,
85 ) -> Result<Self, InvalidationError> {
86 match mode {
87 TranslationTableMode::LEGACY => {}
88 TranslationTableMode::SCALABLE if ecap.smts() => {}
89 TranslationTableMode::ABORT_DMA if ecap.adms() => {}
90 _ => return Err(InvalidationError::InvalidMode(mode)),
91 }
92 if dw {
93 if !ecap.smts() && !ecap.adms() {
94 return Err(InvalidationError::InvalidWidth);
95 }
96 Ok(Self::Bits256)
97 } else if mode == TranslationTableMode::LEGACY {
98 Ok(Self::Bits128)
99 } else {
100 Err(InvalidationError::InvalidWidth)
101 }
102 }
103}
104
105#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
106pub(crate) enum InvalidationError {
107 #[error("invalid descriptor width for the translation mode/capabilities")]
108 InvalidWidth,
109 #[error("invalid translation table mode {0:?}")]
110 InvalidMode(TranslationTableMode),
111 #[error("invalid or unsupported descriptor type {0:?}")]
112 InvalidType(DescriptorType),
113 #[error("reserved bits {bits:#x} in descriptor word {word}")]
114 ReservedBits { word: usize, bits: u64 },
115 #[error("invalid invalidation granularity {0}")]
116 InvalidGranularity(u8),
117 #[error("page-selective IOTLB invalidation is unsupported")]
118 PageInvalidationUnsupported,
119 #[error("unsupported IOTLB address mask {0}")]
120 AddressMask(u8),
121 #[error("unsupported interrupt index mask {0}")]
122 IndexMask(u8),
123 #[error("invalid queue base or reserved IQA bits")]
124 QueueAddress,
125 #[error("invalid queue head offset {0:#x}")]
126 QueueHead(u64),
127 #[error("invalid queue tail offset {0:#x}")]
128 QueueTail(u64),
129}
130
131impl InvalidationDescriptor256 {
132 pub(crate) fn validate(
135 &self,
136 width: DescriptorWidth,
137 mode: TranslationTableMode,
138 cap: CapReg,
139 ecap: EcapReg,
140 ) -> Result<(), InvalidationError> {
141 DescriptorWidth::checked(width == DescriptorWidth::Bits256, mode, ecap)?;
142 let dt = self.lower.descriptor_type();
143 if !(1..=9).contains(&dt.0) || (mode == TranslationTableMode::LEGACY && dt.0 > 5) {
145 return Err(InvalidationError::InvalidType(dt));
146 }
147 if matches!(
151 dt,
152 DescriptorType::DEVICE_TLB_INVALIDATE
153 | DescriptorType::PASID_DEVICE_TLB_INVALIDATE
154 | DescriptorType::PAGE_GROUP_RESPONSE
155 ) || (dt == DescriptorType::INTERRUPT_ENTRY_CACHE_INVALIDATE && !ecap.ir())
156 {
157 return Err(InvalidationError::InvalidType(dt));
158 }
159
160 let lo = self.lower.low();
161 let hi = self.lower.high();
162 let (lo_mask, hi_mask) = match dt {
163 DescriptorType::CONTEXT_CACHE_INVALIDATE => (0x0003_ffff_ffff_003f, 0),
164 DescriptorType::IOTLB_INVALIDATE => (0x0000_0000_ffff_00ff, 0xffff_ffff_ffff_f07f),
165 DescriptorType::INTERRUPT_ENTRY_CACHE_INVALIDATE => (0x0000_ffff_f800_001f, 0),
166 DescriptorType::INVALIDATION_WAIT => {
167 let mask = if ecap.pds() { 0xff } else { 0x7f };
172 (0xffff_ffff_0000_0000 | mask, 0xffff_ffff_ffff_fffc)
173 }
174 DescriptorType::PASID_IOTLB_INVALIDATE => {
175 (0x000f_ffff_ffff_003f, 0xffff_ffff_ffff_f07f)
176 }
177 DescriptorType::PASID_CACHE_INVALIDATE => (0x000f_ffff_ffff_003f, 0),
178 _ => return Err(InvalidationError::InvalidType(dt)),
179 };
180 for (word, (value, allowed)) in [
181 (lo, lo_mask),
182 (hi, hi_mask),
183 (self.upper[0], 0),
184 (self.upper[1], 0),
185 ]
186 .into_iter()
187 .enumerate()
188 {
189 let bits = value & !allowed;
190 if bits != 0 {
191 return Err(InvalidationError::ReservedBits { word, bits });
192 }
193 }
194
195 let granularity = ((lo >> 4) & 3) as u8;
196 match dt {
197 DescriptorType::CONTEXT_CACHE_INVALIDATE | DescriptorType::IOTLB_INVALIDATE
198 if granularity == 0 =>
199 {
200 return Err(InvalidationError::InvalidGranularity(granularity));
201 }
202 DescriptorType::PASID_CACHE_INVALIDATE if granularity == 2 => {
203 return Err(InvalidationError::InvalidGranularity(granularity));
204 }
205 DescriptorType::PASID_IOTLB_INVALIDATE if granularity < 2 => {
206 return Err(InvalidationError::InvalidGranularity(granularity));
207 }
208 _ => {}
209 }
210 if dt == DescriptorType::IOTLB_INVALIDATE && granularity == 3 {
211 if !cap.psi() {
212 return Err(InvalidationError::PageInvalidationUnsupported);
213 }
214 let am = IotlbInvalidateAddress::from(hi).am();
215 if am > cap.mamv() {
216 return Err(InvalidationError::AddressMask(am));
217 }
218 }
219 if dt == DescriptorType::INTERRUPT_ENTRY_CACHE_INVALIDATE {
222 let iec = InterruptCacheInvalidateDw0Dw1::from(lo);
223 if iec.granularity() && iec.im() > ecap.mhmv() {
224 return Err(InvalidationError::IndexMask(iec.im()));
225 }
226 }
227 Ok(())
228 }
229}
230
231open_enum! {
232 #[derive(Inspect)]
234 #[inspect(debug)]
235 pub enum DescriptorType: u8 {
236 CONTEXT_CACHE_INVALIDATE = 0x01,
238 IOTLB_INVALIDATE = 0x02,
240 DEVICE_TLB_INVALIDATE = 0x03,
242 INTERRUPT_ENTRY_CACHE_INVALIDATE = 0x04,
244 INVALIDATION_WAIT = 0x05,
246 PASID_IOTLB_INVALIDATE = 0x06,
248 PASID_CACHE_INVALIDATE = 0x07,
250 PASID_DEVICE_TLB_INVALIDATE = 0x08,
252 PAGE_GROUP_RESPONSE = 0x09,
254 }
255}
256
257#[bitfield(u64)]
278#[derive(Inspect)]
279#[rustfmt::skip]
280pub struct InvalidationWaitDw0Dw1 {
281 #[bits(4)]
283 pub desc_type: u8,
284 pub iflag: bool,
286 pub sw: bool,
288 pub fn_flag: bool,
291 pub pd: bool,
293 #[bits(1)]
294 _reserved1: u64,
295 #[bits(3)]
297 pub type_hi: u8,
298 #[bits(20)]
299 _reserved2: u64,
300 #[bits(32)]
302 pub status_data: u32,
303}
304
305#[bitfield(u64)]
309#[derive(Inspect)]
310#[rustfmt::skip]
311pub struct InvalidationWaitDw2Dw3 {
312 #[bits(2)]
313 _reserved: u64,
314 #[bits(62)]
316 pub sal: u64,
317}
318
319impl InvalidationWaitDw2Dw3 {
320 pub fn status_address(&self) -> u64 {
322 self.sal() << 2
323 }
324}
325
326pub fn parse_invalidation_wait(
328 desc: &InvalidationDescriptor,
329) -> (InvalidationWaitDw0Dw1, InvalidationWaitDw2Dw3) {
330 let lo = desc.low();
331 let hi = desc.high();
332 (
333 InvalidationWaitDw0Dw1::from(lo),
334 InvalidationWaitDw2Dw3::from(hi),
335 )
336}
337
338#[bitfield(u64)]
353#[derive(Inspect)]
354#[rustfmt::skip]
355pub struct ContextCacheInvalidateDw0Dw1 {
356 #[bits(4)]
358 pub desc_type: u8,
359 #[bits(2)]
361 pub granularity: u8,
362 #[bits(3)]
363 _reserved1: u64,
364 #[bits(3)]
366 pub type_hi: u8,
367 #[bits(4)]
368 _reserved_type: u64,
369 #[bits(16)]
371 pub did: u16,
372 #[bits(16)]
374 pub sid: u16,
375 #[bits(2)]
377 pub fm: u8,
378 #[bits(14)]
379 _reserved2: u64,
380}
381
382#[bitfield(u64)]
396#[derive(Inspect)]
397#[rustfmt::skip]
398pub struct IotlbInvalidateDw0Dw1 {
399 #[bits(4)]
401 pub desc_type: u8,
402 #[bits(2)]
404 pub granularity: u8,
405 pub dw: bool,
407 pub dr: bool,
409 #[bits(1)]
410 _reserved1: u64,
411 #[bits(3)]
413 pub type_hi: u8,
414 #[bits(4)]
415 _reserved_type: u64,
416 #[bits(16)]
418 pub did: u16,
419 #[bits(32)]
420 _reserved2: u64,
421}
422
423#[bitfield(u64)]
425#[derive(Inspect)]
426#[rustfmt::skip]
427pub struct IotlbInvalidateAddress {
428 #[bits(6)]
430 pub am: u8,
431 pub ih: bool,
433 #[bits(5)]
434 _reserved: u64,
435 #[bits(52)]
437 pub address: u64,
438}
439
440#[bitfield(u64)]
442#[derive(Inspect)]
443#[rustfmt::skip]
444pub struct PasidInvalidateDw0Dw1 {
445 #[bits(4)]
447 pub desc_type: u8,
448 #[bits(2)]
450 pub granularity: u8,
451 #[bits(3)]
452 _reserved1: u64,
453 #[bits(3)]
455 pub type_hi: u8,
456 #[bits(4)]
457 _reserved2: u64,
458 pub did: u16,
460 #[bits(20)]
462 pub pasid: u32,
463 #[bits(12)]
464 _reserved3: u64,
465}
466
467#[bitfield(u64)]
481#[derive(Inspect)]
482#[rustfmt::skip]
483pub struct InterruptCacheInvalidateDw0Dw1 {
484 #[bits(4)]
486 pub desc_type: u8,
487 pub granularity: bool,
489 #[bits(4)]
490 _reserved1: u64,
491 #[bits(3)]
493 pub type_hi: u8,
494 #[bits(15)]
495 _reserved2: u64,
496 #[bits(5)]
498 pub im: u8,
499 #[bits(16)]
501 pub iidx: u16,
502 #[bits(16)]
503 _reserved3: u64,
504}
505
506pub fn parse_interrupt_cache_invalidate(
508 desc: &InvalidationDescriptor,
509) -> InterruptCacheInvalidateDw0Dw1 {
510 InterruptCacheInvalidateDw0Dw1::from(desc.low())
511}
512
513#[cfg(test)]
514mod tests {
515 use super::*;
516 use test_with_tracing::test;
517
518 fn raw(lo: u64, hi: u64) -> InvalidationDescriptor256 {
519 InvalidationDescriptor256 {
520 lower: InvalidationDescriptor {
521 dw0: lo as u32,
522 dw1: (lo >> 32) as u32,
523 dw2: hi as u32,
524 dw3: (hi >> 32) as u32,
525 },
526 upper: [0; 2],
527 }
528 }
529
530 fn cap() -> CapReg {
531 CapReg::new().with_psi(true).with_mamv(18)
532 }
533
534 fn ecap() -> EcapReg {
535 EcapReg::new().with_smts(true).with_ir(true).with_mhmv(15)
536 }
537
538 fn validate(desc: &InvalidationDescriptor256) -> Result<(), InvalidationError> {
539 desc.validate(
540 DescriptorWidth::Bits256,
541 TranslationTableMode::SCALABLE,
542 cap(),
543 ecap(),
544 )
545 }
546
547 #[test]
548 fn test_full_type_encoding() {
549 assert_eq!(size_of::<InvalidationDescriptor>(), 16);
550 assert_eq!(size_of::<InvalidationDescriptor256>(), 32);
551 for dt in 0..128 {
552 let lo = (dt & 15) | ((dt >> 4) << 9);
553 let desc = raw(lo, 0);
554 assert_eq!(desc.lower.descriptor_type().0, dt as u8);
555 for bit in [4, 5, 6, 7, 8, 12, 13, 14, 15, 31, 32, 63] {
556 assert_eq!(raw(lo | (1 << bit), 0).lower.descriptor_type().0, dt as u8);
557 }
558 }
559 for (dt, value) in [
560 (DescriptorType::CONTEXT_CACHE_INVALIDATE, 1),
561 (DescriptorType::IOTLB_INVALIDATE, 2),
562 (DescriptorType::DEVICE_TLB_INVALIDATE, 3),
563 (DescriptorType::INTERRUPT_ENTRY_CACHE_INVALIDATE, 4),
564 (DescriptorType::INVALIDATION_WAIT, 5),
565 (DescriptorType::PASID_IOTLB_INVALIDATE, 6),
566 (DescriptorType::PASID_CACHE_INVALIDATE, 7),
567 (DescriptorType::PASID_DEVICE_TLB_INVALIDATE, 8),
568 (DescriptorType::PAGE_GROUP_RESPONSE, 9),
569 ] {
570 assert_eq!(dt.0, value);
571 }
572 }
573
574 #[test]
575 fn test_literal_descriptor_bytes() {
576 let bytes = [
577 0x26, 0x00, 0x34, 0x12, 0xde, 0xbc, 0x0a, 0x00, 0x52, 0x10, 0x32, 0x54, 0x76, 0x98,
578 0xba, 0xdc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
579 ];
580 let desc = InvalidationDescriptor256::read_from_bytes(&bytes).unwrap();
581 assert_eq!(desc.lower.low(), 0x000a_bcde_1234_0026);
582 assert_eq!(desc.lower.high(), 0xdcba_9876_5432_1052);
583 assert_eq!(desc.upper, [0, 0]);
584 assert_eq!(desc.as_bytes(), bytes);
585 let short = InvalidationDescriptor::read_from_bytes(&bytes[..16]).unwrap();
586 assert_eq!(short.as_bytes(), &bytes[..16]);
587 assert_eq!(
588 short.descriptor_type(),
589 DescriptorType::PASID_IOTLB_INVALIDATE
590 );
591 assert!(validate(&desc).is_ok());
592 }
593
594 #[test]
595 fn test_context_raw_fields() {
596 let value = 0x0003_abcd_9876_0031;
597 let cc = ContextCacheInvalidateDw0Dw1::from(value);
598 assert_eq!((cc.desc_type(), cc.type_hi(), cc.granularity()), (1, 0, 3));
599 assert_eq!((cc.did(), cc.sid(), cc.fm()), (0x9876, 0xabcd, 3));
600 assert_eq!(
601 ContextCacheInvalidateDw0Dw1::new()
602 .with_desc_type(1)
603 .with_granularity(3)
604 .with_did(0x9876)
605 .with_sid(0xabcd)
606 .with_fm(3)
607 .into_bits(),
608 value
609 );
610 }
611
612 #[test]
613 fn test_iotlb_raw_fields() {
614 for g in 0..4 {
615 for drains in 0..4 {
616 let value = 0xabcd_0002 | (g << 4) | (drains << 6);
617 let iotlb = IotlbInvalidateDw0Dw1::from(value);
618 assert_eq!(iotlb.granularity(), g as u8);
619 assert_eq!(iotlb.dw(), drains & 1 != 0);
620 assert_eq!(iotlb.dr(), drains & 2 != 0);
621 assert_eq!(iotlb.did(), 0xabcd);
622 assert_eq!(
623 IotlbInvalidateDw0Dw1::new()
624 .with_desc_type(2)
625 .with_granularity(g as u8)
626 .with_dw(drains & 1 != 0)
627 .with_dr(drains & 2 != 0)
628 .with_did(0xabcd)
629 .into_bits(),
630 value
631 );
632 }
633 }
634 for am in 0..64 {
635 for ih in [false, true] {
636 let value = 0x1234_5678_9abc_d000 | am | ((ih as u64) << 6);
637 let address = IotlbInvalidateAddress::from(value);
638 assert_eq!(
639 (address.am(), address.ih(), address.address()),
640 (am as u8, ih, 0x0001_2345_6789_abcd)
641 );
642 assert_eq!(
643 IotlbInvalidateAddress::new()
644 .with_am(am as u8)
645 .with_ih(ih)
646 .with_address(0x0001_2345_6789_abcd)
647 .into_bits(),
648 value
649 );
650 }
651 }
652 }
653
654 #[test]
655 fn test_pasid_raw_fields() {
656 for dt in [6, 7] {
657 for g in 0..4 {
658 let value = 0x000a_bcde_1234_0000 | (g << 4) | dt;
659 let pasid = PasidInvalidateDw0Dw1::from(value);
660 assert_eq!(
661 (pasid.desc_type(), pasid.type_hi(), pasid.granularity()),
662 (dt as u8, 0, g as u8)
663 );
664 assert_eq!((pasid.pasid(), pasid.did()), (0xabcde, 0x1234));
665 assert_eq!(
666 PasidInvalidateDw0Dw1::new()
667 .with_desc_type(dt as u8)
668 .with_granularity(g as u8)
669 .with_pasid(0xabcde)
670 .with_did(0x1234)
671 .into_bits(),
672 value
673 );
674 }
675 }
676 }
677
678 #[test]
679 fn test_interrupt_cache_raw_fields() {
680 for im in 0..32 {
681 for g in [false, true] {
682 let value = 0x0000_abcd_0000_0004 | (im << 27) | ((g as u64) << 4);
683 let iec = parse_interrupt_cache_invalidate(&raw(value, 0).lower);
684 assert_eq!(
685 (iec.im(), iec.iidx(), iec.granularity()),
686 (im as u8, 0xabcd, g)
687 );
688 assert_eq!(
689 InterruptCacheInvalidateDw0Dw1::new()
690 .with_desc_type(4)
691 .with_granularity(g)
692 .with_im(im as u8)
693 .with_iidx(0xabcd)
694 .into_bits(),
695 value
696 );
697 }
698 }
699 }
700
701 #[test]
702 fn test_wait_raw_fields() {
703 for flags in 0..16 {
704 let value = 0xabcd_1234_0000_0005 | (flags << 4);
705 let (lo, hi) = parse_invalidation_wait(&raw(value, 0x1234_5678_9abc_defc).lower);
706 assert_eq!(lo.iflag(), flags & 1 != 0);
707 assert_eq!(lo.sw(), flags & 2 != 0);
708 assert_eq!(lo.fn_flag(), flags & 4 != 0);
709 assert_eq!(lo.pd(), flags & 8 != 0);
710 assert_eq!(lo.status_data(), 0xabcd_1234);
711 assert_eq!(hi.status_address(), 0x1234_5678_9abc_defc);
712 assert_eq!(
713 InvalidationWaitDw0Dw1::new()
714 .with_desc_type(5)
715 .with_iflag(flags & 1 != 0)
716 .with_sw(flags & 2 != 0)
717 .with_fn_flag(flags & 4 != 0)
718 .with_pd(flags & 8 != 0)
719 .with_status_data(0xabcd_1234)
720 .into_bits(),
721 value
722 );
723 assert_eq!(
724 InvalidationWaitDw2Dw3::new()
725 .with_sal(0x1234_5678_9abc_defc >> 2)
726 .into_bits(),
727 0x1234_5678_9abc_defc
728 );
729 }
730 }
731
732 #[test]
733 fn test_mode_width_type_matrix() {
734 for smts in [false, true] {
735 for adms in [false, true] {
736 let ecap = ecap().with_smts(smts).with_adms(adms);
737 for mode in 0..4 {
738 for dw in [false, true] {
739 let mode_allowed = match mode {
740 0 => true,
741 1 => smts,
742 3 => adms,
743 _ => false,
744 };
745 let width_allowed = if dw { smts || adms } else { mode == 0 };
746 let width = if dw {
747 DescriptorWidth::Bits256
748 } else {
749 DescriptorWidth::Bits128
750 };
751 assert_eq!(
752 DescriptorWidth::checked(dw, TranslationTableMode(mode), ecap).is_ok(),
753 mode_allowed && width_allowed
754 );
755 for dt in 0..128 {
756 let g = if dt == 6 { 2 } else { 1 };
757 let lo = (dt & 15) | ((dt >> 4) << 9) | (g << 4);
758 let expected = mode_allowed
759 && width_allowed
760 && (matches!(dt, 1 | 2 | 4 | 5)
761 || (mode != 0 && matches!(dt, 6 | 7)));
762 assert_eq!(
763 raw(lo, 0)
764 .validate(width, TranslationTableMode(mode), cap(), ecap)
765 .is_ok(),
766 expected,
767 "smts={smts} adms={adms} mode={mode} dw={dw} type={dt}"
768 );
769 }
770 }
771 }
772 }
773 }
774 }
775
776 #[test]
777 fn test_granularity_and_ignored_fields() {
778 for (dt, valid) in [
779 (1, [false, true, true, true]),
780 (2, [false, true, true, true]),
781 (6, [false, false, true, true]),
782 (7, [true, true, false, true]),
783 ] {
784 for (g, expected) in valid.into_iter().enumerate() {
785 assert_eq!(validate(&raw(dt | ((g as u64) << 4), 0)).is_ok(), expected);
786 }
787 }
788 for lo in [
791 0x0003_ffff_ffff_0011,
792 0x000f_ffff_ffff_0037,
793 0x000f_ffff_ffff_0007,
794 ] {
795 assert!(validate(&raw(lo, 0)).is_ok());
796 }
797 for lo in [0xffff_0012, 0xffff_0022, 0x000f_ffff_ffff_0026] {
800 assert!(validate(&raw(lo, 0xffff_ffff_ffff_f07f)).is_ok());
801 }
802 assert!(validate(&raw(0xffff_ffff_0000_0005, 0xffff_ffff_ffff_fffc)).is_ok());
804 assert!(validate(&raw(5, 1)).is_err());
805 }
806
807 #[test]
808 fn test_capability_dependent_fields() {
809 for am in 0..64 {
810 let desc = raw(0x32, 0xffff_ffff_ffff_f000 | am);
811 assert_eq!(validate(&desc).is_ok(), am <= 18);
812 assert_eq!(
813 desc.validate(
814 DescriptorWidth::Bits256,
815 TranslationTableMode::SCALABLE,
816 cap().with_psi(false),
817 ecap()
818 ),
819 Err(InvalidationError::PageInvalidationUnsupported)
820 );
821 assert!(
823 raw(0x36, am)
824 .validate(
825 DescriptorWidth::Bits256,
826 TranslationTableMode::SCALABLE,
827 cap().with_psi(false).with_mamv(0),
828 ecap()
829 )
830 .is_ok()
831 );
832 }
833 for im in 0..32 {
834 assert_eq!(validate(&raw(0x14 | (im << 27), 0)).is_ok(), im <= 15);
835 assert!(validate(&raw(4 | (im << 27), 0)).is_ok());
836 }
837 assert!(validate(&raw(0x85, 0)).is_err()); assert!(
839 raw(0x85, 0)
840 .validate(
841 DescriptorWidth::Bits256,
842 TranslationTableMode::SCALABLE,
843 cap(),
844 ecap().with_pds(true)
845 )
846 .is_ok()
847 );
848 assert!(
849 raw(4, 0)
850 .validate(
851 DescriptorWidth::Bits256,
852 TranslationTableMode::SCALABLE,
853 cap(),
854 ecap().with_ir(false)
855 )
856 .is_err()
857 );
858 }
859
860 #[test]
861 fn test_every_reserved_bit_and_padding() {
862 for (lo, reserved) in [
864 (0x11, vec![6..9, 12..16, 50..128]),
865 (0x12, vec![8..9, 12..16, 32..64, 71..76]),
866 (0x04, vec![5..9, 12..27, 48..128]),
867 (0x05, vec![7..9, 12..32, 64..66]),
868 (0x26, vec![6..9, 12..16, 52..64, 71..76]),
869 (0x07, vec![6..9, 12..16, 52..128]),
870 ] {
871 for bit in reserved.into_iter().flatten().chain(128..256) {
872 let mut desc = raw(lo, 0);
873 match bit {
874 0..64 => desc.lower = raw(lo | (1 << bit), 0).lower,
875 64..128 => desc.lower = raw(lo, 1 << (bit - 64)).lower,
876 _ => desc.upper[(bit - 128) / 64] = 1 << (bit % 64),
877 }
878 assert!(
879 matches!(validate(&desc), Err(InvalidationError::ReservedBits { .. })),
880 "type={} bit={bit}",
881 lo & 15
882 );
883 }
884 }
885 }
886
887 #[test]
888 fn test_parse_invalidation_wait() {
889 let lo_val = InvalidationWaitDw0Dw1::new()
890 .with_desc_type(0x05)
891 .with_sw(true)
892 .with_status_data(0x42);
893 let hi_val = InvalidationWaitDw2Dw3::new().with_sal(0x1000);
894
895 let lo_raw = u64::from(lo_val);
896 let hi_raw = u64::from(hi_val);
897
898 let desc = InvalidationDescriptor {
899 dw0: lo_raw as u32,
900 dw1: (lo_raw >> 32) as u32,
901 dw2: hi_raw as u32,
902 dw3: (hi_raw >> 32) as u32,
903 };
904
905 let (parsed_lo, parsed_hi) = parse_invalidation_wait(&desc);
906 assert!(parsed_lo.sw());
907 assert_eq!(parsed_lo.status_data(), 0x42);
908 assert_eq!(parsed_hi.status_address(), 0x1000 << 2);
909 }
910
911 #[test]
912 fn test_parse_interrupt_cache_invalidate() {
913 let lo_val = InterruptCacheInvalidateDw0Dw1::new()
914 .with_desc_type(0x04)
915 .with_granularity(true)
916 .with_im(0x1f)
917 .with_iidx(0x1234);
918
919 let lo_raw = u64::from(lo_val);
920 let desc = InvalidationDescriptor {
921 dw0: lo_raw as u32,
922 dw1: (lo_raw >> 32) as u32,
923 dw2: 0,
924 dw3: 0,
925 };
926
927 let parsed = parse_interrupt_cache_invalidate(&desc);
928 assert!(parsed.granularity());
929 assert_eq!(parsed.im(), 0x1f);
930 assert_eq!(parsed.iidx(), 0x1234);
931 }
932}