summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Sandberg <andreas@sandberg.pp.se>2013-09-25 12:35:21 +0200
committerAndreas Sandberg <andreas@sandberg.pp.se>2013-09-25 12:35:21 +0200
commite5c319db43751f45b2bcca1d018fc39d4561ef9c (patch)
tree47edc5bdfaf813bda4b03464bb22307fe473afde
parent599b59b38754c764aced7edf553c2dea846d3cd8 (diff)
downloadgem5-e5c319db43751f45b2bcca1d018fc39d4561ef9c.tar.xz
kvm: Add x86 segment register verification to help debugging
-rw-r--r--src/cpu/kvm/x86_cpu.cc162
1 files changed, 162 insertions, 0 deletions
diff --git a/src/cpu/kvm/x86_cpu.cc b/src/cpu/kvm/x86_cpu.cc
index 61acc59b7..9e7571b4e 100644
--- a/src/cpu/kvm/x86_cpu.cc
+++ b/src/cpu/kvm/x86_cpu.cc
@@ -275,6 +275,156 @@ dumpKvm(const struct kvm_vcpu_events &events)
inform("\tFlags: 0x%x\n", events.flags);
}
+static bool
+isCanonicalAddress(uint64_t addr)
+{
+ // x86-64 doesn't currently use the full 64-bit virtual address
+ // space, instead it uses signed 48 bit addresses that are
+ // sign-extended to 64 bits. Such addresses are known as
+ // "canonical".
+ uint64_t upper_half(addr & 0xffff800000000000ULL);
+ return upper_half == 0 || upper_half == 0xffff800000000000;
+}
+
+static void
+checkSeg(const char *name, const int idx, const struct kvm_segment &seg,
+ struct kvm_sregs sregs)
+{
+ // Check the register base
+ switch (idx) {
+ case MISCREG_TSL:
+ case MISCREG_TR:
+ case MISCREG_FS:
+ case MISCREG_GS:
+ if (!isCanonicalAddress(seg.base))
+ warn("Illegal %s base: 0x%x\n", name, seg.base);
+ break;
+
+ case MISCREG_SS:
+ case MISCREG_DS:
+ case MISCREG_ES:
+ if (seg.unusable)
+ break;
+ case MISCREG_CS:
+ if (seg.base & 0xffffffff00000000ULL)
+ warn("Illegal %s base: 0x%x\n", name, seg.base);
+ break;
+ }
+
+ // Check the type
+ switch (idx) {
+ case MISCREG_CS:
+ switch (seg.type) {
+ case 3:
+ if (seg.dpl != 0)
+ warn("CS type is 3 but dpl != 0.\n");
+ break;
+ case 9:
+ case 11:
+ if (seg.dpl != sregs.ss.dpl)
+ warn("CS type is %i but CS DPL != SS DPL\n", seg.type);
+ break;
+ case 13:
+ case 15:
+ if (seg.dpl > sregs.ss.dpl)
+ warn("CS type is %i but CS DPL > SS DPL\n", seg.type);
+ break;
+ default:
+ warn("Illegal CS type: %i\n", seg.type);
+ break;
+ }
+ break;
+
+ case MISCREG_SS:
+ if (seg.unusable)
+ break;
+ switch (seg.type) {
+ case 3:
+ if (sregs.cs.type == 3 && seg.dpl != 0)
+ warn("CS type is 3, but SS DPL is != 0.\n");
+ /* FALLTHROUGH */
+ case 7:
+ if (!(sregs.cr0 & 1) && seg.dpl != 0)
+ warn("SS DPL is %i, but CR0 PE is 0\n", seg.dpl);
+ break;
+ default:
+ warn("Illegal SS type: %i\n", seg.type);
+ break;
+ }
+ break;
+
+ case MISCREG_DS:
+ case MISCREG_ES:
+ case MISCREG_FS:
+ case MISCREG_GS:
+ if (seg.unusable)
+ break;
+ if (!(seg.type & 0x1) ||
+ ((seg.type & 0x8) && !(seg.type & 0x2)))
+ warn("%s has an illegal type field: %i\n", name, seg.type);
+ break;
+
+ case MISCREG_TR:
+ // TODO: We should check the CPU mode
+ if (seg.type != 3 && seg.type != 11)
+ warn("%s: Illegal segment type (%i)\n", name, seg.type);
+ break;
+
+ case MISCREG_TSL:
+ if (seg.unusable)
+ break;
+ if (seg.type != 2)
+ warn("%s: Illegal segment type (%i)\n", name, seg.type);
+ break;
+ }
+
+ switch (idx) {
+ case MISCREG_SS:
+ case MISCREG_DS:
+ case MISCREG_ES:
+ case MISCREG_FS:
+ case MISCREG_GS:
+ if (seg.unusable)
+ break;
+ case MISCREG_CS:
+ if (!seg.s)
+ warn("%s: S flag not set\n", name);
+ break;
+
+ case MISCREG_TSL:
+ if (seg.unusable)
+ break;
+ case MISCREG_TR:
+ if (seg.s)
+ warn("%s: S flag is set\n", name);
+ break;
+ }
+
+ switch (idx) {
+ case MISCREG_SS:
+ case MISCREG_DS:
+ case MISCREG_ES:
+ case MISCREG_FS:
+ case MISCREG_GS:
+ case MISCREG_TSL:
+ if (seg.unusable)
+ break;
+ case MISCREG_TR:
+ case MISCREG_CS:
+ if (!seg.present)
+ warn("%s: P flag not set\n", name);
+
+ if (((seg.limit & 0xFFF) == 0 && seg.g) ||
+ ((seg.limit & 0xFFF00000) != 0 && !seg.g)) {
+ warn("%s limit (0x%x) and g (%i) combination is illegal.\n",
+ name, seg.limit, seg.g);
+ }
+ break;
+ }
+
+ // TODO: Check CS DB
+}
+
X86KvmCPU::X86KvmCPU(X86KvmCPUParams *params)
: BaseKvmCPU(params)
{
@@ -505,6 +655,18 @@ X86KvmCPU::updateKvmStateSRegs()
// Clear the interrupt bitmap
memset(&sregs.interrupt_bitmap, 0, sizeof(sregs.interrupt_bitmap));
+ RFLAGS rflags_nocc(tc->readMiscReg(MISCREG_RFLAGS));
+ if (!rflags_nocc.vm) {
+ // Do segment verification if the CPU isn't entering virtual
+ // 8086 mode. We currently assume that unrestricted guest
+ // mode is available.
+
+#define APPLY_SEGMENT(kreg, idx) \
+ checkSeg(# kreg, idx + MISCREG_SEG_SEL_BASE, sregs.kreg, sregs)
+
+ FOREACH_SEGMENT();
+#undef APPLY_SEGMENT
+ }
setSpecialRegisters(sregs);
}
void