dtrace_subr.c revision 1.1
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 *
22 * $FreeBSD$
23 *
24 */
25/*
26 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
27 * Use is subject to license terms.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD$");
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/types.h>
36#include <sys/kernel.h>
37#include <sys/malloc.h>
38#include <sys/kmem.h>
39#include <sys/xcall.h>
40#include <sys/cpu.h>
41#include <sys/dtrace_impl.h>
42#include <sys/dtrace_bsd.h>
43#include <machine/regnum.h>
44#include <machine/locore.h>
45#include <machine/trap.h>
46
47#define	DELAYBRANCH(x)	((int)(x) < 0)
48
49#ifdef __FreeBSD__
50#define	CURRENT_CPU		curcpu
51#endif
52#ifdef __NetBSD__
53#define	CURRENT_CPU		cpu_index(curcpu())
54#endif
55
56extern dtrace_id_t	dtrace_probeid_error;
57extern int (*dtrace_invop_jump_addr)(struct trapframe *);
58extern void dtrace_getnanotime(struct timespec *tsp);
59
60int dtrace_invop(uintptr_t, struct trapframe *, uintptr_t);
61void dtrace_invop_init(void);
62void dtrace_invop_uninit(void);
63
64void dtrace_gethrtime_init(void);
65
66typedef struct dtrace_invop_hdlr {
67	int (*dtih_func)(uintptr_t, struct trapframe *, uintptr_t);
68	struct dtrace_invop_hdlr *dtih_next;
69} dtrace_invop_hdlr_t;
70
71dtrace_invop_hdlr_t *dtrace_invop_hdlr;
72
73int
74dtrace_invop(uintptr_t addr, struct trapframe *stack, uintptr_t eax)
75{
76	dtrace_invop_hdlr_t *hdlr;
77	int rval;
78
79	for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next)
80		if ((rval = hdlr->dtih_func(addr, stack, eax)) != 0)
81			return (rval);
82
83	return (0);
84}
85
86void
87dtrace_invop_add(int (*func)(uintptr_t, struct trapframe *, uintptr_t))
88{
89	dtrace_invop_hdlr_t *hdlr;
90
91	hdlr = kmem_alloc(sizeof (dtrace_invop_hdlr_t), KM_SLEEP);
92	hdlr->dtih_func = func;
93	hdlr->dtih_next = dtrace_invop_hdlr;
94	dtrace_invop_hdlr = hdlr;
95}
96
97void
98dtrace_invop_remove(int (*func)(uintptr_t, struct trapframe *, uintptr_t))
99{
100	dtrace_invop_hdlr_t *hdlr, *prev;
101
102	hdlr = dtrace_invop_hdlr;
103	prev = NULL;
104
105	for (;;) {
106		if (hdlr == NULL)
107			panic("attempt to remove non-existent invop handler");
108
109		if (hdlr->dtih_func == func)
110			break;
111
112		prev = hdlr;
113		hdlr = hdlr->dtih_next;
114	}
115
116	if (prev == NULL) {
117		ASSERT(dtrace_invop_hdlr == hdlr);
118		dtrace_invop_hdlr = hdlr->dtih_next;
119	} else {
120		ASSERT(dtrace_invop_hdlr != hdlr);
121		prev->dtih_next = hdlr->dtih_next;
122	}
123
124	kmem_free(hdlr, 0);
125}
126
127/*ARGSUSED*/
128void
129dtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit))
130{
131	/* XXXXXXsimonb what is a "toxic range"? */
132}
133
134static void
135xcall_func(void *arg0, void *arg1)
136{
137	dtrace_xcall_t func = arg0;
138
139	(*func)(arg1);
140}
141
142void
143dtrace_xcall(processorid_t cpu, dtrace_xcall_t func, void *arg)
144{
145	uint64_t where;
146
147	if (cpu == DTRACE_CPUALL) {
148		where = xc_broadcast(0, xcall_func, func, arg);
149	} else {
150		struct cpu_info *ci = cpu_lookup(cpu);
151
152		KASSERT(ci != NULL);
153		where = xc_unicast(0, xcall_func, func, arg, ci);
154	}
155	xc_wait(where);
156}
157
158static void
159dtrace_sync_func(void)
160{
161
162}
163
164void
165dtrace_sync(void)
166{
167
168        dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL);
169}
170
171/*
172 * DTrace needs a high resolution time function which can
173 * be called from a probe context and guaranteed not to have
174 * instrumented with probes itself.
175 *
176 * Returns nanoseconds since boot.
177 */
178uint64_t
179dtrace_gethrtime()
180{
181	struct timespec curtime;
182
183	nanouptime(&curtime);
184
185	return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec);
186
187}
188
189void
190dtrace_gethrtime_init(void)
191{
192}
193
194uint64_t
195dtrace_gethrestime(void)
196{
197	struct timespec current_time;
198
199	dtrace_getnanotime(&current_time);
200
201	return (current_time.tv_sec * 1000000000UL + current_time.tv_nsec);
202}
203
204/* Function to handle DTrace traps during probes. See amd64/amd64/trap.c */
205int
206dtrace_trap(struct trapframe *frame, u_int type)
207{
208	/*
209	 * A trap can occur while DTrace executes a probe. Before
210	 * executing the probe, DTrace blocks re-scheduling and sets
211	 * a flag in its per-cpu flags to indicate that it doesn't
212	 * want to fault. On returning from the probe, the no-fault
213	 * flag is cleared and finally re-scheduling is enabled.
214	 *
215	 * Check if DTrace has enabled 'no-fault' mode:
216	 */
217
218	if ((cpu_core[CURRENT_CPU].cpuc_dtrace_flags & CPU_DTRACE_NOFAULT) != 0) {
219		/*
220		 * There are only a couple of trap types that are expected.
221		 * All the rest will be handled in the usual way.
222		 */
223		switch (type) {
224		/* Page fault. */
225		case T_TLB_ST_MISS:
226		case T_ADDR_ERR_ST:
227		case T_TLB_LD_MISS:
228		case T_ADDR_ERR_LD:
229		case T_BUS_ERR_IFETCH:
230			/* Flag a bad address. */
231			cpu_core[CURRENT_CPU].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
232			cpu_core[CURRENT_CPU].cpuc_dtrace_illval = frame->tf_regs[_R_BADVADDR];
233
234			/*
235			 * Offset the instruction pointer to the instruction
236			 * following the one causing the fault.
237			 */
238			if (DELAYBRANCH(frame->tf_regs[_R_CAUSE]))	 /* Check BD bit */
239			{
240				/* XXX: check MipsEmulateBranch on MIPS64
241				frame->tf_regs[_R_PC] = MipsEmulateBranch(frame,
242				    frame->tf_regs[_R_PC], 0, 0);
243 				*/
244				panic("%s: delay slot at %jx, badvaddr = %jx\n",
245				    __func__, (intmax_t)frame->tf_regs[_R_PC],
246				    (intmax_t)frame->tf_regs[_R_BADVADDR]);
247			}
248			else
249				frame->tf_regs[_R_PC] += sizeof(int);
250			return (1);
251		default:
252			/* Handle all other traps in the usual way. */
253			break;
254		}
255	}
256
257	/* Handle the trap in the usual way. */
258	return (0);
259}
260
261void
262dtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which,
263    int fault, int fltoffs, uintptr_t illval)
264{
265
266	dtrace_probe(dtrace_probeid_error, (uint64_t)(uintptr_t)state,
267	    (uintptr_t)epid,
268	    (uintptr_t)which, (uintptr_t)fault, (uintptr_t)fltoffs);
269}
270
271static int
272dtrace_invop_start(struct trapframe *frame)
273{
274	register_t *sp;
275	int16_t offs;
276	int invop;
277
278	invop = dtrace_invop(frame->tf_regs[_R_PC], frame, frame->tf_regs[_R_PC]);
279	if (invop == 0)
280		return (-1);
281
282	offs = (invop & LDSD_DATA_MASK);
283	sp = (register_t *)(intptr_t)(frame->tf_regs[_R_SP] + offs);
284
285	switch (invop & LDSD_RA_SP_MASK) {
286	case LD_RA_SP:
287		frame->tf_regs[_R_RA] = *sp;
288		frame->tf_regs[_R_PC] += INSN_SIZE;
289		break;
290	case SD_RA_SP:
291		*(sp) = frame->tf_regs[_R_RA];
292		frame->tf_regs[_R_PC] += INSN_SIZE;
293		break;
294	default:
295		printf("%s: 0x%x undefined\n", __func__, invop);
296		return (-1);
297	};
298
299	return (0);
300}
301
302void
303dtrace_invop_init(void)
304{
305
306	dtrace_invop_jump_addr = dtrace_invop_start;
307}
308
309void
310dtrace_invop_uninit(void)
311{
312
313	dtrace_invop_jump_addr = 0;
314}
315