From fa594155c9c8aedadd5e63be2b06e79ad1352bd9 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 15 Mar 2020 14:05:19 +0100 Subject: [PATCH] Allow subdrivers to get the capability structs. --- bgad/src/scheme.rs | 1 - pcid/src/driver_interface.rs | 51 +++++- pcid/src/main.rs | 15 +- pcid/src/pci/cap.rs | 209 +------------------------ pcid/src/pci/mod.rs | 1 + pcid/src/pci/msi.rs | 296 +++++++++++++++++++++++++++++++++++ xhcid/src/main.rs | 253 ++++++++++++++++++------------ xhcid/src/xhci/mod.rs | 4 +- xhcid/src/xhci/scheme.rs | 2 +- xhcid/src/xhci/trb.rs | 9 ++ 10 files changed, 533 insertions(+), 308 deletions(-) create mode 100644 pcid/src/pci/msi.rs diff --git a/bgad/src/scheme.rs b/bgad/src/scheme.rs index bad949c717..635d581017 100644 --- a/bgad/src/scheme.rs +++ b/bgad/src/scheme.rs @@ -1,4 +1,3 @@ -use orbclient; use std::fs::File; use std::io::Write; use std::str; diff --git a/pcid/src/driver_interface.rs b/pcid/src/driver_interface.rs index cf5e24336d..47b8e108a2 100644 --- a/pcid/src/driver_interface.rs +++ b/pcid/src/driver_interface.rs @@ -7,7 +7,8 @@ use std::os::unix::io::{FromRawFd, RawFd}; use serde::{Serialize, Deserialize, de::DeserializeOwned}; use thiserror::Error; -use crate::pci; +pub use crate::pci::PciBar; +pub use crate::pci::msi::{MsiCapability, MsixCapability, MsixTableEntry}; #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct PciFunction { @@ -18,7 +19,7 @@ pub struct PciFunction { /// Number of PCI function pub func_num: u8, /// PCI Base Address Registers - pub bars: [pci::PciBar; 6], + pub bars: [PciBar; 6], /// BAR sizes pub bar_sizes: [u32; 6], /// Legacy IRQ line @@ -49,6 +50,9 @@ impl FeatureStatus { Self::Disabled } } + pub fn is_enabled(&self) -> bool { + if let &Self::Enabled = self { true } else { false } + } } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] @@ -56,6 +60,19 @@ pub enum PciFeature { Msi, MsiX, } +impl PciFeature { + pub fn is_msi(&self) -> bool { + if let &Self::Msi = self { true } else { false } + } + pub fn is_msix(&self) -> bool { + if let &Self::MsiX = self { true } else { false } + } +} +#[derive(Debug, Serialize, Deserialize)] +pub enum PciFeatureInfo { + Msi(MsiCapability), + MsiX(MsixCapability), +} #[derive(Debug, Error)] pub enum PcidClientHandleError { @@ -83,6 +100,7 @@ pub enum PcidClientRequest { RequestFeatures, EnableFeature(PciFeature), FeatureStatus(PciFeature), + FeatureInfo(PciFeature), } #[derive(Debug, Serialize, Deserialize)] @@ -99,6 +117,7 @@ pub enum PcidClientResponse { FeatureEnabled(PciFeature), FeatureStatus(PciFeature, FeatureStatus), Error(PcidServerResponseError), + FeatureInfo(PciFeature, PciFeatureInfo), } // TODO: Ideally, pcid might have its own scheme, like lots of other Redox drivers, where this kind of IPC is done. Otherwise, instead of writing serde messages over @@ -158,4 +177,32 @@ impl PcidServerHandle { other => Err(PcidClientHandleError::InvalidResponse(other)), } } + pub fn fetch_all_features(&mut self) -> Result> { + self.send(&PcidClientRequest::RequestFeatures)?; + match self.recv()? { + PcidClientResponse::AllFeatures(a) => Ok(a), + other => Err(PcidClientHandleError::InvalidResponse(other)), + } + } + pub fn feature_status(&mut self, feature: PciFeature) -> Result { + self.send(&PcidClientRequest::FeatureStatus(feature))?; + match self.recv()? { + PcidClientResponse::FeatureStatus(feat, status) if feat == feature => Ok(status), + other => Err(PcidClientHandleError::InvalidResponse(other)), + } + } + pub fn enable_feature(&mut self, feature: PciFeature) -> Result<()> { + self.send(&PcidClientRequest::EnableFeature(feature))?; + match self.recv()? { + PcidClientResponse::FeatureEnabled(feat) if feat == feature => Ok(()), + other => Err(PcidClientHandleError::InvalidResponse(other)), + } + } + pub fn feature_info(&mut self, feature: PciFeature) -> Result { + self.send(&PcidClientRequest::FeatureInfo(feature))?; + match self.recv()? { + PcidClientResponse::FeatureInfo(feat, info) if feat == feature => Ok(info), + other => Err(PcidClientHandleError::InvalidResponse(other)), + } + } } diff --git a/pcid/src/main.rs b/pcid/src/main.rs index 78416db8c3..42f28f29bf 100644 --- a/pcid/src/main.rs +++ b/pcid/src/main.rs @@ -105,6 +105,18 @@ impl DriverHandler { None }).unwrap_or(FeatureStatus::Disabled), }), + PcidClientRequest::FeatureInfo(feature) => PcidClientResponse::FeatureInfo(feature, match feature { + PciFeature::Msi => if let Some(info) = self.capabilities.iter().find_map(|(_, capability)| capability.as_msi()) { + PciFeatureInfo::Msi(*info) + } else { + return PcidClientResponse::Error(PcidServerResponseError::NonexistentFeature(feature)); + } + PciFeature::MsiX => if let Some(info) = self.capabilities.iter().find_map(|(_, capability)| capability.as_msix()) { + PciFeatureInfo::MsiX(*info) + } else { + return PcidClientResponse::Error(PcidServerResponseError::NonexistentFeature(feature)); + } + }), } } fn handle_spawn(mut self, pcid_to_client_write: Option, pcid_from_client_read: Option, args: driver_interface::SubdriverArguments) { @@ -116,7 +128,7 @@ impl DriverHandler { while let Ok(msg) = recv(&mut pcid_from_client) { let response = self.respond(msg, &args); - send(&mut pcid_to_client, &response); + send(&mut pcid_to_client, &response).unwrap(); } } } @@ -294,6 +306,7 @@ fn handle_parsed_header(state: Arc, config: &Config, bus_num: u8, }; crate::pci::cap::CapabilitiesIter { inner: crate::pci::cap::CapabilityOffsetsIter::new(header.cap_pointer(), &func) }.collect::>() }; + println!("PCI DEVICE CAPABILITIES for {}: {:?}", args.iter().map(|string| string.as_ref()).nth(0).unwrap_or("[unknown]"), capabilities); let func = driver_interface::PciFunction { bars, diff --git a/pcid/src/pci/cap.rs b/pcid/src/pci/cap.rs index c458174536..6159dc0f6a 100644 --- a/pcid/src/pci/cap.rs +++ b/pcid/src/pci/cap.rs @@ -1,4 +1,5 @@ -use super::func::{ConfigReader, ConfigWriter}; +use super::func::ConfigReader; +use serde::{Serialize, Deserialize}; pub struct CapabilityOffsetsIter<'a, R> { offset: u8, @@ -42,7 +43,7 @@ pub enum CapabilityId { Pcie = 0x10, } -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum MsiCapability { _32BitAddress { message_control: u32, @@ -72,220 +73,18 @@ pub enum MsiCapability { }, } -impl MsiCapability { - pub const MC_PVT_CAPABLE_BIT: u16 = 1 << 8; - pub const MC_64_BIT_ADDR_BIT: u16 = 1 << 7; - - pub const MC_MULTI_MESSAGE_MASK: u16 = 0x000E; - pub const MC_MULTI_MESSAGE_SHIFT: u8 = 1; - - pub const MC_MULTI_MESSAGE_ENABLE_MASK: u16 = 0x0070; - pub const MC_MULTI_MESSAGE_ENABLE_SHIFT: u8 = 4; - - pub const MC_MSI_ENABLED_BIT: u16 = 1; - - pub unsafe fn parse(reader: &R, offset: u8) -> Self { - let dword = reader.read_u32(offset); - - let message_control = (dword >> 16) as u16; - - if message_control & Self::MC_PVT_CAPABLE_BIT != 0 { - if message_control & Self::MC_64_BIT_ADDR_BIT != 0 { - Self::_64BitAddressWithPvm { - message_control: dword, - message_address_lo: reader.read_u32(offset + 4), - message_address_hi: reader.read_u32(offset + 8), - message_data: reader.read_u32(offset + 12), - mask_bits: reader.read_u32(offset + 16), - pending_bits: reader.read_u32(offset + 20), - } - } else { - Self::_32BitAddressWithPvm { - message_control: dword, - message_address: reader.read_u32(offset + 4), - message_data: reader.read_u32(offset + 8), - mask_bits: reader.read_u32(offset + 12), - pending_bits: reader.read_u32(offset + 16), - } - } - } else { - if message_control & Self::MC_64_BIT_ADDR_BIT != 0 { - Self::_64BitAddress { - message_control: dword, - message_address_lo: reader.read_u32(offset + 4), - message_address_hi: reader.read_u32(offset + 8), - message_data: reader.read_u32(offset + 12), - } - } else { - Self::_32BitAddress { - message_control: dword, - message_address: reader.read_u32(offset + 4), - message_data: reader.read_u32(offset + 8), - } - } - } - } - - fn message_control_raw(&self) -> u32 { - match self { - Self::_32BitAddress { message_control, .. } | Self::_64BitAddress { message_control, .. } | Self::_32BitAddressWithPvm { message_control, .. } | Self::_64BitAddressWithPvm { message_control, .. } => *message_control, - } - } - pub fn message_control(&self) -> u16 { - self.message_control_raw() as u16 - } - pub unsafe fn set_message_control(&mut self, writer: &W, offset: u8, value: u16) { - let mut new_message_control = self.message_control_raw(); - new_message_control &= 0x0000_FFFF; - new_message_control |= u32::from(value) << 16; - writer.write_u32(offset, new_message_control); - - match self { - Self::_32BitAddress { ref mut message_control, .. } - | Self::_64BitAddress { ref mut message_control, .. } - | Self::_32BitAddressWithPvm { ref mut message_control, .. } - | Self::_64BitAddressWithPvm { ref mut message_control, .. } => *message_control = new_message_control, - } - } - pub fn is_pvt_capable(&self) -> bool { - self.message_control() & Self::MC_PVT_CAPABLE_BIT != 0 - } - pub fn has_64_bit_addr(&self) -> bool { - self.message_control() & Self::MC_64_BIT_ADDR_BIT != 0 - } - pub fn enabled(&self) -> bool { - self.message_control() & Self::MC_MSI_ENABLED_BIT != 0 - } - pub unsafe fn set_enabled(&mut self, writer: &W, offset: u8, enabled: bool) { - let mut new_message_control = self.message_control() & (!Self::MC_MSI_ENABLED_BIT); - new_message_control |= u16::from(enabled); - self.set_message_control(writer, offset, new_message_control) - } - pub fn multi_message_capable(&self) -> u8 { - ((self.message_control() & Self::MC_MULTI_MESSAGE_MASK) >> Self::MC_MULTI_MESSAGE_SHIFT) as u8 - } - pub fn multi_message_enabled(&self) -> u8 { - ((self.message_control() & Self::MC_MULTI_MESSAGE_ENABLE_MASK) >> Self::MC_MULTI_MESSAGE_ENABLE_MASK) as u8 - } -} #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct PcieCapability { } -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct MsixCapability { pub a: u32, pub b: u32, pub c: u32, } -impl MsixCapability { - pub const MC_MSIX_ENABLED_BIT: u16 = 1 << 15; - pub const MC_MSIX_ENABLED_SHIFT: u8 = 15; - pub const MC_FUNCTION_MASK_BIT: u16 = 1 << 14; - pub const MC_FUNCTION_MASK_SHIFT: u8 = 14; - pub const MC_TABLE_SIZE_MASK: u16 = 0x03FF; - - /// The Message Control field, containing the enabled and function mask bits, as well as the - /// table size. - pub const fn message_control(&self) -> u16 { - (self.a >> 16) as u16 - } - - pub fn set_message_control(&mut self, message_control: u16) { - self.a &= 0x0000_FFFF; - self.a |= u32::from(message_control) << 16; - } - /// Returns the MSI-X table size, subtracted by one. - pub const fn table_size_raw(&self) -> u16 { - self.message_control() & Self::MC_TABLE_SIZE_MASK - } - /// Returns the MSI-X table size. - pub const fn table_size(&self) -> u16 { - self.table_size_raw() + 1 - } - /// Returns the MSI-X enabled bit, which enables MSI-X if the MSI enable bit is also set in the - /// MSI capability structure. - pub const fn msix_enabled(&self) -> bool { - self.message_control() & Self::MC_MSIX_ENABLED_BIT != 0 - } - /// The MSI-X function mask, which overrides each of the vectors' mask bit, when set. - pub const fn function_mask(&self) -> bool { - self.message_control() & Self::MC_FUNCTION_MASK_BIT != 0 - } - - pub fn set_msix_enabled(&mut self, enabled: bool) { - let mut new_message_control = self.message_control(); - new_message_control &= !(Self::MC_MSIX_ENABLED_BIT); - new_message_control |= u16::from(enabled) << Self::MC_MSIX_ENABLED_SHIFT; - self.set_message_control(new_message_control); - } - - pub fn set_function_mask(&mut self, function_mask: bool) { - let mut new_message_control = self.message_control(); - new_message_control &= !(Self::MC_FUNCTION_MASK_BIT); - new_message_control |= u16::from(function_mask) << Self::MC_FUNCTION_MASK_SHIFT; - self.set_message_control(new_message_control); - } - pub const TABLE_OFFSET_MASK: u32 = 0xFFFF_FFF8; - pub const TABLE_BIR_MASK: u32 = 0x0000_0007; - - /// The table offset is guaranteed to be QWORD aligned (8 bytes). - pub const fn table_offset(&self) -> u32 { - self.b & Self::TABLE_OFFSET_MASK - } - /// The table BIR, which is used to map the offset to a memory location. - pub const fn table_bir(&self) -> u8 { - (self.b & Self::TABLE_BIR_MASK) as u8 - } - - pub fn set_table_offset(&mut self, offset: u32) { - assert_eq!(offset & Self::TABLE_OFFSET_MASK, offset, "MSI-X table offset has to be QWORD aligned"); - self.b &= !Self::TABLE_OFFSET_MASK; - self.b |= offset; - } - pub const PBA_OFFSET_MASK: u32 = 0xFFFF_FFF8; - pub const PBA_BIR_MASK: u32 = 0x0000_0007; - - /// The Pending Bit Array offset is guaranteed to be QWORD aligned (8 bytes). - pub const fn pba_offset(&self) -> u32 { - self.b & Self::PBA_OFFSET_MASK - } - /// The Pending Bit Array BIR, which is used to map the offset to a memory location. - pub const fn pba_bir(&self) -> u8 { - (self.b & Self::PBA_BIR_MASK) as u8 - } - - pub fn set_pba_offset(&mut self, offset: u32) { - assert_eq!(offset & Self::PBA_OFFSET_MASK, offset, "MSI-X Pending Bit Array offset has to be QWORD aligned"); - self.c &= !Self::PBA_OFFSET_MASK; - self.c |= offset; - } - - /// Write the first DWORD into configuration space (containing the partially modifiable Message - /// Control field). - pub unsafe fn write_a(&self, writer: &W, offset: u8) { - writer.write_u32(offset, self.a) - } - /// Write the second DWORD into configuration space (containing the modifiable table - /// offset and the readonly table BIR). - pub unsafe fn write_b(&self, writer: &W, offset: u8) { - writer.write_u32(offset + 4, self.a) - } - /// Write the third DWORD into configuration space (containing the modifiable pending bit array - /// offset, and the readonly PBA BIR). - pub unsafe fn write_c(&self, writer: &W, offset: u8) { - writer.write_u32(offset + 8, self.a) - } - /// Write this capability structure back to configuration space. - pub unsafe fn write_all(&self, writer: &W, offset: u8) { - self.write_a(writer, offset); - self.write_b(writer, offset); - self.write_c(writer, offset); - } -} - #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum Capability { Msi(MsiCapability), diff --git a/pcid/src/pci/mod.rs b/pcid/src/pci/mod.rs index 6e763a89e4..b3395517eb 100644 --- a/pcid/src/pci/mod.rs +++ b/pcid/src/pci/mod.rs @@ -14,6 +14,7 @@ mod class; mod dev; mod func; pub mod header; +pub mod msi; pub struct Pci { lock: Mutex<()>, diff --git a/pcid/src/pci/msi.rs b/pcid/src/pci/msi.rs new file mode 100644 index 0000000000..2a662f6737 --- /dev/null +++ b/pcid/src/pci/msi.rs @@ -0,0 +1,296 @@ +use std::fmt; + +use super::bar::PciBar; +pub use super::cap::{MsiCapability, MsixCapability}; +use super::func::{ConfigReader, ConfigWriter}; + +use syscall::{Io, Mmio}; + +impl MsiCapability { + pub const MC_PVT_CAPABLE_BIT: u16 = 1 << 8; + pub const MC_64_BIT_ADDR_BIT: u16 = 1 << 7; + + pub const MC_MULTI_MESSAGE_MASK: u16 = 0x000E; + pub const MC_MULTI_MESSAGE_SHIFT: u8 = 1; + + pub const MC_MULTI_MESSAGE_ENABLE_MASK: u16 = 0x0070; + pub const MC_MULTI_MESSAGE_ENABLE_SHIFT: u8 = 4; + + pub const MC_MSI_ENABLED_BIT: u16 = 1; + + pub unsafe fn parse(reader: &R, offset: u8) -> Self { + let dword = reader.read_u32(offset); + + let message_control = (dword >> 16) as u16; + + if message_control & Self::MC_PVT_CAPABLE_BIT != 0 { + if message_control & Self::MC_64_BIT_ADDR_BIT != 0 { + Self::_64BitAddressWithPvm { + message_control: dword, + message_address_lo: reader.read_u32(offset + 4), + message_address_hi: reader.read_u32(offset + 8), + message_data: reader.read_u32(offset + 12), + mask_bits: reader.read_u32(offset + 16), + pending_bits: reader.read_u32(offset + 20), + } + } else { + Self::_32BitAddressWithPvm { + message_control: dword, + message_address: reader.read_u32(offset + 4), + message_data: reader.read_u32(offset + 8), + mask_bits: reader.read_u32(offset + 12), + pending_bits: reader.read_u32(offset + 16), + } + } + } else { + if message_control & Self::MC_64_BIT_ADDR_BIT != 0 { + Self::_64BitAddress { + message_control: dword, + message_address_lo: reader.read_u32(offset + 4), + message_address_hi: reader.read_u32(offset + 8), + message_data: reader.read_u32(offset + 12), + } + } else { + Self::_32BitAddress { + message_control: dword, + message_address: reader.read_u32(offset + 4), + message_data: reader.read_u32(offset + 8), + } + } + } + } + + fn message_control_raw(&self) -> u32 { + match self { + Self::_32BitAddress { message_control, .. } | Self::_64BitAddress { message_control, .. } | Self::_32BitAddressWithPvm { message_control, .. } | Self::_64BitAddressWithPvm { message_control, .. } => *message_control, + } + } + pub fn message_control(&self) -> u16 { + (self.message_control_raw() >> 16) as u16 + } + pub unsafe fn set_message_control(&mut self, writer: &W, offset: u8, value: u16) { + let mut new_message_control = self.message_control_raw(); + new_message_control &= 0x0000_FFFF; + new_message_control |= u32::from(value) << 16; + writer.write_u32(offset, new_message_control); + + match self { + Self::_32BitAddress { ref mut message_control, .. } + | Self::_64BitAddress { ref mut message_control, .. } + | Self::_32BitAddressWithPvm { ref mut message_control, .. } + | Self::_64BitAddressWithPvm { ref mut message_control, .. } => *message_control = new_message_control, + } + } + pub fn is_pvt_capable(&self) -> bool { + self.message_control() & Self::MC_PVT_CAPABLE_BIT != 0 + } + pub fn has_64_bit_addr(&self) -> bool { + self.message_control() & Self::MC_64_BIT_ADDR_BIT != 0 + } + pub fn enabled(&self) -> bool { + self.message_control() & Self::MC_MSI_ENABLED_BIT != 0 + } + pub unsafe fn set_enabled(&mut self, writer: &W, offset: u8, enabled: bool) { + let mut new_message_control = self.message_control() & (!Self::MC_MSI_ENABLED_BIT); + new_message_control |= u16::from(enabled); + self.set_message_control(writer, offset, new_message_control) + } + pub fn multi_message_capable(&self) -> u8 { + ((self.message_control() & Self::MC_MULTI_MESSAGE_MASK) >> Self::MC_MULTI_MESSAGE_SHIFT) as u8 + } + pub fn multi_message_enabled(&self) -> u8 { + ((self.message_control() & Self::MC_MULTI_MESSAGE_ENABLE_MASK) >> Self::MC_MULTI_MESSAGE_ENABLE_SHIFT) as u8 + } +} + +impl MsixCapability { + pub const MC_MSIX_ENABLED_BIT: u16 = 1 << 15; + pub const MC_MSIX_ENABLED_SHIFT: u8 = 15; + pub const MC_FUNCTION_MASK_BIT: u16 = 1 << 14; + pub const MC_FUNCTION_MASK_SHIFT: u8 = 14; + pub const MC_TABLE_SIZE_MASK: u16 = 0x03FF; + + /// The Message Control field, containing the enabled and function mask bits, as well as the + /// table size. + pub const fn message_control(&self) -> u16 { + (self.a >> 16) as u16 + } + + pub fn set_message_control(&mut self, message_control: u16) { + self.a &= 0x0000_FFFF; + self.a |= u32::from(message_control) << 16; + } + /// Returns the MSI-X table size, subtracted by one. + pub const fn table_size_raw(&self) -> u16 { + self.message_control() & Self::MC_TABLE_SIZE_MASK + } + /// Returns the MSI-X table size. + pub const fn table_size(&self) -> u16 { + self.table_size_raw() + 1 + } + /// Returns the MSI-X enabled bit, which enables MSI-X if the MSI enable bit is also set in the + /// MSI capability structure. + pub const fn msix_enabled(&self) -> bool { + self.message_control() & Self::MC_MSIX_ENABLED_BIT != 0 + } + /// The MSI-X function mask, which overrides each of the vectors' mask bit, when set. + pub const fn function_mask(&self) -> bool { + self.message_control() & Self::MC_FUNCTION_MASK_BIT != 0 + } + + pub fn set_msix_enabled(&mut self, enabled: bool) { + let mut new_message_control = self.message_control(); + new_message_control &= !(Self::MC_MSIX_ENABLED_BIT); + new_message_control |= u16::from(enabled) << Self::MC_MSIX_ENABLED_SHIFT; + self.set_message_control(new_message_control); + } + + pub fn set_function_mask(&mut self, function_mask: bool) { + let mut new_message_control = self.message_control(); + new_message_control &= !(Self::MC_FUNCTION_MASK_BIT); + new_message_control |= u16::from(function_mask) << Self::MC_FUNCTION_MASK_SHIFT; + self.set_message_control(new_message_control); + } + pub const TABLE_OFFSET_MASK: u32 = 0xFFFF_FFF8; + pub const TABLE_BIR_MASK: u32 = 0x0000_0007; + + /// The table offset is guaranteed to be QWORD aligned (8 bytes). + pub const fn table_offset(&self) -> u32 { + self.b & Self::TABLE_OFFSET_MASK + } + /// The table BIR, which is used to map the offset to a memory location. + pub const fn table_bir(&self) -> u8 { + (self.b & Self::TABLE_BIR_MASK) as u8 + } + + pub fn set_table_offset(&mut self, offset: u32) { + assert_eq!(offset & Self::TABLE_OFFSET_MASK, offset, "MSI-X table offset has to be QWORD aligned"); + self.b &= !Self::TABLE_OFFSET_MASK; + self.b |= offset; + } + pub const PBA_OFFSET_MASK: u32 = 0xFFFF_FFF8; + pub const PBA_BIR_MASK: u32 = 0x0000_0007; + + /// The Pending Bit Array offset is guaranteed to be QWORD aligned (8 bytes). + pub const fn pba_offset(&self) -> u32 { + self.c & Self::PBA_OFFSET_MASK + } + /// The Pending Bit Array BIR, which is used to map the offset to a memory location. + pub const fn pba_bir(&self) -> u8 { + (self.c & Self::PBA_BIR_MASK) as u8 + } + + pub fn set_pba_offset(&mut self, offset: u32) { + assert_eq!(offset & Self::PBA_OFFSET_MASK, offset, "MSI-X Pending Bit Array offset has to be QWORD aligned"); + self.c &= !Self::PBA_OFFSET_MASK; + self.c |= offset; + } + + pub fn table_base_pointer(&self, bars: [PciBar; 6]) -> usize { + if self.table_bir() > 5 { + panic!("MSI-X Table BIR contained a reserved enum value: {}", self.table_bir()); + } + let base = bars[usize::from(self.table_bir())]; + + if let PciBar::Memory(ptr) = base { + ptr as usize + self.table_offset() as usize + } else { + panic!("MSI-X Table BIR referenced a non-memory BAR: {:?}", base); + } + } + pub fn table_pointer(&self, bars: [PciBar; 6], k: u16) -> usize { + self.table_base_pointer(bars) + k as usize * 16 + } + + pub fn pba_base_pointer(&self, bars: [PciBar; 6]) -> usize { + if self.pba_bir() > 5 { + panic!("MSI-X PBA BIR contained a reserved enum value: {}", self.pba_bir()); + } + let base = bars[usize::from(self.pba_bir())]; + + if let PciBar::Memory(ptr) = base { + ptr as usize + self.pba_offset() as usize + } else { + panic!("MSI-X PBA BIR referenced a non-memory BAR: {:?}", base); + } + } + pub fn pba_pointer_dword(&self, bars: [PciBar; 6], k: u16) -> usize { + self.pba_base_pointer(bars) + (k as usize / 32) * 4 + } + pub const fn pba_bit_dword(&self, k: u16) -> u8 { + (k % 32) as u8 + } + + pub fn pba_pointer_qword(&self, bars: [PciBar; 6], k: u16) -> usize { + self.pba_base_pointer(bars) + (k as usize / 64) * 8 + } + pub const fn pba_bit_qword(&self, k: u16) -> u8 { + (k % 64) as u8 + } + + /// Write the first DWORD into configuration space (containing the partially modifiable Message + /// Control field). + pub unsafe fn write_a(&self, writer: &W, offset: u8) { + writer.write_u32(offset, self.a) + } + /// Write the second DWORD into configuration space (containing the modifiable table + /// offset and the readonly table BIR). + pub unsafe fn write_b(&self, writer: &W, offset: u8) { + writer.write_u32(offset + 4, self.a) + } + /// Write the third DWORD into configuration space (containing the modifiable pending bit array + /// offset, and the readonly PBA BIR). + pub unsafe fn write_c(&self, writer: &W, offset: u8) { + writer.write_u32(offset + 8, self.a) + } + /// Write this capability structure back to configuration space. + pub unsafe fn write_all(&self, writer: &W, offset: u8) { + self.write_a(writer, offset); + self.write_b(writer, offset); + self.write_c(writer, offset); + } +} + +#[repr(packed)] +pub struct MsixTableEntry { + pub addr_lo: Mmio, + pub addr_hi: Mmio, + pub msg_data: Mmio, + pub vec_ctl: Mmio, +} + +impl MsixTableEntry { + pub fn addr_lo(&self) -> u32 { + self.addr_lo.read() + } + pub fn addr_hi(&self) -> u32 { + self.addr_hi.read() + } + pub fn msg_data(&self) -> u32 { + self.msg_data.read() + } + pub fn vec_ctl(&self) -> u32 { + self.vec_ctl.read() + } + pub fn addr(&self) -> u64 { + u64::from(self.addr_lo()) | (u64::from(self.addr_hi()) << 32) + } + pub const VEC_CTL_MASK_BIT: u32 = 1; + + pub fn mask(&mut self) { + self.vec_ctl.writef(Self::VEC_CTL_MASK_BIT, true) + } + pub fn unmask(&mut self) { + self.vec_ctl.writef(Self::VEC_CTL_MASK_BIT, false) + } +} + +impl fmt::Debug for MsixTableEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("MsixTableEntry") + .field("addr", &self.addr()) + .field("msg_data", &self.msg_data()) + .field("vec_ctl", &self.vec_ctl()) + .finish() + } +} diff --git a/xhcid/src/main.rs b/xhcid/src/main.rs index 0de08d86e9..87a09d7d36 100644 --- a/xhcid/src/main.rs +++ b/xhcid/src/main.rs @@ -4,6 +4,9 @@ extern crate event; extern crate plain; extern crate syscall; +use pcid_interface::{PcidServerHandle, PciFeature, PciFeatureInfo}; +use pcid_interface::MsixTableEntry; + use event::{Event, EventQueue}; use std::cell::RefCell; use std::fs::File; @@ -23,127 +26,185 @@ mod usb; mod xhci; fn main() { - println!("xhcid started"); - let mut pcid_handle = pcid_interface::PcidServerHandle::connect_default().expect("xhcid: failed to setup channel to pcid"); - dbg!(); - println!("XHCI from PCI config: {:?}", pcid_handle.fetch_config().expect("xhcid: failed to fetch config")); + 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); - // Close the pipe, allowing pcid to continue. - drop(pcid_handle); + let bar = pci_config.func.bars[0]; + let irq = pci_config.func.legacy_interrupt_line; + let bar_ptr = match bar { + pcid_interface::PciBar::Memory(ptr) => ptr, + other => panic!("Expected memory bar, found {}", other), + }; + + let address = unsafe { + syscall::physmap(bar_ptr as usize, 65536, PHYSMAP_WRITE | PHYSMAP_NO_CACHE) + .expect("xhcid: failed to map address") + }; + + let all_pci_features = pcid_handle.fetch_all_features().expect("xhcid: failed to fetch pci features"); + println!("XHCI PCI FEATURES: {:?}", all_pci_features); + + let (has_msi, mut msi_enabled) = all_pci_features.iter().map(|(feature, status)| (feature.is_msi(), status.is_enabled())).find(|&(f, _)| f).unwrap_or((false, false)); + let (has_msix, mut msix_enabled) = all_pci_features.iter().map(|(feature, status)| (feature.is_msix(), status.is_enabled())).find(|&(f, _)| f).unwrap_or((false, false)); + + dbg!(has_msi, msi_enabled); + dbg!(has_msix, msix_enabled); + + if has_msi && !msi_enabled { + pcid_handle.enable_feature(PciFeature::Msi).expect("xhcid: failed to enable MSI"); + msi_enabled = true; + println!("Enabled MSI"); + } + if has_msi && msi_enabled && has_msix && !msix_enabled { + pcid_handle.enable_feature(PciFeature::MsiX).expect("xhcid: failed to enable MSI-X"); + msix_enabled = true; + println!("Enabled MSI-X"); + } + + if msi_enabled && !msix_enabled { + todo!("only msi-x is currently implemented") + } + 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, + }; + let table_size = capability.table_size(); + let table_base = capability.table_base_pointer(pci_config.func.bars); + let table_min_length = table_size * 16; + let pba_min_length = crate::xhci::scheme::div_round_up(table_size, 8); + + let pba_base = capability.pba_base_pointer(pci_config.func.bars); + dbg!(table_size, table_base, table_min_length, pba_base); + + if !(bar_ptr..bar_ptr + 65536).contains(&(table_base as u32 + table_min_length as u32)) { + todo!() + } + if !(bar_ptr..bar_ptr + 65536).contains(&(pba_base as u32 + pba_min_length as u32)) { + 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; + + 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; + + dbg!(table_entry_pointer, (*pba_pointer >> pba_bit) & 1); + } + } + + 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"); name.push_str("_xhci"); - let bar_str = args.next().expect("xhcid: no address provided"); - let bar = usize::from_str_radix(&bar_str, 16).expect("xhcid: failed to parse address"); - - let irq_str = args.next().expect("xhcid: no IRQ provided"); - let irq = irq_str.parse::().expect("xhcid: failed to parse irq"); - print!( "{}", - format!(" + XHCI {} on: {:X} IRQ: {}\n", name, bar, irq) + format!(" + XHCI {} on: {} IRQ: {}\n", name, bar, irq) ); - // Daemonize - if unsafe { syscall::clone(CloneFlags::empty()).unwrap() } == 0 { - let socket_fd = syscall::open( - format!(":usb/{}", name), - syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK, - ) - .expect("xhcid: failed to create usb scheme"); - let socket = Arc::new(RefCell::new(unsafe { - File::from_raw_fd(socket_fd as RawFd) - })); + let socket_fd = syscall::open( + format!(":usb/{}", name), + syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK, + ) + .expect("xhcid: failed to create usb scheme"); + let socket = Arc::new(RefCell::new(unsafe { + 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 mut irq_file = + File::open(format!("irq:{}", irq)).expect("xhcid: failed to open IRQ file"); - let address = unsafe { - syscall::physmap(bar, 65536, PHYSMAP_WRITE | PHYSMAP_NO_CACHE) - .expect("xhcid: failed to map address") - }; - { - let hci = Arc::new(RefCell::new( - Xhci::new(name, address).expect("xhcid: failed to allocate device"), - )); + { + let hci = Arc::new(RefCell::new( + Xhci::new(name, address).expect("xhcid: failed to allocate device"), + )); - hci.borrow_mut().probe().expect("xhcid: failed to probe"); + hci.borrow_mut().probe().expect("xhcid: failed to probe"); - let mut event_queue = - EventQueue::<()>::new().expect("xhcid: failed to create event queue"); + let mut event_queue = + EventQueue::<()>::new().expect("xhcid: failed to create event queue"); - syscall::setrens(0, 0).expect("xhcid: failed to enter null namespace"); + syscall::setrens(0, 0).expect("xhcid: failed to enter null namespace"); - let todo = Arc::new(RefCell::new(Vec::::new())); + let todo = Arc::new(RefCell::new(Vec::::new())); - let hci_irq = hci.clone(); - let socket_irq = socket.clone(); - let todo_irq = todo.clone(); - event_queue - .add(irq_file.as_raw_fd(), move |_| -> Result> { - let mut irq = [0; 8]; - irq_file.read(&mut irq)?; + let hci_irq = hci.clone(); + let socket_irq = socket.clone(); + let todo_irq = todo.clone(); + event_queue + .add(irq_file.as_raw_fd(), move |_| -> Result> { + let mut irq = [0; 8]; + irq_file.read(&mut irq)?; - if hci_irq.borrow_mut().trigger_irq() { - irq_file.write(&mut irq)?; + if hci_irq.borrow_mut().trigger_irq() { + irq_file.write(&mut irq)?; - let mut todo = todo_irq.borrow_mut(); - let mut i = 0; - while i < todo.len() { - let a = todo[i].a; - hci_irq.borrow_mut().handle(&mut todo[i]); - if todo[i].a == (-EWOULDBLOCK) as usize { - todo[i].a = a; - i += 1; - } else { - socket_irq.borrow_mut().write(&mut todo[i])?; - todo.remove(i); - } - } - } - - Ok(None) - }) - .expect("xhcid: failed to catch events on IRQ file"); - - let socket_fd = socket.borrow().as_raw_fd(); - let socket_packet = socket.clone(); - event_queue - .add(socket_fd, move |_| -> Result> { - loop { - let mut packet = Packet::default(); - match socket_packet.borrow_mut().read(&mut packet) { - Ok(0) => break, - Err(err) if err.kind() == io::ErrorKind::WouldBlock => break, - Ok(_) => (), - Err(err) => return Err(err), - } - - let a = packet.a; - hci.borrow_mut().handle(&mut packet); - if packet.a == (-EWOULDBLOCK) as usize { - packet.a = a; - todo.borrow_mut().push(packet); + let mut todo = todo_irq.borrow_mut(); + let mut i = 0; + while i < todo.len() { + let a = todo[i].a; + hci_irq.borrow_mut().handle(&mut todo[i]); + if todo[i].a == (-EWOULDBLOCK) as usize { + todo[i].a = a; + i += 1; } else { - socket_packet.borrow_mut().write(&mut packet)?; + socket_irq.borrow_mut().write(&mut todo[i])?; + todo.remove(i); } } - Ok(None) - }) - .expect("xhcid: failed to catch events on scheme file"); + } - event_queue - .trigger_all(Event { fd: 0, flags: 0 }) - .expect("xhcid: failed to trigger events"); + Ok(None) + }) + .expect("xhcid: failed to catch events on IRQ file"); - event_queue.run().expect("xhcid: failed to handle events"); - } - unsafe { - let _ = syscall::physunmap(address); - } + let socket_fd = socket.borrow().as_raw_fd(); + let socket_packet = socket.clone(); + event_queue + .add(socket_fd, move |_| -> Result> { + loop { + let mut packet = Packet::default(); + match socket_packet.borrow_mut().read(&mut packet) { + Ok(0) => break, + Err(err) if err.kind() == io::ErrorKind::WouldBlock => break, + Ok(_) => (), + Err(err) => return Err(err), + } + + let a = packet.a; + hci.borrow_mut().handle(&mut packet); + if packet.a == (-EWOULDBLOCK) as usize { + packet.a = a; + todo.borrow_mut().push(packet); + } else { + socket_packet.borrow_mut().write(&mut packet)?; + } + } + Ok(None) + }) + .expect("xhcid: failed to catch events on scheme file"); + + event_queue + .trigger_all(Event { fd: 0, flags: 0 }) + .expect("xhcid: failed to trigger events"); + + event_queue.run().expect("xhcid: failed to handle events"); + } + unsafe { + let _ = syscall::physunmap(address); } } diff --git a/xhcid/src/xhci/mod.rs b/xhcid/src/xhci/mod.rs index ed4a0e29e8..530bb30fad 100644 --- a/xhcid/src/xhci/mod.rs +++ b/xhcid/src/xhci/mod.rs @@ -21,7 +21,7 @@ mod operational; mod port; mod ring; mod runtime; -mod scheme; +pub mod scheme; mod trb; use self::capability::CapabilityRegs; @@ -306,7 +306,7 @@ impl Xhci { Ok(cloned_event_trb.event_slot()) } pub fn disable_port_slot(&mut self, slot: u8) -> Result<()> { - self.execute_command("DISABLE_SLOT", |cmd, cycle| cmd.enable_slot(0, cycle))?; + self.execute_command("DISABLE_SLOT", |cmd, cycle| cmd.disable_slot(0, cycle))?; Ok(()) } diff --git a/xhcid/src/xhci/scheme.rs b/xhcid/src/xhci/scheme.rs index e26b032810..20d8e81c9c 100644 --- a/xhcid/src/xhci/scheme.rs +++ b/xhcid/src/xhci/scheme.rs @@ -2003,7 +2003,7 @@ impl Xhci { } } use std::ops::{Add, Div, Rem}; -fn div_round_up(a: T, b: T) -> T +pub fn div_round_up(a: T, b: T) -> T where T: Add + Div + Rem + PartialEq + From + Copy, { diff --git a/xhcid/src/xhci/trb.rs b/xhcid/src/xhci/trb.rs index d6d4cb30e0..4ad02bee37 100644 --- a/xhcid/src/xhci/trb.rs +++ b/xhcid/src/xhci/trb.rs @@ -203,6 +203,15 @@ impl Trb { | (cycle as u32), ); } + pub fn disable_slot(&mut self, slot: u8, cycle: bool) { + self.set( + 0, + 0, + (u32::from(slot) << 24) + | ((TrbType::DisableSlot as u32) << 10) + | u32::from(cycle) + ); + } pub fn address_device(&mut self, slot_id: u8, input_ctx_ptr: usize, bsr: bool, cycle: bool) { assert_eq!(