xref: /cloud-hypervisor/devices/src/acpi.rs (revision 7d7bfb2034001d4cb15df2ddc56d2d350c8da30f)
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_.GED_".into(),
109             vec![
110                 &aml::Name::new("_HID".into(), &"ACPI0013"),
111                 &aml::Name::new("_UID".into(), &aml::ZERO),
112                 &aml::Name::new(
113                     "_CRS".into(),
114                     &aml::ResourceTemplate::new(vec![&aml::Interrupt::new(
115                         true,
116                         true,
117                         false,
118                         false,
119                         self.ged_irq,
120                     )]),
121                 ),
122                 &aml::OpRegion::new(
123                     "GDST".into(),
124                     aml::OpRegionSpace::SystemMemory,
125                     self.address.0 as usize,
126                     GED_DEVICE_ACPI_SIZE,
127                 ),
128                 &aml::Field::new(
129                     "GDST".into(),
130                     aml::FieldAccessType::Byte,
131                     aml::FieldUpdateRule::WriteAsZeroes,
132                     vec![aml::FieldEntry::Named(*b"GDAT", 8)],
133                 ),
134                 &aml::Method::new(
135                     "_EVT".into(),
136                     1,
137                     true,
138                     vec![
139                         &aml::Store::new(&aml::Local(0), &aml::Path::new("GDAT")),
140                         &aml::And::new(&aml::Local(1), &aml::Local(0), &aml::ONE),
141                         &aml::If::new(
142                             &aml::Equal::new(&aml::Local(1), &aml::ONE),
143                             vec![&aml::MethodCall::new("\\_SB_.CPUS.CSCN".into(), vec![])],
144                         ),
145                         &aml::And::new(&aml::Local(1), &aml::Local(0), &2usize),
146                         &aml::If::new(
147                             &aml::Equal::new(&aml::Local(1), &2usize),
148                             vec![&aml::MethodCall::new("\\_SB_.MHPC.MSCN".into(), vec![])],
149                         ),
150                         &aml::And::new(&aml::Local(1), &aml::Local(0), &4usize),
151                         &aml::If::new(
152                             &aml::Equal::new(&aml::Local(1), &4usize),
153                             vec![&aml::MethodCall::new("\\_SB_.PHPR.PSCN".into(), vec![])],
154                         ),
155                         &aml::And::new(&aml::Local(1), &aml::Local(0), &8usize),
156                         &aml::If::new(
157                             &aml::Equal::new(&aml::Local(1), &8usize),
158                             vec![&aml::Notify::new(
159                                 &aml::Path::new("\\_SB_.PWRB"),
160                                 &0x80usize,
161                             )],
162                         ),
163                     ],
164                 ),
165             ],
166         )
167         .append_aml_bytes(bytes)
168     }
169 }
170 
171 pub struct AcpiPmTimerDevice {
172     start: Instant,
173 }
174 
175 impl AcpiPmTimerDevice {
176     pub fn new() -> Self {
177         Self {
178             start: Instant::now(),
179         }
180     }
181 }
182 
183 impl Default for AcpiPmTimerDevice {
184     fn default() -> Self {
185         Self::new()
186     }
187 }
188 
189 impl BusDevice for AcpiPmTimerDevice {
190     fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
191         if data.len() != std::mem::size_of::<u32>() {
192             warn!("Invalid sized read of PM timer: {}", data.len());
193             return;
194         }
195         let now = Instant::now();
196         let since = now.duration_since(self.start);
197         let nanos = since.as_nanos();
198 
199         const PM_TIMER_FREQUENCY_HZ: u128 = 3_579_545;
200         const NANOS_PER_SECOND: u128 = 1_000_000_000;
201 
202         let counter = (nanos * PM_TIMER_FREQUENCY_HZ) / NANOS_PER_SECOND;
203         let counter: u32 = (counter & 0xffff_ffff) as u32;
204 
205         data.copy_from_slice(&counter.to_le_bytes());
206     }
207 }
208