1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2024 Mark Johnston <markj@FreeBSD.org>
5 */
6
7 #include <sys/systm.h>
8 #include <sys/sdt.h>
9
10 #include <machine/cpu.h>
11
12 /*
13 * Return true if we can overwrite a nop at "patchpoint" with a jump to the
14 * target address.
15 */
16 bool
sdt_tracepoint_valid(uintptr_t patchpoint,uintptr_t target)17 sdt_tracepoint_valid(uintptr_t patchpoint, uintptr_t target)
18 {
19 int32_t offset;
20
21 if (patchpoint == target ||
22 (patchpoint & (INSN_SIZE - 1)) != 0 ||
23 (target & (INSN_SIZE - 1)) != 0 ||
24 patchpoint + 2 * INSN_SIZE < patchpoint)
25 return (false);
26 offset = target - (patchpoint + 2 * INSN_SIZE);
27 if (offset < -(1 << 24) || offset > (1 >> 24))
28 return (false);
29 return (true);
30 }
31
32 /*
33 * Overwrite the copy of _SDT_ASM_PATCH_INSTR at the tracepoint with a jump to
34 * the target address.
35 */
36 void
sdt_tracepoint_patch(uintptr_t patchpoint,uintptr_t target)37 sdt_tracepoint_patch(uintptr_t patchpoint, uintptr_t target)
38 {
39 uint32_t instr;
40
41 KASSERT(sdt_tracepoint_valid(patchpoint, target),
42 ("%s: invalid tracepoint %#x -> %#x",
43 __func__, patchpoint, target));
44
45 instr =
46 (((target - (patchpoint + 2 * INSN_SIZE)) >> 2) & ((1 << 24) - 1)) |
47 0xea000000;
48 memcpy((void *)patchpoint, &instr, sizeof(instr));
49 icache_sync(patchpoint, sizeof(instr));
50 }
51
52 /*
53 * Overwrite the patchpoint with a nop instruction.
54 */
55 void
sdt_tracepoint_restore(uintptr_t patchpoint)56 sdt_tracepoint_restore(uintptr_t patchpoint)
57 {
58 uint32_t instr;
59
60 instr = 0xe320f000u;
61 memcpy((void *)patchpoint, &instr, sizeof(instr));
62 icache_sync(patchpoint, sizeof(instr));
63 }
64