Rust на голому залізі. Полудень.
RTC драйвер
main.rs:
#![no_main] #![no_std] mod exceptions; mod logger; mod pl011; mod pl031; use crate::pl031::Rtc; use arm_gic::gicv3::{IntId, Trigger}; use arm_gic::{irq_enable, wfi}; use chrono::{TimeZone, Utc}; use core::hint::spin_loop; use crate::pl011::Uart; use arm_gic::gicv3::GicV3; use core::panic::PanicInfo; use log::{error, info, trace, LevelFilter}; use smccc::psci::system_off; use smccc::Hvc; /// Базові адреси GICv3. const GICD_BASE_ADDRESS: *mut u64 = 0x800_0000 as _; const GICR_BASE_ADDRESS: *mut u64 = 0x80A_0000 as _; /// Базова адреса основного PL011 UART. const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _; /// Базова адреса PL031 RTC. const PL031_BASE_ADDRESS: *mut u32 = 0x901_0000 as _; /// IRQ, що використовується PL031 RTC. const PL031_IRQ: IntId = IntId::spi(2); #[no_mangle] extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) { // БЕЗПЕКА: `PL011_BASE_ADDRESS` є базовою адресою пристрою PL011, // і ніщо інше не має доступу до цього діапазону адресації. let uart = unsafe { Uart::new(PL011_BASE_ADDRESS) }; logger::init(uart, LevelFilter::Trace).unwrap(); info!("main({:#x}, {:#x}, {:#x}, {:#x})", x0, x1, x2, x3); // БЕЗПЕКА: `GICD_BASE_ADDRESS` і `GICR_BASE_ADDRESS` є базовими // адресами дистриб'ютора і редистриб'ютора GICv3 відповідно, і ніщо // інше не має доступу до цих адресних діапазонів. let mut gic = unsafe { GicV3::new(GICD_BASE_ADDRESS, GICR_BASE_ADDRESS) }; gic.setup(); // БЕЗПЕКА: `PL031_BASE_ADDRESS` є базовою адресою пристрою PL031, // і ніщо інше не має доступу до цього діапазону адресації. let mut rtc = unsafe { Rtc::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, 0x80); gic.set_trigger(PL031_IRQ, Trigger::Level); irq_enable(); gic.enable_interrupt(PL031_IRQ, true); // Чекаємо 3 секунди, без переривань. let target = timestamp + 3; rtc.set_match(target); info!("Чекаємо на {}", 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!("Дочекалися"); // Чекаємо ще 3 секунди на переривання. let target = timestamp + 6; info!("Чекаємо на {}", 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!("Дочекалися"); system_off::<Hvc>().unwrap(); } #[panic_handler] fn panic(info: &PanicInfo) -> ! { error!("{info}"); system_off::<Hvc>().unwrap(); loop {} }
pl031.rs:
#![allow(unused)] fn main() { use core::ptr::{addr_of, addr_of_mut}; #[repr(C, align(4))] struct Registers { /// Регістр даних dr: u32, /// Регістр збігів mr: u32, /// Регістр завантаження lr: u32, /// Регістр управління cr: u8, _reserved0: [u8; 3], /// Регістр установки або очищення маски переривання imsc: u8, _reserved1: [u8; 3], /// Необроблений стан переривання ris: u8, _reserved2: [u8; 3], /// Маскований статус переривання mis: u8, _reserved3: [u8; 3], /// Регістр очищення переривання icr: u8, _reserved4: [u8; 3], } /// Драйвер для годинника реального часу PL031. #[derive(Debug)] pub struct Rtc { registers: *mut Registers, } impl Rtc { /// Створює новий екземпляр драйвера RTC для пристрою PL031 за заданою /// базовою адресою. /// /// # Безпека /// /// Вказана базова адреса має вказувати на регістри керування MMIO /// пристрою PL031, які мають бути відображені у адресному просторі процесу /// як пам'ять пристрою і не мати інших псевдонімів. pub unsafe fn new(base_address: *mut u32) -> Self { Self { registers: base_address as *mut Registers } } /// Зчитує поточне значення RTC. pub fn read(&self) -> u32 { // БЕЗПЕКА: ми знаємо, що self.registers вказує на керуючі // регістри пристрою PL031, який відповідним чином відображено. unsafe { addr_of!((*self.registers).dr).read_volatile() } } /// Записує значення збігу. Коли значення RTC збігається з цим, буде /// згенеровано переривання (якщо його увімкнено). pub fn set_match(&mut self, value: u32) { // БЕЗПЕКА: ми знаємо, що self.registers вказує на керуючі // регістри пристрою PL031, який відповідним чином відображено. unsafe { addr_of_mut!((*self.registers).mr).write_volatile(value) } } /// Повертає, чи відповідає регістр збігу значенню RTC, незалежно від того, /// увімкнено переривання чи ні. pub fn matched(&self) -> bool { // БЕЗПЕКА: ми знаємо, що self.registers вказує на керуючі // регістри пристрою PL031, який відповідним чином відображено. let ris = unsafe { addr_of!((*self.registers).ris).read_volatile() }; (ris & 0x01) != 0 } /// Повертає, чи є переривання в очікуванні. /// /// Це значення має бути істинним тоді і тільки тоді, коли `matched` /// повертає істину і переривання замасковане. pub fn interrupt_pending(&self) -> bool { // БЕЗПЕКА: ми знаємо, що self.registers вказує на керуючі // регістри пристрою PL031, який відповідним чином відображено. let ris = unsafe { addr_of!((*self.registers).mis).read_volatile() }; (ris & 0x01) != 0 } /// Встановлює або очищує маску переривання. /// /// Якщо маска дорівнює істині, переривання увімкнено; якщо ні - /// переривання вимкнено. pub fn enable_interrupt(&mut self, mask: bool) { let imsc = if mask { 0x01 } else { 0x00 }; // БЕЗПЕКА: ми знаємо, що self.registers вказує на керуючі // регістри пристрою PL031, який відповідним чином відображено. unsafe { addr_of_mut!((*self.registers).imsc).write_volatile(imsc) } } /// Очищає очікуване переривання, якщо таке є. pub fn clear_interrupt(&mut self) { // БЕЗПЕКА: ми знаємо, що self.registers вказує на керуючі // регістри пристрою PL031, який відповідним чином відображено. unsafe { addr_of_mut!((*self.registers).icr).write_volatile(0x01) } } } // БЕЗПЕКА: `Rtc` просто містить вказівник на пам'ять пристрою, до якого // можна отримати доступ з будь-якого контексту. unsafe impl Send for Rtc {} }