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