Accurately representing a per-processor, fixed-address MMIO device

⚓ Rust    📅 2026-04-12    👤 surdeus    👁️ 1      

surdeus

Hello,

I am writing a bare-metal operating system, currently working on the x86_64 implementation. I am dealing with the local APIC, which is a per-processor interrupt delivery device that is wired to the same MMIO address on each processor. To me, this is most easily represented with a static structure that contains a pointer to the memory, e.g.:

pub struct LocalApic(NonNull<u32>);

pub static LOCAL_APIC: Lazy<LocalApic> = Lazy::new(...);

impl LocalApic {
    pub fn set_enable(&mut self, enable: bool) { ... }

My concern with this is the mutability semantics. Given the structure is static, taking self by exclusive borrow is not possible. I would, however, like to be able to do this, as it more accurately represents some of the operations I may perform on the local APIC. I see two ways to overcome this:

  1. A mutex or other sync mechanism (which would be semantically pointless, given the memory is processor-local).
  2. Lying to the compiler by taking self by shared borrow, and then using NonNull::as_mut.

I lean towards option 2, but I fear that taking self by shared borrow will allow some kind of unsafe behavior in-place. For instance, if I return a type that references the lifetime of self, e.g.:

pub struct LvtTimer<'a>(&'a mut LocalApic);

impl LocalApic {
    fn get_lvt_timer<'a>(&'a self) -> LvtTimer<'a> { ... }

… then I suspect (but I do not know for sure) that I could somehow violate a safety rule somewhere without noticing.

Any input on how to neatly solve this is greatly appreciated.

Thanks.

2 posts - 2 participants

Read full topic

🏷️ Rust_feed