Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpress.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -303,6 +420,8 @@ EnumerateNvmeDevNamespace (
);
}

DiscoverVendorDeviceFeatures(Private, Device, NamespaceId);

Exit:
if(NamespaceData != NULL) {
FreePool (NamespaceData);
Expand Down
55 changes: 55 additions & 0 deletions MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
21 changes: 21 additions & 0 deletions MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down