/*
 * Original idea of patching kernel by Evan Lojewsky, 2009
 *
 * Copyright (c) 2011-2012 Frank Peng. All rights reserved.
 *
 * Correction and improvements by Clover team
 */

#include "Platform.h"
#include "LoaderUefi.h"
#include "device_tree.h"

#include "kernel_patcher.h"
#include "sse3_patcher.h"
#include "sse3_5_patcher.h"

#define KERNEL_DEBUG 0

#if KERNEL_DEBUG
#define DBG(...)    AsciiPrint(__VA_ARGS__);
#else
#define DBG(...)
#endif

// runtime debug
#define DBG_RT(entry, ...)    if ((entry != NULL) && (entry->KernelAndKextPatches != NULL) && entry->KernelAndKextPatches->KPDebug) { AsciiPrint(__VA_ARGS__); }


EFI_PHYSICAL_ADDRESS    KernelRelocBase = 0;
BootArgs1   *bootArgs1 = NULL;
BootArgs2   *bootArgs2 = NULL;
CHAR8       *dtRoot = NULL;
VOID        *KernelData = NULL;
UINT32      KernelSlide = 0;
BOOLEAN     isKernelcache = FALSE;
BOOLEAN     is64BitKernel = FALSE;
BOOLEAN     SSSE3;

BOOLEAN     PatcherInited = FALSE;
BOOLEAN     gSNBEAICPUFixRequire = FALSE; // SandyBridge-E AppleIntelCpuPowerManagement patch require or not
BOOLEAN     gBDWEIOPCIFixRequire = FALSE; // Broadwell-E IOPCIFamily fix require or not

// notes:
// - 64bit segCmd64->vmaddr is 0xffffff80xxxxxxxx and we are taking
//   only lower 32bit part into PrelinkTextAddr
// - PrelinkTextAddr is segCmd64->vmaddr + KernelRelocBase
UINT32     PrelinkTextLoadCmdAddr = 0;
UINT32     PrelinkTextAddr = 0;
UINT32     PrelinkTextSize = 0;

// notes:
// - 64bit sect->addr is 0xffffff80xxxxxxxx and we are taking
//   only lower 32bit part into PrelinkInfoAddr
// - PrelinkInfoAddr is sect->addr + KernelRelocBase
UINT32     PrelinkInfoLoadCmdAddr = 0;
UINT32     PrelinkInfoAddr = 0;
UINT32     PrelinkInfoSize = 0;


VOID SetKernelRelocBase()
{
//  EFI_STATUS      Status;
  UINTN           DataSize = sizeof(KernelRelocBase);

  KernelRelocBase = 0;
  // OsxAptioFixDrv will set this
  /*Status = */gRT->GetVariable(L"OsxAptioFixDrv-RelocBase", &gEfiAppleBootGuid, NULL, &DataSize, &KernelRelocBase);
  DeleteNvramVariable(L"OsxAptioFixDrv-RelocBase", &gEfiAppleBootGuid); // clean up the temporary variable
  // KernelRelocBase is now either read or 0
  return;
}

//TimeWalker - extended and corrected for systems up to Yosemite
VOID KernelPatcher_64(VOID* kernelData, LOADER_ENTRY *Entry)
{
  UINT8       *bytes = (UINT8*)kernelData;
  UINT32      patchLocation=0, patchLocation1=0;
  UINT32      i;
  UINT32      switchaddr=0;
  UINT32      mask_family=0, mask_model=0;
  UINT32      cpuid_family_addr=0, cpuid_model_addr=0;
  UINT64      os_version;

  DBG_RT(Entry, "\nLooking for _cpuid_set_info _panic ...\n");

  // Determine location of _cpuid_set_info _panic call for reference
  // basically looking for info_p->cpuid_model = bitfield32(reg[eax],  7,  4);
  for (i=0; i<0x1000000; i++) {
    if (bytes[i+ 0] == 0xC7 && bytes[i+ 1] == 0x05 && bytes[i+ 5] == 0x00 &&
        bytes[i+ 6] == 0x07 && bytes[i+ 7] == 0x00 && bytes[i+ 8] == 0x00 && bytes[i+ 9] == 0x00 &&
        bytes[i-5] == 0xE8) { // matching 0xE8 for _panic call start
      patchLocation = i-5;
      break;
    }
  }

  if (!patchLocation) {
    DBG_RT(Entry, "_cpuid_set_info Unsupported CPU _panic not found \n");
    return;
  }

  os_version = AsciiOSVersionToUint64(Entry->OSVersion);

  // make sure only kernels for OSX 10.6.0 to 10.7.3 are being patched by this approach
  if (os_version >= AsciiOSVersionToUint64("10.6") && os_version <= AsciiOSVersionToUint64("10.7.3")) {

    DBG_RT(Entry, "will patch kernel for macOS 10.6.0 to 10.7.3\n");

    // remove tsc_init: unknown CPU family panic for kernels prior to 10.6.2 which still had Atom support
    if (os_version < AsciiOSVersionToUint64("10.6.2")) {
      for (i=0; i<0x1000000; i++) {
        // find _tsc_init panic address by byte sequence 488d3df4632a00
        if (bytes[i] == 0x48 && bytes[i+1] == 0x8D && bytes[i+2] == 0x3D && bytes[i+3] == 0xF4 &&
            bytes[i+4] == 0x63 && bytes[i+5] == 0x2A && bytes[i+6] == 0x00) {
          patchLocation1 = i+9;
          DBG_RT(Entry, "Found _tsc_init _panic address at 0x%08x\n",patchLocation1);
          break;
        }
      }

      // NOP _panic call
      if (patchLocation1) {
        bytes[patchLocation1 + 0] = 0x90;
        bytes[patchLocation1 + 1] = 0x90;
        bytes[patchLocation1 + 2] = 0x90;
        bytes[patchLocation1 + 3] = 0x90;
        bytes[patchLocation1 + 4] = 0x90;
      }
    }
    else { // assume patching logic for OSX 10.6.2 to 10.7.3

      /*
       Here is our case from CPUID switch statement, it sets CPUFAMILY_UNKNOWN
       C7051C2C5F0000000000   mov     dword [ds:0xffffff80008a22c0], 0x0 (example from 10.7)
       */
      switchaddr = patchLocation - 19;
      DBG_RT(Entry, "switch statement patch location is 0x%08x\n", (switchaddr+6));

      if (bytes[switchaddr + 0] == 0xC7 && bytes[switchaddr + 1] == 0x05 &&
          bytes[switchaddr + 5] == 0x00 && bytes[switchaddr + 6] == 0x00 &&
          bytes[switchaddr + 7] == 0x00 && bytes[switchaddr + 8] == 0x00) {

        // Determine cpuid_family address from above mov operation
        cpuid_family_addr =
        bytes[switchaddr + 2] <<  0 |
        bytes[switchaddr + 3] <<  8 |
        bytes[switchaddr + 4] << 16 |
        bytes[switchaddr + 5] << 24;
        cpuid_family_addr = cpuid_family_addr + (switchaddr + 10);

        if (cpuid_family_addr) {

          // Determine cpuid_model address
          // for 10.6.2 kernels it's offset by 299 bytes from cpuid_family address
          if (os_version ==  AsciiOSVersionToUint64("10.6.2")) {
            cpuid_model_addr = cpuid_family_addr - 0X12B;
          }
          // for 10.6.3 to 10.6.7 it's offset by 303 bytes
          else if (os_version <= AsciiOSVersionToUint64("10.6.7")) {
            cpuid_model_addr = cpuid_family_addr - 0X12F;
          }
          // for 10.6.8 to 10.7.3 kernels - by 339 bytes
          else {
            cpuid_model_addr = cpuid_family_addr - 0X153;
          }

          DBG_RT(Entry, "cpuid_family address: 0x%08x\n", cpuid_family_addr);
          DBG_RT(Entry, "cpuid_model address: 0x%08x\n",  cpuid_model_addr);

          switchaddr += 6; // offset 6 bytes in mov operation to write a dword instead of zero

          // calculate mask for patching, cpuid_family mask not needed as we offset on a valid mask
          mask_model   = cpuid_model_addr - (switchaddr+14);
          DBG_RT(Entry, "model mask 0x%08x\n", mask_model);

          DBG_RT(Entry, "overriding cpuid_family and cpuid_model as CPUID_INTEL_PENRYN\n");
          bytes[switchaddr+0] = (CPUFAMILY_INTEL_PENRYN & 0x000000FF) >>  0;
          bytes[switchaddr+1] = (CPUFAMILY_INTEL_PENRYN & 0x0000FF00) >>  8;
          bytes[switchaddr+2] = (CPUFAMILY_INTEL_PENRYN & 0x00FF0000) >> 16;
          bytes[switchaddr+3] = (CPUFAMILY_INTEL_PENRYN & 0xFF000000) >> 24;

          // mov  dword [ds:0xffffff80008a216d], 0x2000117
          bytes[switchaddr+4] = 0xC7;
          bytes[switchaddr+5] = 0x05;
          bytes[switchaddr+6] = (UINT8)((mask_model & 0x000000FF) >> 0);
          bytes[switchaddr+7] = (UINT8)((mask_model & 0x0000FF00) >> 8);
          bytes[switchaddr+8] = (UINT8)((mask_model & 0x00FF0000) >> 16);
          bytes[switchaddr+9] = (UINT8)((mask_model & 0xFF000000) >> 24);
          bytes[switchaddr+10] = 0x17; // cpuid_model (Penryn)
          bytes[switchaddr+11] = 0x01; // cpuid_extmodel
          bytes[switchaddr+12] = 0x00; // cpuid_extfamily
          bytes[switchaddr+13] = 0x02; // cpuid_stepping

          // fill remainder with 4 NOPs
          for (i=14; i<18; i++) {
            bytes[switchaddr+i] = 0x90;
          }
        }
      }
      else {
        DBG_RT(Entry, "Unable to determine cpuid_family address, patching aborted\n");
        return;
      }
    }

    // patch ssse3
    if (!SSSE3 && (AsciiStrnCmp(Entry->OSVersion,"10.6",4)==0)) {
      Patcher_SSE3_6((VOID*)bytes);
    }
    if (!SSSE3 && (AsciiStrnCmp(Entry->OSVersion,"10.7",4)==0)) {
      Patcher_SSE3_7((VOID*)bytes);
    }
  }

  // all 10.7.4+ kernels share common CPUID switch statement logic,
  // it needs to be exploited in diff manner due to the lack of space
  else if (os_version >= AsciiOSVersionToUint64("10.7.4")) {

    DBG_RT(Entry, "will patch kernel for macOS 10.7.4+\n");

    /*
     Here is our switchaddress location ... it should be case 20 from CPUID switch statement
     833D78945F0000  cmp        dword [ds:0xffffff80008a21d0], 0x0;
     7417            je         0xffffff80002a8d71
     */
    switchaddr = patchLocation-45;
    DBG_RT(Entry, "switch statement patch location is 0x%08x\n", switchaddr);

    if(bytes[switchaddr + 0] == 0x83 && bytes[switchaddr + 1] == 0x3D &&
       bytes[switchaddr + 5] == 0x00 && bytes[switchaddr + 6] == 0x00 &&
       bytes[switchaddr + 7] == 0x74) {

      // Determine cpuid_family address
      // 891D4F945F00    mov        dword [ds:0xffffff80008a21a0], ebx
      cpuid_family_addr =
      bytes[switchaddr - 4] <<  0 |
      bytes[switchaddr - 3] <<  8 |
      bytes[switchaddr - 2] << 16 |
      bytes[switchaddr - 1] << 24;
      cpuid_family_addr = cpuid_family_addr + switchaddr;

      if (cpuid_family_addr) {

        // Determine cpuid_model address
        // for 10.6.8+ kernels it's 339 bytes apart from cpuid_family address
        cpuid_model_addr = cpuid_family_addr - 0X153;

        DBG_RT(Entry, "cpuid_family address: 0x%08x\n", cpuid_family_addr);
        DBG_RT(Entry, "cpuid_model address: 0x%08x\n",  cpuid_model_addr);

        // Calculate masks for patching
        mask_family  = cpuid_family_addr - (switchaddr +15);
        mask_model   = cpuid_model_addr -  (switchaddr +25);
        DBG_RT(Entry, "\nfamily mask: 0x%08x \nmodel mask: 0x%08x\n", mask_family, mask_model);

        // retain original
        // test ebx, ebx
        bytes[switchaddr+0] = bytes[patchLocation-13];
        bytes[switchaddr+1] = bytes[patchLocation-12];
        // retain original, but move jump offset by 20 bytes forward
        // jne for above test
        bytes[switchaddr+2] = bytes[patchLocation-11];
        bytes[switchaddr+3] = bytes[patchLocation-10]+0x20;
        // mov ebx, 0x78ea4fbc
        bytes[switchaddr+4] = 0xBB;
        bytes[switchaddr+5] = (CPUFAMILY_INTEL_PENRYN & 0x000000FF) >>  0;
        bytes[switchaddr+6] = (CPUFAMILY_INTEL_PENRYN & 0x0000FF00) >>  8;
        bytes[switchaddr+7] = (CPUFAMILY_INTEL_PENRYN & 0x00FF0000) >> 16;
        bytes[switchaddr+8] = (CPUFAMILY_INTEL_PENRYN & 0xFF000000) >> 24;

        // mov dword, ebx
        bytes[switchaddr+9]  = 0x89;
        bytes[switchaddr+10] = 0x1D;
        // cpuid_cpufamily address 0xffffff80008a21a0
        bytes[switchaddr+11] = (UINT8)((mask_family & 0x000000FF) >> 0);
        bytes[switchaddr+12] = (UINT8)((mask_family & 0x0000FF00) >> 8);
        bytes[switchaddr+13] = (UINT8)((mask_family & 0x00FF0000) >> 16);
        bytes[switchaddr+14] = (UINT8)((mask_family & 0xFF000000) >> 24);

        // mov dword
        bytes[switchaddr+15] = 0xC7;
        bytes[switchaddr+16] = 0x05;
        // cpuid_model address 0xffffff80008b204d
        bytes[switchaddr+17] = (UINT8)((mask_model & 0x000000FF) >> 0);
        bytes[switchaddr+18] = (UINT8)((mask_model & 0x0000FF00) >> 8);
        bytes[switchaddr+19] = (UINT8)((mask_model & 0x00FF0000) >> 16);
        bytes[switchaddr+20] = (UINT8)((mask_model & 0xFF000000) >> 24);

        bytes[switchaddr+21] = 0x17; // cpuid_model
        bytes[switchaddr+22] = 0x01; // cpuid_extmodel
        bytes[switchaddr+23] = 0x00; // cpuid_extfamily
        bytes[switchaddr+24] = 0x02; // cpuid_stepping

        // fill remainder with 25 NOPs
        for (i=25; i<25+25; i++) {
          bytes[switchaddr+i] = 0x90;
        }
      }
    }
    else {
      DBG_RT(Entry, "Unable to determine cpuid_family address, patching aborted\n");
      return;
    }
  }
}

