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
8char _license[] SEC("license") = "GPL";
9
10struct 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
20SEC("?fmod_ret/hid_bpf_device_event")
21int 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
35SEC("?fmod_ret/hid_bpf_device_event")
36int 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
48SEC("?fmod_ret/hid_bpf_device_event")
49int 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
61SEC("syscall")
62int 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
71struct 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
81SEC("syscall")
82int 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
107static 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
141SEC("?fmod_ret/hid_bpf_rdesc_fixup")
142int 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
160SEC("?fmod_ret/hid_bpf_device_event")
161int 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
177SEC("?fmod_ret/hid_bpf_device_event")
178int 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
194SEC("?fmod_ret/hid_bpf_device_event")
195int 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