OvmfPkg: PlatformPei: invert MTRR setup in QemuInitializeRam()

At the moment we work with a UC default MTRR type, and set three memory
ranges to WB:
- [0, 640 KB),
- [1 MB, LowerMemorySize),
- [4 GB, 4 GB + UpperMemorySize).

Unfortunately, coverage for the third range can fail with a high
likelihood. If the alignment of the base (ie. 4 GB) and the alignment of
the size (UpperMemorySize) differ, then MtrrLib creates a series of
variable MTRR entries, with power-of-two sized MTRR masks. And, it's
really easy to run out of variable MTRR entries, dependent on the
alignment difference.

This is a problem because a Linux guest will loudly reject any high memory
that is not covered my MTRR.

So, let's follow the inverse pattern (loosely inspired by SeaBIOS):
- flip the MTRR default type to WB,
- set [0, 640 KB) to WB -- fixed MTRRs have precedence over the default
  type and variable MTRRs, so we can't avoid this,
- set [640 KB, 1 MB) to UC -- implemented with fixed MTRRs,
- set [LowerMemorySize, 4 GB) to UC -- should succeed with variable MTRRs
  more likely than the other scheme (due to less chaotic alignment
  differences).

Effects of this patch can be observed by setting DEBUG_CACHE (0x00200000)
in PcdDebugPrintErrorLevel.

Cc: Maoming <maoming.maoming@huawei.com>
Cc: Huangpeng (Peter) <peter.huangpeng@huawei.com>
Cc: Wei Liu <wei.liu2@citrix.com>
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Tested-by: Maoming <maoming.maoming@huawei.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>

git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17722 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
Laszlo Ersek 2015-06-26 16:09:52 +00:00 committed by lersek
parent cfc80e2e95
commit 79d274b8b6
1 changed files with 43 additions and 4 deletions

View File

@ -252,6 +252,8 @@ QemuInitializeRam (
{
UINT64 LowerMemorySize;
UINT64 UpperMemorySize;
MTRR_SETTINGS MtrrSettings;
EFI_STATUS Status;
DEBUG ((EFI_D_INFO, "%a called\n", __FUNCTION__));
@ -272,12 +274,49 @@ QemuInitializeRam (
}
}
MtrrSetMemoryAttribute (BASE_1MB, LowerMemorySize - BASE_1MB, CacheWriteBack);
//
// We'd like to keep the following ranges uncached:
// - [640 KB, 1 MB)
// - [LowerMemorySize, 4 GB)
//
// Everything else should be WB. Unfortunately, programming the inverse (ie.
// keeping the default UC, and configuring the complement set of the above as
// WB) is not reliable in general, because the end of the upper RAM can have
// practically any alignment, and we may not have enough variable MTRRs to
// cover it exactly.
//
if (IsMtrrSupported ()) {
MtrrGetAllMtrrs (&MtrrSettings);
MtrrSetMemoryAttribute (0, BASE_512KB + BASE_128KB, CacheWriteBack);
//
// MTRRs disabled, fixed MTRRs disabled, default type is uncached
//
ASSERT ((MtrrSettings.MtrrDefType & BIT11) == 0);
ASSERT ((MtrrSettings.MtrrDefType & BIT10) == 0);
ASSERT ((MtrrSettings.MtrrDefType & 0xFF) == 0);
if (UpperMemorySize != 0) {
MtrrSetMemoryAttribute (BASE_4GB, UpperMemorySize, CacheWriteBack);
//
// flip default type to writeback
//
SetMem (&MtrrSettings.Fixed, sizeof MtrrSettings.Fixed, 0x06);
ZeroMem (&MtrrSettings.Variables, sizeof MtrrSettings.Variables);
MtrrSettings.MtrrDefType |= BIT11 | BIT10 | 6;
MtrrSetAllMtrrs (&MtrrSettings);
//
// Set memory range from 640KB to 1MB to uncacheable
//
Status = MtrrSetMemoryAttribute (BASE_512KB + BASE_128KB,
BASE_1MB - (BASE_512KB + BASE_128KB), CacheUncacheable);
ASSERT_EFI_ERROR (Status);
//
// Set memory range from the "top of lower RAM" (RAM below 4GB) to 4GB as
// uncacheable
//
Status = MtrrSetMemoryAttribute (LowerMemorySize,
SIZE_4GB - LowerMemorySize, CacheUncacheable);
ASSERT_EFI_ERROR (Status);
}
}