Skip to content

Commit bc724db

Browse files
authored
Merge pull request #3 from alesharik/master
PCI capabilities and status register support
2 parents a2e1958 + d29b981 commit bc724db

File tree

4 files changed

+521
-9
lines changed

4 files changed

+521
-9
lines changed

src/capability/mod.rs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
use crate::{ConfigRegionAccess, PciAddress};
2+
use bit_field::BitField;
3+
use core::fmt::Formatter;
4+
5+
mod msi;
6+
7+
pub use msi::{MsiCapability, MultipleMessageSupport, TriggerMode};
8+
9+
#[derive(Clone)]
10+
pub struct PciCapabilityAddress {
11+
pub address: PciAddress,
12+
pub offset: u16,
13+
}
14+
15+
impl core::fmt::Debug for PciCapabilityAddress {
16+
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
17+
write!(f, "{}, offset: {:02x}", self.address, self.offset)
18+
}
19+
}
20+
21+
/// PCI capabilities
22+
#[derive(Clone, Debug)]
23+
pub enum PciCapability {
24+
/// Power management capability, Cap ID = `0x01`
25+
PowerManagement(PciCapabilityAddress),
26+
/// Accelerated graphics port capability, Cap ID = `0x02`
27+
AcceleratedGraphicsPort(PciCapabilityAddress),
28+
/// Vital product data capability, Cap ID = `0x3`
29+
VitalProductData(PciCapabilityAddress),
30+
/// Slot identification capability, Cap ID = `0x04`
31+
SlotIdentification(PciCapabilityAddress),
32+
/// Message signalling interrupts capability, Cap ID = `0x05`
33+
Msi(MsiCapability),
34+
/// CompactPCI HotSwap capability, Cap ID = `0x06`
35+
CompactPCIHotswap(PciCapabilityAddress),
36+
/// PCI-X capability, Cap ID = `0x07`
37+
PciX(PciCapabilityAddress),
38+
/// HyperTransport capability, Cap ID = `0x08`
39+
HyperTransport(PciCapabilityAddress),
40+
/// Vendor-specific capability, Cap ID = `0x09`
41+
Vendor(PciCapabilityAddress),
42+
/// Debug port capability, Cap ID = `0x0A`
43+
DebugPort(PciCapabilityAddress),
44+
/// CompactPCI Central Resource Control capability, Cap ID = `0x0B`
45+
CompactPCICentralResourceControl(PciCapabilityAddress),
46+
/// PCI Standard Hot-Plug Controller capability, Cap ID = `0x0C`
47+
PciHotPlugControl(PciCapabilityAddress),
48+
/// Bridge subsystem vendor/device ID capability, Cap ID = `0x0D`
49+
BridgeSubsystemVendorId(PciCapabilityAddress),
50+
/// AGP Target PCI-PCI bridge capability, Cap ID = `0x0E`
51+
AGP3(PciCapabilityAddress),
52+
/// PCI Express capability, Cap ID = `0x10`
53+
PciExpress(PciCapabilityAddress),
54+
/// MSI-X capability, Cap ID = `0x11`
55+
MsiX(PciCapabilityAddress),
56+
/// Unknown capability
57+
Unknown {
58+
address: PciCapabilityAddress,
59+
id: u8,
60+
},
61+
}
62+
63+
impl PciCapability {
64+
fn parse(id: u8, address: PciCapabilityAddress, extension: u16) -> Option<PciCapability> {
65+
match id {
66+
0x00 => None, // null capability
67+
0x01 => Some(PciCapability::PowerManagement(address)),
68+
0x02 => Some(PciCapability::AcceleratedGraphicsPort(address)),
69+
0x03 => Some(PciCapability::VitalProductData(address)),
70+
0x04 => Some(PciCapability::SlotIdentification(address)),
71+
0x05 => Some(PciCapability::Msi(MsiCapability::new(address, extension))),
72+
0x06 => Some(PciCapability::CompactPCIHotswap(address)),
73+
0x07 => Some(PciCapability::PciX(address)),
74+
0x08 => Some(PciCapability::HyperTransport(address)),
75+
0x09 => Some(PciCapability::Vendor(address)),
76+
0x0A => Some(PciCapability::DebugPort(address)),
77+
0x0B => Some(PciCapability::CompactPCICentralResourceControl(address)),
78+
0x0C => Some(PciCapability::PciHotPlugControl(address)),
79+
0x0D => Some(PciCapability::BridgeSubsystemVendorId(address)),
80+
0x0E => Some(PciCapability::AGP3(address)),
81+
0x10 => Some(PciCapability::PciExpress(address)),
82+
0x11 => Some(PciCapability::MsiX(address)),
83+
_ => Some(PciCapability::Unknown { address, id }),
84+
}
85+
}
86+
}
87+
88+
pub struct CapabilityIterator<'a, T: ConfigRegionAccess> {
89+
address: PciAddress,
90+
offset: u16,
91+
access: &'a T,
92+
}
93+
94+
impl<'a, T: ConfigRegionAccess> CapabilityIterator<'a, T> {
95+
pub(crate) fn new(
96+
address: PciAddress,
97+
offset: u16,
98+
access: &'a T,
99+
) -> CapabilityIterator<'a, T> {
100+
CapabilityIterator {
101+
address,
102+
offset,
103+
access,
104+
}
105+
}
106+
}
107+
108+
impl<'a, T: ConfigRegionAccess> Iterator for CapabilityIterator<'a, T> {
109+
type Item = PciCapability;
110+
111+
fn next(&mut self) -> Option<Self::Item> {
112+
loop {
113+
if self.offset == 0 {
114+
return None;
115+
}
116+
let data = unsafe { self.access.read(self.address, self.offset) };
117+
let next_ptr = data.get_bits(8..16);
118+
let id = data.get_bits(0..8);
119+
let extension = data.get_bits(16..32) as u16;
120+
let cap = PciCapability::parse(
121+
id as u8,
122+
PciCapabilityAddress {
123+
address: self.address,
124+
offset: self.offset,
125+
},
126+
extension,
127+
);
128+
self.offset = next_ptr as u16;
129+
if let Some(cap) = cap {
130+
return Some(cap);
131+
}
132+
}
133+
}
134+
}

