1278529Sgnn/*
2278529Sgnn * CDDL HEADER START
3278529Sgnn *
4278529Sgnn * The contents of this file are subject to the terms of the
5278529Sgnn * Common Development and Distribution License, Version 1.0 only
6278529Sgnn * (the "License").  You may not use this file except in compliance
7278529Sgnn * with the License.
8278529Sgnn *
9278529Sgnn * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10278529Sgnn * or http://www.opensolaris.org/os/licensing.
11278529Sgnn * See the License for the specific language governing permissions
12278529Sgnn * and limitations under the License.
13278529Sgnn *
14278529Sgnn * When distributing Covered Code, include this CDDL HEADER in each
15278529Sgnn * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16278529Sgnn * If applicable, add the following below this CDDL HEADER, with the
17278529Sgnn * fields enclosed by brackets "[]" replaced with your own identifying
18278529Sgnn * information: Portions Copyright [yyyy] [name of copyright owner]
19278529Sgnn *
20278529Sgnn * CDDL HEADER END
21278529Sgnn *
22278529Sgnn * $FreeBSD: stable/11/sys/cddl/dev/dtrace/arm/dtrace_subr.c 328386 2018-01-25 02:45:21Z pkelsey $
23278529Sgnn *
24278529Sgnn */
25278529Sgnn/*
26278529Sgnn * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
27278529Sgnn * Use is subject to license terms.
28278529Sgnn */
29278529Sgnn
30278529Sgnn#include <sys/cdefs.h>
31278529Sgnn__FBSDID("$FreeBSD: stable/11/sys/cddl/dev/dtrace/arm/dtrace_subr.c 328386 2018-01-25 02:45:21Z pkelsey $");
32278529Sgnn
33278529Sgnn#include <sys/param.h>
34278529Sgnn#include <sys/systm.h>
35278529Sgnn#include <sys/types.h>
36278529Sgnn#include <sys/kernel.h>
37278529Sgnn#include <sys/malloc.h>
38278529Sgnn#include <sys/kmem.h>
39278529Sgnn#include <sys/smp.h>
40278529Sgnn#include <sys/dtrace_impl.h>
41278529Sgnn#include <sys/dtrace_bsd.h>
42278529Sgnn#include <machine/armreg.h>
43278529Sgnn#include <machine/clock.h>
44278529Sgnn#include <machine/frame.h>
45278529Sgnn#include <machine/trap.h>
46278529Sgnn#include <vm/pmap.h>
47278529Sgnn
48278529Sgnn#define	DELAYBRANCH(x)	((int)(x) < 0)
49279667Sandrew
50279667Sandrew#define	BIT_PC		15
51279667Sandrew#define	BIT_LR		14
52279667Sandrew#define	BIT_SP		13
53279667Sandrew
54278529Sgnnextern dtrace_id_t	dtrace_probeid_error;
55278529Sgnnextern int (*dtrace_invop_jump_addr)(struct trapframe *);
56279721Sgnnextern void dtrace_getnanotime(struct timespec *tsp);
57278529Sgnn
58298171Smarkjint dtrace_invop(uintptr_t, struct trapframe *, uintptr_t);
59278529Sgnnvoid dtrace_invop_init(void);
60278529Sgnnvoid dtrace_invop_uninit(void);
61278529Sgnn
62278529Sgnntypedef struct dtrace_invop_hdlr {
63298171Smarkj	int (*dtih_func)(uintptr_t, struct trapframe *, uintptr_t);
64278529Sgnn	struct dtrace_invop_hdlr *dtih_next;
65278529Sgnn} dtrace_invop_hdlr_t;
66278529Sgnn
67278529Sgnndtrace_invop_hdlr_t *dtrace_invop_hdlr;
68278529Sgnn
69278529Sgnnint
70298171Smarkjdtrace_invop(uintptr_t addr, struct trapframe *frame, uintptr_t eax)
71278529Sgnn{
72278529Sgnn	dtrace_invop_hdlr_t *hdlr;
73278529Sgnn	int rval;
74278529Sgnn
75278529Sgnn	for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next)
76298171Smarkj		if ((rval = hdlr->dtih_func(addr, frame, eax)) != 0)
77278529Sgnn			return (rval);
78278529Sgnn
79278529Sgnn	return (0);
80278529Sgnn}
81278529Sgnn
82278529Sgnn
83278529Sgnnvoid
84298171Smarkjdtrace_invop_add(int (*func)(uintptr_t, struct trapframe *, uintptr_t))
85278529Sgnn{
86278529Sgnn	dtrace_invop_hdlr_t *hdlr;
87278529Sgnn
88278529Sgnn	hdlr = kmem_alloc(sizeof (dtrace_invop_hdlr_t), KM_SLEEP);
89278529Sgnn	hdlr->dtih_func = func;
90278529Sgnn	hdlr->dtih_next = dtrace_invop_hdlr;
91278529Sgnn	dtrace_invop_hdlr = hdlr;
92278529Sgnn}
93278529Sgnn
94278529Sgnnvoid
95298171Smarkjdtrace_invop_remove(int (*func)(uintptr_t, struct trapframe *, uintptr_t))
96278529Sgnn{
97278529Sgnn	dtrace_invop_hdlr_t *hdlr = dtrace_invop_hdlr, *prev = NULL;
98278529Sgnn
99278529Sgnn	for (;;) {
100278529Sgnn		if (hdlr == NULL)
101278529Sgnn			panic("attempt to remove non-existent invop handler");
102278529Sgnn
103278529Sgnn		if (hdlr->dtih_func == func)
104278529Sgnn			break;
105278529Sgnn
106278529Sgnn		prev = hdlr;
107278529Sgnn		hdlr = hdlr->dtih_next;
108278529Sgnn	}
109278529Sgnn
110278529Sgnn	if (prev == NULL) {
111278529Sgnn		ASSERT(dtrace_invop_hdlr == hdlr);
112278529Sgnn		dtrace_invop_hdlr = hdlr->dtih_next;
113278529Sgnn	} else {
114278529Sgnn		ASSERT(dtrace_invop_hdlr != hdlr);
115278529Sgnn		prev->dtih_next = hdlr->dtih_next;
116278529Sgnn	}
117278529Sgnn
118278529Sgnn	kmem_free(hdlr, 0);
119278529Sgnn}
120278529Sgnn
121278529Sgnn
122278529Sgnn/*ARGSUSED*/
123278529Sgnnvoid
124278529Sgnndtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit))
125278529Sgnn{
126278529Sgnn	printf("IMPLEMENT ME: dtrace_toxic_ranges\n");
127278529Sgnn}
128278529Sgnn
129278529Sgnnvoid
130278529Sgnndtrace_xcall(processorid_t cpu, dtrace_xcall_t func, void *arg)
131278529Sgnn{
132278529Sgnn	cpuset_t cpus;
133278529Sgnn
134278529Sgnn	if (cpu == DTRACE_CPUALL)
135278529Sgnn		cpus = all_cpus;
136278529Sgnn	else
137278529Sgnn		CPU_SETOF(cpu, &cpus);
138278529Sgnn
139328386Spkelsey	smp_rendezvous_cpus(cpus, smp_no_rendezvous_barrier, func,
140328386Spkelsey	    smp_no_rendezvous_barrier, arg);
141278529Sgnn}
142278529Sgnn
143278529Sgnnstatic void
144278529Sgnndtrace_sync_func(void)
145278529Sgnn{
146278529Sgnn}
147278529Sgnn
148278529Sgnnvoid
149278529Sgnndtrace_sync(void)
150278529Sgnn{
151278529Sgnn	dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL);
152278529Sgnn}
153278529Sgnn
154278529Sgnn/*
155278529Sgnn * DTrace needs a high resolution time function which can
156278529Sgnn * be called from a probe context and guaranteed not to have
157278529Sgnn * instrumented with probes itself.
158278529Sgnn *
159278529Sgnn * Returns nanoseconds since boot.
160278529Sgnn */
161278529Sgnnuint64_t
162278529Sgnndtrace_gethrtime()
163278529Sgnn{
164278529Sgnn	struct	timespec curtime;
165278529Sgnn
166278529Sgnn	nanouptime(&curtime);
167278529Sgnn
168278529Sgnn	return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec);
169278529Sgnn
170278529Sgnn}
171278529Sgnn
172278529Sgnnuint64_t
173278529Sgnndtrace_gethrestime(void)
174278529Sgnn{
175279721Sgnn	struct timespec current_time;
176278529Sgnn
177279721Sgnn	dtrace_getnanotime(&current_time);
178278529Sgnn
179279721Sgnn	return (current_time.tv_sec * 1000000000UL + current_time.tv_nsec);
180278529Sgnn}
181278529Sgnn
182278529Sgnn/* Function to handle DTrace traps during probes. See amd64/amd64/trap.c */
183278529Sgnnint
184278529Sgnndtrace_trap(struct trapframe *frame, u_int type)
185278529Sgnn{
186278529Sgnn	/*
187278529Sgnn	 * A trap can occur while DTrace executes a probe. Before
188278529Sgnn	 * executing the probe, DTrace blocks re-scheduling and sets
189278529Sgnn	 * a flag in it's per-cpu flags to indicate that it doesn't
190278529Sgnn	 * want to fault. On returning from the probe, the no-fault
191278529Sgnn	 * flag is cleared and finally re-scheduling is enabled.
192278529Sgnn	 *
193278529Sgnn	 * Check if DTrace has enabled 'no-fault' mode:
194278529Sgnn	 *
195278529Sgnn	 */
196278529Sgnn	if ((cpu_core[curcpu].cpuc_dtrace_flags & CPU_DTRACE_NOFAULT) != 0) {
197278529Sgnn		/*
198278529Sgnn		 * There are only a couple of trap types that are expected.
199278529Sgnn		 * All the rest will be handled in the usual way.
200278529Sgnn		 */
201278529Sgnn		switch (type) {
202278529Sgnn		/* Page fault. */
203278529Sgnn		case FAULT_ALIGN:
204278529Sgnn			/* Flag a bad address. */
205278529Sgnn			cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
206278529Sgnn			cpu_core[curcpu].cpuc_dtrace_illval = 0;
207278529Sgnn
208278529Sgnn			/*
209278529Sgnn			 * Offset the instruction pointer to the instruction
210278529Sgnn			 * following the one causing the fault.
211278529Sgnn			 */
212278529Sgnn			frame->tf_pc += sizeof(int);
213278529Sgnn			return (1);
214278529Sgnn		default:
215278529Sgnn			/* Handle all other traps in the usual way. */
216278529Sgnn			break;
217278529Sgnn		}
218278529Sgnn	}
219278529Sgnn
220278529Sgnn	/* Handle the trap in the usual way. */
221278529Sgnn	return (0);
222278529Sgnn}
223278529Sgnn
224278529Sgnnvoid
225278529Sgnndtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which,
226278529Sgnn    int fault, int fltoffs, uintptr_t illval)
227278529Sgnn{
228278529Sgnn
229278529Sgnn	dtrace_probe(dtrace_probeid_error, (uint64_t)(uintptr_t)state,
230278529Sgnn	    (uintptr_t)epid,
231278529Sgnn	    (uintptr_t)which, (uintptr_t)fault, (uintptr_t)fltoffs);
232278529Sgnn}
233278529Sgnn
234278529Sgnnstatic int
235278529Sgnndtrace_invop_start(struct trapframe *frame)
236278529Sgnn{
237279667Sandrew	register_t *r0, *sp;
238279667Sandrew	int data, invop, reg, update_sp;
239279667Sandrew
240298171Smarkj	invop = dtrace_invop(frame->tf_pc, frame, frame->tf_pc);
241279667Sandrew	switch (invop & DTRACE_INVOP_MASK) {
242278529Sgnn	case DTRACE_INVOP_PUSHM:
243279667Sandrew		sp = (register_t *)frame->tf_svc_sp;
244279667Sandrew		r0 = &frame->tf_r0;
245279667Sandrew		data = DTRACE_INVOP_DATA(invop);
246279667Sandrew
247279667Sandrew		/*
248279667Sandrew		 * Store the pc, lr, and sp. These have their own
249279667Sandrew		 * entries in the struct.
250279667Sandrew		 */
251279667Sandrew		if (data & (1 << BIT_PC)) {
252279667Sandrew			sp--;
253279667Sandrew			*sp = frame->tf_pc;
254279667Sandrew		}
255279667Sandrew		if (data & (1 << BIT_LR)) {
256279667Sandrew			sp--;
257279667Sandrew			*sp = frame->tf_svc_lr;
258279667Sandrew		}
259279667Sandrew		if (data & (1 << BIT_SP)) {
260279667Sandrew			sp--;
261279667Sandrew			*sp = frame->tf_svc_sp;
262279667Sandrew		}
263279667Sandrew
264279667Sandrew		/* Store the general registers */
265279667Sandrew		for (reg = 12; reg >= 0; reg--) {
266279667Sandrew			if (data & (1 << reg)) {
267279667Sandrew				sp--;
268279667Sandrew				*sp = r0[reg];
269279667Sandrew			}
270279667Sandrew		}
271279667Sandrew
272279667Sandrew		/* Update the stack pointer and program counter to continue */
273279667Sandrew		frame->tf_svc_sp = (register_t)sp;
274279667Sandrew		frame->tf_pc += 4;
275278529Sgnn		break;
276278529Sgnn	case DTRACE_INVOP_POPM:
277279667Sandrew		sp = (register_t *)frame->tf_svc_sp;
278279667Sandrew		r0 = &frame->tf_r0;
279279667Sandrew		data = DTRACE_INVOP_DATA(invop);
280279667Sandrew
281279667Sandrew		/* Read the general registers */
282279667Sandrew		for (reg = 0; reg <= 12; reg++) {
283279667Sandrew			if (data & (1 << reg)) {
284279667Sandrew				r0[reg] = *sp;
285279667Sandrew				sp++;
286279667Sandrew			}
287279667Sandrew		}
288279667Sandrew
289279667Sandrew		/*
290279667Sandrew		 * Set the stack pointer. If we don't update it here we will
291279667Sandrew		 * need to update it at the end as the instruction would do
292279667Sandrew		 */
293279667Sandrew		update_sp = 1;
294279667Sandrew		if (data & (1 << BIT_SP)) {
295279667Sandrew			frame->tf_svc_sp = *sp;
296279667Sandrew			*sp++;
297279667Sandrew			update_sp = 0;
298279667Sandrew		}
299279667Sandrew
300279667Sandrew		/* Update the link register, we need to use the correct copy */
301279667Sandrew		if (data & (1 << BIT_LR)) {
302279667Sandrew			frame->tf_svc_lr = *sp;
303279667Sandrew			*sp++;
304279667Sandrew		}
305279667Sandrew		/*
306279667Sandrew		 * And the program counter. If it's not in the list skip over
307279667Sandrew		 * it when we return so to not hit this again.
308279667Sandrew		 */
309279667Sandrew		if (data & (1 << BIT_PC)) {
310279667Sandrew			frame->tf_pc = *sp;
311279667Sandrew			*sp++;
312279667Sandrew		} else
313279667Sandrew			frame->tf_pc += 4;
314279667Sandrew
315279667Sandrew		/* Update the stack pointer if we haven't already done so */
316279667Sandrew		if (update_sp)
317279667Sandrew			frame->tf_svc_sp = (register_t)sp;
318278529Sgnn		break;
319278529Sgnn	case DTRACE_INVOP_B:
320279667Sandrew		data = DTRACE_INVOP_DATA(invop) & 0x00ffffff;
321279667Sandrew		/* Sign extend the data */
322279667Sandrew		if ((data & (1 << 23)) != 0)
323279667Sandrew			data |= 0xff000000;
324279667Sandrew		/* The data is the number of 4-byte words to change the pc */
325279667Sandrew		data *= 4;
326279667Sandrew		data += 8;
327279667Sandrew		frame->tf_pc += data;
328278529Sgnn		break;
329278529Sgnn	default:
330278529Sgnn		return (-1);
331278529Sgnn		break;
332278529Sgnn	}
333278529Sgnn
334278529Sgnn	return (0);
335278529Sgnn}
336278529Sgnn
337278529Sgnnvoid dtrace_invop_init(void)
338278529Sgnn{
339278529Sgnn	dtrace_invop_jump_addr = dtrace_invop_start;
340278529Sgnn}
341278529Sgnn
342278529Sgnnvoid dtrace_invop_uninit(void)
343278529Sgnn{
344278529Sgnn	dtrace_invop_jump_addr = 0;
345278529Sgnn}
346