xref: /netbsd-src/sys/dev/tprof/tprof_x86_amd.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
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