From 60ddc88b38a38f2695a98b32f2ed7571a5dadab9 Mon Sep 17 00:00:00 2001 From: Julien THILLARD Date: Mon, 9 Mar 2026 20:27:24 +0100 Subject: [PATCH] Sync computers --- src/main.rs | 11 +- src/pci.rs | 396 +++++++++++++++++++++++++++--------------------- src/virtio.rs | 39 +++++ src/volatile.rs | 4 +- 4 files changed, 274 insertions(+), 176 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9ddfa5a..0f2486f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,11 @@ #![no_main] // #![warn(clippy::pedantic)] #![allow(static_mut_refs)] -#![feature(riscv_ext_intrinsics, str_from_raw_parts, arbitrary_self_types_pointers)] +#![feature( + riscv_ext_intrinsics, + str_from_raw_parts, + arbitrary_self_types_pointers +)] use core::sync::atomic::AtomicBool; @@ -16,7 +20,7 @@ use log::info; use crate::{ io::init_log, - pci::{PciDeviceIterator, scan_pci_for_virtio_keyboard, scan_pci_for_virtio_keyboard2}, + pci::{PciDeviceIterator, scan_pci_for_virtio_keyboard}, riscv::enable_supervisor_interrupt, scheduler::{SCHEDULER, idle}, user::{proc2, test}, @@ -29,7 +33,6 @@ use crate::{ }; extern crate alloc; -mod volatile; mod boot; mod critical_section; mod draw; @@ -51,6 +54,7 @@ mod vga; mod virtio; mod virtual_console; mod virtual_fs; +mod volatile; pub const HEAP_SIZE: usize = 1024 * 1024 * 32; // 32Mo RAM #[global_allocator] @@ -96,7 +100,6 @@ pub extern "C" fn supervisor_mode_entry() { } unsafe { - scan_pci_for_virtio_keyboard2(); let pci_info = scan_pci_for_virtio_keyboard().unwrap(); KBD_DRIVER = Some(VirtioPciDriver::new( pci_info.common_cfg, diff --git a/src/pci.rs b/src/pci.rs index 6f129a4..63f6651 100644 --- a/src/pci.rs +++ b/src/pci.rs @@ -1,6 +1,13 @@ -use core::ops::{Deref, DerefMut}; +use core::{ + ops::{Deref, DerefMut}, + ptr::{addr_of, addr_of_mut}, +}; -use crate::{println, volatile::Volatile}; +use crate::{ + println, + virtio::{VirtioCapability, VirtioCapabilityType, VirtioNotificationCapability}, + volatile::Volatile, +}; // Configuration pour RISC-V Virt const PCI_ECAM_BASE: usize = 0x3000_0000; @@ -48,46 +55,55 @@ pub struct PciDevice { #[repr(C, packed)] pub struct PciHeader { - pub vendor_id: Volatile, - pub device_id: Volatile, - pub command: Volatile, - pub status: Volatile, - pub revision_id: Volatile, - pub programming_interface_bytes: Volatile, - pub subclass: Volatile, - pub class_code: Volatile, - pub cache_line_size: Volatile, - pub latency_timer: Volatile, - pub header_type: Volatile, - pub bist: Volatile, + pub vendor_id: u16, + pub device_id: u16, + pub command: u16, + pub status: u16, + pub revision_id: u8, + pub programming_interface_bytes: u8, + pub subclass: u8, + pub class_code: u8, + pub cache_line_size: u8, + pub latency_timer: u8, + pub header_type: u8, + pub bist: u8, } #[derive(Debug, Clone, Copy)] pub enum PciBar { - B32(PciBar32), - B64(PciBar64), + Mem32(MemoryBar32), + IO32(IOBar32), + Mem64(MemoryBar64), } #[derive(Debug, Clone, Copy)] -pub struct PciBar32(*mut u32); +pub struct MemoryBar32(*mut u32); #[derive(Debug, Clone, Copy)] -pub struct PciBar64(*mut u64); +pub struct IOBar32(*mut u32); + +#[derive(Debug, Clone, Copy)] +pub struct MemoryBar64(*mut u64); #[repr(C, packed)] pub struct PciGeneralHeader { - pub common_header: Volatile, - pub bars: [Volatile; 6], - pub cardbus_cis_pointer: Volatile, - pub subsystem_vendor_id: Volatile, - pub subsystem_id: Volatile, - pub expansion_rom_base_address: Volatile, - pub capabilities_pointer: Volatile, - pub _reserved1: [Volatile; 7], - pub interrupt_line: Volatile, - pub interrupt_pin: Volatile, - pub min_grant: Volatile, - pub max_lantency: Volatile, + pub common_header: PciHeader, + pub bars: [u32; 6], + pub cardbus_cis_pointer: u32, + pub subsystem_vendor_id: u16, + pub subsystem_id: u16, + pub expansion_rom_base_address: u32, + pub capabilities_pointer: u8, + pub _reserved1: [u8; 7], + pub interrupt_line: u8, + pub interrupt_pin: u8, + pub min_grant: u8, + pub max_lantency: u8, +} +#[repr(C, packed)] +pub struct PciCapability { + pub capability_id: u8, + pub next_ptr: u8, } pub struct PciGeneralDevice { @@ -124,17 +140,17 @@ impl PciDevice { (self.get_vendor_id(), self.get_device_id()) } pub fn get_device_id(&self) -> u16 { - unsafe { (&raw const (*self.inner).device_id).read() } + unsafe { (*self.inner).device_id } } pub fn get_vendor_id(&self) -> u16 { unsafe { (*self.inner).vendor_id } } pub fn get_command(&self) -> u16 { - unsafe { (*self.inner).command } + unsafe { core::ptr::read_volatile(&raw const (*self.inner).command) } } pub fn set_command(&mut self, command: u16) { - unsafe { (*self.inner).command = command } + unsafe { core::ptr::write_volatile(&raw mut (*self.inner).command, command) } } /// # Safety @@ -146,14 +162,35 @@ impl PciDevice { impl PciGeneralDevice { pub fn get_interrupt_pin(&self) -> u8 { - unsafe { (*self.inner).interrupt_pin } + unsafe { (&raw const (*self.inner).interrupt_pin).read_volatile() } } pub fn set_interrupt_irq(&mut self, irq: u8) { - unsafe { (*self.inner).interrupt_line = irq } + unsafe { (&raw mut (*self.inner).interrupt_line).write_volatile(irq) } + } + pub fn get_capabilities_pointer(&self) -> *mut PciCapability { + unsafe { + (self.inner as usize + + (&raw mut (*self.inner).capabilities_pointer).read_volatile() as usize) + as *mut PciCapability + } + } + pub fn capabilities(&self) -> PciCapabilitiesIterator { + PciCapabilitiesIterator { + base: self.inner as *mut u8, + current: self.get_capabilities_pointer(), + } } + pub fn get_bars(&mut self) -> PciBarIterator { + PciBarIterator { + current: 0, + base_addr: unsafe { addr_of_mut!((*self.inner).bars) as *mut u32 }, + } + } pub fn get_bar(&mut self, i: u8) -> PciBar { - unsafe { PciBar((&raw mut (*self.inner).bars as *mut u32).add(i as usize)) } + unsafe { + PciBar::new((&raw mut (*self.inner).bars as *mut u32).add(i as usize) as *mut u32) + } } } @@ -163,35 +200,99 @@ pub struct PciBarIterator { } impl Iterator for PciBarIterator { - type Item = PciBar; + type Item = (u8, PciBar); fn next(&mut self) -> Option { if self.current >= 6 { None } else { - let res = if unsafe { *(self.base_addr.add(self.current as usize)) & 0b100 != 0 } { - self.current += 1; - PciBar64 - } else { - }; + let addr = self.base_addr.wrapping_add(self.current as usize); + let res = (self.current, unsafe { PciBar::new(addr) }); self.current += 1; - res + if matches!(res.1, PciBar::Mem64(_)) { + self.current += 1; + } + Some(res) } } } +impl MemoryBar32 { + pub fn is_prefetchable(self) -> bool { + unsafe { self.0.read_volatile() & 0b1000 != 0 } + } + pub fn base_addr(self) -> *const u8 { + unsafe { (self.0.read_volatile() & !0b1111) as *const u8 } + } + pub fn allocate_if_needed(self, addr: *const u8) { + if self.base_addr().is_null() { + self.allocate(addr); + } + } + pub fn allocate(self, addr: *const u8) { + debug_assert!(addr as usize <= 0xFFFF_FFFF); + unsafe { self.0.write_volatile((*self.0 & 0b11) | addr as u32) } + } +} + +impl MemoryBar64 { + pub fn is_prefetchable(self) -> bool { + unsafe { self.0.read_volatile() & 0b1000 != 0 } + } + pub fn base_addr(self) -> *const u8 { + unsafe { (self.0.read_volatile() & !0b1111) as *const u8 } + } + pub fn allocate(self, addr: *const u8) { + unsafe { self.0.write_volatile((*self.0 & 0b1111) | addr as u64) } + } +} + +impl IOBar32 { + pub fn base_addr(self) -> *const u8 { + unsafe { (self.0.read_volatile() & !0b11) as *const u8 } + } + pub fn allocate(self, addr: *const u8) { + debug_assert!(addr as usize <= 0xFFFF_FFFF); + unsafe { self.0.write_volatile((*self.0 & 0b11) | addr as u32) } + } +} + impl PciBar { - pub fn as_ptr(self) -> *const u8 { - self.0 as *const u8 + pub unsafe fn new(addr: *mut u32) -> Self { + if unsafe { *addr & 0b1 != 0 } { + PciBar::IO32(IOBar32(addr)) + } else if unsafe { *addr & 0b100 != 0 } { + PciBar::Mem64(MemoryBar64(addr as *mut u64)) + } else { + PciBar::Mem32(MemoryBar32(addr)) + } } - pub fn as_mut_ptr(self) -> *mut u8 { - self.0 as *mut u8 + pub fn is_memory_space(self) -> bool { + matches!(self, PciBar::Mem32(_) | PciBar::Mem64(_)) } - pub fn is_64bits(self) -> bool { - unsafe { *self.0 & 0b100 != 0 } + pub fn is_io_space(self) -> bool { + matches!(self, PciBar::IO32(_)) } pub fn is_prefetchable(self) -> bool { - unsafe { *self.0 & 0b1000 != 0 } + match self { + PciBar::Mem32(pci_bar32) => pci_bar32.is_prefetchable(), + PciBar::Mem64(pci_bar64) => pci_bar64.is_prefetchable(), + PciBar::IO32(_) => panic!("IO Space Bars are not prefetchable"), + } + } + pub fn base_address(self) -> *const u8 { + match self { + PciBar::Mem32(pci_bar32) => pci_bar32.base_addr(), + PciBar::Mem64(pci_bar64) => pci_bar64.base_addr(), + PciBar::IO32(iobar32) => iobar32.base_addr(), + } + } + pub fn allocate(self, addr: *const u8) { + match self { + PciBar::Mem32(pci_bar32) => pci_bar32.allocate(addr), + PciBar::Mem64(pci_bar64) => pci_bar64.allocate(addr), + PciBar::IO32(iobar32) => iobar32.allocate(addr), + } } } @@ -226,7 +327,34 @@ impl Iterator for PciDeviceIterator { } } } -pub fn scan_pci_for_virtio_keyboard2() -> Option { +pub struct PciCapabilitiesIterator { + base: *mut u8, + current: *mut PciCapability, +} + +impl Iterator for PciCapabilitiesIterator { + type Item = *mut PciCapability; + + fn next(&mut self) -> Option { + if self.current.is_null() { + None + } else { + let res = self.current; + self.current = unsafe { + let offset = + (&raw const (*(self.current as *const PciCapability)).next_ptr).read_volatile(); + if offset != 0 { + self.base.add(offset as usize) as *mut PciCapability + } else { + core::ptr::null_mut() + } + }; + Some(res) + } + } +} + +pub fn scan_pci_for_virtio_keyboard() -> Option { let device = PciDeviceIterator::new() .find(|device| device.vendor_and_device_id() == (0x1af4, 0x1052)) .unwrap(); @@ -235,139 +363,67 @@ pub fn scan_pci_for_virtio_keyboard2() -> Option { device.to_general_device() }; let interrupt_pin = device.get_interrupt_pin(); + // Interrupt swizzling let irq = 32 + (device.device + interrupt_pin - 1) % 4; device.set_interrupt_irq(irq); let command = device.get_command() | 0b110; device.set_command(command); - for i in 0..6 { - let bar = device.get_bar(i); - println!("bar {} {:x?} 64: {}", i, bar, bar.is_64bits()) + for (i, bar) in device.get_bars() { + println!("bar {} {:x?}", i, bar); + if bar.is_memory_space() && bar.base_address().is_null() { + println!("alloc"); + bar.allocate( + (0x5100_0000 + (device.device as u32 * 0x100_000) + (i as u32 * 0x4000)) + as *const u8, + ) + } } - None -} + let mut common_cfg = 0; + let mut notify_cfg = 0; + let mut isr_cfg = 0; + let mut notify_multiplier = 1; -pub fn scan_pci_for_virtio_keyboard() -> Option { - // Sur RISC-V Virt, on scanne généralement le bus 0 - for dev in 0..32 { - let vdev: u32 = pci_read(0, dev, 0, 0x00); - let vendor = (vdev & 0xffff) as u16; - let device = (vdev >> 16) as u16; + for capability_addr in device.capabilities() { + if unsafe { (*capability_addr).capability_id == 0x9 } { + let capability = unsafe { *(capability_addr as *mut VirtioCapability) }; - // On cherche le Vendor ID VirtIO (0x1AF4) - // et le Device ID Keyboard (0x1012 ou 0x1052 pour Modern) - if vendor == 0x1af4 && (device >= 0x1000 && device <= 0x107f) { - // Dans ta boucle de scan, après avoir trouvé le device - // let interrupt_pin = (pci_read::(0, dev, 0, 0x3C) >> 8) & 0xFF; // Offset 0x3D - // let irq = 32 + (dev as u32 + interrupt_pin - 1) % 4; - - // // let old_val = pci_read::(0, 2, 0, 0x3C); - - // // On garde les 24 bits du haut (Interrupt Pin, etc.) et on change les 8 bits du bas - // // let new_val = (old_val & 0xFFFFFF00) | irq; - - // // pci_write(0, 2, 0, 0x3C, new_val); - - // println!( - // "VirtIO Keyboard sur Slot {}, PIN {}, mappé sur IRQ PLIC {}", - // dev, interrupt_pin, irq - // ); // ACTIVER l'accès mémoire et le bus mastering (PCI Command) - // let cmd = pci_read::(0, dev, 0, 0x04); - // pci_write(0, dev, 0, 0x04, cmd | 0x6); - - let mut common_cfg = 0; - let mut notify_cfg = 0; - let mut isr_cfg = 0; - - // On initialise le multiplicateur à 1 par défaut - let mut notify_multiplier: u32 = 1; - - // 1. Activer le Bus Master et le Memory Space globalement pour ce périphérique avant de commencer - // let old_cmd = pci_read::(0, dev, 0, 0x04); - // pci_write(0, dev, 0, 0x04, old_cmd | 0x06); - - // 2. Boucle d'assignation des BARs (AVANT de lire les capabilities) - let mut bar_idx = 0; - while bar_idx < 6 { - let bar_reg = 0x10 + (bar_idx * 4); - let bar_val = pci_read::(0, dev, 0, bar_reg); - - // Si le BAR veut de la mémoire (bit 0 == 0) et n'est pas mappé - if bar_val & 0x1 == 0 && (bar_val & 0xFFFF_FFF0) == 0 { - let new_addr = - 0x5100_0000 + (dev as u32 * 0x100_000) + (bar_idx as u32 * 0x4000); - println!("ALLOCATE BAR {} at {:x}", bar_idx, new_addr); - pci_write(0, dev, 0, bar_reg, new_addr | (bar_val & 0xF)); - - // Gérer les BAR 64-bits (ils consomment deux slots) - if (bar_val >> 1) & 0x3 == 2 { - println!("bar {} is 64 bits", bar_idx); - pci_write(0, dev, 0, bar_reg + 4, 0); - - bar_idx += 1 + if capability.bar <= 5 { + let bar_addr = device.get_bar(capability.bar).base_address(); + let addr = bar_addr.wrapping_add(capability.offset as usize); + match capability.capability_type { + VirtioCapabilityType::Common => { + common_cfg = addr as usize; + println!("[VirtIO] CommonCfg trouvé à 0x{:x?}", addr); } - pci_write(0, dev, 0, bar_reg, new_addr); - let confirm = pci_read::(0, dev, 0, bar_reg); - println!("BAR confirmé : {:x}", confirm); - } - bar_idx += 1 - } - let mut cap_ptr = (pci_read::(0, dev, 0, 0x34) & 0xFF) as u16; - - while cap_ptr != 0 { - let header: u32 = pci_read(0, dev, 0, cap_ptr); - let cap_id = (header & 0xFF) as u8; - let next_ptr = ((header >> 8) & 0xFF) as u16; - let v_type = ((header >> 24) & 0xFF) as u8; - - if cap_id == 0x09 { - // VirtIO Vendor Capability - let bar_idx = (pci_read::(0, dev, 0, cap_ptr + 4) & 0xFF) as u8; - let offset = pci_read::(0, dev, 0, cap_ptr + 8) as usize; - - // VirtIO : bar_idx doit être entre 0 et 5. 0xFF signifie "ignoré". - if bar_idx <= 5 { - let bar_reg_offset = 0x10 + (bar_idx as u16 * 4); - let mut bar_val = pci_read::(0, dev, 0, bar_reg_offset); - - let bar_addr = (bar_val & 0xFFFF_FFF0) as usize; - let final_addr = bar_addr + offset; - - match v_type { - 1 => { - common_cfg = final_addr; - println!("[VirtIO] CommonCfg trouvé à 0x{:x}", final_addr); - } - 2 => { - notify_cfg = final_addr; - // TRÈS IMPORTANT : Lire le multiplicateur (offset 16 de la capability) - notify_multiplier = pci_read(0, dev, 0, cap_ptr + 16); - println!( - "[VirtIO] NotifyCfg trouvé à 0x{:x} (mult: {})", - final_addr, notify_multiplier - ); - } - 3 => { - isr_cfg = final_addr; - println!("[VirtIO] IsrCfg trouvé à 0x{:x}", final_addr); - } - _ => {} - } + VirtioCapabilityType::Notify => { + notify_cfg = addr as usize; + notify_multiplier = unsafe { + (*(capability_addr as *mut VirtioNotificationCapability)) + .notify_offset_multiplier + }; + println!("[VirtIO] NotifyCfg trouvé à 0x{:x?}", addr); } + VirtioCapabilityType::Isr => { + isr_cfg = addr as usize; + println!("[VirtIO] IsrCfg trouvé à 0x{:x?}", addr); + } + _ => {} } - cap_ptr = next_ptr; - } - if common_cfg != 0 && notify_cfg != 0 && isr_cfg != 0 { - return Some(VirtioPciCaps { - common_cfg, - notify_cfg, - isr_cfg, - notify_multiplier, - }); } } } - None + + if common_cfg != 0 && notify_cfg != 0 && isr_cfg != 0 { + Some(VirtioPciCaps { + common_cfg, + notify_cfg, + isr_cfg, + notify_multiplier, + }) + } else { + None + } } diff --git a/src/virtio.rs b/src/virtio.rs index d149f36..edbb31e 100644 --- a/src/virtio.rs +++ b/src/virtio.rs @@ -65,3 +65,42 @@ pub struct VirtioInputEvent { pub code: u16, pub value: u32, } + +#[repr(C, packed)] +#[derive(Debug, Clone, Copy)] +pub struct VirtioCapability { + pub capability_id: u8, + pub next_ptr: u8, + pub capability_len: u8, + pub capability_type: VirtioCapabilityType, + pub bar: u8, + pub id: u8, + pub padding: [u8; 2], + pub offset: u32, + pub length: u32, +} +#[repr(C, packed)] +#[derive(Debug, Clone, Copy)] +pub struct VirtioNotificationCapability { + pub capabilities: VirtioCapability, + pub notify_offset_multiplier: u32, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum VirtioCapabilityType { + /// Common configuration + Common = 1, + /// Notifications + Notify = 2, + /// ISR Status + Isr = 3, + /// Device specific configuration + Device = 4, + /// PCI configuration access + Pci = 5, + /// Shared memory region + SharedMemory = 8, + /// Vendor-specific data + Vendor = 9, +} diff --git a/src/volatile.rs b/src/volatile.rs index 5a68c35..5dd0442 100644 --- a/src/volatile.rs +++ b/src/volatile.rs @@ -3,10 +3,10 @@ pub struct Volatile(T); impl Volatile { - pub unsafe fn read(self: *const Self) -> T { + pub unsafe fn read_volatile(self: *const Self) -> T { unsafe { core::ptr::read_volatile(self as *const T) } } - pub unsafe fn write(self: *mut Self, value: T) { + pub unsafe fn write_volatile(self: *mut Self, value: T) { unsafe { core::ptr::write_volatile(self as *mut T, value) } } }