Successfully recognize (only one) MSI-X IRQ.

Somehow it only happens once though.
This commit is contained in:
4lDO2
2020-03-20 18:50:39 +01:00
parent fa594155c9
commit cb5a72fbc1
8 changed files with 218 additions and 46 deletions
Generated
+1 -1
View File
@@ -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
View File
@@ -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"
+3 -3
View File
@@ -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)]
+48
View File
@@ -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
View File
@@ -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) {
+2 -1
View File
@@ -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 {
+1
View File
@@ -0,0 +1 @@
use super::Xhci;
+63 -13
View File
@@ -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)),