diff options
-rw-r--r-- | sys/arch/i386/i386/pctr.c | 173 | ||||
-rw-r--r-- | sys/arch/i386/include/pctr.h | 103 | ||||
-rw-r--r-- | usr.bin/pctrctl/pctrctl.c | 27 |
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); } |