1 // Copyright 2020 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 use acpi_tables::{rsdp::RSDP, sdt::SDT};
5 use data_model::DataInit;
6 use vm_memory::{GuestAddress, GuestMemory};
7
8 pub struct ACPIDevResource {
9 pub amls: Vec<u8>,
10 pub pm_iobase: u64,
11 /// Additional system descriptor tables.
12 pub sdts: Vec<SDT>,
13 }
14
15 #[repr(C)]
16 #[derive(Clone, Copy, Default)]
17 struct LocalAPIC {
18 _type: u8,
19 _length: u8,
20 _processor_id: u8,
21 _apic_id: u8,
22 _flags: u32,
23 }
24
25 // Safe as LocalAPIC structure only contains raw data
26 unsafe impl DataInit for LocalAPIC {}
27
28 #[repr(C)]
29 #[derive(Clone, Copy, Default)]
30 struct IOAPIC {
31 _type: u8,
32 _length: u8,
33 _ioapic_id: u8,
34 _reserved: u8,
35 _apic_address: u32,
36 _gsi_base: u32,
37 }
38
39 // Safe as IOAPIC structure only contains raw data
40 unsafe impl DataInit for IOAPIC {}
41
42 const OEM_REVISION: u32 = 1;
43 //DSDT
44 const DSDT_REVISION: u8 = 6;
45 // FADT
46 const FADT_LEN: u32 = 276;
47 const FADT_REVISION: u8 = 6;
48 const FADT_MINOR_REVISION: u8 = 3;
49 // FADT flags
50 const FADT_POWER_BUTTON: u32 = 1 << 4;
51 const FADT_SLEEP_BUTTON: u32 = 1 << 5;
52 // FADT fields offset
53 const FADT_FIELD_SCI_INTERRUPT: usize = 46;
54 const FADT_FIELD_PM1A_EVENT_BLK_ADDR: usize = 56;
55 const FADT_FIELD_PM1A_CONTROL_BLK_ADDR: usize = 64;
56 const FADT_FIELD_PM1A_EVENT_BLK_LEN: usize = 88;
57 const FADT_FIELD_PM1A_CONTROL_BLK_LEN: usize = 89;
58 const FADT_FIELD_FLAGS: usize = 112;
59 const FADT_FIELD_MINOR_REVISION: usize = 131;
60 const FADT_FIELD_DSDT_ADDR: usize = 140;
61 const FADT_FIELD_HYPERVISOR_ID: usize = 268;
62 // MADT
63 const MADT_LEN: u32 = 44;
64 const MADT_REVISION: u8 = 5;
65 // MADT fields offset
66 const MADT_FIELD_LAPIC_ADDR: usize = 36;
67 // MADT types
68 const MADT_TYPE_LOCAL_APIC: u8 = 0;
69 const MADT_TYPE_IO_APIC: u8 = 1;
70 // MADT flags
71 const MADT_ENABLED: u32 = 1;
72 // XSDT
73 const XSDT_REVISION: u8 = 1;
74
create_dsdt_table(amls: Vec<u8>) -> SDT75 fn create_dsdt_table(amls: Vec<u8>) -> SDT {
76 let mut dsdt = SDT::new(
77 *b"DSDT",
78 acpi_tables::HEADER_LEN,
79 DSDT_REVISION,
80 *b"CROSVM",
81 *b"CROSVMDT",
82 OEM_REVISION,
83 );
84
85 if !amls.is_empty() {
86 dsdt.append_slice(amls.as_slice());
87 }
88
89 dsdt
90 }
91
92 /// Create ACPI tables and return the RSDP.
93 /// The basic tables DSDT/FACP/MADT/XSDT are constructed in this function.
94 /// # Arguments
95 ///
96 /// * `guest_mem` - The guest memory where the tables will be stored.
97 /// * `num_cpus` - Used to construct the MADT.
98 /// * `sci_irq` - Used to fill the FACP SCI_INTERRUPT field, which
99 /// is going to be used by the ACPI drivers to register
100 /// sci handler.
101 /// * `acpi_dev_resource` - resouces needed by the ACPI devices for creating tables
create_acpi_tables( guest_mem: &GuestMemory, num_cpus: u8, sci_irq: u32, acpi_dev_resource: ACPIDevResource, ) -> Option<GuestAddress>102 pub fn create_acpi_tables(
103 guest_mem: &GuestMemory,
104 num_cpus: u8,
105 sci_irq: u32,
106 acpi_dev_resource: ACPIDevResource,
107 ) -> Option<GuestAddress> {
108 // RSDP is at the HI RSDP WINDOW
109 let rsdp_offset = GuestAddress(super::ACPI_HI_RSDP_WINDOW_BASE);
110 let mut offset = rsdp_offset.checked_add(RSDP::len() as u64)?;
111 let mut tables: Vec<u64> = Vec::new();
112 let mut dsdt_offset: Option<GuestAddress> = None;
113
114 // User supplied System Description Tables, e.g. SSDT.
115 for sdt in acpi_dev_resource.sdts.iter() {
116 guest_mem.write_at_addr(sdt.as_slice(), offset).ok()?;
117 if sdt.is_signature(b"DSDT") {
118 dsdt_offset = Some(offset);
119 } else {
120 tables.push(offset.0);
121 }
122 offset = offset.checked_add(sdt.len() as u64)?;
123 }
124
125 // DSDT
126 let dsdt_offset = match dsdt_offset {
127 Some(dsdt_offset) => dsdt_offset,
128 None => {
129 let dsdt_offset = offset;
130 let dsdt = create_dsdt_table(acpi_dev_resource.amls);
131 guest_mem.write_at_addr(dsdt.as_slice(), offset).ok()?;
132 offset = offset.checked_add(dsdt.len() as u64)?;
133 dsdt_offset
134 }
135 };
136
137 // FACP aka FADT
138 // Revision 6 of the ACPI FADT table is 276 bytes long
139 let mut facp = SDT::new(
140 *b"FACP",
141 FADT_LEN,
142 FADT_REVISION,
143 *b"CROSVM",
144 *b"CROSVMDT",
145 OEM_REVISION,
146 );
147
148 let fadt_flags: u32 = FADT_POWER_BUTTON | FADT_SLEEP_BUTTON; // mask POWER and SLEEP BUTTON
149 facp.write(FADT_FIELD_FLAGS, fadt_flags);
150
151 // SCI Interrupt
152 facp.write(FADT_FIELD_SCI_INTERRUPT, sci_irq as u16);
153
154 // PM1A Event Block Address
155 facp.write(
156 FADT_FIELD_PM1A_EVENT_BLK_ADDR,
157 acpi_dev_resource.pm_iobase as u32,
158 );
159
160 // PM1A Control Block Address
161 facp.write(
162 FADT_FIELD_PM1A_CONTROL_BLK_ADDR,
163 acpi_dev_resource.pm_iobase as u32 + devices::acpi::ACPIPM_RESOURCE_EVENTBLK_LEN as u32,
164 );
165
166 // PM1 Event Block Length
167 facp.write(
168 FADT_FIELD_PM1A_EVENT_BLK_LEN,
169 devices::acpi::ACPIPM_RESOURCE_EVENTBLK_LEN as u8,
170 );
171
172 // PM1 Control Block Length
173 facp.write(
174 FADT_FIELD_PM1A_CONTROL_BLK_LEN,
175 devices::acpi::ACPIPM_RESOURCE_CONTROLBLK_LEN as u8,
176 );
177
178 facp.write(FADT_FIELD_MINOR_REVISION, FADT_MINOR_REVISION); // FADT minor version
179 facp.write(FADT_FIELD_DSDT_ADDR, dsdt_offset.0 as u64); // X_DSDT
180
181 facp.write(FADT_FIELD_HYPERVISOR_ID, *b"CROSVM"); // Hypervisor Vendor Identity
182
183 guest_mem.write_at_addr(facp.as_slice(), offset).ok()?;
184 tables.push(offset.0);
185 offset = offset.checked_add(facp.len() as u64)?;
186
187 // MADT
188 let mut madt = SDT::new(
189 *b"APIC",
190 MADT_LEN,
191 MADT_REVISION,
192 *b"CROSVM",
193 *b"CROSVMDT",
194 OEM_REVISION,
195 );
196 madt.write(
197 MADT_FIELD_LAPIC_ADDR,
198 super::mptable::APIC_DEFAULT_PHYS_BASE as u32,
199 );
200
201 for cpu in 0..num_cpus {
202 let lapic = LocalAPIC {
203 _type: MADT_TYPE_LOCAL_APIC,
204 _length: std::mem::size_of::<LocalAPIC>() as u8,
205 _processor_id: cpu,
206 _apic_id: cpu,
207 _flags: MADT_ENABLED,
208 };
209 madt.append(lapic);
210 }
211
212 madt.append(IOAPIC {
213 _type: MADT_TYPE_IO_APIC,
214 _length: std::mem::size_of::<IOAPIC>() as u8,
215 _apic_address: super::mptable::IO_APIC_DEFAULT_PHYS_BASE,
216 ..Default::default()
217 });
218
219 guest_mem.write_at_addr(madt.as_slice(), offset).ok()?;
220 tables.push(offset.0);
221 offset = offset.checked_add(madt.len() as u64)?;
222
223 // XSDT
224 let mut xsdt = SDT::new(
225 *b"XSDT",
226 acpi_tables::HEADER_LEN,
227 XSDT_REVISION,
228 *b"CROSVM",
229 *b"CROSVMDT",
230 OEM_REVISION,
231 );
232 for table in tables {
233 xsdt.append(table);
234 }
235
236 guest_mem.write_at_addr(xsdt.as_slice(), offset).ok()?;
237
238 // RSDP
239 let rsdp = RSDP::new(*b"CROSVM", offset.0);
240 guest_mem.write_at_addr(rsdp.as_slice(), rsdp_offset).ok()?;
241
242 Some(rsdp_offset)
243 }
244