Newer
Older
AMI-Aptio-BIOS-Reversed / NvdimmCommon / NvdimmCommon.c
@Ajax Dong Ajax Dong 2 days ago 11 KB Init
/** @file
  NvdimmCommon - NVDIMM ACPI<->SMM Communication DXE Driver

  This DXE driver initializes the ACPI<->SMM interface for NVDIMM
  firmware interaction on Intel Purley platforms. It allocates a
  shared memory buffer, installs a protocol with Get/Set driver
  type callbacks, and registers exit boot services and virtual
  address change notifications.

  Build: DEBUG_VS2015 X64
  Source tree: PurleySktPkg\Dxe\NvdimmCommon\NvdimmCommon.c

  Copyright (c) Intel Corporation. All rights reserved.
**/

#include "NvdimmCommon.h"

//
// Globals
//
EFI_HANDLE                  gImageHandle       = NULL;
EFI_SYSTEM_TABLE            *gSystemTable      = NULL;
EFI_BOOT_SERVICES           *gBootServices     = NULL;
EFI_RUNTIME_SERVICES        *gRuntimeServices  = NULL;

EFI_PHYSICAL_ADDRESS        gSmmCommBuffer     = 0;
UINT8                       gNvdimmAcpiDriverType = 0;

EFI_HANDLE                  gNewHandle         = NULL;
EFI_EVENT                   gEventExitBootServices;
EFI_EVENT                   gEventVirtualAddressChange;
VOID                        *gHobList          = NULL;
EFI_DXE_SERVICES            *gDxeServices      = NULL;
NVDIMM_ACPI_SMM_PROTOCOL    *gNvdimmAcpiSmmProtocol = NULL;

UINT64                      gDebugLevel        = 0;
UINT8                       gDebugPort         = 0;

STATIC NVDIMM_ACPI_SMM_PROTOCOL  mAcpiSmmProtocol;

/**
  Entry point for NvdimmCommon DXE driver.

  Initializes UEFI services, registers event handlers for exit boot
  services and virtual address change, locates the HOB list and DXE
  Services Table, then installs the ACPI<->SMM protocol.

  @param[in]  ImageHandle  Handle of this EFI image.
  @param[in]  SystemTable  Pointer to the EFI System Table.

  @retval EFI_SUCCESS           The driver was initialized successfully.
  @retval EFI_UNSUPPORTED       Required services were not available.
  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
**/
EFI_STATUS
EFIAPI
NvdimmCommonEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;
  UINT64      ReturnStatus;

  //
  // Initialize global UEFI service pointers
  //
  gImageHandle = ImageHandle;
  if (ImageHandle == NULL) {
    DEBUG ((DEBUG_ERROR, "gImageHandle != NULL - %a:%d\n", __FILE__, __LINE__));
    ASSERT (ImageHandle != NULL);
  }

  gSystemTable = SystemTable;
  if (SystemTable == NULL) {
    DEBUG ((DEBUG_ERROR, "gST != NULL - %a:%d\n", __FILE__, __LINE__));
    ASSERT (SystemTable != NULL);
  }

  gBootServices = SystemTable->BootServices;
  if (gBootServices == NULL) {
    DEBUG ((DEBUG_ERROR, "gBS != NULL - %a:%d\n", __FILE__, __LINE__));
    ASSERT (gBootServices != NULL);
  }

  gRuntimeServices = SystemTable->RuntimeServices;
  if (gRuntimeServices == NULL) {
    DEBUG ((DEBUG_ERROR, "gRT != NULL - %a:%d\n", __FILE__, __LINE__));
    ASSERT (gRuntimeServices != NULL);
  }

  //
  // Register event handlers
  //
  Status = gBS->CreateEvent (
                  EVT_SIGNAL_EXIT_BOOT_SERVICES,
                  TPL_NOTIFY,
                  NvdimmCommonExitBootServices,
                  NULL,
                  &gEventExitBootServices
                  );
  ASSERT_EFI_ERROR (Status);

  Status = gBS->CreateEvent (
                  EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
                  TPL_NOTIFY,
                  NvdimmCommonVirtualAddressChange,
                  NULL,
                  &gEventVirtualAddressChange
                  );
  ASSERT_EFI_ERROR (Status);

  //
  // Locate the HOB list
  //
  NvdimmCommonLocateHobList ();

  //
  // Locate the DXE Services Table
  //
  Status = EfiGetSystemConfigurationTable (
             &gEfiDxeServicesTableGuid,
             (VOID **) &gDxeServices
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "Failed to locate DXE Services Table: %r\n", Status));
  }
  ASSERT_EFI_ERROR (Status);
  ASSERT (gDxeServices != NULL);

  //
  // Initialize ACPI<->SMM interface
  //
  ReturnStatus = NvdimmCommonInitAcpiSmmInterface (ImageHandle);
  if (ReturnStatus < 0) {
    //
    // Cleanup on failure
    //
    gBS->CloseEvent (gEventVirtualAddressChange);
    gBS->CloseEvent (gEventExitBootServices);
  }

  //
  // AutoGen.c entry point return
  //
  ASSERT_EFI_ERROR (ReturnStatus);

  return ReturnStatus;
}

