xref: /freebsd/sys/cddl/dev/dtrace/riscv/dtrace_isa.c (revision 82283cad)
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