apic/net: better MSI allocation, better PHY setup

This commit is contained in:
Mark Poliakov 2025-02-10 18:07:43 +02:00
parent 3f62374431
commit 2867597c8e
9 changed files with 408 additions and 120 deletions

View File

@ -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());
}
}

View File

@ -93,6 +93,7 @@ primitive_enum! {
PowerManagement = 0x01,
Msi = 0x05,
VendorSpecific = 0x09,
PciExpress = 0x10,
MsiX = 0x11,
}
}

View File

@ -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()
}

View File

@ -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<u16, Error>;
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<Self, Error> {
todo!()
}
pub fn read_reg(&self, reg: u8) -> Result<u16, Error> {
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<BMSR, Error> {
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
}
}

View File

@ -25,6 +25,8 @@ pub mod socket;
pub mod interface;
pub mod util;
pub mod ephy;
pub use interface::register_interface;
pub struct RxPacket {

View File

@ -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<dyn DmaAllocator>) -> Result<Arc<dyn Device>, Error> {
info.init_interrupts(PreferredInterruptMode::Msi(false))?;
if let Some(mut pcie) = info.config_space.capability::<PciExpressCapability>() {
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::<PowerManagementCapability>() {
// power.set_device_power_state(DevicePowerState::D0);
// }
if let Some(power) = info.config_space.capability::<PowerManagementCapability>() {
power.set_device_power_state(DevicePowerState::D0);
}
// Enable MMIO + interrupts + bus mastering
info.set_command(true, true, false, true);

View File

@ -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<u32, RCR::Register>),
(0x0048 => TCTR: ReadWrite<u32>),
(0x004C => MPKT: ReadWrite<u32>),
(0x0050 => R9346CR: ReadWrite<u8>),
(0x0050 => R9346CR: ReadWrite<u8, R9346CR::Register>),
(0x0051 => CONFIG0: ReadWrite<u8>),
(0x0052 => CONFIG1: ReadWrite<u8>),
(0x0053 => CONFIG2: ReadWrite<u8>),
(0x0053 => CONFIG2: ReadWrite<u8, CONFIG2::Register>),
(0x0054 => CONFIG3: ReadWrite<u8>),
(0x0055 => CONFIG4: ReadWrite<u8>),
(0x0056 => CONFIG5: ReadWrite<u8>),
@ -186,6 +196,8 @@ register_structs! {
(0x0064 => _6),
(0x006C => PHYSTATUS: ReadOnly<u8, PHYSTATUS::Register>),
(0x006D => _7),
(0x0082 => LDPS: ReadWrite<u8>),
(0x0083 => _13),
(0x0084 => WAKEUPn: [ReadWrite<u32>; 16]),
(0x00C4 => CRCn: [ReadWrite<u16>; 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<u16, Error> {
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<u16, Error> {
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());

View File

@ -538,7 +538,6 @@ impl Device for Xhci {
impl InterruptHandler for Xhci {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
log::info!("xhci irq");
let status = self.regs.handle_interrupt(0);
if status.matches_all(USBSTS::HSE::SET) {

View File

@ -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<dyn InterruptHandler>,
}
struct MsiVectorTable {
// MSI_MAX_VECTORS * vectors per row
msi_vectors: IrqSafeRwLock<Vec<Vec<MsiVector>>>,
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<dyn InterruptHandler>,
) -> 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<IrqSafeSpinlock<Vec<Arc<dyn InterruptHandler>>>>,
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<dyn InterruptHandler>,
) -> 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,
}
}