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