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