xref: /aosp_15_r20/external/crosvm/devices/src/pl030.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2018 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::convert::TryFrom;
6 use std::time::SystemTime;
7 use std::time::UNIX_EPOCH;
8 
9 use anyhow::Context;
10 use base::warn;
11 use serde::Deserialize;
12 use serde::Serialize;
13 
14 use crate::pci::CrosvmDeviceId;
15 use crate::BusAccessInfo;
16 use crate::BusDevice;
17 use crate::DeviceId;
18 use crate::IrqEdgeEvent;
19 use crate::Suspendable;
20 
21 // Register offsets
22 // Data register
23 const RTCDR: u64 = 0x0;
24 // Match register
25 const RTCMR: u64 = 0x4;
26 // Interrupt status register
27 const RTCSTAT: u64 = 0x8;
28 // Interrupt clear register
29 const RTCEOI: u64 = 0x8;
30 // Counter load register
31 const RTCLR: u64 = 0xC;
32 // Counter register
33 const RTCCR: u64 = 0x10;
34 
35 // A single 4K page is mapped for this device
36 pub const PL030_AMBA_IOMEM_SIZE: u64 = 0x1000;
37 
38 // AMBA id registers are at the end of the allocated memory space
39 const AMBA_ID_OFFSET: u64 = PL030_AMBA_IOMEM_SIZE - 0x20;
40 const AMBA_MASK_OFFSET: u64 = PL030_AMBA_IOMEM_SIZE - 0x28;
41 
42 // This is the AMBA id for this device
43 pub const PL030_AMBA_ID: u32 = 0x00041030;
44 pub const PL030_AMBA_MASK: u32 = 0x000FFFFF;
45 
46 /// An emulated ARM pl030 RTC
47 pub struct Pl030 {
48     // Event to be used to interrupt the guest for an alarm event
49     alarm_evt: IrqEdgeEvent,
50 
51     // This is the delta we subtract from current time to get the
52     // counter value
53     counter_delta_time: u32,
54 
55     // This is the value that triggers an alarm interrupt when it
56     // matches with the rtc time
57     match_value: u32,
58 
59     // status flag to keep track of whether the interrupt is cleared
60     // or not
61     interrupt_active: bool,
62 }
63 
64 #[derive(Debug, Serialize, Deserialize)]
65 struct Pl030Snapshot {
66     counter_delta_time: u32,
67     match_value: u32,
68     interrupt_active: bool,
69 }
70 
get_epoch_time() -> u3271 fn get_epoch_time() -> u32 {
72     let epoch_time = SystemTime::now()
73         .duration_since(UNIX_EPOCH)
74         .expect("SystemTime::duration_since failed");
75     epoch_time.as_secs() as u32
76 }
77 
78 impl Pl030 {
79     /// Constructs a Pl030 device
new(evt: IrqEdgeEvent) -> Pl03080     pub fn new(evt: IrqEdgeEvent) -> Pl030 {
81         Pl030 {
82             alarm_evt: evt,
83             counter_delta_time: get_epoch_time(),
84             match_value: 0,
85             interrupt_active: false,
86         }
87     }
88 }
89 
90 impl BusDevice for Pl030 {
device_id(&self) -> DeviceId91     fn device_id(&self) -> DeviceId {
92         CrosvmDeviceId::Pl030.into()
93     }
94 
debug_label(&self) -> String95     fn debug_label(&self) -> String {
96         "Pl030".to_owned()
97     }
98 
write(&mut self, info: BusAccessInfo, data: &[u8])99     fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
100         let data_array = match <&[u8; 4]>::try_from(data) {
101             Ok(array) => array,
102             _ => {
103                 warn!("bad write size: {} for pl030", data.len());
104                 return;
105             }
106         };
107 
108         let reg_val = u32::from_ne_bytes(*data_array);
109         match info.offset {
110             RTCDR => {
111                 warn!("invalid write to read-only RTCDR register");
112             }
113             RTCMR => {
114                 self.match_value = reg_val;
115                 // TODO(sonnyrao): here we need to set up a timer for
116                 // when host time equals the value written here and
117                 // fire the interrupt
118                 warn!("Not implemented: VM tried to set an RTC alarm");
119             }
120             RTCEOI => {
121                 if reg_val == 0 {
122                     self.interrupt_active = false;
123                 } else {
124                     self.alarm_evt.trigger().unwrap();
125                     self.interrupt_active = true;
126                 }
127             }
128             RTCLR => {
129                 // TODO(sonnyrao): if we ever need to let the VM set it's own time
130                 // then we'll need to keep track of the delta between
131                 // the rtc time it sets and the host's rtc time and
132                 // record that here
133                 warn!("Not implemented: VM tried to set the RTC");
134             }
135             RTCCR => {
136                 self.counter_delta_time = get_epoch_time();
137             }
138             o => panic!("pl030: bad write {}", o),
139         }
140     }
141 
read(&mut self, info: BusAccessInfo, data: &mut [u8])142     fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
143         let data_array = match <&mut [u8; 4]>::try_from(data) {
144             Ok(array) => array,
145             _ => {
146                 warn!("bad write size for pl030");
147                 return;
148             }
149         };
150 
151         let reg_content: u32 = match info.offset {
152             RTCDR => get_epoch_time(),
153             RTCMR => self.match_value,
154             RTCSTAT => self.interrupt_active as u32,
155             RTCLR => {
156                 warn!("invalid read of RTCLR register");
157                 0
158             }
159             RTCCR => get_epoch_time() - self.counter_delta_time,
160             AMBA_ID_OFFSET => PL030_AMBA_ID,
161             AMBA_MASK_OFFSET => PL030_AMBA_MASK,
162 
163             o => panic!("pl030: bad read {}", o),
164         };
165         *data_array = reg_content.to_ne_bytes();
166     }
167 }
168 
169 impl Suspendable for Pl030 {
snapshot(&mut self) -> anyhow::Result<serde_json::Value>170     fn snapshot(&mut self) -> anyhow::Result<serde_json::Value> {
171         serde_json::to_value(Pl030Snapshot {
172             counter_delta_time: self.counter_delta_time,
173             match_value: self.match_value,
174             interrupt_active: self.interrupt_active,
175         })
176         .with_context(|| format!("error serializing {}", self.debug_label()))
177     }
178 
restore(&mut self, data: serde_json::Value) -> anyhow::Result<()>179     fn restore(&mut self, data: serde_json::Value) -> anyhow::Result<()> {
180         let deser: Pl030Snapshot = serde_json::from_value(data)
181             .with_context(|| format!("failed to deserialize {}", self.debug_label()))?;
182         self.counter_delta_time = deser.counter_delta_time;
183         self.match_value = deser.match_value;
184         self.interrupt_active = deser.interrupt_active;
185         Ok(())
186     }
187 
sleep(&mut self) -> anyhow::Result<()>188     fn sleep(&mut self) -> anyhow::Result<()> {
189         Ok(())
190     }
191 
wake(&mut self) -> anyhow::Result<()>192     fn wake(&mut self) -> anyhow::Result<()> {
193         Ok(())
194     }
195 }
196 
197 #[cfg(test)]
198 mod tests {
199     use super::*;
200 
201     // The RTC device is placed at page 2 in the mmio bus
202     const AARCH64_RTC_ADDR: u64 = 0x2000;
203 
pl030_bus_address(offset: u64) -> BusAccessInfo204     fn pl030_bus_address(offset: u64) -> BusAccessInfo {
205         BusAccessInfo {
206             address: AARCH64_RTC_ADDR + offset,
207             offset,
208             id: 0,
209         }
210     }
211 
212     #[test]
test_interrupt_status_register()213     fn test_interrupt_status_register() {
214         let event = IrqEdgeEvent::new().unwrap();
215         let mut device = Pl030::new(event.try_clone().unwrap());
216         let mut register = [0, 0, 0, 0];
217 
218         // set interrupt
219         device.write(pl030_bus_address(RTCEOI), &[1, 0, 0, 0]);
220         device.read(pl030_bus_address(RTCSTAT), &mut register);
221         assert_eq!(register, [1, 0, 0, 0]);
222         event.get_trigger().wait().unwrap();
223 
224         // clear interrupt
225         device.write(pl030_bus_address(RTCEOI), &[0, 0, 0, 0]);
226         device.read(pl030_bus_address(RTCSTAT), &mut register);
227         assert_eq!(register, [0, 0, 0, 0]);
228     }
229 
230     #[test]
test_match_register()231     fn test_match_register() {
232         let mut device = Pl030::new(IrqEdgeEvent::new().unwrap());
233         let mut register = [0, 0, 0, 0];
234 
235         device.write(pl030_bus_address(RTCMR), &[1, 2, 3, 4]);
236         device.read(pl030_bus_address(RTCMR), &mut register);
237         assert_eq!(register, [1, 2, 3, 4]);
238     }
239 }
240