diff --git a/pcid/src/driver_interface/irq_helpers.rs b/pcid/src/driver_interface/irq_helpers.rs index 58d252be7a..5d380dba79 100644 --- a/pcid/src/driver_interface/irq_helpers.rs +++ b/pcid/src/driver_interface/irq_helpers.rs @@ -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 { +pub fn read_bsp_apic_id() -> io::Result { 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::()); - }) + }).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> + 'static> { + Ok(fs::read_dir("irq:")? + .filter_map(|entry| -> Option> { 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-` + 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 { /// 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)>> { +/// +/// 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)>> { + 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> { 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-/`, thus no error but just None match path_str.parse::() { 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)>> { - allocate_aligned_interrupt_vectors(NonZeroU8::new(1).unwrap(), count) +pub fn allocate_interrupt_vectors(cpu_id: usize, count: u8) -> io::Result)>> { + 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> { - let (base, mut files) = match allocate_interrupt_vectors(1) { +pub fn allocate_single_interrupt_vector(cpu_id: usize) -> io::Result> { + 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), diff --git a/xhcid/src/main.rs b/xhcid/src/main.rs index 8b9c38c026..adcad485f8 100644 --- a/xhcid/src/main.rs +++ b/xhcid/src/main.rs @@ -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);