Merge branch 'pci_interrupts_rework2' into 'master'

Various improvements to the MSI-X support

See merge request redox-os/drivers!138
This commit is contained in:
Jeremy Soller
2024-01-26 17:27:17 +00:00
12 changed files with 160 additions and 323 deletions
+3 -12
View File
@@ -6,7 +6,6 @@ extern crate spin;
extern crate syscall;
extern crate event;
use std::convert::TryFrom;
use std::usize;
use std::fs::File;
use std::io::{ErrorKind, Read, Write, Result};
@@ -17,7 +16,7 @@ use std::sync::Arc;
use event::EventQueue;
use pcid_interface::{MsiSetFeatureInfo, PcidServerHandle, PciFeature, PciFeatureInfo, SetFeatureInfo};
use pcid_interface::irq_helpers::{read_bsp_apic_id, allocate_single_interrupt_vector};
use pcid_interface::irq_helpers::{read_bsp_apic_id, allocate_single_interrupt_vector_for_msi};
use redox_log::{OutputBuilder, RedoxLogger};
pub mod hda;
@@ -91,8 +90,6 @@ fn get_int_method(pcid_handle: &mut PcidServerHandle) -> File {
}
if msi_enabled && !msix_enabled {
use pcid_interface::msi::x86_64::{DeliveryMode, self as x86_64_msix};
let capability = match pcid_handle.feature_info(PciFeature::Msi).expect("ihdad: failed to retrieve the MSI capability structure from pcid") {
PciFeatureInfo::Msi(s) => s,
PciFeatureInfo::MsiX(_) => panic!(),
@@ -103,17 +100,11 @@ fn get_int_method(pcid_handle: &mut PcidServerHandle) -> File {
// pcid_interface, so that this can be shared between nvmed, xhcid, ixgebd, etc..
let destination_id = read_bsp_apic_id().expect("ihdad: failed to read BSP apic id");
let lapic_id = u8::try_from(destination_id).expect("CPU id didn't fit inside u8");
let msg_addr = x86_64_msix::message_address(lapic_id, false, false);
let (vector, interrupt_handle) = allocate_single_interrupt_vector(destination_id).expect("ihdad: failed to allocate interrupt vector").expect("ihdad: no interrupt vectors left");
let msg_data = x86_64_msix::message_data_edge_triggered(DeliveryMode::Fixed, vector);
let (msg_addr_and_data, interrupt_handle) = allocate_single_interrupt_vector_for_msi(destination_id);
let set_feature_info = MsiSetFeatureInfo {
multi_message_enable: Some(0),
message_address: Some(msg_addr),
message_upper_address: Some(0),
message_data: Some(msg_data as u16),
message_address_and_data: Some(msg_addr_and_data),
mask_bits: None,
};
pcid_handle.set_feature_info(SetFeatureInfo::Msi(set_feature_info)).expect("ihdad: failed to set feature info");
+8 -40
View File
@@ -88,6 +88,7 @@ fn get_int_method(
PciFeatureInfo::MsiX(msix) => msix,
_ => unreachable!(),
};
capability_struct.validate(function.bars);
fn bar_base(
allocated_bars: &AllocatedBars,
function: &PciFunction,
@@ -108,22 +109,13 @@ fn get_int_method(
}
let table_bar_base: *mut u8 =
bar_base(allocated_bars, function, capability_struct.table_bir())?.as_ptr();
let pba_bar_base: *mut u8 =
bar_base(allocated_bars, function, capability_struct.pba_bir())?.as_ptr();
let table_base =
unsafe { table_bar_base.offset(capability_struct.table_offset() as isize) };
let pba_base = unsafe { pba_bar_base.offset(capability_struct.pba_offset() as isize) };
let vector_count = capability_struct.table_size();
let table_entries: &'static mut [MsixTableEntry] = unsafe {
slice::from_raw_parts_mut(table_base as *mut MsixTableEntry, vector_count as usize)
};
let pba_entries: &'static mut [Mmio<u64>] = unsafe {
slice::from_raw_parts_mut(
table_base as *mut Mmio<u64>,
(vector_count as usize + 63) / 64,
)
};
// Mask all interrupts in case some earlier driver/os already unmasked them (according to
// the PCI Local Bus spec 3.0, they are masked after system reset).
@@ -135,25 +127,14 @@ fn get_int_method(
capability_struct.set_msix_enabled(true); // only affects our local mirror of the cap
let (msix_vector_number, irq_handle) = {
use msi_x86_64::DeliveryMode;
use pcid_interface::msi::x86_64 as msi_x86_64;
let entry: &mut MsixTableEntry = &mut table_entries[0];
let bsp_cpu_id =
irq_helpers::read_bsp_apic_id().expect("nvmed: failed to read APIC ID");
let bsp_lapic_id = bsp_cpu_id
.try_into()
.expect("nvmed: BSP local apic ID couldn't fit inside u8");
let (vector, irq_handle) = irq_helpers::allocate_single_interrupt_vector(bsp_cpu_id)
.expect("nvmed: failed to allocate single MSI-X interrupt vector")
.expect("nvmed: no interrupt vectors left on BSP");
let msg_addr = msi_x86_64::message_address(bsp_lapic_id, false, false);
let msg_data = msi_x86_64::message_data_edge_triggered(DeliveryMode::Fixed, vector);
entry.set_addr_lo(msg_addr);
entry.set_msg_data(msg_data);
let (msg_addr_and_data, irq_handle) =
irq_helpers::allocate_single_interrupt_vector_for_msi(bsp_cpu_id);
entry.write_addr_and_data(msg_addr_and_data);
entry.unmask();
(0, irq_handle)
};
@@ -161,7 +142,6 @@ fn get_int_method(
let interrupt_method = InterruptMethod::MsiX(MsixCfg {
cap: capability_struct,
table: table_entries,
pba: pba_entries,
});
let interrupt_sources =
InterruptSources::MsiX(std::iter::once((msix_vector_number, irq_handle)).collect());
@@ -175,27 +155,15 @@ fn get_int_method(
};
let (msi_vector_number, irq_handle) = {
use msi_x86_64::DeliveryMode;
use pcid_interface::msi::x86_64 as msi_x86_64;
use pcid_interface::{MsiSetFeatureInfo, SetFeatureInfo};
let bsp_cpu_id =
irq_helpers::read_bsp_apic_id().expect("nvmed: failed to read BSP APIC ID");
let bsp_lapic_id = bsp_cpu_id
.try_into()
.expect("nvmed: BSP local apic ID couldn't fit inside u8");
let (vector, irq_handle) = irq_helpers::allocate_single_interrupt_vector(bsp_cpu_id)
.expect("nvmed: failed to allocate single MSI interrupt vector")
.expect("nvmed: no interrupt vectors left on BSP");
let msg_addr = msi_x86_64::message_address(bsp_lapic_id, false, false);
let msg_data =
msi_x86_64::message_data_edge_triggered(DeliveryMode::Fixed, vector) as u16;
let (msg_addr_and_data, irq_handle) =
irq_helpers::allocate_single_interrupt_vector_for_msi(bsp_cpu_id);
pcid_handle.set_feature_info(SetFeatureInfo::Msi(MsiSetFeatureInfo {
message_address: Some(msg_addr),
message_upper_address: Some(0),
message_data: Some(msg_data),
message_address_and_data: Some(msg_addr_and_data),
multi_message_enable: Some(0), // enable 2^0=1 vectors
mask_bits: None,
})).unwrap();
-1
View File
@@ -122,7 +122,6 @@ impl InterruptMethod {
pub struct MsixCfg {
pub cap: MsixCapability,
pub table: &'static mut [MsixTableEntry],
pub pba: &'static mut [Mmio<u64>],
}
#[repr(packed)]
+21
View File
@@ -8,6 +8,8 @@ use std::fs::{self, File};
use std::io::{self, prelude::*};
use std::num::NonZeroU8;
use crate::pci::msi::MsiAddrAndData;
/// Read the local APIC ID of the bootstrap processor.
pub fn read_bsp_apic_id() -> io::Result<usize> {
let mut buffer = [0u8; 8];
@@ -153,3 +155,22 @@ pub fn allocate_single_interrupt_vector(cpu_id: usize) -> io::Result<Option<(u8,
assert_eq!(files.len(), 1);
Ok(Some((base, files.pop().unwrap())))
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub fn allocate_single_interrupt_vector_for_msi(cpu_id: usize) -> (MsiAddrAndData, File) {
use crate::pci::msi::x86_64 as x86_64_msix;
// FIXME for cpu_id >255 we need to use the IOMMU to use IRQ remapping
let lapic_id = u8::try_from(cpu_id).expect("CPU id couldn't fit inside u8");
let rh = false;
let dm = false;
let addr = x86_64_msix::message_address(lapic_id, rh, dm);
let (vector, interrupt_handle) = allocate_single_interrupt_vector(cpu_id)
.expect("failed to allocate interrupt vector")
.expect("no interrupt vectors left");
let msg_data =
x86_64_msix::message_data_edge_triggered(x86_64_msix::DeliveryMode::Fixed, vector);
(MsiAddrAndData::new(addr, msg_data), interrupt_handle)
}
+4 -14
View File
@@ -163,22 +163,12 @@ pub struct MsiSetFeatureInfo {
/// is the log2 of the interrupt vectors, minus one. Can only be 0b000..=0b101.
pub multi_message_enable: Option<u8>,
/// The system-specific message address, must be DWORD aligned.
/// The system-specific message address and data.
///
/// The message address contains things like the CPU that will be targeted, at least on
/// x86_64.
pub message_address: Option<u32>,
/// The upper 32 bits of the 64-bit message address. Not guaranteed to exist, and is
/// reserved on x86_64 (currently).
pub message_upper_address: Option<u32>,
/// The message data, containing the actual interrupt vector (lower 8 bits), etc.
///
/// The spec mentions that the lower N bits can be modified, where N is the multi message
/// enable, which means that the vector set here has to be aligned to that number, and that
/// all vectors in that range have to be allocated.
pub message_data: Option<u16>,
/// x86_64. The message data contains the actual interrupt vector (lower 8 bits) and
/// the kind of interrupt, at least on x86_64.
pub message_address_and_data: Option<msi::MsiAddrAndData>,
/// A bitmap of the vectors that are masked. This field is not guaranteed (and not likely,
/// at least according to the feature flags I got from QEMU), to exist.
+12 -10
View File
@@ -14,7 +14,7 @@ use redox_log::{OutputBuilder, RedoxLogger};
use crate::cfg_access::Pcie;
use crate::config::Config;
use crate::driver_interface::LegacyInterruptLine;
use crate::pci::{PciBar, PciFunc};
use crate::pci::PciFunc;
use crate::pci::cap::Capability as PciCapability;
use crate::pci::func::{ConfigReader, ConfigWriter};
use crate::pci_header::{PciEndpointHeader, PciHeader, PciHeaderError};
@@ -130,20 +130,22 @@ impl DriverHandler {
info.set_multi_message_enable(mme);
}
if let Some(message_addr) = info_to_set.message_address {
if let Some(message_addr_and_data) = info_to_set.message_address_and_data {
let message_addr = message_addr_and_data.addr;
if message_addr & 0b11 != 0 {
return PcidClientResponse::Error(PcidServerResponseError::InvalidBitPattern);
}
info.set_message_address(message_addr);
}
if let Some(message_addr_upper) = info_to_set.message_upper_address {
info.set_message_upper_address(message_addr_upper);
}
if let Some(message_data) = info_to_set.message_data {
if message_data & ((1 << info.multi_message_enable()) - 1) != 0 {
info.set_message_address(message_addr as u32);
info.set_message_upper_address((message_addr >> 32) as u32);
if message_addr_and_data.data & ((1 << info.multi_message_enable()) - 1) != 0 {
return PcidClientResponse::Error(PcidServerResponseError::InvalidBitPattern);
}
info.set_message_data(message_data);
info.set_message_data(
message_addr_and_data
.data
.try_into()
.expect("pcid: MSI message data too big"),
);
}
if let Some(mask_bits) = info_to_set.mask_bits {
info.set_mask_bits(mask_bits);
+67 -19
View File
@@ -4,8 +4,25 @@ use super::bar::PciBar;
pub use super::cap::{MsiCapability, MsixCapability};
use super::func::{ConfigReader, ConfigWriter};
use serde::{Deserialize, Serialize};
use syscall::{Io, Mmio};
/// The address and data to use for MSI and MSI-X.
///
/// For MSI using this only works when you need a single interrupt vector.
/// For MSI-X you can have a single [MsiEntry] for each interrupt vector.
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct MsiAddrAndData {
pub(crate) addr: u64,
pub(crate) data: u32,
}
impl MsiAddrAndData {
pub fn new(addr: u64, data: u32) -> Self {
MsiAddrAndData { addr, data }
}
}
impl MsiCapability {
pub const MC_PVT_CAPABLE_BIT: u16 = 1 << 8;
pub const MC_64_BIT_ADDR_BIT: u16 = 1 << 7;
@@ -203,6 +220,45 @@ impl MsiCapability {
}
impl MsixCapability {
pub fn validate(&self, bars: [PciBar; 6]) {
if self.table_bir() > 5 {
panic!("MSI-X Table BIR contained a reserved enum value: {}", self.table_bir());
}
if self.pba_bir() > 5 {
panic!("MSI-X PBA BIR contained a reserved enum value: {}", self.pba_bir());
}
let table_size = self.table_size();
let table_offset = self.table_offset() as usize;
let table_min_length = table_size * 16;
let pba_offset = self.pba_offset() as usize;
let pba_min_length = table_size.div_ceil(8);
let (_, table_bar_size) = bars[self.table_bir() as usize].expect_mem();
let (_, pba_bar_size) = bars[self.pba_bir() as usize].expect_mem();
// Ensure that the table and PBA are within the BAR.
if !(0..table_bar_size as u64).contains(&(table_offset as u64 + table_min_length as u64)) {
panic!(
"Table {:#x}:{:#x} outside of BAR with length {:#x}",
table_offset,
table_offset + table_min_length as usize,
table_bar_size
);
}
if !(0..pba_bar_size as u64).contains(&(pba_offset as u64 + pba_min_length as u64)) {
panic!(
"PBA {:#x}:{:#x} outside of BAR with length {:#x}",
pba_offset,
pba_offset + pba_min_length as usize,
pba_bar_size
);
}
}
const MC_MSIX_ENABLED_BIT: u16 = 1 << 15;
const MC_MSIX_ENABLED_SHIFT: u8 = 15;
const MC_FUNCTION_MASK_BIT: u16 = 1 << 14;
@@ -271,20 +327,6 @@ impl MsixCapability {
}
pub fn table_base_pointer(&self, bars: [PciBar; 6]) -> usize {
if self.table_bir() > 5 {
panic!("MSI-X Table BIR contained a reserved enum value: {}", self.table_bir());
}
bars[usize::from(self.table_bir())].expect_mem().0 + self.table_offset() as usize
}
pub fn pba_base_pointer(&self, bars: [PciBar; 6]) -> usize {
if self.pba_bir() > 5 {
panic!("MSI-X PBA BIR contained a reserved enum value: {}", self.pba_bir());
}
bars[usize::from(self.pba_bir())].expect_mem().0 + self.pba_offset() as usize
}
/// Write the first DWORD into configuration space (containing the partially modifiable Message
/// Control field).
pub unsafe fn write_a<W: ConfigWriter>(&self, writer: &W, offset: u8) {
@@ -327,11 +369,11 @@ pub mod x86_64 {
}
// TODO: should the reserved field be preserved?
pub const fn message_address(destination_id: u8, rh: bool, dm: bool) -> u32 {
0xFEE0_0000u32
| ((destination_id as u32) << 12)
| ((rh as u32) << 3)
| ((dm as u32) << 2)
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)
@@ -383,6 +425,12 @@ impl MsixTableEntry {
pub fn unmask(&mut self) {
self.set_masked(false);
}
pub fn write_addr_and_data(&mut self, entry: MsiAddrAndData) {
self.set_addr_lo(entry.addr as u32);
self.set_addr_hi((entry.addr >> 32) as u32);
self.set_msg_data(entry.data);
}
}
impl fmt::Debug for MsixTableEntry {
+12 -62
View File
@@ -5,7 +5,7 @@ extern crate netutils;
extern crate syscall;
use std::cell::RefCell;
use std::convert::{TryFrom, TryInto};
use std::convert::TryInto;
use std::{env, process};
use std::fs::File;
use std::io::{ErrorKind, Read, Result, Write};
@@ -15,11 +15,10 @@ use std::sync::Arc;
use event::EventQueue;
use pcid_interface::{MsiSetFeatureInfo, PcidServerHandle, PciFeature, PciFeatureInfo, SetFeatureInfo, SubdriverArguments};
use pcid_interface::irq_helpers::{read_bsp_apic_id, allocate_single_interrupt_vector};
use pcid_interface::irq_helpers::{read_bsp_apic_id, allocate_single_interrupt_vector_for_msi};
use pcid_interface::msi::{MsixCapability, MsixTableEntry};
use redox_log::{RedoxLogger, OutputBuilder};
use syscall::{EventFlags, Packet, SchemeBlockMut};
use syscall::io::Io;
pub mod device;
@@ -81,7 +80,6 @@ where
pub struct MsixInfo {
pub virt_table_base: NonNull<MsixTableEntry>,
pub virt_pba_base: NonNull<u64>,
pub capability: MsixCapability,
}
@@ -93,18 +91,6 @@ impl MsixInfo {
assert!(k < self.capability.table_size() as usize);
unsafe { self.table_entry_pointer_unchecked(k) }
}
pub unsafe fn pba_pointer_unchecked(&mut self, k: usize) -> &mut u64 {
&mut *self.virt_pba_base.as_ptr().offset(k as isize)
}
pub fn pba_pointer(&mut self, k: usize) -> &mut u64 {
assert!(k < self.capability.table_size() as usize);
unsafe { self.pba_pointer_unchecked(k) }
}
pub fn pba(&mut self, k: usize) -> bool {
let byte = k / 64;
let bit = k % 64;
*self.pba_pointer(byte) & (1 << bit) != 0
}
}
#[cfg(target_arch = "x86_64")]
@@ -125,8 +111,6 @@ fn get_int_method(pcid_handle: &mut PcidServerHandle) -> File {
}
if msi_enabled && !msix_enabled {
use pcid_interface::msi::x86_64::{DeliveryMode, self as x86_64_msix};
let capability = match pcid_handle.feature_info(PciFeature::Msi).expect("rtl8139d: failed to retrieve the MSI capability structure from pcid") {
PciFeatureInfo::Msi(s) => s,
PciFeatureInfo::MsiX(_) => panic!(),
@@ -137,17 +121,11 @@ fn get_int_method(pcid_handle: &mut PcidServerHandle) -> File {
// pcid_interface, so that this can be shared between nvmed, xhcid, ixgebd, etc..
let destination_id = read_bsp_apic_id().expect("rtl8139d: failed to read BSP apic id");
let lapic_id = u8::try_from(destination_id).expect("CPU id didn't fit inside u8");
let msg_addr = x86_64_msix::message_address(lapic_id, false, false);
let (vector, interrupt_handle) = allocate_single_interrupt_vector(destination_id).expect("rtl8139d: failed to allocate interrupt vector").expect("rtl8139d: no interrupt vectors left");
let msg_data = x86_64_msix::message_data_edge_triggered(DeliveryMode::Fixed, vector);
let (msg_addr_and_data, interrupt_handle) = allocate_single_interrupt_vector_for_msi(destination_id);
let set_feature_info = MsiSetFeatureInfo {
multi_message_enable: Some(0),
message_address: Some(msg_addr),
message_upper_address: Some(0),
message_data: Some(msg_data as u16),
message_address_and_data: Some(msg_addr_and_data),
mask_bits: None,
};
pcid_handle.set_feature_info(SetFeatureInfo::Msi(set_feature_info)).expect("rtl8139d: failed to set feature info");
@@ -161,41 +139,21 @@ fn get_int_method(pcid_handle: &mut PcidServerHandle) -> File {
PciFeatureInfo::Msi(_) => panic!(),
PciFeatureInfo::MsiX(s) => s,
};
let table_size = capability.table_size();
let table_base = capability.table_base_pointer(pci_config.func.bars);
let table_min_length = table_size * 16;
let pba_min_length = div_round_up(table_size, 8);
capability.validate(pci_config.func.bars);
let pba_base = capability.pba_base_pointer(pci_config.func.bars);
let bar = &pci_config.func.bars[capability.table_bir() as usize];
let bar_address = unsafe { bar.physmap_mem("rtl8139d") } as usize;
let bir = capability.table_bir() as usize;
let bar = &pci_config.func.bars[bir];
let (bar_ptr, bar_size) = bar.expect_mem();
let address = unsafe { bar.physmap_mem("rtl8139d") } as usize;
if !(bar_ptr as u64..bar_ptr as u64 + bar_size as u64).contains(&(table_base as u64 + table_min_length as u64)) {
panic!("Table {:#x}{:#x} outside of BAR {:#x}:{:#x}", table_base, table_base + table_min_length as usize, bar_ptr, bar_ptr + bar_size);
}
if !(bar_ptr as u64..bar_ptr as u64 + bar_size as u64).contains(&(pba_base as u64 + pba_min_length as u64)) {
panic!("PBA {:#x}{:#x} outside of BAR {:#x}:{:#X}", pba_base, pba_base + pba_min_length as usize, bar_ptr, bar_ptr + bar_size);
}
let virt_table_base = ((table_base - bar_ptr) + address) as *mut MsixTableEntry;
let virt_pba_base = ((pba_base - bar_ptr) + address) as *mut u64;
let virt_table_base = (bar_address + capability.table_offset() as usize) as *mut MsixTableEntry;
let mut info = MsixInfo {
virt_table_base: NonNull::new(virt_table_base).unwrap(),
virt_pba_base: NonNull::new(virt_pba_base).unwrap(),
capability,
};
// Allocate one msi vector.
let method = {
use pcid_interface::msi::x86_64::{DeliveryMode, self as x86_64_msix};
// primary interrupter
let k = 0;
@@ -203,18 +161,10 @@ fn get_int_method(pcid_handle: &mut PcidServerHandle) -> File {
let table_entry_pointer = info.table_entry_pointer(k);
let destination_id = read_bsp_apic_id().expect("rtl8139d: failed to read BSP apic id");
let lapic_id = u8::try_from(destination_id).expect("rtl8139d: CPU id couldn't fit inside u8");
let rh = false;
let dm = false;
let addr = x86_64_msix::message_address(lapic_id, rh, dm);
let (vector, interrupt_handle) = allocate_single_interrupt_vector(destination_id).expect("rtl8139d: failed to allocate interrupt vector").expect("rtl8139d: no interrupt vectors left");
let msg_data = x86_64_msix::message_data_edge_triggered(DeliveryMode::Fixed, vector);
table_entry_pointer.addr_lo.write(addr);
table_entry_pointer.addr_hi.write(0);
table_entry_pointer.msg_data.write(msg_data);
table_entry_pointer.vec_ctl.writef(MsixTableEntry::VEC_CTL_MASK_BIT, false);
let (msg_addr_and_data, interrupt_handle) =
allocate_single_interrupt_vector_for_msi(destination_id);
table_entry_pointer.write_addr_and_data(msg_addr_and_data);
table_entry_pointer.unmask();
interrupt_handle
};
+12 -62
View File
@@ -3,7 +3,7 @@ extern crate netutils;
extern crate syscall;
use std::cell::RefCell;
use std::convert::{TryFrom, TryInto};
use std::convert::TryInto;
use std::{env, process};
use std::fs::File;
use std::io::{ErrorKind, Read, Result, Write};
@@ -13,11 +13,10 @@ use std::sync::Arc;
use event::EventQueue;
use pcid_interface::{MsiSetFeatureInfo, PcidServerHandle, PciFeature, PciFeatureInfo, SetFeatureInfo, SubdriverArguments};
use pcid_interface::irq_helpers::{read_bsp_apic_id, allocate_single_interrupt_vector};
use pcid_interface::irq_helpers::{read_bsp_apic_id, allocate_single_interrupt_vector_for_msi};
use pcid_interface::msi::{MsixCapability, MsixTableEntry};
use redox_log::{RedoxLogger, OutputBuilder};
use syscall::{EventFlags, Packet, SchemeBlockMut};
use syscall::io::Io;
pub mod device;
@@ -79,7 +78,6 @@ where
pub struct MsixInfo {
pub virt_table_base: NonNull<MsixTableEntry>,
pub virt_pba_base: NonNull<u64>,
pub capability: MsixCapability,
}
@@ -91,18 +89,6 @@ impl MsixInfo {
assert!(k < self.capability.table_size() as usize);
unsafe { self.table_entry_pointer_unchecked(k) }
}
pub unsafe fn pba_pointer_unchecked(&mut self, k: usize) -> &mut u64 {
&mut *self.virt_pba_base.as_ptr().offset(k as isize)
}
pub fn pba_pointer(&mut self, k: usize) -> &mut u64 {
assert!(k < self.capability.table_size() as usize);
unsafe { self.pba_pointer_unchecked(k) }
}
pub fn pba(&mut self, k: usize) -> bool {
let byte = k / 64;
let bit = k % 64;
*self.pba_pointer(byte) & (1 << bit) != 0
}
}
#[cfg(target_arch = "x86_64")]
@@ -123,8 +109,6 @@ fn get_int_method(pcid_handle: &mut PcidServerHandle) -> File {
}
if msi_enabled && !msix_enabled {
use pcid_interface::msi::x86_64::{DeliveryMode, self as x86_64_msix};
let capability = match pcid_handle.feature_info(PciFeature::Msi).expect("rtl8168d: failed to retrieve the MSI capability structure from pcid") {
PciFeatureInfo::Msi(s) => s,
PciFeatureInfo::MsiX(_) => panic!(),
@@ -135,17 +119,11 @@ fn get_int_method(pcid_handle: &mut PcidServerHandle) -> File {
// pcid_interface, so that this can be shared between nvmed, xhcid, ixgebd, etc..
let destination_id = read_bsp_apic_id().expect("rtl8168d: failed to read BSP apic id");
let lapic_id = u8::try_from(destination_id).expect("CPU id didn't fit inside u8");
let msg_addr = x86_64_msix::message_address(lapic_id, false, false);
let (vector, interrupt_handle) = allocate_single_interrupt_vector(destination_id).expect("rtl8168d: failed to allocate interrupt vector").expect("rtl8168d: no interrupt vectors left");
let msg_data = x86_64_msix::message_data_edge_triggered(DeliveryMode::Fixed, vector);
let (msg_addr_and_data, interrupt_handle) = allocate_single_interrupt_vector_for_msi(destination_id);
let set_feature_info = MsiSetFeatureInfo {
multi_message_enable: Some(0),
message_address: Some(msg_addr),
message_upper_address: Some(0),
message_data: Some(msg_data as u16),
message_address_and_data: Some(msg_addr_and_data),
mask_bits: None,
};
pcid_handle.set_feature_info(SetFeatureInfo::Msi(set_feature_info)).expect("rtl8168d: failed to set feature info");
@@ -159,41 +137,21 @@ fn get_int_method(pcid_handle: &mut PcidServerHandle) -> File {
PciFeatureInfo::Msi(_) => panic!(),
PciFeatureInfo::MsiX(s) => s,
};
let table_size = capability.table_size();
let table_base = capability.table_base_pointer(pci_config.func.bars);
let table_min_length = table_size * 16;
let pba_min_length = div_round_up(table_size, 8);
capability.validate(pci_config.func.bars);
let pba_base = capability.pba_base_pointer(pci_config.func.bars);
let bar = &pci_config.func.bars[capability.table_bir() as usize];
let bar_address = unsafe { bar.physmap_mem("rtl8168d") } as usize;
let bir = capability.table_bir() as usize;
let bar = &pci_config.func.bars[bir];
let (bar_ptr, bar_size) = bar.expect_mem();
let address = unsafe { bar.physmap_mem("rtl8168d") } as usize;
if !(bar_ptr as u64..bar_ptr as u64 + bar_size as u64).contains(&(table_base as u64 + table_min_length as u64)) {
panic!("Table {:#x}{:#x} outside of BAR {:#x}:{:#x}", table_base, table_base + table_min_length as usize, bar_ptr, bar_ptr + bar_size);
}
if !(bar_ptr as u64..bar_ptr as u64 + bar_size as u64).contains(&(pba_base as u64 + pba_min_length as u64)) {
panic!("PBA {:#x}{:#x} outside of BAR {:#x}:{:#X}", pba_base, pba_base + pba_min_length as usize, bar_ptr, bar_ptr + bar_size);
}
let virt_table_base = ((table_base - bar_ptr) + address) as *mut MsixTableEntry;
let virt_pba_base = ((pba_base - bar_ptr) + address) as *mut u64;
let virt_table_base = (bar_address + capability.table_offset() as usize) as *mut MsixTableEntry;
let mut info = MsixInfo {
virt_table_base: NonNull::new(virt_table_base).unwrap(),
virt_pba_base: NonNull::new(virt_pba_base).unwrap(),
capability,
};
// Allocate one msi vector.
let method = {
use pcid_interface::msi::x86_64::{DeliveryMode, self as x86_64_msix};
// primary interrupter
let k = 0;
@@ -201,18 +159,10 @@ fn get_int_method(pcid_handle: &mut PcidServerHandle) -> File {
let table_entry_pointer = info.table_entry_pointer(k);
let destination_id = read_bsp_apic_id().expect("rtl8168d: failed to read BSP apic id");
let lapic_id = u8::try_from(destination_id).expect("rtl8168d: CPU id couldn't fit inside u8");
let rh = false;
let dm = false;
let addr = x86_64_msix::message_address(lapic_id, rh, dm);
let (vector, interrupt_handle) = allocate_single_interrupt_vector(destination_id).expect("rtl8168d: failed to allocate interrupt vector").expect("rtl8168d: no interrupt vectors left");
let msg_data = x86_64_msix::message_data_edge_triggered(DeliveryMode::Fixed, vector);
table_entry_pointer.addr_lo.write(addr);
table_entry_pointer.addr_hi.write(0);
table_entry_pointer.msg_data.write(msg_data);
table_entry_pointer.vec_ctl.writef(MsixTableEntry::VEC_CTL_MASK_BIT, false);
let (msg_addr_and_data, interrupt_handle) =
allocate_single_interrupt_vector_for_msi(destination_id);
table_entry_pointer.write_addr_and_data(msg_addr_and_data);
table_entry_pointer.unmask();
interrupt_handle
};
+10 -44
View File
@@ -1,11 +1,9 @@
use crate::{legacy_transport::LegacyTransport, reinit, transport::Error, Device};
use pcid_interface::irq_helpers::{allocate_single_interrupt_vector, read_bsp_apic_id};
use pcid_interface::msi::{self, MsixTableEntry};
use pcid_interface::irq_helpers::{allocate_single_interrupt_vector_for_msi, read_bsp_apic_id};
use pcid_interface::msi::MsixTableEntry;
use std::{fs::File, ptr::NonNull};
use syscall::Io;
use crate::{probe::MsixInfo, MSIX_PRIMARY_VECTOR};
use pcid_interface::*;
@@ -18,28 +16,11 @@ pub fn enable_msix(pcid_handle: &mut PcidServerHandle) -> Result<File, Error> {
PciFeatureInfo::MsiX(capability) => capability,
_ => unreachable!(),
};
capability.validate(pci_config.func.bars);
let table_size = capability.table_size();
let table_base = capability.table_base_pointer(pci_config.func.bars);
let table_min_length = table_size * 16;
let pba_min_length = table_size.div_ceil(8);
let pba_base = capability.pba_base_pointer(pci_config.func.bars);
let bir = capability.table_bir() as usize;
let bar = &pci_config.func.bars[bir];
let (bar_ptr, bar_size) = bar.expect_mem();
let address = unsafe { bar.physmap_mem("virtio-core") } as usize;
// Ensure that the table and PBA are be within the BAR.
{
let bar_range = bar_ptr as u64..bar_ptr as u64 + bar_size as u64;
assert!(bar_range.contains(&(table_base as u64 + table_min_length as u64)));
assert!(bar_range.contains(&(pba_base as u64 + pba_min_length as u64)));
}
let virt_table_base = ((table_base - bar_ptr as usize) + address) as *mut MsixTableEntry;
let bar = &pci_config.func.bars[capability.table_bir() as usize];
let bar_address = unsafe { bar.physmap_mem("virtio-core") } as usize;
let virt_table_base = (bar_address + capability.table_offset() as usize) as *mut MsixTableEntry;
let mut info = MsixInfo {
virt_table_base: NonNull::new(virt_table_base).unwrap(),
@@ -53,25 +34,10 @@ pub fn enable_msix(pcid_handle: &mut PcidServerHandle) -> Result<File, Error> {
let table_entry_pointer = info.table_entry_pointer(MSIX_PRIMARY_VECTOR as usize);
let destination_id = read_bsp_apic_id().expect("virtio_core: `read_bsp_apic_id()` failed");
let lapic_id = u8::try_from(destination_id).unwrap();
let rh = false;
let dm = false;
let addr = msi::x86_64::message_address(lapic_id, rh, dm);
let (vector, interrupt_handle) = allocate_single_interrupt_vector(destination_id)
.unwrap()
.expect("virtio_core: interrupt vector exhaustion");
let msg_data =
msi::x86_64::message_data_edge_triggered(msi::x86_64::DeliveryMode::Fixed, vector);
table_entry_pointer.addr_lo.write(addr);
table_entry_pointer.addr_hi.write(0);
table_entry_pointer.msg_data.write(msg_data);
table_entry_pointer
.vec_ctl
.writef(MsixTableEntry::VEC_CTL_MASK_BIT, false);
let (msg_addr_and_data, interrupt_handle) =
allocate_single_interrupt_vector_for_msi(destination_id);
table_entry_pointer.write_addr_and_data(msg_addr_and_data);
table_entry_pointer.unmask();
interrupt_handle
};
+11 -46
View File
@@ -12,8 +12,8 @@ use std::sync::{Arc, Mutex};
use std::env;
use pcid_interface::{MsiSetFeatureInfo, PcidServerHandle, PciFeature, PciFeatureInfo, SetFeatureInfo};
use pcid_interface::irq_helpers::{read_bsp_apic_id, allocate_single_interrupt_vector};
use pcid_interface::msi::{MsiCapability, MsixCapability, MsixTableEntry};
use pcid_interface::irq_helpers::{read_bsp_apic_id, allocate_single_interrupt_vector_for_msi};
use pcid_interface::msi::MsixTableEntry;
use event::{Event, EventQueue};
use redox_log::{RedoxLogger, OutputBuilder};
@@ -82,11 +82,9 @@ fn setup_logging(name: &str) -> Option<&'static RedoxLogger> {
}
#[cfg(target_arch = "x86_64")]
fn get_int_method(pcid_handle: &mut PcidServerHandle, address: usize) -> (Option<File>, InterruptMethod) {
fn get_int_method(pcid_handle: &mut PcidServerHandle, bar0_address: usize) -> (Option<File>, InterruptMethod) {
let pci_config = pcid_handle.fetch_config().expect("xhcid: failed to fetch config");
let (bar_ptr, bar_size) = pci_config.func.bars[0].expect_mem();
let all_pci_features = pcid_handle.fetch_all_features().expect("xhcid: failed to fetch pci features");
log::debug!("XHCI PCI FEATURES: {:?}", all_pci_features);
@@ -101,8 +99,6 @@ fn get_int_method(pcid_handle: &mut PcidServerHandle, address: usize) -> (Option
}
if msi_enabled && !msix_enabled {
use pcid_interface::msi::x86_64::{DeliveryMode, self as x86_64_msix};
let mut capability = match pcid_handle.feature_info(PciFeature::Msi).expect("xhcid: failed to retrieve the MSI capability structure from pcid") {
PciFeatureInfo::Msi(s) => s,
PciFeatureInfo::MsiX(_) => panic!(),
@@ -113,17 +109,11 @@ fn get_int_method(pcid_handle: &mut PcidServerHandle, address: usize) -> (Option
// pcid_interface, so that this can be shared between nvmed, xhcid, ixgebd, etc..
let destination_id = read_bsp_apic_id().expect("xhcid: failed to read BSP apic id");
let lapic_id = u8::try_from(destination_id).expect("CPU id didn't fit inside u8");
let msg_addr = x86_64_msix::message_address(lapic_id, false, false);
let (vector, interrupt_handle) = allocate_single_interrupt_vector(destination_id).expect("xhcid: failed to allocate interrupt vector").expect("xhcid: no interrupt vectors left");
let msg_data = x86_64_msix::message_data_edge_triggered(DeliveryMode::Fixed, vector);
let (msg_addr_and_data, interrupt_handle) = allocate_single_interrupt_vector_for_msi(destination_id);
let set_feature_info = MsiSetFeatureInfo {
multi_message_enable: Some(0),
message_address: Some(msg_addr),
message_upper_address: Some(0),
message_data: Some(msg_data as u16),
message_address_and_data: Some(msg_addr_and_data),
mask_bits: None,
};
pcid_handle.set_feature_info(SetFeatureInfo::Msi(set_feature_info)).expect("xhcid: failed to set feature info");
@@ -137,35 +127,19 @@ fn get_int_method(pcid_handle: &mut PcidServerHandle, address: usize) -> (Option
PciFeatureInfo::Msi(_) => panic!(),
PciFeatureInfo::MsiX(s) => s,
};
let table_size = capability.table_size();
let table_base = capability.table_base_pointer(pci_config.func.bars);
let table_min_length = table_size * 16;
let pba_min_length = crate::xhci::scheme::div_round_up(table_size, 8);
capability.validate(pci_config.func.bars);
let pba_base = capability.pba_base_pointer(pci_config.func.bars);
if !(bar_ptr as u64..bar_ptr as u64 + bar_size as u64).contains(&(table_base as u64 + table_min_length as u64)) {
panic!("Table {:#x}{:#x} outside of BAR {:#x}:{:#x}", table_base, table_base + table_min_length as usize, bar_ptr, bar_ptr + bar_size);
}
if !(bar_ptr as u64..bar_ptr as u64 + bar_size as u64).contains(&(pba_base as u64 + pba_min_length as u64)) {
panic!("PBA {:#x}{:#x} outside of BAR {:#x}:{:#X}", pba_base, pba_base + pba_min_length as usize, bar_ptr, bar_ptr + bar_size);
}
let virt_table_base = ((table_base - bar_ptr as usize) + address) as *mut MsixTableEntry;
let virt_pba_base = ((pba_base - bar_ptr as usize) + address) as *mut u64;
assert_eq!(capability.table_bir(), 0);
let virt_table_base = (bar0_address + capability.table_offset() as usize) as *mut MsixTableEntry;
let mut info = xhci::MsixInfo {
virt_table_base: NonNull::new(virt_table_base).unwrap(),
virt_pba_base: NonNull::new(virt_pba_base).unwrap(),
capability,
};
// Allocate one msi vector.
let method = {
use pcid_interface::msi::x86_64::{DeliveryMode, self as x86_64_msix};
// primary interrupter
let k = 0;
@@ -173,18 +147,9 @@ fn get_int_method(pcid_handle: &mut PcidServerHandle, address: usize) -> (Option
let table_entry_pointer = info.table_entry_pointer(k);
let destination_id = read_bsp_apic_id().expect("xhcid: failed to read BSP apic id");
let lapic_id = u8::try_from(destination_id).expect("xhcid: CPU id couldn't fit inside u8");
let rh = false;
let dm = false;
let addr = x86_64_msix::message_address(lapic_id, rh, dm);
let (vector, interrupt_handle) = allocate_single_interrupt_vector(destination_id).expect("xhcid: failed to allocate interrupt vector").expect("xhcid: no interrupt vectors left");
let msg_data = x86_64_msix::message_data_edge_triggered(DeliveryMode::Fixed, vector);
table_entry_pointer.addr_lo.write(addr);
table_entry_pointer.addr_hi.write(0);
table_entry_pointer.msg_data.write(msg_data);
table_entry_pointer.vec_ctl.writef(MsixTableEntry::VEC_CTL_MASK_BIT, false);
let (msg_addr_and_data, interrupt_handle) = allocate_single_interrupt_vector_for_msi(destination_id);
table_entry_pointer.write_addr_and_data(msg_addr_and_data);
table_entry_pointer.unmask();
(Some(interrupt_handle), InterruptMethod::MsiX(Mutex::new(info)))
};
-13
View File
@@ -70,7 +70,6 @@ pub enum InterruptMethod {
pub struct MsixInfo {
pub virt_table_base: NonNull<MsixTableEntry>,
pub virt_pba_base: NonNull<u64>,
pub capability: MsixCapability,
}
impl MsixInfo {
@@ -81,18 +80,6 @@ impl MsixInfo {
assert!(k < self.capability.table_size() as usize);
unsafe { self.table_entry_pointer_unchecked(k) }
}
pub unsafe fn pba_pointer_unchecked(&mut self, k: usize) -> &mut u64 {
&mut *self.virt_pba_base.as_ptr().offset(k as isize)
}
pub fn pba_pointer(&mut self, k: usize) -> &mut u64 {
assert!(k < self.capability.table_size() as usize);
unsafe { self.pba_pointer_unchecked(k) }
}
pub fn pba(&mut self, k: usize) -> bool {
let byte = k / 64;
let bit = k % 64;
*self.pba_pointer(byte) & (1 << bit) != 0
}
}
impl Xhci {