/**
  Initializes the ACPI<->SMM interface for NVDIMM communication.

  Allocates an SMM shared memory buffer, zeros it, populates the
  ACPI<->SMM protocol structure, and installs the protocol.

  @param[in]  ImageHandle  Handle of this EFI image.

  @retval EFI_SUCCESS           The interface was initialized.
  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
  @retval Others                Protocol installation failed.
**/
UINT64
EFIAPI
NvdimmCommonInitAcpiSmmInterface (
  IN EFI_HANDLE  ImageHandle
  )
{
  EFI_STATUS                 Status;
  EFI_PHYSICAL_ADDRESS       SmmCommBuffer;
  NVDIMM_ACPI_SMM_PROTOCOL   *Protocol;

  SmmCommBuffer = (EFI_PHYSICAL_ADDRESS) (UINTN) -1;
  Protocol      = &mAcpiSmmProtocol;

  //
  // Allocate SMM communication buffer
  //
  Status = gBS->AllocatePages (
                  AllocateMaxAddress,
                  EfiReservedMemoryType,
                  EFI_SIZE_TO_PAGES (NVDIMM_SMM_COMM_BUFFER_SIZE),
                  &SmmCommBuffer
                  );
  if (EFI_ERROR (Status)) {
    DEBUG ((
      DEBUG_ERROR,
      "[NGN] ERROR: ACPI<->SMM memory allocation failed with status (%r)\n",
      Status
      ));
    goto ErrorExit;
  }

  DEBUG ((
    DEBUG_INFO,
    "[NGN] ACPI<->SMM interface address: 0x%llx\n",
    SmmCommBuffer
    ));

  gSmmCommBuffer = SmmCommBuffer;

  //
  // Zero the SMM communication buffer
  //
  ZeroMem ((VOID *) (UINTN) SmmCommBuffer, NVDIMM_SMM_COMM_BUFFER_SIZE);

  //
  // Zero the protocol structure
  //
  ZeroMem (Protocol, sizeof (NVDIMM_ACPI_SMM_PROTOCOL));

  //
  // Populate the protocol structure
  //
  Protocol->SmmCommBuffer    = SmmCommBuffer;
  Protocol->DriverType       = NVDIMM_ACPI_DRIVER_TYPE_A;
  Protocol->GetAcpiDriverType = NvdimmCommonGetAcpiDriverType;
  Protocol->SetAcpiDriverType = NvdimmCommonSetAcpiDriverType;

  //
  // Install the ACPI<->SMM protocol
  //
  Status = gBS->InstallProtocolInterface (
                  &gNewHandle,
                  &gNvdimmAcpiSmmProtocolGuid,
                  EFI_NATIVE_INTERFACE,
                  (VOID *) Protocol
                  );
  if (EFI_ERROR (Status)) {
    DEBUG ((
      DEBUG_ERROR,
      "[NGN] ERROR: ACPI<->SMM interface protocol installation failed with status (%r)\n",
      Status
      ));
    goto ErrorExit;
  }

  return (Status >> 63) & 0x8000000000000001ULL;

ErrorExit:
  DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
  DEBUG ((DEBUG_ERROR, "!EFI_ERROR (Status) - %a:%d\n", __FILE__, __LINE__));
  ASSERT_EFI_ERROR (Status);
  return (Status >> 63) & 0x8000000000000001ULL;
}

/**
  Returns the current NVDIMM ACPI driver type.

  @return The current NVDIMM ACPI driver type byte.
**/
UINT8
EFIAPI
NvdimmCommonGetAcpiDriverType (
  VOID
  )
{
  DEBUG ((
    DEBUG_INFO,
    "[NGN] Retrieve current NVDIMM ACPI Driver type (0x%x)\n",
    gNvdimmAcpiDriverType
    ));

  return gNvdimmAcpiDriverType;
}

/**
  Sets the NVDIMM ACPI driver type.

  Valid driver types are NVDIMM_ACPI_DRIVER_TYPE_A (1) and
  NVDIMM_ACPI_DRIVER_TYPE_B (2).

  @param[in]  DriverType  The NVDIMM ACPI driver type to set.

  @retval EFI_SUCCESS           The driver type was set successfully.
  @retval EFI_INVALID_PARAMETER The driver type is invalid.
**/
EFI_STATUS
EFIAPI
NvdimmCommonSetAcpiDriverType (
  IN UINT8  DriverType
  )
{
  if ((DriverType - 1) <= 1) {
    gNvdimmAcpiDriverType |= DriverType;
    return EFI_SUCCESS;
  } else {
    DEBUG ((
      DEBUG_ERROR,
      "[NGN] ERROR: Unknown NVDIMM ACPI Driver type (0x%x)\n",
      DriverType
      ));
    return EFI_INVALID_PARAMETER;
  }
}

