Index: usr.sbin/bhyve/Makefile =================================================================== --- usr.sbin/bhyve/Makefile (revision 248077) +++ usr.sbin/bhyve/Makefile (working copy) @@ -10,7 +10,7 @@ SRCS+= ioapic.c mem.c mevent.c mptbl.c SRCS+= pci_emul.c pci_hostbridge.c pci_passthru.c pci_virtio_block.c SRCS+= pci_virtio_net.c pci_uart.c pit_8254.c pmtmr.c post.c rtc.c uart.c -SRCS+= xmsr.c spinup_ap.c +SRCS+= xmsr.c spinup_ap.c vtx_perf.c .PATH: ${.CURDIR}/../../sys/amd64/vmm SRCS+= vmm_instruction_emul.c Index: usr.sbin/bhyve/xmsr.c =================================================================== --- usr.sbin/bhyve/xmsr.c (revision 248077) +++ usr.sbin/bhyve/xmsr.c (working copy) @@ -39,10 +39,16 @@ #include "xmsr.h" +extern int vtxp_msr_info(struct vmctx *, int, uint32_t, uint64_t); + int emulate_wrmsr(struct vmctx *ctx, int vcpu, uint32_t code, uint64_t val) { - printf("Unknown WRMSR code %x, val %lx, cpu %d\n", code, val, vcpu); - exit(1); + if (vtxp_msr_info(ctx, vcpu, code, val)) { + printf("Unknown WRMSR code %x, val %lx, cpu %d\n", code, val, vcpu); + exit(1); + } + + return (vcpu); } Index: usr.sbin/bhyve/vtx_perf.c =================================================================== --- usr.sbin/bhyve/vtx_perf.c (revision 0) +++ usr.sbin/bhyve/vtx_perf.c (working copy) @@ -0,0 +1,203 @@ +/*- + * Test program to record vtx entry/exit timestamps + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +#include +#include +#include +#include +#include + +#include "inout.h" +#include "mem.h" + +#define VTX_START_PORT 180 +#define VTX_INFO_PORT 181 +#define VTX_END_PORT 182 + +/* The guest uses phys mem at 32G */ +#define VTXP_BADADDR (32*1024*1024*1024UL) + +#define VTXP_ELEMS 256 +struct { + uint64_t start; + uint64_t pre; + uint64_t guest; + uint64_t post; + uint64_t end; +} vtxp[VTXP_ELEMS]; + +u_int vtxp_idx; +u_int stats_idx; + +struct mem_range vtx_range; +int vtx_range_inited; + +static int +vtxp_mem_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, + int size, uint64_t *val, void *arg1, long arg2) +{ + struct timeval tv; + uint64_t *stats; + uint64_t rax, rdx; + int error; + int num_stats; + + vtxp[vtxp_idx].end = rdtsc(); + + /* + * Fetch the guest's edx:eax register to complete the guest timestamp. + */ + error = vm_get_register(ctx, vcpu, VM_REG_GUEST_RDX, &rdx); + assert(error == 0); + error = vm_get_register(ctx, vcpu, VM_REG_GUEST_RAX, &rax); + assert(error == 0); + + vtxp[vtxp_idx].guest = (rdx << 32) | (rax & 0xffffffff); + + /* + * Now fetch the pre/post timestamps from the statistics struct + */ + stats = vm_get_stats(ctx, vcpu, &tv, &num_stats); + assert(stats != NULL); + vtxp[vtxp_idx].pre = stats[stats_idx]; + vtxp[vtxp_idx].post = stats[stats_idx + 1]; + vtxp_idx++; + + vtxp[vtxp_idx].start = rdtsc(); + + return (0); +} + + +static int +vtxp_start(struct vmctx *ctx, int vcpu, int in, int port, int bytes, + uint32_t *eax, void *arg) +{ + struct timeval tv; + uint64_t *stats; + int i; + int num_stats; + + if (bytes != 4) + return (-1); + + vtxp_idx = 0; + + if (!vtx_range_inited) { + vtx_range.name = "vtx perf"; + vtx_range.flags = 0; + vtx_range.handler = vtxp_mem_handler; + vtx_range.arg1 = NULL; + vtx_range.arg2 = 0; + vtx_range.base = VTXP_BADADDR; + vtx_range.size = 128; + assert(register_mem(&vtx_range) == 0); + vtx_range_inited = 1; + } + + if (stats_idx == 0) { + stats = vm_get_stats(ctx, vcpu, &tv, &num_stats); + assert(stats != NULL); + for (i = 0; i < num_stats; i++) { + if (!strncmp(vm_get_stat_desc(ctx, i), "TSC", 3)) { + stats_idx = i; + break; + } + } + assert(stats_idx != 0); + } + vtxp[vtxp_idx].start = rdtsc(); + + return (0); +} + +int +vtxp_msr_info(struct vmctx *ctx, int vcpu, uint32_t msr, uint64_t val) +{ + struct timeval tv; + uint64_t *stats; + int num_stats; + + vtxp[vtxp_idx].end = rdtsc(); + + vtxp[vtxp_idx].guest = val; + + /* + * Now fetch the pre/post timestamps from the statistics struct + */ + stats = vm_get_stats(ctx, vcpu, &tv, &num_stats); + assert(stats != NULL); + vtxp[vtxp_idx].pre = stats[stats_idx]; + vtxp[vtxp_idx].post = stats[stats_idx + 1]; + vtxp_idx++; + + vtxp[vtxp_idx].start = rdtsc(); + + return (0); +} + +static int +vtxp_info(struct vmctx *ctx, int vcpu, int in, int port, int bytes, + uint32_t *eax, void *arg) +{ + struct timeval tv; + uint64_t *stats; + uint64_t rdx; + int error, num_stats; + + if (bytes != 4) + return (-1); + + vtxp[vtxp_idx].end = rdtsc(); + + /* + * Fetch the guest's edx register to complete the guest timestamp. + * eax is passed in from the outl instruction. + */ + error = vm_get_register(ctx, vcpu, VM_REG_GUEST_RDX, &rdx); + assert(error == 0); + + vtxp[vtxp_idx].guest = (rdx << 32) | *eax; + + /* + * Now fetch the pre/post timestamps from the statistics struct + */ + stats = vm_get_stats(ctx, vcpu, &tv, &num_stats); + assert(stats != NULL); + vtxp[vtxp_idx].pre = stats[stats_idx]; + vtxp[vtxp_idx].post = stats[stats_idx + 1]; + vtxp_idx++; + + vtxp[vtxp_idx].start = rdtsc(); + + return (0); +} + +static int +vtxp_end(struct vmctx *ctx, int vcpu, int in, int port, int bytes, + uint32_t *eax, void *arg) +{ + + if (bytes != 4) + return (-1); + + vtxp[vtxp_idx].end = rdtsc(); + + /* + * Dump stats to file ? + */ + + return (0); +} + +INOUT_PORT(vtxp, VTX_START_PORT, IOPORT_F_OUT, vtxp_start); +INOUT_PORT(vtxp, VTX_INFO_PORT, IOPORT_F_OUT, vtxp_info); +INOUT_PORT(vtxp, VTX_END_PORT, IOPORT_F_OUT, vtxp_end); Property changes on: usr.sbin/bhyve/vtx_perf.c ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: sys/amd64/vmm/vmm_stat.h =================================================================== --- sys/amd64/vmm/vmm_stat.h (revision 248077) +++ sys/amd64/vmm/vmm_stat.h (working copy) @@ -68,4 +68,14 @@ #endif } +static void __inline +vmm_stat_set(struct vm *vm, int vcpu, struct vmm_stat_type *vst, uint64_t x) +{ +#ifdef VMM_KEEP_STATS + uint64_t *stats = vcpu_stats(vm, vcpu); + if (vst->index >= 0) + stats[vst->index] = x; #endif +} + +#endif Index: sys/amd64/vmm/intel/vmx.c =================================================================== --- sys/amd64/vmm/intel/vmx.c (revision 248077) +++ sys/amd64/vmm/intel/vmx.c (working copy) @@ -157,6 +157,8 @@ static VMM_STAT_DEFINE(VMEXIT_EXTINT, "vm exits due to external interrupt"); static VMM_STAT_DEFINE(VMEXIT_HLT_IGNORED, "number of times hlt was ignored"); static VMM_STAT_DEFINE(VMEXIT_HLT, "number of times hlt was intercepted"); +static VMM_STAT_DEFINE(VMENTRY_TSC, "TSC entry value"); +static VMM_STAT_DEFINE(VMEXIT_TSC, "TSC exit value"); #ifdef KTR static const char * @@ -1451,6 +1453,12 @@ vmexit->u.vmx.exit_reason = exit_reason = vmcs_exit_reason(); vmexit->u.vmx.exit_qualification = vmcs_exit_qualification(); + /* Update the TSC entry/exit values */ + vmm_stat_set(vmx->vm, vcpu, VMENTRY_TSC, + (uint64_t)vmxctx->pre_tsc[1] << 32 | vmxctx->pre_tsc[0]); + vmm_stat_set(vmx->vm, vcpu, VMEXIT_TSC, + (uint64_t)vmxctx->post_tsc[1] << 32 | vmxctx->post_tsc[0]); + if (astpending) { handled = 1; vmexit->inst_length = 0; Index: sys/amd64/vmm/intel/vmx.h =================================================================== --- sys/amd64/vmm/intel/vmx.h (revision 248077) +++ sys/amd64/vmm/intel/vmx.h (working copy) @@ -65,6 +65,9 @@ /* * XXX todo debug registers and fpu state */ + + uint32_t pre_tsc[2]; + uint32_t post_tsc[2]; int launched; /* vmcs launch state */ int launch_error; Index: sys/amd64/vmm/intel/vmx_genassym.c =================================================================== --- sys/amd64/vmm/intel/vmx_genassym.c (revision 248077) +++ sys/amd64/vmm/intel/vmx_genassym.c (working copy) @@ -71,6 +71,11 @@ ASSYM(VMXCTX_HOST_RBX, offsetof(struct vmxctx, host_rbx)); ASSYM(VMXCTX_HOST_RIP, offsetof(struct vmxctx, host_rip)); +ASSYM(VMXCTX_PRE_TSCL, offsetof(struct vmxctx, pre_tsc[0])); +ASSYM(VMXCTX_PRE_TSCH, offsetof(struct vmxctx, pre_tsc[1])); +ASSYM(VMXCTX_POST_TSCL, offsetof(struct vmxctx, post_tsc[0])); +ASSYM(VMXCTX_POST_TSCH, offsetof(struct vmxctx, post_tsc[1])); + ASSYM(VMXCTX_LAUNCH_ERROR, offsetof(struct vmxctx, launch_error)); ASSYM(VM_SUCCESS, VM_SUCCESS); Index: sys/amd64/vmm/intel/vmx_support.S =================================================================== --- sys/amd64/vmm/intel/vmx_support.S (revision 248077) +++ sys/amd64/vmm/intel/vmx_support.S (working copy) @@ -71,11 +71,9 @@ movq VMXCTX_GUEST_CR2(%rdi),%rsi; \ movq %rsi,%cr2; \ movq VMXCTX_GUEST_RSI(%rdi),%rsi; \ - movq VMXCTX_GUEST_RDX(%rdi),%rdx; \ movq VMXCTX_GUEST_RCX(%rdi),%rcx; \ movq VMXCTX_GUEST_R8(%rdi),%r8; \ movq VMXCTX_GUEST_R9(%rdi),%r9; \ - movq VMXCTX_GUEST_RAX(%rdi),%rax; \ movq VMXCTX_GUEST_RBX(%rdi),%rbx; \ movq VMXCTX_GUEST_RBP(%rdi),%rbp; \ movq VMXCTX_GUEST_R10(%rdi),%r10; \ @@ -84,6 +82,11 @@ movq VMXCTX_GUEST_R13(%rdi),%r13; \ movq VMXCTX_GUEST_R14(%rdi),%r14; \ movq VMXCTX_GUEST_R15(%rdi),%r15; \ + rdtsc; \ + movl %eax,VMXCTX_PRE_TSCL(%rdi); \ + movl %edx,VMXCTX_PRE_TSCH(%rdi); \ + movq VMXCTX_GUEST_RAX(%rdi),%rax; \ + movq VMXCTX_GUEST_RDX(%rdi),%rdx; \ movq VMXCTX_GUEST_RDI(%rdi),%rdi; /* restore rdi the last */ #define VM_INSTRUCTION_ERROR(reg) \ @@ -155,13 +158,16 @@ /* * Save guest state that is not automatically saved in the vmcs. */ + movq %rax,VMXCTX_GUEST_RAX(%rsp) + movq %rdx,VMXCTX_GUEST_RDX(%rsp) + rdtsc + movl %eax,VMXCTX_POST_TSCL(%rsp) + movl %edx,VMXCTX_POST_TSCH(%rsp) movq %rdi,VMXCTX_GUEST_RDI(%rsp) movq %rsi,VMXCTX_GUEST_RSI(%rsp) - movq %rdx,VMXCTX_GUEST_RDX(%rsp) movq %rcx,VMXCTX_GUEST_RCX(%rsp) movq %r8,VMXCTX_GUEST_R8(%rsp) movq %r9,VMXCTX_GUEST_R9(%rsp) - movq %rax,VMXCTX_GUEST_RAX(%rsp) movq %rbx,VMXCTX_GUEST_RBX(%rsp) movq %rbp,VMXCTX_GUEST_RBP(%rsp) movq %r10,VMXCTX_GUEST_R10(%rsp)