From: Tamas K Lengyel Date: Thu, 22 Sep 2022 18:49:55 +0000 (+0000) Subject: xen/x86: Add XEN_DOMCTL_{rd/wr}msr_list hypercalls X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=cd2955a59d0f401b37f0e7914cac71ead69e2d83;p=people%2Ftklengyel%2Fxen.git xen/x86: Add XEN_DOMCTL_{rd/wr}msr_list hypercalls Currently the XEN_DOMCTL_get_vcpu_msrs is only capable of gathering a handful of predetermined vcpu MSRs. In our use-case gathering the vPMU MSRs by an external privileged tool is necessary, thus we introduce a new set of domctls to allow for querying for any guest MSRs. Signed-off-by: Tamas K Lengyel --- wrmsr and AMD side totally unimplemented still needs wiring into guest_{rd/wr}msr --- diff --git a/tools/include/xenctrl.h b/tools/include/xenctrl.h index 0c8b4c3aa7..01a39f6dd4 100644 --- a/tools/include/xenctrl.h +++ b/tools/include/xenctrl.h @@ -872,6 +872,12 @@ int xc_vcpu_getinfo(xc_interface *xch, uint32_t vcpu, xc_vcpuinfo_t *info); +typedef struct xen_domctl_vcpu_msr xc_vcpumsr_t; +int xc_vcpu_rdmsr_list(xc_interface *xch, uint32_t domid, uint32_t vcpu, + uint32_t count, xc_vcpumsr_t *msrs); +int xc_vcpu_wrmsr_list(xc_interface *xch, uint32_t domid, uint32_t vcpu, + uint32_t count, xc_vcpumsr_t *msrs); + long long xc_domain_get_cpu_usage(xc_interface *xch, uint32_t domid, int vcpu); diff --git a/tools/libs/ctrl/xc_domain.c b/tools/libs/ctrl/xc_domain.c index 14c0420c35..7e84ab1272 100644 --- a/tools/libs/ctrl/xc_domain.c +++ b/tools/libs/ctrl/xc_domain.c @@ -2201,6 +2201,57 @@ int xc_domain_soft_reset(xc_interface *xch, domctl.domain = domid; return do_domctl(xch, &domctl); } + +int xc_vcpu_rdmsr_list(xc_interface *xch, uint32_t domid, uint32_t vcpu, + uint32_t count, xc_vcpumsr_t *msrs) +{ + int rc; + DECLARE_DOMCTL; + DECLARE_HYPERCALL_BOUNCE(msrs, count * sizeof(xc_vcpumsr_t), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); + + if ( !count || !msrs ) + return -1; + + domctl.cmd = XEN_DOMCTL_rdmsr_list; + domctl.domain = domid; + domctl.u.vcpu_msrs.vcpu = vcpu; + domctl.u.vcpu_msrs.msr_count = count; + + if ( xc_hypercall_bounce_pre(xch, msrs) ) + return -ENOMEM; + + set_xen_guest_handle(domctl.u.vcpu_msrs.msrs, msrs); + rc = do_domctl(xch, &domctl); + xc_hypercall_bounce_post(xch, msrs); + + return rc; +} + +int xc_vcpu_wrmsr_list(xc_interface *xch, uint32_t domid, uint32_t vcpu, + uint32_t count, xc_vcpumsr_t *msrs) +{ + int rc; + DECLARE_DOMCTL; + DECLARE_HYPERCALL_BOUNCE(msrs, count * sizeof(xc_vcpumsr_t), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); + + if ( !count || !msrs ) + return -1; + + domctl.cmd = XEN_DOMCTL_wrmsr_list; + domctl.domain = domid; + domctl.u.vcpu_msrs.vcpu = vcpu; + domctl.u.vcpu_msrs.msr_count = count; + + if ( xc_hypercall_bounce_pre(xch, msrs) ) + return -ENOMEM; + + set_xen_guest_handle(domctl.u.vcpu_msrs.msrs, msrs); + rc = do_domctl(xch, &domctl); + xc_hypercall_bounce_post(xch, msrs); + + return rc; +} + /* * Local variables: * mode: C diff --git a/xen/arch/x86/cpu/vpmu.c b/xen/arch/x86/cpu/vpmu.c index 64cdbfc48c..8139260667 100644 --- a/xen/arch/x86/cpu/vpmu.c +++ b/xen/arch/x86/cpu/vpmu.c @@ -117,9 +117,8 @@ void vpmu_lvtpc_update(uint32_t val) apic_write(APIC_LVTPC, vpmu->hw_lapic_lvtpc); } -int vpmu_do_msr(unsigned int msr, uint64_t *msr_content, bool is_write) +int vpmu_do_msr(struct vcpu *v, unsigned int msr, uint64_t *msr_content, bool is_write) { - struct vcpu *curr = current; struct vpmu_struct *vpmu; int ret = 0; @@ -129,10 +128,10 @@ int vpmu_do_msr(unsigned int msr, uint64_t *msr_content, bool is_write) */ if ( likely(vpmu_mode == XENPMU_MODE_OFF) || ((vpmu_mode & XENPMU_MODE_ALL) && - !is_hardware_domain(curr->domain)) ) + !is_hardware_domain(v->domain)) ) goto nop; - vpmu = vcpu_vpmu(curr); + vpmu = vcpu_vpmu(v); if ( !vpmu_is_set(vpmu, VPMU_INITIALIZED) ) goto nop; @@ -146,7 +145,7 @@ int vpmu_do_msr(unsigned int msr, uint64_t *msr_content, bool is_write) * and since do_wr/rdmsr may load VPMU context we should save * (and unload) it again. */ - if ( !has_vlapic(curr->domain) && vpmu->xenpmu_data && + if ( !has_vlapic(v->domain) && vpmu->xenpmu_data && vpmu_is_set(vpmu, VPMU_CACHED) ) { vpmu_set(vpmu, VPMU_CONTEXT_SAVE); diff --git a/xen/arch/x86/cpu/vpmu_amd.c b/xen/arch/x86/cpu/vpmu_amd.c index 58794a16f0..0f16fa9a43 100644 --- a/xen/arch/x86/cpu/vpmu_amd.c +++ b/xen/arch/x86/cpu/vpmu_amd.c @@ -350,11 +350,12 @@ static void context_update(unsigned int msr, u64 msr_content) static int cf_check amd_vpmu_do_wrmsr(unsigned int msr, uint64_t msr_content) { - struct vcpu *v = current; struct vpmu_struct *vpmu = vcpu_vpmu(v); unsigned int idx = 0; int type = get_pmu_reg_type(msr, &idx); + ASSERT(v == current); + if ( (type == MSR_TYPE_CTRL ) && ((msr_content & CTRL_RSVD_MASK) != ctrl_rsvd[idx]) ) return -EINVAL; @@ -404,11 +405,13 @@ static int cf_check amd_vpmu_do_wrmsr(unsigned int msr, uint64_t msr_content) return 0; } -static int cf_check amd_vpmu_do_rdmsr(unsigned int msr, uint64_t *msr_content) +static int cf_check amd_vpmu_do_rdmsr(struct vcpu *v, unsigned int msr, + uint64_t *msr_content) { - struct vcpu *v = current; struct vpmu_struct *vpmu = vcpu_vpmu(v); + ASSERT(v == current); + if ( !vpmu_is_set(vpmu, VPMU_CONTEXT_LOADED) || vpmu_is_set(vpmu, VPMU_FROZEN) ) { @@ -518,6 +521,12 @@ static int cf_check svm_vpmu_initialise(struct vcpu *v) return 0; } +static int cf_check amd_get_msr(struct vcpu *v, unsigned int msr, uint64_t *val) +{ + /* TODO in case an external tool needs access to these MSRs */ + return -ENOSYS; +} + #ifdef CONFIG_MEM_SHARING static int cf_check amd_allocate_context(struct vcpu *v) { @@ -535,6 +544,7 @@ static const struct arch_vpmu_ops __initconst_cf_clobber amd_vpmu_ops = { .arch_vpmu_save = amd_vpmu_save, .arch_vpmu_load = amd_vpmu_load, .arch_vpmu_dump = amd_vpmu_dump, + .get_msr = amd_get_msr, #ifdef CONFIG_MEM_SHARING .allocate_context = amd_allocate_context, diff --git a/xen/arch/x86/cpu/vpmu_intel.c b/xen/arch/x86/cpu/vpmu_intel.c index b91d818be0..331335b61c 100644 --- a/xen/arch/x86/cpu/vpmu_intel.c +++ b/xen/arch/x86/cpu/vpmu_intel.c @@ -540,15 +540,20 @@ static int core2_vpmu_msr_common_check(u32 msr_index, int *type, int *index) return 1; } -static int cf_check core2_vpmu_do_wrmsr(unsigned int msr, uint64_t msr_content) +static int cf_check core2_vpmu_do_wrmsr(struct vcpu *v, unsigned int msr, uint64_t msr_content) { int i, tmp; int type = -1, index = -1; - struct vcpu *v = current; struct vpmu_struct *vpmu = vcpu_vpmu(v); struct xen_pmu_intel_ctxt *core2_vpmu_cxt; uint64_t *enabled_cntrs; + if ( v != current ) + { + gdprintk(XENLOG_INFO, "Not yet implemented\n"); + return -ENOSYS; + } + if ( !core2_vpmu_msr_common_check(msr, &type, &index) ) return -EINVAL; @@ -708,13 +713,46 @@ static int cf_check core2_vpmu_do_wrmsr(unsigned int msr, uint64_t msr_content) return 0; } -static int cf_check core2_vpmu_do_rdmsr(unsigned int msr, uint64_t *msr_content) +static int cf_check get_saved_msr(struct vcpu *v, unsigned int msr, uint64_t *val) +{ + int type, index, ret = 0; + struct vpmu_struct *vpmu = vcpu_vpmu(v); + struct xen_pmu_intel_ctxt *core2_vpmu_cxt = vpmu->context; + uint64_t *fixed_counters = vpmu_reg_pointer(core2_vpmu_cxt, fixed_counters); + struct xen_pmu_cntr_pair *xen_pmu_cntr_pair = + vpmu_reg_pointer(core2_vpmu_cxt, arch_counters); + + if ( !is_core2_vpmu_msr(msr, &type, &index) ) + return -EINVAL; + + if ( msr == MSR_CORE_PERF_GLOBAL_OVF_CTRL ) + *val = core2_vpmu_cxt->global_ovf_ctrl; + else if ( msr == MSR_CORE_PERF_GLOBAL_STATUS ) + *val = core2_vpmu_cxt->global_status; + else if ( msr == MSR_CORE_PERF_GLOBAL_CTRL ) + *val = core2_vpmu_cxt->global_ctrl; + else if ( msr >= MSR_CORE_PERF_FIXED_CTR0 && + msr < MSR_CORE_PERF_FIXED_CTR0 + fixed_pmc_cnt ) + *val = fixed_counters[msr - MSR_CORE_PERF_FIXED_CTR0]; + else if ( msr >= MSR_P6_PERFCTR(0) && msr < MSR_P6_PERFCTR(arch_pmc_cnt) ) + *val = xen_pmu_cntr_pair[msr - MSR_P6_PERFCTR(0)].counter; + else if ( msr >= MSR_P6_EVNTSEL(0) && msr < MSR_P6_EVNTSEL(arch_pmc_cnt) ) + *val = xen_pmu_cntr_pair[msr - MSR_P6_EVNTSEL(0)].control; + else + ret = -EINVAL; + + return ret; +} + +static int cf_check core2_vpmu_do_rdmsr(struct vcpu *v, unsigned int msr, uint64_t *msr_content) { int type = -1, index = -1; - struct vcpu *v = current; struct vpmu_struct *vpmu = vcpu_vpmu(v); struct xen_pmu_intel_ctxt *core2_vpmu_cxt; + if ( v != current ) + return get_saved_msr(v, msr, msr_content); + if ( core2_vpmu_msr_common_check(msr, &type, &index) ) { core2_vpmu_cxt = vpmu->context; diff --git a/xen/arch/x86/domctl.c b/xen/arch/x86/domctl.c index e9bfbc57a7..0c52bab511 100644 --- a/xen/arch/x86/domctl.c +++ b/xen/arch/x86/domctl.c @@ -1081,6 +1081,63 @@ long arch_do_domctl( break; } + case XEN_DOMCTL_rdmsr_list: + case XEN_DOMCTL_wrmsr_list: + { + struct xen_domctl_vcpu_msrs *vmsrs = &domctl->u.vcpu_msrs; + struct xen_domctl_vcpu_msr msr = {}; + struct vcpu *v; + unsigned int i; + + ret = -ESRCH; + if ( (vmsrs->vcpu >= d->max_vcpus) || + ((v = d->vcpu[vmsrs->vcpu]) == NULL) ) + break; + + ret = -EINVAL; + if ( v == curr ) /* no vcpu_pause() */ + break; + + if ( !vmsrs->msr_count || guest_handle_is_null(vmsrs->msrs) ) + break; + + vcpu_pause(v); + + ret = 0; + for ( i=0; imsr_count; i++ ) + { + int rc; + + if ( copy_from_guest_offset(&msr, vmsrs->msrs, i, 1) ) + { + ret = -EFAULT; + break; + } + + if ( domctl->cmd == XEN_DOMCTL_rdmsr_list ) + rc = guest_rdmsr(v, msr.index, &msr.value); + else + rc = guest_wrmsr(v, msr.index, msr.value); + + if ( rc != X86EMUL_OKAY ) + { + ret = -EINVAL; + vmsrs->msr_count = i; + copyback = true; + break; + } + + if ( copy_to_guest_offset(vmsrs->msrs, i, &msr, 1) + { + ret = -EFAULT; + break; + } + } + + vcpu_unpause(v); + break; + } + case XEN_DOMCTL_get_vcpu_msrs: case XEN_DOMCTL_set_vcpu_msrs: { diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c index 1aeaabcb13..44ab4f6ceb 100644 --- a/xen/arch/x86/hvm/svm/svm.c +++ b/xen/arch/x86/hvm/svm/svm.c @@ -1952,7 +1952,7 @@ static int cf_check svm_msr_read_intercept( case MSR_AMD_FAM15H_EVNTSEL3: case MSR_AMD_FAM15H_EVNTSEL4: case MSR_AMD_FAM15H_EVNTSEL5: - if ( vpmu_do_rdmsr(msr, msr_content) ) + if ( vpmu_do_rdmsr(v, msr, msr_content) ) goto gpf; break; diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c index 17e103188a..d38e68e4bc 100644 --- a/xen/arch/x86/hvm/vmx/vmx.c +++ b/xen/arch/x86/hvm/vmx/vmx.c @@ -3295,7 +3295,7 @@ static int cf_check vmx_msr_read_intercept( case MSR_CORE_PERF_FIXED_CTR_CTRL...MSR_CORE_PERF_GLOBAL_OVF_CTRL: case MSR_IA32_PEBS_ENABLE: case MSR_IA32_DS_AREA: - if ( vpmu_do_rdmsr(msr, msr_content) ) + if ( vpmu_do_rdmsr(curr, msr, msr_content) ) goto gp_fault; break; diff --git a/xen/arch/x86/include/asm/vpmu.h b/xen/arch/x86/include/asm/vpmu.h index 05e1fbfccf..ca51b3bbec 100644 --- a/xen/arch/x86/include/asm/vpmu.h +++ b/xen/arch/x86/include/asm/vpmu.h @@ -40,8 +40,8 @@ /* Arch specific operations shared by all vpmus */ struct arch_vpmu_ops { int (*initialise)(struct vcpu *v); - int (*do_wrmsr)(unsigned int msr, uint64_t msr_content); - int (*do_rdmsr)(unsigned int msr, uint64_t *msr_content); + int (*do_wrmsr)(struct vcpu *v, unsigned int msr, uint64_t msr_content); + int (*do_rdmsr)(struct vcpu *v, unsigned int msr, uint64_t *msr_content); int (*do_interrupt)(struct cpu_user_regs *regs); void (*arch_vpmu_destroy)(struct vcpu *v); int (*arch_vpmu_save)(struct vcpu *v, bool_t to_guest); @@ -109,7 +109,7 @@ static inline bool_t vpmu_are_all_set(const struct vpmu_struct *vpmu, } void vpmu_lvtpc_update(uint32_t val); -int vpmu_do_msr(unsigned int msr, uint64_t *msr_content, bool is_write); +int vpmu_do_msr(struct vcpu *v, unsigned int msr, uint64_t *msr_content, bool is_write); void vpmu_do_interrupt(struct cpu_user_regs *regs); void vpmu_initialise(struct vcpu *v); void vpmu_destroy(struct vcpu *v); @@ -118,13 +118,13 @@ void cf_check vpmu_save_force(void *arg); int vpmu_load(struct vcpu *v, bool_t from_guest); void vpmu_dump(struct vcpu *v); -static inline int vpmu_do_wrmsr(unsigned int msr, uint64_t msr_content) +static inline int vpmu_do_wrmsr(struct vcpu *v, unsigned int msr, uint64_t msr_content) { - return vpmu_do_msr(msr, &msr_content, true /* write */); + return vpmu_do_msr(v, msr, &msr_content, true /* write */); } -static inline int vpmu_do_rdmsr(unsigned int msr, uint64_t *msr_content) +static inline int vpmu_do_rdmsr(struct vcpu *v, unsigned int msr, uint64_t *msr_content) { - return vpmu_do_msr(msr, msr_content, false /* read */); + return vpmu_do_msr(v, msr, msr_content, false /* read */); } extern unsigned int vpmu_mode; diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c index 95416995a5..9b487a0ae8 100644 --- a/xen/arch/x86/msr.c +++ b/xen/arch/x86/msr.c @@ -431,6 +431,7 @@ int guest_rdmsr(struct vcpu *v, uint32_t msr, uint64_t *val) case MSR_INTEL_CORE_THREAD_COUNT: */ default: + /* TODO: vpmu msrs for external tools should get wired in here */ return X86EMUL_UNHANDLEABLE; } diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c index 5da00e24e4..cfb7c665fd 100644 --- a/xen/arch/x86/pv/emul-priv-op.c +++ b/xen/arch/x86/pv/emul-priv-op.c @@ -992,7 +992,7 @@ static int cf_check read_msr( if ( vpmu_msr || (boot_cpu_data.x86_vendor & (X86_VENDOR_AMD | X86_VENDOR_HYGON)) ) { - if ( vpmu_do_rdmsr(reg, val) ) + if ( vpmu_do_rdmsr(curr, reg, val) ) break; return X86EMUL_OKAY; } diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h index b2ae839c36..01eb8150e6 100644 --- a/xen/include/public/domctl.h +++ b/xen/include/public/domctl.h @@ -971,6 +971,20 @@ DEFINE_XEN_GUEST_HANDLE(xen_domctl_vcpu_msr_t); * Output for set: * - If Xen encounters an error with a specific MSR, -EINVAL shall be returned * and 'msr_count' shall be set to the offending index, to aid debugging. + * +*/ +/* + * XEN_DOMCTL_rdmsr_list / XEN_DOMCTL_wrmsr_list. + * + * Input: + * - 'msr_count' is the number of entries in 'msrs'. + * + * Output: + * - If Xen encounters an error with a specific MSR, including an MSR that's + * unused by the guest, -EINVAL shall be returned and 'msr_count' shall be + * set to the offending index, to aid debugging. The remaining items in the + * list after the offending MSR remain unprocessed by Xen. + * */ struct xen_domctl_vcpu_msrs { uint32_t vcpu; /* IN */ @@ -1274,6 +1288,8 @@ struct xen_domctl { #define XEN_DOMCTL_get_cpu_policy 82 #define XEN_DOMCTL_set_cpu_policy 83 #define XEN_DOMCTL_vmtrace_op 84 +#define XEN_DOMCTL_rdmsr_list 85 +#define XEN_DOMCTL_wrmsr_list 86 #define XEN_DOMCTL_gdbsx_guestmemio 1000 #define XEN_DOMCTL_gdbsx_pausevcpu 1001 #define XEN_DOMCTL_gdbsx_unpausevcpu 1002