From patchwork Fri Mar 18 16:15:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12785604 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 66267C433F5 for ; Fri, 18 Mar 2022 16:16:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238712AbiCRQR1 (ORCPT ); Fri, 18 Mar 2022 12:17:27 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53520 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238709AbiCRQR0 (ORCPT ); Fri, 18 Mar 2022 12:17:26 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 5C277FD6DC for ; Fri, 18 Mar 2022 09:16:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1647620166; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=xDf7SF2Fra40bZaEdvr7u9jw/RZ02CEurbENm/arAE4=; b=M5fuQGoX50zm2Np+INw9PGr6EP3NIG/Od3pTUzuGJijJHfxwoiO32ccBVwHQH17FCFa4gC 4abMwjIargH1Lu14przdB2fAfeZB1oPVv827WaaYYWqliA4lgA+ZiPYNBYkAiKzD+pX8Bn GKiuDl69ODIiQckYMyf7Yvr4r5pVlrM= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-609-uvKNlS3pNXaCQvW5KvtJmw-1; Fri, 18 Mar 2022 12:16:03 -0400 X-MC-Unique: uvKNlS3pNXaCQvW5KvtJmw-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id E2D1F803524; Fri, 18 Mar 2022 16:16:01 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.159]) by smtp.corp.redhat.com (Postfix) with ESMTP id 0B90A33250; Fri, 18 Mar 2022 16:15:44 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires , Sean Young Subject: [PATCH bpf-next v3 01/17] bpf: add new is_sys_admin_prog_type() helper Date: Fri, 18 Mar 2022 17:15:12 +0100 Message-Id: <20220318161528.1531164-2-benjamin.tissoires@redhat.com> In-Reply-To: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> References: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net LIRC_MODE2 does not really need net_admin capability, but only sys_admin. Extract a new helper for it, it will be also used for the HID bpf implementation. Cc: Sean Young Acked-by: Sean Young Signed-off-by: Benjamin Tissoires Acked-by: Song Liu --- changes in v3: - dropped BPF_PROG_TYPE_EXT from the new helper new in v2 --- kernel/bpf/syscall.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 9beb585be5a6..b88688264ad0 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -2165,7 +2165,6 @@ static bool is_net_admin_prog_type(enum bpf_prog_type prog_type) case BPF_PROG_TYPE_LWT_SEG6LOCAL: case BPF_PROG_TYPE_SK_SKB: case BPF_PROG_TYPE_SK_MSG: - case BPF_PROG_TYPE_LIRC_MODE2: case BPF_PROG_TYPE_FLOW_DISSECTOR: case BPF_PROG_TYPE_CGROUP_DEVICE: case BPF_PROG_TYPE_CGROUP_SOCK: @@ -2202,6 +2201,16 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type) } } +static bool is_sys_admin_prog_type(enum bpf_prog_type prog_type) +{ + switch (prog_type) { + case BPF_PROG_TYPE_LIRC_MODE2: + return true; + default: + return false; + } +} + /* last field in 'union bpf_attr' used by this command */ #define BPF_PROG_LOAD_LAST_FIELD core_relo_rec_size @@ -2252,6 +2261,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr) return -EPERM; if (is_perfmon_prog_type(type) && !perfmon_capable()) return -EPERM; + if (is_sys_admin_prog_type(type) && !capable(CAP_SYS_ADMIN)) + return -EPERM; /* attach_prog_fd/attach_btf_obj_fd can specify fd of either bpf_prog * or btf, we need to check which one it is From patchwork Fri Mar 18 16:15:13 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12785606 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B3A2EC433F5 for ; Fri, 18 Mar 2022 16:17:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235735AbiCRQTJ (ORCPT ); Fri, 18 Mar 2022 12:19:09 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55436 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238746AbiCRQRs (ORCPT ); Fri, 18 Mar 2022 12:17:48 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id B8B12200945 for ; Fri, 18 Mar 2022 09:16:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1647620187; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=St07ONfkvmvN0qLjqwmycr5927HY++X1tTrlEyiBJwM=; b=FjHu9SNmhqu9H+nPiJAe2IUGvcGEmr6UKVVuZ90F79wrOXNwtljbb1jGSsauZB4E0ep1w9 5qORSOngLC0eJOfI7NM11hiLW1YSpbLu+/+5jLTi2TBmT+vHk3nF/qXoboBRK2WzmI/ucY K4qj/6j5NwsnnwfEwSLQVoSWsJYcW44= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-138-TLK_ueiRPpmZUr8d5FSMKA-1; Fri, 18 Mar 2022 12:16:24 -0400 X-MC-Unique: TLK_ueiRPpmZUr8d5FSMKA-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id F141C811E75; Fri, 18 Mar 2022 16:16:18 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.159]) by smtp.corp.redhat.com (Postfix) with ESMTP id 2CD1033250; Fri, 18 Mar 2022 16:16:02 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v3 02/17] bpf: introduce hid program type Date: Fri, 18 Mar 2022 17:15:13 +0100 Message-Id: <20220318161528.1531164-3-benjamin.tissoires@redhat.com> In-Reply-To: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> References: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net HID is a protocol that could benefit from using BPF too. This patch implements a net-like use of BPF capability for HID. Any incoming report coming from the device can be injected into a series of BPF programs that can modify it or even discard it by setting the size in the context to 0. The kernel/bpf implementation is based on net-namespace.c, with only the bpf_link part kept, there is no real points in keeping the bpf_prog_{attach|detach} API. The implementation here is only focusing on the bpf changes. The HID changes that hooks onto this are coming in separate patches. Given that HID can be compiled in as a module, and the functions that kernel/bpf/hid.c needs to call in hid.ko are exported in struct hid_hooks. This patch also adds a new flag to BPF_LINK_CREATE that allows to chose the position of the inserted program: at the beginning or at the end. This way, we can have a tracing program that compare the raw event from the device and the transformed stream from all the other bpf programs. This patch introduces the various attachment types the HID BPF programs need: - BPF_HID_DEVICE_EVENT for filtering events coming from the device - BPF_HID_USER_EVENT to initiate a call in a BPF program from userspace - BPF_HID_DRIVER_EVENT for getting notified of a driver action - BPF_HID_RDESC_FIXUP to be able to patch the report descriptor Signed-off-by: Benjamin Tissoires --- changes in v3: - squashed multiple patches from v2 - use of .convert_ctx_access to hide internal kernel API from userspace - also calls HID hook when a program has been updated (useful for reconnecting the device is the report descriptor fixup is used) - add BPF_HID_DRIVER_EVENT - s/hidraw_ino/hidraw_number/ - s/link_attach/pre_link_attach/ - s/link_attached/post_link_attach/ - s/array_detached/post_array_detach/ changes in v2: - split the series by bpf/libbpf/hid/selftests and samples - unsigned long -> __u16 in uapi/linux/bpf_hid.h - change the bpf_ctx to be of variable size, with a min of 1024 bytes - make this 1 kB available directly from bpf program, the rest will need a helper - add some more doc comments in uapi --- include/linux/bpf-hid.h | 113 ++++++ include/linux/bpf_types.h | 4 + include/linux/hid.h | 5 + include/uapi/linux/bpf.h | 31 ++ include/uapi/linux/bpf_hid.h | 31 ++ kernel/bpf/Makefile | 3 + kernel/bpf/btf.c | 1 + kernel/bpf/hid.c | 643 +++++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 14 + 9 files changed, 845 insertions(+) create mode 100644 include/linux/bpf-hid.h create mode 100644 include/uapi/linux/bpf_hid.h create mode 100644 kernel/bpf/hid.c diff --git a/include/linux/bpf-hid.h b/include/linux/bpf-hid.h new file mode 100644 index 000000000000..9c8dbd389995 --- /dev/null +++ b/include/linux/bpf-hid.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _BPF_HID_H +#define _BPF_HID_H + +#include +#include +#include +#include +#include + +struct bpf_prog; +struct bpf_prog_array; +struct hid_device; + +enum bpf_hid_attach_type { + BPF_HID_ATTACH_INVALID = -1, + BPF_HID_ATTACH_DEVICE_EVENT = 0, + BPF_HID_ATTACH_RDESC_FIXUP, + BPF_HID_ATTACH_USER_EVENT, + BPF_HID_ATTACH_DRIVER_EVENT, + MAX_BPF_HID_ATTACH_TYPE +}; + +struct hid_bpf_ctx_kern { + enum hid_bpf_event type; /* read-only */ + struct hid_device *hdev; /* read-only */ + + u16 size; /* used size in data (RW) */ + u8 *data; /* data buffer (RW) */ + u32 allocated_size; /* allocated size of data (RO) */ + + s32 retval; /* in use when BPF_HID_ATTACH_USER_EVENT (RW) */ +}; + +struct bpf_hid { + u8 *device_data; /* allocated when a bpf program of type + * SEC(hid/device_event) has been attached + * to this HID device + */ + u32 allocated_data; + + /* Array of programs to run compiled from links */ + struct bpf_prog_array __rcu *run_array[MAX_BPF_HID_ATTACH_TYPE]; + struct list_head links[MAX_BPF_HID_ATTACH_TYPE]; +}; + +static inline enum bpf_hid_attach_type +to_bpf_hid_attach_type(enum bpf_attach_type attach_type) +{ + switch (attach_type) { + case BPF_HID_DEVICE_EVENT: + return BPF_HID_ATTACH_DEVICE_EVENT; + case BPF_HID_RDESC_FIXUP: + return BPF_HID_ATTACH_RDESC_FIXUP; + case BPF_HID_USER_EVENT: + return BPF_HID_ATTACH_USER_EVENT; + case BPF_HID_DRIVER_EVENT: + return BPF_HID_ATTACH_DRIVER_EVENT; + default: + return BPF_HID_ATTACH_INVALID; + } +} + +union bpf_attr; +struct bpf_prog; + +#if IS_ENABLED(CONFIG_HID) +int bpf_hid_prog_query(const union bpf_attr *attr, + union bpf_attr __user *uattr); +int bpf_hid_link_create(const union bpf_attr *attr, + struct bpf_prog *prog); +#else +static inline int bpf_hid_prog_query(const union bpf_attr *attr, + union bpf_attr __user *uattr) +{ + return -EOPNOTSUPP; +} + +static inline int bpf_hid_link_create(const union bpf_attr *attr, + struct bpf_prog *prog) +{ + return -EOPNOTSUPP; +} +#endif + +static inline bool bpf_hid_link_empty(struct bpf_hid *bpf, + enum bpf_hid_attach_type type) +{ + return list_empty(&bpf->links[type]); +} + +struct bpf_hid_hooks { + struct hid_device *(*hdev_from_fd)(int fd); + int (*pre_link_attach)(struct hid_device *hdev, enum bpf_hid_attach_type type); + void (*post_link_attach)(struct hid_device *hdev, enum bpf_hid_attach_type type); + void (*array_detach)(struct hid_device *hdev, enum bpf_hid_attach_type type); +}; + +#ifdef CONFIG_BPF +int bpf_hid_init(struct hid_device *hdev); +void bpf_hid_exit(struct hid_device *hdev); +void bpf_hid_set_hooks(struct bpf_hid_hooks *hooks); +#else +static inline int bpf_hid_init(struct hid_device *hdev) +{ + return 0; +} + +static inline void bpf_hid_exit(struct hid_device *hdev) {} +static inline void bpf_hid_set_hooks(struct bpf_hid_hooks *hooks) {} +#endif + +#endif /* _BPF_HID_H */ diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h index 48a91c51c015..bb0190356949 100644 --- a/include/linux/bpf_types.h +++ b/include/linux/bpf_types.h @@ -76,6 +76,10 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_EXT, bpf_extension, BPF_PROG_TYPE(BPF_PROG_TYPE_LSM, lsm, void *, void *) #endif /* CONFIG_BPF_LSM */ +#if IS_ENABLED(CONFIG_HID) +BPF_PROG_TYPE(BPF_PROG_TYPE_HID, hid, + struct hid_bpf_ctx, struct hid_bpf_ctx_kern) +#endif #endif BPF_PROG_TYPE(BPF_PROG_TYPE_SYSCALL, bpf_syscall, void *, void *) diff --git a/include/linux/hid.h b/include/linux/hid.h index 7487b0586fe6..56f6f4ad45a7 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -15,6 +15,7 @@ #include +#include #include #include #include @@ -639,6 +640,10 @@ struct hid_device { /* device report descriptor */ struct list_head debug_list; spinlock_t debug_list_lock; wait_queue_head_t debug_wait; + +#ifdef CONFIG_BPF + struct bpf_hid bpf; +#endif }; #define to_hid_device(pdev) \ diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 99fab54ae9c0..0e8438e93768 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -952,6 +952,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_LSM, BPF_PROG_TYPE_SK_LOOKUP, BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */ + BPF_PROG_TYPE_HID, }; enum bpf_attach_type { @@ -997,6 +998,10 @@ enum bpf_attach_type { BPF_SK_REUSEPORT_SELECT, BPF_SK_REUSEPORT_SELECT_OR_MIGRATE, BPF_PERF_EVENT, + BPF_HID_DEVICE_EVENT, + BPF_HID_RDESC_FIXUP, + BPF_HID_USER_EVENT, + BPF_HID_DRIVER_EVENT, __MAX_BPF_ATTACH_TYPE }; @@ -1011,6 +1016,7 @@ enum bpf_link_type { BPF_LINK_TYPE_NETNS = 5, BPF_LINK_TYPE_XDP = 6, BPF_LINK_TYPE_PERF_EVENT = 7, + BPF_LINK_TYPE_HID = 8, MAX_BPF_LINK_TYPE, }; @@ -1118,6 +1124,16 @@ enum bpf_link_type { */ #define BPF_F_XDP_HAS_FRAGS (1U << 5) +/* HID flag used in BPF_LINK_CREATE command + * + * NONE(default): The bpf program will be added at the tail of the list + * of existing bpf program for this type. + * + * BPF_F_INSERT_HEAD: The bpf program will be added at the beginning + * of the list of existing bpf program for this type.. + */ +#define BPF_F_INSERT_HEAD (1U << 0) + /* When BPF ldimm64's insn[0].src_reg != 0 then this can have * the following extensions: * @@ -5129,6 +5145,16 @@ union bpf_attr { * The **hash_algo** is returned on success, * **-EOPNOTSUP** if the hash calculation failed or **-EINVAL** if * invalid arguments are passed. + * + * void *bpf_hid_get_data(void *ctx, u64 offset, u64 size) + * Description + * Returns a pointer to the data associated with context at the given + * offset and size (in bytes). + * + * Note: the returned pointer is refcounted and must be dereferenced + * by a call to bpf_hid_discard; + * Return + * The pointer to the data. On error, a null value is returned. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -5325,6 +5351,7 @@ union bpf_attr { FN(copy_from_user_task), \ FN(skb_set_tstamp), \ FN(ima_file_hash), \ + FN(hid_get_data), \ /* */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper @@ -5925,6 +5952,10 @@ struct bpf_link_info { struct { __u32 ifindex; } xdp; + struct { + __s32 hidraw_number; + __u32 attach_type; + } hid; }; } __attribute__((aligned(8))); diff --git a/include/uapi/linux/bpf_hid.h b/include/uapi/linux/bpf_hid.h new file mode 100644 index 000000000000..64a8b9dd8809 --- /dev/null +++ b/include/uapi/linux/bpf_hid.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */ + +/* + * HID BPF public headers + * + * Copyright (c) 2022 Benjamin Tissoires + */ + +#ifndef _UAPI__LINUX_BPF_HID_H__ +#define _UAPI__LINUX_BPF_HID_H__ + +#include + +enum hid_bpf_event { + HID_BPF_UNDEF = 0, + HID_BPF_DEVICE_EVENT, /* when attach type is BPF_HID_DEVICE_EVENT */ + HID_BPF_RDESC_FIXUP, /* ................... BPF_HID_RDESC_FIXUP */ + HID_BPF_USER_EVENT, /* ................... BPF_HID_USER_EVENT */ +}; + +/* User accessible data for HID programs. Add new fields at the end. */ +struct hid_bpf_ctx { + __u32 type; /* enum hid_bpf_event, RO */ + __u32 allocated_size; /* allocated size of data, RO */ + + __u32 size; /* valid data in data field, RW */ + __s32 retval; /* when type is HID_BPF_USER_EVENT, RW */ +}; + +#endif /* _UAPI__LINUX_BPF_HID_H__ */ + diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index c1a9be6a4b9f..8d5619d3d7e5 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -35,6 +35,9 @@ ifeq ($(CONFIG_BPF_JIT),y) obj-$(CONFIG_BPF_SYSCALL) += bpf_struct_ops.o obj-${CONFIG_BPF_LSM} += bpf_lsm.o endif +ifneq ($(CONFIG_HID),) +obj-$(CONFIG_BPF_SYSCALL) += hid.o +endif obj-$(CONFIG_BPF_PRELOAD) += preload/ obj-$(CONFIG_BPF_SYSCALL) += relo_core.o diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 8b34563a832e..a389626fdf75 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/kernel/bpf/hid.c b/kernel/bpf/hid.c new file mode 100644 index 000000000000..c21dc05f6207 --- /dev/null +++ b/kernel/bpf/hid.c @@ -0,0 +1,643 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include + +/* + * Functions to manage BPF programs attached to hid + */ + +struct bpf_hid_link { + struct bpf_link link; + enum bpf_attach_type type; + enum bpf_hid_attach_type hid_type; + + /* Must be accessed with bpf_hid_mutex held. */ + struct hid_device *hdev; + struct list_head node; /* node in list of links attached to hid */ +}; + +/* Protects updates to bpf_hid */ +DEFINE_MUTEX(bpf_hid_mutex); + +static struct bpf_hid_hooks hid_hooks = {0}; + +void bpf_hid_set_hooks(struct bpf_hid_hooks *hooks) +{ + if (hooks) + hid_hooks = *hooks; + else + memset(&hid_hooks, 0, sizeof(hid_hooks)); +} +EXPORT_SYMBOL_GPL(bpf_hid_set_hooks); + +BPF_CALL_3(bpf_hid_get_data, struct hid_bpf_ctx_kern*, ctx, u64, offset, u64, size) +{ + if (!size) + return 0UL; + + if (offset + size > ctx->allocated_size) + return 0UL; + + return (unsigned long)(ctx->data + offset); +} + +static const struct bpf_func_proto bpf_hid_get_data_proto = { + .func = bpf_hid_get_data, + .gpl_only = true, + .ret_type = RET_PTR_TO_ALLOC_MEM_OR_NULL, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_CONST_ALLOC_SIZE_OR_ZERO, +}; + +static const struct bpf_func_proto * +hid_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) +{ + switch (func_id) { + case BPF_FUNC_hid_get_data: + return &bpf_hid_get_data_proto; + default: + return bpf_base_func_proto(func_id); + } +} + +static bool hid_is_valid_access(int off, int size, + enum bpf_access_type access_type, + const struct bpf_prog *prog, + struct bpf_insn_access_aux *info) +{ + /* everything not in ctx is prohibited */ + const int size_default = sizeof(__u32); + + if (off < 0 || off >= sizeof(struct hid_bpf_ctx)) + return false; + + if (off % size != 0) + return false; + + switch (off) { + /* type is read-only */ + case offsetof(struct hid_bpf_ctx, type): + if (size != size_default) + return false; + return access_type == BPF_READ; + + /* allocated_size is read-only */ + case offsetof(struct hid_bpf_ctx, allocated_size): + if (size != size_default) + return false; + return access_type == BPF_READ; + + case offsetof(struct hid_bpf_ctx, retval): + if (size != size_default) + return false; + return (prog->expected_attach_type == BPF_HID_USER_EVENT || + prog->expected_attach_type == BPF_HID_DRIVER_EVENT); + default: + if (size != size_default) + return false; + break; + } + + /* everything else is read/write */ + return true; +} + +#define HID_ACCESS_FIELD(T, F) \ + T(BPF_FIELD_SIZEOF(struct hid_bpf_ctx_kern, F), \ + si->dst_reg, si->src_reg, \ + offsetof(struct hid_bpf_ctx_kern, F)) + +static u32 hid_convert_ctx_access(enum bpf_access_type type, + const struct bpf_insn *si, + struct bpf_insn *insn_buf, + struct bpf_prog *prog, + u32 *target_size) +{ + struct bpf_insn *insn = insn_buf; + + switch (si->off) { + case offsetof(struct hid_bpf_ctx, type): + *insn++ = HID_ACCESS_FIELD(BPF_LDX_MEM, type); + break; + case offsetof(struct hid_bpf_ctx, allocated_size): + *insn++ = HID_ACCESS_FIELD(BPF_LDX_MEM, allocated_size); + break; + case offsetof(struct hid_bpf_ctx, size): + if (type == BPF_WRITE) + *insn++ = HID_ACCESS_FIELD(BPF_STX_MEM, size); + else + *insn++ = HID_ACCESS_FIELD(BPF_LDX_MEM, size); + break; + case offsetof(struct hid_bpf_ctx, retval): + if (type == BPF_WRITE) + *insn++ = HID_ACCESS_FIELD(BPF_STX_MEM, retval); + else + *insn++ = HID_ACCESS_FIELD(BPF_LDX_MEM, retval); + break; + } + + return insn - insn_buf; +} + +const struct bpf_verifier_ops hid_verifier_ops = { + .get_func_proto = hid_func_proto, + .is_valid_access = hid_is_valid_access, + .convert_ctx_access = hid_convert_ctx_access, +}; + +/* Must be called with bpf_hid_mutex held. */ +static void bpf_hid_run_array_detach(struct hid_device *hdev, + enum bpf_hid_attach_type type) +{ + struct bpf_prog_array *run_array; + + run_array = rcu_replace_pointer(hdev->bpf.run_array[type], NULL, + lockdep_is_held(&bpf_hid_mutex)); + bpf_prog_array_free(run_array); + + if (hid_hooks.array_detach) + hid_hooks.array_detach(hdev, type); +} + +static int link_index(struct hid_device *hdev, enum bpf_hid_attach_type type, + struct bpf_hid_link *link) +{ + struct bpf_hid_link *pos; + int i = 0; + + list_for_each_entry(pos, &hdev->bpf.links[type], node) { + if (pos == link) + return i; + i++; + } + return -ENOENT; +} + +static int link_count(struct hid_device *hdev, enum bpf_hid_attach_type type) +{ + struct list_head *pos; + int i = 0; + + list_for_each(pos, &hdev->bpf.links[type]) + i++; + return i; +} + +static void fill_prog_array(struct hid_device *hdev, enum bpf_hid_attach_type type, + struct bpf_prog_array *prog_array) +{ + struct bpf_hid_link *pos; + unsigned int i = 0; + + list_for_each_entry(pos, &hdev->bpf.links[type], node) { + prog_array->items[i].prog = pos->link.prog; + i++; + } +} + +static void bpf_hid_link_release(struct bpf_link *link) +{ + struct bpf_hid_link *hid_link = + container_of(link, struct bpf_hid_link, link); + enum bpf_hid_attach_type type = hid_link->hid_type; + struct bpf_prog_array *old_array, *new_array; + struct hid_device *hdev; + int cnt, idx; + + mutex_lock(&bpf_hid_mutex); + + hdev = hid_link->hdev; + if (!hdev) + goto out_unlock; + + /* Remember link position in case of safe delete */ + idx = link_index(hdev, type, hid_link); + list_del(&hid_link->node); + + cnt = link_count(hdev, type); + if (!cnt) { + bpf_hid_run_array_detach(hdev, type); + goto out_unlock; + } + + old_array = rcu_dereference_protected(hdev->bpf.run_array[type], + lockdep_is_held(&bpf_hid_mutex)); + new_array = bpf_prog_array_alloc(cnt, GFP_KERNEL); + if (!new_array) { + WARN_ON(bpf_prog_array_delete_safe_at(old_array, idx)); + goto out_unlock; + } + fill_prog_array(hdev, type, new_array); + rcu_assign_pointer(hdev->bpf.run_array[type], new_array); + bpf_prog_array_free(old_array); + +out_unlock: + hid_link->hdev = NULL; + mutex_unlock(&bpf_hid_mutex); +} + +static int bpf_hid_link_detach(struct bpf_link *link) +{ + bpf_hid_link_release(link); + return 0; +} + +static void bpf_hid_link_dealloc(struct bpf_link *link) +{ + struct bpf_hid_link *hid_link = + container_of(link, struct bpf_hid_link, link); + + kfree(hid_link); +} + +static int bpf_hid_link_update_prog(struct bpf_link *link, + struct bpf_prog *new_prog, + struct bpf_prog *old_prog) +{ + struct bpf_hid_link *hid_link = + container_of(link, struct bpf_hid_link, link); + enum bpf_hid_attach_type type = hid_link->hid_type; + struct bpf_prog_array *run_array; + struct hid_device *hdev; + int idx, ret; + + if (old_prog && old_prog != link->prog) + return -EPERM; + if (new_prog->type != link->prog->type) + return -EINVAL; + + mutex_lock(&bpf_hid_mutex); + + hdev = hid_link->hdev; + if (!hdev) { + /* hid dying */ + ret = -ENOLINK; + goto out_unlock; + } + + run_array = rcu_dereference_protected(hdev->bpf.run_array[type], + lockdep_is_held(&bpf_hid_mutex)); + idx = link_index(hdev, type, hid_link); + ret = bpf_prog_array_update_at(run_array, idx, new_prog); + if (ret) + goto out_unlock; + + old_prog = xchg(&link->prog, new_prog); + bpf_prog_put(old_prog); + + if (hid_hooks.post_link_attach) + hid_hooks.post_link_attach(hdev, type); + +out_unlock: + mutex_unlock(&bpf_hid_mutex); + return ret; +} + +static int bpf_hid_link_fill_info(const struct bpf_link *link, + struct bpf_link_info *info) +{ + const struct bpf_hid_link *hid_link = + container_of(link, struct bpf_hid_link, link); + int hidraw_number = -1; + struct hid_device *hdev; + struct hidraw *hidraw; + + mutex_lock(&bpf_hid_mutex); + hdev = hid_link->hdev; + if (hdev && hdev->hidraw) { + hidraw = hdev->hidraw; + hidraw_number = hidraw->minor; + } + mutex_unlock(&bpf_hid_mutex); + + info->hid.hidraw_number = hidraw_number; + info->hid.attach_type = hid_link->type; + return 0; +} + +static void bpf_hid_link_show_fdinfo(const struct bpf_link *link, + struct seq_file *seq) +{ + struct bpf_link_info info = {}; + + bpf_hid_link_fill_info(link, &info); + seq_printf(seq, + "hidraw_number:\t%u\n" + "attach_type:\t%u\n", + info.hid.hidraw_number, + info.hid.attach_type); +} + +static const struct bpf_link_ops bpf_hid_link_ops = { + .release = bpf_hid_link_release, + .dealloc = bpf_hid_link_dealloc, + .detach = bpf_hid_link_detach, + .update_prog = bpf_hid_link_update_prog, + .fill_link_info = bpf_hid_link_fill_info, + .show_fdinfo = bpf_hid_link_show_fdinfo, +}; + +/* Must be called with bpf_hid_mutex held. */ +static int __bpf_hid_prog_query(const union bpf_attr *attr, + union bpf_attr __user *uattr, + struct hid_device *hdev, + enum bpf_hid_attach_type type) +{ + __u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids); + struct bpf_prog_array *run_array; + u32 prog_cnt = 0, flags = 0; + + run_array = rcu_dereference_protected(hdev->bpf.run_array[type], + lockdep_is_held(&bpf_hid_mutex)); + if (run_array) + prog_cnt = bpf_prog_array_length(run_array); + + if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags))) + return -EFAULT; + if (copy_to_user(&uattr->query.prog_cnt, &prog_cnt, sizeof(prog_cnt))) + return -EFAULT; + if (!attr->query.prog_cnt || !prog_ids || !prog_cnt) + return 0; + + return bpf_prog_array_copy_to_user(run_array, prog_ids, + attr->query.prog_cnt); +} + +int bpf_hid_prog_query(const union bpf_attr *attr, + union bpf_attr __user *uattr) +{ + enum bpf_hid_attach_type type; + struct hid_device *hdev; + int ret; + + if (attr->query.query_flags || !hid_hooks.hdev_from_fd) + return -EINVAL; + + type = to_bpf_hid_attach_type(attr->query.attach_type); + if (type < 0) + return -EINVAL; + + hdev = hid_hooks.hdev_from_fd(attr->query.target_fd); + if (IS_ERR(hdev)) + return PTR_ERR(hdev); + + mutex_lock(&bpf_hid_mutex); + ret = __bpf_hid_prog_query(attr, uattr, hdev, type); + mutex_unlock(&bpf_hid_mutex); + + return ret; +} + +static int bpf_hid_max_progs(enum bpf_hid_attach_type type) +{ + switch (type) { + case BPF_HID_ATTACH_DEVICE_EVENT: + return 64; + case BPF_HID_ATTACH_RDESC_FIXUP: + return 1; + case BPF_HID_ATTACH_USER_EVENT: + return 64; + case BPF_HID_ATTACH_DRIVER_EVENT: + return 64; + default: + return 0; + } +} + +static int bpf_hid_link_attach(struct hid_device *hdev, struct bpf_link *link, + enum bpf_hid_attach_type type, u32 flags) +{ + struct bpf_hid_link *hid_link = + container_of(link, struct bpf_hid_link, link); + struct bpf_prog_array *run_array; + int cnt, err = 0; + + mutex_lock(&bpf_hid_mutex); + + cnt = link_count(hdev, type); + if (cnt >= bpf_hid_max_progs(type)) { + err = -E2BIG; + goto out_unlock; + } + + if (hid_hooks.pre_link_attach) { + err = hid_hooks.pre_link_attach(hdev, type); + if (err) + goto out_unlock; + } + + run_array = bpf_prog_array_alloc(cnt + 1, GFP_KERNEL); + if (!run_array) { + err = -ENOMEM; + goto out_unlock; + } + + if (flags & BPF_F_INSERT_HEAD) + list_add(&hid_link->node, &hdev->bpf.links[type]); + else + list_add_tail(&hid_link->node, &hdev->bpf.links[type]); + + fill_prog_array(hdev, type, run_array); + run_array = rcu_replace_pointer(hdev->bpf.run_array[type], run_array, + lockdep_is_held(&bpf_hid_mutex)); + bpf_prog_array_free(run_array); + + if (hid_hooks.post_link_attach) + hid_hooks.post_link_attach(hdev, type); + +out_unlock: + mutex_unlock(&bpf_hid_mutex); + return err; +} + +int bpf_hid_link_create(const union bpf_attr *attr, struct bpf_prog *prog) +{ + enum bpf_hid_attach_type hid_type; + struct bpf_link_primer link_primer; + struct bpf_hid_link *hid_link; + enum bpf_attach_type type; + struct hid_device *hdev; + int err; + + if ((attr->link_create.flags & ~BPF_F_INSERT_HEAD) || !hid_hooks.hdev_from_fd) + return -EINVAL; + + type = attr->link_create.attach_type; + hid_type = to_bpf_hid_attach_type(type); + if (hid_type < 0) + return -EINVAL; + + hdev = hid_hooks.hdev_from_fd(attr->link_create.target_fd); + if (IS_ERR(hdev)) + return PTR_ERR(hdev); + + hid_link = kzalloc(sizeof(*hid_link), GFP_USER); + if (!hid_link) + return -ENOMEM; + + bpf_link_init(&hid_link->link, BPF_LINK_TYPE_HID, + &bpf_hid_link_ops, prog); + hid_link->hdev = hdev; + hid_link->type = type; + hid_link->hid_type = hid_type; + + err = bpf_link_prime(&hid_link->link, &link_primer); + if (err) { + kfree(hid_link); + return err; + } + + err = bpf_hid_link_attach(hdev, &hid_link->link, hid_type, attr->link_create.flags); + if (err) { + bpf_link_cleanup(&link_primer); + return err; + } + + return bpf_link_settle(&link_primer); +} + +static int hid_bpf_prog_test_run(struct bpf_prog *prog, + const union bpf_attr *attr, + union bpf_attr __user *uattr) +{ + struct hid_device *hdev = NULL; + struct bpf_prog_array *progs; + bool valid_prog = false; + int i; + int target_fd, ret; + void __user *data_out = u64_to_user_ptr(attr->test.data_out); + void __user *data_in = u64_to_user_ptr(attr->test.data_in); + u32 user_size_in = attr->test.data_size_in; + u32 user_size_out = attr->test.data_size_out; + u32 allocated_size = max(user_size_in, user_size_out); + struct hid_bpf_ctx_kern ctx = { + .type = HID_BPF_USER_EVENT, + .allocated_size = allocated_size, + }; + + if (!hid_hooks.hdev_from_fd) + return -EOPNOTSUPP; + + if (attr->test.ctx_size_in != sizeof(int)) + return -EINVAL; + + if (allocated_size > HID_MAX_BUFFER_SIZE) + return -E2BIG; + + if (copy_from_user(&target_fd, (void *)attr->test.ctx_in, attr->test.ctx_size_in)) + return -EFAULT; + + hdev = hid_hooks.hdev_from_fd(target_fd); + if (IS_ERR(hdev)) + return PTR_ERR(hdev); + + if (allocated_size) { + ctx.data = kzalloc(allocated_size, GFP_KERNEL); + if (!ctx.data) + return -ENOMEM; + + ctx.allocated_size = allocated_size; + } + ctx.hdev = hdev; + + ret = mutex_lock_interruptible(&bpf_hid_mutex); + if (ret) + return ret; + + /* check if the given program is of correct type and registered */ + progs = rcu_dereference_protected(hdev->bpf.run_array[BPF_HID_ATTACH_USER_EVENT], + lockdep_is_held(&bpf_hid_mutex)); + if (!progs) { + ret = -EFAULT; + goto unlock; + } + + for (i = 0; i < bpf_prog_array_length(progs); i++) { + if (progs->items[i].prog == prog) { + valid_prog = true; + break; + } + } + + if (!valid_prog) { + ret = -EINVAL; + goto unlock; + } + + /* copy data_in from userspace */ + if (user_size_in) { + if (copy_from_user(ctx.data, data_in, user_size_in)) { + ret = -EFAULT; + goto unlock; + } + + ctx.size = user_size_in; + } + + migrate_disable(); + + ret = bpf_prog_run(prog, &ctx); + + migrate_enable(); + + if (user_size_out && data_out) { + user_size_out = min3(user_size_out, (u32)ctx.size, allocated_size); + + if (copy_to_user(data_out, ctx.data, user_size_out)) { + ret = -EFAULT; + goto unlock; + } + + if (copy_to_user(&uattr->test.data_size_out, + &user_size_out, + sizeof(user_size_out))) { + ret = -EFAULT; + goto unlock; + } + } + + if (copy_to_user(&uattr->test.retval, &ctx.retval, sizeof(ctx.retval))) + ret = -EFAULT; + +unlock: + kfree(ctx.data); + + mutex_unlock(&bpf_hid_mutex); + return ret; +} + +const struct bpf_prog_ops hid_prog_ops = { + .test_run = hid_bpf_prog_test_run, +}; + +int bpf_hid_init(struct hid_device *hdev) +{ + int type; + + for (type = 0; type < MAX_BPF_HID_ATTACH_TYPE; type++) + INIT_LIST_HEAD(&hdev->bpf.links[type]); + + return 0; +} +EXPORT_SYMBOL_GPL(bpf_hid_init); + +void bpf_hid_exit(struct hid_device *hdev) +{ + enum bpf_hid_attach_type type; + struct bpf_hid_link *hid_link; + + mutex_lock(&bpf_hid_mutex); + for (type = 0; type < MAX_BPF_HID_ATTACH_TYPE; type++) { + bpf_hid_run_array_detach(hdev, type); + list_for_each_entry(hid_link, &hdev->bpf.links[type], node) { + hid_link->hdev = NULL; /* auto-detach link */ + } + } + mutex_unlock(&bpf_hid_mutex); +} +EXPORT_SYMBOL_GPL(bpf_hid_exit); diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index b88688264ad0..d1c05011e5ab 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -3,6 +3,7 @@ */ #include #include +#include #include #include #include @@ -2205,6 +2206,7 @@ static bool is_sys_admin_prog_type(enum bpf_prog_type prog_type) { switch (prog_type) { case BPF_PROG_TYPE_LIRC_MODE2: + case BPF_PROG_TYPE_HID: return true; default: return false; @@ -3199,6 +3201,11 @@ attach_type_to_prog_type(enum bpf_attach_type attach_type) return BPF_PROG_TYPE_SK_LOOKUP; case BPF_XDP: return BPF_PROG_TYPE_XDP; + case BPF_HID_DEVICE_EVENT: + case BPF_HID_RDESC_FIXUP: + case BPF_HID_USER_EVENT: + case BPF_HID_DRIVER_EVENT: + return BPF_PROG_TYPE_HID; default: return BPF_PROG_TYPE_UNSPEC; } @@ -3342,6 +3349,11 @@ static int bpf_prog_query(const union bpf_attr *attr, case BPF_SK_MSG_VERDICT: case BPF_SK_SKB_VERDICT: return sock_map_bpf_prog_query(attr, uattr); + case BPF_HID_DEVICE_EVENT: + case BPF_HID_RDESC_FIXUP: + case BPF_HID_USER_EVENT: + case BPF_HID_DRIVER_EVENT: + return bpf_hid_prog_query(attr, uattr); default: return -EINVAL; } @@ -4336,6 +4348,8 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr) ret = bpf_perf_link_attach(attr, prog); break; #endif + case BPF_PROG_TYPE_HID: + return bpf_hid_link_create(attr, prog); default: ret = -EINVAL; } From patchwork Fri Mar 18 16:15:14 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12785605 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 578A9C433EF for ; Fri, 18 Mar 2022 16:16:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238722AbiCRQRr (ORCPT ); Fri, 18 Mar 2022 12:17:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55220 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238705AbiCRQRq (ORCPT ); Fri, 18 Mar 2022 12:17:46 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 808A4100E23 for ; Fri, 18 Mar 2022 09:16:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1647620186; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=OQUDhoqS/TjN6VMPOB7LXHwoVdY1tkFbLh8p1nVVoZ4=; b=MUTGL2kHI18uRwnMZw6w8uXofzGa3PA6e6D0qgyJT0cr/O2d0Me/G934zVfZI9wS+lFZHO kCx1pejN65Tgs9xKNOgLVuxnJoQHdpVSd8/MbMbcUveIwaGNYt/z96Ncvmz3r2zoPyvmpX TU49Aqj4sZg3e8KH2djs3/clIn61q9E= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-668-MtPvz53eNkyP14MEJMP5fg-1; Fri, 18 Mar 2022 12:16:23 -0400 X-MC-Unique: MtPvz53eNkyP14MEJMP5fg-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 58852296A624; Fri, 18 Mar 2022 16:16:22 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.159]) by smtp.corp.redhat.com (Postfix) with ESMTP id 39658421C3; Fri, 18 Mar 2022 16:16:19 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v3 03/17] bpf/verifier: prevent non GPL programs to be loaded against HID Date: Fri, 18 Mar 2022 17:15:14 +0100 Message-Id: <20220318161528.1531164-4-benjamin.tissoires@redhat.com> In-Reply-To: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> References: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net This is just to hammer the obvious because I suspect you can not already load a bpf HID program which is not GPL because all of the useful functions are GPL only. Anyway, this ensures that users are not tempted to bypass this requirement and will allow us to ship tested BPF programs in the kernel without having to aorry about the license. Signed-off-by: Benjamin Tissoires Acked-by: Song Liu --- no changes in v3 new in v2: - Note: I placed this statement in check_attach_btf_id() to be local to other similar checks (regarding LSM), however, I have no idea if this is the correct place. Please shout at me if it isn't. --- include/linux/bpf-hid.h | 8 ++++++++ kernel/bpf/hid.c | 12 ++++++++++++ kernel/bpf/verifier.c | 7 +++++++ 3 files changed, 27 insertions(+) diff --git a/include/linux/bpf-hid.h b/include/linux/bpf-hid.h index 9c8dbd389995..7f596554fe8c 100644 --- a/include/linux/bpf-hid.h +++ b/include/linux/bpf-hid.h @@ -2,6 +2,7 @@ #ifndef _BPF_HID_H #define _BPF_HID_H +#include #include #include #include @@ -69,6 +70,8 @@ int bpf_hid_prog_query(const union bpf_attr *attr, union bpf_attr __user *uattr); int bpf_hid_link_create(const union bpf_attr *attr, struct bpf_prog *prog); +int bpf_hid_verify_prog(struct bpf_verifier_log *vlog, + const struct bpf_prog *prog); #else static inline int bpf_hid_prog_query(const union bpf_attr *attr, union bpf_attr __user *uattr) @@ -81,6 +84,11 @@ static inline int bpf_hid_link_create(const union bpf_attr *attr, { return -EOPNOTSUPP; } +static inline int bpf_hid_verify_prog(struct bpf_verifier_log *vlog, + const struct bpf_prog *prog) +{ + return -EOPNOTSUPP; +} #endif static inline bool bpf_hid_link_empty(struct bpf_hid *bpf, diff --git a/kernel/bpf/hid.c b/kernel/bpf/hid.c index c21dc05f6207..2dfeaaa8a83f 100644 --- a/kernel/bpf/hid.c +++ b/kernel/bpf/hid.c @@ -34,6 +34,18 @@ void bpf_hid_set_hooks(struct bpf_hid_hooks *hooks) } EXPORT_SYMBOL_GPL(bpf_hid_set_hooks); +int bpf_hid_verify_prog(struct bpf_verifier_log *vlog, + const struct bpf_prog *prog) +{ + if (!prog->gpl_compatible) { + bpf_log(vlog, + "HID programs must have a GPL compatible license\n"); + return -EINVAL; + } + + return 0; +} + BPF_CALL_3(bpf_hid_get_data, struct hid_bpf_ctx_kern*, ctx, u64, offset, u64, size) { if (!size) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index cf92f9c01556..da06d633fb8d 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -14272,6 +14273,12 @@ static int check_attach_btf_id(struct bpf_verifier_env *env) if (prog->type == BPF_PROG_TYPE_STRUCT_OPS) return check_struct_ops_btf_id(env); + if (prog->type == BPF_PROG_TYPE_HID) { + ret = bpf_hid_verify_prog(&env->log, prog); + if (ret < 0) + return ret; + } + if (prog->type != BPF_PROG_TYPE_TRACING && prog->type != BPF_PROG_TYPE_LSM && prog->type != BPF_PROG_TYPE_EXT) From patchwork Fri Mar 18 16:15:15 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12785615 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8ADC3C433FE for ; Fri, 18 Mar 2022 16:18:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238861AbiCRQTv (ORCPT ); Fri, 18 Mar 2022 12:19:51 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57166 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238826AbiCRQSL (ORCPT ); Fri, 18 Mar 2022 12:18:11 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 724C81D78A3 for ; Fri, 18 Mar 2022 09:16:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1647620197; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=8pb2b2yBvlyePXc4neaXnpSmeAYYQklyRoO6YAaoGbs=; b=CgFRV8nMCc4NPi95IwikJfyHz/rHRrU+t/08kCa9ZA1rvdLgO0KOEo1IcsaAzP8tU2MuXT 2QDVNcZZt/sDmhMjlAvvlEaJyGHG21JcWOCdjgg5j71qOHdKgmohhL35PTUPCWlVO1eU+u 8pnRtOBAWduGz977b2JvF2ECR97dnnk= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-137-AanS3ePaM0SrClnrgLZ8Og-1; Fri, 18 Mar 2022 12:16:35 -0400 X-MC-Unique: AanS3ePaM0SrClnrgLZ8Og-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id D9A6C899EC1; Fri, 18 Mar 2022 16:16:28 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.159]) by smtp.corp.redhat.com (Postfix) with ESMTP id 95DC833250; Fri, 18 Mar 2022 16:16:22 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v3 04/17] libbpf: add HID program type and API Date: Fri, 18 Mar 2022 17:15:15 +0100 Message-Id: <20220318161528.1531164-5-benjamin.tissoires@redhat.com> In-Reply-To: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> References: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net HID-bpf program type are needing new SECs. To bind a hid-bpf program, we can rely on bpf_program__attach_fd() so export a new function to the API. Signed-off-by: Benjamin Tissoires Acked-by: Song Liu --- changes in v3: - squashed the libbpf changes into 1 - moved bpf_program__attach_hid to 0.8.0 - added SEC_DEF("hid/driver_event") changes in v2: - split the series by bpf/libbpf/hid/selftests and samples --- tools/include/uapi/linux/bpf.h | 31 +++++++++++++++++++++++++++++++ tools/lib/bpf/libbpf.c | 23 +++++++++++++++++------ tools/lib/bpf/libbpf.h | 2 ++ tools/lib/bpf/libbpf.map | 1 + 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 99fab54ae9c0..0e8438e93768 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -952,6 +952,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_LSM, BPF_PROG_TYPE_SK_LOOKUP, BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */ + BPF_PROG_TYPE_HID, }; enum bpf_attach_type { @@ -997,6 +998,10 @@ enum bpf_attach_type { BPF_SK_REUSEPORT_SELECT, BPF_SK_REUSEPORT_SELECT_OR_MIGRATE, BPF_PERF_EVENT, + BPF_HID_DEVICE_EVENT, + BPF_HID_RDESC_FIXUP, + BPF_HID_USER_EVENT, + BPF_HID_DRIVER_EVENT, __MAX_BPF_ATTACH_TYPE }; @@ -1011,6 +1016,7 @@ enum bpf_link_type { BPF_LINK_TYPE_NETNS = 5, BPF_LINK_TYPE_XDP = 6, BPF_LINK_TYPE_PERF_EVENT = 7, + BPF_LINK_TYPE_HID = 8, MAX_BPF_LINK_TYPE, }; @@ -1118,6 +1124,16 @@ enum bpf_link_type { */ #define BPF_F_XDP_HAS_FRAGS (1U << 5) +/* HID flag used in BPF_LINK_CREATE command + * + * NONE(default): The bpf program will be added at the tail of the list + * of existing bpf program for this type. + * + * BPF_F_INSERT_HEAD: The bpf program will be added at the beginning + * of the list of existing bpf program for this type.. + */ +#define BPF_F_INSERT_HEAD (1U << 0) + /* When BPF ldimm64's insn[0].src_reg != 0 then this can have * the following extensions: * @@ -5129,6 +5145,16 @@ union bpf_attr { * The **hash_algo** is returned on success, * **-EOPNOTSUP** if the hash calculation failed or **-EINVAL** if * invalid arguments are passed. + * + * void *bpf_hid_get_data(void *ctx, u64 offset, u64 size) + * Description + * Returns a pointer to the data associated with context at the given + * offset and size (in bytes). + * + * Note: the returned pointer is refcounted and must be dereferenced + * by a call to bpf_hid_discard; + * Return + * The pointer to the data. On error, a null value is returned. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -5325,6 +5351,7 @@ union bpf_attr { FN(copy_from_user_task), \ FN(skb_set_tstamp), \ FN(ima_file_hash), \ + FN(hid_get_data), \ /* */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper @@ -5925,6 +5952,10 @@ struct bpf_link_info { struct { __u32 ifindex; } xdp; + struct { + __s32 hidraw_number; + __u32 attach_type; + } hid; }; } __attribute__((aligned(8))); diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 43161fdd44bb..6b9ba313eb5b 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -8675,6 +8675,10 @@ static const struct bpf_sec_def section_defs[] = { SEC_DEF("cgroup/setsockopt", CGROUP_SOCKOPT, BPF_CGROUP_SETSOCKOPT, SEC_ATTACHABLE | SEC_SLOPPY_PFX), SEC_DEF("struct_ops+", STRUCT_OPS, 0, SEC_NONE), SEC_DEF("sk_lookup", SK_LOOKUP, BPF_SK_LOOKUP, SEC_ATTACHABLE | SEC_SLOPPY_PFX), + SEC_DEF("hid/device_event", HID, BPF_HID_DEVICE_EVENT, SEC_ATTACHABLE_OPT), + SEC_DEF("hid/rdesc_fixup", HID, BPF_HID_RDESC_FIXUP, SEC_ATTACHABLE_OPT), + SEC_DEF("hid/user_event", HID, BPF_HID_USER_EVENT, SEC_ATTACHABLE_OPT), + SEC_DEF("hid/driver_event", HID, BPF_HID_DRIVER_EVENT, SEC_ATTACHABLE_OPT), }; static size_t custom_sec_def_cnt; @@ -10630,10 +10634,11 @@ static int attach_lsm(const struct bpf_program *prog, long cookie, struct bpf_li static struct bpf_link * bpf_program__attach_fd(const struct bpf_program *prog, int target_fd, int btf_id, - const char *target_name) + const char *target_name, __u32 flags) { DECLARE_LIBBPF_OPTS(bpf_link_create_opts, opts, - .target_btf_id = btf_id); + .target_btf_id = btf_id, + .flags = flags); enum bpf_attach_type attach_type; char errmsg[STRERR_BUFSIZE]; struct bpf_link *link; @@ -10667,19 +10672,19 @@ bpf_program__attach_fd(const struct bpf_program *prog, int target_fd, int btf_id struct bpf_link * bpf_program__attach_cgroup(const struct bpf_program *prog, int cgroup_fd) { - return bpf_program__attach_fd(prog, cgroup_fd, 0, "cgroup"); + return bpf_program__attach_fd(prog, cgroup_fd, 0, "cgroup", 0); } struct bpf_link * bpf_program__attach_netns(const struct bpf_program *prog, int netns_fd) { - return bpf_program__attach_fd(prog, netns_fd, 0, "netns"); + return bpf_program__attach_fd(prog, netns_fd, 0, "netns", 0); } struct bpf_link *bpf_program__attach_xdp(const struct bpf_program *prog, int ifindex) { /* target_fd/target_ifindex use the same field in LINK_CREATE */ - return bpf_program__attach_fd(prog, ifindex, 0, "xdp"); + return bpf_program__attach_fd(prog, ifindex, 0, "xdp", 0); } struct bpf_link *bpf_program__attach_freplace(const struct bpf_program *prog, @@ -10705,7 +10710,7 @@ struct bpf_link *bpf_program__attach_freplace(const struct bpf_program *prog, if (btf_id < 0) return libbpf_err_ptr(btf_id); - return bpf_program__attach_fd(prog, target_fd, btf_id, "freplace"); + return bpf_program__attach_fd(prog, target_fd, btf_id, "freplace", 0); } else { /* no target, so use raw_tracepoint_open for compatibility * with old kernels @@ -10760,6 +10765,12 @@ static int attach_iter(const struct bpf_program *prog, long cookie, struct bpf_l return libbpf_get_error(*link); } +struct bpf_link * +bpf_program__attach_hid(const struct bpf_program *prog, int hid_fd, __u32 flags) +{ + return bpf_program__attach_fd(prog, hid_fd, 0, "hid", flags); +} + struct bpf_link *bpf_program__attach(const struct bpf_program *prog) { struct bpf_link *link = NULL; diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index c1b0c2ef14d8..13dff4865da5 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -529,6 +529,8 @@ struct bpf_iter_attach_opts { LIBBPF_API struct bpf_link * bpf_program__attach_iter(const struct bpf_program *prog, const struct bpf_iter_attach_opts *opts); +LIBBPF_API struct bpf_link * +bpf_program__attach_hid(const struct bpf_program *prog, int hid_fd, __u32 flags); /* * Libbpf allows callers to adjust BPF programs before being loaded diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index df1b947792c8..cd8da9a8bf36 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -442,6 +442,7 @@ LIBBPF_0.7.0 { LIBBPF_0.8.0 { global: + bpf_program__attach_hid; libbpf_register_prog_handler; libbpf_unregister_prog_handler; } LIBBPF_0.7.0; From patchwork Fri Mar 18 16:15:16 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12785607 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 17C3FC433FE for ; Fri, 18 Mar 2022 16:17:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238765AbiCRQTJ (ORCPT ); Fri, 18 Mar 2022 12:19:09 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56820 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238812AbiCRQSH (ORCPT ); Fri, 18 Mar 2022 12:18:07 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id CB2B42EAF58 for ; Fri, 18 Mar 2022 09:16:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1647620196; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=eXuLy5g/OhC5EeUy3VzZbdQz9XUs2ZGjc4bL864jlXU=; b=AfM3t1AbuaZLiEh9GTEk0Gl5JOa4nl1OoNMmW+99iPU/ei3KG7LaUTiL90N1JmfZ3ux8SY R45qJPYB+gh1oYrvLNU56eJoSlHqmwacl17EcpuqR7vGVqeK+Is+YGdLoBIRMej9/S1IbQ 0trVXp96xDiKXmMYBeSKr5t5Ypq4zss= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-327-uMjUVThaOgikLjtTgGLUNw-1; Fri, 18 Mar 2022 12:16:33 -0400 X-MC-Unique: uMjUVThaOgikLjtTgGLUNw-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 472A03804092; Fri, 18 Mar 2022 16:16:32 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.159]) by smtp.corp.redhat.com (Postfix) with ESMTP id 22E4233250; Fri, 18 Mar 2022 16:16:29 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v3 05/17] HID: hook up with bpf Date: Fri, 18 Mar 2022 17:15:16 +0100 Message-Id: <20220318161528.1531164-6-benjamin.tissoires@redhat.com> In-Reply-To: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> References: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Now that BPF can be compatible with HID, add the capability into HID. drivers/hid/hid-bpf.c takes care of the glue between bpf and HID, and hid-core can then inject any incoming event from the device into a BPF program to filter/analyze it. Signed-off-by: Benjamin Tissoires --- changes in v3: - squashed "only call hid_bpf_raw_event() if a ctx is available" and "bpf: compute only the required buffer size for the device" into this one - ensure the ctx.size is properly bounded by allocated size - s/link_attach/pre_link_attach/ - s/array_detached/array_detach/ - fix default switch case when doing nothing - reworked hid_bpf_pre_link_attach() to avoid the switch changes in v2: - split the series by bpf/libbpf/hid/selftests and samples - addressed review comments from v1 --- drivers/hid/Makefile | 1 + drivers/hid/hid-bpf.c | 174 +++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-core.c | 24 +++++- include/linux/hid.h | 11 +++ 4 files changed, 207 insertions(+), 3 deletions(-) create mode 100644 drivers/hid/hid-bpf.c diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 6d3e630e81af..08d2d7619937 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -4,6 +4,7 @@ # hid-y := hid-core.o hid-input.o hid-quirks.o hid-$(CONFIG_DEBUG_FS) += hid-debug.o +hid-$(CONFIG_BPF) += hid-bpf.o obj-$(CONFIG_HID) += hid.o obj-$(CONFIG_UHID) += uhid.o diff --git a/drivers/hid/hid-bpf.c b/drivers/hid/hid-bpf.c new file mode 100644 index 000000000000..5060ebcb9979 --- /dev/null +++ b/drivers/hid/hid-bpf.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * BPF in HID support for Linux + * + * Copyright (c) 2022 Benjamin Tissoires + */ + +#include +#include +#include + +#include +#include + +static int __hid_bpf_match_sysfs(struct device *dev, const void *data) +{ + struct kernfs_node *kn = dev->kobj.sd; + struct kernfs_node *uevent_kn; + + uevent_kn = kernfs_find_and_get_ns(kn, "uevent", NULL); + + return uevent_kn == data; +} + +static struct hid_device *hid_bpf_fd_to_hdev(int fd) +{ + struct device *dev; + struct hid_device *hdev; + struct fd f = fdget(fd); + struct inode *inode; + struct kernfs_node *node; + + if (!f.file) { + hdev = ERR_PTR(-EBADF); + goto out; + } + + inode = file_inode(f.file); + node = inode->i_private; + + dev = bus_find_device(&hid_bus_type, NULL, node, __hid_bpf_match_sysfs); + + if (dev) + hdev = to_hid_device(dev); + else + hdev = ERR_PTR(-EINVAL); + + out: + fdput(f); + return hdev; +} + +static int hid_bpf_pre_link_attach(struct hid_device *hdev, enum bpf_hid_attach_type type) +{ + int err = 0; + unsigned int i, j, max_report_len = 0; + unsigned int alloc_size = 0; + + if (type != BPF_HID_ATTACH_DEVICE_EVENT) + return 0; + + /* hdev->bpf.device_data is already allocated, abort */ + if (hdev->bpf.device_data) + return 0; + + /* compute the maximum report length for this device */ + for (i = 0; i < HID_REPORT_TYPES; i++) { + struct hid_report_enum *report_enum = hdev->report_enum + i; + + for (j = 0; j < HID_MAX_IDS; j++) { + struct hid_report *report = report_enum->report_id_hash[j]; + + if (report) + max_report_len = max(max_report_len, hid_report_len(report)); + } + } + + /* + * Give us a little bit of extra space and some predictability in the + * buffer length we create. This way, we can tell users that they can + * work on chunks of 64 bytes of memory without having the bpf verifier + * scream at them. + */ + alloc_size = DIV_ROUND_UP(max_report_len, 64) * 64; + + hdev->bpf.device_data = kzalloc(alloc_size, GFP_KERNEL); + if (!hdev->bpf.device_data) + err = -ENOMEM; + else + hdev->bpf.allocated_data = alloc_size; + + return err; +} + +static void hid_bpf_array_detach(struct hid_device *hdev, enum bpf_hid_attach_type type) +{ + switch (type) { + case BPF_HID_ATTACH_DEVICE_EVENT: + kfree(hdev->bpf.device_data); + hdev->bpf.device_data = NULL; + break; + default: + /* do nothing */ + break; + } +} + +static int hid_bpf_run_progs(struct hid_device *hdev, struct hid_bpf_ctx_kern *ctx) +{ + enum bpf_hid_attach_type type; + + if (!ctx) + return -EINVAL; + + switch (ctx->type) { + case HID_BPF_DEVICE_EVENT: + type = BPF_HID_ATTACH_DEVICE_EVENT; + break; + default: + return -EINVAL; + } + + if (!hdev->bpf.run_array[type]) + return 0; + + return BPF_PROG_RUN_ARRAY(hdev->bpf.run_array[type], ctx, bpf_prog_run); +} + +u8 *hid_bpf_raw_event(struct hid_device *hdev, u8 *data, int *size) +{ + int ret; + struct hid_bpf_ctx_kern ctx = { + .type = HID_BPF_DEVICE_EVENT, + .hdev = hdev, + .size = *size, + .data = hdev->bpf.device_data, + .allocated_size = hdev->bpf.allocated_data, + }; + + if (bpf_hid_link_empty(&hdev->bpf, BPF_HID_ATTACH_DEVICE_EVENT)) + return data; + + memset(ctx.data, 0, hdev->bpf.allocated_data); + memcpy(ctx.data, data, *size); + + ret = hid_bpf_run_progs(hdev, &ctx); + if (ret) + return ERR_PTR(-EIO); + + if (!ctx.size || ctx.size > ctx.allocated_size) + return ERR_PTR(-EINVAL); + + *size = ctx.size; + + return ctx.data; +} + +int __init hid_bpf_module_init(void) +{ + struct bpf_hid_hooks hooks = { + .hdev_from_fd = hid_bpf_fd_to_hdev, + .pre_link_attach = hid_bpf_pre_link_attach, + .array_detach = hid_bpf_array_detach, + }; + + bpf_hid_set_hooks(&hooks); + + return 0; +} + +void __exit hid_bpf_module_exit(void) +{ + bpf_hid_set_hooks(NULL); +} diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index f1aed5bbd000..937fab7eb9c6 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1748,13 +1748,24 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size, struct hid_driver *hdrv; unsigned int a; u32 rsize, csize = size; - u8 *cdata = data; + u8 *cdata; int ret = 0; + /* we pre-test if device_data is available here to cut the calls at the earliest */ + if (hid->bpf.device_data) { + data = hid_bpf_raw_event(hid, data, &size); + if (IS_ERR(data)) { + ret = PTR_ERR(data); + goto out; + } + } + report = hid_get_report(report_enum, data); if (!report) goto out; + cdata = data; + if (report_enum->numbered) { cdata++; csize--; @@ -2528,10 +2539,12 @@ int hid_add_device(struct hid_device *hdev) hid_debug_register(hdev, dev_name(&hdev->dev)); ret = device_add(&hdev->dev); - if (!ret) + if (!ret) { hdev->status |= HID_STAT_ADDED; - else + } else { hid_debug_unregister(hdev); + bpf_hid_exit(hdev); + } return ret; } @@ -2567,6 +2580,7 @@ struct hid_device *hid_allocate_device(void) spin_lock_init(&hdev->debug_list_lock); sema_init(&hdev->driver_input_lock, 1); mutex_init(&hdev->ll_open_lock); + bpf_hid_init(hdev); return hdev; } @@ -2574,6 +2588,7 @@ EXPORT_SYMBOL_GPL(hid_allocate_device); static void hid_remove_device(struct hid_device *hdev) { + bpf_hid_exit(hdev); if (hdev->status & HID_STAT_ADDED) { device_del(&hdev->dev); hid_debug_unregister(hdev); @@ -2700,6 +2715,8 @@ static int __init hid_init(void) hid_debug_init(); + hid_bpf_module_init(); + return 0; err_bus: bus_unregister(&hid_bus_type); @@ -2709,6 +2726,7 @@ static int __init hid_init(void) static void __exit hid_exit(void) { + hid_bpf_module_exit(); hid_debug_exit(); hidraw_exit(); bus_unregister(&hid_bus_type); diff --git a/include/linux/hid.h b/include/linux/hid.h index 56f6f4ad45a7..8fd79011f461 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -27,6 +27,7 @@ #include #include #include +#include /* * We parse each description item into this structure. Short items data @@ -1210,4 +1211,14 @@ do { \ #define hid_dbg_once(hid, fmt, ...) \ dev_dbg_once(&(hid)->dev, fmt, ##__VA_ARGS__) +#ifdef CONFIG_BPF +u8 *hid_bpf_raw_event(struct hid_device *hdev, u8 *rd, int *size); +int hid_bpf_module_init(void); +void hid_bpf_module_exit(void); +#else +static inline u8 *hid_bpf_raw_event(struct hid_device *hdev, u8 *rd, int *size) { return rd; } +static inline int hid_bpf_module_init(void) { return 0; } +static inline void hid_bpf_module_exit(void) {} +#endif + #endif From patchwork Fri Mar 18 16:15:17 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12785616 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B9E9DC433F5 for ; Fri, 18 Mar 2022 16:18:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238799AbiCRQTw (ORCPT ); Fri, 18 Mar 2022 12:19:52 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57314 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238856AbiCRQSM (ORCPT ); Fri, 18 Mar 2022 12:18:12 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id A964A2EF0D8 for ; Fri, 18 Mar 2022 09:16:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1647620205; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=g5tRmrZUKM3tsTBn6dkiecWK2nGanjyI5ExDEzCAG/c=; b=eqcPX29FiZO8y/dVbhMJMuS/OsvhPEQa8bMIXFTP7ArdFtuYFnoJ4UnCavnBmFIs2R20YZ fmU0I2hvym94vquwxI2TUULrQyLirnEUuKUcvj0zPIBhNFxScYzp++TpHdndZ3QoZI8P0G FAvnNaZTfhcCHWxvc7tk3c4JiSq0hLM= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-623-ejlFZfI0P8WeJMJ7D26siA-1; Fri, 18 Mar 2022 12:16:41 -0400 X-MC-Unique: ejlFZfI0P8WeJMJ7D26siA-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 6C180380407F; Fri, 18 Mar 2022 16:16:39 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.159]) by smtp.corp.redhat.com (Postfix) with ESMTP id 83F933543C; Fri, 18 Mar 2022 16:16:32 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v3 06/17] HID: allow to change the report descriptor from an eBPF program Date: Fri, 18 Mar 2022 17:15:17 +0100 Message-Id: <20220318161528.1531164-7-benjamin.tissoires@redhat.com> In-Reply-To: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> References: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Make use of BPF_HID_ATTACH_RDESC_FIXUP so we can trigger an rdesc fixup in the bpf world. Whenever the program gets attached/detached, the device is reconnected meaning that userspace will see it disappearing and reappearing with the new report descriptor. Signed-off-by: Benjamin Tissoires --- changes in v3: - ensure the ctx.size is properly bounded by allocated size - s/link_attached/post_link_attach/ - removed the switch statement with only one case changes in v2: - split the series by bpf/libbpf/hid/selftests and samples --- drivers/hid/hid-bpf.c | 62 ++++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-core.c | 3 +- include/linux/hid.h | 6 ++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-bpf.c b/drivers/hid/hid-bpf.c index 5060ebcb9979..45c87ff47324 100644 --- a/drivers/hid/hid-bpf.c +++ b/drivers/hid/hid-bpf.c @@ -50,6 +50,14 @@ static struct hid_device *hid_bpf_fd_to_hdev(int fd) return hdev; } +static int hid_reconnect(struct hid_device *hdev) +{ + if (!test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status)) + return device_reprobe(&hdev->dev); + + return 0; +} + static int hid_bpf_pre_link_attach(struct hid_device *hdev, enum bpf_hid_attach_type type) { int err = 0; @@ -92,6 +100,12 @@ static int hid_bpf_pre_link_attach(struct hid_device *hdev, enum bpf_hid_attach_ return err; } +static void hid_bpf_post_link_attach(struct hid_device *hdev, enum bpf_hid_attach_type type) +{ + if (type == BPF_HID_ATTACH_RDESC_FIXUP) + hid_reconnect(hdev); +} + static void hid_bpf_array_detach(struct hid_device *hdev, enum bpf_hid_attach_type type) { switch (type) { @@ -99,6 +113,9 @@ static void hid_bpf_array_detach(struct hid_device *hdev, enum bpf_hid_attach_ty kfree(hdev->bpf.device_data); hdev->bpf.device_data = NULL; break; + case BPF_HID_ATTACH_RDESC_FIXUP: + hid_reconnect(hdev); + break; default: /* do nothing */ break; @@ -116,6 +133,9 @@ static int hid_bpf_run_progs(struct hid_device *hdev, struct hid_bpf_ctx_kern *c case HID_BPF_DEVICE_EVENT: type = BPF_HID_ATTACH_DEVICE_EVENT; break; + case HID_BPF_RDESC_FIXUP: + type = BPF_HID_ATTACH_RDESC_FIXUP; + break; default: return -EINVAL; } @@ -155,11 +175,53 @@ u8 *hid_bpf_raw_event(struct hid_device *hdev, u8 *data, int *size) return ctx.data; } +u8 *hid_bpf_report_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size) +{ + int ret; + struct hid_bpf_ctx_kern ctx = { + .type = HID_BPF_RDESC_FIXUP, + .hdev = hdev, + .size = *size, + }; + + if (bpf_hid_link_empty(&hdev->bpf, BPF_HID_ATTACH_RDESC_FIXUP)) + goto ignore_bpf; + + ctx.data = kmemdup(rdesc, HID_MAX_DESCRIPTOR_SIZE, GFP_KERNEL); + if (!ctx.data) + goto ignore_bpf; + + ctx.allocated_size = HID_MAX_DESCRIPTOR_SIZE; + + ret = hid_bpf_run_progs(hdev, &ctx); + if (ret) + goto ignore_bpf; + + if (ctx.size > ctx.allocated_size) + goto ignore_bpf; + + *size = ctx.size; + + if (*size) { + rdesc = krealloc(ctx.data, *size, GFP_KERNEL); + } else { + rdesc = NULL; + kfree(ctx.data); + } + + return rdesc; + + ignore_bpf: + kfree(ctx.data); + return kmemdup(rdesc, *size, GFP_KERNEL); +} + int __init hid_bpf_module_init(void) { struct bpf_hid_hooks hooks = { .hdev_from_fd = hid_bpf_fd_to_hdev, .pre_link_attach = hid_bpf_pre_link_attach, + .post_link_attach = hid_bpf_post_link_attach, .array_detach = hid_bpf_array_detach, }; diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 937fab7eb9c6..3182c39db006 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1213,7 +1213,8 @@ int hid_open_report(struct hid_device *device) return -ENODEV; size = device->dev_rsize; - buf = kmemdup(start, size, GFP_KERNEL); + /* hid_bpf_report_fixup() ensures we work on a copy of rdesc */ + buf = hid_bpf_report_fixup(device, start, &size); if (buf == NULL) return -ENOMEM; diff --git a/include/linux/hid.h b/include/linux/hid.h index 8fd79011f461..66d949d10b78 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -1213,10 +1213,16 @@ do { \ #ifdef CONFIG_BPF u8 *hid_bpf_raw_event(struct hid_device *hdev, u8 *rd, int *size); +u8 *hid_bpf_report_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size); int hid_bpf_module_init(void); void hid_bpf_module_exit(void); #else static inline u8 *hid_bpf_raw_event(struct hid_device *hdev, u8 *rd, int *size) { return rd; } +static inline u8 *hid_bpf_report_fixup(struct hid_device *hdev, u8 *rdesc, + unsigned int *size) +{ + return kmemdup(rdesc, *size, GFP_KERNEL); +} static inline int hid_bpf_module_init(void) { return 0; } static inline void hid_bpf_module_exit(void) {} #endif From patchwork Fri Mar 18 16:15:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12785608 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 52C3CC43217 for ; Fri, 18 Mar 2022 16:17:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238783AbiCRQTK (ORCPT ); Fri, 18 Mar 2022 12:19:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56094 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238855AbiCRQSM (ORCPT ); Fri, 18 Mar 2022 12:18:12 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 422D22EF0D5 for ; Fri, 18 Mar 2022 09:16:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1647620206; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ubLJslNgZ9sMaMTCN6rQ+dUrD9zTEF5COSyg2NKPYdQ=; b=jN0t5vco7pQYPF1lUbuzSwIX6DWhrOYgEN8cb1o1giwA0IzIf/OZDNryB1Bs8n4wRpFbBT C0IhgiRJfbr3hNluaFx9qooOe82cMEJtwKUD47SlYWXKCROISgg4uKTTcNJmOxVnGI/FPO oO/NjtyMFCPao9Vk4WTtovUPOW4MW18= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-31-8Yv22pqYM2yaCJwL3w4cNQ-1; Fri, 18 Mar 2022 12:16:44 -0400 X-MC-Unique: 8Yv22pqYM2yaCJwL3w4cNQ-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 2C361185A7A4; Fri, 18 Mar 2022 16:16:43 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.159]) by smtp.corp.redhat.com (Postfix) with ESMTP id AA0027AD1; Fri, 18 Mar 2022 16:16:39 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v3 07/17] selftests/bpf: add tests for the HID-bpf initial implementation Date: Fri, 18 Mar 2022 17:15:18 +0100 Message-Id: <20220318161528.1531164-8-benjamin.tissoires@redhat.com> In-Reply-To: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> References: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net The test is pretty basic: - create a virtual uhid device that no userspace will like (to not mess up the running system) - attach a BPF prog to it - open the matching hidraw node - inject one event and check: * that the BPF program can do something on the event stream * can modify the event stream Signed-off-by: Benjamin Tissoires --- changes in v3: - squashed "hid: rely on uhid event to know if a test device is ready" into this one - add selftests bpf VM config changes - s/hidraw_ino/hidraw_number/ changes in v2: - split the series by bpf/libbpf/hid/selftests and samples --- tools/testing/selftests/bpf/config | 3 + tools/testing/selftests/bpf/prog_tests/hid.c | 444 +++++++++++++++++++ tools/testing/selftests/bpf/progs/hid.c | 25 ++ 3 files changed, 472 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/hid.c create mode 100644 tools/testing/selftests/bpf/progs/hid.c diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config index 763db63a3890..b983c76535f8 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -53,3 +53,6 @@ CONFIG_NF_DEFRAG_IPV4=y CONFIG_NF_DEFRAG_IPV6=y CONFIG_NF_CONNTRACK=y CONFIG_USERFAULTFD=y +CONFIG_HID=y +CONFIG_HIDRAW=y +CONFIG_UHID=y diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c new file mode 100644 index 000000000000..9f9dd08d3232 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/hid.c @@ -0,0 +1,444 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Red Hat */ +#include +#include +#include "hid.skel.h" + +#include +#include +#include +#include +#include +#include + +static unsigned char rdesc[] = { + 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ + 0x09, 0x21, /* Usage (Vendor Usage 0x21) */ + 0xa1, 0x01, /* COLLECTION (Application) */ + 0x09, 0x01, /* Usage (Vendor Usage 0x01) */ + 0xa1, 0x00, /* COLLECTION (Physical) */ + 0x85, 0x01, /* REPORT_ID (1) */ + 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ + 0x19, 0x01, /* USAGE_MINIMUM (1) */ + 0x29, 0x03, /* USAGE_MAXIMUM (3) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x81, 0x02, /* INPUT (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x05, /* REPORT_SIZE (5) */ + 0x81, 0x01, /* INPUT (Cnst,Var,Abs) */ + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x30, /* USAGE (X) */ + 0x09, 0x31, /* USAGE (Y) */ + 0x15, 0x81, /* LOGICAL_MINIMUM (-127) */ + 0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */ + 0x75, 0x10, /* REPORT_SIZE (16) */ + 0x95, 0x02, /* REPORT_COUNT (2) */ + 0x81, 0x06, /* INPUT (Data,Var,Rel) */ + + 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ + 0x19, 0x01, /* USAGE_MINIMUM (1) */ + 0x29, 0x03, /* USAGE_MAXIMUM (3) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x91, 0x02, /* Output (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x05, /* REPORT_SIZE (5) */ + 0x91, 0x01, /* Output (Cnst,Var,Abs) */ + + 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ + 0x19, 0x06, /* USAGE_MINIMUM (6) */ + 0x29, 0x08, /* USAGE_MAXIMUM (8) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0xb1, 0x02, /* Feature (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x05, /* REPORT_SIZE (5) */ + 0x91, 0x01, /* Output (Cnst,Var,Abs) */ + + 0xc0, /* END_COLLECTION */ + 0xc0, /* END_COLLECTION */ +}; + +static pthread_mutex_t uhid_started_mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t uhid_started = PTHREAD_COND_INITIALIZER; + +/* no need to protect uhid_stopped, only one thread accesses it */ +static bool uhid_stopped; + +static int uhid_write(int fd, const struct uhid_event *ev) +{ + ssize_t ret; + + ret = write(fd, ev, sizeof(*ev)); + if (ret < 0) { + fprintf(stderr, "Cannot write to uhid: %m\n"); + return -errno; + } else if (ret != sizeof(*ev)) { + fprintf(stderr, "Wrong size written to uhid: %zd != %zu\n", + ret, sizeof(ev)); + return -EFAULT; + } else { + return 0; + } +} + +static int create(int fd, int rand_nb) +{ + struct uhid_event ev; + char buf[25]; + + sprintf(buf, "test-uhid-device-%d", rand_nb); + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_CREATE; + strcpy((char *)ev.u.create.name, buf); + ev.u.create.rd_data = rdesc; + ev.u.create.rd_size = sizeof(rdesc); + ev.u.create.bus = BUS_USB; + ev.u.create.vendor = 0x0001; + ev.u.create.product = 0x0a37; + ev.u.create.version = 0; + ev.u.create.country = 0; + + sprintf(buf, "%d", rand_nb); + strcpy((char *)ev.u.create.phys, buf); + + return uhid_write(fd, &ev); +} + +static void destroy(int fd) +{ + struct uhid_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_DESTROY; + + uhid_write(fd, &ev); +} + +static int event(int fd) +{ + struct uhid_event ev; + ssize_t ret; + + memset(&ev, 0, sizeof(ev)); + ret = read(fd, &ev, sizeof(ev)); + if (ret == 0) { + fprintf(stderr, "Read HUP on uhid-cdev\n"); + return -EFAULT; + } else if (ret < 0) { + fprintf(stderr, "Cannot read uhid-cdev: %m\n"); + return -errno; + } else if (ret != sizeof(ev)) { + fprintf(stderr, "Invalid size read from uhid-dev: %zd != %zu\n", + ret, sizeof(ev)); + return -EFAULT; + } + + switch (ev.type) { + case UHID_START: + pthread_mutex_lock(&uhid_started_mtx); + pthread_cond_signal(&uhid_started); + pthread_mutex_unlock(&uhid_started_mtx); + + fprintf(stderr, "UHID_START from uhid-dev\n"); + break; + case UHID_STOP: + uhid_stopped = true; + + fprintf(stderr, "UHID_STOP from uhid-dev\n"); + break; + case UHID_OPEN: + fprintf(stderr, "UHID_OPEN from uhid-dev\n"); + break; + case UHID_CLOSE: + fprintf(stderr, "UHID_CLOSE from uhid-dev\n"); + break; + case UHID_OUTPUT: + fprintf(stderr, "UHID_OUTPUT from uhid-dev\n"); + break; + case UHID_GET_REPORT: + fprintf(stderr, "UHID_GET_REPORT from uhid-dev\n"); + break; + case UHID_SET_REPORT: + fprintf(stderr, "UHID_SET_REPORT from uhid-dev\n"); + break; + default: + fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type); + } + + return 0; +} + +static void *read_uhid_events_thread(void *arg) +{ + int fd = *(int *)arg; + struct pollfd pfds[1]; + int ret = 0; + + pfds[0].fd = fd; + pfds[0].events = POLLIN; + + uhid_stopped = false; + + while (!uhid_stopped) { + ret = poll(pfds, 1, 100); + if (ret < 0) { + fprintf(stderr, "Cannot poll for fds: %m\n"); + break; + } + if (pfds[0].revents & POLLIN) { + ret = event(fd); + if (ret) + break; + } + } + + return (void *)(long)ret; +} + +static int uhid_start_listener(pthread_t *tid, int uhid_fd) +{ + int fd = uhid_fd; + + pthread_mutex_lock(&uhid_started_mtx); + if (CHECK_FAIL(pthread_create(tid, NULL, read_uhid_events_thread, + (void *)&fd))) { + pthread_mutex_unlock(&uhid_started_mtx); + close(fd); + return -EIO; + } + pthread_cond_wait(&uhid_started, &uhid_started_mtx); + pthread_mutex_unlock(&uhid_started_mtx); + + return 0; +} + +static int send_event(int fd, u8 *buf, size_t size) +{ + struct uhid_event ev; + + if (size > sizeof(ev.u.input.data)) + return -E2BIG; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_INPUT2; + ev.u.input2.size = size; + + memcpy(ev.u.input2.data, buf, size); + + return uhid_write(fd, &ev); +} + +static int setup_uhid(int rand_nb) +{ + int fd; + const char *path = "/dev/uhid"; + int ret; + + fd = open(path, O_RDWR | O_CLOEXEC); + if (!ASSERT_GE(fd, 0, "open uhid-cdev")) + return -EPERM; + + ret = create(fd, rand_nb); + if (!ASSERT_OK(ret, "create uhid device")) { + close(fd); + return -EPERM; + } + + return fd; +} + +static int get_sysfs_fd(int rand_nb) +{ + const char *workdir = "/sys/devices/virtual/misc/uhid"; + const char *target = "0003:0001:0A37.*"; + char uevent[1024]; + char temp[512]; + char phys[512]; + DIR *d; + struct dirent *dir; + int fd, nread; + int found = -1; + + /* it would be nice to be able to use nftw, but the no_alu32 target doesn't support it */ + + sprintf(phys, "PHYS=%d", rand_nb); + + d = opendir(workdir); + if (d) { + while ((dir = readdir(d)) != NULL) { + if (fnmatch(target, dir->d_name, 0)) + continue; + + /* we found the correct VID/PID, now check for phys */ + sprintf(uevent, "%s/%s/uevent", workdir, dir->d_name); + fd = open(uevent, O_RDONLY | O_NONBLOCK); + if (fd < 0) + continue; + + nread = read(fd, temp, ARRAY_SIZE(temp)); + if (nread > 0 && (strstr(temp, phys)) != NULL) { + found = fd; + break; + } + + close(fd); + fd = 0; + } + closedir(d); + } + + return found; +} + +static int get_hidraw(struct bpf_link *link) +{ + struct bpf_link_info info = {0}; + int prog_id, i; + + /* retry 5 times in case the system is loaded */ + for (i = 5; i > 0; i--) { + usleep(10); + prog_id = link_info_prog_id(link, &info); + if (!prog_id) + continue; + if (info.hid.hidraw_number >= 0) + break; + } + + if (!prog_id) + return -1; + + return info.hid.hidraw_number; +} + +/* + * Attach hid_first_event to the given uhid device, + * retrieve and open the matching hidraw node, + * inject one event in the uhid device, + * check that the program sees it and can change the data + */ +static int test_hid_raw_event(struct hid *hid_skel, int uhid_fd, int sysfs_fd) +{ + int err, hidraw_number, hidraw_fd = -1; + char hidraw_path[64] = {0}; + u8 buf[10] = {0}; + int ret = -1; + + /* check that the program is correctly loaded */ + ASSERT_EQ(hid_skel->data->callback_check, 52, "callback_check1"); + ASSERT_EQ(hid_skel->data->callback2_check, 52, "callback2_check1"); + + /* attach the first program */ + hid_skel->links.hid_first_event = + bpf_program__attach_hid(hid_skel->progs.hid_first_event, sysfs_fd, 0); + if (!ASSERT_OK_PTR(hid_skel->links.hid_first_event, + "attach_hid(hid_first_event)")) + return PTR_ERR(hid_skel->links.hid_first_event); + + hidraw_number = get_hidraw(hid_skel->links.hid_first_event); + if (!ASSERT_GE(hidraw_number, 0, "get_hidraw")) + goto cleanup; + + /* open hidraw node to check the other side of the pipe */ + sprintf(hidraw_path, "/dev/hidraw%d", hidraw_number); + hidraw_fd = open(hidraw_path, O_RDWR | O_NONBLOCK); + + if (!ASSERT_GE(hidraw_fd, 0, "open_hidraw")) + goto cleanup; + + /* inject one event */ + buf[0] = 1; + buf[1] = 42; + send_event(uhid_fd, buf, 6); + + /* check that hid_first_event() was executed */ + ASSERT_EQ(hid_skel->data->callback_check, 42, "callback_check1"); + + /* read the data from hidraw */ + memset(buf, 0, sizeof(buf)); + err = read(hidraw_fd, buf, sizeof(buf)); + if (!ASSERT_EQ(err, 6, "read_hidraw")) + goto cleanup; + + if (!ASSERT_EQ(buf[2], 47, "hid_first_event")) + goto cleanup; + + /* inject another event */ + buf[0] = 1; + buf[1] = 47; + send_event(uhid_fd, buf, 6); + + /* check that hid_first_event() was executed */ + ASSERT_EQ(hid_skel->data->callback_check, 47, "callback_check1"); + + /* read the data from hidraw */ + memset(buf, 0, sizeof(buf)); + err = read(hidraw_fd, buf, sizeof(buf)); + if (!ASSERT_EQ(err, 6, "read_hidraw")) + goto cleanup; + + if (!ASSERT_EQ(buf[2], 52, "hid_first_event")) + goto cleanup; + + ret = 0; + +cleanup: + if (hidraw_fd >= 0) + close(hidraw_fd); + + hid__detach(hid_skel); + + return ret; +} + +void serial_test_hid_bpf(void) +{ + struct hid *hid_skel = NULL; + int err, uhid_fd, sysfs_fd; + void *uhid_err; + time_t t; + pthread_t tid; + int rand_nb; + + /* initialize random number generator */ + srand((unsigned int)time(&t)); + + rand_nb = rand() % 1024; + + uhid_fd = setup_uhid(rand_nb); + if (!ASSERT_GE(uhid_fd, 0, "setup uhid")) + return; + + err = uhid_start_listener(&tid, uhid_fd); + ASSERT_OK(err, "uhid_start_listener"); + + /* locate the uevent file of the created device */ + sysfs_fd = get_sysfs_fd(rand_nb); + if (!ASSERT_GE(sysfs_fd, 0, "locate sysfs uhid device")) + goto cleanup; + + hid_skel = hid__open_and_load(); + if (!ASSERT_OK_PTR(hid_skel, "hid_skel_load")) + goto cleanup; + + /* start the tests! */ + err = test_hid_raw_event(hid_skel, uhid_fd, sysfs_fd); + ASSERT_OK(err, "hid"); + +cleanup: + hid__destroy(hid_skel); + destroy(uhid_fd); + + pthread_join(tid, &uhid_err); + err = (int)(long)uhid_err; + CHECK_FAIL(err); +} diff --git a/tools/testing/selftests/bpf/progs/hid.c b/tools/testing/selftests/bpf/progs/hid.c new file mode 100644 index 000000000000..a28ba19ed933 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/hid.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Red hat */ +#include +#include +#include + +char _license[] SEC("license") = "GPL"; + +__u64 callback_check = 52; +__u64 callback2_check = 52; + +SEC("hid/device_event") +int hid_first_event(struct hid_bpf_ctx *ctx) +{ + __u8 *data = bpf_hid_get_data(ctx, 0 /* offset */, 3 /* size */); + + if (!data) + return 0; /* EPERM check */ + + callback_check = data[1]; + + data[2] = data[1] + 5; + + return 0; +} From patchwork Fri Mar 18 16:15:19 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12785613 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6E837C388F3 for ; Fri, 18 Mar 2022 16:18:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238792AbiCRQTL (ORCPT ); Fri, 18 Mar 2022 12:19:11 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58604 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238971AbiCRQSa (ORCPT ); Fri, 18 Mar 2022 12:18:30 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id E7910139AF4 for ; Fri, 18 Mar 2022 09:17:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1647620230; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=c5khqAiSgzw+4rl9hIjGUC41pcK4+9Fs4UfJK9bCWNY=; b=ML4uE3j23POpMD23I+lPmJIXhvKFSFMFGXBSwxjzO14lP+7gEjTrgo3LydruFB2oH+KX7i 2lU6MVX9WLDNHWGEx2o82ElHMpLfrvBmCFf2z3mM6t8Pjdyjv0sRFLOPC7zIwkwIYblORN 926iF5NN4O659LRt9MWzfjj50xodNx0= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-470-QqnS6V7TPu662kedwZOGAw-1; Fri, 18 Mar 2022 12:17:06 -0400 X-MC-Unique: QqnS6V7TPu662kedwZOGAw-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 70B29296A621; Fri, 18 Mar 2022 16:17:05 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.159]) by smtp.corp.redhat.com (Postfix) with ESMTP id 6A74C7AD1; Fri, 18 Mar 2022 16:16:43 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v3 08/17] selftests/bpf: add report descriptor fixup tests Date: Fri, 18 Mar 2022 17:15:19 +0100 Message-Id: <20220318161528.1531164-9-benjamin.tissoires@redhat.com> In-Reply-To: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> References: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Simple report descriptor override in HID: replace part of the report descriptor from a static definition in the bpf kernel program. Note that this test should be run last because we disconnect/reconnect the device, meaning that it changes the overall uhid device. Signed-off-by: Benjamin Tissoires --- changes in v3: - added a comment to mention that this test needs to be run last changes in v2: - split the series by bpf/libbpf/hid/selftests and samples --- tools/testing/selftests/bpf/prog_tests/hid.c | 79 ++++++++++++++++++++ tools/testing/selftests/bpf/progs/hid.c | 54 +++++++++++++ 2 files changed, 133 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c index 9f9dd08d3232..8c8e17e7385f 100644 --- a/tools/testing/selftests/bpf/prog_tests/hid.c +++ b/tools/testing/selftests/bpf/prog_tests/hid.c @@ -9,6 +9,7 @@ #include #include #include +#include #include static unsigned char rdesc[] = { @@ -400,6 +401,76 @@ static int test_hid_raw_event(struct hid *hid_skel, int uhid_fd, int sysfs_fd) return ret; } +/* + * Attach hid_rdesc_fixup to the given uhid device, + * retrieve and open the matching hidraw node, + * check that the hidraw report descriptor has been updated. + */ +static int test_rdesc_fixup(struct hid *hid_skel, int uhid_fd, int sysfs_fd) +{ + struct hidraw_report_descriptor rpt_desc = {0}; + int err, desc_size, hidraw_ino, hidraw_fd = -1; + char hidraw_path[64] = {0}; + void *uhid_err; + int ret = -1; + pthread_t tid; + + /* attach the program */ + hid_skel->links.hid_rdesc_fixup = + bpf_program__attach_hid(hid_skel->progs.hid_rdesc_fixup, sysfs_fd, 0); + if (!ASSERT_OK_PTR(hid_skel->links.hid_rdesc_fixup, + "attach_hid(hid_rdesc_fixup)")) + return PTR_ERR(hid_skel->links.hid_rdesc_fixup); + + err = uhid_start_listener(&tid, uhid_fd); + ASSERT_OK(err, "uhid_start_listener"); + + hidraw_ino = get_hidraw(hid_skel->links.hid_rdesc_fixup); + if (!ASSERT_GE(hidraw_ino, 0, "get_hidraw")) + goto cleanup; + + /* open hidraw node to check the other side of the pipe */ + sprintf(hidraw_path, "/dev/hidraw%d", hidraw_ino); + hidraw_fd = open(hidraw_path, O_RDWR | O_NONBLOCK); + + if (!ASSERT_GE(hidraw_fd, 0, "open_hidraw")) + goto cleanup; + + /* check that hid_rdesc_fixup() was executed */ + ASSERT_EQ(hid_skel->data->callback2_check, 0x21, "callback_check2"); + + /* read the exposed report descriptor from hidraw */ + err = ioctl(hidraw_fd, HIDIOCGRDESCSIZE, &desc_size); + if (!ASSERT_GE(err, 0, "HIDIOCGRDESCSIZE")) + goto cleanup; + + /* ensure the new size of the rdesc is bigger than the old one */ + if (!ASSERT_GT(desc_size, sizeof(rdesc), "new_rdesc_size")) + goto cleanup; + + rpt_desc.size = desc_size; + err = ioctl(hidraw_fd, HIDIOCGRDESC, &rpt_desc); + if (!ASSERT_GE(err, 0, "HIDIOCGRDESC")) + goto cleanup; + + if (!ASSERT_EQ(rpt_desc.value[4], 0x42, "hid_rdesc_fixup")) + goto cleanup; + + ret = 0; + +cleanup: + if (hidraw_fd >= 0) + close(hidraw_fd); + + hid__detach(hid_skel); + + pthread_join(tid, &uhid_err); + err = (int)(long)uhid_err; + CHECK_FAIL(err); + + return ret; +} + void serial_test_hid_bpf(void) { struct hid *hid_skel = NULL; @@ -434,6 +505,14 @@ void serial_test_hid_bpf(void) err = test_hid_raw_event(hid_skel, uhid_fd, sysfs_fd); ASSERT_OK(err, "hid"); + /* + * this test should be run last because we disconnect/reconnect + * the device, meaning that it changes the overall uhid device + * and messes up with the thread that reads uhid events. + */ + err = test_rdesc_fixup(hid_skel, uhid_fd, sysfs_fd); + ASSERT_OK(err, "hid_rdesc_fixup"); + cleanup: hid__destroy(hid_skel); destroy(uhid_fd); diff --git a/tools/testing/selftests/bpf/progs/hid.c b/tools/testing/selftests/bpf/progs/hid.c index a28ba19ed933..c9ce0e36e7b9 100644 --- a/tools/testing/selftests/bpf/progs/hid.c +++ b/tools/testing/selftests/bpf/progs/hid.c @@ -23,3 +23,57 @@ int hid_first_event(struct hid_bpf_ctx *ctx) return 0; } + +static __u8 rdesc[] = { + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x32, /* USAGE (Z) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x81, 0x06, /* INPUT (Data,Var,Rel) */ + + 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ + 0x19, 0x01, /* USAGE_MINIMUM (1) */ + 0x29, 0x03, /* USAGE_MAXIMUM (3) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x91, 0x02, /* Output (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x05, /* REPORT_SIZE (5) */ + 0x91, 0x01, /* Output (Cnst,Var,Abs) */ + + 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ + 0x19, 0x06, /* USAGE_MINIMUM (6) */ + 0x29, 0x08, /* USAGE_MAXIMUM (8) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0xb1, 0x02, /* Feature (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x05, /* REPORT_SIZE (5) */ + 0x91, 0x01, /* Output (Cnst,Var,Abs) */ + + 0xc0, /* END_COLLECTION */ + 0xc0, /* END_COLLECTION */ +}; + +SEC("hid/rdesc_fixup") +int hid_rdesc_fixup(struct hid_bpf_ctx *ctx) +{ + __u8 *data = bpf_hid_get_data(ctx, 0 /* offset */, 4096 /* size */); + + if (!data) + return 0; /* EPERM check */ + + callback2_check = data[4]; + + /* insert rdesc at offset 52 */ + __builtin_memcpy(&data[52], rdesc, sizeof(rdesc)); + ctx->size = sizeof(rdesc) + 52; + + /* Change Usage Vendor globally */ + data[4] = 0x42; + + return 0; +} From patchwork Fri Mar 18 16:15:20 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12785612 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 04607C433F5 for ; Fri, 18 Mar 2022 16:18:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237486AbiCRQTN (ORCPT ); Fri, 18 Mar 2022 12:19:13 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58964 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238995AbiCRQSi (ORCPT ); Fri, 18 Mar 2022 12:18:38 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 28B801D7891 for ; Fri, 18 Mar 2022 09:17:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1647620236; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=MMsWOein3F9RlSJ9iIFQqy8YzC2KAiacG1VeEjo2p8E=; b=VpWtyquCJhZUZZAx51kDj1SSeu5YQFoJZf4KdBM5H8cWJlFqK8hMb9xGD7Z3jdN7XxBtzk EKOJ+DU9l2WiyyRNXnQvXp7o2FZbbn7qHKB5wO97O2HKOIM6Y5oZgL10+EtbJoedJRZjD2 HX2CM0iw67C8YOlX1Mp2kX/OQgZtQrI= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-591-O2UuoHtVNNKjpEuy8iLloQ-1; Fri, 18 Mar 2022 12:17:10 -0400 X-MC-Unique: O2UuoHtVNNKjpEuy8iLloQ-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id E73563C01B91; Fri, 18 Mar 2022 16:17:09 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.159]) by smtp.corp.redhat.com (Postfix) with ESMTP id ADD397AD1; Fri, 18 Mar 2022 16:17:05 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v3 09/17] selftests/bpf: Add a test for BPF_F_INSERT_HEAD Date: Fri, 18 Mar 2022 17:15:20 +0100 Message-Id: <20220318161528.1531164-10-benjamin.tissoires@redhat.com> In-Reply-To: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> References: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Insert 3 programs to check that we are doing the correct thing: '2', '1', '3' are inserted, but '1' is supposed to be executed first. Signed-off-by: Benjamin Tissoires --- changes in v3: - use the new hid_get_data API new in v2 --- tools/testing/selftests/bpf/prog_tests/hid.c | 80 ++++++++++++++++++++ tools/testing/selftests/bpf/progs/hid.c | 51 +++++++++++++ 2 files changed, 131 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c index 8c8e17e7385f..b8d4dcf20b05 100644 --- a/tools/testing/selftests/bpf/prog_tests/hid.c +++ b/tools/testing/selftests/bpf/prog_tests/hid.c @@ -401,6 +401,83 @@ static int test_hid_raw_event(struct hid *hid_skel, int uhid_fd, int sysfs_fd) return ret; } +/* + * Attach hid_insert{0,1,2} to the given uhid device, + * retrieve and open the matching hidraw node, + * inject one event in the uhid device, + * check that the programs have been inserted in the correct order. + */ +static int test_hid_attach_flags(struct hid *hid_skel, int uhid_fd, int sysfs_fd) +{ + int err, hidraw_ino, hidraw_fd = -1; + char hidraw_path[64] = {0}; + u8 buf[10] = {0}; + int ret = -1; + + /* attach hid_test_insert2 program */ + hid_skel->links.hid_test_insert2 = + bpf_program__attach_hid(hid_skel->progs.hid_test_insert2, sysfs_fd, 0); + if (!ASSERT_OK_PTR(hid_skel->links.hid_test_insert2, + "attach_hid(hid_test_insert2)")) + return PTR_ERR(hid_skel->links.hid_test_insert2); + + /* then attach hid_test_insert1 program before the previous*/ + hid_skel->links.hid_test_insert1 = + bpf_program__attach_hid(hid_skel->progs.hid_test_insert1, + sysfs_fd, + BPF_F_INSERT_HEAD); + if (!ASSERT_OK_PTR(hid_skel->links.hid_test_insert1, + "attach_hid(hid_test_insert1)")) + return PTR_ERR(hid_skel->links.hid_test_insert1); + + /* finally attach hid_test_insert3 at the end */ + hid_skel->links.hid_test_insert3 = + bpf_program__attach_hid(hid_skel->progs.hid_test_insert3, sysfs_fd, 0); + if (!ASSERT_OK_PTR(hid_skel->links.hid_test_insert3, + "attach_hid(hid_test_insert3)")) + return PTR_ERR(hid_skel->links.hid_test_insert3); + + hidraw_ino = get_hidraw(hid_skel->links.hid_test_insert1); + if (!ASSERT_GE(hidraw_ino, 0, "get_hidraw")) + goto cleanup; + + /* open hidraw node to check the other side of the pipe */ + sprintf(hidraw_path, "/dev/hidraw%d", hidraw_ino); + hidraw_fd = open(hidraw_path, O_RDWR | O_NONBLOCK); + + if (!ASSERT_GE(hidraw_fd, 0, "open_hidraw")) + goto cleanup; + + /* inject one event */ + buf[0] = 1; + send_event(uhid_fd, buf, 6); + + /* read the data from hidraw */ + memset(buf, 0, sizeof(buf)); + err = read(hidraw_fd, buf, sizeof(buf)); + if (!ASSERT_EQ(err, 6, "read_hidraw")) + goto cleanup; + + if (!ASSERT_EQ(buf[1], 1, "hid_test_insert1")) + goto cleanup; + + if (!ASSERT_EQ(buf[2], 2, "hid_test_insert2")) + goto cleanup; + + if (!ASSERT_EQ(buf[3], 3, "hid_test_insert3")) + goto cleanup; + + ret = 0; + +cleanup: + if (hidraw_fd >= 0) + close(hidraw_fd); + + hid__detach(hid_skel); + + return ret; +} + /* * Attach hid_rdesc_fixup to the given uhid device, * retrieve and open the matching hidraw node, @@ -505,6 +582,9 @@ void serial_test_hid_bpf(void) err = test_hid_raw_event(hid_skel, uhid_fd, sysfs_fd); ASSERT_OK(err, "hid"); + err = test_hid_attach_flags(hid_skel, uhid_fd, sysfs_fd); + ASSERT_OK(err, "hid_user_raw_request"); + /* * this test should be run last because we disconnect/reconnect * the device, meaning that it changes the overall uhid device diff --git a/tools/testing/selftests/bpf/progs/hid.c b/tools/testing/selftests/bpf/progs/hid.c index c9ce0e36e7b9..390c1bb8d850 100644 --- a/tools/testing/selftests/bpf/progs/hid.c +++ b/tools/testing/selftests/bpf/progs/hid.c @@ -77,3 +77,54 @@ int hid_rdesc_fixup(struct hid_bpf_ctx *ctx) return 0; } + +SEC("hid/device_event") +int hid_test_insert1(struct hid_bpf_ctx *ctx) +{ + __u8 *data = bpf_hid_get_data(ctx, 0 /* offset */, 4 /* size */); + + if (!data) + return 0; /* EPERM check */ + + /* we need to be run first */ + if (data[2] || data[3]) + return -1; + + data[1] = 1; + + return 0; +} + +SEC("hid/device_event") +int hid_test_insert2(struct hid_bpf_ctx *ctx) +{ + __u8 *data = bpf_hid_get_data(ctx, 0 /* offset */, 4 /* size */); + + if (!data) + return 0; /* EPERM check */ + + /* after insert0 and before insert2 */ + if (!data[1] || data[3]) + return -1; + + data[2] = 2; + + return 0; +} + +SEC("hid/device_event") +int hid_test_insert3(struct hid_bpf_ctx *ctx) +{ + __u8 *data = bpf_hid_get_data(ctx, 0 /* offset */, 4 /* size */); + + if (!data) + return 0; /* EPERM check */ + + /* at the end */ + if (!data[1] || !data[2]) + return -1; + + data[3] = 3; + + return 0; +} From patchwork Fri Mar 18 16:15:21 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12785609 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AC128C3527C for ; Fri, 18 Mar 2022 16:17:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238851AbiCRQTO (ORCPT ); Fri, 18 Mar 2022 12:19:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59790 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239046AbiCRQSu (ORCPT ); Fri, 18 Mar 2022 12:18:50 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id ECA55139AF4 for ; Fri, 18 Mar 2022 09:17:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1647620251; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=/OVyC0zaH1G/so0ZWrJlP+iwVNUqwQaVb/Gii8MLOPM=; b=TQvnDPY43a8b5idmlsHU9ZiVUQyRcqDIc8kGTlMzwyJ6nUbU3+O1+pd3iWlt9G4f4YDaT3 sPJsbqP2DYxGWHdtAObEjn84S6+TrC/qqsNsoUglAEbaTAgoxRJKWak4jxWErJ/BAwUVhu lo9rwPjUYFGrx13oY+Fv7SHet6dkw3g= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-528-GokVlFHFMx6rNO_pb1c6EA-1; Fri, 18 Mar 2022 12:17:27 -0400 X-MC-Unique: GokVlFHFMx6rNO_pb1c6EA-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id AF68B83396E; Fri, 18 Mar 2022 16:17:26 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.159]) by smtp.corp.redhat.com (Postfix) with ESMTP id 30DF433250; Fri, 18 Mar 2022 16:17:10 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v3 10/17] selftests/bpf: add test for user call of HID bpf programs Date: Fri, 18 Mar 2022 17:15:21 +0100 Message-Id: <20220318161528.1531164-11-benjamin.tissoires@redhat.com> In-Reply-To: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> References: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Add a simple test to see if we can trigger a bpf program of type "hid/user_event". Signed-off-by: Benjamin Tissoires --- changes in v3: - use the new hid_get_data API changes in v2: - split the series by bpf/libbpf/hid/selftests and samples --- tools/testing/selftests/bpf/prog_tests/hid.c | 56 ++++++++++++++++++++ tools/testing/selftests/bpf/progs/hid.c | 15 ++++++ 2 files changed, 71 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c index b8d4dcf20b05..edc3af71e9ed 100644 --- a/tools/testing/selftests/bpf/prog_tests/hid.c +++ b/tools/testing/selftests/bpf/prog_tests/hid.c @@ -478,6 +478,59 @@ static int test_hid_attach_flags(struct hid *hid_skel, int uhid_fd, int sysfs_fd return ret; } +/* + * Attach hid_user to the given uhid device, + * call the bpf program from userspace + * check that the program is called and does the expected. + */ +static int test_hid_user_call(struct hid *hid_skel, int uhid_fd, int sysfs_fd) +{ + int err, prog_fd; + u8 buf[10] = {0}; + int ret = -1; + + LIBBPF_OPTS(bpf_test_run_opts, run_attrs, + .repeat = 1, + .ctx_in = &sysfs_fd, + .ctx_size_in = sizeof(sysfs_fd), + .data_in = buf, + .data_size_in = sizeof(buf), + .data_out = buf, + .data_size_out = sizeof(buf), + ); + + /* attach hid_user program */ + hid_skel->links.hid_user = bpf_program__attach_hid(hid_skel->progs.hid_user, sysfs_fd, 0); + if (!ASSERT_OK_PTR(hid_skel->links.hid_user, + "attach_hid(hid_user)")) + return PTR_ERR(hid_skel->links.hid_user); + + buf[0] = 39; + + prog_fd = bpf_program__fd(hid_skel->progs.hid_user); + + err = bpf_prog_test_run_opts(prog_fd, &run_attrs); + if (!ASSERT_EQ(err, 0, "bpf_prog_test_run_xattr")) + goto cleanup; + + if (!ASSERT_EQ(run_attrs.retval, 72, "bpf_prog_test_run_opts")) + goto cleanup; + + if (!ASSERT_EQ(buf[1], 42, "hid_user_check_in")) + goto cleanup; + + if (!ASSERT_EQ(buf[2], 4, "hid_user_check_static_out")) + goto cleanup; + + ret = 0; + +cleanup: + + hid__detach(hid_skel); + + return ret; +} + /* * Attach hid_rdesc_fixup to the given uhid device, * retrieve and open the matching hidraw node, @@ -585,6 +638,9 @@ void serial_test_hid_bpf(void) err = test_hid_attach_flags(hid_skel, uhid_fd, sysfs_fd); ASSERT_OK(err, "hid_user_raw_request"); + err = test_hid_user_call(hid_skel, uhid_fd, sysfs_fd); + ASSERT_OK(err, "hid_user"); + /* * this test should be run last because we disconnect/reconnect * the device, meaning that it changes the overall uhid device diff --git a/tools/testing/selftests/bpf/progs/hid.c b/tools/testing/selftests/bpf/progs/hid.c index 390c1bb8d850..fbdbe9d1b605 100644 --- a/tools/testing/selftests/bpf/progs/hid.c +++ b/tools/testing/selftests/bpf/progs/hid.c @@ -128,3 +128,18 @@ int hid_test_insert3(struct hid_bpf_ctx *ctx) return 0; } + +SEC("hid/user_event") +int hid_user(struct hid_bpf_ctx *ctx) +{ + __u8 *data = bpf_hid_get_data(ctx, 0 /* offset */, 3 /* size */); + + if (!data) + return 0; /* EPERM check */ + + data[1] = data[0] + 3; + data[2] = 4; + ctx->retval = 72; + + return 0; +} From patchwork Fri Mar 18 16:15:22 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12785611 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C77AEC35296 for ; Fri, 18 Mar 2022 16:18:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238931AbiCRQTP (ORCPT ); Fri, 18 Mar 2022 12:19:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60232 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239089AbiCRQS4 (ORCPT ); Fri, 18 Mar 2022 12:18:56 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 76E791D760C for ; Fri, 18 Mar 2022 09:17:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1647620256; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=QH08yjZ3KjSzH4uLolphibGhSrMWYB+98W2rwbxZTxY=; b=JSFek9BsN2pNbPT9DvlhiFi1NbSkltjHDpPPXhjVeZmU5JTZ9SAcifw/FMnIiXQm0e62Nk 8n3CwOYvG9cB3crepOQzMHroUTh/q24gRDkeZ2gOnlMRf5oZaKE1lUwwb9ZJa3KRmBJj+m GIlUOivoHeQf14IrICyANgeNhPmOhGk= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-634-o5uZ8XKIOyGXIBy9Leg4Ow-1; Fri, 18 Mar 2022 12:17:31 -0400 X-MC-Unique: o5uZ8XKIOyGXIBy9Leg4Ow-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 1AA613C01B94; Fri, 18 Mar 2022 16:17:30 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.159]) by smtp.corp.redhat.com (Postfix) with ESMTP id ED84A7AD1; Fri, 18 Mar 2022 16:17:26 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v3 11/17] samples/bpf: add new hid_mouse example Date: Fri, 18 Mar 2022 17:15:22 +0100 Message-Id: <20220318161528.1531164-12-benjamin.tissoires@redhat.com> In-Reply-To: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> References: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Everything should be available in the selftest part of the tree, but providing an example without uhid and hidraw will be more easy to follow for users. Signed-off-by: Benjamin Tissoires --- changes in v3: - use the new hid_get_data API - add a comment for the report descriptor fixup to explain what is done changes in v2: - split the series by bpf/libbpf/hid/selftests and samples --- samples/bpf/.gitignore | 1 + samples/bpf/Makefile | 4 ++ samples/bpf/hid_mouse_kern.c | 117 +++++++++++++++++++++++++++++++ samples/bpf/hid_mouse_user.c | 129 +++++++++++++++++++++++++++++++++++ 4 files changed, 251 insertions(+) create mode 100644 samples/bpf/hid_mouse_kern.c create mode 100644 samples/bpf/hid_mouse_user.c diff --git a/samples/bpf/.gitignore b/samples/bpf/.gitignore index 0e7bfdbff80a..65440bd618b2 100644 --- a/samples/bpf/.gitignore +++ b/samples/bpf/.gitignore @@ -2,6 +2,7 @@ cpustat fds_example hbm +hid_mouse ibumad lathist lwt_len_hist diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 38638845db9d..84ef458487df 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -60,6 +60,8 @@ tprogs-y += xdp_redirect_map tprogs-y += xdp_redirect tprogs-y += xdp_monitor +tprogs-y += hid_mouse + # Libbpf dependencies LIBBPF_SRC = $(TOOLS_PATH)/lib/bpf LIBBPF_OUTPUT = $(abspath $(BPF_SAMPLES_PATH))/libbpf @@ -124,6 +126,7 @@ xdp_redirect_cpu-objs := xdp_redirect_cpu_user.o $(XDP_SAMPLE) xdp_redirect_map-objs := xdp_redirect_map_user.o $(XDP_SAMPLE) xdp_redirect-objs := xdp_redirect_user.o $(XDP_SAMPLE) xdp_monitor-objs := xdp_monitor_user.o $(XDP_SAMPLE) +hid_mouse-objs := hid_mouse_user.o # Tell kbuild to always build the programs always-y := $(tprogs-y) @@ -181,6 +184,7 @@ always-y += ibumad_kern.o always-y += hbm_out_kern.o always-y += hbm_edt_kern.o always-y += xdpsock_kern.o +always-y += hid_mouse_kern.o ifeq ($(ARCH), arm) # Strip all except -D__LINUX_ARM_ARCH__ option needed to handle linux diff --git a/samples/bpf/hid_mouse_kern.c b/samples/bpf/hid_mouse_kern.c new file mode 100644 index 000000000000..32285b1920c0 --- /dev/null +++ b/samples/bpf/hid_mouse_kern.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2022 Benjamin Tissoires + */ +#include +#include +#include +#include + +SEC("hid/device_event") +int hid_y_event(struct hid_bpf_ctx *ctx) +{ + s16 y; + __u8 *data = bpf_hid_get_data(ctx, 0 /* offset */, 9 /* size */); + + if (!data) + return 0; /* EPERM check */ + + bpf_printk("event: %02x size: %d", ctx->type, ctx->size); + bpf_printk("incoming event: %02x %02x %02x", + data[0], + data[1], + data[2]); + bpf_printk(" %02x %02x %02x", + data[3], + data[4], + data[5]); + bpf_printk(" %02x %02x %02x", + data[6], + data[7], + data[8]); + + y = data[3] | (data[4] << 8); + + y = -y; + + data[3] = y & 0xFF; + data[4] = (y >> 8) & 0xFF; + + bpf_printk("modified event: %02x %02x %02x", + data[0], + data[1], + data[2]); + bpf_printk(" %02x %02x %02x", + data[3], + data[4], + data[5]); + bpf_printk(" %02x %02x %02x", + data[6], + data[7], + data[8]); + + return 0; +} + +SEC("hid/device_event") +int hid_x_event(struct hid_bpf_ctx *ctx) +{ + s16 x; + __u8 *data = bpf_hid_get_data(ctx, 0 /* offset */, 9 /* size */); + + if (!data) + return 0; /* EPERM check */ + + x = data[1] | (data[2] << 8); + + x = -x; + + data[1] = x & 0xFF; + data[2] = (x >> 8) & 0xFF; + return 0; +} + +SEC("hid/rdesc_fixup") +int hid_rdesc_fixup(struct hid_bpf_ctx *ctx) +{ + __u8 *data = bpf_hid_get_data(ctx, 0 /* offset */, 4096 /* size */); + + if (!data) + return 0; /* EPERM check */ + + if (ctx->type != HID_BPF_RDESC_FIXUP) + return 0; + + bpf_printk("rdesc: %02x %02x %02x", + data[0], + data[1], + data[2]); + bpf_printk(" %02x %02x %02x", + data[3], + data[4], + data[5]); + bpf_printk(" %02x %02x %02x ...", + data[6], + data[7], + data[8]); + + /* + * The original report descriptor contains: + * + * 0x05, 0x01, // Usage Page (Generic Desktop) 30 + * 0x16, 0x01, 0x80, // Logical Minimum (-32767) 32 + * 0x26, 0xff, 0x7f, // Logical Maximum (32767) 35 + * 0x09, 0x30, // Usage (X) 38 + * 0x09, 0x31, // Usage (Y) 40 + * + * So byte 39 contains Usage X and byte 41 Usage Y. + * + * We simply swap the axes here. + */ + data[39] = 0x31; + data[41] = 0x30; + + return 0; +} + +char _license[] SEC("license") = "GPL"; +u32 _version SEC("version") = LINUX_VERSION_CODE; diff --git a/samples/bpf/hid_mouse_user.c b/samples/bpf/hid_mouse_user.c new file mode 100644 index 000000000000..a8a9e7a99b14 --- /dev/null +++ b/samples/bpf/hid_mouse_user.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2022 Benjamin Tissoires + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bpf_util.h" +#include +#include + +static char *sysfs_path; +static int sysfs_fd; +static int prog_count; + +struct prog { + int fd; + struct bpf_link *link; + enum bpf_attach_type type; +}; + +static struct prog progs[10]; + +static void int_exit(int sig) +{ + for (prog_count--; prog_count >= 0; prog_count--) + bpf_link__destroy(progs[prog_count].link); + + close(sysfs_fd); + exit(0); +} + +static void usage(const char *prog) +{ + fprintf(stderr, + "%s: %s /sys/bus/hid/devices/0BUS:0VID:0PID:00ID/uevent\n\n", + __func__, prog); +} + +int main(int argc, char **argv) +{ + struct bpf_prog_info info = {}; + __u32 info_len = sizeof(info); + const char *optstr = ""; + struct bpf_object *obj; + struct bpf_program *prog; + int opt; + char filename[256]; + int err; + + while ((opt = getopt(argc, argv, optstr)) != -1) { + switch (opt) { + default: + usage(basename(argv[0])); + return 1; + } + } + + if (optind == argc) { + usage(basename(argv[0])); + return 1; + } + + sysfs_path = argv[optind]; + if (!sysfs_path) { + perror("sysfs"); + return 1; + } + + snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); + obj = bpf_object__open_file(filename, NULL); + err = libbpf_get_error(obj); + if (err) { + fprintf(stderr, "ERROR: opening BPF object file failed\n"); + obj = NULL; + err = 1; + goto cleanup; + } + + /* load BPF program */ + err = bpf_object__load(obj); + if (err) { + fprintf(stderr, "ERROR: loading BPF object file failed\n"); + goto cleanup; + } + + sysfs_fd = open(sysfs_path, O_RDONLY); + + bpf_object__for_each_program(prog, obj) { + progs[prog_count].fd = bpf_program__fd(prog); + progs[prog_count].type = bpf_program__get_expected_attach_type(prog); + progs[prog_count].link = bpf_program__attach_hid(prog, sysfs_fd, 0); + if (libbpf_get_error(progs[prog_count].link)) { + fprintf(stderr, "bpf_prog_attach: err=%m\n"); + progs[prog_count].fd = 0; + progs[prog_count].link = NULL; + goto cleanup; + } + prog_count++; + } + + signal(SIGINT, int_exit); + signal(SIGTERM, int_exit); + + err = bpf_obj_get_info_by_fd(progs[0].fd, &info, &info_len); + if (err) { + printf("can't get prog info - %s\n", strerror(errno)); + goto cleanup; + } + + while (1) + ; + + cleanup: + for (prog_count--; prog_count >= 0; prog_count--) + bpf_link__destroy(progs[prog_count].link); + + bpf_object__close(obj); + return err; +} From patchwork Fri Mar 18 16:15:23 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12785610 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 12F4EC47080 for ; Fri, 18 Mar 2022 16:18:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238986AbiCRQTQ (ORCPT ); Fri, 18 Mar 2022 12:19:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33272 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238848AbiCRQTN (ORCPT ); Fri, 18 Mar 2022 12:19:13 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 5251716F044 for ; Fri, 18 Mar 2022 09:17:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1647620273; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=vwfuaDa4j47STZzMRDPLEShn/FOuirkz9TKMgr+3Hk8=; b=iXU1JgzkSHa+XRLMVYYBDGlm0tS6uCOZ4WqQdHj0kgrnneTp0y1Deqv0nMYxJwH5gFmOfE i8MIEW0O0N/gBQPqymcZJ4xVh8+DIAHBHZc8EW/aT4zpRrnb7sTXnC3dqJLCKlrkBVsU2k wpEOz3eTslblCK2w+IV4w9uJq5aGFxc= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-614-5tYOD3ijOPeJ_u9ROme-vQ-1; Fri, 18 Mar 2022 12:17:52 -0400 X-MC-Unique: 5tYOD3ijOPeJ_u9ROme-vQ-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 758C2811E80; Fri, 18 Mar 2022 16:17:50 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.159]) by smtp.corp.redhat.com (Postfix) with ESMTP id 5883E7AD1; Fri, 18 Mar 2022 16:17:30 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v3 12/17] bpf/hid: add more HID helpers Date: Fri, 18 Mar 2022 17:15:23 +0100 Message-Id: <20220318161528.1531164-13-benjamin.tissoires@redhat.com> In-Reply-To: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> References: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net When we process an incoming HID report, it is common to have to account for fields that are not aligned in the report. HID is using 2 helpers hid_field_extract() and implement() to pick up any data at any offset within the report. Export those 2 helpers in BPF programs so users can also rely on them. The second net worth advantage of those helpers is that now we can fetch data anywhere in the report without knowing at compile time the location of it. The boundary checks are done in hid-bpf.c, to prevent a memory leak. The third exported helper allows to communicate with the HID device. We give a data buffer, and call either HID_GET_REPORT or HID_SET_REPORT on the device. Signed-off-by: Benjamin Tissoires --- changes in v3: - renamed hid_{get|set}_data into hid_{get|set}_bits - squashed with bpf/hid: add bpf_hid_raw_request helper function changes in v2: - split the patch with libbpf and HID left outside. --- include/linux/bpf-hid.h | 6 +++ include/uapi/linux/bpf.h | 36 +++++++++++++++++ kernel/bpf/hid.c | 73 ++++++++++++++++++++++++++++++++++ tools/include/uapi/linux/bpf.h | 36 +++++++++++++++++ 4 files changed, 151 insertions(+) diff --git a/include/linux/bpf-hid.h b/include/linux/bpf-hid.h index 7f596554fe8c..82b7466b5008 100644 --- a/include/linux/bpf-hid.h +++ b/include/linux/bpf-hid.h @@ -102,6 +102,12 @@ struct bpf_hid_hooks { int (*pre_link_attach)(struct hid_device *hdev, enum bpf_hid_attach_type type); void (*post_link_attach)(struct hid_device *hdev, enum bpf_hid_attach_type type); void (*array_detach)(struct hid_device *hdev, enum bpf_hid_attach_type type); + int (*hid_get_bits)(struct hid_device *hdev, u8 *buf, size_t buf_size, + u64 offset, u32 n, u32 *data); + int (*hid_set_bits)(struct hid_device *hdev, u8 *buf, size_t buf_size, + u64 offset, u32 n, u32 data); + int (*hid_raw_request)(struct hid_device *hdev, u8 *buf, size_t size, + u8 rtype, u8 reqtype); }; #ifdef CONFIG_BPF diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 0e8438e93768..41ab1d068369 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -5155,6 +5155,39 @@ union bpf_attr { * by a call to bpf_hid_discard; * Return * The pointer to the data. On error, a null value is returned. + * + * int bpf_hid_get_bits(void *ctx, u64 offset, u32 n, u32 *data) + * Description + * Get the data of size n (in bits) at the given offset (bits) in the + * ctx->event.data field and store it into data. + * + * n must be less or equal than 32, and we can address with bit + * precision the value in the buffer. data must be a pointer + * to a u32. + * Return + * The length of data copied into data. On error, a negative value + * is returned. + * + * int bpf_hid_set_bits(void *ctx, u64 offset, u32 n, u32 data) + * Description + * Set the data of size n (in bits) at the given offset (bits) in the + * ctx->event.data field. + * + * n must be less or equal than 32, and we can address with bit + * precision the value in the buffer. + * Return + * The length of data copied into ctx->event.data. On error, a negative + * value is returned. + * + * int bpf_hid_raw_request(void *ctx, void *buf, u64 size, u8 report_type, u8 request_type) + * Description + * communicate with the HID device + * + * report_type is one of HID_INPUT_REPORT, HID_OUTPUT_REPORT, HID_FEATURE_REPORT + * request_type is one of HID_REQ_SET_REPORT or HID_REQ_GET_REPORT + * Return + * 0 on success. + * negative value on error. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -5352,6 +5385,9 @@ union bpf_attr { FN(skb_set_tstamp), \ FN(ima_file_hash), \ FN(hid_get_data), \ + FN(hid_get_bits), \ + FN(hid_set_bits), \ + FN(hid_raw_request), \ /* */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper diff --git a/kernel/bpf/hid.c b/kernel/bpf/hid.c index 2dfeaaa8a83f..30a62e8e0f0a 100644 --- a/kernel/bpf/hid.c +++ b/kernel/bpf/hid.c @@ -66,12 +66,85 @@ static const struct bpf_func_proto bpf_hid_get_data_proto = { .arg3_type = ARG_CONST_ALLOC_SIZE_OR_ZERO, }; +BPF_CALL_4(bpf_hid_get_bits, struct hid_bpf_ctx_kern*, ctx, u64, offset, u32, n, u32*, data) +{ + if (!hid_hooks.hid_get_bits) + return -EOPNOTSUPP; + + return hid_hooks.hid_get_bits(ctx->hdev, + ctx->data, + ctx->allocated_size, + offset, n, + data); +} + +static const struct bpf_func_proto bpf_hid_get_bits_proto = { + .func = bpf_hid_get_bits, + .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_ANYTHING, + .arg4_type = ARG_PTR_TO_INT, +}; + +BPF_CALL_4(bpf_hid_set_bits, struct hid_bpf_ctx_kern*, ctx, u64, offset, u32, n, u32, data) +{ + if (!hid_hooks.hid_set_bits) + return -EOPNOTSUPP; + + hid_hooks.hid_set_bits(ctx->hdev, + ctx->data, + ctx->allocated_size, + offset, n, + data); + return 0; +} + +static const struct bpf_func_proto bpf_hid_set_bits_proto = { + .func = bpf_hid_set_bits, + .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_ANYTHING, + .arg4_type = ARG_ANYTHING, +}; + +BPF_CALL_5(bpf_hid_raw_request, struct hid_bpf_ctx_kern*, ctx, void*, buf, u64, size, + u8, report_type, u8, request_type) +{ + if (!hid_hooks.hid_raw_request) + return -EOPNOTSUPP; + + return hid_hooks.hid_raw_request(ctx->hdev, buf, size, report_type, request_type); +} + +static const struct bpf_func_proto bpf_hid_raw_request_proto = { + .func = bpf_hid_raw_request, + .gpl_only = true, /* hid_raw_request is EXPORT_SYMBOL_GPL */ + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_PTR_TO_MEM, + .arg3_type = ARG_CONST_SIZE_OR_ZERO, + .arg4_type = ARG_ANYTHING, + .arg5_type = ARG_ANYTHING, +}; + static const struct bpf_func_proto * hid_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { switch (func_id) { case BPF_FUNC_hid_get_data: return &bpf_hid_get_data_proto; + case BPF_FUNC_hid_get_bits: + return &bpf_hid_get_bits_proto; + case BPF_FUNC_hid_set_bits: + return &bpf_hid_set_bits_proto; + case BPF_FUNC_hid_raw_request: + if (prog->expected_attach_type != BPF_HID_DEVICE_EVENT) + return &bpf_hid_raw_request_proto; + return NULL; default: return bpf_base_func_proto(func_id); } diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 0e8438e93768..41ab1d068369 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -5155,6 +5155,39 @@ union bpf_attr { * by a call to bpf_hid_discard; * Return * The pointer to the data. On error, a null value is returned. + * + * int bpf_hid_get_bits(void *ctx, u64 offset, u32 n, u32 *data) + * Description + * Get the data of size n (in bits) at the given offset (bits) in the + * ctx->event.data field and store it into data. + * + * n must be less or equal than 32, and we can address with bit + * precision the value in the buffer. data must be a pointer + * to a u32. + * Return + * The length of data copied into data. On error, a negative value + * is returned. + * + * int bpf_hid_set_bits(void *ctx, u64 offset, u32 n, u32 data) + * Description + * Set the data of size n (in bits) at the given offset (bits) in the + * ctx->event.data field. + * + * n must be less or equal than 32, and we can address with bit + * precision the value in the buffer. + * Return + * The length of data copied into ctx->event.data. On error, a negative + * value is returned. + * + * int bpf_hid_raw_request(void *ctx, void *buf, u64 size, u8 report_type, u8 request_type) + * Description + * communicate with the HID device + * + * report_type is one of HID_INPUT_REPORT, HID_OUTPUT_REPORT, HID_FEATURE_REPORT + * request_type is one of HID_REQ_SET_REPORT or HID_REQ_GET_REPORT + * Return + * 0 on success. + * negative value on error. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -5352,6 +5385,9 @@ union bpf_attr { FN(skb_set_tstamp), \ FN(ima_file_hash), \ FN(hid_get_data), \ + FN(hid_get_bits), \ + FN(hid_set_bits), \ + FN(hid_raw_request), \ /* */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper From patchwork Fri Mar 18 16:15:24 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12785614 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D5A07C433FE for ; Fri, 18 Mar 2022 16:18:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238848AbiCRQTc (ORCPT ); Fri, 18 Mar 2022 12:19:32 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34048 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238936AbiCRQTV (ORCPT ); Fri, 18 Mar 2022 12:19:21 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id CAA6816F044 for ; Fri, 18 Mar 2022 09:17:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1647620278; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=8nJYYlBY7G+EkGr1cCCnY5dDxM56e+iZoTRwhxTl+8k=; b=G+9YJJjLU4ndS6ZzuDom4Lp3zk1XTyed/tjH9gnqjExKdtsONFnUsjGxnlCaD5oQ70WkuG CCE/MLQCrkdX7WfCbDdSHRNTvVSZWSmEmZCi1Rjpr7WMsR9SfnVlEriu11kPmR9wKveLvB 3EFDXrhMSxGa6k3jRL3M7uMxgUJjhd8= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-622-qlLdcQmqOwKSVMP1eIqycg-1; Fri, 18 Mar 2022 12:17:55 -0400 X-MC-Unique: qlLdcQmqOwKSVMP1eIqycg-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id D534D38035BB; Fri, 18 Mar 2022 16:17:53 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.159]) by smtp.corp.redhat.com (Postfix) with ESMTP id B208A7AD1; Fri, 18 Mar 2022 16:17:50 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v3 13/17] HID: bpf: implement hid_bpf_get|set_bits Date: Fri, 18 Mar 2022 17:15:24 +0100 Message-Id: <20220318161528.1531164-14-benjamin.tissoires@redhat.com> In-Reply-To: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> References: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Export implement() outside of hid-core.c and use this and hid_field_extract() to implement the helprs for hid-bpf. Signed-off-by: Benjamin Tissoires --- changes in v3: - renamed hid_{get|set}_data into hid_{get|set}_bits changes in v2: - split the series by bpf/libbpf/hid/selftests and samples - allow for n > 32, by relying on memcpy --- drivers/hid/hid-bpf.c | 29 +++++++++++++++++++++++++++++ drivers/hid/hid-core.c | 4 ++-- include/linux/hid.h | 2 ++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-bpf.c b/drivers/hid/hid-bpf.c index 45c87ff47324..650dd5e54919 100644 --- a/drivers/hid/hid-bpf.c +++ b/drivers/hid/hid-bpf.c @@ -122,6 +122,33 @@ static void hid_bpf_array_detach(struct hid_device *hdev, enum bpf_hid_attach_ty } } +static int hid_bpf_get_bits(struct hid_device *hdev, u8 *buf, size_t buf_size, u64 offset, u32 n, + u32 *data) +{ + if (n > 32) + return -EINVAL; + + if (((offset + n) >> 3) >= buf_size) + return -E2BIG; + + *data = hid_field_extract(hdev, buf, offset, n); + return n; +} + +static int hid_bpf_set_bits(struct hid_device *hdev, u8 *buf, size_t buf_size, u64 offset, u32 n, + u32 data) +{ + if (n > 32) + return -EINVAL; + + if (((offset + n) >> 3) >= buf_size) + return -E2BIG; + + /* data must be a pointer to a u32 */ + implement(hdev, buf, offset, n, data); + return n; +} + static int hid_bpf_run_progs(struct hid_device *hdev, struct hid_bpf_ctx_kern *ctx) { enum bpf_hid_attach_type type; @@ -223,6 +250,8 @@ int __init hid_bpf_module_init(void) .pre_link_attach = hid_bpf_pre_link_attach, .post_link_attach = hid_bpf_post_link_attach, .array_detach = hid_bpf_array_detach, + .hid_get_bits = hid_bpf_get_bits, + .hid_set_bits = hid_bpf_set_bits, }; bpf_hid_set_hooks(&hooks); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 3182c39db006..4f669dcddc08 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1416,8 +1416,8 @@ static void __implement(u8 *report, unsigned offset, int n, u32 value) } } -static void implement(const struct hid_device *hid, u8 *report, - unsigned offset, unsigned n, u32 value) +void implement(const struct hid_device *hid, u8 *report, unsigned int offset, unsigned int n, + u32 value) { if (unlikely(n > 32)) { hid_warn(hid, "%s() called with n (%d) > 32! (%s)\n", diff --git a/include/linux/hid.h b/include/linux/hid.h index 66d949d10b78..7454e844324c 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -944,6 +944,8 @@ bool hid_compare_device_paths(struct hid_device *hdev_a, s32 hid_snto32(__u32 value, unsigned n); __u32 hid_field_extract(const struct hid_device *hid, __u8 *report, unsigned offset, unsigned n); +void implement(const struct hid_device *hid, u8 *report, unsigned int offset, unsigned int n, + u32 value); #ifdef CONFIG_PM int hid_driver_suspend(struct hid_device *hdev, pm_message_t state); From patchwork Fri Mar 18 16:15:25 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12785632 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 475AAC433F5 for ; Fri, 18 Mar 2022 16:19:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238976AbiCRQUl (ORCPT ); Fri, 18 Mar 2022 12:20:41 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:35916 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239181AbiCRQTs (ORCPT ); Fri, 18 Mar 2022 12:19:48 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 5385213BADA for ; Fri, 18 Mar 2022 09:18:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1647620281; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=o+M0hzPfXbSknPCIGxa8xOYfbwK5d3tQ9k5sjS0RFo8=; b=gRCuns2QOhYXhZ272fCckaIDqL+g9WQBylK+FzcN983zWOzqxnYCVyYexeu2td+gF5JyhS SXysKbPcSTWJiOsDXqshOTUrUT88u2xmuyST6rOzToUQoIwptc//SEmnHhBJU8FXzJ7FK6 BCejSCRwLVyrV35IziF/N0UaZYI/GDQ= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-648-8ira-iG9MMGUCiNAErdmrA-1; Fri, 18 Mar 2022 12:17:58 -0400 X-MC-Unique: 8ira-iG9MMGUCiNAErdmrA-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 52C271C0691C; Fri, 18 Mar 2022 16:17:57 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.159]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1EB7D7AD1; Fri, 18 Mar 2022 16:17:53 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v3 14/17] HID: add implementation of bpf_hid_raw_request Date: Fri, 18 Mar 2022 17:15:25 +0100 Message-Id: <20220318161528.1531164-15-benjamin.tissoires@redhat.com> In-Reply-To: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> References: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Hook up BPF to hid_hw_raw_request. Not much to report here except that we need to export hid_get_report from hid-core so it gets available in hid-bpf.c Signed-off-by: Benjamin Tissoires --- changes in v3: - export HID_*_REPORT definitions in uapi as they are not following the specs changes in v2: - split the series by bpf/libbpf/hid/selftests and samples --- drivers/hid/hid-bpf.c | 63 ++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-core.c | 3 +- include/linux/hid.h | 12 ++------ include/uapi/linux/hid.h | 10 +++++++ 4 files changed, 76 insertions(+), 12 deletions(-) diff --git a/drivers/hid/hid-bpf.c b/drivers/hid/hid-bpf.c index 650dd5e54919..66724aa2ff42 100644 --- a/drivers/hid/hid-bpf.c +++ b/drivers/hid/hid-bpf.c @@ -149,6 +149,68 @@ static int hid_bpf_set_bits(struct hid_device *hdev, u8 *buf, size_t buf_size, u return n; } +static int hid_bpf_raw_request(struct hid_device *hdev, u8 *buf, size_t size, + u8 rtype, u8 reqtype) +{ + struct hid_report *report; + struct hid_report_enum *report_enum; + u8 *dma_data; + u32 report_len; + int ret; + + /* check arguments */ + switch (rtype) { + case HID_INPUT_REPORT: + case HID_OUTPUT_REPORT: + case HID_FEATURE_REPORT: + break; + default: + return -EINVAL; + } + + switch (reqtype) { + case HID_REQ_GET_REPORT: + case HID_REQ_GET_IDLE: + case HID_REQ_GET_PROTOCOL: + case HID_REQ_SET_REPORT: + case HID_REQ_SET_IDLE: + case HID_REQ_SET_PROTOCOL: + break; + default: + return -EINVAL; + } + + if (size < 1) + return -EINVAL; + + report_enum = hdev->report_enum + rtype; + report = hid_get_report(report_enum, buf); + if (!report) + return -EINVAL; + + report_len = hid_report_len(report); + + if (size > report_len) + size = report_len; + + dma_data = kmemdup(buf, size, GFP_KERNEL); + if (!dma_data) + return -ENOMEM; + + ret = hid_hw_raw_request(hdev, + dma_data[0], + dma_data, + size, + rtype, + reqtype); + + if (ret > 0) + memcpy(buf, dma_data, ret); + + kfree(dma_data); + return ret; +} + static int hid_bpf_run_progs(struct hid_device *hdev, struct hid_bpf_ctx_kern *ctx) { enum bpf_hid_attach_type type; @@ -252,6 +314,7 @@ int __init hid_bpf_module_init(void) .array_detach = hid_bpf_array_detach, .hid_get_bits = hid_bpf_get_bits, .hid_set_bits = hid_bpf_set_bits, + .hid_raw_request = hid_bpf_raw_request, }; bpf_hid_set_hooks(&hooks); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 4f669dcddc08..f4b5d22f0831 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1686,8 +1686,7 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) } EXPORT_SYMBOL_GPL(hid_set_field); -static struct hid_report *hid_get_report(struct hid_report_enum *report_enum, - const u8 *data) +struct hid_report *hid_get_report(struct hid_report_enum *report_enum, const u8 *data) { struct hid_report *report; unsigned int n = 0; /* Normally report number is 0 */ diff --git a/include/linux/hid.h b/include/linux/hid.h index 7454e844324c..2f1b40d48265 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -316,15 +316,6 @@ struct hid_item { #define HID_BAT_ABSOLUTESTATEOFCHARGE 0x00850065 #define HID_VD_ASUS_CUSTOM_MEDIA_KEYS 0xff310076 -/* - * HID report types --- Ouch! HID spec says 1 2 3! - */ - -#define HID_INPUT_REPORT 0 -#define HID_OUTPUT_REPORT 1 -#define HID_FEATURE_REPORT 2 - -#define HID_REPORT_TYPES 3 /* * HID connect requests @@ -344,7 +335,7 @@ struct hid_item { * HID device quirks. */ -/* +/* * Increase this if you need to configure more HID quirks at module load time */ #define MAX_USBHID_BOOT_QUIRKS 4 @@ -946,6 +937,7 @@ __u32 hid_field_extract(const struct hid_device *hid, __u8 *report, unsigned offset, unsigned n); void implement(const struct hid_device *hid, u8 *report, unsigned int offset, unsigned int n, u32 value); +struct hid_report *hid_get_report(struct hid_report_enum *report_enum, const u8 *data); #ifdef CONFIG_PM int hid_driver_suspend(struct hid_device *hdev, pm_message_t state); diff --git a/include/uapi/linux/hid.h b/include/uapi/linux/hid.h index b34492a87a8a..bb690343cf9a 100644 --- a/include/uapi/linux/hid.h +++ b/include/uapi/linux/hid.h @@ -42,6 +42,16 @@ #define USB_INTERFACE_PROTOCOL_KEYBOARD 1 #define USB_INTERFACE_PROTOCOL_MOUSE 2 +/* + * HID report types --- Ouch! HID spec says 1 2 3! + */ + +#define HID_INPUT_REPORT 0 +#define HID_OUTPUT_REPORT 1 +#define HID_FEATURE_REPORT 2 + +#define HID_REPORT_TYPES 3 + /* * HID class requests */ From patchwork Fri Mar 18 16:15:26 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12785657 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id CF948C433F5 for ; Fri, 18 Mar 2022 16:20:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239035AbiCRQWJ (ORCPT ); Fri, 18 Mar 2022 12:22:09 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40486 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238968AbiCRQU7 (ORCPT ); Fri, 18 Mar 2022 12:20:59 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 5F345187B9F for ; Fri, 18 Mar 2022 09:18:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1647620292; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=LtpaW99kfZPGzmhb2z89Xff0Ygi8rBSg82+FQ185Veo=; b=Y1K1IZy2pzRXNAci4SS9Uj8FGfoEn3NZfRbMRtG7UZwXiyjQ3a9QtOO+WpIQist9CwI1QE Qi0xG7g44e8EUIAkTawYLsbNR2MXgCRvhE18N6xTPC6EMA8MD163BCu7mTgDaG/Euck9Wn 9DR33L0IyZqSImX7vVIjP2ZGQ/qDbnM= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-569-NJdxMPoKMhqmVt4r8-LRLQ-1; Fri, 18 Mar 2022 12:18:07 -0400 X-MC-Unique: NJdxMPoKMhqmVt4r8-LRLQ-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id E8FA21800754; Fri, 18 Mar 2022 16:18:05 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.159]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8E515420AA; Fri, 18 Mar 2022 16:17:57 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v3 15/17] selftests/bpf: add tests for hid_{get|set}_bits helpers Date: Fri, 18 Mar 2022 17:15:26 +0100 Message-Id: <20220318161528.1531164-16-benjamin.tissoires@redhat.com> In-Reply-To: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> References: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Simple test added here, with one use of each helper. Signed-off-by: Benjamin Tissoires --- changes in v3: - renamed hid_{get|set}_data into hid_{get|set}_bits changes in v2: - split the patch with libbpf left outside. --- tools/testing/selftests/bpf/prog_tests/hid.c | 59 ++++++++++++++++++++ tools/testing/selftests/bpf/progs/hid.c | 19 +++++++ 2 files changed, 78 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c index edc3af71e9ed..e8aa1c6357e8 100644 --- a/tools/testing/selftests/bpf/prog_tests/hid.c +++ b/tools/testing/selftests/bpf/prog_tests/hid.c @@ -531,6 +531,62 @@ static int test_hid_user_call(struct hid *hid_skel, int uhid_fd, int sysfs_fd) return ret; } +/* + * Attach hid_set_get_bits to the given uhid device, + * retrieve and open the matching hidraw node, + * inject one event in the uhid device, + * check that the program makes correct use of bpf_hid_{set|get}_bits. + */ +static int test_hid_set_get_bits(struct hid *hid_skel, int uhid_fd, int sysfs_fd) +{ + int err, hidraw_ino, hidraw_fd = -1; + char hidraw_path[64] = {0}; + u8 buf[10] = {0}; + int ret = -1; + + /* attach hid_set_get_bits program */ + hid_skel->links.hid_set_get_bits = + bpf_program__attach_hid(hid_skel->progs.hid_set_get_bits, sysfs_fd, 0); + if (!ASSERT_OK_PTR(hid_skel->links.hid_set_get_bits, + "attach_hid(hid_set_get_bits)")) + return PTR_ERR(hid_skel->links.hid_set_get_bits); + + hidraw_ino = get_hidraw(hid_skel->links.hid_set_get_bits); + if (!ASSERT_GE(hidraw_ino, 0, "get_hidraw")) + goto cleanup; + + /* open hidraw node to check the other side of the pipe */ + sprintf(hidraw_path, "/dev/hidraw%d", hidraw_ino); + hidraw_fd = open(hidraw_path, O_RDWR | O_NONBLOCK); + + if (!ASSERT_GE(hidraw_fd, 0, "open_hidraw")) + goto cleanup; + + /* inject one event */ + buf[0] = 1; + buf[1] = 42; + send_event(uhid_fd, buf, 6); + + /* read the data from hidraw */ + memset(buf, 0, sizeof(buf)); + err = read(hidraw_fd, buf, sizeof(buf)); + if (!ASSERT_EQ(err, 6, "read_hidraw")) + goto cleanup; + + if (!ASSERT_EQ(buf[2], (42 >> 2), "hid_set_get_bits")) + goto cleanup; + + ret = 0; + +cleanup: + if (hidraw_fd >= 0) + close(hidraw_fd); + + hid__detach(hid_skel); + + return ret; +} + /* * Attach hid_rdesc_fixup to the given uhid device, * retrieve and open the matching hidraw node, @@ -641,6 +697,9 @@ void serial_test_hid_bpf(void) err = test_hid_user_call(hid_skel, uhid_fd, sysfs_fd); ASSERT_OK(err, "hid_user"); + err = test_hid_set_get_bits(hid_skel, uhid_fd, sysfs_fd); + ASSERT_OK(err, "hid_set_get_data"); + /* * this test should be run last because we disconnect/reconnect * the device, meaning that it changes the overall uhid device diff --git a/tools/testing/selftests/bpf/progs/hid.c b/tools/testing/selftests/bpf/progs/hid.c index fbdbe9d1b605..d57571b9af9a 100644 --- a/tools/testing/selftests/bpf/progs/hid.c +++ b/tools/testing/selftests/bpf/progs/hid.c @@ -143,3 +143,22 @@ int hid_user(struct hid_bpf_ctx *ctx) return 0; } + +SEC("hid/device_event") +int hid_set_get_bits(struct hid_bpf_ctx *ctx) +{ + int ret; + __u32 data = 0; + + /* extract data at bit offset 10 of size 4 (half a byte) */ + ret = bpf_hid_get_bits(ctx, 10, 4, &data); + if (ret < 0) + return ret; + + /* reinject it */ + ret = bpf_hid_set_bits(ctx, 16, 4, data); + if (ret < 0) + return ret; + + return 0; +} From patchwork Fri Mar 18 16:15:27 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12785659 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 067C5C433F5 for ; Fri, 18 Mar 2022 16:21:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239078AbiCRQW2 (ORCPT ); Fri, 18 Mar 2022 12:22:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39302 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239041AbiCRQWJ (ORCPT ); Fri, 18 Mar 2022 12:22:09 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 8F2F62F09EE for ; Fri, 18 Mar 2022 09:18:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1647620300; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=iDasbQdKPIktrclpqHMmE3acJqdiFWhVrVqJ26AsKBw=; b=aChWACSla+1uwWtGZOWJ2eYuyE/pVcA/Co7y3ZEll0r0LofqbX0MtgvohSqYbiriUZzv/c KW3LqNRWK/zyCw1BDkR5GNcdlfpVeyTRJ+CS1VrCf3QNMLCGi/S9XRSsIBDTOT/43R2NtF sF3WJ75Oa63As6+ZWnFDvr59pNi3XYo= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-407-_EBLqudMNQKjWXIXAtoJpg-1; Fri, 18 Mar 2022 12:18:16 -0400 X-MC-Unique: _EBLqudMNQKjWXIXAtoJpg-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 416FB38035BF; Fri, 18 Mar 2022 16:18:15 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.159]) by smtp.corp.redhat.com (Postfix) with ESMTP id 328A533250; Fri, 18 Mar 2022 16:18:06 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v3 16/17] selftests/bpf: add tests for bpf_hid_hw_request Date: Fri, 18 Mar 2022 17:15:27 +0100 Message-Id: <20220318161528.1531164-17-benjamin.tissoires@redhat.com> In-Reply-To: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> References: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Add tests for the newly implemented function. We test here only the GET_REPORT part because the other calls are pure HID protocol and won't infer the result of the test of the bpf hook. Signed-off-by: Benjamin Tissoires --- changes in v3: - use the new hid_get_data API - directly use HID_FEATURE_REPORT and HID_REQ_GET_REPORT from uapi changes in v2: - split the series by bpf/libbpf/hid/selftests and samples --- tools/testing/selftests/bpf/prog_tests/hid.c | 72 +++++++++++++++++++- tools/testing/selftests/bpf/progs/hid.c | 41 +++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c index e8aa1c6357e8..27b6ea7da599 100644 --- a/tools/testing/selftests/bpf/prog_tests/hid.c +++ b/tools/testing/selftests/bpf/prog_tests/hid.c @@ -11,6 +11,7 @@ #include #include #include +#include static unsigned char rdesc[] = { 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ @@ -67,6 +68,8 @@ static unsigned char rdesc[] = { 0xc0, /* END_COLLECTION */ }; +static u8 feature_data[] = { 1, 2 }; + static pthread_mutex_t uhid_started_mtx = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t uhid_started = PTHREAD_COND_INITIALIZER; @@ -126,7 +129,7 @@ static void destroy(int fd) static int event(int fd) { - struct uhid_event ev; + struct uhid_event ev, answer; ssize_t ret; memset(&ev, 0, sizeof(ev)); @@ -143,6 +146,8 @@ static int event(int fd) return -EFAULT; } + memset(&answer, 0, sizeof(answer)); + switch (ev.type) { case UHID_START: pthread_mutex_lock(&uhid_started_mtx); @@ -167,6 +172,15 @@ static int event(int fd) break; case UHID_GET_REPORT: fprintf(stderr, "UHID_GET_REPORT from uhid-dev\n"); + + answer.type = UHID_GET_REPORT_REPLY; + answer.u.get_report_reply.id = ev.u.get_report.id; + answer.u.get_report_reply.err = ev.u.get_report.rnum == 1 ? 0 : -EIO; + answer.u.get_report_reply.size = sizeof(feature_data); + memcpy(answer.u.get_report_reply.data, feature_data, sizeof(feature_data)); + + uhid_write(fd, &answer); + break; case UHID_SET_REPORT: fprintf(stderr, "UHID_SET_REPORT from uhid-dev\n"); @@ -587,6 +601,59 @@ static int test_hid_set_get_bits(struct hid *hid_skel, int uhid_fd, int sysfs_fd return ret; } +/* + * Attach hid_user_raw_request to the given uhid device, + * call the bpf program from userspace + * check that the program is called and does the expected. + */ +static int test_hid_user_raw_request_call(struct hid *hid_skel, int uhid_fd, int sysfs_fd) +{ + int err, prog_fd; + u8 buf[10] = {0}; + int ret = -1; + + LIBBPF_OPTS(bpf_test_run_opts, run_attrs, + .repeat = 1, + .ctx_in = &sysfs_fd, + .ctx_size_in = sizeof(sysfs_fd), + .data_in = buf, + .data_size_in = sizeof(buf), + .data_out = buf, + .data_size_out = sizeof(buf), + ); + + /* attach hid_user_raw_request program */ + hid_skel->links.hid_user_raw_request = + bpf_program__attach_hid(hid_skel->progs.hid_user_raw_request, sysfs_fd, 0); + if (!ASSERT_OK_PTR(hid_skel->links.hid_user_raw_request, + "attach_hid(hid_user_raw_request)")) + return PTR_ERR(hid_skel->links.hid_user_raw_request); + + buf[0] = HID_FEATURE_REPORT; + buf[1] = HID_REQ_GET_REPORT; + buf[2] = 1; /* report ID */ + + prog_fd = bpf_program__fd(hid_skel->progs.hid_user_raw_request); + + err = bpf_prog_test_run_opts(prog_fd, &run_attrs); + if (!ASSERT_EQ(err, 0, "bpf_prog_test_run_xattr")) + goto cleanup; + + if (!ASSERT_EQ(run_attrs.retval, 2, "bpf_prog_test_run_xattr_retval")) + goto cleanup; + + if (!ASSERT_EQ(buf[3], 2, "hid_user_raw_request_check_in")) + goto cleanup; + + ret = 0; + +cleanup: + + hid__detach(hid_skel); + + return ret; +} + /* * Attach hid_rdesc_fixup to the given uhid device, * retrieve and open the matching hidraw node, @@ -700,6 +767,9 @@ void serial_test_hid_bpf(void) err = test_hid_set_get_bits(hid_skel, uhid_fd, sysfs_fd); ASSERT_OK(err, "hid_set_get_data"); + err = test_hid_user_raw_request_call(hid_skel, uhid_fd, sysfs_fd); + ASSERT_OK(err, "hid_user_raw_request"); + /* * this test should be run last because we disconnect/reconnect * the device, meaning that it changes the overall uhid device diff --git a/tools/testing/selftests/bpf/progs/hid.c b/tools/testing/selftests/bpf/progs/hid.c index d57571b9af9a..d7b75c0cba4b 100644 --- a/tools/testing/selftests/bpf/progs/hid.c +++ b/tools/testing/selftests/bpf/progs/hid.c @@ -162,3 +162,44 @@ int hid_set_get_bits(struct hid_bpf_ctx *ctx) return 0; } + +SEC("hid/user_event") +int hid_user_raw_request(struct hid_bpf_ctx *ctx) +{ + int ret; + __u32 size; + __u8 rtype, reqtype; + __u8 *data = bpf_hid_get_data(ctx, 0 /* offset */, 10 /* size */); + + if (!data) + return 0; /* EPERM check */ + + /* + * build up a custom API for our needs: + * offset 0, size 1: report type + * offset 1, size 1: request type + * offset 2+: data + */ + rtype = data[0]; + reqtype = data[1]; + size = ctx->size - 2; + + if (size <= 8) { /* 8 = len(data) - 2 */ + ret = bpf_hid_raw_request(ctx, + &data[2], + size, + rtype, + reqtype); + if (ret < 0) + return ret; + } else { + return -7; /* -E2BIG */ + } + + ctx->size = ret + 2; + ctx->retval = ret; + + ret = 0; + + return ret; +} From patchwork Fri Mar 18 16:15:28 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12785661 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 454FEC43217 for ; Fri, 18 Mar 2022 16:21:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237713AbiCRQWe (ORCPT ); Fri, 18 Mar 2022 12:22:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40298 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238895AbiCRQWL (ORCPT ); Fri, 18 Mar 2022 12:22:11 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id C5D6A180205 for ; Fri, 18 Mar 2022 09:18:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1647620303; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=5nkT5UIYivW5SFiCEpbqJoYmzN8iv9pwyawnhfGj5UQ=; b=HmXSffYpj3crE2Pf++dVEX3yytWWnlo5eB94oJpSBAYczNSY1sxXiDFb2vTG3mlIIAzZJ7 6IQlgiZAF0hZKkHmgxC9qAhDoRGSZnLiF6BInLXLakvM1mIo2Tnf0C5iJbwWfs0n9HaMs+ pZ2fv0carNawn8XTLAqgNQkXJpgXTnA= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-477-3xVfldHIP7OokTFHQ4LbEw-1; Fri, 18 Mar 2022 12:18:19 -0400 X-MC-Unique: 3xVfldHIP7OokTFHQ4LbEw-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id B100510113D8; Fri, 18 Mar 2022 16:18:18 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.159]) by smtp.corp.redhat.com (Postfix) with ESMTP id 81E3533250; Fri, 18 Mar 2022 16:18:15 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v3 17/17] Documentation: add HID-BPF docs Date: Fri, 18 Mar 2022 17:15:28 +0100 Message-Id: <20220318161528.1531164-18-benjamin.tissoires@redhat.com> In-Reply-To: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> References: <20220318161528.1531164-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Gives a primer on HID-BPF. Signed-off-by: Benjamin Tissoires --- new in v3 --- Documentation/hid/hid-bpf.rst | 444 ++++++++++++++++++++++++++++++++++ Documentation/hid/index.rst | 1 + include/uapi/linux/bpf_hid.h | 54 ++++- 3 files changed, 492 insertions(+), 7 deletions(-) create mode 100644 Documentation/hid/hid-bpf.rst diff --git a/Documentation/hid/hid-bpf.rst b/Documentation/hid/hid-bpf.rst new file mode 100644 index 000000000000..0bf0d937b0e1 --- /dev/null +++ b/Documentation/hid/hid-bpf.rst @@ -0,0 +1,444 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======= +HID-BPF +======= + +HID is a standard protocol for input devices and it can greatly make use +of the eBPF capabilities to speed up development and add new capabilities +to the existing HID interfaces. + +.. contents:: + :local: + :depth: 2 + + +When (and why) using HID-BPF +============================ + +We can enumerate several use cases for when using HID-BPF is better than +using a standard kernel driver fix. + +dead zone of a joystick +----------------------- + +Assuming you have a joystick that is getting older, it is common to see it +wobbling around its neutral point. This is usually filtered at the application +level by adding a *dead zone* for this specific axis. + +With HID-BPF, we can put the filtering of this dead zone in the kernel directly +so we don't wake up userspace when nothing else is happening on the input +controller. + +Of course, given that this dead zone is device specific, we can not create a +generic fix for all of the same joysticks. We *could* create a custom kernel +API for this (by adding a sysfs for instance), but there is no guarantees this +new kernel API will be broadly adopted and maintained. + +HID-BPF allows the userspace program who knows it will make use of this capability +to load the program itself, ensuring we only load the custom API when we have a user. + +simple fixup of report descriptor +--------------------------------- + +In the HID tree, we have half of the drivers that are "simple" and +that just fix one key or one byte in the report descriptor. +Currently, for users of such devices, the process of fixing them +is long and painful. + +With eBPF, we can reduce the burden of building those fixup kernel patches +by providing an eBPF program that does it. Once this has been validated by +the user, we can then embed the source code into the kernel tree and ship it +and load it directly instead of loading a specific kernel module for it. + +Note: the distribution and inclusion in the kernel is still not there yet. + +add a new fancy feature that requires a new kernel API +------------------------------------------------------ + +We have the case currently for the Universal Stylus Interface pens for example. +Basically, USI pens are requiring a new kernel API because there are +some channels of communication our HID and input stack are not capable +of. Instead of using hidraw or creating new sysfs or ioctls, we can rely +on eBPF to have the kernel API controlled by the consumer and to not +impact the performances by waking up userspace every time there is an +event. + +morph a device into something else and control that from userspace +------------------------------------------------------------------ + +Right now, the kernel has to make a choice on how a device looks like. +For that, it can not decide to transform a given device into something else +because that would be lying to userspace and will be even more harder to +unwind when we need the actual definition of the device. + +However, sometimes some new devices are useless with that sane way of defining +devices. For example, the Microsoft Surface Dial is a pushbutton with haptic +feedback that is barely usable as of today. + +With eBPF, userspace can morph that device into a mouse, and convert the dial +events into wheel events. Also, the userspace program can set/unset the haptic +feedback depending on the context. For example, if a menu is popped-up on the +screen we likely need to have a haptic click every 5 degrees, while when +we are fine-grain scrolling in a web page, we probably want the best resolution +without those annoying clicks. + +firewall +-------- + +What if we want to prevent other users to access a specific feature of a +device? (think a possibly bonker firmware update entry popint) + +With eBPF, we can intercept any HID command emitted to the device and +validate it or not. + +This also allows to sync the state between the userspace and the +kernel/bpf program because we can intercept any incoming command. + +tracing +------- + +The last usage is tracing events and all the fun we can do we BPF to summarize +and analyze events. + +Right now, tracing relies on hidraw. It works well except for a couple +of issues: + +1. if the driver doesn't export a hidraw node, we can't trace anything + (eBPF will be a "god-mode" there, so it might raise some eyebrows) +2. hidraw doesn't catch the other process requests to the device, which + means that we have cases where we need to add printks to the kernel + to understand what is happening. + +High-level view of HID-BPF +========================== + +The main idea behind HID-BPF is that it works at an array of bytes level. +Thus, all of the parsing of the HID report and the HID report descriptor +must be implemented in the userspace component that loads the eBPF +program. + +For example, in the dead zone joystick from above, knowing which fields +in the data stream needs to be set to ``0`` needs to be computed by userspace. + +A corrolar of this is that HID-BPF doesn't know about the other subsystems +available in the kernel. *You can not directly emit input event through the +input API from eBPF*. + +When a BPF program need to emit input events, it needs to talk HID, and rely +on the HID kernel processing to translate the HID data into input events. + +Available types of programs +=========================== + +HID-BPF has the following attachment types available: + +1. ``BPF_HID_DEVICE_EVENT`` defined with a ``SEC("hid/device_event")`` in libbpf +2. ``BPF_HID_USER_EVENT`` defined with a ``SEC("hid/user_event")`` in libbpf +3. ``BPF_HID_DRIVER_EVENT`` defined with a ``SEC("hid/driver_event")`` in libbpf +4. ``BPF_HID_RDESC_FIXUP`` defined with a ``SEC("hid/rdesc_fixup")`` in libbpf + +The above types are defined based on where the event came from. + +A ``BPF_HID_DEVICE_EVENT`` is calling a BPF program when an event is received from +the device. Thus we are in IRQ context and can act on the data or notify userspace. + +In the same way, a ``BPF_HID_USER_EVENT`` means that userspace called the syscall +``BPF_PROG_RUN`` facility. + +A ``BPF_HID_DRIVER_EVENT`` means that the kernel driver emitted an event that the bpf +programs want to be notified of. Such events are resume, reset, probed, but also +a call to a request toward the device (a call to ``hid_hw_raw_request()`` for example). + +Last, ``BPF_HID_RDESC_FIXUP`` is different from the others as there can be only one +BPF program of this type. This is called on ``probe`` from the driver and allows to +change the report descriptor from the BPF program. + +General overview of a HID-BPF program +===================================== + +User API available as a context in programs +------------------------------------------- + +.. kernel-doc:: include/uapi/linux/bpf_hid.h + +Accessing the data attached to the context +------------------------------------------ + +The ``struct hid_bpf_ctx`` doesn't export the ``data`` fields directly and to access +it, a bpf program needs to first call ``bpf_hid_get_data(context, offset, size)``. + +``offset`` can be any integer, but ``size`` needs to be constant, known at compile +time. + +This allows the following: + +1. for a given device, if we know that the report length will always be of a certain value, + we can request the ``data`` pointer to point at the full report length. + + The kernel will ensure we are using a correct size and offset and eBPF will ensure + the code will not attempt to read or write outside of the boundaries:: + + __u8 *data = bpf_hid_get_data(ctx, 0 /* offset */, 256 /* size */); + + if (!data) + return 0; /* ensure data is correct, now the verifier knows we + * have 256 bytes available */ + + bpf_printk("hello world: %02x %02x %02x", data[0], data[128], data[255]); + +2. if the report length is variable, but we know the value of ``X`` is always a 16-bits + integer, we can then have a pointer to that value only:: + + __u16 *x = bpf_hid_get_data(ctx, offset, sizeof(*x)); + + if (!x) + return 0; /* something when wrong */ + + *x += 1; /* increment X by one */ + +Bit access of data field (an alternative to ``bpf_hid_get_data``) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The HID devices often work at the bit level in the emitted report. +For instance, a given button will be at a given offset (in bits) in the report +and usually of size 1 bit. + +In order to easily access those data, BPF program can rely on ``bpf_hid_get_bits()`` +and ``bpf_hid_set_bits()``. + +Those 2 functions do not require any of the arguments to be a constant like +``bpf_hid_get_data()``, meaning that they are appropriate for accessing +a field unknown at compile time of an unknown size. +The counterpart of those 2 helpers is that they are effectively copying the +data to/from a ``__u32`` instead of having a direct pointer access to the +field. + +Effect of a HID-BPF program +--------------------------- + +For all HID-BPF attachment types except for ``BPF_HID_RDESC_FIXUP``, several eBPF +programs can be attached to the same device. + +Unless ``BPF_F_INSERT_HEAD`` is added to the flags while attaching the program, the +new program is appended at the end of the list. In some cases (tracing for instance) +we need to get the unprocessed events from the device, and ``BPF_F_INSERT_HEAD`` will +insert the new program at the beginning of the list. + +Note however that there is no guarantees that another program is loaded with that +same flag, and thus our program may be second in the list. + +``BPF_HID_DEVICE_EVENT`` +~~~~~~~~~~~~~~~~~~~~~~~~ + +Whenever a matching event is raised, the eBPF programs are called one after the other +and are working on the same data buffer. + +If a program changes the data associated with the context, the next one will see +the new data and will have *no* idea of what the original data was. + +Once all the programs are run and return ``0``, the rest of the HID stack will +work on the modified data, with the ``size`` field of the hid_bpf_ctx being the new +size of the input stream of data. + +If a BPF program returns a negative error, this has the same effect than setting +the ``size`` field of ``hid_bpf_ctx`` to ``0``: the event is dropped from the HID +processing. No clients (hidraw, input, LEDs) will ever see that event coming in. + +``BPF_HID_USER_EVENT`` +~~~~~~~~~~~~~~~~~~~~~~ + +One can attach several ``BPF_HID_USER_EVENT`` on a given device. But because the caller +needs to set which BPF program is used when calling the syscall ``BPF_PROG_RUN``, only +this particular BPF program will be run. + +The ``data`` associated with the ``hid_bpf_ctx`` contains the input data of the given +syscall and is big enough to contain both the input data and the requested output data. + +The output data from the syscall, if ``size_out`` is set to a positive value, is copied +from the content of the ``data`` field of ``hid_bpf_ctx``. + +``BPF_HID_DRIVER_EVENT`` +~~~~~~~~~~~~~~~~~~~~~~~~ + +The availability of the ``data`` field of ``hid_bpf_ctx`` depends on the event subtype: + +* **probe** event: ``data`` contains the report descriptor of the device (this is read-only) +* **reset**, **resume** events: no data is provided +* **device request**: ``data`` contains the incoming request made to the device. The BPF + program can decide whether or not forwarding the request to the device. +* **device request answer**: ``data`` contains the answer of the request. + +``BPF_HID_RDESC_FIXUP`` +~~~~~~~~~~~~~~~~~~~~~~~ + +Last, the ``BPF_HID_RDESC_FIXUP`` program works in the similar maneer than +``.report_fixup`` of ``struct hid_driver``. + +When the device is probed, the kernel sets the data buffer of the context with the +content of the report descriptor. The memory associated with that buffer is +``HID_MAX_DESCRIPTOR_SIZE`` (4 kB as of now). + +The eBPF program can modify the data buffer at will and then the kernel uses the +new content and size as the report descriptor. + +Whenever a ``BPF_HID_RDESC_FIXUP`` program is attached (if no program was attached +before), the kernel immediately disconnects the HID device, and do a reprobe. + +In the same way, when the ``BPF_HID_RDESC_FIXUP`` program is detached, the kernel +issues a disconnect on the device. + +To update the report descriptor in place, users can replace the current +``BPF_HID_RDESC_FIXUP`` program through the usual ``BPF_LINK_UPDATE`` syscall. +There will be only one disconnect emitted by the kernel. + +Attaching a bpf program to a device +=================================== + +``libbpf`` exports a helper to attach a HID-BPF program: ``bpf_program__attach_hid(program, fd, flags)``. + +The ``program`` and ``flags`` parameters are relatively obvious, but what about the +``fd`` argument? + +While working on HID-BPF it came out quickly enough that we can not rely on +hidraw to bind a BPF program to a HID device. hidraw is an artefact of the processing +of the HID device, and is not stable. Some drivers even disable it, so that removes the +tracing capabilies on those devices (where it is interesting to get the non-hidraw +traces). + +The solution is to use the sysfs tree and more specifically the ``uevent`` sysfs entry. + +For a given HID device, we have the ``uevent`` node at ``/sys/bus/hid/devices/BUS:VID:PID.000N/uevent``. + +``uevent`` is convenient as it contains already some information about the device itself: +the driver in use, the name of the HID device, its ID, its Unique field and the modalias. + +However, as mentioned previously, we can not really rely on hidraw for HID-BPF. +Which means we need to get the report descriptor from the device thorugh the sysfs too. +This is available at ``/sys/bus/hid/devices/BUS:VID:PID.000N/report_descriptor`` as a +binary stream. + +Parsing the report descriptor is the responsibility of the BPF programmer or the userspace +component that loads the eBPF program. + +An (almost) complete example of a BPF enhanced HID device +========================================================= + +*Foreword: for most parts, this could be implemented as a kernel driver* + +Let's imagine we have a new tablet device that has some haptic capabilities +to simulate the surface the user is scratching on. This device would also have +a specific 3 positions switch to toggle between *pencil on paper*, *cray on a wall* +and *brush on a painting canvas*. To make things even better, we can control the +physical position of the switch through a feature report. + +And of course, the switch is relying on some userspace component to control the +haptic feature of the device itself. + +Filtering events +---------------- + +The first step consists in filtering events from the device. Given that the switch +position is actually reported in the flow of the pen events, using hidraw to implement +that filtering would mean that we wake up userspace for every single event. + +This is OK for libinput, but having an external library that is just interested in +one byte in the report is less than ideal. + +For that, we can create a basic skeleton for our BPF program:: + + #include + #include + #include + + /* HID programs need to be GPL */ + char _license[] SEC("license") = "GPL"; + + struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 4096 * 64); + } ringbuf SEC(".maps"); + + __u8 current_value = 0; + + SEC("hid/device_event") + int filter_switch(struct hid_bpf_ctx *ctx) + { + __u8 *data = bpf_hid_get_data(ctx, 0 /* offset */, 192 /* size */); + __u8 *buf; + + if (!data) + return 0; /* EPERM check */ + + if (current_value != data[152]) { + buf = bpf_ringbuf_reserve(&ringbuf, 1, 0); + if (!buf) + return 0; + + *buf = data[152]; + + bpf_ringbuf_commit(buf, 0); + + current_value = data[152]; + } + + return 0; + } + +Our userspace program can now listen to notifications on the ring buffer, and +is awaken only when the value changes. + +Controlling the device +---------------------- + +To be able to change the haptic feedback from the tablet, the userspace program +needs to emit a feature report on the device itself. + +Instead of using hidraw for that, we can create a ``BPF_HID_USER_EVENT`` program +that talks to the device:: + + SEC("hid/user_event") + int send_haptic(struct hid_bpf_ctx *ctx) + { + __u8 *data = bpf_hid_get_data(ctx, 0 /* offset */, 1 /* size */); + int ret; + + if (!data) + return 0; /* EPERM check */ + + ret = bpf_hid_raw_request(ctx, data, HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + ctx->retval = ret; + + return 0; + } + +And then userspace needs to call that program directly:: + + static int set_haptic(struct hid *hid_skel, int sysfs_fd, __u8 haptic_value) + int err, prog_fd; + int ret = -1; + + LIBBPF_OPTS(bpf_test_run_opts, run_attrs, + .repeat = 1, + .ctx_in = &sysfs_fd, + .ctx_size_in = sizeof(sysfs_fd), + .data_in = &haptic_value, + .data_size_in = 1, + ); + + prog_fd = bpf_program__fd(hid_skel->progs.set_haptic); + + err = bpf_prog_test_run_opts(prog_fd, &run_attrs); + return err; + } + +Now the interesting bit is that our userspace program is aware of the haptic +state and can control it. + +Which means, that we can also export a dbus API for applications to change the +haptic feedback. The dbus API would be simple (probably just a string property). + +The interesting bit here is that we did not created a new kernel API for this. +Which means that if there is a bug in our implementation, we can change the +interface with the kernel as will, because the userspace application is responsible +for its own usage. diff --git a/Documentation/hid/index.rst b/Documentation/hid/index.rst index e50f513c579c..b2028f382f11 100644 --- a/Documentation/hid/index.rst +++ b/Documentation/hid/index.rst @@ -11,6 +11,7 @@ Human Interface Devices (HID) hidraw hid-sensor hid-transport + hid-bpf uhid diff --git a/include/uapi/linux/bpf_hid.h b/include/uapi/linux/bpf_hid.h index 64a8b9dd8809..8e907e7fe77b 100644 --- a/include/uapi/linux/bpf_hid.h +++ b/include/uapi/linux/bpf_hid.h @@ -11,20 +11,60 @@ #include +/** + * enum hid_bpf_event - event types in struct hid_bpf_ctx + * + * Event types are not tied to a given attach type, there might + * be multiple event types for one attach type. + * + * @HID_BPF_UNDEF: sentinel value, should never be set by the kernel + * @HID_BPF_DEVICE_EVENT: used when attach type is ``BPF_HID_DEVICE_EVENT`` + * @HID_BPF_RDESC_FIXUP: used when attach type is ``BPF_HID_RDESC_FIXUP`` + * @HID_BPF_USER_EVENT: used when attach type is ``BPF_HID_USER_EVENT`` + */ enum hid_bpf_event { HID_BPF_UNDEF = 0, - HID_BPF_DEVICE_EVENT, /* when attach type is BPF_HID_DEVICE_EVENT */ - HID_BPF_RDESC_FIXUP, /* ................... BPF_HID_RDESC_FIXUP */ - HID_BPF_USER_EVENT, /* ................... BPF_HID_USER_EVENT */ + HID_BPF_DEVICE_EVENT, + HID_BPF_RDESC_FIXUP, + HID_BPF_USER_EVENT, }; /* User accessible data for HID programs. Add new fields at the end. */ +/** + * struct hid_bpf_ctx - User accessible data for all HID programs + * + * ``data`` is not directly accessible from the context. We need to issue + * a call to ``bpf_hid_get_data()`` in order to get a pointer to that field. + * + * @type: Of type enum hid_bpf_event. This value is read-only and matters when there + * is more than one event type per attachment type. + * @allocated_size: Allocated size of data, read-only. + * + * This is how much memory is available and can be requested + * by the HID program. + * Note that for ``HID_BPF_RDESC_FIXUP``, that memory is set to + * ``4096`` (4 KB) + * @size: Valid data in the data field, read-write. + * + * Programs can get the available valid size in data by fetching this field. + * Programs can also change this value and even set it to ``0`` to discard the + * data from this event. + * + * ``size`` must always be less or equal than ``allocated_size`` (it is enforced + * once all BPF programs have been run). + * @retval: Return value of the program when type is ``HID_BPF_USER_EVENT``, read-write. + * + * Returning from the program an error means that the execution of the program + * failed. However, one may want to express that the program executed correctly, + * but the underlying calls failed for a specific reason. This is when we use + * retval. + */ struct hid_bpf_ctx { - __u32 type; /* enum hid_bpf_event, RO */ - __u32 allocated_size; /* allocated size of data, RO */ + __u32 type; + __u32 allocated_size; - __u32 size; /* valid data in data field, RW */ - __s32 retval; /* when type is HID_BPF_USER_EVENT, RW */ + __u32 size; + __s32 retval; }; #endif /* _UAPI__LINUX_BPF_HID_H__ */