Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/hid/progs/hid.c
26308 views
1
// SPDX-License-Identifier: GPL-2.0
2
/* Copyright (c) 2022 Red hat */
3
#include "hid_bpf_helpers.h"
4
5
char _license[] SEC("license") = "GPL";
6
7
struct attach_prog_args {
8
int prog_fd;
9
unsigned int hid;
10
int retval;
11
int insert_head;
12
};
13
14
__u64 callback_check = 52;
15
__u64 callback2_check = 52;
16
17
SEC("?struct_ops/hid_device_event")
18
int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
19
{
20
__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);
21
22
if (!rw_data)
23
return 0; /* EPERM check */
24
25
callback_check = rw_data[1];
26
27
rw_data[2] = rw_data[1] + 5;
28
29
return hid_ctx->size;
30
}
31
32
SEC(".struct_ops.link")
33
struct hid_bpf_ops first_event = {
34
.hid_device_event = (void *)hid_first_event,
35
.hid_id = 2,
36
};
37
38
int __hid_subprog_first_event(struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
39
{
40
__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);
41
42
if (!rw_data)
43
return 0; /* EPERM check */
44
45
rw_data[2] = rw_data[1] + 5;
46
47
return hid_ctx->size;
48
}
49
50
SEC("?struct_ops/hid_device_event")
51
int BPF_PROG(hid_subprog_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
52
{
53
return __hid_subprog_first_event(hid_ctx, type);
54
}
55
56
SEC(".struct_ops.link")
57
struct hid_bpf_ops subprog_first_event = {
58
.hid_device_event = (void *)hid_subprog_first_event,
59
.hid_id = 2,
60
};
61
62
SEC("?struct_ops/hid_device_event")
63
int BPF_PROG(hid_second_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
64
{
65
__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
66
67
if (!rw_data)
68
return 0; /* EPERM check */
69
70
rw_data[3] = rw_data[2] + 5;
71
72
return hid_ctx->size;
73
}
74
75
SEC(".struct_ops.link")
76
struct hid_bpf_ops second_event = {
77
.hid_device_event = (void *)hid_second_event,
78
};
79
80
SEC("?struct_ops/hid_device_event")
81
int BPF_PROG(hid_change_report_id, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
82
{
83
__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);
84
85
if (!rw_data)
86
return 0; /* EPERM check */
87
88
rw_data[0] = 2;
89
90
return 9;
91
}
92
93
SEC(".struct_ops.link")
94
struct hid_bpf_ops change_report_id = {
95
.hid_device_event = (void *)hid_change_report_id,
96
};
97
98
struct hid_hw_request_syscall_args {
99
/* data needs to come at offset 0 so we can use it in calls */
100
__u8 data[10];
101
unsigned int hid;
102
int retval;
103
size_t size;
104
enum hid_report_type type;
105
__u8 request_type;
106
};
107
108
SEC("syscall")
109
int hid_user_raw_request(struct hid_hw_request_syscall_args *args)
110
{
111
struct hid_bpf_ctx *ctx;
112
const size_t size = args->size;
113
int i, ret = 0;
114
115
if (size > sizeof(args->data))
116
return -7; /* -E2BIG */
117
118
ctx = hid_bpf_allocate_context(args->hid);
119
if (!ctx)
120
return -1; /* EPERM check */
121
122
ret = hid_bpf_hw_request(ctx,
123
args->data,
124
size,
125
args->type,
126
args->request_type);
127
args->retval = ret;
128
129
hid_bpf_release_context(ctx);
130
131
return 0;
132
}
133
134
SEC("syscall")
135
int hid_user_output_report(struct hid_hw_request_syscall_args *args)
136
{
137
struct hid_bpf_ctx *ctx;
138
const size_t size = args->size;
139
int i, ret = 0;
140
141
if (size > sizeof(args->data))
142
return -7; /* -E2BIG */
143
144
ctx = hid_bpf_allocate_context(args->hid);
145
if (!ctx)
146
return -1; /* EPERM check */
147
148
ret = hid_bpf_hw_output_report(ctx,
149
args->data,
150
size);
151
args->retval = ret;
152
153
hid_bpf_release_context(ctx);
154
155
return 0;
156
}
157
158
SEC("syscall")
159
int hid_user_input_report(struct hid_hw_request_syscall_args *args)
160
{
161
struct hid_bpf_ctx *ctx;
162
const size_t size = args->size;
163
int i, ret = 0;
164
165
if (size > sizeof(args->data))
166
return -7; /* -E2BIG */
167
168
ctx = hid_bpf_allocate_context(args->hid);
169
if (!ctx)
170
return -1; /* EPERM check */
171
172
ret = hid_bpf_input_report(ctx, HID_INPUT_REPORT, args->data, size);
173
args->retval = ret;
174
175
hid_bpf_release_context(ctx);
176
177
return 0;
178
}
179
180
static const __u8 rdesc[] = {
181
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
182
0x09, 0x32, /* USAGE (Z) */
183
0x95, 0x01, /* REPORT_COUNT (1) */
184
0x81, 0x06, /* INPUT (Data,Var,Rel) */
185
186
0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
187
0x19, 0x01, /* USAGE_MINIMUM (1) */
188
0x29, 0x03, /* USAGE_MAXIMUM (3) */
189
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
190
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
191
0x95, 0x03, /* REPORT_COUNT (3) */
192
0x75, 0x01, /* REPORT_SIZE (1) */
193
0x91, 0x02, /* Output (Data,Var,Abs) */
194
0x95, 0x01, /* REPORT_COUNT (1) */
195
0x75, 0x05, /* REPORT_SIZE (5) */
196
0x91, 0x01, /* Output (Cnst,Var,Abs) */
197
198
0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
199
0x19, 0x06, /* USAGE_MINIMUM (6) */
200
0x29, 0x08, /* USAGE_MAXIMUM (8) */
201
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
202
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
203
0x95, 0x03, /* REPORT_COUNT (3) */
204
0x75, 0x01, /* REPORT_SIZE (1) */
205
0xb1, 0x02, /* Feature (Data,Var,Abs) */
206
0x95, 0x01, /* REPORT_COUNT (1) */
207
0x75, 0x05, /* REPORT_SIZE (5) */
208
0x91, 0x01, /* Output (Cnst,Var,Abs) */
209
210
0xc0, /* END_COLLECTION */
211
0xc0, /* END_COLLECTION */
212
};
213
214
/*
215
* the following program is marked as sleepable (struct_ops.s).
216
* This is not strictly mandatory but is a nice test for
217
* sleepable struct_ops
218
*/
219
SEC("?struct_ops.s/hid_rdesc_fixup")
220
int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hid_ctx)
221
{
222
__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4096 /* size */);
223
224
if (!data)
225
return 0; /* EPERM check */
226
227
callback2_check = data[4];
228
229
/* insert rdesc at offset 73 */
230
__builtin_memcpy(&data[73], rdesc, sizeof(rdesc));
231
232
/* Change Usage Vendor globally */
233
data[4] = 0x42;
234
235
return sizeof(rdesc) + 73;
236
}
237
238
SEC(".struct_ops.link")
239
struct hid_bpf_ops rdesc_fixup = {
240
.hid_rdesc_fixup = (void *)hid_rdesc_fixup,
241
};
242
243
SEC("?struct_ops/hid_device_event")
244
int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
245
{
246
__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
247
248
if (!data)
249
return 0; /* EPERM check */
250
251
/* we need to be run first */
252
if (data[2] || data[3])
253
return -1;
254
255
data[1] = 1;
256
257
return 0;
258
}
259
260
SEC(".struct_ops.link")
261
struct hid_bpf_ops test_insert1 = {
262
.hid_device_event = (void *)hid_test_insert1,
263
.flags = BPF_F_BEFORE,
264
};
265
266
SEC("?struct_ops/hid_device_event")
267
int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
268
{
269
__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
270
271
if (!data)
272
return 0; /* EPERM check */
273
274
/* after insert0 and before insert2 */
275
if (!data[1] || data[3])
276
return -1;
277
278
data[2] = 2;
279
280
return 0;
281
}
282
283
SEC(".struct_ops.link")
284
struct hid_bpf_ops test_insert2 = {
285
.hid_device_event = (void *)hid_test_insert2,
286
};
287
288
SEC("?struct_ops/hid_device_event")
289
int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
290
{
291
__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
292
293
if (!data)
294
return 0; /* EPERM check */
295
296
/* at the end */
297
if (!data[1] || !data[2])
298
return -1;
299
300
data[3] = 3;
301
302
return 0;
303
}
304
305
SEC(".struct_ops.link")
306
struct hid_bpf_ops test_insert3 = {
307
.hid_device_event = (void *)hid_test_insert3,
308
};
309
310
SEC("?struct_ops/hid_hw_request")
311
int BPF_PROG(hid_test_filter_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum,
312
enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)
313
{
314
return -20;
315
}
316
317
SEC(".struct_ops.link")
318
struct hid_bpf_ops test_filter_raw_request = {
319
.hid_hw_request = (void *)hid_test_filter_raw_request,
320
};
321
322
static struct file *current_file;
323
324
SEC("fentry/hidraw_open")
325
int BPF_PROG(hidraw_open, struct inode *inode, struct file *file)
326
{
327
current_file = file;
328
return 0;
329
}
330
331
SEC("?struct_ops.s/hid_hw_request")
332
int BPF_PROG(hid_test_hidraw_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum,
333
enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)
334
{
335
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);
336
int ret;
337
338
if (!data)
339
return 0; /* EPERM check */
340
341
/* check if the incoming request comes from our hidraw operation */
342
if (source == (__u64)current_file) {
343
data[0] = reportnum;
344
345
ret = hid_bpf_hw_request(hctx, data, 2, rtype, reqtype);
346
if (ret != 2)
347
return -1;
348
data[0] = reportnum + 1;
349
data[1] = reportnum + 2;
350
data[2] = reportnum + 3;
351
return 3;
352
}
353
354
return 0;
355
}
356
357
SEC(".struct_ops.link")
358
struct hid_bpf_ops test_hidraw_raw_request = {
359
.hid_hw_request = (void *)hid_test_hidraw_raw_request,
360
};
361
362
SEC("?struct_ops.s/hid_hw_request")
363
int BPF_PROG(hid_test_infinite_loop_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum,
364
enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)
365
{
366
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);
367
int ret;
368
369
if (!data)
370
return 0; /* EPERM check */
371
372
/* always forward the request as-is to the device, hid-bpf should prevent
373
* infinite loops.
374
*/
375
data[0] = reportnum;
376
377
ret = hid_bpf_hw_request(hctx, data, 2, rtype, reqtype);
378
if (ret == 2)
379
return 3;
380
381
return 0;
382
}
383
384
SEC(".struct_ops.link")
385
struct hid_bpf_ops test_infinite_loop_raw_request = {
386
.hid_hw_request = (void *)hid_test_infinite_loop_raw_request,
387
};
388
389
SEC("?struct_ops/hid_hw_output_report")
390
int BPF_PROG(hid_test_filter_output_report, struct hid_bpf_ctx *hctx, unsigned char reportnum,
391
enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)
392
{
393
return -25;
394
}
395
396
SEC(".struct_ops.link")
397
struct hid_bpf_ops test_filter_output_report = {
398
.hid_hw_output_report = (void *)hid_test_filter_output_report,
399
};
400
401
SEC("?struct_ops.s/hid_hw_output_report")
402
int BPF_PROG(hid_test_hidraw_output_report, struct hid_bpf_ctx *hctx, __u64 source)
403
{
404
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);
405
int ret;
406
407
if (!data)
408
return 0; /* EPERM check */
409
410
/* check if the incoming request comes from our hidraw operation */
411
if (source == (__u64)current_file)
412
return hid_bpf_hw_output_report(hctx, data, 2);
413
414
return 0;
415
}
416
417
SEC(".struct_ops.link")
418
struct hid_bpf_ops test_hidraw_output_report = {
419
.hid_hw_output_report = (void *)hid_test_hidraw_output_report,
420
};
421
422
SEC("?struct_ops.s/hid_hw_output_report")
423
int BPF_PROG(hid_test_infinite_loop_output_report, struct hid_bpf_ctx *hctx, __u64 source)
424
{
425
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);
426
int ret;
427
428
if (!data)
429
return 0; /* EPERM check */
430
431
/* always forward the request as-is to the device, hid-bpf should prevent
432
* infinite loops.
433
*/
434
435
ret = hid_bpf_hw_output_report(hctx, data, 2);
436
if (ret == 2)
437
return 2;
438
439
return 0;
440
}
441
442
SEC(".struct_ops.link")
443
struct hid_bpf_ops test_infinite_loop_output_report = {
444
.hid_hw_output_report = (void *)hid_test_infinite_loop_output_report,
445
};
446
447
struct elem {
448
struct bpf_wq work;
449
};
450
451
struct {
452
__uint(type, BPF_MAP_TYPE_HASH);
453
__uint(max_entries, 1);
454
__type(key, int);
455
__type(value, struct elem);
456
} hmap SEC(".maps");
457
458
static int wq_cb_sleepable(void *map, int *key, void *work)
459
{
460
__u8 buf[9] = {2, 3, 4, 5, 6, 7, 8, 9, 10};
461
struct hid_bpf_ctx *hid_ctx;
462
463
hid_ctx = hid_bpf_allocate_context(*key);
464
if (!hid_ctx)
465
return 0; /* EPERM check */
466
467
hid_bpf_input_report(hid_ctx, HID_INPUT_REPORT, buf, sizeof(buf));
468
469
hid_bpf_release_context(hid_ctx);
470
471
return 0;
472
}
473
474
static int test_inject_input_report_callback(int *key)
475
{
476
struct elem init = {}, *val;
477
struct bpf_wq *wq;
478
479
if (bpf_map_update_elem(&hmap, key, &init, 0))
480
return -1;
481
482
val = bpf_map_lookup_elem(&hmap, key);
483
if (!val)
484
return -2;
485
486
wq = &val->work;
487
if (bpf_wq_init(wq, &hmap, 0) != 0)
488
return -3;
489
490
if (bpf_wq_set_callback(wq, wq_cb_sleepable, 0))
491
return -4;
492
493
if (bpf_wq_start(wq, 0))
494
return -5;
495
496
return 0;
497
}
498
499
SEC("?struct_ops/hid_device_event")
500
int BPF_PROG(hid_test_multiply_events_wq, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
501
{
502
__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 9 /* size */);
503
int hid = hid_ctx->hid->id;
504
int ret;
505
506
if (!data)
507
return 0; /* EPERM check */
508
509
if (data[0] != 1)
510
return 0;
511
512
ret = test_inject_input_report_callback(&hid);
513
if (ret)
514
return ret;
515
516
data[1] += 5;
517
518
return 0;
519
}
520
521
SEC(".struct_ops.link")
522
struct hid_bpf_ops test_multiply_events_wq = {
523
.hid_device_event = (void *)hid_test_multiply_events_wq,
524
};
525
526
SEC("?struct_ops/hid_device_event")
527
int BPF_PROG(hid_test_multiply_events, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
528
{
529
__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 9 /* size */);
530
__u8 buf[9];
531
int ret;
532
533
if (!data)
534
return 0; /* EPERM check */
535
536
if (data[0] != 1)
537
return 0;
538
539
/*
540
* we have to use an intermediate buffer as hid_bpf_input_report
541
* will memset data to \0
542
*/
543
__builtin_memcpy(buf, data, sizeof(buf));
544
545
buf[0] = 2;
546
buf[1] += 5;
547
ret = hid_bpf_try_input_report(hid_ctx, HID_INPUT_REPORT, buf, sizeof(buf));
548
if (ret < 0)
549
return ret;
550
551
/*
552
* In real world we should reset the original buffer as data might be garbage now,
553
* but it actually now has the content of 'buf'
554
*/
555
data[1] += 5;
556
557
return 9;
558
}
559
560
SEC(".struct_ops.link")
561
struct hid_bpf_ops test_multiply_events = {
562
.hid_device_event = (void *)hid_test_multiply_events,
563
};
564
565
SEC("?struct_ops/hid_device_event")
566
int BPF_PROG(hid_test_infinite_loop_input_report, struct hid_bpf_ctx *hctx,
567
enum hid_report_type report_type, __u64 source)
568
{
569
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 6 /* size */);
570
__u8 buf[6];
571
572
if (!data)
573
return 0; /* EPERM check */
574
575
/*
576
* we have to use an intermediate buffer as hid_bpf_input_report
577
* will memset data to \0
578
*/
579
__builtin_memcpy(buf, data, sizeof(buf));
580
581
/* always forward the request as-is to the device, hid-bpf should prevent
582
* infinite loops.
583
* the return value is ignored so the event is passing to userspace.
584
*/
585
586
hid_bpf_try_input_report(hctx, report_type, buf, sizeof(buf));
587
588
/* each time we process the event, we increment by one data[1]:
589
* after each successful call to hid_bpf_try_input_report, buf
590
* has been memcopied into data by the kernel.
591
*/
592
data[1] += 1;
593
594
return 0;
595
}
596
597
SEC(".struct_ops.link")
598
struct hid_bpf_ops test_infinite_loop_input_report = {
599
.hid_device_event = (void *)hid_test_infinite_loop_input_report,
600
};
601
602