diff --git a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c index 62886d5c91f5..30cc669c89bd 100644 --- a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c +++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c @@ -41,6 +41,123 @@ GLOBAL_REMOVE_IF_UNREFERENCED EFI_NVM_EXPRESS_PASS_THRU_MODE gEfiNvmExpressPassT 0x10100 }; +#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 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_PROPOLIS_DEVICE_FEATURES 0xF0 + +typedef union { + UINT32 DW0; + struct { + UINT32 ReadOnly:1; + UINT32 Reserved:31; + }; +} OXIDE_DEVICE_FEATURES; + +VOID +DiscoverVendorDeviceFeatures ( + IN NVME_CONTROLLER_PRIVATE_DATA *Private, + IN NVME_DEVICE_PRIVATE_DATA *Device, + UINT32 NamespaceId + ) +{ + 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 (VendorId == OXIDE_VENDOR_ID && DeviceId == OXIDE_PROPOLIS_NVME_DEV_ID) { + OXIDE_DEVICE_FEATURES DevFeats; + + DEBUG (( + EFI_D_INFO, + "%a: Matched Oxide Propolis NVMe device -- getting device features.\n", + __FUNCTION__ + )); + + Status = NvmeGetFeatures( + Private, + NamespaceId, + OXIDE_VENDOR_FEATURE_PROPOLIS_DEVICE_FEATURES, + &DevFeats.DW0 + ); + if (EFI_ERROR(Status)) { + DEBUG (( + EFI_D_WARN, + "%a: Failed to get Oxide Device Features (%r).\n", + __FUNCTION__, + Status + )); + return; + } + + DEBUG (( + EFI_D_INFO, + "%a: Oxide Device Features - 0x%x\n", + __FUNCTION__, + DevFeats.DW0 + )); + + if (DevFeats.ReadOnly) { + Device->Media.ReadOnly = TRUE; + } + } +} + /** Check if the specified Nvm Express device namespace is active, and create child handles for them with BlockIo and DiskInfo protocol instances. @@ -303,6 +420,8 @@ EnumerateNvmeDevNamespace ( ); } + 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..f66cc8b2ef61 100644 --- a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.c +++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.c @@ -511,6 +511,61 @@ 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 (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, + IN UINT32 NamespaceId, + IN UINT8 FeatureId, + OUT UINT32 *DW0 + ) +{ + 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)) { + *DW0 = 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..f183f7d694c1 100644 --- a/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.h +++ b/MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.h @@ -48,6 +48,27 @@ 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, + IN UINT32 NamespaceId, + IN UINT8 FeatureId, + OUT UINT32 *DW0 + ); + /** Get specified identify namespace data.