/**
  Zeros a memory buffer.

  Wrapper around internal ZeroMem implementation.

  @param[in]  Buffer  Pointer to the buffer to zero.
  @param[in]  Length  Number of bytes to zero.

  @return Pointer to the buffer.
**/
VOID *
EFIAPI
InternalZeroMem (
  IN VOID   *Buffer,
  IN UINTN  Length
  )
{
  ASSERT (Buffer != NULL);
  ASSERT (Length <= (MAX_UINTN - (UINTN) Buffer + 1));
  return ZeroMem (Buffer, Length);
}

/**
  Locates the DebugPort protocol for debug message output.

  Uses LocateProtocol to find an instance of the DebugPort protocol.
  The protocol is cached for subsequent debug message delivery.

  @return Pointer to the DebugPort protocol interface, or NULL.
**/
DEBUGPORT_PROTOCOL *
EFIAPI
LocateDebugPortProtocol (
  VOID
  )
{
  DEBUGPORT_PROTOCOL  *DebugPort;

  DebugPort = NULL;
  if (gBS != NULL) {
    //
    // Check debug port availability (port 0x70/0x71 I/O access)
    //
    gDebugPort = IoRead8 (0x70);
    IoWrite8 (0x70, gDebugPort & 0x80 | 0x4B);

    gDebugLevel = IoRead8 (0x71);
    if (gDebugLevel > 3) {
      gDebugLevel = 3;
    }

    if ((gDebugLevel - 1) <= 0xFD) {
      if (gDebugLevel == 1) {
        IoWrite8 (0x71, 0x4B);
      }

      //
      // Locate DebugPort protocol
      //
      gBS->LocateProtocol (
             &gDebugPortProtocolGuid,
             NULL,
             (VOID **) &DebugPort
             );
    }
  }

  return DebugPort;
}

/**
  Sends an ASSERT-style message through the DebugPort protocol.

  @param[in]  FileName  The source file name string.
  @param[in]  LineNumber  The line number in the source file.
  @param[in]  Description  The assertion description string.
**/
VOID
EFIAPI
DebugPortAssert (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  )
{
  DEBUGPORT_PROTOCOL  *DebugPort;

  DebugPort = LocateDebugPortProtocol ();
  if (DebugPort != NULL) {
    DebugPort->Assert (FileName, LineNumber, Description);
  }
}

/**
  Exit Boot Services notification handler.

  Clears the BootServices pointer and performs necessary cleanup.

  @param[in]  Event   The event that was signaled.
  @param[in]  Context  Event context (unused).
**/
VOID
EFIAPI
NvdimmCommonExitBootServices (
  IN EFI_EVENT  Event,
  IN VOID       *Context
  )
{
  gBS = NULL;
}

/**
  Virtual Address Change notification handler.

  Converts the DebugPort protocol pointer for the new virtual address map.

  @param[in]  Event   The event that was signaled.
  @param[in]  Context  Event context (unused).
**/
VOID
EFIAPI
NvdimmCommonVirtualAddressChange (
  IN EFI_EVENT  Event,
  IN VOID       *Context
  )
{
  if (gDebugPortProtocol != NULL) {
    gRT->ConvertPointer (0, (VOID **) &gDebugPortProtocol);
  }
}

/**
  Locates the HOB list from the HOB List GUID.

  Uses the DXE Services Table to find the HOB list pointer.
**/
VOID
EFIAPI
NvdimmCommonLocateHobList (
  VOID
  )
{
  EFI_STATUS  Status;

  Status = EfiGetSystemConfigurationTable (
             &gEfiHobListGuid,
             &gHobList
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
    DEBUG ((DEBUG_ERROR, "!EFI_ERROR (Status) - %a:%d\n", __FILE__, __LINE__));
    ASSERT_EFI_ERROR (Status);
  }

  if (gHobList == NULL) {
    DEBUG ((DEBUG_ERROR, "mHobList != NULL - %a:%d\n", __FILE__, __LINE__));
    ASSERT (gHobList != NULL);
  }
}

/**
  Compares two GUID values by reading them as 64-bit values.

  @param[in]  Guid1  Pointer to the first GUID.
  @param[in]  Guid2  Pointer to the second GUID.

  @retval TRUE   The GUIDs are equal.
  @retval FALSE  The GUIDs are not equal.
**/
BOOLEAN
EFIAPI
CompareGuid (
  IN CONST GUID  *Guid1,
  IN CONST GUID  *Guid2
  )
{
  UINT64  Data1;
  UINT64  Data2;
  UINT64  Data3;
  UINT64  Data4;

  Data1 = ReadUnaligned64 (Guid1);
  Data2 = ReadUnaligned64 (Guid2);
  Data3 = ReadUnaligned64 ((CONST UINT64 *) Guid1 + 1);
  Data4 = ReadUnaligned64 ((CONST UINT64 *) Guid2 + 1);

  return (BOOLEAN) (Data1 == Data2 && Data3 == Data4);
}

/**
  Reads an unaligned 64-bit value from memory.

  ASSERTs if the buffer pointer is NULL.

  @param[in]  Buffer  Pointer to the unaligned 64-bit value.

  @return The 64-bit value read from the buffer.
**/
UINT64
EFIAPI
ReadUnaligned64 (
  IN CONST UINT64  *Buffer
  )
{
  ASSERT (Buffer != NULL);
  return *Buffer;
}