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(¤t_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