xref: /openbsd/sys/arch/amd64/amd64/pctr.c (revision ccd74f94)
1 /*	$OpenBSD: pctr.c,v 1.10 2024/04/03 02:01:21 guenther Exp $	*/
2 
3 /*
4  * Copyright (c) 2007 Mike Belopuhov
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * Pentium performance counter driver for OpenBSD.
21  * Copyright 1996 David Mazieres <dm@lcs.mit.edu>.
22  *
23  * Modification and redistribution in source and binary forms is
24  * permitted provided that due credit is given to the author and the
25  * OpenBSD project by leaving this copyright notice intact.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/errno.h>
30 #include <sys/fcntl.h>
31 #include <sys/ioccom.h>
32 #include <sys/mutex.h>
33 #include <sys/systm.h>
34 
35 #include <machine/intr.h>
36 #include <machine/pctr.h>
37 #include <machine/cpu.h>
38 #include <machine/specialreg.h>
39 
40 #define PCTR_AMD_NUM	PCTR_NUM
41 #define PCTR_INTEL_NUM	2		/* Intel supports only 2 counters */
42 #define PCTR_INTEL_VERSION_MASK 0xff
43 
44 #define usetsc		(cpu_feature & CPUID_TSC)
45 #define usepctr		((pctr_isamd && ((cpu_id >> 8) & 15) >= 6) || \
46 			    (pctr_isintel && \
47 			    (pctr_intel_cap & PCTR_INTEL_VERSION_MASK) >= 1))
48 
49 int			pctr_isamd;
50 int			pctr_isintel;
51 uint32_t		pctr_intel_cap;
52 
53 struct mutex		pctr_conf_lock = MUTEX_INITIALIZER(IPL_HIGH);
54 uint32_t		pctr_fn[PCTR_NUM];
55 
56 static void		pctrrd(struct pctrst *);
57 static int		pctrsel(int, uint32_t, uint32_t);
58 static void		pctr_enable(struct cpu_info *);
59 
60 static void
pctrrd(struct pctrst * st)61 pctrrd(struct pctrst *st)
62 {
63 	int i, num, reg;
64 
65 	num = pctr_isamd ? PCTR_AMD_NUM : PCTR_INTEL_NUM;
66 	reg = pctr_isamd ? MSR_K7_EVNTSEL0 : MSR_EVNTSEL0;
67 	for (i = 0; i < num; i++)
68 		st->pctr_fn[i] = rdmsr(reg + i);
69 	__asm volatile("cli");
70 	st->pctr_tsc = rdtsc();
71 	for (i = 0; i < num; i++)
72 		st->pctr_hwc[i] = rdpmc(i);
73 	__asm volatile("sti");
74 }
75 
76 void
pctrattach(int num)77 pctrattach(int num)
78 {
79 	struct cpu_info *ci = &cpu_info_primary;
80 	uint32_t dummy;
81 
82 	if (num > 1)
83 		return;
84 
85 	pctr_isamd = (ci->ci_vendor == CPUV_AMD);
86 	if (!pctr_isamd) {
87 		pctr_isintel = (ci->ci_vendor == CPUV_INTEL);
88 		CPUID(0xa, pctr_intel_cap, dummy, dummy, dummy);
89 	}
90 }
91 
92 void
pctr_enable(struct cpu_info * ci)93 pctr_enable(struct cpu_info *ci)
94 {
95 	if (usepctr) {
96 		/* Enable RDTSC and RDPMC instructions from user-level. */
97 		__asm volatile("movq %%cr4,%%rax\n"
98 				 "\tandq %0,%%rax\n"
99 				 "\torq %1,%%rax\n"
100 				 "\tmovq %%rax,%%cr4"
101 				 :: "i" (~CR4_TSD), "i" (CR4_PCE) : "rax");
102 	} else if (usetsc) {
103 		/* Enable RDTSC instruction from user-level. */
104 		__asm volatile("movq %%cr4,%%rax\n"
105 				 "\tandq %0,%%rax\n"
106 				 "\tmovq %%rax,%%cr4"
107 				 :: "i" (~CR4_TSD) : "rax");
108 	}
109 }
110 
111 int
pctropen(dev_t dev,int oflags,int devtype,struct proc * p)112 pctropen(dev_t dev, int oflags, int devtype, struct proc *p)
113 {
114 
115 	if (minor(dev))
116 		return (ENXIO);
117 	return (0);
118 }
119 
120 int
pctrclose(dev_t dev,int oflags,int devtype,struct proc * p)121 pctrclose(dev_t dev, int oflags, int devtype, struct proc *p)
122 {
123 
124 	return (0);
125 }
126 
127 static int
pctrsel(int fflag,uint32_t cmd,uint32_t fn)128 pctrsel(int fflag, uint32_t cmd, uint32_t fn)
129 {
130 	int msrsel, msrval, changed;
131 
132 	cmd -= PCIOCS0;
133 	if (pctr_isamd) {
134 		if (cmd > PCTR_AMD_NUM-1)
135 			return (EINVAL);
136 		msrsel = MSR_K7_EVNTSEL0 + cmd;
137 		msrval = MSR_K7_PERFCTR0 + cmd;
138 	} else {
139 		if (cmd > PCTR_INTEL_NUM-1)
140 			return (EINVAL);
141 		msrsel = MSR_EVNTSEL0 + cmd;
142 		msrval = MSR_PERFCTR0 + cmd;
143 	}
144 
145 	if (!(fflag & FWRITE))
146 		return (EPERM);
147 	if (fn & 0x380000)
148 		return (EINVAL);
149 
150 	if (fn != 0)
151 		pctr_enable(curcpu());
152 
153 	mtx_enter(&pctr_conf_lock);
154 	changed = fn != pctr_fn[cmd];
155 	if (changed) {
156 		pctr_fn[cmd] = fn;
157 		wrmsr(msrval, 0);
158 		wrmsr(msrsel, fn);
159 		wrmsr(msrval, 0);
160 	}
161 	mtx_leave(&pctr_conf_lock);
162 #ifdef MULTIPROCESSOR
163 	if (changed)
164 		x86_broadcast_ipi(X86_IPI_PCTR);
165 #endif
166 
167 	return (0);
168 }
169 
170 int
pctrioctl(dev_t dev,u_long cmd,caddr_t data,int fflag,struct proc * p)171 pctrioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p)
172 {
173 	switch (cmd) {
174 	case PCIOCRD:
175 	{
176 		struct pctrst *st = (struct pctrst *)data;
177 
178 		if (usepctr)
179 			pctrrd(st);
180 		else if (usetsc)
181 			st->pctr_tsc = rdtsc();
182 		return (0);
183 	}
184 	case PCIOCS0:
185 	case PCIOCS1:
186 	case PCIOCS2:
187 	case PCIOCS3:
188 		if (usepctr)
189 			return (pctrsel(fflag, cmd, *(u_int *)data));
190 		return (ENODEV);
191 	default:
192 		return (EINVAL);
193 	}
194 }
195 
196 void
pctr_reload(struct cpu_info * ci)197 pctr_reload(struct cpu_info *ci)
198 {
199 	int num, i, msrsel, msrval, anyset;
200 	uint32_t fn;
201 
202 	if (pctr_isamd) {
203 		num = PCTR_AMD_NUM;
204 		msrsel = MSR_K7_EVNTSEL0;
205 		msrval = MSR_K7_PERFCTR0;
206 	} else {
207 		num = PCTR_INTEL_NUM;
208 		msrsel = MSR_EVNTSEL0;
209 		msrval = MSR_PERFCTR0;
210 	}
211 
212 	anyset = 0;
213 	mtx_enter(&pctr_conf_lock);
214 	for (i = 0; i < num; i++) {
215 		/* only update the ones that don't match */
216 		/* XXX generation numbers for zeroing? */
217 		fn = rdmsr(msrsel + i);
218 		if (fn != pctr_fn[i]) {
219 			wrmsr(msrval + i, 0);
220 			wrmsr(msrsel + i, pctr_fn[i]);
221 			wrmsr(msrval + i, 0);
222 		}
223 		if (fn)
224 			anyset = 1;
225 	}
226 	mtx_leave(&pctr_conf_lock);
227 
228 	if (! anyset)
229 		pctr_enable(curcpu());
230 }
231 
232 void
pctr_resume(struct cpu_info * ci)233 pctr_resume(struct cpu_info *ci)
234 {
235 	if (usepctr)
236 		pctr_reload(ci);
237 }
238