From aa6d3f056a76f7d8f46016b48d22b97adf8f18af Mon Sep 17 00:00:00 2001 From: Luqman Aden Date: Fri, 13 Feb 2026 10:07:53 -0800 Subject: [PATCH 1/3] NvmExpressDxe: Use Oxide-specific vendor feature to mark drive as read-only. --- .../Bus/Pci/NvmExpressDxe/NvmExpress.c | 72 +++++++++++++++++++ .../Bus/Pci/NvmExpressDxe/NvmExpressHci.c | 54 ++++++++++++++ .../Bus/Pci/NvmExpressDxe/NvmExpressHci.h | 8 +++ 3 files changed, 134 insertions(+) diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c index 62886d5c91f5..6e22a71c9efc 100644 --- a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c +++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c @@ -41,6 +41,76 @@ GLOBAL_REMOVE_IF_UNREFERENCED EFI_NVM_EXPRESS_PASS_THRU_MODE gEfiNvmExpressPassT 0x10100 }; +#define OXIDE_VENDOR_ID 0x1DE +/** + Oxide-specific feature retreived via the standard NVMe Get Features Command. + + Provides device-specific features beyond the standard NVMe spec returned as a + single Dword result in CDW10. + + Bit 0 [ReadOnly] - If set, the device will complete all writes with + STS_WRITE_READ_ONLY_RANGE. + Bits 31-1 - Reserved. + */ +#define OXIDE_VENDOR_FEATURE_DEVICE_FEATURES 0xF0 + +typedef union { + UINT32 Cdw10; + struct { + UINT32 ReadOnly:1; + UINT32 Reserved:31; + }; +} OXIDE_DEVICE_FEATURES; + +EFI_STATUS +DiscoverVendorDeviceFeatures ( + IN NVME_CONTROLLER_PRIVATE_DATA *Private, + IN NVME_DEVICE_PRIVATE_DATA *Device, + UINT32 NamespaceId + ) +{ + EFI_STATUS Status = EFI_SUCCESS; + + if (Private->ControllerData->Vid == OXIDE_VENDOR_ID) { + OXIDE_DEVICE_FEATURES DevFeats; + + DEBUG (( + EFI_D_INFO, + "%a: Matched Oxide NVMe device -- getting device features.\n", + __FUNCTION__ + )); + + Status = NvmeGetFeatures( + Private, + NamespaceId, + OXIDE_VENDOR_FEATURE_DEVICE_FEATURES, + &DevFeats.Cdw10 + ); + if (EFI_ERROR(Status)) { + DEBUG (( + EFI_D_INFO, + "%a: Failed to get Oxide Device Features.\n", + __FUNCTION__ + )); + goto Exit; + } + + DEBUG (( + EFI_D_INFO, + "%a: Oxide Device Features - 0x%x\n", + __FUNCTION__, + DevFeats.Cdw10 + )); + + if (DevFeats.ReadOnly) { + Device->Media.ReadOnly = 1; + } + } + +Exit: + return Status; +} + /** Check if the specified Nvm Express device namespace is active, and create child handles for them with BlockIo and DiskInfo protocol instances. @@ -303,6 +373,8 @@ EnumerateNvmeDevNamespace ( ); } + Status = DiscoverVendorDeviceFeatures(Private, Device, NamespaceId); + Exit: if(NamespaceData != NULL) { FreePool (NamespaceData); diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.c b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.c index 95f8b18bc4f8..1fb31e262ca1 100644 --- a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.c +++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.c @@ -511,6 +511,60 @@ NvmeIdentifyController ( return Status; } +/** + Get specified NVMe feature. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + @param NamespaceId The specified namespace identifier. + @param FeatureId The specified feature identifier. + @param Data A pointer to store the Dword attribute returned for the feature. + + @return EFI_SUCCESS Successfully get attribute for specified feature. + @return EFI_DEVICE_ERROR Fail to get attribute for specified feature. + +**/ +EFI_STATUS +NvmeGetFeatures ( + IN NVME_CONTROLLER_PRIVATE_DATA *Private, + IN UINT32 NamespaceId, + IN UINT8 FeatureId, + OUT UINT32 *Data + ) +{ + EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + EFI_NVM_EXPRESS_COMMAND Command; + EFI_NVM_EXPRESS_COMPLETION Completion; + EFI_STATUS Status; + + ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND)); + ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION)); + + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeCompletion = &Completion; + + Command.Cdw0.Opcode = NVME_ADMIN_GET_FEATURES_CMD; + Command.Nsid = NamespaceId; + CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT; + CommandPacket.QueueType = NVME_ADMIN_QUEUE; + + CommandPacket.NvmeCmd->Cdw10 = FeatureId; + CommandPacket.NvmeCmd->Flags = CDW10_VALID; + + Status = Private->Passthru.PassThru ( + &Private->Passthru, + NamespaceId, + &CommandPacket, + NULL + ); + + if (!EFI_ERROR(Status)) { + *Data = Completion.DW0; + } + + return Status; +} + /** Get specified identify namespace data. diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.h b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.h index 60b3770580c8..3fffebd102cf 100644 --- a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.h +++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.h @@ -48,6 +48,14 @@ NvmeIdentifyController ( IN VOID *Buffer ); +EFI_STATUS +NvmeGetFeatures ( + IN NVME_CONTROLLER_PRIVATE_DATA *Private, + IN UINT32 NamespaceId, + IN UINT8 FeatureId, + OUT UINT32 *DW0 + ); + /** Get specified identify namespace data. From 8c60ff4c271b0442b9a506a5cf9eab04cd5554aa Mon Sep 17 00:00:00 2001 From: Luqman Aden Date: Fri, 13 Feb 2026 13:06:09 -0800 Subject: [PATCH 2/3] Don't fail if we can't get feature like on older propolis. --- .../Bus/Pci/NvmExpressDxe/NvmExpress.c | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c index 6e22a71c9efc..b9c69c3a38ae 100644 --- a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c +++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c @@ -62,14 +62,14 @@ typedef union { }; } OXIDE_DEVICE_FEATURES; -EFI_STATUS +VOID DiscoverVendorDeviceFeatures ( IN NVME_CONTROLLER_PRIVATE_DATA *Private, IN NVME_DEVICE_PRIVATE_DATA *Device, UINT32 NamespaceId ) { - EFI_STATUS Status = EFI_SUCCESS; + EFI_STATUS Status; if (Private->ControllerData->Vid == OXIDE_VENDOR_ID) { OXIDE_DEVICE_FEATURES DevFeats; @@ -88,11 +88,12 @@ DiscoverVendorDeviceFeatures ( ); if (EFI_ERROR(Status)) { DEBUG (( - EFI_D_INFO, - "%a: Failed to get Oxide Device Features.\n", - __FUNCTION__ + EFI_D_WARN, + "%a: Failed to get Oxide Device Features (%r).\n", + __FUNCTION__, + Status )); - goto Exit; + return; } DEBUG (( @@ -103,12 +104,9 @@ DiscoverVendorDeviceFeatures ( )); if (DevFeats.ReadOnly) { - Device->Media.ReadOnly = 1; + Device->Media.ReadOnly = TRUE; } } - -Exit: - return Status; } /** @@ -373,7 +371,7 @@ EnumerateNvmeDevNamespace ( ); } - Status = DiscoverVendorDeviceFeatures(Private, Device, NamespaceId); + DiscoverVendorDeviceFeatures(Private, Device, NamespaceId); Exit: if(NamespaceData != NULL) { From b3dc94e7513117b4555eaef4da2cd5a9a6732087 Mon Sep 17 00:00:00 2001 From: Luqman Aden Date: Wed, 18 Feb 2026 14:28:23 -0800 Subject: [PATCH 3/3] Review: Make field names clearer and compare both vendor and dev id. --- .../Bus/Pci/NvmExpressDxe/NvmExpress.c | 65 ++++++++++++++++--- .../Bus/Pci/NvmExpressDxe/NvmExpressHci.c | 9 +-- .../Bus/Pci/NvmExpressDxe/NvmExpressHci.h | 13 ++++ 3 files changed, 75 insertions(+), 12 deletions(-) diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c index b9c69c3a38ae..30cc669c89bd 100644 --- a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c +++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c @@ -42,20 +42,22 @@ GLOBAL_REMOVE_IF_UNREFERENCED EFI_NVM_EXPRESS_PASS_THRU_MODE gEfiNvmExpressPassT }; #define OXIDE_VENDOR_ID 0x1DE +#define OXIDE_PROPOLIS_NVME_DEV_ID 0x0 + /** Oxide-specific feature retreived via the standard NVMe Get Features Command. Provides device-specific features beyond the standard NVMe spec returned as a - single Dword result in CDW10. + single Dword result in Dword 0 of the Completion Queue Entry. Bit 0 [ReadOnly] - If set, the device will complete all writes with STS_WRITE_READ_ONLY_RANGE. Bits 31-1 - Reserved. */ -#define OXIDE_VENDOR_FEATURE_DEVICE_FEATURES 0xF0 +#define OXIDE_VENDOR_FEATURE_PROPOLIS_DEVICE_FEATURES 0xF0 typedef union { - UINT32 Cdw10; + UINT32 DW0; struct { UINT32 ReadOnly:1; UINT32 Reserved:31; @@ -70,21 +72,68 @@ DiscoverVendorDeviceFeatures ( ) { EFI_STATUS Status; + UINT16 VendorId; + UINT16 DeviceId; + + // The VendorId is also present in the identify controller data but don't + // assume we've already issued an Identify command. + + Status = Private->PciIo->Pci.Read( + Private->PciIo, + EfiPciIoWidthUint16, + PCI_VENDOR_ID_OFFSET, + 1, + &VendorId + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a: Failed to read VendorId: %r\n", + __FUNCTION__, + Status + )); + return; + } + + Status = Private->PciIo->Pci.Read( + Private->PciIo, + EfiPciIoWidthUint16, + PCI_DEVICE_ID_OFFSET, + 1, + &DeviceId + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a: Failed to read DeviceId: %r\n", + __FUNCTION__, + Status + )); + return; + } + + DEBUG (( + EFI_D_INFO, + "%a: Checking 0x%x:0x%x\n", + __FUNCTION__, + VendorId, + DeviceId + )); - if (Private->ControllerData->Vid == OXIDE_VENDOR_ID) { + if (VendorId == OXIDE_VENDOR_ID && DeviceId == OXIDE_PROPOLIS_NVME_DEV_ID) { OXIDE_DEVICE_FEATURES DevFeats; DEBUG (( EFI_D_INFO, - "%a: Matched Oxide NVMe device -- getting device features.\n", + "%a: Matched Oxide Propolis NVMe device -- getting device features.\n", __FUNCTION__ )); Status = NvmeGetFeatures( Private, NamespaceId, - OXIDE_VENDOR_FEATURE_DEVICE_FEATURES, - &DevFeats.Cdw10 + OXIDE_VENDOR_FEATURE_PROPOLIS_DEVICE_FEATURES, + &DevFeats.DW0 ); if (EFI_ERROR(Status)) { DEBUG (( @@ -100,7 +149,7 @@ DiscoverVendorDeviceFeatures ( EFI_D_INFO, "%a: Oxide Device Features - 0x%x\n", __FUNCTION__, - DevFeats.Cdw10 + DevFeats.DW0 )); if (DevFeats.ReadOnly) { diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.c b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.c index 1fb31e262ca1..f66cc8b2ef61 100644 --- a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.c +++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.c @@ -515,9 +515,10 @@ NvmeIdentifyController ( Get specified NVMe feature. @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. - @param NamespaceId The specified namespace identifier. + @param NamespaceId The specified namespace identifier (or 0 if not relevant). @param FeatureId The specified feature identifier. - @param Data A pointer to store the Dword attribute returned for the feature. + @param DW0 A pointer to store the feature-specific attributes as returned in Dword 0 + of the Completion Queue Entry. @return EFI_SUCCESS Successfully get attribute for specified feature. @return EFI_DEVICE_ERROR Fail to get attribute for specified feature. @@ -528,7 +529,7 @@ NvmeGetFeatures ( IN NVME_CONTROLLER_PRIVATE_DATA *Private, IN UINT32 NamespaceId, IN UINT8 FeatureId, - OUT UINT32 *Data + OUT UINT32 *DW0 ) { EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; @@ -559,7 +560,7 @@ NvmeGetFeatures ( ); if (!EFI_ERROR(Status)) { - *Data = Completion.DW0; + *DW0 = Completion.DW0; } return Status; diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.h b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.h index 3fffebd102cf..f183f7d694c1 100644 --- a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.h +++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.h @@ -48,6 +48,19 @@ NvmeIdentifyController ( IN VOID *Buffer ); +/** + Get specified NVMe feature. + + @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + @param NamespaceId The specified namespace identifier (or 0 if not relevant). + @param FeatureId The specified feature identifier. + @param DW0 A pointer to store the feature-specific attributes as returned in Dword 0 + of the Completion Queue Entry. + + @return EFI_SUCCESS Successfully get attribute for specified feature. + @return EFI_DEVICE_ERROR Fail to get attribute for specified feature. + +**/ EFI_STATUS NvmeGetFeatures ( IN NVME_CONTROLLER_PRIVATE_DATA *Private,