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