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 1102205d2eSStanislav Fomichev #define lirc_rcu_dereference(p) \ 1202205d2eSStanislav Fomichev rcu_dereference_protected(p, lockdep_is_held(&ir_raw_handler_lock)) 1302205d2eSStanislav 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 BPF_CALL_4(bpf_rc_keydown, u32*, sample, u32, protocol, u64, scancode, 39f4364dcfSSean Young u32, toggle) 40f4364dcfSSean Young { 41f4364dcfSSean Young struct ir_raw_event_ctrl *ctrl; 42f4364dcfSSean Young 43f4364dcfSSean Young ctrl = container_of(sample, struct ir_raw_event_ctrl, bpf_sample); 44f4364dcfSSean Young 45f4364dcfSSean Young rc_keydown(ctrl->dev, protocol, scancode, toggle != 0); 46f4364dcfSSean Young 47f4364dcfSSean Young return 0; 48f4364dcfSSean Young } 49f4364dcfSSean Young 50f4364dcfSSean Young static const struct bpf_func_proto rc_keydown_proto = { 51f4364dcfSSean Young .func = bpf_rc_keydown, 52f4364dcfSSean Young .gpl_only = true, /* rc_keydown is EXPORT_SYMBOL_GPL */ 53f4364dcfSSean Young .ret_type = RET_INTEGER, 54f4364dcfSSean Young .arg1_type = ARG_PTR_TO_CTX, 55f4364dcfSSean Young .arg2_type = ARG_ANYTHING, 56f4364dcfSSean Young .arg3_type = ARG_ANYTHING, 57f4364dcfSSean Young .arg4_type = ARG_ANYTHING, 58f4364dcfSSean Young }; 59f4364dcfSSean Young 6001d3240aSSean Young BPF_CALL_3(bpf_rc_pointer_rel, u32*, sample, s32, rel_x, s32, rel_y) 6101d3240aSSean Young { 6201d3240aSSean Young struct ir_raw_event_ctrl *ctrl; 6301d3240aSSean Young 6401d3240aSSean Young ctrl = container_of(sample, struct ir_raw_event_ctrl, bpf_sample); 6501d3240aSSean Young 6601d3240aSSean Young input_report_rel(ctrl->dev->input_dev, REL_X, rel_x); 6701d3240aSSean Young input_report_rel(ctrl->dev->input_dev, REL_Y, rel_y); 6801d3240aSSean Young input_sync(ctrl->dev->input_dev); 6901d3240aSSean Young 7001d3240aSSean Young return 0; 7101d3240aSSean Young } 7201d3240aSSean Young 7301d3240aSSean Young static const struct bpf_func_proto rc_pointer_rel_proto = { 7401d3240aSSean Young .func = bpf_rc_pointer_rel, 7501d3240aSSean Young .gpl_only = true, 7601d3240aSSean Young .ret_type = RET_INTEGER, 7701d3240aSSean Young .arg1_type = ARG_PTR_TO_CTX, 7801d3240aSSean Young .arg2_type = ARG_ANYTHING, 7901d3240aSSean Young .arg3_type = ARG_ANYTHING, 8001d3240aSSean Young }; 8101d3240aSSean Young 82f4364dcfSSean Young static const struct bpf_func_proto * 83f4364dcfSSean Young lirc_mode2_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) 84f4364dcfSSean Young { 85f4364dcfSSean Young switch (func_id) { 86f4364dcfSSean Young case BPF_FUNC_rc_repeat: 87f4364dcfSSean Young return &rc_repeat_proto; 88f4364dcfSSean Young case BPF_FUNC_rc_keydown: 89f4364dcfSSean Young return &rc_keydown_proto; 9001d3240aSSean Young case BPF_FUNC_rc_pointer_rel: 9101d3240aSSean Young return &rc_pointer_rel_proto; 92f4364dcfSSean Young case BPF_FUNC_map_lookup_elem: 93f4364dcfSSean Young return &bpf_map_lookup_elem_proto; 94f4364dcfSSean Young case BPF_FUNC_map_update_elem: 95f4364dcfSSean Young return &bpf_map_update_elem_proto; 96f4364dcfSSean Young case BPF_FUNC_map_delete_elem: 97f4364dcfSSean Young return &bpf_map_delete_elem_proto; 9802a8c817SAlban Crequy case BPF_FUNC_map_push_elem: 9902a8c817SAlban Crequy return &bpf_map_push_elem_proto; 10002a8c817SAlban Crequy case BPF_FUNC_map_pop_elem: 10102a8c817SAlban Crequy return &bpf_map_pop_elem_proto; 10202a8c817SAlban Crequy case BPF_FUNC_map_peek_elem: 10302a8c817SAlban Crequy return &bpf_map_peek_elem_proto; 104f4364dcfSSean Young case BPF_FUNC_ktime_get_ns: 105f4364dcfSSean Young return &bpf_ktime_get_ns_proto; 10671d19214SMaciej Żenczykowski case BPF_FUNC_ktime_get_boot_ns: 10771d19214SMaciej Żenczykowski return &bpf_ktime_get_boot_ns_proto; 108f4364dcfSSean Young case BPF_FUNC_tail_call: 109f4364dcfSSean Young return &bpf_tail_call_proto; 110f4364dcfSSean Young case BPF_FUNC_get_prandom_u32: 111f4364dcfSSean Young return &bpf_get_prandom_u32_proto; 112f4364dcfSSean Young case BPF_FUNC_trace_printk: 113f4364dcfSSean Young if (capable(CAP_SYS_ADMIN)) 114f4364dcfSSean Young return bpf_get_trace_printk_proto(); 115f4364dcfSSean Young /* fall through */ 116f4364dcfSSean Young default: 117f4364dcfSSean Young return NULL; 118f4364dcfSSean Young } 119f4364dcfSSean Young } 120f4364dcfSSean Young 121f4364dcfSSean Young static bool lirc_mode2_is_valid_access(int off, int size, 122f4364dcfSSean Young enum bpf_access_type type, 123f4364dcfSSean Young const struct bpf_prog *prog, 124f4364dcfSSean Young struct bpf_insn_access_aux *info) 125f4364dcfSSean Young { 126f4364dcfSSean Young /* We have one field of u32 */ 127f4364dcfSSean Young return type == BPF_READ && off == 0 && size == sizeof(u32); 128f4364dcfSSean Young } 129f4364dcfSSean Young 130f4364dcfSSean Young const struct bpf_verifier_ops lirc_mode2_verifier_ops = { 131f4364dcfSSean Young .get_func_proto = lirc_mode2_func_proto, 132f4364dcfSSean Young .is_valid_access = lirc_mode2_is_valid_access 133f4364dcfSSean Young }; 134f4364dcfSSean Young 135f4364dcfSSean Young #define BPF_MAX_PROGS 64 136f4364dcfSSean Young 137f4364dcfSSean Young static int lirc_bpf_attach(struct rc_dev *rcdev, struct bpf_prog *prog) 138f4364dcfSSean Young { 13902205d2eSStanislav Fomichev struct bpf_prog_array *old_array; 140f4364dcfSSean Young struct bpf_prog_array *new_array; 141f4364dcfSSean Young struct ir_raw_event_ctrl *raw; 142f4364dcfSSean Young int ret; 143f4364dcfSSean Young 144f4364dcfSSean Young if (rcdev->driver_type != RC_DRIVER_IR_RAW) 145f4364dcfSSean Young return -EINVAL; 146f4364dcfSSean Young 147f4364dcfSSean Young ret = mutex_lock_interruptible(&ir_raw_handler_lock); 148f4364dcfSSean Young if (ret) 149f4364dcfSSean Young return ret; 150f4364dcfSSean Young 151f4364dcfSSean Young raw = rcdev->raw; 152f4364dcfSSean Young if (!raw) { 153f4364dcfSSean Young ret = -ENODEV; 154f4364dcfSSean Young goto unlock; 155f4364dcfSSean Young } 156f4364dcfSSean Young 15702205d2eSStanislav Fomichev old_array = lirc_rcu_dereference(raw->progs); 15802205d2eSStanislav Fomichev if (old_array && bpf_prog_array_length(old_array) >= BPF_MAX_PROGS) { 159f4364dcfSSean Young ret = -E2BIG; 160f4364dcfSSean Young goto unlock; 161f4364dcfSSean Young } 162f4364dcfSSean Young 163f4364dcfSSean Young ret = bpf_prog_array_copy(old_array, NULL, prog, &new_array); 164f4364dcfSSean Young if (ret < 0) 165f4364dcfSSean Young goto unlock; 166f4364dcfSSean Young 167f4364dcfSSean Young rcu_assign_pointer(raw->progs, new_array); 168f4364dcfSSean Young bpf_prog_array_free(old_array); 169f4364dcfSSean Young 170f4364dcfSSean Young unlock: 171f4364dcfSSean Young mutex_unlock(&ir_raw_handler_lock); 172f4364dcfSSean Young return ret; 173f4364dcfSSean Young } 174f4364dcfSSean Young 175f4364dcfSSean Young static int lirc_bpf_detach(struct rc_dev *rcdev, struct bpf_prog *prog) 176f4364dcfSSean Young { 17702205d2eSStanislav Fomichev struct bpf_prog_array *old_array; 178f4364dcfSSean Young struct bpf_prog_array *new_array; 179f4364dcfSSean Young struct ir_raw_event_ctrl *raw; 180f4364dcfSSean Young int ret; 181f4364dcfSSean Young 182f4364dcfSSean Young if (rcdev->driver_type != RC_DRIVER_IR_RAW) 183f4364dcfSSean Young return -EINVAL; 184f4364dcfSSean Young 185f4364dcfSSean Young ret = mutex_lock_interruptible(&ir_raw_handler_lock); 186f4364dcfSSean Young if (ret) 187f4364dcfSSean Young return ret; 188f4364dcfSSean Young 189f4364dcfSSean Young raw = rcdev->raw; 190f4364dcfSSean Young if (!raw) { 191f4364dcfSSean Young ret = -ENODEV; 192f4364dcfSSean Young goto unlock; 193f4364dcfSSean Young } 194f4364dcfSSean Young 19502205d2eSStanislav Fomichev old_array = lirc_rcu_dereference(raw->progs); 196f4364dcfSSean Young ret = bpf_prog_array_copy(old_array, prog, NULL, &new_array); 197f4364dcfSSean Young /* 198f4364dcfSSean Young * Do not use bpf_prog_array_delete_safe() as we would end up 199f4364dcfSSean Young * with a dummy entry in the array, and the we would free the 200f4364dcfSSean Young * dummy in lirc_bpf_free() 201f4364dcfSSean Young */ 202f4364dcfSSean Young if (ret) 203f4364dcfSSean Young goto unlock; 204f4364dcfSSean Young 205f4364dcfSSean Young rcu_assign_pointer(raw->progs, new_array); 206f4364dcfSSean Young bpf_prog_array_free(old_array); 20792cab799SSean Young bpf_prog_put(prog); 208f4364dcfSSean Young unlock: 209f4364dcfSSean Young mutex_unlock(&ir_raw_handler_lock); 210f4364dcfSSean Young return ret; 211f4364dcfSSean Young } 212f4364dcfSSean Young 213f4364dcfSSean Young void lirc_bpf_run(struct rc_dev *rcdev, u32 sample) 214f4364dcfSSean Young { 215f4364dcfSSean Young struct ir_raw_event_ctrl *raw = rcdev->raw; 216f4364dcfSSean Young 217f4364dcfSSean Young raw->bpf_sample = sample; 218f4364dcfSSean Young 219f4364dcfSSean Young if (raw->progs) 220f4364dcfSSean Young BPF_PROG_RUN_ARRAY(raw->progs, &raw->bpf_sample, BPF_PROG_RUN); 221f4364dcfSSean Young } 222f4364dcfSSean Young 223f4364dcfSSean Young /* 224f4364dcfSSean Young * This should be called once the rc thread has been stopped, so there can be 225f4364dcfSSean Young * no concurrent bpf execution. 22602205d2eSStanislav Fomichev * 22702205d2eSStanislav Fomichev * Should be called with the ir_raw_handler_lock held. 228f4364dcfSSean Young */ 229f4364dcfSSean Young void lirc_bpf_free(struct rc_dev *rcdev) 230f4364dcfSSean Young { 231394e40a2SRoman Gushchin struct bpf_prog_array_item *item; 23202205d2eSStanislav Fomichev struct bpf_prog_array *array; 233f4364dcfSSean Young 23402205d2eSStanislav Fomichev array = lirc_rcu_dereference(rcdev->raw->progs); 23502205d2eSStanislav Fomichev if (!array) 236f4364dcfSSean Young return; 237f4364dcfSSean Young 23802205d2eSStanislav Fomichev for (item = array->items; item->prog; item++) 239394e40a2SRoman Gushchin bpf_prog_put(item->prog); 240f4364dcfSSean Young 24102205d2eSStanislav Fomichev bpf_prog_array_free(array); 242f4364dcfSSean Young } 243f4364dcfSSean Young 244fdb5c453SSean Young int lirc_prog_attach(const union bpf_attr *attr, struct bpf_prog *prog) 245f4364dcfSSean Young { 246f4364dcfSSean Young struct rc_dev *rcdev; 247f4364dcfSSean Young int ret; 248f4364dcfSSean Young 249f4364dcfSSean Young if (attr->attach_flags) 250f4364dcfSSean Young return -EINVAL; 251f4364dcfSSean Young 252f4364dcfSSean Young rcdev = rc_dev_get_from_fd(attr->target_fd); 253fdb5c453SSean Young if (IS_ERR(rcdev)) 254f4364dcfSSean Young return PTR_ERR(rcdev); 255f4364dcfSSean Young 256f4364dcfSSean Young ret = lirc_bpf_attach(rcdev, prog); 257f4364dcfSSean Young 258f4364dcfSSean Young put_device(&rcdev->dev); 259f4364dcfSSean Young 260f4364dcfSSean Young return ret; 261f4364dcfSSean Young } 262f4364dcfSSean Young 263f4364dcfSSean Young int lirc_prog_detach(const union bpf_attr *attr) 264f4364dcfSSean Young { 265f4364dcfSSean Young struct bpf_prog *prog; 266f4364dcfSSean Young struct rc_dev *rcdev; 267f4364dcfSSean Young int ret; 268f4364dcfSSean Young 269f4364dcfSSean Young if (attr->attach_flags) 270f4364dcfSSean Young return -EINVAL; 271f4364dcfSSean Young 272f4364dcfSSean Young prog = bpf_prog_get_type(attr->attach_bpf_fd, 273f4364dcfSSean Young BPF_PROG_TYPE_LIRC_MODE2); 274f4364dcfSSean Young if (IS_ERR(prog)) 275f4364dcfSSean Young return PTR_ERR(prog); 276f4364dcfSSean Young 277f4364dcfSSean Young rcdev = rc_dev_get_from_fd(attr->target_fd); 278f4364dcfSSean Young if (IS_ERR(rcdev)) { 279f4364dcfSSean Young bpf_prog_put(prog); 280f4364dcfSSean Young return PTR_ERR(rcdev); 281f4364dcfSSean Young } 282f4364dcfSSean Young 283f4364dcfSSean Young ret = lirc_bpf_detach(rcdev, prog); 284f4364dcfSSean Young 285f4364dcfSSean Young bpf_prog_put(prog); 286f4364dcfSSean Young put_device(&rcdev->dev); 287f4364dcfSSean Young 288f4364dcfSSean Young return ret; 289f4364dcfSSean Young } 290f4364dcfSSean Young 291f4364dcfSSean Young int lirc_prog_query(const union bpf_attr *attr, union bpf_attr __user *uattr) 292f4364dcfSSean Young { 293f4364dcfSSean Young __u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids); 29402205d2eSStanislav Fomichev struct bpf_prog_array *progs; 295f4364dcfSSean Young struct rc_dev *rcdev; 296f4364dcfSSean Young u32 cnt, flags = 0; 297f4364dcfSSean Young int ret; 298f4364dcfSSean Young 299f4364dcfSSean Young if (attr->query.query_flags) 300f4364dcfSSean Young return -EINVAL; 301f4364dcfSSean Young 302f4364dcfSSean Young rcdev = rc_dev_get_from_fd(attr->query.target_fd); 303f4364dcfSSean Young if (IS_ERR(rcdev)) 304f4364dcfSSean Young return PTR_ERR(rcdev); 305f4364dcfSSean Young 306f4364dcfSSean Young if (rcdev->driver_type != RC_DRIVER_IR_RAW) { 307f4364dcfSSean Young ret = -EINVAL; 308f4364dcfSSean Young goto put; 309f4364dcfSSean Young } 310f4364dcfSSean Young 311f4364dcfSSean Young ret = mutex_lock_interruptible(&ir_raw_handler_lock); 312f4364dcfSSean Young if (ret) 313f4364dcfSSean Young goto put; 314f4364dcfSSean Young 31502205d2eSStanislav Fomichev progs = lirc_rcu_dereference(rcdev->raw->progs); 316f4364dcfSSean Young cnt = progs ? bpf_prog_array_length(progs) : 0; 317f4364dcfSSean Young 318f4364dcfSSean Young if (copy_to_user(&uattr->query.prog_cnt, &cnt, sizeof(cnt))) { 319f4364dcfSSean Young ret = -EFAULT; 320f4364dcfSSean Young goto unlock; 321f4364dcfSSean Young } 322f4364dcfSSean Young 323f4364dcfSSean Young if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags))) { 324f4364dcfSSean Young ret = -EFAULT; 325f4364dcfSSean Young goto unlock; 326f4364dcfSSean Young } 327f4364dcfSSean Young 328f4364dcfSSean Young if (attr->query.prog_cnt != 0 && prog_ids && cnt) 329f4364dcfSSean Young ret = bpf_prog_array_copy_to_user(progs, prog_ids, cnt); 330f4364dcfSSean Young 331f4364dcfSSean Young unlock: 332f4364dcfSSean Young mutex_unlock(&ir_raw_handler_lock); 333f4364dcfSSean Young put: 334f4364dcfSSean Young put_device(&rcdev->dev); 335f4364dcfSSean Young 336f4364dcfSSean Young return ret; 337f4364dcfSSean Young } 338