diff --git a/src/lib.rs b/src/lib.rs index b6beb0a..d253c37 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -413,6 +413,61 @@ pub enum NamespaceIdentifierType { Csi(nvme::CommandSetIdentifier), } +// Base v2.1, 3.2.1 +// Base v2.1, 3.2.1.5, Figure 71 +#[derive(Clone, Copy, Debug)] +enum NamespaceIdDisposition<'a> { + Invalid, + Broadcast, + Unallocated, + Inactive(&'a Namespace), + Active(&'a Namespace), +} + +// NSID +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct NamespaceId(u32); + +impl NamespaceId { + fn disposition<'a>(&self, subsys: &'a Subsystem) -> NamespaceIdDisposition<'a> { + if self.0 == 0 { + return NamespaceIdDisposition::Invalid; + } + + if self.0 == u32::MAX { + return NamespaceIdDisposition::Broadcast; + } + + assert!(subsys.nss.capacity() <= u32::MAX.try_into().unwrap()); + if self.0 > subsys.nss.capacity() as u32 { + return NamespaceIdDisposition::Invalid; + } + + let Some(ns) = subsys.nss.iter().find(|nsid| self.0 == nsid.id.0) else { + return NamespaceIdDisposition::Unallocated; + }; + + if !subsys + .ctlrs + .iter() + .flat_map(|c| c.active_ns.iter()) + .any(|&nsid| nsid.0 == self.0) + { + return NamespaceIdDisposition::Inactive(ns); + } + + NamespaceIdDisposition::Active(ns) + } + + fn max(subsys: &Subsystem) -> u32 { + subsys + .nss + .capacity() + .try_into() + .expect("Too many namespaces") + } +} + #[derive(Debug)] pub struct Namespace { id: NamespaceId, @@ -423,10 +478,6 @@ pub struct Namespace { nids: [NamespaceIdentifierType; 2], } -// NSID -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct NamespaceId(u32); - impl Namespace { fn generate_uuid(seed: &[u8], nsid: NamespaceId) -> Uuid { let mut hasher = hmac::Hmac::::new_from_slice(seed).unwrap(); diff --git a/src/nvme.rs b/src/nvme.rs index 10c9cae..0f38f92 100644 --- a/src/nvme.rs +++ b/src/nvme.rs @@ -5,8 +5,9 @@ pub mod mi; use deku::ctx::Endian; -use deku::{DekuRead, DekuWrite, deku_derive}; +use deku::{DekuError, DekuRead, DekuWrite, deku_derive}; use flagset::flags; +use log::debug; use crate::wire::WireFlagSet; use crate::wire::WireString; @@ -85,7 +86,7 @@ enum CommandRetryDelay { } unsafe impl Discriminant for CommandRetryDelay {} -// Base v2.1, 4.3.2, Figure 101 +// Base v2.1, 4.2.3, Figure 101 #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(u8)] enum AdminIoCqeStatusType { @@ -106,9 +107,18 @@ unsafe impl Discriminant for AdminIoCqeStatusType {} enum AdminIoCqeGenericCommandStatus { SuccessfulCompletion = 0x00, InvalidFieldInCommand = 0x02, + InternalError = 0x06, + InvalidNamespaceOrFormat = 0x0b, } unsafe impl Discriminant for AdminIoCqeGenericCommandStatus {} +impl From for AdminIoCqeGenericCommandStatus { + fn from(err: DekuError) -> Self { + debug!("Codec operation failed: {err}"); + Self::InternalError + } +} + // Base v2.1, 4.6.1, Figure 137 // TODO: Unify with ControllerListResponse #[derive(Debug, DekuRead, Eq, PartialEq)] @@ -375,6 +385,8 @@ enum AdminIdentifyCnsRequestType { IoActiveNamespaceIdList = 0x07, IdentifyNamespace = 0x08, AllocatedNamespaceIdList = 0x10, + IdentifyNamespaceForAllocatedNamespaceId = 0x11, + NamespaceAttachedControllerList = 0x12, NvmSubsystemControllerList = 0x13, SecondaryControllerList = 0x15, } @@ -404,6 +416,26 @@ pub struct AdminIdentifyNvmIdentifyNamespaceResponse { } impl Encode<4096> for AdminIdentifyNvmIdentifyNamespaceResponse {} +impl From<&crate::Namespace> for AdminIdentifyNvmIdentifyNamespaceResponse { + fn from(value: &crate::Namespace) -> Self { + Self { + nsze: value.size, + ncap: value.capacity, + nuse: value.used, + nsfeat: ((value.size == value.capacity) as u8), + nlbaf: 0, + flbas: 0, + mc: 0, + dpc: 0, + dps: 0, + nvmcap: 2_u128.pow(value.block_order as u32) * value.size as u128, + lbaf0: 0, + lbaf0_lbads: value.block_order, + lbaf0_rp: 0, + } + } +} + // Base v2.1, 5.1.13.1, Figure 311 #[derive(Clone, Copy, Debug, DekuRead, DekuWrite)] #[deku(id_type = "u8", endian = "endian", ctx = "endian: Endian")] diff --git a/src/nvme/mi/dev.rs b/src/nvme/mi/dev.rs index 9340896..02726b1 100644 --- a/src/nvme/mi/dev.rs +++ b/src/nvme/mi/dev.rs @@ -10,7 +10,7 @@ use mctp::{AsyncRespChannel, MsgIC}; use crate::{ CommandEffect, CommandEffectError, Controller, ControllerError, ControllerType, Discriminant, - MAX_CONTROLLERS, MAX_NAMESPACES, NamespaceId, SubsystemError, + MAX_CONTROLLERS, MAX_NAMESPACES, NamespaceId, NamespaceIdDisposition, SubsystemError, nvme::{ AdminFormatNvmConfiguration, AdminGetLogPageLidRequestType, AdminGetLogPageSupportedLogPagesResponse, AdminIdentifyActiveNamespaceIdListResponse, @@ -936,7 +936,10 @@ where Ok(()) } -async fn admin_send_invalid_field(resp: &mut C) -> Result<(), ResponseStatus> +async fn admin_send_status( + resp: &mut C, + status: AdminIoCqeStatusType, +) -> Result<(), ResponseStatus> where C: AsyncRespChannel, { @@ -949,9 +952,7 @@ where cqedw3: AdminIoCqeStatus { cid: 0, p: true, - status: AdminIoCqeStatusType::GenericCommandStatus( - AdminIoCqeGenericCommandStatus::InvalidFieldInCommand, - ), + status, crd: crate::nvme::CommandRetryDelay::None, m: false, dnr: true, @@ -992,7 +993,13 @@ impl RequestHandler for AdminGetLogPageRequest { | AdminGetLogPageLidRequestType::FeatureIdentifiersSupportedAndEffects => { if self.csi != 0 { debug!("Support CSI"); - return Err(ResponseStatus::InternalError); + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InternalError, + ), + ) + .await; } } AdminGetLogPageLidRequestType::ErrorInformation @@ -1002,7 +1009,13 @@ impl RequestHandler for AdminGetLogPageRequest { let Some(ctlr) = subsys.ctlrs.get(ctx.ctlid as usize) else { debug!("Unrecognised CTLID: {}", ctx.ctlid); - return Err(ResponseStatus::InvalidParameter); + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InvalidFieldInCommand, + ), + ) + .await; }; let Some(flags) = ctlr.lsaes.get(self.req.id() as usize) else { @@ -1010,7 +1023,13 @@ impl RequestHandler for AdminGetLogPageRequest { "LSAE mismatch with known LID {:?} on controller {}", self.req, ctlr.id.0 ); - return Err(ResponseStatus::InternalError); + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InvalidFieldInCommand, + ), + ) + .await; }; // Base v2.1, 5.1.12 @@ -1019,7 +1038,13 @@ impl RequestHandler for AdminGetLogPageRequest { if flags.contains(LidSupportedAndEffectsFlags::Ios) { todo!("Add OT support"); } else { - return admin_send_invalid_field(resp).await; + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InvalidFieldInCommand, + ), + ) + .await; } } @@ -1036,7 +1061,13 @@ impl RequestHandler for AdminGetLogPageRequest { AdminGetLogPageLidRequestType::SupportedLogPages => { if (self.numdw + 1) * 4 != 1024 { debug!("Implement support for NUMDL / NUMDU"); - return Err(ResponseStatus::InternalError); + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InternalError, + ), + ) + .await; } let mut lsids = WireVec::new(); @@ -1062,7 +1093,13 @@ impl RequestHandler for AdminGetLogPageRequest { AdminGetLogPageLidRequestType::ErrorInformation => { if (self.numdw + 1) * 4 != 64 { debug!("Implement support for NUMDL / NUMDU"); - return Err(ResponseStatus::InternalError); + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InternalError, + ), + ) + .await; } admin_send_response_body( resp, @@ -1073,20 +1110,38 @@ impl RequestHandler for AdminGetLogPageRequest { AdminGetLogPageLidRequestType::SmartHealthInformation => { if (self.numdw + 1) * 4 != 512 { debug!("Implement support for NUMDL / NUMDU"); - return Err(ResponseStatus::InternalError); + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InternalError, + ), + ) + .await; } // Base v2.1, 5.1.2, Figure 199 let lpol = self.lpo & !3u64; if lpol > 512 { - return admin_send_invalid_field(resp).await; + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InvalidFieldInCommand, + ), + ) + .await; } if self.nsid != 0 && self.nsid != u32::MAX { if ctlr.lpa.contains(LogPageAttributes::Smarts) { todo!(); } else { - return admin_send_invalid_field(resp).await; + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InvalidFieldInCommand, + ), + ) + .await; } } @@ -1149,7 +1204,13 @@ impl RequestHandler for AdminGetLogPageRequest { AdminGetLogPageLidRequestType::FeatureIdentifiersSupportedAndEffects => { if (self.numdw + 1) * 4 != 1024 { debug!("Implement support for NUMDL / NUMDU"); - return Err(ResponseStatus::InternalError); + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InternalError, + ), + ) + .await; } admin_send_response_body( @@ -1166,7 +1227,13 @@ impl RequestHandler for AdminGetLogPageRequest { AdminGetLogPageLidRequestType::SanitizeStatus => { if (self.numdw + 1) * 4 != 512 { debug!("Implement support for NUMDL / NUMDU"); - return Err(ResponseStatus::InternalError); + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InternalError, + ), + ) + .await; } let sslpr = SanitizeStatusLogPageResponse { @@ -1221,152 +1288,112 @@ impl RequestHandler for AdminIdentifyRequest { return Err(ResponseStatus::InvalidCommandSize); } - match &self.req { + let res = match &self.req { AdminIdentifyCnsRequestType::NvmIdentifyNamespace => { - assert!(subsys.nss.len() <= u32::MAX.try_into().unwrap()); - - if self.nsid == u32::MAX { - let ainvminr = AdminIdentifyNvmIdentifyNamespaceResponse { - lbaf0_lbads: 9, // TODO: Tie to controller model - ..Default::default() + match NamespaceId(self.nsid).disposition(subsys) { + NamespaceIdDisposition::Invalid => { + debug!("Invalid NSID: {}", self.nsid); + Err(AdminIoCqeGenericCommandStatus::InvalidNamespaceOrFormat) } - .encode()?; - - return admin_send_response_body( - resp, - admin_constrain_body(self.dofst, self.dlen, &ainvminr.0)?, - ) - .await; - } - - if self.nsid == 0 || self.nsid > subsys.nss.capacity() as u32 { - debug!("Invalid NSID: {}", self.nsid); - return Err(ResponseStatus::InvalidParameter); - } - - let Some(ns) = subsys.nss.get(self.nsid as usize - 1) else { - debug!("Unallocated NSID: {}", self.nsid); - return Err(ResponseStatus::InvalidParameter); - }; - - // 4.1.5.1 NVM Command Set Spec, v1.0c - // TODO: Ensure the associated controller is an IO controller - // FIXME: Improve determination algo - let active = subsys - .ctlrs - .iter() - .flat_map(|c| c.active_ns.iter()) - .any(|&nsid| nsid.0 == self.nsid); - let ainvminr = if active { - AdminIdentifyNvmIdentifyNamespaceResponse { - nsze: ns.size, - ncap: ns.capacity, - nuse: ns.used, - nsfeat: ((ns.size == ns.capacity) as u8), - nlbaf: 0, - flbas: 0, - mc: 0, - dpc: 0, - dps: 0, - nvmcap: 2_u128.pow(ns.block_order as u32) * ns.size as u128, - lbaf0: 0, - lbaf0_lbads: ns.block_order, - lbaf0_rp: 0, + NamespaceIdDisposition::Broadcast => { + AdminIdentifyNvmIdentifyNamespaceResponse { + lbaf0_lbads: 9, // TODO: Tie to controller model + ..Default::default() + } + .encode() + .map_err(AdminIoCqeGenericCommandStatus::from) + } + NamespaceIdDisposition::Unallocated => { + debug!("Unallocated NSID: {}", self.nsid); + Err(AdminIoCqeGenericCommandStatus::InvalidNamespaceOrFormat) + } + NamespaceIdDisposition::Inactive(_) => { + AdminIdentifyNvmIdentifyNamespaceResponse::default() + .encode() + .map_err(AdminIoCqeGenericCommandStatus::from) + } + // 4.1.5.1 NVM Command Set Spec, v1.0c + NamespaceIdDisposition::Active(ns) => { + Into::::into(ns) + .encode() + .map_err(AdminIoCqeGenericCommandStatus::from) } - } else { - AdminIdentifyNvmIdentifyNamespaceResponse::default() } - .encode()?; - - admin_send_response_body( - resp, - admin_constrain_body(self.dofst, self.dlen, &ainvminr.0)?, - ) - .await } AdminIdentifyCnsRequestType::IdentifyController => { - let Some(ctlr) = subsys.ctlrs.get(ctx.ctlid as usize) else { - debug!("No such CTLID: {}", ctx.ctlid); - return Err(ResponseStatus::InvalidParameter); - }; - - let aicr = AdminIdentifyControllerResponse { - vid: subsys.info.pci_vid, - ssvid: subsys.info.pci_svid, - sn: WireString::from(subsys.sn)?, - mn: WireString::from(subsys.mn)?, - fr: WireString::from(subsys.fr)?, - rab: 0, - ieee: { - // 4.5.3, Base v2.1 - let mut fixup = subsys.info.ieee_oui; - fixup.reverse(); - fixup - }, - cmic: ((subsys.ctlrs.len() > 1) as u8) << 1 // MCTRS + if let Some(ctlr) = subsys.ctlrs.get(ctx.ctlid as usize) { + AdminIdentifyControllerResponse { + vid: subsys.info.pci_vid, + ssvid: subsys.info.pci_svid, + sn: WireString::from(subsys.sn)?, + mn: WireString::from(subsys.mn)?, + fr: WireString::from(subsys.fr)?, + rab: 0, + ieee: { + // 4.5.3, Base v2.1 + let mut fixup = subsys.info.ieee_oui; + fixup.reverse(); + fixup + }, + cmic: ((subsys.ctlrs.len() > 1) as u8) << 1 // MCTRS | ((subsys.ports.len() > 1) as u8), // MPORTS - mdts: 0, - cntlid: ctlr.id.0, - ver: 0, - rtd3r: 0, - rtd3e: 0, - oaes: 0, - // TODO: Tie to data model - ctratt: ((false as u32) << 14) // DNVMS + mdts: 0, + cntlid: ctlr.id.0, + ver: 0, + rtd3r: 0, + rtd3e: 0, + oaes: 0, + // TODO: Tie to data model + ctratt: ((false as u32) << 14) // DNVMS | ((false as u32) << 13) // DEG | ((false as u32) << 4) // EGS | ((false as u32) << 2), // NSETS - cntrltype: ctlr.cntrltype.into(), - // TODO: Tie to data model - nvmsr: ((false as u8) << 1) // NVMEE + cntrltype: ctlr.cntrltype.into(), + // TODO: Tie to data model + nvmsr: ((false as u8) << 1) // NVMEE | (true as u8), // NVMESD - vwci: 0, - mec: ((subsys.ports.iter().any(|p| matches!(p.typ, crate::PortType::Pcie(_)))) as u8) << 1 // PCIEME + vwci: 0, + mec: ((subsys.ports.iter().any(|p| matches!(p.typ, crate::PortType::Pcie(_)))) as u8) << 1 // PCIEME | (subsys.ports.iter().any(|p| matches!(p.typ, crate::PortType::TwoWire(_)))) as u8, // TWPME - ocas: 0, - acl: 0, - aerl: 0, - frmw: 0, - lpa: ctlr.lpa.into(), - elpe: 0, - npss: 0, - avscc: 0, - wctemp: 0x157, - cctemp: 0x157, - fwug: 0, - kas: 0, - cqt: 0, - sqes: 0, - cqes: 0, - maxcmd: 0, - nn: subsys - .nss - .capacity() - .try_into() - .expect("Too many namespaces"), - oncs: 0, - fuses: 0, - fna: ctlr.fna.into(), - vwc: 0, - awun: 0, - awupf: 0, - icsvscc: 0, - nwpc: 0, - mnan: 0, - subnqn: WireString::new(), - fcatt: 0, - msdbd: 0, - ofcs: 0, - apsta: 0, - sanicap: subsys.sanicap.into(), + ocas: 0, + acl: 0, + aerl: 0, + frmw: 0, + lpa: ctlr.lpa.into(), + elpe: 0, + npss: 0, + avscc: 0, + wctemp: 0x157, + cctemp: 0x157, + fwug: 0, + kas: 0, + cqt: 0, + sqes: 0, + cqes: 0, + maxcmd: 0, + nn: NamespaceId::max(subsys), + oncs: 0, + fuses: 0, + fna: ctlr.fna.into(), + vwc: 0, + awun: 0, + awupf: 0, + icsvscc: 0, + nwpc: 0, + mnan: 0, + subnqn: WireString::new(), + fcatt: 0, + msdbd: 0, + ofcs: 0, + apsta: 0, + sanicap: subsys.sanicap.into(), + } + .encode() + .map_err(AdminIoCqeGenericCommandStatus::from) + } else { + debug!("No such CTLID: {}", ctx.ctlid); + Err(AdminIoCqeGenericCommandStatus::InvalidFieldInCommand) } - .encode()?; - - admin_send_response_body( - resp, - admin_constrain_body(self.dofst, self.dlen, &aicr.0)?, - ) - .await } AdminIdentifyCnsRequestType::ActiveNamespaceIDList => { // 5.1.13.2.2, Base v2.1 @@ -1391,53 +1418,51 @@ impl RequestHandler for AdminIdentifyRequest { return Err(ResponseStatus::InternalError); }; } - let aianidlr = aianidlr.encode()?; - - admin_send_response_body( - resp, - admin_constrain_body(self.dofst, self.dlen, &aianidlr.0)?, - ) - .await + aianidlr + .encode() + .map_err(AdminIoCqeGenericCommandStatus::from) } AdminIdentifyCnsRequestType::NamespaceIdentificationDescriptorList => { // 5.1.13.2.3, Base v2.1 - if self.nsid >= u32::MAX - 1 { - debug!("Unacceptable NSID for Namespace Identification Descriptor List"); - return Err(ResponseStatus::InvalidParameter); - } - - if self.nsid == 0 || self.nsid > subsys.nss.capacity() as u32 { - debug!("Invalid NSID: {}", self.nsid); - return Err(ResponseStatus::InvalidParameter); - } - - let Some(ns) = subsys.nss.get(self.nsid as usize - 1) else { - debug!("Unallocated NSID: {}", self.nsid); - return Err(ResponseStatus::InvalidParameter); - }; - - let ainsidlr = AdminIdentifyNamespaceIdentificationDescriptorListResponse { - nids: { - let mut vec = WireVec::new(); - for nid in &ns.nids { - if vec - .push(Into::::into(*nid)) - .is_err() - { - debug!("Failed to push NID {nid:?}"); - return Err(ResponseStatus::InternalError); - } + match NamespaceId(self.nsid).disposition(subsys) { + NamespaceIdDisposition::Invalid => { + if self.nsid == u32::MAX - 1 { + debug!( + "Unacceptable NSID for Namespace Identification Descriptor List" + ); + } else { + debug!("Invalid NSID: {}", self.nsid); } - vec - }, + Err(AdminIoCqeGenericCommandStatus::InvalidNamespaceOrFormat) + } + NamespaceIdDisposition::Broadcast => { + debug!("Invalid NSID: {}", self.nsid); + Err(AdminIoCqeGenericCommandStatus::InvalidNamespaceOrFormat) + } + NamespaceIdDisposition::Unallocated => { + debug!("Unallocated NSID: {}", self.nsid); + Err(AdminIoCqeGenericCommandStatus::InvalidNamespaceOrFormat) + } + NamespaceIdDisposition::Inactive(ns) | NamespaceIdDisposition::Active(ns) => { + AdminIdentifyNamespaceIdentificationDescriptorListResponse { + nids: { + let mut vec = WireVec::new(); + for nid in &ns.nids { + if vec + .push(Into::::into(*nid)) + .is_err() + { + debug!("Failed to push NID {nid:?}"); + return Err(ResponseStatus::InternalError); + } + } + vec + }, + } + .encode() + .map_err(AdminIoCqeGenericCommandStatus::from) + } } - .encode()?; - - admin_send_response_body( - resp, - admin_constrain_body(self.dofst, self.dlen, &ainsidlr.0)?, - ) - .await } AdminIdentifyCnsRequestType::AllocatedNamespaceIdList => { // 5.1.13.2.9, Base v2.1 @@ -1446,28 +1471,79 @@ impl RequestHandler for AdminIdentifyRequest { return Err(ResponseStatus::InvalidParameter); } - assert!(subsys.nss.len() < 4096 / core::mem::size_of::()); - let aiansidl = AdminIdentifyAllocatedNamespaceIdListResponse { + assert!(NamespaceId::max(subsys) < (4096 / core::mem::size_of::()) as u32); + AdminIdentifyAllocatedNamespaceIdListResponse { nsid: { - let start = self.nsid + 1; - let end = subsys.nss.len() as u32; + let mut allocated: heapless::Vec = subsys + .nss + .iter() + .map(|ns| ns.id.0) + .filter(|nsid| *nsid > self.nsid) + .collect(); + allocated.sort_unstable(); let mut vec = WireVec::new(); - for nsid in start..=end { + for nsid in allocated { if vec.push(nsid).is_err() { - debug!("Failed to inset NSID {nsid}"); + debug!("Failed to insert NSID {nsid}"); return Err(ResponseStatus::InternalError); }; } vec }, } - .encode()?; - - admin_send_response_body( - resp, - admin_constrain_body(self.dofst, self.dlen, &aiansidl.0)?, - ) - .await + .encode() + .map_err(AdminIoCqeGenericCommandStatus::from) + } + AdminIdentifyCnsRequestType::IdentifyNamespaceForAllocatedNamespaceId => { + // Base v2.1, 5.1.13.2.10 + match NamespaceId(self.nsid).disposition(subsys) { + NamespaceIdDisposition::Invalid | NamespaceIdDisposition::Broadcast => { + Err(AdminIoCqeGenericCommandStatus::InvalidNamespaceOrFormat) + } + NamespaceIdDisposition::Unallocated => { + AdminIdentifyNvmIdentifyNamespaceResponse::default() + .encode() + .map_err(AdminIoCqeGenericCommandStatus::from) + } + NamespaceIdDisposition::Inactive(ns) | NamespaceIdDisposition::Active(ns) => { + let ainvminr: AdminIdentifyNvmIdentifyNamespaceResponse = ns.into(); + ainvminr + .encode() + .map_err(AdminIoCqeGenericCommandStatus::from) + } + } + } + AdminIdentifyCnsRequestType::NamespaceAttachedControllerList => { + match NamespaceId(self.nsid).disposition(subsys) { + NamespaceIdDisposition::Invalid => ControllerListResponse::new() + .encode() + .map_err(AdminIoCqeGenericCommandStatus::from), + NamespaceIdDisposition::Broadcast => { + Err(AdminIoCqeGenericCommandStatus::InvalidFieldInCommand) + } + NamespaceIdDisposition::Unallocated | NamespaceIdDisposition::Inactive(_) => { + ControllerListResponse::new() + .encode() + .map_err(AdminIoCqeGenericCommandStatus::from) + } + NamespaceIdDisposition::Active(ns) => { + let mut clr = ControllerListResponse::new(); + for cid in subsys.ctlrs.iter().filter_map(|c| { + if c.id.0 >= self.cntid && c.active_ns.contains(&ns.id) { + Some(c.id) + } else { + None + } + }) { + if let Err(id) = clr.ids.push(cid.0) { + debug!("Failed to push controller ID {id}"); + return Err(ResponseStatus::InternalError); + } + } + clr.update()?; + clr.encode().map_err(AdminIoCqeGenericCommandStatus::from) + } + } } AdminIdentifyCnsRequestType::NvmSubsystemControllerList => { assert!( @@ -1483,10 +1559,7 @@ impl RequestHandler for AdminIdentifyRequest { }; } cl.update()?; - let cl = cl.encode()?; - - admin_send_response_body(resp, admin_constrain_body(self.dofst, self.dlen, &cl.0)?) - .await + cl.encode().map_err(AdminIoCqeGenericCommandStatus::from) } AdminIdentifyCnsRequestType::SecondaryControllerList => { let Some(ctlr) = subsys.ctlrs.get(ctx.ctlid as usize) else { @@ -1498,15 +1571,24 @@ impl RequestHandler for AdminIdentifyRequest { todo!("Support listing secondary controllers"); } + Ok(([0u8; 4096], 4096usize)) + } + _ => { + debug!("Unimplemented CNS: {self:?}"); + return Err(ResponseStatus::InternalError); + } + }; + + match res { + Ok(response) => { admin_send_response_body( resp, - admin_constrain_body(self.dofst, self.dlen, &[0u8; 4096])?, + admin_constrain_body(self.dofst, self.dlen, &response.0)?, ) .await } - _ => { - debug!("Unimplemented CNS: {self:?}"); - Err(ResponseStatus::InternalError) + Err(err) => { + admin_send_status(resp, AdminIoCqeStatusType::GenericCommandStatus(err)).await } } } @@ -1543,13 +1625,25 @@ impl RequestHandler for AdminNamespaceManagementRequest { crate::nvme::mi::AdminNamespaceManagementSelect::Create(req) => { if self.csi != 0 { debug!("Support CSI {}", self.csi); - return Err(ResponseStatus::InternalError); + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InternalError, + ), + ) + .await; } let Ok(nsid) = subsys.add_namespace(req.ncap) else { debug!("Failed to create namespace"); // TODO: Implement Base v2.1, 5.1.21.1, Figure 370 - return Err(ResponseStatus::InternalError); + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InternalError, + ), + ) + .await; }; let mh = MessageHeader::respond(MessageType::NvmeAdminCommand).encode()?; @@ -1666,7 +1760,13 @@ impl RequestHandler for AdminNamespaceAttachmentRequest { if self.nsid == u32::MAX { debug!("Refusing to perform {:?} for broadcast NSID", self.sel); - return Err(ResponseStatus::InvalidParameter); + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InvalidFieldInCommand, + ), + ) + .await; } // TODO: Handle MAXCNA @@ -1769,12 +1869,24 @@ impl RequestHandler for AdminSanitizeRequest { let Ok(config) = TryInto::::try_into(self.config) else { debug!("Invalid sanitize configuration: {}", self.config); - return Err(ResponseStatus::InvalidParameter); + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InvalidFieldInCommand, + ), + ) + .await; }; if subsys.sanicap.ndi && config.ndas { debug!("Request for No-Deallocate After Sanitize when No-Deallocate is inhibited"); - return Err(ResponseStatus::InvalidParameter); + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InvalidFieldInCommand, + ), + ) + .await; } // TODO: Implement action latency, progress state machine, error states @@ -1843,22 +1955,46 @@ impl RequestHandler for AdminFormatNvmRequest { let Some(ctlr) = subsys.ctlrs.iter().find(|c| c.id.0 == ctx.ctlid) else { debug!("Unrecognised CTLID: {}", ctx.ctlid); - return Err(ResponseStatus::InvalidParameter); + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InvalidFieldInCommand, + ), + ) + .await; }; let Ok(config) = TryInto::::try_into(self.config) else { debug!("Invalid configuration for Admin Format NVM"); - return Err(ResponseStatus::InvalidParameter); + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InvalidFieldInCommand, + ), + ) + .await; }; if config.lbafi != 0 { debug!("Unsupported LBA format index: {}", config.lbafi); - return Err(ResponseStatus::InvalidParameter); + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InvalidFieldInCommand, + ), + ) + .await; } if !ctlr.active_ns.iter().any(|ns| ns.0 == self.nsid) && self.nsid != u32::MAX { debug!("Unrecognised NSID: {}", self.nsid); - return Err(ResponseStatus::InvalidParameter); + return admin_send_status( + resp, + AdminIoCqeStatusType::GenericCommandStatus( + AdminIoCqeGenericCommandStatus::InvalidFieldInCommand, + ), + ) + .await; } // TODO: handle config.ses diff --git a/tests/admin.rs b/tests/admin.rs index 3577298..e25f76c 100644 --- a/tests/admin.rs +++ b/tests/admin.rs @@ -35,6 +35,16 @@ const RESP_ADMIN_SUCCESS: [u8; 23] = [ 0x30, 0xd5, 0xa2, 0x9b ]; +#[rustfmt::skip] +const RESP_ADMIN_STATUS_INVALID_FIELD: [u8; 23] = [ + 0x90, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x80, + 0x94, 0x8f, 0xde, 0x57, +]; + mod prohibited { use super::RESP_INVALID_COMMAND; use crate::common::{DeviceType, ExpectedRespChannel, new_device, setup}; @@ -89,6 +99,7 @@ mod prohibited { mod identify { use super::RESP_INVALID_COMMAND_SIZE; use super::RESP_INVALID_PARAMETER; + use crate::RESP_ADMIN_STATUS_INVALID_FIELD; use crate::common::DeviceType; use crate::common::ExpectedField; use crate::common::ExpectedRespChannel; @@ -98,6 +109,16 @@ mod identify { use crate::common::setup; use mctp::MsgIC; + #[rustfmt::skip] + const RESP_ADMIN_STATUS_INVALID_NAMESPACE: [u8; 23] = [ + 0x90, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x17, 0x80, + 0xfb, 0x4e, 0x5e, 0x4f + ]; + #[test] fn controller_short() { setup(); @@ -612,7 +633,7 @@ mod identify { 0x12, 0x14, 0x1c, 0x57 ]; - let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); + let resp = ExpectedRespChannel::new(&RESP_ADMIN_STATUS_INVALID_NAMESPACE); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -703,7 +724,7 @@ mod identify { 0x49, 0xb0, 0xa7, 0x22 ]; - let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); + let resp = ExpectedRespChannel::new(&RESP_ADMIN_STATUS_INVALID_NAMESPACE); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -847,16 +868,570 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // MIC - 0x49, 0xb0, 0xa7, 0x22 + 0x49, 0xb0, 0xa7, 0x22 + ]; + + #[rustfmt::skip] + let resp_fields: Vec = vec![ + (0, &[0x90]), + (19, &[0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), + (27, &[0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), + (35, &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), + (147, &[0x00, 0x00, 0x09, 0x00]) + ]; + + let resp = RelaxedRespChannel::new(resp_fields); + smol::block_on(async { + mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) + .await + }); + } + + #[test] + fn active_namespace_id_list_empty() { + setup(); + + let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a0a); + + #[rustfmt::skip] + const REQ: [u8; 71] = [ + 0x10, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, + + // SQE DWORD 1 + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // DOFST + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, + + // Reserved + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // SQE DWORD 10 + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // MIC + 0xff, 0xe7, 0x6f, 0x26 + ]; + + #[rustfmt::skip] + let resp_fields: Vec = vec![ + (0, &[0x90]), + (19, &[0; 4096]), + ]; + + let resp = RelaxedRespChannel::new(resp_fields); + smol::block_on(async { + mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) + .await + }); + } + + #[test] + fn active_namespace_id_list_populated() { + setup(); + + let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); + + #[rustfmt::skip] + const REQ: [u8; 71] = [ + 0x10, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, + + // SQE DWORD 1 + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // DOFST + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, + + // Reserved + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // SQE DWORD 10 + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // MIC + 0xff, 0xe7, 0x6f, 0x26 + ]; + + #[rustfmt::skip] + let resp_fields: Vec = vec![ + (0, &[0x90]), + (19, &[0x01, 0x00, 0x00, 0x00]), + ]; + + let resp = RelaxedRespChannel::new(resp_fields); + smol::block_on(async { + mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) + .await + }); + } + + #[test] + fn active_namespace_id_list_constrained_empty() { + setup(); + + let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); + + #[rustfmt::skip] + const REQ: [u8; 71] = [ + 0x10, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, + + // SQE DWORD 1 + 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // DOFST + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, + + // Reserved + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // SQE DWORD 10 + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // MIC + 0xa4, 0x43, 0xd4, 0x53 + ]; + + #[rustfmt::skip] + let resp_fields: Vec = vec![ + (0, &[0x90]), + (19, &[0; 4096]), + ]; + + let resp = RelaxedRespChannel::new(resp_fields); + smol::block_on(async { + mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) + .await + }); + } + + #[test] + fn namespace_identification_descriptor_list_bad_nsid() { + setup(); + + let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); + + #[rustfmt::skip] + const REQ: [u8; 71] = [ + 0x10, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, + + // SQE DWORD 1 + 0xfe, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // DOFST + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, + + // Reserved + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // SQE DWORD 10 + 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // MIC + 0xe4, 0x7b, 0x6f, 0x4c + ]; + + let resp = ExpectedRespChannel::new(&RESP_ADMIN_STATUS_INVALID_NAMESPACE); + smol::block_on(async { + mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) + .await + }); + } + + #[test] + fn namespace_identification_descriptor_list_broadcast_nsid() { + setup(); + + let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); + + #[rustfmt::skip] + const REQ: [u8; 71] = [ + 0x10, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, + + // SQE DWORD 1 + 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // DOFST + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, + + // Reserved + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // SQE DWORD 10 + 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // MIC + 0xbf, 0xdf, 0xd4, 0x39 + ]; + + let resp = ExpectedRespChannel::new(&RESP_ADMIN_STATUS_INVALID_NAMESPACE); + smol::block_on(async { + mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) + .await + }); + } + + #[test] + fn namespace_identification_descriptor_list_invalid_nsid_zero() { + setup(); + + let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); + + #[rustfmt::skip] + const REQ: [u8; 71] = [ + 0x10, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, + + // SQE DWORD 1 + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // DOFST + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, + + // Reserved + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // SQE DWORD 10 + 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // MIC + 0x71, 0x25, 0x20, 0x9c + ]; + + let resp = ExpectedRespChannel::new(&RESP_ADMIN_STATUS_INVALID_NAMESPACE); + smol::block_on(async { + mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) + .await + }); + } + + #[test] + fn namespace_identification_descriptor_list_invalid_nsid_exceeds() { + setup(); + + let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); + + #[rustfmt::skip] + const REQ: [u8; 71] = [ + 0x10, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, + + // SQE DWORD 1 + 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // DOFST + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, + + // Reserved + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // SQE DWORD 10 + 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // MIC + 0x9c, 0xc9, 0xec, 0x02 + ]; + + let resp = ExpectedRespChannel::new(&RESP_ADMIN_STATUS_INVALID_NAMESPACE); + smol::block_on(async { + mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) + .await + }); + } + + #[test] + fn namespace_identification_descriptor_list_unallocated_nsid() { + setup(); + + let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); + + #[rustfmt::skip] + const REQ: [u8; 71] = [ + 0x10, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, + + // SQE DWORD 1 + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // DOFST + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, + + // Reserved + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // SQE DWORD 10 + 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // MIC + 0xc7, 0x6d, 0x57, 0x77 + ]; + + let resp = ExpectedRespChannel::new(&RESP_ADMIN_STATUS_INVALID_NAMESPACE); + smol::block_on(async { + mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) + .await + }); + } + + #[test] + fn namespace_identification_descriptor_list() { + setup(); + + let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); + + #[rustfmt::skip] + const REQ: [u8; 71] = [ + 0x10, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, + + // SQE DWORD 1 + 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // DOFST + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, + + // Reserved + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // SQE DWORD 10 + 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // MIC + 0x2a, 0x81, 0x9b, 0xe9 + ]; + + #[rustfmt::skip] + let resp_fields: Vec = vec![ + (0, &[0x90]), + (19, &[0x03]), + (20, &[0x10]), + (39, &[0x04]), + (40, &[0x01]), + (41, &[0x00]), + (42, &[0x00]), + (43, &[0x00]), + ]; + + let resp = RelaxedRespChannel::new(resp_fields); + smol::block_on(async { + mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) + .await + }); + } + + #[test] + fn namespace_identify_large_size() { + setup(); + + let mut t = TestDevice::new(); + let ctlrid = t.subsys.add_controller(t.ppid).unwrap(); + // TODO lbads is assumed from current Namespace::new() + let lbads = 9; + let blocks = u64::MAX; + let nvmcap = (blocks as u128) * 2_u128.pow(lbads); + let nsid = t.subsys.add_namespace(blocks).unwrap(); + let ctrl = t.subsys.controller_mut(ctlrid); + ctrl.attach_namespace(nsid).unwrap(); + + #[rustfmt::skip] + const REQ: [u8; 71] = [ + 0x10, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, + + // SQE DWORD 1 + 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // DOFST + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, + + // Reserved + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // SQE DWORD 10 + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // MIC + 0x49, 0xb0, 0xa7, 0x22 + ]; + + let blocks_repr = blocks.to_le_bytes(); + let nvmcap_repr = nvmcap.to_le_bytes(); + + #[rustfmt::skip] + let resp_fields: Vec = vec![ + // NSZE + (19, &blocks_repr), + // NCAP + (19+8, &blocks_repr), + // NVMCAP + (19+48, &nvmcap_repr), + ]; + + let resp = RelaxedRespChannel::new(resp_fields); + smol::block_on(async { + t.mep + .handle_async(&mut t.subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) + .await + }); + } + + #[test] + fn allocated_namespace_id_list_populated() { + setup(); + + let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a0a); + + #[rustfmt::skip] + const REQ: [u8; 71] = [ + 0x10, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, + + // SQE DWORD 1 + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // DOFST + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, + + // Reserved + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // SQE DWORD 10 + 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + // MIC + 0x69, 0x10, 0xb7, 0xd2 ]; #[rustfmt::skip] let resp_fields: Vec = vec![ (0, &[0x90]), - (19, &[0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), - (27, &[0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), - (35, &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), - (147, &[0x00, 0x00, 0x09, 0x00]) + (19, &[0x01, 0x00, 0x00, 0x00]), + (23, &[0x00, 0x00, 0x00, 0x00]) ]; let resp = RelaxedRespChannel::new(resp_fields); @@ -867,7 +1442,7 @@ mod identify { } #[test] - fn active_namespace_id_list_empty() { + fn allocated_namespace_id_list_constrained_empty() { setup(); let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a0a); @@ -878,7 +1453,7 @@ mod identify { 0x06, 0x00, 0x00, 0x00, // SQE DWORD 1 - 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -893,7 +1468,7 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // SQE DWORD 10 - 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -901,7 +1476,7 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // MIC - 0xff, 0xe7, 0x6f, 0x26 + 0x32, 0xb4, 0x0c, 0xa7 ]; #[rustfmt::skip] @@ -918,10 +1493,10 @@ mod identify { } #[test] - fn active_namespace_id_list_populated() { + fn allocated_namespace_id_list_constrained_bad_nsid() { setup(); - let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); + let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a0a); #[rustfmt::skip] const REQ: [u8; 71] = [ @@ -929,7 +1504,7 @@ mod identify { 0x06, 0x00, 0x00, 0x00, // SQE DWORD 1 - 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -944,7 +1519,7 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // SQE DWORD 10 - 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -952,16 +1527,10 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // MIC - 0xff, 0xe7, 0x6f, 0x26 - ]; - - #[rustfmt::skip] - let resp_fields: Vec = vec![ - (0, &[0x90]), - (19, &[0x01, 0x00, 0x00, 0x00]), + 0xfc, 0x4e, 0xf8, 0x02 ]; - let resp = RelaxedRespChannel::new(resp_fields); + let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -969,10 +1538,10 @@ mod identify { } #[test] - fn active_namespace_id_list_constrained_empty() { + fn allocated_namespace_id_list_constrained_broadcast_nsid() { setup(); - let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); + let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a0a); #[rustfmt::skip] const REQ: [u8; 71] = [ @@ -980,7 +1549,7 @@ mod identify { 0x06, 0x00, 0x00, 0x00, // SQE DWORD 1 - 0x01, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -995,7 +1564,7 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // SQE DWORD 10 - 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1003,16 +1572,10 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // MIC - 0xa4, 0x43, 0xd4, 0x53 - ]; - - #[rustfmt::skip] - let resp_fields: Vec = vec![ - (0, &[0x90]), - (19, &[0; 4096]), + 0xa7, 0xea, 0x43, 0x77 ]; - let resp = RelaxedRespChannel::new(resp_fields); + let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -1020,7 +1583,7 @@ mod identify { } #[test] - fn namespace_identification_descriptor_list_bad_nsid() { + fn identify_namespace_for_allocated_namespace_id_invalid_nsid() { setup(); let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); @@ -1031,7 +1594,7 @@ mod identify { 0x06, 0x00, 0x00, 0x00, // SQE DWORD 1 - 0xfe, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1046,7 +1609,7 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // SQE DWORD 10 - 0x03, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1054,10 +1617,10 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // MIC - 0xe4, 0x7b, 0x6f, 0x4c + 0xe7, 0xd2, 0xf8, 0x68 ]; - let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); + let resp = ExpectedRespChannel::new(&RESP_ADMIN_STATUS_INVALID_NAMESPACE); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -1065,7 +1628,7 @@ mod identify { } #[test] - fn namespace_identification_descriptor_list_broadcast_nsid() { + fn identify_namespace_for_allocated_namespace_id_broadcast_nsid() { setup(); let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); @@ -1091,7 +1654,7 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // SQE DWORD 10 - 0x03, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1099,10 +1662,10 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // MIC - 0xbf, 0xdf, 0xd4, 0x39 + 0x29, 0x28, 0x0c, 0xcd ]; - let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); + let resp = ExpectedRespChannel::new(&RESP_ADMIN_STATUS_INVALID_NAMESPACE); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -1110,7 +1673,7 @@ mod identify { } #[test] - fn namespace_identification_descriptor_list_invalid_nsid_zero() { + fn identify_namespace_for_allocated_namespace_id_unallocated_nsid() { setup(); let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); @@ -1121,7 +1684,7 @@ mod identify { 0x06, 0x00, 0x00, 0x00, // SQE DWORD 1 - 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1136,7 +1699,7 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // SQE DWORD 10 - 0x03, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1144,10 +1707,24 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // MIC - 0x71, 0x25, 0x20, 0x9c + 0x51, 0x9a, 0x8f, 0x83 ]; - let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); + #[rustfmt::skip] + const RESP_DATA: [u8; 19] = [ + 0x90, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00 + ]; + + const RESP_LEN: usize = 4119; + let mut resp: [u8; RESP_LEN] = [0; RESP_LEN]; + resp[..RESP_DATA.len()].copy_from_slice(&RESP_DATA); + resp[(RESP_LEN - 4)..].copy_from_slice(&[0x87, 0xd7, 0x1f, 0xaa]); + + let resp = ExpectedRespChannel::new(&resp); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -1155,10 +1732,14 @@ mod identify { } #[test] - fn namespace_identification_descriptor_list_invalid_nsid_exceeds() { + fn identify_namespace_for_allocated_namespace_id_inactive_nsid() { setup(); - let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); + let lbads = 9u8; + let blocks = 1024u64; + let nvmcap = (blocks as u128) * 2_u128.pow(lbads as u32); + + let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a0a); #[rustfmt::skip] const REQ: [u8; 71] = [ @@ -1166,7 +1747,7 @@ mod identify { 0x06, 0x00, 0x00, 0x00, // SQE DWORD 1 - 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1181,7 +1762,7 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // SQE DWORD 10 - 0x03, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1189,10 +1770,26 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // MIC - 0x9c, 0xc9, 0xec, 0x02 + 0xbc, 0x76, 0x43, 0x1d ]; - let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); + let blocks_repr = blocks.to_le_bytes(); + let nvmcap_repr = nvmcap.to_le_bytes(); + let lbads_repr = [lbads]; + + #[rustfmt::skip] + let resp_fields: Vec = vec![ + // NSZE + (19, &blocks_repr), + // NCAP + (19+8, &blocks_repr), + // NVMCAP + (19+48, &nvmcap_repr), + // LBAF0_LBADS + (19+130, &lbads_repr) + ]; + + let resp = RelaxedRespChannel::new(resp_fields); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -1200,9 +1797,13 @@ mod identify { } #[test] - fn namespace_identification_descriptor_list_unallocated_nsid() { + fn identify_namespace_for_allocated_namespace_id_active_nsid() { setup(); + let lbads = 9u8; + let blocks = 1024u64; + let nvmcap = (blocks as u128) * 2_u128.pow(lbads as u32); + let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); #[rustfmt::skip] @@ -1211,7 +1812,7 @@ mod identify { 0x06, 0x00, 0x00, 0x00, // SQE DWORD 1 - 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1226,7 +1827,7 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // SQE DWORD 10 - 0x03, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1234,10 +1835,26 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // MIC - 0xc7, 0x6d, 0x57, 0x77 + 0xbc, 0x76, 0x43, 0x1d ]; - let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); + let blocks_repr = blocks.to_le_bytes(); + let nvmcap_repr = nvmcap.to_le_bytes(); + let lbads_repr = [lbads]; + + #[rustfmt::skip] + let resp_fields: Vec = vec![ + // NSZE + (19, &blocks_repr), + // NCAP + (19+8, &blocks_repr), + // NVMCAP + (19+48, &nvmcap_repr), + // LBAF0_LBADS + (19+130, &lbads_repr) + ]; + + let resp = RelaxedRespChannel::new(resp_fields); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -1245,7 +1862,7 @@ mod identify { } #[test] - fn namespace_identification_descriptor_list() { + fn namespace_attached_controller_list_invalid_nsid() { setup(); let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); @@ -1256,7 +1873,7 @@ mod identify { 0x06, 0x00, 0x00, 0x00, // SQE DWORD 1 - 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1271,7 +1888,7 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // SQE DWORD 10 - 0x03, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1279,22 +1896,24 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // MIC - 0x2a, 0x81, 0x9b, 0xe9 + 0x84, 0xe3, 0xc4, 0xa3 ]; #[rustfmt::skip] - let resp_fields: Vec = vec![ - (0, &[0x90]), - (19, &[0x03]), - (20, &[0x10]), - (39, &[0x04]), - (40, &[0x01]), - (41, &[0x00]), - (42, &[0x00]), - (43, &[0x00]), + const RESP_DATA: [u8; 19] = [ + 0x90, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00 ]; - let resp = RelaxedRespChannel::new(resp_fields); + const RESP_LEN: usize = 4119; + let mut resp: [u8; RESP_LEN] = [0; RESP_LEN]; + resp[..RESP_DATA.len()].copy_from_slice(&RESP_DATA); + resp[(RESP_LEN - 4)..].copy_from_slice(&[0x87, 0xd7, 0x1f, 0xaa]); + + let resp = ExpectedRespChannel::new(&resp); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -1302,18 +1921,10 @@ mod identify { } #[test] - fn namespace_identify_large_size() { + fn namespace_attached_controller_list_broadcast_nsid() { setup(); - let mut t = TestDevice::new(); - let ctlrid = t.subsys.add_controller(t.ppid).unwrap(); - // TODO lbads is assumed from current Namespace::new() - let lbads = 9; - let blocks = u64::MAX; - let nvmcap = (blocks as u128) * 2_u128.pow(lbads); - let nsid = t.subsys.add_namespace(blocks).unwrap(); - let ctrl = t.subsys.controller_mut(ctlrid); - ctrl.attach_namespace(nsid).unwrap(); + let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); #[rustfmt::skip] const REQ: [u8; 71] = [ @@ -1321,7 +1932,7 @@ mod identify { 0x06, 0x00, 0x00, 0x00, // SQE DWORD 1 - 0x01, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1336,7 +1947,7 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // SQE DWORD 10 - 0x00, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1344,35 +1955,21 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // MIC - 0x49, 0xb0, 0xa7, 0x22 - ]; - - let blocks_repr = blocks.to_le_bytes(); - let nvmcap_repr = nvmcap.to_le_bytes(); - - #[rustfmt::skip] - let resp_fields: Vec = vec![ - // NSZE - (19, &blocks_repr), - // NCAP - (19+8, &blocks_repr), - // NVMCAP - (19+48, &nvmcap_repr), + 0x4a, 0x19, 0x30, 0x06 ]; - let resp = RelaxedRespChannel::new(resp_fields); + let resp = ExpectedRespChannel::new(&RESP_ADMIN_STATUS_INVALID_FIELD); smol::block_on(async { - t.mep - .handle_async(&mut t.subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) + mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await }); } #[test] - fn allocated_namespace_id_list_populated() { + fn namespace_attached_controller_list_unallocated_nsid() { setup(); - let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a0a); + let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); #[rustfmt::skip] const REQ: [u8; 71] = [ @@ -1380,7 +1977,7 @@ mod identify { 0x06, 0x00, 0x00, 0x00, // SQE DWORD 1 - 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1395,7 +1992,7 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // SQE DWORD 10 - 0x10, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1403,17 +2000,24 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // MIC - 0x69, 0x10, 0xb7, 0xd2 + 0x32, 0xab, 0xb3, 0x48 ]; #[rustfmt::skip] - let resp_fields: Vec = vec![ - (0, &[0x90]), - (19, &[0x01, 0x00, 0x00, 0x00]), - (23, &[0x00, 0x00, 0x00, 0x00]) + const RESP_DATA: [u8; 19] = [ + 0x90, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00 ]; - let resp = RelaxedRespChannel::new(resp_fields); + const RESP_LEN: usize = 4119; + let mut resp: [u8; RESP_LEN] = [0; RESP_LEN]; + resp[..RESP_DATA.len()].copy_from_slice(&RESP_DATA); + resp[(RESP_LEN - 4)..].copy_from_slice(&[0x87, 0xd7, 0x1f, 0xaa]); + + let resp = ExpectedRespChannel::new(&resp); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -1421,7 +2025,7 @@ mod identify { } #[test] - fn allocated_namespace_id_list_constrained_empty() { + fn namespace_attached_controller_list_inactive_nsid() { setup(); let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a0a); @@ -1447,7 +2051,7 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // SQE DWORD 10 - 0x10, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1455,16 +2059,24 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // MIC - 0x32, 0xb4, 0x0c, 0xa7 + 0xdf, 0x47, 0x7f, 0xd6 ]; #[rustfmt::skip] - let resp_fields: Vec = vec![ - (0, &[0x90]), - (19, &[0; 4096]), + const RESP_DATA: [u8; 19] = [ + 0x90, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00 ]; - let resp = RelaxedRespChannel::new(resp_fields); + const RESP_LEN: usize = 4119; + let mut resp: [u8; RESP_LEN] = [0; RESP_LEN]; + resp[..RESP_DATA.len()].copy_from_slice(&RESP_DATA); + resp[(RESP_LEN - 4)..].copy_from_slice(&[0x87, 0xd7, 0x1f, 0xaa]); + + let resp = ExpectedRespChannel::new(&resp); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -1472,10 +2084,10 @@ mod identify { } #[test] - fn allocated_namespace_id_list_constrained_bad_nsid() { + fn namespace_attached_controller_list_active_nsid_unconstrained() { setup(); - let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a0a); + let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); #[rustfmt::skip] const REQ: [u8; 71] = [ @@ -1483,7 +2095,7 @@ mod identify { 0x06, 0x00, 0x00, 0x00, // SQE DWORD 1 - 0xfe, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1498,7 +2110,7 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // SQE DWORD 10 - 0x10, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1506,10 +2118,17 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // MIC - 0xfc, 0x4e, 0xf8, 0x02 + 0xdf, 0x47, 0x7f, 0xd6 ]; - let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); + #[rustfmt::skip] + let resp_fields: Vec = vec![ + (0, &[0x90]), + (19, &[1, 0]), + (19 + 2, &[0, 0]), + ]; + + let resp = RelaxedRespChannel::new(resp_fields); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -1517,10 +2136,10 @@ mod identify { } #[test] - fn allocated_namespace_id_list_constrained_broadcast_nsid() { + fn namespace_attached_controller_list_active_nsid_constrained() { setup(); - let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a0a); + let (mut mep, mut subsys) = new_device(DeviceType::P1p1tC1iN1a1a); #[rustfmt::skip] const REQ: [u8; 71] = [ @@ -1528,7 +2147,7 @@ mod identify { 0x06, 0x00, 0x00, 0x00, // SQE DWORD 1 - 0xff, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1543,7 +2162,7 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // SQE DWORD 10 - 0x10, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1551,10 +2170,16 @@ mod identify { 0x00, 0x00, 0x00, 0x00, // MIC - 0xa7, 0xea, 0x43, 0x77 + 0x6b, 0xa9, 0x6a, 0x8a ]; - let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); + #[rustfmt::skip] + let resp_fields: Vec = vec![ + (0, &[0x90]), + (19, &[0, 0]), + ]; + + let resp = RelaxedRespChannel::new(resp_fields); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -1620,24 +2245,12 @@ mod get_log_page { }; use crate::{ - RESP_INVALID_COMMAND_SIZE, RESP_INVALID_PARAMETER, + RESP_ADMIN_STATUS_INVALID_FIELD, RESP_INVALID_COMMAND_SIZE, common::{ DeviceType, ExpectedField, ExpectedRespChannel, RelaxedRespChannel, new_device, setup, }, }; - // Base v2.1, 5.1.12 - // Admin command response with SCT / SC set - #[rustfmt::skip] - const RESP_ADMIN_STATUS_INVALID_FIELD: [u8; 23] = [ - 0x90, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x05, 0x80, - 0x94, 0x8f, 0xde, 0x57, - ]; - #[test] fn get_supported_log_pages_short() { setup(); @@ -1767,7 +2380,7 @@ mod get_log_page { 0x29, 0xe2, 0x53, 0x0a ]; - let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); + let resp = ExpectedRespChannel::new(&RESP_ADMIN_STATUS_INVALID_FIELD); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -1955,7 +2568,7 @@ mod get_log_page { 0x80, 0x60, 0xc4, 0x3b ]; - let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); + let resp = ExpectedRespChannel::new(&RESP_ADMIN_STATUS_INVALID_FIELD); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -3100,7 +3713,7 @@ mod namespace_attachment { use mctp::MsgIC; use crate::{ - RESP_INVALID_COMMAND_SIZE, RESP_INVALID_PARAMETER, + RESP_ADMIN_STATUS_INVALID_FIELD, RESP_INVALID_COMMAND_SIZE, common::{DeviceType, ExpectedRespChannel, new_device, setup}, }; @@ -3297,7 +3910,7 @@ mod namespace_attachment { req[..REQ_DATA.len()].copy_from_slice(&REQ_DATA); req[{ len - REQ_MIC.len() }..].copy_from_slice(&REQ_MIC); - let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); + let resp = ExpectedRespChannel::new(&RESP_ADMIN_STATUS_INVALID_FIELD); smol::block_on(async { mep.handle_async(&mut subsys, &req, MsgIC(true), resp, async |_| Ok(())) .await @@ -3680,7 +4293,7 @@ mod namespace_attachment { req[..REQ_DATA.len()].copy_from_slice(&REQ_DATA); req[{ len - REQ_MIC.len() }..].copy_from_slice(&REQ_MIC); - let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); + let resp = ExpectedRespChannel::new(&RESP_ADMIN_STATUS_INVALID_FIELD); smol::block_on(async { mep.handle_async(&mut subsys, &req, MsgIC(true), resp, async |_| Ok(())) .await @@ -3875,7 +4488,7 @@ mod sanitize { use mctp::MsgIC; use crate::{ - RESP_ADMIN_SUCCESS, RESP_INVALID_COMMAND_SIZE, RESP_INVALID_PARAMETER, + RESP_ADMIN_STATUS_INVALID_FIELD, RESP_ADMIN_SUCCESS, RESP_INVALID_COMMAND_SIZE, common::{DeviceType, ExpectedRespChannel, new_device, setup}, }; @@ -4143,7 +4756,7 @@ mod sanitize { 0xc8, 0x47, 0x8d, 0x88 ]; - let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); + let resp = ExpectedRespChannel::new(&RESP_ADMIN_STATUS_INVALID_FIELD); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -4200,7 +4813,7 @@ mod format_nvm { use mctp::MsgIC; use crate::{ - RESP_ADMIN_SUCCESS, RESP_INVALID_COMMAND_SIZE, RESP_INVALID_PARAMETER, + RESP_ADMIN_STATUS_INVALID_FIELD, RESP_ADMIN_SUCCESS, RESP_INVALID_COMMAND_SIZE, common::{DeviceType, ExpectedRespChannel, new_device, setup}, }; @@ -4332,7 +4945,7 @@ mod format_nvm { 0x0f, 0x14, 0xe6, 0x14 ]; - let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); + let resp = ExpectedRespChannel::new(&RESP_ADMIN_STATUS_INVALID_FIELD); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -4377,7 +4990,7 @@ mod format_nvm { 0x7a, 0x05, 0xbd, 0xb8 ]; - let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); + let resp = ExpectedRespChannel::new(&RESP_ADMIN_STATUS_INVALID_FIELD); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -4422,7 +5035,7 @@ mod format_nvm { 0x27, 0xdd, 0x59, 0xa2 ]; - let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); + let resp = ExpectedRespChannel::new(&RESP_ADMIN_STATUS_INVALID_FIELD); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await @@ -4467,7 +5080,7 @@ mod format_nvm { 0xd1, 0xad, 0x95, 0x56 ]; - let resp = ExpectedRespChannel::new(&RESP_INVALID_PARAMETER); + let resp = ExpectedRespChannel::new(&RESP_ADMIN_STATUS_INVALID_FIELD); smol::block_on(async { mep.handle_async(&mut subsys, &REQ, MsgIC(true), resp, async |_| Ok(())) .await diff --git a/tests/common/mod.rs b/tests/common/mod.rs index d2a1f1e..26ddca7 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -100,11 +100,12 @@ impl mctp::AsyncRespChannel for ExpectedRespChannel<'_> { async fn send_vectored(&mut self, _integrity_check: MsgIC, bufs: &[&[u8]]) -> mctp::Result<()> { self.sent = true; - assert!( - self.resp.is_empty() == bufs.iter().all(|b| b.is_empty()), - "Failed emptiness consensus:\n\tExpected: {:02x?}\n\tFound: {bufs:02x?}", - self.resp + assert_eq!( + self.resp.is_empty(), + bufs.iter().all(|b| b.is_empty()), + "Failed emptiness consensus" ); + assert_eq!(bufs.iter().map(|b| b.len()).sum::(), self.resp.len()); assert!( core::iter::zip(self.resp, bufs.iter().flat_map(|b| b.iter())).all(|(e, f)| e == f), "Expected: {:02x?}, found: {:02x?}",