Skip to content

Commit 8b38ed3

Browse files
committed
nvme: mi: dev: Implement PCIe / Configuration {Read,Write}
Signed-off-by: Andrew Jeffery <[email protected]>
1 parent 4deccf7 commit 8b38ed3

File tree

5 files changed

+594
-2
lines changed

5 files changed

+594
-2
lines changed

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use nvme::{
1616
use uuid::Uuid;
1717

1818
pub mod nvme;
19+
mod pcie;
1920
mod wire;
2021

2122
extern crate deku;

src/nvme/mi.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ pub enum ResponseStatus {
8484
InvalidParameter = 0x04,
8585
InvalidCommandSize = 0x05,
8686
InvalidCommandInputDataSize = 0x06,
87+
AccessDenied = 0x07,
8788
}
8889
unsafe impl Discriminant<u8> for ResponseStatus {}
8990

@@ -908,3 +909,40 @@ struct AdminCommandResponseHeader {
908909
cqedw3: u32,
909910
}
910911
impl Encode<16> for AdminCommandResponseHeader {}
912+
913+
// MI v2.0, 7, Figure 146
914+
#[derive(Debug, DekuRead, DekuWrite)]
915+
#[deku(endian = "little")]
916+
struct PcieCommandRequestHeader {
917+
_opcode: u8,
918+
#[deku(seek_from_current = "1")]
919+
ctlid: u16,
920+
#[deku(ctx = "*_opcode")]
921+
op: PcieCommandRequestType,
922+
}
923+
924+
// MI v2.0, 7, Figure 148
925+
#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
926+
#[deku(ctx = "endian: Endian, opcode: u8", id = "opcode", endian = "endian")]
927+
#[repr(u8)]
928+
enum PcieCommandRequestType {
929+
#[deku(id = 0x00)]
930+
ConfigurationRead(PcieConfigurationAccessRequest),
931+
#[deku(id = 0x01)]
932+
ConfigurationWrite(PcieConfigurationAccessRequest),
933+
MemoryRead = 0x02,
934+
MemoryWrite = 0x03,
935+
IoRead = 0x04,
936+
IoWrite = 0x05,
937+
}
938+
unsafe impl Discriminant<u8> for PcieCommandRequestType {}
939+
940+
// MI v2.0, 7, Figure 151-152
941+
#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
942+
#[deku(ctx = "endian: Endian", endian = "endian")]
943+
struct PcieConfigurationAccessRequest {
944+
length: u16,
945+
#[deku(seek_from_current = "2")]
946+
#[deku(pad_bytes_after = "6")]
947+
offset: u16,
948+
}

src/nvme/mi/dev.rs

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ use crate::{
3232
ControllerPropertyFlags, MessageType, NvmSubsystemHealthDataStructureResponse,
3333
NvmSubsystemInformationResponse, NvmeManagementResponse, NvmeMiCommandRequestHeader,
3434
NvmeMiCommandRequestType, NvmeMiDataStructureManagementResponse,
35-
NvmeMiDataStructureRequestType, PciePortDataResponse, PortInformationResponse,
36-
TwoWirePortDataResponse,
35+
NvmeMiDataStructureRequestType, PcieCommandRequestHeader, PciePortDataResponse,
36+
PortInformationResponse, TwoWirePortDataResponse,
3737
},
3838
},
39+
pcie::PciDeviceFunctionConfigurationSpace,
3940
wire::{WireString, WireVec},
4041
};
4142

@@ -121,6 +122,18 @@ impl RequestHandler for MessageHeader {
121122
}
122123
}
123124
}
125+
MessageType::PcieCommand => {
126+
match &PcieCommandRequestHeader::from_bytes((rest, 0)) {
127+
Ok(((rest, _), ch)) => ch.handle(ch, mep, subsys, rest, resp, app).await,
128+
Err(err) => {
129+
debug!(
130+
"Unable to parse PcieCommandRequestHeader from message buffer: {err:?}"
131+
);
132+
// TODO: This is a bad assumption: Can see DekuError::InvalidParam too
133+
Err(ResponseStatus::InvalidCommandSize)
134+
}
135+
}
136+
}
124137
_ => {
125138
debug!("Unimplemented NMINT: {:?}", ctx.nmimt());
126139
Err(ResponseStatus::InternalError)
@@ -2003,6 +2016,82 @@ impl RequestHandler for AdminFormatNvmRequest {
20032016
}
20042017
}
20052018

