1 .. SPDX-License-Identifier: GPL-2.0 2 3 ======= 4 HID-BPF 5 ======= 6 7 HID is a standard protocol for input devices b 8 custom tweaks, traditionally done with a kerne 9 capabilities instead speeds up development and 10 existing HID interfaces. 11 12 .. contents:: 13 :local: 14 :depth: 2 15 16 17 When (and why) to use HID-BPF 18 ============================= 19 20 There are several use cases when using HID-BPF 21 than standard kernel driver fix: 22 23 Dead zone of a joystick 24 ----------------------- 25 26 Assuming you have a joystick that is getting o 27 wobbling around its neutral point. This is usu 28 level by adding a *dead zone* for this specifi 29 30 With HID-BPF, we can apply this filtering in t 31 does not get woken up when nothing else is hap 32 33 Of course, given that this dead zone is specif 34 can not create a generic fix for all of the sa 35 kernel API for this (e.g. by adding a sysfs en 36 kernel API will be broadly adopted and maintai 37 38 HID-BPF allows the userspace program to load t 39 only load the custom API when we have a user. 40 41 Simple fixup of report descriptor 42 --------------------------------- 43 44 In the HID tree, half of the drivers only fix 45 in the report descriptor. These fixes all requ 46 subsequent shepherding into a release, a long 47 48 We can reduce this burden by providing an eBPF 49 program has been verified by the user, we can 50 kernel tree and ship the eBPF program and load 51 a specific kernel module for it. 52 53 Note: distribution of eBPF programs and their 54 yet fully implemented 55 56 Add a new feature that requires a new kernel A 57 ---------------------------------------------- 58 59 An example for such a feature are the Universa 60 Basically, USI pens require a new kernel API b 61 channels of communication that our HID and inp 62 Instead of using hidraw or creating new sysfs 63 on eBPF to have the kernel API controlled by t 64 impact the performances by waking up userspace 65 event. 66 67 Morph a device into something else and control 68 ---------------------------------------------- 69 70 The kernel has a relatively static mapping of 71 It cannot decide to dynamically transform a gi 72 as it does not have the required context and a 73 undone (or even discovered) by userspace. 74 75 However, some devices are useless with that st 76 example, the Microsoft Surface Dial is a pushb 77 is barely usable as of today. 78 79 With eBPF, userspace can morph that device int 80 events into wheel events. Also, the userspace 81 feedback depending on the context. For example 82 screen we likely need to have a haptic click e 83 scrolling in a web page the user experience is 84 events at the highest resolution. 85 86 Firewall 87 -------- 88 89 What if we want to prevent other users to acce 90 device? (think a possibly broken firmware upda 91 92 With eBPF, we can intercept any HID command em 93 validate it or not. 94 95 This also allows to sync the state between the 96 kernel/bpf program because we can intercept an 97 98 Tracing 99 ------- 100 101 The last usage is tracing events and all the f 102 and analyze events. 103 104 Right now, tracing relies on hidraw. It works 105 of issues: 106 107 1. if the driver doesn't export a hidraw node, 108 (eBPF will be a "god-mode" there, so this m 109 2. hidraw doesn't catch other processes' reque 110 means that we have cases where we need to a 111 to understand what is happening. 112 113 High-level view of HID-BPF 114 ========================== 115 116 The main idea behind HID-BPF is that it works 117 Thus, all of the parsing of the HID report and 118 must be implemented in the userspace component 119 program. 120 121 For example, in the dead zone joystick from ab 122 in the data stream needs to be set to ``0`` ne 123 124 A corollary of this is that HID-BPF doesn't kn 125 available in the kernel. *You can not directly 126 input API from eBPF*. 127 128 When a BPF program needs to emit input events, 129 protocol, and rely on the HID kernel processin 130 input events. 131 132 In-tree HID-BPF programs and ``udev-hid-bpf`` 133 ============================================= 134 135 Official device fixes are shipped in the kerne 136 ``drivers/hid/bpf/progs`` directory. This allo 137 ``tools/testing/selftests/hid``. 138 139 However, the compilation of these objects is n 140 given that they need an external tool to be lo 141 `udev-hid-bpf <https://libevdev.pages.freedesk 142 143 For convenience, that external repository dupl 144 ``drivers/hid/bpf/progs`` into its own ``src/b 145 distributions to not have to pull the entire k 146 those HID-BPF fixes. ``udev-hid-bpf`` also has 147 objects files depending on the kernel the user 148 149 Available types of programs 150 =========================== 151 152 HID-BPF is built "on top" of BPF, meaning that 153 declare our programs. 154 155 HID-BPF has the following attachment types ava 156 157 1. event processing/filtering with ``SEC("stru 158 2. actions coming from userspace with ``SEC("s 159 3. change of the report descriptor with ``SEC( 160 ``SEC("struct_ops.s/hid_rdesc_fixup")`` in 161 162 A ``hid_device_event`` is calling a BPF progra 163 the device. Thus we are in IRQ context and can 164 And given that we are in IRQ context, we can n 165 166 A ``syscall`` means that userspace called the 167 This time, we can do any operations allowed by 168 allowed. 169 170 Last, ``hid_rdesc_fixup`` is different from th 171 BPF program of this type. This is called on `` 172 change the report descriptor from the BPF prog 173 program has been loaded, it is not possible to 174 inserted it allows us by pinning the program a 175 176 Note that ``hid_rdesc_fixup`` can be declared 177 178 179 Developer API: 180 ============== 181 182 Available ``struct_ops`` for HID-BPF: 183 ------------------------------------- 184 185 .. kernel-doc:: include/linux/hid_bpf.h 186 :identifiers: hid_bpf_ops 187 188 189 User API data structures available in programs 190 ---------------------------------------------- 191 192 .. kernel-doc:: include/linux/hid_bpf.h 193 :identifiers: hid_bpf_ctx 194 195 Available API that can be used in all HID-BPF 196 ---------------------------------------------- 197 198 .. kernel-doc:: drivers/hid/bpf/hid_bpf_dispat 199 :identifiers: hid_bpf_get_data 200 201 Available API that can be used in syscall HID- 202 ---------------------------------------------- 203 204 .. kernel-doc:: drivers/hid/bpf/hid_bpf_dispat 205 :identifiers: hid_bpf_hw_request hid_bpf_hw 206 207 General overview of a HID-BPF program 208 ===================================== 209 210 Accessing the data attached to the context 211 ------------------------------------------ 212 213 The ``struct hid_bpf_ctx`` doesn't export the 214 it, a bpf program needs to first call :c:func: 215 216 ``offset`` can be any integer, but ``size`` ne 217 time. 218 219 This allows the following: 220 221 1. for a given device, if we know that the rep 222 we can request the ``data`` pointer to poin 223 224 The kernel will ensure we are using a corre 225 the code will not attempt to read or write 226 227 __u8 *data = hid_bpf_get_data(ctx, 0 /* o 228 229 if (!data) 230 return 0; /* ensure data is correct, 231 * have 256 bytes available 232 233 bpf_printk("hello world: %02x %02x %02x", 234 235 2. if the report length is variable, but we kn 236 integer, we can then have a pointer to that 237 238 __u16 *x = hid_bpf_get_data(ctx, offset, 239 240 if (!x) 241 return 0; /* something went wrong */ 242 243 *x += 1; /* increment X by one */ 244 245 Effect of a HID-BPF program 246 --------------------------- 247 248 For all HID-BPF attachment types except for :c 249 programs can be attached to the same device. I 250 :c:func:`hid_rdesc_fixup` while another is alr 251 kernel will return `-EINVAL` when attaching th 252 253 Unless ``BPF_F_BEFORE`` is added to the flags 254 program is appended at the end of the list. 255 ``BPF_F_BEFORE`` will insert the new program a 256 useful for e.g. tracing where we need to get t 257 258 Note that if there are multiple programs using 259 only the most recently loaded one is actually 260 261 ``SEC("struct_ops/hid_device_event")`` 262 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 263 264 Whenever a matching event is raised, the eBPF 265 and are working on the same data buffer. 266 267 If a program changes the data associated with 268 the modified data but it will have *no* idea o 269 270 Once all the programs are run and return ``0`` 271 HID stack will work on the modified data, with 272 being the new size of the input stream of data 273 274 A BPF program returning a negative error disca 275 processed by the HID stack. Clients (hidraw, i 276 277 ``SEC("syscall")`` 278 ~~~~~~~~~~~~~~~~~~ 279 280 ``syscall`` are not attached to a given device 281 with, userspace needs to refer to the device b 282 in the sysfs path: ``/sys/bus/hid/devices/xxxx 283 284 To retrieve a context associated with the devi 285 hid_bpf_allocate_context() and must release it 286 before returning. 287 Once the context is retrieved, one can also re 288 hid_bpf_get_data(). This memory is big enough 289 reports of the given device. 290 291 ``SEC("struct_ops/hid_rdesc_fixup")`` 292 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 293 294 The ``hid_rdesc_fixup`` program works in a sim 295 of ``struct hid_driver``. 296 297 When the device is probed, the kernel sets the 298 content of the report descriptor. The memory a 299 ``HID_MAX_DESCRIPTOR_SIZE`` (currently 4kB). 300 301 The eBPF program can modify the data buffer at 302 modified content and size as the report descri 303 304 Whenever a struct_ops containing a ``SEC("stru 305 is attached (if no program was attached before 306 the HID device and does a reprobe. 307 308 In the same way, when this struct_ops is detac 309 on the device. 310 311 There is no ``detach`` facility in HID-BPF. De 312 all the user space file descriptors pointing a 313 Thus, if we need to replace a report descripto 314 required from the owner of the original report 315 The previous owner will likely pin the struct_ 316 replace it through normal bpf operations. 317 318 Attaching a bpf program to a device 319 =================================== 320 321 We now use standard struct_ops attachment thro 322 But given that we need to attach a struct_ops 323 must set ``hid_id`` in the struct_ops map befo 324 325 ``hid_id`` is the unique system ID of the HID 326 sysfs path: ``/sys/bus/hid/devices/xxxx:yyyy:z 327 328 One can also set ``flags``, which is of type ` 329 330 We can not rely on hidraw to bind a BPF progra 331 artefact of the processing of the HID device, 332 even disable it, so that removes the tracing c 333 (where it is interesting to get the non-hidraw 334 335 On the other hand, the ``hid_id`` is stable fo 336 even if we change its report descriptor. 337 338 Given that hidraw is not stable when the devic 339 accessing the current report descriptor of the 340 This is available at ``/sys/bus/hid/devices/BU 341 binary stream. 342 343 Parsing the report descriptor is the responsib 344 component that loads the eBPF program. 345 346 An (almost) complete example of a BPF enhanced 347 ============================================== 348 349 *Foreword: for most parts, this could be imple 350 351 Let's imagine we have a new tablet device that 352 to simulate the surface the user is scratching 353 a specific 3 positions switch to toggle betwee 354 and *brush on a painting canvas*. To make thin 355 physical position of the switch through a feat 356 357 And of course, the switch is relying on some u 358 haptic feature of the device itself. 359 360 Filtering events 361 ---------------- 362 363 The first step consists in filtering events fr 364 position is actually reported in the flow of t 365 that filtering would mean that we wake up user 366 367 This is OK for libinput, but having an externa 368 one byte in the report is less than ideal. 369 370 For that, we can create a basic skeleton for o 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 381 unsigned int off 382 const size_t __s 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_c 393 { 394 __u8 *data = hid_bpf_get_data(hid_ctx, 395 __u8 *buf; 396 397 if (!data) 398 return 0; /* EPERM check */ 399 400 if (current_value != data[152]) { 401 buf = bpf_ringbuf_reserve(&rin 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_swi 418 }; 419 420 421 To attach ``haptic_tablet``, userspace needs t 422 423 static int attach_filter(struct hid *hid_ske 424 { 425 int err, link_fd; 426 427 hid_skel->struct_ops.haptic_tablet->hi 428 err = hid__load(skel); 429 if (err) 430 return err; 431 432 link_fd = bpf_map__attach_struct_ops(h 433 if (!link_fd) { 434 fprintf(stderr, "can not attac 435 return -1; 436 } 437 438 return link_fd; /* the fd of the creat 439 } 440 441 Our userspace program can now listen to notifi 442 is awaken only when the value changes. 443 444 When the userspace program doesn't need to lis 445 close the returned bpf link from :c:func:`atta 446 detach the program from the HID device. 447 448 Of course, in other use cases, the userspace p 449 BPF filesystem through a call to :c:func:`bpf_ 450 451 Controlling the device 452 ---------------------- 453 454 To be able to change the haptic feedback from 455 needs to emit a feature report on the device i 456 457 Instead of using hidraw for that, we can creat 458 that talks to the device:: 459 460 /* some more HID-BPF kfunc API definitions * 461 extern struct hid_bpf_ctx *hid_bpf_allocate_ 462 extern void hid_bpf_release_context(struct h 463 extern int hid_bpf_hw_request(struct hid_bpf 464 __u8* data, 465 size_t len, 466 enum hid_report_ 467 enum hid_class_r 468 469 470 struct hid_send_haptics_args { 471 /* data needs to come at offset 0 so w 472 __u8 data[10]; 473 unsigned int hid; 474 }; 475 476 SEC("syscall") 477 int send_haptic(struct hid_send_haptics_args 478 { 479 struct hid_bpf_ctx *ctx; 480 int ret = 0; 481 482 ctx = hid_bpf_allocate_context(args->h 483 if (!ctx) 484 return 0; /* EPERM check */ 485 486 ret = hid_bpf_hw_request(ctx, 487 args->data, 488 10, 489 HID_FEATURE_R 490 HID_REQ_SET_R 491 492 hid_bpf_release_context(ctx); 493 494 return ret; 495 } 496 497 And then userspace needs to call that program 498 499 static int set_haptic(struct hid *hid_skel, 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, 507 .ctx_in = &args, 508 .ctx_size_in = sizeof(args), 509 ); 510 511 args.data[0] = 0x02; /* report ID of t 512 args.data[1] = haptic_value; 513 514 prog_fd = bpf_program__fd(hid_skel->pr 515 516 err = bpf_prog_test_run_opts(prog_fd, 517 return err; 518 } 519 520 Now our userspace program is aware of the hapt 521 program could make this state further availabl 522 (e.g. via a DBus API). 523 524 The interesting bit here is that we did not cr 525 Which means that if there is a bug in our impl 526 interface with the kernel at-will, because the 527 responsible for its own usage.
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.