1use async_trait::async_trait;
10use chipset_device::ChipsetDevice;
11use chipset_device::io::IoError;
12use chipset_device::io::IoResult;
13use chipset_device::mmio::MmioIntercept;
14use chipset_device::poll_device::PollDevice;
15use chipset_device_resources::IRQ_LINE_SET;
16use chipset_device_resources::ResolveChipsetDeviceHandleParams;
17use chipset_device_resources::ResolvedChipsetDevice;
18use chipset_resources::acpi_power_button::ACPI_POWER_BUTTON_MMIO_BASE;
19use chipset_resources::acpi_power_button::AcpiPowerButtonDeviceHandle;
20use chipset_resources::acpi_power_button::PowerButtonRpc;
21use futures::StreamExt;
22use inspect::InspectMut;
23use std::convert::Infallible;
24use std::ops::RangeInclusive;
25use std::task::Context;
26use std::task::Poll;
27use vm_resource::AsyncResolveResource;
28use vm_resource::ResourceResolver;
29use vm_resource::declare_static_async_resolver;
30use vm_resource::kind::ChipsetDeviceHandleKind;
31use vmcore::device_state::ChangeDeviceState;
32use vmcore::line_interrupt::LineInterrupt;
33
34#[derive(InspectMut)]
35struct AcpiPowerButton {
36 interrupt: LineInterrupt,
37 pending: bool,
38 #[inspect(skip)]
39 mmio_region: (&'static str, RangeInclusive<u64>),
40 #[inspect(skip)]
41 recv: mesh::Receiver<PowerButtonRpc>,
42}
43
44impl AcpiPowerButton {
45 fn new(interrupt: LineInterrupt, recv: mesh::Receiver<PowerButtonRpc>) -> Self {
46 let base = u64::from(ACPI_POWER_BUTTON_MMIO_BASE);
47 Self {
48 interrupt,
49 pending: false,
50 mmio_region: ("status", base..=base + 3),
51 recv,
52 }
53 }
54
55 fn set_pending(&mut self, pending: bool) {
56 self.pending = pending;
57 self.interrupt.set_level(pending);
58 }
59}
60
61impl ChipsetDevice for AcpiPowerButton {
62 fn supports_mmio(&mut self) -> Option<&mut dyn MmioIntercept> {
63 Some(self)
64 }
65
66 fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
67 Some(self)
68 }
69}
70
71impl MmioIntercept for AcpiPowerButton {
72 fn mmio_read(&mut self, address: u64, data: &mut [u8]) -> IoResult {
73 if address != u64::from(ACPI_POWER_BUTTON_MMIO_BASE) {
74 return IoResult::Err(IoError::InvalidRegister);
75 }
76 if data.len() != size_of::<u32>() {
77 return IoResult::Err(IoError::InvalidAccessSize);
78 }
79 data.copy_from_slice(&u32::from(self.pending).to_le_bytes());
80 IoResult::Ok
81 }
82
83 fn mmio_write(&mut self, address: u64, data: &[u8]) -> IoResult {
84 if address != u64::from(ACPI_POWER_BUTTON_MMIO_BASE) {
85 return IoResult::Err(IoError::InvalidRegister);
86 }
87 let Ok(bytes) = data.try_into() else {
88 return IoResult::Err(IoError::InvalidAccessSize);
89 };
90 if u32::from_le_bytes(bytes) & 1 != 0 {
91 self.set_pending(false);
92 }
93 IoResult::Ok
94 }
95
96 fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u64>)] {
97 std::slice::from_ref(&self.mmio_region)
98 }
99}
100
101impl PollDevice for AcpiPowerButton {
102 fn poll_device(&mut self, cx: &mut Context<'_>) {
103 while let Poll::Ready(Some(PowerButtonRpc::Press(rpc))) = self.recv.poll_next_unpin(cx) {
104 self.set_pending(true);
105 rpc.complete(());
106 }
107 }
108}
109
110impl ChangeDeviceState for AcpiPowerButton {
111 fn start(&mut self) {}
112
113 async fn stop(&mut self) {}
114
115 async fn reset(&mut self) {
116 self.set_pending(false);
117 }
118}
119
120mod save_restore {
121 use super::*;
122 use vmcore::save_restore::RestoreError;
123 use vmcore::save_restore::SaveError;
124 use vmcore::save_restore::SaveRestore;
125
126 mod state {
127 use mesh::payload::Protobuf;
128 use vmcore::save_restore::SavedStateRoot;
129
130 #[derive(Protobuf, SavedStateRoot)]
131 #[mesh(package = "chipset.acpi_power_button")]
132 pub struct SavedState {
133 #[mesh(1)]
134 pub pending: bool,
135 }
136 }
137
138 impl SaveRestore for AcpiPowerButton {
139 type SavedState = state::SavedState;
140
141 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
142 let Self {
143 interrupt: _,
144 pending,
145 mmio_region: _,
146 recv: _,
147 } = *self;
148
149 let saved_state = state::SavedState { pending };
150 Ok(saved_state)
151 }
152
153 fn restore(&mut self, saved_state: Self::SavedState) -> Result<(), RestoreError> {
154 let state::SavedState { pending } = saved_state;
155 self.set_pending(pending);
156 Ok(())
157 }
158 }
159}
160
161pub struct AcpiPowerButtonResolver;
163
164declare_static_async_resolver! {
165 AcpiPowerButtonResolver,
166 (ChipsetDeviceHandleKind, AcpiPowerButtonDeviceHandle),
167}
168
169#[async_trait]
170impl AsyncResolveResource<ChipsetDeviceHandleKind, AcpiPowerButtonDeviceHandle>
171 for AcpiPowerButtonResolver
172{
173 type Output = ResolvedChipsetDevice;
174 type Error = Infallible;
175
176 async fn resolve(
177 &self,
178 _resolver: &ResourceResolver,
179 resource: AcpiPowerButtonDeviceHandle,
180 input: ResolveChipsetDeviceHandleParams<'_>,
181 ) -> Result<Self::Output, Self::Error> {
182 Ok(AcpiPowerButton::new(
183 input
184 .configure
185 .new_line(IRQ_LINE_SET, "power-button", resource.irq),
186 resource.recv,
187 )
188 .into())
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195 use mesh::rpc::RpcSend;
196 use parking_lot::Mutex;
197 use std::sync::Arc;
198 use test_with_tracing::test;
199 use vmcore::line_interrupt::LineSetTarget;
200 use vmcore::save_restore::SaveRestore;
201
202 #[derive(Default)]
203 struct Interrupts(Mutex<Vec<(u32, bool)>>);
204
205 impl LineSetTarget for Interrupts {
206 fn set_irq(&self, vector: u32, high: bool) {
207 self.0.lock().push((vector, high));
208 }
209 }
210
211 #[test]
212 fn presses_latch_until_guest_acknowledgement() {
213 futures::executor::block_on(async {
214 let interrupts = Arc::new(Interrupts::default());
215 let (send, recv) = mesh::channel();
216 let mut button = AcpiPowerButton::new(
217 LineInterrupt::new_with_target("button", interrupts.clone(), 10),
218 recv,
219 );
220 button.start();
221 assert!(interrupts.0.lock().is_empty());
222
223 let base = u64::from(ACPI_POWER_BUTTON_MMIO_BASE);
224 assert_eq!(button.get_static_regions(), [("status", base..=base + 3)]);
225 for _ in 0..2 {
226 let mut response = std::pin::pin!(send.call(PowerButtonRpc::Press, ()));
227 assert!(futures::poll!(&mut response).is_pending());
228 futures::future::poll_fn(|cx| {
229 button.poll_device(cx);
230 Poll::Ready(())
231 })
232 .await;
233 response.await.unwrap();
234 assert_eq!(*interrupts.0.lock(), [(10, true)]);
235 let mut status = [0; 4];
236 button.mmio_read(base, &mut status).unwrap();
237 assert_eq!(u32::from_le_bytes(status), 1);
238 button.mmio_write(base, &0u32.to_le_bytes()).unwrap();
239 button.mmio_write(base, &2u32.to_le_bytes()).unwrap();
240 assert_eq!(*interrupts.0.lock(), [(10, true)]);
241 button.mmio_write(base, &1u32.to_le_bytes()).unwrap();
242 assert_eq!(*interrupts.0.lock(), [(10, true), (10, false)]);
243 button.mmio_read(base, &mut status).unwrap();
244 assert_eq!(u32::from_le_bytes(status), 0);
245 interrupts.0.lock().clear();
246 }
247 button.stop().await;
248 assert!(interrupts.0.lock().is_empty());
249 });
250 }
251
252 #[test]
253 fn pending_event_survives_stop_and_save_restore_and_reset_clears_it() {
254 futures::executor::block_on(async {
255 let interrupts = Arc::new(Interrupts::default());
256 let mut button = AcpiPowerButton::new(
257 LineInterrupt::new_with_target("button", interrupts.clone(), 10),
258 mesh::channel().1,
259 );
260 button.set_pending(true);
261 button.stop().await;
262 let pending = button.save().unwrap();
263 assert_eq!(*interrupts.0.lock(), [(10, true)]);
264 button.start();
265 assert_eq!(*interrupts.0.lock(), [(10, true)]);
266
267 button.reset().await;
268 assert!(!button.pending);
269 let idle = button.save().unwrap();
270 button.restore(pending).unwrap();
271 assert!(button.pending);
272 assert_eq!(*interrupts.0.lock(), [(10, true), (10, false), (10, true)]);
273 button.restore(idle).unwrap();
274 assert!(!button.pending);
275 assert_eq!(interrupts.0.lock().last(), Some(&(10, false)));
276 });
277 }
278
279 #[test]
280 fn acknowledgement_preserves_other_sources_on_a_shared_line() {
281 let interrupts = Arc::new(Interrupts::default());
282 let interrupt = LineInterrupt::new_with_target("button", interrupts.clone(), 10);
283 let other = interrupt.new_shared("other").unwrap();
284 let mut button = AcpiPowerButton::new(interrupt, mesh::channel().1);
285 other.set_level(true);
286 button.set_pending(true);
287 button.set_pending(true);
288 button
289 .mmio_write(u64::from(ACPI_POWER_BUTTON_MMIO_BASE), &1u32.to_le_bytes())
290 .unwrap();
291 assert!(!button.pending);
292 assert!(interrupts.0.lock().iter().all(|&(_, high)| high));
293 other.set_level(false);
294 assert_eq!(interrupts.0.lock().last(), Some(&(10, false)));
295 }
296
297 #[test]
298 fn invalid_mmio_accesses_do_not_clear_pending_event() {
299 let mut button = AcpiPowerButton::new(LineInterrupt::detached(), mesh::channel().1);
300 button.set_pending(true);
301 let base = u64::from(ACPI_POWER_BUTTON_MMIO_BASE);
302 for size in [0, 1, 2, 3, 5, 8] {
303 let mut data = vec![0xff; size];
304 assert!(matches!(
305 button.mmio_read(base, &mut data),
306 IoResult::Err(IoError::InvalidAccessSize)
307 ));
308 assert!(matches!(
309 button.mmio_write(base, &data),
310 IoResult::Err(IoError::InvalidAccessSize)
311 ));
312 }
313 for address in [0, base - 1, base + 1, base + 4, u64::MAX] {
314 assert!(matches!(
315 button.mmio_read(address, &mut [0; 4]),
316 IoResult::Err(IoError::InvalidRegister)
317 ));
318 assert!(matches!(
319 button.mmio_write(address, &1u32.to_le_bytes()),
320 IoResult::Err(IoError::InvalidRegister)
321 ));
322 }
323 assert!(button.pending);
324 }
325}