xref: /cloud-hypervisor/devices/src/acpi.rs (revision 88a9f799449c04180c6b9a21d3b9c0c4b57e2bd6)
15a187ee2SRob Bradford // Copyright © 2019 Intel Corporation
25a187ee2SRob Bradford //
35a187ee2SRob Bradford // SPDX-License-Identifier: Apache-2.0
45a187ee2SRob Bradford //
55a187ee2SRob Bradford 
606dc7085SRob Bradford use std::sync::atomic::{AtomicBool, Ordering};
71fc6d50fSRob Bradford use std::sync::{Arc, Barrier};
806dc7085SRob Bradford use std::thread;
9aae5d988SRob Bradford use std::time::Instant;
10*88a9f799SRob Bradford 
11*88a9f799SRob Bradford use acpi_tables::{aml, Aml, AmlSink};
12bb8cd9ebSSebastien Boeuf use vm_device::interrupt::InterruptSourceGroup;
1315025d71SRob Bradford use vm_device::BusDevice;
1428ab6ceaSRob Bradford use vm_memory::GuestAddress;
155a187ee2SRob Bradford use vmm_sys_util::eventfd::EventFd;
165a187ee2SRob Bradford 
17*88a9f799SRob Bradford use super::AcpiNotificationFlags;
18*88a9f799SRob Bradford 
1928ab6ceaSRob Bradford pub const GED_DEVICE_ACPI_SIZE: usize = 0x1;
2028ab6ceaSRob Bradford 
215a187ee2SRob Bradford /// A device for handling ACPI shutdown and reboot
225a187ee2SRob Bradford pub struct AcpiShutdownDevice {
235a187ee2SRob Bradford     exit_evt: EventFd,
245a187ee2SRob Bradford     reset_evt: EventFd,
2506dc7085SRob Bradford     vcpus_kill_signalled: Arc<AtomicBool>,
265a187ee2SRob Bradford }
275a187ee2SRob Bradford 
285a187ee2SRob Bradford impl AcpiShutdownDevice {
295a187ee2SRob Bradford     /// Constructs a device that will signal the given event when the guest requests it.
new( exit_evt: EventFd, reset_evt: EventFd, vcpus_kill_signalled: Arc<AtomicBool>, ) -> AcpiShutdownDevice3006dc7085SRob Bradford     pub fn new(
3106dc7085SRob Bradford         exit_evt: EventFd,
3206dc7085SRob Bradford         reset_evt: EventFd,
3306dc7085SRob Bradford         vcpus_kill_signalled: Arc<AtomicBool>,
3406dc7085SRob Bradford     ) -> AcpiShutdownDevice {
355a187ee2SRob Bradford         AcpiShutdownDevice {
365a187ee2SRob Bradford             exit_evt,
375a187ee2SRob Bradford             reset_evt,
3806dc7085SRob Bradford             vcpus_kill_signalled,
395a187ee2SRob Bradford         }
405a187ee2SRob Bradford     }
415a187ee2SRob Bradford }
425a187ee2SRob Bradford 
435a187ee2SRob Bradford // Same I/O port used for shutdown and reboot
445a187ee2SRob Bradford impl BusDevice for AcpiShutdownDevice {
455a187ee2SRob Bradford     // Spec has all fields as zero
read(&mut self, _base: u64, _offset: u64, data: &mut [u8])465a187ee2SRob Bradford     fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
47a77160dcSRob Bradford         data.fill(0)
485a187ee2SRob Bradford     }
495a187ee2SRob Bradford 
write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>>501fc6d50fSRob Bradford     fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
515a187ee2SRob Bradford         if data[0] == 1 {
52968902dfSRob Bradford             info!("ACPI Reboot signalled");
535a187ee2SRob Bradford             if let Err(e) = self.reset_evt.write(1) {
545a187ee2SRob Bradford                 error!("Error triggering ACPI reset event: {}", e);
555a187ee2SRob Bradford             }
5606dc7085SRob Bradford             // Spin until we are sure the reset_evt has been handled and that when
5706dc7085SRob Bradford             // we return from the KVM_RUN we will exit rather than re-enter the guest.
5806dc7085SRob Bradford             while !self.vcpus_kill_signalled.load(Ordering::SeqCst) {
5906dc7085SRob Bradford                 // This is more effective than thread::yield_now() at
6006dc7085SRob Bradford                 // avoiding a priority inversion with the VMM thread
6106dc7085SRob Bradford                 thread::sleep(std::time::Duration::from_millis(1));
6206dc7085SRob Bradford             }
635a187ee2SRob Bradford         }
645a187ee2SRob Bradford         // The ACPI DSDT table specifies the S5 sleep state (shutdown) as value 5
655a187ee2SRob Bradford         const S5_SLEEP_VALUE: u8 = 5;
665a187ee2SRob Bradford         const SLEEP_STATUS_EN_BIT: u8 = 5;
675a187ee2SRob Bradford         const SLEEP_VALUE_BIT: u8 = 2;
685a187ee2SRob Bradford         if data[0] == (S5_SLEEP_VALUE << SLEEP_VALUE_BIT) | (1 << SLEEP_STATUS_EN_BIT) {
69968902dfSRob Bradford             info!("ACPI Shutdown signalled");
705a187ee2SRob Bradford             if let Err(e) = self.exit_evt.write(1) {
715a187ee2SRob Bradford                 error!("Error triggering ACPI shutdown event: {}", e);
725a187ee2SRob Bradford             }
7306dc7085SRob Bradford             // Spin until we are sure the reset_evt has been handled and that when
7406dc7085SRob Bradford             // we return from the KVM_RUN we will exit rather than re-enter the guest.
7506dc7085SRob Bradford             while !self.vcpus_kill_signalled.load(Ordering::SeqCst) {
7606dc7085SRob Bradford                 // This is more effective than thread::yield_now() at
7706dc7085SRob Bradford                 // avoiding a priority inversion with the VMM thread
7806dc7085SRob Bradford                 thread::sleep(std::time::Duration::from_millis(1));
7906dc7085SRob Bradford             }
805a187ee2SRob Bradford         }
811fc6d50fSRob Bradford         None
825a187ee2SRob Bradford     }
835a187ee2SRob Bradford }
84623755ccSRob Bradford 
85623755ccSRob Bradford /// A device for handling ACPI GED event generation
86c5d15fd9SRob Bradford pub struct AcpiGedDevice {
87dcc646f5SSebastien Boeuf     interrupt: Arc<dyn InterruptSourceGroup>,
887b376fa8SRob Bradford     notification_type: AcpiNotificationFlags,
89ba59c620SRob Bradford     ged_irq: u32,
9028ab6ceaSRob Bradford     address: GuestAddress,
91623755ccSRob Bradford }
92623755ccSRob Bradford 
93c5d15fd9SRob Bradford impl AcpiGedDevice {
new( interrupt: Arc<dyn InterruptSourceGroup>, ged_irq: u32, address: GuestAddress, ) -> AcpiGedDevice9428ab6ceaSRob Bradford     pub fn new(
95dcc646f5SSebastien Boeuf         interrupt: Arc<dyn InterruptSourceGroup>,
9628ab6ceaSRob Bradford         ged_irq: u32,
9728ab6ceaSRob Bradford         address: GuestAddress,
98c5d15fd9SRob Bradford     ) -> AcpiGedDevice {
99c5d15fd9SRob Bradford         AcpiGedDevice {
100623755ccSRob Bradford             interrupt,
1017b376fa8SRob Bradford             notification_type: AcpiNotificationFlags::NO_DEVICES_CHANGED,
102ba59c620SRob Bradford             ged_irq,
10328ab6ceaSRob Bradford             address,
104623755ccSRob Bradford         }
105623755ccSRob Bradford     }
106623755ccSRob Bradford 
notify( &mut self, notification_type: AcpiNotificationFlags, ) -> Result<(), std::io::Error>107623755ccSRob Bradford     pub fn notify(
108623755ccSRob Bradford         &mut self,
1097b376fa8SRob Bradford         notification_type: AcpiNotificationFlags,
110623755ccSRob Bradford     ) -> Result<(), std::io::Error> {
1117310ab6fSRob Bradford         self.notification_type |= notification_type;
112bb8cd9ebSSebastien Boeuf         self.interrupt.trigger(0)
113623755ccSRob Bradford     }
114ba59c620SRob Bradford 
irq(&self) -> u32115ba59c620SRob Bradford     pub fn irq(&self) -> u32 {
116ba59c620SRob Bradford         self.ged_irq
117ba59c620SRob Bradford     }
118623755ccSRob Bradford }
119623755ccSRob Bradford 
12057ed0069SRob Bradford // I/O port reports what type of notification was made
121c5d15fd9SRob Bradford impl BusDevice for AcpiGedDevice {
122623755ccSRob Bradford     // Spec has all fields as zero
read(&mut self, _base: u64, _offset: u64, data: &mut [u8])123623755ccSRob Bradford     fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
1247310ab6fSRob Bradford         data[0] = self.notification_type.bits();
1257b376fa8SRob Bradford         self.notification_type = AcpiNotificationFlags::NO_DEVICES_CHANGED;
126623755ccSRob Bradford     }
127623755ccSRob Bradford }
1289de3ace8SQiu Wenbo 
129c5d15fd9SRob Bradford impl Aml for AcpiGedDevice {
to_aml_bytes(&self, sink: &mut dyn AmlSink)13073c41567SRob Bradford     fn to_aml_bytes(&self, sink: &mut dyn AmlSink) {
1319de3ace8SQiu Wenbo         aml::Device::new(
1324465bbafSRob Bradford             "_SB_.GEC_".into(),
1339de3ace8SQiu Wenbo             vec![
13473c41567SRob Bradford                 &aml::Name::new("_HID".into(), &aml::EISAName::new("PNP0A06")),
1354465bbafSRob Bradford                 &aml::Name::new("_UID".into(), &"Generic Event Controller"),
1369de3ace8SQiu Wenbo                 &aml::Name::new(
1379de3ace8SQiu Wenbo                     "_CRS".into(),
1384465bbafSRob Bradford                     &aml::ResourceTemplate::new(vec![&aml::AddressSpace::new_memory(
1394548de19SRob Bradford                         aml::AddressSpaceCacheable::NotCacheable,
1409de3ace8SQiu Wenbo                         true,
141a9ec0f33SBo Chen                         self.address.0,
1424465bbafSRob Bradford                         self.address.0 + GED_DEVICE_ACPI_SIZE as u64 - 1,
143f485922bSRob Bradford                         None,
1449de3ace8SQiu Wenbo                     )]),
1459de3ace8SQiu Wenbo                 ),
14628ab6ceaSRob Bradford                 &aml::OpRegion::new(
14728ab6ceaSRob Bradford                     "GDST".into(),
14828ab6ceaSRob Bradford                     aml::OpRegionSpace::SystemMemory,
14973c41567SRob Bradford                     &(self.address.0 as usize),
15073c41567SRob Bradford                     &GED_DEVICE_ACPI_SIZE,
15128ab6ceaSRob Bradford                 ),
1529de3ace8SQiu Wenbo                 &aml::Field::new(
1539de3ace8SQiu Wenbo                     "GDST".into(),
1549de3ace8SQiu Wenbo                     aml::FieldAccessType::Byte,
15573c41567SRob Bradford                     aml::FieldLockRule::NoLock,
1569de3ace8SQiu Wenbo                     aml::FieldUpdateRule::WriteAsZeroes,
1579de3ace8SQiu Wenbo                     vec![aml::FieldEntry::Named(*b"GDAT", 8)],
1589de3ace8SQiu Wenbo                 ),
1599de3ace8SQiu Wenbo                 &aml::Method::new(
1604465bbafSRob Bradford                     "ESCN".into(),
1614465bbafSRob Bradford                     0,
1629de3ace8SQiu Wenbo                     true,
1639de3ace8SQiu Wenbo                     vec![
1649de3ace8SQiu Wenbo                         &aml::Store::new(&aml::Local(0), &aml::Path::new("GDAT")),
1659de3ace8SQiu Wenbo                         &aml::And::new(&aml::Local(1), &aml::Local(0), &aml::ONE),
1669de3ace8SQiu Wenbo                         &aml::If::new(
1679de3ace8SQiu Wenbo                             &aml::Equal::new(&aml::Local(1), &aml::ONE),
1689de3ace8SQiu Wenbo                             vec![&aml::MethodCall::new("\\_SB_.CPUS.CSCN".into(), vec![])],
1699de3ace8SQiu Wenbo                         ),
1709de3ace8SQiu Wenbo                         &aml::And::new(&aml::Local(1), &aml::Local(0), &2usize),
1719de3ace8SQiu Wenbo                         &aml::If::new(
1729de3ace8SQiu Wenbo                             &aml::Equal::new(&aml::Local(1), &2usize),
1739de3ace8SQiu Wenbo                             vec![&aml::MethodCall::new("\\_SB_.MHPC.MSCN".into(), vec![])],
1749de3ace8SQiu Wenbo                         ),
1752eb26d4dSSebastien Boeuf                         &aml::And::new(&aml::Local(1), &aml::Local(0), &4usize),
1762eb26d4dSSebastien Boeuf                         &aml::If::new(
1772eb26d4dSSebastien Boeuf                             &aml::Equal::new(&aml::Local(1), &4usize),
1787a4606f8SRob Bradford                             vec![&aml::MethodCall::new("\\_SB_.PHPR.PSCN".into(), vec![])],
1792eb26d4dSSebastien Boeuf                         ),
180c381b36bSRob Bradford                         &aml::And::new(&aml::Local(1), &aml::Local(0), &8usize),
181c381b36bSRob Bradford                         &aml::If::new(
182c381b36bSRob Bradford                             &aml::Equal::new(&aml::Local(1), &8usize),
183c381b36bSRob Bradford                             vec![&aml::Notify::new(
184c381b36bSRob Bradford                                 &aml::Path::new("\\_SB_.PWRB"),
185c381b36bSRob Bradford                                 &0x80usize,
186c381b36bSRob Bradford                             )],
187c381b36bSRob Bradford                         ),
1889de3ace8SQiu Wenbo                     ],
1899de3ace8SQiu Wenbo                 ),
1909de3ace8SQiu Wenbo             ],
1919de3ace8SQiu Wenbo         )
19273c41567SRob Bradford         .to_aml_bytes(sink);
1934465bbafSRob Bradford         aml::Device::new(
1944465bbafSRob Bradford             "_SB_.GED_".into(),
1954465bbafSRob Bradford             vec![
1964465bbafSRob Bradford                 &aml::Name::new("_HID".into(), &"ACPI0013"),
1974465bbafSRob Bradford                 &aml::Name::new("_UID".into(), &aml::ZERO),
1984465bbafSRob Bradford                 &aml::Name::new(
1994465bbafSRob Bradford                     "_CRS".into(),
2004465bbafSRob Bradford                     &aml::ResourceTemplate::new(vec![&aml::Interrupt::new(
2014465bbafSRob Bradford                         true,
2024465bbafSRob Bradford                         true,
2034465bbafSRob Bradford                         false,
2044465bbafSRob Bradford                         false,
2054465bbafSRob Bradford                         self.ged_irq,
2064465bbafSRob Bradford                     )]),
2074465bbafSRob Bradford                 ),
2084465bbafSRob Bradford                 &aml::Method::new(
2094465bbafSRob Bradford                     "_EVT".into(),
2104465bbafSRob Bradford                     1,
2114465bbafSRob Bradford                     true,
2124465bbafSRob Bradford                     vec![&aml::MethodCall::new("\\_SB_.GEC_.ESCN".into(), vec![])],
2134465bbafSRob Bradford                 ),
2144465bbafSRob Bradford             ],
2154465bbafSRob Bradford         )
21673c41567SRob Bradford         .to_aml_bytes(sink)
2179de3ace8SQiu Wenbo     }
2189de3ace8SQiu Wenbo }
219aae5d988SRob Bradford 
220c5d15fd9SRob Bradford pub struct AcpiPmTimerDevice {
221aae5d988SRob Bradford     start: Instant,
222aae5d988SRob Bradford }
223aae5d988SRob Bradford 
224c5d15fd9SRob Bradford impl AcpiPmTimerDevice {
new() -> Self225aae5d988SRob Bradford     pub fn new() -> Self {
226aae5d988SRob Bradford         Self {
227aae5d988SRob Bradford             start: Instant::now(),
228aae5d988SRob Bradford         }
229aae5d988SRob Bradford     }
230aae5d988SRob Bradford }
231aae5d988SRob Bradford 
232c5d15fd9SRob Bradford impl Default for AcpiPmTimerDevice {
default() -> Self233aae5d988SRob Bradford     fn default() -> Self {
234aae5d988SRob Bradford         Self::new()
235aae5d988SRob Bradford     }
236aae5d988SRob Bradford }
237aae5d988SRob Bradford 
238c5d15fd9SRob Bradford impl BusDevice for AcpiPmTimerDevice {
read(&mut self, _base: u64, _offset: u64, data: &mut [u8])239aae5d988SRob Bradford     fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
240a77160dcSRob Bradford         if data.len() != std::mem::size_of::<u32>() {
241a77160dcSRob Bradford             warn!("Invalid sized read of PM timer: {}", data.len());
242a77160dcSRob Bradford             return;
243a77160dcSRob Bradford         }
244aae5d988SRob Bradford         let now = Instant::now();
245aae5d988SRob Bradford         let since = now.duration_since(self.start);
246aae5d988SRob Bradford         let nanos = since.as_nanos();
247aae5d988SRob Bradford 
248aae5d988SRob Bradford         const PM_TIMER_FREQUENCY_HZ: u128 = 3_579_545;
249aae5d988SRob Bradford         const NANOS_PER_SECOND: u128 = 1_000_000_000;
250aae5d988SRob Bradford 
251aae5d988SRob Bradford         let counter = (nanos * PM_TIMER_FREQUENCY_HZ) / NANOS_PER_SECOND;
252aae5d988SRob Bradford         let counter: u32 = (counter & 0xffff_ffff) as u32;
253aae5d988SRob Bradford 
254aae5d988SRob Bradford         data.copy_from_slice(&counter.to_le_bytes());
255aae5d988SRob Bradford     }
256aae5d988SRob Bradford }
257