xref: /linux/tools/testing/selftests/hid/progs/hid.c (revision 05b3b8f19441b6bf039cec1990de3c75bb9dbbd9)
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