1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2022 Red Hat */ 3 #include "hid.skel.h" 4 5 #include "../kselftest_harness.h" 6 7 #include <bpf/bpf.h> 8 #include <fcntl.h> 9 #include <fnmatch.h> 10 #include <dirent.h> 11 #include <poll.h> 12 #include <pthread.h> 13 #include <stdbool.h> 14 #include <linux/hidraw.h> 15 #include <linux/uhid.h> 16 17 #define SHOW_UHID_DEBUG 0 18 19 static unsigned char rdesc[] = { 20 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ 21 0x09, 0x21, /* Usage (Vendor Usage 0x21) */ 22 0xa1, 0x01, /* COLLECTION (Application) */ 23 0x09, 0x01, /* Usage (Vendor Usage 0x01) */ 24 0xa1, 0x00, /* COLLECTION (Physical) */ 25 0x85, 0x02, /* REPORT_ID (2) */ 26 0x19, 0x01, /* USAGE_MINIMUM (1) */ 27 0x29, 0x08, /* USAGE_MAXIMUM (3) */ 28 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 29 0x25, 0xff, /* LOGICAL_MAXIMUM (255) */ 30 0x95, 0x08, /* REPORT_COUNT (8) */ 31 0x75, 0x08, /* REPORT_SIZE (8) */ 32 0x81, 0x02, /* INPUT (Data,Var,Abs) */ 33 0xc0, /* END_COLLECTION */ 34 0x09, 0x01, /* Usage (Vendor Usage 0x01) */ 35 0xa1, 0x00, /* COLLECTION (Physical) */ 36 0x85, 0x01, /* REPORT_ID (1) */ 37 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ 38 0x19, 0x01, /* USAGE_MINIMUM (1) */ 39 0x29, 0x03, /* USAGE_MAXIMUM (3) */ 40 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 41 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 42 0x95, 0x03, /* REPORT_COUNT (3) */ 43 0x75, 0x01, /* REPORT_SIZE (1) */ 44 0x81, 0x02, /* INPUT (Data,Var,Abs) */ 45 0x95, 0x01, /* REPORT_COUNT (1) */ 46 0x75, 0x05, /* REPORT_SIZE (5) */ 47 0x81, 0x01, /* INPUT (Cnst,Var,Abs) */ 48 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ 49 0x09, 0x30, /* USAGE (X) */ 50 0x09, 0x31, /* USAGE (Y) */ 51 0x15, 0x81, /* LOGICAL_MINIMUM (-127) */ 52 0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */ 53 0x75, 0x10, /* REPORT_SIZE (16) */ 54 0x95, 0x02, /* REPORT_COUNT (2) */ 55 0x81, 0x06, /* INPUT (Data,Var,Rel) */ 56 57 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ 58 0x19, 0x01, /* USAGE_MINIMUM (1) */ 59 0x29, 0x03, /* USAGE_MAXIMUM (3) */ 60 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 61 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 62 0x95, 0x03, /* REPORT_COUNT (3) */ 63 0x75, 0x01, /* REPORT_SIZE (1) */ 64 0x91, 0x02, /* Output (Data,Var,Abs) */ 65 0x95, 0x01, /* REPORT_COUNT (1) */ 66 0x75, 0x05, /* REPORT_SIZE (5) */ 67 0x91, 0x01, /* Output (Cnst,Var,Abs) */ 68 69 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ 70 0x19, 0x06, /* USAGE_MINIMUM (6) */ 71 0x29, 0x08, /* USAGE_MAXIMUM (8) */ 72 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 73 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 74 0x95, 0x03, /* REPORT_COUNT (3) */ 75 0x75, 0x01, /* REPORT_SIZE (1) */ 76 0xb1, 0x02, /* Feature (Data,Var,Abs) */ 77 0x95, 0x01, /* REPORT_COUNT (1) */ 78 0x75, 0x05, /* REPORT_SIZE (5) */ 79 0x91, 0x01, /* Output (Cnst,Var,Abs) */ 80 81 0xc0, /* END_COLLECTION */ 82 0xc0, /* END_COLLECTION */ 83 }; 84 85 static __u8 feature_data[] = { 1, 2 }; 86 87 struct attach_prog_args { 88 int prog_fd; 89 unsigned int hid; 90 int retval; 91 }; 92 93 struct hid_hw_request_syscall_args { 94 __u8 data[10]; 95 unsigned int hid; 96 int retval; 97 size_t size; 98 enum hid_report_type type; 99 __u8 request_type; 100 }; 101 102 #define ASSERT_OK(data) ASSERT_FALSE(data) 103 #define ASSERT_OK_PTR(ptr) ASSERT_NE(NULL, ptr) 104 105 #define UHID_LOG(fmt, ...) do { \ 106 if (SHOW_UHID_DEBUG) \ 107 TH_LOG(fmt, ##__VA_ARGS__); \ 108 } while (0) 109 110 static pthread_mutex_t uhid_started_mtx = PTHREAD_MUTEX_INITIALIZER; 111 static pthread_cond_t uhid_started = PTHREAD_COND_INITIALIZER; 112 113 /* no need to protect uhid_stopped, only one thread accesses it */ 114 static bool uhid_stopped; 115 116 static int uhid_write(struct __test_metadata *_metadata, int fd, const struct uhid_event *ev) 117 { 118 ssize_t ret; 119 120 ret = write(fd, ev, sizeof(*ev)); 121 if (ret < 0) { 122 TH_LOG("Cannot write to uhid: %m"); 123 return -errno; 124 } else if (ret != sizeof(*ev)) { 125 TH_LOG("Wrong size written to uhid: %zd != %zu", 126 ret, sizeof(ev)); 127 return -EFAULT; 128 } else { 129 return 0; 130 } 131 } 132 133 static int uhid_create(struct __test_metadata *_metadata, int fd, int rand_nb) 134 { 135 struct uhid_event ev; 136 char buf[25]; 137 138 sprintf(buf, "test-uhid-device-%d", rand_nb); 139 140 memset(&ev, 0, sizeof(ev)); 141 ev.type = UHID_CREATE; 142 strcpy((char *)ev.u.create.name, buf); 143 ev.u.create.rd_data = rdesc; 144 ev.u.create.rd_size = sizeof(rdesc); 145 ev.u.create.bus = BUS_USB; 146 ev.u.create.vendor = 0x0001; 147 ev.u.create.product = 0x0a37; 148 ev.u.create.version = 0; 149 ev.u.create.country = 0; 150 151 sprintf(buf, "%d", rand_nb); 152 strcpy((char *)ev.u.create.phys, buf); 153 154 return uhid_write(_metadata, fd, &ev); 155 } 156 157 static void uhid_destroy(struct __test_metadata *_metadata, int fd) 158 { 159 struct uhid_event ev; 160 161 memset(&ev, 0, sizeof(ev)); 162 ev.type = UHID_DESTROY; 163 164 uhid_write(_metadata, fd, &ev); 165 } 166 167 static int uhid_event(struct __test_metadata *_metadata, int fd) 168 { 169 struct uhid_event ev, answer; 170 ssize_t ret; 171 172 memset(&ev, 0, sizeof(ev)); 173 ret = read(fd, &ev, sizeof(ev)); 174 if (ret == 0) { 175 UHID_LOG("Read HUP on uhid-cdev"); 176 return -EFAULT; 177 } else if (ret < 0) { 178 UHID_LOG("Cannot read uhid-cdev: %m"); 179 return -errno; 180 } else if (ret != sizeof(ev)) { 181 UHID_LOG("Invalid size read from uhid-dev: %zd != %zu", 182 ret, sizeof(ev)); 183 return -EFAULT; 184 } 185 186 switch (ev.type) { 187 case UHID_START: 188 pthread_mutex_lock(&uhid_started_mtx); 189 pthread_cond_signal(&uhid_started); 190 pthread_mutex_unlock(&uhid_started_mtx); 191 192 UHID_LOG("UHID_START from uhid-dev"); 193 break; 194 case UHID_STOP: 195 uhid_stopped = true; 196 197 UHID_LOG("UHID_STOP from uhid-dev"); 198 break; 199 case UHID_OPEN: 200 UHID_LOG("UHID_OPEN from uhid-dev"); 201 break; 202 case UHID_CLOSE: 203 UHID_LOG("UHID_CLOSE from uhid-dev"); 204 break; 205 case UHID_OUTPUT: 206 UHID_LOG("UHID_OUTPUT from uhid-dev"); 207 break; 208 case UHID_GET_REPORT: 209 UHID_LOG("UHID_GET_REPORT from uhid-dev"); 210 211 answer.type = UHID_GET_REPORT_REPLY; 212 answer.u.get_report_reply.id = ev.u.get_report.id; 213 answer.u.get_report_reply.err = ev.u.get_report.rnum == 1 ? 0 : -EIO; 214 answer.u.get_report_reply.size = sizeof(feature_data); 215 memcpy(answer.u.get_report_reply.data, feature_data, sizeof(feature_data)); 216 217 uhid_write(_metadata, fd, &answer); 218 219 break; 220 case UHID_SET_REPORT: 221 UHID_LOG("UHID_SET_REPORT from uhid-dev"); 222 break; 223 default: 224 TH_LOG("Invalid event from uhid-dev: %u", ev.type); 225 } 226 227 return 0; 228 } 229 230 struct uhid_thread_args { 231 int fd; 232 struct __test_metadata *_metadata; 233 }; 234 static void *uhid_read_events_thread(void *arg) 235 { 236 struct uhid_thread_args *args = (struct uhid_thread_args *)arg; 237 struct __test_metadata *_metadata = args->_metadata; 238 struct pollfd pfds[1]; 239 int fd = args->fd; 240 int ret = 0; 241 242 pfds[0].fd = fd; 243 pfds[0].events = POLLIN; 244 245 uhid_stopped = false; 246 247 while (!uhid_stopped) { 248 ret = poll(pfds, 1, 100); 249 if (ret < 0) { 250 TH_LOG("Cannot poll for fds: %m"); 251 break; 252 } 253 if (pfds[0].revents & POLLIN) { 254 ret = uhid_event(_metadata, fd); 255 if (ret) 256 break; 257 } 258 } 259 260 return (void *)(long)ret; 261 } 262 263 static int uhid_start_listener(struct __test_metadata *_metadata, pthread_t *tid, int uhid_fd) 264 { 265 struct uhid_thread_args args = { 266 .fd = uhid_fd, 267 ._metadata = _metadata, 268 }; 269 int err; 270 271 pthread_mutex_lock(&uhid_started_mtx); 272 err = pthread_create(tid, NULL, uhid_read_events_thread, (void *)&args); 273 ASSERT_EQ(0, err) { 274 TH_LOG("Could not start the uhid thread: %d", err); 275 pthread_mutex_unlock(&uhid_started_mtx); 276 close(uhid_fd); 277 return -EIO; 278 } 279 pthread_cond_wait(&uhid_started, &uhid_started_mtx); 280 pthread_mutex_unlock(&uhid_started_mtx); 281 282 return 0; 283 } 284 285 static int uhid_send_event(struct __test_metadata *_metadata, int fd, __u8 *buf, size_t size) 286 { 287 struct uhid_event ev; 288 289 if (size > sizeof(ev.u.input.data)) 290 return -E2BIG; 291 292 memset(&ev, 0, sizeof(ev)); 293 ev.type = UHID_INPUT2; 294 ev.u.input2.size = size; 295 296 memcpy(ev.u.input2.data, buf, size); 297 298 return uhid_write(_metadata, fd, &ev); 299 } 300 301 static int setup_uhid(struct __test_metadata *_metadata, int rand_nb) 302 { 303 int fd; 304 const char *path = "/dev/uhid"; 305 int ret; 306 307 fd = open(path, O_RDWR | O_CLOEXEC); 308 ASSERT_GE(fd, 0) TH_LOG("open uhid-cdev failed; %d", fd); 309 310 ret = uhid_create(_metadata, fd, rand_nb); 311 ASSERT_EQ(0, ret) { 312 TH_LOG("create uhid device failed: %d", ret); 313 close(fd); 314 } 315 316 return fd; 317 } 318 319 static bool match_sysfs_device(int dev_id, const char *workdir, struct dirent *dir) 320 { 321 const char *target = "0003:0001:0A37.*"; 322 char phys[512]; 323 char uevent[1024]; 324 char temp[512]; 325 int fd, nread; 326 bool found = false; 327 328 if (fnmatch(target, dir->d_name, 0)) 329 return false; 330 331 /* we found the correct VID/PID, now check for phys */ 332 sprintf(uevent, "%s/%s/uevent", workdir, dir->d_name); 333 334 fd = open(uevent, O_RDONLY | O_NONBLOCK); 335 if (fd < 0) 336 return false; 337 338 sprintf(phys, "PHYS=%d", dev_id); 339 340 nread = read(fd, temp, ARRAY_SIZE(temp)); 341 if (nread > 0 && (strstr(temp, phys)) != NULL) 342 found = true; 343 344 close(fd); 345 346 return found; 347 } 348 349 static int get_hid_id(int dev_id) 350 { 351 const char *workdir = "/sys/devices/virtual/misc/uhid"; 352 const char *str_id; 353 DIR *d; 354 struct dirent *dir; 355 int found = -1, attempts = 3; 356 357 /* it would be nice to be able to use nftw, but the no_alu32 target doesn't support it */ 358 359 while (found < 0 && attempts > 0) { 360 attempts--; 361 d = opendir(workdir); 362 if (d) { 363 while ((dir = readdir(d)) != NULL) { 364 if (!match_sysfs_device(dev_id, workdir, dir)) 365 continue; 366 367 str_id = dir->d_name + sizeof("0003:0001:0A37."); 368 found = (int)strtol(str_id, NULL, 16); 369 370 break; 371 } 372 closedir(d); 373 } 374 if (found < 0) 375 usleep(100000); 376 } 377 378 return found; 379 } 380 381 static int get_hidraw(int dev_id) 382 { 383 const char *workdir = "/sys/devices/virtual/misc/uhid"; 384 char sysfs[1024]; 385 DIR *d, *subd; 386 struct dirent *dir, *subdir; 387 int i, found = -1; 388 389 /* retry 5 times in case the system is loaded */ 390 for (i = 5; i > 0; i--) { 391 usleep(10); 392 d = opendir(workdir); 393 394 if (!d) 395 continue; 396 397 while ((dir = readdir(d)) != NULL) { 398 if (!match_sysfs_device(dev_id, workdir, dir)) 399 continue; 400 401 sprintf(sysfs, "%s/%s/hidraw", workdir, dir->d_name); 402 403 subd = opendir(sysfs); 404 if (!subd) 405 continue; 406 407 while ((subdir = readdir(subd)) != NULL) { 408 if (fnmatch("hidraw*", subdir->d_name, 0)) 409 continue; 410 411 found = atoi(subdir->d_name + strlen("hidraw")); 412 } 413 414 closedir(subd); 415 416 if (found > 0) 417 break; 418 } 419 closedir(d); 420 } 421 422 return found; 423 } 424 425 static int open_hidraw(int dev_id) 426 { 427 int hidraw_number; 428 char hidraw_path[64] = { 0 }; 429 430 hidraw_number = get_hidraw(dev_id); 431 if (hidraw_number < 0) 432 return hidraw_number; 433 434 /* open hidraw node to check the other side of the pipe */ 435 sprintf(hidraw_path, "/dev/hidraw%d", hidraw_number); 436 return open(hidraw_path, O_RDWR | O_NONBLOCK); 437 } 438 439 FIXTURE(hid_bpf) { 440 int dev_id; 441 int uhid_fd; 442 int hidraw_fd; 443 int hid_id; 444 pthread_t tid; 445 struct hid *skel; 446 }; 447 static void detach_bpf(FIXTURE_DATA(hid_bpf) * self) 448 { 449 if (self->hidraw_fd) 450 close(self->hidraw_fd); 451 self->hidraw_fd = 0; 452 453 hid__destroy(self->skel); 454 self->skel = NULL; 455 } 456 457 FIXTURE_TEARDOWN(hid_bpf) { 458 void *uhid_err; 459 460 uhid_destroy(_metadata, self->uhid_fd); 461 462 detach_bpf(self); 463 pthread_join(self->tid, &uhid_err); 464 } 465 #define TEARDOWN_LOG(fmt, ...) do { \ 466 TH_LOG(fmt, ##__VA_ARGS__); \ 467 hid_bpf_teardown(_metadata, self, variant); \ 468 } while (0) 469 470 FIXTURE_SETUP(hid_bpf) 471 { 472 time_t t; 473 int err; 474 475 /* initialize random number generator */ 476 srand((unsigned int)time(&t)); 477 478 self->dev_id = rand() % 1024; 479 480 self->uhid_fd = setup_uhid(_metadata, self->dev_id); 481 482 /* locate the uev, self, variant);ent file of the created device */ 483 self->hid_id = get_hid_id(self->dev_id); 484 ASSERT_GT(self->hid_id, 0) 485 TEARDOWN_LOG("Could not locate uhid device id: %d", self->hid_id); 486 487 err = uhid_start_listener(_metadata, &self->tid, self->uhid_fd); 488 ASSERT_EQ(0, err) TEARDOWN_LOG("could not start udev listener: %d", err); 489 } 490 491 struct test_program { 492 const char *name; 493 }; 494 #define LOAD_PROGRAMS(progs) \ 495 load_programs(progs, ARRAY_SIZE(progs), _metadata, self, variant) 496 #define LOAD_BPF \ 497 load_programs(NULL, 0, _metadata, self, variant) 498 static void load_programs(const struct test_program programs[], 499 const size_t progs_count, 500 struct __test_metadata *_metadata, 501 FIXTURE_DATA(hid_bpf) * self, 502 const FIXTURE_VARIANT(hid_bpf) * variant) 503 { 504 int attach_fd, err = -EINVAL; 505 struct attach_prog_args args = { 506 .retval = -1, 507 }; 508 DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattr, 509 .ctx_in = &args, 510 .ctx_size_in = sizeof(args), 511 ); 512 513 /* open the bpf file */ 514 self->skel = hid__open(); 515 ASSERT_OK_PTR(self->skel) TEARDOWN_LOG("Error while calling hid__open"); 516 517 for (int i = 0; i < progs_count; i++) { 518 struct bpf_program *prog; 519 520 prog = bpf_object__find_program_by_name(*self->skel->skeleton->obj, 521 programs[i].name); 522 ASSERT_OK_PTR(prog) TH_LOG("can not find program by name '%s'", programs[i].name); 523 524 bpf_program__set_autoload(prog, true); 525 } 526 527 err = hid__load(self->skel); 528 ASSERT_OK(err) TH_LOG("hid_skel_load failed: %d", err); 529 530 attach_fd = bpf_program__fd(self->skel->progs.attach_prog); 531 ASSERT_GE(attach_fd, 0) TH_LOG("locate attach_prog: %d", attach_fd); 532 533 for (int i = 0; i < progs_count; i++) { 534 struct bpf_program *prog; 535 536 prog = bpf_object__find_program_by_name(*self->skel->skeleton->obj, 537 programs[i].name); 538 ASSERT_OK_PTR(prog) TH_LOG("can not find program by name '%s'", programs[i].name); 539 540 args.prog_fd = bpf_program__fd(prog); 541 args.hid = self->hid_id; 542 err = bpf_prog_test_run_opts(attach_fd, &tattr); 543 ASSERT_OK(args.retval) TH_LOG("attach_hid(%s): %d", programs[i].name, args.retval); 544 } 545 546 self->hidraw_fd = open_hidraw(self->dev_id); 547 ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); 548 } 549 550 /* 551 * A simple test to see if the fixture is working fine. 552 * If this fails, none of the other tests will pass. 553 */ 554 TEST_F(hid_bpf, test_create_uhid) 555 { 556 } 557 558 /* 559 * Attach hid_first_event to the given uhid device, 560 * retrieve and open the matching hidraw node, 561 * inject one event in the uhid device, 562 * check that the program sees it and can change the data 563 */ 564 TEST_F(hid_bpf, raw_event) 565 { 566 const struct test_program progs[] = { 567 { .name = "hid_first_event" }, 568 }; 569 __u8 buf[10] = {0}; 570 int err; 571 572 LOAD_PROGRAMS(progs); 573 574 /* check that the program is correctly loaded */ 575 ASSERT_EQ(self->skel->data->callback_check, 52) TH_LOG("callback_check1"); 576 ASSERT_EQ(self->skel->data->callback2_check, 52) TH_LOG("callback2_check1"); 577 578 /* inject one event */ 579 buf[0] = 1; 580 buf[1] = 42; 581 uhid_send_event(_metadata, self->uhid_fd, buf, 6); 582 583 /* check that hid_first_event() was executed */ 584 ASSERT_EQ(self->skel->data->callback_check, 42) TH_LOG("callback_check1"); 585 586 /* read the data from hidraw */ 587 memset(buf, 0, sizeof(buf)); 588 err = read(self->hidraw_fd, buf, sizeof(buf)); 589 ASSERT_EQ(err, 6) TH_LOG("read_hidraw"); 590 ASSERT_EQ(buf[0], 1); 591 ASSERT_EQ(buf[2], 47); 592 593 /* inject another event */ 594 memset(buf, 0, sizeof(buf)); 595 buf[0] = 1; 596 buf[1] = 47; 597 uhid_send_event(_metadata, self->uhid_fd, buf, 6); 598 599 /* check that hid_first_event() was executed */ 600 ASSERT_EQ(self->skel->data->callback_check, 47) TH_LOG("callback_check1"); 601 602 /* read the data from hidraw */ 603 memset(buf, 0, sizeof(buf)); 604 err = read(self->hidraw_fd, buf, sizeof(buf)); 605 ASSERT_EQ(err, 6) TH_LOG("read_hidraw"); 606 ASSERT_EQ(buf[2], 52); 607 } 608 609 /* 610 * Ensures that we can attach/detach programs 611 */ 612 TEST_F(hid_bpf, test_attach_detach) 613 { 614 const struct test_program progs[] = { 615 { .name = "hid_first_event" }, 616 }; 617 __u8 buf[10] = {0}; 618 int err; 619 620 LOAD_PROGRAMS(progs); 621 622 /* inject one event */ 623 buf[0] = 1; 624 buf[1] = 42; 625 uhid_send_event(_metadata, self->uhid_fd, buf, 6); 626 627 /* read the data from hidraw */ 628 memset(buf, 0, sizeof(buf)); 629 err = read(self->hidraw_fd, buf, sizeof(buf)); 630 ASSERT_EQ(err, 6) TH_LOG("read_hidraw"); 631 ASSERT_EQ(buf[0], 1); 632 ASSERT_EQ(buf[2], 47); 633 634 /* pin the program and immediately unpin it */ 635 #define PIN_PATH "/sys/fs/bpf/hid_first_event" 636 bpf_program__pin(self->skel->progs.hid_first_event, PIN_PATH); 637 remove(PIN_PATH); 638 #undef PIN_PATH 639 usleep(100000); 640 641 /* detach the program */ 642 detach_bpf(self); 643 644 self->hidraw_fd = open_hidraw(self->dev_id); 645 ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); 646 647 /* inject another event */ 648 memset(buf, 0, sizeof(buf)); 649 buf[0] = 1; 650 buf[1] = 47; 651 uhid_send_event(_metadata, self->uhid_fd, buf, 6); 652 653 /* read the data from hidraw */ 654 memset(buf, 0, sizeof(buf)); 655 err = read(self->hidraw_fd, buf, sizeof(buf)); 656 ASSERT_EQ(err, 6) TH_LOG("read_hidraw_no_bpf"); 657 ASSERT_EQ(buf[0], 1); 658 ASSERT_EQ(buf[1], 47); 659 ASSERT_EQ(buf[2], 0); 660 661 /* re-attach our program */ 662 663 LOAD_PROGRAMS(progs); 664 665 /* inject one event */ 666 memset(buf, 0, sizeof(buf)); 667 buf[0] = 1; 668 buf[1] = 42; 669 uhid_send_event(_metadata, self->uhid_fd, buf, 6); 670 671 /* read the data from hidraw */ 672 memset(buf, 0, sizeof(buf)); 673 err = read(self->hidraw_fd, buf, sizeof(buf)); 674 ASSERT_EQ(err, 6) TH_LOG("read_hidraw"); 675 ASSERT_EQ(buf[0], 1); 676 ASSERT_EQ(buf[2], 47); 677 } 678 679 /* 680 * Attach hid_change_report_id to the given uhid device, 681 * retrieve and open the matching hidraw node, 682 * inject one event in the uhid device, 683 * check that the program sees it and can change the data 684 */ 685 TEST_F(hid_bpf, test_hid_change_report) 686 { 687 const struct test_program progs[] = { 688 { .name = "hid_change_report_id" }, 689 }; 690 __u8 buf[10] = {0}; 691 int err; 692 693 LOAD_PROGRAMS(progs); 694 695 /* inject one event */ 696 buf[0] = 1; 697 buf[1] = 42; 698 uhid_send_event(_metadata, self->uhid_fd, buf, 6); 699 700 /* read the data from hidraw */ 701 memset(buf, 0, sizeof(buf)); 702 err = read(self->hidraw_fd, buf, sizeof(buf)); 703 ASSERT_EQ(err, 9) TH_LOG("read_hidraw"); 704 ASSERT_EQ(buf[0], 2); 705 ASSERT_EQ(buf[1], 42); 706 ASSERT_EQ(buf[2], 0) TH_LOG("leftovers_from_previous_test"); 707 } 708 709 /* 710 * Attach hid_user_raw_request to the given uhid device, 711 * call the bpf program from userspace 712 * check that the program is called and does the expected. 713 */ 714 TEST_F(hid_bpf, test_hid_user_raw_request_call) 715 { 716 struct hid_hw_request_syscall_args args = { 717 .retval = -1, 718 .type = HID_FEATURE_REPORT, 719 .request_type = HID_REQ_GET_REPORT, 720 .size = 10, 721 }; 722 DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs, 723 .ctx_in = &args, 724 .ctx_size_in = sizeof(args), 725 ); 726 int err, prog_fd; 727 728 LOAD_BPF; 729 730 args.hid = self->hid_id; 731 args.data[0] = 1; /* report ID */ 732 733 prog_fd = bpf_program__fd(self->skel->progs.hid_user_raw_request); 734 735 err = bpf_prog_test_run_opts(prog_fd, &tattrs); 736 ASSERT_OK(err) TH_LOG("error while calling bpf_prog_test_run_opts"); 737 738 ASSERT_EQ(args.retval, 2); 739 740 ASSERT_EQ(args.data[1], 2); 741 } 742 743 static int libbpf_print_fn(enum libbpf_print_level level, 744 const char *format, va_list args) 745 { 746 char buf[1024]; 747 748 if (level == LIBBPF_DEBUG) 749 return 0; 750 751 snprintf(buf, sizeof(buf), "# %s", format); 752 753 vfprintf(stdout, buf, args); 754 return 0; 755 } 756 757 static void __attribute__((constructor)) __constructor_order_last(void) 758 { 759 if (!__constructor_order) 760 __constructor_order = _CONSTRUCTOR_ORDER_BACKWARD; 761 } 762 763 int main(int argc, char **argv) 764 { 765 /* Use libbpf 1.0 API mode */ 766 libbpf_set_strict_mode(LIBBPF_STRICT_ALL); 767 libbpf_set_print(libbpf_print_fn); 768 769 return test_harness_run(argc, argv); 770 } 771