1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2022 Red hat */
3 #include "vmlinux.h"
4 #include <bpf/bpf_helpers.h>
5 #include <bpf/bpf_tracing.h>
6 #include "hid_bpf_helpers.h"
7 
8 char _license[] SEC("license") = "GPL";
9 
10 struct attach_prog_args {
11 	int prog_fd;
12 	unsigned int hid;
13 	int retval;
14 	int insert_head;
15 };
16 
17 __u64 callback_check = 52;
18 __u64 callback2_check = 52;
19 
20 SEC("?fmod_ret/hid_bpf_device_event")
21 int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx)
22 {
23 	__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);
24 
25 	if (!rw_data)
26 		return 0; /* EPERM check */
27 
28 	callback_check = rw_data[1];
29 
30 	rw_data[2] = rw_data[1] + 5;
31 
32 	return hid_ctx->size;
33 }
34 
35 SEC("?fmod_ret/hid_bpf_device_event")
36 int BPF_PROG(hid_second_event, struct hid_bpf_ctx *hid_ctx)
37 {
38 	__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
39 
40 	if (!rw_data)
41 		return 0; /* EPERM check */
42 
43 	rw_data[3] = rw_data[2] + 5;
44 
45 	return hid_ctx->size;
46 }
47 
48 SEC("?fmod_ret/hid_bpf_device_event")
49 int BPF_PROG(hid_change_report_id, struct hid_bpf_ctx *hid_ctx)
50 {
51 	__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);
52 
53 	if (!rw_data)
54 		return 0; /* EPERM check */
55 
56 	rw_data[0] = 2;
57 
58 	return 9;
59 }
60 
61 SEC("syscall")
62 int attach_prog(struct attach_prog_args *ctx)
63 {
64 	ctx->retval = hid_bpf_attach_prog(ctx->hid,
65 					  ctx->prog_fd,
66 					  ctx->insert_head ? HID_BPF_FLAG_INSERT_HEAD :
67 							     HID_BPF_FLAG_NONE);
68 	return 0;
69 }
70 
71 struct hid_hw_request_syscall_args {
72 	/* data needs to come at offset 0 so we can use it in calls */
73 	__u8 data[10];
74 	unsigned int hid;
75 	int retval;
76 	size_t size;
77 	enum hid_report_type type;
78 	__u8 request_type;
79 };
80 
81 SEC("syscall")
82 int hid_user_raw_request(struct hid_hw_request_syscall_args *args)
83 {
84 	struct hid_bpf_ctx *ctx;
85 	const size_t size = args->size;
86 	int i, ret = 0;
87 
88 	if (size > sizeof(args->data))
89 		return -7; /* -E2BIG */
90 
91 	ctx = hid_bpf_allocate_context(args->hid);
92 	if (!ctx)
93 		return -1; /* EPERM check */
94 
95 	ret = hid_bpf_hw_request(ctx,
96 				 args->data,
97 				 size,
98 				 args->type,
99 				 args->request_type);
100 	args->retval = ret;
101 
102 	hid_bpf_release_context(ctx);
103 
104 	return 0;
105 }
106 
107 static const __u8 rdesc[] = {
108 	0x05, 0x01,				/* USAGE_PAGE (Generic Desktop) */
109 	0x09, 0x32,				/* USAGE (Z) */
110 	0x95, 0x01,				/* REPORT_COUNT (1) */
111 	0x81, 0x06,				/* INPUT (Data,Var,Rel) */
112 
113 	0x06, 0x00, 0xff,			/* Usage Page (Vendor Defined Page 1) */
114 	0x19, 0x01,				/* USAGE_MINIMUM (1) */
115 	0x29, 0x03,				/* USAGE_MAXIMUM (3) */
116 	0x15, 0x00,				/* LOGICAL_MINIMUM (0) */
117 	0x25, 0x01,				/* LOGICAL_MAXIMUM (1) */
118 	0x95, 0x03,				/* REPORT_COUNT (3) */
119 	0x75, 0x01,				/* REPORT_SIZE (1) */
120 	0x91, 0x02,				/* Output (Data,Var,Abs) */
121 	0x95, 0x01,				/* REPORT_COUNT (1) */
122 	0x75, 0x05,				/* REPORT_SIZE (5) */
123 	0x91, 0x01,				/* Output (Cnst,Var,Abs) */
124 
125 	0x06, 0x00, 0xff,			/* Usage Page (Vendor Defined Page 1) */
126 	0x19, 0x06,				/* USAGE_MINIMUM (6) */
127 	0x29, 0x08,				/* USAGE_MAXIMUM (8) */
128 	0x15, 0x00,				/* LOGICAL_MINIMUM (0) */
129 	0x25, 0x01,				/* LOGICAL_MAXIMUM (1) */
130 	0x95, 0x03,				/* REPORT_COUNT (3) */
131 	0x75, 0x01,				/* REPORT_SIZE (1) */
132 	0xb1, 0x02,				/* Feature (Data,Var,Abs) */
133 	0x95, 0x01,				/* REPORT_COUNT (1) */
134 	0x75, 0x05,				/* REPORT_SIZE (5) */
135 	0x91, 0x01,				/* Output (Cnst,Var,Abs) */
136 
137 	0xc0,				/* END_COLLECTION */
138 	0xc0,			/* END_COLLECTION */
139 };
140 
141 SEC("?fmod_ret/hid_bpf_rdesc_fixup")
142 int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hid_ctx)
143 {
144 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4096 /* size */);
145 
146 	if (!data)
147 		return 0; /* EPERM check */
148 
149 	callback2_check = data[4];
150 
151 	/* insert rdesc at offset 73 */
152 	__builtin_memcpy(&data[73], rdesc, sizeof(rdesc));
153 
154 	/* Change Usage Vendor globally */
155 	data[4] = 0x42;
156 
157 	return sizeof(rdesc) + 73;
158 }
159 
160 SEC("?fmod_ret/hid_bpf_device_event")
161 int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx)
162 {
163 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
164 
165 	if (!data)
166 		return 0; /* EPERM check */
167 
168 	/* we need to be run first */
169 	if (data[2] || data[3])
170 		return -1;
171 
172 	data[1] = 1;
173 
174 	return 0;
175 }
176 
177 SEC("?fmod_ret/hid_bpf_device_event")
178 int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx)
179 {
180 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
181 
182 	if (!data)
183 		return 0; /* EPERM check */
184 
185 	/* after insert0 and before insert2 */
186 	if (!data[1] || data[3])
187 		return -1;
188 
189 	data[2] = 2;
190 
191 	return 0;
192 }
193 
194 SEC("?fmod_ret/hid_bpf_device_event")
195 int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx)
196 {
197 	__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
198 
199 	if (!data)
200 		return 0; /* EPERM check */
201 
202 	/* at the end */
203 	if (!data[1] || !data[2])
204 		return -1;
205 
206 	data[3] = 3;
207 
208 	return 0;
209 }
210