On Sat, Mar 05, 2011 at 10:40:08AM +0100, Francis Moreau wrote: > [ was post on LKML but try it here since it's more appropriate ] > > Hello, > > I'm running kernel 2.6.38-rc6 with qemu-kvm 0.12.3. > > Doing this inside the guest: > > int main(void) > { > if (ioperm(0x3e0, 4, 1) < 0) { > perror("ioperm"); > exit(1); > } > > outb(0, 0x3e0); > inb(0x3e1); > printf("exiting succesfully\n"); > return 0; > } > > make a general protection error. > > Looking for the faulty instruction with gdb and found that's the 'inb' > instruction the culprit. > > Running the same program on the host works fine. > > Could anybody tell me what's wrong ? > IO permission checking for 64-bit guest in KVM is wrong. The patch bellow should fix it. diff --git a/arch/x86/include/asm/kvm_emulate.h b/arch/x86/include/asm/kvm_emulate.h index 50ebc32..7ef5b86 100644 --- a/arch/x86/include/asm/kvm_emulate.h +++ b/arch/x86/include/asm/kvm_emulate.h @@ -142,10 +142,8 @@ struct x86_emulate_ops { int (*pio_out_emulated)(int size, unsigned short port, const void *val, unsigned int count, struct kvm_vcpu *vcpu); - bool (*get_cached_descriptor)(struct desc_struct *desc, - int seg, struct kvm_vcpu *vcpu); - void (*set_cached_descriptor)(struct desc_struct *desc, - int seg, struct kvm_vcpu *vcpu); + bool (*get_cached_descriptor)(void *p, int seg, struct kvm_vcpu *vcpu); + void (*set_cached_descriptor)(void *p, int seg, struct kvm_vcpu *vcpu); u16 (*get_segment_selector)(int seg, struct kvm_vcpu *vcpu); void (*set_segment_selector)(u16 sel, int seg, struct kvm_vcpu *vcpu); unsigned long (*get_cached_segment_base)(int seg, struct kvm_vcpu *vcpu); diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index ad46239..d7d8b63 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -1764,25 +1764,35 @@ static bool emulator_io_port_access_allowed(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops, u16 port, u16 len) { - struct desc_struct tr_seg; + union { + struct desc_struct tss32; +#ifdef CONFIG_X86_64 + struct ldttss_desc64 tss64; +#endif + } tr_seg; int r; u16 io_bitmap_ptr; u8 perm, bit_idx = port & 0x7; unsigned mask = (1 << len) - 1; + unsigned long base; ops->get_cached_descriptor(&tr_seg, VCPU_SREG_TR, ctxt->vcpu); - if (!tr_seg.p) + if (!tr_seg.tss32.p) return false; - if (desc_limit_scaled(&tr_seg) < 103) + if (desc_limit_scaled(&tr_seg.tss32) < 103) return false; - r = ops->read_std(get_desc_base(&tr_seg) + 102, &io_bitmap_ptr, 2, - ctxt->vcpu, NULL); + base = get_desc_base(&tr_seg.tss32); +#ifdef CONFIG_X86_64 + if (ctxt->mode == X86EMUL_MODE_PROT64) + base |= ((u64)tr_seg.tss64.base3) << 32; +#endif + r = ops->read_std(base + 102, &io_bitmap_ptr, 2, ctxt->vcpu, NULL); if (r != X86EMUL_CONTINUE) return false; - if (io_bitmap_ptr + port/8 > desc_limit_scaled(&tr_seg)) + if (io_bitmap_ptr + port/8 > desc_limit_scaled(&tr_seg.tss32)) return false; - r = ops->read_std(get_desc_base(&tr_seg) + io_bitmap_ptr + port/8, - &perm, 1, ctxt->vcpu, NULL); + r = ops->read_std(base + io_bitmap_ptr + port/8, &perm, 1, ctxt->vcpu, + NULL); if (r != X86EMUL_CONTINUE) return false; if ((perm >> bit_idx) & mask) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 785ae0c..e41e098 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -4162,11 +4162,12 @@ static unsigned long emulator_get_cached_segment_base(int seg, return get_segment_base(vcpu, seg); } -static bool emulator_get_cached_descriptor(struct desc_struct *desc, int seg, +static bool emulator_get_cached_descriptor(void *p, int seg, struct kvm_vcpu *vcpu) { struct kvm_segment var; - + struct desc_struct *desc = p; + kvm_get_segment(vcpu, &var, seg); if (var.unusable) @@ -4185,13 +4186,22 @@ static bool emulator_get_cached_descriptor(struct desc_struct *desc, int seg, desc->d = var.db; desc->g = var.g; +#ifdef CONFIG_X86_64 + if (seg == VCPU_SREG_TR && is_long_mode(vcpu)) { + struct ldttss_desc64 *tss64 = p; + tss64->base3 = var.base >> 32; + tss64->zero1 = 0; + } +#endif + return true; } -static void emulator_set_cached_descriptor(struct desc_struct *desc, int seg, +static void emulator_set_cached_descriptor(void *p, int seg, struct kvm_vcpu *vcpu) { struct kvm_segment var; + struct desc_struct *desc = p; /* needed to preserve selector */ kvm_get_segment(vcpu, &var, seg); @@ -4211,6 +4221,12 @@ static void emulator_set_cached_descriptor(struct desc_struct *desc, int seg, var.present = desc->p; var.unusable = !var.present; var.padding = 0; +#ifdef CONFIG_X86_64 + if (seg == VCPU_SREG_TR && is_long_mode(vcpu)) { + struct ldttss_desc64 *tss64 = p; + var.base |= ((u64)tss64->base3) << 32; + } +#endif kvm_set_segment(vcpu, &var, seg); return; -- Gleb. -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html