/*
* Nvme.c -- UEFI SMM NVMe Bus Driver
* Source: FlashDriverSmm.efi (HR650X BIOS, AmiModulePkg)
* Image: PE32+ x64, entry 0x3D4, image size 0x9880
* Source path: e:\hs\AmiModulePkg\Nvme\NvmeBus.c
*
* This driver manages NVMe SSD detection, configuration, I/O queue setup,
* BlockSID security, and legacy device installation over PCI(e) NVMe
* controllers.
*
* Architecture:
* ModuleEntryPoint -> NvmeDriverEntry (UEFI driver init)
* -> NvmeMainEntry (driver binding installation)
* Driver Binding Start -> NvmeDetectAndStart -> NvmeInitController
* -> NvmeInstallBlockIo -> NvmeInstallProtocols
*
* Source path strings from debug info confirm file NvmeBus.c.
*/
#include "Nvme.h"
//
// ========================================================================
// Globals - populated at module entry
// ========================================================================
//
extern EFI_HANDLE gImageHandle;
EFI_SYSTEM_TABLE *gST;
EFI_BOOT_SERVICES *gBS;
EFI_RUNTIME_SERVICES *gRT;
UINT64 gNvmeControllerBuffer; // qword_9040 - 4920 bytes
UINT64 gNvmeIdentifyBuffer; // qword_9020 - 4096 bytes
UINT64 gNvmeFeaturesBuffer; // qword_9018 - 48 bytes
UINT64 gNvmeProtocolBuffer; // qword_9038 - protocol table
EFI_HANDLE gNvmeImageHandle; // ImageHandle_0 @ 0x8FA0
UINT8 gNvmeLegacySmmIndex; // byte_8FC0
UINT8 gNvmeLegacyNsNext; // byte_8FD8 - next free NS slot
EFI_HANDLE gNvmeChildHandle; // @ 0x8FF8
EFI_HANDLE gNvmeBlockIoHandle; // gBS->InstallProtocolInterface handle @ 0x9008
EFI_HANDLE gNvmePassthruHandle; // @ 0x9010
EFI_HANDLE gNvmeDevicePathHandle; // @ 0x9028
VOID *gNvmeSmmCommProtocol; // @ 0x9030
EFI_HANDLE gNvmeDriverBindingHandle;// @ 0x9000
VOID *gNvmeSmmIoProtocol; // @ 0x9068 - located via LocateProtocol
UINT8 gNvmeFeaturePresent; // byte_8FC0 bit 0
//
// Protocol GUIDs stored in .rdata / known from table at 0x8D80
//
EFI_GUID gEfiSmmIoProtocolGuid = {0x8E,0xC6,0x9D,0xBD,0x4C,0x9D,0x94,0xDB,0x65,0xAC,0xC5,0xC3,0x32,0x28,0xB8,0xB2};
//
// ========================================================================
// 0x3D4 ModuleEntryPoint (NVMe Driver Entry Point)
// ========================================================================
//
EFI_STATUS
EFIAPI
NvmeDriverEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
gImageHandle = ImageHandle;
if (ImageHandle == NULL) {
DEBUG ((EFI_D_ERROR, "gImageHandle != NULL\n"));
}
gST = SystemTable;
if (SystemTable == NULL) {
DEBUG ((EFI_D_ERROR, "gST != NULL\n"));
}
gBS = SystemTable->BootServices;
if (gBS == NULL) {
DEBUG ((EFI_D_ERROR, "gBS != NULL\n"));
}
gRT = SystemTable->RuntimeServices;
if (gRT == NULL) {
DEBUG ((EFI_D_ERROR, "gRT != NULL\n"));
}
//
// Initialize SMM I/O protocol and debug infrastructure
//
NvmeInitSmmIo ();
gNvmeSmmIoProtocol = (VOID*)(*(UINT64*)(NvmeLocateProtocol (&gEfiSmmIoProtocolGuid) + 32))(5);
//
// Enable debug assertions and output for the driver
//
if ((INT8)*((UINT8*)NvmeGetCmosAddress (0xF9C4)) >= 0) {
NvmeDebugPrint ((UINT64)NvmeGetCmosAddress (0xF9C0), L"Enabled");
*((UINT8*)NvmeGetCmosAddress (0xF9C4)) |= 0x80;
}
//
// Delay / stall for port initialization
//
UINT16 StallStatus = IoRead16 (0x80);
UINT64 EndTime = (NvmeGetTsc () & 0xFFFFFF) + 357;
while ((((UINT32)EndTime + 357 - (UINT32)NvmeGetTsc ()) & 0x800000) == 0) {
CpuPause ();
}
if ((StallStatus & 0x200) != 0) {
return NvmeDisableInterrupts ();
} else {
return NvmeEnableInterrupts ();
}
}
//
// ========================================================================
// 0x544 NvmeMainEntry -- Install driver binding and protocol interfaces
// ========================================================================
//
EFI_STATUS
NvmeMainEntry (
IN EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
UINTN Size;
//
// GUID for MemoryOverwriteRequestControl variable (MorControl)
//
EFI_GUID MorGuid = {0xE20939BE, 0x7AD8, 0x4D6F, {0xA0, 0xE1, 0x3A, 0xF5, 0x6E, 0xE0, 0xE1, 0xA4}};
//
// Allocate 4920 bytes for NVME_CONTROLLER
//
Status = gBS->AllocatePool (EfiBootServicesData, 4920, &gNvmeControllerBuffer);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Allocate 4096-byte identify buffer
//
Status = gBS->AllocatePool (EfiBootServicesData, 4096, &gNvmeIdentifyBuffer);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Allocate 48-byte features buffer
//
Status = gBS->AllocatePool (EfiBootServicesData, 48, &gNvmeFeaturesBuffer);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Allocate 0-sized protocol buffer (just handle table)
//
Status = gBS->AllocatePool (EfiBootServicesData, 0, &gNvmeProtocolBuffer);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Install timer callback for SMI watchdog (sub_494C)
//
Status = gBS->SetTimer (
gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NvmeTimerCallback1, NULL),
TimerPeriodic,
80000 // 8 * 10ms?
);
ASSERT_EFI_ERROR (Status);
//
// Open the driver binding protocol on our image handles
// (open protocol with BY_DRIVER)
//
Status = gBS->OpenProtocol (
&gEfiNvmeProtocolTable,
ImageHandle_0,
&gNvmeProtocolBuffer
);
ASSERT_EFI_ERROR (Status);
//
// Install second timer callback (sub_4A40)
//
Status = gBS->SetTimer (
gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NvmeTimerCallback2, NULL),
TimerPeriodic,
80000
);
ASSERT_EFI_ERROR (Status);
//
// Open protocol on second table (unk_8E60)
//
Status = gBS->OpenProtocol (
&gEfiNvmeProtocolTable2,
ImageHandle_0,
&gNvmeProtocolBuffer
);
ASSERT_EFI_ERROR (Status);
//
// Install the component name / driver family protocol
//
gNvmeBlockIoHandle = NULL;
Status = gBS->InstallMultipleProtocolInterfaces (
&gNvmeBlockIoHandle,
&gEfiNvmeComponentNameProtocol,
gNvmeComponentName,
&gEfiNvmeComponentName2Protocol,
gNvmeComponentName2,
NULL
);
ASSERT_EFI_ERROR (Status);
//
// Register the legacy SMM NVMe handler
//
Status = gBS->RegisterProtocolNotify (
&gEfiNvmeLegacyProtocol,
TPL_CALLBACK,
NvmeLegacySmmHandler,
&gNvmeChildHandle
);
ASSERT_EFI_ERROR (Status);
//
// Set MemoryOverwriteRequestControl variable
//
UINT16 MorData = 1;
gRT->SetVariable (
L"MemoryOverwriteRequestControl",
&MorGuid,
0,
sizeof (MorData),
&MorData
);
return Status;
}
//
// ========================================================================
// 0x494C NvmeTimerCallback1 -- First periodic timer (8 sec)
// Sends SMI/SW SMI to update controller context
// ========================================================================
//
EFI_STATUS
EFIAPI
NvmeTimerCallback1 (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
UINT64 SwSmiData = 0x494D535753LL; // "SMI SW" signature
UINT64 BufferSize = 32;
VOID *SmmCommunicationProtocol;
//
// Clear and set up the SW SMI data buffer (gNvmeControllerBuffer)
//
gBS->SetMem ((VOID*)gNvmeControllerBuffer, 32, 0);
//
// Locate the SMM communication protocol and trigger SMI
//
Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid, NULL, &SmmCommunicationProtocol);
if (!EFI_ERROR (Status)) {
Status = gBS->LocateProtocol (&gEfiSmmSwapProtocolGuid, NULL, &gNvmeSmmCommProtocol);
if (!EFI_ERROR (Status)) {
//
// Prepare communication buffer
//
gBS->CopyMem ((VOID*)gNvmeControllerBuffer, &gEfiNvmeProtocolBufferGuid, 16);
*(UINT64*)(gNvmeControllerBuffer + 16) = 8;
gBS->CopyMem ((VOID*)(gNvmeControllerBuffer + 24), &SwSmiData, 8);
//
// Send the communication
//
((EFI_SMM_COMMUNICATION_PROTOCOL*)gNvmeSmmCommProtocol)->Communicate (
gNvmeSmmCommProtocol,
(VOID*)gNvmeControllerBuffer,
&BufferSize
);
}
}
//
// Signal the timer event completion
//
gBS->SignalEvent (Event);
return EFI_SUCCESS;
}
//
// ========================================================================
// 0x4A40 NvmeTimerCallback2 -- Second periodic timer (8 sec)
// Iterates all NVME partitions and records legacy device info
// ========================================================================
//
EFI_STATUS
EFIAPI
NvmeTimerCallback2 (
IN EFI_EVENT Event,
IN VOID *Context
)
{
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
EFI_STATUS Status;
UINTN Index;
UINT64 NvmeControllerData;
NVME_CONTROLLER *Controller;
//
// Get list of all handles with EFI_NVM_EXPRESS_PASS_THRU protocol
//
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiNvmeExpressPassThruProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status) || HandleCount == 0) {
return Status;
}
//
// Iterate up to 32 handles, for each
//
for (Index = 0; Index < HandleCount && Index < 32; Index++) {
Status = gBS->OpenProtocol (
HandleBuffer[Index],
&gEfiNvmeExpressPassThruProtocolGuid,
&NvmeControllerData,
gImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
continue;
}
//
// Prepare the name string for "AMI NVMe BUS Driver" component
//
gBS->SetMem ((VOID*)(gNvmeControllerBuffer + 24), 437, 0);
*(UINT16*)(gNvmeControllerBuffer + 32) = (UINT16)Index;
*(UINT64*)(gNvmeControllerBuffer + 24) = 0x4D4D534F54455844LL; // "DOTEXOSMM"
//
// Copy the NVME_CONTROLLER data (424 bytes @ 0x8D80 area)
//
gBS->CopyMem (
(VOID*)(gNvmeControllerBuffer + 37),
(VOID*)(NvmeControllerData),
424
);
//
// Copy the controller's namespace bitfield into identify buffer
//
Controller = (NVME_CONTROLLER*)NvmeControllerData;
gBS->CopyMem (
(VOID*)gNvmeIdentifyBuffer,
(VOID*)(Controller->ControllerData + 360),
4096
);
//
// Enumerate namespace list
//
LIST_ENTRY *NamespaceList = &Controller->NamespaceList;
LIST_ENTRY *Node = NamespaceList->ForwardLink;
while (Node != NamespaceList) {
NVME_NAMESPACE *Ns = CR (Node, NVME_NAMESPACE, ...);
if (Ns->NsidExported) {
//
// Identify namespace data via protocol
//
NvmeIdentifyNamespace (Controller, Ns);
}
//
// Copy namespace data into the scratch buffer
//
*(UINT16*)(gNvmeControllerBuffer + 34) = Ns->BlockSize;
gBS->CopyMem (
(VOID*)(gNvmeControllerBuffer + 37),
(VOID*)Ns,
NVME_NAMESPACE_ENTRY_SIZE
);
//
// Copy media info from identify
//
gBS->CopyMem (
(VOID*)gNvmeFeaturesBuffer,
(VOID*)(Ns + 48),
48
);
//
// Mark if last node
//
if (Node->ForwardLink == NamespaceList) {
*(UINT8*)(gNvmeControllerBuffer + 36) = 1;
}
NvmeLegacySmmProtocolInstall (437);
Node = Node->ForwardLink;
}
gBS->FreePool (HandleBuffer);
}
return gBS->SignalEvent (Event);
}
//
// ========================================================================
// 0xA30 NvmeDetectAndStart -- Main NVMe detection and configuration flow
// Called from DriverBinding.Start
// Args: a1 = This (driver binding), a2 = ControllerHandle, a3 = RemainingDevicePath
// ========================================================================
//
EFI_STATUS
NvmeDetectAndStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
NVME_CONTROLLER *Controller;
VOID *PciIoInterface;
VOID *DevicePathInterface;
EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;
Controller = NULL;
//
// Ensure device does not already have NvmePassThru installed
//
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiNvmExpressPassThruProtocolGuid,
&PciIoInterface,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (Status == EFI_ALREADY_STARTED) {
//
// Already has pass-thru protocol -- skip NVMe detection
//
goto SKIP_NVME;
}
if (Status != EFI_SUCCESS && Status != EFI_ALREADY_STARTED) {
//
// Open DevicePath protocol (required for NVMe)
//
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiDevicePathProtocolGuid,
&DevicePathInterface,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (Status != EFI_SUCCESS && Status != EFI_ALREADY_STARTED) {
return EFI_OUT_OF_RESOURCES;
}
//
// Open PCI_IO protocol
//
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiPciIoProtocolGuid,
&PciIoInterface,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (Status != EFI_SUCCESS && Status != EFI_ALREADY_STARTED) {
return EFI_OUT_OF_RESOURCES;
}
}
SKK_NVME:
NvmeDebugPrint (1, L"\nNVMe Driver Detection and Configuration starts\n");
//
// Detect the NVME device - open controller
//
if (Status != EFI_ALREADY_STARTED) {
//
// Initialize the controller structure
//
Status = NvmeInitController (ControllerHandle, This->DriverBindingHandle, &Controller);
if (Status == EFI_OUT_OF_RESOURCES) {
NvmeDebugPrint (2, L"\nNVMe Driver Detection and Configuration Ends with Status = EFI_ALREADY_STARTED\n");
}
if (Status == EFI_SUCCESS) {
//
// Allocate namespace data buffer (4096 bytes)
//
UINT64 NamespaceBuffer;
Status = gBS->AllocatePool (EfiBootServicesData, 4096, &NamespaceBuffer);
if (Status == EFI_SUCCESS) {
gBS->SetMem ((VOID*)NamespaceBuffer, 4096, 0);
Controller->NamespaceData = NamespaceBuffer;
//
// Set up IO queues
//
Status = NvmeCreateIoSubmissionQueue (Controller, NamespaceBuffer, 1, 0);
if (Status == EFI_SUCCESS) {
//
// Set up IO completion queues
//
NvmeSetQueueInterruptCoalescing (Controller);
//
// Set additional queue configuration
//
Status = gBS->AllocatePool (EfiBootServicesData, 0x8000, &Controller->IoCompletionQueue);
if (Status == EFI_SUCCESS) {
gBS->SetMem (Controller->IoCompletionQueue, 0x2000, 0);
Controller->QueueCount = Controller->IoCompletionQueue;
//
// Set max queue entries
//
if (Controller->Version < 0x10100) {
for (UINT32 i = 1; i < *(UINT32*)(Controller->NamespaceData + 516) + 1; i++) {
*(UINT32*)(Controller->QueueCount + 4 * i) = i;
}
}
Controller->QueueCountEnabled = 1;
UINT32 QueueSize = Controller->MaxQueueEntries;
if (QueueSize >= 256) QueueSize = 256;
//
// Create IO completion queue
//
Status = NvmeCreateIoCompletionQueue (Controller, Controller->AdminSubmissionQueue, 1, QueueSize);
if (Status == EFI_SUCCESS) {
//
// Create child protocol handle
//
VOID *ChildBuffer;
Status = gBS->AllocatePool (EfiBootServicesData, 56, &ChildBuffer);
if (Status == EFI_SUCCESS) {
Status = gBS->AllocatePool (EfiBootServicesData, 12, ChildBuffer);
if (Status == EFI_SUCCESS) {
EFI_NVM_EXPRESS_PASS_THRU *PassthruProtocol;
//
// Set up pass-through protocol instance
//
PassthruProtocol = (EFI_NVM_EXPRESS_PASS_THRU*)ChildBuffer;
PassthruProtocol->NamespaceCount = 9;
PassthruProtocol->NamespaceIdType = 4;
PassthruProtocol->Reserved = 0;
//
// Wire up protocol function pointers
//
PassthruProtocol->PassThru = NvmePassThru;
PassthruProtocol->GetNextNamespace = NvmeGetNextNamespace;
PassthruProtocol->GetNamespaceDevicePath = NvmeGetNamespaceDevicePath;
PassthruProtocol->BuildDevicePath = NvmeBuildDevicePath;
//
// Install the protocol
//
Status = gBS->InstallMultipleProtocolInterfaces (
&PassthruProtocol->ControllerHandle,
&gEfiNvmExpressPassThruProtocolGuid,
PassthruProtocol,
NULL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "!EFI_ERROR (Status)\n"));
}
}
}
//
// Initialize the namespace list (linked list head)
//
InitializeListHead (&Controller->NamespaceList);
//
// If RemainingDevicePath provided, process it
//
if (RemainingDevicePath != NULL) {
if (RemainingDevicePath->Type != MESSAGING_DEVICE_PATH ||
RemainingDevicePath->SubType != MSG_NVME_NAMESPACE_DP ||
DevicePathNodeLength (RemainingDevicePath) != 16) {
return EFI_INVALID_PARAMETER;
}
NvmeCreateIoEventBuffer (Controller, *(UINT32*)(RemainingDevicePath + 4));
}
//
// Process namespace list
//
while (TRUE) {
if (IsListEmpty (&Controller->NamespaceList)) {
break;
}
//
// Iterate to find all namespaces
//
LIST_ENTRY *FirstEntry = Controller->NamespaceList.ForwardLink;
if (FirstEntry->BackLink == &Controller->NamespaceList) {
// only one entry
break;
}
//
// Get the namespace node
//
NVME_NAMESPACE *EntryNs = (NVME_NAMESPACE*)((UINT8*)FirstEntry - OFFSET_OF_NS_ENTRY);
NvmeCreateDeviceNode (This, ControllerHandle, Controller);
if (FirstEntry == Controller->NamespaceList.ForwardLink) {
break;
}
}
}
}
}
}
//
// Try to locate and call external protocols for advanced features
//
if (gNvmeBlockIoHandle != NULL) {
((EFI_DRIVER_FAMILY_OVERRIDE_PROTOCOL*)gNvmeBlockIoHandle->)->GetVersion (Controller, 2);
if (*(UINT8*)(gNvmeChildHandle + 24)) {
((EFI_DRIVER_FAMILY_OVERRIDE_PROTOCOL*)gNvmeBlockIoHandle)-> (Controller, 2);
}
}
//
// Block I/O installation
//
if (gNvmeBlockIoHandle == NULL) {
gBS->LocateProtocol (&gEfiBlockIoProtocolGuid, NULL, &gNvmeBlockIoHandle);
}
//
// Try SMM communication protocol for BlockIoSmm
//
if (gNvmeSmmCommProtocol == NULL || gNvmeSmmCommProtocol != NULL) {
// fallback
}
if (gNvmeDevicePathHandle == NULL) {
gBS->LocateProtocol (&gEfiDevicePathProtocolGuid, NULL, &gNvmeDevicePathHandle);
}
//
// Install Block I/O and Disk I/O protocols
//
Status = NvmeInstallBlockIo (This, Controller);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "!EFI_ERROR (Status)\n"));
}
//
// Install driver diagnostic protocol
//
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiDriverDiagnosticsProtocolGuid,
&DevicePathNode,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND) {
VOID *DiagBuffer;
if (gBS->AllocatePool (EfiBootServicesData, 8, &DiagBuffer) == EFI_SUCCESS) {
*(UINT64*)DiagBuffer = (UINT64)NvmeDiagnostics;
Status = gBS->InstallMultipleProtocolInterfaces (
&ControllerHandle,
&gEfiDriverDiagnosticsProtocolGuid,
DiagBuffer,
NULL
);
}
}
}
//
// Register for end of DXE notification
//
if (gNvmeDriverBindingHandle == NULL) {
gBS->RegisterProtocolNotify (
&gEfiEndOfDxeProtocolGuid,
TPL_CALLBACK,
NvmeEndOfDxeHandler,
&gNvmeDriverBindingHandle
);
}
NvmeDebugPrint (64, L"\nNVMe Driver Detection and Configuration Ends with Status = EFI_SUCCESS\n");
return EFI_SUCCESS;
}
//
// If already started, just open pass-through on handle
//
{
//
// Set up I/O completion and SQs
//
NvmeSetQueueInterruptCoalescing (Controller);
NvmeSetFeatures (Controller, NVME_FEATURE_NUMBER_OF_QUEUES, 1, Controller->MaxNamespaces);
//
// Register with SMM if needed
//
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiSmmSwDispatch2ProtocolGuid,
&DevicePathInterface,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (Status == EFI_SUCCESS) {
gBS->UninstallMultipleProtocolInterfaces (
ControllerHandle,
&gEfiSmmSwDispatch2ProtocolGuid,
NULL
);
}
Status = gBS->InstallMultipleProtocolInterfaces (
&ControllerHandle,
&gEfiSmmSwDispatch2ProtocolGuid,
NULL,
NULL
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "!EFI_ERROR (Status)\n"));
}
}
//
// Install Block I/O and Disk I/O protocols
//
{
if (gNvmeBlockIoHandle == NULL) {
// fallback
}
Status = NvmeInstallBlockIo (This, Controller);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "!EFI_ERROR (Status)\n"));
}
}
NvmeDebugPrint (64, L"\nNVMe Driver Detection and Configuration Ends with Status = EFI_SUCCESS\n");
return EFI_SUCCESS;
}
//
// ========================================================================
// 0x1D34 NvmeInitController -- Initialize the NVME controller structure
// Parses PCI IO, identifies controller capabilities, allocates admin
// queues, and prepares the controller for operation.
// ========================================================================
//
EFI_STATUS
NvmeInitController (
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE DriverBindingHandle,
OUT NVME_CONTROLLER **Controller
)
{
EFI_PCI_IO_PROTOCOL *PciIo;
EFI_STATUS Status;
NVME_CONTROLLER *Ctrl;
*Controller = NULL;
//
// Allocate controller structure (424 bytes)
//
Status = gBS->AllocatePool (EfiBootServicesData, sizeof (NVME_CONTROLLER), (VOID**)&Ctrl);
if (EFI_ERROR (Status)) {
return Status;
}
gBS->SetMem (Ctrl, sizeof (NVME_CONTROLLER), 0);
*Controller = Ctrl;
//
// Allocate PCI IO structure (24 bytes)
//
Status = gBS->AllocatePool (EfiBootServicesData, 24, (VOID**)&Ctrl->PciIo);
if (EFI_ERROR (Status)) {
return Status;
}
gBS->SetMem (Ctrl->PciIo, 24, 0);
Ctrl->ControllerHandle = ControllerHandle;
//
// Initialize namespace linked list
//
InitializeListHead (&Ctrl->NamespaceList);
//
// Open PCI_IO protocol to access the NVMe BAR
//
{
EFI_PCI_IO_PROTOCOL *TempPciIo;
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiPciIoProtocolGuid,
(VOID**)&TempPciIo,
DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
Ctrl->PciIo = TempPciIo;
//
// Read PCI config space for MMIO base address (BAR 0)
//
Status = TempPciIo->Pci.Read (TempPciIo, EfiPciIoWidthUint16, 0x24, 1, &Ctrl->MaxQueueEntries);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Read capabilities (BAR size)
//
UINT32 BarValue;
Status = TempPciIo->Pci.Read (TempPciIo, EfiPciIoWidthUint32, 0x10, 1, &BarValue);
if (EFI_ERROR (Status)) {
return Status;
}
Ctrl->MmioPhysBase = BarValue & 0xFFFFFFF8;
Ctrl->MmioPhysBase |= ((UINT64)(*(UINT32*)((UINT8*)&BarValue + 4))) << 32;
}
//
// Parse PCI IO BAR 0 to get MMIO base via GetBarAttributes
//
{
UINT64 BarLength;
UINTN AddressSpaceCount;
Ctrl->PciIo->GetBarAttributes (
Ctrl->PciIo,
0,
&AddressSpaceCount,
&Ctrl->MaxQueueEntries
);
//
// Read the NVMe controller registers from MMIO
//
UINT8 IdentifyBuffer[64];
Status = TempPciIo->Mem.Read (
TempPciIo,
EfiPciIoWidthUint8,
0, // BAR0
0, // offset to NVMe regs
64,
IdentifyBuffer
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Decode NVMe capability register (CAP)
//
Ctrl->Signature = *(UINT32*)IdentifyBuffer; // "RNTC"
Ctrl->MaxQueueEntries = *(UINT32*)(IdentifyBuffer + 8) // CAP.MQES
& 0xFFFFFFF8;
Ctrl->PageSize = (*(UINT32*)(IdentifyBuffer + 8) >> 32); // CAP high
Ctrl->PciIo = Ctrl->PciIo; // re-assign
Ctrl->MaxQueueEntries = NvmeGetCapability (Ctrl, 4);
Ctrl->MaxQueueEntries <<= 32;
Ctrl->MaxQueueEntries |= NvmeGetCapability (Ctrl, 0);
Ctrl->PciIo->Pci.Read (Ctrl->PciIo, EfiPciIoWidthUint8, 0, 64, IdentifyBuffer);
Ctrl->Version = *(UINT32*)IdentifyBuffer;
Ctrl->MaxQueueEntries = (UINT16)(Ctrl->MaxQueueEntries) + 1;
Ctrl->PageMin = BYTE2 (Ctrl->MaxQueueEntries) & 1;
Ctrl->PageMin |= (Ctrl->MaxQueueEntries >> 17) & 3;
Ctrl->PageMin |= (Ctrl->MaxQueueEntries >> 24);
Ctrl->PageMin |= (Ctrl->MaxQueueEntries >> 37) & 0xF;
Ctrl->PageSize = (Ctrl->MaxQueueEntries & 0x1000000000LL) != 0;
Ctrl->PageSize >>= 37;
}
//
// Get max page entries and set namespace capacity
//
Ctrl->MaxNamespaces = NvmeGetCapability (Ctrl, 8);
Ctrl->MaxNamespaces &= 0xFFFF;
//
// Check controller ready status
//
if ((Ctrl->PageSize & 1) == 0) {
return EFI_UNSUPPORTED;
}
//
// Clear CSTS.CFS if set
//
if ((NvmeGetCapability (Ctrl, 20) & 1) != 0) {
UINT32 csts = NvmeGetCapability (Ctrl, 20);
NvmeSetCapability (Ctrl, 20, csts & ~1);
}
//
// Wait for controller ready (CSTS.RDY)
//
{
UINT32 TimeoutMs = 500 * Ctrl->PageMin;
while ((NvmeGetCapability (Ctrl, 28) & 1) == 0) {
gBS->Stall (1000);
if (--TimeoutMs == 0) {
break;
}
}
if (TimeoutMs == 0) {
NvmeDebugPrint (0x80000000LL, L"Controller not ready : %X\n", NvmeGetCapability (Ctrl, 28));
return EFI_TIMEOUT;
}
}
//
// Configure the controller (CC register)
//
Ctrl->PageSize = Ctrl->MaxQueueEntries;
if (Ctrl->PageSize > 0x10000) {
Ctrl->PageSize = 0x10000;
}
UINT32 PageShift = 1;
if ((Ctrl->PageSize & 0xFFFFFFFE) > 2) {
do { ++PageShift; } while (Ctrl->PageSize >> PageShift > 1);
}
NvmeSetCapability (Ctrl, 20, ((PageShift - 12) | 0x8C00u) << 7);
//
// Allocate admin submission queue (ASQ) buffer
//
{
UINT32 NumQueues = Ctrl->MaxQueueEntries;
if (NumQueues >= 256) {
NumQueues = 256;
}
Ctrl->AdminQueueSize = NumQueues;
UINT64 AsqBufferSize = (UINT64)NumQueues << 6;
UINT64 CompletionBufferSize = AsqBufferSize + Ctrl->PageSize;
UINT32 NumPages = (UINT32)((CompletionBufferSize & 0xFFF) ? (CompletionBufferSize >> 12) + 1 : (CompletionBufferSize >> 12));
//
// Allocate physically contiguous pages for admin queues
//
Ctrl->PciIo->AllocateBuffer (
Ctrl->PciIo,
AllocateAnyPages,
EfiBootServicesData,
NumPages,
&Ctrl->AdminSubmissionQueuePhys,
2176
);
Ctrl->AdminSubmissionQueue = (VOID*)(Ctrl->AdminSubmissionQueuePhys & ~(Ctrl->PageSize - 1));
Ctrl->AdminQueueNumPages = NumPages;
Ctrl->AdminCompletionQueuePhys = Ctrl->AdminSubmissionQueuePhys;
//
// Map the buffer
//
Ctrl->PciIo->Map (
Ctrl->PciIo,
EfiPciIoOperationBusMasterCommonBuffer,
Ctrl->AdminSubmissionQueuePhys,
&AsqBufferSize,
&Ctrl->AdminQueueMap,
&Ctrl->AdminQueueNumPages
);
//
// Allocate second buffer for admin completion queue
//
Ctrl->PciIo->AllocateBuffer (
Ctrl->PciIo,
AllocateAnyPages,
EfiBootServicesData,
NumPages,
&Ctrl->IoCompletionQueuePhys,
2176
);
Ctrl->AdminCompletionQueueBuff = (VOID*)(Ctrl->IoCompletionQueuePhys & ~(Ctrl->PageSize - 1));
Ctrl->IoQueueNumEntries = AsqBufferSize;
Ctrl->PciIo->Map (
Ctrl->PciIo,
EfiPciIoOperationBusMasterCommonBuffer,
Ctrl->IoCompletionQueuePhys,
&AsqBufferSize,
&Ctrl->IoCompletionQueueMap,
&Ctrl->IoQueueNumPages
);
//
// Set admin queue sizes (ASQ and ACQ entries)
//
if (Ctrl->MaxQueueEntries < 0x100) {
Ctrl->AdminQueueHead = Ctrl->MaxQueueEntries;
Ctrl->AdminQueueTail = Ctrl->MaxQueueEntries;
} else {
Ctrl->AdminQueueHead = 256;
Ctrl->AdminQueueTail = 256;
}
//
// Zero the admin queues
//
gBS->SetMem (Ctrl->AdminSubmissionQueue, AsqBufferSize, 0);
gBS->SetMem (Ctrl->AdminCompletionQueueBuff, AsqBufferSize, 0);
//
// Set AQA (Admin Queue Attributes) register
//
NvmeSetCapability (Ctrl, 36,
Ctrl->AdminQueueHead - 65537 + (Ctrl->AdminQueueTail << 16));
NvmeSetCapability (Ctrl, 40, (UINT32)(Ctrl->AdminSubmissionQueuePhys));
NvmeSetCapability (Ctrl, 44, (UINT32)(Ctrl->AdminSubmissionQueuePhys >> 32));
NvmeSetCapability (Ctrl, 48, (UINT32)(Ctrl->AdminCompletionQueuePhys));
NvmeSetCapability (Ctrl, 52, (UINT32)(Ctrl->AdminCompletionQueuePhys >> 32));
//
// Enable the controller (CC.EN = 1)
//
Ctrl->Cc &= ~1;
Ctrl->Cc |= 1;
//
// Allocate scratch buffer for admin commands (72 bytes)
//
Status = gBS->AllocatePool (EfiBootServicesData, 72, &Ctrl->AdminCmdBuf);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Enable the controller with set features
//
{
UINT32 CcValue = NvmeGetCapability (Ctrl, 20);
NvmeSetCapability (Ctrl, 20, CcValue | 1);
//
// Wait for ready
//
Ctrl = (NVME_CONTROLLER*)Ctrl; // keep consistent
UINT32 WaitTime = 500 * Ctrl->PageMin;
while ((NvmeGetCapability (Ctrl, 28) & 1) == 0) {
gBS->Stall (1000);
if (--WaitTime == 0) {
break;
}
}
if (WaitTime == 0) {
NvmeDebugPrint (0x80000000LL, L"Controller not ready : %X\n", NvmeGetCapability (Ctrl, 28));
return EFI_TIMEOUT;
}
}
//
// Submit admin command to set features (number of queues)
//
Status = NvmeAdminSetFeatures (Ctrl, Ctrl->AdminCmdBuf);
if (Status == EFI_SUCCESS) {
//
// Install the pass-through protocol on the controller handle
//
gBS->InstallMultipleProtocolInterfaces (
&ControllerHandle,
&gEfiNvmExpressPassThruProtocolGuid,
Ctrl,
NULL
);
}
}
return Status;
}
//
// ========================================================================
// 0x75C0 NvmeSubmitAdminCommand -- Submit an admin command to NVMe
// Wraps the command into a submission queue entry, writes doorbell.
// ========================================================================
//
EFI_STATUS
NvmeSubmitAdminCommand (
IN NVME_CONTROLLER *Controller,
IN NVME_ADMIN_CMD *Cmd,
OUT UINT32 *CplResult OPTIONAL
)
{
UINT16 *QueueInfo;
CHAR8 CmdType;
UINT16 CmdId;
UINT64 CompletionBase;
UINT16 CompletionHead;
UINT16 CompletionIndex;
VOID *SubmissionEntry;
QueueInfo = (UINT16*)Controller->ControllerData;
//
// The admin queue is now busy
//
if (Controller->AdminBusy) {
return EFI_NOT_READY;
}
CmdType = Cmd->IsAdmin;
if (CmdType) {
CmdId = QueueInfo[0];
} else {
CmdId = QueueInfo[1];
}
if (CmdType) {
CompletionBase = Controller->AdminDoorbell;
} else {
CompletionBase = Controller->IoSubmissionQueue2Db;
}
//
// Get the current completion queue phase and index
//
if (CmdType) {
CompletionHead = QueueInfo[2];
} else {
CompletionHead = QueueInfo[7];
}
if (CompletionHead == 0) {
//
// Flip the phase tag
//
if (CmdType) {
QueueInfo[6] ^= 1;
} else {
QueueInfo[11] ^= 1;
}
}
//
// Get the submission entry
//
CompletionIndex = CompletionHead;
SubmissionEntry = (VOID*)(CompletionBase + 16 * CompletionIndex);
//
// Wait for a free slot in the submission queue
//
UINT32 TimeoutMs = 100 * Cmd->TimeoutMs;
while (TRUE) {
//
// Check if slot is available
//
if (((UINT16*)SubmissionEntry)[6] == CmdId &&
((UINT16*)SubmissionEntry)[5] == Cmd->QueueId) {
//
// Check phase tag
//
UINT8 PhaseTag = CmdType ? QueueInfo[6] : QueueInfo[11];
if ((((UINT16*)SubmissionEntry)[7] & 1) == (PhaseTag & 1)) {
break;
}
}
//
// Check for controller fatal error
//
if ((NvmeGetCapability (Controller, 28) & 2) != 0) {
if (!Controller->Status) {
NvmeDebugPrint (0x80000000LL, L"Nvme Fatal Error\n");
}
return EFI_DEVICE_ERROR;
}
NvmeMicrosecondDelay (35);
if (--TimeoutMs == 0) {
if (!Controller->Status) {
NvmeDebugPrint (0x80000000LL, L"Nvme TimeOut\n");
}
return EFI_TIMEOUT;
}
}
if (TimeoutMs == 0) {
return EFI_TIMEOUT;
}
//
// Set doorbell register
//
CompletionIndex = ((UINT16*)SubmissionEntry)[4];
if (CmdType) {
QueueInfo[4] = CompletionIndex;
QueueInfo[2] = CompletionIndex;
} else {
QueueInfo[9] = CompletionIndex;
QueueInfo[7] = CompletionIndex;
}
//
// Write the submission queue tail doorbell
//
NvmeMmioWrite (
Controller,
(2 * Cmd->QueueId + 1) * (4 << Controller->PageMin) + 4096,
CompletionIndex
);
//
// Copy completion result
//
if (CplResult != NULL) {
NvmeCopyMem (CplResult, SubmissionEntry, 64);
}
if ((*(UINT16*)((UINT8*)SubmissionEntry + 14) & 0x1FE) != 0 ||
(*(UINT16*)((UINT8*)SubmissionEntry + 14) & 0xE00) != 0) {
return EFI_WARN_BUFFER_TOO_SMALL;
}
//
// Advance the submission queue tail
//
if (CmdType) {
QueueInfo[0]++;
} else {
QueueInfo[1]++;
}
return EFI_SUCCESS;
}
//
// ========================================================================
// 0x5014 NvmePassThru -- EFI_NVM_EXPRESS_PASS_THRU.PassThru
// Main command dispatch for read/write/admin commands.
// ========================================================================
//
EFI_STATUS
EFIAPI
NvmePassThru (
IN EFI_NVM_EXPRESS_PASS_THRU *This,
IN UINT32 NamespaceId,
IN EFI_NVM_EXPRESS_PASS_THRU_COMMAND *Command,
IN BOOLEAN NvmeCmd,
IN OUT VOID *Buffer,
IN UINTN *TransferLength OPTIONAL
)
{
NVME_CONTROLLER *Controller = (NVME_CONTROLLER*)This->ControllerData;
EFI_STATUS Status;
//
// Determine if admin or I/O command
//
if (Command->IsAdmin) {
//
// Admin command -> submit via admin submission queue
//
Status = NvmeSubmitAdminCommand (Controller, Command, NULL);
} else {
//
// I/O command -> submit via I/O submission queue
//
switch (Command->Opcode) {
case NVME_IO_CMD_READ:
Status = NvmeReadSectors (Controller, Command->Lba, Command->TransferLength, Buffer);
break;
case NVME_IO_CMD_WRITE:
Status = NvmeWriteSectors (Controller, Command->Lba, Command->TransferLength, Buffer);
break;
case NVME_IO_CMD_FLUSH:
Status = NvmeFlushBlocks (Controller);
break;
default:
Status = EFI_UNSUPPORTED;
break;
}
}
return Status;
}
//
// ========================================================================
// 0x6FC4 NvmeReadSectors -- Read sectors from a namespace
// ========================================================================
//
EFI_STATUS
NvmeReadSectors (
IN NVME_CONTROLLER *Controller,
IN UINT64 StartLba,
IN UINTN SectorCount,
OUT VOID *Buffer
)
{
EFI_STATUS Status;
NVME_ADMIN_CMD Cmd;
UINT16 CmdId;
UINT64 Prp1, Prp2;
UINT64 DataPhys;
//
// Prepare the command
//
NvmeZeroMem (&Cmd, sizeof (Cmd));
Cmd.Opcode = NVME_IO_CMD_READ;
Cmd.Nsid = 0; // namespace will be assigned
Cmd.CmdId = Controller->IoSubmissionTail;
Cmd.Cdw10 = (UINT32)(StartLba & 0xFFFFFFFF);
Cmd.Cdw11 = (UINT32)((StartLba >> 32) & 0xFFFFFFFF);
Cmd.Cdw12 = (UINT32)(SectorCount - 1);
Cmd.TimeoutMs = 1000;
//
// Map the buffer for DMA
//
DataPhys = (UINT64)Buffer; // for simplicity, assume mapped
Cmd.MpHost = DataPhys;
if ((DataPhys & (Controller->PageSize - 1)) + SectorCount * 512 > Controller->PageSize) {
//
// PRP2 needed
//
Cmd.PrpHost = DataPhys;
Cmd.PrpHost2 = DataPhys + Controller->PageSize;
} else {
Cmd.PrpHost = DataPhys;
Cmd.PrpHost2 = 0;
}
Status = NvmeSubmitAdminCommand (Controller, &Cmd, NULL);
return Status;
}
//
// ========================================================================
// 0x5E28 NvmeCopyUnicodeToAscii -- Unicode string to ascii conversion
// ========================================================================
//
VOID
NvmeCopyUnicodeToAscii (
IN UINT16 *UnicodeStr,
OUT UINT8 *AsciiBuf
)
{
//
// Basic ASCII-safe unicode copy
//
while (*UnicodeStr) {
*AsciiBuf++ = (UINT8)(*UnicodeStr++ & 0xFF);
}
*AsciiBuf = 0;
}
//
// ========================================================================
// 0x7C88 NvmeMmioRead32 -- Read 32-bit MMIO register (via PciIo or memory)
// ========================================================================
//
UINT32
NvmeMmioRead32 (
IN NVME_CONTROLLER *Controller,
IN UINTN Offset
)
{
UINT32 Value = 0;
if (Controller == NULL) {
return 0;
}
if (Controller->MmioPhysBase == 0) {
//
// Fallback: read via PciIo.Mem.Read
//
if (Controller->PciIo != NULL) {
Controller->PciIo->Mem.Read (
Controller->PciIo,
EfiPciIoWidthUint32,
0,
Offset,
1,
&Value
);
} else {
Value = *(UINT32*)(Offset + Controller->MmioPhysBase);
}
} else {
if (Controller->PciIo != NULL) {
Controller->PciIo->Mem.Read (
Controller->PciIo,
EfiPciIoWidthUint32,
0,
Offset,
1,
&Value
);
} else {
Value = *(UINT32*)(Offset + Controller->MmioPhysBase);
}
}
return Value;
}
//
// ========================================================================
// 0x7CE4 NvmeMmioWrite32 -- Write 32-bit MMIO register
// ========================================================================
//
VOID
NvmeMmioWrite32 (
IN NVME_CONTROLLER *Controller,
IN UINTN Offset,
IN UINT32 Value
)
{
if (Controller == NULL) {
return;
}
if (Controller->PciIo != NULL && !Controller->MmioPhysBase) {
Controller->PciIo->Mem.Write (
Controller->PciIo,
EfiPciIoWidthUint32,
0,
Offset,
1,
&Value
);
} else {
*(UINT32*)(Offset + (UINT64)Controller->PciIo) = Value;
}
return;
}
//
// ========================================================================
// 0x7BE8 NvmeAdminSetFeatures -- Set controller features (admin cmd 0x09)
// Sets features and triggers admin queue doorbell (ASQ tail doorbell)
// ========================================================================
//
EFI_STATUS
NvmeAdminSetFeatures (
IN NVME_CONTROLLER *Controller,
IN VOID *CommandBuffer
)
{
NVME_ADMIN_CMD *Cmd = (NVME_ADMIN_CMD*)CommandBuffer;
if (Controller->AdminBusy == 1 && Controller->AdminDoorbell > 0) {
return EFI_SUCCESS; // already in progress
}
Controller->AdminCmdInProgress = 1;
gBS->SetMem (Cmd, sizeof (NVME_ADMIN_CMD), 0);
Cmd->Opcode = NVME_ADMIN_CMD_SET_FEATURES;
Cmd->CmdId = *((UINT16*)Controller->ControllerData);
Cmd->Cdw10 = NVME_FEATURE_NUMBER_OF_QUEUES;
Cmd->Cdw11 = 0x10001; // NCQR = 1, NSQR = 1 (one IO CQ, one IO SQ)
Cmd->TimeoutMs = 1000;
Cmd->IsAdmin = 1;
Cmd->QueueId = 0;
EFI_STATUS Status = NvmeSubmitAdminCommand (Controller, Cmd, NULL);
Controller->AdminCmdInProgress = 0;
return Status;
}
//
// ========================================================================
// 0x406C NvmeInstallBlockIo -- Install Block I/O protocol on namespaces
// Called after NVMe detection completes.
// ========================================================================
//
EFI_STATUS
NvmeInstallBlockIo (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN NVME_CONTROLLER *Controller
)
{
EFI_STATUS Status;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
UINTN Index;
UINT64 NvmeProtocolData;
EFI_HANDLE NewHandle;
//
// Get list of handles with NVME pass-thru protocol
//
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiNvmExpressPassThruProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status) || HandleCount == 0) {
return Status;
}
//
// Allocate the Block IO protocol structure (72 bytes)
//
VOID *BlockIoBuffer;
Status = gBS->AllocatePool (EfiBootServicesData, 72, &BlockIoBuffer);
if (EFI_ERROR (Status)) {
//
// Error condition, already reported
//
NvmeDebugPrint (0x80000000LL, L"\nASSERT_EFI_ERROR (Status = %r)\n", Status);
DEBUG ((EFI_D_ERROR, "!EFI_ERROR (Status)\n"));
return Status;
}
//
// Iterate over all handles to install block I/O for each namespace
//
for (Index = 0; Index < HandleCount; Index++) {
Status = gBS->OpenProtocol (
HandleBuffer[Index],
&gEfiNvmExpressPassThruProtocolGuid,
&NvmeProtocolData,
This->DriverBindingHandle,
HandleBuffer[Index],
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
NvmeDebugPrint (0x80000000LL, L"\nASSERT_EFI_ERROR (Status = %r)\n", Status);
continue;
}
//
// Parse the namespace from the controller
//
NVME_CONTROLLER *Ctrl = (NVME_CONTROLLER*)NvmeProtocolData;
UINT32 *NsInfo = (UINT32*)(Ctrl->ControllerData + 344);
UINT16 NvmeVersion = *NsInfo;
//
// Prep the admin command for identify namespace
//
NvmeZeroMem (BlockIoBuffer, 72);
//
// Send IDENTIFY (ACTIVE) command (Opcode=6, CNS=2)
//
*(UINT8*)BlockIoBuffer = 0;
*(UINT16*)(BlockIoBuffer + 2) = NvmeVersion;
*(UINT32*)(BlockIoBuffer + 40) = *(UINT16*)(Ctrl->ControllerData + 164);
*(UINT8*)(BlockIoBuffer + 64) = 1;
*(UINT16*)(BlockIoBuffer + 66) = 0;
*(UINT32*)(BlockIoBuffer + 68) = 1000;
Status = NvmeSubmitAdminCommand (Ctrl, BlockIoBuffer, NULL);
if (EFI_ERROR (Status)) {
continue;
}
//
// Send IDENTIFY (NS, CNS=0) to get namespace details
//
NvmeZeroMem (BlockIoBuffer, 72);
*(UINT8*)BlockIoBuffer = 4;
*(UINT16*)(BlockIoBuffer + 2) = NvmeVersion;
*(UINT32*)(BlockIoBuffer + 40) = *(UINT16*)(Ctrl->ControllerData + 164);
*(UINT8*)(BlockIoBuffer + 64) = 1;
*(UINT16*)(BlockIoBuffer + 66) = 0;
*(UINT32*)(BlockIoBuffer + 68) = 1000;
Status = NvmeSubmitAdminCommand (Ctrl, BlockIoBuffer, NULL);
if (EFI_ERROR (Status)) {
continue;
}
//
// Enable asynchronous event notification
//
{
UINT32 Aen = (UINT32)NvmeMmioRead32 (Ctrl, 20);
NvmeMmioWrite32 (Ctrl, 20, Aen | 0x4000);
}
//
// Determine the controller timeout for completion
//
{
UINT64 NsList = (UINT64)Ctrl->NamespaceData;
UINT32 TimeoutMs;
if (NsList != 0) {
UINT32 Capability = *(UINT32*)(NsList + 88);
TimeoutMs = Capability / 1000;
UINT32 Remainder = Capability % 1000;
if (TimeoutMs == 0) {
TimeoutMs = 500 * Ctrl->PageMin;
}
if (Remainder != 0) {
TimeoutMs++;
}
} else {
TimeoutMs = 500 * Ctrl->PageMin;
}
//
// Wait for command completion (CSTS phase bit)
//
while (((NvmeMmioRead32 (Ctrl, 28) & 0xC) != 8)) {
gBS->Stall (1000);
if (--TimeoutMs == 0) {
break;
}
}
if (TimeoutMs == 0) {
DEBUG ((EFI_D_ERROR, "((BOOLEAN)(0==1))\n"));
}
}
}
gBS->FreePool (BlockIoBuffer);
gBS->FreePool (HandleBuffer);
return EFI_SUCCESS;
}
//
// ========================================================================
// 0x77F0 NvmeSubmitIoCommand -- Submit an I/O command to the SQ
// Handles command slot allocation, doorbell registration.
// ========================================================================
//
//
// 0x79EC NvmeProcessCompletionQueue -- Process I/O completion queue
// Reaps completions, re-arms the CQ doorbell, re-submits if needed.
// =
//
//
// 0x2834 NvmeCreateDeviceNode -- Create a namespace device node
// Walks the namespace list, allocates BlockIO/DevicePath protocols.
// =
//
// 0x14A8 NvmeFreeDeviceMemory -- Free all allocated controller resources
// Uninstalls protocols, frees buffers, maps, and the controller itself.
// =
//
// 0x3120 NvmeSetQueueInterruptCoalescing -- Set IO queue interrupt
// Configures the controller's interrupt coalescing registers.
// =
//
// 0x244C NvmeCreateIoSubmissionQueue -- Create IO Submission Queue
// Allocates the queue, maps to physical address, sends admin command.
// =
//
// 0x6A1C NvmeCreateIoCompletionQueue -- Create IO Completion Queue
// Allocates and maps the completion queue, enables controller.
// =
//
// 0x3304 NvmeSecurityInit -- Security initialization (BlockSID)
// Issues SAT3 Freeze Lock and BlockSID commands over pass-through.
// =
//
// 0x4E80 NvmeLegacySmmHandler -- Handle legacy SMM event
// Triggered at end of DXE to install legacy device protocols.
// =
//
// 0x4C80 NvmeLegacyDeviceInstall -- Install NVMe legacy device protocol
// Sends AQ commands for namespace flush then configures legacy path.
// =
//
// 0x43F4 NvmeIdentifyNamespace -- Identify and record namespace info
// Performs IDENTIFY command on a namespace via SMM communication.
// =
//
// 0x467C NvmeLegacySmmProtocolInstall -- Install legacy SMM protocol
// Adds the legacy NVMe device protocol to the SMM communication.
// =