mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 21:51:40 +00:00
HID: bpf: introduce hid_hw_request()
This function can not be called under IRQ, thus it is only available while in SEC("syscall"). For consistency, this function requires a HID-BPF context to work with, and so we also provide a helper to create one based on the HID unique ID. Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> -- changes in v12: - variable dereferenced before check 'ctx' |Reported-by: kernel test robot <lkp@intel.com> |Reported-by: Dan Carpenter <error27@gmail.com> no changes in v11 no changes in v10 changes in v9: - fixed kfunc declaration aaccording to latest upstream changes no changes in v8 changes in v7: - hid_bpf_allocate_context: remove unused variable - ensures buf is not NULL changes in v6: - rename parameter size into buf__sz to teach the verifier about the actual buffer size used by the call - remove the allocated data in the user created context, it's not used new-ish in v5 Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
parent
0330f725cc
commit
91a7f802d1
@ -220,9 +220,143 @@ hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags)
|
||||
return __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* hid_bpf_allocate_context - Allocate a context to the given HID device
|
||||
*
|
||||
* @hid_id: the system unique identifier of the HID device
|
||||
*
|
||||
* @returns A pointer to &struct hid_bpf_ctx on success, %NULL on error.
|
||||
*/
|
||||
noinline struct hid_bpf_ctx *
|
||||
hid_bpf_allocate_context(unsigned int hid_id)
|
||||
{
|
||||
struct hid_device *hdev;
|
||||
struct hid_bpf_ctx_kern *ctx_kern = NULL;
|
||||
struct device *dev;
|
||||
|
||||
if (!hid_bpf_ops)
|
||||
return NULL;
|
||||
|
||||
dev = bus_find_device(hid_bpf_ops->bus_type, NULL, &hid_id, device_match_id);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
hdev = to_hid_device(dev);
|
||||
|
||||
ctx_kern = kzalloc(sizeof(*ctx_kern), GFP_KERNEL);
|
||||
if (!ctx_kern)
|
||||
return NULL;
|
||||
|
||||
ctx_kern->ctx.hid = hdev;
|
||||
|
||||
return &ctx_kern->ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* hid_bpf_release_context - Release the previously allocated context @ctx
|
||||
*
|
||||
* @ctx: the HID-BPF context to release
|
||||
*
|
||||
*/
|
||||
noinline void
|
||||
hid_bpf_release_context(struct hid_bpf_ctx *ctx)
|
||||
{
|
||||
struct hid_bpf_ctx_kern *ctx_kern;
|
||||
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);
|
||||
|
||||
kfree(ctx_kern);
|
||||
}
|
||||
|
||||
/**
|
||||
* hid_bpf_hw_request - Communicate with a HID device
|
||||
*
|
||||
* @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context()
|
||||
* @buf: a %PTR_TO_MEM buffer
|
||||
* @buf__sz: the size of the data to transfer
|
||||
* @rtype: the type of the report (%HID_INPUT_REPORT, %HID_FEATURE_REPORT, %HID_OUTPUT_REPORT)
|
||||
* @reqtype: the type of the request (%HID_REQ_GET_REPORT, %HID_REQ_SET_REPORT, ...)
|
||||
*
|
||||
* @returns %0 on success, a negative error code otherwise.
|
||||
*/
|
||||
noinline int
|
||||
hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz,
|
||||
enum hid_report_type rtype, enum hid_class_request reqtype)
|
||||
{
|
||||
struct hid_device *hdev;
|
||||
struct hid_report *report;
|
||||
struct hid_report_enum *report_enum;
|
||||
u8 *dma_data;
|
||||
u32 report_len;
|
||||
int ret;
|
||||
|
||||
/* check arguments */
|
||||
if (!ctx || !hid_bpf_ops || !buf)
|
||||
return -EINVAL;
|
||||
|
||||
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 (buf__sz < 1)
|
||||
return -EINVAL;
|
||||
|
||||
hdev = (struct hid_device *)ctx->hid; /* discard const */
|
||||
|
||||
report_enum = hdev->report_enum + rtype;
|
||||
report = hid_bpf_ops->hid_get_report(report_enum, buf);
|
||||
if (!report)
|
||||
return -EINVAL;
|
||||
|
||||
report_len = hid_report_len(report);
|
||||
|
||||
if (buf__sz > report_len)
|
||||
buf__sz = report_len;
|
||||
|
||||
dma_data = kmemdup(buf, buf__sz, GFP_KERNEL);
|
||||
if (!dma_data)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = hid_bpf_ops->hid_hw_raw_request(hdev,
|
||||
dma_data[0],
|
||||
dma_data,
|
||||
buf__sz,
|
||||
rtype,
|
||||
reqtype);
|
||||
|
||||
if (ret > 0)
|
||||
memcpy(buf, dma_data, ret);
|
||||
|
||||
kfree(dma_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* for syscall HID-BPF */
|
||||
BTF_SET8_START(hid_bpf_syscall_kfunc_ids)
|
||||
BTF_ID_FLAGS(func, hid_bpf_attach_prog)
|
||||
BTF_ID_FLAGS(func, hid_bpf_allocate_context, KF_ACQUIRE | KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE)
|
||||
BTF_ID_FLAGS(func, hid_bpf_hw_request)
|
||||
BTF_SET8_END(hid_bpf_syscall_kfunc_ids)
|
||||
|
||||
static const struct btf_kfunc_id_set hid_bpf_syscall_kfunc_set = {
|
||||
|
@ -2919,6 +2919,8 @@ EXPORT_SYMBOL_GPL(hid_check_keys_pressed);
|
||||
|
||||
#ifdef CONFIG_HID_BPF
|
||||
static struct hid_bpf_ops hid_ops = {
|
||||
.hid_get_report = hid_get_report,
|
||||
.hid_hw_raw_request = hid_hw_raw_request,
|
||||
.owner = THIS_MODULE,
|
||||
.bus_type = &hid_bus_type,
|
||||
};
|
||||
|
@ -76,11 +76,15 @@ enum hid_bpf_attach_flags {
|
||||
int hid_bpf_device_event(struct hid_bpf_ctx *ctx);
|
||||
|
||||
/* Following functions are kfunc that we export to BPF programs */
|
||||
/* only available in tracing */
|
||||
/* available everywhere in HID-BPF */
|
||||
__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);
|
||||
int hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz,
|
||||
enum hid_report_type rtype, enum hid_class_request reqtype);
|
||||
struct hid_bpf_ctx *hid_bpf_allocate_context(unsigned int hid_id);
|
||||
void hid_bpf_release_context(struct hid_bpf_ctx *ctx);
|
||||
|
||||
/*
|
||||
* Below is HID internal
|
||||
@ -99,7 +103,14 @@ enum hid_bpf_prog_type {
|
||||
HID_BPF_PROG_TYPE_MAX,
|
||||
};
|
||||
|
||||
struct hid_report_enum;
|
||||
|
||||
struct hid_bpf_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);
|
||||
struct module *owner;
|
||||
struct bus_type *bus_type;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user