1

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.

lx07
  • 113
  • 6

1 Answers1

1

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

unclewebb
  • 26
  • 1
  • 1
    Thanks I'll investigate the loop as you suggest. When you say Windows changes MSR 0x199 at boot though do you mean that there is no point trying to set it in EFI before booting Windows at all? Presumably it sets it to the 0x0C (multiplier 12) that I see in ThrottleStop based on something... – lx07 May 27 '19 at 15:44
  • Note that UEFI doesn't provide a way to start or use other CPUs, so you'd have to start all the other CPUs yourself, then set their MSRs, then stop all the CPUs you've started. The CPUs start in real mode (not 64-bit long mode) and UEFI's memory model doesn't guarantee any of your application's memory will be accessible in real mode. For "stop all the CPUs you started"; to guarantee reliability you need a safe place to park them (e.g. in case of NMI interrupting a `cli; hlt; jmp` loop) and for UEFI applications all the memory is freed on exit so there's no safe place to park. 1/2 – Brendan May 27 '19 at 20:12
  • Essentially, to make it work you'd need memory allocations and a trampoline that wasn't written in C (e.g. 16-bit real mode assembly); and a UEFI driver (and not an application) to ensure memory isn't freed on exit. Mostly the complexity is orders of magnitude higher than you're hoping. 2/2 – Brendan May 27 '19 at 20:14
  • 2
    @Brendan, there is a UEFI protocol that supports starting one or all APs to run some code (in long mode), after which they return to the BIOS to sleep. It’s called the MP services protocol. It’s optional, but it has been supported in every BIOS where I’ve tried to use it. – prl May 27 '19 at 21:51
  • @prl: No, there was a "draft request for comments" thing floating around about 8 years ago that never made it into the UEFI spec. – Brendan May 28 '19 at 03:37
  • @Brendan, It’s a separate spec, not part of the UEFI spec. – prl May 28 '19 at 08:11
  • @prl - you mean *EnableDisableAP*, *StartupThisAP* etc as described [here](https://uefi.org/sites/default/files/resources/Plugfest_Multiprocessing-with_UEFI-McDaniel.pdf)? – lx07 May 28 '19 at 09:02
  • 1
    Found this on GitHub which loops through processors and applies msr change - it does seem to be the way to do it : [Intel(R) Xeon(R) Processor Max Effort Turbo Boost UEFI DXE driver](https://github.com/freecableguy/v3x4) – lx07 Jun 01 '19 at 09:30