xref: /freebsd/sys/arm64/spe/arm_spe_backend.c (revision 68f185ccc9f8f9498d536f4737d888b37cf11882)
1*68f185ccSZachary Leaf /*-
2*68f185ccSZachary Leaf  * SPDX-License-Identifier: BSD-2-Clause
3*68f185ccSZachary Leaf  *
4*68f185ccSZachary Leaf  * Copyright (c) 2024 Arm Ltd
5*68f185ccSZachary Leaf  * Copyright (c) 2022 The FreeBSD Foundation
6*68f185ccSZachary Leaf  *
7*68f185ccSZachary Leaf  * Portions of this software were developed by Andrew Turner under sponsorship
8*68f185ccSZachary Leaf  * from the FreeBSD Foundation.
9*68f185ccSZachary Leaf  *
10*68f185ccSZachary Leaf  * Redistribution and use in source and binary forms, with or without
11*68f185ccSZachary Leaf  * modification, are permitted provided that the following conditions
12*68f185ccSZachary Leaf  * are met:
13*68f185ccSZachary Leaf  * 1. Redistributions of source code must retain the above copyright
14*68f185ccSZachary Leaf  *    notice, this list of conditions and the following disclaimer.
15*68f185ccSZachary Leaf  * 2. Redistributions in binary form must reproduce the above copyright
16*68f185ccSZachary Leaf  *    notice, this list of conditions and the following disclaimer in the
17*68f185ccSZachary Leaf  *    documentation and/or other materials provided with the distribution.
18*68f185ccSZachary Leaf  *
19*68f185ccSZachary Leaf  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20*68f185ccSZachary Leaf  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21*68f185ccSZachary Leaf  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22*68f185ccSZachary Leaf  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23*68f185ccSZachary Leaf  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24*68f185ccSZachary Leaf  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25*68f185ccSZachary Leaf  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26*68f185ccSZachary Leaf  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27*68f185ccSZachary Leaf  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28*68f185ccSZachary Leaf  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29*68f185ccSZachary Leaf  * SUCH DAMAGE.
30*68f185ccSZachary Leaf  */
31*68f185ccSZachary Leaf 
32*68f185ccSZachary Leaf /*
33*68f185ccSZachary Leaf  * Arm Statistical Profiling Extension (SPE) backend
34*68f185ccSZachary Leaf  *
35*68f185ccSZachary Leaf  * Basic SPE operation
36*68f185ccSZachary Leaf  *
37*68f185ccSZachary Leaf  *   SPE is enabled and configured on a per-core basis, with each core requiring
38*68f185ccSZachary Leaf  *   separate code to enable and configure. Each core also requires a separate
39*68f185ccSZachary Leaf  *   buffer passed as config where the CPU will write profiling data. When the
40*68f185ccSZachary Leaf  *   profiling buffer is full, an interrupt will be taken on the same CPU.
41*68f185ccSZachary Leaf  *
42*68f185ccSZachary Leaf  * Driver Design
43*68f185ccSZachary Leaf  *
44*68f185ccSZachary Leaf  * - HWT allocates a large single buffer per core. This buffer is split in half
45*68f185ccSZachary Leaf  *   to create a 2 element circular buffer (aka ping-pong buffer) where the
46*68f185ccSZachary Leaf  *   kernel writes to one half while userspace is copying the other half
47*68f185ccSZachary Leaf  * - SMP calls are used to enable and configure each core, with SPE initially
48*68f185ccSZachary Leaf  *   configured to write to the first half of the buffer
49*68f185ccSZachary Leaf  * - When the first half of the buffer is full, a buffer full interrupt will
50*68f185ccSZachary Leaf  *   immediately switch writing to the second half. The kernel adds the details
51*68f185ccSZachary Leaf  *   of the half that needs copying to a FIFO STAILQ and notifies userspace via
52*68f185ccSZachary Leaf  *   kqueue by sending a ARM_SPE_KQ_BUF kevent with how many buffers on the
53*68f185ccSZachary Leaf  *   queue need servicing
54*68f185ccSZachary Leaf  * - The kernel responds to HWT_IOC_BUFPTR_GET ioctl by sending details of the
55*68f185ccSZachary Leaf  *   first item from the queue
56*68f185ccSZachary Leaf  * - The buffers pending copying will not be overwritten until an
57*68f185ccSZachary Leaf  *   HWT_IOC_SVC_BUF ioctl is received from userspace confirming the data has
58*68f185ccSZachary Leaf  *   been copied out
59*68f185ccSZachary Leaf  * - In the case where both halfs of the buffer are full, profiling will be
60*68f185ccSZachary Leaf  *   paused until notification via HWT_IOC_SVC_BUF is received
61*68f185ccSZachary Leaf  *
62*68f185ccSZachary Leaf  * Future improvements and limitations
63*68f185ccSZachary Leaf  *
64*68f185ccSZachary Leaf  * - Using large buffer sizes should minimise pauses and loss of profiling
65*68f185ccSZachary Leaf  *   data while kernel is waiting for userspace to copy out data. Since it is
66*68f185ccSZachary Leaf  *   generally expected that consuming (copying) this data is faster than
67*68f185ccSZachary Leaf  *   producing it, in practice this has not so far been an issue. If it does
68*68f185ccSZachary Leaf  *   prove to be an issue even with large buffer sizes then additional buffering
69*68f185ccSZachary Leaf  *   i.e. n element circular buffers might be required.
70*68f185ccSZachary Leaf  *
71*68f185ccSZachary Leaf  * - kqueue can only notify and queue one kevent of the same type, with
72*68f185ccSZachary Leaf  *   subsequent events overwriting data in the first event. The kevent
73*68f185ccSZachary Leaf  *   ARM_SPE_KQ_BUF can therefore only contain the number of buffers on the
74*68f185ccSZachary Leaf  *   STAILQ, incrementing each time a new buffer is full. In this case kqueue
75*68f185ccSZachary Leaf  *   serves just as a notification to userspace to wake up and query the kernel
76*68f185ccSZachary Leaf  *   with the appropriate ioctl. An alternative might be custom kevents where
77*68f185ccSZachary Leaf  *   the kevent identifier is encoded with something like n+cpu_id or n+tid. In
78*68f185ccSZachary Leaf  *   this case data could be sent directly with kqueue via the kevent data and
79*68f185ccSZachary Leaf  *   fflags elements, avoiding the extra ioctl.
80*68f185ccSZachary Leaf  *
81*68f185ccSZachary Leaf  */
82*68f185ccSZachary Leaf 
83*68f185ccSZachary Leaf #include <sys/param.h>
84*68f185ccSZachary Leaf #include <sys/bus.h>
85*68f185ccSZachary Leaf #include <sys/conf.h>
86*68f185ccSZachary Leaf #include <sys/hwt.h>
87*68f185ccSZachary Leaf #include <sys/kernel.h>
88*68f185ccSZachary Leaf #include <sys/lock.h>
89*68f185ccSZachary Leaf #include <sys/malloc.h>
90*68f185ccSZachary Leaf #include <sys/mman.h>
91*68f185ccSZachary Leaf #include <sys/module.h>
92*68f185ccSZachary Leaf #include <sys/mutex.h>
93*68f185ccSZachary Leaf #include <sys/proc.h>
94*68f185ccSZachary Leaf #include <sys/rman.h>
95*68f185ccSZachary Leaf #include <sys/rwlock.h>
96*68f185ccSZachary Leaf #include <sys/smp.h>
97*68f185ccSZachary Leaf #include <sys/sysctl.h>
98*68f185ccSZachary Leaf #include <sys/systm.h>
99*68f185ccSZachary Leaf 
100*68f185ccSZachary Leaf #include <machine/bus.h>
101*68f185ccSZachary Leaf 
102*68f185ccSZachary Leaf #include <arm64/spe/arm_spe_dev.h>
103*68f185ccSZachary Leaf 
104*68f185ccSZachary Leaf #include <dev/hwt/hwt_vm.h>
105*68f185ccSZachary Leaf #include <dev/hwt/hwt_backend.h>
106*68f185ccSZachary Leaf #include <dev/hwt/hwt_config.h>
107*68f185ccSZachary Leaf #include <dev/hwt/hwt_context.h>
108*68f185ccSZachary Leaf #include <dev/hwt/hwt_cpu.h>
109*68f185ccSZachary Leaf #include <dev/hwt/hwt_thread.h>
110*68f185ccSZachary Leaf 
111*68f185ccSZachary Leaf MALLOC_DECLARE(M_ARM_SPE);
112*68f185ccSZachary Leaf 
113*68f185ccSZachary Leaf extern u_int mp_maxid;
114*68f185ccSZachary Leaf extern struct taskqueue *taskqueue_arm_spe;
115*68f185ccSZachary Leaf 
116*68f185ccSZachary Leaf int spe_backend_disable_smp(struct hwt_context *ctx);
117*68f185ccSZachary Leaf 
118*68f185ccSZachary Leaf static device_t spe_dev;
119*68f185ccSZachary Leaf static struct hwt_backend_ops spe_ops;
120*68f185ccSZachary Leaf static struct hwt_backend backend = {
121*68f185ccSZachary Leaf 	.ops = &spe_ops,
122*68f185ccSZachary Leaf 	.name = "spe",
123*68f185ccSZachary Leaf 	.kva_req = 1,
124*68f185ccSZachary Leaf };
125*68f185ccSZachary Leaf 
126*68f185ccSZachary Leaf static struct arm_spe_info *spe_info;
127*68f185ccSZachary Leaf 
128*68f185ccSZachary Leaf static int
spe_backend_init_thread(struct hwt_context * ctx)129*68f185ccSZachary Leaf spe_backend_init_thread(struct hwt_context *ctx)
130*68f185ccSZachary Leaf {
131*68f185ccSZachary Leaf 	return (ENOTSUP);
132*68f185ccSZachary Leaf }
133*68f185ccSZachary Leaf 
134*68f185ccSZachary Leaf static void
spe_backend_init_cpu(struct hwt_context * ctx)135*68f185ccSZachary Leaf spe_backend_init_cpu(struct hwt_context *ctx)
136*68f185ccSZachary Leaf {
137*68f185ccSZachary Leaf 	struct arm_spe_info *info;
138*68f185ccSZachary Leaf 	struct arm_spe_softc *sc = device_get_softc(spe_dev);
139*68f185ccSZachary Leaf 	char lock_name[32];
140*68f185ccSZachary Leaf 	char *tmp = "Arm SPE lock/cpu/";
141*68f185ccSZachary Leaf 	int cpu_id;
142*68f185ccSZachary Leaf 
143*68f185ccSZachary Leaf 	spe_info = malloc(sizeof(struct arm_spe_info) * mp_ncpus,
144*68f185ccSZachary Leaf 	   M_ARM_SPE, M_WAITOK | M_ZERO);
145*68f185ccSZachary Leaf 
146*68f185ccSZachary Leaf 	sc->spe_info = spe_info;
147*68f185ccSZachary Leaf 
148*68f185ccSZachary Leaf 	CPU_FOREACH_ISSET(cpu_id, &ctx->cpu_map) {
149*68f185ccSZachary Leaf 		info = &spe_info[cpu_id];
150*68f185ccSZachary Leaf 		info->sc = sc;
151*68f185ccSZachary Leaf 		info->ident = cpu_id;
152*68f185ccSZachary Leaf 		info->buf_info[0].info = info;
153*68f185ccSZachary Leaf 		info->buf_info[0].buf_idx = 0;
154*68f185ccSZachary Leaf 		info->buf_info[1].info = info;
155*68f185ccSZachary Leaf 		info->buf_info[1].buf_idx = 1;
156*68f185ccSZachary Leaf 		snprintf(lock_name, sizeof(lock_name), "%s%d", tmp, cpu_id);
157*68f185ccSZachary Leaf 		mtx_init(&info->lock, lock_name, NULL, MTX_SPIN);
158*68f185ccSZachary Leaf 	}
159*68f185ccSZachary Leaf }
160*68f185ccSZachary Leaf 
161*68f185ccSZachary Leaf static int
spe_backend_init(struct hwt_context * ctx)162*68f185ccSZachary Leaf spe_backend_init(struct hwt_context *ctx)
163*68f185ccSZachary Leaf {
164*68f185ccSZachary Leaf 	struct arm_spe_softc *sc = device_get_softc(spe_dev);
165*68f185ccSZachary Leaf 	int error = 0;
166*68f185ccSZachary Leaf 
167*68f185ccSZachary Leaf 	/*
168*68f185ccSZachary Leaf 	 * HWT currently specifies buffer size must be a multiple of PAGE_SIZE,
169*68f185ccSZachary Leaf 	 * i.e. minimum 4KB + the maximum PMBIDR.Align is 2KB
170*68f185ccSZachary Leaf 	 * This should never happen but it's good to sense check
171*68f185ccSZachary Leaf 	 */
172*68f185ccSZachary Leaf 	if (ctx->bufsize % sc->kva_align != 0)
173*68f185ccSZachary Leaf 		return (EINVAL);
174*68f185ccSZachary Leaf 
175*68f185ccSZachary Leaf 	/*
176*68f185ccSZachary Leaf 	 * Since we're splitting the buffer in half + PMBLIMITR needs to be page
177*68f185ccSZachary Leaf 	 * aligned, minimum buffer size needs to be 2x PAGE_SIZE
178*68f185ccSZachary Leaf 	 */
179*68f185ccSZachary Leaf 	if (ctx->bufsize < (2 * PAGE_SIZE))
180*68f185ccSZachary Leaf 		return (EINVAL);
181*68f185ccSZachary Leaf 
182*68f185ccSZachary Leaf 	sc->ctx = ctx;
183*68f185ccSZachary Leaf 	sc->kqueue_fd = ctx->kqueue_fd;
184*68f185ccSZachary Leaf 	sc->hwt_td = ctx->hwt_td;
185*68f185ccSZachary Leaf 
186*68f185ccSZachary Leaf 	if (ctx->mode == HWT_MODE_THREAD)
187*68f185ccSZachary Leaf 		error = spe_backend_init_thread(ctx);
188*68f185ccSZachary Leaf 	else
189*68f185ccSZachary Leaf 		spe_backend_init_cpu(ctx);
190*68f185ccSZachary Leaf 
191*68f185ccSZachary Leaf 	return (error);
192*68f185ccSZachary Leaf }
193*68f185ccSZachary Leaf 
194*68f185ccSZachary Leaf #ifdef ARM_SPE_DEBUG
hex_dump(uint8_t * buf,size_t len)195*68f185ccSZachary Leaf static void hex_dump(uint8_t *buf, size_t len)
196*68f185ccSZachary Leaf {
197*68f185ccSZachary Leaf 	size_t i;
198*68f185ccSZachary Leaf 
199*68f185ccSZachary Leaf 	printf("--------------------------------------------------------------\n");
200*68f185ccSZachary Leaf 	for (i = 0; i < len; ++i) {
201*68f185ccSZachary Leaf 		if (i % 8 == 0) {
202*68f185ccSZachary Leaf 			printf(" ");
203*68f185ccSZachary Leaf 		}
204*68f185ccSZachary Leaf 		if (i % 16 == 0) {
205*68f185ccSZachary Leaf 			if (i != 0) {
206*68f185ccSZachary Leaf 				printf("\r\n");
207*68f185ccSZachary Leaf 			}
208*68f185ccSZachary Leaf 			printf("\t");
209*68f185ccSZachary Leaf 		}
210*68f185ccSZachary Leaf 		printf("%02X ", buf[i]);
211*68f185ccSZachary Leaf 	}
212*68f185ccSZachary Leaf 	printf("\r\n");
213*68f185ccSZachary Leaf }
214*68f185ccSZachary Leaf #endif
215*68f185ccSZachary Leaf 
216*68f185ccSZachary Leaf static int
spe_backend_deinit(struct hwt_context * ctx)217*68f185ccSZachary Leaf spe_backend_deinit(struct hwt_context *ctx)
218*68f185ccSZachary Leaf {
219*68f185ccSZachary Leaf #ifdef ARM_SPE_DEBUG
220*68f185ccSZachary Leaf 	struct arm_spe_info *info;
221*68f185ccSZachary Leaf 	int cpu_id;
222*68f185ccSZachary Leaf 
223*68f185ccSZachary Leaf 	CPU_FOREACH_ISSET(cpu_id, &ctx->cpu_map) {
224*68f185ccSZachary Leaf 		info = &spe_info[cpu_id];
225*68f185ccSZachary Leaf 		hex_dump((void *)info->kvaddr, 128);
226*68f185ccSZachary Leaf 		hex_dump((void *)(info->kvaddr + (info->buf_size/2)), 128);
227*68f185ccSZachary Leaf 	}
228*68f185ccSZachary Leaf #endif
229*68f185ccSZachary Leaf 
230*68f185ccSZachary Leaf 	if (ctx->state == CTX_STATE_RUNNING) {
231*68f185ccSZachary Leaf 		spe_backend_disable_smp(ctx);
232*68f185ccSZachary Leaf 		ctx->state = CTX_STATE_STOPPED;
233*68f185ccSZachary Leaf 	}
234*68f185ccSZachary Leaf 
235*68f185ccSZachary Leaf 	free(spe_info, M_ARM_SPE);
236*68f185ccSZachary Leaf 
237*68f185ccSZachary Leaf 	return (0);
238*68f185ccSZachary Leaf }
239*68f185ccSZachary Leaf 
240*68f185ccSZachary Leaf static uint64_t
arm_spe_min_interval(struct arm_spe_softc * sc)241*68f185ccSZachary Leaf arm_spe_min_interval(struct arm_spe_softc *sc)
242*68f185ccSZachary Leaf {
243*68f185ccSZachary Leaf 	/* IMPLEMENTATION DEFINED */
244*68f185ccSZachary Leaf 	switch (PMSIDR_Interval_VAL(sc->pmsidr))
245*68f185ccSZachary Leaf 	{
246*68f185ccSZachary Leaf 	case PMSIDR_Interval_256:
247*68f185ccSZachary Leaf 		return (256);
248*68f185ccSZachary Leaf 	case PMSIDR_Interval_512:
249*68f185ccSZachary Leaf 		return (512);
250*68f185ccSZachary Leaf 	case PMSIDR_Interval_768:
251*68f185ccSZachary Leaf 		return (768);
252*68f185ccSZachary Leaf 	case PMSIDR_Interval_1024:
253*68f185ccSZachary Leaf 		return (1024);
254*68f185ccSZachary Leaf 	case PMSIDR_Interval_1536:
255*68f185ccSZachary Leaf 		return (1536);
256*68f185ccSZachary Leaf 	case PMSIDR_Interval_2048:
257*68f185ccSZachary Leaf 		return (2048);
258*68f185ccSZachary Leaf 	case PMSIDR_Interval_3072:
259*68f185ccSZachary Leaf 		return (3072);
260*68f185ccSZachary Leaf 	case PMSIDR_Interval_4096:
261*68f185ccSZachary Leaf 		return (4096);
262*68f185ccSZachary Leaf 	default:
263*68f185ccSZachary Leaf 		return (4096);
264*68f185ccSZachary Leaf 	}
265*68f185ccSZachary Leaf }
266*68f185ccSZachary Leaf 
267*68f185ccSZachary Leaf static inline void
arm_spe_set_interval(struct arm_spe_info * info,uint64_t interval)268*68f185ccSZachary Leaf arm_spe_set_interval(struct arm_spe_info *info, uint64_t interval)
269*68f185ccSZachary Leaf {
270*68f185ccSZachary Leaf 	uint64_t min_interval = arm_spe_min_interval(info->sc);
271*68f185ccSZachary Leaf 
272*68f185ccSZachary Leaf 	interval = MAX(interval, min_interval);
273*68f185ccSZachary Leaf 	interval = MIN(interval, 1 << 24);      /* max 24 bits */
274*68f185ccSZachary Leaf 
275*68f185ccSZachary Leaf 	dprintf("%s %lu\n", __func__, interval);
276*68f185ccSZachary Leaf 
277*68f185ccSZachary Leaf 	info->pmsirr &= ~(PMSIRR_INTERVAL_MASK);
278*68f185ccSZachary Leaf 	info->pmsirr |= (interval << PMSIRR_INTERVAL_SHIFT);
279*68f185ccSZachary Leaf }
280*68f185ccSZachary Leaf 
281*68f185ccSZachary Leaf static int
spe_backend_configure(struct hwt_context * ctx,int cpu_id,int session_id)282*68f185ccSZachary Leaf spe_backend_configure(struct hwt_context *ctx, int cpu_id, int session_id)
283*68f185ccSZachary Leaf {
284*68f185ccSZachary Leaf 	struct arm_spe_info *info = &spe_info[cpu_id];
285*68f185ccSZachary Leaf 	struct arm_spe_config *cfg;
286*68f185ccSZachary Leaf 	int err = 0;
287*68f185ccSZachary Leaf 
288*68f185ccSZachary Leaf 	mtx_lock_spin(&info->lock);
289*68f185ccSZachary Leaf 	info->ident = cpu_id;
290*68f185ccSZachary Leaf 	/* Set defaults */
291*68f185ccSZachary Leaf 	info->pmsfcr = 0;
292*68f185ccSZachary Leaf 	info->pmsevfr = 0xFFFFFFFFFFFFFFFFUL;
293*68f185ccSZachary Leaf 	info->pmslatfr = 0;
294*68f185ccSZachary Leaf 	info->pmsirr =
295*68f185ccSZachary Leaf 	    (arm_spe_min_interval(info->sc) << PMSIRR_INTERVAL_SHIFT)
296*68f185ccSZachary Leaf 	    | PMSIRR_RND;
297*68f185ccSZachary Leaf 	info->pmsicr = 0;
298*68f185ccSZachary Leaf 	info->pmscr = PMSCR_TS | PMSCR_PA | PMSCR_CX | PMSCR_E1SPE | PMSCR_E0SPE;
299*68f185ccSZachary Leaf 
300*68f185ccSZachary Leaf 	if (ctx->config != NULL &&
301*68f185ccSZachary Leaf 	    ctx->config_size == sizeof(struct arm_spe_config) &&
302*68f185ccSZachary Leaf 	    ctx->config_version == 1) {
303*68f185ccSZachary Leaf 		cfg = (struct arm_spe_config *)ctx->config;
304*68f185ccSZachary Leaf 		if (cfg->interval)
305*68f185ccSZachary Leaf 			arm_spe_set_interval(info, cfg->interval);
306*68f185ccSZachary Leaf 		if (cfg->level == ARM_SPE_KERNEL_ONLY)
307*68f185ccSZachary Leaf 			info->pmscr &= ~(PMSCR_E0SPE); /* turn off user */
308*68f185ccSZachary Leaf 		if (cfg->level == ARM_SPE_USER_ONLY)
309*68f185ccSZachary Leaf 			info->pmscr &= ~(PMSCR_E1SPE); /* turn off kern */
310*68f185ccSZachary Leaf 		if (cfg->ctx_field)
311*68f185ccSZachary Leaf 			info->ctx_field = cfg->ctx_field;
312*68f185ccSZachary Leaf 	} else
313*68f185ccSZachary Leaf 		err = (EINVAL);
314*68f185ccSZachary Leaf 	mtx_unlock_spin(&info->lock);
315*68f185ccSZachary Leaf 
316*68f185ccSZachary Leaf 	return (err);
317*68f185ccSZachary Leaf }
318*68f185ccSZachary Leaf 
319*68f185ccSZachary Leaf 
320*68f185ccSZachary Leaf static void
arm_spe_enable(void * arg __unused)321*68f185ccSZachary Leaf arm_spe_enable(void *arg __unused)
322*68f185ccSZachary Leaf {
323*68f185ccSZachary Leaf 	struct arm_spe_info *info = &spe_info[PCPU_GET(cpuid)];
324*68f185ccSZachary Leaf 	uint64_t base, limit;
325*68f185ccSZachary Leaf 
326*68f185ccSZachary Leaf 	dprintf("%s on cpu:%d\n", __func__, PCPU_GET(cpuid));
327*68f185ccSZachary Leaf 
328*68f185ccSZachary Leaf 	mtx_lock_spin(&info->lock);
329*68f185ccSZachary Leaf 
330*68f185ccSZachary Leaf 	if (info->ctx_field == ARM_SPE_CTX_CPU_ID)
331*68f185ccSZachary Leaf 		WRITE_SPECIALREG(CONTEXTIDR_EL1_REG, PCPU_GET(cpuid));
332*68f185ccSZachary Leaf 
333*68f185ccSZachary Leaf 	WRITE_SPECIALREG(PMSFCR_EL1_REG, info->pmsfcr);
334*68f185ccSZachary Leaf 	WRITE_SPECIALREG(PMSEVFR_EL1_REG, info->pmsevfr);
335*68f185ccSZachary Leaf 	WRITE_SPECIALREG(PMSLATFR_EL1_REG, info->pmslatfr);
336*68f185ccSZachary Leaf 
337*68f185ccSZachary Leaf 	/* Set the sampling interval */
338*68f185ccSZachary Leaf 	WRITE_SPECIALREG(PMSIRR_EL1_REG, info->pmsirr);
339*68f185ccSZachary Leaf 	isb();
340*68f185ccSZachary Leaf 
341*68f185ccSZachary Leaf 	/* Write 0 here before enabling sampling */
342*68f185ccSZachary Leaf 	WRITE_SPECIALREG(PMSICR_EL1_REG, info->pmsicr);
343*68f185ccSZachary Leaf 	isb();
344*68f185ccSZachary Leaf 
345*68f185ccSZachary Leaf 	base = info->kvaddr;
346*68f185ccSZachary Leaf 	limit = base + (info->buf_size/2);
347*68f185ccSZachary Leaf 	/* Enable the buffer */
348*68f185ccSZachary Leaf 	limit &= PMBLIMITR_LIMIT_MASK; /* Zero lower 12 bits */
349*68f185ccSZachary Leaf 	limit |= PMBLIMITR_E;
350*68f185ccSZachary Leaf 	/* Set the base and limit */
351*68f185ccSZachary Leaf 	WRITE_SPECIALREG(PMBPTR_EL1_REG, base);
352*68f185ccSZachary Leaf 	WRITE_SPECIALREG(PMBLIMITR_EL1_REG, limit);
353*68f185ccSZachary Leaf 	isb();
354*68f185ccSZachary Leaf 
355*68f185ccSZachary Leaf 	/* Enable sampling */
356*68f185ccSZachary Leaf 	WRITE_SPECIALREG(PMSCR_EL1_REG, info->pmscr);
357*68f185ccSZachary Leaf 	isb();
358*68f185ccSZachary Leaf 
359*68f185ccSZachary Leaf 	info->enabled = true;
360*68f185ccSZachary Leaf 
361*68f185ccSZachary Leaf 	mtx_unlock_spin(&info->lock);
362*68f185ccSZachary Leaf }
363*68f185ccSZachary Leaf 
364*68f185ccSZachary Leaf static int
spe_backend_enable_smp(struct hwt_context * ctx)365*68f185ccSZachary Leaf spe_backend_enable_smp(struct hwt_context *ctx)
366*68f185ccSZachary Leaf {
367*68f185ccSZachary Leaf 	struct arm_spe_info *info;
368*68f185ccSZachary Leaf 	struct hwt_vm *vm;
369*68f185ccSZachary Leaf 	int cpu_id;
370*68f185ccSZachary Leaf 
371*68f185ccSZachary Leaf 	HWT_CTX_LOCK(ctx);
372*68f185ccSZachary Leaf 	CPU_FOREACH_ISSET(cpu_id, &ctx->cpu_map) {
373*68f185ccSZachary Leaf 		vm = hwt_cpu_get(ctx, cpu_id)->vm;
374*68f185ccSZachary Leaf 
375*68f185ccSZachary Leaf 		info = &spe_info[cpu_id];
376*68f185ccSZachary Leaf 
377*68f185ccSZachary Leaf 		mtx_lock_spin(&info->lock);
378*68f185ccSZachary Leaf 		info->kvaddr = vm->kvaddr;
379*68f185ccSZachary Leaf 		info->buf_size = ctx->bufsize;
380*68f185ccSZachary Leaf 		mtx_unlock_spin(&info->lock);
381*68f185ccSZachary Leaf 	}
382*68f185ccSZachary Leaf 	HWT_CTX_UNLOCK(ctx);
383*68f185ccSZachary Leaf 
384*68f185ccSZachary Leaf 	cpu_id = CPU_FFS(&ctx->cpu_map) - 1;
385*68f185ccSZachary Leaf 	info = &spe_info[cpu_id];
386*68f185ccSZachary Leaf 	if (info->ctx_field == ARM_SPE_CTX_PID)
387*68f185ccSZachary Leaf 		arm64_pid_in_contextidr = true;
388*68f185ccSZachary Leaf 	else
389*68f185ccSZachary Leaf 		arm64_pid_in_contextidr = false;
390*68f185ccSZachary Leaf 
391*68f185ccSZachary Leaf 	smp_rendezvous_cpus(ctx->cpu_map, smp_no_rendezvous_barrier,
392*68f185ccSZachary Leaf 	    arm_spe_enable, smp_no_rendezvous_barrier, NULL);
393*68f185ccSZachary Leaf 
394*68f185ccSZachary Leaf 	return (0);
395*68f185ccSZachary Leaf }
396*68f185ccSZachary Leaf 
397*68f185ccSZachary Leaf void
arm_spe_disable(void * arg __unused)398*68f185ccSZachary Leaf arm_spe_disable(void *arg __unused)
399*68f185ccSZachary Leaf {
400*68f185ccSZachary Leaf 	struct arm_spe_info *info = &spe_info[PCPU_GET(cpuid)];
401*68f185ccSZachary Leaf 	struct arm_spe_buf_info *buf = &info->buf_info[info->buf_idx];
402*68f185ccSZachary Leaf 
403*68f185ccSZachary Leaf 	if (!info->enabled)
404*68f185ccSZachary Leaf 		return;
405*68f185ccSZachary Leaf 
406*68f185ccSZachary Leaf 	dprintf("%s on cpu:%d\n", __func__, PCPU_GET(cpuid));
407*68f185ccSZachary Leaf 
408*68f185ccSZachary Leaf 	/* Disable profiling */
409*68f185ccSZachary Leaf 	WRITE_SPECIALREG(PMSCR_EL1_REG, 0x0);
410*68f185ccSZachary Leaf 	isb();
411*68f185ccSZachary Leaf 
412*68f185ccSZachary Leaf 	/* Drain any remaining tracing data */
413*68f185ccSZachary Leaf 	psb_csync();
414*68f185ccSZachary Leaf 	dsb(nsh);
415*68f185ccSZachary Leaf 
416*68f185ccSZachary Leaf 	/* Disable the profiling buffer */
417*68f185ccSZachary Leaf 	WRITE_SPECIALREG(PMBLIMITR_EL1_REG, 0);
418*68f185ccSZachary Leaf 	isb();
419*68f185ccSZachary Leaf 
420*68f185ccSZachary Leaf 	/* Clear interrupt status reg */
421*68f185ccSZachary Leaf 	WRITE_SPECIALREG(PMBSR_EL1_REG, 0x0);
422*68f185ccSZachary Leaf 
423*68f185ccSZachary Leaf 	/* Clear PID/CPU_ID from context ID reg */
424*68f185ccSZachary Leaf 	WRITE_SPECIALREG(CONTEXTIDR_EL1_REG, 0);
425*68f185ccSZachary Leaf 
426*68f185ccSZachary Leaf 	mtx_lock_spin(&info->lock);
427*68f185ccSZachary Leaf 	buf->pmbptr = READ_SPECIALREG(PMBPTR_EL1_REG);
428*68f185ccSZachary Leaf 	info->enabled = false;
429*68f185ccSZachary Leaf 	mtx_unlock_spin(&info->lock);
430*68f185ccSZachary Leaf }
431*68f185ccSZachary Leaf 
432*68f185ccSZachary Leaf int
spe_backend_disable_smp(struct hwt_context * ctx)433*68f185ccSZachary Leaf spe_backend_disable_smp(struct hwt_context *ctx)
434*68f185ccSZachary Leaf {
435*68f185ccSZachary Leaf 	struct kevent kev;
436*68f185ccSZachary Leaf 	struct arm_spe_info *info;
437*68f185ccSZachary Leaf 	struct arm_spe_buf_info *buf;
438*68f185ccSZachary Leaf 	int cpu_id;
439*68f185ccSZachary Leaf 	int ret;
440*68f185ccSZachary Leaf 
441*68f185ccSZachary Leaf 	/* Disable and send out remaining data in bufs */
442*68f185ccSZachary Leaf 	smp_rendezvous_cpus(ctx->cpu_map, smp_no_rendezvous_barrier,
443*68f185ccSZachary Leaf 	    arm_spe_disable, smp_no_rendezvous_barrier, NULL);
444*68f185ccSZachary Leaf 
445*68f185ccSZachary Leaf 	CPU_FOREACH_ISSET(cpu_id, &ctx->cpu_map) {
446*68f185ccSZachary Leaf 		info = &spe_info[cpu_id];
447*68f185ccSZachary Leaf 		buf = &info->buf_info[info->buf_idx];
448*68f185ccSZachary Leaf 		arm_spe_send_buffer(buf, 0);
449*68f185ccSZachary Leaf 	}
450*68f185ccSZachary Leaf 
451*68f185ccSZachary Leaf 	arm64_pid_in_contextidr = false;
452*68f185ccSZachary Leaf 
453*68f185ccSZachary Leaf 	/*
454*68f185ccSZachary Leaf 	 * Tracing on all CPUs has been disabled, and we've sent write ptr
455*68f185ccSZachary Leaf 	 * offsets for all bufs - let userspace know it can shutdown
456*68f185ccSZachary Leaf 	 */
457*68f185ccSZachary Leaf 	EV_SET(&kev, ARM_SPE_KQ_SHUTDOWN, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
458*68f185ccSZachary Leaf 	ret = kqfd_register(ctx->kqueue_fd, &kev, ctx->hwt_td, M_WAITOK);
459*68f185ccSZachary Leaf 	if (ret)
460*68f185ccSZachary Leaf 		dprintf("%s kqfd_register ret:%d\n", __func__, ret);
461*68f185ccSZachary Leaf 
462*68f185ccSZachary Leaf 	return (0);
463*68f185ccSZachary Leaf }
464*68f185ccSZachary Leaf 
465*68f185ccSZachary Leaf static void
spe_backend_stop(struct hwt_context * ctx)466*68f185ccSZachary Leaf spe_backend_stop(struct hwt_context *ctx)
467*68f185ccSZachary Leaf {
468*68f185ccSZachary Leaf 	spe_backend_disable_smp(ctx);
469*68f185ccSZachary Leaf }
470*68f185ccSZachary Leaf 
471*68f185ccSZachary Leaf static void
arm_spe_reenable(void * arg __unused)472*68f185ccSZachary Leaf arm_spe_reenable(void *arg __unused)
473*68f185ccSZachary Leaf {
474*68f185ccSZachary Leaf 	struct arm_spe_info *info = &spe_info[PCPU_GET(cpuid)];;
475*68f185ccSZachary Leaf 
476*68f185ccSZachary Leaf 	WRITE_SPECIALREG(PMSCR_EL1_REG, info->pmscr);
477*68f185ccSZachary Leaf 	isb();
478*68f185ccSZachary Leaf }
479*68f185ccSZachary Leaf 
480*68f185ccSZachary Leaf static int
spe_backend_svc_buf(struct hwt_context * ctx,void * data,size_t data_size,int data_version)481*68f185ccSZachary Leaf spe_backend_svc_buf(struct hwt_context *ctx, void *data, size_t data_size,
482*68f185ccSZachary Leaf     int data_version)
483*68f185ccSZachary Leaf {
484*68f185ccSZachary Leaf 	struct arm_spe_info *info;
485*68f185ccSZachary Leaf 	struct arm_spe_buf_info *buf;
486*68f185ccSZachary Leaf 	struct arm_spe_svc_buf *s;
487*68f185ccSZachary Leaf 	int err = 0;
488*68f185ccSZachary Leaf 	cpuset_t cpu_set;
489*68f185ccSZachary Leaf 
490*68f185ccSZachary Leaf 	if (data_size != sizeof(struct arm_spe_svc_buf))
491*68f185ccSZachary Leaf 		return (E2BIG);
492*68f185ccSZachary Leaf 
493*68f185ccSZachary Leaf 	if (data_version != 1)
494*68f185ccSZachary Leaf 		return (EINVAL);
495*68f185ccSZachary Leaf 
496*68f185ccSZachary Leaf 	s = (struct arm_spe_svc_buf *)data;
497*68f185ccSZachary Leaf 	if (s->buf_idx > 1)
498*68f185ccSZachary Leaf 		return (ENODEV);
499*68f185ccSZachary Leaf 	if (s->ident >= mp_ncpus)
500*68f185ccSZachary Leaf 		return (EINVAL);
501*68f185ccSZachary Leaf 
502*68f185ccSZachary Leaf 	info = &spe_info[s->ident];
503*68f185ccSZachary Leaf 	mtx_lock_spin(&info->lock);
504*68f185ccSZachary Leaf 
505*68f185ccSZachary Leaf 	buf = &info->buf_info[s->buf_idx];
506*68f185ccSZachary Leaf 
507*68f185ccSZachary Leaf 	if (!info->enabled) {
508*68f185ccSZachary Leaf 		err = ENXIO;
509*68f185ccSZachary Leaf 		goto end;
510*68f185ccSZachary Leaf 	}
511*68f185ccSZachary Leaf 
512*68f185ccSZachary Leaf 	/* Clear the flag the signals buffer needs servicing */
513*68f185ccSZachary Leaf 	buf->buf_svc = false;
514*68f185ccSZachary Leaf 
515*68f185ccSZachary Leaf 	/* Re-enable profiling if we've been waiting for this notification */
516*68f185ccSZachary Leaf 	if (buf->buf_wait) {
517*68f185ccSZachary Leaf 		CPU_SETOF(s->ident, &cpu_set);
518*68f185ccSZachary Leaf 
519*68f185ccSZachary Leaf 		mtx_unlock_spin(&info->lock);
520*68f185ccSZachary Leaf 		smp_rendezvous_cpus(cpu_set, smp_no_rendezvous_barrier,
521*68f185ccSZachary Leaf 		    arm_spe_reenable, smp_no_rendezvous_barrier, NULL);
522*68f185ccSZachary Leaf 		mtx_lock_spin(&info->lock);
523*68f185ccSZachary Leaf 
524*68f185ccSZachary Leaf 		buf->buf_wait = false;
525*68f185ccSZachary Leaf 	}
526*68f185ccSZachary Leaf 
527*68f185ccSZachary Leaf end:
528*68f185ccSZachary Leaf 	mtx_unlock_spin(&info->lock);
529*68f185ccSZachary Leaf 	return (err);
530*68f185ccSZachary Leaf }
531*68f185ccSZachary Leaf 
532*68f185ccSZachary Leaf static int
spe_backend_read(struct hwt_vm * vm,int * ident,vm_offset_t * offset,uint64_t * data)533*68f185ccSZachary Leaf spe_backend_read(struct hwt_vm *vm, int *ident, vm_offset_t *offset,
534*68f185ccSZachary Leaf     uint64_t *data)
535*68f185ccSZachary Leaf {
536*68f185ccSZachary Leaf 	struct arm_spe_queue *q;
537*68f185ccSZachary Leaf 	struct arm_spe_softc *sc = device_get_softc(spe_dev);
538*68f185ccSZachary Leaf 	int error = 0;
539*68f185ccSZachary Leaf 
540*68f185ccSZachary Leaf 	mtx_lock_spin(&sc->sc_lock);
541*68f185ccSZachary Leaf 
542*68f185ccSZachary Leaf 	/* Return the first pending buffer that needs servicing */
543*68f185ccSZachary Leaf 	q = STAILQ_FIRST(&sc->pending);
544*68f185ccSZachary Leaf 	if (q == NULL) {
545*68f185ccSZachary Leaf 		error = ENOENT;
546*68f185ccSZachary Leaf 		goto error;
547*68f185ccSZachary Leaf 	}
548*68f185ccSZachary Leaf 	*ident = q->ident;
549*68f185ccSZachary Leaf 	*offset = q->offset;
550*68f185ccSZachary Leaf 	*data = (q->buf_idx << KQ_BUF_POS_SHIFT) |
551*68f185ccSZachary Leaf 	    (q->partial_rec << KQ_PARTREC_SHIFT) |
552*68f185ccSZachary Leaf 	    (q->final_buf << KQ_FINAL_BUF_SHIFT);
553*68f185ccSZachary Leaf 
554*68f185ccSZachary Leaf 	STAILQ_REMOVE_HEAD(&sc->pending, next);
555*68f185ccSZachary Leaf 	sc->npending--;
556*68f185ccSZachary Leaf 
557*68f185ccSZachary Leaf error:
558*68f185ccSZachary Leaf 	mtx_unlock_spin(&sc->sc_lock);
559*68f185ccSZachary Leaf 	if (error)
560*68f185ccSZachary Leaf 		return (error);
561*68f185ccSZachary Leaf 
562*68f185ccSZachary Leaf 	free(q, M_ARM_SPE);
563*68f185ccSZachary Leaf 	return (0);
564*68f185ccSZachary Leaf }
565*68f185ccSZachary Leaf 
566*68f185ccSZachary Leaf static struct hwt_backend_ops spe_ops = {
567*68f185ccSZachary Leaf 	.hwt_backend_init = spe_backend_init,
568*68f185ccSZachary Leaf 	.hwt_backend_deinit = spe_backend_deinit,
569*68f185ccSZachary Leaf 
570*68f185ccSZachary Leaf 	.hwt_backend_configure = spe_backend_configure,
571*68f185ccSZachary Leaf 	.hwt_backend_svc_buf = spe_backend_svc_buf,
572*68f185ccSZachary Leaf 	.hwt_backend_stop = spe_backend_stop,
573*68f185ccSZachary Leaf 
574*68f185ccSZachary Leaf 	.hwt_backend_enable_smp = spe_backend_enable_smp,
575*68f185ccSZachary Leaf 	.hwt_backend_disable_smp = spe_backend_disable_smp,
576*68f185ccSZachary Leaf 
577*68f185ccSZachary Leaf 	.hwt_backend_read = spe_backend_read,
578*68f185ccSZachary Leaf };
579*68f185ccSZachary Leaf 
580*68f185ccSZachary Leaf int
spe_register(device_t dev)581*68f185ccSZachary Leaf spe_register(device_t dev)
582*68f185ccSZachary Leaf {
583*68f185ccSZachary Leaf 	spe_dev = dev;
584*68f185ccSZachary Leaf 
585*68f185ccSZachary Leaf 	return (hwt_backend_register(&backend));
586*68f185ccSZachary Leaf }
587