Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/cddl/contrib/opensolaris/uts/powerpc/dtrace/fasttrap_isa.c
96395 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 (the "License").
6
* You may not use this file except in compliance with the License.
7
*
8
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9
* or http://www.opensolaris.org/os/licensing.
10
* See the License for the specific language governing permissions
11
* and limitations under the License.
12
*
13
* When distributing Covered Code, include this CDDL HEADER in each
14
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15
* If applicable, add the following below this CDDL HEADER, with the
16
* fields enclosed by brackets "[]" replaced with your own identifying
17
* information: Portions Copyright [yyyy] [name of copyright owner]
18
*
19
* CDDL HEADER END
20
*/
21
/* Portions Copyright 2013 Justin Hibbits */
22
/*
23
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24
* Use is subject to license terms.
25
*/
26
27
#include <sys/fasttrap_isa.h>
28
#include <sys/fasttrap_impl.h>
29
#include <sys/dtrace.h>
30
#include <sys/dtrace_impl.h>
31
#include <cddl/dev/dtrace/dtrace_cddl.h>
32
#include <sys/proc.h>
33
#include <sys/types.h>
34
#include <sys/uio.h>
35
#include <sys/ptrace.h>
36
#include <sys/rmlock.h>
37
#include <sys/sysent.h>
38
39
#define OP(x) ((x) >> 26)
40
#define OPX(x) (((x) >> 2) & 0x3FF)
41
#define OP_BO(x) (((x) & 0x03E00000) >> 21)
42
#define OP_BI(x) (((x) & 0x001F0000) >> 16)
43
#define OP_RS(x) (((x) & 0x03E00000) >> 21)
44
#define OP_RA(x) (((x) & 0x001F0000) >> 16)
45
#define OP_RB(x) (((x) & 0x0000F100) >> 11)
46
47
int
48
fasttrap_tracepoint_install(proc_t *p, fasttrap_tracepoint_t *tp)
49
{
50
fasttrap_instr_t instr = FASTTRAP_INSTR;
51
52
if (uwrite(p, &instr, 4, tp->ftt_pc) != 0)
53
return (-1);
54
55
return (0);
56
}
57
58
int
59
fasttrap_tracepoint_remove(proc_t *p, fasttrap_tracepoint_t *tp)
60
{
61
uint32_t instr;
62
63
/*
64
* Distinguish between read or write failures and a changed
65
* instruction.
66
*/
67
if (uread(p, &instr, 4, tp->ftt_pc) != 0)
68
return (0);
69
if (instr != FASTTRAP_INSTR)
70
return (0);
71
if (uwrite(p, &tp->ftt_instr, 4, tp->ftt_pc) != 0)
72
return (-1);
73
74
return (0);
75
}
76
77
int
78
fasttrap_tracepoint_init(proc_t *p, fasttrap_tracepoint_t *tp, uintptr_t pc,
79
fasttrap_probe_type_t type)
80
{
81
uint32_t instr;
82
//int32_t disp;
83
84
/*
85
* Read the instruction at the given address out of the process's
86
* address space. We don't have to worry about a debugger
87
* changing this instruction before we overwrite it with our trap
88
* instruction since P_PR_LOCK is set.
89
*/
90
if (uread(p, &instr, 4, pc) != 0)
91
return (-1);
92
93
/*
94
* Decode the instruction to fill in the probe flags. We can have
95
* the process execute most instructions on its own using a pc/npc
96
* trick, but pc-relative control transfer present a problem since
97
* we're relocating the instruction. We emulate these instructions
98
* in the kernel. We assume a default type and over-write that as
99
* needed.
100
*
101
* pc-relative instructions must be emulated for correctness;
102
* other instructions (which represent a large set of commonly traced
103
* instructions) are emulated or otherwise optimized for performance.
104
*/
105
tp->ftt_type = FASTTRAP_T_COMMON;
106
tp->ftt_instr = instr;
107
108
switch (OP(instr)) {
109
/* The following are invalid for trapping (invalid opcodes, tw/twi). */
110
case 0:
111
case 1:
112
case 2:
113
case 4:
114
case 5:
115
case 6:
116
case 30:
117
case 39:
118
case 58:
119
case 62:
120
case 3: /* twi */
121
return (-1);
122
case 31: /* tw */
123
if (OPX(instr) == 4)
124
return (-1);
125
else if (OPX(instr) == 444 && OP_RS(instr) == OP_RA(instr) &&
126
OP_RS(instr) == OP_RB(instr))
127
tp->ftt_type = FASTTRAP_T_NOP;
128
break;
129
case 16:
130
tp->ftt_type = FASTTRAP_T_BC;
131
tp->ftt_dest = instr & 0x0000FFFC; /* Extract target address */
132
if (instr & 0x00008000)
133
tp->ftt_dest |= 0xFFFF0000;
134
/* Use as offset if not absolute address. */
135
if (!(instr & 0x02))
136
tp->ftt_dest += pc;
137
tp->ftt_bo = OP_BO(instr);
138
tp->ftt_bi = OP_BI(instr);
139
break;
140
case 18:
141
tp->ftt_type = FASTTRAP_T_B;
142
tp->ftt_dest = instr & 0x03FFFFFC; /* Extract target address */
143
if (instr & 0x02000000)
144
tp->ftt_dest |= 0xFC000000;
145
/* Use as offset if not absolute address. */
146
if (!(instr & 0x02))
147
tp->ftt_dest += pc;
148
break;
149
case 19:
150
switch (OPX(instr)) {
151
case 528: /* bcctr */
152
tp->ftt_type = FASTTRAP_T_BCTR;
153
tp->ftt_bo = OP_BO(instr);
154
tp->ftt_bi = OP_BI(instr);
155
break;
156
case 16: /* bclr */
157
tp->ftt_type = FASTTRAP_T_BCTR;
158
tp->ftt_bo = OP_BO(instr);
159
tp->ftt_bi = OP_BI(instr);
160
break;
161
};
162
break;
163
case 24:
164
if (OP_RS(instr) == OP_RA(instr) &&
165
(instr & 0x0000FFFF) == 0)
166
tp->ftt_type = FASTTRAP_T_NOP;
167
break;
168
};
169
170
/*
171
* We don't know how this tracepoint is going to be used, but in case
172
* it's used as part of a function return probe, we need to indicate
173
* whether it's always a return site or only potentially a return
174
* site. If it's part of a return probe, it's always going to be a
175
* return from that function if it's a restore instruction or if
176
* the previous instruction was a return. If we could reliably
177
* distinguish jump tables from return sites, this wouldn't be
178
* necessary.
179
*/
180
#if 0
181
if (tp->ftt_type != FASTTRAP_T_RESTORE &&
182
(uread(p, &instr, 4, pc - sizeof (instr)) != 0 ||
183
!(OP(instr) == 2 && OP3(instr) == OP3_RETURN)))
184
tp->ftt_flags |= FASTTRAP_F_RETMAYBE;
185
#endif
186
187
return (0);
188
}
189
190
static uint64_t
191
fasttrap_anarg(struct reg *rp, int argno)
192
{
193
uint64_t value;
194
proc_t *p = curproc;
195
196
/* The first 8 arguments are in registers. */
197
if (argno < 8)
198
return rp->fixreg[argno + 3];
199
200
/* Arguments on stack start after SP+LR (2 register slots). */
201
if (SV_PROC_FLAG(p, SV_ILP32)) {
202
DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
203
value = dtrace_fuword32((void *)(rp->fixreg[1] + 8 +
204
((argno - 8) * sizeof(uint32_t))));
205
DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | CPU_DTRACE_BADADDR);
206
} else {
207
DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
208
value = dtrace_fuword64((void *)(rp->fixreg[1] + 48 +
209
((argno - 8) * sizeof(uint64_t))));
210
DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | CPU_DTRACE_BADADDR);
211
}
212
return value;
213
}
214
215
uint64_t
216
fasttrap_pid_getarg(void *arg, dtrace_id_t id, void *parg, int argno,
217
int aframes)
218
{
219
struct reg r;
220
221
fill_regs(curthread, &r);
222
223
return (fasttrap_anarg(&r, argno));
224
}
225
226
uint64_t
227
fasttrap_usdt_getarg(void *arg, dtrace_id_t id, void *parg, int argno,
228
int aframes)
229
{
230
struct reg r;
231
232
fill_regs(curthread, &r);
233
234
return (fasttrap_anarg(&r, argno));
235
}
236
237
static void
238
fasttrap_usdt_args(fasttrap_probe_t *probe, struct reg *rp, int argc,
239
uintptr_t *argv)
240
{
241
int i, x, cap = MIN(argc, probe->ftp_nargs);
242
243
for (i = 0; i < cap; i++) {
244
x = probe->ftp_argmap[i];
245
246
if (x < 8)
247
argv[i] = rp->fixreg[x];
248
else
249
#ifdef __powerpc64__
250
if (SV_PROC_FLAG(curproc, SV_ILP32))
251
#endif
252
argv[i] = fuword32((void *)(rp->fixreg[1] + 8 +
253
(x * sizeof(uint32_t))));
254
#ifdef __powerpc64__
255
else
256
argv[i] = fuword64((void *)(rp->fixreg[1] + 48 +
257
(x * sizeof(uint64_t))));
258
#endif
259
}
260
261
for (; i < argc; i++) {
262
argv[i] = 0;
263
}
264
}
265
266
static void
267
fasttrap_return_common(struct reg *rp, uintptr_t pc, pid_t pid,
268
uintptr_t new_pc)
269
{
270
struct rm_priotracker tracker;
271
fasttrap_tracepoint_t *tp;
272
fasttrap_bucket_t *bucket;
273
fasttrap_id_t *id;
274
275
rm_rlock(&fasttrap_tp_lock, &tracker);
276
bucket = &fasttrap_tpoints.fth_table[FASTTRAP_TPOINTS_INDEX(pid, pc)];
277
278
for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) {
279
if (pid == tp->ftt_pid && pc == tp->ftt_pc &&
280
tp->ftt_proc->ftpc_acount != 0)
281
break;
282
}
283
284
/*
285
* Don't sweat it if we can't find the tracepoint again; unlike
286
* when we're in fasttrap_pid_probe(), finding the tracepoint here
287
* is not essential to the correct execution of the process.
288
*/
289
if (tp == NULL) {
290
rm_runlock(&fasttrap_tp_lock, &tracker);
291
return;
292
}
293
294
for (id = tp->ftt_retids; id != NULL; id = id->fti_next) {
295
/*
296
* If there's a branch that could act as a return site, we
297
* need to trace it, and check here if the program counter is
298
* external to the function.
299
*/
300
/* Skip function-local branches. */
301
if ((new_pc - id->fti_probe->ftp_faddr) < id->fti_probe->ftp_fsize)
302
continue;
303
304
dtrace_probe(id->fti_probe->ftp_id,
305
pc - id->fti_probe->ftp_faddr,
306
rp->fixreg[3], rp->fixreg[4], 0, 0);
307
}
308
rm_runlock(&fasttrap_tp_lock, &tracker);
309
}
310
311
312
static int
313
fasttrap_branch_taken(int bo, int bi, struct reg *regs)
314
{
315
int crzero = 0;
316
317
/* Branch always? */
318
if ((bo & 0x14) == 0x14)
319
return 1;
320
321
/* Handle decrementing ctr */
322
if (!(bo & 0x04)) {
323
--regs->ctr;
324
crzero = (regs->ctr == 0);
325
if (bo & 0x10) {
326
return (!(crzero ^ (bo >> 1)));
327
}
328
}
329
330
return (crzero | (((regs->cr >> (31 - bi)) ^ (bo >> 3)) ^ 1));
331
}
332
333
334
int
335
fasttrap_pid_probe(struct trapframe *frame)
336
{
337
struct reg reg, *rp;
338
struct rm_priotracker tracker;
339
proc_t *p = curproc;
340
uintptr_t pc;
341
uintptr_t new_pc = 0;
342
fasttrap_bucket_t *bucket;
343
fasttrap_tracepoint_t *tp, tp_local;
344
pid_t pid;
345
dtrace_icookie_t cookie;
346
uint_t is_enabled = 0;
347
348
fill_regs(curthread, &reg);
349
rp = &reg;
350
pc = rp->pc;
351
352
/*
353
* It's possible that a user (in a veritable orgy of bad planning)
354
* could redirect this thread's flow of control before it reached the
355
* return probe fasttrap. In this case we need to kill the process
356
* since it's in a unrecoverable state.
357
*/
358
if (curthread->t_dtrace_step) {
359
ASSERT(curthread->t_dtrace_on);
360
fasttrap_sigtrap(p, curthread, pc);
361
return (0);
362
}
363
364
/*
365
* Clear all user tracing flags.
366
*/
367
curthread->t_dtrace_ft = 0;
368
curthread->t_dtrace_pc = 0;
369
curthread->t_dtrace_npc = 0;
370
curthread->t_dtrace_scrpc = 0;
371
curthread->t_dtrace_astpc = 0;
372
373
rm_rlock(&fasttrap_tp_lock, &tracker);
374
pid = p->p_pid;
375
bucket = &fasttrap_tpoints.fth_table[FASTTRAP_TPOINTS_INDEX(pid, pc)];
376
377
/*
378
* Lookup the tracepoint that the process just hit.
379
*/
380
for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) {
381
if (pid == tp->ftt_pid && pc == tp->ftt_pc &&
382
tp->ftt_proc->ftpc_acount != 0)
383
break;
384
}
385
386
/*
387
* If we couldn't find a matching tracepoint, either a tracepoint has
388
* been inserted without using the pid<pid> ioctl interface (see
389
* fasttrap_ioctl), or somehow we have mislaid this tracepoint.
390
*/
391
if (tp == NULL) {
392
rm_runlock(&fasttrap_tp_lock, &tracker);
393
return (-1);
394
}
395
396
if (tp->ftt_ids != NULL) {
397
fasttrap_id_t *id;
398
399
for (id = tp->ftt_ids; id != NULL; id = id->fti_next) {
400
fasttrap_probe_t *probe = id->fti_probe;
401
402
if (id->fti_ptype == DTFTP_ENTRY) {
403
/*
404
* We note that this was an entry
405
* probe to help ustack() find the
406
* first caller.
407
*/
408
cookie = dtrace_interrupt_disable();
409
DTRACE_CPUFLAG_SET(CPU_DTRACE_ENTRY);
410
dtrace_probe(probe->ftp_id, rp->fixreg[3],
411
rp->fixreg[4], rp->fixreg[5], rp->fixreg[6],
412
rp->fixreg[7]);
413
DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_ENTRY);
414
dtrace_interrupt_enable(cookie);
415
} else if (id->fti_ptype == DTFTP_IS_ENABLED) {
416
/*
417
* Note that in this case, we don't
418
* call dtrace_probe() since it's only
419
* an artificial probe meant to change
420
* the flow of control so that it
421
* encounters the true probe.
422
*/
423
is_enabled = 1;
424
} else if (probe->ftp_argmap == NULL) {
425
dtrace_probe(probe->ftp_id, rp->fixreg[3],
426
rp->fixreg[4], rp->fixreg[5], rp->fixreg[6],
427
rp->fixreg[7]);
428
} else {
429
uintptr_t t[5];
430
431
fasttrap_usdt_args(probe, rp,
432
sizeof (t) / sizeof (t[0]), t);
433
434
dtrace_probe(probe->ftp_id, t[0], t[1],
435
t[2], t[3], t[4]);
436
}
437
}
438
}
439
440
/*
441
* We're about to do a bunch of work so we cache a local copy of
442
* the tracepoint to emulate the instruction, and then find the
443
* tracepoint again later if we need to light up any return probes.
444
*/
445
tp_local = *tp;
446
rm_runlock(&fasttrap_tp_lock, &tracker);
447
tp = &tp_local;
448
449
/*
450
* If there's an is-enabled probe connected to this tracepoint it
451
* means that there was a 'xor r3, r3, r3'
452
* instruction that was placed there by DTrace when the binary was
453
* linked. As this probe is, in fact, enabled, we need to stuff 1
454
* into R3. Accordingly, we can bypass all the instruction
455
* emulation logic since we know the inevitable result. It's possible
456
* that a user could construct a scenario where the 'is-enabled'
457
* probe was on some other instruction, but that would be a rather
458
* exotic way to shoot oneself in the foot.
459
*/
460
if (is_enabled) {
461
rp->fixreg[3] = 1;
462
new_pc = rp->pc + 4;
463
goto done;
464
}
465
466
467
switch (tp->ftt_type) {
468
case FASTTRAP_T_NOP:
469
new_pc = rp->pc + 4;
470
break;
471
case FASTTRAP_T_BC:
472
if (!fasttrap_branch_taken(tp->ftt_bo, tp->ftt_bi, rp))
473
break;
474
/* FALLTHROUGH */
475
case FASTTRAP_T_B:
476
if (tp->ftt_instr & 0x01)
477
rp->lr = rp->pc + 4;
478
new_pc = tp->ftt_dest;
479
break;
480
case FASTTRAP_T_BLR:
481
case FASTTRAP_T_BCTR:
482
if (!fasttrap_branch_taken(tp->ftt_bo, tp->ftt_bi, rp))
483
break;
484
/* FALLTHROUGH */
485
if (tp->ftt_type == FASTTRAP_T_BCTR)
486
new_pc = rp->ctr;
487
else
488
new_pc = rp->lr;
489
if (tp->ftt_instr & 0x01)
490
rp->lr = rp->pc + 4;
491
break;
492
case FASTTRAP_T_COMMON:
493
curthread->t_dtrace_pc = pc;
494
curthread->t_dtrace_npc = pc + 4;
495
curthread->t_dtrace_on = 1;
496
new_pc = pc;
497
break;
498
};
499
done:
500
/*
501
* If there were no return probes when we first found the tracepoint,
502
* we should feel no obligation to honor any return probes that were
503
* subsequently enabled -- they'll just have to wait until the next
504
* time around.
505
*/
506
if (tp->ftt_retids != NULL) {
507
/*
508
* We need to wait until the results of the instruction are
509
* apparent before invoking any return probes. If this
510
* instruction was emulated we can just call
511
* fasttrap_return_common(); if it needs to be executed, we
512
* need to wait until the user thread returns to the kernel.
513
*/
514
if (tp->ftt_type != FASTTRAP_T_COMMON) {
515
fasttrap_return_common(rp, pc, pid, new_pc);
516
} else {
517
ASSERT(curthread->t_dtrace_ret != 0);
518
ASSERT(curthread->t_dtrace_pc == pc);
519
ASSERT(curthread->t_dtrace_scrpc != 0);
520
ASSERT(new_pc == curthread->t_dtrace_astpc);
521
}
522
}
523
524
rp->pc = new_pc;
525
set_regs(curthread, rp);
526
527
return (0);
528
}
529
530
int
531
fasttrap_return_probe(struct trapframe *tf)
532
{
533
struct reg reg, *rp;
534
proc_t *p = curproc;
535
uintptr_t pc = curthread->t_dtrace_pc;
536
uintptr_t npc = curthread->t_dtrace_npc;
537
538
curthread->t_dtrace_pc = 0;
539
curthread->t_dtrace_npc = 0;
540
curthread->t_dtrace_scrpc = 0;
541
curthread->t_dtrace_astpc = 0;
542
543
fill_regs(curthread, &reg);
544
rp = &reg;
545
546
/*
547
* We set rp->pc to the address of the traced instruction so
548
* that it appears to dtrace_probe() that we're on the original
549
* instruction.
550
*/
551
rp->pc = pc;
552
553
fasttrap_return_common(rp, pc, p->p_pid, npc);
554
555
return (0);
556
}
557
558