#include <mach/mach_types.h>
#include <machine/machine_routines.h>
#include <kern/processor.h>
-#include <kern/kalloc.h>
#include <i386/cpuid.h>
#include <i386/proc_reg.h>
#include <i386/mp.h>
#define IA32_FIXED_CTR_ENABLE_ALL_CTRS_ALL_RINGS (0x333)
#define IA32_FIXED_CTR_ENABLE_ALL_PMI (0x888)
+#define IA32_PERFEVT_USER_EN (0x10000)
+#define IA32_PERFEVT_OS_EN (0x20000)
+
#define IA32_PERFEVTSEL_PMI (1ull << 20)
#define IA32_PERFEVTSEL_EN (1ull << 22)
enabled = ml_set_interrupts_enabled(FALSE);
if (curcpu) {
- *curcpu = current_processor()->cpu_id;
+ *curcpu = cpu_number();
}
mp_cpus_call(CPUMASK_ALL, ASYNC, kpc_get_curcpu_counters_mp_call, &hdl);
return 0;
}
-/* PMI stuff */
+static uintptr_t
+get_interrupted_pc(bool *kernel_out)
+{
+ x86_saved_state_t *state = current_cpu_datap()->cpu_int_state;
+ if (!state) {
+ return 0;
+ }
+
+ bool state_64 = is_saved_state64(state);
+ uint64_t cs;
+ if (state_64) {
+ cs = saved_state64(state)->isf.cs;
+ } else {
+ cs = saved_state32(state)->cs;
+ }
+ bool kernel = (cs & SEL_PL) != SEL_PL_U;
+ *kernel_out = kernel;
+
+ uintptr_t pc = 0;
+ if (state_64) {
+ pc = saved_state64(state)->isf.rip;
+ } else {
+ pc = saved_state32(state)->eip;
+ }
+ if (kernel) {
+ pc = VM_KERNEL_UNSLIDE(pc);
+ }
+ return pc;
+}
+
+static void
+kpc_sample_kperf_x86(uint32_t ctr, uint32_t actionid, uint64_t count,
+ uint64_t config)
+{
+ bool kernel = false;
+ uintptr_t pc = get_interrupted_pc(&kernel);
+ kperf_kpc_flags_t flags = kernel ? KPC_KERNEL_PC : 0;
+ if ((config) & IA32_PERFEVT_USER_EN) {
+ flags |= KPC_USER_COUNTING;
+ }
+ if ((config) & IA32_PERFEVT_OS_EN) {
+ flags |= KPC_KERNEL_COUNTING;
+ }
+ kpc_sample_kperf(actionid, ctr,
+ config & 0xffff /* just the number and umask */, count, pc, flags);
+}
+
void
kpc_pmi_handler(void)
{
FIXED_SHADOW(ctr)
+= (kpc_fixed_max() - FIXED_RELOAD(ctr) + 1 /* Wrap */) + extra;
- BUF_INFO(PERF_KPC_FCOUNTER, ctr, FIXED_SHADOW(ctr), extra, FIXED_ACTIONID(ctr));
+ uint32_t actionid = FIXED_ACTIONID(ctr);
+ BUF_INFO(PERF_KPC_FCOUNTER, ctr, FIXED_SHADOW(ctr), extra, actionid);
- if (FIXED_ACTIONID(ctr)) {
- kpc_sample_kperf(FIXED_ACTIONID(ctr));
+ if (actionid != 0) {
+ kpc_sample_kperf_x86(ctr, actionid, FIXED_SHADOW(ctr) + extra, 0);
}
}
}
-#endif
+#endif // FIXED_COUNTER_SHADOW
for (ctr = 0; ctr < kpc_configurable_count(); ctr++) {
if ((1ULL << ctr) & status) {
extra = kpc_reload_configurable(ctr);
- CONFIGURABLE_SHADOW(ctr)
- += kpc_configurable_max() - CONFIGURABLE_RELOAD(ctr) + extra;
+ CONFIGURABLE_SHADOW(ctr) += kpc_configurable_max() -
+ CONFIGURABLE_RELOAD(ctr) + extra;
/* kperf can grab the PMCs when it samples so we need to make sure the overflow
* bits are in the correct state before the call to kperf_sample */
wrmsr64(MSR_IA32_PERF_GLOBAL_OVF_CTRL, 1ull << ctr);
- BUF_INFO(PERF_KPC_COUNTER, ctr, CONFIGURABLE_SHADOW(ctr), extra, CONFIGURABLE_ACTIONID(ctr));
+ unsigned int actionid = CONFIGURABLE_ACTIONID(ctr);
+ BUF_INFO(PERF_KPC_COUNTER, ctr, CONFIGURABLE_SHADOW(ctr), extra, actionid);
- if (CONFIGURABLE_ACTIONID(ctr)) {
- kpc_sample_kperf(CONFIGURABLE_ACTIONID(ctr));
+ if (actionid != 0) {
+ uint64_t config = IA32_PERFEVTSELx(ctr);
+ kpc_sample_kperf_x86(ctr + kpc_fixed_count(), actionid,
+ CONFIGURABLE_SHADOW(ctr) + extra, config);
}
}
}