Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/cddl/dev/dtrace/arm/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 <vm/pmap.h>
43
44
#define DELAYBRANCH(x) ((int)(x) < 0)
45
46
#define BIT_PC 15
47
#define BIT_LR 14
48
#define BIT_SP 13
49
50
extern dtrace_id_t dtrace_probeid_error;
51
extern int (*dtrace_invop_jump_addr)(struct trapframe *);
52
extern void dtrace_getnanotime(struct timespec *tsp);
53
extern void dtrace_getnanouptime(struct timespec *tsp);
54
55
int dtrace_invop(uintptr_t, struct trapframe *, uintptr_t);
56
void dtrace_invop_init(void);
57
void dtrace_invop_uninit(void);
58
59
typedef struct dtrace_invop_hdlr {
60
int (*dtih_func)(uintptr_t, struct trapframe *, uintptr_t);
61
struct dtrace_invop_hdlr *dtih_next;
62
} dtrace_invop_hdlr_t;
63
64
dtrace_invop_hdlr_t *dtrace_invop_hdlr;
65
66
int
67
dtrace_invop(uintptr_t addr, struct trapframe *frame, uintptr_t eax)
68
{
69
struct thread *td;
70
dtrace_invop_hdlr_t *hdlr;
71
int rval;
72
73
rval = 0;
74
td = curthread;
75
td->t_dtrace_trapframe = frame;
76
for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next)
77
if ((rval = hdlr->dtih_func(addr, frame, eax)) != 0)
78
break;
79
td->t_dtrace_trapframe = NULL;
80
return (rval);
81
}
82
83
84
void
85
dtrace_invop_add(int (*func)(uintptr_t, struct trapframe *, uintptr_t))
86
{
87
dtrace_invop_hdlr_t *hdlr;
88
89
hdlr = kmem_alloc(sizeof (dtrace_invop_hdlr_t), KM_SLEEP);
90
hdlr->dtih_func = func;
91
hdlr->dtih_next = dtrace_invop_hdlr;
92
dtrace_invop_hdlr = hdlr;
93
}
94
95
void
96
dtrace_invop_remove(int (*func)(uintptr_t, struct trapframe *, uintptr_t))
97
{
98
dtrace_invop_hdlr_t *hdlr = dtrace_invop_hdlr, *prev = NULL;
99
100
for (;;) {
101
if (hdlr == NULL)
102
panic("attempt to remove non-existent invop handler");
103
104
if (hdlr->dtih_func == func)
105
break;
106
107
prev = hdlr;
108
hdlr = hdlr->dtih_next;
109
}
110
111
if (prev == NULL) {
112
ASSERT(dtrace_invop_hdlr == hdlr);
113
dtrace_invop_hdlr = hdlr->dtih_next;
114
} else {
115
ASSERT(dtrace_invop_hdlr != hdlr);
116
prev->dtih_next = hdlr->dtih_next;
117
}
118
119
kmem_free(hdlr, 0);
120
}
121
122
123
/*ARGSUSED*/
124
void
125
dtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit))
126
{
127
128
/*
129
* There are no ranges to exclude that are common to all 32-bit arm
130
* platforms. This function only needs to exclude ranges "... in
131
* which it is impossible to recover from such a load after it has been
132
* attempted." -- i.e., accessing within the range causes some sort
133
* fault in the system which is not handled by the normal arm
134
* exception-handling mechanisms. If systems exist where that is the
135
* case, a method to handle this functionality would have to be added to
136
* the platform_if interface so that those systems could provide their
137
* specific toxic range(s).
138
*/
139
}
140
141
/*
142
* DTrace needs a high resolution time function which can
143
* be called from a probe context and guaranteed not to have
144
* instrumented with probes itself.
145
*
146
* Returns nanoseconds since boot.
147
*/
148
uint64_t
149
dtrace_gethrtime(void)
150
{
151
struct timespec curtime;
152
153
dtrace_getnanouptime(&curtime);
154
155
return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec);
156
157
}
158
159
uint64_t
160
dtrace_gethrestime(void)
161
{
162
struct timespec current_time;
163
164
dtrace_getnanotime(&current_time);
165
166
return (current_time.tv_sec * 1000000000UL + current_time.tv_nsec);
167
}
168
169
/* Function to handle DTrace traps during probes. See amd64/amd64/trap.c */
170
int
171
dtrace_trap(struct trapframe *frame, u_int type)
172
{
173
/*
174
* A trap can occur while DTrace executes a probe. Before
175
* executing the probe, DTrace blocks re-scheduling and sets
176
* a flag in its per-cpu flags to indicate that it doesn't
177
* want to fault. On returning from the probe, the no-fault
178
* flag is cleared and finally re-scheduling is enabled.
179
*
180
* Check if DTrace has enabled 'no-fault' mode:
181
*
182
*/
183
if ((cpu_core[curcpu].cpuc_dtrace_flags & CPU_DTRACE_NOFAULT) != 0) {
184
/*
185
* There are only a couple of trap types that are expected.
186
* All the rest will be handled in the usual way.
187
*/
188
switch (type) {
189
/* Page fault. */
190
case FAULT_ALIGN:
191
/* Flag a bad address. */
192
cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
193
cpu_core[curcpu].cpuc_dtrace_illval = 0;
194
195
/*
196
* Offset the instruction pointer to the instruction
197
* following the one causing the fault.
198
*/
199
frame->tf_pc += sizeof(int);
200
return (1);
201
default:
202
/* Handle all other traps in the usual way. */
203
break;
204
}
205
}
206
207
/* Handle the trap in the usual way. */
208
return (0);
209
}
210
211
void
212
dtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which,
213
int fault, int fltoffs, uintptr_t illval)
214
{
215
216
dtrace_probe(dtrace_probeid_error, (uint64_t)(uintptr_t)state,
217
(uintptr_t)epid,
218
(uintptr_t)which, (uintptr_t)fault, (uintptr_t)fltoffs);
219
}
220
221
static int
222
dtrace_invop_start(struct trapframe *frame)
223
{
224
register_t *r0, *sp;
225
int data, invop, reg, update_sp;
226
227
invop = dtrace_invop(frame->tf_pc, frame, frame->tf_r0);
228
switch (invop & DTRACE_INVOP_MASK) {
229
case DTRACE_INVOP_PUSHM:
230
sp = (register_t *)frame->tf_svc_sp;
231
r0 = &frame->tf_r0;
232
data = DTRACE_INVOP_DATA(invop);
233
234
/*
235
* Store the pc, lr, and sp. These have their own
236
* entries in the struct.
237
*/
238
if (data & (1 << BIT_PC)) {
239
sp--;
240
*sp = frame->tf_pc;
241
}
242
if (data & (1 << BIT_LR)) {
243
sp--;
244
*sp = frame->tf_svc_lr;
245
}
246
if (data & (1 << BIT_SP)) {
247
sp--;
248
*sp = frame->tf_svc_sp;
249
}
250
251
/* Store the general registers */
252
for (reg = 12; reg >= 0; reg--) {
253
if (data & (1 << reg)) {
254
sp--;
255
*sp = r0[reg];
256
}
257
}
258
259
/* Update the stack pointer and program counter to continue */
260
frame->tf_svc_sp = (register_t)sp;
261
frame->tf_pc += 4;
262
break;
263
case DTRACE_INVOP_POPM:
264
sp = (register_t *)frame->tf_svc_sp;
265
r0 = &frame->tf_r0;
266
data = DTRACE_INVOP_DATA(invop);
267
268
/* Read the general registers */
269
for (reg = 0; reg <= 12; reg++) {
270
if (data & (1 << reg)) {
271
r0[reg] = *sp;
272
sp++;
273
}
274
}
275
276
/*
277
* Set the stack pointer. If we don't update it here we will
278
* need to update it at the end as the instruction would do
279
*/
280
update_sp = 1;
281
if (data & (1 << BIT_SP)) {
282
frame->tf_svc_sp = *sp;
283
*sp++;
284
update_sp = 0;
285
}
286
287
/* Update the link register, we need to use the correct copy */
288
if (data & (1 << BIT_LR)) {
289
frame->tf_svc_lr = *sp;
290
*sp++;
291
}
292
/*
293
* And the program counter. If it's not in the list skip over
294
* it when we return so to not hit this again.
295
*/
296
if (data & (1 << BIT_PC)) {
297
frame->tf_pc = *sp;
298
*sp++;
299
} else
300
frame->tf_pc += 4;
301
302
/* Update the stack pointer if we haven't already done so */
303
if (update_sp)
304
frame->tf_svc_sp = (register_t)sp;
305
break;
306
case DTRACE_INVOP_B:
307
data = DTRACE_INVOP_DATA(invop) & 0x00ffffff;
308
/* Sign extend the data */
309
if ((data & (1 << 23)) != 0)
310
data |= 0xff000000;
311
/* The data is the number of 4-byte words to change the pc */
312
data *= 4;
313
data += 8;
314
frame->tf_pc += data;
315
break;
316
default:
317
return (-1);
318
break;
319
}
320
321
return (0);
322
}
323
324
void dtrace_invop_init(void)
325
{
326
dtrace_invop_jump_addr = dtrace_invop_start;
327
}
328
329
void dtrace_invop_uninit(void)
330
{
331
dtrace_invop_jump_addr = 0;
332
}
333
334