The lack of XCPM support for my new Broadwell E processors was a bit frustrating and thus I had to do something about it. And I guess that most of you know me by now so it was just a matter of time. As in. Consider it done!
Yup. Time to say good-bye to the good old AppleIntelCPUPowerManagement.kext and the NullCPUPowerManagement.kext that served so many people for so long. Adieu my friends. They won’t be missed. LOL
Right. I can boot macOS Sierra and I see IOPPF: XCPM mode along with XCPM registered. That is pretty cool. In fact. This is amazing. Let’s go check some stuff.
sysctl -n machdep.xcpm.mode
returns 1 so that is fine. Also. Running kextstat shows me that both AppleIntelCPUPowerManagement.kext and NullCPUPowerManagement.kext are missing in the list with loaded kexts. Great. Now a Geekbench v3.4.1 top score – one of four runs – of the Intel i7-6850K.
I must admit. The longer I use this processor the more I get impressed by it. Who would have thought that? And let’s not forget. This is with XCPM so it is the more impressive. In fact. People with an old i7-5930K may keep it.
One thing though and that is that the RAM can’t run at full speed (runs like 2133MHz due to some bug) but anyway. Here is the output of AppleIntelInfo.kext
AppleIntelInfo.kext v1.5 Copyright © 2012-2016 Pike R. Alpha. All rights reserved
Settings:
------------------------------------
logMSRs............................: 1
logIGPU............................: 0
logCStates.........................: 1
logIPGStyle........................: 1
InitialTSC.........................: 0x150cbc299c8
MWAIT C-States.....................: 8480
Model Specific Registers
-----------------------------------
MSR_CORE_THREAD_COUNT......(0x35) : 0x6000C
MSR_PLATFORM_INFO..........(0xCE) : 0x20080C3BF3812400
MSR_PMG_CST_CONFIG_CONTROL.(0xE2) : 0x8402
MSR_PMG_IO_CAPTURE_BASE....(0xE4) : 0x10414
IA32_MPERF.................(0xE7) : 0x387EBCA78F
IA32_APERF.................(0xE8) : 0x3A56B9B06F
MSR_0x150..................(0x150) : 0x0
MSR_FLEX_RATIO.............(0x194) : 0xE0000
MSR_IA32_PERF_STATUS.......(0x198) : 0x23CF00002500
MSR_IA32_PERF_CONTROL......(0x199) : 0x2400
IA32_CLOCK_MODULATION......(0x19A) : 0x0
IA32_THERM_STATUS..........(0x19C) : 0x88490000
IA32_MISC_ENABLES..........(0x1A0) : 0x840089
MSR_MISC_PWR_MGMT..........(0x1AA) : 0x402000
MSR_TURBO_RATIO_LIMIT......(0x1AD) : 0x2525252525252828
IA32_ENERGY_PERF_BIAS......(0x1B0) : 0x1
MSR_POWER_CTL..............(0x1FC) : 0x2904005B
MSR_RAPL_POWER_UNIT........(0x606) : 0xA0E03
MSR_PKG_POWER_LIMIT........(0x610) : 0x7FFF80015FFF8
MSR_PKG_ENERGY_STATUS......(0x611) : 0x90C48C3
MSR_PKG_POWER_INFO.........(0x614) : 0x1700460
MSR_PP0_POWER_LIMIT........(0x638) : 0x0
MSR_PP0_ENERGY_STATUS......(0x639) : 0x0
MSR_PKGC6_IRTL.............(0x60b) : 0x0
MSR_PKG_C2_RESIDENCY.......(0x60d) : 0xA4268B844
MSR_PKG_C6_RESIDENCY.......(0x3f9) : 0x79FDD64350
IA32_TSC_DEADLINE..........(0x6E0) : 0x150CD144AC8
CPU Ratio Info:
------------------------------------
CPU Low Frequency Mode.............: 1200 MHz
CPU Maximum non-Turbo Frequency....: 3600 MHz
CPU Maximum Turbo Frequency........: 4000 MHz
CPU P-States [ 33 37 (40) ]
CPU C6-Cores [ 0 2 5 7 9 10 ]
CPU P-States [ 31 33 37 (40) ]
CPU C6-Cores [ 0 2 3 4 5 7 9 10 ]
CPU P-States [ (12) 31 33 37 40 ]
CPU C6-Cores [ 0 1 2 3 4 5 6 7 9 10 11 ]
CPU P-States [ (12) 25 31 33 37 40 ]
CPU P-States [ 12 25 29 31 33 37 (40) ]
CPU P-States [ 12 25 29 30 31 33 37 (40) ]
CPU P-States [ 12 25 28 29 30 31 33 37 (40) ]
CPU C6-Cores [ 0 1 2 3 4 5 6 7 8 9 10 11 ]
CPU P-States [ (12) 22 25 28 29 30 31 33 37 40 ]
CPU P-States [ (12) 21 22 25 28 29 30 31 33 37 40 ]
CPU P-States [ (12) 21 22 23 25 28 29 30 31 33 37 40 ]
CPU P-States [ (12) 20 21 22 23 25 28 29 30 31 33 37 40 ]
CPU P-States [ 12 20 21 22 23 25 28 29 30 31 32 33 37 (40) ]
CPU P-States [ 12 20 21 22 23 25 28 29 30 31 32 33 37 39 (40) ]
CPU P-States [ 12 20 21 22 23 25 27 28 29 30 31 32 33 37 39 (40) ]
CPU P-States [ (12) 17 20 21 22 23 25 27 28 29 30 31 32 33 37 39 40 ]
CPU P-States [ (12) 16 17 20 21 22 23 25 27 28 29 30 31 32 33 37 39 40 ]
CPU P-States [ (12) 16 17 18 20 21 22 23 25 27 28 29 30 31 32 33 37 39 40 ]
CPU P-States [ 12 16 17 18 19 20 21 22 23 25 27 28 29 30 31 32 33 37 39 (40) ]
CPU P-States [ 12 16 17 18 19 20 21 22 23 25 27 28 29 30 31 32 33 37 38 39 (40) ]
CPU P-States [ 12 16 17 18 19 20 21 22 23 25 26 27 28 29 30 31 32 33 37 38 39 (40) ]
CPU P-States [ 12 16 17 18 19 20 21 22 23 25 26 27 28 29 30 31 32 33 36 37 38 39 (40) ]
CPU P-States [ (12) 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 36 37 38 39 40 ]
CPU P-States [ 12 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 36 37 38 39 (40) ]
CPU P-States [ 12 14 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 36 37 38 39 (40) ]
CPU P-States [ (12) 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 36 37 38 39 40 ]
CPU P-States [ 12 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 (40) ]
CPU P-States [ (12) 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 ]
Hmm. C3 is missing. Seems like I forgot to enable C3 for Broadwell E processors and thus I have to fix this and re-compile the kext. On a second thought. The redirection bits are not set in MSR 0xE2 so this is expected result.
But both MSR_PKG_C2_RESIDENCY and MSR_PKG_C6_RESIDENCY are non-zero. A sweet spot.
Update: I don’t have a lot of time, but I also do not want to let you wait so here you have it!
//
// kernel location 0x1fb451 (0xE9 in DP2/DP3/DP4, 0xF1 in DP1) _cpuid_set_info
//
// Broadwell E (0x4F) -> Broadwell (0x47) => 0x4F-0x47 = 0x08 -> change 0xE9 into 0xE1
// Haswell E (0x3F) -> Haswell (0x3C) => 0x3F-0x3C = 0x03 -> change 0xE9 into 0xE6
// Ivy Bridge E (0x3E) -> Ivy Bridge (0x3A) => 0x3E-0x3A = 0x04 -> change 0xE9 into 0xE5
../..
//
// kernel location 0x22a422 (0xC4 in DP2/DP3/DP4, 0x?? in DP1) _xcpm_bootstrap
//
// Broadwell E (0x4F) -> Broadwell (0x47) => 0x4F-0x47 = 0x08 -> change 0xC4 into 0xBC
// Haswell E (0x3F) -> Haswell (0x3C) => 0x3F-0x3C = 0x03 -> change 0xC4 into 0xC1
// Ivy Bridge E (0x3E) -> Haswell (0x3C) => 0x3E-0x3C = 0x02 -> change 0xC4 into 0xC2
//
// Broadwell E (0x4F) -> Haswell (0x3C) => 0x4F-0x3C = 0x13 -> change 0xC4 into 0xB1
../..
//
// If MSR(0xE2) is locked (bit-15 is set) then also change
//
// kernel location 0x220b30 and 0x220b5f in _xcpm_idle from 0x0f30 (wrmsr) to 0x9090 (nop nop).
//
// Additionally (if booting results in a immediate reboot)
//
// kernel location 0x22a820 change 0x55 into 0xC3 (ret) to stop the KP.
//
Update: DP4 is now available and the byte patterns are still the same, but the locations have changed to: 0x1f8d81, 0x227d32, 0x21e440, 0x21e46f and 0x228130 (same order as above).
Update-2: DP5 is now available and the byte patterns are still the same, but the locations have changed to: 0x1f8930, 0x228af0, 0x21eff0, 0x21f01f and 0x228f50 (same order as above).
Update-3: DP6 is now available and the byte patterns are still the same, but the locations have changed to: 0x1f7cc0, 0x227f30, 0x21e430, 0x21e45f and 0x428390 (same order as above).
Update-4: DP7 is now available and the byte patterns are still the same, but the locations have changed to: 0x1f8d71, 0x227fc2, 0x21e460, 0x21e48f and 0x2283c0 (same order as above).
The patches can be done in at least three different ways and I picked what I think is the easiest one to understand. So I hope. Also. I don’t use FakeCPUID – nor Clover for that matter – with my patches, but some people using Clover reported a hang. For them it won’t boot without FakeCPUID (thanks to giacomoleopardo for the update).
Update: Owners of Ivy Bridge E or Haswell E processors do not need to patch the switch table used by _cpuid_set_info, because Apple already took care of it. Anyway. Have a look at this table:

