1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2008 Joseph Koshy
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/pmc.h>
34 #include <sys/pmckern.h>
35 #include <sys/systm.h>
36
37 #include <machine/specialreg.h>
38
39 /*
40 * TSC support.
41 */
42
43 #define TSC_CAPS PMC_CAP_READ
44
45 struct tsc_descr {
46 struct pmc_descr pm_descr; /* "base class" */
47 };
48
49 static struct tsc_descr tsc_pmcdesc[TSC_NPMCS] =
50 {
51 {
52 .pm_descr =
53 {
54 .pd_name = "TSC",
55 .pd_class = PMC_CLASS_TSC,
56 .pd_caps = TSC_CAPS,
57 .pd_width = 64
58 }
59 }
60 };
61
62 /*
63 * Per-CPU data structure for TSCs.
64 */
65
66 struct tsc_cpu {
67 struct pmc_hw tc_hw;
68 };
69
70 static struct tsc_cpu **tsc_pcpu;
71
72 static int
73 tsc_allocate_pmc(int cpu, int ri, struct pmc *pm,
74 const struct pmc_op_pmcallocate *a)
75 {
76 (void) cpu;
77
78 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
79 ("[tsc,%d] illegal CPU value %d", __LINE__, cpu));
80 KASSERT(ri >= 0 && ri < TSC_NPMCS,
81 ("[tsc,%d] illegal row index %d", __LINE__, ri));
82
83 if (a->pm_class != PMC_CLASS_TSC)
84 return (EINVAL);
85
86 if (a->pm_ev != PMC_EV_TSC_TSC ||
87 a->pm_mode != PMC_MODE_SC)
88 return (EINVAL);
89
90 return (0);
91 }
92
93 static int
94 tsc_config_pmc(int cpu, int ri, struct pmc *pm)
95 {
96 struct pmc_hw *phw;
97
98 PMCDBG3(MDP,CFG,1, "cpu=%d ri=%d pm=%p", cpu, ri, pm);
99
100 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
101 ("[tsc,%d] illegal CPU value %d", __LINE__, cpu));
102 KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri));
103
104 phw = &tsc_pcpu[cpu]->tc_hw;
105
106 KASSERT(pm == NULL || phw->phw_pmc == NULL,
107 ("[tsc,%d] pm=%p phw->pm=%p hwpmc not unconfigured", __LINE__,
108 pm, phw->phw_pmc));
109
110 phw->phw_pmc = pm;
111
112 return (0);
113 }
114
115 static int
116 tsc_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc)
117 {
118 int error;
119 size_t copied;
120 const struct tsc_descr *pd;
121 struct pmc_hw *phw;
122
123 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
124 ("[tsc,%d] illegal CPU %d", __LINE__, cpu));
125 KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri));
126
127 phw = &tsc_pcpu[cpu]->tc_hw;
128 pd = &tsc_pmcdesc[ri];
129
130 if ((error = copystr(pd->pm_descr.pd_name, pi->pm_name,
131 PMC_NAME_MAX, &copied)) != 0)
132 return (error);
133
134 pi->pm_class = pd->pm_descr.pd_class;
135
136 if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) {
137 pi->pm_enabled = TRUE;
138 *ppmc = phw->phw_pmc;
139 } else {
140 pi->pm_enabled = FALSE;
141 *ppmc = NULL;
142 }
143
144 return (0);
145 }
146
147 static int
148 tsc_get_config(int cpu, int ri, struct pmc **ppm)
149 {
150 (void) ri;
151
152 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
153 ("[tsc,%d] illegal CPU %d", __LINE__, cpu));
154 KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri));
155
156 *ppm = tsc_pcpu[cpu]->tc_hw.phw_pmc;
157
158 return (0);
159 }
160
161 static int
162 tsc_get_msr(int ri, uint32_t *msr)
163 {
164 (void) ri;
165
166 KASSERT(ri >= 0 && ri < TSC_NPMCS,
167 ("[tsc,%d] ri %d out of range", __LINE__, ri));
168
169 *msr = MSR_TSC;
170
171 return (0);
172 }
173
174 static int
175 tsc_pcpu_fini(struct pmc_mdep *md, int cpu)
176 {
177 int ri;
178 struct pmc_cpu *pc;
179
180 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
181 ("[tsc,%d] illegal cpu %d", __LINE__, cpu));
182 KASSERT(tsc_pcpu[cpu] != NULL, ("[tsc,%d] null pcpu", __LINE__));
183
184 free(tsc_pcpu[cpu], M_PMC);
185 tsc_pcpu[cpu] = NULL;
186
187 ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_TSC].pcd_ri;
188
189 pc = pmc_pcpu[cpu];
190 pc->pc_hwpmcs[ri] = NULL;
191
192 return (0);
193 }
194
195 static int
196 tsc_pcpu_init(struct pmc_mdep *md, int cpu)
197 {
198 int ri;
199 struct pmc_cpu *pc;
200 struct tsc_cpu *tsc_pc;
201
202
203 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
204 ("[tsc,%d] illegal cpu %d", __LINE__, cpu));
205 KASSERT(tsc_pcpu, ("[tsc,%d] null pcpu", __LINE__));
206 KASSERT(tsc_pcpu[cpu] == NULL, ("[tsc,%d] non-null per-cpu",
207 __LINE__));
208
209 tsc_pc = malloc(sizeof(struct tsc_cpu), M_PMC, M_WAITOK|M_ZERO);
210
211 tsc_pc->tc_hw.phw_state = PMC_PHW_FLAG_IS_ENABLED |
212 PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(0) |
213 PMC_PHW_FLAG_IS_SHAREABLE;
214
215 tsc_pcpu[cpu] = tsc_pc;
216
217 ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_TSC].pcd_ri;
218
219 KASSERT(pmc_pcpu, ("[tsc,%d] null generic pcpu", __LINE__));
220
221 pc = pmc_pcpu[cpu];
222
223 KASSERT(pc, ("[tsc,%d] null generic per-cpu", __LINE__));
224
225 pc->pc_hwpmcs[ri] = &tsc_pc->tc_hw;
226
227 return (0);
228 }
229
230 static int
231 tsc_read_pmc(int cpu, int ri, pmc_value_t *v)
232 {
233 struct pmc *pm;
234 enum pmc_mode mode __diagused;
235 const struct pmc_hw *phw;
236
237 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
238 ("[tsc,%d] illegal CPU value %d", __LINE__, cpu));
239 KASSERT(ri == 0, ("[tsc,%d] illegal ri %d", __LINE__, ri));
240
241 phw = &tsc_pcpu[cpu]->tc_hw;
242 pm = phw->phw_pmc;
243
244 KASSERT(pm != NULL,
245 ("[tsc,%d] no owner for PHW [cpu%d,pmc%d]", __LINE__, cpu, ri));
246
247 mode = PMC_TO_MODE(pm);
248
249 KASSERT(mode == PMC_MODE_SC,
250 ("[tsc,%d] illegal pmc mode %d", __LINE__, mode));
251
252 PMCDBG1(MDP,REA,1,"tsc-read id=%d", ri);
253
254 *v = rdtsc();
255
256 return (0);
257 }
258
259 static int
260 tsc_release_pmc(int cpu, int ri, struct pmc *pmc)
261 {
262 struct pmc_hw *phw __diagused;
263
264 (void) pmc;
265
266 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
267 ("[tsc,%d] illegal CPU value %d", __LINE__, cpu));
268 KASSERT(ri == 0,
269 ("[tsc,%d] illegal row-index %d", __LINE__, ri));
270
271 phw = &tsc_pcpu[cpu]->tc_hw;
272
273 KASSERT(phw->phw_pmc == NULL,
274 ("[tsc,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc));
275
276 /*
277 * Nothing to do.
278 */
279 return (0);
280 }
281
282 static int
283 tsc_start_pmc(int cpu, int ri)
284 {
285 (void) cpu;
286
287 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
288 ("[tsc,%d] illegal CPU value %d", __LINE__, cpu));
289 KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri));
290
291 return (0); /* TSCs are always running. */
292 }
293
294 static int
295 tsc_stop_pmc(int cpu, int ri)
296 {
297 (void) cpu; (void) ri;
298
299 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
300 ("[tsc,%d] illegal CPU value %d", __LINE__, cpu));
301 KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri));
302
303 return (0); /* Cannot actually stop a TSC. */
304 }
305
306 static int
307 tsc_write_pmc(int cpu, int ri, pmc_value_t v)
308 {
309 (void) cpu; (void) ri; (void) v;
310
311 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
312 ("[tsc,%d] illegal CPU value %d", __LINE__, cpu));
313 KASSERT(ri == 0, ("[tsc,%d] illegal row-index %d", __LINE__, ri));
314
315 /*
316 * The TSCs are used as timecounters by the kernel, so even
317 * though some i386 CPUs support writeable TSCs, we don't
318 * support writing changing TSC values through the HWPMC API.
319 */
320 return (0);
321 }
322
323 int
324 pmc_tsc_initialize(struct pmc_mdep *md, int maxcpu)
325 {
326 struct pmc_classdep *pcd;
327
328 KASSERT(md != NULL, ("[tsc,%d] md is NULL", __LINE__));
329 KASSERT(md->pmd_nclass >= 1, ("[tsc,%d] dubious md->nclass %d",
330 __LINE__, md->pmd_nclass));
331
332 tsc_pcpu = malloc(sizeof(struct tsc_cpu *) * maxcpu, M_PMC,
333 M_ZERO|M_WAITOK);
334
335 pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_TSC];
336
337 pcd->pcd_caps = PMC_CAP_READ;
338 pcd->pcd_class = PMC_CLASS_TSC;
339 pcd->pcd_num = TSC_NPMCS;
340 pcd->pcd_ri = md->pmd_npmc;
341 pcd->pcd_width = 64;
342
343 pcd->pcd_allocate_pmc = tsc_allocate_pmc;
344 pcd->pcd_config_pmc = tsc_config_pmc;
345 pcd->pcd_describe = tsc_describe;
346 pcd->pcd_get_config = tsc_get_config;
347 pcd->pcd_get_msr = tsc_get_msr;
348 pcd->pcd_pcpu_init = tsc_pcpu_init;
349 pcd->pcd_pcpu_fini = tsc_pcpu_fini;
350 pcd->pcd_read_pmc = tsc_read_pmc;
351 pcd->pcd_release_pmc = tsc_release_pmc;
352 pcd->pcd_start_pmc = tsc_start_pmc;
353 pcd->pcd_stop_pmc = tsc_stop_pmc;
354 pcd->pcd_write_pmc = tsc_write_pmc;
355
356 md->pmd_npmc += TSC_NPMCS;
357
358 return (0);
359 }
360
361 void
362 pmc_tsc_finalize(struct pmc_mdep *md)
363 {
364 #ifdef INVARIANTS
365 int i, ncpus;
366
367 ncpus = pmc_cpu_max();
368 for (i = 0; i < ncpus; i++)
369 KASSERT(tsc_pcpu[i] == NULL, ("[tsc,%d] non-null pcpu cpu %d",
370 __LINE__, i));
371
372 KASSERT(md->pmd_classdep[PMC_MDEP_CLASS_INDEX_TSC].pcd_class ==
373 PMC_CLASS_TSC, ("[tsc,%d] class mismatch", __LINE__));
374
375 #else
376 (void) md;
377 #endif
378
379 free(tsc_pcpu, M_PMC);
380 tsc_pcpu = NULL;
381 }
Cache object: f8f08b4c15784e457244fb1e18e5ba70
|