root/arch/arm64/kvm/pmu.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. kvm_pmu_switch_needed
  2. kvm_set_pmu_events
  3. kvm_clr_pmu_events
  4. kvm_vcpu_pmu_read_evtype_direct
  5. kvm_vcpu_pmu_write_evtype_direct
  6. kvm_vcpu_pmu_enable_el0
  7. kvm_vcpu_pmu_disable_el0
  8. kvm_vcpu_pmu_restore_guest
  9. kvm_vcpu_pmu_restore_host

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Copyright 2019 Arm Limited
   4  * Author: Andrew Murray <Andrew.Murray@arm.com>
   5  */
   6 #include <linux/kvm_host.h>
   7 #include <linux/perf_event.h>
   8 #include <asm/kvm_hyp.h>
   9 
  10 /*
  11  * Given the perf event attributes and system type, determine
  12  * if we are going to need to switch counters at guest entry/exit.
  13  */
  14 static bool kvm_pmu_switch_needed(struct perf_event_attr *attr)
  15 {
  16         /**
  17          * With VHE the guest kernel runs at EL1 and the host at EL2,
  18          * where user (EL0) is excluded then we have no reason to switch
  19          * counters.
  20          */
  21         if (has_vhe() && attr->exclude_user)
  22                 return false;
  23 
  24         /* Only switch if attributes are different */
  25         return (attr->exclude_host != attr->exclude_guest);
  26 }
  27 
  28 /*
  29  * Add events to track that we may want to switch at guest entry/exit
  30  * time.
  31  */
  32 void kvm_set_pmu_events(u32 set, struct perf_event_attr *attr)
  33 {
  34         struct kvm_host_data *ctx = this_cpu_ptr(&kvm_host_data);
  35 
  36         if (!kvm_pmu_switch_needed(attr))
  37                 return;
  38 
  39         if (!attr->exclude_host)
  40                 ctx->pmu_events.events_host |= set;
  41         if (!attr->exclude_guest)
  42                 ctx->pmu_events.events_guest |= set;
  43 }
  44 
  45 /*
  46  * Stop tracking events
  47  */
  48 void kvm_clr_pmu_events(u32 clr)
  49 {
  50         struct kvm_host_data *ctx = this_cpu_ptr(&kvm_host_data);
  51 
  52         ctx->pmu_events.events_host &= ~clr;
  53         ctx->pmu_events.events_guest &= ~clr;
  54 }
  55 
  56 #define PMEVTYPER_READ_CASE(idx)                                \
  57         case idx:                                               \
  58                 return read_sysreg(pmevtyper##idx##_el0)
  59 
  60 #define PMEVTYPER_WRITE_CASE(idx)                               \
  61         case idx:                                               \
  62                 write_sysreg(val, pmevtyper##idx##_el0);        \
  63                 break
  64 
  65 #define PMEVTYPER_CASES(readwrite)                              \
  66         PMEVTYPER_##readwrite##_CASE(0);                        \
  67         PMEVTYPER_##readwrite##_CASE(1);                        \
  68         PMEVTYPER_##readwrite##_CASE(2);                        \
  69         PMEVTYPER_##readwrite##_CASE(3);                        \
  70         PMEVTYPER_##readwrite##_CASE(4);                        \
  71         PMEVTYPER_##readwrite##_CASE(5);                        \
  72         PMEVTYPER_##readwrite##_CASE(6);                        \
  73         PMEVTYPER_##readwrite##_CASE(7);                        \
  74         PMEVTYPER_##readwrite##_CASE(8);                        \
  75         PMEVTYPER_##readwrite##_CASE(9);                        \
  76         PMEVTYPER_##readwrite##_CASE(10);                       \
  77         PMEVTYPER_##readwrite##_CASE(11);                       \
  78         PMEVTYPER_##readwrite##_CASE(12);                       \
  79         PMEVTYPER_##readwrite##_CASE(13);                       \
  80         PMEVTYPER_##readwrite##_CASE(14);                       \
  81         PMEVTYPER_##readwrite##_CASE(15);                       \
  82         PMEVTYPER_##readwrite##_CASE(16);                       \
  83         PMEVTYPER_##readwrite##_CASE(17);                       \
  84         PMEVTYPER_##readwrite##_CASE(18);                       \
  85         PMEVTYPER_##readwrite##_CASE(19);                       \
  86         PMEVTYPER_##readwrite##_CASE(20);                       \
  87         PMEVTYPER_##readwrite##_CASE(21);                       \
  88         PMEVTYPER_##readwrite##_CASE(22);                       \
  89         PMEVTYPER_##readwrite##_CASE(23);                       \
  90         PMEVTYPER_##readwrite##_CASE(24);                       \
  91         PMEVTYPER_##readwrite##_CASE(25);                       \
  92         PMEVTYPER_##readwrite##_CASE(26);                       \
  93         PMEVTYPER_##readwrite##_CASE(27);                       \
  94         PMEVTYPER_##readwrite##_CASE(28);                       \
  95         PMEVTYPER_##readwrite##_CASE(29);                       \
  96         PMEVTYPER_##readwrite##_CASE(30)
  97 
  98 /*
  99  * Read a value direct from PMEVTYPER<idx> where idx is 0-30
 100  * or PMCCFILTR_EL0 where idx is ARMV8_PMU_CYCLE_IDX (31).
 101  */
 102 static u64 kvm_vcpu_pmu_read_evtype_direct(int idx)
 103 {
 104         switch (idx) {
 105         PMEVTYPER_CASES(READ);
 106         case ARMV8_PMU_CYCLE_IDX:
 107                 return read_sysreg(pmccfiltr_el0);
 108         default:
 109                 WARN_ON(1);
 110         }
 111 
 112         return 0;
 113 }
 114 
 115 /*
 116  * Write a value direct to PMEVTYPER<idx> where idx is 0-30
 117  * or PMCCFILTR_EL0 where idx is ARMV8_PMU_CYCLE_IDX (31).
 118  */
 119 static void kvm_vcpu_pmu_write_evtype_direct(int idx, u32 val)
 120 {
 121         switch (idx) {
 122         PMEVTYPER_CASES(WRITE);
 123         case ARMV8_PMU_CYCLE_IDX:
 124                 write_sysreg(val, pmccfiltr_el0);
 125                 break;
 126         default:
 127                 WARN_ON(1);
 128         }
 129 }
 130 
 131 /*
 132  * Modify ARMv8 PMU events to include EL0 counting
 133  */
 134 static void kvm_vcpu_pmu_enable_el0(unsigned long events)
 135 {
 136         u64 typer;
 137         u32 counter;
 138 
 139         for_each_set_bit(counter, &events, 32) {
 140                 typer = kvm_vcpu_pmu_read_evtype_direct(counter);
 141                 typer &= ~ARMV8_PMU_EXCLUDE_EL0;
 142                 kvm_vcpu_pmu_write_evtype_direct(counter, typer);
 143         }
 144 }
 145 
 146 /*
 147  * Modify ARMv8 PMU events to exclude EL0 counting
 148  */
 149 static void kvm_vcpu_pmu_disable_el0(unsigned long events)
 150 {
 151         u64 typer;
 152         u32 counter;
 153 
 154         for_each_set_bit(counter, &events, 32) {
 155                 typer = kvm_vcpu_pmu_read_evtype_direct(counter);
 156                 typer |= ARMV8_PMU_EXCLUDE_EL0;
 157                 kvm_vcpu_pmu_write_evtype_direct(counter, typer);
 158         }
 159 }
 160 
 161 /*
 162  * On VHE ensure that only guest events have EL0 counting enabled
 163  */
 164 void kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu)
 165 {
 166         struct kvm_cpu_context *host_ctxt;
 167         struct kvm_host_data *host;
 168         u32 events_guest, events_host;
 169 
 170         if (!has_vhe())
 171                 return;
 172 
 173         host_ctxt = vcpu->arch.host_cpu_context;
 174         host = container_of(host_ctxt, struct kvm_host_data, host_ctxt);
 175         events_guest = host->pmu_events.events_guest;
 176         events_host = host->pmu_events.events_host;
 177 
 178         kvm_vcpu_pmu_enable_el0(events_guest);
 179         kvm_vcpu_pmu_disable_el0(events_host);
 180 }
 181 
 182 /*
 183  * On VHE ensure that only host events have EL0 counting enabled
 184  */
 185 void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu)
 186 {
 187         struct kvm_cpu_context *host_ctxt;
 188         struct kvm_host_data *host;
 189         u32 events_guest, events_host;
 190 
 191         if (!has_vhe())
 192                 return;
 193 
 194         host_ctxt = vcpu->arch.host_cpu_context;
 195         host = container_of(host_ctxt, struct kvm_host_data, host_ctxt);
 196         events_guest = host->pmu_events.events_guest;
 197         events_host = host->pmu_events.events_host;
 198 
 199         kvm_vcpu_pmu_enable_el0(events_host);
 200         kvm_vcpu_pmu_disable_el0(events_guest);
 201 }

/* [<][>][^][v][top][bottom][index][help] */