1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//! Support for the DRHD: DMAR Hardware Unit Definition Structure.

use super::*;

/// DRHD: DMAR Hardware Unit Definition Structure.
///
/// This table is described in Section 8.3 of the VT Directed I/O Spec.
#[derive(Clone, Copy, Debug, FromBytes)]
#[repr(packed)]
pub(crate) struct Drhd {
    _header: DmarEntryRecord,
    flags: u8,
    _reserved: u8,
    segment_number: u16,
    register_base_address: u64,
    // Following this is a variable number of variable-sized DMAR device scope table entries,
    // so we cannot include them here in the static struct definition.
}
const _: () = assert!(core::mem::size_of::<Drhd>() == 16);
const _: () = assert!(core::mem::align_of::<Drhd>() == 1);


/// DRHD: DMAR Hardware Unit Definition Structure.
///
/// This table is described in Section 8.3 of the VT Directed I/O Spec.
#[derive(Debug)]
pub struct DmarDrhd<'t> {
    /// The fixed-size part of the actual DRHD ACPI table.
    table: &'t Drhd,
    /// The underlying MappedPages that cover this table.
    mapped_pages: &'t MappedPages,
    /// The offset into the above `mapped_pages` at which the dynamic part 
    /// (the [`DmarDeviceScope`] structures) of the DRHD table begins.
    dynamic_entries_starting_offset: usize,
    /// The total size in bytes of all dynamic [`DmarDeviceScope`] entries.
    /// This is *not* the number of entries.
    dynamic_entries_total_size: usize,
}

impl<'t> DmarDrhd<'t> {
    pub(crate) fn from_entry(
        mp: &'t MappedPages,
        mp_offset: usize,
        entry: &DmarEntryRecord,
    ) -> Result<DmarDrhd<'t>, &'static str> {
        Ok(DmarDrhd {
            table: mp.as_type(mp_offset)?,
            mapped_pages: mp,
            dynamic_entries_starting_offset: mp_offset + size_of::<Drhd>(), 
            dynamic_entries_total_size: entry.length as usize - size_of::<Drhd>(),
        })
    }
}


impl<'t> DmarDrhd<'t> {
    /// Returns an [`Iterator`] over the [`DmarDeviceScope`] entries in this DRHD,
    /// which are variable in both number and size.
    pub fn iter(&self) -> DrhdIter<'t> {
        DrhdIter {
            mapped_pages: self.mapped_pages,
            offset: self.dynamic_entries_starting_offset,
            end_of_entries: self.dynamic_entries_starting_offset + self.dynamic_entries_total_size,
        }
    }

    /// Returns the value of the `INCLUDE_PCI_ALL` flag,
    /// the only bit flag in this DRHD table.
    ///
    /// # Description from Intel Spec
    /// If `false`, this remapping hardware unit has under its scope only
    /// devices in the specified segment that are explicitly identified through
    /// the Device Scope field. The device can be of any type as described by
    /// the Type field in the Device Scope Structure including (but not limited to)
    /// IOAPIC and HPET.
    /// 
    /// If `true`, this remapping hardware unit has under its scope all PCI
    /// compatible devices in the specified segment, except devices reported
    /// under the scope of other remapping hardware units for the same segment. 
    /// As such, one can use the Device Scope structures to enumerate 
    /// IOAPIC and HPET devices under its scope.
    pub fn include_pci_all(&self) -> bool {
        self.table.flags & 0x01 == 0x01
    }

    /// Returns the PCI segment number associated with this DRHD.
    pub fn segment_number(&self) -> u16 {
        self.table.segment_number
    }

    /// Returns the base address of this DRHD's remapping hardware register set.
    pub fn register_base_address(&self) -> u64 {
        self.table.register_base_address
    }
}


/// An [`Iterator`] over the dynamic entries ([`DmarDeviceScope`]s) of the [`DmarDrhd`].
/// Its lifetime is dependent upon the lifetime of its [`DmarDrhd`] instance,
/// which itself is bound to the lifetime of the underlying [`AcpiTables`]. 
#[derive(Clone)]
pub struct DrhdIter<'t> {
    /// The underlying MappedPages that contain all ACPI tables.
    mapped_pages: &'t MappedPages,
    /// The offset of the next entry, which should point to a [`DmarDeviceScope`]
    /// at the start of each iteration.
    offset: usize,
    /// The end bound of all DRHD entries. 
    /// This is fixed and should not ever change throughout iteration.
    end_of_entries: usize,
}

impl<'t> Iterator for DrhdIter<'t> {
    type Item = DmarDeviceScope<'t>;

    fn next(&mut self) -> Option<Self::Item> {
        if (self.offset + size_of::<DeviceScope>()) < self.end_of_entries {
            if let Ok(dev_scope) = DmarDeviceScope::from_entry(self.mapped_pages, self.offset) {
                self.offset += dev_scope.length() as usize;
                return Some(dev_scope);
            }
        }
        None
    }
}