/*
* 5038E34E-0774-47A0-A5EF-4B94AF1A43DA.c
* NVDIMM Serial Interface / REST Command Handler
* Lenovo HR650X BIOS DXE Driver
*
* This DXE driver provides a REST-style HTTP command interface for
* managing NVDIMM (Non-Volatile DIMM) modules via serial I/O. It
* responds to URL-encoded requests on the "NVMDIMM_IFRDATA" path and
* dispatches NVDIMM management commands (opcodes 0xE1-0xF9) to the
* underlying NVDIMM hardware.
*
* Disassembly analysis:
* 207 functions total (96 entrypoints + 111 subroutines)
* Image size: 0x25bc0 (154,560 bytes)
* Arch: x86-64 (PE32+)
* Module GUID: 5038E34E-0774-47A0-A5EF-4B94AF1A43DA
*/
#include "5038E34E-0774-47A0-A5EF-4B94AF1A43DA.h"
/*===========================================================================
* Global variables (in .data section)
*===========================================================================*/
EFI_SYSTEM_TABLE *gSystemTable = NULL; /* 0x24750 */
EFI_BOOT_SERVICES *gBootServices = NULL; /* 0x24758 */
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL; /* 0x24760 */
EFI_HANDLE gImageHandle = NULL; /* 0x24798 */
/* Protocol interface pointers (located at startup) */
VOID *gProtocol13710 = NULL; /* 0x24768 */
VOID *gProtocol136F0 = NULL; /* 0x24788 */
VOID *gProtocol136C0 = NULL; /* 0x24778 */
VOID *gProtocol20810 = NULL; /* 0x24770 */
VOID *gProtocol136E0 = NULL; /* 0x24780 */
/* Private driver context (allocated in NvmdimmInstallProtocols) */
NVDIMM_DRIVER_CONTEXT *gNvmdimmContext = NULL; /* 0x21D40 */
/*===========================================================================
* UART / COM1 initialization (sub_364 / NvmdimmInit)
*===========================================================================
*
* Initializes COM1 serial port (I/O 0x3F8-0x3FD) and locates the
* platform protocols required for NVDIMM access.
*
* UART registers:
* 0x3F8 - THR/RBR (DLL low when DLAB=1)
* 0x3F9 - IER (DLH high when DLAB=1)
* 0x3FA - IIR/FCR
* 0x3FB - LCR (Line Control Register)
* 0x3FC - MCR (Modem Control Register)
* 0x3FD - LSR (Line Status Register)
*
* Detect UART by LCR bit 0+1 == 3 (8N1 mode) and device_id == 1.
* If absent, do full init: wait Tx empty, set 115200 8N1, no FIFO.
*===========================================================================*/
EFI_STATUS
NvmdimmInit (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
UINT8 lcr;
UINT8 dlm, dll;
UINT16 device_id;
BOOLEAN uart_present;
lcr = __inbyte(0x3FB);
__outbyte(0x3FB, lcr | 0x80); /* DLAB=1 */
dlm = __inbyte(0x3F9);
dll = __inbyte(0x3F8);
device_id = (dlm << 8) | dll;
__outbyte(0x3FB, lcr & 0x7F); /* DLAB=0 */
uart_present = ((lcr & 0x3F) == 3) && (device_id == 1);
if (!uart_present) {
while ((__inbyte(0x3FD) & 0x60) != 0x60)
;
__outbyte(0x3FB, 0x80); /* DLAB=1 */
__outbyte(0x3F9, 0); /* DLM=0 */
__outbyte(0x3F8, 1); /* DLL=1 => 115200 baud */
__outbyte(0x3FB, 3); /* 8N1 */
__outbyte(0x3FA, 0); /* FCR=0 (FIFO disable) */
__outbyte(0x3FA, 1); /* FCR=1 (FIFO enable, 14B) */
__outbyte(0x3FC, 0); /* MCR=0 */
}
gSystemTable = SystemTable;
gBootServices = SystemTable->BootServices;
gRuntimeServices = SystemTable->RuntimeServices;
gBootServices->LocateProtocol(&gGuid_13710, 0, &gProtocol13710);
gBootServices->LocateProtocol(&gGuid_136F0, 0, &gProtocol136F0);
gBootServices->LocateProtocol(&gGuid_136C0, 0, &gProtocol136C0);
gBootServices->LocateProtocol(&gGuid_20810, 0, &gProtocol20810);
gBootServices->LocateProtocol(&gGuid_136E0, 0, &gProtocol136E0);
return EFI_SUCCESS;
}
/*===========================================================================
* Module Entry Point (0x31c)
*===========================================================================
*
* 1. NvmdimmInit - UART init + protocol locate
* 2. Install EFI_SERIAL_IO_PROTOCOL on ImageHandle
* 3. Set service stub at protocol+88
* 4. NvmdimmInstallProtocols - full protocol tree
*===========================================================================*/
EFI_STATUS
EFIAPI
NvmdimmEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
VOID *SerialIoInterface;
NvmdimmInit(ImageHandle, SystemTable);
Status = gBootServices->InstallProtocolInterface(
&ImageHandle, &gEfiSerialIoProtocolGuid,
EFI_NATIVE_INTERFACE, &SerialIoInterface);
if (!EFI_ERROR(Status))
*((VOID **)SerialIoInterface + 11) = &NvmdimmServiceStub;
return NvmdimmInstallProtocols(ImageHandle);
}
/*===========================================================================
* Protocol installation (sub_65C)
*===========================================================================
*
* Installs multiple protocol instances on ImageHandle and allocates
* the private driver context (40200 bytes).
*
* Protocols installed (via InstallMultipleProtocolInterfaces):
* &unk_1D0A8, &unk_207F0, &unk_20800, &unk_13690,
* &unk_13660, &unk_13630, &unk_207E0, &unk_13680,
* &xmmword_13640 (PCI Root Bridge IO)
*
* Context function pointers at offsets:
* +0x50 (80): NvmdimmUriDispatch
* +0x58 (88): NvmdimmCmdCommit
* +0x60 (96): NvmdimmCmdIo
*
* Protocol pointers resolved into context:
* +0x28 (40): gGuid_136F0 protocol
* +0x30 (48): gGuid_13710 protocol (Serial IO)
* +0x38 (56): gGuid_136C0 protocol
* +0x40 (64): gGuid_207E0 protocol
* +0x48 (72): gGuid_136D0 protocol (optional)
*===========================================================================*/
EFI_STATUS
NvmdimmInstallProtocols (
IN EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
EFI_HANDLE NewHandle;
VOID *Protocol1, *Protocol2, *Protocol3;
VOID *Protocol4, *Protocol5;
Protocol1 = Protocol2 = Protocol3 = Protocol4 = Protocol5 = NULL;
Status = gBootServices->LocateProtocol(&gGuid_1D0A8, 0, &NewHandle);
if (Status == EFI_NOT_FOUND) return EFI_UNSUPPORTED;
if (EFI_ERROR(Status)) goto Error;
Status = gBootServices->OpenProtocol(
&NewHandle, &gGuid_1D0A8, &gImageHandle,
ImageHandle, ImageHandle,
EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
if (EFI_ERROR(Status)) goto Error;
Status = gBootServices->InstallMultipleProtocolInterfaces(
&NewHandle,
&gGuid_207F0, &gFuncTable_1D040,
&gGuid_20800, &gFuncTable_20870,
&gGuid_13690, &gFuncTable_20858,
&gGuid_13660, &gFuncTable_20838,
&gGuid_13630, &gFuncTable_20828,
NULL);
if (EFI_ERROR(Status)) goto Error;
gNvmdimmContext = NvmdimmAllocatePool(40200);
if (!gNvmdimmContext) { Status = EFI_OUT_OF_RESOURCES; goto Error; }
gNvmdimmContext->Signature = NVDIMM_DRIVER_CONTEXT_SIGNATURE;
gNvmdimmContext->ImageHandle = NewHandle;
gNvmdimmContext->CmdHandlerSet = NvmdimmUriDispatch;
gNvmdimmContext->CmdHandlerCommit = NvmdimmCmdCommit;
gNvmdimmContext->CmdHandlerIo = NvmdimmCmdIo;
if (EFI_ERROR(gBootServices->LocateProtocol(
&gGuid_136F0, 0, &Protocol1))) goto Error;
gNvmdimmContext->ProtocolAt40 = Protocol1;
if (EFI_ERROR(gBootServices->LocateProtocol(
&gGuid_13710, 0, &Protocol2))) goto Error;
gNvmdimmContext->SerialIo = Protocol2;
if (EFI_ERROR(gBootServices->LocateProtocol(
&gGuid_207E0, 0, &Protocol3))) goto Error;
gNvmdimmContext->ProtocolAt50 = Protocol3;
if (EFI_ERROR(gBootServices->LocateProtocol(
&gGuid_136C0, 0, &Protocol4))) goto Error;
gNvmdimmContext->ProtocolAt48 = Protocol4;
if (EFI_ERROR(gBootServices->LocateProtocol(
&gGuid_136D0, 0, &Protocol5))) Protocol5 = NULL;
gNvmdimmContext->ProtocolAt58 = Protocol5;
Status = gBootServices->InstallMultipleProtocolInterfaces(
&NewHandle,
&gGuid_13680, &gProtoInstance_20820,
&gGuid_13640, &gFuncTable_20848,
NULL);
if (EFI_ERROR(Status)) goto Error;
Status = gBootServices->InstallProtocolInterface(
&ImageHandle, &gEfiSerialIoProtocolGuid,
EFI_NATIVE_INTERFACE, gNvmdimmContext);
if (!EFI_ERROR(Status))
*((VOID **)gNvmdimmContext + 11) = &NvmdimmServiceStub;
return Status;
Error:
if (gNvmdimmContext) { gBootServices->FreePool(gNvmdimmContext); gNvmdimmContext = NULL; }
return Status;
}
/*===========================================================================
* Protocol unload handler (sub_4BC)
*===========================================================================
*
* Unloads all protocol instances: close child handles, uninstall
* interfaces, free driver context.
*===========================================================================*/
EFI_STATUS
EFIAPI
NvmdimmUnload (
IN EFI_HANDLE ImageHandle
)
{
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
EFI_STATUS Status;
UINTN Index;
HandleBuffer = NULL; HandleCount = 0;
Status = gBootServices->LocateHandleBuffer(
ByProtocol, &gGuid_1D0A8, NULL, &HandleCount, &HandleBuffer);
if (!EFI_ERROR(Status) && HandleCount > 0) {
for (Index = 0; Index < HandleCount; Index++)
gBootServices->CloseProtocol(
HandleBuffer[Index], ImageHandle, NULL);
gBootServices->FreePool(HandleBuffer);
}
gBootServices->UninstallMultipleProtocolInterfaces(
ImageHandle,
&gGuid_207F0, &gFuncTable_1D040,
&gGuid_20800, &gFuncTable_20870,
&gGuid_13660, &gFuncTable_20838,
&gGuid_1D0A8, &gImageHandle, NULL);
gBootServices->UninstallMultipleProtocolInterfaces(
ImageHandle,
&gGuid_13690, &gFuncTable_20858,
&gGuid_13630, &gFuncTable_20828, NULL);
gBootServices->UninstallMultipleProtocolInterfaces(
ImageHandle, &gGuid_13680, &gProtoInstance_20820, NULL);
gBootServices->UninstallMultipleProtocolInterfaces(
ImageHandle, &gGuid_13640, &gFuncTable_20848, NULL);
if (gNvmdimmContext) {
gBootServices->FreePool(gNvmdimmContext);
gNvmdimmContext = NULL;
}
return Status;
}
/*===========================================================================
* REST URI dispatcher (sub_7BC0 / NvmdimmUriDispatch)
*===========================================================================
*
* Parses URL-encoded request URIs and dispatches to NVDIMM command
* handler.
*
* Supported URL formats:
* NVMDIMM_IFRDATA -> return all IFR data
* NVMDIMM_IFRDATA?OFFSET=0&WIDTH=%016lx -> specific range
* GUID=<g>&NAME=<n> -> access by GUID+name
* PATH=<path>... -> access by path+offset
*
* When no URI is provided (NULL), builds a default string using
* the NVMDIMM_IFRDATA path plus "&OFFSET=0&WIDTH=%016lx". The
* WIDTH constant 39856 (0x9BB0) matches the IFR data structure size.
*===========================================================================*/
EFI_STATUS
NvmdimmUriDispatch (
IN NVDIMM_DRIVER_CONTEXT *Context,
IN CHAR16 *UriString,
OUT CHAR16 **ResponseUri,
IN UINTN MaxResponseSize
)
{
CHAR16 *DispatchUri;
BOOLEAN AllocatedUri;
UINTN UriLen;
CHAR16 *NvmdimmStr;
CHAR16 *PathPtr;
AllocatedUri = FALSE;
if (!ResponseUri || !MaxResponseSize) return EFI_INVALID_PARAMETER;
*ResponseUri = UriString;
if (!UriString) {
NvmdimmStr = NvmdimmBuildString(Context, NVMDIMM_IFR_DATA_PATH, Context->SomeField);
if (!NvmdimmStr) return EFI_OUT_OF_RESOURCES;
UriLen = 0; while (NvmdimmStr[UriLen]) UriLen++;
DispatchUri = NvmdimmAllocatePool(2 * UriLen + 66);
if (!DispatchUri) { gBootServices->FreePool(NvmdimmStr); return EFI_OUT_OF_RESOURCES; }
AllocatedUri = TRUE;
UnicodeSPrint(DispatchUri, 2 * UriLen + 66,
L"%s&OFFSET=0&WIDTH=%016lx", NvmdimmStr, 39856);
gBootServices->FreePool(NvmdimmStr);
goto Dispatch;
}
DispatchUri = UriString;
if (NvmdimmStrStr(UriString, L"OFFSET")) goto Dispatch;
PathPtr = NvmdimmStrStr(UriString, L"PATH");
if (!PathPtr) return EFI_INVALID_PARAMETER;
if (!NvmdimmStrStr(PathPtr, L"&")) {
UriLen = 0; while (UriString[UriLen]) UriLen++;
DispatchUri = NvmdimmAllocatePool(2 * UriLen + 66);
if (!DispatchUri) return EFI_OUT_OF_RESOURCES;
AllocatedUri = TRUE;
UnicodeSPrint(DispatchUri, 2 * UriLen + 66,
L"%s&OFFSET=0&WIDTH=%016lx", UriString, 39856);
}
Dispatch:
{
EFI_STATUS S = Context->ProtocolAt40->NvmdimmIo(
Context, DispatchUri, &Context->SomeBuffer,
39856, MaxResponseSize, ResponseUri);
if (AllocatedUri && DispatchUri) gBootServices->FreePool(DispatchUri);
if (UriString && !NvmdimmStrStr(UriString, L"OFFSET")) {
CHAR16 *Ptr = UriString; while (*Ptr) Ptr++;
*ResponseUri = &UriString[Ptr - UriString];
} else { *ResponseUri = NULL; }
return S;
}
}
/*===========================================================================
* NVDIMM IFR data enumerator (sub_7E30 / NvmdimmCmdCommit)
*===========================================================================
*
* Enumerates NVDIMM IFR data after a SET/NEW operation. Refreshes
* cached NVDIMM configuration from hardware. Expects URI with
* "&OFFSET=" parameter and validates NVMDIMM_IFRDATA prefix match.
*
* On success (Flag40184 set), flushes all internal async buffers
* and resets the IFR cache.
*===========================================================================*/
EFI_STATUS
NvmdimmCmdCommit (
IN NVDIMM_DRIVER_CONTEXT *Context,
IN CHAR16 *UriString,
OUT CHAR16 **ResponseUri
)
{
CHAR16 *PrefixStr;
CHAR16 *OffsetParam;
CHAR16 *LocalUri;
CHAR16 *p1, *p2;
UINTN PrefixLen;
EFI_STATUS Status;
UINTN MaxResponseSize;
if (!UriString || !Context || !ResponseUri) return EFI_INVALID_PARAMETER;
*ResponseUri = UriString; MaxResponseSize = 39856;
PrefixStr = NvmdimmBuildString(Context, NVMDIMM_IFR_DATA_PATH, Context->SomeField);
OffsetParam = NvmdimmStrStr(UriString, L"&OFFSET=");
if (!OffsetParam) { gBootServices->FreePool(PrefixStr); return EFI_INVALID_PARAMETER; }
PrefixLen = (OffsetParam - UriString) / sizeof(CHAR16);
LocalUri = NvmdimmAllocatePool(2 * PrefixLen + 2);
if (!LocalUri) { gBootServices->FreePool(PrefixStr); return EFI_OUT_OF_RESOURCES; }
NvmdimmStrCpy(LocalUri, UriString, PrefixLen + 1);
p1 = LocalUri; p2 = PrefixStr;
while (*p1 && *p1 == *p2) { p1++; p2++; }
Status = (*p1 == *p2) ? EFI_SUCCESS : EFI_NOT_FOUND;
gBootServices->FreePool(LocalUri);
if (!EFI_ERROR(Status)) {
Status = Context->ProtocolAt40->NvmdimmCommit(
Context, UriString, &Context->ConfigData,
&MaxResponseSize, ResponseUri);
if (!EFI_ERROR(Status) && Context->Flag40184) {
NvmdimmFlushBuffers(Context);
Context->Buffer39992 = NULL; Context->Buffer40000 = NULL;
Context->Buffer40008 = NULL; Context->Buffer40016 = NULL;
Context->Buffer40104 = NULL; Context->Buffer40112 = NULL;
Context->Buffer40056 = NULL; Context->Buffer40064 = NULL;
NvmdimmResetCache(Context);
}
}
gBootServices->FreePool(PrefixStr);
return Status;
}
/*===========================================================================
* NVDIMM I/O command dispatch (sub_8370 / NvmdimmCmdIo)
*===========================================================================
*
* Validates command parameters and routes to NvmdimmDispatchCmd.
*
* IoWidth values:
* 3 - Set command type flag, dispatch as opcode
* 4 - Clear command type flag, dispatch
* 4096 - Validate opcode against flag, then dispatch
*===========================================================================*/
EFI_STATUS
NvmdimmCmdIo (
IN NVDIMM_DRIVER_CONTEXT *Context,
IN UINTN IoWidth,
IN UINT16 Data,
IN UINTN Count,
IN UINTN OperationFlags,
OUT VOID *Buffer
)
{
UINT8 Opcode;
Opcode = (UINT8)(Data >> 8);
if (!Buffer && !Context) return EFI_INVALID_PARAMETER;
if (OperationFlags == 0 && (IoWidth < 3 || IoWidth > 4)) return EFI_INVALID_PARAMETER;
*((UINTN *)Buffer) = 0;
if (Count > 0x0B) {
if (((Count - 12) & 0xFFFFFFEB) != 0 || Count == 28) return EFI_UNSUPPORTED;
} else if (Count >= 0x0C) { return EFI_UNSUPPORTED; }
if (IoWidth == 3) { Context->OpTypeFlag = Opcode; }
else if (IoWidth == 4) { Context->OpTypeFlag = 0; }
else if (IoWidth == 4096) {
if (Context->OpTypeFlag && Context->OpTypeFlag != Opcode &&
Opcode != 0xE1 && Opcode != 0xF9) return EFI_UNSUPPORTED;
Context->OpTypeFlag = 0;
return NvmdimmDispatchCmd(Opcode, Context, IoWidth, (UINT8)Data, Buffer);
}
return NvmdimmDispatchCmd(Opcode, Context, 4, (UINT8)Data, Buffer);
}
/*===========================================================================
* NVDIMM command dispatch table (sub_80AC / NvmdimmDispatchCmd)
*===========================================================================
*
* Opcode Name Handler Description
* ------ ---------------- --------- ------------------------------------
* 0xE1 ReadJdec sub_7690 Read JEDEC data from NVDIMM
* 0xE2 ReadReg sub_7760 Read NVDIMM register
* 0xE3 WriteReg sub_781C Write NVDIMM register
* 0xE5 ReadIfrData sub_9478 Read IFR (Interface Data)
* 0xE6 GetEnergyStatus sub_E798 Get energy source status
* 0xE7 GetHealth sub_FC94 Get NVDIMM health status
* 0xE8 GetArmed sub_C3E8 Get arm status
* 0xE9 GetLatch sub_BD88 Get latch status
* 0xEA GetIfr sub_DCC0 Get IFR configuration
* 0xEB SetIfr sub_DB04 Set IFR configuration
* 0xED ReadEcc sub_DD6C Read ECC data
* 0xF0 Reset sub_BE80 Reset NVDIMM
* 0xF1 SetArmed sub_C02C Set NVDIMM arm
* 0xF2 Save sub_EA34 Save configuration (data=16)
* 0xF4 Restore sub_C250 Restore configuration
* 0xF5 ManageEnergy sub_6D34 Manage energy source
* 0xF6 Erase sub_6AE8 Erase NVDIMM (data=16)
* 0xF9 GetSetEnergy sub_56E4 Get/set energy config
*===========================================================================*/
EFI_STATUS
NvmdimmDispatchCmd (
IN UINT8 Opcode,
IN NVDIMM_DRIVER_CONTEXT *Context,
IN UINTN Param,
IN UINT8 Data,
OUT VOID *Buffer
)
{
if (Opcode > 0xEB) {
switch (Opcode) {
case 0xED: NvmdimmReadEcc(Context, Param, Data); break;
case 0xF0: NvmdimmReset(Context, Param, Data); break;
case 0xF1: NvmdimmSetArmed(Context, Param, Data); break;
case 0xF2: return NvmdimmSave(Context, Param, Data, Buffer);
case 0xF4: NvmdimmRestore(Context, Param, Data); break;
case 0xF5: NvmdimmManageEnergy(Context, Param, Data); break;
case 0xF6: return NvmdimmErase(Context, Param, Data, Buffer);
case 0xF9: return NvmdimmGetSetEnergy(Context, Param, Data, Buffer);
default: return EFI_UNSUPPORTED;
}
} else {
switch (Opcode) {
case 0xE1: NvmdimmReadJdec(Context, Param, Data); break;
case 0xE2: NvmdimmReadReg(Context, Param, Data); break;
case 0xE3: NvmdimmWriteReg(Context, Param, Data); break;
case 0xE5: NvmdimmReadIfrData(Context, Param, Data); break;
case 0xE6: NvmdimmGetEnergyStatus(Context, Param, Data);break;
case 0xE7: return NvmdimmGetHealth(Context, Param, Data, Buffer);
case 0xE8: NvmdimmGetArmed(Context, Param, Data); break;
case 0xE9: NvmdimmGetLatch(Context, Param, Data); break;
case 0xEA: NvmdimmGetIfr(Context, Param, Data); break;
case 0xEB: NvmdimmSetIfr(Context, Param, Data); break;
default: return EFI_UNSUPPORTED;
}
}
return EFI_SUCCESS;
}
/*===========================================================================
* Service stub (sub_314)
*===========================================================================*/
VOID
NvmdimmServiceStub (VOID) { return; }
/*===========================================================================
* Helper functions
*===========================================================================*/
VOID NvmdimmFreeBuffer (VOID) { }
VOID NvmdimmResetCache (NVDIMM_DRIVER_CONTEXT *Context) { }
EFI_STATUS NvmdimmFlushBuffers (NVDIMM_DRIVER_CONTEXT *Context) { return EFI_SUCCESS; }
CHAR16 *NvmdimmBuildString (NVDIMM_DRIVER_CONTEXT *Context, CHAR16 *Template, UINTN ExtraField) { return NULL; }
BOOLEAN NvmdimmStrPrefix (CHAR16 *String, CHAR16 *Prefix, CHAR16 *Alt1, CHAR16 *Alt2) { return TRUE; }
CHAR16 *
NvmdimmStrStr (CHAR16 *String, CHAR16 *Substring)
{
CHAR16 *s, *p;
if (!*Substring) return String;
for (; *String; String++) {
if (*String != *Substring) continue;
s = String; p = Substring;
while (*s && *s == *p) { s++; p++; }
if (!*p) return String;
}
return NULL;
}
VOID
NvmdimmStrCpy (CHAR16 *Dst, CHAR16 *Src, UINTN Count)
{
while (Count-- && *Src) *Dst++ = *Src++;
*Dst = 0;
}
VOID *
NvmdimmAllocatePool (UINTN Size)
{
VOID *Buffer;
gBootServices->AllocatePool(EfiBootServicesData, Size, &Buffer);
return Buffer;
}