12d7bb03aSChristos Margiolis /*
22d7bb03aSChristos Margiolis * SPDX-License-Identifier: CDDL 1.0
32d7bb03aSChristos Margiolis *
42d7bb03aSChristos Margiolis * Copyright (c) 2023 The FreeBSD Foundation
52d7bb03aSChristos Margiolis *
62d7bb03aSChristos Margiolis * This software was developed by Christos Margiolis <christos@FreeBSD.org>
72d7bb03aSChristos Margiolis * under sponsorship from the FreeBSD Foundation.
82d7bb03aSChristos Margiolis */
92d7bb03aSChristos Margiolis
102d7bb03aSChristos Margiolis #include <sys/param.h>
112d7bb03aSChristos Margiolis
122d7bb03aSChristos Margiolis #include <sys/dtrace.h>
132d7bb03aSChristos Margiolis #include <cddl/dev/dtrace/dtrace_cddl.h>
142d7bb03aSChristos Margiolis
152d7bb03aSChristos Margiolis #include "kinst.h"
162d7bb03aSChristos Margiolis
172517b208SChristos Margiolis DPCPU_DEFINE_STATIC(struct kinst_cpu_state, kinst_state);
182d7bb03aSChristos Margiolis
192d7bb03aSChristos Margiolis #define _MATCH_REG(reg) \
202d7bb03aSChristos Margiolis (offsetof(struct trapframe, tf_ ## reg) / sizeof(register_t))
212d7bb03aSChristos Margiolis
222d7bb03aSChristos Margiolis static int
kinst_regoff(struct trapframe * frame,int n)232d7bb03aSChristos Margiolis kinst_regoff(struct trapframe *frame, int n)
242d7bb03aSChristos Margiolis {
252d7bb03aSChristos Margiolis switch (n) {
262d7bb03aSChristos Margiolis case 0:
272d7bb03aSChristos Margiolis /* There is no zero register in the trapframe structure. */
282d7bb03aSChristos Margiolis return (-1);
292d7bb03aSChristos Margiolis case 1:
302d7bb03aSChristos Margiolis return (_MATCH_REG(ra));
312d7bb03aSChristos Margiolis case 2:
322d7bb03aSChristos Margiolis return (_MATCH_REG(sp));
332d7bb03aSChristos Margiolis case 3:
342d7bb03aSChristos Margiolis return (_MATCH_REG(gp));
352d7bb03aSChristos Margiolis case 4:
362d7bb03aSChristos Margiolis return (_MATCH_REG(tp));
372d7bb03aSChristos Margiolis case 5 ... 7:
382d7bb03aSChristos Margiolis return (_MATCH_REG(t[n - 5]));
392d7bb03aSChristos Margiolis case 8 ... 9:
402d7bb03aSChristos Margiolis return (_MATCH_REG(s[n - 8]));
412d7bb03aSChristos Margiolis case 10 ... 17:
422d7bb03aSChristos Margiolis return (_MATCH_REG(a[n - 10]));
432d7bb03aSChristos Margiolis case 18 ... 27:
442d7bb03aSChristos Margiolis return (_MATCH_REG(s[n - 18 + 2]));
452d7bb03aSChristos Margiolis case 28 ... 31:
462d7bb03aSChristos Margiolis return (_MATCH_REG(t[n - 28 + 3]));
472d7bb03aSChristos Margiolis default:
482d7bb03aSChristos Margiolis panic("%s: unhandled register index %d", __func__, n);
492d7bb03aSChristos Margiolis }
502d7bb03aSChristos Margiolis }
512d7bb03aSChristos Margiolis
522d7bb03aSChristos Margiolis static int
kinst_c_regoff(struct trapframe * frame,int n)532d7bb03aSChristos Margiolis kinst_c_regoff(struct trapframe *frame, int n)
542d7bb03aSChristos Margiolis {
552d7bb03aSChristos Margiolis switch (n) {
562d7bb03aSChristos Margiolis case 0 ... 1:
572d7bb03aSChristos Margiolis return (_MATCH_REG(s[n]));
582d7bb03aSChristos Margiolis case 2 ... 7:
592d7bb03aSChristos Margiolis return (_MATCH_REG(a[n - 2]));
602d7bb03aSChristos Margiolis default:
612d7bb03aSChristos Margiolis panic("%s: unhandled register index %d", __func__, n);
622d7bb03aSChristos Margiolis }
632d7bb03aSChristos Margiolis }
642d7bb03aSChristos Margiolis
652d7bb03aSChristos Margiolis #undef _MATCH_REG
662d7bb03aSChristos Margiolis
672d7bb03aSChristos Margiolis static int
kinst_emulate(struct trapframe * frame,const struct kinst_probe * kp)682517b208SChristos Margiolis kinst_emulate(struct trapframe *frame, const struct kinst_probe *kp)
692d7bb03aSChristos Margiolis {
702d7bb03aSChristos Margiolis kinst_patchval_t instr = kp->kp_savedval;
712d7bb03aSChristos Margiolis register_t prevpc;
722d7bb03aSChristos Margiolis uint64_t imm;
732d7bb03aSChristos Margiolis uint16_t off;
742d7bb03aSChristos Margiolis uint8_t funct;
752d7bb03aSChristos Margiolis
762d7bb03aSChristos Margiolis if (kp->kp_md.instlen == INSN_SIZE) {
772d7bb03aSChristos Margiolis #define rs1_index ((instr & RS1_MASK) >> RS1_SHIFT)
782d7bb03aSChristos Margiolis #define rs2_index ((instr & RS2_MASK) >> RS2_SHIFT)
792d7bb03aSChristos Margiolis #define rd_index ((instr & RD_MASK) >> RD_SHIFT)
802d7bb03aSChristos Margiolis #define rs1 ((register_t *)frame)[kinst_regoff(frame, rs1_index)]
812d7bb03aSChristos Margiolis #define rs2 ((register_t *)frame)[kinst_regoff(frame, rs2_index)]
822d7bb03aSChristos Margiolis #define rd ((register_t *)frame)[kinst_regoff(frame, rd_index)]
832d7bb03aSChristos Margiolis #define rs1_lval (rs1_index != 0 ? rs1 : 0)
842d7bb03aSChristos Margiolis #define rs2_lval (rs2_index != 0 ? rs2 : 0)
852d7bb03aSChristos Margiolis switch (instr & 0x7f) {
862d7bb03aSChristos Margiolis case 0b1101111: /* jal */
872d7bb03aSChristos Margiolis imm = 0;
882d7bb03aSChristos Margiolis imm |= ((instr >> 21) & 0x03ff) << 1;
892d7bb03aSChristos Margiolis imm |= ((instr >> 20) & 0x0001) << 11;
902d7bb03aSChristos Margiolis imm |= ((instr >> 12) & 0x00ff) << 12;
912d7bb03aSChristos Margiolis imm |= ((instr >> 31) & 0x0001) << 20;
922d7bb03aSChristos Margiolis if (imm & 0x0000000000100000)
932d7bb03aSChristos Margiolis imm |= 0xfffffffffff00000;
942d7bb03aSChristos Margiolis if (rd_index != 0)
952d7bb03aSChristos Margiolis rd = frame->tf_sepc + INSN_SIZE;
962d7bb03aSChristos Margiolis frame->tf_sepc += imm;
972d7bb03aSChristos Margiolis break;
982d7bb03aSChristos Margiolis case 0b1100111: /* jalr */
992d7bb03aSChristos Margiolis prevpc = frame->tf_sepc;
1002d7bb03aSChristos Margiolis imm = (instr & IMM_MASK) >> IMM_SHIFT;
1012d7bb03aSChristos Margiolis if (imm & 0x0000000000000800)
1022d7bb03aSChristos Margiolis imm |= 0xfffffffffffff000;
1032d7bb03aSChristos Margiolis frame->tf_sepc = (rs1_lval + imm) & ~1;
1042d7bb03aSChristos Margiolis if (rd_index != 0)
1052d7bb03aSChristos Margiolis rd = prevpc + INSN_SIZE;
1062d7bb03aSChristos Margiolis break;
1072d7bb03aSChristos Margiolis case 0b1100011: /* branch */
1082d7bb03aSChristos Margiolis imm = 0;
1092d7bb03aSChristos Margiolis imm |= ((instr >> 8) & 0x000f) << 1;
1102d7bb03aSChristos Margiolis imm |= ((instr >> 25) & 0x003f) << 5;
1112d7bb03aSChristos Margiolis imm |= ((instr >> 7) & 0x0001) << 11;
1122d7bb03aSChristos Margiolis imm |= ((instr >> 31) & 0x0001) << 12;
1132d7bb03aSChristos Margiolis if (imm & 0x0000000000001000)
1142d7bb03aSChristos Margiolis imm |= 0xfffffffffffff000;
1152d7bb03aSChristos Margiolis funct = (instr >> 12) & 0x07;
1162d7bb03aSChristos Margiolis switch (funct) {
1172d7bb03aSChristos Margiolis case 0b000: /* beq */
1182d7bb03aSChristos Margiolis if (rs1_lval == rs2_lval)
1192d7bb03aSChristos Margiolis frame->tf_sepc += imm;
1202d7bb03aSChristos Margiolis else
1212d7bb03aSChristos Margiolis frame->tf_sepc += INSN_SIZE;
1222d7bb03aSChristos Margiolis break;
1232d7bb03aSChristos Margiolis case 0b001: /* bne */
1242d7bb03aSChristos Margiolis if (rs1_lval != rs2_lval)
1252d7bb03aSChristos Margiolis frame->tf_sepc += imm;
1262d7bb03aSChristos Margiolis else
1272d7bb03aSChristos Margiolis frame->tf_sepc += INSN_SIZE;
1282d7bb03aSChristos Margiolis break;
1292d7bb03aSChristos Margiolis case 0b100: /* blt */
1302d7bb03aSChristos Margiolis if ((int64_t)rs1_lval < (int64_t)rs2_lval)
1312d7bb03aSChristos Margiolis frame->tf_sepc += imm;
1322d7bb03aSChristos Margiolis else
1332d7bb03aSChristos Margiolis frame->tf_sepc += INSN_SIZE;
1342d7bb03aSChristos Margiolis break;
1352d7bb03aSChristos Margiolis case 0b110: /* bltu */
1362d7bb03aSChristos Margiolis if ((uint64_t)rs1_lval < (uint64_t)rs2_lval)
1372d7bb03aSChristos Margiolis frame->tf_sepc += imm;
1382d7bb03aSChristos Margiolis else
1392d7bb03aSChristos Margiolis frame->tf_sepc += INSN_SIZE;
1402d7bb03aSChristos Margiolis break;
1412d7bb03aSChristos Margiolis case 0b101: /* bge */
1422d7bb03aSChristos Margiolis if ((int64_t)rs1_lval >= (int64_t)rs2_lval)
1432d7bb03aSChristos Margiolis frame->tf_sepc += imm;
1442d7bb03aSChristos Margiolis else
1452d7bb03aSChristos Margiolis frame->tf_sepc += INSN_SIZE;
1462d7bb03aSChristos Margiolis break;
1472d7bb03aSChristos Margiolis case 0b111: /* bgeu */
1482d7bb03aSChristos Margiolis if ((uint64_t)rs1_lval >= (uint64_t)rs2_lval)
1492d7bb03aSChristos Margiolis frame->tf_sepc += imm;
1502d7bb03aSChristos Margiolis else
1512d7bb03aSChristos Margiolis frame->tf_sepc += INSN_SIZE;
1522d7bb03aSChristos Margiolis break;
1532d7bb03aSChristos Margiolis }
1542d7bb03aSChristos Margiolis break;
1552d7bb03aSChristos Margiolis case 0b0010111: /* auipc */
1562d7bb03aSChristos Margiolis imm = instr & 0xfffff000;
1572d7bb03aSChristos Margiolis rd = frame->tf_sepc +
1582d7bb03aSChristos Margiolis (imm & 0x0000000080000000 ?
1592d7bb03aSChristos Margiolis imm | 0xffffffff80000000 : imm);
1602d7bb03aSChristos Margiolis frame->tf_sepc += INSN_SIZE;
1612d7bb03aSChristos Margiolis break;
1622d7bb03aSChristos Margiolis }
1632d7bb03aSChristos Margiolis #undef rs1_lval
1642d7bb03aSChristos Margiolis #undef rs2_lval
1652d7bb03aSChristos Margiolis #undef rs1
1662d7bb03aSChristos Margiolis #undef rs2
1672d7bb03aSChristos Margiolis #undef rd
1682d7bb03aSChristos Margiolis #undef rs1_index
1692d7bb03aSChristos Margiolis #undef rs2_index
1702d7bb03aSChristos Margiolis #undef rd_index
1712d7bb03aSChristos Margiolis } else {
1722d7bb03aSChristos Margiolis switch (instr & 0x03) {
1732d7bb03aSChristos Margiolis #define rs1 \
1742d7bb03aSChristos Margiolis ((register_t *)frame)[kinst_c_regoff(frame, (instr >> 7) & 0x07)]
1752d7bb03aSChristos Margiolis case 0b01:
1762d7bb03aSChristos Margiolis funct = (instr >> 13) & 0x07;
1772d7bb03aSChristos Margiolis switch (funct) {
1782d7bb03aSChristos Margiolis case 0b101: /* c.j */
1792d7bb03aSChristos Margiolis off = (instr >> 2) & 0x07ff;
1802d7bb03aSChristos Margiolis imm = 0;
1812d7bb03aSChristos Margiolis imm |= ((off >> 1) & 0x07) << 1;
1822d7bb03aSChristos Margiolis imm |= ((off >> 9) & 0x01) << 4;
1832d7bb03aSChristos Margiolis imm |= ((off >> 0) & 0x01) << 5;
1842d7bb03aSChristos Margiolis imm |= ((off >> 5) & 0x01) << 6;
1852d7bb03aSChristos Margiolis imm |= ((off >> 4) & 0x01) << 7;
1862d7bb03aSChristos Margiolis imm |= ((off >> 7) & 0x03) << 8;
1872d7bb03aSChristos Margiolis imm |= ((off >> 6) & 0x01) << 10;
1882d7bb03aSChristos Margiolis imm |= ((off >> 10) & 0x01) << 11;
1892d7bb03aSChristos Margiolis if (imm & 0x0000000000000800)
1902d7bb03aSChristos Margiolis imm |= 0xfffffffffffff000;
1912d7bb03aSChristos Margiolis frame->tf_sepc += imm;
1922d7bb03aSChristos Margiolis break;
1932d7bb03aSChristos Margiolis case 0b110: /* c.beqz */
1942d7bb03aSChristos Margiolis case 0b111: /* c.bnez */
1952d7bb03aSChristos Margiolis imm = 0;
1962d7bb03aSChristos Margiolis imm |= ((instr >> 3) & 0x03) << 1;
1972d7bb03aSChristos Margiolis imm |= ((instr >> 10) & 0x03) << 3;
1982d7bb03aSChristos Margiolis imm |= ((instr >> 2) & 0x01) << 5;
1992d7bb03aSChristos Margiolis imm |= ((instr >> 5) & 0x03) << 6;
2002d7bb03aSChristos Margiolis imm |= ((instr >> 12) & 0x01) << 8;
2012d7bb03aSChristos Margiolis if (imm & 0x0000000000000100)
2022d7bb03aSChristos Margiolis imm |= 0xffffffffffffff00;
2032d7bb03aSChristos Margiolis if (funct == 0b110 && rs1 == 0)
2042d7bb03aSChristos Margiolis frame->tf_sepc += imm;
2052d7bb03aSChristos Margiolis else if (funct == 0b111 && rs1 != 0)
2062d7bb03aSChristos Margiolis frame->tf_sepc += imm;
2072d7bb03aSChristos Margiolis else
2082d7bb03aSChristos Margiolis frame->tf_sepc += INSN_C_SIZE;
2092d7bb03aSChristos Margiolis break;
2102d7bb03aSChristos Margiolis }
2112d7bb03aSChristos Margiolis break;
2122d7bb03aSChristos Margiolis #undef rs1
2132d7bb03aSChristos Margiolis #define rs1_index ((instr & RD_MASK) >> RD_SHIFT)
2142d7bb03aSChristos Margiolis #define rs1 ((register_t *)frame)[kinst_regoff(frame, rs1_index)]
2152d7bb03aSChristos Margiolis case 0b10:
2162d7bb03aSChristos Margiolis funct = (instr >> 13) & 0x07;
2172d7bb03aSChristos Margiolis if (funct == 0b100 && rs1_index != 0) {
2182d7bb03aSChristos Margiolis /* c.jr/c.jalr */
2192d7bb03aSChristos Margiolis prevpc = frame->tf_sepc;
2202d7bb03aSChristos Margiolis frame->tf_sepc = rs1;
2212d7bb03aSChristos Margiolis if (((instr >> 12) & 0x01) != 0)
2222d7bb03aSChristos Margiolis frame->tf_ra = prevpc + INSN_C_SIZE;
2232d7bb03aSChristos Margiolis }
2242d7bb03aSChristos Margiolis break;
2252d7bb03aSChristos Margiolis #undef rs1
2262d7bb03aSChristos Margiolis #undef rs1_index
2272d7bb03aSChristos Margiolis }
2282d7bb03aSChristos Margiolis }
2292d7bb03aSChristos Margiolis
2302d7bb03aSChristos Margiolis return (MATCH_C_NOP);
2312d7bb03aSChristos Margiolis }
2322d7bb03aSChristos Margiolis
2332d7bb03aSChristos Margiolis static int
kinst_jump_next_instr(struct trapframe * frame,const struct kinst_probe * kp)2342517b208SChristos Margiolis kinst_jump_next_instr(struct trapframe *frame, const struct kinst_probe *kp)
2352d7bb03aSChristos Margiolis {
2362517b208SChristos Margiolis frame->tf_sepc = (register_t)((const uint8_t *)kp->kp_patchpoint +
2372d7bb03aSChristos Margiolis kp->kp_md.instlen);
2382d7bb03aSChristos Margiolis
2392d7bb03aSChristos Margiolis return (MATCH_C_NOP);
2402d7bb03aSChristos Margiolis }
2412d7bb03aSChristos Margiolis
2422d7bb03aSChristos Margiolis static void
kinst_trampoline_populate(struct kinst_probe * kp)2432517b208SChristos Margiolis kinst_trampoline_populate(struct kinst_probe *kp)
2442d7bb03aSChristos Margiolis {
2452d7bb03aSChristos Margiolis static uint16_t nop = MATCH_C_NOP;
2462d7bb03aSChristos Margiolis static uint32_t ebreak = MATCH_EBREAK;
2472d7bb03aSChristos Margiolis int ilen;
2482d7bb03aSChristos Margiolis
2492d7bb03aSChristos Margiolis ilen = kp->kp_md.instlen;
2502517b208SChristos Margiolis kinst_memcpy(kp->kp_tramp, &kp->kp_savedval, ilen);
2512d7bb03aSChristos Margiolis
2522d7bb03aSChristos Margiolis /*
2532d7bb03aSChristos Margiolis * Since we cannot encode large displacements in a single instruction
2542d7bb03aSChristos Margiolis * in order to encode a far-jump back to the next instruction, and we
2552d7bb03aSChristos Margiolis * also cannot clobber a register inside the trampoline, we execute a
2562d7bb03aSChristos Margiolis * breakpoint after the copied instruction. kinst_invop() is
2572d7bb03aSChristos Margiolis * responsible for detecting this special case and performing the
2582d7bb03aSChristos Margiolis * "jump" manually.
2592d7bb03aSChristos Margiolis *
2602d7bb03aSChristos Margiolis * Add a NOP after a compressed instruction for padding.
2612d7bb03aSChristos Margiolis */
2622d7bb03aSChristos Margiolis if (ilen == INSN_C_SIZE)
2632517b208SChristos Margiolis kinst_memcpy(&kp->kp_tramp[ilen], &nop, INSN_C_SIZE);
2642d7bb03aSChristos Margiolis
2652517b208SChristos Margiolis kinst_memcpy(&kp->kp_tramp[INSN_SIZE], &ebreak, INSN_SIZE);
2662d7bb03aSChristos Margiolis
2672d7bb03aSChristos Margiolis fence_i();
2682d7bb03aSChristos Margiolis }
2692d7bb03aSChristos Margiolis
2702d7bb03aSChristos Margiolis /*
2712d7bb03aSChristos Margiolis * There are two ways by which an instruction is traced:
2722d7bb03aSChristos Margiolis *
2732d7bb03aSChristos Margiolis * - By using the trampoline.
2742d7bb03aSChristos Margiolis * - By emulating it in software (see kinst_emulate()).
2752d7bb03aSChristos Margiolis *
2762d7bb03aSChristos Margiolis * The trampoline is used for instructions that can be copied and executed
2772d7bb03aSChristos Margiolis * as-is without additional modification. However, instructions that use
2782d7bb03aSChristos Margiolis * PC-relative addressing have to be emulated, because RISC-V doesn't allow
2792d7bb03aSChristos Margiolis * encoding of large displacements in a single instruction, and since we cannot
2802d7bb03aSChristos Margiolis * clobber a register in order to encode the two-instruction sequence needed to
2812d7bb03aSChristos Margiolis * create large displacements, we cannot use the trampoline at all.
2822d7bb03aSChristos Margiolis * Fortunately, the instructions are simple enough to be emulated in just a few
2832d7bb03aSChristos Margiolis * lines of code.
2842d7bb03aSChristos Margiolis *
2852d7bb03aSChristos Margiolis * The problem discussed above also means that, unlike amd64, we cannot encode
2862d7bb03aSChristos Margiolis * a far-jump back from the trampoline to the next instruction. The mechanism
2872d7bb03aSChristos Margiolis * employed to achieve this functionality, is to use a breakpoint instead of a
2882d7bb03aSChristos Margiolis * jump after the copied instruction. This breakpoint is detected and handled
2892d7bb03aSChristos Margiolis * by kinst_invop(), which performs the jump back to the next instruction
2902d7bb03aSChristos Margiolis * manually (see kinst_jump_next_instr()).
2912d7bb03aSChristos Margiolis */
2922d7bb03aSChristos Margiolis int
kinst_invop(uintptr_t addr,struct trapframe * frame,uintptr_t scratch)2932d7bb03aSChristos Margiolis kinst_invop(uintptr_t addr, struct trapframe *frame, uintptr_t scratch)
2942d7bb03aSChristos Margiolis {
2952d7bb03aSChristos Margiolis solaris_cpu_t *cpu;
2962517b208SChristos Margiolis struct kinst_cpu_state *ks;
2972517b208SChristos Margiolis const struct kinst_probe *kp;
2982d7bb03aSChristos Margiolis
2992517b208SChristos Margiolis ks = DPCPU_PTR(kinst_state);
3002d7bb03aSChristos Margiolis
3012d7bb03aSChristos Margiolis /*
3022d7bb03aSChristos Margiolis * Detect if the breakpoint was triggered by the trampoline, and
3032d7bb03aSChristos Margiolis * manually set the PC to the next instruction.
3042d7bb03aSChristos Margiolis */
3052517b208SChristos Margiolis if (ks->state == KINST_PROBE_FIRED &&
3062517b208SChristos Margiolis addr == (uintptr_t)(ks->kp->kp_tramp + INSN_SIZE)) {
3072517b208SChristos Margiolis /*
3082517b208SChristos Margiolis * Restore interrupts if they were enabled prior to the first
3092517b208SChristos Margiolis * breakpoint.
3102517b208SChristos Margiolis */
3112517b208SChristos Margiolis if ((ks->status & SSTATUS_SPIE) != 0)
3122517b208SChristos Margiolis frame->tf_sstatus |= SSTATUS_SPIE;
3132517b208SChristos Margiolis ks->state = KINST_PROBE_ARMED;
3142517b208SChristos Margiolis return (kinst_jump_next_instr(frame, ks->kp));
3152517b208SChristos Margiolis }
3162d7bb03aSChristos Margiolis
3172d7bb03aSChristos Margiolis LIST_FOREACH(kp, KINST_GETPROBE(addr), kp_hashnext) {
3182d7bb03aSChristos Margiolis if ((uintptr_t)kp->kp_patchpoint == addr)
3192d7bb03aSChristos Margiolis break;
3202d7bb03aSChristos Margiolis }
3212d7bb03aSChristos Margiolis if (kp == NULL)
3222d7bb03aSChristos Margiolis return (0);
3232d7bb03aSChristos Margiolis
3242d7bb03aSChristos Margiolis cpu = &solaris_cpu[curcpu];
3252d7bb03aSChristos Margiolis cpu->cpu_dtrace_caller = addr;
3262d7bb03aSChristos Margiolis dtrace_probe(kp->kp_id, 0, 0, 0, 0, 0);
3272d7bb03aSChristos Margiolis cpu->cpu_dtrace_caller = 0;
3282d7bb03aSChristos Margiolis
3292d7bb03aSChristos Margiolis if (kp->kp_md.emulate)
3302d7bb03aSChristos Margiolis return (kinst_emulate(frame, kp));
3312d7bb03aSChristos Margiolis
3322517b208SChristos Margiolis ks->state = KINST_PROBE_FIRED;
3332517b208SChristos Margiolis ks->kp = kp;
3342517b208SChristos Margiolis
3352d7bb03aSChristos Margiolis /*
3362517b208SChristos Margiolis * Cache the current SSTATUS and clear interrupts for the
3372517b208SChristos Margiolis * duration of the double breakpoint.
3382d7bb03aSChristos Margiolis */
3392517b208SChristos Margiolis ks->status = frame->tf_sstatus;
3402517b208SChristos Margiolis frame->tf_sstatus &= ~SSTATUS_SPIE;
3412517b208SChristos Margiolis frame->tf_sepc = (register_t)kp->kp_tramp;
3422d7bb03aSChristos Margiolis
3432d7bb03aSChristos Margiolis return (MATCH_C_NOP);
3442d7bb03aSChristos Margiolis }
3452d7bb03aSChristos Margiolis
3462d7bb03aSChristos Margiolis void
kinst_patch_tracepoint(struct kinst_probe * kp,kinst_patchval_t val)3472d7bb03aSChristos Margiolis kinst_patch_tracepoint(struct kinst_probe *kp, kinst_patchval_t val)
3482d7bb03aSChristos Margiolis {
3492d7bb03aSChristos Margiolis switch (kp->kp_patchval) {
3502d7bb03aSChristos Margiolis case KINST_C_PATCHVAL:
3512d7bb03aSChristos Margiolis *(uint16_t *)kp->kp_patchpoint = (uint16_t)val;
3522d7bb03aSChristos Margiolis fence_i();
3532d7bb03aSChristos Margiolis break;
3542d7bb03aSChristos Margiolis case KINST_PATCHVAL:
3552d7bb03aSChristos Margiolis *kp->kp_patchpoint = val;
3562d7bb03aSChristos Margiolis fence_i();
3572d7bb03aSChristos Margiolis break;
3582d7bb03aSChristos Margiolis }
3592d7bb03aSChristos Margiolis }
3602d7bb03aSChristos Margiolis
3612d7bb03aSChristos Margiolis static void
kinst_instr_dissect(struct kinst_probe * kp,int instrsize)3622d7bb03aSChristos Margiolis kinst_instr_dissect(struct kinst_probe *kp, int instrsize)
3632d7bb03aSChristos Margiolis {
3642d7bb03aSChristos Margiolis struct kinst_probe_md *kpmd;
3652d7bb03aSChristos Margiolis kinst_patchval_t instr = kp->kp_savedval;
3662d7bb03aSChristos Margiolis uint8_t funct;
3672d7bb03aSChristos Margiolis
3682d7bb03aSChristos Margiolis kpmd = &kp->kp_md;
3692d7bb03aSChristos Margiolis kpmd->instlen = instrsize;
3702d7bb03aSChristos Margiolis kpmd->emulate = false;
3712d7bb03aSChristos Margiolis
3722d7bb03aSChristos Margiolis /*
3732d7bb03aSChristos Margiolis * The following instructions use PC-relative addressing and need to be
3742d7bb03aSChristos Margiolis * emulated in software.
3752d7bb03aSChristos Margiolis */
3762d7bb03aSChristos Margiolis if (kpmd->instlen == INSN_SIZE) {
3772d7bb03aSChristos Margiolis switch (instr & 0x7f) {
3782d7bb03aSChristos Margiolis case 0b1101111: /* jal */
3792d7bb03aSChristos Margiolis case 0b1100111: /* jalr */
3802d7bb03aSChristos Margiolis case 0b1100011: /* branch */
3812d7bb03aSChristos Margiolis case 0b0010111: /* auipc */
3822d7bb03aSChristos Margiolis kpmd->emulate = true;
3832d7bb03aSChristos Margiolis break;
3842d7bb03aSChristos Margiolis }
3852d7bb03aSChristos Margiolis } else {
3862d7bb03aSChristos Margiolis switch (instr & 0x03) {
3872d7bb03aSChristos Margiolis case 0b01:
3882d7bb03aSChristos Margiolis funct = (instr >> 13) & 0x07;
3892d7bb03aSChristos Margiolis switch (funct) {
3902d7bb03aSChristos Margiolis case 0b101: /* c.j */
3912d7bb03aSChristos Margiolis case 0b110: /* c.beqz */
3922d7bb03aSChristos Margiolis case 0b111: /* c.bnez */
3932d7bb03aSChristos Margiolis kpmd->emulate = true;
3942d7bb03aSChristos Margiolis break;
3952d7bb03aSChristos Margiolis }
3962d7bb03aSChristos Margiolis break;
3972d7bb03aSChristos Margiolis case 0b10:
3982d7bb03aSChristos Margiolis funct = (instr >> 13) & 0x07;
3992d7bb03aSChristos Margiolis if (funct == 0b100 &&
4002d7bb03aSChristos Margiolis ((instr >> 7) & 0x1f) != 0 &&
4012d7bb03aSChristos Margiolis ((instr >> 2) & 0x1f) == 0)
4022d7bb03aSChristos Margiolis kpmd->emulate = true; /* c.jr/c.jalr */
4032d7bb03aSChristos Margiolis break;
4042d7bb03aSChristos Margiolis }
4052d7bb03aSChristos Margiolis }
4062517b208SChristos Margiolis
4072517b208SChristos Margiolis if (!kpmd->emulate)
4082517b208SChristos Margiolis kinst_trampoline_populate(kp);
4092d7bb03aSChristos Margiolis }
4102d7bb03aSChristos Margiolis
4112d7bb03aSChristos Margiolis static bool
kinst_instr_system(kinst_patchval_t instr)4122d7bb03aSChristos Margiolis kinst_instr_system(kinst_patchval_t instr)
4132d7bb03aSChristos Margiolis {
4142d7bb03aSChristos Margiolis if (dtrace_match_opcode(instr, MATCH_C_EBREAK, MASK_C_EBREAK) ||
4152d7bb03aSChristos Margiolis (instr & 0x7f) == 0b1110011)
4162d7bb03aSChristos Margiolis return (true);
4172d7bb03aSChristos Margiolis
4182d7bb03aSChristos Margiolis return (false);
4192d7bb03aSChristos Margiolis }
4202d7bb03aSChristos Margiolis
4212d7bb03aSChristos Margiolis static bool
kinst_instr_lr(kinst_patchval_t instr)4222d7bb03aSChristos Margiolis kinst_instr_lr(kinst_patchval_t instr)
4232d7bb03aSChristos Margiolis {
4242d7bb03aSChristos Margiolis if (dtrace_match_opcode(instr, MATCH_LR_W, MASK_LR_W) ||
4252d7bb03aSChristos Margiolis dtrace_match_opcode(instr, MATCH_LR_D, MASK_LR_D))
4262d7bb03aSChristos Margiolis return (true);
4272d7bb03aSChristos Margiolis
4282d7bb03aSChristos Margiolis return (false);
4292d7bb03aSChristos Margiolis }
4302d7bb03aSChristos Margiolis
4312d7bb03aSChristos Margiolis static bool
kinst_instr_sc(kinst_patchval_t instr)4322d7bb03aSChristos Margiolis kinst_instr_sc(kinst_patchval_t instr)
4332d7bb03aSChristos Margiolis {
4342d7bb03aSChristos Margiolis if (dtrace_match_opcode(instr, MATCH_SC_W, MASK_SC_W) ||
4352d7bb03aSChristos Margiolis dtrace_match_opcode(instr, MATCH_SC_D, MASK_SC_D))
4362d7bb03aSChristos Margiolis return (true);
4372d7bb03aSChristos Margiolis
4382d7bb03aSChristos Margiolis return (false);
4392d7bb03aSChristos Margiolis }
4402d7bb03aSChristos Margiolis
4412d7bb03aSChristos Margiolis int
kinst_make_probe(linker_file_t lf,int symindx,linker_symval_t * symval,void * opaque)4422d7bb03aSChristos Margiolis kinst_make_probe(linker_file_t lf, int symindx, linker_symval_t *symval,
4432d7bb03aSChristos Margiolis void *opaque)
4442d7bb03aSChristos Margiolis {
4452d7bb03aSChristos Margiolis struct kinst_probe *kp;
4462d7bb03aSChristos Margiolis dtrace_kinst_probedesc_t *pd;
4472d7bb03aSChristos Margiolis const char *func;
4482d7bb03aSChristos Margiolis kinst_patchval_t *insn, v;
4492d7bb03aSChristos Margiolis uint8_t *instr, *limit;
4502d7bb03aSChristos Margiolis int instrsize, n, off;
451*bbe8195bSChristos Margiolis bool lrsc_block, store_found;
4522d7bb03aSChristos Margiolis
4532d7bb03aSChristos Margiolis pd = opaque;
4542d7bb03aSChristos Margiolis func = symval->name;
4552d7bb03aSChristos Margiolis
4562d7bb03aSChristos Margiolis if (kinst_excluded(func))
4572d7bb03aSChristos Margiolis return (0);
4582d7bb03aSChristos Margiolis if (strcmp(func, pd->kpd_func) != 0)
4592d7bb03aSChristos Margiolis return (0);
4602d7bb03aSChristos Margiolis
4612d7bb03aSChristos Margiolis instr = (uint8_t *)(symval->value);
4622d7bb03aSChristos Margiolis limit = (uint8_t *)(symval->value + symval->size);
4632d7bb03aSChristos Margiolis if (instr >= limit)
4642d7bb03aSChristos Margiolis return (0);
4652d7bb03aSChristos Margiolis
4662d7bb03aSChristos Margiolis /* Check for the usual function prologue. */
467*bbe8195bSChristos Margiolis store_found = false;
4682d7bb03aSChristos Margiolis for (insn = (kinst_patchval_t *)instr;
4692d7bb03aSChristos Margiolis insn < (kinst_patchval_t *)limit; insn++) {
470*bbe8195bSChristos Margiolis if (dtrace_instr_sdsp(&insn) || dtrace_instr_c_sdsp(&insn)) {
4712d7bb03aSChristos Margiolis store_found = true;
4722d7bb03aSChristos Margiolis break;
4732d7bb03aSChristos Margiolis }
474*bbe8195bSChristos Margiolis }
475*bbe8195bSChristos Margiolis if (!store_found)
4762d7bb03aSChristos Margiolis return (0);
4772d7bb03aSChristos Margiolis
4782d7bb03aSChristos Margiolis n = 0;
4792d7bb03aSChristos Margiolis lrsc_block = false;
4802d7bb03aSChristos Margiolis while (instr < limit) {
4812d7bb03aSChristos Margiolis instrsize = dtrace_instr_size(instr);
4822d7bb03aSChristos Margiolis off = (int)(instr - (uint8_t *)symval->value);
4832d7bb03aSChristos Margiolis
4842d7bb03aSChristos Margiolis /*
4852d7bb03aSChristos Margiolis * Avoid undefined behavior (i.e simply casting `*instr` to
4862d7bb03aSChristos Margiolis * `kinst_patchval_t`) in case the pointer is unaligned.
4872d7bb03aSChristos Margiolis * memcpy() can safely operate on unaligned pointers.
4882d7bb03aSChristos Margiolis */
4892d7bb03aSChristos Margiolis memcpy(&v, instr, sizeof(kinst_patchval_t));
4902d7bb03aSChristos Margiolis
4912d7bb03aSChristos Margiolis /* Skip SYSTEM instructions. */
4922d7bb03aSChristos Margiolis if (kinst_instr_system(v))
4932d7bb03aSChristos Margiolis goto cont;
4942d7bb03aSChristos Margiolis
4952d7bb03aSChristos Margiolis /*
4962d7bb03aSChristos Margiolis * Skip LR/SC blocks used to build atomic operations. If a
4972d7bb03aSChristos Margiolis * breakpoint is placed in a LR/SC block, the loop becomes
4982d7bb03aSChristos Margiolis * unconstrained. In this case we violate the operation and the
4992d7bb03aSChristos Margiolis * loop might fail on some implementations (see section 8.3 of
5002d7bb03aSChristos Margiolis * the RISC-V unprivileged spec).
5012d7bb03aSChristos Margiolis */
5022d7bb03aSChristos Margiolis if (kinst_instr_lr(v))
5032d7bb03aSChristos Margiolis lrsc_block = true;
5042d7bb03aSChristos Margiolis else if (kinst_instr_sc(v)) {
5052d7bb03aSChristos Margiolis lrsc_block = false;
5062d7bb03aSChristos Margiolis goto cont;
5072d7bb03aSChristos Margiolis }
5082d7bb03aSChristos Margiolis if (lrsc_block)
5092d7bb03aSChristos Margiolis goto cont;
5102d7bb03aSChristos Margiolis
5112d7bb03aSChristos Margiolis if (pd->kpd_off != -1 && off != pd->kpd_off)
5122d7bb03aSChristos Margiolis goto cont;
5132d7bb03aSChristos Margiolis
5142d7bb03aSChristos Margiolis /*
5152d7bb03aSChristos Margiolis * Prevent separate dtrace(1) instances from creating copies of
5162d7bb03aSChristos Margiolis * the same probe.
5172d7bb03aSChristos Margiolis */
5182d7bb03aSChristos Margiolis LIST_FOREACH(kp, KINST_GETPROBE(instr), kp_hashnext) {
5192d7bb03aSChristos Margiolis if (strcmp(kp->kp_func, func) == 0 &&
5202d7bb03aSChristos Margiolis strtol(kp->kp_name, NULL, 10) == off)
5212d7bb03aSChristos Margiolis return (0);
5222d7bb03aSChristos Margiolis }
5232d7bb03aSChristos Margiolis if (++n > KINST_PROBETAB_MAX) {
5242d7bb03aSChristos Margiolis KINST_LOG("probe list full: %d entries", n);
5252d7bb03aSChristos Margiolis return (ENOMEM);
5262d7bb03aSChristos Margiolis }
5272d7bb03aSChristos Margiolis kp = malloc(sizeof(struct kinst_probe), M_KINST,
5282d7bb03aSChristos Margiolis M_WAITOK | M_ZERO);
5292d7bb03aSChristos Margiolis kp->kp_func = func;
5302d7bb03aSChristos Margiolis snprintf(kp->kp_name, sizeof(kp->kp_name), "%d", off);
5312d7bb03aSChristos Margiolis kp->kp_patchpoint = (kinst_patchval_t *)instr;
5322d7bb03aSChristos Margiolis kp->kp_savedval = v;
5332d7bb03aSChristos Margiolis if (instrsize == INSN_SIZE)
5342d7bb03aSChristos Margiolis kp->kp_patchval = KINST_PATCHVAL;
5352d7bb03aSChristos Margiolis else
5362d7bb03aSChristos Margiolis kp->kp_patchval = KINST_C_PATCHVAL;
5372517b208SChristos Margiolis if ((kp->kp_tramp = kinst_trampoline_alloc(M_WAITOK)) == NULL) {
5382517b208SChristos Margiolis KINST_LOG("cannot allocate trampoline for %p", instr);
5392517b208SChristos Margiolis return (ENOMEM);
5402517b208SChristos Margiolis }
5412d7bb03aSChristos Margiolis
5422d7bb03aSChristos Margiolis kinst_instr_dissect(kp, instrsize);
5432d7bb03aSChristos Margiolis kinst_probe_create(kp, lf);
5442d7bb03aSChristos Margiolis cont:
5452d7bb03aSChristos Margiolis instr += instrsize;
5462d7bb03aSChristos Margiolis }
5472d7bb03aSChristos Margiolis if (lrsc_block)
5482d7bb03aSChristos Margiolis KINST_LOG("warning: unterminated LR/SC block");
5492d7bb03aSChristos Margiolis
5502d7bb03aSChristos Margiolis return (0);
5512d7bb03aSChristos Margiolis }
5522d7bb03aSChristos Margiolis
5532d7bb03aSChristos Margiolis int
kinst_md_init(void)5542d7bb03aSChristos Margiolis kinst_md_init(void)
5552d7bb03aSChristos Margiolis {
5562517b208SChristos Margiolis struct kinst_cpu_state *ks;
5572d7bb03aSChristos Margiolis int cpu;
5582d7bb03aSChristos Margiolis
5592d7bb03aSChristos Margiolis CPU_FOREACH(cpu) {
5602517b208SChristos Margiolis ks = DPCPU_PTR(kinst_state);
5612517b208SChristos Margiolis ks->state = KINST_PROBE_ARMED;
5622d7bb03aSChristos Margiolis }
5632d7bb03aSChristos Margiolis
5642d7bb03aSChristos Margiolis return (0);
5652d7bb03aSChristos Margiolis }
5662d7bb03aSChristos Margiolis
5672d7bb03aSChristos Margiolis void
kinst_md_deinit(void)5682d7bb03aSChristos Margiolis kinst_md_deinit(void)
5692d7bb03aSChristos Margiolis {
5702d7bb03aSChristos Margiolis }
5712d7bb03aSChristos Margiolis
5722d7bb03aSChristos Margiolis /*
5732d7bb03aSChristos Margiolis * Exclude machine-dependent functions that are not safe-to-trace.
5742d7bb03aSChristos Margiolis */
5752d7bb03aSChristos Margiolis bool
kinst_md_excluded(const char * name)5762d7bb03aSChristos Margiolis kinst_md_excluded(const char *name)
5772d7bb03aSChristos Margiolis {
5782d7bb03aSChristos Margiolis if (strcmp(name, "cpu_exception_handler") == 0 ||
5792d7bb03aSChristos Margiolis strcmp(name, "cpu_exception_handler_supervisor") == 0 ||
5802d7bb03aSChristos Margiolis strcmp(name, "cpu_exception_handler_user") == 0 ||
5812d7bb03aSChristos Margiolis strcmp(name, "do_trap_supervisor") == 0 ||
5822d7bb03aSChristos Margiolis strcmp(name, "do_trap_user") == 0)
5832d7bb03aSChristos Margiolis return (true);
5842d7bb03aSChristos Margiolis
5852d7bb03aSChristos Margiolis return (false);
5862d7bb03aSChristos Margiolis }
587