Allow subdrivers to get the capability structs.
This commit is contained in:
@@ -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
@@ -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
@@ -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),
|
||||
|
||||
@@ -14,6 +14,7 @@ mod class;
|
||||
mod dev;
|
||||
mod func;
|
||||
pub mod header;
|
||||
pub mod msi;
|
||||
|
||||
pub struct Pci {
|
||||
lock: Mutex<()>,
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user