Commit | Line | Data |
---|---|---|
f4364dcf SY |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // bpf-lirc.c - handles bpf | |
3 | // | |
4 | // Copyright (C) 2018 Sean Young <sean@mess.org> | |
5 | ||
6 | #include <linux/bpf.h> | |
7 | #include <linux/filter.h> | |
8 | #include <linux/bpf_lirc.h> | |
9 | #include "rc-core-priv.h" | |
10 | ||
11 | /* | |
12 | * BPF interface for raw IR | |
13 | */ | |
14 | const struct bpf_prog_ops lirc_mode2_prog_ops = { | |
15 | }; | |
16 | ||
17 | BPF_CALL_1(bpf_rc_repeat, u32*, sample) | |
18 | { | |
19 | struct ir_raw_event_ctrl *ctrl; | |
20 | ||
21 | ctrl = container_of(sample, struct ir_raw_event_ctrl, bpf_sample); | |
22 | ||
23 | rc_repeat(ctrl->dev); | |
24 | ||
25 | return 0; | |
26 | } | |
27 | ||
28 | static const struct bpf_func_proto rc_repeat_proto = { | |
29 | .func = bpf_rc_repeat, | |
30 | .gpl_only = true, /* rc_repeat is EXPORT_SYMBOL_GPL */ | |
31 | .ret_type = RET_INTEGER, | |
32 | .arg1_type = ARG_PTR_TO_CTX, | |
33 | }; | |
34 | ||
35 | /* | |
36 | * Currently rc-core does not support 64-bit scancodes, but there are many | |
37 | * known protocols with more than 32 bits. So, define the interface as u64 | |
38 | * as a future-proof. | |
39 | */ | |
40 | BPF_CALL_4(bpf_rc_keydown, u32*, sample, u32, protocol, u64, scancode, | |
41 | u32, toggle) | |
42 | { | |
43 | struct ir_raw_event_ctrl *ctrl; | |
44 | ||
45 | ctrl = container_of(sample, struct ir_raw_event_ctrl, bpf_sample); | |
46 | ||
47 | rc_keydown(ctrl->dev, protocol, scancode, toggle != 0); | |
48 | ||
49 | return 0; | |
50 | } | |
51 | ||
52 | static const struct bpf_func_proto rc_keydown_proto = { | |
53 | .func = bpf_rc_keydown, | |
54 | .gpl_only = true, /* rc_keydown is EXPORT_SYMBOL_GPL */ | |
55 | .ret_type = RET_INTEGER, | |
56 | .arg1_type = ARG_PTR_TO_CTX, | |
57 | .arg2_type = ARG_ANYTHING, | |
58 | .arg3_type = ARG_ANYTHING, | |
59 | .arg4_type = ARG_ANYTHING, | |
60 | }; | |
61 | ||
01d3240a SY |
62 | BPF_CALL_3(bpf_rc_pointer_rel, u32*, sample, s32, rel_x, s32, rel_y) |
63 | { | |
64 | struct ir_raw_event_ctrl *ctrl; | |
65 | ||
66 | ctrl = container_of(sample, struct ir_raw_event_ctrl, bpf_sample); | |
67 | ||
68 | input_report_rel(ctrl->dev->input_dev, REL_X, rel_x); | |
69 | input_report_rel(ctrl->dev->input_dev, REL_Y, rel_y); | |
70 | input_sync(ctrl->dev->input_dev); | |
71 | ||
72 | return 0; | |
73 | } | |
74 | ||
75 | static const struct bpf_func_proto rc_pointer_rel_proto = { | |
76 | .func = bpf_rc_pointer_rel, | |
77 | .gpl_only = true, | |
78 | .ret_type = RET_INTEGER, | |
79 | .arg1_type = ARG_PTR_TO_CTX, | |
80 | .arg2_type = ARG_ANYTHING, | |
81 | .arg3_type = ARG_ANYTHING, | |
82 | }; | |
83 | ||
f4364dcf SY |
84 | static const struct bpf_func_proto * |
85 | lirc_mode2_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) | |
86 | { | |
87 | switch (func_id) { | |
88 | case BPF_FUNC_rc_repeat: | |
89 | return &rc_repeat_proto; | |
90 | case BPF_FUNC_rc_keydown: | |
91 | return &rc_keydown_proto; | |
01d3240a SY |
92 | case BPF_FUNC_rc_pointer_rel: |
93 | return &rc_pointer_rel_proto; | |
f4364dcf SY |
94 | case BPF_FUNC_map_lookup_elem: |
95 | return &bpf_map_lookup_elem_proto; | |
96 | case BPF_FUNC_map_update_elem: | |
97 | return &bpf_map_update_elem_proto; | |
98 | case BPF_FUNC_map_delete_elem: | |
99 | return &bpf_map_delete_elem_proto; | |
100 | case BPF_FUNC_ktime_get_ns: | |
101 | return &bpf_ktime_get_ns_proto; | |
102 | case BPF_FUNC_tail_call: | |
103 | return &bpf_tail_call_proto; | |
104 | case BPF_FUNC_get_prandom_u32: | |
105 | return &bpf_get_prandom_u32_proto; | |
106 | case BPF_FUNC_trace_printk: | |
107 | if (capable(CAP_SYS_ADMIN)) | |
108 | return bpf_get_trace_printk_proto(); | |
109 | /* fall through */ | |
110 | default: | |
111 | return NULL; | |
112 | } | |
113 | } | |
114 | ||
115 | static bool lirc_mode2_is_valid_access(int off, int size, | |
116 | enum bpf_access_type type, | |
117 | const struct bpf_prog *prog, | |
118 | struct bpf_insn_access_aux *info) | |
119 | { | |
120 | /* We have one field of u32 */ | |
121 | return type == BPF_READ && off == 0 && size == sizeof(u32); | |
122 | } | |
123 | ||
124 | const struct bpf_verifier_ops lirc_mode2_verifier_ops = { | |
125 | .get_func_proto = lirc_mode2_func_proto, | |
126 | .is_valid_access = lirc_mode2_is_valid_access | |
127 | }; | |
128 | ||
129 | #define BPF_MAX_PROGS 64 | |
130 | ||
131 | static int lirc_bpf_attach(struct rc_dev *rcdev, struct bpf_prog *prog) | |
132 | { | |
133 | struct bpf_prog_array __rcu *old_array; | |
134 | struct bpf_prog_array *new_array; | |
135 | struct ir_raw_event_ctrl *raw; | |
136 | int ret; | |
137 | ||
138 | if (rcdev->driver_type != RC_DRIVER_IR_RAW) | |
139 | return -EINVAL; | |
140 | ||
141 | ret = mutex_lock_interruptible(&ir_raw_handler_lock); | |
142 | if (ret) | |
143 | return ret; | |
144 | ||
145 | raw = rcdev->raw; | |
146 | if (!raw) { | |
147 | ret = -ENODEV; | |
148 | goto unlock; | |
149 | } | |
150 | ||
151 | if (raw->progs && bpf_prog_array_length(raw->progs) >= BPF_MAX_PROGS) { | |
152 | ret = -E2BIG; | |
153 | goto unlock; | |
154 | } | |
155 | ||
156 | old_array = raw->progs; | |
157 | ret = bpf_prog_array_copy(old_array, NULL, prog, &new_array); | |
158 | if (ret < 0) | |
159 | goto unlock; | |
160 | ||
161 | rcu_assign_pointer(raw->progs, new_array); | |
162 | bpf_prog_array_free(old_array); | |
163 | ||
164 | unlock: | |
165 | mutex_unlock(&ir_raw_handler_lock); | |
166 | return ret; | |
167 | } | |
168 | ||
169 | static int lirc_bpf_detach(struct rc_dev *rcdev, struct bpf_prog *prog) | |
170 | { | |
171 | struct bpf_prog_array __rcu *old_array; | |
172 | struct bpf_prog_array *new_array; | |
173 | struct ir_raw_event_ctrl *raw; | |
174 | int ret; | |
175 | ||
176 | if (rcdev->driver_type != RC_DRIVER_IR_RAW) | |
177 | return -EINVAL; | |
178 | ||
179 | ret = mutex_lock_interruptible(&ir_raw_handler_lock); | |
180 | if (ret) | |
181 | return ret; | |
182 | ||
183 | raw = rcdev->raw; | |
184 | if (!raw) { | |
185 | ret = -ENODEV; | |
186 | goto unlock; | |
187 | } | |
188 | ||
189 | old_array = raw->progs; | |
190 | ret = bpf_prog_array_copy(old_array, prog, NULL, &new_array); | |
191 | /* | |
192 | * Do not use bpf_prog_array_delete_safe() as we would end up | |
193 | * with a dummy entry in the array, and the we would free the | |
194 | * dummy in lirc_bpf_free() | |
195 | */ | |
196 | if (ret) | |
197 | goto unlock; | |
198 | ||
199 | rcu_assign_pointer(raw->progs, new_array); | |
200 | bpf_prog_array_free(old_array); | |
92cab799 | 201 | bpf_prog_put(prog); |
f4364dcf SY |
202 | unlock: |
203 | mutex_unlock(&ir_raw_handler_lock); | |
204 | return ret; | |
205 | } | |
206 | ||
207 | void lirc_bpf_run(struct rc_dev *rcdev, u32 sample) | |
208 | { | |
209 | struct ir_raw_event_ctrl *raw = rcdev->raw; | |
210 | ||
211 | raw->bpf_sample = sample; | |
212 | ||
213 | if (raw->progs) | |
214 | BPF_PROG_RUN_ARRAY(raw->progs, &raw->bpf_sample, BPF_PROG_RUN); | |
215 | } | |
216 | ||
217 | /* | |
218 | * This should be called once the rc thread has been stopped, so there can be | |
219 | * no concurrent bpf execution. | |
220 | */ | |
221 | void lirc_bpf_free(struct rc_dev *rcdev) | |
222 | { | |
394e40a2 | 223 | struct bpf_prog_array_item *item; |
f4364dcf SY |
224 | |
225 | if (!rcdev->raw->progs) | |
226 | return; | |
227 | ||
394e40a2 RG |
228 | item = rcu_dereference(rcdev->raw->progs)->items; |
229 | while (item->prog) { | |
230 | bpf_prog_put(item->prog); | |
231 | item++; | |
232 | } | |
f4364dcf SY |
233 | |
234 | bpf_prog_array_free(rcdev->raw->progs); | |
235 | } | |
236 | ||
fdb5c453 | 237 | int lirc_prog_attach(const union bpf_attr *attr, struct bpf_prog *prog) |
f4364dcf | 238 | { |
f4364dcf SY |
239 | struct rc_dev *rcdev; |
240 | int ret; | |
241 | ||
242 | if (attr->attach_flags) | |
243 | return -EINVAL; | |
244 | ||
f4364dcf | 245 | rcdev = rc_dev_get_from_fd(attr->target_fd); |
fdb5c453 | 246 | if (IS_ERR(rcdev)) |
f4364dcf | 247 | return PTR_ERR(rcdev); |
f4364dcf SY |
248 | |
249 | ret = lirc_bpf_attach(rcdev, prog); | |
f4364dcf SY |
250 | |
251 | put_device(&rcdev->dev); | |
252 | ||
253 | return ret; | |
254 | } | |
255 | ||
256 | int lirc_prog_detach(const union bpf_attr *attr) | |
257 | { | |
258 | struct bpf_prog *prog; | |
259 | struct rc_dev *rcdev; | |
260 | int ret; | |
261 | ||
262 | if (attr->attach_flags) | |
263 | return -EINVAL; | |
264 | ||
265 | prog = bpf_prog_get_type(attr->attach_bpf_fd, | |
266 | BPF_PROG_TYPE_LIRC_MODE2); | |
267 | if (IS_ERR(prog)) | |
268 | return PTR_ERR(prog); | |
269 | ||
270 | rcdev = rc_dev_get_from_fd(attr->target_fd); | |
271 | if (IS_ERR(rcdev)) { | |
272 | bpf_prog_put(prog); | |
273 | return PTR_ERR(rcdev); | |
274 | } | |
275 | ||
276 | ret = lirc_bpf_detach(rcdev, prog); | |
277 | ||
278 | bpf_prog_put(prog); | |
279 | put_device(&rcdev->dev); | |
280 | ||
281 | return ret; | |
282 | } | |
283 | ||
284 | int lirc_prog_query(const union bpf_attr *attr, union bpf_attr __user *uattr) | |
285 | { | |
286 | __u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids); | |
287 | struct bpf_prog_array __rcu *progs; | |
288 | struct rc_dev *rcdev; | |
289 | u32 cnt, flags = 0; | |
290 | int ret; | |
291 | ||
292 | if (attr->query.query_flags) | |
293 | return -EINVAL; | |
294 | ||
295 | rcdev = rc_dev_get_from_fd(attr->query.target_fd); | |
296 | if (IS_ERR(rcdev)) | |
297 | return PTR_ERR(rcdev); | |
298 | ||
299 | if (rcdev->driver_type != RC_DRIVER_IR_RAW) { | |
300 | ret = -EINVAL; | |
301 | goto put; | |
302 | } | |
303 | ||
304 | ret = mutex_lock_interruptible(&ir_raw_handler_lock); | |
305 | if (ret) | |
306 | goto put; | |
307 | ||
308 | progs = rcdev->raw->progs; | |
309 | cnt = progs ? bpf_prog_array_length(progs) : 0; | |
310 | ||
311 | if (copy_to_user(&uattr->query.prog_cnt, &cnt, sizeof(cnt))) { | |
312 | ret = -EFAULT; | |
313 | goto unlock; | |
314 | } | |
315 | ||
316 | if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags))) { | |
317 | ret = -EFAULT; | |
318 | goto unlock; | |
319 | } | |
320 | ||
321 | if (attr->query.prog_cnt != 0 && prog_ids && cnt) | |
322 | ret = bpf_prog_array_copy_to_user(progs, prog_ids, cnt); | |
323 | ||
324 | unlock: | |
325 | mutex_unlock(&ir_raw_handler_lock); | |
326 | put: | |
327 | put_device(&rcdev->dev); | |
328 | ||
329 | return ret; | |
330 | } |