From 5d8e3f5836e4243bbb7ec5cdc6c467e5c3eeecae Mon Sep 17 00:00:00 2001 From: Mike Beaton Date: Mon, 3 Apr 2023 19:55:48 +0100 Subject: [PATCH] OcConsoleLib: Additional info in GOP dump; additional guards on GopBurstMode --- Changelog.md | 2 + Docs/Configuration.tex | 14 +- Include/Acidanthera/Library/OcConsoleLib.h | 44 ++- Library/OcConsoleLib/GopInfoDump.c | 278 +++++++++++------- .../{GopBurstMode.c => GopUtils.c} | 160 +++++++++- Library/OcConsoleLib/OcConsoleLib.inf | 2 +- 6 files changed, 378 insertions(+), 122 deletions(-) rename Library/OcConsoleLib/{GopBurstMode.c => GopUtils.c} (57%) diff --git a/Changelog.md b/Changelog.md index 56c74502..4f0e7cd1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,8 @@ OpenCore Changelog #### v0.9.2 - Added `DisableIoMapperMapping` quirk, thx @CaseySJ - Fixed disabling single user mode when Apple Secure Boot is enabled +- Improved guard checks for `GopBurstMode` on systems where it's not needed +- Improved compatibilty of `GopBurstMode` with some very non-standard GOP implementations #### v0.9.1 - Fixed long comment printing for ACPI patches, thx @corpnewt diff --git a/Docs/Configuration.tex b/Docs/Configuration.tex index 54fe69f7..8913dd78 100755 --- a/Docs/Configuration.tex +++ b/Docs/Configuration.tex @@ -8252,8 +8252,18 @@ for additional options. for GOP memory, even though the CPU supports it. Setting this can give a considerable speed-up for GOP operations, especially on systems which require \texttt{DirectGopRendering}. - \emph{Note}: This takes effect whether or not \texttt{DirectGopRendering} is set, and may - give some speed-up to GOP operations even when \texttt{DirectGopRendering} is \texttt{false}. + \emph{Note 1}: This quirk takes effect whether or not \texttt{DirectGopRendering} is set, and + in some cases may give a noticeable speed-up to GOP operations even when \texttt{DirectGopRendering} + is \texttt{false}. + + \emph{Note 2}: On most systems from circa 2013 onwards, write-combining + caching is already applied by the firmware to GOP memory, in which case \texttt{GopBurstMode} + is unnecessary. On such systems enabling the quirk should normally be harmless, producing an + \texttt{OCC:} debug log entry indicating that burst mode is already started. + + \emph{Note 3}: Some caution should be taken when enabling this quirk, as it has been observed + to cause hangs on a few systems. Since additional guards have been added to try to prevent this, + please log a bugtracker issue if such a system is found. \item \texttt{GopPassThrough}\\ diff --git a/Include/Acidanthera/Library/OcConsoleLib.h b/Include/Acidanthera/Library/OcConsoleLib.h index 2b0d832f..af9e000c 100644 --- a/Include/Acidanthera/Library/OcConsoleLib.h +++ b/Include/Acidanthera/Library/OcConsoleLib.h @@ -15,9 +15,10 @@ #ifndef OC_CONSOLE_LIB_H #define OC_CONSOLE_LIB_H -#include #include #include +#include +#include #include @@ -113,6 +114,47 @@ OcSetGopBurstMode ( VOID ); +/** + Return the bytes per pixel for the current GOP mode. + + GOP mode information does not include anything directly containing the bytes + per pixel, but there is a defined algorithm for working out this size, even for + non-standard pixel masks, and also a defined situation (PixelBltOnly pixel + format) where there is no such size. + + @param[in] Mode GOP mode. + @param[in] BytesPerPixel Bytes per pixel for the mode in use. + + @retval EFI_SUCCESS Success. + @retval EFI_UNSUPPORTED There is no frame buffer. + @retval EFI_INVALID_PARAMETER Mode info parameters are outside valid ranges. +**/ +EFI_STATUS +OcGopModeBytesPerPixel ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode, + OUT UINTN *BytesPerPixel + ); + +/** + Return the frame buffer size in use for the current GOP mode, even where + Gop->Mode->FrameBufferSize misreports this. + + Occasional GOP implementations report a frame buffer size far larger (e.g. ~100x) + than required for the actual mode in use. + + @param[in] Mode GOP mode. + @param[in] FrameBufferSize Frame buffer size in use. + + @retval EFI_SUCCESS Success. + @retval EFI_UNSUPPORTED There is no frame buffer. + @retval EFI_INVALID_PARAMETER Size parameters are outside valid ranges. +**/ +EFI_STATUS +OcGopModeSafeFrameBufferSize ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode, + OUT UINTN *FrameBufferSize + ); + /** Set screen resolution on console handle. diff --git a/Library/OcConsoleLib/GopInfoDump.c b/Library/OcConsoleLib/GopInfoDump.c index c1eaad59..9bad33af 100644 --- a/Library/OcConsoleLib/GopInfoDump.c +++ b/Library/OcConsoleLib/GopInfoDump.c @@ -1,5 +1,5 @@ /** @file - Dump GOP info - currently dumps memory caching info from MTRR and PAT. + Dump GOP info for current/default mode, including memory caching info from MTRR and PAT. Copyright (C) 2023, Mike Beaton. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause @@ -34,6 +34,8 @@ OcGopInfoDump ( EFI_VIRTUAL_ADDRESS VirtualAddress; EFI_PHYSICAL_ADDRESS PhysicalAddress; EFI_PHYSICAL_ADDRESS EndAddress; + UINTN FrameBufferSize; + UINTN BytesPerPixel; UINT64 Bits; UINT8 Level; BOOLEAN HasMtrr; @@ -65,115 +67,183 @@ OcGopInfoDump ( return EFI_OUT_OF_RESOURCES; } - HasMtrr = IsMtrrSupported (); - HasPat = OcIsPatSupported (); + Status = EFI_SUCCESS; - OcAsciiPrintBuffer ( - &FileBuffer, - &FileBufferSize, - "MTRR %asupported PAT %asupported\n", - HasMtrr ? "" : "not ", - HasPat ? "" : "not " - ); - - if (HasMtrr) { - if (HasPat) { - PatMsr = AsmReadMsr64 (MSR_IA32_CR_PAT); - HasDefaultPat = (PatMsr == PAT_DEFAULTS); - - OcAsciiPrintBuffer ( - &FileBuffer, - &FileBufferSize, - "PAT 0x%016LX (%a)\n", - PatMsr, - HasDefaultPat ? "default" : "not default!" - ); - } - - PageTable = OcGetCurrentPageTable (NULL); - - VirtualAddress = Gop->Mode->FrameBufferBase; - EndAddress = VirtualAddress + Gop->Mode->FrameBufferSize; - - Status = EFI_SUCCESS; - - do { - MtrrCacheType = MtrrGetMemoryAttribute (VirtualAddress); - - if (HasPat) { - Status = OcGetSetPageTableInfoForAddress ( - PageTable, - VirtualAddress, - &PhysicalAddress, - &Level, - &Bits, - &PatIndex, - FALSE - ); - - if (EFI_ERROR (Status)) { - break; - } - - OcAsciiPrintBuffer ( - &FileBuffer, - &FileBufferSize, - "0x%LX->0x%LX MTRR %u=%a PTE%u bits 0x%016LX PAT %u->%u=%a\n", - VirtualAddress, - PhysicalAddress, - MtrrCacheType, - OcGetMtrrTypeString (MtrrCacheType), - Level, - Bits, - PatIndex.Index, - GET_PAT_N (PatMsr, PatIndex.Index), - OcGetPatTypeString (GET_PAT_N (PatMsr, PatIndex.Index)) - ); - - if (VirtualAddress != PhysicalAddress) { - Status = EFI_UNSUPPORTED; - break; - } - } else { - Level = 2; - - OcAsciiPrintBuffer ( - &FileBuffer, - &FileBufferSize, - "0x%LX MTRR %u=%a\n", - VirtualAddress, - MtrrCacheType, - OcGetMtrrTypeString (MtrrCacheType) - ); - } - - switch (Level) { - case 1: - VirtualAddress += SIZE_1GB; - break; - - case 2: - VirtualAddress += SIZE_2MB; - break; - - case 4: - VirtualAddress += SIZE_4KB; - break; - - default: - Status = EFI_UNSUPPORTED; - break; - } - } while (!EFI_ERROR (Status) && VirtualAddress < EndAddress); - } - - if (EFI_ERROR (Status)) { + if ((Gop->Mode == NULL) || (Gop->Mode->Info == NULL)) { OcAsciiPrintBuffer ( &FileBuffer, &FileBufferSize, - "Failure reading page table! - %r\n", + "INVALID GOP %p %p!\n", + Gop->Mode, + (Gop->Mode != NULL) ? Gop->Mode->Info : NULL + ); + Status = EFI_UNSUPPORTED; + } + + if (!EFI_ERROR (Status)) { + OcAsciiPrintBuffer ( + &FileBuffer, + &FileBufferSize, + "GOP INFO %u(%u)x%u fmt %u\n", + Gop->Mode->Info->HorizontalResolution, + Gop->Mode->Info->PixelsPerScanLine, + Gop->Mode->Info->VerticalResolution, + Gop->Mode->Info->PixelFormat + ); + + if (Gop->Mode->Info->PixelFormat == PixelBitMask) { + OcAsciiPrintBuffer ( + &FileBuffer, + &FileBufferSize, + "GOP pix mask 0x%X:0x%X:0x%X:0x%X\n", + Gop->Mode->Info->PixelInformation.RedMask, + Gop->Mode->Info->PixelInformation.GreenMask, + Gop->Mode->Info->PixelInformation.BlueMask, + Gop->Mode->Info->PixelInformation.ReservedMask + ); + } + + OcAsciiPrintBuffer ( + &FileBuffer, + &FileBufferSize, + "GOP FB 0x%LX[0x%X]\n", + Gop->Mode->FrameBufferBase, + Gop->Mode->FrameBufferSize + ); + + Status = OcGopModeBytesPerPixel (Gop->Mode, &BytesPerPixel); + OcAsciiPrintBuffer ( + &FileBuffer, + &FileBufferSize, + "GOP BPP %u - %r\n", + BytesPerPixel, Status ); + + if (!EFI_ERROR (Status)) { + Status = OcGopModeSafeFrameBufferSize (Gop->Mode, &FrameBufferSize); + OcAsciiPrintBuffer ( + &FileBuffer, + &FileBufferSize, + "GOP FBS 0x%X%a - %r\n", + FrameBufferSize, + (Gop->Mode->FrameBufferSize != FrameBufferSize) ? " (FBS mismatch! Using this.)" : "", + Status + ); + } + } + + if (!EFI_ERROR (Status)) { + HasMtrr = IsMtrrSupported (); + HasPat = OcIsPatSupported (); + + OcAsciiPrintBuffer ( + &FileBuffer, + &FileBufferSize, + "MTRR %asupported PAT %asupported\n", + HasMtrr ? "" : "not ", + HasPat ? "" : "not " + ); + + if (HasMtrr) { + if (HasPat) { + PatMsr = AsmReadMsr64 (MSR_IA32_CR_PAT); + HasDefaultPat = (PatMsr == PAT_DEFAULTS); + + OcAsciiPrintBuffer ( + &FileBuffer, + &FileBufferSize, + "PAT 0x%016LX (%a)\n", + PatMsr, + HasDefaultPat ? "default" : "not default!" + ); + } + + PageTable = OcGetCurrentPageTable (NULL); + + VirtualAddress = Gop->Mode->FrameBufferBase; + EndAddress = VirtualAddress + FrameBufferSize; + + Status = EFI_SUCCESS; + + do { + MtrrCacheType = MtrrGetMemoryAttribute (VirtualAddress); + + if (HasPat) { + Status = OcGetSetPageTableInfoForAddress ( + PageTable, + VirtualAddress, + &PhysicalAddress, + &Level, + &Bits, + &PatIndex, + FALSE + ); + + if (EFI_ERROR (Status)) { + break; + } + + OcAsciiPrintBuffer ( + &FileBuffer, + &FileBufferSize, + "0x%LX~0x%LX MTRR %u=%a PTE%u bits 0x%016LX PAT@%u->%u=%a\n", + VirtualAddress, + PhysicalAddress, + MtrrCacheType, + OcGetMtrrTypeString (MtrrCacheType), + Level, + Bits, + PatIndex.Index, + GET_PAT_N (PatMsr, PatIndex.Index), + OcGetPatTypeString (GET_PAT_N (PatMsr, PatIndex.Index)) + ); + + if (VirtualAddress != PhysicalAddress) { + Status = EFI_UNSUPPORTED; + break; + } + } else { + Level = 2; + + OcAsciiPrintBuffer ( + &FileBuffer, + &FileBufferSize, + "0x%LX MTRR %u=%a\n", + VirtualAddress, + MtrrCacheType, + OcGetMtrrTypeString (MtrrCacheType) + ); + } + + switch (Level) { + case 1: + VirtualAddress += SIZE_1GB; + break; + + case 2: + VirtualAddress += SIZE_2MB; + break; + + case 4: + VirtualAddress += SIZE_4KB; + break; + + default: + Status = EFI_UNSUPPORTED; + break; + } + } while (!EFI_ERROR (Status) && VirtualAddress < EndAddress); + } + + if (EFI_ERROR (Status)) { + OcAsciiPrintBuffer ( + &FileBuffer, + &FileBufferSize, + "Failure reading page table! - %r\n", + Status + ); + } } // diff --git a/Library/OcConsoleLib/GopBurstMode.c b/Library/OcConsoleLib/GopUtils.c similarity index 57% rename from Library/OcConsoleLib/GopBurstMode.c rename to Library/OcConsoleLib/GopUtils.c index a203e7d5..892e3717 100644 --- a/Library/OcConsoleLib/GopBurstMode.c +++ b/Library/OcConsoleLib/GopUtils.c @@ -1,6 +1,5 @@ /** @file - Use PAT to enable write-combining caching (burst mode) on GOP memory, - when it is suppported but firmware has not set it up. + GOP buffer and pixel size utility methods, and GOP burst mode caching code. Copyright (C) 2023, Mike Beaton. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause @@ -18,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +25,104 @@ #define PAT_INDEX_TO_CHANGE 7 +EFI_STATUS +OcGopModeBytesPerPixel ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode, + OUT UINTN *BytesPerPixel + ) +{ + UINT32 MergedMasks; + + if ( (Mode == NULL) + || (Mode->Info == NULL)) + { + ASSERT ( + (Mode != NULL) + && (Mode->Info != NULL) + ); + return EFI_UNSUPPORTED; + } + + // + // This can occur without PixelBltOnly, including in rotated DirectGopRendering - + // see comment about PixelFormat in ConsoleGop.c RotateMode method. + // + if (Mode->FrameBufferBase == 0ULL) { + return EFI_UNSUPPORTED; + } + + switch (Mode->Info->PixelFormat) { + case PixelRedGreenBlueReserved8BitPerColor: + case PixelBlueGreenRedReserved8BitPerColor: + *BytesPerPixel = sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + return EFI_SUCCESS; + + case PixelBitMask: + break; + + case PixelBltOnly: + return EFI_UNSUPPORTED; + + default: + return EFI_INVALID_PARAMETER; + } + + MergedMasks = Mode->Info->PixelInformation.RedMask + || Mode->Info->PixelInformation.GreenMask + || Mode->Info->PixelInformation.BlueMask + || Mode->Info->PixelInformation.ReservedMask; + + if (MergedMasks == 0) { + return EFI_INVALID_PARAMETER; + } + + *BytesPerPixel = (UINT32)((HighBitSet32 (MergedMasks) + 7) / 8); + + return EFI_SUCCESS; +} + +EFI_STATUS +OcGopModeSafeFrameBufferSize ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode, + OUT UINTN *FrameBufferSize + ) +{ + EFI_STATUS Status; + UINTN BytesPerPixel; + + if ( (Mode == NULL) + || (Mode->Info == NULL)) + { + ASSERT ( + (Mode != NULL) + && (Mode->Info != NULL) + ); + return EFI_UNSUPPORTED; + } + + Status = OcGopModeBytesPerPixel (Mode, &BytesPerPixel); + + if (EFI_ERROR (Status)) { + return Status; + } + + if (OcOverflowTriMulUN ( + Mode->Info->PixelsPerScanLine, + Mode->Info->VerticalResolution, + BytesPerPixel, + FrameBufferSize + )) + { + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +// +// Use PAT to enable write-combining caching (burst mode) on GOP memory, +// when it is suppported but firmware has not set it up. +// STATIC EFI_STATUS WriteCombineGop ( @@ -37,6 +135,7 @@ WriteCombineGop ( PAGE_MAP_AND_DIRECTORY_POINTER *PageTable; EFI_VIRTUAL_ADDRESS VirtualAddress; EFI_PHYSICAL_ADDRESS PhysicalAddress; + UINTN FrameBufferSize; UINT64 Bits; UINT8 Level; BOOLEAN HasMtrr; @@ -51,6 +150,23 @@ WriteCombineGop ( return EFI_SUCCESS; } + if ( (Gop == NULL) + || (Gop->Mode == NULL) + || (Gop->Mode->Info == NULL)) + { + ASSERT ( + (Gop != NULL) + && (Gop->Mode != NULL) + && (Gop->Mode->Info != NULL) + ); + return EFI_UNSUPPORTED; + } + + Status = OcGopModeSafeFrameBufferSize (Gop->Mode, &FrameBufferSize); + if (EFI_ERROR (Status)) { + return Status; + } + HasMtrr = IsMtrrSupported (); HasPat = OcIsPatSupported (); @@ -81,8 +197,6 @@ WriteCombineGop ( MtrrCacheType = MtrrGetMemoryAttribute (Gop->Mode->FrameBufferBase); - DEBUG_CODE_BEGIN (); - VirtualAddress = Gop->Mode->FrameBufferBase; Status = OcGetSetPageTableInfoForAddress ( @@ -97,9 +211,8 @@ WriteCombineGop ( DEBUG (( DEBUG_INFO, - "OCC: 0x%LX->0x%LX MTRR %u=%a PTE%u bits 0x%016LX PAT %u->%u=%a - %r\n", + "OCC: 0x%LX MTRR %u=%a PTE%u bits 0x%016LX PAT@%u->%u=%a - %r\n", VirtualAddress, - PhysicalAddress, MtrrCacheType, OcGetMtrrTypeString (MtrrCacheType), Level, @@ -110,20 +223,27 @@ WriteCombineGop ( Status )); - ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + ASSERT (!AlreadySet); + return Status; + } + ASSERT (VirtualAddress == PhysicalAddress); if (AlreadySet) { break; } - DEBUG_CODE_END (); - // - // If MTRR is already set to WC, no need to set it via PAT. - // (Ignore implausible scenario where WC is set via MTRR but overridden via PAT.) + // Attempting to set again if set in PAT works on some systems (including if set + // before by us) but fails with a hang on some others, so avoid it even though we + // might otherwise prefer to make sure to set the whole memory for the current mode. + // Definitely no need to set again if set in MTRR. // - if (MtrrCacheType == CacheWriteCombining) { + if ( (MtrrCacheType == CacheWriteCombining) + || (GET_PAT_N (PatMsr, PatIndex.Index) == PatWriteCombining) + ) + { return EFI_ALREADY_STARTED; } @@ -161,7 +281,15 @@ WriteCombineGop ( } } - Status = OcSetPatIndexForAddressRange (PageTable, Gop->Mode->FrameBufferBase, Gop->Mode->FrameBufferSize, &PatIndex); + // + // TODO: Use full GOP memory range not just range in use for current mode? + // + Status = OcSetPatIndexForAddressRange ( + PageTable, + Gop->Mode->FrameBufferBase, + FrameBufferSize, + &PatIndex + ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "OCC: Failed to set PAT index for range - %r\n", Status)); @@ -197,7 +325,11 @@ OcSetGopBurstMode ( Status = WriteCombineGop (Gop, TRUE); if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_WARN, "OCC: Failed to set burst mode - %r\n", Status)); + DEBUG (( + (Status == EFI_ALREADY_STARTED) ? DEBUG_INFO : DEBUG_WARN, + "OCC: Failed to set burst mode - %r\n", + Status + )); } return Status; diff --git a/Library/OcConsoleLib/OcConsoleLib.inf b/Library/OcConsoleLib/OcConsoleLib.inf index 2804857f..5dc287ee 100644 --- a/Library/OcConsoleLib/OcConsoleLib.inf +++ b/Library/OcConsoleLib/OcConsoleLib.inf @@ -47,7 +47,7 @@ Eg2Info.c FramebufferInfo.c GopInfoDump.c - GopBurstMode.c + GopUtils.c GopPassThrough.c OcConsoleLib.c OcConsoleLibInternal.h