Home | History | Annotate | Line # | Download | only in tprof
tprof_x86_intel.c revision 1.2.2.2
      1 /*	$NetBSD: tprof_x86_intel.c,v 1.2.2.2 2018/07/28 04:37:57 pgoyette 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.2.2.2 2018/07/28 04:37:57 pgoyette 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_PCINT);
    123 	lapic_writereg(LAPIC_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_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_PCINT);
    166 	KASSERT((pcint & LAPIC_LVT_MASKED) != 0);
    167 	lapic_writereg(LAPIC_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