Skip to content

Commit

Permalink
Very rough MSI-X implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
moehr1z committed Dec 6, 2024
1 parent 0fa511b commit fbd71df
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 13 deletions.
2 changes: 2 additions & 0 deletions kernel/src/drivers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
pub mod fs;
#[cfg(not(feature = "pci"))]
pub mod mmio;
#[cfg(feature = "pci")]
pub mod msix;
#[cfg(any(feature = "tcp", feature = "udp"))]
pub mod net;
#[cfg(feature = "pci")]
Expand Down
68 changes: 68 additions & 0 deletions kernel/src/drivers/msix.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Stolen from Redox OS...

#[repr(u8)]
pub enum TriggerMode {
Edge = 0,
Level = 1,
}

#[repr(u8)]
pub enum LevelTriggerMode {
Deassert = 0,
Assert = 1,
}

#[repr(u8)]
pub enum DeliveryMode {
Fixed = 0b000,
LowestPriority = 0b001,
Smi = 0b010,
// 0b011 is reserved
Nmi = 0b100,
Init = 0b101,
// 0b110 is reserved
ExtInit = 0b111,
}

// TODO: should the reserved field be preserved?
pub const fn message_address(
destination_id: u8,
redirect_hint: bool,
dest_mode_logical: bool,
) -> u64 {
0x0000_0000_FEE0_0000u64
| ((destination_id as u64) << 12)
| ((redirect_hint as u64) << 3)
| ((dest_mode_logical as u64) << 2)
}
pub const fn message_data(
trigger_mode: TriggerMode,
level_trigger_mode: LevelTriggerMode,
delivery_mode: DeliveryMode,
vector: u8,
) -> u32 {
((trigger_mode as u32) << 15)
| ((level_trigger_mode as u32) << 14)
| ((delivery_mode as u32) << 8)
| vector as u32
}
pub const fn message_data_level_triggered(
level_trigger_mode: LevelTriggerMode,
delivery_mode: DeliveryMode,
vector: u8,
) -> u32 {
message_data(
TriggerMode::Level,
level_trigger_mode,
delivery_mode,
vector,
)
}
pub const fn message_data_edge_triggered(delivery_mode: DeliveryMode, vector: u8) -> u32 {
message_data(
TriggerMode::Edge,
LevelTriggerMode::Deassert,
delivery_mode,
vector,
)
}
11 changes: 3 additions & 8 deletions kernel/src/drivers/net/virtio_net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -607,14 +607,9 @@ impl NetworkDriver for VirtioNetDriver {
#[cfg(not(target_arch = "riscv64"))]
increment_irq_counter(32 + self.irq);

let result = if self.isr_stat.is_interrupt() {
true
} else if self.isr_stat.is_cfg_change() {
info!("Configuration changes are not possible! Aborting");
todo!("Implement possibility to change config on the fly...")
} else {
false
};
// With MSI-X you shouldn't check the ISR status, and since this is the handler for the
// queue we can just return true
let result = true;

self.isr_stat.acknowledge();

Expand Down
74 changes: 70 additions & 4 deletions kernel/src/drivers/net/virtio_pci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,23 @@
//! The module contains ...
use alloc::vec::Vec;
use core::ptr;
use core::str::FromStr;

use pci_types::CommandRegister;
use pci_types::{Bar, CommandRegister};
use smoltcp::phy::ChecksumCapabilities;

use crate::arch::mm::PhysAddr;
use crate::arch::pci::PciConfigRegion;
use crate::core_scheduler;
use crate::drivers::msix::*;
use crate::drivers::net::virtio_net::{CtrlQueue, NetDevCfg, RxQueues, TxQueues, VirtioNetDriver};
use crate::drivers::net::{apic, network_irqhandler, ExceptionStackFrame};
use crate::drivers::pci::PciDevice;
use crate::drivers::virtio::env::memory::*;
use crate::drivers::virtio::error::{self, VirtioError};
use crate::drivers::virtio::transport::pci;
use crate::drivers::virtio::transport::pci::{PciCap, UniCapsColl};
use crate::drivers::virtio::transport::pci::{read_msix_cap, PciCap, UniCapsColl};

/// Virtio's network device configuration structure.
/// See specification v1.1. - 5.1.4
Expand Down Expand Up @@ -180,8 +186,68 @@ impl VirtioNetDriver {
}
};

