arch-x86: Move CPUID values to python
CPUID values for X86 are currently hard-coded in the C++ source file. This makes it difficult to configure the bits if needed. Move these to python instead. This will provide a few benefits: 1. We can enable features for certain configurations, for example AVX can be enabled when the KVM CPU is used, but otherwise should not be enabled as gem5 does not have full AVX support. 2. We can more accurately communicate things like cache/TLB sizes based on the actual gem5 configuration. The CPUID values are can be used by some libraries, e.g., MPI, to query system topology. 3. Enabling some bits breaks things in certain configurations and this can be prevented by configuring in python. For example, enabling AVX seems to currently be breaking SMP, meaning gem5 can only boot one CPU in that configuration. Change-Id: Ib3866f39c86d61374b9451e60b119a3155575884
This commit is contained in:
@@ -54,3 +54,59 @@ class X86ISA(BaseISA):
|
||||
vendor_string = Param.String(
|
||||
"HygonGenuine", "Vendor string for CPUID instruction"
|
||||
)
|
||||
name_string = Param.String(
|
||||
"Fake gem5 x86_64 CPU", "Processor name for CPUID instruction"
|
||||
)
|
||||
|
||||
# For the functions that return numerical values we use a vector of ints.
|
||||
# The order of the values is: EAX, EBX, EDX, ECX.
|
||||
#
|
||||
# If the CPU function can take an index, the index value is used as an
|
||||
# offset into the vector and four numerical values are added for each
|
||||
# possible index value. For example, if the function accepts 3 index
|
||||
# values, there are 12 total ints in the vector param. In addition, the
|
||||
# last values for functions which take an index must be all zeros. All
|
||||
# zeros indicates to the KVM cpu / OS that there are no more index values
|
||||
# to iterate over.
|
||||
#
|
||||
# A good resource for these values can be found here:
|
||||
# https://sandpile.org/x86/cpuid.htm
|
||||
# 0000_0001h
|
||||
FamilyModelStepping = VectorParam.UInt32(
|
||||
[0x00020F51, 0x00000805, 0xEFDBFBFF, 0x00000209],
|
||||
"type/family/model/stepping and feature flags",
|
||||
)
|
||||
# 0000_0004h
|
||||
CacheParams = VectorParam.UInt32(
|
||||
[0x00000000, 0x00000000, 0x00000000, 0x00000000],
|
||||
"cache configuration descriptors",
|
||||
)
|
||||
# 0000_0007h
|
||||
ExtendedFeatures = VectorParam.UInt32(
|
||||
[0x00000000, 0x01800000, 0x00000000, 0x00000000], "feature flags"
|
||||
)
|
||||
# 8000_0001h
|
||||
FamilyModelSteppingBrandFeatures = VectorParam.UInt32(
|
||||
[0x00020F51, 0x00000405, 0xEBD3FBFF, 0x00020001],
|
||||
"family/model/stepping and features flags",
|
||||
)
|
||||
# 8000_0005h
|
||||
L1CacheAndTLB = VectorParam.UInt32(
|
||||
[0xFF08FF08, 0xFF20FF20, 0x40020140, 0x40020140],
|
||||
"L1 cache and L1 TLB configuration descriptors",
|
||||
)
|
||||
# 8000_0006h
|
||||
L2L3CacheAndL2TLB = VectorParam.UInt32(
|
||||
[0x00000000, 0x42004200, 0x00000000, 0x04008140],
|
||||
"L2/L3 cache and L2 TLB configuration descriptors",
|
||||
)
|
||||
# 8000_0007h
|
||||
APMInfo = VectorParam.UInt32(
|
||||
[0x80000018, 0x68747541, 0x69746E65, 0x444D4163],
|
||||
"processor feedback capabilities",
|
||||
)
|
||||
# 8000_0008h
|
||||
LongModeAddressSize = VectorParam.UInt32(
|
||||
[0x00003030, 0x00000000, 0x00000000, 0x00000000],
|
||||
"miscellaneous information",
|
||||
)
|
||||
|
||||
@@ -31,162 +31,105 @@
|
||||
#include "arch/x86/isa.hh"
|
||||
#include "base/bitfield.hh"
|
||||
#include "cpu/thread_context.hh"
|
||||
#include "debug/X86.hh"
|
||||
|
||||
namespace gem5
|
||||
{
|
||||
|
||||
namespace X86ISA {
|
||||
enum StandardCpuidFunction
|
||||
{
|
||||
VendorAndLargestStdFunc,
|
||||
FamilyModelStepping,
|
||||
CacheAndTLB,
|
||||
SerialNumber,
|
||||
CacheParams,
|
||||
MonitorMwait,
|
||||
ThermalPowerMgmt,
|
||||
ExtendedFeatures,
|
||||
NumStandardCpuidFuncs
|
||||
};
|
||||
namespace X86ISA
|
||||
{
|
||||
|
||||
enum ExtendedCpuidFunctions
|
||||
{
|
||||
VendorAndLargestExtFunc,
|
||||
FamilyModelSteppingBrandFeatures,
|
||||
NameString1,
|
||||
NameString2,
|
||||
NameString3,
|
||||
L1CacheAndTLB,
|
||||
L2L3CacheAndL2TLB,
|
||||
APMInfo,
|
||||
LongModeAddressSize,
|
||||
X86CPUID::X86CPUID(const std::string& vendor, const std::string& name)
|
||||
: vendorString(vendor), nameString(name)
|
||||
{
|
||||
fatal_if(vendorString.size() != 12,
|
||||
"CPUID vendor string must be 12 characters\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* The following are defined by the spec but not yet implemented
|
||||
*/
|
||||
/* // Function 9 is reserved
|
||||
SVMInfo = 10,
|
||||
// Functions 11-24 are reserved
|
||||
TLB1GBPageInfo = 25,
|
||||
PerformanceInfo,*/
|
||||
void
|
||||
X86CPUID::addStandardFunc(uint32_t func, std::vector<uint32_t> values)
|
||||
{
|
||||
capabilities[func] = values;
|
||||
}
|
||||
|
||||
NumExtendedCpuidFuncs
|
||||
};
|
||||
void
|
||||
X86CPUID::addExtendedFunc(uint32_t func, std::vector<uint32_t> values)
|
||||
{
|
||||
// Extended functions begin with 8000_0000h, but the enum is based from
|
||||
// zero, so we need to add that to the function value.
|
||||
capabilities[func | 0x80000000] = values;
|
||||
}
|
||||
|
||||
static const int nameStringSize = 48;
|
||||
static const char nameString[nameStringSize] = "Fake M5 x86_64 CPU";
|
||||
bool
|
||||
X86CPUID::doCpuid(ThreadContext * tc, uint32_t function, uint32_t index,
|
||||
CpuidResult &result)
|
||||
{
|
||||
constexpr uint32_t ext = 0x80000000;
|
||||
|
||||
uint64_t
|
||||
stringToRegister(const char *str)
|
||||
{
|
||||
uint64_t reg = 0;
|
||||
for (int pos = 3; pos >=0; pos--) {
|
||||
reg <<= 8;
|
||||
reg |= str[pos];
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
DPRINTF(X86, "Calling CPUID function %x with index %d\n", function, index);
|
||||
|
||||
bool
|
||||
doCpuid(ThreadContext * tc, uint32_t function,
|
||||
uint32_t index, CpuidResult &result)
|
||||
{
|
||||
uint16_t family = bits(function, 31, 16);
|
||||
uint16_t funcNum = bits(function, 15, 0);
|
||||
if (family == 0x8000) {
|
||||
// The extended functions
|
||||
switch (funcNum) {
|
||||
case VendorAndLargestExtFunc:
|
||||
{
|
||||
ISA *isa = dynamic_cast<ISA *>(tc->getIsaPtr());
|
||||
auto vendor_string = isa->getVendorString();
|
||||
result = CpuidResult(
|
||||
0x80000000 + NumExtendedCpuidFuncs - 1,
|
||||
stringToRegister(vendor_string.c_str()),
|
||||
stringToRegister(vendor_string.c_str() + 4),
|
||||
stringToRegister(vendor_string.c_str() + 8));
|
||||
}
|
||||
break;
|
||||
case FamilyModelSteppingBrandFeatures:
|
||||
result = CpuidResult(0x00020f51, 0x00000405,
|
||||
0xebd3fbff, 0x00020001);
|
||||
break;
|
||||
case NameString1:
|
||||
case NameString2:
|
||||
case NameString3:
|
||||
{
|
||||
// Zero fill anything beyond the end of the string. This
|
||||
// should go away once the string is a vetted parameter.
|
||||
char cleanName[nameStringSize];
|
||||
memset(cleanName, '\0', nameStringSize);
|
||||
strncpy(cleanName, nameString, nameStringSize);
|
||||
// Handle the string-related CPUID functions specially
|
||||
if (function == VendorAndLargestStdFunc) {
|
||||
result = CpuidResult(NumStandardCpuidFuncs - 1,
|
||||
stringToRegister(vendorString.c_str()),
|
||||
stringToRegister(vendorString.c_str() + 4),
|
||||
stringToRegister(vendorString.c_str() + 8));
|
||||
|
||||
int offset = (funcNum - NameString1) * 16;
|
||||
assert(nameStringSize >= offset + 16);
|
||||
result = CpuidResult(
|
||||
stringToRegister(cleanName + offset + 0),
|
||||
stringToRegister(cleanName + offset + 4),
|
||||
stringToRegister(cleanName + offset + 12),
|
||||
stringToRegister(cleanName + offset + 8));
|
||||
}
|
||||
break;
|
||||
case L1CacheAndTLB:
|
||||
result = CpuidResult(0xff08ff08, 0xff20ff20,
|
||||
0x40020140, 0x40020140);
|
||||
break;
|
||||
case L2L3CacheAndL2TLB:
|
||||
result = CpuidResult(0x00000000, 0x42004200,
|
||||
0x00000000, 0x04008140);
|
||||
break;
|
||||
case APMInfo:
|
||||
result = CpuidResult(0x80000018, 0x68747541,
|
||||
0x69746e65, 0x444d4163);
|
||||
break;
|
||||
case LongModeAddressSize:
|
||||
result = CpuidResult(0x00003030, 0x00000000,
|
||||
0x00000000, 0x00000000);
|
||||
break;
|
||||
/* case SVMInfo:
|
||||
case TLB1GBPageInfo:
|
||||
case PerformanceInfo:*/
|
||||
default:
|
||||
warn("x86 cpuid family 0x8000: unimplemented function %u",
|
||||
funcNum);
|
||||
return false;
|
||||
}
|
||||
} else if (family == 0x0000) {
|
||||
// The standard functions
|
||||
switch (funcNum) {
|
||||
case VendorAndLargestStdFunc:
|
||||
{
|
||||
ISA *isa = dynamic_cast<ISA *>(tc->getIsaPtr());
|
||||
auto vendor_string = isa->getVendorString();
|
||||
result = CpuidResult(
|
||||
NumStandardCpuidFuncs - 1,
|
||||
stringToRegister(vendor_string.c_str()),
|
||||
stringToRegister(vendor_string.c_str() + 4),
|
||||
stringToRegister(vendor_string.c_str() + 8));
|
||||
}
|
||||
break;
|
||||
case FamilyModelStepping:
|
||||
result = CpuidResult(0x00020f51, 0x00000805,
|
||||
0xefdbfbff, 0x00000209);
|
||||
break;
|
||||
case ExtendedFeatures:
|
||||
result = CpuidResult(0x00000000, 0x01800000,
|
||||
0x00000000, 0x00000000);
|
||||
break;
|
||||
default:
|
||||
warn("x86 cpuid family 0x0000: unimplemented function %u",
|
||||
funcNum);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
warn("x86 cpuid: unknown family %#x", family);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if (function == (ext | VendorAndLargestExtFunc)) {
|
||||
result = CpuidResult(0x80000000 + NumExtendedCpuidFuncs - 1,
|
||||
stringToRegister(vendorString.c_str()),
|
||||
stringToRegister(vendorString.c_str() + 4),
|
||||
stringToRegister(vendorString.c_str() + 8));
|
||||
|
||||
return true;
|
||||
} else if ((function == (ext | NameString1)) ||
|
||||
(function == (ext | NameString2)) ||
|
||||
(function == (ext | NameString3))) {
|
||||
// Zero fill anything beyond the end of the string. This
|
||||
// should go away once the string is a vetted parameter.
|
||||
char cleanName[nameStringSize];
|
||||
memset(cleanName, '\0', nameStringSize);
|
||||
strncpy(cleanName, nameString.c_str(), nameStringSize-1);
|
||||
|
||||
int funcNum = bits(function, 15, 0);
|
||||
int offset = (funcNum - NameString1) * 16;
|
||||
assert(nameStringSize >= offset + 16);
|
||||
result = CpuidResult(
|
||||
stringToRegister(cleanName + offset + 0),
|
||||
stringToRegister(cleanName + offset + 4),
|
||||
stringToRegister(cleanName + offset + 12),
|
||||
stringToRegister(cleanName + offset + 8));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ignore anything not in the map of supported CPUID functions.
|
||||
// This is checked after the string-related functions as those are not
|
||||
// in the capabilities map.
|
||||
if (!capabilities.count(function)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto &cap_vec = capabilities[function];
|
||||
result = CpuidResult(cap_vec[0], cap_vec[1],
|
||||
cap_vec[2], cap_vec[3]);
|
||||
DPRINTF(X86, "CPUID function %x returning (%x, %x, %x, %x)\n",
|
||||
function, result.rax, result.rbx, result.rdx, result.rcx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
X86CPUID::stringToRegister(const char *str)
|
||||
{
|
||||
uint64_t reg = 0;
|
||||
for (int pos = 3; pos >=0; pos--) {
|
||||
reg <<= 8;
|
||||
reg |= str[pos];
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
|
||||
} // namespace X86ISA
|
||||
} // namespace gem5
|
||||
|
||||
@@ -29,7 +29,10 @@
|
||||
#ifndef __ARCH_X86_CPUID_HH__
|
||||
#define __ARCH_X86_CPUID_HH__
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "base/types.hh"
|
||||
#include "params/X86ISA.hh"
|
||||
|
||||
namespace gem5
|
||||
{
|
||||
@@ -38,28 +41,72 @@ class ThreadContext;
|
||||
|
||||
namespace X86ISA
|
||||
{
|
||||
struct CpuidResult
|
||||
{
|
||||
uint64_t rax;
|
||||
uint64_t rbx;
|
||||
uint64_t rcx;
|
||||
uint64_t rdx;
|
||||
|
||||
// These are not in alphebetical order on purpose. The order reflects
|
||||
// how the CPUID orders the registers when it returns results.
|
||||
CpuidResult(uint64_t _rax, uint64_t _rbx,
|
||||
uint64_t _rdx, uint64_t _rcx) :
|
||||
rax(_rax), rbx(_rbx), rcx(_rcx), rdx(_rdx)
|
||||
{}
|
||||
enum StandardCpuidFunction
|
||||
{
|
||||
VendorAndLargestStdFunc,
|
||||
FamilyModelStepping,
|
||||
CacheAndTLB,
|
||||
SerialNumber,
|
||||
CacheParams,
|
||||
MonitorMwait,
|
||||
ThermalPowerMgmt,
|
||||
ExtendedFeatures,
|
||||
NumStandardCpuidFuncs
|
||||
};
|
||||
|
||||
CpuidResult()
|
||||
{}
|
||||
};
|
||||
enum ExtendedCpuidFunctions
|
||||
{
|
||||
VendorAndLargestExtFunc,
|
||||
FamilyModelSteppingBrandFeatures,
|
||||
NameString1,
|
||||
NameString2,
|
||||
NameString3,
|
||||
L1CacheAndTLB,
|
||||
L2L3CacheAndL2TLB,
|
||||
APMInfo,
|
||||
LongModeAddressSize,
|
||||
NumExtendedCpuidFuncs
|
||||
};
|
||||
|
||||
uint64_t stringToRegister(const char *str);
|
||||
constexpr int nameStringSize = 48;
|
||||
|
||||
struct CpuidResult
|
||||
{
|
||||
uint64_t rax;
|
||||
uint64_t rbx;
|
||||
uint64_t rcx;
|
||||
uint64_t rdx;
|
||||
|
||||
// These are not in alphebetical order on purpose. The order reflects
|
||||
// how the CPUID orders the registers when it returns results.
|
||||
CpuidResult(uint64_t _rax, uint64_t _rbx,
|
||||
uint64_t _rdx, uint64_t _rcx) :
|
||||
rax(_rax), rbx(_rbx), rcx(_rcx), rdx(_rdx)
|
||||
{}
|
||||
|
||||
CpuidResult()
|
||||
{}
|
||||
};
|
||||
|
||||
class X86CPUID
|
||||
{
|
||||
public:
|
||||
X86CPUID(const std::string& vendor, const std::string& name);
|
||||
|
||||
void addStandardFunc(uint32_t func, std::vector<uint32_t> values);
|
||||
void addExtendedFunc(uint32_t func, std::vector<uint32_t> values);
|
||||
|
||||
bool doCpuid(ThreadContext * tc, uint32_t function,
|
||||
uint32_t index, CpuidResult &result);
|
||||
uint32_t index, CpuidResult &result);
|
||||
|
||||
private:
|
||||
const std::string vendorString;
|
||||
const std::string nameString;
|
||||
std::unordered_map<uint32_t, std::vector<uint32_t>> capabilities;
|
||||
|
||||
uint64_t stringToRegister(const char *str);
|
||||
};
|
||||
|
||||
} // namespace X86ISA
|
||||
} // namespace gem5
|
||||
|
||||
@@ -151,10 +151,19 @@ RegClass matRegClass(MatRegClass, MatRegClassName, 1, debug::MatRegs);
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
ISA::ISA(const X86ISAParams &p) : BaseISA(p), vendorString(p.vendor_string)
|
||||
ISA::ISA(const X86ISAParams &p)
|
||||
: BaseISA(p), cpuid(new X86CPUID(p.vendor_string, p.name_string))
|
||||
{
|
||||
fatal_if(vendorString.size() != 12,
|
||||
"CPUID vendor string must be 12 characters\n");
|
||||
cpuid->addStandardFunc(FamilyModelStepping, p.FamilyModelStepping);
|
||||
cpuid->addStandardFunc(CacheParams, p.CacheParams);
|
||||
cpuid->addStandardFunc(ExtendedFeatures, p.ExtendedFeatures);
|
||||
|
||||
cpuid->addExtendedFunc(FamilyModelSteppingBrandFeatures,
|
||||
p.FamilyModelSteppingBrandFeatures);
|
||||
cpuid->addExtendedFunc(L1CacheAndTLB, p.L1CacheAndTLB);
|
||||
cpuid->addExtendedFunc(L2L3CacheAndL2TLB, p.L2L3CacheAndL2TLB);
|
||||
cpuid->addExtendedFunc(APMInfo, p.APMInfo);
|
||||
cpuid->addExtendedFunc(LongModeAddressSize, p.LongModeAddressSize);
|
||||
|
||||
_regClasses.push_back(&flatIntRegClass);
|
||||
_regClasses.push_back(&flatFloatRegClass);
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "arch/generic/isa.hh"
|
||||
#include "arch/x86/cpuid.hh"
|
||||
#include "arch/x86/pcstate.hh"
|
||||
#include "arch/x86/regs/ccr.hh"
|
||||
#include "arch/x86/regs/float.hh"
|
||||
@@ -93,6 +94,8 @@ class ISA : public BaseISA
|
||||
void setThreadContext(ThreadContext *_tc) override;
|
||||
|
||||
std::string getVendorString() const;
|
||||
|
||||
std::unique_ptr<X86CPUID> cpuid;
|
||||
};
|
||||
|
||||
} // namespace X86ISA
|
||||
|
||||
@@ -690,8 +690,9 @@
|
||||
}
|
||||
0x2: CPUIDInst::CPUID({{
|
||||
CpuidResult result;
|
||||
bool success = doCpuid(xc->tcBase(), bits(Rax, 31, 0),
|
||||
bits(Rcx, 31, 0), result);
|
||||
ISA *isa = dynamic_cast<ISA *>(xc->tcBase()->getIsaPtr());
|
||||
bool success = isa->cpuid->doCpuid(xc->tcBase(),
|
||||
bits(Rax, 31, 0), bits(Rcx, 31, 0), result);
|
||||
if (success) {
|
||||
Rax = result.rax;
|
||||
Rbx = result.rbx;
|
||||
|
||||
@@ -63,6 +63,7 @@ output header {{
|
||||
#include "arch/x86/insts/microregop.hh"
|
||||
#include "arch/x86/insts/microspecop.hh"
|
||||
#include "arch/x86/insts/static_inst.hh"
|
||||
#include "arch/x86/isa.hh"
|
||||
#include "arch/x86/regs/ccr.hh"
|
||||
#include "arch/x86/regs/int.hh"
|
||||
#include "arch/x86/regs/misc.hh"
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "arch/x86/cpuid.hh"
|
||||
#include "arch/x86/faults.hh"
|
||||
#include "arch/x86/interrupts.hh"
|
||||
#include "arch/x86/isa.hh"
|
||||
#include "arch/x86/regs/float.hh"
|
||||
#include "arch/x86/regs/int.hh"
|
||||
#include "arch/x86/regs/msr.hh"
|
||||
@@ -1443,26 +1444,27 @@ X86KvmCPU::updateCPUID()
|
||||
* currently not a problem since M5 doesn't expose any of them at
|
||||
* the moment.
|
||||
*/
|
||||
X86ISA::ISA *isa = dynamic_cast<X86ISA::ISA *>(tc->getIsaPtr());
|
||||
|
||||
/* Basic features */
|
||||
CpuidResult func0;
|
||||
X86ISA::doCpuid(tc, 0x0, 0, func0);
|
||||
isa->cpuid->doCpuid(tc, 0x0, 0, func0);
|
||||
for (uint32_t function = 0; function <= func0.rax; ++function) {
|
||||
CpuidResult cpuid;
|
||||
uint32_t idx(0);
|
||||
|
||||
X86ISA::doCpuid(tc, function, idx, cpuid);
|
||||
isa->cpuid->doCpuid(tc, function, idx, cpuid);
|
||||
m5_supported.push_back(makeKvmCpuid(function, idx, cpuid));
|
||||
}
|
||||
|
||||
/* Extended features */
|
||||
CpuidResult efunc0;
|
||||
X86ISA::doCpuid(tc, 0x80000000, 0, efunc0);
|
||||
isa->cpuid->doCpuid(tc, 0x80000000, 0, efunc0);
|
||||
for (uint32_t function = 0x80000000; function <= efunc0.rax; ++function) {
|
||||
CpuidResult cpuid;
|
||||
uint32_t idx(0);
|
||||
|
||||
X86ISA::doCpuid(tc, function, idx, cpuid);
|
||||
isa->cpuid->doCpuid(tc, function, idx, cpuid);
|
||||
m5_supported.push_back(makeKvmCpuid(function, idx, cpuid));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user