1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-05-22 18:30:33 +02:00

Use safe-mmio in RTC exercise solution.

This commit is contained in:
Andrew Walbran 2025-05-16 13:40:36 +00:00
parent cb94a34cbf
commit 5fa060546b
5 changed files with 34 additions and 48 deletions

View File

@ -157,8 +157,10 @@ dependencies = [
"bitflags", "bitflags",
"chrono", "chrono",
"log", "log",
"safe-mmio",
"smccc", "smccc",
"spin", "spin",
"zerocopy",
] ]
[[package]] [[package]]

View File

@ -14,5 +14,7 @@ arm-pl011-uart = "0.3.1"
bitflags = "2.9.0" bitflags = "2.9.0"
chrono = { version = "0.4.41", default-features = false } chrono = { version = "0.4.41", default-features = false }
log = "0.4.27" log = "0.4.27"
safe-mmio = "0.2.5"
smccc = "0.2.1" smccc = "0.2.1"
spin = "0.10.0" spin = "0.10.0"
zerocopy = "0.8.25"

View File

@ -72,7 +72,8 @@ initial_pagetable!({
// ANCHOR_END: imports // ANCHOR_END: imports
/// Base address of the PL031 RTC. /// Base address of the PL031 RTC.
const PL031_BASE_ADDRESS: *mut u32 = 0x901_0000 as _; const PL031_BASE_ADDRESS: NonNull<pl031::Registers> =
NonNull::new(0x901_0000 as _).unwrap();
/// The IRQ used by the PL031 RTC. /// The IRQ used by the PL031 RTC.
const PL031_IRQ: IntId = IntId::spi(2); const PL031_IRQ: IntId = IntId::spi(2);
@ -96,7 +97,7 @@ fn main(x0: u64, x1: u64, x2: u64, x3: u64) -> ! {
// SAFETY: `PL031_BASE_ADDRESS` is the base address of a PL031 device, and // SAFETY: `PL031_BASE_ADDRESS` is the base address of a PL031 device, and
// nothing else accesses that address range. // nothing else accesses that address range.
let mut rtc = unsafe { Rtc::new(PL031_BASE_ADDRESS) }; let mut rtc = unsafe { Rtc::new(UniqueMmioPointer::new(PL031_BASE_ADDRESS)) };
let timestamp = rtc.read(); let timestamp = rtc.read();
let time = Utc.timestamp_opt(timestamp.into(), 0).unwrap(); let time = Utc.timestamp_opt(timestamp.into(), 0).unwrap();
info!("RTC: {time}"); info!("RTC: {time}");

View File

@ -12,72 +12,63 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use safe_mmio::fields::{ReadPure, ReadPureWrite, WriteOnly};
use safe_mmio::{field, field_shared, UniqueMmioPointer};
// ANCHOR: solution // ANCHOR: solution
#[repr(C, align(4))] #[repr(C, align(4))]
struct Registers { pub struct Registers {
/// Data register /// Data register
dr: u32, dr: ReadPure<u32>,
/// Match register /// Match register
mr: u32, mr: ReadPureWrite<u32>,
/// Load register /// Load register
lr: u32, lr: ReadPureWrite<u32>,
/// Control register /// Control register
cr: u8, cr: ReadPureWrite<u8>,
_reserved0: [u8; 3], _reserved0: [u8; 3],
/// Interrupt Mask Set or Clear register /// Interrupt Mask Set or Clear register
imsc: u8, imsc: ReadPureWrite<u8>,
_reserved1: [u8; 3], _reserved1: [u8; 3],
/// Raw Interrupt Status /// Raw Interrupt Status
ris: u8, ris: ReadPure<u8>,
_reserved2: [u8; 3], _reserved2: [u8; 3],
/// Masked Interrupt Status /// Masked Interrupt Status
mis: u8, mis: ReadPure<u8>,
_reserved3: [u8; 3], _reserved3: [u8; 3],
/// Interrupt Clear Register /// Interrupt Clear Register
icr: u8, icr: WriteOnly<u8>,
_reserved4: [u8; 3], _reserved4: [u8; 3],
} }
/// Driver for a PL031 real-time clock. /// Driver for a PL031 real-time clock.
#[derive(Debug)] #[derive(Debug)]
pub struct Rtc { pub struct Rtc<'a> {
registers: *mut Registers, registers: UniqueMmioPointer<'a, Registers>,
} }
impl Rtc { impl<'a> Rtc<'a> {
/// Constructs a new instance of the RTC driver for a PL031 device at the /// Constructs a new instance of the RTC driver for a PL031 device with the
/// given base address. /// given set of registers.
/// pub unsafe fn new(registers: UniqueMmioPointer<'a, Registers>) -> Self {
/// # Safety Self { registers }
///
/// The given base address must point to the MMIO control registers of a
/// PL031 device, which must be mapped into the address space of the process
/// as device memory and not have any other aliases.
pub unsafe fn new(base_address: *mut u32) -> Self {
Self { registers: base_address as *mut Registers }
} }
/// Reads the current RTC value. /// Reads the current RTC value.
pub fn read(&self) -> u32 { pub fn read(&self) -> u32 {
// SAFETY: We know that self.registers points to the control registers field_shared!(self.registers, dr).read()
// of a PL031 device which is appropriately mapped.
unsafe { (&raw const (*self.registers).dr).read_volatile() }
} }
/// Writes a match value. When the RTC value matches this then an interrupt /// Writes a match value. When the RTC value matches this then an interrupt
/// will be generated (if it is enabled). /// will be generated (if it is enabled).
pub fn set_match(&mut self, value: u32) { pub fn set_match(&mut self, value: u32) {
// SAFETY: We know that self.registers points to the control registers field!(self.registers, mr).write(value);
// of a PL031 device which is appropriately mapped.
unsafe { (&raw mut (*self.registers).mr).write_volatile(value) }
} }
/// Returns whether the match register matches the RTC value, whether or not /// Returns whether the match register matches the RTC value, whether or not
/// the interrupt is enabled. /// the interrupt is enabled.
pub fn matched(&self) -> bool { pub fn matched(&self) -> bool {
// SAFETY: We know that self.registers points to the control registers let ris = field_shared!(self.registers, ris).read();
// of a PL031 device which is appropriately mapped.
let ris = unsafe { (&raw const (*self.registers).ris).read_volatile() };
(ris & 0x01) != 0 (ris & 0x01) != 0
} }
@ -86,10 +77,8 @@ impl Rtc {
/// This should be true if and only if `matched` returns true and the /// This should be true if and only if `matched` returns true and the
/// interrupt is masked. /// interrupt is masked.
pub fn interrupt_pending(&self) -> bool { pub fn interrupt_pending(&self) -> bool {
// SAFETY: We know that self.registers points to the control registers let mis = field_shared!(self.registers, mis).read();
// of a PL031 device which is appropriately mapped. (mis & 0x01) != 0
let ris = unsafe { (&raw const (*self.registers).mis).read_volatile() };
(ris & 0x01) != 0
} }
/// Sets or clears the interrupt mask. /// Sets or clears the interrupt mask.
@ -98,19 +87,11 @@ impl Rtc {
/// interrupt is disabled. /// interrupt is disabled.
pub fn enable_interrupt(&mut self, mask: bool) { pub fn enable_interrupt(&mut self, mask: bool) {
let imsc = if mask { 0x01 } else { 0x00 }; let imsc = if mask { 0x01 } else { 0x00 };
// SAFETY: We know that self.registers points to the control registers field!(self.registers, imsc).write(imsc);
// of a PL031 device which is appropriately mapped.
unsafe { (&raw mut (*self.registers).imsc).write_volatile(imsc) }
} }
/// Clears a pending interrupt, if any. /// Clears a pending interrupt, if any.
pub fn clear_interrupt(&mut self) { pub fn clear_interrupt(&mut self) {
// SAFETY: We know that self.registers points to the control registers field!(self.registers, icr).write(0x01);
// of a PL031 device which is appropriately mapped.
unsafe { (&raw mut (*self.registers).icr).write_volatile(0x01) }
} }
} }
// SAFETY: `Rtc` just contains a pointer to device memory, which can be
// accessed from any context.
unsafe impl Send for Rtc {}

View File

@ -12,6 +12,6 @@ _main.rs_:
_pl031.rs_: _pl031.rs_:
```rust ```rust,compile_fail
{{#include rtc/src/pl031.rs:solution}} {{#include rtc/src/pl031.rs:solution}}
``` ```