Allow subdrivers to get the capability structs.

This commit is contained in:
4lDO2
2020-03-15 14:05:19 +01:00
parent 735c55f656
commit fa594155c9
10 changed files with 533 additions and 308 deletions
-1
View File
@@ -1,4 +1,3 @@
use orbclient;
use std::fs::File;
use std::io::Write;
use std::str;
+49 -2
View File
@@ -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<Vec<(PciFeature, FeatureStatus)>> {
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<FeatureStatus> {
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<PciFeatureInfo> {
self.send(&PcidClientRequest::FeatureInfo(feature))?;
match self.recv()? {
PcidClientResponse::FeatureInfo(feat, info) if feat == feature => Ok(info),
other => Err(PcidClientHandleError::InvalidResponse(other)),
}
}
}
+14 -1
View File
@@ -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<usize>, pcid_from_client_read: Option<usize>, 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<State>, config: &Config, bus_num: u8,
};
crate::pci::cap::CapabilitiesIter { inner: crate::pci::cap::CapabilityOffsetsIter::new(header.cap_pointer(), &func) }.collect::<Vec<_>>()
};
println!("PCI DEVICE CAPABILITIES for {}: {:?}", args.iter().map(|string| string.as_ref()).nth(0).unwrap_or("[unknown]"), capabilities);
let func = driver_interface::PciFunction {
bars,
+4 -205
View File
@@ -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<R: ConfigReader>(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<W: ConfigWriter>(&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<W: ConfigWriter>(&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<W: ConfigWriter>(&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<W: ConfigWriter>(&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<W: ConfigWriter>(&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<W: ConfigWriter>(&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),
+1
View File
@@ -14,6 +14,7 @@ mod class;
mod dev;
mod func;
pub mod header;
pub mod msi;
pub struct Pci {
lock: Mutex<()>,
+296
View File
@@ -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<R: ConfigReader>(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<W: ConfigWriter>(&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<W: ConfigWriter>(&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<W: ConfigWriter>(&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<W: ConfigWriter>(&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<W: ConfigWriter>(&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<W: ConfigWriter>(&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<u32>,
pub addr_hi: Mmio<u32>,
pub msg_data: Mmio<u32>,
pub vec_ctl: Mmio<u32>,
}
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()
}
}
+157 -96
View File
@@ -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::<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;
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::<u8>().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::<Packet>::new()));
let todo = Arc::new(RefCell::new(Vec::<Packet>::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<Option<()>> {
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<Option<()>> {
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<Option<()>> {
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<Option<()>> {
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);
}
}
+2 -2
View File
@@ -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(())
}
+1 -1
View File
@@ -2003,7 +2003,7 @@ impl Xhci {
}
}
use std::ops::{Add, Div, Rem};
fn div_round_up<T>(a: T, b: T) -> T
pub fn div_round_up<T>(a: T, b: T) -> T
where
T: Add<Output = T> + Div<Output = T> + Rem<Output = T> + PartialEq + From<u8> + Copy,
{
+9
View File
@@ -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!(