VOID KernelPatcher_32(VOID* kernelData, CHAR8 *OSVersion)
{
  UINT8* bytes = (UINT8*)kernelData;
  UINT32 patchLocation=0, patchLocation1=0;
  UINT32 i;
  UINT32 jumpaddr;

  DBG("Found _cpuid_set_info _panic Start\n");
  // _cpuid_set_info _panic address
  for (i=0; i<0x1000000; i++) {
    if (bytes[i] == 0xC7 && bytes[i+1] == 0x05 && bytes[i+6] == 0x07 && bytes[i+7] == 0x00 &&
        bytes[i+8] == 0x00 && bytes[i+9] == 0x00 && bytes[i+10] == 0xC7 && bytes[i+11] == 0x05 &&
        bytes[i-5] == 0xE8) {
      patchLocation = i-5;
      DBG("Found _cpuid_set_info _panic address at 0x%08x\n",patchLocation);
      break;
    }
  }

  if (!patchLocation) {
    DBG("Can't find _cpuid_set_info _panic address, patch kernel abort.\n",i);
    return;
  }

  // this for 10.6.0 and 10.6.1 kernel and remove tsc.c unknow cpufamily panic
  //  c70424540e5900
  // find _tsc_init panic address
  for (i=0; i<0x1000000; i++) {
    // _cpuid_set_info _panic address
    if (bytes[i] == 0xC7 && bytes[i+1] == 0x04 && bytes[i+2] == 0x24 &&
        bytes[i+3] == 0x54 && bytes[i+4] == 0x0E && bytes[i+5] == 0x59 &&
        bytes[i+6] == 0x00) {
      patchLocation1 = i+7;
      DBG("Found _tsc_init _panic address at 0x%08x\n",patchLocation1);
      break;
    }
  }

  // found _tsc_init panic addres and patch it
  if (patchLocation1) {
    bytes[patchLocation1 + 0] = 0x90;
    bytes[patchLocation1 + 1] = 0x90;
    bytes[patchLocation1 + 2] = 0x90;
    bytes[patchLocation1 + 3] = 0x90;
    bytes[patchLocation1 + 4] = 0x90;
  }
  // end tsc.c panic

  //first move panic code total 5 bytes, if patch cpuid fail still can boot with kernel
  bytes[patchLocation + 0] = 0x90;
  bytes[patchLocation + 1] = 0x90;
  bytes[patchLocation + 2] = 0x90;
  bytes[patchLocation + 3] = 0x90;
  bytes[patchLocation + 4] = 0x90;

  jumpaddr = patchLocation;

  for (i=0;i<500;i++) {
    if (bytes[jumpaddr-i-3] == 0x85 && bytes[jumpaddr-i-2] == 0xC0 &&
        bytes[jumpaddr-i-1] == 0x75 ) {
      jumpaddr -= i;
      bytes[jumpaddr-1] = 0x77;
      if(bytes[patchLocation - 17] == 0xC7)
        bytes[jumpaddr] -=10;

      break;
    }
  }

  if (jumpaddr == patchLocation) {
    DBG("Can't Found jumpaddr address.\n");
    return;  //can't find jump location
  }
  // patch info_p->cpufamily to CPUFAMILY_INTEL_MEROM

  if (bytes[patchLocation - 17] == 0xC7) {
    bytes[patchLocation - 11] = (CPUFAMILY_INTEL_MEROM & 0x000000FF) >>  0;
    bytes[patchLocation - 10] = (CPUFAMILY_INTEL_MEROM & 0x0000FF00) >>  8;
    bytes[patchLocation -  9] = (CPUFAMILY_INTEL_MEROM & 0x00FF0000) >> 16;
    bytes[patchLocation -  8] = (CPUFAMILY_INTEL_MEROM & 0xFF000000) >> 24;
  }

  //patch info->cpuid_cpufamily
  bytes[patchLocation -  7] = 0xC7;
  bytes[patchLocation -  6] = 0x05;
  bytes[patchLocation -  5] = bytes[jumpaddr + 3];
  bytes[patchLocation -  4] = bytes[jumpaddr + 4];
  bytes[patchLocation -  3] = bytes[jumpaddr + 5];
  bytes[patchLocation -  2] = bytes[jumpaddr + 6];

  bytes[patchLocation -  1] = CPUIDFAMILY_DEFAULT; //cpuid_family  need alway set 0x06
  bytes[patchLocation +  0] = CPUID_MODEL_MEROM;   //cpuid_model set CPUID_MODEL_MEROM
  bytes[patchLocation +  1] = 0x01;                //cpuid_extmodel alway set 0x01
  bytes[patchLocation +  2] = 0x00;                //cpuid_extfamily alway set 0x00
  bytes[patchLocation +  3] = 0x90;
  bytes[patchLocation +  4] = 0x90;

  if (OSVersion) {
    if (AsciiStrnCmp(OSVersion,"10.7",4)==0) return;

    if (!SSSE3 && (AsciiStrnCmp(OSVersion,"10.6",4)==0)) {
      Patcher_SSE3_6((VOID*)bytes);
    }
    if (!SSSE3 && (AsciiStrnCmp(OSVersion,"10.5",4)==0)) {
      Patcher_SSE3_5((VOID*)bytes);
    }
  }
}

//Slice - FakeCPUID substitution, (c)2014

//procedure location
STATIC UINT8 StrCpuid1[] = {
  0xb8, 0x01, 0x00, 0x00, 0x00, 0x31, 0xdb, 0x89, 0xd9, 0x89, 0xda, 0x0f, 0xa2};

STATIC UINT8 StrMsr8b[]       = {0xb9, 0x8b, 0x00, 0x00, 0x00, 0x0f, 0x32};

// Snow Leopard
/*
 This patch searches
  and eax, 0xf0   ||    and eax, 0x0f0000
  shr eax, 0x04   ||    shr eax, 0x10
 and replaces to
  mov eax, FakeModel  | mov eax, FakeExt
 */
STATIC UINT8 SnowSearchModel[]  = {0x25, 0xf0, 0x00, 0x00, 0x00, 0xc1, 0xe8, 0x04};
STATIC UINT8 SnowSearchExt[]    = {0x25, 0x00, 0x00, 0x0f, 0x00, 0xc1, 0xe8, 0x10};
STATIC UINT8 SnowReplaceModel[] = {0xb8, 0x07, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90};

// Lion
/*
 This patch searches
  mov ecx, eax
  shr ecx, 0x04   ||  shr ecx, 0x10
 and replaces to
  mov ecx, FakeModel  || mov ecx, FakeExt
 */
STATIC UINT8 LionSearchModel[]  = {0x89, 0xc1, 0xc1, 0xe9, 0x04};
STATIC UINT8 LionSearchExt[]    = {0x89, 0xc1, 0xc1, 0xe9, 0x10};
STATIC UINT8 LionReplaceModel[] = {0xb9, 0x07, 0x00, 0x00, 0x00};

// Mountain Lion/Mavericks
/*
 This patch searches
  mov bl, al     ||   shr eax, 0x10
  shr bl, 0x04   ||   and al,0x0f
 and replaces to
  mov ebx, FakeModel || mov eax, FakeExt
 */
STATIC UINT8 MLMavSearchModel[]   = {0x88, 0xc3, 0xc0, 0xeb, 0x04};
STATIC UINT8 MLMavSearchExt[]     = {0xc1, 0xe8, 0x10, 0x24, 0x0f};
STATIC UINT8 MLMavReplaceModel[]  = {0xbb, 0x0a, 0x00, 0x00, 0x00};
STATIC UINT8 MLMavReplaceExt[]    = {0xb8, 0x02, 0x00, 0x00, 0x00};

// Yosemite/El Capitan/Sierra
/*
 This patch searches
  mov cl, al     ||   mov ecx, eax
  shr cl, 0x04   ||   shr ecx, 0x10
 and replaces to
  mov ecx, FakeModel || mov ecx, FakeExt
 */
STATIC UINT8 YosECSieSearchModel[]   = {0x88, 0xc1, 0xc0, 0xe9, 0x04};
STATIC UINT8 YosECSieSearchExt[]     = {0x89, 0xc1, 0xc1, 0xe9, 0x10};
// Need to use LionReplaceModel

// High Sierra/Mojave
/*
 This patch searches
  mov ecx, ecx   ||   mov ecx, eax
  shr cl, 0x04   ||   shr ecx, 0x10
 and replaces to
  mov ecx, FakeModel || mov ecx, FakeExt
 */
STATIC UINT8 HSieMojSearchModel[]   = {0x89, 0xc1, 0xc0, 0xe9, 0x04};
// Need to use YosECSieSearchExt, LionReplaceModel

// Catalina
/*
 This patch searches
  mov eax, r12   ||   mov eax, r12
  shr al, 0x4    ||   shr eax, 0x10
 and replaces to
  mov eax, FakeModel || mov eax, FakeExt
  nop                || nop
*/
STATIC UINT8 CataSearchModel[]      = {0x44, 0x89, 0xE0, 0xC0, 0xE8, 0x04};
STATIC UINT8 CataSearchExt[]        = {0x44, 0x89, 0xE0, 0xC1, 0xE8, 0x10};
STATIC UINT8 CataReplaceMovEax[]    = {0xB8, 0x00, 0x00, 0x00, 0x00, 0x90}; // mov eax, val || nop

BOOLEAN PatchCPUID(UINT8* bytes, UINT8* Location, INT32 LenLoc,
                   UINT8* Search4, UINT8* Search10, UINT8* ReplaceModel,
                   UINT8* ReplaceExt, INT32 Len, LOADER_ENTRY *Entry)
{
  INT32 patchLocation=0, patchLocation1=0;
  INT32 Adr = 0, Num;
  BOOLEAN Patched = FALSE;
  UINT8 FakeModel = (Entry->KernelAndKextPatches->FakeCPUID >> 4) & 0x0f;
  UINT8 FakeExt = (Entry->KernelAndKextPatches->FakeCPUID >> 0x10) & 0x0f;
  for (Num = 0; Num < 2; Num++) {
    Adr = FindBin(&bytes[Adr], 0x800000 - Adr, Location, LenLoc);
    if (Adr < 0) {
      break;
    }
    DBG_RT(Entry, "found location at %x\n", Adr);
    patchLocation = FindBin(&bytes[Adr], 0x100, Search4, Len);
    if (patchLocation > 0 && patchLocation < 70) {
      //found
      DBG_RT(Entry, "found Model location at %x\n", Adr + patchLocation);
      CopyMem(&bytes[Adr + patchLocation], ReplaceModel, Len);
      bytes[Adr + patchLocation + 1] = FakeModel;
      patchLocation1 = FindBin(&bytes[Adr], 0x100, Search10, Len);
      if (patchLocation1 > 0 && patchLocation1 < 100) {
        DBG_RT(Entry, "found ExtModel location at %x\n", Adr + patchLocation1);
        CopyMem(&bytes[Adr + patchLocation1], ReplaceExt, Len);
        bytes[Adr + patchLocation1 + 1] = FakeExt;
      }
      Patched = TRUE;
    }
  }
  return Patched;
}

