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