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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <sys/dtrace.h>
30#include <sys/fasttrap.h>
31#include <sys/x_call.h>
32#include <sys/atomic.h>
33#include <sys/machsystm.h>
34
35static void
36dtrace_xcall_func(uint64_t arg1, uint64_t arg2)
37{
38	(*(dtrace_xcall_t)arg1)((void *)(arg2));
39}
40
41void
42dtrace_xcall(processorid_t cpu, dtrace_xcall_t func, void *arg)
43{
44	if (cpu == DTRACE_CPUALL) {
45		xc_all(dtrace_xcall_func, (uint64_t)func, (uint64_t)arg);
46	} else {
47		xc_one(cpu, dtrace_xcall_func, (uint64_t)func, (uint64_t)arg);
48	}
49}
50
51/*ARGSUSED*/
52static void
53dtrace_sync_func(uint64_t arg1, uint64_t arg2)
54{
55	membar_consumer();
56}
57
58void
59dtrace_sync(void)
60{
61	membar_producer();
62	xc_all(dtrace_sync_func, 0, 0);
63}
64
65void
66dtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit))
67{
68	(*func)(PIOMAPBASE, PIOMAPBASE + PIOMAPSIZE);
69	(*func)(OFW_START_ADDR, OFW_END_ADDR);
70
71	if (hole_end > hole_start)
72		(*func)((uintptr_t)hole_start, (uintptr_t)hole_end);
73}
74
75int (*dtrace_pid_probe_ptr)(struct regs *);
76
77void
78dtrace_pid_probe(struct regs *rp)
79{
80	krwlock_t *rwp = &CPU->cpu_ft_lock;
81	uint32_t instr;
82
83	/*
84	 * This trap should only be invoked if there's a corresponding
85	 * enabled dtrace probe. If there isn't, send SIGILL as though
86	 * the process had executed an invalid trap instruction.
87	 */
88	rw_enter(rwp, RW_READER);
89	if (dtrace_pid_probe_ptr != NULL && (*dtrace_pid_probe_ptr)(rp) == 0) {
90		rw_exit(rwp);
91		return;
92	}
93	rw_exit(rwp);
94
95	/*
96	 * It is possible that we were preempted after entering the kernel,
97	 * and the tracepoint was removed. If it appears that the process hit
98	 * our reserved trap instruction, we call send SIGILL just as though
99	 * the user had executed an unused trap instruction.
100	 */
101	if (fuword32((void *)rp->r_pc, &instr) != 0 ||
102	    instr == FASTTRAP_INSTR) {
103		sigqueue_t *sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
104		proc_t *p = curproc;
105
106		sqp->sq_info.si_signo = SIGILL;
107		sqp->sq_info.si_code = ILL_ILLTRP;
108		sqp->sq_info.si_addr = (caddr_t)rp->r_pc;
109		sqp->sq_info.si_trapno = 0x38;
110
111		mutex_enter(&p->p_lock);
112		sigaddqa(p, curthread, sqp);
113		mutex_exit(&p->p_lock);
114		aston(curthread);
115	}
116}
117
118int (*dtrace_return_probe_ptr)(struct regs *);
119
120void
121dtrace_return_probe(struct regs *rp)
122{
123	krwlock_t *rwp;
124	uintptr_t npc = curthread->t_dtrace_npc;
125	uint8_t step = curthread->t_dtrace_step;
126	uint8_t ret = curthread->t_dtrace_ret;
127
128	if (curthread->t_dtrace_ast) {
129		aston(curthread);
130		curthread->t_sig_check = 1;
131	}
132
133	/*
134	 * Clear all user tracing flags.
135	 */
136	curthread->t_dtrace_ft = 0;
137
138	/*
139	 * If we weren't expecting to take a return probe trap, kill the
140	 * process as though it had just executed an unassigned trap
141	 * instruction.
142	 */
143	if (step == 0) {
144		tsignal(curthread, SIGILL);
145		return;
146	}
147
148	ASSERT(rp->r_npc == rp->r_pc + 4);
149
150	/*
151	 * If we hit this trap unrelated to a return probe, we're just here
152	 * to reset the AST flag since we deferred a signal until after we
153	 * logically single-stepped the instruction we copied out.
154	 */
155	if (ret == 0) {
156		rp->r_pc = npc;
157		rp->r_npc = npc + 4;
158		return;
159	}
160
161	/*
162	 * We need to wait until after we've called the dtrace_return_probe_ptr
163	 * function pointer to set %pc and %npc.
164	 */
165	rwp = &CPU->cpu_ft_lock;
166	rw_enter(rwp, RW_READER);
167	if (dtrace_return_probe_ptr != NULL)
168		(void) (*dtrace_return_probe_ptr)(rp);
169	rw_exit(rwp);
170	rp->r_pc = npc;
171	rp->r_npc = npc + 4;
172}
173
174void
175dtrace_safe_synchronous_signal(void)
176{
177	kthread_t *t = curthread;
178	struct regs *rp = lwptoregs(ttolwp(t));
179
180	ASSERT(t->t_dtrace_on);
181
182	/*
183	 * If we're not actively tracing an instruction, turn off tracing
184	 * flags. If the instruction we copied out caused a synchronous
185	 * trap, reset the pc and npc back to their original values and turn
186	 * off the flags.
187	 */
188	if (rp->r_pc != t->t_dtrace_scrpc && rp->r_pc != t->t_dtrace_astpc &&
189	    rp->r_npc != t->t_dtrace_astpc) {
190		t->t_dtrace_ft = 0;
191	} else if (rp->r_pc == t->t_dtrace_scrpc) {
192		rp->r_pc = t->t_dtrace_pc;
193		rp->r_npc = t->t_dtrace_npc;
194		t->t_dtrace_ft = 0;
195	}
196}
197
198int
199dtrace_safe_defer_signal(void)
200{
201	kthread_t *t = curthread;
202	struct regs *rp = lwptoregs(ttolwp(t));
203
204	ASSERT(t->t_dtrace_on);
205
206	/*
207	 * If we're not actively tracing an instruction, turn off tracing
208	 * flags.
209	 */
210	if (rp->r_pc != t->t_dtrace_scrpc && rp->r_pc != t->t_dtrace_astpc &&
211	    rp->r_npc != t->t_dtrace_astpc) {
212		t->t_dtrace_ft = 0;
213		return (0);
214	}
215
216	/*
217	 * Otherwise, make sure we'll return to the kernel after executing
218	 * the instruction we copied out.
219	 */
220	if (!t->t_dtrace_step) {
221		ASSERT(rp->r_pc == t->t_dtrace_scrpc);
222		rp->r_npc = t->t_dtrace_astpc;
223		t->t_dtrace_step = 1;
224	}
225
226	t->t_dtrace_ast = 1;
227
228	return (1);
229}
230
231/*
232 * Additional artificial frames for the machine type. For SPARC, we're already
233 * accounted for, so return 0.
234 */
235int
236dtrace_mach_aframes(void)
237{
238	return (0);
239}
240