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