WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] [PATCH] x86: emulate I/O port access breakpoints

To: <xen-devel@xxxxxxxxxxxxxxxxxxx>
Subject: [Xen-devel] [PATCH] x86: emulate I/O port access breakpoints
From: "Jan Beulich" <jbeulich@xxxxxxxxxx>
Date: Thu, 22 Nov 2007 16:59:46 +0000
Delivery-date: Thu, 22 Nov 2007 08:59:28 -0800
Envelope-to: www-data@xxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
Emulate the trapping on I/O port accesses when emulating IN/OUT.

Also allow 8-byte breakpoints on x86-64 (and on i686 if the hardware
supports them), and tighten the condition for loading debug registers
during context switch.

This patch depends on the prior single step injection patch.

Signed-off-by: Jan Beulich <jbeulich@xxxxxxxxxx>

Index: 2007-11-13/xen/arch/x86/domain.c
===================================================================
--- 2007-11-13.orig/xen/arch/x86/domain.c       2007-11-12 08:47:42.000000000 
+0100
+++ 2007-11-13/xen/arch/x86/domain.c    2007-11-21 09:44:48.000000000 +0100
@@ -42,6 +42,7 @@
 #include <asm/hypercall.h>
 #include <asm/hvm/hvm.h>
 #include <asm/hvm/support.h>
+#include <asm/debugreg.h>
 #include <asm/msr.h>
 #include <asm/nmi.h>
 #include <asm/iommu.h>
@@ -1219,7 +1220,7 @@ static void paravirt_ctxt_switch_from(st
      * inside Xen, before we get a chance to reload DR7, and this cannot always
      * safely be handled.
      */
-    if ( unlikely(v->arch.guest_context.debugreg[7]) )
+    if ( unlikely(v->arch.guest_context.debugreg[7] & DR7_ACTIVE_MASK) )
         write_debugreg(7, 0);
 }
 
