1 /* $NetBSD: tprof_x86_amd.c,v 1.5 2019/10/11 18:04:52 jmcneill 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_amd.c,v 1.5 2019/10/11 18:04:52 jmcneill Exp $"); 60 61 #include <sys/param.h> 62 #include <sys/systm.h> 63 #include <sys/kernel.h> 64 #include <sys/module.h> 65 66 #include <sys/cpu.h> 67 #include <sys/xcall.h> 68 69 #include <dev/tprof/tprof.h> 70 71 #include <uvm/uvm.h> /* VM_MIN_KERNEL_ADDRESS */ 72 73 #include <x86/nmi.h> 74 75 #include <machine/cpufunc.h> 76 #include <machine/cputypes.h> /* CPUVENDOR_* */ 77 #include <machine/cpuvar.h> /* cpu_vendor */ 78 #include <machine/i82489reg.h> 79 #include <machine/i82489var.h> 80 81 #define NCTRS 4 82 83 #define PERFEVTSEL(i) (0xc0010000 + (i)) 84 #define PERFCTR(i) (0xc0010004 + (i)) 85 86 #define PESR_EVENT_MASK_LO __BITS(0, 7) 87 #define PESR_UNIT_MASK __BITS(8, 15) 88 #define PESR_USR __BIT(16) 89 #define PESR_OS __BIT(17) 90 #define PESR_E __BIT(18) 91 #define PESR_PC __BIT(19) 92 #define PESR_INT __BIT(20) 93 /* bit 21 reserved */ 94 #define PESR_EN __BIT(22) 95 #define PESR_INV __BIT(23) 96 #define PESR_COUNTER_MASK __BITS(24, 31) 97 #define PESR_EVENT_MASK_HI __BITS(32, 35) 98 /* bit 36-39 reserved */ 99 #define PESR_GO __BIT(40) 100 #define PESR_HO __BIT(41) 101 /* bit 42-63 reserved */ 102 103 /* 104 * Documents: 105 * http://support.amd.com/TechDocs/32559.pdf 106 * http://developer.amd.com/wordpress/media/2012/10/Basic_Performance_Measurements.pdf 107 */ 108 109 static int ctrno = 0; 110 static uint64_t counter_val = 5000000; 111 static uint64_t counter_reset_val; 112 static uint32_t amd_lapic_saved[MAXCPUS]; 113 static nmi_handler_t *amd_nmi_handle; 114 static tprof_param_t amd_param; 115 116 static void 117 tprof_amd_start_cpu(void *arg1, void *arg2) 118 { 119 struct cpu_info * const ci = curcpu(); 120 uint64_t pesr; 121 uint64_t event_lo; 122 uint64_t event_hi; 123 124 event_hi = amd_param.p_event >> 8; 125 event_lo = amd_param.p_event & 0xff; 126 pesr = 127 ((amd_param.p_flags & TPROF_PARAM_USER) ? PESR_USR : 0) | 128 ((amd_param.p_flags & TPROF_PARAM_KERN) ? PESR_OS : 0) | 129 PESR_INT | 130 __SHIFTIN(event_lo, PESR_EVENT_MASK_LO) | 131 __SHIFTIN(event_hi, PESR_EVENT_MASK_HI) | 132 __SHIFTIN(0, PESR_COUNTER_MASK) | 133 __SHIFTIN(amd_param.p_unit, PESR_UNIT_MASK); 134 135 wrmsr(PERFCTR(ctrno), counter_reset_val); 136 wrmsr(PERFEVTSEL(ctrno), pesr); 137 138 amd_lapic_saved[cpu_index(ci)] = lapic_readreg(LAPIC_LVT_PCINT); 139 lapic_writereg(LAPIC_LVT_PCINT, LAPIC_DLMODE_NMI); 140 141 wrmsr(PERFEVTSEL(ctrno), pesr | PESR_EN); 142 } 143 144 static void 145 tprof_amd_stop_cpu(void *arg1, void *arg2) 146 { 147 struct cpu_info * const ci = curcpu(); 148 149 wrmsr(PERFEVTSEL(ctrno), 0); 150 151 lapic_writereg(LAPIC_LVT_PCINT, amd_lapic_saved[cpu_index(ci)]); 152 } 153 154 static int 155 tprof_amd_nmi(const struct trapframe *tf, void *dummy) 156 { 157 tprof_frame_info_t tfi; 158 uint64_t ctr; 159 160 KASSERT(dummy == NULL); 161 162 /* check if it's for us */ 163 ctr = rdmsr(PERFCTR(ctrno)); 164 if ((ctr & (UINT64_C(1) << 63)) != 0) { /* check if overflowed */ 165 /* not ours */ 166 return 0; 167 } 168 169 /* record a sample */ 170 #if defined(__x86_64__) 171 tfi.tfi_pc = tf->tf_rip; 172 #else 173 tfi.tfi_pc = tf->tf_eip; 174 #endif 175 tfi.tfi_inkernel = tfi.tfi_pc >= VM_MIN_KERNEL_ADDRESS; 176 tprof_sample(NULL, &tfi); 177 178 /* reset counter */ 179 wrmsr(PERFCTR(ctrno), counter_reset_val); 180 181 return 1; 182 } 183 184 static uint64_t 185 tprof_amd_estimate_freq(void) 186 { 187 uint64_t cpufreq = curcpu()->ci_data.cpu_cc_freq; 188 uint64_t freq = 10000; 189 190 counter_val = cpufreq / freq; 191 if (counter_val == 0) { 192 counter_val = UINT64_C(4000000000) / freq; 193 } 194 return freq; 195 } 196 197 static uint32_t 198 tprof_amd_ident(void) 199 { 200 struct cpu_info *ci = curcpu(); 201 202 if (cpu_vendor != CPUVENDOR_AMD) { 203 return TPROF_IDENT_NONE; 204 } 205 206 switch (CPUID_TO_FAMILY(ci->ci_signature)) { 207 case 0x10: 208 case 0x15: 209 case 0x17: 210 return TPROF_IDENT_AMD_GENERIC; 211 } 212 213 return TPROF_IDENT_NONE; 214 } 215 216 static int 217 tprof_amd_start(const tprof_param_t *param) 218 { 219 uint64_t xc; 220 221 if (tprof_amd_ident() == TPROF_IDENT_NONE) { 222 return ENOTSUP; 223 } 224 225 KASSERT(amd_nmi_handle == NULL); 226 amd_nmi_handle = nmi_establish(tprof_amd_nmi, NULL); 227 228 counter_reset_val = - counter_val + 1; 229 memcpy(&amd_param, param, sizeof(*param)); 230 231 xc = xc_broadcast(0, tprof_amd_start_cpu, NULL, NULL); 232 xc_wait(xc); 233 234 return 0; 235 } 236 237 static void 238 tprof_amd_stop(const tprof_param_t *param) 239 { 240 uint64_t xc; 241 242 xc = xc_broadcast(0, tprof_amd_stop_cpu, NULL, NULL); 243 xc_wait(xc); 244 245 KASSERT(amd_nmi_handle != NULL); 246 nmi_disestablish(amd_nmi_handle); 247 amd_nmi_handle = NULL; 248 } 249 250 const tprof_backend_ops_t tprof_amd_ops = { 251 .tbo_estimate_freq = tprof_amd_estimate_freq, 252 .tbo_ident = tprof_amd_ident, 253 .tbo_start = tprof_amd_start, 254 .tbo_stop = tprof_amd_stop, 255 }; 256