xref: /openbmc/linux/drivers/hid/bpf/hid_bpf_jmp_table.c (revision c595db6d7c8bcf87ef42204391fa890e5950e566)
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 #include <linux/bitops.h>
10f5c27da4SBenjamin Tissoires #include <linux/btf.h>
11f5c27da4SBenjamin Tissoires #include <linux/btf_ids.h>
12f5c27da4SBenjamin Tissoires #include <linux/circ_buf.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/module.h>
18f5c27da4SBenjamin Tissoires #include <linux/workqueue.h>
19f5c27da4SBenjamin Tissoires #include "hid_bpf_dispatch.h"
20f5c27da4SBenjamin Tissoires #include "entrypoints/entrypoints.lskel.h"
21f5c27da4SBenjamin Tissoires 
22f5c27da4SBenjamin Tissoires #define HID_BPF_MAX_PROGS 1024 /* keep this in sync with preloaded bpf,
23f5c27da4SBenjamin Tissoires 				* needs to be a power of 2 as we use it as
24f5c27da4SBenjamin Tissoires 				* a circular buffer
25f5c27da4SBenjamin Tissoires 				*/
26f5c27da4SBenjamin Tissoires 
27f5c27da4SBenjamin Tissoires #define NEXT(idx) (((idx) + 1) & (HID_BPF_MAX_PROGS - 1))
28f5c27da4SBenjamin Tissoires #define PREV(idx) (((idx) - 1) & (HID_BPF_MAX_PROGS - 1))
29f5c27da4SBenjamin Tissoires 
30f5c27da4SBenjamin Tissoires /*
31f5c27da4SBenjamin Tissoires  * represents one attached program stored in the hid jump table
32f5c27da4SBenjamin Tissoires  */
33f5c27da4SBenjamin Tissoires struct hid_bpf_prog_entry {
34f5c27da4SBenjamin Tissoires 	struct bpf_prog *prog;
35f5c27da4SBenjamin Tissoires 	struct hid_device *hdev;
36f5c27da4SBenjamin Tissoires 	enum hid_bpf_prog_type type;
37f5c27da4SBenjamin Tissoires 	u16 idx;
38f5c27da4SBenjamin Tissoires };
39f5c27da4SBenjamin Tissoires 
40f5c27da4SBenjamin Tissoires struct hid_bpf_jmp_table {
41f5c27da4SBenjamin Tissoires 	struct bpf_map *map;
42f5c27da4SBenjamin Tissoires 	struct hid_bpf_prog_entry entries[HID_BPF_MAX_PROGS]; /* compacted list, circular buffer */
43f5c27da4SBenjamin Tissoires 	int tail, head;
44f5c27da4SBenjamin Tissoires 	struct bpf_prog *progs[HID_BPF_MAX_PROGS]; /* idx -> progs mapping */
45f5c27da4SBenjamin Tissoires 	unsigned long enabled[BITS_TO_LONGS(HID_BPF_MAX_PROGS)];
46f5c27da4SBenjamin Tissoires };
47f5c27da4SBenjamin Tissoires 
48f5c27da4SBenjamin Tissoires #define FOR_ENTRIES(__i, __start, __end) \
49f5c27da4SBenjamin Tissoires 	for (__i = __start; CIRC_CNT(__end, __i, HID_BPF_MAX_PROGS); __i = NEXT(__i))
50f5c27da4SBenjamin Tissoires 
51f5c27da4SBenjamin Tissoires static struct hid_bpf_jmp_table jmp_table;
52f5c27da4SBenjamin Tissoires 
53f5c27da4SBenjamin Tissoires static DEFINE_MUTEX(hid_bpf_attach_lock);		/* held when attaching/detaching programs */
54f5c27da4SBenjamin Tissoires 
55f5c27da4SBenjamin Tissoires static void hid_bpf_release_progs(struct work_struct *work);
56f5c27da4SBenjamin Tissoires 
57f5c27da4SBenjamin Tissoires static DECLARE_WORK(release_work, hid_bpf_release_progs);
58f5c27da4SBenjamin Tissoires 
59f5c27da4SBenjamin Tissoires BTF_ID_LIST(hid_bpf_btf_ids)
BTF_ID(func,hid_bpf_device_event)60f5c27da4SBenjamin Tissoires BTF_ID(func, hid_bpf_device_event)			/* HID_BPF_PROG_TYPE_DEVICE_EVENT */
61ad190df1SBenjamin Tissoires BTF_ID(func, hid_bpf_rdesc_fixup)			/* HID_BPF_PROG_TYPE_RDESC_FIXUP */
62f5c27da4SBenjamin Tissoires 
63f5c27da4SBenjamin Tissoires static int hid_bpf_max_programs(enum hid_bpf_prog_type type)
64f5c27da4SBenjamin Tissoires {
65f5c27da4SBenjamin Tissoires 	switch (type) {
66f5c27da4SBenjamin Tissoires 	case HID_BPF_PROG_TYPE_DEVICE_EVENT:
67f5c27da4SBenjamin Tissoires 		return HID_BPF_MAX_PROGS_PER_DEV;
68ad190df1SBenjamin Tissoires 	case HID_BPF_PROG_TYPE_RDESC_FIXUP:
69ad190df1SBenjamin Tissoires 		return 1;
70f5c27da4SBenjamin Tissoires 	default:
71f5c27da4SBenjamin Tissoires 		return -EINVAL;
72f5c27da4SBenjamin Tissoires 	}
73f5c27da4SBenjamin Tissoires }
74f5c27da4SBenjamin Tissoires 
hid_bpf_program_count(struct hid_device * hdev,struct bpf_prog * prog,enum hid_bpf_prog_type type)75f5c27da4SBenjamin Tissoires static int hid_bpf_program_count(struct hid_device *hdev,
76f5c27da4SBenjamin Tissoires 				 struct bpf_prog *prog,
77f5c27da4SBenjamin Tissoires 				 enum hid_bpf_prog_type type)
78f5c27da4SBenjamin Tissoires {
79f5c27da4SBenjamin Tissoires 	int i, n = 0;
80f5c27da4SBenjamin Tissoires 
81f5c27da4SBenjamin Tissoires 	if (type >= HID_BPF_PROG_TYPE_MAX)
82f5c27da4SBenjamin Tissoires 		return -EINVAL;
83f5c27da4SBenjamin Tissoires 
84f5c27da4SBenjamin Tissoires 	FOR_ENTRIES(i, jmp_table.tail, jmp_table.head) {
85f5c27da4SBenjamin Tissoires 		struct hid_bpf_prog_entry *entry = &jmp_table.entries[i];
86f5c27da4SBenjamin Tissoires 
87f5c27da4SBenjamin Tissoires 		if (type != HID_BPF_PROG_TYPE_UNDEF && entry->type != type)
88f5c27da4SBenjamin Tissoires 			continue;
89f5c27da4SBenjamin Tissoires 
90f5c27da4SBenjamin Tissoires 		if (hdev && entry->hdev != hdev)
91f5c27da4SBenjamin Tissoires 			continue;
92f5c27da4SBenjamin Tissoires 
93f5c27da4SBenjamin Tissoires 		if (prog && entry->prog != prog)
94f5c27da4SBenjamin Tissoires 			continue;
95f5c27da4SBenjamin Tissoires 
96f5c27da4SBenjamin Tissoires 		n++;
97f5c27da4SBenjamin Tissoires 	}
98f5c27da4SBenjamin Tissoires 
99f5c27da4SBenjamin Tissoires 	return n;
100f5c27da4SBenjamin Tissoires }
101f5c27da4SBenjamin Tissoires 
__hid_bpf_tail_call(struct hid_bpf_ctx * ctx)102f5c27da4SBenjamin Tissoires __weak noinline int __hid_bpf_tail_call(struct hid_bpf_ctx *ctx)
103f5c27da4SBenjamin Tissoires {
104f5c27da4SBenjamin Tissoires 	return 0;
105f5c27da4SBenjamin Tissoires }
106f5c27da4SBenjamin Tissoires 
hid_bpf_prog_run(struct hid_device * hdev,enum hid_bpf_prog_type type,struct hid_bpf_ctx_kern * ctx_kern)107f5c27da4SBenjamin Tissoires int hid_bpf_prog_run(struct hid_device *hdev, enum hid_bpf_prog_type type,
108f5c27da4SBenjamin Tissoires 		     struct hid_bpf_ctx_kern *ctx_kern)
109f5c27da4SBenjamin Tissoires {
110f5c27da4SBenjamin Tissoires 	struct hid_bpf_prog_list *prog_list;
111f5c27da4SBenjamin Tissoires 	int i, idx, err = 0;
112f5c27da4SBenjamin Tissoires 
113f5c27da4SBenjamin Tissoires 	rcu_read_lock();
114f5c27da4SBenjamin Tissoires 	prog_list = rcu_dereference(hdev->bpf.progs[type]);
115f5c27da4SBenjamin Tissoires 
116f5c27da4SBenjamin Tissoires 	if (!prog_list)
117f5c27da4SBenjamin Tissoires 		goto out_unlock;
118f5c27da4SBenjamin Tissoires 
119f5c27da4SBenjamin Tissoires 	for (i = 0; i < prog_list->prog_cnt; i++) {
120f5c27da4SBenjamin Tissoires 		idx = prog_list->prog_idx[i];
121f5c27da4SBenjamin Tissoires 
122f5c27da4SBenjamin Tissoires 		if (!test_bit(idx, jmp_table.enabled))
123f5c27da4SBenjamin Tissoires 			continue;
124f5c27da4SBenjamin Tissoires 
125f5c27da4SBenjamin Tissoires 		ctx_kern->ctx.index = idx;
126f5c27da4SBenjamin Tissoires 		err = __hid_bpf_tail_call(&ctx_kern->ctx);
127658ee5a6SBenjamin Tissoires 		if (err < 0)
128f5c27da4SBenjamin Tissoires 			break;
129658ee5a6SBenjamin Tissoires 		if (err)
130658ee5a6SBenjamin Tissoires 			ctx_kern->ctx.retval = err;
131f5c27da4SBenjamin Tissoires 	}
132f5c27da4SBenjamin Tissoires 
133f5c27da4SBenjamin Tissoires  out_unlock:
134f5c27da4SBenjamin Tissoires 	rcu_read_unlock();
135f5c27da4SBenjamin Tissoires 
136f5c27da4SBenjamin Tissoires 	return err;
137f5c27da4SBenjamin Tissoires }
138f5c27da4SBenjamin Tissoires 
139f5c27da4SBenjamin Tissoires /*
140f5c27da4SBenjamin Tissoires  * assign the list of programs attached to a given hid device.
141f5c27da4SBenjamin Tissoires  */
__hid_bpf_set_hdev_progs(struct hid_device * hdev,struct hid_bpf_prog_list * new_list,enum hid_bpf_prog_type type)142f5c27da4SBenjamin Tissoires static void __hid_bpf_set_hdev_progs(struct hid_device *hdev, struct hid_bpf_prog_list *new_list,
143f5c27da4SBenjamin Tissoires 				     enum hid_bpf_prog_type type)
144f5c27da4SBenjamin Tissoires {
145f5c27da4SBenjamin Tissoires 	struct hid_bpf_prog_list *old_list;
146f5c27da4SBenjamin Tissoires 
147f5c27da4SBenjamin Tissoires 	spin_lock(&hdev->bpf.progs_lock);
148f5c27da4SBenjamin Tissoires 	old_list = rcu_dereference_protected(hdev->bpf.progs[type],
149f5c27da4SBenjamin Tissoires 					     lockdep_is_held(&hdev->bpf.progs_lock));
150f5c27da4SBenjamin Tissoires 	rcu_assign_pointer(hdev->bpf.progs[type], new_list);
151f5c27da4SBenjamin Tissoires 	spin_unlock(&hdev->bpf.progs_lock);
152f5c27da4SBenjamin Tissoires 	synchronize_rcu();
153f5c27da4SBenjamin Tissoires 
154f5c27da4SBenjamin Tissoires 	kfree(old_list);
155f5c27da4SBenjamin Tissoires }
156f5c27da4SBenjamin Tissoires 
157f5c27da4SBenjamin Tissoires /*
158f5c27da4SBenjamin Tissoires  * allocate and populate the list of programs attached to a given hid device.
159f5c27da4SBenjamin Tissoires  *
160f5c27da4SBenjamin Tissoires  * Must be called under lock.
161f5c27da4SBenjamin Tissoires  */
hid_bpf_populate_hdev(struct hid_device * hdev,enum hid_bpf_prog_type type)162f5c27da4SBenjamin Tissoires static int hid_bpf_populate_hdev(struct hid_device *hdev, enum hid_bpf_prog_type type)
163f5c27da4SBenjamin Tissoires {
164f5c27da4SBenjamin Tissoires 	struct hid_bpf_prog_list *new_list;
165f5c27da4SBenjamin Tissoires 	int i;
166f5c27da4SBenjamin Tissoires 
167f5c27da4SBenjamin Tissoires 	if (type >= HID_BPF_PROG_TYPE_MAX || !hdev)
168f5c27da4SBenjamin Tissoires 		return -EINVAL;
169f5c27da4SBenjamin Tissoires 
170f5c27da4SBenjamin Tissoires 	if (hdev->bpf.destroyed)
171f5c27da4SBenjamin Tissoires 		return 0;
172f5c27da4SBenjamin Tissoires 
173f5c27da4SBenjamin Tissoires 	new_list = kzalloc(sizeof(*new_list), GFP_KERNEL);
174f5c27da4SBenjamin Tissoires 	if (!new_list)
175f5c27da4SBenjamin Tissoires 		return -ENOMEM;
176f5c27da4SBenjamin Tissoires 
177f5c27da4SBenjamin Tissoires 	FOR_ENTRIES(i, jmp_table.tail, jmp_table.head) {
178f5c27da4SBenjamin Tissoires 		struct hid_bpf_prog_entry *entry = &jmp_table.entries[i];
179f5c27da4SBenjamin Tissoires 
180f5c27da4SBenjamin Tissoires 		if (entry->type == type && entry->hdev == hdev &&
181f5c27da4SBenjamin Tissoires 		    test_bit(entry->idx, jmp_table.enabled))
182f5c27da4SBenjamin Tissoires 			new_list->prog_idx[new_list->prog_cnt++] = entry->idx;
183f5c27da4SBenjamin Tissoires 	}
184f5c27da4SBenjamin Tissoires 
185f5c27da4SBenjamin Tissoires 	__hid_bpf_set_hdev_progs(hdev, new_list, type);
186f5c27da4SBenjamin Tissoires 
187f5c27da4SBenjamin Tissoires 	return 0;
188f5c27da4SBenjamin Tissoires }
189f5c27da4SBenjamin Tissoires 
__hid_bpf_do_release_prog(int map_fd,unsigned int idx)190f5c27da4SBenjamin Tissoires static void __hid_bpf_do_release_prog(int map_fd, unsigned int idx)
191f5c27da4SBenjamin Tissoires {
192f5c27da4SBenjamin Tissoires 	skel_map_delete_elem(map_fd, &idx);
193f5c27da4SBenjamin Tissoires 	jmp_table.progs[idx] = NULL;
194f5c27da4SBenjamin Tissoires }
195f5c27da4SBenjamin Tissoires 
hid_bpf_release_progs(struct work_struct * work)196f5c27da4SBenjamin Tissoires static void hid_bpf_release_progs(struct work_struct *work)
197f5c27da4SBenjamin Tissoires {
198f5c27da4SBenjamin Tissoires 	int i, j, n, map_fd = -1;
199*467fce63SBenjamin Tissoires 	bool hdev_destroyed;
200f5c27da4SBenjamin Tissoires 
201f5c27da4SBenjamin Tissoires 	if (!jmp_table.map)
202f5c27da4SBenjamin Tissoires 		return;
203f5c27da4SBenjamin Tissoires 
204f5c27da4SBenjamin Tissoires 	/* retrieve a fd of our prog_array map in BPF */
205f5c27da4SBenjamin Tissoires 	map_fd = skel_map_get_fd_by_id(jmp_table.map->id);
206f5c27da4SBenjamin Tissoires 	if (map_fd < 0)
207f5c27da4SBenjamin Tissoires 		return;
208f5c27da4SBenjamin Tissoires 
209f5c27da4SBenjamin Tissoires 	mutex_lock(&hid_bpf_attach_lock); /* protects against attaching new programs */
210f5c27da4SBenjamin Tissoires 
211f5c27da4SBenjamin Tissoires 	/* detach unused progs from HID devices */
212f5c27da4SBenjamin Tissoires 	FOR_ENTRIES(i, jmp_table.tail, jmp_table.head) {
213f5c27da4SBenjamin Tissoires 		struct hid_bpf_prog_entry *entry = &jmp_table.entries[i];
214f5c27da4SBenjamin Tissoires 		enum hid_bpf_prog_type type;
215f5c27da4SBenjamin Tissoires 		struct hid_device *hdev;
216f5c27da4SBenjamin Tissoires 
217f5c27da4SBenjamin Tissoires 		if (test_bit(entry->idx, jmp_table.enabled))
218f5c27da4SBenjamin Tissoires 			continue;
219f5c27da4SBenjamin Tissoires 
220f5c27da4SBenjamin Tissoires 		/* we have an attached prog */
221f5c27da4SBenjamin Tissoires 		if (entry->hdev) {
222f5c27da4SBenjamin Tissoires 			hdev = entry->hdev;
223f5c27da4SBenjamin Tissoires 			type = entry->type;
224*467fce63SBenjamin Tissoires 			/*
225*467fce63SBenjamin Tissoires 			 * hdev is still valid, even if we are called after hid_destroy_device():
226*467fce63SBenjamin Tissoires 			 * when hid_bpf_attach() gets called, it takes a ref on the dev through
227*467fce63SBenjamin Tissoires 			 * bus_find_device()
228*467fce63SBenjamin Tissoires 			 */
229*467fce63SBenjamin Tissoires 			hdev_destroyed = hdev->bpf.destroyed;
230f5c27da4SBenjamin Tissoires 
231f5c27da4SBenjamin Tissoires 			hid_bpf_populate_hdev(hdev, type);
232f5c27da4SBenjamin Tissoires 
233f5c27da4SBenjamin Tissoires 			/* mark all other disabled progs from hdev of the given type as detached */
234f5c27da4SBenjamin Tissoires 			FOR_ENTRIES(j, i, jmp_table.head) {
235f5c27da4SBenjamin Tissoires 				struct hid_bpf_prog_entry *next;
236f5c27da4SBenjamin Tissoires 
237f5c27da4SBenjamin Tissoires 				next = &jmp_table.entries[j];
238f5c27da4SBenjamin Tissoires 
239f5c27da4SBenjamin Tissoires 				if (test_bit(next->idx, jmp_table.enabled))
240f5c27da4SBenjamin Tissoires 					continue;
241f5c27da4SBenjamin Tissoires 
242*467fce63SBenjamin Tissoires 				if (next->hdev == hdev && next->type == type) {
243*467fce63SBenjamin Tissoires 					/*
244*467fce63SBenjamin Tissoires 					 * clear the hdev reference and decrement the device ref
245*467fce63SBenjamin Tissoires 					 * that was taken during bus_find_device() while calling
246*467fce63SBenjamin Tissoires 					 * hid_bpf_attach()
247*467fce63SBenjamin Tissoires 					 */
248f5c27da4SBenjamin Tissoires 					next->hdev = NULL;
249*467fce63SBenjamin Tissoires 					put_device(&hdev->dev);
250*467fce63SBenjamin Tissoires 				}
251f5c27da4SBenjamin Tissoires 			}
252ad190df1SBenjamin Tissoires 
253*467fce63SBenjamin Tissoires 			/* if type was rdesc fixup and the device is not gone, reconnect device */
254*467fce63SBenjamin Tissoires 			if (type == HID_BPF_PROG_TYPE_RDESC_FIXUP && !hdev_destroyed)
255ad190df1SBenjamin Tissoires 				hid_bpf_reconnect(hdev);
256f5c27da4SBenjamin Tissoires 		}
257f5c27da4SBenjamin Tissoires 	}
258f5c27da4SBenjamin Tissoires 
259f5c27da4SBenjamin Tissoires 	/* remove all unused progs from the jump table */
260f5c27da4SBenjamin Tissoires 	FOR_ENTRIES(i, jmp_table.tail, jmp_table.head) {
261f5c27da4SBenjamin Tissoires 		struct hid_bpf_prog_entry *entry = &jmp_table.entries[i];
262f5c27da4SBenjamin Tissoires 
263f5c27da4SBenjamin Tissoires 		if (test_bit(entry->idx, jmp_table.enabled))
264f5c27da4SBenjamin Tissoires 			continue;
265f5c27da4SBenjamin Tissoires 
266f5c27da4SBenjamin Tissoires 		if (entry->prog)
267f5c27da4SBenjamin Tissoires 			__hid_bpf_do_release_prog(map_fd, entry->idx);
268f5c27da4SBenjamin Tissoires 	}
269f5c27da4SBenjamin Tissoires 
270f5c27da4SBenjamin Tissoires 	/* compact the entry list */
271f5c27da4SBenjamin Tissoires 	n = jmp_table.tail;
272f5c27da4SBenjamin Tissoires 	FOR_ENTRIES(i, jmp_table.tail, jmp_table.head) {
273f5c27da4SBenjamin Tissoires 		struct hid_bpf_prog_entry *entry = &jmp_table.entries[i];
274f5c27da4SBenjamin Tissoires 
275f5c27da4SBenjamin Tissoires 		if (!test_bit(entry->idx, jmp_table.enabled))
276f5c27da4SBenjamin Tissoires 			continue;
277f5c27da4SBenjamin Tissoires 
278f5c27da4SBenjamin Tissoires 		jmp_table.entries[n] = jmp_table.entries[i];
279f5c27da4SBenjamin Tissoires 		n = NEXT(n);
280f5c27da4SBenjamin Tissoires 	}
281f5c27da4SBenjamin Tissoires 
282f5c27da4SBenjamin Tissoires 	jmp_table.head = n;
283f5c27da4SBenjamin Tissoires 
284f5c27da4SBenjamin Tissoires 	mutex_unlock(&hid_bpf_attach_lock);
285f5c27da4SBenjamin Tissoires 
286f5c27da4SBenjamin Tissoires 	if (map_fd >= 0)
287f5c27da4SBenjamin Tissoires 		close_fd(map_fd);
288f5c27da4SBenjamin Tissoires }
289f5c27da4SBenjamin Tissoires 
hid_bpf_release_prog_at(int idx)290f5c27da4SBenjamin Tissoires static void hid_bpf_release_prog_at(int idx)
291f5c27da4SBenjamin Tissoires {
292f5c27da4SBenjamin Tissoires 	int map_fd = -1;
293f5c27da4SBenjamin Tissoires 
294f5c27da4SBenjamin Tissoires 	/* retrieve a fd of our prog_array map in BPF */
295f5c27da4SBenjamin Tissoires 	map_fd = skel_map_get_fd_by_id(jmp_table.map->id);
296f5c27da4SBenjamin Tissoires 	if (map_fd < 0)
297f5c27da4SBenjamin Tissoires 		return;
298f5c27da4SBenjamin Tissoires 
299f5c27da4SBenjamin Tissoires 	__hid_bpf_do_release_prog(map_fd, idx);
300f5c27da4SBenjamin Tissoires 
301f5c27da4SBenjamin Tissoires 	close(map_fd);
302f5c27da4SBenjamin Tissoires }
303f5c27da4SBenjamin Tissoires 
304f5c27da4SBenjamin Tissoires /*
305f5c27da4SBenjamin Tissoires  * Insert the given BPF program represented by its fd in the jmp table.
306f5c27da4SBenjamin Tissoires  * Returns the index in the jump table or a negative error.
307f5c27da4SBenjamin Tissoires  */
hid_bpf_insert_prog(int prog_fd,struct bpf_prog * prog)308f5c27da4SBenjamin Tissoires static int hid_bpf_insert_prog(int prog_fd, struct bpf_prog *prog)
309f5c27da4SBenjamin Tissoires {
3100baef373SBenjamin Tissoires 	int i, index = -1, map_fd = -1, err = -EINVAL;
311f5c27da4SBenjamin Tissoires 
312f5c27da4SBenjamin Tissoires 	/* retrieve a fd of our prog_array map in BPF */
313f5c27da4SBenjamin Tissoires 	map_fd = skel_map_get_fd_by_id(jmp_table.map->id);
314f5c27da4SBenjamin Tissoires 
3150baef373SBenjamin Tissoires 	if (map_fd < 0) {
316f5c27da4SBenjamin Tissoires 		err = -EINVAL;
317f5c27da4SBenjamin Tissoires 		goto out;
318f5c27da4SBenjamin Tissoires 	}
319f5c27da4SBenjamin Tissoires 
3200baef373SBenjamin Tissoires 	/* find the first available index in the jmp_table */
321f5c27da4SBenjamin Tissoires 	for (i = 0; i < HID_BPF_MAX_PROGS; i++) {
322f5c27da4SBenjamin Tissoires 		if (!jmp_table.progs[i] && index < 0) {
323f5c27da4SBenjamin Tissoires 			/* mark the index as used */
324f5c27da4SBenjamin Tissoires 			jmp_table.progs[i] = prog;
325f5c27da4SBenjamin Tissoires 			index = i;
326f5c27da4SBenjamin Tissoires 			__set_bit(i, jmp_table.enabled);
327f5c27da4SBenjamin Tissoires 		}
328f5c27da4SBenjamin Tissoires 	}
329f5c27da4SBenjamin Tissoires 	if (index < 0) {
330f5c27da4SBenjamin Tissoires 		err = -ENOMEM;
331f5c27da4SBenjamin Tissoires 		goto out;
332f5c27da4SBenjamin Tissoires 	}
333f5c27da4SBenjamin Tissoires 
334f5c27da4SBenjamin Tissoires 	/* insert the program in the jump table */
335f5c27da4SBenjamin Tissoires 	err = skel_map_update_elem(map_fd, &index, &prog_fd, 0);
336f5c27da4SBenjamin Tissoires 	if (err)
337f5c27da4SBenjamin Tissoires 		goto out;
338f5c27da4SBenjamin Tissoires 
339f5c27da4SBenjamin Tissoires 	/* return the index */
340f5c27da4SBenjamin Tissoires 	err = index;
341f5c27da4SBenjamin Tissoires 
342f5c27da4SBenjamin Tissoires  out:
343f5c27da4SBenjamin Tissoires 	if (err < 0)
344f5c27da4SBenjamin Tissoires 		__hid_bpf_do_release_prog(map_fd, index);
345f5c27da4SBenjamin Tissoires 	if (map_fd >= 0)
346f5c27da4SBenjamin Tissoires 		close_fd(map_fd);
347f5c27da4SBenjamin Tissoires 	return err;
348f5c27da4SBenjamin Tissoires }
349f5c27da4SBenjamin Tissoires 
hid_bpf_get_prog_attach_type(struct bpf_prog * prog)350d83a7e59SBenjamin Tissoires int hid_bpf_get_prog_attach_type(struct bpf_prog *prog)
351f5c27da4SBenjamin Tissoires {
352f5c27da4SBenjamin Tissoires 	int prog_type = HID_BPF_PROG_TYPE_UNDEF;
353d83a7e59SBenjamin Tissoires 	int i;
354f5c27da4SBenjamin Tissoires 
355f5c27da4SBenjamin Tissoires 	for (i = 0; i < HID_BPF_PROG_TYPE_MAX; i++) {
356f5c27da4SBenjamin Tissoires 		if (hid_bpf_btf_ids[i] == prog->aux->attach_btf_id) {
357f5c27da4SBenjamin Tissoires 			prog_type = i;
358f5c27da4SBenjamin Tissoires 			break;
359f5c27da4SBenjamin Tissoires 		}
360f5c27da4SBenjamin Tissoires 	}
361f5c27da4SBenjamin Tissoires 
362f5c27da4SBenjamin Tissoires 	return prog_type;
363f5c27da4SBenjamin Tissoires }
364f5c27da4SBenjamin Tissoires 
hid_bpf_link_release(struct bpf_link * link)3654b9a3f49SBenjamin Tissoires static void hid_bpf_link_release(struct bpf_link *link)
3664b9a3f49SBenjamin Tissoires {
3674b9a3f49SBenjamin Tissoires 	struct hid_bpf_link *hid_link =
3684b9a3f49SBenjamin Tissoires 		container_of(link, struct hid_bpf_link, link);
3694b9a3f49SBenjamin Tissoires 
3704b9a3f49SBenjamin Tissoires 	__clear_bit(hid_link->hid_table_index, jmp_table.enabled);
3714b9a3f49SBenjamin Tissoires 	schedule_work(&release_work);
3724b9a3f49SBenjamin Tissoires }
3734b9a3f49SBenjamin Tissoires 
hid_bpf_link_dealloc(struct bpf_link * link)3744b9a3f49SBenjamin Tissoires static void hid_bpf_link_dealloc(struct bpf_link *link)
3754b9a3f49SBenjamin Tissoires {
3764b9a3f49SBenjamin Tissoires 	struct hid_bpf_link *hid_link =
3774b9a3f49SBenjamin Tissoires 		container_of(link, struct hid_bpf_link, link);
3784b9a3f49SBenjamin Tissoires 
3794b9a3f49SBenjamin Tissoires 	kfree(hid_link);
3804b9a3f49SBenjamin Tissoires }
3814b9a3f49SBenjamin Tissoires 
hid_bpf_link_show_fdinfo(const struct bpf_link * link,struct seq_file * seq)3824b9a3f49SBenjamin Tissoires static void hid_bpf_link_show_fdinfo(const struct bpf_link *link,
3834b9a3f49SBenjamin Tissoires 					 struct seq_file *seq)
3844b9a3f49SBenjamin Tissoires {
3854b9a3f49SBenjamin Tissoires 	seq_printf(seq,
3864b9a3f49SBenjamin Tissoires 		   "attach_type:\tHID-BPF\n");
3874b9a3f49SBenjamin Tissoires }
3884b9a3f49SBenjamin Tissoires 
3894b9a3f49SBenjamin Tissoires static const struct bpf_link_ops hid_bpf_link_lops = {
3904b9a3f49SBenjamin Tissoires 	.release = hid_bpf_link_release,
3914b9a3f49SBenjamin Tissoires 	.dealloc = hid_bpf_link_dealloc,
3924b9a3f49SBenjamin Tissoires 	.show_fdinfo = hid_bpf_link_show_fdinfo,
3934b9a3f49SBenjamin Tissoires };
3944b9a3f49SBenjamin Tissoires 
395f5c27da4SBenjamin Tissoires /* called from syscall */
396f5c27da4SBenjamin Tissoires noinline int
__hid_bpf_attach_prog(struct hid_device * hdev,enum hid_bpf_prog_type prog_type,int prog_fd,struct bpf_prog * prog,__u32 flags)397f5c27da4SBenjamin Tissoires __hid_bpf_attach_prog(struct hid_device *hdev, enum hid_bpf_prog_type prog_type,
398d83a7e59SBenjamin Tissoires 		      int prog_fd, struct bpf_prog *prog, __u32 flags)
399f5c27da4SBenjamin Tissoires {
4004b9a3f49SBenjamin Tissoires 	struct bpf_link_primer link_primer;
4014b9a3f49SBenjamin Tissoires 	struct hid_bpf_link *link;
402f5c27da4SBenjamin Tissoires 	struct hid_bpf_prog_entry *prog_entry;
4034b9a3f49SBenjamin Tissoires 	int cnt, err = -EINVAL, prog_table_idx = -1;
404f5c27da4SBenjamin Tissoires 
405f5c27da4SBenjamin Tissoires 	mutex_lock(&hid_bpf_attach_lock);
406f5c27da4SBenjamin Tissoires 
4074b9a3f49SBenjamin Tissoires 	link = kzalloc(sizeof(*link), GFP_USER);
4084b9a3f49SBenjamin Tissoires 	if (!link) {
4094b9a3f49SBenjamin Tissoires 		err = -ENOMEM;
4104b9a3f49SBenjamin Tissoires 		goto err_unlock;
4114b9a3f49SBenjamin Tissoires 	}
4124b9a3f49SBenjamin Tissoires 
4134b9a3f49SBenjamin Tissoires 	bpf_link_init(&link->link, BPF_LINK_TYPE_UNSPEC,
4144b9a3f49SBenjamin Tissoires 		      &hid_bpf_link_lops, prog);
4154b9a3f49SBenjamin Tissoires 
416f5c27da4SBenjamin Tissoires 	/* do not attach too many programs to a given HID device */
417f5c27da4SBenjamin Tissoires 	cnt = hid_bpf_program_count(hdev, NULL, prog_type);
418f5c27da4SBenjamin Tissoires 	if (cnt < 0) {
419f5c27da4SBenjamin Tissoires 		err = cnt;
4204b9a3f49SBenjamin Tissoires 		goto err_unlock;
421f5c27da4SBenjamin Tissoires 	}
422f5c27da4SBenjamin Tissoires 
423f5c27da4SBenjamin Tissoires 	if (cnt >= hid_bpf_max_programs(prog_type)) {
424f5c27da4SBenjamin Tissoires 		err = -E2BIG;
4254b9a3f49SBenjamin Tissoires 		goto err_unlock;
426f5c27da4SBenjamin Tissoires 	}
427f5c27da4SBenjamin Tissoires 
4284b9a3f49SBenjamin Tissoires 	prog_table_idx = hid_bpf_insert_prog(prog_fd, prog);
429f5c27da4SBenjamin Tissoires 	/* if the jmp table is full, abort */
4304b9a3f49SBenjamin Tissoires 	if (prog_table_idx < 0) {
4314b9a3f49SBenjamin Tissoires 		err = prog_table_idx;
4324b9a3f49SBenjamin Tissoires 		goto err_unlock;
433f5c27da4SBenjamin Tissoires 	}
434f5c27da4SBenjamin Tissoires 
435f5c27da4SBenjamin Tissoires 	if (flags & HID_BPF_FLAG_INSERT_HEAD) {
436f5c27da4SBenjamin Tissoires 		/* take the previous prog_entry slot */
437f5c27da4SBenjamin Tissoires 		jmp_table.tail = PREV(jmp_table.tail);
438f5c27da4SBenjamin Tissoires 		prog_entry = &jmp_table.entries[jmp_table.tail];
439f5c27da4SBenjamin Tissoires 	} else {
440f5c27da4SBenjamin Tissoires 		/* take the next prog_entry slot */
441f5c27da4SBenjamin Tissoires 		prog_entry = &jmp_table.entries[jmp_table.head];
442f5c27da4SBenjamin Tissoires 		jmp_table.head = NEXT(jmp_table.head);
443f5c27da4SBenjamin Tissoires 	}
444f5c27da4SBenjamin Tissoires 
445f5c27da4SBenjamin Tissoires 	/* we steal the ref here */
446f5c27da4SBenjamin Tissoires 	prog_entry->prog = prog;
4474b9a3f49SBenjamin Tissoires 	prog_entry->idx = prog_table_idx;
448f5c27da4SBenjamin Tissoires 	prog_entry->hdev = hdev;
449f5c27da4SBenjamin Tissoires 	prog_entry->type = prog_type;
450f5c27da4SBenjamin Tissoires 
451f5c27da4SBenjamin Tissoires 	/* finally store the index in the device list */
452f5c27da4SBenjamin Tissoires 	err = hid_bpf_populate_hdev(hdev, prog_type);
4534b9a3f49SBenjamin Tissoires 	if (err) {
4544b9a3f49SBenjamin Tissoires 		hid_bpf_release_prog_at(prog_table_idx);
4554b9a3f49SBenjamin Tissoires 		goto err_unlock;
4564b9a3f49SBenjamin Tissoires 	}
457f5c27da4SBenjamin Tissoires 
4584b9a3f49SBenjamin Tissoires 	link->hid_table_index = prog_table_idx;
4594b9a3f49SBenjamin Tissoires 
4604b9a3f49SBenjamin Tissoires 	err = bpf_link_prime(&link->link, &link_primer);
4614b9a3f49SBenjamin Tissoires 	if (err)
4624b9a3f49SBenjamin Tissoires 		goto err_unlock;
4634b9a3f49SBenjamin Tissoires 
464f5c27da4SBenjamin Tissoires 	mutex_unlock(&hid_bpf_attach_lock);
465f5c27da4SBenjamin Tissoires 
4664b9a3f49SBenjamin Tissoires 	return bpf_link_settle(&link_primer);
4674b9a3f49SBenjamin Tissoires 
4684b9a3f49SBenjamin Tissoires  err_unlock:
4694b9a3f49SBenjamin Tissoires 	mutex_unlock(&hid_bpf_attach_lock);
4704b9a3f49SBenjamin Tissoires 
4714b9a3f49SBenjamin Tissoires 	kfree(link);
472f5c27da4SBenjamin Tissoires 
473f5c27da4SBenjamin Tissoires 	return err;
474f5c27da4SBenjamin Tissoires }
475f5c27da4SBenjamin Tissoires 
__hid_bpf_destroy_device(struct hid_device * hdev)476f5c27da4SBenjamin Tissoires void __hid_bpf_destroy_device(struct hid_device *hdev)
477f5c27da4SBenjamin Tissoires {
478f5c27da4SBenjamin Tissoires 	int type, i;
479f5c27da4SBenjamin Tissoires 	struct hid_bpf_prog_list *prog_list;
480f5c27da4SBenjamin Tissoires 
481f5c27da4SBenjamin Tissoires 	rcu_read_lock();
482f5c27da4SBenjamin Tissoires 
483f5c27da4SBenjamin Tissoires 	for (type = 0; type < HID_BPF_PROG_TYPE_MAX; type++) {
484f5c27da4SBenjamin Tissoires 		prog_list = rcu_dereference(hdev->bpf.progs[type]);
485f5c27da4SBenjamin Tissoires 
486f5c27da4SBenjamin Tissoires 		if (!prog_list)
487f5c27da4SBenjamin Tissoires 			continue;
488f5c27da4SBenjamin Tissoires 
489f5c27da4SBenjamin Tissoires 		for (i = 0; i < prog_list->prog_cnt; i++)
490f5c27da4SBenjamin Tissoires 			__clear_bit(prog_list->prog_idx[i], jmp_table.enabled);
491f5c27da4SBenjamin Tissoires 	}
492f5c27da4SBenjamin Tissoires 
493f5c27da4SBenjamin Tissoires 	rcu_read_unlock();
494f5c27da4SBenjamin Tissoires 
495f5c27da4SBenjamin Tissoires 	for (type = 0; type < HID_BPF_PROG_TYPE_MAX; type++)
496f5c27da4SBenjamin Tissoires 		__hid_bpf_set_hdev_progs(hdev, NULL, type);
497f5c27da4SBenjamin Tissoires 
498f5c27da4SBenjamin Tissoires 	/* schedule release of all detached progs */
499f5c27da4SBenjamin Tissoires 	schedule_work(&release_work);
500f5c27da4SBenjamin Tissoires }
501f5c27da4SBenjamin Tissoires 
5024b9a3f49SBenjamin Tissoires #define HID_BPF_PROGS_COUNT 1
503f5c27da4SBenjamin Tissoires 
504f5c27da4SBenjamin Tissoires static struct bpf_link *links[HID_BPF_PROGS_COUNT];
505f5c27da4SBenjamin Tissoires static struct entrypoints_bpf *skel;
506f5c27da4SBenjamin Tissoires 
hid_bpf_free_links_and_skel(void)507f5c27da4SBenjamin Tissoires void hid_bpf_free_links_and_skel(void)
508f5c27da4SBenjamin Tissoires {
509f5c27da4SBenjamin Tissoires 	int i;
510f5c27da4SBenjamin Tissoires 
511f5c27da4SBenjamin Tissoires 	/* the following is enough to release all programs attached to hid */
512f5c27da4SBenjamin Tissoires 	if (jmp_table.map)
513f5c27da4SBenjamin Tissoires 		bpf_map_put_with_uref(jmp_table.map);
514f5c27da4SBenjamin Tissoires 
515f5c27da4SBenjamin Tissoires 	for (i = 0; i < ARRAY_SIZE(links); i++) {
516f5c27da4SBenjamin Tissoires 		if (!IS_ERR_OR_NULL(links[i]))
517f5c27da4SBenjamin Tissoires 			bpf_link_put(links[i]);
518f5c27da4SBenjamin Tissoires 	}
519f5c27da4SBenjamin Tissoires 	entrypoints_bpf__destroy(skel);
520f5c27da4SBenjamin Tissoires }
521f5c27da4SBenjamin Tissoires 
522f5c27da4SBenjamin Tissoires #define ATTACH_AND_STORE_LINK(__name) do {					\
523f5c27da4SBenjamin Tissoires 	err = entrypoints_bpf__##__name##__attach(skel);			\
524f5c27da4SBenjamin Tissoires 	if (err)								\
525f5c27da4SBenjamin Tissoires 		goto out;							\
526f5c27da4SBenjamin Tissoires 										\
527f5c27da4SBenjamin Tissoires 	links[idx] = bpf_link_get_from_fd(skel->links.__name##_fd);		\
528f5c27da4SBenjamin Tissoires 	if (IS_ERR(links[idx])) {						\
529f5c27da4SBenjamin Tissoires 		err = PTR_ERR(links[idx]);					\
530f5c27da4SBenjamin Tissoires 		goto out;							\
531f5c27da4SBenjamin Tissoires 	}									\
532f5c27da4SBenjamin Tissoires 										\
533f5c27da4SBenjamin Tissoires 	/* Avoid taking over stdin/stdout/stderr of init process. Zeroing out	\
534f5c27da4SBenjamin Tissoires 	 * makes skel_closenz() a no-op later in iterators_bpf__destroy().	\
535f5c27da4SBenjamin Tissoires 	 */									\
536f5c27da4SBenjamin Tissoires 	close_fd(skel->links.__name##_fd);					\
537f5c27da4SBenjamin Tissoires 	skel->links.__name##_fd = 0;						\
538f5c27da4SBenjamin Tissoires 	idx++;									\
539f5c27da4SBenjamin Tissoires } while (0)
540f5c27da4SBenjamin Tissoires 
hid_bpf_preload_skel(void)541f5c27da4SBenjamin Tissoires int hid_bpf_preload_skel(void)
542f5c27da4SBenjamin Tissoires {
543f5c27da4SBenjamin Tissoires 	int err, idx = 0;
544f5c27da4SBenjamin Tissoires 
545f5c27da4SBenjamin Tissoires 	skel = entrypoints_bpf__open();
546f5c27da4SBenjamin Tissoires 	if (!skel)
547f5c27da4SBenjamin Tissoires 		return -ENOMEM;
548f5c27da4SBenjamin Tissoires 
549f5c27da4SBenjamin Tissoires 	err = entrypoints_bpf__load(skel);
550f5c27da4SBenjamin Tissoires 	if (err)
551f5c27da4SBenjamin Tissoires 		goto out;
552f5c27da4SBenjamin Tissoires 
553f5c27da4SBenjamin Tissoires 	jmp_table.map = bpf_map_get_with_uref(skel->maps.hid_jmp_table.map_fd);
554f5c27da4SBenjamin Tissoires 	if (IS_ERR(jmp_table.map)) {
555f5c27da4SBenjamin Tissoires 		err = PTR_ERR(jmp_table.map);
556f5c27da4SBenjamin Tissoires 		goto out;
557f5c27da4SBenjamin Tissoires 	}
558f5c27da4SBenjamin Tissoires 
559f5c27da4SBenjamin Tissoires 	ATTACH_AND_STORE_LINK(hid_tail_call);
560f5c27da4SBenjamin Tissoires 
561f5c27da4SBenjamin Tissoires 	return 0;
562f5c27da4SBenjamin Tissoires out:
563f5c27da4SBenjamin Tissoires 	hid_bpf_free_links_and_skel();
564f5c27da4SBenjamin Tissoires 	return err;
565f5c27da4SBenjamin Tissoires }
566