@@ -1234,7 +1235,7 @@ static void paravirt_ctxt_switch_to(stru
     if ( unlikely(cr4 != read_cr4()) )
         write_cr4(cr4);
 
-    if ( unlikely(v->arch.guest_context.debugreg[7]) )
+    if ( unlikely(v->arch.guest_context.debugreg[7] & DR7_ACTIVE_MASK) )
     {
         write_debugreg(0, v->arch.guest_context.debugreg[0]);
         write_debugreg(1, v->arch.guest_context.debugreg[1]);
Index: 2007-11-13/xen/arch/x86/domctl.c
===================================================================
--- 2007-11-13.orig/xen/arch/x86/domctl.c       2007-11-12 08:47:42.000000000 
+0100
+++ 2007-11-13/xen/arch/x86/domctl.c    2007-11-21 12:37:04.000000000 +0100
@@ -825,12 +825,18 @@ void arch_get_info_guest(struct vcpu *v,
                 c.nat->ctrlreg[1] = xen_pfn_to_cr3(
                     pagetable_get_pfn(v->arch.guest_table_user));
 #endif
+
+            c.nat->debugreg[7] |= c.nat->debugreg[5];
+            c.nat->debugreg[5] = 0;
         }
 #ifdef CONFIG_COMPAT
         else
         {
             l4_pgentry_t *l4e = __va(pagetable_get_paddr(v->arch.guest_table));
             c.cmp->ctrlreg[3] = compat_pfn_to_cr3(l4e_get_pfn(*l4e));
+
+            c.cmp->debugreg[7] |= c.cmp->debugreg[5];
+            c.cmp->debugreg[5] = 0;
         }
 #endif
 
Index: 2007-11-13/xen/arch/x86/hvm/svm/svm.c
===================================================================
--- 2007-11-13.orig/xen/arch/x86/hvm/svm/svm.c  2007-11-20 16:46:55.000000000 
+0100
+++ 2007-11-13/xen/arch/x86/hvm/svm/svm.c       2007-11-21 09:47:01.000000000 
+0100
@@ -34,6 +34,7 @@
 #include <asm/cpufeature.h>
 #include <asm/processor.h>
 #include <asm/types.h>
+#include <asm/debugreg.h>
 #include <asm/msr.h>
 #include <asm/spinlock.h>
 #include <asm/hvm/hvm.h>
@@ -189,8 +190,6 @@ static void __restore_debug_registers(st
  * if one of the breakpoints is enabled.  So mask out all bits that don't
  * enable some breakpoint functionality.
  */
-#define DR7_ACTIVE_MASK 0xff
-
 static void svm_restore_dr(struct vcpu *v)
 {
     if ( unlikely(v->arch.guest_context.debugreg[7] & DR7_ACTIVE_MASK) )
Index: 2007-11-13/xen/arch/x86/hvm/vmx/vmx.c
===================================================================
--- 2007-11-13.orig/xen/arch/x86/hvm/vmx/vmx.c  2007-11-20 16:32:57.000000000 
+0100
+++ 2007-11-13/xen/arch/x86/hvm/vmx/vmx.c       2007-11-21 09:47:07.000000000 
+0100
@@ -33,6 +33,7 @@
 #include <asm/cpufeature.h>
 #include <asm/processor.h>
 #include <asm/types.h>
+#include <asm/debugreg.h>
 #include <asm/msr.h>
 #include <asm/spinlock.h>
 #include <asm/paging.h>
@@ -434,8 +435,6 @@ static void __restore_debug_registers(st
  * if one of the breakpoints is enabled.  So mask out all bits that don't
  * enable some breakpoint functionality.
  */
-#define DR7_ACTIVE_MASK 0xff
-
 static void vmx_restore_dr(struct vcpu *v)
 {
     /* NB. __vmread() is not usable here, so we cannot read from the VMCS. */
Index: 2007-11-13/xen/arch/x86/traps.c
===================================================================
--- 2007-11-13.orig/xen/arch/x86/traps.c        2007-11-20 15:46:19.000000000 
+0100
+++ 2007-11-13/xen/arch/x86/traps.c     2007-11-21 11:29:05.000000000 +0100
@@ -412,17 +412,51 @@ static int do_guest_trap(
     return 0;
 }
 
-static void instruction_done(struct cpu_user_regs *regs, unsigned long eip)
+static void instruction_done(struct cpu_user_regs *regs,
+    unsigned long eip, unsigned int bpmatch)
 {
     regs->eip = eip;
     regs->eflags &= ~X86_EFLAGS_RF;
-    if ( regs->eflags & X86_EFLAGS_TF )
+    if ( bpmatch || (regs->eflags & X86_EFLAGS_TF) )
     {
-        current->arch.guest_context.debugreg[6] |= 0xffff4ff0;
+        current->arch.guest_context.debugreg[6] |= bpmatch | 0xffff0ff0;
+        if ( regs->eflags & X86_EFLAGS_TF )
+            current->arch.guest_context.debugreg[6] |= 0x4000;
         do_guest_trap(TRAP_debug, regs, 0);
     }
 }
 
+static unsigned int check_guest_io_breakpoint(struct vcpu *v,
+    unsigned int port, unsigned int len)
+{
+    unsigned int match = 0;
+
+    if ( unlikely(v->arch.guest_context.ctrlreg[4] & X86_CR4_DE) )
+    {
+        unsigned int i;
+
+        for ( i = 0; i < 4; ++i )
+            if ( v->arch.guest_context.debugreg[5] &
+                 (3 << (i * DR_ENABLE_SIZE)) )
+            {
+               unsigned long start = v->arch.guest_context.debugreg[i];
+               unsigned int width = 0;
+
+               switch ( (v->arch.guest_context.debugreg[7] >>
+                         (DR_CONTROL_SHIFT + i * DR_CONTROL_SIZE)) & 0xc )
+               {
+               case DR_LEN_1: width = 1; break;
+               case DR_LEN_2: width = 2; break;
+               case DR_LEN_4: width = 4; break;
+               case DR_LEN_8: width = 8; break;
+               }
+               if ( start < port + len && start + width > port )
+                   match |= 1 << i;
+            }
+    }
+    return match;
+}
+
 /*
  * Called from asm to set up the NMI trapbounce info.
  * Returns 0 if no callback is set up, else 1.
@@ -639,7 +673,6 @@ static int emulate_forced_invalid_op(str
     {
         /* Modify Feature Information. */
         clear_bit(X86_FEATURE_VME, &d);
-        clear_bit(X86_FEATURE_DE,  &d);
         clear_bit(X86_FEATURE_PSE, &d);
         clear_bit(X86_FEATURE_PGE, &d);
         if ( !cpu_has_sep )
@@ -668,7 +701,7 @@ static int emulate_forced_invalid_op(str
     regs->ebx = b;
     regs->ecx = c;
     regs->edx = d;
-    instruction_done(regs, eip);
+    instruction_done(regs, eip, 0);
 
     trace_trap_one_addr(TRC_PV_FORCED_INVALID_OP, regs->eip);
 
@@ -1325,7 +1358,7 @@ static int emulate_privileged_op(struct 
     unsigned long *reg, eip = regs->eip, res;
     u8 opcode, modrm_reg = 0, modrm_rm = 0, rep_prefix = 0, lock = 0, rex = 0;
     enum { lm_seg_none, lm_seg_fs, lm_seg_gs } lm_ovr = lm_seg_none;
-    unsigned int port, i, data_sel, ar, data, rc;
+    unsigned int port, i, data_sel, ar, data, rc, bpmatch = 0;
     unsigned int op_bytes, op_default, ad_bytes, ad_default;
 #define rd_ad(reg) (ad_bytes >= sizeof(regs->reg) \
                     ? regs->reg \
@@ -1475,6 +1508,8 @@ static int emulate_privileged_op(struct 
         }
 #endif
 
+        port = (u16)regs->edx;
+
     continue_io_string:
         switch ( opcode )
         {
@@ -1483,9 +1518,8 @@ static int emulate_privileged_op(struct 
         case 0x6d: /* INSW/INSL */
             if ( data_limit < op_bytes - 1 ||
                  rd_ad(edi) > data_limit - (op_bytes - 1) ||
-                 !guest_io_okay((u16)regs->edx, op_bytes, v, regs) )
+                 !guest_io_okay(port, op_bytes, v, regs) )
                 goto fail;
-            port = (u16)regs->edx;
             switch ( op_bytes )
             {
             case 1:
@@ -1515,7 +1549,7 @@ static int emulate_privileged_op(struct 
         case 0x6f: /* OUTSW/OUTSL */
             if ( data_limit < op_bytes - 1 ||
                  rd_ad(esi) > data_limit - (op_bytes - 1) ||
-                 !guest_io_okay((u16)regs->edx, op_bytes, v, regs) )
+                 !guest_io_okay(port, op_bytes, v, regs) )
                 goto fail;
             rc = copy_from_user(&data, (void *)data_base + rd_ad(esi), 
op_bytes);
             if ( rc != 0 )
@@ -1523,7 +1557,6 @@ static int emulate_privileged_op(struct 
                 propagate_page_fault(data_base + rd_ad(esi) + op_bytes - rc, 
0);
                 return EXCRET_fault_fixed;
             }
-            port = (u16)regs->edx;
             switch ( op_bytes )
             {
             case 1:
@@ -1549,9 +1582,11 @@ static int emulate_privileged_op(struct 
             break;
         }
 
+        bpmatch = check_guest_io_breakpoint(v, port, op_bytes);
+
         if ( rep_prefix && (wr_ad(ecx, regs->ecx - 1) != 0) )
         {
-            if ( !hypercall_preempt_check() )
+            if ( !bpmatch && !hypercall_preempt_check() )
                 goto continue_io_string;
             eip = regs->eip;
         }
@@ -1630,6 +1665,7 @@ static int emulate_privileged_op(struct 
                 regs->eax = (u32)~0;
             break;
         }
+        bpmatch = check_guest_io_breakpoint(v, port, op_bytes);
         goto done;
 
     case 0xec: /* IN %dx,%al */
@@ -1667,6 +1703,7 @@ static int emulate_privileged_op(struct 
                 io_emul(regs);
             break;
         }
+        bpmatch = check_guest_io_breakpoint(v, port, op_bytes);
         goto done;
 
     case 0xee: /* OUT %al,%dx */
@@ -1960,7 +1997,7 @@ static int emulate_privileged_op(struct 
 #undef rd_ad
 
  done:
-    instruction_done(regs, eip);
+    instruction_done(regs, eip, bpmatch);
     return EXCRET_fault_fixed;
 
  fail:
@@ -2330,7 +2367,7 @@ static int emulate_gate_op(struct cpu_u
         sel |= (regs->cs & 3);
 
     regs->cs = sel;
-    instruction_done(regs, off);
+    instruction_done(regs, off, 0);
 #endif
 
     return 0;
@@ -2842,25 +2879,44 @@ long set_debugreg(struct vcpu *v, int re
         /*
          * DR7: Bit 10 reserved (set to 1).
          *      Bits 11-12,14-15 reserved (set to 0).
+         */
+        value &= ~DR_CONTROL_RESERVED_ZERO; /* reserved bits => 0 */
+        value |=  DR_CONTROL_RESERVED_ONE;  /* reserved bits => 1 */
+        /*
          * Privileged bits:
          *      GD (bit 13): must be 0.
-         *      R/Wn (bits 16-17,20-21,24-25,28-29): mustn't be 10.
-         *      LENn (bits 18-19,22-23,26-27,30-31): mustn't be 10.
          */
-        /* DR7 == 0 => debugging disabled for this domain. */
-        if ( value != 0 )
+        if ( value & DR_GENERAL_DETECT )
+            return -EPERM;
+        /* DR7.{G,L}E = 0 => debugging disabled for this domain. */
+        if ( value & DR7_ACTIVE_MASK )
         {
-            value &= 0xffff27ff; /* reserved bits => 0 */
-            value |= 0x00000400; /* reserved bits => 1 */
-            if ( (value & (1<<13)) != 0 ) return -EPERM;
-            for ( i = 0; i < 16; i += 2 )
-                if ( ((value >> (i+16)) & 3) == 2 ) return -EPERM;
+            unsigned int io_enable = 0;
+
+            for ( i = DR_CONTROL_SHIFT; i < 32; i += DR_CONTROL_SIZE )
+            {
+                if ( ((value >> i) & 3) == DR_IO )
+                {
+                    if ( !(v->arch.guest_context.ctrlreg[4] & X86_CR4_DE) )
+                        return -EPERM;
+                    io_enable |= value & (3 << ((i - 16) >> 1));
+                }
+#ifdef __i386__
+                if ( (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ||
+                      !boot_cpu_has(X86_FEATURE_LM)) &&
+                     ((value >> i) & 0xc) == DR_LEN_8 )
+                    return -EPERM;
+#endif
+            }
+            v->arch.guest_context.debugreg[5] = io_enable;
+            value &= ~io_enable;
             /*
              * If DR7 was previously clear then we need to load all other
              * debug registers at this point as they were not restored during
              * context switch.
              */
-            if ( (v == curr) && (v->arch.guest_context.debugreg[7] == 0) )
+            if ( (v == curr) &&
+                 !(v->arch.guest_context.debugreg[7] & DR7_ACTIVE_MASK) )
             {
                 write_debugreg(0, v->arch.guest_context.debugreg[0]);
                 write_debugreg(1, v->arch.guest_context.debugreg[1]);
@@ -2868,9 +2924,9 @@ long set_debugreg(struct vcpu *v, int re
                 write_debugreg(3, v->arch.guest_context.debugreg[3]);
                 write_debugreg(6, v->arch.guest_context.debugreg[6]);
             }
+            if ( v == curr )
+                write_debugreg(7, value);
         }
-        if ( v == curr ) 
-            write_debugreg(7, value);
         break;
     default:
         return -EINVAL;
@@ -2887,8 +2943,19 @@ long do_set_debugreg(int reg, unsigned l
 
 unsigned long do_get_debugreg(int reg)
 {
-    if ( (reg < 0) || (reg > 7) ) return -EINVAL;
-    return current->arch.guest_context.debugreg[reg];
+    switch ( reg )
+    {
+    case 0 ... 3:
+    case 6:
+        return current->arch.guest_context.debugreg[reg];
+    case 7:
+        return current->arch.guest_context.debugreg[7] |
+               current->arch.guest_context.debugreg[5];
+    case 4 ... 5:
+        return current->arch.guest_context.ctrlreg[4] & X86_CR4_DE ?
+               current->arch.guest_context.debugreg[reg + 2] : 0;
+    }
+    return -EINVAL;
 }
 
 /*
Index: 2007-11-13/xen/include/asm-x86/debugreg.h
===================================================================
--- 2007-11-13.orig/xen/include/asm-x86/debugreg.h      2005-11-17 
15:51:06.000000000 +0100
+++ 2007-11-13/xen/include/asm-x86/debugreg.h   2007-11-21 09:39:32.000000000 
+0100
@@ -33,11 +33,13 @@
 
 #define DR_RW_EXECUTE (0x0)   /* Settings for the access types to trap on */
 #define DR_RW_WRITE (0x1)
+#define DR_IO (0x2)
 #define DR_RW_READ (0x3)
 
 #define DR_LEN_1 (0x0) /* Settings for data length to trap on */
 #define DR_LEN_2 (0x4)
 #define DR_LEN_4 (0xC)
+#define DR_LEN_8 (0x8)
 
 /* The low byte to the control register determine which registers are
    enabled.  There are 4 fields of two bits.  One bit is "local", meaning
@@ -53,12 +55,16 @@
 #define DR_LOCAL_ENABLE_MASK (0x55)  /* Set  local bits for all 4 regs */
 #define DR_GLOBAL_ENABLE_MASK (0xAA) /* Set global bits for all 4 regs */
 
+#define DR7_ACTIVE_MASK (DR_LOCAL_ENABLE_MASK|DR_GLOBAL_ENABLE_MASK)
+
 /* The second byte to the control register has a few special things.
    We can slow the instruction pipeline for instructions coming via the
    gdt or the ldt if we want to.  I am not sure why this is an advantage */
 
-#define DR_CONTROL_RESERVED (~0xFFFF03FFUL) /* Reserved by Intel */
-#define DR_LOCAL_SLOWDOWN (0x100)   /* Local slow the pipeline */
-#define DR_GLOBAL_SLOWDOWN (0x200)  /* Global slow the pipeline */
+#define DR_CONTROL_RESERVED_ZERO (~0xFFFF23FFUL) /* Reserved, read as zero */
+#define DR_CONTROL_RESERVED_ONE  ( 0x00000400  ) /* Reserved, read as one */
+#define DR_LOCAL_EXACT_ENABLE    ( 0x00000100  ) /* Local exact enable */
+#define DR_GLOBAL_EXACT_ENABLE   ( 0x00000200  ) /* Global exact enable */
+#define DR_GENERAL_DETECT        ( 0x00002000  ) /* General detect enable */
 
 #endif /* _X86_DEBUGREG_H */



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

<Prev in Thread] Current Thread [Next in Thread>
  • [Xen-devel] [PATCH] x86: emulate I/O port access breakpoints, Jan Beulich <=