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<Box<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<Box<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