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 data.fill(0) 38 } 39 40 fn write(&mut self, _base: u64, _offset: u64, data: &[u8]) -> Option<Arc<Barrier>> { 41 if data[0] == 1 { 42 info!("ACPI Reboot signalled"); 43 if let Err(e) = self.reset_evt.write(1) { 44 error!("Error triggering ACPI reset event: {}", e); 45 } 46 } 47 // The ACPI DSDT table specifies the S5 sleep state (shutdown) as value 5 48 const S5_SLEEP_VALUE: u8 = 5; 49 const SLEEP_STATUS_EN_BIT: u8 = 5; 50 const SLEEP_VALUE_BIT: u8 = 2; 51 if data[0] == (S5_SLEEP_VALUE << SLEEP_VALUE_BIT) | (1 << SLEEP_STATUS_EN_BIT) { 52 info!("ACPI Shutdown signalled"); 53 if let Err(e) = self.exit_evt.write(1) { 54 error!("Error triggering ACPI shutdown event: {}", e); 55 } 56 } 57 None 58 } 59 } 60 61 /// A device for handling ACPI GED event generation 62 pub struct AcpiGedDevice { 63 interrupt: Arc<dyn InterruptSourceGroup>, 64 notification_type: AcpiNotificationFlags, 65 ged_irq: u32, 66 address: GuestAddress, 67 } 68 69 impl AcpiGedDevice { 70 pub fn new( 71 interrupt: Arc<dyn InterruptSourceGroup>, 72 ged_irq: u32, 73 address: GuestAddress, 74 ) -> AcpiGedDevice { 75 AcpiGedDevice { 76 interrupt, 77 notification_type: AcpiNotificationFlags::NO_DEVICES_CHANGED, 78 ged_irq, 79 address, 80 } 81 } 82 83 pub fn notify( 84 &mut self, 85 notification_type: AcpiNotificationFlags, 86 ) -> Result<(), std::io::Error> { 87 self.notification_type |= notification_type; 88 self.interrupt.trigger(0) 89 } 90 91 pub fn irq(&self) -> u32 { 92 self.ged_irq 93 } 94 } 95 96 // I/O port reports what type of notification was made 97 impl BusDevice for AcpiGedDevice { 98 // Spec has all fields as zero 99 fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) { 100 data[0] = self.notification_type.bits(); 101 self.notification_type = AcpiNotificationFlags::NO_DEVICES_CHANGED; 102 } 103 } 104 105 impl Aml for AcpiGedDevice { 106 fn append_aml_bytes(&self, bytes: &mut Vec<u8>) { 107 aml::Device::new( 108 "_SB_.GEC_".into(), 109 vec![ 110 &aml::Name::new("_HID".into(), &aml::EisaName::new("PNP0A06")), 111 &aml::Name::new("_UID".into(), &"Generic Event Controller"), 112 &aml::Name::new( 113 "_CRS".into(), 114 &aml::ResourceTemplate::new(vec![&aml::AddressSpace::new_memory( 115 aml::AddressSpaceCachable::NotCacheable, 116 true, 117 self.address.0 as u64, 118 self.address.0 + GED_DEVICE_ACPI_SIZE as u64 - 1, 119 )]), 120 ), 121 &aml::OpRegion::new( 122 "GDST".into(), 123 aml::OpRegionSpace::SystemMemory, 124 self.address.0 as usize, 125 GED_DEVICE_ACPI_SIZE, 126 ), 127 &aml::Field::new( 128 "GDST".into(), 129 aml::FieldAccessType::Byte, 130 aml::FieldUpdateRule::WriteAsZeroes, 131 vec![aml::FieldEntry::Named(*b"GDAT", 8)], 132 ), 133 &aml::Method::new( 134 "ESCN".into(), 135 0, 136 true, 137 vec![ 138 &aml::Store::new(&aml::Local(0), &aml::Path::new("GDAT")), 139 &aml::And::new(&aml::Local(1), &aml::Local(0), &aml::ONE), 140 &aml::If::new( 141 &aml::Equal::new(&aml::Local(1), &aml::ONE), 142 vec![&aml::MethodCall::new("\\_SB_.CPUS.CSCN".into(), vec![])], 143 ), 144 &aml::And::new(&aml::Local(1), &aml::Local(0), &2usize), 145 &aml::If::new( 146 &aml::Equal::new(&aml::Local(1), &2usize), 147 vec![&aml::MethodCall::new("\\_SB_.MHPC.MSCN".into(), vec![])], 148 ), 149 &aml::And::new(&aml::Local(1), &aml::Local(0), &4usize), 150 &aml::If::new( 151 &aml::Equal::new(&aml::Local(1), &4usize), 152 vec![&aml::MethodCall::new("\\_SB_.PHPR.PSCN".into(), vec![])], 153 ), 154 &aml::And::new(&aml::Local(1), &aml::Local(0), &8usize), 155 &aml::If::new( 156 &aml::Equal::new(&aml::Local(1), &8usize), 157 vec![&aml::Notify::new( 158 &aml::Path::new("\\_SB_.PWRB"), 159 &0x80usize, 160 )], 161 ), 162 ], 163 ), 164 ], 165 ) 166 .append_aml_bytes(bytes); 167 aml::Device::new( 168 "_SB_.GED_".into(), 169 vec![ 170 &aml::Name::new("_HID".into(), &"ACPI0013"), 171 &aml::Name::new("_UID".into(), &aml::ZERO), 172 &aml::Name::new( 173 "_CRS".into(), 174 &aml::ResourceTemplate::new(vec![&aml::Interrupt::new( 175 true, 176 true, 177 false, 178 false, 179 self.ged_irq, 180 )]), 181 ), 182 &aml::Method::new( 183 "_EVT".into(), 184 1, 185 true, 186 vec![&aml::MethodCall::new("\\_SB_.GEC_.ESCN".into(), vec![])], 187 ), 188 ], 189 ) 190 .append_aml_bytes(bytes) 191 } 192 } 193 194 pub struct AcpiPmTimerDevice { 195 start: Instant, 196 } 197 198 impl AcpiPmTimerDevice { 199 pub fn new() -> Self { 200 Self { 201 start: Instant::now(), 202 } 203 } 204 } 205 206 impl Default for AcpiPmTimerDevice { 207 fn default() -> Self { 208 Self::new() 209 } 210 } 211 212 impl BusDevice for AcpiPmTimerDevice { 213 fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) { 214 if data.len() != std::mem::size_of::<u32>() { 215 warn!("Invalid sized read of PM timer: {}", data.len()); 216 return; 217 } 218 let now = Instant::now(); 219 let since = now.duration_since(self.start); 220 let nanos = since.as_nanos(); 221 222 const PM_TIMER_FREQUENCY_HZ: u128 = 3_579_545; 223 const NANOS_PER_SECOND: u128 = 1_000_000_000; 224 225 let counter = (nanos * PM_TIMER_FREQUENCY_HZ) / NANOS_PER_SECOND; 226 let counter: u32 = (counter & 0xffff_ffff) as u32; 227 228 data.copy_from_slice(&counter.to_le_bytes()); 229 } 230 } 231