diff --git a/Makefile b/Makefile index 8bb3f30..111c083 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,8 @@ QEMU_OPTS+=-kernel $(O)/kernel.bin \ -M virt,virtualization=off \ -cpu cortex-a72 \ -m 512 \ - -serial chardev:serial0 + -serial chardev:serial0 \ + -device virtio-serial-pci endif ifeq ($(MACH),rpi3b) QEMU_OPTS+=-kernel $(O)/kernel.bin \ diff --git a/kernel/src/arch/aarch64/mach_qemu/mod.rs b/kernel/src/arch/aarch64/mach_qemu/mod.rs index 244f216..b64c9cf 100644 --- a/kernel/src/arch/aarch64/mach_qemu/mod.rs +++ b/kernel/src/arch/aarch64/mach_qemu/mod.rs @@ -7,6 +7,7 @@ use crate::arch::aarch64::{ use crate::dev::timer::TimestampSource; use crate::dev::{ irq::{IntController, IntSource}, + pci::{PciHostDevice, pcie::gpex::GenericPcieHost}, rtc::pl031::Pl031, serial::{pl011::Pl011, SerialDevice}, Device, @@ -19,6 +20,8 @@ const UART0_BASE: usize = 0x09000000; const RTC_BASE: usize = 0x09010000; const GICD_BASE: usize = 0x08000000; const GICC_BASE: usize = 0x08010000; +// TODO extract this from device tree +const ECAM_BASE: usize = 0x4010000000; #[allow(missing_docs)] pub fn init_board() -> Result<(), Errno> { @@ -30,6 +33,9 @@ pub fn init_board() -> Result<(), Errno> { RTC.enable()?; RTC.init_irqs()?; + + PCIE.enable()?; + PCIE.map()?; } Ok(()) } @@ -55,4 +61,5 @@ pub fn intc() -> &'static impl IntController { static UART0: Pl011 = unsafe { Pl011::new(UART0_BASE, IrqNumber::new(33)) }; static RTC: Pl031 = unsafe { Pl031::new(RTC_BASE, IrqNumber::new(34)) }; static GIC: Gic = unsafe { Gic::new(GICD_BASE, GICC_BASE) }; +static PCIE: GenericPcieHost = unsafe { GenericPcieHost::new(ECAM_BASE, 8) }; static LOCAL_TIMER: GenericTimer = GenericTimer {}; diff --git a/kernel/src/dev/mod.rs b/kernel/src/dev/mod.rs index 1a03c41..26de822 100644 --- a/kernel/src/dev/mod.rs +++ b/kernel/src/dev/mod.rs @@ -5,6 +5,7 @@ use error::Errno; // Device classes pub mod gpio; pub mod irq; +pub mod pci; pub mod rtc; pub mod serial; pub mod timer; diff --git a/kernel/src/dev/pci/mod.rs b/kernel/src/dev/pci/mod.rs new file mode 100644 index 0000000..4b24d94 --- /dev/null +++ b/kernel/src/dev/pci/mod.rs @@ -0,0 +1,106 @@ +#![allow(missing_docs)] + +use crate::dev::Device; +use core::fmt; +use error::Errno; + +pub mod pcie; + +macro_rules! ecam_field { + ($getter:ident, $off:expr, u16) => { + #[inline(always)] + fn $getter(&self) -> u16 { + self.readw($off) + } + }; + ($getter:ident, $off:expr, u8) => { + #[inline(always)] + fn $getter(&self) -> u8 { + self.readb($off) + } + }; + ($getter:ident, $setter:ident, $off:expr, u16) => { + #[inline(always)] + unsafe fn $setter(&self, v: u16) { + self.writew($off, v) + } + + ecam_field! { $getter, $off, u16 } + }; +} + +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct PciAddress { + value: u32, +} + +pub trait PciCfgSpace { + fn readl(&self, off: usize) -> u32; + unsafe fn writel(&self, off: usize, val: u32); + + #[inline(always)] + fn readw(&self, off: usize) -> u16 { + assert!(off & 0x1 == 0); + (self.readl(off & !0x3) >> ((off & 0x3) * 8)) as u16 + } + + #[inline(always)] + fn readb(&self, off: usize) -> u8 { + (self.readl(off & !0x3) >> ((off & 0x3) * 8)) as u8 + } + + ecam_field! { vendor_id, 0x00, u16 } + ecam_field! { device_id, 0x02, u16 } + ecam_field! { header_type, 0x0E, u8 } + + #[inline(always)] + fn is_valid(&self) -> bool { + self.readl(0) != 0xFFFFFFFF + } +} + +pub trait PciHostDevice: Device { + fn map(&self) -> Result<(), Errno>; +} + +impl PciAddress { + #[inline(always)] + pub const fn new(bus: u8, dev: u8, func: u8) -> Self { + Self { + value: ((bus as u32) << 8) | ((dev as u32) << 3) | (func as u32), + } + } + + #[inline(always)] + pub const fn bus(self) -> u8 { + (self.value >> 8) as u8 + } + + #[inline(always)] + pub const fn dev(self) -> u8 { + ((self.value >> 3) as u8) & 0x1F + } + + #[inline(always)] + pub const fn func(self) -> u8 { + (self.value as u8) & 0x7 + } + + #[inline(always)] + pub const fn with_func(self, func: u8) -> Self { + Self::new(self.bus(), self.dev(), func) + } +} + +impl fmt::Debug for PciAddress { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{:02x}:{:02x}:{:02x}", + self.bus(), + self.dev(), + self.func() + ) + } +} diff --git a/kernel/src/dev/pci/pcie/gpex.rs b/kernel/src/dev/pci/pcie/gpex.rs new file mode 100644 index 0000000..5ab4f9c --- /dev/null +++ b/kernel/src/dev/pci/pcie/gpex.rs @@ -0,0 +1,76 @@ +use crate::dev::{ + pci::{pcie::EcamCfgSpace, PciCfgSpace, PciAddress, PciHostDevice}, + Device, +}; +use error::Errno; + +pub struct GenericPcieHost { + ecam_base: usize, + // TODO + #[allow(dead_code)] + bus_count: u8, +} + +impl Device for GenericPcieHost { + fn name(&self) -> &'static str { + "Generic PCIe Host Controller" + } + + unsafe fn enable(&self) -> Result<(), Errno> { + Ok(()) + } +} + +impl PciHostDevice for GenericPcieHost { + fn map(&self) -> Result<(), Errno> { + let bus0 = unsafe { EcamCfgSpace::new(self.ecam_base, PciAddress::new(0, 0, 0)) }; + + if bus0.header_type() & 0x80 == 0 { + self.map_bus(0)?; + } else { + todo!() + } + + Ok(()) + } +} + +impl GenericPcieHost { + fn map_device(&self, addr: PciAddress) -> Result<(), Errno> { + let fn0 = unsafe { EcamCfgSpace::new(self.ecam_base, addr) }; + if !fn0.is_valid() { + return Ok(()); + } + let ty = fn0.header_type(); + + //self.map_function(addr, fn0)?; + + // Check if device is a multi-function one + if ty & 0x80 != 0 { + for func in 1..8 { + let addr = addr.with_func(func); + let f = unsafe { EcamCfgSpace::new(self.ecam_base, addr) }; + if f.is_valid() { + //self.map_function(addr, f)?; + } + } + } + + Ok(()) + } + + fn map_bus(&self, bus: u8) -> Result<(), Errno> { + for dev in 0u8..=255 { + self.map_device(PciAddress::new(bus, dev, 0))?; + } + + Ok(()) + } + + pub const unsafe fn new(ecam_base: usize, bus_count: u8) -> Self { + Self { + ecam_base, + bus_count, + } + } +} diff --git a/kernel/src/dev/pci/pcie/mod.rs b/kernel/src/dev/pci/pcie/mod.rs new file mode 100644 index 0000000..3690bff --- /dev/null +++ b/kernel/src/dev/pci/pcie/mod.rs @@ -0,0 +1,33 @@ +use crate::dev::pci::{PciAddress, PciCfgSpace}; + +pub mod gpex; + +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct EcamCfgSpace { + base: usize, +} + +impl EcamCfgSpace { + pub const unsafe fn new(ecam_base: usize, addr: PciAddress) -> Self { + Self { + base: ecam_base + (addr.value as usize) * 4096 + } + } +} + +impl PciCfgSpace for EcamCfgSpace { + #[inline(always)] + fn readl(&self, off: usize) -> u32 { + assert!(off & 0x3 == 0); + unsafe { + core::ptr::read_volatile((self.base + off) as *const u32) + } + } + + #[inline(always)] + unsafe fn writel(&self, off: usize, val: u32) { + assert!(off & 0x3 == 0); + core::ptr::write_volatile((self.base + off) as *mut u32, val); + } +}