xref: /linux/drivers/hid/bpf/hid_bpf_struct_ops.c (revision f23aa4c0761a70bfd046dd5755281667f0769a94)
1ebc0d809SBenjamin Tissoires // SPDX-License-Identifier: GPL-2.0-only
2ebc0d809SBenjamin Tissoires 
3ebc0d809SBenjamin Tissoires /*
4ebc0d809SBenjamin Tissoires  *  HID-BPF support for Linux
5ebc0d809SBenjamin Tissoires  *
6ebc0d809SBenjamin Tissoires  *  Copyright (c) 2024 Benjamin Tissoires
7ebc0d809SBenjamin Tissoires  */
8ebc0d809SBenjamin Tissoires 
9ebc0d809SBenjamin Tissoires #include <linux/bitops.h>
10ebc0d809SBenjamin Tissoires #include <linux/bpf_verifier.h>
11ebc0d809SBenjamin Tissoires #include <linux/bpf.h>
12ebc0d809SBenjamin Tissoires #include <linux/btf.h>
13ebc0d809SBenjamin Tissoires #include <linux/btf_ids.h>
14ebc0d809SBenjamin Tissoires #include <linux/filter.h>
15ebc0d809SBenjamin Tissoires #include <linux/hid.h>
16ebc0d809SBenjamin Tissoires #include <linux/hid_bpf.h>
17ebc0d809SBenjamin Tissoires #include <linux/init.h>
18ebc0d809SBenjamin Tissoires #include <linux/module.h>
19f1a5fb6cSBenjamin Tissoires #include <linux/stddef.h>
20ebc0d809SBenjamin Tissoires #include <linux/workqueue.h>
21ebc0d809SBenjamin Tissoires #include "hid_bpf_dispatch.h"
22ebc0d809SBenjamin Tissoires 
23ebc0d809SBenjamin Tissoires static struct btf *hid_bpf_ops_btf;
24ebc0d809SBenjamin Tissoires 
hid_bpf_ops_init(struct btf * btf)25ebc0d809SBenjamin Tissoires static int hid_bpf_ops_init(struct btf *btf)
26ebc0d809SBenjamin Tissoires {
27ebc0d809SBenjamin Tissoires 	hid_bpf_ops_btf = btf;
28ebc0d809SBenjamin Tissoires 	return 0;
29ebc0d809SBenjamin Tissoires }
30ebc0d809SBenjamin Tissoires 
hid_bpf_ops_is_valid_access(int off,int size,enum bpf_access_type type,const struct bpf_prog * prog,struct bpf_insn_access_aux * info)31ebc0d809SBenjamin Tissoires static bool hid_bpf_ops_is_valid_access(int off, int size,
32ebc0d809SBenjamin Tissoires 					  enum bpf_access_type type,
33ebc0d809SBenjamin Tissoires 					  const struct bpf_prog *prog,
34ebc0d809SBenjamin Tissoires 					  struct bpf_insn_access_aux *info)
35ebc0d809SBenjamin Tissoires {
36ebc0d809SBenjamin Tissoires 	return bpf_tracing_btf_ctx_access(off, size, type, prog, info);
37ebc0d809SBenjamin Tissoires }
38ebc0d809SBenjamin Tissoires 
hid_bpf_ops_check_member(const struct btf_type * t,const struct btf_member * member,const struct bpf_prog * prog)39ebc0d809SBenjamin Tissoires static int hid_bpf_ops_check_member(const struct btf_type *t,
40ebc0d809SBenjamin Tissoires 				      const struct btf_member *member,
41ebc0d809SBenjamin Tissoires 				      const struct bpf_prog *prog)
42ebc0d809SBenjamin Tissoires {
43ebc0d809SBenjamin Tissoires 	u32 moff = __btf_member_bit_offset(t, member) / 8;
44ebc0d809SBenjamin Tissoires 
45ebc0d809SBenjamin Tissoires 	switch (moff) {
46ebc0d809SBenjamin Tissoires 	case offsetof(struct hid_bpf_ops, hid_rdesc_fixup):
478bd0488bSBenjamin Tissoires 	case offsetof(struct hid_bpf_ops, hid_hw_request):
489286675aSBenjamin Tissoires 	case offsetof(struct hid_bpf_ops, hid_hw_output_report):
49ebc0d809SBenjamin Tissoires 		break;
50ebc0d809SBenjamin Tissoires 	default:
51ebc0d809SBenjamin Tissoires 		if (prog->sleepable)
52ebc0d809SBenjamin Tissoires 			return -EINVAL;
53ebc0d809SBenjamin Tissoires 	}
54ebc0d809SBenjamin Tissoires 
55ebc0d809SBenjamin Tissoires 	return 0;
56ebc0d809SBenjamin Tissoires }
57ebc0d809SBenjamin Tissoires 
58f1a5fb6cSBenjamin Tissoires struct hid_bpf_offset_write_range {
59f1a5fb6cSBenjamin Tissoires 	const char *struct_name;
60f1a5fb6cSBenjamin Tissoires 	u32 struct_length;
61f1a5fb6cSBenjamin Tissoires 	u32 start;
62f1a5fb6cSBenjamin Tissoires 	u32 end;
63f1a5fb6cSBenjamin Tissoires };
64f1a5fb6cSBenjamin Tissoires 
hid_bpf_ops_btf_struct_access(struct bpf_verifier_log * log,const struct bpf_reg_state * reg,int off,int size)65ebc0d809SBenjamin Tissoires static int hid_bpf_ops_btf_struct_access(struct bpf_verifier_log *log,
66ebc0d809SBenjamin Tissoires 					   const struct bpf_reg_state *reg,
67ebc0d809SBenjamin Tissoires 					   int off, int size)
68ebc0d809SBenjamin Tissoires {
69f1a5fb6cSBenjamin Tissoires #define WRITE_RANGE(_name, _field, _is_string)					\
70f1a5fb6cSBenjamin Tissoires 	{									\
71f1a5fb6cSBenjamin Tissoires 		.struct_name = #_name,						\
72f1a5fb6cSBenjamin Tissoires 		.struct_length = sizeof(struct _name),				\
73f1a5fb6cSBenjamin Tissoires 		.start = offsetof(struct _name, _field),			\
74f1a5fb6cSBenjamin Tissoires 		.end = offsetofend(struct _name, _field) - !!(_is_string),	\
75f1a5fb6cSBenjamin Tissoires 	}
76f1a5fb6cSBenjamin Tissoires 
77f1a5fb6cSBenjamin Tissoires 	const struct hid_bpf_offset_write_range write_ranges[] = {
78f1a5fb6cSBenjamin Tissoires 		WRITE_RANGE(hid_bpf_ctx, retval, false),
7933c0fb85SBenjamin Tissoires 		WRITE_RANGE(hid_device, name, true),
8033c0fb85SBenjamin Tissoires 		WRITE_RANGE(hid_device, uniq, true),
8133c0fb85SBenjamin Tissoires 		WRITE_RANGE(hid_device, phys, true),
82f1a5fb6cSBenjamin Tissoires 	};
83f1a5fb6cSBenjamin Tissoires #undef WRITE_RANGE
84f1a5fb6cSBenjamin Tissoires 	const struct btf_type *state = NULL;
85ebc0d809SBenjamin Tissoires 	const struct btf_type *t;
86f1a5fb6cSBenjamin Tissoires 	const char *cur = NULL;
87f1a5fb6cSBenjamin Tissoires 	int i;
88f1a5fb6cSBenjamin Tissoires 
89f1a5fb6cSBenjamin Tissoires 	t = btf_type_by_id(reg->btf, reg->btf_id);
90f1a5fb6cSBenjamin Tissoires 
91f1a5fb6cSBenjamin Tissoires 	for (i = 0; i < ARRAY_SIZE(write_ranges); i++) {
92f1a5fb6cSBenjamin Tissoires 		const struct hid_bpf_offset_write_range *write_range = &write_ranges[i];
93ebc0d809SBenjamin Tissoires 		s32 type_id;
94ebc0d809SBenjamin Tissoires 
95f1a5fb6cSBenjamin Tissoires 		/* we already found a writeable struct, but there is a
96f1a5fb6cSBenjamin Tissoires 		 * new one, let's break the loop.
97f1a5fb6cSBenjamin Tissoires 		 */
98f1a5fb6cSBenjamin Tissoires 		if (t == state && write_range->struct_name != cur)
99f1a5fb6cSBenjamin Tissoires 			break;
100f1a5fb6cSBenjamin Tissoires 
101f1a5fb6cSBenjamin Tissoires 		/* new struct to look for */
102f1a5fb6cSBenjamin Tissoires 		if (write_range->struct_name != cur) {
103f1a5fb6cSBenjamin Tissoires 			type_id = btf_find_by_name_kind(reg->btf, write_range->struct_name,
104ebc0d809SBenjamin Tissoires 							BTF_KIND_STRUCT);
105ebc0d809SBenjamin Tissoires 			if (type_id < 0)
106ebc0d809SBenjamin Tissoires 				return -EINVAL;
107ebc0d809SBenjamin Tissoires 
108ebc0d809SBenjamin Tissoires 			state = btf_type_by_id(reg->btf, type_id);
109f1a5fb6cSBenjamin Tissoires 		}
110f1a5fb6cSBenjamin Tissoires 
111f1a5fb6cSBenjamin Tissoires 		/* this is not the struct we are looking for */
112ebc0d809SBenjamin Tissoires 		if (t != state) {
113f1a5fb6cSBenjamin Tissoires 			cur = write_range->struct_name;
114f1a5fb6cSBenjamin Tissoires 			continue;
115f1a5fb6cSBenjamin Tissoires 		}
116f1a5fb6cSBenjamin Tissoires 
117f1a5fb6cSBenjamin Tissoires 		/* first time we see this struct, check for out of bounds */
118f1a5fb6cSBenjamin Tissoires 		if (cur != write_range->struct_name &&
119f1a5fb6cSBenjamin Tissoires 		    off + size > write_range->struct_length) {
120f1a5fb6cSBenjamin Tissoires 			bpf_log(log, "write access for struct %s at off %d with size %d\n",
121f1a5fb6cSBenjamin Tissoires 				write_range->struct_name, off, size);
122ebc0d809SBenjamin Tissoires 			return -EACCES;
123ebc0d809SBenjamin Tissoires 		}
124ebc0d809SBenjamin Tissoires 
125f1a5fb6cSBenjamin Tissoires 		/* now check if we are in our boundaries */
126f1a5fb6cSBenjamin Tissoires 		if (off >= write_range->start && off + size <= write_range->end)
127ebc0d809SBenjamin Tissoires 			return NOT_INIT;
128f1a5fb6cSBenjamin Tissoires 
129f1a5fb6cSBenjamin Tissoires 		cur = write_range->struct_name;
130f1a5fb6cSBenjamin Tissoires 	}
131f1a5fb6cSBenjamin Tissoires 
132f1a5fb6cSBenjamin Tissoires 
133f1a5fb6cSBenjamin Tissoires 	if (t != state)
134f1a5fb6cSBenjamin Tissoires 		bpf_log(log, "write access to this struct is not supported\n");
135f1a5fb6cSBenjamin Tissoires 	else
136f1a5fb6cSBenjamin Tissoires 		bpf_log(log,
137f1a5fb6cSBenjamin Tissoires 			"write access at off %d with size %d on read-only part of %s\n",
138f1a5fb6cSBenjamin Tissoires 			off, size, cur);
139f1a5fb6cSBenjamin Tissoires 
140f1a5fb6cSBenjamin Tissoires 	return -EACCES;
141ebc0d809SBenjamin Tissoires }
142ebc0d809SBenjamin Tissoires 
143ebc0d809SBenjamin Tissoires static const struct bpf_verifier_ops hid_bpf_verifier_ops = {
144bd074754SBenjamin Tissoires 	.get_func_proto = bpf_base_func_proto,
145ebc0d809SBenjamin Tissoires 	.is_valid_access = hid_bpf_ops_is_valid_access,
146ebc0d809SBenjamin Tissoires 	.btf_struct_access = hid_bpf_ops_btf_struct_access,
147ebc0d809SBenjamin Tissoires };
148ebc0d809SBenjamin Tissoires 
hid_bpf_ops_init_member(const struct btf_type * t,const struct btf_member * member,void * kdata,const void * udata)149ebc0d809SBenjamin Tissoires static int hid_bpf_ops_init_member(const struct btf_type *t,
150ebc0d809SBenjamin Tissoires 				 const struct btf_member *member,
151ebc0d809SBenjamin Tissoires 				 void *kdata, const void *udata)
152ebc0d809SBenjamin Tissoires {
153ebc0d809SBenjamin Tissoires 	const struct hid_bpf_ops *uhid_bpf_ops;
154ebc0d809SBenjamin Tissoires 	struct hid_bpf_ops *khid_bpf_ops;
155ebc0d809SBenjamin Tissoires 	u32 moff;
156ebc0d809SBenjamin Tissoires 
157ebc0d809SBenjamin Tissoires 	uhid_bpf_ops = (const struct hid_bpf_ops *)udata;
158ebc0d809SBenjamin Tissoires 	khid_bpf_ops = (struct hid_bpf_ops *)kdata;
159ebc0d809SBenjamin Tissoires 
160ebc0d809SBenjamin Tissoires 	moff = __btf_member_bit_offset(t, member) / 8;
161ebc0d809SBenjamin Tissoires 
162ebc0d809SBenjamin Tissoires 	switch (moff) {
163ebc0d809SBenjamin Tissoires 	case offsetof(struct hid_bpf_ops, hid_id):
164ebc0d809SBenjamin Tissoires 		/* For hid_id and flags fields, this function has to copy it
165ebc0d809SBenjamin Tissoires 		 * and return 1 to indicate that the data has been handled by
166ebc0d809SBenjamin Tissoires 		 * the struct_ops type, or the verifier will reject the map if
167ebc0d809SBenjamin Tissoires 		 * the value of those fields is not zero.
168ebc0d809SBenjamin Tissoires 		 */
169ebc0d809SBenjamin Tissoires 		khid_bpf_ops->hid_id = uhid_bpf_ops->hid_id;
170ebc0d809SBenjamin Tissoires 		return 1;
171ebc0d809SBenjamin Tissoires 	case offsetof(struct hid_bpf_ops, flags):
172ebc0d809SBenjamin Tissoires 		if (uhid_bpf_ops->flags & ~BPF_F_BEFORE)
173ebc0d809SBenjamin Tissoires 			return -EINVAL;
174ebc0d809SBenjamin Tissoires 		khid_bpf_ops->flags = uhid_bpf_ops->flags;
175ebc0d809SBenjamin Tissoires 		return 1;
176ebc0d809SBenjamin Tissoires 	}
177ebc0d809SBenjamin Tissoires 	return 0;
178ebc0d809SBenjamin Tissoires }
179ebc0d809SBenjamin Tissoires 
hid_bpf_reg(void * kdata,struct bpf_link * link)1806e504d2cSLinus Torvalds static int hid_bpf_reg(void *kdata, struct bpf_link *link)
181ebc0d809SBenjamin Tissoires {
182ebc0d809SBenjamin Tissoires 	struct hid_bpf_ops *ops = kdata;
183ebc0d809SBenjamin Tissoires 	struct hid_device *hdev;
184ebc0d809SBenjamin Tissoires 	int count, err = 0;
185ebc0d809SBenjamin Tissoires 
186acd34cfcSBenjamin Tissoires 	/* prevent multiple attach of the same struct_ops */
187acd34cfcSBenjamin Tissoires 	if (ops->hdev)
188acd34cfcSBenjamin Tissoires 		return -EINVAL;
189acd34cfcSBenjamin Tissoires 
190ebc0d809SBenjamin Tissoires 	hdev = hid_get_device(ops->hid_id);
191ebc0d809SBenjamin Tissoires 	if (IS_ERR(hdev))
192ebc0d809SBenjamin Tissoires 		return PTR_ERR(hdev);
193ebc0d809SBenjamin Tissoires 
194ebc0d809SBenjamin Tissoires 	ops->hdev = hdev;
195ebc0d809SBenjamin Tissoires 
196ebc0d809SBenjamin Tissoires 	mutex_lock(&hdev->bpf.prog_list_lock);
197ebc0d809SBenjamin Tissoires 
198ebc0d809SBenjamin Tissoires 	count = list_count_nodes(&hdev->bpf.prog_list);
199ebc0d809SBenjamin Tissoires 	if (count >= HID_BPF_MAX_PROGS_PER_DEV) {
200ebc0d809SBenjamin Tissoires 		err = -E2BIG;
201ebc0d809SBenjamin Tissoires 		goto out_unlock;
202ebc0d809SBenjamin Tissoires 	}
203ebc0d809SBenjamin Tissoires 
204ebc0d809SBenjamin Tissoires 	if (ops->hid_rdesc_fixup) {
205ebc0d809SBenjamin Tissoires 		if (hdev->bpf.rdesc_ops) {
206ebc0d809SBenjamin Tissoires 			err = -EINVAL;
207ebc0d809SBenjamin Tissoires 			goto out_unlock;
208ebc0d809SBenjamin Tissoires 		}
209ebc0d809SBenjamin Tissoires 
210ebc0d809SBenjamin Tissoires 		hdev->bpf.rdesc_ops = ops;
211ebc0d809SBenjamin Tissoires 	}
212ebc0d809SBenjamin Tissoires 
213ebc0d809SBenjamin Tissoires 	if (ops->hid_device_event) {
214ebc0d809SBenjamin Tissoires 		err = hid_bpf_allocate_event_data(hdev);
215ebc0d809SBenjamin Tissoires 		if (err)
216ebc0d809SBenjamin Tissoires 			goto out_unlock;
217ebc0d809SBenjamin Tissoires 	}
218ebc0d809SBenjamin Tissoires 
219ebc0d809SBenjamin Tissoires 	if (ops->flags & BPF_F_BEFORE)
220ebc0d809SBenjamin Tissoires 		list_add_rcu(&ops->list, &hdev->bpf.prog_list);
221ebc0d809SBenjamin Tissoires 	else
222ebc0d809SBenjamin Tissoires 		list_add_tail_rcu(&ops->list, &hdev->bpf.prog_list);
2236cd735f0SBenjamin Tissoires 	synchronize_srcu(&hdev->bpf.srcu);
224ebc0d809SBenjamin Tissoires 
225ebc0d809SBenjamin Tissoires out_unlock:
226ebc0d809SBenjamin Tissoires 	mutex_unlock(&hdev->bpf.prog_list_lock);
227ebc0d809SBenjamin Tissoires 
228ebc0d809SBenjamin Tissoires 	if (err) {
229ebc0d809SBenjamin Tissoires 		if (hdev->bpf.rdesc_ops == ops)
230ebc0d809SBenjamin Tissoires 			hdev->bpf.rdesc_ops = NULL;
231ebc0d809SBenjamin Tissoires 		hid_put_device(hdev);
232ebc0d809SBenjamin Tissoires 	} else if (ops->hid_rdesc_fixup) {
233ebc0d809SBenjamin Tissoires 		hid_bpf_reconnect(hdev);
234ebc0d809SBenjamin Tissoires 	}
235ebc0d809SBenjamin Tissoires 
236ebc0d809SBenjamin Tissoires 	return err;
237ebc0d809SBenjamin Tissoires }
238ebc0d809SBenjamin Tissoires 
hid_bpf_unreg(void * kdata,struct bpf_link * link)2396e504d2cSLinus Torvalds static void hid_bpf_unreg(void *kdata, struct bpf_link *link)
240ebc0d809SBenjamin Tissoires {
241ebc0d809SBenjamin Tissoires 	struct hid_bpf_ops *ops = kdata;
242ebc0d809SBenjamin Tissoires 	struct hid_device *hdev;
243ebc0d809SBenjamin Tissoires 	bool reconnect = false;
244ebc0d809SBenjamin Tissoires 
245ebc0d809SBenjamin Tissoires 	hdev = ops->hdev;
246ebc0d809SBenjamin Tissoires 
247ebc0d809SBenjamin Tissoires 	/* check if __hid_bpf_ops_destroy_device() has been called */
248ebc0d809SBenjamin Tissoires 	if (!hdev)
249ebc0d809SBenjamin Tissoires 		return;
250ebc0d809SBenjamin Tissoires 
251ebc0d809SBenjamin Tissoires 	mutex_lock(&hdev->bpf.prog_list_lock);
252ebc0d809SBenjamin Tissoires 
253ebc0d809SBenjamin Tissoires 	list_del_rcu(&ops->list);
2546cd735f0SBenjamin Tissoires 	synchronize_srcu(&hdev->bpf.srcu);
255acd34cfcSBenjamin Tissoires 	ops->hdev = NULL;
256ebc0d809SBenjamin Tissoires 
257ebc0d809SBenjamin Tissoires 	reconnect = hdev->bpf.rdesc_ops == ops;
258ebc0d809SBenjamin Tissoires 	if (reconnect)
259ebc0d809SBenjamin Tissoires 		hdev->bpf.rdesc_ops = NULL;
260ebc0d809SBenjamin Tissoires 
261ebc0d809SBenjamin Tissoires 	mutex_unlock(&hdev->bpf.prog_list_lock);
262ebc0d809SBenjamin Tissoires 
263ebc0d809SBenjamin Tissoires 	if (reconnect)
264ebc0d809SBenjamin Tissoires 		hid_bpf_reconnect(hdev);
265ebc0d809SBenjamin Tissoires 
266ebc0d809SBenjamin Tissoires 	hid_put_device(hdev);
267ebc0d809SBenjamin Tissoires }
268ebc0d809SBenjamin Tissoires 
__hid_bpf_device_event(struct hid_bpf_ctx * ctx,enum hid_report_type type,u64 source)269762ced16SBenjamin Tissoires static int __hid_bpf_device_event(struct hid_bpf_ctx *ctx, enum hid_report_type type, u64 source)
270ebc0d809SBenjamin Tissoires {
271ebc0d809SBenjamin Tissoires 	return 0;
272ebc0d809SBenjamin Tissoires }
273ebc0d809SBenjamin Tissoires 
__hid_bpf_rdesc_fixup(struct hid_bpf_ctx * ctx)274ebc0d809SBenjamin Tissoires static int __hid_bpf_rdesc_fixup(struct hid_bpf_ctx *ctx)
275ebc0d809SBenjamin Tissoires {
276ebc0d809SBenjamin Tissoires 	return 0;
277ebc0d809SBenjamin Tissoires }
278ebc0d809SBenjamin Tissoires 
__hid_bpf_hw_request(struct hid_bpf_ctx * ctx,unsigned char reportnum,enum hid_report_type rtype,enum hid_class_request reqtype,u64 source)279*acd5f76fSBenjamin Tissoires static int __hid_bpf_hw_request(struct hid_bpf_ctx *ctx, unsigned char reportnum,
280*acd5f76fSBenjamin Tissoires 				enum hid_report_type rtype, enum hid_class_request reqtype,
281*acd5f76fSBenjamin Tissoires 				u64 source)
282*acd5f76fSBenjamin Tissoires {
283*acd5f76fSBenjamin Tissoires 	return 0;
284*acd5f76fSBenjamin Tissoires }
285*acd5f76fSBenjamin Tissoires 
__hid_bpf_hw_output_report(struct hid_bpf_ctx * ctx,u64 source)286*acd5f76fSBenjamin Tissoires static int __hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, u64 source)
287*acd5f76fSBenjamin Tissoires {
288*acd5f76fSBenjamin Tissoires 	return 0;
289*acd5f76fSBenjamin Tissoires }
290*acd5f76fSBenjamin Tissoires 
291ebc0d809SBenjamin Tissoires static struct hid_bpf_ops __bpf_hid_bpf_ops = {
292ebc0d809SBenjamin Tissoires 	.hid_device_event = __hid_bpf_device_event,
293ebc0d809SBenjamin Tissoires 	.hid_rdesc_fixup = __hid_bpf_rdesc_fixup,
294*acd5f76fSBenjamin Tissoires 	.hid_hw_request = __hid_bpf_hw_request,
295*acd5f76fSBenjamin Tissoires 	.hid_hw_output_report = __hid_bpf_hw_output_report,
296ebc0d809SBenjamin Tissoires };
297ebc0d809SBenjamin Tissoires 
298ebc0d809SBenjamin Tissoires static struct bpf_struct_ops bpf_hid_bpf_ops = {
299ebc0d809SBenjamin Tissoires 	.verifier_ops = &hid_bpf_verifier_ops,
300ebc0d809SBenjamin Tissoires 	.init = hid_bpf_ops_init,
301ebc0d809SBenjamin Tissoires 	.check_member = hid_bpf_ops_check_member,
302ebc0d809SBenjamin Tissoires 	.init_member = hid_bpf_ops_init_member,
303ebc0d809SBenjamin Tissoires 	.reg = hid_bpf_reg,
304ebc0d809SBenjamin Tissoires 	.unreg = hid_bpf_unreg,
305ebc0d809SBenjamin Tissoires 	.name = "hid_bpf_ops",
306ebc0d809SBenjamin Tissoires 	.cfi_stubs = &__bpf_hid_bpf_ops,
307ebc0d809SBenjamin Tissoires 	.owner = THIS_MODULE,
308ebc0d809SBenjamin Tissoires };
309ebc0d809SBenjamin Tissoires 
__hid_bpf_ops_destroy_device(struct hid_device * hdev)310ebc0d809SBenjamin Tissoires void __hid_bpf_ops_destroy_device(struct hid_device *hdev)
311ebc0d809SBenjamin Tissoires {
312ebc0d809SBenjamin Tissoires 	struct hid_bpf_ops *e;
313ebc0d809SBenjamin Tissoires 
314ebc0d809SBenjamin Tissoires 	rcu_read_lock();
315ebc0d809SBenjamin Tissoires 	list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) {
316ebc0d809SBenjamin Tissoires 		hid_put_device(hdev);
317ebc0d809SBenjamin Tissoires 		e->hdev = NULL;
318ebc0d809SBenjamin Tissoires 	}
319ebc0d809SBenjamin Tissoires 	rcu_read_unlock();
320ebc0d809SBenjamin Tissoires }
321ebc0d809SBenjamin Tissoires 
hid_bpf_struct_ops_init(void)322ebc0d809SBenjamin Tissoires static int __init hid_bpf_struct_ops_init(void)
323ebc0d809SBenjamin Tissoires {
324ebc0d809SBenjamin Tissoires 	return register_bpf_struct_ops(&bpf_hid_bpf_ops, hid_bpf_ops);
325ebc0d809SBenjamin Tissoires }
326ebc0d809SBenjamin Tissoires late_initcall(hid_bpf_struct_ops_init);
327