diff options
author | tychon <tychon@FreeBSD.org> | 2015-06-26 18:00:29 +0000 |
---|---|---|
committer | tychon <tychon@FreeBSD.org> | 2015-06-26 18:00:29 +0000 |
commit | d4a6573433ab40fe0dceead2226ca9750881bce9 (patch) | |
tree | c89c06ef3f541bd643ffc9824ba851bf683cbc0a | |
parent | 7fae2fa74ecfc17309910d1123e159f4542972d2 (diff) | |
download | FreeBSD-src-d4a6573433ab40fe0dceead2226ca9750881bce9.zip FreeBSD-src-d4a6573433ab40fe0dceead2226ca9750881bce9.tar.gz |
verify_gla() needs to account for non-zero segment base addresses.
Reviewed by: neel
-rw-r--r-- | sys/amd64/vmm/vmm_instruction_emul.c | 51 |
1 files changed, 44 insertions, 7 deletions
diff --git a/sys/amd64/vmm/vmm_instruction_emul.c b/sys/amd64/vmm/vmm_instruction_emul.c index 6dadcc1..ae5330f 100644 --- a/sys/amd64/vmm/vmm_instruction_emul.c +++ b/sys/amd64/vmm/vmm_instruction_emul.c @@ -2321,10 +2321,13 @@ decode_moffset(struct vie *vie) * page table fault matches with our instruction decoding. */ static int -verify_gla(struct vm *vm, int cpuid, uint64_t gla, struct vie *vie) +verify_gla(struct vm *vm, int cpuid, uint64_t gla, struct vie *vie, + enum vm_cpu_mode cpu_mode) { int error; - uint64_t base, idx, gla2; + uint64_t base, segbase, idx, gla2; + enum vm_reg_name seg; + struct seg_desc desc; /* Skip 'gla' verification */ if (gla == VIE_INVALID_GLA) @@ -2357,14 +2360,48 @@ verify_gla(struct vm *vm, int cpuid, uint64_t gla, struct vie *vie) } } - /* XXX assuming that the base address of the segment is 0 */ - gla2 = base + vie->scale * idx + vie->displacement; + /* + * From "Specifying a Segment Selector", Intel SDM, Vol 1 + * + * In 64-bit mode, segmentation is generally (but not + * completely) disabled. The exceptions are the FS and GS + * segments. + * + * In legacy IA-32 mode, when the ESP or EBP register is used + * as the base, the SS segment is the default segment. For + * other data references, except when relative to stack or + * string destination the DS segment is the default. These + * can be overridden to allow other segments to be accessed. + */ + if (vie->segment_override) + seg = vie->segment_register; + else if (vie->base_register == VM_REG_GUEST_RSP || + vie->base_register == VM_REG_GUEST_RBP) + seg = VM_REG_GUEST_SS; + else + seg = VM_REG_GUEST_DS; + if (cpu_mode == CPU_MODE_64BIT && seg != VM_REG_GUEST_FS && + seg != VM_REG_GUEST_GS) { + segbase = 0; + } else { + error = vm_get_seg_desc(vm, cpuid, seg, &desc); + if (error) { + printf("verify_gla: error %d getting segment" + " descriptor %d", error, + vie->segment_register); + return (-1); + } + segbase = desc.base; + } + + gla2 = segbase + base + vie->scale * idx + vie->displacement; gla2 &= size2mask[vie->addrsize]; if (gla != gla2) { - printf("verify_gla mismatch: " + printf("verify_gla mismatch: segbase(0x%0lx)" "base(0x%0lx), scale(%d), index(0x%0lx), " "disp(0x%0lx), gla(0x%0lx), gla2(0x%0lx)\n", - base, vie->scale, idx, vie->displacement, gla, gla2); + segbase, base, vie->scale, idx, vie->displacement, + gla, gla2); return (-1); } @@ -2398,7 +2435,7 @@ vmm_decode_instruction(struct vm *vm, int cpuid, uint64_t gla, return (-1); if ((vie->op.op_flags & VIE_OP_F_NO_GLA_VERIFICATION) == 0) { - if (verify_gla(vm, cpuid, gla, vie)) + if (verify_gla(vm, cpuid, gla, vie, cpu_mode)) return (-1); } |