summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/arch/i386/i386/pctr.c173
-rw-r--r--sys/arch/i386/include/pctr.h103
-rw-r--r--usr.bin/pctrctl/pctrctl.c27
3 files changed, 219 insertions, 84 deletions
diff --git a/sys/arch/i386/i386/pctr.c b/sys/arch/i386/i386/pctr.c
index ed4c5dae112..15339775cee 100644
--- a/sys/arch/i386/i386/pctr.c
+++ b/sys/arch/i386/i386/pctr.c
@@ -1,8 +1,13 @@
-/* $OpenBSD: pctr.c,v 1.3 1996/08/08 22:21:23 dm Exp $ */
+/* $OpenBSD: pctr.c,v 1.4 1996/08/14 03:02:54 dm Exp $ */
/*
* Pentium performance counter driver for OpenBSD.
- * Author: David Mazieres <dm@lcs.mit.edu>
+ * Copyright 1996 David Mazieres <dm@lcs.mit.edu>.
+ *
+ * Modification and redistribution in source and binary forms is
+ * permitted provided that due credit is given to the author and the
+ * OpenBSD project (for instance by leaving this copyright notice
+ * intact).
*/
#include <sys/types.h>
@@ -16,48 +21,46 @@
#include <machine/cpu.h>
pctrval pctr_idlcnt; /* Gets incremented in locore.s */
-static int cpuid;
-#define rdtsc() \
-({ \
- pctrval v; \
- __asm __volatile (".byte 0xf, 0x31" : "=A" (v)); \
- v; \
-})
-
-#define rdmsr(msr) \
-({ \
- pctrval v; \
- __asm __volatile (".byte 0xf, 0x32" : "=A" (v) : "c" (msr)); \
- v; \
-})
-
-#define wrmsr(msr, v) \
- __asm __volatile (".byte 0xf, 0x30" :: "A" (v), "c" (msr));
+static int usetsc;
+static int usep5ctr;
+static int usep6ctr;
void
pctrattach (int num)
{
+ pctrval id;
+
if (num > 1)
panic ("no more than one pctr device");
-
- __asm __volatile ("cli\n"
- "\tpushfl\n"
- "\tpopl %%eax\n"
- "\tmovl %%eax,%%ecx\n"
- "\txorl %1,%%eax\n"
- "\tpushl %%eax\n"
- "\tpopfl\n"
- "\tpushfl\n"
- "\tpopl %%eax\n"
- "\tpushl %%ecx\n"
- "\tpopfl\n"
- "\tcmpl %%eax,%%ecx\n"
- "\tmov $0,%0\n"
- "\tje 1f\n"
- "\tcpuid\n"
- "1:\tsti"
- : "=a" (cpuid) : "i" (PSL_ID) : "edx", "ecx", "ebx");
+
+ id = __cpuid ();
+ usetsc = __hastsc (id);
+ usep5ctr = __hasp5ctr (id);
+ usep6ctr = __hasp6ctr (id);
+
+ if (usep6ctr)
+ /* Enable RDTSC and RDPMC instructions from user-level. */
+ asm volatile (".byte 0xf,0x20,0xe0 # movl %%cr4,%%eax\n"
+ "\tandl %0,%%eax\n"
+ "\torl %1,%%eax\n"
+ "\t.byte 0xf,0x22,0xe0 # movl %%cr4,%%eax"
+ :: "i" (~CR4_TSD), "i" (CR4_PCE) : "eax");
+ else if (usetsc)
+ /* Enable RDTSC instruction from user-level. */
+ asm volatile (".byte 0xf,0x20,0xe0 # movl %%cr4,%%eax\n"
+ "\tandl %0,%%eax\n"
+ "\t.byte 0xf,0x22,0xe0 # movl %%cr4,%%eax"
+ :: "i" (~CR4_TSD) : "eax");
+
+ if (usep6ctr)
+ printf ("pctr: Pentium Pro user-level performance counters enabled\n");
+ else if (usep5ctr)
+ printf ("pctr: Pentium performance counters enabled\n");
+ else if (usetsc)
+ printf ("pctr: Cycle counter enabled\n");
+ else
+ printf ("pctr: Performance counters not supported by CPU\n");
}
int
@@ -75,29 +78,23 @@ pctrclose (dev_t dev, int oflags, int devtype, struct proc *p)
}
static int
-pctrset (int fflag, int cmd, u_short fn)
+p5ctrsel (int fflag, u_int cmd, u_int fn)
{
pctrval msr11;
int msr;
int shift;
- switch (cmd) {
- case PCIOCS0:
- msr = 0x12;
- shift = 0;
- break;
- case PCIOCS1:
- msr = 0x13;
- shift = 16;
- break;
- default:
+ cmd -= PCIOCS0;
+ if (cmd > 1)
return EINVAL;
- }
+ msr = P5MSR_CTR0 + cmd;
+ shift = cmd ? 0x10 : 0;
if (! (fflag & FWRITE))
return EPERM;
if (fn >= 0x200)
return EINVAL;
+
msr11 = rdmsr (0x11);
msr11 &= ~(0x1ffLL << shift);
msr11 |= fn << shift;
@@ -107,29 +104,71 @@ pctrset (int fflag, int cmd, u_short fn)
return 0;
}
+static inline int
+p5ctrrd (struct pctrst *st)
+{
+ u_int msr11;
+
+ msr11 = rdmsr (P5MSR_CTRSEL);
+ st->pctr_fn[0] = msr11 & 0xffff;
+ st->pctr_fn[1] = msr11 >> 16;
+ __asm __volatile ("cli");
+ st->pctr_tsc = rdtsc ();
+ st->pctr_hwc[0] = rdmsr (P5MSR_CTR0);
+ st->pctr_hwc[1] = rdmsr (P5MSR_CTR1);
+ __asm __volatile ("sti");
+}
+
+static int
+p6ctrsel (int fflag, u_int cmd, u_int fn)
+{
+ int msrsel, msrval;
+
+ cmd -= PCIOCS0;
+ if (cmd > 1)
+ return EINVAL;
+ msrsel = P6MSR_CTRSEL0 + cmd;
+ msrval = P6MSR_CTR0 + cmd;
+
+ if (! (fflag & FWRITE))
+ return EPERM;
+ if (fn & 0x380000)
+ return EINVAL;
+
+ wrmsr (msrsel, fn);
+ wrmsr (msrval, 0LL);
+
+ return 0;
+}
+
+static inline int
+p6ctrrd (struct pctrst *st)
+{
+ st->pctr_fn[0] = rdmsr (P6MSR_CTRSEL0);
+ st->pctr_fn[1] = rdmsr (P6MSR_CTRSEL1);
+ __asm __volatile ("cli");
+ st->pctr_tsc = rdtsc ();
+ st->pctr_hwc[0] = rdmsr (P6MSR_CTR0);
+ st->pctr_hwc[1] = rdmsr (P6MSR_CTR1);
+ __asm __volatile ("sti");
+}
+
+
int
pctrioctl (dev_t dev, int cmd, caddr_t data, int fflag, struct proc *p)
{
switch (cmd) {
case PCIOCRD:
{
- u_int msr11;
- struct pctrst *st;
-
- st = (void *) data;
- if (cpuid == 1) {
- msr11 = rdmsr (0x11);
- st->pctr_fn[0] = msr11 & 0xffff;
- st->pctr_fn[1] = msr11 >> 16;
- __asm __volatile ("cli");
- st->pctr_tsc = rdtsc ();
- st->pctr_hwc[0] = rdmsr (0x12);
- st->pctr_hwc[1] = rdmsr (0x13);
- __asm __volatile ("sti");
- }
+ struct pctrst *st = (void *) data;
+
+ if (usep6ctr)
+ p6ctrrd (st);
+ else if (usep5ctr)
+ p5ctrrd (st);
else {
bzero (st, sizeof (*st));
- if (cpuid)
+ if (usetsc)
st->pctr_tsc = rdtsc ();
}
st->pctr_idl = pctr_idlcnt;
@@ -137,8 +176,10 @@ pctrioctl (dev_t dev, int cmd, caddr_t data, int fflag, struct proc *p)
}
case PCIOCS0:
case PCIOCS1:
- if (cpuid == 1)
- return pctrset (fflag, cmd, *(u_short *) data);
+ if (usep6ctr)
+ return p6ctrsel (fflag, cmd, *(u_int *) data);
+ if (usep5ctr)
+ return p5ctrsel (fflag, cmd, *(u_int *) data);
return EINVAL;
default:
return EINVAL;
diff --git a/sys/arch/i386/include/pctr.h b/sys/arch/i386/include/pctr.h
index 641b3436ca5..6b7011a8c27 100644
--- a/sys/arch/i386/include/pctr.h
+++ b/sys/arch/i386/include/pctr.h
@@ -1,8 +1,13 @@
-/* $OpenBSD: pctr.h,v 1.1 1996/08/08 18:47:04 dm Exp $ */
+/* $OpenBSD: pctr.h,v 1.2 1996/08/14 03:02:53 dm Exp $ */
/*
* Pentium performance counter driver for OpenBSD.
- * Author: David Mazieres <dm@lcs.mit.edu>
+ * Copyright 1996 David Mazieres <dm@lcs.mit.edu>.
+ *
+ * Modification and redistribution in source and binary forms is
+ * permitted provided that due credit is given to the author and the
+ * OpenBSD project (for instance by leaving this copyright notice
+ * intact).
*/
#ifndef _I386_PERFCNT_H_
@@ -13,22 +18,104 @@ typedef u_quad_t pctrval;
#define PCTR_NUM 2
struct pctrst {
- u_short pctr_fn[PCTR_NUM];
+ u_int pctr_fn[PCTR_NUM];
pctrval pctr_tsc;
pctrval pctr_hwc[PCTR_NUM];
pctrval pctr_idl;
};
/* Bit values in fn fields and PIOCS ioctl's */
-#define PCTR_K 0x40 /* Monitor kernel-level events */
-#define PCTR_U 0x80 /* Monitor user-level events */
-#define PCTR_C 0x100 /* count cycles rather than events */
+#define P5CTR_K 0x40 /* Monitor kernel-level events */
+#define P5CTR_U 0x80 /* Monitor user-level events */
+#define P5CTR_C 0x100 /* count cycles rather than events */
+
+#define P6CTR_U 0x010000 /* Monitor user-level events */
+#define P6CTR_K 0x020000 /* Monitor kernel-level events */
+#define P6CTR_E 0x040000 /* Edge detect */
+#define P6CTR_EN 0x400000 /* Enable counters (counter 0 only) */
+#define P6CTR_I 0x800000 /* Invert counter mask */
/* ioctl to set which counter a device tracks. */
#define PCIOCRD _IOR('c', 1, struct pctrst) /* Read counter value */
-#define PCIOCS0 _IOW('c', 8, unsigned short) /* Set counter 0 function */
-#define PCIOCS1 _IOW('c', 9, unsigned short) /* Set counter 1 function */
+#define PCIOCS0 _IOW('c', 8, unsigned int) /* Set counter 0 function */
+#define PCIOCS1 _IOW('c', 9, unsigned int) /* Set counter 1 function */
#define _PATH_PCTR "/dev/pctr"
+
+#define __cpuid() \
+({ \
+ pctrval id; \
+ __asm __volatile ("pushfl\n" \
+ "\tpopl %%eax\n" \
+ "\tmovl %%eax,%%ecx\n" \
+ "\txorl %1,%%eax\n" \
+ "\tpushl %%eax\n" \
+ "\tpopfl\n" \
+ "\tpushfl\n" \
+ "\tpopl %%eax\n" \
+ "\tpushl %%ecx\n" \
+ "\tpopfl\n" \
+ "\tcmpl %%eax,%%ecx\n" \
+ "\tmovl $0,%%eax\n" \
+ "\tje 1f\n" \
+ "\tcpuid\n" \
+ "\ttestl %%eax,%%eax\n" \
+ "\tje 1f\n" \
+ "\tmovl $1,%%eax\n" \
+ "\tcpuid\n" \
+ "1:\t" \
+ : "=A" (id) : "i" (PSL_ID) \
+ : "edx", "ecx", "ebx"); \
+ id; \
+})
+
+#define __hastsc(id) (((id) & 0x1000000000ULL) != 0ULL)
+#define __hasp5ctr(id) (((id) & 0xf00) == 0x500 \
+ && (((id) & 0xf0) == 0x10 \
+ || ((id) & 0xf0) == 0x20))
+#define __hasp6ctr(id) (((id) & 0xf00) == 0x600)
+
+#define __cpufamily() ((__cpuid() >> 8) & 0xf)
+
+#define rdtsc() \
+({ \
+ pctrval v; \
+ __asm __volatile (".byte 0xf, 0x31" : "=A" (v)); \
+ v; \
+})
+
+/* Read the performance counters (Pentium Pro only) */
+#define rdpmc(ctr) \
+({ \
+ pctrval v; \
+ __asm __volatile (".byte 0xf, 0x33" : "=A" (v) : "c" (ctr)); \
+ v; \
+})
+
+#ifdef _KERNEL
+
+#define CR4_TSD 0x040 /* Time stamp disable */
+#define CR4_PCE 0x100 /* Performance counter enable */
+
+#define MSR_TSC 0x10 /* MSR for TSC */
+#define P5MSR_CTRSEL 0x11 /* MSR for selecting both counters on P5 */
+#define P5MSR_CTR0 0x12 /* Value of Ctr0 on P5 */
+#define P5MSR_CTR1 0x13 /* Value of Ctr1 on P5 */
+#define P6MSR_CTRSEL0 0x186 /* MSR for programming CTR0 on P6 */
+#define P6MSR_CTRSEL1 0x187 /* MSR for programming CTR0 on P6 */
+#define P6MSR_CTR0 0xc1 /* Ctr0 on P6 */
+#define P6MSR_CTR1 0xc2 /* Ctr1 on P6 */
+
+#define rdmsr(msr) \
+({ \
+ pctrval v; \
+ __asm __volatile (".byte 0xf, 0x32" : "=A" (v) : "c" (msr)); \
+ v; \
+})
+
+#define wrmsr(msr, v) \
+ __asm __volatile (".byte 0xf, 0x30" :: "A" (v), "c" (msr));
+
+#endif /* _KERNEL */
#endif /* ! _I386_PERFCNT_H_ */
diff --git a/usr.bin/pctrctl/pctrctl.c b/usr.bin/pctrctl/pctrctl.c
index c69b6ac6a80..6e43fbe9055 100644
--- a/usr.bin/pctrctl/pctrctl.c
+++ b/usr.bin/pctrctl/pctrctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pctrctl.c,v 1.1 1996/08/08 18:47:03 dm Exp $ */
+/* $OpenBSD: pctrctl.c,v 1.2 1996/08/14 03:02:52 dm Exp $ */
/*
* Pentium performance counter driver for OpenBSD.
* Author: David Mazieres <dm@lcs.mit.edu>
@@ -11,6 +11,7 @@
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
+#include <machine/cpu.h>
#include <machine/pctr.h>
char *progname;
@@ -93,9 +94,9 @@ readst (void)
for (i = 0; i < PCTR_NUM; i++)
printf (" ctr%d = %16qd [%c%c%c %02x (%s)]\n", i, st.pctr_hwc[i],
- (st.pctr_fn[i] & PCTR_C) ? 'c' : 'e',
- (st.pctr_fn[i] & PCTR_U) ? 'u' : '-',
- (st.pctr_fn[i] & PCTR_K) ? 'k' : '-',
+ (st.pctr_fn[i] & P5CTR_C) ? 'c' : 'e',
+ (st.pctr_fn[i] & P5CTR_U) ? 'u' : '-',
+ (st.pctr_fn[i] & P5CTR_K) ? 'k' : '-',
(st.pctr_fn[i] & 0x3f),
(((st.pctr_fn[i] & 0x3f) < pctr_name_size
&& pctr_name[st.pctr_fn[i] & 0x3f])
@@ -104,7 +105,7 @@ readst (void)
}
static void
-setctr (int ctr, u_short val)
+setctr (int ctr, u_int val)
{
int fd;
@@ -144,6 +145,12 @@ main (int argc, char **argv)
u_int ctr;
char *cp;
u_short fn;
+ pctrval id = __cpuid ();
+
+ if (__hasp6ctr (id)) {
+ fprintf (stderr, "Pentium Pro not supported\n");
+ exit (1);
+ }
if (progname = strrchr (argv[0], '/'))
progname++;
@@ -167,16 +174,16 @@ main (int argc, char **argv)
for (cp = argv[3]; *cp; cp++) {
switch (*cp) {
case 'c':
- fn |= PCTR_C;
+ fn |= P5CTR_C;
break;
case 'e':
- fn &= ~PCTR_C;
+ fn &= ~P5CTR_C;
break;
case 'k':
- fn |= PCTR_K;
+ fn |= P5CTR_K;
break;
case 'u':
- fn |= PCTR_U;
+ fn |= P5CTR_U;
break;
default:
usage ();
@@ -187,7 +194,7 @@ main (int argc, char **argv)
fn = strtoul (argv[3], NULL, 16);
if (fn & ~0x3f)
usage ();
- fn |= PCTR_K | PCTR_U;
+ fn |= P5CTR_K | P5CTR_U;
}
setctr (ctr, fn);
}