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 * Portions Copyright 2016 Ruslan Bukin <br@bsdpad.com>
23 *
24 * $FreeBSD$
25 */
26 /*
27 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
29 */
30 #include <sys/cdefs.h>
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/stack.h>
36 #include <sys/pcpu.h>
37
38 #include <machine/frame.h>
39 #include <machine/md_var.h>
40 #include <machine/reg.h>
41
42 #include <vm/vm.h>
43 #include <vm/vm_param.h>
44 #include <vm/pmap.h>
45
46 #include <machine/atomic.h>
47 #include <machine/db_machdep.h>
48 #include <machine/md_var.h>
49 #include <machine/stack.h>
50 #include <ddb/db_sym.h>
51 #include <ddb/ddb.h>
52 #include <sys/kdb.h>
53
54 #include "regset.h"
55
56 /*
57 * Wee need some reasonable default to prevent backtrace code
58 * from wandering too far
59 */
60 #define MAX_FUNCTION_SIZE 0x10000
61 #define MAX_PROLOGUE_SIZE 0x100
62 #define MAX_USTACK_DEPTH 2048
63
64 uint8_t dtrace_fuword8_nocheck(void *);
65 uint16_t dtrace_fuword16_nocheck(void *);
66 uint32_t dtrace_fuword32_nocheck(void *);
67 uint64_t dtrace_fuword64_nocheck(void *);
68
69 void
dtrace_getpcstack(pc_t * pcstack,int pcstack_limit,int aframes,uint32_t * intrpc)70 dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
71 uint32_t *intrpc)
72 {
73 struct unwind_state state;
74 int scp_offset;
75 register_t sp;
76 int depth;
77
78 depth = 0;
79
80 if (intrpc != 0) {
81 pcstack[depth++] = (pc_t) intrpc;
82 }
83
84 aframes++;
85
86 __asm __volatile("mv %0, sp" : "=&r" (sp));
87
88 state.fp = (uint64_t)__builtin_frame_address(0);
89 state.sp = sp;
90 state.pc = (uint64_t)dtrace_getpcstack;
91
92 while (depth < pcstack_limit) {
93 if (unwind_frame(&state))
94 break;
95
96 if (!INKERNEL(state.pc) || !INKERNEL(state.fp))
97 break;
98
99 /*
100 * NB: Unlike some other architectures, we don't need to
101 * explicitly insert cpu_dtrace_caller as it appears in the
102 * normal kernel stack trace rather than a special trap frame.
103 */
104 if (aframes > 0) {
105 aframes--;
106 } else {
107 pcstack[depth++] = state.pc;
108 }
109
110 }
111
112 for (; depth < pcstack_limit; depth++) {
113 pcstack[depth] = 0;
114 }
115 }
116
117 static int
dtrace_getustack_common(uint64_t * pcstack,int pcstack_limit,uintptr_t pc,uintptr_t fp)118 dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc,
119 uintptr_t fp)
120 {
121 volatile uint16_t *flags;
122 uintptr_t oldfp;
123 int ret;
124
125 ret = 0;
126 flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
127
128 ASSERT(pcstack == NULL || pcstack_limit > 0);
129
130 while (pc != 0) {
131 /*
132 * We limit the number of times we can go around this
133 * loop to account for a circular stack.
134 */
135 if (ret++ >= MAX_USTACK_DEPTH) {
136 *flags |= CPU_DTRACE_BADSTACK;
137 cpu_core[curcpu].cpuc_dtrace_illval = fp;
138 break;
139 }
140
141 if (pcstack != NULL) {
142 *pcstack++ = (uint64_t)pc;
143 pcstack_limit--;
144 if (pcstack_limit <= 0)
145 break;
146 }
147
148 if (fp == 0)
149 break;
150
151 pc = dtrace_fuword64((void *)(fp +
152 offsetof(struct riscv_frame, f_retaddr)));
153 fp = dtrace_fuword64((void *)fp);
154
155 if (fp == oldfp) {
156 *flags |= CPU_DTRACE_BADSTACK;
157 cpu_core[curcpu].cpuc_dtrace_illval = fp;
158 break;
159 }
160 }
161
162 return (ret);
163 }
164
165 void
dtrace_getupcstack(uint64_t * pcstack,int pcstack_limit)166 dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit)
167 {
168 volatile uint16_t *flags;
169 struct trapframe *tf;
170 uintptr_t pc, sp, fp;
171 proc_t *p;
172 int n;
173
174 p = curproc;
175 flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
176
177 if (*flags & CPU_DTRACE_FAULT)
178 return;
179
180 if (pcstack_limit <= 0)
181 return;
182
183 /*
184 * If there's no user context we still need to zero the stack.
185 */
186 if (p == NULL || (tf = curthread->td_frame) == NULL)
187 goto zero;
188
189 *pcstack++ = (uint64_t)p->p_pid;
190 pcstack_limit--;
191
192 if (pcstack_limit <= 0)
193 return;
194
195 pc = tf->tf_sepc;
196 sp = tf->tf_sp;
197 fp = tf->tf_s[0];
198
199 if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
200 /*
201 * In an entry probe. The frame pointer has not yet been
202 * pushed (that happens in the function prologue). The
203 * best approach is to add the current pc as a missing top
204 * of stack and back the pc up to the caller, which is stored
205 * at the current stack pointer address since the call
206 * instruction puts it there right before the branch.
207 */
208
209 *pcstack++ = (uint64_t)pc;
210 pcstack_limit--;
211 if (pcstack_limit <= 0)
212 return;
213
214 pc = tf->tf_ra;
215 }
216
217 n = dtrace_getustack_common(pcstack, pcstack_limit, pc, fp);
218 ASSERT(n >= 0);
219 ASSERT(n <= pcstack_limit);
220
221 pcstack += n;
222 pcstack_limit -= n;
223
224 zero:
225 while (pcstack_limit-- > 0)
226 *pcstack++ = 0;
227 }
228
229 int
dtrace_getustackdepth(void)230 dtrace_getustackdepth(void)
231 {
232
233 printf("IMPLEMENT ME: %s\n", __func__);
234
235 return (0);
236 }
237
238 void
dtrace_getufpstack(uint64_t * pcstack,uint64_t * fpstack,int pcstack_limit)239 dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit)
240 {
241
242 printf("IMPLEMENT ME: %s\n", __func__);
243 }
244
245 /*ARGSUSED*/
246 uint64_t
dtrace_getarg(int arg,int aframes)247 dtrace_getarg(int arg, int aframes)
248 {
249
250 printf("IMPLEMENT ME: %s\n", __func__);
251
252 return (0);
253 }
254
255 int
dtrace_getstackdepth(int aframes)256 dtrace_getstackdepth(int aframes)
257 {
258 struct unwind_state state;
259 int scp_offset;
260 register_t sp;
261 int depth;
262 int done;
263
264 depth = 1;
265 done = 0;
266
267 __asm __volatile("mv %0, sp" : "=&r" (sp));
268
269 state.fp = (uint64_t)__builtin_frame_address(0);
270 state.sp = sp;
271 state.pc = (uint64_t)dtrace_getstackdepth;
272
273 do {
274 done = unwind_frame(&state);
275 if (!INKERNEL(state.pc) || !INKERNEL(state.fp))
276 break;
277 depth++;
278 } while (!done);
279
280 if (depth < aframes)
281 return (0);
282 else
283 return (depth - aframes);
284 }
285
286 ulong_t
dtrace_getreg(struct trapframe * rp,uint_t reg)287 dtrace_getreg(struct trapframe *rp, uint_t reg)
288 {
289
290 printf("IMPLEMENT ME: %s\n", __func__);
291
292 return (0);
293 }
294
295 static int
dtrace_copycheck(uintptr_t uaddr,uintptr_t kaddr,size_t size)296 dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size)
297 {
298
299 if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) {
300 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
301 cpu_core[curcpu].cpuc_dtrace_illval = uaddr;
302 return (0);
303 }
304
305 return (1);
306 }
307
308 void
dtrace_copyin(uintptr_t uaddr,uintptr_t kaddr,size_t size,volatile uint16_t * flags)309 dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size,
310 volatile uint16_t *flags)
311 {
312
313 if (dtrace_copycheck(uaddr, kaddr, size))
314 dtrace_copy(uaddr, kaddr, size);
315 }
316
317 void
dtrace_copyout(uintptr_t kaddr,uintptr_t uaddr,size_t size,volatile uint16_t * flags)318 dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,
319 volatile uint16_t *flags)
320 {
321
322 if (dtrace_copycheck(uaddr, kaddr, size))
323 dtrace_copy(kaddr, uaddr, size);
324 }
325
326 void
dtrace_copyinstr(uintptr_t uaddr,uintptr_t kaddr,size_t size,volatile uint16_t * flags)327 dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
328 volatile uint16_t *flags)
329 {
330
331 if (dtrace_copycheck(uaddr, kaddr, size))
332 dtrace_copystr(uaddr, kaddr, size, flags);
333 }
334
335 void
dtrace_copyoutstr(uintptr_t kaddr,uintptr_t uaddr,size_t size,volatile uint16_t * flags)336 dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,
337 volatile uint16_t *flags)
338 {
339
340 if (dtrace_copycheck(uaddr, kaddr, size))
341 dtrace_copystr(kaddr, uaddr, size, flags);
342 }
343
344 uint8_t
dtrace_fuword8(void * uaddr)345 dtrace_fuword8(void *uaddr)
346 {
347
348 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
349 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
350 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
351 return (0);
352 }
353
354 return (dtrace_fuword8_nocheck(uaddr));
355 }
356
357 uint16_t
dtrace_fuword16(void * uaddr)358 dtrace_fuword16(void *uaddr)
359 {
360
361 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
362 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
363 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
364 return (0);
365 }
366
367 return (dtrace_fuword16_nocheck(uaddr));
368 }
369
370 uint32_t
dtrace_fuword32(void * uaddr)371 dtrace_fuword32(void *uaddr)
372 {
373
374 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
375 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
376 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
377 return (0);
378 }
379
380 return (dtrace_fuword32_nocheck(uaddr));
381 }
382
383 uint64_t
dtrace_fuword64(void * uaddr)384 dtrace_fuword64(void *uaddr)
385 {
386
387 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
388 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
389 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
390 return (0);
391 }
392
393 return (dtrace_fuword64_nocheck(uaddr));
394 }
395