xref: /cloud-hypervisor/devices/src/legacy/gpio_pl061.rs (revision fa7a000dbe9637eb256af18ae8c3c4a8d5bf9c8f)
1 // Copyright 2021 Arm Limited (or its affiliates). All rights reserved.
2 //
3 // SPDX-License-Identifier: Apache-2.0
4 
5 //! ARM PrimeCell General Purpose Input/Output(PL061)
6 //!
7 //! This module implements an ARM PrimeCell General Purpose Input/Output(PL061) to support gracefully poweroff microvm from external.
8 //!
9 
10 use crate::{read_le_u32, write_le_u32};
11 use std::result;
12 use std::sync::{Arc, Barrier};
13 use std::{fmt, io};
14 use versionize::{VersionMap, Versionize, VersionizeResult};
15 use versionize_derive::Versionize;
16 use vm_device::interrupt::InterruptSourceGroup;
17 use vm_device::BusDevice;
18 use vm_migration::{
19     Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable, VersionMapped,
20 };
21 
22 const OFS_DATA: u64 = 0x400; // Data Register
23 const GPIODIR: u64 = 0x400; // Direction Register
24 const GPIOIS: u64 = 0x404; // Interrupt Sense Register
25 const GPIOIBE: u64 = 0x408; // Interrupt Both Edges Register
26 const GPIOIEV: u64 = 0x40c; // Interrupt Event Register
27 const GPIOIE: u64 = 0x410; // Interrupt Mask Register
28 const GPIORIE: u64 = 0x414; // Raw Interrupt Status Register
29 const GPIOMIS: u64 = 0x418; // Masked Interrupt Status Register
30 const GPIOIC: u64 = 0x41c; // Interrupt Clear Register
31 const GPIOAFSEL: u64 = 0x420; // Mode Control Select Register
32                               // From 0x424 to 0xFDC => reserved space.
33                               // From 0xFE0 to 0xFFC => Peripheral and PrimeCell Identification Registers which are Read Only registers.
34                               // These registers can conceptually be treated as a 32-bit register, and PartNumber[11:0] is used to identify the peripheral.
35                               // We are putting the expected values (look at 'Reset value' column from above mentioned document) in an array.
36 const GPIO_ID: [u8; 8] = [0x61, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1];
37 // ID Margins
38 const GPIO_ID_LOW: u64 = 0xfe0;
39 const GPIO_ID_HIGH: u64 = 0x1000;
40 
41 const N_GPIOS: u32 = 8;
42 
43 #[derive(Debug)]
44 pub enum Error {
45     BadWriteOffset(u64),
46     GpioInterruptDisabled,
47     GpioInterruptFailure(io::Error),
48     GpioTriggerKeyFailure(u32),
49 }
50 
51 impl fmt::Display for Error {
52     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53         match self {
54             Error::BadWriteOffset(offset) => write!(f, "Bad Write Offset: {offset}"),
55             Error::GpioInterruptDisabled => write!(f, "GPIO interrupt disabled by guest driver.",),
56             Error::GpioInterruptFailure(ref e) => {
57                 write!(f, "Could not trigger GPIO interrupt: {e}.")
58             }
59             Error::GpioTriggerKeyFailure(key) => {
60                 write!(f, "Invalid GPIO Input key triggered: {key}.")
61             }
62         }
63     }
64 }
65 
66 type Result<T> = result::Result<T, Error>;
67 
68 /// A GPIO device following the PL061 specification.
69 pub struct Gpio {
70     id: String,
71     // Data Register
72     data: u32,
73     old_in_data: u32,
74     // Direction Register
75     dir: u32,
76     // Interrupt Sense Register
77     isense: u32,
78     // Interrupt Both Edges Register
79     ibe: u32,
80     // Interrupt Event Register
81     iev: u32,
82     // Interrupt Mask Register
83     im: u32,
84     // Raw Interrupt Status Register
85     istate: u32,
86     // Mode Control Select Register
87     afsel: u32,
88     // GPIO irq_field
89     interrupt: Arc<dyn InterruptSourceGroup>,
90 }
91 
92 #[derive(Versionize)]
93 pub struct GpioState {
94     data: u32,
95     old_in_data: u32,
96     dir: u32,
97     isense: u32,
98     ibe: u32,
99     iev: u32,
100     im: u32,
101     istate: u32,
102     afsel: u32,
103 }
104 
105 impl VersionMapped for GpioState {}
106 
107 impl Gpio {
108     /// Constructs an PL061 GPIO device.
109     pub fn new(
110         id: String,
111         interrupt: Arc<dyn InterruptSourceGroup>,
112         state: Option<GpioState>,
113     ) -> Self {
114         let (data, old_in_data, dir, isense, ibe, iev, im, istate, afsel) =
115             if let Some(state) = state {
116                 (
117                     state.data,
118                     state.old_in_data,
119                     state.dir,
120                     state.isense,
121                     state.ibe,
122                     state.iev,
123                     state.im,
124                     state.istate,
125                     state.afsel,
126                 )
127             } else {
128                 (0, 0, 0, 0, 0, 0, 0, 0, 0)
129             };
130 
131         Self {
132             id,
133             data,
134             old_in_data,
135             dir,
136             isense,
137             ibe,
138             iev,
139             im,
140             istate,
141             afsel,
142             interrupt,
143         }
144     }
145 
146     fn state(&self) -> GpioState {
147         GpioState {
148             data: self.data,
149             old_in_data: self.old_in_data,
150             dir: self.dir,
151             isense: self.isense,
152             ibe: self.ibe,
153             iev: self.iev,
154             im: self.im,
155             istate: self.istate,
156             afsel: self.afsel,
157         }
158     }
159 
160     fn pl061_internal_update(&mut self) {
161         // FIXME:
162         //  Missing Output Interrupt Emulation.
163 
164         // Input Edging Interrupt Emulation.
165         let changed = (self.old_in_data ^ self.data) & !self.dir;
166         if changed > 0 {
167             self.old_in_data = self.data;
168             for i in 0..N_GPIOS {
169                 let mask = (1 << i) as u32;
170                 if (changed & mask) > 0 {
171                     // Bits set high in GPIOIS(Interrupt sense register) configure the corresponding
172                     // pins to detect levels, otherwise, detect edges.
173                     if (self.isense & mask) == 0 {
174                         if (self.ibe & mask) > 0 {
175                             // Bits set high in GPIOIBE(Interrupt both-edges register) configure the corresponding
176                             // pins to detect both falling and rising edges.
177                             // Clearing a bit configures the pin to be controlled by GPIOIEV.
178                             self.istate |= mask;
179                         } else {
180                             // Bits set to high in GPIOIEV(Interrupt event register) configure the
181                             // corresponding pin to detect rising edges, otherwise, detect falling edges.
182                             self.istate |= !(self.data ^ self.iev) & mask;
183                         }
184                     }
185                 }
186             }
187         }
188 
189         // Input Level Interrupt Emulation.
190         self.istate |= !(self.data ^ self.iev) & self.isense;
191     }
192 
193     fn handle_write(&mut self, offset: u64, val: u32) -> Result<()> {
194         if offset < OFS_DATA {
195             // In order to write to data register, the corresponding bits in the mask, resulting
196             // from the offsite[9:2], must be HIGH. otherwise the bit values remain unchanged.
197             let mask = (offset >> 2) as u32 & self.dir;
198             self.data = (self.data & !mask) | (val & mask);
199         } else {
200             match offset {
201                 GPIODIR => {
202                     /* Direction Register */
203                     self.dir = val & 0xff;
204                 }
205                 GPIOIS => {
206                     /* Interrupt Sense Register */
207                     self.isense = val & 0xff;
208                 }
209                 GPIOIBE => {
210                     /* Interrupt Both Edges Register */
211                     self.ibe = val & 0xff;
212                 }
213                 GPIOIEV => {
214                     /* Interrupt Event Register */
215                     self.iev = val & 0xff;
216                 }
217                 GPIOIE => {
218                     /* Interrupt Mask Register */
219                     self.im = val & 0xff;
220                 }
221                 GPIOIC => {
222                     /* Interrupt Clear Register */
223                     self.istate &= !val;
224                 }
225                 GPIOAFSEL => {
226                     /* Mode Control Select Register */
227                     self.afsel = val & 0xff;
228                 }
229                 o => {
230                     return Err(Error::BadWriteOffset(o));
231                 }
232             }
233         }
234         Ok(())
235     }
236 
237     pub fn trigger_key(&mut self, key: u32) -> Result<()> {
238         let mask = (1 << key) as u32;
239         if (!self.dir & mask) > 0 {
240             // emulate key event
241             // By default, Input Pin is configured to detect both rising and falling edges.
242             // So reverse the input pin data to generate a pulse.
243             self.data |= !(self.data & mask) & mask;
244             self.pl061_internal_update();
245 
246             match self.trigger_gpio_interrupt() {
247                 Ok(_) | Err(Error::GpioInterruptDisabled) => return Ok(()),
248                 Err(e) => return Err(e),
249             }
250         }
251 
252         Err(Error::GpioTriggerKeyFailure(key))
253     }
254 
255     fn trigger_gpio_interrupt(&self) -> Result<()> {
256         // Bits set to high in GPIOIE(Interrupt mask register) allow the corresponding pins to
257         // trigger their individual interrupts and then the combined GPIOINTR line.
258         if (self.istate & self.im) == 0 {
259             warn!("Failed to trigger GPIO input interrupt (disabled by guest OS)");
260             return Err(Error::GpioInterruptDisabled);
261         }
262         self.interrupt
263             .trigger(0)
264             .map_err(Error::GpioInterruptFailure)?;
265         Ok(())
266     }
267 }
268 
269 impl BusDevice for Gpio {
270     fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
271         let value;
272         let mut read_ok = true;
273 
274         if (GPIO_ID_LOW..GPIO_ID_HIGH).contains(&offset) {
275             let index = ((offset - GPIO_ID_LOW) >> 2) as usize;
276             value = u32::from(GPIO_ID[index]);
277         } else if offset < OFS_DATA {
278             value = self.data & ((offset >> 2) as u32)
279         } else {
280             value = match offset {
281                 GPIODIR => self.dir,
282                 GPIOIS => self.isense,
283                 GPIOIBE => self.ibe,
284                 GPIOIEV => self.iev,
285                 GPIOIE => self.im,
286                 GPIORIE => self.istate,
287                 GPIOMIS => self.istate & self.im,
288                 GPIOAFSEL => self.afsel,
289                 _ => {
290                     read_ok = false;
291                     0
292                 }
293             };
294         }
295 
296         if read_ok && data.len() <= 4 {
297             write_le_u32(data, value);
298         } else {
299             warn!(
300                 "Invalid GPIO PL061 read: offset {}, data length {}",
301                 offset,
302                 data.len()
303             );
304         }
305     }
306 
307     fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
308         if data.len() <= 4 {
309             let value = read_le_u32(data);
310             if let Err(e) = self.handle_write(offset, value) {
311                 warn!("Failed to write to GPIO PL061 device: {}", e);
312             }
313         } else {
314             warn!(
315                 "Invalid GPIO PL061 write: offset {}, data length {}",
316                 offset,
317                 data.len()
318             );
319         }
320 
321         None
322     }
323 }
324 
325 impl Snapshottable for Gpio {
326     fn id(&self) -> String {
327         self.id.clone()
328     }
329 
330     fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
331         Snapshot::new_from_versioned_state(&self.state())
332     }
333 }
334 
335 impl Pausable for Gpio {}
336 impl Transportable for Gpio {}
337 impl Migratable for Gpio {}
338 
339 #[cfg(test)]
340 mod tests {
341     use super::*;
342     use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
343     use vmm_sys_util::eventfd::EventFd;
344 
345     const GPIO_NAME: &str = "gpio";
346     const LEGACY_GPIO_MAPPED_IO_START: u64 = 0x0902_0000;
347 
348     struct TestInterrupt {
349         event_fd: EventFd,
350     }
351 
352     impl InterruptSourceGroup for TestInterrupt {
353         fn trigger(&self, _index: InterruptIndex) -> result::Result<(), std::io::Error> {
354             self.event_fd.write(1)
355         }
356 
357         fn update(
358             &self,
359             _index: InterruptIndex,
360             _config: InterruptSourceConfig,
361             _masked: bool,
362             _set_gsi: bool,
363         ) -> result::Result<(), std::io::Error> {
364             Ok(())
365         }
366 
367         fn set_gsi(&self) -> result::Result<(), std::io::Error> {
368             Ok(())
369         }
370 
371         fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
372             Some(self.event_fd.try_clone().unwrap())
373         }
374     }
375 
376     impl TestInterrupt {
377         fn new(event_fd: EventFd) -> Self {
378             TestInterrupt { event_fd }
379         }
380     }
381 
382     #[test]
383     fn test_gpio_read_write_and_event() {
384         let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
385         let mut gpio = Gpio::new(
386             String::from(GPIO_NAME),
387             Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())),
388             None,
389         );
390         let mut data = [0; 4];
391 
392         // Read and write to the GPIODIR register.
393         // Set pin 0 output pin.
394         write_le_u32(&mut data, 1);
395         gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIODIR, &data);
396         gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIODIR, &mut data);
397         let v = read_le_u32(&data);
398         assert_eq!(v, 1);
399 
400         // Read and write to the GPIODATA register.
401         write_le_u32(&mut data, 1);
402         // Set pin 0 high.
403         let offset = 0x00000004_u64;
404         gpio.write(LEGACY_GPIO_MAPPED_IO_START, offset, &data);
405         gpio.read(LEGACY_GPIO_MAPPED_IO_START, offset, &mut data);
406         let v = read_le_u32(&data);
407         assert_eq!(v, 1);
408 
409         // Read and write to the GPIOIS register.
410         // Configure pin 0 detecting level interrupt.
411         write_le_u32(&mut data, 1);
412         gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIS, &data);
413         gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIS, &mut data);
414         let v = read_le_u32(&data);
415         assert_eq!(v, 1);
416 
417         // Read and write to the GPIOIBE register.
418         // Configure pin 1 detecting both falling and rising edges.
419         write_le_u32(&mut data, 2);
420         gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIBE, &data);
421         gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIBE, &mut data);
422         let v = read_le_u32(&data);
423         assert_eq!(v, 2);
424 
425         // Read and write to the GPIOIEV register.
426         // Configure pin 2 detecting both falling and rising edges.
427         write_le_u32(&mut data, 4);
428         gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIEV, &data);
429         gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIEV, &mut data);
430         let v = read_le_u32(&data);
431         assert_eq!(v, 4);
432 
433         // Read and write to the GPIOIE register.
434         // Configure pin 0...2 capable of triggering their individual interrupts
435         // and then the combined GPIOINTR line.
436         write_le_u32(&mut data, 7);
437         gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIE, &data);
438         gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIE, &mut data);
439         let v = read_le_u32(&data);
440         assert_eq!(v, 7);
441 
442         let mask = 0x00000002_u32;
443         // emulate an rising pulse in pin 1.
444         gpio.data |= !(gpio.data & mask) & mask;
445         gpio.pl061_internal_update();
446         // The interrupt line on pin 1 should be on.
447         // Read the GPIOMIS register.
448         gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOMIS, &mut data);
449         let v = read_le_u32(&data);
450         assert_eq!(v, 2);
451 
452         // Read and Write to the GPIOIC register.
453         // clear interrupt in pin 1.
454         write_le_u32(&mut data, 2);
455         gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIOIC, &data);
456         gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIOIC, &mut data);
457         let v = read_le_u32(&data);
458         assert_eq!(v, 2);
459 
460         // Attempts to write beyond the writable space.
461         write_le_u32(&mut data, 0);
462         gpio.write(LEGACY_GPIO_MAPPED_IO_START, GPIO_ID_LOW, &data);
463 
464         let mut data = [0; 4];
465         gpio.read(LEGACY_GPIO_MAPPED_IO_START, GPIO_ID_LOW, &mut data);
466         let index = GPIO_ID_LOW + 3;
467         assert_eq!(data[0], GPIO_ID[((index - GPIO_ID_LOW) >> 2) as usize]);
468     }
469 }
470