dtrace_subr.c revision 279667
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: head/sys/cddl/dev/dtrace/arm/dtrace_subr.c 279667 2015-03-05 17:55:31Z andrew $
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: head/sys/cddl/dev/dtrace/arm/dtrace_subr.c 279667 2015-03-05 17:55:31Z andrew $");
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 uintptr_t 	dtrace_in_probe_addr;
55278529Sgnnextern int		dtrace_in_probe;
56278529Sgnnextern dtrace_id_t	dtrace_probeid_error;
57278529Sgnnextern int (*dtrace_invop_jump_addr)(struct trapframe *);
58278529Sgnn
59278529Sgnnint dtrace_invop(uintptr_t, uintptr_t *, uintptr_t);
60278529Sgnnvoid dtrace_invop_init(void);
61278529Sgnnvoid dtrace_invop_uninit(void);
62278529Sgnn
63278529Sgnntypedef struct dtrace_invop_hdlr {
64278529Sgnn	int (*dtih_func)(uintptr_t, uintptr_t *, uintptr_t);
65278529Sgnn	struct dtrace_invop_hdlr *dtih_next;
66278529Sgnn} dtrace_invop_hdlr_t;
67278529Sgnn
68278529Sgnndtrace_invop_hdlr_t *dtrace_invop_hdlr;
69278529Sgnn
70278529Sgnnint
71278529Sgnndtrace_invop(uintptr_t addr, uintptr_t *stack, uintptr_t eax)
72278529Sgnn{
73278529Sgnn	dtrace_invop_hdlr_t *hdlr;
74278529Sgnn	int rval;
75278529Sgnn
76278529Sgnn	for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next)
77278529Sgnn		if ((rval = hdlr->dtih_func(addr, stack, eax)) != 0)
78278529Sgnn			return (rval);
79278529Sgnn
80278529Sgnn	return (0);
81278529Sgnn}
82278529Sgnn
83278529Sgnn
84278529Sgnnvoid
85278529Sgnndtrace_invop_add(int (*func)(uintptr_t, uintptr_t *, uintptr_t))
86278529Sgnn{
87278529Sgnn	dtrace_invop_hdlr_t *hdlr;
88278529Sgnn
89278529Sgnn	hdlr = kmem_alloc(sizeof (dtrace_invop_hdlr_t), KM_SLEEP);
90278529Sgnn	hdlr->dtih_func = func;
91278529Sgnn	hdlr->dtih_next = dtrace_invop_hdlr;
92278529Sgnn	dtrace_invop_hdlr = hdlr;
93278529Sgnn}
94278529Sgnn
95278529Sgnnvoid
96278529Sgnndtrace_invop_remove(int (*func)(uintptr_t, uintptr_t *, uintptr_t))
97278529Sgnn{
98278529Sgnn	dtrace_invop_hdlr_t *hdlr = dtrace_invop_hdlr, *prev = NULL;
99278529Sgnn
100278529Sgnn	for (;;) {
101278529Sgnn		if (hdlr == NULL)
102278529Sgnn			panic("attempt to remove non-existent invop handler");
103278529Sgnn
104278529Sgnn		if (hdlr->dtih_func == func)
105278529Sgnn			break;
106278529Sgnn
107278529Sgnn		prev = hdlr;
108278529Sgnn		hdlr = hdlr->dtih_next;
109278529Sgnn	}
110278529Sgnn
111278529Sgnn	if (prev == NULL) {
112278529Sgnn		ASSERT(dtrace_invop_hdlr == hdlr);
113278529Sgnn		dtrace_invop_hdlr = hdlr->dtih_next;
114278529Sgnn	} else {
115278529Sgnn		ASSERT(dtrace_invop_hdlr != hdlr);
116278529Sgnn		prev->dtih_next = hdlr->dtih_next;
117278529Sgnn	}
118278529Sgnn
119278529Sgnn	kmem_free(hdlr, 0);
120278529Sgnn}
121278529Sgnn
122278529Sgnn
123278529Sgnn/*ARGSUSED*/
124278529Sgnnvoid
125278529Sgnndtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit))
126278529Sgnn{
127278529Sgnn	printf("IMPLEMENT ME: dtrace_toxic_ranges\n");
128278529Sgnn}
129278529Sgnn
130278529Sgnnvoid
131278529Sgnndtrace_xcall(processorid_t cpu, dtrace_xcall_t func, void *arg)
132278529Sgnn{
133278529Sgnn	cpuset_t cpus;
134278529Sgnn
135278529Sgnn	if (cpu == DTRACE_CPUALL)
136278529Sgnn		cpus = all_cpus;
137278529Sgnn	else
138278529Sgnn		CPU_SETOF(cpu, &cpus);
139278529Sgnn
140278529Sgnn	smp_rendezvous_cpus(cpus, smp_no_rendevous_barrier, func,
141278529Sgnn	    smp_no_rendevous_barrier, arg);
142278529Sgnn}
143278529Sgnn
144278529Sgnnstatic void
145278529Sgnndtrace_sync_func(void)
146278529Sgnn{
147278529Sgnn}
148278529Sgnn
149278529Sgnnvoid
150278529Sgnndtrace_sync(void)
151278529Sgnn{
152278529Sgnn	dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL);
153278529Sgnn}
154278529Sgnn
155278529Sgnn/*
156278529Sgnn * DTrace needs a high resolution time function which can
157278529Sgnn * be called from a probe context and guaranteed not to have
158278529Sgnn * instrumented with probes itself.
159278529Sgnn *
160278529Sgnn * Returns nanoseconds since boot.
161278529Sgnn */
162278529Sgnnuint64_t
163278529Sgnndtrace_gethrtime()
164278529Sgnn{
165278529Sgnn	struct	timespec curtime;
166278529Sgnn
167278529Sgnn	nanouptime(&curtime);
168278529Sgnn
169278529Sgnn	return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec);
170278529Sgnn
171278529Sgnn}
172278529Sgnn
173278529Sgnnuint64_t
174278529Sgnndtrace_gethrestime(void)
175278529Sgnn{
176278529Sgnn	struct	timespec curtime;
177278529Sgnn
178278529Sgnn	getnanotime(&curtime);
179278529Sgnn
180278529Sgnn	return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec);
181278529Sgnn}
182278529Sgnn
183278529Sgnn/* Function to handle DTrace traps during probes. See amd64/amd64/trap.c */
184278529Sgnnint
185278529Sgnndtrace_trap(struct trapframe *frame, u_int type)
186278529Sgnn{
187278529Sgnn	/*
188278529Sgnn	 * A trap can occur while DTrace executes a probe. Before
189278529Sgnn	 * executing the probe, DTrace blocks re-scheduling and sets
190278529Sgnn	 * a flag in it's per-cpu flags to indicate that it doesn't
191278529Sgnn	 * want to fault. On returning from the probe, the no-fault
192278529Sgnn	 * flag is cleared and finally re-scheduling is enabled.
193278529Sgnn	 *
194278529Sgnn	 * Check if DTrace has enabled 'no-fault' mode:
195278529Sgnn	 *
196278529Sgnn	 */
197278529Sgnn	if ((cpu_core[curcpu].cpuc_dtrace_flags & CPU_DTRACE_NOFAULT) != 0) {
198278529Sgnn		/*
199278529Sgnn		 * There are only a couple of trap types that are expected.
200278529Sgnn		 * All the rest will be handled in the usual way.
201278529Sgnn		 */
202278529Sgnn		switch (type) {
203278529Sgnn		/* Page fault. */
204278529Sgnn		case FAULT_ALIGN:
205278529Sgnn			/* Flag a bad address. */
206278529Sgnn			cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
207278529Sgnn			cpu_core[curcpu].cpuc_dtrace_illval = 0;
208278529Sgnn
209278529Sgnn			/*
210278529Sgnn			 * Offset the instruction pointer to the instruction
211278529Sgnn			 * following the one causing the fault.
212278529Sgnn			 */
213278529Sgnn			frame->tf_pc += sizeof(int);
214278529Sgnn			return (1);
215278529Sgnn		default:
216278529Sgnn			/* Handle all other traps in the usual way. */
217278529Sgnn			break;
218278529Sgnn		}
219278529Sgnn	}
220278529Sgnn
221278529Sgnn	/* Handle the trap in the usual way. */
222278529Sgnn	return (0);
223278529Sgnn}
224278529Sgnn
225278529Sgnnvoid
226278529Sgnndtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which,
227278529Sgnn    int fault, int fltoffs, uintptr_t illval)
228278529Sgnn{
229278529Sgnn
230278529Sgnn	dtrace_probe(dtrace_probeid_error, (uint64_t)(uintptr_t)state,
231278529Sgnn	    (uintptr_t)epid,
232278529Sgnn	    (uintptr_t)which, (uintptr_t)fault, (uintptr_t)fltoffs);
233278529Sgnn}
234278529Sgnn
235278529Sgnnstatic int
236278529Sgnndtrace_invop_start(struct trapframe *frame)
237278529Sgnn{
238279667Sandrew	register_t *r0, *sp;
239279667Sandrew	int data, invop, reg, update_sp;
240279667Sandrew
241279667Sandrew	invop = dtrace_invop(frame->tf_pc, (uintptr_t *)frame, frame->tf_pc);
242279667Sandrew	switch (invop & DTRACE_INVOP_MASK) {
243278529Sgnn	case DTRACE_INVOP_PUSHM:
244279667Sandrew		sp = (register_t *)frame->tf_svc_sp;
245279667Sandrew		r0 = &frame->tf_r0;
246279667Sandrew		data = DTRACE_INVOP_DATA(invop);
247279667Sandrew
248279667Sandrew		/*
249279667Sandrew		 * Store the pc, lr, and sp. These have their own
250279667Sandrew		 * entries in the struct.
251279667Sandrew		 */
252279667Sandrew		if (data & (1 << BIT_PC)) {
253279667Sandrew			sp--;
254279667Sandrew			*sp = frame->tf_pc;
255279667Sandrew		}
256279667Sandrew		if (data & (1 << BIT_LR)) {
257279667Sandrew			sp--;
258279667Sandrew			*sp = frame->tf_svc_lr;
259279667Sandrew		}
260279667Sandrew		if (data & (1 << BIT_SP)) {
261279667Sandrew			sp--;
262279667Sandrew			*sp = frame->tf_svc_sp;
263279667Sandrew		}
264279667Sandrew
265279667Sandrew		/* Store the general registers */
266279667Sandrew		for (reg = 12; reg >= 0; reg--) {
267279667Sandrew			if (data & (1 << reg)) {
268279667Sandrew				sp--;
269279667Sandrew				*sp = r0[reg];
270279667Sandrew			}
271279667Sandrew		}
272279667Sandrew
273279667Sandrew		/* Update the stack pointer and program counter to continue */
274279667Sandrew		frame->tf_svc_sp = (register_t)sp;
275279667Sandrew		frame->tf_pc += 4;
276278529Sgnn		break;
277278529Sgnn	case DTRACE_INVOP_POPM:
278279667Sandrew		sp = (register_t *)frame->tf_svc_sp;
279279667Sandrew		r0 = &frame->tf_r0;
280279667Sandrew		data = DTRACE_INVOP_DATA(invop);
281279667Sandrew
282279667Sandrew		/* Read the general registers */
283279667Sandrew		for (reg = 0; reg <= 12; reg++) {
284279667Sandrew			if (data & (1 << reg)) {
285279667Sandrew				r0[reg] = *sp;
286279667Sandrew				sp++;
287279667Sandrew			}
288279667Sandrew		}
289279667Sandrew
290279667Sandrew		/*
291279667Sandrew		 * Set the stack pointer. If we don't update it here we will
292279667Sandrew		 * need to update it at the end as the instruction would do
293279667Sandrew		 */
294279667Sandrew		update_sp = 1;
295279667Sandrew		if (data & (1 << BIT_SP)) {
296279667Sandrew			frame->tf_svc_sp = *sp;
297279667Sandrew			*sp++;
298279667Sandrew			update_sp = 0;
299279667Sandrew		}
300279667Sandrew
301279667Sandrew		/* Update the link register, we need to use the correct copy */
302279667Sandrew		if (data & (1 << BIT_LR)) {
303279667Sandrew			frame->tf_svc_lr = *sp;
304279667Sandrew			*sp++;
305279667Sandrew		}
306279667Sandrew		/*
307279667Sandrew		 * And the program counter. If it's not in the list skip over
308279667Sandrew		 * it when we return so to not hit this again.
309279667Sandrew		 */
310279667Sandrew		if (data & (1 << BIT_PC)) {
311279667Sandrew			frame->tf_pc = *sp;
312279667Sandrew			*sp++;
313279667Sandrew		} else
314279667Sandrew			frame->tf_pc += 4;
315279667Sandrew
316279667Sandrew		/* Update the stack pointer if we haven't already done so */
317279667Sandrew		if (update_sp)
318279667Sandrew			frame->tf_svc_sp = (register_t)sp;
319278529Sgnn		break;
320278529Sgnn	case DTRACE_INVOP_B:
321279667Sandrew		data = DTRACE_INVOP_DATA(invop) & 0x00ffffff;
322279667Sandrew		/* Sign extend the data */
323279667Sandrew		if ((data & (1 << 23)) != 0)
324279667Sandrew			data |= 0xff000000;
325279667Sandrew		/* The data is the number of 4-byte words to change the pc */
326279667Sandrew		data *= 4;
327279667Sandrew		data += 8;
328279667Sandrew		frame->tf_pc += data;
329278529Sgnn		break;
330278529Sgnn	default:
331278529Sgnn		return (-1);
332278529Sgnn		break;
333278529Sgnn	}
334278529Sgnn
335278529Sgnn	return (0);
336278529Sgnn}
337278529Sgnn
338278529Sgnnvoid dtrace_invop_init(void)
339278529Sgnn{
340278529Sgnn	dtrace_invop_jump_addr = dtrace_invop_start;
341278529Sgnn}
342278529Sgnn
343278529Sgnnvoid dtrace_invop_uninit(void)
344278529Sgnn{
345278529Sgnn	dtrace_invop_jump_addr = 0;
346278529Sgnn}
347