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