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 <vm/vm.h>
11 #include <vm/pmap.h>
12
13 #include <machine/md_var.h>
14 #include <machine/vmparam.h>
15
16 #define SDT_PATCH_SIZE 5
17
18 /*
19 * Return true if we can overwrite a nop at "patchpoint" with a jump to the
20 * target address.
21 */
22 bool
sdt_tracepoint_valid(uintptr_t patchpoint,uintptr_t target)23 sdt_tracepoint_valid(uintptr_t patchpoint, uintptr_t target)
24 {
25 #ifdef __amd64__
26 if (patchpoint < KERNSTART || target < KERNSTART)
27 return (false);
28 #endif
29 if (patchpoint == target ||
30 patchpoint + SDT_PATCH_SIZE < patchpoint)
31 return (false);
32 #ifdef __amd64__
33 int64_t offset = target - (patchpoint + SDT_PATCH_SIZE);
34 if (offset < -(1l << 31) || offset > (1l << 31))
35 return (false);
36 #endif
37 return (true);
38 }
39
40 /*
41 * Overwrite the copy of _SDT_ASM_PATCH_INSTR at the tracepoint with a jump to
42 * the target address.
43 */
44 void
sdt_tracepoint_patch(uintptr_t patchpoint,uintptr_t target)45 sdt_tracepoint_patch(uintptr_t patchpoint, uintptr_t target)
46 {
47 uint8_t instr[SDT_PATCH_SIZE];
48 int32_t disp;
49 bool old_wp;
50
51 KASSERT(sdt_tracepoint_valid(patchpoint, target),
52 ("%s: invalid tracepoint %#jx -> %#jx",
53 __func__, (uintmax_t)patchpoint, (uintmax_t)target));
54
55 instr[0] = 0xe9;
56 disp = target - (patchpoint + SDT_PATCH_SIZE);
57 memcpy(&instr[1], &disp, sizeof(disp));
58
59 old_wp = disable_wp();
60 memcpy((void *)patchpoint, instr, sizeof(instr));
61 restore_wp(old_wp);
62 }
63
64 /*
65 * Overwrite the patchpoint with a nop instruction.
66 */
67 void
sdt_tracepoint_restore(uintptr_t patchpoint)68 sdt_tracepoint_restore(uintptr_t patchpoint)
69 {
70 uint8_t instr[SDT_PATCH_SIZE];
71 bool old_wp;
72
73 #ifdef __amd64__
74 KASSERT(patchpoint >= KERNSTART,
75 ("%s: invalid patchpoint %#lx", __func__, patchpoint));
76 #endif
77
78 for (int i = 0; i < SDT_PATCH_SIZE; i++)
79 instr[i] = 0x90;
80
81 old_wp = disable_wp();
82 memcpy((void *)patchpoint, instr, sizeof(instr));
83 restore_wp(old_wp);
84 }
85