Yalın (Bare) Metal Rust Öğleden Sonra
RTC driver
main.rs:
#![no_main] #![no_std] mod exceptions; mod logger; mod pl031; use crate::pl031::Rtc; use arm_gic::{IntId, Trigger, irq_enable, wfi}; use chrono::{TimeZone, Utc}; use core::hint::spin_loop; use aarch64_paging::paging::Attributes; use aarch64_rt::{InitialPagetable, entry, initial_pagetable}; use arm_gic::gicv3::GicV3; use arm_gic::gicv3::registers::{Gicd, GicrSgi}; use arm_pl011_uart::{PL011Registers, Uart, UniqueMmioPointer}; use core::panic::PanicInfo; use core::ptr::NonNull; use log::{LevelFilter, error, info, trace}; use smccc::Hvc; use smccc::psci::system_off; /// Base addresses of the GICv3. const GICD_BASE_ADDRESS: *mut Gicd = 0x800_0000 as _; const GICR_BASE_ADDRESS: *mut GicrSgi = 0x80A_0000 as _; /// Base address of the primary PL011 UART. const PL011_BASE_ADDRESS: NonNull<PL011Registers> = NonNull::new(0x900_0000 as _).unwrap(); /// Attributes to use for device memory in the initial identity map. const DEVICE_ATTRIBUTES: Attributes = Attributes::VALID .union(Attributes::ATTRIBUTE_INDEX_0) .union(Attributes::ACCESSED) .union(Attributes::UXN); /// Attributes to use for normal memory in the initial identity map. const MEMORY_ATTRIBUTES: Attributes = Attributes::VALID .union(Attributes::ATTRIBUTE_INDEX_1) .union(Attributes::INNER_SHAREABLE) .union(Attributes::ACCESSED) .union(Attributes::NON_GLOBAL); initial_pagetable!({ let mut idmap = [0; 512]; // 1 GiB of device memory. idmap[0] = DEVICE_ATTRIBUTES.bits(); // 1 GiB of normal memory. idmap[1] = MEMORY_ATTRIBUTES.bits() | 0x40000000; // Another 1 GiB of device memory starting at 256 GiB. idmap[256] = DEVICE_ATTRIBUTES.bits() | 0x4000000000; InitialPagetable(idmap) }); /// Base address of the PL031 RTC. const PL031_BASE_ADDRESS: NonNull<pl031::Registers> = NonNull::new(0x901_0000 as _).unwrap(); /// The IRQ used by the PL031 RTC. const PL031_IRQ: IntId = IntId::spi(2); entry!(main); fn main(x0: u64, x1: u64, x2: u64, x3: u64) -> ! { // SAFETY: `PL011_BASE_ADDRESS` is the base address of a PL011 device, and // nothing else accesses that address range. let uart = unsafe { Uart::new(UniqueMmioPointer::new(PL011_BASE_ADDRESS)) }; logger::init(uart, LevelFilter::Trace).unwrap(); info!("main({:#x}, {:#x}, {:#x}, {:#x})", x0, x1, x2, x3); // SAFETY: `GICD_BASE_ADDRESS` and `GICR_BASE_ADDRESS` are the base // addresses of a GICv3 distributor and redistributor respectively, and // nothing else accesses those address ranges. let mut gic = unsafe { GicV3::new(GICD_BASE_ADDRESS, GICR_BASE_ADDRESS, 1, false) }; gic.setup(0); // SAFETY: `PL031_BASE_ADDRESS` is the base address of a PL031 device, and // nothing else accesses that address range. let mut rtc = unsafe { Rtc::new(UniqueMmioPointer::new(PL031_BASE_ADDRESS)) }; let timestamp = rtc.read(); let time = Utc.timestamp_opt(timestamp.into(), 0).unwrap(); info!("RTC: {time}"); GicV3::set_priority_mask(0xff); gic.set_interrupt_priority(PL031_IRQ, None, 0x80); gic.set_trigger(PL031_IRQ, None, Trigger::Level); irq_enable(); gic.enable_interrupt(PL031_IRQ, None, true); // Wait for 3 seconds, without interrupts. let target = timestamp + 3; rtc.set_match(target); info!("Waiting for {}", Utc.timestamp_opt(target.into(), 0).unwrap()); trace!( "matched={}, interrupt_pending={}", rtc.matched(), rtc.interrupt_pending() ); while !rtc.matched() { spin_loop(); } trace!( "matched={}, interrupt_pending={}", rtc.matched(), rtc.interrupt_pending() ); info!("Finished waiting"); // Wait another 3 seconds for an interrupt. let target = timestamp + 6; info!("Waiting for {}", Utc.timestamp_opt(target.into(), 0).unwrap()); rtc.set_match(target); rtc.clear_interrupt(); rtc.enable_interrupt(true); trace!( "matched={}, interrupt_pending={}", rtc.matched(), rtc.interrupt_pending() ); while !rtc.interrupt_pending() { wfi(); } trace!( "matched={}, interrupt_pending={}", rtc.matched(), rtc.interrupt_pending() ); info!("Finished waiting"); system_off::<Hvc>().unwrap(); panic!("system_off returned"); } #[panic_handler] fn panic(info: &PanicInfo) -> ! { error!("{info}"); system_off::<Hvc>().unwrap(); loop {} }
pl031.rs:
#![allow(unused)] fn main() { #[repr(C, align(4))] pub struct Registers { /// Data register dr: ReadPure<u32>, /// Match register mr: ReadPureWrite<u32>, /// Load register lr: ReadPureWrite<u32>, /// Control register cr: ReadPureWrite<u8>, _reserved0: [u8; 3], /// Interrupt Mask Set or Clear register imsc: ReadPureWrite<u8>, _reserved1: [u8; 3], /// Raw Interrupt Status ris: ReadPure<u8>, _reserved2: [u8; 3], /// Masked Interrupt Status mis: ReadPure<u8>, _reserved3: [u8; 3], /// Interrupt Clear Register icr: WriteOnly<u8>, _reserved4: [u8; 3], } /// Driver for a PL031 real-time clock. #[derive(Debug)] pub struct Rtc<'a> { registers: UniqueMmioPointer<'a, Registers>, } impl<'a> Rtc<'a> { /// Constructs a new instance of the RTC driver for a PL031 device with the /// given set of registers. pub fn new(registers: UniqueMmioPointer<'a, Registers>) -> Self { Self { registers } } /// Reads the current RTC value. pub fn read(&self) -> u32 { field_shared!(self.registers, dr).read() } /// Writes a match value. When the RTC value matches this then an interrupt /// will be generated (if it is enabled). pub fn set_match(&mut self, value: u32) { field!(self.registers, mr).write(value); } /// Returns whether the match register matches the RTC value, whether or not /// the interrupt is enabled. pub fn matched(&self) -> bool { let ris = field_shared!(self.registers, ris).read(); (ris & 0x01) != 0 } /// Returns whether there is currently an interrupt pending. /// /// This should be true if and only if `matched` returns true and the /// interrupt is masked. pub fn interrupt_pending(&self) -> bool { let mis = field_shared!(self.registers, mis).read(); (mis & 0x01) != 0 } /// Sets or clears the interrupt mask. /// /// When the mask is true the interrupt is enabled; when it is false the /// interrupt is disabled. pub fn enable_interrupt(&mut self, mask: bool) { let imsc = if mask { 0x01 } else { 0x00 }; field!(self.registers, imsc).write(imsc); } /// Clears a pending interrupt, if any. pub fn clear_interrupt(&mut self) { field!(self.registers, icr).write(0x01); } } }