diff --git a/src/io.rs b/src/io.rs index e9448cb..978b26c 100644 --- a/src/io.rs +++ b/src/io.rs @@ -64,8 +64,8 @@ macro_rules! println { () => { $crate::print!("\n\r") }; - ($($args:expr),*) => { + ($($args:expr),*) => {{ $crate::print!($($args),*); - $crate::println!() - }; + $crate::println!(); + }}; } diff --git a/src/main.rs b/src/main.rs index 931e628..0f2411b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ use log::info; use crate::{ io::init_log, - pci::scan_pci_for_virtio_keyboard, + pci::{PciDeviceIterator, scan_pci_for_virtio_keyboard}, riscv::enable_supervisor_interrupt, scheduler::{SCHEDULER, idle}, user::{proc2, test}, @@ -60,6 +60,9 @@ static HEAP_INITIALIZED: AtomicBool = AtomicBool::new(false); // Usize is assumed to be an u64 in the whole kernel const _: () = assert!(core::mem::size_of::() == core::mem::size_of::()); +#[cfg(not(target_endian = "little"))] +compile_error! {"This kernel implementation assume endianness is little-endian. Some memory access like PCI could not work in big-endian."} + // 1. Allouer de la mémoire statique alignée pour la queue static mut KBD_QUEUE: Virtqueue = unsafe { core::mem::zeroed() }; pub static mut KBD_DRIVER: Option = None; @@ -87,6 +90,10 @@ pub extern "C" fn supervisor_mode_entry() { enable_supervisor_interrupt(); + for pci in PciDeviceIterator::new() { + println!("{:x?}", pci.vendor_and_device_id()) + } + unsafe { let pci_info = scan_pci_for_virtio_keyboard().unwrap(); KBD_DRIVER = Some(VirtioPciDriver::new( diff --git a/src/pci.rs b/src/pci.rs index 2982bac..8079569 100644 --- a/src/pci.rs +++ b/src/pci.rs @@ -10,23 +10,24 @@ pub struct VirtioPciRegion { pub offset: u32, pub length: u32, } + // Helpers ECAM -fn pci_read_u32(bus: u8, dev: u8, func: u8, offset: u16) -> u32 { +fn pci_read(bus: u8, dev: u8, func: u8, offset: u16) -> T { let addr = PCI_ECAM_BASE | ((bus as usize) << 20) | ((dev as usize) << 15) | ((func as usize) << 12) | (offset as usize); - unsafe { core::ptr::read_volatile(addr as *const u32) } + unsafe { core::ptr::read_volatile(addr as *const T) } } -fn pci_write_u32(bus: u8, dev: u8, func: u8, offset: u16, val: u32) { +fn pci_write(bus: u8, dev: u8, func: u8, offset: u16, val: T) { let addr = PCI_ECAM_BASE | ((bus as usize) << 20) | ((dev as usize) << 15) | ((func as usize) << 12) | (offset as usize); - unsafe { core::ptr::write_volatile(addr as *mut u32, val) } + unsafe { core::ptr::write_volatile(addr as *mut T, val) } } #[derive(Copy, Clone, Debug)] pub struct VirtioPciCaps { @@ -36,24 +37,95 @@ pub struct VirtioPciCaps { pub notify_multiplier: u32, } +#[derive(Debug, Clone, Copy)] pub struct PciDevice { - device_id: u8, + pub bus: u8, + pub device: u8, } +pub struct PciGeneralDevice(PciDevice); + impl PciDevice { - pub fn new(device_id: u8) -> Self { - Self { device_id } + pub fn new(bus: u8, device: u8) -> Self { + Self { bus, device } + } + + pub fn vendor_and_device_id(&self) -> (u16, u16) { + let id = self.read::(0); + ((id & 0xFFFF) as u16, (id >> 16) as u16) + } + pub fn device_id(&self) -> u16 { + self.vendor_and_device_id().1 + } + pub fn vendor_id(&self) -> u16 { + self.vendor_and_device_id().0 + } + + pub fn interrupt_pin(&self) -> u8 { + self.read(0x3D) + } + + fn read(&self, offset: u16) -> T { + pci_read(self.bus, self.device, 0, offset) + } + + fn write_u32(&self, offset: u16, value: u32) { + pci_write(self.bus, self.device, 0, offset, value) + } + + /// # Safety + /// `self` must be a valid general PCI device. + pub unsafe fn to_general_device(self) -> PciGeneralDevice { + PciGeneralDevice(self) } } -pub fn pci_iter() { - « +pub struct PciDeviceIterator { + pub device: u8, +} + +impl PciDeviceIterator { + pub fn new() -> Self { + let header_type: u32 = pci_read(0, 0, 0, 0); + if header_type & 0x80 != 0 { + unimplemented!("Multiple PCI host controllers are not supported") + } + Self { device: 0 } + } +} + +impl Iterator for PciDeviceIterator { + type Item = PciDevice; + + fn next(&mut self) -> Option { + if self.device == 32 { + return None; + } + let dev = self.device; + self.device += 1; + let vendor: u16 = pci_read(0, dev, 0, 0); + if vendor == 0xFFFF { + self.next() + } else { + Some(PciDevice::new(0, dev)) + } + } +} +pub fn scan_pci_for_virtio_keyboard2() -> Option { + let device = PciDeviceIterator::new() + .find(|device| device.vendor_and_device_id() == (0x1af4, 0x1052)) + .unwrap(); + let device = unsafe { + // VirtIO keyboard is a general PCI device + device.to_general_device() + }; + None } 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 = pci_read_u32(0, dev, 0, 0x00); + let vdev: u32 = pci_read(0, dev, 0, 0x00); let vendor = (vdev & 0xffff) as u16; let device = (vdev >> 16) as u16; @@ -61,22 +133,22 @@ pub fn scan_pci_for_virtio_keyboard() -> Option { // 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_u32(0, dev, 0, 0x3C) >> 8) & 0xFF; // Offset 0x3D + 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_u32(0, 2, 0, 0x3C); + 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_u32(0, 2, 0, 0x3C, new_val); + 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_u32(0, dev, 0, 0x04); - pci_write_u32(0, dev, 0, 0x04, cmd | 0x6); + 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; @@ -86,52 +158,52 @@ pub fn scan_pci_for_virtio_keyboard() -> Option { 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_u32(0, dev, 0, 0x04); - pci_write_u32(0, dev, 0, 0x04, old_cmd | 0x06); + 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_u32(0, dev, 0, bar_reg); + 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_u32(0, dev, 0, bar_reg, new_addr | (bar_val & 0xF)); + 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_u32(0, dev, 0, bar_reg + 4, 0); + pci_write(0, dev, 0, bar_reg + 4, 0); bar_idx += 1 } - pci_write_u32(0, dev, 0, bar_reg, new_addr); - let confirm = pci_read_u32(0, dev, 0, bar_reg); + 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_u32(0, dev, 0, 0x34) & 0xFF) as u16; + let mut cap_ptr = (pci_read::(0, dev, 0, 0x34) & 0xFF) as u16; while cap_ptr != 0 { - let header = pci_read_u32(0, dev, 0, cap_ptr); + 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_u32(0, dev, 0, cap_ptr + 4) & 0xFF) as u8; - let offset = pci_read_u32(0, dev, 0, cap_ptr + 8) as usize; + 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_u32(0, dev, 0, bar_reg_offset); + 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; @@ -144,7 +216,7 @@ pub fn scan_pci_for_virtio_keyboard() -> Option { 2 => { notify_cfg = final_addr; // TRÈS IMPORTANT : Lire le multiplicateur (offset 16 de la capability) - notify_multiplier = pci_read_u32(0, dev, 0, cap_ptr + 16); + notify_multiplier = pci_read(0, dev, 0, cap_ptr + 16); println!( "[VirtIO] NotifyCfg trouvé à 0x{:x} (mult: {})", final_addr, notify_multiplier