mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 07:39:12 -04:00
Pull kvm updates from Paolo Bonzini:
"arm64:
This is a bit of an odd merge window on the KVM/arm64 front. There
is absolutely no new feature in the pull request. It is purely
fixes, because it is simply becoming too hard to review new stuff
when so many AI-fuelled fixes hit the list.
- Significant cleanup of the vgic-v5 PPI support which was merged in
7.1. This makes the code more maintainable, and squashes a couple
of bugs in the meantime
- Set of fixes for the handling of the MMU in an NV context,
particularly VNCR-triggered faults. S1POE support is fixed as well
- Large set of pKVM fixes, mostly addressing recurring issues around
hypervisor tracking of donated pages in obscure cases where the
donation could fail and leave things in a bizarre state
- Fixes for the so-called "lazy vgic init", which resulted in
sleeping operations in non-preemptible sections. This turned out to
be far more invasive than initially expected..
- Reduce the overhead of L1/L2 context switch by not touching the FP
registers
- Fix the way non-implemented page sizes are dealt with when a guest
insist on using them for S2 translation
- The usual set of low-impact fixes and cleanups all over the map
Loongarch:
- On a request for lazy FPU load, load all FPU state that the VM
supports instead of enabling only the part (FPU, LSX or LASX) that
caused the FPU load request
- Some enhancements about interrupt injection
- Some bug fixes and other small changes
RISC-V:
- Batch G-stage TLB flushes for GPA range based page table updates
- Convert HGEI line management to fully per-HART
- Fix missing CSR dirty marking when FWFT state updated via ONE_REG
- Fix stale FWFT feature exposure to Guest/VM
- Speed up dirty logging write faults using MMU rwlock and atomic PTE
updates using cmpxchg() for permission-only changes
- Use flexible array for APLIC IRQ state
- Use kvm_slot_dirty_track_enabled() for logging enable check on a
memslot
- Avoid skipping valid pages in kvm_riscv_gstage_wp_range()
- Avoid skipping valid pages in kvm_riscv_gstage_unmap_range()
- Use endian-specific __lelong for NACL shared memory
S390:
- KVM_PRE_FAULT_MEMORY support
- Support for 2G hugepages
- Support for the ASTFLEIE 2 facility
- Support for fast inject using kvm_arch_set_irq_inatomic
- Fix potential leak of uninitialized bytes
- A few more misc gmap fixes
x86:
- Generic support for the more granular permissions allowed by EPT,
namely "read" (which was previously usurping the U bit) and
separate execution bits for kernel and userspace
- Do not assume that all page tables start with U=1/W=1/NX=0 at the
root, as AMD GMET needs to have U=0 at the root
- Introduce common assembly macros for use within Intel and AMD
vendor-specific vmentry code. This touches the SPEC_CTRL handling,
which is now entirely done in assembly for Intel (by reusing the
AMD code that already existed), and register save/restore which
uses some macro magic to compute the offsets in the struct. Both of
these are preparatory changes for upcoming APX support
- Clean up KVM's register tracking and storage, primarily to prepare
for APX support, which expands the maximum number of GPRs from 16
to 32
- Keep a single copy of the PDPTRs rather than two, since
architecturally there is just one
- Handle EXIT_FASTPATH_EXIT_USERSPACE in vendor code to ensure vendor
code gets a chance to handle things like reaping the PML buffer
- Update KVM's view of PV async enabling if and only if the MSR write
fully succeeds
- Fix a variety of issues where the emulator doesn't honor
guest-debug state, and clean up related code along the way
- Synthesize EPT Violation and #NPF "error code" bits when injecting
faults into L1 that didn't originate in hardware (in which case the
VMCS/VMCB doesn't hold relevant information)
- Add support for virtualizing (well, emulating) AMD's flavor of
CPL>0 CPUID faulting
- Clean up the GPR APIs so that KVM's use of "raw" is consistent, and
fix a variety of minor bugs along the way
- Fix an OOB memory access due to not checking the VP ID when
handling a Hyper-V PV TLB flush for L2
- Fix a bug in the mediated PMU's handling of fixed counters that
allowed the guest to bypass the PMU event filter
- Allow userspace to return EAGAIN when handling SNP and TDX
hypercalls, so the KVM can forward a "retry" status code to the
guest, and reserve all unused error codes for future usage
- Overhaul the TDP MMU => S-EPT code to move as much S-EPT specific
logic as possible into the TDX code, and to funnel (almost) all
S-EPT updates into a single chokepoint. The motivation is largely
to prepare for upcoming Dynamic PAMT support, but the cleanups are
nice to have on their own
- Plug a hole in shadow page table handling, where KVM fails to
recursively zap nested EPT/NPT shadow page tables when the nested
hypervisor tears down its own EPT/NPT page tables from the bottom
up
x86 (Intel):
- Support for nested MBEC (Mode-Based Execute Control), see above in
the generic section; also run with MBEC enabled even for non-nested
mode
- Use the kernel's "enum pg_level" in the TDX APIs instead of the
TDX-Module's level definitions (which are 0-based)
- Rework the TDX memory APIs to not require/assume that guest memory
is backed by "struct page" (in prepartion for guest_memfd hugepage
support)
- Fix a largely benign bug where KVM TDX would incorrectly state it
could emulate several x2APIC MSRs
- Use the "safe" WRMSR API when proxying LBR MSR writes as the
to-be-written value is guest controlled and completely unvalidated
x86 (AMD):
- Support for nested GMET (Guest Mode Execution Trap), see above in
the generic section; also run with GMET enabled even for non-nested
mode
- Fixes and minor cleanups to GHCB handling, on top of the earlier
work already merged into 7.1-rc
- Ensure KVM's copy of CR0 and CR3 are up-to-date prior to invoking
fastpath handlers
- Add support for virtualizing gPAT (KVM previously just used L1's
PAT when running L2)
- Fix goofs where KVM mishandles side effects (e.g. single-step and
PMC updates) when emulating VMRUN
- Fix a variety of bugs in AVIC's handling of x2APIC MSR
interception, most notably where KVM didn't disable interception of
IRR, ISR, and TMR regs
- Add support for virtualizing Host-Only/Guest-Only bits in the
mediated PMU
- Don't advertise support for unusable VM types, and account for VM
types that are disabled by firmware, e.g. to mitigate security
vulnerabilities
- Rewrite the SEV {en,de}crypt debug ioctls as they were riddle with
bugs and unnecessarily complicated, and add comprehensive tests
- Clean up and deduplicate the SEV page pinning code
- Fix minor goofs related to writing back CPUID information after
firmware rejects a CPUID page for an SNP vCPU
Generic:
- Rename invalidate_begin() to invalidate_start() throughout KVM to
follow the kernel's nomenclature, e.g. for mmu_notifiers
- Use guard() to cleanup up various KVM+VFIO flows
- Minor cleanups
guest_memfd:
- Return -EEXIST instead of -EINVAL if userspace attempts to bind a
gmem range to multiple memslots, and fix the test that was supposed
to ensure KVM returns -EEXIST
- Treat memslot binding offsets and sizes as unsigned values to fix a
bug where KVM interprets a large "offset + size" as a negative
value and allows a nonsensical offset
- Use the inode number instead of the page offset for the NUMA
interleaving index to fix a bug where the effective index would
jump by two for consecutive pages (the caller also adds in the page
offset)
Selftests:
- Randomize the dirty log test's delay when reaping the bitmap on the
first pass, as always waiting only 1ms hid a KVM RISC-V bug as the
test reaped the bitmap before KVM could build up enough state to
hit the bug
- A pile of one-off fixes and cleanups"
* tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm: (326 commits)
KVM: x86/mmu: Ensure hugepage is in by slot before checking max mapping level
KVM: x86: Fix shadow paging use-after-free due to unexpected role
KVM: s390: Introducing kvm_arch_set_irq_inatomic fast inject
KVM: s390: Enable adapter_indicators_set to use mapped pages
KVM: s390: Add map/unmap ioctl and clean mappings post-guest
riscv: kvm: Use endian-specific __lelong for NACL shared memory
KVM: selftests: access_tracking_perf_test: bump number of NUMA nodes to 32
KVM: s390: vsie: Implement ASTFLEIE facility 2
KVM: s390: vsie: Refactor handle_stfle
s390/sclp: Detect ASTFLEIE 2 facility
KVM: s390: Minor refactor of base/ext facility lists
KVM: x86/mmu: move pdptrs out of the MMU
KVM: x86: check that kvm_handle_invpcid is only invoked with shadow paging
KVM: nSVM: invalidate cached PDPTRs across nested NPT transitions
KVM: nVMX: remove unnecessary code in prepare_vmcs02_rare
KVM: x86: remove nested_mmu from mmu_is_nested()
KVM: arm64: vgic-its: Make ABI commit helpers return void
KVM: s390: Initialize KVM_S390_GET_CMMA_BITS memory
LoongArch: KVM: Add missing slots_lock for device register/unregister
LoongArch: KVM: Validate irqchip index in irqfd routing
...
862 lines
24 KiB
C
862 lines
24 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* KVM PMU support for Intel CPUs
|
|
*
|
|
* Copyright 2011 Red Hat, Inc. and/or its affiliates.
|
|
*
|
|
* Authors:
|
|
* Avi Kivity <avi@redhat.com>
|
|
* Gleb Natapov <gleb@redhat.com>
|
|
*/
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/kvm_host.h>
|
|
#include <linux/perf_event.h>
|
|
#include <asm/msr.h>
|
|
#include <asm/perf_event.h>
|
|
#include <asm/cpuid/api.h>
|
|
#include "x86.h"
|
|
#include "cpuid.h"
|
|
#include "lapic.h"
|
|
#include "nested.h"
|
|
#include "pmu.h"
|
|
#include "tdx.h"
|
|
|
|
/*
|
|
* Perf's "BASE" is wildly misleading, architectural PMUs use bits 31:16 of ECX
|
|
* to encode the "type" of counter to read, i.e. this is not a "base". And to
|
|
* further confuse things, non-architectural PMUs use bit 31 as a flag for
|
|
* "fast" reads, whereas the "type" is an explicit value.
|
|
*/
|
|
#define INTEL_RDPMC_GP 0
|
|
#define INTEL_RDPMC_FIXED INTEL_PMC_FIXED_RDPMC_BASE
|
|
|
|
#define INTEL_RDPMC_TYPE_MASK GENMASK(31, 16)
|
|
#define INTEL_RDPMC_INDEX_MASK GENMASK(15, 0)
|
|
|
|
#define MSR_PMC_FULL_WIDTH_BIT (MSR_IA32_PMC0 - MSR_IA32_PERFCTR0)
|
|
|
|
static struct lbr_desc *vcpu_to_lbr_desc(struct kvm_vcpu *vcpu)
|
|
{
|
|
if (is_td_vcpu(vcpu))
|
|
return NULL;
|
|
|
|
return &to_vmx(vcpu)->lbr_desc;
|
|
}
|
|
|
|
static struct x86_pmu_lbr *vcpu_to_lbr_records(struct kvm_vcpu *vcpu)
|
|
{
|
|
if (is_td_vcpu(vcpu))
|
|
return NULL;
|
|
|
|
return &to_vmx(vcpu)->lbr_desc.records;
|
|
}
|
|
|
|
#pragma GCC poison to_vmx
|
|
|
|
static void reprogram_fixed_counters(struct kvm_pmu *pmu, u64 data)
|
|
{
|
|
/*
|
|
* Compare against the value the mediated PMU shoves into hardware, not
|
|
* the guest's desired value. For the emulated PMU (proxied via perf),
|
|
* they are one and the same (fixed_ctr_ctrl_hw isn't used other than
|
|
* here). For the mediated PMU, KVM needs to reprogram the actual MSR,
|
|
* and so needs to react to potential changes in the value shoved into
|
|
* hardware, e.g. to ensure the event filter is enforced.
|
|
*/
|
|
u64 old_fixed_ctr_ctrl = pmu->fixed_ctr_ctrl_hw;
|
|
struct kvm_pmc *pmc;
|
|
int i;
|
|
|
|
pmu->fixed_ctr_ctrl = data;
|
|
pmu->fixed_ctr_ctrl_hw = data;
|
|
for (i = 0; i < pmu->nr_arch_fixed_counters; i++) {
|
|
u8 new_ctrl = fixed_ctrl_field(data, i);
|
|
u8 old_ctrl = fixed_ctrl_field(old_fixed_ctr_ctrl, i);
|
|
|
|
if (old_ctrl == new_ctrl)
|
|
continue;
|
|
|
|
pmc = get_fixed_pmc(pmu, MSR_CORE_PERF_FIXED_CTR0 + i);
|
|
|
|
__set_bit(KVM_FIXED_PMC_BASE_IDX + i, pmu->pmc_in_use);
|
|
kvm_pmu_request_counter_reprogram(pmc);
|
|
}
|
|
}
|
|
|
|
static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
|
|
unsigned int idx, u64 *mask)
|
|
{
|
|
unsigned int type = idx & INTEL_RDPMC_TYPE_MASK;
|
|
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
|
|
struct kvm_pmc *counters;
|
|
unsigned int num_counters;
|
|
u64 bitmask;
|
|
|
|
/*
|
|
* The encoding of ECX for RDPMC is different for architectural versus
|
|
* non-architecturals PMUs (PMUs with version '0'). For architectural
|
|
* PMUs, bits 31:16 specify the PMC type and bits 15:0 specify the PMC
|
|
* index. For non-architectural PMUs, bit 31 is a "fast" flag, and
|
|
* bits 30:0 specify the PMC index.
|
|
*
|
|
* Yell and reject attempts to read PMCs for a non-architectural PMU,
|
|
* as KVM doesn't support such PMUs.
|
|
*/
|
|
if (WARN_ON_ONCE(!pmu->version))
|
|
return NULL;
|
|
|
|
/*
|
|
* General Purpose (GP) PMCs are supported on all PMUs, and fixed PMCs
|
|
* are supported on all architectural PMUs, i.e. on all virtual PMUs
|
|
* supported by KVM. Note, KVM only emulates fixed PMCs for PMU v2+,
|
|
* but the type itself is still valid, i.e. let RDPMC fail due to
|
|
* accessing a non-existent counter. Reject attempts to read all other
|
|
* types, which are unknown/unsupported.
|
|
*/
|
|
switch (type) {
|
|
case INTEL_RDPMC_FIXED:
|
|
counters = pmu->fixed_counters;
|
|
num_counters = pmu->nr_arch_fixed_counters;
|
|
bitmask = pmu->counter_bitmask[KVM_PMC_FIXED];
|
|
break;
|
|
case INTEL_RDPMC_GP:
|
|
counters = pmu->gp_counters;
|
|
num_counters = pmu->nr_arch_gp_counters;
|
|
bitmask = pmu->counter_bitmask[KVM_PMC_GP];
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
idx &= INTEL_RDPMC_INDEX_MASK;
|
|
if (idx >= num_counters)
|
|
return NULL;
|
|
|
|
*mask &= bitmask;
|
|
return &counters[array_index_nospec(idx, num_counters)];
|
|
}
|
|
|
|
static inline struct kvm_pmc *get_fw_gp_pmc(struct kvm_pmu *pmu, u32 msr)
|
|
{
|
|
if (!fw_writes_is_enabled(pmu_to_vcpu(pmu)))
|
|
return NULL;
|
|
|
|
return get_gp_pmc(pmu, msr, MSR_IA32_PMC0);
|
|
}
|
|
|
|
static bool intel_pmu_lbr_is_compatible(struct kvm_vcpu *vcpu)
|
|
{
|
|
if (is_td_vcpu(vcpu))
|
|
return false;
|
|
|
|
return cpuid_model_is_consistent(vcpu);
|
|
}
|
|
|
|
bool intel_pmu_lbr_is_enabled(struct kvm_vcpu *vcpu)
|
|
{
|
|
if (is_td_vcpu(vcpu))
|
|
return false;
|
|
|
|
return !!vcpu_to_lbr_records(vcpu)->nr;
|
|
}
|
|
|
|
static bool intel_pmu_is_valid_lbr_msr(struct kvm_vcpu *vcpu, u32 index)
|
|
{
|
|
struct x86_pmu_lbr *records = vcpu_to_lbr_records(vcpu);
|
|
bool ret = false;
|
|
|
|
if (!intel_pmu_lbr_is_enabled(vcpu))
|
|
return ret;
|
|
|
|
ret = (index == MSR_LBR_SELECT) || (index == MSR_LBR_TOS) ||
|
|
(index >= records->from && index < records->from + records->nr) ||
|
|
(index >= records->to && index < records->to + records->nr);
|
|
|
|
if (!ret && records->info)
|
|
ret = (index >= records->info && index < records->info + records->nr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool intel_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
|
|
{
|
|
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
|
|
u64 perf_capabilities;
|
|
int ret;
|
|
|
|
switch (msr) {
|
|
case MSR_CORE_PERF_FIXED_CTR_CTRL:
|
|
return kvm_pmu_has_perf_global_ctrl(pmu);
|
|
case MSR_IA32_PEBS_ENABLE:
|
|
ret = vcpu_get_perf_capabilities(vcpu) & PERF_CAP_PEBS_FORMAT;
|
|
break;
|
|
case MSR_IA32_DS_AREA:
|
|
ret = guest_cpu_cap_has(vcpu, X86_FEATURE_DS);
|
|
break;
|
|
case MSR_PEBS_DATA_CFG:
|
|
perf_capabilities = vcpu_get_perf_capabilities(vcpu);
|
|
ret = (perf_capabilities & PERF_CAP_PEBS_BASELINE) &&
|
|
((perf_capabilities & PERF_CAP_PEBS_FORMAT) > 3);
|
|
break;
|
|
default:
|
|
ret = get_gp_pmc(pmu, msr, MSR_IA32_PERFCTR0) ||
|
|
get_gp_pmc(pmu, msr, MSR_P6_EVNTSEL0) ||
|
|
get_fixed_pmc(pmu, msr) || get_fw_gp_pmc(pmu, msr) ||
|
|
intel_pmu_is_valid_lbr_msr(vcpu, msr);
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct kvm_pmc *intel_msr_idx_to_pmc(struct kvm_vcpu *vcpu, u32 msr)
|
|
{
|
|
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
|
|
struct kvm_pmc *pmc;
|
|
|
|
pmc = get_fixed_pmc(pmu, msr);
|
|
pmc = pmc ? pmc : get_gp_pmc(pmu, msr, MSR_P6_EVNTSEL0);
|
|
pmc = pmc ? pmc : get_gp_pmc(pmu, msr, MSR_IA32_PERFCTR0);
|
|
|
|
return pmc;
|
|
}
|
|
|
|
static inline void intel_pmu_release_guest_lbr_event(struct kvm_vcpu *vcpu)
|
|
{
|
|
struct lbr_desc *lbr_desc = vcpu_to_lbr_desc(vcpu);
|
|
|
|
if (!lbr_desc)
|
|
return;
|
|
|
|
if (lbr_desc->event) {
|
|
perf_event_release_kernel(lbr_desc->event);
|
|
lbr_desc->event = NULL;
|
|
vcpu_to_pmu(vcpu)->event_count--;
|
|
}
|
|
}
|
|
|
|
int intel_pmu_create_guest_lbr_event(struct kvm_vcpu *vcpu)
|
|
{
|
|
struct lbr_desc *lbr_desc = vcpu_to_lbr_desc(vcpu);
|
|
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
|
|
struct perf_event *event;
|
|
|
|
/*
|
|
* The perf_event_attr is constructed in the minimum efficient way:
|
|
* - set 'pinned = true' to make it task pinned so that if another
|
|
* cpu pinned event reclaims LBR, the event->oncpu will be set to -1;
|
|
* - set '.exclude_host = true' to record guest branches behavior;
|
|
*
|
|
* - set '.config = INTEL_FIXED_VLBR_EVENT' to indicates host perf
|
|
* schedule the event without a real HW counter but a fake one;
|
|
* check is_guest_lbr_event() and __intel_get_event_constraints();
|
|
*
|
|
* - set 'sample_type = PERF_SAMPLE_BRANCH_STACK' and
|
|
* 'branch_sample_type = PERF_SAMPLE_BRANCH_CALL_STACK |
|
|
* PERF_SAMPLE_BRANCH_USER' to configure it as a LBR callstack
|
|
* event, which helps KVM to save/restore guest LBR records
|
|
* during host context switches and reduces quite a lot overhead,
|
|
* check branch_user_callstack() and intel_pmu_lbr_sched_task();
|
|
*/
|
|
struct perf_event_attr attr = {
|
|
.type = PERF_TYPE_RAW,
|
|
.size = sizeof(attr),
|
|
.config = INTEL_FIXED_VLBR_EVENT,
|
|
.sample_type = PERF_SAMPLE_BRANCH_STACK,
|
|
.pinned = true,
|
|
.exclude_host = true,
|
|
.branch_sample_type = PERF_SAMPLE_BRANCH_CALL_STACK |
|
|
PERF_SAMPLE_BRANCH_USER,
|
|
};
|
|
|
|
if (WARN_ON_ONCE(!lbr_desc))
|
|
return 0;
|
|
|
|
if (unlikely(lbr_desc->event)) {
|
|
__set_bit(INTEL_PMC_IDX_FIXED_VLBR, pmu->pmc_in_use);
|
|
return 0;
|
|
}
|
|
|
|
event = perf_event_create_kernel_counter(&attr, -1,
|
|
current, NULL, NULL);
|
|
if (IS_ERR(event)) {
|
|
pr_debug_ratelimited("%s: failed %ld\n",
|
|
__func__, PTR_ERR(event));
|
|
return PTR_ERR(event);
|
|
}
|
|
lbr_desc->event = event;
|
|
pmu->event_count++;
|
|
__set_bit(INTEL_PMC_IDX_FIXED_VLBR, pmu->pmc_in_use);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* It's safe to access LBR msrs from guest when they have not
|
|
* been passthrough since the host would help restore or reset
|
|
* the LBR msrs records when the guest LBR event is scheduled in.
|
|
*/
|
|
static bool intel_pmu_handle_lbr_msrs_access(struct kvm_vcpu *vcpu,
|
|
struct msr_data *msr_info, bool read)
|
|
{
|
|
struct lbr_desc *lbr_desc = vcpu_to_lbr_desc(vcpu);
|
|
u32 index = msr_info->index;
|
|
|
|
if (!intel_pmu_is_valid_lbr_msr(vcpu, index))
|
|
return false;
|
|
|
|
if (!lbr_desc->event && intel_pmu_create_guest_lbr_event(vcpu) < 0)
|
|
goto dummy;
|
|
|
|
/*
|
|
* Disable irq to ensure the LBR feature doesn't get reclaimed by the
|
|
* host at the time the value is read from the msr, and this avoids the
|
|
* host LBR value to be leaked to the guest. If LBR has been reclaimed,
|
|
* return 0 on guest reads.
|
|
*/
|
|
local_irq_disable();
|
|
if (lbr_desc->event->state == PERF_EVENT_STATE_ACTIVE) {
|
|
int err = 0;
|
|
|
|
if (read)
|
|
rdmsrq(index, msr_info->data);
|
|
else
|
|
err = wrmsrq_safe(index, msr_info->data);
|
|
__set_bit(INTEL_PMC_IDX_FIXED_VLBR, vcpu_to_pmu(vcpu)->pmc_in_use);
|
|
local_irq_enable();
|
|
return !err;
|
|
}
|
|
clear_bit(INTEL_PMC_IDX_FIXED_VLBR, vcpu_to_pmu(vcpu)->pmc_in_use);
|
|
local_irq_enable();
|
|
|
|
dummy:
|
|
if (read)
|
|
msr_info->data = 0;
|
|
return true;
|
|
}
|
|
|
|
static int intel_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
|
|
{
|
|
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
|
|
struct kvm_pmc *pmc;
|
|
u32 msr = msr_info->index;
|
|
|
|
switch (msr) {
|
|
case MSR_CORE_PERF_FIXED_CTR_CTRL:
|
|
msr_info->data = pmu->fixed_ctr_ctrl;
|
|
break;
|
|
case MSR_IA32_PEBS_ENABLE:
|
|
msr_info->data = pmu->pebs_enable;
|
|
break;
|
|
case MSR_IA32_DS_AREA:
|
|
msr_info->data = pmu->ds_area;
|
|
break;
|
|
case MSR_PEBS_DATA_CFG:
|
|
msr_info->data = pmu->pebs_data_cfg;
|
|
break;
|
|
default:
|
|
if ((pmc = get_gp_pmc(pmu, msr, MSR_IA32_PERFCTR0)) ||
|
|
(pmc = get_gp_pmc(pmu, msr, MSR_IA32_PMC0))) {
|
|
u64 val = pmc_read_counter(pmc);
|
|
msr_info->data =
|
|
val & pmu->counter_bitmask[KVM_PMC_GP];
|
|
break;
|
|
} else if ((pmc = get_fixed_pmc(pmu, msr))) {
|
|
u64 val = pmc_read_counter(pmc);
|
|
msr_info->data =
|
|
val & pmu->counter_bitmask[KVM_PMC_FIXED];
|
|
break;
|
|
} else if ((pmc = get_gp_pmc(pmu, msr, MSR_P6_EVNTSEL0))) {
|
|
msr_info->data = pmc->eventsel;
|
|
break;
|
|
} else if (intel_pmu_handle_lbr_msrs_access(vcpu, msr_info, true)) {
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
|
|
{
|
|
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
|
|
struct kvm_pmc *pmc;
|
|
u32 msr = msr_info->index;
|
|
u64 data = msr_info->data;
|
|
u64 reserved_bits, diff;
|
|
|
|
switch (msr) {
|
|
case MSR_CORE_PERF_FIXED_CTR_CTRL:
|
|
if (data & pmu->fixed_ctr_ctrl_rsvd)
|
|
return 1;
|
|
|
|
if (pmu->fixed_ctr_ctrl != data)
|
|
reprogram_fixed_counters(pmu, data);
|
|
break;
|
|
case MSR_IA32_PEBS_ENABLE:
|
|
if (data & pmu->pebs_enable_rsvd)
|
|
return 1;
|
|
|
|
if (pmu->pebs_enable != data) {
|
|
diff = pmu->pebs_enable ^ data;
|
|
pmu->pebs_enable = data;
|
|
kvm_pmu_request_counters_reprogram(pmu, diff);
|
|
}
|
|
break;
|
|
case MSR_IA32_DS_AREA:
|
|
if (is_noncanonical_msr_address(data, vcpu))
|
|
return 1;
|
|
|
|
pmu->ds_area = data;
|
|
break;
|
|
case MSR_PEBS_DATA_CFG:
|
|
if (data & pmu->pebs_data_cfg_rsvd)
|
|
return 1;
|
|
|
|
pmu->pebs_data_cfg = data;
|
|
break;
|
|
default:
|
|
if ((pmc = get_gp_pmc(pmu, msr, MSR_IA32_PERFCTR0)) ||
|
|
(pmc = get_gp_pmc(pmu, msr, MSR_IA32_PMC0))) {
|
|
if ((msr & MSR_PMC_FULL_WIDTH_BIT) &&
|
|
(data & ~pmu->counter_bitmask[KVM_PMC_GP]))
|
|
return 1;
|
|
|
|
if (!msr_info->host_initiated &&
|
|
!(msr & MSR_PMC_FULL_WIDTH_BIT))
|
|
data = (s64)(s32)data;
|
|
pmc_write_counter(pmc, data);
|
|
break;
|
|
} else if ((pmc = get_fixed_pmc(pmu, msr))) {
|
|
pmc_write_counter(pmc, data);
|
|
break;
|
|
} else if ((pmc = get_gp_pmc(pmu, msr, MSR_P6_EVNTSEL0))) {
|
|
reserved_bits = pmu->reserved_bits;
|
|
if ((pmc->idx == 2) &&
|
|
(pmu->raw_event_mask & HSW_IN_TX_CHECKPOINTED))
|
|
reserved_bits ^= HSW_IN_TX_CHECKPOINTED;
|
|
if (data & reserved_bits)
|
|
return 1;
|
|
|
|
if (data != pmc->eventsel) {
|
|
pmc->eventsel = data;
|
|
pmc->eventsel_hw = data;
|
|
kvm_pmu_request_counter_reprogram(pmc);
|
|
}
|
|
break;
|
|
} else if (intel_pmu_handle_lbr_msrs_access(vcpu, msr_info, false)) {
|
|
break;
|
|
}
|
|
/* Not a known PMU MSR. */
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Map fixed counter events to architectural general purpose event encodings.
|
|
* Perf doesn't provide APIs to allow KVM to directly program a fixed counter,
|
|
* and so KVM instead programs the architectural event to effectively request
|
|
* the fixed counter. Perf isn't guaranteed to use a fixed counter and may
|
|
* instead program the encoding into a general purpose counter, e.g. if a
|
|
* different perf_event is already utilizing the requested counter, but the end
|
|
* result is the same (ignoring the fact that using a general purpose counter
|
|
* will likely exacerbate counter contention).
|
|
*
|
|
* Forcibly inlined to allow asserting on @index at build time, and there should
|
|
* never be more than one user.
|
|
*/
|
|
static __always_inline u64 intel_get_fixed_pmc_eventsel(unsigned int index)
|
|
{
|
|
const enum perf_hw_id fixed_pmc_perf_ids[] = {
|
|
[0] = PERF_COUNT_HW_INSTRUCTIONS,
|
|
[1] = PERF_COUNT_HW_CPU_CYCLES,
|
|
[2] = PERF_COUNT_HW_REF_CPU_CYCLES,
|
|
};
|
|
u64 eventsel;
|
|
|
|
BUILD_BUG_ON(ARRAY_SIZE(fixed_pmc_perf_ids) != KVM_MAX_NR_INTEL_FIXED_COUNTERS);
|
|
BUILD_BUG_ON(index >= KVM_MAX_NR_INTEL_FIXED_COUNTERS);
|
|
|
|
/*
|
|
* Yell if perf reports support for a fixed counter but perf doesn't
|
|
* have a known encoding for the associated general purpose event.
|
|
*/
|
|
eventsel = perf_get_hw_event_config(fixed_pmc_perf_ids[index]);
|
|
WARN_ON_ONCE(!eventsel && index < kvm_pmu_cap.num_counters_fixed);
|
|
return eventsel;
|
|
}
|
|
|
|
static void intel_pmu_enable_fixed_counter_bits(struct kvm_pmu *pmu, u64 bits)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < pmu->nr_arch_fixed_counters; i++)
|
|
pmu->fixed_ctr_ctrl_rsvd &= ~intel_fixed_bits_by_idx(i, bits);
|
|
}
|
|
|
|
static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
|
|
{
|
|
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
|
|
struct lbr_desc *lbr_desc = vcpu_to_lbr_desc(vcpu);
|
|
struct kvm_cpuid_entry2 *entry;
|
|
union cpuid10_eax eax;
|
|
union cpuid10_edx edx;
|
|
u64 perf_capabilities;
|
|
u64 counter_rsvd;
|
|
|
|
if (!lbr_desc)
|
|
return;
|
|
|
|
memset(&lbr_desc->records, 0, sizeof(lbr_desc->records));
|
|
|
|
/*
|
|
* Setting passthrough of LBR MSRs is done only in the VM-Entry loop,
|
|
* and PMU refresh is disallowed after the vCPU has run, i.e. this code
|
|
* should never be reached while KVM is passing through MSRs.
|
|
*/
|
|
if (KVM_BUG_ON(lbr_desc->msr_passthrough, vcpu->kvm))
|
|
return;
|
|
|
|
entry = kvm_find_cpuid_entry(vcpu, 0xa);
|
|
if (!entry)
|
|
return;
|
|
|
|
eax.full = entry->eax;
|
|
edx.full = entry->edx;
|
|
|
|
pmu->version = eax.split.version_id;
|
|
if (!pmu->version)
|
|
return;
|
|
|
|
pmu->nr_arch_gp_counters = min_t(int, eax.split.num_counters,
|
|
kvm_pmu_cap.num_counters_gp);
|
|
eax.split.bit_width = min_t(int, eax.split.bit_width,
|
|
kvm_pmu_cap.bit_width_gp);
|
|
pmu->counter_bitmask[KVM_PMC_GP] = BIT_ULL(eax.split.bit_width) - 1;
|
|
eax.split.mask_length = min_t(int, eax.split.mask_length,
|
|
kvm_pmu_cap.events_mask_len);
|
|
pmu->available_event_types = ~entry->ebx & (BIT_ULL(eax.split.mask_length) - 1);
|
|
|
|
entry = kvm_find_cpuid_entry_index(vcpu, 7, 0);
|
|
if (entry &&
|
|
(boot_cpu_has(X86_FEATURE_HLE) || boot_cpu_has(X86_FEATURE_RTM)) &&
|
|
(entry->ebx & (X86_FEATURE_HLE|X86_FEATURE_RTM))) {
|
|
pmu->reserved_bits ^= HSW_IN_TX;
|
|
pmu->raw_event_mask |= (HSW_IN_TX|HSW_IN_TX_CHECKPOINTED);
|
|
}
|
|
|
|
perf_capabilities = vcpu_get_perf_capabilities(vcpu);
|
|
if (intel_pmu_lbr_is_compatible(vcpu) &&
|
|
(perf_capabilities & PERF_CAP_LBR_FMT))
|
|
memcpy(&lbr_desc->records, &vmx_lbr_caps, sizeof(vmx_lbr_caps));
|
|
else
|
|
lbr_desc->records.nr = 0;
|
|
|
|
if (lbr_desc->records.nr)
|
|
bitmap_set(pmu->all_valid_pmc_idx, INTEL_PMC_IDX_FIXED_VLBR, 1);
|
|
|
|
if (pmu->version == 1)
|
|
return;
|
|
|
|
pmu->nr_arch_fixed_counters = min_t(int, edx.split.num_counters_fixed,
|
|
kvm_pmu_cap.num_counters_fixed);
|
|
edx.split.bit_width_fixed = min_t(int, edx.split.bit_width_fixed,
|
|
kvm_pmu_cap.bit_width_fixed);
|
|
pmu->counter_bitmask[KVM_PMC_FIXED] = BIT_ULL(edx.split.bit_width_fixed) - 1;
|
|
|
|
intel_pmu_enable_fixed_counter_bits(pmu, INTEL_FIXED_0_KERNEL |
|
|
INTEL_FIXED_0_USER |
|
|
INTEL_FIXED_0_ENABLE_PMI);
|
|
|
|
counter_rsvd = ~((BIT_ULL(pmu->nr_arch_gp_counters) - 1) |
|
|
((BIT_ULL(pmu->nr_arch_fixed_counters) - 1) << KVM_FIXED_PMC_BASE_IDX));
|
|
pmu->global_ctrl_rsvd = counter_rsvd;
|
|
|
|
/*
|
|
* GLOBAL_STATUS and GLOBAL_OVF_CONTROL (a.k.a. GLOBAL_STATUS_RESET)
|
|
* share reserved bit definitions. The kernel just happens to use
|
|
* OVF_CTRL for the names.
|
|
*/
|
|
pmu->global_status_rsvd = pmu->global_ctrl_rsvd
|
|
& ~(MSR_CORE_PERF_GLOBAL_OVF_CTRL_OVF_BUF |
|
|
MSR_CORE_PERF_GLOBAL_OVF_CTRL_COND_CHGD);
|
|
if (vmx_pt_mode_is_host_guest())
|
|
pmu->global_status_rsvd &=
|
|
~MSR_CORE_PERF_GLOBAL_OVF_CTRL_TRACE_TOPA_PMI;
|
|
|
|
if (perf_capabilities & PERF_CAP_PEBS_FORMAT) {
|
|
if (perf_capabilities & PERF_CAP_PEBS_BASELINE) {
|
|
pmu->pebs_enable_rsvd = counter_rsvd;
|
|
pmu->reserved_bits &= ~ICL_EVENTSEL_ADAPTIVE;
|
|
pmu->pebs_data_cfg_rsvd = ~0xff00000full;
|
|
intel_pmu_enable_fixed_counter_bits(pmu, ICL_FIXED_0_ADAPTIVE);
|
|
} else {
|
|
pmu->pebs_enable_rsvd = ~(BIT_ULL(pmu->nr_arch_gp_counters) - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void intel_pmu_init(struct kvm_vcpu *vcpu)
|
|
{
|
|
int i;
|
|
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
|
|
struct lbr_desc *lbr_desc = vcpu_to_lbr_desc(vcpu);
|
|
|
|
if (!lbr_desc)
|
|
return;
|
|
|
|
for (i = 0; i < KVM_MAX_NR_INTEL_GP_COUNTERS; i++) {
|
|
pmu->gp_counters[i].type = KVM_PMC_GP;
|
|
pmu->gp_counters[i].vcpu = vcpu;
|
|
pmu->gp_counters[i].idx = i;
|
|
pmu->gp_counters[i].current_config = 0;
|
|
}
|
|
|
|
for (i = 0; i < KVM_MAX_NR_INTEL_FIXED_COUNTERS; i++) {
|
|
pmu->fixed_counters[i].type = KVM_PMC_FIXED;
|
|
pmu->fixed_counters[i].vcpu = vcpu;
|
|
pmu->fixed_counters[i].idx = i + KVM_FIXED_PMC_BASE_IDX;
|
|
pmu->fixed_counters[i].current_config = 0;
|
|
pmu->fixed_counters[i].eventsel = intel_get_fixed_pmc_eventsel(i);
|
|
}
|
|
|
|
lbr_desc->records.nr = 0;
|
|
lbr_desc->event = NULL;
|
|
lbr_desc->msr_passthrough = false;
|
|
}
|
|
|
|
static void intel_pmu_reset(struct kvm_vcpu *vcpu)
|
|
{
|
|
intel_pmu_release_guest_lbr_event(vcpu);
|
|
}
|
|
|
|
/*
|
|
* Emulate LBR_On_PMI behavior for 1 < pmu.version < 4.
|
|
*
|
|
* If Freeze_LBR_On_PMI = 1, the LBR is frozen on PMI and
|
|
* the KVM emulates to clear the LBR bit (bit 0) in IA32_DEBUGCTL.
|
|
*
|
|
* Guest needs to re-enable LBR to resume branches recording.
|
|
*/
|
|
static void intel_pmu_legacy_freezing_lbrs_on_pmi(struct kvm_vcpu *vcpu)
|
|
{
|
|
u64 data = vmx_guest_debugctl_read();
|
|
|
|
if (data & DEBUGCTLMSR_FREEZE_LBRS_ON_PMI) {
|
|
data &= ~DEBUGCTLMSR_LBR;
|
|
vmx_guest_debugctl_write(vcpu, data);
|
|
}
|
|
}
|
|
|
|
static void intel_pmu_deliver_pmi(struct kvm_vcpu *vcpu)
|
|
{
|
|
u8 version = vcpu_to_pmu(vcpu)->version;
|
|
|
|
if (!intel_pmu_lbr_is_enabled(vcpu))
|
|
return;
|
|
|
|
if (version > 1 && version < 4)
|
|
intel_pmu_legacy_freezing_lbrs_on_pmi(vcpu);
|
|
}
|
|
|
|
static void vmx_update_intercept_for_lbr_msrs(struct kvm_vcpu *vcpu, bool set)
|
|
{
|
|
struct x86_pmu_lbr *lbr = vcpu_to_lbr_records(vcpu);
|
|
int i;
|
|
|
|
for (i = 0; i < lbr->nr; i++) {
|
|
vmx_set_intercept_for_msr(vcpu, lbr->from + i, MSR_TYPE_RW, set);
|
|
vmx_set_intercept_for_msr(vcpu, lbr->to + i, MSR_TYPE_RW, set);
|
|
if (lbr->info)
|
|
vmx_set_intercept_for_msr(vcpu, lbr->info + i, MSR_TYPE_RW, set);
|
|
}
|
|
|
|
vmx_set_intercept_for_msr(vcpu, MSR_LBR_SELECT, MSR_TYPE_RW, set);
|
|
vmx_set_intercept_for_msr(vcpu, MSR_LBR_TOS, MSR_TYPE_RW, set);
|
|
}
|
|
|
|
static inline void vmx_disable_lbr_msrs_passthrough(struct kvm_vcpu *vcpu)
|
|
{
|
|
struct lbr_desc *lbr_desc = vcpu_to_lbr_desc(vcpu);
|
|
|
|
if (!lbr_desc->msr_passthrough)
|
|
return;
|
|
|
|
vmx_update_intercept_for_lbr_msrs(vcpu, true);
|
|
lbr_desc->msr_passthrough = false;
|
|
}
|
|
|
|
static inline void vmx_enable_lbr_msrs_passthrough(struct kvm_vcpu *vcpu)
|
|
{
|
|
struct lbr_desc *lbr_desc = vcpu_to_lbr_desc(vcpu);
|
|
|
|
if (lbr_desc->msr_passthrough)
|
|
return;
|
|
|
|
vmx_update_intercept_for_lbr_msrs(vcpu, false);
|
|
lbr_desc->msr_passthrough = true;
|
|
}
|
|
|
|
/*
|
|
* Higher priority host perf events (e.g. cpu pinned) could reclaim the
|
|
* pmu resources (e.g. LBR) that were assigned to the guest. This is
|
|
* usually done via ipi calls (more details in perf_install_in_context).
|
|
*
|
|
* Before entering the non-root mode (with irq disabled here), double
|
|
* confirm that the pmu features enabled to the guest are not reclaimed
|
|
* by higher priority host events. Otherwise, disallow vcpu's access to
|
|
* the reclaimed features.
|
|
*/
|
|
void vmx_passthrough_lbr_msrs(struct kvm_vcpu *vcpu)
|
|
{
|
|
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
|
|
struct lbr_desc *lbr_desc = vcpu_to_lbr_desc(vcpu);
|
|
|
|
if (WARN_ON_ONCE(!lbr_desc))
|
|
return;
|
|
|
|
if (!lbr_desc->event) {
|
|
vmx_disable_lbr_msrs_passthrough(vcpu);
|
|
if (vmx_guest_debugctl_read() & DEBUGCTLMSR_LBR)
|
|
goto warn;
|
|
if (test_bit(INTEL_PMC_IDX_FIXED_VLBR, pmu->pmc_in_use))
|
|
goto warn;
|
|
return;
|
|
}
|
|
|
|
if (lbr_desc->event->state < PERF_EVENT_STATE_ACTIVE) {
|
|
vmx_disable_lbr_msrs_passthrough(vcpu);
|
|
__clear_bit(INTEL_PMC_IDX_FIXED_VLBR, pmu->pmc_in_use);
|
|
goto warn;
|
|
} else
|
|
vmx_enable_lbr_msrs_passthrough(vcpu);
|
|
|
|
return;
|
|
|
|
warn:
|
|
pr_warn_ratelimited("vcpu-%d: fail to passthrough LBR.\n", vcpu->vcpu_id);
|
|
}
|
|
|
|
static void intel_pmu_cleanup(struct kvm_vcpu *vcpu)
|
|
{
|
|
if (!(vmx_guest_debugctl_read() & DEBUGCTLMSR_LBR))
|
|
intel_pmu_release_guest_lbr_event(vcpu);
|
|
}
|
|
|
|
void intel_pmu_cross_mapped_check(struct kvm_pmu *pmu)
|
|
{
|
|
struct kvm_pmc *pmc = NULL;
|
|
int bit, hw_idx;
|
|
|
|
kvm_for_each_pmc(pmu, pmc, bit, (unsigned long *)&pmu->global_ctrl) {
|
|
if (!pmc_is_locally_enabled(pmc) ||
|
|
!pmc_is_globally_enabled(pmc) || !pmc->perf_event)
|
|
continue;
|
|
|
|
/*
|
|
* A negative index indicates the event isn't mapped to a
|
|
* physical counter in the host, e.g. due to contention.
|
|
*/
|
|
hw_idx = pmc->perf_event->hw.idx;
|
|
if (hw_idx != pmc->idx && hw_idx > -1)
|
|
pmu->host_cross_mapped_mask |= BIT_ULL(hw_idx);
|
|
}
|
|
}
|
|
|
|
static bool intel_pmu_is_mediated_pmu_supported(struct x86_pmu_capability *host_pmu)
|
|
{
|
|
u64 host_perf_cap = 0;
|
|
|
|
if (boot_cpu_has(X86_FEATURE_PDCM))
|
|
rdmsrq(MSR_IA32_PERF_CAPABILITIES, host_perf_cap);
|
|
|
|
/*
|
|
* Require v4+ for MSR_CORE_PERF_GLOBAL_STATUS_SET, and full-width
|
|
* writes so that KVM can precisely load guest counter values.
|
|
*/
|
|
if (host_pmu->version < 4 || !(host_perf_cap & PERF_CAP_FW_WRITES))
|
|
return false;
|
|
|
|
/*
|
|
* All CPUs that support a mediated PMU are expected to support loading
|
|
* PERF_GLOBAL_CTRL via dedicated VMCS fields.
|
|
*/
|
|
if (WARN_ON_ONCE(!cpu_has_load_perf_global_ctrl()))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void intel_pmu_write_global_ctrl(u64 global_ctrl)
|
|
{
|
|
vmcs_write64(GUEST_IA32_PERF_GLOBAL_CTRL, global_ctrl);
|
|
}
|
|
|
|
|
|
static void intel_mediated_pmu_load(struct kvm_vcpu *vcpu)
|
|
{
|
|
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
|
|
u64 global_status, toggle;
|
|
|
|
rdmsrq(MSR_CORE_PERF_GLOBAL_STATUS, global_status);
|
|
toggle = pmu->global_status ^ global_status;
|
|
if (global_status & toggle)
|
|
wrmsrq(MSR_CORE_PERF_GLOBAL_OVF_CTRL, global_status & toggle);
|
|
if (pmu->global_status & toggle)
|
|
wrmsrq(MSR_CORE_PERF_GLOBAL_STATUS_SET, pmu->global_status & toggle);
|
|
|
|
wrmsrq(MSR_CORE_PERF_FIXED_CTR_CTRL, pmu->fixed_ctr_ctrl_hw);
|
|
}
|
|
|
|
static void intel_mediated_pmu_put(struct kvm_vcpu *vcpu)
|
|
{
|
|
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
|
|
|
|
/* MSR_CORE_PERF_GLOBAL_CTRL is already saved at VM-exit. */
|
|
rdmsrq(MSR_CORE_PERF_GLOBAL_STATUS, pmu->global_status);
|
|
|
|
/* Clear hardware MSR_CORE_PERF_GLOBAL_STATUS MSR, if non-zero. */
|
|
if (pmu->global_status)
|
|
wrmsrq(MSR_CORE_PERF_GLOBAL_OVF_CTRL, pmu->global_status);
|
|
|
|
/*
|
|
* Clear hardware FIXED_CTR_CTRL MSR to avoid information leakage and
|
|
* also to avoid accidentally enabling fixed counters (based on guest
|
|
* state) while running in the host, e.g. when setting global ctrl.
|
|
*/
|
|
if (pmu->fixed_ctr_ctrl_hw)
|
|
wrmsrq(MSR_CORE_PERF_FIXED_CTR_CTRL, 0);
|
|
}
|
|
|
|
struct kvm_pmu_ops intel_pmu_ops __initdata = {
|
|
.rdpmc_ecx_to_pmc = intel_rdpmc_ecx_to_pmc,
|
|
.msr_idx_to_pmc = intel_msr_idx_to_pmc,
|
|
.is_valid_msr = intel_is_valid_msr,
|
|
.get_msr = intel_pmu_get_msr,
|
|
.set_msr = intel_pmu_set_msr,
|
|
.refresh = intel_pmu_refresh,
|
|
.init = intel_pmu_init,
|
|
.reset = intel_pmu_reset,
|
|
.deliver_pmi = intel_pmu_deliver_pmi,
|
|
.cleanup = intel_pmu_cleanup,
|
|
|
|
.is_mediated_pmu_supported = intel_pmu_is_mediated_pmu_supported,
|
|
.mediated_load = intel_mediated_pmu_load,
|
|
.mediated_put = intel_mediated_pmu_put,
|
|
.write_global_ctrl = intel_pmu_write_global_ctrl,
|
|
|
|
.EVENTSEL_EVENT = ARCH_PERFMON_EVENTSEL_EVENT,
|
|
.MAX_NR_GP_COUNTERS = KVM_MAX_NR_INTEL_GP_COUNTERS,
|
|
.MIN_NR_GP_COUNTERS = 1,
|
|
|
|
.PERF_GLOBAL_CTRL = MSR_CORE_PERF_GLOBAL_CTRL,
|
|
.GP_EVENTSEL_BASE = MSR_P6_EVNTSEL0,
|
|
.GP_COUNTER_BASE = MSR_IA32_PMC0,
|
|
.FIXED_COUNTER_BASE = MSR_CORE_PERF_FIXED_CTR0,
|
|
.MSR_STRIDE = 1,
|
|
};
|