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 /*
25 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
27 */
28 #include <sys/cdefs.h>
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/dtrace_impl.h>
33 #include <sys/kernel.h>
34 #include <sys/stack.h>
35 #include <sys/pcpu.h>
36
37 #include <machine/frame.h>
38 #include <machine/md_var.h>
39 #include <machine/encoding.h>
40 #include <machine/riscvreg.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 #define MAX_USTACK_DEPTH 2048
57
58 uint8_t dtrace_fuword8_nocheck(void *);
59 uint16_t dtrace_fuword16_nocheck(void *);
60 uint32_t dtrace_fuword32_nocheck(void *);
61 uint64_t dtrace_fuword64_nocheck(void *);
62
63 int dtrace_match_opcode(uint32_t, int, int);
64 int dtrace_instr_sdsp(uint32_t **);
65 int dtrace_instr_ret(uint32_t **);
66 int dtrace_instr_c_sdsp(uint32_t **);
67 int dtrace_instr_c_ret(uint32_t **);
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 uintptr_t caller;
75 register_t sp;
76 int scp_offset;
77 int depth;
78
79 depth = 0;
80 caller = solaris_cpu[curcpu].cpu_dtrace_caller;
81
82 if (intrpc != 0) {
83 pcstack[depth++] = (pc_t)intrpc;
84 }
85
86 /*
87 * Construct the unwind state, starting from this function. This frame,
88 * and 'aframes' others will be skipped.
89 */
90 __asm __volatile("mv %0, sp" : "=&r" (sp));
91
92 state.fp = (uintptr_t)__builtin_frame_address(0);
93 state.sp = (uintptr_t)sp;
94 state.pc = (uintptr_t)dtrace_getpcstack;
95
96 while (depth < pcstack_limit) {
97 if (!unwind_frame(curthread, &state))
98 break;
99
100 if (!INKERNEL(state.pc) || !kstack_contains(curthread,
101 (vm_offset_t)state.fp, sizeof(uintptr_t)))
102 break;
103
104 if (aframes > 0) {
105 aframes--;
106
107 /*
108 * fbt_invop() records the return address at the time
109 * the FBT probe fires. We need to insert this into the
110 * backtrace manually, since the stack frame state at
111 * the time of the probe does not capture it.
112 */
113 if (aframes == 0 && caller != 0)
114 pcstack[depth++] = caller;
115 } else {
116 pcstack[depth++] = state.pc;
117 }
118 }
119
120 for (; depth < pcstack_limit; depth++) {
121 pcstack[depth] = 0;
122 }
123 }
124
125 static int
dtrace_getustack_common(uint64_t * pcstack,int pcstack_limit,uintptr_t pc,uintptr_t fp)126 dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc,
127 uintptr_t fp)
128 {
129 volatile uint16_t *flags;
130 uintptr_t oldfp;
131 int ret;
132
133 oldfp = fp;
134 ret = 0;
135 flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
136
137 ASSERT(pcstack == NULL || pcstack_limit > 0);
138
139 while (pc != 0) {
140 /*
141 * We limit the number of times we can go around this
142 * loop to account for a circular stack.
143 */
144 if (ret++ >= MAX_USTACK_DEPTH) {
145 *flags |= CPU_DTRACE_BADSTACK;
146 cpu_core[curcpu].cpuc_dtrace_illval = fp;
147 break;
148 }
149
150 if (pcstack != NULL) {
151 *pcstack++ = (uint64_t)pc;
152 pcstack_limit--;
153 if (pcstack_limit <= 0)
154 break;
155 }
156
157 if (fp == 0)
158 break;
159
160 pc = dtrace_fuword64((void *)(fp - 1 * sizeof(uint64_t)));
161 fp = dtrace_fuword64((void *)(fp - 2 * sizeof(uint64_t)));
162
163 if (fp == oldfp) {
164 *flags |= CPU_DTRACE_BADSTACK;
165 cpu_core[curcpu].cpuc_dtrace_illval = fp;
166 break;
167 }
168 oldfp = fp;
169 }
170
171 return (ret);
172 }
173
174 void
dtrace_getupcstack(uint64_t * pcstack,int pcstack_limit)175 dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit)
176 {
177 volatile uint16_t *flags;
178 struct trapframe *tf;
179 uintptr_t pc, fp;
180 proc_t *p;
181 int n;
182
183 p = curproc;
184 flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
185
186 if (*flags & CPU_DTRACE_FAULT)
187 return;
188
189 if (pcstack_limit <= 0)
190 return;
191
192 /*
193 * If there's no user context we still need to zero the stack.
194 */
195 if (p == NULL || (tf = curthread->td_frame) == NULL)
196 goto zero;
197
198 *pcstack++ = (uint64_t)p->p_pid;
199 pcstack_limit--;
200
201 if (pcstack_limit <= 0)
202 return;
203
204 pc = tf->tf_sepc;
205 fp = tf->tf_s[0];
206
207 if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
208 /*
209 * In an entry probe. The frame pointer has not yet been
210 * pushed (that happens in the function prologue). The
211 * best approach is to add the current pc as a missing top
212 * of stack and back the pc up to the caller, which is stored
213 * at the current stack pointer address since the call
214 * instruction puts it there right before the branch.
215 */
216 *pcstack++ = (uint64_t)pc;
217 pcstack_limit--;
218 if (pcstack_limit <= 0)
219 return;
220
221 pc = tf->tf_ra;
222 }
223
224 n = dtrace_getustack_common(pcstack, pcstack_limit, pc, fp);
225 ASSERT(n >= 0);
226 ASSERT(n <= pcstack_limit);
227
228 pcstack += n;
229 pcstack_limit -= n;
230
231 zero:
232 while (pcstack_limit-- > 0)
233 *pcstack++ = 0;
234 }
235
236 int
dtrace_getustackdepth(void)237 dtrace_getustackdepth(void)
238 {
239 struct trapframe *tf;
240 uintptr_t pc, fp;
241 int n = 0;
242
243 if (curproc == NULL || (tf = curthread->td_frame) == NULL)
244 return (0);
245
246 if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_FAULT))
247 return (-1);
248
249 pc = tf->tf_sepc;
250 fp = tf->tf_s[0];
251
252 if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
253 /*
254 * In an entry probe. The frame pointer has not yet been
255 * pushed (that happens in the function prologue). The
256 * best approach is to add the current pc as a missing top
257 * of stack and back the pc up to the caller, which is stored
258 * at the current stack pointer address since the call
259 * instruction puts it there right before the branch.
260 */
261 pc = tf->tf_ra;
262 n++;
263 }
264
265 n += dtrace_getustack_common(NULL, 0, pc, fp);
266
267 return (0);
268 }
269
270 void
dtrace_getufpstack(uint64_t * pcstack,uint64_t * fpstack,int pcstack_limit)271 dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit)
272 {
273
274 printf("IMPLEMENT ME: %s\n", __func__);
275 }
276
277 /*ARGSUSED*/
278 uint64_t
dtrace_getarg(int arg,int aframes)279 dtrace_getarg(int arg, int aframes)
280 {
281
282 printf("IMPLEMENT ME: %s\n", __func__);
283
284 return (0);
285 }
286
287 int
dtrace_getstackdepth(int aframes)288 dtrace_getstackdepth(int aframes)
289 {
290 struct unwind_state state;
291 int scp_offset;
292 register_t sp;
293 int depth;
294 bool done;
295
296 depth = 1;
297 done = false;
298
299 __asm __volatile("mv %0, sp" : "=&r" (sp));
300
301 state.fp = (uintptr_t)__builtin_frame_address(0);
302 state.sp = sp;
303 state.pc = (uintptr_t)dtrace_getstackdepth;
304
305 do {
306 done = !unwind_frame(curthread, &state);
307 if (!INKERNEL(state.pc) || !INKERNEL(state.fp))
308 break;
309 depth++;
310 } while (!done);
311
312 if (depth < aframes)
313 return (0);
314 else
315 return (depth - aframes);
316 }
317
318 ulong_t
dtrace_getreg(struct trapframe * frame,uint_t reg)319 dtrace_getreg(struct trapframe *frame, uint_t reg)
320 {
321 switch (reg) {
322 case REG_ZERO:
323 return (0);
324 case REG_RA:
325 return (frame->tf_ra);
326 case REG_SP:
327 return (frame->tf_sp);
328 case REG_GP:
329 return (frame->tf_gp);
330 case REG_TP:
331 return (frame->tf_tp);
332 case REG_T0 ... REG_T2:
333 return (frame->tf_t[reg - REG_T0]);
334 case REG_S0 ... REG_S1:
335 return (frame->tf_s[reg - REG_S0]);
336 case REG_A0 ... REG_A7:
337 return (frame->tf_a[reg - REG_A0]);
338 case REG_S2 ... REG_S11:
339 return (frame->tf_s[reg - REG_S2 + 2]);
340 case REG_T3 ... REG_T6:
341 return (frame->tf_t[reg - REG_T3 + 3]);
342 case REG_PC:
343 return (frame->tf_sepc);
344 default:
345 DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
346 return (0);
347 }
348 /* NOTREACHED */
349 }
350
351 static int
dtrace_copycheck(uintptr_t uaddr,uintptr_t kaddr,size_t size)352 dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size)
353 {
354
355 if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) {
356 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
357 cpu_core[curcpu].cpuc_dtrace_illval = uaddr;
358 return (0);
359 }
360
361 return (1);
362 }
363
364 void
dtrace_copyin(uintptr_t uaddr,uintptr_t kaddr,size_t size,volatile uint16_t * flags)365 dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size,
366 volatile uint16_t *flags)
367 {
368
369 if (dtrace_copycheck(uaddr, kaddr, size))
370 dtrace_copy(uaddr, kaddr, size);
371 }
372
373 void
dtrace_copyout(uintptr_t kaddr,uintptr_t uaddr,size_t size,volatile uint16_t * flags)374 dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,
375 volatile uint16_t *flags)
376 {
377
378 if (dtrace_copycheck(uaddr, kaddr, size))
379 dtrace_copy(kaddr, uaddr, size);
380 }
381
382 void
dtrace_copyinstr(uintptr_t uaddr,uintptr_t kaddr,size_t size,volatile uint16_t * flags)383 dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
384 volatile uint16_t *flags)
385 {
386
387 if (dtrace_copycheck(uaddr, kaddr, size))
388 dtrace_copystr(uaddr, kaddr, size, flags);
389 }
390
391 void
dtrace_copyoutstr(uintptr_t kaddr,uintptr_t uaddr,size_t size,volatile uint16_t * flags)392 dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,
393 volatile uint16_t *flags)
394 {
395
396 if (dtrace_copycheck(uaddr, kaddr, size))
397 dtrace_copystr(kaddr, uaddr, size, flags);
398 }
399
400 uint8_t
dtrace_fuword8(void * uaddr)401 dtrace_fuword8(void *uaddr)
402 {
403
404 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
405 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
406 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
407 return (0);
408 }
409
410 return (dtrace_fuword8_nocheck(uaddr));
411 }
412
413 uint16_t
dtrace_fuword16(void * uaddr)414 dtrace_fuword16(void *uaddr)
415 {
416
417 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
418 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
419 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
420 return (0);
421 }
422
423 return (dtrace_fuword16_nocheck(uaddr));
424 }
425
426 uint32_t
dtrace_fuword32(void * uaddr)427 dtrace_fuword32(void *uaddr)
428 {
429
430 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
431 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
432 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
433 return (0);
434 }
435
436 return (dtrace_fuword32_nocheck(uaddr));
437 }
438
439 uint64_t
dtrace_fuword64(void * uaddr)440 dtrace_fuword64(void *uaddr)
441 {
442
443 if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
444 DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
445 cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
446 return (0);
447 }
448
449 return (dtrace_fuword64_nocheck(uaddr));
450 }
451
452 int
dtrace_match_opcode(uint32_t insn,int match,int mask)453 dtrace_match_opcode(uint32_t insn, int match, int mask)
454 {
455 if (((insn ^ match) & mask) == 0)
456 return (1);
457
458 return (0);
459 }
460
461 int
dtrace_instr_sdsp(uint32_t ** instr)462 dtrace_instr_sdsp(uint32_t **instr)
463 {
464 if (dtrace_match_opcode(**instr, (MATCH_SD | RS2_RA | RS1_SP),
465 (MASK_SD | RS2_MASK | RS1_MASK)))
466 return (1);
467
468 return (0);
469 }
470
471 int
dtrace_instr_c_sdsp(uint32_t ** instr)472 dtrace_instr_c_sdsp(uint32_t **instr)
473 {
474 uint16_t *instr1;
475 int i;
476
477 for (i = 0; i < 2; i++) {
478 instr1 = (uint16_t *)(*instr) + i;
479 if (dtrace_match_opcode(*instr1, (MATCH_C_SDSP | RS2_C_RA),
480 (MASK_C_SDSP | RS2_C_MASK))) {
481 *instr = (uint32_t *)instr1;
482 return (1);
483 }
484 }
485
486 return (0);
487 }
488
489 int
dtrace_instr_ret(uint32_t ** instr)490 dtrace_instr_ret(uint32_t **instr)
491 {
492 if (dtrace_match_opcode(**instr, (MATCH_JALR | (X_RA << RS1_SHIFT)),
493 (MASK_JALR | RD_MASK | RS1_MASK | IMM_MASK)))
494 return (1);
495
496 return (0);
497 }
498
499 int
dtrace_instr_c_ret(uint32_t ** instr)500 dtrace_instr_c_ret(uint32_t **instr)
501 {
502 uint16_t *instr1;
503 int i;
504
505 for (i = 0; i < 2; i++) {
506 instr1 = (uint16_t *)(*instr) + i;
507 if (dtrace_match_opcode(*instr1,
508 (MATCH_C_JR | (X_RA << RD_SHIFT)), (MASK_C_JR | RD_MASK))) {
509 *instr = (uint32_t *)instr1;
510 return (1);
511 }
512 }
513
514 return (0);
515 }
516