xref: /freebsd/sys/cddl/dev/dtrace/riscv/dtrace_isa.c (revision 1719886f6d08408b834d270c59ffcfd821c8f63a)
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
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
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
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
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
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
278 dtrace_getarg(int arg, int aframes)
279 {
280 
281 	printf("IMPLEMENT ME: %s\n", __func__);
282 
283 	return (0);
284 }
285 
286 int
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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