xref: /freebsd/sys/dev/hwpmc/hwpmc_ibs.c (revision e51ef8ae490fc9f73191f33e7ad388c2511c454a)
1*e51ef8aeSAli Mashtizadeh /*-
2*e51ef8aeSAli Mashtizadeh  * SPDX-License-Identifier: BSD-2-Clause
3*e51ef8aeSAli Mashtizadeh  *
4*e51ef8aeSAli Mashtizadeh  * Copyright (c) 2026, Ali Jose Mashtizadeh
5*e51ef8aeSAli Mashtizadeh  * All rights reserved.
6*e51ef8aeSAli Mashtizadeh  *
7*e51ef8aeSAli Mashtizadeh  * Redistribution and use in source and binary forms, with or without
8*e51ef8aeSAli Mashtizadeh  * modification, are permitted provided that the following conditions
9*e51ef8aeSAli Mashtizadeh  * are met:
10*e51ef8aeSAli Mashtizadeh  * 1. Redistributions of source code must retain the above copyright
11*e51ef8aeSAli Mashtizadeh  *    notice, this list of conditions and the following disclaimer.
12*e51ef8aeSAli Mashtizadeh  * 2. Redistributions in binary form must reproduce the above copyright
13*e51ef8aeSAli Mashtizadeh  *    notice, this list of conditions and the following disclaimer in the
14*e51ef8aeSAli Mashtizadeh  *    documentation and/or other materials provided with the distribution.
15*e51ef8aeSAli Mashtizadeh  *
16*e51ef8aeSAli Mashtizadeh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17*e51ef8aeSAli Mashtizadeh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*e51ef8aeSAli Mashtizadeh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*e51ef8aeSAli Mashtizadeh  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20*e51ef8aeSAli Mashtizadeh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*e51ef8aeSAli Mashtizadeh  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22*e51ef8aeSAli Mashtizadeh  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23*e51ef8aeSAli Mashtizadeh  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*e51ef8aeSAli Mashtizadeh  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*e51ef8aeSAli Mashtizadeh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*e51ef8aeSAli Mashtizadeh  * SUCH DAMAGE.
27*e51ef8aeSAli Mashtizadeh  */
28*e51ef8aeSAli Mashtizadeh 
29*e51ef8aeSAli Mashtizadeh /* Support for the AMD IBS */
30*e51ef8aeSAli Mashtizadeh 
31*e51ef8aeSAli Mashtizadeh #include <sys/param.h>
32*e51ef8aeSAli Mashtizadeh #include <sys/lock.h>
33*e51ef8aeSAli Mashtizadeh #include <sys/malloc.h>
34*e51ef8aeSAli Mashtizadeh #include <sys/mutex.h>
35*e51ef8aeSAli Mashtizadeh #include <sys/pcpu.h>
36*e51ef8aeSAli Mashtizadeh #include <sys/pmc.h>
37*e51ef8aeSAli Mashtizadeh #include <sys/pmckern.h>
38*e51ef8aeSAli Mashtizadeh #include <sys/pmclog.h>
39*e51ef8aeSAli Mashtizadeh #include <sys/smp.h>
40*e51ef8aeSAli Mashtizadeh #include <sys/systm.h>
41*e51ef8aeSAli Mashtizadeh 
42*e51ef8aeSAli Mashtizadeh #include <machine/cpu.h>
43*e51ef8aeSAli Mashtizadeh #include <machine/cpufunc.h>
44*e51ef8aeSAli Mashtizadeh #include <machine/md_var.h>
45*e51ef8aeSAli Mashtizadeh #include <machine/specialreg.h>
46*e51ef8aeSAli Mashtizadeh 
47*e51ef8aeSAli Mashtizadeh #define	IBS_STOP_ITER		50 /* Stopping iterations */
48*e51ef8aeSAli Mashtizadeh 
49*e51ef8aeSAli Mashtizadeh /* AMD IBS PMCs */
50*e51ef8aeSAli Mashtizadeh struct ibs_descr {
51*e51ef8aeSAli Mashtizadeh 	struct pmc_descr pm_descr;  /* "base class" */
52*e51ef8aeSAli Mashtizadeh };
53*e51ef8aeSAli Mashtizadeh 
54*e51ef8aeSAli Mashtizadeh /*
55*e51ef8aeSAli Mashtizadeh  * Globals
56*e51ef8aeSAli Mashtizadeh  */
57*e51ef8aeSAli Mashtizadeh static uint64_t ibs_features;
58*e51ef8aeSAli Mashtizadeh 
59*e51ef8aeSAli Mashtizadeh /*
60*e51ef8aeSAli Mashtizadeh  * Per-processor information
61*e51ef8aeSAli Mashtizadeh  */
62*e51ef8aeSAli Mashtizadeh #define	IBS_CPU_RUNNING		1
63*e51ef8aeSAli Mashtizadeh #define	IBS_CPU_STOPPING	2
64*e51ef8aeSAli Mashtizadeh #define	IBS_CPU_STOPPED		3
65*e51ef8aeSAli Mashtizadeh 
66*e51ef8aeSAli Mashtizadeh struct ibs_cpu {
67*e51ef8aeSAli Mashtizadeh 	int		pc_status;
68*e51ef8aeSAli Mashtizadeh 	struct pmc_hw	pc_ibspmcs[IBS_NPMCS];
69*e51ef8aeSAli Mashtizadeh };
70*e51ef8aeSAli Mashtizadeh static struct ibs_cpu **ibs_pcpu;
71*e51ef8aeSAli Mashtizadeh 
72*e51ef8aeSAli Mashtizadeh /*
73*e51ef8aeSAli Mashtizadeh  * Read a PMC value from the MSR.
74*e51ef8aeSAli Mashtizadeh  */
75*e51ef8aeSAli Mashtizadeh static int
ibs_read_pmc(int cpu,int ri,struct pmc * pm,pmc_value_t * v)76*e51ef8aeSAli Mashtizadeh ibs_read_pmc(int cpu, int ri, struct pmc *pm, pmc_value_t *v)
77*e51ef8aeSAli Mashtizadeh {
78*e51ef8aeSAli Mashtizadeh 
79*e51ef8aeSAli Mashtizadeh 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
80*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] illegal CPU value %d", __LINE__, cpu));
81*e51ef8aeSAli Mashtizadeh 	KASSERT(ri >= 0 && ri < IBS_NPMCS,
82*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] illegal row-index %d", __LINE__, ri));
83*e51ef8aeSAli Mashtizadeh 	KASSERT(ibs_pcpu[cpu],
84*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] null per-cpu, cpu %d", __LINE__, cpu));
85*e51ef8aeSAli Mashtizadeh 
86*e51ef8aeSAli Mashtizadeh 	/* read the IBS ctl */
87*e51ef8aeSAli Mashtizadeh 	switch (ri) {
88*e51ef8aeSAli Mashtizadeh 	case IBS_PMC_FETCH:
89*e51ef8aeSAli Mashtizadeh 		*v = rdmsr(IBS_FETCH_CTL);
90*e51ef8aeSAli Mashtizadeh 		break;
91*e51ef8aeSAli Mashtizadeh 	case IBS_PMC_OP:
92*e51ef8aeSAli Mashtizadeh 		*v = rdmsr(IBS_OP_CTL);
93*e51ef8aeSAli Mashtizadeh 		break;
94*e51ef8aeSAli Mashtizadeh 	}
95*e51ef8aeSAli Mashtizadeh 
96*e51ef8aeSAli Mashtizadeh 	PMCDBG2(MDP, REA, 2, "ibs-read id=%d -> %jd", ri, *v);
97*e51ef8aeSAli Mashtizadeh 
98*e51ef8aeSAli Mashtizadeh 	return (0);
99*e51ef8aeSAli Mashtizadeh }
100*e51ef8aeSAli Mashtizadeh 
101*e51ef8aeSAli Mashtizadeh /*
102*e51ef8aeSAli Mashtizadeh  * Write a PMC MSR.
103*e51ef8aeSAli Mashtizadeh  */
104*e51ef8aeSAli Mashtizadeh static int
ibs_write_pmc(int cpu,int ri,struct pmc * pm,pmc_value_t v)105*e51ef8aeSAli Mashtizadeh ibs_write_pmc(int cpu, int ri, struct pmc *pm, pmc_value_t v)
106*e51ef8aeSAli Mashtizadeh {
107*e51ef8aeSAli Mashtizadeh 
108*e51ef8aeSAli Mashtizadeh 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
109*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] illegal CPU value %d", __LINE__, cpu));
110*e51ef8aeSAli Mashtizadeh 	KASSERT(ri >= 0 && ri < IBS_NPMCS,
111*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] illegal row-index %d", __LINE__, ri));
112*e51ef8aeSAli Mashtizadeh 
113*e51ef8aeSAli Mashtizadeh 	PMCDBG3(MDP, WRI, 1, "ibs-write cpu=%d ri=%d v=%jx", cpu, ri, v);
114*e51ef8aeSAli Mashtizadeh 
115*e51ef8aeSAli Mashtizadeh 	return (0);
116*e51ef8aeSAli Mashtizadeh }
117*e51ef8aeSAli Mashtizadeh 
118*e51ef8aeSAli Mashtizadeh /*
119*e51ef8aeSAli Mashtizadeh  * Configure hardware PMC according to the configuration recorded in 'pm'.
120*e51ef8aeSAli Mashtizadeh  */
121*e51ef8aeSAli Mashtizadeh static int
ibs_config_pmc(int cpu,int ri,struct pmc * pm)122*e51ef8aeSAli Mashtizadeh ibs_config_pmc(int cpu, int ri, struct pmc *pm)
123*e51ef8aeSAli Mashtizadeh {
124*e51ef8aeSAli Mashtizadeh 	struct pmc_hw *phw;
125*e51ef8aeSAli Mashtizadeh 
126*e51ef8aeSAli Mashtizadeh 	PMCDBG3(MDP, CFG, 1, "cpu=%d ri=%d pm=%p", cpu, ri, pm);
127*e51ef8aeSAli Mashtizadeh 
128*e51ef8aeSAli Mashtizadeh 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
129*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] illegal CPU value %d", __LINE__, cpu));
130*e51ef8aeSAli Mashtizadeh 	KASSERT(ri >= 0 && ri < IBS_NPMCS,
131*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] illegal row-index %d", __LINE__, ri));
132*e51ef8aeSAli Mashtizadeh 
133*e51ef8aeSAli Mashtizadeh 	phw = &ibs_pcpu[cpu]->pc_ibspmcs[ri];
134*e51ef8aeSAli Mashtizadeh 
135*e51ef8aeSAli Mashtizadeh 	KASSERT(pm == NULL || phw->phw_pmc == NULL,
136*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] pm=%p phw->pm=%p hwpmc not unconfigured",
137*e51ef8aeSAli Mashtizadeh 		__LINE__, pm, phw->phw_pmc));
138*e51ef8aeSAli Mashtizadeh 
139*e51ef8aeSAli Mashtizadeh 	phw->phw_pmc = pm;
140*e51ef8aeSAli Mashtizadeh 
141*e51ef8aeSAli Mashtizadeh 	return (0);
142*e51ef8aeSAli Mashtizadeh }
143*e51ef8aeSAli Mashtizadeh 
144*e51ef8aeSAli Mashtizadeh /*
145*e51ef8aeSAli Mashtizadeh  * Retrieve a configured PMC pointer from hardware state.
146*e51ef8aeSAli Mashtizadeh  */
147*e51ef8aeSAli Mashtizadeh static int
ibs_get_config(int cpu,int ri,struct pmc ** ppm)148*e51ef8aeSAli Mashtizadeh ibs_get_config(int cpu, int ri, struct pmc **ppm)
149*e51ef8aeSAli Mashtizadeh {
150*e51ef8aeSAli Mashtizadeh 
151*e51ef8aeSAli Mashtizadeh 	*ppm = ibs_pcpu[cpu]->pc_ibspmcs[ri].phw_pmc;
152*e51ef8aeSAli Mashtizadeh 
153*e51ef8aeSAli Mashtizadeh 	return (0);
154*e51ef8aeSAli Mashtizadeh }
155*e51ef8aeSAli Mashtizadeh 
156*e51ef8aeSAli Mashtizadeh /*
157*e51ef8aeSAli Mashtizadeh  * Check if a given PMC allocation is feasible.
158*e51ef8aeSAli Mashtizadeh  */
159*e51ef8aeSAli Mashtizadeh static int
ibs_allocate_pmc(int cpu __unused,int ri,struct pmc * pm,const struct pmc_op_pmcallocate * a)160*e51ef8aeSAli Mashtizadeh ibs_allocate_pmc(int cpu __unused, int ri, struct pmc *pm,
161*e51ef8aeSAli Mashtizadeh     const struct pmc_op_pmcallocate *a)
162*e51ef8aeSAli Mashtizadeh {
163*e51ef8aeSAli Mashtizadeh 	uint64_t caps, config;
164*e51ef8aeSAli Mashtizadeh 
165*e51ef8aeSAli Mashtizadeh 	KASSERT(ri >= 0 && ri < IBS_NPMCS,
166*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] illegal row index %d", __LINE__, ri));
167*e51ef8aeSAli Mashtizadeh 
168*e51ef8aeSAli Mashtizadeh 	/* check class match */
169*e51ef8aeSAli Mashtizadeh 	if (a->pm_class != PMC_CLASS_IBS)
170*e51ef8aeSAli Mashtizadeh 		return (EINVAL);
171*e51ef8aeSAli Mashtizadeh 	if (a->pm_md.pm_ibs.ibs_type != ri)
172*e51ef8aeSAli Mashtizadeh 		return (EINVAL);
173*e51ef8aeSAli Mashtizadeh 
174*e51ef8aeSAli Mashtizadeh 	caps = pm->pm_caps;
175*e51ef8aeSAli Mashtizadeh 
176*e51ef8aeSAli Mashtizadeh 	PMCDBG2(MDP, ALL, 1, "ibs-allocate ri=%d caps=0x%x", ri, caps);
177*e51ef8aeSAli Mashtizadeh 
178*e51ef8aeSAli Mashtizadeh 	if ((caps & PMC_CAP_SYSTEM) == 0)
179*e51ef8aeSAli Mashtizadeh 		return (EINVAL);
180*e51ef8aeSAli Mashtizadeh 
181*e51ef8aeSAli Mashtizadeh 	config = a->pm_md.pm_ibs.ibs_ctl;
182*e51ef8aeSAli Mashtizadeh 	pm->pm_md.pm_ibs.ibs_ctl = config;
183*e51ef8aeSAli Mashtizadeh 
184*e51ef8aeSAli Mashtizadeh 	PMCDBG2(MDP, ALL, 2, "ibs-allocate ri=%d -> config=0x%x", ri, config);
185*e51ef8aeSAli Mashtizadeh 
186*e51ef8aeSAli Mashtizadeh 	return (0);
187*e51ef8aeSAli Mashtizadeh }
188*e51ef8aeSAli Mashtizadeh 
189*e51ef8aeSAli Mashtizadeh /*
190*e51ef8aeSAli Mashtizadeh  * Release machine dependent state associated with a PMC.  This is a
191*e51ef8aeSAli Mashtizadeh  * no-op on this architecture.
192*e51ef8aeSAli Mashtizadeh  */
193*e51ef8aeSAli Mashtizadeh static int
ibs_release_pmc(int cpu,int ri,struct pmc * pmc __unused)194*e51ef8aeSAli Mashtizadeh ibs_release_pmc(int cpu, int ri, struct pmc *pmc __unused)
195*e51ef8aeSAli Mashtizadeh {
196*e51ef8aeSAli Mashtizadeh 	struct pmc_hw *phw __diagused;
197*e51ef8aeSAli Mashtizadeh 
198*e51ef8aeSAli Mashtizadeh 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
199*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] illegal CPU value %d", __LINE__, cpu));
200*e51ef8aeSAli Mashtizadeh 	KASSERT(ri >= 0 && ri < IBS_NPMCS,
201*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] illegal row-index %d", __LINE__, ri));
202*e51ef8aeSAli Mashtizadeh 
203*e51ef8aeSAli Mashtizadeh 	PMCDBG1(MDP, ALL, 1, "ibs-release ri=%d", ri);
204*e51ef8aeSAli Mashtizadeh 
205*e51ef8aeSAli Mashtizadeh 	phw = &ibs_pcpu[cpu]->pc_ibspmcs[ri];
206*e51ef8aeSAli Mashtizadeh 
207*e51ef8aeSAli Mashtizadeh 	KASSERT(phw->phw_pmc == NULL,
208*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc));
209*e51ef8aeSAli Mashtizadeh 
210*e51ef8aeSAli Mashtizadeh 	return (0);
211*e51ef8aeSAli Mashtizadeh }
212*e51ef8aeSAli Mashtizadeh 
213*e51ef8aeSAli Mashtizadeh /*
214*e51ef8aeSAli Mashtizadeh  * Start a PMC.
215*e51ef8aeSAli Mashtizadeh  */
216*e51ef8aeSAli Mashtizadeh static int
ibs_start_pmc(int cpu __diagused,int ri,struct pmc * pm)217*e51ef8aeSAli Mashtizadeh ibs_start_pmc(int cpu __diagused, int ri, struct pmc *pm)
218*e51ef8aeSAli Mashtizadeh {
219*e51ef8aeSAli Mashtizadeh 	uint64_t config;
220*e51ef8aeSAli Mashtizadeh 
221*e51ef8aeSAli Mashtizadeh 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
222*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] illegal CPU value %d", __LINE__, cpu));
223*e51ef8aeSAli Mashtizadeh 	KASSERT(ri >= 0 && ri < IBS_NPMCS,
224*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] illegal row-index %d", __LINE__, ri));
225*e51ef8aeSAli Mashtizadeh 
226*e51ef8aeSAli Mashtizadeh 	PMCDBG2(MDP, STA, 1, "ibs-start cpu=%d ri=%d", cpu, ri);
227*e51ef8aeSAli Mashtizadeh 
228*e51ef8aeSAli Mashtizadeh 	/*
229*e51ef8aeSAli Mashtizadeh 	 * This is used to handle spurious NMIs.  All that matters is that it
230*e51ef8aeSAli Mashtizadeh 	 * is not in the stopping state.
231*e51ef8aeSAli Mashtizadeh 	 */
232*e51ef8aeSAli Mashtizadeh 	atomic_store_int(&ibs_pcpu[cpu]->pc_status, IBS_CPU_RUNNING);
233*e51ef8aeSAli Mashtizadeh 
234*e51ef8aeSAli Mashtizadeh 	/*
235*e51ef8aeSAli Mashtizadeh 	 * Turn on the ENABLE bit.  Zeroing out the control register eliminates
236*e51ef8aeSAli Mashtizadeh 	 * stale valid bits from spurious NMIs and it resets the counter.
237*e51ef8aeSAli Mashtizadeh 	 */
238*e51ef8aeSAli Mashtizadeh 	switch (ri) {
239*e51ef8aeSAli Mashtizadeh 	case IBS_PMC_FETCH:
240*e51ef8aeSAli Mashtizadeh 		wrmsr(IBS_FETCH_CTL, 0);
241*e51ef8aeSAli Mashtizadeh 		config = pm->pm_md.pm_ibs.ibs_ctl | IBS_FETCH_CTL_ENABLE;
242*e51ef8aeSAli Mashtizadeh 		wrmsr(IBS_FETCH_CTL, config);
243*e51ef8aeSAli Mashtizadeh 		break;
244*e51ef8aeSAli Mashtizadeh 	case IBS_PMC_OP:
245*e51ef8aeSAli Mashtizadeh 		wrmsr(IBS_OP_CTL, 0);
246*e51ef8aeSAli Mashtizadeh 		config = pm->pm_md.pm_ibs.ibs_ctl | IBS_OP_CTL_ENABLE;
247*e51ef8aeSAli Mashtizadeh 		wrmsr(IBS_OP_CTL, config);
248*e51ef8aeSAli Mashtizadeh 		break;
249*e51ef8aeSAli Mashtizadeh 	}
250*e51ef8aeSAli Mashtizadeh 
251*e51ef8aeSAli Mashtizadeh 	return (0);
252*e51ef8aeSAli Mashtizadeh }
253*e51ef8aeSAli Mashtizadeh 
254*e51ef8aeSAli Mashtizadeh /*
255*e51ef8aeSAli Mashtizadeh  * Stop a PMC.
256*e51ef8aeSAli Mashtizadeh  */
257*e51ef8aeSAli Mashtizadeh static int
ibs_stop_pmc(int cpu __diagused,int ri,struct pmc * pm)258*e51ef8aeSAli Mashtizadeh ibs_stop_pmc(int cpu __diagused, int ri, struct pmc *pm)
259*e51ef8aeSAli Mashtizadeh {
260*e51ef8aeSAli Mashtizadeh 	int i;
261*e51ef8aeSAli Mashtizadeh 	uint64_t config;
262*e51ef8aeSAli Mashtizadeh 
263*e51ef8aeSAli Mashtizadeh 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
264*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] illegal CPU value %d", __LINE__, cpu));
265*e51ef8aeSAli Mashtizadeh 	KASSERT(ri >= 0 && ri < IBS_NPMCS,
266*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] illegal row-index %d", __LINE__, ri));
267*e51ef8aeSAli Mashtizadeh 
268*e51ef8aeSAli Mashtizadeh 	PMCDBG1(MDP, STO, 1, "ibs-stop ri=%d", ri);
269*e51ef8aeSAli Mashtizadeh 
270*e51ef8aeSAli Mashtizadeh 	/*
271*e51ef8aeSAli Mashtizadeh 	 * Turn off the ENABLE bit, but unfortunately there are a few quirks
272*e51ef8aeSAli Mashtizadeh 	 * that generate excess NMIs.  Workaround #420 in the Revision Guide
273*e51ef8aeSAli Mashtizadeh 	 * for AMD Family 10h Processors 41322 Rev. 3.92 March 2012. requires
274*e51ef8aeSAli Mashtizadeh 	 * that we clear the count before clearing enable.
275*e51ef8aeSAli Mashtizadeh 	 *
276*e51ef8aeSAli Mashtizadeh 	 * Even after clearing the counter spurious NMIs are still possible so
277*e51ef8aeSAli Mashtizadeh 	 * we use a per-CPU atomic variable to notify the interrupt handler we
278*e51ef8aeSAli Mashtizadeh 	 * are stopping and discard spurious NMIs.  We then retry clearing the
279*e51ef8aeSAli Mashtizadeh 	 * control register for 50us.  This gives us enough time and ensures
280*e51ef8aeSAli Mashtizadeh 	 * that the valid bit is not accidently stuck after a spurious NMI.
281*e51ef8aeSAli Mashtizadeh 	 */
282*e51ef8aeSAli Mashtizadeh 	config = pm->pm_md.pm_ibs.ibs_ctl;
283*e51ef8aeSAli Mashtizadeh 
284*e51ef8aeSAli Mashtizadeh 	atomic_store_int(&ibs_pcpu[cpu]->pc_status, IBS_CPU_STOPPING);
285*e51ef8aeSAli Mashtizadeh 
286*e51ef8aeSAli Mashtizadeh 	switch (ri) {
287*e51ef8aeSAli Mashtizadeh 	case IBS_PMC_FETCH:
288*e51ef8aeSAli Mashtizadeh 		wrmsr(IBS_FETCH_CTL, config & ~IBS_FETCH_CTL_MAXCNTMASK);
289*e51ef8aeSAli Mashtizadeh 		DELAY(1);
290*e51ef8aeSAli Mashtizadeh 		config &= ~IBS_FETCH_CTL_ENABLE;
291*e51ef8aeSAli Mashtizadeh 		wrmsr(IBS_FETCH_CTL, config);
292*e51ef8aeSAli Mashtizadeh 		break;
293*e51ef8aeSAli Mashtizadeh 	case IBS_PMC_OP:
294*e51ef8aeSAli Mashtizadeh 		wrmsr(IBS_FETCH_CTL, config & ~IBS_FETCH_CTL_MAXCNTMASK);
295*e51ef8aeSAli Mashtizadeh 		DELAY(1);
296*e51ef8aeSAli Mashtizadeh 		config &= ~IBS_OP_CTL_ENABLE;
297*e51ef8aeSAli Mashtizadeh 		wrmsr(IBS_OP_CTL, config);
298*e51ef8aeSAli Mashtizadeh 		break;
299*e51ef8aeSAli Mashtizadeh 	}
300*e51ef8aeSAli Mashtizadeh 
301*e51ef8aeSAli Mashtizadeh 	for (i = 0; i < IBS_STOP_ITER; i++) {
302*e51ef8aeSAli Mashtizadeh 		DELAY(1);
303*e51ef8aeSAli Mashtizadeh 
304*e51ef8aeSAli Mashtizadeh 		switch (ri) {
305*e51ef8aeSAli Mashtizadeh 		case IBS_PMC_FETCH:
306*e51ef8aeSAli Mashtizadeh 			wrmsr(IBS_FETCH_CTL, 0);
307*e51ef8aeSAli Mashtizadeh 			break;
308*e51ef8aeSAli Mashtizadeh 		case IBS_PMC_OP:
309*e51ef8aeSAli Mashtizadeh 			wrmsr(IBS_OP_CTL, 0);
310*e51ef8aeSAli Mashtizadeh 			break;
311*e51ef8aeSAli Mashtizadeh 		}
312*e51ef8aeSAli Mashtizadeh 	}
313*e51ef8aeSAli Mashtizadeh 
314*e51ef8aeSAli Mashtizadeh 	atomic_store_int(&ibs_pcpu[cpu]->pc_status, IBS_CPU_STOPPED);
315*e51ef8aeSAli Mashtizadeh 
316*e51ef8aeSAli Mashtizadeh 	return (0);
317*e51ef8aeSAli Mashtizadeh }
318*e51ef8aeSAli Mashtizadeh 
319*e51ef8aeSAli Mashtizadeh static void
pmc_ibs_process_fetch(struct pmc * pm,struct trapframe * tf,uint64_t config)320*e51ef8aeSAli Mashtizadeh pmc_ibs_process_fetch(struct pmc *pm, struct trapframe *tf, uint64_t config)
321*e51ef8aeSAli Mashtizadeh {
322*e51ef8aeSAli Mashtizadeh 	struct pmc_multipart mpd;
323*e51ef8aeSAli Mashtizadeh 
324*e51ef8aeSAli Mashtizadeh 	if (pm == NULL)
325*e51ef8aeSAli Mashtizadeh 		return;
326*e51ef8aeSAli Mashtizadeh 
327*e51ef8aeSAli Mashtizadeh 	if (pm->pm_state != PMC_STATE_RUNNING)
328*e51ef8aeSAli Mashtizadeh 		return;
329*e51ef8aeSAli Mashtizadeh 
330*e51ef8aeSAli Mashtizadeh 	memset(&mpd, 0, sizeof(mpd));
331*e51ef8aeSAli Mashtizadeh 
332*e51ef8aeSAli Mashtizadeh 	mpd.pl_type = PMC_CC_MULTIPART_IBS_FETCH;
333*e51ef8aeSAli Mashtizadeh 	mpd.pl_length = 4;
334*e51ef8aeSAli Mashtizadeh 	mpd.pl_mpdata[PMC_MPIDX_FETCH_CTL] = config;
335*e51ef8aeSAli Mashtizadeh 	if (ibs_features) {
336*e51ef8aeSAli Mashtizadeh 		mpd.pl_mpdata[PMC_MPIDX_FETCH_EXTCTL] = rdmsr(IBS_FETCH_EXTCTL);
337*e51ef8aeSAli Mashtizadeh 	}
338*e51ef8aeSAli Mashtizadeh 	mpd.pl_mpdata[PMC_MPIDX_FETCH_CTL] = config;
339*e51ef8aeSAli Mashtizadeh 	mpd.pl_mpdata[PMC_MPIDX_FETCH_LINADDR] = rdmsr(IBS_FETCH_LINADDR);
340*e51ef8aeSAli Mashtizadeh 	if ((config & IBS_FETCH_CTL_PHYSADDRVALID) != 0) {
341*e51ef8aeSAli Mashtizadeh 		mpd.pl_mpdata[PMC_MPIDX_FETCH_PHYSADDR] =
342*e51ef8aeSAli Mashtizadeh 		    rdmsr(IBS_FETCH_PHYSADDR);
343*e51ef8aeSAli Mashtizadeh 	}
344*e51ef8aeSAli Mashtizadeh 
345*e51ef8aeSAli Mashtizadeh 	pmc_process_interrupt_mp(PMC_HR, pm, tf, &mpd);
346*e51ef8aeSAli Mashtizadeh }
347*e51ef8aeSAli Mashtizadeh 
348*e51ef8aeSAli Mashtizadeh static void
pmc_ibs_process_op(struct pmc * pm,struct trapframe * tf,uint64_t config)349*e51ef8aeSAli Mashtizadeh pmc_ibs_process_op(struct pmc *pm, struct trapframe *tf, uint64_t config)
350*e51ef8aeSAli Mashtizadeh {
351*e51ef8aeSAli Mashtizadeh 	struct pmc_multipart mpd;
352*e51ef8aeSAli Mashtizadeh 
353*e51ef8aeSAli Mashtizadeh 	if (pm == NULL)
354*e51ef8aeSAli Mashtizadeh 		return;
355*e51ef8aeSAli Mashtizadeh 
356*e51ef8aeSAli Mashtizadeh 	if (pm->pm_state != PMC_STATE_RUNNING)
357*e51ef8aeSAli Mashtizadeh 		return;
358*e51ef8aeSAli Mashtizadeh 
359*e51ef8aeSAli Mashtizadeh 	memset(&mpd, 0, sizeof(mpd));
360*e51ef8aeSAli Mashtizadeh 
361*e51ef8aeSAli Mashtizadeh 	mpd.pl_type = PMC_CC_MULTIPART_IBS_OP;
362*e51ef8aeSAli Mashtizadeh 	mpd.pl_length = 8;
363*e51ef8aeSAli Mashtizadeh 	mpd.pl_mpdata[PMC_MPIDX_OP_CTL] = config;
364*e51ef8aeSAli Mashtizadeh 	mpd.pl_mpdata[PMC_MPIDX_OP_RIP] = rdmsr(IBS_OP_RIP);
365*e51ef8aeSAli Mashtizadeh 	mpd.pl_mpdata[PMC_MPIDX_OP_DATA] = rdmsr(IBS_OP_DATA);
366*e51ef8aeSAli Mashtizadeh 	mpd.pl_mpdata[PMC_MPIDX_OP_DATA2] = rdmsr(IBS_OP_DATA2);
367*e51ef8aeSAli Mashtizadeh 	mpd.pl_mpdata[PMC_MPIDX_OP_DATA3] = rdmsr(IBS_OP_DATA3);
368*e51ef8aeSAli Mashtizadeh 	mpd.pl_mpdata[PMC_MPIDX_OP_DC_LINADDR] = rdmsr(IBS_OP_DC_LINADDR);
369*e51ef8aeSAli Mashtizadeh 	mpd.pl_mpdata[PMC_MPIDX_OP_DC_PHYSADDR] = rdmsr(IBS_OP_DC_PHYSADDR);
370*e51ef8aeSAli Mashtizadeh 
371*e51ef8aeSAli Mashtizadeh 	pmc_process_interrupt_mp(PMC_HR, pm, tf, &mpd);
372*e51ef8aeSAli Mashtizadeh 
373*e51ef8aeSAli Mashtizadeh 	wrmsr(IBS_OP_CTL, pm->pm_md.pm_ibs.ibs_ctl | IBS_OP_CTL_ENABLE);
374*e51ef8aeSAli Mashtizadeh }
375*e51ef8aeSAli Mashtizadeh 
376*e51ef8aeSAli Mashtizadeh /*
377*e51ef8aeSAli Mashtizadeh  * Interrupt handler.  This function needs to return '1' if the
378*e51ef8aeSAli Mashtizadeh  * interrupt was this CPU's PMCs or '0' otherwise.  It is not allowed
379*e51ef8aeSAli Mashtizadeh  * to sleep or do anything a 'fast' interrupt handler is not allowed
380*e51ef8aeSAli Mashtizadeh  * to do.
381*e51ef8aeSAli Mashtizadeh  */
382*e51ef8aeSAli Mashtizadeh int
pmc_ibs_intr(struct trapframe * tf)383*e51ef8aeSAli Mashtizadeh pmc_ibs_intr(struct trapframe *tf)
384*e51ef8aeSAli Mashtizadeh {
385*e51ef8aeSAli Mashtizadeh 	struct ibs_cpu *pac;
386*e51ef8aeSAli Mashtizadeh 	struct pmc *pm;
387*e51ef8aeSAli Mashtizadeh 	int retval, cpu;
388*e51ef8aeSAli Mashtizadeh 	uint64_t config;
389*e51ef8aeSAli Mashtizadeh 
390*e51ef8aeSAli Mashtizadeh 	cpu = curcpu;
391*e51ef8aeSAli Mashtizadeh 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
392*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] out of range CPU %d", __LINE__, cpu));
393*e51ef8aeSAli Mashtizadeh 
394*e51ef8aeSAli Mashtizadeh 	PMCDBG3(MDP, INT, 1, "cpu=%d tf=%p um=%d", cpu, tf, TRAPF_USERMODE(tf));
395*e51ef8aeSAli Mashtizadeh 
396*e51ef8aeSAli Mashtizadeh 	retval = 0;
397*e51ef8aeSAli Mashtizadeh 
398*e51ef8aeSAli Mashtizadeh 	pac = ibs_pcpu[cpu];
399*e51ef8aeSAli Mashtizadeh 
400*e51ef8aeSAli Mashtizadeh 	config = rdmsr(IBS_FETCH_CTL);
401*e51ef8aeSAli Mashtizadeh 	if ((config & IBS_FETCH_CTL_VALID) != 0) {
402*e51ef8aeSAli Mashtizadeh 		pm = pac->pc_ibspmcs[IBS_PMC_FETCH].phw_pmc;
403*e51ef8aeSAli Mashtizadeh 
404*e51ef8aeSAli Mashtizadeh 		retval = 1;
405*e51ef8aeSAli Mashtizadeh 
406*e51ef8aeSAli Mashtizadeh 		pmc_ibs_process_fetch(pm, tf, config);
407*e51ef8aeSAli Mashtizadeh 	}
408*e51ef8aeSAli Mashtizadeh 
409*e51ef8aeSAli Mashtizadeh 	config = rdmsr(IBS_OP_CTL);
410*e51ef8aeSAli Mashtizadeh 	if ((retval == 0) && ((config & IBS_OP_CTL_VALID) != 0)) {
411*e51ef8aeSAli Mashtizadeh 		pm = pac->pc_ibspmcs[IBS_PMC_OP].phw_pmc;
412*e51ef8aeSAli Mashtizadeh 
413*e51ef8aeSAli Mashtizadeh 		retval = 1;
414*e51ef8aeSAli Mashtizadeh 
415*e51ef8aeSAli Mashtizadeh 		pmc_ibs_process_op(pm, tf, config);
416*e51ef8aeSAli Mashtizadeh 	}
417*e51ef8aeSAli Mashtizadeh 
418*e51ef8aeSAli Mashtizadeh 	if (retval == 0) {
419*e51ef8aeSAli Mashtizadeh 		// Lets check for a stray NMI when stopping
420*e51ef8aeSAli Mashtizadeh 		if (atomic_load_int(&pac->pc_status) == IBS_CPU_STOPPING) {
421*e51ef8aeSAli Mashtizadeh 			return (1);
422*e51ef8aeSAli Mashtizadeh 		}
423*e51ef8aeSAli Mashtizadeh 	}
424*e51ef8aeSAli Mashtizadeh 
425*e51ef8aeSAli Mashtizadeh 
426*e51ef8aeSAli Mashtizadeh 	if (retval)
427*e51ef8aeSAli Mashtizadeh 		counter_u64_add(pmc_stats.pm_intr_processed, 1);
428*e51ef8aeSAli Mashtizadeh 	else
429*e51ef8aeSAli Mashtizadeh 		counter_u64_add(pmc_stats.pm_intr_ignored, 1);
430*e51ef8aeSAli Mashtizadeh 
431*e51ef8aeSAli Mashtizadeh 	PMCDBG1(MDP, INT, 2, "retval=%d", retval);
432*e51ef8aeSAli Mashtizadeh 
433*e51ef8aeSAli Mashtizadeh 	return (retval);
434*e51ef8aeSAli Mashtizadeh }
435*e51ef8aeSAli Mashtizadeh 
436*e51ef8aeSAli Mashtizadeh /*
437*e51ef8aeSAli Mashtizadeh  * Describe a PMC.
438*e51ef8aeSAli Mashtizadeh  */
439*e51ef8aeSAli Mashtizadeh static int
ibs_describe(int cpu,int ri,struct pmc_info * pi,struct pmc ** ppmc)440*e51ef8aeSAli Mashtizadeh ibs_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc)
441*e51ef8aeSAli Mashtizadeh {
442*e51ef8aeSAli Mashtizadeh 	struct pmc_hw *phw;
443*e51ef8aeSAli Mashtizadeh 
444*e51ef8aeSAli Mashtizadeh 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
445*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] illegal CPU %d", __LINE__, cpu));
446*e51ef8aeSAli Mashtizadeh 	KASSERT(ri >= 0 && ri < IBS_NPMCS,
447*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] row-index %d out of range", __LINE__, ri));
448*e51ef8aeSAli Mashtizadeh 
449*e51ef8aeSAli Mashtizadeh 	phw = &ibs_pcpu[cpu]->pc_ibspmcs[ri];
450*e51ef8aeSAli Mashtizadeh 
451*e51ef8aeSAli Mashtizadeh 	if (ri == IBS_PMC_FETCH) {
452*e51ef8aeSAli Mashtizadeh 		strlcpy(pi->pm_name, "IBS-FETCH", sizeof(pi->pm_name));
453*e51ef8aeSAli Mashtizadeh 		pi->pm_class = PMC_CLASS_IBS;
454*e51ef8aeSAli Mashtizadeh 		pi->pm_enabled = true;
455*e51ef8aeSAli Mashtizadeh 		*ppmc          = phw->phw_pmc;
456*e51ef8aeSAli Mashtizadeh 	} else {
457*e51ef8aeSAli Mashtizadeh 		strlcpy(pi->pm_name, "IBS-OP", sizeof(pi->pm_name));
458*e51ef8aeSAli Mashtizadeh 		pi->pm_class = PMC_CLASS_IBS;
459*e51ef8aeSAli Mashtizadeh 		pi->pm_enabled = true;
460*e51ef8aeSAli Mashtizadeh 		*ppmc          = phw->phw_pmc;
461*e51ef8aeSAli Mashtizadeh 	}
462*e51ef8aeSAli Mashtizadeh 
463*e51ef8aeSAli Mashtizadeh 	return (0);
464*e51ef8aeSAli Mashtizadeh }
465*e51ef8aeSAli Mashtizadeh 
466*e51ef8aeSAli Mashtizadeh /*
467*e51ef8aeSAli Mashtizadeh  * Processor-dependent initialization.
468*e51ef8aeSAli Mashtizadeh  */
469*e51ef8aeSAli Mashtizadeh static int
ibs_pcpu_init(struct pmc_mdep * md,int cpu)470*e51ef8aeSAli Mashtizadeh ibs_pcpu_init(struct pmc_mdep *md, int cpu)
471*e51ef8aeSAli Mashtizadeh {
472*e51ef8aeSAli Mashtizadeh 	struct ibs_cpu *pac;
473*e51ef8aeSAli Mashtizadeh 	struct pmc_cpu *pc;
474*e51ef8aeSAli Mashtizadeh 	struct pmc_hw  *phw;
475*e51ef8aeSAli Mashtizadeh 	int first_ri, n;
476*e51ef8aeSAli Mashtizadeh 
477*e51ef8aeSAli Mashtizadeh 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
478*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] insane cpu number %d", __LINE__, cpu));
479*e51ef8aeSAli Mashtizadeh 
480*e51ef8aeSAli Mashtizadeh 	PMCDBG1(MDP, INI, 1, "ibs-init cpu=%d", cpu);
481*e51ef8aeSAli Mashtizadeh 
482*e51ef8aeSAli Mashtizadeh 	ibs_pcpu[cpu] = pac = malloc(sizeof(struct ibs_cpu), M_PMC,
483*e51ef8aeSAli Mashtizadeh 	    M_WAITOK | M_ZERO);
484*e51ef8aeSAli Mashtizadeh 
485*e51ef8aeSAli Mashtizadeh 	/*
486*e51ef8aeSAli Mashtizadeh 	 * Set the content of the hardware descriptors to a known
487*e51ef8aeSAli Mashtizadeh 	 * state and initialize pointers in the MI per-cpu descriptor.
488*e51ef8aeSAli Mashtizadeh 	 */
489*e51ef8aeSAli Mashtizadeh 	pc = pmc_pcpu[cpu];
490*e51ef8aeSAli Mashtizadeh 	first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_IBS].pcd_ri;
491*e51ef8aeSAli Mashtizadeh 
492*e51ef8aeSAli Mashtizadeh 	KASSERT(pc != NULL, ("[ibs,%d] NULL per-cpu pointer", __LINE__));
493*e51ef8aeSAli Mashtizadeh 
494*e51ef8aeSAli Mashtizadeh 	for (n = 0, phw = pac->pc_ibspmcs; n < IBS_NPMCS; n++, phw++) {
495*e51ef8aeSAli Mashtizadeh 		phw->phw_state = PMC_PHW_FLAG_IS_ENABLED |
496*e51ef8aeSAli Mashtizadeh 		    PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(n);
497*e51ef8aeSAli Mashtizadeh 		phw->phw_pmc = NULL;
498*e51ef8aeSAli Mashtizadeh 		pc->pc_hwpmcs[n + first_ri] = phw;
499*e51ef8aeSAli Mashtizadeh 	}
500*e51ef8aeSAli Mashtizadeh 
501*e51ef8aeSAli Mashtizadeh 	return (0);
502*e51ef8aeSAli Mashtizadeh }
503*e51ef8aeSAli Mashtizadeh 
504*e51ef8aeSAli Mashtizadeh /*
505*e51ef8aeSAli Mashtizadeh  * Processor-dependent cleanup prior to the KLD being unloaded.
506*e51ef8aeSAli Mashtizadeh  */
507*e51ef8aeSAli Mashtizadeh static int
ibs_pcpu_fini(struct pmc_mdep * md,int cpu)508*e51ef8aeSAli Mashtizadeh ibs_pcpu_fini(struct pmc_mdep *md, int cpu)
509*e51ef8aeSAli Mashtizadeh {
510*e51ef8aeSAli Mashtizadeh 	struct ibs_cpu *pac;
511*e51ef8aeSAli Mashtizadeh 	struct pmc_cpu *pc;
512*e51ef8aeSAli Mashtizadeh 	int first_ri, i;
513*e51ef8aeSAli Mashtizadeh 
514*e51ef8aeSAli Mashtizadeh 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
515*e51ef8aeSAli Mashtizadeh 	    ("[ibs,%d] insane cpu number (%d)", __LINE__, cpu));
516*e51ef8aeSAli Mashtizadeh 
517*e51ef8aeSAli Mashtizadeh 	PMCDBG1(MDP, INI, 1, "ibs-cleanup cpu=%d", cpu);
518*e51ef8aeSAli Mashtizadeh 
519*e51ef8aeSAli Mashtizadeh 	/*
520*e51ef8aeSAli Mashtizadeh 	 * Turn off IBS.
521*e51ef8aeSAli Mashtizadeh 	 */
522*e51ef8aeSAli Mashtizadeh 	wrmsr(IBS_FETCH_CTL, 0);
523*e51ef8aeSAli Mashtizadeh 	wrmsr(IBS_OP_CTL, 0);
524*e51ef8aeSAli Mashtizadeh 
525*e51ef8aeSAli Mashtizadeh 	/*
526*e51ef8aeSAli Mashtizadeh 	 * Free up allocated space.
527*e51ef8aeSAli Mashtizadeh 	 */
528*e51ef8aeSAli Mashtizadeh 	if ((pac = ibs_pcpu[cpu]) == NULL)
529*e51ef8aeSAli Mashtizadeh 		return (0);
530*e51ef8aeSAli Mashtizadeh 
531*e51ef8aeSAli Mashtizadeh 	ibs_pcpu[cpu] = NULL;
532*e51ef8aeSAli Mashtizadeh 
533*e51ef8aeSAli Mashtizadeh 	pc = pmc_pcpu[cpu];
534*e51ef8aeSAli Mashtizadeh 	KASSERT(pc != NULL, ("[ibs,%d] NULL per-cpu state", __LINE__));
535*e51ef8aeSAli Mashtizadeh 
536*e51ef8aeSAli Mashtizadeh 	first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_IBS].pcd_ri;
537*e51ef8aeSAli Mashtizadeh 
538*e51ef8aeSAli Mashtizadeh 	/*
539*e51ef8aeSAli Mashtizadeh 	 * Reset pointers in the MI 'per-cpu' state.
540*e51ef8aeSAli Mashtizadeh 	 */
541*e51ef8aeSAli Mashtizadeh 	for (i = 0; i < IBS_NPMCS; i++)
542*e51ef8aeSAli Mashtizadeh 		pc->pc_hwpmcs[i + first_ri] = NULL;
543*e51ef8aeSAli Mashtizadeh 
544*e51ef8aeSAli Mashtizadeh 	free(pac, M_PMC);
545*e51ef8aeSAli Mashtizadeh 
546*e51ef8aeSAli Mashtizadeh 	return (0);
547*e51ef8aeSAli Mashtizadeh }
548*e51ef8aeSAli Mashtizadeh 
549*e51ef8aeSAli Mashtizadeh /*
550*e51ef8aeSAli Mashtizadeh  * Initialize ourselves.
551*e51ef8aeSAli Mashtizadeh  */
552*e51ef8aeSAli Mashtizadeh int
pmc_ibs_initialize(struct pmc_mdep * pmc_mdep,int ncpus)553*e51ef8aeSAli Mashtizadeh pmc_ibs_initialize(struct pmc_mdep *pmc_mdep, int ncpus)
554*e51ef8aeSAli Mashtizadeh {
555*e51ef8aeSAli Mashtizadeh 	u_int regs[4];
556*e51ef8aeSAli Mashtizadeh 	struct pmc_classdep *pcd;
557*e51ef8aeSAli Mashtizadeh 
558*e51ef8aeSAli Mashtizadeh 	/*
559*e51ef8aeSAli Mashtizadeh 	 * Allocate space for pointers to PMC HW descriptors and for
560*e51ef8aeSAli Mashtizadeh 	 * the MDEP structure used by MI code.
561*e51ef8aeSAli Mashtizadeh 	 */
562*e51ef8aeSAli Mashtizadeh 	ibs_pcpu = malloc(sizeof(struct ibs_cpu *) * pmc_cpu_max(), M_PMC,
563*e51ef8aeSAli Mashtizadeh 	    M_WAITOK | M_ZERO);
564*e51ef8aeSAli Mashtizadeh 
565*e51ef8aeSAli Mashtizadeh 	/* Initialize AMD IBS handling. */
566*e51ef8aeSAli Mashtizadeh 	pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_IBS];
567*e51ef8aeSAli Mashtizadeh 
568*e51ef8aeSAli Mashtizadeh 	pcd->pcd_caps		= IBS_PMC_CAPS;
569*e51ef8aeSAli Mashtizadeh 	pcd->pcd_class		= PMC_CLASS_IBS;
570*e51ef8aeSAli Mashtizadeh 	pcd->pcd_num		= IBS_NPMCS;
571*e51ef8aeSAli Mashtizadeh 	pcd->pcd_ri		= pmc_mdep->pmd_npmc;
572*e51ef8aeSAli Mashtizadeh 	pcd->pcd_width		= 0;
573*e51ef8aeSAli Mashtizadeh 
574*e51ef8aeSAli Mashtizadeh 	pcd->pcd_allocate_pmc	= ibs_allocate_pmc;
575*e51ef8aeSAli Mashtizadeh 	pcd->pcd_config_pmc	= ibs_config_pmc;
576*e51ef8aeSAli Mashtizadeh 	pcd->pcd_describe	= ibs_describe;
577*e51ef8aeSAli Mashtizadeh 	pcd->pcd_get_config	= ibs_get_config;
578*e51ef8aeSAli Mashtizadeh 	pcd->pcd_pcpu_fini	= ibs_pcpu_fini;
579*e51ef8aeSAli Mashtizadeh 	pcd->pcd_pcpu_init	= ibs_pcpu_init;
580*e51ef8aeSAli Mashtizadeh 	pcd->pcd_release_pmc	= ibs_release_pmc;
581*e51ef8aeSAli Mashtizadeh 	pcd->pcd_start_pmc	= ibs_start_pmc;
582*e51ef8aeSAli Mashtizadeh 	pcd->pcd_stop_pmc	= ibs_stop_pmc;
583*e51ef8aeSAli Mashtizadeh 	pcd->pcd_read_pmc	= ibs_read_pmc;
584*e51ef8aeSAli Mashtizadeh 	pcd->pcd_write_pmc	= ibs_write_pmc;
585*e51ef8aeSAli Mashtizadeh 
586*e51ef8aeSAli Mashtizadeh 	pmc_mdep->pmd_npmc	+= IBS_NPMCS;
587*e51ef8aeSAli Mashtizadeh 
588*e51ef8aeSAli Mashtizadeh 	if (cpu_exthigh >= CPUID_IBSID) {
589*e51ef8aeSAli Mashtizadeh 		do_cpuid(CPUID_IBSID, regs);
590*e51ef8aeSAli Mashtizadeh 		ibs_features = regs[0];
591*e51ef8aeSAli Mashtizadeh 	} else {
592*e51ef8aeSAli Mashtizadeh 		ibs_features = 0;
593*e51ef8aeSAli Mashtizadeh 	}
594*e51ef8aeSAli Mashtizadeh 
595*e51ef8aeSAli Mashtizadeh 	PMCDBG0(MDP, INI, 0, "ibs-initialize");
596*e51ef8aeSAli Mashtizadeh 
597*e51ef8aeSAli Mashtizadeh 	return (0);
598*e51ef8aeSAli Mashtizadeh }
599*e51ef8aeSAli Mashtizadeh 
600*e51ef8aeSAli Mashtizadeh /*
601*e51ef8aeSAli Mashtizadeh  * Finalization code for AMD CPUs.
602*e51ef8aeSAli Mashtizadeh  */
603*e51ef8aeSAli Mashtizadeh void
pmc_ibs_finalize(struct pmc_mdep * md)604*e51ef8aeSAli Mashtizadeh pmc_ibs_finalize(struct pmc_mdep *md)
605*e51ef8aeSAli Mashtizadeh {
606*e51ef8aeSAli Mashtizadeh 	PMCDBG0(MDP, INI, 1, "ibs-finalize");
607*e51ef8aeSAli Mashtizadeh 
608*e51ef8aeSAli Mashtizadeh 	for (int i = 0; i < pmc_cpu_max(); i++)
609*e51ef8aeSAli Mashtizadeh 		KASSERT(ibs_pcpu[i] == NULL,
610*e51ef8aeSAli Mashtizadeh 		    ("[ibs,%d] non-null pcpu cpu %d", __LINE__, i));
611*e51ef8aeSAli Mashtizadeh 
612*e51ef8aeSAli Mashtizadeh 	free(ibs_pcpu, M_PMC);
613*e51ef8aeSAli Mashtizadeh 	ibs_pcpu = NULL;
614*e51ef8aeSAli Mashtizadeh }
615