// set memory enable bit
device.set_command(CommandRegister::MEMORY_ENABLE);
// get msi-x bar
let mut cap = read_msix_cap(device);
if let Some(mut msix_cap) = cap {
let table_bar_nr = msix_cap.table_bar();
let bar = device.get_bar(table_bar_nr);

// set memory enable bit
device.set_command(CommandRegister::MEMORY_ENABLE);

match bar {
Some(Bar::Memory32 {
address,
size,
prefetchable,
}) => {
let virtual_address = VirtMemAddr::from(
crate::mm::map(
PhysAddr::from(address as u64),
size.try_into().unwrap(),
true,
true,
true,
)
.0,
);

let table_base: usize =
(virtual_address + MemOff::from(msix_cap.table_offset())).into();

// Set table entry
let lapic_id = 1;
let rh = false;
let dm = true;
let addr = message_address(lapic_id, rh, dm);

let irq = 32 + device.get_irq().unwrap();
let data = message_data_edge_triggered(DeliveryMode::Fixed, irq);

// set MSI-X table entry
let addr_lo = (table_base + 0 * 4) as *mut u32;
let addr_hi = (table_base + 1 * 4) as *mut u32;
let msg_data = (table_base + 2 * 4) as *mut u32;
let vec_ctl = (table_base + 3 * 4) as *mut u32;

unsafe {
ptr::write(addr_lo, addr as u32);
ptr::write(addr_hi, (addr >> 32) as u32);
ptr::write(msg_data, data);
ptr::write(vec_ctl, 0);
}

msix_cap.set_enabled(true, device.access());
}
_ => {
warn!("Expected 32 bit MSI-X BAR. Not using MSI-X.");
}
}
} else {
warn!("No MSI-X capability found. Not using MSI-X.");
// set memory enable bit
device.set_command(CommandRegister::MEMORY_ENABLE);
}

match drv.init_dev() {
Ok(_) => info!(
Expand Down
33 changes: 32 additions & 1 deletion kernel/src/drivers/virtio/transport/pci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use core::ptr::NonNull;
use core::sync::atomic::{fence, Ordering};
use core::{mem, ptr};

use pci_types::capability::PciCapability;
use pci_types::capability::{MsixCapability, PciCapability};
use virtio_spec::pci::{
Cap, CapCfgType, CommonCfg, CommonCfgVolatileFieldAccess, CommonCfgVolatileWideFieldAccess,
IsrStatus as IsrStatusRaw,
Expand Down Expand Up @@ -318,6 +318,11 @@ pub struct VqCfgHandler<'a> {
}

impl<'a> VqCfgHandler<'a> {
pub fn set_msix_vector(&mut self) {
self.select_queue();
self.raw.as_mut_ptr().queue_msix_vector().write(0.into());
}

// TODO: Create type for queue selected invariant to get rid of `self.select_queue()` everywhere.
fn select_queue(&mut self) {
self.raw
Expand Down Expand Up @@ -893,6 +898,32 @@ fn read_caps(
}
}

/// Return MSI-X capability
pub(crate) fn read_msix_cap(device: &PciDevice<PciConfigRegion>) -> Option<MsixCapability> {
let device_id = device.device_id();

let mut capabilities = device
.capabilities()
.unwrap()
.filter_map(|capability| match capability {
PciCapability::MsiX(capability) => Some(capability),
_ => None,
})
.collect::<Vec<_>>();

if capabilities.is_empty() {
warn!("No MSI-X capability found for device {:x}", device_id);
return None;
} else if capabilities.len() > 1 {
warn!(
"More than one MSI-X capability found for device {:x}",
device_id
);
}

return Some(capabilities.remove(0));
}

/// Maps memory areas indicated by devices BAR's into virtual address space.
fn map_bars(device: &PciDevice<PciConfigRegion>) -> Result<Vec<PciBar>, PciError> {
crate::drivers::virtio::env::pci::map_bar_mem(device)
Expand Down
2 changes: 2 additions & 0 deletions kernel/src/drivers/virtio/virtqueue/split.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,8 @@ impl Virtq for SplitVq {
None => return Err(VirtqError::QueueNotExisting(index.into())),
};

vq_handler.set_msix_vector();

let size = vq_handler.set_vq_size(size.0);
const ALLOCATOR: DeviceAlloc = DeviceAlloc;

Expand Down

0 comments on commit fbd71df

Please sign in to comment.