255 lines
7.9 KiB
Rust
255 lines
7.9 KiB
Rust
use core::{ptr::write_volatile, sync::atomic::Ordering};
|
|
|
|
use bytes_struct::VolatilePackedStruct;
|
|
|
|
use crate::{
|
|
println,
|
|
virtio::{DeviceStatus, QUEUE_SIZE, VirtioPciCommonCfg, Virtqueue},
|
|
};
|
|
|
|
pub struct VirtioPciDriver<S, F: Fn(&mut S, &VirtioInputEvent) = fn(&mut S, &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,
|
|
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<S, F: Fn(&mut S, &VirtioInputEvent)> VirtioPciDriver<S, F> {
|
|
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::<VirtioInputEvent>() 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)));
|
|
}
|
|
}
|