Newer
Older
AMI-Aptio-BIOS-Reversed / Build / GuidArtifacts / 5038E34E-0774-47A0-A5EF-4B94AF1A43DA / 5038E34E-0774-47A0-A5EF-4B94AF1A43DA.c
@Ajax Dong Ajax Dong 2 days ago 21 KB Full restructure
/*
 * 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;
}