1 // Copyright 2019 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 use std::collections::BTreeMap;
6 use std::str::FromStr;
7 use std::sync::atomic::AtomicBool;
8 use std::sync::atomic::Ordering;
9 use std::sync::Arc;
10 use std::time::Instant;
11
12 use acpi_tables::aml;
13 use acpi_tables::aml::Aml;
14 use anyhow::bail;
15 use anyhow::Context;
16 use base::custom_serde::serialize_arc_mutex;
17 use base::error;
18 use base::warn;
19 use base::Error as SysError;
20 use base::Event;
21 use base::EventToken;
22 use base::SendTube;
23 use base::Tube;
24 use base::VmEventType;
25 use base::WaitContext;
26 use base::WorkerThread;
27 use metrics::log_metric;
28 use metrics::MetricEventType;
29 use serde::Deserialize;
30 use serde::Serialize;
31 use sync::Mutex;
32 use thiserror::Error;
33 use vm_control::GpeNotify;
34 use vm_control::PmResource;
35 use vm_control::PmeNotify;
36 use vm_control::VmRequest;
37 use vm_control::VmResponse;
38
39 use crate::ac_adapter::AcAdapter;
40 use crate::pci::pm::PmConfig;
41 use crate::pci::CrosvmDeviceId;
42 use crate::BusAccessInfo;
43 use crate::BusDevice;
44 use crate::BusResumeDevice;
45 use crate::DeviceId;
46 use crate::IrqLevelEvent;
47 use crate::Suspendable;
48
49 #[derive(Error, Debug)]
50 pub enum ACPIPMError {
51 /// Creating WaitContext failed.
52 #[error("failed to create wait context: {0}")]
53 CreateWaitContext(SysError),
54 /// Error while waiting for events.
55 #[error("failed to wait for events: {0}")]
56 WaitError(SysError),
57 #[error("Did not find group_id corresponding to acpi_mc_group")]
58 AcpiMcGroupError,
59 #[error("Failed to create and bind NETLINK_GENERIC socket for acpi_mc_group: {0}")]
60 AcpiEventSockError(base::Error),
61 #[error("GPE {0} is out of bound")]
62 GpeOutOfBound(u32),
63 }
64
65 #[derive(Debug, Copy, Clone, Serialize, Deserialize)]
66 pub enum ACPIPMFixedEvent {
67 GlobalLock,
68 PowerButton,
69 SleepButton,
70 RTC,
71 }
72
73 #[derive(Serialize, Deserialize, Clone)]
74 pub(crate) struct Pm1Resource {
75 pub(crate) status: u16,
76 enable: u16,
77 control: u16,
78 }
79
80 #[derive(Serialize, Deserialize, Clone)]
81 pub(crate) struct GpeResource {
82 pub(crate) status: [u8; ACPIPM_RESOURCE_GPE0_BLK_LEN as usize / 2],
83 enable: [u8; ACPIPM_RESOURCE_GPE0_BLK_LEN as usize / 2],
84 #[serde(skip_serializing, skip_deserializing)]
85 pub(crate) gpe_notify: BTreeMap<u32, Vec<Arc<Mutex<dyn GpeNotify>>>>,
86 }
87
88 #[derive(Serialize, Deserialize, Clone)]
89 pub(crate) struct PciResource {
90 #[serde(skip_serializing, skip_deserializing)]
91 pub(crate) pme_notify: BTreeMap<u8, Vec<Arc<Mutex<dyn PmeNotify>>>>,
92 }
93
94 /// ACPI PM resource for handling OS suspend/resume request
95 #[allow(dead_code)]
96 #[derive(Serialize)]
97 pub struct ACPIPMResource {
98 // This is SCI interrupt that will be raised in the VM.
99 #[serde(skip_serializing)]
100 sci_evt: IrqLevelEvent,
101 #[serde(skip_serializing)]
102 worker_thread: Option<WorkerThread<()>>,
103 #[serde(skip_serializing)]
104 suspend_evt: Event,
105 #[serde(skip_serializing)]
106 exit_evt_wrtube: SendTube,
107 #[serde(serialize_with = "serialize_arc_mutex")]
108 pm1: Arc<Mutex<Pm1Resource>>,
109 #[serde(serialize_with = "serialize_arc_mutex")]
110 gpe0: Arc<Mutex<GpeResource>>,
111 #[serde(serialize_with = "serialize_arc_mutex")]
112 pci: Arc<Mutex<PciResource>>,
113 #[serde(skip_serializing)]
114 acdc: Option<Arc<Mutex<AcAdapter>>>,
115 }
116
117 #[derive(Deserialize)]
118 struct ACPIPMResrourceSerializable {
119 pm1: Pm1Resource,
120 gpe0: GpeResource,
121 }
122
123 impl ACPIPMResource {
124 /// Constructs ACPI Power Management Resouce.
125 #[allow(dead_code)]
new( sci_evt: IrqLevelEvent, suspend_evt: Event, exit_evt_wrtube: SendTube, acdc: Option<Arc<Mutex<AcAdapter>>>, ) -> ACPIPMResource126 pub fn new(
127 sci_evt: IrqLevelEvent,
128 suspend_evt: Event,
129 exit_evt_wrtube: SendTube,
130 acdc: Option<Arc<Mutex<AcAdapter>>>,
131 ) -> ACPIPMResource {
132 let pm1 = Pm1Resource {
133 status: 0,
134 enable: 0,
135 control: 0,
136 };
137 let gpe0 = GpeResource {
138 status: Default::default(),
139 enable: Default::default(),
140 gpe_notify: BTreeMap::new(),
141 };
142 let pci = PciResource {
143 pme_notify: BTreeMap::new(),
144 };
145
146 ACPIPMResource {
147 sci_evt,
148 worker_thread: None,
149 suspend_evt,
150 exit_evt_wrtube,
151 pm1: Arc::new(Mutex::new(pm1)),
152 gpe0: Arc::new(Mutex::new(gpe0)),
153 pci: Arc::new(Mutex::new(pci)),
154 acdc,
155 }
156 }
157
start(&mut self)158 pub fn start(&mut self) {
159 let sci_evt = self.sci_evt.try_clone().expect("failed to clone event");
160 let pm1 = self.pm1.clone();
161 let gpe0 = self.gpe0.clone();
162 let acdc = self.acdc.clone();
163
164 let acpi_event_ignored_gpe = Vec::new();
165
166 self.worker_thread = Some(WorkerThread::start("ACPI PM worker", move |kill_evt| {
167 if let Err(e) = run_worker(sci_evt, kill_evt, pm1, gpe0, acpi_event_ignored_gpe, acdc) {
168 error!("{}", e);
169 }
170 }));
171 }
172 }
173
174 impl Suspendable for ACPIPMResource {
snapshot(&mut self) -> anyhow::Result<serde_json::Value>175 fn snapshot(&mut self) -> anyhow::Result<serde_json::Value> {
176 serde_json::to_value(&self)
177 .with_context(|| format!("error serializing {}", self.debug_label()))
178 }
179
restore(&mut self, data: serde_json::Value) -> anyhow::Result<()>180 fn restore(&mut self, data: serde_json::Value) -> anyhow::Result<()> {
181 let acpi_snapshot: ACPIPMResrourceSerializable = serde_json::from_value(data)
182 .with_context(|| format!("error deserializing {}", self.debug_label()))?;
183 {
184 let mut pm1 = self.pm1.lock();
185 *pm1 = acpi_snapshot.pm1;
186 }
187 {
188 let mut gpe0 = self.gpe0.lock();
189 gpe0.status = acpi_snapshot.gpe0.status;
190 gpe0.enable = acpi_snapshot.gpe0.enable;
191 }
192 Ok(())
193 }
194
sleep(&mut self) -> anyhow::Result<()>195 fn sleep(&mut self) -> anyhow::Result<()> {
196 if let Some(worker_thread) = self.worker_thread.take() {
197 worker_thread.stop();
198 }
199 Ok(())
200 }
201
wake(&mut self) -> anyhow::Result<()>202 fn wake(&mut self) -> anyhow::Result<()> {
203 self.start();
204 Ok(())
205 }
206 }
207
run_worker( sci_evt: IrqLevelEvent, kill_evt: Event, pm1: Arc<Mutex<Pm1Resource>>, gpe0: Arc<Mutex<GpeResource>>, acpi_event_ignored_gpe: Vec<u32>, arced_ac_adapter: Option<Arc<Mutex<AcAdapter>>>, ) -> Result<(), ACPIPMError>208 fn run_worker(
209 sci_evt: IrqLevelEvent,
210 kill_evt: Event,
211 pm1: Arc<Mutex<Pm1Resource>>,
212 gpe0: Arc<Mutex<GpeResource>>,
213 acpi_event_ignored_gpe: Vec<u32>,
214 arced_ac_adapter: Option<Arc<Mutex<AcAdapter>>>,
215 ) -> Result<(), ACPIPMError> {
216 let acpi_event_sock = crate::sys::get_acpi_event_sock()?;
217 #[derive(EventToken)]
218 enum Token {
219 AcpiEvent,
220 InterruptResample,
221 Kill,
222 }
223
224 let wait_ctx: WaitContext<Token> = WaitContext::build_with(&[
225 (sci_evt.get_resample(), Token::InterruptResample),
226 (&kill_evt, Token::Kill),
227 ])
228 .map_err(ACPIPMError::CreateWaitContext)?;
229 if let Some(acpi_event_sock) = &acpi_event_sock {
230 wait_ctx
231 .add(acpi_event_sock, Token::AcpiEvent)
232 .map_err(ACPIPMError::CreateWaitContext)?;
233 }
234
235 loop {
236 let events = wait_ctx.wait().map_err(ACPIPMError::WaitError)?;
237 for event in events.iter().filter(|e| e.is_readable) {
238 match event.token {
239 Token::AcpiEvent => {
240 crate::sys::acpi_event_run(
241 &sci_evt,
242 &acpi_event_sock,
243 &gpe0,
244 &acpi_event_ignored_gpe,
245 &arced_ac_adapter,
246 );
247 }
248 Token::InterruptResample => {
249 sci_evt.clear_resample();
250
251 // Re-trigger SCI if PM1 or GPE status is still not cleared.
252 pm1.lock().trigger_sci(&sci_evt);
253 gpe0.lock().trigger_sci(&sci_evt);
254 }
255 Token::Kill => return Ok(()),
256 }
257 }
258 }
259 }
260
261 impl Pm1Resource {
trigger_sci(&self, sci_evt: &IrqLevelEvent)262 fn trigger_sci(&self, sci_evt: &IrqLevelEvent) {
263 if self.status & self.enable & ACPIPMFixedEvent::bitmask_all() != 0 {
264 if let Err(e) = sci_evt.trigger() {
265 error!("ACPIPM: failed to trigger sci event for pm1: {}", e);
266 }
267 }
268 }
269 }
270
271 impl GpeResource {
trigger_sci(&self, sci_evt: &IrqLevelEvent)272 pub fn trigger_sci(&self, sci_evt: &IrqLevelEvent) {
273 if (0..self.status.len()).any(|i| self.status[i] & self.enable[i] != 0) {
274 if let Err(e) = sci_evt.trigger() {
275 error!("ACPIPM: failed to trigger sci event for gpe: {}", e);
276 }
277 }
278 }
279
set_active(&mut self, gpe: u32) -> Result<(), ACPIPMError>280 pub fn set_active(&mut self, gpe: u32) -> Result<(), ACPIPMError> {
281 if let Some(status_byte) = self.status.get_mut(gpe as usize / 8) {
282 *status_byte |= 1 << (gpe % 8);
283 } else {
284 return Err(ACPIPMError::GpeOutOfBound(gpe));
285 }
286 Ok(())
287 }
288 }
289
290 /// the ACPI PM register length.
291 pub const ACPIPM_RESOURCE_EVENTBLK_LEN: u8 = 4;
292 pub const ACPIPM_RESOURCE_CONTROLBLK_LEN: u8 = 2;
293 pub const ACPIPM_RESOURCE_GPE0_BLK_LEN: u8 = 64;
294 pub const ACPIPM_RESOURCE_LEN: u8 = ACPIPM_RESOURCE_EVENTBLK_LEN + 4 + ACPIPM_RESOURCE_GPE0_BLK_LEN;
295
296 // Should be in sync with gpe_allocator range
297 pub const ACPIPM_GPE_MAX: u16 = ACPIPM_RESOURCE_GPE0_BLK_LEN as u16 / 2 * 8 - 1;
298
299 /// ACPI PM register value definitions
300
301 /// 4.8.4.1.1 PM1 Status Registers, ACPI Spec Version 6.4
302 /// Register Location: <PM1a_EVT_BLK / PM1b_EVT_BLK> System I/O or Memory Space (defined in FADT)
303 /// Size: PM1_EVT_LEN / 2 (defined in FADT)
304 const PM1_STATUS: u16 = 0;
305
306 /// 4.8.4.1.2 PM1Enable Registers, ACPI Spec Version 6.4
307 /// Register Location: <<PM1a_EVT_BLK / PM1b_EVT_BLK> + PM1_EVT_LEN / 2 System I/O or Memory Space
308 /// (defined in FADT)
309 /// Size: PM1_EVT_LEN / 2 (defined in FADT)
310 const PM1_ENABLE: u16 = PM1_STATUS + (ACPIPM_RESOURCE_EVENTBLK_LEN as u16 / 2);
311
312 /// 4.8.4.2.1 PM1 Control Registers, ACPI Spec Version 6.4
313 /// Register Location: <PM1a_CNT_BLK / PM1b_CNT_BLK> System I/O or Memory Space (defined in FADT)
314 /// Size: PM1_CNT_LEN (defined in FADT)
315 const PM1_CONTROL: u16 = PM1_STATUS + ACPIPM_RESOURCE_EVENTBLK_LEN as u16;
316
317 /// 4.8.5.1 General-Purpose Event Register Blocks, ACPI Spec Version 6.4
318 /// - Each register block contains two registers: an enable and a status register.
319 /// - Each register block is 32-bit aligned.
320 /// - Each register in the block is accessed as a byte.
321
322 /// 4.8.5.1.1 General-Purpose Event 0 Register Block, ACPI Spec Version 6.4
323 /// This register block consists of two registers: The GPE0_STS and the GPE0_EN registers. Each
324 /// register’s length is defined to be half the length of the GPE0 register block, and is described
325 /// in the ACPI FADT’s GPE0_BLK and GPE0_BLK_LEN operators.
326
327 /// 4.8.5.1.1.1 General-Purpose Event 0 Status Register, ACPI Spec Version 6.4
328 /// Register Location: <GPE0_STS> System I/O or System Memory Space (defined in FADT)
329 /// Size: GPE0_BLK_LEN/2 (defined in FADT)
330 const GPE0_STATUS: u16 = PM1_STATUS + ACPIPM_RESOURCE_EVENTBLK_LEN as u16 + 4; // ensure alignment
331
332 /// 4.8.5.1.1.2 General-Purpose Event 0 Enable Register, ACPI Spec Version 6.4
333 /// Register Location: <GPE0_EN> System I/O or System Memory Space (defined in FADT)
334 /// Size: GPE0_BLK_LEN/2 (defined in FADT)
335 const GPE0_ENABLE: u16 = GPE0_STATUS + (ACPIPM_RESOURCE_GPE0_BLK_LEN as u16 / 2);
336
337 /// 4.8.4.1.1, 4.8.4.1.2 Fixed event bits in both PM1 Status and PM1 Enable registers.
338 const BITSHIFT_PM1_GBL: u16 = 5;
339 const BITSHIFT_PM1_PWRBTN: u16 = 8;
340 const BITSHIFT_PM1_SLPBTN: u16 = 9;
341 const BITSHIFT_PM1_RTC: u16 = 10;
342
343 const BITMASK_PM1CNT_SLEEP_ENABLE: u16 = 0x2000;
344 const BITMASK_PM1CNT_WAKE_STATUS: u16 = 0x8000;
345
346 const BITMASK_PM1CNT_SLEEP_TYPE: u16 = 0x1C00;
347 const SLEEP_TYPE_S1: u16 = 1 << 10;
348 const SLEEP_TYPE_S5: u16 = 0 << 10;
349
350 impl ACPIPMFixedEvent {
bitshift(self) -> u16351 fn bitshift(self) -> u16 {
352 match self {
353 ACPIPMFixedEvent::GlobalLock => BITSHIFT_PM1_GBL,
354 ACPIPMFixedEvent::PowerButton => BITSHIFT_PM1_PWRBTN,
355 ACPIPMFixedEvent::SleepButton => BITSHIFT_PM1_SLPBTN,
356 ACPIPMFixedEvent::RTC => BITSHIFT_PM1_RTC,
357 }
358 }
359
bitmask(self) -> u16360 pub(crate) fn bitmask(self) -> u16 {
361 1 << self.bitshift()
362 }
363
bitmask_all() -> u16364 fn bitmask_all() -> u16 {
365 (1 << BITSHIFT_PM1_GBL)
366 | (1 << BITSHIFT_PM1_PWRBTN)
367 | (1 << BITSHIFT_PM1_SLPBTN)
368 | (1 << BITSHIFT_PM1_RTC)
369 }
370 }
371
372 impl FromStr for ACPIPMFixedEvent {
373 type Err = &'static str;
374
from_str(s: &str) -> Result<Self, Self::Err>375 fn from_str(s: &str) -> Result<Self, Self::Err> {
376 match s {
377 "gbllock" => Ok(ACPIPMFixedEvent::GlobalLock),
378 "powerbtn" => Ok(ACPIPMFixedEvent::PowerButton),
379 "sleepbtn" => Ok(ACPIPMFixedEvent::SleepButton),
380 "rtc" => Ok(ACPIPMFixedEvent::RTC),
381 _ => Err("unknown event, must be: gbllock|powerbtn|sleepbtn|rtc"),
382 }
383 }
384 }
385
386 impl PmResource for ACPIPMResource {
pwrbtn_evt(&mut self)387 fn pwrbtn_evt(&mut self) {
388 let mut pm1 = self.pm1.lock();
389
390 pm1.status |= ACPIPMFixedEvent::PowerButton.bitmask();
391 pm1.trigger_sci(&self.sci_evt);
392 }
393
slpbtn_evt(&mut self)394 fn slpbtn_evt(&mut self) {
395 let mut pm1 = self.pm1.lock();
396
397 pm1.status |= ACPIPMFixedEvent::SleepButton.bitmask();
398 pm1.trigger_sci(&self.sci_evt);
399 }
400
rtc_evt(&mut self)401 fn rtc_evt(&mut self) {
402 let mut pm1 = self.pm1.lock();
403
404 pm1.status |= ACPIPMFixedEvent::RTC.bitmask();
405 pm1.trigger_sci(&self.sci_evt);
406 }
407
gpe_evt(&mut self, gpe: u32)408 fn gpe_evt(&mut self, gpe: u32) {
409 let mut gpe0 = self.gpe0.lock();
410
411 match gpe0.set_active(gpe) {
412 Ok(_) => gpe0.trigger_sci(&self.sci_evt),
413 Err(e) => error!("{}", e),
414 }
415 }
416
pme_evt(&mut self, requester_id: u16)417 fn pme_evt(&mut self, requester_id: u16) {
418 let bus = ((requester_id >> 8) & 0xFF) as u8;
419 let mut pci = self.pci.lock();
420 if let Some(root_ports) = pci.pme_notify.get_mut(&bus) {
421 for root_port in root_ports {
422 root_port.lock().notify(requester_id);
423 }
424 }
425 }
426
register_gpe_notify_dev(&mut self, gpe: u32, notify_dev: Arc<Mutex<dyn GpeNotify>>)427 fn register_gpe_notify_dev(&mut self, gpe: u32, notify_dev: Arc<Mutex<dyn GpeNotify>>) {
428 let mut gpe0 = self.gpe0.lock();
429 match gpe0.gpe_notify.get_mut(&gpe) {
430 Some(v) => v.push(notify_dev),
431 None => {
432 gpe0.gpe_notify.insert(gpe, vec![notify_dev]);
433 }
434 }
435 }
436
register_pme_notify_dev(&mut self, bus: u8, notify_dev: Arc<Mutex<dyn PmeNotify>>)437 fn register_pme_notify_dev(&mut self, bus: u8, notify_dev: Arc<Mutex<dyn PmeNotify>>) {
438 let mut pci = self.pci.lock();
439 match pci.pme_notify.get_mut(&bus) {
440 Some(v) => v.push(notify_dev),
441 None => {
442 pci.pme_notify.insert(bus, vec![notify_dev]);
443 }
444 }
445 }
446 }
447
448 const PM1_STATUS_LAST: u16 = PM1_STATUS + (ACPIPM_RESOURCE_EVENTBLK_LEN as u16 / 2) - 1;
449 const PM1_ENABLE_LAST: u16 = PM1_ENABLE + (ACPIPM_RESOURCE_EVENTBLK_LEN as u16 / 2) - 1;
450 const PM1_CONTROL_LAST: u16 = PM1_CONTROL + ACPIPM_RESOURCE_CONTROLBLK_LEN as u16 - 1;
451 const GPE0_STATUS_LAST: u16 = GPE0_STATUS + (ACPIPM_RESOURCE_GPE0_BLK_LEN as u16 / 2) - 1;
452 const GPE0_ENABLE_LAST: u16 = GPE0_ENABLE + (ACPIPM_RESOURCE_GPE0_BLK_LEN as u16 / 2) - 1;
453
454 impl BusDevice for ACPIPMResource {
device_id(&self) -> DeviceId455 fn device_id(&self) -> DeviceId {
456 CrosvmDeviceId::ACPIPMResource.into()
457 }
458
debug_label(&self) -> String459 fn debug_label(&self) -> String {
460 "ACPIPMResource".to_owned()
461 }
462
read(&mut self, info: BusAccessInfo, data: &mut [u8])463 fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
464 match info.offset as u16 {
465 // Accesses to the PM1 registers are done through byte or word accesses
466 PM1_STATUS..=PM1_STATUS_LAST => {
467 if data.len() > std::mem::size_of::<u16>()
468 || info.offset + data.len() as u64 > (PM1_STATUS_LAST + 1).into()
469 {
470 warn!("ACPIPM: bad read size: {}", data.len());
471 return;
472 }
473 let offset = (info.offset - PM1_STATUS as u64) as usize;
474
475 let v = self.pm1.lock().status.to_ne_bytes();
476 for (i, j) in (offset..offset + data.len()).enumerate() {
477 data[i] = v[j];
478 }
479 }
480 PM1_ENABLE..=PM1_ENABLE_LAST => {
481 if data.len() > std::mem::size_of::<u16>()
482 || info.offset + data.len() as u64 > (PM1_ENABLE_LAST + 1).into()
483 {
484 warn!("ACPIPM: bad read size: {}", data.len());
485 return;
486 }
487 let offset = (info.offset - PM1_ENABLE as u64) as usize;
488
489 let v = self.pm1.lock().enable.to_ne_bytes();
490 for (i, j) in (offset..offset + data.len()).enumerate() {
491 data[i] = v[j];
492 }
493 }
494 PM1_CONTROL..=PM1_CONTROL_LAST => {
495 if data.len() > std::mem::size_of::<u16>()
496 || info.offset + data.len() as u64 > (PM1_CONTROL_LAST + 1).into()
497 {
498 warn!("ACPIPM: bad read size: {}", data.len());
499 return;
500 }
501 let offset = (info.offset - PM1_CONTROL as u64) as usize;
502 data.copy_from_slice(
503 &self.pm1.lock().control.to_ne_bytes()[offset..offset + data.len()],
504 );
505 }
506 // OSPM accesses GPE registers through byte accesses (regardless of their length)
507 GPE0_STATUS..=GPE0_STATUS_LAST => {
508 if data.len() > std::mem::size_of::<u8>()
509 || info.offset + data.len() as u64 > (GPE0_STATUS_LAST + 1).into()
510 {
511 warn!("ACPIPM: bad read size: {}", data.len());
512 return;
513 }
514 let offset = (info.offset - GPE0_STATUS as u64) as usize;
515 data[0] = self.gpe0.lock().status[offset];
516 }
517 GPE0_ENABLE..=GPE0_ENABLE_LAST => {
518 if data.len() > std::mem::size_of::<u8>()
519 || info.offset + data.len() as u64 > (GPE0_ENABLE_LAST + 1).into()
520 {
521 warn!("ACPIPM: bad read size: {}", data.len());
522 return;
523 }
524 let offset = (info.offset - GPE0_ENABLE as u64) as usize;
525 data[0] = self.gpe0.lock().enable[offset];
526 }
527 _ => {
528 warn!("ACPIPM: Bad read from {}", info);
529 }
530 }
531 }
532
write(&mut self, info: BusAccessInfo, data: &[u8])533 fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
534 match info.offset as u16 {
535 // Accesses to the PM1 registers are done through byte or word accesses
536 PM1_STATUS..=PM1_STATUS_LAST => {
537 if data.len() > std::mem::size_of::<u16>()
538 || info.offset + data.len() as u64 > (PM1_STATUS_LAST + 1).into()
539 {
540 warn!("ACPIPM: bad write size: {}", data.len());
541 return;
542 }
543 let offset = (info.offset - PM1_STATUS as u64) as usize;
544
545 let mut pm1 = self.pm1.lock();
546 let mut v = pm1.status.to_ne_bytes();
547 for (i, j) in (offset..offset + data.len()).enumerate() {
548 v[j] &= !data[i];
549 }
550 pm1.status = u16::from_ne_bytes(v);
551 }
552 PM1_ENABLE..=PM1_ENABLE_LAST => {
553 if data.len() > std::mem::size_of::<u16>()
554 || info.offset + data.len() as u64 > (PM1_ENABLE_LAST + 1).into()
555 {
556 warn!("ACPIPM: bad write size: {}", data.len());
557 return;
558 }
559 let offset = (info.offset - PM1_ENABLE as u64) as usize;
560
561 let mut pm1 = self.pm1.lock();
562 let mut v = pm1.enable.to_ne_bytes();
563 for (i, j) in (offset..offset + data.len()).enumerate() {
564 v[j] = data[i];
565 }
566 pm1.enable = u16::from_ne_bytes(v);
567 pm1.trigger_sci(&self.sci_evt);
568 }
569 PM1_CONTROL..=PM1_CONTROL_LAST => {
570 if data.len() > std::mem::size_of::<u16>()
571 || info.offset + data.len() as u64 > (PM1_CONTROL_LAST + 1).into()
572 {
573 warn!("ACPIPM: bad write size: {}", data.len());
574 return;
575 }
576 let offset = (info.offset - PM1_CONTROL as u64) as usize;
577
578 let mut pm1 = self.pm1.lock();
579
580 let mut v = pm1.control.to_ne_bytes();
581 for (i, j) in (offset..offset + data.len()).enumerate() {
582 v[j] = data[i];
583 }
584 let val = u16::from_ne_bytes(v);
585
586 // SLP_EN is a write-only bit and reads to it always return a zero
587 if (val & BITMASK_PM1CNT_SLEEP_ENABLE) != 0 {
588 match val & BITMASK_PM1CNT_SLEEP_TYPE {
589 SLEEP_TYPE_S1 => {
590 if let Err(e) = self.suspend_evt.signal() {
591 error!("ACPIPM: failed to trigger suspend event: {}", e);
592 }
593 }
594 SLEEP_TYPE_S5 => {
595 if let Err(e) =
596 self.exit_evt_wrtube.send::<VmEventType>(&VmEventType::Exit)
597 {
598 error!("ACPIPM: failed to trigger exit event: {}", e);
599 }
600 }
601 _ => error!(
602 "ACPIPM: unknown SLP_TYP written: {}",
603 (val & BITMASK_PM1CNT_SLEEP_TYPE) >> 10
604 ),
605 }
606 }
607 pm1.control = val & !BITMASK_PM1CNT_SLEEP_ENABLE;
608 }
609 // OSPM accesses GPE registers through byte accesses (regardless of their length)
610 GPE0_STATUS..=GPE0_STATUS_LAST => {
611 if data.len() > std::mem::size_of::<u8>()
612 || info.offset + data.len() as u64 > (GPE0_STATUS_LAST + 1).into()
613 {
614 warn!("ACPIPM: bad write size: {}", data.len());
615 return;
616 }
617 let offset = (info.offset - GPE0_STATUS as u64) as usize;
618 self.gpe0.lock().status[offset] &= !data[0];
619 }
620 GPE0_ENABLE..=GPE0_ENABLE_LAST => {
621 if data.len() > std::mem::size_of::<u8>()
622 || info.offset + data.len() as u64 > (GPE0_ENABLE_LAST + 1).into()
623 {
624 warn!("ACPIPM: bad write size: {}", data.len());
625 return;
626 }
627 let offset = (info.offset - GPE0_ENABLE as u64) as usize;
628 let mut gpe = self.gpe0.lock();
629 gpe.enable[offset] = data[0];
630 gpe.trigger_sci(&self.sci_evt);
631 }
632 _ => {
633 warn!("ACPIPM: Bad write to {}", info);
634 }
635 };
636 }
637 }
638
639 impl BusResumeDevice for ACPIPMResource {
resume_imminent(&mut self)640 fn resume_imminent(&mut self) {
641 self.pm1.lock().status |= BITMASK_PM1CNT_WAKE_STATUS;
642 }
643 }
644
645 impl Aml for ACPIPMResource {
to_aml_bytes(&self, bytes: &mut Vec<u8>)646 fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
647 // S1
648 aml::Name::new(
649 "_S1_".into(),
650 &aml::Package::new(vec![&aml::ONE, &aml::ONE, &aml::ZERO, &aml::ZERO]),
651 )
652 .to_aml_bytes(bytes);
653
654 // S5
655 aml::Name::new(
656 "_S5_".into(),
657 &aml::Package::new(vec![&aml::ZERO, &aml::ZERO, &aml::ZERO, &aml::ZERO]),
658 )
659 .to_aml_bytes(bytes);
660 }
661 }
662
663 pub const PM_WAKEUP_GPIO: u32 = 0;
664
665 pub struct PmWakeupEvent {
666 active: AtomicBool,
667 vm_control_tube: Arc<Mutex<Tube>>,
668 pm_config: Arc<Mutex<PmConfig>>,
669 metrics_event: MetricEventType,
670 armed_time: Arc<Mutex<Instant>>,
671 }
672
673 impl PmWakeupEvent {
new( vm_control_tube: Arc<Mutex<Tube>>, pm_config: Arc<Mutex<PmConfig>>, metrics_event: MetricEventType, ) -> Self674 pub fn new(
675 vm_control_tube: Arc<Mutex<Tube>>,
676 pm_config: Arc<Mutex<PmConfig>>,
677 metrics_event: MetricEventType,
678 ) -> Self {
679 Self {
680 active: AtomicBool::new(false),
681 vm_control_tube,
682 pm_config,
683 metrics_event,
684 // Not actually armed, but simpler than wrapping with an Option.
685 armed_time: Arc::new(Mutex::new(Instant::now())),
686 }
687 }
688
trigger_wakeup(&self) -> anyhow::Result<()>689 pub fn trigger_wakeup(&self) -> anyhow::Result<()> {
690 if self.active.load(Ordering::SeqCst) && self.pm_config.lock().should_trigger_pme() {
691 let elapsed = self.armed_time.lock().elapsed().as_millis();
692 log_metric(
693 self.metrics_event.clone(),
694 elapsed.try_into().unwrap_or(i64::MAX),
695 );
696
697 let tube = self.vm_control_tube.lock();
698 tube.send(&VmRequest::Gpe(PM_WAKEUP_GPIO))
699 .with_context(|| format!("{:?} failed to send pme", self.metrics_event))?;
700 match tube.recv::<VmResponse>() {
701 Ok(VmResponse::Ok) => (),
702 e => bail!("{:?} pme failure {:?}", self.metrics_event, e),
703 }
704 }
705 Ok(())
706 }
707
set_active(&self, active: bool)708 pub fn set_active(&self, active: bool) {
709 self.active.store(active, Ordering::SeqCst);
710 if active {
711 *self.armed_time.lock() = Instant::now();
712 }
713 }
714 }
715
716 #[cfg(test)]
717 mod tests {
718 use base::SendTube;
719 use base::Tube;
720
721 use super::*;
722 use crate::suspendable_tests;
723
get_evt_tube() -> SendTube724 fn get_evt_tube() -> SendTube {
725 let (vm_evt_wrtube, _) = Tube::directional_pair().unwrap();
726 vm_evt_wrtube
727 }
728
get_irq_evt() -> IrqLevelEvent729 fn get_irq_evt() -> IrqLevelEvent {
730 match crate::IrqLevelEvent::new() {
731 Ok(evt) => evt,
732 Err(e) => panic!(
733 "failed to create irqlevelevt: {} - panic. Can't test ACPI",
734 e
735 ),
736 }
737 }
738
modify_device(acpi: &mut ACPIPMResource)739 fn modify_device(acpi: &mut ACPIPMResource) {
740 {
741 let mut pm1 = acpi.pm1.lock();
742 pm1.enable += 1;
743 }
744 }
745
746 suspendable_tests!(
747 acpi,
748 ACPIPMResource::new(get_irq_evt(), Event::new().unwrap(), get_evt_tube(), None,),
749 modify_device
750 );
751 }
752