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