/** @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", __func__, 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", | |
__func__, | |
(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", | |
__func__, | |
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", __func__)); | |
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 ( | |
IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob | |
) | |
{ | |
EFI_STATUS Status; | |
if (!PlatformInfoHob->SmmSmramRequire) { | |
// | |
// 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", | |
__func__, | |
Status | |
)); | |
ASSERT (FALSE); | |
CpuDeadLoop (); | |
} | |
} |