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 132Available types of programs 133=========================== 134 135HID-BPF is built "on top" of BPF, meaning that we use tracing method to 136declare our programs. 137 138HID-BPF has the following attachment types available: 139 1401. event processing/filtering with ``SEC("fmod_ret/hid_bpf_device_event")`` in libbpf 1412. actions coming from userspace with ``SEC("syscall")`` in libbpf 1423. change of the report descriptor with ``SEC("fmod_ret/hid_bpf_rdesc_fixup")`` in libbpf 143 144A ``hid_bpf_device_event`` is calling a BPF program when an event is received from 145the device. Thus we are in IRQ context and can act on the data or notify userspace. 146And given that we are in IRQ context, we can not talk back to the device. 147 148A ``syscall`` means that userspace called the syscall ``BPF_PROG_RUN`` facility. 149This time, we can do any operations allowed by HID-BPF, and talking to the device is 150allowed. 151 152Last, ``hid_bpf_rdesc_fixup`` is different from the others as there can be only one 153BPF program of this type. This is called on ``probe`` from the driver and allows to 154change the report descriptor from the BPF program. Once a ``hid_bpf_rdesc_fixup`` 155program has been loaded, it is not possible to overwrite it unless the program which 156inserted it allows us by pinning the program and closing all of its fds pointing to it. 157 158Developer API: 159============== 160 161User API data structures available in programs: 162----------------------------------------------- 163 164.. kernel-doc:: include/linux/hid_bpf.h 165 166Available tracing functions to attach a HID-BPF program: 167-------------------------------------------------------- 168 169.. kernel-doc:: drivers/hid/bpf/hid_bpf_dispatch.c 170 :functions: hid_bpf_device_event hid_bpf_rdesc_fixup 171 172Available API that can be used in all HID-BPF programs: 173------------------------------------------------------- 174 175.. kernel-doc:: drivers/hid/bpf/hid_bpf_dispatch.c 176 :functions: hid_bpf_get_data 177 178Available API that can be used in syscall HID-BPF programs: 179----------------------------------------------------------- 180 181.. kernel-doc:: drivers/hid/bpf/hid_bpf_dispatch.c 182 :functions: hid_bpf_attach_prog hid_bpf_hw_request hid_bpf_allocate_context hid_bpf_release_context 183 184General overview of a HID-BPF program 185===================================== 186 187Accessing the data attached to the context 188------------------------------------------ 189 190The ``struct hid_bpf_ctx`` doesn't export the ``data`` fields directly and to access 191it, a bpf program needs to first call :c:func:`hid_bpf_get_data`. 192 193``offset`` can be any integer, but ``size`` needs to be constant, known at compile 194time. 195 196This allows the following: 197 1981. for a given device, if we know that the report length will always be of a certain value, 199 we can request the ``data`` pointer to point at the full report length. 200 201 The kernel will ensure we are using a correct size and offset and eBPF will ensure 202 the code will not attempt to read or write outside of the boundaries:: 203 204 __u8 *data = hid_bpf_get_data(ctx, 0 /* offset */, 256 /* size */); 205 206 if (!data) 207 return 0; /* ensure data is correct, now the verifier knows we 208 * have 256 bytes available */ 209 210 bpf_printk("hello world: %02x %02x %02x", data[0], data[128], data[255]); 211 2122. if the report length is variable, but we know the value of ``X`` is always a 16-bit 213 integer, we can then have a pointer to that value only:: 214 215 __u16 *x = hid_bpf_get_data(ctx, offset, sizeof(*x)); 216 217 if (!x) 218 return 0; /* something went wrong */ 219 220 *x += 1; /* increment X by one */ 221 222Effect of a HID-BPF program 223--------------------------- 224 225For all HID-BPF attachment types except for :c:func:`hid_bpf_rdesc_fixup`, several eBPF 226programs can be attached to the same device. 227 228Unless ``HID_BPF_FLAG_INSERT_HEAD`` is added to the flags while attaching the 229program, the new program is appended at the end of the list. 230``HID_BPF_FLAG_INSERT_HEAD`` will insert the new program at the beginning of the 231list which is useful for e.g. tracing where we need to get the unprocessed events 232from the device. 233 234Note that if there are multiple programs using the ``HID_BPF_FLAG_INSERT_HEAD`` flag, 235only the most recently loaded one is actually the first in the list. 236 237``SEC("fmod_ret/hid_bpf_device_event")`` 238~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 239 240Whenever a matching event is raised, the eBPF programs are called one after the other 241and are working on the same data buffer. 242 243If a program changes the data associated with the context, the next one will see 244the modified data but it will have *no* idea of what the original data was. 245 246Once all the programs are run and return ``0`` or a positive value, the rest of the 247HID stack will work on the modified data, with the ``size`` field of the last hid_bpf_ctx 248being the new size of the input stream of data. 249 250A BPF program returning a negative error discards the event, i.e. this event will not be 251processed by the HID stack. Clients (hidraw, input, LEDs) will **not** see this event. 252 253``SEC("syscall")`` 254~~~~~~~~~~~~~~~~~~ 255 256``syscall`` are not attached to a given device. To tell which device we are working 257with, userspace needs to refer to the device by its unique system id (the last 4 numbers 258in the sysfs path: ``/sys/bus/hid/devices/xxxx:yyyy:zzzz:0000``). 259 260To retrieve a context associated with the device, the program must call 261:c:func:`hid_bpf_allocate_context` and must release it with :c:func:`hid_bpf_release_context` 262before returning. 263Once the context is retrieved, one can also request a pointer to kernel memory with 264:c:func:`hid_bpf_get_data`. This memory is big enough to support all input/output/feature 265reports of the given device. 266 267``SEC("fmod_ret/hid_bpf_rdesc_fixup")`` 268~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 269 270The ``hid_bpf_rdesc_fixup`` program works in a similar manner to 271``.report_fixup`` of ``struct hid_driver``. 272 273When the device is probed, the kernel sets the data buffer of the context with the 274content of the report descriptor. The memory associated with that buffer is 275``HID_MAX_DESCRIPTOR_SIZE`` (currently 4kB). 276 277The eBPF program can modify the data buffer at-will and the kernel uses the 278modified content and size as the report descriptor. 279 280Whenever a ``SEC("fmod_ret/hid_bpf_rdesc_fixup")`` program is attached (if no 281program was attached before), the kernel immediately disconnects the HID device 282and does a reprobe. 283 284In the same way, when the ``SEC("fmod_ret/hid_bpf_rdesc_fixup")`` program is 285detached, the kernel issues a disconnect on the device. 286 287There is no ``detach`` facility in HID-BPF. Detaching a program happens when 288all the user space file descriptors pointing at a program are closed. 289Thus, if we need to replace a report descriptor fixup, some cooperation is 290required from the owner of the original report descriptor fixup. 291The previous owner will likely pin the program in the bpffs, and we can then 292replace it through normal bpf operations. 293 294Attaching a bpf program to a device 295=================================== 296 297``libbpf`` does not export any helper to attach a HID-BPF program. 298Users need to use a dedicated ``syscall`` program which will call 299``hid_bpf_attach_prog(hid_id, program_fd, flags)``. 300 301``hid_id`` is the unique system ID of the HID device (the last 4 numbers in the 302sysfs path: ``/sys/bus/hid/devices/xxxx:yyyy:zzzz:0000``) 303 304``progam_fd`` is the opened file descriptor of the program to attach. 305 306``flags`` is of type ``enum hid_bpf_attach_flags``. 307 308We can not rely on hidraw to bind a BPF program to a HID device. hidraw is an 309artefact of the processing of the HID device, and is not stable. Some drivers 310even disable it, so that removes the tracing capabilies on those devices 311(where it is interesting to get the non-hidraw traces). 312 313On the other hand, the ``hid_id`` is stable for the entire life of the HID device, 314even if we change its report descriptor. 315 316Given that hidraw is not stable when the device disconnects/reconnects, we recommend 317accessing the current report descriptor of the device through the sysfs. 318This is available at ``/sys/bus/hid/devices/BUS:VID:PID.000N/report_descriptor`` as a 319binary stream. 320 321Parsing the report descriptor is the responsibility of the BPF programmer or the userspace 322component that loads the eBPF program. 323 324An (almost) complete example of a BPF enhanced HID device 325========================================================= 326 327*Foreword: for most parts, this could be implemented as a kernel driver* 328 329Let's imagine we have a new tablet device that has some haptic capabilities 330to simulate the surface the user is scratching on. This device would also have 331a specific 3 positions switch to toggle between *pencil on paper*, *cray on a wall* 332and *brush on a painting canvas*. To make things even better, we can control the 333physical position of the switch through a feature report. 334 335And of course, the switch is relying on some userspace component to control the 336haptic feature of the device itself. 337 338Filtering events 339---------------- 340 341The first step consists in filtering events from the device. Given that the switch 342position is actually reported in the flow of the pen events, using hidraw to implement 343that filtering would mean that we wake up userspace for every single event. 344 345This is OK for libinput, but having an external library that is just interested in 346one byte in the report is less than ideal. 347 348For that, we can create a basic skeleton for our BPF program:: 349 350 #include "vmlinux.h" 351 #include <bpf/bpf_helpers.h> 352 #include <bpf/bpf_tracing.h> 353 354 /* HID programs need to be GPL */ 355 char _license[] SEC("license") = "GPL"; 356 357 /* HID-BPF kfunc API definitions */ 358 extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx, 359 unsigned int offset, 360 const size_t __sz) __ksym; 361 extern int hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, u32 flags) __ksym; 362 363 struct { 364 __uint(type, BPF_MAP_TYPE_RINGBUF); 365 __uint(max_entries, 4096 * 64); 366 } ringbuf SEC(".maps"); 367 368 struct attach_prog_args { 369 int prog_fd; 370 unsigned int hid; 371 unsigned int flags; 372 int retval; 373 }; 374 375 SEC("syscall") 376 int attach_prog(struct attach_prog_args *ctx) 377 { 378 ctx->retval = hid_bpf_attach_prog(ctx->hid, 379 ctx->prog_fd, 380 ctx->flags); 381 return 0; 382 } 383 384 __u8 current_value = 0; 385 386 SEC("?fmod_ret/hid_bpf_device_event") 387 int BPF_PROG(filter_switch, struct hid_bpf_ctx *hid_ctx) 388 { 389 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 192 /* size */); 390 __u8 *buf; 391 392 if (!data) 393 return 0; /* EPERM check */ 394 395 if (current_value != data[152]) { 396 buf = bpf_ringbuf_reserve(&ringbuf, 1, 0); 397 if (!buf) 398 return 0; 399 400 *buf = data[152]; 401 402 bpf_ringbuf_commit(buf, 0); 403 404 current_value = data[152]; 405 } 406 407 return 0; 408 } 409 410To attach ``filter_switch``, userspace needs to call the ``attach_prog`` syscall 411program first:: 412 413 static int attach_filter(struct hid *hid_skel, int hid_id) 414 { 415 int err, prog_fd; 416 int ret = -1; 417 struct attach_prog_args args = { 418 .hid = hid_id, 419 }; 420 DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs, 421 .ctx_in = &args, 422 .ctx_size_in = sizeof(args), 423 ); 424 425 args.prog_fd = bpf_program__fd(hid_skel->progs.filter_switch); 426 427 prog_fd = bpf_program__fd(hid_skel->progs.attach_prog); 428 429 err = bpf_prog_test_run_opts(prog_fd, &tattrs); 430 return err; 431 } 432 433Our userspace program can now listen to notifications on the ring buffer, and 434is awaken only when the value changes. 435 436Controlling the device 437---------------------- 438 439To be able to change the haptic feedback from the tablet, the userspace program 440needs to emit a feature report on the device itself. 441 442Instead of using hidraw for that, we can create a ``SEC("syscall")`` program 443that talks to the device:: 444 445 /* some more HID-BPF kfunc API definitions */ 446 extern struct hid_bpf_ctx *hid_bpf_allocate_context(unsigned int hid_id) __ksym; 447 extern void hid_bpf_release_context(struct hid_bpf_ctx *ctx) __ksym; 448 extern int hid_bpf_hw_request(struct hid_bpf_ctx *ctx, 449 __u8* data, 450 size_t len, 451 enum hid_report_type type, 452 enum hid_class_request reqtype) __ksym; 453 454 455 struct hid_send_haptics_args { 456 /* data needs to come at offset 0 so we can do a memcpy into it */ 457 __u8 data[10]; 458 unsigned int hid; 459 }; 460 461 SEC("syscall") 462 int send_haptic(struct hid_send_haptics_args *args) 463 { 464 struct hid_bpf_ctx *ctx; 465 int ret = 0; 466 467 ctx = hid_bpf_allocate_context(args->hid); 468 if (!ctx) 469 return 0; /* EPERM check */ 470 471 ret = hid_bpf_hw_request(ctx, 472 args->data, 473 10, 474 HID_FEATURE_REPORT, 475 HID_REQ_SET_REPORT); 476 477 hid_bpf_release_context(ctx); 478 479 return ret; 480 } 481 482And then userspace needs to call that program directly:: 483 484 static int set_haptic(struct hid *hid_skel, int hid_id, __u8 haptic_value) 485 { 486 int err, prog_fd; 487 int ret = -1; 488 struct hid_send_haptics_args args = { 489 .hid = hid_id, 490 }; 491 DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs, 492 .ctx_in = &args, 493 .ctx_size_in = sizeof(args), 494 ); 495 496 args.data[0] = 0x02; /* report ID of the feature on our device */ 497 args.data[1] = haptic_value; 498 499 prog_fd = bpf_program__fd(hid_skel->progs.set_haptic); 500 501 err = bpf_prog_test_run_opts(prog_fd, &tattrs); 502 return err; 503 } 504 505Now our userspace program is aware of the haptic state and can control it. The 506program could make this state further available to other userspace programs 507(e.g. via a DBus API). 508 509The interesting bit here is that we did not created a new kernel API for this. 510Which means that if there is a bug in our implementation, we can change the 511interface with the kernel at-will, because the userspace application is 512responsible for its own usage. 513