Some time ago I blogged about XCPM aka Xnu CPU Power Management and I have been digging ever since the first Developer Preview of Mavericks, but I never shared what I know about it. And now that OS X 10.9 Mavericks GM (Golden Master) is released to registered developers, I think that we should start sharing everything we know. People who want to know more about XCPM should also come back, from time to time, because this is only a kick-off blog article. Meaning that more updates will be added in the near future.

Ok. First of all. XCPM is initialised and controlled through the following functions in the XNU kernel (mach_kernel) – no longer with help of AppleIntelCPUPowerManagment.kext:

_xcpm_idle _xcpm_signal_cpu _xcpm_select_cpu _xcpm_cpu_halt _xcpm_exit_halt _xcpm_exit_halt_to_off _xcpm_interrupt_prewake_applicable _xcpm_monitor_init _xcpm_quiesce _xcpm_apply_mbd _xcpm_restore_mbd _xcpm_fi_init _xcpm_fi_update _xcpm_get_ratio _xcpm_transform_vector _xcpm_resolve_limits _xcpm_get_pkg_max _xcpm_accumulate_pstate_ctrs _xcpm_update_last_pstate_time _xcpm_enable_pstate_ctrs _xcpm_resolve_pstate _xcpm_get_deadline _xcpm_timer _xcpm_urgency _xcpm_dvfs_transfer_load _xcpm_dvfs_clear_transferred_load _xcpm_dvfs_start _xcpm_dvfs_configure _xcpm_dvfs_init_deadlines _xcpm_dvfs_pause _xcpm_dvfs_resume _xcpm_qos_name2id _xcpm_qos_id2name _xcpm_dvfs_calibrate _xcpm_set_max_bus_delay _xcpm_get_max_bus_delay _xcpm_set_max_int_delay _xcpm_get_max_int_delay _xcpm_ubpc_read _xcpm_ubpc_config _xcpm_bootstrap _xcpm_perf_bias_set _xcpm_perf_bias_get _xcpm_init _xcpm_register_dispatch_table _xcpm_init_complete _xcpm_enabled _xcpm_cpu_model_get _xcpm_ioctl_init _xcpm_cpu_control _xcpm_callbacks_register

There is however still nothing to be found in the currently available/released XNU source code. Unfortunately. There is no osfmk/x86_64/xcpm directory and thus files like: osfmk/x86_64/xcpm/xcpm_idle.c and osfmk/x86_64/xcpm/xcpm_dvfs.c are missing. We just have to wait for the next update.

Update:

Great. Apple released the source code of OS X 10.8.5 (xnu-2050.48.11) and 10.9.0 (xnu-2422.1.72) and now the xcpm directory is there… but that is all there is because the directory is (still) empty!

Update-3:

Apple released the source code of OS X 10.9.2 (xnu-2422.90.20) but unfortunately for us… the xcpm directory is still empty!

AppleIntelCPUPowerManagement.kext

We’ve long been using/relying on AppleIntelCPUPowerManagement.kext but that is no longer used for properly configured XCPM. In fact. The kext isn’t even loaded anymore because Apple now sets IOResources property intel_cpupm_matching to 3. Previously this was set to 0 and thus the match in AppleIntelCPUPowerManagment.kext/Contents/Info.plist fails:

<key>IOKitPersonalities</key> <dict> <key>IntelCPUPowerManagement</key> <dict> <key>CFBundleIdentifier</key> <string>com.apple.driver.AppleIntelCPUPowerManagement</string> <key>IOClass</key> <string>AppleIntelCPUPowerManagement</string> <key>IOMatchCategory</key> <string>AppleIntelCPUPowerManagement</string> <key>IOPropertyMatch</key> <dict> <key>intel_cpupm_matching</key> <integer>0</integer> </dict> <key>IOProviderClass</key> <string>IOResources</string> <key>IOResourceMatch</key> <string>IOKit</string> </dict> </dict>

How do I know that it works like this? Easy. Open pal_routines.h and look for this snippet:

