Haswell Power Management Fix

Almost all UEFI BIOS versions lock MSR 0xE2. Not that it matters, because we know exactly what/where to look for. Something that has helped us to patch the BIOS before and we are about to do it again. This blog article however will show you the exact UEFI source code that locks BIT 15 of MSR 0xE2 (MSR_PMG_CST_CONFIG_CONTROL). Let’s start with the PowerManagement.inf

#/*++
#  This file contains an 'Intel Peripheral Driver' and uniquely
#  identified as "Intel Reference Module" and is
#  licensed for Intel CPUs and chipsets under the terms of your
#  license agreement with Intel or your vendor.  This file may
#  be modified by the user, subject to additional terms of the
#  license agreement
#--*/
#
#/*++
#
#Copyright (c)  1999 - 2010 Intel Corporation. All rights reserved
#This software and associated documentation (if any) is furnished
#under a license and may only be used or copied in accordance
#with the terms of the license. Except as permitted by such
#license, no part of this software or documentation may be
#reproduced, stored in a retrieval system, or transmitted in any
#form or by any means without the express written consent of
#Intel Corporation.
#
#Module Name:
#
#  PowerManagement.inf
#
#Abstract:
#
#  Component description file for Power Management module
#
#--*/

[defines]
BASE_NAME            = PowerManagement2
FILE_GUID            = f7731b4c-58a2-4df4-8980-5645d39ece58
COMPONENT_TYPE       = RT_DRIVER

[sources.common]
  PowerManagement.h
  PowerManagement.c

  Ppm.h
  PpmInitialization.c
  PpmRuntime.c

#
# Edk II Glue Driver Entry Point
#
 EdkIIGlueSmmDriverEntryPoint.c

[sources.x64]
  x64\ProcessorSaveStateSupport.h
  x64\ProcessorSaveStateSupport.c

[includes.common]
  .

  $(EDK_SOURCE)\Foundation
  $(EDK_SOURCE)\Foundation\Include
  $(EDK_SOURCE)\Foundation\Efi
  $(EDK_SOURCE)\Foundation\Efi\Include
  $(EDK_SOURCE)\Foundation\Framework
  $(EDK_SOURCE)\Foundation\Framework\Include
  $(EDK_SOURCE)\Foundation\Include\IndustryStandard
  $(EDK_SOURCE)\Foundation\Library\Dxe\Include
  $(EFI_SOURCE)
  $(EFI_SOURCE)\Include
  $(EFI_SOURCE)\Include\IndustryStandard
  $(EFI_SOURCE)\Platform\$(PROJECT_FAMILY)\Common
  $(EFI_SOURCE)\Platform\$(PROJECT_FAMILY)\Common\Include
  $(EFI_SOURCE)\$(PROJECT_PPM_ROOT)
  $(EFI_SOURCE)\$(PROJECT_PPM_ROOT)\Include
  $(EFI_SOURCE)\$(PROJECT_PPM_ROOT)\Library\SandyBridgePpm\Smm
  $(EFI_SOURCE)\$(PROJECT_PCH_ROOT)\Include
  $(EFI_SOURCE)\$(PROJECT_PCH_ROOT)\Include\Library
  $(EFI_SOURCE)\$(PROJECT_PCH_ROOT)

#
# Typically the sample code referenced will be available in the code base already
# So keep this include at the end to defer to the source base definition
# and only use the sample code definition if source base does not include these files.
#
  $(EFI_SOURCE)\$(PROJECT_PPM_ROOT)\SampleCode\Include
  $(EFI_SOURCE)\$(PROJECT_PPM_ROOT)\SampleCode

#
# Edk II Glue Library, some hearder are included by R9 header so have to include
#

 $(EFI_SOURCE)
 $(EFI_SOURCE)\Framework
 $(EDK_SOURCE)\Foundation
 $(EDK_SOURCE)\Foundation\Framework
 $(EDK_SOURCE)\Foundation\Include\IndustryStandard
 $(EDK_SOURCE)\Foundation\Core\Dxe
 $(EDK_SOURCE)\Foundation\Include\Pei
 $(EDK_SOURCE)\Foundation\Library\Dxe\Include
 $(EDK_SOURCE)\Foundation\Library\EdkIIGlueLib\Include

[libraries.common]

  PowerManagementGuidLib
  PowerManagementProtocolLib
  SandyBridgePpmLib
  DxeAslUpdateLib
  EfiProtocolLib
  EdkFrameworkProtocolLib
  EdkIIGlueBaseLib
  EdkIIGlueBaseIoLibIntrinsic
  EdkIIGlueBaseMemoryLib
  EdkIIGlueDxeMemoryAllocationLib
  EdkIIGlueDxeDebugLibReportStatusCode
  EdkIIGlueSmmRuntimeDxeReportStatusCodeLib
  EdkIIGlueUefiBootServicesTableLib
  EdkIIGlueUefiDevicePathLib
  EfiScriptLib
  EdkProtocolLib

[libraries.ia32,libraries.x64]
  CpuIa32Lib

[nmake.common]
  IMAGE_ENTRY_POINT = _ModuleEntryPoint
  DPX_SOURCE        = $(PROCESSOR)\PowerManagement.dxs