VOID KernelCPUIDPatch(UINT8* kernelData, LOADER_ENTRY *Entry)
{
// Snow Leopard patterns
  DBG_RT(Entry, "CPUID: try Snow Leopard patch...\n");
  if (PatchCPUID(kernelData, &StrCpuid1[0], sizeof(StrCpuid1), &SnowSearchModel[0],
                 &SnowSearchExt[0], &SnowReplaceModel[0], &SnowReplaceModel[0],
                 sizeof(SnowSearchModel), Entry)) {
    DBG_RT(Entry, "...done!\n");
    return;
  }
// Lion patterns
  DBG_RT(Entry, "CPUID: try Lion patch...\n");
  if (PatchCPUID(kernelData, &StrMsr8b[0], sizeof(StrMsr8b), &LionSearchModel[0],
                 &LionSearchExt[0], &LionReplaceModel[0], &LionReplaceModel[0],
                 sizeof(LionSearchModel), Entry)) {
    DBG_RT(Entry, "...done!\n");
    return;
  }
// Mountain Lion/Mavericks patterns
  DBG_RT(Entry, "CPUID: try Mountain Lion/Mavericks patch...\n");
  if (PatchCPUID(kernelData, &StrMsr8b[0], sizeof(StrMsr8b), &MLMavSearchModel[0],
                 &MLMavSearchExt[0], &MLMavReplaceModel[0], &MLMavReplaceExt[0],
                 sizeof(MLMavSearchModel), Entry)) {
    DBG_RT(Entry, "...done!\n");
    return;
  }
// Yosemite/El Capitan/Sierra patterns
  DBG_RT(Entry, "CPUID: try Yosemite/El Capitan/Sierra patch...\n");
  if (PatchCPUID(kernelData, &StrMsr8b[0], sizeof(StrMsr8b), &YosECSieSearchModel[0],
                 &YosECSieSearchExt[0], &LionReplaceModel[0], &LionReplaceModel[0],
                 sizeof(YosECSieSearchModel), Entry)) {
    DBG_RT(Entry, "...done!\n");
    return;
  }
// High Sierra/Mojave patterns
// Sherlocks: 10.13/10.14
  DBG_RT(Entry, "CPUID: try High Sierra/Mojave patch...\n");
  if (PatchCPUID(kernelData, &StrMsr8b[0], sizeof(StrMsr8b), &HSieMojSearchModel[0],
                 &YosECSieSearchExt[0], &LionReplaceModel[0], &LionReplaceModel[0],
                 sizeof(HSieMojSearchModel), Entry)) {
    DBG_RT(Entry, "...done!\n");
    return;
  }
// Catalina patterns
// PMheart: 10.15.DP1
  DBG_RT(Entry, "CPUID: try Catalina patch...\n");
  if (PatchCPUID(kernelData, &StrMsr8b[0], sizeof(StrMsr8b), &CataSearchModel[0],
                 &CataSearchExt[0], &CataReplaceMovEax[0], &CataReplaceMovEax[0],
                 sizeof(CataSearchModel), Entry)) {
    DBG_RT(Entry, "...done!\n");
    return;
  }
}

// new way by RehabMan 2017-08-13
#define CompareWithMask(x,m,c) (((x) & (m)) == (c))

BOOLEAN KernelPatchPm(VOID *kernelData, LOADER_ENTRY *Entry)
{
  UINT64* Ptr = (UINT64*)kernelData;
  UINT64* End = Ptr + 0x1000000/sizeof(UINT64);
  if (Ptr == NULL) {
    return FALSE;
  }
  // Credits to RehabMan for the kernel patch information
  DBG("Patching kernel power management...\n");
  for (; Ptr < End; Ptr += 2) {
    // check for xcpm_scope_msr common 0xe2 prologue
    //    e2000000 xxxx0000 00000000 00000000 xx040000 00000000
    if (CompareWithMask(Ptr[0], 0xFFFF0000FFFFFFFF, 0x00000000000000e2) && 0 == Ptr[1] &&
        CompareWithMask(Ptr[2], 0xFFFFFFFFFFFFFF00, 0x0000000000000400)) {
      // check for last xcpm_scope_msr entry; terminates search
      // example data:
      //   e2000000 10000000 00000000 00000000 00040000 00000000 0800007e 00000000 00000000 00000000 00000000 00000000
      // or
      //   e2000000 90330000 00000000 00000000 0f040000 00000000 0800007e 00000000 00000000 00000000 00000000 00000000
      if (0x000000007e000008 == Ptr[3] && 0 == Ptr[4] && 0 == Ptr[5]) {
        // zero out 0xE2 MSR and CPU mask
        Ptr[0] = 0;
        DBG("Kernel power management: LAST entry found and patched\n");
        return TRUE;
      }
      // check for other xcpm_scope_msr entry
      // example data:
      //   e2000000 02000000 00000000 00000000 00040000 00000000 0700001e 00000000 00000000 00000000 00000000 00000000
      //   e2000000 0c000000 00000000 00000000 00040000 00000000 0500001e 00000000 00000000 00000000 00000000 00000000
      // or
      //   e2000000 4c000000 00000000 00000000 0f040000 00000000 0500001e 00000000 00000000 00000000 00000000 00000000
      else if (CompareWithMask(Ptr[3], 0xFFFFFFFFFFFFFF00, 0x000000001e000000) && 0 == Ptr[4] && 0 == Ptr[5]) {
        // zero out 0xE2 MSR and CPU mask
        Ptr[0] = 0;
        DBG("Kernel power management: entry found and patched\n");
        // last entry not found yet; continue searching for other entries
      }
    }
  }
  DBG("Kernel power management: LAST patch region not found!\n");
  return FALSE;
}

STATIC UINT8 PanicNoKextDumpFind[6]    = {0x00, 0x25, 0x2E, 0x2A, 0x73, 0x00};
//STATIC UINT8 PanicNoKextDumpReplace[6] = {0x00, 0x00, 0x2E, 0x2A, 0x73, 0x00};

BOOLEAN KernelPanicNoKextDump(VOID *kernelData)
{
  UINT8      *bytes = (UINT8*)kernelData;
  INT32      patchLocation;
  patchLocation = FindBin(bytes, 0xF00000, PanicNoKextDumpFind, 6);
  if (patchLocation > 0) {
    bytes[patchLocation + 1] = 0;
    return TRUE;
  }
  return FALSE;
}

BOOLEAN KernelLapicPatch_64(VOID *kernelData)
{
  // Credits to donovan6000 and Sherlocks for providing the lapic kernel patch source used to build this function

  UINT8       *bytes = (UINT8*)kernelData;
  UINT32      patchLocation1 = 0, patchLocation2 = 0;
  UINT32      i, y;

  DBG("Looking for Lapic panic call (64-bit) Start\n");

  for (i = 0; i < 0x1000000; i++) {
    if (bytes[i+0] == 0x65 && bytes[i+1] == 0x8B && bytes[i+2] == 0x04 && bytes[i+3] == 0x25 &&
        bytes[i+4] == 0x3C && bytes[i+5] == 0x00 && bytes[i+6] == 0x00 && bytes[i+7] == 0x00 &&
        bytes[i+45] == 0x65 && bytes[i+46] == 0x8B && bytes[i+47] == 0x04 && bytes[i+48] == 0x25 &&
        bytes[i+49] == 0x3C && bytes[i+50] == 0x00 && bytes[i+51] == 0x00 && bytes[i+52] == 0x00) {
      patchLocation1 = i+40;
      DBG("Found Lapic panic (10.6) at 0x%08x\n", patchLocation1);
      break;
    } else if (bytes[i+0]  == 0x65 && bytes[i+1]  == 0x8B && bytes[i+2]  == 0x04 && bytes[i+3]  == 0x25 &&
               bytes[i+4]  == 0x14 && bytes[i+5]  == 0x00 && bytes[i+6]  == 0x00 && bytes[i+7]  == 0x00 &&
               bytes[i+35] == 0x65 && bytes[i+36] == 0x8B && bytes[i+37] == 0x04 && bytes[i+38] == 0x25 &&
               bytes[i+39] == 0x14 && bytes[i+40] == 0x00 && bytes[i+41] == 0x00 && bytes[i+42] == 0x00) {
      patchLocation1 = i+30;
      DBG("Found Lapic panic (10.7 - 10.8) at 0x%08x\n", patchLocation1);
      break;
    } else if (bytes[i+0] == 0x65 && bytes[i+1] == 0x8B && bytes[i+2] == 0x04 && bytes[i+3] == 0x25 &&
               bytes[i+4] == 0x1C && bytes[i+5] == 0x00 && bytes[i+6] == 0x00 && bytes[i+7] == 0x00 &&
               bytes[i+36] == 0x65 && bytes[i+37] == 0x8B && bytes[i+38] == 0x04 && bytes[i+39] == 0x25 &&
               bytes[i+40] == 0x1C && bytes[i+41] == 0x00 && bytes[i+42] == 0x00 && bytes[i+43] == 0x00) {
      patchLocation1 = i+31;
      DBG("Found Lapic panic (10.9) at 0x%08x\n", patchLocation1);
      break;
    // 00 29 C7 78 XX 31 DB 8D 47 FA 83
    } else if (bytes[i+0] == 0x00 && bytes[i+1] == 0x29 && bytes[i+2] == 0xC7 && bytes[i+3] == 0x78 &&
               //(bytes[i+4] == 0x3F || bytes[i+4] == 0x4F) && // 3F:10.10-10.12/4F:10.13+
               bytes[i+5] == 0x31 && bytes[i+6] == 0xDB && bytes[i+7] == 0x8D && bytes[i+8] == 0x47 &&
               bytes[i+9] == 0xFA && bytes[i+10] == 0x83) {
      for (y = i; y < 0x1000000; y++) {
        // Lapic panic patch, by vit9696
        // mov eax, gs:1Ch
        // cmp eax, cs:_master_cpu
        // 65 8B 04 25 1C 00 00 00 3B 05 XX XX XX 00
        if (bytes[y+0] == 0x65 && bytes[y+1] == 0x8B && bytes[y+2] == 0x04 && bytes[y+3] == 0x25 &&
            bytes[y+4] == 0x1C && bytes[y+5] == 0x00 && bytes[y+6] == 0x00 && bytes[y+7] == 0x00 &&
            bytes[y+8] == 0x3B && bytes[y+9] == 0x05 && bytes[y+13] == 0x00) {
          patchLocation1 = y;
          DBG("Found Lapic panic (10.10 - recent macOS) at 0x%08x\n", patchLocation1);
          break;
        }
      }
      break;
    }
  }

  if (!patchLocation1) {
    DBG("Can't find Lapic panic, kernel patch aborted.\n");
    return FALSE;
  }

  // Already patched?  May be running a non-vanilla kernel already?
  if (bytes[patchLocation1 + 0] == 0x90 && bytes[patchLocation1 + 1] == 0x90 &&
      bytes[patchLocation1 + 2] == 0x90 && bytes[patchLocation1 + 3] == 0x90 &&
      bytes[patchLocation1 + 4] == 0x90) {
    DBG("Lapic panic already patched, kernel file (10.6 - 10.9) manually patched?\n");
    return FALSE;
  } else if (bytes[patchLocation1 + 0] == 0x31 && bytes[patchLocation1 + 1] == 0xC0 &&
             bytes[patchLocation1 + 2] == 0x90 && bytes[patchLocation1 + 3] == 0x90) {
    DBG("Lapic panic already patched, kernel file (10.10 - recent macOS) manually patched?\n");
    return FALSE;
  } else {
    if (bytes[patchLocation1 + 8] == 0x3B && bytes[patchLocation1 + 9] == 0x05 &&
        bytes[patchLocation1 + 13] == 0x00) {
      // 65 8B 04 25 1C 00 00 00 3B XX XX XX XX 00
      // 31 C0 90 90 90 90 90 90 90 90 90 90 90 90
      DBG("Patched Lapic panic (10.10 - recent macOS)\n");
      bytes[patchLocation1 + 0] = 0x31;
      bytes[patchLocation1 + 1] = 0xC0;
      for (i = 2; i < 14; i++) {
        bytes[patchLocation1 + i] = 0x90;
      }

      for (i = 0; i < 0x1000000; i++) {
        // 00 29 C7 78 XX 31 DB 8D 47 FA 83
        if (bytes[i+0] == 0x00 && bytes[i+1] == 0x29 && bytes[i+2] == 0xC7 && bytes[i+3] == 0x78 &&
            //(bytes[i+4] == 0x3F || bytes[i+4] == 0x4F) && // 3F:10.10-10.12/4F:10.13+
            bytes[i+5] == 0x31 && bytes[i+6] == 0xDB && bytes[i+7] == 0x8D && bytes[i+8] == 0x47 &&
            bytes[i+9] == 0xFA && bytes[i+10] == 0x83) {
          for (y = i; y < 0x1000000; y++) {
            // Lapic panic master patch, by vit9696
            // cmp cs:_debug_boot_arg, 0
            // E8 XX XX FF FF 83 XX XX XX XX 00 00
            if (bytes[y+0] == 0xE8 && bytes[y+3] == 0xFF && bytes[y+4] == 0xFF &&
                bytes[y+5] == 0x83 && bytes[y+10] == 0x00 && bytes[y+11] == 0x00) {
              patchLocation2 = y;
              DBG("Found Lapic panic master (10.10 - recent macOS) at 0x%08x\n", patchLocation2);
              break;
            }
          }
          break;
        }
      }
        
      if (!patchLocation2) {
        DBG("Can't find Lapic panic master (10.10 - recent macOS), kernel patch aborted.\n");
        return FALSE;
      }
        
      // Already patched? May be running a non-vanilla kernel already?
      if (bytes[patchLocation2 + 5] == 0x31 && bytes[patchLocation2 + 6] == 0xC0) {
        DBG("Lapic panic master already patched, kernel file (10.10 - recent macOS) manually patched?\n");
        return FALSE;
      } else {
        DBG("Patched Lapic panic master (10.10 - recent macOS)\n");
        // E8 XX XX FF FF 83 XX XX XX XX 00 00
        // E8 XX XX FF FF 31 C0 90 90 90 90 90
        bytes[patchLocation2 + 5] = 0x31;
        bytes[patchLocation2 + 6] = 0xC0;
        for (i = 7; i < 12; i++) {
          bytes[patchLocation2 + i] = 0x90;
        }
      }
    } else {
      DBG("Patched Lapic panic (10.6 - 10.9)\n");
      for (i = 0; i < 5; i++) {
        bytes[patchLocation1 + i] = 0x90;
      }
    }
  }
  return TRUE;
}

