From 650e27cd2590422830e634833ac572be873fb975 Mon Sep 17 00:00:00 2001 From: Garrett Wollman Date: Wed, 27 Mar 1996 22:02:18 +0000 Subject: [PATCH] A slightly-closer-to-working version that includes code appropriate to regular Pentiums. Unfortunately, it doesn't work on mine, but I'm not sure if this is the fault of the driver. --- sys/i386/i386/perfmon.c | 124 +++++++++++++++++++++++++++++----------- 1 file changed, 91 insertions(+), 33 deletions(-) diff --git a/sys/i386/i386/perfmon.c b/sys/i386/i386/perfmon.c index 290d339a22bb..df231cd7ee8b 100644 --- a/sys/i386/i386/perfmon.c +++ b/sys/i386/i386/perfmon.c @@ -26,7 +26,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id$ + * $Id: perfmon.c,v 1.1 1996/03/26 19:57:53 wollman Exp $ */ #include @@ -43,6 +43,11 @@ static int perfmon_inuse; static int perfmon_cpuok; static int msr_ctl[NPMC]; static int msr_pmc[NPMC]; +static unsigned int ctl_shadow[NPMC]; +static quad_t pmc_shadow[NPMC]; /* used when ctr is stopped on P5 */ +static int (*writectl)(int); +static int writectl5(int); +static int writectl6(int); /* * Must be called after cpu_class is set up. @@ -51,13 +56,21 @@ void perfmon_init(void) { switch(cpu_class) { - case CPUCLASS_586: /* assume it's the same for now */ + case CPUCLASS_586: + perfmon_cpuok = 1; + msr_ctl[0] = 0x11; + msr_ctl[1] = 0x11; + msr_pmc[0] = 0x12; + msr_pmc[1] = 0x13; + writectl = writectl5; + break; case CPUCLASS_686: perfmon_cpuok = 1; msr_ctl[0] = 0x186; msr_ctl[1] = 0x187; msr_pmc[0] = 0xc1; msr_pmc[1] = 0xc2; + writectl = writectl6; break; default: @@ -80,8 +93,11 @@ perfmon_setup(int pmc, unsigned int control) perfmon_inuse |= (1 << pmc); control &= ~(PMCF_SYS_FLAGS << 16); - wrmsr(msr_ctl[pmc], control); - wrmsr(msr_pmc[pmc], 0); + disable_intr(); + ctl_shadow[pmc] = control; + writectl(pmc); + wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0); + enable_intr(); return 0; } @@ -92,7 +108,7 @@ perfmon_get(int pmc, unsigned int *control) return EINVAL; if (perfmon_inuse & (1 << pmc)) { - *control = rdmsr(msr_ctl[pmc]); + *control = ctl_shadow[pmc]; return 0; } return EBUSY; /* XXX reversed sense */ @@ -106,6 +122,7 @@ perfmon_fini(int pmc) if (perfmon_inuse & (1 << pmc)) { perfmon_stop(pmc); + ctl_shadow[pmc] = 0; perfmon_inuse &= ~(1 << pmc); return 0; } @@ -115,48 +132,34 @@ perfmon_fini(int pmc) int perfmon_start(int pmc) { - /* - * XXX - Current Intel design does not allow counters to be enabled - * independently. - */ - if (pmc != PMC_ALL) + if (pmc < 0 || pmc >= NPMC) return EINVAL; -#if 0 - if (perfmon_inuse & (1 << pmc)) { - wrmsr(msr_ctl[pmc], rdmsr(msr_ctl[pmc]) | (PMCF_EN << 16)); - return 0; - } -#else if (perfmon_inuse) { - wrmsr(msr_ctl[0], rdmsr(msr_ctl[0]) | (PMCF_EN << 16)); + disable_intr(); + ctl_shadow[pmc] |= (PMCF_EN << 16); + wrmsr(msr_pmc[pmc], pmc_shadow[pmc]); + writectl(pmc); + enable_intr(); return 0; } -#endif return EBUSY; } int perfmon_stop(int pmc) { - /* - * XXX - Current Intel design does not allow counters to be enabled - * independently. - */ - if (pmc != PMC_ALL) + if (pmc < 0 || pmc >= NPMC) return EINVAL; -#if 0 if (perfmon_inuse & (1 << pmc)) { - wrmsr(msr_ctl[pmc], rdmsr(msr_ctl[pmc]) & ~(PMCF_EN << 16)); + disable_intr(); + pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]); + ctl_shadow[pmc] &= ~(PMCF_EN << 16); + writectl(pmc); + enable_intr(); return 0; } -#else - if (perfmon_inuse) { - wrmsr(msr_ctl[0], rdmsr(msr_ctl[0]) & ~(PMCF_EN << 16)); - return 0; - } -#endif return EBUSY; } @@ -167,7 +170,10 @@ perfmon_read(int pmc, quad_t *val) return EINVAL; if (perfmon_inuse & (1 << pmc)) { - *val = rdmsr(msr_pmc[pmc]); + if (ctl_shadow[pmc] & (PMCF_EN << 16)) + *val = rdmsr(msr_pmc[pmc]); + else + *val = pmc_shadow[pmc]; return 0; } @@ -181,12 +187,64 @@ perfmon_reset(int pmc) return EINVAL; if (perfmon_inuse & (1 << pmc)) { - wrmsr(msr_pmc[pmc], 0); + wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0); return 0; } return EBUSY; } +/* + * Unfortunately, the performance-monitoring registers are laid out + * differently in the P5 and P6. We keep everything in P6 format + * internally (except for the event code), and convert to P5 + * format as needed on those CPUs. The writectl function pointer + * is set up to point to one of these functions by perfmon_init(). + */ +int +writectl6(int pmc) +{ + if (pmc > 0 && !(ctl_shadow[pmc] & (PMCF_EN << 16))) { + wrmsr(msr_ctl[pmc], 0); + } else { + wrmsr(msr_ctl[pmc], ctl_shadow[pmc]); + } + return 0; +} + +#define P5FLAG_P 0x200 +#define P5FLAG_E 0x100 +#define P5FLAG_USR 0x80 +#define P5FLAG_OS 0x40 + +int +writectl5(int pmc) +{ + quad_t newval = 0; + + if (ctl_shadow[1] & (PMCF_EN << 16)) { + if (ctl_shadow[1] & (PMCF_USR << 16)) + newval |= P5FLAG_USR << 16; + if (ctl_shadow[1] & (PMCF_OS << 16)) + newval |= P5FLAG_OS << 16; + if (ctl_shadow[1] & (PMCF_E << 16)) + newval |= P5FLAG_E << 16; + newval |= (ctl_shadow[1] & 0x3f) << 16; + } + if (ctl_shadow[0] & (PMCF_EN << 16)) { + if (ctl_shadow[1] & (PMCF_USR << 16)) + newval |= P5FLAG_USR; + if (ctl_shadow[1] & (PMCF_OS << 16)) + newval |= P5FLAG_OS; + if (ctl_shadow[1] & (PMCF_E << 16)) + newval |= P5FLAG_E; + newval |= ctl_shadow[1] & 0x3f; + } + printf("about to wrmsr(%x, %x)\n", msr_ctl[0], (unsigned)newval); + printf("old value is %x\n", (unsigned)rdmsr(msr_ctl[0])); + wrmsr(msr_ctl[0], newval); + return 0; /* XXX should check for errors */ +} + /* * Now the user-mode interface, called from a subdevice of mem.c. */