xref: /freebsd/sys/dev/hwt/hwt.c (revision df114daef4c48548c3c2b86717612761185ae18f)
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