[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] [XEN] Proper segmentation emulation added to HVM emulated PTE writes.
# HG changeset patch # User kfraser@xxxxxxxxxxxxxxxxxxxxx # Node ID d5d5915f4a7c61d8bd0ef2374c1ae9787a4ae8c6 # Parent 01860d20feac41e3f1cf68edc8e41a0d8a3a9e62 [XEN] Proper segmentation emulation added to HVM emulated PTE writes. Sigmed-off-by: Keir Fraser <keir@xxxxxxxxxxxxx> --- xen/arch/x86/hvm/platform.c | 18 ++-- xen/arch/x86/hvm/svm/emulate.c | 4 - xen/arch/x86/hvm/svm/svm.c | 100 +++++++++++++++++---------- xen/arch/x86/hvm/svm/vmcb.c | 22 +++--- xen/arch/x86/hvm/vmx/vmx.c | 134 +++++++++++++++++++++++++++++-------- xen/arch/x86/mm.c | 8 +- xen/arch/x86/mm/shadow/common.c | 104 ++++++++++++++++++++++++++-- xen/arch/x86/x86_emulate.c | 32 ++++---- xen/include/asm-x86/hvm/hvm.h | 66 ++++++++++++++---- xen/include/asm-x86/hvm/svm/vmcb.h | 50 +++---------- xen/include/asm-x86/x86_emulate.h | 36 ++++++--- 11 files changed, 396 insertions(+), 178 deletions(-) diff -r 01860d20feac -r d5d5915f4a7c xen/arch/x86/hvm/platform.c --- a/xen/arch/x86/hvm/platform.c Thu Nov 30 16:36:18 2006 +0000 +++ b/xen/arch/x86/hvm/platform.c Thu Nov 30 17:07:26 2006 +0000 @@ -920,7 +920,7 @@ void handle_mmio(unsigned long gpa) df = regs->eflags & X86_EFLAGS_DF ? 1 : 0; mode = hvm_guest_x86_mode(v); - inst_addr = hvm_get_segment_base(v, seg_cs) + regs->eip; + inst_addr = hvm_get_segment_base(v, x86_seg_cs) + regs->eip; inst_len = hvm_instruction_length(inst_addr, mode); if ( inst_len <= 0 ) { @@ -964,10 +964,10 @@ void handle_mmio(unsigned long gpa) addr = regs->edi; if ( ad_size == WORD ) addr &= 0xFFFF; - addr += hvm_get_segment_base(v, seg_es); + addr += hvm_get_segment_base(v, x86_seg_es); if ( addr == gpa ) { - enum segment seg; + enum x86_segment seg; dir = IOREQ_WRITE; addr = regs->esi; @@ -975,13 +975,13 @@ void handle_mmio(unsigned long gpa) addr &= 0xFFFF; switch ( seg_sel ) { - case 0x26: seg = seg_es; break; - case 0x2e: seg = seg_cs; break; - case 0x36: seg = seg_ss; break; + case 0x26: seg = x86_seg_es; break; + case 0x2e: seg = x86_seg_cs; break; + case 0x36: seg = x86_seg_ss; break; case 0: - case 0x3e: seg = seg_ds; break; - case 0x64: seg = seg_fs; break; - case 0x65: seg = seg_gs; break; + case 0x3e: seg = x86_seg_ds; break; + case 0x64: seg = x86_seg_fs; break; + case 0x65: seg = x86_seg_gs; break; default: domain_crash_synchronous(); } addr += hvm_get_segment_base(v, seg); diff -r 01860d20feac -r d5d5915f4a7c xen/arch/x86/hvm/svm/emulate.c --- a/xen/arch/x86/hvm/svm/emulate.c Thu Nov 30 16:36:18 2006 +0000 +++ b/xen/arch/x86/hvm/svm/emulate.c Thu Nov 30 17:07:26 2006 +0000 @@ -209,7 +209,7 @@ unsigned long get_effective_addr_modrm64 #if __x86_64__ /* 64-bit mode */ - if (vmcb->cs.attributes.fields.l && (vmcb->efer & EFER_LMA)) + if (vmcb->cs.attr.fields.l && (vmcb->efer & EFER_LMA)) return vmcb->rip + inst_len + *size + disp; #endif return disp; @@ -334,7 +334,7 @@ unsigned long svm_rip2pointer(struct vmc * no matter what kind of addressing is used. */ unsigned long p = vmcb->cs.base + vmcb->rip; - if (!(vmcb->cs.attributes.fields.l && vmcb->efer & EFER_LMA)) + if (!(vmcb->cs.attr.fields.l && vmcb->efer & EFER_LMA)) return (u32)p; /* mask to 32 bits */ /* NB. Should mask to 16 bits if in real mode or 16-bit protected mode. */ return p; diff -r 01860d20feac -r d5d5915f4a7c xen/arch/x86/hvm/svm/svm.c --- a/xen/arch/x86/hvm/svm/svm.c Thu Nov 30 16:36:18 2006 +0000 +++ b/xen/arch/x86/hvm/svm/svm.c Thu Nov 30 17:07:26 2006 +0000 @@ -476,13 +476,13 @@ static int svm_guest_x86_mode(struct vcp struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; if ( vmcb->efer & EFER_LMA ) - return (vmcb->cs.attributes.fields.l ? + return (vmcb->cs.attr.fields.l ? X86EMUL_MODE_PROT64 : X86EMUL_MODE_PROT32); if ( svm_realmode(v) ) return X86EMUL_MODE_REAL; - return (vmcb->cs.attributes.fields.db ? + return (vmcb->cs.attr.fields.db ? X86EMUL_MODE_PROT32 : X86EMUL_MODE_PROT16); } @@ -509,29 +509,49 @@ unsigned long svm_get_ctrl_reg(struct vc return 0; /* dummy */ } -static unsigned long svm_get_segment_base(struct vcpu *v, enum segment seg) +static unsigned long svm_get_segment_base(struct vcpu *v, enum x86_segment seg) { struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; int long_mode = 0; #ifdef __x86_64__ - long_mode = vmcb->cs.attributes.fields.l && (vmcb->efer & EFER_LMA); + long_mode = vmcb->cs.attr.fields.l && (vmcb->efer & EFER_LMA); #endif switch ( seg ) { - case seg_cs: return long_mode ? 0 : vmcb->cs.base; - case seg_ds: return long_mode ? 0 : vmcb->ds.base; - case seg_es: return long_mode ? 0 : vmcb->es.base; - case seg_fs: return vmcb->fs.base; - case seg_gs: return vmcb->gs.base; - case seg_ss: return long_mode ? 0 : vmcb->ss.base; - case seg_tr: return vmcb->tr.base; - case seg_gdtr: return vmcb->gdtr.base; - case seg_idtr: return vmcb->idtr.base; - case seg_ldtr: return vmcb->ldtr.base; + case x86_seg_cs: return long_mode ? 0 : vmcb->cs.base; + case x86_seg_ds: return long_mode ? 0 : vmcb->ds.base; + case x86_seg_es: return long_mode ? 0 : vmcb->es.base; + case x86_seg_fs: return vmcb->fs.base; + case x86_seg_gs: return vmcb->gs.base; + case x86_seg_ss: return long_mode ? 0 : vmcb->ss.base; + case x86_seg_tr: return vmcb->tr.base; + case x86_seg_gdtr: return vmcb->gdtr.base; + case x86_seg_idtr: return vmcb->idtr.base; + case x86_seg_ldtr: return vmcb->ldtr.base; } BUG(); return 0; +} + +static void svm_get_segment_register(struct vcpu *v, enum x86_segment seg, + struct segment_register *reg) +{ + struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; + switch ( seg ) + { + case x86_seg_cs: memcpy(reg, &vmcb->cs, sizeof(*reg)); break; + case x86_seg_ds: memcpy(reg, &vmcb->ds, sizeof(*reg)); break; + case x86_seg_es: memcpy(reg, &vmcb->es, sizeof(*reg)); break; + case x86_seg_fs: memcpy(reg, &vmcb->fs, sizeof(*reg)); break; + case x86_seg_gs: memcpy(reg, &vmcb->gs, sizeof(*reg)); break; + case x86_seg_ss: memcpy(reg, &vmcb->ss, sizeof(*reg)); break; + case x86_seg_tr: memcpy(reg, &vmcb->tr, sizeof(*reg)); break; + case x86_seg_gdtr: memcpy(reg, &vmcb->gdtr, sizeof(*reg)); break; + case x86_seg_idtr: memcpy(reg, &vmcb->idtr, sizeof(*reg)); break; + case x86_seg_ldtr: memcpy(reg, &vmcb->ldtr, sizeof(*reg)); break; + default: BUG(); + } } /* Make sure that xen intercepts any FP accesses from current */ @@ -785,6 +805,11 @@ static void svm_vcpu_destroy(struct vcpu svm_destroy_vmcb(v); } +static void svm_hvm_inject_exception(unsigned int trapnr, int errcode) +{ + svm_inject_exception(current, trapnr, (errcode != -1), errcode); +} + int start_svm(void) { u32 eax, ecx, edx; @@ -844,11 +869,14 @@ int start_svm(void) hvm_funcs.guest_x86_mode = svm_guest_x86_mode; hvm_funcs.get_guest_ctrl_reg = svm_get_ctrl_reg; hvm_funcs.get_segment_base = svm_get_segment_base; + hvm_funcs.get_segment_register = svm_get_segment_register; hvm_funcs.update_host_cr3 = svm_update_host_cr3; hvm_funcs.stts = svm_stts; hvm_funcs.set_tsc_offset = svm_set_tsc_offset; + + hvm_funcs.inject_exception = svm_hvm_inject_exception; hvm_funcs.init_ap_context = svm_init_ap_context; hvm_funcs.init_hypercall_page = svm_init_hypercall_page; @@ -1154,7 +1182,7 @@ static void svm_dr_access(struct vcpu *v static void svm_get_prefix_info( struct vmcb_struct *vmcb, - unsigned int dir, segment_selector_t **seg, unsigned int *asize) + unsigned int dir, svm_segment_register_t **seg, unsigned int *asize) { unsigned char inst[MAX_INST_LEN]; int i; @@ -1235,18 +1263,18 @@ static inline int svm_get_io_address( unsigned long reg; unsigned int asize, isize; int long_mode = 0; - segment_selector_t *seg = NULL; + svm_segment_register_t *seg = NULL; struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; #ifdef __x86_64__ /* If we're in long mode, we shouldn't check the segment presence & limit */ - long_mode = vmcb->cs.attributes.fields.l && vmcb->efer & EFER_LMA; + long_mode = vmcb->cs.attr.fields.l && vmcb->efer & EFER_LMA; #endif - /* d field of cs.attributes is 1 for 32-bit, 0 for 16 or 64 bit. + /* d field of cs.attr is 1 for 32-bit, 0 for 16 or 64 bit. * l field combined with EFER_LMA -> longmode says whether it's 16 or 64 bit. */ - asize = (long_mode)?64:((vmcb->cs.attributes.fields.db)?32:16); + asize = (long_mode)?64:((vmcb->cs.attr.fields.db)?32:16); /* The ins/outs instructions are single byte, so if we have got more @@ -1266,7 +1294,7 @@ static inline int svm_get_io_address( reg = regs->esi; if (!seg) /* If no prefix, used DS. */ seg = &vmcb->ds; - if (!long_mode && (seg->attributes.fields.type & 0xa) == 0x8) { + if (!long_mode && (seg->attr.fields.type & 0xa) == 0x8) { svm_inject_exception(v, TRAP_gp_fault, 1, 0); return 0; } @@ -1275,14 +1303,14 @@ static inline int svm_get_io_address( { reg = regs->edi; seg = &vmcb->es; /* Note: This is ALWAYS ES. */ - if (!long_mode && (seg->attributes.fields.type & 0xa) != 0x2) { + if (!long_mode && (seg->attr.fields.type & 0xa) != 0x2) { svm_inject_exception(v, TRAP_gp_fault, 1, 0); return 0; } } /* If the segment isn't present, give GP fault! */ - if (!long_mode && !seg->attributes.fields.p) + if (!long_mode && !seg->attr.fields.p) { svm_inject_exception(v, TRAP_gp_fault, 1, 0); return 0; @@ -1305,7 +1333,7 @@ static inline int svm_get_io_address( { ASSERT(*addr == (u32)*addr); if ((u32)(*addr + size - 1) < (u32)*addr || - (seg->attributes.fields.type & 0xc) != 0x4 ? + (seg->attr.fields.type & 0xc) != 0x4 ? *addr + size - 1 > seg->limit : *addr <= seg->limit) { @@ -1318,9 +1346,9 @@ static inline int svm_get_io_address( occur. Note that the checking is not necessary for page granular segments as transfers crossing page boundaries will be broken up anyway. */ - if (!seg->attributes.fields.g && *count > 1) - { - if ((seg->attributes.fields.type & 0xc) != 0x4) + if (!seg->attr.fields.g && *count > 1) + { + if ((seg->attr.fields.type & 0xc) != 0x4) { /* expand-up */ if (!(regs->eflags & EF_DF)) @@ -2154,52 +2182,52 @@ static int svm_do_vmmcall_reset_to_realm /* setup the segment registers and all their hidden states */ vmcb->cs.sel = 0xF000; - vmcb->cs.attributes.bytes = 0x089b; + vmcb->cs.attr.bytes = 0x089b; vmcb->cs.limit = 0xffff; vmcb->cs.base = 0x000F0000; vmcb->ss.sel = 0x00; - vmcb->ss.attributes.bytes = 0x0893; + vmcb->ss.attr.bytes = 0x0893; vmcb->ss.limit = 0xffff; vmcb->ss.base = 0x00; vmcb->ds.sel = 0x00; - vmcb->ds.attributes.bytes = 0x0893; + vmcb->ds.attr.bytes = 0x0893; vmcb->ds.limit = 0xffff; vmcb->ds.base = 0x00; vmcb->es.sel = 0x00; - vmcb->es.attributes.bytes = 0x0893; + vmcb->es.attr.bytes = 0x0893; vmcb->es.limit = 0xffff; vmcb->es.base = 0x00; vmcb->fs.sel = 0x00; - vmcb->fs.attributes.bytes = 0x0893; + vmcb->fs.attr.bytes = 0x0893; vmcb->fs.limit = 0xffff; vmcb->fs.base = 0x00; vmcb->gs.sel = 0x00; - vmcb->gs.attributes.bytes = 0x0893; + vmcb->gs.attr.bytes = 0x0893; vmcb->gs.limit = 0xffff; vmcb->gs.base = 0x00; vmcb->ldtr.sel = 0x00; - vmcb->ldtr.attributes.bytes = 0x0000; + vmcb->ldtr.attr.bytes = 0x0000; vmcb->ldtr.limit = 0x0; vmcb->ldtr.base = 0x00; vmcb->gdtr.sel = 0x00; - vmcb->gdtr.attributes.bytes = 0x0000; + vmcb->gdtr.attr.bytes = 0x0000; vmcb->gdtr.limit = 0x0; vmcb->gdtr.base = 0x00; vmcb->tr.sel = 0; - vmcb->tr.attributes.bytes = 0; + vmcb->tr.attr.bytes = 0; vmcb->tr.limit = 0x0; vmcb->tr.base = 0; vmcb->idtr.sel = 0x00; - vmcb->idtr.attributes.bytes = 0x0000; + vmcb->idtr.attr.bytes = 0x0000; vmcb->idtr.limit = 0x3ff; vmcb->idtr.base = 0x00; diff -r 01860d20feac -r d5d5915f4a7c xen/arch/x86/hvm/svm/vmcb.c --- a/xen/arch/x86/hvm/svm/vmcb.c Thu Nov 30 16:36:18 2006 +0000 +++ b/xen/arch/x86/hvm/svm/vmcb.c Thu Nov 30 17:07:26 2006 +0000 @@ -90,7 +90,7 @@ static int construct_vmcb(struct vcpu *v { struct arch_svm_struct *arch_svm = &v->arch.hvm_svm; struct vmcb_struct *vmcb = arch_svm->vmcb; - segment_attributes_t attrib; + svm_segment_attributes_t attrib; /* Always flush the TLB on VMRUN. */ vmcb->tlb_control = 1; @@ -166,13 +166,13 @@ static int construct_vmcb(struct vcpu *v attrib.fields.p = 1; /* segment present */ attrib.fields.db = 1; /* 32-bit */ attrib.fields.g = 1; /* 4K pages in limit */ - vmcb->es.attributes = attrib; - vmcb->ss.attributes = attrib; - vmcb->ds.attributes = attrib; - vmcb->fs.attributes = attrib; - vmcb->gs.attributes = attrib; + vmcb->es.attr = attrib; + vmcb->ss.attr = attrib; + vmcb->ds.attr = attrib; + vmcb->fs.attr = attrib; + vmcb->gs.attr = attrib; attrib.fields.type = 0xb; /* type=0xb -> executable/readable, accessed */ - vmcb->cs.attributes = attrib; + vmcb->cs.attr = attrib; /* Guest IDT. */ vmcb->idtr.base = 0; @@ -186,11 +186,11 @@ static int construct_vmcb(struct vcpu *v vmcb->ldtr.sel = 0; vmcb->ldtr.base = 0; vmcb->ldtr.limit = 0; - vmcb->ldtr.attributes.bytes = 0; + vmcb->ldtr.attr.bytes = 0; /* Guest TSS. */ attrib.fields.type = 0xb; /* 32-bit TSS (busy) */ - vmcb->tr.attributes = attrib; + vmcb->tr.attr = attrib; vmcb->tr.base = 0; vmcb->tr.limit = 0xff; @@ -278,10 +278,10 @@ void svm_do_launch(struct vcpu *v) v->arch.schedule_tail = arch_svm_do_resume; } -static void svm_dump_sel(char *name, segment_selector_t *s) +static void svm_dump_sel(char *name, svm_segment_register_t *s) { printk("%s: sel=0x%04x, attr=0x%04x, limit=0x%08x, base=0x%016llx\n", - name, s->sel, s->attributes.bytes, s->limit, + name, s->sel, s->attr.bytes, s->limit, (unsigned long long)s->base); } diff -r 01860d20feac -r d5d5915f4a7c xen/arch/x86/hvm/vmx/vmx.c --- a/xen/arch/x86/hvm/vmx/vmx.c Thu Nov 30 16:36:18 2006 +0000 +++ b/xen/arch/x86/hvm/vmx/vmx.c Thu Nov 30 17:07:26 2006 +0000 @@ -501,7 +501,7 @@ static unsigned long vmx_get_ctrl_reg(st return 0; /* dummy */ } -static unsigned long vmx_get_segment_base(struct vcpu *v, enum segment seg) +static unsigned long vmx_get_segment_base(struct vcpu *v, enum x86_segment seg) { unsigned long base = 0; int long_mode = 0; @@ -516,20 +516,92 @@ static unsigned long vmx_get_segment_bas switch ( seg ) { - case seg_cs: if ( !long_mode ) base = __vmread(GUEST_CS_BASE); break; - case seg_ds: if ( !long_mode ) base = __vmread(GUEST_DS_BASE); break; - case seg_es: if ( !long_mode ) base = __vmread(GUEST_ES_BASE); break; - case seg_fs: base = __vmread(GUEST_FS_BASE); break; - case seg_gs: base = __vmread(GUEST_GS_BASE); break; - case seg_ss: if ( !long_mode ) base = __vmread(GUEST_SS_BASE); break; - case seg_tr: base = __vmread(GUEST_TR_BASE); break; - case seg_gdtr: base = __vmread(GUEST_GDTR_BASE); break; - case seg_idtr: base = __vmread(GUEST_IDTR_BASE); break; - case seg_ldtr: base = __vmread(GUEST_LDTR_BASE); break; + case x86_seg_cs: if ( !long_mode ) base = __vmread(GUEST_CS_BASE); break; + case x86_seg_ds: if ( !long_mode ) base = __vmread(GUEST_DS_BASE); break; + case x86_seg_es: if ( !long_mode ) base = __vmread(GUEST_ES_BASE); break; + case x86_seg_fs: base = __vmread(GUEST_FS_BASE); break; + case x86_seg_gs: base = __vmread(GUEST_GS_BASE); break; + case x86_seg_ss: if ( !long_mode ) base = __vmread(GUEST_SS_BASE); break; + case x86_seg_tr: base = __vmread(GUEST_TR_BASE); break; + case x86_seg_gdtr: base = __vmread(GUEST_GDTR_BASE); break; + case x86_seg_idtr: base = __vmread(GUEST_IDTR_BASE); break; + case x86_seg_ldtr: base = __vmread(GUEST_LDTR_BASE); break; default: BUG(); break; } return base; +} + +static void vmx_get_segment_register(struct vcpu *v, enum x86_segment seg, + struct segment_register *reg) +{ + u16 attr = 0; + + ASSERT(v == current); + + switch ( seg ) + { + case x86_seg_cs: + reg->sel = __vmread(GUEST_CS_SELECTOR); + reg->limit = __vmread(GUEST_CS_LIMIT); + reg->base = __vmread(GUEST_CS_BASE); + attr = __vmread(GUEST_CS_AR_BYTES); + break; + case x86_seg_ds: + reg->sel = __vmread(GUEST_DS_SELECTOR); + reg->limit = __vmread(GUEST_DS_LIMIT); + reg->base = __vmread(GUEST_DS_BASE); + attr = __vmread(GUEST_DS_AR_BYTES); + break; + case x86_seg_es: + reg->sel = __vmread(GUEST_ES_SELECTOR); + reg->limit = __vmread(GUEST_ES_LIMIT); + reg->base = __vmread(GUEST_ES_BASE); + attr = __vmread(GUEST_ES_AR_BYTES); + break; + case x86_seg_fs: + reg->sel = __vmread(GUEST_FS_SELECTOR); + reg->limit = __vmread(GUEST_FS_LIMIT); + reg->base = __vmread(GUEST_FS_BASE); + attr = __vmread(GUEST_FS_AR_BYTES); + break; + case x86_seg_gs: + reg->sel = __vmread(GUEST_GS_SELECTOR); + reg->limit = __vmread(GUEST_GS_LIMIT); + reg->base = __vmread(GUEST_GS_BASE); + attr = __vmread(GUEST_GS_AR_BYTES); + break; + case x86_seg_ss: + reg->sel = __vmread(GUEST_SS_SELECTOR); + reg->limit = __vmread(GUEST_SS_LIMIT); + reg->base = __vmread(GUEST_SS_BASE); + attr = __vmread(GUEST_SS_AR_BYTES); + break; + case x86_seg_tr: + reg->sel = __vmread(GUEST_TR_SELECTOR); + reg->limit = __vmread(GUEST_TR_LIMIT); + reg->base = __vmread(GUEST_TR_BASE); + attr = __vmread(GUEST_TR_AR_BYTES); + break; + case x86_seg_gdtr: + reg->limit = __vmread(GUEST_GDTR_LIMIT); + reg->base = __vmread(GUEST_GDTR_BASE); + break; + case x86_seg_idtr: + reg->limit = __vmread(GUEST_IDTR_LIMIT); + reg->base = __vmread(GUEST_IDTR_BASE); + break; + case x86_seg_ldtr: + reg->sel = __vmread(GUEST_LDTR_SELECTOR); + reg->limit = __vmread(GUEST_LDTR_LIMIT); + reg->base = __vmread(GUEST_LDTR_BASE); + attr = __vmread(GUEST_LDTR_AR_BYTES); + break; + default: + BUG(); + } + + reg->attr.bytes = (attr & 0xff) | ((attr >> 4) & 0xf00); } /* Make sure that xen intercepts any FP accesses from current */ @@ -630,6 +702,11 @@ static int vmx_pae_enabled(struct vcpu * return (vmx_paging_enabled(v) && (cr4 & X86_CR4_PAE)); } +static void vmx_inject_exception(unsigned int trapnr, int errcode) +{ + vmx_inject_hw_exception(current, trapnr, errcode); +} + /* Setup HVM interfaces */ static void vmx_setup_hvm_funcs(void) { @@ -650,11 +727,14 @@ static void vmx_setup_hvm_funcs(void) hvm_funcs.guest_x86_mode = vmx_guest_x86_mode; hvm_funcs.get_guest_ctrl_reg = vmx_get_ctrl_reg; hvm_funcs.get_segment_base = vmx_get_segment_base; + hvm_funcs.get_segment_register = vmx_get_segment_register; hvm_funcs.update_host_cr3 = vmx_update_host_cr3; hvm_funcs.stts = vmx_stts; hvm_funcs.set_tsc_offset = vmx_set_tsc_offset; + + hvm_funcs.inject_exception = vmx_inject_exception; hvm_funcs.init_ap_context = vmx_init_ap_context; @@ -962,14 +1042,14 @@ static void vmx_do_invlpg(unsigned long static int vmx_check_descriptor(int long_mode, unsigned long eip, int inst_len, - enum segment seg, unsigned long *base, + enum x86_segment seg, unsigned long *base, u32 *limit, u32 *ar_bytes) { enum vmcs_field ar_field, base_field, limit_field; *base = 0; *limit = 0; - if ( seg != seg_es ) + if ( seg != x86_seg_es ) { unsigned char inst[MAX_INST_LEN]; int i; @@ -999,22 +1079,22 @@ static int vmx_check_descriptor(int long #endif continue; case 0x2e: /* CS */ - seg = seg_cs; + seg = x86_seg_cs; continue; case 0x36: /* SS */ - seg = seg_ss; + seg = x86_seg_ss; continue; case 0x26: /* ES */ - seg = seg_es; + seg = x86_seg_es; continue; case 0x64: /* FS */ - seg = seg_fs; + seg = x86_seg_fs; continue; case 0x65: /* GS */ - seg = seg_gs; + seg = x86_seg_gs; continue; case 0x3e: /* DS */ - seg = seg_ds; + seg = x86_seg_ds; continue; } } @@ -1022,32 +1102,32 @@ static int vmx_check_descriptor(int long switch ( seg ) { - case seg_cs: + case x86_seg_cs: ar_field = GUEST_CS_AR_BYTES; base_field = GUEST_CS_BASE; limit_field = GUEST_CS_LIMIT; break; - case seg_ds: + case x86_seg_ds: ar_field = GUEST_DS_AR_BYTES; base_field = GUEST_DS_BASE; limit_field = GUEST_DS_LIMIT; break; - case seg_es: + case x86_seg_es: ar_field = GUEST_ES_AR_BYTES; base_field = GUEST_ES_BASE; limit_field = GUEST_ES_LIMIT; break; - case seg_fs: + case x86_seg_fs: ar_field = GUEST_FS_AR_BYTES; base_field = GUEST_FS_BASE; limit_field = GUEST_FS_LIMIT; break; - case seg_gs: + case x86_seg_gs: ar_field = GUEST_FS_AR_BYTES; base_field = GUEST_FS_BASE; limit_field = GUEST_FS_LIMIT; break; - case seg_ss: + case x86_seg_ss: ar_field = GUEST_GS_AR_BYTES; base_field = GUEST_GS_BASE; limit_field = GUEST_GS_LIMIT; @@ -1057,7 +1137,7 @@ static int vmx_check_descriptor(int long return 0; } - if ( !long_mode || seg == seg_fs || seg == seg_gs ) + if ( !long_mode || seg == x86_seg_fs || seg == x86_seg_gs ) { *base = __vmread(base_field); *limit = __vmread(limit_field); @@ -1127,7 +1207,7 @@ static void vmx_io_instruction(unsigned * selector is null. */ if ( !vmx_check_descriptor(long_mode, regs->eip, inst_len, - dir == IOREQ_WRITE ? seg_ds : seg_es, + dir==IOREQ_WRITE ? x86_seg_ds : x86_seg_es, &base, &limit, &ar_bytes) ) { if ( !long_mode ) { vmx_inject_hw_exception(current, TRAP_gp_fault, 0); diff -r 01860d20feac -r d5d5915f4a7c xen/arch/x86/mm.c --- a/xen/arch/x86/mm.c Thu Nov 30 16:36:18 2006 +0000 +++ b/xen/arch/x86/mm.c Thu Nov 30 17:07:26 2006 +0000 @@ -3040,7 +3040,7 @@ struct ptwr_emulate_ctxt { }; static int ptwr_emulated_read( - unsigned int seg, + enum x86_segment seg, unsigned long offset, unsigned long *val, unsigned int bytes, @@ -3183,7 +3183,7 @@ static int ptwr_emulated_update( } static int ptwr_emulated_write( - unsigned int seg, + enum x86_segment seg, unsigned long offset, unsigned long val, unsigned int bytes, @@ -3195,7 +3195,7 @@ static int ptwr_emulated_write( } static int ptwr_emulated_cmpxchg( - unsigned int seg, + enum x86_segment seg, unsigned long offset, unsigned long old, unsigned long new, @@ -3208,7 +3208,7 @@ static int ptwr_emulated_cmpxchg( } static int ptwr_emulated_cmpxchg8b( - unsigned int seg, + enum x86_segment seg, unsigned long offset, unsigned long old, unsigned long old_hi, diff -r 01860d20feac -r d5d5915f4a7c xen/arch/x86/mm/shadow/common.c --- a/xen/arch/x86/mm/shadow/common.c Thu Nov 30 16:36:18 2006 +0000 +++ b/xen/arch/x86/mm/shadow/common.c Thu Nov 30 17:07:26 2006 +0000 @@ -69,14 +69,87 @@ int _shadow_mode_refcounts(struct domain /* x86 emulator support for the shadow code */ +static int hvm_translate_linear_addr( + enum x86_segment seg, + unsigned long offset, + unsigned int bytes, + unsigned int is_write, + unsigned long *paddr) +{ + struct segment_register creg, dreg; + unsigned long limit, addr = offset; + uint32_t last_byte; + + hvm_get_segment_register(current, x86_seg_cs, &creg); + hvm_get_segment_register(current, seg, &dreg); + + if ( !creg.attr.fields.l || !hvm_long_mode_enabled(current) ) + { + /* + * COMPATIBILITY MODE: Apply segment checks and add base. + */ + + /* If this is a store, is the segment a writable data segment? */ + if ( is_write && ((dreg.attr.fields.type & 0xa) != 0x2) ) + goto gpf; + + /* Calculate the segment limit, including granularity flag. */ + limit = dreg.limit; + if ( dreg.attr.fields.g ) + limit = (limit << 12) | 0xfff; + + last_byte = offset + bytes - 1; + + /* Is this a grows-down data segment? Special limit check if so. */ + if ( (dreg.attr.fields.type & 0xc) == 0x4 ) + { + /* Is upper limit 0xFFFF or 0xFFFFFFFF? */ + if ( !dreg.attr.fields.db ) + last_byte = (uint16_t)last_byte; + + /* Check first byte and last byte against respective bounds. */ + if ( (offset <= limit) || (last_byte < offset) ) + goto gpf; + } + else if ( (last_byte > limit) || (last_byte < offset) ) + goto gpf; /* last byte is beyond limit or wraps 0xFFFFFFFF */ + + /* + * Hardware truncates to 32 bits in compatibility mode. + * It does not truncate to 16 bits in 16-bit address-size mode. + */ + addr = (uint32_t)(addr + dreg.base); + } + else if ( (seg == x86_seg_fs) || (seg == x86_seg_gs) ) + { + /* + * LONG MODE: FS and GS add a segment base. + */ + addr += dreg.base; + } + + *paddr = addr; + return 0; + + gpf: + /* Inject #GP(0). */ + hvm_inject_exception(TRAP_gp_fault, 0); + return X86EMUL_PROPAGATE_FAULT; +} + static int -sh_x86_emulate_read(unsigned int seg, +sh_x86_emulate_read(enum x86_segment seg, unsigned long offset, unsigned long *val, unsigned int bytes, struct x86_emulate_ctxt *ctxt) { - unsigned long addr = offset; + unsigned long addr; + int rc; + + rc = hvm_translate_linear_addr(seg, offset, bytes, 0, &addr); + if ( rc ) + return rc; *val = 0; // XXX -- this is WRONG. @@ -102,14 +175,19 @@ sh_x86_emulate_read(unsigned int seg, } static int -sh_x86_emulate_write(unsigned int seg, +sh_x86_emulate_write(enum x86_segment seg, unsigned long offset, unsigned long val, unsigned int bytes, struct x86_emulate_ctxt *ctxt) { struct vcpu *v = current; - unsigned long addr = offset; + unsigned long addr; + int rc; + + rc = hvm_translate_linear_addr(seg, offset, bytes, 1, &addr); + if ( rc ) + return rc; #if 0 SHADOW_PRINTK("d=%u v=%u a=%#lx v=%#lx bytes=%u\n", @@ -119,7 +197,7 @@ sh_x86_emulate_write(unsigned int seg, } static int -sh_x86_emulate_cmpxchg(unsigned int seg, +sh_x86_emulate_cmpxchg(enum x86_segment seg, unsigned long offset, unsigned long old, unsigned long new, @@ -127,7 +205,12 @@ sh_x86_emulate_cmpxchg(unsigned int seg, struct x86_emulate_ctxt *ctxt) { struct vcpu *v = current; - unsigned long addr = offset; + unsigned long addr; + int rc; + + rc = hvm_translate_linear_addr(seg, offset, bytes, 1, &addr); + if ( rc ) + return rc; #if 0 SHADOW_PRINTK("d=%u v=%u a=%#lx o?=%#lx n:=%#lx bytes=%u\n", @@ -138,7 +221,7 @@ sh_x86_emulate_cmpxchg(unsigned int seg, } static int -sh_x86_emulate_cmpxchg8b(unsigned int seg, +sh_x86_emulate_cmpxchg8b(enum x86_segment seg, unsigned long offset, unsigned long old_lo, unsigned long old_hi, @@ -147,7 +230,12 @@ sh_x86_emulate_cmpxchg8b(unsigned int se struct x86_emulate_ctxt *ctxt) { struct vcpu *v = current; - unsigned long addr = offset; + unsigned long addr; + int rc; + + rc = hvm_translate_linear_addr(seg, offset, 8, 1, &addr); + if ( rc ) + return rc; #if 0 SHADOW_PRINTK("d=%u v=%u a=%#lx o?=%#lx:%lx n:=%#lx:%lx\n", diff -r 01860d20feac -r d5d5915f4a7c xen/arch/x86/x86_emulate.c --- a/xen/arch/x86/x86_emulate.c Thu Nov 30 16:36:18 2006 +0000 +++ b/xen/arch/x86/x86_emulate.c Thu Nov 30 17:07:26 2006 +0000 @@ -199,8 +199,8 @@ struct operand { /* OP_REG: Pointer to register field. */ unsigned long *reg; /* OP_MEM: Segment and offset. */ - unsigned int mem_seg; - unsigned long mem_off; + enum x86_segment mem_seg; + unsigned long mem_off; }; /* EFLAGS bit definitions. */ @@ -375,7 +375,7 @@ do{ __asm__ __volatile__ ( /* Fetch next part of the instruction being emulated. */ #define _insn_fetch(_size) \ ({ unsigned long _x; \ - rc = ops->read(_regs.cs, _regs.eip, &_x, (_size), ctxt); \ + rc = ops->read(x86_seg_cs, _regs.eip, &_x, (_size), ctxt); \ if ( rc != 0 ) \ goto done; \ _regs.eip += (_size); \ @@ -452,7 +452,7 @@ dump_instr( dprintf("Instr:"); for ( i = 0; i < 16; i++, eip++ ) { - if ( ops->read(ctxt->regs->cs, eip, &x, 1, ctxt) != 0 ) + if ( ops->read(x86_seg_cs, eip, &x, 1, ctxt) != 0 ) printk(" ??"); else printk(" %02x", (uint8_t)x); @@ -476,8 +476,8 @@ x86_emulate_memop( struct operand src, dst; int mode = ctxt->mode; - unsigned int ea_seg = X86_SEG_DS; - unsigned long ea_off = 0; + enum x86_segment ea_seg = x86_seg_ds; + unsigned long ea_off = 0; switch ( mode ) { @@ -513,22 +513,22 @@ x86_emulate_memop( ad_bytes ^= 6; /* switch between 2/4 bytes */ break; case 0x2e: /* CS override */ - ea_seg = X86_SEG_CS; + ea_seg = x86_seg_cs; break; case 0x3e: /* DS override */ - ea_seg = X86_SEG_DS; + ea_seg = x86_seg_ds; break; case 0x26: /* ES override */ - ea_seg = X86_SEG_ES; + ea_seg = x86_seg_es; break; case 0x64: /* FS override */ - ea_seg = X86_SEG_FS; + ea_seg = x86_seg_fs; break; case 0x65: /* GS override */ - ea_seg = X86_SEG_GS; + ea_seg = x86_seg_gs; break; case 0x36: /* SS override */ - ea_seg = X86_SEG_SS; + ea_seg = x86_seg_ss; break; case 0xf0: /* LOCK */ lock_prefix = 1; @@ -860,7 +860,7 @@ x86_emulate_memop( /* 64-bit mode: POP always pops a 64-bit operand. */ if ( mode == X86EMUL_MODE_PROT64 ) dst.bytes = 8; - if ( (rc = ops->read(X86_SEG_SS, truncate_ea(_regs.esp), + if ( (rc = ops->read(x86_seg_ss, truncate_ea(_regs.esp), &dst.val, dst.bytes, ctxt)) != 0 ) goto done; register_address_increment(_regs.esp, dst.bytes); @@ -942,7 +942,7 @@ x86_emulate_memop( goto done; } register_address_increment(_regs.esp, -dst.bytes); - if ( (rc = ops->write(X86_SEG_SS, truncate_ea(_regs.esp), + if ( (rc = ops->write(x86_seg_ss, truncate_ea(_regs.esp), dst.val, dst.bytes, ctxt)) != 0 ) goto done; dst.val = dst.orig_val; /* skanky: disable writeback */ @@ -1024,7 +1024,7 @@ x86_emulate_memop( case 0xa4 ... 0xa5: /* movs */ dst.type = OP_MEM; dst.bytes = (d & ByteOp) ? 1 : op_bytes; - dst.mem_seg = X86_SEG_ES; + dst.mem_seg = x86_seg_es; dst.mem_off = truncate_ea(_regs.edi); if ( (rc = ops->read(ea_seg, truncate_ea(_regs.esi), &dst.val, dst.bytes, ctxt)) != 0 ) @@ -1037,7 +1037,7 @@ x86_emulate_memop( case 0xaa ... 0xab: /* stos */ dst.type = OP_MEM; dst.bytes = (d & ByteOp) ? 1 : op_bytes; - dst.mem_seg = X86_SEG_ES; + dst.mem_seg = x86_seg_es; dst.mem_off = truncate_ea(_regs.edi); dst.val = _regs.eax; register_address_increment( diff -r 01860d20feac -r d5d5915f4a7c xen/include/asm-x86/hvm/hvm.h --- a/xen/include/asm-x86/hvm/hvm.h Thu Nov 30 16:36:18 2006 +0000 +++ b/xen/include/asm-x86/hvm/hvm.h Thu Nov 30 17:07:26 2006 +0000 @@ -17,28 +17,47 @@ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307 USA. */ + #ifndef __ASM_X86_HVM_HVM_H__ #define __ASM_X86_HVM_HVM_H__ -enum segment { - seg_cs, - seg_ss, - seg_ds, - seg_es, - seg_fs, - seg_gs, - seg_tr, - seg_ldtr, - seg_gdtr, - seg_idtr -}; +#include <asm/x86_emulate.h> + +/* + * Attribute for segment selector. This is a copy of bit 40:47 & 52:55 of the + * segment descriptor. It happens to match the format of an AMD SVM VMCB. + */ +typedef union segment_attributes { + u16 bytes; + struct + { + u16 type:4; /* 0; Bit 40-43 */ + u16 s: 1; /* 4; Bit 44 */ + u16 dpl: 2; /* 5; Bit 45-46 */ + u16 p: 1; /* 7; Bit 47 */ + u16 avl: 1; /* 8; Bit 52 */ + u16 l: 1; /* 9; Bit 53 */ + u16 db: 1; /* 10; Bit 54 */ + u16 g: 1; /* 11; Bit 55 */ + } fields; +} __attribute__ ((packed)) segment_attributes_t; + +/* + * Full state of a segment register (visible and hidden portions). + * Again, this happens to match the format of an AMD SVM VMCB. + */ +typedef struct segment_register { + u16 sel; + segment_attributes_t attr; + u32 limit; + u64 base; +} __attribute__ ((packed)) segment_register_t; /* * The hardware virtual machine (HVM) interface abstracts away from the * x86/x86_64 CPU virtualization assist specifics. Currently this interface * supports Intel's VT-x and AMD's SVM extensions. */ - struct hvm_function_table { /* * Disable HVM functionality @@ -74,7 +93,9 @@ struct hvm_function_table { int (*pae_enabled)(struct vcpu *v); int (*guest_x86_mode)(struct vcpu *v); unsigned long (*get_guest_ctrl_reg)(struct vcpu *v, unsigned int num); - unsigned long (*get_segment_base)(struct vcpu *v, enum segment seg); + unsigned long (*get_segment_base)(struct vcpu *v, enum x86_segment seg); + void (*get_segment_register)(struct vcpu *v, enum x86_segment seg, + struct segment_register *reg); /* * Re-set the value of CR3 that Xen runs on when handling VM exits @@ -89,6 +110,8 @@ struct hvm_function_table { void (*stts)(struct vcpu *v); void (*set_tsc_offset)(struct vcpu *v, u64 offset); + void (*inject_exception)(unsigned int trapnr, int errcode); + void (*init_ap_context)(struct vcpu_guest_context *ctxt, int vcpuid, int trampoline_vector); @@ -172,9 +195,16 @@ hvm_get_guest_ctrl_reg(struct vcpu *v, u } static inline unsigned long -hvm_get_segment_base(struct vcpu *v, enum segment seg) +hvm_get_segment_base(struct vcpu *v, enum x86_segment seg) { return hvm_funcs.get_segment_base(v, seg); +} + +static inline void +hvm_get_segment_register(struct vcpu *v, enum x86_segment seg, + struct segment_register *reg) +{ + hvm_funcs.get_segment_register(v, seg, reg); } void hvm_stts(struct vcpu *v); @@ -190,6 +220,12 @@ hvm_init_ap_context(struct vcpu_guest_co return hvm_funcs.init_ap_context(ctxt, vcpuid, trampoline_vector); } +static inline void +hvm_inject_exception(unsigned int trapnr, int errcode) +{ + hvm_funcs.inject_exception(trapnr, errcode); +} + int hvm_bringup_ap(int vcpuid, int trampoline_vector); #endif /* __ASM_X86_HVM_HVM_H__ */ diff -r 01860d20feac -r d5d5915f4a7c xen/include/asm-x86/hvm/svm/vmcb.h --- a/xen/include/asm-x86/hvm/svm/vmcb.h Thu Nov 30 16:36:18 2006 +0000 +++ b/xen/include/asm-x86/hvm/svm/vmcb.h Thu Nov 30 17:07:26 2006 +0000 @@ -311,33 +311,9 @@ enum { SVM_CPU_STATE_ASSIST_ENABLED, }; -/* - * Attribute for segment selector. This is a copy of bit 40:47 & 52:55 of the - * segment descriptor. - */ -typedef union -{ - u16 bytes; - struct - { - u16 type:4; /* 0; Bit 40-43 */ - u16 s: 1; /* 4; Bit 44 */ - u16 dpl: 2; /* 5; Bit 45-46 */ - u16 p: 1; /* 7; Bit 47 */ - u16 avl: 1; /* 8; Bit 52 */ - u16 l: 1; /* 9; Bit 53 */ - u16 db: 1; /* 10; Bit 54 */ - u16 g: 1; /* 11; Bit 55 */ - } fields; -} __attribute__ ((packed)) segment_attributes_t; - -typedef struct -{ - u16 sel; - segment_attributes_t attributes; - u32 limit; - u64 base; -} __attribute__ ((packed)) segment_selector_t; +/* Definitions of segment state are borrowed by the generic HVM code. */ +typedef segment_attributes_t svm_segment_attributes_t; +typedef segment_register_t svm_segment_register_t; typedef union { @@ -426,16 +402,16 @@ struct vmcb_struct { u64 h_cr3; /* offset 0xB0 */ u64 res09[105]; /* offset 0xB8 pad to save area */ - segment_selector_t es; /* offset 1024 */ - segment_selector_t cs; - segment_selector_t ss; - segment_selector_t ds; - segment_selector_t fs; - segment_selector_t gs; - segment_selector_t gdtr; - segment_selector_t ldtr; - segment_selector_t idtr; - segment_selector_t tr; + svm_segment_register_t es; /* offset 1024 */ + svm_segment_register_t cs; + svm_segment_register_t ss; + svm_segment_register_t ds; + svm_segment_register_t fs; + svm_segment_register_t gs; + svm_segment_register_t gdtr; + svm_segment_register_t ldtr; + svm_segment_register_t idtr; + svm_segment_register_t tr; u64 res10[5]; u8 res11[3]; u8 cpl; diff -r 01860d20feac -r d5d5915f4a7c xen/include/asm-x86/x86_emulate.h --- a/xen/include/asm-x86/x86_emulate.h Thu Nov 30 16:36:18 2006 +0000 +++ b/xen/include/asm-x86/x86_emulate.h Thu Nov 30 17:07:26 2006 +0000 @@ -11,16 +11,26 @@ struct x86_emulate_ctxt; -#define X86_SEG_CS 0 -#define X86_SEG_SS 1 -#define X86_SEG_DS 2 -#define X86_SEG_ES 3 -#define X86_SEG_FS 4 -#define X86_SEG_GS 5 +/* + * Comprehensive enumeration of x86 segment registers. Note that the system + * registers (TR, LDTR, GDTR, IDTR) are never referenced by the emulator. + */ +enum x86_segment { + /* General purpose. */ + x86_seg_cs, + x86_seg_ss, + x86_seg_ds, + x86_seg_es, + x86_seg_fs, + x86_seg_gs, + /* System. */ + x86_seg_tr, + x86_seg_ldtr, + x86_seg_gdtr, + x86_seg_idtr +}; /* - * x86_emulate_ops: - * * These operations represent the instruction emulator's interface to memory. * * NOTES: @@ -45,7 +55,7 @@ struct x86_emulate_ops { /* * All functions: - * @seg: [IN ] Segment being dereferenced (specified as X86_SEG_??). + * @seg: [IN ] Segment being dereferenced (specified as x86_seg_??). * @offset [IN ] Offset within segment. */ @@ -55,7 +65,7 @@ struct x86_emulate_ops * @bytes: [IN ] Number of bytes to read from memory. */ int (*read)( - unsigned int seg, + enum x86_segment seg, unsigned long offset, unsigned long *val, unsigned int bytes, @@ -67,7 +77,7 @@ struct x86_emulate_ops * @bytes: [IN ] Number of bytes to write to memory. */ int (*write)( - unsigned int seg, + enum x86_segment seg, unsigned long offset, unsigned long val, unsigned int bytes, @@ -80,7 +90,7 @@ struct x86_emulate_ops * @bytes: [IN ] Number of bytes to access using CMPXCHG. */ int (*cmpxchg)( - unsigned int seg, + enum x86_segment seg, unsigned long offset, unsigned long old, unsigned long new, @@ -98,7 +108,7 @@ struct x86_emulate_ops * to defining a function that always returns X86EMUL_UNHANDLEABLE. */ int (*cmpxchg8b)( - unsigned int seg, + enum x86_segment seg, unsigned long offset, unsigned long old_lo, unsigned long old_hi, _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |