xref: /freebsd/sys/x86/x86/sdt_machdep.c (revision ddf0ed09bd8f83677407db36828aca2c10f419c9)
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