From 79d274b8b6b113248661c18f31c4be03c7da32de Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Fri, 26 Jun 2015 16:09:52 +0000 Subject: 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 Cc: Huangpeng (Peter) Cc: Wei Liu Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Laszlo Ersek Tested-by: Maoming Reviewed-by: Jordan Justen git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17722 6f19259b-4bc3-4df7-8a09-765794883524 --- OvmfPkg/PlatformPei/MemDetect.c | 47 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) (limited to 'OvmfPkg/PlatformPei') diff --git a/OvmfPkg/PlatformPei/MemDetect.c b/OvmfPkg/PlatformPei/MemDetect.c index b74308f562..612bb4a3ef 100644 --- a/OvmfPkg/PlatformPei/MemDetect.c +++ b/OvmfPkg/PlatformPei/MemDetect.c @@ -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); + + // + // MTRRs disabled, fixed MTRRs disabled, default type is uncached + // + ASSERT ((MtrrSettings.MtrrDefType & BIT11) == 0); + ASSERT ((MtrrSettings.MtrrDefType & BIT10) == 0); + ASSERT ((MtrrSettings.MtrrDefType & 0xFF) == 0); + + // + // 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); - MtrrSetMemoryAttribute (0, BASE_512KB + BASE_128KB, CacheWriteBack); + // + // 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); - if (UpperMemorySize != 0) { - MtrrSetMemoryAttribute (BASE_4GB, UpperMemorySize, CacheWriteBack); + // + // 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); } } -- cgit v1.2.3