arch-x86: Support CPUID functions with indexes

Various CPUID functions will return different values depending on the
value of ECX when executing the CPUID instruction. Add support for this
in the X86 KVM CPU. A subsequent patch will add a CPUID function which
requires iterating through multiple ECX values.

Change-Id: Ib44a52be52ea632d5e2cee3fb2ca390b60a7202a
This commit is contained in:
Matthew Poremba
2023-07-21 14:08:18 -05:00
parent 63d98018ea
commit 3946f7ba2c
3 changed files with 80 additions and 14 deletions

View File

@@ -111,9 +111,19 @@ X86CPUID::doCpuid(ThreadContext * tc, uint32_t function, uint32_t index,
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[0], cap_vec[1],
cap_vec[2], cap_vec[3]);
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);
@@ -131,5 +141,13 @@ X86CPUID::stringToRegister(const char *str)
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)
{
return false;
}
} // namespace X86ISA
} // namespace gem5

View File

@@ -99,6 +99,7 @@ class X86CPUID
bool doCpuid(ThreadContext * tc, uint32_t function,
uint32_t index, CpuidResult &result);
bool hasSignificantIndex(uint32_t function);
private:
const std::string vendorString;

View File

@@ -74,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;
@@ -1420,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;
@@ -1438,12 +1445,6 @@ 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 */
@@ -1453,8 +1454,31 @@ X86KvmCPU::updateCPUID()
CpuidResult cpuid;
uint32_t idx(0);
isa->cpuid->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 */
@@ -1464,8 +1488,31 @@ X86KvmCPU::updateCPUID()
CpuidResult cpuid;
uint32_t idx(0);
isa->cpuid->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);