Added perfmon interrupt handling to allow overflow based profiling
authorDavide Libenzi <dlibenzi@google.com>
Mon, 16 Nov 2015 15:13:13 +0000 (07:13 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 16 Dec 2015 21:27:06 +0000 (16:27 -0500)
Added perfmon interrupt handling to allow overflow based profiling.
This allow to set the INT bit in the Intel perfmon counter
configuration, and, by setting an initial counter value close to
the overflow, to sample code which triggers overflowing the most.

Signed-off-by: Davide Libenzi <dlibenzi@google.com>
[checkpatch complaints]
Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/x86/perfmon.c
kern/arch/x86/perfmon.h
kern/arch/x86/ros/perfmon.h [new file with mode: 0644]
kern/arch/x86/trap.c

index 4fe6cf8..f444ea6 100644 (file)
-#include <arch/perfmon.h> 
+/* Copyright (c) 2015 Google Inc
+ * Davide Libenzi <dlibenzi@google.com>
+ * See LICENSE for details.
+ */
 
+#include <sys/types.h>
+#include <arch/ros/msr-index.h>
+#include <arch/x86.h>
+#include <arch/msr.h>
+#include <arch/uaccess.h>
+#include <ros/errno.h>
+#include <assert.h>
+#include <trap.h>
+#include <smp.h>
+#include <atomic.h>
+#include <core_set.h>
+#include <kref.h>
+#include <percpu.h>
+#include <kmalloc.h>
+#include <err.h>
+#include <string.h>
+#include <profiler.h>
+#include <arch/perfmon.h>
 
-static void setup_counter(int index, uint8_t mask, uint8_t event) {
-       //just a magic number for now to indicate the most common case
-       uint32_t os_user_enabled = 0x43;
-       write_msr(IA32_PERFEVTSEL_BASE + index, (os_user_enabled<<16 | mask<<8 | event));
+#define FIXCNTR_NBITS 4
+#define FIXCNTR_MASK (((uint64_t) 1 << FIXCNTR_NBITS) - 1)
+
+struct perfmon_cpu_context {
+       spinlock_t lock;
+       struct perfmon_event counters[MAX_VAR_COUNTERS];
+       struct perfmon_event fixed_counters[MAX_FIX_COUNTERS];
+};
+
+struct perfmon_status_env {
+       struct perfmon_alloc *pa;
+       struct perfmon_status *pef;
+};
+
+static struct perfmon_cpu_caps cpu_caps;
+static DEFINE_PERCPU(struct perfmon_cpu_context, counters_env);
+DEFINE_PERCPU_INIT(perfmon_counters_env_init);
+
+static void perfmon_counters_env_init(void)
+{
+       for (int i = 0; i < num_cores; i++) {
+               struct perfmon_cpu_context *cctx = _PERCPU_VARPTR(counters_env, i);
+
+               spinlock_init_irqsave(&cctx->lock);
+       }
+}
+
+static void perfmon_read_cpu_caps(struct perfmon_cpu_caps *pcc)
+{
+       uint32_t a, b, c, d;
+
+       cpuid(0x0a, 0, &a, &b, &c, &d);
+
+       ZERO_DATA(*pcc);
+       pcc->perfmon_version = a & 0xff;
+       pcc->proc_arch_events = a >> 24;
+       pcc->bits_x_counter = (a >> 16) & 0xff;
+       pcc->counters_x_proc = (a >> 8) & 0xff;
+       pcc->bits_x_fix_counter = (d >> 5) & 0xff;
+       pcc->fix_counters_x_proc = d & 0x1f;
+}
+
+static void perfmon_enable_event(int event, bool enable)
+{
+       uint64_t gctrl = read_msr(MSR_CORE_PERF_GLOBAL_CTRL);
+
+       if (enable)
+               write_msr(MSR_CORE_PERF_GLOBAL_CTRL, gctrl | (1 << event));
+       else
+               write_msr(MSR_CORE_PERF_GLOBAL_CTRL, gctrl & ~(1 << event));
+}
+
+static void perfmon_enable_fix_event(int event, bool enable)
+{
+       uint64_t gctrl = read_msr(MSR_CORE_PERF_GLOBAL_CTRL);
+
+       if (enable)
+               write_msr(MSR_CORE_PERF_GLOBAL_CTRL,
+                                 gctrl | ((uint64_t) 1 << (32 + event)));
+       else
+               write_msr(MSR_CORE_PERF_GLOBAL_CTRL,
+                                 gctrl & ~((uint64_t) 1 << (32 + event)));
+}
+
+static bool perfmon_event_available(uint32_t event)
+{
+       return read_msr(MSR_ARCH_PERFMON_EVENTSEL0 + event) == 0;
+}
+
+static uint64_t perfmon_get_fixevent_mask(const struct perfmon_event *pev,
+                                                                                 int eventno, uint64_t base)
+{
+       uint64_t m = 0;
+
+       if (pev->u.b.inten)
+               m |= 1 << 3;
+       if (pev->u.b.os)
+               m |= (1 << 0);
+       if (pev->u.b.usr)
+               m |= (1 << 1);
+
+       m <<= eventno * FIXCNTR_NBITS;
+       m |= base & ~(FIXCNTR_MASK << (eventno * FIXCNTR_NBITS));
+
+       return m;
+}
+
+static void perfmon_do_cores_alloc(void *opaque)
+{
+       struct perfmon_alloc *pa = (struct perfmon_alloc *) opaque;
+       struct perfmon_cpu_context *cctx = PERCPU_VARPTR(counters_env);
+       int i;
+
+       spin_lock_irqsave(&cctx->lock);
+       if (perfmon_is_fixed_event(&pa->ev)) {
+               uint64_t fxctrl_value = read_msr(MSR_CORE_PERF_FIXED_CTR_CTRL), tmp;
+
+               i = pa->ev.u.b.event;
+               if (i >= (int) cpu_caps.fix_counters_x_proc) {
+                       i = -EINVAL;
+               } else if (fxctrl_value & (FIXCNTR_MASK << i)) {
+                       i = -EBUSY;
+               } else {
+                       cctx->fixed_counters[i] = pa->ev;
+                       cctx->fixed_counters[i].u.b.en = 1;
+
+                       tmp = perfmon_get_fixevent_mask(&pa->ev, i, fxctrl_value);
+
+                       perfmon_enable_fix_event(i, TRUE);
+
+                       write_msr(MSR_CORE_PERF_FIXED_CTR0 + i,
+                                         -(int64_t) pa->ev.trigger_count);
+                       write_msr(MSR_CORE_PERF_FIXED_CTR_CTRL, tmp);
+               }
+       } else {
+               for (i = 0; i < (int) cpu_caps.counters_x_proc; i++) {
+                       if (cctx->counters[i].u.v == 0) {
+                               if (!perfmon_event_available(i))
+                                       warn_once("Counter %d is free but not available", i);
+                               else
+                                       break;
+                       }
+               }
+               if (i < (int) cpu_caps.counters_x_proc) {
+                       cctx->counters[i] = pa->ev;
+                       cctx->counters[i].u.b.en = 1;
+
+                       perfmon_enable_event(i, TRUE);
+
+                       write_msr(MSR_IA32_PERFCTR0 + i, -(int64_t) pa->ev.trigger_count);
+                       write_msr(MSR_ARCH_PERFMON_EVENTSEL0 + i,
+                                         cctx->counters[i].u.v);
+               } else {
+                       i = -ENOSPC;
+               }
+       }
+       spin_unlock_irqsave(&cctx->lock);
+
+       pa->cores_counters[core_id()] = (counter_t) i;
+}
+
+static void perfmon_do_cores_free(void *opaque)
+{
+       struct perfmon_alloc *pa = (struct perfmon_alloc *) opaque;
+       struct perfmon_cpu_context *cctx = PERCPU_VARPTR(counters_env);
+       int err = 0, coreno = core_id();
+       counter_t ccno = pa->cores_counters[coreno];
+
+       spin_lock_irqsave(&cctx->lock);
+       if (perfmon_is_fixed_event(&pa->ev)) {
+               unsigned int ccbitsh = ccno * FIXCNTR_NBITS;
+               uint64_t fxctrl_value = read_msr(MSR_CORE_PERF_FIXED_CTR_CTRL);
+
+               if ((ccno >= cpu_caps.fix_counters_x_proc) ||
+                       !(fxctrl_value & (FIXCNTR_MASK << ccbitsh))) {
+                       err = -ENOENT;
+               } else {
+                       perfmon_init_event(&cctx->fixed_counters[ccno]);
+
+                       perfmon_enable_fix_event((int) ccno, FALSE);
+
+                       write_msr(MSR_CORE_PERF_FIXED_CTR_CTRL,
+                                         fxctrl_value & ~(FIXCNTR_MASK << ccbitsh));
+                       write_msr(MSR_CORE_PERF_FIXED_CTR0 + ccno, 0);
+               }
+       } else {
+               if (ccno < (int) cpu_caps.counters_x_proc) {
+                       perfmon_init_event(&cctx->counters[ccno]);
+
+                       perfmon_enable_event((int) ccno, FALSE);
+
+                       write_msr(MSR_ARCH_PERFMON_EVENTSEL0 + ccno, 0);
+                       write_msr(MSR_IA32_PERFCTR0 + ccno, 0);
+               } else {
+                       err = -ENOENT;
+               }
+       }
+       spin_unlock_irqsave(&cctx->lock);
+
+       pa->cores_counters[coreno] = (counter_t) err;
+}
+
+static void perfmon_do_cores_status(void *opaque)
+{
+       struct perfmon_status_env *env = (struct perfmon_status_env *) opaque;
+       struct perfmon_cpu_context *cctx = PERCPU_VARPTR(counters_env);
+       int coreno = core_id();
+       counter_t ccno = env->pa->cores_counters[coreno];
+
+       spin_lock_irqsave(&cctx->lock);
+       if (perfmon_is_fixed_event(&env->pa->ev))
+               env->pef->cores_values[coreno] =
+                       read_msr(MSR_CORE_PERF_FIXED_CTR0 + ccno);
+       else
+               env->pef->cores_values[coreno] =
+                       read_msr(MSR_IA32_PERFCTR0 + ccno);
+       spin_unlock_irqsave(&cctx->lock);
+}
+
+static void perfmon_setup_alloc_core_set(const struct perfmon_alloc *pa,
+                                                                                struct core_set *cset)
+{
+       int i;
+
+       core_set_init(cset);
+       for (i = 0; i < num_cores; i++) {
+               if (pa->cores_counters[i] >= 0)
+                       core_set_setcpu(cset, i);
+       }
+}
+
+static void perfmon_cleanup_cores_alloc(struct perfmon_alloc *pa)
+{
+       struct core_set cset;
+
+       perfmon_setup_alloc_core_set(pa, &cset);
+       smp_do_in_cores(&cset, perfmon_do_cores_free, pa);
+}
+
+static void perfmon_free_alloc(struct perfmon_alloc *pa)
+{
+       kfree(pa);
+}
+
+static void perfmon_destroy_alloc(struct perfmon_alloc *pa)
+{
+       if (pa) {
+               perfmon_cleanup_cores_alloc(pa);
+               perfmon_free_alloc(pa);
+       }
+}
+
+static void perfmon_release_alloc(struct kref *kref)
+{
+       struct perfmon_alloc *pa = container_of(kref, struct perfmon_alloc, ref);
+
+       perfmon_destroy_alloc(pa);
+}
+
+static struct perfmon_alloc *perfmon_create_alloc(const struct perfmon_event *pev)
+{
+       int i;
+       struct perfmon_alloc *pa = kzmalloc(sizeof(struct perfmon_alloc) +
+                                                                               num_cores * sizeof(counter_t),
+                                                                               KMALLOC_WAIT);
+
+       kref_init(&pa->ref, perfmon_release_alloc, 1);
+       pa->ev = *pev;
+       for (i = 0; i < num_cores; i++)
+               pa->cores_counters[i] = INVALID_COUNTER;
+
+       return pa;
+}
+
+static struct perfmon_status *perfmon_alloc_status(void)
+{
+       struct perfmon_status *pef = kzmalloc(sizeof(struct perfmon_status) +
+                                                                                 num_cores * sizeof(uint64_t),
+                                                                                 KMALLOC_WAIT);
+
+       return pef;
 }
 
-void perfmon_init() {
-#if 0
-       // Examples of how to set up for cache misses:
-       setup_counter(0, LLCACHE_REF_MASK, LLCACHE_EVENT);
-       setup_counter(1, LLCACHE_MISS_MASK, LLCACHE_EVENT);
-#endif
+void perfmon_init(void)
+{
+       int i;
 
        /* Enable user level access to the performance counters */
        lcr4(rcr4() | CR4_PCE);
+
+       /* This will be called from every core, no need to execute more than once.
+        */
+       if (cpu_caps.perfmon_version == 0)
+               perfmon_read_cpu_caps(&cpu_caps);
+
+       /* Reset all the counters and selectors to zero.
+        */
+       write_msr(MSR_CORE_PERF_GLOBAL_CTRL, 0);
+       for (i = 0; i < (int) cpu_caps.counters_x_proc; i++) {
+               write_msr(MSR_ARCH_PERFMON_EVENTSEL0 + i, 0);
+               write_msr(MSR_IA32_PERFCTR0 + i, 0);
+       }
+       write_msr(MSR_CORE_PERF_FIXED_CTR_CTRL, 0);
+       for (i = 0; i < (int) cpu_caps.fix_counters_x_proc; i++)
+               write_msr(MSR_CORE_PERF_FIXED_CTR0 + i, 0);
+
+       write_mmreg32(LAPIC_LVT_PERFMON, IdtLAPIC_PCINT);
+}
+
+void perfmon_interrupt(struct hw_trapframe *hw_tf, void *data)
+{
+       int i;
+       struct perfmon_cpu_context *cctx = PERCPU_VARPTR(counters_env);
+       uint64_t gctrl, status;
+
+       profiler_add_hw_sample(hw_tf);
+
+       spin_lock_irqsave(&cctx->lock);
+       /* We need to save the global control status, because we need to disable
+        * counters in order to be able to reset their values.
+        * We will restore the global control status on exit.
+        */
+       gctrl = read_msr(MSR_CORE_PERF_GLOBAL_CTRL);
+       write_msr(MSR_CORE_PERF_GLOBAL_CTRL, 0);
+       status = read_msr(MSR_CORE_PERF_GLOBAL_STATUS);
+       for (i = 0; i < (int) cpu_caps.counters_x_proc; i++) {
+               if (status & ((uint64_t) 1 << i)) {
+                       if (cctx->counters[i].u.v)
+                               write_msr(MSR_IA32_PERFCTR0 + i,
+                                                 -(int64_t) cctx->counters[i].trigger_count);
+               }
+       }
+       for (i = 0; i < (int) cpu_caps.fix_counters_x_proc; i++) {
+               if (status & ((uint64_t) 1 << (32 + i))) {
+                       if (cctx->fixed_counters[i].u.v)
+                               write_msr(MSR_CORE_PERF_FIXED_CTR0 + i,
+                                                 -(int64_t) cctx->fixed_counters[i].trigger_count);
+               }
+       }
+       write_msr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, status);
+       write_msr(MSR_CORE_PERF_GLOBAL_CTRL, gctrl);
+       spin_unlock_irqsave(&cctx->lock);
+
+       /* We need to re-arm the IRQ as the PFM IRQ gets masked on trigger.
+        */
+       write_mmreg32(LAPIC_LVT_PERFMON, IdtLAPIC_PCINT);
+}
+
+void perfmon_get_cpu_caps(struct perfmon_cpu_caps *pcc)
+{
+       memcpy(pcc, &cpu_caps, sizeof(*pcc));
+}
+
+static int perfmon_install_session_alloc(struct perfmon_session *ps,
+                                                                                struct perfmon_alloc *pa)
+{
+       int i;
+
+       spin_lock(&ps->lock);
+       for (i = 0; (i < ARRAY_SIZE(ps->allocs)) && (ps->allocs[i] != NULL); i++)
+               ;
+       if (likely(i < ARRAY_SIZE(ps->allocs)))
+               ps->allocs[i] = pa;
+       else
+               i = -ENFILE;
+       spin_unlock(&ps->lock);
+       if (unlikely(i < 0))
+               error(-i, NULL);
+
+       return i;
+}
+
+int perfmon_open_event(const struct core_set *cset, struct perfmon_session *ps,
+                                          const struct perfmon_event *pev)
+{
+       ERRSTACK(1);
+       int i;
+       struct perfmon_alloc *pa = perfmon_create_alloc(pev);
+
+       if (waserror()) {
+               perfmon_destroy_alloc(pa);
+               nexterror();
+       }
+       smp_do_in_cores(cset, perfmon_do_cores_alloc, pa);
+
+       for (i = 0; i < num_cores; i++) {
+               if (core_set_getcpu(cset, i)) {
+                       counter_t ccno = pa->cores_counters[i];
+
+                       if (unlikely(ccno < 0)) {
+                               perfmon_destroy_alloc(pa);
+                               return (int) ccno;
+                       }
+               }
+       }
+       /* The perfmon_alloc data structure will not be visible to userspace,
+        * until the perfmon_install_session_alloc() completes, and at that
+        * time the smp_do_in_cores(perfmon_do_cores_alloc) will have run on
+        * all cores.
+        * The perfmon_alloc data structure will never be changed once published.
+        */
+       i = perfmon_install_session_alloc(ps, pa);
+       poperror();
+
+       return i;
+}
+
+static void perfmon_alloc_get(struct perfmon_session *ps, int ped, bool reset,
+                                                         struct perfmon_alloc **ppa)
+{
+       struct perfmon_alloc *pa;
+
+       if (unlikely((ped < 0) || (ped >= ARRAY_SIZE(ps->allocs))))
+               error(EBADFD, NULL);
+       spin_lock(&ps->lock);
+       pa = ps->allocs[ped];
+       if (likely(pa)) {
+               if (reset)
+                       ps->allocs[ped] = NULL;
+               else
+                       kref_get(&pa->ref, 1);
+       }
+       spin_unlock(&ps->lock);
+       if (unlikely(!pa))
+               error(ENOENT, NULL);
+       *ppa = pa;
+}
+
+void perfmon_close_event(struct perfmon_session *ps, int ped)
+{
+       struct perfmon_alloc *pa;
+
+       perfmon_alloc_get(ps, ped, TRUE, &pa);
+       kref_put(&pa->ref);
+}
+
+struct perfmon_status *perfmon_get_event_status(struct perfmon_session *ps,
+                                                                                               int ped)
+{
+       struct core_set cset;
+       struct perfmon_status_env env;
+
+       perfmon_alloc_get(ps, ped, FALSE, &env.pa);
+       env.pef = perfmon_alloc_status();
+       perfmon_setup_alloc_core_set(env.pa, &cset);
+
+       smp_do_in_cores(&cset, perfmon_do_cores_status, &env);
+
+       kref_put(&env.pa->ref);
+
+       return env.pef;
+}
+
+void perfmon_free_event_status(struct perfmon_status *pef)
+{
+       kfree(pef);
+}
+
+static void perfmon_release_session(struct kref *kref)
+{
+       struct perfmon_session *ps = container_of(kref, struct perfmon_session,
+                                                                                         ref);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ps->allocs); i++) {
+               struct perfmon_alloc *pa = ps->allocs[i];
+
+               if (pa)
+                       kref_put(&pa->ref);
+       }
+       kfree(ps);
+}
+
+struct perfmon_session *perfmon_create_session(void)
+{
+       struct perfmon_session *ps = kzmalloc(sizeof(struct perfmon_session),
+                                                                                 KMALLOC_WAIT);
+
+       kref_init(&ps->ref, perfmon_release_session, 1);
+       spinlock_init(&ps->lock);
+
+       return ps;
+}
+
+void perfmon_get_session(struct perfmon_session *ps)
+{
+       kref_get(&ps->ref, 1);
+}
+
+void perfmon_close_session(struct perfmon_session *ps)
+{
+       if (likely(ps))
+               kref_put(&ps->ref);
 }
