/** @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;
}