2022-07-24 22:11:50 -05:00

1796 lines
55 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 *LocationSnow32;
UINT8 *LocationTigerTsc;
UINT8 *LocationTigerTscEnd;
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;
LocationTigerTsc = NULL;
LocationTigerTscEnd = 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 are 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 = 0; 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;
}
LocationTigerTsc = 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;
}
LocationTigerTscEnd = 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 ? LocationTigerTsc - Start : 0,
IsTigerOld ? LocationTigerTscEnd - 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 - (LocationTigerTsc + 5));
*LocationTigerTsc++ = 0xE8;
CopyMem (LocationTigerTsc, &Delta, sizeof (Delta));
LocationTigerTsc += sizeof (Delta);
while (LocationTigerTsc < LocationTigerTscEnd) {
*LocationTigerTsc++ = 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 BOOLEAN Is32Bit,
IN OUT UINT64 *FuncAddr,
IN UINT64 VarSymAddr,
IN UINT64 Value
)
{
UINT8 *Start;
INT32 Delta;
UINT32 VarAddr32;
UINT32 ValueLower;
UINT32 ValueUpper;
Start = Location;
DEBUG ((DEBUG_VERBOSE, "OCAK: Current TSC func address: 0x%llX, variable address: 0x%llX\n", *FuncAddr, VarSymAddr));
if (Is32Bit) {
ValueLower = (UINT32)Value;
ValueUpper = (UINT32)(Value >> 32);
//
// 32-bit uses absolute addressing
//
// mov [var], value lower
//
VarAddr32 = (UINT32)(VarSymAddr);
*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);
*FuncAddr += (Location - Start);
} else {
//
// mov rax, value
//
*Location++ = 0x48;
*Location++ = 0xB8;
CopyMem (Location, &Value, sizeof (Value));
Location += sizeof (Value);
*FuncAddr += sizeof (Value) + 2;
//
// mov [var], rax
//
Delta = (INT32)(VarSymAddr - (*FuncAddr + 7));
DEBUG ((DEBUG_VERBOSE, "OCAK: TSC func delta 0x%X\n", Delta));
*Location++ = 0x48;
*Location++ = 0x89;
*Location++ = 0x05;
CopyMem (Location, &Delta, sizeof (Delta));
Location += sizeof (Delta);
*FuncAddr += sizeof (Delta) + 3;
}
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;
INT32 Delta;
UINT32 VarAddr32;
UINT32 ValueLower;
UINT32 ValueUpper;
UINT8 *Record;
UINT8 *Start;
UINT8 *Last;
UINT32 Index;
UINT8 *TscInitFunc;
UINT8 *TmrCvtFunc;
UINT64 TscInitFuncSymAddr;
UINT64 BusFreqSymAddr;
UINT64 BusFCvtt2nSymAddr;
UINT64 BusFCvtn2tSymAddr;
UINT64 TscFreqSymAddr;
UINT64 TscFCvtt2nSymAddr;
UINT64 TscFCvtn2tSymAddr;
UINT64 TscGranularitySymAddr;
UINT64 Bus2TscSymAddr;
UINT8 *TscLocation;
UINT64 busFreqValue;
UINT64 busFCvtt2nValue;
UINT64 busFCvtn2tValue;
UINT64 tscFreqValue;
UINT64 tscFCvtt2nValue;
UINT64 tscFCvtn2tValue;
UINT64 tscGranularityValue;
BOOLEAN IsTiger;
BOOLEAN IsTigerCacheUnsupported;
UINT8 *LocationTigerCache;
UINT8 *LocationTigerCacheEnd;
APPLE_INTEL_CPU_CACHE_TYPE CacheType;
CPUID_CACHE_PARAMS_EAX CpuidCacheEax;
CPUID_CACHE_PARAMS_EBX CpuidCacheEbx;
UINT32 CacheSets;
UINT32 CacheSizes[LCACHE_MAX];
UINT32 CacheLineSizes[LCACHE_MAX];
UINT32 CacheLineSize;
UINT32 msrCoreThreadCount;
ASSERT (Patcher != NULL);
LocationTigerCache = NULL;
LocationTigerCacheEnd = NULL;
Start = ((UINT8 *)MachoGetMachHeader (&Patcher->MachContext));
Last = Start + MachoGetInnerSize (&Patcher->MachContext) - EFI_PAGE_SIZE * 2;
IsTiger = OcMatchDarwinVersion (KernelVersion, KERNEL_VERSION_TIGER_MIN, KERNEL_VERSION_TIGER_MAX);
//
// 10.4 does not support pulling CPUID leaf 4 that may contain cache info instead of leaf 2.
// On processors that support leaf 4, use that instead.
//
IsTigerCacheUnsupported = IsTiger && (CpuInfo->MaxId >= CPUID_CACHE_PARAMS);
Status = EFI_SUCCESS;
Status |= PatchProvideCurrentCpuInfoMSR35h (Patcher, CpuInfo, KernelVersion);
//
// Pull required symbols.
//
Status |= PatcherGetSymbolAddressValue (Patcher, "_tsc_init", (UINT8 **)&TscInitFunc, &TscInitFuncSymAddr);
Status |= PatcherGetSymbolAddress (Patcher, "_tmrCvt", (UINT8 **)&TmrCvtFunc);
//
// _busFreq only exists on 10.5 and higher.
//
if (!IsTiger) {
Status |= PatcherGetSymbolValue (Patcher, "_busFreq", &BusFreqSymAddr);
}
Status |= PatcherGetSymbolValue (Patcher, "_busFCvtt2n", &BusFCvtt2nSymAddr);
Status |= PatcherGetSymbolValue (Patcher, "_busFCvtn2t", &BusFCvtn2tSymAddr);
Status |= PatcherGetSymbolValue (Patcher, "_tscFreq", &TscFreqSymAddr);
Status |= PatcherGetSymbolValue (Patcher, "_tscFCvtt2n", &TscFCvtt2nSymAddr);
Status |= PatcherGetSymbolValue (Patcher, "_tscFCvtn2t", &TscFCvtn2tSymAddr);
Status |= PatcherGetSymbolValue (Patcher, "_tscGranularity", &TscGranularitySymAddr);
Status |= PatcherGetSymbolValue (Patcher, "_bus2tsc", &Bus2TscSymAddr);
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;
if (OcMatchDarwinVersion (KernelVersion, KERNEL_VERSION_LEOPARD_MIN, 0)) {
TscLocation = PatchMovVar (TscLocation, Patcher->Is32Bit, &TscInitFuncSymAddr, BusFreqSymAddr, busFreqValue);
}
TscLocation = PatchMovVar (TscLocation, Patcher->Is32Bit, &TscInitFuncSymAddr, BusFCvtt2nSymAddr, busFCvtt2nValue);
TscLocation = PatchMovVar (TscLocation, Patcher->Is32Bit, &TscInitFuncSymAddr, BusFCvtn2tSymAddr, busFCvtn2tValue);
TscLocation = PatchMovVar (TscLocation, Patcher->Is32Bit, &TscInitFuncSymAddr, TscFreqSymAddr, tscFreqValue);
TscLocation = PatchMovVar (TscLocation, Patcher->Is32Bit, &TscInitFuncSymAddr, TscFCvtt2nSymAddr, tscFCvtt2nValue);
TscLocation = PatchMovVar (TscLocation, Patcher->Is32Bit, &TscInitFuncSymAddr, TscFCvtn2tSymAddr, tscFCvtn2tValue);
TscLocation = PatchMovVar (TscLocation, Patcher->Is32Bit, &TscInitFuncSymAddr, TscGranularitySymAddr, tscGranularityValue);
if (Patcher->Is32Bit) {
//
// push ebp
// move ebp, esp
//
*TscLocation++ = 0x55;
*TscLocation++ = 0x89;
*TscLocation++ = 0xE5;
ValueLower = (UINT32)busFreqValue;
ValueUpper = (UINT32)(busFreqValue >> 32);
//
// mov eax, FSB freq (lower)
//
*TscLocation++ = 0xB8;
CopyMem (TscLocation, &ValueLower, sizeof (ValueLower));
TscLocation += sizeof (ValueLower);
//
// push eax
//
*TscLocation++ = 0x50;
//
// mov eax, FSB freq (higher)
//
*TscLocation++ = 0xB8;
CopyMem (TscLocation, &ValueUpper, sizeof (ValueUpper));
TscLocation += sizeof (ValueUpper);
//
// push eax
//
*TscLocation++ = 0x50;
ValueLower = (UINT32)tscFreqValue;
ValueUpper = (UINT32)(tscFreqValue >> 32);
//
// mov eax, TSC freq (lower)
//
*TscLocation++ = 0xB8;
CopyMem (TscLocation, &ValueLower, sizeof (ValueLower));
TscLocation += sizeof (ValueLower);
//
// push eax
//
*TscLocation++ = 0x50;
//
// mov eax, TSC freq (higher)
//
*TscLocation++ = 0xB8;
CopyMem (TscLocation, &ValueUpper, sizeof (ValueUpper));
TscLocation += sizeof (ValueUpper);
//
// push eax
//
*TscLocation++ = 0x50;
//
// call _tmrCvt(busFCvtt2n, tscFCvtn2t)
//
Delta = (INT32)(TmrCvtFunc - (TscLocation + 5));
*TscLocation++ = 0xE8;
CopyMem (TscLocation, &Delta, sizeof (Delta));
TscLocation += sizeof (Delta);
//
// mov [_bus2tsc], eax
//
VarAddr32 = (UINT32)(Bus2TscSymAddr);
*TscLocation++ = 0xA3;
CopyMem (TscLocation, &VarAddr32, sizeof (VarAddr32));
TscLocation += sizeof (VarAddr32);
//
// mov [_bus2tsc+4], edx
//
VarAddr32 += sizeof (UINT32);
*TscLocation++ = 0x89;
*TscLocation++ = 0x15;
CopyMem (TscLocation, &VarAddr32, sizeof (VarAddr32));
TscLocation += sizeof (VarAddr32);
//
// pop eax (x4)
// leave
//
*TscLocation++ = 0x58;
*TscLocation++ = 0x58;
*TscLocation++ = 0x58;
*TscLocation++ = 0x58;
*TscLocation++ = 0xC9;
} else {
//
// mov rdi, FSB freq
//
*TscLocation++ = 0x48;
*TscLocation++ = 0xBF;
CopyMem (TscLocation, &busFreqValue, sizeof (busFreqValue));
TscLocation += sizeof (busFreqValue);
TscInitFuncSymAddr += sizeof (busFreqValue) + 2;
//
// mov rsi, TSC freq
//
*TscLocation++ = 0x48;
*TscLocation++ = 0xBE;
CopyMem (TscLocation, &tscFreqValue, sizeof (tscFreqValue));
TscLocation += sizeof (tscFreqValue);
TscInitFuncSymAddr += sizeof (tscFreqValue) + 2;
//
// call _tmrCvt(busFCvtt2n, tscFCvtn2t)
//
Delta = (INT32)(TmrCvtFunc - (TscLocation + 5));
*TscLocation++ = 0xE8;
CopyMem (TscLocation, &Delta, sizeof (Delta));
TscLocation += sizeof (Delta);
TscInitFuncSymAddr += sizeof (Delta) + 1;
//
// mov [_bus2tsc], rax
//
Delta = (INT32)(Bus2TscSymAddr - (TscInitFuncSymAddr + 7));
*TscLocation++ = 0x48;
*TscLocation++ = 0x89;
*TscLocation++ = 0x05;
CopyMem (TscLocation, &Delta, sizeof (Delta));
TscLocation += sizeof (Delta);
}
//
// ret
//
*TscLocation++ = 0xC3;
//
// Find patch region for injecting CPU cache information
// into set_intel_cache_info.
//
if (IsTigerCacheUnsupported) {
Status = PatcherGetSymbolAddress (Patcher, "_cpuid_info", (UINT8 **)&Record);
if (EFI_ERROR (Status) || (Record >= Last)) {
DEBUG ((DEBUG_WARN, "OCAK: Failed to locate _cpuid_info (%p) - %r\n", Record, Status));
return EFI_NOT_FOUND;
}
//
// Start of patch area.
// We'll use this area to populate info_p.
//
STATIC CONST UINT8 mKernelCpuidFindCacheLocTigerStart[9] = {
0x8B, 0x45, 0x08, // mov eax, dword [ebp+0x8] (*info_p argument)
0x8B, 0x50, 0x74, // mov edx, dword [eax+0x74]
0x85, 0xD2, // test edx, edx
0x75 // jne ...
};
//
// End of patch area.
//
STATIC CONST UINT8 mKernelCpuidFindCacheLocTigerEnd[5] = {
0x31, 0xC0, // xor eax, eax
0x0F, 0xA2, // cpuid
0x89 // mov ...
};
for (Index = 0; Index < EFI_PAGE_SIZE; Index++, Record++) {
if ( (Record[0] == mKernelCpuidFindCacheLocTigerStart[0])
&& (Record[1] == mKernelCpuidFindCacheLocTigerStart[1])
&& (Record[2] == mKernelCpuidFindCacheLocTigerStart[2])
&& (Record[3] == mKernelCpuidFindCacheLocTigerStart[3])
&& (Record[4] == mKernelCpuidFindCacheLocTigerStart[4])
&& (Record[5] == mKernelCpuidFindCacheLocTigerStart[5])
&& (Record[6] == mKernelCpuidFindCacheLocTigerStart[6])
&& (Record[7] == mKernelCpuidFindCacheLocTigerStart[7])
&& (Record[8] == mKernelCpuidFindCacheLocTigerStart[8]))
{
break;
}
}
if (Index >= EFI_PAGE_SIZE) {
return EFI_NOT_FOUND;
}
LocationTigerCache = Record + 3;
for ( ; Index < EFI_PAGE_SIZE; Index++, Record++) {
if ( (Record[0] == mKernelCpuidFindCacheLocTigerEnd[0])
&& (Record[1] == mKernelCpuidFindCacheLocTigerEnd[1])
&& (Record[2] == mKernelCpuidFindCacheLocTigerEnd[2])
&& (Record[3] == mKernelCpuidFindCacheLocTigerEnd[3])
&& (Record[4] == mKernelCpuidFindCacheLocTigerEnd[4]))
{
break;
}
}
if (Index >= EFI_PAGE_SIZE) {
return EFI_NOT_FOUND;
}
LocationTigerCacheEnd = Record;
DEBUG ((
DEBUG_INFO,
"OCAK: CPU info cache loc %p:%p\n",
LocationTigerCache - Start,
LocationTigerCacheEnd - Start
));
ZeroMem (CacheLineSizes, sizeof (CacheLineSizes));
//
// Build CPU info struct
//
Index = 0;
do {
AsmCpuidEx (CPUID_CACHE_PARAMS, Index, &CpuidCacheEax.Uint32, &CpuidCacheEbx.Uint32, &CacheSets, NULL);
if (CpuidCacheEax.Bits.CacheType == 0) {
break;
}
switch (CpuidCacheEax.Bits.CacheLevel) {
case 1:
CacheType = (CpuidCacheEax.Bits.CacheType == 1) ? L1D :
(CpuidCacheEax.Bits.CacheType == 2) ? L1I :
Lnone;
break;
case 2:
CacheType = (CpuidCacheEax.Bits.CacheType == 3) ? L2U :
Lnone;
break;
case 3:
CacheType = (CpuidCacheEax.Bits.CacheType == 3) ? L3U :
Lnone;
break;
default:
CacheType = Lnone;
}
if (CacheType != Lnone) {
CacheSizes[CacheType] = (CpuidCacheEbx.Bits.LineSize + 1) * (CacheSets + 1) * (CpuidCacheEbx.Bits.Ways + 1);
CacheLineSizes[CacheType] = CpuidCacheEbx.Bits.LineSize + 1;
}
Index++;
} while (TRUE);
if (CacheLineSizes[L2U]) {
CacheLineSize = CacheLineSizes[L2U];
} else if (CacheLineSizes[L1D]) {
CacheLineSize = CacheLineSizes[L1D];
} else {
//
// XNU would panic here.
//
DEBUG ((DEBUG_WARN, "OCAK: Unable to determine CPU cache line size\n"));
return EFI_UNSUPPORTED;
}
//
// Populate cache sizes.
// EAX = address of *info_p struct in stack; 0x68 = offset of cache size array in *info_p struct.
//
for (Index = L1I; Index < LCACHE_MAX; Index++) {
*LocationTigerCache++ = 0xC7;
*LocationTigerCache++ = 0x40;
*LocationTigerCache++ = 0x68 + ((UINT8)(sizeof (UINT32) * Index));
CopyMem (LocationTigerCache, &CacheSizes[Index], sizeof (CacheSizes[Index]));
LocationTigerCache += sizeof (CacheSizes[Index]);
DEBUG ((DEBUG_INFO, "OCAK: Cache size (L%u): %u bytes\n", (Index >= 3) ? (Index - 1) : 1, CacheSizes[Index]));
}
//
// Populate cache line size.
// 0x7C = offset of cache line size in *info_p struct.
//
*LocationTigerCache++ = 0xC7;
*LocationTigerCache++ = 0x40;
*LocationTigerCache++ = 0x7C;
CopyMem (LocationTigerCache, &CacheLineSize, sizeof (CacheLineSize));
LocationTigerCache += sizeof (CacheLineSize);
DEBUG ((DEBUG_INFO, "OCAK: Cache line size: %u bytes\n", CacheLineSize));
while (LocationTigerCache < LocationTigerCacheEnd) {
*LocationTigerCache++ = 0x90;
}
}
//
// 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;
}