src/capability/msi.rs

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
use crate::{capability::PciCapabilityAddress, ConfigRegionAccess};
2+
use bit_field::BitField;
3+
use core::convert::TryFrom;
4+
5+
/// Specifies how many MSI interrupts one device can have.
6+
/// Device will modify lower bits of interrupt vector to send multiple messages, so interrupt block
7+
/// must be aligned accordingly.
8+
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
9+
pub enum MultipleMessageSupport {
10+
/// Device can send 1 interrupt. No interrupt vector modification is happening here
11+
Int1 = 0b000,
12+
/// Device can send 2 interrupts
13+
Int2 = 0b001,
14+
/// Device can send 4 interrupts
15+
Int4 = 0b010,
16+
/// Device can send 8 interrupts
17+
Int8 = 0b011,
18+
/// Device can send 16 interrupts
19+
Int16 = 0b100,
20+
/// Device can send 32 interrupts
21+
Int32 = 0b101,
22+
}
23+
24+
impl TryFrom<u8> for MultipleMessageSupport {
25+
type Error = ();
26+
27+
fn try_from(value: u8) -> Result<Self, Self::Error> {
28+
match value {
29+
0b000 => Ok(MultipleMessageSupport::Int1),
30+
0b001 => Ok(MultipleMessageSupport::Int2),
31+
0b010 => Ok(MultipleMessageSupport::Int4),
32+
0b011 => Ok(MultipleMessageSupport::Int8),
33+
0b100 => Ok(MultipleMessageSupport::Int16),
34+
0b101 => Ok(MultipleMessageSupport::Int32),
35+
_ => Err(()),
36+
}
37+
}
38+
}
39+
40+
/// When device should trigger the interrupt
41+
#[derive(Debug)]
42+
pub enum TriggerMode {
43+
Edge = 0b00,
44+
LevelAssert = 0b11,
45+
LevelDeassert = 0b10,
46+
}
47+
48+
#[derive(Debug, Clone)]
49+
pub struct MsiCapability {
50+
address: PciCapabilityAddress,
51+
per_vector_masking: bool,
52+
is_64bit: bool,
53+
multiple_message_capable: MultipleMessageSupport,
54+
}
55+
56+
impl MsiCapability {
57+
pub(crate) fn new(address: PciCapabilityAddress, control: u16) -> MsiCapability {
58+
MsiCapability {
59+
address,
60+
per_vector_masking: control.get_bit(8),
61+
is_64bit: control.get_bit(7),
62+
multiple_message_capable:
63+
MultipleMessageSupport::try_from(control.get_bits(1..4) as u8)
64+
.unwrap_or(MultipleMessageSupport::Int1),
65+
}
66+
}
67+
68+
/// Does device supports masking individual vectors?
69+
#[inline]
70+
pub fn has_per_vector_masking(&self) -> bool {
71+
self.per_vector_masking
72+
}
73+
74+
/// Is device using 64-bit addressing?
75+
#[inline]
76+
pub fn is_64bit(&self) -> bool {
77+
self.is_64bit
78+
}
79+
80+
/// How many interrupts this device has?
81+
#[inline]
82+
pub fn get_multiple_message_capable(&self) -> MultipleMessageSupport {
83+
self.multiple_message_capable
84+
}
85+
86+
/// Is MSI capability enabled?
87+
pub fn is_enabled(&self, access: &impl ConfigRegionAccess) -> bool {
88+
let reg = unsafe { access.read(self.address.address, self.address.offset) };
89+
reg.get_bit(0)
90+
}
91+
92+
/// Enable or disable MSI capability
93+
pub fn set_enabled(&self, enabled: bool, access: &impl ConfigRegionAccess) {
94+
let mut reg = unsafe { access.read(self.address.address, self.address.offset) };
95+
reg.set_bit(0, enabled);
96+
unsafe { access.write(self.address.address, self.address.offset, reg) };
97+
}
98+
99+
/// Set how many interrupts the device will use. If requested count is bigger than supported count,
100+
/// the second will be used.
101+
pub fn set_multiple_message_enable(
102+
&self,
103+
data: MultipleMessageSupport,
104+
access: &impl ConfigRegionAccess,
105+
) {
106+
let mut reg = unsafe { access.read(self.address.address, self.address.offset) };
107+
reg.set_bits(4..7, (data.min(self.multiple_message_capable)) as u32);
108+
unsafe { access.write(self.address.address, self.address.offset, reg) };
109+
}
110+
111+
/// Return how many interrupts the device is using
112+
pub fn get_multiple_message_enable(
113+
&self,
114+
access: &impl ConfigRegionAccess,
115+
) -> MultipleMessageSupport {
116+
let reg = unsafe { access.read(self.address.address, self.address.offset) };
117+
MultipleMessageSupport::try_from(reg.get_bits(4..7) as u8)
118+
.unwrap_or(MultipleMessageSupport::Int1)
119+
}
120+
121+
/// Set where the interrupts will be sent to
122+
///
123+
/// # Arguments
124+
/// * `address` - Target Local APIC address (if not changed, can be calculated with `0xFEE00000 | (processor << 12)`)
125+
/// * `vector` - Which interrupt vector should be triggered on LAPIC
126+
/// * `trigger_mode` - When interrupt should be triggered
127+
/// * `access` - PCI Configuration Space accessor
128+
pub fn set_message_info(
129+
&self,
130+
address: u32,
131+
vector: u8,
132+
trigger_mode: TriggerMode,
133+
access: &impl ConfigRegionAccess,
134+
) {
135+
unsafe { access.write(self.address.address, self.address.offset + 0x4, address) }
136+
let data_offset = if self.is_64bit { 0xC } else { 0x8 };
137+
let mut data =
138+
unsafe { access.read(self.address.address, self.address.offset + data_offset) };
139+
data.set_bits(0..8, vector as u32);
140+
data.set_bits(14..16, trigger_mode as u32);
141+
unsafe {
142+
access.write(
143+
self.address.address,
144+
self.address.offset + data_offset,
145+
data,
146+
)
147+
}
148+
}
149+
150+
/// Get interrupt mask
151+
///
152+
/// # Note
153+
/// Only supported on when device supports 64-bit addressing and per-vector masking. Otherwise
154+
/// returns `0`
155+
pub fn get_message_mask(&self, access: &impl ConfigRegionAccess) -> u32 {
156+
if self.is_64bit && self.per_vector_masking {
157+
unsafe { access.read(self.address.address, self.address.offset + 0x10) }
158+
} else {
159+
0
160+
}
161+
}
162+
163+
/// Set interrupt mask
164+
///
165+
/// # Note
166+
/// Only supported on when device supports 64-bit addressing and per-vector masking. Otherwise
167+
/// will do nothing
168+
pub fn set_message_mask(&self, access: &impl ConfigRegionAccess, mask: u32) {
169+
if self.is_64bit && self.per_vector_masking {
170+
unsafe { access.write(self.address.address, self.address.offset + 0x10, mask) }
171+
}
172+
}
173+
174+
/// Get pending interrupts
175+
///
176+
/// # Note
177+
/// Only supported on when device supports 64-bit addressing. Otherwise will return `0`
178+
pub fn get_pending(&self, access: &impl ConfigRegionAccess) -> u32 {
179+
if self.is_64bit {
180+
unsafe { access.read(self.address.address, self.address.offset + 0x14) }
181+
} else {
182+
0
183+
}
184+
}
185+
}

0 commit comments

Comments
 (0)