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