/* Include a PAL-specific header, too, for xnu-internal overrides */ #include <i386/pal_native.h> extern boolean_t virtualized; #define PAL_VIRTUALIZED_PROPERTY_VALUE 4 /* Allow for tricky IOKit property matching */ #define PAL_AICPM_PROPERTY_NAME "intel_cpupm_matching" static inline void pal_get_resource_property(const char **property_name, int *property_value) { *property_name = PAL_AICPM_PROPERTY_NAME; *property_value = PAL_AICPM_PROPERTY_VALUE; if (virtualized) *property_value = PAL_VIRTUALIZED_PROPERTY_VALUE; }

It gets called from the init routine in IOServices.cpp:

bool IOResources::init( OSDictionary * dictionary ) { // Do super init first if ( !super::init() ) return false; // Allow PAL layer to publish a value const char *property_name; int property_value; pal_get_resource_property( &property_name, &property_value ); if( property_name ) { OSNumber *num; const OSSymbol * sym; if( (num = OSNumber::withNumber(property_value, 32)) != 0 ) { if( (sym = OSSymbol::withCString( property_name)) != 0 ) { this->setProperty( sym, num ); sym->release(); } num->release(); } } return true; }

And lastly. Look at this snippet from pal_native.h:

#define PAL_AICPM_PROPERTY_VALUE 0

That sets it to 0. Again. This is still from OS X 10.8 (currently available) source code. Not Mavericks. But you understand what Apple changed. Don’t you 😉

Good news

Now the good news. AppleIntelCPUPowerManagement.kext is no longer used and you may even remove it. So why is this good news for us? Well. I guess that we all remember the days that we had to use NullCPUPowerManagement.kext because Apple writes to MSR 0xE2 in it. A problem located/solved by Sam (RevoGirl) after she figured out how to patch AppleIntelCPUPowerManagement.kext and some time later the BIOS. However. This may no longer be required. I mean. Apple basically solve it for us. Pretty sweet.

We can now even disabled EIST, Turbo and the Cn states in the (UEFI) BIOS and XCPM will still work. A big change.

plugin-type

Let us go back to Ivy Bridge power management for a sec, which introduced a new property called plugin-type. Apple used a _DSM (Device Specific Method) in one of their ACPI tables to trigger additional plugins to load (X86PlatformPlugin.kext and X86PlatformShim.kext). And we still need to set this property, otherwise the plugins won’t load (matching will fail) and you end up seeing this error in system.log:

ACPI_SMC_PlatformPlugin::start – waitForService(resourceMatching(AppleIntelCPUPowerManagement) timed out

WARNING: IOPlatformPluginUtil : getCPUIDInfo: this is an unknown CPU model 0x3c — power management may be incomplete or unsupported

ACPI_SMC_PlatformPlugin.kext

No. We no longer need it. It is no longer required to have ACPI_SMC_PlatformPlugin.kext loaded. This also means that property Supported Features/Stepper CPU won’t be set, but you need to set plugin-type or you will get the above error.

AppleSMBusPCI.kext

No. We no longer need it. It is no longer required to have AppleSMBusPCI.kext loaded.

AppleSMBusController.kext

Yes. We still need it so make sure that it is getting loaded (run kextstat).

AppleSMCPDRC.kext

No. We no longer need it. It is no longer required to have AppleSMCPDRC.kext loaded.

AppleLPC.kext

Yes. We still need it so make sure it is getting loaded (run kextstat). Please note that Apple is using a different device-id (0x9c43) and we/most of us are using 0x8c44 so it will match differently. Or not at all.

AppleSMC.kext

I missed quite a few of SMC keys (see below) but the thing is that it isn’t always complaining about the missing keys, but I am still trying to figure out why this is. And here is the list of SMC keys that it complained about:

F0Ac, len: 2, type: [fpe2], data: 1217 (bytes 13 04) | Base64: EwQ= PC0R, len: 2, type: [sp78], data: (bytes 09 3a) | Base64: CTo= PM0C, len: 2, type: [sp78], data: (bytes 01 0a) | Base64: AQo= PO0R, len: 2, type: [sp78], data: (bytes 08 ad) | Base64: CK0= PZ0F, len: 4, type: [flt ], data: (bytes 66 96 00 41) | Base64: ZpYAQQ== PZ0E, len: 4, type: [flt ], data: (bytes 00 00 e0 41) | Base64: AADgQQ== PZ1F, len: 4, type: [flt ], data: (bytes 00 00 30 3e) | Base64: AAAwPg== PZ1E, len: 4, type: [flt ], data: (bytes 06 67 f6 40) | Base64: Bmf2QA== PSTR, len: 2, type: [sp78], data: (bytes 08 49) | Base64: CEk= PDTR, len: 2, type: [sp78], data: (bytes 0c 92) | Base64: DJI= MSAc, len: 2, type: [ui8 ], data: 0 (bytes 00) | Base64: AA== MSAf, len: 2, type: [fp6a], data: (bytes 00 00) | Base64: AAA= MSAg, len: 2, type: [ui8 ], data: 0 (bytes 00) | Base64: AA== MSAj, len: 2, type: [fp88], data: (bytes 00 00) | Base64: AAA= MSPA, len: 2, type: [fp6a], data: (bytes 00 00) | Base64: AAA= MSHT, len: 1, type: [ui8 ], data: 0 (bytes 00) | Base64: AA== DM0T, len: 2, type: [hex_], data: (bytes 00 00) | Base64: AAA= WOr0, len: 1, type: [si8 ], data: (bytes ff) | Base64: /w== WOw0, len: 1, type: [si8 ], data: (bytes ff) | Base64: /w== SBFL, len: 4, type: [hex_], data: (bytes 00 00 00 00) | Base64: AAAAAA== TCXC, len: 2, type: [sp78], data: (bytes 47 08) | Base64: Rwg= Ts0S, len: 2, type: [sp78], data: (bytes 26 33) | Base64: JjM= TH0F, len: 2, type: [sp78], data: (bytes dd 20) | Base64: 3SA= TB0T, len: 2, type: [sp78], data: (bytes 21 99) | Base64: IZk= B0AC, len: 2, type: [si16], data: (bytes 00 00) | Base64: AAA= B0AV, len: 2, type: [ui16], data: 92 (bytes 21 5c) | Base64: IVw= B0RM, len: 2, type: [ui16], data: 230 (bytes 1c e6) | Base64: HOY= BRSC, len: 2, type: [ui16], data: 100 (bytes 00 64) | Base64: AGQ= B0St, len: 2, type: [hex_], data: (bytes 40 e0) | Base64: QOA= BIMX, len: 2, type: [ui16], data: 255 (bytes 7f ff) | Base64: f/8= MSMD, len: 1, type: [ui8 ], data: 1 (bytes 01) | Base64: AQ== MSMN, len: 1, type: [fp6a], data: (bytes 00 00) | Base64: AAA= ACIC, len: 2, type: [ui16], data: 128 (bytes 0b 80 | Base64: C4A= MSMF, len: 1, type: [ui8 ], data: 3 (bytes 03) | Base64: Aw== DICT, len: 1, type: [flag], data: (bytes 00) | Base64: AA==

This list may be longer/shorter for you, since it depends on the version of FakeSMC.kext that you are using, and the keys that you already added yourself.

Note: FakeSMC.kext doesn’t report the reads of the above SMC keys. Not at every startup. This may be due caching or the fact that FakeSMC.kext isn’t loading early enough. This is why I would like to suggest to rename FakeSMC.kext to AppleEmulator.kext and changes the source code in such way that it loads properly aka in time for XCPM.

Mach Kernel

Now we know that XCPM is baked into the mach_kernel. For both OS X 10.8.5 and OS X 10.9 Mavericks and thus I skimmed through the mach_kernel and found a couple of boot arguments that we can use:

-xcpm

-xcpm_assert (sets machdep.asserts to 1)

-xcpm_assert_trace (sets machdep.asserts_traced to 1)

-xcpm_ignore_fv (ignores the frequency vectors)

-xcpm_ipi

idlehalt

cstates

sysctl

The command line tool sysctl now also includes the MIB name (Management Information Base) machdep.xcpm:

sysctl machdep.xcpm

And running the above command in a terminal window dumped this:

machdep.xcpm.mode: 1

machdep.xcpm.asserts_enabled: 0

machdep.xcpm.asserts_traced: 0

machdep.xcpm.cpu_wakeup_energy_cost_selector: 0

machdep.xcpm.disable_quiesce: 0

machdep.xcpm.mp_match: 0

machdep.xcpm.disable_idle_self_select: 0

machdep.xcpm.mp_load_txfr_coeff: 128

machdep.xcpm.scalability_cpi_threshold: 4

machdep.xcpm.scalability_cpi_demotion_threshold: 5

machdep.xcpm.scalability_reeval_interval: 1000

machdep.xcpm.scalability_eval_ratio_min: 17

machdep.xcpm.scalability_detection_enabled: 0

machdep.xcpm.hard_plimit_max_100mhz_ratio: 38

machdep.xcpm.hard_plimit_min_100mhz_ratio: 8

machdep.xcpm.soft_plimit_max_100mhz_ratio: 38

machdep.xcpm.soft_plimit_min_100mhz_ratio: 8

machdep.xcpm.ratio_changes_hf: 0

machdep.xcpm.ratio_changes_lf: 0

machdep.xcpm.ratio_change_limited: 0

machdep.xcpm.ratio_change_hf_limit: 2

machdep.xcpm.ratio_change_lf_limit: 1

machdep.xcpm.ratio_change_ratelimit_ns: 500000

machdep.xcpm.ratio_changes_total: 33246

machdep.xcpm.maxbusdelay: 4294967295

machdep.xcpm.maxintdelay: 0

machdep.xcpm.mbd_mode: 1

machdep.xcpm.mbd_applications: 437

machdep.xcpm.mbd_relaxations: 437

machdep.xcpm.forced_idle_ratio: 100

machdep.xcpm.forced_idle_period: 30000000

machdep.xcpm.deep_idle_log: 0

machdep.xcpm.qos_txfr: 1

machdep.xcpm.q_migration_ttd_min: 1000000

machdep.xcpm.q_migration_ttd_max: 100000000

machdep.xcpm.preidle_spin_tsc: 0

machdep.xcpm.qos_ratio_change_limited: 0

machdep.xcpm.qos_ratio_change_hf_limit: 8

machdep.xcpm.qos_ratio_change_lf_limit: 1

machdep.xcpm.qos_ratelimiting_enabled: 1

machdep.xcpm.quiesce_trace: 0

machdep.xcpm.deep_idle_count: 0

machdep.xcpm.deep_idle_last_stats: n/a

machdep.xcpm.deep_idle_total_stats: n/a

Running sysctl machdep.xcpm.mode shows us if XCPM is active and sysctl machdep.xcpm.ratio_changes_total shows us the number of ratio changes. Zero means that XCPM is not functioning/operational. In that case you should also check the former MIB as well.

UEFI BIOS Settings

Like I said earlier. We can now disable: EIST, Turbo, and CST settings in our UEFI BIOS and still enjoy using XCPM, simply because the old days are over. That is for hardware with the following product-name:

MacBookAir, iMac, Macmini, MacBook Pro and MacPro

And one of the following board-id‘s:

Mac-00BE6ED71E35EB86 – iMac13,1

Mac-031AEE4D24BFF0B1 – Macmini6,1

Mac-031B6874CF7F642A – iMac14,1 (Haswell/FrequencyVectors)

Mac-189A3D4F975D5FFC – MacBookPro11,1 (Haswell/FrequencyVectors)

Mac-27ADBB7B4CEE8E61 – iMac14,2 (Haswell/FrequencyVectors)

Mac-2E6FAB96566FE58C – MacBookAir5,2

Mac-35C1E88140C3E6CF – MacBookAir6,1 (Haswell/FrequencyVectors)

Mac-3CBD00234E554E41 – MacBookPro11,2 (Haswell/FrequencyVectors)

Mac-2BD1B31983FE1663 – MacBookPro11,3 (Haswell/FrequencyVectors)

Mac-4B7AC7E43945597E – MacBookPro9,1

Mac-66F35F19FE2A0D05 – MacBookAir5,1

Mac-6F01561E16C75D06 – MacBookPro9,2

Mac-77EB7D7DAF985301 – iMac14,3 (Haswell/FrequencyVectors)

Mac-7DF21CB3ED6977E5 – MacBookAir6,2 (Haswell/FrequencyVectors)

Mac-7DF2A3B5E5D671ED – Mac??? (2012 model)

Mac-AFD8A9D944EA4843 – MacBookPro10,2

Mac-C3EC7CD22292981F – MacBookPro10,1

Mac-F60DEB81FF30ACF6 – MacPro6,1

Mac-F65AE981FFA204ED – Macmini6,2

Mac-FC02E91DDD3FA6A4 – iMac13,2

This also means that the boot argument -xcpm_ignore_fv is only supported/works with certain board-id’s. This boot option will disable all P-States between the lowest (800 MHz) and highest non-Turbo P-State (in my case above 800 MHz and below 3.4 GHz)

Native Power Management

Some folks think to have “native” power management on their Haswell setups, just because their boot loader isn’t loading a DSDT and/or (generated) SSDT with proper data, but most of them are wrong. Sure. AICPUPMI may show something like this:

CPU P-States [ 8 34 35 (36) 37 ]

iGPU P-States [ 4 7 8 9 (10) 11 12 14 15 16 17 25 ]

But all it means is that a limited number of P-States is used. It says absolutely nothing about what is driving it and if low power states (C-States) are even used or not. A next write-up from me will make this clear for everyone. Well. That is my intention.

Note: The above iGPU data shows a 4, but that is wrong since the installed desktop processor can’t scale down to 200 MHz, it is however part of the AGPM data and thus we end up seeing it. A new update of AppleIntelCPUPowerManagementInfo.kext (soon to be released) will take care of it. Yes it can (link).

This is still a limited amount of information. Stuff that I gathered about XCPM during my use of OS X 10.9 Mavericks, but I am pretty sure that many blog posts and forum threads will show in the near future. And of course. You can count on me being one of the first to push out more updates about XCPM. All I need to do is to make some time for it, but I didn’t want to wait any longer with this information/data but this has to be it for today (it was a lot of work).

Update

_xcpm_core_scope_msrs is defined as 0xE2 so it seems like people with a locked (UEFI) BIOS are screwed once again. Maybe someone with a Z77 board with locked UEFI BIOS can verify this for us. And yes indeed. XCPM also supports Ivy Bridge processors.

Update-2:

I have setup my hack as a Macmini7,1 MacBookPro11,1 and now I get this:

AICPUPMI:

CPU P-States [ 8 (21) 34 35 36 37 38 ]

iGPU P-States [ 7 8 9 10 11 12 13 14 15 16 17 18 19 (23) 24 25 ]

Showing all iGPU P-States, but still a limited number (7) of CPU states. Better than nothing. This with every PM option (EIST/Turbo and C-States) disabled in the UEFI BIOS, but it is still a work in progress.

Update-3:

A new error is triggered in OS X 10.9.2 Mavericks (Build 13C32) when you set Name (APLF, Zero) to anything but Zero:

XCPM: P-state table mismatch (error:0x8) X86PlatformShim::sendPStates - pmCPUControl (XCPMIO_SETPSTATETABLE) returned 0x8 X86PlatformShim::start - Failed to send PStates

You may also see:

X86PlatformShim::start - Failed to send stepper

Which means that the FrequencyVectors data in, for example, the Mac-F60DEB81FF30ACF6.plist is missing.