[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [xen-unstable] x86_emulate: Check I/O port accesses.



# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1206638722 0
# Node ID e7abfeee280832606426baed80190a9e337ccf4e
# Parent  892a20f824a7aa5c5ed57ce80fc61f1dffd4b4d2
x86_emulate: Check I/O port accesses.
Implements both CPL/IOPL and TSS-bitmap checks.
Requires changes to read/write callback hooks to disable user-access
checks when walking pagetables on behalf of GDT/LDT/TSS accesses.
Signed-off-by: Keir Fraser <keir.fraser@xxxxxxxxxx>
---
 xen/arch/x86/hvm/emulate.c        |   62 +++++++++++------
 xen/arch/x86/hvm/hvm.c            |  133 +++++++++++++++++---------------------
 xen/arch/x86/hvm/svm/emulate.c    |    4 -
 xen/arch/x86/mm/shadow/common.c   |    8 +-
 xen/arch/x86/x86_emulate.c        |   65 ++++++++++++++++--
 xen/include/asm-x86/hvm/support.h |   18 +++--
 6 files changed, 177 insertions(+), 113 deletions(-)

diff -r 892a20f824a7 -r e7abfeee2808 xen/arch/x86/hvm/emulate.c
--- a/xen/arch/x86/hvm/emulate.c        Thu Mar 27 17:14:41 2008 +0000
+++ b/xen/arch/x86/hvm/emulate.c        Thu Mar 27 17:25:22 2008 +0000
@@ -94,19 +94,18 @@ static int hvmemul_do_mmio(
  * Convert addr from linear to physical form, valid over the range
  * [addr, addr + *reps * bytes_per_rep]. *reps is adjusted according to
  * the valid computed range. It is always >0 when X86EMUL_OKAY is returned.
+ * @pfec indicates the access checks to be performed during page-table walks.
  */
 static int hvmemul_linear_to_phys(
     unsigned long addr,
     paddr_t *paddr,
     unsigned int bytes_per_rep,
     unsigned long *reps,
-    enum hvm_access_type access_type,
+    uint32_t pfec,
     struct hvm_emulate_ctxt *hvmemul_ctxt)
 {
     struct vcpu *curr = current;
     unsigned long pfn, npfn, done, todo, i;
-    struct segment_register *sreg;
-    uint32_t pfec;
 
     /* Clip repetitions to a sensible maximum. */
     *reps = min_t(unsigned long, *reps, 4096);
@@ -119,14 +118,6 @@ static int hvmemul_linear_to_phys(
     }
 
     *paddr = addr & ~PAGE_MASK;
-
-    /* Gather access-type information for the page walks. */
-    sreg = hvmemul_get_seg_reg(x86_seg_ss, hvmemul_ctxt);
-    pfec = PFEC_page_present;
-    if ( sreg->attr.fields.dpl == 3 )
-        pfec |= PFEC_user_mode;
-    if ( access_type == hvm_access_write )
-        pfec |= PFEC_write_access;
 
     /* Get the first PFN in the range. */
     if ( (pfn = paging_gva_to_gfn(curr, addr, &pfec)) == INVALID_GFN )
@@ -216,6 +207,7 @@ static int __hvmemul_read(
 {
     struct vcpu *curr = current;
     unsigned long addr;
+    uint32_t pfec = PFEC_page_present;
     paddr_t gpa;
     int rc;
 
@@ -237,9 +229,13 @@ static int __hvmemul_read(
             return hvmemul_do_mmio(gpa, 1, bytes, 0, IOREQ_READ, 0, 0, val);
     }
 
+    if ( (seg != x86_seg_none) &&
+         (hvmemul_ctxt->seg_reg[x86_seg_ss].attr.fields.dpl == 3) )
+        pfec |= PFEC_user_mode;
+
     rc = ((access_type == hvm_access_insn_fetch) ?
-          hvm_fetch_from_guest_virt(val, addr, bytes) :
-          hvm_copy_from_guest_virt(val, addr, bytes));
+          hvm_fetch_from_guest_virt(val, addr, bytes, pfec) :
+          hvm_copy_from_guest_virt(val, addr, bytes, pfec));
     if ( rc == HVMCOPY_bad_gva_to_gfn )
         return X86EMUL_EXCEPTION;
 
@@ -251,7 +247,7 @@ static int __hvmemul_read(
             return X86EMUL_UNHANDLEABLE;
 
         rc = hvmemul_linear_to_phys(
-            addr, &gpa, bytes, &reps, access_type, hvmemul_ctxt);
+            addr, &gpa, bytes, &reps, pfec, hvmemul_ctxt);
         if ( rc != X86EMUL_OKAY )
             return rc;
 
@@ -307,6 +303,7 @@ static int hvmemul_write(
         container_of(ctxt, struct hvm_emulate_ctxt, ctxt);
     struct vcpu *curr = current;
     unsigned long addr;
+    uint32_t pfec = PFEC_page_present | PFEC_write_access;
     paddr_t gpa;
     int rc;
 
@@ -325,7 +322,11 @@ static int hvmemul_write(
                                    0, 0, NULL);
     }
 
-    rc = hvm_copy_to_guest_virt(addr, &val, bytes);
+    if ( (seg != x86_seg_none) &&
+         (hvmemul_ctxt->seg_reg[x86_seg_ss].attr.fields.dpl == 3) )
+        pfec |= PFEC_user_mode;
+
+    rc = hvm_copy_to_guest_virt(addr, &val, bytes, pfec);
     if ( rc == HVMCOPY_bad_gva_to_gfn )
         return X86EMUL_EXCEPTION;
 
@@ -334,7 +335,7 @@ static int hvmemul_write(
         unsigned long reps = 1;
 
         rc = hvmemul_linear_to_phys(
-            addr, &gpa, bytes, &reps, hvm_access_write, hvmemul_ctxt);
+            addr, &gpa, bytes, &reps, pfec, hvmemul_ctxt);
         if ( rc != X86EMUL_OKAY )
             return rc;
 
@@ -367,6 +368,7 @@ static int hvmemul_rep_ins(
     struct hvm_emulate_ctxt *hvmemul_ctxt =
         container_of(ctxt, struct hvm_emulate_ctxt, ctxt);
     unsigned long addr;
+    uint32_t pfec = PFEC_page_present | PFEC_write_access;
     paddr_t gpa;
     int rc;
 
@@ -376,8 +378,11 @@ static int hvmemul_rep_ins(
     if ( rc != X86EMUL_OKAY )
         return rc;
 
+    if ( hvmemul_ctxt->seg_reg[x86_seg_ss].attr.fields.dpl == 3 )
+        pfec |= PFEC_user_mode;
+
     rc = hvmemul_linear_to_phys(
-        addr, &gpa, bytes_per_rep, reps, hvm_access_write, hvmemul_ctxt);
+        addr, &gpa, bytes_per_rep, reps, pfec, hvmemul_ctxt);
     if ( rc != X86EMUL_OKAY )
         return rc;
 
@@ -396,6 +401,7 @@ static int hvmemul_rep_outs(
     struct hvm_emulate_ctxt *hvmemul_ctxt =
         container_of(ctxt, struct hvm_emulate_ctxt, ctxt);
     unsigned long addr;
+    uint32_t pfec = PFEC_page_present;
     paddr_t gpa;
     int rc;
 
@@ -405,8 +411,11 @@ static int hvmemul_rep_outs(
     if ( rc != X86EMUL_OKAY )
         return rc;
 
+    if ( hvmemul_ctxt->seg_reg[x86_seg_ss].attr.fields.dpl == 3 )
+        pfec |= PFEC_user_mode;
+
     rc = hvmemul_linear_to_phys(
-        addr, &gpa, bytes_per_rep, reps, hvm_access_read, hvmemul_ctxt);
+        addr, &gpa, bytes_per_rep, reps, pfec, hvmemul_ctxt);
     if ( rc != X86EMUL_OKAY )
         return rc;
 
@@ -427,6 +436,7 @@ static int hvmemul_rep_movs(
         container_of(ctxt, struct hvm_emulate_ctxt, ctxt);
     unsigned long saddr, daddr;
     paddr_t sgpa, dgpa;
+    uint32_t pfec = PFEC_page_present;
     p2m_type_t p2mt;
     int rc;
 
@@ -442,13 +452,17 @@ static int hvmemul_rep_movs(
     if ( rc != X86EMUL_OKAY )
         return rc;
 
+    if ( hvmemul_ctxt->seg_reg[x86_seg_ss].attr.fields.dpl == 3 )
+        pfec |= PFEC_user_mode;
+
     rc = hvmemul_linear_to_phys(
-        saddr, &sgpa, bytes_per_rep, reps, hvm_access_read, hvmemul_ctxt);
+        saddr, &sgpa, bytes_per_rep, reps, pfec, hvmemul_ctxt);
     if ( rc != X86EMUL_OKAY )
         return rc;
 
     rc = hvmemul_linear_to_phys(
-        daddr, &dgpa, bytes_per_rep, reps, hvm_access_write, hvmemul_ctxt);
+        daddr, &dgpa, bytes_per_rep, reps,
+        pfec | PFEC_write_access, hvmemul_ctxt);
     if ( rc != X86EMUL_OKAY )
         return rc;
 
@@ -696,7 +710,7 @@ int hvm_emulate_one(
 {
     struct cpu_user_regs *regs = hvmemul_ctxt->ctxt.regs;
     struct vcpu *curr = current;
-    uint32_t new_intr_shadow;
+    uint32_t new_intr_shadow, pfec = PFEC_page_present;
     unsigned long addr;
     int rc;
 
@@ -712,6 +726,9 @@ int hvm_emulate_one(
         hvmemul_ctxt->ctxt.sp_size =
             hvmemul_ctxt->seg_reg[x86_seg_ss].attr.fields.db ? 32 : 16;
     }
+
+    if ( hvmemul_ctxt->seg_reg[x86_seg_ss].attr.fields.dpl == 3 )
+        pfec |= PFEC_user_mode;
 
     hvmemul_ctxt->insn_buf_eip = regs->eip;
     hvmemul_ctxt->insn_buf_bytes =
@@ -720,7 +737,8 @@ int hvm_emulate_one(
             regs->eip, sizeof(hvmemul_ctxt->insn_buf),
             hvm_access_insn_fetch, hvmemul_ctxt->ctxt.addr_size, &addr) &&
          !hvm_fetch_from_guest_virt_nofault(
-             hvmemul_ctxt->insn_buf, addr, sizeof(hvmemul_ctxt->insn_buf)))
+             hvmemul_ctxt->insn_buf, addr,
+             sizeof(hvmemul_ctxt->insn_buf), pfec))
         ? sizeof(hvmemul_ctxt->insn_buf) : 0;
 
     hvmemul_ctxt->exn_pending = 0;
diff -r 892a20f824a7 -r e7abfeee2808 xen/arch/x86/hvm/hvm.c
--- a/xen/arch/x86/hvm/hvm.c    Thu Mar 27 17:14:41 2008 +0000
+++ b/xen/arch/x86/hvm/hvm.c    Thu Mar 27 17:25:22 2008 +0000
@@ -1302,7 +1302,7 @@ void hvm_task_switch(
         goto out;
     }
 
-    if ( !tr.attr.fields.g && (tr.limit < (sizeof(tss)-1)) )
+    if ( tr.limit < (sizeof(tss)-1) )
     {
         hvm_inject_exception(TRAP_invalid_tss, tss_sel & 0xfff8, 0);
         goto out;
@@ -1410,7 +1410,7 @@ void hvm_task_switch(
         if ( hvm_virtual_to_linear_addr(x86_seg_ss, &reg, regs->esp,
                                         4, hvm_access_write, 32,
                                         &linear_addr) )
-            hvm_copy_to_guest_virt_nofault(linear_addr, &errcode, 4);
+            hvm_copy_to_guest_virt_nofault(linear_addr, &errcode, 4, 0);
     }
 
  out:
@@ -1418,60 +1418,31 @@ void hvm_task_switch(
     hvm_unmap(nptss_desc);
 }
 
-/*
- * __hvm_copy():
- *  @buf  = hypervisor buffer
- *  @addr = guest address to copy to/from
- *  @size = number of bytes to copy
- *  @dir  = copy *to* guest (TRUE) or *from* guest (FALSE)?
- *  @virt = addr is *virtual* (TRUE) or *guest physical* (FALSE)?
- *  @fetch = copy is an instruction fetch?
- * Returns number of bytes failed to copy (0 == complete success).
- */
+#define HVMCOPY_from_guest (0u<<0)
+#define HVMCOPY_to_guest   (1u<<0)
+#define HVMCOPY_no_fault   (0u<<1)
+#define HVMCOPY_fault      (1u<<1)
+#define HVMCOPY_phys       (0u<<2)
+#define HVMCOPY_virt       (1u<<2)
 static enum hvm_copy_result __hvm_copy(
-    void *buf, paddr_t addr, int size, int dir, int virt, int fetch)
+    void *buf, paddr_t addr, int size, unsigned int flags, uint32_t pfec)
 {
     struct vcpu *curr = current;
     unsigned long gfn, mfn;
     p2m_type_t p2mt;
     char *p;
-    int count, todo;
-    uint32_t pfec = PFEC_page_present;
-
-    /*
-     * We cannot use hvm_get_segment_register() while executing in
-     * vmx_realmode() as segment register state is cached. Furthermore,
-     * VMREADs on every data access hurts emulation performance.
-     * Hence we do not gather extra PFEC flags if CR0.PG == 0.
-     */
-    if ( !(curr->arch.hvm_vcpu.guest_cr[0] & X86_CR0_PG) )
-        virt = 0;
-
-    if ( virt )
-    {
-        struct segment_register sreg;
-        hvm_get_segment_register(curr, x86_seg_ss, &sreg);
-        if ( sreg.attr.fields.dpl == 3 )
-            pfec |= PFEC_user_mode;
-
-        if ( dir ) 
-            pfec |= PFEC_write_access;
-
-        if ( fetch ) 
-            pfec |= PFEC_insn_fetch;
-    }
-
-    todo = size;
+    int count, todo = size;
+
     while ( todo > 0 )
     {
         count = min_t(int, PAGE_SIZE - (addr & ~PAGE_MASK), todo);
 
-        if ( virt )
+        if ( flags & HVMCOPY_virt )
         {
             gfn = paging_gva_to_gfn(curr, addr, &pfec);
             if ( gfn == INVALID_GFN )
             {
-                if ( virt == 2 ) /* 2 means generate a fault */
+                if ( flags & HVMCOPY_fault )
                     hvm_inject_exception(TRAP_page_fault, pfec, addr);
                 return HVMCOPY_bad_gva_to_gfn;
             }
@@ -1489,16 +1460,18 @@ static enum hvm_copy_result __hvm_copy(
 
         p = (char *)map_domain_page(mfn) + (addr & ~PAGE_MASK);
 
-        if ( dir )
+        if ( flags & HVMCOPY_to_guest )
         {
-            memcpy(p, buf, count); /* dir == TRUE:  *to* guest */
+            memcpy(p, buf, count);
             paging_mark_dirty(curr->domain, mfn);
         }
         else
-            memcpy(buf, p, count); /* dir == FALSE: *from guest */
+        {
+            memcpy(buf, p, count);
+        }
 
         unmap_domain_page(p);
-        
+
         addr += count;
         buf  += count;
         todo -= count;
@@ -1510,56 +1483,73 @@ enum hvm_copy_result hvm_copy_to_guest_p
 enum hvm_copy_result hvm_copy_to_guest_phys(
     paddr_t paddr, void *buf, int size)
 {
-    return __hvm_copy(buf, paddr, size, 1, 0, 0);
+    return __hvm_copy(buf, paddr, size,
+                      HVMCOPY_to_guest | HVMCOPY_fault | HVMCOPY_phys,
+                      0);
 }
 
 enum hvm_copy_result hvm_copy_from_guest_phys(
     void *buf, paddr_t paddr, int size)
 {
-    return __hvm_copy(buf, paddr, size, 0, 0, 0);
+    return __hvm_copy(buf, paddr, size,
+                      HVMCOPY_from_guest | HVMCOPY_fault | HVMCOPY_phys,
+                      0);
 }
 
 enum hvm_copy_result hvm_copy_to_guest_virt(
-    unsigned long vaddr, void *buf, int size)
-{
-    return __hvm_copy(buf, vaddr, size, 1, 2, 0);
+    unsigned long vaddr, void *buf, int size, uint32_t pfec)
+{
+    return __hvm_copy(buf, vaddr, size,
+                      HVMCOPY_to_guest | HVMCOPY_fault | HVMCOPY_virt,
+                      PFEC_page_present | PFEC_write_access | pfec);
 }
 
 enum hvm_copy_result hvm_copy_from_guest_virt(
-    void *buf, unsigned long vaddr, int size)
-{
-    return __hvm_copy(buf, vaddr, size, 0, 2, 0);
+    void *buf, unsigned long vaddr, int size, uint32_t pfec)
+{
+    return __hvm_copy(buf, vaddr, size,
+                      HVMCOPY_from_guest | HVMCOPY_fault | HVMCOPY_virt,
+                      PFEC_page_present | pfec);
 }
 
 enum hvm_copy_result hvm_fetch_from_guest_virt(
-    void *buf, unsigned long vaddr, int size)
-{
-    return __hvm_copy(buf, vaddr, size, 0, 2, hvm_nx_enabled(current));
+    void *buf, unsigned long vaddr, int size, uint32_t pfec)
+{
+    if ( hvm_nx_enabled(current) )
+        pfec |= PFEC_insn_fetch;
+    return __hvm_copy(buf, vaddr, size,
+                      HVMCOPY_from_guest | HVMCOPY_fault | HVMCOPY_virt,
+                      PFEC_page_present | pfec);
 }
 
 enum hvm_copy_result hvm_copy_to_guest_virt_nofault(
-    unsigned long vaddr, void *buf, int size)
-{
-    return __hvm_copy(buf, vaddr, size, 1, 1, 0);
+    unsigned long vaddr, void *buf, int size, uint32_t pfec)
+{
+    return __hvm_copy(buf, vaddr, size,
+                      HVMCOPY_to_guest | HVMCOPY_no_fault | HVMCOPY_virt,
+                      PFEC_page_present | PFEC_write_access | pfec);
 }
 
 enum hvm_copy_result hvm_copy_from_guest_virt_nofault(
-    void *buf, unsigned long vaddr, int size)
-{
-    return __hvm_copy(buf, vaddr, size, 0, 1, 0);
+    void *buf, unsigned long vaddr, int size, uint32_t pfec)
+{
+    return __hvm_copy(buf, vaddr, size,
+                      HVMCOPY_from_guest | HVMCOPY_no_fault | HVMCOPY_virt,
+                      PFEC_page_present | pfec);
 }
 
 enum hvm_copy_result hvm_fetch_from_guest_virt_nofault(
-    void *buf, unsigned long vaddr, int size)
-{
-    return __hvm_copy(buf, vaddr, size, 0, 1, hvm_nx_enabled(current));
+    void *buf, unsigned long vaddr, int size, uint32_t pfec)
+{
+    if ( hvm_nx_enabled(current) )
+        pfec |= PFEC_insn_fetch;
+    return __hvm_copy(buf, vaddr, size,
+                      HVMCOPY_from_guest | HVMCOPY_no_fault | HVMCOPY_virt,
+                      PFEC_page_present | pfec);
 }
 
 DEFINE_PER_CPU(int, guest_handles_in_xen_space);
 
-/* Note that copy_{to,from}_user_hvm require the PTE to be writable even
-   when they're only trying to read from it.  The guest is expected to
-   deal with this. */
 unsigned long copy_to_user_hvm(void *to, const void *from, unsigned len)
 {
     int rc;
@@ -1570,7 +1560,8 @@ unsigned long copy_to_user_hvm(void *to,
         return 0;
     }
 
-    rc = hvm_copy_to_guest_virt_nofault((unsigned long)to, (void *)from, len);
+    rc = hvm_copy_to_guest_virt_nofault((unsigned long)to, (void *)from,
+                                        len, 0);
     return rc ? len : 0; /* fake a copy_to_user() return code */
 }
 
@@ -1584,7 +1575,7 @@ unsigned long copy_from_user_hvm(void *t
         return 0;
     }
 
-    rc = hvm_copy_from_guest_virt_nofault(to, (unsigned long)from, len);
+    rc = hvm_copy_from_guest_virt_nofault(to, (unsigned long)from, len, 0);
     return rc ? len : 0; /* fake a copy_from_user() return code */
 }
 
diff -r 892a20f824a7 -r e7abfeee2808 xen/arch/x86/hvm/svm/emulate.c
--- a/xen/arch/x86/hvm/svm/emulate.c    Thu Mar 27 17:14:41 2008 +0000
+++ b/xen/arch/x86/hvm/svm/emulate.c    Thu Mar 27 17:25:22 2008 +0000
@@ -32,9 +32,11 @@ static int inst_copy_from_guest(
 static int inst_copy_from_guest(
     unsigned char *buf, unsigned long guest_eip, int inst_len)
 {
+    struct vmcb_struct *vmcb = current->arch.hvm_svm.vmcb;
+    uint32_t pfec = (vmcb->cpl == 3) ? PFEC_user_mode : 0;
     if ( (inst_len > MAX_INST_LEN) || (inst_len <= 0) )
         return 0;
-    if ( hvm_fetch_from_guest_virt_nofault(buf, guest_eip, inst_len) )
+    if ( hvm_fetch_from_guest_virt_nofault(buf, guest_eip, inst_len, pfec) )
         return 0;
     return inst_len;
 }
diff -r 892a20f824a7 -r e7abfeee2808 xen/arch/x86/mm/shadow/common.c
--- a/xen/arch/x86/mm/shadow/common.c   Thu Mar 27 17:14:41 2008 +0000
+++ b/xen/arch/x86/mm/shadow/common.c   Thu Mar 27 17:25:22 2008 +0000
@@ -152,9 +152,9 @@ hvm_read(enum x86_segment seg,
     *val = 0;
 
     if ( access_type == hvm_access_insn_fetch )
-        rc = hvm_fetch_from_guest_virt(val, addr, bytes);
+        rc = hvm_fetch_from_guest_virt(val, addr, bytes, 0);
     else
-        rc = hvm_copy_from_guest_virt(val, addr, bytes);
+        rc = hvm_copy_from_guest_virt(val, addr, bytes, 0);
 
     switch ( rc )
     {
@@ -416,7 +416,7 @@ struct x86_emulate_ops *shadow_init_emul
             x86_seg_cs, regs->eip, sizeof(sh_ctxt->insn_buf),
             hvm_access_insn_fetch, sh_ctxt, &addr) &&
          !hvm_fetch_from_guest_virt_nofault(
-             sh_ctxt->insn_buf, addr, sizeof(sh_ctxt->insn_buf)))
+             sh_ctxt->insn_buf, addr, sizeof(sh_ctxt->insn_buf), 0))
         ? sizeof(sh_ctxt->insn_buf) : 0;
 
     return &hvm_shadow_emulator_ops;
@@ -444,7 +444,7 @@ void shadow_continue_emulation(struct sh
                     x86_seg_cs, regs->eip, sizeof(sh_ctxt->insn_buf),
                     hvm_access_insn_fetch, sh_ctxt, &addr) &&
                  !hvm_fetch_from_guest_virt_nofault(
-                     sh_ctxt->insn_buf, addr, sizeof(sh_ctxt->insn_buf)))
+                     sh_ctxt->insn_buf, addr, sizeof(sh_ctxt->insn_buf), 0))
                 ? sizeof(sh_ctxt->insn_buf) : 0;
             sh_ctxt->insn_buf_eip = regs->eip;
         }
diff -r 892a20f824a7 -r e7abfeee2808 xen/arch/x86/x86_emulate.c
--- a/xen/arch/x86/x86_emulate.c        Thu Mar 27 17:14:41 2008 +0000
+++ b/xen/arch/x86/x86_emulate.c        Thu Mar 27 17:25:22 2008 +0000
@@ -787,7 +787,7 @@ _mode_iopl(
     int cpl = get_cpl(ctxt, ops);
     if ( cpl == -1 )
         return -1;
-    return ((cpl >= 0) && (cpl <= ((ctxt->regs->eflags >> 12) & 3)));
+    return (cpl <= ((ctxt->regs->eflags >> 12) & 3));
 }
 
 #define mode_ring0() ({                         \
@@ -800,6 +800,50 @@ _mode_iopl(
     fail_if(_iopl < 0);                         \
     _iopl;                                      \
 })
+
+static int ioport_access_check(
+    unsigned int first_port,
+    unsigned int bytes,
+    struct x86_emulate_ctxt *ctxt,
+    struct x86_emulate_ops *ops)
+{
+    unsigned long iobmp;
+    struct segment_register tr;
+    int rc = X86EMUL_OKAY;
+
+    if ( !(ctxt->regs->eflags & EFLG_VM) && mode_iopl() )
+        return X86EMUL_OKAY;
+
+    fail_if(ops->read_segment == NULL);
+    if ( (rc = ops->read_segment(x86_seg_tr, &tr, ctxt)) != 0 )
+        return rc;
+
+    /* Ensure that the TSS is valid and has an io-bitmap-offset field. */
+    if ( !tr.attr.fields.p ||
+         ((tr.attr.fields.type & 0xd) != 0x9) ||
+         (tr.limit < 0x67) )
+        goto raise_exception;
+
+    if ( (rc = ops->read(x86_seg_none, tr.base + 0x66, &iobmp, 2, ctxt)) )
+        return rc;
+
+    /* Ensure TSS includes two bytes including byte containing first port. */
+    iobmp += first_port / 8;
+    if ( tr.limit <= iobmp )
+        goto raise_exception;
+
+    if ( (rc = ops->read(x86_seg_none, tr.base + iobmp, &iobmp, 2, ctxt)) )
+        return rc;
+    if ( (iobmp & (((1<<bytes)-1) << (first_port&7))) != 0 )
+        goto raise_exception;
+
+ done:
+    return rc;
+
+ raise_exception:
+    fail_if(ops->inject_hw_exception == NULL);
+    return ops->inject_hw_exception(EXC_GP, 0, ctxt) ? : X86EMUL_EXCEPTION;
+}
 
 static int
 in_realmode(
@@ -2265,12 +2309,14 @@ x86_emulate(
 
     case 0x6c ... 0x6d: /* ins %dx,%es:%edi */ {
         unsigned long nr_reps = get_rep_prefix();
+        unsigned int port = (uint16_t)_regs.edx;
         dst.bytes = !(b & 1) ? 1 : (op_bytes == 8) ? 4 : op_bytes;
         dst.mem.seg = x86_seg_es;
         dst.mem.off = truncate_ea(_regs.edi);
+        if ( (rc = ioport_access_check(port, dst.bytes, ctxt, ops)) != 0 )
+            goto done;
         if ( (nr_reps > 1) && (ops->rep_ins != NULL) &&
-             ((rc = ops->rep_ins((uint16_t)_regs.edx, dst.mem.seg,
-                                 dst.mem.off, dst.bytes,
+             ((rc = ops->rep_ins(port, dst.mem.seg, dst.mem.off, dst.bytes,
                                  &nr_reps, ctxt)) != X86EMUL_UNHANDLEABLE) )
         {
             if ( rc != 0 )
@@ -2279,8 +2325,7 @@ x86_emulate(
         else
         {
             fail_if(ops->read_io == NULL);
-            if ( (rc = ops->read_io((uint16_t)_regs.edx, dst.bytes,
-                                    &dst.val, ctxt)) != 0 )
+            if ( (rc = ops->read_io(port, dst.bytes, &dst.val, ctxt)) != 0 )
                 goto done;
             dst.type = OP_MEM;
             nr_reps = 1;
@@ -2294,10 +2339,13 @@ x86_emulate(
 
     case 0x6e ... 0x6f: /* outs %esi,%dx */ {
         unsigned long nr_reps = get_rep_prefix();
+        unsigned int port = (uint16_t)_regs.edx;
         dst.bytes = !(b & 1) ? 1 : (op_bytes == 8) ? 4 : op_bytes;
+        if ( (rc = ioport_access_check(port, dst.bytes, ctxt, ops)) != 0 )
+            goto done;
         if ( (nr_reps > 1) && (ops->rep_outs != NULL) &&
              ((rc = ops->rep_outs(ea.mem.seg, truncate_ea(_regs.esi),
-                                  (uint16_t)_regs.edx, dst.bytes,
+                                  port, dst.bytes,
                                   &nr_reps, ctxt)) != X86EMUL_UNHANDLEABLE) )
         {
             if ( rc != 0 )
@@ -2309,8 +2357,7 @@ x86_emulate(
                                  &dst.val, dst.bytes, ctxt)) != 0 )
                 goto done;
             fail_if(ops->write_io == NULL);
-            if ( (rc = ops->write_io((uint16_t)_regs.edx, dst.bytes,
-                                     dst.val, ctxt)) != 0 )
+            if ( (rc = ops->write_io(port, dst.bytes, dst.val, ctxt)) != 0 )
                 goto done;
             nr_reps = 1;
         }
@@ -2831,6 +2878,8 @@ x86_emulate(
                              ? insn_fetch_type(uint8_t)
                              : (uint16_t)_regs.edx);
         op_bytes = !(b & 1) ? 1 : (op_bytes == 8) ? 4 : op_bytes;
+        if ( (rc = ioport_access_check(port, op_bytes, ctxt, ops)) != 0 )
+            goto done;
         if ( b & 2 )
         {
             /* out */
diff -r 892a20f824a7 -r e7abfeee2808 xen/include/asm-x86/hvm/support.h
--- a/xen/include/asm-x86/hvm/support.h Thu Mar 27 17:14:41 2008 +0000
+++ b/xen/include/asm-x86/hvm/support.h Thu Mar 27 17:25:22 2008 +0000
@@ -99,7 +99,11 @@ enum hvm_copy_result hvm_copy_from_guest
     void *buf, paddr_t paddr, int size);
 
 /*
- * Copy to/from a guest virtual address.
+ * Copy to/from a guest virtual address. @pfec should include PFEC_user_mode
+ * if emulating a user-mode access (CPL=3). All other flags in @pfec are
+ * managed by the called function: it is therefore optional for the caller
+ * to set them.
+ * 
  * Returns:
  *  HVMCOPY_okay: Copy was entirely successful.
  *  HVMCOPY_bad_gfn_to_mfn: Some guest physical address did not map to
@@ -110,22 +114,22 @@ enum hvm_copy_result hvm_copy_from_guest
  *                          for injection into the current HVM VCPU.
  */
 enum hvm_copy_result hvm_copy_to_guest_virt(
-    unsigned long vaddr, void *buf, int size);
+    unsigned long vaddr, void *buf, int size, uint32_t pfec);
 enum hvm_copy_result hvm_copy_from_guest_virt(
-    void *buf, unsigned long vaddr, int size);
+    void *buf, unsigned long vaddr, int size, uint32_t pfec);
 enum hvm_copy_result hvm_fetch_from_guest_virt(
-    void *buf, unsigned long vaddr, int size);
+    void *buf, unsigned long vaddr, int size, uint32_t pfec);
 
 /*
  * As above (copy to/from a guest virtual address), but no fault is generated
  * when HVMCOPY_bad_gva_to_gfn is returned.
  */
 enum hvm_copy_result hvm_copy_to_guest_virt_nofault(
-    unsigned long vaddr, void *buf, int size);
+    unsigned long vaddr, void *buf, int size, uint32_t pfec);
 enum hvm_copy_result hvm_copy_from_guest_virt_nofault(
-    void *buf, unsigned long vaddr, int size);
+    void *buf, unsigned long vaddr, int size, uint32_t pfec);
 enum hvm_copy_result hvm_fetch_from_guest_virt_nofault(
-    void *buf, unsigned long vaddr, int size);
+    void *buf, unsigned long vaddr, int size, uint32_t pfec);
 
 void hvm_print_line(struct vcpu *v, const char c);
 void hlt_timer_fn(void *data);

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.