diff --git a/Changelog.md b/Changelog.md index 17390152..83168850 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,8 @@ OpenCore Changelog - Added Linux support to legacy boot BootInstall script - Updated builtin firmware versions for SMBIOS and the rest - Fixed incomplete console mode initialisation when started in graphics mode +- Provided additional UEFI forge mode, for use in firmware drivers +- Implemented firmware driver enabling pre-OpenCore graphics on non-natively supported GPUs on EFI-era Macs #### v0.8.8 - Updated underlying EDK II package to edk2-stable202211 diff --git a/Docs/Configuration.tex b/Docs/Configuration.tex index f1dfa2d0..6ddbc34c 100755 --- a/Docs/Configuration.tex +++ b/Docs/Configuration.tex @@ -3723,10 +3723,15 @@ the default boot entry choice will remain changed until the next manual reconfig \emph{Note 3}: On Macs with problematic GOP, it may be difficult to re-bless OpenCore if its bless status is lost. The \texttt{BootKicker} utility can be used to work around this problem, if set up as a Tool - in OpenCore (e.g. on a CDROM) with \texttt{FullNvramAccess} enabled. It will launch the Apple picker, - which allows selection of an item to boot next (with \texttt{Enter}), or next and subsequently, i.e. - as the blessed entry (with \texttt{CTRL+Enter}), as normal. After the selection is made, the system - will reboot and the chosen entry will be booted. + in OpenCore with \texttt{FullNvramAccess} enabled. It will launch the Apple picker, + which allows selection of an item to boot next (with \texttt{Enter}), or next and from then on until + the next change (with \texttt{CTRL+Enter}). Note that after the selection is made, the system + \emph{will reboot} before the chosen entry is booted. While this behaviour might seem surprising, + it can be used both to switch which OpenCore installation is blessed, with \texttt{CTRL+Enter}, + e.g. from a recovery OpenCore installation on CD (selected with the \texttt{C} key on boot) back + to the main installion of OpenCore on the hard drive, if this is lost after an NVRAM reset. It can + also be used, even when the native picker cannot be shown normally (unsupported GPU), to do a one-shot + boot without OpenCore, e.g. to another OS or tool, or to an earlier version of macOS. \item \texttt{PickerVariant}\\ @@ -6355,6 +6360,11 @@ even cause permanent firmware damage. Some of the known drivers are listed below any available writeable filesystem upon pressing \texttt{F10}. This is a modified version of \href{https://github.com/LongSoft/CrScreenshotDxe}{\texttt{CrScreenshotDxe}} driver by \href{https://github.com/NikolajSchlej}{Nikolaj Schlej}. \\ +\href{https://github.com/acidanthera/OpenCorePkg}{\texttt{EnableGop\{Direct\}}}\textbf{*} +& Early beta release firmware-embeddable driver providing pre-OpenCore non-native GPU support + on MacPro5,1. Installation instructions can be found in the + \href{https://github.com/acidanthera/OpenCorePkg/blob/master/Staging/EnableGop/README.md}{\texttt{Utilities/EnableGop}} + directory of the OpenCore release zip file - proceed with caution. \\ \href{https://github.com/acidanthera/OcBinaryData}{\texttt{ExFatDxe}} & Proprietary ExFAT file system driver for Bootcamp support commonly found in Apple firmware. For Sandy Bridge and earlier CPUs, the \texttt{ExFatDxeLegacy} driver should be diff --git a/Include/Acidanthera/Library/OcBootManagementLib.h b/Include/Acidanthera/Library/OcBootManagementLib.h index 1a6eb90f..f2e4bfdc 100644 --- a/Include/Acidanthera/Library/OcBootManagementLib.h +++ b/Include/Acidanthera/Library/OcBootManagementLib.h @@ -6,7 +6,8 @@ #ifndef OC_BOOT_MANAGEMENT_LIB_H #define OC_BOOT_MANAGEMENT_LIB_H -#include +#include +#include #include #include #include @@ -19,6 +20,8 @@ #include #include #include +#include +#include #if defined (OC_TARGET_DEBUG) || defined (OC_TARGET_NOOPT) // #define BUILTIN_DEMONSTRATE_TYPING @@ -2075,6 +2078,17 @@ OcAddEntriesFromBootEntryProtocol ( IN BOOLEAN CreateForHotKey ); +/** + Force Apple Firmware UI to always reconnect to current console GOP. + + @retval EFI_SUCCESS Firmware UI ConnectGop method was successfully reset. + @retval other Compatible firmware UI protocol for reset could not be found. +**/ +EFI_STATUS +OcUnlockAppleFirmwareUI ( + VOID + ); + /** Launch Apple boot picker firmware application. diff --git a/Include/Acidanthera/Library/OcDeviceMiscLib.h b/Include/Acidanthera/Library/OcDeviceMiscLib.h index dd8fdb71..c04ec8e9 100644 --- a/Include/Acidanthera/Library/OcDeviceMiscLib.h +++ b/Include/Acidanthera/Library/OcDeviceMiscLib.h @@ -73,11 +73,17 @@ OcPciInfoDump ( /** Upgrade UEFI version to 2.x. + @param[in] Forge If TRUE forge, else just report status. + @param[in] Trash If TRUE trash gBS->CreateEventEx directly, else reallocate gBS. + Reallocate strategy will only affect current image and any images + it loads. Trash strategy should affect all images. + @retval EFI_SUCCESS on success. **/ EFI_STATUS OcForgeUefiSupport ( - VOID + IN BOOLEAN Forge, + IN BOOLEAN Trash ); /** diff --git a/Library/OcBootManagementLib/OcBootManagementLib.c b/Library/OcBootManagementLib/OcBootManagementLib.c index d4a90faa..568f45ea 100644 --- a/Library/OcBootManagementLib/OcBootManagementLib.c +++ b/Library/OcBootManagementLib/OcBootManagementLib.c @@ -459,16 +459,30 @@ OcRunBootPicker ( } } +STATIC +EFI_STATUS +SetPickerEntryReason ( + IN APPLE_PICKER_ENTRY_REASON PickerEntryReason + ) +{ + return gRT->SetVariable ( + APPLE_PICKER_ENTRY_REASON_VARIABLE_NAME, + &gAppleVendorVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (PickerEntryReason), + &PickerEntryReason + ); +} + EFI_STATUS OcRunFirmwareApplication ( IN EFI_GUID *ApplicationGuid, IN BOOLEAN SetReason ) { - EFI_STATUS Status; - EFI_HANDLE NewHandle; - EFI_DEVICE_PATH_PROTOCOL *Dp; - APPLE_PICKER_ENTRY_REASON PickerEntryReason; + EFI_STATUS Status; + EFI_HANDLE NewHandle; + EFI_DEVICE_PATH_PROTOCOL *Dp; DEBUG ((DEBUG_INFO, "OCB: run fw app attempting to find %g...\n", ApplicationGuid)); @@ -493,14 +507,7 @@ OcRunFirmwareApplication ( if (!EFI_ERROR (Status)) { if (SetReason) { - PickerEntryReason = ApplePickerEntryReasonUnknown; - Status = gRT->SetVariable ( - APPLE_PICKER_ENTRY_REASON_VARIABLE_NAME, - &gAppleVendorVariableGuid, - EFI_VARIABLE_BOOTSERVICE_ACCESS, - sizeof (PickerEntryReason), - &PickerEntryReason - ); + Status = SetPickerEntryReason (ApplePickerEntryReasonUnknown); } DEBUG (( @@ -524,14 +531,86 @@ OcRunFirmwareApplication ( return Status; } +// +// Patching prolog of this function works on more similar era firmware +// than assuming that mGopAlreadyConnected is located immediately after +// protocol interface (which applies on MacPro5,1 v144.0.0.0.0 but not others). +// +// MacPro5,1 + some iMacs: +// +// sub rsp, 28h +// cmp cs:mGopAlreadyConnected, 0 ///< Ignore offset of this var +// jz short loc_10004431 +// xor eax, eax +// jmp short loc_1000446F ///< Change this to no jump +// +STATIC CONST UINT8 ConnectGopPrologue[] = { + 0x48, 0x83, 0xEC, 0x28, 0x80, 0x3D, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x74, 0x04, 0x33, 0xC0, 0xEB, + 0x3E +}; + +STATIC CONST UINT8 ConnectGopPrologueMask[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF +}; + +STATIC CONST UINT8 ConnectGopReplace[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 +}; + +STATIC CONST UINT8 ConnectGopReplaceMask[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF +}; + +// +// iMac11,1: +// +// push rbx +// sub rsp, 30h +// cmp cs:byte_100065C8, 0 +// jz short loc_10004077 +// xor ebx, ebx +// jmp short loc_100040D1 +// + +STATIC CONST UINT8 AltConnectGopPrologue[] = { + 0x48, 0x53, 0x48, 0x83, 0xEC, 0x30, + 0x80, 0x3D, 0x00, 0x00, 0x00, 0x00,0x00, + 0x74, 0x04, 0x33, 0xDB, 0xEB, 0x5A +}; + +STATIC CONST UINT8 AltConnectGopPrologueMask[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +STATIC CONST UINT8 AltConnectGopReplace[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +STATIC CONST UINT8 AltConnectGopReplaceMask[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF +}; + EFI_STATUS -OcLaunchAppleBootPicker ( +OcUnlockAppleFirmwareUI ( VOID ) { EFI_STATUS Status; APPLE_FIRMWARE_USER_INTERFACE_PROTOCOL *FirmwareUI; - UINT8 *aGopAlreadyConnected; + UINT32 ReplaceCount; Status = gBS->LocateProtocol ( &gAppleFirmwareUserInterfaceProtocolGuid, @@ -540,11 +619,11 @@ OcLaunchAppleBootPicker ( ); if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_INFO, "OC: Cannot locate FirmwareUI protocol - %r\n", Status)); + DEBUG ((DEBUG_INFO, "OCB: Cannot locate FirmwareUI protocol - %r\n", Status)); } else if (FirmwareUI->Revision != APPLE_FIRMWARE_USER_INTERFACE_PROTOCOL_REVISION) { DEBUG (( DEBUG_INFO, - "OC: Launch Apple picker incompatible FirmwareUI protocol revision %u != %u\n", + "OCB: Unlock FirmwareUI incompatible protocol revision %u != %u\n", FirmwareUI->Revision, APPLE_FIRMWARE_USER_INTERFACE_PROTOCOL_REVISION )); @@ -552,19 +631,63 @@ OcLaunchAppleBootPicker ( } if (!EFI_ERROR (Status)) { - // - // Location of relevant byte variable within loaded driver. - // - aGopAlreadyConnected = (VOID *)((UINT8 *)FirmwareUI + sizeof (APPLE_FIRMWARE_USER_INTERFACE_PROTOCOL)); + ReplaceCount = ApplyPatch ( + ConnectGopPrologue, + ConnectGopPrologueMask, + sizeof (ConnectGopPrologue), + ConnectGopReplace, + ConnectGopReplaceMask, + (VOID *)FirmwareUI->ConnectGop, + sizeof (ConnectGopPrologue), + 1, + 0 + ); - if (*aGopAlreadyConnected != 1) { - DEBUG ((DEBUG_WARN, "OC: Cannot force reconnect Apple GOP %u\n", *aGopAlreadyConnected)); - } else { - *aGopAlreadyConnected = 0; - DEBUG ((DEBUG_INFO, "OC: Force reconnect Apple GOP\n")); + if (ReplaceCount == 0) { + ReplaceCount = ApplyPatch ( + AltConnectGopPrologue, + AltConnectGopPrologueMask, + sizeof (AltConnectGopPrologue), + AltConnectGopReplace, + AltConnectGopReplaceMask, + (VOID *)FirmwareUI->ConnectGop, + sizeof (AltConnectGopPrologue), + 1, + 0 + ); } + + Status = EFI_SUCCESS; + if (ReplaceCount == 0) { + Status = EFI_NOT_FOUND; + DEBUG (( + DEBUG_INFO, + "OCB: 0x%016LX 0x%016LX 0x%016LX\n", + *((UINT64 *)((UINT8 *)FirmwareUI->ConnectGop)), + *((UINT64 *)(((UINT8 *)FirmwareUI->ConnectGop) + 8)), + *((UINT64 *)(((UINT8 *)FirmwareUI->ConnectGop) + 16)) + )); + } + + DEBUG (( + EFI_ERROR (Status) ? DEBUG_WARN : DEBUG_INFO, + "OCB: FirmwareUI ConnectGop patch - %r\n", + Status + )); } + return Status; +} + +EFI_STATUS +OcLaunchAppleBootPicker ( + VOID + ) +{ + EFI_STATUS Status; + + OcUnlockAppleFirmwareUI (); + Status = OcRunFirmwareApplication (&gAppleBootPickerFileGuid, TRUE); return Status; diff --git a/Library/OcDeviceMiscLib/ForgeUefi.c b/Library/OcDeviceMiscLib/ForgeUefi.c index 28726af8..f951fa79 100644 --- a/Library/OcDeviceMiscLib/ForgeUefi.c +++ b/Library/OcDeviceMiscLib/ForgeUefi.c @@ -1,6 +1,6 @@ /** @file Copyright (c) 2020, joevt. All rights reserved. - Copyright (C) 2021, vit9696. All rights reserved. + Copyright (C) 2021-2023, vit9696, mikebeaton. All rights reserved. All rights reserved. @@ -13,13 +13,14 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ -#include +#include #include #include #include #include +#include #include #include #include @@ -67,23 +68,46 @@ OcCreateEventEx ( return EFI_SUCCESS; } +// +// The Trash strategy relies on old Apple firmware allocating gBS and gDS consecutively. +// This layout is directly inherited from standard edk EFI code. +// The strategy trashes the DXE_SERVICES_SIGNATURE value in gDS->Hdr.Signature, which +// happily is only used when the memory is being loaded (when we check for references +// to DXE_SERVICES_SIGNATURE throughout the edk code). +// For the Trash strategy to work, we are required to trash exactly that QWORD of memory, +// but in the targeted firmware we can confirm that it is harmless to do so before proceeding. +// EFI_STATUS OcForgeUefiSupport ( - VOID + IN BOOLEAN Forge, + IN BOOLEAN Trash ) { EFI_BOOT_SERVICES *NewBS; DEBUG (( DEBUG_INFO, - "OCDM: Found 0x%X UEFI version (%u bytes, rebuilding to %u)\n", + "OCDM: Found 0x%X/0x%X UEFI version (%u bytes, %u %a to %u) gST %p gBS %p gBS->CreateEventEx %p &gBS %p\n", gST->Hdr.Revision, + gBS->Hdr.Revision, gBS->Hdr.HeaderSize, - (UINT32)sizeof (EFI_BOOT_SERVICES) + Forge, + Trash ? "trashing" : "rebuilding", + (UINT32)sizeof (EFI_BOOT_SERVICES), + gST, + gBS, + gBS->CreateEventEx, + &gBS )); + if (!Forge) { + return EFI_SUCCESS; + } + // // Already too new. + // This check will replace any earlier forge to 2.0 <= UEFI < 2.3. + // This is desirable in some cases and harmless in others. // if (gST->Hdr.Revision >= EFI_2_30_SYSTEM_TABLE_REVISION) { return EFI_ALREADY_STARTED; @@ -93,13 +117,29 @@ OcForgeUefiSupport ( return EFI_INVALID_PARAMETER; } - NewBS = AllocateZeroPool (sizeof (EFI_BOOT_SERVICES)); - if (NewBS == NULL) { - DEBUG ((DEBUG_INFO, "OCDM: Failed to allocate BS copy\n")); - return EFI_OUT_OF_RESOURCES; - } + if (Trash) { + if ((VOID *)&gBS->CreateEventEx != (VOID *)gDS) { + DEBUG (( + DEBUG_WARN, + "OCDM: Aborting trash strategy, gDS does not follow gBS\n" + )); + return EFI_UNSUPPORTED; + } - CopyMem (NewBS, gBS, gBS->Hdr.HeaderSize); + DEBUG (( + DEBUG_INFO, + "OCDM: Trashing gDS->Hdr.Signature with gBS->CreateEventEx\n" + )); + NewBS = gBS; + } else { + NewBS = AllocateZeroPool (sizeof (EFI_BOOT_SERVICES)); + if (NewBS == NULL) { + DEBUG ((DEBUG_INFO, "OCDM: Failed to allocate BS copy\n")); + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (NewBS, gBS, gBS->Hdr.HeaderSize); + } NewBS->CreateEventEx = OcCreateEventEx; NewBS->Hdr.HeaderSize = sizeof (EFI_BOOT_SERVICES); @@ -113,5 +153,10 @@ OcForgeUefiSupport ( gST->Hdr.CRC32 = 0; gST->Hdr.CRC32 = CalculateCrc32 (gST, gST->Hdr.HeaderSize); + if (Trash) { + gDS->Hdr.CRC32 = 0; + gDS->Hdr.CRC32 = CalculateCrc32 (gDS, gDS->Hdr.HeaderSize); + } + return EFI_SUCCESS; } diff --git a/Library/OcDeviceMiscLib/OcDeviceMiscLib.inf b/Library/OcDeviceMiscLib/OcDeviceMiscLib.inf index ec0f7fa1..a6a644f2 100755 --- a/Library/OcDeviceMiscLib/OcDeviceMiscLib.inf +++ b/Library/OcDeviceMiscLib/OcDeviceMiscLib.inf @@ -62,6 +62,7 @@ PrintLib UefiLib UefiBootServicesTableLib + DxeServicesTableLib [Sources] ActivateHpetSupport.c diff --git a/Library/OcMainLib/OpenCoreUefi.c b/Library/OcMainLib/OpenCoreUefi.c index 5f0c503e..a4550fcd 100644 --- a/Library/OcMainLib/OpenCoreUefi.c +++ b/Library/OcMainLib/OpenCoreUefi.c @@ -939,9 +939,7 @@ OcLoadUefiSupport ( OcInstallPermissiveSecurityPolicy (); } - if (Config->Uefi.Quirks.ForgeUefiSupport) { - OcForgeUefiSupport (); - } + OcForgeUefiSupport (Config->Uefi.Quirks.ForgeUefiSupport, FALSE); if (Config->Uefi.Quirks.ReloadOptionRoms) { OcReloadOptionRoms (); diff --git a/OpenCorePkg.dec b/OpenCorePkg.dec index 24c16819..6832118e 100755 --- a/OpenCorePkg.dec +++ b/OpenCorePkg.dec @@ -744,6 +744,12 @@ ## @Prompt Use Pin Capabilities to identify audio outputs. gOpenCorePkgTokenSpaceGuid.PcdAudioControllerUsePinCapsForOutputs|TRUE|BOOLEAN|0x00000008 + ## Indicates whether to build EnableGop driver with DirectGopRendering.

