/** @file
|
Produce the memory type information HOB.
|
|
Copyright (C) 2017-2020, Red Hat, Inc.
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
**/
|
|
#include <Guid/MemoryTypeInformation.h>
|
#include <Library/BaseLib.h>
|
#include <Library/DebugLib.h>
|
#include <Library/HobLib.h>
|
#include <Library/PcdLib.h>
|
#include <Library/PeiServicesLib.h>
|
#include <Ppi/ReadOnlyVariable2.h>
|
#include <Uefi/UefiMultiPhase.h>
|
|
#include "Platform.h"
|
|
#define MEMORY_TYPE_INFO_DEFAULT(Type) \
|
{ Type, FixedPcdGet32 (PcdMemoryType ## Type) }
|
|
STATIC EFI_MEMORY_TYPE_INFORMATION mMemoryTypeInformation[] = {
|
MEMORY_TYPE_INFO_DEFAULT (EfiACPIMemoryNVS),
|
MEMORY_TYPE_INFO_DEFAULT (EfiACPIReclaimMemory),
|
MEMORY_TYPE_INFO_DEFAULT (EfiReservedMemoryType),
|
MEMORY_TYPE_INFO_DEFAULT (EfiRuntimeServicesCode),
|
MEMORY_TYPE_INFO_DEFAULT (EfiRuntimeServicesData),
|
{ EfiMaxMemoryType, 0 }
|
};
|
|
STATIC
|
VOID
|
BuildMemTypeInfoHob (
|
VOID
|
)
|
{
|
BuildGuidDataHob (
|
&gEfiMemoryTypeInformationGuid,
|
mMemoryTypeInformation,
|
sizeof mMemoryTypeInformation
|
);
|
}
|
|
/**
|
Refresh the mMemoryTypeInformation array (which we'll turn into the
|
MemoryTypeInformation HOB) from the MemoryTypeInformation UEFI variable.
|
|
Normally, the DXE IPL PEIM builds the HOB from the UEFI variable. But it does
|
so *transparently*. Instead, we consider the UEFI variable as a list of
|
hints, for updating our HOB defaults:
|
|
- Record types not covered in mMemoryTypeInformation are ignored. In
|
particular, this hides record types from the UEFI variable that may lead to
|
reboots without benefiting SMM security, such as EfiBootServicesData.
|
|
- Records that would lower the defaults in mMemoryTypeInformation are also
|
ignored.
|
|
@param[in] ReadOnlyVariable2 The EFI_PEI_READ_ONLY_VARIABLE2_PPI used for
|
retrieving the MemoryTypeInformation UEFI
|
variable.
|
**/
|
STATIC
|
VOID
|
RefreshMemTypeInfo (
|
IN EFI_PEI_READ_ONLY_VARIABLE2_PPI *ReadOnlyVariable2
|
)
|
{
|
UINTN DataSize;
|
EFI_MEMORY_TYPE_INFORMATION Entries[EfiMaxMemoryType + 1];
|
EFI_STATUS Status;
|
UINTN NumEntries;
|
UINTN HobRecordIdx;
|
|
//
|
// Read the MemoryTypeInformation UEFI variable from the
|
// gEfiMemoryTypeInformationGuid namespace.
|
//
|
DataSize = sizeof Entries;
|
Status = ReadOnlyVariable2->GetVariable (
|
ReadOnlyVariable2,
|
EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME,
|
&gEfiMemoryTypeInformationGuid,
|
NULL,
|
&DataSize,
|
Entries
|
);
|
if (EFI_ERROR (Status)) {
|
//
|
// If the UEFI variable does not exist (EFI_NOT_FOUND), we can't use it for
|
// udpating mMemoryTypeInformation.
|
//
|
// If the UEFI variable exists but Entries is too small to hold it
|
// (EFI_BUFFER_TOO_SMALL), then the variable contents are arguably invalid.
|
// That's because Entries has room for every distinct EFI_MEMORY_TYPE,
|
// including the terminator record with EfiMaxMemoryType. Thus, we can't
|
// use the UEFI variable for updating mMemoryTypeInformation.
|
//
|
// If the UEFI variable couldn't be read for some other reason, we
|
// similarly can't use it for udpating mMemoryTypeInformation.
|
//
|
DEBUG ((DEBUG_ERROR, "%a: GetVariable(): %r\n", __FUNCTION__, Status));
|
return;
|
}
|
|
//
|
// Sanity-check the UEFI variable size against the record size.
|
//
|
if (DataSize % sizeof Entries[0] != 0) {
|
DEBUG ((DEBUG_ERROR, "%a: invalid UEFI variable size %Lu\n", __FUNCTION__,
|
(UINT64)DataSize));
|
return;
|
}
|
NumEntries = DataSize / sizeof Entries[0];
|
|
//
|
// For each record in mMemoryTypeInformation, except the terminator record,
|
// look up the first match (if any) in the UEFI variable, based on the memory
|
// type.
|
//
|
for (HobRecordIdx = 0;
|
HobRecordIdx < ARRAY_SIZE (mMemoryTypeInformation) - 1;
|
HobRecordIdx++) {
|
EFI_MEMORY_TYPE_INFORMATION *HobRecord;
|
UINTN Idx;
|
EFI_MEMORY_TYPE_INFORMATION *VariableRecord;
|
|
HobRecord = &mMemoryTypeInformation[HobRecordIdx];
|
|
for (Idx = 0; Idx < NumEntries; Idx++) {
|
VariableRecord = &Entries[Idx];
|
|
if (VariableRecord->Type == HobRecord->Type) {
|
break;
|
}
|
}
|
|
//
|
// If there is a match, allow the UEFI variable to increase NumberOfPages.
|
//
|
if (Idx < NumEntries &&
|
HobRecord->NumberOfPages < VariableRecord->NumberOfPages) {
|
DEBUG ((DEBUG_VERBOSE, "%a: Type 0x%x: NumberOfPages 0x%x -> 0x%x\n",
|
__FUNCTION__, HobRecord->Type, HobRecord->NumberOfPages,
|
VariableRecord->NumberOfPages));
|
|
HobRecord->NumberOfPages = VariableRecord->NumberOfPages;
|
}
|
}
|
}
|
|
/**
|
Notification function called when EFI_PEI_READ_ONLY_VARIABLE2_PPI becomes
|
available.
|
|
@param[in] PeiServices Indirect reference to the PEI Services Table.
|
@param[in] NotifyDescriptor Address of the notification descriptor data
|
structure.
|
@param[in] Ppi Address of the PPI that was installed.
|
|
@return Status of the notification. The status code returned from this
|
function is ignored.
|
**/
|
STATIC
|
EFI_STATUS
|
EFIAPI
|
OnReadOnlyVariable2Available (
|
IN EFI_PEI_SERVICES **PeiServices,
|
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
|
IN VOID *Ppi
|
)
|
{
|
DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__));
|
|
RefreshMemTypeInfo (Ppi);
|
BuildMemTypeInfoHob ();
|
return EFI_SUCCESS;
|
}
|
|
//
|
// Notification object for registering the callback, for when
|
// EFI_PEI_READ_ONLY_VARIABLE2_PPI becomes available.
|
//
|
STATIC CONST EFI_PEI_NOTIFY_DESCRIPTOR mReadOnlyVariable2Notify = {
|
(EFI_PEI_PPI_DESCRIPTOR_NOTIFY_DISPATCH |
|
EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), // Flags
|
&gEfiPeiReadOnlyVariable2PpiGuid, // Guid
|
OnReadOnlyVariable2Available // Notify
|
};
|
|
VOID
|
MemTypeInfoInitialization (
|
VOID
|
)
|
{
|
EFI_STATUS Status;
|
|
if (!FeaturePcdGet (PcdSmmSmramRequire)) {
|
//
|
// EFI_PEI_READ_ONLY_VARIABLE2_PPI will never be available; install
|
// the default memory type information HOB right away.
|
//
|
BuildMemTypeInfoHob ();
|
return;
|
}
|
|
Status = PeiServicesNotifyPpi (&mReadOnlyVariable2Notify);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "%a: failed to set up R/O Variable 2 callback: %r\n",
|
__FUNCTION__, Status));
|
ASSERT (FALSE);
|
CpuDeadLoop ();
|
}
|
}
|