# HG changeset patch
# User kfraser@xxxxxxxxxxxxxxxxxxxxx
# Date 1190112582 -3600
# Node ID 35fb20c4822c40ca404fe61b1734b063b80047a6
# Parent 49700bb716bb71b8a3ed23216522a62cf8f95259
hvm: Support hardware task switching.
Signed-off-by: Keir Fraser <keir@xxxxxxxxxxxxx>
---
xen/arch/x86/hvm/hvm.c | 406 ++++++++++++++++++++++++++++++++++++++++
xen/arch/x86/hvm/svm/svm.c | 26 +-
xen/arch/x86/hvm/vmx/vmx.c | 28 ++
xen/arch/x86/mm/shadow/common.c | 88 +-------
xen/include/asm-x86/hvm/hvm.h | 17 +
5 files changed, 479 insertions(+), 86 deletions(-)
diff -r 49700bb716bb -r 35fb20c4822c xen/arch/x86/hvm/hvm.c
--- a/xen/arch/x86/hvm/hvm.c Mon Sep 17 13:33:09 2007 +0100
+++ b/xen/arch/x86/hvm/hvm.c Tue Sep 18 11:49:42 2007 +0100
@@ -670,6 +670,412 @@ int hvm_set_cr4(unsigned long value)
gpf:
hvm_inject_exception(TRAP_gp_fault, 0, 0);
return 0;
+}
+
+int hvm_virtual_to_linear_addr(
+ enum x86_segment seg,
+ struct segment_register *reg,
+ unsigned long offset,
+ unsigned int bytes,
+ enum hvm_access_type access_type,
+ unsigned int addr_size,
+ unsigned long *linear_addr)
+{
+ unsigned long addr = offset;
+ uint32_t last_byte;
+
+ if ( addr_size != 64 )
+ {
+ /*
+ * COMPATIBILITY MODE: Apply segment checks and add base.
+ */
+
+ switch ( access_type )
+ {
+ case hvm_access_read:
+ if ( (reg->attr.fields.type & 0xa) == 0x8 )
+ goto gpf; /* execute-only code segment */
+ break;
+ case hvm_access_write:
+ if ( (reg->attr.fields.type & 0xa) != 0x2 )
+ goto gpf; /* not a writable data segment */
+ break;
+ default:
+ break;
+ }
+
+ last_byte = offset + bytes - 1;
+
+ /* Is this a grows-down data segment? Special limit check if so. */
+ if ( (reg->attr.fields.type & 0xc) == 0x4 )
+ {
+ /* Is upper limit 0xFFFF or 0xFFFFFFFF? */
+ if ( !reg->attr.fields.db )
+ last_byte = (uint16_t)last_byte;
+
+ /* Check first byte and last byte against respective bounds. */
+ if ( (offset <= reg->limit) || (last_byte < offset) )
+ goto gpf;
+ }
+ else if ( (last_byte > reg->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 + reg->base);
+ }
+ else
+ {
+ /*
+ * LONG MODE: FS and GS add segment base. Addresses must be canonical.
+ */
+
+ if ( (seg == x86_seg_fs) || (seg == x86_seg_gs) )
+ addr += reg->base;
+
+ if ( !is_canonical_address(addr) )
+ goto gpf;
+ }
+
+ *linear_addr = addr;
+ return 1;
+
+ gpf:
+ return 0;
+}
+
+static void *hvm_map(unsigned long va, int size)
+{
+ unsigned long gfn, mfn;
+ p2m_type_t p2mt;
+
+ if ( ((va & ~PAGE_MASK) + size) > PAGE_SIZE )
+ {
+ hvm_inject_exception(TRAP_page_fault, PFEC_write_access,
+ (va + PAGE_SIZE - 1) & PAGE_MASK);
+ return NULL;
+ }
+
+ gfn = paging_gva_to_gfn(current, va);
+ mfn = mfn_x(gfn_to_mfn_current(gfn, &p2mt));
+ if ( !p2m_is_ram(p2mt) )
+ {
+ hvm_inject_exception(TRAP_page_fault, PFEC_write_access, va);
+ return NULL;
+ }
+
+ ASSERT(mfn_valid(mfn));
+
+ paging_mark_dirty(current->domain, mfn);
+
+ return (char *)map_domain_page(mfn) + (va & ~PAGE_MASK);
+}
+
+static void hvm_unmap(void *p)
+{
+ if ( p )
+ unmap_domain_page(p);
+}
+
+static int hvm_load_segment_selector(
+ struct vcpu *v, enum x86_segment seg, uint16_t sel)
+{
+ struct segment_register desctab, cs, segr;
+ struct desc_struct *pdesc, desc;
+ u8 dpl, rpl, cpl;
+ int fault_type = TRAP_invalid_tss;
+
+ /* NULL selector? */
+ if ( (sel & 0xfffc) == 0 )
+ {
+ if ( (seg == x86_seg_cs) || (seg == x86_seg_ss) )
+ goto fail;
+ memset(&segr, 0, sizeof(segr));
+ hvm_set_segment_register(v, seg, &segr);
+ return 0;
+ }
+
+ /* LDT descriptor must be in the GDT. */
+ if ( (seg == x86_seg_ldtr) && (sel & 4) )
+ goto fail;
+
+ hvm_get_segment_register(v, x86_seg_cs, &cs);
+ hvm_get_segment_register(
+ v, (sel & 4) ? x86_seg_ldtr : x86_seg_gdtr, &desctab);
+
+ /* Check against descriptor table limit. */
+ if ( ((sel & 0xfff8) + 7) > desctab.limit )
+ goto fail;
+
+ pdesc = hvm_map(desctab.base + (sel & 0xfff8), 8);
+ if ( pdesc == NULL )
+ goto hvm_map_fail;
+
+ do {
+ desc = *pdesc;
+
+ /* Segment present in memory? */
+ if ( !(desc.b & (1u<<15)) )
+ {
+ fault_type = TRAP_no_segment;
+ goto unmap_and_fail;
+ }
+
+ /* LDT descriptor is a system segment. All others are code/data. */
+ if ( (desc.b & (1u<<12)) == ((seg == x86_seg_ldtr) << 12) )
+ goto unmap_and_fail;
+
+ dpl = (desc.b >> 13) & 3;
+ rpl = sel & 3;
+ cpl = cs.sel & 3;
+
+ switch ( seg )
+ {
+ case x86_seg_cs:
+ /* Code segment? */
+ if ( !(desc.b & (1u<<11)) )
+ goto unmap_and_fail;
+ /* Non-conforming segment: check DPL against RPL. */
+ if ( ((desc.b & (6u<<9)) != 6) && (dpl != rpl) )
+ goto unmap_and_fail;
+ break;
+ case x86_seg_ss:
+ /* Writable data segment? */
+ if ( (desc.b & (5u<<9)) != (1u<<9) )
+ goto unmap_and_fail;
+ if ( (dpl != cpl) || (dpl != rpl) )
+ goto unmap_and_fail;
+ break;
+ case x86_seg_ldtr:
+ /* LDT system segment? */
+ if ( (desc.b & (15u<<8)) != (2u<<8) )
+ goto unmap_and_fail;
+ goto skip_accessed_flag;
+ default:
+ /* Readable code or data segment? */
+ if ( (desc.b & (5u<<9)) == (4u<<9) )
+ goto unmap_and_fail;
+ /* Non-conforming segment: check DPL against RPL and CPL. */
+ if ( ((desc.b & (6u<<9)) != 6) && ((dpl < cpl) || (dpl < rpl)) )
+ goto unmap_and_fail;
+ break;
+ }
+ } while ( !(desc.b & 0x100) && /* Ensure Accessed flag is set */
+ (cmpxchg(&pdesc->b, desc.b, desc.b | 0x100) != desc.b) );
+
+ /* Force the Accessed flag in our local copy. */
+ desc.b |= 0x100;
+
+ skip_accessed_flag:
+ hvm_unmap(pdesc);
+
+ segr.base = (((desc.b << 0) & 0xff000000u) |
+ ((desc.b << 16) & 0x00ff0000u) |
+ ((desc.a >> 16) & 0x0000ffffu));
+ segr.attr.bytes = (((desc.b >> 8) & 0x00ffu) |
+ ((desc.b >> 12) & 0x0f00u));
+ segr.limit = (desc.b & 0x000f0000u) | (desc.a & 0x0000ffffu);
+ if ( segr.attr.fields.g )
+ segr.limit = (segr.limit << 12) | 0xfffu;
+ segr.sel = sel;
+ hvm_set_segment_register(v, seg, &segr);
+
+ return 0;
+
+ unmap_and_fail:
+ hvm_unmap(pdesc);
+ fail:
+ hvm_inject_exception(fault_type, sel & 0xfffc, 0);
+ hvm_map_fail:
+ return 1;
+}
+
+void hvm_task_switch(
+ uint16_t tss_sel, enum hvm_task_switch_reason taskswitch_reason,
+ int32_t errcode)
+{
+ struct vcpu *v = current;
+ struct cpu_user_regs *regs = guest_cpu_user_regs();
+ struct segment_register gdt, tr, prev_tr, segr;
+ struct desc_struct *optss_desc = NULL, *nptss_desc = NULL, tss_desc;
+ unsigned long eflags;
+ int exn_raised;
+ struct {
+ u16 back_link,__blh;
+ u32 esp0;
+ u16 ss0, _0;
+ u32 esp1;
+ u16 ss1, _1;
+ u32 esp2;
+ u16 ss2, _2;
+ u32 cr3, eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi;
+ u16 es, _3, cs, _4, ss, _5, ds, _6, fs, _7, gs, _8, ldt, _9;
+ u16 trace, iomap;
+ } *ptss, tss;
+
+ hvm_get_segment_register(v, x86_seg_gdtr, &gdt);
+ hvm_get_segment_register(v, x86_seg_tr, &prev_tr);
+
+ if ( ((tss_sel & 0xfff8) + 7) > gdt.limit )
+ {
+ hvm_inject_exception((taskswitch_reason == TSW_iret) ?
+ TRAP_invalid_tss : TRAP_gp_fault,
+ tss_sel & 0xfff8, 0);
+ goto out;
+ }
+
+ optss_desc = hvm_map(gdt.base + (prev_tr.sel & 0xfff8), 8);
+ if ( optss_desc == NULL )
+ goto out;
+
+ nptss_desc = hvm_map(gdt.base + (tss_sel & 0xfff8), 8);
+ if ( nptss_desc == NULL )
+ goto out;
+
+ tss_desc = *nptss_desc;
+ tr.sel = tss_sel;
+ tr.base = (((tss_desc.b << 0) & 0xff000000u) |
+ ((tss_desc.b << 16) & 0x00ff0000u) |
+ ((tss_desc.a >> 16) & 0x0000ffffu));
+ tr.limit = (tss_desc.b & 0x000f0000u) | (tss_desc.a & 0x0000ffffu);
+ tr.attr.bytes = (((tss_desc.b >> 8) & 0x00ffu) |
+ ((tss_desc.b >> 20) & 0x0f00u));
+
+ if ( !tr.attr.fields.p )
+ {
+ hvm_inject_exception(TRAP_no_segment, tss_sel & 0xfff8, 0);
+ goto out;
+ }
+
+ if ( tr.attr.fields.type != ((taskswitch_reason == TSW_iret) ? 0xb : 0x9) )
+ {
+ hvm_inject_exception(
+ (taskswitch_reason == TSW_iret) ? TRAP_invalid_tss : TRAP_gp_fault,
+ tss_sel & 0xfff8, 0);
+ goto out;
+ }
+
+ if ( !tr.attr.fields.g && (tr.limit < (sizeof(tss)-1)) )
+ {
+ hvm_inject_exception(TRAP_invalid_tss, tss_sel & 0xfff8, 0);
+ goto out;
+ }
+
+ hvm_store_cpu_guest_regs(v, regs, NULL);
+
+ ptss = hvm_map(prev_tr.base, sizeof(tss));
+ if ( ptss == NULL )
+ goto out;
+
+ eflags = regs->eflags;
+ if ( taskswitch_reason == TSW_iret )
+ eflags &= ~X86_EFLAGS_NT;
+
+ ptss->cr3 = v->arch.hvm_vcpu.guest_cr[3];
+ ptss->eip = regs->eip;
+ ptss->eflags = eflags;
+ ptss->eax = regs->eax;
+ ptss->ecx = regs->ecx;
+ ptss->edx = regs->edx;
+ ptss->ebx = regs->ebx;
+ ptss->esp = regs->esp;
+ ptss->ebp = regs->ebp;
+ ptss->esi = regs->esi;
+ ptss->edi = regs->edi;
+
+ hvm_get_segment_register(v, x86_seg_es, &segr);
+ ptss->es = segr.sel;
+ hvm_get_segment_register(v, x86_seg_cs, &segr);
+ ptss->cs = segr.sel;
+ hvm_get_segment_register(v, x86_seg_ss, &segr);
+ ptss->ss = segr.sel;
+ hvm_get_segment_register(v, x86_seg_ds, &segr);
+ ptss->ds = segr.sel;
+ hvm_get_segment_register(v, x86_seg_fs, &segr);
+ ptss->fs = segr.sel;
+ hvm_get_segment_register(v, x86_seg_gs, &segr);
+ ptss->gs = segr.sel;
+ hvm_get_segment_register(v, x86_seg_ldtr, &segr);
+ ptss->ldt = segr.sel;
+
+ hvm_unmap(ptss);
+
+ ptss = hvm_map(tr.base, sizeof(tss));
+ if ( ptss == NULL )
+ goto out;
+
+ if ( !hvm_set_cr3(ptss->cr3) )
+ {
+ hvm_unmap(ptss);
+ goto out;
+ }
+
+ regs->eip = ptss->eip;
+ regs->eflags = ptss->eflags;
+ regs->eax = ptss->eax;
+ regs->ecx = ptss->ecx;
+ regs->edx = ptss->edx;
+ regs->ebx = ptss->ebx;
+ regs->esp = ptss->esp;
+ regs->ebp = ptss->ebp;
+ regs->esi = ptss->esi;
+ regs->edi = ptss->edi;
+
+ if ( (taskswitch_reason == TSW_call_or_int) )
+ {
+ regs->eflags |= X86_EFLAGS_NT;
+ ptss->back_link = prev_tr.sel;
+ }
+
+ exn_raised = 0;
+ if ( hvm_load_segment_selector(v, x86_seg_es, ptss->es) ||
+ hvm_load_segment_selector(v, x86_seg_cs, ptss->cs) ||
+ hvm_load_segment_selector(v, x86_seg_ss, ptss->ss) ||
+ hvm_load_segment_selector(v, x86_seg_ds, ptss->ds) ||
+ hvm_load_segment_selector(v, x86_seg_fs, ptss->fs) ||
+ hvm_load_segment_selector(v, x86_seg_gs, ptss->gs) ||
+ hvm_load_segment_selector(v, x86_seg_ldtr, ptss->ldt) )
+ exn_raised = 1;
+
+ if ( (ptss->trace & 1) && !exn_raised )
+ hvm_inject_exception(TRAP_debug, tss_sel & 0xfff8, 0);
+
+ hvm_unmap(ptss);
+
+ tr.attr.fields.type = 0xb; /* busy 32-bit tss */
+ hvm_set_segment_register(v, x86_seg_tr, &tr);
+ paging_update_cr3(v);
+
+ v->arch.hvm_vcpu.guest_cr[0] |= X86_CR0_TS;
+ hvm_update_guest_cr(v, 0);
+
+ if ( (taskswitch_reason == TSW_iret) ||
+ (taskswitch_reason == TSW_jmp) )
+ clear_bit(41, optss_desc); /* clear B flag of old task */
+
+ if ( taskswitch_reason != TSW_iret )
+ set_bit(41, nptss_desc); /* set B flag of new task */
+
+ if ( errcode >= 0 )
+ {
+ struct segment_register reg;
+ unsigned long linear_addr;
+ regs->esp -= 4;
+ hvm_get_segment_register(current, x86_seg_ss, ®);
+ /* Todo: do not ignore access faults here. */
+ if ( hvm_virtual_to_linear_addr(x86_seg_ss, ®, regs->esp,
+ 4, hvm_access_write, 32,
+ &linear_addr) )
+ hvm_copy_to_guest_virt(linear_addr, &errcode, 4);
+ }
+
+ hvm_load_cpu_guest_regs(v, regs);
+
+ out:
+ hvm_unmap(optss_desc);
+ hvm_unmap(nptss_desc);
}
/*
diff -r 49700bb716bb -r 35fb20c4822c xen/arch/x86/hvm/svm/svm.c
--- a/xen/arch/x86/hvm/svm/svm.c Mon Sep 17 13:33:09 2007 +0100
+++ b/xen/arch/x86/hvm/svm/svm.c Tue Sep 18 11:49:42 2007 +0100
@@ -648,6 +648,8 @@ static void svm_get_segment_register(str
{
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
+ ASSERT(v == current);
+
switch ( seg )
{
case x86_seg_cs:
@@ -694,10 +696,13 @@ static void svm_set_segment_register(str
{
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
+ ASSERT(v == current);
+
switch ( seg )
{
case x86_seg_cs:
memcpy(&vmcb->cs, reg, sizeof(*reg));
+ guest_cpu_user_regs()->cs = reg->sel;
break;
case x86_seg_ds:
memcpy(&vmcb->ds, reg, sizeof(*reg));
@@ -717,6 +722,7 @@ static void svm_set_segment_register(str
break;
case x86_seg_ss:
memcpy(&vmcb->ss, reg, sizeof(*reg));
+ guest_cpu_user_regs()->ss = reg->sel;
break;
case x86_seg_tr:
svm_sync_vmcb(v);
@@ -2299,12 +2305,20 @@ asmlinkage void svm_vmexit_handler(struc
svm_vmexit_do_invd(v);
break;
- case VMEXIT_GDTR_WRITE:
- printk("WRITE to GDTR\n");
- break;
-
- case VMEXIT_TASK_SWITCH:
- goto exit_and_crash;
+ case VMEXIT_TASK_SWITCH: {
+ enum hvm_task_switch_reason reason;
+ int32_t errcode = -1;
+ if ( (vmcb->exitinfo2 >> 36) & 1 )
+ reason = TSW_iret;
+ else if ( (vmcb->exitinfo2 >> 38) & 1 )
+ reason = TSW_jmp;
+ else
+ reason = TSW_call_or_int;
+ if ( (vmcb->exitinfo2 >> 44) & 1 )
+ errcode = (uint32_t)vmcb->exitinfo2;
+ hvm_task_switch((uint16_t)vmcb->exitinfo1, reason, errcode);
+ break;
+ }
case VMEXIT_CPUID:
svm_vmexit_do_cpuid(vmcb, regs);
diff -r 49700bb716bb -r 35fb20c4822c xen/arch/x86/hvm/vmx/vmx.c
--- a/xen/arch/x86/hvm/vmx/vmx.c Mon Sep 17 13:33:09 2007 +0100
+++ b/xen/arch/x86/hvm/vmx/vmx.c Tue Sep 18 11:49:42 2007 +0100
@@ -885,7 +885,7 @@ static void vmx_get_segment_register(str
static void vmx_get_segment_register(struct vcpu *v, enum x86_segment seg,
struct segment_register *reg)
{
- u16 attr = 0;
+ uint32_t attr = 0;
ASSERT(v == current);
@@ -960,12 +960,16 @@ static void vmx_set_segment_register(str
static void vmx_set_segment_register(struct vcpu *v, enum x86_segment seg,
struct segment_register *reg)
{
- u16 attr;
+ uint32_t attr;
ASSERT(v == current);
attr = reg->attr.bytes;
attr = ((attr & 0xf00) << 4) | (attr & 0xff);
+
+ /* Not-present must mean unusable. */
+ if ( !reg->attr.fields.p )
+ attr |= (1u << 16);
switch ( seg )
{
@@ -974,6 +978,7 @@ static void vmx_set_segment_register(str
__vmwrite(GUEST_CS_LIMIT, reg->limit);
__vmwrite(GUEST_CS_BASE, reg->base);
__vmwrite(GUEST_CS_AR_BYTES, attr);
+ guest_cpu_user_regs()->cs = reg->sel;
break;
case x86_seg_ds:
__vmwrite(GUEST_DS_SELECTOR, reg->sel);
@@ -1004,6 +1009,7 @@ static void vmx_set_segment_register(str
__vmwrite(GUEST_SS_LIMIT, reg->limit);
__vmwrite(GUEST_SS_BASE, reg->base);
__vmwrite(GUEST_SS_AR_BYTES, attr);
+ guest_cpu_user_regs()->ss = reg->sel;
break;
case x86_seg_tr:
__vmwrite(GUEST_TR_SELECTOR, reg->sel);
@@ -2668,7 +2674,8 @@ asmlinkage void vmx_vmexit_handler(struc
/* Event delivery caused this intercept? Queue for redelivery. */
idtv_info = __vmread(IDT_VECTORING_INFO);
- if ( unlikely(idtv_info & INTR_INFO_VALID_MASK) )
+ if ( unlikely(idtv_info & INTR_INFO_VALID_MASK) &&
+ (exit_reason != EXIT_REASON_TASK_SWITCH) )
{
if ( hvm_event_needs_reinjection((idtv_info>>8)&7, idtv_info&0xff) )
{
@@ -2785,8 +2792,19 @@ asmlinkage void vmx_vmexit_handler(struc
__vmwrite(CPU_BASED_VM_EXEC_CONTROL,
v->arch.hvm_vmx.exec_control);
break;
- case EXIT_REASON_TASK_SWITCH:
- goto exit_and_crash;
+ case EXIT_REASON_TASK_SWITCH: {
+ const enum hvm_task_switch_reason reasons[] = {
+ TSW_call_or_int, TSW_iret, TSW_jmp, TSW_call_or_int };
+ int32_t errcode = -1;
+ exit_qualification = __vmread(EXIT_QUALIFICATION);
+ if ( (idtv_info & INTR_INFO_VALID_MASK) &&
+ (idtv_info & INTR_INFO_DELIVER_CODE_MASK) )
+ errcode = __vmread(IDT_VECTORING_ERROR_CODE);
+ hvm_task_switch((uint16_t)exit_qualification,
+ reasons[(exit_qualification >> 30) & 3],
+ errcode);
+ break;
+ }
case EXIT_REASON_CPUID:
inst_len = __get_instruction_length(); /* Safe: CPUID */
__update_guest_eip(inst_len);
diff -r 49700bb716bb -r 35fb20c4822c xen/arch/x86/mm/shadow/common.c
--- a/xen/arch/x86/mm/shadow/common.c Mon Sep 17 13:33:09 2007 +0100
+++ b/xen/arch/x86/mm/shadow/common.c Tue Sep 18 11:49:42 2007 +0100
@@ -101,7 +101,7 @@ int _shadow_mode_refcounts(struct domain
/* x86 emulator support for the shadow code
*/
-struct segment_register *hvm_get_seg_reg(
+static struct segment_register *hvm_get_seg_reg(
enum x86_segment seg, struct sh_emulate_ctxt *sh_ctxt)
{
struct segment_register *seg_reg = &sh_ctxt->seg_reg[seg];
@@ -109,10 +109,6 @@ struct segment_register *hvm_get_seg_reg
hvm_get_segment_register(current, seg, seg_reg);
return seg_reg;
}
-
-enum hvm_access_type {
- hvm_access_insn_fetch, hvm_access_read, hvm_access_write
-};
static int hvm_translate_linear_addr(
enum x86_segment seg,
@@ -123,76 +119,18 @@ static int hvm_translate_linear_addr(
unsigned long *paddr)
{
struct segment_register *reg = hvm_get_seg_reg(seg, sh_ctxt);
- unsigned long limit, addr = offset;
- uint32_t last_byte;
-
- if ( sh_ctxt->ctxt.addr_size != 64 )
- {
- /*
- * COMPATIBILITY MODE: Apply segment checks and add base.
- */
-
- switch ( access_type )
- {
- case hvm_access_read:
- if ( (reg->attr.fields.type & 0xa) == 0x8 )
- goto gpf; /* execute-only code segment */
- break;
- case hvm_access_write:
- if ( (reg->attr.fields.type & 0xa) != 0x2 )
- goto gpf; /* not a writable data segment */
- break;
- default:
- break;
- }
-
- /* Calculate the segment limit, including granularity flag. */
- limit = reg->limit;
- if ( reg->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 ( (reg->attr.fields.type & 0xc) == 0x4 )
- {
- /* Is upper limit 0xFFFF or 0xFFFFFFFF? */
- if ( !reg->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 + reg->base);
- }
- else
- {
- /*
- * LONG MODE: FS and GS add segment base. Addresses must be canonical.
- */
-
- if ( (seg == x86_seg_fs) || (seg == x86_seg_gs) )
- addr += reg->base;
-
- if ( !is_canonical_address(addr) )
- goto gpf;
- }
-
- *paddr = addr;
- return 0;
-
- gpf:
- /* Inject #GP(0). */
- hvm_inject_exception(TRAP_gp_fault, 0, 0);
- return X86EMUL_EXCEPTION;
+ int okay;
+
+ okay = hvm_virtual_to_linear_addr(
+ seg, reg, offset, bytes, access_type, sh_ctxt->ctxt.addr_size, paddr);
+
+ if ( !okay )
+ {
+ hvm_inject_exception(TRAP_gp_fault, 0, 0);
+ return X86EMUL_EXCEPTION;
+ }
+
+ return 0;
}
static int
diff -r 49700bb716bb -r 35fb20c4822c xen/include/asm-x86/hvm/hvm.h
--- a/xen/include/asm-x86/hvm/hvm.h Mon Sep 17 13:33:09 2007 +0100
+++ b/xen/include/asm-x86/hvm/hvm.h Tue Sep 18 11:49:42 2007 +0100
@@ -362,4 +362,21 @@ static inline void hvm_cpu_down(void)
hvm_funcs.cpu_down();
}
+enum hvm_task_switch_reason { TSW_jmp, TSW_iret, TSW_call_or_int };
+void hvm_task_switch(
+ uint16_t tss_sel, enum hvm_task_switch_reason taskswitch_reason,
+ int32_t errcode);
+
+enum hvm_access_type {
+ hvm_access_insn_fetch, hvm_access_read, hvm_access_write
+};
+int hvm_virtual_to_linear_addr(
+ enum x86_segment seg,
+ struct segment_register *reg,
+ unsigned long offset,
+ unsigned int bytes,
+ enum hvm_access_type access_type,
+ unsigned int addr_size,
+ unsigned long *linear_addr);
+
#endif /* __ASM_X86_HVM_HVM_H__ */
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|