acpi_spec/dmar.rs
1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! DMAR (DMA Remapping Reporting Structure) types for Intel VT-d discovery.
5//!
6//! The DMAR ACPI table describes Intel VT-d remapping hardware to the guest
7//! OS. It contains one or more DRHD (DMA Remapping Hardware Unit Definition)
8//! structures, each describing a single VT-d remapping unit: its MMIO base,
9//! PCI segment, and the set of devices behind it via device scope entries.
10//!
11//! Reference: Intel Virtualization Technology for Directed I/O Architecture
12//! Specification, Document 774206, §8.
13
14use super::Table;
15use crate::packed_nums::*;
16use core::mem::size_of;
17use static_assertions::const_assert_eq;
18use zerocopy::FromBytes;
19use zerocopy::Immutable;
20use zerocopy::IntoBytes;
21use zerocopy::KnownLayout;
22use zerocopy::Unaligned;
23
24/// DMAR table revision (VT-d spec §8.1).
25pub const DMAR_REVISION: u8 = 1;
26
27/// DMAR flags: interrupt remapping supported (§8.1, Table 8-1).
28pub const DMAR_FLAGS_INTR_REMAP: u8 = 0x01;
29
30/// DRHD structure type (§8.2).
31pub const DMAR_TYPE_DRHD: u16 = 0x0000;
32
33/// DRHD flags: INCLUDE_PCI_ALL (§8.2).
34pub const DRHD_FLAGS_INCLUDE_PCI_ALL: u8 = 0x01;
35
36/// Device scope entry type: PCI endpoint device (§8.3.1, Table 8-5).
37pub const DEVICE_SCOPE_PCI_ENDPOINT: u8 = 0x01;
38
39/// Device scope entry type: PCI sub-hierarchy (§8.3.1, Table 8-5).
40pub const DEVICE_SCOPE_PCI_SUB_HIERARCHY: u8 = 0x02;
41
42/// Device scope entry type: IOAPIC (§8.3.1, Table 8-5).
43pub const DEVICE_SCOPE_IOAPIC: u8 = 0x03;
44
45/// Device scope entry type: HPET (§8.3.1, Table 8-5).
46pub const DEVICE_SCOPE_HPET: u8 = 0x04;
47
48/// DMAR fixed table header (follows the standard ACPI `Header`).
49///
50/// The DMAR table starts with the standard 36-byte ACPI header, followed by
51/// this 12-byte structure (HAW + flags + reserved), followed by one or more
52/// remapping structures (DRHD, RMRR, etc.).
53///
54/// Reference: VT-d spec §8.1, Table 8-1.
55#[repr(C)]
56#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, Unaligned)]
57pub struct Dmar {
58 /// Host Address Width: maximum DMA physical addressability.
59 /// Value is N where address width = N + 1. E.g., 0x2F = 48-bit.
60 pub host_address_width: u8,
61 /// Flags (see `DMAR_FLAGS_*` constants).
62 pub flags: u8,
63 /// Reserved, must be zero.
64 pub reserved: [u8; 10],
65}
66
67impl Dmar {
68 /// Create a new DMAR header with the given host address width and flags.
69 pub fn new(host_address_width: u8, flags: u8) -> Self {
70 Self {
71 host_address_width,
72 flags,
73 reserved: [0; 10],
74 }
75 }
76}
77
78impl Table for Dmar {
79 const SIGNATURE: [u8; 4] = *b"DMAR";
80}
81
82const_assert_eq!(size_of::<Dmar>(), 12);
83
84/// DRHD (DMA Remapping Hardware Unit Definition) structure (§8.2).
85///
86/// Describes a single VT-d remapping unit. The structure is followed by
87/// zero or more device scope entries.
88#[repr(C)]
89#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, Unaligned)]
90pub struct DmarDrhd {
91 /// Structure type: always [`DMAR_TYPE_DRHD`].
92 pub structure_type: u16_ne,
93 /// Length of the entire DRHD structure including device scope entries.
94 pub length: u16_ne,
95 /// Flags (see `DRHD_FLAGS_*` constants).
96 pub flags: u8,
97 /// Size: register set size as 2^N 4KB pages. 0 = 1 page (4KB).
98 pub size: u8,
99 /// PCI segment number.
100 pub segment_number: u16_ne,
101 /// Register base address of the remapping unit (MMIO base).
102 pub register_base_address: u64_ne,
103}
104
105impl DmarDrhd {
106 /// Create a new DRHD structure.
107 pub fn new(flags: u8, segment_number: u16, register_base_address: u64) -> Self {
108 Self {
109 structure_type: DMAR_TYPE_DRHD.into(),
110 length: (size_of::<Self>() as u16).into(),
111 flags,
112 size: 0, // 1 page (4KB)
113 segment_number: segment_number.into(),
114 register_base_address: register_base_address.into(),
115 }
116 }
117
118 /// Set the total length (header + device scope entries).
119 pub fn with_length(mut self, length: u16) -> Self {
120 self.length = length.into();
121 self
122 }
123}
124
125const_assert_eq!(size_of::<DmarDrhd>(), 16);
126
127/// Device Scope entry (§8.3.1).
128///
129/// A device scope entry identifies a PCI device or sub-hierarchy covered
130/// by the parent DRHD. The entry is followed by one or more `DmarDevicePath`
131/// entries describing the PCI path from the host bridge to the device.
132#[repr(C)]
133#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, Unaligned)]
134pub struct DmarDeviceScope {
135 /// Device scope type (see `DEVICE_SCOPE_*` constants).
136 pub device_scope_type: u8,
137 /// Length of this device scope entry including path entries.
138 pub length: u8,
139 /// Reserved, must be zero.
140 pub reserved: u16_ne,
141 /// Enumeration ID. For IOAPIC/HPET scopes, this is the I/O APIC ID
142 /// or HPET number. For PCI scopes, must be 0.
143 pub enumeration_id: u8,
144 /// Start bus number for PCI sub-hierarchy scopes.
145 pub start_bus_number: u8,
146}
147
148impl DmarDeviceScope {
149 /// Create a device scope entry with the given type and start bus number.
150 ///
151 /// The entry is sized for a single path entry (one `DmarDevicePath`).
152 pub fn new(device_scope_type: u8, start_bus_number: u8) -> Self {
153 Self {
154 device_scope_type,
155 // Length = 6 (header) + 2 (one path entry)
156 length: (size_of::<Self>() + size_of::<DmarDevicePath>()) as u8,
157 reserved: 0.into(),
158 enumeration_id: 0,
159 start_bus_number,
160 }
161 }
162}
163
164const_assert_eq!(size_of::<DmarDeviceScope>(), 6);
165
166/// Device path entry within a device scope (§8.3.1).
167///
168/// Each path entry is a (device, function) pair describing one hop from
169/// the start bus to the target device.
170#[repr(C)]
171#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, Unaligned)]
172pub struct DmarDevicePath {
173 /// PCI device number.
174 pub device: u8,
175 /// PCI function number.
176 pub function: u8,
177}
178
179const_assert_eq!(size_of::<DmarDevicePath>(), 2);