1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
5 *
6 * This work was supported by Innovate UK project 105694, "Digital Security
7 * by Design (DSbD) Technology Platform Prototype".
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 /*
32 * Hardware Tracing framework.
33 *
34 * The framework manages hardware tracing units that collect information
35 * about software execution and store it as events in highly compressed format
36 * into DRAM. The events cover information about control flow changes of a
37 * program, whether branches taken or not, exceptions taken, timing information,
38 * cycles elapsed and more. That allows us to restore entire program flow of a
39 * given application without performance impact.
40 *
41 * Design overview.
42 *
43 * The framework provides character devices for mmap(2) and ioctl(2) system
44 * calls to allow user to manage CPU (hardware) tracing units.
45 *
46 * /dev/hwt:
47 * .ioctl:
48 * hwt_ioctl():
49 * a) HWT_IOC_ALLOC
50 * Allocates kernel tracing context CTX based on requested mode
51 * of operation. Verifies the information that comes with the
52 * request (pid, cpus), allocates unique ID for the context.
53 * Creates a new character device for CTX management.
54 *
55 * /dev/hwt_%d[_%d], ident[, thread_id]
56 * .mmap
57 * Maps tracing buffers of the corresponding thread to userspace.
58 * .ioctl
59 * hwt_thread_ioctl():
60 * a) HWT_IOC_START
61 * Enables tracing unit for a given context.
62 * b) HWT_IOC_RECORD_GET
63 * Transfers (small) record entries collected during program
64 * execution for a given context to userspace, such as mmaping
65 * tables of executable and dynamic libraries, interpreter,
66 * kernel mappings, tid of threads created, etc.
67 * c) HWT_IOC_SET_CONFIG
68 * Allows to specify backend-specific configuration of the
69 * trace unit.
70 * d) HWT_IOC_WAKEUP
71 * Wakes up a thread that is currently sleeping.
72 * e) HWT_IOC_BUFPTR_GET
73 * Transfers current hardware pointer in the filling buffer
74 * to the userspace.
75 * f) HWT_IOC_SVC_BUF
76 * To avoid data loss, userspace may notify kernel it has
77 * copied out the given buffer, so kernel is ok to overwrite
78 *
79 * HWT context lifecycle in THREAD mode of operation:
80 * 1. User invokes HWT_IOC_ALLOC ioctl with information about pid to trace and
81 * size of the buffers for the trace data to allocate.
82 * Some architectures may have different tracing units supported, so user
83 * also provides backend name to use for this context, e.g. "coresight".
84 * 2. Kernel allocates context, lookups the proc for the given pid. Then it
85 * creates first hwt_thread in the context and allocates trace buffers for
86 * it. Immediately, kernel initializes tracing backend.
87 * Kernel creates character device and returns unique identificator of
88 * trace context to the user.
89 * 3. To manage the new context, user opens the character device created.
90 * User invokes HWT_IOC_START ioctl, kernel marks context as RUNNING.
91 * At this point any HWT hook invocation by scheduler enables/disables
92 * tracing for threads associated with the context (threads of the proc).
93 * Any new threads creation (of the target proc) procedures will be invoking
94 * corresponding hooks in HWT framework, so that new hwt_thread and buffers
95 * allocated, character device for mmap(2) created on the fly.
96 * 4. User issues HWT_IOC_RECORD_GET ioctl to fetch information about mmaping
97 * tables and threads created during application startup.
98 * 5. User mmaps tracing buffers of each thread to userspace (using
99 * /dev/hwt_%d_%d % (ident, thread_id) character devices).
100 * 6. User can repeat 4 if expected thread is not yet created during target
101 * application execution.
102 * 7. User issues HWT_IOC_BUFPTR_GET ioctl to get current filling level of the
103 * hardware buffer of a given thread.
104 * 8. User invokes trace decoder library to process available data and see the
105 * results in human readable form.
106 * 9. User repeats 7 if needed.
107 *
108 * HWT context lifecycle in CPU mode of operation:
109 * 1. User invokes HWT_IOC_ALLOC ioctl providing a set of CPU to trace within
110 * single CTX.
111 * 2. Kernel verifies the set of CPU and allocates tracing context, creates
112 * a buffer for each CPU.
113 * Kernel creates a character device for every CPU provided in the request.
114 * Kernel initialized tracing backend.
115 * 3. User opens character devices of interest to map the buffers to userspace.
116 * User can start tracing by invoking HWT_IOC_START on any of character
117 * device within the context, entire context will be marked as RUNNING.
118 * 4. The rest is similar to the THREAD mode.
119 *
120 */
121
122 #include <sys/param.h>
123 #include <sys/conf.h>
124 #include <sys/eventhandler.h>
125 #include <sys/kernel.h>
126 #include <sys/module.h>
127
128 #include <dev/hwt/hwt_context.h>
129 #include <dev/hwt/hwt_contexthash.h>
130 #include <dev/hwt/hwt_thread.h>
131 #include <dev/hwt/hwt_owner.h>
132 #include <dev/hwt/hwt_ownerhash.h>
133 #include <dev/hwt/hwt_backend.h>
134 #include <dev/hwt/hwt_record.h>
135 #include <dev/hwt/hwt_ioctl.h>
136 #include <dev/hwt/hwt_hook.h>
137
138 #define HWT_DEBUG
139 #undef HWT_DEBUG
140
141 #ifdef HWT_DEBUG
142 #define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
143 #else
144 #define dprintf(fmt, ...)
145 #endif
146
147 static eventhandler_tag hwt_exit_tag;
148 static struct cdev *hwt_cdev;
149 static struct cdevsw hwt_cdevsw = {
150 .d_version = D_VERSION,
151 .d_name = "hwt",
152 .d_mmap_single = NULL,
153 .d_ioctl = hwt_ioctl
154 };
155
156 static void
hwt_process_exit(void * arg __unused,struct proc * p)157 hwt_process_exit(void *arg __unused, struct proc *p)
158 {
159 struct hwt_owner *ho;
160
161 /* Stop HWTs associated with exiting owner, if any. */
162 ho = hwt_ownerhash_lookup(p);
163 if (ho)
164 hwt_owner_shutdown(ho);
165 }
166
167 static int
hwt_load(void)168 hwt_load(void)
169 {
170 struct make_dev_args args;
171 int error;
172
173 make_dev_args_init(&args);
174 args.mda_devsw = &hwt_cdevsw;
175 args.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK;
176 args.mda_uid = UID_ROOT;
177 args.mda_gid = GID_WHEEL;
178 args.mda_mode = 0660;
179 args.mda_si_drv1 = NULL;
180
181 hwt_backend_load();
182 hwt_ctx_load();
183 hwt_contexthash_load();
184 hwt_ownerhash_load();
185 hwt_record_load();
186
187 error = make_dev_s(&args, &hwt_cdev, "hwt");
188 if (error != 0)
189 return (error);
190
191 hwt_exit_tag = EVENTHANDLER_REGISTER(process_exit, hwt_process_exit,
192 NULL, EVENTHANDLER_PRI_ANY);
193
194 hwt_hook_load();
195
196 return (0);
197 }
198
199 static int
hwt_unload(void)200 hwt_unload(void)
201 {
202
203 hwt_hook_unload();
204 EVENTHANDLER_DEREGISTER(process_exit, hwt_exit_tag);
205 destroy_dev(hwt_cdev);
206 hwt_record_unload();
207 hwt_ownerhash_unload();
208 hwt_contexthash_unload();
209 hwt_ctx_unload();
210 hwt_backend_unload();
211
212 return (0);
213 }
214
215 static int
hwt_modevent(module_t mod,int type,void * data)216 hwt_modevent(module_t mod, int type, void *data)
217 {
218 int error;
219
220 switch (type) {
221 case MOD_LOAD:
222 error = hwt_load();
223 break;
224 case MOD_UNLOAD:
225 error = hwt_unload();
226 break;
227 default:
228 error = 0;
229 break;
230 }
231
232 return (error);
233 }
234
235 static moduledata_t hwt_mod = {
236 "hwt",
237 hwt_modevent,
238 NULL
239 };
240
241 DECLARE_MODULE(hwt, hwt_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
242 MODULE_VERSION(hwt, 1);
243