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