BOOLEAN KernelLapicPatch_32(VOID *kernelData)
{
  // Credits to donovan6000 and Sherlocks for providing the lapic kernel patch source used to build this function

  UINT8       *bytes = (UINT8*)kernelData;
  UINT32      patchLocation = 0;
  UINT32      i;

  DBG("Looking for Lapic panic call (32-bit) Start\n");

  for (i = 0; i < 0x1000000; i++) {
    if (bytes[i+0]  == 0x65 && bytes[i+1]  == 0xA1 && bytes[i+2]  == 0x0C && bytes[i+3]  == 0x00 &&
        bytes[i+4]  == 0x00 && bytes[i+5]  == 0x00 &&
        bytes[i+30] == 0x65 && bytes[i+31] == 0xA1 && bytes[i+32] == 0x0C && bytes[i+33] == 0x00 &&
        bytes[i+34] == 0x00 && bytes[i+35] == 0x00) {
      patchLocation = i+25;
      DBG("Found Lapic panic at 0x%08x\n", patchLocation);
      break;
    }
  }

  if (!patchLocation) {
    DBG("Can't find Lapic panic, kernel patch aborted.\n");
    return FALSE;
  }

  // Already patched?  May be running a non-vanilla kernel already?

  if (bytes[patchLocation + 0] == 0x90 && bytes[patchLocation + 1] == 0x90 &&
      bytes[patchLocation + 2] == 0x90 && bytes[patchLocation + 3] == 0x90 &&
      bytes[patchLocation + 4] == 0x90) {
    DBG("Lapic panic already patched, kernel file manually patched?\n");
    return FALSE;
  } else {
    DBG("Patched Lapic panic (32-bit)\n");
    for (i = 0; i < 5; i++) {
      bytes[patchLocation + i] = 0x90;
    }
  }
  return TRUE;
}

//
// syscl - EnableExtCpuXCPM(): enable extra(unsupport) Cpu XCPM function
// PowerManagement that will be enabled on:
// SandyBridge-E, Ivy Bridge, Ivy Bridge-E, Haswell Celeron/Pentium, Haswell-E, Broadwell-E, ...
// credit Pike R.Alpha, stinga11, syscl
//
BOOLEAN (*EnableExtCpuXCPM)(VOID *kernelData, LOADER_ENTRY *Entry, BOOLEAN use_xcpm_idle);

//
// syscl - applyKernPatch a wrapper for SearchAndReplace() to make the CpuPM patch tidy and clean
//
static inline VOID applyKernPatch(UINT8 *kern, UINT8 *find, UINTN size, UINT8 *repl, const CHAR8 *comment)
{
    DBG("Searching %a...\n", comment);
    if (SearchAndReplace(kern, KERNEL_MAX_SIZE, find, size, repl, 0)) {
        DBG("Found %a\nApplied %a patch\n", comment, comment);
    } else {
        DBG("%a no found, patched already?\n", comment);
    }
}

// PMHeart
// Global XCPM patches compatibility
// Currently 10.8.5 - 10.15
//
static inline BOOLEAN IsXCPMOSVersionCompat(UINT64 os_version)
{
  return (os_version >= AsciiOSVersionToUint64("10.8.5")) && (os_version < AsciiOSVersionToUint64("10.16")); 
}

//
// Enable Unsupported CPU PowerManagement
//
// syscl - SandyBridgeEPM(): enable PowerManagement on SandyBridge-E
//
BOOLEAN SandyBridgeEPM(VOID *kernelData, LOADER_ENTRY *Entry, BOOLEAN use_xcpm_idle)
{
    // note: a dummy function that made patches consistency
    return TRUE;
}

