xref: /openbsd-src/sys/arch/i386/i386/pctr.c (revision f4e7063748a2ac72b2bab4389c0a7efc72d82189)
1 /*	$OpenBSD: pctr.c,v 1.31 2023/01/30 10:49:05 jsg Exp $	*/
2 
3 /*
4  * Pentium performance counter driver for OpenBSD.
5  * Copyright 1996 David Mazieres <dm@lcs.mit.edu>.
6  *
7  * Modification and redistribution in source and binary forms is
8  * permitted provided that due credit is given to the author and the
9  * OpenBSD project by leaving this copyright notice intact.
10  */
11 
12 #include <sys/param.h>
13 #include <sys/errno.h>
14 #include <sys/fcntl.h>
15 #include <sys/systm.h>
16 
17 #include <machine/pctr.h>
18 #include <machine/cpu.h>
19 #include <machine/specialreg.h>
20 
21 #define PCTR_AMD_NUM	PCTR_NUM
22 #define PCTR_INTEL_NUM	2		/* Intel supports only 2 counters */
23 #define PCTR_INTEL_VERSION_MASK 0xff
24 
25 #define usetsc		(cpu_feature & CPUID_TSC)
26 #define usep5ctr	(pctr_isintel && (((cpu_id >> 8) & 15) == 5) && \
27 				(((cpu_id >> 4) & 15) > 0))
28 #define usepctr		((pctr_isamd && ((cpu_id >> 8) & 15) >= 6) || \
29 			    (pctr_isintel && \
30 			    (pctr_intel_cap & PCTR_INTEL_VERSION_MASK) >= 1))
31 
32 int			pctr_isamd;
33 int			pctr_isintel;
34 uint32_t		pctr_intel_cap;
35 
36 static int		p5ctrsel(int fflag, u_int cmd, u_int fn);
37 static int		pctrsel(int fflag, u_int cmd, u_int fn);
38 static void		pctrrd(struct pctrst *);
39 
40 static void
p5ctrrd(struct pctrst * st)41 p5ctrrd(struct pctrst *st)
42 {
43 	u_int msr11;
44 
45 	msr11 = rdmsr(P5MSR_CTRSEL);
46 	st->pctr_fn[0] = msr11 & 0xffff;
47 	st->pctr_fn[1] = msr11 >> 16;
48 	__asm volatile("cli");
49 	st->pctr_tsc = rdtsc();
50 	st->pctr_hwc[0] = rdmsr(P5MSR_CTR0);
51 	st->pctr_hwc[1] = rdmsr(P5MSR_CTR1);
52 	__asm volatile("sti");
53 }
54 
55 static void
pctrrd(struct pctrst * st)56 pctrrd(struct pctrst *st)
57 {
58 	int i, num, reg;
59 
60 	num = pctr_isamd ? PCTR_AMD_NUM : PCTR_INTEL_NUM;
61 	reg = pctr_isamd ? MSR_K7_EVNTSEL0 : P6MSR_CTRSEL0;
62 	for (i = 0; i < num; i++)
63 			st->pctr_fn[i] = rdmsr(reg + i);
64 	__asm volatile("cli");
65 	st->pctr_tsc = rdtsc();
66 	for (i = 0; i < num; i++)
67 		st->pctr_hwc[i] = rdpmc(i);
68 	__asm volatile("sti");
69 }
70 
71 void
pctrattach(int num)72 pctrattach(int num)
73 {
74 	uint32_t dummy;
75 
76 	if (num > 1)
77 		return;
78 
79 	pctr_isamd = (strcmp(cpu_vendor, "AuthenticAMD") == 0);
80 	if (!pctr_isamd && cpuid_level >= 0xa) {
81 		pctr_isintel = (strcmp(cpu_vendor, "GenuineIntel") == 0);
82 		CPUID(0xa, pctr_intel_cap, dummy, dummy, dummy);
83 	}
84 
85 	if (usepctr) {
86 		/* Enable RDTSC and RDPMC instructions from user-level. */
87 		__asm volatile ("movl %%cr4,%%eax\n"
88 				  "\tandl %0,%%eax\n"
89 				  "\torl %1,%%eax\n"
90 				  "\tmovl %%eax,%%cr4"
91 				  :: "i" (~CR4_TSD), "i" (CR4_PCE) : "eax");
92 	} else if (usetsc) {
93 		/* Enable RDTSC instruction from user-level. */
94 		__asm volatile ("movl %%cr4,%%eax\n"
95 				  "\tandl %0,%%eax\n"
96 				  "\tmovl %%eax,%%cr4"
97 				  :: "i" (~CR4_TSD) : "eax");
98 	}
99 }
100 
101 int
pctropen(dev_t dev,int oflags,int devtype,struct proc * p)102 pctropen(dev_t dev, int oflags, int devtype, struct proc *p)
103 {
104 
105 	if (minor(dev))
106 		return (ENXIO);
107 	return (0);
108 }
109 
110 int
pctrclose(dev_t dev,int oflags,int devtype,struct proc * p)111 pctrclose(dev_t dev, int oflags, int devtype, struct proc *p)
112 {
113 
114 	return (0);
115 }
116 
117 int
p5ctrsel(int fflag,u_int cmd,u_int fn)118 p5ctrsel(int fflag, u_int cmd, u_int fn)
119 {
120 	pctrval msr11;
121 	int msr, shift;
122 
123 	cmd -= PCIOCS0;
124 	if (cmd > 1)
125 		return (EINVAL);
126 	msr = P5MSR_CTR0 + cmd;
127 	shift = cmd ? 0x10 : 0;
128 
129 	if (!(fflag & FWRITE))
130 		return (EPERM);
131 	if (fn >= 0x200)
132 		return (EINVAL);
133 
134 	msr11 = rdmsr(P5MSR_CTRSEL);
135 	msr11 &= ~(0x1ffLL << shift);
136 	msr11 |= fn << shift;
137 	wrmsr(P5MSR_CTRSEL, msr11);
138 	wrmsr(msr, 0);
139 
140 	return (0);
141 }
142 
143 int
pctrsel(int fflag,u_int cmd,u_int fn)144 pctrsel(int fflag, u_int cmd, u_int fn)
145 {
146 	int msrsel, msrval;
147 
148 	cmd -= PCIOCS0;
149 	if (pctr_isamd) {
150 		if (cmd > PCTR_AMD_NUM-1)
151 			return (EINVAL);
152 		msrsel = MSR_K7_EVNTSEL0 + cmd;
153 		msrval = MSR_K7_PERFCTR0 + cmd;
154 	} else {
155 		if (cmd > PCTR_INTEL_NUM-1)
156 			return (EINVAL);
157 		msrsel = P6MSR_CTRSEL0 + cmd;
158 		msrval = P6MSR_CTR0 + cmd;
159 	}
160 
161 	if (!(fflag & FWRITE))
162 		return (EPERM);
163 	if (fn & 0x380000)
164 		return (EINVAL);
165 
166 	wrmsr(msrval, 0);
167 	wrmsr(msrsel, fn);
168 	wrmsr(msrval, 0);
169 
170 	return (0);
171 }
172 
173 int
pctrioctl(dev_t dev,u_long cmd,caddr_t data,int fflag,struct proc * p)174 pctrioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p)
175 {
176 	switch (cmd) {
177 	case PCIOCRD:
178 	{
179 		struct pctrst *st = (struct pctrst *)data;
180 
181 		if (usepctr)
182 			pctrrd(st);
183 		else if (usep5ctr)
184 			p5ctrrd(st);
185 		else {
186 			bzero(st, sizeof(*st));
187 			if (usetsc)
188 				st->pctr_tsc = rdtsc();
189 		}
190 		return (0);
191 	}
192 	case PCIOCS0:
193 	case PCIOCS1:
194 	case PCIOCS2:
195 	case PCIOCS3:
196 		if (usepctr)
197 			return (pctrsel(fflag, cmd, *(u_int *)data));
198 		if (usep5ctr)
199 			return (p5ctrsel(fflag, cmd, *(u_int *)data));
200 		return (ENODEV);
201 	default:
202 		return (EINVAL);
203 	}
204 }
205