intel_vtd/spec/registers.rs
1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! MMIO register definitions for the Intel VT-d IOMMU.
5//!
6//! Based on the Intel VT-d Specification Rev 4.1, §10.4 (Register
7//! Descriptions). VT-d is a pure MMIO device — no PCI config space.
8
9use bitfield_struct::bitfield;
10use inspect::Inspect;
11use open_enum::open_enum;
12
13// =============================================================================
14// MMIO Register Offsets (§10.4)
15// =============================================================================
16
17open_enum! {
18 /// MMIO register offsets for the Intel VT-d IOMMU.
19 ///
20 /// 64-bit registers have separate `_HI` variants for the upper DWORD
21 /// (base + 4), enabling flat match dispatch without nested fallbacks.
22 #[derive(Inspect)]
23 #[inspect(debug)]
24 pub enum MmioRegister: u16 {
25 /// Version Register (32-bit, RO). §10.4.1.
26 VER = 0x000,
27 /// Capability Register — lo DWORD (64-bit, RO). §10.4.2.
28 CAP = 0x008,
29 /// Capability Register — hi DWORD.
30 CAP_HI = 0x00C,
31 /// Extended Capability Register — lo DWORD (64-bit, RO). §10.4.3.
32 ECAP = 0x010,
33 /// Extended Capability Register — hi DWORD.
34 ECAP_HI = 0x014,
35 /// Global Command Register (32-bit, WO). §10.4.4.
36 GCMD = 0x018,
37 /// Global Status Register (32-bit, RO). §10.4.5.
38 GSTS = 0x01C,
39 /// Root Table Address Register — lo DWORD (64-bit, RW). §10.4.6.
40 RTADDR = 0x020,
41 /// Root Table Address Register — hi DWORD.
42 RTADDR_HI = 0x024,
43 /// Context Command Register — lo DWORD (64-bit, RW). §10.4.7.
44 CCMD = 0x028,
45 /// Context Command Register — hi DWORD.
46 CCMD_HI = 0x02C,
47 /// Fault Status Register (32-bit, RW1C). §10.4.9.
48 FSTS = 0x034,
49 /// Fault Event Control Register (32-bit, RW). §10.4.10.
50 FECTL = 0x038,
51 /// Fault Event Data Register (32-bit, RW). §10.4.11.
52 FEDATA = 0x03C,
53 /// Fault Event Address Register (32-bit, RW). §10.4.12.
54 FEADDR = 0x040,
55 /// Fault Event Upper Address Register (32-bit, RW). §10.4.13.
56 FEUADDR = 0x044,
57 /// Invalidation Queue Head Register — lo DWORD (64-bit, RO). §10.4.17.
58 IQH = 0x080,
59 /// Invalidation Queue Head Register — hi DWORD.
60 IQH_HI = 0x084,
61 /// Invalidation Queue Tail Register — lo DWORD (64-bit, RW). §10.4.18.
62 IQT = 0x088,
63 /// Invalidation Queue Tail Register — hi DWORD.
64 IQT_HI = 0x08C,
65 /// Invalidation Queue Address Register — lo DWORD (64-bit, RW). §10.4.19.
66 IQA = 0x090,
67 /// Invalidation Queue Address Register — hi DWORD.
68 IQA_HI = 0x094,
69 /// Invalidation Completion Status Register (32-bit, RW1C). §10.4.20.
70 ICS = 0x09C,
71 /// Invalidation Event Control Register (32-bit, RW). §10.4.21.
72 IECTL = 0x0A0,
73 /// Invalidation Event Data Register (32-bit, RW). §10.4.22.
74 IEDATA = 0x0A4,
75 /// Invalidation Event Address Register (32-bit, RW). §10.4.23.
76 IEADDR = 0x0A8,
77 /// Invalidation Event Upper Address Register (32-bit, RW). §10.4.24.
78 IEUADDR = 0x0AC,
79 /// Interrupt Remapping Table Address Register — lo DWORD (64-bit, RW). §10.4.29.
80 IRTA = 0x0B8,
81 /// Interrupt Remapping Table Address Register — hi DWORD.
82 IRTA_HI = 0x0BC,
83 /// Invalidate Address Register — lo DWORD (64-bit, RW). §10.4.15.
84 IVA = 0x100,
85 /// Invalidate Address Register — hi DWORD.
86 IVA_HI = 0x104,
87 /// IOTLB Invalidate Register — lo DWORD (64-bit, RW). §10.4.16.
88 IOTLB = 0x108,
89 /// IOTLB Invalidate Register — hi DWORD.
90 IOTLB_HI = 0x10C,
91 /// Fault Recording Register DWORD 0 (FrcdLo lo). §10.4.14.
92 FRCD_DW0 = 0x120,
93 /// Fault Recording Register DWORD 1 (FrcdLo hi).
94 FRCD_DW1 = 0x124,
95 /// Fault Recording Register DWORD 2 (FrcdHi lo).
96 FRCD_DW2 = 0x128,
97 /// Fault Recording Register DWORD 3 (FrcdHi hi, contains F bit).
98 FRCD_DW3 = 0x12C,
99 }
100}
101
102/// MMIO region size in bytes (4KB, one page).
103pub const MMIO_REGION_SIZE: u64 = 0x1000;
104
105// =============================================================================
106// Register Bitfield Definitions
107// =============================================================================
108
109/// Version Register (MMIO offset 0x000, 32-bit, RO). §10.4.1.
110///
111/// Reports the architecture version implemented by the hardware.
112#[bitfield(u32)]
113#[derive(Inspect)]
114#[rustfmt::skip]
115pub struct VersionReg {
116 /// Minor version number.
117 #[bits(4)]
118 pub min: u8,
119 /// Major version number.
120 #[bits(4)]
121 pub max: u8,
122 #[bits(24)]
123 _reserved: u32,
124}
125
126/// Capability Register (MMIO offset 0x008, 64-bit, RO). §10.4.2.
127///
128/// Reports hardware capabilities to software. Key fields control the
129/// supported address widths, large page sizes, fault recording, and
130/// domain count.
131#[bitfield(u64)]
132#[derive(Inspect)]
133#[rustfmt::skip]
134pub struct CapReg {
135 /// Number of domains supported (3 bits). See [`NumDomains`].
136 #[bits(3)]
137 pub nd: u8,
138 /// Advanced Fault Logging (not supported in emulator).
139 pub afl: bool,
140 /// Required Write-Buffer Flushing — 0 = not required.
141 pub rwbf: bool,
142 /// Protected Low-Memory Region support.
143 pub plmr: bool,
144 /// Protected High-Memory Region support.
145 pub phmr: bool,
146 /// Caching Mode — 0 = non-caching (walk tables on every access).
147 pub cm: bool,
148 /// Supported Adjusted Guest Address Widths (5 bits).
149 /// Bit 0 (reserved), bit 1 = 39-bit (3-level), bit 2 = 48-bit (4-level),
150 /// bit 3 = 57-bit (5-level), bit 4 (reserved).
151 #[bits(5)]
152 pub sagaw: u8,
153 #[bits(3)]
154 _reserved1: u64,
155 /// Maximum Guest Address Width (6 bits) — value is MGAW-1.
156 /// 47 = 48-bit address width.
157 #[bits(6)]
158 pub mgaw: u8,
159 /// Zero-Length Read — 1 = supports zero-length read requests.
160 pub zlr: bool,
161 /// Deprecated (was FLR — Fault Log Register, pre-spec 2.0).
162 #[bits(1)]
163 _deprecated: u64,
164 /// Fault Recording Register Offset (10 bits).
165 /// Offset in 16-byte units from register base. FRO=0x12 → offset 0x120.
166 #[bits(10)]
167 pub fro: u16,
168 /// Second-Level Large Page Support (4 bits).
169 /// Bit 0 = 2MB (21-bit), bit 1 = 1GB (30-bit).
170 #[bits(4)]
171 pub sllps: u8,
172 #[bits(1)]
173 _reserved_38: u64,
174 /// Page-Selective Invalidation support.
175 pub psi: bool,
176 /// Number of Fault Recording registers minus 1 (8 bits).
177 /// NFR=0 means 1 fault record.
178 #[bits(8)]
179 pub nfr: u8,
180 /// Maximum Address Mask Value (6 bits) for page-selective invalidation.
181 #[bits(6)]
182 pub mamv: u8,
183 /// DMA Write Draining — 1 = hardware drains write buffers.
184 pub dwd: bool,
185 /// DMA Read Draining — 1 = hardware drains read requests.
186 pub drd: bool,
187 /// First-Level 1GB page support (not used in legacy mode).
188 pub fl1gp: bool,
189 #[bits(2)]
190 _reserved2: u64,
191 /// Page-walk Incoherency — 1 = page walk results may be incoherent.
192 pub pi: bool,
193 #[bits(4)]
194 _reserved3: u64,
195}
196
197open_enum! {
198 /// CAP.ND encoding: number of supported domain IDs. §10.4.2, Table 10-2.
199 #[derive(Inspect)]
200 #[inspect(debug)]
201 pub enum NumDomains: u8 {
202 /// 16 domains (4-bit domain ID).
203 ND_16 = 0,
204 /// 64 domains (6-bit domain ID).
205 ND_64 = 1,
206 /// 256 domains (8-bit domain ID).
207 ND_256 = 2,
208 /// 1024 domains (10-bit domain ID).
209 ND_1K = 3,
210 /// 4096 domains (12-bit domain ID).
211 ND_4K = 4,
212 /// 16384 domains (14-bit domain ID).
213 ND_16K = 5,
214 /// 65536 domains (16-bit domain ID).
215 ND_64K = 6,
216 }
217}
218
219/// CAP.SLLPS bit: 2MB large page support (21-bit page offset).
220pub const SLLPS_2MB: u8 = 1 << 0;
221/// CAP.SLLPS bit: 1GB large page support (30-bit page offset).
222pub const SLLPS_1GB: u8 = 1 << 1;
223
224/// Extended Capability Register (MMIO offset 0x010, 64-bit, RO). §11.4.3.
225///
226/// Reports queued invalidation, interrupt remapping, scalable translation,
227/// and second-stage accessed/dirty capabilities.
228#[bitfield(u64)]
229#[derive(Inspect)]
230#[rustfmt::skip]
231pub struct EcapReg {
232 /// Page-walk Coherency — 1 = hardware ensures coherency.
233 pub c: bool,
234 /// Queued Invalidation support.
235 pub qi: bool,
236 /// Device-TLB support (not implemented).
237 pub dt: bool,
238 /// Interrupt Remapping support.
239 pub ir: bool,
240 /// Extended Interrupt Mode — 1 = x2APIC (32-bit destination IDs).
241 pub eim: bool,
242 #[bits(1)]
243 _reserved1: u64,
244 /// Pass Through support.
245 pub pt: bool,
246 /// Snoop Control support.
247 pub sc: bool,
248 /// IOTLB Register Offset (10 bits).
249 /// Offset in 16-byte units from register base. IRO=0x10 → offset 0x100.
250 #[bits(10)]
251 pub iro: u16,
252 #[bits(2)]
253 _reserved2: u64,
254 /// Maximum Handle Mask Value (4 bits) for interrupt remapping.
255 #[bits(4)]
256 pub mhmv: u8,
257 #[bits(1)]
258 _reserved3: u64,
259 /// Memory Type Support (not used).
260 pub mts: bool,
261 /// Nested Translation support (not implemented).
262 pub nest: bool,
263 #[bits(2)]
264 _reserved4: u64,
265 /// Page Request support (not implemented).
266 pub prs: bool,
267 /// Execute Request support (not implemented).
268 pub ers: bool,
269 /// Supervisor Request support (not implemented).
270 pub srs: bool,
271 #[bits(1)]
272 _reserved5: u64,
273 /// No Write Flag support.
274 pub nwfs: bool,
275 /// Extended Accessed Flag support.
276 pub eafs: bool,
277 /// Process Address Space ID Size (5 bits, not used).
278 #[bits(5)]
279 pub pss: u8,
280 /// PASID Translation support (not implemented).
281 pub pasid: bool,
282 /// Device-TLB Invalidation Throttle (not used).
283 pub dit: bool,
284 /// Page-request Drain Support (not implemented).
285 pub pds: bool,
286 /// Scalable-mode DMA remapping through context and PASID tables.
287 pub smts: bool,
288 /// Virtual Command support (not used).
289 pub vcs: bool,
290 /// Second-stage accessed/dirty flag support, enabled by PASID-entry SSADE.
291 pub ssads: bool,
292 /// Second-stage translation support in scalable mode.
293 pub ssts: bool,
294 /// First-stage Translation Support (scalable mode, not used).
295 pub flts: bool,
296 /// Scalable-mode page walks snoop according to PASID-entry PWSNP.
297 pub smpwcs: bool,
298 /// Context-entry RID_PASID support for requests without an explicit PASID.
299 pub rps: bool,
300 #[bits(1)]
301 _reserved6: u64,
302 /// Performance Monitoring Support (not used).
303 pub pms: bool,
304 /// Abort DMA Mode Support (not used).
305 pub adms: bool,
306 /// RID_PRIV Support (not used).
307 pub rprivs: bool,
308 #[bits(4)]
309 _reserved7: u64,
310 /// Stop Marker Support (not used).
311 pub sms: bool,
312 #[bits(5)]
313 _reserved8: u64,
314}
315
316/// Global Command Register (MMIO offset 0x018, 32-bit, WO). §10.4.4.
317///
318/// Write-only register for controlling IOMMU operation. Reads return 0.
319/// Each bit triggers a corresponding action; status is reflected in GSTS.
320#[bitfield(u32)]
321#[derive(Inspect)]
322#[rustfmt::skip]
323pub struct GcmdReg {
324 #[bits(23)]
325 _reserved: u32,
326 /// Compatibility Format Interrupt — pass through compatibility-format
327 /// MSIs when IR is enabled.
328 pub cfi: bool,
329 /// Set Interrupt Remapping Table Pointer — latch IRTA register.
330 pub sirtp: bool,
331 /// Interrupt Remapping Enable.
332 pub ire: bool,
333 /// Queued Invalidation Enable.
334 pub qie: bool,
335 /// Write Buffer Flush — flush internal write buffers.
336 pub wbf: bool,
337 /// Enable Advanced Fault Logging (not supported, treated as no-op).
338 pub eafl: bool,
339 /// Set Fault Log (not supported, treated as no-op).
340 pub sfl: bool,
341 /// Set Root Table Pointer — latch RTADDR register.
342 pub srtp: bool,
343 /// Translation Enable — enable/disable DMA translation.
344 pub te: bool,
345}
346
347/// Global Status Register (MMIO offset 0x01C, 32-bit, RO). §10.4.5.
348///
349/// Reports the status corresponding to GCMD operations.
350#[bitfield(u32)]
351#[derive(Inspect)]
352#[rustfmt::skip]
353pub struct GstsReg {
354 #[bits(23)]
355 _reserved: u32,
356 /// Compatibility Format Interrupt Status.
357 pub cfis: bool,
358 /// Interrupt Remapping Table Pointer Status — IRTA has been latched.
359 pub irtps: bool,
360 /// Interrupt Remapping Enable Status.
361 pub ires: bool,
362 /// Queued Invalidation Enable Status.
363 pub qies: bool,
364 /// Write Buffer Flush Status.
365 pub wbfs: bool,
366 /// Advanced Fault Logging Status.
367 pub afls: bool,
368 /// Fault Log Status.
369 pub fls: bool,
370 /// Root Table Pointer Status — RTADDR has been latched.
371 pub rtps: bool,
372 /// Translation Enable Status.
373 pub tes: bool,
374}
375
376/// Root Table Address Register (MMIO offset 0x020, 64-bit, RW). §10.4.6.
377///
378/// Holds the physical address of the root table. The value is consumed when
379/// GCMD.SRTP is written. Bits 11:10 select translation type (00=legacy).
380#[bitfield(u64)]
381#[derive(Inspect)]
382#[rustfmt::skip]
383pub struct RtaddrReg {
384 #[bits(10)]
385 _reserved: u64,
386 /// Translation table mode (bits 11:10).
387 /// 00 = legacy mode, 01 = scalable mode.
388 #[bits(2)]
389 pub ttm: u8,
390 /// Root table address, bits [63:12]. 4KB-aligned.
391 #[bits(52)]
392 pub rta: u64,
393}
394
395impl RtaddrReg {
396 /// Get the full root table physical address (bits 63:12 shifted).
397 pub fn root_table_address(&self) -> u64 {
398 self.rta() << 12
399 }
400}
401
402open_enum! {
403 /// Translation table mode in RTADDR.TTM (§11.4.5).
404 #[derive(Inspect)]
405 #[inspect(debug)]
406 pub enum TranslationTableMode: u8 {
407 /// Legacy root/context tables.
408 LEGACY = 0b00,
409 /// Scalable root/context and PASID tables.
410 SCALABLE = 0b01,
411 /// Abort DMA (not supported by the emulator).
412 ABORT_DMA = 0b11,
413 }
414}
415
416/// Context Command Register (MMIO offset 0x028, 64-bit, RW). §10.4.7.
417///
418/// Register-based context-cache invalidation (used before QI is enabled).
419#[bitfield(u64)]
420#[derive(Inspect)]
421#[rustfmt::skip]
422pub struct CcmdReg {
423 /// Domain ID for domain-selective invalidation.
424 #[bits(16)]
425 pub did: u16,
426 /// Source ID for device-selective invalidation.
427 #[bits(16)]
428 pub sid: u16,
429 /// Function Mask for device-selective invalidation.
430 #[bits(2)]
431 pub fm: u8,
432 #[bits(25)]
433 _reserved: u64,
434 /// Context Actual Invalidation Granularity (RO, set by HW).
435 #[bits(2)]
436 pub caig: u8,
437 /// Context Invalidation Request Granularity.
438 /// 01=global, 10=domain, 11=device.
439 #[bits(2)]
440 pub cirg: u8,
441 /// Invalidate Context-Cache — set by SW, cleared by HW when done.
442 pub icc: bool,
443}
444
445/// Fault Status Register (MMIO offset 0x034, 32-bit, RW1C). §10.4.9.
446///
447/// Reports fault recording status. PPF is dynamically computed as OR of all
448/// FRCD\[n\].F bits.
449#[bitfield(u32)]
450#[derive(Inspect)]
451#[rustfmt::skip]
452pub struct FstsReg {
453 /// Primary Fault Overflow — a fault was discarded because all records
454 /// were occupied.
455 pub pfo: bool,
456 /// Primary Fault Pending — dynamically computed as OR of FRCD[n].F.
457 /// Not independently stored; always reflects current FRCD state.
458 pub ppf: bool,
459 /// Advanced Fault Overflow (not used).
460 pub afo: bool,
461 /// Advanced Fault Pending (not used).
462 pub apf: bool,
463 /// Invalidation Queue Error — illegal descriptor in the queue.
464 pub iqe: bool,
465 /// Invalidation Completion Error (not used).
466 pub ice: bool,
467 /// Invalidation Timeout Error (not used).
468 pub ite: bool,
469 #[bits(1)]
470 _reserved1: u32,
471 /// Fault Record Index — index of the first pending fault record.
472 #[bits(8)]
473 pub fri: u8,
474 #[bits(16)]
475 _reserved2: u32,
476}
477
478/// Fault Event Control Register (MMIO offset 0x038, 32-bit, RW). §10.4.10.
479///
480/// Controls fault event MSI signaling.
481#[bitfield(u32)]
482#[derive(Inspect)]
483#[rustfmt::skip]
484pub struct FectlReg {
485 #[bits(30)]
486 _reserved: u32,
487 /// Interrupt Pending (RO) — set when fault interrupt is pending.
488 pub ip: bool,
489 /// Interrupt Mask — 1 = mask fault event interrupt.
490 pub im: bool,
491}
492
493/// Fault Event Data Register (MMIO offset 0x03C, 32-bit, RW). §10.4.11.
494#[bitfield(u32)]
495#[derive(Inspect)]
496#[rustfmt::skip]
497pub struct FedataReg {
498 /// Interrupt Message Data.
499 #[bits(16)]
500 pub imd: u16,
501 /// Extended Interrupt Message Data (for x2APIC).
502 #[bits(16)]
503 pub eimd: u16,
504}
505
506/// Fault Event Address Register (MMIO offset 0x040, 32-bit, RW). §10.4.12.
507#[bitfield(u32)]
508#[derive(Inspect)]
509#[rustfmt::skip]
510pub struct FeaddrReg {
511 #[bits(2)]
512 _reserved: u32,
513 /// Message Address (bits 31:2).
514 #[bits(30)]
515 pub ma: u32,
516}
517
518impl FeaddrReg {
519 /// Get the full message address (bits 31:2 shifted).
520 pub fn message_address(&self) -> u32 {
521 self.ma() << 2
522 }
523}
524
525/// Invalidation Queue Head Register (MMIO offset 0x080, 64-bit). §10.4.17.
526///
527/// Read-only from software. Points to the next descriptor to be fetched.
528#[bitfield(u64)]
529#[derive(Inspect)]
530#[rustfmt::skip]
531pub struct IqhReg {
532 #[bits(4)]
533 _reserved1: u64,
534 /// Queue head in 16-byte units (bits 18:4); bit 4 is zero for IQA.DW=1.
535 #[bits(15)]
536 pub qh: u32,
537 #[bits(45)]
538 _reserved2: u64,
539}
540
541impl IqhReg {
542 /// Get the byte offset of the head pointer.
543 pub fn head_offset(&self) -> u64 {
544 (self.qh() as u64) << 4
545 }
546}
547
548/// Invalidation Queue Tail Register (MMIO offset 0x088, 64-bit, RW). §10.4.18.
549///
550/// Software writes this to submit new descriptors.
551#[bitfield(u64)]
552#[derive(Inspect)]
553#[rustfmt::skip]
554pub struct IqtReg {
555 #[bits(4)]
556 _reserved1: u64,
557 /// Queue tail in 16-byte units (bits 18:4); bit 4 is reserved for IQA.DW=1.
558 #[bits(15)]
559 pub qt: u32,
560 #[bits(45)]
561 _reserved2: u64,
562}
563
564impl IqtReg {
565 /// Get the byte offset of the tail pointer.
566 pub fn tail_offset(&self) -> u64 {
567 (self.qt() as u64) << 4
568 }
569}
570
571/// Invalidation Queue Address Register (MMIO offset 0x090, 64-bit, RW). §11.4.9.3.
572///
573/// Holds the base address and size of the invalidation queue. Only writable
574/// when QIE=0.
575#[bitfield(u64)]
576#[derive(Inspect)]
577#[rustfmt::skip]
578pub struct IqaReg {
579 /// Queue size in 4KB pages = 2^QS, independent of descriptor width.
580 #[bits(3)]
581 pub qs: u8,
582 #[bits(8)]
583 _reserved: u64,
584 /// Descriptor width (bit 11): 0=128-bit, 1=256-bit.
585 /// 256-bit descriptors require scalable-mode or abort-DMA capability.
586 pub dw: bool,
587 /// Queue base address, bits [63:12]. 4KB-aligned.
588 #[bits(52)]
589 pub iqa: u64,
590}
591
592impl IqaReg {
593 /// Get the queue base physical address.
594 pub fn queue_base_address(&self) -> u64 {
595 self.iqa() << 12
596 }
597
598 /// Get the queue size in bytes: 4096 * 2^QS, for either descriptor width.
599 pub fn queue_size_bytes(&self) -> u64 {
600 4096u64 << self.qs()
601 }
602}
603
604/// Invalidation Completion Status Register (MMIO offset 0x09C, 32-bit, RW1C).
605/// §10.4.20.
606#[bitfield(u32)]
607#[derive(Inspect)]
608#[rustfmt::skip]
609pub struct IcsReg {
610 /// Invalidation Wait Descriptor Complete.
611 pub iwc: bool,
612 #[bits(31)]
613 _reserved: u32,
614}
615
616/// Invalidation Event Control Register (MMIO offset 0x0A0, 32-bit, RW).
617/// §10.4.21.
618///
619/// Controls invalidation completion event MSI signaling. Separate from the
620/// fault event registers (FECTL/FEDATA).
621#[bitfield(u32)]
622#[derive(Inspect)]
623#[rustfmt::skip]
624pub struct IectlReg {
625 #[bits(30)]
626 _reserved: u32,
627 /// Interrupt Pending (RO).
628 pub ip: bool,
629 /// Interrupt Mask — 1 = mask invalidation completion interrupt.
630 pub im: bool,
631}
632
633/// Interrupt Remapping Table Address Register (MMIO offset 0x0B8, 64-bit, RW).
634/// §10.4.29.
635///
636/// Holds the base address, size, and mode of the interrupt remapping table.
637/// Only writable when IRE=0.
638#[bitfield(u64)]
639#[derive(Inspect)]
640#[rustfmt::skip]
641pub struct IrtaReg {
642 /// Size: number of entries = 2^(S+1). 0=2, 15=65536.
643 #[bits(4)]
644 pub s: u8,
645 #[bits(7)]
646 _reserved: u64,
647 /// Extended Interrupt Mode Enable — 1 = x2APIC 32-bit destinations.
648 pub eime: bool,
649 /// IRT base address, bits [63:12]. 4KB-aligned.
650 #[bits(52)]
651 pub irta: u64,
652}
653
654impl IrtaReg {
655 /// Get the IRT base physical address.
656 pub fn irt_base_address(&self) -> u64 {
657 self.irta() << 12
658 }
659
660 /// Get the number of entries in the IRT: 2^(S+1).
661 pub fn entry_count(&self) -> u32 {
662 1u32 << (self.s() as u32 + 1)
663 }
664}
665
666/// IOTLB Invalidate Register (ECAP.IRO*16 + 0x08, 64-bit, RW). §10.4.16.
667///
668/// Register-based IOTLB invalidation (used before QI is enabled).
669#[bitfield(u64)]
670#[derive(Inspect)]
671#[rustfmt::skip]
672pub struct IotlbReg {
673 #[bits(32)]
674 _reserved1: u64,
675 /// Domain ID for domain-selective invalidation.
676 #[bits(16)]
677 pub did: u16,
678 /// Drain Writes.
679 pub dw: bool,
680 /// Drain Reads.
681 pub dr: bool,
682 #[bits(7)]
683 _reserved2: u64,
684 /// IOTLB Actual Invalidation Granularity (RO, set by HW).
685 #[bits(2)]
686 pub iaig: u8,
687 #[bits(1)]
688 _reserved3: u64,
689 /// IOTLB Invalidation Request Granularity.
690 /// 01=global, 10=domain, 11=page.
691 #[bits(2)]
692 pub iirg: u8,
693 #[bits(1)]
694 _reserved4: u64,
695 /// Invalidate IOTLB — set by SW, cleared by HW when done.
696 pub ivt: bool,
697}
698
699/// Fault Recording Register — high 64 bits (CAP.FRO*16 + 8). §11.4.7.6.
700///
701/// Contains fault metadata: source ID, fault reason, type, and the fault bit.
702#[bitfield(u64)]
703#[derive(Inspect)]
704#[rustfmt::skip]
705pub struct FrcdHi {
706 /// Source ID (BDF) of the faulting device.
707 #[bits(16)]
708 pub sid: u16,
709 // Includes T2=0 (no Page Request/AtomicOp reporting) and unsupported
710 // explicit-PASID fields PRIV, EXE, PP.
711 #[bits(16)]
712 _reserved1: u64,
713 /// Fault Reason code, full-register bits 103:96. See [`FaultReason`].
714 #[bits(8)]
715 pub fr: u8,
716 /// PASID value is reserved-zero without explicit PASID support.
717 #[bits(20)]
718 _reserved2: u64,
719 /// Address Type (2 bits).
720 #[bits(2)]
721 pub at: u8,
722 /// Type — 0 = write request, 1 = read request (or other non-write).
723 pub t: bool,
724 /// Fault — 1 = this record contains a valid fault. RW1C (write 1 to clear).
725 pub f: bool,
726}
727
728/// Fault Recording Register — low 64 bits (CAP.FRO*16). §11.4.7.6.
729///
730/// Contains the faulting address (bits 63:12) and reserved low bits.
731#[bitfield(u64)]
732#[derive(Inspect)]
733#[rustfmt::skip]
734pub struct FrcdLo {
735 #[bits(12)]
736 _reserved1: u64,
737 /// Fault Info — the faulting IOVA address, bits [63:12].
738 #[bits(52)]
739 pub fi: u64,
740}
741
742impl FrcdLo {
743 /// Get the full faulting address (bits 63:12 shifted).
744 pub fn fault_address(&self) -> u64 {
745 self.fi() << 12
746 }
747}
748
749// =============================================================================
750// Fault Reason Codes (§7.1.3, §5.1.4.1)
751// =============================================================================
752
753open_enum! {
754 /// VT-d fault reason codes.
755 #[derive(Inspect)]
756 #[inspect(debug)]
757 pub enum FaultReason: u8 {
758 // DMA remapping faults (§7.1.3)
759
760 /// Root entry not present (P=0).
761 ROOT_NOT_PRESENT = 0x01,
762 /// Context entry not present (P=0).
763 CONTEXT_NOT_PRESENT = 0x02,
764 /// Invalid context entry programming (reserved TT value).
765 INVALID_CONTEXT_ENTRY = 0x03,
766 /// Address beyond MGAW (IOVA overflow).
767 ADDRESS_BEYOND_MGAW = 0x04,
768 /// Write permission fault (W=0 in page table path).
769 WRITE_ACCESS_DENIED = 0x05,
770 /// Read permission fault (R=0 in page table path).
771 READ_ACCESS_DENIED = 0x06,
772 /// Error accessing second-level paging entry (GPA read failure).
773 SL_PTE_ACCESS_ERROR = 0x07,
774 /// Error accessing root entry (GPA read failure).
775 ROOT_ENTRY_ACCESS_ERROR = 0x08,
776 /// Error accessing context entry (GPA read failure).
777 CONTEXT_ENTRY_ACCESS_ERROR = 0x09,
778 /// Reserved bit set in root entry.
779 ROOT_ENTRY_RESERVED_BIT = 0x0A,
780 /// Reserved bit set in context entry.
781 CONTEXT_ENTRY_RESERVED_BIT = 0x0B,
782 /// Reserved bit set in second-level paging entry.
783 SL_PTE_RESERVED_BIT = 0x0C,
784 /// Context entry blocks translation/translated requests.
785 CONTEXT_ENTRY_TT_BLOCK = 0x0D,
786 /// Output address in interrupt address range.
787 OUTPUT_ADDR_IN_INTR_RANGE = 0x0E,
788
789 // Scalable translation faults (Table 26, §7.1.3). These definitions
790 // do not enable scalable translation or change legacy fault mappings.
791
792 /// RTA.1: invalid translation table mode.
793 INVALID_ROOT_TABLE_MODE = 0x30,
794 /// SRT.1: scalable root-entry access error.
795 SCALABLE_ROOT_ACCESS_ERROR = 0x38,
796 /// SRT.2: selected scalable root half not present.
797 SCALABLE_ROOT_NOT_PRESENT = 0x39,
798 /// SRT.3: reserved field in a present root half.
799 SCALABLE_ROOT_RESERVED_BIT = 0x3A,
800 /// SCT.1: scalable context-entry access error.
801 SCALABLE_CONTEXT_ACCESS_ERROR = 0x40,
802 /// SCT.2: scalable context not present.
803 SCALABLE_CONTEXT_NOT_PRESENT = 0x41,
804 /// SCT.3: reserved field in a present context.
805 SCALABLE_CONTEXT_RESERVED_BIT = 0x42,
806 /// SCT.4: invalid context programming, including RID_PASID > PDTS limit.
807 SCALABLE_INVALID_CONTEXT = 0x43,
808 /// SPD.1: PASID-directory access error.
809 PASID_DIRECTORY_ACCESS_ERROR = 0x50,
810 /// SPD.2: PASID-directory entry not present.
811 PASID_DIRECTORY_NOT_PRESENT = 0x51,
812 /// SPD.3: reserved field in a present PASID-directory entry.
813 PASID_DIRECTORY_RESERVED_BIT = 0x52,
814 /// SPT.1: PASID-table access error.
815 PASID_TABLE_ACCESS_ERROR = 0x58,
816 /// SPT.2: PASID-table entry not present.
817 PASID_TABLE_NOT_PRESENT = 0x59,
818 /// SPT.3: reserved field in a present PASID-table entry.
819 PASID_TABLE_RESERVED_BIT = 0x5A,
820 /// SPT.4: invalid PASID-table programming, including AW and PGTT.
821 PASID_TABLE_INVALID_ENTRY = 0x5B,
822 /// SSS.1: access error through a preceding second-stage paging entry.
823 SCALABLE_SL_PTE_ACCESS_ERROR = 0x78,
824 /// SSS.2: R=W=0 in an entry or in accumulated second-stage permissions.
825 SCALABLE_SL_PTE_NOT_PRESENT = 0x79,
826 /// SSS.3: reserved field in a present second-stage paging entry.
827 SCALABLE_SL_PTE_RESERVED_BIT = 0x7A,
828 /// SSS.4: access error through the PASID entry's SSPTPTR.
829 SCALABLE_SL_ROOT_ACCESS_ERROR = 0x7B,
830 /// SSS.5: an A/D update is needed but page walks do not snoop caches.
831 SL_AD_UPDATE_NON_SNOOP = 0x7C,
832 /// SSS.6: hardware failed to atomically update a second-stage A/D bit.
833 SL_AD_UPDATE_FAILED = 0x7D,
834 /// SGN.4: host input address exceeds HAW.
835 SCALABLE_ADDRESS_BEYOND_HAW = 0x83,
836 /// SGN.5: second-stage input address exceeds min(MGAW, AGAW).
837 SCALABLE_ADDRESS_BEYOND_AGAW = 0x84,
838 /// SGN.6: write permission fault in scalable translation.
839 SCALABLE_WRITE_ACCESS_DENIED = 0x85,
840 /// SGN.7: read permission fault in scalable second-stage translation.
841 SCALABLE_READ_ACCESS_DENIED = 0x86,
842 /// SGN.8: scalable translation output is in the interrupt address range.
843 SCALABLE_OUTPUT_ADDR_IN_INTR_RANGE = 0x87,
844
845 // Interrupt remapping faults (§5.1.4.1)
846
847 /// Reserved field set in remappable interrupt request.
848 IR_RESERVED_FIELD = 0x20,
849 /// Interrupt index exceeds IRT size.
850 IR_INDEX_EXCEEDS_SIZE = 0x21,
851 /// IRTE not present (P=0).
852 IRTE_NOT_PRESENT = 0x22,
853 /// Error accessing IRTE (GPA read failure).
854 IRTE_ACCESS_ERROR = 0x23,
855 /// Reserved field set in present IRTE.
856 IRTE_RESERVED_FIELD = 0x24,
857 /// Compatibility-format interrupt blocked.
858 COMPAT_FORMAT_BLOCKED = 0x25,
859 /// Source-ID verification failure.
860 SOURCE_ID_VERIFICATION_FAIL = 0x26,
861 }
862}
863
864#[cfg(test)]
865mod tests {
866 use super::*;
867 use test_with_tracing::test;
868
869 #[test]
870 fn test_gcmd_gsts_bits_aligned() {
871 // GCMD and GSTS must have matching bit positions for the status bits.
872 let gcmd = GcmdReg::new()
873 .with_te(true)
874 .with_srtp(true)
875 .with_qie(true)
876 .with_ire(true)
877 .with_sirtp(true)
878 .with_cfi(true)
879 .with_wbf(true);
880
881 let gsts = GstsReg::new()
882 .with_tes(true)
883 .with_rtps(true)
884 .with_qies(true)
885 .with_ires(true)
886 .with_irtps(true)
887 .with_cfis(true)
888 .with_wbfs(true);
889
890 // The bit positions should match between command and status.
891 let gcmd_val = u32::from(gcmd);
892 let gsts_val = u32::from(gsts);
893 assert_eq!(gcmd_val, gsts_val);
894 }
895
896 #[test]
897 fn test_iqa_queue_size() {
898 for qs in 0..=7 {
899 for dw in [false, true] {
900 let raw = 0x1234_5678_9000 | ((dw as u64) << 11) | qs;
901 let iqa = IqaReg::from(raw);
902 assert_eq!(iqa.qs(), qs as u8);
903 assert_eq!(iqa.dw(), dw);
904 assert_eq!(iqa.queue_base_address(), 0x1234_5678_9000);
905 assert_eq!(iqa.queue_size_bytes(), 4096 << qs);
906 }
907 }
908 assert_eq!(IqaReg::new().with_dw(true).into_bits(), 0x800);
909 for bit in 3..=10 {
910 let iqa = IqaReg::from(1 << bit);
911 assert!(!iqa.dw());
912 assert_eq!(iqa.qs(), 0);
913 assert_eq!(iqa.queue_base_address(), 0);
914 }
915 }
916
917 #[test]
918 fn test_frcd_literal_layout() {
919 let raw = (1 << 63) | (1 << 62) | (2 << 60) | (0x7D << 32) | 0xFEDC;
920 let frcd = FrcdHi::from(raw);
921 assert_eq!(frcd.sid(), 0xFEDC);
922 assert_eq!(frcd.fr(), 0x7D);
923 assert_eq!(frcd.at(), 2);
924 assert!(frcd.t());
925 assert!(frcd.f());
926 assert_eq!(
927 FrcdHi::new()
928 .with_sid(0xFEDC)
929 .with_fr(0x7D)
930 .with_at(2)
931 .with_t(true)
932 .with_f(true)
933 .into_bits(),
934 raw
935 );
936 assert_eq!(FrcdHi::from(0xFF << 48).fr(), 0);
937 }
938
939 #[test]
940 fn test_translation_table_mode_encodings() {
941 assert_eq!(TranslationTableMode::LEGACY.0, 0);
942 assert_eq!(TranslationTableMode::SCALABLE.0, 1);
943 assert_eq!(TranslationTableMode::ABORT_DMA.0, 3);
944 for mode in 0..=3 {
945 let rtaddr = RtaddrReg::from(0x1234_5000 | ((mode as u64) << 10));
946 assert_eq!(TranslationTableMode(rtaddr.ttm()).0, mode);
947 assert_eq!(rtaddr.root_table_address(), 0x1234_5000);
948 }
949 }
950
951 #[test]
952 fn test_scalable_fault_reason_codes() {
953 // Independent literal encodings from §7.1.3, Table 26.
954 for (reason, expected) in [
955 (FaultReason::INVALID_ROOT_TABLE_MODE, 0x30),
956 (FaultReason::SCALABLE_ROOT_ACCESS_ERROR, 0x38),
957 (FaultReason::SCALABLE_ROOT_NOT_PRESENT, 0x39),
958 (FaultReason::SCALABLE_ROOT_RESERVED_BIT, 0x3a),
959 (FaultReason::SCALABLE_CONTEXT_ACCESS_ERROR, 0x40),
960 (FaultReason::SCALABLE_CONTEXT_NOT_PRESENT, 0x41),
961 (FaultReason::SCALABLE_CONTEXT_RESERVED_BIT, 0x42),
962 (FaultReason::SCALABLE_INVALID_CONTEXT, 0x43),
963 (FaultReason::PASID_DIRECTORY_ACCESS_ERROR, 0x50),
964 (FaultReason::PASID_DIRECTORY_NOT_PRESENT, 0x51),
965 (FaultReason::PASID_DIRECTORY_RESERVED_BIT, 0x52),
966 (FaultReason::PASID_TABLE_ACCESS_ERROR, 0x58),
967 (FaultReason::PASID_TABLE_NOT_PRESENT, 0x59),
968 (FaultReason::PASID_TABLE_RESERVED_BIT, 0x5a),
969 (FaultReason::PASID_TABLE_INVALID_ENTRY, 0x5b),
970 (FaultReason::SCALABLE_SL_PTE_ACCESS_ERROR, 0x78),
971 (FaultReason::SCALABLE_SL_PTE_NOT_PRESENT, 0x79),
972 (FaultReason::SCALABLE_SL_PTE_RESERVED_BIT, 0x7a),
973 (FaultReason::SCALABLE_SL_ROOT_ACCESS_ERROR, 0x7b),
974 (FaultReason::SL_AD_UPDATE_NON_SNOOP, 0x7c),
975 (FaultReason::SL_AD_UPDATE_FAILED, 0x7d),
976 (FaultReason::SCALABLE_ADDRESS_BEYOND_HAW, 0x83),
977 (FaultReason::SCALABLE_ADDRESS_BEYOND_AGAW, 0x84),
978 (FaultReason::SCALABLE_WRITE_ACCESS_DENIED, 0x85),
979 (FaultReason::SCALABLE_READ_ACCESS_DENIED, 0x86),
980 (FaultReason::SCALABLE_OUTPUT_ADDR_IN_INTR_RANGE, 0x87),
981 ] {
982 assert_eq!(reason.0, expected);
983 }
984 assert_eq!(FaultReason(0xff).0, 0xff);
985 }
986
987 #[test]
988 fn test_irta_entry_count() {
989 assert_eq!(IrtaReg::new().with_s(0).entry_count(), 2);
990 assert_eq!(IrtaReg::new().with_s(15).entry_count(), 65536);
991 }
992
993 #[test]
994 fn test_iqh_iqt_offsets() {
995 assert_eq!(IqhReg::new().with_qh(16).head_offset(), 256);
996 assert_eq!(IqtReg::new().with_qt(32).tail_offset(), 512);
997 }
998}