1 // Copyright © 2019 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 use std::sync::atomic::{AtomicBool, Ordering}; 7 use std::sync::{Arc, Barrier}; 8 use std::thread; 9 use std::time::Instant; 10 11 use acpi_tables::{aml, Aml, AmlSink}; 12 use vm_device::interrupt::InterruptSourceGroup; 13 use vm_device::BusDevice; 14 use vm_memory::GuestAddress; 15 use vmm_sys_util::eventfd::EventFd; 16 17 use super::AcpiNotificationFlags; 18 19 pub const GED_DEVICE_ACPI_SIZE: usize = 0x1; 20 21 /// A device for handling ACPI shutdown and reboot 22 pub struct AcpiShutdownDevice { 23 exit_evt: EventFd, 24 reset_evt: EventFd, 25 vcpus_kill_signalled: Arc<AtomicBool>, 26 } 27 28 impl AcpiShutdownDevice { 29 /// Constructs a device that will signal the given event when the guest requests it. 30 pub fn new( 31 exit_evt: EventFd, 32 reset_evt: EventFd, 33 vcpus_kill_signalled: Arc<AtomicBool>, 34 ) -> AcpiShutdownDevice { 35 AcpiShutdownDevice { 36 exit_evt, 37 reset_evt, 38 vcpus_kill_signalled, 39 } 40 } 41 } 42 43 // Same I/O port used for shutdown and reboot 44 impl BusDevice for AcpiShutdownDevice { 45 // Spec has all fields as zero 46 fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) { 47 data.fill(0) 48 } 49 50 fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>> { 51 if data[0] == 1 { 52 info!("ACPI Reboot signalled"); 53 if let Err(e) = self.reset_evt.write(1) { 54 error!("Error triggering ACPI reset event: {}", e); 55 } 56 // Spin until we are sure the reset_evt has been handled and that when 57 // we return from the KVM_RUN we will exit rather than re-enter the guest. 58 while !self.vcpus_kill_signalled.load(Ordering::SeqCst) { 59 // This is more effective than thread::yield_now() at 60 // avoiding a priority inversion with the VMM thread 61 thread::sleep(std::time::Duration::from_millis(1)); 62 } 63 } 64 // The ACPI DSDT table specifies the S5 sleep state (shutdown) as value 5 65 const S5_SLEEP_VALUE: u8 = 5; 66 const SLEEP_STATUS_EN_BIT: u8 = 5; 67 const SLEEP_VALUE_BIT: u8 = 2; 68 if data[0] == (S5_SLEEP_VALUE << SLEEP_VALUE_BIT) | (1 << SLEEP_STATUS_EN_BIT) { 69 info!("ACPI Shutdown signalled"); 70 if let Err(e) = self.exit_evt.write(1) { 71 error!("Error triggering ACPI shutdown event: {}", e); 72 } 73 // Spin until we are sure the reset_evt has been handled and that when 74 // we return from the KVM_RUN we will exit rather than re-enter the guest. 75 while !self.vcpus_kill_signalled.load(Ordering::SeqCst) { 76 // This is more effective than thread::yield_now() at 77 // avoiding a priority inversion with the VMM thread 78 thread::sleep(std::time::Duration::from_millis(1)); 79 } 80 } 81 None 82 } 83 } 84 85 /// A device for handling ACPI GED event generation 86 pub struct AcpiGedDevice { 87 interrupt: Arc<dyn InterruptSourceGroup>, 88 notification_type: AcpiNotificationFlags, 89 ged_irq: u32, 90 address: GuestAddress, 91 } 92 93 impl AcpiGedDevice { 94 pub fn new( 95 interrupt: Arc<dyn InterruptSourceGroup>, 96 ged_irq: u32, 97 address: GuestAddress, 98 ) -> AcpiGedDevice { 99 AcpiGedDevice { 100 interrupt, 101 notification_type: AcpiNotificationFlags::NO_DEVICES_CHANGED, 102 ged_irq, 103 address, 104 } 105 } 106 107 pub fn notify( 108 &mut self, 109 notification_type: AcpiNotificationFlags, 110 ) -> Result<(), std::io::Error> { 111 self.notification_type |= notification_type; 112 self.interrupt.trigger(0) 113 } 114 115 pub fn irq(&self) -> u32 { 116 self.ged_irq 117 } 118 } 119 120 // I/O port reports what type of notification was made 121 impl BusDevice for AcpiGedDevice { 122 // Spec has all fields as zero 123 fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) { 124 data[0] = self.notification_type.bits(); 125 self.notification_type = AcpiNotificationFlags::NO_DEVICES_CHANGED; 126 } 127 } 128 129 impl Aml for AcpiGedDevice { 130 fn to_aml_bytes(&self, sink: &mut dyn AmlSink) { 131 aml::Device::new( 132 "_SB_.GEC_".into(), 133 vec![ 134 &aml::Name::new("_HID".into(), &aml::EISAName::new("PNP0A06")), 135 &aml::Name::new("_UID".into(), &"Generic Event Controller"), 136 &aml::Name::new( 137 "_CRS".into(), 138 &aml::ResourceTemplate::new(vec![&aml::AddressSpace::new_memory( 139 aml::AddressSpaceCacheable::NotCacheable, 140 true, 141 self.address.0, 142 self.address.0 + GED_DEVICE_ACPI_SIZE as u64 - 1, 143 None, 144 )]), 145 ), 146 &aml::OpRegion::new( 147 "GDST".into(), 148 aml::OpRegionSpace::SystemMemory, 149 &(self.address.0 as usize), 150 &GED_DEVICE_ACPI_SIZE, 151 ), 152 &aml::Field::new( 153 "GDST".into(), 154 aml::FieldAccessType::Byte, 155 aml::FieldLockRule::NoLock, 156 aml::FieldUpdateRule::WriteAsZeroes, 157 vec![aml::FieldEntry::Named(*b"GDAT", 8)], 158 ), 159 &aml::Method::new( 160 "ESCN".into(), 161 0, 162 true, 163 vec![ 164 &aml::Store::new(&aml::Local(0), &aml::Path::new("GDAT")), 165 &aml::And::new(&aml::Local(1), &aml::Local(0), &aml::ONE), 166 &aml::If::new( 167 &aml::Equal::new(&aml::Local(1), &aml::ONE), 168 vec![&aml::MethodCall::new("\\_SB_.CPUS.CSCN".into(), vec![])], 169 ), 170 &aml::And::new(&aml::Local(1), &aml::Local(0), &2usize), 171 &aml::If::new( 172 &aml::Equal::new(&aml::Local(1), &2usize), 173 vec![&aml::MethodCall::new("\\_SB_.MHPC.MSCN".into(), vec![])], 174 ), 175 &aml::And::new(&aml::Local(1), &aml::Local(0), &4usize), 176 &aml::If::new( 177 &aml::Equal::new(&aml::Local(1), &4usize), 178 vec![&aml::MethodCall::new("\\_SB_.PHPR.PSCN".into(), vec![])], 179 ), 180 &aml::And::new(&aml::Local(1), &aml::Local(0), &8usize), 181 &aml::If::new( 182 &aml::Equal::new(&aml::Local(1), &8usize), 183 vec![&aml::Notify::new( 184 &aml::Path::new("\\_SB_.PWRB"), 185 &0x80usize, 186 )], 187 ), 188 ], 189 ), 190 ], 191 ) 192 .to_aml_bytes(sink); 193 aml::Device::new( 194 "_SB_.GED_".into(), 195 vec![ 196 &aml::Name::new("_HID".into(), &"ACPI0013"), 197 &aml::Name::new("_UID".into(), &aml::ZERO), 198 &aml::Name::new( 199 "_CRS".into(), 200 &aml::ResourceTemplate::new(vec![&aml::Interrupt::new( 201 true, 202 true, 203 false, 204 false, 205 self.ged_irq, 206 )]), 207 ), 208 &aml::Method::new( 209 "_EVT".into(), 210 1, 211 true, 212 vec![&aml::MethodCall::new("\\_SB_.GEC_.ESCN".into(), vec![])], 213 ), 214 ], 215 ) 216 .to_aml_bytes(sink) 217 } 218 } 219 220 pub struct AcpiPmTimerDevice { 221 start: Instant, 222 } 223 224 impl AcpiPmTimerDevice { 225 pub fn new() -> Self { 226 Self { 227 start: Instant::now(), 228 } 229 } 230 } 231 232 impl Default for AcpiPmTimerDevice { 233 fn default() -> Self { 234 Self::new() 235 } 236 } 237 238 impl BusDevice for AcpiPmTimerDevice { 239 fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) { 240 if data.len() != std::mem::size_of::<u32>() { 241 warn!("Invalid sized read of PM timer: {}", data.len()); 242 return; 243 } 244 let now = Instant::now(); 245 let since = now.duration_since(self.start); 246 let nanos = since.as_nanos(); 247 248 const PM_TIMER_FREQUENCY_HZ: u128 = 3_579_545; 249 const NANOS_PER_SECOND: u128 = 1_000_000_000; 250 251 let counter = (nanos * PM_TIMER_FREQUENCY_HZ) / NANOS_PER_SECOND; 252 let counter: u32 = (counter & 0xffff_ffff) as u32; 253 254 data.copy_from_slice(&counter.to_le_bytes()); 255 } 256 } 257