nvme_mi_dev/nvme/mi/
dev.rs

1// SPDX-License-Identifier: GPL-3.0-only
2/*
3 * Copyright (c) 2025 Code Construct
4 */
5use deku::prelude::*;
6use flagset::FlagSet;
7use heapless::Vec;
8use log::debug;
9use mctp::{AsyncRespChannel, MsgIC};
10
11use crate::nvme::mi::{NvmSubsystemStatusFlags, PortCapabilityFlags};
12use crate::nvme::{
13    ControllerAttributeFlags, ControllerMultipathIoNamespaceSharingCapabilityFlags,
14    ManagementEndpointCapabilityFlags, NvmSubsystemReportFlags, SanitizeCapabilityFlags,
15};
16use crate::{
17    CommandEffect, CommandEffectError, Controller, ControllerError, ControllerType, Discriminant,
18    MAX_CONTROLLERS, MAX_NAMESPACES, NamespaceId, NamespaceIdDisposition, SubsystemError,
19    nvme::{
20        AdminGetLogPageLidRequestType, AdminGetLogPageSupportedLogPagesResponse,
21        AdminIdentifyActiveNamespaceIdListResponse, AdminIdentifyAllocatedNamespaceIdListResponse,
22        AdminIdentifyCnsRequestType, AdminIdentifyControllerResponse,
23        AdminIdentifyNamespaceIdentificationDescriptorListResponse,
24        AdminIdentifyNvmIdentifyNamespaceResponse, AdminIoCqeGenericCommandStatus,
25        AdminIoCqeStatusType, ControllerListResponse, LidSupportedAndEffectsDataStructure,
26        LidSupportedAndEffectsFlags, LogPageAttributes, NamespaceIdentifierType, SanitizeAction,
27        SanitizeOperationStatus, SanitizeState, SanitizeStateInformation, SanitizeStatus,
28        SanitizeStatusLogPageResponse, SmartHealthInformationLogPageResponse,
29        mi::{
30            AdminCommandRequestHeader, AdminCommandResponseHeader, AdminFormatNvmRequest,
31            AdminNamespaceAttachmentRequest, AdminNamespaceManagementRequest, AdminSanitizeRequest,
32            CompositeControllerStatusDataStructureResponse, CompositeControllerStatusFlagSet,
33            ControllerFunctionAndReportingFlags, ControllerHealthDataStructure,
34            ControllerHealthStatusPollResponse, ControllerInformationResponse,
35            ControllerPropertyFlags, MessageType, NvmSubsystemHealthDataStructureResponse,
36            NvmSubsystemInformationResponse, NvmeManagementResponse, NvmeMiCommandRequestHeader,
37            NvmeMiCommandRequestType, NvmeMiDataStructureManagementResponse,
38            NvmeMiDataStructureRequestType, PcieCommandRequestHeader, PciePortDataResponse,
39            PcieSupportedLinkSpeeds, PortInformationResponse, TwoWirePortDataResponse,
40        },
41    },
42    pcie::PciDeviceFunctionConfigurationSpace,
43    wire::{WireString, WireVec},
44};
45
46use crate::Encode;
47use crate::RequestHandler;
48
49use super::{
50    AdminCommandRequestType, AdminGetLogPageRequest, AdminIdentifyRequest,
51    GetHealthStatusChangeResponse, GetMctpTransmissionUnitSizeResponse,
52    GetSmbusI2cFrequencyResponse, MessageHeader, NvmeMiConfigurationGetRequest,
53    NvmeMiConfigurationIdentifierRequestType, NvmeMiConfigurationSetRequest,
54    NvmeMiDataStructureRequest, ResponseStatus,
55};
56
57const ISCSI: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_ISCSI);
58const MAX_FRAGMENTS: usize = 6;
59
60async fn send_response(resp: &mut impl AsyncRespChannel, bufs: &[&[u8]]) {
61    let mut digest = ISCSI.digest();
62    digest.update(&[0x80 | 0x04]);
63
64    for s in bufs {
65        digest.update(s);
66    }
67    let icv = digest.finalize().to_le_bytes();
68
69    let Ok(mut bufs) = Vec::<&[u8], MAX_FRAGMENTS>::from_slice(bufs) else {
70        debug!("Failed to gather buffers into vec");
71        return;
72    };
73
74    if bufs.push(icv.as_slice()).is_err() {
75        debug!("Failed to apply integrity check to response");
76        return;
77    }
78
79    if let Err(e) = resp.send_vectored(MsgIC(true), bufs.as_slice()).await {
80        debug!("Failed to send NVMe-MI response: {e:?}");
81    }
82}
83
84impl RequestHandler for MessageHeader {
85    type Ctx = Self;
86
87    async fn handle<A, C>(
88        &self,
89        ctx: &Self::Ctx,
90        mep: &mut crate::ManagementEndpoint,
91        subsys: &mut crate::Subsystem,
92        rest: &[u8],
93        resp: &mut C,
94        app: A,
95    ) -> Result<(), ResponseStatus>
96    where
97        A: AsyncFnMut(CommandEffect) -> Result<(), CommandEffectError>,
98        C: AsyncRespChannel,
99    {
100        debug!("{self:x?}");
101        // TODO: Command and Feature Lockdown handling
102        // TODO: Handle subsystem reset, section 8.1, v2.0
103        match ctx.nmimt {
104            MessageType::NvmeMiCommand => {
105                match &NvmeMiCommandRequestHeader::from_bytes((rest, 0)) {
106                    Ok(((rest, _), ch)) => ch.handle(ch, mep, subsys, rest, resp, app).await,
107                    Err(err) => {
108                        debug!("Unable to parse NVMeMICommandHeader from message buffer: {err:?}");
109                        // TODO: This is a bad assumption: Can see DekuError::InvalidParam too
110                        Err(ResponseStatus::InvalidCommandSize)
111                    }
112                }
113            }
114            MessageType::NvmeAdminCommand => {
115                match &AdminCommandRequestHeader::from_bytes((rest, 0)) {
116                    Ok(((rest, _), ch)) => ch.handle(ch, mep, subsys, rest, resp, app).await,
117                    Err(err) => {
118                        debug!("Unable to parse AdminCommandHeader from message buffer: {err:?}");
119                        // TODO: This is a bad assumption: Can see DekuError::InvalidParam too
120                        Err(ResponseStatus::InvalidCommandSize)
121                    }
122                }
123            }
124            MessageType::PcieCommand => {
125                match &PcieCommandRequestHeader::from_bytes((rest, 0)) {
126                    Ok(((rest, _), ch)) => ch.handle(ch, mep, subsys, rest, resp, app).await,
127                    Err(err) => {
128                        debug!(
129                            "Unable to parse PcieCommandRequestHeader from message buffer: {err:?}"
130                        );
131                        // TODO: This is a bad assumption: Can see DekuError::InvalidParam too
132                        Err(ResponseStatus::InvalidCommandSize)
133                    }
134                }
135            }
136            _ => {
137                debug!("Unimplemented NMINT: {:?}", ctx.nmimt);
138                Err(ResponseStatus::InternalError)
139            }
140        }
141    }
142}
143
144impl RequestHandler for NvmeMiCommandRequestHeader {
145    type Ctx = Self;
146
147    async fn handle<A, C>(
148        &self,
149        ctx: &Self::Ctx,
150        mep: &mut crate::ManagementEndpoint,
151        subsys: &mut crate::Subsystem,
152        rest: &[u8],
153        resp: &mut C,
154        app: A,
155    ) -> Result<(), ResponseStatus>
156    where
157        A: AsyncFnMut(CommandEffect) -> Result<(), CommandEffectError>,
158        C: AsyncRespChannel,
159    {
160        debug!("{self:x?}");
161        match &self.body {
162            NvmeMiCommandRequestType::ReadNvmeMiDataStructure(ds) => {
163                ds.handle(self, mep, subsys, rest, resp, app).await
164            }
165            NvmeMiCommandRequestType::NvmSubsystemHealthStatusPoll(shsp) => {
166                // 5.6, Figure 108, v2.0
167                if !rest.is_empty() {
168                    debug!("Lost coherence decoding {:?}", ctx.opcode);
169                    return Err(ResponseStatus::InvalidCommandSize);
170                }
171
172                let mh = MessageHeader::respond(MessageType::NvmeMiCommand).encode()?;
173
174                let mr = NvmeManagementResponse {
175                    status: ResponseStatus::Success,
176                }
177                .encode()?;
178
179                // Implementation-specific strategy is to pick the first controller.
180                let ctlr = subsys
181                    .ctlrs
182                    .first()
183                    .expect("Device needs at least one controller");
184
185                let Some(port) = subsys.ports.iter().find(|p| p.id == ctlr.port) else {
186                    panic!(
187                        "Inconsistent port association for controller {:?}: {:?}",
188                        ctlr.id, ctlr.port
189                    );
190                };
191
192                let crate::PortType::Pcie(pprt) = port.typ else {
193                    panic!("Non-PCIe port associated with controller {:?}", ctlr.id);
194                };
195
196                // Derive ASCBT from spare vs capacity
197                if ctlr.spare > ctlr.capacity {
198                    debug!(
199                        "spare capacity {} exceeds drive capacity {}",
200                        ctlr.spare, ctlr.capacity
201                    );
202                    return Err(ResponseStatus::InternalError);
203                }
204
205                // Derive TTC from operating range comparison
206                assert!(ctlr.temp_range.kind == crate::UnitKind::Kelvin);
207
208                // Derive CTEMP from controller temperature via conversions
209                // Clamp to Figure 108, NVMe MI v2.0 requirements
210                let clamped = ctlr
211                    .temp
212                    .clamp(ctlr.temp_range.lower, ctlr.temp_range.upper);
213
214                // Convert to celcius from kelvin
215                let celcius: i32 = clamped as i32 - 273;
216
217                // Convert to unsigned representation of two's-complement value
218                let ctemp = if celcius < 0 {
219                    celcius + u8::MAX as i32 + 1
220                } else {
221                    celcius
222                };
223                debug_assert!(ctemp <= u8::MAX.into());
224
225                // Derive PLDU from write age and expected lifespan
226                let pdlu = core::cmp::min(255, 100 * ctlr.write_age / ctlr.write_lifespan);
227
228                let nvmshds = NvmSubsystemHealthDataStructureResponse {
229                    nss: {
230                        let mut flags = subsys.health.nss;
231
232                        if pprt.cls != crate::pcie::LinkSpeed::Inactive {
233                            flags |= NvmSubsystemStatusFlags::P0la;
234                        }
235
236                        flags
237                    }
238                    .into(),
239                    sw: {
240                        let mut flags = FlagSet::full();
241
242                        if ctlr.ro {
243                            flags -= super::SmartWarningFlags::Amro;
244                            flags -= super::SmartWarningFlags::Ndr;
245                        }
246
247                        if ctlr.temp_range.lower <= ctlr.temp && ctlr.temp <= ctlr.temp_range.upper
248                        {
249                            flags -= super::SmartWarningFlags::Ttc;
250                        }
251
252                        if (100 * ctlr.spare / ctlr.capacity) < ctlr.spare_range.lower {
253                            flags -= super::SmartWarningFlags::Ascbt;
254                        }
255
256                        flags
257                    }
258                    .into(),
259                    ctemp: ctemp as u8,
260                    pldu: pdlu as u8,
261                }
262                .encode()?;
263
264                let ccs = CompositeControllerStatusDataStructureResponse {
265                    ccsf: mep.ccsf.0.bits(),
266                }
267                .encode()?;
268
269                // CS: See Figure 106, NVMe MI v2.0
270                if shsp.cs {
271                    mep.ccsf.0.clear();
272                }
273
274                send_response(resp, &[&mh.0, &mr.0, &nvmshds.0, &ccs.0]).await;
275                Ok(())
276            }
277            NvmeMiCommandRequestType::ControllerHealthStatusPoll(req) => {
278                // MI v2.0, 5.3
279                if !rest.is_empty() {
280                    debug!("Lost coherence decoding {:?}", ctx.opcode);
281                    return Err(ResponseStatus::InvalidCommandSize);
282                }
283
284                if !req
285                    .functions
286                    .0
287                    .contains(ControllerFunctionAndReportingFlags::All)
288                {
289                    debug!("TODO: Implement support for property-based selectors");
290                    return Err(ResponseStatus::InternalError);
291                }
292
293                if req.functions.0.contains(
294                    ControllerFunctionAndReportingFlags::Incf
295                        | ControllerFunctionAndReportingFlags::Incpf
296                        | ControllerFunctionAndReportingFlags::Incvf,
297                ) {
298                    debug!("TODO: Implement support for function-base selectors");
299                    return Err(ResponseStatus::InternalError);
300                }
301
302                assert!(MAX_CONTROLLERS <= u8::MAX as usize);
303                if req.maxrent < MAX_CONTROLLERS as u8 {
304                    debug!("TODO: Implement response entry constraint");
305                    return Err(ResponseStatus::InternalError);
306                }
307
308                if req.sctlid > 0 {
309                    debug!("TODO: Implement starting controller ID constraint");
310                    return Err(ResponseStatus::InternalError);
311                }
312
313                let mh = MessageHeader::respond(MessageType::NvmeMiCommand).encode()?;
314
315                let mut chspr = ControllerHealthStatusPollResponse {
316                    status: ResponseStatus::Success,
317                    rent: 0,
318                    body: WireVec::new(),
319                };
320
321                for ctlr in &subsys.ctlrs {
322                    chspr
323                        .body
324                        .push(ControllerHealthDataStructure {
325                            ctlid: ctlr.id.0,
326                            csts: ctlr.csts.into(),
327                            ctemp: ctlr.temp,
328                            pdlu: core::cmp::min(255, 100 * ctlr.write_age / ctlr.write_lifespan)
329                                as u8,
330                            spare: <u8>::try_from(100 * ctlr.spare / ctlr.capacity)
331                                .map_err(|_| ResponseStatus::InternalError)?
332                                .clamp(0, 100),
333                            cwarn: {
334                                let mut fs = FlagSet::empty();
335
336                                if ctlr.spare < ctlr.spare_range.lower {
337                                    fs |= crate::nvme::mi::CriticalWarningFlags::St;
338                                }
339
340                                if ctlr.temp < ctlr.temp_range.lower
341                                    || ctlr.temp > ctlr.temp_range.upper
342                                {
343                                    fs |= crate::nvme::mi::CriticalWarningFlags::Taut;
344                                }
345
346                                // TODO: RD
347
348                                if ctlr.ro {
349                                    fs |= crate::nvme::mi::CriticalWarningFlags::Ro;
350                                }
351
352                                // TODO: VMBF
353                                // TODO: PMRRO
354
355                                fs.into()
356                            },
357                            chsc: {
358                                let mecs = &mut mep.mecss[ctlr.id.0 as usize];
359                                let fs = mecs.chscf;
360
361                                if req.properties.0.contains(ControllerPropertyFlags::Ccf) {
362                                    mecs.chscf.clear();
363                                    // TODO: Clear NAC, FA, TCIDA in controller health
364                                }
365
366                                fs.into()
367                            },
368                        })
369                        .map_err(|_| {
370                            debug!("Failed to push ControllerHealthDataStructure");
371                            ResponseStatus::InternalError
372                        })?;
373                }
374                chspr.update()?;
375                let chspr = chspr.encode()?;
376
377                send_response(resp, &[&mh.0, &chspr.0[..chspr.1]]).await;
378                Ok(())
379            }
380            NvmeMiCommandRequestType::ConfigurationSet(cid) => {
381                cid.handle(ctx, mep, subsys, rest, resp, app).await
382            }
383            NvmeMiCommandRequestType::ConfigurationGet(cid) => {
384                cid.handle(ctx, mep, subsys, rest, resp, app).await
385            }
386            _ => {
387                debug!("Unimplemented OPCODE: {:?}", ctx.opcode);
388                Err(ResponseStatus::InternalError)
389            }
390        }
391    }
392}
393
394impl RequestHandler for NvmeMiConfigurationSetRequest {
395    type Ctx = NvmeMiCommandRequestHeader;
396
397    async fn handle<A, C>(
398        &self,
399        _ctx: &Self::Ctx,
400        mep: &mut crate::ManagementEndpoint,
401        subsys: &mut crate::Subsystem,
402        rest: &[u8],
403        resp: &mut C,
404        mut app: A,
405    ) -> Result<(), ResponseStatus>
406    where
407        A: AsyncFnMut(CommandEffect) -> Result<(), CommandEffectError>,
408        C: AsyncRespChannel,
409    {
410        match &self.body {
411            NvmeMiConfigurationIdentifierRequestType::Reserved => {
412                Err(ResponseStatus::InvalidParameter)
413            }
414            NvmeMiConfigurationIdentifierRequestType::SmbusI2cFrequency(sifr) => {
415                if !rest.is_empty() {
416                    debug!("Lost synchronisation when decoding ConfigurationSet SmbusI2cFrequency");
417                    return Err(ResponseStatus::InvalidCommandSize);
418                }
419
420                let Some(port) = subsys.ports.get_mut(sifr.portid as usize) else {
421                    debug!("Unrecognised port ID: {}", sifr.portid);
422                    return Err(ResponseStatus::InvalidParameter);
423                };
424
425                let crate::PortType::TwoWire(twprt) = &mut port.typ else {
426                    debug!("Port {} is not a TwoWire port: {:?}", sifr.portid, port);
427                    return Err(ResponseStatus::InvalidParameter);
428                };
429
430                if sifr.sfreq > twprt.msmbfreq.into() {
431                    debug!("Unsupported SMBus frequency: {:?}", sifr.sfreq);
432                    return Err(ResponseStatus::InvalidParameter);
433                }
434
435                app(CommandEffect::SetSmbusFreq {
436                    port_id: port.id,
437                    freq: sifr.sfreq.into(),
438                })
439                .await?;
440                twprt.smbfreq = sifr.sfreq.into();
441
442                let mh = MessageHeader::respond(MessageType::NvmeMiCommand).encode()?;
443
444                // Success
445                let status = [0u8; 4];
446
447                send_response(resp, &[&mh.0, &status]).await;
448                Ok(())
449            }
450            NvmeMiConfigurationIdentifierRequestType::HealthStatusChange(hscr) => {
451                if !rest.is_empty() {
452                    debug!(
453                        "Lost synchronisation when decoding ConfigurationSet HealthStatusChange"
454                    );
455                    return Err(ResponseStatus::InvalidCommandSize);
456                }
457
458                let Ok(clear) = FlagSet::<super::HealthStatusChangeFlags>::new(hscr.dw1) else {
459                    debug!(
460                        "Invalid composite controller status flags in request: {}",
461                        hscr.dw1
462                    );
463                    return Err(ResponseStatus::InvalidParameter);
464                };
465                let clear: super::CompositeControllerStatusFlagSet = clear.into();
466                mep.ccsf.0 -= clear.0;
467
468                let mh = MessageHeader::respond(MessageType::NvmeMiCommand).encode()?;
469
470                // Success
471                let status = [0u8; 4];
472
473                send_response(resp, &[&mh.0, &status]).await;
474                Ok(())
475            }
476            NvmeMiConfigurationIdentifierRequestType::MctpTransmissionUnitSize(mtusr) => {
477                if !rest.is_empty() {
478                    debug!(
479                        "Lost synchronisation when decoding ConfigurationSet MCTPTransmissionUnitSize"
480                    );
481                    return Err(ResponseStatus::InvalidCommandSize);
482                }
483
484                let Some(port) = subsys.ports.get_mut(mtusr.dw0_portid as usize) else {
485                    debug!("Unrecognised port ID: {}", mtusr.dw0_portid);
486                    return Err(ResponseStatus::InvalidParameter);
487                };
488
489                app(CommandEffect::SetMtu {
490                    port_id: port.id,
491                    mtus: mtusr.dw1_mtus as usize,
492                })
493                .await?;
494                port.mtus = mtusr.dw1_mtus;
495
496                let mh = MessageHeader::respond(MessageType::NvmeMiCommand).encode()?;
497                let status = [0u8; 4];
498
499                send_response(resp, &[&mh.0, &status]).await;
500                Ok(())
501            }
502            NvmeMiConfigurationIdentifierRequestType::AsynchronousEvent => todo!(),
503        }
504    }
505}
506
507impl RequestHandler for NvmeMiConfigurationGetRequest {
508    type Ctx = NvmeMiCommandRequestHeader;
509
510    async fn handle<A, C>(
511        &self,
512        _ctx: &Self::Ctx,
513        _mep: &mut crate::ManagementEndpoint,
514        subsys: &mut crate::Subsystem,
515        rest: &[u8],
516        resp: &mut C,
517        _app: A,
518    ) -> Result<(), ResponseStatus>
519    where
520        A: AsyncFnMut(CommandEffect) -> Result<(), CommandEffectError>,
521        C: AsyncRespChannel,
522    {
523        match &self.body {
524            NvmeMiConfigurationIdentifierRequestType::Reserved => {
525                Err(ResponseStatus::InvalidParameter)
526            }
527            NvmeMiConfigurationIdentifierRequestType::SmbusI2cFrequency(sifr) => {
528                if !rest.is_empty() {
529                    debug!("Lost synchronisation when decoding ConfigurationGet SMBusI2CFrequency");
530                    return Err(ResponseStatus::InvalidCommandSize);
531                }
532
533                let Some(port) = subsys.ports.get(sifr.portid as usize) else {
534                    debug!("Unrecognised port ID: {}", sifr.portid);
535                    return Err(ResponseStatus::InvalidParameter);
536                };
537
538                let crate::PortType::TwoWire(twprt) = port.typ else {
539                    debug!("Port {} is not a TwoWire port: {:?}", sifr.portid, port);
540                    return Err(ResponseStatus::InvalidParameter);
541                };
542
543                let mh = MessageHeader::respond(MessageType::NvmeMiCommand).encode()?;
544
545                let fr = GetSmbusI2cFrequencyResponse {
546                    status: ResponseStatus::Success,
547                    sfreq: twprt.smbfreq.into(),
548                }
549                .encode()?;
550
551                send_response(resp, &[&mh.0, &fr.0]).await;
552                Ok(())
553            }
554            NvmeMiConfigurationIdentifierRequestType::HealthStatusChange(_) => {
555                // MI v2.0, 5.1.2
556                if !rest.is_empty() {
557                    debug!(
558                        "Lost synchronisation when decoding ConfigurationGet HealthStatusChange"
559                    );
560                    return Err(ResponseStatus::InvalidCommandSize);
561                }
562
563                let mh = MessageHeader::respond(MessageType::NvmeMiCommand).encode()?;
564                let hscr = GetHealthStatusChangeResponse {
565                    status: ResponseStatus::Success,
566                }
567                .encode()?;
568
569                send_response(resp, &[&mh.0, &hscr.0]).await;
570                Ok(())
571            }
572            NvmeMiConfigurationIdentifierRequestType::MctpTransmissionUnitSize(mtusr) => {
573                if !rest.is_empty() {
574                    debug!(
575                        "Lost synchronisation when decoding ConfigurationGet MCTPTransmissionUnitSize"
576                    );
577                    return Err(ResponseStatus::InvalidCommandSize);
578                }
579
580                let Some(port) = subsys.ports.get(mtusr.dw0_portid as usize) else {
581                    debug!("Unrecognised port ID: {}", mtusr.dw0_portid);
582                    return Err(ResponseStatus::InvalidParameter);
583                };
584
585                let mh = MessageHeader::respond(MessageType::NvmeMiCommand).encode()?;
586
587                let fr = GetMctpTransmissionUnitSizeResponse {
588                    status: ResponseStatus::Success,
589                    mr_mtus: port.mtus,
590                }
591                .encode()?;
592
593                send_response(resp, &[&mh.0, &fr.0]).await;
594                Ok(())
595            }
596            NvmeMiConfigurationIdentifierRequestType::AsynchronousEvent => todo!(),
597        }
598    }
599}
600
601impl RequestHandler for NvmeMiDataStructureRequest {
602    type Ctx = NvmeMiCommandRequestHeader;
603
604    async fn handle<A, C>(
605        &self,
606        _ctx: &Self::Ctx,
607        _mep: &mut crate::ManagementEndpoint,
608        subsys: &mut crate::Subsystem,
609        rest: &[u8],
610        resp: &mut C,
611        _app: A,
612    ) -> Result<(), ResponseStatus>
613    where
614        A: AsyncFnMut(CommandEffect) -> Result<(), CommandEffectError>,
615        C: AsyncRespChannel,
616    {
617        if !rest.is_empty() {
618            debug!("Lost coherence decoding NVMe-MI message");
619            return Err(ResponseStatus::InvalidCommandInputDataSize);
620        }
621
622        let mh = MessageHeader::respond(MessageType::NvmeMiCommand).encode()?;
623
624        match self.body {
625            NvmeMiDataStructureRequestType::NvmSubsystemInformation => {
626                assert!(!subsys.ports.is_empty(), "Need at least one port defined");
627                // See 5.7.1, 5.1.1 of v2.0
628                assert!(
629                    subsys.ports.len() < u8::MAX as usize,
630                    "Too many ports defined: {}",
631                    subsys.ports.len()
632                );
633                // See 5.7.1 of v2.0
634                let nvmsi = NvmSubsystemInformationResponse {
635                    nump: subsys.ports.len() as u8 - 1,
636                    mjr: subsys.mi.mjr,
637                    mnr: subsys.mi.mnr,
638                    nnsc: subsys.caps.sre.into(),
639                }
640                .encode()?;
641
642                debug_assert!(nvmsi.0.len() <= u16::MAX as usize);
643                let dsmr = NvmeMiDataStructureManagementResponse {
644                    status: ResponseStatus::Success,
645                    rdl: nvmsi.0.len() as u16,
646                }
647                .encode()?;
648
649                send_response(resp, &[&mh.0, &dsmr.0, &nvmsi.0]).await;
650                Ok(())
651            }
652            NvmeMiDataStructureRequestType::PortInformation => {
653                let Some(port) = subsys.ports.iter().find(|p| p.id.0 == self.portid) else {
654                    // TODO: Propagate PEL
655                    return Err(ResponseStatus::InvalidParameter);
656                };
657                let pi = PortInformationResponse {
658                    prttyp: Into::<crate::nvme::mi::PortType>::into(&port.typ),
659                    prtcap: {
660                        let mut flags = FlagSet::empty();
661
662                        if port.caps.ciaps {
663                            flags |= PortCapabilityFlags::Ciaps;
664                        }
665
666                        if port.caps.aems {
667                            flags |= PortCapabilityFlags::Aems;
668                        }
669
670                        flags
671                    }
672                    .into(),
673                    mmtus: port.mmtus,
674                    mebs: port.mebs,
675                }
676                .encode()?;
677
678                match port.typ {
679                    crate::PortType::Pcie(pprt) => {
680                        let ppd = PciePortDataResponse {
681                            pciemps: pprt.mps.into(),
682                            pcieslsv: {
683                                PcieSupportedLinkSpeeds::Gts2p5
684                                    | PcieSupportedLinkSpeeds::Gts5
685                                    | PcieSupportedLinkSpeeds::Gts8
686                                    | PcieSupportedLinkSpeeds::Gts16
687                                    | PcieSupportedLinkSpeeds::Gts32
688                                    | PcieSupportedLinkSpeeds::Gts64
689                            }
690                            .into(),
691                            pciecls: pprt.cls.into(),
692                            pciemlw: pprt.mlw.into(),
693                            pcienlw: pprt.nlw.into(),
694                            pciepn: port.id.0,
695                        }
696                        .encode()?;
697
698                        debug_assert!(pi.0.len() + ppd.0.len() <= u16::MAX as usize);
699                        let dsmr = NvmeMiDataStructureManagementResponse {
700                            status: ResponseStatus::Success,
701                            rdl: (pi.0.len() + ppd.0.len()) as u16,
702                        }
703                        .encode()?;
704
705                        send_response(resp, &[&mh.0, &dsmr.0, &pi.0, &ppd.0]).await;
706                        Ok(())
707                    }
708                    crate::PortType::TwoWire(twprt) => {
709                        let twpd = TwoWirePortDataResponse {
710                            cvpdaddr: twprt.cvpdaddr,
711                            mvpdfreq: twprt.mvpdfreq.into(),
712                            cmeaddr: twprt.cmeaddr,
713                            twprt_i3csprt: twprt.i3csprt,
714                            twprt_msmbfreq: twprt.msmbfreq.into(),
715                            nvmebm: twprt.nvmebms.into(),
716                        }
717                        .encode()?;
718
719                        debug_assert!((pi.0.len() + twpd.0.len()) <= u16::MAX as usize);
720                        let dsmr = NvmeMiDataStructureManagementResponse {
721                            status: ResponseStatus::Success,
722                            rdl: (pi.0.len() + twpd.0.len()) as u16,
723                        }
724                        .encode()?;
725
726                        send_response(resp, &[&mh.0, &dsmr.0, &pi.0, &twpd.0]).await;
727                        Ok(())
728                    }
729                    _ => {
730                        debug!("Unimplemented port type: {:?}", port.typ);
731                        Err(ResponseStatus::InternalError)
732                    }
733                }
734            }
735            NvmeMiDataStructureRequestType::ControllerList => {
736                assert!(
737                    subsys.ctlrs.len() <= 2047,
738                    "Invalid number of controllers in drive model: {}",
739                    subsys.ctlrs.len()
740                );
741
742                let mut cl = ControllerListResponse::new();
743                for ctlr in subsys
744                    .ctlrs
745                    .iter()
746                    // Section 5.7.3, NVMe MI v2.0
747                    .filter(|c| c.id.0 >= self.ctrlid)
748                {
749                    if let Err(id) = cl.ids.push(ctlr.id.0) {
750                        debug!("Failed to push controller ID {id}");
751                        return Err(ResponseStatus::InternalError);
752                    };
753                }
754
755                // NVMeSubsystemInformation and PortInformation are defined to
756                // be a minimum of 32 bytes in v2.0 of the NVMe specification.
757                // ControllerList in the NVMe MI v2.0 specification defers to
758                // Figure 137 in v2.1 of the NVMe base specification, which
759                // says "Unused entries are zero-filled". The motivation
760                // for padding out to 32 bytes for NVMeSubsystemInformation
761                // and PortInformation messages is unclear wrt whether
762                // ControllerList should be similarly padded or dynamically
763                // sized, given it can extend to 4096 bytes.
764                //
765                // Assume it's always dynamically sized appropriate for the
766                // requested controllers for now. mi-mctp seems okay with this.
767                //
768                // Note that for zero or even numbers of controllers in the
769                // response the MIC falls out of natural alignment.
770                cl.update()?;
771                let cl = cl.encode()?;
772                let rdl = cl.1 as u16;
773
774                let dsmr = NvmeMiDataStructureManagementResponse {
775                    status: ResponseStatus::Success,
776                    rdl,
777                }
778                .encode()?;
779
780                send_response(resp, &[&mh.0, &dsmr.0, &cl.0[..cl.1]]).await;
781                Ok(())
782            }
783            NvmeMiDataStructureRequestType::ControllerInformation => {
784                let Some(ctlr) = subsys.ctlrs.iter().find(|c| c.id.0 == self.ctrlid) else {
785                    debug!("Unknown controller ID: {:?}", self.ctrlid);
786                    return Err(ResponseStatus::InvalidParameter);
787                };
788
789                let Some(port) = subsys.ports.iter().find(|p| p.id == ctlr.port) else {
790                    panic!(
791                        "Inconsistent port association for controller {:?}: {:?}",
792                        ctlr.id, ctlr.port
793                    );
794                };
795
796                let crate::PortType::Pcie(pprt) = port.typ else {
797                    panic!("Non-PCIe port associated with controller {:?}", ctlr.id);
798                };
799
800                let ci = ControllerInformationResponse {
801                    portid: ctlr.port.0,
802                    prii_pcieriv: true,
803                    pri_pcibn: pprt.b,
804                    pri_pcidn: pprt.d,
805                    pri_pcifn: pprt.f,
806                    pcivid: subsys.info.pci_vid,
807                    pcidid: subsys.info.pci_did,
808                    pcisvid: subsys.info.pci_svid,
809                    pcisdid: subsys.info.pci_sdid,
810                    pciesn: pprt.seg,
811                }
812                .encode()?;
813
814                debug_assert!(ci.0.len() < u16::MAX as usize);
815                let dsmr = NvmeMiDataStructureManagementResponse {
816                    status: ResponseStatus::Success,
817                    rdl: ci.0.len() as u16,
818                }
819                .encode()?;
820
821                send_response(resp, &[&mh.0, &dsmr.0, &ci.0]).await;
822                Ok(())
823            }
824            _ => {
825                debug!("Unimplemented DTYP: {:?}", self.dtyp);
826                Err(ResponseStatus::InternalError)
827            }
828        }
829    }
830}
831
832impl RequestHandler for AdminCommandRequestHeader {
833    type Ctx = Self;
834
835    async fn handle<A, C>(
836        &self,
837        ctx: &Self::Ctx,
838        mep: &mut crate::ManagementEndpoint,
839        subsys: &mut crate::Subsystem,
840        rest: &[u8],
841        resp: &mut C,
842        app: A,
843    ) -> Result<(), ResponseStatus>
844    where
845        A: AsyncFnMut(CommandEffect) -> Result<(), CommandEffectError>,
846        C: AsyncRespChannel,
847    {
848        debug!("{self:x?}");
849
850        // ISH
851        if ctx.cflgs & 4 != 0 {
852            debug!("Support ignore shutdown state");
853            return Err(ResponseStatus::InternalError);
854        }
855
856        match &self.op {
857            AdminCommandRequestType::GetLogPage(req) => {
858                req.handle(ctx, mep, subsys, rest, resp, app).await
859            }
860            AdminCommandRequestType::Identify(req) => {
861                req.handle(ctx, mep, subsys, rest, resp, app).await
862            }
863            AdminCommandRequestType::NamespaceAttachement(req) => {
864                req.handle(ctx, mep, subsys, rest, resp, app).await
865            }
866            AdminCommandRequestType::NamespaceManagement(req) => {
867                req.handle(ctx, mep, subsys, rest, resp, app).await
868            }
869            AdminCommandRequestType::FormatNvm(req) => {
870                req.handle(ctx, mep, subsys, rest, resp, app).await
871            }
872            AdminCommandRequestType::Sanitize(req) => {
873                req.handle(ctx, mep, subsys, rest, resp, app).await
874            }
875            AdminCommandRequestType::DeleteIoSubmissionQueue
876            | AdminCommandRequestType::CreateIoSubmissionQueue
877            | AdminCommandRequestType::DeleteIoCompletionQueue
878            | AdminCommandRequestType::CreateIoCompletionQueue
879            | AdminCommandRequestType::Abort
880            | AdminCommandRequestType::AsynchronousEventRequest
881            | AdminCommandRequestType::KeepAlive
882            | AdminCommandRequestType::DirectiveSend
883            | AdminCommandRequestType::DirectiveReceive
884            | AdminCommandRequestType::NvmeMiSend
885            | AdminCommandRequestType::NvmeMiReceive
886            | AdminCommandRequestType::DiscoveryInformationManagement
887            | AdminCommandRequestType::FabricZoningReceive
888            | AdminCommandRequestType::FabricZoningLookup
889            | AdminCommandRequestType::FabricZoningSend
890            | AdminCommandRequestType::SendDiscoveryLogPage
891            | AdminCommandRequestType::TrackSend
892            | AdminCommandRequestType::TrackReceive
893            | AdminCommandRequestType::MigrationSend
894            | AdminCommandRequestType::MigrationReceive
895            | AdminCommandRequestType::ControllerDataQueue
896            | AdminCommandRequestType::DoorbellBufferConfig
897            | AdminCommandRequestType::FabricsCommands
898            | AdminCommandRequestType::LoadProgram
899            | AdminCommandRequestType::ProgramActivationManagement
900            | AdminCommandRequestType::MemoryRangeSetManagement => {
901                debug!("Prohibited MI admin command opcode: {:?}", self.op.id());
902                Err(ResponseStatus::InvalidCommandOpcode)
903            }
904            _ => {
905                debug!("Unimplemented OPCODE: {:?}", self.op.id());
906                Err(ResponseStatus::InternalError)
907            }
908        }
909    }
910}
911
912fn admin_constrain_body(dofst: u32, dlen: u32, body: &[u8]) -> Result<&[u8], ResponseStatus> {
913    // See Figure 136 in NVMe MI v2.0
914
915    // Use send_response() instead
916    assert!(!body.is_empty());
917
918    // TODO: propagate PEL for all errors
919    if dofst & 3 != 0 {
920        debug!("Unnatural DOFST value: {dofst:?}");
921        return Err(ResponseStatus::InvalidParameter);
922    }
923
924    // FIXME: casts
925    let dofst = dofst as usize;
926    let dlen = dlen as usize;
927
928    if dofst >= body.len() {
929        debug!("DOFST value exceeds unconstrained response length: {dofst:?}");
930        return Err(ResponseStatus::InvalidParameter);
931    }
932
933    if dlen & 3 != 0 {
934        debug!("Unnatural DLEN value: {dlen:?}");
935        return Err(ResponseStatus::InvalidParameter);
936    }
937
938    if dlen > 4096 {
939        debug!("DLEN too large: {dlen:?}");
940        return Err(ResponseStatus::InvalidParameter);
941    }
942
943    if dlen > body.len() || body.len() - dlen < dofst {
944        debug!(
945            "Requested response data range beginning at {:?} for {:?} bytes exceeds bounds of unconstrained response length {:?}",
946            dofst,
947            dlen,
948            body.len()
949        );
950        return Err(ResponseStatus::InvalidParameter);
951    }
952
953    if dlen == 0 {
954        debug!("DLEN cleared for command with data response: {dlen:?}");
955        return Err(ResponseStatus::InvalidParameter);
956    }
957
958    let end = dofst + dlen;
959    Ok(&body[dofst..end])
960}
961
962async fn admin_send_response_body<C>(resp: &mut C, body: &[u8]) -> Result<(), ResponseStatus>
963where
964    C: AsyncRespChannel,
965{
966    let mh = MessageHeader::respond(MessageType::NvmeAdminCommand).encode()?;
967
968    let acrh = AdminCommandResponseHeader {
969        status: ResponseStatus::Success,
970        cqedw0: 0,
971        cqedw1: 0,
972        cqedw3: AdminIoCqeStatusType::GenericCommandStatus(
973            AdminIoCqeGenericCommandStatus::SuccessfulCompletion,
974        )
975        .into(),
976    }
977    .encode()?;
978
979    send_response(resp, &[&mh.0, &acrh.0, body]).await;
980
981    Ok(())
982}
983
984async fn admin_send_status<C>(
985    resp: &mut C,
986    status: AdminIoCqeStatusType,
987) -> Result<(), ResponseStatus>
988where
989    C: AsyncRespChannel,
990{
991    let mh = MessageHeader::respond(MessageType::NvmeAdminCommand).encode()?;
992
993    let acrh = AdminCommandResponseHeader {
994        status: ResponseStatus::Success,
995        cqedw0: 0,
996        cqedw1: 0,
997        cqedw3: status.into(),
998    }
999    .encode()?;
1000
1001    send_response(resp, &[&mh.0, &acrh.0]).await;
1002
1003    Ok(())
1004}
1005
1006impl RequestHandler for AdminGetLogPageRequest {
1007    type Ctx = AdminCommandRequestHeader;
1008
1009    async fn handle<A, C>(
1010        &self,
1011        ctx: &Self::Ctx,
1012        _mep: &mut crate::ManagementEndpoint,
1013        subsys: &mut crate::Subsystem,
1014        rest: &[u8],
1015        resp: &mut C,
1016        _app: A,
1017    ) -> Result<(), ResponseStatus>
1018    where
1019        A: AsyncFnMut(CommandEffect) -> Result<(), CommandEffectError>,
1020        C: AsyncRespChannel,
1021    {
1022        if !rest.is_empty() {
1023            debug!("Invalid request size for Admin Get Log Page");
1024            return Err(ResponseStatus::InvalidCommandSize);
1025        }
1026
1027        // Base v2.1, 5.1.12, Figure 202
1028        match &self.req {
1029            AdminGetLogPageLidRequestType::SupportedLogPages
1030            | AdminGetLogPageLidRequestType::FeatureIdentifiersSupportedAndEffects => {
1031                if self.csi != 0 {
1032                    debug!("Support CSI");
1033                    return admin_send_status(
1034                        resp,
1035                        AdminIoCqeStatusType::GenericCommandStatus(
1036                            AdminIoCqeGenericCommandStatus::InternalError,
1037                        ),
1038                    )
1039                    .await;
1040                }
1041            }
1042            AdminGetLogPageLidRequestType::ErrorInformation
1043            | AdminGetLogPageLidRequestType::SmartHealthInformation
1044            | AdminGetLogPageLidRequestType::SanitizeStatus => (),
1045        };
1046
1047        let Some(ctlr) = subsys.ctlrs.get(ctx.ctlid as usize) else {
1048            debug!("Unrecognised CTLID: {}", ctx.ctlid);
1049            return admin_send_status(
1050                resp,
1051                AdminIoCqeStatusType::GenericCommandStatus(
1052                    AdminIoCqeGenericCommandStatus::InvalidFieldInCommand,
1053                ),
1054            )
1055            .await;
1056        };
1057
1058        let Some(flags) = ctlr.lsaes.get(self.req.id() as usize) else {
1059            debug!(
1060                "LSAE mismatch with known LID {:?} on controller {}",
1061                self.req, ctlr.id.0
1062            );
1063            return admin_send_status(
1064                resp,
1065                AdminIoCqeStatusType::GenericCommandStatus(
1066                    AdminIoCqeGenericCommandStatus::InvalidFieldInCommand,
1067                ),
1068            )
1069            .await;
1070        };
1071
1072        // Base v2.1, 5.1.12
1073        if self.ot != 0 {
1074            // Base v2.1, 5.1.12, Figure 199, LPOL
1075            if flags.contains(LidSupportedAndEffectsFlags::Ios) {
1076                todo!("Add OT support");
1077            } else {
1078                return admin_send_status(
1079                    resp,
1080                    AdminIoCqeStatusType::GenericCommandStatus(
1081                        AdminIoCqeGenericCommandStatus::InvalidFieldInCommand,
1082                    ),
1083                )
1084                .await;
1085            }
1086        }
1087
1088        // Base v2.1, 5.1.12
1089        let _numdw = if ctlr.lpa.contains(LogPageAttributes::Lpeds) {
1090            todo!("Add support for extended NUMDL / NUMDU")
1091        } else {
1092            self.numdw & ((1u32 << 13) - 1)
1093        };
1094
1095        // TODO: RAE processing
1096
1097        match &self.req {
1098            AdminGetLogPageLidRequestType::SupportedLogPages => {
1099                if (self.numdw + 1) * 4 != 1024 {
1100                    debug!("Implement support for NUMDL / NUMDU");
1101                    return admin_send_status(
1102                        resp,
1103                        AdminIoCqeStatusType::GenericCommandStatus(
1104                            AdminIoCqeGenericCommandStatus::InternalError,
1105                        ),
1106                    )
1107                    .await;
1108                }
1109
1110                let mut lsids = WireVec::new();
1111                for e in ctlr.lsaes {
1112                    let lsaeds = LidSupportedAndEffectsDataStructure {
1113                        flags: e.into(),
1114                        lidsp: 0,
1115                    };
1116                    lsids.push(lsaeds).map_err(|_| {
1117                        debug!("Failed to push LidSupportedAndEffectsDataStructure");
1118                        ResponseStatus::InternalError
1119                    })?;
1120                }
1121
1122                let slpr = AdminGetLogPageSupportedLogPagesResponse { lsids }.encode()?;
1123
1124                admin_send_response_body(
1125                    resp,
1126                    admin_constrain_body(self.dofst, self.dlen, &slpr.0)?,
1127                )
1128                .await
1129            }
1130            AdminGetLogPageLidRequestType::ErrorInformation => {
1131                if (self.numdw + 1) * 4 != 64 {
1132                    debug!("Implement support for NUMDL / NUMDU");
1133                    return admin_send_status(
1134                        resp,
1135                        AdminIoCqeStatusType::GenericCommandStatus(
1136                            AdminIoCqeGenericCommandStatus::InternalError,
1137                        ),
1138                    )
1139                    .await;
1140                }
1141                admin_send_response_body(
1142                    resp,
1143                    admin_constrain_body(self.dofst, self.dlen, &[0u8; 64])?,
1144                )
1145                .await
1146            }
1147            AdminGetLogPageLidRequestType::SmartHealthInformation => {
1148                if (self.numdw + 1) * 4 != 512 {
1149                    debug!("Implement support for NUMDL / NUMDU");
1150                    return admin_send_status(
1151                        resp,
1152                        AdminIoCqeStatusType::GenericCommandStatus(
1153                            AdminIoCqeGenericCommandStatus::InternalError,
1154                        ),
1155                    )
1156                    .await;
1157                }
1158
1159                // Base v2.1, 5.1.2, Figure 199
1160                let lpol = self.lpo & !3u64;
1161                if lpol > 512 {
1162                    return admin_send_status(
1163                        resp,
1164                        AdminIoCqeStatusType::GenericCommandStatus(
1165                            AdminIoCqeGenericCommandStatus::InvalidFieldInCommand,
1166                        ),
1167                    )
1168                    .await;
1169                }
1170
1171                if self.nsid != 0 && self.nsid != u32::MAX {
1172                    if ctlr.lpa.contains(LogPageAttributes::Smarts) {
1173                        todo!();
1174                    } else {
1175                        return admin_send_status(
1176                            resp,
1177                            AdminIoCqeStatusType::GenericCommandStatus(
1178                                AdminIoCqeGenericCommandStatus::InvalidFieldInCommand,
1179                            ),
1180                        )
1181                        .await;
1182                    }
1183                }
1184
1185                let shilpr = SmartHealthInformationLogPageResponse {
1186                    cw: {
1187                        let mut fs = FlagSet::empty();
1188
1189                        if ctlr.spare < ctlr.spare_range.lower {
1190                            fs |= crate::nvme::CriticalWarningFlags::Ascbt;
1191                        }
1192
1193                        if ctlr.temp < ctlr.temp_range.lower || ctlr.temp > ctlr.temp_range.upper {
1194                            fs |= crate::nvme::CriticalWarningFlags::Ttc;
1195                        }
1196
1197                        // TODO: NDR
1198
1199                        if ctlr.ro {
1200                            fs |= crate::nvme::CriticalWarningFlags::Amro;
1201                        }
1202
1203                        // TODO: VMBF
1204                        // TODO: PMRRO
1205
1206                        fs.into()
1207                    },
1208                    ctemp: ctlr.temp,
1209                    avsp: <u8>::try_from(100 * ctlr.spare / ctlr.capacity)
1210                        .map_err(|_| ResponseStatus::InternalError)?
1211                        .clamp(0, 100),
1212                    avspt: <u8>::try_from(100 * ctlr.spare_range.lower / ctlr.capacity)
1213                        .map_err(|_| ResponseStatus::InternalError)?
1214                        .clamp(0, 100),
1215                    pused: (100 * ctlr.write_age / ctlr.write_lifespan).clamp(0, 255) as u8,
1216                    egcws: FlagSet::empty().into(), // TODO: Endurance Groups
1217                    dur: 0,
1218                    duw: 0,
1219                    hrc: 0,
1220                    hwc: 0,
1221                    cbt: 0,
1222                    pwrc: 0, // TOOD: track power cycles
1223                    poh: 0,  // TODO: Track power on hours
1224                    upl: 0,  // TODO: Track unexpected power loss
1225                    mdie: 0,
1226                    neile: 0, // TODO: Track error log entries
1227                    wctt: 0,  // TODO: Track temperature excursions
1228                    cctt: 0,  // TODO: track temperature excursions
1229                    tsen: [ctlr.temp; 8],
1230                    tmttc: [0; 2],
1231                    tttmt: [0; 2],
1232                }
1233                .encode()?;
1234
1235                admin_send_response_body(
1236                    resp,
1237                    admin_constrain_body(self.dofst, self.dlen, &shilpr.0)?,
1238                )
1239                .await
1240            }
1241            AdminGetLogPageLidRequestType::FeatureIdentifiersSupportedAndEffects => {
1242                if (self.numdw + 1) * 4 != 1024 {
1243                    debug!("Implement support for NUMDL / NUMDU");
1244                    return admin_send_status(
1245                        resp,
1246                        AdminIoCqeStatusType::GenericCommandStatus(
1247                            AdminIoCqeGenericCommandStatus::InternalError,
1248                        ),
1249                    )
1250                    .await;
1251                }
1252
1253                admin_send_response_body(
1254                    resp,
1255                    admin_constrain_body(
1256                        self.dofst,
1257                        self.dlen,
1258                        // TODO: Support feature reporting
1259                        &[0u8; 1024],
1260                    )?,
1261                )
1262                .await
1263            }
1264            AdminGetLogPageLidRequestType::SanitizeStatus => {
1265                if (self.numdw + 1) * 4 != 512 {
1266                    debug!("Implement support for NUMDL / NUMDU");
1267                    return admin_send_status(
1268                        resp,
1269                        AdminIoCqeStatusType::GenericCommandStatus(
1270                            AdminIoCqeGenericCommandStatus::InternalError,
1271                        ),
1272                    )
1273                    .await;
1274                }
1275
1276                let sslpr = SanitizeStatusLogPageResponse {
1277                    sprog: u16::MAX,
1278                    sstat: subsys.sstat,
1279                    scdw10: subsys.sconf.unwrap_or_default(),
1280                    eto: u32::MAX,
1281                    etbe: u32::MAX,
1282                    etce: u32::MAX,
1283                    etodmm: u32::MAX,
1284                    etbenmm: u32::MAX,
1285                    etcenmm: u32::MAX,
1286                    etpvds: u32::MAX,
1287                    ssi: subsys.ssi,
1288                }
1289                .encode()?;
1290
1291                admin_send_response_body(
1292                    resp,
1293                    admin_constrain_body(self.dofst, self.dlen, &sslpr.0)?,
1294                )
1295                .await
1296            }
1297        }
1298    }
1299}
1300
1301impl RequestHandler for AdminIdentifyRequest {
1302    type Ctx = AdminCommandRequestHeader;
1303
1304    async fn handle<A, C>(
1305        &self,
1306        ctx: &Self::Ctx,
1307        _mep: &mut crate::ManagementEndpoint,
1308        subsys: &mut crate::Subsystem,
1309        rest: &[u8],
1310        resp: &mut C,
1311        _app: A,
1312    ) -> Result<(), ResponseStatus>
1313    where
1314        A: AsyncFnMut(CommandEffect) -> Result<(), CommandEffectError>,
1315        C: AsyncRespChannel,
1316    {
1317        if !rest.is_empty() {
1318            debug!("Invalid request size for Admin Identify");
1319            return Err(ResponseStatus::InvalidCommandSize);
1320        }
1321
1322        let res = match &self.req {
1323            AdminIdentifyCnsRequestType::NvmIdentifyNamespace => {
1324                match NamespaceId(self.nsid).disposition(subsys) {
1325                    NamespaceIdDisposition::Invalid => {
1326                        debug!("Invalid NSID: {}", self.nsid);
1327                        Err(AdminIoCqeGenericCommandStatus::InvalidNamespaceOrFormat)
1328                    }
1329                    NamespaceIdDisposition::Broadcast => {
1330                        AdminIdentifyNvmIdentifyNamespaceResponse {
1331                            lbaf0_lbads: 9, // TODO: Tie to controller model
1332                            ..Default::default()
1333                        }
1334                        .encode()
1335                        .map_err(AdminIoCqeGenericCommandStatus::from)
1336                    }
1337                    NamespaceIdDisposition::Unallocated => {
1338                        debug!("Unallocated NSID: {}", self.nsid);
1339                        Err(AdminIoCqeGenericCommandStatus::InvalidNamespaceOrFormat)
1340                    }
1341                    NamespaceIdDisposition::Inactive(_) => {
1342                        AdminIdentifyNvmIdentifyNamespaceResponse::default()
1343                            .encode()
1344                            .map_err(AdminIoCqeGenericCommandStatus::from)
1345                    }
1346                    // 4.1.5.1 NVM Command Set Spec, v1.0c
1347                    NamespaceIdDisposition::Active(ns) => {
1348                        Into::<AdminIdentifyNvmIdentifyNamespaceResponse>::into(ns)
1349                            .encode()
1350                            .map_err(AdminIoCqeGenericCommandStatus::from)
1351                    }
1352                }
1353            }
1354            AdminIdentifyCnsRequestType::IdentifyController => {
1355                if let Some(ctlr) = subsys.ctlrs.get(ctx.ctlid as usize) {
1356                    AdminIdentifyControllerResponse {
1357                        vid: subsys.info.pci_vid,
1358                        ssvid: subsys.info.pci_svid,
1359                        sn: WireString::from(subsys.sn)?,
1360                        mn: WireString::from(subsys.mn)?,
1361                        fr: WireString::from(subsys.fr)?,
1362                        rab: 0,
1363                        ieee: {
1364                            // 4.5.3, Base v2.1
1365                            let mut fixup = subsys.info.ieee_oui;
1366                            fixup.reverse();
1367                            fixup
1368                        },
1369                        cmic: {
1370                            let mut flags = FlagSet::empty();
1371
1372                            if subsys.ctlrs.len() > 1 {
1373                                flags |=
1374                                    ControllerMultipathIoNamespaceSharingCapabilityFlags::Mctrs;
1375                            }
1376
1377                            if subsys.ports.len() > 1 {
1378                                flags |=
1379                                    ControllerMultipathIoNamespaceSharingCapabilityFlags::Mports;
1380                            }
1381
1382                            flags
1383                        }
1384                        .into(),
1385                        mdts: 0,
1386                        cntlid: ctlr.id.0,
1387                        ver: 0,
1388                        rtd3r: 0,
1389                        rtd3e: 0,
1390                        oaes: 0,
1391                        // TODO: Tie to data model
1392                        ctratt: {
1393                            let mut flags = FlagSet::empty();
1394
1395                            flags -= ControllerAttributeFlags::Nsets;
1396                            flags -= ControllerAttributeFlags::Egs;
1397                            flags -= ControllerAttributeFlags::Deg;
1398                            flags -= ControllerAttributeFlags::Dnvms;
1399
1400                            flags
1401                        }
1402                        .into(),
1403                        cntrltype: ctlr.cntrltype.into(),
1404                        // TODO: Tie to data model
1405                        nvmsr: { FlagSet::empty() | NvmSubsystemReportFlags::Nvmesd }.into(),
1406                        vwci: 0,
1407                        mec: {
1408                            let mut flags = FlagSet::empty();
1409
1410                            if subsys
1411                                .ports
1412                                .iter()
1413                                .any(|p| matches!(p.typ, crate::PortType::Pcie(_)))
1414                            {
1415                                flags |= ManagementEndpointCapabilityFlags::Pcieme;
1416                            }
1417
1418                            if subsys
1419                                .ports
1420                                .iter()
1421                                .any(|p| matches!(p.typ, crate::PortType::TwoWire(_)))
1422                            {
1423                                flags |= ManagementEndpointCapabilityFlags::Twpme;
1424                            }
1425
1426                            flags
1427                        }
1428                        .into(),
1429                        ocas: 0,
1430                        acl: 0,
1431                        aerl: 0,
1432                        frmw: 0,
1433                        lpa: ctlr.lpa.into(),
1434                        elpe: 0,
1435                        npss: 0,
1436                        avscc: 0,
1437                        wctemp: 0x157,
1438                        cctemp: 0x157,
1439                        fwug: 0,
1440                        kas: 0,
1441                        cqt: 0,
1442                        sqes: 0,
1443                        cqes: 0,
1444                        maxcmd: 0,
1445                        nn: NamespaceId::max(subsys),
1446                        oncs: 0,
1447                        fuses: 0,
1448                        fna: ctlr.fna.into(),
1449                        vwc: 0,
1450                        awun: 0,
1451                        awupf: 0,
1452                        icsvscc: 0,
1453                        nwpc: 0,
1454                        mnan: 0,
1455                        subnqn: WireString::new(),
1456                        fcatt: 0,
1457                        msdbd: 0,
1458                        ofcs: 0,
1459                        apsta: 0,
1460                        sanicap: crate::nvme::SanitizeCapabilities {
1461                            caps: subsys.sanicap.into(),
1462                            nodmmas: subsys.nodmmas,
1463                        },
1464                    }
1465                    .encode()
1466                    .map_err(AdminIoCqeGenericCommandStatus::from)
1467                } else {
1468                    debug!("No such CTLID: {}", ctx.ctlid);
1469                    Err(AdminIoCqeGenericCommandStatus::InvalidFieldInCommand)
1470                }
1471            }
1472            AdminIdentifyCnsRequestType::ActiveNamespaceIDList => {
1473                // 5.1.13.2.2, Base v2.1
1474                let mut active: heapless::Vec<u32, MAX_NAMESPACES> = subsys
1475                    .ctlrs
1476                    .iter()
1477                    .flat_map(|c| c.active_ns.iter())
1478                    .map(|nsid| nsid.0)
1479                    .filter(|nsid| *nsid > self.nsid)
1480                    .collect();
1481                active.sort_unstable();
1482                // IndexSet iterator is insertion-order:
1483                // https://docs.rs/heapless/0.8.0/heapless/struct.IndexSet.html#method.iter
1484                let unique: heapless::FnvIndexSet<u32, MAX_NAMESPACES> =
1485                    active.iter().copied().collect();
1486
1487                let mut aianidlr = AdminIdentifyActiveNamespaceIdListResponse::new();
1488                // TODO: Improve this with better iterator handling?
1489                for nsid in unique.iter() {
1490                    if aianidlr.nsid.push(*nsid).is_err() {
1491                        debug!("Failed to insert NSID {nsid}");
1492                        return Err(ResponseStatus::InternalError);
1493                    };
1494                }
1495                aianidlr
1496                    .encode()
1497                    .map_err(AdminIoCqeGenericCommandStatus::from)
1498            }
1499            AdminIdentifyCnsRequestType::NamespaceIdentificationDescriptorList => {
1500                // 5.1.13.2.3, Base v2.1
1501                match NamespaceId(self.nsid).disposition(subsys) {
1502                    NamespaceIdDisposition::Invalid => {
1503                        if self.nsid == u32::MAX - 1 {
1504                            debug!(
1505                                "Unacceptable NSID for Namespace Identification Descriptor List"
1506                            );
1507                        } else {
1508                            debug!("Invalid NSID: {}", self.nsid);
1509                        }
1510                        Err(AdminIoCqeGenericCommandStatus::InvalidNamespaceOrFormat)
1511                    }
1512                    NamespaceIdDisposition::Broadcast => {
1513                        debug!("Invalid NSID: {}", self.nsid);
1514                        Err(AdminIoCqeGenericCommandStatus::InvalidNamespaceOrFormat)
1515                    }
1516                    NamespaceIdDisposition::Unallocated => {
1517                        debug!("Unallocated NSID: {}", self.nsid);
1518                        Err(AdminIoCqeGenericCommandStatus::InvalidNamespaceOrFormat)
1519                    }
1520                    NamespaceIdDisposition::Inactive(ns) | NamespaceIdDisposition::Active(ns) => {
1521                        AdminIdentifyNamespaceIdentificationDescriptorListResponse {
1522                            nids: {
1523                                let mut vec = WireVec::new();
1524                                for nid in &ns.nids {
1525                                    if vec
1526                                        .push(Into::<NamespaceIdentifierType>::into(*nid))
1527                                        .is_err()
1528                                    {
1529                                        debug!("Failed to push NID {nid:?}");
1530                                        return Err(ResponseStatus::InternalError);
1531                                    }
1532                                }
1533                                vec
1534                            },
1535                        }
1536                        .encode()
1537                        .map_err(AdminIoCqeGenericCommandStatus::from)
1538                    }
1539                }
1540            }
1541            AdminIdentifyCnsRequestType::AllocatedNamespaceIdList => {
1542                // 5.1.13.2.9, Base v2.1
1543                if self.nsid >= u32::MAX - 1 {
1544                    debug!("Invalid NSID");
1545                    return Err(ResponseStatus::InvalidParameter);
1546                }
1547
1548                assert!(NamespaceId::max(subsys) < (4096 / core::mem::size_of::<u32>()) as u32);
1549                AdminIdentifyAllocatedNamespaceIdListResponse {
1550                    nsid: {
1551                        let mut allocated: heapless::Vec<u32, MAX_NAMESPACES> = subsys
1552                            .nss
1553                            .iter()
1554                            .map(|ns| ns.id.0)
1555                            .filter(|nsid| *nsid > self.nsid)
1556                            .collect();
1557                        allocated.sort_unstable();
1558                        let mut vec = WireVec::new();
1559                        for nsid in allocated {
1560                            if vec.push(nsid).is_err() {
1561                                debug!("Failed to insert NSID {nsid}");
1562                                return Err(ResponseStatus::InternalError);
1563                            };
1564                        }
1565                        vec
1566                    },
1567                }
1568                .encode()
1569                .map_err(AdminIoCqeGenericCommandStatus::from)
1570            }
1571            AdminIdentifyCnsRequestType::IdentifyNamespaceForAllocatedNamespaceId => {
1572                // Base v2.1, 5.1.13.2.10
1573                match NamespaceId(self.nsid).disposition(subsys) {
1574                    NamespaceIdDisposition::Invalid | NamespaceIdDisposition::Broadcast => {
1575                        Err(AdminIoCqeGenericCommandStatus::InvalidNamespaceOrFormat)
1576                    }
1577                    NamespaceIdDisposition::Unallocated => {
1578                        AdminIdentifyNvmIdentifyNamespaceResponse::default()
1579                            .encode()
1580                            .map_err(AdminIoCqeGenericCommandStatus::from)
1581                    }
1582                    NamespaceIdDisposition::Inactive(ns) | NamespaceIdDisposition::Active(ns) => {
1583                        let ainvminr: AdminIdentifyNvmIdentifyNamespaceResponse = ns.into();
1584                        ainvminr
1585                            .encode()
1586                            .map_err(AdminIoCqeGenericCommandStatus::from)
1587                    }
1588                }
1589            }
1590            AdminIdentifyCnsRequestType::NamespaceAttachedControllerList => {
1591                match NamespaceId(self.nsid).disposition(subsys) {
1592                    NamespaceIdDisposition::Invalid => ControllerListResponse::new()
1593                        .encode()
1594                        .map_err(AdminIoCqeGenericCommandStatus::from),
1595                    NamespaceIdDisposition::Broadcast => {
1596                        Err(AdminIoCqeGenericCommandStatus::InvalidFieldInCommand)
1597                    }
1598                    NamespaceIdDisposition::Unallocated | NamespaceIdDisposition::Inactive(_) => {
1599                        ControllerListResponse::new()
1600                            .encode()
1601                            .map_err(AdminIoCqeGenericCommandStatus::from)
1602                    }
1603                    NamespaceIdDisposition::Active(ns) => {
1604                        let mut clr = ControllerListResponse::new();
1605                        for cid in subsys.ctlrs.iter().filter_map(|c| {
1606                            if c.id.0 >= self.cntid && c.active_ns.contains(&ns.id) {
1607                                Some(c.id)
1608                            } else {
1609                                None
1610                            }
1611                        }) {
1612                            if let Err(id) = clr.ids.push(cid.0) {
1613                                debug!("Failed to push controller ID {id}");
1614                                return Err(ResponseStatus::InternalError);
1615                            }
1616                        }
1617                        clr.update()?;
1618                        clr.encode().map_err(AdminIoCqeGenericCommandStatus::from)
1619                    }
1620                }
1621            }
1622            AdminIdentifyCnsRequestType::NvmSubsystemControllerList => {
1623                assert!(
1624                    subsys.ctlrs.len() <= 2047,
1625                    "Invalid number of controllers in drive model: {}",
1626                    subsys.ctlrs.len()
1627                );
1628                let mut cl = ControllerListResponse::new();
1629                for ctlr in subsys.ctlrs.iter().filter(|v| v.id.0 >= self.cntid) {
1630                    if let Err(id) = cl.ids.push(ctlr.id.0) {
1631                        debug!("Failed to push controller ID {id}");
1632                        return Err(ResponseStatus::InternalError);
1633                    };
1634                }
1635                cl.update()?;
1636                cl.encode().map_err(AdminIoCqeGenericCommandStatus::from)
1637            }
1638            AdminIdentifyCnsRequestType::SecondaryControllerList => {
1639                let Some(ctlr) = subsys.ctlrs.get(ctx.ctlid as usize) else {
1640                    debug!("No such CTLID: {}", ctx.ctlid);
1641                    return Err(ResponseStatus::InvalidParameter);
1642                };
1643
1644                if !ctlr.secondaries.is_empty() {
1645                    todo!("Support listing secondary controllers");
1646                }
1647
1648                Ok(([0u8; 4096], 4096usize))
1649            }
1650            _ => {
1651                debug!("Unimplemented CNS: {self:?}");
1652                return Err(ResponseStatus::InternalError);
1653            }
1654        };
1655
1656        match res {
1657            Ok(response) => {
1658                admin_send_response_body(
1659                    resp,
1660                    admin_constrain_body(self.dofst, self.dlen, &response.0)?,
1661                )
1662                .await
1663            }
1664            Err(err) => {
1665                admin_send_status(resp, AdminIoCqeStatusType::GenericCommandStatus(err)).await
1666            }
1667        }
1668    }
1669}
1670
1671impl RequestHandler for AdminNamespaceManagementRequest {
1672    type Ctx = AdminCommandRequestHeader;
1673
1674    async fn handle<A, C>(
1675        &self,
1676        _ctx: &Self::Ctx,
1677        _mep: &mut crate::ManagementEndpoint,
1678        subsys: &mut crate::Subsystem,
1679        rest: &[u8],
1680        resp: &mut C,
1681        _app: A,
1682    ) -> Result<(), ResponseStatus>
1683    where
1684        A: AsyncFnMut(CommandEffect) -> Result<(), CommandEffectError>,
1685        C: AsyncRespChannel,
1686    {
1687        #[repr(u8)]
1688        enum CommandSpecificStatus {
1689            NamespaceIdentifierUnavailable = 0x16,
1690        }
1691        unsafe impl Discriminant<u8> for CommandSpecificStatus {}
1692
1693        if !rest.is_empty() {
1694            debug!("Invalid request size for Admin Identify");
1695            return Err(ResponseStatus::InvalidCommandSize);
1696        }
1697
1698        match &self.req {
1699            crate::nvme::mi::AdminNamespaceManagementSelect::Create(req) => {
1700                if self.csi != 0 {
1701                    debug!("Support CSI {}", self.csi);
1702                    return admin_send_status(
1703                        resp,
1704                        AdminIoCqeStatusType::GenericCommandStatus(
1705                            AdminIoCqeGenericCommandStatus::InternalError,
1706                        ),
1707                    )
1708                    .await;
1709                }
1710
1711                let Ok(nsid) = subsys.add_namespace(req.ncap) else {
1712                    debug!("Failed to create namespace");
1713                    // TODO: Implement Base v2.1, 5.1.21.1, Figure 370
1714                    return admin_send_status(
1715                        resp,
1716                        AdminIoCqeStatusType::GenericCommandStatus(
1717                            AdminIoCqeGenericCommandStatus::InternalError,
1718                        ),
1719                    )
1720                    .await;
1721                };
1722                let mh = MessageHeader::respond(MessageType::NvmeAdminCommand).encode()?;
1723
1724                let acrh = AdminCommandResponseHeader {
1725                    status: ResponseStatus::Success,
1726                    cqedw0: nsid.0,
1727                    cqedw1: 0,
1728                    cqedw3: AdminIoCqeStatusType::GenericCommandStatus(
1729                        AdminIoCqeGenericCommandStatus::SuccessfulCompletion,
1730                    )
1731                    .into(),
1732                }
1733                .encode()?;
1734
1735                send_response(resp, &[&mh.0, &acrh.0]).await;
1736
1737                Ok(())
1738            }
1739            crate::nvme::mi::AdminNamespaceManagementSelect::Delete => {
1740                let res = subsys.remove_namespace(NamespaceId(self.nsid));
1741                let status = match &res {
1742                    Ok(_) => AdminIoCqeStatusType::GenericCommandStatus(
1743                        AdminIoCqeGenericCommandStatus::SuccessfulCompletion,
1744                    ),
1745                    Err(err) => {
1746                        assert_eq!(err, &SubsystemError::NamespaceIdentifierUnavailable);
1747                        AdminIoCqeStatusType::CommandSpecificStatus(
1748                            CommandSpecificStatus::NamespaceIdentifierUnavailable.id(),
1749                        )
1750                    }
1751                };
1752                let mh = MessageHeader::respond(MessageType::NvmeAdminCommand).encode()?;
1753
1754                let acrh = AdminCommandResponseHeader {
1755                    status: ResponseStatus::Success,
1756                    cqedw0: self.nsid, // TODO: Base v2.1, 5.1.21 unclear, test against hardware
1757                    cqedw1: 0,
1758                    cqedw3: status.into(),
1759                }
1760                .encode()?;
1761
1762                send_response(resp, &[&mh.0, &acrh.0]).await;
1763
1764                Ok(())
1765            }
1766        }
1767    }
1768}
1769
1770impl RequestHandler for AdminNamespaceAttachmentRequest {
1771    type Ctx = AdminCommandRequestHeader;
1772
1773    async fn handle<A, C>(
1774        &self,
1775        _ctx: &Self::Ctx,
1776        _mep: &mut crate::ManagementEndpoint,
1777        subsys: &mut crate::Subsystem,
1778        rest: &[u8],
1779        resp: &mut C,
1780        _app: A,
1781    ) -> Result<(), ResponseStatus>
1782    where
1783        A: AsyncFnMut(CommandEffect) -> Result<(), CommandEffectError>,
1784        C: AsyncRespChannel,
1785    {
1786        // Base v2.1, 5.1.20.1, Figure 365
1787        #[repr(u8)]
1788        enum CommandSpecificStatus {
1789            NamespaceAlreadyAttached = 0x18,
1790            NamespaceNotAttached = 0x1a,
1791            ControllerListInvalid = 0x1c,
1792            NamespaceAttachmentLimitExceeded = 0x27,
1793        }
1794        unsafe impl Discriminant<u8> for CommandSpecificStatus {}
1795
1796        impl From<ControllerError> for CommandSpecificStatus {
1797            fn from(value: ControllerError) -> Self {
1798                match value {
1799                    ControllerError::NamespaceAlreadyAttached => Self::NamespaceAlreadyAttached,
1800                    ControllerError::NamespaceAttachmentLimitExceeded => {
1801                        Self::NamespaceAttachmentLimitExceeded
1802                    }
1803                    ControllerError::NamespaceNotAttached => Self::NamespaceNotAttached,
1804                }
1805            }
1806        }
1807
1808        // SAFETY: Protected in parsing by DekuError::InvalidParam
1809        debug_assert!(self.body.numids <= 2047);
1810
1811        let expected = (2047 - (self.body.numids as usize)) * core::mem::size_of::<u16>();
1812        if rest.len() != expected {
1813            debug!(
1814                "Invalid request size for Admin Namespace Attachment: Found {}, expected {expected}",
1815                rest.len()
1816            );
1817            return Err(ResponseStatus::InvalidCommandSize);
1818        }
1819
1820        if self.nsid == u32::MAX {
1821            debug!("Refusing to perform {:?} for broadcast NSID", self.sel);
1822            return admin_send_status(
1823                resp,
1824                AdminIoCqeStatusType::GenericCommandStatus(
1825                    AdminIoCqeGenericCommandStatus::InvalidFieldInCommand,
1826                ),
1827            )
1828            .await;
1829        }
1830
1831        // TODO: Handle MAXCNA
1832
1833        let mut status = AdminIoCqeStatusType::GenericCommandStatus(
1834            AdminIoCqeGenericCommandStatus::SuccessfulCompletion,
1835        );
1836
1837        let action = match &self.sel {
1838            crate::nvme::AdminNamespaceAttachmentSelect::ControllerAttach => {
1839                |ctlr: &mut Controller, ns: NamespaceId| ctlr.attach_namespace(ns)
1840            }
1841            crate::nvme::AdminNamespaceAttachmentSelect::ControllerDetach => {
1842                |ctlr: &mut Controller, ns: NamespaceId| ctlr.detach_namespace(ns)
1843            }
1844        };
1845
1846        for cid in &self.body.ids.0 {
1847            let Some(ctlr) = subsys.ctlrs.get_mut(*cid as usize) else {
1848                debug!("Unrecognised controller ID: {cid}");
1849                status = AdminIoCqeStatusType::CommandSpecificStatus(
1850                    CommandSpecificStatus::ControllerListInvalid.id(),
1851                );
1852                break;
1853            };
1854
1855            // TODO: Allow addition of non-IO controllers
1856            if ctlr.cntrltype != ControllerType::Io {
1857                debug!(
1858                    "Require {:?} controller type, have {:?}",
1859                    ControllerType::Io,
1860                    ctlr.cntrltype
1861                );
1862                status = AdminIoCqeStatusType::CommandSpecificStatus(
1863                    CommandSpecificStatus::ControllerListInvalid.id(),
1864                );
1865                break;
1866            }
1867
1868            // TODO: Handle Namespace Is Private
1869            // TODO: Handle I/O Command Set Not Supported
1870            // TODO: Handle I/O Command Set Not Enabled
1871
1872            // XXX: Should this be transactional? Two loops?
1873            if let Err(err) = action(ctlr, NamespaceId(self.nsid)) {
1874                let err: CommandSpecificStatus = err.into();
1875                status = AdminIoCqeStatusType::CommandSpecificStatus(err.id());
1876                break;
1877            }
1878        }
1879
1880        let mh = MessageHeader::respond(MessageType::NvmeAdminCommand).encode()?;
1881
1882        let acrh = AdminCommandResponseHeader {
1883            status: ResponseStatus::Success,
1884            cqedw0: self.nsid,
1885            cqedw1: 0,
1886            cqedw3: status.into(),
1887        }
1888        .encode()?;
1889
1890        send_response(resp, &[&mh.0, &acrh.0]).await;
1891
1892        Ok(())
1893    }
1894}
1895
1896impl RequestHandler for AdminSanitizeRequest {
1897    type Ctx = AdminCommandRequestHeader;
1898
1899    async fn handle<A, C>(
1900        &self,
1901        _ctx: &Self::Ctx,
1902        _mep: &mut crate::ManagementEndpoint,
1903        subsys: &mut crate::Subsystem,
1904        rest: &[u8],
1905        resp: &mut C,
1906        _app: A,
1907    ) -> Result<(), ResponseStatus>
1908    where
1909        A: AsyncFnMut(CommandEffect) -> Result<(), CommandEffectError>,
1910        C: AsyncRespChannel,
1911    {
1912        if !rest.is_empty() {
1913            debug!("Invalid request size for Admin Sanitize");
1914            return Err(ResponseStatus::InvalidCommandSize);
1915        }
1916
1917        if subsys.sanicap.contains(SanitizeCapabilityFlags::Ndi) && self.config.ndas {
1918            debug!("Request for No-Deallocate After Sanitize when No-Deallocate is inhibited");
1919            return admin_send_status(
1920                resp,
1921                AdminIoCqeStatusType::GenericCommandStatus(
1922                    AdminIoCqeGenericCommandStatus::InvalidFieldInCommand,
1923                ),
1924            )
1925            .await;
1926        }
1927
1928        // TODO: Implement action latency, progress state machine, error states
1929        match self.config.sanact {
1930            SanitizeAction::Reserved => Err(ResponseStatus::InvalidParameter),
1931            SanitizeAction::ExitFailureMode | SanitizeAction::ExitMediaVerificationState => {
1932                if subsys.ssi.sans != SanitizeState::Idle {
1933                    todo!("Implement sanitize state machine!");
1934                }
1935                admin_send_response_body(resp, &[]).await
1936            }
1937            SanitizeAction::StartBlockErase | SanitizeAction::StartCryptoErase => {
1938                subsys.ssi = SanitizeStateInformation {
1939                    sans: SanitizeState::Idle,
1940                    fails: 0,
1941                };
1942                subsys.sstat = SanitizeStatus {
1943                    opc: 0,
1944                    sos: SanitizeOperationStatus::Sanitized,
1945                    mvcncled: false,
1946                    gde: true,
1947                };
1948                subsys.sconf = Some(self.config);
1949
1950                admin_send_response_body(resp, &[]).await
1951            }
1952            SanitizeAction::StartOverwrite => {
1953                subsys.ssi = SanitizeStateInformation {
1954                    sans: SanitizeState::Idle,
1955                    fails: 0,
1956                };
1957                subsys.sstat = SanitizeStatus {
1958                    opc: self.config.owpass,
1959                    sos: SanitizeOperationStatus::Sanitized,
1960                    mvcncled: false,
1961                    gde: true,
1962                };
1963                subsys.sconf = Some(self.config);
1964
1965                admin_send_response_body(resp, &[]).await
1966            }
1967        }
1968    }
1969}
1970
1971impl RequestHandler for AdminFormatNvmRequest {
1972    type Ctx = AdminCommandRequestHeader;
1973
1974    async fn handle<A, C>(
1975        &self,
1976        ctx: &Self::Ctx,
1977        _mep: &mut crate::ManagementEndpoint,
1978        subsys: &mut crate::Subsystem,
1979        rest: &[u8],
1980        resp: &mut C,
1981        _app: A,
1982    ) -> Result<(), ResponseStatus>
1983    where
1984        A: AsyncFnMut(CommandEffect) -> Result<(), CommandEffectError>,
1985        C: AsyncRespChannel,
1986    {
1987        if !rest.is_empty() {
1988            debug!("Invalid request size for Admin Format NVM");
1989            return Err(ResponseStatus::InvalidCommandSize);
1990        }
1991
1992        let Some(ctlr) = subsys.ctlrs.iter().find(|c| c.id.0 == ctx.ctlid) else {
1993            debug!("Unrecognised CTLID: {}", ctx.ctlid);
1994            return admin_send_status(
1995                resp,
1996                AdminIoCqeStatusType::GenericCommandStatus(
1997                    AdminIoCqeGenericCommandStatus::InvalidFieldInCommand,
1998                ),
1999            )
2000            .await;
2001        };
2002
2003        if self.config.lbafi() != 0 {
2004            debug!("Unsupported LBA format index: {}", self.config.lbafi());
2005            return admin_send_status(
2006                resp,
2007                AdminIoCqeStatusType::GenericCommandStatus(
2008                    AdminIoCqeGenericCommandStatus::InvalidFieldInCommand,
2009                ),
2010            )
2011            .await;
2012        }
2013
2014        if !ctlr.active_ns.iter().any(|ns| ns.0 == self.nsid) && self.nsid != u32::MAX {
2015            debug!("Unrecognised NSID: {}", self.nsid);
2016            return admin_send_status(
2017                resp,
2018                AdminIoCqeStatusType::GenericCommandStatus(
2019                    AdminIoCqeGenericCommandStatus::InvalidFieldInCommand,
2020                ),
2021            )
2022            .await;
2023        }
2024
2025        // TODO: handle config.ses
2026
2027        admin_send_response_body(resp, &[]).await
2028    }
2029}
2030
2031impl RequestHandler for PcieCommandRequestHeader {
2032    type Ctx = PcieCommandRequestHeader;
2033
2034    async fn handle<A, C>(
2035        &self,
2036        ctx: &Self::Ctx,
2037        _mep: &mut crate::ManagementEndpoint,
2038        subsys: &mut crate::Subsystem,
2039        rest: &[u8],
2040        resp: &mut C,
2041        _app: A,
2042    ) -> Result<(), ResponseStatus>
2043    where
2044        A: AsyncFnMut(crate::CommandEffect) -> Result<(), CommandEffectError>,
2045        C: mctp::AsyncRespChannel,
2046    {
2047        match &ctx.op {
2048            super::PcieCommandRequestType::ConfigurationRead(req) => {
2049                if !rest.is_empty() {
2050                    debug!("Invalid request size for PcieCommand");
2051                    return Err(ResponseStatus::InvalidCommandSize);
2052                }
2053
2054                if req.length != 4096 {
2055                    debug!("Implement length support");
2056                    return Err(ResponseStatus::InternalError);
2057                }
2058
2059                if req.offset != 0 {
2060                    debug!("Implement offset support");
2061                    return Err(ResponseStatus::InternalError);
2062                }
2063
2064                let mh = MessageHeader::respond(MessageType::PcieCommand).encode()?;
2065
2066                let status = [0u8; 4]; /* Success */
2067
2068                let cr = PciDeviceFunctionConfigurationSpace::builder()
2069                    .vid(subsys.info.pci_vid)
2070                    .did(subsys.info.pci_did)
2071                    .svid(subsys.info.pci_svid)
2072                    .sdid(subsys.info.pci_sdid)
2073                    .build()
2074                    .encode()?;
2075
2076                send_response(resp, &[&mh.0, &status, &cr.0]).await;
2077                Ok(())
2078            }
2079            super::PcieCommandRequestType::ConfigurationWrite(req) => {
2080                let response = if rest.len() == req.length as usize {
2081                    debug!("Unsupported write at {} for {}", req.offset, req.length);
2082                    ResponseStatus::AccessDenied
2083                } else {
2084                    debug!(
2085                        "Request data size {} does not match requested write size {}",
2086                        rest.len(),
2087                        req.length
2088                    );
2089                    ResponseStatus::InvalidCommandInputDataSize
2090                };
2091
2092                let mh = MessageHeader::respond(MessageType::PcieCommand).encode()?;
2093
2094                let status = [response.id(), 0, 0, 0];
2095
2096                send_response(resp, &[&mh.0, &status]).await;
2097                Ok(())
2098            }
2099            _ => {
2100                debug!("Unimplemented OPCODE: {:?}", ctx._opcode);
2101                Err(ResponseStatus::InternalError)
2102            }
2103        }
2104    }
2105}
2106
2107impl crate::ManagementEndpoint {
2108    fn update(&mut self, subsys: &crate::Subsystem) {
2109        assert!(subsys.ctlrs.len() <= self.mecss.len());
2110        for c in &subsys.ctlrs {
2111            let mecs = &mut self.mecss[c.id.0 as usize];
2112
2113            // It might seem tempting to compose self.ccsf with an
2114            // assignment-union over each controller's mecs.chscf. However, this
2115            // doesn't work in practice due to the requirements of NVMe MI /
2116            // Configuration Set / Health Status Change on the behaviour of
2117            // clearing the composite controller flags, against the requirements
2118            // of NVMe MI / Controller Health Status Poll on the behaviour of
2119            // clearing the controller health status flags.
2120            //
2121            // Instead, update each independently by first gathering the change
2122            // flags for the current update cycle, then using union-assignment
2123            // into both mecs.chscf and self.ccsf (in the case of the latter,
2124            // via the conversion to the composite controller flag set).
2125            let mut update = FlagSet::empty();
2126
2127            if mecs.cc.en != c.cc.en {
2128                update |= crate::nvme::mi::ControllerHealthStatusChangedFlags::Ceco;
2129            }
2130
2131            if mecs.csts.contains(crate::nvme::ControllerStatusFlags::Rdy)
2132                != c.csts.contains(crate::nvme::ControllerStatusFlags::Rdy)
2133            {
2134                update |= crate::nvme::mi::ControllerHealthStatusChangedFlags::Rdy;
2135            }
2136
2137            mecs.chscf |= update;
2138
2139            let update: CompositeControllerStatusFlagSet = update.into();
2140            self.ccsf.0 |= update.0;
2141
2142            mecs.cc = c.cc;
2143            mecs.csts = c.csts;
2144        }
2145    }
2146
2147    pub async fn handle_async<
2148        A: AsyncFnMut(CommandEffect) -> Result<(), CommandEffectError>,
2149        C: mctp::AsyncRespChannel,
2150    >(
2151        &mut self,
2152        subsys: &mut crate::Subsystem,
2153        msg: &[u8],
2154        ic: MsgIC,
2155        mut resp: C,
2156        app: A,
2157    ) {
2158        self.update(subsys);
2159
2160        if !ic.0 {
2161            debug!("NVMe-MI requires IC set for OOB messages");
2162            return;
2163        }
2164
2165        if msg.len() < 4 {
2166            debug!("Message cannot contain a valid IC object");
2167            return;
2168        }
2169
2170        let Some((msg, icv)) = msg.split_at_checked(msg.len() - 4) else {
2171            debug!("Message too short to extract integrity check");
2172            return;
2173        };
2174
2175        let mut digest = ISCSI.digest();
2176        digest.update(&[0x80 | 0x04]);
2177        digest.update(msg);
2178        let calculated = digest.finalize().to_le_bytes();
2179
2180        if icv != calculated {
2181            debug!("checksum mismatch: {icv:02x?}, {calculated:02x?}");
2182            return;
2183        }
2184
2185        let res = MessageHeader::from_bytes((msg, 0));
2186        let Ok(((rest, _), mh)) = &res else {
2187            debug!(
2188                "Message too short to extract NVMeMIMessageHeader: {:?}",
2189                res
2190            );
2191            return;
2192        };
2193
2194        if mh.csi != 0 {
2195            debug!("Support second command slot");
2196            return;
2197        }
2198
2199        if mh.ror {
2200            debug!("NVMe-MI message was not a request: {:?}", mh.ror);
2201            return;
2202        }
2203
2204        if let Err(status) = mh.handle(mh, self, subsys, rest, &mut resp, app).await {
2205            let mut digest = ISCSI.digest();
2206            digest.update(&[0x80 | 0x04]);
2207
2208            let Ok(mh) = MessageHeader::respond(mh.nmimt).encode() else {
2209                debug!("Failed to encode MessageHeader for error response");
2210                return;
2211            };
2212            digest.update(&mh.0);
2213
2214            let ss: [u8; 4] = [status.id(), 0, 0, 0];
2215            digest.update(&ss);
2216
2217            let icv = digest.finalize().to_le_bytes();
2218            let respv = [mh.0.as_slice(), ss.as_slice(), icv.as_slice()];
2219            if let Err(e) = resp.send_vectored(MsgIC(true), &respv).await {
2220                debug!("Failed to send NVMe-MI error response: {e:?}");
2221            }
2222        }
2223    }
2224}