--- /dev/null
+/*
+ * Copyright (c) 2012 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+#include <mach/mach_types.h>
+#include <machine/machine_routines.h>
+#include <kern/processor.h>
+#include <kern/kalloc.h>
+#include <kern/thread.h>
+#include <sys/errno.h>
+#include <arm/cpu_data_internal.h>
+#include <arm/cpu_internal.h>
+#include <kern/kpc.h>
+
+#ifdef ARMA7
+/* PMU v2 based implementation for A7 */
+static uint32_t saved_PMXEVTYPER[MAX_CPUS][KPC_ARM_TOTAL_COUNT];
+static uint32_t saved_PMCNTENSET[MAX_CPUS];
+static uint64_t saved_counter[MAX_CPUS][KPC_ARM_TOTAL_COUNT];
+static uint32_t saved_PMOVSR[MAX_CPUS];
+
+static uint32_t kpc_configured = 0;
+static uint32_t kpc_xcall_sync;
+static uint64_t kpc_running_cfg_pmc_mask = 0;
+static uint32_t kpc_running_classes = 0;
+static uint32_t kpc_reload_sync;
+static uint32_t kpc_enabled_counters = 0;
+
+static int first_time = 1;
+
+/* Private */
+
+static boolean_t
+enable_counter(uint32_t counter)
+{
+ boolean_t enabled;
+ uint32_t PMCNTENSET;
+ /* Cycle counter is MSB; configurable counters reside in LSBs */
+ uint32_t mask = (counter == 0) ? (1 << 31) : (1 << (counter - 1));
+
+ /* Enabled? */
+ __asm__ volatile("mrc p15, 0, %0, c9, c12, 1;" : "=r" (PMCNTENSET));
+
+ enabled = (PMCNTENSET & mask);
+ if (!enabled) {
+ /* Counter interrupt enable (PMINTENSET) */
+ __asm__ volatile("mcr p15, 0, %0, c9, c14, 1;" : : "r" (mask));
+
+ /* Individual counter enable set (PMCNTENSET) */
+ __asm__ volatile("mcr p15, 0, %0, c9, c12, 1;" : : "r" (mask));
+
+ kpc_enabled_counters++;
+
+ /* 1st enabled counter? Set the master enable bit in PMCR */
+ if (kpc_enabled_counters == 1) {
+ uint32_t PMCR = 1;
+ __asm__ volatile("mcr p15, 0, %0, c9, c12, 0;" : : "r" (PMCR));
+ }
+ }
+
+ return enabled;
+}
+
+static boolean_t
+disable_counter(uint32_t counter)
+{
+ boolean_t enabled;
+ uint32_t PMCNTENCLR;
+ /* Cycle counter is MSB; configurable counters reside in LSBs */
+ uint32_t mask = (counter == 0) ? (1 << 31) : (1 << (counter - 1));
+
+ /* Enabled? */
+ __asm__ volatile("mrc p15, 0, %0, c9, c12, 2;" : "=r" (PMCNTENCLR));
+
+ enabled = (PMCNTENCLR & mask);
+ if (enabled) {
+ /* Individual counter enable clear (PMCNTENCLR) */
+ __asm__ volatile("mcr p15, 0, %0, c9, c12, 2;" : : "r" (mask));
+
+ /* Counter interrupt disable (PMINTENCLR) */
+ __asm__ volatile("mcr p15, 0, %0, c9, c14, 2;" : : "r" (mask));
+
+ kpc_enabled_counters--;
+
+ /* Last enabled counter? Clear the master enable bit in PMCR */
+ if (kpc_enabled_counters == 0) {
+ uint32_t PMCR = 0;
+ __asm__ volatile("mcr p15, 0, %0, c9, c12, 0;" : : "r" (PMCR));
+ }
+ }
+
+ return enabled;
+}
+
+static uint64_t
+read_counter(uint32_t counter)
+{
+ uint32_t low = 0;
+
+ switch (counter) {
+ case 0:
+ /* Fixed counter */
+ __asm__ volatile("mrc p15, 0, %0, c9, c13, 0;" : "=r" (low));
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ /* Configurable. Set PMSELR... */
+ __asm__ volatile("mcr p15, 0, %0, c9, c12, 5;" : : "r" (counter - 1));
+ /* ...then read PMXEVCNTR */
+ __asm__ volatile("mrc p15, 0, %0, c9, c13, 2;" : "=r" (low));
+ break;
+ default:
+ /* ??? */
+ break;
+ }
+
+ return (uint64_t)low;
+}
+
+static void
+write_counter(uint32_t counter, uint64_t value)
+{
+ uint32_t low = value & 0xFFFFFFFF;
+
+ switch (counter) {
+ case 0:
+ /* Fixed counter */
+ __asm__ volatile("mcr p15, 0, %0, c9, c13, 0;" : : "r" (low));
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ /* Configurable. Set PMSELR... */
+ __asm__ volatile("mcr p15, 0, %0, c9, c12, 5;" : : "r" (counter - 1));
+ /* ...then write PMXEVCNTR */
+ __asm__ volatile("mcr p15, 0, %0, c9, c13, 2;" : : "r" (low));
+ break;
+ default:
+ /* ??? */
+ break;
+ }
+}
+
+static uint64_t
+kpc_reload_counter(int ctr)
+{
+ uint64_t old = read_counter(ctr);
+ write_counter(ctr, FIXED_RELOAD(ctr));
+ return old;
+}
+
+static void
+set_running_fixed(boolean_t on)
+{
+ int i;
+ boolean_t enabled;
+ int n = KPC_ARM_FIXED_COUNT;
+
+ enabled = ml_set_interrupts_enabled(FALSE);
+
+ for( i = 0; i < n; i++ ) {
+ if (on) {
+ enable_counter(i);
+ } else {
+ disable_counter(i);
+ }
+ }
+
+ ml_set_interrupts_enabled(enabled);
+}
+
+static void
+set_running_configurable(uint64_t target_mask, uint64_t state_mask)
+{
+ uint32_t cfg_count = kpc_configurable_count(), offset = kpc_fixed_count();
+ boolean_t enabled;
+
+ enabled = ml_set_interrupts_enabled(FALSE);
+
+ for (uint32_t i = 0; i < cfg_count; ++i) {
+ if (((1ULL << i) & target_mask) == 0)
+ continue;
+ assert(kpc_controls_counter(offset + i));
+
+ if ((1ULL << i) & state_mask) {
+ enable_counter(offset + i);
+ } else {
+ disable_counter(offset + i);
+ }
+ }
+
+ ml_set_interrupts_enabled(enabled);
+}
+
+void kpc_pmi_handler(cpu_id_t source);
+void
+kpc_pmi_handler(cpu_id_t source)
+{
+ uint64_t extra;
+ int ctr;
+ int enabled;
+
+ enabled = ml_set_interrupts_enabled(FALSE);
+
+ /* The pmi must be delivered to the CPU that generated it */
+ if (source != getCpuDatap()->interrupt_nub) {
+ panic("pmi from IOCPU %p delivered to IOCPU %p", source, getCpuDatap()->interrupt_nub);
+ }
+
+ for (ctr = 0;
+ ctr < (KPC_ARM_FIXED_COUNT + KPC_ARM_CONFIGURABLE_COUNT);
+ ctr++)
+ {
+ uint32_t PMOVSR;
+ uint32_t mask;
+
+ /* check the counter for overflow */
+ if (ctr == 0) {
+ mask = 1 << 31;
+ } else {
+ mask = 1 << (ctr - 1);
+ }
+
+ /* read PMOVSR */
+ __asm__ volatile("mrc p15, 0, %0, c9, c12, 3;" : "=r" (PMOVSR));
+
+ if (PMOVSR & mask) {
+ extra = kpc_reload_counter(ctr);
+
+ FIXED_SHADOW(ctr)
+ += (kpc_fixed_max() - FIXED_RELOAD(ctr) + 1 /* wrap */) + extra;
+
+ if (FIXED_ACTIONID(ctr))
+ kpc_sample_kperf(FIXED_ACTIONID(ctr));
+
+ /* clear PMOVSR bit */
+ __asm__ volatile("mcr p15, 0, %0, c9, c12, 3;" : : "r" (mask));
+ }
+ }
+
+ ml_set_interrupts_enabled(enabled);
+}
+
+static void
+kpc_set_running_xcall( void *vstate )
+{
+ struct kpc_running_remote *mp_config = (struct kpc_running_remote*) vstate;
+ assert(mp_config);
+
+ if (kpc_controls_fixed_counters())
+ set_running_fixed(mp_config->classes & KPC_CLASS_FIXED_MASK);
+
+ set_running_configurable(mp_config->cfg_target_mask,
+ mp_config->cfg_state_mask);
+
+ if (hw_atomic_sub(&kpc_xcall_sync, 1) == 0) {
+ thread_wakeup((event_t) &kpc_xcall_sync);
+ }
+}
+
+static uint64_t
+get_counter_config(uint32_t counter)
+{
+ uint32_t config = 0;
+
+ switch (counter) {
+ case 0:
+ /* Fixed counter accessed via top bit... */
+ counter = 31;
+ /* Write PMSELR.SEL */
+ __asm__ volatile("mcr p15, 0, %0, c9, c12, 5;" : : "r" (counter));
+ /* Read PMXEVTYPER */
+ __asm__ volatile("mcr p15, 0, %0, c9, c13, 1;" : "=r" (config));
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ /* Offset */
+ counter -= 1;
+ /* Write PMSELR.SEL to select the configurable counter */
+ __asm__ volatile("mcr p15, 0, %0, c9, c12, 5;" : : "r" (counter));
+ /* Read PMXEVTYPER to get the config */
+ __asm__ volatile("mrc p15, 0, %0, c9, c13, 1;" : "=r" (config));
+ break;
+ default:
+ break;
+ }
+
+ return config;
+}
+
+static void
+set_counter_config(uint32_t counter, uint64_t config)
+{
+ switch (counter) {
+ case 0:
+ /* Write PMSELR.SEL */
+ __asm__ volatile("mcr p15, 0, %0, c9, c12, 5;" : : "r" (31));
+ /* Write PMXEVTYPER */
+ __asm__ volatile("mcr p15, 0, %0, c9, c13, 1;" : : "r" (config & 0xFFFFFFFF));
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ /* Write PMSELR.SEL */
+ __asm__ volatile("mcr p15, 0, %0, c9, c12, 5;" : : "r" (counter - 1));
+ /* Write PMXEVTYPER */
+ __asm__ volatile("mcr p15, 0, %0, c9, c13, 1;" : : "r" (config & 0xFFFFFFFF));
+ break;
+ default:
+ break;
+ }
+}
+
+/* Common */
+
+void
+kpc_arch_init(void)
+{
+ uint32_t PMCR;
+ uint32_t event_counters;
+
+ /* read PMOVSR and determine the number of event counters */
+ __asm__ volatile("mrc p15, 0, %0, c9, c12, 0;" : "=r" (PMCR));
+ event_counters = (PMCR >> 11) & 0x1F;
+
+ assert(event_counters >= KPC_ARM_CONFIGURABLE_COUNT);
+}
+
+uint32_t
+kpc_get_classes(void)
+{
+ return KPC_CLASS_FIXED_MASK | KPC_CLASS_CONFIGURABLE_MASK;
+}
+
+uint32_t
+kpc_fixed_count(void)
+{
+ return KPC_ARM_FIXED_COUNT;
+}
+
+uint32_t
+kpc_configurable_count(void)
+{
+ return KPC_ARM_CONFIGURABLE_COUNT;
+}
+
+uint32_t
+kpc_fixed_config_count(void)
+{
+ return KPC_ARM_FIXED_COUNT;
+}
+
+uint32_t
+kpc_configurable_config_count(uint64_t pmc_mask)
+{
+ assert(kpc_popcount(pmc_mask) <= kpc_configurable_count());
+ return kpc_popcount(pmc_mask);
+}
+
+int
+kpc_get_fixed_config(kpc_config_t *configv)
+{
+ configv[0] = get_counter_config(0);
+ return 0;
+}
+
+uint64_t
+kpc_fixed_max(void)
+{
+ return (1ULL << KPC_ARM_COUNTER_WIDTH) - 1;
+}
+
+uint64_t
+kpc_configurable_max(void)
+{
+ return (1ULL << KPC_ARM_COUNTER_WIDTH) - 1;
+}
+
+int
+kpc_get_configurable_counters(uint64_t *counterv, uint64_t pmc_mask)
+{
+ uint32_t cfg_count = kpc_configurable_count(), offset = kpc_fixed_count();
+
+ assert(counterv);
+
+ for (uint32_t i = 0; i < cfg_count; ++i) {
+ uint32_t PMOVSR;
+ uint32_t mask;
+ uint64_t ctr;
+
+ if (((1ULL << i) & pmc_mask) == 0)
+ continue;
+ ctr = read_counter(i + offset);
+
+ /* check the counter for overflow */
+ mask = 1 << i;
+
+ /* read PMOVSR */
+ __asm__ volatile("mrc p15, 0, %0, c9, c12, 3;" : "=r" (PMOVSR));
+
+ if (PMOVSR & mask) {
+ ctr = CONFIGURABLE_SHADOW(i) +
+ (kpc_configurable_max() - CONFIGURABLE_RELOAD(i) + 1 /* Wrap */) +
+ ctr;
+ } else {
+ ctr = CONFIGURABLE_SHADOW(i) +
+ (ctr - CONFIGURABLE_RELOAD(i));
+ }
+
+ *counterv++ = ctr;
+ }
+
+ return 0;
+}
+
+int
+kpc_get_fixed_counters(uint64_t *counterv)
+{
+ uint32_t PMOVSR;
+ uint32_t mask;
+ uint64_t ctr;
+
+ /* check the counter for overflow */
+ mask = 1 << 31;
+
+ /* read PMOVSR */
+ __asm__ volatile("mrc p15, 0, %0, c9, c12, 3;" : "=r" (PMOVSR));
+
+ ctr = read_counter(0);
+
+ if (PMOVSR & mask) {
+ ctr = FIXED_SHADOW(0) +
+ (kpc_fixed_max() - FIXED_RELOAD(0) + 1 /* Wrap */) +
+ (ctr & 0xFFFFFFFF);
+ } else {
+ ctr = FIXED_SHADOW(0) +
+ (ctr - FIXED_RELOAD(0));
+ }
+
+ counterv[0] = ctr;
+
+ return 0;
+}
+boolean_t
+kpc_is_running_fixed(void)
+{
+ return (kpc_running_classes & KPC_CLASS_FIXED_MASK) == KPC_CLASS_FIXED_MASK;
+}
+
+boolean_t
+kpc_is_running_configurable(uint64_t pmc_mask)
+{
+ assert(kpc_popcount(pmc_mask) <= kpc_configurable_count());
+ return ((kpc_running_classes & KPC_CLASS_CONFIGURABLE_MASK) == KPC_CLASS_CONFIGURABLE_MASK) &&
+ ((kpc_running_cfg_pmc_mask & pmc_mask) == pmc_mask);
+}
+
+int
+kpc_set_running_arch(struct kpc_running_remote *mp_config)
+{
+ unsigned int cpu;
+
+ assert(mp_config);
+
+ if (first_time) {
+ kprintf( "kpc: setting PMI handler\n" );
+ PE_cpu_perfmon_interrupt_install_handler(kpc_pmi_handler);
+ for (cpu = 0; cpu < real_ncpus; cpu++)
+ PE_cpu_perfmon_interrupt_enable(cpu_datap(cpu)->cpu_id,
+ TRUE);
+ first_time = 0;
+ }
+
+ /* dispatch to all CPUs */
+ cpu_broadcast_xcall(&kpc_xcall_sync, TRUE, kpc_set_running_xcall,
+ mp_config);
+
+ kpc_running_cfg_pmc_mask = mp_config->cfg_state_mask;
+ kpc_running_classes = mp_config->classes;
+ kpc_configured = 1;
+
+ return 0;
+}
+
+static void
+save_regs(void)
+{
+ int i;
+ int cpuid = current_processor()->cpu_id;
+ uint32_t PMCR = 0;
+
+ __asm__ volatile("dmb ish");
+
+ /* Clear master enable */
+ __asm__ volatile("mcr p15, 0, %0, c9, c12, 0;" : : "r" (PMCR));
+
+ /* Save individual enable state */
+ __asm__ volatile("mrc p15, 0, %0, c9, c12, 1;" : "=r" (saved_PMCNTENSET[cpuid]));
+
+ /* Save PMOVSR */
+ __asm__ volatile("mrc p15, 0, %0, c9, c12, 3;" : "=r" (saved_PMOVSR[cpuid]));
+
+ /* Select fixed counter with PMSELR.SEL */
+ __asm__ volatile("mcr p15, 0, %0, c9, c12, 5;" : : "r" (31));
+ /* Read PMXEVTYPER */
+ __asm__ volatile("mrc p15, 0, %0, c9, c13, 1;" : "=r" (saved_PMXEVTYPER[cpuid][0]));
+
+ /* Save configurable event selections */
+ for (i = 0; i < 4; i++) {
+ /* Select counter with PMSELR.SEL */
+ __asm__ volatile("mcr p15, 0, %0, c9, c12, 5;" : : "r" (i));
+ /* Read PMXEVTYPER */
+ __asm__ volatile("mrc p15, 0, %0, c9, c13, 1;" : "=r" (saved_PMXEVTYPER[cpuid][i + 1]));
+ }
+
+ /* Finally, save count for each counter */
+ for (i=0; i < 5; i++) {
+ saved_counter[cpuid][i] = read_counter(i);
+ }
+}
+
+static void
+restore_regs(void)
+{
+ int i;
+ int cpuid = current_processor()->cpu_id;
+ uint64_t extra;
+ uint32_t PMCR = 1;
+
+ /* Restore counter values */
+ for (i = 0; i < 5; i++) {
+ /* did we overflow? if so handle it now since we won't get a pmi */
+ uint32_t mask;
+
+ /* check the counter for overflow */
+ if (i == 0) {
+ mask = 1 << 31;
+ } else {
+ mask = 1 << (i - 1);
+ }
+
+ if (saved_PMOVSR[cpuid] & mask) {
+ extra = kpc_reload_counter(i);
+
+ /*
+ * CONFIGURABLE_* directly follows FIXED, so we can simply
+ * increment the index here. Although it's ugly.
+ */
+ FIXED_SHADOW(i)
+ += (kpc_fixed_max() - FIXED_RELOAD(i) + 1 /* Wrap */) + extra;
+
+ if (FIXED_ACTIONID(i))
+ kpc_sample_kperf(FIXED_ACTIONID(i));
+ } else {
+ write_counter(i, saved_counter[cpuid][i]);
+ }
+ }
+
+ /* Restore configuration - first, the fixed... */
+ __asm__ volatile("mcr p15, 0, %0, c9, c12, 5;" : : "r" (31));
+ /* Write PMXEVTYPER */
+ __asm__ volatile("mcr p15, 0, %0, c9, c13, 1;" : : "r" (saved_PMXEVTYPER[cpuid][0]));
+
+ /* ...then the configurable */
+ for (i = 0; i < 4; i++) {
+ /* Select counter with PMSELR.SEL */
+ __asm__ volatile("mcr p15, 0, %0, c9, c12, 5;" : : "r" (i));
+ /* Write PMXEVTYPER */
+ __asm__ volatile("mcr p15, 0, %0, c9, c13, 1;" : : "r" (saved_PMXEVTYPER[cpuid][i + 1]));
+ }
+
+ /* Restore enable state */
+ __asm__ volatile("mcr p15, 0, %0, c9, c12, 1;" : : "r" (saved_PMCNTENSET[cpuid]));
+
+ /* Counter master re-enable */
+ __asm__ volatile("mcr p15, 0, %0, c9, c12, 0;" : : "r" (PMCR));
+}
+
+static void
+kpc_set_reload_xcall(void *vmp_config)
+{
+ struct kpc_config_remote *mp_config = vmp_config;
+ uint32_t classes = 0, count = 0, offset = kpc_fixed_count();
+ uint64_t *new_period = NULL, max = kpc_configurable_max();
+ boolean_t enabled;
+
+ assert(mp_config);
+ assert(mp_config->configv);
+ classes = mp_config->classes;
+ new_period = mp_config->configv;
+
+ enabled = ml_set_interrupts_enabled(FALSE);
+
+ if ((classes & KPC_CLASS_FIXED_MASK) && kpc_controls_fixed_counters()) {
+ /* update shadow counters */
+ kpc_get_fixed_counters(&FIXED_SHADOW(0));
+
+ /* set the new period */
+ count = kpc_fixed_count();
+ for (uint32_t i = 0; i < count; ++i) {
+ if (*new_period == 0)
+ *new_period = kpc_fixed_max();
+ FIXED_RELOAD(i) = max - *new_period;
+ /* reload the counter if possible */
+ kpc_reload_counter(i);
+ /* next period value */
+ new_period++;
+ }
+ }
+
+ if (classes & KPC_CLASS_CONFIGURABLE_MASK) {
+ /*
+ * Update _all_ shadow counters, this cannot be done for only
+ * selected PMCs. Otherwise, we would corrupt the configurable
+ * shadow buffer since the PMCs are muxed according to the pmc
+ * mask.
+ */
+ uint64_t all_cfg_mask = (1ULL << kpc_configurable_count()) - 1;
+ kpc_get_configurable_counters(&CONFIGURABLE_SHADOW(0), all_cfg_mask);
+
+ /* set the new period */
+ count = kpc_configurable_count();
+ for (uint32_t i = 0; i < count; ++i) {
+ /* ignore the counter */
+ if (((1ULL << i) & mp_config->pmc_mask) == 0)
+ continue;
+ if (*new_period == 0)
+ *new_period = kpc_configurable_max();
+ CONFIGURABLE_RELOAD(i) = max - *new_period;
+ /* reload the counter */
+ kpc_reload_counter(offset + i);
+ /* next period value */
+ new_period++;
+ }
+ }
+
+ ml_set_interrupts_enabled(enabled);
+
+ if (hw_atomic_sub(&kpc_reload_sync, 1) == 0)
+ thread_wakeup((event_t) &kpc_reload_sync);
+}
+
+
+int
+kpc_set_period_arch(struct kpc_config_remote *mp_config)
+{
+ /* dispatch to all CPUs */
+ cpu_broadcast_xcall(&kpc_reload_sync, TRUE, kpc_set_reload_xcall, mp_config);
+
+ kpc_configured = 1;
+
+ return 0;
+}
+
+int
+kpc_get_configurable_config(kpc_config_t *configv, uint64_t pmc_mask)
+{
+ uint32_t cfg_count = kpc_configurable_count(), offset = kpc_fixed_count();
+
+ assert(configv);
+
+ for (uint32_t i = 0; i < cfg_count; ++i)
+ if ((1ULL << i) & pmc_mask)
+ *configv++ = get_counter_config(i + offset);
+
+ return 0;
+}
+
+static int
+kpc_set_configurable_config(kpc_config_t *configv, uint64_t pmc_mask)
+{
+ uint32_t cfg_count = kpc_configurable_count(), offset = kpc_fixed_count();
+ boolean_t enabled;
+
+ assert(configv);
+
+ enabled = ml_set_interrupts_enabled(FALSE);
+
+ for (uint32_t i = 0; i < cfg_count; ++i) {
+ if (((1ULL << i) & pmc_mask) == 0)
+ continue;
+ assert(kpc_controls_counter(i + offset));
+
+ set_counter_config(i + offset, *configv++);
+ }
+
+ ml_set_interrupts_enabled(enabled);
+
+ return 0;
+}
+
+static uint32_t kpc_config_sync;
+static void
+kpc_set_config_xcall(void *vmp_config)
+{
+ struct kpc_config_remote *mp_config = vmp_config;
+ kpc_config_t *new_config = NULL;
+ uint32_t classes = 0ULL;
+
+ assert(mp_config);
+ assert(mp_config->configv);
+ classes = mp_config->classes;
+ new_config = mp_config->configv;
+
+ if (classes & KPC_CLASS_CONFIGURABLE_MASK) {
+ kpc_set_configurable_config(new_config, mp_config->pmc_mask);
+ new_config += kpc_popcount(mp_config->pmc_mask);
+ }
+
+ if (hw_atomic_sub(&kpc_config_sync, 1) == 0)
+ thread_wakeup((event_t) &kpc_config_sync);
+}
+
+int
+kpc_set_config_arch(struct kpc_config_remote *mp_config)
+{
+ /* dispatch to all CPUs */
+ cpu_broadcast_xcall(&kpc_config_sync, TRUE, kpc_set_config_xcall, mp_config);
+
+ kpc_configured = 1;
+
+ return 0;
+}
+
+void
+kpc_idle(void)
+{
+ if (kpc_configured)
+ save_regs();
+}
+
+void
+kpc_idle_exit(void)
+{
+ if (kpc_configured)
+ restore_regs();
+}
+
+static uint32_t kpc_xread_sync;
+static void
+kpc_get_curcpu_counters_xcall(void *args)
+{
+ struct kpc_get_counters_remote *handler = args;
+ int offset=0, r=0;
+
+ assert(handler);
+ assert(handler->buf);
+
+ offset = cpu_number() * handler->buf_stride;
+ r = kpc_get_curcpu_counters(handler->classes, NULL, &handler->buf[offset]);
+
+ /* number of counters added by this CPU, needs to be atomic */
+ hw_atomic_add(&(handler->nb_counters), r);
+
+ if (hw_atomic_sub(&kpc_xread_sync, 1) == 0)
+ thread_wakeup((event_t) &kpc_xread_sync);
+}
+
+int
+kpc_get_all_cpus_counters(uint32_t classes, int *curcpu, uint64_t *buf)
+{
+ int enabled = 0;
+
+ struct kpc_get_counters_remote hdl = {
+ .classes = classes, .nb_counters = 0,
+ .buf_stride = kpc_get_counter_count(classes),
+ .buf = buf
+ };
+
+ assert(buf);
+
+ enabled = ml_set_interrupts_enabled(FALSE);
+
+ if (curcpu)
+ *curcpu = current_processor()->cpu_id;
+ cpu_broadcast_xcall(&kpc_xread_sync, TRUE, kpc_get_curcpu_counters_xcall, &hdl);
+
+ ml_set_interrupts_enabled(enabled);
+
+ return hdl.nb_counters;
+}
+
+int
+kpc_get_pmu_version(void)
+{
+ return KPC_PMU_ARM_V2;
+}
+
+int
+kpc_set_sw_inc( uint32_t mask )
+{
+ /* Only works with the configurable counters set to count the increment event (0x0) */
+
+ /* Write to PMSWINC */
+ __asm__ volatile("mcr p15, 0, %0, c9, c12, 4;" : : "r" (mask));
+
+ return 0;
+}
+
+#else /* !ARMA7 */
+
+/* no kpc */
+
+void
+kpc_arch_init(void)
+{
+ /* No-op */
+}
+
+uint32_t
+kpc_get_classes(void)
+{
+ return 0;
+}
+
+uint32_t
+kpc_fixed_count(void)
+{
+ return 0;
+}
+
+uint32_t
+kpc_configurable_count(void)
+{
+ return 0;
+}
+
+uint32_t
+kpc_fixed_config_count(void)
+{
+ return 0;
+}
+
+uint32_t
+kpc_configurable_config_count(uint64_t pmc_mask __unused)
+{
+ return 0;
+}
+
+int
+kpc_get_fixed_config(kpc_config_t *configv __unused)
+{
+ return 0;
+}
+
+uint64_t
+kpc_fixed_max(void)
+{
+ return 0;
+}
+
+uint64_t
+kpc_configurable_max(void)
+{
+ return 0;
+}
+
+int
+kpc_get_configurable_config(kpc_config_t *configv __unused, uint64_t pmc_mask __unused)
+{
+ return ENOTSUP;
+}
+
+int
+kpc_get_configurable_counters(uint64_t *counterv __unused, uint64_t pmc_mask __unused)
+{
+ return ENOTSUP;
+}
+
+int
+kpc_get_fixed_counters(uint64_t *counterv __unused)
+{
+ return 0;
+}
+
+boolean_t
+kpc_is_running_fixed(void)
+{
+ return FALSE;
+}
+
+boolean_t
+kpc_is_running_configurable(uint64_t pmc_mask __unused)
+{
+ return FALSE;
+}
+
+int
+kpc_set_running_arch(struct kpc_running_remote *mp_config __unused)
+{
+ return ENOTSUP;
+}
+
+int
+kpc_set_period_arch(struct kpc_config_remote *mp_config __unused)
+{
+ return ENOTSUP;
+}
+
+int
+kpc_set_config_arch(struct kpc_config_remote *mp_config __unused)
+{
+ return ENOTSUP;
+}
+
+void
+kpc_idle(void)
+{
+ // do nothing
+}
+
+void
+kpc_idle_exit(void)
+{
+ // do nothing
+}
+
+int
+kpc_get_all_cpus_counters(uint32_t classes, int *curcpu, uint64_t *buf)
+{
+#pragma unused(classes)
+#pragma unused(curcpu)
+#pragma unused(buf)
+
+ return 0;
+}
+
+int
+kpc_set_sw_inc( uint32_t mask __unused )
+{
+ return ENOTSUP;
+}
+
+int
+kpc_get_pmu_version(void)
+{
+ return KPC_PMU_ERROR;
+}
+
+#endif
+
+/*
+ * RAWPMU isn't implemented for any of the 32-bit ARMs.
+ */
+
+uint32_t
+kpc_rawpmu_config_count(void)
+{
+ return 0;
+}
+
+int
+kpc_get_rawpmu_config(__unused kpc_config_t *configv)
+{
+ return 0;
+}