+ ## TRUE - DirectGopRendering will be used.
+ ## FALSE - DirectGopRendering will not be used.
+ ## @Prompt Use DirectGopRendering. + gOpenCorePkgTokenSpaceGuid.PcdEnableGopDirect|FALSE|BOOLEAN|0x00000009 + [PcdsFixedAtBuild] ## Defines the Console Control initialization mode set on entry.

## 0 - EfiConsoleControlScreenText
diff --git a/OpenCorePkg.dsc b/OpenCorePkg.dsc index 86484eb8..344f6ec4 100755 --- a/OpenCorePkg.dsc +++ b/OpenCorePkg.dsc @@ -22,6 +22,7 @@ BUILD_TARGETS = RELEASE|DEBUG|NOOPT SKUID_IDENTIFIER = DEFAULT DSC_SPECIFICATION = 0x00010006 + FLASH_DEFINITION = OpenCorePkg/OpenCorePkg.fdf # # Network definition @@ -319,6 +320,14 @@ OpenCorePkg/Platform/ResetNvramEntry/ResetNvramEntry.inf OpenCorePkg/Platform/ToggleSipEntry/ToggleSipEntry.inf OpenCorePkg/Staging/AudioDxe/AudioDxe.inf + OpenCorePkg/Staging/EnableGop/EnableGop.inf { + + DebugLib|OpenCorePkg/Library/OcDebugLibNull/OcDebugLibNull.inf + } + OpenCorePkg/Staging/EnableGop/EnableGopDirect.inf { + + DebugLib|OpenCorePkg/Library/OcDebugLibNull/OcDebugLibNull.inf + } OpenCorePkg/Staging/OpenHfsPlus/OpenHfsPlus.inf OpenCorePkg/Tests/AcpiTest/AcpiTest.inf OpenCorePkg/Tests/AcpiTest/AcpiTestApp.inf diff --git a/OpenCorePkg.fdf b/OpenCorePkg.fdf new file mode 100644 index 00000000..447a986a --- /dev/null +++ b/OpenCorePkg.fdf @@ -0,0 +1,105 @@ +## @file +# Compile standalone firmware filesystem files. +# +# Copyright (C) 2022-2023, Mike Beaton. All rights reserved.
+# SPDX-License-Identifier: BSD-3-Clause +## + +# +# For now disable non-64-bit build; Mac Pro cannot use it, and EDK-II +# build system does not separate 64 bit and 32 bit .ffs files, which +# we want to distribute. +# +# If we needed all archs in future we could distribute the complete +# .fv files, from which the .ffs can be re-extracted. +# +!if ($(ARCH) == X64) + +!if ($(ARCH) == X64) + [FV.FfsFilesX64] +!else + [FV.FfsFilesIA32] +!endif + + BlockSize = 0x10000 + NumBlocks = 8 + FvAlignment = 16 #FV alignment and FV attributes setting. + ERASE_POLARITY = 1 + MEMORY_MAPPED = TRUE + STICKY_WRITE = TRUE + LOCK_CAP = TRUE + LOCK_STATUS = TRUE + WRITE_DISABLED_CAP = TRUE + WRITE_ENABLED_CAP = TRUE + WRITE_STATUS = TRUE + WRITE_LOCK_CAP = TRUE + WRITE_LOCK_STATUS = TRUE + READ_DISABLED_CAP = TRUE + READ_ENABLED_CAP = TRUE + READ_STATUS = TRUE + READ_LOCK_CAP = TRUE + READ_LOCK_STATUS = TRUE + + # + # DXE Drivers (other .inf files for conversion to .ffs may be added here) + # + INF OpenCorePkg/Staging/EnableGop/EnableGop.inf + +!if ($(ARCH) == X64) + [FV.EnableGopDirectX64] +!else + [FV.EnableGopDirectIA32] +!endif + + BlockSize = 0x10000 + NumBlocks = 8 + FvAlignment = 16 #FV alignment and FV attributes setting. + ERASE_POLARITY = 1 + MEMORY_MAPPED = TRUE + STICKY_WRITE = TRUE + LOCK_CAP = TRUE + LOCK_STATUS = TRUE + WRITE_DISABLED_CAP = TRUE + WRITE_ENABLED_CAP = TRUE + WRITE_STATUS = TRUE + WRITE_LOCK_CAP = TRUE + WRITE_LOCK_STATUS = TRUE + READ_DISABLED_CAP = TRUE + READ_ENABLED_CAP = TRUE + READ_STATUS = TRUE + READ_LOCK_CAP = TRUE + READ_LOCK_STATUS = TRUE + + # + # DXE Drivers + # + + # + # EnableGopDirect intentionally has the same GUID as EnableGop, so it must go in a separate FV. + # (We don't care about the FV, rather the intermediate FFS files which are generated during build.) + # + INF OpenCorePkg/Staging/EnableGop/EnableGopDirect.inf + +!endif + +# +# Ideally we would match the compressed layout of DXE drivers in Mac Pro 144.0.0.0.0 firmware, +# as per the commented out lines, but Mac Pro will not load this with standard compression. +# With lines commented out we make a different, non-compressed layout which the Mac Pro will load. +# +# TODO: Is there a way to make a Tiano-compressed COMPRESS section, as in Mac Pro firmware, using +# EDK-II build tools? If we manually reconstuct one with UEFITool 0.25.1, Mac Pro will load it. +# +# Note: `GUIDED A31280AD-481E-41B6-95E8-127F4C984779` will make a Tiano compressed GUIDED +# section, but this is different and 144.0.0.0.0 will not load it. +# +[Rule.Common.DXE_DRIVER] + FILE DRIVER = $(NAMED_GUID) Checksum { +# COMPRESS { +# GUIDED { + DXE_DEPEX DXE_DEPEX Optional $(INF_OUTPUT)/$(MODULE_NAME).depex + PE32 PE32 $(INF_OUTPUT)/$(MODULE_NAME).efi + UI STRING="$(MODULE_NAME)" Optional +# } +# } + } diff --git a/Staging/EnableGop/EnableGop.c b/Staging/EnableGop/EnableGop.c new file mode 100644 index 00000000..971d77d0 --- /dev/null +++ b/Staging/EnableGop/EnableGop.c @@ -0,0 +1,132 @@ +/** @file + Provide GOP on unsupported graphics cards on EFI-era MacPro and iMac. + + Copyright (c) 2022-2023, Mike Beaton. All rights reserved.
+ SPDX-License-Identifier: BSD-3-Clause +**/ + +#include + +#include +#include +#include + +#include +#include +#include + +STATIC EFI_GET_MEMORY_SPACE_MAP mOriginalGetMemorySpaceMap; + +// +// Equivalent to a very cut down OcLoadUefiOutputSupport. +// +STATIC +EFI_STATUS +LoadUefiOutputSupport ( + VOID + ) +{ + EFI_STATUS Status; + + Status = OcProvideConsoleGop (FALSE); + if (EFI_ERROR (Status)) { + return Status; + } + + OcSetConsoleResolution ( + 0, + 0, + 0, + FALSE + ); + + if (FeaturePcdGet (PcdEnableGopDirect)) { + OcUseDirectGop (-1); + } + + OcSetupConsole ( + OcConsoleRendererBuiltinGraphics, + FALSE, + FALSE, + FALSE, + FALSE + ); + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +ProvideGop ( + VOID + ) +{ + OcUnlockAppleFirmwareUI (); + + return LoadUefiOutputSupport (); +} + +// +// This memory map access happens twice during PlatformBdsPolicyBehavior, once +// in the equivalent of efi InitializeMemoryTest at the start of the function, +// and once during PlatformBdsDiagnostics, slightly later. The second call(s) +// (there is more than one code path, depending on the boot type) are after +// the default console has been connected, and therefore ideal for us. +// +EFI_STATUS +EFIAPI +WrappedGetMemorySpaceMap ( + OUT UINTN *NumberOfDescriptors, + OUT EFI_GCD_MEMORY_SPACE_DESCRIPTOR **MemorySpaceMap + ) +{ + STATIC UINTN mGetMemorySpaceMapAccessCount = 0; + + mGetMemorySpaceMapAccessCount++; + + if (mGetMemorySpaceMapAccessCount == 2) { + ProvideGop (); + } + + return mOriginalGetMemorySpaceMap ( + NumberOfDescriptors, + MemorySpaceMap + ); +} + +STATIC +VOID +WrapGetMemorySpaceMap ( + VOID + ) +{ + mOriginalGetMemorySpaceMap = gDS->GetMemorySpaceMap; + gDS->GetMemorySpaceMap = WrappedGetMemorySpaceMap; + + gDS->Hdr.CRC32 = 0; + gBS->CalculateCrc32 (gDS, gDS->Hdr.HeaderSize, &gDS->Hdr.CRC32); +} + +// +// If we execute the entire console setup at Driver#### time then we have to forge UEFI, reload option ROMs +// and connect them, all at that point. +// This works on some systems but causes crashes others, or causes an empty picker with question mark folder. +// Current strategy is: +// - Forge UEFI early; relatively easy: just do it immediately, as below, and insert this driver either +// a) anywhere in main firmware volume, or b) in VBIOS anywhere before GOP driver which needs it +// - Execute rest of payload after option roms have been loaded, _and_ after firmware has already connected +// them (see WrapGetMemorySpaceMap strategy above). +// With this strategy we do not need to reload or reconnect any option ROMs, which is much more stable. +// +EFI_STATUS +EFIAPI +UefiMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + OcForgeUefiSupport (TRUE, TRUE); + WrapGetMemorySpaceMap (); + + return EFI_SUCCESS; +} diff --git a/Staging/EnableGop/EnableGop.inf b/Staging/EnableGop/EnableGop.inf new file mode 100644 index 00000000..ee226132 --- /dev/null +++ b/Staging/EnableGop/EnableGop.inf @@ -0,0 +1,46 @@ +## @file +# +# Provide GOP on unsupported graphics cards on EFI-era MacPro and iMac. +# +# Copyright (c) 2022-2023, Mike Beaton. All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = EnableGop + FILE_GUID = 3FBA58B1-F8C0-41BC-ACD8-253043A3A17F + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = UefiMain + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + EnableGop.c + +[Packages] + MdePkg/MdePkg.dec + OpenCorePkg/OpenCorePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DxeServicesTableLib + OcBootManagementLib + OcConsoleLib + OcDeviceMiscLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + +[FeaturePcd] + gOpenCorePkgTokenSpaceGuid.PcdEnableGopDirect|FALSE + +[Depex] + TRUE diff --git a/Staging/EnableGop/EnableGopDirect.inf b/Staging/EnableGop/EnableGopDirect.inf new file mode 100644 index 00000000..7e3da32e --- /dev/null +++ b/Staging/EnableGop/EnableGopDirect.inf @@ -0,0 +1,46 @@ +## @file +# +# Provide GOP on unsupported graphics cards on EFI-era MacPro and iMac using DirectGopRendering. +# +# Copyright (c) 2023, Mike Beaton. All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = EnableGopDirect + FILE_GUID = 3FBA58B1-F8C0-41BC-ACD8-253043A3A17F + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = UefiMain + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + EnableGop.c + +[Packages] + MdePkg/MdePkg.dec + OpenCorePkg/OpenCorePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DxeServicesTableLib + OcBootManagementLib + OcConsoleLib + OcDeviceMiscLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + +[FeaturePcd] + gOpenCorePkgTokenSpaceGuid.PcdEnableGopDirect|TRUE + +[Depex] + TRUE diff --git a/Staging/EnableGop/NvInsertEfi.sh b/Staging/EnableGop/NvInsertEfi.sh new file mode 100755 index 00000000..633fe579 --- /dev/null +++ b/Staging/EnableGop/NvInsertEfi.sh @@ -0,0 +1,117 @@ +#!/bin/bash + +# +# Copyright © 2023 Mike Beaton. All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +# +# Insert EFI into Nvidia VBIOS. +# +# TODO: Check that original GOP is present, and locate it in the original file automatically. +# + +usage() { + echo "Usage: ./${SELFNAME} {rom-file} {efi-file} {GOP offset} {out-file}" + echo "E.g.:" + echo " ./${SELFNAME} nv.rom GOP.efi 0xFC00 mod.rom" + echo " ./${SELFNAME} nv.rom GOP.efi 64512 mod.rom" + echo "" +} + +SELFNAME="$(/usr/bin/basename "${0}")" + +if [ "$#" -ne 4 ] ; then + usage + exit 0 +fi + +commands=( + "EfiRom" + "UEFIRomExtract" +) +FOUND=1 +for command in "${commands[@]}"; do + if ! command -v "$command" 1>/dev/null ; then + echo "${command} not available!" + FOUND=0 + fi +done + +if [ "$FOUND" -eq 0 ] ; then + exit 1 +fi + +ROM_FILE="$1" +EFI_FILE="$2" +GOP_OFFSET="$3" +OUT_FILE="$4" + +# https://unix.stackexchange.com/a/84980/340732 +tmpdir=$(mktemp -d 2>/dev/null || mktemp -d -t 'vbios') || exit 1 + +echo "Splitting original ROM..." + +dd bs=1 if="$ROM_FILE" of="$tmpdir/original_first_part.rom" count=$(($GOP_OFFSET)) status=none || exit 1 +dd bs=1 if="$ROM_FILE" of="$tmpdir/original_last_part.rom" skip=$(($GOP_OFFSET)) status=none || exit 1 + +echo "Compressing ${EFI_FILE} using EfiRom..." +EfiRom -o "$tmpdir/insert.rom" -ec "$EFI_FILE" -f 0xAAAA -i 0xBBBB -l 0x30000 || exit 1 + +echo "Adding Nvidia header..." +dd bs=1 if="$tmpdir/insert.rom" of="$tmpdir/insert_first_part" count=$((0x38)) status=none || exit 1 +dd bs=1 if="$tmpdir/insert.rom" of="$tmpdir/insert_last_part" skip=$((0x38)) status=none || exit 1 + +INSERT_SIZE=$(stat -f%z "$tmpdir/insert.rom") || exit 1 + +# add NPDE from original GOP +dd bs=1 if="$tmpdir/original_last_part.rom" of="$tmpdir/insert_first_part" skip=$((0x38)) seek=$((0x38)) count=$((0x18)) status=none || exit 1 +cat "$tmpdir/insert_first_part" "$tmpdir/insert_last_part" > "$tmpdir/insert_oversize.rom" || exit 1 +# `truncate` not present by default on macOS +dd bs=1 if="$tmpdir/insert_oversize.rom" of="$tmpdir/insert_fixed.rom" count="$INSERT_SIZE" status=none || exit 1 + +# patch size in NPDE +dd bs=1 if="$tmpdir/insert.rom" of="$tmpdir/insert_fixed.rom" skip=$((0x2)) seek=$((0x48)) count=1 conv=notrunc status=none || exit 1 + +# patch with vendor and device id from original GOP +dd bs=1 if="$tmpdir/original_last_part.rom" of="$tmpdir/insert_fixed.rom" skip=$((0x20)) seek=$((0x20)) count=4 conv=notrunc status=none || exit 1 + +# patch size in PCIR +dd bs=1 if="$tmpdir/original_last_part.rom" of="$tmpdir/insert_fixed.rom" skip=$((0x16)) seek=$((0x16)) count=1 conv=notrunc status=none || exit 1 + +# patch end marker in NPDE in fixed ROM (leave PCIR correct and EFI extractable from fixed ROM) +echo -n -e '\x00' | dd bs=1 of="$tmpdir/insert_fixed.rom" seek=$((0x4A)) conv=notrunc status=none || exit 1 + +echo "Writing ${OUT_FILE}..." +cat "$tmpdir/original_first_part.rom" "$tmpdir/insert_fixed.rom" "$tmpdir/original_last_part.rom" > "$OUT_FILE" || exit 1 + +# patch end marker in PCIR in out file +echo -n -e '\x00' | dd bs=1 of="$OUT_FILE" seek=$(($GOP_OFFSET + 0x31)) conv=notrunc status=none || exit 1 + +echo "Verifying ${OUT_FILE}..." +dd bs=1 if="$OUT_FILE" of="$tmpdir/out_efi_part.rom" skip=$(($GOP_OFFSET)) status=none || exit 1 +UEFIRomExtract "$tmpdir/out_efi_part.rom" "$tmpdir/extracted.efi" 1>/dev/null || exit 1 +ERROR=0 +diff "$tmpdir/extracted.efi" "$EFI_FILE" 1>/dev/null || ERROR=1 + +if [ "$ERROR" -ne 0 ] ; then + echo " - Failure comparing extracted EFI to original!" +fi + +OLD_EFI_COUNT=$(EfiRom -d "$tmpdir/original_last_part.rom" | grep "0x0EF1" | wc -l) || exit 1 +OLD_EFI_COUNT=$(($OLD_EFI_COUNT)) || exit 1 + +NEW_EFI_COUNT=$(EfiRom -d "$tmpdir/out_efi_part.rom" | grep "0x0EF1" | wc -l) || exit 1 +NEW_EFI_COUNT=$(($NEW_EFI_COUNT)) || exit 1 + +if [ "$NEW_EFI_COUNT" -ne $(($OLD_EFI_COUNT + 1)) ] ; then + echo " - Found ${NEW_EFI_COUNT} EFI parts, expected $(($OLD_EFI_COUNT + 1))!" +fi + +if [ "$ERROR" -eq 0 ] ; then + echo "SUCCESS." +else + echo "*** WARNING - FAIL ***" +fi + +rm -rf "$tmpdir" || exit 1 + +echo "Done." diff --git a/Staging/EnableGop/README.md b/Staging/EnableGop/README.md new file mode 100644 index 00000000..6b133120 --- /dev/null +++ b/Staging/EnableGop/README.md @@ -0,0 +1,154 @@ +# Provides standalone GOP driver for EFI era Mac Pro and iMac + +## Status +**Current status: Early beta release.** + +This driver has been tested and is working on several iMac models +with several different GPUs, and on several MacPro4,1/5,1 machines with several different GPUs. However, in the worst +case (and still quite possible) scenario, an incompatible or incorrectly installed driver +in firmware may brick your hardware. + +*In all cases take a backup of the main firmware or VBIOS firmware which you are modifying, and confirm that +you can successfully restore from this, before starting.* + +## Recovery from bricked hardware + - If attempting firmware insertion on a MacPro4,1/5,1, for recovery from a bricked device you will either +need a Matt card (which may breach intellectual property laws in some jurisdictions) or the ability to +desolder and reprogram your own NVRAM chip. + - If testing via firmware insertion on an iMac, you will need the ability to disassemble your iMac and +reprogram its NVRAM chip using a SOIC clip attached to a CH341A controller running on another computer. + - If testing via VBIOS insertion (iMac or Mac Pro), you will need the ability to disassemble your system, +likely remove the heat sink from the graphics card, and then reprogram its NVRAM chip using a SOIC +clip attached to a CH341A controller running on another computer. + - If testing via VBIOS insertion, in some cases it may also be possible +to use physical electrical connection to your GPU NVRAM chip in order to boot with no graphics, then connect +to your machine with `ssh` (which must have been enabled beforehand) and reprogram the GPU NVRAM. Advice on +this is not provided here, but may be found for instance on the iMac GPU related forum threads listed below. + +*If you are not familiar with the above procedures, you are strongly recommended to wait for further testing by +users who are. No further help can be provided here, and you proceed entirely at your own risk.* + +## Summary +Targetting EFI-era (~2009-2012) MacPro4,1/5,1 and iMac firmware, this driver gathers and injects the parts of +OpenCore needed for pre-boot graphics support with non-natively supported GPUs. + +The requirements for using this driver are: + + - EFI-era (~2009-2012) MacPro4,1/5,1 or iMac with most recent firmware. + - A GPU which does not produce native pre-boot graphics (such as native picker when pressing ALT key during boot) + before OpenCore starts (otherwise, you do not need it). + - A GPU which produces graphics when using OpenCore (this must include successfully showing the native Apple boot + picker when started via the latest version of OpenCore tool `BootKicker.efi`) (otherwise, the driver will not work). + - *Note*: If your OpenCore installation includes a required GOP driver for your graphics card (this is added + automatically on some systems by recent versions of OpenCore Legacy Patcher, as a way to enable OpenCore menu + in cards such as ex-mining GPUs), then you would also need to burn that driver to the VBIOS of your graphics + card in order to obtain pre-OpenCore graphics; instructions for this are outside the scope of this tutorial, + although the procedures required for modifying VBIOS are similar to what is covered here. + +When installed, the driver should enable: + + - Native boot picker via ALT key + - Firmware password UI + - Target disk mode UI + - macOS boot progress screen + - etc. + +Compiled versions of the driver files and these instructions may be found in the `Utilities/EnableGop` +directory of the OpenCore release package. + +For GPUs needing `DirectGopRendering` in OpenCore configuration, use `EnableGopDirect.efi`, otherwise use `EnableGop.efi` +as it renders faster on most other systems. + +The driver may be installed to VBIOS or to main firmware. It is expected that most Mac Pro users will use firmware insertion +and most iMac users will chose VBIOS insertion, however both techniques work on both systems (but it is harder to modify the +iMac firmware). + +## Usage + +## Install to firmware + +For reading and writing to firmware on the Mac Pro, @Macschrauber's [Rom Dump](https://www.youtube.com/watch?v=q4NW00oyUKE) works +well. Alternatively the kexts and executables which this uses can be sourced individually (or extracted from the Rom Dump app) and +run from the command line. + +The firmware on the iMac cannot be updated without an initial hardware flash (SOIC clip plus CH341A controller), therefore +the recommended approach on iMac systems is [VBIOS injection](#install-to-vbios). However, the below instructions for firmware +injection do work, if you are willing to do a hardware flash of the resulting firmware file, or if you have already +[unprotected your iMac firmware](https://forums.macrumors.com/threads/imac-2011-see-more-uefi-firmware-mod.2257435/page-3?post=31087001#post-31087001) - +which reduces security, and is only recommended for those actively developing firmware modifications. + +The `.ffs` file provided in this directory can be manually added to the extracted firmware file using [`UEFITool`](https://github.com/LongSoft/UEFITool), +or automatically added using @dosdude1's [`DXEInject`](https://dosdude1.com/apps/). Once more, if you are not familiar with these procedures, +you are recommended to proceed with extreme caution. + +### Using DXEInject + +To install the driver via `DXEInject`, the command is: + + - `DXEInject {original}.rom {modified}.rom EnableGop.ffs` + +The file `{modifed}.rom` is ready for burning, although the result can be checked using UEFITool, if required. + +> *Note*: If only reading a file with UEFITool, the latest version is recommended, as it provides the most information. +For writing, the older version 0.25.1 must be used, as described below. + +### Using UEFITool + +The `.ffs` file may be inserted anywhere within the same firmware volume which contains `DuetBds` +(file GUID `A6F691AC-31C8-4444-854C-E2C1A6950F92`). However, for simplicity, these instructions +will insert it in the same place that `DXEInject` does: + + - Use UEFITool 0.25.1 (it must be that old version, not the newer NE or new engine versions, which +cannot yet edit) + - Perform a header-only GUID search for `BAE7599F-3C6B-43B7-BDF0-9CE07AA91AA6` + - Double-click on the search result + - Right-click on the DXE driver with that GUID which should then appear + - Choose "Insert after..." and select `EnableGop.ffs` + - Save the modified firmware + +The end result, after saving and re-loading, should look like this: + + + +## Install to VBIOS + +Instructions and a script for inserting the driver into NVidia VBIOS are provided. +Similar techniques are appropriate for AMD GPUs. + +For further information on VBIOS modification, see: + + - https://forums.macrumors.com/threads/2011-imac-graphics-card-upgrade.1596614/ + - https://forums.macrumors.com/threads/imac-2011-maxwell-and-pascal-gpu-upgrade.2300989/ + - https://github.com/Ausdauersportler/IMAC-EFI-BOOT-SCREEN/wiki + - https://winraid.level1techs.com/t/amd-and-nvidia-gop-update-no-requests-diy/30917 + +### Nvidia + +To use the provided `NvInsertEfi.sh` script: + + - Locate an appropriate version of the `nvflash` tool (avilable for Linux and Windows), which can be used to read + from and write to your Nvidia GPU VBIOS. + - Use `nvflash` to read a copy of your VBIOS. + - Using a hex editor, search in the VBIOS for the byte sequence `F1 0E 00 00` with the byte sequence `55 AA` coming + close before it; the start address of the `55 AA` is the value needed for the insertion offset in the next step. + - Run `./NvInsertEfi.Sh {original}.rom EnableGop.ffs {offset} {modified}/.rom`. + - Only if you modified a rom file obtained via `flashrom` rather than `nvflash`, you should manually truncate + the modified file's size to the original file's size at this point. (`nvflash` files only contain the used section + of the VBIOS, and therefore must not be truncated.) + - The new file `{modified}.rom` may be burnt to the VBIOS firmware. + + Please note all the cautions already given above about the difficulty of recovering, unless you are familiar with + the procedures necessary, if this process fails. + +### AMD + +Similar procedures as for Nvidia apply. + + - Further assistance and information may be available in the forums and pages listed above. + +## No longer supported: Install as Driver#### entry + +Early test versions of this driver included code to allow it to work when installed as a `Driver####` entry +(`Driver####` entries do work when installed via `bcfg` and using MacPro4,1/5,1 firmware; they do not work on +that firmware if using `efibootmgr`, and they are not supported by iMac firmware). This code has been removed +in order to minimise the driver size for VBIOS insertion. diff --git a/Staging/EnableGop/UEFITool_Inserted_Screenshot.png b/Staging/EnableGop/UEFITool_Inserted_Screenshot.png new file mode 100644 index 00000000..cc42985b Binary files /dev/null and b/Staging/EnableGop/UEFITool_Inserted_Screenshot.png differ diff --git a/Staging/README.md b/Staging/README.md index 77aa9f6f..57f4fab4 100755 --- a/Staging/README.md +++ b/Staging/README.md @@ -1,8 +1,11 @@ Staging drivers considered experimental and not production ready. -## OpenHfsPlus -This driver implements HFS+ support with bless extensions. - ## AudioDxe Improved audio driver (currently only Intel HD audio). HDMI or other digital outputs don't work. + +## EnableGop +Standalone firmware-embeddable driver for non-native GPU support on EFI-era Mac Pro and iMac. + +## OpenHfsPlus +This driver implements HFS+ support with bless extensions. diff --git a/build_oc.tool b/build_oc.tool index 43955930..9301dbf6 100755 --- a/build_oc.tool +++ b/build_oc.tool @@ -249,6 +249,32 @@ package() { fi done + # Copy Mac Pro GOP firmware driver. + mkdir -p "${dstdir}/Utilities/EnableGop" || exit 1 + ENABLE_GOP_GUID="3FBA58B1-F8C0-41BC-ACD8-253043A3A17F" + ffsNames=( + "EnableGop" + "EnableGopDirect" + ) + for ffsName in "${ffsNames[@]}"; do + cp "FV/Ffs/${ENABLE_GOP_GUID}${ffsName}/${ENABLE_GOP_GUID}.ffs" "${dstdir}/Utilities/EnableGop/${ffsName}.ffs" || exit 1 + done + gopDrivers=( + "EnableGop.efi" + "EnableGopDirect.efi" + ) + for file in "${gopDrivers[@]}"; do + cp "X64/${file}" "${dstdir}/Utilities/EnableGop"/ || exit 1 + done + helpFiles=( + "README.md" + "UEFITool_Inserted_Screenshot.png" + "NvInsertEfi.sh" + ) + for file in "${helpFiles[@]}"; do + cp "${selfdir}/Staging/EnableGop/${file}" "${dstdir}/Utilities/EnableGop"/ || exit 1 + done + utils=( "ACPIe" "acdtinfo"