Skip to main content

chipset/
acpi_power_button.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! ACPI generic event device for a control-method power button.
5//!
6//! The 32-bit MMIO status register exposes a pending bit (bit 0). Presses
7//! coalesce until the guest writes one to that bit, deasserting the interrupt.
8
9use 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
161/// Resolver for the ACPI power-button generic event device.
162pub 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}