From cb5a72fbc100cca943f7995b96311f835f019b6f Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 20 Mar 2020 18:50:39 +0100 Subject: [PATCH] Successfully recognize (only one) MSI-X IRQ. Somehow it only happens once though. --- Cargo.lock | 2 +- pcid/Cargo.toml | 2 +- pcid/src/driver_interface.rs | 6 +- pcid/src/pci/msi.rs | 48 +++++++++++++ xhcid/src/main.rs | 126 +++++++++++++++++++++++++++-------- xhcid/src/xhci/command.rs | 3 +- xhcid/src/xhci/executor.rs | 1 + xhcid/src/xhci/mod.rs | 76 +++++++++++++++++---- 8 files changed, 218 insertions(+), 46 deletions(-) create mode 100644 xhcid/src/xhci/executor.rs diff --git a/Cargo.lock b/Cargo.lock index e0a97cc6da..43d3e114a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -697,7 +697,7 @@ dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (git+https://gitlab.redox-os.org/redox-os/syscall.git)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/pcid/Cargo.toml b/pcid/Cargo.toml index 189aed0338..966d0ebff2 100644 --- a/pcid/Cargo.toml +++ b/pcid/Cargo.toml @@ -16,7 +16,7 @@ bincode = "1.2" bitflags = "1" byteorder = "1.2" libc = "0.2" -redox_syscall = "0.1" +redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git" } serde = { version = "1", features = ["derive"] } serde_json = "1" thiserror = "1" diff --git a/pcid/src/driver_interface.rs b/pcid/src/driver_interface.rs index 47b8e108a2..e2f5b68d8c 100644 --- a/pcid/src/driver_interface.rs +++ b/pcid/src/driver_interface.rs @@ -8,7 +8,7 @@ use serde::{Serialize, Deserialize, de::DeserializeOwned}; use thiserror::Error; pub use crate::pci::PciBar; -pub use crate::pci::msi::{MsiCapability, MsixCapability, MsixTableEntry}; +pub use crate::pci::msi; #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct PciFunction { @@ -70,8 +70,8 @@ impl PciFeature { } #[derive(Debug, Serialize, Deserialize)] pub enum PciFeatureInfo { - Msi(MsiCapability), - MsiX(MsixCapability), + Msi(msi::MsiCapability), + MsiX(msi::MsixCapability), } #[derive(Debug, Error)] diff --git a/pcid/src/pci/msi.rs b/pcid/src/pci/msi.rs index 2a662f6737..da993840ec 100644 --- a/pcid/src/pci/msi.rs +++ b/pcid/src/pci/msi.rs @@ -259,6 +259,54 @@ pub struct MsixTableEntry { pub vec_ctl: Mmio, } +#[cfg(target_arch = "x86_64")] +pub mod x86_64 { + #[repr(u8)] + pub enum TriggerMode { + Level = 0, + Edge = 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, rh: bool, dm: bool, xx: u8) -> u32 { + 0xFEE0_0000u32 + | ((destination_id as u32) << 12) + | ((rh as u32) << 3) + | ((dm as u32) << 2) + | xx as u32 + } + 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) + } +} + impl MsixTableEntry { pub fn addr_lo(&self) -> u32 { self.addr_lo.read() diff --git a/xhcid/src/main.rs b/xhcid/src/main.rs index 87a09d7d36..e8c29b2848 100644 --- a/xhcid/src/main.rs +++ b/xhcid/src/main.rs @@ -5,19 +5,22 @@ extern crate plain; extern crate syscall; use pcid_interface::{PcidServerHandle, PciFeature, PciFeatureInfo}; -use pcid_interface::MsixTableEntry; +use pcid_interface::msi::{MsiCapability, MsixCapability, MsixTableEntry}; use event::{Event, EventQueue}; use std::cell::RefCell; -use std::fs::File; -use std::io::{Read, Result, Write}; +use std::convert::TryInto; +use std::fs::{self, File}; +use std::io::{self, Read, Write}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::ptr::NonNull; use std::sync::Arc; -use std::{env, io}; +use std::env; use syscall::data::Packet; use syscall::error::EWOULDBLOCK; use syscall::flag::{CloneFlags, PHYSMAP_NO_CACHE, PHYSMAP_WRITE}; use syscall::scheme::SchemeMut; +use syscall::io::Io; use crate::xhci::Xhci; @@ -25,7 +28,55 @@ mod driver_interface; mod usb; mod xhci; +/// Read the local APIC id of the bootstrap processor. +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 + } 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::()); + }) +} +/// Allocate an interrupt vector, located at the BSP's IDT. +fn allocate_interrupt_vector() -> io::Result> { + 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::() { + // 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) +} + fn main() { + // Daemonize + if unsafe { syscall::clone(CloneFlags::empty()).unwrap() } != 0 { + return; + } + let mut pcid_handle = PcidServerHandle::connect_default().expect("xhcid: failed to setup channel to pcid"); let pci_config = pcid_handle.fetch_config().expect("xhcid: failed to fetch config"); println!("XHCI PCI CONFIG: {:?}", pci_config); @@ -63,10 +114,9 @@ fn main() { println!("Enabled MSI-X"); } - if msi_enabled && !msix_enabled { + let (mut irq_file, msix_info) = if msi_enabled && !msix_enabled { todo!("only msi-x is currently implemented") - } - if msix_enabled { + } 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!(), PciFeatureInfo::MsiX(s) => s, @@ -86,26 +136,49 @@ fn main() { todo!() } - let virt_table_base = ((table_base - bar_ptr as usize) + address) as *const MsixTableEntry; - let virt_pba_base = ((pba_base - bar_ptr as usize) + address) as *const u64; + 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; + + 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. + + { + use pcid_interface::msi::x86_64::{DeliveryMode, self as x86_64_msix}; + + // primary interrupter + let k = 0; - for k in 0..table_size { assert_eq!(std::mem::size_of::(), 16); - let table_entry_pointer = unsafe { virt_table_base.offset(k as isize).as_ref().unwrap() }; - let pba_pointer = unsafe { virt_pba_base.offset(k as isize / 64).as_ref().unwrap() }; - let pba_bit = k % 64; + let table_entry_pointer = info.table_entry_pointer(k); - dbg!(table_entry_pointer, (*pba_pointer >> pba_bit) & 1); + let destination_id = read_bsp_apic_id().expect("xhcid: failed to read BSP apic id"); + 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 (vector, interrupt_handle) = allocate_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); + + dbg!(vector, destination_id); + + 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); + + (interrupt_handle, Some(info)) } - } + } else { + (File::open(format!("irq:{}", irq)).expect("xhcid: failed to open legacy IRQ file"), None) + }; std::thread::sleep(std::time::Duration::from_millis(300)); - // Daemonize - if unsafe { syscall::clone(CloneFlags::empty()).unwrap() } != 0 { - return; - } - let mut args = env::args().skip(1); let mut name = args.next().expect("xhcid: no name provided"); @@ -125,12 +198,9 @@ fn main() { File::from_raw_fd(socket_fd as RawFd) })); - let mut irq_file = - File::open(format!("irq:{}", irq)).expect("xhcid: failed to open IRQ file"); - { let hci = Arc::new(RefCell::new( - Xhci::new(name, address).expect("xhcid: failed to allocate device"), + Xhci::new(name, address, msi_enabled, msix_enabled, msix_info).expect("xhcid: failed to allocate device"), )); hci.borrow_mut().probe().expect("xhcid: failed to probe"); @@ -146,11 +216,13 @@ fn main() { let socket_irq = socket.clone(); let todo_irq = todo.clone(); event_queue - .add(irq_file.as_raw_fd(), move |_| -> Result> { + .add(irq_file.as_raw_fd(), move |_| -> io::Result> { let mut irq = [0; 8]; irq_file.read(&mut irq)?; - if hci_irq.borrow_mut().trigger_irq() { + if hci_irq.borrow_mut().received_irq() { + hci_irq.borrow_mut().on_irq(); + irq_file.write(&mut irq)?; let mut todo = todo_irq.borrow_mut(); @@ -175,7 +247,7 @@ fn main() { let socket_fd = socket.borrow().as_raw_fd(); let socket_packet = socket.clone(); event_queue - .add(socket_fd, move |_| -> Result> { + .add(socket_fd, move |_| -> io::Result> { loop { let mut packet = Packet::default(); match socket_packet.borrow_mut().read(&mut packet) { diff --git a/xhcid/src/xhci/command.rs b/xhcid/src/xhci/command.rs index ca78d592fe..c82dc4311e 100644 --- a/xhcid/src/xhci/command.rs +++ b/xhcid/src/xhci/command.rs @@ -22,7 +22,8 @@ impl CommandRing { } pub fn erdp(&self) -> u64 { - self.events.ring.register() + let address = self.events.ring.register(); + address & 0xFFFF_FFFF_FFFF_FFF0 } pub fn erstba(&self) -> u64 { diff --git a/xhcid/src/xhci/executor.rs b/xhcid/src/xhci/executor.rs new file mode 100644 index 0000000000..01fe6603d8 --- /dev/null +++ b/xhcid/src/xhci/executor.rs @@ -0,0 +1 @@ +use super::Xhci; diff --git a/xhcid/src/xhci/mod.rs b/xhcid/src/xhci/mod.rs index 530bb30fad..47df7db0a8 100644 --- a/xhcid/src/xhci/mod.rs +++ b/xhcid/src/xhci/mod.rs @@ -11,11 +11,14 @@ use syscall::io::{Dma, Io}; use crate::usb; +use pcid_interface::msi::{MsixTableEntry, MsixCapability}; + mod capability; mod command; mod context; mod doorbell; mod event; +pub mod executor; mod extended; mod operational; mod port; @@ -39,6 +42,33 @@ use self::scheme::EndpIfState; use crate::driver_interface::*; +pub struct MsixInfo { + pub virt_table_base: NonNull, + pub virt_pba_base: NonNull, + pub capability: MsixCapability, +} +impl MsixInfo { + pub unsafe fn table_entry_pointer_unchecked(&mut self, k: usize) -> &mut MsixTableEntry { + &mut *self.virt_table_base.as_ptr().offset(k as isize) + } + pub fn table_entry_pointer(&mut self, k: usize) -> &mut MsixTableEntry { + 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 + } +} + struct Device<'a> { ring: &'a mut Ring, cmd: &'a mut CommandRing, @@ -139,6 +169,10 @@ pub struct Xhci { drivers: BTreeMap, scheme_name: String, + + msi: bool, + msix: bool, + msix_info: Option, } struct PortState { @@ -167,7 +201,7 @@ impl EndpointState { } impl Xhci { - pub fn new(scheme_name: String, address: usize) -> Result { + pub fn new(scheme_name: String, address: usize, msi: bool, msix: bool, msix_info: Option) -> Result { let cap = unsafe { &mut *(address as *mut CapabilityRegs) }; println!(" - CAP {:X}", address); @@ -239,6 +273,9 @@ impl Xhci { }), drivers: BTreeMap::new(), scheme_name, + msi, + msix, + msix_info, }; xhci.init(max_slots); @@ -277,7 +314,11 @@ impl Xhci { println!(" - Write ERSTBA: {:X}", erstba); self.run.ints[0].erstba.write(erstba as u64); + println!(" - Write IMODC and IMODI: {} and {}", 0, 0); + self.run.ints[0].imod.write(0); + println!(" - Enable interrupts"); + self.run.ints[0].iman.writef(1, true); // clear interrupt pending if set earlier by the BIOS self.run.ints[0].iman.writef(1 << 1, true); } @@ -506,25 +547,34 @@ impl Xhci { } - pub fn trigger_irq(&mut self) -> bool { - // Read the Interrupter Pending bit. - if self.run.ints[0].iman.readf(1) { - //println!("XHCI Interrupt"); - - // If set, set it back to zero, so that new interrupts can be triggered. - // FIXME: MSI and MSI-X systems + /// Checks whether an IRQ has been received from *this* device, in case of an interrupt. Always + /// true when using MSI/MSI-X. + pub fn received_irq(&mut self) -> bool { + if self.msi || self.msix { + // Since using MSI and MSI-X implies having no IRQ sharing whatsoever, the IP bit + // doesn't have to be touched. + println!("Successfully received MSI/MSI-X interrupt, IP={}, EHB={}", self.run.ints[0].iman.readf(1), self.run.ints[0].erdp.readf(3)); + println!("MSI-X PB={}", self.msix_info.as_ref().unwrap().pba(0)); + true + } else if self.run.ints[0].iman.readf(1) { + // If MSI and/or MSI-X are not used, the interrupt has to be shared, and thus there is + // a special register to specify whether the IRQ actually came from the xHC. self.run.ints[0].iman.writef(1, true); - // Wake all futures awaiting the IRQ. - for waker in self.irq_state.wakers.lock().unwrap().drain(..) { - waker.wake(); - } - + // The interrupt came from the xHC. true } else { + // The interrupt came from a different device. false } } + /// Handle an IRQ event. + pub fn on_irq(&mut self) { + // Wake all futures awaiting the IRQ. + for waker in self.irq_state.wakers.lock().unwrap().drain(..) { + waker.wake(); + } + } pub(crate) fn irq(&self) -> IrqFuture { IrqFuture { state: IrqFutureState::Pending(Arc::downgrade(&self.irq_state)),