mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
1632 lines
56 KiB
C
Executable File
1632 lines
56 KiB
C
Executable File
/** @file
|
|
Copyright (C) 2016 - 2017, The HermitCrabs Lab. All rights reserved.
|
|
|
|
All rights reserved.
|
|
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
**/
|
|
|
|
#include <Uefi.h>
|
|
|
|
#include <IndustryStandard/CpuId.h>
|
|
#include <IndustryStandard/GenericIch.h>
|
|
#include <IndustryStandard/Pci.h>
|
|
#include <IndustryStandard/AppleSmBios.h>
|
|
|
|
#include <Protocol/PciIo.h>
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/IoLib.h>
|
|
#include <Library/OcCpuLib.h>
|
|
#include <Library/PciLib.h>
|
|
#include <Library/OcMiscLib.h>
|
|
#include <Library/OcStringLib.h>
|
|
#include <Library/OcTimerLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/UefiLib.h>
|
|
|
|
#include <ProcessorInfo.h>
|
|
#include <Register/Microcode.h>
|
|
#include <Register/Msr.h>
|
|
#include <Register/Intel/Msr/SandyBridgeMsr.h>
|
|
#include <Register/Intel/Msr/NehalemMsr.h>
|
|
|
|
//
|
|
// Tolerance within which we consider two frequency values to be roughly
|
|
// equivalent.
|
|
//
|
|
#define OC_CPU_FREQUENCY_TOLERANCE 50000000ULL // 50 Mhz
|
|
|
|
STATIC
|
|
UINT8
|
|
DetectAppleMajorType (
|
|
IN CONST CHAR8 *BrandString
|
|
)
|
|
{
|
|
CONST CHAR8 *BrandInfix;
|
|
|
|
BrandInfix = AsciiStrStr (BrandString, "Core");
|
|
if (BrandInfix != NULL) {
|
|
while ((*BrandInfix != ' ') && (*BrandInfix != '\0')) {
|
|
++BrandInfix;
|
|
}
|
|
|
|
while (*BrandInfix == ' ') {
|
|
++BrandInfix;
|
|
}
|
|
|
|
if (AsciiStrnCmp (BrandInfix, "i7", L_STR_LEN ("i7")) == 0) {
|
|
return AppleProcessorMajorI7;
|
|
}
|
|
if (AsciiStrnCmp (BrandInfix, "i5", L_STR_LEN ("i5")) == 0) {
|
|
return AppleProcessorMajorI5;
|
|
}
|
|
if (AsciiStrnCmp (BrandInfix, "i3", L_STR_LEN ("i3")) == 0) {
|
|
return AppleProcessorMajorI3;
|
|
}
|
|
if (AsciiStrnCmp (BrandInfix, "i9", L_STR_LEN ("i9")) == 0) {
|
|
return AppleProcessorMajorI9;
|
|
}
|
|
if (AsciiStrnCmp (BrandInfix, "m3", L_STR_LEN ("m3")) == 0) {
|
|
return AppleProcessorMajorM3;
|
|
}
|
|
if (AsciiStrnCmp (BrandInfix, "m5", L_STR_LEN ("m5")) == 0) {
|
|
return AppleProcessorMajorM5;
|
|
}
|
|
if (AsciiStrnCmp (BrandInfix, "m7", L_STR_LEN ("m7")) == 0) {
|
|
return AppleProcessorMajorM7;
|
|
}
|
|
if (AsciiStrnCmp (BrandInfix, "M", L_STR_LEN ("M")) == 0) {
|
|
return AppleProcessorMajorM;
|
|
}
|
|
if (AsciiStrnCmp (BrandInfix, "Duo", L_STR_LEN ("Duo")) == 0) {
|
|
return AppleProcessorMajorCore2;
|
|
}
|
|
if (AsciiStrnCmp (BrandInfix, "Quad", L_STR_LEN ("Quad")) == 0) {
|
|
return AppleProcessorMajorXeonPenryn;
|
|
}
|
|
return AppleProcessorMajorCore;
|
|
}
|
|
|
|
BrandInfix = AsciiStrStr (BrandString, "Xeon");
|
|
if (BrandInfix != NULL) {
|
|
while ((*BrandInfix != ' ') && (*BrandInfix != '\0')) {
|
|
++BrandInfix;
|
|
}
|
|
|
|
while (*BrandInfix == ' ') {
|
|
++BrandInfix;
|
|
}
|
|
|
|
//
|
|
// Support Xeon Scalable chips: Xeon(R) Gold 6136 CPU
|
|
//
|
|
if (AsciiStrnCmp (BrandInfix, "Bronze", L_STR_LEN ("Bronze")) == 0 ||
|
|
AsciiStrnCmp (BrandInfix, "Silver", L_STR_LEN ("Silver")) == 0 ||
|
|
AsciiStrnCmp (BrandInfix, "Gold", L_STR_LEN ("Gold")) == 0 ||
|
|
AsciiStrnCmp (BrandInfix, "Platinum", L_STR_LEN ("Platinum")) == 0) {
|
|
// Treat Xeon Scalable chips as their closest relatives, Xeon W
|
|
return AppleProcessorMajorXeonW;
|
|
}
|
|
|
|
//
|
|
// Support both variants: Xeon(R) E5-1234 and Xeon(R) CPU E5-1234
|
|
//
|
|
if (AsciiStrnCmp (BrandInfix, "CPU", L_STR_LEN ("CPU")) == 0) {
|
|
BrandInfix += L_STR_LEN ("CPU");
|
|
while (*BrandInfix == ' ') {
|
|
++BrandInfix;
|
|
}
|
|
}
|
|
|
|
if (AsciiStrnCmp (BrandInfix, "E5", L_STR_LEN ("E5")) == 0) {
|
|
return AppleProcessorMajorXeonE5;
|
|
}
|
|
if (AsciiStrnCmp (BrandInfix, "W", L_STR_LEN ("W")) == 0) {
|
|
return AppleProcessorMajorXeonW;
|
|
}
|
|
return AppleProcessorMajorXeonNehalem;
|
|
}
|
|
|
|
return AppleProcessorMajorUnknown;
|
|
}
|
|
|
|
STATIC
|
|
UINT16
|
|
DetectAppleProcessorType (
|
|
IN UINT8 Model,
|
|
IN UINT8 Stepping,
|
|
IN UINT8 AppleMajorType
|
|
)
|
|
{
|
|
switch (Model) {
|
|
//
|
|
// Yonah: https://en.wikipedia.org/wiki/Yonah_(microprocessor)#Models_and_brand_names
|
|
//
|
|
// Used by Apple:
|
|
// Core Duo, Core Solo
|
|
//
|
|
// NOT used by Apple:
|
|
// Pentium, Celeron
|
|
//
|
|
// All 0x0201.
|
|
//
|
|
case CPU_MODEL_DOTHAN: // 0x0D
|
|
case CPU_MODEL_YONAH: // 0x0E
|
|
// IM41 (T2400/T2500), MM11 (Solo T1200 / Duo T2300/T2400),
|
|
// MBP11 (L2400/T2400/T2500/T2600), MBP12 (T2600),
|
|
// MB11 (T2400/T2500)
|
|
return AppleProcessorTypeCoreSolo; // 0x0201
|
|
|
|
//
|
|
// Merom: https://en.wikipedia.org/wiki/Merom_(microprocessor)#Variants
|
|
// Penryn: https://en.wikipedia.org/wiki/Penryn_(microprocessor)#Variants
|
|
//
|
|
// Used by Apple:
|
|
// Core 2 Extreme, Core 2 Duo (Merom),
|
|
// Core 2 Duo, (Penryn),
|
|
// certain Clovertown (Merom) / Harpertown (Penryn) based models
|
|
//
|
|
// Not used by Apple:
|
|
// Merom: Core 2 Solo, Pentium, Celeron M, Celeron
|
|
// Penryn: Core 2 Extreme, Core 2 Quad, Core 2 Solo, Pentium, Celeron
|
|
//
|
|
case CPU_MODEL_MEROM: // 0x0F
|
|
case CPU_MODEL_PENRYN: // 0x17
|
|
if (AppleMajorType == AppleProcessorMajorCore2) {
|
|
// TODO: add check for models above. (by changing the following "if (0)")
|
|
if (0) {
|
|
// ONLY MBA31 (SU9400/SU9600) and MBA32 (SL9400/SL9600)
|
|
return AppleProcessorTypeCore2DuoType2; // 0x0302
|
|
}
|
|
// IM51 (T7200), IM61 (T7400), IM71 (T7300), IM81 (E8435), IM101 (E7600),
|
|
// MM21 (unknown), MM31 (P7350),
|
|
// MBP21 (T7600), MBP22 (unknown), MBP31 (T7700), MBP41 (T8300), MBP71 (P8600),
|
|
// MBP51 (P8600), MBP52 (T9600), MBP53 (P8800), MBP54 (P8700), MBP55 (P7550),
|
|
// MBA11 (P7500), MBA21 (SL9600),
|
|
// MB21 (unknown), MB31 (T7500), MB41 (T8300), MB51 (P8600), MB52 (P7450), MB61 (P7550), MB71 (P8600)
|
|
return AppleProcessorTypeCore2DuoType1; // 0x0301
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorXeonPenryn) {
|
|
// MP21 (2x X5365), MP31 (2x E5462) - 0x0402
|
|
// FIXME: check when 0x0401 will be used.
|
|
return AppleProcessorTypeXeonPenrynType2; // 0x0402
|
|
}
|
|
// here stands for models not used by Apple (Merom/Penryn), putting 0x0301 as lowest
|
|
return AppleProcessorTypeCore2DuoType1; // 0x0301
|
|
|
|
//
|
|
// Nehalem: https://en.wikipedia.org/wiki/Nehalem_(microarchitecture)#Server_and_desktop_processors
|
|
// Westmere: https://en.wikipedia.org/wiki/Westmere_(microarchitecture)#Server_/_Desktop_processors
|
|
//
|
|
// Used by Apple:
|
|
// Gainestown (Xeon), Bloomfield (Xeon), Lynnfield (i5/i7) [Nehalem]
|
|
// Gulftown (aka Westmere-EP, Xeon), Clarkdale (i3/i5), Arrandale (i5/i7) [Westmere]
|
|
//
|
|
// Not used by Apple:
|
|
// Beckton (Xeon), Jasper Forest (Xeon), Clarksfield (i7), Pentium, Celeron [Nehalem]
|
|
// Westmere-EX (Xeon E7), Pentium, Celeron [Westmere]
|
|
//
|
|
case CPU_MODEL_NEHALEM: // 0x1A
|
|
case CPU_MODEL_NEHALEM_EX: // 0x2E, not used by Apple
|
|
case CPU_MODEL_FIELDS: // 0x1E, Lynnfield, Clarksfield (part of Nehalem)
|
|
case CPU_MODEL_WESTMERE: // 0x2C
|
|
case CPU_MODEL_WESTMERE_EX: // 0x2F, not used by Apple
|
|
case CPU_MODEL_DALES_32NM: // 0x25, Clarkdale, Arrandale (part of Westmere)
|
|
if (AppleMajorType == AppleProcessorMajorXeonNehalem) {
|
|
// MP41 & Xserve31 (2x E5520, CPU_MODEL_NEHALEM), MP51 (2x X5670, CPU_MODEL_WESTMERE)
|
|
return AppleProcessorTypeXeon; // 0x0501
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI3) {
|
|
// IM112 (i3-540, 0x0901, CPU_MODEL_DALES_32NM)
|
|
return AppleProcessorTypeCorei3Type1; // 0x0901
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI5) {
|
|
// FIXME: no idea what it is on IM112 (i5-680)
|
|
// MBP61, i5-640M, 0x0602, CPU_MODEL_DALES_32NM
|
|
return AppleProcessorTypeCorei5Type2; // 0x0602
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI7) {
|
|
// FIXME: used by Apple, no idea what to use, assuming 0x0702 for now (based off 0x0602 on i5)
|
|
return AppleProcessorTypeCorei7Type2; // 0x0702
|
|
}
|
|
// here stands for Pentium and Celeron (Nehalem/Westmere), not used by Apple at all.
|
|
// putting 0x0901 (i3) as lowest
|
|
return AppleProcessorTypeCorei3Type1; // 0x0901
|
|
|
|
//
|
|
// Sandy Bridge: https://en.wikipedia.org/wiki/Sandy_Bridge#List_of_Sandy_Bridge_processors
|
|
// Sandy Bridge-E: https://en.wikipedia.org/wiki/Sandy_Bridge-E#Overview
|
|
//
|
|
// Used by Apple:
|
|
// Core i5/i7 / i3 (see NOTE below)
|
|
//
|
|
// NOTE: There seems to be one more i3-2100 used on IM121 (EDU),
|
|
// assuming it exists for now.
|
|
//
|
|
// Not used by Apple:
|
|
// Xeon v1 (E5/E3),
|
|
// SNB-E based Core i7 (and Extreme): 3970X, 3960X, 3930K, 3820,
|
|
// Pentium, Celeron
|
|
//
|
|
case CPU_MODEL_SANDYBRIDGE: // 0x2A
|
|
case CPU_MODEL_JAKETOWN: // 0x2D, SNB-E, not used by Apple
|
|
if (AppleMajorType == AppleProcessorMajorI3) {
|
|
// FIXME: used by Apple on iMac12,1 (EDU, i3-2100), not confirmed yet
|
|
return AppleProcessorTypeCorei3Type3; // 0x0903
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI5) {
|
|
// NOTE: two values are used here. (0x0602 and 0x0603)
|
|
// TODO: how to classify them. (by changing "if (0)")
|
|
if (0) {
|
|
// MM51 (i5-2415M), MM52 (i5-2520M), MBA41 (i5-2467M), MBA42 (i5-2557M)
|
|
return AppleProcessorTypeCorei5Type2; // 0x0602
|
|
}
|
|
// IM121 (i5-2400S), MBP81 (i5-2415M)
|
|
return AppleProcessorTypeCorei5Type3; // 0x0603
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI7) {
|
|
// IM122 (i7-2600), MBP82 (i7-2675QM), MBP83 (i7-2820QM)
|
|
//
|
|
// FIXME: will those i7 not used by Apple (see above), be identified as AppleProcessorMajorI7?
|
|
return AppleProcessorTypeCorei7Type3; // 0x0703
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorXeonE5) { // see TODO below
|
|
// for Sandy Xeon E5, not used by Apple
|
|
// FIXME: is AppleProcessorMajorXeonE5, which seems to be for IVY-E only, compatible with SNB-E too?
|
|
// TODO: write some decent code to check SNB-E based Xeon E5.
|
|
return AppleProcessorTypeXeonE5; // 0x0A01
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorXeonNehalem) { // see TODO below
|
|
// for Sandy Xeon E3
|
|
// NOTE: Xeon E3 is not used by Apple at all and should be somehow treated as i7,
|
|
// but here we'd like to show Xeon in "About This Mac".
|
|
// TODO: CPU major type check for SNB based Xeon E3
|
|
return AppleProcessorTypeXeon; // 0x0501
|
|
}
|
|
// here stands for Pentium and Celeron (Sandy), not used by Apple at all.
|
|
// putting 0x0903 (i3) as lowest
|
|
return AppleProcessorTypeCorei3Type3; // 0x0903
|
|
|
|
//
|
|
// Ivy Bridge: https://en.wikipedia.org/wiki/Ivy_Bridge_(microarchitecture)#List_of_Ivy_Bridge_processors
|
|
// Ivy Bridge-E: https://en.wikipedia.org/wiki/Ivy_Bridge_(microarchitecture)#Models_and_steppings_2
|
|
//
|
|
// Used by Apple:
|
|
// Core i5/i7 / i3 (see NOTE below),
|
|
// Xeon E5 v2
|
|
//
|
|
// NOTE: There seems to be an iMac13,1 (EDU version), or rather iMac13,3, with CPU i3-3225,
|
|
// assuming it exists for now.
|
|
//
|
|
// Not used by Apple:
|
|
// Xeon v2 (E7/E3),
|
|
// IVY-E based Core i7 (and Extreme): 4960X, 4930K, 4820K,
|
|
// Pentium, Celeron
|
|
//
|
|
case CPU_MODEL_IVYBRIDGE: // 0x3A
|
|
case CPU_MODEL_IVYBRIDGE_EP: // 0x3E
|
|
if (AppleMajorType == AppleProcessorMajorXeonE5) {
|
|
// MP61 (E5-1620 v2)
|
|
return AppleProcessorTypeXeonE5; // 0x0A01
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI5) {
|
|
// IM131 (i5-3470S), IM132 (i5-3470S),
|
|
// MBP92 (i5-3210M), MBP102 (i5-3210M)
|
|
// MBA51 (i6-3317U), MBA52 (i5-3427U)
|
|
return AppleProcessorTypeCorei5Type4; // 0x0604
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI7) {
|
|
// MM62 (i7-3615QM),
|
|
// MBP91 (i7-3615QM), MBP101 (i7-3820QM)
|
|
//
|
|
// FIXME: will those i7 not used by Apple (see above), be identified as AppleProcessorMajorI7?
|
|
return AppleProcessorTypeCorei7Type4; // 0x0704
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI3) {
|
|
// FIXME: used by Apple (if iMac13,3 were existent, i3-3225), not confirmed yet
|
|
// assuming it exists for now
|
|
return AppleProcessorTypeCorei3Type4; // 0x0904
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorXeonNehalem) { // see TODO below
|
|
// for Ivy/Ivy-E E3/E7, not used by Apple
|
|
// NOTE: Xeon E3/E7 is not used by Apple at all and should be somehow treated as i7,
|
|
// but here we'd like to show Xeon in "About This Mac".
|
|
// TODO: CPU major type check for IVY based Xeon E3/E7
|
|
return AppleProcessorTypeXeon; // 0x0501
|
|
}
|
|
// here stands for Pentium and Celeron (Ivy), not used by Apple at all.
|
|
// putting 0x0904 (i3) as lowest.
|
|
return AppleProcessorTypeCorei3Type4; // 0x0904
|
|
|
|
//
|
|
// Haswell: https://en.wikipedia.org/wiki/Haswell_(microarchitecture)#List_of_Haswell_processors
|
|
// Haswell-E: basically the same page.
|
|
//
|
|
// Used by Apple:
|
|
// Core i5/i7
|
|
//
|
|
// Not used by Apple:
|
|
// Xeon v3 (E7/E5/E3),
|
|
// Core i3,
|
|
// Haswell-E based Core i7 Extreme: 5960X, 5930K, 5820K,
|
|
// Pentium, Celeron
|
|
//
|
|
case CPU_MODEL_HASWELL: // 0x3C
|
|
case CPU_MODEL_HASWELL_EP: // 0x3F
|
|
case CPU_MODEL_HASWELL_ULT: // 0x45
|
|
if (AppleMajorType == AppleProcessorMajorI5) {
|
|
// IM141 (i5-4570R), IM142 (i5-4670), IM151 (i5-4690),
|
|
// MM71 (i5-4260U),
|
|
// MBA62 (i5-4250U)
|
|
return AppleProcessorTypeCorei5Type5; // 0x0605
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI7) {
|
|
// MBP112 (i7-4770HQ), MBP113 (i7-4850HQ)
|
|
//
|
|
// FIXME: will those i7 not used by Apple (see above), be identified as AppleProcessorMajorI7?
|
|
return AppleProcessorTypeCorei7Type5; // 0x0705
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI3) {
|
|
// for i3, not used by Apple, just for showing i3 in "About This Mac".
|
|
return AppleProcessorTypeCorei3Type5; // 0x0905
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorXeonE5) { // see TODO below
|
|
// for Haswell-E Xeon E5, not used by Apple
|
|
// FIXME: is AppleProcessorMajorXeonE5, which seems to be for IVY-E only, compatible with Haswell-E too?
|
|
// TODO: write some decent code to check Haswell-E based Xeon E5.
|
|
return AppleProcessorTypeXeonE5; // 0x0A01
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorXeonNehalem) { // see TODO below
|
|
// for Haswell/Haswell-E E3/E7, not used by Apple
|
|
// NOTE: Xeon E3/E7 is not used by Apple at all and should be somehow treated as i7,
|
|
// but here we'd like to show Xeon in "About This Mac".
|
|
// TODO: CPU major type check for Haswell/Haswell-E based Xeon E3/E7
|
|
return AppleProcessorTypeXeon; // 0x0501
|
|
}
|
|
// here stands for Pentium and Celeron (Haswell), not used by Apple at all.
|
|
// putting 0x0905 (i3) as lowest.
|
|
return AppleProcessorTypeCorei3Type5; // 0x0905
|
|
|
|
//
|
|
// Broadwell: https://en.wikipedia.org/wiki/Broadwell_(microarchitecture)#List_of_Broadwell_processors
|
|
// Broadwell-E: https://en.wikipedia.org/wiki/Broadwell_(microarchitecture)#"Broadwell-E"_HEDT_(14_nm)
|
|
//
|
|
// NOTE: support table for BDW-E is missing in XNU, thus a CPUID patch might be needed. (See Clover FakeCPUID)
|
|
//
|
|
// Used by Apple:
|
|
// Core i5/i7, Core M
|
|
//
|
|
// Not used by Apple:
|
|
// Broadwell-E: i7 6950X/6900K/6850K/6800K,
|
|
// Xeon v4 (E5/E3),
|
|
// Core i3,
|
|
// Pentium, Celeron
|
|
//
|
|
case CPU_MODEL_BROADWELL: // 0x3D
|
|
case CPU_MODEL_CRYSTALWELL: // 0x46
|
|
case CPU_MODEL_BRYSTALWELL: // 0x47
|
|
if (AppleMajorType == AppleProcessorMajorM) {
|
|
// MB81 (M 5Y51)
|
|
return AppleProcessorTypeCoreMType6; // 0x0B06
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI5) {
|
|
// IM161 (i5-5250U), IM162 (i5-5675R),
|
|
// MBP121 (i5-5257U),
|
|
// MBA71 (i5-5250U), MBA72 (unknown)
|
|
return AppleProcessorTypeCorei5Type6; // 0x0606
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI7) {
|
|
// FIXME: 0x0706 is just an ideal value for i7, waiting for confirmation
|
|
// FIXME: will those i7 not used by Apple (see above), be identified as AppleProcessorMajorI7?
|
|
return AppleProcessorTypeCorei7Type6; // 0x0706
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI3) {
|
|
// for i3, not used by Apple, just for showing i3 in "About This Mac".
|
|
// FIXME: 0x0906 is just an ideal value for i3, waiting for confirmation
|
|
return AppleProcessorTypeCorei3Type6; // 0x0906
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorXeonE5) { // see TODO below
|
|
// for Broadwell-E Xeon E5, not used by Apple
|
|
// FIXME: is AppleProcessorMajorXeonE5, which seems to be for IVY-E only, compatible with Broadwell-E too?
|
|
// TODO: write some decent code to check Broadwell-E based Xeon E5.
|
|
return AppleProcessorTypeXeonE5; // 0x0A01
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorXeonNehalem) { // see TODO below
|
|
// for Broadwell E3, not used by Apple
|
|
// NOTE: Xeon E3 is not used by Apple at all and should be somehow treated as i7,
|
|
// but here we'd like to show Xeon in "About This Mac".
|
|
// TODO: CPU major type check for Broadwell based Xeon E3
|
|
return AppleProcessorTypeXeon; // 0x0501
|
|
}
|
|
// here stands for Pentium and Celeron (Broadwell), not used by Apple at all.
|
|
// putting 0x0906 (i3) as lowest.
|
|
return AppleProcessorTypeCorei3Type5; // 0x0906
|
|
|
|
//
|
|
// Skylake: https://en.wikipedia.org/wiki/Skylake_(microarchitecture)#List_of_Skylake_processor_models
|
|
//
|
|
// Used by Apple:
|
|
// Xeon W, Core m3, m5, m7, i5, i7
|
|
//
|
|
// Not used by Apple:
|
|
// Core i3,
|
|
// all high-end models (Core i9, i7 Extreme): see https://en.wikipedia.org/wiki/Skylake_(microarchitecture)#High-end_desktop_processors
|
|
// Xeon E3 v5, Xeon Scalable
|
|
// Pentium, Celeron
|
|
//
|
|
case CPU_MODEL_SKYLAKE: // 0x4E
|
|
case CPU_MODEL_SKYLAKE_DT: // 0x5E
|
|
case CPU_MODEL_SKYLAKE_W: // 0x55, also SKL-X and SKL-SP
|
|
if (AppleMajorType == AppleProcessorMajorXeonW) {
|
|
// IMP11 (Xeon W 2140B)
|
|
return AppleProcessorTypeXeonW; // 0x0F01
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorM3) {
|
|
// FIXME: we dont have any m3 (Skylake) dump!
|
|
// using an ideal value (0x0C07), which is used on MB101 (m3-7Y32)
|
|
return AppleProcessorTypeCoreM3Type7; // 0x0C07
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorM5) {
|
|
// MB91 (m5 6Y54)
|
|
return AppleProcessorTypeCoreM5Type7; // 0x0D07
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorM7) {
|
|
// FIXME: we dont have any m7 (Skylake) dump!
|
|
// using an ideal value (0x0E07)
|
|
return AppleProcessorTypeCoreM7Type7; // 0x0E07
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI5) {
|
|
return AppleProcessorTypeCorei5Type5; // 0x0605
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI7) {
|
|
// FIXME: used by Apple, but not sure what to use...
|
|
// 0x0707 is used on MBP133 (i7-6700HQ),
|
|
// 0x0705 is not confirmed, just an ideal one comparing to 0x0605 (AppleProcessorTypeCorei5Type5)
|
|
// using 0x0705 for now
|
|
return AppleProcessorTypeCorei7Type5; // 0x0705
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI3) {
|
|
// for i3, not used by Apple, just for showing i3 in "About This Mac".
|
|
return AppleProcessorTypeCorei3Type5; // 0x0905
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI9) {
|
|
// for i9 (SKL-X), not used by Apple, just for showing i9 in "About This Mac".
|
|
// FIXME: i9 was not introdced in this era but later (MBP151, Coffee Lake),
|
|
// will AppleProcessorMajorI9 work here?
|
|
// NOTE: using a mostly invalid value 0x1005 for now...
|
|
return AppleProcessorTypeCorei9Type5; // 0x1005
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorXeonNehalem) { // see TODO below
|
|
// for Skylake E3 (there's no E5/E7 on Skylake), not used by Apple
|
|
// NOTE: Xeon E3 is not used by Apple at all and should be somehow treated as i7,
|
|
// but here we'd like to show Xeon in "About This Mac".
|
|
// TODO: CPU major type check for Skylake based Xeon E3
|
|
return AppleProcessorTypeXeon; // 0x0501
|
|
}
|
|
// here stands for Pentium and Celeron (Skylake), not used by Apple at all.
|
|
// putting 0x0905 (i3) as lowest.
|
|
return AppleProcessorTypeCorei3Type5; // 0x0905
|
|
|
|
//
|
|
// Kaby Lake: https://en.wikipedia.org/wiki/Kaby_Lake#List_of_7th_generation_Kaby_Lake_processors
|
|
// Coffee Lake: https://en.wikipedia.org/wiki/Coffee_Lake#List_of_8th_generation_Coffee_Lake_processors
|
|
//
|
|
// Used by Apple:
|
|
// Core m3 [Kaby],
|
|
// Core i5/i7 [Kaby/Coffee],
|
|
// Core i9 [Coffee],
|
|
//
|
|
// Not used by Apple:
|
|
// Core i3 [Kaby/Coffee],
|
|
// Xeon E3 v6 [Kaby],
|
|
// Xeon E [Coffee],
|
|
// Pentium, Celeron
|
|
//
|
|
case CPU_MODEL_KABYLAKE: // 0x8E
|
|
case CPU_MODEL_COFFEELAKE: // 0x9E
|
|
if (AppleMajorType == AppleProcessorMajorM3) {
|
|
// MB101 (m3 7Y32)
|
|
return AppleProcessorTypeCoreM3Type7; // 0x0C07
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI5) {
|
|
// Kaby has 0x9 stepping, and Coffee use 0xA / 0xB stepping.
|
|
if (Stepping == 9) {
|
|
// IM181 (i5-7360U), IM182 (i5-7400), IM183 (i5-7600), IM191 (i5-8600) [NOTE 1]
|
|
// MBP141 (i5-7360U), MBP142 (i5-7267U)
|
|
//
|
|
// NOTE 1: IM191 is Coffee and thus 0x0609 will be used, TODO.
|
|
return AppleProcessorTypeCorei5Type5; // 0x0605
|
|
}
|
|
// MM81 (i5-8500B)
|
|
// MBP152 (i5-8259U)
|
|
return AppleProcessorTypeCorei5Type9; // 0x0609
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI7) {
|
|
// Kaby has 0x9 stepping, and Coffee use 0xA / 0xB stepping.
|
|
if (Stepping == 9) {
|
|
// FIXME: used by Apple, but not sure what to use...
|
|
// 0x0709 is used on MBP151 (i7-8850H),
|
|
// 0x0705 is not confirmed, just an ideal one comparing to 0x0605 (AppleProcessorTypeCorei5Type5)
|
|
// using 0x0705 for now
|
|
return AppleProcessorTypeCorei7Type5; // 0x0705
|
|
}
|
|
// MM81 (i7-8700B)
|
|
return AppleProcessorTypeCorei7Type9; // 0x0709
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI9) {
|
|
// FIXME: find a dump from MBP151 with i9-8950HK,
|
|
// for now using an ideal value (0x1009), comparing to 0x0709 (used on MBP151, i7-8850H and MM81, i7-8700B)
|
|
return AppleProcessorTypeCorei9Type9; // 0x1009
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorI3) {
|
|
// FIXME: find a dump from MM71 with i3...
|
|
// for now using an idea value (0x0905)
|
|
return AppleProcessorTypeCorei3Type5; // 0x0905
|
|
}
|
|
if (AppleMajorType == AppleProcessorMajorXeonNehalem) { // see TODO below
|
|
// for Kaby Lake/Coffee Lake E3 (there's no E5/E7 on either), not used by Apple
|
|
// NOTE: Xeon E3 is not used by Apple at all and should be somehow treated as i7,
|
|
// but here we'd like to show Xeon in "About This Mac".
|
|
// TODO: CPU major type check for KBL/CFL based Xeon E3
|
|
return AppleProcessorTypeXeon; // 0x0501
|
|
}
|
|
// here stands for Pentium and Celeron (KBL/CFL), not used by Apple at all.
|
|
// putting 0x0905 (i3) as lowest.
|
|
return AppleProcessorTypeCorei3Type5; // 0x0905
|
|
|
|
default:
|
|
// NOTE: by default it is really unknown, but we fallback
|
|
return AppleProcessorTypeCorei5Type5; // 0x0605
|
|
}
|
|
}
|
|
|
|
UINTN
|
|
OcGetPmTimerAddr (
|
|
OUT CONST CHAR8 **Type OPTIONAL
|
|
)
|
|
{
|
|
UINTN TimerAddr;
|
|
UINT32 CpuVendor;
|
|
|
|
TimerAddr = 0;
|
|
|
|
if (Type != NULL) {
|
|
*Type = "Failure";
|
|
}
|
|
|
|
//
|
|
// Intel timer support.
|
|
// Here we obtain the address of 24-bit or 32-bit PM1_TMR.
|
|
// TODO: I believe that there is little reason to enforce our timer lib to calculate
|
|
// CPU frequency through ACPI PM timer on modern Intel CPUs. Starting from Skylake
|
|
// we have crystal clock, which allows us to get quite reliable values. Perhaps
|
|
// this code should be put to OcCpuLib, and the best available source is to be used.
|
|
//
|
|
if (PciRead16 (PCI_ICH_LPC_ADDRESS (0)) == V_ICH_PCI_VENDOR_ID) {
|
|
//
|
|
// On legacy platforms PM1_TMR can be found in ACPI I/O space.
|
|
// 1. For platforms prior to Intel Skylake (Sunrisepoint PCH) iTCO watchdog
|
|
// resources reside in LPC device (D31:F0).
|
|
// 2. For platforms from Intel Skylake till Intel Kaby Lake inclusive they reside in
|
|
// PMC controller (D31:F2).
|
|
// Checking whether ACPI I/O space is enabled is done via ACPI_CNTL register bit 0.
|
|
//
|
|
// On modern platforms, starting from Intel Coffee Lake, the space is roughly the same,
|
|
// but it is referred to as PMC I/O space, and the addressing is done through BAR2.
|
|
// In addition to that on B360 and friends PMC controller may be just missing.
|
|
//
|
|
if ((PciRead8 (PCI_ICH_LPC_ADDRESS (R_ICH_LPC_ACPI_CNTL)) & B_ICH_LPC_ACPI_CNTL_ACPI_EN) != 0) {
|
|
TimerAddr = (PciRead16 (PCI_ICH_LPC_ADDRESS (R_ICH_LPC_ACPI_BASE)) & B_ICH_LPC_ACPI_BASE_BAR) + R_ACPI_PM1_TMR;
|
|
if (Type != NULL) {
|
|
*Type = "LPC";
|
|
}
|
|
} else if (PciRead16 (PCI_ICH_PMC_ADDRESS (0)) == V_ICH_PCI_VENDOR_ID) {
|
|
if ((PciRead8 (PCI_ICH_PMC_ADDRESS (R_ICH_PMC_ACPI_CNTL)) & B_ICH_PMC_ACPI_CNTL_ACPI_EN) != 0) {
|
|
TimerAddr = (PciRead16 (PCI_ICH_PMC_ADDRESS (R_ICH_PMC_ACPI_BASE)) & B_ICH_PMC_ACPI_BASE_BAR) + R_ACPI_PM1_TMR;
|
|
if (Type != NULL) {
|
|
*Type = "PMC ACPI";
|
|
}
|
|
} else if ((PciRead16 (PCI_ICH_PMC_ADDRESS (R_ICH_PMC_BAR2_BASE)) & B_ICH_PMC_BAR2_BASE_BAR_EN) != 0) {
|
|
TimerAddr = (PciRead16 (PCI_ICH_PMC_ADDRESS (R_ICH_PMC_BAR2_BASE)) & B_ICH_PMC_BAR2_BASE_BAR) + R_ACPI_PM1_TMR;
|
|
if (Type != NULL) {
|
|
*Type = "PMC BAR2";
|
|
}
|
|
} else if (Type != NULL) {
|
|
*Type = "Invalid INTEL PMC";
|
|
}
|
|
} else if (Type != NULL) {
|
|
//
|
|
// This is currently the case for Z390 and B360 boards.
|
|
//
|
|
*Type = "Unknown INTEL";
|
|
}
|
|
}
|
|
|
|
//
|
|
// AMD timer support.
|
|
//
|
|
if (TimerAddr == 0) {
|
|
//
|
|
// In an ideal world I believe we should detect AMD SMBus controller...
|
|
//
|
|
CpuVendor = 0;
|
|
AsmCpuid (CPUID_SIGNATURE, NULL, &CpuVendor, NULL, NULL);
|
|
|
|
if (CpuVendor == CPUID_VENDOR_AMD) {
|
|
TimerAddr = MmioRead32 (
|
|
R_AMD_ACPI_MMIO_BASE + R_AMD_ACPI_MMIO_PMIO_BASE + R_AMD_ACPI_PM_TMR_BLOCK
|
|
);
|
|
if (Type != NULL) {
|
|
*Type = "AMD";
|
|
}
|
|
}
|
|
}
|
|
|
|
return TimerAddr;
|
|
}
|
|
|
|
/**
|
|
Calculate the TSC frequency via PM timer
|
|
|
|
@param[in] Recalculate Do not re-use previously cached information.
|
|
|
|
@retval The calculated TSC frequency.
|
|
**/
|
|
STATIC
|
|
UINT64
|
|
OcCalculateTSCFromPMTimer (
|
|
IN BOOLEAN Recalculate
|
|
)
|
|
{
|
|
//
|
|
// Cache the result to speed up multiple calls. For example, we might need
|
|
// this frequency on module entry to initialise a TimerLib instance, and at
|
|
// a later point in time to gather CPU information.
|
|
//
|
|
STATIC UINT64 TSCFrequency = 0;
|
|
|
|
UINTN TimerAddr;
|
|
UINT64 Tsc0;
|
|
UINT64 Tsc1;
|
|
UINT32 AcpiTick0;
|
|
UINT32 AcpiTick1;
|
|
UINT32 AcpiTicksDelta;
|
|
UINT32 AcpiTicksTarget;
|
|
UINT32 TimerResolution;
|
|
EFI_TPL PrevTpl;
|
|
|
|
if (Recalculate) {
|
|
TSCFrequency = 0;
|
|
}
|
|
|
|
if (TSCFrequency == 0) {
|
|
TimerAddr = OcGetPmTimerAddr (NULL);
|
|
TimerResolution = 10;
|
|
|
|
if (TimerAddr != 0) {
|
|
//
|
|
// Check that timer is advancing (it does not on some virtual machines).
|
|
//
|
|
AcpiTick0 = IoRead32 (TimerAddr);
|
|
gBS->Stall (500);
|
|
AcpiTick1 = IoRead32 (TimerAddr);
|
|
|
|
if (AcpiTick0 != AcpiTick1) {
|
|
//
|
|
// ACPI PM timers are usually of 24-bit length, but there are some less common cases of 32-bit length also.
|
|
// When the maximal number is reached, it overflows.
|
|
// The code below can handle overflow with AcpiTicksTarget of up to 24-bit size,
|
|
// on both available sizes of ACPI PM Timers (24-bit and 32-bit).
|
|
//
|
|
// 357954 clocks of ACPI timer (100ms)
|
|
//
|
|
AcpiTicksTarget = V_ACPI_TMR_FREQUENCY / TimerResolution;
|
|
|
|
//
|
|
// Disable all events to ensure that nobody interrupts us.
|
|
//
|
|
PrevTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
|
|
|
|
AcpiTick0 = IoRead32 (TimerAddr);
|
|
Tsc0 = AsmReadTsc ();
|
|
|
|
do {
|
|
CpuPause ();
|
|
|
|
//
|
|
// Check how many AcpiTicks have passed since we started.
|
|
//
|
|
AcpiTick1 = IoRead32 (TimerAddr);
|
|
|
|
if (AcpiTick0 <= AcpiTick1) {
|
|
//
|
|
// No overflow.
|
|
//
|
|
AcpiTicksDelta = AcpiTick1 - AcpiTick0;
|
|
} else if (AcpiTick0 - AcpiTick1 <= 0x00FFFFFF) {
|
|
//
|
|
// Overflow, 24-bit timer.
|
|
//
|
|
AcpiTicksDelta = 0x00FFFFFF - AcpiTick0 + AcpiTick1;
|
|
} else {
|
|
//
|
|
// Overflow, 32-bit timer.
|
|
//
|
|
AcpiTicksDelta = MAX_UINT32 - AcpiTick0 + AcpiTick1;
|
|
}
|
|
|
|
//
|
|
// Keep checking AcpiTicks until target is reached.
|
|
//
|
|
} while (AcpiTicksDelta < AcpiTicksTarget);
|
|
|
|
Tsc1 = AsmReadTsc ();
|
|
|
|
//
|
|
// On some systems we may end up waiting for notably longer than 100ms,
|
|
// despite disabling all events. Divide by actual time passed as suggested
|
|
// by asava's Clover patch r2668.
|
|
//
|
|
TSCFrequency = DivU64x32 (
|
|
MultU64x32 (Tsc1 - Tsc0, V_ACPI_TMR_FREQUENCY), AcpiTicksDelta
|
|
);
|
|
|
|
//
|
|
// Restore to normal TPL.
|
|
//
|
|
gBS->RestoreTPL (PrevTpl);
|
|
}
|
|
}
|
|
|
|
DEBUG ((DEBUG_VERBOSE, "TscFrequency %lld\n", TSCFrequency));
|
|
}
|
|
|
|
return TSCFrequency;
|
|
}
|
|
|
|
/**
|
|
Calculate the ART frequency and derieve the CPU frequency for Intel CPUs
|
|
|
|
@param[out] CPUFrequency The derieved CPU frequency.
|
|
@param[in] Recalculate Do not re-use previously cached information.
|
|
|
|
@retval The calculated ART frequency.
|
|
**/
|
|
STATIC
|
|
UINT64
|
|
OcCalcluateARTFrequencyIntel (
|
|
OUT UINT64 *CPUFrequency,
|
|
IN BOOLEAN Recalculate
|
|
)
|
|
{
|
|
//
|
|
// Cache the result to speed up multiple calls. For example, we might need
|
|
// this frequency on module entry to initialise a TimerLib instance, and at
|
|
// a later point in time to gather CPU information.
|
|
//
|
|
STATIC UINT64 ARTFrequency = 0;
|
|
STATIC UINT64 CPUFrequencyFromART = 0;
|
|
|
|
UINT32 MaxId;
|
|
UINT32 CpuVendor;
|
|
|
|
UINT32 CpuidDenominatorEax;
|
|
UINT32 CpuidNumeratorEbx;
|
|
UINT32 CpuidARTFrequencyEcx;
|
|
CPUID_PROCESSOR_FREQUENCY_EAX CpuidFrequencyEax;
|
|
UINT64 TscAdjust;
|
|
UINT64 CPUFrequencyFromTSC;
|
|
CPUID_VERSION_INFO_EAX CpuidVerEax;
|
|
UINT8 Model;
|
|
|
|
if (Recalculate) {
|
|
ARTFrequency = 0;
|
|
CPUFrequencyFromART = 0;
|
|
}
|
|
|
|
if (ARTFrequency == 0) {
|
|
//
|
|
// Get vendor CPUID 0x00000000
|
|
//
|
|
AsmCpuid (CPUID_SIGNATURE, &MaxId, &CpuVendor, NULL, NULL);
|
|
//
|
|
// Determine our core crystal clock frequency
|
|
//
|
|
if (CpuVendor == CPUID_VENDOR_INTEL && MaxId >= CPUID_TIME_STAMP_COUNTER) {
|
|
TscAdjust = AsmReadMsr64 (MSR_IA32_TSC_ADJUST);
|
|
DEBUG ((DEBUG_INFO, "OCCPU: TSC Adjust %Lu\n", TscAdjust));
|
|
|
|
AsmCpuid (
|
|
CPUID_TIME_STAMP_COUNTER,
|
|
&CpuidDenominatorEax,
|
|
&CpuidNumeratorEbx,
|
|
&CpuidARTFrequencyEcx,
|
|
NULL
|
|
);
|
|
if (CpuidARTFrequencyEcx > 0) {
|
|
ARTFrequency = CpuidARTFrequencyEcx;
|
|
DEBUG ((DEBUG_INFO, "OCCPU: Queried Core Crystal Clock Frequency %11LuHz\n", ARTFrequency));
|
|
} else {
|
|
AsmCpuid (CPUID_VERSION_INFO, &CpuidVerEax.Uint32, NULL, NULL, NULL);
|
|
Model = (UINT8) CpuidVerEax.Bits.Model | (UINT8) (CpuidVerEax.Bits.ExtendedModelId << 4U);
|
|
//
|
|
// Fall back to identifying ART frequency based on known models
|
|
//
|
|
switch (Model) {
|
|
case CPU_MODEL_SKYLAKE:
|
|
case CPU_MODEL_SKYLAKE_DT:
|
|
case CPU_MODEL_KABYLAKE:
|
|
case CPU_MODEL_KABYLAKE_DT:
|
|
ARTFrequency = CLIENT_ART_CLOCK_SOURCE; // 24 Mhz
|
|
break;
|
|
case CPU_MODEL_DENVERTON:
|
|
ARTFrequency = SERVER_ART_CLOCK_SOURCE; // 25 Mhz
|
|
break;
|
|
case CPU_MODEL_GOLDMONT:
|
|
ARTFrequency = ATOM_ART_CLOCK_SOURCE; // 19.2 Mhz
|
|
break;
|
|
}
|
|
if (ARTFrequency > 0) {
|
|
DEBUG ((DEBUG_INFO, "OCCPU: Known Model Core Crystal Clock Frequency %11LuHz\n", ARTFrequency));
|
|
}
|
|
}
|
|
|
|
if (CpuidDenominatorEax > 0 && CpuidNumeratorEbx > 0) {
|
|
//
|
|
// Some Intel chips don't report their core crystal clock frequency.
|
|
// Calculate it by dividing the TSC frequency by the TSC ratio.
|
|
//
|
|
if (ARTFrequency == 0 && MaxId >= CPUID_PROCESSOR_FREQUENCY) {
|
|
CPUFrequencyFromTSC = OcCalculateTSCFromPMTimer (Recalculate);
|
|
ARTFrequency = MultThenDivU64x64x32(
|
|
CPUFrequencyFromTSC,
|
|
CpuidDenominatorEax,
|
|
CpuidNumeratorEbx,
|
|
NULL
|
|
);
|
|
if (ARTFrequency > 0ULL) {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCCPU: Core Crystal Clock Frequency from TSC %11LuHz = %11LuHz * %u / %u\n",
|
|
ARTFrequency,
|
|
CPUFrequencyFromTSC,
|
|
CpuidDenominatorEax,
|
|
CpuidNumeratorEbx
|
|
));
|
|
//
|
|
// Use the reported CPU frequency rather than deriving it from ARTFrequency
|
|
//
|
|
AsmCpuid (CPUID_PROCESSOR_FREQUENCY, &CpuidFrequencyEax.Uint32, NULL, NULL, NULL);
|
|
CPUFrequencyFromART = MultU64x32 (CpuidFrequencyEax.Bits.ProcessorBaseFrequency, 1000000);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we still can't determine the core crystal clock frequency, assume
|
|
// it's 24 Mhz like most Intel chips to date.
|
|
//
|
|
if (ARTFrequency == 0ULL) {
|
|
ARTFrequency = DEFAULT_ART_CLOCK_SOURCE;
|
|
DEBUG ((DEBUG_INFO, "OCCPU: Fallback Core Crystal Clock Frequency %11LuHz\n", ARTFrequency));
|
|
}
|
|
|
|
ASSERT (ARTFrequency > 0ULL);
|
|
if (CPUFrequencyFromART == 0ULL) {
|
|
CPUFrequencyFromART = MultThenDivU64x64x32 (
|
|
ARTFrequency,
|
|
CpuidNumeratorEbx,
|
|
CpuidDenominatorEax,
|
|
NULL
|
|
);
|
|
}
|
|
ASSERT (CPUFrequencyFromART > 0ULL);
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCCPU: CPUFrequencyFromART %11LuHz %5LuMHz = %Lu * %u / %u\n",
|
|
CPUFrequencyFromART,
|
|
DivU64x32 (CPUFrequencyFromART, 1000000),
|
|
ARTFrequency,
|
|
CpuidNumeratorEbx,
|
|
CpuidDenominatorEax
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
*CPUFrequency = CPUFrequencyFromART;
|
|
return ARTFrequency;
|
|
}
|
|
|
|
UINT64
|
|
OcGetTSCFrequency (
|
|
VOID
|
|
)
|
|
{
|
|
UINT64 CPUFrequency;
|
|
//
|
|
// For Intel platforms (the vendor check is covered by the callee), prefer
|
|
// the CPU Frequency derieved from the ART, as the PM timer might not be
|
|
// available (e.g. 300 series chipsets).
|
|
// TODO: For AMD, the base clock can be determined from P-registers.
|
|
//
|
|
OcCalcluateARTFrequencyIntel (&CPUFrequency, FALSE);
|
|
if (CPUFrequency == 0) {
|
|
CPUFrequency = OcCalculateTSCFromPMTimer (FALSE);
|
|
}
|
|
//
|
|
// For all known models with an invariant TSC, its frequency is equal to the
|
|
// CPU's specified base clock.
|
|
//
|
|
return CPUFrequency;
|
|
}
|
|
|
|
VOID
|
|
ScanIntelProcessor (
|
|
IN OUT OC_CPU_INFO *Cpu
|
|
)
|
|
{
|
|
UINT64 Msr;
|
|
CPUID_CACHE_PARAMS_EAX CpuidCacheEax;
|
|
CPUID_CACHE_PARAMS_EBX CpuidCacheEbx;
|
|
UINT8 AppleMajorType;
|
|
MSR_SANDY_BRIDGE_PKG_CST_CONFIG_CONTROL_REGISTER PkgCstConfigControl;
|
|
MSR_IA32_PERF_STATUS_REGISTER PerfStatus;
|
|
MSR_NEHALEM_PLATFORM_INFO_REGISTER PlatformInfo;
|
|
MSR_NEHALEM_TURBO_RATIO_LIMIT_REGISTER TurboLimit;
|
|
UINT16 CoreCount;
|
|
CONST CHAR8 *TimerSourceType;
|
|
UINTN TimerAddr;
|
|
BOOLEAN Recalculate;
|
|
|
|
AppleMajorType = DetectAppleMajorType (Cpu->BrandString);
|
|
Cpu->AppleProcessorType = DetectAppleProcessorType (Cpu->Model, Cpu->Stepping, AppleMajorType);
|
|
|
|
DEBUG ((DEBUG_INFO, "OCCPU: Detected Apple Processor Type: %02X -> %04X\n", AppleMajorType, Cpu->AppleProcessorType));
|
|
|
|
if ((Cpu->Family != 0x06 || Cpu->Model < 0x0c)
|
|
&& (Cpu->Family != 0x0f || Cpu->Model < 0x03)) {
|
|
return;
|
|
}
|
|
|
|
if (Cpu->Model >= CPU_MODEL_SANDYBRIDGE) {
|
|
PkgCstConfigControl.Uint64 = AsmReadMsr64 (MSR_SANDY_BRIDGE_PKG_CST_CONFIG_CONTROL);
|
|
Cpu->CstConfigLock = PkgCstConfigControl.Bits.CFGLock == 1;
|
|
}
|
|
|
|
//
|
|
// When the CPU is virtualized and cpuid invtsc is enabled, then we already get
|
|
// the information we want outside the function, skip anyway.
|
|
// Things may be different in other hypervisors, but should work with QEMU/VMWare for now.
|
|
//
|
|
if (Cpu->CPUFrequencyFromVMT == 0) {
|
|
//
|
|
// TODO: this may not be accurate on some older processors.
|
|
//
|
|
if (Cpu->Model >= CPU_MODEL_NEHALEM) {
|
|
PerfStatus.Uint64 = AsmReadMsr64 (MSR_IA32_PERF_STATUS);
|
|
Cpu->CurBusRatio = (UINT8) (PerfStatus.Bits.State >> 8U);
|
|
PlatformInfo.Uint64 = AsmReadMsr64 (MSR_NEHALEM_PLATFORM_INFO);
|
|
Cpu->MinBusRatio = (UINT8) PlatformInfo.Bits.MaximumEfficiencyRatio;
|
|
Cpu->MaxBusRatio = (UINT8) PlatformInfo.Bits.MaximumNonTurboRatio;
|
|
} else if (Cpu->Model >= CPU_MODEL_PENRYN) {
|
|
PerfStatus.Uint64 = AsmReadMsr64 (MSR_IA32_PERF_STATUS);
|
|
Cpu->MaxBusRatio = (UINT8) (PerfStatus.Uint64 >> 8U) & 0x1FU;
|
|
//
|
|
// Undocumented values:
|
|
// Non-integer bus ratio for the max-multi.
|
|
// Non-integer bus ratio for the current-multi.
|
|
//
|
|
// MaxBusRatioDiv = (UINT8)(PerfStatus.Uint64 >> 46U) & 0x01U;
|
|
// CurrDiv = (UINT8)(PerfStatus.Uint64 >> 14U) & 0x01U;
|
|
//
|
|
}
|
|
|
|
if (Cpu->Model >= CPU_MODEL_NEHALEM
|
|
&& Cpu->Model != CPU_MODEL_NEHALEM_EX
|
|
&& Cpu->Model != CPU_MODEL_WESTMERE_EX) {
|
|
TurboLimit.Uint64 = AsmReadMsr64 (MSR_NEHALEM_TURBO_RATIO_LIMIT);
|
|
Cpu->TurboBusRatio1 = (UINT8) TurboLimit.Bits.Maximum1C;
|
|
Cpu->TurboBusRatio2 = (UINT8) TurboLimit.Bits.Maximum2C;
|
|
Cpu->TurboBusRatio3 = (UINT8) TurboLimit.Bits.Maximum3C;
|
|
Cpu->TurboBusRatio4 = (UINT8) TurboLimit.Bits.Maximum4C;
|
|
}
|
|
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCCPU: Ratio Min %d Max %d Current %d Turbo %d %d %d %d\n",
|
|
Cpu->MinBusRatio,
|
|
Cpu->MaxBusRatio,
|
|
Cpu->CurBusRatio,
|
|
Cpu->TurboBusRatio1,
|
|
Cpu->TurboBusRatio2,
|
|
Cpu->TurboBusRatio3,
|
|
Cpu->TurboBusRatio4
|
|
));
|
|
|
|
//
|
|
// For logging purposes (the first call to these functions might happen
|
|
// before logging is fully initialised), do not use the cached results in
|
|
// DEBUG builds.
|
|
//
|
|
Recalculate = FALSE;
|
|
|
|
DEBUG_CODE_BEGIN ();
|
|
Recalculate = TRUE;
|
|
DEBUG_CODE_END ();
|
|
|
|
//
|
|
// Calculate the Tsc frequency
|
|
//
|
|
DEBUG_CODE_BEGIN ();
|
|
TimerAddr = OcGetPmTimerAddr (&TimerSourceType);
|
|
DEBUG ((DEBUG_INFO, "OCCPU: Timer address is %Lx from %a\n", (UINT64) TimerAddr, TimerSourceType));
|
|
DEBUG_CODE_END ();
|
|
Cpu->CPUFrequencyFromTSC = OcCalculateTSCFromPMTimer (Recalculate);
|
|
|
|
//
|
|
// Determine our core crystal clock frequency
|
|
//
|
|
Cpu->ARTFrequency = OcCalcluateARTFrequencyIntel (&Cpu->CPUFrequencyFromART, Recalculate);
|
|
|
|
//
|
|
// Calculate CPU frequency based on ART if present, otherwise TSC
|
|
//
|
|
Cpu->CPUFrequency = Cpu->CPUFrequencyFromART > 0 ? Cpu->CPUFrequencyFromART : Cpu->CPUFrequencyFromTSC;
|
|
|
|
//
|
|
// Verify that our two CPU frequency calculations do not differ substantially.
|
|
//
|
|
if (Cpu->CPUFrequencyFromART > 0 && Cpu->CPUFrequencyFromTSC > 0
|
|
&& ABS((INT64) Cpu->CPUFrequencyFromART - (INT64) Cpu->CPUFrequencyFromTSC) > OC_CPU_FREQUENCY_TOLERANCE) {
|
|
DEBUG ((
|
|
DEBUG_WARN,
|
|
"OCCPU: ART based CPU frequency differs substantially from TSC: %11LuHz != %11LuHz\n",
|
|
Cpu->CPUFrequencyFromART,
|
|
Cpu->CPUFrequencyFromTSC
|
|
));
|
|
}
|
|
|
|
//
|
|
// There may be some quirks with virtual CPUs (VMware is fine).
|
|
// Formerly we checked Cpu->MinBusRatio > 0, but we have no MinBusRatio on Penryn.
|
|
//
|
|
if (Cpu->CPUFrequency > 0 && Cpu->MaxBusRatio > Cpu->MinBusRatio) {
|
|
Cpu->FSBFrequency = DivU64x32 (Cpu->CPUFrequency, Cpu->MaxBusRatio);
|
|
} else {
|
|
//
|
|
// TODO: It seems to be possible that CPU frequency == 0 here...
|
|
//
|
|
Cpu->FSBFrequency = 100000000; // 100 Mhz
|
|
}
|
|
}
|
|
//
|
|
// Calculate number of cores
|
|
// If we are under virtualization, then we should get the topology from CPUID the same was as with Penryn.
|
|
//
|
|
if (Cpu->MaxId >= CPUID_CACHE_PARAMS && (Cpu->Model <= CPU_MODEL_PENRYN || Cpu->Hypervisor)) {
|
|
AsmCpuidEx (CPUID_CACHE_PARAMS, 0, &CpuidCacheEax.Uint32, &CpuidCacheEbx.Uint32, NULL, NULL);
|
|
if (CpuidCacheEax.Bits.CacheType != CPUID_CACHE_PARAMS_CACHE_TYPE_NULL) {
|
|
CoreCount = (UINT16)GetPowerOfTwo32 (CpuidCacheEax.Bits.MaximumAddressableIdsForProcessorCores + 1);
|
|
if (CoreCount < CpuidCacheEax.Bits.MaximumAddressableIdsForProcessorCores + 1) {
|
|
CoreCount *= 2;
|
|
}
|
|
Cpu->CoreCount = CoreCount;
|
|
//
|
|
// We should not be blindly relying on Cpu->Features & CPUID_FEATURE_HTT.
|
|
// On Penryn CPUs it is set even without Hyper Threading.
|
|
//
|
|
if (Cpu->ThreadCount < Cpu->CoreCount) {
|
|
Cpu->ThreadCount = Cpu->CoreCount;
|
|
}
|
|
}
|
|
} else if (Cpu->Model == CPU_MODEL_WESTMERE) {
|
|
Msr = AsmReadMsr64 (MSR_CORE_THREAD_COUNT);
|
|
Cpu->CoreCount = (UINT16)BitFieldRead64 (Msr, 16, 19);
|
|
Cpu->ThreadCount = (UINT16)BitFieldRead64 (Msr, 0, 15);
|
|
} else {
|
|
Msr = AsmReadMsr64 (MSR_CORE_THREAD_COUNT);
|
|
Cpu->CoreCount = (UINT16)BitFieldRead64 (Msr, 16, 31);
|
|
Cpu->ThreadCount = (UINT16)BitFieldRead64 (Msr, 0, 15);
|
|
}
|
|
|
|
if (Cpu->CoreCount == 0) {
|
|
Cpu->CoreCount = 1;
|
|
}
|
|
|
|
if (Cpu->ThreadCount == 0) {
|
|
Cpu->ThreadCount = 1;
|
|
}
|
|
|
|
//
|
|
// TODO: handle package count...
|
|
//
|
|
}
|
|
|
|
VOID
|
|
ScanAmdProcessor (
|
|
IN OUT OC_CPU_INFO *Cpu
|
|
)
|
|
{
|
|
UINT32 CpuidEbx;
|
|
UINT32 CpuidEcx;
|
|
UINT64 CofVid;
|
|
UINT8 CoreFrequencyID;
|
|
UINT8 CoreDivisorID;
|
|
UINT8 Divisor;
|
|
BOOLEAN Recalculate;
|
|
|
|
//
|
|
// For logging purposes (the first call to these functions might happen
|
|
// before logging is fully initialised), do not use the cached results in
|
|
// DEBUG builds.
|
|
//
|
|
Recalculate = FALSE;
|
|
|
|
DEBUG_CODE_BEGIN ();
|
|
Recalculate = TRUE;
|
|
DEBUG_CODE_END ();
|
|
|
|
//
|
|
// Faking an Intel Core i5 Processor.
|
|
// This value is purely cosmetic, but it makes sense to fake something
|
|
// that is somewhat representative of the kind of Processor that's actually
|
|
// in the system
|
|
//
|
|
Cpu->AppleProcessorType = AppleProcessorTypeCorei5Type5;
|
|
//
|
|
// get TSC Frequency calculated in OcTimerLib, unless we got it already from virtualization extensions.
|
|
// FIXME(1): This code assumes the CPU operates in P0. Either ensure it does
|
|
// and raise the mode on demand, or adapt the logic to consider
|
|
// both the operating and the nominal frequency, latter for
|
|
// the invariant TSC.
|
|
//
|
|
if (Cpu->CPUFrequencyFromVMT == 0) {
|
|
Cpu->CPUFrequencyFromTSC = OcCalculateTSCFromPMTimer (Recalculate);
|
|
Cpu->CPUFrequency = Cpu->CPUFrequencyFromTSC;
|
|
}
|
|
//
|
|
// Get core and thread count from CPUID
|
|
//
|
|
if (Cpu->MaxExtId >= 0x80000008) {
|
|
AsmCpuid (0x80000008, NULL, NULL, &CpuidEcx, NULL);
|
|
Cpu->ThreadCount = (UINT16) (BitFieldRead32 (CpuidEcx, 0, 7) + 1);
|
|
}
|
|
|
|
if (Cpu->Family == AMD_CPU_FAMILY) {
|
|
Divisor = 0;
|
|
CoreFrequencyID = 0;
|
|
CoreDivisorID = 0;
|
|
|
|
switch (Cpu->ExtFamily) {
|
|
case AMD_CPU_EXT_FAMILY_17H:
|
|
if (Cpu->CPUFrequencyFromVMT == 0) {
|
|
CofVid = AsmReadMsr64 (K10_PSTATE_STATUS);
|
|
CoreFrequencyID = (UINT8)BitFieldRead64 (CofVid, 0, 7);
|
|
CoreDivisorID = (UINT8)BitFieldRead64 (CofVid, 8, 13);
|
|
if (CoreDivisorID > 0ULL) {
|
|
//
|
|
// Sometimes incorrect hypervisor configuration will lead to dividing by zero,
|
|
// but these variables will not be used under hypervisor, so just skip these.
|
|
//
|
|
Cpu->MaxBusRatio = (UINT8) (CoreFrequencyID / CoreDivisorID * 2);
|
|
}
|
|
}
|
|
//
|
|
// Get core count from CPUID
|
|
//
|
|
if (Cpu->MaxExtId >= 0x8000001E) {
|
|
AsmCpuid (0x8000001E, NULL, &CpuidEbx, NULL, NULL);
|
|
Cpu->CoreCount =
|
|
(UINT16) DivU64x32 (
|
|
Cpu->ThreadCount,
|
|
(BitFieldRead32 (CpuidEbx, 8, 15) + 1)
|
|
);
|
|
}
|
|
break;
|
|
case AMD_CPU_EXT_FAMILY_15H:
|
|
case AMD_CPU_EXT_FAMILY_16H:
|
|
if (Cpu->CPUFrequencyFromVMT == 0) {
|
|
// FIXME: Please refer to FIXME(1) for the MSR used here.
|
|
CofVid = AsmReadMsr64 (K10_COFVID_STATUS);
|
|
CoreFrequencyID = (UINT8)BitFieldRead64 (CofVid, 0, 5);
|
|
CoreDivisorID = (UINT8)BitFieldRead64 (CofVid, 6, 8);
|
|
Divisor = 1U << CoreDivisorID;
|
|
//
|
|
// BKDG for AMD Family 15h Models 10h-1Fh Processors (42300 Rev 3.12)
|
|
// Core current operating frequency in MHz. CoreCOF = 100 *
|
|
// (MSRC001_00[6B:64][CpuFid] + 10h) / (2 ^ MSRC001_00[6B:64][CpuDid]).
|
|
//
|
|
if (Divisor > 0ULL) {
|
|
//
|
|
// Sometimes incorrect hypervisor configuration will lead to dividing by zero,
|
|
// but these variables will not be used under hypervisor, so just skip these.
|
|
//
|
|
Cpu->MaxBusRatio = (UINT8)((CoreFrequencyID + 0x10) / Divisor);
|
|
}
|
|
}
|
|
//
|
|
// AMD 15h and 16h CPUs don't support hyperthreading,
|
|
// so the core count is equal to the thread count
|
|
//
|
|
Cpu->CoreCount = Cpu->ThreadCount;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCCPU: FID %u DID %u Divisor %u MaxBR %u\n",
|
|
CoreFrequencyID,
|
|
CoreDivisorID,
|
|
Divisor,
|
|
Cpu->MaxBusRatio
|
|
));
|
|
|
|
//
|
|
// When under virtualization this information is already available to us.
|
|
//
|
|
if (Cpu->CPUFrequencyFromVMT == 0) {
|
|
//
|
|
// Sometimes incorrect hypervisor configuration will lead to dividing by zero.
|
|
//
|
|
if (Cpu->MaxBusRatio == 0) {
|
|
Cpu->FSBFrequency = 100000000; // 100 Mhz like Intel part.
|
|
Cpu->MaxBusRatio = 1; // TODO: Maybe unsafe too, we need more investigation.
|
|
} else {
|
|
Cpu->FSBFrequency = DivU64x32 (Cpu->CPUFrequency, Cpu->MaxBusRatio);
|
|
}
|
|
//
|
|
// CPUPM is not supported on AMD, meaning the current
|
|
// and minimum bus ratio are equal to the maximum bus ratio
|
|
//
|
|
Cpu->CurBusRatio = Cpu->MaxBusRatio;
|
|
Cpu->MinBusRatio = Cpu->MaxBusRatio;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/** Scan the processor and fill the cpu info structure with results
|
|
|
|
@param[in] Cpu A pointer to the cpu info structure to fill with results
|
|
|
|
@retval EFI_SUCCESS The scan was completed successfully.
|
|
**/
|
|
VOID
|
|
OcCpuScanProcessor (
|
|
IN OUT OC_CPU_INFO *Cpu
|
|
)
|
|
{
|
|
UINT32 CpuidEax;
|
|
UINT32 CpuidEbx;
|
|
UINT32 CpuidEcx;
|
|
UINT32 CpuidEdx;
|
|
|
|
ASSERT (Cpu != NULL);
|
|
|
|
ZeroMem (Cpu, sizeof (*Cpu));
|
|
|
|
//
|
|
// Get vendor CPUID 0x00000000
|
|
//
|
|
AsmCpuid (CPUID_SIGNATURE, &CpuidEax, &Cpu->Vendor[0], &Cpu->Vendor[2], &Cpu->Vendor[1]);
|
|
|
|
Cpu->MaxId = CpuidEax;
|
|
|
|
//
|
|
// Get extended CPUID 0x80000000
|
|
//
|
|
AsmCpuid (CPUID_EXTENDED_FUNCTION, &CpuidEax, &CpuidEbx, &CpuidEcx, &CpuidEdx);
|
|
|
|
Cpu->MaxExtId = CpuidEax;
|
|
|
|
//
|
|
// Get brand string CPUID 0x80000002 - 0x80000004
|
|
//
|
|
if (Cpu->MaxExtId >= CPUID_BRAND_STRING3) {
|
|
//
|
|
// The brandstring 48 bytes max, guaranteed NULL terminated.
|
|
//
|
|
UINT32 *BrandString = (UINT32 *) Cpu->BrandString;
|
|
|
|
AsmCpuid (
|
|
CPUID_BRAND_STRING1,
|
|
BrandString,
|
|
(BrandString + 1),
|
|
(BrandString + 2),
|
|
(BrandString + 3)
|
|
);
|
|
|
|
AsmCpuid (
|
|
CPUID_BRAND_STRING2,
|
|
(BrandString + 4),
|
|
(BrandString + 5),
|
|
(BrandString + 6),
|
|
(BrandString + 7)
|
|
);
|
|
|
|
AsmCpuid (
|
|
CPUID_BRAND_STRING3,
|
|
(BrandString + 8),
|
|
(BrandString + 9),
|
|
(BrandString + 10),
|
|
(BrandString + 11)
|
|
);
|
|
}
|
|
|
|
Cpu->PackageCount = 1;
|
|
Cpu->CoreCount = 1;
|
|
Cpu->ThreadCount = 1;
|
|
|
|
//
|
|
// Get processor signature and decode
|
|
//
|
|
if (Cpu->MaxId >= CPUID_VERSION_INFO) {
|
|
//
|
|
// Intel SDM requires us issuing CPUID 1 read during microcode
|
|
// version read, so let's do it here for simplicity.
|
|
//
|
|
|
|
if (Cpu->Vendor[0] == CPUID_VENDOR_INTEL) {
|
|
AsmWriteMsr64 (MSR_IA32_BIOS_SIGN_ID, 0);
|
|
}
|
|
|
|
AsmCpuid (
|
|
CPUID_VERSION_INFO,
|
|
&Cpu->CpuidVerEax.Uint32,
|
|
&Cpu->CpuidVerEbx.Uint32,
|
|
&Cpu->CpuidVerEcx.Uint32,
|
|
&Cpu->CpuidVerEdx.Uint32
|
|
);
|
|
|
|
if (Cpu->Vendor[0] == CPUID_VENDOR_INTEL) {
|
|
Cpu->MicrocodeRevision = AsmReadMsr32 (MSR_IA32_BIOS_SIGN_ID);
|
|
}
|
|
|
|
Cpu->Signature = Cpu->CpuidVerEax.Uint32;
|
|
Cpu->Stepping = (UINT8) Cpu->CpuidVerEax.Bits.SteppingId;
|
|
Cpu->ExtModel = (UINT8) Cpu->CpuidVerEax.Bits.ExtendedModelId;
|
|
Cpu->Model = (UINT8) Cpu->CpuidVerEax.Bits.Model | (UINT8) (Cpu->CpuidVerEax.Bits.ExtendedModelId << 4U);
|
|
Cpu->Family = (UINT8) Cpu->CpuidVerEax.Bits.FamilyId;
|
|
Cpu->Type = (UINT8) Cpu->CpuidVerEax.Bits.ProcessorType;
|
|
Cpu->ExtFamily = (UINT8) Cpu->CpuidVerEax.Bits.ExtendedFamilyId;
|
|
Cpu->Brand = (UINT8) Cpu->CpuidVerEbx.Bits.BrandIndex;
|
|
Cpu->Features = (((UINT64) Cpu->CpuidVerEcx.Uint32) << 32ULL) | Cpu->CpuidVerEdx.Uint32;
|
|
|
|
//
|
|
// TODO: We do not have Hypervisor support in EDK II CPUID structure yet.
|
|
// See https://github.com/acidanthera/audk/pull/2.
|
|
// Get Hypervisor/Virtualization information.
|
|
//
|
|
Cpu->Hypervisor = (BOOLEAN) (Cpu->CpuidVerEcx.Bits.NotUsed > 0);
|
|
DEBUG ((DEBUG_INFO, "OCCPU: Hypervisor: %d\n", Cpu->Hypervisor));
|
|
|
|
if (Cpu->Features & CPUID_FEATURE_HTT) {
|
|
Cpu->ThreadCount = (UINT16) Cpu->CpuidVerEbx.Bits.MaximumAddressableIdsForLogicalProcessors;
|
|
}
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "OCCPU: %a %a\n", "Found", Cpu->BrandString));
|
|
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCCPU: Signature %0X Stepping %0X Model %0X Family %0X Type %0X ExtModel %0X ExtFamily %0X\n",
|
|
Cpu->Signature,
|
|
Cpu->Stepping,
|
|
Cpu->Model,
|
|
Cpu->Family,
|
|
Cpu->Type,
|
|
Cpu->ExtModel,
|
|
Cpu->ExtFamily
|
|
));
|
|
|
|
//
|
|
// If we are under virtualization and cpuid invtsc is enabled, we can just read
|
|
// TSCFrequency and FSBFrequency from VMWare Timing node instead of reading MSR
|
|
// (which hypervisors may not implemented yet), at least in QEMU/VMWare it works.
|
|
// Source:
|
|
// 1. CPUID usage for interaction between Hypervisors and Linux.: https://lwn.net/Articles/301888/
|
|
// 2. [Qemu-devel] [PATCH v2 0/3] x86-kvm: Fix Mac guest timekeeping by exposi: https://lists.gnu.org/archive/html/qemu-devel/2017-01/msg04344.html
|
|
//
|
|
AsmCpuid (0x40000000, &CpuidEax, NULL, NULL, NULL);
|
|
if (Cpu->Hypervisor && (CpuidEax >= 0x40000010)) {
|
|
DEBUG ((DEBUG_INFO, "OCCPU: Cpu under virtualization, try get TSC/FSB frequency from VMWare Timing\n"));
|
|
AsmCpuid (0x40000010, &CpuidEax, &CpuidEbx, NULL, NULL);
|
|
|
|
if (CpuidEax & CpuidEbx) {
|
|
//
|
|
// We get kHZ from node and we should translate it first.
|
|
//
|
|
Cpu->CPUFrequencyFromVMT = CpuidEax * 1000;
|
|
Cpu->FSBFrequency = CpuidEbx * 1000;
|
|
|
|
Cpu->CPUFrequency = Cpu->CPUFrequencyFromVMT;
|
|
//
|
|
// We can caculate Bus Ratio here
|
|
//
|
|
Cpu->MaxBusRatio = (UINT8)DivU64x32 (Cpu->CPUFrequency, (UINT32)Cpu->FSBFrequency);
|
|
//
|
|
// We don't have anything like turbo, so we just assign some variables here
|
|
//
|
|
Cpu->MinBusRatio = Cpu->MaxBusRatio;
|
|
Cpu->CurBusRatio = Cpu->MaxBusRatio;
|
|
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCCPU: VMWare TSC: %11LuHz, %5LuMHz; FSB: %11LuHz, %5LuMHz; BusRatio: %d\n",
|
|
Cpu->CPUFrequency,
|
|
DivU64x32 (Cpu->CPUFrequency, 1000000),
|
|
Cpu->FSBFrequency,
|
|
DivU64x32 (Cpu->FSBFrequency, 1000000),
|
|
Cpu->MaxBusRatio
|
|
));
|
|
}
|
|
}
|
|
|
|
if (Cpu->Vendor[0] == CPUID_VENDOR_INTEL) {
|
|
ScanIntelProcessor (Cpu);
|
|
} else if (Cpu->Vendor[0] == CPUID_VENDOR_AMD) {
|
|
ScanAmdProcessor (Cpu);
|
|
} else {
|
|
DEBUG ((DEBUG_WARN, "Found unsupported CPU vendor: %0X", Cpu->Vendor[0]));
|
|
return;
|
|
}
|
|
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCCPU: CPUFrequencyFromTSC %11LuHz %5LuMHz\n",
|
|
Cpu->CPUFrequencyFromTSC,
|
|
DivU64x32 (Cpu->CPUFrequencyFromTSC, 1000000)
|
|
));
|
|
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCCPU: CPUFrequency %11LuHz %5LuMHz\n",
|
|
Cpu->CPUFrequency,
|
|
DivU64x32 (Cpu->CPUFrequency, 1000000)
|
|
));
|
|
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCCPU: FSBFrequency %11LuHz %5LuMHz\n",
|
|
Cpu->FSBFrequency,
|
|
DivU64x32 (Cpu->FSBFrequency, 1000000)
|
|
));
|
|
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCCPU: Pkg %u Cores %u Threads %u\n",
|
|
Cpu->PackageCount,
|
|
Cpu->CoreCount,
|
|
Cpu->ThreadCount
|
|
));
|
|
}
|
|
|
|
VOID
|
|
OcCpuCorrectFlexRatio (
|
|
IN OC_CPU_INFO *Cpu
|
|
)
|
|
{
|
|
UINT64 Msr;
|
|
UINT64 FlexRatio;
|
|
|
|
if (Cpu->Vendor[0] == CPUID_VENDOR_INTEL
|
|
&& Cpu->Model != CPU_MODEL_GOLDMONT
|
|
&& Cpu->Model != CPU_MODEL_AIRMONT
|
|
&& Cpu->Model != CPU_MODEL_AVOTON) {
|
|
Msr = AsmReadMsr64 (MSR_FLEX_RATIO);
|
|
if (Msr & FLEX_RATIO_EN) {
|
|
FlexRatio = BitFieldRead64 (Msr, 8, 15);
|
|
if (FlexRatio == 0) {
|
|
//
|
|
// Disable Flex Ratio if current value is 0.
|
|
//
|
|
AsmWriteMsr64 (MSR_FLEX_RATIO, Msr & ~((UINT64) FLEX_RATIO_EN));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UINT32
|
|
OcCpuModelToAppleFamily (
|
|
IN CPUID_VERSION_INFO_EAX VersionEax
|
|
)
|
|
{
|
|
UINT8 Model;
|
|
|
|
if (VersionEax.Bits.FamilyId != 6) {
|
|
return CPUFAMILY_UNKNOWN;
|
|
}
|
|
|
|
//
|
|
// This MUST be 1 to 1 with Apple XNU kernel implemenation.
|
|
//
|
|
|
|
Model = (UINT8) VersionEax.Bits.Model | (UINT8) (VersionEax.Bits.ExtendedModelId << 4U);
|
|
|
|
switch (Model) {
|
|
case CPU_MODEL_PENRYN:
|
|
return CPUFAMILY_INTEL_PENRYN;
|
|
case CPU_MODEL_NEHALEM:
|
|
case CPU_MODEL_FIELDS:
|
|
case CPU_MODEL_DALES:
|
|
case CPU_MODEL_NEHALEM_EX:
|
|
return CPUFAMILY_INTEL_NEHALEM;
|
|
case CPU_MODEL_DALES_32NM:
|
|
case CPU_MODEL_WESTMERE:
|
|
case CPU_MODEL_WESTMERE_EX:
|
|
return CPUFAMILY_INTEL_WESTMERE;
|
|
case CPU_MODEL_SANDYBRIDGE:
|
|
case CPU_MODEL_JAKETOWN:
|
|
return CPUFAMILY_INTEL_SANDYBRIDGE;
|
|
case CPU_MODEL_IVYBRIDGE:
|
|
case CPU_MODEL_IVYBRIDGE_EP:
|
|
return CPUFAMILY_INTEL_IVYBRIDGE;
|
|
case CPU_MODEL_HASWELL:
|
|
case CPU_MODEL_HASWELL_EP:
|
|
case CPU_MODEL_HASWELL_ULT:
|
|
case CPU_MODEL_CRYSTALWELL:
|
|
return CPUFAMILY_INTEL_HASWELL;
|
|
case CPU_MODEL_BROADWELL:
|
|
case CPU_MODEL_BRYSTALWELL:
|
|
return CPUFAMILY_INTEL_BROADWELL;
|
|
case CPU_MODEL_SKYLAKE:
|
|
case CPU_MODEL_SKYLAKE_DT:
|
|
case CPU_MODEL_SKYLAKE_W:
|
|
return CPUFAMILY_INTEL_SKYLAKE;
|
|
case CPU_MODEL_KABYLAKE:
|
|
case CPU_MODEL_KABYLAKE_DT:
|
|
return CPUFAMILY_INTEL_KABYLAKE;
|
|
default:
|
|
return CPUFAMILY_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
OcIsSandyOrIvy (
|
|
VOID
|
|
)
|
|
{
|
|
CPU_MICROCODE_PROCESSOR_SIGNATURE Sig;
|
|
BOOLEAN SandyOrIvy;
|
|
UINT32 CpuFamily;
|
|
UINT32 CpuModel;
|
|
|
|
Sig.Uint32 = 0;
|
|
|
|
AsmCpuid (1, &Sig.Uint32, NULL, NULL, NULL);
|
|
|
|
CpuFamily = Sig.Bits.Family;
|
|
if (CpuFamily == 15) {
|
|
CpuFamily += Sig.Bits.ExtendedFamily;
|
|
}
|
|
|
|
CpuModel = Sig.Bits.Model;
|
|
if (CpuFamily == 15 || CpuFamily == 6) {
|
|
CpuModel |= Sig.Bits.ExtendedModel << 4;
|
|
}
|
|
|
|
SandyOrIvy = CpuFamily == 6 && (CpuModel == 0x2A || CpuModel == 0x3A);
|
|
|
|
DEBUG ((
|
|
DEBUG_VERBOSE,
|
|
"OCCPU: Discovered CpuFamily %d CpuModel %d SandyOrIvy %a\n",
|
|
CpuFamily,
|
|
CpuModel,
|
|
SandyOrIvy ? "YES" : "NO"
|
|
));
|
|
|
|
return SandyOrIvy;
|
|
}
|