Marvin Häuser d916dd65b8 OcMachoLib: Treat container Mach-O as reference file
As of macOS 13 Developer Beta 3, the Kernel Collection's inner kernel
references a segment that precedes itself. The current model is that
a Kernel Collection is a container format and the included files are
(mostly) separate. Hence, this was treated as an out-of-bounds issue.
Kernel Collections apparently are rather an unconventional composite
format, where the sub-files are still part of the whole. Redesign
OcMachoLib to treat the Kernel Collection as the reference file.
Patches still use only the inner file, while parsing considers the
whole file.
2022-07-07 17:52:25 +02:00

1514 lines
48 KiB
C

/** @file
CPUID kernel patches.
Copyright (c) 2018-2020, vit9696, Goldfish64. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include <Base.h>
#include <IndustryStandard/AppleIntelCpuInfo.h>
#include <Library/BaseMemoryLib.h>
#include <Library/OcDebugLogLib.h>
#include <Library/OcAppleKernelLib.h>
#include <Library/PrintLib.h>
#include <Library/OcFileLib.h>
#include <Library/OcStringLib.h>
#include <Library/UefiLib.h>
STATIC
CONST UINT8
mKernelCpuIdFindRelNew[] = {
0xB9, 0x8B, 0x00, 0x00, 0x00, // mov ecx, 8Bh
0x31, 0xC0, // xor eax, eax
0x31, 0xD2, // xor edx, edx
0x0F, 0x30, // wrmsr
0xB8, 0x01, 0x00, 0x00, 0x00, // mov eax, 1
0x31, 0xDB, // xor ebx, ebx
0x31, 0xC9, // xor ecx, ecx
0x31, 0xD2, // xor edx, edx
0x0F, 0xA2 // cpuid
};
STATIC
CONST UINT8
mKernelCpuIdFindRelOld[] = {
0xB9, 0x8B, 0x00, 0x00, 0x00, // mov ecx, 8Bh
0x31, 0xD2, // xor edx, edx
0x0F, 0x30, // wrmsr
0xB8, 0x01, 0x00, 0x00, 0x00, // mov eax, 1
0x31, 0xDB, // xor ebx, ebx
0x31, 0xC9, // xor ecx, ecx
0x31, 0xD2, // xor edx, edx
0x0F, 0xA2 // cpuid
};
STATIC
CONST UINT8
mKernelCpuidFindMcRel[] = {
0xB9, 0x8B, 0x00, 0x00, 0x00, // mov ecx, 8Bh
0x0F, 0x32 // rdmsr
};
/**
cpu->cpuid_signature = 0x11111111;
cpu->cpuid_stepping = 0x22;
cpu->cpuid_model = 0x33;
cpu->cpuid_family = 0x44;
cpu->cpuid_type = 0x55555555;
cpu->cpuid_extmodel = 0x66;
cpu->cpuid_extfamily = 0x77;
cpu->cpuid_features = 0x8888888888888888;
cpu->cpuid_logical_per_package = 0x99999999;
cpu->cpuid_cpufamily = 0xAAAAAAAA;
return 0xAAAAAAAA;
**/
STATIC
CONST UINT8
mKernelCpuidReplaceDbg[] = {
0xC7, 0x47, 0x68, 0x11, 0x11, 0x11, 0x11, // mov dword ptr [rdi+68h], 11111111h
0xC6, 0x47, 0x50, 0x22, // mov byte ptr [rdi+50h], 22h
0x48, 0xB8, 0x55, 0x55, 0x55, 0x55, 0x44, 0x33, 0x66, 0x77, // mov rax, 7766334455555555h
0x48, 0x89, 0x47, 0x48, // mov [rdi+48h], rax
0x48, 0xB8, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, // mov rax, 8888888888888888h
0x48, 0x89, 0x47, 0x58, // mov [rdi+58h], rax
0xC7, 0x87, 0xCC, 0x00, 0x00, 0x00, 0x99, 0x99, 0x99, 0x99, // mov dword ptr [rdi+0CCh], 99999999h
0xC7, 0x87, 0x88, 0x01, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, // mov dword ptr [rdi+188h], AAAAAAAAh
0xB8, 0xAA, 0xAA, 0xAA, 0xAA, // mov eax, AAAAAAAAh
0xC3 // retn
};
#pragma pack(push, 1)
typedef struct {
UINT8 Code1[3];
UINT32 Signature;
UINT8 Code2[3];
UINT8 Stepping;
UINT8 Code3[2];
UINT32 Type;
UINT8 Family;
UINT8 Model;
UINT8 ExtModel;
UINT8 ExtFamily;
UINT8 Code4[6];
UINT64 Features;
UINT8 Code5[10];
UINT32 LogicalPerPkg;
UINT8 Code6[6];
UINT32 AppleFamily1;
UINT8 Code7;
UINT32 AppleFamily2;
UINT8 Code8;
} INTERNAL_CPUID_FN_PATCH;
STATIC_ASSERT (
sizeof (INTERNAL_CPUID_FN_PATCH) == sizeof (mKernelCpuidReplaceDbg),
"Check your CPUID patch layout"
);
typedef struct {
UINT8 EaxCmd;
UINT32 EaxVal;
UINT8 EbxCmd;
UINT32 EbxVal;
UINT8 EcxCmd;
UINT32 EcxVal;
UINT8 EdxCmd;
UINT32 EdxVal;
} INTERNAL_CPUID_PATCH;
typedef struct {
UINT8 EdxCmd;
UINT32 EdxVal;
} INTERNAL_MICROCODE_PATCH;
#pragma pack(pop)
STATIC
EFI_STATUS
PatchKernelCpuIdLegacy (
IN OUT PATCHER_CONTEXT *Patcher,
IN UINT32 KernelVersion,
IN OC_CPU_INFO *CpuInfo,
IN UINT32 *Data,
IN UINT32 *DataMask,
IN UINT8 *Start,
IN UINT8 *Last
)
{
EFI_STATUS Status;
UINT8 *Record;
UINT8 *BlockClearFunc;
UINT8 *StartPointer;
UINT8 *EndPointer;
UINT32 StructAddr;
UINT8 *Location;
UINT8 *LocationEnd;
UINT8 *LocationTsc;
UINT8 *LocationTscEnd;
UINT8 *LocationSnow32;
UINT32 Signature[3];
BOOLEAN IsTiger;
BOOLEAN IsTigerOld;
BOOLEAN IsLeopard;
BOOLEAN IsSnow;
BOOLEAN IsLion;
UINT32 Index;
UINT32 MaxExt;
INT32 Delta;
INTERNAL_CPUID_PATCH Patch;
//
// XNU 8.10.1 and older require an additional patch to _tsc_init.
//
IsTiger = OcMatchDarwinVersion (KernelVersion, KERNEL_VERSION_TIGER_MIN, KERNEL_VERSION_TIGER_MAX);
IsTigerOld = OcMatchDarwinVersion (KernelVersion, KERNEL_VERSION_TIGER_MIN, KERNEL_VERSION (8, 10, 1));
IsLeopard = OcMatchDarwinVersion (KernelVersion, KERNEL_VERSION_LEOPARD_MIN, KERNEL_VERSION_LEOPARD_MAX);
IsSnow = OcMatchDarwinVersion (KernelVersion, KERNEL_VERSION_SNOW_LEOPARD_MIN, KERNEL_VERSION_SNOW_LEOPARD_MAX);
IsLion = OcMatchDarwinVersion (KernelVersion, KERNEL_VERSION_LION_MIN, KERNEL_VERSION_LION_MAX);
StructAddr = 0;
LocationSnow32 = NULL;
LocationTsc = NULL;
LocationTscEnd = NULL;
//
// Locate _cpuid_set_info or _cpuid_get_info.
// _cpuid_set_info is also patched in 10.4, and is located below _cpuid_get_info.
//
Status = PatcherGetSymbolAddress (Patcher, IsTiger ? "_cpuid_get_info" : "_cpuid_set_info", (UINT8 **)&Record);
if (EFI_ERROR (Status) || (Record >= Last)) {
DEBUG ((DEBUG_WARN, "OCAK: Failed to locate _cpuid_%a_info (%p) - %r\n", IsTiger ? "get" : "set", Record, Status));
return EFI_NOT_FOUND;
}
//
// Start of patch area.
//
// The existing code here will be replaced with a hardcoded vendor string (CPUID (0)).
// We'll also hardcode the CPUID (1) data here, jumped to from the next patch location area.
//
// 10.4 is in _cpuid_get_info.
// Others are in _cpuid_set_info.
//
// 10.4 has the struct in esi.
// 10.5 and 32-bit 10.6 have the struct hardcoded. This can be
// pulled right below the call to _blkclr, as below.
// 64-bit 10.6 has the struct in rdi, a bit before lea rsi... below.
// 32-bit 10.7 has the struct hardcoded, and can be pulled right
// before the call to _bzero
//
STATIC CONST UINT8 mKernelCpuidFindPatchTigerStart[4] = {
0x8D, 0x45, 0xEC, // lea eax, dword [...]
0xC7 // mov from mov dword [...], 4
};
STATIC CONST UINT8 mKernelCpuidFindPatchLeoSnowLionStruct32[2] = {
0x00, // 0 from end of mov dword [...], [struct address]
0xE8 // call from call _blkclr or _bzero
};
STATIC CONST UINT8 mKernelCpuidFindPatchLeoSnowLionStart32[5] = {
0x04, 0x00, 0x00, 0x00, // 4 from mov dword [...], 4
0xC7 // mov...
};
STATIC CONST UINT8 mKernelCpuidFindPatchSnowStart64[8] = {
0xBA, 0x04, 0x00, 0x00, 0x00, // mov edx, 4
0x48, 0x8D, 0x35 // lea rsi, ...
};
if (Patcher->Is32Bit) {
if (IsTiger) {
for (Index = 0; Index < EFI_PAGE_SIZE; Index++, Record++) {
if ( (Record[0] == mKernelCpuidFindPatchTigerStart[0])
&& (Record[1] == mKernelCpuidFindPatchTigerStart[1])
&& (Record[2] == mKernelCpuidFindPatchTigerStart[2])
&& (Record[3] == mKernelCpuidFindPatchTigerStart[3]))
{
break;
}
}
} else {
//
// We need to get the address of _blkclr or _bzero first for proper matching.
//
Status = PatcherGetSymbolAddress (Patcher, IsLion ? "_bzero" : "_blkclr", (UINT8 **)&BlockClearFunc);
if (EFI_ERROR (Status) || (Record >= Last)) {
DEBUG ((DEBUG_WARN, "OCAK: Failed to locate %a (%p) - %r\n", IsLion ? "_bzero" : "_blkclr", Record, Status));
return EFI_NOT_FOUND;
}
//
// Get struct address first.
//
for (Index = 0; Index < EFI_PAGE_SIZE; Index++, Record++) {
if ( (Record[0] == mKernelCpuidFindPatchLeoSnowLionStruct32[0])
&& (Record[1] == mKernelCpuidFindPatchLeoSnowLionStruct32[1])
&& (*((INT32 *)&Record[2]) == (INT32)(BlockClearFunc - (Record + sizeof (mKernelCpuidFindPatchLeoSnowLionStruct32) + sizeof (UINT32)))))
{
break;
}
}
if (Index >= EFI_PAGE_SIZE) {
return EFI_NOT_FOUND;
}
StructAddr = *((UINT32 *)(Record - 3));
for (Index = 0; Index < EFI_PAGE_SIZE; Index++, Record++) {
if ( (Record[0] == mKernelCpuidFindPatchLeoSnowLionStart32[0])
&& (Record[1] == mKernelCpuidFindPatchLeoSnowLionStart32[1])
&& (Record[2] == mKernelCpuidFindPatchLeoSnowLionStart32[2])
&& (Record[3] == mKernelCpuidFindPatchLeoSnowLionStart32[3])
&& (Record[4] == mKernelCpuidFindPatchLeoSnowLionStart32[4]))
{
break;
}
}
}
} else {
for (Index = 0; Index < EFI_PAGE_SIZE; Index++, Record++) {
if ( (Record[0] == mKernelCpuidFindPatchSnowStart64[0])
&& (Record[1] == mKernelCpuidFindPatchSnowStart64[1])
&& (Record[2] == mKernelCpuidFindPatchSnowStart64[2])
&& (Record[3] == mKernelCpuidFindPatchSnowStart64[3])
&& (Record[4] == mKernelCpuidFindPatchSnowStart64[4])
&& (Record[5] == mKernelCpuidFindPatchSnowStart64[5])
&& (Record[6] == mKernelCpuidFindPatchSnowStart64[6])
&& (Record[7] == mKernelCpuidFindPatchSnowStart64[7]))
{
break;
}
}
}
if (Index >= EFI_PAGE_SIZE) {
return EFI_NOT_FOUND;
}
if (Patcher->Is32Bit) {
StartPointer = IsTiger ? Record : Record - 4;
} else {
StartPointer = Record + sizeof (mKernelCpuidFindPatchSnowStart64) + sizeof (UINT32);
}
//
// End of patch area.
//
STATIC CONST UINT8 mKernelCpuidFindPatchTigerEnd[4] = {
0x00, // 0 from mov byte [...], 0
0x31, 0xDB, // xor ebx, ebx
0x8B // mov ...
};
STATIC UINT8 mKernelCpuidFindPatchSnowLionEnd32[7] = {
0xC6, 0x05, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 // mov byte [struct_addr], 0
};
STATIC UINT32 *mKernelCpuidFindPatchSnowLionEndPtr32 = (UINT32 *)&mKernelCpuidFindPatchSnowLionEnd32[2];
*mKernelCpuidFindPatchSnowLionEndPtr32 = StructAddr + sizeof (Signature[0]) * 3;
STATIC CONST UINT8 mKernelCpuidFindPatchLeoEnd1[5] = {
0xB8, 0x00, 0x00, 0x00, 0x80 // mov eax/edx, 80000000h
};
STATIC CONST UINT8 mKernelCpuidFindPatchLeoEnd1Mask = 0xFD;
if (IsTiger) {
for (Index = 0; Index < EFI_PAGE_SIZE; Index++, Record++) {
if ( (Record[0] == mKernelCpuidFindPatchTigerEnd[0])
&& (Record[1] == mKernelCpuidFindPatchTigerEnd[1])
&& (Record[2] == mKernelCpuidFindPatchTigerEnd[2])
&& (Record[3] == mKernelCpuidFindPatchTigerEnd[3]))
{
break;
}
}
} else if ((IsSnow || IsLion) && Patcher->Is32Bit) {
for (Index = 0; Index < EFI_PAGE_SIZE; Index++, Record++) {
if ( (Record[0] == mKernelCpuidFindPatchSnowLionEnd32[0])
&& (Record[1] == mKernelCpuidFindPatchSnowLionEnd32[1])
&& (Record[2] == mKernelCpuidFindPatchSnowLionEnd32[2])
&& (Record[3] == mKernelCpuidFindPatchSnowLionEnd32[3])
&& (Record[4] == mKernelCpuidFindPatchSnowLionEnd32[4])
&& (Record[5] == mKernelCpuidFindPatchSnowLionEnd32[5])
&& (Record[6] == mKernelCpuidFindPatchSnowLionEnd32[6]))
{
break;
}
}
} else {
for (Index = 0; Index < EFI_PAGE_SIZE; Index++, Record++) {
if ( ((Record[0] & mKernelCpuidFindPatchLeoEnd1Mask) == mKernelCpuidFindPatchLeoEnd1[0])
&& (Record[1] == mKernelCpuidFindPatchLeoEnd1[1])
&& (Record[2] == mKernelCpuidFindPatchLeoEnd1[2])
&& (Record[3] == mKernelCpuidFindPatchLeoEnd1[3])
&& (Record[4] == mKernelCpuidFindPatchLeoEnd1[4]))
{
break;
}
}
}
if (Index >= EFI_PAGE_SIZE) {
return EFI_NOT_FOUND;
}
if (IsSnow && !Patcher->Is32Bit) {
STATIC CONST UINT8 mKernelCpuidFindPatchLeoEnd2[3] = {
0x0F, 0xA2, // cpuid
0x89 // mov ...
};
for ( ; Index < EFI_PAGE_SIZE; Index++, Record++) {
if ( (Record[0] == mKernelCpuidFindPatchLeoEnd2[0])
&& (Record[1] == mKernelCpuidFindPatchLeoEnd2[1])
&& (Record[2] == mKernelCpuidFindPatchLeoEnd2[2]))
{
break;
}
}
if (Index >= EFI_PAGE_SIZE) {
return EFI_NOT_FOUND;
}
}
EndPointer = IsTiger ? Record - 3 : Record;
//
// Start of CPUID location.
//
// We'll replace this with a call to the previous patched section to
// populate the CPUID (1) info.
//
// 32-bit 10.6 has two different mov eax, 0x1 pairs for 32-bit and 64-bit.
// The first is patched out with nops, and we'll use the second for the jmp.
//
// 32-bit 10.7 has a call to a function that is very similar to 32-bit 10.6, and calls
// cpuid in 64-bit mode if supported. We can simply replace this call with one to the patched area.
//
STATIC CONST UINT8 mKernelCpuidFindLocLeoTigerStart[7] = {
0xB9, 0x01, 0x00, 0x00, 0x00, // mov ecx, 1
0x89, 0xC8 // mov eax, ecx
};
STATIC CONST UINT8 mKernelCpuidFindLocSnowStart[5] = {
0xB8, 0x01, 0x00, 0x00, 0x00 // mov eax, 1
};
STATIC CONST UINT8 mKernelCpuidFindLocSnowStart32[6] = {
0xE8, 0xFF, 0xFF, 0xFF, 0xFF, // call _cpuid64
0xEB // jmp ...
};
STATIC CONST UINT8 mKernelCpuidFindLocLionStart32[8] = {
0xB9, 0x01, 0x00, 0x00, 0x00, // mov ecx, 1
0x8D, 0x55, 0xD0 // lea edx, dword [...]
};
if (IsTiger || IsLeopard) {
for ( ; Index < EFI_PAGE_SIZE; Index++, Record++) {
if ( (Record[0] == mKernelCpuidFindLocLeoTigerStart[0])
&& (Record[1] == mKernelCpuidFindLocLeoTigerStart[1])
&& (Record[2] == mKernelCpuidFindLocLeoTigerStart[2])
&& (Record[3] == mKernelCpuidFindLocLeoTigerStart[3])
&& (Record[4] == mKernelCpuidFindLocLeoTigerStart[4])
&& (Record[5] == mKernelCpuidFindLocLeoTigerStart[5])
&& (Record[6] == mKernelCpuidFindLocLeoTigerStart[6]))
{
break;
}
}
} else if (IsSnow) {
for ( ; Index < EFI_PAGE_SIZE; Index++, Record++) {
if ( (Record[0] == mKernelCpuidFindLocSnowStart[0])
&& (Record[1] == mKernelCpuidFindLocSnowStart[1])
&& (Record[2] == mKernelCpuidFindLocSnowStart[2])
&& (Record[3] == mKernelCpuidFindLocSnowStart[3])
&& (Record[4] == mKernelCpuidFindLocSnowStart[4]))
{
break;
}
}
if (Index >= EFI_PAGE_SIZE) {
return EFI_NOT_FOUND;
}
//
// Find where call _cpuid64 is located.
// We'll then look for the second mov eax, 0x1 after that.
//
if (Patcher->Is32Bit) {
for ( ; Index < EFI_PAGE_SIZE; Index++, Record++) {
if ( (Record[0] == mKernelCpuidFindLocSnowStart32[0])
&& (Record[5] == mKernelCpuidFindLocSnowStart32[5]))
{
break;
}
}
if (Index >= EFI_PAGE_SIZE) {
return EFI_NOT_FOUND;
}
LocationSnow32 = Record;
for ( ; Index < EFI_PAGE_SIZE; Index++, Record++) {
if ( (Record[0] == mKernelCpuidFindLocSnowStart[0])
&& (Record[1] == mKernelCpuidFindLocSnowStart[1])
&& (Record[2] == mKernelCpuidFindLocSnowStart[2])
&& (Record[3] == mKernelCpuidFindLocSnowStart[3])
&& (Record[4] == mKernelCpuidFindLocSnowStart[4]))
{
break;
}
}
}
} else {
for ( ; Index < EFI_PAGE_SIZE; Index++, Record++) {
if ( (Record[0] == mKernelCpuidFindLocLionStart32[0])
&& (Record[1] == mKernelCpuidFindLocLionStart32[1])
&& (Record[2] == mKernelCpuidFindLocLionStart32[2])
&& (Record[3] == mKernelCpuidFindLocLionStart32[3])
&& (Record[4] == mKernelCpuidFindLocLionStart32[4])
&& (Record[5] == mKernelCpuidFindLocLionStart32[5])
&& (Record[6] == mKernelCpuidFindLocLionStart32[6])
&& (Record[7] == mKernelCpuidFindLocLionStart32[7]))
{
break;
}
}
}
if (Index >= EFI_PAGE_SIZE) {
return EFI_NOT_FOUND;
}
Location = IsLion ? Record + sizeof (mKernelCpuidFindLocLionStart32) : Record;
//
// End of CPUID location. Not applicable to 10.7.
//
STATIC CONST UINT8 mKernelCpuidFindLegacyLocEnd[3] = {
0x0F, 0xA2, // cpuid
0x89 // mov ...
};
if (!IsLion) {
for ( ; Index < EFI_PAGE_SIZE; Index++, Record++) {
if ( (Record[0] == mKernelCpuidFindLegacyLocEnd[0])
&& (Record[1] == mKernelCpuidFindLegacyLocEnd[1])
&& (Record[2] == mKernelCpuidFindLegacyLocEnd[2]))
{
break;
}
}
if (Index >= EFI_PAGE_SIZE) {
return EFI_NOT_FOUND;
}
LocationEnd = Record + 2;
} else {
LocationEnd = Location;
}
//
// Locate _tsc_init on 10.4, as there is a CPUID (1) call that needs to be patched.
// This only applies to XNU 8.10.1 and older. Some recovery versions of
// 10.4.10 have a newer XNU 8.10.3 kernel with code changes to _tsc_init.
//
// It's possible 8.10.2 may require the patch, but there is no sources or kernels
// available to verify.
//
if (IsTigerOld) {
Status = PatcherGetSymbolAddress (Patcher, "_tsc_init", (UINT8 **)&Record);
if (EFI_ERROR (Status) || (Record >= Last)) {
DEBUG ((DEBUG_WARN, "OCAK: Failed to locate _tsc_init (%p) - %r\n", Record, Status));
return EFI_NOT_FOUND;
}
//
// Start of _tsc_init CPUID location.
//
// We'll replace this with a call to the previous patched section to
// populate the CPUID (1) info.
//
STATIC CONST UINT8 mKernelCpuidFindTscLocTigerStart[7] = {
0xBA, 0x01, 0x00, 0x00, 0x00, // mov edx, 1
0x89, 0xD0 // mov eax, edx
};
for ( ; Index < EFI_PAGE_SIZE; Index++, Record++) {
if ( (Record[0] == mKernelCpuidFindTscLocTigerStart[0])
&& (Record[1] == mKernelCpuidFindTscLocTigerStart[1])
&& (Record[2] == mKernelCpuidFindTscLocTigerStart[2])
&& (Record[3] == mKernelCpuidFindTscLocTigerStart[3])
&& (Record[4] == mKernelCpuidFindTscLocTigerStart[4])
&& (Record[5] == mKernelCpuidFindTscLocTigerStart[5])
&& (Record[6] == mKernelCpuidFindTscLocTigerStart[6]))
{
break;
}
}
if (Index >= EFI_PAGE_SIZE) {
return EFI_NOT_FOUND;
}
LocationTsc = Record;
//
// End of _tsc_init CPUID location.
//
for ( ; Index < EFI_PAGE_SIZE; Index++, Record++) {
if ( (Record[0] == mKernelCpuidFindLegacyLocEnd[0])
&& (Record[1] == mKernelCpuidFindLegacyLocEnd[1])
&& (Record[2] == mKernelCpuidFindLegacyLocEnd[2]))
{
break;
}
}
if (Index >= EFI_PAGE_SIZE) {
return EFI_NOT_FOUND;
}
LocationTscEnd = Record + 2;
}
DEBUG ((
DEBUG_INFO,
"OCAK: Legacy CPUID patch %p:%p, loc %p:%p, tsc loc %p:%p struct @ 0x%X\n",
StartPointer - Start,
EndPointer - Start,
Location - Start,
LocationEnd - Start,
IsTigerOld ? LocationTsc - Start : 0,
IsTigerOld ? LocationTscEnd - Start : 0,
StructAddr
));
//
// Free 2 more bytes in the end by assigning EAX directly.
//
if (IsSnow && !Patcher->Is32Bit) {
AsmCpuid (0x80000000, &MaxExt, NULL, NULL, NULL);
EndPointer[0] = 0xB8;
CopyMem (&EndPointer[1], &MaxExt, sizeof (MaxExt));
}
//
// Short-write CPU signature to create space for CPUID (1) patch.
//
// 29 bytes.
//
AsmCpuid (0, NULL, &Signature[0], &Signature[2], &Signature[1]);
for (Index = 0; Index < 3; ++Index) {
//
// mov eax, <signature>
//
*StartPointer++ = 0xB8;
CopyMem (StartPointer, &Signature[Index], sizeof (Signature[0]));
StartPointer += sizeof (Signature[0]);
if (IsLeopard || ((IsSnow || IsLion) && Patcher->Is32Bit)) {
//
// mov dword [struct_addr], eax
//
*StartPointer++ = 0xA3;
CopyMem (StartPointer, &StructAddr, sizeof (StructAddr));
StartPointer += sizeof (StructAddr);
StructAddr += sizeof (Signature[0]);
} else if (IsTiger) {
//
// mov [esi+offset], eax
//
*StartPointer++ = 0x89;
*StartPointer++ = 0x46;
*StartPointer++ = (UINT8)(Index * sizeof (Signature[0]));
} else {
//
// mov [rsi], eax
//
*StartPointer++ = 0x89;
*StartPointer++ = 0x06;
if (Index < 2) {
//
// add rsi, 4
//
*StartPointer++ = 0x48;
*StartPointer++ = 0x83;
*StartPointer++ = 0xC6;
*StartPointer++ = 0x04;
}
}
}
//
// Ensure that we still have room, which is within 2-byte jmp (127)
// and has at least 2-byte jmp and patch + 1-byte ret.
//
if ( (StartPointer >= EndPointer)
|| (EndPointer - StartPointer > 128)
|| ((UINTN)(EndPointer - StartPointer) < sizeof (INTERNAL_CPUID_PATCH) + 3))
{
return EFI_OUT_OF_RESOURCES;
}
//
// Short jmp to EndPointer.
//
StartPointer[0] = 0xEB;
StartPointer[1] = (UINT8)(EndPointer - StartPointer - 2);
StartPointer += 2;
//
// Workaround incorrect OSXSAVE handling, see below.
//
if ( (CpuInfo->CpuidVerEcx.Bits.XSAVE != 0)
&& (CpuInfo->CpuidVerEcx.Bits.OSXSAVE == 0)
&& (CpuInfo->CpuidVerEcx.Bits.AVX != 0))
{
CpuInfo->CpuidVerEcx.Bits.OSXSAVE = 1;
}
//
// NOP out call _cpuid64 in 32-bit 10.6.
//
if (IsSnow && Patcher->Is32Bit) {
while (LocationSnow32 < LocationEnd) {
*LocationSnow32++ = 0x90;
}
}
//
// Call from location to patch area.
//
Delta = (INT32)(StartPointer - (Location + 5));
*Location++ = 0xE8;
CopyMem (Location, &Delta, sizeof (Delta));
Location += sizeof (Delta);
while (Location < LocationEnd) {
*Location++ = 0x90;
}
//
// In 10.4, we need to replace a call to CPUID (1) with a call to
// the patch area like above in _tsc_init.
//
if (IsTigerOld) {
Delta = (INT32)(StartPointer - (LocationTsc + 5));
*LocationTsc++ = 0xE8;
CopyMem (LocationTsc, &Delta, sizeof (Delta));
LocationTsc += sizeof (Delta);
while (LocationTsc < LocationTscEnd) {
*LocationTsc++ = 0x90;
}
}
//
// Write virtualised CPUID.
// 20 bytes.
// 10.7 requires the registers to be copied to a memory location in EDX, so we'll use ESI instead.
//
Patch.EaxCmd = 0xB8;
Patch.EaxVal = (Data[0] & DataMask[0]) | (CpuInfo->CpuidVerEax.Uint32 & ~DataMask[0]);
Patch.EbxCmd = 0xBB;
Patch.EbxVal = (Data[1] & DataMask[1]) | (CpuInfo->CpuidVerEbx.Uint32 & ~DataMask[1]);
Patch.EcxCmd = 0xB9;
Patch.EcxVal = (Data[2] & DataMask[2]) | (CpuInfo->CpuidVerEcx.Uint32 & ~DataMask[2]);
Patch.EdxCmd = IsLion ? 0xBE : 0xBA;
Patch.EdxVal = (Data[3] & DataMask[3]) | (CpuInfo->CpuidVerEdx.Uint32 & ~DataMask[3]);
CopyMem (StartPointer, &Patch, sizeof (Patch));
StartPointer += sizeof (Patch);
//
// Under 10.7, we need to copy registers to memory in EDX.
//
if (IsLion) {
//
// mov [edx], eax
//
*StartPointer++ = 0x89;
*StartPointer++ = 0x02;
//
// mov [edx+4], ebx
//
*StartPointer++ = 0x89;
*StartPointer++ = 0x5A;
*StartPointer++ = 0x04;
//
// mov [edx+8], ecx
//
*StartPointer++ = 0x89;
*StartPointer++ = 0x4A;
*StartPointer++ = 0x08;
//
// mov [edx+12], esi
//
*StartPointer++ = 0x89;
*StartPointer++ = 0x72;
*StartPointer++ = 0x0C;
}
//
// Return to the end of location.
//
*StartPointer++ = 0xC3;
DEBUG ((DEBUG_INFO, "OCAK: Legacy CPUID patch completed @ %p\n", StartPointer - Start));
return EFI_SUCCESS;
}
EFI_STATUS
PatchKernelCpuId (
IN OUT PATCHER_CONTEXT *Patcher,
IN OC_CPU_INFO *CpuInfo,
IN UINT32 *Data,
IN UINT32 *DataMask,
IN UINT32 KernelVersion
)
{
EFI_STATUS Status;
UINT8 *CpuidSetInfo;
UINT8 *Record;
UINT8 *Start;
UINT8 *Last;
UINT32 Index;
UINT32 FoundSize;
INTERNAL_CPUID_PATCH *CpuidPatch;
INTERNAL_MICROCODE_PATCH *McPatch;
INTERNAL_CPUID_FN_PATCH *FnPatch;
CPUID_VERSION_INFO_EAX Eax;
CPUID_VERSION_INFO_EBX Ebx;
CPUID_VERSION_INFO_ECX Ecx;
CPUID_VERSION_INFO_EDX Edx;
BOOLEAN FoundReleaseKernel;
ASSERT (Patcher != NULL);
STATIC_ASSERT (
sizeof (mKernelCpuIdFindRelNew) > sizeof (mKernelCpuIdFindRelOld),
"Kernel CPUID patch seems wrong"
);
ASSERT (
mKernelCpuIdFindRelNew[0] == mKernelCpuIdFindRelOld[0]
&& mKernelCpuIdFindRelNew[1] == mKernelCpuIdFindRelOld[1]
&& mKernelCpuIdFindRelNew[2] == mKernelCpuIdFindRelOld[2]
&& mKernelCpuIdFindRelNew[3] == mKernelCpuIdFindRelOld[3]
);
Start = ((UINT8 *)MachoGetMachHeader (&Patcher->MachContext));
Last = Start + MachoGetInnerSize (&Patcher->MachContext) - EFI_PAGE_SIZE * 2 - sizeof (mKernelCpuIdFindRelNew);
//
// Do legacy patching for 32-bit 10.7, and 10.6 and older.
//
if ( (OcMatchDarwinVersion (KernelVersion, KERNEL_VERSION_LION_MIN, KERNEL_VERSION_LION_MAX) && Patcher->Is32Bit)
|| OcMatchDarwinVersion (KernelVersion, 0, KERNEL_VERSION_SNOW_LEOPARD_MAX))
{
Status = PatchKernelCpuIdLegacy (Patcher, KernelVersion, CpuInfo, Data, DataMask, Start, Last);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "OCAK: Failed to patch legacy CPUID - %r\n", Status));
}
return Status;
}
Status = PatcherGetSymbolAddress (Patcher, "_cpuid_set_info", (UINT8 **)&CpuidSetInfo);
if (EFI_ERROR (Status) || (CpuidSetInfo >= Last)) {
DEBUG ((DEBUG_WARN, "OCAK: Failed to locate _cpuid_set_info (%p) - %r\n", CpuidSetInfo, Status));
return EFI_NOT_FOUND;
}
Record = CpuidSetInfo;
FoundSize = 0;
for (Index = 0; Index < EFI_PAGE_SIZE; ++Index, ++Record) {
if ( (Record[0] == mKernelCpuIdFindRelNew[0])
&& (Record[1] == mKernelCpuIdFindRelNew[1])
&& (Record[2] == mKernelCpuIdFindRelNew[2])
&& (Record[3] == mKernelCpuIdFindRelNew[3]))
{
if (CompareMem (Record, mKernelCpuIdFindRelNew, sizeof (mKernelCpuIdFindRelNew)) == 0) {
FoundSize = sizeof (mKernelCpuIdFindRelNew);
break;
} else if (CompareMem (Record, mKernelCpuIdFindRelOld, sizeof (mKernelCpuIdFindRelOld)) == 0) {
FoundSize = sizeof (mKernelCpuIdFindRelOld);
break;
}
}
}
FoundReleaseKernel = FoundSize > 0;
//
// When patching the release kernel we do not allow reevaluating CPUID information,
// which is used to report OSXSAVE availability. This causes issues with some programs,
// like Docker using Hypervisor.framework, which rely on sysctl to track CPU feature.
//
// To workaround this we make sure to always report OSXSAVE bit when it is available
// regardless of the reevaluation performed by init_fpu in XNU.
//
// REF: https://github.com/acidanthera/bugtracker/issues/1035
//
if ( FoundReleaseKernel
&& (CpuInfo->CpuidVerEcx.Bits.XSAVE != 0)
&& (CpuInfo->CpuidVerEcx.Bits.OSXSAVE == 0)
&& (CpuInfo->CpuidVerEcx.Bits.AVX != 0))
{
CpuInfo->CpuidVerEcx.Bits.OSXSAVE = 1;
}
Eax.Uint32 = (Data[0] & DataMask[0]) | (CpuInfo->CpuidVerEax.Uint32 & ~DataMask[0]);
Ebx.Uint32 = (Data[1] & DataMask[1]) | (CpuInfo->CpuidVerEbx.Uint32 & ~DataMask[1]);
Ecx.Uint32 = (Data[2] & DataMask[2]) | (CpuInfo->CpuidVerEcx.Uint32 & ~DataMask[2]);
Edx.Uint32 = (Data[3] & DataMask[3]) | (CpuInfo->CpuidVerEdx.Uint32 & ~DataMask[3]);
if (FoundReleaseKernel) {
CpuidPatch = (INTERNAL_CPUID_PATCH *)Record;
CpuidPatch->EaxCmd = 0xB8;
CpuidPatch->EaxVal = Eax.Uint32;
CpuidPatch->EbxCmd = 0xBB;
CpuidPatch->EbxVal = Ebx.Uint32;
CpuidPatch->EcxCmd = 0xB9;
CpuidPatch->EcxVal = Ecx.Uint32;
CpuidPatch->EdxCmd = 0xBA;
CpuidPatch->EdxVal = Edx.Uint32;
SetMem (
Record + sizeof (INTERNAL_CPUID_PATCH),
FoundSize - sizeof (INTERNAL_CPUID_PATCH),
0x90
);
Record += FoundSize;
for (Index = 0; Index < EFI_PAGE_SIZE - sizeof (mKernelCpuidFindMcRel); ++Index, ++Record) {
if (CompareMem (Record, mKernelCpuidFindMcRel, sizeof (mKernelCpuidFindMcRel)) == 0) {
McPatch = (INTERNAL_MICROCODE_PATCH *)Record;
McPatch->EdxCmd = 0xBA;
McPatch->EdxVal = CpuInfo->MicrocodeRevision;
SetMem (
Record + sizeof (INTERNAL_MICROCODE_PATCH),
sizeof (mKernelCpuidFindMcRel) - sizeof (INTERNAL_MICROCODE_PATCH),
0x90
);
return EFI_SUCCESS;
}
}
} else {
//
// Handle debug kernel here...
//
Status = PatcherGetSymbolAddress (Patcher, "_cpuid_set_cpufamily", (UINT8 **)&Record);
if (EFI_ERROR (Status) || (Record >= Last)) {
DEBUG ((DEBUG_WARN, "OCAK: Failed to locate _cpuid_set_cpufamily (%p) - %r\n", Record, Status));
return EFI_NOT_FOUND;
}
CopyMem (Record, mKernelCpuidReplaceDbg, sizeof (mKernelCpuidReplaceDbg));
FnPatch = (INTERNAL_CPUID_FN_PATCH *)Record;
FnPatch->Signature = Eax.Uint32;
FnPatch->Stepping = (UINT8)Eax.Bits.SteppingId;
FnPatch->ExtModel = (UINT8)Eax.Bits.ExtendedModelId;
FnPatch->Model = (UINT8)Eax.Bits.Model | (UINT8)(Eax.Bits.ExtendedModelId << 4U);
FnPatch->Family = (UINT8)Eax.Bits.FamilyId;
FnPatch->Type = (UINT8)Eax.Bits.ProcessorType;
FnPatch->ExtFamily = (UINT8)Eax.Bits.ExtendedFamilyId;
FnPatch->Features = LShiftU64 (Ecx.Uint32, 32) | (UINT64)Edx.Uint32;
if (FnPatch->Features & CPUID_FEATURE_HTT) {
FnPatch->LogicalPerPkg = (UINT16)Ebx.Bits.MaximumAddressableIdsForLogicalProcessors;
} else {
FnPatch->LogicalPerPkg = 1;
}
FnPatch->AppleFamily1 = FnPatch->AppleFamily2 = OcCpuModelToAppleFamily (Eax);
return EFI_SUCCESS;
}
DEBUG ((DEBUG_WARN, "OCAK: Failed to find either CPUID patch (%u)\n", FoundSize));
return EFI_UNSUPPORTED;
}
STATIC
UINT8
mProvideCurrentCpuInfoTopologyValidationReplace[] = {
// ret
0xC3
};
STATIC
PATCHER_GENERIC_PATCH
mProvideCurrentCpuInfoTopologyValidationPatch = {
.Comment = DEBUG_POINTER ("ProvideCurrentCpuInfoTopologyValidation"),
.Base = "_x86_validate_topology",
.Find = NULL,
.Mask = NULL,
.Replace = mProvideCurrentCpuInfoTopologyValidationReplace,
.ReplaceMask = NULL,
.Size = sizeof (mProvideCurrentCpuInfoTopologyValidationReplace),
.Count = 1,
.Skip = 0,
.Limit = 0
};
// Offset of value in below patch.
#define CURRENT_CPU_INFO_CORE_COUNT_OFFSET 1
STATIC
UINT8
mProvideCurrentCpuInfoZeroMsrThreadCoreCountFind[] = {
// mov ecx, 0x10001
0xB9, 0x01, 0x00, 0x01, 0x00
};
STATIC
UINT8
mProvideCurrentCpuInfoZeroMsrThreadCoreCountReplace[] = {
// mov ecx, core/thread count
0xB9, 0x00, 0x00, 0x00, 0x00
};
STATIC
PATCHER_GENERIC_PATCH
mProvideCurrentCpuInfoZeroMsrThreadCoreCountPatch = {
.Comment = DEBUG_POINTER ("ProvideCurrentCpuInfoZeroMsrThreadCoreCount"),
.Base = "_cpuid_set_info",
.Find = mProvideCurrentCpuInfoZeroMsrThreadCoreCountFind,
.Mask = NULL,
.Replace = mProvideCurrentCpuInfoZeroMsrThreadCoreCountReplace,
.ReplaceMask = NULL,
.Size = sizeof (mProvideCurrentCpuInfoZeroMsrThreadCoreCountFind),
.Count = 1,
.Skip = 0,
.Limit = 0
};
STATIC
UINT8 *
PatchMovVar (
IN OUT UINT8 *Location,
IN UINT8 *Start,
IN MACH_SECTION_ANY *DataSection,
IN MACH_SECTION_ANY *TextSection,
IN BOOLEAN Is32Bit,
IN UINT8 *Var,
IN UINT64 Value
)
{
INT32 Delta;
UINT64 LocationAddr;
UINT64 VarAddr64;
UINT32 VarAddr32;
UINT32 ValueLower;
UINT32 ValueUpper;
if (Is32Bit) {
ValueLower = (UINT32)Value;
ValueUpper = (UINT32)(Value >> 32);
//
// mov [var], value lower
//
VarAddr32 = (UINT32)((Var - Start) + DataSection->Section32.Address - DataSection->Section32.Offset);
*Location++ = 0xC7;
*Location++ = 0x05;
CopyMem (Location, &VarAddr32, sizeof (VarAddr32));
Location += sizeof (VarAddr32);
CopyMem (Location, &ValueLower, sizeof (ValueLower));
Location += sizeof (ValueLower);
//
// mov [var+4], value upper
//
VarAddr32 += sizeof (UINT32);
*Location++ = 0xC7;
*Location++ = 0x05;
CopyMem (Location, &VarAddr32, sizeof (VarAddr32));
Location += sizeof (VarAddr32);
CopyMem (Location, &ValueUpper, sizeof (ValueUpper));
Location += sizeof (ValueUpper);
} else {
//
// mov rax, value
//
*Location++ = 0x48;
*Location++ = 0xB8;
CopyMem (Location, &Value, sizeof (Value));
Location += sizeof (Value);
LocationAddr = (Location - Start) + TextSection->Section64.Address - TextSection->Section64.Offset;
VarAddr64 = (Var - Start) + DataSection->Section64.Address - DataSection->Section64.Offset;
//
// mov [var], rax
//
Delta = (INT32)(VarAddr64 - (LocationAddr + 7));
*Location++ = 0x48;
*Location++ = 0x89;
*Location++ = 0x05;
CopyMem (Location, &Delta, sizeof (Delta));
Location += sizeof (Delta);
}
return Location;
}
// 10.14:
// 44 89 E8 mov eax, r13d
// C1 E8 1A shr eax, 0x1a
// FF C0 inc eax
// 89 05 68 60 98 00 mov dword [dword_ffffff8000e4b1c8], eax
// 10.15
// 44 89 EA mov edx, r13d
// C1 EA 1A shr edx, 26
// FF C2 inc edx
// 89 15 2F 55 A2 00 mov dword [dword_FFFFFF8000E551D8], edx
// 11.0.1
// 44 89 EA mov edx, r13d
// C1 EA 1A shr edx, 0x1a
// FF C2 inc edx
// 89 15 9F 9C A8 00 mov dword [dword_ffffff8000e4b1d8], edx
// 12.0.1
// 44 89 EA mov edx, r13d
// C1 EA 1A shr edx, 0x1a
// 83 C2 01 add edx, 0x1
// 89 15 DD F9 AA 00 mov dword [dword_ffffff8000e6e1d8], edx
STATIC
CONST UINT8
mProvideCurrentCpuInfoTopologyCoreCountFind[] = {
0xB9, 0x35, 0x00, 0x00, 0x00, 0x0F, 0x32
};
STATIC
UINT8
mProvideCurrentCpuInfoTopologyCoreCountReplace[] = {
0xB8, 0x00, 0x00, 0x00, 0x00, 0x31, 0xD2
};
STATIC
PATCHER_GENERIC_PATCH
mProvideCurrentCpuInfoTopologyCoreCountPatch = {
.Comment = DEBUG_POINTER ("Set core count to thread count"),
.Base = "_cpuid_set_info",
.Find = mProvideCurrentCpuInfoTopologyCoreCountFind,
.Mask = NULL,
.Replace = mProvideCurrentCpuInfoTopologyCoreCountReplace,
.ReplaceMask = NULL,
.Size = sizeof (mProvideCurrentCpuInfoTopologyCoreCountReplace),
.Count = 2,
.Skip = 0,
.Limit = 0
};
STATIC
CONST UINT8
mProvideCurrentCpuInfoTopologyCorePerPackageV1Find[] = {
// mov eax, r13d
// shr eax, 0x1a <--- start
// inc eax
// mov dword[], eax
0xC1, 0xE8, 0x1A, 0xFF, 0xC0, 0x89
};
STATIC
CONST UINT8
mProvideCurrentCpuInfoTopologyCorePerPackageV1Replace[] = {
// mov eax, r13d
// mov eax, 0x80 <--- start
// mov dword[], eax
0xB8, 0x80, 0x00, 0x00, 0x00, 0x89
};
STATIC
PATCHER_GENERIC_PATCH
mProvideCurrentCpuInfoTopologyCorePerPackageV1Patch = {
.Comment = DEBUG_POINTER ("Set core per package count to 128 V1"),
.Base = NULL,
.Find = mProvideCurrentCpuInfoTopologyCorePerPackageV1Find,
.Mask = NULL,
.Replace = mProvideCurrentCpuInfoTopologyCorePerPackageV1Replace,
.ReplaceMask = NULL,
.Size = sizeof (mProvideCurrentCpuInfoTopologyCorePerPackageV1Replace),
.Count = 1,
.Skip = 0,
.Limit = 0
};
STATIC
CONST UINT8
mProvideCurrentCpuInfoTopologyCorePerPackageV1_5Find[] = {
// mov edx, r13d
// shr edx, 0x1a <--- start
// inc edx
// mov dword[], edx
0xC1, 0xEA, 0x1A, 0xFF, 0xC2, 0x89
};
STATIC
CONST UINT8
mProvideCurrentCpuInfoTopologyCorePerPackageV1_5Replace[] = {
// mov edx, r13d
// mov edx, 0x80 <--- start
// mov dword[], edx
0xBA, 0x80, 0x00, 0x00, 0x00, 0x89
};
STATIC
PATCHER_GENERIC_PATCH
mProvideCurrentCpuInfoTopologyCorePerPackageV1_5Patch = {
.Comment = DEBUG_POINTER ("Set core per package count to 128 V1_5"),
.Base = NULL,
.Find = mProvideCurrentCpuInfoTopologyCorePerPackageV1_5Find,
.Mask = NULL,
.Replace = mProvideCurrentCpuInfoTopologyCorePerPackageV1_5Replace,
.ReplaceMask = NULL,
.Size = sizeof (mProvideCurrentCpuInfoTopologyCorePerPackageV1_5Replace),
.Count = 1,
.Skip = 0,
.Limit = 0
};
STATIC
CONST UINT8
mProvideCurrentCpuInfoTopologyCorePerPackageV2Find[] = {
// mov eax/edx, r13d
// shr eax/edx, 0x1a <--- start
// add eax/edx, 1
// mov dword[], eax/edx
0xC1, 0xE8 /* 0xEA */, 0x1A, 0x83, 0xC0 /* 0xC2 */, 0x01, 0x89
};
STATIC
CONST UINT8
mProvideCurrentCpuInfoTopologyCorePerPackageV2Mask[] = {
0xFF, 0xFD, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF
};
STATIC
CONST UINT8
mProvideCurrentCpuInfoTopologyCorePerPackageV2Replace[] = {
// mov eax/edx, r13d
// shr eax/edx, 0x1a <--- start
// nop
// mov al/dl, 128
// mov dword[], eax/edx
0xC1, 0xE8 /* 0xEA */, 0x1A, 0x90, 0xB0 /* 0xB2 */, 0x80, 0x89
};
STATIC
PATCHER_GENERIC_PATCH
mProvideCurrentCpuInfoTopologyCorePerPackageV2Patch = {
.Comment = DEBUG_POINTER ("Set core per package count to 128 V2"),
.Base = NULL,
.Find = mProvideCurrentCpuInfoTopologyCorePerPackageV2Find,
.Mask = mProvideCurrentCpuInfoTopologyCorePerPackageV2Mask,
.Replace = mProvideCurrentCpuInfoTopologyCorePerPackageV2Replace,
.ReplaceMask = mProvideCurrentCpuInfoTopologyCorePerPackageV2Mask,
.Size = sizeof (mProvideCurrentCpuInfoTopologyCorePerPackageV2Replace),
.Count = 1,
.Skip = 0,
.Limit = 0
};
STATIC
EFI_STATUS
PatchProvideCurrentCpuInfoMSR35h (
IN OUT PATCHER_CONTEXT *Patcher,
IN OC_CPU_INFO *CpuInfo,
IN UINT32 KernelVersion
)
{
EFI_STATUS Status;
UINT32 CoreThreadCount;
BOOLEAN IsAmpCpu;
//
// TODO: We can support older, just there is no real need.
// Anyone can test/contribute as needed.
//
if (KernelVersion < KERNEL_VERSION_MOJAVE_MIN) {
DEBUG ((DEBUG_INFO, "OCAK: Ignoring CPU INFO for AMP below macOS 10.14\n"));
return EFI_SUCCESS;
}
IsAmpCpu = (CpuInfo->ThreadCount % CpuInfo->CoreCount) != 0;
//
// Provide real values for normal CPUs.
// Provide Thread=Thread for AMP (ADL) CPUs.
//
if (IsAmpCpu) {
CoreThreadCount =
(((UINT32)CpuInfo->ThreadCount) << 16U) | ((UINT32)CpuInfo->ThreadCount);
} else {
CoreThreadCount =
(((UINT32)CpuInfo->CoreCount) << 16U) | ((UINT32)CpuInfo->ThreadCount);
}
CopyMem (
&mProvideCurrentCpuInfoTopologyCoreCountReplace[1],
&CoreThreadCount,
sizeof (UINT32)
);
Status = PatcherApplyGenericPatch (
Patcher,
&mProvideCurrentCpuInfoTopologyCoreCountPatch
);
DEBUG ((DEBUG_INFO, "OCAK: Patching MSR 35h to %08x - %r\n", CoreThreadCount, Status));
if (EFI_ERROR (Status) || !IsAmpCpu) {
return Status;
}
if (KernelVersion >= KERNEL_VERSION_MONTEREY_MIN) {
Status = PatcherApplyGenericPatch (
Patcher,
&mProvideCurrentCpuInfoTopologyCorePerPackageV2Patch
);
} else {
Status = PatcherApplyGenericPatch (
Patcher,
&mProvideCurrentCpuInfoTopologyCorePerPackageV1Patch
);
if (EFI_ERROR (Status)) {
Status = PatcherApplyGenericPatch (
Patcher,
&mProvideCurrentCpuInfoTopologyCorePerPackageV1_5Patch
);
}
}
DEBUG ((DEBUG_INFO, "OCAK: Patching core per package count - %r\n", Status));
return Status;
}
EFI_STATUS
PatchProvideCurrentCpuInfo (
IN OUT PATCHER_CONTEXT *Patcher,
IN OC_CPU_INFO *CpuInfo,
IN UINT32 KernelVersion
)
{
EFI_STATUS Status;
UINT8 *Start;
MACH_SECTION_ANY *DataSection;
MACH_SECTION_ANY *TextSection;
INT32 Delta;
UINT64 LocationAddr;
UINT64 VarAddr64;
UINT8 *TscInitFunc;
UINT8 *TmrCvtFunc;
UINT8 *BusFreq;
UINT8 *BusFCvtt2n;
UINT8 *BusFCvtn2t;
UINT8 *TscFreq;
UINT8 *TscFCvtt2n;
UINT8 *TscFCvtn2t;
UINT8 *TscGranularity;
UINT8 *Bus2Tsc;
UINT8 *TscLocation;
UINT64 busFreqValue;
UINT64 busFCvtt2nValue;
UINT64 busFCvtn2tValue;
UINT64 tscFreqValue;
UINT64 tscFCvtt2nValue;
UINT64 tscFCvtn2tValue;
UINT64 tscGranularityValue;
UINT32 msrCoreThreadCount;
ASSERT (Patcher != NULL);
Status = EFI_SUCCESS;
Status |= PatchProvideCurrentCpuInfoMSR35h (Patcher, CpuInfo, KernelVersion);
Start = ((UINT8 *)MachoGetMachHeader (&Patcher->MachContext));
//
// 10.6 and below has variables in __DATA/__data instead of __DATA/__common
//
if (OcMatchDarwinVersion (KernelVersion, KERNEL_VERSION_LION_MIN, 0)) {
DataSection = MachoGetSegmentSectionByName (&Patcher->MachContext, "__DATA", "__common");
} else {
DataSection = MachoGetSegmentSectionByName (&Patcher->MachContext, "__DATA", "__data");
}
TextSection = MachoGetSegmentSectionByName (&Patcher->MachContext, "__TEXT", "__text");
//
// Pull required symbols.
//
Status |= PatcherGetSymbolAddress (Patcher, "_tsc_init", (UINT8 **)&TscInitFunc);
Status |= PatcherGetSymbolAddress (Patcher, "_tmrCvt", (UINT8 **)&TmrCvtFunc);
Status |= PatcherGetSymbolAddress (Patcher, "_busFreq", (UINT8 **)&BusFreq);
Status |= PatcherGetSymbolAddress (Patcher, "_busFCvtt2n", (UINT8 **)&BusFCvtt2n);
Status |= PatcherGetSymbolAddress (Patcher, "_busFCvtn2t", (UINT8 **)&BusFCvtn2t);
Status |= PatcherGetSymbolAddress (Patcher, "_tscFreq", (UINT8 **)&TscFreq);
Status |= PatcherGetSymbolAddress (Patcher, "_tscFCvtt2n", (UINT8 **)&TscFCvtt2n);
Status |= PatcherGetSymbolAddress (Patcher, "_tscFCvtn2t", (UINT8 **)&TscFCvtn2t);
Status |= PatcherGetSymbolAddress (Patcher, "_tscGranularity", (UINT8 **)&TscGranularity);
Status |= PatcherGetSymbolAddress (Patcher, "_bus2tsc", (UINT8 **)&Bus2Tsc);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "OCAK: Failed to locate one or more TSC symbols - %r\n", Status));
return EFI_NOT_FOUND;
}
//
// Perform TSC and FSB calculations. This is traditionally done in tsc.c in XNU.
//
busFreqValue = CpuInfo->FSBFrequency;
busFCvtt2nValue = DivU64x64Remainder ((1000000000ULL << 32), busFreqValue, NULL);
busFCvtn2tValue = DivU64x64Remainder (0xFFFFFFFFFFFFFFFFULL, busFCvtt2nValue, NULL);
tscFreqValue = CpuInfo->CPUFrequency;
tscFCvtt2nValue = DivU64x64Remainder ((1000000000ULL << 32), tscFreqValue, NULL);
tscFCvtn2tValue = DivU64x64Remainder (0xFFFFFFFFFFFFFFFFULL, tscFCvtt2nValue, NULL);
tscGranularityValue = DivU64x64Remainder (tscFreqValue, busFreqValue, NULL);
DEBUG ((DEBUG_INFO, "OCAK: BusFreq = %LuHz, BusFCvtt2n = %Lu, BusFCvtn2t = %Lu\n", busFreqValue, busFCvtt2nValue, busFCvtn2tValue));
DEBUG ((DEBUG_INFO, "OCAK: TscFreq = %LuHz, TscFCvtt2n = %Lu, TscFCvtn2t = %Lu\n", tscFreqValue, tscFCvtt2nValue, tscFCvtn2tValue));
//
// Patch _tsc_init with above values.
//
TscLocation = TscInitFunc;
TscLocation = PatchMovVar (TscLocation, Start, DataSection, TextSection, Patcher->Is32Bit, BusFreq, busFreqValue);
TscLocation = PatchMovVar (TscLocation, Start, DataSection, TextSection, Patcher->Is32Bit, BusFCvtt2n, busFCvtt2nValue);
TscLocation = PatchMovVar (TscLocation, Start, DataSection, TextSection, Patcher->Is32Bit, BusFCvtn2t, busFCvtn2tValue);
TscLocation = PatchMovVar (TscLocation, Start, DataSection, TextSection, Patcher->Is32Bit, TscFreq, tscFreqValue);
TscLocation = PatchMovVar (TscLocation, Start, DataSection, TextSection, Patcher->Is32Bit, TscFCvtt2n, tscFCvtt2nValue);
TscLocation = PatchMovVar (TscLocation, Start, DataSection, TextSection, Patcher->Is32Bit, TscFCvtn2t, tscFCvtn2tValue);
TscLocation = PatchMovVar (TscLocation, Start, DataSection, TextSection, Patcher->Is32Bit, TscGranularity, tscGranularityValue);
TscLocation = PatchMovVar (TscLocation, Start, DataSection, TextSection, Patcher->Is32Bit, BusFreq, busFreqValue);
if (Patcher->Is32Bit) {
// TODO
} else {
//
// mov rdi, FSB freq
//
*TscLocation++ = 0x48;
*TscLocation++ = 0xBF;
CopyMem (TscLocation, &busFreqValue, sizeof (busFreqValue));
TscLocation += sizeof (busFreqValue);
//
// mov rsi, TSC freq
//
*TscLocation++ = 0x48;
*TscLocation++ = 0xBE;
CopyMem (TscLocation, &tscFreqValue, sizeof (tscFreqValue));
TscLocation += sizeof (tscFreqValue);
//
// call _tmrCvt
//
Delta = (INT32)(TmrCvtFunc - (TscLocation + 5));
*TscLocation++ = 0xE8;
CopyMem (TscLocation, &Delta, sizeof (Delta));
TscLocation += sizeof (Delta);
//
// mov [_bus2tsc], rax
//
LocationAddr = (TscLocation - Start) + TextSection->Section64.Address - TextSection->Section64.Offset;
VarAddr64 = (Bus2Tsc - Start) + DataSection->Section64.Address - DataSection->Section64.Offset;
Delta = (INT32)(VarAddr64 - (LocationAddr + 7));
*TscLocation++ = 0x48;
*TscLocation++ = 0x89;
*TscLocation++ = 0x05;
CopyMem (TscLocation, &Delta, sizeof (Delta));
TscLocation += sizeof (Delta);
}
//
// ret
//
*TscLocation++ = 0xC3;
//
// Patch MSR 0x35 fallback value on 10.13 and above.
//
// This value is used if the MSR 0x35 is read as zero, typically on VMs or AMD processors.
//
if (OcMatchDarwinVersion (KernelVersion, KERNEL_VERSION_HIGH_SIERRA_MIN, 0)) {
//
// 10.15 and above have two instances that need patching.
//
if (OcMatchDarwinVersion (KernelVersion, KERNEL_VERSION_CATALINA_MIN, 0)) {
mProvideCurrentCpuInfoZeroMsrThreadCoreCountPatch.Count = 2;
}
msrCoreThreadCount = (CpuInfo->CoreCount << 16) | CpuInfo->ThreadCount;
CopyMem (
&mProvideCurrentCpuInfoZeroMsrThreadCoreCountReplace[CURRENT_CPU_INFO_CORE_COUNT_OFFSET],
&msrCoreThreadCount,
sizeof (msrCoreThreadCount)
);
Status = PatcherApplyGenericPatch (
Patcher,
&mProvideCurrentCpuInfoZeroMsrThreadCoreCountPatch
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "OCAK: Failed to find CPU MSR 0x35 default value patch - %r\n", Status));
}
} else {
DEBUG ((DEBUG_INFO, "OCAK: Skipping CPU MSR 0x35 default value patch on %u\n", KernelVersion));
}
//
// Disable _x86_validate_topology on 10.13 and above.
//
if (OcMatchDarwinVersion (KernelVersion, KERNEL_VERSION_HIGH_SIERRA_MIN, 0)) {
Status = PatcherApplyGenericPatch (
Patcher,
&mProvideCurrentCpuInfoTopologyValidationPatch
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "OCAK: Failed to find CPU topology validation patch - %r\n", Status));
}
} else {
DEBUG ((DEBUG_INFO, "OCAK: Skipping CPU topology validation patch on %u\n", KernelVersion));
}
return EFI_SUCCESS;
}