The offset for the Broadwell E processor is missing in this table. There is an address but that jumps to a location for unsupported processors, which is why we need a patch for the Broadwell E processor (the red arrow shows you what we do with the patch) but I think that Apple will fix this with a future update of macOS Sierra so that we also no longer need to patch the table for Broadwell E processors.
Please note that the table was converted from the original one in the kernel, which looks like this:
0xffffff80003fbc6c dd 0xfffff82c, 0xfffff831, 0xfffff831, 0xfffff7fb
0xffffff80003fbc7c dd 0xfffff831, 0xfffff831, 0xfffff831, 0xfffff7fb
0xffffff80003fbc8c dd 0xfffff7fb, 0xfffff831, 0xfffff831, 0xfffff831
0xffffff80003fbc9c dd 0xfffff831, 0xfffff831, 0xfffff809, 0xfffff831
0xffffff80003fbcac dd 0xfffff831, 0xfffff831, 0xfffff831, 0xfffff810
0xffffff80003fbcbc dd 0xfffff831, 0xfffff809, 0xfffff810, 0xfffff7fb
0xffffff80003fbccc dd 0xfffff809, 0xfffff831, 0xfffff831, 0xfffff831
0xffffff80003fbcdc dd 0xfffff831, 0xfffff831, 0xfffff831, 0xfffff831
0xffffff80003fbcec dd 0xfffff831, 0xfffff831, 0xfffff831, 0xfffff817
0xffffff80003fbcfc dd 0xfffff831, 0xfffff802, 0xfffff81e, 0xfffff817
0xffffff80003fbd0c dd 0xfffff802, 0xfffff831, 0xfffff831, 0xfffff831
0xffffff80003fbd1c dd 0xfffff831, 0xfffff831, 0xfffff802, 0xfffff802
0xffffff80003fbd2c dd 0xfffff81e, 0xfffff831, 0xfffff831, 0xfffff831
0xffffff80003fbd3c dd 0xfffff831, 0xfffff831, 0xfffff831, 0xfffff825
0xffffff80003fbd4c dd 0xfffff831, 0xfffff831, 0xfffff831, 0xfffff831
0xffffff80003fbd5c dd 0xfffff831, 0xfffff831, 0xfffff831, 0xfffff831
0xffffff80003fbd6c dd 0xfffff831, 0xfffff831, 0xfffff831, 0xfffff831
0xffffff80003fbd7c dd 0xfffff831, 0xfffff831, 0xfffff831, 0xfffff825
Update: DP4 is now available and while the table is still the same, the location of it has changed to: 0xffffff80003f959c
Update-2: DP5 is now available and while the table is still the same, the location of it has changed to: 0xffffff8000428cbc
Update-3: DP7 is now available and while the table is still the same, the location of it has changed to: 0xffffff80003f958c
And here is what I did with the values. Let’s take the last value (0xfffff825 ) as an example:
0xffffffff - (0xfffff825 - 0x01) = 0x7DB
I then used the value as a negative offset, and we do that because the table is located at a higher memory address, so here we go:
0xffffff80003fbc6c - 0x7DB = 0xffffff80003FB491
Great. Now look at this disassembled code snippet:
0xffffff80003fb43b movzwl 0x6fcc42(%rip), %eax
0xffffff80003fb442 xorl %ebx, %ebx // Zero out %ebx
0xffffff80003fb444 movzbl %al, %ecx
0xffffff80003fb447 cmpl $0x6, %ecx // Check CPU family
0xffffff80003fb44a jne 0xffffff80003fb49d
0xffffff80003fb44c movzbl %ah, %eax
0xffffff80003fb44f addl $-0x17, %eax // Lower model number (example: 0x5e -> 0x4f)
0xffffff80003fb452 cmpl $0x47, %eax // Check for unsupported model numbers
0xffffff80003fb455 ja 0xffffff80003fb49d // Jump if greater than 0x47 (unsupported model numbers)
0xffffff80003fb457 leaq 0x80e(%rip), %rcx // Store address of case table 0xffffff80003fbc6c into %rcx
0xffffff80003fb45e movslq (%rcx,%rax,4), %rax // Move and sign-extend 32-bit value from switch table in %rax
0xffffff80003fb462 addq %rcx, %rax // Example: 0xffffff80003fbc6c - 0x7db = 0xffffff80003fb491 (CPUFAMILY_INTEL_SKYLAKE)
0xffffff80003fb465 jmpq *%rax // Jump to target address
0xffffff80003fb467 movl $0x6b5a4cd2, %ebx // CPUFAMILY_INTEL_NEHALEM
0xffffff80003fb46c jmp 0xffffff80003fb49d
0xffffff80003fb46e movl $0x10b282dc, %ebx // CPUFAMILY_INTEL_HASWELL
0xffffff80003fb473 jmp 0xffffff80003fb49d
0xffffff80003fb475 movl $0x573b5eec, %ebx // CPUFAMILY_INTEL_WESTMERE
0xffffff80003fb47a jmp 0xffffff80003fb49d
0xffffff80003fb47c movl $0x5490b78c, %ebx // CPUFAMILY_INTEL_SANDYBRIDGE
0xffffff80003fb481 jmp 0xffffff80003fb49d
0xffffff80003fb483 movl $0x1f65e835, %ebx // CPUFAMILY_INTEL_IVYBRIDGE
0xffffff80003fb488 jmp 0xffffff80003fb49d
0xffffff80003fb48a movl $0x582ed09c, %ebx // CPUFAMILY_INTEL_BROADWELL
0xffffff80003fb48f jmp 0xffffff80003fb49d
0xffffff80003fb491 movl $0x37fc219f, %ebx // CPUFAMILY_INTEL_SKYLAKE
0xffffff80003fb496 jmp 0xffffff80003fb49d
0xffffff80003fb498 movl $0x78ea4fbc, %ebx // CPUFAMILY_INTEL_PENRYN
0xffffff80003fb49d movl %ebx, 0x6fcd35(%rip)
0xffffff80003fb4a3 cmpl $0x0, 0x6fcb6e(%rip)
0xffffff80003fb4aa je 0xffffff80003fb4c3
Our calculated address is spot on. Now let’s have a look at the switch table that is used by _xcpm_bootstrap in macOS Sierra DP3:
0xffffff800042a58c dd 0xfffffeac, 0xfffffec7, 0xfffffeb8, 0xfffffeb8
0xffffff800042a59c dd 0xfffffeb8, 0xfffffeb8, 0xfffffeb8, 0xfffffeb8
0xffffff800042a5ac dd 0xfffffeb8, 0xfffffed3, 0xfffffee9, 0xffffff0a
0xffffff800042a5bc dd 0xfffffeb8, 0xfffffeb8, 0xfffffeb8, 0xfffffeb8
0xffffff800042a5cc dd 0xfffffeb8, 0xfffffeb8, 0xffffff16, 0xfffffeb8
0xffffff800042a5dc dd 0xfffffeb8, 0xfffffeb8, 0xfffffeb8, 0xfffffeb8
0xffffff800042a5ec dd 0xfffffeb8, 0xfffffeb8, 0xfffffeb8, 0xfffffeb8
0xffffff800042a5fc dd 0xfffffeb8, 0xfffffeb8, 0xfffffeb8, 0xfffffeb8
0xffffff800042a60c dd 0xfffffeb8, 0xfffffeb8, 0xffffff2c
Update: DP4 is now available and while the table is still the same, the location of it has changed to: 0xffffff8000427e9c
Update: DP7 is now available and while the table is still the same, the location of it has changed to: 0xffffff800042812c
A shorter table with 34 instead of 72 values, but the idea here is the same. And with the same kind of table conversion we get this:

