From 45f8ba2cd28432be176aa54a54d1fe844c63122d Mon Sep 17 00:00:00 2001 From: John Davis Date: Sun, 23 Feb 2025 10:51:40 -0600 Subject: [PATCH] Add ClearTaskSwitchBit booter quirk (#573) This quirk is needed to boot macOS 10.7 and older when using a 32-bit kernel on a 64-bit UEFI firmware that makes uses of FPU or SSE instructions in runtime services (such as Hyper-V). --- Docs/Configuration.tex | 16 +++ Docs/Sample.plist | 2 + Docs/SampleCustom.plist | 2 + .../Library/OcAfterBootCompatLib.h | 5 + .../Acidanthera/Library/OcConfigurationLib.h | 1 + .../Acidanthera/Protocol/OcFirmwareRuntime.h | 7 ++ .../OcAfterBootCompatLib.c | 5 +- .../OcAfterBootCompatLib/ServiceOverrides.c | 10 +- .../OcConfigurationLib/OcConfigurationLib.c | 1 + Library/OcMainLib/OpenCoreUefi.c | 1 + Platform/OpenRuntime/UefiRuntimeServices.c | 115 ++++++++++++------ Utilities/ocvalidate/ValidateBooter.c | 7 ++ 12 files changed, 129 insertions(+), 43 deletions(-) diff --git a/Docs/Configuration.tex b/Docs/Configuration.tex index 4b55f999600..0bae3df83d8 100755 --- a/Docs/Configuration.tex +++ b/Docs/Configuration.tex @@ -1517,6 +1517,22 @@ \subsection{Quirks Properties}\label{booterpropsquirks} \emph{Note}: Most types of firmware, apart from Apple and VMware, need this quirk. +\item + \texttt{ClearTaskSwitchBit}\\ + \textbf{Type}: \texttt{plist\ boolean}\\ + \textbf{Failsafe}: \texttt{false}\\ + \textbf{Description}: Clear task switch bit during UEFI runtime services calls. + + This quirk resolves FPU-related crashes in UEFI runtime services when using either the + 32-bit kernel on a 64-bit UEFI implementation, or the 64-bit kernel on a 32-bit UEFI implementation + by removing the task switch (\texttt{TS}) bit from \texttt{CR0} register during their execution. + These crashes occur if there is any FPU/SSE instruction usage in UEFI runtime services as XNU + is unable to handle exceptions from mixed-mode code. This quirk requires \texttt{OC\_FIRMWARE\_RUNTIME} + protocol implemented in \texttt{OpenRuntime.efi}. + + \emph{Note}: This quirk should only be required when running older macOS versions when the + 32-bit kernel is used on a 64-bit UEFI implementation, such as Microsoft Hyper-V. + \item \texttt{DevirtualiseMmio}\\ \textbf{Type}: \texttt{plist\ boolean}\\ diff --git a/Docs/Sample.plist b/Docs/Sample.plist index 9cc7ffb5e38..de8fef33c17 100644 --- a/Docs/Sample.plist +++ b/Docs/Sample.plist @@ -307,6 +307,8 @@ AvoidRuntimeDefrag + ClearTaskSwitchBit + DevirtualiseMmio DisableSingleUser diff --git a/Docs/SampleCustom.plist b/Docs/SampleCustom.plist index 63d1d0c00bb..7d251caedd0 100644 --- a/Docs/SampleCustom.plist +++ b/Docs/SampleCustom.plist @@ -307,6 +307,8 @@ AvoidRuntimeDefrag + ClearTaskSwitchBit + DevirtualiseMmio DisableSingleUser diff --git a/Include/Acidanthera/Library/OcAfterBootCompatLib.h b/Include/Acidanthera/Library/OcAfterBootCompatLib.h index 9de93a7eadf..8f5a1375700 100644 --- a/Include/Acidanthera/Library/OcAfterBootCompatLib.h +++ b/Include/Acidanthera/Library/OcAfterBootCompatLib.h @@ -149,6 +149,11 @@ typedef struct OC_ABC_SETTINGS_ { /// BOOLEAN EnableWriteUnprotector; /// + /// Clear task switch bit in UEFI runtime services. Fixes crashes due to SSE instruction + /// usage on some firmware configurations (i.e. Hyper-V Gen2 with 32-bit macOS kernel). + /// + BOOLEAN ClearTaskSwitchBit; + /// /// Signal OSInfo protocol that every loaded non-macOS OS is macOS. /// Works around disabled IGPU in Windows and Linux on Apple laptops. /// diff --git a/Include/Acidanthera/Library/OcConfigurationLib.h b/Include/Acidanthera/Library/OcConfigurationLib.h index d92e1f64939..5c150a592ec 100644 --- a/Include/Acidanthera/Library/OcConfigurationLib.h +++ b/Include/Acidanthera/Library/OcConfigurationLib.h @@ -136,6 +136,7 @@ OC_DECLARE (OC_BOOTER_PATCH_ARRAY) #define OC_BOOTER_QUIRKS_FIELDS(_, __) \ _(BOOLEAN , AllowRelocationBlock , , FALSE , ()) \ _(BOOLEAN , AvoidRuntimeDefrag , , FALSE , ()) \ + _(BOOLEAN , ClearTaskSwitchBit , , FALSE , ()) \ _(BOOLEAN , DevirtualiseMmio , , FALSE , ()) \ _(BOOLEAN , DisableSingleUser , , FALSE , ()) \ _(BOOLEAN , DisableVariableWrite , , FALSE , ()) \ diff --git a/Include/Acidanthera/Protocol/OcFirmwareRuntime.h b/Include/Acidanthera/Protocol/OcFirmwareRuntime.h index b561ffca3de..e390a79cd18 100644 --- a/Include/Acidanthera/Protocol/OcFirmwareRuntime.h +++ b/Include/Acidanthera/Protocol/OcFirmwareRuntime.h @@ -59,6 +59,13 @@ typedef struct OC_FWRT_CONFIG_ { /// Secure boot variable protection. /// BOOLEAN ProtectSecureBoot; + /// + /// Make UEFI runtime services drop CR0 TS bit on calls to prevent a 0x7 exception + /// from being triggered due to SSE instruction usage on some firmware configurations. + /// Currently this affects Hyper-V generation 2 VMs when invoking EFI runtime services + /// from the 32-bit macOS kernel. + /// + BOOLEAN ClearTaskSwitchBit; } OC_FWRT_CONFIG; /** diff --git a/Library/OcAfterBootCompatLib/OcAfterBootCompatLib.c b/Library/OcAfterBootCompatLib/OcAfterBootCompatLib.c index 071f7ed3e25..6dbfc84a91a 100644 --- a/Library/OcAfterBootCompatLib/OcAfterBootCompatLib.c +++ b/Library/OcAfterBootCompatLib/OcAfterBootCompatLib.c @@ -112,7 +112,7 @@ OcAbcInitialize ( DEBUG (( DEBUG_INFO, - "OCABC: ALRBL %d RTDFRG %d DEVMMIO %d NOSU %d NOVRWR %d NOSB %d FBSIG %d NOHBMAP %d SMSLIDE %d WRUNPROT %d\n", + "OCABC: ALRBL %d RTDFRG %d DEVMMIO %d NOSU %d NOVRWR %d NOSB %d FBSIG %d NOHBMAP %d SMSLIDE %d WRUNPROT %d CLRTS %d\n", Settings->AllowRelocationBlock, Settings->AvoidRuntimeDefrag, Settings->DevirtualiseMmio, @@ -122,7 +122,8 @@ OcAbcInitialize ( Settings->ForceBooterSignature, Settings->DiscardHibernateMap, Settings->EnableSafeModeSlide, - Settings->EnableWriteUnprotector + Settings->EnableWriteUnprotector, + Settings->ClearTaskSwitchBit )); DEBUG (( diff --git a/Library/OcAfterBootCompatLib/ServiceOverrides.c b/Library/OcAfterBootCompatLib/ServiceOverrides.c index 2a4a23d90cb..ecd884d7319 100644 --- a/Library/OcAfterBootCompatLib/ServiceOverrides.c +++ b/Library/OcAfterBootCompatLib/ServiceOverrides.c @@ -1311,11 +1311,13 @@ OcStartImage ( // Disable them when this is no longer Apple. // if (BootCompat->ServiceState.AppleBootNestedCount > 0) { - Config.WriteProtection = BootCompat->Settings.DisableVariableWrite; - Config.WriteUnprotector = BootCompat->Settings.EnableWriteUnprotector; + Config.ClearTaskSwitchBit = BootCompat->Settings.ClearTaskSwitchBit; + Config.WriteProtection = BootCompat->Settings.DisableVariableWrite; + Config.WriteUnprotector = BootCompat->Settings.EnableWriteUnprotector; } else { - Config.WriteProtection = FALSE; - Config.WriteUnprotector = FALSE; + Config.ClearTaskSwitchBit = FALSE; + Config.WriteProtection = FALSE; + Config.WriteUnprotector = FALSE; } BootCompat->ServiceState.FwRuntime->SetMain ( diff --git a/Library/OcConfigurationLib/OcConfigurationLib.c b/Library/OcConfigurationLib/OcConfigurationLib.c index a1b90da4dc1..2a5deaf1b40 100644 --- a/Library/OcConfigurationLib/OcConfigurationLib.c +++ b/Library/OcConfigurationLib/OcConfigurationLib.c @@ -184,6 +184,7 @@ OC_SCHEMA mBooterQuirksSchema[] = { OC_SCHEMA_BOOLEAN_IN ("AllowRelocationBlock", OC_GLOBAL_CONFIG, Booter.Quirks.AllowRelocationBlock), OC_SCHEMA_BOOLEAN_IN ("AvoidRuntimeDefrag", OC_GLOBAL_CONFIG, Booter.Quirks.AvoidRuntimeDefrag), + OC_SCHEMA_BOOLEAN_IN ("ClearTaskSwitchBit", OC_GLOBAL_CONFIG, Booter.Quirks.ClearTaskSwitchBit), OC_SCHEMA_BOOLEAN_IN ("DevirtualiseMmio", OC_GLOBAL_CONFIG, Booter.Quirks.DevirtualiseMmio), OC_SCHEMA_BOOLEAN_IN ("DisableSingleUser", OC_GLOBAL_CONFIG, Booter.Quirks.DisableSingleUser), OC_SCHEMA_BOOLEAN_IN ("DisableVariableWrite", OC_GLOBAL_CONFIG, Booter.Quirks.DisableVariableWrite), diff --git a/Library/OcMainLib/OpenCoreUefi.c b/Library/OcMainLib/OpenCoreUefi.c index 6ad1b6929cd..2183d9a93ab 100644 --- a/Library/OcMainLib/OpenCoreUefi.c +++ b/Library/OcMainLib/OpenCoreUefi.c @@ -697,6 +697,7 @@ OcLoadBooterUefiSupport ( AbcSettings.AllowRelocationBlock = Config->Booter.Quirks.AllowRelocationBlock; AbcSettings.EnableSafeModeSlide = Config->Booter.Quirks.EnableSafeModeSlide; AbcSettings.EnableWriteUnprotector = Config->Booter.Quirks.EnableWriteUnprotector; + AbcSettings.ClearTaskSwitchBit = Config->Booter.Quirks.ClearTaskSwitchBit; AbcSettings.ForceExitBootServices = Config->Booter.Quirks.ForceExitBootServices; AbcSettings.ForceBooterSignature = Config->Booter.Quirks.ForceBooterSignature; CopyMem (AbcSettings.BooterSignature, Signature, sizeof (AbcSettings.BooterSignature)); diff --git a/Platform/OpenRuntime/UefiRuntimeServices.c b/Platform/OpenRuntime/UefiRuntimeServices.c index e419486dec6..cf6242e34cf 100644 --- a/Platform/OpenRuntime/UefiRuntimeServices.c +++ b/Platform/OpenRuntime/UefiRuntimeServices.c @@ -65,42 +65,74 @@ STATIC BOOLEAN mInsideVarService; STATIC VOID -WriteUnprotectorPrologue ( +Cr0QuirkPrologue ( OUT BOOLEAN *Ints, - OUT BOOLEAN *Wp + OUT BOOLEAN *Wp, + OUT BOOLEAN *Ts ) { IA32_CR0 Cr0; - if (gCurrentConfig->WriteUnprotector) { + if (gCurrentConfig->WriteUnprotector || gCurrentConfig->ClearTaskSwitchBit) { *Ints = SaveAndDisableInterrupts (); Cr0.UintN = AsmReadCr0 (); - if (Cr0.Bits.WP == 1) { - *Wp = TRUE; - Cr0.Bits.WP = 0; + + if (gCurrentConfig->WriteUnprotector) { + if (Cr0.Bits.WP == 1) { + *Wp = TRUE; + Cr0.Bits.WP = 0; + } else { + *Wp = FALSE; + } + } + + if (gCurrentConfig->ClearTaskSwitchBit) { + if (Cr0.Bits.TS == 1) { + *Ts = TRUE; + Cr0.Bits.TS = 0; + } else { + *Ts = FALSE; + } + } + + if (*Wp || *Ts) { AsmWriteCr0 (Cr0.UintN); - } else { - *Wp = FALSE; } } else { *Ints = FALSE; *Wp = FALSE; + *Ts = FALSE; } } STATIC VOID -WriteUnprotectorEpilogue ( +Cr0QuirkEpilogue ( IN BOOLEAN Ints, - IN BOOLEAN Wp + IN BOOLEAN Wp, + IN BOOLEAN Ts ) { IA32_CR0 Cr0; - if (gCurrentConfig->WriteUnprotector) { - if (Wp) { - Cr0.UintN = AsmReadCr0 (); - Cr0.Bits.WP = 1; + if (gCurrentConfig->WriteUnprotector || gCurrentConfig->ClearTaskSwitchBit) { + if (Wp || Ts) { + Cr0.UintN = AsmReadCr0 (); + } + + if (gCurrentConfig->WriteUnprotector) { + if (Wp) { + Cr0.Bits.WP = 1; + } + } + + if (gCurrentConfig->ClearTaskSwitchBit) { + if (Ts) { + Cr0.Bits.TS = 1; + } + } + + if (Wp || Ts) { AsmWriteCr0 (Cr0.UintN); } @@ -221,8 +253,9 @@ WrapGetTime ( EFI_STATUS Status; BOOLEAN Ints; BOOLEAN Wp; + BOOLEAN Ts; - WriteUnprotectorPrologue (&Ints, &Wp); + Cr0QuirkPrologue (&Ints, &Wp, &Ts); Status = mStoredGetTime ( Time, @@ -242,7 +275,7 @@ WrapGetTime ( } } - WriteUnprotectorEpilogue (Ints, Wp); + Cr0QuirkEpilogue (Ints, Wp, Ts); return Status; } @@ -257,14 +290,15 @@ WrapSetTime ( EFI_STATUS Status; BOOLEAN Ints; BOOLEAN Wp; + BOOLEAN Ts; - WriteUnprotectorPrologue (&Ints, &Wp); + Cr0QuirkPrologue (&Ints, &Wp, &Ts); Status = mStoredSetTime ( Time ); - WriteUnprotectorEpilogue (Ints, Wp); + Cr0QuirkEpilogue (Ints, Wp, Ts); return Status; } @@ -281,8 +315,9 @@ WrapGetWakeupTime ( EFI_STATUS Status; BOOLEAN Ints; BOOLEAN Wp; + BOOLEAN Ts; - WriteUnprotectorPrologue (&Ints, &Wp); + Cr0QuirkPrologue (&Ints, &Wp, &Ts); Status = mStoredGetWakeupTime ( Enabled, @@ -290,7 +325,7 @@ WrapGetWakeupTime ( Time ); - WriteUnprotectorEpilogue (Ints, Wp); + Cr0QuirkEpilogue (Ints, Wp, Ts); return Status; } @@ -306,15 +341,16 @@ WrapSetWakeupTime ( EFI_STATUS Status; BOOLEAN Ints; BOOLEAN Wp; + BOOLEAN Ts; - WriteUnprotectorPrologue (&Ints, &Wp); + Cr0QuirkPrologue (&Ints, &Wp, &Ts); Status = mStoredSetWakeupTime ( Enable, Time ); - WriteUnprotectorEpilogue (Ints, Wp); + Cr0QuirkEpilogue (Ints, Wp, Ts); return Status; } @@ -334,6 +370,7 @@ WrapGetVariable ( CHAR16 TempName[OC_VARIABLE_NAME_SIZE]; BOOLEAN Ints; BOOLEAN Wp; + BOOLEAN Ts; // // Perform early checks for speedup. @@ -373,7 +410,7 @@ WrapGetVariable ( VendorGuid = &gOcVendorVariableGuid; } - WriteUnprotectorPrologue (&Ints, &Wp); + Cr0QuirkPrologue (&Ints, &Wp, &Ts); Status = (mCustomGetVariable != NULL ? mCustomGetVariable : mStoredGetVariable)( VariableName, @@ -383,7 +420,7 @@ WrapGetVariable ( Data ); - WriteUnprotectorEpilogue (Ints, Wp); + Cr0QuirkEpilogue (Ints, Wp, Ts); return Status; } @@ -405,6 +442,7 @@ WrapGetNextVariableName ( BOOLEAN StartBootVar; BOOLEAN Ints; BOOLEAN Wp; + BOOLEAN Ts; #ifdef OC_DEBUG_VAR_SERVICE if (!mInsideVarService) { @@ -442,14 +480,14 @@ WrapGetNextVariableName ( return EFI_INVALID_PARAMETER; } - WriteUnprotectorPrologue (&Ints, &Wp); + Cr0QuirkPrologue (&Ints, &Wp, &Ts); // // In case we do not redirect, simply do nothing. // if (!gCurrentConfig->BootVariableRedirect) { Status = mStoredGetNextVariableName (VariableNameSize, VariableName, VendorGuid); - WriteUnprotectorEpilogue (Ints, Wp); + Cr0QuirkEpilogue (Ints, Wp, Ts); return Status; } @@ -499,7 +537,7 @@ WrapGetNextVariableName ( Status = EFI_BUFFER_TOO_SMALL; } - WriteUnprotectorEpilogue (Ints, Wp); + Cr0QuirkEpilogue (Ints, Wp, Ts); return Status; } else { // @@ -512,7 +550,7 @@ WrapGetNextVariableName ( // At this step we cannot do anything, but let's replace error // with something sensible. // - WriteUnprotectorEpilogue (Ints, Wp); + Cr0QuirkEpilogue (Ints, Wp, Ts); return EFI_DEVICE_ERROR; } else if (Status == EFI_NOT_FOUND) { // @@ -527,7 +565,7 @@ WrapGetNextVariableName ( // We got EFI_UNSUPPORTED, EFI_DEVICE_ERROR or EFI_INVALID_PARAMETER. // Return as is. // - WriteUnprotectorEpilogue (Ints, Wp); + Cr0QuirkEpilogue (Ints, Wp, Ts); return Status; } } @@ -585,7 +623,7 @@ WrapGetNextVariableName ( Status = EFI_BUFFER_TOO_SMALL; } - WriteUnprotectorEpilogue (Ints, Wp); + Cr0QuirkEpilogue (Ints, Wp, Ts); return Status; } } else { @@ -599,7 +637,7 @@ WrapGetNextVariableName ( // // Report this is the end. // - WriteUnprotectorEpilogue (Ints, Wp); + Cr0QuirkEpilogue (Ints, Wp, Ts); return EFI_NOT_FOUND; } @@ -618,6 +656,7 @@ WrapSetVariable ( CHAR16 TempName[OC_VARIABLE_NAME_SIZE]; BOOLEAN Ints; BOOLEAN Wp; + BOOLEAN Ts; #ifdef OC_DEBUG_VAR_SERVICE if (!mInsideVarService) { @@ -681,7 +720,7 @@ WrapSetVariable ( VendorGuid = &gOcVendorVariableGuid; } - WriteUnprotectorPrologue (&Ints, &Wp); + Cr0QuirkPrologue (&Ints, &Wp, &Ts); Status = mStoredSetVariable ( VariableName, @@ -691,7 +730,7 @@ WrapSetVariable ( Data ); - WriteUnprotectorEpilogue (Ints, Wp); + Cr0QuirkEpilogue (Ints, Wp, Ts); return Status; } @@ -706,14 +745,15 @@ WrapGetNextHighMonotonicCount ( EFI_STATUS Status; BOOLEAN Ints; BOOLEAN Wp; + BOOLEAN Ts; - WriteUnprotectorPrologue (&Ints, &Wp); + Cr0QuirkPrologue (&Ints, &Wp, &Ts); Status = mStoredGetNextHighMonotonicCount ( Count ); - WriteUnprotectorEpilogue (Ints, Wp); + Cr0QuirkEpilogue (Ints, Wp, Ts); return Status; } @@ -730,8 +770,9 @@ WrapResetSystem ( { BOOLEAN Ints; BOOLEAN Wp; + BOOLEAN Ts; - WriteUnprotectorPrologue (&Ints, &Wp); + Cr0QuirkPrologue (&Ints, &Wp, &Ts); mStoredResetSystem ( ResetType, @@ -740,7 +781,7 @@ WrapResetSystem ( ResetData ); - WriteUnprotectorEpilogue (Ints, Wp); + Cr0QuirkEpilogue (Ints, Wp, Ts); } STATIC diff --git a/Utilities/ocvalidate/ValidateBooter.c b/Utilities/ocvalidate/ValidateBooter.c index 0f479d54ebe..793a3cba61e 100644 --- a/Utilities/ocvalidate/ValidateBooter.c +++ b/Utilities/ocvalidate/ValidateBooter.c @@ -148,6 +148,7 @@ CheckBooterQuirks ( BOOLEAN IsEnableSafeModeSlideEnabled; BOOLEAN IsDisableVariableWriteEnabled; BOOLEAN IsEnableWriteUnprotectorEnabled; + BOOLEAN IsClearTaskSwitchBitEnabled; BOOLEAN HasOpenRuntimeEfiDriver; INT8 ResizeAppleGpuBars; @@ -157,6 +158,7 @@ CheckBooterQuirks ( IsEnableSafeModeSlideEnabled = Config->Booter.Quirks.EnableSafeModeSlide; IsDisableVariableWriteEnabled = Config->Booter.Quirks.DisableVariableWrite; IsEnableWriteUnprotectorEnabled = Config->Booter.Quirks.EnableWriteUnprotector; + IsClearTaskSwitchBitEnabled = Config->Booter.Quirks.ClearTaskSwitchBit; HasOpenRuntimeEfiDriver = FALSE; MaxSlide = Config->Booter.Quirks.ProvideMaxSlide; ResizeAppleGpuBars = Config->Booter.Quirks.ResizeAppleGpuBars; @@ -188,6 +190,11 @@ CheckBooterQuirks ( DEBUG ((DEBUG_WARN, "Booter->Quirks->EnableWriteUnprotector is enabled, but OpenRuntime.efi is not loaded at UEFI->Drivers!\n")); ++ErrorCount; } + + if (IsClearTaskSwitchBitEnabled) { + DEBUG ((DEBUG_WARN, "Booter->Quirks->ClearTaskSwitchBit is enabled, but OpenRuntime.efi is not loaded at UEFI->Drivers!\n")); + ++ErrorCount; + } } if (!IsProvideCustomSlideEnabled) {