1 /* $NetBSD: tprof_x86_intel.c,v 1.3 2019/06/14 11:50:35 msaitoh Exp $ */ 2 3 /* 4 * Copyright (c) 2018 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Maxime Villard. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Copyright (c)2008,2009 YAMAMOTO Takashi, 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 48 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 55 * SUCH DAMAGE. 56 */ 57 58 #include <sys/cdefs.h> 59 __KERNEL_RCSID(0, "$NetBSD: tprof_x86_intel.c,v 1.3 2019/06/14 11:50:35 msaitoh Exp $"); 60 61 #include <sys/param.h> 62 #include <sys/systm.h> 63 #include <sys/device.h> 64 #include <sys/kernel.h> 65 #include <sys/module.h> 66 67 #include <sys/cpu.h> 68 #include <sys/xcall.h> 69 70 #include <dev/tprof/tprof.h> 71 72 #include <uvm/uvm.h> /* VM_MIN_KERNEL_ADDRESS */ 73 74 #include <x86/nmi.h> 75 76 #include <machine/cpufunc.h> 77 #include <machine/cputypes.h> /* CPUVENDOR_* */ 78 #include <machine/cpuvar.h> /* cpu_vendor */ 79 #include <machine/i82489reg.h> 80 #include <machine/i82489var.h> 81 82 #define PERFEVTSEL_EVENT_SELECT __BITS(0, 7) 83 #define PERFEVTSEL_UNIT_MASK __BITS(8, 15) 84 #define PERFEVTSEL_USR __BIT(16) 85 #define PERFEVTSEL_OS __BIT(17) 86 #define PERFEVTSEL_E __BIT(18) 87 #define PERFEVTSEL_PC __BIT(19) 88 #define PERFEVTSEL_INT __BIT(20) 89 #define PERFEVTSEL_EN __BIT(22) 90 #define PERFEVTSEL_INV __BIT(23) 91 #define PERFEVTSEL_COUNTER_MASK __BITS(24, 31) 92 93 #define CPUID_0A_VERSION __BITS(0, 7) 94 #define CPUID_0A_NCOUNTERS __BITS(8, 15) 95 #define CPUID_0A_BITWIDTH __BITS(16, 23) 96 97 static uint64_t counter_bitwidth; 98 static uint64_t counter_val = 5000000; 99 static uint64_t counter_reset_val; 100 101 static uint32_t intel_lapic_saved[MAXCPUS]; 102 static nmi_handler_t *intel_nmi_handle; 103 static tprof_param_t intel_param; 104 105 static void 106 tprof_intel_start_cpu(void *arg1, void *arg2) 107 { 108 struct cpu_info * const ci = curcpu(); 109 uint64_t evtval; 110 111 evtval = 112 __SHIFTIN(intel_param.p_event, PERFEVTSEL_EVENT_SELECT) | 113 __SHIFTIN(intel_param.p_unit, PERFEVTSEL_UNIT_MASK) | 114 ((intel_param.p_flags & TPROF_PARAM_USER) ? PERFEVTSEL_USR : 0) | 115 ((intel_param.p_flags & TPROF_PARAM_KERN) ? PERFEVTSEL_OS : 0) | 116 PERFEVTSEL_INT | 117 PERFEVTSEL_EN; 118 119 wrmsr(MSR_PERFCTR0, counter_reset_val); 120 wrmsr(MSR_EVNTSEL0, evtval); 121 122 intel_lapic_saved[cpu_index(ci)] = lapic_readreg(LAPIC_LVT_PCINT); 123 lapic_writereg(LAPIC_LVT_PCINT, LAPIC_DLMODE_NMI); 124 } 125 126 static void 127 tprof_intel_stop_cpu(void *arg1, void *arg2) 128 { 129 struct cpu_info * const ci = curcpu(); 130 131 wrmsr(MSR_EVNTSEL0, 0); 132 wrmsr(MSR_PERFCTR0, 0); 133 134 lapic_writereg(LAPIC_LVT_PCINT, intel_lapic_saved[cpu_index(ci)]); 135 } 136 137 static int 138 tprof_intel_nmi(const struct trapframe *tf, void *dummy) 139 { 140 uint32_t pcint; 141 uint64_t ctr; 142 tprof_frame_info_t tfi; 143 144 KASSERT(dummy == NULL); 145 146 ctr = rdmsr(MSR_PERFCTR0); 147 /* If the highest bit is non zero, then it's not for us. */ 148 if ((ctr & __BIT(counter_bitwidth-1)) != 0) { 149 return 0; 150 } 151 152 /* record a sample */ 153 #if defined(__x86_64__) 154 tfi.tfi_pc = tf->tf_rip; 155 #else 156 tfi.tfi_pc = tf->tf_eip; 157 #endif 158 tfi.tfi_inkernel = tfi.tfi_pc >= VM_MIN_KERNEL_ADDRESS; 159 tprof_sample(NULL, &tfi); 160 161 /* reset counter */ 162 wrmsr(MSR_PERFCTR0, counter_reset_val); 163 164 /* unmask PMI */ 165 pcint = lapic_readreg(LAPIC_LVT_PCINT); 166 KASSERT((pcint & LAPIC_LVT_MASKED) != 0); 167 lapic_writereg(LAPIC_LVT_PCINT, pcint & ~LAPIC_LVT_MASKED); 168 169 return 1; 170 } 171 172 static uint64_t 173 tprof_intel_estimate_freq(void) 174 { 175 uint64_t cpufreq = curcpu()->ci_data.cpu_cc_freq; 176 uint64_t freq = 10000; 177 178 counter_val = cpufreq / freq; 179 if (counter_val == 0) { 180 counter_val = UINT64_C(4000000000) / freq; 181 } 182 return freq; 183 } 184 185 static uint32_t 186 tprof_intel_ident(void) 187 { 188 uint32_t descs[4]; 189 190 if (cpu_vendor != CPUVENDOR_INTEL) { 191 return TPROF_IDENT_NONE; 192 } 193 194 if (cpuid_level < 0x0A) { 195 return TPROF_IDENT_NONE; 196 } 197 x86_cpuid(0x0A, descs); 198 if ((descs[0] & CPUID_0A_VERSION) == 0) { 199 return TPROF_IDENT_NONE; 200 } 201 if ((descs[0] & CPUID_0A_NCOUNTERS) == 0) { 202 return TPROF_IDENT_NONE; 203 } 204 205 counter_bitwidth = __SHIFTOUT(descs[0], CPUID_0A_BITWIDTH); 206 207 return TPROF_IDENT_INTEL_GENERIC; 208 } 209 210 static int 211 tprof_intel_start(const tprof_param_t *param) 212 { 213 uint64_t xc; 214 215 if (tprof_intel_ident() == TPROF_IDENT_NONE) { 216 return ENOTSUP; 217 } 218 219 KASSERT(intel_nmi_handle == NULL); 220 intel_nmi_handle = nmi_establish(tprof_intel_nmi, NULL); 221 222 counter_reset_val = - counter_val + 1; 223 memcpy(&intel_param, param, sizeof(*param)); 224 225 xc = xc_broadcast(0, tprof_intel_start_cpu, NULL, NULL); 226 xc_wait(xc); 227 228 return 0; 229 } 230 231 static void 232 tprof_intel_stop(const tprof_param_t *param) 233 { 234 uint64_t xc; 235 236 xc = xc_broadcast(0, tprof_intel_stop_cpu, NULL, NULL); 237 xc_wait(xc); 238 239 KASSERT(intel_nmi_handle != NULL); 240 nmi_disestablish(intel_nmi_handle); 241 intel_nmi_handle = NULL; 242 } 243 244 const tprof_backend_ops_t tprof_intel_ops = { 245 .tbo_estimate_freq = tprof_intel_estimate_freq, 246 .tbo_ident = tprof_intel_ident, 247 .tbo_start = tprof_intel_start, 248 .tbo_stop = tprof_intel_stop, 249 }; 250