arch-x86: Move CPUID values to python (#113)
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.
This commit is contained in:
@@ -231,6 +231,42 @@ def makeGpuFSSystem(args):
|
||||
clock=args.ruby_clock, voltage_domain=system.voltage_domain
|
||||
)
|
||||
|
||||
# If we are using KVM cpu, enable AVX. AVX is used in some ROCm libraries
|
||||
# such as rocBLAS which is used in higher level libraries like PyTorch.
|
||||
use_avx = False
|
||||
if ObjectList.is_kvm_cpu(TestCPUClass):
|
||||
# AVX also requires CR4.osxsave to be 1. These must be set together
|
||||
# of KVM will error out.
|
||||
system.workload.enable_osxsave = 1
|
||||
use_avx = True
|
||||
|
||||
# These values are taken from a real CPU and are further explained here:
|
||||
# https://sandpile.org/x86/cpuid.htm#level_0000_000Dh
|
||||
avx_extended_state = [
|
||||
0x00000007,
|
||||
0x00000340,
|
||||
0x00000000,
|
||||
0x00000340,
|
||||
0x0000000F,
|
||||
0x00000340,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000100,
|
||||
0x00000240,
|
||||
0x00000000,
|
||||
0x00000040,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
]
|
||||
|
||||
# This modifies the default value for ECX only (4th in this array).
|
||||
# See: https://sandpile.org/x86/cpuid.htm#level_0000_0001h
|
||||
# Enables AVX, OSXSAVE, XSAVE, POPCNT, SSE4.2, SSE4.1, CMPXCHG16B,
|
||||
# and FMA.
|
||||
avx_cpu_features = [0x00020F51, 0x00000805, 0xEFDBFBFF, 0x1C983209]
|
||||
|
||||
for (i, cpu) in enumerate(system.cpu):
|
||||
# Break once we reach the shader "CPU"
|
||||
if i == args.num_cpus:
|
||||
@@ -247,6 +283,9 @@ def makeGpuFSSystem(args):
|
||||
|
||||
for j in range(len(system.cpu[i].isa)):
|
||||
system.cpu[i].isa[j].vendor_string = "AuthenticAMD"
|
||||
if use_avx:
|
||||
system.cpu[i].isa[j].ExtendedState = avx_extended_state
|
||||
system.cpu[i].isa[j].FamilyModelStepping = avx_cpu_features
|
||||
|
||||
if args.host_parallel:
|
||||
# To get the KVM CPUs to run on different host CPUs, specify a
|
||||
|
||||
@@ -65,6 +65,7 @@ class X86FsWorkload(KernelWorkload):
|
||||
acpi_description_table_pointer = Param.X86ACPIRSDP(
|
||||
X86ACPIRSDP(), "ACPI root description pointer structure"
|
||||
)
|
||||
enable_osxsave = Param.Bool(False, "Enable OSXSAVE in CR4 register")
|
||||
|
||||
|
||||
class X86FsLinux(X86FsWorkload):
|
||||
|
||||
@@ -54,3 +54,73 @@ 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"
|
||||
)
|
||||
# 0000_000Dh - This uses ECX index, so the last entry must be all zeros
|
||||
ExtendedState = VectorParam.UInt32(
|
||||
[
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
],
|
||||
"extended state enumeration",
|
||||
)
|
||||
# 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,135 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
int cap_offset = 0;
|
||||
|
||||
// Ignore index values for functions that do not take index values.
|
||||
if (hasSignificantIndex(function)) {
|
||||
cap_offset = index * 4;
|
||||
}
|
||||
|
||||
// Ensure we have the offset and 4 dwords after it.
|
||||
assert(capabilities[function].size() >= (cap_offset + 4));
|
||||
|
||||
auto &cap_vec = capabilities[function];
|
||||
result = CpuidResult(cap_vec[cap_offset + 0], cap_vec[cap_offset + 1],
|
||||
cap_vec[cap_offset + 2], cap_vec[cap_offset + 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;
|
||||
}
|
||||
|
||||
// Return true if the CPUID function takes ECX index as an input AND
|
||||
// those multiple index values are supported in gem5.
|
||||
bool
|
||||
X86CPUID::hasSignificantIndex(uint32_t function)
|
||||
{
|
||||
uint16_t family = bits(function, 31, 16);
|
||||
uint16_t funcNum = bits(function, 15, 0);
|
||||
|
||||
if (family == 0x0000) {
|
||||
switch (funcNum) {
|
||||
case ExtendedState:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // 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,74 @@ 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,
|
||||
ExtendedState = 0xD,
|
||||
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);
|
||||
bool hasSignificantIndex(uint32_t function);
|
||||
|
||||
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
|
||||
|
||||
@@ -58,7 +58,8 @@ FsWorkload::FsWorkload(const Params &p) : KernelWorkload(p),
|
||||
smbiosTable(p.smbios_table),
|
||||
mpFloatingPointer(p.intel_mp_pointer),
|
||||
mpConfigTable(p.intel_mp_table),
|
||||
rsdp(p.acpi_description_table_pointer)
|
||||
rsdp(p.acpi_description_table_pointer),
|
||||
enable_osxsave(p.enable_osxsave)
|
||||
{}
|
||||
|
||||
void
|
||||
@@ -295,6 +296,7 @@ FsWorkload::initState()
|
||||
CR4 cr4 = tc->readMiscRegNoEffect(misc_reg::Cr4);
|
||||
// Turn on pae.
|
||||
cr4.pae = 1;
|
||||
cr4.osxsave = enable_osxsave;
|
||||
tc->setMiscReg(misc_reg::Cr4, cr4);
|
||||
|
||||
// Point to the page tables.
|
||||
|
||||
@@ -106,6 +106,9 @@ class FsWorkload : public KernelWorkload
|
||||
Addr &fpSize, Addr &tableSize, Addr table=0);
|
||||
|
||||
void writeOutACPITables(Addr begin, Addr &size);
|
||||
|
||||
private:
|
||||
bool enable_osxsave;
|
||||
};
|
||||
|
||||
} // namespace X86ISA
|
||||
|
||||
@@ -151,10 +151,20 @@ 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->addStandardFunc(ExtendedState, p.ExtendedState);
|
||||
|
||||
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"
|
||||
@@ -73,6 +74,13 @@ using namespace X86ISA;
|
||||
// data) is used to indicate that a segment has been accessed.
|
||||
#define SEG_TYPE_BIT_ACCESSED 1
|
||||
|
||||
// Some linux distro s(e.g., RHEL7) define the KVM macros using "BIT" but do
|
||||
// not include where BIT is defined, so define it here in that case.
|
||||
#ifndef BIT
|
||||
#define BIT(nr) (1UL << (nr))
|
||||
#endif
|
||||
|
||||
|
||||
struct GEM5_PACKED FXSave
|
||||
{
|
||||
uint16_t fcw;
|
||||
@@ -1419,12 +1427,12 @@ X86KvmCPU::ioctlRun()
|
||||
|
||||
static struct kvm_cpuid_entry2
|
||||
makeKvmCpuid(uint32_t function, uint32_t index,
|
||||
CpuidResult &result)
|
||||
CpuidResult &result, uint32_t flags = 0)
|
||||
{
|
||||
struct kvm_cpuid_entry2 e;
|
||||
e.function = function;
|
||||
e.index = index;
|
||||
e.flags = 0;
|
||||
e.flags = flags;
|
||||
e.eax = (uint32_t)result.rax;
|
||||
e.ebx = (uint32_t)result.rbx;
|
||||
e.ecx = (uint32_t)result.rcx;
|
||||
@@ -1437,33 +1445,74 @@ void
|
||||
X86KvmCPU::updateCPUID()
|
||||
{
|
||||
Kvm::CPUIDVector m5_supported;
|
||||
|
||||
/* TODO: We currently don't support any of the functions that
|
||||
* iterate through data structures in the CPU using an index. It's
|
||||
* 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);
|
||||
m5_supported.push_back(makeKvmCpuid(function, idx, cpuid));
|
||||
if (!isa->cpuid->hasSignificantIndex(function)) {
|
||||
isa->cpuid->doCpuid(tc, function, idx, cpuid);
|
||||
m5_supported.push_back(makeKvmCpuid(function, idx, cpuid));
|
||||
} else {
|
||||
while (true) {
|
||||
bool rv = isa->cpuid->doCpuid(tc, function, idx, cpuid);
|
||||
assert(rv);
|
||||
|
||||
if (idx &&
|
||||
!cpuid.rax && !cpuid.rbx && !cpuid.rdx && !cpuid.rcx) {
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* For functions in family 0, this flag tells Linux to compare
|
||||
* the index as well as the function number rather than only
|
||||
* the function number. Important: Do NOT set this flag if the
|
||||
* function does not take an index. Doing so will break SMP.
|
||||
*/
|
||||
uint32_t flag = KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
|
||||
m5_supported.push_back(
|
||||
makeKvmCpuid(function, idx, cpuid, flag));
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
m5_supported.push_back(makeKvmCpuid(function, idx, cpuid));
|
||||
if (!isa->cpuid->hasSignificantIndex(function)) {
|
||||
isa->cpuid->doCpuid(tc, function, idx, cpuid);
|
||||
m5_supported.push_back(makeKvmCpuid(function, idx, cpuid));
|
||||
} else {
|
||||
while (true) {
|
||||
bool rv = isa->cpuid->doCpuid(tc, function, idx, cpuid);
|
||||
assert(rv);
|
||||
|
||||
if (idx &&
|
||||
!cpuid.rax && !cpuid.rbx && !cpuid.rdx && !cpuid.rcx) {
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* For functions in family 0, this flag tells Linux to compare
|
||||
* the index as well as the function number rather than only
|
||||
* the function number. Important: Do NOT set this flag if the
|
||||
* function does not take an index. Doing so will break SMP.
|
||||
*/
|
||||
uint32_t flag = KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
|
||||
m5_supported.push_back(
|
||||
makeKvmCpuid(function, idx, cpuid, flag));
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setCPUID(m5_supported);
|
||||
|
||||
Reference in New Issue
Block a user