Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/pl030.rs
5392 views
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
use snapshot::AnySnapshot;
14
use vm_control::DeviceId;
15
use vm_control::PlatformDeviceId;
16
17
use crate::BusAccessInfo;
18
use crate::BusDevice;
19
use crate::IrqEdgeEvent;
20
use crate::Suspendable;
21
22
// Register offsets
23
// Data register
24
const RTCDR: u64 = 0x0;
25
// Match register
26
const RTCMR: u64 = 0x4;
27
// Interrupt status register
28
const RTCSTAT: u64 = 0x8;
29
// Interrupt clear register
30
const RTCEOI: u64 = 0x8;
31
// Counter load register
32
const RTCLR: u64 = 0xC;
33
// Counter register
34
const RTCCR: u64 = 0x10;
35
36
// A single 4K page is mapped for this device
37
pub const PL030_AMBA_IOMEM_SIZE: u64 = 0x1000;
38
39
// AMBA id registers are at the end of the allocated memory space
40
const AMBA_ID_OFFSET: u64 = PL030_AMBA_IOMEM_SIZE - 0x20;
41
const AMBA_MASK_OFFSET: u64 = PL030_AMBA_IOMEM_SIZE - 0x28;
42
43
// This is the AMBA id for this device
44
pub const PL030_AMBA_ID: u32 = 0x00041030;
45
pub const PL030_AMBA_MASK: u32 = 0x000FFFFF;
46
47
/// An emulated ARM pl030 RTC
48
pub struct Pl030 {
49
// Event to be used to interrupt the guest for an alarm event
50
alarm_evt: IrqEdgeEvent,
51
52
// This is the delta we subtract from current time to get the
53
// counter value
54
counter_delta_time: u32,
55
56
// This is the value that triggers an alarm interrupt when it
57
// matches with the rtc time
58
match_value: u32,
59
60
// status flag to keep track of whether the interrupt is cleared
61
// or not
62
interrupt_active: bool,
63
}
64
65
#[derive(Debug, Serialize, Deserialize)]
66
struct Pl030Snapshot {
67
counter_delta_time: u32,
68
match_value: u32,
69
interrupt_active: bool,
70
}
71
72
fn 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
79
impl Pl030 {
80
/// Constructs a Pl030 device
81
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
91
impl BusDevice for Pl030 {
92
fn device_id(&self) -> DeviceId {
93
PlatformDeviceId::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
// TODO(sonnyrao): here we need to set up a timer for
117
// when host time equals the value written here and
118
// fire the interrupt
119
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
// TODO(sonnyrao): if we ever need to let the VM set it's own time
131
// then we'll need to keep track of the delta between
132
// the rtc time it sets and the host's rtc time and
133
// record that here
134
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
170
impl 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)]
199
mod tests {
200
use super::*;
201
202
// The RTC device is placed at page 2 in the mmio bus
203
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
// set interrupt
220
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
// clear interrupt
226
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
}
241
242