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