diff --git a/kernel/driver/bus/pci/src/capability.rs b/kernel/driver/bus/pci/src/capability.rs index fe93aea9..8e53e948 100644 --- a/kernel/driver/bus/pci/src/capability.rs +++ b/kernel/driver/bus/pci/src/capability.rs @@ -1,6 +1,7 @@ //! PCI capability structures and queries use alloc::{sync::Arc, vec, vec::Vec}; +use bitflags::bitflags; use device_api::interrupt::{ InterruptAffinity, InterruptHandler, MessageInterruptController, MsiInfo, }; @@ -15,6 +16,16 @@ use crate::PciBaseAddress; use super::{PciCapability, PciCapabilityId, PciConfigurationSpace}; +bitflags! { + pub struct PcieLinkControl: u16 { + const ASPM_DISABLE = 0 << 0; + // Active state power management control + const ASPM_MASK = 0x3 << 0; + // Enable clock power management + const ECPM = 1 << 8; + } +} + #[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))] use core::mem::offset_of; #[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))] @@ -50,12 +61,12 @@ pub trait VirtioCapability { /// Power management capability entry pub struct PowerManagementCapability; - /// MSI-X capability query pub struct MsiXCapability; - /// MSI capability query pub struct MsiCapability; +/// PCIe capability +pub struct PciExpressCapability; // VirtIO-over-PCI capabilities /// VirtIO PCI configuration access @@ -116,6 +127,12 @@ pub struct MsiData<'s, S: PciConfigurationSpace + ?Sized + 's> { offset: usize, } +/// PCI Express capability data structure +pub struct PcieData<'s, S: PciConfigurationSpace + ?Sized + 's> { + space: &'s S, + offset: usize, +} + pub struct VirtioDeviceConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> { space: &'s S, offset: usize, @@ -193,6 +210,19 @@ impl PciCapability for MsiCapability { } } +impl PciCapability for PciExpressCapability { + const ID: PciCapabilityId = PciCapabilityId::PciExpress; + type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = PcieData<'a, S>; + + fn data<'s, S: PciConfigurationSpace + ?Sized + 's>( + space: &'s S, + offset: usize, + _len: usize, + ) -> Self::CapabilityData<'s, S> { + PcieData { space, offset } + } +} + impl VirtioCapability for VirtioDeviceConfigCapability { const CFG_TYPE: u8 = 0x04; type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioDeviceConfigData<'a, S>; @@ -571,3 +601,13 @@ impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiData<'s, S> { Ok(info) } } + +impl<'s, S: PciConfigurationSpace + ?Sized + 's> PcieData<'s, S> { + pub fn link_control(&self) -> PcieLinkControl { + PcieLinkControl::from_bits_retain(self.space.read_u16(self.offset + 0x10)) + } + + pub fn set_link_control(&mut self, value: PcieLinkControl) { + self.space.write_u16(self.offset + 0x10, value.bits()); + } +} diff --git a/kernel/driver/bus/pci/src/lib.rs b/kernel/driver/bus/pci/src/lib.rs index 616fdaef..d256fa1d 100644 --- a/kernel/driver/bus/pci/src/lib.rs +++ b/kernel/driver/bus/pci/src/lib.rs @@ -93,6 +93,7 @@ primitive_enum! { PowerManagement = 0x01, Msi = 0x05, VendorSpecific = 0x09, + PciExpress = 0x10, MsiX = 0x11, } } diff --git a/kernel/driver/bus/pci/src/nodes.rs b/kernel/driver/bus/pci/src/nodes.rs index 04a7357b..84a46c2e 100644 --- a/kernel/driver/bus/pci/src/nodes.rs +++ b/kernel/driver/bus/pci/src/nodes.rs @@ -110,6 +110,7 @@ pub(crate) fn make_sysfs_object( Some(PciCapabilityId::Msi) => write!(output, "msi").ok(), Some(PciCapabilityId::MsiX) => write!(output, "msix").ok(), Some(PciCapabilityId::VendorSpecific) => write!(output, "vendor-specific").ok(), + Some(PciCapabilityId::PciExpress) => write!(output, "pcie").ok(), Some(PciCapabilityId::PowerManagement) => { write!(output, "power-management").ok() } diff --git a/kernel/driver/net/core/src/ephy.rs b/kernel/driver/net/core/src/ephy.rs new file mode 100644 index 00000000..8993f07f --- /dev/null +++ b/kernel/driver/net/core/src/ephy.rs @@ -0,0 +1,181 @@ +//! Ethernet PHY utilities + +use libk::error::Error; +use yggdrasil_abi::bitflags; + +bitflags! { + pub struct BMCR: u16 { + const RESET: bit 15; + const LOOPBACK: bit 14; + const SPEED0: bit 13; + const ANE: bit 12; + const PWD: bit 11; + const ISOLATE: bit 10; + const RESTART_AN: bit 9; + const DUPLEX: bit 8; + const SPEED1: bit 6; + } +} + +bitflags! { + pub struct BMSR: u16 { + const HAVE_100BASET4: bit 15; + const HAVE_100BASETX_FULL: bit 14; + const HAVE_100BASETX_HALF: bit 13; + const HAVE_10BASET_FULL: bit 12; + const HAVE_10BASET_HALF: bit 11; + const HAVE_100BASET2_FULL: bit 10; + const HAVE_100BASET2_HALF: bit 9; + const EXT_STATUS_1000BASET: bit 8; + const PREAMBLE_SUPP: bit 6; + const AN_COMPLETE: bit 5; + const REMOTE_FAULT: bit 4; + const AN_AVAILABLE: bit 3; + const LINK_STATUS: bit 2; + const JABBER: bit 1; + const EXTENDED_CAP: bit 0; + } +} + +bitflags! { + pub struct ANAR: u16 { + const HAVE_10BASET: bit 5; + const HAVE_10BASET_FULL: bit 6; + const HAVE_100BASETX: bit 7; + const HAVE_100BASETX_FULL: bit 8; + const HAVE_100BASET4: bit 9; + const HAVE_PAUSE: bit 10; + const ASM_DIR: bit 11; + const REMOTE_FAULT: bit 13; + const NEXT_PAGE: bit 15; + } +} + +bitflags! { + pub struct GBCR: u16 { + const HAVE_1000BASET_HALF: bit 8; + const HAVE_1000BASET_FULL: bit 9; + } +} + +bitflags! { + pub struct GBESR: u16 { + const HAVE_1000BASET_HALF: bit 12; + const HAVE_1000BASET_FULL: bit 13; + } +} + +pub trait MdioBus { + fn mii_read(&self, phyaddr: u8, regaddr: u8) -> Result; + fn mii_write(&self, phyaddr: u8, regaddr: u8, value: u16) -> Result<(), Error>; +} + +pub struct PhyAccess<'a, M: MdioBus> { + address: u8, + mii: &'a M, +} + +pub const REG_BMCR: u8 = 0x00; +pub const REG_BMSR: u8 = 0x01; +pub const REG_ANAR: u8 = 0x04; +pub const REG_GBCR: u8 = 0x09; +pub const REG_GBESR: u8 = 0x0F; + +pub const ANAR_SELECTOR_802_3: u16 = 0b00001; +pub const ANAR_SELECTOR_MASK: u16 = 0b11111; + +impl<'a, M: MdioBus> PhyAccess<'a, M> { + pub fn new(mii: &'a M, address: u8) -> Self { + Self { address, mii } + } + + pub fn probe(_mii: &'a M, _address: u8) -> Result { + todo!() + } + + pub fn read_reg(&self, reg: u8) -> Result { + self.mii.mii_read(self.address, reg) + } + + pub fn write_reg(&self, reg: u8, value: u16) -> Result<(), Error> { + self.mii.mii_write(self.address, reg, value) + } + + pub fn status(&self) -> Result { + self.read_reg(REG_BMSR).map(BMSR::from) + } + + pub fn reset(&self, mut timeout: u64) -> Result<(), Error> { + self.write_reg(REG_BMCR, BMCR::RESET.bits())?; + while timeout > 0 && self.read_reg(REG_BMCR)? & BMCR::RESET.bits() != 0 { + core::hint::spin_loop(); + timeout -= 1; + } + + if timeout > 0 { + Ok(()) + } else { + Err(Error::TimedOut) + } + } + + pub fn setup_link(&self, have_pause: bool, force_gbesr: GBESR) -> Result<(), Error> { + let bmsr = BMSR::from(self.read_reg(REG_BMSR)?); + let mut gbesr = if bmsr.contains(BMSR::EXT_STATUS_1000BASET) { + GBESR::from(self.read_reg(REG_GBESR)?) + } else { + GBESR::empty() + }; + gbesr |= force_gbesr; + let mut anar = ANAR::from_capabilities(bmsr); + if have_pause { + anar |= ANAR::HAVE_PAUSE | ANAR::ASM_DIR; + } + let mut gbcr = GBCR::empty(); + if gbesr.contains(GBESR::HAVE_1000BASET_HALF) { + gbcr |= GBCR::HAVE_1000BASET_HALF; + } + if gbesr.contains(GBESR::HAVE_1000BASET_FULL) { + gbcr |= GBCR::HAVE_1000BASET_FULL; + } + + self.write_reg(REG_ANAR, anar.bits())?; + for _ in 0..10000000 { + core::hint::spin_loop(); + } + + self.write_reg(REG_GBCR, gbcr.bits())?; + for _ in 0..10000000 { + core::hint::spin_loop(); + } + + self.write_reg(REG_BMCR, (BMCR::ANE | BMCR::RESTART_AN).bits())?; + for _ in 0..10000000 { + core::hint::spin_loop(); + } + + Ok(()) + } +} + +impl ANAR { + pub fn from_capabilities(bmsr: BMSR) -> ANAR { + let mut anar = ANAR::from(ANAR_SELECTOR_802_3); + if bmsr.contains(BMSR::HAVE_10BASET_HALF) { + anar |= ANAR::HAVE_10BASET; + } + if bmsr.contains(BMSR::HAVE_10BASET_FULL) { + anar |= ANAR::HAVE_10BASET_FULL; + } + if bmsr.contains(BMSR::HAVE_100BASETX_HALF) { + anar |= ANAR::HAVE_100BASETX; + } + if bmsr.contains(BMSR::HAVE_100BASETX_FULL) { + anar |= ANAR::HAVE_100BASETX_FULL; + } + if bmsr.contains(BMSR::HAVE_100BASET4) { + anar |= ANAR::HAVE_100BASET4; + } + anar + } +} diff --git a/kernel/driver/net/core/src/lib.rs b/kernel/driver/net/core/src/lib.rs index 870c3a5e..33461cab 100644 --- a/kernel/driver/net/core/src/lib.rs +++ b/kernel/driver/net/core/src/lib.rs @@ -25,6 +25,8 @@ pub mod socket; pub mod interface; pub mod util; +pub mod ephy; + pub use interface::register_interface; pub struct RxPacket { diff --git a/kernel/driver/net/rtl81xx/src/lib.rs b/kernel/driver/net/rtl81xx/src/lib.rs index 3138cfe8..a72218f8 100644 --- a/kernel/driver/net/rtl81xx/src/lib.rs +++ b/kernel/driver/net/rtl81xx/src/lib.rs @@ -6,6 +6,9 @@ use libk::error::Error; use rtl8139::Rtl8139; use rtl8168::Rtl8168; use ygg_driver_pci::{ + capability::{ + DevicePowerState, PciExpressCapability, PcieLinkControl, PowerManagementCapability, + }, device::{PciDeviceInfo, PreferredInterruptMode}, macros::pci_driver, PciBaseAddress, PciCommandRegister, PciConfigurationSpace, @@ -26,6 +29,12 @@ pci_driver! { fn probe(&self, info: &PciDeviceInfo, dma: &Arc) -> Result, Error> { info.init_interrupts(PreferredInterruptMode::Msi(false))?; + if let Some(mut pcie) = info.config_space.capability::() { + let mut lcr = pcie.link_control(); + lcr.remove(PcieLinkControl::ASPM_MASK | PcieLinkControl::ECPM); + pcie.set_link_control(lcr); + } + // Enable MMIO + interrupts + bus mastering let mut command = info.config_space.command(); command |= (PciCommandRegister::BUS_MASTER | PciCommandRegister::ENABLE_MEMORY).bits(); @@ -58,9 +67,9 @@ pci_driver! { .and_then(PciBaseAddress::as_memory) .ok_or(Error::InvalidArgument)?; - // if let Some(power) = info.config_space.capability::() { - // power.set_device_power_state(DevicePowerState::D0); - // } + if let Some(power) = info.config_space.capability::() { + power.set_device_power_state(DevicePowerState::D0); + } // Enable MMIO + interrupts + bus mastering info.set_command(true, true, false, true); diff --git a/kernel/driver/net/rtl81xx/src/rtl8168.rs b/kernel/driver/net/rtl81xx/src/rtl8168.rs index 005fc3b8..323b8b20 100644 --- a/kernel/driver/net/rtl81xx/src/rtl8168.rs +++ b/kernel/driver/net/rtl81xx/src/rtl8168.rs @@ -21,6 +21,7 @@ use tock_registers::{ registers::{ReadOnly, ReadWrite, WriteOnly}, }; use ygg_driver_net_core::{ + ephy::{MdioBus, PhyAccess, GBESR}, interface::{NetworkDevice, NetworkInterfaceType}, RxPacket, }; @@ -48,6 +49,15 @@ register_bitfields! { NPQ OFFSET(6) NUMBITS(1) [], FSWInt OFFSET(0) NUMBITS(1) [], ], + R9346CR [ + EEM OFFSET(6) NUMBITS(2) [ + Normal = 0b00, + ConfigWriteEnable = 0b11 + ], + ], + CONFIG2 [ + MSI OFFSET(5) NUMBITS(1) [], + ], PHYSTATUS [ TXFLOW OFFSET(6) NUMBITS(1) [], RXFLOW OFFSET(5) NUMBITS(1) [], @@ -172,10 +182,10 @@ register_structs! { (0x0044 => RCR: ReadWrite), (0x0048 => TCTR: ReadWrite), (0x004C => MPKT: ReadWrite), - (0x0050 => R9346CR: ReadWrite), + (0x0050 => R9346CR: ReadWrite), (0x0051 => CONFIG0: ReadWrite), (0x0052 => CONFIG1: ReadWrite), - (0x0053 => CONFIG2: ReadWrite), + (0x0053 => CONFIG2: ReadWrite), (0x0054 => CONFIG3: ReadWrite), (0x0055 => CONFIG4: ReadWrite), (0x0056 => CONFIG5: ReadWrite), @@ -186,6 +196,8 @@ register_structs! { (0x0064 => _6), (0x006C => PHYSTATUS: ReadOnly), (0x006D => _7), + (0x0082 => LDPS: ReadWrite), + (0x0083 => _13), (0x0084 => WAKEUPn: [ReadWrite; 16]), (0x00C4 => CRCn: [ReadWrite; 5]), (0x00CE => _8), @@ -420,41 +432,6 @@ impl Descriptor { impl Regs { const TCR_REVISION_MASK: u32 = 0x7C800000; - pub fn gmii_write(&self, reg: u8, value: u16, mut timeout: u64) -> Result<(), Error> { - self.PHYAR.write( - PHYAR::REGADDR.val(reg as u32) + PHYAR::DATA.val(value as u32) + PHYAR::FLAG::Write, - ); - while timeout > 0 && self.PHYAR.matches_all(PHYAR::FLAG::SET) { - core::hint::spin_loop(); - timeout -= 1; - } - if timeout == 0 { - Err(Error::TimedOut) - } else { - Ok(()) - } - } - - #[allow(unused)] - pub fn gmii_read(&self, reg: u8, mut timeout: u64) -> Result { - self.PHYAR - .write(PHYAR::REGADDR.val(reg as u32) + PHYAR::FLAG::Read); - - loop { - if timeout == 0 { - return Err(Error::TimedOut); - } - - let status = self.PHYAR.extract(); - if status.matches_all(PHYAR::FLAG::SET) { - return Ok(status.read(PHYAR::DATA) as u16); - } - - core::hint::spin_loop(); - timeout -= 1; - } - } - pub fn get_link_state(&self) -> EthernetLinkState { let phystatus = self.PHYSTATUS.extract(); if phystatus.matches_all(PHYSTATUS::LINKSTS::SET) { @@ -492,6 +469,10 @@ impl Regs { pub fn reset_device(&self, mut timeout: u64) -> Result<(), Error> { self.CR.write(CR::RST::SET); + for _ in 0..100000 { + core::hint::spin_loop(); + } + while timeout > 0 && self.CR.matches_all(CR::RST::SET) { core::hint::spin_loop(); timeout -= 1; @@ -505,32 +486,51 @@ impl Regs { } pub fn reset_phy(&self, timeout: u64) -> Result<(), Error> { - // Set the reset bit in bmcr - self.gmii_write(0x00, 1 << 15, timeout)?; - for _ in 0..timeout { - core::hint::spin_loop(); - } - // Disable power off mode + start auto-negotiation - self.gmii_write(0x00, 1 << 12, timeout)?; - for _ in 0..timeout { - core::hint::spin_loop(); - } + let phy = PhyAccess::new(self, 0x00); - self.gmii_write(0x1F, 0x00, timeout)?; - self.gmii_write(0x0E, 0x00, timeout)?; + phy.reset(timeout)?; + phy.setup_link(true, GBESR::empty())?; Ok(()) } +} - // #[inline] - // pub fn lock_config(&self) { - // self.R9346CR.set(0x00); - // } +impl MdioBus for Regs { + fn mii_read(&self, _phyaddr: u8, reg: u8) -> Result { + let mut timeout = 1000000; + self.PHYAR + .write(PHYAR::REGADDR.val(reg as u32) + PHYAR::FLAG::Read); - // #[inline] - // pub fn unlock_config(&self) { - // self.R9346CR.set(0xC0); - // } + loop { + if timeout == 0 { + return Err(Error::TimedOut); + } + + let status = self.PHYAR.extract(); + if status.matches_all(PHYAR::FLAG::SET) { + return Ok(status.read(PHYAR::DATA) as u16); + } + + core::hint::spin_loop(); + timeout -= 1; + } + } + + fn mii_write(&self, _phyaddr: u8, reg: u8, value: u16) -> Result<(), Error> { + let mut timeout = 1000000; + self.PHYAR.write( + PHYAR::REGADDR.val(reg as u32) + PHYAR::DATA.val(value as u32) + PHYAR::FLAG::Write, + ); + while timeout > 0 && self.PHYAR.matches_all(PHYAR::FLAG::SET) { + core::hint::spin_loop(); + timeout -= 1; + } + if timeout == 0 { + Err(Error::TimedOut) + } else { + Ok(()) + } + } } impl Rtl8168 { @@ -565,6 +565,7 @@ impl InterruptHandler for Rtl8168 { if status.get() == 0 { return false; } + log::info!("RTL8168 IRQ"); regs.ISR.set(status.get()); let mut any = false; @@ -609,12 +610,28 @@ impl Device for Rtl8168 { log::info!("Initialize rtl8168"); log::info!("MAC: {}", self.mac); + // Bind IRQ + self.pci + .init_interrupts(PreferredInterruptMode::Msi(false))?; + let msi_info = self.pci.map_interrupt(Default::default(), self.clone())?; + + let regs = self.regs.lock(); + + regs.R9346CR.write(R9346CR::EEM::ConfigWriteEnable); + if msi_info.is_some() { + log::info!("Switch rtl816x into MSI(-x) mode"); + regs.CONFIG2.modify(CONFIG2::MSI::SET); + } else { + log::info!("Switch rtl816x into legacy IRQ mode"); + regs.CONFIG2.modify(CONFIG2::MSI::CLEAR); + } + regs.R9346CR.write(R9346CR::EEM::Normal); + let rx_ring = RxRing::with_capacity(&*self.dma, 256)?; let tx_ring = TxRing::with_capacity(&*self.dma, 256)?; let rx_ring_base = rx_ring.base_address().into_u64(); let tx_ring_base = tx_ring.base_address().into_u64(); - let regs = self.regs.lock(); let hwrev = regs.get_chip_revision(); log::info!("Revision: {:?}", hwrev); @@ -633,6 +650,9 @@ impl Device for Rtl8168 { } regs.CPCR.write(cpcr); + regs.IMR.set(0x0000); + regs.ISR.set(0xFFFF); + // Reset the PHY if flags.contains(ChipFlags::NEED_PHY_RESET) { regs.reset_phy(10000000) @@ -667,7 +687,10 @@ impl Device for Rtl8168 { regs.CR.write(CR::RE::SET + CR::TE::SET); // Setup interrupts - regs.IMR.set(0xFFFF); + // regs.IMR.set(0xFFFF); + regs.IMR.write( + IMR_ISR::RDU::SET + IMR_ISR::ROK::SET + IMR_ISR::TOK::SET + IMR_ISR::LINKCHG::SET, + ); regs.ISR.set(0xFFFF); // Clear missed packet counter (?) @@ -681,11 +704,6 @@ impl Device for Rtl8168 { self.rx.init(IrqSafeSpinlock::new(rx_ring)); self.tx.init(IrqSafeSpinlock::new(tx_ring)); - // Bind IRQ - self.pci - .init_interrupts(PreferredInterruptMode::Msi(false))?; - self.pci.map_interrupt(Default::default(), self.clone())?; - let interface = ygg_driver_net_core::register_interface(NetworkInterfaceType::Ethernet, self.clone()); self.nic.init(interface.id()); diff --git a/kernel/driver/usb/xhci/src/controller.rs b/kernel/driver/usb/xhci/src/controller.rs index 9a56a6fc..e45d67f9 100644 --- a/kernel/driver/usb/xhci/src/controller.rs +++ b/kernel/driver/usb/xhci/src/controller.rs @@ -538,7 +538,6 @@ impl Device for Xhci { impl InterruptHandler for Xhci { fn handle_irq(self: Arc, _vector: IrqVector) -> bool { - log::info!("xhci irq"); let status = self.regs.handle_interrupt(0); if status.matches_all(USBSTS::HSE::SET) { diff --git a/kernel/src/arch/x86_64/apic/local.rs b/kernel/src/arch/x86_64/apic/local.rs index c07613dc..90578d3d 100644 --- a/kernel/src/arch/x86_64/apic/local.rs +++ b/kernel/src/arch/x86_64/apic/local.rs @@ -1,5 +1,5 @@ //! x86-64 Local APIC driver implementation -use core::sync::atomic::Ordering; +use core::sync::atomic::{AtomicUsize, Ordering}; use abi::error::Error; use alloc::{sync::Arc, vec, vec::Vec}; @@ -15,7 +15,7 @@ use kernel_arch_x86_64::{mem::table::L3, LocalApicInterface, CPU_COUNT}; use libk::arch::Cpu; use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo, table::EntryLevelExt}; use libk_util::{ - sync::{IrqGuard, IrqSafeSpinlock}, + sync::{spin_rwlock::IrqSafeRwLock, IrqGuard}, OneTimeInit, }; use tock_registers::{ @@ -134,11 +134,91 @@ register_structs! { } } +#[derive(Clone)] +struct MsiVector { + logical: usize, + handler: Arc, +} + +struct MsiVectorTable { + // MSI_MAX_VECTORS * vectors per row + msi_vectors: IrqSafeRwLock>>, + last_msi_allocated: AtomicUsize, +} + +impl MsiVectorTable { + pub fn new(physical_vectors: usize) -> Self { + let rows = vec![Vec::new(); physical_vectors]; + + Self { + msi_vectors: IrqSafeRwLock::new(rows), + last_msi_allocated: AtomicUsize::new(0), + } + } + + pub fn map( + &self, + apic_id: usize, + range: &mut [MsiInfo], + handler: Arc, + ) -> Result<(), Error> { + if range.is_empty() { + return Err(Error::InvalidArgument); + } + + let seq = self + .last_msi_allocated + .fetch_add(range.len(), Ordering::AcqRel); + let mut rows = self.msi_vectors.write(); + + let physical_start = seq % rows.len(); + let physical_end = seq.wrapping_add(range.len()) % rows.len(); + if range.len() == 1 { + log::info!("Map MSI {:?} -> {}", handler.display_name(), physical_start); + } else { + log::info!( + "Map MSI {:?} -> {:?}", + handler.display_name(), + physical_start..physical_end - 1 + ); + } + + for (logical, info) in range.iter_mut().enumerate() { + let physical = logical.wrapping_add(seq) % rows.len(); + rows[physical].push(MsiVector { + logical, + handler: handler.clone(), + }); + + let address = 0xFEE00000 | (apic_id << 12); + let value = 32 + APIC_MSI_OFFSET + physical as u32; + + *info = MsiInfo { + vector: logical, + affinity: InterruptAffinity::Specific(0), + address, + value, + }; + } + + Ok(()) + } + + pub fn invoke(&self, physical_vector: usize) { + let rows = self.msi_vectors.read(); + let row = &rows[physical_vector]; + + for col in row.iter() { + col.handler.clone().handle_irq(IrqVector::Msi(col.logical)); + } + } +} + /// Per-processor local APIC interface pub struct LocalApic { regs: DeviceMemoryIo<'static, Regs>, id: u32, - msi_vectors: Vec>>>, + msi_table: MsiVectorTable, } unsafe impl Send for LocalApic {} @@ -210,21 +290,7 @@ impl LocalApicInterface for LocalApic { impl MessageInterruptController for LocalApic { fn handle_msi(&self, vector: usize) { - // TODO this is ugly - let row = &self.msi_vectors[vector]; - let mut i = 0; - - loop { - let table = row.lock(); - let Some(handler) = table.get(i).cloned() else { - break; - }; - drop(table); - - handler.handle_irq(IrqVector::Msi(vector)); - - i += 1; - } + self.msi_table.invoke(vector); } fn register_msi_range( @@ -233,36 +299,7 @@ impl MessageInterruptController for LocalApic { handler: Arc, ) -> Result<(), Error> { let _guard = IrqGuard::acquire(); - - // TODO fill smallest vectors first - // TODO don't ignore affinity - - for (i, msi) in range.iter_mut().enumerate() { - let row = &self.msi_vectors[i]; - let mut row = row.lock(); - - row.push(handler.clone()); - - log::info!( - "Bind {}:{} -> apic{}:msi{}", - handler.display_name(), - i, - self.id, - i - ); - - let value = 32 + APIC_MSI_OFFSET + i as u32; - let address = 0xFEE00000 | ((self.id as usize) << 12); - - *msi = MsiInfo { - address, - value, - vector: i, - affinity: InterruptAffinity::Specific(self.id as _), - }; - } - - Ok(()) + self.msi_table.map(self.id as usize, range, handler) } } @@ -348,12 +385,12 @@ impl LocalApic { LocalVectorEntry::Mask::Masked + LocalVectorEntry::Vector.val(APIC_LINT1_VECTOR + 32), ); - let msi_vectors = vec![IrqSafeSpinlock::new(Vec::new()); MAX_MSI_VECTORS as _]; + let msi_table = MsiVectorTable::new(MAX_MSI_VECTORS as _); Self { id, regs, - msi_vectors, + msi_table, } }