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