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 #[cfg(feature = "acpi")] 106 impl Aml for AcpiGedDevice { 107 fn to_aml_bytes(&self) -> Vec<u8> { 108 aml::Device::new( 109 "_SB_.GED_".into(), 110 vec![ 111 &aml::Name::new("_HID".into(), &"ACPI0013"), 112 &aml::Name::new("_UID".into(), &aml::ZERO), 113 &aml::Name::new( 114 "_CRS".into(), 115 &aml::ResourceTemplate::new(vec![&aml::Interrupt::new( 116 true, 117 true, 118 false, 119 false, 120 self.ged_irq, 121 )]), 122 ), 123 &aml::OpRegion::new( 124 "GDST".into(), 125 aml::OpRegionSpace::SystemMemory, 126 self.address.0 as usize, 127 GED_DEVICE_ACPI_SIZE, 128 ), 129 &aml::Field::new( 130 "GDST".into(), 131 aml::FieldAccessType::Byte, 132 aml::FieldUpdateRule::WriteAsZeroes, 133 vec![aml::FieldEntry::Named(*b"GDAT", 8)], 134 ), 135 &aml::Method::new( 136 "_EVT".into(), 137 1, 138 true, 139 vec![ 140 &aml::Store::new(&aml::Local(0), &aml::Path::new("GDAT")), 141 &aml::And::new(&aml::Local(1), &aml::Local(0), &aml::ONE), 142 &aml::If::new( 143 &aml::Equal::new(&aml::Local(1), &aml::ONE), 144 vec![&aml::MethodCall::new("\\_SB_.CPUS.CSCN".into(), vec![])], 145 ), 146 &aml::And::new(&aml::Local(1), &aml::Local(0), &2usize), 147 &aml::If::new( 148 &aml::Equal::new(&aml::Local(1), &2usize), 149 vec![&aml::MethodCall::new("\\_SB_.MHPC.MSCN".into(), vec![])], 150 ), 151 &aml::And::new(&aml::Local(1), &aml::Local(0), &4usize), 152 &aml::If::new( 153 &aml::Equal::new(&aml::Local(1), &4usize), 154 vec![&aml::MethodCall::new("\\_SB_.PCI0.PCNT".into(), vec![])], 155 ), 156 &aml::And::new(&aml::Local(1), &aml::Local(0), &8usize), 157 &aml::If::new( 158 &aml::Equal::new(&aml::Local(1), &8usize), 159 vec![&aml::Notify::new( 160 &aml::Path::new("\\_SB_.PWRB"), 161 &0x80usize, 162 )], 163 ), 164 ], 165 ), 166 ], 167 ) 168 .to_aml_bytes() 169 } 170 } 171 172 pub struct AcpiPmTimerDevice { 173 start: Instant, 174 } 175 176 impl AcpiPmTimerDevice { 177 pub fn new() -> Self { 178 Self { 179 start: Instant::now(), 180 } 181 } 182 } 183 184 impl Default for AcpiPmTimerDevice { 185 fn default() -> Self { 186 Self::new() 187 } 188 } 189 190 impl BusDevice for AcpiPmTimerDevice { 191 fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) { 192 if data.len() != std::mem::size_of::<u32>() { 193 warn!("Invalid sized read of PM timer: {}", data.len()); 194 return; 195 } 196 let now = Instant::now(); 197 let since = now.duration_since(self.start); 198 let nanos = since.as_nanos(); 199 200 const PM_TIMER_FREQUENCY_HZ: u128 = 3_579_545; 201 const NANOS_PER_SECOND: u128 = 1_000_000_000; 202 203 let counter = (nanos * PM_TIMER_FREQUENCY_HZ) / NANOS_PER_SECOND; 204 let counter: u32 = (counter & 0xffff_ffff) as u32; 205 206 data.copy_from_slice(&counter.to_le_bytes()); 207 } 208 } 209