nvme_mi_dev/nvme/
mi.rs

1// SPDX-License-Identifier: GPL-3.0-only
2/*
3 * Copyright (c) 2025 Code Construct
4 */
5
6use deku::ctx::{BitSize, Endian, Order};
7use deku::{DekuError, DekuRead, DekuWrite};
8use flagset::{FlagSet, flags};
9use log::debug;
10
11use crate::nvme::{
12    AdminFormatNvmConfiguration, AdminNamespaceAttachmentSelect, AdminNamespaceManagementSelect,
13    AdminSanitizeConfiguration, ControllerListRequest,
14};
15use crate::wire::{WireFlagSet, WireVec};
16use crate::{CommandEffectError, Discriminant, Encode, MAX_CONTROLLERS};
17
18use super::{AdminGetLogPageLidRequestType, AdminIdentifyCnsRequestType};
19
20pub mod dev;
21
22// MI v2.0, 3.1.1, Figure 20, NMIMT
23#[derive(Clone, Copy, Debug, DekuRead, DekuWrite, PartialEq, Eq)]
24#[deku(
25    bits = "4",
26    bit_order = "order",
27    ctx = "endian: Endian, order: Order",
28    endian = "endian",
29    id_type = "u8"
30)]
31#[repr(u8)]
32enum MessageType {
33    ControlPrimitive = 0x00,
34    NvmeMiCommand = 0x01,
35    NvmeAdminCommand = 0x02,
36    PcieCommand = 0x04,
37    AsynchronousEvent = 0x05,
38}
39
40// MI v2.0, 3.1.1, Figure 20
41#[derive(Debug, DekuRead, DekuWrite)]
42#[deku(bit_order = "lsb", endian = "little")]
43struct MessageHeader {
44    #[deku(bits = "1", pad_bits_after = "2")]
45    csi: u8,
46    nmimt: MessageType,
47    #[deku(bits = "1", pad_bytes_after = "2")]
48    ror: bool,
49}
50impl Encode<3> for MessageHeader {}
51
52impl MessageHeader {
53    fn respond(nmimt: MessageType) -> Self {
54        Self {
55            csi: 0,
56            nmimt,
57            ror: true,
58        }
59    }
60}
61// MI v2.0, 4.1.2, Figure 29
62#[derive(Debug, DekuRead, DekuWrite, PartialEq)]
63#[deku(endian = "endian", ctx = "endian: Endian", id_type = "u8")]
64#[repr(u8)]
65pub enum ResponseStatus {
66    Success = 0x00,
67    InternalError = 0x02,
68    InvalidCommandOpcode = 0x03,
69    InvalidParameter = 0x04,
70    InvalidCommandSize = 0x05,
71    InvalidCommandInputDataSize = 0x06,
72    AccessDenied = 0x07,
73}
74unsafe impl Discriminant<u8> for ResponseStatus {}
75
76impl From<DekuError> for ResponseStatus {
77    fn from(err: DekuError) -> Self {
78        debug!("Codec operation failed: {err}");
79        Self::InternalError
80    }
81}
82
83impl From<()> for ResponseStatus {
84    fn from(_: ()) -> Self {
85        Self::InternalError
86    }
87}
88
89impl From<CommandEffectError> for ResponseStatus {
90    fn from(value: CommandEffectError) -> Self {
91        debug!("Failed to apply command effect: {value:?}");
92        Self::InternalError
93    }
94}
95
96// MI v2.0, 5, Figure 67
97#[derive(Debug, DekuRead, DekuWrite)]
98#[deku(endian = "little")]
99struct NvmeMiCommandRequestHeader {
100    #[deku(pad_bytes_after = "3")]
101    #[deku(update = "self.body.id()")]
102    opcode: u8,
103    #[deku(ctx = "*opcode")]
104    body: NvmeMiCommandRequestType,
105}
106impl Encode<4> for NvmeMiCommandRequestHeader {}
107
108// MI v2.0, 5, Figure 68
109#[derive(Debug, DekuRead, DekuWrite, PartialEq, Eq)]
110#[deku(ctx = "endian: Endian, opcode: u8", id = "opcode", endian = "endian")]
111#[repr(u8)]
112enum NvmeMiCommandRequestType {
113    #[deku(id = "0x00")]
114    ReadNvmeMiDataStructure(NvmeMiDataStructureRequest),
115    #[deku(id = "0x01")]
116    NvmSubsystemHealthStatusPoll(NvmSubsystemHealthStatusPollRequest),
117    #[deku(id = "0x02")]
118    ControllerHealthStatusPoll(ControllerHealthStatusPollRequest),
119    #[deku(id = "0x03")]
120    ConfigurationSet(NvmeMiConfigurationSetRequest),
121    #[deku(id = "0x04")]
122    ConfigurationGet(NvmeMiConfigurationGetRequest),
123    VpdRead = 0x05,
124    VpdWrite = 0x06,
125    Reset = 0x07,
126    SesReceive = 0x08,
127    SesSend = 0x09,
128    ManagementEndpointBufferRead = 0x0a,
129    ManagementEndpointBufferWrite = 0x0b,
130    Shutdown = 0x0c,
131}
132unsafe impl Discriminant<u8> for NvmeMiCommandRequestType {}
133
134// MI v2.0, 5, Figure 71
135#[derive(Debug, DekuRead, DekuWrite)]
136#[deku(endian = "little")]
137struct NvmeManagementResponse {
138    #[deku(pad_bytes_after = "3")]
139    status: ResponseStatus,
140}
141impl Encode<4> for NvmeManagementResponse {}
142
143// MI v2.0, 5, Figure 73
144#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
145#[deku(ctx = "endian: Endian", endian = "endian")]
146struct NvmeMiConfigurationGetRequest {
147    body: NvmeMiConfigurationIdentifierRequestType,
148}
149
150// MI v2.0, 5, Figure 75
151#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
152#[deku(id_type = "u8", ctx = "endian: Endian", endian = "endian")]
153#[repr(u8)]
154enum NvmeMiConfigurationIdentifierRequestType {
155    Reserved = 0x00,
156    #[deku(id = "0x01")]
157    SmbusI2cFrequency(SmbusI2cFrequencyRequest),
158    #[deku(id = "0x02")]
159    HealthStatusChange(HealthStatusChangeRequest),
160    #[deku(id = "0x03")]
161    MctpTransmissionUnitSize(MctpTransmissionUnitSizeRequest),
162    AsynchronousEvent = 0x04,
163}
164
165// MI v2.0, 5.1.1, Figure 77, SFREQ
166#[derive(Debug, Clone, Copy, DekuRead, DekuWrite, Eq, PartialEq, PartialOrd)]
167#[deku(
168    bits = "bits.0",
169    id_type = "u8",
170    ctx = "endian: Endian, bits: BitSize",
171    endian = "endian"
172)]
173#[repr(u8)]
174enum SmbusFrequency {
175    Reserved = 0x00,
176    Freq100Khz = 0x01,
177    Freq400Khz = 0x02,
178    Freq1Mhz = 0x03,
179}
180
181impl From<SmbusFrequency> for crate::smbus::BusFrequency {
182    fn from(value: SmbusFrequency) -> Self {
183        match value {
184            SmbusFrequency::Reserved => Self::NotSupported,
185            SmbusFrequency::Freq100Khz => Self::Freq100Khz,
186            SmbusFrequency::Freq400Khz => Self::Freq400Khz,
187            SmbusFrequency::Freq1Mhz => Self::Freq1Mhz,
188        }
189    }
190}
191
192impl From<crate::smbus::BusFrequency> for SmbusFrequency {
193    fn from(value: crate::smbus::BusFrequency) -> Self {
194        match value {
195            crate::smbus::BusFrequency::NotSupported => Self::Reserved,
196            crate::smbus::BusFrequency::Freq100Khz => Self::Freq100Khz,
197            crate::smbus::BusFrequency::Freq400Khz => Self::Freq400Khz,
198            crate::smbus::BusFrequency::Freq1Mhz => Self::Freq1Mhz,
199        }
200    }
201}
202
203// MI v2.0, 5.1.1, Figure 77
204#[derive(Debug, DekuWrite, PartialEq)]
205#[deku(endian = "little")]
206struct GetSmbusI2cFrequencyResponse {
207    status: ResponseStatus,
208    #[deku(bits = "4", pad_bits_before = "4", pad_bytes_after = "2")]
209    sfreq: SmbusFrequency,
210}
211impl Encode<4> for GetSmbusI2cFrequencyResponse {}
212
213// MI v2.0, 5.1.2
214#[derive(Debug, DekuWrite)]
215#[deku(endian = "little")]
216struct GetHealthStatusChangeResponse {
217    #[deku(pad_bytes_after = "3")]
218    status: ResponseStatus,
219}
220impl Encode<4> for GetHealthStatusChangeResponse {}
221
222// MI v2.0, 5.1.3, Figure 79
223#[derive(Debug, DekuWrite)]
224#[deku(endian = "little")]
225struct GetMctpTransmissionUnitSizeResponse {
226    status: ResponseStatus,
227    #[deku(pad_bytes_after = "1")]
228    mr_mtus: u16,
229}
230impl Encode<4> for GetMctpTransmissionUnitSizeResponse {}
231
232// MI v2.0, 5.2, Figure 84
233#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
234#[deku(ctx = "endian: Endian", endian = "endian")]
235struct NvmeMiConfigurationSetRequest {
236    body: NvmeMiConfigurationIdentifierRequestType,
237}
238
239// MI v2.0, 5.2.1, Figure 86
240#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
241#[deku(ctx = "endian: Endian", endian = "endian")]
242struct SmbusI2cFrequencyRequest {
243    #[deku(bits = "4", pad_bits_before = "4", pad_bytes_after = "1")]
244    sfreq: SmbusFrequency,
245    #[deku(pad_bytes_after = "4")]
246    portid: u8,
247}
248
249// MI v2.0, 5.2.2, Figure 87
250#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
251#[deku(ctx = "endian: Endian", endian = "endian")]
252struct HealthStatusChangeRequest {
253    // Skip intermediate bytes comprising DWORD 0
254    #[deku(seek_from_current = "3")]
255    dw1: u32,
256}
257
258// MI v2.0, 5.2.2, Figure 88
259flags! {
260    #[repr(u32)]
261    enum HealthStatusChangeFlags: u32 {
262        Rdy,
263        Cfs,
264        Shst,
265        Nssro,
266        Ceco,
267        Nac,
268        Fa,
269        Csts,
270        Ctemp,
271        Pldu,
272        Spare,
273        Cwarn,
274        Tcida,
275    }
276}
277
278// MI v2.0, 5.2.3, Figure 89
279#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
280#[deku(ctx = "endian: Endian", endian = "endian")]
281struct MctpTransmissionUnitSizeRequest {
282    #[deku(seek_from_current = "2")]
283    dw0_portid: u8,
284    #[deku(pad_bytes_after = "2")]
285    dw1_mtus: u16,
286}
287
288// MI v2.0, 5.3, Figure 94
289flags! {
290    pub enum ControllerFunctionAndReportingFlags: u8 {
291        Incf = 1 << 0,
292        Incpf = 1 << 1,
293        Incvf = 1 << 2,
294        All = 1 << 7,
295    }
296}
297
298// MI v2.0, 5.3, Figure 95
299flags! {
300    pub enum ControllerPropertyFlags: u32 {
301        Csts = 1 << 0,
302        Ctemp = 1 << 1,
303        Pldu = 1 << 2,
304        Spare = 1 << 3,
305        Cwarn = 1 << 4,
306        Ccf = 1 << 31,
307    }
308}
309
310// MI v2.0, 5.3, Figures 94, 95
311#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
312#[deku(ctx = "endian: Endian", endian = "endian")]
313struct ControllerHealthStatusPollRequest {
314    sctlid: u16,
315    maxrent: u8,
316    functions: WireFlagSet<ControllerFunctionAndReportingFlags>,
317    properties: WireFlagSet<ControllerPropertyFlags>,
318}
319
320// MI v2.0, 5.3, Figure 96
321#[derive(Debug, DekuRead, DekuWrite)]
322#[deku(endian = "little")]
323struct ControllerHealthStatusPollResponse {
324    status: ResponseStatus,
325    #[deku(pad_bytes_before = "2", update = "self.body.len() as u8")]
326    rent: u8,
327    body: WireVec<ControllerHealthDataStructure, MAX_CONTROLLERS>,
328}
329impl Encode<{ 4 + 16 * MAX_CONTROLLERS }> for ControllerHealthStatusPollResponse {}
330
331// MI v2.0, 5.3, Figure 97, CSTS
332flags! {
333    pub enum ControllerStatusFlags: u16 {
334        Rdy = 1 << 0,
335        Cfs = 1 << 1,
336        ShstInProgress = 1 << 2,
337        ShstComplete = 1 << 3,
338        ShstReserved = (ControllerStatusFlags::ShstInProgress | ControllerStatusFlags::ShstComplete).bits(),
339        Nssro = 1 << 4,
340        Ceco = 1 << 5,
341        Nac = 1 << 6,
342        Fa = 1 << 7,
343        Tcida = 1 << 8,
344    }
345}
346
347// XXX: Consider improving the data model to handle the incongruence of the two flag
348// sets
349impl From<FlagSet<super::ControllerStatusFlags>> for WireFlagSet<ControllerStatusFlags> {
350    fn from(value: FlagSet<super::ControllerStatusFlags>) -> Self {
351        use super::ControllerStatusFlags as F;
352        use ControllerStatusFlags as T;
353
354        let mut fs = FlagSet::empty();
355
356        for f in value {
357            fs |= match f {
358                F::Rdy => T::Rdy,
359                F::Cfs => T::Cfs,
360                F::ShstInProgress => T::ShstInProgress,
361                F::ShstComplete => T::ShstComplete,
362                F::ShstReserved => T::ShstReserved,
363                F::Nssro => T::Nssro,
364                F::Pp => todo!(),
365                F::St => todo!(),
366            };
367        }
368
369        Self(fs)
370    }
371}
372
373// MI v2.0, 5.3, Figure 97, CWARN
374flags! {
375    pub enum CriticalWarningFlags: u8 {
376        St,
377        Taut,
378        Rd,
379        Ro,
380        Vmbf,
381        Pmre
382    }
383}
384
385// MI v2.0, 5.3, Figure 97
386#[derive(Debug, DekuRead, DekuWrite)]
387#[deku(ctx = "endian: Endian", endian = "endian")]
388struct ControllerHealthDataStructure {
389    ctlid: u16,
390    csts: WireFlagSet<ControllerStatusFlags>,
391    ctemp: u16,
392    pdlu: u8,
393    spare: u8,
394    cwarn: WireFlagSet<CriticalWarningFlags>,
395    #[deku(pad_bytes_after = "5")]
396    chsc: WireFlagSet<ControllerHealthStatusChangedFlags>,
397}
398
399// MI v2.0, 5.3, Figure 98
400flags! {
401    // NOTE: These are the same as CompositeControllerStatusFlags
402    pub enum ControllerHealthStatusChangedFlags: u16 {
403        Rdy = 1 << 0,
404        Cfs = 1 << 1,
405        Shst = 1 << 2,
406        Nssro = 1 << 4,
407        Ceco = 1 << 5,
408        Nac = 1 << 6,
409        Fa = 1 << 7,
410        Csts = 1 << 8,
411        Ctemp = 1 << 9,
412        Pdlu = 1 << 10,
413        Spare = 1 << 11,
414        Cwarn = 1 << 12,
415        Tcida = 1 << 13,
416    }
417}
418
419// MI v2.0, 5.6, Figure 106
420#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
421#[deku(ctx = "endian: Endian", endian = "endian")]
422struct NvmSubsystemHealthStatusPollRequest {
423    #[deku(bits = "1", pad_bits_after = "7", pad_bytes_before = "7")]
424    cs: bool,
425}
426
427// MI v2.0, 5.6, Figure 107
428flags! {
429    #[repr(u16)]
430    enum CompositeControllerStatusFlags: u16 {
431        Rdy = 1 << 0,
432        Cfs = 1 << 1,
433        Shst = 1 << 2,
434        Nssro = 1 << 4,
435        Ceco = 1 << 5,
436        Nac = 1 << 6,
437        Fa = 1 << 7,
438        Csts = 1 << 8,
439        Ctemp = 1 << 9,
440        Pdlu = 1 << 10,
441        Spare = 1 << 11,
442        Cwarn = 1 << 12,
443        Tcida = 1 << 13,
444    }
445}
446
447#[derive(Debug)]
448pub struct CompositeControllerStatusFlagSet(FlagSet<CompositeControllerStatusFlags>);
449
450impl CompositeControllerStatusFlagSet {
451    pub fn empty() -> Self {
452        Self(FlagSet::empty())
453    }
454}
455
456impl From<FlagSet<HealthStatusChangeFlags>> for CompositeControllerStatusFlagSet {
457    fn from(value: FlagSet<HealthStatusChangeFlags>) -> Self {
458        use CompositeControllerStatusFlags as T;
459        use HealthStatusChangeFlags as F;
460
461        let mut converted = FlagSet::empty();
462        for flag in value {
463            converted |= match flag {
464                F::Rdy => T::Rdy,
465                F::Cfs => T::Cfs,
466                F::Shst => T::Shst,
467                F::Nssro => T::Nssro,
468                F::Ceco => T::Ceco,
469                F::Nac => T::Nac,
470                F::Fa => T::Fa,
471                F::Csts => T::Csts,
472                F::Ctemp => T::Ctemp,
473                F::Pldu => T::Pdlu,
474                F::Spare => T::Spare,
475                F::Cwarn => T::Cwarn,
476                F::Tcida => T::Tcida,
477            }
478        }
479        Self(converted)
480    }
481}
482
483impl From<FlagSet<ControllerHealthStatusChangedFlags>> for CompositeControllerStatusFlagSet {
484    fn from(value: FlagSet<ControllerHealthStatusChangedFlags>) -> Self {
485        // SAFETY: Separate declarations have the equal definitions
486        Self(FlagSet::new(value.bits()).expect("Divergent flag definitions"))
487    }
488}
489
490// MI v2.0, 5.6, Figure 107, 108
491#[derive(Debug, DekuRead, DekuWrite)]
492#[deku(endian = "little")]
493struct CompositeControllerStatusDataStructureResponse {
494    #[deku(pad_bytes_after = "2")]
495    ccsf: u16,
496}
497impl Encode<4> for CompositeControllerStatusDataStructureResponse {}
498
499// MI v2.0, 5.6, Figure 108, NSS
500flags! {
501    pub enum NvmSubsystemStatusFlags: u8 {
502        P1la = 1 << 2,
503        P0la = 1 << 3,
504        Rnr = 1 << 4,
505        Df = 1 << 5,
506        Sfm = 1 << 6,
507        Atf = 1 << 7,
508    }
509}
510
511// MI v2.0, 5.6, Figure 108, SW
512flags! {
513    pub enum SmartWarningFlags: u8 {
514        Ascbt,
515        Ttc,
516        Ndr,
517        Amro,
518        Vmbf,
519        Pmrro,
520    }
521}
522
523impl From<FlagSet<super::CriticalWarningFlags>> for WireFlagSet<SmartWarningFlags> {
524    fn from(value: FlagSet<super::CriticalWarningFlags>) -> Self {
525        FlagSet::<SmartWarningFlags>::new((!value.bits()) & 0x3f)
526            .expect("Undefined bits set")
527            .into()
528    }
529}
530
531// MI v2.0, 5.6, Figure 108
532#[derive(Debug, DekuRead, DekuWrite)]
533#[deku(endian = "little")]
534struct NvmSubsystemHealthDataStructureResponse {
535    nss: WireFlagSet<NvmSubsystemStatusFlags>,
536    sw: WireFlagSet<SmartWarningFlags>,
537    ctemp: u8,
538    pldu: u8,
539}
540impl Encode<4> for NvmSubsystemHealthDataStructureResponse {}
541
542// MI v2.0, 5.7, Figure 109, DTYP
543#[derive(Debug, DekuRead, DekuWrite, PartialEq, Eq)]
544#[deku(ctx = "endian: Endian, dtyp: u8", endian = "endian", id = "dtyp")]
545#[repr(u8)]
546enum NvmeMiDataStructureRequestType {
547    NvmSubsystemInformation = 0x00,
548    PortInformation = 0x01,
549    ControllerList = 0x02,
550    ControllerInformation = 0x03,
551    OptionallySupportedCommandList = 0x04,
552    ManagementEndpointBufferCommandSupportList = 0x05,
553}
554unsafe impl Discriminant<u8> for NvmeMiDataStructureRequestType {}
555
556// MI v2.0, 5.7, Figure 109
557#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
558#[deku(ctx = "endian: Endian", endian = "endian")]
559struct NvmeMiDataStructureRequest {
560    ctrlid: u16,
561    portid: u8,
562    #[deku(update = "self.body.id()")]
563    dtyp: u8,
564    #[deku(pad_bytes_after = "3")]
565    iocsi: u8,
566    #[deku(ctx = "*dtyp")]
567    body: NvmeMiDataStructureRequestType,
568}
569
570// MI v2.0, 5.7, Figure 111
571#[derive(Debug, DekuRead, DekuWrite)]
572#[deku(endian = "little")]
573struct NvmeMiDataStructureManagementResponse {
574    status: ResponseStatus,
575    rdl: u16,
576}
577impl Encode<4> for NvmeMiDataStructureManagementResponse {}
578
579// MI v2.0, 5.7.1, Figure 112, NNSC
580// TODO: Convert to Flags/FlagSet
581#[derive(Debug)]
582pub struct SubsystemCapabilities {
583    sre: bool,
584}
585
586impl SubsystemCapabilities {
587    pub fn new() -> Self {
588        Self { sre: false }
589    }
590}
591
592impl Default for SubsystemCapabilities {
593    fn default() -> Self {
594        Self::new()
595    }
596}
597
598// MI v2.0, 5.7.1, Figure 112
599#[derive(Debug, DekuWrite)]
600#[deku(endian = "little")]
601struct NvmSubsystemInformationResponse {
602    nump: u8,
603    mjr: u8,
604    mnr: u8,
605    nnsc: u8,
606}
607impl Encode<32> for NvmSubsystemInformationResponse {}
608
609// MI v2.0, 5.7.2, Figure 114, PRTTYP
610#[derive(Clone, Copy, Debug, DekuRead, DekuWrite, PartialEq, Eq)]
611#[deku(ctx = "endian: Endian", endian = "endian", id_type = "u8")]
612#[repr(u8)]
613pub enum PortType {
614    Inactive = 0x00,
615    Pcie = 0x01,
616    TwoWire = 0x02,
617}
618
619impl From<&crate::PortType> for PortType {
620    fn from(value: &crate::PortType) -> Self {
621        match value {
622            crate::PortType::Inactive => Self::Inactive,
623            crate::PortType::Pcie(_) => Self::Pcie,
624            crate::PortType::TwoWire(_) => Self::TwoWire,
625        }
626    }
627}
628
629// MI v2.0, 5.7.2, Figure 114, PRTCAP
630flags! {
631    enum PortCapabilityFlags: u8 {
632        Ciaps,
633        Aems,
634    }
635}
636
637// MI v2.0, 5.7.2, Figure 114
638#[derive(Debug, DekuWrite)]
639#[deku(endian = "little")]
640struct PortInformationResponse {
641    prttyp: PortType,
642    prtcap: WireFlagSet<PortCapabilityFlags>,
643    mmtus: u16,
644    mebs: u32,
645}
646impl Encode<8> for PortInformationResponse {}
647
648// MI v2.0, 5.7.2, Figure 115, PCIEMPS
649#[derive(Clone, Copy, DekuRead, DekuWrite, Debug, Eq, PartialEq)]
650#[deku(ctx = "endian: Endian", endian = "endian", id_type = "u8")]
651#[repr(u8)]
652enum PciePayloadSize {
653    Payload128B = 0x00,
654    Payload256B = 0x01,
655    Payload512B = 0x02,
656    Payload1Kb = 0x03,
657    Payload2Kb = 0x04,
658    Payload4Kb = 0x05,
659}
660
661impl From<crate::pcie::PayloadSize> for PciePayloadSize {
662    fn from(value: crate::pcie::PayloadSize) -> Self {
663        match value {
664            crate::pcie::PayloadSize::Payload128B => Self::Payload128B,
665            crate::pcie::PayloadSize::Payload256B => Self::Payload256B,
666            crate::pcie::PayloadSize::Payload512B => Self::Payload512B,
667            crate::pcie::PayloadSize::Payload1Kb => Self::Payload1Kb,
668            crate::pcie::PayloadSize::Payload2Kb => Self::Payload2Kb,
669            crate::pcie::PayloadSize::Payload4Kb => Self::Payload4Kb,
670        }
671    }
672}
673
674impl From<PciePayloadSize> for crate::pcie::PayloadSize {
675    fn from(value: PciePayloadSize) -> Self {
676        match value {
677            PciePayloadSize::Payload128B => Self::Payload128B,
678            PciePayloadSize::Payload256B => Self::Payload256B,
679            PciePayloadSize::Payload512B => Self::Payload512B,
680            PciePayloadSize::Payload1Kb => Self::Payload1Kb,
681            PciePayloadSize::Payload2Kb => Self::Payload2Kb,
682            PciePayloadSize::Payload4Kb => Self::Payload4Kb,
683        }
684    }
685}
686
687// MI v2.0, 5.7.2, Figure 115, PCIESLSV
688flags! {
689    #[repr(u8)]
690    enum PcieSupportedLinkSpeeds: u8 {
691        Gts2p5,
692        Gts5,
693        Gts8,
694        Gts16,
695        Gts32,
696        Gts64,
697    }
698}
699
700// MI v2.0, 5.7.2, Figure 115, PCIECLS
701#[derive(Clone, Copy, Debug, DekuRead, DekuWrite, Eq, PartialEq)]
702#[deku(ctx = "endian: Endian", endian = "endian", id_type = "u8")]
703#[repr(u8)]
704enum PcieLinkSpeed {
705    Inactive = 0x00,
706    Gts2p5 = 0x01,
707    Gts5 = 0x02,
708    Gts8 = 0x03,
709    Gts16 = 0x04,
710    Gts32 = 0x05,
711    Gts64 = 0x06,
712}
713
714impl From<crate::pcie::LinkSpeed> for PcieLinkSpeed {
715    fn from(value: crate::pcie::LinkSpeed) -> Self {
716        match value {
717            crate::pcie::LinkSpeed::Inactive => Self::Inactive,
718            crate::pcie::LinkSpeed::Gts2p5 => Self::Gts2p5,
719            crate::pcie::LinkSpeed::Gts5 => Self::Gts5,
720            crate::pcie::LinkSpeed::Gts8 => Self::Gts8,
721            crate::pcie::LinkSpeed::Gts16 => Self::Gts16,
722            crate::pcie::LinkSpeed::Gts32 => Self::Gts32,
723            crate::pcie::LinkSpeed::Gts64 => Self::Gts64,
724        }
725    }
726}
727
728impl From<PcieLinkSpeed> for crate::pcie::LinkSpeed {
729    fn from(value: PcieLinkSpeed) -> Self {
730        match value {
731            PcieLinkSpeed::Inactive => Self::Inactive,
732            PcieLinkSpeed::Gts2p5 => Self::Gts2p5,
733            PcieLinkSpeed::Gts5 => Self::Gts5,
734            PcieLinkSpeed::Gts8 => Self::Gts8,
735            PcieLinkSpeed::Gts16 => Self::Gts16,
736            PcieLinkSpeed::Gts32 => Self::Gts32,
737            PcieLinkSpeed::Gts64 => Self::Gts64,
738        }
739    }
740}
741
742// MI v2.0, 5.7.2, Figure 115, PCIEMLW
743#[derive(Clone, Copy, Debug, DekuRead, DekuWrite, Eq, PartialEq)]
744#[deku(ctx = "endian: Endian", endian = "endian", id_type = "u8")]
745#[repr(u8)]
746enum PcieLinkWidth {
747    X1 = 1,
748    X2 = 2,
749    X4 = 4,
750    X8 = 8,
751    X12 = 12,
752    X16 = 16,
753    X32 = 32,
754}
755
756impl From<crate::pcie::LinkWidth> for PcieLinkWidth {
757    fn from(value: crate::pcie::LinkWidth) -> Self {
758        match value {
759            crate::pcie::LinkWidth::X1 => Self::X1,
760            crate::pcie::LinkWidth::X2 => Self::X2,
761            crate::pcie::LinkWidth::X4 => Self::X4,
762            crate::pcie::LinkWidth::X8 => Self::X8,
763            crate::pcie::LinkWidth::X12 => Self::X12,
764            crate::pcie::LinkWidth::X16 => Self::X16,
765            crate::pcie::LinkWidth::X32 => Self::X32,
766        }
767    }
768}
769
770impl From<PcieLinkWidth> for crate::pcie::LinkWidth {
771    fn from(value: PcieLinkWidth) -> Self {
772        match value {
773            PcieLinkWidth::X1 => Self::X1,
774            PcieLinkWidth::X2 => Self::X2,
775            PcieLinkWidth::X4 => Self::X4,
776            PcieLinkWidth::X8 => Self::X8,
777            PcieLinkWidth::X12 => Self::X12,
778            PcieLinkWidth::X16 => Self::X16,
779            PcieLinkWidth::X32 => Self::X32,
780        }
781    }
782}
783
784// MI v2.0, 5.7.2, Figure 115
785#[derive(Debug, DekuRead, DekuWrite)]
786#[deku(endian = "little")]
787struct PciePortDataResponse {
788    pciemps: PciePayloadSize,
789    pcieslsv: WireFlagSet<PcieSupportedLinkSpeeds>,
790    pciecls: PcieLinkSpeed,
791    pciemlw: PcieLinkWidth,
792    pcienlw: PcieLinkWidth,
793    pciepn: u8,
794}
795impl Encode<24> for PciePortDataResponse {}
796
797// MI v2.0, 5.7.2, Figure 116
798#[derive(Debug, DekuWrite)]
799#[deku(endian = "little")]
800struct TwoWirePortDataResponse {
801    cvpdaddr: u8,
802    #[deku(bits = "8")]
803    mvpdfreq: SmbusFrequency,
804    cmeaddr: u8,
805    #[deku(bits = "1", pad_bits_after = "5")]
806    twprt_i3csprt: bool,
807    #[deku(bits = "2")]
808    twprt_msmbfreq: SmbusFrequency,
809    nvmebm: u8,
810}
811impl Encode<24> for TwoWirePortDataResponse {}
812
813// MI v2.0, 5.7.4, Figure 117
814#[derive(Debug, DekuRead, DekuWrite)]
815#[deku(endian = "little")]
816struct ControllerInformationResponse {
817    #[deku(pad_bytes_after = "4")]
818    portid: u8,
819    #[deku(bits = "1", pad_bits_before = "7")]
820    prii_pcieriv: bool,
821    #[deku(bits = "8")]
822    pri_pcibn: u16,
823    #[deku(bits = "5")]
824    pri_pcidn: u16,
825    #[deku(bits = "3")]
826    pri_pcifn: u16,
827    pcivid: u16,
828    pcidid: u16,
829    pcisvid: u16,
830    pcisdid: u16,
831    pciesn: u8,
832}
833impl Encode<32> for ControllerInformationResponse {}
834
835// MI v2.0, 6, Figure 134
836#[expect(clippy::large_enum_variant)] // FIXME
837#[derive(Debug, DekuRead, PartialEq, Eq)]
838#[deku(ctx = "endian: Endian, opcode: u8", id = "opcode", endian = "endian")]
839#[repr(u8)]
840enum AdminCommandRequestType {
841    DeleteIoSubmissionQueue = 0x00, // P
842    CreateIoSubmissionQueue = 0x01, // P
843    #[deku(id = 0x02)]
844    GetLogPage(AdminGetLogPageRequest), // M
845    DeleteIoCompletionQueue = 0x04, // P
846    CreateIoCompletionQueue = 0x05, // P
847    #[deku(id = 0x06)]
848    Identify(AdminIdentifyRequest), // M
849    Abort = 0x08,                   // P
850    GetFeatures = 0x0a,             // M
851    AsynchronousEventRequest = 0x0c, // P
852    #[deku(id = 0x0d)]
853    NamespaceManagement(AdminNamespaceManagementRequest),
854    #[deku(id = 0x15)]
855    NamespaceAttachement(AdminNamespaceAttachmentRequest),
856    KeepAlive = 0x18,                      // P
857    DirectiveSend = 0x19,                  // P
858    DirectiveReceive = 0x1a,               // P
859    NvmeMiSend = 0x1d,                     // P
860    NvmeMiReceive = 0x1e,                  // P
861    DiscoveryInformationManagement = 0x21, // P
862    FabricZoningReceive = 0x22,            // P
863    FabricZoningLookup = 0x25,             // P
864    FabricZoningSend = 0x29,               // P
865    SendDiscoveryLogPage = 0x39,           // P
866    TrackSend = 0x3d,                      // P
867    TrackReceive = 0x3e,                   // P
868    MigrationSend = 0x41,                  // P
869    MigrationReceive = 0x42,               // P
870    ControllerDataQueue = 0x45,            // P
871    DoorbellBufferConfig = 0x7c,           // P
872    FabricsCommands = 0x7f,                // P
873    #[deku(id = 0x80)]
874    FormatNvm(AdminFormatNvmRequest),
875    #[deku(id = 0x84)]
876    Sanitize(AdminSanitizeRequest),
877    LoadProgram = 0x85,                 // P
878    ProgramActivationManagement = 0x88, // P
879    MemoryRangeSetManagement = 0x89,    // P
880}
881unsafe impl Discriminant<u8> for AdminCommandRequestType {}
882
883// MI v2.0, 6, Figure 136
884#[derive(Debug, DekuRead)]
885#[deku(endian = "little")]
886struct AdminCommandRequestHeader {
887    _opcode: u8,
888    cflgs: u8,
889    ctlid: u16,
890    #[deku(ctx = "*_opcode")]
891    op: AdminCommandRequestType,
892}
893
894// MI v2.0, 6, Figure 136
895// Base v2.1, 5.1.10, Figure 189
896#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
897#[deku(ctx = "endian: Endian", endian = "endian")]
898struct AdminFormatNvmRequest {
899    nsid: u32,
900    #[deku(seek_from_current = "16")]
901    dofst: u32,
902    dlen: u32,
903    #[deku(seek_from_current = "8")]
904    #[deku(pad_bytes_after = "20")]
905    config: AdminFormatNvmConfiguration,
906}
907
908// MI v2.0, 6, Figure 136
909// Base v2.1, 5.1.12, Figures 197-201
910#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
911#[deku(ctx = "endian: Endian", endian = "endian")]
912struct AdminGetLogPageRequest {
913    nsid: u32,
914    #[deku(seek_from_current = "16")]
915    dofst: u32,
916    dlen: u32,
917    #[deku(seek_from_current = "8")]
918    #[deku(update = "self.req.id()")]
919    lid: u8,
920    lsp_rae: u8,
921    numdw: u32, // Synthesised from NUMDL / NUMDU
922    lsi: u16,
923    lpo: u64, // Synthesised from LPOL / LPOU
924    uidx: u8,
925    #[deku(seek_from_current = "1")]
926    ot: u8,
927    csi: u8,
928    #[deku(pad_bytes_after = "4")]
929    #[deku(ctx = "*lid")]
930    req: AdminGetLogPageLidRequestType,
931}
932
933// MI v2.0, 6, Figure 136
934// Base v2.1, 5.1.13.1, Figures 306-309
935#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
936#[deku(ctx = "endian: Endian", endian = "endian")]
937struct AdminIdentifyRequest {
938    nsid: u32,
939    #[deku(seek_from_current = "16")]
940    dofst: u32,
941    dlen: u32,
942    #[deku(seek_from_current = "8")]
943    #[deku(update = "self.req.id()")]
944    cns: u8,
945    #[deku(seek_from_current = "1")]
946    cntid: u16,
947    cnssid: u16,
948    #[deku(seek_from_current = "1")]
949    csi: u8,
950    #[deku(seek_from_current = "8")]
951    uidx: u8,
952    #[deku(pad_bytes_after = "7")]
953    #[deku(ctx = "*cns")]
954    req: AdminIdentifyCnsRequestType,
955}
956
957// MI v2.0, 6, Figure 136
958// Base v2.1, 5.1.21, Figures 367-369
959#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
960#[deku(ctx = "endian: Endian", endian = "endian")]
961struct AdminNamespaceManagementRequest {
962    nsid: u32,
963    #[deku(seek_from_current = "16")]
964    dofst: u32,
965    dlen: u32,
966    #[deku(seek_from_current = "8")]
967    sel: u8, // NOTE: SEL is the bottom nibble
968    #[deku(seek_from_current = "6")]
969    csi: u8,
970    #[deku(seek_from_current = "16")]
971    #[deku(ctx = "*sel")]
972    req: AdminNamespaceManagementSelect,
973}
974
975// MI v2.0, 6, Figure 136
976// Base v2.1, 5.1.20, Figure 364
977#[derive(Debug, DekuRead, Eq, PartialEq)]
978#[deku(ctx = "endian: Endian", endian = "endian")]
979struct AdminNamespaceAttachmentRequest {
980    nsid: u32,
981    #[deku(seek_from_current = "16")]
982    dofst: u32,
983    dlen: u32,
984    #[deku(seek_from_current = "8")]
985    sel: AdminNamespaceAttachmentSelect, // NOTE: SEL is the bottom nibble
986    #[deku(seek_from_current = "23")]
987    body: ControllerListRequest,
988}
989
990// MI v2.0, 6, Figure 136
991// Base v2.1, 5.1.22, Figure 372
992#[derive(Debug, DekuRead, Eq, PartialEq)]
993#[deku(ctx = "endian: Endian", endian = "endian")]
994struct AdminSanitizeRequest {
995    nsid: u32,
996    #[deku(seek_from_current = "16")]
997    dofst: u32,
998    dlen: u32,
999    #[deku(seek_from_current = "8")]
1000    config: AdminSanitizeConfiguration,
1001    #[deku(pad_bytes_after = "16")]
1002    ovrpat: u32,
1003}
1004
1005// MI v2.0, 6, Figure 138
1006#[derive(Debug, DekuRead, DekuWrite)]
1007#[deku(endian = "little")]
1008struct AdminCommandResponseHeader {
1009    status: ResponseStatus,
1010    #[deku(seek_from_start = "4")]
1011    cqedw0: u32,
1012    cqedw1: u32,
1013    cqedw3: super::AdminIoCqeStatus,
1014}
1015impl Encode<16> for AdminCommandResponseHeader {}
1016
1017// MI v2.0, 7, Figure 146
1018#[derive(Debug, DekuRead, DekuWrite)]
1019#[deku(endian = "little")]
1020struct PcieCommandRequestHeader {
1021    _opcode: u8,
1022    #[deku(seek_from_current = "1")]
1023    ctlid: u16,
1024    #[deku(ctx = "*_opcode")]
1025    op: PcieCommandRequestType,
1026}
1027
1028// MI v2.0, 7, Figure 148
1029#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
1030#[deku(ctx = "endian: Endian, opcode: u8", id = "opcode", endian = "endian")]
1031#[repr(u8)]
1032enum PcieCommandRequestType {
1033    #[deku(id = 0x00)]
1034    ConfigurationRead(PcieConfigurationAccessRequest),
1035    #[deku(id = 0x01)]
1036    ConfigurationWrite(PcieConfigurationAccessRequest),
1037    MemoryRead = 0x02,
1038    MemoryWrite = 0x03,
1039    IoRead = 0x04,
1040    IoWrite = 0x05,
1041}
1042
1043// MI v2.0, 7, Figure 151-152
1044#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
1045#[deku(ctx = "endian: Endian", endian = "endian")]
1046struct PcieConfigurationAccessRequest {
1047    length: u16,
1048    #[deku(seek_from_current = "2")]
1049    #[deku(pad_bytes_after = "6")]
1050    offset: u16,
1051}