xref: /cloud-hypervisor/devices/src/acpi.rs (revision f7f2f25a574b1b2dba22c094fc8226d404157d15)
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         for i in data.iter_mut() {
38             *i = 0;
39         }
40     }
41 
42     fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
43         if data[0] == 1 {
44             debug!("ACPI Reboot signalled");
45             if let Err(e) = self.reset_evt.write(1) {
46                 error!("Error triggering ACPI reset event: {}", e);
47             }
48         }
49         // The ACPI DSDT table specifies the S5 sleep state (shutdown) as value 5
50         const S5_SLEEP_VALUE: u8 = 5;
51         const SLEEP_STATUS_EN_BIT: u8 = 5;
52         const SLEEP_VALUE_BIT: u8 = 2;
53         if data[0] == (S5_SLEEP_VALUE << SLEEP_VALUE_BIT) | (1 << SLEEP_STATUS_EN_BIT) {
54             debug!("ACPI Shutdown signalled");
55             if let Err(e) = self.exit_evt.write(1) {
56                 error!("Error triggering ACPI shutdown event: {}", e);
57             }
58         }
59         None
60     }
61 }
62 
63 /// A device for handling ACPI GED event generation
64 pub struct AcpiGedDevice {
65     interrupt: Arc<dyn InterruptSourceGroup>,
66     notification_type: AcpiNotificationFlags,
67     ged_irq: u32,
68     address: GuestAddress,
69 }
70 
71 impl AcpiGedDevice {
72     pub fn new(
73         interrupt: Arc<dyn InterruptSourceGroup>,
74         ged_irq: u32,
75         address: GuestAddress,
76     ) -> AcpiGedDevice {
77         AcpiGedDevice {
78             interrupt,
79             notification_type: AcpiNotificationFlags::NO_DEVICES_CHANGED,
80             ged_irq,
81             address,
82         }
83     }
84 
85     pub fn notify(
86         &mut self,
87         notification_type: AcpiNotificationFlags,
88     ) -> Result<(), std::io::Error> {
89         self.notification_type |= notification_type;
90         self.interrupt.trigger(0)
91     }
92 
93     pub fn irq(&self) -> u32 {
94         self.ged_irq
95     }
96 }
97 
98 // I/O port reports what type of notification was made
99 impl BusDevice for AcpiGedDevice {
100     // Spec has all fields as zero
101     fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
102         data[0] = self.notification_type.bits();
103         self.notification_type = AcpiNotificationFlags::NO_DEVICES_CHANGED;
104     }
105 }
106 
107 #[cfg(feature = "acpi")]
108 impl Aml for AcpiGedDevice {
109     fn to_aml_bytes(&self) -> Vec<u8> {
110         aml::Device::new(
111             "_SB_.GED_".into(),
112             vec![
113                 &aml::Name::new("_HID".into(), &"ACPI0013"),
114                 &aml::Name::new("_UID".into(), &aml::ZERO),
115                 &aml::Name::new(
116                     "_CRS".into(),
117                     &aml::ResourceTemplate::new(vec![&aml::Interrupt::new(
118                         true,
119                         true,
120                         false,
121                         false,
122                         self.ged_irq,
123                     )]),
124                 ),
125                 &aml::OpRegion::new(
126                     "GDST".into(),
127                     aml::OpRegionSpace::SystemMemory,
128                     self.address.0 as usize,
129                     GED_DEVICE_ACPI_SIZE,
130                 ),
131                 &aml::Field::new(
132                     "GDST".into(),
133                     aml::FieldAccessType::Byte,
134                     aml::FieldUpdateRule::WriteAsZeroes,
135                     vec![aml::FieldEntry::Named(*b"GDAT", 8)],
136                 ),
137                 &aml::Method::new(
138                     "_EVT".into(),
139                     1,
140                     true,
141                     vec![
142                         &aml::Store::new(&aml::Local(0), &aml::Path::new("GDAT")),
143                         &aml::And::new(&aml::Local(1), &aml::Local(0), &aml::ONE),
144                         &aml::If::new(
145                             &aml::Equal::new(&aml::Local(1), &aml::ONE),
146                             vec![&aml::MethodCall::new("\\_SB_.CPUS.CSCN".into(), vec![])],
147                         ),
148                         &aml::And::new(&aml::Local(1), &aml::Local(0), &2usize),
149                         &aml::If::new(
150                             &aml::Equal::new(&aml::Local(1), &2usize),
151                             vec![&aml::MethodCall::new("\\_SB_.MHPC.MSCN".into(), vec![])],
152                         ),
153                         &aml::And::new(&aml::Local(1), &aml::Local(0), &4usize),
154                         &aml::If::new(
155                             &aml::Equal::new(&aml::Local(1), &4usize),
156                             vec![&aml::MethodCall::new("\\_SB_.PCI0.PCNT".into(), vec![])],
157                         ),
158                         &aml::And::new(&aml::Local(1), &aml::Local(0), &8usize),
159                         &aml::If::new(
160                             &aml::Equal::new(&aml::Local(1), &8usize),
161                             vec![&aml::Notify::new(
162                                 &aml::Path::new("\\_SB_.PWRB"),
163                                 &0x80usize,
164                             )],
165                         ),
166                     ],
167                 ),
168             ],
169         )
170         .to_aml_bytes()
171     }
172 }
173 
174 pub struct AcpiPmTimerDevice {
175     start: Instant,
176 }
177 
178 impl AcpiPmTimerDevice {
179     pub fn new() -> Self {
180         Self {
181             start: Instant::now(),
182         }
183     }
184 }
185 
186 impl Default for AcpiPmTimerDevice {
187     fn default() -> Self {
188         Self::new()
189     }
190 }
191 
192 impl BusDevice for AcpiPmTimerDevice {
193     fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
194         let now = Instant::now();
195         let since = now.duration_since(self.start);
196         let nanos = since.as_nanos();
197 
198         const PM_TIMER_FREQUENCY_HZ: u128 = 3_579_545;
199         const NANOS_PER_SECOND: u128 = 1_000_000_000;
200 
201         let counter = (nanos * PM_TIMER_FREQUENCY_HZ) / NANOS_PER_SECOND;
202         let counter: u32 = (counter & 0xffff_ffff) as u32;
203 
204         data.copy_from_slice(&counter.to_le_bytes());
205     }
206 }
207