Successfully recognize (only one) MSI-X IRQ.
Somehow it only happens once though.
This commit is contained in:
Generated
+1
-1
@@ -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)",
|
||||
|
||||
+1
-1
@@ -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"
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -259,6 +259,54 @@ pub struct MsixTableEntry {
|
||||
pub vec_ctl: Mmio<u32>,
|
||||
}
|
||||
|
||||
#[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()
|
||||
|
||||
+99
-27
@@ -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<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)
|
||||
}
|
||||
|
||||
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::<MsixTableEntry>(), 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<Option<()>> {
|
||||
.add(irq_file.as_raw_fd(), move |_| -> io::Result<Option<()>> {
|
||||
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<Option<()>> {
|
||||
.add(socket_fd, move |_| -> io::Result<Option<()>> {
|
||||
loop {
|
||||
let mut packet = Packet::default();
|
||||
match socket_packet.borrow_mut().read(&mut packet) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
use super::Xhci;
|
||||
+63
-13
@@ -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<MsixTableEntry>,
|
||||
pub virt_pba_base: NonNull<u64>,
|
||||
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<usize, process::Child>,
|
||||
scheme_name: String,
|
||||
|
||||
msi: bool,
|
||||
msix: bool,
|
||||
msix_info: Option<MsixInfo>,
|
||||
}
|
||||
|
||||
struct PortState {
|
||||
@@ -167,7 +201,7 @@ impl EndpointState {
|
||||
}
|
||||
|
||||
impl Xhci {
|
||||
pub fn new(scheme_name: String, address: usize) -> Result<Xhci> {
|
||||
pub fn new(scheme_name: String, address: usize, msi: bool, msix: bool, msix_info: Option<MsixInfo>) -> Result<Xhci> {
|
||||
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)),
|
||||
|
||||
Reference in New Issue
Block a user