x86: more debugging adjustments
RFC 1/1 x86: more debugging adjustments
Apart from the cleanup patches just sent I also have two bigger ones in the queue. I'm not certain they qualify for 3.2 at this point, though: While most of them can be considered enhancements, the io-brkp one (which depends on the inject-sstep one) also fixes the issue of guests on x86-64 not being able to utilize 8-byte breakpoints, and it reduces the likelihood of needing to restore the debug registers by only looking at the enable bits in the respective conditions (as HVM was already doing).
Therefore I'd like to understand whether these patches can go in in their current shape, or whether I should split out at least the 8-byte bug fix and submit this separately.
Thanks, Jan
xen/arch/x86/domain.c +3 -2
@@ -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(struif (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 ]);
xen/arch/x86/domctl.c +6 -0
@@ -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
xen/arch/x86/hvm/svm/svm.c +1 -2
@@ -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 ) )
xen/arch/x86/hvm/vmx/vmx.c +1 -2
@@ -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. */
xen/arch/x86/traps.c +94 -27
238 lines not on this page · raw message
xen/include/asm-x86/debugreg.h +9 -3
@@ -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/arch/x86/hvm/io.c +2 -0
@@ -863,6 +863,8 @@ void hvm_io_assist(void)/* Copy register changes back into current guest state. */ regs->eflags &= ~X86_EFLAGS_RF ;memcpy (guest_cpu_user_regs (), regs,HVM_CONTEXT_STACK_BYTES ); +if ( regs->eflags &X86_EFLAGS_TF ) +hvm_inject_exception (TRAP_debug,HVM_DELIVER_NO_ERROR_CODE ,0 ); out:vcpu_end_shutdown_deferral (v);
xen/arch/x86/hvm/platform.c +0 -3
@@ -1061,7 +1061,6 @@ void handle_mmio(unsigned long gpa) } regs->eip += inst_len;/* advance %eip */ - regs->eflags &= ~X86_EFLAGS_RF ;switch ( mmio_op->instr ) {case INSTR_MOV :
@@ -1121,7 +1120,6 @@ void handle_mmio(unsigned long gpa)/* The guest does not have the non-mmio address mapped. * Need to send in a page fault */ regs->eip -= inst_len;/* do not advance %eip */ - regs->eflags |=X86_EFLAGS_RF ;/* RF was set by original #PF */ hvm_inject_exception (TRAP_page_fault, pfec, addr);return ; }
@@ -1150,7 +1148,6 @@ void handle_mmio(unsigned long gpa)/* Failed on the page-spanning copy. Inject PF into * the guest for the address where we failed */ regs->eip -= inst_len;/* do not advance %eip */ - regs->eflags |=X86_EFLAGS_RF ;/* RF was set by #PF */ /* Must set CR2 at the failing address */ addr += size - rv; gdprintk(XENLOG_DEBUG, "Pagefault on non-io side of a "
xen/arch/x86/hvm/svm/svm.c +29 -18
119 lines not on this page · raw message
xen/arch/x86/hvm/vmx/vmx.c +25 -10
@@ -1093,6 +1093,14 @@ static void vmx_inject_exception(vmx_inject_hw_exception (v, trapnr, errcode);if ( trapnr == TRAP_page_fault ) v->arch.hvm_vcpu.guest_cr[2 ] = cr2; +if ( trapnr == TRAP_debug && + (guest_cpu_user_regs ()->eflags &X86_EFLAGS_TF ) ) + { +unsigned long dr6 =read_debugreg (6 ); + + v->arch.guest_context.debugreg[6 ] = dr6 |=0x4000 ; +write_debugreg (6 , dr6); + } }static void vmx_update_vtpr (struct vcpu *v,unsigned long value)
@@ -1184,13 +1192,20 @@ static int __get_instruction_length(voidreturn len; } -static void __update_guest_eip (unsigned long inst_len) +static void __update_guest_eip (struct vcpu *v,unsigned long inst_len) {struct cpu_user_regs *regs =guest_cpu_user_regs ();unsigned long x; regs->eip += inst_len; regs->eflags &= ~X86_EFLAGS_RF ; +if ( v && (regs->eflags &X86_EFLAGS_TF ) ) + { + x =read_debugreg (6 ); + v->arch.guest_context.debugreg[6 ] = x |=0x4000 ; +write_debugreg (6 , x); +vmx_inject_hw_exception (v, TRAP_debug,HVM_DELIVER_NO_ERROR_CODE ); + } x =__vmread (GUEST_INTERRUPTIBILITY_INFO );if ( x & (VMX_INTR_SHADOW_STI |VMX_INTR_SHADOW_MOV_SS ) )
@@ -2824,18 +2839,18 @@ asmlinkage void vmx_vmexit_handler(struc }case EXIT_REASON_CPUID : inst_len =__get_instruction_length ();/* Safe: CPUID */ -__update_guest_eip (inst_len); +__update_guest_eip (v, inst_len);vmx_do_cpuid (regs);break ;case EXIT_REASON_HLT : inst_len =__get_instruction_length ();/* Safe: HLT */ -__update_guest_eip (inst_len); +__update_guest_eip (NULL , inst_len);vmx_do_hlt (regs);break ;case EXIT_REASON_INVLPG : { inst_len =__get_instruction_length ();/* Safe: INVLPG */ -__update_guest_eip (inst_len); +__update_guest_eip (v, inst_len); exit_qualification =__vmread (EXIT_QUALIFICATION );vmx_do_invlpg (exit_qualification);break ;
@@ -2848,7 +2863,7 @@ asmlinkage void vmx_vmexit_handler(struc rc =hvm_do_hypercall (regs);if ( rc != HVM_HCALL_preempted ) { -__update_guest_eip (inst_len); +__update_guest_eip (v, inst_len);if ( rc == HVM_HCALL_invalidate )send_invalidate_req (); }
@@ -2859,7 +2874,7 @@ asmlinkage void vmx_vmexit_handler(struc exit_qualification =__vmread (EXIT_QUALIFICATION ); inst_len =__get_instruction_length ();/* Safe: MOV Cn, LMSW, CLTS */ if (vmx_cr_access (exit_qualification, regs) ) -__update_guest_eip (inst_len); +__update_guest_eip (v, inst_len);break ; }case EXIT_REASON_DR_ACCESS :
@@ -2874,12 +2889,12 @@ asmlinkage void vmx_vmexit_handler(struccase EXIT_REASON_MSR_READ : inst_len =__get_instruction_length ();/* Safe: RDMSR */ if (vmx_do_msr_read (regs) ) -__update_guest_eip (inst_len); +__update_guest_eip (v, inst_len);break ;case EXIT_REASON_MSR_WRITE : inst_len =__get_instruction_length ();/* Safe: WRMSR */ if (vmx_do_msr_write (regs) ) -__update_guest_eip (inst_len); +__update_guest_eip (v, inst_len);break ;case EXIT_REASON_MWAIT_INSTRUCTION :
@@ -2893,7 +2908,7 @@ asmlinkage void vmx_vmexit_handler(struccase EXIT_REASON_VMWRITE :case EXIT_REASON_VMXOFF :case EXIT_REASON_VMXON : -vmx_inject_hw_exception (v, TRAP_invalid_op,VMX_DELIVER_NO_ERROR_CODE ); +vmx_inject_hw_exception (v, TRAP_invalid_op,HVM_DELIVER_NO_ERROR_CODE );break ;case EXIT_REASON_TPR_BELOW_THRESHOLD :
@@ -2911,7 +2926,7 @@ asmlinkage void vmx_vmexit_handler(struccase EXIT_REASON_INVD : { inst_len =__get_instruction_length ();/* Safe: INVD */ -__update_guest_eip (inst_len); +__update_guest_eip (v, inst_len);if ( !list_empty (&(domain_hvm_iommu (v->domain)->pdev_list)) )wbinvd ();break ;
xen/arch/x86/traps.c +14 -5
@@ -412,6 +412,17 @@ static void do_guest_trap( regs->error_code); } +static void instruction_done (struct cpu_user_regs *regs,unsigned long eip) +{ + regs->eip = eip; + regs->eflags &= ~X86_EFLAGS_RF ; +if ( regs->eflags &X86_EFLAGS_TF ) + { + current->arch.guest_context.debugreg[6 ] |=0xffff4ff0 ; +do_guest_trap (TRAP_debug, regs,0 ); + } +} +/* * Called from asm to set up the NMI trapbounce info. * Returns 0 if no callback is set up, else 1.
@@ -657,8 +668,7 @@ static int emulate_forced_invalid_op(str regs->ebx = b; regs->ecx = c; regs->edx = d; - regs->eip = eip; - regs->eflags &= ~X86_EFLAGS_RF ; +instruction_done (regs, eip);trace_trap_one_addr (TRC_PV_FORCED_INVALID_OP , regs->eip);
@@ -1950,8 +1960,7 @@ static int emulate_privileged_op(struct#undef rd_ad done: - regs->eip = eip; - regs->eflags &= ~X86_EFLAGS_RF ; +instruction_done (regs, eip); skip:return EXCRET_fault_fixed;
@@ -2320,8 +2329,8 @@ static void emulate_gate_op(struct cpu_u else sel |= (regs->cs & 3); - regs->eip = off; regs->cs = sel; +instruction_done (regs, off);#endif }
xen/arch/x86/x86_emulate.c +1 -0
@@ -1625,6 +1625,7 @@ x86_emulate(/* Commit shadow register state. */ _regs.eflags &= ~EF_RF ; *ctxt->regs = _regs; +/* FIXME generate_exception_if(_regs.eflags & EF_TF, EXC_DB); */ done:return rc;
xen/include/asm-x86/hvm/support.h +1 -1
@@ -50,7 +50,7 @@ static inline vcpu_iodata_t *get_ioreq(s#define TYPE_CLTS (2 << 4)#define TYPE_LMSW (3 << 4) -#define VMX_DELIVER_NO_ERROR_CODE -1 +#define HVM_DELIVER_NO_ERROR_CODE -1#if HVM_DEBUG #define DBG_LEVEL_0 (1 << 0)
xen/include/asm-x86/hvm/vmx/vmx.h +3 -3
@@ -272,7 +272,7 @@ static inline void __vmx_inject_exceptio*/ intr_fields = (INTR_INFO_VALID_MASK | (type<<8 ) | trap); -if ( error_code !=VMX_DELIVER_NO_ERROR_CODE ) { +if ( error_code !=HVM_DELIVER_NO_ERROR_CODE ) {__vmwrite (VM_ENTRY_EXCEPTION_ERROR_CODE , error_code); intr_fields |=INTR_INFO_DELIVER_CODE_MASK ; }
@@ -294,13 +294,13 @@ static inline void vmx_inject_hw_exceptistatic inline void vmx_inject_extint (struct vcpu *v,int trap) {__vmx_inject_exception (v, trap,X86_EVENTTYPE_EXT_INTR , -VMX_DELIVER_NO_ERROR_CODE ); +HVM_DELIVER_NO_ERROR_CODE ); }static inline void vmx_inject_nmi (struct vcpu *v) {__vmx_inject_exception (v,2 ,X86_EVENTTYPE_NMI , -VMX_DELIVER_NO_ERROR_CODE ); +HVM_DELIVER_NO_ERROR_CODE ); }#endif /* __ASM_X86_HVM_VMX_VMX_H__ */
_______________________________________________
Xen-devel mailing list
[email protected]
http://lists.xensource.com/xen-devel
Inject-sstep: Looks fine in principle. A few specific questions:
* Why is all the RFLAGS.RF logic removed from platform.c?
* Why is DR6 adjustment done in svm_hvm_inject_exception() rather thansvm_inject_exception() (to match vmx_inject_exception())?
* Why do some instruction emulations pass NULL to update_guest_eip() and
hence bypass #DB injection? That seems bogus.
Io-brkp: This one needs more explanation about exactly what things are being improved. There's more going on here than your brief explanation below. My only comment at this point is that I don't see that supporting CR4.DE==0 is very useful. I don't subscribe to the view that we should support every little detail of x86 architecture just because it's there, especially for PV guests.
All other patches are applied and I queued up a couple for 3.1.3 also.
-- Keir
Quoted 18 lines · Jan Beulich
Apart from the cleanup patches just sent I also have two bigger ones in the queue. I'm not certain they qualify for 3.2 at this point, though: While most of them can be considered enhancements, the io-brkp one (which depends on the inject-sstep one) also fixes the issue of guests on x86-64 not being able to utilize 8-byte breakpoints, and it reduces the likelihood of needing to restore the debug registers by only looking at the enable bits in the respective conditions (as HVM was already doing).
Therefore I'd like to understand whether these patches can go in in their current shape, or whether I should split out at least the 8-byte bug fix and submit this separately.
Thanks, Jan
_______________________________________________
Xen-devel mailing list
[email protected]
http://lists.xensource.com/xen-devel
Quoted 3 lines
Quoted 1 line
Quoted 1 line
Keir Fraser <[email protected]> 22.11.07 15:50 >>>
Inject-sstep: Looks fine in principle. A few specific questions:
* Why is all the RFLAGS.RF logic removed fromplatform.c?
As it seems to me it was misplaced there (yes, I added it, but I probably didn't understand the execution flow properly back then). The instruction completes in hvm_io_assist(), there's no register state writeback elsewhere that I could see, and hence the updating of EFLAGS.RF also belongs just there.
Quoted 2 lines
* Why is DR6 adjustment done in
svm_hvm_inject_exception()rather thansvm_inject_exception()(to matchvmx_inject_exception())?
Because
a) svm_hvm_inject_exception() is the counterpart of vmx_inject_exception(), svm_inject_exception() is an internal helper.
Quoted 2 lines
* Why do some instruction emulations pass NULL to
update_guest_eip()and hence bypass #DB injection? That seems bogus.
That's intentional (to a certain degree): Especially for the HLT case I wasn't really certain injecting an exception here would have the intended effect. I'm pretty sure you'd have to suppress the normal HLT handling in that case, and it seemed safer for a first cut to not inject an exception at all here (thus simply retaining current behavior for this special case).
For SVM's VMEXIT_EXCEPTION_BP case it seems certainly wrong to inject one.
Quoted 2 lines
Io-brkp: This one needs more explanation about exactly what things are being improved. There's more going on here than your brief explanation below. My
Oh, I see that I didn't describe the original purpose of emulating the #DB on I/O port accesses - I must have thought that this is implied by the name. This is being done by not committing the respective enable bits to %dr7, but rather tracking them in the DR5 shadow.
Beyond that, the only things I didn't describe are mere cleanup items, like replacing numeric literals with manifest constants.
Quoted 1 line
only comment at this point is that I don't see that supporting CR4.DE==0 is
You probably mean CR4.DE==1? If you doubt that, why did you apply the recent change to allow the guest to drive the bit?
Quoted 3 lines
very useful. I don't subscribe to the view that we should support every little detail of x86 architecture just because it's there, especially for PV guests.
I think that a kernel debugger in a guest should certainly have the capability of doing this, but I understand your reservation to a certain degree.
Quoted 1 line
All other patches are applied and I queued up a couple for 3.1.3 also.
Thanks. Btw., the 'official' tree appears to be stuck again...
Jan
Quoted 11 lines · Jan Beulich
Quoted 2 lines
* Why do some instruction emulations pass NULL to
update_guest_eip()and hence bypass #DB injection? That seems bogus.That's intentional (to a certain degree): Especially for the HLT case I wasn't really certain injecting an exception here would have the intended effect. I'm pretty sure you'd have to suppress the normal HLT handling in that case, and it seemed safer for a first cut to not inject an exception at all here (thus
simply retaining current behavior for this special case).
For SVM'sVMEXIT_EXCEPTION_BPcase it seems certainly wrong to inject one.
Ah, you could be right. I don't have that much experience with EFLAGS.TF, but from the reference manuals it looks rather as if, on return from an exception or interrupt, when EFLAGS.TF is re-set by IRET, you actually do not #DB with EIP pointing at the instruction you IRET to? And this is because single-step #DB is a trap? Also I suppose that a software exception or interrupt causes single-step #DB to be skipped when you actually execute INT n, INT3, INTO or whatever. So you effectively never see single-step #DB with EIP pointing at the instruction following one of those INT instructions?
I suppose I could validate this for myself. :-)
Anyway, apart from that the patches are fine. Re-submit with Signed-off-by lines and I'll drop them in.
-- Keir