kvm: x86: Fix segment registers to make them VMX compatible
There are cases when the segment registers in gem5 are not compatible with VMX. This changeset works around all known such issues. Specifically: * The accessed bits in CS, SS, DD, ES, FS, GS are forced to 1. * The busy bit in TR is forced to 1. * The protection level of SS is forced to the same protection level as CS. The difference /seems/ to be caused by a bug in gem5's x86 implementation.
This commit is contained in:
parent
fbc1feb39a
commit
30841926a3
1 changed files with 60 additions and 0 deletions
|
@ -53,6 +53,21 @@ using namespace X86ISA;
|
||||||
#define IO_PCI_CONF_ADDR 0xCF8
|
#define IO_PCI_CONF_ADDR 0xCF8
|
||||||
#define IO_PCI_CONF_DATA_BASE 0xCFC
|
#define IO_PCI_CONF_DATA_BASE 0xCFC
|
||||||
|
|
||||||
|
// Task segment type of an inactive 32-bit or 64-bit task
|
||||||
|
#define SEG_SYS_TYPE_TSS_AVAILABLE 9
|
||||||
|
// Task segment type of an active 32-bit or 64-bit task
|
||||||
|
#define SEG_SYS_TYPE_TSS_BUSY 11
|
||||||
|
|
||||||
|
// Non-conforming accessed code segment
|
||||||
|
#define SEG_CS_TYPE_ACCESSED 9
|
||||||
|
// Non-conforming accessed code segment that can be read
|
||||||
|
#define SEG_CS_TYPE_READ_ACCESSED 11
|
||||||
|
|
||||||
|
// The lowest bit of the type field for normal segments (code and
|
||||||
|
// data) is used to indicate that a segment has been accessed.
|
||||||
|
#define SEG_TYPE_BIT_ACCESSED 1
|
||||||
|
|
||||||
|
|
||||||
#define FOREACH_IREG() \
|
#define FOREACH_IREG() \
|
||||||
do { \
|
do { \
|
||||||
APPLY_IREG(rax, INTREG_RAX); \
|
APPLY_IREG(rax, INTREG_RAX); \
|
||||||
|
@ -635,6 +650,18 @@ setKvmDTableReg(ThreadContext *tc, struct kvm_dtable &kvm_dtable,
|
||||||
kvm_dtable.limit = tc->readMiscRegNoEffect(MISCREG_SEG_LIMIT(index));
|
kvm_dtable.limit = tc->readMiscRegNoEffect(MISCREG_SEG_LIMIT(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
forceSegAccessed(struct kvm_segment &seg)
|
||||||
|
{
|
||||||
|
// Intel's VMX requires that (some) usable segments are flagged as
|
||||||
|
// 'accessed' (i.e., the lowest bit in the segment type is set)
|
||||||
|
// when entering VMX. This wouldn't necessary be the case even if
|
||||||
|
// gem5 did set the access bits correctly, so we force it to one
|
||||||
|
// in that case.
|
||||||
|
if (!seg.unusable)
|
||||||
|
seg.type |= SEG_TYPE_BIT_ACCESSED;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
X86KvmCPU::updateKvmStateSRegs()
|
X86KvmCPU::updateKvmStateSRegs()
|
||||||
{
|
{
|
||||||
|
@ -655,6 +682,38 @@ X86KvmCPU::updateKvmStateSRegs()
|
||||||
// Clear the interrupt bitmap
|
// Clear the interrupt bitmap
|
||||||
memset(&sregs.interrupt_bitmap, 0, sizeof(sregs.interrupt_bitmap));
|
memset(&sregs.interrupt_bitmap, 0, sizeof(sregs.interrupt_bitmap));
|
||||||
|
|
||||||
|
// VMX requires CS, SS, DS, ES, FS, and GS to have the accessed
|
||||||
|
// bit in the type field set.
|
||||||
|
forceSegAccessed(sregs.cs);
|
||||||
|
forceSegAccessed(sregs.ss);
|
||||||
|
forceSegAccessed(sregs.ds);
|
||||||
|
forceSegAccessed(sregs.es);
|
||||||
|
forceSegAccessed(sregs.fs);
|
||||||
|
forceSegAccessed(sregs.gs);
|
||||||
|
|
||||||
|
// There are currently some cases where the active task isn't
|
||||||
|
// marked as busy. This is illegal in VMX, so we force it to busy.
|
||||||
|
if (sregs.tr.type == SEG_SYS_TYPE_TSS_AVAILABLE) {
|
||||||
|
hack("tr.type (%i) is not busy. Forcing the busy bit.\n",
|
||||||
|
sregs.tr.type);
|
||||||
|
sregs.tr.type = SEG_SYS_TYPE_TSS_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VMX requires the DPL of SS and CS to be the same for
|
||||||
|
// non-conforming code segments. It seems like m5 doesn't set the
|
||||||
|
// DPL of SS correctly when taking interrupts, so we need to fix
|
||||||
|
// that here.
|
||||||
|
if ((sregs.cs.type == SEG_CS_TYPE_ACCESSED ||
|
||||||
|
sregs.cs.type == SEG_CS_TYPE_READ_ACCESSED) &&
|
||||||
|
sregs.cs.dpl != sregs.ss.dpl) {
|
||||||
|
|
||||||
|
hack("CS.DPL (%i) != SS.DPL (%i): Forcing SS.DPL to %i\n",
|
||||||
|
sregs.cs.dpl, sregs.ss.dpl, sregs.cs.dpl);
|
||||||
|
sregs.ss.dpl = sregs.cs.dpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do checks after fixing up the state to avoid getting excessive
|
||||||
|
// amounts of warnings.
|
||||||
RFLAGS rflags_nocc(tc->readMiscReg(MISCREG_RFLAGS));
|
RFLAGS rflags_nocc(tc->readMiscReg(MISCREG_RFLAGS));
|
||||||
if (!rflags_nocc.vm) {
|
if (!rflags_nocc.vm) {
|
||||||
// Do segment verification if the CPU isn't entering virtual
|
// Do segment verification if the CPU isn't entering virtual
|
||||||
|
@ -667,6 +726,7 @@ X86KvmCPU::updateKvmStateSRegs()
|
||||||
FOREACH_SEGMENT();
|
FOREACH_SEGMENT();
|
||||||
#undef APPLY_SEGMENT
|
#undef APPLY_SEGMENT
|
||||||
}
|
}
|
||||||
|
|
||||||
setSpecialRegisters(sregs);
|
setSpecialRegisters(sregs);
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
|
|
Loading…
Reference in a new issue