This time the table starts at 0x3c instead of 0x17 and there are no addresses for the Ivy Bridge E, Haswell E and Broadwell E processors, again, for the same reason; it jumps to a location for processors that do not support XCPM.
Speaking about that address. All locations in the switch table in the kernel with the value 0xfffffeb8 jump to the same location for unsupported processors and here is how to calculate the offset and target address for the jmp instruction:
0xffffffff - (0xfffffeb8-0x01) = 0x148
0xffffff800042a58c - 0x148 = 0xffffff800042a444
And looking at the disassembled code snipped of _xcpm_bootstrap that is where XCPM gets disabled:
0xffffff800042a420 addl $-0x3c, %ebx // Lower model number (example: 0x5e -> 0x22(34)
0xffffff800042a423 cmpl $0x22, %ebx // Check for unsupported models
0xffffff800042a426 ja 0xffffff800042a444 // Jump if greater than 0x22 (unsupported model numbers)
0xffffff800042a428 leaq 0x15d(%rip), %rax // Store address of case table 0xffffff800042a58c into %rcx
0xffffff800042a42f movslq (%rax,%rbx,4), %rcx // Move and sign-extend 32-bit value from switch table in %rax
0xffffff800042a433 addq %rax, %rcx // Example: 0xffffff800042a58c - 0xd4 = 0xffffff80000x42a4b8 (Skylake)
0xffffff800042a436 jmpq *%rcx // Jump to target address
0xffffff800042a438 movl $0x4, _xcpm_cpu_model(%rip) // case 0: Haswell (0x3c-0x3c=0)
0xffffff800042a442 jmp 0xffffff800042a47f
0xffffff800042a444 movl $0x0, _xcpm_mode(%rip) // case 33: xcpm_mode = 0 (XCPM disabled)
0xffffff800042a44e jmp 0xffffff800042a580
0xffffff800042a453 movl $0x80, _xcpm_cpu_model(%rip) // case 1: Broadwell (0x3d-0x3c=1)
0xffffff800042a45d jmp 0xffffff800042a4ac
0xffffff800042a45f movl $0x10, _xcpm_cpu_model(%rip) // case 9: Haswell-ULT (0x45-0x3c=9)
0xffffff800042a469 movl $0x1, 0x602981(%rip)
0xffffff800042a473 jmp 0xffffff800042a47f
0xffffff800042a475 movl $0x8, _xcpm_cpu_model(%rip) // case 10: Crystalwell (0x46-0x3c=10)
0xffffff800042a47f movl $0x1, 0x602973(%rip)
0xffffff800042a489 movq $0x0, 0x6029dc(%rip)
0xffffff800042a494 jmp 0xffffff800042a50c
0xffffff800042a496 movl $0x40, _xcpm_cpu_model(%rip) // case 11: Broadwell-H (0x47-0x3c=11)
0xffffff800042a4a0 jmp 0xffffff800042a50c
0xffffff800042a4a2 movl $0x200, _xcpm_cpu_model(%rip) // case 18: Skylake (0x4e-0x3c=18)
0xffffff800042a4ac movl $0x1, 0x60293e(%rip)
0xffffff800042a4b6 jmp 0xffffff800042a50c
0xffffff800042a4b8 movq 0x6cebf1(%rip), %rdi // case 34: Skylake (0x5e-0x3c=34)
0xffffff800042a4bf testq %rdi, %rdi
0xffffff800042a4c2 jne 0xffffff800042a4d9
0xffffff800042a4c4 movq 0x6999b5(%rip), %rax
0xffffff800042a4cb movq 0x488(%rax), %rdi
0xffffff800042a4d2 movq %rdi, 0x6cebd7(%rip)
0xffffff800042a4d9 addq $0x2, %rdi
0xffffff800042a4dd movl $0x2, %esi
0xffffff800042a4e2 callq 0xffffff8000402580
0xffffff800042a4e7 movzwl %ax, %eax
0xffffff800042a4ea cmpl $0x1910, %eax
0xffffff800042a4ef movl $0x2000, %eax
0xffffff800042a4f4 movl $0x1000, %ecx
0xffffff800042a4f9 cmovel %eax, %ecx
0xffffff800042a4fc movl %ecx, _xcpm_cpu_model(%rip)
0xffffff800042a502 movl $0x0, 0x6028e8(%rip)
This routine has no matching case number for the Ivy Bridge E, Haswell E and Broadwell E processors. That is why we lower the model number to make it match with a supported processors model. Like we are using a normal Ivy Bridge, Haswell or Broadwell processor. A simple but effective trick it seems.
I hope that my explanation about all this helps you to understand what we are doing.
Edit: Make sure that you either use a SMBIOS model/board-id with FrequencyVectors data in its plist, or patch it with help of freqVectorsEdit.sh v2.3.
You can verify that the FrequencyVector data is loaded with help of sysctl -n machdep.xcpm.vectors_loaded_count
Tips:
1.) The X86PlatformPlugin.kext will only load with the plugin-type property is set on the first logical CPU. This however is not enough to enable XCPM mode. No. You may still use AppleIntelCPUPowerManagement.kext Even when X86PlatformShim.kext is loaded.
2.) The FrequencyVectors data in the plist is used to configure power management, and is not the same for all models/board-ids. Please use one that works for your setup.
3.) If sysctl -n machdep.xcpm.vectors_loaded_count
returns 0 then the FrequencyVectors data is not being used. Backup the plist for your board-id and replace it with a different plist.
4.) If you use ssdtPRGen.sh to generate your ssdt_pr.aml then make sure to use the -turbo [top-turbo-frequency] argument for overclocked setups.
5.) Check for XCPM related errors at boot time. Like this: X86PlatformShim::start – Failed to send stepper. You got to fix errors or things may not work properly.
TODOs:
1.) Fix LFM frequency (fixed to 800MHz).
2.) Figure out what MSRs trigger a reboot and only block them. Not all other supported MSRs as well.