1/* $NetBSD: tprof_pmi.c,v 1.11 2010/05/09 20:32:41 rmind Exp $ */ 2 3/*- 4 * Copyright (c)2008,2009 YAMAMOTO Takashi, 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: tprof_pmi.c,v 1.11 2010/05/09 20:32:41 rmind Exp $"); 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/device.h> 35#include <sys/kernel.h> 36#include <sys/module.h> 37 38#include <sys/cpu.h> 39#include <sys/xcall.h> 40 41#include <dev/tprof/tprof.h> 42 43#include <uvm/uvm.h> /* VM_MIN_KERNEL_ADDRESS */ 44 45#include <x86/tprof.h> 46#include <x86/nmi.h> 47 48#include <machine/cpufunc.h> 49#include <machine/cputypes.h> /* CPUVENDER_* */ 50#include <machine/cpuvar.h> /* cpu_vendor */ 51#include <machine/i82489reg.h> 52#include <machine/i82489var.h> 53 54#define ESCR_T1_USR __BIT(0) 55#define ESCR_T1_OS __BIT(1) 56#define ESCR_T0_USR __BIT(2) 57#define ESCR_T0_OS __BIT(3) 58#define ESCR_TAG_ENABLE __BIT(4) 59#define ESCR_TAG_VALUE __BITS(5, 8) 60#define ESCR_EVENT_MASK __BITS(9, 24) 61#define ESCR_EVENT_SELECT __BITS(25, 30) 62 63#define CCCR_ENABLE __BIT(12) 64#define CCCR_ESCR_SELECT __BITS(13, 15) 65#define CCCR_MUST_BE_SET __BITS(16, 17) 66#define CCCR_COMPARE __BIT(18) 67#define CCCR_COMPLEMENT __BIT(19) 68#define CCCR_THRESHOLD __BITS(20, 23) 69#define CCCR_EDGE __BIT(24) 70#define CCCR_FORCE_OVF __BIT(25) 71#define CCCR_OVF_PMI_T0 __BIT(26) 72#define CCCR_OVF_PMI_T1 __BIT(27) 73#define CCCR_CASCADE __BIT(30) 74#define CCCR_OVF __BIT(31) 75 76struct msrs { 77 u_int msr_cccr; 78 u_int msr_escr; 79 u_int msr_counter; 80}; 81 82/* 83 * parameters (see 253669.pdf Table A-6) 84 * 85 * XXX should not hardcode 86 */ 87 88static const struct msrs msrs[] = { 89 { 90 .msr_cccr = 0x360, /* MSR_BPU_CCCR0 */ 91 .msr_escr = 0x3a2, /* MSR_FSB_ESCR0 */ 92 .msr_counter = 0x300, /* MSR_BPU_COUNTER0 */ 93 }, 94 { 95 .msr_cccr = 0x362, /* MSR_BPU_CCCR2 */ 96 .msr_escr = 0x3a3, /* MSR_FSB_ESCR1 */ 97 .msr_counter = 0x302, /* MSR_BPU_COUNTER2 */ 98 }, 99}; 100static const u_int cccr_escr_select = 0x6; /* MSR_FSB_ESCR? */ 101static const u_int escr_event_select = 0x13; /* global_power_events */ 102static const u_int escr_event_mask = 0x1; /* running */ 103 104static uint64_t counter_val = 5000000; 105static uint64_t counter_reset_val; 106static uint32_t tprof_pmi_lapic_saved[MAXCPUS]; 107 108static nmi_handler_t *tprof_pmi_nmi_handle; 109static tprof_backend_cookie_t *tprof_cookie; 110 111static void 112tprof_pmi_start_cpu(void *arg1, void *arg2) 113{ 114 struct cpu_info * const ci = curcpu(); 115 const struct msrs *msr; 116 uint64_t cccr; 117 uint64_t escr; 118 119 if (ci->ci_smt_id >= 2) { 120 printf("%s: ignoring %s smt id=%u", 121 __func__, device_xname(ci->ci_dev), 122 (u_int)ci->ci_smt_id); 123 return; 124 } 125 msr = &msrs[ci->ci_smt_id]; 126 escr = __SHIFTIN(escr_event_mask, ESCR_EVENT_MASK) | 127 __SHIFTIN(escr_event_select, ESCR_EVENT_SELECT); 128 cccr = CCCR_ENABLE | __SHIFTIN(cccr_escr_select, CCCR_ESCR_SELECT) | 129 CCCR_MUST_BE_SET; 130 if (ci->ci_smt_id == 0) { 131 escr |= ESCR_T0_OS | ESCR_T0_USR; 132 cccr |= CCCR_OVF_PMI_T0; 133 } else { 134 escr |= ESCR_T1_OS | ESCR_T0_USR; 135 cccr |= CCCR_OVF_PMI_T1; 136 } 137 138 wrmsr(msr->msr_counter, counter_reset_val); 139 wrmsr(msr->msr_escr, escr); 140 wrmsr(msr->msr_cccr, cccr); 141 tprof_pmi_lapic_saved[cpu_index(ci)] = i82489_readreg(LAPIC_PCINT); 142 i82489_writereg(LAPIC_PCINT, LAPIC_DLMODE_NMI); 143} 144 145static void 146tprof_pmi_stop_cpu(void *arg1, void *arg2) 147{ 148 struct cpu_info * const ci = curcpu(); 149 const struct msrs *msr; 150 151 if (ci->ci_smt_id >= 2) { 152 printf("%s: ignoring %s smt id=%u", 153 __func__, device_xname(ci->ci_dev), 154 (u_int)ci->ci_smt_id); 155 return; 156 } 157 msr = &msrs[ci->ci_smt_id]; 158 159 wrmsr(msr->msr_escr, 0); 160 wrmsr(msr->msr_cccr, 0); 161 i82489_writereg(LAPIC_PCINT, tprof_pmi_lapic_saved[cpu_index(ci)]); 162} 163 164static int 165tprof_pmi_nmi(const struct trapframe *tf, void *dummy) 166{ 167 struct cpu_info * const ci = curcpu(); 168 const struct msrs *msr; 169 uint32_t pcint; 170 uint64_t cccr; 171 tprof_frame_info_t tfi; 172 173 KASSERT(dummy == NULL); 174 175 if (ci->ci_smt_id >= 2) { 176 /* not ours */ 177 return 0; 178 } 179 msr = &msrs[ci->ci_smt_id]; 180 181 /* check if it's for us */ 182 cccr = rdmsr(msr->msr_cccr); 183 if ((cccr & CCCR_OVF) == 0) { 184 /* not ours */ 185 return 0; 186 } 187 188 /* record a sample */ 189#if defined(__x86_64__) 190 tfi.tfi_pc = tf->tf_rip; 191#else /* defined(__x86_64__) */ 192 tfi.tfi_pc = tf->tf_eip; 193#endif /* defined(__x86_64__) */ 194 tfi.tfi_inkernel = tfi.tfi_pc >= VM_MIN_KERNEL_ADDRESS; 195 tprof_sample(tprof_cookie, &tfi); 196 197 /* reset counter */ 198 wrmsr(msr->msr_counter, counter_reset_val); 199 wrmsr(msr->msr_cccr, cccr & ~CCCR_OVF); 200 201 /* unmask PMI */ 202 pcint = i82489_readreg(LAPIC_PCINT); 203 KASSERT((pcint & LAPIC_LVT_MASKED) != 0); 204 i82489_writereg(LAPIC_PCINT, pcint & ~LAPIC_LVT_MASKED); 205 206 return 1; 207} 208 209static uint64_t 210tprof_pmi_estimate_freq(void) 211{ 212 uint64_t cpufreq = curcpu()->ci_data.cpu_cc_freq; 213 uint64_t freq = 10000; 214 215 counter_val = cpufreq / freq; 216 if (counter_val == 0) { 217 counter_val = UINT64_C(4000000000) / freq; 218 return freq; 219 } 220 return freq; 221} 222 223static int 224tprof_pmi_start(tprof_backend_cookie_t *cookie) 225{ 226 struct cpu_info * const ci = curcpu(); 227 uint64_t xc; 228 229 if (!(cpu_vendor == CPUVENDOR_INTEL && 230 CPUID2FAMILY(ci->ci_signature) == 15)) { 231 return ENOTSUP; 232 } 233 234 KASSERT(tprof_pmi_nmi_handle == NULL); 235 tprof_pmi_nmi_handle = nmi_establish(tprof_pmi_nmi, NULL); 236 237 counter_reset_val = - counter_val + 1; 238 xc = xc_broadcast(0, tprof_pmi_start_cpu, NULL, NULL); 239 xc_wait(xc); 240 241 KASSERT(tprof_cookie == NULL); 242 tprof_cookie = cookie; 243 244 return 0; 245} 246 247static void 248tprof_pmi_stop(tprof_backend_cookie_t *cookie) 249{ 250 uint64_t xc; 251 252 xc = xc_broadcast(0, tprof_pmi_stop_cpu, NULL, NULL); 253 xc_wait(xc); 254 255 KASSERT(tprof_pmi_nmi_handle != NULL); 256 KASSERT(tprof_cookie == cookie); 257 nmi_disestablish(tprof_pmi_nmi_handle); 258 tprof_pmi_nmi_handle = NULL; 259 tprof_cookie = NULL; 260} 261 262static const tprof_backend_ops_t tprof_pmi_ops = { 263 .tbo_estimate_freq = tprof_pmi_estimate_freq, 264 .tbo_start = tprof_pmi_start, 265 .tbo_stop = tprof_pmi_stop, 266}; 267 268MODULE(MODULE_CLASS_DRIVER, tprof_pmi, "tprof"); 269 270static int 271tprof_pmi_modcmd(modcmd_t cmd, void *arg) 272{ 273 274 switch (cmd) { 275 case MODULE_CMD_INIT: 276 return tprof_backend_register("tprof_pmi", &tprof_pmi_ops, 277 TPROF_BACKEND_VERSION); 278 279 case MODULE_CMD_FINI: 280 return tprof_backend_unregister("tprof_pmi"); 281 282 default: 283 return ENOTTY; 284 } 285} 286