xref: /linux/tools/testing/selftests/hid/progs/hid.c (revision d7696738d66b4f1379fe77eef61cd1047d7f0773)
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 SEC("?struct_ops/hid_device_event")
39 int BPF_PROG(hid_second_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
40 {
41 	__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
42 
43 	if (!rw_data)
44 		return 0; /* EPERM check */
45 
46 	rw_data[3] = rw_data[2] + 5;
47 
48 	return hid_ctx->size;
49 }
50 
51 SEC(".struct_ops.link")
52 struct hid_bpf_ops second_event = {
53 	.hid_device_event = (void *)hid_second_event,
54 };
55 
56 SEC("?struct_ops/hid_device_event")
57 int BPF_PROG(hid_change_report_id, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
58 {
59 	__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);
60 
61 	if (!rw_data)
62 		return 0; /* EPERM check */
63 
64 	rw_data[0] = 2;
65 
66 	return 9;
67 }
68 
69 SEC(".struct_ops.link")
70 struct hid_bpf_ops change_report_id = {
71 	.hid_device_event = (void *)hid_change_report_id,
72 };
73 
74 struct hid_hw_request_syscall_args {
75 	/* data needs to come at offset 0 so we can use it in calls */
76 	__u8 data[10];
77 	unsigned int hid;
78 	int retval;
79 	size_t size;
80 	enum hid_report_type type;
81 	__u8 request_type;
82 };
83 
84 SEC("syscall")
85 int hid_user_raw_request(struct hid_hw_request_syscall_args *args)
86 {
87 	struct hid_bpf_ctx *ctx;
88 	const size_t size = args->size;
89 	int i, ret = 0;
90 
91 	if (size > sizeof(args->data))
92 		return -7; /* -E2BIG */
93 
94 	ctx = hid_bpf_allocate_context(args->hid);
95 	if (!ctx)
96 		return -1; /* EPERM check */
97 
98 	ret = hid_bpf_hw_request(ctx,
99 				 args->data,
100 				 size,
101 				 args->type,
102 				 args->request_type);
103 	args->retval = ret;
104 
105 	hid_bpf_release_context(ctx);
106 
107 	return 0;
108 }
109 
110 SEC("syscall")
111 int hid_user_output_report(struct hid_hw_request_syscall_args *args)
112 {
113 	struct hid_bpf_ctx *ctx;
114 	const size_t size = args->size;
115 	int i, ret = 0;
116 
117 	if (size > sizeof(args->data))
118 		return -7; /* -E2BIG */
119 
120 	ctx = hid_bpf_allocate_context(args->hid);
121 	if (!ctx)
122 		return -1; /* EPERM check */
123 
124 	ret = hid_bpf_hw_output_report(ctx,
125 				       args->data,
126 				       size);
127 	args->retval = ret;
128 
129 	hid_bpf_release_context(ctx);
130 
131 	return 0;
132 }
133 
134 SEC("syscall")
135 int hid_user_input_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_input_report(ctx, HID_INPUT_REPORT, args->data, size);
149 	args->retval = ret;
150 
151 	hid_bpf_release_context(ctx);
152 
153 	return 0;
154 }
155 
156 static const __u8 rdesc[] = {
157 	0x05, 0x01,				/* USAGE_PAGE (Generic Desktop) */
158 	0x09, 0x32,				/* USAGE (Z) */
159 	0x95, 0x01,				/* REPORT_COUNT (1) */
160 	0x81, 0x06,				/* INPUT (Data,Var,Rel) */
161 
162 	0x06, 0x00, 0xff,			/* Usage Page (Vendor Defined Page 1) */
163 	0x19, 0x01,				/* USAGE_MINIMUM (1) */
164 	0x29, 0x03,				/* USAGE_MAXIMUM (3) */
165 	0x15, 0x00,				/* LOGICAL_MINIMUM (0) */
166 	0x25, 0x01,				/* LOGICAL_MAXIMUM (1) */
167 	0x95, 0x03,				/* REPORT_COUNT (3) */
168 	0x75, 0x01,				/* REPORT_SIZE (1) */
169 	0x91, 0x02,				/* Output (Data,Var,Abs) */
170 	0x95, 0x01,				/* REPORT_COUNT (1) */
171 	0x75, 0x05,				/* REPORT_SIZE (5) */
172 	0x91, 0x01,				/* Output (Cnst,Var,Abs) */
173 
174 	0x06, 0x00, 0xff,			/* Usage Page (Vendor Defined Page 1) */
175 	0x19, 0x06,				/* USAGE_MINIMUM (6) */
176 	0x29, 0x08,				/* USAGE_MAXIMUM (8) */
177 	0x15, 0x00,				/* LOGICAL_MINIMUM (0) */
178 	0x25, 0x01,				/* LOGICAL_MAXIMUM (1) */
179 	0x95, 0x03,				/* REPORT_COUNT (3) */
180 	0x75, 0x01,				/* REPORT_SIZE (1) */
181 	0xb1, 0x02,				/* Feature (Data,Var,Abs) */
182 	0x95, 0x01,				/* REPORT_COUNT (1) */
183 	0x75, 0x05,				/* REPORT_SIZE (5) */
184 	0x91, 0x01,				/* Output (Cnst,Var,Abs) */
185 
186 	0xc0,				/* END_COLLECTION */
187 	0xc0,			/* END_COLLECTION */
188 };
189 
190 /*
191  * the following program is marked as sleepable (struct_ops.s).
192  * This is not strictly mandatory but is a nice test for
193  * sleepable struct_ops
194  */
195 SEC("?struct_ops.s/hid_rdesc_fixup")
196 int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hid_ctx)
197 {
198 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4096 /* size */);
199 
200 	if (!data)
201 		return 0; /* EPERM check */
202 
203 	callback2_check = data[4];
204 
205 	/* insert rdesc at offset 73 */
206 	__builtin_memcpy(&data[73], rdesc, sizeof(rdesc));
207 
208 	/* Change Usage Vendor globally */
209 	data[4] = 0x42;
210 
211 	return sizeof(rdesc) + 73;
212 }
213 
214 SEC(".struct_ops.link")
215 struct hid_bpf_ops rdesc_fixup = {
216 	.hid_rdesc_fixup = (void *)hid_rdesc_fixup,
217 };
218 
219 SEC("?struct_ops/hid_device_event")
220 int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
221 {
222 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
223 
224 	if (!data)
225 		return 0; /* EPERM check */
226 
227 	/* we need to be run first */
228 	if (data[2] || data[3])
229 		return -1;
230 
231 	data[1] = 1;
232 
233 	return 0;
234 }
235 
236 SEC(".struct_ops.link")
237 struct hid_bpf_ops test_insert1 = {
238 	.hid_device_event = (void *)hid_test_insert1,
239 	.flags = BPF_F_BEFORE,
240 };
241 
242 SEC("?struct_ops/hid_device_event")
243 int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
244 {
245 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
246 
247 	if (!data)
248 		return 0; /* EPERM check */
249 
250 	/* after insert0 and before insert2 */
251 	if (!data[1] || data[3])
252 		return -1;
253 
254 	data[2] = 2;
255 
256 	return 0;
257 }
258 
259 SEC(".struct_ops.link")
260 struct hid_bpf_ops test_insert2 = {
261 	.hid_device_event = (void *)hid_test_insert2,
262 };
263 
264 SEC("?struct_ops/hid_device_event")
265 int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
266 {
267 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
268 
269 	if (!data)
270 		return 0; /* EPERM check */
271 
272 	/* at the end */
273 	if (!data[1] || !data[2])
274 		return -1;
275 
276 	data[3] = 3;
277 
278 	return 0;
279 }
280 
281 SEC(".struct_ops.link")
282 struct hid_bpf_ops test_insert3 = {
283 	.hid_device_event = (void *)hid_test_insert3,
284 };
285