Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/cddl/dev/dtrace/aarch64/dtrace_subr.c
48375 views
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
*/
23
/*
24
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
25
* Use is subject to license terms.
26
*/
27
28
#include <sys/param.h>
29
#include <sys/systm.h>
30
#include <sys/kernel.h>
31
#include <sys/malloc.h>
32
#include <sys/kmem.h>
33
#include <sys/proc.h>
34
#include <sys/smp.h>
35
#include <sys/dtrace_impl.h>
36
#include <sys/dtrace_bsd.h>
37
#include <cddl/dev/dtrace/dtrace_cddl.h>
38
#include <machine/armreg.h>
39
#include <machine/clock.h>
40
#include <machine/frame.h>
41
#include <machine/trap.h>
42
#include <machine/vmparam.h>
43
#include <vm/pmap.h>
44
45
extern dtrace_id_t dtrace_probeid_error;
46
extern int (*dtrace_invop_jump_addr)(struct trapframe *);
47
extern void dtrace_getnanotime(struct timespec *tsp);
48
extern void dtrace_getnanouptime(struct timespec *tsp);
49
50
int dtrace_invop(uintptr_t, struct trapframe *, uintptr_t);
51
void dtrace_invop_init(void);
52
void dtrace_invop_uninit(void);
53
54
typedef struct dtrace_invop_hdlr {
55
int (*dtih_func)(uintptr_t, struct trapframe *, uintptr_t);
56
struct dtrace_invop_hdlr *dtih_next;
57
} dtrace_invop_hdlr_t;
58
59
dtrace_invop_hdlr_t *dtrace_invop_hdlr;
60
61
int
62
dtrace_invop(uintptr_t addr, struct trapframe *frame, uintptr_t eax)
63
{
64
struct thread *td;
65
dtrace_invop_hdlr_t *hdlr;
66
int rval;
67
68
rval = 0;
69
td = curthread;
70
td->t_dtrace_trapframe = frame;
71
for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next)
72
if ((rval = hdlr->dtih_func(addr, frame, eax)) != 0)
73
break;
74
td->t_dtrace_trapframe = NULL;
75
return (rval);
76
}
77
78
void
79
dtrace_invop_add(int (*func)(uintptr_t, struct trapframe *, uintptr_t))
80
{
81
dtrace_invop_hdlr_t *hdlr;
82
83
hdlr = kmem_alloc(sizeof (dtrace_invop_hdlr_t), KM_SLEEP);
84
hdlr->dtih_func = func;
85
hdlr->dtih_next = dtrace_invop_hdlr;
86
dtrace_invop_hdlr = hdlr;
87
}
88
89
void
90
dtrace_invop_remove(int (*func)(uintptr_t, struct trapframe *, uintptr_t))
91
{
92
dtrace_invop_hdlr_t *hdlr, *prev;
93
94
hdlr = dtrace_invop_hdlr;
95
prev = NULL;
96
97
for (;;) {
98
if (hdlr == NULL)
99
panic("attempt to remove non-existent invop handler");
100
101
if (hdlr->dtih_func == func)
102
break;
103
104
prev = hdlr;
105
hdlr = hdlr->dtih_next;
106
}
107
108
if (prev == NULL) {
109
ASSERT(dtrace_invop_hdlr == hdlr);
110
dtrace_invop_hdlr = hdlr->dtih_next;
111
} else {
112
ASSERT(dtrace_invop_hdlr != hdlr);
113
prev->dtih_next = hdlr->dtih_next;
114
}
115
116
kmem_free(hdlr, 0);
117
}
118
119
/*ARGSUSED*/
120
void
121
dtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit))
122
{
123
124
(*func)(0, (uintptr_t)VM_MIN_KERNEL_ADDRESS);
125
}
126
127
static uint64_t nsec_scale;
128
129
#define SCALE_SHIFT 25
130
131
/*
132
* Choose scaling factors which let us convert a cntvct_el0 value to nanoseconds
133
* without overflow, as in the amd64 implementation.
134
*
135
* Documentation for the ARM generic timer states that typical counter
136
* frequencies are in the range 1Mhz-50Mhz; in ARMv9 the frequency is fixed at
137
* 1GHz. The lower bound of 1MHz forces the shift to be at most 25 bits. At
138
* that frequency, the calculation (hi * scale) << (32 - shift) will not
139
* overflow for over 100 years, assuming that the counter value starts at 0 upon
140
* boot.
141
*/
142
static void
143
dtrace_gethrtime_init(void *arg __unused)
144
{
145
uint64_t freq;
146
147
freq = READ_SPECIALREG(cntfrq_el0);
148
nsec_scale = ((uint64_t)NANOSEC << SCALE_SHIFT) / freq;
149
}
150
SYSINIT(dtrace_gethrtime_init, SI_SUB_DTRACE, SI_ORDER_ANY,
151
dtrace_gethrtime_init, NULL);
152
153
/*
154
* DTrace needs a high resolution time function which can be called from a
155
* probe context and guaranteed not to have instrumented with probes itself.
156
*
157
* Returns nanoseconds since some arbitrary point in time (likely SoC reset?).
158
*/
159
uint64_t
160
dtrace_gethrtime(void)
161
{
162
uint64_t count, freq;
163
uint32_t lo, hi;
164
165
count = READ_SPECIALREG(cntvct_el0);
166
lo = count;
167
hi = count >> 32;
168
return (((lo * nsec_scale) >> SCALE_SHIFT) +
169
((hi * nsec_scale) << (32 - SCALE_SHIFT)));
170
}
171
172
/*
173
* Return a much lower resolution wallclock time based on the system clock
174
* updated by the timer. If needed, we could add a version interpolated from
175
* the system clock as is the case with dtrace_gethrtime().
176
*/
177
uint64_t
178
dtrace_gethrestime(void)
179
{
180
struct timespec current_time;
181
182
dtrace_getnanotime(&current_time);
183
184
return (current_time.tv_sec * 1000000000UL + current_time.tv_nsec);
185
}
186
187
/* Function to handle DTrace traps during probes. See arm64/arm64/trap.c */
188
int
189
dtrace_trap(struct trapframe *frame, u_int type)
190
{
191
/*
192
* A trap can occur while DTrace executes a probe. Before
193
* executing the probe, DTrace blocks re-scheduling and sets
194
* a flag in its per-cpu flags to indicate that it doesn't
195
* want to fault. On returning from the probe, the no-fault
196
* flag is cleared and finally re-scheduling is enabled.
197
*
198
* Check if DTrace has enabled 'no-fault' mode:
199
*
200
*/
201
202
if ((cpu_core[curcpu].cpuc_dtrace_flags & CPU_DTRACE_NOFAULT) != 0) {
203
/*
204
* There are only a couple of trap types that are expected.
205
* All the rest will be handled in the usual way.
206
*/
207
switch (type) {
208
case EXCP_DATA_ABORT:
209
/* Flag a bad address. */
210
cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
211
cpu_core[curcpu].cpuc_dtrace_illval = frame->tf_far;
212
213
/*
214
* Offset the instruction pointer to the instruction
215
* following the one causing the fault.
216
*/
217
frame->tf_elr += 4;
218
return (1);
219
default:
220
/* Handle all other traps in the usual way. */
221
break;
222
}
223
}
224
225
/* Handle the trap in the usual way. */
226
return (0);
227
}
228
229
void
230
dtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which,
231
int fault, int fltoffs, uintptr_t illval)
232
{
233
234
dtrace_probe(dtrace_probeid_error, (uint64_t)(uintptr_t)state,
235
(uintptr_t)epid,
236
(uintptr_t)which, (uintptr_t)fault, (uintptr_t)fltoffs);
237
}
238
239
static void
240
dtrace_load64(uint64_t *addr, struct trapframe *frame, u_int reg)
241
{
242
243
KASSERT(reg <= 31, ("dtrace_load64: Invalid register %u", reg));
244
if (reg < nitems(frame->tf_x))
245
frame->tf_x[reg] = *addr;
246
else if (reg == 30) /* lr */
247
frame->tf_lr = *addr;
248
/* Nothing to do for load to xzr */
249
}
250
251
static void
252
dtrace_store64(uint64_t *addr, struct trapframe *frame, u_int reg)
253
{
254
255
KASSERT(reg <= 31, ("dtrace_store64: Invalid register %u", reg));
256
if (reg < nitems(frame->tf_x))
257
*addr = frame->tf_x[reg];
258
else if (reg == 30) /* lr */
259
*addr = frame->tf_lr;
260
else if (reg == 31) /* xzr */
261
*addr = 0;
262
}
263
264
static int
265
dtrace_invop_start(struct trapframe *frame)
266
{
267
int data, invop, tmp;
268
269
invop = dtrace_invop(frame->tf_elr, frame, frame->tf_x[0]);
270
271
tmp = (invop & LDP_STP_MASK);
272
if (tmp == STP_64 || tmp == LDP_64) {
273
register_t arg1, arg2, *sp;
274
int offs;
275
276
sp = (register_t *)frame->tf_sp;
277
data = invop;
278
arg1 = (data >> ARG1_SHIFT) & ARG1_MASK;
279
arg2 = (data >> ARG2_SHIFT) & ARG2_MASK;
280
281
offs = (data >> OFFSET_SHIFT) & OFFSET_MASK;
282
283
switch (tmp) {
284
case STP_64:
285
if (offs >> (OFFSET_SIZE - 1))
286
sp -= (~offs & OFFSET_MASK) + 1;
287
else
288
sp += (offs);
289
dtrace_store64(sp + 0, frame, arg1);
290
dtrace_store64(sp + 1, frame, arg2);
291
break;
292
case LDP_64:
293
dtrace_load64(sp + 0, frame, arg1);
294
dtrace_load64(sp + 1, frame, arg2);
295
if (offs >> (OFFSET_SIZE - 1))
296
sp -= (~offs & OFFSET_MASK) + 1;
297
else
298
sp += (offs);
299
break;
300
default:
301
break;
302
}
303
304
/* Update the stack pointer and program counter to continue */
305
frame->tf_sp = (register_t)sp;
306
frame->tf_elr += INSN_SIZE;
307
return (0);
308
}
309
310
if ((invop & SUB_MASK) == SUB_INSTR) {
311
frame->tf_sp -= (invop >> SUB_IMM_SHIFT) & SUB_IMM_MASK;
312
frame->tf_elr += INSN_SIZE;
313
return (0);
314
}
315
316
if (invop == NOP_INSTR) {
317
frame->tf_elr += INSN_SIZE;
318
return (0);
319
}
320
321
if ((invop & B_MASK) == B_INSTR) {
322
data = (invop & B_DATA_MASK);
323
/* The data is the number of 4-byte words to change the pc */
324
data *= 4;
325
frame->tf_elr += data;
326
return (0);
327
}
328
329
if (invop == RET_INSTR) {
330
frame->tf_elr = frame->tf_lr;
331
return (0);
332
}
333
334
return (-1);
335
}
336
337
void
338
dtrace_invop_init(void)
339
{
340
341
dtrace_invop_jump_addr = dtrace_invop_start;
342
}
343
344
void
345
dtrace_invop_uninit(void)
346
{
347
348
dtrace_invop_jump_addr = 0;
349
}
350
351