index e084584..a655098 100644 (file)
@@ -1,21 +1,71 @@
+/* Copyright (c) 2015 Google Inc
+ * Davide Libenzi <dlibenzi@google.com>
+ * See LICENSE for details.
+ */
+
 #pragma once
+
+#include <sys/types.h>
 #include <ros/common.h>
+#include <ros/arch/perfmon.h>
 #include <arch/x86.h>
+#include <atomic.h>
+#include <core_set.h>
+#include <kref.h>
+#include <stdint.h>
+
+#define MAX_VAR_COUNTERS 32
+#define MAX_FIX_COUNTERS 16
+#define MAX_PERFMON_COUNTERS (MAX_VAR_COUNTERS + MAX_FIX_COUNTERS)
+#define INVALID_COUNTER INT32_MIN
+
+struct hw_trapframe;
+
+typedef int32_t counter_t;
 
-#define IA32_PMC_BASE 0xC1
-#define IA32_PERFEVTSEL_BASE 0x186
+struct perfmon_cpu_caps {
+       uint32_t perfmon_version;
+       uint32_t proc_arch_events;
+       uint32_t bits_x_counter;
+       uint32_t counters_x_proc;
+       uint32_t bits_x_fix_counter;
+       uint32_t fix_counters_x_proc;
+};
 
