1*65f88a47Sriastradh /* $NetBSD: dtrace_subr.c,v 1.14 2022/08/21 18:58:45 riastradh Exp $ */
201c9547eSdarran
3bb8023b5Sdarran /*
4bb8023b5Sdarran * CDDL HEADER START
5bb8023b5Sdarran *
6bb8023b5Sdarran * The contents of this file are subject to the terms of the
7bb8023b5Sdarran * Common Development and Distribution License, Version 1.0 only
8bb8023b5Sdarran * (the "License"). You may not use this file except in compliance
9bb8023b5Sdarran * with the License.
10bb8023b5Sdarran *
11bb8023b5Sdarran * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
12bb8023b5Sdarran * or http://www.opensolaris.org/os/licensing.
13bb8023b5Sdarran * See the License for the specific language governing permissions
14bb8023b5Sdarran * and limitations under the License.
15bb8023b5Sdarran *
16bb8023b5Sdarran * When distributing Covered Code, include this CDDL HEADER in each
17bb8023b5Sdarran * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
18bb8023b5Sdarran * If applicable, add the following below this CDDL HEADER, with the
19bb8023b5Sdarran * fields enclosed by brackets "[]" replaced with your own identifying
20bb8023b5Sdarran * information: Portions Copyright [yyyy] [name of copyright owner]
21bb8023b5Sdarran *
22bb8023b5Sdarran * CDDL HEADER END
23bb8023b5Sdarran *
24eada09acSchs * $FreeBSD: head/sys/cddl/dev/dtrace/i386/dtrace_subr.c 313850 2017-02-17 03:27:20Z markj $
25bb8023b5Sdarran *
26bb8023b5Sdarran */
27bb8023b5Sdarran /*
28bb8023b5Sdarran * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
29bb8023b5Sdarran * Use is subject to license terms.
30bb8023b5Sdarran */
31bb8023b5Sdarran
32eada09acSchs /*
33eada09acSchs * Copyright (c) 2011, Joyent, Inc. All rights reserved.
34eada09acSchs */
35eada09acSchs
36bb8023b5Sdarran #include <sys/param.h>
37bb8023b5Sdarran #include <sys/systm.h>
38bb8023b5Sdarran #include <sys/types.h>
39bb8023b5Sdarran #include <sys/kernel.h>
40bb8023b5Sdarran #include <sys/malloc.h>
41bb8023b5Sdarran #include <sys/kmem.h>
4201c9547eSdarran #include <sys/xcall.h>
4301c9547eSdarran #include <sys/cpu.h>
4401c9547eSdarran #include <sys/cpuvar.h>
45bb8023b5Sdarran #include <sys/dtrace_impl.h>
46bb8023b5Sdarran #include <sys/dtrace_bsd.h>
4701c9547eSdarran #include <machine/cpu.h>
48*65f88a47Sriastradh #include <machine/cpufunc.h>
49bb8023b5Sdarran #include <machine/clock.h>
50bb8023b5Sdarran #include <machine/frame.h>
5101c9547eSdarran #include <uvm/uvm_pglist.h>
5201c9547eSdarran #include <uvm/uvm_prot.h>
5301c9547eSdarran #include <uvm/uvm_pmap.h>
54bb8023b5Sdarran
554a21f050Stron #include <x86/include/cpu_counter.h>
564a21f050Stron
57bb8023b5Sdarran extern uintptr_t kernelbase;
58eada09acSchs
59eada09acSchs extern void dtrace_getnanotime(struct timespec *tsp);
60bb8023b5Sdarran
610fa27af2Schs int dtrace_invop(uintptr_t, struct trapframe *, uintptr_t);
62bb8023b5Sdarran
63bb8023b5Sdarran typedef struct dtrace_invop_hdlr {
640fa27af2Schs int (*dtih_func)(uintptr_t, struct trapframe *, uintptr_t);
65bb8023b5Sdarran struct dtrace_invop_hdlr *dtih_next;
66bb8023b5Sdarran } dtrace_invop_hdlr_t;
67bb8023b5Sdarran
68bb8023b5Sdarran dtrace_invop_hdlr_t *dtrace_invop_hdlr;
69bb8023b5Sdarran
704a21f050Stron void dtrace_gethrtime_init(void *arg);
714a21f050Stron
72bb8023b5Sdarran int
dtrace_invop(uintptr_t addr,struct trapframe * frame,uintptr_t eax)730fa27af2Schs dtrace_invop(uintptr_t addr, struct trapframe *frame, uintptr_t eax)
74bb8023b5Sdarran {
75bb8023b5Sdarran dtrace_invop_hdlr_t *hdlr;
76bb8023b5Sdarran int rval;
77bb8023b5Sdarran
78bb8023b5Sdarran for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next)
790fa27af2Schs if ((rval = hdlr->dtih_func(addr, frame, eax)) != 0)
80bb8023b5Sdarran return (rval);
81bb8023b5Sdarran
82bb8023b5Sdarran return (0);
83bb8023b5Sdarran }
84bb8023b5Sdarran
85bb8023b5Sdarran void
dtrace_invop_add(int (* func)(uintptr_t,struct trapframe *,uintptr_t))860fa27af2Schs dtrace_invop_add(int (*func)(uintptr_t, struct trapframe *, uintptr_t))
87bb8023b5Sdarran {
88bb8023b5Sdarran dtrace_invop_hdlr_t *hdlr;
89bb8023b5Sdarran
90894b3478Ssimonb hdlr = kmem_alloc(sizeof(*hdlr), KM_SLEEP);
91bb8023b5Sdarran hdlr->dtih_func = func;
92bb8023b5Sdarran hdlr->dtih_next = dtrace_invop_hdlr;
93bb8023b5Sdarran dtrace_invop_hdlr = hdlr;
94bb8023b5Sdarran }
95bb8023b5Sdarran
96bb8023b5Sdarran void
dtrace_invop_remove(int (* func)(uintptr_t,struct trapframe *,uintptr_t))970fa27af2Schs dtrace_invop_remove(int (*func)(uintptr_t, struct trapframe *, uintptr_t))
98bb8023b5Sdarran {
99bb8023b5Sdarran dtrace_invop_hdlr_t *hdlr = dtrace_invop_hdlr, *prev = NULL;
100bb8023b5Sdarran
101bb8023b5Sdarran for (;;) {
102bb8023b5Sdarran if (hdlr == NULL)
103bb8023b5Sdarran panic("attempt to remove non-existent invop handler");
104bb8023b5Sdarran
105bb8023b5Sdarran if (hdlr->dtih_func == func)
106bb8023b5Sdarran break;
107bb8023b5Sdarran
108bb8023b5Sdarran prev = hdlr;
109bb8023b5Sdarran hdlr = hdlr->dtih_next;
110bb8023b5Sdarran }
111bb8023b5Sdarran
112bb8023b5Sdarran if (prev == NULL) {
113bb8023b5Sdarran ASSERT(dtrace_invop_hdlr == hdlr);
114bb8023b5Sdarran dtrace_invop_hdlr = hdlr->dtih_next;
115bb8023b5Sdarran } else {
116bb8023b5Sdarran ASSERT(dtrace_invop_hdlr != hdlr);
117bb8023b5Sdarran prev->dtih_next = hdlr->dtih_next;
118bb8023b5Sdarran }
119bb8023b5Sdarran
120894b3478Ssimonb kmem_free(hdlr, sizeof(*hdlr));
121bb8023b5Sdarran }
122bb8023b5Sdarran
123bb8023b5Sdarran void
dtrace_toxic_ranges(void (* func)(uintptr_t base,uintptr_t limit))124bb8023b5Sdarran dtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit))
125bb8023b5Sdarran {
126bb8023b5Sdarran (*func)(0, kernelbase);
127bb8023b5Sdarran }
128bb8023b5Sdarran
12901c9547eSdarran static void
xcall_func(void * arg0,void * arg1)13001c9547eSdarran xcall_func(void *arg0, void *arg1)
13101c9547eSdarran {
13201c9547eSdarran dtrace_xcall_t func = arg0;
13301c9547eSdarran
13401c9547eSdarran (*func)(arg1);
13501c9547eSdarran }
13601c9547eSdarran
137bb8023b5Sdarran void
dtrace_xcall(processorid_t cpu,dtrace_xcall_t func,void * arg)138ebd379eeSchs dtrace_xcall(processorid_t cpu, dtrace_xcall_t func, void *arg)
139bb8023b5Sdarran {
14001c9547eSdarran uint64_t where;
141bb8023b5Sdarran
142ebd379eeSchs if (cpu == DTRACE_CPUALL) {
14301c9547eSdarran where = xc_broadcast(0, xcall_func, func, arg);
14401c9547eSdarran } else {
14501c9547eSdarran struct cpu_info *cinfo = cpu_lookup(cpu);
146bb8023b5Sdarran
14701c9547eSdarran KASSERT(cinfo != NULL);
14801c9547eSdarran where = xc_unicast(0, xcall_func, func, arg, cinfo);
149bb8023b5Sdarran }
15001c9547eSdarran xc_wait(where);
151bb8023b5Sdarran
15201c9547eSdarran /* XXX Q. Do we really need the other cpus to wait also?
15301c9547eSdarran * (see solaris:xc_sync())
15401c9547eSdarran */
155bb8023b5Sdarran }
156bb8023b5Sdarran
157bb8023b5Sdarran static void
dtrace_sync_func(void)158bb8023b5Sdarran dtrace_sync_func(void)
159bb8023b5Sdarran {
160bb8023b5Sdarran }
161bb8023b5Sdarran
162bb8023b5Sdarran void
dtrace_sync(void)163bb8023b5Sdarran dtrace_sync(void)
164bb8023b5Sdarran {
165bb8023b5Sdarran dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL);
166bb8023b5Sdarran }
167bb8023b5Sdarran
168bb8023b5Sdarran #ifdef notyet
169bb8023b5Sdarran void
dtrace_safe_synchronous_signal(void)170bb8023b5Sdarran dtrace_safe_synchronous_signal(void)
171bb8023b5Sdarran {
172bb8023b5Sdarran kthread_t *t = curthread;
173bb8023b5Sdarran struct regs *rp = lwptoregs(ttolwp(t));
174bb8023b5Sdarran size_t isz = t->t_dtrace_npc - t->t_dtrace_pc;
175bb8023b5Sdarran
176bb8023b5Sdarran ASSERT(t->t_dtrace_on);
177bb8023b5Sdarran
178bb8023b5Sdarran /*
179bb8023b5Sdarran * If we're not in the range of scratch addresses, we're not actually
180bb8023b5Sdarran * tracing user instructions so turn off the flags. If the instruction
181bb8023b5Sdarran * we copied out caused a synchonous trap, reset the pc back to its
182bb8023b5Sdarran * original value and turn off the flags.
183bb8023b5Sdarran */
184bb8023b5Sdarran if (rp->r_pc < t->t_dtrace_scrpc ||
185bb8023b5Sdarran rp->r_pc > t->t_dtrace_astpc + isz) {
186bb8023b5Sdarran t->t_dtrace_ft = 0;
187bb8023b5Sdarran } else if (rp->r_pc == t->t_dtrace_scrpc ||
188bb8023b5Sdarran rp->r_pc == t->t_dtrace_astpc) {
189bb8023b5Sdarran rp->r_pc = t->t_dtrace_pc;
190bb8023b5Sdarran t->t_dtrace_ft = 0;
191bb8023b5Sdarran }
192bb8023b5Sdarran }
193bb8023b5Sdarran
194bb8023b5Sdarran int
dtrace_safe_defer_signal(void)195bb8023b5Sdarran dtrace_safe_defer_signal(void)
196bb8023b5Sdarran {
197bb8023b5Sdarran kthread_t *t = curthread;
198bb8023b5Sdarran struct regs *rp = lwptoregs(ttolwp(t));
199bb8023b5Sdarran size_t isz = t->t_dtrace_npc - t->t_dtrace_pc;
200bb8023b5Sdarran
201bb8023b5Sdarran ASSERT(t->t_dtrace_on);
202bb8023b5Sdarran
203bb8023b5Sdarran /*
204bb8023b5Sdarran * If we're not in the range of scratch addresses, we're not actually
205bb8023b5Sdarran * tracing user instructions so turn off the flags.
206bb8023b5Sdarran */
207bb8023b5Sdarran if (rp->r_pc < t->t_dtrace_scrpc ||
208bb8023b5Sdarran rp->r_pc > t->t_dtrace_astpc + isz) {
209bb8023b5Sdarran t->t_dtrace_ft = 0;
210bb8023b5Sdarran return (0);
211bb8023b5Sdarran }
212bb8023b5Sdarran
213bb8023b5Sdarran /*
214eada09acSchs * If we have executed the original instruction, but we have performed
215eada09acSchs * neither the jmp back to t->t_dtrace_npc nor the clean up of any
216eada09acSchs * registers used to emulate %rip-relative instructions in 64-bit mode,
217eada09acSchs * we'll save ourselves some effort by doing that here and taking the
218eada09acSchs * signal right away. We detect this condition by seeing if the program
219eada09acSchs * counter is the range [scrpc + isz, astpc).
220bb8023b5Sdarran */
221eada09acSchs if (rp->r_pc >= t->t_dtrace_scrpc + isz &&
222eada09acSchs rp->r_pc < t->t_dtrace_astpc) {
223bb8023b5Sdarran #ifdef __amd64
224bb8023b5Sdarran /*
225bb8023b5Sdarran * If there is a scratch register and we're on the
226bb8023b5Sdarran * instruction immediately after the modified instruction,
227bb8023b5Sdarran * restore the value of that scratch register.
228bb8023b5Sdarran */
229bb8023b5Sdarran if (t->t_dtrace_reg != 0 &&
230bb8023b5Sdarran rp->r_pc == t->t_dtrace_scrpc + isz) {
231bb8023b5Sdarran switch (t->t_dtrace_reg) {
232bb8023b5Sdarran case REG_RAX:
233bb8023b5Sdarran rp->r_rax = t->t_dtrace_regv;
234bb8023b5Sdarran break;
235bb8023b5Sdarran case REG_RCX:
236bb8023b5Sdarran rp->r_rcx = t->t_dtrace_regv;
237bb8023b5Sdarran break;
238bb8023b5Sdarran case REG_R8:
239bb8023b5Sdarran rp->r_r8 = t->t_dtrace_regv;
240bb8023b5Sdarran break;
241bb8023b5Sdarran case REG_R9:
242bb8023b5Sdarran rp->r_r9 = t->t_dtrace_regv;
243bb8023b5Sdarran break;
244bb8023b5Sdarran }
245bb8023b5Sdarran }
246bb8023b5Sdarran #endif
247bb8023b5Sdarran rp->r_pc = t->t_dtrace_npc;
248bb8023b5Sdarran t->t_dtrace_ft = 0;
249bb8023b5Sdarran return (0);
250bb8023b5Sdarran }
251bb8023b5Sdarran
252bb8023b5Sdarran /*
253bb8023b5Sdarran * Otherwise, make sure we'll return to the kernel after executing
254bb8023b5Sdarran * the copied out instruction and defer the signal.
255bb8023b5Sdarran */
256bb8023b5Sdarran if (!t->t_dtrace_step) {
257bb8023b5Sdarran ASSERT(rp->r_pc < t->t_dtrace_astpc);
258bb8023b5Sdarran rp->r_pc += t->t_dtrace_astpc - t->t_dtrace_scrpc;
259bb8023b5Sdarran t->t_dtrace_step = 1;
260bb8023b5Sdarran }
261bb8023b5Sdarran
262bb8023b5Sdarran t->t_dtrace_ast = 1;
263bb8023b5Sdarran
264bb8023b5Sdarran return (1);
265bb8023b5Sdarran }
266bb8023b5Sdarran #endif
267bb8023b5Sdarran
268bb8023b5Sdarran static int64_t tgt_cpu_tsc;
269bb8023b5Sdarran static int64_t hst_cpu_tsc;
27001c9547eSdarran static int64_t tsc_skew[MAXCPUS];
271bb8023b5Sdarran static uint64_t nsec_scale;
272bb8023b5Sdarran
273bb8023b5Sdarran /* See below for the explanation of this macro. */
274bb8023b5Sdarran #define SCALE_SHIFT 28
275bb8023b5Sdarran
27601c9547eSdarran static __inline uint64_t
dtrace_rdtsc(void)27701c9547eSdarran dtrace_rdtsc(void)
27801c9547eSdarran {
27901c9547eSdarran uint64_t rv;
28001c9547eSdarran
28101c9547eSdarran __asm __volatile("rdtsc" : "=A" (rv));
28201c9547eSdarran return (rv);
28301c9547eSdarran }
28401c9547eSdarran
285bb8023b5Sdarran static void
dtrace_gethrtime_init_cpu(void * arg)286bb8023b5Sdarran dtrace_gethrtime_init_cpu(void *arg)
287bb8023b5Sdarran {
288bb8023b5Sdarran uintptr_t cpu = (uintptr_t) arg;
289bb8023b5Sdarran
29001c9547eSdarran if (cpu == cpu_number())
29101c9547eSdarran tgt_cpu_tsc = dtrace_rdtsc();
292bb8023b5Sdarran else
29301c9547eSdarran hst_cpu_tsc = dtrace_rdtsc();
294bb8023b5Sdarran }
295bb8023b5Sdarran
29601c9547eSdarran void
dtrace_gethrtime_init(void * arg)297bb8023b5Sdarran dtrace_gethrtime_init(void *arg)
298bb8023b5Sdarran {
299bb8023b5Sdarran uint64_t tsc_f;
30001c9547eSdarran CPU_INFO_ITERATOR cpuind;
30101c9547eSdarran struct cpu_info *cinfo = curcpu();
30201c9547eSdarran cpuid_t cur_cpuid = cpu_number(); /* current cpu id */
303bb8023b5Sdarran
304bb8023b5Sdarran /*
305bb8023b5Sdarran * Get TSC frequency known at this moment.
306bb8023b5Sdarran * This should be constant if TSC is invariant.
307bb8023b5Sdarran * Otherwise tick->time conversion will be inaccurate, but
308bb8023b5Sdarran * will preserve monotonic property of TSC.
309bb8023b5Sdarran */
31001c9547eSdarran tsc_f = cpu_frequency(cinfo);
311bb8023b5Sdarran
312bb8023b5Sdarran /*
313bb8023b5Sdarran * The following line checks that nsec_scale calculated below
314bb8023b5Sdarran * doesn't overflow 32-bit unsigned integer, so that it can multiply
315bb8023b5Sdarran * another 32-bit integer without overflowing 64-bit.
316bb8023b5Sdarran * Thus minimum supported TSC frequency is 62.5MHz.
317bb8023b5Sdarran */
318eada09acSchs KASSERTMSG(tsc_f > (NANOSEC >> (32 - SCALE_SHIFT)),
319eada09acSchs "TSC frequency is too low");
320bb8023b5Sdarran
321bb8023b5Sdarran /*
322bb8023b5Sdarran * We scale up NANOSEC/tsc_f ratio to preserve as much precision
323bb8023b5Sdarran * as possible.
324bb8023b5Sdarran * 2^28 factor was chosen quite arbitrarily from practical
325bb8023b5Sdarran * considerations:
326bb8023b5Sdarran * - it supports TSC frequencies as low as 62.5MHz (see above);
327bb8023b5Sdarran * - it provides quite good precision (e < 0.01%) up to THz
328bb8023b5Sdarran * (terahertz) values;
329bb8023b5Sdarran */
330bb8023b5Sdarran nsec_scale = ((uint64_t)NANOSEC << SCALE_SHIFT) / tsc_f;
331bb8023b5Sdarran
332bb8023b5Sdarran /* The current CPU is the reference one. */
33301c9547eSdarran tsc_skew[cur_cpuid] = 0;
334bb8023b5Sdarran
33501c9547eSdarran for (CPU_INFO_FOREACH(cpuind, cinfo)) {
33601c9547eSdarran /* use skew relative to cpu 0 */
33701c9547eSdarran tsc_skew[cpu_index(cinfo)] = cinfo->ci_data.cpu_cc_skew;
33801c9547eSdarran }
33901c9547eSdarran
34001c9547eSdarran /* Already handled in x86/tsc.c for ci_data.cpu_cc_skew */
34101c9547eSdarran #if 0
342eada09acSchs /* The current CPU is the reference one. */
343eada09acSchs sched_pin();
344eada09acSchs tsc_skew[curcpu] = 0;
345eada09acSchs CPU_FOREACH(i) {
346bb8023b5Sdarran if (i == curcpu)
347bb8023b5Sdarran continue;
348bb8023b5Sdarran
349eada09acSchs pc = pcpu_find(i);
350eada09acSchs CPU_SETOF(PCPU_GET(cpuid), &map);
351eada09acSchs CPU_SET(pc->pc_cpuid, &map);
352bb8023b5Sdarran
353eada09acSchs smp_rendezvous_cpus(map, NULL,
354bb8023b5Sdarran dtrace_gethrtime_init_cpu,
355bb8023b5Sdarran smp_no_rendevous_barrier, (void *)(uintptr_t) i);
356bb8023b5Sdarran
357bb8023b5Sdarran tsc_skew[i] = tgt_cpu_tsc - hst_cpu_tsc;
358bb8023b5Sdarran }
359eada09acSchs sched_unpin();
36001c9547eSdarran #endif
361bb8023b5Sdarran }
362bb8023b5Sdarran
363eada09acSchs #ifdef __FreeBSD__
364eada09acSchs #ifdef EARLY_AP_STARTUP
365eada09acSchs SYSINIT(dtrace_gethrtime_init, SI_SUB_DTRACE, SI_ORDER_ANY,
366eada09acSchs dtrace_gethrtime_init, NULL);
367eada09acSchs #else
368eada09acSchs SYSINIT(dtrace_gethrtime_init, SI_SUB_SMP, SI_ORDER_ANY, dtrace_gethrtime_init,
369eada09acSchs NULL);
370eada09acSchs #endif
371eada09acSchs #endif
372eada09acSchs
373bb8023b5Sdarran /*
374bb8023b5Sdarran * DTrace needs a high resolution time function which can
375bb8023b5Sdarran * be called from a probe context and guaranteed not to have
376bb8023b5Sdarran * instrumented with probes itself.
377bb8023b5Sdarran *
378bb8023b5Sdarran * Returns nanoseconds since boot.
379bb8023b5Sdarran */
380bb8023b5Sdarran uint64_t
dtrace_gethrtime()381bb8023b5Sdarran dtrace_gethrtime()
382bb8023b5Sdarran {
383bb8023b5Sdarran uint64_t tsc;
384bb8023b5Sdarran uint32_t lo;
385bb8023b5Sdarran uint32_t hi;
386bb8023b5Sdarran
387bb8023b5Sdarran /*
388bb8023b5Sdarran * We split TSC value into lower and higher 32-bit halves and separately
389bb8023b5Sdarran * scale them with nsec_scale, then we scale them down by 2^28
390bb8023b5Sdarran * (see nsec_scale calculations) taking into account 32-bit shift of
391bb8023b5Sdarran * the higher half and finally add.
392bb8023b5Sdarran */
39301c9547eSdarran tsc = dtrace_rdtsc() + tsc_skew[cpu_number()];
394bb8023b5Sdarran lo = tsc;
395bb8023b5Sdarran hi = tsc >> 32;
396bb8023b5Sdarran return (((lo * nsec_scale) >> SCALE_SHIFT) +
397bb8023b5Sdarran ((hi * nsec_scale) << (32 - SCALE_SHIFT)));
398bb8023b5Sdarran }
399bb8023b5Sdarran
400bb8023b5Sdarran uint64_t
dtrace_gethrestime(void)401bb8023b5Sdarran dtrace_gethrestime(void)
402bb8023b5Sdarran {
403eada09acSchs struct timespec current_time;
404eada09acSchs
405eada09acSchs dtrace_getnanotime(¤t_time);
406eada09acSchs
407eada09acSchs return (current_time.tv_sec * 1000000000ULL + current_time.tv_nsec);
408bb8023b5Sdarran }
409bb8023b5Sdarran
410bb8023b5Sdarran /* Function to handle DTrace traps during probes. See i386/i386/trap.c */
411bb8023b5Sdarran int
dtrace_trap(struct trapframe * frame,u_int type)412bb8023b5Sdarran dtrace_trap(struct trapframe *frame, u_int type)
413bb8023b5Sdarran {
414eada09acSchs bool nofault;
41501c9547eSdarran cpuid_t cpuid = cpu_number(); /* current cpu id */
41601c9547eSdarran
417bb8023b5Sdarran /*
418bb8023b5Sdarran * A trap can occur while DTrace executes a probe. Before
419bb8023b5Sdarran * executing the probe, DTrace blocks re-scheduling and sets
420eada09acSchs * a flag in its per-cpu flags to indicate that it doesn't
421eada09acSchs * want to fault. On returning from the probe, the no-fault
422bb8023b5Sdarran * flag is cleared and finally re-scheduling is enabled.
423bb8023b5Sdarran *
424bb8023b5Sdarran * Check if DTrace has enabled 'no-fault' mode:
425bb8023b5Sdarran */
426eada09acSchs nofault = (cpu_core[cpuid].cpuc_dtrace_flags & CPU_DTRACE_NOFAULT) != 0;
427eada09acSchs if (nofault) {
42849f82716Srin KASSERTMSG((x86_read_flags() & PSL_I) == 0,
42949f82716Srin "interrupts enabled");
430eada09acSchs
431bb8023b5Sdarran /*
432bb8023b5Sdarran * There are only a couple of trap types that are expected.
433bb8023b5Sdarran * All the rest will be handled in the usual way.
434bb8023b5Sdarran */
435bb8023b5Sdarran switch (type) {
436bb8023b5Sdarran /* General protection fault. */
437bb8023b5Sdarran case T_PROTFLT:
438bb8023b5Sdarran /* Flag an illegal operation. */
43901c9547eSdarran cpu_core[cpuid].cpuc_dtrace_flags |= CPU_DTRACE_ILLOP;
440bb8023b5Sdarran
441bb8023b5Sdarran /*
442bb8023b5Sdarran * Offset the instruction pointer to the instruction
443bb8023b5Sdarran * following the one causing the fault.
444bb8023b5Sdarran */
445bb8023b5Sdarran frame->tf_eip += dtrace_instr_size((u_char *) frame->tf_eip);
446bb8023b5Sdarran return (1);
447bb8023b5Sdarran /* Page fault. */
448bb8023b5Sdarran case T_PAGEFLT:
449bb8023b5Sdarran /* Flag a bad address. */
45001c9547eSdarran cpu_core[cpuid].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
45101c9547eSdarran cpu_core[cpuid].cpuc_dtrace_illval = rcr2();
452bb8023b5Sdarran
453bb8023b5Sdarran /*
454bb8023b5Sdarran * Offset the instruction pointer to the instruction
455bb8023b5Sdarran * following the one causing the fault.
456bb8023b5Sdarran */
457bb8023b5Sdarran frame->tf_eip += dtrace_instr_size((u_char *) frame->tf_eip);
458bb8023b5Sdarran return (1);
459bb8023b5Sdarran default:
460bb8023b5Sdarran /* Handle all other traps in the usual way. */
461bb8023b5Sdarran break;
462bb8023b5Sdarran }
463bb8023b5Sdarran }
464bb8023b5Sdarran
465bb8023b5Sdarran /* Handle the trap in the usual way. */
466bb8023b5Sdarran return (0);
467bb8023b5Sdarran }
468