//! Bounded ownership and publication model for a modern Virtio MMIO bridge. pub const MAGIC: u32 = 0x7472_6976; pub const VERSION: u32 = 2; pub const VERSION_1: u32 = 1; pub const SLOT_SIZE: u64 = 0x200; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { InvalidWindow, WrongIdentity, MissingDevice, DuplicateSlot, Full, MissingSlot, StaleClaim, InvalidFeatures, InvalidQueue, WrongPhase, StillReady, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Window { pub base: u64, pub size: u64 } impl Window { pub fn new(base: u64, size: u64, ram_start: u64, ram_end: u64) -> Result { let end = base.checked_add(size).ok_or(Error::InvalidWindow)?; if base == 0 || base % SLOT_SIZE != 0 || size != SLOT_SIZE || ram_start >= ram_end || base < ram_end && ram_start < end { return Err(Error::InvalidWindow); } Ok(Self {base, size}) } pub fn end(self) -> u64 { self.base + self.size } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Identity { pub magic: u32, pub version: u32, pub device_id: u32, pub vendor_id: u32, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Claim { pub window: Window, pub generation: u64 } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Phase { Reset, FeaturesOk, QueueReady, DriverOk } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Queue { pub maximum: u16, pub size: u16, pub desc: u64, pub available: u64, pub used: u64, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct Slot { claim: Claim, identity: Identity, phase: Phase, accepted_low: u32, accepted_high: u32, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Snapshot { entries: [Option; N], pub count: usize, next_generation: u64, } pub struct Registry { entries: [Option; N], count: usize, next_generation: u64, } impl Registry { pub const fn new() -> Self { Self {entries: [None; N], count: 0, next_generation: 1} } pub const fn count(&self) -> usize { self.count } pub fn snapshot(&self) -> Snapshot { Snapshot {entries: self.entries, count: self.count, next_generation: self.next_generation} } fn slot_mut(&mut self, claim: Claim) -> Result<&mut Slot, Error> { let slot = self.entries.iter_mut().flatten() .find(|slot| slot.claim.window == claim.window) .ok_or(Error::MissingSlot)?; if slot.claim.generation != claim.generation { return Err(Error::StaleClaim); } Ok(slot) } pub fn claim(&mut self, window: Window, identity: Identity) -> Result { if identity.magic != MAGIC || identity.version != VERSION { return Err(Error::WrongIdentity); } if identity.device_id == 0 { return Err(Error::MissingDevice); } if self.entries.iter().flatten().any(|entry| window.base < entry.claim.window.end() && entry.claim.window.base < window.end()) { return Err(Error::DuplicateSlot); } let index = self.entries.iter().position(Option::is_none).ok_or(Error::Full)?; let generation = self.next_generation; self.next_generation = generation.checked_add(1).ok_or(Error::Full)?; let claim = Claim {window, generation}; self.entries[index] = Some(Slot {claim, identity, phase: Phase::Reset, accepted_low: 0, accepted_high: 0}); self.count += 1; Ok(claim) } pub fn negotiate(&mut self, claim: Claim, offered: (u32, u32), accepted: (u32, u32)) -> Result<(), Error> { let slot = self.slot_mut(claim)?; if slot.phase != Phase::Reset { return Err(Error::WrongPhase); } if offered.1 & VERSION_1 == 0 || accepted.1 & VERSION_1 == 0 || accepted.0 & !offered.0 != 0 || accepted.1 & !offered.1 != 0 { return Err(Error::InvalidFeatures); } slot.accepted_low = accepted.0; slot.accepted_high = accepted.1; slot.phase = Phase::FeaturesOk; Ok(()) } pub fn queue_ready(&mut self, claim: Claim, queue: Queue) -> Result<(), Error> { let slot = self.slot_mut(claim)?; if slot.phase != Phase::FeaturesOk { return Err(Error::WrongPhase); } if queue.maximum == 0 || queue.maximum > 1024 || queue.size == 0 || !queue.size.is_power_of_two() || queue.size > queue.maximum || queue.desc == 0 || queue.desc % 16 != 0 || queue.available == 0 || queue.available % 2 != 0 || queue.used == 0 || queue.used % 4 != 0 { return Err(Error::InvalidQueue); } slot.phase = Phase::QueueReady; Ok(()) } pub fn driver_ok(&mut self, claim: Claim) -> Result<(), Error> { let slot = self.slot_mut(claim)?; if slot.phase != Phase::QueueReady { return Err(Error::WrongPhase); } slot.phase = Phase::DriverOk; Ok(()) } /// Caller first observes a physical DeviceStatus=0 and QueueReady=0. pub fn observe_reset(&mut self, claim: Claim, status: u32, queue_ready: u32) -> Result<(), Error> { let slot = self.slot_mut(claim)?; if status != 0 || queue_ready != 0 { return Err(Error::StillReady); } slot.phase = Phase::Reset; slot.accepted_low = 0; slot.accepted_high = 0; Ok(()) } pub fn release(&mut self, claim: Claim) -> Result<(), Error> { let index = self.entries.iter().position(|entry| entry.is_some_and(|slot| slot.claim.window == claim.window)) .ok_or(Error::MissingSlot)?; let slot = self.entries[index].ok_or(Error::MissingSlot)?; if slot.claim.generation != claim.generation { return Err(Error::StaleClaim); } if slot.phase != Phase::Reset { return Err(Error::StillReady); } self.entries[index] = None; self.count -= 1; Ok(()) } pub fn phase(&mut self, claim: Claim) -> Result { Ok(self.slot_mut(claim)?.phase) } }