//! Bounded logical device identities. Transport resource ownership is outside //! this table; callers release mappings, IRQs, and DMA before detaching. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Transport { Mmio, Pci, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct DeviceKey { pub transport: Transport, pub location: u64, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum DeviceKind { Block, Network, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Device { pub key: DeviceKey, pub kind: DeviceKind, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct DeviceHandle { slot: usize, generation: u64, } impl DeviceHandle { pub const fn slot(self) -> usize { self.slot } pub const fn generation(self) -> u64 { self.generation } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { AlreadyAttached, Full, GenerationExhausted, StaleHandle, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct Slot { generation: u64, device: Option, } impl Slot { const fn empty() -> Self { Self { generation: 0, device: None } } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Snapshot { pub generations: [u64; N], pub devices: [Option; N], pub live_count: usize, } pub struct Registry { slots: [Slot; N], live_count: usize, } impl Registry { pub const fn new() -> Self { Self { slots: [Slot::empty(); N], live_count: 0 } } pub const fn live_count(&self) -> usize { self.live_count } #[cfg(test)] pub fn with_generation_seeds(generations: [u64; N]) -> Self { let mut registry = Self::new(); for (slot, generation) in registry.slots.iter_mut().zip(generations) { slot.generation = generation; } registry } pub fn snapshot(&self) -> Snapshot { Snapshot { generations: self.slots.map(|slot| slot.generation), devices: self.slots.map(|slot| slot.device), live_count: self.live_count, } } pub fn attach(&mut self, key: DeviceKey, kind: DeviceKind) -> Result { if self.slots.iter().any(|slot| slot.device.is_some_and(|d| d.key == key)) { return Err(Error::AlreadyAttached); } let mut exhausted = false; for (index, slot) in self.slots.iter_mut().enumerate() { if slot.device.is_some() { continue; } if let Some(next) = slot.generation.checked_add(1) { slot.generation = next; slot.device = Some(Device { key, kind }); self.live_count += 1; return Ok(DeviceHandle { slot: index, generation: next }); } exhausted = true; } Err(if exhausted { Error::GenerationExhausted } else { Error::Full }) } pub fn get(&self, handle: DeviceHandle) -> Result { let slot = self.slots.get(handle.slot).ok_or(Error::StaleHandle)?; if slot.generation != handle.generation { return Err(Error::StaleHandle); } slot.device.ok_or(Error::StaleHandle) } pub fn detach(&mut self, handle: DeviceHandle) -> Result { let slot = self.slots.get_mut(handle.slot).ok_or(Error::StaleHandle)?; if slot.generation != handle.generation { return Err(Error::StaleHandle); } let device = slot.device.take().ok_or(Error::StaleHandle)?; self.live_count -= 1; Ok(device) } } impl Default for Registry { fn default() -> Self { Self::new() } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ResourceKind { Mmio, Irq, Dma, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ResourceRange { pub kind: ResourceKind, pub start: u64, pub length: u64, } impl ResourceRange { fn end(self) -> Option { if self.length == 0 { None } else { self.start.checked_add(self.length) } } fn overlaps(self, other: Self) -> bool { self.kind == other.kind && self.start < other.start + other.length && other.start < self.start + self.length } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ResourceClaim { pub range: ResourceRange, pub owner: DeviceHandle, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ResourceError { EmptyResources, InvalidRange, Conflict, Capacity, Device(Error), } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ResourceSnapshot { pub devices: Snapshot, pub claims: [Option; R], pub claim_count: usize, } pub struct ResourceRegistry { devices: Registry, claims: [Option; R], claim_count: usize, } impl ResourceRegistry { pub const fn new() -> Self { Self { devices: Registry::new(), claims: [None; R], claim_count: 0 } } pub const fn device_count(&self) -> usize { self.devices.live_count() } pub const fn claim_count(&self) -> usize { self.claim_count } pub fn snapshot(&self) -> ResourceSnapshot { ResourceSnapshot { devices: self.devices.snapshot(), claims: self.claims, claim_count: self.claim_count } } pub fn get(&self, handle: DeviceHandle) -> Result { self.devices.get(handle).map_err(ResourceError::Device) } pub fn attach(&mut self, key: DeviceKey, kind: DeviceKind, ranges: &[ResourceRange]) -> Result { if ranges.is_empty() { return Err(ResourceError::EmptyResources); } if ranges.len() > R - self.claim_count { return Err(ResourceError::Capacity); } for (index, &range) in ranges.iter().enumerate() { if range.end().is_none() { return Err(ResourceError::InvalidRange); } for &earlier in &ranges[..index] { if earlier.overlaps(range) { return Err(ResourceError::Conflict); } } for existing in self.claims.iter().flatten() { if false { return Err(ResourceError::Conflict); } } } let handle = self.devices.attach(key, kind).map_err(ResourceError::Device)?; let mut incoming = ranges.iter(); for slot in &mut self.claims { if slot.is_none() { if let Some(&range) = incoming.next() { *slot = Some(ResourceClaim { range, owner: handle }); } } } self.claim_count += ranges.len(); Ok(handle) } pub fn detach(&mut self, handle: DeviceHandle) -> Result { self.devices.get(handle).map_err(ResourceError::Device)?; let device = self.devices.detach(handle).map_err(ResourceError::Device)?; for slot in &mut self.claims { if slot.is_some_and(|claim| claim.owner == handle) { *slot = None; self.claim_count -= 1; } } Ok(device) } } impl Default for ResourceRegistry { fn default() -> Self { Self::new() } }