xref: /linux/Documentation/hid/hid-bpf.rst (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
1.. SPDX-License-Identifier: GPL-2.0
2
3=======
4HID-BPF
5=======
6
7HID is a standard protocol for input devices but some devices may require
8custom tweaks, traditionally done with a kernel driver fix. Using the eBPF
9capabilities instead speeds up development and adds new capabilities to the
10existing HID interfaces.
11
12.. contents::
13    :local:
14    :depth: 2
15
16
17When (and why) to use HID-BPF
18=============================
19
20There are several use cases when using HID-BPF is better
21than standard kernel driver fix:
22
23Dead zone of a joystick
24-----------------------
25
26Assuming you have a joystick that is getting older, it is common to see it
27wobbling around its neutral point. This is usually filtered at the application
28level by adding a *dead zone* for this specific axis.
29
30With HID-BPF, we can apply this filtering in the kernel directly so userspace
31does not get woken up when nothing else is happening on the input controller.
32
33Of course, given that this dead zone is specific to an individual device, we
34can not create a generic fix for all of the same joysticks. Adding a custom
35kernel API for this (e.g. by adding a sysfs entry) does not guarantee this new
36kernel API will be broadly adopted and maintained.
37
38HID-BPF allows the userspace program to load the program itself, ensuring we
39only load the custom API when we have a user.
40
41Simple fixup of report descriptor
42---------------------------------
43
44In the HID tree, half of the drivers only fix one key or one byte
45in the report descriptor. These fixes all require a kernel patch and the
46subsequent shepherding into a release, a long and painful process for users.
47
48We can reduce this burden by providing an eBPF program instead. Once such a
49program  has been verified by the user, we can embed the source code into the
50kernel tree and ship the eBPF program and load it directly instead of loading
51a specific kernel module for it.
52
53Note: distribution of eBPF programs and their inclusion in the kernel is not
54yet fully implemented
55
56Add a new feature that requires a new kernel API
57------------------------------------------------
58
59An example for such a feature are the Universal Stylus Interface (USI) pens.
60Basically, USI pens require a new kernel API because there are new
61channels of communication that our HID and input stack do not support.
62Instead of using hidraw or creating new sysfs entries or ioctls, we can rely
63on eBPF to have the kernel API controlled by the consumer and to not
64impact the performances by waking up userspace every time there is an
65event.
66
67Morph a device into something else and control that from userspace
68------------------------------------------------------------------
69
70The kernel has a relatively static mapping of HID items to evdev bits.
71It cannot decide to dynamically transform a given device into something else
72as it does not have the required context and any such transformation cannot be
73undone (or even discovered) by userspace.
74
75However, some devices are useless with that static way of defining devices. For
76example, the Microsoft Surface Dial is a pushbutton with haptic feedback that
77is barely usable as of today.
78
79With eBPF, userspace can morph that device into a mouse, and convert the dial
80events into wheel events. Also, the userspace program can set/unset the haptic
81feedback depending on the context. For example, if a menu is visible on the
82screen we likely need to have a haptic click every 15 degrees. But when
83scrolling in a web page the user experience is better when the device emits
84events at the highest resolution.
85
86Firewall
87--------
88
89What if we want to prevent other users to access a specific feature of a
90device? (think a possibly broken firmware update entry point)
91
92With eBPF, we can intercept any HID command emitted to the device and
93validate it or not.
94
95This also allows to sync the state between the userspace and the
96kernel/bpf program because we can intercept any incoming command.
97
98Tracing
99-------
100
101The last usage is tracing events and all the fun we can do we BPF to summarize
102and analyze events.
103
104Right now, tracing relies on hidraw. It works well except for a couple
105of issues:
106
1071. if the driver doesn't export a hidraw node, we can't trace anything
108   (eBPF will be a "god-mode" there, so this may raise some eyebrows)
1092. hidraw doesn't catch other processes' requests to the device, which
110   means that we have cases where we need to add printks to the kernel
111   to understand what is happening.
112
113High-level view of HID-BPF
114==========================
115
116The main idea behind HID-BPF is that it works at an array of bytes level.
117Thus, all of the parsing of the HID report and the HID report descriptor
118must be implemented in the userspace component that loads the eBPF
119program.
120
121For example, in the dead zone joystick from above, knowing which fields
122in the data stream needs to be set to ``0`` needs to be computed by userspace.
123
124A corollary of this is that HID-BPF doesn't know about the other subsystems
125available in the kernel. *You can not directly emit input event through the
126input API from eBPF*.
127
128When a BPF program needs to emit input events, it needs to talk with the HID
129protocol, and rely on the HID kernel processing to translate the HID data into
130input events.
131
132In-tree HID-BPF programs and ``udev-hid-bpf``
133=============================================
134
135Official device fixes are shipped in the kernel tree as source in the
136``drivers/hid/bpf/progs`` directory. This allows to add selftests to them in
137``tools/testing/selftests/hid``.
138
139However, the compilation of these objects is not part of a regular kernel compilation
140given that they need an external tool to be loaded. This tool is currently
141`udev-hid-bpf <https://libevdev.pages.freedesktop.org/udev-hid-bpf/index.html>`_.
142
143For convenience, that external repository duplicates the files from here in
144``drivers/hid/bpf/progs`` into its own ``src/bpf/stable`` directory. This allows
145distributions to not have to pull the entire kernel source tree to ship and package
146those HID-BPF fixes. ``udev-hid-bpf`` also has capabilities of handling multiple
147objects files depending on the kernel the user is running.
148
149Available types of programs
150===========================
151
152HID-BPF is built "on top" of BPF, meaning that we use bpf struct_ops method to
153declare our programs.
154
155HID-BPF has the following attachment types available:
156
1571. event processing/filtering with ``SEC("struct_ops/hid_device_event")`` in libbpf
1582. actions coming from userspace with ``SEC("syscall")`` in libbpf
1593. change of the report descriptor with ``SEC("struct_ops/hid_rdesc_fixup")`` or
160   ``SEC("struct_ops.s/hid_rdesc_fixup")`` in libbpf
161
162A ``hid_device_event`` is calling a BPF program when an event is received from
163the device. Thus we are in IRQ context and can act on the data or notify userspace.
164And given that we are in IRQ context, we can not talk back to the device.
165
166A ``syscall`` means that userspace called the syscall ``BPF_PROG_RUN`` facility.
167This time, we can do any operations allowed by HID-BPF, and talking to the device is
168allowed.
169
170Last, ``hid_rdesc_fixup`` is different from the others as there can be only one
171BPF program of this type. This is called on ``probe`` from the driver and allows to
172change the report descriptor from the BPF program. Once a ``hid_rdesc_fixup``
173program has been loaded, it is not possible to overwrite it unless the program which
174inserted it allows us by pinning the program and closing all of its fds pointing to it.
175
176Note that ``hid_rdesc_fixup`` can be declared as sleepable (``SEC("struct_ops.s/hid_rdesc_fixup")``).
177
178
179Developer API:
180==============
181
182Available ``struct_ops`` for HID-BPF:
183-------------------------------------
184
185.. kernel-doc:: include/linux/hid_bpf.h
186   :identifiers: hid_bpf_ops
187
188
189User API data structures available in programs:
190-----------------------------------------------
191
192.. kernel-doc:: include/linux/hid_bpf.h
193   :identifiers: hid_bpf_ctx
194
195Available API that can be used in all HID-BPF struct_ops programs:
196------------------------------------------------------------------
197
198.. kernel-doc:: drivers/hid/bpf/hid_bpf_dispatch.c
199   :identifiers: hid_bpf_get_data
200
201Available API that can be used in syscall HID-BPF programs or in sleepable HID-BPF struct_ops programs:
202-------------------------------------------------------------------------------------------------------
203
204.. kernel-doc:: drivers/hid/bpf/hid_bpf_dispatch.c
205   :identifiers: hid_bpf_hw_request hid_bpf_hw_output_report hid_bpf_input_report hid_bpf_try_input_report hid_bpf_allocate_context hid_bpf_release_context
206
207General overview of a HID-BPF program
208=====================================
209
210Accessing the data attached to the context
211------------------------------------------
212
213The ``struct hid_bpf_ctx`` doesn't export the ``data`` fields directly and to access
214it, a bpf program needs to first call :c:func:`hid_bpf_get_data`.
215
216``offset`` can be any integer, but ``size`` needs to be constant, known at compile
217time.
218
219This allows the following:
220
2211. for a given device, if we know that the report length will always be of a certain value,
222   we can request the ``data`` pointer to point at the full report length.
223
224   The kernel will ensure we are using a correct size and offset and eBPF will ensure
225   the code will not attempt to read or write outside of the boundaries::
226
227     __u8 *data = hid_bpf_get_data(ctx, 0 /* offset */, 256 /* size */);
228
229     if (!data)
230         return 0; /* ensure data is correct, now the verifier knows we
231                    * have 256 bytes available */
232
233     bpf_printk("hello world: %02x %02x %02x", data[0], data[128], data[255]);
234
2352. if the report length is variable, but we know the value of ``X`` is always a 16-bit
236   integer, we can then have a pointer to that value only::
237
238      __u16 *x = hid_bpf_get_data(ctx, offset, sizeof(*x));
239
240      if (!x)
241          return 0; /* something went wrong */
242
243      *x += 1; /* increment X by one */
244
245Effect of a HID-BPF program
246---------------------------
247
248For all HID-BPF attachment types except for :c:func:`hid_rdesc_fixup`, several eBPF
249programs can be attached to the same device. If a HID-BPF struct_ops has a
250:c:func:`hid_rdesc_fixup` while another is already attached to the device, the
251kernel will return `-EINVAL` when attaching the struct_ops.
252
253Unless ``BPF_F_BEFORE`` is added to the flags while attaching the program, the new
254program is appended at the end of the list.
255``BPF_F_BEFORE`` will insert the new program at the beginning of the list which is
256useful for e.g. tracing where we need to get the unprocessed events from the device.
257
258Note that if there are multiple programs using the ``BPF_F_BEFORE`` flag,
259only the most recently loaded one is actually the first in the list.
260
261``SEC("struct_ops/hid_device_event")``
262~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
263
264Whenever a matching event is raised, the eBPF programs are called one after the other
265and are working on the same data buffer.
266
267If a program changes the data associated with the context, the next one will see
268the modified data but it will have *no* idea of what the original data was.
269
270Once all the programs are run and return ``0`` or a positive value, the rest of the
271HID stack will work on the modified data, with the ``size`` field of the last hid_bpf_ctx
272being the new size of the input stream of data.
273
274A BPF program returning a negative error discards the event, i.e. this event will not be
275processed by the HID stack. Clients (hidraw, input, LEDs) will **not** see this event.
276
277``SEC("syscall")``
278~~~~~~~~~~~~~~~~~~
279
280``syscall`` are not attached to a given device. To tell which device we are working
281with, userspace needs to refer to the device by its unique system id (the last 4 numbers
282in the sysfs path: ``/sys/bus/hid/devices/xxxx:yyyy:zzzz:0000``).
283
284To retrieve a context associated with the device, the program must call
285hid_bpf_allocate_context() and must release it with hid_bpf_release_context()
286before returning.
287Once the context is retrieved, one can also request a pointer to kernel memory with
288hid_bpf_get_data(). This memory is big enough to support all input/output/feature
289reports of the given device.
290
291``SEC("struct_ops/hid_rdesc_fixup")``
292~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
293
294The ``hid_rdesc_fixup`` program works in a similar manner to ``.report_fixup``
295of ``struct hid_driver``.
296
297When the device is probed, the kernel sets the data buffer of the context with the
298content of the report descriptor. The memory associated with that buffer is
299``HID_MAX_DESCRIPTOR_SIZE`` (currently 4kB).
300
301The eBPF program can modify the data buffer at-will and the kernel uses the
302modified content and size as the report descriptor.
303
304Whenever a struct_ops containing a ``SEC("struct_ops/hid_rdesc_fixup")`` program
305is attached (if no program was attached before), the kernel immediately disconnects
306the HID device and does a reprobe.
307
308In the same way, when this struct_ops is detached, the kernel issues a disconnect
309on the device.
310
311There is no ``detach`` facility in HID-BPF. Detaching a program happens when
312all the user space file descriptors pointing at a HID-BPF struct_ops link are closed.
313Thus, if we need to replace a report descriptor fixup, some cooperation is
314required from the owner of the original report descriptor fixup.
315The previous owner will likely pin the struct_ops link in the bpffs, and we can then
316replace it through normal bpf operations.
317
318Attaching a bpf program to a device
319===================================
320
321We now use standard struct_ops attachment through ``bpf_map__attach_struct_ops()``.
322But given that we need to attach a struct_ops to a dedicated HID device, the caller
323must set ``hid_id`` in the struct_ops map before loading the program in the kernel.
324
325``hid_id`` is the unique system ID of the HID device (the last 4 numbers in the
326sysfs path: ``/sys/bus/hid/devices/xxxx:yyyy:zzzz:0000``)
327
328One can also set ``flags``, which is of type ``enum hid_bpf_attach_flags``.
329
330We can not rely on hidraw to bind a BPF program to a HID device. hidraw is an
331artefact of the processing of the HID device, and is not stable. Some drivers
332even disable it, so that removes the tracing capabilities on those devices
333(where it is interesting to get the non-hidraw traces).
334
335On the other hand, the ``hid_id`` is stable for the entire life of the HID device,
336even if we change its report descriptor.
337
338Given that hidraw is not stable when the device disconnects/reconnects, we recommend
339accessing the current report descriptor of the device through the sysfs.
340This is available at ``/sys/bus/hid/devices/BUS:VID:PID.000N/report_descriptor`` as a
341binary stream.
342
343Parsing the report descriptor is the responsibility of the BPF programmer or the userspace
344component that loads the eBPF program.
345
346An (almost) complete example of a BPF enhanced HID device
347=========================================================
348
349*Foreword: for most parts, this could be implemented as a kernel driver*
350
351Let's imagine we have a new tablet device that has some haptic capabilities
352to simulate the surface the user is scratching on. This device would also have
353a specific 3 positions switch to toggle between *pencil on paper*, *cray on a wall*
354and *brush on a painting canvas*. To make things even better, we can control the
355physical position of the switch through a feature report.
356
357And of course, the switch is relying on some userspace component to control the
358haptic feature of the device itself.
359
360Filtering events
361----------------
362
363The first step consists in filtering events from the device. Given that the switch
364position is actually reported in the flow of the pen events, using hidraw to implement
365that filtering would mean that we wake up userspace for every single event.
366
367This is OK for libinput, but having an external library that is just interested in
368one byte in the report is less than ideal.
369
370For that, we can create a basic skeleton for our BPF program::
371
372  #include "vmlinux.h"
373  #include <bpf/bpf_helpers.h>
374  #include <bpf/bpf_tracing.h>
375
376  /* HID programs need to be GPL */
377  char _license[] SEC("license") = "GPL";
378
379  /* HID-BPF kfunc API definitions */
380  extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx,
381			      unsigned int offset,
382			      const size_t __sz) __ksym;
383
384  struct {
385	__uint(type, BPF_MAP_TYPE_RINGBUF);
386	__uint(max_entries, 4096 * 64);
387  } ringbuf SEC(".maps");
388
389  __u8 current_value = 0;
390
391  SEC("struct_ops/hid_device_event")
392  int BPF_PROG(filter_switch, struct hid_bpf_ctx *hid_ctx)
393  {
394	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 192 /* size */);
395	__u8 *buf;
396
397	if (!data)
398		return 0; /* EPERM check */
399
400	if (current_value != data[152]) {
401		buf = bpf_ringbuf_reserve(&ringbuf, 1, 0);
402		if (!buf)
403			return 0;
404
405		*buf = data[152];
406
407		bpf_ringbuf_commit(buf, 0);
408
409		current_value = data[152];
410	}
411
412	return 0;
413  }
414
415  SEC(".struct_ops.link")
416  struct hid_bpf_ops haptic_tablet = {
417  	.hid_device_event = (void *)filter_switch,
418  };
419
420
421To attach ``haptic_tablet``, userspace needs to set ``hid_id`` first::
422
423  static int attach_filter(struct hid *hid_skel, int hid_id)
424  {
425  	int err, link_fd;
426
427  	hid_skel->struct_ops.haptic_tablet->hid_id = hid_id;
428  	err = hid__load(skel);
429  	if (err)
430  		return err;
431
432  	link_fd = bpf_map__attach_struct_ops(hid_skel->maps.haptic_tablet);
433  	if (!link_fd) {
434  		fprintf(stderr, "can not attach HID-BPF program: %m\n");
435  		return -1;
436  	}
437
438  	return link_fd; /* the fd of the created bpf_link */
439  }
440
441Our userspace program can now listen to notifications on the ring buffer, and
442is awaken only when the value changes.
443
444When the userspace program doesn't need to listen to events anymore, it can just
445close the returned bpf link from :c:func:`attach_filter`, which will tell the kernel to
446detach the program from the HID device.
447
448Of course, in other use cases, the userspace program can also pin the fd to the
449BPF filesystem through a call to :c:func:`bpf_obj_pin`, as with any bpf_link.
450
451Controlling the device
452----------------------
453
454To be able to change the haptic feedback from the tablet, the userspace program
455needs to emit a feature report on the device itself.
456
457Instead of using hidraw for that, we can create a ``SEC("syscall")`` program
458that talks to the device::
459
460  /* some more HID-BPF kfunc API definitions */
461  extern struct hid_bpf_ctx *hid_bpf_allocate_context(unsigned int hid_id) __ksym;
462  extern void hid_bpf_release_context(struct hid_bpf_ctx *ctx) __ksym;
463  extern int hid_bpf_hw_request(struct hid_bpf_ctx *ctx,
464			      __u8* data,
465			      size_t len,
466			      enum hid_report_type type,
467			      enum hid_class_request reqtype) __ksym;
468
469
470  struct hid_send_haptics_args {
471	/* data needs to come at offset 0 so we can do a memcpy into it */
472	__u8 data[10];
473	unsigned int hid;
474  };
475
476  SEC("syscall")
477  int send_haptic(struct hid_send_haptics_args *args)
478  {
479	struct hid_bpf_ctx *ctx;
480	int ret = 0;
481
482	ctx = hid_bpf_allocate_context(args->hid);
483	if (!ctx)
484		return 0; /* EPERM check */
485
486	ret = hid_bpf_hw_request(ctx,
487				 args->data,
488				 10,
489				 HID_FEATURE_REPORT,
490				 HID_REQ_SET_REPORT);
491
492	hid_bpf_release_context(ctx);
493
494	return ret;
495  }
496
497And then userspace needs to call that program directly::
498
499  static int set_haptic(struct hid *hid_skel, int hid_id, __u8 haptic_value)
500  {
501	int err, prog_fd;
502	int ret = -1;
503	struct hid_send_haptics_args args = {
504		.hid = hid_id,
505	};
506	DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs,
507		.ctx_in = &args,
508		.ctx_size_in = sizeof(args),
509	);
510
511	args.data[0] = 0x02; /* report ID of the feature on our device */
512	args.data[1] = haptic_value;
513
514	prog_fd = bpf_program__fd(hid_skel->progs.set_haptic);
515
516	err = bpf_prog_test_run_opts(prog_fd, &tattrs);
517	return err;
518  }
519
520Now our userspace program is aware of the haptic state and can control it. The
521program could make this state further available to other userspace programs
522(e.g. via a DBus API).
523
524The interesting bit here is that we did not created a new kernel API for this.
525Which means that if there is a bug in our implementation, we can change the
526interface with the kernel at-will, because the userspace application is
527responsible for its own usage.
528