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