2019+
impl RequestHandler for PcieCommandRequestHeader {
2020+
type Ctx = PcieCommandRequestHeader;
2021+
2022+
async fn handle<A, C>(
2023+
&self,
2024+
ctx: &Self::Ctx,
2025+
_mep: &mut crate::ManagementEndpoint,
2026+
subsys: &mut crate::Subsystem,
2027+
rest: &[u8],
2028+
resp: &mut C,
2029+
_app: A,
2030+
) -> Result<(), ResponseStatus>
2031+
where
2032+
A: AsyncFnMut(crate::CommandEffect) -> Result<(), CommandEffectError>,
2033+
C: mctp::AsyncRespChannel,
2034+
{
2035+
match &ctx.op {
2036+
super::PcieCommandRequestType::ConfigurationRead(req) => {
2037+
if !rest.is_empty() {
2038+
debug!("Invalid request size for PcieCommand");
2039+
return Err(ResponseStatus::InvalidCommandSize);
2040+
}
2041+
2042+
if req.length != 4096 {
2043+
debug!("Implement length support");
2044+
return Err(ResponseStatus::InternalError);
2045+
}
2046+
2047+
if req.offset != 0 {
2048+
debug!("Implement offset support");
2049+
return Err(ResponseStatus::InternalError);
2050+
}
2051+
2052+
let mh = MessageHeader::respond(MessageType::PcieCommand).encode()?;
2053+
2054+
let status = [0u8; 4]; /* Success */
2055+
2056+
let cr = PciDeviceFunctionConfigurationSpace::builder()
2057+
.vid(subsys.info.pci_vid)
2058+
.did(subsys.info.pci_did)
2059+
.svid(subsys.info.pci_svid)
2060+
.sdid(subsys.info.pci_sdid)
2061+
.build()
2062+
.encode()?;
2063+
2064+
send_response(resp, &[&mh.0, &status, &cr.0]).await;
2065+
Ok(())
2066+
}
2067+
super::PcieCommandRequestType::ConfigurationWrite(req) => {
2068+
let response = if rest.len() == req.length as usize {
2069+
debug!("Unsupported write at {} for {}", req.offset, req.length);
2070+
ResponseStatus::AccessDenied
2071+
} else {
2072+
debug!(
2073+
"Request data size {} does not match requested write size {}",
2074+
rest.len(),
2075+
req.length
2076+
);
2077+
ResponseStatus::InvalidCommandInputDataSize
2078+
};
2079+
2080+
let mh = MessageHeader::respond(MessageType::PcieCommand).encode()?;
2081+
2082+
let status = [response.id(), 0, 0, 0];
2083+
2084+
send_response(resp, &[&mh.0, &status]).await;
2085+
Ok(())
2086+
}
2087+
_ => {
2088+
debug!("Unimplemented OPCODE: {:?}", ctx._opcode);
2089+
Err(ResponseStatus::InternalError)
2090+
}
2091+
}
2092+
}
2093+
}
2094+
20062095
impl crate::ManagementEndpoint {
20072096
fn update(&mut self, subsys: &crate::Subsystem) {
20082097
assert!(subsys.ctlrs.len() <= self.mecss.len());

src/pcie.rs

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
/*
3+
* Copyright (c) 2025 Code Construct
4+
*/
5+
use deku::ctx::Endian;
6+
use deku::{DekuRead, DekuWrite};
7+
8+
// PCIe Base 4.0r1.0, 7.5.1.2, Figure 7-10
9+
#[derive(Debug, DekuRead, DekuWrite)]
10+
#[deku(endian = "little")]
11+
pub struct PciDeviceFunctionConfigurationSpace {
12+
vid: u16,
13+
did: u16,
14+
cmd: u16,
15+
sts: u16,
16+
rid: u8,
17+
#[deku(bytes = "3")]
18+
cc: u32,
19+
cls: u8,
20+
lt: u8,
21+
ht: u8,
22+
bist: u8,
23+
bars: [u32; 6],
24+
cis: u32,
25+
svid: u16,
26+
sdid: u16,
27+
rom: u32,
28+
cap: u8,
29+
#[deku(seek_from_current = "7")]
30+
il: u8,
31+
ip: u8,
32+
min_gnt: u8,
33+
max_lat: u8,
34+
caps: [PciCapabilityType; 2],
35+
}
36+
impl crate::Encode<4096> for PciDeviceFunctionConfigurationSpace {}
37+
38+
impl PciDeviceFunctionConfigurationSpace {
39+
pub fn new() -> Self {
40+
Self {
41+
vid: 0xffff,
42+
did: 0xffff,
43+
cmd: 0,
44+
sts: 0x0010,
45+
rid: 0,
46+
cc: 0x010803,
47+
cls: 0,
48+
lt: 0,
49+
ht: 0,
50+
bist: 0,
51+
bars: [0; 6],
52+
cis: 0,
53+
svid: 0xffff,
54+
sdid: 0xffff,
55+
rom: 0,
56+
cap: 0x40,
57+
il: 0,
58+
ip: 0,
59+
min_gnt: 0,
60+
max_lat: 0,
61+
caps: [
62+
PciCapabilityType::PciPowerManagement(PciPowerManagementCapability {
63+
next: 0x48,
64+
pmc: {
65+
PowerManagementCapabilities {
66+
version: 3,
67+
pme_clock: false,
68+
ready_d0: true,
69+
dsi: false,
70+
aux_current: 0,
71+
d1: false,
72+
d2: false,
73+
pme: 0,
74+
}
75+
}
76+
.into(),
77+
pmcsr: 0,
78+
data: 0,
79+
}),
80+
PciCapabilityType::Pcie(PcieCapability::default()),
81+
],
82+
}
83+
}
84+
85+
pub fn builder() -> PciDeviceFunctionConfigurationSpaceBuilder {
86+
Default::default()
87+
}
88+
}
89+
90+
impl Default for PciDeviceFunctionConfigurationSpace {
91+
fn default() -> Self {
92+
PciDeviceFunctionConfigurationSpace::new()
93+
}
94+
}
95+
96+
pub struct PciDeviceFunctionConfigurationSpaceBuilder {
97+
vid: u16,
98+
did: u16,
99+
svid: u16,
100+
sdid: u16,
101+
}
102+
103+
impl Default for PciDeviceFunctionConfigurationSpaceBuilder {
104+
fn default() -> Self {
105+
Self {
106+
vid: 0xffff,
107+
did: 0xffff,
108+
svid: 0xffff,
109+
sdid: 0xffff,
110+
}
111+
}
112+
}
113+
114+
impl PciDeviceFunctionConfigurationSpaceBuilder {
115+
pub fn vid(&mut self, vid: u16) -> &mut Self {
116+
self.vid = vid;
117+
self
118+
}
119+
120+
pub fn did(&mut self, did: u16) -> &mut Self {
121+
self.did = did;
122+
self
123+
}
124+
125+
pub fn svid(&mut self, svid: u16) -> &mut Self {
126+
self.svid = svid;
127+
self
128+
}
129+
130+
pub fn sdid(&mut self, sdid: u16) -> &mut Self {
131+
self.sdid = sdid;
132+
self
133+
}
134+
135+
pub fn build(&self) -> PciDeviceFunctionConfigurationSpace {
136+
PciDeviceFunctionConfigurationSpace {
137+
vid: self.vid,
138+
did: self.did,
139+
svid: self.svid,
140+
sdid: self.sdid,
141+
..Default::default()
142+
}
143+
}
144+
}
145+
146+
#[derive(Debug)]
147+
pub struct PowerManagementCapabilities {
148+
version: u8,
149+
pme_clock: bool,
150+
ready_d0: bool,
151+
dsi: bool,
152+
aux_current: u8,
153+
d1: bool,
154+
d2: bool,
155+
pme: u8,
156+
}
157+
158+
impl From<PowerManagementCapabilities> for u16 {
159+
fn from(value: PowerManagementCapabilities) -> Self {
160+
((value.pme as u16 & 0xf) << 11)
161+
| ((value.d2 as u16) << 10)
162+
| ((value.d1 as u16) << 9)
163+
| ((value.aux_current as u16 & 0x7) << 6)
164+
| ((value.dsi as u16) << 5)
165+
| ((value.ready_d0 as u16) << 4)
166+
| ((value.pme_clock as u16) << 3)
167+
| (value.version as u16 & 0x7)
168+
}
169+
}
170+
171+
#[derive(Debug, DekuRead, DekuWrite)]
172+
#[deku(ctx = "endian: Endian", endian = "endian")]
173+
pub struct PciPowerManagementCapability {
174+
next: u8,
175+
pmc: u16,
176+
pmcsr: u16,
177+
#[deku(seek_from_current = "1")]
178+
data: u8,
179+
}
180+
181+
#[derive(Debug, Default, DekuRead, DekuWrite)]
182+
#[deku(ctx = "endian: Endian", endian = "endian")]
183+
pub struct PcieCapability {
184+
next: u8,
185+
pciec: u16,
186+
devcap: u32,
187+
devctl: u16,
188+
devsts: u16,
189+
linkcap: u32,
190+
linkctl: u16,
191+
linksts: u16,
192+
slotctl: u16,
193+
slotsts: u16,
194+
rootctl: u16,
195+
rootsts: u16,
196+
devcap2: u32,
197+
devctl2: u16,
198+
devsts2: u16,
199+
linkcap2: u32,
200+
linkctl2: u16,
201+
linksts2: u16,
202+
slotcap2: u32,
203+
slotctl2: u16,
204+
slotsts2: u16,
205+
}
206+
207+
#[derive(Debug, DekuRead, DekuWrite)]
208+
#[deku(ctx = "endian: Endian", endian = "endian", id_type = "u8")]
209+
#[repr(u8)]
210+
pub enum PciCapabilityType {
211+
#[deku(id = "0x01")]
212+
PciPowerManagement(PciPowerManagementCapability),
213+
#[deku(id = "0x10")]
214+
Pcie(PcieCapability),
215+
}
216+
unsafe impl crate::Discriminant<u8> for PciCapabilityType {}

0 commit comments

Comments
 (0)