1use super::Table;
15use crate::packed_nums::*;
16use bitfield_struct::bitfield;
17use core::mem::size_of;
18use static_assertions::const_assert_eq;
19use zerocopy::FromBytes;
20use zerocopy::Immutable;
21use zerocopy::IntoBytes;
22use zerocopy::KnownLayout;
23use zerocopy::Unaligned;
24
25pub const IVRS_REVISION: u8 = 2;
27
28pub const IVHD_TYPE_10: u8 = 0x10;
30
31pub const IVHD_TYPE_11: u8 = 0x11;
33
34pub const IVHD_TYPE_40: u8 = 0x40;
36
37pub const IVHD_DEV_ALL: u8 = 0x01;
39
40pub const IVHD_DEV_SELECT: u8 = 0x02;
42
43pub const IVHD_DEV_RANGE_START: u8 = 0x03;
45
46pub const IVHD_DEV_RANGE_END: u8 = 0x04;
48
49pub const IVHD_DEV_SPECIAL: u8 = 0x48;
54
55pub const IVHD_SPECIAL_IOAPIC: u8 = 0x01;
57
58pub const IVHD_SPECIAL_HPET: u8 = 0x02;
60
61pub const IVHD_DTE_SETTING_INIT_PASS: u8 = 0x01;
63pub const IVHD_DTE_SETTING_EINT_PASS: u8 = 0x02;
64pub const IVHD_DTE_SETTING_NMI_PASS: u8 = 0x04;
65
66#[bitfield(u32)]
68pub struct IvInfo {
69 pub efr_sup: bool,
71 pub dma_remap_sup: bool,
73 #[bits(3)]
75 _reserved1: u32,
76 #[bits(3)]
79 pub gva_size: u8,
80 #[bits(7)]
83 pub pa_size: u8,
84 #[bits(7)]
87 pub va_size: u8,
88 pub ht_ats_resv: bool,
90 #[bits(9)]
92 _reserved2: u32,
93}
94
95#[repr(C)]
102#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, Unaligned)]
103pub struct Ivrs {
104 pub iv_info: u32_ne,
106 pub reserved: [u8; 8],
108}
109
110impl Ivrs {
111 pub fn new(iv_info: u32) -> Self {
113 Self {
114 iv_info: iv_info.into(),
115 reserved: [0; 8],
116 }
117 }
118}
119
120impl Table for Ivrs {
121 const SIGNATURE: [u8; 4] = *b"IVRS";
122}
123
124const_assert_eq!(size_of::<Ivrs>(), 12);
125
126#[repr(C)]
143#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, Unaligned)]
144pub struct IvhdType11 {
145 pub ivhd_type: u8,
147 pub flags: u8,
149 pub length: u16_ne,
151 pub device_id: u16_ne,
153 pub capability_offset: u16_ne,
155 pub iommu_base_address: u64_ne,
157 pub pci_segment: u16_ne,
159 pub iommu_info: u16_ne,
161 pub iommu_attributes: u32_ne,
164 pub efr_register: u64_ne,
166 pub efr_register2: u64_ne,
168}
169
170impl IvhdType11 {
171 pub fn new(
173 device_id: u16,
174 capability_offset: u16,
175 iommu_base_address: u64,
176 pci_segment: u16,
177 efr: u64,
178 ) -> Self {
179 Self {
180 ivhd_type: IVHD_TYPE_11,
181 flags: 0,
182 length: (size_of::<Self>() as u16).into(),
183 device_id: device_id.into(),
184 capability_offset: capability_offset.into(),
185 iommu_base_address: iommu_base_address.into(),
186 pci_segment: pci_segment.into(),
187 iommu_info: 0.into(),
188 iommu_attributes: 0.into(),
189 efr_register: efr.into(),
190 efr_register2: 0.into(),
191 }
192 }
193
194 pub fn with_length(mut self, length: u16) -> Self {
196 self.length = length.into();
197 self
198 }
199
200 pub fn with_flags(mut self, flags: u8) -> Self {
202 self.flags = flags;
203 self
204 }
205}
206
207const_assert_eq!(size_of::<IvhdType11>(), 40);
208
209#[repr(C)]
214#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, Unaligned)]
215pub struct IvhdDeviceEntry4 {
216 pub entry_type: u8,
218 pub device_id: u16_ne,
220 pub dte_setting: u8,
229}
230
231impl IvhdDeviceEntry4 {
232 pub fn all(dte_setting: u8) -> Self {
234 Self {
235 entry_type: IVHD_DEV_ALL,
236 device_id: 0.into(),
237 dte_setting,
238 }
239 }
240
241 pub fn select(device_id: u16, dte_setting: u8) -> Self {
243 Self {
244 entry_type: IVHD_DEV_SELECT,
245 device_id: device_id.into(),
246 dte_setting,
247 }
248 }
249
250 pub fn range_start(device_id: u16, dte_setting: u8) -> Self {
252 Self {
253 entry_type: IVHD_DEV_RANGE_START,
254 device_id: device_id.into(),
255 dte_setting,
256 }
257 }
258
259 pub fn range_end(device_id: u16) -> Self {
261 Self {
262 entry_type: IVHD_DEV_RANGE_END,
263 device_id: device_id.into(),
264 dte_setting: 0,
265 }
266 }
267}
268
269const_assert_eq!(size_of::<IvhdDeviceEntry4>(), 4);
270
271#[repr(C)]
278#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, Unaligned)]
279pub struct IvhdSpecialDeviceEntry8 {
280 pub entry_type: u8,
282 _reserved: u16_ne,
284 pub dte_setting: u8,
286 pub handle: u8,
290 pub source_device_id: u16_ne,
293 pub variety: u8,
295}
296
297impl IvhdSpecialDeviceEntry8 {
298 pub fn ioapic(rid: u16, ioapic_id: u8) -> Self {
303 Self {
304 entry_type: IVHD_DEV_SPECIAL,
305 _reserved: 0.into(),
306 dte_setting: 0,
307 handle: ioapic_id,
308 source_device_id: rid.into(),
309 variety: IVHD_SPECIAL_IOAPIC,
310 }
311 }
312}
313
314const_assert_eq!(size_of::<IvhdSpecialDeviceEntry8>(), 8);
315
316#[cfg(test)]
317mod tests {
318 extern crate alloc;
319
320 use super::*;
321 use alloc::vec::Vec;
322 use zerocopy::IntoBytes;
323
324 #[test]
325 fn test_ivrs_header() {
326 let ivrs = Ivrs::new(0x0040_3000);
327 assert_eq!(ivrs.iv_info.get(), 0x0040_3000);
328 assert_eq!(ivrs.reserved, [0; 8]);
329 let bytes = ivrs.as_bytes();
330 assert_eq!(bytes.len(), 12);
331 }
332
333 #[test]
334 fn test_iv_info_bitfield() {
335 let iv_info = IvInfo::new()
336 .with_efr_sup(true)
337 .with_pa_size(48)
338 .with_va_size(48);
339 let raw = u32::from(iv_info);
340 assert_eq!(raw & 1, 1); assert_eq!((raw >> 8) & 0x7F, 48); assert_eq!((raw >> 15) & 0x7F, 48); }
344
345 #[test]
346 fn test_ivrs_signature() {
347 assert_eq!(Ivrs::SIGNATURE, *b"IVRS");
348 }
349
350 #[test]
351 fn test_ivhd_type11_defaults() {
352 let ivhd = IvhdType11::new(0x0002, 0x40, 0xFD00_0000, 0, 0xC0);
353 assert_eq!(ivhd.ivhd_type, IVHD_TYPE_11);
354 assert_eq!(ivhd.flags, 0);
355 assert_eq!(ivhd.length.get(), size_of::<IvhdType11>() as u16);
356 assert_eq!(ivhd.device_id.get(), 0x0002);
357 assert_eq!(ivhd.capability_offset.get(), 0x40);
358 assert_eq!(ivhd.iommu_base_address.get(), 0xFD00_0000);
359 assert_eq!(ivhd.pci_segment.get(), 0);
360 assert_eq!(ivhd.iommu_info.get(), 0);
361 assert_eq!(ivhd.iommu_attributes.get(), 0);
362 assert_eq!(ivhd.efr_register.get(), 0xC0);
363 assert_eq!(ivhd.efr_register2.get(), 0);
364 }
365
366 #[test]
367 fn test_ivhd_type11_with_length() {
368 let ivhd = IvhdType11::new(0x0002, 0x40, 0xFD00_0000, 0, 0).with_length(48);
369 assert_eq!(ivhd.length.get(), 48);
370 }
371
372 #[test]
373 fn test_ivhd_device_entry_all() {
374 let entry = IvhdDeviceEntry4::all(0);
375 assert_eq!(entry.entry_type, IVHD_DEV_ALL);
376 assert_eq!(entry.device_id.get(), 0);
377 assert_eq!(entry.dte_setting, 0);
378 }
379
380 #[test]
381 fn test_ivhd_device_entry_select() {
382 let entry = IvhdDeviceEntry4::select(0x0108, 0x07);
383 assert_eq!(entry.entry_type, IVHD_DEV_SELECT);
384 assert_eq!(entry.device_id.get(), 0x0108);
385 assert_eq!(entry.dte_setting, 0x07);
386 }
387
388 #[test]
389 fn test_ivhd_device_entry_range() {
390 let start = IvhdDeviceEntry4::range_start(0x0001, 0);
391 let end = IvhdDeviceEntry4::range_end(0xFFFF);
392 assert_eq!(start.entry_type, IVHD_DEV_RANGE_START);
393 assert_eq!(start.device_id.get(), 0x0001);
394 assert_eq!(end.entry_type, IVHD_DEV_RANGE_END);
395 assert_eq!(end.device_id.get(), 0xFFFF);
396 }
397
398 #[test]
399 fn test_ivhd_device_entry_size() {
400 assert_eq!(size_of::<IvhdDeviceEntry4>(), 4);
401 }
402
403 #[test]
404 fn test_ivrs_round_trip() {
405 let ivrs = Ivrs::new(0x0040_3000);
407 let dev_entries_size = 2 * size_of::<IvhdDeviceEntry4>() as u16;
408 let ivhd_total = size_of::<IvhdType11>() as u16 + dev_entries_size;
409 let ivhd = IvhdType11::new(0x0002, 0x40, 0xFD00_0000, 0, 0).with_length(ivhd_total);
410
411 let range_start = IvhdDeviceEntry4::range_start(0x0001, 0);
412 let range_end = IvhdDeviceEntry4::range_end(0xFFFF);
413
414 let mut buf = Vec::new();
416 buf.extend_from_slice(ivrs.as_bytes());
417 buf.extend_from_slice(ivhd.as_bytes());
418 buf.extend_from_slice(range_start.as_bytes());
419 buf.extend_from_slice(range_end.as_bytes());
420
421 assert_eq!(buf.len(), 60);
426
427 assert_eq!(buf[12], IVHD_TYPE_11);
429 assert_eq!(buf[52], IVHD_DEV_RANGE_START);
431 assert_eq!(buf[56], IVHD_DEV_RANGE_END);
432 }
433}