Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/cddl/dev/dtrace/powerpc/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/clock.h>
39
#include <machine/frame.h>
40
#include <machine/trap.h>
41
#include <vm/pmap.h>
42
43
#define DELAYBRANCH(x) ((int)(x) < 0)
44
45
extern dtrace_id_t dtrace_probeid_error;
46
extern int (*dtrace_invop_jump_addr)(struct trapframe *);
47
48
extern void dtrace_getnanotime(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 arg0)
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, arg0)) != 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 = dtrace_invop_hdlr, *prev = NULL;
93
94
for (;;) {
95
if (hdlr == NULL)
96
panic("attempt to remove non-existent invop handler");
97
98
if (hdlr->dtih_func == func)
99
break;
100
101
prev = hdlr;
102
hdlr = hdlr->dtih_next;
103
}
104
105
if (prev == NULL) {
106
ASSERT(dtrace_invop_hdlr == hdlr);
107
dtrace_invop_hdlr = hdlr->dtih_next;
108
} else {
109
ASSERT(dtrace_invop_hdlr != hdlr);
110
prev->dtih_next = hdlr->dtih_next;
111
}
112
113
kmem_free(hdlr, 0);
114
}
115
116
117
/*ARGSUSED*/
118
void
119
dtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit))
120
{
121
/*
122
* No toxic regions?
123
*/
124
}
125
126
static int64_t tgt_cpu_tsc;
127
static int64_t hst_cpu_tsc;
128
static int64_t timebase_skew[MAXCPU];
129
static uint64_t nsec_scale;
130
131
/* See below for the explanation of this macro. */
132
/* This is taken from the amd64 dtrace_subr, to provide a synchronized timer
133
* between multiple processors in dtrace. Since PowerPC Timebases can be much
134
* lower than x86, the scale shift is 26 instead of 28, allowing for a 15.63MHz
135
* timebase.
136
*/
137
#define SCALE_SHIFT 26
138
139
static void
140
dtrace_gethrtime_init_cpu(void *arg)
141
{
142
uintptr_t cpu = (uintptr_t) arg;
143
144
if (cpu == curcpu)
145
tgt_cpu_tsc = mftb();
146
else
147
hst_cpu_tsc = mftb();
148
}
149
150
static void
151
dtrace_gethrtime_init(void *arg)
152
{
153
struct pcpu *pc;
154
uint64_t tb_f;
155
cpuset_t map;
156
int i;
157
158
tb_f = cpu_tickrate();
159
160
/*
161
* The following line checks that nsec_scale calculated below
162
* doesn't overflow 32-bit unsigned integer, so that it can multiply
163
* another 32-bit integer without overflowing 64-bit.
164
* Thus minimum supported Timebase frequency is 15.63MHz.
165
*/
166
KASSERT(tb_f > (NANOSEC >> (32 - SCALE_SHIFT)), ("Timebase frequency is too low"));
167
168
/*
169
* We scale up NANOSEC/tb_f ratio to preserve as much precision
170
* as possible.
171
* 2^26 factor was chosen quite arbitrarily from practical
172
* considerations:
173
* - it supports TSC frequencies as low as 15.63MHz (see above);
174
*/
175
nsec_scale = ((uint64_t)NANOSEC << SCALE_SHIFT) / tb_f;
176
177
/* The current CPU is the reference one. */
178
sched_pin();
179
timebase_skew[curcpu] = 0;
180
CPU_FOREACH(i) {
181
if (i == curcpu)
182
continue;
183
184
pc = pcpu_find(i);
185
CPU_SETOF(PCPU_GET(cpuid), &map);
186
CPU_SET(pc->pc_cpuid, &map);
187
188
smp_rendezvous_cpus(map, NULL,
189
dtrace_gethrtime_init_cpu,
190
smp_no_rendezvous_barrier, (void *)(uintptr_t) i);
191
192
timebase_skew[i] = tgt_cpu_tsc - hst_cpu_tsc;
193
}
194
sched_unpin();
195
}
196
#ifdef EARLY_AP_STARTUP
197
SYSINIT(dtrace_gethrtime_init, SI_SUB_DTRACE, SI_ORDER_ANY,
198
dtrace_gethrtime_init, NULL);
199
#else
200
SYSINIT(dtrace_gethrtime_init, SI_SUB_SMP, SI_ORDER_ANY, dtrace_gethrtime_init,
201
NULL);
202
#endif
203
204
/*
205
* DTrace needs a high resolution time function which can
206
* be called from a probe context and guaranteed not to have
207
* instrumented with probes itself.
208
*
209
* Returns nanoseconds since boot.
210
*/
211
uint64_t
212
dtrace_gethrtime(void)
213
{
214
uint64_t timebase;
215
uint32_t lo;
216
uint32_t hi;
217
218
/*
219
* We split timebase value into lower and higher 32-bit halves and separately
220
* scale them with nsec_scale, then we scale them down by 2^28
221
* (see nsec_scale calculations) taking into account 32-bit shift of
222
* the higher half and finally add.
223
*/
224
timebase = mftb() - timebase_skew[curcpu];
225
lo = timebase;
226
hi = timebase >> 32;
227
return (((lo * nsec_scale) >> SCALE_SHIFT) +
228
((hi * nsec_scale) << (32 - SCALE_SHIFT)));
229
}
230
231
uint64_t
232
dtrace_gethrestime(void)
233
{
234
struct timespec curtime;
235
236
dtrace_getnanotime(&curtime);
237
238
return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec);
239
}
240
241
/* Function to handle DTrace traps during probes. See powerpc/powerpc/trap.c */
242
int
243
dtrace_trap(struct trapframe *frame, u_int type)
244
{
245
uint16_t nofault;
246
247
/*
248
* A trap can occur while DTrace executes a probe. Before
249
* executing the probe, DTrace blocks re-scheduling and sets
250
* a flag in its per-cpu flags to indicate that it doesn't
251
* want to fault. On returning from the probe, the no-fault
252
* flag is cleared and finally re-scheduling is enabled.
253
*
254
* Check if DTrace has enabled 'no-fault' mode:
255
*/
256
sched_pin();
257
nofault = cpu_core[curcpu].cpuc_dtrace_flags & CPU_DTRACE_NOFAULT;
258
sched_unpin();
259
if (nofault) {
260
KASSERT((frame->srr1 & PSL_EE) == 0, ("interrupts enabled"));
261
/*
262
* There are only a couple of trap types that are expected.
263
* All the rest will be handled in the usual way.
264
*/
265
switch (type) {
266
/* Page fault. */
267
case EXC_DSI:
268
case EXC_DSE:
269
/* Flag a bad address. */
270
cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
271
cpu_core[curcpu].cpuc_dtrace_illval = frame->dar;
272
273
/*
274
* Offset the instruction pointer to the instruction
275
* following the one causing the fault.
276
*/
277
frame->srr0 += sizeof(int);
278
return (1);
279
case EXC_ISI:
280
case EXC_ISE:
281
/* Flag a bad address. */
282
cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
283
cpu_core[curcpu].cpuc_dtrace_illval = frame->srr0;
284
285
/*
286
* Offset the instruction pointer to the instruction
287
* following the one causing the fault.
288
*/
289
frame->srr0 += sizeof(int);
290
return (1);
291
default:
292
/* Handle all other traps in the usual way. */
293
break;
294
}
295
}
296
297
/* Handle the trap in the usual way. */
298
return (0);
299
}
300
301
void
302
dtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which,
303
int fault, int fltoffs, uintptr_t illval)
304
{
305
306
dtrace_probe(dtrace_probeid_error, (uint64_t)(uintptr_t)state,
307
(uintptr_t)epid,
308
(uintptr_t)which, (uintptr_t)fault, (uintptr_t)fltoffs);
309
}
310
311
static int
312
dtrace_invop_start(struct trapframe *frame)
313
{
314
315
switch (dtrace_invop(frame->srr0, frame, frame->fixreg[3])) {
316
case DTRACE_INVOP_JUMP:
317
break;
318
case DTRACE_INVOP_BCTR:
319
frame->srr0 = frame->ctr;
320
break;
321
case DTRACE_INVOP_BLR:
322
frame->srr0 = frame->lr;
323
break;
324
case DTRACE_INVOP_MFLR_R0:
325
frame->fixreg[0] = frame->lr;
326
frame->srr0 = frame->srr0 + 4;
327
break;
328
default:
329
return (-1);
330
}
331
return (0);
332
}
333
334
void dtrace_invop_init(void)
335
{
336
dtrace_invop_jump_addr = dtrace_invop_start;
337
}
338
339
void dtrace_invop_uninit(void)
340
{
341
dtrace_invop_jump_addr = 0;
342
}
343
344