/** @file
Copyright (c) 2017, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
#define MEMORY_ATTRIBUTE_MASK (EFI_RESOURCE_ATTRIBUTE_PRESENT | \
EFI_RESOURCE_ATTRIBUTE_INITIALIZED | \
EFI_RESOURCE_ATTRIBUTE_TESTED | \
EFI_RESOURCE_ATTRIBUTE_16_BIT_IO | \
EFI_RESOURCE_ATTRIBUTE_32_BIT_IO | \
EFI_RESOURCE_ATTRIBUTE_64_BIT_IO \
)
#define TESTED_MEMORY_ATTRIBUTES (EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | EFI_RESOURCE_ATTRIBUTE_TESTED)
#define INITIALIZED_MEMORY_ATTRIBUTES (EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED)
#define PRESENT_MEMORY_ATTRIBUTES (EFI_RESOURCE_ATTRIBUTE_PRESENT)
MTRR_MEMORY_CACHE_TYPE
SetCurrentCacheType (
IN MTRR_MEMORY_CACHE_TYPE CurrentCacheType,
IN MTRR_MEMORY_CACHE_TYPE NewCacheType
)
{
switch (CurrentCacheType) {
case CacheUncacheable:
return CacheUncacheable;
break;
case CacheWriteBack:
if (NewCacheType == CacheWriteThrough) {
return CacheWriteThrough;
} else {
return CacheInvalid;
}
break;
case CacheWriteThrough:
if (NewCacheType == CacheWriteBack) {
return CacheWriteThrough;
} else {
return CacheInvalid;
}
break;
default:
if (NewCacheType == CacheUncacheable) {
return CacheUncacheable;
} else {
return CacheInvalid;
}
break;
}
}
EFI_STATUS
TestPointCheckCacheType (
IN MTRR_SETTINGS *Mtrrs,
IN VARIABLE_MTRR *VariableMtrr,
IN UINT64 Base,
IN UINT64 Length,
IN MTRR_MEMORY_CACHE_TYPE ExpectedCacheType
)
{
UINT64 TempBase;
UINT64 TempLength;
UINTN VariableMtrrIndex;
UINTN VariableMtrrCount;
if (Base < BASE_1MB) {
// Check Fixed MTRR
return EFI_SUCCESS;
}
//
// Check
//
VariableMtrrCount = GetVariableMtrrCount ();
for (VariableMtrrIndex = 0; VariableMtrrIndex < VariableMtrrCount; VariableMtrrIndex++) {
if (!VariableMtrr[VariableMtrrIndex].Valid) {
continue;
}
if (((Base >= VariableMtrr[VariableMtrrIndex].BaseAddress) && (Base < VariableMtrr[VariableMtrrIndex].BaseAddress + VariableMtrr[VariableMtrrIndex].Length)) ||
((VariableMtrr[VariableMtrrIndex].BaseAddress >= Base) && (VariableMtrr[VariableMtrrIndex].BaseAddress < Base + Length))) {
// Overlap check
if (VariableMtrr[VariableMtrrIndex].Type != ExpectedCacheType) {
DEBUG ((DEBUG_ERROR, "Cache [0x%lx, 0x%lx] is not expected\n", Base, Length));
return EFI_INVALID_PARAMETER;
}
}
}
TempBase = Base;
TempLength = Length;
for (VariableMtrrIndex = 0; VariableMtrrIndex < VariableMtrrCount; VariableMtrrIndex++) {
if (!VariableMtrr[VariableMtrrIndex].Valid) {
continue;
}
if (((TempBase >= VariableMtrr[VariableMtrrIndex].BaseAddress) && (TempBase < VariableMtrr[VariableMtrrIndex].BaseAddress + VariableMtrr[VariableMtrrIndex].Length)) ||
((VariableMtrr[VariableMtrrIndex].BaseAddress >= TempBase) && (VariableMtrr[VariableMtrrIndex].BaseAddress < TempBase + TempLength))) {
// Update checked region
if (TempBase >= VariableMtrr[VariableMtrrIndex].BaseAddress) {
if (TempBase + TempLength > VariableMtrr[VariableMtrrIndex].BaseAddress + VariableMtrr[VariableMtrrIndex].Length) {
TempLength = TempBase + TempLength - (VariableMtrr[VariableMtrrIndex].BaseAddress + VariableMtrr[VariableMtrrIndex].Length);
TempBase = VariableMtrr[VariableMtrrIndex].BaseAddress + VariableMtrr[VariableMtrrIndex].Length;
} else {
TempLength = 0;
}
} else {
TempLength = VariableMtrr[VariableMtrrIndex].BaseAddress - TempBase;
}
}
}
if (TempLength != 0) {
if ((Mtrrs->MtrrDefType & 0xFF) != ExpectedCacheType) {
DEBUG ((DEBUG_ERROR, "Cache [0x%lx, 0x%lx] is not expected in default\n", TempBase, TempLength));
return EFI_INVALID_PARAMETER;
}
}
return EFI_SUCCESS;
}
EFI_STATUS
TestPointCheckMtrrMask (
IN MTRR_SETTINGS *Mtrrs
)
{
UINTN Index;
UINT64 Length;
UINT32 RegEax;
UINT8 PhysicalAddressBits;
UINTN VariableMtrrCount;
AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
if (RegEax >= 0x80000008) {
AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
PhysicalAddressBits = (UINT8) RegEax;
} else {
PhysicalAddressBits = 36;
}
VariableMtrrCount = GetVariableMtrrCount ();
for (Index = 0; Index < VariableMtrrCount; Index++) {
if ((Mtrrs->Variables.Mtrr[Index].Mask & BIT11) == 0) {
continue;
}
Length = Mtrrs->Variables.Mtrr[Index].Mask & ~0xFFFull;
Length = ~Length + 1;
Length = Length & (LShiftU64 (1, PhysicalAddressBits) - 1);
if (Length != GetPowerOfTwo64 (Length)) {
DEBUG ((DEBUG_ERROR, "MTRR Mask (0x%016lx) is invalid\n", Mtrrs->Variables.Mtrr[Index].Mask));
return EFI_INVALID_PARAMETER;
}
}
return EFI_SUCCESS;
}
VOID
TestPointMtrrConvert (
IN MTRR_SETTINGS *Mtrrs,
OUT VARIABLE_MTRR *VariableMtrr
)
{
UINT32 RegEax;
UINT8 PhysicalAddressBits;
VARIABLE_MTRR TempVariableMtrr;
UINTN Index;
UINTN VariableMtrrIndex;
UINTN VariableMtrrCount;
AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
if (RegEax >= 0x80000008) {
AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
PhysicalAddressBits = (UINT8) RegEax;
} else {
PhysicalAddressBits = 36;
}
//
// Calculate Length
//
VariableMtrrIndex = 0;
VariableMtrrCount = GetVariableMtrrCount ();
for (Index = 0; Index < VariableMtrrCount; Index++) {
if ((Mtrrs->Variables.Mtrr[Index].Mask & BIT11) == 0) {
continue;
}
VariableMtrr[VariableMtrrIndex].Length = Mtrrs->Variables.Mtrr[Index].Mask & ~0xFFFull;
VariableMtrr[VariableMtrrIndex].Length = ~VariableMtrr[VariableMtrrIndex].Length + 1;
VariableMtrr[VariableMtrrIndex].Length = VariableMtrr[VariableMtrrIndex].Length & (LShiftU64 (1, PhysicalAddressBits) - 1);
VariableMtrr[VariableMtrrIndex].BaseAddress = Mtrrs->Variables.Mtrr[Index].Base & ~0xFFFull;
VariableMtrr[VariableMtrrIndex].Type = Mtrrs->Variables.Mtrr[Index].Base & 0xFF;
VariableMtrr[VariableMtrrIndex].Valid = TRUE;
VariableMtrrIndex ++;
}
VariableMtrrCount = VariableMtrrIndex;
//
// Sort
//
if (VariableMtrrCount > 1) {
for (VariableMtrrIndex = 0; VariableMtrrIndex < VariableMtrrCount; VariableMtrrIndex++) {
Index = VariableMtrrIndex + 1;
for (Index = VariableMtrrIndex + 1; Index < VariableMtrrCount; Index++) {
if (VariableMtrr[VariableMtrrIndex].BaseAddress > VariableMtrr[Index].BaseAddress) {
CopyMem (&TempVariableMtrr, &VariableMtrr[VariableMtrrIndex], sizeof(VARIABLE_MTRR));
CopyMem (&VariableMtrr[VariableMtrrIndex], &VariableMtrr[Index], sizeof(VARIABLE_MTRR));
CopyMem (&VariableMtrr[Index], &TempVariableMtrr, sizeof(VARIABLE_MTRR));
}
}
}
}
//
// Dump
//
DEBUG ((DEBUG_INFO, "CACHE Result:\n"));
for (VariableMtrrIndex = 0; VariableMtrrIndex < VariableMtrrCount; VariableMtrrIndex++) {
if (VariableMtrr[VariableMtrrIndex].Valid) {
DEBUG ((DEBUG_INFO, "CACHE - 0x%016lx 0x%016lx %d\n",
VariableMtrr[VariableMtrrIndex].BaseAddress,
VariableMtrr[VariableMtrrIndex].Length,
VariableMtrr[VariableMtrrIndex].Type
));
}
}
//
// Remove overlap
//
if (VariableMtrrCount > 1) {
for (VariableMtrrIndex = 0; VariableMtrrIndex < VariableMtrrCount; VariableMtrrIndex++) {
Index = VariableMtrrIndex + 1;
for (Index = VariableMtrrIndex + 1; Index < VariableMtrrCount - 1; Index++) {
if (VariableMtrr[VariableMtrrIndex].BaseAddress + VariableMtrr[VariableMtrrIndex].Length > VariableMtrr[Index].BaseAddress) {
VariableMtrr[VariableMtrrIndex].Length = VariableMtrr[Index].BaseAddress - VariableMtrr[VariableMtrrIndex].BaseAddress;
}
}
}
}
//
// Dump
//
DEBUG ((DEBUG_INFO, "CACHE Final:\n"));
for (VariableMtrrIndex = 0; VariableMtrrIndex < VariableMtrrCount; VariableMtrrIndex++) {
if (VariableMtrr[VariableMtrrIndex].Valid) {
DEBUG ((DEBUG_INFO, "CACHE - 0x%016lx 0x%016lx %d\n",
VariableMtrr[VariableMtrrIndex].BaseAddress,
VariableMtrr[VariableMtrrIndex].Length,
VariableMtrr[VariableMtrrIndex].Type
));
}
}
}
EFI_STATUS
TestPointCheckMtrrForPei (
IN MTRR_SETTINGS *Mtrrs,
IN VARIABLE_MTRR *VariableMtrr
)
{
EFI_STATUS Status;
VOID *HobList;
EFI_HOB_HANDOFF_INFO_TABLE *PhitHob;
HobList = GetHobList ();
PhitHob = HobList;
//
// DRAM must be WB
//
DEBUG ((DEBUG_INFO, "MTRR Checking 0x%lx 0x%lx\n", PhitHob->EfiMemoryBottom, PhitHob->EfiMemoryTop - PhitHob->EfiMemoryBottom));
Status = TestPointCheckCacheType (
Mtrrs,
VariableMtrr,
PhitHob->EfiMemoryBottom,
PhitHob->EfiMemoryTop - PhitHob->EfiMemoryBottom,
CacheWriteBack
);
if (EFI_ERROR(Status)) {
return Status;
}
//
// FV must be WB or WP
//
//
// MMIO must be UC
//
return EFI_SUCCESS;
}
EFI_STATUS
TestPointCheckMtrrForDxe (
IN MTRR_SETTINGS *Mtrrs,
IN VARIABLE_MTRR *VariableMtrr
)
{
EFI_STATUS Status;
VOID *HobList;
EFI_PEI_HOB_POINTERS Hob;
EFI_HOB_RESOURCE_DESCRIPTOR *ResourceHob;
HobList = GetHobList ();
//
// DRAM must be WB
//
for (Hob.Raw = HobList; !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) {
if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
ResourceHob = Hob.ResourceDescriptor;
switch (ResourceHob->ResourceType) {
case EFI_RESOURCE_SYSTEM_MEMORY:
if (((ResourceHob->ResourceAttribute & MEMORY_ATTRIBUTE_MASK) == TESTED_MEMORY_ATTRIBUTES) ||
((ResourceHob->ResourceAttribute & MEMORY_ATTRIBUTE_MASK) == INITIALIZED_MEMORY_ATTRIBUTES)) {
DEBUG ((DEBUG_INFO, "MTRR Checking 0x%lx 0x%lx\n", ResourceHob->PhysicalStart, ResourceHob->ResourceLength));
Status = TestPointCheckCacheType (
Mtrrs,
VariableMtrr,
ResourceHob->PhysicalStart,
ResourceHob->ResourceLength,
CacheWriteBack
);
if (EFI_ERROR(Status)) {
return Status;
}
}
break;
default:
break;
}
}
}
//
// FV must be WB or WP
//
//
// MMIO must be UC
//
return EFI_SUCCESS;
}
EFI_STATUS
TestPointCheckMtrr (
IN BOOLEAN IsForDxe
)
{
EFI_STATUS Status;
MTRR_SETTINGS LocalMtrrs;
MTRR_SETTINGS *Mtrrs;
UINTN Index;
UINTN VariableMtrrCount;
BOOLEAN Result;
VARIABLE_MTRR VariableMtrr[MTRR_NUMBER_OF_VARIABLE_MTRR];
DEBUG ((DEBUG_INFO, "==== TestPointCheckMtrr - Enter\n"));
MtrrGetAllMtrrs (&LocalMtrrs);
Mtrrs = &LocalMtrrs;
DEBUG ((DEBUG_INFO, "MTRR Default Type: %016lx\n", Mtrrs->MtrrDefType));
for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) {
DEBUG ((DEBUG_INFO, "Fixed MTRR[%02d] : %016lx\n", Index, Mtrrs->Fixed.Mtrr[Index]));
}
VariableMtrrCount = GetVariableMtrrCount ();
for (Index = 0; Index < VariableMtrrCount; Index++) {
DEBUG ((DEBUG_INFO, "Variable MTRR[%02d]: Base=%016lx Mask=%016lx\n",
Index,
Mtrrs->Variables.Mtrr[Index].Base,
Mtrrs->Variables.Mtrr[Index].Mask
));
}
DEBUG ((DEBUG_INFO, "\n"));
DEBUG ((DEBUG_INFO, "==== TestPointCheckMtrr - Exit\n"));
//
// Check Mask
//
Status = TestPointCheckMtrrMask (Mtrrs);
if (EFI_ERROR(Status)) {
Result = FALSE;
} else {
ZeroMem (VariableMtrr, sizeof(VariableMtrr));
TestPointMtrrConvert (Mtrrs, VariableMtrr);
if (IsForDxe) {
Status = TestPointCheckMtrrForDxe (Mtrrs, VariableMtrr);
} else {
Status = TestPointCheckMtrrForPei (Mtrrs, VariableMtrr);
}
if (EFI_ERROR(Status)) {
Result = FALSE;
} else {
Result = TRUE;
}
}
if (!Result) {
if (IsForDxe) {
TestPointLibAppendErrorString (
PLATFORM_TEST_POINT_ROLE_PLATFORM_IBV,
NULL,
TEST_POINT_BYTE2_END_OF_PEI_MTRR_FUNCTIONAL_ERROR_CODE \
TEST_POINT_END_OF_PEI \
TEST_POINT_BYTE2_END_OF_PEI_MTRR_FUNCTIONAL_ERROR_STRING
);
} else {
TestPointLibAppendErrorString (
PLATFORM_TEST_POINT_ROLE_PLATFORM_IBV,
NULL,
TEST_POINT_BYTE1_MEMORY_DISCOVERED_MTRR_FUNCTIONAL_ERROR_CODE \
TEST_POINT_MEMORY_DISCOVERED \
TEST_POINT_BYTE1_MEMORY_DISCOVERED_MTRR_FUNCTIONAL_ERROR_STRING
);
}
}
return Status;
}