1use 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 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 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 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 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 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 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 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 assert!(ctlr.temp_range.kind == crate::UnitKind::Kelvin);
207
208 let clamped = ctlr
211 .temp
212 .clamp(ctlr.temp_range.lower, ctlr.temp_range.upper);
213
214 let celcius: i32 = clamped as i32 - 273;
216
217 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 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 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 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 if ctlr.ro {
349 fs |= crate::nvme::mi::CriticalWarningFlags::Ro;
350 }
351
352 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 }
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 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 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 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 assert!(
629 subsys.ports.len() < u8::MAX as usize,
630 "Too many ports defined: {}",
631 subsys.ports.len()
632 );
633 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 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 .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 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 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 assert!(!body.is_empty());
917
918 if dofst & 3 != 0 {
920 debug!("Unnatural DOFST value: {dofst:?}");
921 return Err(ResponseStatus::InvalidParameter);
922 }
923
924 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 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 if self.ot != 0 {
1074 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 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 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 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 if ctlr.ro {
1200 fs |= crate::nvme::CriticalWarningFlags::Amro;
1201 }
1202
1203 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(), dur: 0,
1218 duw: 0,
1219 hrc: 0,
1220 hwc: 0,
1221 cbt: 0,
1222 pwrc: 0, poh: 0, upl: 0, mdie: 0,
1226 neile: 0, wctt: 0, cctt: 0, 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 &[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, ..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 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 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 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 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 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 let unique: heapless::FnvIndexSet<u32, MAX_NAMESPACES> =
1485 active.iter().copied().collect();
1486
1487 let mut aianidlr = AdminIdentifyActiveNamespaceIdListResponse::new();
1488 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 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 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 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 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, 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 #[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 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 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 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 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 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 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]; 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 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}