Various fixes.
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
//! IRQ helpers.
|
||||
//!
|
||||
//! This module allows easy handling of the `irq:` scheme, and allocating interrupt vectors for use
|
||||
//! by INTx#, MSI, or MSI-X.
|
||||
|
||||
use std::fs::{self, File};
|
||||
use std::io::{self, prelude::*};
|
||||
use std::num::NonZeroU8;
|
||||
use std::ops;
|
||||
|
||||
/// Read the local APIC ID of the bootstrap processor.
|
||||
pub fn read_bsp_apic_id() -> io::Result<u32> {
|
||||
let mut buffer = [0u8; 8];
|
||||
|
||||
let mut file = File::open("irq:bsp")?;
|
||||
let bytes_read = file.read(&mut buffer)?;
|
||||
|
||||
Ok(if bytes_read == 8 {
|
||||
u64::from_le_bytes(buffer) as u32
|
||||
} else if bytes_read == 4 {
|
||||
u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]])
|
||||
} else {
|
||||
panic!("`irq:` scheme responded with {} bytes, expected {}", bytes_read, std::mem::size_of::<usize>());
|
||||
})
|
||||
}
|
||||
|
||||
/// Allocate multiple interrupt vectors, from the IDT of the bootstrap processor, returning the
|
||||
/// start vector and the IRQ handles.
|
||||
///
|
||||
/// The alignment is a requirement for the allocation range. For example, with an alignment of 8,
|
||||
/// only ranges that begin with a multiple of eight are accepted. The IRQ handles returned will
|
||||
/// always correspond to the subsequent IRQ numbers beginning the first value in the return tuple.
|
||||
///
|
||||
/// This function is not actually guaranteed to allocate all of the IRQs specified in `count`,
|
||||
/// since another process might already have requested that vector. The caller must check that
|
||||
/// the returned vector have the same length as `count`. In the future this function may perhaps
|
||||
/// lock the entire directory to prevent this from happening, or maybe find the smallest free range
|
||||
/// with the minimum alignment, to allow other drivers to obtain their necessary IRQs.
|
||||
///
|
||||
/// Note that this count/alignment restriction is only mandatory for MSI; MSI-X allows for
|
||||
/// individually allocated vectors that might be spread out, even on multiple CPUs. Thus, multiple
|
||||
/// invocations with alignment 1 and count 1 are totally acceptable, although allocating in bulk
|
||||
/// minimizes the initialization overhead, even though it's negligible.
|
||||
pub fn allocate_aligned_interrupt_vectors(alignment: NonZeroU8, count: u8) -> io::Result<Option<(u8, Vec<File>)>> {
|
||||
if count == 0 { return Ok(None) }
|
||||
|
||||
let available_irqs = fs::read_dir("irq:")?;
|
||||
let mut available_irq_numbers = available_irqs.filter_map(|entry| -> Option<io::Result<_>> {
|
||||
let entry = match entry {
|
||||
Ok(e) => e,
|
||||
Err(err) => return Some(Err(err)),
|
||||
};
|
||||
|
||||
let path = entry.path();
|
||||
|
||||
let file_name = match path.file_name() {
|
||||
Some(f) => f,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let path_str = match file_name.to_str() {
|
||||
Some(s) => s,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
// note that there might be future subdirectories in the IRQ scheme, such as `cpu-<APIC
|
||||
// ID>/<IRQ>`, thus no error but just None
|
||||
match path_str.parse::<u8>() {
|
||||
Ok(p) => Some(Ok(p)),
|
||||
Err(_) => None,
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: fcntl F_SETLK on `irq:/`?
|
||||
|
||||
let mut handles = Vec::with_capacity(usize::from(count));
|
||||
|
||||
let mut index = 0;
|
||||
let mut first = None;
|
||||
|
||||
while let Some(number) = available_irq_numbers.next() {
|
||||
let number = number?;
|
||||
|
||||
// Skip until a suitable alignment is found.
|
||||
if number % u8::from(alignment) != 0 {
|
||||
continue;
|
||||
}
|
||||
let first = *first.get_or_insert(number);
|
||||
let irq_number = first + index;
|
||||
|
||||
// From the point where the range is aligned, we can start to advance until `count` IRQs
|
||||
// have been allocated.
|
||||
if index >= count {
|
||||
break;
|
||||
}
|
||||
|
||||
// if found, reserve the irq
|
||||
let irq_handle = match File::create(format!("irq:{}", irq_number)) {
|
||||
Ok(handle) => handle,
|
||||
|
||||
// return early if the entire range couldn't be allocated
|
||||
Err(err) if err.kind() == io::ErrorKind::NotFound => break,
|
||||
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
handles.push(irq_handle);
|
||||
index += 1;
|
||||
}
|
||||
if handles.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
let first = match first {
|
||||
Some(f) => f,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
Ok(Some((first + 32, handles)))
|
||||
}
|
||||
|
||||
/// Allocate at most `count` interrupt vectors, which can start at any offset. Unless MSI is used
|
||||
/// and an entire aligned range of vectors is needed, this function should be used.
|
||||
pub fn allocate_interrupt_vectors(count: u8) -> io::Result<Option<(u8, Vec<File>)>> {
|
||||
allocate_aligned_interrupt_vectors(NonZeroU8::new(1).unwrap(), count)
|
||||
}
|
||||
|
||||
/// Allocate a single interrupt vector, returning both the vector number (starting from 32 up to
|
||||
/// 254), and its IRQ handle which is then reserved. Returns Ok(None) if allocation fails due to
|
||||
/// no available IRQs.
|
||||
pub fn allocate_single_interrupt_vector() -> io::Result<Option<(u8, File)>> {
|
||||
let (base, mut files) = match allocate_interrupt_vectors(1) {
|
||||
Ok(Some((base, files))) => (base, files),
|
||||
Ok(None) => return Ok(None),
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
assert_eq!(files.len(), 1);
|
||||
Ok(Some((base, files.pop().unwrap())))
|
||||
}
|
||||
@@ -10,6 +10,8 @@ use thiserror::Error;
|
||||
pub use crate::pci::PciBar;
|
||||
pub use crate::pci::msi;
|
||||
|
||||
pub mod irq_helpers;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
#[repr(u8)]
|
||||
pub enum LegacyInterruptPin {
|
||||
@@ -118,7 +120,7 @@ pub type Result<T, E = PcidClientHandleError> = std::result::Result<T, E>;
|
||||
|
||||
// TODO: Remove these "features" and just go strait to the actual thing.
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub struct MsiSetFeatureInfo {
|
||||
/// The Multi Message Enable field of the Message Control in the MSI Capability Structure,
|
||||
/// is the log2 of the interrupt vectors, minus one. Can only be 0b000..=0b101.
|
||||
@@ -273,4 +275,11 @@ impl PcidServerHandle {
|
||||
other => Err(PcidClientHandleError::InvalidResponse(other)),
|
||||
}
|
||||
}
|
||||
pub fn set_feature_info(&mut self, info: SetFeatureInfo) -> Result<()> {
|
||||
self.send(&PcidClientRequest::SetFeatureInfo(info))?;
|
||||
match self.recv()? {
|
||||
PcidClientResponse::SetFeatureInfo(_) => Ok(()),
|
||||
other => Err(PcidClientHandleError::InvalidResponse(other)),
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -195,7 +195,7 @@ impl State {
|
||||
|
||||
fn handle_parsed_header(state: Arc<State>, config: &Config, bus_num: u8,
|
||||
dev_num: u8, func_num: u8, header: PciHeader) {
|
||||
let pci = &state.pci;
|
||||
let pci = state.preferred_cfg_access();
|
||||
|
||||
let raw_class: u8 = header.class().into();
|
||||
let mut string = format!("PCI {:>02X}/{:>02X}/{:>02X} {:>04X}:{:>04X} {:>02X}.{:>02X}.{:>02X}.{:>02X} {:?}",
|
||||
|
||||
+35
-57
@@ -11,7 +11,8 @@ use std::ptr::NonNull;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::env;
|
||||
|
||||
use pcid_interface::{PcidServerHandle, PciFeature, PciFeatureInfo};
|
||||
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 event::{Event, EventQueue};
|
||||
@@ -32,49 +33,6 @@ pub mod driver_interface;
|
||||
mod usb;
|
||||
mod xhci;
|
||||
|
||||
/// Read the local APIC id of the bootstrap processor.
|
||||
fn read_bsp_apic_id() -> io::Result<u32> {
|
||||
let mut buffer = [0u8; 8];
|
||||
|
||||
let mut file = File::open("irq:bsp")?;
|
||||
let bytes_read = file.read(&mut buffer)?;
|
||||
|
||||
Ok(if bytes_read == 8 {
|
||||
u64::from_le_bytes(buffer) as u32
|
||||
} else if bytes_read == 4 {
|
||||
u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]])
|
||||
} else {
|
||||
panic!("`irq:` scheme responded with {} bytes, expected {}", bytes_read, std::mem::size_of::<usize>());
|
||||
})
|
||||
}
|
||||
/// Allocate an interrupt vector, located at the BSP's IDT.
|
||||
fn allocate_interrupt_vector() -> io::Result<Option<(u8, File)>> {
|
||||
let available_irqs = fs::read_dir("irq:")?;
|
||||
|
||||
for entry in available_irqs {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
|
||||
let file_name = match path.file_name() {
|
||||
Some(f) => f,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let path_str = match file_name.to_str() {
|
||||
Some(s) => s,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
if let Ok(irq_number) = path_str.parse::<u8>() {
|
||||
// if found, reserve the irq
|
||||
let irq_handle = File::create(format!("irq:{}", irq_number))?;
|
||||
let interrupt_vector = irq_number + 32;
|
||||
return Ok(Some((interrupt_vector, irq_handle)));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn handle_packet(hci: Arc<Xhci>, packet: Packet) -> Packet {
|
||||
todo!()
|
||||
}
|
||||
@@ -124,29 +82,44 @@ fn main() {
|
||||
let (has_msi, mut msi_enabled) = all_pci_features.iter().map(|(feature, status)| (feature.is_msi(), status.is_enabled())).find(|&(f, _)| f).unwrap_or((false, false));
|
||||
let (has_msix, mut msix_enabled) = all_pci_features.iter().map(|(feature, status)| (feature.is_msix(), status.is_enabled())).find(|&(f, _)| f).unwrap_or((false, false));
|
||||
|
||||
dbg!(has_msi, msi_enabled);
|
||||
dbg!(has_msix, msix_enabled);
|
||||
|
||||
if has_msi && !msi_enabled && !has_msix {
|
||||
pcid_handle.enable_feature(PciFeature::Msi).expect("xhcid: failed to enable MSI");
|
||||
info!("Enabled MSI");
|
||||
msi_enabled = true;
|
||||
}
|
||||
if has_msix && !msix_enabled {
|
||||
pcid_handle.enable_feature(PciFeature::MsiX).expect("xhcid: failed to enable MSI-X");
|
||||
info!("Enabled MSI-X");
|
||||
msix_enabled = true;
|
||||
}
|
||||
|
||||
let (mut irq_file, interrupt_method) = 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::MsiX).expect("xhcid: failed to retrieve the MSI capability structure from pcid") {
|
||||
PciFeatureInfo::Msi(s) => s,
|
||||
PciFeatureInfo::MsiX(_) => panic!(),
|
||||
};
|
||||
// use one vector
|
||||
capability.set_multi_message_enable(0);
|
||||
// TODO: Allow allocation of up to 32 vectors.
|
||||
|
||||
todo!("msi (msix is implemented though)")
|
||||
// TODO: Find a way to abstract this away, potantially as a helper module for
|
||||
// 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 msg_addr = x86_64_msix::message_address(destination_id as u8, false, false, 0b00);
|
||||
|
||||
let (vector, interrupt_handle) = allocate_single_interrupt_vector().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 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),
|
||||
mask_bits: None,
|
||||
};
|
||||
pcid_handle.set_feature_info(SetFeatureInfo::Msi(set_feature_info)).expect("xhcid: failed to set feature info");
|
||||
|
||||
pcid_handle.enable_feature(PciFeature::Msi).expect("xhcid: failed to enable MSI");
|
||||
info!("Enabled MSI");
|
||||
|
||||
(Some(interrupt_handle), InterruptMethod::Msi)
|
||||
} else if msix_enabled {
|
||||
let capability = match pcid_handle.feature_info(PciFeature::MsiX).expect("xhcid: failed to retrieve the MSI-X capability structure from pcid") {
|
||||
PciFeatureInfo::Msi(_) => panic!(),
|
||||
@@ -178,7 +151,7 @@ fn main() {
|
||||
|
||||
// Allocate one msi vector.
|
||||
|
||||
{
|
||||
let method = {
|
||||
use pcid_interface::msi::x86_64::{DeliveryMode, self as x86_64_msix};
|
||||
|
||||
// primary interrupter
|
||||
@@ -192,7 +165,7 @@ fn main() {
|
||||
let dm = false;
|
||||
let addr = x86_64_msix::message_address(destination_id.try_into().expect("xhcid: BSP apic id couldn't fit u8"), rh, dm, 0b00);
|
||||
|
||||
let (vector, interrupt_handle) = allocate_interrupt_vector().expect("xhcid: failed to allocate interrupt vector").expect("xhcid: no interrupt vectors left");
|
||||
let (vector, interrupt_handle) = allocate_single_interrupt_vector().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);
|
||||
@@ -201,7 +174,12 @@ fn main() {
|
||||
table_entry_pointer.vec_ctl.writef(MsixTableEntry::VEC_CTL_MASK_BIT, false);
|
||||
|
||||
(Some(interrupt_handle), InterruptMethod::MsiX(Mutex::new(info)))
|
||||
}
|
||||
};
|
||||
|
||||
pcid_handle.enable_feature(PciFeature::MsiX).expect("xhcid: failed to enable MSI-X");
|
||||
info!("Enabled MSI-X");
|
||||
|
||||
method
|
||||
} else if pci_config.func.legacy_interrupt_pin.is_some() {
|
||||
// legacy INTx# interrupt pins.
|
||||
(Some(File::open(format!("irq:{}", irq)).expect("xhcid: failed to open legacy IRQ file")), InterruptMethod::Intx)
|
||||
|
||||
Reference in New Issue
Block a user