-#define LLCACHE_EVENT 0x2E
-#define LLCACHE_MISS_MASK 0x41
-#define LLCACHE_REF_MASK 0x4F
-#define ENABLE_PERFCTR 0x00400000
-#define DISABLE_PERFCTR 0xFFAFFFFF
+struct perfmon_alloc {
+       struct kref ref;
+       struct perfmon_event ev;
+       counter_t cores_counters[0];
+};
+
+struct perfmon_session {
+       struct kref ref;
+       spinlock_t lock;
+       struct perfmon_alloc *allocs[MAX_PERFMON_COUNTERS];
+};
+
+struct perfmon_status {
+       struct perfmon_event ev;
+       uint64_t cores_values[0];
+};
+
+void perfmon_init(void);
+void perfmon_interrupt(struct hw_trapframe *hw_tf, void *data);
+void perfmon_get_cpu_caps(struct perfmon_cpu_caps *pcc);
+int perfmon_open_event(const struct core_set *cset, struct perfmon_session *ps,
+                                          const struct perfmon_event *pev);
+void perfmon_close_event(struct perfmon_session *ps, int ped);
+struct perfmon_status *perfmon_get_event_status(struct perfmon_session *ps,
+                                                                                               int ped);
+void perfmon_free_event_status(struct perfmon_status *pef);
+struct perfmon_session *perfmon_create_session(void);
+void perfmon_get_session(struct perfmon_session *ps);
+void perfmon_close_session(struct perfmon_session *ps);
 
 static inline uint64_t read_pmc(uint32_t index)
 {
        uint32_t edx, eax;
+
        asm volatile("rdpmc" : "=d"(edx), "=a"(eax) : "c"(index));
-       return (uint64_t)edx << 32 | eax;
+       return ((uint64_t) edx << 32) | eax;
 }