//
// syscl - Enable Haswell-E XCPM
// Hex data provided and polished (c) PMheart, idea (c) Pike R.Alpha
//
BOOLEAN HaswellEXCPM(VOID *kernelData, LOADER_ENTRY *Entry, BOOLEAN use_xcpm_idle)
{
  DBG("HaswellEXCPM() ===>\n");
  UINT8       *kern = (UINT8*)kernelData;
  CHAR8       *comment;
  UINT32      i;
  UINT32      patchLocation;
  UINT64      os_version = AsciiOSVersionToUint64(Entry->OSVersion);

  // check OS version suit for patches
  if (!IsXCPMOSVersionCompat(os_version)) {
    DBG("HaswellEXCPM(): Unsupported macOS.\n");
    DBG("HaswellEXCPM() <===FALSE\n");
    return FALSE;
  }

  // _cpuid_set_info
  comment = "_cpuid_set_info";
  if (os_version <= AsciiOSVersionToUint64("10.8.5")) {
    // 10.8.5
    STATIC UINT8 find[] = { 0x83, 0xF8, 0x3C, 0x74, 0x2D };
    STATIC UINT8 repl[] = { 0x83, 0xF8, 0x3F, 0x74, 0x2D };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version < AsciiOSVersionToUint64("10.10")) {
    // 10.9.x
    STATIC UINT8 find[] = { 0x83, 0xF8, 0x3C, 0x75, 0x07 };
    STATIC UINT8 repl[] = { 0x83, 0xF8, 0x3F, 0x75, 0x07 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version <= AsciiOSVersionToUint64("10.10.1")) {
    // 10.10 - 10.10.1
    STATIC UINT8 find[] = { 0x74, 0x11, 0x83, 0xF8, 0x3C };
    STATIC UINT8 repl[] = { 0x74, 0x11, 0x83, 0xF8, 0x3F };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } // 10.10.2+: native support reached, no need to patch

  // _xcpm_bootstrap
  comment = "_xcpm_bootstrap";
  if (os_version <= AsciiOSVersionToUint64("10.8.5")) {
    // 10.8.5
    STATIC UINT8 find[] = { 0x83, 0xFB, 0x3C, 0x75, 0x54 };
    STATIC UINT8 repl[] = { 0x83, 0xFB, 0x3F, 0x75, 0x54 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version < AsciiOSVersionToUint64("10.10")) {
    // 10.9.x
    STATIC UINT8 find[] = { 0x83, 0xFB, 0x3C, 0x75, 0x68 };
    STATIC UINT8 repl[] = { 0x83, 0xFB, 0x3F, 0x75, 0x68 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version <= AsciiOSVersionToUint64("10.10.2")) {
    // 10.10 - 10.10.2
    STATIC UINT8 find[] = { 0x83, 0xFB, 0x3C, 0x75, 0x63 };
    STATIC UINT8 repl[] = { 0x83, 0xFB, 0x3F, 0x75, 0x63 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version <= AsciiOSVersionToUint64("10.10.5")) {
    // 10.10.3 - 10.10.5
    STATIC UINT8 find[] = { 0x83, 0xC3, 0xC6, 0x83, 0xFB, 0x0D };
    STATIC UINT8 repl[] = { 0x83, 0xC3, 0xC3, 0x83, 0xFB, 0x0D };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version <= AsciiOSVersionToUint64("10.11")) {
    // 10.11 DB/PB - 10.11.0
    STATIC UINT8 find[] = { 0x83, 0xC3, 0xC6, 0x83, 0xFB, 0x0D };
    STATIC UINT8 repl[] = { 0x83, 0xC3, 0xC3, 0x83, 0xFB, 0x0D };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version <= AsciiOSVersionToUint64("10.11.6")) {
    // 10.11.1 - 10.11.6
    STATIC UINT8 find[] = { 0x83, 0xC3, 0xBB, 0x83, 0xFB, 0x09 };
    STATIC UINT8 repl[] = { 0x83, 0xC3, 0xB8, 0x83, 0xFB, 0x09 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version <= AsciiOSVersionToUint64("10.12.5")) {
    // 10.12 - 10.12.5
    STATIC UINT8 find[] = { 0x83, 0xC3, 0xC4, 0x83, 0xFB, 0x22 };
    STATIC UINT8 repl[] = { 0x83, 0xC3, 0xC1, 0x83, 0xFB, 0x22 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version < AsciiOSVersionToUint64("10.13")) {
    // 10.12.6
    STATIC UINT8 find[] = { 0x8D, 0x43, 0xC4, 0x83, 0xF8, 0x22 };
    STATIC UINT8 repl[] = { 0x8D, 0x43, 0xC1, 0x83, 0xF8, 0x22 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
    // PMheart: attempt to add 10.14 compatibility
  } else if (os_version < AsciiOSVersionToUint64("10.15")) {
    // 10.13/10.14
    STATIC UINT8 find[] = { 0x89, 0xD8, 0x04, 0xC4, 0x3C, 0x22 };
    STATIC UINT8 repl[] = { 0x89, 0xD8, 0x04, 0xC1, 0x3C, 0x22 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
    // PMheart: attempt to add 10.15 compatibility
  } else if (os_version < AsciiOSVersionToUint64("10.16")) {
    STATIC UINT8 find[] = { 0x8D, 0x43, 0xC4, 0x3C, 0x22 };
    STATIC UINT8 repl[] = { 0x8D, 0x43, 0xC1, 0x3C, 0x22 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  }

  DBG("Searching _xcpm_pkg_scope_msr ...\n");
  comment = "_xcpm_pkg_scope_msrs";
  if (os_version <= AsciiOSVersionToUint64("10.8.5")) {
    // 10.8.5
    STATIC UINT8 find[] = {
      0x48, 0x8D, 0x3D, 0x02, 0x71, 0x55, 0x00, 0xBE,
      0x07, 0x00, 0x00, 0x00, 0xEB, 0x1F, 0x48, 0x8D,
      0x3D, 0xF4, 0x70, 0x55, 0x00, 0xBE, 0x07, 0x00,
      0x00, 0x00, 0x31, 0xD2, 0xE8, 0x28, 0x02, 0x00, 0x00
    };
    STATIC UINT8 repl[] = {
      0x48, 0x8D, 0x3D, 0x02, 0x71, 0x55, 0x00, 0xBE,
      0x07, 0x00, 0x00, 0x00, 0x90, 0x90, 0x48, 0x8D,
      0x3D, 0xF4, 0x70, 0x55, 0x00, 0xBE, 0x07, 0x00,
      0x00, 0x00, 0x31, 0xD2, 0x90, 0x90, 0x90, 0x90, 0x90
    };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version < AsciiOSVersionToUint64("10.10")) {
    // 10.9.x
    STATIC UINT8 find[] = { 0xBE, 0x07, 0x00, 0x00, 0x00, 0x74, 0x13, 0x31, 0xD2, 0xE8, 0x5F, 0x02, 0x00, 0x00 };
    STATIC UINT8 repl[] = { 0xBE, 0x07, 0x00, 0x00, 0x00, 0x90, 0x90, 0x31, 0xD2, 0x90, 0x90, 0x90, 0x90, 0x90 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else {
    // 10.10+
    patchLocation = 0; // clean out the value just in case
    for (i = 0; i < 0x1000000; i++) {
      if (kern[i+0] == 0xBE && kern[i+1] == 0x07 && kern[i+2] == 0x00 && kern[i+3] == 0x00 &&
          kern[i+4] == 0x00 && kern[i+5] == 0x31 && kern[i+6] == 0xD2 && kern[i+7] == 0xE8) {
        patchLocation = i+7;
        DBG("Found _xcpm_pkg_scope_msr\n");
        break;
      }
    }

    if (patchLocation) {
      for (i = 0; i < 5; i++) {
        kern[patchLocation+i] = 0x90;
      }
      DBG("Applied _xcpm_pkg_scope_msr patch\n");
    } else {
      DBG("_xcpm_pkg_scope_msr not found, patch aborted\n");
      DBG("HaswellEXCPM() <===FALSE\n");
      return FALSE;
    }
  }

  DBG("HaswellEXCPM() <===\n");
  return TRUE;
}

//
// Enable Broadwell-E/EP PowerManagement on 10.12+ by syscl
//
BOOLEAN BroadwellEPM(VOID *kernelData, LOADER_ENTRY *Entry, BOOLEAN use_xcpm_idle)
{
  DBG("BroadwellEPM() ===>\n");
  UINT8       *kern = (UINT8*)kernelData;
  UINT32      i;
  UINT32      patchLocation;
  UINT64      os_version = AsciiOSVersionToUint64(Entry->OSVersion);

  // check OS version suit for patches
  if (!IsXCPMOSVersionCompat(os_version)) {
    DBG("BroadwellEPM(): Unsupported macOS.\n");
    DBG("BroadwellEPM() <===FALSE\n");
    return FALSE;
  }

  Entry->KernelAndKextPatches->FakeCPUID = (UINT32)(os_version < AsciiOSVersionToUint64("10.10.3") ? 0x0306C0 : 0x040674);
  KernelCPUIDPatch(kern, Entry);

  DBG("Searching _xcpm_pkg_scope_msr ...\n");
  if (os_version >= AsciiOSVersionToUint64("10.12")) {
    // 10.12+
    patchLocation = 0; // clean out the value just in case
    for (i = 0; i < 0x1000000; i++) {
      if (kern[i+0] == 0xBE && kern[i+1] == 0x07 && kern[i+2] == 0x00 && kern[i+3] == 0x00 &&
          kern[i+4] == 0x00 && kern[i+5] == 0x31 && kern[i+6] == 0xD2 && kern[i+7] == 0xE8) {
        patchLocation = i+7;
        DBG("Found _xcpm_pkg_scope_msr\n");
        break;
      }
    }

    if (patchLocation) {
      for (i = 0; i < 5; i++) {
        kern[patchLocation+i] = 0x90;
      }
      DBG("Applied _xcpm_pkg_scope_msr patch\n");
    } else {
      DBG("_xcpm_pkg_scope_msr not found, patch aborted\n");
      DBG("BroadwellEPM() <===FALSE\n");
      return FALSE;
    }
  }

  DBG("BroadwellEPM() <===\n");
  return TRUE;
}
//
// syscl - this patch provides XCPM support for Haswell low-end(HSWLowEnd) and platforms later than Haswell
// implemented by syscl
// credit also Pike R.Alpha, stinga11, Sherlocks, vit9696
//
BOOLEAN HaswellLowEndXCPM(VOID *kernelData, LOADER_ENTRY *Entry, BOOLEAN use_xcpm_idle)
{
  DBG("HaswellLowEndXCPM() ===>\n");
  UINT8       *kern = (UINT8*)kernelData;
  UINT64      os_version = AsciiOSVersionToUint64(Entry->OSVersion);
  CHAR8       *comment;

  // check OS version suit for patches
  if (!IsXCPMOSVersionCompat(os_version)) {
    DBG("HaswellLowEndXCPM(): Unsupported macOS.\n");
    DBG("HaswellLowEndXCPM() <===FALSE\n");
    return FALSE;
  }

  Entry->KernelAndKextPatches->FakeCPUID = (UINT32)(0x0306A0);    // correct FakeCPUID
  KernelCPUIDPatch(kern, Entry);

  // 10.8.5 - 10.11.x no need the following kernel patches on Haswell Celeron/Pentium
  if (os_version >= AsciiOSVersionToUint64("10.8.5") && os_version < AsciiOSVersionToUint64("10.12") &&
      (!use_xcpm_idle)) {
    DBG("HaswellLowEndXCPM() <===\n");
    return TRUE;
  }

  // _xcpm_idle
  if (use_xcpm_idle) {
    DBG("HWPEnable - ON.\n");
    comment = "_xcpm_idle";
    STATIC UINT8 find[] = { 0xB9, 0xE2, 0x00, 0x00, 0x00, 0x0F, 0x30 };
    STATIC UINT8 repl[] = { 0xB9, 0xE2, 0x00, 0x00, 0x00, 0x90, 0x90 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  }

  comment = "_xcpm_bootstrap";
  if (os_version <= AsciiOSVersionToUint64("10.12.5")) {
    // 10.12 - 10.12.5
    STATIC UINT8 find[] = { 0x83, 0xC3, 0xC4, 0x83, 0xFB, 0x22 };
    STATIC UINT8 repl[] = { 0x83, 0xC3, 0xC6, 0x83, 0xFB, 0x22 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version < AsciiOSVersionToUint64("10.13")) {
    // 10.12.6
    STATIC UINT8 find[] = { 0x8D, 0x43, 0xC4, 0x83, 0xF8, 0x22 };
    STATIC UINT8 repl[] = { 0x8D, 0x43, 0xC6, 0x83, 0xF8, 0x22 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version < AsciiOSVersionToUint64("10.15")) {
    // 10.13/10.14
    STATIC UINT8 find[] = { 0x89, 0xD8, 0x04, 0xC4, 0x3C, 0x22 };
    STATIC UINT8 repl[] = { 0x89, 0xD8, 0x04, 0xC6, 0x3C, 0x22 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
    // PMheart: attempt to add 10.15 compatibility
  } else if (os_version < AsciiOSVersionToUint64("10.16")) {
    STATIC UINT8 find[] = { 0x8D, 0x43, 0xC4, 0x3C, 0x22 };
    STATIC UINT8 repl[] = { 0x8D, 0x43, 0xC6, 0x3C, 0x22 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  }

  comment = "_cpuid_set_info_rdmsr";
  // PMheart: bytes seem stable as of 10.12
  if (os_version >= AsciiOSVersionToUint64("10.12")) {
    // 10.12+
    STATIC UINT8 find[] = { 0xB9, 0xA0, 0x01, 0x00, 0x00, 0x0F, 0x32 };
    STATIC UINT8 repl[] = { 0xB9, 0xA0, 0x01, 0x00, 0x00, 0x31, 0xC0 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  }

  DBG("HaswellLowEndXCPM() <===\n");
  return TRUE;
}

//
// this patch provides XCPM support for Ivy Bridge. by PMheart
//
BOOLEAN KernelIvyBridgeXCPM(VOID *kernelData, LOADER_ENTRY *Entry, BOOLEAN use_xcpm_idle)
{
  UINT8       *kern = (UINT8*)kernelData;
  CHAR8       *comment;
  UINT32      i;
  UINT32      patchLocation;
  UINT64      os_version = AsciiOSVersionToUint64(Entry->OSVersion);

  // check whether Ivy Bridge
  if (gCPUStructure.Model != CPU_MODEL_IVY_BRIDGE) {
    DBG("KernelIvyBridgeXCPM(): Unsupported platform.\nRequires Ivy Bridge, aborted\n");
    DBG("KernelIvyBridgeXCPM() <===FALSE\n");
    return FALSE;
  }

  // check OS version suit for patches
  // PMheart: attempt to add 10.14 compatibility
  if (!IsXCPMOSVersionCompat(os_version)) {
    DBG("KernelIvyBridgeXCPM():Unsupported macOS.\n");
    DBG("KernelIvyBridgeXCPM() <===FALSE\n");
    return FALSE;
  } else if (os_version >= AsciiOSVersionToUint64("10.8.5") && os_version < AsciiOSVersionToUint64("10.12")) {
    // 10.8.5 - 10.11.x no need the following kernel patches on Ivy Bridge - we just use -xcpm boot-args
    DBG("KernelIvyBridgeXCPM() <===\n");
    return TRUE;
  }

  DBG("Searching _xcpm_pkg_scope_msr ...\n");
  if (os_version >= AsciiOSVersionToUint64("10.12")) {
    // 10.12+
    patchLocation = 0; // clean out the value just in case
    for (i = 0; i < 0x1000000; i++) {
      if (kern[i+0] == 0xBE && kern[i+1] == 0x07 && kern[i+2] == 0x00 && kern[i+3] == 0x00 &&
          kern[i+4] == 0x00 && kern[i+5] == 0x31 && kern[i+6] == 0xD2 && kern[i+7] == 0xE8) {
        patchLocation = i+7;
        DBG("Found _xcpm_pkg_scope_msr\n");
        break;
      }
    }

    if (patchLocation) {
      for (i = 0; i < 5; i++) {
        kern[patchLocation+i] = 0x90;
      }
      DBG("Applied _xcpm_pkg_scope_msr patch\n");
    } else {
      DBG("_xcpm_pkg_scope_msr not found, patch aborted\n");
      DBG("KernelIvyBridgeXCPM() <===FALSE\n");
      return FALSE;
    }
  }

  comment = "_xcpm_bootstrap";
  if (os_version <= AsciiOSVersionToUint64("10.12.5")) {
    // 10.12 - 10.12.5
    STATIC UINT8 find[] = { 0x83, 0xC3, 0xC4, 0x83, 0xFB, 0x22 };
    STATIC UINT8 repl[] = { 0x83, 0xC3, 0xC6, 0x83, 0xFB, 0x22 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version < AsciiOSVersionToUint64("10.13")) {
    // 10.12.6
    STATIC UINT8 find[] = { 0x8D, 0x43, 0xC4, 0x83, 0xF8, 0x22 };
    STATIC UINT8 repl[] = { 0x8D, 0x43, 0xC6, 0x83, 0xF8, 0x22 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
    // PMheart: attempt to add 10.14 compatibility
  } else if (os_version < AsciiOSVersionToUint64("10.15")) {
    // 10.13/10.14
    STATIC UINT8 find[] = { 0x89, 0xD8, 0x04, 0xC4, 0x3C, 0x22 };
    STATIC UINT8 repl[] = { 0x89, 0xD8, 0x04, 0xC6, 0x3C, 0x22 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
    // PMheart: attempt to add 10.15 compatibility
  } else if (os_version < AsciiOSVersionToUint64("10.16")) {
    STATIC UINT8 find[] = { 0x8D, 0x43, 0xC4, 0x3C, 0x22 };
    STATIC UINT8 repl[] = { 0x8D, 0x43, 0xC6, 0x3C, 0x22 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  }

  DBG("KernelIvyBridgeXCPM() <===\n");
  return TRUE;
}

//
// this patch provides XCPM support for Ivy Bridge-E. by PMheart
// attempt to enable XCPM for Ivy-E, still need to test further
//
BOOLEAN KernelIvyE5XCPM(VOID *kernelData, LOADER_ENTRY *Entry, BOOLEAN use_xcpm_idle)
{
  UINT8       *kern = (UINT8*)kernelData;
  CHAR8       *comment;
  UINT32      i;
  UINT32      patchLocation;
  UINT64      os_version = AsciiOSVersionToUint64(Entry->OSVersion);
  
  // check whether Ivy Bridge-E5
  if (gCPUStructure.Model != CPU_MODEL_IVY_BRIDGE_E5) {
    DBG("KernelIvyE5XCPM(): Unsupported platform.\nRequires Ivy Bridge-E, aborted\n");
    DBG("KernelIvyE5XCPM() <===FALSE\n");
    return FALSE;
  }
  
  // check OS version suit for patches
  // PMheart: attempt to add 10.15 compatibility
  if (!IsXCPMOSVersionCompat(os_version)) {
    DBG("KernelIvyE5XCPM(): Unsupported macOS.\n");
    DBG("KernelIvyE5XCPM() <===FALSE\n");
    return FALSE;
  }
  
  // _cpuid_set_info
  // TO-DO: should we use FakeCPUID instead?
  comment = "_cpuid_set_info";
  if (os_version <= AsciiOSVersionToUint64("10.8.5")) {
    // 10.8.5
    STATIC UINT8 find[] = { 0x83, 0xF8, 0x3C, 0x74, 0x2D };
    STATIC UINT8 repl[] = { 0x83, 0xF8, 0x3E, 0x74, 0x2D };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version == AsciiOSVersionToUint64("10.9") || os_version == AsciiOSVersionToUint64("10.9.1")) {
    // 10.9.0 - 10.9.1
    STATIC UINT8 find[] = { 0x83, 0xF8, 0x3C, 0x75, 0x07 };
    STATIC UINT8 repl[] = { 0x83, 0xF8, 0x3E, 0x75, 0x07 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } // 10.9.2+: native support reached, no need to patch
  
  // _xcpm_pkg_scope_msrs
  DBG("Searching _xcpm_pkg_scope_msrs ...\n");
  comment = "_xcpm_pkg_scope_msrs";
  if (os_version <= AsciiOSVersionToUint64("10.8.5")) {
    // 10.8.5
    STATIC UINT8 find[] = {
      0x48, 0x8D, 0x3D, 0x02, 0x71, 0x55, 0x00, 0xBE,
      0x07, 0x00, 0x00, 0x00, 0xEB, 0x1F, 0x48, 0x8D,
      0x3D, 0xF4, 0x70, 0x55, 0x00, 0xBE, 0x07, 0x00,
      0x00, 0x00, 0x31, 0xD2, 0xE8, 0x28, 0x02, 0x00, 0x00
    };
    STATIC UINT8 repl[] = {
      0x48, 0x8D, 0x3D, 0x02, 0x71, 0x55, 0x00, 0xBE,
      0x07, 0x00, 0x00, 0x00, 0x90, 0x90, 0x48, 0x8D,
      0x3D, 0xF4, 0x70, 0x55, 0x00, 0xBE, 0x07, 0x00,
      0x00, 0x00, 0x31, 0xD2, 0x90, 0x90, 0x90, 0x90, 0x90
    };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version < AsciiOSVersionToUint64("10.10")) {
    // 10.9.x
    STATIC UINT8 find[] = { 0xBE, 0x07, 0x00, 0x00, 0x00, 0x74, 0x13, 0x31, 0xD2, 0xE8, 0x5F, 0x02, 0x00, 0x00 };
    STATIC UINT8 repl[] = { 0xBE, 0x07, 0x00, 0x00, 0x00, 0x90, 0x90, 0x31, 0xD2, 0x90, 0x90, 0x90, 0x90, 0x90 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else {
    // 10.10+
    patchLocation = 0; // clean out the value just in case
    for (i = 0; i < 0x1000000; i++) {
      if (kern[i+0] == 0xBE && kern[i+1] == 0x07 && kern[i+2] == 0x00 && kern[i+3] == 0x00 &&
          kern[i+4] == 0x00 && kern[i+5] == 0x31 && kern[i+6] == 0xD2 && kern[i+7] == 0xE8) {
        patchLocation = i+7;
        DBG("Found _xcpm_pkg_scope_msr\n");
        break;
      }
    }
    
    if (patchLocation) {
      for (i = 0; i < 5; i++) {
        kern[patchLocation+i] = 0x90;
      }
      DBG("Applied _xcpm_pkg_scope_msr patch\n");
    } else {
      DBG("_xcpm_pkg_scope_msr not found, patch aborted\n");
      DBG("KernelIvyE5XCPM() <===FALSE\n");
      return FALSE;
    }
  }
  
  // _xcpm_bootstrap
  comment = "_xcpm_bootstrap";
  if (os_version <= AsciiOSVersionToUint64("10.8.5")) {
    // 10.8.5
    STATIC UINT8 find[] = { 0x83, 0xFB, 0x3C, 0x75, 0x54 };
    STATIC UINT8 repl[] = { 0x83, 0xFB, 0x3E, 0x75, 0x54 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version < AsciiOSVersionToUint64("10.10")) {
    // 10.9.x
    STATIC UINT8 find[] = { 0x83, 0xFB, 0x3C, 0x75, 0x68 };
    STATIC UINT8 repl[] = { 0x83, 0xFB, 0x3E, 0x75, 0x68 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version <= AsciiOSVersionToUint64("10.10.2")) {
    // 10.10 - 10.10.2
    STATIC UINT8 find[] = { 0x83, 0xFB, 0x3C, 0x75, 0x63 };
    STATIC UINT8 repl[] = { 0x83, 0xFB, 0x3E, 0x75, 0x63 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version <= AsciiOSVersionToUint64("10.10.5")) {
    // 10.10.3 - 10.10.5
    STATIC UINT8 find[] = { 0x83, 0xC3, 0xC6, 0x83, 0xFB, 0x0D };
    STATIC UINT8 repl[] = { 0x83, 0xC3, 0xC4, 0x83, 0xFB, 0x0D };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version <= AsciiOSVersionToUint64("10.11")) {
    // 10.11 DB/PB - 10.11.0
    STATIC UINT8 find[] = { 0x83, 0xC3, 0xC6, 0x83, 0xFB, 0x0D };
    STATIC UINT8 repl[] = { 0x83, 0xC3, 0xC4, 0x83, 0xFB, 0x0D };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version <= AsciiOSVersionToUint64("10.11.6")) {
    // 10.11.1 - 10.11.6
    STATIC UINT8 find[] = { 0x83, 0xC3, 0xBB, 0x83, 0xFB, 0x09 };
    STATIC UINT8 repl[] = { 0x83, 0xC3, 0xB9, 0x83, 0xFB, 0x09 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version <= AsciiOSVersionToUint64("10.12.5")) {
    // 10.12 - 10.12.5
    STATIC UINT8 find[] = { 0x83, 0xC3, 0xC4, 0x83, 0xFB, 0x22 };
    STATIC UINT8 repl[] = { 0x83, 0xC3, 0xC2, 0x83, 0xFB, 0x22 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  } else if (os_version < AsciiOSVersionToUint64("10.13")) {
    // 10.12.6
    STATIC UINT8 find[] = { 0x8D, 0x43, 0xC4, 0x83, 0xF8, 0x22 };
    STATIC UINT8 repl[] = { 0x8D, 0x43, 0xC2, 0x83, 0xF8, 0x22 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  // PMheart: attempt to add 10.14 compatibility
  } else if (os_version < AsciiOSVersionToUint64("10.15")) {
    // 10.13/10.14
    STATIC UINT8 find[] = { 0x89, 0xD8, 0x04, 0xC4, 0x3C, 0x22 };
    STATIC UINT8 repl[] = { 0x89, 0xD8, 0x04, 0xC1, 0x3C, 0x22 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  // PMheart: attempt to add 10.15 compatibility
  } else if (os_version < AsciiOSVersionToUint64("10.16")) {
    STATIC UINT8 find[] = { 0x8D, 0x43, 0xC4, 0x3C, 0x22 };
    STATIC UINT8 repl[] = { 0x8D, 0x43, 0xC1, 0x3C, 0x22 };
    applyKernPatch(kern, find, sizeof(find), repl, comment);
  }
  
  DBG("KernelIvyE5XCPM() <===\n");
  return TRUE;
}

VOID Patcher_SSE3_6(VOID* kernelData)
{
  UINT8* bytes = (UINT8*)kernelData;
  UINT32 patchLocation1 = 0;
  UINT32 patchLocation2 = 0;
  UINT32 patchlast = 0;
  UINT32 i;
  //UINT32 Length = sizeof(kernelData);

  DBG("Start find SSE3 address\n");
  i=0;
  //for (i=0;i<Length;i++)
  while(TRUE) {
    if (bytes[i] == 0x66 && bytes[i+1] == 0x0F && bytes[i+2] == 0x6F &&
        bytes[i+3] == 0x44 && bytes[i+4] == 0x0E && bytes[i+5] == 0xF1 &&
        bytes[i-1664-32] == 0x55
        ) {
      patchLocation1 = i-1664-32;
      DBG("Found SSE3 data address at 0x%08x\n",patchLocation1);
    }

    // hasSSE2+..... title
    if (bytes[i] == 0xE3 && bytes[i+1] == 0x07 && bytes[i+2] == 0x00 &&
        bytes[i+3] == 0x00 && bytes[i+4] == 0x80 && bytes[i+5] == 0x07 &&
        bytes[i+6] == 0xFF && bytes[i+7] == 0xFF && bytes[i+8] == 0x24 &&
        bytes[i+9] == 0x01) {
      patchLocation2 = i;
      DBG("Found SSE3 Title address at 0x%08x\n",patchLocation2);
      break;
    }
    i++;
  }

  if (!patchLocation1 || !patchLocation2) {
    DBG("Can't found SSE3 data addres or Title address at 0x%08x 0x%08x\n", patchLocation1, patchLocation2);
    return;
  }

  DBG("Found SSE3 last data addres Start\n");
  i = patchLocation1 + 1500;
  //for (i=(patchLocation1+1500); i<(patchLocation1+3000); i++)
  while(TRUE) {
    if (bytes[i] == 0x90 && bytes[i+1] == 0x90 && bytes[i+2] == 0x55 ) {
      patchlast = (i+1) - patchLocation1;
      DBG("Found SSE3 last data addres at 0x%08x\n", patchlast);
      break;
    }
    i++;
  }

  if (!patchlast) {
    DBG("Can't found SSE3 data last addres at 0x%08x\n", patchlast);
    return;
  }
  // patch sse3_64 data

  for (i=0; i<patchlast; i++) {
    if (i<sizeof(sse3_patcher)) {
      bytes[patchLocation1 + i] = sse3_patcher[i];
    } else {
      bytes[patchLocation1 + i] = 0x90;
    }
  }

  // patch kHasSSE3 title
  bytes[patchLocation2 + 0] = 0xFC;
  bytes[patchLocation2 + 1] = 0x05;
  bytes[patchLocation2 + 8] = 0x2C;
  bytes[patchLocation2 + 9] = 0x00;

}

VOID Patcher_SSE3_5(VOID* kernelData)
{
  UINT8* bytes = (UINT8*)kernelData;
  UINT32 patchLocation1 = 0;
  UINT32 patchLocation2 = 0;
  UINT32 patchlast=0;
  UINT32 Length = sizeof(kernelData);
  UINT32 i;

  DBG("Start find SSE3 address\n");

  for (i=256; i<(Length-256); i++) {
    if (bytes[i] == 0x66 && bytes[i+1] == 0x0F && bytes[i+2] == 0x6F &&
        bytes[i+3] == 0x44 && bytes[i+4] == 0x0E && bytes[i+5] == 0xF1 &&
        bytes[i-1680-32] == 0x55) {
      patchLocation1 = i-1680-32;
      DBG("Found SSE3 data address at 0x%08x\n",patchLocation1);
    }

    // khasSSE2+..... title
    if (bytes[i] == 0xF3 && bytes[i+1] == 0x07 && bytes[i+2] == 0x00 &&
        bytes[i+3] == 0x00 && bytes[i+4] == 0x80 && bytes[i+5] == 0x07 &&
        bytes[i+6] == 0xFF && bytes[i+7] == 0xFF && bytes[i+8] == 0x24 &&
        bytes[i+9] == 0x01) {
      patchLocation2 = i;
      DBG("Found SSE3 Title address at 0x%08x\n",patchLocation2);
      break;
    }
  }

  if (!patchLocation1 || !patchLocation2) {
    DBG("Can't found SSE3 data addres or Title address at 0x%08x 0x%08x\n", patchLocation1, patchLocation2);
    return;
  }

  DBG("Found SSE3 last data addres Start\n");

  for (i=(patchLocation1+1500);i<Length;i++) {
    if (bytes[i] == 0x90 && bytes[i+1] == 0x90 && bytes[i+2] == 0x55) {
      patchlast = (i+1) - patchLocation1;
      DBG("Found SSE3 last data addres at 0x%08x\n", patchlast);
      break;
    }
  }

  if (!patchlast) {
    DBG("Can't found SSE3 data last addres at 0x%08x\n", patchlast);
    return;
  }

  // patech sse3_64 data

  for (i=0; i<patchlast; i++) {
    if (i<sizeof(sse3_5_patcher)) {
      bytes[patchLocation1 + i] = sse3_5_patcher[i];
    } else {
      bytes[patchLocation1 + i] = 0x90;
    }
  }

  // patch kHasSSE3 title
  bytes[patchLocation2 + 0] = 0x0C;
  bytes[patchLocation2 + 1] = 0x06;
  bytes[patchLocation2 + 8] = 0x2C;
  bytes[patchLocation2 + 9] = 0x00;

}

VOID Patcher_SSE3_7(VOID* kernelData)
{
     // not support yet
     return;
}

VOID Get_PreLink()
{
  UINT32  ncmds, cmdsize;
  UINT32  binaryIndex;
  UINTN   cnt;
  UINT8*  binary = (UINT8*)KernelData;
  struct  load_command        *loadCommand;
  struct  segment_command     *segCmd;
  struct  segment_command_64  *segCmd64;


  if (is64BitKernel) {
    binaryIndex = sizeof(struct mach_header_64);
  } else {
    binaryIndex = sizeof(struct mach_header);
  }

  ncmds = MACH_GET_NCMDS(binary);

  for (cnt = 0; cnt < ncmds; cnt++) {
    loadCommand = (struct load_command *)(binary + binaryIndex);
    cmdsize = loadCommand->cmdsize;

    switch (loadCommand->cmd) {
      case LC_SEGMENT_64:
        segCmd64 = (struct segment_command_64 *)loadCommand;
        //DBG("segCmd64->segname = %a\n",segCmd64->segname);
        //DBG("segCmd64->vmaddr = 0x%08x\n",segCmd64->vmaddr)
        //DBG("segCmd64->vmsize = 0x%08x\n",segCmd64->vmsize);
        if (AsciiStrCmp(segCmd64->segname, kPrelinkTextSegment) == 0) {
          DBG("Found PRELINK_TEXT, 64bit\n");
          if (segCmd64->vmsize > 0) {
            // 64bit segCmd64->vmaddr is 0xffffff80xxxxxxxx
            // PrelinkTextAddr = xxxxxxxx + KernelRelocBase
            PrelinkTextAddr = (UINT32)(segCmd64->vmaddr ? segCmd64->vmaddr + KernelRelocBase : 0);
            PrelinkTextSize = (UINT32)segCmd64->vmsize;
            PrelinkTextLoadCmdAddr = (UINT32)(UINTN)segCmd64;
          }
          DBG("at %p: vmaddr = 0x%lx, vmsize = 0x%lx\n", segCmd64, segCmd64->vmaddr, segCmd64->vmsize);
          DBG("PrelinkTextLoadCmdAddr = 0x%x, PrelinkTextAddr = 0x%x, PrelinkTextSize = 0x%x\n",
              PrelinkTextLoadCmdAddr, PrelinkTextAddr, PrelinkTextSize);
          //DBG("cmd = 0x%08x\n",segCmd64->cmd);
          //DBG("cmdsize = 0x%08x\n",segCmd64->cmdsize);
          //DBG("vmaddr = 0x%08x\n",segCmd64->vmaddr);
          //DBG("vmsize = 0x%08x\n",segCmd64->vmsize);
          //DBG("fileoff = 0x%08x\n",segCmd64->fileoff);
          //DBG("filesize = 0x%08x\n",segCmd64->filesize);
          //DBG("maxprot = 0x%08x\n",segCmd64->maxprot);
          //DBG("initprot = 0x%08x\n",segCmd64->initprot);
          //DBG("nsects = 0x%08x\n",segCmd64->nsects);
          //DBG("flags = 0x%08x\n",segCmd64->flags);
        }
        if (AsciiStrCmp(segCmd64->segname, kPrelinkInfoSegment) == 0) {
          UINT32 sectionIndex;
          struct section_64 *sect;

          DBG("Found PRELINK_INFO, 64bit\n");
          //DBG("cmd = 0x%08x\n",segCmd64->cmd);
          //DBG("cmdsize = 0x%08x\n",segCmd64->cmdsize);
          DBG("vmaddr = 0x%08x\n",segCmd64->vmaddr);
          DBG("vmsize = 0x%08x\n",segCmd64->vmsize);
          //DBG("fileoff = 0x%08x\n",segCmd64->fileoff);
          //DBG("filesize = 0x%08x\n",segCmd64->filesize);
          //DBG("maxprot = 0x%08x\n",segCmd64->maxprot);
          //DBG("initprot = 0x%08x\n",segCmd64->initprot);
          //DBG("nsects = 0x%08x\n",segCmd64->nsects);
          //DBG("flags = 0x%08x\n",segCmd64->flags);
          sectionIndex = sizeof(struct segment_command_64);

          while(sectionIndex < segCmd64->cmdsize) {
            sect = (struct section_64 *)((UINT8*)segCmd64 + sectionIndex);
            sectionIndex += sizeof(struct section_64);

            if(AsciiStrCmp(sect->sectname, kPrelinkInfoSection) == 0 && AsciiStrCmp(sect->segname, kPrelinkInfoSegment) == 0) {
              if (sect->size > 0) {
                // 64bit sect->addr is 0xffffff80xxxxxxxx
                // PrelinkInfoAddr = xxxxxxxx + KernelRelocBase
                PrelinkInfoLoadCmdAddr = (UINT32)(UINTN)sect;
                PrelinkInfoAddr = (UINT32)(sect->addr ? sect->addr + KernelRelocBase : 0);
                PrelinkInfoSize = (UINT32)sect->size;
              }
              DBG("__info found at %p: addr = 0x%lx, size = 0x%lx\n", sect, sect->addr, sect->size);
              DBG("PrelinkInfoLoadCmdAddr = 0x%x, PrelinkInfoAddr = 0x%x, PrelinkInfoSize = 0x%x\n",
                  PrelinkInfoLoadCmdAddr, PrelinkInfoAddr, PrelinkInfoSize);
            }
          }
        }
        break;

      case LC_SEGMENT:
        segCmd = (struct segment_command *)loadCommand;
        //DBG("segCmd->segname = %a\n",segCmd->segname);
        //DBG("segCmd->vmaddr = 0x%08x\n",segCmd->vmaddr)
        //DBG("segCmd->vmsize = 0x%08x\n",segCmd->vmsize);
        if (AsciiStrCmp(segCmd->segname, kPrelinkTextSegment) == 0) {
          DBG("Found PRELINK_TEXT, 32bit\n");
          if (segCmd->vmsize > 0) {
            // PrelinkTextAddr = vmaddr + KernelRelocBase
            PrelinkTextAddr = (UINT32)(segCmd->vmaddr ? segCmd->vmaddr + KernelRelocBase : 0);
            PrelinkTextSize = (UINT32)segCmd->vmsize;
            PrelinkTextLoadCmdAddr = (UINT32)(UINTN)segCmd;
          }
          DBG("at %p: vmaddr = 0x%lx, vmsize = 0x%lx\n", segCmd, segCmd->vmaddr, segCmd->vmsize);
          DBG("PrelinkTextLoadCmdAddr = 0x%x, PrelinkTextAddr = 0x%x, PrelinkTextSize = 0x%x\n",
              PrelinkTextLoadCmdAddr, PrelinkTextAddr, PrelinkTextSize);
          //gBS->Stall(30*1000000);
        }
        if (AsciiStrCmp(segCmd->segname, kPrelinkInfoSegment) == 0) {
          UINT32 sectionIndex;
          struct section *sect;

          DBG("Found PRELINK_INFO, 32bit\n");
          //DBG("cmd = 0x%08x\n",segCmd->cmd);
          //DBG("cmdsize = 0x%08x\n",segCmd->cmdsize);
          DBG("vmaddr = 0x%08x\n",segCmd->vmaddr);
          DBG("vmsize = 0x%08x\n",segCmd->vmsize);
          //DBG("fileoff = 0x%08x\n",segCmd->fileoff);
          //DBG("filesize = 0x%08x\n",segCmd->filesize);
          //DBG("maxprot = 0x%08x\n",segCmd->maxprot);
          //DBG("initprot = 0x%08x\n",segCmd->initprot);
          //DBG("nsects = 0x%08x\n",segCmd->nsects);
          //DBG("flags = 0x%08x\n",segCmd->flags);
          sectionIndex = sizeof(struct segment_command);

          while(sectionIndex < segCmd->cmdsize) {
            sect = (struct section *)((UINT8*)segCmd + sectionIndex);
            sectionIndex += sizeof(struct section);

            if(AsciiStrCmp(sect->sectname, kPrelinkInfoSection) == 0 && AsciiStrCmp(sect->segname, kPrelinkInfoSegment) == 0) {
              if (sect->size > 0) {
                // PrelinkInfoAddr = sect->addr + KernelRelocBase
                PrelinkInfoLoadCmdAddr = (UINT32)(UINTN)sect;
                PrelinkInfoAddr = (UINT32)(sect->addr ? sect->addr + KernelRelocBase : 0);
                PrelinkInfoSize = (UINT32)sect->size;
              }
              DBG("__info found at %p: addr = 0x%lx, size = 0x%lx\n", sect, sect->addr, sect->size);
              DBG("PrelinkInfoLoadCmdAddr = 0x%x, PrelinkInfoAddr = 0x%x, PrelinkInfoSize = 0x%x\n",
                  PrelinkInfoLoadCmdAddr, PrelinkInfoAddr, PrelinkInfoSize);
              //gBS->Stall(30*1000000);
            }
          }
        }
        break;

      default:
        break;
    }
    binaryIndex += cmdsize;
  }

  //gBS->Stall(20*1000000);
  return;
}

VOID
FindBootArgs(IN LOADER_ENTRY *Entry)
{
  UINT8           *ptr;
  UINT8           archMode = sizeof(UINTN) * 8;

  // start searching from 0x200000.
  ptr = (UINT8*)(UINTN)0x200000;


  while(TRUE) {

    // check bootargs for 10.7 and up
    bootArgs2 = (BootArgs2*)ptr;

    if (bootArgs2->Version==2 && bootArgs2->Revision==0
        // plus additional checks - some values are not inited by boot.efi yet
        && bootArgs2->efiMode == archMode
        && bootArgs2->kaddr == 0 && bootArgs2->ksize == 0
        && bootArgs2->efiSystemTable == 0
        ) {
      // set vars
      dtRoot = (CHAR8*)(UINTN)bootArgs2->deviceTreeP;
      KernelSlide = bootArgs2->kslide;

      DBG_RT(Entry, "Found bootArgs2 at 0x%08x, DevTree at %p\n", ptr, dtRoot);
      //DBG("bootArgs2->kaddr = 0x%08x and bootArgs2->ksize =  0x%08x\n", bootArgs2->kaddr, bootArgs2->ksize);
      //DBG("bootArgs2->efiMode = 0x%02x\n", bootArgs2->efiMode);
      DBG_RT(Entry, "bootArgs2->CommandLine = %a\n", bootArgs2->CommandLine);
      DBG_RT(Entry, "bootArgs2->flags = 0x%x\n", bootArgs2->flags);
      DBG_RT(Entry, "bootArgs2->kslide = 0x%x\n", bootArgs2->kslide);
      DBG_RT(Entry, "bootArgs2->bootMemStart = 0x%x\n", bootArgs2->bootMemStart);
      if (Entry && Entry->KernelAndKextPatches && Entry->KernelAndKextPatches->KPDebug)
      gBS->Stall(2000000);

      // disable other pointer
      bootArgs1 = NULL;
      break;
    }

    // check bootargs for 10.4 - 10.6.x
    bootArgs1 = (BootArgs1*)ptr;

    if (bootArgs1->Version==1
        && (bootArgs1->Revision==6 || bootArgs1->Revision==5 || bootArgs1->Revision==4)
        // plus additional checks - some values are not inited by boot.efi yet
        && bootArgs1->efiMode == archMode
        && bootArgs1->kaddr == 0 && bootArgs1->ksize == 0
        && bootArgs1->efiSystemTable == 0
        ) {
      // set vars
      dtRoot = (CHAR8*)(UINTN)bootArgs1->deviceTreeP;

      DBG_RT(Entry, "Found bootArgs1 at 0x%08x, DevTree at %p\n", ptr, dtRoot);
      //DBG("bootArgs1->kaddr = 0x%08x and bootArgs1->ksize =  0x%08x\n", bootArgs1->kaddr, bootArgs1->ksize);
      //DBG("bootArgs1->efiMode = 0x%02x\n", bootArgs1->efiMode);

      // disable other pointer
      bootArgs2 = NULL;
      break;
    }

    ptr += 0x1000;
  }
}

BOOLEAN
KernelUserPatch(IN UINT8 *UKernelData, LOADER_ENTRY *Entry)
{
  INTN Num, i = 0, y = 0;
  for (; i < Entry->KernelAndKextPatches->NrKernels; ++i) {
    DBG_RT(Entry, "Patch[%d]: %a\n", i, Entry->KernelAndKextPatches->KernelPatches[i].Label);
    if (!Entry->KernelAndKextPatches->KernelPatches[i].MenuItem.BValue) {
      //DBG_RT(Entry, "Patch[%d]: %a :: is not allowed for booted OS %a\n", i, Entry->KernelAndKextPatches->KernelPatches[i].Label, Entry->OSVersion);
      DBG_RT(Entry, "==> disabled\n");
      continue;
    }

#if 0 //!defined(FKERNELPATCH)
    Num = SearchAndCount(
      UKernelData,
      KERNEL_MAX_SIZE,
      Entry->KernelAndKextPatches->KernelPatches[i].Data,
      Entry->KernelAndKextPatches->KernelPatches[i].DataLen
    );

    if (!Num) {
      DBG_RT(Entry, "==> pattern(s) not found.\n");
      continue;
    }
#endif //FKERNELPATCH

    Num = SearchAndReplace(
      UKernelData,
      KERNEL_MAX_SIZE,
      Entry->KernelAndKextPatches->KernelPatches[i].Data,
      Entry->KernelAndKextPatches->KernelPatches[i].DataLen,
      Entry->KernelAndKextPatches->KernelPatches[i].Patch,
      Entry->KernelAndKextPatches->KernelPatches[i].Count
    );

    if (Num) {
      y++;
    }

    DBG_RT(Entry, "==> %a : %d replaces done\n", Num ? "Success" : "Error", Num);
  }
  if (Entry->KernelAndKextPatches->KPDebug) {
    gBS->Stall(2000000);
  }

  return (y != 0);
}

BOOLEAN
BooterPatch(IN UINT8 *BooterData, IN UINT64 BooterSize, LOADER_ENTRY *Entry)
{
  INTN Num, i = 0, y = 0;
  for (; i < Entry->KernelAndKextPatches->NrBoots; ++i) {
    DBG_RT(Entry, "Patch[%d]: %a\n", i, Entry->KernelAndKextPatches->BootPatches[i].Label);
    if (!Entry->KernelAndKextPatches->BootPatches[i].MenuItem.BValue) {
      DBG_RT(Entry, "==> disabled\n");
      continue;
    }
    
    Num = SearchAndReplace(
                           BooterData,
                           BooterSize,
                           Entry->KernelAndKextPatches->BootPatches[i].Data,
                           Entry->KernelAndKextPatches->BootPatches[i].DataLen,
                           Entry->KernelAndKextPatches->BootPatches[i].Patch,
                           Entry->KernelAndKextPatches->BootPatches[i].Count
                           );
    
    if (Num) {
      y++;
    }
    
    DBG_RT(Entry, "==> %a : %d replaces done\n", Num ? "Success" : "Error", Num);
  }
  if (Entry->KernelAndKextPatches->KPDebug) {
    gBS->Stall(2000000);
  }
  
  return (y != 0);
}

VOID
KernelAndKextPatcherInit(IN LOADER_ENTRY *Entry)
{
  if (PatcherInited) {
    return;
  }

  PatcherInited = TRUE;

  // KernelRelocBase will normally be 0
  // but if OsxAptioFixDrv is used, then it will be > 0
  SetKernelRelocBase();
  DBG("KernelRelocBase = %lx\n", KernelRelocBase);

  // Find bootArgs - we need then for proper detection
  // of kernel Mach-O header
  FindBootArgs(Entry);
  if (bootArgs1 == NULL && bootArgs2 == NULL) {
    DBG("BootArgs not found - skipping patches!\n");
    return;
  }

  // Find kernel Mach-O header:
  // for ML: bootArgs2->kslide + 0x00200000
  // for older versions: just 0x200000
  // for AptioFix booting - it's always at KernelRelocBase + 0x200000
  KernelData = (VOID*)(UINTN)(KernelSlide + KernelRelocBase + 0x00200000);

  // check that it is Mach-O header and detect architecture
  if(MACH_GET_MAGIC(KernelData) == MH_MAGIC || MACH_GET_MAGIC(KernelData) == MH_CIGAM) {
    DBG("Found 32 bit kernel at 0x%p\n", KernelData);
    is64BitKernel = FALSE;
  } else if (MACH_GET_MAGIC(KernelData) == MH_MAGIC_64 || MACH_GET_MAGIC(KernelData) == MH_CIGAM_64) {
    DBG_RT(Entry, "Found 64 bit kernel at 0x%p\n", KernelData);
    is64BitKernel = TRUE;
  } else {
    // not valid Mach-O header - exiting
    DBG_RT(Entry, "Kernel not found at 0x%p - skipping patches!", KernelData);
    KernelData = NULL;
    return;
  }

  // find __PRELINK_TEXT and __PRELINK_INFO
  Get_PreLink();

  isKernelcache = PrelinkTextSize > 0 && PrelinkInfoSize > 0;
  DBG_RT(Entry, "isKernelcache: %s\n", isKernelcache ? L"Yes" : L"No");
}

VOID
KernelAndKextsPatcherStart(IN LOADER_ENTRY *Entry)
{
  BOOLEAN KextPatchesNeeded, patchedOk;

  // we will call KernelAndKextPatcherInit() only if needed
  if ((Entry == NULL) || (Entry->KernelAndKextPatches == NULL)) return;

  KextPatchesNeeded = (
    Entry->KernelAndKextPatches->KPAppleIntelCPUPM ||
    Entry->KernelAndKextPatches->KPAppleRTC ||
    Entry->KernelAndKextPatches->KPDELLSMBIOS ||
    (Entry->KernelAndKextPatches->KPATIConnectorsPatch != NULL) ||
    ((Entry->KernelAndKextPatches->NrKexts > 0) && (Entry->KernelAndKextPatches->KextPatches != NULL))
  );

  DBG_RT(Entry, "\nKernelToPatch: ");
  if (gSettings.KernelPatchesAllowed && (Entry->KernelAndKextPatches->KernelPatches != NULL) && Entry->KernelAndKextPatches->NrKernels) {
    DBG_RT(Entry, "Enabled: ");
    KernelAndKextPatcherInit(Entry);
    if (KernelData == NULL) goto NoKernelData;
    patchedOk = KernelUserPatch(KernelData, Entry);
    DBG_RT(Entry, patchedOk ? " OK\n" : " FAILED!\n");
  } else {
    DBG_RT(Entry, "Disabled\n");
  }

  DBG_RT(Entry, "\nKernelCpu patch: ");
  if (Entry->KernelAndKextPatches->KPKernelCpu) {
    //
    // Kernel patches
    //
    DBG_RT(Entry, "Enabled: ");
    KernelAndKextPatcherInit(Entry);
    if (KernelData == NULL) goto NoKernelData;
    if(is64BitKernel) {
      DBG_RT(Entry, "64 bit patch ...");
      KernelPatcher_64(KernelData, Entry);
    } else {
      DBG_RT(Entry, "32 bit patch ...");
      KernelPatcher_32(KernelData, Entry->OSVersion);
    }
    DBG_RT(Entry, " OK\n");
  } else {
    DBG_RT(Entry, "Disabled\n");
  }

  //other method for KernelCPU patch is FakeCPUID
  DBG_RT(Entry, "\nFakeCPUID patch: ");
  if (Entry->KernelAndKextPatches->FakeCPUID) {
    DBG_RT(Entry, "Enabled: 0x%06x\n", Entry->KernelAndKextPatches->FakeCPUID);
    KernelAndKextPatcherInit(Entry);
    if (KernelData == NULL) goto NoKernelData;
    KernelCPUIDPatch((UINT8*)KernelData, Entry);
  } else {
    DBG_RT(Entry, "Disabled\n");
  }

  // CPU power management patch for haswell with locked msr
  DBG_RT(Entry, "\nKernelPm patch: ");
  if (Entry->KernelAndKextPatches->KPKernelPm) {
    DBG_RT(Entry, "Enabled: ");
    KernelAndKextPatcherInit(Entry);
    if (KernelData == NULL) goto NoKernelData;
    patchedOk = FALSE;
    if (is64BitKernel) {
      patchedOk = KernelPatchPm(KernelData, Entry);
    }
    DBG_RT(Entry, patchedOk ? " OK\n" : " FAILED!\n");
  } else {
    DBG_RT(Entry, "Disabled\n");
  }
  
  // Patch to not dump kext at panic (c)vit9696
  DBG_RT(Entry, "\nPanicNoKextDump patch: ");
  if (Entry->KernelAndKextPatches->KPPanicNoKextDump) {
    DBG_RT(Entry, "Enabled: ");
    KernelAndKextPatcherInit(Entry);
    if (KernelData == NULL) goto NoKernelData;
    patchedOk = KernelPanicNoKextDump(KernelData);
    DBG_RT(Entry, patchedOk ? " OK\n" : " FAILED!\n");
  } else {
    DBG_RT(Entry, "Disabled\n");
  }


  // Lapic Panic Kernel Patch
  DBG_RT(Entry, "\nKernelLapic patch: ");
  if (Entry->KernelAndKextPatches->KPKernelLapic) {
    KernelAndKextPatcherInit(Entry);
    if (KernelData == NULL) goto NoKernelData;
    if(is64BitKernel) {
      DBG_RT(Entry, "64-bit patch ...");
      patchedOk = KernelLapicPatch_64(KernelData);
    } else {
      DBG_RT(Entry, "32-bit patch ...");
      patchedOk = KernelLapicPatch_32(KernelData);
    }
    DBG_RT(Entry, patchedOk ? " OK\n" : " FAILED!\n");
  } else {
    DBG_RT(Entry, "Disabled\n");
  }

  if (Entry->KernelAndKextPatches->KPKernelXCPM) {
    //
    // syscl - EnableExtCpuXCPM: Enable unsupported CPU's PowerManagement
    //
    EnableExtCpuXCPM = NULL;
    patchedOk = FALSE;
    if (gCPUStructure.Vendor == CPU_VENDOR_INTEL) {
      switch (gCPUStructure.Model) {
          case CPU_MODEL_JAKETOWN:
            // SandyBridge-E LGA2011
            EnableExtCpuXCPM = SandyBridgeEPM;
            gSNBEAICPUFixRequire = TRUE;       // turn on SandyBridge-E AppleIntelCPUPowerManagement Fix
            break;
              
          case CPU_MODEL_IVY_BRIDGE:
            // IvyBridge
            EnableExtCpuXCPM = KernelIvyBridgeXCPM;
            break;
              
          case CPU_MODEL_IVY_BRIDGE_E5:
            // IvyBridge-E
            EnableExtCpuXCPM = KernelIvyE5XCPM;
            break;

          case CPU_MODEL_HASWELL_E:
            // Haswell-E
            EnableExtCpuXCPM = HaswellEXCPM;
            break;
              
          case CPU_MODEL_BROADWELL_E5:
          case CPU_MODEL_BROADWELL_DE:
            // Broadwell-E/EP
            EnableExtCpuXCPM = BroadwellEPM;
            gBDWEIOPCIFixRequire = TRUE;
            break;

          default:
            if (gCPUStructure.Model >= CPU_MODEL_HASWELL &&
               (AsciiStrStr(gCPUStructure.BrandString, "Celeron") || AsciiStrStr(gCPUStructure.BrandString, "Pentium"))) {
              // Haswell+ low-end CPU
              EnableExtCpuXCPM = HaswellLowEndXCPM;
            }
            break;
      }
        
      // syscl - now enable extra Cpu's PowerManagement
      // only Intel support this feature till now
      // move below code outside the if condition if AMD supports
      // XCPM later on
      if (EnableExtCpuXCPM) {
        BOOLEAN apply_idle_patch = (gCPUStructure.Model >= CPU_MODEL_SKYLAKE_U) && gSettings.HWP;
        KernelAndKextPatcherInit(Entry);
        if (KernelData == NULL) goto NoKernelData;
        patchedOk = EnableExtCpuXCPM(KernelData, Entry, apply_idle_patch);
      }
    }
    DBG_RT(Entry, "EnableExtCpuXCPM - %a!\n", patchedOk? "OK" : "FAILED");
  }

  if (Entry->KernelAndKextPatches->KPDebug) {
    gBS->Stall(2000000);
  }

  //
  // Kext patches
  //

  // we need to scan kexts if "InjectKexts true and CheckFakeSMC"
  if (/*OSFLAG_ISSET(Entry->Flags, OSFLAG_WITHKEXTS) || */
      OSFLAG_ISSET(Entry->Flags, OSFLAG_CHECKFAKESMC)) {
    DBG_RT(Entry, "\nAllowing kext patching to check if FakeSMC is present\n");
    gSettings.KextPatchesAllowed = TRUE;
    KextPatchesNeeded = TRUE;
  }

  DBG_RT(Entry, "\nKextPatches Needed: %c, Allowed: %c ... ",
         (KextPatchesNeeded ? L'Y' : L'n'),
         (gSettings.KextPatchesAllowed ? L'Y' : L'n')
         );

  if (KextPatchesNeeded && gSettings.KextPatchesAllowed) {
    KernelAndKextPatcherInit(Entry);
    if (KernelData == NULL) goto NoKernelData;
    DBG_RT(Entry, "\nKext patching STARTED\n");
    KextPatcherStart(Entry);  //is FakeSMC found in cache then inject will be disabled
    DBG_RT(Entry, "\nKext patching ENDED\n");
  } else {
    DBG_RT(Entry, "Disabled\n");
  }

  if (Entry->KernelAndKextPatches->KPDebug) {
    DBG_RT(Entry, "Pausing 10 secs ...\n\n");
    gBS->Stall(10000000);
  }

  //
  // Kext add
  //
  if (Entry->KernelAndKextPatches->KPDebug) {
    if (OSFLAG_ISSET(Entry->Flags, OSFLAG_CHECKFAKESMC) &&
        OSFLAG_ISUNSET(Entry->Flags, OSFLAG_WITHKEXTS)) {
    // disabled kext injection if FakeSMC is already present
 //   Entry->Flags = OSFLAG_UNSET(Entry->Flags, OSFLAG_WITHKEXTS); //Slice - we are already here
    
      DBG_RT(Entry, "\nInjectKexts: disabled because FakeSMC is already present and InjectKexts option set to Detect\n");
      gBS->Stall(500000);
    }
  }

  if (OSFLAG_ISSET(Entry->Flags, OSFLAG_WITHKEXTS)) {
    UINT32      deviceTreeP;
    UINT32      deviceTreeLength;
    EFI_STATUS  Status;
    UINTN       DataSize;

    // check if FSInject already injected kexts
    DataSize = 0;
    Status = gRT->GetVariable (L"FSInject.KextsInjected", &gEfiGlobalVariableGuid, NULL, &DataSize, NULL);
    if (Status == EFI_BUFFER_TOO_SMALL) {
      // var exists - just exit
      if (Entry->KernelAndKextPatches->KPDebug) {
        DBG_RT(Entry, "\nInjectKexts: skipping, FSInject already injected them\n");
        gBS->Stall(500000);
      }
      return;
    }

    KernelAndKextPatcherInit(Entry);
    if (KernelData == NULL) goto NoKernelData;
    if (bootArgs1 != NULL) {
      deviceTreeP = bootArgs1->deviceTreeP;
      deviceTreeLength = bootArgs1->deviceTreeLength;
    } else if (bootArgs2 != NULL) {
      deviceTreeP = bootArgs2->deviceTreeP;
      deviceTreeLength = bootArgs2->deviceTreeLength;
    } else return;

    Status = InjectKexts(deviceTreeP, &deviceTreeLength, Entry);

    if (!EFI_ERROR(Status)) KernelBooterExtensionsPatch(KernelData, Entry);
  }

  return;

NoKernelData:
  if (Entry->KernelAndKextPatches->KPDebug) {
    DBG_RT(Entry, "==> ERROR: Kernel not found\n");
    gBS->Stall(5000000);
  }
}
