Allow per-cpu IRQ allocation.
This commit is contained in:
@@ -3,25 +3,45 @@
|
||||
//! This module allows easy handling of the `irq:` scheme, and allocating interrupt vectors for use
|
||||
//! by INTx#, MSI, or MSI-X.
|
||||
|
||||
use std::convert::TryFrom;
|
||||
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> {
|
||||
pub fn read_bsp_apic_id() -> io::Result<usize> {
|
||||
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
|
||||
(if bytes_read == 8 {
|
||||
usize::try_from(u64::from_le_bytes(buffer))
|
||||
} else if bytes_read == 4 {
|
||||
u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]])
|
||||
usize::try_from(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>());
|
||||
})
|
||||
}).or(Err(io::Error::new(io::ErrorKind::InvalidData, "bad BSP int size")))
|
||||
}
|
||||
|
||||
// TODO: Perhaps read the MADT instead?
|
||||
/// Obtains an interator over all of the visible CPU ids, for use in IRQ allocation and MSI
|
||||
/// capability structs or MSI-X tables.
|
||||
pub fn cpu_ids() -> io::Result<impl Iterator<Item = io::Result<usize>> + 'static> {
|
||||
Ok(fs::read_dir("irq:")?
|
||||
.filter_map(|entry| -> Option<io::Result<_>> { match entry {
|
||||
Ok(e) => {
|
||||
let path = e.path();
|
||||
let file_name = path.file_name()?.to_str()?;
|
||||
// the file name should be in the format `cpu-<CPU ID>`
|
||||
if ! file_name.starts_with("cpu-") {
|
||||
return None;
|
||||
}
|
||||
u8::from_str_radix(&file_name[4..], 16).map(usize::from).map(Ok).ok()
|
||||
}
|
||||
Err(e) => Some(Err(e)),
|
||||
} }))
|
||||
}
|
||||
|
||||
/// Allocate multiple interrupt vectors, from the IDT of the bootstrap processor, returning the
|
||||
@@ -41,10 +61,13 @@ pub fn read_bsp_apic_id() -> io::Result<u32> {
|
||||
/// 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>)>> {
|
||||
///
|
||||
/// Every interrupt vector will be allocated in the same CPU's IDT, as specified by `cpu_id`.
|
||||
pub fn allocate_aligned_interrupt_vectors(cpu_id: usize, alignment: NonZeroU8, count: u8) -> io::Result<Option<(u8, Vec<File>)>> {
|
||||
let cpu_id = u8::try_from(cpu_id).expect("usize cpu ids not implemented yet");
|
||||
if count == 0 { return Ok(None) }
|
||||
|
||||
let available_irqs = fs::read_dir("irq:")?;
|
||||
let available_irqs = fs::read_dir(format!("irq:{:02x}", cpu_id))?;
|
||||
let mut available_irq_numbers = available_irqs.filter_map(|entry| -> Option<io::Result<_>> {
|
||||
let entry = match entry {
|
||||
Ok(e) => e,
|
||||
@@ -63,8 +86,6 @@ pub fn allocate_aligned_interrupt_vectors(alignment: NonZeroU8, count: u8) -> io
|
||||
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,
|
||||
@@ -119,15 +140,15 @@ pub fn allocate_aligned_interrupt_vectors(alignment: NonZeroU8, count: u8) -> io
|
||||
|
||||
/// 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)
|
||||
pub fn allocate_interrupt_vectors(cpu_id: usize, count: u8) -> io::Result<Option<(u8, Vec<File>)>> {
|
||||
allocate_aligned_interrupt_vectors(cpu_id, 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) {
|
||||
pub fn allocate_single_interrupt_vector(cpu_id: usize) -> io::Result<Option<(u8, File)>> {
|
||||
let (base, mut files) = match allocate_interrupt_vectors(cpu_id, 1) {
|
||||
Ok(Some((base, files))) => (base, files),
|
||||
Ok(None) => return Ok(None),
|
||||
Err(err) => return Err(err),
|
||||
|
||||
+8
-6
@@ -1,7 +1,7 @@
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::fs::{self, File};
|
||||
use std::future::Future;
|
||||
use std::io::{self, Read, Write};
|
||||
@@ -52,7 +52,7 @@ fn main() {
|
||||
Ok(logger) => match logger.with_stdout_mirror().enable() {
|
||||
Ok(_) => {
|
||||
println!("xhcid: enabled logger");
|
||||
log::set_max_level(log::LevelFilter::Debug);
|
||||
log::set_max_level(log::LevelFilter::Trace);
|
||||
}
|
||||
Err(error) => eprintln!("xhcid: failed to set default logger: {}", error),
|
||||
}
|
||||
@@ -102,9 +102,10 @@ fn main() {
|
||||
// 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 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, 0b00);
|
||||
|
||||
let (vector, interrupt_handle) = allocate_single_interrupt_vector().expect("xhcid: failed to allocate interrupt vector").expect("xhcid: no interrupt vectors left");
|
||||
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 set_feature_info = MsiSetFeatureInfo {
|
||||
@@ -161,11 +162,12 @@ fn main() {
|
||||
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(destination_id.try_into().expect("xhcid: BSP apic id couldn't fit u8"), rh, dm, 0b00);
|
||||
let addr = x86_64_msix::message_address(lapic_id, rh, dm, 0b00);
|
||||
|
||||
let (vector, interrupt_handle) = allocate_single_interrupt_vector().expect("xhcid: failed to allocate interrupt vector").expect("xhcid: no interrupt vectors left");
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user