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