107864a8aSChristos Margiolis /*
207864a8aSChristos Margiolis * SPDX-License-Identifier: CDDL 1.0
307864a8aSChristos Margiolis *
407864a8aSChristos Margiolis * Copyright (c) 2022 Christos Margiolis <christos@FreeBSD.org>
507864a8aSChristos Margiolis * Copyright (c) 2022 Mark Johnston <markj@FreeBSD.org>
607864a8aSChristos Margiolis * Copyright (c) 2023 The FreeBSD Foundation
707864a8aSChristos Margiolis *
807864a8aSChristos Margiolis * Portions of this software were developed by Christos Margiolis
907864a8aSChristos Margiolis * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
1007864a8aSChristos Margiolis */
1107864a8aSChristos Margiolis
1207864a8aSChristos Margiolis #include <sys/param.h>
1307864a8aSChristos Margiolis
1407864a8aSChristos Margiolis #include <sys/dtrace.h>
1507864a8aSChristos Margiolis #include <cddl/dev/dtrace/dtrace_cddl.h>
1607864a8aSChristos Margiolis
1707864a8aSChristos Margiolis #include "kinst.h"
1807864a8aSChristos Margiolis
1907864a8aSChristos Margiolis DPCPU_DEFINE_STATIC(struct kinst_cpu_state, kinst_state);
2007864a8aSChristos Margiolis
2107864a8aSChristos Margiolis static int
kinst_emulate(struct trapframe * frame,const struct kinst_probe * kp)2207864a8aSChristos Margiolis kinst_emulate(struct trapframe *frame, const struct kinst_probe *kp)
2307864a8aSChristos Margiolis {
2407864a8aSChristos Margiolis kinst_patchval_t instr = kp->kp_savedval;
2507864a8aSChristos Margiolis uint64_t imm;
2607864a8aSChristos Margiolis uint8_t cond, reg, bitpos;
2707864a8aSChristos Margiolis bool res;
2807864a8aSChristos Margiolis
2907864a8aSChristos Margiolis if (((instr >> 24) & 0x1f) == 0b10000) {
3007864a8aSChristos Margiolis /* adr/adrp */
3107864a8aSChristos Margiolis reg = instr & 0x1f;
3207864a8aSChristos Margiolis imm = (instr >> 29) & 0x3;
3307864a8aSChristos Margiolis imm |= ((instr >> 5) & 0x0007ffff) << 2;
3407864a8aSChristos Margiolis if (((instr >> 31) & 0x1) == 0) {
3507864a8aSChristos Margiolis /* adr */
3607864a8aSChristos Margiolis if (imm & 0x0000000000100000)
3707864a8aSChristos Margiolis imm |= 0xfffffffffff00000;
3807864a8aSChristos Margiolis frame->tf_x[reg] = frame->tf_elr + imm;
3907864a8aSChristos Margiolis } else {
4007864a8aSChristos Margiolis /* adrp */
4107864a8aSChristos Margiolis imm <<= 12;
4207864a8aSChristos Margiolis if (imm & 0x0000000100000000)
4307864a8aSChristos Margiolis imm |= 0xffffffff00000000;
4407864a8aSChristos Margiolis frame->tf_x[reg] = (frame->tf_elr & ~0xfff) + imm;
4507864a8aSChristos Margiolis }
4607864a8aSChristos Margiolis frame->tf_elr += INSN_SIZE;
4707864a8aSChristos Margiolis } else if (((instr >> 26) & 0x3f) == 0b000101) {
4807864a8aSChristos Margiolis /* b */
4907864a8aSChristos Margiolis imm = instr & 0x03ffffff;
5007864a8aSChristos Margiolis if (imm & 0x0000000002000000)
5107864a8aSChristos Margiolis imm |= 0xfffffffffe000000;
5207864a8aSChristos Margiolis frame->tf_elr += imm << 2;
5307864a8aSChristos Margiolis } else if (((instr >> 24) & 0xff) == 0b01010100) {
5407864a8aSChristos Margiolis /* b.cond */
5507864a8aSChristos Margiolis imm = (instr >> 5) & 0x0007ffff;
5607864a8aSChristos Margiolis if (imm & 0x0000000000040000)
5707864a8aSChristos Margiolis imm |= 0xfffffffffffc0000;
5807864a8aSChristos Margiolis cond = instr & 0xf;
5907864a8aSChristos Margiolis switch ((cond >> 1) & 0x7) {
6007864a8aSChristos Margiolis case 0b000: /* eq/ne */
6107864a8aSChristos Margiolis res = (frame->tf_spsr & PSR_Z) != 0;
6207864a8aSChristos Margiolis break;
6307864a8aSChristos Margiolis case 0b001: /* cs/cc */
6407864a8aSChristos Margiolis res = (frame->tf_spsr & PSR_C) != 0;
6507864a8aSChristos Margiolis break;
6607864a8aSChristos Margiolis case 0b010: /* mi/pl */
6707864a8aSChristos Margiolis res = (frame->tf_spsr & PSR_N) != 0;
6807864a8aSChristos Margiolis break;
6907864a8aSChristos Margiolis case 0b011: /* vs/vc */
7007864a8aSChristos Margiolis res = (frame->tf_spsr & PSR_V) != 0;
7107864a8aSChristos Margiolis break;
7207864a8aSChristos Margiolis case 0b100: /* hi/ls */
7307864a8aSChristos Margiolis res = ((frame->tf_spsr & PSR_C) != 0) &&
7407864a8aSChristos Margiolis ((frame->tf_spsr & PSR_Z) == 0);
7507864a8aSChristos Margiolis break;
7607864a8aSChristos Margiolis case 0b101: /* ge/lt */
7707864a8aSChristos Margiolis res = ((frame->tf_spsr & PSR_N) != 0) ==
7807864a8aSChristos Margiolis ((frame->tf_spsr & PSR_V) != 0);
7907864a8aSChristos Margiolis break;
8007864a8aSChristos Margiolis case 0b110: /* gt/le */
8107864a8aSChristos Margiolis res = ((frame->tf_spsr & PSR_Z) == 0) &&
8207864a8aSChristos Margiolis (((frame->tf_spsr & PSR_N) != 0) ==
8307864a8aSChristos Margiolis ((frame->tf_spsr & PSR_V) != 0));
8407864a8aSChristos Margiolis break;
8507864a8aSChristos Margiolis case 0b111: /* al */
8607864a8aSChristos Margiolis res = 1;
8707864a8aSChristos Margiolis break;
8807864a8aSChristos Margiolis }
8907864a8aSChristos Margiolis if ((cond & 0x1) && cond != 0b1111)
9007864a8aSChristos Margiolis res = !res;
9107864a8aSChristos Margiolis if (res)
9207864a8aSChristos Margiolis frame->tf_elr += imm << 2;
9307864a8aSChristos Margiolis else
9407864a8aSChristos Margiolis frame->tf_elr += INSN_SIZE;
9507864a8aSChristos Margiolis } else if (((instr >> 26) & 0x3f) == 0b100101) {
9607864a8aSChristos Margiolis /* bl */
9707864a8aSChristos Margiolis imm = instr & 0x03ffffff;
9807864a8aSChristos Margiolis if (imm & 0x0000000002000000)
9907864a8aSChristos Margiolis imm |= 0xfffffffffe000000;
10007864a8aSChristos Margiolis frame->tf_lr = frame->tf_elr + INSN_SIZE;
10107864a8aSChristos Margiolis frame->tf_elr += imm << 2;
10207864a8aSChristos Margiolis } else if (((instr >> 25) & 0x3f) == 0b011010) {
10307864a8aSChristos Margiolis /* cbnz/cbz */
10407864a8aSChristos Margiolis cond = (instr >> 24) & 0x1;
10507864a8aSChristos Margiolis reg = instr & 0x1f;
10607864a8aSChristos Margiolis imm = (instr >> 5) & 0x0007ffff;
10707864a8aSChristos Margiolis if (imm & 0x0000000000040000)
10807864a8aSChristos Margiolis imm |= 0xfffffffffffc0000;
10907864a8aSChristos Margiolis if (cond == 1 && frame->tf_x[reg] != 0)
11007864a8aSChristos Margiolis /* cbnz */
11107864a8aSChristos Margiolis frame->tf_elr += imm << 2;
11207864a8aSChristos Margiolis else if (cond == 0 && frame->tf_x[reg] == 0)
11307864a8aSChristos Margiolis /* cbz */
11407864a8aSChristos Margiolis frame->tf_elr += imm << 2;
11507864a8aSChristos Margiolis else
11607864a8aSChristos Margiolis frame->tf_elr += INSN_SIZE;
11707864a8aSChristos Margiolis } else if (((instr >> 25) & 0x3f) == 0b011011) {
11807864a8aSChristos Margiolis /* tbnz/tbz */
11907864a8aSChristos Margiolis cond = (instr >> 24) & 0x1;
12007864a8aSChristos Margiolis reg = instr & 0x1f;
12107864a8aSChristos Margiolis bitpos = (instr >> 19) & 0x1f;
12207864a8aSChristos Margiolis bitpos |= ((instr >> 31) & 0x1) << 5;
12307864a8aSChristos Margiolis imm = (instr >> 5) & 0x3fff;
12407864a8aSChristos Margiolis if (imm & 0x0000000000002000)
12507864a8aSChristos Margiolis imm |= 0xffffffffffffe000;
12607864a8aSChristos Margiolis if (cond == 1 && (frame->tf_x[reg] & (1 << bitpos)) != 0)
12707864a8aSChristos Margiolis /* tbnz */
12807864a8aSChristos Margiolis frame->tf_elr += imm << 2;
12907864a8aSChristos Margiolis else if (cond == 0 && (frame->tf_x[reg] & (1 << bitpos)) == 0)
13007864a8aSChristos Margiolis /* tbz */
13107864a8aSChristos Margiolis frame->tf_elr += imm << 2;
13207864a8aSChristos Margiolis else
13307864a8aSChristos Margiolis frame->tf_elr += INSN_SIZE;
13407864a8aSChristos Margiolis }
13507864a8aSChristos Margiolis
13607864a8aSChristos Margiolis return (0);
13707864a8aSChristos Margiolis }
13807864a8aSChristos Margiolis
13907864a8aSChristos Margiolis static int
kinst_jump_next_instr(struct trapframe * frame,const struct kinst_probe * kp)14007864a8aSChristos Margiolis kinst_jump_next_instr(struct trapframe *frame, const struct kinst_probe *kp)
14107864a8aSChristos Margiolis {
14207864a8aSChristos Margiolis frame->tf_elr = (register_t)((const uint8_t *)kp->kp_patchpoint +
14307864a8aSChristos Margiolis INSN_SIZE);
14407864a8aSChristos Margiolis
14507864a8aSChristos Margiolis return (0);
14607864a8aSChristos Margiolis }
14707864a8aSChristos Margiolis
14807864a8aSChristos Margiolis static void
kinst_trampoline_populate(struct kinst_probe * kp)14907864a8aSChristos Margiolis kinst_trampoline_populate(struct kinst_probe *kp)
15007864a8aSChristos Margiolis {
15107864a8aSChristos Margiolis static uint32_t bpt = KINST_PATCHVAL;
15207864a8aSChristos Margiolis
15307864a8aSChristos Margiolis kinst_memcpy(kp->kp_tramp, &kp->kp_savedval, INSN_SIZE);
15407864a8aSChristos Margiolis kinst_memcpy(&kp->kp_tramp[INSN_SIZE], &bpt, INSN_SIZE);
15507864a8aSChristos Margiolis
1561e3f42b6SJohn Baldwin cpu_icache_sync_range(kp->kp_tramp, KINST_TRAMP_SIZE);
15707864a8aSChristos Margiolis }
15807864a8aSChristos Margiolis
15907864a8aSChristos Margiolis /*
16007864a8aSChristos Margiolis * There are two ways by which an instruction is traced:
16107864a8aSChristos Margiolis *
16207864a8aSChristos Margiolis * - By using the trampoline.
16307864a8aSChristos Margiolis * - By emulating it in software (see kinst_emulate()).
16407864a8aSChristos Margiolis *
16507864a8aSChristos Margiolis * The trampoline is used for instructions that can be copied and executed
16607864a8aSChristos Margiolis * as-is without additional modification. However, instructions that use
16707864a8aSChristos Margiolis * PC-relative addressing have to be emulated, because ARM64 doesn't allow
16807864a8aSChristos Margiolis * encoding of large displacements in a single instruction, and since we cannot
16907864a8aSChristos Margiolis * clobber a register in order to encode the two-instruction sequence needed to
17007864a8aSChristos Margiolis * create large displacements, we cannot use the trampoline at all.
17107864a8aSChristos Margiolis * Fortunately, the instructions are simple enough to be emulated in just a few
17207864a8aSChristos Margiolis * lines of code.
17307864a8aSChristos Margiolis *
17407864a8aSChristos Margiolis * The problem discussed above also means that, unlike amd64, we cannot encode
17507864a8aSChristos Margiolis * a far-jump back from the trampoline to the next instruction. The mechanism
17607864a8aSChristos Margiolis * employed to achieve this functionality, is to use a breakpoint instead of a
17707864a8aSChristos Margiolis * jump after the copied instruction. This breakpoint is detected and handled
17807864a8aSChristos Margiolis * by kinst_invop(), which performs the jump back to the next instruction
17907864a8aSChristos Margiolis * manually (see kinst_jump_next_instr()).
18007864a8aSChristos Margiolis */
18107864a8aSChristos Margiolis int
kinst_invop(uintptr_t addr,struct trapframe * frame,uintptr_t scratch)18207864a8aSChristos Margiolis kinst_invop(uintptr_t addr, struct trapframe *frame, uintptr_t scratch)
18307864a8aSChristos Margiolis {
18407864a8aSChristos Margiolis solaris_cpu_t *cpu;
18507864a8aSChristos Margiolis struct kinst_cpu_state *ks;
18607864a8aSChristos Margiolis const struct kinst_probe *kp;
18707864a8aSChristos Margiolis
18807864a8aSChristos Margiolis ks = DPCPU_PTR(kinst_state);
18907864a8aSChristos Margiolis
19007864a8aSChristos Margiolis /*
19107864a8aSChristos Margiolis * Detect if the breakpoint was triggered by the trampoline, and
19207864a8aSChristos Margiolis * manually set the PC to the next instruction.
19307864a8aSChristos Margiolis */
19407864a8aSChristos Margiolis if (ks->state == KINST_PROBE_FIRED &&
19507864a8aSChristos Margiolis addr == (uintptr_t)(ks->kp->kp_tramp + INSN_SIZE)) {
19607864a8aSChristos Margiolis /*
19707864a8aSChristos Margiolis * Restore interrupts if they were enabled prior to the first
19807864a8aSChristos Margiolis * breakpoint.
19907864a8aSChristos Margiolis */
20007864a8aSChristos Margiolis if ((ks->status & PSR_I) == 0)
20107864a8aSChristos Margiolis frame->tf_spsr &= ~PSR_I;
20207864a8aSChristos Margiolis ks->state = KINST_PROBE_ARMED;
20307864a8aSChristos Margiolis return (kinst_jump_next_instr(frame, ks->kp));
20407864a8aSChristos Margiolis }
20507864a8aSChristos Margiolis
20607864a8aSChristos Margiolis LIST_FOREACH(kp, KINST_GETPROBE(addr), kp_hashnext) {
20707864a8aSChristos Margiolis if ((uintptr_t)kp->kp_patchpoint == addr)
20807864a8aSChristos Margiolis break;
20907864a8aSChristos Margiolis }
21007864a8aSChristos Margiolis if (kp == NULL)
21107864a8aSChristos Margiolis return (0);
21207864a8aSChristos Margiolis
21307864a8aSChristos Margiolis cpu = &solaris_cpu[curcpu];
21407864a8aSChristos Margiolis cpu->cpu_dtrace_caller = addr;
21507864a8aSChristos Margiolis dtrace_probe(kp->kp_id, 0, 0, 0, 0, 0);
21607864a8aSChristos Margiolis cpu->cpu_dtrace_caller = 0;
21707864a8aSChristos Margiolis
21807864a8aSChristos Margiolis if (kp->kp_md.emulate)
21907864a8aSChristos Margiolis return (kinst_emulate(frame, kp));
22007864a8aSChristos Margiolis
22107864a8aSChristos Margiolis ks->state = KINST_PROBE_FIRED;
22207864a8aSChristos Margiolis ks->kp = kp;
22307864a8aSChristos Margiolis
22407864a8aSChristos Margiolis /*
22507864a8aSChristos Margiolis * Cache the current SPSR and clear interrupts for the duration
22607864a8aSChristos Margiolis * of the double breakpoint.
22707864a8aSChristos Margiolis */
22807864a8aSChristos Margiolis ks->status = frame->tf_spsr;
22907864a8aSChristos Margiolis frame->tf_spsr |= PSR_I;
23007864a8aSChristos Margiolis frame->tf_elr = (register_t)kp->kp_tramp;
23107864a8aSChristos Margiolis
23207864a8aSChristos Margiolis return (0);
23307864a8aSChristos Margiolis }
23407864a8aSChristos Margiolis
23507864a8aSChristos Margiolis void
kinst_patch_tracepoint(struct kinst_probe * kp,kinst_patchval_t val)23607864a8aSChristos Margiolis kinst_patch_tracepoint(struct kinst_probe *kp, kinst_patchval_t val)
23707864a8aSChristos Margiolis {
238*e48770deSJohn Baldwin void *addr;
23907864a8aSChristos Margiolis
240*e48770deSJohn Baldwin if (!arm64_get_writable_addr(kp->kp_patchpoint, &addr))
24107864a8aSChristos Margiolis panic("%s: Unable to write new instruction", __func__);
24207864a8aSChristos Margiolis *(kinst_patchval_t *)addr = val;
2431e3f42b6SJohn Baldwin cpu_icache_sync_range(kp->kp_patchpoint, INSN_SIZE);
24407864a8aSChristos Margiolis }
24507864a8aSChristos Margiolis
24607864a8aSChristos Margiolis static void
kinst_instr_dissect(struct kinst_probe * kp)24707864a8aSChristos Margiolis kinst_instr_dissect(struct kinst_probe *kp)
24807864a8aSChristos Margiolis {
24907864a8aSChristos Margiolis struct kinst_probe_md *kpmd;
25007864a8aSChristos Margiolis kinst_patchval_t instr = kp->kp_savedval;
25107864a8aSChristos Margiolis
25207864a8aSChristos Margiolis kpmd = &kp->kp_md;
25307864a8aSChristos Margiolis kpmd->emulate = false;
25407864a8aSChristos Margiolis
25507864a8aSChristos Margiolis if (((instr >> 24) & 0x1f) == 0b10000)
25607864a8aSChristos Margiolis kpmd->emulate = true; /* adr/adrp */
25707864a8aSChristos Margiolis else if (((instr >> 26) & 0x3f) == 0b000101)
25807864a8aSChristos Margiolis kpmd->emulate = true; /* b */
25907864a8aSChristos Margiolis else if (((instr >> 24) & 0xff) == 0b01010100)
26007864a8aSChristos Margiolis kpmd->emulate = true; /* b.cond */
26107864a8aSChristos Margiolis else if (((instr >> 26) & 0x3f) == 0b100101)
26207864a8aSChristos Margiolis kpmd->emulate = true; /* bl */
26307864a8aSChristos Margiolis else if (((instr >> 25) & 0x3f) == 0b011010)
26407864a8aSChristos Margiolis kpmd->emulate = true; /* cbnz/cbz */
26507864a8aSChristos Margiolis else if (((instr >> 25) & 0x3f) == 0b011011)
26607864a8aSChristos Margiolis kpmd->emulate = true; /* tbnz/tbz */
26707864a8aSChristos Margiolis
26807864a8aSChristos Margiolis if (!kpmd->emulate)
26907864a8aSChristos Margiolis kinst_trampoline_populate(kp);
27007864a8aSChristos Margiolis }
27107864a8aSChristos Margiolis
27207864a8aSChristos Margiolis static bool
kinst_instr_ldx(kinst_patchval_t instr)27307864a8aSChristos Margiolis kinst_instr_ldx(kinst_patchval_t instr)
27407864a8aSChristos Margiolis {
27507864a8aSChristos Margiolis if (((instr >> 22) & 0xff) == 0b00100001)
27607864a8aSChristos Margiolis return (true);
27707864a8aSChristos Margiolis
27807864a8aSChristos Margiolis return (false);
27907864a8aSChristos Margiolis }
28007864a8aSChristos Margiolis
28107864a8aSChristos Margiolis static bool
kinst_instr_stx(kinst_patchval_t instr)28207864a8aSChristos Margiolis kinst_instr_stx(kinst_patchval_t instr)
28307864a8aSChristos Margiolis {
28407864a8aSChristos Margiolis if (((instr >> 22) & 0xff) == 0b00100000)
28507864a8aSChristos Margiolis return (true);
28607864a8aSChristos Margiolis
28707864a8aSChristos Margiolis return (false);
28807864a8aSChristos Margiolis }
28907864a8aSChristos Margiolis
29007864a8aSChristos Margiolis int
kinst_make_probe(linker_file_t lf,int symindx,linker_symval_t * symval,void * opaque)29107864a8aSChristos Margiolis kinst_make_probe(linker_file_t lf, int symindx, linker_symval_t *symval,
29207864a8aSChristos Margiolis void *opaque)
29307864a8aSChristos Margiolis {
29407864a8aSChristos Margiolis struct kinst_probe *kp;
29507864a8aSChristos Margiolis dtrace_kinst_probedesc_t *pd;
29607864a8aSChristos Margiolis const char *func;
29707864a8aSChristos Margiolis kinst_patchval_t *instr, *limit, *tmp;
29807864a8aSChristos Margiolis int n, off;
29907864a8aSChristos Margiolis bool ldxstx_block, found;
30007864a8aSChristos Margiolis
30107864a8aSChristos Margiolis pd = opaque;
30207864a8aSChristos Margiolis func = symval->name;
30307864a8aSChristos Margiolis
30407864a8aSChristos Margiolis if (kinst_excluded(func))
30507864a8aSChristos Margiolis return (0);
30607864a8aSChristos Margiolis if (strcmp(func, pd->kpd_func) != 0)
30707864a8aSChristos Margiolis return (0);
30807864a8aSChristos Margiolis
30907864a8aSChristos Margiolis instr = (kinst_patchval_t *)(symval->value);
31007864a8aSChristos Margiolis limit = (kinst_patchval_t *)(symval->value + symval->size);
31107864a8aSChristos Margiolis if (instr >= limit)
31207864a8aSChristos Margiolis return (0);
31307864a8aSChristos Margiolis
31407864a8aSChristos Margiolis tmp = instr;
31507864a8aSChristos Margiolis
31607864a8aSChristos Margiolis /*
31707864a8aSChristos Margiolis * Ignore any bti instruction at the start of the function
31807864a8aSChristos Margiolis * we need to keep it there for any indirect branches calling
31907864a8aSChristos Margiolis * the function on Armv8.5+
32007864a8aSChristos Margiolis */
32107864a8aSChristos Margiolis if ((*tmp & BTI_MASK) == BTI_INSTR)
32207864a8aSChristos Margiolis tmp++;
32307864a8aSChristos Margiolis
32407864a8aSChristos Margiolis /* Look for stp (pre-indexed) operation */
32507864a8aSChristos Margiolis found = false;
32607864a8aSChristos Margiolis
32707864a8aSChristos Margiolis /*
32807864a8aSChristos Margiolis * If the first instruction is a nop it's a specially marked
32907864a8aSChristos Margiolis * asm function. We only support a nop first as it's not a normal
33007864a8aSChristos Margiolis * part of the function prologue.
33107864a8aSChristos Margiolis */
33207864a8aSChristos Margiolis if (*tmp == NOP_INSTR)
33307864a8aSChristos Margiolis found = true;
33407864a8aSChristos Margiolis for (; !found && tmp < limit; tmp++) {
33507864a8aSChristos Margiolis /*
33607864a8aSChristos Margiolis * Functions start with "stp xt1, xt2, [xn, <const>]!" or
33707864a8aSChristos Margiolis * "sub sp, sp, <const>".
33807864a8aSChristos Margiolis *
33907864a8aSChristos Margiolis * Sometimes the compiler will have a sub instruction that is
34007864a8aSChristos Margiolis * not of the above type so don't stop if we see one.
34107864a8aSChristos Margiolis */
34207864a8aSChristos Margiolis if ((*tmp & LDP_STP_MASK) == STP_64) {
34307864a8aSChristos Margiolis /*
34407864a8aSChristos Margiolis * Assume any other store of this type means we are
34507864a8aSChristos Margiolis * past the function prolog.
34607864a8aSChristos Margiolis */
34707864a8aSChristos Margiolis if (((*tmp >> ADDR_SHIFT) & ADDR_MASK) == 31)
34807864a8aSChristos Margiolis found = true;
34907864a8aSChristos Margiolis } else if ((*tmp & SUB_MASK) == SUB_INSTR &&
35007864a8aSChristos Margiolis ((*tmp >> SUB_RD_SHIFT) & SUB_R_MASK) == 31 &&
35107864a8aSChristos Margiolis ((*tmp >> SUB_RN_SHIFT) & SUB_R_MASK) == 31)
35207864a8aSChristos Margiolis found = true;
35307864a8aSChristos Margiolis }
35407864a8aSChristos Margiolis
35507864a8aSChristos Margiolis if (!found)
35607864a8aSChristos Margiolis return (0);
35707864a8aSChristos Margiolis
35807864a8aSChristos Margiolis ldxstx_block = false;
35907864a8aSChristos Margiolis for (n = 0; instr < limit; instr++) {
36007864a8aSChristos Margiolis off = (int)((uint8_t *)instr - (uint8_t *)symval->value);
36107864a8aSChristos Margiolis
36207864a8aSChristos Margiolis /*
36307864a8aSChristos Margiolis * Skip LDX/STX blocks that contain atomic operations. If a
36407864a8aSChristos Margiolis * breakpoint is placed in a LDX/STX block, we violate the
36507864a8aSChristos Margiolis * operation and the loop might fail.
36607864a8aSChristos Margiolis */
36707864a8aSChristos Margiolis if (kinst_instr_ldx(*instr))
36807864a8aSChristos Margiolis ldxstx_block = true;
36907864a8aSChristos Margiolis else if (kinst_instr_stx(*instr)) {
37007864a8aSChristos Margiolis ldxstx_block = false;
37107864a8aSChristos Margiolis continue;
37207864a8aSChristos Margiolis }
37307864a8aSChristos Margiolis if (ldxstx_block)
37407864a8aSChristos Margiolis continue;
37507864a8aSChristos Margiolis
37607864a8aSChristos Margiolis /*
37707864a8aSChristos Margiolis * XXX: Skip ADR and ADRP instructions. The arm64 exception
37807864a8aSChristos Margiolis * handler has a micro-optimization where it doesn't restore
37907864a8aSChristos Margiolis * callee-saved registers when returning from exceptions in
38007864a8aSChristos Margiolis * EL1. This results in a panic when the kinst emulation code
38107864a8aSChristos Margiolis * modifies one of those registers.
38207864a8aSChristos Margiolis */
38307864a8aSChristos Margiolis if (((*instr >> 24) & 0x1f) == 0b10000)
38407864a8aSChristos Margiolis continue;
38507864a8aSChristos Margiolis
38607864a8aSChristos Margiolis if (pd->kpd_off != -1 && off != pd->kpd_off)
38707864a8aSChristos Margiolis continue;
38807864a8aSChristos Margiolis
38907864a8aSChristos Margiolis /*
39007864a8aSChristos Margiolis * Prevent separate dtrace(1) instances from creating copies of
39107864a8aSChristos Margiolis * the same probe.
39207864a8aSChristos Margiolis */
39307864a8aSChristos Margiolis LIST_FOREACH(kp, KINST_GETPROBE(instr), kp_hashnext) {
39407864a8aSChristos Margiolis if (strcmp(kp->kp_func, func) == 0 &&
39507864a8aSChristos Margiolis strtol(kp->kp_name, NULL, 10) == off)
39607864a8aSChristos Margiolis return (0);
39707864a8aSChristos Margiolis }
39807864a8aSChristos Margiolis if (++n > KINST_PROBETAB_MAX) {
39907864a8aSChristos Margiolis KINST_LOG("probe list full: %d entries", n);
40007864a8aSChristos Margiolis return (ENOMEM);
40107864a8aSChristos Margiolis }
40207864a8aSChristos Margiolis kp = malloc(sizeof(struct kinst_probe), M_KINST,
40307864a8aSChristos Margiolis M_WAITOK | M_ZERO);
40407864a8aSChristos Margiolis kp->kp_func = func;
40507864a8aSChristos Margiolis snprintf(kp->kp_name, sizeof(kp->kp_name), "%d", off);
40607864a8aSChristos Margiolis kp->kp_patchpoint = instr;
40707864a8aSChristos Margiolis kp->kp_savedval = *instr;
40807864a8aSChristos Margiolis kp->kp_patchval = KINST_PATCHVAL;
40907864a8aSChristos Margiolis if ((kp->kp_tramp = kinst_trampoline_alloc(M_WAITOK)) == NULL) {
41007864a8aSChristos Margiolis KINST_LOG("cannot allocate trampoline for %p", instr);
41107864a8aSChristos Margiolis return (ENOMEM);
41207864a8aSChristos Margiolis }
41307864a8aSChristos Margiolis
41407864a8aSChristos Margiolis kinst_instr_dissect(kp);
41507864a8aSChristos Margiolis kinst_probe_create(kp, lf);
41607864a8aSChristos Margiolis }
41707864a8aSChristos Margiolis if (ldxstx_block)
41807864a8aSChristos Margiolis KINST_LOG("warning: unterminated LDX/STX block");
41907864a8aSChristos Margiolis
42007864a8aSChristos Margiolis return (0);
42107864a8aSChristos Margiolis }
42207864a8aSChristos Margiolis
42307864a8aSChristos Margiolis int
kinst_md_init(void)42407864a8aSChristos Margiolis kinst_md_init(void)
42507864a8aSChristos Margiolis {
42607864a8aSChristos Margiolis struct kinst_cpu_state *ks;
42707864a8aSChristos Margiolis int cpu;
42807864a8aSChristos Margiolis
42907864a8aSChristos Margiolis CPU_FOREACH(cpu) {
43007864a8aSChristos Margiolis ks = DPCPU_PTR(kinst_state);
43107864a8aSChristos Margiolis ks->state = KINST_PROBE_ARMED;
43207864a8aSChristos Margiolis }
43307864a8aSChristos Margiolis
43407864a8aSChristos Margiolis return (0);
43507864a8aSChristos Margiolis }
43607864a8aSChristos Margiolis
43707864a8aSChristos Margiolis void
kinst_md_deinit(void)43807864a8aSChristos Margiolis kinst_md_deinit(void)
43907864a8aSChristos Margiolis {
44007864a8aSChristos Margiolis }
44107864a8aSChristos Margiolis
44207864a8aSChristos Margiolis /*
44307864a8aSChristos Margiolis * Exclude machine-dependent functions that are not safe-to-trace.
44407864a8aSChristos Margiolis */
44507864a8aSChristos Margiolis bool
kinst_md_excluded(const char * name)44607864a8aSChristos Margiolis kinst_md_excluded(const char *name)
44707864a8aSChristos Margiolis {
44807864a8aSChristos Margiolis if (strcmp(name, "handle_el1h_sync") == 0 ||
44907864a8aSChristos Margiolis strcmp(name, "do_el1h_sync") == 0)
45007864a8aSChristos Margiolis return (true);
45107864a8aSChristos Margiolis
45207864a8aSChristos Margiolis return (false);
45307864a8aSChristos Margiolis }
454