• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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