From: Keir Fraser Date: Fri, 12 Oct 2007 12:06:02 +0000 (+0100) Subject: xentrace/x86: PV guest tracing extensions. X-Git-Tag: archive/raspbian/4.8.0-1+rpi1~1^2~14847^2~51 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=06a73c9f9380bba455cbf1a5502b902d01a5dc3f;p=xen.git xentrace/x86: PV guest tracing extensions. From: George Dunlap Signed-off-by: Keir Fraser --- diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile index 3899874714..aa4f42ffd1 100644 --- a/xen/arch/x86/Makefile +++ b/xen/arch/x86/Makefile @@ -40,6 +40,7 @@ obj-y += srat.o obj-y += string.o obj-y += sysctl.o obj-y += time.o +obj-y += trace.o obj-y += traps.o obj-y += usercopy.o obj-y += x86_emulate.o diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index ecd41cf4a6..94f8f89182 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -111,6 +111,7 @@ #include #include #include +#include #define MEM_LOG(_f, _a...) gdprintk(XENLOG_WARNING , _f "\n" , ## _a) @@ -3402,6 +3403,8 @@ static int ptwr_emulated_update( BUG(); } + trace_ptwr_emulation(addr, nl1e); + unmap_domain_page(pl1e); /* Finally, drop the old PTE. */ diff --git a/xen/arch/x86/trace.c b/xen/arch/x86/trace.c new file mode 100644 index 0000000000..ddb32202bd --- /dev/null +++ b/xen/arch/x86/trace.c @@ -0,0 +1,231 @@ +#include +#include +#include +#include +#include +#include +#include + +#ifndef __x86_64__ +#undef TRC_PV_64_FLAG +#define TRC_PV_64_FLAG 0 +#endif + +asmlinkage void trace_hypercall(void) +{ + struct cpu_user_regs *regs = guest_cpu_user_regs(); + + if ( !tb_init_done ) + return; + +#ifdef __x86_64__ + if ( is_pv_32on64_vcpu(current) ) + { + struct { + u32 eip,eax; + } __attribute__((packed)) d; + + d.eip = regs->eip; + d.eax = regs->eax; + + __trace_var(TRC_PV_HYPERCALL, 1, + sizeof(d), (unsigned char *)&d); + } + else +#endif + { + struct { + unsigned long eip; + u32 eax; + } __attribute__((packed)) d; + u32 event; + + event = TRC_PV_HYPERCALL; + event |= TRC_PV_64_FLAG; + d.eip = regs->eip; + d.eax = regs->eax; + + __trace_var(event, 1/*tsc*/, sizeof(d), (unsigned char*)&d); + } +} + +void __trace_pv_trap(int trapnr, unsigned long eip, + int use_error_code, unsigned error_code) +{ + if ( !tb_init_done ) + return; + +#ifdef __x86_64__ + if ( is_pv_32on64_vcpu(current) ) + { + struct { + unsigned eip:32, + trapnr:15, + use_error_code:1, + error_code:16; + } __attribute__((packed)) d; + + d.eip = eip; + d.trapnr = trapnr; + d.error_code = error_code; + d.use_error_code=!!use_error_code; + + __trace_var(TRC_PV_TRAP, 1, + sizeof(d), (unsigned char *)&d); + } + else +#endif + { + struct { + unsigned long eip; + unsigned trapnr:15, + use_error_code:1, + error_code:16; + } __attribute__((packed)) d; + unsigned event; + + d.eip = eip; + d.trapnr = trapnr; + d.error_code = error_code; + d.use_error_code=!!use_error_code; + + event = TRC_PV_TRAP; + event |= TRC_PV_64_FLAG; + __trace_var(event, 1, sizeof(d), (unsigned char *)&d); + } +} + +void __trace_pv_page_fault(unsigned long addr, unsigned error_code) +{ + unsigned long eip = guest_cpu_user_regs()->eip; + + if ( !tb_init_done ) + return; + +#ifdef __x86_64__ + if ( is_pv_32on64_vcpu(current) ) + { + struct { + u32 eip, addr, error_code; + } __attribute__((packed)) d; + + d.eip = eip; + d.addr = addr; + d.error_code = error_code; + + __trace_var(TRC_PV_PAGE_FAULT, 1, sizeof(d), (unsigned char *)&d); + } + else +#endif + { + struct { + unsigned long eip, addr; + u32 error_code; + } __attribute__((packed)) d; + unsigned event; + + d.eip = eip; + d.addr = addr; + d.error_code = error_code; + event = TRC_PV_PAGE_FAULT; + event |= TRC_PV_64_FLAG; + __trace_var(event, 1, sizeof(d), (unsigned char *)&d); + } +} + +void __trace_trap_one_addr(unsigned event, unsigned long va) +{ + if ( !tb_init_done ) + return; + +#ifdef __x86_64__ + if ( is_pv_32on64_vcpu(current) ) + { + u32 d = va; + __trace_var(event, 1, sizeof(d), (unsigned char *)&d); + } + else +#endif + { + event |= TRC_PV_64_FLAG; + __trace_var(event, 1, sizeof(va), (unsigned char *)&va); + } +} + +void __trace_trap_two_addr(unsigned event, unsigned long va1, + unsigned long va2) +{ + if ( !tb_init_done ) + return; + +#ifdef __x86_64__ + if ( is_pv_32on64_vcpu(current) ) + { + struct { + u32 va1, va2; + } __attribute__((packed)) d; + d.va1=va1; + d.va2=va2; + __trace_var(event, 1, sizeof(d), (unsigned char *)&d); + } + else +#endif + { + struct { + unsigned long va1, va2; + } __attribute__((packed)) d; + d.va1=va1; + d.va2=va2; + event |= TRC_PV_64_FLAG; + __trace_var(event, 1, sizeof(d), (unsigned char *)&d); + } +} + +void __trace_ptwr_emulation(unsigned long addr, l1_pgentry_t npte) +{ + unsigned long eip = guest_cpu_user_regs()->eip; + + if ( !tb_init_done ) + return; + + /* We have a couple of different modes to worry about: + * - 32-on-32: 32-bit pte, 32-bit virtual addresses + * - pae-on-pae, pae-on-64: 64-bit pte, 32-bit virtual addresses + * - 64-on-64: 64-bit pte, 64-bit virtual addresses + * pae-on-64 is the only one that requires extra code; in all other + * cases, "unsigned long" is the size of a guest virtual address. + */ + +#ifdef __x86_64__ + if ( is_pv_32on64_vcpu(current) ) + { + struct { + l1_pgentry_t pte; + u32 addr, eip; + } __attribute__((packed)) d; + d.addr = addr; + d.eip = eip; + d.pte = npte; + + __trace_var(TRC_PV_PTWR_EMULATION_PAE, 1, + sizeof(d), (unsigned char *)&d); + } + else +#endif + { + struct { + l1_pgentry_t pte; + unsigned long addr, eip; + } d; + unsigned event; + + d.addr = addr; + d.eip = eip; + d.pte = npte; + + event = ((CONFIG_PAGING_LEVELS == 3) ? + TRC_PV_PTWR_EMULATION_PAE : TRC_PV_PTWR_EMULATION); + event |= TRC_PV_64_FLAG; + __trace_var(event, 1/*tsc*/, sizeof(d), (unsigned char *)&d); + } +} diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c index 4d4fdffca7..51d84739e9 100644 --- a/xen/arch/x86/traps.c +++ b/xen/arch/x86/traps.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -380,6 +381,8 @@ static int do_guest_trap( struct trap_bounce *tb; const struct trap_info *ti; + trace_pv_trap(trapnr, regs->eip, use_error_code, regs->error_code); + tb = &v->arch.trap_bounce; ti = &v->arch.guest_context.trap_ctxt[trapnr]; @@ -633,6 +636,8 @@ static int emulate_forced_invalid_op(struct cpu_user_regs *regs) regs->eip = eip; regs->eflags &= ~X86_EFLAGS_RF; + trace_trap_one_addr(TRC_PV_FORCED_INVALID_OP, regs->eip); + return EXCRET_fault_fixed; } @@ -752,6 +757,8 @@ void propagate_page_fault(unsigned long addr, u16 error_code) if ( !guest_kernel_mode(v, guest_cpu_user_regs()) ) error_code |= PFEC_user_mode; + trace_pv_page_fault(addr, error_code); + ti = &v->arch.guest_context.trap_ctxt[TRAP_page_fault]; tb->flags = TBF_EXCEPTION | TBF_EXCEPTION_ERRCODE; tb->error_code = error_code; @@ -783,7 +790,13 @@ static int handle_gdt_ldt_mapping_fault( if ( likely(is_ldt_area) ) { /* LDT fault: Copy a mapping from the guest's LDT, if it is valid. */ - if ( unlikely(map_ldt_shadow_page(offset >> PAGE_SHIFT) == 0) ) + if ( likely(map_ldt_shadow_page(offset >> PAGE_SHIFT)) ) + { + if ( guest_mode(regs) ) + trace_trap_two_addr(TRC_PV_GDT_LDT_MAPPING_FAULT, + regs->eip, offset); + } + else { /* In hypervisor mode? Leave it to the #PF handler to fix up. */ if ( !guest_mode(regs) ) @@ -939,7 +952,12 @@ static int fixup_page_fault(unsigned long addr, struct cpu_user_regs *regs) if ( unlikely(IN_HYPERVISOR_RANGE(addr)) ) { if ( paging_mode_external(d) && guest_mode(regs) ) - return paging_fault(addr, regs); + { + int ret = paging_fault(addr, regs); + if ( ret == EXCRET_fault_fixed ) + trace_trap_two_addr(TRC_PV_PAGING_FIXUP, regs->eip, addr); + return ret; + } if ( (addr >= GDT_LDT_VIRT_START) && (addr < GDT_LDT_VIRT_END) ) return handle_gdt_ldt_mapping_fault( addr - GDT_LDT_VIRT_START, regs); @@ -955,7 +973,12 @@ static int fixup_page_fault(unsigned long addr, struct cpu_user_regs *regs) return EXCRET_fault_fixed; if ( paging_mode_enabled(d) ) - return paging_fault(addr, regs); + { + int ret = paging_fault(addr, regs); + if ( ret == EXCRET_fault_fixed ) + trace_trap_two_addr(TRC_PV_PAGING_FIXUP, regs->eip, addr); + return ret; + } return 0; } @@ -1872,13 +1895,19 @@ asmlinkage int do_general_protection(struct cpu_user_regs *regs) /* Emulate some simple privileged and I/O instructions. */ if ( (regs->error_code == 0) && emulate_privileged_op(regs) ) + { + trace_trap_one_addr(TRC_PV_EMULATE_PRIVOP, regs->eip); return 0; + } #if defined(__i386__) if ( VM_ASSIST(v->domain, VMASST_TYPE_4gb_segments) && (regs->error_code == 0) && gpf_emulate_4gb(regs) ) + { + TRACE_1D(TRC_PV_EMULATE_4GB, regs->eip); return 0; + } #endif /* Pass on GPF as is. */ @@ -2030,6 +2059,8 @@ asmlinkage int do_device_not_available(struct cpu_user_regs *regs) do_guest_trap(TRAP_no_device, regs, 0); current->arch.guest_context.ctrlreg[0] &= ~X86_CR0_TS; } + else + TRACE_0D(TRC_PV_MATH_STATE_RESTORE); return EXCRET_fault_fixed; } diff --git a/xen/arch/x86/x86_32/entry.S b/xen/arch/x86/x86_32/entry.S index 0ccfb8edc7..37ed92d612 100644 --- a/xen/arch/x86/x86_32/entry.S +++ b/xen/arch/x86/x86_32/entry.S @@ -194,6 +194,12 @@ ENTRY(hypercall) pushl 20(%esp) # ECX pushl 20(%esp) # EBX #endif + cmpb $0,tb_init_done + je tracing_off + call trace_hypercall + /* Now restore all the registers that trace_hypercall clobbered */ + movl UREGS_eax+24(%esp),%eax /* Hypercall # */ +tracing_off: call *hypercall_table(,%eax,4) addl $24,%esp # Discard the shadow parameters #ifndef NDEBUG diff --git a/xen/arch/x86/x86_64/compat/entry.S b/xen/arch/x86/x86_64/compat/entry.S index 8bf2dd010b..3504e26058 100644 --- a/xen/arch/x86/x86_64/compat/entry.S +++ b/xen/arch/x86/x86_64/compat/entry.S @@ -56,6 +56,18 @@ ENTRY(compat_hypercall) movl %ebp,%r9d /* Arg 6 */ movl UREGS_rbx(%rsp),%edi /* Arg 1 */ #endif + cmpb $0,tb_init_done(%rip) + je compat_tracing_off + call trace_hypercall + /* Now restore all the registers that trace_hypercall clobbered */ + movl UREGS_rax(%rsp),%eax /* Hypercall # */ + movl UREGS_rbx(%rsp),%edi /* Arg 1 */ + movl UREGS_rcx(%rsp),%esi /* Arg 2 */ + movl UREGS_rdx(%rsp),%edx /* Arg 3 */ + movl UREGS_rsi(%rsp),%ecx /* Arg 4 */ + movl UREGS_rdi(%rsp),%r8d /* Arg 5 */ + movl UREGS_rbp(%rsp),%r9d /* Arg 6 */ +compat_tracing_off: leaq compat_hypercall_table(%rip),%r10 PERFC_INCR(PERFC_hypercalls, %rax, %rbx) callq *(%r10,%rax,8) diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S index 30d3bf38ce..ad9e9ceebc 100644 --- a/xen/arch/x86/x86_64/entry.S +++ b/xen/arch/x86/x86_64/entry.S @@ -148,6 +148,18 @@ ENTRY(syscall_enter) pushq %rax pushq UREGS_rip+8(%rsp) #endif + cmpb $0,tb_init_done(%rip) + je tracing_off + call trace_hypercall + /* Now restore all the registers that trace_hypercall clobbered */ + movq UREGS_rax(%rsp),%rax /* Hypercall # */ + movq UREGS_rdi(%rsp),%rdi /* Arg 1 */ + movq UREGS_rsi(%rsp),%rsi /* Arg 2 */ + movq UREGS_rdx(%rsp),%rdx /* Arg 3 */ + movq UREGS_r10(%rsp),%rcx /* Arg 4 */ + movq UREGS_rdi(%rsp),%r8 /* Arg 5 */ + movq UREGS_rbp(%rsp),%r9 /* Arg 6 */ +tracing_off: leaq hypercall_table(%rip),%r10 PERFC_INCR(PERFC_hypercalls, %rax, %rbx) callq *(%r10,%rax,8) diff --git a/xen/include/asm-ia64/trace.h b/xen/include/asm-ia64/trace.h new file mode 100644 index 0000000000..edef1bb099 --- /dev/null +++ b/xen/include/asm-ia64/trace.h @@ -0,0 +1,4 @@ +#ifndef __ASM_TRACE_H__ +#define __ASM_TRACE_H__ + +#endif /* __ASM_TRACE_H__ */ diff --git a/xen/include/asm-powerpc/trace.h b/xen/include/asm-powerpc/trace.h new file mode 100644 index 0000000000..edef1bb099 --- /dev/null +++ b/xen/include/asm-powerpc/trace.h @@ -0,0 +1,4 @@ +#ifndef __ASM_TRACE_H__ +#define __ASM_TRACE_H__ + +#endif /* __ASM_TRACE_H__ */ diff --git a/xen/include/asm-x86/trace.h b/xen/include/asm-x86/trace.h new file mode 100644 index 0000000000..c8b13c4b86 --- /dev/null +++ b/xen/include/asm-x86/trace.h @@ -0,0 +1,46 @@ +#ifndef __ASM_TRACE_H__ +#define __ASM_TRACE_H__ + +#include + +void __trace_pv_trap(int trapnr, unsigned long eip, + int use_error_code, unsigned error_code); +static inline void trace_pv_trap(int trapnr, unsigned long eip, + int use_error_code, unsigned error_code) +{ + if ( tb_init_done ) + __trace_pv_trap(trapnr, eip, use_error_code, error_code); +} + +void __trace_pv_page_fault(unsigned long addr, unsigned error_code); +static inline void trace_pv_page_fault(unsigned long addr, + unsigned error_code) +{ + if ( tb_init_done ) + __trace_pv_page_fault(addr, error_code); +} + +void __trace_trap_one_addr(unsigned event, unsigned long va); +static inline void trace_trap_one_addr(unsigned event, unsigned long va) +{ + if ( tb_init_done ) + __trace_trap_one_addr(event, va); +} + +void __trace_trap_two_addr(unsigned event, unsigned long va1, + unsigned long va2); +static inline void trace_trap_two_addr(unsigned event, unsigned long va1, + unsigned long va2) +{ + if ( tb_init_done ) + __trace_trap_two_addr(event, va1, va2); +} + +void __trace_ptwr_emulation(unsigned long addr, l1_pgentry_t npte); +static inline void trace_ptwr_emulation(unsigned long addr, l1_pgentry_t npte) +{ + if ( tb_init_done ) + __trace_ptwr_emulation(addr, npte); +} + +#endif /* __ASM_TRACE_H__ */ diff --git a/xen/include/public/trace.h b/xen/include/public/trace.h index a2d75ba2f2..4cbb896342 100644 --- a/xen/include/public/trace.h +++ b/xen/include/public/trace.h @@ -36,6 +36,7 @@ #define TRC_DOM0OP 0x0004f000 /* Xen DOM0 operation trace */ #define TRC_HVM 0x0008f000 /* Xen HVM trace */ #define TRC_MEM 0x0010f000 /* Xen memory trace */ +#define TRC_PV 0x0020f000 /* Xen PV traces */ #define TRC_ALL 0x0ffff000 #define TRC_HD_TO_EVENT(x) ((x)&0x0fffffff) #define TRC_HD_CYCLE_FLAG (1UL<<31) @@ -74,6 +75,20 @@ #define TRC_MEM_PAGE_GRANT_UNMAP (TRC_MEM + 2) #define TRC_MEM_PAGE_GRANT_TRANSFER (TRC_MEM + 3) +#define TRC_PV_HYPERCALL (TRC_PV + 1) +#define TRC_PV_TRAP (TRC_PV + 3) +#define TRC_PV_PAGE_FAULT (TRC_PV + 4) +#define TRC_PV_FORCED_INVALID_OP (TRC_PV + 5) +#define TRC_PV_EMULATE_PRIVOP (TRC_PV + 6) +#define TRC_PV_EMULATE_4GB (TRC_PV + 7) +#define TRC_PV_MATH_STATE_RESTORE (TRC_PV + 8) +#define TRC_PV_PAGING_FIXUP (TRC_PV + 9) +#define TRC_PV_GDT_LDT_MAPPING_FAULT (TRC_PV + 10) +#define TRC_PV_PTWR_EMULATION (TRC_PV + 11) +#define TRC_PV_PTWR_EMULATION_PAE (TRC_PV + 12) + /* Indicates that addresses in trace record are 64 bits */ +#define TRC_PV_64_FLAG (0x100) + /* trace events per subclass */ #define TRC_HVM_VMENTRY (TRC_HVM_ENTRYEXIT + 0x01) #define TRC_HVM_VMEXIT (TRC_HVM_ENTRYEXIT + 0x02) diff --git a/xen/include/xen/domain.h b/xen/include/xen/domain.h index aa8c72f0ac..2b3a24cb2d 100644 --- a/xen/include/xen/domain.h +++ b/xen/include/xen/domain.h @@ -2,6 +2,8 @@ #ifndef __XEN_DOMAIN_H__ #define __XEN_DOMAIN_H__ +#include + typedef union { struct vcpu_guest_context *nat; struct compat_vcpu_guest_context *cmp; diff --git a/xen/include/xen/trace.h b/xen/include/xen/trace.h index 41db3f7dc6..7635f2dbf6 100644 --- a/xen/include/xen/trace.h +++ b/xen/include/xen/trace.h @@ -21,11 +21,12 @@ #ifndef __XEN_TRACE_H__ #define __XEN_TRACE_H__ +extern int tb_init_done; + #include #include #include - -extern int tb_init_done; +#include /* Used to initialise trace buffer functionality */ void init_trace_bufs(void);