1/* $OpenBSD: pctr.c,v 1.10 2024/04/03 02:01:21 guenther Exp $ */ 2 3/* 4 * Copyright (c) 2007 Mike Belopuhov 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19/* 20 * Pentium performance counter driver for OpenBSD. 21 * Copyright 1996 David Mazieres <dm@lcs.mit.edu>. 22 * 23 * Modification and redistribution in source and binary forms is 24 * permitted provided that due credit is given to the author and the 25 * OpenBSD project by leaving this copyright notice intact. 26 */ 27 28#include <sys/param.h> 29#include <sys/errno.h> 30#include <sys/fcntl.h> 31#include <sys/ioccom.h> 32#include <sys/mutex.h> 33#include <sys/systm.h> 34 35#include <machine/intr.h> 36#include <machine/pctr.h> 37#include <machine/cpu.h> 38#include <machine/specialreg.h> 39 40#define PCTR_AMD_NUM PCTR_NUM 41#define PCTR_INTEL_NUM 2 /* Intel supports only 2 counters */ 42#define PCTR_INTEL_VERSION_MASK 0xff 43 44#define usetsc (cpu_feature & CPUID_TSC) 45#define usepctr ((pctr_isamd && ((cpu_id >> 8) & 15) >= 6) || \ 46 (pctr_isintel && \ 47 (pctr_intel_cap & PCTR_INTEL_VERSION_MASK) >= 1)) 48 49int pctr_isamd; 50int pctr_isintel; 51uint32_t pctr_intel_cap; 52 53struct mutex pctr_conf_lock = MUTEX_INITIALIZER(IPL_HIGH); 54uint32_t pctr_fn[PCTR_NUM]; 55 56static void pctrrd(struct pctrst *); 57static int pctrsel(int, uint32_t, uint32_t); 58static void pctr_enable(struct cpu_info *); 59 60static void 61pctrrd(struct pctrst *st) 62{ 63 int i, num, reg; 64 65 num = pctr_isamd ? PCTR_AMD_NUM : PCTR_INTEL_NUM; 66 reg = pctr_isamd ? MSR_K7_EVNTSEL0 : MSR_EVNTSEL0; 67 for (i = 0; i < num; i++) 68 st->pctr_fn[i] = rdmsr(reg + i); 69 __asm volatile("cli"); 70 st->pctr_tsc = rdtsc(); 71 for (i = 0; i < num; i++) 72 st->pctr_hwc[i] = rdpmc(i); 73 __asm volatile("sti"); 74} 75 76void 77pctrattach(int num) 78{ 79 struct cpu_info *ci = &cpu_info_primary; 80 uint32_t dummy; 81 82 if (num > 1) 83 return; 84 85 pctr_isamd = (ci->ci_vendor == CPUV_AMD); 86 if (!pctr_isamd) { 87 pctr_isintel = (ci->ci_vendor == CPUV_INTEL); 88 CPUID(0xa, pctr_intel_cap, dummy, dummy, dummy); 89 } 90} 91 92void 93pctr_enable(struct cpu_info *ci) 94{ 95 if (usepctr) { 96 /* Enable RDTSC and RDPMC instructions from user-level. */ 97 __asm volatile("movq %%cr4,%%rax\n" 98 "\tandq %0,%%rax\n" 99 "\torq %1,%%rax\n" 100 "\tmovq %%rax,%%cr4" 101 :: "i" (~CR4_TSD), "i" (CR4_PCE) : "rax"); 102 } else if (usetsc) { 103 /* Enable RDTSC instruction from user-level. */ 104 __asm volatile("movq %%cr4,%%rax\n" 105 "\tandq %0,%%rax\n" 106 "\tmovq %%rax,%%cr4" 107 :: "i" (~CR4_TSD) : "rax"); 108 } 109} 110 111int 112pctropen(dev_t dev, int oflags, int devtype, struct proc *p) 113{ 114 115 if (minor(dev)) 116 return (ENXIO); 117 return (0); 118} 119 120int 121pctrclose(dev_t dev, int oflags, int devtype, struct proc *p) 122{ 123 124 return (0); 125} 126 127static int 128pctrsel(int fflag, uint32_t cmd, uint32_t fn) 129{ 130 int msrsel, msrval, changed; 131 132 cmd -= PCIOCS0; 133 if (pctr_isamd) { 134 if (cmd > PCTR_AMD_NUM-1) 135 return (EINVAL); 136 msrsel = MSR_K7_EVNTSEL0 + cmd; 137 msrval = MSR_K7_PERFCTR0 + cmd; 138 } else { 139 if (cmd > PCTR_INTEL_NUM-1) 140 return (EINVAL); 141 msrsel = MSR_EVNTSEL0 + cmd; 142 msrval = MSR_PERFCTR0 + cmd; 143 } 144 145 if (!(fflag & FWRITE)) 146 return (EPERM); 147 if (fn & 0x380000) 148 return (EINVAL); 149 150 if (fn != 0) 151 pctr_enable(curcpu()); 152 153 mtx_enter(&pctr_conf_lock); 154 changed = fn != pctr_fn[cmd]; 155 if (changed) { 156 pctr_fn[cmd] = fn; 157 wrmsr(msrval, 0); 158 wrmsr(msrsel, fn); 159 wrmsr(msrval, 0); 160 } 161 mtx_leave(&pctr_conf_lock); 162#ifdef MULTIPROCESSOR 163 if (changed) 164 x86_broadcast_ipi(X86_IPI_PCTR); 165#endif 166 167 return (0); 168} 169 170int 171pctrioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p) 172{ 173 switch (cmd) { 174 case PCIOCRD: 175 { 176 struct pctrst *st = (struct pctrst *)data; 177 178 if (usepctr) 179 pctrrd(st); 180 else if (usetsc) 181 st->pctr_tsc = rdtsc(); 182 return (0); 183 } 184 case PCIOCS0: 185 case PCIOCS1: 186 case PCIOCS2: 187 case PCIOCS3: 188 if (usepctr) 189 return (pctrsel(fflag, cmd, *(u_int *)data)); 190 return (ENODEV); 191 default: 192 return (EINVAL); 193 } 194} 195 196void 197pctr_reload(struct cpu_info *ci) 198{ 199 int num, i, msrsel, msrval, anyset; 200 uint32_t fn; 201 202 if (pctr_isamd) { 203 num = PCTR_AMD_NUM; 204 msrsel = MSR_K7_EVNTSEL0; 205 msrval = MSR_K7_PERFCTR0; 206 } else { 207 num = PCTR_INTEL_NUM; 208 msrsel = MSR_EVNTSEL0; 209 msrval = MSR_PERFCTR0; 210 } 211 212 anyset = 0; 213 mtx_enter(&pctr_conf_lock); 214 for (i = 0; i < num; i++) { 215 /* only update the ones that don't match */ 216 /* XXX generation numbers for zeroing? */ 217 fn = rdmsr(msrsel + i); 218 if (fn != pctr_fn[i]) { 219 wrmsr(msrval + i, 0); 220 wrmsr(msrsel + i, pctr_fn[i]); 221 wrmsr(msrval + i, 0); 222 } 223 if (fn) 224 anyset = 1; 225 } 226 mtx_leave(&pctr_conf_lock); 227 228 if (! anyset) 229 pctr_enable(curcpu()); 230} 231 232void 233pctr_resume(struct cpu_info *ci) 234{ 235 if (usepctr) 236 pctr_reload(ci); 237} 238