1// SPDX-License-Identifier: GPL-2.0
2/* Copyright (c) 2022 Red hat */
3#include "hid_bpf_helpers.h"
4
5char _license[] SEC("license") = "GPL";
6
7struct 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
17SEC("?fmod_ret/hid_bpf_device_event")
18int 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
32SEC("?fmod_ret/hid_bpf_device_event")
33int 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
45SEC("?fmod_ret/hid_bpf_device_event")
46int 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
58SEC("syscall")
59int 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
68struct 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
78SEC("syscall")
79int 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
104static 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
138SEC("?fmod_ret/hid_bpf_rdesc_fixup")
139int 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
157SEC("?fmod_ret/hid_bpf_device_event")
158int 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
174SEC("?fmod_ret/hid_bpf_device_event")
175int 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
191SEC("?fmod_ret/hid_bpf_device_event")
192int 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