acpi_spec/
srat.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#[cfg(feature = "alloc")]
5pub use self::alloc_parse::*;
6
7use super::Table;
8use crate::packed_nums::*;
9use core::mem::size_of;
10use static_assertions::const_assert_eq;
11use zerocopy::FromBytes;
12use zerocopy::Immutable;
13use zerocopy::IntoBytes;
14use zerocopy::KnownLayout;
15use zerocopy::Ref;
16use zerocopy::Unaligned;
17
18#[repr(C)]
19#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, Unaligned)]
20pub struct SratHeader {
21    pub rsvd1: u32_ne,
22    pub rsvd2: u64_ne,
23}
24
25impl SratHeader {
26    pub fn new() -> SratHeader {
27        SratHeader {
28            rsvd1: 1.into(),
29            rsvd2: 0.into(),
30        }
31    }
32}
33
34impl Table for SratHeader {
35    const SIGNATURE: [u8; 4] = *b"SRAT";
36}
37
38pub const SRAT_REVISION: u8 = 3;
39
40open_enum::open_enum! {
41    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Unaligned)]
42    pub enum SratType: u8 {
43        APIC = 0,
44        MEMORY = 1,
45        X2APIC = 2,
46        GICC = 3,
47    }
48}
49
50#[repr(C)]
51#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, Unaligned)]
52pub struct SratApic {
53    pub typ: SratType,
54    pub length: u8,
55    pub proximity_domain_byte1: u8,
56    pub apic_id: u8,
57    pub flags: u32_ne,
58    pub local_sapic_eid: u8,
59    pub proximity_domain_byte2: u8,
60    pub proximity_domain_byte3: u8,
61    pub proximity_domain_byte4: u8,
62    pub clock_domain: u32_ne,
63}
64
65const_assert_eq!(size_of::<SratApic>(), 16);
66
67pub const SRAT_APIC_ENABLED: u32 = 1 << 0;
68
69impl SratApic {
70    pub fn new(apic_id: u8, vnode: u32) -> Self {
71        let vnode = vnode.to_le_bytes();
72        Self {
73            typ: SratType::APIC,
74            length: size_of::<Self>() as u8,
75            proximity_domain_byte1: vnode[0],
76            apic_id,
77            flags: SRAT_APIC_ENABLED.into(),
78            local_sapic_eid: 0,
79            proximity_domain_byte2: vnode[1],
80            proximity_domain_byte3: vnode[2],
81            proximity_domain_byte4: vnode[3],
82            clock_domain: 0.into(),
83        }
84    }
85}
86
87#[repr(C)]
88#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, Unaligned)]
89pub struct SratX2Apic {
90    pub typ: SratType,
91    pub length: u8,
92    pub reserved: u16_ne,
93    pub proximity_domain: u32_ne,
94    pub x2_apic_id: u32_ne,
95    pub flags: u32_ne,
96    pub clock_domain: u32_ne,
97    pub reserved2: u32_ne,
98}
99
100const_assert_eq!(size_of::<SratX2Apic>(), 24);
101
102impl SratX2Apic {
103    pub fn new(x2_apic_id: u32, vnode: u32) -> Self {
104        Self {
105            typ: SratType::X2APIC,
106            length: size_of::<Self>() as u8,
107            x2_apic_id: x2_apic_id.into(),
108            flags: SRAT_APIC_ENABLED.into(),
109            clock_domain: 0.into(),
110            reserved: 0.into(),
111            proximity_domain: vnode.into(),
112            reserved2: 0.into(),
113        }
114    }
115}
116
117#[repr(C)]
118#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, Unaligned)]
119pub struct SratGicc {
120    pub typ: SratType,
121    pub length: u8,
122    pub proximity_domain: u32_ne,
123    pub acpi_processor_uid: u32_ne,
124    pub flags: u32_ne,
125    pub clock_domain: u32_ne,
126}
127
128const_assert_eq!(size_of::<SratGicc>(), 18);
129
130impl SratGicc {
131    pub fn new(acpi_processor_uid: u32, vnode: u32) -> Self {
132        Self {
133            typ: SratType::GICC,
134            length: size_of::<Self>() as u8,
135            acpi_processor_uid: acpi_processor_uid.into(),
136            flags: SRAT_APIC_ENABLED.into(),
137            clock_domain: 0.into(),
138            proximity_domain: vnode.into(),
139        }
140    }
141}
142
143#[repr(C)]
144#[derive(Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes, Unaligned)]
145pub struct SratMemory {
146    pub typ: SratType,
147    pub length: u8,
148    pub proximity_domain: u32_ne,
149    pub rsvd1: u16_ne,
150    pub low_address: u32_ne,
151    pub high_address: u32_ne,
152    pub low_length: u32_ne,
153    pub high_length: u32_ne,
154    pub rsvd2: u32_ne,
155    pub flags: u32_ne,
156    pub rsvd3: u64_ne,
157}
158
159impl core::fmt::Debug for SratMemory {
160    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
161        let address =
162            u64::read_from_bytes([self.low_address, self.high_address].as_bytes()).unwrap();
163        let length = u64::read_from_bytes([self.low_length, self.high_length].as_bytes()).unwrap();
164
165        f.debug_struct("SratMemory")
166            .field("typ", &self.typ)
167            .field("length", &self.length)
168            .field("proximity_domain", &self.proximity_domain)
169            .field("rsvd1", &self.rsvd1)
170            .field("address", &address)
171            .field("_end_address", &(address + length))
172            .field("length", &length)
173            .field("rsvd2", &self.rsvd2)
174            .field("flags", &self.flags)
175            .field("rsvd3", &self.rsvd3)
176            .finish()
177    }
178}
179
180const_assert_eq!(size_of::<SratMemory>(), 40);
181
182open_enum::open_enum! {
183    pub enum SratMemoryFlags: u32 {
184        ENABLED       = 1 << 0,
185        HOT_PLUGGABLE = 1 << 1,
186        NVRAM         = 1 << 2,
187    }
188}
189
190impl SratMemory {
191    pub fn new(addr: u64, len: u64, vnode: u32) -> Self {
192        Self {
193            typ: SratType::MEMORY,
194            length: size_of::<Self>() as u8,
195            proximity_domain: vnode.into(),
196            rsvd1: 0.into(),
197            low_address: (addr as u32).into(),
198            high_address: ((addr >> 32) as u32).into(),
199            low_length: (len as u32).into(),
200            high_length: ((len >> 32) as u32).into(),
201            rsvd2: 0.into(),
202            flags: SratMemoryFlags::ENABLED.0.into(),
203            rsvd3: 0.into(),
204        }
205    }
206}
207
208#[derive(Debug)]
209pub enum ParseSratError {
210    MissingAcpiHeader,
211    InvalidSignature([u8; 4]),
212    MismatchedLength { in_header: usize, actual: usize },
213    MissingFixedHeader,
214    BadApic,
215    BadMemory,
216    UnknownType(u8),
217}
218
219impl core::fmt::Display for ParseSratError {
220    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
221        match self {
222            Self::MissingAcpiHeader => write!(f, "could not read standard ACPI header"),
223            Self::InvalidSignature(sig) => {
224                write!(f, "invalid signature. expected b\"SRAT\", found {sig:?}")
225            }
226            Self::MismatchedLength { in_header, actual } => {
227                write!(f, "mismatched len. in_header: {in_header}, actual {actual}")
228            }
229            Self::MissingFixedHeader => write!(f, "missing fixed SRAT header"),
230            Self::BadApic => write!(f, "could not read APIC structure"),
231            Self::BadMemory => write!(f, "could not read MEMORY structure"),
232            Self::UnknownType(ty) => write!(f, "unknown SRAT structure type: {ty}"),
233        }
234    }
235}
236
237impl core::error::Error for ParseSratError {}
238
239pub fn parse_srat<'a>(
240    raw_srat: &'a [u8],
241    mut on_apic: impl FnMut(&'a SratApic),
242    mut on_memory: impl FnMut(&'a SratMemory),
243) -> Result<(&'a crate::Header, &'a SratHeader), ParseSratError> {
244    let raw_srat_len = raw_srat.len();
245    let (acpi_header, buf) = Ref::<_, crate::Header>::from_prefix(raw_srat)
246        .map_err(|_| ParseSratError::MissingAcpiHeader)?; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
247
248    if acpi_header.signature != *b"SRAT" {
249        return Err(ParseSratError::InvalidSignature(acpi_header.signature));
250    }
251
252    if acpi_header.length.get() as usize != raw_srat_len {
253        return Err(ParseSratError::MismatchedLength {
254            in_header: acpi_header.length.get() as usize,
255            actual: raw_srat_len,
256        });
257    }
258
259    let (srat_header, mut buf) =
260        Ref::<_, SratHeader>::from_prefix(buf).map_err(|_| ParseSratError::MissingFixedHeader)?; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
261
262    while !buf.is_empty() {
263        buf = match SratType(buf[0]) {
264            SratType::APIC => {
265                let (apic, rest) =
266                    Ref::<_, SratApic>::from_prefix(buf).map_err(|_| ParseSratError::BadApic)?; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
267                on_apic(Ref::into_ref(apic));
268                rest
269            }
270            SratType::MEMORY => {
271                let (mem, rest) = Ref::<_, SratMemory>::from_prefix(buf)
272                    .map_err(|_| ParseSratError::BadMemory)?; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
273                on_memory(Ref::into_ref(mem));
274                rest
275            }
276            _ => return Err(ParseSratError::UnknownType(buf[0])),
277        }
278    }
279
280    Ok((Ref::into_ref(acpi_header), Ref::into_ref(srat_header)))
281}
282
283#[cfg(feature = "alloc")]
284pub mod alloc_parse {
285    use super::*;
286    use alloc::vec::Vec;
287
288    #[derive(Debug)]
289    pub struct BorrowedSrat<'a> {
290        pub acpi_header: &'a crate::Header,
291        pub srat_header: &'a SratHeader,
292        pub apics: Vec<&'a SratApic>,
293        pub memory: Vec<&'a SratMemory>,
294    }
295
296    #[derive(Debug)]
297    pub struct OwnedSrat {
298        pub acpi_header: crate::Header,
299        pub srat_header: SratHeader,
300        pub apics: Vec<SratApic>,
301        pub memory: Vec<SratMemory>,
302    }
303
304    impl From<BorrowedSrat<'_>> for OwnedSrat {
305        fn from(b: BorrowedSrat<'_>) -> Self {
306            OwnedSrat {
307                acpi_header: *b.acpi_header,
308                srat_header: *b.srat_header,
309                apics: b.apics.into_iter().cloned().collect(),
310                memory: b.memory.into_iter().cloned().collect(),
311            }
312        }
313    }
314
315    impl BorrowedSrat<'_> {
316        pub fn new(raw_srat: &[u8]) -> Result<BorrowedSrat<'_>, ParseSratError> {
317            let mut apics = Vec::new();
318            let mut memory = Vec::new();
319            let (acpi_header, srat_header) =
320                parse_srat(raw_srat, |x| apics.push(x), |x| memory.push(x))?;
321
322            Ok(BorrowedSrat {
323                acpi_header,
324                srat_header,
325                apics,
326                memory,
327            })
328        }
329    }
330
331    impl OwnedSrat {
332        pub fn new(raw_srat: &[u8]) -> Result<OwnedSrat, ParseSratError> {
333            Ok(BorrowedSrat::new(raw_srat)?.into())
334        }
335    }
336}