1use super::sdt::Sdt;
34use zerocopy::FromBytes;
35
36pub const IVRS_SIGNATURE: &[u8; 4] = b"IVRS";
38
39#[derive(Clone, Copy, Debug, FromBytes)]
43#[repr(C, packed)]
44pub struct IvrsHeader {
45 pub header: Sdt,
46 pub ivinfo: u16,
48 pub _reserved1: u8,
49 pub flags: u8,
51 pub total_length: u16,
53 pub dev_entry_count: u16,
55 pub entry_offset: u16,
57}
58
59impl IvrsHeader {
60 pub fn get() -> Option<&'static Self> {
62 unsafe { super::find_table(IVRS_SIGNATURE).map(|ptr| &*(ptr as *const Self)) }
63 }
64
65 pub fn has_draint(&self) -> bool {
67 self.flags & 0x01 != 0
68 }
69
70 pub fn is_coherent(&self) -> bool {
72 self.flags & 0x02 != 0
73 }
74
75 pub fn ivhd_ivmd_count(&self) -> usize {
77 0
79 }
80
81 pub fn entries(&self) -> IvrsEntryIter<'_> {
83 IvrsEntryIter {
84 base: self as *const Self as *const u8,
85 offset: self.entry_offset as usize,
86 total_len: self.header.length as usize,
87 _phantom: core::marker::PhantomData,
88 }
89 }
90}
91
92#[derive(Clone, Copy, Debug, PartialEq, Eq)]
94pub enum IvhdType {
95 LegacyV10 = 0x10,
97 ExtendedV11 = 0x11,
99}
100
101#[derive(Clone, Copy, Debug)]
103pub struct IvhdFlags(u8);
104
105impl IvhdFlags {
106 pub fn iotlb_support(&self) -> bool {
107 self.0 & 0x01 != 0
108 }
109 pub fn prefetch_support(&self) -> bool {
110 self.0 & 0x02 != 0
111 }
112 pub fn ppr_support(&self) -> bool {
113 self.0 & 0x04 != 0
114 }
115}
116
117#[derive(Clone, Debug)]
121pub struct IvhdBlock {
122 pub typ: IvhdType,
124 pub flags: IvhdFlags,
126 pub length: u16,
128 pub device_id: u16,
130 pub capability_offset: u16,
132 pub ioapic_id: u8,
134 pub iommu_base: u64,
136 pub pci_segment: u16,
138 pub mmio_base: Option<u64>,
140 device_entries_off: u64,
142 device_entries_len: usize,
144}
145
146#[derive(Clone, Copy, Debug, FromBytes)]
148#[repr(C, packed)]
149struct RawIvhdCommon {
150 typ: u8,
151 flags: u8,
152 length: u16,
153 device_id: u16,
154 capability_offset: u16,
155 ioapic_id: u8,
156 _reserved: u8,
157 iommu_base: u64,
158 pci_segment: u16,
159}
160
161#[derive(Clone, Copy, Debug, FromBytes)]
163#[repr(C, packed)]
164struct RawIvhdTail11 {
165 mmio_base: u64,
166}
167
168#[derive(Clone, Copy, Debug)]
174pub struct IvmdBlock {
175 pub typ: u8,
177 pub flags: u8,
179 pub length: u16,
181 pub device_id: u16,
183 pub pci_segment: u16,
185 pub iommu_base: u64,
187 pub start_addr: u64,
189 pub length_bytes: u64,
191}
192
193#[derive(Clone, Copy, Debug, FromBytes)]
195#[repr(C, packed)]
196struct RawIvmd {
197 typ: u8,
198 flags: u8,
199 length: u16,
200 device_id: u16,
201 pci_segment: u16,
202 iommu_base: u64,
203 start_addr: u64,
204 length_bytes: u64,
205}
206
207#[derive(Clone, Copy, Debug)]
212pub struct IvhdDeviceEntry {
213 pub raw_type: u8,
215 pub device_id: u16,
217 pub data: [u8; 4],
219 pub ext_data: Option<u16>,
221 pub is_range: bool,
223 pub range_count: u8,
225}
226
227#[derive(Clone, Copy, Debug, FromBytes)]
229#[repr(C, packed)]
230struct RawDevEntryHdr {
231 raw_type: u8,
232 device_id: u16,
233}
234
235pub struct IvrsEntryIter<'a> {
237 base: *const u8,
238 offset: usize,
239 total_len: usize,
240 _phantom: core::marker::PhantomData<&'a ()>,
241}
242
243impl<'a> IvrsEntryIter<'a> {
244 pub fn next_block(&mut self) -> Option<IvrsBlock> {
246 if self.offset >= self.total_len {
247 return None;
248 }
249 let ptr = unsafe { self.base.add(self.offset) };
250 let typ = unsafe { *ptr };
251
252 match typ {
253 0x10 | 0x11 => {
254 let common = unsafe { &*(ptr as *const RawIvhdCommon) };
257 let block_len = common.length as usize;
258
259 const IVHD10_HEADER_SIZE: usize = 24;
263 const IVHD11_HEADER_SIZE: usize = 32;
264 let actual_hdr_len = if typ == 0x10 {
265 IVHD10_HEADER_SIZE
266 } else {
267 IVHD11_HEADER_SIZE
268 };
269
270 let entry_off = self.offset + actual_hdr_len;
271 let entry_len = block_len.saturating_sub(actual_hdr_len);
272
273 let mmio = if typ == 0x11 {
274 let tail = unsafe { &*(ptr.add(IVHD10_HEADER_SIZE) as *const RawIvhdTail11) };
276 Some(tail.mmio_base)
277 } else {
278 None
279 };
280
281 let block = IvrsBlock::Ivhd(IvhdBlock {
282 typ: if typ == 0x10 {
283 IvhdType::LegacyV10
284 } else {
285 IvhdType::ExtendedV11
286 },
287 flags: IvhdFlags(common.flags),
288 length: common.length,
289 device_id: common.device_id,
290 capability_offset: common.capability_offset,
291 ioapic_id: common.ioapic_id,
292 iommu_base: common.iommu_base,
293 pci_segment: common.pci_segment,
294 mmio_base: mmio,
295 device_entries_off: entry_off as u64,
296 device_entries_len: entry_len,
297 });
298
299 self.offset += block_len;
300 Some(block)
301 }
302 0x20 | 0x22 => {
303 let raw = unsafe { &*(ptr as *const RawIvmd) };
304 let block = IvrsBlock::Ivmd(IvmdBlock {
305 typ: raw.typ,
306 flags: raw.flags,
307 length: raw.length,
308 device_id: raw.device_id,
309 pci_segment: raw.pci_segment,
310 iommu_base: raw.iommu_base,
311 start_addr: raw.start_addr,
312 length_bytes: raw.length_bytes,
313 });
314 self.offset += raw.length as usize;
315 Some(block)
316 }
317 _ => {
318 self.offset += 4;
320 self.next_block()
321 }
322 }
323 }
324}
325
326#[derive(Clone, Debug)]
329pub enum IvrsBlock {
330 Ivhd(IvhdBlock),
332 Ivmd(IvmdBlock),
334}
335
336impl IvhdBlock {
337 pub fn device_entries(&self) -> IvhdDevEntryIter<'_> {
339 IvhdDevEntryIter {
340 base: self.device_entries_off as *const u8,
341 remaining: self.device_entries_len,
342 _phantom: core::marker::PhantomData,
343 }
344 }
345
346 pub fn summary(&self) -> alloc::string::String {
348 use alloc::fmt::Write;
349 let mut s = alloc::string::String::new();
350 let _ = write!(
351 s,
352 "IVHD type={:?} flags={:#04x} dev_id={:#06x} cap_off={:#06x} ioapic={} iommu_base={:#018x} seg={}",
353 self.typ,
354 self.flags.0,
355 self.device_id,
356 self.capability_offset,
357 self.ioapic_id,
358 self.iommu_base,
359 self.pci_segment,
360 );
361 if let Some(mmio) = self.mmio_base {
362 let _ = write!(s, " mmio_base={:#018x}", mmio);
363 }
364 s
365 }
366}
367
368pub struct IvhdDevEntryIter<'a> {
370 base: *const u8,
371 remaining: usize,
372 _phantom: core::marker::PhantomData<&'a ()>,
373}
374
375impl<'a> IvhdDevEntryIter<'a> {
376 pub fn next_entry(&mut self) -> Option<IvhdDeviceEntry> {
378 if self.remaining < 1 {
379 return None;
380 }
381
382 let ptr = self.base;
383 let raw_type = unsafe { *ptr };
384 let hdr = unsafe { &*(ptr as *const RawDevEntryHdr) };
385
386 let (entry_size, is_range, range_count, ext_data) =
388 decode_dev_entry_type(raw_type, hdr, ptr);
389 if entry_size == 0 || entry_size > self.remaining {
390 return None;
391 }
392
393 let data = [
394 unsafe { *ptr.add(4) },
395 unsafe { *ptr.add(5) },
396 unsafe { *ptr.add(6) },
397 unsafe { *ptr.add(7) },
398 ];
399
400 let entry = IvhdDeviceEntry {
401 raw_type,
402 device_id: hdr.device_id,
403 data,
404 ext_data,
405 is_range,
406 range_count,
407 };
408
409 self.base = unsafe { self.base.add(entry_size) };
410 self.remaining -= entry_size;
411 Some(entry)
412 }
413
414 pub fn collect(&mut self) -> alloc::vec::Vec<IvhdDeviceEntry> {
416 let mut entries = alloc::vec::Vec::new();
417 while let Some(e) = self.next_entry() {
418 entries.push(e);
419 }
420 entries
421 }
422}
423
424fn decode_dev_entry_type(
429 raw_type: u8,
430 hdr: &RawDevEntryHdr,
431 ptr: *const u8,
432) -> (usize, bool, u8, Option<u16>) {
433 match raw_type {
434 0x00 | 0x01 => (4, false, 0, None),
436 0x02 | 0x03 => (4, false, 0, None),
437 0x04 | 0x05 | 0x06 | 0x07 => {
439 let count = unsafe { *ptr.add(4) }; (8, true, count, None)
441 }
442 0x20 | 0x21 | 0x22 | 0x23 => (8, false, 0, None),
444 0x40 | 0x41 | 0x42 | 0x43 => {
446 let alias = unsafe { core::ptr::read_unaligned(ptr.add(4) as *const u16) };
447 (8, false, 0, Some(alias))
448 }
449 0x50 | 0x51 | 0x52 | 0x53 => (8, false, 0, None),
451 0x60 | 0x61 | 0x62 | 0x63 => {
453 let ext = unsafe { core::ptr::read_unaligned(ptr.add(4) as *const u16) };
454 (8, false, 0, Some(ext))
455 }
456 0x70 | 0x71 | 0x72 | 0x73 => {
458 let ext = unsafe { core::ptr::read_unaligned(ptr.add(4) as *const u16) };
459 let count = unsafe { *ptr.add(6) };
460 (16, true, count, Some(ext))
461 }
462 0xF0 => {
464 let handle = unsafe { core::ptr::read_unaligned(ptr.add(4) as *const u16) };
465 (8, false, 0, Some(handle))
466 }
467 _ => (0, false, 0, None), }
469}
470
471pub struct Ivrs {
473 header: &'static IvrsHeader,
474}
475
476impl Ivrs {
477 pub fn get() -> Option<Self> {
479 IvrsHeader::get().map(|header| Ivrs { header })
480 }
481
482 pub fn header(&self) -> &'static IvrsHeader {
484 self.header
485 }
486
487 pub fn blocks(&self) -> IvrsEntryIter<'_> {
489 self.header.entries()
490 }
491
492 pub fn iommu_count(&self) -> usize {
494 let mut count = 0;
495 let mut iter = self.blocks();
496 while let Some(block) = iter.next_block() {
497 if matches!(block, IvrsBlock::Ivhd(_)) {
498 count += 1;
499 }
500 }
501 count
502 }
503
504 pub fn ivhd_blocks(&self) -> alloc::vec::Vec<IvhdBlock> {
506 let mut blocks = alloc::vec::Vec::new();
507 let mut iter = self.blocks();
508 while let Some(block) = iter.next_block() {
509 if let IvrsBlock::Ivhd(hd) = block {
510 blocks.push(hd);
511 }
512 }
513 blocks
514 }
515
516 pub fn ivmd_blocks(&self) -> alloc::vec::Vec<IvmdBlock> {
518 let mut blocks = alloc::vec::Vec::new();
519 let mut iter = self.blocks();
520 while let Some(block) = iter.next_block() {
521 if let IvrsBlock::Ivmd(md) = block {
522 blocks.push(md);
523 }
524 }
525 blocks
526 }
527
528 pub fn dump(&self) {
530 let dev_entry_count =
531 unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(self.header.dev_entry_count)) };
532 let entry_offset =
533 unsafe { core::ptr::read_unaligned(core::ptr::addr_of!(self.header.entry_offset)) };
534 log::info!(
535 "IVRS: Draint={} Coherent={} entries={} entry_offset={}",
536 self.header.has_draint(),
537 self.header.is_coherent(),
538 dev_entry_count,
539 entry_offset,
540 );
541
542 let mut ivhd_idx = 0;
543 let mut iter = self.blocks();
544 while let Some(block) = iter.next_block() {
545 match block {
546 IvrsBlock::Ivhd(hd) => {
547 log::info!(" IVHD #{}: {}", ivhd_idx, hd.summary());
548 let mut dev_iter = hd.device_entries();
549 let mut dev_count = 0;
550 while let Some(dev) = dev_iter.next_entry() {
551 log::debug!(
552 " device type={:#04x} id={:#06x} data={:02x}{:02x}{:02x}{:02x}",
553 dev.raw_type,
554 dev.device_id,
555 dev.data[0],
556 dev.data[1],
557 dev.data[2],
558 dev.data[3],
559 );
560 dev_count += 1;
561 }
562 log::info!(" -> {} device entries", dev_count);
563 ivhd_idx += 1;
564 }
565 IvrsBlock::Ivmd(md) => {
566 log::info!(
567 " IVMD: type={:#04x} flags={:#04x} dev_id={:#06x} iommu_base={:#018x} region={:#018x}-{:#018x}",
568 md.typ,
569 md.flags,
570 md.device_id,
571 md.iommu_base,
572 md.start_addr,
573 md.start_addr + md.length_bytes,
574 );
575 }
576 }
577 }
578 }
579}