acpi_tables/
madt.rs

1// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// Copyright 2023 Rivos, Inc.
3//
4// SPDX-License-Identifier: Apache-2.0
5
6use std::mem::size_of;
7
8use vm_memory::{Address, Bytes, GuestAddress, GuestMemory};
9use zerocopy::little_endian::U32;
10use zerocopy::{Immutable, IntoBytes};
11
12use crate::{AcpiError, Result, Sdt, SdtHeader, checksum};
13
14const MADT_CPU_ENABLE_FLAG: u32 = 0;
15
16// clippy doesn't understand that we actually "use" the fields of this struct when we serialize
17// them as bytes in guest memory, so here we just ignore dead code to avoid having to name
18// everything with an underscore prefix
19#[allow(dead_code)]
20#[repr(C, packed)]
21#[derive(Copy, Clone, Debug, Default, IntoBytes, Immutable)]
22pub struct LocalAPIC {
23    r#type: u8,
24    length: u8,
25    processor_uid: u8,
26    apic_id: u8,
27    flags: U32,
28}
29
30impl LocalAPIC {
31    pub fn new(cpu_id: u8) -> Self {
32        Self {
33            r#type: 0,
34            length: 8,
35            processor_uid: cpu_id,
36            apic_id: cpu_id,
37            flags: U32::new(1u32 << MADT_CPU_ENABLE_FLAG),
38        }
39    }
40}
41
42// clippy doesn't understand that we actually "use" the fields of this struct when we serialize
43// them as bytes in guest memory, so here we just ignore dead code to avoid having to name
44// everything with an underscore prefix
45#[allow(dead_code)]
46#[repr(C, packed)]
47#[derive(Copy, Clone, Debug, Default, IntoBytes, Immutable)]
48pub struct IoAPIC {
49    r#type: u8,
50    length: u8,
51    ioapic_id: u8,
52    reserved: u8,
53    apic_address: U32,
54    gsi_base: U32,
55}
56
57impl IoAPIC {
58    pub fn new(ioapic_id: u8, apic_address: u32) -> Self {
59        IoAPIC {
60            r#type: 1,
61            length: 12,
62            ioapic_id,
63            reserved: 0,
64            apic_address: U32::new(apic_address),
65            gsi_base: U32::ZERO,
66        }
67    }
68}
69
70// clippy doesn't understand that we actually "use" the fields of this struct when we serialize
71// them as bytes in guest memory, so here we just ignore dead code to avoid having to name
72// everything with an underscore prefix
73#[allow(dead_code)]
74#[repr(C, packed)]
75#[derive(Debug, IntoBytes, Immutable)]
76struct MadtHeader {
77    sdt: SdtHeader,
78    base_address: U32,
79    flags: U32,
80}
81
82/// Multiple APIC Description Table (MADT)
83///
84/// This table includes information about the interrupt controllers of the device.
85/// More information about this table can be found in the ACPI specification:
86/// https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#multiple-apic-description-table-madt
87#[derive(Debug)]
88pub struct Madt {
89    header: MadtHeader,
90    interrupt_controllers: Vec<u8>,
91}
92
93impl Madt {
94    pub fn new(
95        oem_id: [u8; 6],
96        oem_table_id: [u8; 8],
97        oem_revision: u32,
98        base_address: u32,
99        interrupt_controllers: Vec<u8>,
100    ) -> Self {
101        let length = size_of::<MadtHeader>() + interrupt_controllers.len();
102        let sdt_header = SdtHeader::new(
103            *b"APIC",
104            // It is ok to unwrap the conversion of `length` to u32. `SdtHeader` is 36 bytes long,
105            // so `length` here has a value of 44.
106            length.try_into().unwrap(),
107            6,
108            oem_id,
109            oem_table_id,
110            oem_revision,
111        );
112
113        let mut header = MadtHeader {
114            sdt: sdt_header,
115            base_address: U32::new(base_address),
116            flags: U32::ZERO,
117        };
118
119        header.sdt.checksum = checksum(&[header.as_bytes(), interrupt_controllers.as_bytes()]);
120
121        Madt {
122            header,
123            interrupt_controllers,
124        }
125    }
126}
127
128impl Sdt for Madt {
129    fn len(&self) -> usize {
130        self.header.sdt.length.get().try_into().unwrap()
131    }
132
133    fn write_to_guest<M: GuestMemory>(&mut self, mem: &M, address: GuestAddress) -> Result<()> {
134        mem.write_slice(self.header.as_bytes(), address)?;
135        let address = address
136            .checked_add(size_of::<MadtHeader>() as u64)
137            .ok_or(AcpiError::InvalidGuestAddress)?;
138        mem.write_slice(self.interrupt_controllers.as_bytes(), address)?;
139
140        Ok(())
141    }
142}