8f1549e284
This allows a single PCI daemon to run on the whole system, prevents multiple drivers from claiming the same PCI device and makes it possible for userspace to enumerate all available PCI devices. In the future this will enable an lspci tool, possibly PCIe hot plugging and more. Co-Authored-By: 4lDO2 <4lDO2@protonmail.com>
284 lines
13 KiB
Rust
284 lines
13 KiB
Rust
use std::sync::Arc;
|
|
|
|
use pci_types::capability::{MultipleMessageSupport, PciCapability};
|
|
use pci_types::{ConfigRegionAccess, EndpointHeader};
|
|
use pcid_interface::PciFunction;
|
|
|
|
use crate::State;
|
|
|
|
pub struct DriverHandler<'a> {
|
|
func: PciFunction,
|
|
endpoint_header: &'a mut EndpointHeader,
|
|
capabilities: Vec<PciCapability>,
|
|
|
|
state: Arc<State>,
|
|
}
|
|
|
|
impl<'a> DriverHandler<'a> {
|
|
pub fn new(
|
|
func: PciFunction,
|
|
endpoint_header: &'a mut EndpointHeader,
|
|
capabilities: Vec<PciCapability>,
|
|
state: Arc<State>,
|
|
) -> Self {
|
|
DriverHandler {
|
|
func,
|
|
endpoint_header,
|
|
capabilities,
|
|
state,
|
|
}
|
|
}
|
|
|
|
pub fn respond(
|
|
&mut self,
|
|
request: pcid_interface::PcidClientRequest,
|
|
) -> pcid_interface::PcidClientResponse {
|
|
use pcid_interface::*;
|
|
|
|
#[forbid(non_exhaustive_omitted_patterns)]
|
|
match request {
|
|
PcidClientRequest::EnableDevice => {
|
|
self.func.legacy_interrupt_line =
|
|
crate::enable_function(&self.state, &mut self.endpoint_header);
|
|
|
|
PcidClientResponse::EnabledDevice
|
|
}
|
|
PcidClientRequest::RequestVendorCapabilities => PcidClientResponse::VendorCapabilities(
|
|
self.capabilities
|
|
.iter()
|
|
.filter_map(|capability| match capability {
|
|
PciCapability::Vendor(addr) => unsafe {
|
|
Some(VendorSpecificCapability::parse(*addr, &self.state.pcie))
|
|
},
|
|
_ => None,
|
|
})
|
|
.collect::<Vec<_>>(),
|
|
),
|
|
PcidClientRequest::RequestConfig => {
|
|
PcidClientResponse::Config(SubdriverArguments { func: self.func })
|
|
}
|
|
PcidClientRequest::RequestFeatures => PcidClientResponse::AllFeatures(
|
|
self.capabilities
|
|
.iter()
|
|
.filter_map(|capability| match capability {
|
|
PciCapability::Msi(_) => Some(PciFeature::Msi),
|
|
PciCapability::MsiX(_) => Some(PciFeature::MsiX),
|
|
_ => None,
|
|
})
|
|
.collect(),
|
|
),
|
|
PcidClientRequest::EnableFeature(feature) => {
|
|
match feature {
|
|
PciFeature::Msi => {
|
|
if let Some(msix_capability) =
|
|
self.capabilities
|
|
.iter_mut()
|
|
.find_map(|capability| match capability {
|
|
PciCapability::MsiX(cap) => Some(cap),
|
|
_ => None,
|
|
})
|
|
{
|
|
// If MSI-X is supported disable it before enabling MSI as they can't be
|
|
// active at the same time.
|
|
msix_capability.set_enabled(false, &self.state.pcie);
|
|
}
|
|
|
|
let capability = match self.capabilities.iter_mut().find_map(|capability| {
|
|
match capability {
|
|
PciCapability::Msi(cap) => Some(cap),
|
|
_ => None,
|
|
}
|
|
}) {
|
|
Some(capability) => capability,
|
|
None => {
|
|
return PcidClientResponse::Error(
|
|
PcidServerResponseError::NonexistentFeature(feature),
|
|
)
|
|
}
|
|
};
|
|
capability.set_enabled(true, &self.state.pcie);
|
|
PcidClientResponse::FeatureEnabled(feature)
|
|
}
|
|
PciFeature::MsiX => {
|
|
if let Some(msi_capability) =
|
|
self.capabilities
|
|
.iter_mut()
|
|
.find_map(|capability| match capability {
|
|
PciCapability::Msi(cap) => Some(cap),
|
|
_ => None,
|
|
})
|
|
{
|
|
// If MSI is supported disable it before enabling MSI-X as they can't be
|
|
// active at the same time.
|
|
msi_capability.set_enabled(false, &self.state.pcie);
|
|
}
|
|
|
|
let capability = match self.capabilities.iter_mut().find_map(|capability| {
|
|
match capability {
|
|
PciCapability::MsiX(cap) => Some(cap),
|
|
_ => None,
|
|
}
|
|
}) {
|
|
Some(capability) => capability,
|
|
None => {
|
|
return PcidClientResponse::Error(
|
|
PcidServerResponseError::NonexistentFeature(feature),
|
|
)
|
|
}
|
|
};
|
|
capability.set_enabled(true, &self.state.pcie);
|
|
PcidClientResponse::FeatureEnabled(feature)
|
|
}
|
|
}
|
|
}
|
|
PcidClientRequest::FeatureInfo(feature) => PcidClientResponse::FeatureInfo(
|
|
feature,
|
|
match feature {
|
|
PciFeature::Msi => {
|
|
if let Some(info) =
|
|
self.capabilities
|
|
.iter()
|
|
.find_map(|capability| match capability {
|
|
PciCapability::Msi(cap) => Some(cap),
|
|
_ => None,
|
|
})
|
|
{
|
|
PciFeatureInfo::Msi(msi::MsiInfo {
|
|
log2_multiple_message_capable: info.multiple_message_capable()
|
|
as u8,
|
|
is_64bit: info.is_64bit(),
|
|
has_per_vector_masking: info.has_per_vector_masking(),
|
|
})
|
|
} else {
|
|
return PcidClientResponse::Error(
|
|
PcidServerResponseError::NonexistentFeature(feature),
|
|
);
|
|
}
|
|
}
|
|
PciFeature::MsiX => {
|
|
if let Some(info) =
|
|
self.capabilities
|
|
.iter()
|
|
.find_map(|capability| match capability {
|
|
PciCapability::MsiX(cap) => Some(cap),
|
|
_ => None,
|
|
})
|
|
{
|
|
PciFeatureInfo::MsiX(msi::MsixInfo {
|
|
table_bar: info.table_bar(),
|
|
table_offset: info.table_offset(),
|
|
table_size: info.table_size(),
|
|
pba_bar: info.pba_bar(),
|
|
pba_offset: info.pba_offset(),
|
|
})
|
|
} else {
|
|
return PcidClientResponse::Error(
|
|
PcidServerResponseError::NonexistentFeature(feature),
|
|
);
|
|
}
|
|
}
|
|
},
|
|
),
|
|
PcidClientRequest::SetFeatureInfo(info_to_set) => match info_to_set {
|
|
SetFeatureInfo::Msi(info_to_set) => {
|
|
if let Some(info) =
|
|
self.capabilities
|
|
.iter_mut()
|
|
.find_map(|capability| match capability {
|
|
PciCapability::Msi(cap) => Some(cap),
|
|
_ => None,
|
|
})
|
|
{
|
|
if let Some(mme) = info_to_set.multi_message_enable {
|
|
if (info.multiple_message_capable() as u8) < mme {
|
|
return PcidClientResponse::Error(
|
|
PcidServerResponseError::InvalidBitPattern,
|
|
);
|
|
}
|
|
info.set_multiple_message_enable(
|
|
match mme {
|
|
0 => MultipleMessageSupport::Int1,
|
|
1 => MultipleMessageSupport::Int2,
|
|
2 => MultipleMessageSupport::Int4,
|
|
3 => MultipleMessageSupport::Int8,
|
|
4 => MultipleMessageSupport::Int16,
|
|
5 => MultipleMessageSupport::Int32,
|
|
_ => {
|
|
return PcidClientResponse::Error(
|
|
PcidServerResponseError::InvalidBitPattern,
|
|
)
|
|
}
|
|
},
|
|
&self.state.pcie,
|
|
);
|
|
}
|
|
if let Some(message_addr_and_data) = info_to_set.message_address_and_data {
|
|
let message_addr = message_addr_and_data.addr;
|
|
if message_addr & 0b11 != 0 {
|
|
return PcidClientResponse::Error(
|
|
PcidServerResponseError::InvalidBitPattern,
|
|
);
|
|
}
|
|
if message_addr_and_data.data
|
|
& ((1 << info.multiple_message_enable(&self.state.pcie) as u8) - 1)
|
|
!= 0
|
|
{
|
|
return PcidClientResponse::Error(
|
|
PcidServerResponseError::InvalidBitPattern,
|
|
);
|
|
}
|
|
info.set_message_info(
|
|
message_addr,
|
|
message_addr_and_data
|
|
.data
|
|
.try_into()
|
|
.expect("pcid: MSI message data too big"),
|
|
&self.state.pcie,
|
|
);
|
|
}
|
|
if let Some(mask_bits) = info_to_set.mask_bits {
|
|
info.set_message_mask(mask_bits, &self.state.pcie);
|
|
}
|
|
PcidClientResponse::SetFeatureInfo(PciFeature::Msi)
|
|
} else {
|
|
return PcidClientResponse::Error(
|
|
PcidServerResponseError::NonexistentFeature(PciFeature::Msi),
|
|
);
|
|
}
|
|
}
|
|
SetFeatureInfo::MsiX { function_mask } => {
|
|
if let Some(info) =
|
|
self.capabilities
|
|
.iter_mut()
|
|
.find_map(|capability| match capability {
|
|
PciCapability::MsiX(cap) => Some(cap),
|
|
_ => None,
|
|
})
|
|
{
|
|
if let Some(mask) = function_mask {
|
|
info.set_function_mask(mask, &self.state.pcie);
|
|
}
|
|
PcidClientResponse::SetFeatureInfo(PciFeature::MsiX)
|
|
} else {
|
|
return PcidClientResponse::Error(
|
|
PcidServerResponseError::NonexistentFeature(PciFeature::MsiX),
|
|
);
|
|
}
|
|
}
|
|
_ => unreachable!(),
|
|
},
|
|
PcidClientRequest::ReadConfig(offset) => {
|
|
let value = unsafe { self.state.pcie.read(self.func.addr, offset) };
|
|
return PcidClientResponse::ReadConfig(value);
|
|
}
|
|
PcidClientRequest::WriteConfig(offset, value) => {
|
|
unsafe {
|
|
self.state.pcie.write(self.func.addr, offset, value);
|
|
}
|
|
return PcidClientResponse::WriteConfig;
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|