xref: /linux/tools/testing/selftests/hid/progs/hid.c (revision d5859510d35d8e7d63fed5169f1775317f40fb03)
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("?fmod_ret/hid_bpf_device_event")
18 int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx)
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("?fmod_ret/hid_bpf_device_event")
33 int BPF_PROG(hid_second_event, struct hid_bpf_ctx *hid_ctx)
34 {
35 	__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
36 
37 	if (!rw_data)
38 		return 0; /* EPERM check */
39 
40 	rw_data[3] = rw_data[2] + 5;
41 
42 	return hid_ctx->size;
43 }
44 
45 SEC("?fmod_ret/hid_bpf_device_event")
46 int BPF_PROG(hid_change_report_id, struct hid_bpf_ctx *hid_ctx)
47 {
48 	__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);
49 
50 	if (!rw_data)
51 		return 0; /* EPERM check */
52 
53 	rw_data[0] = 2;
54 
55 	return 9;
56 }
57 
58 SEC("syscall")
59 int attach_prog(struct attach_prog_args *ctx)
60 {
61 	ctx->retval = hid_bpf_attach_prog(ctx->hid,
62 					  ctx->prog_fd,
63 					  ctx->insert_head ? HID_BPF_FLAG_INSERT_HEAD :
64 							     HID_BPF_FLAG_NONE);
65 	return 0;
66 }
67 
68 struct hid_hw_request_syscall_args {
69 	/* data needs to come at offset 0 so we can use it in calls */
70 	__u8 data[10];
71 	unsigned int hid;
72 	int retval;
73 	size_t size;
74 	enum hid_report_type type;
75 	__u8 request_type;
76 };
77 
78 SEC("syscall")
79 int hid_user_raw_request(struct hid_hw_request_syscall_args *args)
80 {
81 	struct hid_bpf_ctx *ctx;
82 	const size_t size = args->size;
83 	int i, ret = 0;
84 
85 	if (size > sizeof(args->data))
86 		return -7; /* -E2BIG */
87 
88 	ctx = hid_bpf_allocate_context(args->hid);
89 	if (!ctx)
90 		return -1; /* EPERM check */
91 
92 	ret = hid_bpf_hw_request(ctx,
93 				 args->data,
94 				 size,
95 				 args->type,
96 				 args->request_type);
97 	args->retval = ret;
98 
99 	hid_bpf_release_context(ctx);
100 
101 	return 0;
102 }
103 
104 SEC("syscall")
105 int hid_user_output_report(struct hid_hw_request_syscall_args *args)
106 {
107 	struct hid_bpf_ctx *ctx;
108 	const size_t size = args->size;
109 	int i, ret = 0;
110 
111 	if (size > sizeof(args->data))
112 		return -7; /* -E2BIG */
113 
114 	ctx = hid_bpf_allocate_context(args->hid);
115 	if (!ctx)
116 		return -1; /* EPERM check */
117 
118 	ret = hid_bpf_hw_output_report(ctx,
119 				       args->data,
120 				       size);
121 	args->retval = ret;
122 
123 	hid_bpf_release_context(ctx);
124 
125 	return 0;
126 }
127 
128 SEC("syscall")
129 int hid_user_input_report(struct hid_hw_request_syscall_args *args)
130 {
131 	struct hid_bpf_ctx *ctx;
132 	const size_t size = args->size;
133 	int i, ret = 0;
134 
135 	if (size > sizeof(args->data))
136 		return -7; /* -E2BIG */
137 
138 	ctx = hid_bpf_allocate_context(args->hid);
139 	if (!ctx)
140 		return -1; /* EPERM check */
141 
142 	ret = hid_bpf_input_report(ctx, HID_INPUT_REPORT, args->data, size);
143 	args->retval = ret;
144 
145 	hid_bpf_release_context(ctx);
146 
147 	return 0;
148 }
149 
150 static const __u8 rdesc[] = {
151 	0x05, 0x01,				/* USAGE_PAGE (Generic Desktop) */
152 	0x09, 0x32,				/* USAGE (Z) */
153 	0x95, 0x01,				/* REPORT_COUNT (1) */
154 	0x81, 0x06,				/* INPUT (Data,Var,Rel) */
155 
156 	0x06, 0x00, 0xff,			/* Usage Page (Vendor Defined Page 1) */
157 	0x19, 0x01,				/* USAGE_MINIMUM (1) */
158 	0x29, 0x03,				/* USAGE_MAXIMUM (3) */
159 	0x15, 0x00,				/* LOGICAL_MINIMUM (0) */
160 	0x25, 0x01,				/* LOGICAL_MAXIMUM (1) */
161 	0x95, 0x03,				/* REPORT_COUNT (3) */
162 	0x75, 0x01,				/* REPORT_SIZE (1) */
163 	0x91, 0x02,				/* Output (Data,Var,Abs) */
164 	0x95, 0x01,				/* REPORT_COUNT (1) */
165 	0x75, 0x05,				/* REPORT_SIZE (5) */
166 	0x91, 0x01,				/* Output (Cnst,Var,Abs) */
167 
168 	0x06, 0x00, 0xff,			/* Usage Page (Vendor Defined Page 1) */
169 	0x19, 0x06,				/* USAGE_MINIMUM (6) */
170 	0x29, 0x08,				/* USAGE_MAXIMUM (8) */
171 	0x15, 0x00,				/* LOGICAL_MINIMUM (0) */
172 	0x25, 0x01,				/* LOGICAL_MAXIMUM (1) */
173 	0x95, 0x03,				/* REPORT_COUNT (3) */
174 	0x75, 0x01,				/* REPORT_SIZE (1) */
175 	0xb1, 0x02,				/* Feature (Data,Var,Abs) */
176 	0x95, 0x01,				/* REPORT_COUNT (1) */
177 	0x75, 0x05,				/* REPORT_SIZE (5) */
178 	0x91, 0x01,				/* Output (Cnst,Var,Abs) */
179 
180 	0xc0,				/* END_COLLECTION */
181 	0xc0,			/* END_COLLECTION */
182 };
183 
184 SEC("?fmod_ret/hid_bpf_rdesc_fixup")
185 int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hid_ctx)
186 {
187 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4096 /* size */);
188 
189 	if (!data)
190 		return 0; /* EPERM check */
191 
192 	callback2_check = data[4];
193 
194 	/* insert rdesc at offset 73 */
195 	__builtin_memcpy(&data[73], rdesc, sizeof(rdesc));
196 
197 	/* Change Usage Vendor globally */
198 	data[4] = 0x42;
199 
200 	return sizeof(rdesc) + 73;
201 }
202 
203 SEC("?fmod_ret/hid_bpf_device_event")
204 int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx)
205 {
206 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
207 
208 	if (!data)
209 		return 0; /* EPERM check */
210 
211 	/* we need to be run first */
212 	if (data[2] || data[3])
213 		return -1;
214 
215 	data[1] = 1;
216 
217 	return 0;
218 }
219 
220 SEC("?fmod_ret/hid_bpf_device_event")
221 int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx)
222 {
223 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
224 
225 	if (!data)
226 		return 0; /* EPERM check */
227 
228 	/* after insert0 and before insert2 */
229 	if (!data[1] || data[3])
230 		return -1;
231 
232 	data[2] = 2;
233 
234 	return 0;
235 }
236 
237 SEC("?fmod_ret/hid_bpf_device_event")
238 int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx)
239 {
240 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
241 
242 	if (!data)
243 		return 0; /* EPERM check */
244 
245 	/* at the end */
246 	if (!data[1] || !data[2])
247 		return -1;
248 
249 	data[3] = 3;
250 
251 	return 0;
252 }
253