1use std::convert::TryFrom;
6use std::time::SystemTime;
7use std::time::UNIX_EPOCH;
8
9use anyhow::Context;
10use base::warn;
11use serde::Deserialize;
12use serde::Serialize;
13use snapshot::AnySnapshot;
14
15use crate::pci::CrosvmDeviceId;
16use crate::BusAccessInfo;
17use crate::BusDevice;
18use crate::DeviceId;
19use crate::IrqEdgeEvent;
20use crate::Suspendable;
21
22const RTCDR: u64 = 0x0;
25const RTCMR: u64 = 0x4;
27const RTCSTAT: u64 = 0x8;
29const RTCEOI: u64 = 0x8;
31const RTCLR: u64 = 0xC;
33const RTCCR: u64 = 0x10;
35
36pub const PL030_AMBA_IOMEM_SIZE: u64 = 0x1000;
38
39const AMBA_ID_OFFSET: u64 = PL030_AMBA_IOMEM_SIZE - 0x20;
41const AMBA_MASK_OFFSET: u64 = PL030_AMBA_IOMEM_SIZE - 0x28;
42
43pub const PL030_AMBA_ID: u32 = 0x00041030;
45pub const PL030_AMBA_MASK: u32 = 0x000FFFFF;
46
47pub struct Pl030 {
49 alarm_evt: IrqEdgeEvent,
51
52 counter_delta_time: u32,
55
56 match_value: u32,
59
60 interrupt_active: bool,
63}
64
65#[derive(Debug, Serialize, Deserialize)]
66struct Pl030Snapshot {
67 counter_delta_time: u32,
68 match_value: u32,
69 interrupt_active: bool,
70}
71
72fn get_epoch_time() -> u32 {
73 let epoch_time = SystemTime::now()
74 .duration_since(UNIX_EPOCH)
75 .expect("SystemTime::duration_since failed");
76 epoch_time.as_secs() as u32
77}
78
79impl Pl030 {
80 pub fn new(evt: IrqEdgeEvent) -> Pl030 {
82 Pl030 {
83 alarm_evt: evt,
84 counter_delta_time: get_epoch_time(),
85 match_value: 0,
86 interrupt_active: false,
87 }
88 }
89}
90
91impl BusDevice for Pl030 {
92 fn device_id(&self) -> DeviceId {
93 CrosvmDeviceId::Pl030.into()
94 }
95
96 fn debug_label(&self) -> String {
97 "Pl030".to_owned()
98 }
99
100 fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
101 let data_array = match <&[u8; 4]>::try_from(data) {
102 Ok(array) => array,
103 _ => {
104 warn!("bad write size: {} for pl030", data.len());
105 return;
106 }
107 };
108
109 let reg_val = u32::from_ne_bytes(*data_array);
110 match info.offset {
111 RTCDR => {
112 warn!("invalid write to read-only RTCDR register");
113 }
114 RTCMR => {
115 self.match_value = reg_val;
116 warn!("Not implemented: VM tried to set an RTC alarm");
120 }
121 RTCEOI => {
122 if reg_val == 0 {
123 self.interrupt_active = false;
124 } else {
125 self.alarm_evt.trigger().unwrap();
126 self.interrupt_active = true;
127 }
128 }
129 RTCLR => {
130 warn!("Not implemented: VM tried to set the RTC");
135 }
136 RTCCR => {
137 self.counter_delta_time = get_epoch_time();
138 }
139 o => panic!("pl030: bad write {o}"),
140 }
141 }
142
143 fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
144 let data_array = match <&mut [u8; 4]>::try_from(data) {
145 Ok(array) => array,
146 _ => {
147 warn!("bad write size for pl030");
148 return;
149 }
150 };
151
152 let reg_content: u32 = match info.offset {
153 RTCDR => get_epoch_time(),
154 RTCMR => self.match_value,
155 RTCSTAT => self.interrupt_active as u32,
156 RTCLR => {
157 warn!("invalid read of RTCLR register");
158 0
159 }
160 RTCCR => get_epoch_time() - self.counter_delta_time,
161 AMBA_ID_OFFSET => PL030_AMBA_ID,
162 AMBA_MASK_OFFSET => PL030_AMBA_MASK,
163
164 o => panic!("pl030: bad read {o}"),
165 };
166 *data_array = reg_content.to_ne_bytes();
167 }
168}
169
170impl Suspendable for Pl030 {
171 fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
172 AnySnapshot::to_any(Pl030Snapshot {
173 counter_delta_time: self.counter_delta_time,
174 match_value: self.match_value,
175 interrupt_active: self.interrupt_active,
176 })
177 .with_context(|| format!("error serializing {}", self.debug_label()))
178 }
179
180 fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
181 let deser: Pl030Snapshot = AnySnapshot::from_any(data)
182 .with_context(|| format!("failed to deserialize {}", self.debug_label()))?;
183 self.counter_delta_time = deser.counter_delta_time;
184 self.match_value = deser.match_value;
185 self.interrupt_active = deser.interrupt_active;
186 Ok(())
187 }
188
189 fn sleep(&mut self) -> anyhow::Result<()> {
190 Ok(())
191 }
192
193 fn wake(&mut self) -> anyhow::Result<()> {
194 Ok(())
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 const AARCH64_RTC_ADDR: u64 = 0x2000;
204
205 fn pl030_bus_address(offset: u64) -> BusAccessInfo {
206 BusAccessInfo {
207 address: AARCH64_RTC_ADDR + offset,
208 offset,
209 id: 0,
210 }
211 }
212
213 #[test]
214 fn test_interrupt_status_register() {
215 let event = IrqEdgeEvent::new().unwrap();
216 let mut device = Pl030::new(event.try_clone().unwrap());
217 let mut register = [0, 0, 0, 0];
218
219 device.write(pl030_bus_address(RTCEOI), &[1, 0, 0, 0]);
221 device.read(pl030_bus_address(RTCSTAT), &mut register);
222 assert_eq!(register, [1, 0, 0, 0]);
223 event.get_trigger().wait().unwrap();
224
225 device.write(pl030_bus_address(RTCEOI), &[0, 0, 0, 0]);
227 device.read(pl030_bus_address(RTCSTAT), &mut register);
228 assert_eq!(register, [0, 0, 0, 0]);
229 }
230
231 #[test]
232 fn test_match_register() {
233 let mut device = Pl030::new(IrqEdgeEvent::new().unwrap());
234 let mut register = [0, 0, 0, 0];
235
236 device.write(pl030_bus_address(RTCMR), &[1, 2, 3, 4]);
237 device.read(pl030_bus_address(RTCMR), &mut register);
238 assert_eq!(register, [1, 2, 3, 4]);
239 }
240}