Alioth Code Coverage

pl031.rs0.00%

1// Copyright 2025 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Emulated PL031 Real Time Clock (RTC) device.
16//! See: https://developer.arm.com/documentation/ddi0224/c
17
18use std::time::{SystemTime, UNIX_EPOCH};
19
20use parking_lot::Mutex;
21
22use crate::device::{self, MmioDev, Pause};
23use crate::mem::emulated::{Action, Mmio};
24use crate::{bitflags, mem};
25
26const RTC_DR: u64 = 0x000;
27const RTC_MR: u64 = 0x004;
28const RTC_LR: u64 = 0x008;
29const RTC_CR: u64 = 0x00C;
30const RTC_IMSC: u64 = 0x010;
31const RTC_RIS: u64 = 0x014;
32const RTC_MIS: u64 = 0x018;
33const RTC_ICR: u64 = 0x01C;
34
35const RTC_PERIPH_ID0: u64 = 0xFE0;
36const RTC_PERIPH_ID1: u64 = 0xFE4;
37const RTC_PERIPH_ID2: u64 = 0xFE8;
38const RTC_PERIPH_ID3: u64 = 0xFEC;
39const RTC_PCELL_ID0: u64 = 0xFF0;
40const RTC_PCELL_ID1: u64 = 0xFF4;
41const RTC_PCELL_ID2: u64 = 0xFF8;
42const RTC_PCELL_ID3: u64 = 0xFFC;
43
44const PERIPH_ID: [u8; 4] = [0x31, 0x10, 0x04, 0x00];
45const PCELL_ID: [u8; 4] = [0x0d, 0xf0, 0x05, 0xb1];
46
47bitflags! {
48 #[derive(Default)]
49 struct Interrupt(u32) {
50 RTCINTR = 1 << 0;
51 }
52}
53
54#[derive(Debug, Default)]
55struct Pl031Reg {
56 mr: u32,
57 lr: u32,
58 offset: u32,
59}
60
61#[derive(Debug)]
62pub struct Pl031 {
63 name: Box<str>,
64 reg: Mutex<Pl031Reg>,
65}
66
67impl Pl031 {
68 pub fn new(base_addr: u64) -> Self {
69 Self {
70 name: Box::from(format!("pl031@{base_addr:x}")),
71 reg: Mutex::new(Pl031Reg::default()),
72 }
73 }
74
75 fn now() -> u32 {
76 match SystemTime::now().duration_since(UNIX_EPOCH) {
77 Ok(duration) => duration.as_secs() as u32,
78 Err(_) => {
79 log::error!("System clock is before UNIX_EPOCH. PL031 won't work!");
80 0
81 }
82 }
83 }
84}
85
86impl Mmio for Pl031 {
87 fn size(&self) -> u64 {
88 0x1000
89 }
90
91 fn read(&self, offset: u64, _size: u8) -> mem::Result<u64> {
92 let reg = self.reg.lock();
93 let val = match offset {
94 RTC_DR => reg.offset.wrapping_add(Self::now()),
95 RTC_MR => reg.mr,
96 RTC_LR => reg.lr,
97 RTC_CR => 1, // RTC is always enabled
98 RTC_IMSC | RTC_RIS | RTC_MIS => 0, // Interrupts are not supported
99 RTC_PERIPH_ID0 => PERIPH_ID[0] as u32,
100 RTC_PERIPH_ID1 => PERIPH_ID[1] as u32,
101 RTC_PERIPH_ID2 => PERIPH_ID[2] as u32,
102 RTC_PERIPH_ID3 => PERIPH_ID[3] as u32,
103 RTC_PCELL_ID0 => PCELL_ID[0] as u32,
104 RTC_PCELL_ID1 => PCELL_ID[1] as u32,
105 RTC_PCELL_ID2 => PCELL_ID[2] as u32,
106 RTC_PCELL_ID3 => PCELL_ID[3] as u32,
107 _ => {
108 log::warn!("{}: read from unknown offset {offset:#x}", self.name);
109 0
110 }
111 };
112 log::trace!("{}: read {val:#x} from offset {offset:#x}", self.name);
113 Ok(val as u64)
114 }
115
116 fn write(&self, offset: u64, _size: u8, val: u64) -> mem::Result<Action> {
117 let mut reg = self.reg.lock();
118 let val = val as u32;
119 match offset {
120 RTC_MR => reg.mr = val,
121 RTC_LR => {
122 reg.offset = val.wrapping_sub(Self::now());
123 reg.lr = val;
124 }
125 RTC_CR => {} // RTC is always enabled
126 RTC_IMSC => {
127 // Interrupt is alwasy masked
128 if Interrupt::from_bits_retain(val).contains(Interrupt::RTCINTR) {
129 log::warn!("{}: guest tries to unmask interrupt", self.name);
130 }
131 }
132 RTC_ICR => {} // Interrupts are not supported
133 _ => {
134 log::warn!(
135 "{}: write {val:#x} to unknown offset {offset:#x}",
136 self.name,
137 );
138 }
139 };
140 log::trace!("{}: write {val:#x} to offset {offset:#x}", self.name);
141 Ok(Action::None)
142 }
143}
144
145impl Pause for Pl031 {
146 fn pause(&self) -> device::Result<()> {
147 Ok(())
148 }
149
150 fn resume(&self) -> device::Result<()> {
151 Ok(())
152 }
153}
154
155impl MmioDev for Pl031 {}
156