Search code examples
cuefimsrrefind

How to make changes to msr 0x199 from EFI stick?


I have a macbookpro11,3 without a battery. When battery is removed the firmware throttles the CPU to half speed. In Windows I can override this using Throttlestop to turn off BD PROCHOT and set multiplier to 25. I want to do this from EFI so that boot and updates run at a normal speed.

Based on source for rEFInd which updates 0x3a register I wrote this program but while BD PROCHOT is disabled correctly after booting into Windows the multiplier is not.

#include "../include/tiano_includes.h"
static VOID DisablePROCHOT(VOID)
{
    UINT32 msr = 0x1FC;
    UINT32 low_bits = 0, high_bits = 0;

    __asm__ volatile ("rdmsr" : "=a" (low_bits), "=d" (high_bits) : "c" (msr));

    // lowest bit is BD PROCHOT 
    low_bits &= ~(1 << 0);

    __asm__ volatile ("wrmsr" : : "c" (msr), "a" (low_bits), "d" (high_bits));
} // VOID DisablePROCHOT()

static VOID SetMultiplier25(VOID)
{
    UINT32 msr = 0x199;
    UINT32 low_bits = 0, high_bits = 0;

    __asm__ volatile ("rdmsr" : "=a" (low_bits), "=d" (high_bits) : "c" (msr));

    // second lowest byte is multiplier
    // 25 is .... xxxxxxxx 00011001 xxxxxxxx 
    low_bits |= 1 << 8;
    low_bits &= ~(1 << 9);
    low_bits &= ~(1 << 10);
    low_bits |= 1 << 11;
    low_bits |= 1 << 12;
    low_bits &= ~(1 << 13);
    low_bits &= ~(1 << 14);
    low_bits &= ~(1 << 15);

    __asm__ volatile ("wrmsr" : : "c" (msr), "a" (low_bits), "d" (high_bits));
} // VOID SetMultiplier25()

EFI_STATUS
EFIAPI
efi_main (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  DisablePROCHOT();
  SetMultiplier25();

  return EFI_SUCCESS;
}

Reading the registers with rdmsr from EFI appears to show both are set correctly however when booted into Windows while bit 0 of 0x1FC is correctly set off the multiplier stored in 0x199 is unchanged from the default of 12 when I expect it to be 25.

Default values

These are values after standard boot into Windows (from RWEverything)

Standard boot

Results after calling program

Program was called from EFI shell before calling Windows boot loader bootmgfw.efi

0x1FC is updated, 0x199 is not.

Boot after calling program

Updating 0x199 with RWEverything from within Windows changes the multiplier correctly so I'm fairly sure it is the correct register.

As this is my first EFI (or C) program I may have overlooked something trivial.


Solution

  • You have to create a loop and change processor affinity each time through the loop. Then you do a wrmsr for each thread (CPU1, CPU2, CPU3, CPU4) each time through the loop. In Windows you use this function.

    https://learn.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-setthreadaffinitymask

    As soon as you boot up, Windows changes the values in MSR 0x199 so seeing what values are in MSR 0x199 after you boot up does not prove anything.

    To simplify things, you can do this in SetMultiplier,

    low_bits = 0x1900