-
-void perfmon_init();
diff --git a/kern/arch/x86/ros/perfmon.h b/kern/arch/x86/ros/perfmon.h
new file mode 100644 (file)
index 0000000..aa7091c
--- /dev/null
@@ -0,0 +1,116 @@
+/* Copyright (c) 2015 Google Inc
+ * Davide Libenzi <dlibenzi@google.com>
+ * See LICENSE for details.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+#include <ros/common.h>
+#include <ros/memops.h>
+#include <ros/bitfield.h>
+#include <stdint.h>
+
+/* The request format for the #arch/perf device is as follow (all the integers
+ * listed here are little endian):
+ *
+ * U8 CMD;
+ * [CMD dependent payload]
+ *
+ * The following command are supported, with their own structure:
+ *
+ * PERFMON_CMD_COUNTER_OPEN request
+ *   U8 CMD; (= PERFMON_CMD_COUNTER_OPEN)
+ *   U64 EVENT_DESCRIPTOR;
+ *   U64 EVENT_FLAGS;
+ *   U64 EVENT_TRIGGER_COUNT;
+ *   U32 NUM_CPUMASK_BYTES;
+ *   U8 CPUMASK_BYTES[NUM_CPUMASK_BYTES];
+ * PERFMON_CMD_COUNTER_OPEN response
+ *   U32 EVENT_DESCRIPTOR;
+ *
+ * PERFMON_CMD_COUNTER_STATUS request
+ *   U8 CMD; (= PERFMON_CMD_COUNTER_STATUS)
+ *   U32 EVENT_DESCRIPTOR;
+ * PERFMON_CMD_COUNTER_STATUS response
+ *   U64 EVENT_DESCRIPTOR;
+ *   U64 EVENT_FLAGS;
+ *   U64 EVENT_TRIGGER_COUNT;
+ *   U32 NUM_VALUES; (always num_cores)
+ *   U64 VALUES[NUM_VALUES]; (one value per core - zero if the counter was not
+ *                            active in that core)
+ *
+ * PERFMON_CMD_COUNTER_CLOSE request
+ *   U8 CMD; (= PERFMON_CMD_COUNTER_CLOSE)
+ *   U32 EVENT_DESCRIPTOR;
+ * PERFMON_CMD_COUNTER_CLOSE response
+ *   NONE
+ *
+ * PERFMON_CMD_CPU_CAPS request
+ *   U8 CMD; (= PERFMON_CMD_CPU_CAPS)
+ * PERFMON_CMD_CPU_CAPS response
+ *   U32 PERFMON_VERSION;
+ *   U32 ARCH_EVENTS;
+ *   U32 BITS_X_COUNTER;
+ *   U32 COUNTERS_X_PROC;
+ *   U32 BITS_X_FIX_COUNTER;
+ *   U32 FIX_COUNTERS_X_PROC;
+ */
+
+#define PERFMON_CMD_COUNTER_OPEN 1
+#define PERFMON_CMD_COUNTER_STATUS 2
+#define PERFMON_CMD_COUNTER_CLOSE 3
+#define PERFMON_CMD_CPU_CAPS 4
+
+#define PERFMON_FIXED_EVENT (1 << 0)
+
+#define PMEV_EVENT MKBITFIELD(0, 8)
+#define PMEV_MASK MKBITFIELD(8, 8)
+#define PMEV_USR MKBITFIELD(16, 1)
+#define PMEV_OS MKBITFIELD(17, 1)
+#define PMEV_EDGE MKBITFIELD(18, 1)
+#define PMEV_PC MKBITFIELD(19, 1)
+#define PMEV_INTEN MKBITFIELD(20, 1)
+#define PMEV_ANYTH MKBITFIELD(21, 1)
+#define PMEV_EN MKBITFIELD(22, 1)
+#define PMEV_INVCMSK MKBITFIELD(23, 1)
+#define PMEV_CMASK MKBITFIELD(24, 8)
+
+#define PMEV_GET_EVENT(v) BF_GETFIELD(v, PMEV_EVENT)
+#define PMEV_SET_EVENT(v, x) BF_SETFIELD(v, x, PMEV_EVENT)
+#define PMEV_GET_MASK(v) BF_GETFIELD(v, PMEV_MASK)
+#define PMEV_SET_MASK(v, x) BF_SETFIELD(v, x, PMEV_MASK)
+#define PMEV_GET_USR(v) BF_GETFIELD(v, PMEV_USR)
+#define PMEV_SET_USR(v, x) BF_SETFIELD(v, x, PMEV_USR)
+#define PMEV_GET_OS(v) BF_GETFIELD(v, PMEV_OS)
+#define PMEV_SET_OS(v, x) BF_SETFIELD(v, x, PMEV_OS)
+#define PMEV_GET_EDGE(v) BF_GETFIELD(v, PMEV_EDGE)
+#define PMEV_SET_EDGE(v, x) BF_SETFIELD(v, x, PMEV_EDGE)
+#define PMEV_GET_PC(v) BF_GETFIELD(v, PMEV_PC)
+#define PMEV_SET_PC(v, x) BF_SETFIELD(v, x, PMEV_PC)
+#define PMEV_GET_INTEN(v) BF_GETFIELD(v, PMEV_INTEN)
+#define PMEV_SET_INTEN(v, x) BF_SETFIELD(v, x, PMEV_INTEN)
+#define PMEV_GET_ANYTH(v) BF_GETFIELD(v, PMEV_ANYTH)
+#define PMEV_SET_ANYTH(v, x) BF_SETFIELD(v, x, PMEV_ANYTH)
+#define PMEV_GET_EN(v) BF_GETFIELD(v, PMEV_EN)
+#define PMEV_SET_EN(v, x) BF_SETFIELD(v, x, PMEV_EN)
+#define PMEV_GET_INVCMSK(v) BF_GETFIELD(v, PMEV_INVCMSK)
+#define PMEV_SET_INVCMSK(v, x) BF_SETFIELD(v, x, PMEV_INVCMSK)
+#define PMEV_GET_CMASK(v) BF_GETFIELD(v, PMEV_CMASK)
+#define PMEV_SET_CMASK(v, x) BF_SETFIELD(v, x, PMEV_CMASK)
+
+struct perfmon_event {
+       uint64_t event;
+       uint64_t flags;
+       uint64_t trigger_count;
+};
+
+static inline void perfmon_init_event(struct perfmon_event *pev)
+{
+       ZERO_DATA(*pev);
+}
+
+static inline bool perfmon_is_fixed_event(const struct perfmon_event *pev)
+{
+       return (pev->flags & PERFMON_FIXED_EVENT) != 0;
+}
index 2cb9e7c..6326290 100644 (file)
@@ -3,6 +3,7 @@
 #include <arch/arch.h>
 #include <arch/console.h>
 #include <arch/apic.h>
+#include <arch/perfmon.h>
 #include <ros/common.h>
 #include <smp.h>
 #include <assert.h>
@@ -193,6 +194,8 @@ void idt_init(void)
                     MKBUS(BusLAPIC, 0, 0, 0));
        register_irq(IdtLAPIC_ERROR, handle_lapic_error, NULL,
                     MKBUS(BusLAPIC, 0, 0, 0));
+       register_irq(IdtLAPIC_PCINT, perfmon_interrupt, NULL,
+                    MKBUS(BusLAPIC, 0, 0, 0));
        register_irq(I_KERNEL_MSG, handle_kmsg_ipi, NULL, MKBUS(BusIPI, 0, 0, 0));
 }