use lancer_core::untyped::UntypedState; use x86_64::PhysAddr; use crate::static_vec::StaticVec; use crate::types::{ObjectId, Pid, Priority}; pub use lancer_core::object_tag::ObjectTag; macro_rules! object_accessors { ($( $variant:ident => { $( ref $ref_name:ident -> $ref_ty:ty; )* $( mut $mut_name:ident -> $mut_ty:ty; )* } )*) => { $( $( #[allow(dead_code)] pub fn $ref_name(&self) -> Result<&$ref_ty, crate::error::KernelError> { match self { Self::$variant(inner) => Ok(inner), _ => Err(crate::error::KernelError::InvalidType), } } )* $( #[allow(dead_code)] pub fn $mut_name(&mut self) -> Result<&mut $mut_ty, crate::error::KernelError> { match self { Self::$variant(inner) => Ok(inner), _ => Err(crate::error::KernelError::InvalidType), } } )* )* }; } #[derive(Debug)] #[allow(dead_code, clippy::large_enum_variant)] pub enum ObjectData { Endpoint(EndpointData), Notification(NotificationData), Process(ProcessObjectData), SchedContext(SchedContextData), IrqHandler(IrqHandlerData), Framebuffer(FramebufferData), PciDevice(PciDeviceData), CNode(CNodeData), Untyped(UntypedObjectData), Frame(FrameObjectData), } impl ObjectData { pub const fn tag(&self) -> ObjectTag { match self { Self::Endpoint(_) => ObjectTag::Endpoint, Self::Notification(_) => ObjectTag::Notification, Self::Process(_) => ObjectTag::Process, Self::SchedContext(_) => ObjectTag::SchedContext, Self::IrqHandler(_) => ObjectTag::IrqHandler, Self::Framebuffer(_) => ObjectTag::Framebuffer, Self::PciDevice(_) => ObjectTag::PciDevice, Self::CNode(_) => ObjectTag::CNode, Self::Untyped(_) => ObjectTag::Untyped, Self::Frame(_) => ObjectTag::Frame, } } object_accessors! { Endpoint => { ref as_endpoint -> EndpointData; mut as_endpoint_mut -> EndpointData; } Notification => { ref as_notification -> NotificationData; mut as_notification_mut -> NotificationData; } Process => { ref as_process -> ProcessObjectData; mut as_process_mut -> ProcessObjectData; } SchedContext => { ref as_sched_context -> SchedContextData; mut as_sched_context_mut -> SchedContextData; } IrqHandler => { ref as_irq_handler -> IrqHandlerData; mut as_irq_handler_mut -> IrqHandlerData; } Framebuffer => { ref as_framebuffer -> FramebufferData; mut as_framebuffer_mut -> FramebufferData; } PciDevice => { ref as_pci_device -> PciDeviceData; mut as_pci_device_mut -> PciDeviceData; } CNode => { ref as_cnode -> CNodeData; mut as_cnode_mut -> CNodeData; } Untyped => { ref as_untyped -> UntypedObjectData; mut as_untyped_mut -> UntypedObjectData; } Frame => { ref as_frame -> FrameObjectData; mut as_frame_mut -> FrameObjectData; } } } pub const MAX_NOTIFICATION_WAITERS: usize = 4; #[derive(Debug, Clone, Copy)] pub struct PidQueue { pub head: Option, pub tail: Option, len: u16, } impl PidQueue { pub const fn new() -> Self { Self { head: None, tail: None, len: 0, } } #[allow(dead_code)] pub const fn len(&self) -> u16 { self.len } #[allow(dead_code)] pub const fn is_empty(&self) -> bool { self.len == 0 } pub fn check_capacity(&self) -> Result<(), crate::error::KernelError> { if (self.len as usize) < crate::types::MAX_PIDS { Ok(()) } else { Err(crate::error::KernelError::ResourceExhausted) } } pub fn inc_len(&mut self) -> Result<(), crate::error::KernelError> { self.len = self .len .checked_add(1) .ok_or(crate::error::KernelError::ResourceExhausted)?; Ok(()) } pub fn dec_len(&mut self) { match self.len.checked_sub(1) { Some(n) => self.len = n, None => { crate::show!(ipc, error, "pidqueue dec_len underflow, clamping to 0"); self.len = 0; } } } pub fn validate(&self, next_link: impl Fn(Pid) -> Option) -> bool { let head_tail_ok = match self.len { 0 => self.head.is_none() && self.tail.is_none(), _ => self.head.is_some() && self.tail.is_some(), }; if !head_tail_ok { crate::show!(ipc, error, "pidqueue invariant violation len {} head {:?} tail {:?}", self.len, self.head, self.tail ); return false; } let max = crate::types::MAX_PIDS; let mut last = None; let chain_len = core::iter::successors(self.head, |&cur| { last = Some(cur); next_link(cur) }) .take(max + 1) .count(); if chain_len != self.len as usize { crate::show!(ipc, error, "pidqueue length mismatch chain {} stored {}", chain_len, self.len ); return false; } match (self.tail, last) { (Some(t), Some(l)) => { if t == l { true } else { crate::show!(ipc, error, "pidqueue tail mismatch stored {} walked {}", t.raw(), l.raw() ); false } } (None, None) => true, _ => { crate::show!(ipc, error, "pidqueue tail inconsistency stored {:?} walked {:?}", self.tail, last ); false } } } pub fn from_parts(head: Option, tail: Option, len: u16) -> Self { if len == 0 { assert!( head.is_none() && tail.is_none(), "PidQueue invariant: len=0 requires head=None, tail=None" ); Self::new() } else { assert!( head.is_some() && tail.is_some(), "PidQueue invariant: len>0 requires head and tail" ); Self { head, tail, len } } } } #[derive(Debug, Clone)] pub struct EndpointData { pub senders: PidQueue, pub receivers: PidQueue, pub holder: Option, } impl EndpointData { pub const fn new() -> Self { Self { senders: PidQueue::new(), receivers: PidQueue::new(), holder: None, } } } #[derive(Debug, Clone)] pub struct NotificationData { pub word: u64, pub waiters: StaticVec, } impl NotificationData { pub const fn new() -> Self { Self { word: 0, waiters: StaticVec::new(), } } } #[derive(Debug, Clone, Copy)] pub struct ProcessObjectData { pub pid: Pid, } #[allow(dead_code)] #[derive(Debug, Clone, Copy)] pub struct SchedContextData { pub budget_us: u64, pub period_us: u64, pub remaining_us: u64, pub priority: Priority, pub replenish_at: Option, pub attached_pid: Option, } impl SchedContextData { pub const fn new(budget_us: u64, period_us: u64, priority: Priority) -> Self { Self { budget_us, period_us, remaining_us: budget_us, priority, replenish_at: None, attached_pid: None, } } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct IoPort(u16); impl IoPort { pub const fn new(port: u16) -> Self { Self(port) } pub(crate) const fn raw(self) -> u16 { self.0 } } impl core::fmt::Display for IoPort { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{:#x}", self.0) } } #[derive(Debug, Clone, Copy)] pub struct PortRange { base: u16, count: u16, } impl PortRange { pub const fn new(base: u16, count: u16) -> Option { if count == 0 { return None; } let end = base as u32 + count as u32; if end > 0x10000 { return None; } Some(Self { base, count }) } pub const fn base(self) -> u16 { self.base } pub const fn count(self) -> u16 { self.count } pub fn contains(self, port: IoPort) -> bool { (port.0 as u32) >= (self.base as u32) && (port.0 as u32) < (self.base as u32) + (self.count as u32) } } #[derive(Debug, Clone, Copy)] pub enum IrqSource { Ioapic { gsi: crate::arch::ioapic::Gsi, }, Msix { device_table_idx: u8, entry_idx: u16, }, } #[allow(dead_code)] #[derive(Debug, Clone, Copy)] pub struct IrqHandlerData { pub vector: crate::arch::idt::IrqVector, pub source: IrqSource, pub port_range: PortRange, } #[allow(dead_code)] #[derive(Debug, Clone, Copy)] pub struct FramebufferData { phys_addr: u64, width: u32, height: u32, pitch: u32, bpp: u16, byte_size: u64, } impl FramebufferData { #[allow(dead_code)] pub fn new( phys_addr: u64, width: u32, height: u32, pitch: u32, bpp: u16, byte_size: u64, ) -> Option { if width == 0 || height == 0 || bpp == 0 { return None; } let bytes_per_pixel = (bpp as u32).checked_div(8)?; let min_pitch = width.checked_mul(bytes_per_pixel)?; if pitch < min_pitch { return None; } let computed_size = (pitch as u64).checked_mul(height as u64)?; if byte_size < computed_size { return None; } Some(Self { phys_addr, width, height, pitch, bpp, byte_size, }) } pub const fn phys_addr(&self) -> u64 { self.phys_addr } #[allow(dead_code)] pub const fn width(&self) -> u32 { self.width } #[allow(dead_code)] pub const fn height(&self) -> u32 { self.height } #[allow(dead_code)] pub const fn pitch(&self) -> u32 { self.pitch } #[allow(dead_code)] pub const fn bpp(&self) -> u16 { self.bpp } pub const fn byte_size(&self) -> u64 { self.byte_size } } #[derive(Debug, Clone, Copy)] pub struct PciDeviceData { pub device_table_idx: u8, } #[derive(Debug, Clone, Copy)] pub struct CNodeData { pub slots_phys: x86_64::PhysAddr, pub size_bits: u8, pub frame_count: u8, } #[allow(dead_code)] #[derive(Debug)] pub struct UntypedObjectData { pub phys_base: PhysAddr, pub state: UntypedState, pub first_child_id: Option, } impl UntypedObjectData { #[allow(dead_code)] pub fn new(phys_base: PhysAddr, size_bits: u8, is_device: bool) -> Self { Self { phys_base, state: UntypedState::new(size_bits, is_device), first_child_id: None, } } } pub const MAX_FRAME_MAPPINGS: usize = 4; #[derive(Debug, Clone, Copy)] pub struct FrameMapping { pub pid: Pid, pub vaddr: x86_64::VirtAddr, } #[allow(dead_code)] #[derive(Debug)] pub struct FrameObjectData { pub phys_addr: PhysAddr, pub from_untyped: bool, pub mappings: StaticVec, } impl FrameObjectData { #[allow(dead_code)] pub fn new(phys_addr: PhysAddr) -> Self { Self { phys_addr, from_untyped: false, mappings: StaticVec::new(), } } pub fn new_from_untyped(phys_addr: PhysAddr) -> Self { Self { phys_addr, from_untyped: true, mappings: StaticVec::new(), } } pub fn add_mapping( &mut self, pid: Pid, vaddr: x86_64::VirtAddr, ) -> Result<(), crate::error::KernelError> { self.mappings .push(FrameMapping { pid, vaddr }) .map_err(|_| crate::error::KernelError::ResourceExhausted) } pub fn remove_mapping(&mut self, pid: Pid, vaddr: x86_64::VirtAddr) -> bool { let before = self.mappings.len(); self.mappings .retain(|m| !(m.pid == pid && m.vaddr == vaddr)); self.mappings.len() < before } #[allow(dead_code)] pub fn remove_all_for_pid(&mut self, pid: Pid) { self.mappings.retain(|m| m.pid != pid); } }