1f5c27da4SBenjamin Tissoires // SPDX-License-Identifier: GPL-2.0-only
2f5c27da4SBenjamin Tissoires 
3f5c27da4SBenjamin Tissoires /*
4f5c27da4SBenjamin Tissoires  *  HID-BPF support for Linux
5f5c27da4SBenjamin Tissoires  *
6f5c27da4SBenjamin Tissoires  *  Copyright (c) 2022 Benjamin Tissoires
7f5c27da4SBenjamin Tissoires  */
8f5c27da4SBenjamin Tissoires 
9f5c27da4SBenjamin Tissoires #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10f5c27da4SBenjamin Tissoires #include <linux/bitops.h>
11f5c27da4SBenjamin Tissoires #include <linux/btf.h>
12f5c27da4SBenjamin Tissoires #include <linux/btf_ids.h>
13f5c27da4SBenjamin Tissoires #include <linux/filter.h>
14f5c27da4SBenjamin Tissoires #include <linux/hid.h>
15f5c27da4SBenjamin Tissoires #include <linux/hid_bpf.h>
16f5c27da4SBenjamin Tissoires #include <linux/init.h>
17f5c27da4SBenjamin Tissoires #include <linux/kfifo.h>
18ad190df1SBenjamin Tissoires #include <linux/minmax.h>
19f5c27da4SBenjamin Tissoires #include <linux/module.h>
20f5c27da4SBenjamin Tissoires #include <linux/workqueue.h>
21f5c27da4SBenjamin Tissoires #include "hid_bpf_dispatch.h"
22f5c27da4SBenjamin Tissoires #include "entrypoints/entrypoints.lskel.h"
23f5c27da4SBenjamin Tissoires 
24f5c27da4SBenjamin Tissoires struct hid_bpf_ops *hid_bpf_ops;
25f5c27da4SBenjamin Tissoires EXPORT_SYMBOL(hid_bpf_ops);
26f5c27da4SBenjamin Tissoires 
27f5c27da4SBenjamin Tissoires /**
28f5c27da4SBenjamin Tissoires  * hid_bpf_device_event - Called whenever an event is coming in from the device
29f5c27da4SBenjamin Tissoires  *
30f5c27da4SBenjamin Tissoires  * @ctx: The HID-BPF context
31f5c27da4SBenjamin Tissoires  *
32658ee5a6SBenjamin Tissoires  * @return %0 on success and keep processing; a positive value to change the
33658ee5a6SBenjamin Tissoires  * incoming size buffer; a negative error code to interrupt the processing
34658ee5a6SBenjamin Tissoires  * of this event
35f5c27da4SBenjamin Tissoires  *
36f5c27da4SBenjamin Tissoires  * Declare an %fmod_ret tracing bpf program to this function and attach this
37f5c27da4SBenjamin Tissoires  * program through hid_bpf_attach_prog() to have this helper called for
38f5c27da4SBenjamin Tissoires  * any incoming event from the device itself.
39f5c27da4SBenjamin Tissoires  *
40f5c27da4SBenjamin Tissoires  * The function is called while on IRQ context, so we can not sleep.
41f5c27da4SBenjamin Tissoires  */
42f5c27da4SBenjamin Tissoires /* never used by the kernel but declared so we can load and attach a tracepoint */
hid_bpf_device_event(struct hid_bpf_ctx * ctx)43f5c27da4SBenjamin Tissoires __weak noinline int hid_bpf_device_event(struct hid_bpf_ctx *ctx)
44f5c27da4SBenjamin Tissoires {
45f5c27da4SBenjamin Tissoires 	return 0;
46f5c27da4SBenjamin Tissoires }
47f5c27da4SBenjamin Tissoires 
48658ee5a6SBenjamin Tissoires u8 *
dispatch_hid_bpf_device_event(struct hid_device * hdev,enum hid_report_type type,u8 * data,u32 * size,int interrupt)49f5c27da4SBenjamin Tissoires dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type, u8 *data,
50658ee5a6SBenjamin Tissoires 			      u32 *size, int interrupt)
51f5c27da4SBenjamin Tissoires {
52f5c27da4SBenjamin Tissoires 	struct hid_bpf_ctx_kern ctx_kern = {
53f5c27da4SBenjamin Tissoires 		.ctx = {
54f5c27da4SBenjamin Tissoires 			.hid = hdev,
55f5c27da4SBenjamin Tissoires 			.report_type = type,
56658ee5a6SBenjamin Tissoires 			.allocated_size = hdev->bpf.allocated_data,
57658ee5a6SBenjamin Tissoires 			.size = *size,
58f5c27da4SBenjamin Tissoires 		},
59658ee5a6SBenjamin Tissoires 		.data = hdev->bpf.device_data,
60f5c27da4SBenjamin Tissoires 	};
61658ee5a6SBenjamin Tissoires 	int ret;
62f5c27da4SBenjamin Tissoires 
63f5c27da4SBenjamin Tissoires 	if (type >= HID_REPORT_TYPES)
64658ee5a6SBenjamin Tissoires 		return ERR_PTR(-EINVAL);
65f5c27da4SBenjamin Tissoires 
66658ee5a6SBenjamin Tissoires 	/* no program has been attached yet */
67658ee5a6SBenjamin Tissoires 	if (!hdev->bpf.device_data)
68658ee5a6SBenjamin Tissoires 		return data;
69658ee5a6SBenjamin Tissoires 
70658ee5a6SBenjamin Tissoires 	memset(ctx_kern.data, 0, hdev->bpf.allocated_data);
71658ee5a6SBenjamin Tissoires 	memcpy(ctx_kern.data, data, *size);
72658ee5a6SBenjamin Tissoires 
73658ee5a6SBenjamin Tissoires 	ret = hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_DEVICE_EVENT, &ctx_kern);
74658ee5a6SBenjamin Tissoires 	if (ret < 0)
75658ee5a6SBenjamin Tissoires 		return ERR_PTR(ret);
76658ee5a6SBenjamin Tissoires 
77658ee5a6SBenjamin Tissoires 	if (ret) {
78658ee5a6SBenjamin Tissoires 		if (ret > ctx_kern.ctx.allocated_size)
79658ee5a6SBenjamin Tissoires 			return ERR_PTR(-EINVAL);
80658ee5a6SBenjamin Tissoires 
81658ee5a6SBenjamin Tissoires 		*size = ret;
82658ee5a6SBenjamin Tissoires 	}
83658ee5a6SBenjamin Tissoires 
84658ee5a6SBenjamin Tissoires 	return ctx_kern.data;
85f5c27da4SBenjamin Tissoires }
86f5c27da4SBenjamin Tissoires EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event);
87f5c27da4SBenjamin Tissoires 
88f5c27da4SBenjamin Tissoires /**
89ad190df1SBenjamin Tissoires  * hid_bpf_rdesc_fixup - Called when the probe function parses the report
90ad190df1SBenjamin Tissoires  * descriptor of the HID device
91ad190df1SBenjamin Tissoires  *
92ad190df1SBenjamin Tissoires  * @ctx: The HID-BPF context
93ad190df1SBenjamin Tissoires  *
94ad190df1SBenjamin Tissoires  * @return 0 on success and keep processing; a positive value to change the
95ad190df1SBenjamin Tissoires  * incoming size buffer; a negative error code to interrupt the processing
96ad190df1SBenjamin Tissoires  * of this event
97ad190df1SBenjamin Tissoires  *
98ad190df1SBenjamin Tissoires  * Declare an %fmod_ret tracing bpf program to this function and attach this
99ad190df1SBenjamin Tissoires  * program through hid_bpf_attach_prog() to have this helper called before any
100ad190df1SBenjamin Tissoires  * parsing of the report descriptor by HID.
101ad190df1SBenjamin Tissoires  */
102ad190df1SBenjamin Tissoires /* never used by the kernel but declared so we can load and attach a tracepoint */
hid_bpf_rdesc_fixup(struct hid_bpf_ctx * ctx)103ad190df1SBenjamin Tissoires __weak noinline int hid_bpf_rdesc_fixup(struct hid_bpf_ctx *ctx)
104ad190df1SBenjamin Tissoires {
105ad190df1SBenjamin Tissoires 	return 0;
106ad190df1SBenjamin Tissoires }
107ad190df1SBenjamin Tissoires 
call_hid_bpf_rdesc_fixup(struct hid_device * hdev,u8 * rdesc,unsigned int * size)108ad190df1SBenjamin Tissoires u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size)
109ad190df1SBenjamin Tissoires {
110ad190df1SBenjamin Tissoires 	int ret;
111ad190df1SBenjamin Tissoires 	struct hid_bpf_ctx_kern ctx_kern = {
112ad190df1SBenjamin Tissoires 		.ctx = {
113ad190df1SBenjamin Tissoires 			.hid = hdev,
114ad190df1SBenjamin Tissoires 			.size = *size,
115ad190df1SBenjamin Tissoires 			.allocated_size = HID_MAX_DESCRIPTOR_SIZE,
116ad190df1SBenjamin Tissoires 		},
117ad190df1SBenjamin Tissoires 	};
118ad190df1SBenjamin Tissoires 
119ad190df1SBenjamin Tissoires 	ctx_kern.data = kzalloc(ctx_kern.ctx.allocated_size, GFP_KERNEL);
120ad190df1SBenjamin Tissoires 	if (!ctx_kern.data)
121ad190df1SBenjamin Tissoires 		goto ignore_bpf;
122ad190df1SBenjamin Tissoires 
123ad190df1SBenjamin Tissoires 	memcpy(ctx_kern.data, rdesc, min_t(unsigned int, *size, HID_MAX_DESCRIPTOR_SIZE));
124ad190df1SBenjamin Tissoires 
125ad190df1SBenjamin Tissoires 	ret = hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_RDESC_FIXUP, &ctx_kern);
126ad190df1SBenjamin Tissoires 	if (ret < 0)
127ad190df1SBenjamin Tissoires 		goto ignore_bpf;
128ad190df1SBenjamin Tissoires 
129ad190df1SBenjamin Tissoires 	if (ret) {
130ad190df1SBenjamin Tissoires 		if (ret > ctx_kern.ctx.allocated_size)
131ad190df1SBenjamin Tissoires 			goto ignore_bpf;
132ad190df1SBenjamin Tissoires 
133ad190df1SBenjamin Tissoires 		*size = ret;
134ad190df1SBenjamin Tissoires 	}
135ad190df1SBenjamin Tissoires 
136ad190df1SBenjamin Tissoires 	rdesc = krealloc(ctx_kern.data, *size, GFP_KERNEL);
137ad190df1SBenjamin Tissoires 
138ad190df1SBenjamin Tissoires 	return rdesc;
139ad190df1SBenjamin Tissoires 
140ad190df1SBenjamin Tissoires  ignore_bpf:
141ad190df1SBenjamin Tissoires 	kfree(ctx_kern.data);
142ad190df1SBenjamin Tissoires 	return kmemdup(rdesc, *size, GFP_KERNEL);
143ad190df1SBenjamin Tissoires }
144ad190df1SBenjamin Tissoires EXPORT_SYMBOL_GPL(call_hid_bpf_rdesc_fixup);
145ad190df1SBenjamin Tissoires 
146ad190df1SBenjamin Tissoires /**
147f5c27da4SBenjamin Tissoires  * hid_bpf_get_data - Get the kernel memory pointer associated with the context @ctx
148f5c27da4SBenjamin Tissoires  *
149f5c27da4SBenjamin Tissoires  * @ctx: The HID-BPF context
150f5c27da4SBenjamin Tissoires  * @offset: The offset within the memory
151f5c27da4SBenjamin Tissoires  * @rdwr_buf_size: the const size of the buffer
152f5c27da4SBenjamin Tissoires  *
153f5c27da4SBenjamin Tissoires  * @returns %NULL on error, an %__u8 memory pointer on success
154f5c27da4SBenjamin Tissoires  */
155f5c27da4SBenjamin Tissoires noinline __u8 *
hid_bpf_get_data(struct hid_bpf_ctx * ctx,unsigned int offset,const size_t rdwr_buf_size)156f5c27da4SBenjamin Tissoires hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t rdwr_buf_size)
157f5c27da4SBenjamin Tissoires {
158f5c27da4SBenjamin Tissoires 	struct hid_bpf_ctx_kern *ctx_kern;
159f5c27da4SBenjamin Tissoires 
160f5c27da4SBenjamin Tissoires 	if (!ctx)
161f5c27da4SBenjamin Tissoires 		return NULL;
162f5c27da4SBenjamin Tissoires 
163f5c27da4SBenjamin Tissoires 	ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);
164f5c27da4SBenjamin Tissoires 
165658ee5a6SBenjamin Tissoires 	if (rdwr_buf_size + offset > ctx->allocated_size)
166f5c27da4SBenjamin Tissoires 		return NULL;
167f5c27da4SBenjamin Tissoires 
168f5c27da4SBenjamin Tissoires 	return ctx_kern->data + offset;
169f5c27da4SBenjamin Tissoires }
170f5c27da4SBenjamin Tissoires 
171f5c27da4SBenjamin Tissoires /*
172f5c27da4SBenjamin Tissoires  * The following set contains all functions we agree BPF programs
173f5c27da4SBenjamin Tissoires  * can use.
174f5c27da4SBenjamin Tissoires  */
175f5c27da4SBenjamin Tissoires BTF_SET8_START(hid_bpf_kfunc_ids)
176f5c27da4SBenjamin Tissoires BTF_ID_FLAGS(func, hid_bpf_get_data, KF_RET_NULL)
177f5c27da4SBenjamin Tissoires BTF_SET8_END(hid_bpf_kfunc_ids)
178f5c27da4SBenjamin Tissoires 
179f5c27da4SBenjamin Tissoires static const struct btf_kfunc_id_set hid_bpf_kfunc_set = {
180f5c27da4SBenjamin Tissoires 	.owner = THIS_MODULE,
181f5c27da4SBenjamin Tissoires 	.set   = &hid_bpf_kfunc_ids,
182f5c27da4SBenjamin Tissoires };
183f5c27da4SBenjamin Tissoires 
device_match_id(struct device * dev,const void * id)184f5c27da4SBenjamin Tissoires static int device_match_id(struct device *dev, const void *id)
185f5c27da4SBenjamin Tissoires {
186f5c27da4SBenjamin Tissoires 	struct hid_device *hdev = to_hid_device(dev);
187f5c27da4SBenjamin Tissoires 
188f5c27da4SBenjamin Tissoires 	return hdev->id == *(int *)id;
189f5c27da4SBenjamin Tissoires }
190f5c27da4SBenjamin Tissoires 
__hid_bpf_allocate_data(struct hid_device * hdev,u8 ** data,u32 * size)191658ee5a6SBenjamin Tissoires static int __hid_bpf_allocate_data(struct hid_device *hdev, u8 **data, u32 *size)
192658ee5a6SBenjamin Tissoires {
193658ee5a6SBenjamin Tissoires 	u8 *alloc_data;
194658ee5a6SBenjamin Tissoires 	unsigned int i, j, max_report_len = 0;
195658ee5a6SBenjamin Tissoires 	size_t alloc_size = 0;
196658ee5a6SBenjamin Tissoires 
197658ee5a6SBenjamin Tissoires 	/* compute the maximum report length for this device */
198658ee5a6SBenjamin Tissoires 	for (i = 0; i < HID_REPORT_TYPES; i++) {
199658ee5a6SBenjamin Tissoires 		struct hid_report_enum *report_enum = hdev->report_enum + i;
200658ee5a6SBenjamin Tissoires 
201658ee5a6SBenjamin Tissoires 		for (j = 0; j < HID_MAX_IDS; j++) {
202658ee5a6SBenjamin Tissoires 			struct hid_report *report = report_enum->report_id_hash[j];
203658ee5a6SBenjamin Tissoires 
204658ee5a6SBenjamin Tissoires 			if (report)
205658ee5a6SBenjamin Tissoires 				max_report_len = max(max_report_len, hid_report_len(report));
206658ee5a6SBenjamin Tissoires 		}
207658ee5a6SBenjamin Tissoires 	}
208658ee5a6SBenjamin Tissoires 
209658ee5a6SBenjamin Tissoires 	/*
210658ee5a6SBenjamin Tissoires 	 * Give us a little bit of extra space and some predictability in the
211658ee5a6SBenjamin Tissoires 	 * buffer length we create. This way, we can tell users that they can
212658ee5a6SBenjamin Tissoires 	 * work on chunks of 64 bytes of memory without having the bpf verifier
213658ee5a6SBenjamin Tissoires 	 * scream at them.
214658ee5a6SBenjamin Tissoires 	 */
215658ee5a6SBenjamin Tissoires 	alloc_size = DIV_ROUND_UP(max_report_len, 64) * 64;
216658ee5a6SBenjamin Tissoires 
217658ee5a6SBenjamin Tissoires 	alloc_data = kzalloc(alloc_size, GFP_KERNEL);
218658ee5a6SBenjamin Tissoires 	if (!alloc_data)
219658ee5a6SBenjamin Tissoires 		return -ENOMEM;
220658ee5a6SBenjamin Tissoires 
221658ee5a6SBenjamin Tissoires 	*data = alloc_data;
222658ee5a6SBenjamin Tissoires 	*size = alloc_size;
223658ee5a6SBenjamin Tissoires 
224658ee5a6SBenjamin Tissoires 	return 0;
225658ee5a6SBenjamin Tissoires }
226658ee5a6SBenjamin Tissoires 
hid_bpf_allocate_event_data(struct hid_device * hdev)227658ee5a6SBenjamin Tissoires static int hid_bpf_allocate_event_data(struct hid_device *hdev)
228658ee5a6SBenjamin Tissoires {
229658ee5a6SBenjamin Tissoires 	/* hdev->bpf.device_data is already allocated, abort */
230658ee5a6SBenjamin Tissoires 	if (hdev->bpf.device_data)
231658ee5a6SBenjamin Tissoires 		return 0;
232658ee5a6SBenjamin Tissoires 
233658ee5a6SBenjamin Tissoires 	return __hid_bpf_allocate_data(hdev, &hdev->bpf.device_data, &hdev->bpf.allocated_data);
234658ee5a6SBenjamin Tissoires }
235658ee5a6SBenjamin Tissoires 
hid_bpf_reconnect(struct hid_device * hdev)236ad190df1SBenjamin Tissoires int hid_bpf_reconnect(struct hid_device *hdev)
237ad190df1SBenjamin Tissoires {
238ad190df1SBenjamin Tissoires 	if (!test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status))
239ad190df1SBenjamin Tissoires 		return device_reprobe(&hdev->dev);
240ad190df1SBenjamin Tissoires 
241ad190df1SBenjamin Tissoires 	return 0;
242ad190df1SBenjamin Tissoires }
243ad190df1SBenjamin Tissoires 
do_hid_bpf_attach_prog(struct hid_device * hdev,int prog_fd,struct bpf_prog * prog,__u32 flags)244d83a7e59SBenjamin Tissoires static int do_hid_bpf_attach_prog(struct hid_device *hdev, int prog_fd, struct bpf_prog *prog,
245d83a7e59SBenjamin Tissoires 				  __u32 flags)
246d83a7e59SBenjamin Tissoires {
247d83a7e59SBenjamin Tissoires 	int fd, err, prog_type;
248d83a7e59SBenjamin Tissoires 
249d83a7e59SBenjamin Tissoires 	prog_type = hid_bpf_get_prog_attach_type(prog);
250d83a7e59SBenjamin Tissoires 	if (prog_type < 0)
251d83a7e59SBenjamin Tissoires 		return prog_type;
252d83a7e59SBenjamin Tissoires 
253d83a7e59SBenjamin Tissoires 	if (prog_type >= HID_BPF_PROG_TYPE_MAX)
254d83a7e59SBenjamin Tissoires 		return -EINVAL;
255d83a7e59SBenjamin Tissoires 
256d83a7e59SBenjamin Tissoires 	if (prog_type == HID_BPF_PROG_TYPE_DEVICE_EVENT) {
257d83a7e59SBenjamin Tissoires 		err = hid_bpf_allocate_event_data(hdev);
258d83a7e59SBenjamin Tissoires 		if (err)
259d83a7e59SBenjamin Tissoires 			return err;
260d83a7e59SBenjamin Tissoires 	}
261d83a7e59SBenjamin Tissoires 
262d83a7e59SBenjamin Tissoires 	fd = __hid_bpf_attach_prog(hdev, prog_type, prog_fd, prog, flags);
263d83a7e59SBenjamin Tissoires 	if (fd < 0)
264d83a7e59SBenjamin Tissoires 		return fd;
265d83a7e59SBenjamin Tissoires 
266d83a7e59SBenjamin Tissoires 	if (prog_type == HID_BPF_PROG_TYPE_RDESC_FIXUP) {
267d83a7e59SBenjamin Tissoires 		err = hid_bpf_reconnect(hdev);
268d83a7e59SBenjamin Tissoires 		if (err) {
269d83a7e59SBenjamin Tissoires 			close_fd(fd);
270d83a7e59SBenjamin Tissoires 			return err;
271d83a7e59SBenjamin Tissoires 		}
272d83a7e59SBenjamin Tissoires 	}
273d83a7e59SBenjamin Tissoires 
274d83a7e59SBenjamin Tissoires 	return fd;
275d83a7e59SBenjamin Tissoires }
276d83a7e59SBenjamin Tissoires 
277f5c27da4SBenjamin Tissoires /**
278f5c27da4SBenjamin Tissoires  * hid_bpf_attach_prog - Attach the given @prog_fd to the given HID device
279f5c27da4SBenjamin Tissoires  *
280f5c27da4SBenjamin Tissoires  * @hid_id: the system unique identifier of the HID device
281f5c27da4SBenjamin Tissoires  * @prog_fd: an fd in the user process representing the program to attach
282f5c27da4SBenjamin Tissoires  * @flags: any logical OR combination of &enum hid_bpf_attach_flags
283f5c27da4SBenjamin Tissoires  *
2844b9a3f49SBenjamin Tissoires  * @returns an fd of a bpf_link object on success (> %0), an error code otherwise.
2854b9a3f49SBenjamin Tissoires  * Closing this fd will detach the program from the HID device (unless the bpf_link
2864b9a3f49SBenjamin Tissoires  * is pinned to the BPF file system).
287f5c27da4SBenjamin Tissoires  */
288f5c27da4SBenjamin Tissoires /* called from syscall */
289f5c27da4SBenjamin Tissoires noinline int
hid_bpf_attach_prog(unsigned int hid_id,int prog_fd,__u32 flags)290f5c27da4SBenjamin Tissoires hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags)
291f5c27da4SBenjamin Tissoires {
292f5c27da4SBenjamin Tissoires 	struct hid_device *hdev;
293d83a7e59SBenjamin Tissoires 	struct bpf_prog *prog;
294f5c27da4SBenjamin Tissoires 	struct device *dev;
295*467fce63SBenjamin Tissoires 	int err, fd;
296f5c27da4SBenjamin Tissoires 
297f5c27da4SBenjamin Tissoires 	if (!hid_bpf_ops)
298f5c27da4SBenjamin Tissoires 		return -EINVAL;
299f5c27da4SBenjamin Tissoires 
300f5c27da4SBenjamin Tissoires 	if ((flags & ~HID_BPF_FLAG_MASK))
301f5c27da4SBenjamin Tissoires 		return -EINVAL;
302f5c27da4SBenjamin Tissoires 
303f5c27da4SBenjamin Tissoires 	dev = bus_find_device(hid_bpf_ops->bus_type, NULL, &hid_id, device_match_id);
304f5c27da4SBenjamin Tissoires 	if (!dev)
305f5c27da4SBenjamin Tissoires 		return -EINVAL;
306f5c27da4SBenjamin Tissoires 
307f5c27da4SBenjamin Tissoires 	hdev = to_hid_device(dev);
308f5c27da4SBenjamin Tissoires 
309d83a7e59SBenjamin Tissoires 	/*
310d83a7e59SBenjamin Tissoires 	 * take a ref on the prog itself, it will be released
311d83a7e59SBenjamin Tissoires 	 * on errors or when it'll be detached
312d83a7e59SBenjamin Tissoires 	 */
313d83a7e59SBenjamin Tissoires 	prog = bpf_prog_get(prog_fd);
314*467fce63SBenjamin Tissoires 	if (IS_ERR(prog)) {
315*467fce63SBenjamin Tissoires 		err = PTR_ERR(prog);
316*467fce63SBenjamin Tissoires 		goto out_dev_put;
317*467fce63SBenjamin Tissoires 	}
318658ee5a6SBenjamin Tissoires 
319d83a7e59SBenjamin Tissoires 	fd = do_hid_bpf_attach_prog(hdev, prog_fd, prog, flags);
320*467fce63SBenjamin Tissoires 	if (fd < 0) {
321*467fce63SBenjamin Tissoires 		err = fd;
322*467fce63SBenjamin Tissoires 		goto out_prog_put;
323*467fce63SBenjamin Tissoires 	}
324ad190df1SBenjamin Tissoires 
3254b9a3f49SBenjamin Tissoires 	return fd;
326*467fce63SBenjamin Tissoires 
327*467fce63SBenjamin Tissoires  out_prog_put:
328*467fce63SBenjamin Tissoires 	bpf_prog_put(prog);
329*467fce63SBenjamin Tissoires  out_dev_put:
330*467fce63SBenjamin Tissoires 	put_device(dev);
331*467fce63SBenjamin Tissoires 	return err;
332f5c27da4SBenjamin Tissoires }
333f5c27da4SBenjamin Tissoires 
33491a7f802SBenjamin Tissoires /**
33591a7f802SBenjamin Tissoires  * hid_bpf_allocate_context - Allocate a context to the given HID device
33691a7f802SBenjamin Tissoires  *
33791a7f802SBenjamin Tissoires  * @hid_id: the system unique identifier of the HID device
33891a7f802SBenjamin Tissoires  *
33991a7f802SBenjamin Tissoires  * @returns A pointer to &struct hid_bpf_ctx on success, %NULL on error.
34091a7f802SBenjamin Tissoires  */
34191a7f802SBenjamin Tissoires noinline struct hid_bpf_ctx *
hid_bpf_allocate_context(unsigned int hid_id)34291a7f802SBenjamin Tissoires hid_bpf_allocate_context(unsigned int hid_id)
34391a7f802SBenjamin Tissoires {
34491a7f802SBenjamin Tissoires 	struct hid_device *hdev;
34591a7f802SBenjamin Tissoires 	struct hid_bpf_ctx_kern *ctx_kern = NULL;
34691a7f802SBenjamin Tissoires 	struct device *dev;
34791a7f802SBenjamin Tissoires 
34891a7f802SBenjamin Tissoires 	if (!hid_bpf_ops)
34991a7f802SBenjamin Tissoires 		return NULL;
35091a7f802SBenjamin Tissoires 
35191a7f802SBenjamin Tissoires 	dev = bus_find_device(hid_bpf_ops->bus_type, NULL, &hid_id, device_match_id);
35291a7f802SBenjamin Tissoires 	if (!dev)
35391a7f802SBenjamin Tissoires 		return NULL;
35491a7f802SBenjamin Tissoires 
35591a7f802SBenjamin Tissoires 	hdev = to_hid_device(dev);
35691a7f802SBenjamin Tissoires 
35791a7f802SBenjamin Tissoires 	ctx_kern = kzalloc(sizeof(*ctx_kern), GFP_KERNEL);
358*467fce63SBenjamin Tissoires 	if (!ctx_kern) {
359*467fce63SBenjamin Tissoires 		put_device(dev);
36091a7f802SBenjamin Tissoires 		return NULL;
361*467fce63SBenjamin Tissoires 	}
36291a7f802SBenjamin Tissoires 
36391a7f802SBenjamin Tissoires 	ctx_kern->ctx.hid = hdev;
36491a7f802SBenjamin Tissoires 
36591a7f802SBenjamin Tissoires 	return &ctx_kern->ctx;
36691a7f802SBenjamin Tissoires }
36791a7f802SBenjamin Tissoires 
36891a7f802SBenjamin Tissoires /**
36991a7f802SBenjamin Tissoires  * hid_bpf_release_context - Release the previously allocated context @ctx
37091a7f802SBenjamin Tissoires  *
37191a7f802SBenjamin Tissoires  * @ctx: the HID-BPF context to release
37291a7f802SBenjamin Tissoires  *
37391a7f802SBenjamin Tissoires  */
37491a7f802SBenjamin Tissoires noinline void
hid_bpf_release_context(struct hid_bpf_ctx * ctx)37591a7f802SBenjamin Tissoires hid_bpf_release_context(struct hid_bpf_ctx *ctx)
37691a7f802SBenjamin Tissoires {
37791a7f802SBenjamin Tissoires 	struct hid_bpf_ctx_kern *ctx_kern;
378*467fce63SBenjamin Tissoires 	struct hid_device *hid;
37991a7f802SBenjamin Tissoires 
38091a7f802SBenjamin Tissoires 	ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);
381*467fce63SBenjamin Tissoires 	hid = (struct hid_device *)ctx_kern->ctx.hid; /* ignore const */
38291a7f802SBenjamin Tissoires 
38391a7f802SBenjamin Tissoires 	kfree(ctx_kern);
384*467fce63SBenjamin Tissoires 
385*467fce63SBenjamin Tissoires 	/* get_device() is called by bus_find_device() */
386*467fce63SBenjamin Tissoires 	put_device(&hid->dev);
38791a7f802SBenjamin Tissoires }
38891a7f802SBenjamin Tissoires 
38991a7f802SBenjamin Tissoires /**
39091a7f802SBenjamin Tissoires  * hid_bpf_hw_request - Communicate with a HID device
39191a7f802SBenjamin Tissoires  *
39291a7f802SBenjamin Tissoires  * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context()
39391a7f802SBenjamin Tissoires  * @buf: a %PTR_TO_MEM buffer
39491a7f802SBenjamin Tissoires  * @buf__sz: the size of the data to transfer
39591a7f802SBenjamin Tissoires  * @rtype: the type of the report (%HID_INPUT_REPORT, %HID_FEATURE_REPORT, %HID_OUTPUT_REPORT)
39691a7f802SBenjamin Tissoires  * @reqtype: the type of the request (%HID_REQ_GET_REPORT, %HID_REQ_SET_REPORT, ...)
39791a7f802SBenjamin Tissoires  *
39891a7f802SBenjamin Tissoires  * @returns %0 on success, a negative error code otherwise.
39991a7f802SBenjamin Tissoires  */
40091a7f802SBenjamin Tissoires noinline int
hid_bpf_hw_request(struct hid_bpf_ctx * ctx,__u8 * buf,size_t buf__sz,enum hid_report_type rtype,enum hid_class_request reqtype)40191a7f802SBenjamin Tissoires hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz,
40291a7f802SBenjamin Tissoires 		   enum hid_report_type rtype, enum hid_class_request reqtype)
40391a7f802SBenjamin Tissoires {
40491a7f802SBenjamin Tissoires 	struct hid_device *hdev;
40591a7f802SBenjamin Tissoires 	struct hid_report *report;
40691a7f802SBenjamin Tissoires 	struct hid_report_enum *report_enum;
40791a7f802SBenjamin Tissoires 	u8 *dma_data;
40891a7f802SBenjamin Tissoires 	u32 report_len;
40991a7f802SBenjamin Tissoires 	int ret;
41091a7f802SBenjamin Tissoires 
41191a7f802SBenjamin Tissoires 	/* check arguments */
41291a7f802SBenjamin Tissoires 	if (!ctx || !hid_bpf_ops || !buf)
41391a7f802SBenjamin Tissoires 		return -EINVAL;
41491a7f802SBenjamin Tissoires 
41591a7f802SBenjamin Tissoires 	switch (rtype) {
41691a7f802SBenjamin Tissoires 	case HID_INPUT_REPORT:
41791a7f802SBenjamin Tissoires 	case HID_OUTPUT_REPORT:
41891a7f802SBenjamin Tissoires 	case HID_FEATURE_REPORT:
41991a7f802SBenjamin Tissoires 		break;
42091a7f802SBenjamin Tissoires 	default:
42191a7f802SBenjamin Tissoires 		return -EINVAL;
42291a7f802SBenjamin Tissoires 	}
42391a7f802SBenjamin Tissoires 
42491a7f802SBenjamin Tissoires 	switch (reqtype) {
42591a7f802SBenjamin Tissoires 	case HID_REQ_GET_REPORT:
42691a7f802SBenjamin Tissoires 	case HID_REQ_GET_IDLE:
42791a7f802SBenjamin Tissoires 	case HID_REQ_GET_PROTOCOL:
42891a7f802SBenjamin Tissoires 	case HID_REQ_SET_REPORT:
42991a7f802SBenjamin Tissoires 	case HID_REQ_SET_IDLE:
43091a7f802SBenjamin Tissoires 	case HID_REQ_SET_PROTOCOL:
43191a7f802SBenjamin Tissoires 		break;
43291a7f802SBenjamin Tissoires 	default:
43391a7f802SBenjamin Tissoires 		return -EINVAL;
43491a7f802SBenjamin Tissoires 	}
43591a7f802SBenjamin Tissoires 
43691a7f802SBenjamin Tissoires 	if (buf__sz < 1)
43791a7f802SBenjamin Tissoires 		return -EINVAL;
43891a7f802SBenjamin Tissoires 
43991a7f802SBenjamin Tissoires 	hdev = (struct hid_device *)ctx->hid; /* discard const */
44091a7f802SBenjamin Tissoires 
44191a7f802SBenjamin Tissoires 	report_enum = hdev->report_enum + rtype;
44291a7f802SBenjamin Tissoires 	report = hid_bpf_ops->hid_get_report(report_enum, buf);
44391a7f802SBenjamin Tissoires 	if (!report)
44491a7f802SBenjamin Tissoires 		return -EINVAL;
44591a7f802SBenjamin Tissoires 
44691a7f802SBenjamin Tissoires 	report_len = hid_report_len(report);
44791a7f802SBenjamin Tissoires 
44891a7f802SBenjamin Tissoires 	if (buf__sz > report_len)
44991a7f802SBenjamin Tissoires 		buf__sz = report_len;
45091a7f802SBenjamin Tissoires 
45191a7f802SBenjamin Tissoires 	dma_data = kmemdup(buf, buf__sz, GFP_KERNEL);
45291a7f802SBenjamin Tissoires 	if (!dma_data)
45391a7f802SBenjamin Tissoires 		return -ENOMEM;
45491a7f802SBenjamin Tissoires 
45591a7f802SBenjamin Tissoires 	ret = hid_bpf_ops->hid_hw_raw_request(hdev,
45691a7f802SBenjamin Tissoires 					      dma_data[0],
45791a7f802SBenjamin Tissoires 					      dma_data,
45891a7f802SBenjamin Tissoires 					      buf__sz,
45991a7f802SBenjamin Tissoires 					      rtype,
46091a7f802SBenjamin Tissoires 					      reqtype);
46191a7f802SBenjamin Tissoires 
46291a7f802SBenjamin Tissoires 	if (ret > 0)
46391a7f802SBenjamin Tissoires 		memcpy(buf, dma_data, ret);
46491a7f802SBenjamin Tissoires 
46591a7f802SBenjamin Tissoires 	kfree(dma_data);
46691a7f802SBenjamin Tissoires 	return ret;
46791a7f802SBenjamin Tissoires }
46891a7f802SBenjamin Tissoires 
46986020156SBenjamin Tissoires /* our HID-BPF entrypoints */
47086020156SBenjamin Tissoires BTF_SET8_START(hid_bpf_fmodret_ids)
47186020156SBenjamin Tissoires BTF_ID_FLAGS(func, hid_bpf_device_event)
47286020156SBenjamin Tissoires BTF_ID_FLAGS(func, hid_bpf_rdesc_fixup)
47386020156SBenjamin Tissoires BTF_ID_FLAGS(func, __hid_bpf_tail_call)
47486020156SBenjamin Tissoires BTF_SET8_END(hid_bpf_fmodret_ids)
47586020156SBenjamin Tissoires 
47686020156SBenjamin Tissoires static const struct btf_kfunc_id_set hid_bpf_fmodret_set = {
47786020156SBenjamin Tissoires 	.owner = THIS_MODULE,
47886020156SBenjamin Tissoires 	.set   = &hid_bpf_fmodret_ids,
47986020156SBenjamin Tissoires };
48086020156SBenjamin Tissoires 
481f5c27da4SBenjamin Tissoires /* for syscall HID-BPF */
482f5c27da4SBenjamin Tissoires BTF_SET8_START(hid_bpf_syscall_kfunc_ids)
483f5c27da4SBenjamin Tissoires BTF_ID_FLAGS(func, hid_bpf_attach_prog)
48491a7f802SBenjamin Tissoires BTF_ID_FLAGS(func, hid_bpf_allocate_context, KF_ACQUIRE | KF_RET_NULL)
48591a7f802SBenjamin Tissoires BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE)
48691a7f802SBenjamin Tissoires BTF_ID_FLAGS(func, hid_bpf_hw_request)
487f5c27da4SBenjamin Tissoires BTF_SET8_END(hid_bpf_syscall_kfunc_ids)
488f5c27da4SBenjamin Tissoires 
489f5c27da4SBenjamin Tissoires static const struct btf_kfunc_id_set hid_bpf_syscall_kfunc_set = {
490f5c27da4SBenjamin Tissoires 	.owner = THIS_MODULE,
491f5c27da4SBenjamin Tissoires 	.set   = &hid_bpf_syscall_kfunc_ids,
492f5c27da4SBenjamin Tissoires };
493f5c27da4SBenjamin Tissoires 
hid_bpf_connect_device(struct hid_device * hdev)494658ee5a6SBenjamin Tissoires int hid_bpf_connect_device(struct hid_device *hdev)
495658ee5a6SBenjamin Tissoires {
496658ee5a6SBenjamin Tissoires 	struct hid_bpf_prog_list *prog_list;
497658ee5a6SBenjamin Tissoires 
498658ee5a6SBenjamin Tissoires 	rcu_read_lock();
499658ee5a6SBenjamin Tissoires 	prog_list = rcu_dereference(hdev->bpf.progs[HID_BPF_PROG_TYPE_DEVICE_EVENT]);
500658ee5a6SBenjamin Tissoires 	rcu_read_unlock();
501658ee5a6SBenjamin Tissoires 
502658ee5a6SBenjamin Tissoires 	/* only allocate BPF data if there are programs attached */
503658ee5a6SBenjamin Tissoires 	if (!prog_list)
504658ee5a6SBenjamin Tissoires 		return 0;
505658ee5a6SBenjamin Tissoires 
506658ee5a6SBenjamin Tissoires 	return hid_bpf_allocate_event_data(hdev);
507658ee5a6SBenjamin Tissoires }
508658ee5a6SBenjamin Tissoires EXPORT_SYMBOL_GPL(hid_bpf_connect_device);
509658ee5a6SBenjamin Tissoires 
hid_bpf_disconnect_device(struct hid_device * hdev)510658ee5a6SBenjamin Tissoires void hid_bpf_disconnect_device(struct hid_device *hdev)
511658ee5a6SBenjamin Tissoires {
512658ee5a6SBenjamin Tissoires 	kfree(hdev->bpf.device_data);
513658ee5a6SBenjamin Tissoires 	hdev->bpf.device_data = NULL;
514658ee5a6SBenjamin Tissoires 	hdev->bpf.allocated_data = 0;
515658ee5a6SBenjamin Tissoires }
516658ee5a6SBenjamin Tissoires EXPORT_SYMBOL_GPL(hid_bpf_disconnect_device);
517658ee5a6SBenjamin Tissoires 
hid_bpf_destroy_device(struct hid_device * hdev)518f5c27da4SBenjamin Tissoires void hid_bpf_destroy_device(struct hid_device *hdev)
519f5c27da4SBenjamin Tissoires {
520f5c27da4SBenjamin Tissoires 	if (!hdev)
521f5c27da4SBenjamin Tissoires 		return;
522f5c27da4SBenjamin Tissoires 
523f5c27da4SBenjamin Tissoires 	/* mark the device as destroyed in bpf so we don't reattach it */
524f5c27da4SBenjamin Tissoires 	hdev->bpf.destroyed = true;
525f5c27da4SBenjamin Tissoires 
526f5c27da4SBenjamin Tissoires 	__hid_bpf_destroy_device(hdev);
527f5c27da4SBenjamin Tissoires }
528f5c27da4SBenjamin Tissoires EXPORT_SYMBOL_GPL(hid_bpf_destroy_device);
529f5c27da4SBenjamin Tissoires 
hid_bpf_device_init(struct hid_device * hdev)530f5c27da4SBenjamin Tissoires void hid_bpf_device_init(struct hid_device *hdev)
531f5c27da4SBenjamin Tissoires {
532f5c27da4SBenjamin Tissoires 	spin_lock_init(&hdev->bpf.progs_lock);
533f5c27da4SBenjamin Tissoires }
534f5c27da4SBenjamin Tissoires EXPORT_SYMBOL_GPL(hid_bpf_device_init);
535f5c27da4SBenjamin Tissoires 
hid_bpf_init(void)536f5c27da4SBenjamin Tissoires static int __init hid_bpf_init(void)
537f5c27da4SBenjamin Tissoires {
538f5c27da4SBenjamin Tissoires 	int err;
539f5c27da4SBenjamin Tissoires 
540f5c27da4SBenjamin Tissoires 	/* Note: if we exit with an error any time here, we would entirely break HID, which
541f5c27da4SBenjamin Tissoires 	 * is probably not something we want. So we log an error and return success.
542f5c27da4SBenjamin Tissoires 	 *
543f5c27da4SBenjamin Tissoires 	 * This is not a big deal: the syscall allowing to attach a BPF program to a HID device
544f5c27da4SBenjamin Tissoires 	 * will not be available, so nobody will be able to use the functionality.
545f5c27da4SBenjamin Tissoires 	 */
546f5c27da4SBenjamin Tissoires 
54786020156SBenjamin Tissoires 	err = register_btf_fmodret_id_set(&hid_bpf_fmodret_set);
54886020156SBenjamin Tissoires 	if (err) {
54986020156SBenjamin Tissoires 		pr_warn("error while registering fmodret entrypoints: %d", err);
55086020156SBenjamin Tissoires 		return 0;
55186020156SBenjamin Tissoires 	}
55286020156SBenjamin Tissoires 
553f5c27da4SBenjamin Tissoires 	err = hid_bpf_preload_skel();
554f5c27da4SBenjamin Tissoires 	if (err) {
555f5c27da4SBenjamin Tissoires 		pr_warn("error while preloading HID BPF dispatcher: %d", err);
556f5c27da4SBenjamin Tissoires 		return 0;
557f5c27da4SBenjamin Tissoires 	}
558f5c27da4SBenjamin Tissoires 
5590c2d5728SBenjamin Tissoires 	/* register tracing kfuncs after we are sure we can load our preloaded bpf program */
5600c2d5728SBenjamin Tissoires 	err = register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &hid_bpf_kfunc_set);
5610c2d5728SBenjamin Tissoires 	if (err) {
5620c2d5728SBenjamin Tissoires 		pr_warn("error while setting HID BPF tracing kfuncs: %d", err);
5630c2d5728SBenjamin Tissoires 		return 0;
5640c2d5728SBenjamin Tissoires 	}
5650c2d5728SBenjamin Tissoires 
566f5c27da4SBenjamin Tissoires 	/* register syscalls after we are sure we can load our preloaded bpf program */
567f5c27da4SBenjamin Tissoires 	err = register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &hid_bpf_syscall_kfunc_set);
568f5c27da4SBenjamin Tissoires 	if (err) {
569f5c27da4SBenjamin Tissoires 		pr_warn("error while setting HID BPF syscall kfuncs: %d", err);
570f5c27da4SBenjamin Tissoires 		return 0;
571f5c27da4SBenjamin Tissoires 	}
572f5c27da4SBenjamin Tissoires 
573f5c27da4SBenjamin Tissoires 	return 0;
574f5c27da4SBenjamin Tissoires }
575f5c27da4SBenjamin Tissoires 
hid_bpf_exit(void)576f5c27da4SBenjamin Tissoires static void __exit hid_bpf_exit(void)
577f5c27da4SBenjamin Tissoires {
578f5c27da4SBenjamin Tissoires 	/* HID depends on us, so if we hit that code, we are guaranteed that hid
579f5c27da4SBenjamin Tissoires 	 * has been removed and thus we do not need to clear the HID devices
580f5c27da4SBenjamin Tissoires 	 */
581f5c27da4SBenjamin Tissoires 	hid_bpf_free_links_and_skel();
582f5c27da4SBenjamin Tissoires }
583f5c27da4SBenjamin Tissoires 
584f5c27da4SBenjamin Tissoires late_initcall(hid_bpf_init);
585f5c27da4SBenjamin Tissoires module_exit(hid_bpf_exit);
586f5c27da4SBenjamin Tissoires MODULE_AUTHOR("Benjamin Tissoires");
587f5c27da4SBenjamin Tissoires MODULE_LICENSE("GPL");
588