mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-06 05:06:29 +00:00
8b7fd6a15f
Currently, hid_bpf_rdesc_fixup() is called once the match between the HID device and the driver is done. This can be problematic in case the driver selected by the kernel would change the report descriptor after the fact. To give a chance for hid_bpf_rdesc_fixup() to provide hints on to how to select a dedicated driver or not, move the call to that BPF hook earlier in the .probe() process, when we get the first match. However, this means that we might get called more than once (typically once for hid-generic, and once for hid-vendor-specific). So we store the result of HID-BPF fixup in struct hid_device. Basically, this means that ->bpf_rdesc can replace ->dev_rdesc when it was used in the code. In order to not grow struct hid_device, some fields are re-ordered. This was the output of pahole for the first 128 bytes: struct hid_device { __u8 * dev_rdesc; /* 0 8 */ unsigned int dev_rsize; /* 8 4 */ /* XXX 4 bytes hole, try to pack */ __u8 * rdesc; /* 16 8 */ unsigned int rsize; /* 24 4 */ /* XXX 4 bytes hole, try to pack */ struct hid_collection * collection; /* 32 8 */ unsigned int collection_size; /* 40 4 */ unsigned int maxcollection; /* 44 4 */ unsigned int maxapplication; /* 48 4 */ __u16 bus; /* 52 2 */ __u16 group; /* 54 2 */ __u32 vendor; /* 56 4 */ __u32 product; /* 60 4 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 version; /* 64 4 */ enum hid_type type; /* 68 4 */ unsigned int country; /* 72 4 */ /* XXX 4 bytes hole, try to pack */ struct hid_report_enum report_enum[3]; /* 80 6216 */ Basically, we got three holes of 4 bytes. We can reorder things a little and makes those 3 holes a continuous 12 bytes hole, which can be replaced by the new pointer and the new unsigned int we need. In terms of code allocation, when not using HID-BPF, we are back to kernel v6.2 in hid_open_report(). These multiple kmemdup() calls will be fixed in a later commit. Link: https://patch.msgid.link/20241001-hid-bpf-hid-generic-v3-1-2ef1019468df@kernel.org Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
237 lines
8.2 KiB
C
237 lines
8.2 KiB
C
/* SPDX-License-Identifier: GPL-2.0+ */
|
|
|
|
#ifndef __HID_BPF_H
|
|
#define __HID_BPF_H
|
|
|
|
#include <linux/bpf.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/srcu.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.
|
|
*
|
|
* @hid: the &struct hid_device representing the device itself
|
|
* @allocated_size: Allocated size of data.
|
|
*
|
|
* 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.
|
|
*
|
|
* Programs can get the available valid size in data by fetching this field.
|
|
* Programs can also change this value by returning a positive number in the
|
|
* program.
|
|
* To discard the event, return a negative error code.
|
|
*
|
|
* ``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 previous program.
|
|
*
|
|
* ``hid`` and ``allocated_size`` are read-only, ``size`` and ``retval`` are read-write.
|
|
*/
|
|
struct hid_bpf_ctx {
|
|
struct hid_device *hid;
|
|
__u32 allocated_size;
|
|
union {
|
|
__s32 retval;
|
|
__s32 size;
|
|
};
|
|
};
|
|
|
|
/*
|
|
* Below is HID internal
|
|
*/
|
|
|
|
#define HID_BPF_MAX_PROGS_PER_DEV 64
|
|
#define HID_BPF_FLAG_MASK (((HID_BPF_FLAG_MAX - 1) << 1) - 1)
|
|
|
|
|
|
struct hid_report_enum;
|
|
|
|
struct hid_ops {
|
|
struct hid_report *(*hid_get_report)(struct hid_report_enum *report_enum, const u8 *data);
|
|
int (*hid_hw_raw_request)(struct hid_device *hdev,
|
|
unsigned char reportnum, __u8 *buf,
|
|
size_t len, enum hid_report_type rtype,
|
|
enum hid_class_request reqtype,
|
|
u64 source, bool from_bpf);
|
|
int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len,
|
|
u64 source, bool from_bpf);
|
|
int (*hid_input_report)(struct hid_device *hid, enum hid_report_type type,
|
|
u8 *data, u32 size, int interrupt, u64 source, bool from_bpf,
|
|
bool lock_already_taken);
|
|
struct module *owner;
|
|
const struct bus_type *bus_type;
|
|
};
|
|
|
|
extern struct hid_ops *hid_ops;
|
|
|
|
/**
|
|
* struct hid_bpf_ops - A BPF struct_ops of callbacks allowing to attach HID-BPF
|
|
* programs to a HID device
|
|
* @hid_id: the HID uniq ID to attach to. This is writeable before ``load()``, and
|
|
* cannot be changed after
|
|
* @flags: flags used while attaching the struct_ops to the device. Currently only
|
|
* available value is %0 or ``BPF_F_BEFORE``.
|
|
* Writeable only before ``load()``
|
|
*/
|
|
struct hid_bpf_ops {
|
|
/* hid_id needs to stay first so we can easily change it
|
|
* from userspace.
|
|
*/
|
|
int hid_id;
|
|
u32 flags;
|
|
|
|
/* private: do not show up in the docs */
|
|
struct list_head list;
|
|
|
|
/* public: rest should show up in the docs */
|
|
|
|
/**
|
|
* @hid_device_event: called whenever an event is coming in from the device
|
|
*
|
|
* It has the following arguments:
|
|
*
|
|
* ``ctx``: The HID-BPF context as &struct hid_bpf_ctx
|
|
*
|
|
* Return: %0 on success and keep processing; a positive
|
|
* value to change the incoming size buffer; a negative
|
|
* error code to interrupt the processing of this event
|
|
*
|
|
* Context: Interrupt context.
|
|
*/
|
|
int (*hid_device_event)(struct hid_bpf_ctx *ctx, enum hid_report_type report_type,
|
|
u64 source);
|
|
|
|
/**
|
|
* @hid_rdesc_fixup: called when the probe function parses the report descriptor
|
|
* of the HID device
|
|
*
|
|
* It has the following arguments:
|
|
*
|
|
* ``ctx``: The HID-BPF context as &struct hid_bpf_ctx
|
|
*
|
|
* Return: %0 on success and keep processing; a positive
|
|
* value to change the incoming size buffer; a negative
|
|
* error code to interrupt the processing of this device
|
|
*/
|
|
int (*hid_rdesc_fixup)(struct hid_bpf_ctx *ctx);
|
|
|
|
/**
|
|
* @hid_hw_request: called whenever a hid_hw_raw_request() call is emitted
|
|
* on the HID device
|
|
*
|
|
* It has the following arguments:
|
|
*
|
|
* ``ctx``: The HID-BPF context as &struct hid_bpf_ctx
|
|
*
|
|
* ``reportnum``: the report number, as in hid_hw_raw_request()
|
|
*
|
|
* ``rtype``: the report type (``HID_INPUT_REPORT``, ``HID_FEATURE_REPORT``,
|
|
* ``HID_OUTPUT_REPORT``)
|
|
*
|
|
* ``reqtype``: the request
|
|
*
|
|
* ``source``: a u64 referring to a uniq but identifiable source. If %0, the
|
|
* kernel itself emitted that call. For hidraw, ``source`` is set
|
|
* to the associated ``struct file *``.
|
|
*
|
|
* Return: %0 to keep processing the request by hid-core; any other value
|
|
* stops hid-core from processing that event. A positive value should be
|
|
* returned with the number of bytes returned in the incoming buffer; a
|
|
* negative error code interrupts the processing of this call.
|
|
*/
|
|
int (*hid_hw_request)(struct hid_bpf_ctx *ctx, unsigned char reportnum,
|
|
enum hid_report_type rtype, enum hid_class_request reqtype,
|
|
u64 source);
|
|
|
|
/**
|
|
* @hid_hw_output_report: called whenever a hid_hw_output_report() call is emitted
|
|
* on the HID device
|
|
*
|
|
* It has the following arguments:
|
|
*
|
|
* ``ctx``: The HID-BPF context as &struct hid_bpf_ctx
|
|
*
|
|
* ``source``: a u64 referring to a uniq but identifiable source. If %0, the
|
|
* kernel itself emitted that call. For hidraw, ``source`` is set
|
|
* to the associated ``struct file *``.
|
|
*
|
|
* Return: %0 to keep processing the request by hid-core; any other value
|
|
* stops hid-core from processing that event. A positive value should be
|
|
* returned with the number of bytes written to the device; a negative error
|
|
* code interrupts the processing of this call.
|
|
*/
|
|
int (*hid_hw_output_report)(struct hid_bpf_ctx *ctx, u64 source);
|
|
|
|
|
|
/* private: do not show up in the docs */
|
|
struct hid_device *hdev;
|
|
};
|
|
|
|
/* stored in each device */
|
|
struct hid_bpf {
|
|
u8 *device_data; /* allocated when a bpf program of type
|
|
* SEC(f.../hid_bpf_device_event) has been attached
|
|
* to this HID device
|
|
*/
|
|
u32 allocated_data;
|
|
bool destroyed; /* prevents the assignment of any progs */
|
|
|
|
struct hid_bpf_ops *rdesc_ops;
|
|
struct list_head prog_list;
|
|
struct mutex prog_list_lock; /* protects prog_list update */
|
|
struct srcu_struct srcu; /* protects prog_list read-only access */
|
|
};
|
|
|
|
#ifdef CONFIG_HID_BPF
|
|
u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data,
|
|
u32 *size, int interrupt, u64 source, bool from_bpf);
|
|
int dispatch_hid_bpf_raw_requests(struct hid_device *hdev,
|
|
unsigned char reportnum, __u8 *buf,
|
|
u32 size, enum hid_report_type rtype,
|
|
enum hid_class_request reqtype,
|
|
u64 source, bool from_bpf);
|
|
int dispatch_hid_bpf_output_report(struct hid_device *hdev, __u8 *buf, u32 size,
|
|
u64 source, bool from_bpf);
|
|
int hid_bpf_connect_device(struct hid_device *hdev);
|
|
void hid_bpf_disconnect_device(struct hid_device *hdev);
|
|
void hid_bpf_destroy_device(struct hid_device *hid);
|
|
int hid_bpf_device_init(struct hid_device *hid);
|
|
const u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size);
|
|
#else /* CONFIG_HID_BPF */
|
|
static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type,
|
|
u8 *data, u32 *size, int interrupt,
|
|
u64 source, bool from_bpf) { return data; }
|
|
static inline int dispatch_hid_bpf_raw_requests(struct hid_device *hdev,
|
|
unsigned char reportnum, u8 *buf,
|
|
u32 size, enum hid_report_type rtype,
|
|
enum hid_class_request reqtype,
|
|
u64 source, bool from_bpf) { return 0; }
|
|
static inline int dispatch_hid_bpf_output_report(struct hid_device *hdev, __u8 *buf, u32 size,
|
|
u64 source, bool from_bpf) { return 0; }
|
|
static inline int hid_bpf_connect_device(struct hid_device *hdev) { return 0; }
|
|
static inline void hid_bpf_disconnect_device(struct hid_device *hdev) {}
|
|
static inline void hid_bpf_destroy_device(struct hid_device *hid) {}
|
|
static inline int hid_bpf_device_init(struct hid_device *hid) { return 0; }
|
|
static inline const u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc,
|
|
unsigned int *size) { return rdesc; }
|
|
|
|
#endif /* CONFIG_HID_BPF */
|
|
|
|
#endif /* __HID_BPF_H */
|