#
# Module Entry Point
#
 C_FLAGS = $(C_FLAGS) /D__EDKII_GLUE_MODULE_ENTRY_POINT__=InitializePowerManagement
 C_FLAGS = $(C_FLAGS) /D __EDKII_GLUE_BASE_LIB__ \
                      /D __EDKII_GLUE_BASE_IO_LIB_INTRINSIC__ \
                      /D __EDKII_GLUE_BASE_MEMORY_LIB__ \
                      /D __EDKII_GLUE_DXE_MEMORY_ALLOCATION_LIB__ \
                      /D __EDKII_GLUE_DXE_DEBUG_LIB_REPORT_STATUS_CODE__ \
                      /D __EDKII_GLUE_SMM_RUNTIME_DXE_REPORT_STATUS_CODE_LIB__ \
                      /D __EDKII_GLUE_UEFI_BOOT_SERVICES_TABLE_LIB__ \
                      /D __EDKII_GLUE_UEFI_DEVICE_PATH_LIB__

This gives us the target GUID of the Power Management module aka F7731B4C-58A2-4DF4-8980-5645D39ECE58 which is also used for the filename in the iMac13,N EFI.
For example:

F7731B4C-58A2-4DF4-8980-5645D39ECE58_0_164.ROM
F7731B4C-58A2-4DF4-8980-5645D39ECE58_0_170.ROM

This should help you to locate the PM module. Next up. The actual locking code:

//
//---------------------------------------------------------------------------
//
// Procedure:   DoCstateProgramming
//
// Description: Program C-state registers.
//
// Input:
//  IN VOID *SetupHandle
//
// Output:  VOID
//
//---------------------------------------------------------------------------
//

VOID DoCstateProgramming(IN VOID *SetupHandle)
{
    UINT32 CpuSignature = GetCpuSignature();
    UINT32 CpuSigNoVer = CpuSignature & 0xfffffff0;
    UINT8  CpuSigVer = CpuSignature & 0xf;
    BOOLEAN IsSandyBridge = CpuSigNoVer == SANDY_BRIDGE || CpuSigNoVer == JAKETOWN || CpuSigNoVer == IVY_BRIDGE || CpuSigNoVer == HASWELL;
	UINT8	CstRange;
    UINT8   PkgCstLimitProg;
    UINT32  PmgCstConfCntr;
	UINT32	RegEax, RegEbx, RegEcx, RegEdx;

    CPULib_CpuID(5, &RegEax, &RegEbx, &RegEcx, &RegEdx);

    //Check if C1E is available.
    if ((RegEdx & C1_SUB_STATES_MASK) > 0x10) {  //bits [7:4] are for C1
        //Enable C1E [1]
        ReadWriteMsr(MSR_POWER_CTL, BIT1, (UINT64)-1);   //0x1fc
    }

    if (IsSandyBridge) {
        ReadWriteMsr(MSR_PKGC3_IRTL, 0x50 + (2 << 10) + BIT15, (UINT64)0xffffffffffffe00);
        ReadWriteMsr(MSR_PKGC6_IRTL, 0x68 + (2 << 10) + BIT15, (UINT64)0xffffffffffffe00);
        ReadWriteMsr(MSR_PKGC7_IRTL, 0x6d + (2 < 4 || CpuSigNoVer == JAKETOWN) {
            //Enable C1 and C3 Un-demotion and lock.
            PmgCstConfCntr |= BIT28 + BIT27 + BIT15;
        }
    } else {
        PmgCstConfCntr |= BIT15;   //Lock CFG
    }

    // Program IO-redirect as well as Package C State Limit
    ReadWriteMsr(MSR_PMG_CST_CONFIG_CONTROL, PmgCstConfCntr, (UINT64)-8);

    if(RegEdx & C7_SUB_STATES_MASK)         CstRange = 2;
    else if (RegEdx & C6_SUB_STATES_MASK)   CstRange = 1;
	else CstRange = 0;

	WriteMsr(MSR_PMG_IO_CAPTURE_ADDR, (CstRange << 16) + PM_BASE_ADDRESS + 0x14);	//0xe4 - C State IO Capture Base Address.
}

That is it. Nothing more to see. Problem located and ready to get patched!

Note: Intel® Corporation (legal) representatives are kindly asked to contact me per e-mail for the express written consent to publish this – to prevent DMCA take down notices.

5 thoughts on “Haswell Power Management Fix

  1. Well, l’m not a coder/programmer but I did a lot of reading and research. Tracing the code I “believe” this is the start of check for Cstates./Lock MSR_e2

    405: b9 e2 00 00 00 mov $0xe2,%ecx //move state of MSR_e2 >%ecx
    40a: 48 dec %eax
    40b: 8b 10 mov (%eax),%edx
    40d: 8b 7a 09 mov 0x9(%edx),%edi
    410: 8b df mov %edi,%ebx
    412: 83 e7 0f and $0xf,%edi
    415: 81 e3 f0 0f ff 0f and $0xfff0ff0,%ebx
    41b: f6 42 01 7a testb $0x7a,0x1(%edx) //bits [7:4] are for C1 //Enable C1E [1]
    41f: 75 19 jne 0x43a //checks flags if not Equal or 0x01jump?

    We want this to happen no matter what??

    So my question is: Am I on the right track? If not I’ll look deeper. There are only 2 other places where MSR_e2 is checked. But I believe this to be the JMP that need to be non conditional.

    • Don’t bother looking into this problem. The MSR 0xE2 is under control already. We also have a tool to patch BIOS files. The only problem left to solve is to flash a mod BIOS file, which certain motherboards reject with a security violation error.

  2. Pingback: Setting up MSI Z97i-AC motherboard (for running OS X) | mackonsti

Leave a comment