dtrace_subr.c revision 278529
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 278529 2015-02-10 19:41:30Z gnn $
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 278529 2015-02-10 19:41:30Z gnn $");
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)
49278529Sgnn
50278529Sgnnextern uintptr_t 	dtrace_in_probe_addr;
51278529Sgnnextern int		dtrace_in_probe;
52278529Sgnnextern dtrace_id_t	dtrace_probeid_error;
53278529Sgnnextern int (*dtrace_invop_jump_addr)(struct trapframe *);
54278529Sgnn
55278529Sgnnint dtrace_invop(uintptr_t, uintptr_t *, uintptr_t);
56278529Sgnnvoid dtrace_invop_init(void);
57278529Sgnnvoid dtrace_invop_uninit(void);
58278529Sgnn
59278529Sgnntypedef struct dtrace_invop_hdlr {
60278529Sgnn	int (*dtih_func)(uintptr_t, uintptr_t *, uintptr_t);
61278529Sgnn	struct dtrace_invop_hdlr *dtih_next;
62278529Sgnn} dtrace_invop_hdlr_t;
63278529Sgnn
64278529Sgnndtrace_invop_hdlr_t *dtrace_invop_hdlr;
65278529Sgnn
66278529Sgnnint
67278529Sgnndtrace_invop(uintptr_t addr, uintptr_t *stack, uintptr_t eax)
68278529Sgnn{
69278529Sgnn	dtrace_invop_hdlr_t *hdlr;
70278529Sgnn	int rval;
71278529Sgnn
72278529Sgnn	for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next)
73278529Sgnn		if ((rval = hdlr->dtih_func(addr, stack, eax)) != 0)
74278529Sgnn			return (rval);
75278529Sgnn
76278529Sgnn	return (0);
77278529Sgnn}
78278529Sgnn
79278529Sgnn
80278529Sgnnvoid
81278529Sgnndtrace_invop_add(int (*func)(uintptr_t, uintptr_t *, uintptr_t))
82278529Sgnn{
83278529Sgnn	dtrace_invop_hdlr_t *hdlr;
84278529Sgnn
85278529Sgnn	hdlr = kmem_alloc(sizeof (dtrace_invop_hdlr_t), KM_SLEEP);
86278529Sgnn	hdlr->dtih_func = func;
87278529Sgnn	hdlr->dtih_next = dtrace_invop_hdlr;
88278529Sgnn	dtrace_invop_hdlr = hdlr;
89278529Sgnn}
90278529Sgnn
91278529Sgnnvoid
92278529Sgnndtrace_invop_remove(int (*func)(uintptr_t, uintptr_t *, uintptr_t))
93278529Sgnn{
94278529Sgnn	dtrace_invop_hdlr_t *hdlr = dtrace_invop_hdlr, *prev = NULL;
95278529Sgnn
96278529Sgnn	for (;;) {
97278529Sgnn		if (hdlr == NULL)
98278529Sgnn			panic("attempt to remove non-existent invop handler");
99278529Sgnn
100278529Sgnn		if (hdlr->dtih_func == func)
101278529Sgnn			break;
102278529Sgnn
103278529Sgnn		prev = hdlr;
104278529Sgnn		hdlr = hdlr->dtih_next;
105278529Sgnn	}
106278529Sgnn
107278529Sgnn	if (prev == NULL) {
108278529Sgnn		ASSERT(dtrace_invop_hdlr == hdlr);
109278529Sgnn		dtrace_invop_hdlr = hdlr->dtih_next;
110278529Sgnn	} else {
111278529Sgnn		ASSERT(dtrace_invop_hdlr != hdlr);
112278529Sgnn		prev->dtih_next = hdlr->dtih_next;
113278529Sgnn	}
114278529Sgnn
115278529Sgnn	kmem_free(hdlr, 0);
116278529Sgnn}
117278529Sgnn
118278529Sgnn
119278529Sgnn/*ARGSUSED*/
120278529Sgnnvoid
121278529Sgnndtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit))
122278529Sgnn{
123278529Sgnn	printf("IMPLEMENT ME: dtrace_toxic_ranges\n");
124278529Sgnn}
125278529Sgnn
126278529Sgnnvoid
127278529Sgnndtrace_xcall(processorid_t cpu, dtrace_xcall_t func, void *arg)
128278529Sgnn{
129278529Sgnn	cpuset_t cpus;
130278529Sgnn
131278529Sgnn	if (cpu == DTRACE_CPUALL)
132278529Sgnn		cpus = all_cpus;
133278529Sgnn	else
134278529Sgnn		CPU_SETOF(cpu, &cpus);
135278529Sgnn
136278529Sgnn	smp_rendezvous_cpus(cpus, smp_no_rendevous_barrier, func,
137278529Sgnn	    smp_no_rendevous_barrier, arg);
138278529Sgnn}
139278529Sgnn
140278529Sgnnstatic void
141278529Sgnndtrace_sync_func(void)
142278529Sgnn{
143278529Sgnn}
144278529Sgnn
145278529Sgnnvoid
146278529Sgnndtrace_sync(void)
147278529Sgnn{
148278529Sgnn	dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL);
149278529Sgnn}
150278529Sgnn
151278529Sgnn/*
152278529Sgnn * DTrace needs a high resolution time function which can
153278529Sgnn * be called from a probe context and guaranteed not to have
154278529Sgnn * instrumented with probes itself.
155278529Sgnn *
156278529Sgnn * Returns nanoseconds since boot.
157278529Sgnn */
158278529Sgnnuint64_t
159278529Sgnndtrace_gethrtime()
160278529Sgnn{
161278529Sgnn	struct	timespec curtime;
162278529Sgnn
163278529Sgnn	nanouptime(&curtime);
164278529Sgnn
165278529Sgnn	return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec);
166278529Sgnn
167278529Sgnn}
168278529Sgnn
169278529Sgnnuint64_t
170278529Sgnndtrace_gethrestime(void)
171278529Sgnn{
172278529Sgnn	struct	timespec curtime;
173278529Sgnn
174278529Sgnn	getnanotime(&curtime);
175278529Sgnn
176278529Sgnn	return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec);
177278529Sgnn}
178278529Sgnn
179278529Sgnn/* Function to handle DTrace traps during probes. See amd64/amd64/trap.c */
180278529Sgnnint
181278529Sgnndtrace_trap(struct trapframe *frame, u_int type)
182278529Sgnn{
183278529Sgnn	/*
184278529Sgnn	 * A trap can occur while DTrace executes a probe. Before
185278529Sgnn	 * executing the probe, DTrace blocks re-scheduling and sets
186278529Sgnn	 * a flag in it's per-cpu flags to indicate that it doesn't
187278529Sgnn	 * want to fault. On returning from the probe, the no-fault
188278529Sgnn	 * flag is cleared and finally re-scheduling is enabled.
189278529Sgnn	 *
190278529Sgnn	 * Check if DTrace has enabled 'no-fault' mode:
191278529Sgnn	 *
192278529Sgnn	 */
193278529Sgnn	if ((cpu_core[curcpu].cpuc_dtrace_flags & CPU_DTRACE_NOFAULT) != 0) {
194278529Sgnn		/*
195278529Sgnn		 * There are only a couple of trap types that are expected.
196278529Sgnn		 * All the rest will be handled in the usual way.
197278529Sgnn		 */
198278529Sgnn		switch (type) {
199278529Sgnn		/* Page fault. */
200278529Sgnn		case FAULT_ALIGN:
201278529Sgnn			/* Flag a bad address. */
202278529Sgnn			cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
203278529Sgnn			cpu_core[curcpu].cpuc_dtrace_illval = 0;
204278529Sgnn
205278529Sgnn			/*
206278529Sgnn			 * Offset the instruction pointer to the instruction
207278529Sgnn			 * following the one causing the fault.
208278529Sgnn			 */
209278529Sgnn			frame->tf_pc += sizeof(int);
210278529Sgnn			return (1);
211278529Sgnn		default:
212278529Sgnn			/* Handle all other traps in the usual way. */
213278529Sgnn			break;
214278529Sgnn		}
215278529Sgnn	}
216278529Sgnn
217278529Sgnn	/* Handle the trap in the usual way. */
218278529Sgnn	return (0);
219278529Sgnn}
220278529Sgnn
221278529Sgnnvoid
222278529Sgnndtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which,
223278529Sgnn    int fault, int fltoffs, uintptr_t illval)
224278529Sgnn{
225278529Sgnn
226278529Sgnn	dtrace_probe(dtrace_probeid_error, (uint64_t)(uintptr_t)state,
227278529Sgnn	    (uintptr_t)epid,
228278529Sgnn	    (uintptr_t)which, (uintptr_t)fault, (uintptr_t)fltoffs);
229278529Sgnn}
230278529Sgnn
231278529Sgnnstatic int
232278529Sgnndtrace_invop_start(struct trapframe *frame)
233278529Sgnn{
234278529Sgnn	printf("IMPLEMENT ME: %s\n", __func__);
235278529Sgnn	switch (dtrace_invop(frame->tf_pc, (uintptr_t *)frame, frame->tf_pc)) {
236278529Sgnn	case DTRACE_INVOP_PUSHM:
237278529Sgnn		// TODO:
238278529Sgnn		break;
239278529Sgnn	case DTRACE_INVOP_POPM:
240278529Sgnn		// TODO:
241278529Sgnn		break;
242278529Sgnn	case DTRACE_INVOP_B:
243278529Sgnn		// TODO
244278529Sgnn		break;
245278529Sgnn	default:
246278529Sgnn		return (-1);
247278529Sgnn		break;
248278529Sgnn	}
249278529Sgnn
250278529Sgnn	return (0);
251278529Sgnn}
252278529Sgnn
253278529Sgnnvoid dtrace_invop_init(void)
254278529Sgnn{
255278529Sgnn	dtrace_invop_jump_addr = dtrace_invop_start;
256278529Sgnn}
257278529Sgnn
258278529Sgnnvoid dtrace_invop_uninit(void)
259278529Sgnn{
260278529Sgnn	dtrace_invop_jump_addr = 0;
261278529Sgnn}
262