xref: /linux/tools/testing/selftests/hid/progs/hid.c (revision db624e82c55f227b84ac9ebfa3de2f6f5fad666b)
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 static const __u8 rdesc[] = {
129 	0x05, 0x01,				/* USAGE_PAGE (Generic Desktop) */
130 	0x09, 0x32,				/* USAGE (Z) */
131 	0x95, 0x01,				/* REPORT_COUNT (1) */
132 	0x81, 0x06,				/* INPUT (Data,Var,Rel) */
133 
134 	0x06, 0x00, 0xff,			/* Usage Page (Vendor Defined Page 1) */
135 	0x19, 0x01,				/* USAGE_MINIMUM (1) */
136 	0x29, 0x03,				/* USAGE_MAXIMUM (3) */
137 	0x15, 0x00,				/* LOGICAL_MINIMUM (0) */
138 	0x25, 0x01,				/* LOGICAL_MAXIMUM (1) */
139 	0x95, 0x03,				/* REPORT_COUNT (3) */
140 	0x75, 0x01,				/* REPORT_SIZE (1) */
141 	0x91, 0x02,				/* Output (Data,Var,Abs) */
142 	0x95, 0x01,				/* REPORT_COUNT (1) */
143 	0x75, 0x05,				/* REPORT_SIZE (5) */
144 	0x91, 0x01,				/* Output (Cnst,Var,Abs) */
145 
146 	0x06, 0x00, 0xff,			/* Usage Page (Vendor Defined Page 1) */
147 	0x19, 0x06,				/* USAGE_MINIMUM (6) */
148 	0x29, 0x08,				/* USAGE_MAXIMUM (8) */
149 	0x15, 0x00,				/* LOGICAL_MINIMUM (0) */
150 	0x25, 0x01,				/* LOGICAL_MAXIMUM (1) */
151 	0x95, 0x03,				/* REPORT_COUNT (3) */
152 	0x75, 0x01,				/* REPORT_SIZE (1) */
153 	0xb1, 0x02,				/* Feature (Data,Var,Abs) */
154 	0x95, 0x01,				/* REPORT_COUNT (1) */
155 	0x75, 0x05,				/* REPORT_SIZE (5) */
156 	0x91, 0x01,				/* Output (Cnst,Var,Abs) */
157 
158 	0xc0,				/* END_COLLECTION */
159 	0xc0,			/* END_COLLECTION */
160 };
161 
162 SEC("?fmod_ret/hid_bpf_rdesc_fixup")
163 int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hid_ctx)
164 {
165 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4096 /* size */);
166 
167 	if (!data)
168 		return 0; /* EPERM check */
169 
170 	callback2_check = data[4];
171 
172 	/* insert rdesc at offset 73 */
173 	__builtin_memcpy(&data[73], rdesc, sizeof(rdesc));
174 
175 	/* Change Usage Vendor globally */
176 	data[4] = 0x42;
177 
178 	return sizeof(rdesc) + 73;
179 }
180 
181 SEC("?fmod_ret/hid_bpf_device_event")
182 int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx)
183 {
184 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
185 
186 	if (!data)
187 		return 0; /* EPERM check */
188 
189 	/* we need to be run first */
190 	if (data[2] || data[3])
191 		return -1;
192 
193 	data[1] = 1;
194 
195 	return 0;
196 }
197 
198 SEC("?fmod_ret/hid_bpf_device_event")
199 int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx)
200 {
201 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
202 
203 	if (!data)
204 		return 0; /* EPERM check */
205 
206 	/* after insert0 and before insert2 */
207 	if (!data[1] || data[3])
208 		return -1;
209 
210 	data[2] = 2;
211 
212 	return 0;
213 }
214 
215 SEC("?fmod_ret/hid_bpf_device_event")
216 int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx)
217 {
218 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
219 
220 	if (!data)
221 		return 0; /* EPERM check */
222 
223 	/* at the end */
224 	if (!data[1] || !data[2])
225 		return -1;
226 
227 	data[3] = 3;
228 
229 	return 0;
230 }
231