mirror of
https://github.com/armbian/linux-cix.git
synced 2026-01-06 12:30:45 -08:00
HID: initial BPF implementation
Declare an entry point that can use fmod_ret BPF programs, and also an API to access and change the incoming data. A simpler implementation would consist in just calling hid_bpf_device_event() for any incoming event and let users deal with the fact that they will be called for any event of any device. The goal of HID-BPF is to partially replace drivers, so this situation can be problematic because we might have programs which will step on each other toes. For that, we add a new API hid_bpf_attach_prog() that can be called from a syscall and we manually deal with a jump table in hid-bpf. Whenever we add a program to the jump table (in other words, when we attach a program to a HID device), we keep the number of time we added this program in the jump table so we can release it whenever there are no other users. HID devices have an RCU protected list of available programs in the jump table, and those programs are called one after the other thanks to bpf_tail_call(). To achieve the detection of users losing their fds on the programs we attached, we add 2 tracing facilities on bpf_prog_release() (for when a fd is closed) and bpf_free_inode() (for when a pinned program gets unpinned). Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
committed by
Jiri Kosina
parent
25621bcc89
commit
f5c27da4e3
@@ -1284,6 +1284,8 @@ config HID_KUNIT_TEST
|
||||
|
||||
endmenu
|
||||
|
||||
source "drivers/hid/bpf/Kconfig"
|
||||
|
||||
endif # HID
|
||||
|
||||
source "drivers/hid/usbhid/Kconfig"
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
hid-y := hid-core.o hid-input.o hid-quirks.o
|
||||
hid-$(CONFIG_DEBUG_FS) += hid-debug.o
|
||||
|
||||
obj-$(CONFIG_HID_BPF) += bpf/
|
||||
|
||||
obj-$(CONFIG_HID) += hid.o
|
||||
obj-$(CONFIG_UHID) += uhid.o
|
||||
|
||||
|
||||
17
drivers/hid/bpf/Kconfig
Normal file
17
drivers/hid/bpf/Kconfig
Normal file
@@ -0,0 +1,17 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
menu "HID-BPF support"
|
||||
|
||||
config HID_BPF
|
||||
bool "HID-BPF support"
|
||||
default HID_SUPPORT
|
||||
depends on BPF && BPF_SYSCALL
|
||||
help
|
||||
This option allows to support eBPF programs on the HID subsystem.
|
||||
eBPF programs can fix HID devices in a lighter way than a full
|
||||
kernel patch and allow a lot more flexibility.
|
||||
|
||||
For documentation, see Documentation/hid/hid-bpf.rst
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
endmenu
|
||||
11
drivers/hid/bpf/Makefile
Normal file
11
drivers/hid/bpf/Makefile
Normal file
@@ -0,0 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for HID-BPF
|
||||
#
|
||||
|
||||
LIBBPF_INCLUDE = $(srctree)/tools/lib
|
||||
|
||||
obj-$(CONFIG_HID_BPF) += hid_bpf.o
|
||||
CFLAGS_hid_bpf_dispatch.o += -I$(LIBBPF_INCLUDE)
|
||||
CFLAGS_hid_bpf_jmp_table.o += -I$(LIBBPF_INCLUDE)
|
||||
hid_bpf-objs += hid_bpf_dispatch.o hid_bpf_jmp_table.o
|
||||
93
drivers/hid/bpf/entrypoints/Makefile
Normal file
93
drivers/hid/bpf/entrypoints/Makefile
Normal file
@@ -0,0 +1,93 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
OUTPUT := .output
|
||||
abs_out := $(abspath $(OUTPUT))
|
||||
|
||||
CLANG ?= clang
|
||||
LLC ?= llc
|
||||
LLVM_STRIP ?= llvm-strip
|
||||
|
||||
TOOLS_PATH := $(abspath ../../../../tools)
|
||||
BPFTOOL_SRC := $(TOOLS_PATH)/bpf/bpftool
|
||||
BPFTOOL_OUTPUT := $(abs_out)/bpftool
|
||||
DEFAULT_BPFTOOL := $(BPFTOOL_OUTPUT)/bootstrap/bpftool
|
||||
BPFTOOL ?= $(DEFAULT_BPFTOOL)
|
||||
|
||||
LIBBPF_SRC := $(TOOLS_PATH)/lib/bpf
|
||||
LIBBPF_OUTPUT := $(abs_out)/libbpf
|
||||
LIBBPF_DESTDIR := $(LIBBPF_OUTPUT)
|
||||
LIBBPF_INCLUDE := $(LIBBPF_DESTDIR)/include
|
||||
BPFOBJ := $(LIBBPF_OUTPUT)/libbpf.a
|
||||
|
||||
INCLUDES := -I$(OUTPUT) -I$(LIBBPF_INCLUDE) -I$(TOOLS_PATH)/include/uapi
|
||||
CFLAGS := -g -Wall
|
||||
|
||||
VMLINUX_BTF_PATHS ?= $(if $(O),$(O)/vmlinux) \
|
||||
$(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/vmlinux) \
|
||||
../../../../vmlinux \
|
||||
/sys/kernel/btf/vmlinux \
|
||||
/boot/vmlinux-$(shell uname -r)
|
||||
VMLINUX_BTF ?= $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS))))
|
||||
ifeq ($(VMLINUX_BTF),)
|
||||
$(error Cannot find a vmlinux for VMLINUX_BTF at any of "$(VMLINUX_BTF_PATHS)")
|
||||
endif
|
||||
|
||||
ifeq ($(V),1)
|
||||
Q =
|
||||
msg =
|
||||
else
|
||||
Q = @
|
||||
msg = @printf ' %-8s %s%s\n' "$(1)" "$(notdir $(2))" "$(if $(3), $(3))";
|
||||
MAKEFLAGS += --no-print-directory
|
||||
submake_extras := feature_display=0
|
||||
endif
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: entrypoints.lskel.h
|
||||
|
||||
clean:
|
||||
$(call msg,CLEAN)
|
||||
$(Q)rm -rf $(OUTPUT) entrypoints
|
||||
|
||||
entrypoints.lskel.h: $(OUTPUT)/entrypoints.bpf.o | $(BPFTOOL)
|
||||
$(call msg,GEN-SKEL,$@)
|
||||
$(Q)$(BPFTOOL) gen skeleton -L $< > $@
|
||||
|
||||
|
||||
$(OUTPUT)/entrypoints.bpf.o: entrypoints.bpf.c $(OUTPUT)/vmlinux.h $(BPFOBJ) | $(OUTPUT)
|
||||
$(call msg,BPF,$@)
|
||||
$(Q)$(CLANG) -g -O2 -target bpf $(INCLUDES) \
|
||||
-c $(filter %.c,$^) -o $@ && \
|
||||
$(LLVM_STRIP) -g $@
|
||||
|
||||
$(OUTPUT)/vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL) | $(INCLUDE_DIR)
|
||||
ifeq ($(VMLINUX_H),)
|
||||
$(call msg,GEN,,$@)
|
||||
$(Q)$(BPFTOOL) btf dump file $(VMLINUX_BTF) format c > $@
|
||||
else
|
||||
$(call msg,CP,,$@)
|
||||
$(Q)cp "$(VMLINUX_H)" $@
|
||||
endif
|
||||
|
||||
$(OUTPUT) $(LIBBPF_OUTPUT) $(BPFTOOL_OUTPUT):
|
||||
$(call msg,MKDIR,$@)
|
||||
$(Q)mkdir -p $@
|
||||
|
||||
$(BPFOBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(LIBBPF_OUTPUT)
|
||||
$(Q)$(MAKE) $(submake_extras) -C $(LIBBPF_SRC) \
|
||||
OUTPUT=$(abspath $(dir $@))/ prefix= \
|
||||
DESTDIR=$(LIBBPF_DESTDIR) $(abspath $@) install_headers
|
||||
|
||||
ifeq ($(CROSS_COMPILE),)
|
||||
$(DEFAULT_BPFTOOL): $(BPFOBJ) | $(BPFTOOL_OUTPUT)
|
||||
$(Q)$(MAKE) $(submake_extras) -C $(BPFTOOL_SRC) \
|
||||
OUTPUT=$(BPFTOOL_OUTPUT)/ \
|
||||
LIBBPF_BOOTSTRAP_OUTPUT=$(LIBBPF_OUTPUT)/ \
|
||||
LIBBPF_BOOTSTRAP_DESTDIR=$(LIBBPF_DESTDIR)/ bootstrap
|
||||
else
|
||||
$(DEFAULT_BPFTOOL): | $(BPFTOOL_OUTPUT)
|
||||
$(Q)$(MAKE) $(submake_extras) -C $(BPFTOOL_SRC) \
|
||||
OUTPUT=$(BPFTOOL_OUTPUT)/ bootstrap
|
||||
endif
|
||||
4
drivers/hid/bpf/entrypoints/README
Normal file
4
drivers/hid/bpf/entrypoints/README
Normal file
@@ -0,0 +1,4 @@
|
||||
WARNING:
|
||||
If you change "entrypoints.bpf.c" do "make -j" in this directory to rebuild "entrypoints.skel.h".
|
||||
Make sure to have clang 10 installed.
|
||||
See Documentation/bpf/bpf_devel_QA.rst
|
||||
66
drivers/hid/bpf/entrypoints/entrypoints.bpf.c
Normal file
66
drivers/hid/bpf/entrypoints/entrypoints.bpf.c
Normal file
@@ -0,0 +1,66 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2022 Benjamin Tissoires */
|
||||
|
||||
#include ".output/vmlinux.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
#define HID_BPF_MAX_PROGS 1024
|
||||
|
||||
extern bool call_hid_bpf_prog_release(u64 prog, int table_cnt) __ksym;
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
|
||||
__uint(max_entries, HID_BPF_MAX_PROGS);
|
||||
__uint(key_size, sizeof(__u32));
|
||||
__uint(value_size, sizeof(__u32));
|
||||
} hid_jmp_table SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(max_entries, HID_BPF_MAX_PROGS * HID_BPF_PROG_TYPE_MAX);
|
||||
__type(key, void *);
|
||||
__type(value, __u8);
|
||||
} progs_map SEC(".maps");
|
||||
|
||||
SEC("fmod_ret/__hid_bpf_tail_call")
|
||||
int BPF_PROG(hid_tail_call, struct hid_bpf_ctx *hctx)
|
||||
{
|
||||
bpf_tail_call(ctx, &hid_jmp_table, hctx->index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void release_prog(u64 prog)
|
||||
{
|
||||
u8 *value;
|
||||
|
||||
value = bpf_map_lookup_elem(&progs_map, &prog);
|
||||
if (!value)
|
||||
return;
|
||||
|
||||
if (call_hid_bpf_prog_release(prog, *value))
|
||||
bpf_map_delete_elem(&progs_map, &prog);
|
||||
}
|
||||
|
||||
SEC("fexit/bpf_prog_release")
|
||||
int BPF_PROG(hid_prog_release, struct inode *inode, struct file *filp)
|
||||
{
|
||||
u64 prog = (u64)filp->private_data;
|
||||
|
||||
release_prog(prog);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("fexit/bpf_free_inode")
|
||||
int BPF_PROG(hid_free_inode, struct inode *inode)
|
||||
{
|
||||
u64 prog = (u64)inode->i_private;
|
||||
|
||||
release_prog(prog);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char LICENSE[] SEC("license") = "GPL";
|
||||
648
drivers/hid/bpf/entrypoints/entrypoints.lskel.h
Normal file
648
drivers/hid/bpf/entrypoints/entrypoints.lskel.h
Normal file
File diff suppressed because it is too large
Load Diff
223
drivers/hid/bpf/hid_bpf_dispatch.c
Normal file
223
drivers/hid/bpf/hid_bpf_dispatch.c
Normal file
@@ -0,0 +1,223 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
/*
|
||||
* HID-BPF support for Linux
|
||||
*
|
||||
* Copyright (c) 2022 Benjamin Tissoires
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/btf.h>
|
||||
#include <linux/btf_ids.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/hid_bpf.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include "hid_bpf_dispatch.h"
|
||||
#include "entrypoints/entrypoints.lskel.h"
|
||||
|
||||
struct hid_bpf_ops *hid_bpf_ops;
|
||||
EXPORT_SYMBOL(hid_bpf_ops);
|
||||
|
||||
/**
|
||||
* hid_bpf_device_event - Called whenever an event is coming in from the device
|
||||
*
|
||||
* @ctx: The HID-BPF context
|
||||
*
|
||||
* @return %0 on success and keep processing; a negative error code to interrupt
|
||||
* the processing of this event
|
||||
*
|
||||
* Declare an %fmod_ret tracing bpf program to this function and attach this
|
||||
* program through hid_bpf_attach_prog() to have this helper called for
|
||||
* any incoming event from the device itself.
|
||||
*
|
||||
* The function is called while on IRQ context, so we can not sleep.
|
||||
*/
|
||||
/* never used by the kernel but declared so we can load and attach a tracepoint */
|
||||
__weak noinline int hid_bpf_device_event(struct hid_bpf_ctx *ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ALLOW_ERROR_INJECTION(hid_bpf_device_event, ERRNO);
|
||||
|
||||
int
|
||||
dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type, u8 *data,
|
||||
u32 size, int interrupt)
|
||||
{
|
||||
struct hid_bpf_ctx_kern ctx_kern = {
|
||||
.ctx = {
|
||||
.hid = hdev,
|
||||
.report_type = type,
|
||||
.size = size,
|
||||
},
|
||||
.data = data,
|
||||
};
|
||||
|
||||
if (type >= HID_REPORT_TYPES)
|
||||
return -EINVAL;
|
||||
|
||||
return hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_DEVICE_EVENT, &ctx_kern);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event);
|
||||
|
||||
/**
|
||||
* hid_bpf_get_data - Get the kernel memory pointer associated with the context @ctx
|
||||
*
|
||||
* @ctx: The HID-BPF context
|
||||
* @offset: The offset within the memory
|
||||
* @rdwr_buf_size: the const size of the buffer
|
||||
*
|
||||
* @returns %NULL on error, an %__u8 memory pointer on success
|
||||
*/
|
||||
noinline __u8 *
|
||||
hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t rdwr_buf_size)
|
||||
{
|
||||
struct hid_bpf_ctx_kern *ctx_kern;
|
||||
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
|
||||
ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);
|
||||
|
||||
if (rdwr_buf_size + offset > ctx->size)
|
||||
return NULL;
|
||||
|
||||
return ctx_kern->data + offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following set contains all functions we agree BPF programs
|
||||
* can use.
|
||||
*/
|
||||
BTF_SET8_START(hid_bpf_kfunc_ids)
|
||||
BTF_ID_FLAGS(func, call_hid_bpf_prog_release)
|
||||
BTF_ID_FLAGS(func, hid_bpf_get_data, KF_RET_NULL)
|
||||
BTF_SET8_END(hid_bpf_kfunc_ids)
|
||||
|
||||
static const struct btf_kfunc_id_set hid_bpf_kfunc_set = {
|
||||
.owner = THIS_MODULE,
|
||||
.set = &hid_bpf_kfunc_ids,
|
||||
};
|
||||
|
||||
static int device_match_id(struct device *dev, const void *id)
|
||||
{
|
||||
struct hid_device *hdev = to_hid_device(dev);
|
||||
|
||||
return hdev->id == *(int *)id;
|
||||
}
|
||||
|
||||
/**
|
||||
* hid_bpf_attach_prog - Attach the given @prog_fd to the given HID device
|
||||
*
|
||||
* @hid_id: the system unique identifier of the HID device
|
||||
* @prog_fd: an fd in the user process representing the program to attach
|
||||
* @flags: any logical OR combination of &enum hid_bpf_attach_flags
|
||||
*
|
||||
* @returns %0 on success, an error code otherwise.
|
||||
*/
|
||||
/* called from syscall */
|
||||
noinline int
|
||||
hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags)
|
||||
{
|
||||
struct hid_device *hdev;
|
||||
struct device *dev;
|
||||
int prog_type = hid_bpf_get_prog_attach_type(prog_fd);
|
||||
|
||||
if (!hid_bpf_ops)
|
||||
return -EINVAL;
|
||||
|
||||
if (prog_type < 0)
|
||||
return prog_type;
|
||||
|
||||
if (prog_type >= HID_BPF_PROG_TYPE_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if ((flags & ~HID_BPF_FLAG_MASK))
|
||||
return -EINVAL;
|
||||
|
||||
dev = bus_find_device(hid_bpf_ops->bus_type, NULL, &hid_id, device_match_id);
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
hdev = to_hid_device(dev);
|
||||
|
||||
return __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags);
|
||||
}
|
||||
|
||||
/* for syscall HID-BPF */
|
||||
BTF_SET8_START(hid_bpf_syscall_kfunc_ids)
|
||||
BTF_ID_FLAGS(func, hid_bpf_attach_prog)
|
||||
BTF_SET8_END(hid_bpf_syscall_kfunc_ids)
|
||||
|
||||
static const struct btf_kfunc_id_set hid_bpf_syscall_kfunc_set = {
|
||||
.owner = THIS_MODULE,
|
||||
.set = &hid_bpf_syscall_kfunc_ids,
|
||||
};
|
||||
|
||||
void hid_bpf_destroy_device(struct hid_device *hdev)
|
||||
{
|
||||
if (!hdev)
|
||||
return;
|
||||
|
||||
/* mark the device as destroyed in bpf so we don't reattach it */
|
||||
hdev->bpf.destroyed = true;
|
||||
|
||||
__hid_bpf_destroy_device(hdev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_bpf_destroy_device);
|
||||
|
||||
void hid_bpf_device_init(struct hid_device *hdev)
|
||||
{
|
||||
spin_lock_init(&hdev->bpf.progs_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_bpf_device_init);
|
||||
|
||||
static int __init hid_bpf_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Note: if we exit with an error any time here, we would entirely break HID, which
|
||||
* is probably not something we want. So we log an error and return success.
|
||||
*
|
||||
* This is not a big deal: the syscall allowing to attach a BPF program to a HID device
|
||||
* will not be available, so nobody will be able to use the functionality.
|
||||
*/
|
||||
|
||||
err = register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &hid_bpf_kfunc_set);
|
||||
if (err) {
|
||||
pr_warn("error while setting HID BPF tracing kfuncs: %d", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = hid_bpf_preload_skel();
|
||||
if (err) {
|
||||
pr_warn("error while preloading HID BPF dispatcher: %d", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* register syscalls after we are sure we can load our preloaded bpf program */
|
||||
err = register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &hid_bpf_syscall_kfunc_set);
|
||||
if (err) {
|
||||
pr_warn("error while setting HID BPF syscall kfuncs: %d", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit hid_bpf_exit(void)
|
||||
{
|
||||
/* HID depends on us, so if we hit that code, we are guaranteed that hid
|
||||
* has been removed and thus we do not need to clear the HID devices
|
||||
*/
|
||||
hid_bpf_free_links_and_skel();
|
||||
}
|
||||
|
||||
late_initcall(hid_bpf_init);
|
||||
module_exit(hid_bpf_exit);
|
||||
MODULE_AUTHOR("Benjamin Tissoires");
|
||||
MODULE_LICENSE("GPL");
|
||||
27
drivers/hid/bpf/hid_bpf_dispatch.h
Normal file
27
drivers/hid/bpf/hid_bpf_dispatch.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef _BPF_HID_BPF_DISPATCH_H
|
||||
#define _BPF_HID_BPF_DISPATCH_H
|
||||
|
||||
#include <linux/hid.h>
|
||||
|
||||
struct hid_bpf_ctx_kern {
|
||||
struct hid_bpf_ctx ctx;
|
||||
u8 *data;
|
||||
};
|
||||
|
||||
int hid_bpf_preload_skel(void);
|
||||
void hid_bpf_free_links_and_skel(void);
|
||||
int hid_bpf_get_prog_attach_type(int prog_fd);
|
||||
int __hid_bpf_attach_prog(struct hid_device *hdev, enum hid_bpf_prog_type prog_type, int prog_fd,
|
||||
__u32 flags);
|
||||
void __hid_bpf_destroy_device(struct hid_device *hdev);
|
||||
int hid_bpf_prog_run(struct hid_device *hdev, enum hid_bpf_prog_type type,
|
||||
struct hid_bpf_ctx_kern *ctx_kern);
|
||||
|
||||
struct bpf_prog;
|
||||
|
||||
/* HID-BPF internal kfuncs API */
|
||||
bool call_hid_bpf_prog_release(u64 prog, int table_cnt);
|
||||
|
||||
#endif
|
||||
568
drivers/hid/bpf/hid_bpf_jmp_table.c
Normal file
568
drivers/hid/bpf/hid_bpf_jmp_table.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2040,6 +2040,10 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data
|
||||
report_enum = hid->report_enum + type;
|
||||
hdrv = hid->driver;
|
||||
|
||||
ret = dispatch_hid_bpf_device_event(hid, type, data, size, interrupt);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
if (!size) {
|
||||
dbg_hid("empty report\n");
|
||||
ret = -1;
|
||||
@@ -2790,6 +2794,8 @@ struct hid_device *hid_allocate_device(void)
|
||||
sema_init(&hdev->driver_input_lock, 1);
|
||||
mutex_init(&hdev->ll_open_lock);
|
||||
|
||||
hid_bpf_device_init(hdev);
|
||||
|
||||
return hdev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_allocate_device);
|
||||
@@ -2816,6 +2822,7 @@ static void hid_remove_device(struct hid_device *hdev)
|
||||
*/
|
||||
void hid_destroy_device(struct hid_device *hdev)
|
||||
{
|
||||
hid_bpf_destroy_device(hdev);
|
||||
hid_remove_device(hdev);
|
||||
put_device(&hdev->dev);
|
||||
}
|
||||
@@ -2902,6 +2909,13 @@ int hid_check_keys_pressed(struct hid_device *hid)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_check_keys_pressed);
|
||||
|
||||
#ifdef CONFIG_HID_BPF
|
||||
static struct hid_bpf_ops hid_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.bus_type = &hid_bus_type,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int __init hid_init(void)
|
||||
{
|
||||
int ret;
|
||||
@@ -2916,6 +2930,10 @@ static int __init hid_init(void)
|
||||
goto err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HID_BPF
|
||||
hid_bpf_ops = &hid_ops;
|
||||
#endif
|
||||
|
||||
ret = hidraw_init();
|
||||
if (ret)
|
||||
goto err_bus;
|
||||
@@ -2931,6 +2949,9 @@ err:
|
||||
|
||||
static void __exit hid_exit(void)
|
||||
{
|
||||
#ifdef CONFIG_HID_BPF
|
||||
hid_bpf_ops = NULL;
|
||||
#endif
|
||||
hid_debug_exit();
|
||||
hidraw_exit();
|
||||
bus_unregister(&hid_bus_type);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <uapi/linux/hid.h>
|
||||
#include <linux/hid_bpf.h>
|
||||
|
||||
/*
|
||||
* We parse each description item into this structure. Short items data
|
||||
@@ -651,6 +652,10 @@ struct hid_device { /* device report descriptor */
|
||||
wait_queue_head_t debug_wait;
|
||||
|
||||
unsigned int id; /* system unique id */
|
||||
|
||||
#ifdef CONFIG_BPF
|
||||
struct hid_bpf bpf; /* hid-bpf data */
|
||||
#endif /* CONFIG_BPF */
|
||||
};
|
||||
|
||||
#define to_hid_device(pdev) \
|
||||
|
||||
117
include/linux/hid_bpf.h
Normal file
117
include/linux/hid_bpf.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
|
||||
#ifndef __HID_BPF_H
|
||||
#define __HID_BPF_H
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <uapi/linux/hid.h>
|
||||
|
||||
struct hid_device;
|
||||
|
||||
/*
|
||||
* The following is the user facing HID BPF API.
|
||||
*
|
||||
* Extra care should be taken when editing this part, as
|
||||
* it might break existing out of the tree bpf programs.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 ``hid_bpf_get_data()`` in order to get a pointer to that field.
|
||||
*
|
||||
* All of these fields are currently read-only.
|
||||
*
|
||||
* @index: program index in the jump table. No special meaning (a smaller index
|
||||
* doesn't mean the program will be executed before another program with
|
||||
* a bigger index).
|
||||
* @hid: the ``struct hid_device`` representing the device itself
|
||||
* @report_type: used for ``hid_bpf_device_event()``
|
||||
* @size: Valid data in the data field.
|
||||
*
|
||||
* Programs can get the available valid size in data by fetching this field.
|
||||
*/
|
||||
struct hid_bpf_ctx {
|
||||
__u32 index;
|
||||
const struct hid_device *hid;
|
||||
enum hid_report_type report_type;
|
||||
__s32 size;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum hid_bpf_attach_flags - flags used when attaching a HIF-BPF program
|
||||
*
|
||||
* @HID_BPF_FLAG_NONE: no specific flag is used, the kernel choses where to
|
||||
* insert the program
|
||||
* @HID_BPF_FLAG_INSERT_HEAD: insert the given program before any other program
|
||||
* currently attached to the device. This doesn't
|
||||
* guarantee that this program will always be first
|
||||
* @HID_BPF_FLAG_MAX: sentinel value, not to be used by the callers
|
||||
*/
|
||||
enum hid_bpf_attach_flags {
|
||||
HID_BPF_FLAG_NONE = 0,
|
||||
HID_BPF_FLAG_INSERT_HEAD = _BITUL(0),
|
||||
HID_BPF_FLAG_MAX,
|
||||
};
|
||||
|
||||
/* Following functions are tracepoints that BPF programs can attach to */
|
||||
int hid_bpf_device_event(struct hid_bpf_ctx *ctx);
|
||||
|
||||
/* Following functions are kfunc that we export to BPF programs */
|
||||
/* only available in tracing */
|
||||
__u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t __sz);
|
||||
|
||||
/* only available in syscall */
|
||||
int hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags);
|
||||
|
||||
/*
|
||||
* Below is HID internal
|
||||
*/
|
||||
|
||||
/* internal function to call eBPF programs, not to be used by anybody */
|
||||
int __hid_bpf_tail_call(struct hid_bpf_ctx *ctx);
|
||||
|
||||
#define HID_BPF_MAX_PROGS_PER_DEV 64
|
||||
#define HID_BPF_FLAG_MASK (((HID_BPF_FLAG_MAX - 1) << 1) - 1)
|
||||
|
||||
/* types of HID programs to attach to */
|
||||
enum hid_bpf_prog_type {
|
||||
HID_BPF_PROG_TYPE_UNDEF = -1,
|
||||
HID_BPF_PROG_TYPE_DEVICE_EVENT, /* an event is emitted from the device */
|
||||
HID_BPF_PROG_TYPE_MAX,
|
||||
};
|
||||
|
||||
struct hid_bpf_ops {
|
||||
struct module *owner;
|
||||
struct bus_type *bus_type;
|
||||
};
|
||||
|
||||
extern struct hid_bpf_ops *hid_bpf_ops;
|
||||
|
||||
struct hid_bpf_prog_list {
|
||||
u16 prog_idx[HID_BPF_MAX_PROGS_PER_DEV];
|
||||
u8 prog_cnt;
|
||||
};
|
||||
|
||||
/* stored in each device */
|
||||
struct hid_bpf {
|
||||
struct hid_bpf_prog_list __rcu *progs[HID_BPF_PROG_TYPE_MAX]; /* attached BPF progs */
|
||||
bool destroyed; /* prevents the assignment of any progs */
|
||||
|
||||
spinlock_t progs_lock; /* protects RCU update of progs */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_HID_BPF
|
||||
int dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data,
|
||||
u32 size, int interrupt);
|
||||
void hid_bpf_destroy_device(struct hid_device *hid);
|
||||
void hid_bpf_device_init(struct hid_device *hid);
|
||||
#else /* CONFIG_HID_BPF */
|
||||
static inline int dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type,
|
||||
u8 *data, u32 size, int interrupt) { return 0; }
|
||||
static inline void hid_bpf_destroy_device(struct hid_device *hid) {}
|
||||
static inline void hid_bpf_device_init(struct hid_device *hid) {}
|
||||
#endif /* CONFIG_HID_BPF */
|
||||
|
||||
#endif /* __HID_BPF_H */
|
||||
Reference in New Issue
Block a user