xref: /cloud-hypervisor/devices/src/acpi.rs (revision 6f8bd27cf7629733582d930519e98d19e90afb16)
1 // Copyright © 2019 Intel Corporation
2 //
3 // SPDX-License-Identifier: Apache-2.0
4 //
5 
6 use super::AcpiNotificationFlags;
7 use acpi_tables::{aml, aml::Aml};
8 use std::sync::{Arc, Barrier};
9 use std::time::Instant;
10 use vm_device::interrupt::InterruptSourceGroup;
11 use vm_device::BusDevice;
12 use vm_memory::GuestAddress;
13 use vmm_sys_util::eventfd::EventFd;
14 
15 pub const GED_DEVICE_ACPI_SIZE: usize = 0x1;
16 
17 /// A device for handling ACPI shutdown and reboot
18 pub struct AcpiShutdownDevice {
19     exit_evt: EventFd,
20     reset_evt: EventFd,
21 }
22 
23 impl AcpiShutdownDevice {
24     /// Constructs a device that will signal the given event when the guest requests it.
25     pub fn new(exit_evt: EventFd, reset_evt: EventFd) -> AcpiShutdownDevice {
26         AcpiShutdownDevice {
27             exit_evt,
28             reset_evt,
29         }
30     }
31 }
32 
33 // Same I/O port used for shutdown and reboot
34 impl BusDevice for AcpiShutdownDevice {
35     // Spec has all fields as zero
36     fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
37         data.fill(0)
38     }
39 
40     fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
41         if data[0] == 1 {
42             info!("ACPI Reboot signalled");
43             if let Err(e) = self.reset_evt.write(1) {
44                 error!("Error triggering ACPI reset event: {}", e);
45             }
46         }
47         // The ACPI DSDT table specifies the S5 sleep state (shutdown) as value 5
48         const S5_SLEEP_VALUE: u8 = 5;
49         const SLEEP_STATUS_EN_BIT: u8 = 5;
50         const SLEEP_VALUE_BIT: u8 = 2;
51         if data[0] == (S5_SLEEP_VALUE << SLEEP_VALUE_BIT) | (1 << SLEEP_STATUS_EN_BIT) {
52             info!("ACPI Shutdown signalled");
53             if let Err(e) = self.exit_evt.write(1) {
54                 error!("Error triggering ACPI shutdown event: {}", e);
55             }
56         }
57         None
58     }
59 }
60 
61 /// A device for handling ACPI GED event generation
62 pub struct AcpiGedDevice {
63     interrupt: Arc<dyn InterruptSourceGroup>,
64     notification_type: AcpiNotificationFlags,
65     ged_irq: u32,
66     address: GuestAddress,
67 }
68 
69 impl AcpiGedDevice {
70     pub fn new(
71         interrupt: Arc<dyn InterruptSourceGroup>,
72         ged_irq: u32,
73         address: GuestAddress,
74     ) -> AcpiGedDevice {
75         AcpiGedDevice {
76             interrupt,
77             notification_type: AcpiNotificationFlags::NO_DEVICES_CHANGED,
78             ged_irq,
79             address,
80         }
81     }
82 
83     pub fn notify(
84         &mut self,
85         notification_type: AcpiNotificationFlags,
86     ) -> Result<(), std::io::Error> {
87         self.notification_type |= notification_type;
88         self.interrupt.trigger(0)
89     }
90 
91     pub fn irq(&self) -> u32 {
92         self.ged_irq
93     }
94 }
95 
96 // I/O port reports what type of notification was made
97 impl BusDevice for AcpiGedDevice {
98     // Spec has all fields as zero
99     fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
100         data[0] = self.notification_type.bits();
101         self.notification_type = AcpiNotificationFlags::NO_DEVICES_CHANGED;
102     }
103 }
104 
105 impl Aml for AcpiGedDevice {
106     fn append_aml_bytes(&self, bytes: &mut Vec<u8>) {
107         aml::Device::new(
108             "_SB_.GEC_".into(),
109             vec![
110                 &aml::Name::new("_HID".into(), &aml::EisaName::new("PNP0A06")),
111                 &aml::Name::new("_UID".into(), &"Generic Event Controller"),
112                 &aml::Name::new(
113                     "_CRS".into(),
114                     &aml::ResourceTemplate::new(vec![&aml::AddressSpace::new_memory(
115                         aml::AddressSpaceCachable::NotCacheable,
116                         true,
117                         self.address.0,
118                         self.address.0 + GED_DEVICE_ACPI_SIZE as u64 - 1,
119                     )]),
120                 ),
121                 &aml::OpRegion::new(
122                     "GDST".into(),
123                     aml::OpRegionSpace::SystemMemory,
124                     self.address.0 as usize,
125                     GED_DEVICE_ACPI_SIZE,
126                 ),
127                 &aml::Field::new(
128                     "GDST".into(),
129                     aml::FieldAccessType::Byte,
130                     aml::FieldUpdateRule::WriteAsZeroes,
131                     vec![aml::FieldEntry::Named(*b"GDAT", 8)],
132                 ),
133                 &aml::Method::new(
134                     "ESCN".into(),
135                     0,
136                     true,
137                     vec![
138                         &aml::Store::new(&aml::Local(0), &aml::Path::new("GDAT")),
139                         &aml::And::new(&aml::Local(1), &aml::Local(0), &aml::ONE),
140                         &aml::If::new(
141                             &aml::Equal::new(&aml::Local(1), &aml::ONE),
142                             vec![&aml::MethodCall::new("\\_SB_.CPUS.CSCN".into(), vec![])],
143                         ),
144                         &aml::And::new(&aml::Local(1), &aml::Local(0), &2usize),
145                         &aml::If::new(
146                             &aml::Equal::new(&aml::Local(1), &2usize),
147                             vec![&aml::MethodCall::new("\\_SB_.MHPC.MSCN".into(), vec![])],
148                         ),
149                         &aml::And::new(&aml::Local(1), &aml::Local(0), &4usize),
150                         &aml::If::new(
151                             &aml::Equal::new(&aml::Local(1), &4usize),
152                             vec![&aml::MethodCall::new("\\_SB_.PHPR.PSCN".into(), vec![])],
153                         ),
154                         &aml::And::new(&aml::Local(1), &aml::Local(0), &8usize),
155                         &aml::If::new(
156                             &aml::Equal::new(&aml::Local(1), &8usize),
157                             vec![&aml::Notify::new(
158                                 &aml::Path::new("\\_SB_.PWRB"),
159                                 &0x80usize,
160                             )],
161                         ),
162                     ],
163                 ),
164             ],
165         )
166         .append_aml_bytes(bytes);
167         aml::Device::new(
168             "_SB_.GED_".into(),
169             vec![
170                 &aml::Name::new("_HID".into(), &"ACPI0013"),
171                 &aml::Name::new("_UID".into(), &aml::ZERO),
172                 &aml::Name::new(
173                     "_CRS".into(),
174                     &aml::ResourceTemplate::new(vec![&aml::Interrupt::new(
175                         true,
176                         true,
177                         false,
178                         false,
179                         self.ged_irq,
180                     )]),
181                 ),
182                 &aml::Method::new(
183                     "_EVT".into(),
184                     1,
185                     true,
186                     vec![&aml::MethodCall::new("\\_SB_.GEC_.ESCN".into(), vec![])],
187                 ),
188             ],
189         )
190         .append_aml_bytes(bytes)
191     }
192 }
193 
194 pub struct AcpiPmTimerDevice {
195     start: Instant,
196 }
197 
198 impl AcpiPmTimerDevice {
199     pub fn new() -> Self {
200         Self {
201             start: Instant::now(),
202         }
203     }
204 }
205 
206 impl Default for AcpiPmTimerDevice {
207     fn default() -> Self {
208         Self::new()
209     }
210 }
211 
212 impl BusDevice for AcpiPmTimerDevice {
213     fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
214         if data.len() != std::mem::size_of::<u32>() {
215             warn!("Invalid sized read of PM timer: {}", data.len());
216             return;
217         }
218         let now = Instant::now();
219         let since = now.duration_since(self.start);
220         let nanos = since.as_nanos();
221 
222         const PM_TIMER_FREQUENCY_HZ: u128 = 3_579_545;
223         const NANOS_PER_SECOND: u128 = 1_000_000_000;
224 
225         let counter = (nanos * PM_TIMER_FREQUENCY_HZ) / NANOS_PER_SECOND;
226         let counter: u32 = (counter & 0xffff_ffff) as u32;
227 
228         data.copy_from_slice(&counter.to_le_bytes());
229     }
230 }
231