pub mod mi;
use deku::ctx::{BitSize, Endian, Order};
use deku::{DekuError, DekuRead, DekuWrite, deku_derive};
use flagset::flags;
use log::debug;
use crate::wire::WireFlagSet;
use crate::wire::WireString;
use crate::wire::WireUuid;
use crate::wire::WireVec;
use crate::{Discriminant, Encode};
#[repr(usize)]
pub enum ControllerProperties {
Cc(ControllerConfiguration) = 0x14,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct ControllerConfiguration {
pub en: bool,
}
flags! {
#[repr(u32)]
pub enum ControllerStatusFlags: u32 {
Rdy = 1 << 0,
Cfs = 1 << 1,
ShstInProgress = 1 << 2,
ShstComplete = 1 << 3,
ShstReserved = (ControllerStatusFlags::ShstInProgress | ControllerStatusFlags::ShstComplete).bits(),
Nssro = 1 << 4,
Pp = 1 << 5,
St = 1 << 6,
}
}
#[derive(Debug, DekuRead, DekuWrite)]
#[deku(bit_order = "lsb", ctx = "endian: Endian", endian = "endian")]
struct AdminIoCqeStatus {
cid: u16,
#[deku(bits = 1)]
p: bool,
#[deku(bits = 8)]
sc: u8,
#[deku(bits = 3)]
sct: u8,
#[deku(bits = 2)]
crd: CommandRetryDelay,
#[deku(bits = 1)]
m: bool,
#[deku(bits = 1)]
dnr: bool,
}
impl From<AdminIoCqeStatusType> for AdminIoCqeStatus {
fn from(value: AdminIoCqeStatusType) -> Self {
match value {
AdminIoCqeStatusType::GenericCommandStatus(sts) => AdminIoCqeStatus {
cid: 0,
sc: sts as u8,
p: true,
dnr: sts != AdminIoCqeGenericCommandStatus::SuccessfulCompletion,
m: false,
crd: CommandRetryDelay::None,
sct: value.id(),
},
AdminIoCqeStatusType::CommandSpecificStatus(sts) => AdminIoCqeStatus {
cid: 0,
sc: sts,
p: true,
dnr: true,
m: false,
crd: CommandRetryDelay::None,
sct: value.id(),
},
AdminIoCqeStatusType::MediaAndDataIntegrityErrors => todo!(),
AdminIoCqeStatusType::PathRelatedStatus => todo!(),
AdminIoCqeStatusType::VendorSpecific => todo!(),
}
}
}
#[derive(Debug, DekuRead, DekuWrite)]
#[deku(
bits = "bits.0",
bit_order = "order",
ctx = "endian: Endian, bits: BitSize, order: Order",
endian = "endian",
id_type = "u8"
)]
#[repr(u8)]
enum CommandRetryDelay {
None = 0x00,
Time1 = 0x01,
Time2 = 0x02,
Time3 = 0x03,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
enum AdminIoCqeStatusType {
GenericCommandStatus(AdminIoCqeGenericCommandStatus) = 0x00,
CommandSpecificStatus(u8) = 0x01,
#[expect(dead_code)]
MediaAndDataIntegrityErrors = 0x02,
#[expect(dead_code)]
PathRelatedStatus = 0x03,
#[expect(dead_code)]
VendorSpecific = 0x07,
}
unsafe impl Discriminant<u8> for AdminIoCqeStatusType {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
enum AdminIoCqeGenericCommandStatus {
SuccessfulCompletion = 0x00,
InvalidFieldInCommand = 0x02,
InternalError = 0x06,
InvalidNamespaceOrFormat = 0x0b,
}
impl From<DekuError> for AdminIoCqeGenericCommandStatus {
fn from(err: DekuError) -> Self {
debug!("Codec operation failed: {err}");
Self::InternalError
}
}
#[derive(Debug, DekuRead, Eq, PartialEq)]
#[deku(ctx = "endian: Endian", endian = "little")]
struct ControllerListRequest {
numids: u16,
#[deku(count = "*numids")]
ids: WireVec<u16, 2047>,
}
#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
#[deku(
bits = "bits.0",
ctx = "endian: Endian, bits: BitSize",
endian = "endian",
id_type = "u8"
)]
#[repr(u8)]
enum SecureEraseSettings {
NoOperation = 0b000,
UserDataErase = 0b001,
CryptographicErase = 0b010,
}
#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
#[deku(ctx = "endian: Endian", endian = "endian")]
struct AdminFormatNvmConfiguration {
#[deku(bits = "3")]
pi: u8,
#[deku(bits = "1")]
mset: bool,
#[deku(bits = "4")]
lbafl: u8,
#[deku(bits = "2", pad_bits_before = "2")]
lbafu: u8,
#[deku(bits = "3")]
ses: SecureEraseSettings,
#[deku(bits = "1", pad_bytes_after = "2")]
pil: bool,
}
impl AdminFormatNvmConfiguration {
fn lbafi(&self) -> u8 {
self.lbafu << 4 | self.lbafl
}
}
#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
#[deku(ctx = "endian: Endian, lid: u8", id = "lid", endian = "endian")]
#[repr(u8)]
pub enum AdminGetLogPageLidRequestType {
SupportedLogPages = 0x00,
ErrorInformation = 0x01,
SmartHealthInformation = 0x02,
FeatureIdentifiersSupportedAndEffects = 0x12,
SanitizeStatus = 0x81,
}
unsafe impl crate::Discriminant<u8> for AdminGetLogPageLidRequestType {}
#[derive(Debug, DekuRead, DekuWrite)]
#[deku(endian = "little")]
pub struct AdminGetLogPageSupportedLogPagesResponse {
lsids: WireVec<LidSupportedAndEffectsDataStructure, 256>,
}
impl Encode<1024> for AdminGetLogPageSupportedLogPagesResponse {}
flags! {
pub enum LidSupportedAndEffectsFlags: u8 {
Lsupp,
Ios,
}
}
#[derive(Clone, Copy, Debug, DekuRead, DekuWrite)]
#[deku(ctx = "endian: Endian", endian = "endian")]
pub struct LidSupportedAndEffectsDataStructure {
flags: WireFlagSet<LidSupportedAndEffectsFlags>,
#[deku(seek_from_current = "1")]
lidsp: u16,
}
flags! {
pub enum CriticalWarningFlags: u8 {
Ascbt,
Ttc,
Ndr,
Amro,
Vmbf,
Pmrro,
}
}
flags! {
pub enum EnduranceGroupCriticalWarningSummaryFlags: u8 {
Egascbt = 1 << 0,
Egdr = 1 << 2,
Egro = 1 << 3,
}
}
#[derive(Debug, Default, DekuRead, DekuWrite)]
#[deku(endian = "little")]
pub struct SmartHealthInformationLogPageResponse {
cw: WireFlagSet<CriticalWarningFlags>,
ctemp: u16,
avsp: u8,
avspt: u8,
pused: u8,
egcws: WireFlagSet<EnduranceGroupCriticalWarningSummaryFlags>,
#[deku(seek_from_current = "25")]
dur: u128,
duw: u128,
hrc: u128,
hwc: u128,
cbt: u128,
pwrc: u128,
poh: u128,
upl: u128,
mdie: u128,
neile: u128,
wctt: u32,
cctt: u32,
tsen: [u16; 8],
tmttc: [u32; 2],
#[deku(pad_bytes_after = "280")]
tttmt: [u32; 2],
}
impl Encode<512> for SmartHealthInformationLogPageResponse {}
flags! {
pub enum FidSupportedAndEffectsFlags: u32 {
Fsupp = 1 << 0,
Udcc = 1 << 1,
Ncc = 1 << 2,
Nic = 1 << 3,
Ccc = 1 << 4,
Uss = 1 << 19,
FspNscpe = 1 << 20,
FspCscpe = 1 << 21,
FspNsetscpe = 1 << 22,
FspEgscpe = 1 << 23,
FspDscpe = 1 << 24,
FspNsubscpe = 1 << 25,
FspCdqscp = 1 << 26,
FspMask = (
FidSupportedAndEffectsFlags::FspNscpe
| FidSupportedAndEffectsFlags::FspNsetscpe
| FidSupportedAndEffectsFlags::FspEgscpe
| FidSupportedAndEffectsFlags::FspNsubscpe
| FidSupportedAndEffectsFlags::FspCdqscp
).bits()
}
}
#[derive(Clone, Copy, Debug, Default, DekuRead, DekuWrite, Eq, PartialEq)]
#[deku(
bits = "bits.0",
ctx = "endian: Endian, bits: BitSize",
endian = "endian",
id_type = "u8"
)]
#[repr(u8)]
pub enum SanitizeOperationStatus {
#[default]
SanitizeNeverStarted = 0b000,
Sanitized = 0b001,
Sanitizing = 0b010,
SanitizeFailed = 0b011,
SanitizedUnexpectedDeallocate = 0b100,
}
unsafe impl crate::Discriminant<u8> for SanitizeOperationStatus {}
#[derive(Clone, Copy, Debug, Default, DekuRead, DekuWrite)]
#[deku(ctx = "endian: Endian", endian = "endian")]
pub struct SanitizeStatus {
#[deku(bits = "5")]
opc: u8,
#[deku(bits = "3")]
sos: SanitizeOperationStatus,
#[deku(bits = "1", pad_bits_before = "6")]
mvcncled: bool,
#[deku(bits = "1")]
gde: bool,
}
#[derive(Clone, Copy, Debug, Default, DekuRead, DekuWrite, Eq, PartialEq)]
#[deku(
bits = "bits.0",
ctx = "endian: Endian, bits: BitSize",
endian = "endian",
id_type = "u8"
)]
#[repr(u8)]
enum SanitizeState {
#[default]
Idle = 0x00,
RestrictedProcessing = 0x01,
RestrictedFailure = 0x02,
UnrestrictedProcessing = 0x03,
UnrestrictedFailure = 0x04,
MediaVerification = 0x05,
PostVerificationDeallocation = 0x06,
}
unsafe impl crate::Discriminant<u8> for SanitizeState {}
#[derive(Clone, Copy, Debug, Default, DekuRead, DekuWrite)]
#[deku(ctx = "endian: Endian", endian = "endian")]
pub struct SanitizeStateInformation {
#[deku(bits = "4")]
fails: u8,
#[deku(bits = "4")]
sans: SanitizeState,
}
#[derive(Debug, DekuRead, DekuWrite)]
#[deku(endian = "little")]
struct SanitizeStatusLogPageResponse {
sprog: u16,
sstat: SanitizeStatus,
scdw10: AdminSanitizeConfiguration,
eto: u32,
etbe: u32,
etce: u32,
etodmm: u32,
etbenmm: u32,
etcenmm: u32,
etpvds: u32,
ssi: SanitizeStateInformation,
}
impl Encode<512> for SanitizeStatusLogPageResponse {}
#[derive(Clone, Copy, Debug, DekuRead, DekuWrite, Eq, PartialEq)]
#[deku(ctx = "endian: Endian, cns: u8", id = "cns", endian = "endian")]
#[repr(u8)]
enum AdminIdentifyCnsRequestType {
NvmIdentifyNamespace = 0x00,
IdentifyController = 0x01,
ActiveNamespaceIDList = 0x02,
NamespaceIdentificationDescriptorList = 0x03,
IoIdentifyNamespace = 0x05,
IoIdentifyController = 0x06,
IoActiveNamespaceIdList = 0x07,
IdentifyNamespace = 0x08,
AllocatedNamespaceIdList = 0x10,
IdentifyNamespaceForAllocatedNamespaceId = 0x11,
NamespaceAttachedControllerList = 0x12,
NvmSubsystemControllerList = 0x13,
SecondaryControllerList = 0x15,
}
unsafe impl Discriminant<u8> for AdminIdentifyCnsRequestType {}
#[derive(Debug, Default, DekuRead, DekuWrite)]
#[deku(endian = "little")]
pub struct AdminIdentifyNvmIdentifyNamespaceResponse {
nsze: u64,
ncap: u64,
nuse: u64,
nsfeat: u8,
nlbaf: u8,
flbas: u8,
mc: u8,
dpc: u8,
dps: u8,
#[deku(seek_from_current = "18")]
nvmcap: u128,
#[deku(seek_from_current = "64")]
lbaf0: u16,
lbaf0_lbads: u8,
lbaf0_rp: u8,
}
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,
}
}
}
#[derive(Clone, Copy, Debug, DekuRead, DekuWrite)]
#[deku(id_type = "u8", endian = "endian", ctx = "endian: Endian")]
#[repr(u8)]
pub enum CommandSetIdentifier {
Nvm = 0x00,
KeyValue = 0x01,
ZonedNamespace = 0x02,
SubsystemLocalMemory = 0x03,
ComputationalPrograms = 0x04,
}
flags! {
enum ControllerMultipathIoNamespaceSharingCapabilityFlags: u8 {
Mports,
Mctrs,
Ft,
Anars,
}
}
flags! {
enum ControllerAttributeFlags: u32 {
Hids,
Nopspm,
Nsets,
Rrlvls,
Egs,
Plm,
Tbkas,
Ng,
Sqa,
Ulist,
Mds,
Fcm,
Vcm,
Deg,
Dnvms,
Elbas,
Mem,
Hmbr,
Rhii,
Fdps,
}
}
#[derive(Clone, Copy, Debug, DekuRead, DekuWrite, PartialEq)]
#[deku(id_type = "u8", endian = "endian", ctx = "endian: Endian")]
#[repr(u8)]
enum ControllerType {
Reserved = 0x00,
IoController = 0x01,
DiscoveryController = 0x02,
AdministrativeController = 0x03,
}
impl From<crate::ControllerType> for ControllerType {
fn from(value: crate::ControllerType) -> Self {
match value {
crate::ControllerType::Io => Self::IoController,
crate::ControllerType::Discovery => Self::DiscoveryController,
crate::ControllerType::Administrative => Self::AdministrativeController,
}
}
}
flags! {
enum NvmSubsystemReportFlags: u8 {
Nvmesd,
Nvmee,
}
}
flags! {
enum ManagementEndpointCapabilityFlags: u8 {
Twpme,
Pcieme,
}
}
flags! {
#[repr(u8)]
pub enum LogPageAttributes: u8 {
Smarts,
Cses,
Lpeds,
Ts,
Pes,
Mlps,
Da4s,
}
}
#[derive(Clone, Copy, Debug, Default, DekuRead, DekuWrite)]
#[deku(
bits = "bits.0",
bit_order = "order",
ctx = "endian: Endian, bits: BitSize, order: Order",
endian = "endian",
id_type = "u8"
)]
#[repr(u8)]
pub enum NoDeallocateModifiesMediaAfterSanitize {
Undefined = 0b00,
#[default]
Unmodified = 0b01,
Modified = 0b10,
Reserved = 0b11,
}
flags! {
pub enum SanitizeCapabilityFlags: u32 {
Ces = 1 << 0,
Bes = 1 << 1,
Ows = 1 << 2,
Vers = 1 << 3,
Ndi = 1 << 29,
}
}
#[derive(Clone, Copy, Debug, DekuRead, DekuWrite)]
#[deku(bit_order = "lsb", ctx = "endian: Endian", endian = "endian")]
pub struct SanitizeCapabilities {
#[deku(bits = 30)]
caps: WireFlagSet<SanitizeCapabilityFlags>,
#[deku(bits = 2)]
nodmmas: NoDeallocateModifiesMediaAfterSanitize,
}
flags! {
pub enum FormatNvmAttributes: u8 {
Fns,
Sens,
Cryes,
Fnvmbs,
}
}
#[derive(Debug, DekuRead, DekuWrite)]
#[deku(endian = "little")]
struct AdminIdentifyControllerResponse {
vid: u16,
ssvid: u16,
sn: WireString<20>,
mn: WireString<40>,
fr: WireString<8>,
rab: u8,
ieee: [u8; 3],
cmic: WireFlagSet<ControllerMultipathIoNamespaceSharingCapabilityFlags>,
mdts: u8,
cntlid: u16,
ver: u32,
rtd3r: u32,
rtd3e: u32,
oaes: u32,
ctratt: WireFlagSet<ControllerAttributeFlags>,
#[deku(seek_from_current = "11")]
cntrltype: crate::nvme::ControllerType,
#[deku(seek_from_current = "141")]
nvmsr: WireFlagSet<NvmSubsystemReportFlags>,
vwci: u8,
mec: WireFlagSet<ManagementEndpointCapabilityFlags>,
ocas: u16,
acl: u8,
aerl: u8,
frmw: u8,
lpa: WireFlagSet<LogPageAttributes>,
elpe: u8,
npss: u8,
avscc: u8,
apsta: u8,
wctemp: u16,
cctemp: u16,
#[deku(seek_from_current = "49")]
fwug: u8,
kas: u16,
#[deku(seek_from_current = "6")]
sanicap: SanitizeCapabilities,
#[deku(seek_from_current = "54")]
cqt: u16,
#[deku(seek_from_current = "124")]
sqes: u8,
cqes: u8,
maxcmd: u16,
nn: u32,
oncs: u16,
fuses: u16,
fna: WireFlagSet<FormatNvmAttributes>,
vwc: u8,
awun: u16,
awupf: u16,
icsvscc: u8,
nwpc: u8,
#[deku(seek_from_current = "8")]
mnan: u32,
#[deku(seek_from_current = "224")]
subnqn: WireString<256>,
#[deku(seek_from_current = "778")]
fcatt: u8,
msdbd: u8,
ofcs: u16,
}
impl Encode<4096> for AdminIdentifyControllerResponse {}
#[derive(Debug, DekuRead, DekuWrite)]
#[deku(endian = "little")]
struct AdminIdentifyActiveNamespaceIdListResponse {
nsid: WireVec<u32, 1024>,
}
impl Encode<4096> for AdminIdentifyActiveNamespaceIdListResponse {}
impl AdminIdentifyActiveNamespaceIdListResponse {
fn new() -> Self {
Self {
nsid: WireVec::new(),
}
}
}
#[derive(Clone, Copy, Debug, DekuRead, DekuWrite)]
#[deku(id_type = "u8", endian = "endian", ctx = "endian: Endian")]
#[repr(u8)]
enum NamespaceIdentifierType {
Reserved = 0,
#[deku(id = 1)]
Ieuid(u8, u16, [u8; 8]),
#[deku(id = 2)]
Nguid(u8, u16, [u8; 16]),
#[deku(id = 3)]
Nuuid(u8, u16, WireUuid),
#[deku(id = 4)]
Csi(u8, u16, crate::nvme::CommandSetIdentifier),
}
impl From<crate::NamespaceIdentifierType> for NamespaceIdentifierType {
fn from(value: crate::NamespaceIdentifierType) -> Self {
match value {
crate::NamespaceIdentifierType::Ieuid(v) => Self::Ieuid(v.len() as u8, 0, v),
crate::NamespaceIdentifierType::Nguid(v) => Self::Nguid(v.len() as u8, 0, v),
crate::NamespaceIdentifierType::Nuuid(uuid) => Self::Nuuid(16, 0, WireUuid::new(uuid)),
crate::NamespaceIdentifierType::Csi(v) => Self::Csi(1, 0, v),
}
}
}
#[derive(Debug)]
#[deku_derive(DekuRead, DekuWrite)]
#[deku(endian = "little")]
struct AdminIdentifyNamespaceIdentificationDescriptorListResponse {
nids: WireVec<NamespaceIdentifierType, { crate::MAX_NIDTS }>,
}
impl Encode<4096> for AdminIdentifyNamespaceIdentificationDescriptorListResponse {}
#[derive(Debug, DekuRead, DekuWrite)]
#[deku(endian = "little")]
struct AdminIdentifyAllocatedNamespaceIdListResponse {
nsid: WireVec<u32, 1024>,
}
impl Encode<4096> for AdminIdentifyAllocatedNamespaceIdListResponse {}
#[derive(Debug, DekuWrite)]
#[deku(endian = "little")]
struct ControllerListResponse {
#[deku(update = "self.ids.len()")]
numids: u16,
#[deku(count = "numids")]
ids: WireVec<u16, 2047>,
}
impl Encode<4096> for ControllerListResponse {}
impl ControllerListResponse {
fn new() -> Self {
Self {
numids: 0,
ids: WireVec::new(),
}
}
}
#[derive(Debug, DekuRead, Eq, PartialEq)]
#[deku(ctx = "endian: Endian", endian = "endian", id_type = "u8")]
#[repr(u8)]
enum AdminNamespaceAttachmentSelect {
ControllerAttach = 0x00,
ControllerDetach = 0x01,
}
#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
#[deku(ctx = "endian: Endian, sel: u8", endian = "endian", id = "sel")]
#[repr(u8)]
enum AdminNamespaceManagementSelect {
#[deku(id = 0x00)]
Create(NvmNamespaceManagementCreate),
Delete = 0x01,
}
#[derive(Debug, DekuRead, DekuWrite, Eq, PartialEq)]
#[deku(ctx = "endian: Endian", endian = "endian")]
struct NvmNamespaceManagementCreate {
nsze: u64,
ncap: u64,
#[deku(seek_from_current = "10")]
flbas: u8,
#[deku(seek_from_current = "2")]
dps: u8,
nmic: u8,
#[deku(seek_from_current = "61")]
anagrpid: u32,
#[deku(seek_from_current = "4")]
nvmsetid: u16,
endgid: u16,
#[deku(seek_from_current = "280")]
#[deku(pad_bytes_after = "3704")]
lbstm: u64,
}
#[derive(Clone, Copy, Debug, Default, DekuRead, DekuWrite, Eq, PartialEq)]
#[deku(
bits = "bits.0",
ctx = "endian: Endian, bits: BitSize",
endian = "endian",
id_type = "u8"
)]
#[repr(u8)]
enum SanitizeAction {
#[default]
Reserved = 0x00,
ExitFailureMode = 0x01,
StartBlockErase = 0x02,
StartOverwrite = 0x03,
StartCryptoErase = 0x04,
ExitMediaVerificationState = 0x05,
}
impl TryFrom<u32> for SanitizeAction {
type Error = ();
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0x00 => Ok(Self::Reserved),
0x01 => Ok(Self::ExitFailureMode),
0x02 => Ok(Self::StartBlockErase),
0x03 => Ok(Self::StartOverwrite),
0x04 => Ok(Self::StartCryptoErase),
0x05 => Ok(Self::ExitMediaVerificationState),
_ => Err(()),
}
}
}
#[derive(Clone, Copy, Debug, Default, DekuRead, DekuWrite, Eq, PartialEq)]
#[deku(ctx = "endian: Endian", endian = "endian")]
pub struct AdminSanitizeConfiguration {
#[deku(bits = "4")]
owpass: u8,
#[deku(bits = "1")]
ause: bool,
#[deku(bits = "3")]
sanact: SanitizeAction,
#[deku(bits = "1", pad_bits_before = "5")]
emvs: bool,
#[deku(bits = "1")]
ndas: bool,
#[deku(bits = "1", pad_bytes_after = "2")]
oipbp: bool,
}
#[derive(Debug, DekuRead, DekuWrite)]
#[deku(ctx = "endian: Endian", endian = "endian", id_type = "u8")]
#[repr(u8)]
pub enum FeatureIdentifiers {
KeepAliveTimer = 0x0f,
}