pl031.rs87.93%
1
// Copyright 2025 Google LLC2
//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 at6
//7
// https://www.apache.org/licenses/LICENSE-2.08
//9
// Unless required by applicable law or agreed to in writing, software10
// 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 and13
// limitations under the License.14
15
//! Emulated PL031 Real Time Clock (RTC) device.16
//! See: https://developer.arm.com/documentation/ddi0224/c17
18
use parking_lot::Mutex;19
20
use crate::device::clock::Clock;21
use crate::device::{MmioDev, Pause, Result};22
use crate::mem::emulated::{Action, Mmio};23
use crate::{bitflags, mem};24
25
const RTC_DR: u64 = 0x000;26
const RTC_MR: u64 = 0x004;27
const RTC_LR: u64 = 0x008;28
const RTC_CR: u64 = 0x00C;29
const RTC_IMSC: u64 = 0x010;30
const RTC_RIS: u64 = 0x014;31
const RTC_MIS: u64 = 0x018;32
const RTC_ICR: u64 = 0x01C;33
34
const RTC_PERIPH_ID0: u64 = 0xFE0;35
const RTC_PERIPH_ID1: u64 = 0xFE4;36
const RTC_PERIPH_ID2: u64 = 0xFE8;37
const RTC_PERIPH_ID3: u64 = 0xFEC;38
const RTC_PCELL_ID0: u64 = 0xFF0;39
const RTC_PCELL_ID1: u64 = 0xFF4;40
const RTC_PCELL_ID2: u64 = 0xFF8;41
const RTC_PCELL_ID3: u64 = 0xFFC;42
43
const PERIPH_ID: [u8; 4] = [0x31, 0x10, 0x04, 0x00];44
const PCELL_ID: [u8; 4] = [0x0d, 0xf0, 0x05, 0xb1];45
46
bitflags! {47
#[derive(Default)]48
struct Interrupt(u32) {49
RTCINTR = 1 << 0;50
}51
}52
53
#[derive(Debug, Default)]54
struct Pl031Reg {55
mr: u32,56
lr: u32,57
offset: u32,58
}59
60
#[derive(Debug)]61
pub struct Pl031<C> {62
name: Box<str>,63
reg: Mutex<Pl031Reg>,64
clock: C,65
}66
67
impl<C> Pl031<C> {68
pub fn new(base_addr: u64, clock: C) -> Self {3x69
Self {3x70
name: Box::from(format!("pl031@{base_addr:x}")),3x71
reg: Mutex::new(Pl031Reg::default()),3x72
clock,3x73
}3x74
}3x75
}76
77
impl<C: Clock> Pl031<C> {78
fn now(&self) -> u32 {9x79
self.clock.now().as_secs() as u329x80
}9x81
}82
83
impl<C: Clock> Mmio for Pl031<C> {84
fn size(&self) -> u64 {3x85
0x10003x86
}3x87
88
fn read(&self, offset: u64, _size: u8) -> mem::Result<u64> {51x89
let reg = self.reg.lock();51x90
let val = match offset {51x91
RTC_DR => reg.offset.wrapping_add(self.now()),6x92
RTC_MR => reg.mr,3x93
RTC_LR => reg.lr,3x94
RTC_CR => 1, // RTC is always enabled3x95
RTC_IMSC | RTC_RIS | RTC_MIS => 0, // Interrupts are not supported9x96
RTC_PERIPH_ID0 => PERIPH_ID[0] as u32,3x97
RTC_PERIPH_ID1 => PERIPH_ID[1] as u32,3x98
RTC_PERIPH_ID2 => PERIPH_ID[2] as u32,3x99
RTC_PERIPH_ID3 => PERIPH_ID[3] as u32,3x100
RTC_PCELL_ID0 => PCELL_ID[0] as u32,3x101
RTC_PCELL_ID1 => PCELL_ID[1] as u32,3x102
RTC_PCELL_ID2 => PCELL_ID[2] as u32,3x103
RTC_PCELL_ID3 => PCELL_ID[3] as u32,3x104
_ => {105
log::warn!("{}: read from unknown offset {offset:#x}", self.name);3x106
03x107
}108
};109
log::trace!("{}: read {val:#x} from offset {offset:#x}", self.name);51x110
Ok(val as u64)51x111
}51x112
113
fn write(&self, offset: u64, _size: u8, val: u64) -> mem::Result<Action> {18x114
let mut reg = self.reg.lock();18x115
let val = val as u32;18x116
match offset {18x117
RTC_MR => reg.mr = val,3x118
RTC_LR => {3x119
reg.offset = val.wrapping_sub(self.now());3x120
reg.lr = val;3x121
}3x122
RTC_CR => {} // RTC is always enabled3x123
RTC_IMSC => {124
// Interrupt is alwasy masked125
if Interrupt::from_bits_retain(val).contains(Interrupt::RTCINTR) {3x126
log::warn!("{}: guest tries to unmask interrupt", self.name);3x127
}128
}129
RTC_ICR => {} // Interrupts are not supported3x130
_ => {131
log::warn!(3x132
"{}: 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);18x138
Ok(Action::None)18x139
}18x140
}141
142
impl<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
152
impl<C: Clock> MmioDev for Pl031<C> {}153
154
#[cfg(test)]155
#[path = "pl031_test.rs"]156
mod tests;157