From 90a2e46ab1afa72991f56cb9c21c6826458f7f00 Mon Sep 17 00:00:00 2001 From: thorpej Date: Wed, 13 Sep 2000 04:47:00 +0000 Subject: [PATCH] Add support for using the 586- and 686-class performance counters. --- sys/arch/i386/conf/files.i386 | 7 +- sys/arch/i386/i386/pmc.c | 223 +++++++++++++++++++++++++++++++ sys/arch/i386/i386/sys_machdep.c | 21 ++- sys/arch/i386/include/pmc.h | 43 ++++++ sys/arch/i386/include/sysarch.h | 41 +++++- 5 files changed, 332 insertions(+), 3 deletions(-) create mode 100644 sys/arch/i386/i386/pmc.c create mode 100644 sys/arch/i386/include/pmc.h diff --git a/sys/arch/i386/conf/files.i386 b/sys/arch/i386/conf/files.i386 index 860d5516717c..20c8230ddf3f 100644 --- a/sys/arch/i386/conf/files.i386 +++ b/sys/arch/i386/conf/files.i386 @@ -1,4 +1,4 @@ -# $NetBSD: files.i386,v 1.166 2000/09/07 17:20:58 thorpej Exp $ +# $NetBSD: files.i386,v 1.167 2000/09/13 04:47:00 thorpej Exp $ # # new style config file for i386 architecture # @@ -31,6 +31,9 @@ defopt USER_LDT # Speed hack; make NOPs dummies (might break on some machines) defopt DUMMY_NOPS +# Performance counter support +defopt PERFCTRS + # X server support in console drivers defopt opt_xserver.h XSERVER XSERVER_DDB @@ -73,6 +76,8 @@ file arch/i386/i386/trap.c file arch/i386/i386/vm_machdep.c file dev/cons.c +file arch/i386/i386/pmc.c perfctrs + # # Machine-independent SCSI drivers # diff --git a/sys/arch/i386/i386/pmc.c b/sys/arch/i386/i386/pmc.c new file mode 100644 index 000000000000..547000c72050 --- /dev/null +++ b/sys/arch/i386/i386/pmc.c @@ -0,0 +1,223 @@ +/* $NetBSD: pmc.c,v 1.1 2000/09/13 04:47:01 thorpej Exp $ */ + +/*- + * Copyright (c) 2000 Zembu Labs, Inc. + * All rights reserved. + * + * Author: Jason R. Thorpe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Zembu Labs, Inc. + * 4. Neither the name of Zembu Labs nor the names of its employees may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ZEMBU LABS, INC. ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAR- + * RANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DIS- + * CLAIMED. IN NO EVENT SHALL ZEMBU LABS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Interface to x86 CPU Performance Counters. + */ + +#include +#include +#include + +#include +#include +#include +#include + +static int pmc_initialized; +static int pmc_type; +static int pmc_flags; + +static struct pmc_state { + u_int64_t pmcs_val; + u_int64_t pmcs_tsc; + u_int64_t pmcs_control; + u_int32_t pmcs_ctrmsr; +} pmc_state[PMC_NCOUNTERS]; + +static int pmc_running; + +static void +pmc_init(void) +{ + + if (pmc_initialized) + return; + + switch (cpu_class) { + case CPUCLASS_586: + pmc_type = PMC_TYPE_I586; + pmc_state[0].pmcs_ctrmsr = MSR_CTR0; + pmc_state[1].pmcs_ctrmsr = MSR_CTR1; + break; + + case CPUCLASS_686: + pmc_type = PMC_TYPE_I686; + pmc_state[0].pmcs_ctrmsr = MSR_PERFCTR0; + pmc_state[1].pmcs_ctrmsr = MSR_PERFCTR1; + break; + + default: + pmc_type = PMC_TYPE_NONE; + } + + if (pmc_type != PMC_TYPE_NONE && (cpu_feature & CPUID_TSC) != 0) + pmc_flags |= PMC_INFO_HASTSC; + + pmc_initialized = 1; +} + +int +pmc_info(struct proc *p, struct i386_pmc_info_args *uargs, register_t *retval) +{ + struct i386_pmc_info_args rv; + + memset(&rv, 0, sizeof(rv)); + + if (pmc_initialized == 0) + pmc_init(); + + rv.type = pmc_type; + rv.flags = pmc_flags; + + return (copyout(&rv, uargs, sizeof(rv))); +} + +int +pmc_startstop(struct proc *p, struct i386_pmc_startstop_args *uargs, + register_t *retval) +{ + struct i386_pmc_startstop_args args; + int error, mask, start; + + if (pmc_initialized == 0) + pmc_init(); + if (pmc_type == PMC_TYPE_NONE) + return (ENODEV); + + error = copyin(uargs, &args, sizeof(args)); + if (error) + return (error); + + if (args.counter < 0 || args.counter >= PMC_NCOUNTERS) + return (EINVAL); + + mask = 1 << args.counter; + start = (args.flags & (PMC_SETUP_KERNEL|PMC_SETUP_USER)) != 0; + + if ((pmc_running & mask) != 0 && start != 0) + return (EBUSY); + else if ((pmc_running & mask) == 0 && start == 0) + return (0); + + if (start) { + pmc_running |= mask; + pmc_state[args.counter].pmcs_val = args.val; + switch (pmc_type) { + case PMC_TYPE_I586: + pmc_state[args.counter].pmcs_control = args.event | + ((args.flags & PMC_SETUP_KERNEL) ? + PMC5_CESR_OS : 0) | + ((args.flags & PMC_SETUP_USER) ? + PMC5_CESR_USR : 0) | + ((args.flags & PMC_SETUP_EDGE) ? + PMC5_CESR_E : 0); + break; + + case PMC_TYPE_I686: + pmc_state[args.counter].pmcs_control = args.event | + (args.unit << PMC6_EVTSEL_UNIT_SHIFT) | + ((args.flags & PMC_SETUP_KERNEL) ? + PMC6_EVTSEL_OS : 0) | + ((args.flags & PMC_SETUP_USER) ? + PMC6_EVTSEL_USR : 0) | + ((args.flags & PMC_SETUP_EDGE) ? + PMC6_EVTSEL_E : 0) | + ((args.flags & PMC_SETUP_INV) ? + PMC6_EVTSEL_INV : 0) | + (args.compare << PMC6_EVTSEL_COUNTER_MASK_SHIFT); + break; + } + disable_intr(); + wrmsr(pmc_state[args.counter].pmcs_ctrmsr, + pmc_state[args.counter].pmcs_val); + enable_intr(); + } else { + pmc_running &= ~mask; + pmc_state[args.counter].pmcs_control = 0; + } + + switch (pmc_type) { + case PMC_TYPE_I586: + disable_intr(); + wrmsr(MSR_CESR, pmc_state[0].pmcs_control | + (pmc_state[1].pmcs_control << 16)); + enable_intr(); + break; + + case PMC_TYPE_I686: + disable_intr(); + if (args.counter == 1) + wrmsr(MSR_EVNTSEL1, pmc_state[1].pmcs_control); + wrmsr(MSR_EVNTSEL0, pmc_state[0].pmcs_control | + (pmc_running ? PMC6_EVTSEL_EN : 0)); + enable_intr(); + break; + } + + return (0); +} + +int +pmc_read(struct proc *p, struct i386_pmc_read_args *uargs, register_t *retval) +{ + struct i386_pmc_read_args args; + int error; + + if (pmc_initialized == 0) + pmc_init(); + if (pmc_type == PMC_TYPE_NONE) + return (ENODEV); + + error = copyin(uargs, &args, sizeof(args)); + if (error) + return (error); + + if (args.counter < 0 || args.counter >= PMC_NCOUNTERS) + return (EINVAL); + + if (pmc_running & (1 << args.counter)) { + pmc_state[args.counter].pmcs_val = + rdmsr(pmc_state[args.counter].pmcs_ctrmsr) & + 0xffffffffffULL; + if (pmc_flags & PMC_INFO_HASTSC) + pmc_state[args.counter].pmcs_tsc = rdtsc(); + } + + args.val = pmc_state[args.counter].pmcs_val; + args.time = pmc_state[args.counter].pmcs_tsc; + + return (copyout(&args, uargs, sizeof(args))); +} diff --git a/sys/arch/i386/i386/sys_machdep.c b/sys/arch/i386/i386/sys_machdep.c index c13fe3c8e787..dabc3974d8c3 100644 --- a/sys/arch/i386/i386/sys_machdep.c +++ b/sys/arch/i386/i386/sys_machdep.c @@ -1,4 +1,4 @@ -/* $NetBSD: sys_machdep.c,v 1.50 2000/06/29 08:44:54 mrg Exp $ */ +/* $NetBSD: sys_machdep.c,v 1.51 2000/09/13 04:47:01 thorpej Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -76,6 +76,7 @@ #include "opt_vm86.h" #include "opt_user_ldt.h" +#include "opt_perfctrs.h" #include #include @@ -106,6 +107,10 @@ #include #endif +#ifdef PERFCTRS +#include +#endif + extern vm_map_t kernel_map; #ifdef USER_LDT @@ -476,6 +481,20 @@ sys_sysarch(p, v, retval) break; #endif +#ifdef PERFCTRS + case I386_PMC_INFO: + error = pmc_info(p, SCARG(uap, parms), retval); + break; + + case I386_PMC_STARTSTOP: + error = pmc_startstop(p, SCARG(uap, parms), retval); + break; + + case I386_PMC_READ: + error = pmc_read(p, SCARG(uap, parms), retval); + break; +#endif + default: error = EINVAL; break; diff --git a/sys/arch/i386/include/pmc.h b/sys/arch/i386/include/pmc.h new file mode 100644 index 000000000000..dbc8f0db6e13 --- /dev/null +++ b/sys/arch/i386/include/pmc.h @@ -0,0 +1,43 @@ +/* $NetBSD: pmc.h,v 1.1 2000/09/13 04:47:01 thorpej Exp $ */ + +/*- + * Copyright (c) 2000 Zembu Labs, Inc. + * All rights reserved. + * + * Author: Jason R. Thorpe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Zembu Labs, Inc. + * 4. Neither the name of Zembu Labs nor the names of its employees may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ZEMBU LABS, INC. ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAR- + * RANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DIS- + * CLAIMED. IN NO EVENT SHALL ZEMBU LABS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef _KERNEL +int pmc_info(struct proc *, struct i386_pmc_info_args *, + register_t *); +int pmc_startstop(struct proc *, struct i386_pmc_startstop_args *, + register_t *); +int pmc_read(struct proc *, struct i386_pmc_read_args *, + register_t *); +#endif diff --git a/sys/arch/i386/include/sysarch.h b/sys/arch/i386/include/sysarch.h index a443196c6f06..c843d47cf4da 100644 --- a/sys/arch/i386/include/sysarch.h +++ b/sys/arch/i386/include/sysarch.h @@ -1,4 +1,4 @@ -/* $NetBSD: sysarch.h,v 1.9 1998/02/25 21:27:05 perry Exp $ */ +/* $NetBSD: sysarch.h,v 1.10 2000/09/13 04:47:01 thorpej Exp $ */ #ifndef _I386_SYSARCH_H_ #define _I386_SYSARCH_H_ @@ -12,6 +12,9 @@ #define I386_GET_IOPERM 3 #define I386_SET_IOPERM 4 #define I386_VM86 5 +#define I386_PMC_INFO 8 +#define I386_PMC_STARTSTOP 9 +#define I386_PMC_READ 10 struct i386_get_ldt_args { int start; @@ -37,12 +40,48 @@ struct i386_set_ioperm_args { u_long *iomap; }; +struct i386_pmc_info_args { + int type; + int flags; +}; + +#define PMC_TYPE_NONE 0 +#define PMC_TYPE_I586 1 +#define PMC_TYPE_I686 2 + +#define PMC_INFO_HASTSC 0x01 + +#define PMC_NCOUNTERS 2 + +struct i386_pmc_startstop_args { + int counter; + u_int64_t val; + u_int8_t event; + u_int8_t unit; + u_int8_t compare; + u_int8_t flags; +}; + +#define PMC_SETUP_KERNEL 0x01 +#define PMC_SETUP_USER 0x02 +#define PMC_SETUP_EDGE 0x04 +#define PMC_SETUP_INV 0x08 + +struct i386_pmc_read_args { + int counter; + u_int64_t val; + u_int64_t time; +}; + #ifndef _KERNEL int i386_get_ldt __P((int, union descriptor *, int)); int i386_set_ldt __P((int, union descriptor *, int)); int i386_iopl __P((int)); int i386_get_ioperm __P((u_long *)); int i386_set_ioperm __P((u_long *)); +int i386_pmc_info __P((struct i386_pmc_info_args *)); +int i386_pmc_startstop __P((struct i386_pmc_startstop_args *)); +int i386_pmc_read __P((struct i386_pmc_read_args *)); int sysarch __P((int, void *)); #endif