Alioth Code Coverage

pl031.rs87.93%

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 parking_lot::Mutex;
19
20use crate::device::clock::Clock;
21use crate::device::{MmioDev, Pause, Result};
22use crate::mem::emulated::{Action, Mmio};
23use crate::{bitflags, mem};
24
25const RTC_DR: u64 = 0x000;
26const RTC_MR: u64 = 0x004;
27const RTC_LR: u64 = 0x008;
28const RTC_CR: u64 = 0x00C;
29const RTC_IMSC: u64 = 0x010;
30const RTC_RIS: u64 = 0x014;
31const RTC_MIS: u64 = 0x018;
32const RTC_ICR: u64 = 0x01C;
33
34const RTC_PERIPH_ID0: u64 = 0xFE0;
35const RTC_PERIPH_ID1: u64 = 0xFE4;
36const RTC_PERIPH_ID2: u64 = 0xFE8;
37const RTC_PERIPH_ID3: u64 = 0xFEC;
38const RTC_PCELL_ID0: u64 = 0xFF0;
39const RTC_PCELL_ID1: u64 = 0xFF4;
40const RTC_PCELL_ID2: u64 = 0xFF8;
41const RTC_PCELL_ID3: u64 = 0xFFC;
42
43const PERIPH_ID: [u8; 4] = [0x31, 0x10, 0x04, 0x00];
44const PCELL_ID: [u8; 4] = [0x0d, 0xf0, 0x05, 0xb1];
45
46bitflags! {
47 #[derive(Default)]
48 struct Interrupt(u32) {
49 RTCINTR = 1 << 0;
50 }
51}
52
53#[derive(Debug, Default)]
54struct Pl031Reg {
55 mr: u32,
56 lr: u32,
57 offset: u32,
58}
59
60#[derive(Debug)]
61pub struct Pl031<C> {
62 name: Box<str>,
63 reg: Mutex<Pl031Reg>,
64 clock: C,
65}
66
67impl<C> Pl031<C> {
68 pub fn new(base_addr: u64, clock: C) -> Self {2x
69 Self {2x
70 name: Box::from(format!("pl031@{base_addr:x}")),2x
71 reg: Mutex::new(Pl031Reg::default()),2x
72 clock,2x
73 }2x
74 }2x
75}
76
77impl<C: Clock> Pl031<C> {
78 fn now(&self) -> u32 {6x
79 self.clock.now().as_secs() as u326x
80 }6x
81}
82
83impl<C: Clock> Mmio for Pl031<C> {
84 fn size(&self) -> u64 {2x
85 0x10002x
86 }2x
87
88 fn read(&self, offset: u64, _size: u8) -> mem::Result<u64> {34x
89 let reg = self.reg.lock();34x
90 let val = match offset {34x
91 RTC_DR => reg.offset.wrapping_add(self.now()),4x
92 RTC_MR => reg.mr,2x
93 RTC_LR => reg.lr,2x
94 RTC_CR => 1, // RTC is always enabled2x
95 RTC_IMSC | RTC_RIS | RTC_MIS => 0, // Interrupts are not supported6x
96 RTC_PERIPH_ID0 => PERIPH_ID[0] as u32,2x
97 RTC_PERIPH_ID1 => PERIPH_ID[1] as u32,2x
98 RTC_PERIPH_ID2 => PERIPH_ID[2] as u32,2x
99 RTC_PERIPH_ID3 => PERIPH_ID[3] as u32,2x
100 RTC_PCELL_ID0 => PCELL_ID[0] as u32,2x
101 RTC_PCELL_ID1 => PCELL_ID[1] as u32,2x
102 RTC_PCELL_ID2 => PCELL_ID[2] as u32,2x
103 RTC_PCELL_ID3 => PCELL_ID[3] as u32,2x
104 _ => {
105 log::warn!("{}: read from unknown offset {offset:#x}", self.name);2x
106 02x
107 }
108 };
109 log::trace!("{}: read {val:#x} from offset {offset:#x}", self.name);34x
110 Ok(val as u64)34x
111 }34x
112
113 fn write(&self, offset: u64, _size: u8, val: u64) -> mem::Result<Action> {12x
114 let mut reg = self.reg.lock();12x
115 let val = val as u32;12x
116 match offset {12x
117 RTC_MR => reg.mr = val,2x
118 RTC_LR => {2x
119 reg.offset = val.wrapping_sub(self.now());2x
120 reg.lr = val;2x
121 }2x
122 RTC_CR => {} // RTC is always enabled2x
123 RTC_IMSC => {
124 // Interrupt is alwasy masked
125 if Interrupt::from_bits_retain(val).contains(Interrupt::RTCINTR) {2x
126 log::warn!("{}: guest tries to unmask interrupt", self.name);2x
127 }
128 }
129 RTC_ICR => {} // Interrupts are not supported2x
130 _ => {
131 log::warn!(2x
132 "{}: write {val:#x} to unknown offset {offset:#x}",
133 self.name,
134 );
135 }
136 };
137 log::trace!("{}: write {val:#x} to offset {offset:#x}", self.name);12x
138 Ok(Action::None)12x
139 }12x
140}
141
142impl<C: Clock> Pause for Pl031<C> {
143 fn pause(&self) -> Result<()> {
144 Ok(())
145 }
146
147 fn resume(&self) -> Result<()> {
148 Ok(())
149 }
150}
151
152impl<C: Clock> MmioDev for Pl031<C> {}
153
154#[cfg(test)]
155#[path = "pl031_test.rs"]
156mod tests;
157