linux/arch/x86/kvm/pmu_amd.c
<<
>>
Prefs
   1/*
   2 * KVM PMU support for AMD
   3 *
   4 * Copyright 2015, Red Hat, Inc. and/or its affiliates.
   5 *
   6 * Author:
   7 *   Wei Huang <wei@redhat.com>
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2.  See
  10 * the COPYING file in the top-level directory.
  11 *
  12 * Implementation is based on pmu_intel.c file
  13 */
  14#include <linux/types.h>
  15#include <linux/kvm_host.h>
  16#include <linux/perf_event.h>
  17#include "x86.h"
  18#include "cpuid.h"
  19#include "lapic.h"
  20#include "pmu.h"
  21
  22/* duplicated from amd_perfmon_event_map, K7 and above should work. */
  23static struct kvm_event_hw_type_mapping amd_event_mapping[] = {
  24        [0] = { 0x76, 0x00, PERF_COUNT_HW_CPU_CYCLES },
  25        [1] = { 0xc0, 0x00, PERF_COUNT_HW_INSTRUCTIONS },
  26        [2] = { 0x7d, 0x07, PERF_COUNT_HW_CACHE_REFERENCES },
  27        [3] = { 0x7e, 0x07, PERF_COUNT_HW_CACHE_MISSES },
  28        [4] = { 0xc2, 0x00, PERF_COUNT_HW_BRANCH_INSTRUCTIONS },
  29        [5] = { 0xc3, 0x00, PERF_COUNT_HW_BRANCH_MISSES },
  30        [6] = { 0xd0, 0x00, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND },
  31        [7] = { 0xd1, 0x00, PERF_COUNT_HW_STALLED_CYCLES_BACKEND },
  32};
  33
  34static unsigned amd_find_arch_event(struct kvm_pmu *pmu,
  35                                    u8 event_select,
  36                                    u8 unit_mask)
  37{
  38        int i;
  39
  40        for (i = 0; i < ARRAY_SIZE(amd_event_mapping); i++)
  41                if (amd_event_mapping[i].eventsel == event_select
  42                    && amd_event_mapping[i].unit_mask == unit_mask)
  43                        break;
  44
  45        if (i == ARRAY_SIZE(amd_event_mapping))
  46                return PERF_COUNT_HW_MAX;
  47
  48        return amd_event_mapping[i].event_type;
  49}
  50
  51/* return PERF_COUNT_HW_MAX as AMD doesn't have fixed events */
  52static unsigned amd_find_fixed_event(int idx)
  53{
  54        return PERF_COUNT_HW_MAX;
  55}
  56
  57/* check if a PMC is enabled by comparing it against global_ctrl bits. Because
  58 * AMD CPU doesn't have global_ctrl MSR, all PMCs are enabled (return TRUE).
  59 */
  60static bool amd_pmc_is_enabled(struct kvm_pmc *pmc)
  61{
  62        return true;
  63}
  64
  65static struct kvm_pmc *amd_pmc_idx_to_pmc(struct kvm_pmu *pmu, int pmc_idx)
  66{
  67        return get_gp_pmc(pmu, MSR_K7_EVNTSEL0 + pmc_idx, MSR_K7_EVNTSEL0);
  68}
  69
  70/* returns 0 if idx's corresponding MSR exists; otherwise returns 1. */
  71static int amd_is_valid_msr_idx(struct kvm_vcpu *vcpu, unsigned idx)
  72{
  73        struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
  74
  75        idx &= ~(3u << 30);
  76
  77        return (idx >= pmu->nr_arch_gp_counters);
  78}
  79
  80/* idx is the ECX register of RDPMC instruction */
  81static struct kvm_pmc *amd_msr_idx_to_pmc(struct kvm_vcpu *vcpu, unsigned idx)
  82{
  83        struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
  84        struct kvm_pmc *counters;
  85
  86        idx &= ~(3u << 30);
  87        if (idx >= pmu->nr_arch_gp_counters)
  88                return NULL;
  89        counters = pmu->gp_counters;
  90
  91        return &counters[idx];
  92}
  93
  94static bool amd_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
  95{
  96        struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
  97        int ret = false;
  98
  99        ret = get_gp_pmc(pmu, msr, MSR_K7_PERFCTR0) ||
 100                get_gp_pmc(pmu, msr, MSR_K7_EVNTSEL0);
 101
 102        return ret;
 103}
 104
 105static int amd_pmu_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *data)
 106{
 107        struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
 108        struct kvm_pmc *pmc;
 109
 110        /* MSR_K7_PERFCTRn */
 111        pmc = get_gp_pmc(pmu, msr, MSR_K7_PERFCTR0);
 112        if (pmc) {
 113                *data = pmc_read_counter(pmc);
 114                return 0;
 115        }
 116        /* MSR_K7_EVNTSELn */
 117        pmc = get_gp_pmc(pmu, msr, MSR_K7_EVNTSEL0);
 118        if (pmc) {
 119                *data = pmc->eventsel;
 120                return 0;
 121        }
 122
 123        return 1;
 124}
 125
 126static int amd_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 127{
 128        struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
 129        struct kvm_pmc *pmc;
 130        u32 msr = msr_info->index;
 131        u64 data = msr_info->data;
 132
 133        /* MSR_K7_PERFCTRn */
 134        pmc = get_gp_pmc(pmu, msr, MSR_K7_PERFCTR0);
 135        if (pmc) {
 136                pmc->counter += data - pmc_read_counter(pmc);
 137                return 0;
 138        }
 139        /* MSR_K7_EVNTSELn */
 140        pmc = get_gp_pmc(pmu, msr, MSR_K7_EVNTSEL0);
 141        if (pmc) {
 142                if (data == pmc->eventsel)
 143                        return 0;
 144                if (!(data & pmu->reserved_bits)) {
 145                        reprogram_gp_counter(pmc, data);
 146                        return 0;
 147                }
 148        }
 149
 150        return 1;
 151}
 152
 153static void amd_pmu_refresh(struct kvm_vcpu *vcpu)
 154{
 155        struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
 156
 157        pmu->nr_arch_gp_counters = AMD64_NUM_COUNTERS;
 158        pmu->counter_bitmask[KVM_PMC_GP] = ((u64)1 << 48) - 1;
 159        pmu->reserved_bits = 0xffffffff00200000ull;
 160        /* not applicable to AMD; but clean them to prevent any fall out */
 161        pmu->counter_bitmask[KVM_PMC_FIXED] = 0;
 162        pmu->nr_arch_fixed_counters = 0;
 163        pmu->version = 0;
 164        pmu->global_status = 0;
 165}
 166
 167static void amd_pmu_init(struct kvm_vcpu *vcpu)
 168{
 169        struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
 170        int i;
 171
 172        for (i = 0; i < AMD64_NUM_COUNTERS ; i++) {
 173                pmu->gp_counters[i].type = KVM_PMC_GP;
 174                pmu->gp_counters[i].vcpu = vcpu;
 175                pmu->gp_counters[i].idx = i;
 176        }
 177}
 178
 179static void amd_pmu_reset(struct kvm_vcpu *vcpu)
 180{
 181        struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
 182        int i;
 183
 184        for (i = 0; i < AMD64_NUM_COUNTERS; i++) {
 185                struct kvm_pmc *pmc = &pmu->gp_counters[i];
 186
 187                pmc_stop_counter(pmc);
 188                pmc->counter = pmc->eventsel = 0;
 189        }
 190}
 191
 192struct kvm_pmu_ops amd_pmu_ops = {
 193        .find_arch_event = amd_find_arch_event,
 194        .find_fixed_event = amd_find_fixed_event,
 195        .pmc_is_enabled = amd_pmc_is_enabled,
 196        .pmc_idx_to_pmc = amd_pmc_idx_to_pmc,
 197        .msr_idx_to_pmc = amd_msr_idx_to_pmc,
 198        .is_valid_msr_idx = amd_is_valid_msr_idx,
 199        .is_valid_msr = amd_is_valid_msr,
 200        .get_msr = amd_pmu_get_msr,
 201        .set_msr = amd_pmu_set_msr,
 202        .refresh = amd_pmu_refresh,
 203        .init = amd_pmu_init,
 204        .reset = amd_pmu_reset,
 205};
 206