1285009Sbr/*
2285009Sbr * CDDL HEADER START
3285009Sbr *
4285009Sbr * The contents of this file are subject to the terms of the
5285009Sbr * Common Development and Distribution License, Version 1.0 only
6285009Sbr * (the "License").  You may not use this file except in compliance
7285009Sbr * with the License.
8285009Sbr *
9285009Sbr * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10285009Sbr * or http://www.opensolaris.org/os/licensing.
11285009Sbr * See the License for the specific language governing permissions
12285009Sbr * and limitations under the License.
13285009Sbr *
14285009Sbr * When distributing Covered Code, include this CDDL HEADER in each
15285009Sbr * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16285009Sbr * If applicable, add the following below this CDDL HEADER, with the
17285009Sbr * fields enclosed by brackets "[]" replaced with your own identifying
18285009Sbr * information: Portions Copyright [yyyy] [name of copyright owner]
19285009Sbr *
20285009Sbr * CDDL HEADER END
21285009Sbr *
22285009Sbr * $FreeBSD: releng/11.0/sys/cddl/dev/dtrace/aarch64/dtrace_subr.c 300619 2016-05-24 17:38:27Z bz $
23285009Sbr *
24285009Sbr */
25285009Sbr/*
26285009Sbr * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
27285009Sbr * Use is subject to license terms.
28285009Sbr */
29285009Sbr
30285009Sbr#include <sys/cdefs.h>
31285009Sbr__FBSDID("$FreeBSD: releng/11.0/sys/cddl/dev/dtrace/aarch64/dtrace_subr.c 300619 2016-05-24 17:38:27Z bz $");
32285009Sbr
33285009Sbr#include <sys/param.h>
34285009Sbr#include <sys/systm.h>
35285009Sbr#include <sys/types.h>
36285009Sbr#include <sys/kernel.h>
37285009Sbr#include <sys/malloc.h>
38285009Sbr#include <sys/kmem.h>
39285009Sbr#include <sys/smp.h>
40285009Sbr#include <sys/dtrace_impl.h>
41285009Sbr#include <sys/dtrace_bsd.h>
42285009Sbr#include <machine/armreg.h>
43285009Sbr#include <machine/clock.h>
44285009Sbr#include <machine/frame.h>
45285009Sbr#include <machine/trap.h>
46300619Sbz#include <machine/vmparam.h>
47285009Sbr#include <vm/pmap.h>
48285009Sbr
49285009Sbrextern dtrace_id_t	dtrace_probeid_error;
50285009Sbrextern int (*dtrace_invop_jump_addr)(struct trapframe *);
51285009Sbrextern void dtrace_getnanotime(struct timespec *tsp);
52285009Sbr
53298171Smarkjint dtrace_invop(uintptr_t, struct trapframe *, uintptr_t);
54285009Sbrvoid dtrace_invop_init(void);
55285009Sbrvoid dtrace_invop_uninit(void);
56285009Sbr
57285009Sbrtypedef struct dtrace_invop_hdlr {
58298171Smarkj	int (*dtih_func)(uintptr_t, struct trapframe *, uintptr_t);
59285009Sbr	struct dtrace_invop_hdlr *dtih_next;
60285009Sbr} dtrace_invop_hdlr_t;
61285009Sbr
62285009Sbrdtrace_invop_hdlr_t *dtrace_invop_hdlr;
63285009Sbr
64285009Sbrint
65298171Smarkjdtrace_invop(uintptr_t addr, struct trapframe *frame, uintptr_t eax)
66285009Sbr{
67285009Sbr	dtrace_invop_hdlr_t *hdlr;
68285009Sbr	int rval;
69285009Sbr
70285009Sbr	for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next)
71298171Smarkj		if ((rval = hdlr->dtih_func(addr, frame, eax)) != 0)
72285009Sbr			return (rval);
73285009Sbr
74285009Sbr	return (0);
75285009Sbr}
76285009Sbr
77285009Sbr
78285009Sbrvoid
79298171Smarkjdtrace_invop_add(int (*func)(uintptr_t, struct trapframe *, uintptr_t))
80285009Sbr{
81285009Sbr	dtrace_invop_hdlr_t *hdlr;
82285009Sbr
83285009Sbr	hdlr = kmem_alloc(sizeof (dtrace_invop_hdlr_t), KM_SLEEP);
84285009Sbr	hdlr->dtih_func = func;
85285009Sbr	hdlr->dtih_next = dtrace_invop_hdlr;
86285009Sbr	dtrace_invop_hdlr = hdlr;
87285009Sbr}
88285009Sbr
89285009Sbrvoid
90298171Smarkjdtrace_invop_remove(int (*func)(uintptr_t, struct trapframe *, uintptr_t))
91285009Sbr{
92285009Sbr	dtrace_invop_hdlr_t *hdlr, *prev;
93285009Sbr
94285009Sbr	hdlr = dtrace_invop_hdlr;
95285009Sbr	prev = NULL;
96285009Sbr
97285009Sbr	for (;;) {
98285009Sbr		if (hdlr == NULL)
99285009Sbr			panic("attempt to remove non-existent invop handler");
100285009Sbr
101285009Sbr		if (hdlr->dtih_func == func)
102285009Sbr			break;
103285009Sbr
104285009Sbr		prev = hdlr;
105285009Sbr		hdlr = hdlr->dtih_next;
106285009Sbr	}
107285009Sbr
108285009Sbr	if (prev == NULL) {
109285009Sbr		ASSERT(dtrace_invop_hdlr == hdlr);
110285009Sbr		dtrace_invop_hdlr = hdlr->dtih_next;
111285009Sbr	} else {
112285009Sbr		ASSERT(dtrace_invop_hdlr != hdlr);
113285009Sbr		prev->dtih_next = hdlr->dtih_next;
114285009Sbr	}
115285009Sbr
116285009Sbr	kmem_free(hdlr, 0);
117285009Sbr}
118285009Sbr
119285009Sbr/*ARGSUSED*/
120285009Sbrvoid
121285009Sbrdtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit))
122285009Sbr{
123285009Sbr
124300611Sandrew	(*func)(0, (uintptr_t)VM_MIN_KERNEL_ADDRESS);
125285009Sbr}
126285009Sbr
127285009Sbrvoid
128285009Sbrdtrace_xcall(processorid_t cpu, dtrace_xcall_t func, void *arg)
129285009Sbr{
130285009Sbr	cpuset_t cpus;
131285009Sbr
132285009Sbr	if (cpu == DTRACE_CPUALL)
133285009Sbr		cpus = all_cpus;
134285009Sbr	else
135285009Sbr		CPU_SETOF(cpu, &cpus);
136285009Sbr
137285009Sbr	smp_rendezvous_cpus(cpus, smp_no_rendevous_barrier, func,
138285009Sbr	    smp_no_rendevous_barrier, arg);
139285009Sbr}
140285009Sbr
141285009Sbrstatic void
142285009Sbrdtrace_sync_func(void)
143285009Sbr{
144285009Sbr
145285009Sbr}
146285009Sbr
147285009Sbrvoid
148285009Sbrdtrace_sync(void)
149285009Sbr{
150285009Sbr
151285009Sbr	dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL);
152285009Sbr}
153285009Sbr
154285009Sbr/*
155285009Sbr * DTrace needs a high resolution time function which can
156285009Sbr * be called from a probe context and guaranteed not to have
157285009Sbr * instrumented with probes itself.
158285009Sbr *
159285009Sbr * Returns nanoseconds since boot.
160285009Sbr */
161285009Sbruint64_t
162285009Sbrdtrace_gethrtime()
163285009Sbr{
164285009Sbr	struct timespec curtime;
165285009Sbr
166285009Sbr	nanouptime(&curtime);
167285009Sbr
168285009Sbr	return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec);
169285009Sbr
170285009Sbr}
171285009Sbr
172285009Sbruint64_t
173285009Sbrdtrace_gethrestime(void)
174285009Sbr{
175285009Sbr	struct timespec current_time;
176285009Sbr
177285009Sbr	dtrace_getnanotime(&current_time);
178285009Sbr
179285009Sbr	return (current_time.tv_sec * 1000000000UL + current_time.tv_nsec);
180285009Sbr}
181285009Sbr
182285009Sbr/* Function to handle DTrace traps during probes. See arm64/arm64/trap.c */
183285009Sbrint
184285009Sbrdtrace_trap(struct trapframe *frame, u_int type)
185285009Sbr{
186285009Sbr	/*
187285009Sbr	 * A trap can occur while DTrace executes a probe. Before
188285009Sbr	 * executing the probe, DTrace blocks re-scheduling and sets
189285009Sbr	 * a flag in it's per-cpu flags to indicate that it doesn't
190285009Sbr	 * want to fault. On returning from the probe, the no-fault
191285009Sbr	 * flag is cleared and finally re-scheduling is enabled.
192285009Sbr	 *
193285009Sbr	 * Check if DTrace has enabled 'no-fault' mode:
194285009Sbr	 *
195285009Sbr	 */
196285009Sbr
197285009Sbr	if ((cpu_core[curcpu].cpuc_dtrace_flags & CPU_DTRACE_NOFAULT) != 0) {
198285009Sbr		/*
199285009Sbr		 * There are only a couple of trap types that are expected.
200285009Sbr		 * All the rest will be handled in the usual way.
201285009Sbr		 */
202285009Sbr		switch (type) {
203285009Sbr		case EXCP_DATA_ABORT:
204285009Sbr			/* Flag a bad address. */
205285009Sbr			cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
206285009Sbr			cpu_core[curcpu].cpuc_dtrace_illval = 0;
207285009Sbr
208285009Sbr			/*
209285009Sbr			 * Offset the instruction pointer to the instruction
210285009Sbr			 * following the one causing the fault.
211285009Sbr			 */
212285009Sbr			frame->tf_elr += 4;
213285009Sbr			return (1);
214285009Sbr		default:
215285009Sbr			/* Handle all other traps in the usual way. */
216285009Sbr			break;
217285009Sbr		}
218285009Sbr	}
219285009Sbr
220285009Sbr	/* Handle the trap in the usual way. */
221285009Sbr	return (0);
222285009Sbr}
223285009Sbr
224285009Sbrvoid
225285009Sbrdtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which,
226285009Sbr    int fault, int fltoffs, uintptr_t illval)
227285009Sbr{
228285009Sbr
229285009Sbr	dtrace_probe(dtrace_probeid_error, (uint64_t)(uintptr_t)state,
230285009Sbr	    (uintptr_t)epid,
231285009Sbr	    (uintptr_t)which, (uintptr_t)fault, (uintptr_t)fltoffs);
232285009Sbr}
233285009Sbr
234285009Sbrstatic int
235285009Sbrdtrace_invop_start(struct trapframe *frame)
236285009Sbr{
237285009Sbr	int data, invop, reg, update_sp;
238285009Sbr	register_t arg1, arg2;
239285009Sbr	register_t *sp;
240285009Sbr	int offs;
241285009Sbr	int tmp;
242285009Sbr	int i;
243285009Sbr
244298171Smarkj	invop = dtrace_invop(frame->tf_elr, frame, frame->tf_elr);
245285009Sbr
246285009Sbr	tmp = (invop & LDP_STP_MASK);
247285009Sbr	if (tmp == STP_64 || tmp == LDP_64) {
248285009Sbr		sp = (register_t *)frame->tf_sp;
249285009Sbr		data = invop;
250285009Sbr		arg1 = (data >> ARG1_SHIFT) & ARG1_MASK;
251285009Sbr		arg2 = (data >> ARG2_SHIFT) & ARG2_MASK;
252285009Sbr
253285009Sbr		offs = (data >> OFFSET_SHIFT) & OFFSET_MASK;
254285009Sbr
255285009Sbr		switch (tmp) {
256285009Sbr		case STP_64:
257285009Sbr			if (offs >> (OFFSET_SIZE - 1))
258285009Sbr				sp -= (~offs & OFFSET_MASK) + 1;
259285009Sbr			else
260285009Sbr				sp += (offs);
261285009Sbr			*(sp + 0) = frame->tf_x[arg1];
262285009Sbr			*(sp + 1) = frame->tf_x[arg2];
263285009Sbr			break;
264285009Sbr		case LDP_64:
265285009Sbr			frame->tf_x[arg1] = *(sp + 0);
266285009Sbr			frame->tf_x[arg2] = *(sp + 1);
267285009Sbr			if (offs >> (OFFSET_SIZE - 1))
268285009Sbr				sp -= (~offs & OFFSET_MASK) + 1;
269285009Sbr			else
270285009Sbr				sp += (offs);
271285009Sbr			break;
272285009Sbr		default:
273285009Sbr			break;
274285009Sbr		}
275285009Sbr
276285009Sbr		/* Update the stack pointer and program counter to continue */
277285009Sbr		frame->tf_sp = (register_t)sp;
278285009Sbr		frame->tf_elr += INSN_SIZE;
279285009Sbr		return (0);
280285009Sbr	}
281285009Sbr
282285009Sbr	if ((invop & B_MASK) == B_INSTR) {
283285009Sbr		data = (invop & B_DATA_MASK);
284285009Sbr		/* The data is the number of 4-byte words to change the pc */
285285009Sbr		data *= 4;
286285009Sbr		frame->tf_elr += data;
287285009Sbr		return (0);
288285009Sbr	}
289285009Sbr
290285009Sbr	if (invop == RET_INSTR) {
291285009Sbr		frame->tf_elr = frame->tf_lr;
292285009Sbr		return (0);
293285009Sbr	}
294285009Sbr
295285009Sbr	return (-1);
296285009Sbr}
297285009Sbr
298285009Sbrvoid
299285009Sbrdtrace_invop_init(void)
300285009Sbr{
301285009Sbr
302285009Sbr	dtrace_invop_jump_addr = dtrace_invop_start;
303285009Sbr}
304285009Sbr
305285009Sbrvoid
306285009Sbrdtrace_invop_uninit(void)
307285009Sbr{
308285009Sbr
309285009Sbr	dtrace_invop_jump_addr = 0;
310285009Sbr}
311