Add mouse and clean virtio

This commit is contained in:
2026-03-11 16:42:58 +01:00
parent 60ddc88b38
commit f67718c3fe
14 changed files with 453 additions and 255 deletions

View File

@@ -1,87 +1,160 @@
use core::{
ptr::{read_volatile, write_volatile},
sync::atomic::Ordering,
};
use core::{ptr::write_volatile, sync::atomic::Ordering};
use bytes_struct::VolatilePackedStruct;
use crate::{
println,
virtio::{QUEUE_SIZE, VirtioInputEvent, Virtqueue},
virtio::{DeviceStatus, QUEUE_SIZE, VirtioPciCommonCfg, Virtqueue},
};
pub struct VirtioPciDriver {
// Les 3 zones clés du PCI Modern
common_cfg: usize,
notify_cfg: usize,
isr_cfg: usize,
pub struct VirtioPciDriver<F: Fn(&VirtioInputEvent) = fn(&VirtioInputEvent)> {
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,
}
impl VirtioPciDriver {
pub const unsafe fn new(
common_cfg: usize,
notify_cfg: usize,
isr_cfg: usize,
notify_mult: u32,
queue_mem: &'static mut Virtqueue,
) -> Self {
#[repr(u16)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum EventType {
Sync = 0,
Key = 1,
Relative = 2,
Absolute = 3,
}
#[repr(u16)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum EventCodeRelative {
X = 0,
Y = 1,
Wheel = 8,
}
#[repr(u16)]
#[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<F: Fn(&VirtioInputEvent)> VirtioPciDriver<F> {
pub const unsafe fn new(handle_event: F, queue_mem: &'static mut Virtqueue) -> Self {
Self {
common_cfg,
notify_cfg,
isr_cfg,
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: 0,
event_type: EventType::Sync,
code: 0,
value: 0,
}; QUEUE_SIZE],
last_used_idx: 0,
notify_off: notify_mult,
notify_off: 0,
handle_event,
}
}
pub unsafe fn init(&mut self) {
println!("{:x}", self.common_cfg);
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.write_common_u8(0x14, 0); // device_status
self.write_common_u8(0x14, 1 | 2); // ACK | DRIVER
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.write_common_u32(0x08, 1); // driver_feature_select = page 1
let f1 = self.read_common_u32(0x0C); // driver_feature
self.write_common_u32(0x0C, f1 | 1); // Accepter VIRTIO_F_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.write_common_u8(0x14, 1 | 2 | 8); // FEATURES_OK
if (self.read_common_u8(0x14) & 8) == 0 {
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.write_common_u16(0x16, 0); // queue_select = 0
// Fixer la taille
self.write_common_u16(0x18, QUEUE_SIZE as u16); // queue_size
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 *const _ as u64;
let desc_addr = self.queue.descriptors.as_ptr() as u64;
println!("desc_addr: {:x}", desc_addr);
self.write_common_u32(0x20, (desc_addr & 0xffffffff) as u32); // queue_desc_lo
self.write_common_u32(0x24, (desc_addr >> 32) as u32); // queue_desc_hi
self.common_cfg.set_queue_desc(desc_addr);
let avail_addr = &self.queue.available as *const _ as u64;
self.write_common_u32(0x28, (avail_addr & 0xffffffff) as u32); // queue_avail_lo
self.write_common_u32(0x2C, (avail_addr >> 32) as u32); // queue_avail_hi
let avail_addr = &raw const self.queue.available as u64;
self.common_cfg.set_queue_driver(avail_addr);
let used_addr = &self.queue.used as *const _ as u64;
self.write_common_u32(0x30, (used_addr & 0xffffffff) as u32); // queue_used_lo
self.write_common_u32(0x34, (used_addr >> 32) as u32); // queue_used_hi
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 = &self.event_pool[i] as *const _ as u64;
self.queue.descriptors[i].addr = &raw const self.event_pool[i] as u64;
self.queue.descriptors[i].len = core::mem::size_of::<VirtioInputEvent>() as u32;
self.queue.descriptors[i].flags = 2; // VRING_DESC_F_WRITE
self.queue.available.ring[i] = i as u16;
@@ -92,30 +165,37 @@ impl VirtioPciDriver {
.store(QUEUE_SIZE as u16, Ordering::Release);
// --- 6. Activation ---
self.write_common_u16(0x1C, 1); // queue_enable = 1
self.common_cfg.set_queue_enable(1);
if self.read_common_u16(0x1C) == 0 {
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.write_common_u8(0x14, 1 | 2 | 4 | 8);
// --- 8. PREMIER KICK ---
// Compute the notify address for queue 0 using the multiplier provided by PCI capability
let notify_addr = self.notify_cfg + (self.notify_off as usize * 0);
self.common_cfg.set_device_status(
DeviceStatus::Acknowledge
| DeviceStatus::Driver
| DeviceStatus::DriverOk
| DeviceStatus::FeaturesOk,
);
unsafe {
core::ptr::write_volatile(notify_addr as *mut u16, 0); // 0 = index de la queue
// --- 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 { read_volatile(self.isr_cfg as *const u8) };
let isr = unsafe { self.isr_cfg.read_volatile() };
if isr & 1 == 0 {
// Not a queue interrupt
return;
} // Pas une interruption de queue
}
let used_idx = self.queue.used.idx.load(Ordering::Acquire);
while self.last_used_idx != used_idx {
@@ -123,10 +203,7 @@ impl VirtioPciDriver {
let used_elem = &self.queue.used.ring[ring_slot];
let event = &self.event_pool[used_elem.id as usize];
if event.event_type == 1 {
println!("key pressed");
// self.on_key(event.code, event.value);
}
(self.handle_event)(event);
// Recyclage
let avail_head = self.queue.available.idx.load(Ordering::Relaxed) as usize % QUEUE_SIZE;
@@ -136,49 +213,35 @@ impl VirtioPciDriver {
self.last_used_idx = self.last_used_idx.wrapping_add(1);
}
// Notifier qu'on a traité les buffers (si besoin)
unsafe {
write_volatile(self.notify_cfg as *mut u16, 0);
write_volatile(self.notify_cfg, 0);
}
}
// Helpers de lecture/écriture (Note : PCI utilise des tailles mixtes u8/u16/u32)
unsafe fn write_common_u8(&self, off: usize, val: u8) {
write_volatile((self.common_cfg + off) as *mut u8, val);
}
unsafe fn write_common_u16(&self, off: usize, val: u16) {
write_volatile((self.common_cfg + off) as *mut u16, val);
}
unsafe fn write_common_u32(&self, off: usize, val: u32) {
write_volatile((self.common_cfg + off) as *mut u32, val);
}
unsafe fn read_common_u8(&self, off: usize) -> u8 {
read_volatile((self.common_cfg + off) as *const u8)
}
unsafe fn read_common_u16(&self, off: usize) -> u16 {
read_volatile((self.common_cfg + off) as *const u16)
}
unsafe fn read_common_u32(&self, off: usize) -> u32 {
read_volatile((self.common_cfg + off) as *const u32)
}
}
pub const PLIC_BASE: usize = 0x0c00_0000;
pub const IRQ_VIRTIO: u32 = 1;
pub const PLIC_BASE: usize = 0x0C00_0000;
pub const HANDLING_INTERRUPT: usize = PLIC_BASE + 0x20_0000;
pub const ENABLE_INTERRUPT: usize = PLIC_BASE + 0x2000;
pub const S_MODE_CLAIM_COMPLETE: *mut u32 = 0x0c20_1004 as *mut u32;
#[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 (offset 0x201000)
let threshold_ptr = (PLIC_BASE + 0x201000) as *mut u32;
unsafe { threshold_ptr.write_volatile(0) };
// 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)
// 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_reg_offset = (irq / 32) as usize * 4;
let enable_ptr = (PLIC_BASE + 0x2080 + enable_reg_offset) as *mut 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)));