use core::{ptr::write_volatile, sync::atomic::Ordering}; use bytes_struct::VolatilePackedStruct; use crate::{ println, virtio::{DeviceStatus, QUEUE_SIZE, VirtioPciCommonCfg, Virtqueue}, }; pub struct VirtioPciDriver { common_cfg: *mut VirtioPciCommonCfg, notify_cfg: *mut u16, isr_cfg: *mut u8, queue: &'static mut Virtqueue, event_pool: [VirtioInputEvent; QUEUE_SIZE], last_used_idx: u16, notify_off: u32, // Multiplier from PCI notify capability (used to compute notify address) handle_event: F, state: S, } #[repr(u16)] #[allow(unused)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum EventType { Sync = 0, Key = 1, Relative = 2, Absolute = 3, } #[repr(u16)] #[allow(unused)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum EventCodeRelative { X = 0, Y = 1, Wheel = 8, } #[repr(u16)] #[allow(unused)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum EventCodeValue { Released = 0, Pressed = 1, AutomaticRepetition = 2, } #[repr(C)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct VirtioInputEvent { pub event_type: EventType, pub code: u16, pub value: u32, } #[repr(C)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct VirtioRelativeEvent { pub event_type: EventType, pub code: EventCodeRelative, pub value: i32, } #[repr(C)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct VirtioKeyEvent { pub event_type: EventType, pub code: u16, pub value: EventCodeValue, } impl VirtioInputEvent { pub fn is_relative(&self) -> bool { self.event_type == EventType::Relative } pub unsafe fn as_relative_event(&self) -> &VirtioRelativeEvent { unsafe { &*(self as *const Self as *const VirtioRelativeEvent) } } pub fn is_key(&self) -> bool { self.event_type == EventType::Key } pub unsafe fn as_key_event(&self) -> &VirtioKeyEvent { unsafe { &*(self as *const Self as *const VirtioKeyEvent) } } } impl VirtioPciDriver { pub const unsafe fn new(handle_event: F, state: S, queue_mem: &'static mut Virtqueue) -> Self { Self { common_cfg: core::ptr::null_mut(), notify_cfg: core::ptr::null_mut(), isr_cfg: core::ptr::null_mut(), queue: queue_mem, event_pool: [VirtioInputEvent { event_type: EventType::Sync, code: 0, value: 0, }; QUEUE_SIZE], last_used_idx: 0, notify_off: 0, handle_event, state, } } pub unsafe fn init( &mut self, common_cfg: *mut VirtioPciCommonCfg, notify_cfg: *mut u16, isr_cfg: *mut u8, notify_mult: u32, ) { self.common_cfg = common_cfg; self.notify_cfg = notify_cfg; self.isr_cfg = isr_cfg; self.notify_off = notify_mult; println!("{:x?}", self.common_cfg); // --- 1. Reset & Status --- self.common_cfg .set_device_status(DeviceStatus::Reinitialized); self.common_cfg .set_device_status(DeviceStatus::Acknowledge | DeviceStatus::Driver); // --- 2. Négociation Features (Bit 32 = Version 1) --- self.common_cfg.set_driver_feature_select(1); // Page 1 let f1 = self.common_cfg.get_driver_feature(); self.common_cfg.set_driver_feature(f1 | 1); // bit 32 VIRTIO_F_VERSION_1 self.common_cfg.set_device_status( DeviceStatus::Acknowledge | DeviceStatus::Driver | DeviceStatus::FeaturesOk, ); if !self .common_cfg .get_device_status() .contains(DeviceStatus::FeaturesOk) { panic!("PCI Virtio: Features rejected"); } // --- 3. Configuration de la Queue 0 --- self.common_cfg.set_queue_select(0); self.common_cfg.set_queue_size(QUEUE_SIZE as u16); // --- 4. Adresses 64 bits --- let desc_addr = self.queue.descriptors.as_ptr() as u64; println!("desc_addr: {:x}", desc_addr); self.common_cfg.set_queue_desc(desc_addr); let avail_addr = &raw const self.queue.available as u64; self.common_cfg.set_queue_driver(avail_addr); let used_addr = &raw const self.queue.used as u64; self.common_cfg.set_queue_device(used_addr); // --- 5. Remplissage initial --- for i in 0..QUEUE_SIZE { self.queue.descriptors[i].addr = &raw const self.event_pool[i] as u64; self.queue.descriptors[i].len = core::mem::size_of::() as u32; self.queue.descriptors[i].flags = 2; // VRING_DESC_F_WRITE self.queue.available.ring[i] = i as u16; } self.queue .available .idx .store(QUEUE_SIZE as u16, Ordering::Release); // --- 6. Activation --- self.common_cfg.set_queue_enable(1); if self.common_cfg.get_queue_enable() == 0 { panic!("Le périphérique refuse d'activer la queue !"); } // --- 7. Driver OK (must be set after queue is enabled) --- self.common_cfg.set_device_status( DeviceStatus::Acknowledge | DeviceStatus::Driver | DeviceStatus::DriverOk | DeviceStatus::FeaturesOk, ); unsafe { // --- 8. PREMIER KICK --- // Compute the notify address for queue 0 using the multiplier provided by PCI capability let notify_addr = self.notify_cfg.byte_add( self.notify_off as usize * self.common_cfg.get_queue_notify_off() as usize, ); core::ptr::write_volatile(notify_addr, 0); // 0 = index de la queue } } pub fn handle_interrupt(&mut self) { // 1. Lire et acquitter l'ISR PCI (Indispensable pour baisser la ligne IRQ) let isr = unsafe { self.isr_cfg.read_volatile() }; if isr & 1 == 0 { // Not a queue interrupt return; } let used_idx = self.queue.used.idx.load(Ordering::Acquire); while self.last_used_idx != used_idx { let ring_slot = self.last_used_idx as usize % QUEUE_SIZE; let used_elem = &self.queue.used.ring[ring_slot]; let event = &self.event_pool[used_elem.id as usize]; (self.handle_event)(&mut self.state, event); // Recyclage let avail_head = self.queue.available.idx.load(Ordering::Relaxed) as usize % QUEUE_SIZE; self.queue.available.ring[avail_head] = used_elem.id as u16; self.queue.available.idx.fetch_add(1, Ordering::Release); self.last_used_idx = self.last_used_idx.wrapping_add(1); } unsafe { write_volatile(self.notify_cfg, 0); } } } pub const PLIC_BASE: usize = 0x0C00_0000; pub const HANDLING_INTERRUPT: usize = PLIC_BASE + 0x20_0000; pub const ENABLE_INTERRUPT: usize = PLIC_BASE + 0x2000; #[repr(C, packed)] #[derive(Debug, Clone, Copy, VolatilePackedStruct)] pub struct PlicHandlingInterrupt { pub priority_threshold: u32, pub claim: u32, } /// See https://wiki.osdev.org/PLIC pub fn init_plic_pci(irq: u32) { let priority_ptr = (PLIC_BASE + (irq as usize * 4)) as *mut u32; unsafe { priority_ptr.write_volatile(1) }; // Mettre le Threshold à 0 pour Hart 0 S-Mode (context 1) let handling_interrupt = (HANDLING_INTERRUPT + 0x1000) as *mut PlicHandlingInterrupt; handling_interrupt.set_priority_threshold(0); // Activer l'IRQ pour le S-Mode (Hart 0) (context 1) // Attention : l'enable bit pour l'IRQ 33 est le bit 1 du deuxième mot u32 let enable_ptr = (ENABLE_INTERRUPT + 0x80 + (irq / 32) as usize * 4) as *mut u32; unsafe { let current = enable_ptr.read_volatile(); enable_ptr.write_volatile(current | (1 << (irq % 32))); } }