Device Driver Architecture
VeridianOS implements a user-space driver model that prioritizes isolation, security, and fault tolerance while maintaining high performance through capability-based hardware access and zero-copy communication.
Design Philosophy
Core Principles
- User-Space Isolation: All device drivers run in separate user-space processes
- Capability-Based Access: Hardware resources accessed only through unforgeable capabilities
- Fault Tolerance: Driver crashes don't bring down the entire system
- Hot-Pluggable: Drivers can be loaded, unloaded, and restarted dynamically
- Performance: Zero-copy DMA and efficient interrupt handling
Benefits over Kernel Drivers
| Aspect | User-Space Drivers | Kernel Drivers |
|---|---|---|
| Fault Isolation | Driver crash isolated | System-wide failure |
| Security | Capability-controlled access | Full kernel privileges |
| Debugging | Standard debugging tools | Kernel debugging required |
| Development | User-space comfort | Kernel constraints |
| Memory Protection | Full MMU protection | No protection |
| Hot-Plug | Dynamic load/unload | Static or complex |
Driver Framework
Driver Trait
All drivers implement a common interface:
#![allow(unused)] fn main() { #[async_trait] pub trait Driver: Send + Sync { /// Initialize driver with hardware capabilities async fn init(&mut self, capabilities: HardwareCapabilities) -> Result<(), DriverError>; /// Start driver operation async fn start(&mut self) -> Result<(), DriverError>; /// Handle hardware interrupt async fn handle_interrupt(&self, vector: u32) -> Result<(), DriverError>; /// Handle device hotplug event async fn hotplug(&self, event: HotplugEvent) -> Result<(), DriverError>; /// Shutdown driver gracefully async fn shutdown(&mut self) -> Result<(), DriverError>; /// Get driver metadata fn metadata(&self) -> DriverMetadata; } pub struct DriverMetadata { pub name: String, pub version: Version, pub vendor_id: Option<u16>, pub device_id: Option<u16>, pub device_class: DeviceClass, pub capabilities_required: Vec<CapabilityType>, } }
Hardware Capabilities
Access to hardware resources is granted through capabilities:
#![allow(unused)] fn main() { pub struct HardwareCapabilities { /// Memory-mapped I/O regions pub mmio_regions: Vec<MmioRegion>, /// Interrupt lines pub interrupts: Vec<InterruptLine>, /// DMA capability for memory transfers pub dma_capability: Option<DmaCapability>, /// PCI configuration space access pub pci_config: Option<PciConfigCapability>, /// I/O port access (x86_64 only) #[cfg(target_arch = "x86_64")] pub io_ports: Vec<IoPortRange>, } pub struct MmioRegion { /// Physical base address pub base_addr: PhysAddr, /// Region size in bytes pub size: usize, /// Access permissions pub permissions: MmioPermissions, /// Cache policy pub cache_policy: CachePolicy, } #[derive(Debug, Clone, Copy)] pub struct MmioPermissions { pub read: bool, pub write: bool, pub execute: bool, } #[derive(Debug, Clone, Copy)] pub enum CachePolicy { /// Cacheable, write-back WriteBack, /// Cacheable, write-through WriteThrough, /// Uncacheable Uncached, /// Write-combining (for framebuffers) WriteCombining, } }
Hardware Abstraction Layer
Register Access
Safe register access through memory-mapped I/O:
#![allow(unused)] fn main() { pub struct RegisterBlock<T> { base: VirtAddr, _phantom: PhantomData<T>, } impl<T> RegisterBlock<T> { /// Create new register block from capability pub fn new(mmio_cap: MmioCapability) -> Result<Self, DriverError> { let base = map_mmio_region(mmio_cap)?; Ok(Self { base, _phantom: PhantomData, }) } /// Read 32-bit register pub fn read32(&self, offset: usize) -> u32 { unsafe { let addr = self.base.as_ptr::<u32>().add(offset / 4); core::ptr::read_volatile(addr) } } /// Write 32-bit register pub fn write32(&self, offset: usize, value: u32) { unsafe { let addr = self.base.as_ptr::<u32>().add(offset / 4); core::ptr::write_volatile(addr, value); } } /// Atomic read-modify-write pub fn modify32<F>(&self, offset: usize, f: F) where F: FnOnce(u32) -> u32, { let old = self.read32(offset); let new = f(old); self.write32(offset, new); } } // Type-safe register definitions #[repr(C)] pub struct NetworkControllerRegs { pub control: RW<u32>, // Offset 0x00 pub status: RO<u32>, // Offset 0x04 pub interrupt_mask: RW<u32>, // Offset 0x08 pub dma_addr: RW<u64>, // Offset 0x0C _reserved: [u8; 240], } // Register field access impl NetworkControllerRegs { pub fn enable(&mut self) { self.control.modify(|val| val | CONTROL_ENABLE); } pub fn is_link_up(&self) -> bool { self.status.read() & STATUS_LINK_UP != 0 } } }
DMA Operations
Zero-copy DMA for high-performance data transfer:
#![allow(unused)] fn main() { pub struct DmaBuffer { /// Virtual address for CPU access pub virt_addr: VirtAddr, /// Physical address for device access pub phys_addr: PhysAddr, /// Buffer size pub size: usize, /// DMA direction pub direction: DmaDirection, } #[derive(Debug, Clone, Copy)] pub enum DmaDirection { /// Device to memory FromDevice, /// Memory to device ToDevice, /// Bidirectional Bidirectional, } impl DmaBuffer { /// Allocate DMA buffer pub fn allocate( size: usize, direction: DmaDirection, dma_cap: &DmaCapability, ) -> Result<Self, DriverError> { let layout = Layout::from_size_align(size, PAGE_SIZE)?; // Allocate physically contiguous memory let phys_addr = allocate_dma_memory(layout, dma_cap)?; // Map into driver's address space let virt_addr = map_dma_buffer(phys_addr, size, direction)?; Ok(Self { virt_addr, phys_addr, size, direction, }) } /// Sync buffer for CPU access pub fn sync_for_cpu(&self) -> Result<(), DriverError> { match self.direction { DmaDirection::FromDevice | DmaDirection::Bidirectional => { invalidate_cache_range(self.virt_addr, self.size); } _ => {} } Ok(()) } /// Sync buffer for device access pub fn sync_for_device(&self) -> Result<(), DriverError> { match self.direction { DmaDirection::ToDevice | DmaDirection::Bidirectional => { flush_cache_range(self.virt_addr, self.size); } _ => {} } Ok(()) } } // Scatter-gather DMA pub struct ScatterGatherList { pub entries: Vec<DmaEntry>, } pub struct DmaEntry { pub addr: PhysAddr, pub len: usize, } impl ScatterGatherList { /// Create scatter-gather list from user buffer pub fn from_user_buffer( buffer: UserBuffer, dma_cap: &DmaCapability, ) -> Result<Self, DriverError> { let mut entries = Vec::new(); for page in buffer.pages() { let phys_addr = virt_to_phys(page.virt_addr)?; entries.push(DmaEntry { addr: phys_addr, len: page.len, }); } Ok(Self { entries }) } } }
Interrupt Handling
Efficient interrupt handling with capability-based access:
#![allow(unused)] fn main() { pub struct InterruptHandler { vector: u32, handler: Box<dyn Fn() -> InterruptResult + Send + Sync>, } #[derive(Debug, Clone, Copy)] pub enum InterruptResult { /// Interrupt handled Handled, /// Not our interrupt NotHandled, /// Wake up blocked thread WakeThread(ThreadId), /// Schedule bottom half ScheduleBottomHalf, } impl InterruptHandler { /// Register interrupt handler pub fn register( vector: u32, handler: impl Fn() -> InterruptResult + Send + Sync + 'static, interrupt_cap: InterruptCapability, ) -> Result<Self, DriverError> { // Validate capability validate_interrupt_capability(&interrupt_cap, vector)?; // Register with kernel sys_register_interrupt_handler(vector, current_process_id())?; Ok(Self { vector, handler: Box::new(handler), }) } /// Enable interrupt pub fn enable(&self) -> Result<(), DriverError> { sys_enable_interrupt(self.vector) } /// Disable interrupt pub fn disable(&self) -> Result<(), DriverError> { sys_disable_interrupt(self.vector) } } // Message-signaled interrupts (MSI/MSI-X) pub struct MsiHandler { pub vectors: Vec<u32>, pub handlers: Vec<InterruptHandler>, } impl MsiHandler { /// Configure MSI interrupts pub fn configure_msi( pci_dev: &PciDevice, num_vectors: usize, ) -> Result<Self, DriverError> { let vectors = pci_dev.allocate_msi_vectors(num_vectors)?; let mut handlers = Vec::new(); for vector in &vectors { let handler = InterruptHandler::register( *vector, move || handle_msi_interrupt(*vector), pci_dev.interrupt_capability(), )?; handlers.push(handler); } Ok(Self { vectors, handlers }) } } }
Device Classes
Block Device Framework
#![allow(unused)] fn main() { #[async_trait] pub trait BlockDevice: Driver { /// Read blocks from device async fn read_blocks( &self, start_block: u64, blocks: &mut [Block], ) -> Result<usize, BlockError>; /// Write blocks to device async fn write_blocks( &self, start_block: u64, blocks: &[Block], ) -> Result<usize, BlockError>; /// Flush cached writes async fn flush(&self) -> Result<(), BlockError>; /// Get device information fn info(&self) -> BlockDeviceInfo; } pub struct BlockDeviceInfo { pub block_size: usize, pub num_blocks: u64, pub read_only: bool, pub removable: bool, pub model: String, pub serial: String, } pub type Block = [u8; 512]; // Standard block size // Example NVMe driver implementation pub struct NvmeDriver { regs: RegisterBlock<NvmeRegs>, admin_queue: AdminQueue, io_queues: Vec<IoQueue>, namespaces: Vec<Namespace>, } #[async_trait] impl BlockDevice for NvmeDriver { async fn read_blocks( &self, start_block: u64, blocks: &mut [Block], ) -> Result<usize, BlockError> { let namespace = &self.namespaces[0]; // Primary namespace let lba = start_block; let num_blocks = blocks.len() as u16; // Create read command let cmd = NvmeCommand::read(namespace.id, lba, num_blocks); // Submit to I/O queue let result = self.io_queues[0].submit_and_wait(cmd).await?; // Copy data to user buffer result.copy_to_blocks(blocks)?; Ok(blocks.len()) } async fn write_blocks( &self, start_block: u64, blocks: &[Block], ) -> Result<usize, BlockError> { let namespace = &self.namespaces[0]; let lba = start_block; let num_blocks = blocks.len() as u16; // Create write command let cmd = NvmeCommand::write(namespace.id, lba, num_blocks); // Submit to I/O queue self.io_queues[0].submit_and_wait(cmd).await?; Ok(blocks.len()) } } }
Network Device Framework
#![allow(unused)] fn main() { #[async_trait] pub trait NetworkDevice: Driver { /// Send network packet async fn send_packet(&self, packet: NetworkPacket) -> Result<(), NetworkError>; /// Receive network packet async fn receive_packet(&self) -> Result<NetworkPacket, NetworkError>; /// Get MAC address fn mac_address(&self) -> MacAddress; /// Set promiscuous mode fn set_promiscuous(&self, enabled: bool) -> Result<(), NetworkError>; /// Get link status fn link_status(&self) -> LinkStatus; } pub struct NetworkPacket { pub data: Vec<u8>, pub timestamp: Instant, pub checksum_offload: bool, } #[derive(Debug, Clone, Copy)] pub struct MacAddress([u8; 6]); #[derive(Debug, Clone, Copy)] pub enum LinkStatus { Up { speed: LinkSpeed, duplex: Duplex }, Down, } #[derive(Debug, Clone, Copy)] pub enum LinkSpeed { Mbps10, Mbps100, Gbps1, Gbps10, Gbps25, Gbps40, Gbps100, } // Example Intel e1000 driver pub struct E1000Driver { regs: RegisterBlock<E1000Regs>, rx_ring: RxRing, tx_ring: TxRing, mac_addr: MacAddress, } #[async_trait] impl NetworkDevice for E1000Driver { async fn send_packet(&self, packet: NetworkPacket) -> Result<(), NetworkError> { // Get next TX descriptor let desc = self.tx_ring.next_descriptor()?; // Set up DMA transfer desc.setup_packet(packet)?; // Ring doorbell self.regs.write32(E1000_TDT, self.tx_ring.tail); // Wait for completion desc.wait_completion().await?; Ok(()) } async fn receive_packet(&self) -> Result<NetworkPacket, NetworkError> { // Wait for packet let desc = self.rx_ring.wait_packet().await?; // Extract packet data let packet = desc.extract_packet()?; // Refill descriptor self.rx_ring.refill_descriptor(desc)?; Ok(packet) } } }
Graphics Device Framework
#![allow(unused)] fn main() { #[async_trait] pub trait GraphicsDevice: Driver { /// Set display mode async fn set_mode(&self, mode: DisplayMode) -> Result<(), GraphicsError>; /// Get framebuffer fn framebuffer(&self) -> Result<Framebuffer, GraphicsError>; /// Present frame async fn present(&self) -> Result<(), GraphicsError>; /// Wait for vertical blank async fn wait_vblank(&self) -> Result<(), GraphicsError>; } pub struct DisplayMode { pub width: u32, pub height: u32, pub refresh_rate: u32, pub color_depth: ColorDepth, } #[derive(Debug, Clone, Copy)] pub enum ColorDepth { Rgb565, Rgb888, Rgba8888, } pub struct Framebuffer { pub addr: VirtAddr, pub width: u32, pub height: u32, pub stride: u32, pub format: PixelFormat, } // Simple framebuffer driver pub struct SimpleFbDriver { framebuffer: Framebuffer, mmio_region: MmioRegion, } #[async_trait] impl GraphicsDevice for SimpleFbDriver { async fn set_mode(&self, mode: DisplayMode) -> Result<(), GraphicsError> { // Simple framebuffer doesn't support mode switching Err(GraphicsError::ModeNotSupported) } fn framebuffer(&self) -> Result<Framebuffer, GraphicsError> { Ok(self.framebuffer.clone()) } async fn present(&self) -> Result<(), GraphicsError> { // Simple framebuffer is always presenting Ok(()) } } }
Driver Management
Driver Registry
#![allow(unused)] fn main() { pub struct DriverRegistry { drivers: HashMap<DeviceId, Arc<dyn Driver>>, device_tree: DeviceTree, hotplug_manager: HotplugManager, } impl DriverRegistry { /// Register new driver pub fn register_driver( &mut self, driver: Arc<dyn Driver>, device_id: DeviceId, ) -> Result<(), RegistryError> { // Validate driver metadata let metadata = driver.metadata(); self.validate_metadata(&metadata)?; // Check for conflicts if self.drivers.contains_key(&device_id) { return Err(RegistryError::DeviceAlreadyClaimed); } // Initialize driver let capabilities = self.allocate_capabilities(&metadata)?; driver.init(capabilities).await?; // Add to registry self.drivers.insert(device_id, driver); Ok(()) } /// Unregister driver pub fn unregister_driver(&mut self, device_id: &DeviceId) -> Result<(), RegistryError> { if let Some(driver) = self.drivers.remove(device_id) { // Shutdown driver gracefully driver.shutdown().await?; // Revoke capabilities self.revoke_capabilities(device_id)?; } Ok(()) } /// Handle device hotplug pub async fn handle_hotplug(&self, event: HotplugEvent) -> Result<(), RegistryError> { match event.event_type { HotplugEventType::DeviceAdded => { self.probe_device(event.device_id).await?; } HotplugEventType::DeviceRemoved => { self.remove_device(event.device_id).await?; } } Ok(()) } } }
Device Discovery
#![allow(unused)] fn main() { pub struct DeviceDiscovery { pci_bus: PciBus, platform_devices: Vec<PlatformDevice>, } impl DeviceDiscovery { /// Enumerate all devices pub fn enumerate_devices(&self) -> Result<Vec<DeviceInfo>, DiscoveryError> { let mut devices = Vec::new(); // Enumerate PCI devices for device in self.pci_bus.enumerate()? { devices.push(DeviceInfo::from_pci(device)); } // Enumerate platform devices for device in &self.platform_devices { devices.push(DeviceInfo::from_platform(device)); } Ok(devices) } /// Probe specific device pub async fn probe_device(&self, device_id: DeviceId) -> Result<Arc<dyn Driver>, DiscoveryError> { let device_info = self.get_device_info(device_id)?; // Match device to driver let driver_name = self.match_driver(&device_info)?; // Load driver let driver = self.load_driver(driver_name).await?; Ok(driver) } } pub struct DeviceInfo { pub device_id: DeviceId, pub vendor_id: u16, pub product_id: u16, pub device_class: DeviceClass, pub subsystem_vendor: Option<u16>, pub subsystem_device: Option<u16>, pub resources: Vec<DeviceResource>, } #[derive(Debug, Clone)] pub enum DeviceResource { MmioRegion { base: PhysAddr, size: usize }, IoPort { base: u16, size: u16 }, Interrupt { vector: u32, shared: bool }, DmaChannel { channel: u8 }, } }
Power Management
Driver Power States
#![allow(unused)] fn main() { #[derive(Debug, Clone, Copy)] pub enum PowerState { /// Fully operational D0, /// Low power, context preserved D1, /// Lower power, some context lost D2, /// Lowest power, most context lost D3Hot, /// Power removed D3Cold, } #[async_trait] pub trait PowerManagement { /// Set device power state async fn set_power_state(&self, state: PowerState) -> Result<(), PowerError>; /// Get current power state fn get_power_state(&self) -> PowerState; /// Prepare for system sleep async fn prepare_sleep(&self) -> Result<(), PowerError>; /// Resume from system sleep async fn resume(&self) -> Result<(), PowerError>; } // Example implementation impl PowerManagement for E1000Driver { async fn set_power_state(&self, state: PowerState) -> Result<(), PowerError> { match state { PowerState::D0 => { // Full power self.regs.write32(E1000_CTRL, CTRL_NORMAL_OPERATION); } PowerState::D3Hot => { // Low power self.regs.write32(E1000_CTRL, CTRL_POWER_DOWN); } _ => return Err(PowerError::StateNotSupported), } Ok(()) } } }
Performance Optimization
Zero-Copy Data Paths
#![allow(unused)] fn main() { pub struct ZeroCopyBuffer { /// User virtual address user_addr: VirtAddr, /// Physical pages pages: Vec<PhysFrame>, /// DMA mapping dma_addr: PhysAddr, } impl ZeroCopyBuffer { /// Create from user buffer pub fn from_user_buffer( user_buffer: UserBuffer, direction: DmaDirection, ) -> Result<Self, DriverError> { // Pin user pages in memory let pages = pin_user_pages(user_buffer.addr, user_buffer.len)?; // Create DMA mapping let dma_addr = create_dma_mapping(&pages, direction)?; Ok(Self { user_addr: user_buffer.addr, pages, dma_addr, }) } /// Get DMA address for device pub fn dma_addr(&self) -> PhysAddr { self.dma_addr } } // Efficient packet processing pub struct PacketBuffer { pub head: usize, pub tail: usize, pub data: DmaBuffer, } impl PacketBuffer { /// Reserve headroom for headers pub fn reserve_headroom(&mut self, len: usize) { self.head += len; } /// Add data to tail pub fn push_tail(&mut self, data: &[u8]) -> Result<(), BufferError> { if self.tail + data.len() > self.data.size { return Err(BufferError::InsufficientSpace); } unsafe { let dst = self.data.virt_addr.as_ptr::<u8>().add(self.tail); core::ptr::copy_nonoverlapping(data.as_ptr(), dst, data.len()); } self.tail += data.len(); Ok(()) } } }
Interrupt Coalescing
#![allow(unused)] fn main() { pub struct InterruptCoalescing { /// Maximum interrupts per second max_rate: u32, /// Minimum packets before interrupt min_packets: u32, /// Maximum delay before interrupt (μs) max_delay: u32, } impl InterruptCoalescing { /// Configure interrupt coalescing pub fn configure(&self, regs: &RegisterBlock<E1000Regs>) { // Set interrupt throttling let itr = 1_000_000 / self.max_rate; // Convert to ITR units regs.write32(E1000_ITR, itr); // Set receive delay timer regs.write32(E1000_RDTR, self.max_delay / 256); // Set receive interrupt packet count regs.write32(E1000_RADV, self.min_packets); } } }
Driver Development
Driver Template
#![allow(unused)] fn main() { use veridian_driver_framework::*; pub struct MyDriver { regs: RegisterBlock<MyDeviceRegs>, interrupt_handler: InterruptHandler, dma_buffer: DmaBuffer, } #[async_trait] impl Driver for MyDriver { async fn init(&mut self, caps: HardwareCapabilities) -> Result<(), DriverError> { // Map MMIO regions self.regs = RegisterBlock::new(caps.mmio_regions[0].clone())?; // Allocate DMA buffer self.dma_buffer = DmaBuffer::allocate( PAGE_SIZE, DmaDirection::Bidirectional, &caps.dma_capability.unwrap(), )?; // Register interrupt handler self.interrupt_handler = InterruptHandler::register( caps.interrupts[0].vector, || self.handle_interrupt(), caps.interrupts[0].capability, )?; // Initialize device self.regs.write32(CONTROL_REG, CONTROL_RESET); Ok(()) } async fn start(&mut self) -> Result<(), DriverError> { // Enable device self.regs.write32(CONTROL_REG, CONTROL_ENABLE); self.interrupt_handler.enable()?; Ok(()) } async fn handle_interrupt(&self, vector: u32) -> Result<(), DriverError> { let status = self.regs.read32(STATUS_REG); if status & STATUS_RX_READY != 0 { // Handle received data self.handle_rx().await?; } if status & STATUS_TX_COMPLETE != 0 { // Handle transmit completion self.handle_tx_complete().await?; } // Clear interrupt self.regs.write32(STATUS_REG, status); Ok(()) } async fn shutdown(&mut self) -> Result<(), DriverError> { // Disable interrupts self.interrupt_handler.disable()?; // Reset device self.regs.write32(CONTROL_REG, CONTROL_RESET); Ok(()) } fn metadata(&self) -> DriverMetadata { DriverMetadata { name: "MyDriver".to_string(), version: Version::new(1, 0, 0), vendor_id: Some(0x1234), device_id: Some(0x5678), device_class: DeviceClass::Network, capabilities_required: vec![ CapabilityType::Mmio, CapabilityType::Interrupt, CapabilityType::Dma, ], } } } }
Build System Integration
# Cargo.toml for driver
[package]
name = "my-driver"
version = "0.1.0"
edition = "2021"
[dependencies]
veridian-driver-framework = { path = "../../framework" }
async-trait = "0.1"
log = "0.4"
[lib]
crate-type = ["cdylib"]
# Driver manifest
[package.metadata.veridian]
device-class = "network"
vendor-id = 0x1234
device-id = 0x5678
Future Enhancements
Planned Features
- Driver Verification: Formal verification of critical drivers
- GPU Support: High-performance GPU drivers with compute capabilities
- Real-Time Drivers: Deterministic driver execution for RT systems
- Driver Sandboxing: Additional isolation using hardware features
- Hot-Patching: Update drivers without system restart
Research Areas
- AI-Driven Optimization: Machine learning for driver performance tuning
- Hardware Offload: Driver logic implemented in hardware
- Distributed Drivers: Driver components across multiple machines
- Quantum Computing: Quantum device driver interfaces
This driver architecture provides a secure, maintainable, and high-performance foundation for device support in VeridianOS while maintaining the microkernel's principles of isolation and capability-based security.