From: "Jan Beulich" Subject: [PATCH] x86: emulate I/O port access breakpoints Date: Thu, 22 Nov 2007 16:59:46 +0000 Message-ID: <4745C392.76E4.0078.0@novell.com> Mime-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: quoted-printable Return-path: Content-Disposition: inline List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xensource.com Errors-To: xen-devel-bounces@lists.xensource.com To: xen-devel@lists.xensource.com List-Id: xen-devel@lists.xenproject.org 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 Index: 2007-11-13/xen/arch/x86/domain.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- 2007-11-13.orig/xen/arch/x86/domain.c 2007-11-12 08:47:42.0000000= 00 +0100 +++ 2007-11-13/xen/arch/x86/domain.c 2007-11-21 09:44:48.000000000 = +0100 @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -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); } =20 @@ -1234,7 +1235,7 @@ static void paravirt_ctxt_switch_to(stru if ( unlikely(cr4 !=3D read_cr4()) ) write_cr4(cr4); =20 - 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 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- 2007-11-13.orig/xen/arch/x86/domctl.c 2007-11-12 08:47:42.0000000= 00 +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] =3D xen_pfn_to_cr3( pagetable_get_pfn(v->arch.guest_table_user)); #endif + + c.nat->debugreg[7] |=3D c.nat->debugreg[5]; + c.nat->debugreg[5] =3D 0; } #ifdef CONFIG_COMPAT else { l4_pgentry_t *l4e =3D __va(pagetable_get_paddr(v->arch.guest_t= able)); c.cmp->ctrlreg[3] =3D compat_pfn_to_cr3(l4e_get_pfn(*l4e)); + + c.cmp->debugreg[7] |=3D c.cmp->debugreg[5]; + c.cmp->debugreg[5] =3D 0; } #endif =20 Index: 2007-11-13/xen/arch/x86/hvm/svm/svm.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- 2007-11-13.orig/xen/arch/x86/hvm/svm/svm.c 2007-11-20 16:46:55.0000000= 00 +0100 +++ 2007-11-13/xen/arch/x86/hvm/svm/svm.c 2007-11-21 09:47:01.0000000= 00 +0100 @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -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 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- 2007-11-13.orig/xen/arch/x86/hvm/vmx/vmx.c 2007-11-20 16:32:57.0000000= 00 +0100 +++ 2007-11-13/xen/arch/x86/hvm/vmx/vmx.c 2007-11-21 09:47:07.0000000= 00 +0100 @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -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 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- 2007-11-13.orig/xen/arch/x86/traps.c 2007-11-20 15:46:19.0000000= 00 +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; } =20 -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 =3D eip; regs->eflags &=3D ~X86_EFLAGS_RF; - if ( regs->eflags & X86_EFLAGS_TF ) + if ( bpmatch || (regs->eflags & X86_EFLAGS_TF) ) { - current->arch.guest_context.debugreg[6] |=3D 0xffff4ff0; + current->arch.guest_context.debugreg[6] |=3D bpmatch | 0xffff0ff0;= + if ( regs->eflags & X86_EFLAGS_TF ) + current->arch.guest_context.debugreg[6] |=3D 0x4000; do_guest_trap(TRAP_debug, regs, 0); } } =20 +static unsigned int check_guest_io_breakpoint(struct vcpu *v, + unsigned int port, unsigned int len) +{ + unsigned int match =3D 0; + + if ( unlikely(v->arch.guest_context.ctrlreg[4] & X86_CR4_DE) ) + { + unsigned int i; + + for ( i =3D 0; i < 4; ++i ) + if ( v->arch.guest_context.debugreg[5] & + (3 << (i * DR_ENABLE_SIZE)) ) + { + unsigned long start =3D v->arch.guest_context.debugreg[i]; + unsigned int width =3D 0; + + switch ( (v->arch.guest_context.debugreg[7] >> + (DR_CONTROL_SHIFT + i * DR_CONTROL_SIZE)) & 0xc = ) + { + case DR_LEN_1: width =3D 1; break; + case DR_LEN_2: width =3D 2; break; + case DR_LEN_4: width =3D 4; break; + case DR_LEN_8: width =3D 8; break; + } + if ( start < port + len && start + width > port ) + match |=3D 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 =3D b; regs->ecx =3D c; regs->edx =3D d; - instruction_done(regs, eip); + instruction_done(regs, eip, 0); =20 trace_trap_one_addr(TRC_PV_FORCED_INVALID_OP, regs->eip); =20 @@ -1325,7 +1358,7 @@ static int emulate_privileged_op(struct=20 unsigned long *reg, eip =3D regs->eip, res; u8 opcode, modrm_reg =3D 0, modrm_rm =3D 0, rep_prefix =3D 0, lock = =3D 0, rex =3D 0; enum { lm_seg_none, lm_seg_fs, lm_seg_gs } lm_ovr =3D lm_seg_none; - unsigned int port, i, data_sel, ar, data, rc; + unsigned int port, i, data_sel, ar, data, rc, bpmatch =3D 0; unsigned int op_bytes, op_default, ad_bytes, ad_default; #define rd_ad(reg) (ad_bytes >=3D sizeof(regs->reg) \ ? regs->reg \ @@ -1475,6 +1508,8 @@ static int emulate_privileged_op(struct=20 } #endif =20 + port =3D (u16)regs->edx; + continue_io_string: switch ( opcode ) { @@ -1483,9 +1518,8 @@ static int emulate_privileged_op(struct=20 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 =3D (u16)regs->edx; switch ( op_bytes ) { case 1: @@ -1515,7 +1549,7 @@ static int emulate_privileged_op(struct=20 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 =3D copy_from_user(&data, (void *)data_base + rd_ad(esi), = op_bytes); if ( rc !=3D 0 ) @@ -1523,7 +1557,6 @@ static int emulate_privileged_op(struct=20 propagate_page_fault(data_base + rd_ad(esi) + op_bytes - = rc, 0); return EXCRET_fault_fixed; } - port =3D (u16)regs->edx; switch ( op_bytes ) { case 1: @@ -1549,9 +1582,11 @@ static int emulate_privileged_op(struct=20 break; } =20 + bpmatch =3D check_guest_io_breakpoint(v, port, op_bytes); + if ( rep_prefix && (wr_ad(ecx, regs->ecx - 1) !=3D 0) ) { - if ( !hypercall_preempt_check() ) + if ( !bpmatch && !hypercall_preempt_check() ) goto continue_io_string; eip =3D regs->eip; } @@ -1630,6 +1665,7 @@ static int emulate_privileged_op(struct=20 regs->eax =3D (u32)~0; break; } + bpmatch =3D check_guest_io_breakpoint(v, port, op_bytes); goto done; =20 case 0xec: /* IN %dx,%al */ @@ -1667,6 +1703,7 @@ static int emulate_privileged_op(struct=20 io_emul(regs); break; } + bpmatch =3D check_guest_io_breakpoint(v, port, op_bytes); goto done; =20 case 0xee: /* OUT %al,%dx */ @@ -1960,7 +1997,7 @@ static int emulate_privileged_op(struct=20 #undef rd_ad =20 done: - instruction_done(regs, eip); + instruction_done(regs, eip, bpmatch); return EXCRET_fault_fixed; =20 fail: @@ -2330,7 +2367,7 @@ static int emulate_gate_op(struct cpu_u sel |=3D (regs->cs & 3); =20 regs->cs =3D sel; - instruction_done(regs, off); + instruction_done(regs, off, 0); #endif =20 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 &=3D ~DR_CONTROL_RESERVED_ZERO; /* reserved bits =3D> 0 */ + value |=3D DR_CONTROL_RESERVED_ONE; /* reserved bits =3D> 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 =3D=3D 0 =3D> debugging disabled for this domain. */ - if ( value !=3D 0 ) + if ( value & DR_GENERAL_DETECT ) + return -EPERM; + /* DR7.{G,L}E =3D 0 =3D> debugging disabled for this domain. */ + if ( value & DR7_ACTIVE_MASK ) { - value &=3D 0xffff27ff; /* reserved bits =3D> 0 */ - value |=3D 0x00000400; /* reserved bits =3D> 1 */ - if ( (value & (1<<13)) !=3D 0 ) return -EPERM; - for ( i =3D 0; i < 16; i +=3D 2 ) - if ( ((value >> (i+16)) & 3) =3D=3D 2 ) return -EPERM; + unsigned int io_enable =3D 0; + + for ( i =3D DR_CONTROL_SHIFT; i < 32; i +=3D DR_CONTROL_SIZE = ) + { + if ( ((value >> i) & 3) =3D=3D DR_IO ) + { + if ( !(v->arch.guest_context.ctrlreg[4] & X86_CR4_DE) = ) + return -EPERM; + io_enable |=3D value & (3 << ((i - 16) >> 1)); + } +#ifdef __i386__ + if ( (boot_cpu_data.x86_vendor !=3D X86_VENDOR_INTEL || + !boot_cpu_has(X86_FEATURE_LM)) && + ((value >> i) & 0xc) =3D=3D DR_LEN_8 ) + return -EPERM; +#endif + } + v->arch.guest_context.debugreg[5] =3D io_enable; + value &=3D ~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 =3D=3D curr) && (v->arch.guest_context.debugreg[7] = =3D=3D 0) ) + if ( (v =3D=3D 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 =3D=3D curr ) + write_debugreg(7, value); } - if ( v =3D=3D curr )=20 - write_debugreg(7, value); break; default: return -EINVAL; @@ -2887,8 +2943,19 @@ long do_set_debugreg(int reg, unsigned l =20 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; } =20 /* Index: 2007-11-13/xen/include/asm-x86/debugreg.h =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- 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.0000000= 00 +0100 @@ -33,11 +33,13 @@ =20 #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) =20 #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) =20 /* 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 */ =20 +#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 = */ =20 -#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 = */ =20 #endif /* _X86_DEBUGREG_H */