linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC hid v1 00/10] HID-BPF: add support for in-tree BPF programs
@ 2022-11-24 15:15 Benjamin Tissoires
  2022-11-24 15:15 ` [RFC hid v1 01/10] bpftool: generate json output of skeletons Benjamin Tissoires
                   ` (9 more replies)
  0 siblings, 10 replies; 14+ messages in thread
From: Benjamin Tissoires @ 2022-11-24 15:15 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Dmitry Torokhov
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf, Benjamin Tissoires

Hi all,

sending this as an RFC because it's not complete, but I'd like to start
the discussion:

While presenting HID-BPF, I always mentioned that device fixes should be
integrated in the kernel. And I am trying to do that in this series.

Load a generic bpf from the kernel:
===================================

The first step is to be able to load that bpf program without knowing
its interface. So I studied the output of the light skeletons and
squized them into a simple C array. Then I wrote a BPF loader based on
that same skeleton, and now I can iterate over an array of BPF programs
and load the ones that match the device.

The step 0 in that translation was to generate a json instead of a
proper C header for the light skeleton. The idea is that I can then
transform that json into whatever I want, without having to mess up with
bpftool. IIRC this was briefly discussed at plumbers, so I hope this is
not too weird.

Pin the program to the bpffs:
=============================

AFAICT, the infrastructure is not completely ready to pin programs from
the kernel itself (outside of bpf_preload).

I encountered a few hiccups and I'd like to know if I am on the correct
path:
- to be able to pin to the bpffs, it first needs to be mounted by
  userspace. Should I add some sort of list of already available
  programs that would be picked up by the kernel when bpffs is mounted?

- I am not sure the way I added the pinned program is correct: I am
  reusing the skeleton of bpf_iter_link_pin_kernel(), but using
  kernel_path_create() in the same way bpf_obj_pin_user() does seems
  better, though I always get -ENOENT even with bpffs mounted.

- I also need to be able to add a hierarchy of directories in bpffs from
  the kernel, and this requires some more code digging... :)

Produce the actual "compiled" bpf program:
==========================================

The current code here relies on the user to run `make` in the
drivers/hid/bpf/progs directory to regenerate the files.
Leaving aside the fact that I need to check on how to make this step
integrated by the generic root-level make, I wonder if using python to
generate that file is OK.

I am not very happy to add a requirement to build the whole kernel, but
OTOH, writing the same tool in C is desperately annoying. I would rather
have a tool written in rust TBH, if rust is now part of the required
toolchain.

Ship the "compiled" bpf programs:
=================================

In this version, the bpf program is embedded in vmlinux, for no other
reasons that splitting that out in a module would require some more
effort before submitting that RFC (and subject to concurrency races when
a device has several interfaces at once).

However, I wonder what should be the final "product":

- In a first pass, I can keep the current form and have a dedicated
  kernel module that contains all of the bpf fixes. The kernel would
  load it, check for any match, pin the programs, and unload this kernel
  module.

  This works but isn't very modular as we just enable/ship all of the
  fixes or nothing.

- another idea I had, was to rely on the firmware kernel interface. Now
  that I have a simple "bpf module" format (or even the json file), I
  could "compile" it into a binary and then have the kernel request the
  firmware on a device plug. This way we don't load/unload a module at
  each plug, and we rely on the existing firmware capabilities.

  I really like this idea, but then I wonder how I can ship those
  firmwares. I'd like them to be tied to the currently running kernel,
  so should I namespace them in the firmware directory on install? Is
  there any other way to be able to have 2 or more firmwares depending
  on the kernel version?

I think that's it. Again, this series is just a PoC on top of
hid.git/for-6.2/hid-bpf, and I can change everything if I am not headed
to the correct direction.

Cheers,
Benjamin

Benjamin Tissoires (10):
  bpftool: generate json output of skeletons
  WIP: bpf: allow to pin programs from the kernel when bpffs is mounted
  HID: add a tool to convert a bpf source into a generic bpf loader
  HID: add the bpf loader that can attach a generic hid-bpf program
  HID: add report descriptor override for the X-Keys XK24
  selftests: hid: add vmtest.sh
  selftests: hid: Add a variant parameter so we can emulate specific
    devices
  selftests: hid: add XK-24 tests
  selftests: hid: ensure the program is correctly pinned
  wip: vmtest aarch64

 MAINTAINERS                                   |   1 +
 drivers/hid/bpf/Makefile                      |   3 +-
 drivers/hid/bpf/hid_bpf_dispatch.c            |   3 +-
 drivers/hid/bpf/hid_bpf_loader.c              | 243 +++++++++++++++
 drivers/hid/bpf/progs/Makefile                | 105 +++++++
 .../bpf/progs/b0003g0001v05F3p0405-xk24.bpf.c | 106 +++++++
 .../progs/b0003g0001v05F3p0405-xk24.hidbpf.h  | 292 ++++++++++++++++++
 drivers/hid/bpf/progs/hid_bpf.h               |  15 +
 drivers/hid/bpf/progs/hid_bpf_helpers.h       |  22 ++
 drivers/hid/bpf/progs/hid_bpf_progs.h         |  50 +++
 drivers/hid/hid-core.c                        |   2 +
 include/linux/bpf.h                           |   1 +
 include/linux/hid_bpf.h                       |   2 +
 kernel/bpf/inode.c                            |  41 ++-
 tools/bpf/bpftool/gen.c                       |  95 ++++++
 tools/hid/build_progs_list.py                 | 231 ++++++++++++++
 tools/testing/selftests/hid/.gitignore        |   1 +
 tools/testing/selftests/hid/config.aarch64    |  39 +++
 tools/testing/selftests/hid/config.common     | 241 +++++++++++++++
 tools/testing/selftests/hid/config.x86_64     |   4 +
 tools/testing/selftests/hid/hid_bpf.c         | 150 +++++++--
 tools/testing/selftests/hid/vmtest.sh         | 286 +++++++++++++++++
 22 files changed, 1907 insertions(+), 26 deletions(-)
 create mode 100644 drivers/hid/bpf/hid_bpf_loader.c
 create mode 100644 drivers/hid/bpf/progs/Makefile
 create mode 100644 drivers/hid/bpf/progs/b0003g0001v05F3p0405-xk24.bpf.c
 create mode 100644 drivers/hid/bpf/progs/b0003g0001v05F3p0405-xk24.hidbpf.h
 create mode 100644 drivers/hid/bpf/progs/hid_bpf.h
 create mode 100644 drivers/hid/bpf/progs/hid_bpf_helpers.h
 create mode 100644 drivers/hid/bpf/progs/hid_bpf_progs.h
 create mode 100755 tools/hid/build_progs_list.py
 create mode 100644 tools/testing/selftests/hid/config.aarch64
 create mode 100644 tools/testing/selftests/hid/config.common
 create mode 100644 tools/testing/selftests/hid/config.x86_64
 create mode 100755 tools/testing/selftests/hid/vmtest.sh

-- 
2.38.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [RFC hid v1 01/10] bpftool: generate json output of skeletons
  2022-11-24 15:15 [RFC hid v1 00/10] HID-BPF: add support for in-tree BPF programs Benjamin Tissoires
@ 2022-11-24 15:15 ` Benjamin Tissoires
  2022-11-30 23:05   ` Andrii Nakryiko
  2022-11-24 15:15 ` [RFC hid v1 02/10] WIP: bpf: allow to pin programs from the kernel when bpffs is mounted Benjamin Tissoires
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 14+ messages in thread
From: Benjamin Tissoires @ 2022-11-24 15:15 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Dmitry Torokhov
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf, Benjamin Tissoires

So we can then build light skeletons with loaders in any language.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 tools/bpf/bpftool/gen.c | 95 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)

diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
index cf8b4e525c88..818a5209b3ac 100644
--- a/tools/bpf/bpftool/gen.c
+++ b/tools/bpf/bpftool/gen.c
@@ -904,6 +904,96 @@ codegen_progs_skeleton(struct bpf_object *obj, size_t prog_cnt, bool populate_li
 	}
 }
 
+static int gen_json(struct bpf_object *obj, const char *obj_name, size_t file_sz, uint8_t *obj_data)
+{
+	struct bpf_program *prog;
+	struct bpf_map *map;
+	char ident[256];
+
+	jsonw_start_object(json_wtr);	/* root object */
+
+	jsonw_string_field(json_wtr, "name", obj_name);
+
+	jsonw_bool_field(json_wtr, "use_loader", use_loader);
+
+	/* print all maps */
+	jsonw_name(json_wtr, "maps");
+	jsonw_start_array(json_wtr);
+	bpf_object__for_each_map(map, obj) {
+		if (!get_map_ident(map, ident, sizeof(ident))) {
+			p_err("ignoring unrecognized internal map '%s'...",
+			      bpf_map__name(map));
+			continue;
+		}
+
+		jsonw_start_object(json_wtr);	/* map object */
+		jsonw_string_field(json_wtr, "ident", ident);
+		jsonw_string_field(json_wtr, "name", bpf_map__name(map));
+
+		/* print mmap data value */
+		if (is_internal_mmapable_map(map, ident, sizeof(ident))) {
+			const void *mmap_data = NULL;
+			size_t mmap_size = 0;
+
+			mmap_data = bpf_map__initial_value(map, &mmap_size);
+
+			jsonw_uint_field(json_wtr, "size", mmap_size);
+			jsonw_uint_field(json_wtr, "mmap_sz", bpf_map_mmap_sz(map));
+			jsonw_name(json_wtr, "data");
+			print_hex_data_json((uint8_t *)mmap_data, mmap_size);
+
+		}
+		jsonw_end_object(json_wtr);	/* map object */
+	}
+	jsonw_end_array(json_wtr);
+
+	/* print all progs */
+	jsonw_name(json_wtr, "progs");
+	jsonw_start_array(json_wtr);
+	bpf_object__for_each_program(prog, obj) {
+		jsonw_start_object(json_wtr);	/* prog object */
+		jsonw_string_field(json_wtr, "name", bpf_program__name(prog));
+		jsonw_string_field(json_wtr, "sec", bpf_program__section_name(prog));
+		jsonw_end_object(json_wtr);	/* prog object */
+	}
+	jsonw_end_array(json_wtr);
+
+	/* print object data */
+	if (use_loader) {
+		DECLARE_LIBBPF_OPTS(gen_loader_opts, opts);
+		int err = 0;
+
+		err = bpf_object__gen_loader(obj, &opts);
+		if (err)
+			return err;
+
+		err = bpf_object__load(obj);
+		if (err) {
+			p_err("failed to load object file");
+			return err;
+		}
+		/* If there was no error during load then gen_loader_opts
+		 * are populated with the loader program.
+		 */
+
+		jsonw_uint_field(json_wtr, "data_sz", opts.data_sz);
+		jsonw_name(json_wtr, "data");
+		print_hex_data_json((uint8_t *)opts.data, opts.data_sz);
+
+		jsonw_uint_field(json_wtr, "insns_sz", opts.insns_sz);
+		jsonw_name(json_wtr, "insns");
+		print_hex_data_json((uint8_t *)opts.insns, opts.insns_sz);
+
+	} else {
+		jsonw_name(json_wtr, "data");
+		print_hex_data_json(obj_data, file_sz);
+	}
+
+	jsonw_end_object(json_wtr);	/* root object */
+
+	return 0;
+}
+
 static int do_skeleton(int argc, char **argv)
 {
 	char header_guard[MAX_OBJ_NAME_LEN + sizeof("__SKEL_H__")];
@@ -986,6 +1076,11 @@ static int do_skeleton(int argc, char **argv)
 		goto out;
 	}
 
+	if (json_output) {
+		err = gen_json(obj, obj_name, file_sz, (uint8_t *)obj_data);
+		goto out;
+	}
+
 	bpf_object__for_each_map(map, obj) {
 		if (!get_map_ident(map, ident, sizeof(ident))) {
 			p_err("ignoring unrecognized internal map '%s'...",
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC hid v1 02/10] WIP: bpf: allow to pin programs from the kernel when bpffs is mounted
  2022-11-24 15:15 [RFC hid v1 00/10] HID-BPF: add support for in-tree BPF programs Benjamin Tissoires
  2022-11-24 15:15 ` [RFC hid v1 01/10] bpftool: generate json output of skeletons Benjamin Tissoires
@ 2022-11-24 15:15 ` Benjamin Tissoires
  2022-11-24 15:15 ` [RFC hid v1 03/10] HID: add a tool to convert a bpf source into a generic bpf loader Benjamin Tissoires
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Benjamin Tissoires @ 2022-11-24 15:15 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Dmitry Torokhov
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf, Benjamin Tissoires

I want to be able to pin programs loaded by the kernel and expose them
through the bpffs so userspace knows what is loaded.

There are a few things missings in this WIP:
- locking on bpffs_sb
- ability to create a hierarchy from the kernel: I'd like to store all
  of my programs in /sys/fs/bpf/hid, not everything at the root of
  the mount
- ability to store programs when bpffs is not mounted

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 include/linux/bpf.h |  1 +
 kernel/bpf/inode.c  | 41 ++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 0566705c1d4e..f5a7dca520eb 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1806,6 +1806,7 @@ struct bpf_link *bpf_link_get_curr_or_next(u32 *id);
 
 int bpf_obj_pin_user(u32 ufd, const char __user *pathname);
 int bpf_obj_get_user(const char __user *pathname, int flags);
+int bpf_prog_pin_kernel(const char *name, struct bpf_prog *prog);
 
 #define BPF_ITER_FUNC_PREFIX "bpf_iter_"
 #define DEFINE_BPF_ITER_FUNC(target, args...)			\
diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c
index 4f841e16779e..7be24ffad7f7 100644
--- a/kernel/bpf/inode.c
+++ b/kernel/bpf/inode.c
@@ -29,6 +29,9 @@ enum bpf_type {
 	BPF_TYPE_LINK,
 };
 
+
+static struct super_block *bpffs_sb;
+
 static void *bpf_any_get(void *raw, enum bpf_type type)
 {
 	switch (type) {
@@ -435,6 +438,34 @@ static int bpf_iter_link_pin_kernel(struct dentry *parent,
 	return ret;
 }
 
+/* pin a program in the bpffs */
+/* TODO: handle path relative to mount point instead of plain name by recreating
+ * the hierarchy, like in drivers/base/devtmpfs.c
+ */
+int bpf_prog_pin_kernel(const char *name, struct bpf_prog *prog)
+{
+	struct dentry *parent;
+	umode_t mode = S_IFREG | S_IRUSR;
+	struct dentry *dentry;
+	int ret;
+
+	if (!bpffs_sb)
+		return -ENOENT;
+
+	parent = bpffs_sb->s_root;
+
+	inode_lock(parent->d_inode);
+	dentry = lookup_one_len(name, parent, strlen(name));
+	if (IS_ERR(dentry)) {
+		inode_unlock(parent->d_inode);
+		return PTR_ERR(dentry);
+	}
+	ret = bpf_mkprog(dentry, mode, prog);
+	dput(dentry);
+	inode_unlock(parent->d_inode);
+	return ret;
+}
+
 static int bpf_obj_do_pin(const char __user *pathname, void *raw,
 			  enum bpf_type type)
 {
@@ -758,6 +789,8 @@ static int bpf_fill_super(struct super_block *sb, struct fs_context *fc)
 	inode->i_mode &= ~S_IALLUGO;
 	populate_bpffs(sb->s_root);
 	inode->i_mode |= S_ISVTX | opts->mode;
+
+	bpffs_sb = sb;
 	return 0;
 }
 
@@ -795,12 +828,18 @@ static int bpf_init_fs_context(struct fs_context *fc)
 	return 0;
 }
 
+static void bpf_kill_sb(struct super_block *sb)
+{
+	bpffs_sb = NULL;
+	kill_litter_super(sb);
+}
+
 static struct file_system_type bpf_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "bpf",
 	.init_fs_context = bpf_init_fs_context,
 	.parameters	= bpf_fs_parameters,
-	.kill_sb	= kill_litter_super,
+	.kill_sb	= bpf_kill_sb,
 };
 
 static int __init bpf_init(void)
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC hid v1 03/10] HID: add a tool to convert a bpf source into a generic bpf loader
  2022-11-24 15:15 [RFC hid v1 00/10] HID-BPF: add support for in-tree BPF programs Benjamin Tissoires
  2022-11-24 15:15 ` [RFC hid v1 01/10] bpftool: generate json output of skeletons Benjamin Tissoires
  2022-11-24 15:15 ` [RFC hid v1 02/10] WIP: bpf: allow to pin programs from the kernel when bpffs is mounted Benjamin Tissoires
@ 2022-11-24 15:15 ` Benjamin Tissoires
  2022-11-24 15:15 ` [RFC hid v1 04/10] HID: add the bpf loader that can attach a generic hid-bpf program Benjamin Tissoires
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Benjamin Tissoires @ 2022-11-24 15:15 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Dmitry Torokhov
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf, Benjamin Tissoires

We first use bpftool to generate a json representation of the program,
and then use a python script to convert it into a C array element that
will be able to be generically loaded by the kernel.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 MAINTAINERS                             |   1 +
 drivers/hid/bpf/progs/Makefile          | 105 +++++++++++
 drivers/hid/bpf/progs/hid_bpf.h         |  15 ++
 drivers/hid/bpf/progs/hid_bpf_helpers.h |  22 +++
 drivers/hid/bpf/progs/hid_bpf_progs.h   |  49 +++++
 tools/hid/build_progs_list.py           | 231 ++++++++++++++++++++++++
 6 files changed, 423 insertions(+)
 create mode 100644 drivers/hid/bpf/progs/Makefile
 create mode 100644 drivers/hid/bpf/progs/hid_bpf.h
 create mode 100644 drivers/hid/bpf/progs/hid_bpf_helpers.h
 create mode 100644 drivers/hid/bpf/progs/hid_bpf_progs.h
 create mode 100755 tools/hid/build_progs_list.py

diff --git a/MAINTAINERS b/MAINTAINERS
index 752126fba795..8580895e280f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9102,6 +9102,7 @@ F:	drivers/hid/
 F:	include/linux/hid*
 F:	include/uapi/linux/hid*
 F:	samples/hid/
+F:	tools/hid
 F:	tools/testing/selftests/hid/
 
 HID LOGITECH DRIVERS
diff --git a/drivers/hid/bpf/progs/Makefile b/drivers/hid/bpf/progs/Makefile
new file mode 100644
index 000000000000..ee0203d3349a
--- /dev/null
+++ b/drivers/hid/bpf/progs/Makefile
@@ -0,0 +1,105 @@
+# 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
+
+HID_BPF_CONVERTER := $(TOOLS_PATH)/hid/build_progs_list.py
+
+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
+
+TARGETS = $(patsubst %.bpf.c,%.hidbpf.h, $(wildcard *.bpf.c))
+TARGETS += hid_bpf_progs.h
+
+all: $(TARGETS)
+
+clean:
+	$(call msg,CLEAN)
+	$(Q)rm -rf $(OUTPUT) $(TARGETS)
+
+$(OUTPUT)/%.json: $(OUTPUT)/%.bpf.o | $(BPFTOOL)
+	$(call msg,GEN-SKEL,$@)
+	$(Q)$(BPFTOOL) gen skeleton -L -j $< > $@
+
+%.hidbpf.h: $(OUTPUT)/%.json | $(HID_BPF_CONVERTER)
+	$(call msg,GEN-HIDBPF,$@)
+	$(Q)$(HID_BPF_CONVERTER) build_prog $< -o $@
+
+hid_bpf_progs.h: $(addprefix $(OUTPUT)/,$(patsubst %.bpf.c,%.json, $(wildcard *.bpf.c))) | $(HID_BPF_CONVERTER)
+	$(call msg,GEN-HIDBPF-LIST,$@)
+	$(Q)$(HID_BPF_CONVERTER) build_list $< -o $@
+
+$(OUTPUT)/%.bpf.o: %.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
diff --git a/drivers/hid/bpf/progs/hid_bpf.h b/drivers/hid/bpf/progs/hid_bpf.h
new file mode 100644
index 000000000000..7ee371cac2e1
--- /dev/null
+++ b/drivers/hid/bpf/progs/hid_bpf.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2022 Benjamin Tissoires
+ */
+
+#ifndef ____HID_BPF__H
+#define ____HID_BPF__H
+
+struct hid_bpf_probe_args {
+	unsigned int hid;
+	unsigned int rdesc_size;
+	unsigned char rdesc[4096];
+	int retval;
+};
+
+#endif /* ____HID_BPF__H */
diff --git a/drivers/hid/bpf/progs/hid_bpf_helpers.h b/drivers/hid/bpf/progs/hid_bpf_helpers.h
new file mode 100644
index 000000000000..4c4e63a516b3
--- /dev/null
+++ b/drivers/hid/bpf/progs/hid_bpf_helpers.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2022 Benjamin Tissoires
+ */
+
+#ifndef __HID_BPF_HELPERS_H
+#define __HID_BPF_HELPERS_H
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+
+extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx,
+			      unsigned int offset,
+			      const size_t __sz) __ksym;
+extern struct hid_bpf_ctx *hid_bpf_allocate_context(unsigned int hid_id) __ksym;
+extern void hid_bpf_release_context(struct hid_bpf_ctx *ctx) __ksym;
+extern int hid_bpf_hw_request(struct hid_bpf_ctx *ctx,
+			      __u8 *data,
+			      size_t buf__sz,
+			      enum hid_report_type type,
+			      enum hid_class_request reqtype) __ksym;
+
+#endif /* __HID_BPF_HELPERS_H */
diff --git a/drivers/hid/bpf/progs/hid_bpf_progs.h b/drivers/hid/bpf/progs/hid_bpf_progs.h
new file mode 100644
index 000000000000..430e0fb47484
--- /dev/null
+++ b/drivers/hid/bpf/progs/hid_bpf_progs.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+/* THIS FILE IS AUTOGENERATED BY build_progs_list.py ! */
+
+#ifndef __HID_BPF_PROGS_H__
+#define __HID_BPF_PROGS_H__
+
+/**
+ * struct hid_bpf_map: custom HID representation of a map
+ */
+struct hid_bpf_map {
+	unsigned int size;
+	unsigned int mmap_sz;
+	char *data;
+};
+
+/**
+ * struct hid_bpf_prog: custom HID representation of a program
+ */
+struct hid_bpf_prog {
+};
+
+/**
+ * struct hid_bpf_object: custom HID representation of a BPF object
+ * @map_cnt: number of maps available in the BPF object
+ * @prog_cnt: number of programs available
+ * @probe: index of the probe program if any (greater than prog_cnt if none)
+ */
+struct hid_bpf_object {
+	struct hid_device_id id;
+	unsigned int map_cnt;
+	struct hid_bpf_map maps[0];
+	unsigned int prog_cnt;
+	struct hid_bpf_prog progs[0];
+	unsigned int probe;
+	unsigned int rodata;
+	unsigned int data_sz;
+	char *data;
+	unsigned int insns_sz;
+	char *insns;
+};
+
+static struct hid_bpf_object hid_objects[] = {
+
+
+	{ },
+};
+
+#endif /* __HID_BPF_PROGS_H__ */
+
diff --git a/tools/hid/build_progs_list.py b/tools/hid/build_progs_list.py
new file mode 100755
index 000000000000..8103bc2f433f
--- /dev/null
+++ b/tools/hid/build_progs_list.py
@@ -0,0 +1,231 @@
+#!/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright (c) 2022 Benjamin Tissoires
+
+import argparse
+import json
+import re
+import sys
+
+from pathlib import Path
+
+
+def create_header(fp, max_maps_cnt, max_progs_cnt):
+    print(
+        f"""/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+/* THIS FILE IS AUTOGENERATED BY build_progs_list.py ! */
+
+#ifndef __HID_BPF_PROGS_H__
+#define __HID_BPF_PROGS_H__
+
+/**
+ * struct hid_bpf_map: custom HID representation of a map
+ */
+struct hid_bpf_map {{
+	unsigned int size;
+	unsigned int mmap_sz;
+	char *data;
+}};
+
+/**
+ * struct hid_bpf_prog: custom HID representation of a program
+ */
+struct hid_bpf_prog {{
+}};
+
+/**
+ * struct hid_bpf_object: custom HID representation of a BPF object
+ * @map_cnt: number of maps available in the BPF object
+ * @prog_cnt: number of programs available
+ * @probe: index of the probe program if any (greater than prog_cnt if none)
+ */
+struct hid_bpf_object {{
+	struct hid_device_id id;
+	unsigned int map_cnt;
+	struct hid_bpf_map maps[{max_maps_cnt}];
+	unsigned int prog_cnt;
+	struct hid_bpf_prog progs[{max_progs_cnt}];
+	unsigned int probe;
+	unsigned int rodata;
+	unsigned int data_sz;
+	char *data;
+	unsigned int insns_sz;
+	char *insns;
+}};
+
+static struct hid_bpf_object hid_objects[] = {{
+""",
+        file=fp,
+    )
+
+
+def create_footer(fp):
+    print(
+        """
+	{ },
+};
+
+#endif /* __HID_BPF_PROGS_H__ */
+""",
+        file=fp,
+    )
+
+
+def get_bus_group_vid_pid(json_data):
+    path_regex = re.compile(
+        r"b([0-9a-fA-F]{4})g([0-9a-fA-F]{4})v([0-9a-fA-F]{4})p([0-9a-fA-F]{4}).*"
+    )
+    return path_regex.match(json_data["name"])
+
+
+def is_valid_bpf_json(json_data):
+    return get_bus_group_vid_pid(json_data) is not None and json_data["use_loader"]
+
+
+def build_prog_list(args):
+    input_files = [Path(p) for p in args.path]
+    output = args.output
+    max_maps_cnt = 0
+    max_progs_cnt = 0
+    valid_files = []
+
+    for prog in input_files:
+        with open(prog) as f:
+            data = json.load(f)
+        if not is_valid_bpf_json(data):
+            print(f"ignoring {prog.relative_to('.')}", file=sys.stderr)
+            continue
+        generated_name = prog.name.replace(".json", ".hidbpf.h")
+        max_maps_cnt = max(max_maps_cnt, len(data["maps"]))
+        max_progs_cnt = max(max_progs_cnt, len(data["progs"]))
+
+        valid_files.append(generated_name)
+
+    create_header(output, max_maps_cnt, max_progs_cnt)
+
+    valid_files.sort()
+
+    for file in valid_files:
+        print(f'#include "{file}"', file=output)
+
+    create_footer(output)
+
+
+def write_c_data(bytes):
+	outputs = ['"', '']
+	for b in bytes:
+		w = 2 if b == '0x00' else 4
+		if len(outputs[-1]) + w > 78:
+			outputs.append('')
+		if b != "0x00":
+			outputs[-1] += f"\\x{b[2:]}"
+		else:
+			outputs[-1] += "\\0"
+	outputs[-1] += '"'
+	return '\\\n'.join(outputs)
+
+
+def build_c_object(args):
+    input_file = Path(args.path[0])
+    output = args.output
+
+    with open(input_file) as f:
+        data = json.load(f)
+
+    if not is_valid_bpf_json(data):
+        print(f"ignoring {input_file.relative_to('.')}", file=sys.stderr)
+        return
+
+    try:
+        probe_idx = [p["name"] for p in data["progs"]].index("probe")
+    except ValueError:
+        probe_idx = len(data["progs"]) + 1
+
+    try:
+        rodata_idx = [m["ident"] for m in data["maps"]].index("rodata")
+    except ValueError:
+        rodata_idx = len(data["maps"]) + 1
+
+    header_marker = (
+        f"__{input_file.name}__".replace(".", "_")
+        .replace("-", "_")
+        .replace("json", "hidbpf_h")
+        .upper()
+    )
+    m = get_bus_group_vid_pid(data)
+    output.write(
+        f"""/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+/* THIS FILE IS AUTOGENERATED BY build_progs_list.py ! */
+
+#ifndef {header_marker}
+#define {header_marker}
+
+{{
+	.id = {{
+		.bus = 0x{m.group(1)},
+		.group = 0x{m.group(2)},
+		.vendor = 0x{m.group(3)},
+		.product = 0x{m.group(4)},
+	}},
+	.map_cnt = {len(data['maps'])},
+	.prog_cnt = {len(data['progs'])},
+	.probe = {probe_idx},
+	.rodata = {rodata_idx},
+	.maps = {{""")
+
+    for map in data['maps']:
+         output.write(f"""
+		{{
+			.size = {map['size']},
+			.mmap_sz = {map['mmap_sz']},
+			.data = {write_c_data(map['data'])},
+		}},""")
+
+    output.write(f"""
+	}},
+	.data_sz = {data['data_sz']},
+	.data = {write_c_data(data['data'])},
+	.insns_sz = {data['insns_sz']},
+	.insns = {write_c_data(data['insns'])},
+}},
+
+#endif /* {header_marker} */
+"""
+    )
+
+
+def main():
+    ap = argparse.ArgumentParser(description="custom HID BPF json converter")
+
+    def add_output(parser):
+        parser.add_argument(
+            "-o",
+            "--output",
+            nargs="?",
+            type=argparse.FileType("w"),
+            default=sys.stdout,
+            help="Output file, if not given, use stdout",
+        )
+
+    sp = ap.add_subparsers(help="sub-command help")
+
+    obj_parser = sp.add_parser(
+        "build_prog", help="Convert a JSON into a generic HID BPF header"
+    )
+    obj_parser.add_argument("path", nargs=1, help="Input file.", metavar="PATH")
+    add_output(obj_parser)
+    obj_parser.set_defaults(func=build_c_object)
+
+    list_parser = sp.add_parser(
+        "build_list", help="Convert a list of JSON into a generic HID BPF header list"
+    )
+    list_parser.add_argument("path", nargs="*", help="Input file(s).", metavar="PATH")
+    add_output(list_parser)
+    list_parser.set_defaults(func=build_prog_list)
+
+    args = ap.parse_args()
+    args.func(args)
+
+
+if __name__ == "__main__":
+    main()
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC hid v1 04/10] HID: add the bpf loader that can attach a generic hid-bpf program
  2022-11-24 15:15 [RFC hid v1 00/10] HID-BPF: add support for in-tree BPF programs Benjamin Tissoires
                   ` (2 preceding siblings ...)
  2022-11-24 15:15 ` [RFC hid v1 03/10] HID: add a tool to convert a bpf source into a generic bpf loader Benjamin Tissoires
@ 2022-11-24 15:15 ` Benjamin Tissoires
  2022-11-24 15:15 ` [RFC hid v1 05/10] HID: add report descriptor override for the X-Keys XK24 Benjamin Tissoires
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Benjamin Tissoires @ 2022-11-24 15:15 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Dmitry Torokhov
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf, Benjamin Tissoires

When a device is added to the hid bus, we check if there are any known
in-kernel bpf programs related to this device, and when found, we
load them, run the probe syscall if any, and then attach and pin
those programs to the HID device before presenting it to the userspace.

There are still a few bits missing right now:
- the list of programs should not be in kernel but in a separated module
  at least
- the pinned program has an incorrect path (not namespaced to the device)
- the pinned program stay around when the device is disconnected
- we could use the firmware interface to replace the array of programs,
  but how do we distribute and install them?

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/hid/bpf/Makefile           |   3 +-
 drivers/hid/bpf/hid_bpf_dispatch.c |   3 +-
 drivers/hid/bpf/hid_bpf_loader.c   | 243 +++++++++++++++++++++++++++++
 drivers/hid/hid-core.c             |   2 +
 include/linux/hid_bpf.h            |   2 +
 5 files changed, 251 insertions(+), 2 deletions(-)
 create mode 100644 drivers/hid/bpf/hid_bpf_loader.c

diff --git a/drivers/hid/bpf/Makefile b/drivers/hid/bpf/Makefile
index cf55120cf7d6..d0c5fbc506b4 100644
--- a/drivers/hid/bpf/Makefile
+++ b/drivers/hid/bpf/Makefile
@@ -8,4 +8,5 @@ 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
+CFLAGS_hid_bpf_loader.o += -I$(LIBBPF_INCLUDE)
+hid_bpf-objs += hid_bpf_dispatch.o hid_bpf_jmp_table.o hid_bpf_loader.o
diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c
index c3920c7964dc..3c989e74d249 100644
--- a/drivers/hid/bpf/hid_bpf_dispatch.c
+++ b/drivers/hid/bpf/hid_bpf_dispatch.c
@@ -238,7 +238,8 @@ static int hid_bpf_allocate_event_data(struct hid_device *hdev)
 
 int hid_bpf_reconnect(struct hid_device *hdev)
 {
-	if (!test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status))
+	if (hdev->status & HID_STAT_ADDED &&
+	    !test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status))
 		return device_reprobe(&hdev->dev);
 
 	return 0;
diff --git a/drivers/hid/bpf/hid_bpf_loader.c b/drivers/hid/bpf/hid_bpf_loader.c
new file mode 100644
index 000000000000..d2aa0a8e78c7
--- /dev/null
+++ b/drivers/hid/bpf/hid_bpf_loader.c
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2022 Benjamin Tissoires
+ */
+
+#include <bpf/skel_internal.h>
+#include <linux/hid.h>
+#include <linux/hid_bpf.h>
+#include <linux/minmax.h>
+
+#include "hid_bpf_dispatch.h"
+#include "progs/hid_bpf.h"
+#include "progs/hid_bpf_progs.h"
+
+struct generic_bpf_skel {
+	struct hid_bpf_object *obj;
+	struct bpf_map_desc *maps;
+	struct bpf_prog_desc *progs;
+	int *links;
+	void *rodata;
+	/*
+	 * ctx needs to come last, it is populated by the bpf loader
+	 * beyond the end of the struct, which we get through maps
+	 * and progs above.
+	 */
+	struct bpf_loader_ctx ctx;
+};
+
+static size_t hid_bpf_get_obj_ctx_size(struct hid_bpf_object *obj)
+{
+	return sizeof(struct generic_bpf_skel) +
+	       sizeof(struct bpf_map_desc) * obj->map_cnt +
+	       sizeof(struct bpf_prog_desc) * obj->prog_cnt +
+	       sizeof(int) * obj->prog_cnt;
+}
+
+static void
+generic_bpf_skel__detach(struct generic_bpf_skel *skel)
+{
+	int i;
+
+	for (i = 0; i < skel->obj->prog_cnt; i++)
+		skel_closenz(skel->links[i]);
+}
+
+static void
+generic_bpf_skel__destroy(struct generic_bpf_skel *skel)
+{
+	int i;
+
+	if (!skel)
+		return;
+
+	generic_bpf_skel__detach(skel);
+
+	for (i = 0; i < skel->obj->prog_cnt; i++)
+		skel_closenz(skel->progs[i].prog_fd);
+
+	if (skel->obj->rodata < skel->obj->map_cnt) {
+		skel_free_map_data(skel->rodata,
+				   skel->maps[skel->obj->rodata].initial_value,
+				   skel->obj->maps[skel->obj->rodata].mmap_sz);
+	}
+
+	for (i = 0; i < skel->obj->map_cnt; i++)
+		skel_closenz(skel->maps[i].map_fd);
+
+	skel_free(skel);
+}
+
+static struct generic_bpf_skel *
+generic_bpf_skel__open(struct hid_bpf_object *obj)
+{
+	struct generic_bpf_skel *skel;
+
+	skel = skel_alloc(hid_bpf_get_obj_ctx_size(obj));
+	if (!skel)
+		goto cleanup;
+	skel->obj = obj;
+	skel->ctx.sz = hid_bpf_get_obj_ctx_size(obj) - offsetof(struct generic_bpf_skel, ctx);
+
+	skel->maps = (struct bpf_map_desc *)(skel + 1);
+	skel->progs = (struct bpf_prog_desc *)(skel->maps + obj->map_cnt);
+	skel->links = (int *)(skel->progs + obj->prog_cnt);
+
+	if (obj->rodata < obj->map_cnt) {
+		skel->rodata = skel_prep_map_data((void *)obj->maps[obj->rodata].data,
+						  obj->maps[obj->rodata].mmap_sz,
+						  obj->maps[obj->rodata].size);
+
+		if (!skel->rodata)
+			goto cleanup;
+		skel->maps[obj->rodata].initial_value = (__u64) (long) skel->rodata;
+	}
+
+	return (struct generic_bpf_skel *)skel;
+
+cleanup:
+	generic_bpf_skel__destroy((struct generic_bpf_skel *)skel);
+	return NULL;
+}
+
+static int
+generic_bpf_skel__load(struct generic_bpf_skel *skel)
+{
+	struct bpf_load_and_run_opts opts = {};
+	int err;
+
+	opts.ctx = &skel->ctx;
+	opts.data_sz = skel->obj->data_sz;
+	opts.data = (void *)skel->obj->data;
+	opts.insns_sz = skel->obj->insns_sz;
+	opts.insns = skel->obj->insns;
+
+	err = bpf_load_and_run(&opts);
+	if (err < 0)
+		return err;
+
+	if (skel->obj->rodata < skel->obj->map_cnt) {
+		skel->rodata = skel_finalize_map_data(&skel->maps[skel->obj->rodata].initial_value,
+						      skel->obj->maps[skel->obj->rodata].mmap_sz,
+						      PROT_READ,
+						      skel->maps[skel->obj->rodata].map_fd);
+		if (!skel->rodata)
+			return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static struct generic_bpf_skel *
+generic_bpf_skel__open_and_load(struct hid_bpf_object *obj)
+{
+	struct generic_bpf_skel *skel;
+
+	skel = generic_bpf_skel__open(obj);
+	if (!skel)
+		return NULL;
+	if (generic_bpf_skel__load(skel)) {
+		generic_bpf_skel__destroy(skel);
+		return NULL;
+	}
+	return skel;
+}
+
+static int
+execute_probe(struct hid_device *hdev, struct generic_bpf_skel *skel)
+{
+	const size_t test_run_attr_sz = offsetofend(union bpf_attr, test);
+	struct hid_bpf_probe_args *ctx;
+	union bpf_attr attr;
+	int err;
+
+	/* early abort if there is no probe program */
+	if (skel->obj->probe >= skel->obj->prog_cnt)
+		return 0;
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	memset(&attr, 0, test_run_attr_sz);
+
+	attr.test.prog_fd = skel->progs[skel->obj->probe].prog_fd;
+
+	attr.test.ctx_in = (long) ctx;
+	attr.test.ctx_size_in = sizeof(*ctx);
+
+	ctx->hid = hdev->id;
+
+	ctx->rdesc_size = hdev->dev_rsize;
+
+	memcpy(ctx->rdesc, hdev->dev_rdesc,
+	       min_t(unsigned int, sizeof(ctx->rdesc), hdev->dev_rsize));
+
+	err = skel_sys_bpf(BPF_PROG_RUN, &attr, test_run_attr_sz);
+	if (err < 0 || (int)attr.test.retval < 0) {
+		if (err == 0)
+			err = (int)attr.test.retval;
+	} else {
+		err = ctx->retval;
+	}
+
+	kfree(ctx);
+	return err;
+}
+
+static void __hid_load_internal_bpf_program(struct hid_device *hdev, struct hid_bpf_object *obj)
+{
+	struct generic_bpf_skel *skel;
+	int err;
+
+	skel = generic_bpf_skel__open_and_load(obj);
+	if (!skel)
+		return;
+
+	err = execute_probe(hdev, skel);
+	if (err)
+		goto out;
+
+	/*
+	 * The bpf object is loaded and probe returned 0, we can bind
+	 * all of its HID-BPF programs to the device.
+	 */
+	for (int i = 0; i < obj->prog_cnt; i++) {
+		int prog_fd = skel->progs[i].prog_fd;
+		enum hid_bpf_prog_type prog_type;
+		struct bpf_prog *prog;
+
+		if (i == obj->probe)
+			continue;
+
+		prog_type = hid_bpf_get_prog_attach_type(prog_fd);
+
+		/* ignore non HID-BPF programs */
+		if (prog_type < 0 || prog_type >= HID_BPF_PROG_TYPE_MAX)
+			continue;
+
+		err = __hid_bpf_attach_prog(hdev, prog_type, prog_fd, 0);
+
+		if (!err) {
+			prog = bpf_prog_get(prog_fd);
+			/* TODO: prefix the name with 'hid/bus_vid_pid_id/' */
+			err = bpf_prog_pin_kernel(prog->aux->name, prog);
+			if (err)
+				bpf_prog_put(prog);
+		}
+	}
+
+ out:
+	generic_bpf_skel__destroy(skel);
+}
+
+int hid_load_internal_bpf_programs(struct hid_device *hdev)
+{
+	struct hid_bpf_object *obj;
+
+	for (obj = &hid_objects[0]; obj->id.bus; obj++) {
+		if (hid_match_one_id(hdev, &obj->id))
+			__hid_load_internal_bpf_program(hdev, obj);
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(hid_load_internal_bpf_programs);
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index fa4436b8101e..b27b4faad5bc 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2761,6 +2761,8 @@ int hid_add_device(struct hid_device *hdev)
 	dev_set_name(&hdev->dev, "%04X:%04X:%04X.%04X", hdev->bus,
 		     hdev->vendor, hdev->product, hdev->id);
 
+	hid_load_internal_bpf_programs(hdev);
+
 	hid_debug_register(hdev, dev_name(&hdev->dev));
 	ret = device_add(&hdev->dev);
 	if (!ret)
diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h
index 3ca85ab91325..2b06ddf64c1c 100644
--- a/include/linux/hid_bpf.h
+++ b/include/linux/hid_bpf.h
@@ -146,6 +146,7 @@ void hid_bpf_disconnect_device(struct hid_device *hdev);
 void hid_bpf_destroy_device(struct hid_device *hid);
 void hid_bpf_device_init(struct hid_device *hid);
 u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size);
+int hid_load_internal_bpf_programs(struct hid_device *hdev);
 #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) { return data; }
@@ -157,6 +158,7 @@ static inline u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, u
 {
 	return kmemdup(rdesc, *size, GFP_KERNEL);
 }
+static inline int hid_load_internal_bpf_programs(struct hid_device *hdev) { return 0; }
 
 #endif /* CONFIG_HID_BPF */
 
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC hid v1 05/10] HID: add report descriptor override for the X-Keys XK24
  2022-11-24 15:15 [RFC hid v1 00/10] HID-BPF: add support for in-tree BPF programs Benjamin Tissoires
                   ` (3 preceding siblings ...)
  2022-11-24 15:15 ` [RFC hid v1 04/10] HID: add the bpf loader that can attach a generic hid-bpf program Benjamin Tissoires
@ 2022-11-24 15:15 ` Benjamin Tissoires
  2022-11-24 15:15 ` [RFC hid v1 06/10] selftests: hid: add vmtest.sh Benjamin Tissoires
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Benjamin Tissoires @ 2022-11-24 15:15 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Dmitry Torokhov
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf, Benjamin Tissoires

The current report descriptor of the XK-24 is hiding all programmable
buttons. However, we can export them as such, so userspace do not need
to reprogram the device itself and can have some handling depending on
the application.

Also add the compiled files as a demonstration of the output, but
hopefully these outputs should be generated automatically when running
make at the root of the kernel sources.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 .../bpf/progs/b0003g0001v05F3p0405-xk24.bpf.c | 106 +++++++
 .../progs/b0003g0001v05F3p0405-xk24.hidbpf.h  | 292 ++++++++++++++++++
 drivers/hid/bpf/progs/hid_bpf_progs.h         |   5 +-
 3 files changed, 401 insertions(+), 2 deletions(-)
 create mode 100644 drivers/hid/bpf/progs/b0003g0001v05F3p0405-xk24.bpf.c
 create mode 100644 drivers/hid/bpf/progs/b0003g0001v05F3p0405-xk24.hidbpf.h

diff --git a/drivers/hid/bpf/progs/b0003g0001v05F3p0405-xk24.bpf.c b/drivers/hid/bpf/progs/b0003g0001v05F3p0405-xk24.bpf.c
new file mode 100644
index 000000000000..736861988a94
--- /dev/null
+++ b/drivers/hid/bpf/progs/b0003g0001v05F3p0405-xk24.bpf.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2022 Benjamin Tissoires
+ */
+
+#include "vmlinux.h"
+#include "hid_bpf_helpers.h"
+#include <bpf/bpf_tracing.h>
+
+static const __u8 fixed_rdesc[] = {
+	0x05, 0x0c,                    // Usage Page (Consumer Devices)
+	0x09, 0x01,                    // Usage (Consumer Control)
+	0xa1, 0x01,                    // Collection (Application)
+
+	0xa1, 0x02,                    //  Collection (Logical)
+	0x05, 0x08,                    //   Usage Page (LEDs)
+	0x09, 0x4b,                    //   Usage (Generic Indicator)
+	0x75, 0x08,                    //   Report Size (8)
+	0x95, 0x23,                    //   Report Count (35)
+	0x91, 0x02,                    //   Output (Data,Var,Abs)
+	0xc0,                          //  End Collection
+
+	0xa1, 0x02,                    //  Collection (Logical)
+	0x09, 0x00,                    //   Usage (Undefined)
+	0x75, 0x08,                    //   Report Size (8)
+	0x95, 0x01,                    //   Report Count (1)
+	0x81, 0x03,                    //   Input (Cnst,Var,Abs)
+	0x05, 0x09,                    //   Usage Page (Button)
+	0x09, 0xff,                    //   Usage (Vendor Usage 0xff)
+	0x75, 0x01,                    //   Report Size (1)
+	0x95, 0x01,                    //   Report Count (1)
+	0x81, 0x02,                    //   Input (Data,Var,Abs)
+	0x95, 0x07,                    //   Report Count (7)
+	0x81, 0x03,                    //   Input (Cnst,Var,Abs)
+
+	0x05, 0x0c,                    //   Usage Page (Consumer Devices)
+	0x09, 0x03,                    //   Usage (Programmable Buttons)
+	0xa1, 0x04,                    //   Collection (Named_array)
+	0x05, 0x09,                    //   Usage Page (Button)
+
+	0x19, 0x01,                    //   Usage Minimum (1)
+	0x29, 0x06,                    //   Usage Maximum (6)
+	0x95, 0x06,                    //   Report Count (6)
+	0x81, 0x02,                    //   Input (Data,Var,Abs)
+	0x95, 0x02,                    //   Report Count (2)
+	0x81, 0x03,                    //   Input (Cnst,Var,Abs)
+
+	0x19, 0x07,                    //   Usage Minimum (7)
+	0x29, 0x0c,                    //   Usage Maximum (12)
+	0x95, 0x06,                    //   Report Count (6)
+	0x81, 0x02,                    //   Input (Data,Var,Abs)
+	0x95, 0x02,                    //   Report Count (2)
+	0x81, 0x03,                    //   Input (Cnst,Var,Abs)
+
+	0x19, 0x0d,                    //   Usage Minimum (13)
+	0x29, 0x12,                    //   Usage Maximum (18)
+	0x95, 0x06,                    //   Report Count (6)
+	0x81, 0x02,                    //   Input (Data,Var,Abs)
+	0x95, 0x02,                    //   Report Count (2)
+	0x81, 0x03,                    //   Input (Cnst,Var,Abs)
+
+	0x19, 0x13,                    //   Usage Minimum (19)
+	0x29, 0x19,                    //   Usage Maximum (24)
+	0x95, 0x06,                    //   Report Count (6)
+	0x81, 0x02,                    //   Input (Data,Var,Abs)
+	0x95, 0x02,                    //   Report Count (2)
+	0x81, 0x03,                    //   Input (Cnst,Var,Abs)
+
+	0xc0,                          //   End Collection
+
+	0x75, 0x08,                    //   Report Size (8)
+	0x95, 0x1a,                    //   Report Count (26)
+	0x81, 0x03,                    //   Input (Cnst,Var,Abs)
+	0xc0,                          //  End Collection
+	0xc0,                          // End Collection
+};
+
+SEC("fmod_ret/hid_bpf_rdesc_fixup")
+int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx)
+{
+	s16 y;
+	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);
+
+	if (!data)
+		return 0; /* EPERM check */
+
+	__builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc));
+
+	return sizeof(fixed_rdesc);
+
+	return 0;
+}
+
+SEC("syscall")
+int probe(struct hid_bpf_probe_args *ctx)
+{
+	/*
+	 * The device exports 3 interfaces.
+	 */
+	ctx->retval = ctx->rdesc_size != 33;
+	if (ctx->retval)
+		ctx->retval = -22;
+
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/drivers/hid/bpf/progs/b0003g0001v05F3p0405-xk24.hidbpf.h b/drivers/hid/bpf/progs/b0003g0001v05F3p0405-xk24.hidbpf.h
new file mode 100644
index 000000000000..f193d4dea6fd
--- /dev/null
+++ b/drivers/hid/bpf/progs/b0003g0001v05F3p0405-xk24.hidbpf.h
@@ -0,0 +1,292 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+/* THIS FILE IS AUTOGENERATED BY build_progs_list.py ! */
+
+#ifndef __B0003G0001V05F3P0405_XK24_HIDBPF_H__
+#define __B0003G0001V05F3P0405_XK24_HIDBPF_H__
+
+{
+	.id = {
+		.bus = 0x0003,
+		.group = 0x0001,
+		.vendor = 0x05F3,
+		.product = 0x0405,
+	},
+	.map_cnt = 1,
+	.prog_cnt = 2,
+	.probe = 1,
+	.rodata = 0,
+	.maps = {
+		{
+			.size = 108,
+			.mmap_sz = 4096,
+			.data = "\
+\x05\x0c\x09\x01\xa1\x01\xa1\x02\x05\x08\x09\x4b\x75\x08\x95\x23\x91\x02\xc0\
+\xa1\x02\x09\0\x75\x08\x95\x01\x81\x03\x05\x09\x09\xff\x75\x01\x95\x01\x81\x02\
+\x95\x07\x81\x03\x05\x0c\x09\x03\xa1\x04\x05\x09\x19\x01\x29\x06\x95\x06\x81\
+\x02\x95\x02\x81\x03\x19\x07\x29\x0c\x95\x06\x81\x02\x95\x02\x81\x03\x19\x0d\
+\x29\x12\x95\x06\x81\x02\x95\x02\x81\x03\x19\x13\x29\x19\x95\x06\x81\x02\x95\
+\x02\x81\x03\xc0\x75\x08\x95\x1a\x81\x03\xc0\xc0",
+		},
+	},
+	.data_sz = 5088,
+	.data = "\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x9f\xeb\x01\0\
+\x18\0\0\0\0\0\0\0\x28\x03\0\0\x28\x03\0\0\x4c\x03\0\0\0\0\0\0\0\0\0\x02\x02\0\
+\0\0\x01\0\0\0\0\0\0\x01\x08\0\0\0\x40\0\0\0\0\0\0\0\x01\0\0\x0d\x04\0\0\0\x14\
+\0\0\0\x01\0\0\0\x18\0\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\x01\x1c\0\0\0\x01\0\0\
+\x0c\x03\0\0\0\0\0\0\0\x03\0\0\x0d\x07\0\0\0\x41\x03\0\0\x0a\0\0\0\x41\x03\0\0\
+\x0d\0\0\0\x41\x03\0\0\x13\0\0\0\0\0\0\0\0\0\0\x02\x08\0\0\0\x58\x01\0\0\0\0\0\
+\x08\x09\0\0\0\x5d\x01\0\0\0\0\0\x01\x01\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\x02\x0b\
+\0\0\0\x6b\x01\0\0\x05\0\0\x04\x20\0\0\0\x77\x01\0\0\x0c\0\0\0\0\0\0\0\x7d\x01\
+\0\0\x0e\0\0\0\x40\0\0\0\x81\x01\0\0\x0c\0\0\0\x80\0\0\0\x90\x01\0\0\x10\0\0\0\
+\xa0\0\0\0\0\0\0\0\x11\0\0\0\xc0\0\0\0\x9c\x01\0\0\0\0\0\x08\x0d\0\0\0\xa2\x01\
+\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\x02\x0f\0\0\0\0\0\0\0\0\0\0\
+\x0a\x28\0\0\0\xaf\x01\0\0\x04\0\0\x06\x04\0\0\0\xbf\x01\0\0\0\0\0\0\xd0\x01\0\
+\0\x01\0\0\0\xe2\x01\0\0\x02\0\0\0\xf5\x01\0\0\x03\0\0\0\0\0\0\0\x02\0\0\x05\
+\x04\0\0\0\x06\x02\0\0\x12\0\0\0\0\0\0\0\x0d\x02\0\0\x12\0\0\0\0\0\0\0\x12\x02\
+\0\0\0\0\0\x08\x04\0\0\0\0\0\0\0\0\0\0\x0a\x14\0\0\0\x18\x02\0\0\0\0\0\x08\x15\
+\0\0\0\x1f\x02\0\0\0\0\0\x08\x16\0\0\0\x2f\x02\0\0\0\0\0\x08\x17\0\0\0\x40\x02\
+\0\0\0\0\0\x01\x08\0\0\0\x40\0\0\0\x4e\x02\0\0\x01\0\0\x0c\x06\0\0\0\0\0\0\0\0\
+\0\0\x02\x1a\0\0\0\x5f\x02\0\0\x04\0\0\x04\x0c\x10\0\0\x7d\x01\0\0\x0d\0\0\0\0\
+\0\0\0\x72\x02\0\0\x0d\0\0\0\x20\0\0\0\x7d\x02\0\0\x1b\0\0\0\x40\0\0\0\x06\x02\
+\0\0\x04\0\0\0\x40\x80\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x09\0\0\0\x1c\0\0\0\0\x10\
+\0\0\x83\x02\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\0\0\0\0\0\x01\0\0\x0d\x04\0\0\0\
+\x14\0\0\0\x19\0\0\0\x97\x02\0\0\x01\0\0\x0c\x1d\0\0\0\x05\x03\0\0\0\0\0\x01\
+\x01\0\0\0\x08\0\0\x01\0\0\0\0\0\0\0\x03\0\0\0\0\x1f\0\0\0\x1c\0\0\0\x04\0\0\0\
+\x0a\x03\0\0\0\0\0\x0e\x20\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x0a\x08\0\0\0\0\0\0\0\
+\0\0\0\x03\0\0\0\0\x22\0\0\0\x1c\0\0\0\x6c\0\0\0\x13\x03\0\0\0\0\0\x0e\x23\0\0\
+\0\0\0\0\0\x1f\x03\0\0\x01\0\0\x0f\x04\0\0\0\x29\0\0\0\0\0\0\0\x04\0\0\0\x26\
+\x03\0\0\x01\0\0\x0f\x6c\0\0\0\x24\0\0\0\0\0\0\0\x6c\0\0\0\x2e\x03\0\0\x01\0\0\
+\x0f\x04\0\0\0\x21\0\0\0\0\0\0\0\x04\0\0\0\x36\x03\0\0\0\0\0\x07\0\0\0\0\x41\
+\x03\0\0\0\0\0\x0e\x04\0\0\0\x01\0\0\0\0\x75\x6e\x73\x69\x67\x6e\x65\x64\x20\
+\x6c\x6f\x6e\x67\x20\x6c\x6f\x6e\x67\0\x63\x74\x78\0\x69\x6e\x74\0\x68\x69\x64\
+\x5f\x66\x69\x78\x5f\x72\x64\x65\x73\x63\0\x66\x6d\x6f\x64\x5f\x72\x65\x74\x2f\
+\x68\x69\x64\x5f\x62\x70\x66\x5f\x72\x64\x65\x73\x63\x5f\x66\x69\x78\x75\x70\0\
+\x2f\x68\x6f\x6d\x65\x2f\x62\x74\x69\x73\x73\x6f\x69\x72\x2f\x53\x72\x63\x2f\
+\x68\x69\x64\x2f\x64\x72\x69\x76\x65\x72\x73\x2f\x68\x69\x64\x2f\x62\x70\x66\
+\x2f\x70\x72\x6f\x67\x73\x2f\x62\x30\x30\x30\x33\x67\x30\x30\x30\x31\x76\x30\
+\x35\x46\x33\x70\x30\x34\x30\x35\x2d\x78\x6b\x32\x34\x2e\x62\x70\x66\x2e\x63\0\
+\x69\x6e\x74\x20\x42\x50\x46\x5f\x50\x52\x4f\x47\x28\x68\x69\x64\x5f\x66\x69\
+\x78\x5f\x72\x64\x65\x73\x63\x2c\x20\x73\x74\x72\x75\x63\x74\x20\x68\x69\x64\
+\x5f\x62\x70\x66\x5f\x63\x74\x78\x20\x2a\x68\x63\x74\x78\x29\0\x09\x5f\x5f\x75\
+\x38\x20\x2a\x64\x61\x74\x61\x20\x3d\x20\x68\x69\x64\x5f\x62\x70\x66\x5f\x67\
+\x65\x74\x5f\x64\x61\x74\x61\x28\x68\x63\x74\x78\x2c\x20\x30\x20\x2f\x2a\x20\
+\x6f\x66\x66\x73\x65\x74\x20\x2a\x2f\x2c\x20\x34\x30\x39\x36\x20\x2f\x2a\x20\
+\x73\x69\x7a\x65\x20\x2a\x2f\x29\x3b\0\x09\x69\x66\x20\x28\x21\x64\x61\x74\x61\
+\x29\0\x09\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x5f\x6d\x65\x6d\x63\x70\x79\x28\
+\x64\x61\x74\x61\x2c\x20\x66\x69\x78\x65\x64\x5f\x72\x64\x65\x73\x63\x2c\x20\
+\x73\x69\x7a\x65\x6f\x66\x28\x66\x69\x78\x65\x64\x5f\x72\x64\x65\x73\x63\x29\
+\x29\x3b\0\x5f\x5f\x75\x38\0\x75\x6e\x73\x69\x67\x6e\x65\x64\x20\x63\x68\x61\
+\x72\0\x68\x69\x64\x5f\x62\x70\x66\x5f\x63\x74\x78\0\x69\x6e\x64\x65\x78\0\x68\
+\x69\x64\0\x61\x6c\x6c\x6f\x63\x61\x74\x65\x64\x5f\x73\x69\x7a\x65\0\x72\x65\
+\x70\x6f\x72\x74\x5f\x74\x79\x70\x65\0\x5f\x5f\x75\x33\x32\0\x75\x6e\x73\x69\
+\x67\x6e\x65\x64\x20\x69\x6e\x74\0\x68\x69\x64\x5f\x72\x65\x70\x6f\x72\x74\x5f\
+\x74\x79\x70\x65\0\x48\x49\x44\x5f\x49\x4e\x50\x55\x54\x5f\x52\x45\x50\x4f\x52\
+\x54\0\x48\x49\x44\x5f\x4f\x55\x54\x50\x55\x54\x5f\x52\x45\x50\x4f\x52\x54\0\
+\x48\x49\x44\x5f\x46\x45\x41\x54\x55\x52\x45\x5f\x52\x45\x50\x4f\x52\x54\0\x48\
+\x49\x44\x5f\x52\x45\x50\x4f\x52\x54\x5f\x54\x59\x50\x45\x53\0\x72\x65\x74\x76\
+\x61\x6c\0\x73\x69\x7a\x65\0\x5f\x5f\x73\x33\x32\0\x73\x69\x7a\x65\x5f\x74\0\
+\x5f\x5f\x6b\x65\x72\x6e\x65\x6c\x5f\x73\x69\x7a\x65\x5f\x74\0\x5f\x5f\x6b\x65\
+\x72\x6e\x65\x6c\x5f\x75\x6c\x6f\x6e\x67\x5f\x74\0\x75\x6e\x73\x69\x67\x6e\x65\
+\x64\x20\x6c\x6f\x6e\x67\0\x68\x69\x64\x5f\x62\x70\x66\x5f\x67\x65\x74\x5f\x64\
+\x61\x74\x61\0\x68\x69\x64\x5f\x62\x70\x66\x5f\x70\x72\x6f\x62\x65\x5f\x61\x72\
+\x67\x73\0\x72\x64\x65\x73\x63\x5f\x73\x69\x7a\x65\0\x72\x64\x65\x73\x63\0\x5f\
+\x5f\x41\x52\x52\x41\x59\x5f\x53\x49\x5a\x45\x5f\x54\x59\x50\x45\x5f\x5f\0\x70\
+\x72\x6f\x62\x65\0\x73\x79\x73\x63\x61\x6c\x6c\0\x30\x3a\x31\0\x09\x63\x74\x78\
+\x2d\x3e\x72\x65\x74\x76\x61\x6c\x20\x3d\x20\x63\x74\x78\x2d\x3e\x72\x64\x65\
+\x73\x63\x5f\x73\x69\x7a\x65\x20\x21\x3d\x20\x33\x33\x3b\0\x30\x3a\x33\0\x09\
+\x69\x66\x20\x28\x63\x74\x78\x2d\x3e\x72\x65\x74\x76\x61\x6c\x29\0\x09\x09\x63\
+\x74\x78\x2d\x3e\x72\x65\x74\x76\x61\x6c\x20\x3d\x20\x2d\x32\x32\x3b\0\x09\x72\
+\x65\x74\x75\x72\x6e\x20\x30\x3b\0\x63\x68\x61\x72\0\x5f\x6c\x69\x63\x65\x6e\
+\x73\x65\0\x66\x69\x78\x65\x64\x5f\x72\x64\x65\x73\x63\0\x2e\x6b\x73\x79\x6d\
+\x73\0\x2e\x72\x6f\x64\x61\x74\x61\0\x6c\x69\x63\x65\x6e\x73\x65\0\x68\x69\x64\
+\x5f\x64\x65\x76\x69\x63\x65\0\x64\x75\x6d\x6d\x79\x5f\x6b\x73\x79\x6d\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x8c\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\
+\0\x04\0\0\0\x6c\0\0\0\x01\0\0\0\x80\x04\0\0\0\0\0\0\0\0\0\0\x62\x30\x30\x30\
+\x33\x67\x30\x30\x2e\x72\x6f\x64\x61\x74\x61\0\0\0\0\0\0\0\0\0\0\0\0\0\x26\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\x05\x0c\x09\x01\xa1\x01\xa1\x02\x05\x08\x09\x4b\x75\
+\x08\x95\x23\x91\x02\xc0\xa1\x02\x09\0\x75\x08\x95\x01\x81\x03\x05\x09\x09\xff\
+\x75\x01\x95\x01\x81\x02\x95\x07\x81\x03\x05\x0c\x09\x03\xa1\x04\x05\x09\x19\
+\x01\x29\x06\x95\x06\x81\x02\x95\x02\x81\x03\x19\x07\x29\x0c\x95\x06\x81\x02\
+\x95\x02\x81\x03\x19\x0d\x29\x12\x95\x06\x81\x02\x95\x02\x81\x03\x19\x13\x29\
+\x19\x95\x06\x81\x02\x95\x02\x81\x03\xc0\x75\x08\x95\x1a\x81\x03\xc0\xc0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\x47\x50\x4c\0\0\0\0\0\x79\x11\0\0\0\0\0\0\xb7\x06\0\0\0\0\
+\0\0\xb7\x02\0\0\0\0\0\0\xb7\x03\0\0\0\x10\0\0\x85\x20\0\0\0\0\0\0\x15\0\x87\0\
+\0\0\0\0\xb7\x01\0\0\x1a\0\0\0\x73\x10\x67\0\0\0\0\0\xb7\x01\0\0\x13\0\0\0\x73\
+\x10\x58\0\0\0\0\0\xb7\x01\0\0\x12\0\0\0\x73\x10\x4e\0\0\0\0\0\xb7\x01\0\0\x0d\
+\0\0\0\x73\x10\x4c\0\0\0\0\0\xb7\x01\0\0\x06\0\0\0\x73\x10\x5c\0\0\0\0\0\x73\
+\x10\x50\0\0\0\0\0\x73\x10\x44\0\0\0\0\0\x73\x10\x38\0\0\0\0\0\x73\x10\x36\0\0\
+\0\0\0\xb7\x01\0\0\x29\0\0\0\x73\x10\x59\0\0\0\0\0\x73\x10\x4d\0\0\0\0\0\x73\
+\x10\x41\0\0\0\0\0\x73\x10\x35\0\0\0\0\0\xb7\x01\0\0\x19\0\0\0\x73\x10\x5a\0\0\
+\0\0\0\x73\x10\x57\0\0\0\0\0\x73\x10\x4b\0\0\0\0\0\x73\x10\x3f\0\0\0\0\0\x73\
+\x10\x33\0\0\0\0\0\xb7\x01\0\0\x04\0\0\0\x73\x10\x30\0\0\0\0\0\xb7\x01\0\0\x07\
+\0\0\0\x73\x10\x40\0\0\0\0\0\x73\x10\x28\0\0\0\0\0\xb7\x01\0\0\xff\0\0\0\x73\
+\x10\x20\0\0\0\0\0\xb7\x01\0\0\x03\0\0\0\x73\x10\x69\0\0\0\0\0\x73\x10\x62\0\0\
+\0\0\0\x73\x10\x56\0\0\0\0\0\x73\x10\x4a\0\0\0\0\0\x73\x10\x3e\0\0\0\0\0\x73\
+\x10\x2e\0\0\0\0\0\x73\x10\x2a\0\0\0\0\0\x73\x10\x1c\0\0\0\0\0\xb7\x01\0\0\x81\
+\0\0\0\x73\x10\x68\0\0\0\0\0\x73\x10\x61\0\0\0\0\0\x73\x10\x5d\0\0\0\0\0\x73\
+\x10\x55\0\0\0\0\0\x73\x10\x51\0\0\0\0\0\x73\x10\x49\0\0\0\0\0\x73\x10\x45\0\0\
+\0\0\0\x73\x10\x3d\0\0\0\0\0\x73\x10\x39\0\0\0\0\0\x73\x10\x29\0\0\0\0\0\x73\
+\x10\x25\0\0\0\0\0\x73\x10\x1b\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x73\x10\x16\0\0\0\
+\0\0\xb7\x01\0\0\xc0\0\0\0\x73\x10\x6b\0\0\0\0\0\x73\x10\x6a\0\0\0\0\0\x73\x10\
+\x63\0\0\0\0\0\x73\x10\x12\0\0\0\0\0\xb7\x01\0\0\x91\0\0\0\x73\x10\x10\0\0\0\0\
+\0\xb7\x01\0\0\x23\0\0\0\x73\x10\x0f\0\0\0\0\0\xb7\x01\0\0\x95\0\0\0\x73\x10\
+\x66\0\0\0\0\0\x73\x10\x5f\0\0\0\0\0\x73\x10\x5b\0\0\0\0\0\x73\x10\x53\0\0\0\0\
+\0\x73\x10\x4f\0\0\0\0\0\x73\x10\x47\0\0\0\0\0\x73\x10\x43\0\0\0\0\0\x73\x10\
+\x3b\0\0\0\0\0\x73\x10\x37\0\0\0\0\0\x73\x10\x27\0\0\0\0\0\x73\x10\x23\0\0\0\0\
+\0\x73\x10\x19\0\0\0\0\0\x73\x10\x0e\0\0\0\0\0\xb7\x01\0\0\x75\0\0\0\x73\x10\
+\x64\0\0\0\0\0\x73\x10\x21\0\0\0\0\0\x73\x10\x17\0\0\0\0\0\x73\x10\x0c\0\0\0\0\
+\0\xb7\x01\0\0\x4b\0\0\0\x73\x10\x0b\0\0\0\0\0\xb7\x01\0\0\x08\0\0\0\x73\x10\
+\x65\0\0\0\0\0\x73\x10\x18\0\0\0\0\0\x73\x10\x0d\0\0\0\0\0\x73\x10\x09\0\0\0\0\
+\0\xb7\x01\0\0\x02\0\0\0\x73\x10\x60\0\0\0\0\0\x73\x10\x5e\0\0\0\0\0\x73\x10\
+\x54\0\0\0\0\0\x73\x10\x52\0\0\0\0\0\x73\x10\x48\0\0\0\0\0\x73\x10\x46\0\0\0\0\
+\0\x73\x10\x3c\0\0\0\0\0\x73\x10\x3a\0\0\0\0\0\x73\x10\x26\0\0\0\0\0\x73\x10\
+\x14\0\0\0\0\0\x73\x10\x11\0\0\0\0\0\x73\x10\x07\0\0\0\0\0\xb7\x01\0\0\xa1\0\0\
+\0\x73\x10\x2f\0\0\0\0\0\x73\x10\x13\0\0\0\0\0\x73\x10\x06\0\0\0\0\0\x73\x10\
+\x04\0\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x10\x34\0\0\0\0\0\x73\x10\x24\0\0\0\0\
+\0\x73\x10\x22\0\0\0\0\0\x73\x10\x1a\0\0\0\0\0\x73\x10\x05\0\0\0\0\0\x73\x10\
+\x03\0\0\0\0\0\xb7\x01\0\0\x09\0\0\0\x73\x10\x32\0\0\0\0\0\x73\x10\x2d\0\0\0\0\
+\0\x73\x10\x1f\0\0\0\0\0\x73\x10\x1e\0\0\0\0\0\x73\x10\x15\0\0\0\0\0\x73\x10\
+\x0a\0\0\0\0\0\x73\x10\x02\0\0\0\0\0\xb7\x01\0\0\x0c\0\0\0\x73\x10\x42\0\0\0\0\
+\0\x73\x10\x2c\0\0\0\0\0\x73\x10\x01\0\0\0\0\0\xb7\x01\0\0\x05\0\0\0\x73\x10\
+\x31\0\0\0\0\0\x73\x10\x2b\0\0\0\0\0\x73\x10\x1d\0\0\0\0\0\x73\x10\x08\0\0\0\0\
+\0\x73\x10\0\0\0\0\0\0\xb7\x06\0\0\x6c\0\0\0\xbf\x60\0\0\0\0\0\0\x95\0\0\0\0\0\
+\0\0\0\0\0\0\x05\0\0\0\0\0\0\0\x47\0\0\0\x94\0\0\0\x05\x38\x01\0\x02\0\0\0\x47\
+\0\0\0\xca\0\0\0\x0f\x44\x01\0\x05\0\0\0\x47\0\0\0\x11\x01\0\0\x06\x4c\x01\0\
+\x07\0\0\0\x47\0\0\0\x1d\x01\0\0\x02\x58\x01\0\x8d\0\0\0\x47\0\0\0\x94\0\0\0\
+\x05\x38\x01\0\x1a\0\0\0\x8f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x68\x69\x64\x5f\x66\x69\x78\x5f\x72\x64\
+\x65\x73\x63\0\0\0\0\0\0\0\x1a\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\
+\0\x10\0\0\0\0\0\0\0\0\0\0\0\x05\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x68\x69\x64\x5f\x62\x70\x66\x5f\x72\x64\
+\x65\x73\x63\x5f\x66\x69\x78\x75\x70\0\0\0\0\0\x68\x69\x64\x5f\x62\x70\x66\x5f\
+\x67\x65\x74\x5f\x64\x61\x74\x61\0\0\0\0\0\0\0\0\x47\x50\x4c\0\0\0\0\0\x61\x13\
+\x04\0\0\0\0\0\xb7\x02\0\0\x01\0\0\0\x55\x03\x01\0\x21\0\0\0\xb7\x02\0\0\0\0\0\
+\0\x63\x21\x08\x10\0\0\0\0\x61\x12\x08\x10\0\0\0\0\x15\x02\x03\0\0\0\0\0\x18\
+\x02\0\0\xea\xff\xff\xff\0\0\0\0\0\0\0\0\x63\x21\x08\x10\0\0\0\0\xb7\0\0\0\0\0\
+\0\0\x95\0\0\0\0\0\0\0\0\0\0\0\x1e\0\0\0\0\0\0\0\x47\0\0\0\xa9\x02\0\0\x15\x8c\
+\x01\0\x02\0\0\0\x47\0\0\0\xa9\x02\0\0\x20\x8c\x01\0\x04\0\0\0\x47\0\0\0\xa9\
+\x02\0\0\x0e\x8c\x01\0\x05\0\0\0\x47\0\0\0\xd3\x02\0\0\x0b\x90\x01\0\x06\0\0\0\
+\x47\0\0\0\xd3\x02\0\0\x06\x90\x01\0\x09\0\0\0\x47\0\0\0\xe5\x02\0\0\x0f\x94\
+\x01\0\x0a\0\0\0\x47\0\0\0\xfa\x02\0\0\x02\x9c\x01\0\0\0\0\0\x1a\0\0\0\xa5\x02\
+\0\0\0\0\0\0\x20\0\0\0\x1a\0\0\0\xcf\x02\0\0\0\0\0\0\x28\0\0\0\x1a\0\0\0\xcf\
+\x02\0\0\0\0\0\0\x48\0\0\0\x1a\0\0\0\xcf\x02\0\0\0\0\0\0\x1f\0\0\0\x0c\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x10\0\0\
+\0\x70\x72\x6f\x62\x65\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\
+\0\0\0\0\0\0\0\0\x01\0\0\0\x10\0\0\0\0\0\0\0\0\0\0\0\x07\0\0\0\0\0\0\0\0\0\0\0\
+\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0",
+	.insns_sz = 2304,
+	.insns = "\
+\xbf\x16\0\0\0\0\0\0\xbf\xa1\0\0\0\0\0\0\x07\x01\0\0\x78\xff\xff\xff\xb7\x02\0\
+\0\x88\0\0\0\xb7\x03\0\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x05\0\x14\0\0\0\0\0\x61\
+\xa1\x78\xff\0\0\0\0\xd5\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa1\x7c\xff\
+\0\0\0\0\xd5\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa1\x80\xff\0\0\0\0\xd5\
+\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa1\x84\xff\0\0\0\0\xd5\x01\x01\0\0\
+\0\0\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x61\x01\0\0\0\0\
+\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\xbf\x70\0\0\
+\0\0\0\0\x95\0\0\0\0\0\0\0\x61\x60\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\
+\xa8\x0b\0\0\x63\x01\0\0\0\0\0\0\x61\x60\x0c\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\
+\0\0\xa4\x0b\0\0\x63\x01\0\0\0\0\0\0\x79\x60\x10\0\0\0\0\0\x18\x61\0\0\0\0\0\0\
+\0\0\0\0\x98\x0b\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\x05\0\0\
+\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\x0b\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x12\0\
+\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x90\x0b\0\0\xb7\x03\0\0\x1c\0\0\0\x85\0\0\0\
+\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xd4\xff\0\0\0\0\x63\x7a\x78\xff\0\0\0\0\
+\x61\xa0\x78\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xe0\x0b\0\0\x63\x01\0\0\0\
+\0\0\0\x61\x60\x1c\0\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\
+\xbc\x0b\0\0\x63\x01\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\
+\0\xb0\x0b\0\0\xb7\x03\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\
+\xc5\x07\xc3\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x63\x71\0\0\0\0\0\
+\0\x79\x63\x20\0\0\0\0\0\x15\x03\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xf8\
+\x0b\0\0\xb7\x02\0\0\x6c\0\0\0\x61\x60\x04\0\0\0\0\0\x45\0\x02\0\x01\0\0\0\x85\
+\0\0\0\x94\0\0\0\x05\0\x01\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x18\x62\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\x61\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x70\x0c\0\0\x63\
+\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x68\x0c\0\0\x18\x61\0\0\0\0\0\0\0\
+\0\0\0\x78\x0c\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xf8\x0b\0\0\
+\x18\x61\0\0\0\0\0\0\0\0\0\0\x80\x0c\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\0\0\x02\0\
+\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x70\x0c\0\0\xb7\x03\0\0\x20\0\0\0\x85\0\0\0\
+\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x9f\xff\0\0\0\0\x18\x62\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\x61\x20\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\x0c\0\0\x63\
+\x01\0\0\0\0\0\0\xb7\x01\0\0\x16\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x90\x0c\0\0\
+\xb7\x03\0\0\x04\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x92\xff\
+\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x98\x0c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\
+\x80\x11\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xa0\x0c\0\0\x18\
+\x61\0\0\0\0\0\0\0\0\0\0\x78\x11\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\
+\0\0\0\x18\x11\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xc0\x11\0\0\x7b\x01\0\0\0\0\0\0\
+\x18\x60\0\0\0\0\0\0\0\0\0\0\x20\x11\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xd0\x11\0\
+\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x70\x11\0\0\x18\x61\0\0\0\0\
+\0\0\0\0\0\0\xf0\x11\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xe8\x11\0\0\x7b\x01\0\0\0\0\0\0\x61\x60\x08\0\0\
+\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x88\x11\0\0\x63\x01\0\0\0\0\0\0\x61\x60\x0c\
+\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x8c\x11\0\0\x63\x01\0\0\0\0\0\0\x79\x60\
+\x10\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\x11\0\0\x7b\x01\0\0\0\0\0\0\x61\
+\xa0\x78\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb8\x11\0\0\x63\x01\0\0\0\0\0\
+\0\x18\x61\0\0\0\0\0\0\0\0\0\0\0\x12\0\0\xb7\x02\0\0\x14\0\0\0\xb7\x03\0\0\x0c\
+\0\0\0\xb7\x04\0\0\0\0\0\0\x85\0\0\0\xa7\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\x5c\
+\xff\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x70\x11\0\0\x63\x70\x6c\0\0\0\0\0\x77\
+\x07\0\0\x20\0\0\0\x63\x70\x70\0\0\0\0\0\x18\x68\0\0\0\0\0\0\0\0\0\0\xc0\x0c\0\
+\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x18\x12\0\0\xb7\x02\0\0\x11\0\0\0\xb7\x03\0\0\
+\x0c\0\0\0\xb7\x04\0\0\0\0\0\0\x85\0\0\0\xa7\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\
+\x4d\xff\0\0\0\0\x75\x07\x03\0\0\0\0\0\x62\x08\x04\0\0\0\0\0\x6a\x08\x02\0\0\0\
+\0\0\x05\0\x0a\0\0\0\0\0\x63\x78\x04\0\0\0\0\0\xbf\x79\0\0\0\0\0\0\x77\x09\0\0\
+\x20\0\0\0\x55\x09\x02\0\0\0\0\0\x6a\x08\x02\0\0\0\0\0\x05\0\x04\0\0\0\0\0\x18\
+\x60\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\x63\x90\0\0\0\0\0\0\x6a\x08\x02\0\x40\0\0\0\
+\xb7\x01\0\0\x05\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x70\x11\0\0\xb7\x03\0\0\x8c\
+\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\
+\x01\0\0\x61\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\
+\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xe0\x11\0\0\x61\x01\0\0\0\0\0\0\xd5\
+\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\xc5\x07\x2c\xff\0\0\
+\0\0\x63\x7a\x80\xff\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x30\x12\0\0\x18\x61\0\
+\0\0\0\0\0\0\0\0\0\x60\x13\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\
+\x38\x12\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x58\x13\0\0\x7b\x01\0\0\0\0\0\0\x18\
+\x60\0\0\0\0\0\0\0\0\0\0\x98\x12\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa0\x13\0\0\
+\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xa0\x12\0\0\x18\x61\0\0\0\0\0\
+\0\0\0\0\0\xb0\x13\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x10\x13\
+\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xd0\x13\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xc8\x13\0\0\x7b\x01\0\0\0\0\
+\0\0\x61\x60\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x68\x13\0\0\x63\x01\0\0\
+\0\0\0\0\x61\x60\x0c\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x6c\x13\0\0\x63\x01\
+\0\0\0\0\0\0\x79\x60\x10\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x70\x13\0\0\x7b\
+\x01\0\0\0\0\0\0\x61\xa0\x78\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x98\x13\0\
+\0\x63\x01\0\0\0\0\0\0\xb7\x01\0\0\x05\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x50\
+\x13\0\0\xb7\x03\0\0\x8c\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\
+\xf6\xfe\0\0\0\0\x63\x7a\x84\xff\0\0\0\0\x61\xa1\x78\xff\0\0\0\0\xd5\x01\x02\0\
+\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa0\x80\xff\0\0\0\0\x63\
+\x06\x28\0\0\0\0\0\x61\xa0\x84\xff\0\0\0\0\x63\x06\x2c\0\0\0\0\0\x18\x61\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\x61\x10\0\0\0\0\0\0\x63\x06\x18\0\0\0\0\0\xb7\0\0\0\0\0\
+\0\0\x95\0\0\0\0\0\0\0",
+},
+
+#endif /* __B0003G0001V05F3P0405_XK24_HIDBPF_H__ */
diff --git a/drivers/hid/bpf/progs/hid_bpf_progs.h b/drivers/hid/bpf/progs/hid_bpf_progs.h
index 430e0fb47484..be911c3c725b 100644
--- a/drivers/hid/bpf/progs/hid_bpf_progs.h
+++ b/drivers/hid/bpf/progs/hid_bpf_progs.h
@@ -28,9 +28,9 @@ struct hid_bpf_prog {
 struct hid_bpf_object {
 	struct hid_device_id id;
 	unsigned int map_cnt;
-	struct hid_bpf_map maps[0];
+	struct hid_bpf_map maps[1];
 	unsigned int prog_cnt;
-	struct hid_bpf_prog progs[0];
+	struct hid_bpf_prog progs[2];
 	unsigned int probe;
 	unsigned int rodata;
 	unsigned int data_sz;
@@ -41,6 +41,7 @@ struct hid_bpf_object {
 
 static struct hid_bpf_object hid_objects[] = {
 
+#include "b0003g0001v05F3p0405-xk24.hidbpf.h"
 
 	{ },
 };
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC hid v1 06/10] selftests: hid: add vmtest.sh
  2022-11-24 15:15 [RFC hid v1 00/10] HID-BPF: add support for in-tree BPF programs Benjamin Tissoires
                   ` (4 preceding siblings ...)
  2022-11-24 15:15 ` [RFC hid v1 05/10] HID: add report descriptor override for the X-Keys XK24 Benjamin Tissoires
@ 2022-11-24 15:15 ` Benjamin Tissoires
  2022-11-24 15:16 ` [RFC hid v1 07/10] selftests: hid: Add a variant parameter so we can emulate specific devices Benjamin Tissoires
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Benjamin Tissoires @ 2022-11-24 15:15 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Dmitry Torokhov
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf, Benjamin Tissoires

Similar-ish in many points from the script in selftests/bpf, with a few
differences:
- relies on boot2container instead of a plain qemu image (meaning that
  we can take any container in a registry as a base)
- runs in the hid selftest dir, and such uses the test program from there

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 tools/testing/selftests/hid/.gitignore    |   1 +
 tools/testing/selftests/hid/config.common | 241 +++++++++++++++++++
 tools/testing/selftests/hid/config.x86_64 |   4 +
 tools/testing/selftests/hid/vmtest.sh     | 280 ++++++++++++++++++++++
 4 files changed, 526 insertions(+)
 create mode 100644 tools/testing/selftests/hid/config.common
 create mode 100644 tools/testing/selftests/hid/config.x86_64
 create mode 100755 tools/testing/selftests/hid/vmtest.sh

diff --git a/tools/testing/selftests/hid/.gitignore b/tools/testing/selftests/hid/.gitignore
index a462ca6ab2c0..995af0670f69 100644
--- a/tools/testing/selftests/hid/.gitignore
+++ b/tools/testing/selftests/hid/.gitignore
@@ -2,3 +2,4 @@ bpftool
 *.skel.h
 /tools
 hid_bpf
+results
diff --git a/tools/testing/selftests/hid/config.common b/tools/testing/selftests/hid/config.common
new file mode 100644
index 000000000000..0617275d93cc
--- /dev/null
+++ b/tools/testing/selftests/hid/config.common
@@ -0,0 +1,241 @@
+CONFIG_9P_FS_POSIX_ACL=y
+CONFIG_9P_FS_SECURITY=y
+CONFIG_9P_FS=y
+CONFIG_AUDIT=y
+CONFIG_BINFMT_MISC=y
+CONFIG_BLK_CGROUP_IOLATENCY=y
+CONFIG_BLK_CGROUP=y
+CONFIG_BLK_DEV_BSGLIB=y
+CONFIG_BLK_DEV_IO_TRACE=y
+CONFIG_BLK_DEV_RAM_SIZE=16384
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_THROTTLING=y
+CONFIG_BONDING=y
+CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y
+CONFIG_BOOTTIME_TRACING=y
+CONFIG_BSD_DISKLABEL=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_CFS_BANDWIDTH=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_HUGETLB=y
+CONFIG_CGROUP_NET_CLASSID=y
+CONFIG_CGROUP_NET_PRIO=y
+CONFIG_CGROUP_PERF=y
+CONFIG_CGROUP_PIDS=y
+CONFIG_CGROUP_RDMA=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_WRITEBACK=y
+CONFIG_CMA_AREAS=7
+CONFIG_CMA=y
+CONFIG_COMPAT_32BIT_TIME=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_IDLE_GOV_LADDER=y
+CONFIG_CPUSETS=y
+CONFIG_CRC_T10DIF=y
+CONFIG_CRYPTO_BLAKE2B=y
+CONFIG_CRYPTO_DEV_VIRTIO=y
+CONFIG_CRYPTO_SEQIV=y
+CONFIG_CRYPTO_XXHASH=y
+CONFIG_DCB=y
+CONFIG_DEBUG_ATOMIC_SLEEP=y
+CONFIG_DEBUG_CREDENTIALS=y
+CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_DEFAULT_FQ_CODEL=y
+CONFIG_DEFAULT_RENO=y
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_DEVTMPFS=y
+CONFIG_DMA_CMA=y
+CONFIG_DNS_RESOLVER=y
+CONFIG_EFI_STUB=y
+CONFIG_EFI=y
+CONFIG_EXPERT=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_EXT4_FS=y
+CONFIG_FAIL_FUNCTION=y
+CONFIG_FAULT_INJECTION_DEBUG_FS=y
+CONFIG_FAULT_INJECTION=y
+CONFIG_FB_MODE_HELPERS=y
+CONFIG_FB_TILEBLITTING=y
+CONFIG_FB_VESA=y
+CONFIG_FB=y
+CONFIG_FONT_8x16=y
+CONFIG_FONT_MINI_4x6=y
+CONFIG_FONTS=y
+CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
+CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FUSE_FS=y
+CONFIG_FW_LOADER_USER_HELPER=y
+CONFIG_GART_IOMMU=y
+CONFIG_GENERIC_PHY=y
+CONFIG_HARDLOCKUP_DETECTOR=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_HPET=y
+CONFIG_HUGETLBFS=y
+CONFIG_HUGETLB_PAGE=y
+CONFIG_HWPOISON_INJECT=y
+CONFIG_HZ_1000=y
+CONFIG_INET=y
+CONFIG_INTEL_POWERCLAMP=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_NAT=y
+CONFIG_IP6_NF_TARGET_MASQUERADE=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_SEG6_LWTUNNEL=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_IRQ_POLL=y
+CONFIG_JUMP_LABEL=y
+CONFIG_KARMA_PARTITION=y
+CONFIG_KEXEC=y
+CONFIG_KPROBES=y
+CONFIG_KSM=y
+CONFIG_LEGACY_VSYSCALL_NONE=y
+CONFIG_LOG_BUF_SHIFT=21
+CONFIG_LOG_CPU_MAX_BUF_SHIFT=0
+CONFIG_LOGO=y
+CONFIG_LSM="selinux,bpf,integrity"
+CONFIG_MAC_PARTITION=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_MCORE2=y
+CONFIG_MEMCG=y
+CONFIG_MEMORY_FAILURE=y
+CONFIG_MINIX_SUBPARTITION=y
+CONFIG_MODULES=y
+CONFIG_NAMESPACES=y
+CONFIG_NET_9P_VIRTIO=y
+CONFIG_NET_9P=y
+CONFIG_NET_ACT_BPF=y
+CONFIG_NET_CLS_CGROUP=y
+CONFIG_NETDEVICES=y
+CONFIG_NET_EMATCH=y
+CONFIG_NETFILTER_NETLINK_LOG=y
+CONFIG_NETFILTER_NETLINK_QUEUE=y
+CONFIG_NETFILTER_XTABLES=y
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_NAT=y
+CONFIG_NETFILTER_XT_TARGET_MASQUERADE=y
+CONFIG_NET_IPGRE_BROADCAST=y
+CONFIG_NET_L3_MASTER_DEV=y
+CONFIG_NETLABEL=y
+CONFIG_NET_SCH_DEFAULT=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_FQ_CODEL=y
+CONFIG_NET_TC_SKB_EXT=y
+CONFIG_NET_VRF=y
+CONFIG_NET=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_NAT_MASQUERADE=y
+CONFIG_NF_NAT=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NO_HZ=y
+CONFIG_NR_CPUS=128
+CONFIG_NUMA_BALANCING=y
+CONFIG_NUMA=y
+CONFIG_NVMEM=y
+CONFIG_OSF_PARTITION=y
+CONFIG_OVERLAY_FS_INDEX=y
+CONFIG_OVERLAY_FS_METACOPY=y
+CONFIG_OVERLAY_FS_XINO_AUTO=y
+CONFIG_OVERLAY_FS=y
+CONFIG_PACKET=y
+CONFIG_PANIC_ON_OOPS=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_PCI_IOV=y
+CONFIG_PCI_MSI=y
+CONFIG_PCI=y
+CONFIG_PHYSICAL_ALIGN=0x1000000
+CONFIG_POSIX_MQUEUE=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_PREEMPT=y
+CONFIG_PRINTK_TIME=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROFILING=y
+CONFIG_PROVE_LOCKING=y
+CONFIG_PTP_1588_CLOCK=y
+CONFIG_RC_DEVICES=y
+CONFIG_RC_LOOPBACK=y
+CONFIG_RCU_CPU_STALL_TIMEOUT=60
+CONFIG_SCHED_STACK_END_CHECK=y
+CONFIG_SCHEDSTATS=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY_SELINUX=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_DETECT_IRQ=y
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_NR_UARTS=32
+CONFIG_SERIAL_8250_RSA=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_NONSTANDARD=y
+CONFIG_SERIO_LIBPS2=y
+CONFIG_SGI_PARTITION=y
+CONFIG_SMP=y
+CONFIG_SOCK_CGROUP_DATA=y
+CONFIG_SOLARIS_X86_PARTITION=y
+CONFIG_SUN_PARTITION=y
+CONFIG_SYNC_FILE=y
+CONFIG_SYSVIPC=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_MD5SIG=y
+CONFIG_TLS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_TMPFS=y
+CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
+CONFIG_TRANSPARENT_HUGEPAGE=y
+CONFIG_TUN=y
+CONFIG_UNIXWARE_DISKLABEL=y
+CONFIG_UNIX=y
+CONFIG_USER_NS=y
+CONFIG_VALIDATE_FS_PARSER=y
+CONFIG_VETH=y
+CONFIG_VIRT_DRIVERS=y
+CONFIG_VIRTIO_BALLOON=y
+CONFIG_VIRTIO_BLK=y
+CONFIG_VIRTIO_CONSOLE=y
+CONFIG_VIRTIO_FS=y
+CONFIG_VIRTIO_NET=y
+CONFIG_VIRTIO_PCI=y
+CONFIG_VLAN_8021Q=y
+CONFIG_XFRM_SUB_POLICY=y
+CONFIG_XFRM_USER=y
+CONFIG_ZEROPLUS_FF=y
diff --git a/tools/testing/selftests/hid/config.x86_64 b/tools/testing/selftests/hid/config.x86_64
new file mode 100644
index 000000000000..a8721f403c21
--- /dev/null
+++ b/tools/testing/selftests/hid/config.x86_64
@@ -0,0 +1,4 @@
+CONFIG_X86_ACPI_CPUFREQ=y
+CONFIG_X86_CPUID=y
+CONFIG_X86_MSR=y
+CONFIG_X86_POWERNOW_K8=y
diff --git a/tools/testing/selftests/hid/vmtest.sh b/tools/testing/selftests/hid/vmtest.sh
new file mode 100755
index 000000000000..f124cf6b0d0f
--- /dev/null
+++ b/tools/testing/selftests/hid/vmtest.sh
@@ -0,0 +1,280 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+set -u
+set -e
+
+# This script currently only works for x86_64
+ARCH="$(uname -m)"
+case "${ARCH}" in
+x86_64)
+	QEMU_BINARY=qemu-system-x86_64
+	BZIMAGE="arch/x86/boot/bzImage"
+	;;
+*)
+	echo "Unsupported architecture"
+	exit 1
+	;;
+esac
+DEFAULT_COMMAND="./hid_bpf"
+OUTPUT_DIR="$PWD/results"
+KCONFIG_REL_PATHS=("tools/testing/selftests/hid/config" "tools/testing/selftests/hid/config.common" "tools/testing/selftests/hid/config.${ARCH}")
+B2C_URL="https://gitlab.freedesktop.org/mupuf/boot2container/-/raw/master/vm2c.py"
+NUM_COMPILE_JOBS="$(nproc)"
+LOG_FILE_BASE="$(date +"hid_selftests.%Y-%m-%d_%H-%M-%S")"
+LOG_FILE="${LOG_FILE_BASE}.log"
+EXIT_STATUS_FILE="${LOG_FILE_BASE}.exit_status"
+
+usage()
+{
+	cat <<EOF
+Usage: $0 [-i] [-s] [-d <output_dir>] -- [<command>]
+
+<command> is the command you would normally run when you are in
+tools/testing/selftests/bpf. e.g:
+
+	$0 -- ./hid_bpf
+
+If no command is specified and a debug shell (-s) is not requested,
+"${DEFAULT_COMMAND}" will be run by default.
+
+If you build your kernel using KBUILD_OUTPUT= or O= options, these
+can be passed as environment variables to the script:
+
+  O=<kernel_build_path> $0 -- ./hid_bpf
+
+or
+
+  KBUILD_OUTPUT=<kernel_build_path> $0 -- ./hid_bpf
+
+Options:
+
+	-u)		Update the boot2container script to a newer version.
+	-d)		Update the output directory (default: ${OUTPUT_DIR})
+	-j)		Number of jobs for compilation, similar to -j in make
+			(default: ${NUM_COMPILE_JOBS})
+	-s)		Instead of powering off the VM, start an interactive
+			shell. If <command> is specified, the shell runs after
+			the command finishes executing
+EOF
+}
+
+download()
+{
+	local file="$1"
+
+	echo "Downloading $file..." >&2
+	curl -Lsf "$file" -o "${@:2}"
+}
+
+recompile_kernel()
+{
+	local kernel_checkout="$1"
+	local make_command="$2"
+
+	cd "${kernel_checkout}"
+
+	${make_command} olddefconfig
+	${make_command}
+}
+
+update_selftests()
+{
+	local kernel_checkout="$1"
+	local selftests_dir="${kernel_checkout}/tools/testing/selftests/hid"
+
+	cd "${selftests_dir}"
+	${make_command}
+}
+
+run_vm()
+{
+	local b2c="$1"
+	local kernel_bzimage="$2"
+	local command="$3"
+	local post_command=""
+
+	if ! which "${QEMU_BINARY}" &> /dev/null; then
+		cat <<EOF
+Could not find ${QEMU_BINARY}
+Please install qemu or set the QEMU_BINARY environment variable.
+EOF
+		exit 1
+	fi
+
+	# alpine (used in post-container requires the PATH to have /bin
+	export PATH=$PATH:/bin
+
+	if [[ "${debug_shell}" != "yes" ]]
+	then
+		touch ${OUTPUT_DIR}/${LOG_FILE}
+		command="set -o pipefail ; ${command} 2>&1 | tee ${OUTPUT_DIR}/${LOG_FILE}"
+		post_command="cat ${OUTPUT_DIR}/${LOG_FILE}"
+	fi
+
+	set +e
+	$b2c --command "${command}" \
+	     --kernel ${kernel_bzimage} \
+	     --workdir ${OUTPUT_DIR} \
+	     --image registry.fedoraproject.org/fedora:36
+
+	echo $? > ${OUTPUT_DIR}/${EXIT_STATUS_FILE}
+
+	set -e
+
+	${post_command}
+}
+
+is_rel_path()
+{
+	local path="$1"
+
+	[[ ${path:0:1} != "/" ]]
+}
+
+do_update_kconfig()
+{
+	local kernel_checkout="$1"
+	local kconfig_file="$2"
+
+	rm -f "$kconfig_file" 2> /dev/null
+
+	for config in "${KCONFIG_REL_PATHS[@]}"; do
+		local kconfig_src="${kernel_checkout}/${config}"
+		cat "$kconfig_src" >> "$kconfig_file"
+	done
+}
+
+update_kconfig()
+{
+	local kernel_checkout="$1"
+	local kconfig_file="$2"
+
+	if [[ -f "${kconfig_file}" ]]; then
+		local local_modified="$(stat -c %Y "${kconfig_file}")"
+
+		for config in "${KCONFIG_REL_PATHS[@]}"; do
+			local kconfig_src="${kernel_checkout}/${config}"
+			local src_modified="$(stat -c %Y "${kconfig_src}")"
+			# Only update the config if it has been updated after the
+			# previously cached config was created. This avoids
+			# unnecessarily compiling the kernel and selftests.
+			if [[ "${src_modified}" -gt "${local_modified}" ]]; then
+				do_update_kconfig "$kernel_checkout" "$kconfig_file"
+				# Once we have found one outdated configuration
+				# there is no need to check other ones.
+				break
+			fi
+		done
+	else
+		do_update_kconfig "$kernel_checkout" "$kconfig_file"
+	fi
+}
+
+main()
+{
+	local script_dir="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
+	local kernel_checkout=$(realpath "${script_dir}"/../../../../)
+	# By default the script searches for the kernel in the checkout directory but
+	# it also obeys environment variables O= and KBUILD_OUTPUT=
+	local kernel_bzimage="${kernel_checkout}/${BZIMAGE}"
+	local command="${DEFAULT_COMMAND}"
+	local update_b2c="no"
+	local debug_shell="no"
+
+	while getopts ':hsud:j:' opt; do
+		case ${opt} in
+		u)
+			update_b2c="yes"
+			;;
+		d)
+			OUTPUT_DIR="$OPTARG"
+			;;
+		j)
+			NUM_COMPILE_JOBS="$OPTARG"
+			;;
+		s)
+			command="/bin/sh"
+			debug_shell="yes"
+			;;
+		h)
+			usage
+			exit 0
+			;;
+		\? )
+			echo "Invalid Option: -$OPTARG"
+			usage
+			exit 1
+			;;
+		: )
+			echo "Invalid Option: -$OPTARG requires an argument"
+			usage
+			exit 1
+			;;
+		esac
+	done
+	shift $((OPTIND -1))
+
+	# trap 'catch "$?"' EXIT
+
+	if [[ "${debug_shell}" == "no" ]]; then
+		if [[ $# -eq 0 ]]; then
+			echo "No command specified, will run ${DEFAULT_COMMAND} in the vm"
+		else
+			command="$@"
+
+			if [[ "${command}" == "/bin/bash" || "${command}" == "bash" ]]
+			then
+				debug_shell="yes"
+			fi
+		fi
+	fi
+
+	local kconfig_file="${OUTPUT_DIR}/latest.config"
+	local make_command="make -j ${NUM_COMPILE_JOBS} KCONFIG_CONFIG=${kconfig_file}"
+
+	# Figure out where the kernel is being built.
+	# O takes precedence over KBUILD_OUTPUT.
+	if [[ "${O:=""}" != "" ]]; then
+		if is_rel_path "${O}"; then
+			O="$(realpath "${PWD}/${O}")"
+		fi
+		kernel_bzimage="${O}/${BZIMAGE}"
+		make_command="${make_command} O=${O}"
+	elif [[ "${KBUILD_OUTPUT:=""}" != "" ]]; then
+		if is_rel_path "${KBUILD_OUTPUT}"; then
+			KBUILD_OUTPUT="$(realpath "${PWD}/${KBUILD_OUTPUT}")"
+		fi
+		kernel_bzimage="${KBUILD_OUTPUT}/${BZIMAGE}"
+		make_command="${make_command} KBUILD_OUTPUT=${KBUILD_OUTPUT}"
+	fi
+
+	local b2c="${OUTPUT_DIR}/vm2c.py"
+
+	echo "Output directory: ${OUTPUT_DIR}"
+
+	mkdir -p "${OUTPUT_DIR}"
+	update_kconfig "${kernel_checkout}" "${kconfig_file}"
+
+	recompile_kernel "${kernel_checkout}" "${make_command}"
+
+	if [[ "${update_b2c}" == "no" && ! -f "${b2c}" ]]; then
+		echo "vm2c script not found in ${b2c}"
+		update_b2c="yes"
+	fi
+
+	if [[ "${update_b2c}" == "yes" ]]; then
+		download $B2C_URL $b2c
+		chmod +x $b2c
+	fi
+
+	update_selftests "${kernel_checkout}" "${make_command}"
+	run_vm $b2c "${kernel_bzimage}" "${command}"
+	if [[ "${debug_shell}" != "yes" ]]; then
+		echo "Logs saved in ${OUTPUT_DIR}/${LOG_FILE}"
+	fi
+
+	exit $(cat ${OUTPUT_DIR}/${EXIT_STATUS_FILE})
+}
+
+main "$@"
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC hid v1 07/10] selftests: hid: Add a variant parameter so we can emulate specific devices
  2022-11-24 15:15 [RFC hid v1 00/10] HID-BPF: add support for in-tree BPF programs Benjamin Tissoires
                   ` (5 preceding siblings ...)
  2022-11-24 15:15 ` [RFC hid v1 06/10] selftests: hid: add vmtest.sh Benjamin Tissoires
@ 2022-11-24 15:16 ` Benjamin Tissoires
  2022-11-24 15:16 ` [RFC hid v1 08/10] selftests: hid: add XK-24 tests Benjamin Tissoires
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Benjamin Tissoires @ 2022-11-24 15:16 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Dmitry Torokhov
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf, Benjamin Tissoires

When testing with in-kernel bpf programs, we need to emulate a specific
device, because otherwise we won't be loading the matching bpf program.

Add a variant parameter to the fixture hid_bpf so that we can re-use it
later with different devices.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 tools/testing/selftests/hid/hid_bpf.c | 75 +++++++++++++++++++--------
 1 file changed, 53 insertions(+), 22 deletions(-)

diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c
index 6615c26fb5dd..738ddb2c6a62 100644
--- a/tools/testing/selftests/hid/hid_bpf.c
+++ b/tools/testing/selftests/hid/hid_bpf.c
@@ -114,6 +114,15 @@ static pthread_cond_t uhid_started = PTHREAD_COND_INITIALIZER;
 /* no need to protect uhid_stopped, only one thread accesses it */
 static bool uhid_stopped;
 
+struct hid_device_id {
+	int bus;
+	int vendor;
+	int product;
+	int version;
+	unsigned char *rdesc;
+	const size_t rdesc_sz;
+};
+
 static int uhid_write(struct __test_metadata *_metadata, int fd, const struct uhid_event *ev)
 {
 	ssize_t ret;
@@ -131,7 +140,8 @@ static int uhid_write(struct __test_metadata *_metadata, int fd, const struct uh
 	}
 }
 
-static int uhid_create(struct __test_metadata *_metadata, int fd, int rand_nb)
+static int uhid_create(struct __test_metadata *_metadata, const struct hid_device_id *variant,
+		       int fd, int rand_nb)
 {
 	struct uhid_event ev;
 	char buf[25];
@@ -141,12 +151,12 @@ static int uhid_create(struct __test_metadata *_metadata, int fd, int 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.rd_data = variant->rdesc;
+	ev.u.create.rd_size = variant->rdesc_sz;
+	ev.u.create.bus = variant->bus;
+	ev.u.create.vendor = variant->vendor;
+	ev.u.create.product = variant->product;
+	ev.u.create.version = variant->version;
 	ev.u.create.country = 0;
 
 	sprintf(buf, "%d", rand_nb);
@@ -299,7 +309,8 @@ static int uhid_send_event(struct __test_metadata *_metadata, int fd, __u8 *buf,
 	return uhid_write(_metadata, fd, &ev);
 }
 
-static int setup_uhid(struct __test_metadata *_metadata, int rand_nb)
+static int setup_uhid(struct __test_metadata *_metadata, const struct hid_device_id *variant,
+		      int rand_nb)
 {
 	int fd;
 	const char *path = "/dev/uhid";
@@ -308,7 +319,7 @@ static int setup_uhid(struct __test_metadata *_metadata, int rand_nb)
 	fd = open(path, O_RDWR | O_CLOEXEC);
 	ASSERT_GE(fd, 0) TH_LOG("open uhid-cdev failed; %d", fd);
 
-	ret = uhid_create(_metadata, fd, rand_nb);
+	ret = uhid_create(_metadata, variant, fd, rand_nb);
 	ASSERT_EQ(0, ret) {
 		TH_LOG("create uhid device failed: %d", ret);
 		close(fd);
@@ -317,15 +328,19 @@ static int setup_uhid(struct __test_metadata *_metadata, int rand_nb)
 	return fd;
 }
 
-static bool match_sysfs_device(int dev_id, const char *workdir, struct dirent *dir)
+static bool match_sysfs_device(int dev_id, const struct hid_device_id *variant,
+			       const char *workdir, struct dirent *dir)
 {
-	const char *target = "0003:0001:0A37.*";
+	char target[17] = "0003:0001:0A37.*";
 	char phys[512];
 	char uevent[1024];
 	char temp[512];
 	int fd, nread;
 	bool found = false;
 
+	snprintf(target, sizeof(target), "0003:%04X:%04X.*",
+		 variant->vendor, variant->product);
+
 	if (fnmatch(target, dir->d_name, 0))
 		return false;
 
@@ -347,7 +362,7 @@ static bool match_sysfs_device(int dev_id, const char *workdir, struct dirent *d
 	return found;
 }
 
-static int get_hid_id(int dev_id)
+static int get_hid_id(int dev_id, const struct hid_device_id *variant)
 {
 	const char *workdir = "/sys/devices/virtual/misc/uhid";
 	const char *str_id;
@@ -362,7 +377,7 @@ static int get_hid_id(int dev_id)
 		d = opendir(workdir);
 		if (d) {
 			while ((dir = readdir(d)) != NULL) {
-				if (!match_sysfs_device(dev_id, workdir, dir))
+				if (!match_sysfs_device(dev_id, variant, workdir, dir))
 					continue;
 
 				str_id = dir->d_name + sizeof("0003:0001:0A37.");
@@ -379,7 +394,7 @@ static int get_hid_id(int dev_id)
 	return found;
 }
 
-static int get_hidraw(int dev_id)
+static int get_hidraw(int dev_id, const struct hid_device_id *variant)
 {
 	const char *workdir = "/sys/devices/virtual/misc/uhid";
 	char sysfs[1024];
@@ -396,7 +411,7 @@ static int get_hidraw(int dev_id)
 			continue;
 
 		while ((dir = readdir(d)) != NULL) {
-			if (!match_sysfs_device(dev_id, workdir, dir))
+			if (!match_sysfs_device(dev_id, variant, workdir, dir))
 				continue;
 
 			sprintf(sysfs, "%s/%s/hidraw", workdir, dir->d_name);
@@ -423,12 +438,12 @@ static int get_hidraw(int dev_id)
 	return found;
 }
 
-static int open_hidraw(int dev_id)
+static int open_hidraw(int dev_id, const struct hid_device_id *variant)
 {
 	int hidraw_number;
 	char hidraw_path[64] = { 0 };
 
-	hidraw_number = get_hidraw(dev_id);
+	hidraw_number = get_hidraw(dev_id, variant);
 	if (hidraw_number < 0)
 		return hidraw_number;
 
@@ -445,6 +460,22 @@ FIXTURE(hid_bpf) {
 	pthread_t tid;
 	struct hid *skel;
 };
+
+FIXTURE_VARIANT(hid_bpf) {
+	const struct hid_device_id id;
+};
+
+FIXTURE_VARIANT_ADD(hid_bpf, generic_device) {
+	.id = {
+		.bus = BUS_USB,
+		.vendor = 0x0001,
+		.product = 0x0a37,
+		.version = 0,
+		.rdesc = rdesc,
+		.rdesc_sz = sizeof(rdesc),
+	},
+};
+
 static void detach_bpf(FIXTURE_DATA(hid_bpf) * self)
 {
 	if (self->hidraw_fd)
@@ -478,10 +509,10 @@ FIXTURE_SETUP(hid_bpf)
 
 	self->dev_id = rand() % 1024;
 
-	self->uhid_fd = setup_uhid(_metadata, self->dev_id);
+	self->uhid_fd = setup_uhid(_metadata, &variant->id, self->dev_id);
 
-	/* locate the uev, self, variant);ent file of the created device */
-	self->hid_id = get_hid_id(self->dev_id);
+	/* locate the file of the created device */
+	self->hid_id = get_hid_id(self->dev_id, &variant->id);
 	ASSERT_GT(self->hid_id, 0)
 		TEARDOWN_LOG("Could not locate uhid device id: %d", self->hid_id);
 
@@ -546,7 +577,7 @@ static void load_programs(const struct test_program programs[],
 		ASSERT_OK(args.retval) TH_LOG("attach_hid(%s): %d", programs[i].name, args.retval);
 	}
 
-	self->hidraw_fd = open_hidraw(self->dev_id);
+	self->hidraw_fd = open_hidraw(self->dev_id, &variant->id);
 	ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
 }
 
@@ -644,7 +675,7 @@ TEST_F(hid_bpf, test_attach_detach)
 	/* detach the program */
 	detach_bpf(self);
 
-	self->hidraw_fd = open_hidraw(self->dev_id);
+	self->hidraw_fd = open_hidraw(self->dev_id, &variant->id);
 	ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
 
 	/* inject another event */
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC hid v1 08/10] selftests: hid: add XK-24 tests
  2022-11-24 15:15 [RFC hid v1 00/10] HID-BPF: add support for in-tree BPF programs Benjamin Tissoires
                   ` (6 preceding siblings ...)
  2022-11-24 15:16 ` [RFC hid v1 07/10] selftests: hid: Add a variant parameter so we can emulate specific devices Benjamin Tissoires
@ 2022-11-24 15:16 ` Benjamin Tissoires
  2022-11-24 15:16 ` [RFC hid v1 09/10] selftests: hid: ensure the program is correctly pinned Benjamin Tissoires
  2022-11-24 15:16 ` [RFC hid v1 10/10] wip: vmtest aarch64 Benjamin Tissoires
  9 siblings, 0 replies; 14+ messages in thread
From: Benjamin Tissoires @ 2022-11-24 15:16 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Dmitry Torokhov
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf, Benjamin Tissoires

this is the first device to be added in the kernel with a bpf program
associated, so we better use it to ensure we get the things right.

We define another fixture that will reuse everything from hid_bpf except
for the variant parameters. And then we can use that easily in a new
dedicated test.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 tools/testing/selftests/hid/hid_bpf.c | 72 +++++++++++++++++++++++++++
 1 file changed, 72 insertions(+)

diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c
index 738ddb2c6a62..4bdd1cfa7d13 100644
--- a/tools/testing/selftests/hid/hid_bpf.c
+++ b/tools/testing/selftests/hid/hid_bpf.c
@@ -520,6 +520,51 @@ FIXTURE_SETUP(hid_bpf)
 	ASSERT_EQ(0, err) TEARDOWN_LOG("could not start udev listener: %d", err);
 }
 
+static unsigned char xk24_rdesc[] = {
+	0x05, 0x0c,                    // Usage Page (Consumer Devices)       0
+	0x09, 0x01,                    // Usage (Consumer Control)            2
+	0xa1, 0x01,                    // Collection (Application)            4
+	0xa1, 0x02,                    //  Collection (Logical)               6
+	0x05, 0x08,                    //   Usage Page (LEDs)                 8
+	0x09, 0x4b,                    //   Usage (Generic Indicator)         10
+	0x75, 0x08,                    //   Report Size (8)                   12
+	0x95, 0x23,                    //   Report Count (35)                 14
+	0x91, 0x02,                    //   Output (Data,Var,Abs)             16
+	0xc0,                          //  End Collection                     18
+	0xa1, 0x02,                    //  Collection (Logical)               19
+	0x05, 0x09,                    //   Usage Page (Button)               21
+	0x09, 0x4b,                    //   Usage (Vendor Usage 0x4b)         23
+	0x75, 0x08,                    //   Report Size (8)                   25
+	0x95, 0x20,                    //   Report Count (32)                 27
+	0x81, 0x02,                    //   Input (Data,Var,Abs)              29
+	0xc0,                          //  End Collection                     31
+	0xc0,                          // End Collection                      32
+};
+
+#define _test_data_hid_bpf_xkeys _test_data_hid_bpf
+#define _fixture_variant_hid_bpf_xkeys _fixture_variant_hid_bpf
+FIXTURE(hid_bpf_xkeys);
+FIXTURE_VARIANT(hid_bpf_xkeys);
+FIXTURE_VARIANT_ADD(hid_bpf_xkeys, xk24) {
+	.id = {
+		.bus = BUS_USB,
+		.vendor = 0x05F3,
+		.product = 0x0405,
+		.version = 0,
+		.rdesc = xk24_rdesc,
+		.rdesc_sz = sizeof(xk24_rdesc),
+	},
+};
+
+FIXTURE_SETUP(hid_bpf_xkeys)
+{
+	hid_bpf_setup(_metadata, self, variant);
+}
+FIXTURE_TEARDOWN(hid_bpf_xkeys)
+{
+	hid_bpf_teardown(_metadata, self, variant);
+}
+
 struct test_program {
 	const char *name;
 	int insert_head;
@@ -846,6 +891,33 @@ TEST_F(hid_bpf, test_rdesc_fixup)
 	ASSERT_EQ(rpt_desc.value[4], 0x42);
 }
 
+/*
+ * Attach an emulated XK24, which has an in-tree eBPF program, and ensure
+ * we got it loaded.
+ */
+TEST_F(hid_bpf_xkeys, test_xk24)
+{
+	struct hidraw_report_descriptor rpt_desc = {0};
+	int err, desc_size;
+
+	/* open the kernel provided hidraw node */
+	self->hidraw_fd = open_hidraw(self->dev_id, &variant->id);
+	ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
+
+	/* read the exposed report descriptor from hidraw */
+	err = ioctl(self->hidraw_fd, HIDIOCGRDESCSIZE, &desc_size);
+	ASSERT_GE(err, 0) TH_LOG("error while reading HIDIOCGRDESCSIZE: %d", err);
+
+	/* ensure the new size of the rdesc is bigger than the old one */
+	ASSERT_GT(desc_size, sizeof(xk24_rdesc));
+
+	rpt_desc.size = desc_size;
+	err = ioctl(self->hidraw_fd, HIDIOCGRDESC, &rpt_desc);
+	ASSERT_GE(err, 0) TH_LOG("error while reading HIDIOCGRDESC: %d", err);
+
+	ASSERT_EQ(rpt_desc.value[21], 0x09);
+}
+
 static int libbpf_print_fn(enum libbpf_print_level level,
 			   const char *format, va_list args)
 {
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC hid v1 09/10] selftests: hid: ensure the program is correctly pinned
  2022-11-24 15:15 [RFC hid v1 00/10] HID-BPF: add support for in-tree BPF programs Benjamin Tissoires
                   ` (7 preceding siblings ...)
  2022-11-24 15:16 ` [RFC hid v1 08/10] selftests: hid: add XK-24 tests Benjamin Tissoires
@ 2022-11-24 15:16 ` Benjamin Tissoires
  2022-11-24 15:16 ` [RFC hid v1 10/10] wip: vmtest aarch64 Benjamin Tissoires
  9 siblings, 0 replies; 14+ messages in thread
From: Benjamin Tissoires @ 2022-11-24 15:16 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Dmitry Torokhov
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf, Benjamin Tissoires

Turns out that if bpffs was not mounted, the test was silently passing.

So ensure it passes, but also force the mount of the bpffs in vmtest.sh
so we get passing results.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 tools/testing/selftests/hid/hid_bpf.c | 3 ++-
 tools/testing/selftests/hid/vmtest.sh | 4 +++-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c
index 4bdd1cfa7d13..3ae544bf24fe 100644
--- a/tools/testing/selftests/hid/hid_bpf.c
+++ b/tools/testing/selftests/hid/hid_bpf.c
@@ -712,7 +712,8 @@ TEST_F(hid_bpf, test_attach_detach)
 
 	/* pin the program and immediately unpin it */
 #define PIN_PATH "/sys/fs/bpf/hid_first_event"
-	bpf_program__pin(self->skel->progs.hid_first_event, PIN_PATH);
+	err = bpf_program__pin(self->skel->progs.hid_first_event, PIN_PATH);
+	ASSERT_OK(err) TH_LOG("error while calling bpf_program__pin");
 	remove(PIN_PATH);
 #undef PIN_PATH
 	usleep(100000);
diff --git a/tools/testing/selftests/hid/vmtest.sh b/tools/testing/selftests/hid/vmtest.sh
index f124cf6b0d0f..bd60f65acb72 100755
--- a/tools/testing/selftests/hid/vmtest.sh
+++ b/tools/testing/selftests/hid/vmtest.sh
@@ -108,8 +108,10 @@ EOF
 	if [[ "${debug_shell}" != "yes" ]]
 	then
 		touch ${OUTPUT_DIR}/${LOG_FILE}
-		command="set -o pipefail ; ${command} 2>&1 | tee ${OUTPUT_DIR}/${LOG_FILE}"
+		command="mount bpffs -t bpf /sys/fs/bpf/; set -o pipefail ; ${command} 2>&1 | tee ${OUTPUT_DIR}/${LOG_FILE}"
 		post_command="cat ${OUTPUT_DIR}/${LOG_FILE}"
+	else
+		command="mount bpffs -t bpf /sys/fs/bpf/; ${command}"
 	fi
 
 	set +e
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC hid v1 10/10] wip: vmtest aarch64
  2022-11-24 15:15 [RFC hid v1 00/10] HID-BPF: add support for in-tree BPF programs Benjamin Tissoires
                   ` (8 preceding siblings ...)
  2022-11-24 15:16 ` [RFC hid v1 09/10] selftests: hid: ensure the program is correctly pinned Benjamin Tissoires
@ 2022-11-24 15:16 ` Benjamin Tissoires
  9 siblings, 0 replies; 14+ messages in thread
From: Benjamin Tissoires @ 2022-11-24 15:16 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Dmitry Torokhov
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf, Benjamin Tissoires

untested

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 tools/testing/selftests/hid/config.aarch64 | 39 ++++++++++++++++++++++
 tools/testing/selftests/hid/vmtest.sh      |  6 +++-
 2 files changed, 44 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/hid/config.aarch64

diff --git a/tools/testing/selftests/hid/config.aarch64 b/tools/testing/selftests/hid/config.aarch64
new file mode 100644
index 000000000000..76581bc6395b
--- /dev/null
+++ b/tools/testing/selftests/hid/config.aarch64
@@ -0,0 +1,39 @@
+# EFI
+CONFIG_EFI_PARAMS_FROM_FDT=y
+CONFIG_EFI_GENERIC_STUB=y
+CONFIG_EFI_ARMSTUB_DTB_LOADER=y
+
+# Disable unsupported AARCH64 platforms
+CONFIG_ARCH_ACTIONS=n
+CONFIG_ARCH_SUNXI=n
+CONFIG_ARCH_ALPINE=n
+CONFIG_ARCH_APPLE=n
+CONFIG_ARCH_BERLIN=n
+CONFIG_ARCH_EXYNOS=n
+CONFIG_ARCH_K3=n
+CONFIG_ARCH_LAYERSCAPE=n
+CONFIG_ARCH_LG1K=n
+CONFIG_ARCH_HISI=n
+CONFIG_ARCH_KEEMBAY=n
+CONFIG_ARCH_MEDIATEK=n
+CONFIG_ARCH_MESON=n
+CONFIG_ARCH_MVEBU=n
+CONFIG_ARCH_MXC=n
+CONFIG_ARCH_QCOM=n
+CONFIG_ARCH_RENESAS=n
+CONFIG_ARCH_S32=n
+CONFIG_ARCH_SEATTLE=n
+CONFIG_ARCH_INTEL_SOCFPGA=n
+CONFIG_ARCH_SYNQUACER=n
+CONFIG_ARCH_TEGRA=n
+CONFIG_ARCH_TESLA_FSD=n
+CONFIG_ARCH_SPRD=n
+CONFIG_ARCH_THUNDER=n
+CONFIG_ARCH_THUNDER2=n
+CONFIG_ARCH_UNIPHIER=n
+CONFIG_ARCH_VEXPRESS=n
+CONFIG_ARCH_VISCONTI=n
+CONFIG_ARCH_XGENE=n
+CONFIG_ARCH_ZYNQMP=n
+
+CONFIG_NET_VENDOR_MELLANOX=n
diff --git a/tools/testing/selftests/hid/vmtest.sh b/tools/testing/selftests/hid/vmtest.sh
index bd60f65acb72..d9e4a4c0557a 100755
--- a/tools/testing/selftests/hid/vmtest.sh
+++ b/tools/testing/selftests/hid/vmtest.sh
@@ -4,9 +4,13 @@
 set -u
 set -e
 
-# This script currently only works for x86_64
+# This script currently only works for x86_64 and aarch64.
 ARCH="$(uname -m)"
 case "${ARCH}" in
+aarch64)
+	QEMU_BINARY=qemu-system-aarch64
+	BZIMAGE="arch/aarch64/boot/bzImage"
+	;;
 x86_64)
 	QEMU_BINARY=qemu-system-x86_64
 	BZIMAGE="arch/x86/boot/bzImage"
-- 
2.38.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [RFC hid v1 01/10] bpftool: generate json output of skeletons
  2022-11-24 15:15 ` [RFC hid v1 01/10] bpftool: generate json output of skeletons Benjamin Tissoires
@ 2022-11-30 23:05   ` Andrii Nakryiko
  2022-12-01 14:22     ` Benjamin Tissoires
  0 siblings, 1 reply; 14+ messages in thread
From: Andrii Nakryiko @ 2022-11-30 23:05 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Dmitry Torokhov, Tero Kristo, linux-kernel,
	linux-input, netdev, bpf

On Thu, Nov 24, 2022 at 7:16 AM Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
>
> So we can then build light skeletons with loaders in any language.
>

It would be useful to include an example generated JSON. Other than
that the overall idea makes sense. Kind of machine-friendly "BPF
object schema" to allow automation.

> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> ---
>  tools/bpf/bpftool/gen.c | 95 +++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 95 insertions(+)
>
> diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
> index cf8b4e525c88..818a5209b3ac 100644
> --- a/tools/bpf/bpftool/gen.c
> +++ b/tools/bpf/bpftool/gen.c
> @@ -904,6 +904,96 @@ codegen_progs_skeleton(struct bpf_object *obj, size_t prog_cnt, bool populate_li
>         }
>  }
>
> +static int gen_json(struct bpf_object *obj, const char *obj_name, size_t file_sz, uint8_t *obj_data)
> +{
> +       struct bpf_program *prog;
> +       struct bpf_map *map;
> +       char ident[256];
> +
> +       jsonw_start_object(json_wtr);   /* root object */
> +
> +       jsonw_string_field(json_wtr, "name", obj_name);
> +
> +       jsonw_bool_field(json_wtr, "use_loader", use_loader);
> +
> +       /* print all maps */
> +       jsonw_name(json_wtr, "maps");
> +       jsonw_start_array(json_wtr);
> +       bpf_object__for_each_map(map, obj) {
> +               if (!get_map_ident(map, ident, sizeof(ident))) {
> +                       p_err("ignoring unrecognized internal map '%s'...",
> +                             bpf_map__name(map));
> +                       continue;
> +               }
> +
> +               jsonw_start_object(json_wtr);   /* map object */
> +               jsonw_string_field(json_wtr, "ident", ident);
> +               jsonw_string_field(json_wtr, "name", bpf_map__name(map));
> +
> +               /* print mmap data value */
> +               if (is_internal_mmapable_map(map, ident, sizeof(ident))) {
> +                       const void *mmap_data = NULL;
> +                       size_t mmap_size = 0;
> +
> +                       mmap_data = bpf_map__initial_value(map, &mmap_size);
> +
> +                       jsonw_uint_field(json_wtr, "size", mmap_size);
> +                       jsonw_uint_field(json_wtr, "mmap_sz", bpf_map_mmap_sz(map));
> +                       jsonw_name(json_wtr, "data");
> +                       print_hex_data_json((uint8_t *)mmap_data, mmap_size);
> +
> +               }
> +               jsonw_end_object(json_wtr);     /* map object */
> +       }
> +       jsonw_end_array(json_wtr);
> +
> +       /* print all progs */
> +       jsonw_name(json_wtr, "progs");
> +       jsonw_start_array(json_wtr);
> +       bpf_object__for_each_program(prog, obj) {
> +               jsonw_start_object(json_wtr);   /* prog object */
> +               jsonw_string_field(json_wtr, "name", bpf_program__name(prog));
> +               jsonw_string_field(json_wtr, "sec", bpf_program__section_name(prog));
> +               jsonw_end_object(json_wtr);     /* prog object */
> +       }
> +       jsonw_end_array(json_wtr);
> +
> +       /* print object data */
> +       if (use_loader) {
> +               DECLARE_LIBBPF_OPTS(gen_loader_opts, opts);
> +               int err = 0;
> +
> +               err = bpf_object__gen_loader(obj, &opts);
> +               if (err)
> +                       return err;
> +
> +               err = bpf_object__load(obj);
> +               if (err) {
> +                       p_err("failed to load object file");
> +                       return err;
> +               }
> +               /* If there was no error during load then gen_loader_opts
> +                * are populated with the loader program.
> +                */
> +
> +               jsonw_uint_field(json_wtr, "data_sz", opts.data_sz);
> +               jsonw_name(json_wtr, "data");
> +               print_hex_data_json((uint8_t *)opts.data, opts.data_sz);
> +
> +               jsonw_uint_field(json_wtr, "insns_sz", opts.insns_sz);
> +               jsonw_name(json_wtr, "insns");
> +               print_hex_data_json((uint8_t *)opts.insns, opts.insns_sz);
> +
> +       } else {
> +               jsonw_name(json_wtr, "data");
> +               print_hex_data_json(obj_data, file_sz);
> +       }
> +
> +       jsonw_end_object(json_wtr);     /* root object */
> +
> +       return 0;
> +}
> +
>  static int do_skeleton(int argc, char **argv)
>  {
>         char header_guard[MAX_OBJ_NAME_LEN + sizeof("__SKEL_H__")];
> @@ -986,6 +1076,11 @@ static int do_skeleton(int argc, char **argv)
>                 goto out;
>         }
>
> +       if (json_output) {
> +               err = gen_json(obj, obj_name, file_sz, (uint8_t *)obj_data);
> +               goto out;
> +       }
> +
>         bpf_object__for_each_map(map, obj) {
>                 if (!get_map_ident(map, ident, sizeof(ident))) {
>                         p_err("ignoring unrecognized internal map '%s'...",
> --
> 2.38.1
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC hid v1 01/10] bpftool: generate json output of skeletons
  2022-11-30 23:05   ` Andrii Nakryiko
@ 2022-12-01 14:22     ` Benjamin Tissoires
  2022-12-01 18:21       ` Andrii Nakryiko
  0 siblings, 1 reply; 14+ messages in thread
From: Benjamin Tissoires @ 2022-12-01 14:22 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Dmitry Torokhov, Tero Kristo, linux-kernel,
	linux-input, netdev, bpf

On Thu, Dec 1, 2022 at 12:06 AM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Thu, Nov 24, 2022 at 7:16 AM Benjamin Tissoires
> <benjamin.tissoires@redhat.com> wrote:
> >
> > So we can then build light skeletons with loaders in any language.
> >
>
> It would be useful to include an example generated JSON. Other than
> that the overall idea makes sense. Kind of machine-friendly "BPF
> object schema" to allow automation.
>

Great :)
I'll add the generated example in v2 then.

However, I have problems figuring out how I can hit the
"codegen_datasecs()" path. I think that's the only code path I am not
duplicating from the do_skeleton() function, but I don't know what
kind of .bpf.c program will trigger that part.

Also, should I add some tests for it in tools/testing/selftests/bpf?

Cheers,
Benjamin

> > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > ---
> >  tools/bpf/bpftool/gen.c | 95 +++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 95 insertions(+)
> >
> > diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
> > index cf8b4e525c88..818a5209b3ac 100644
> > --- a/tools/bpf/bpftool/gen.c
> > +++ b/tools/bpf/bpftool/gen.c
> > @@ -904,6 +904,96 @@ codegen_progs_skeleton(struct bpf_object *obj, size_t prog_cnt, bool populate_li
> >         }
> >  }
> >
> > +static int gen_json(struct bpf_object *obj, const char *obj_name, size_t file_sz, uint8_t *obj_data)
> > +{
> > +       struct bpf_program *prog;
> > +       struct bpf_map *map;
> > +       char ident[256];
> > +
> > +       jsonw_start_object(json_wtr);   /* root object */
> > +
> > +       jsonw_string_field(json_wtr, "name", obj_name);
> > +
> > +       jsonw_bool_field(json_wtr, "use_loader", use_loader);
> > +
> > +       /* print all maps */
> > +       jsonw_name(json_wtr, "maps");
> > +       jsonw_start_array(json_wtr);
> > +       bpf_object__for_each_map(map, obj) {
> > +               if (!get_map_ident(map, ident, sizeof(ident))) {
> > +                       p_err("ignoring unrecognized internal map '%s'...",
> > +                             bpf_map__name(map));
> > +                       continue;
> > +               }
> > +
> > +               jsonw_start_object(json_wtr);   /* map object */
> > +               jsonw_string_field(json_wtr, "ident", ident);
> > +               jsonw_string_field(json_wtr, "name", bpf_map__name(map));
> > +
> > +               /* print mmap data value */
> > +               if (is_internal_mmapable_map(map, ident, sizeof(ident))) {
> > +                       const void *mmap_data = NULL;
> > +                       size_t mmap_size = 0;
> > +
> > +                       mmap_data = bpf_map__initial_value(map, &mmap_size);
> > +
> > +                       jsonw_uint_field(json_wtr, "size", mmap_size);
> > +                       jsonw_uint_field(json_wtr, "mmap_sz", bpf_map_mmap_sz(map));
> > +                       jsonw_name(json_wtr, "data");
> > +                       print_hex_data_json((uint8_t *)mmap_data, mmap_size);
> > +
> > +               }
> > +               jsonw_end_object(json_wtr);     /* map object */
> > +       }
> > +       jsonw_end_array(json_wtr);
> > +
> > +       /* print all progs */
> > +       jsonw_name(json_wtr, "progs");
> > +       jsonw_start_array(json_wtr);
> > +       bpf_object__for_each_program(prog, obj) {
> > +               jsonw_start_object(json_wtr);   /* prog object */
> > +               jsonw_string_field(json_wtr, "name", bpf_program__name(prog));
> > +               jsonw_string_field(json_wtr, "sec", bpf_program__section_name(prog));
> > +               jsonw_end_object(json_wtr);     /* prog object */
> > +       }
> > +       jsonw_end_array(json_wtr);
> > +
> > +       /* print object data */
> > +       if (use_loader) {
> > +               DECLARE_LIBBPF_OPTS(gen_loader_opts, opts);
> > +               int err = 0;
> > +
> > +               err = bpf_object__gen_loader(obj, &opts);
> > +               if (err)
> > +                       return err;
> > +
> > +               err = bpf_object__load(obj);
> > +               if (err) {
> > +                       p_err("failed to load object file");
> > +                       return err;
> > +               }
> > +               /* If there was no error during load then gen_loader_opts
> > +                * are populated with the loader program.
> > +                */
> > +
> > +               jsonw_uint_field(json_wtr, "data_sz", opts.data_sz);
> > +               jsonw_name(json_wtr, "data");
> > +               print_hex_data_json((uint8_t *)opts.data, opts.data_sz);
> > +
> > +               jsonw_uint_field(json_wtr, "insns_sz", opts.insns_sz);
> > +               jsonw_name(json_wtr, "insns");
> > +               print_hex_data_json((uint8_t *)opts.insns, opts.insns_sz);
> > +
> > +       } else {
> > +               jsonw_name(json_wtr, "data");
> > +               print_hex_data_json(obj_data, file_sz);
> > +       }
> > +
> > +       jsonw_end_object(json_wtr);     /* root object */
> > +
> > +       return 0;
> > +}
> > +
> >  static int do_skeleton(int argc, char **argv)
> >  {
> >         char header_guard[MAX_OBJ_NAME_LEN + sizeof("__SKEL_H__")];
> > @@ -986,6 +1076,11 @@ static int do_skeleton(int argc, char **argv)
> >                 goto out;
> >         }
> >
> > +       if (json_output) {
> > +               err = gen_json(obj, obj_name, file_sz, (uint8_t *)obj_data);
> > +               goto out;
> > +       }
> > +
> >         bpf_object__for_each_map(map, obj) {
> >                 if (!get_map_ident(map, ident, sizeof(ident))) {
> >                         p_err("ignoring unrecognized internal map '%s'...",
> > --
> > 2.38.1
> >
>


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC hid v1 01/10] bpftool: generate json output of skeletons
  2022-12-01 14:22     ` Benjamin Tissoires
@ 2022-12-01 18:21       ` Andrii Nakryiko
  0 siblings, 0 replies; 14+ messages in thread
From: Andrii Nakryiko @ 2022-12-01 18:21 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Dmitry Torokhov, Tero Kristo, linux-kernel,
	linux-input, netdev, bpf

On Thu, Dec 1, 2022 at 6:23 AM Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
>
> On Thu, Dec 1, 2022 at 12:06 AM Andrii Nakryiko
> <andrii.nakryiko@gmail.com> wrote:
> >
> > On Thu, Nov 24, 2022 at 7:16 AM Benjamin Tissoires
> > <benjamin.tissoires@redhat.com> wrote:
> > >
> > > So we can then build light skeletons with loaders in any language.
> > >
> >
> > It would be useful to include an example generated JSON. Other than
> > that the overall idea makes sense. Kind of machine-friendly "BPF
> > object schema" to allow automation.
> >
>
> Great :)
> I'll add the generated example in v2 then.
>
> However, I have problems figuring out how I can hit the
> "codegen_datasecs()" path. I think that's the only code path I am not
> duplicating from the do_skeleton() function, but I don't know what
> kind of .bpf.c program will trigger that part.
>

You'll hit this whenever you have global variables in your .bpf.c
code. It's very important part, so let's make sure it is covered. Try
using both `const volatile` read-only variables (they will end up in
.rodata datasec) and global non-read-only variables (they will go into
.data, if initialized to non-zero defaults, or .bss if
zero-initialized).

> Also, should I add some tests for it in tools/testing/selftests/bpf?

Yes, it probably makes sense to check expected JSON output is
generated for a few example BPF object files.

>
> Cheers,
> Benjamin
>
> > > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > > ---
> > >  tools/bpf/bpftool/gen.c | 95 +++++++++++++++++++++++++++++++++++++++++
> > >  1 file changed, 95 insertions(+)
> > >
> > > diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
> > > index cf8b4e525c88..818a5209b3ac 100644
> > > --- a/tools/bpf/bpftool/gen.c
> > > +++ b/tools/bpf/bpftool/gen.c
> > > @@ -904,6 +904,96 @@ codegen_progs_skeleton(struct bpf_object *obj, size_t prog_cnt, bool populate_li
> > >         }
> > >  }
> > >
> > > +static int gen_json(struct bpf_object *obj, const char *obj_name, size_t file_sz, uint8_t *obj_data)
> > > +{
> > > +       struct bpf_program *prog;
> > > +       struct bpf_map *map;
> > > +       char ident[256];
> > > +
> > > +       jsonw_start_object(json_wtr);   /* root object */
> > > +
> > > +       jsonw_string_field(json_wtr, "name", obj_name);
> > > +
> > > +       jsonw_bool_field(json_wtr, "use_loader", use_loader);
> > > +
> > > +       /* print all maps */
> > > +       jsonw_name(json_wtr, "maps");
> > > +       jsonw_start_array(json_wtr);
> > > +       bpf_object__for_each_map(map, obj) {
> > > +               if (!get_map_ident(map, ident, sizeof(ident))) {
> > > +                       p_err("ignoring unrecognized internal map '%s'...",
> > > +                             bpf_map__name(map));
> > > +                       continue;
> > > +               }
> > > +
> > > +               jsonw_start_object(json_wtr);   /* map object */
> > > +               jsonw_string_field(json_wtr, "ident", ident);
> > > +               jsonw_string_field(json_wtr, "name", bpf_map__name(map));
> > > +
> > > +               /* print mmap data value */
> > > +               if (is_internal_mmapable_map(map, ident, sizeof(ident))) {
> > > +                       const void *mmap_data = NULL;
> > > +                       size_t mmap_size = 0;
> > > +
> > > +                       mmap_data = bpf_map__initial_value(map, &mmap_size);
> > > +
> > > +                       jsonw_uint_field(json_wtr, "size", mmap_size);
> > > +                       jsonw_uint_field(json_wtr, "mmap_sz", bpf_map_mmap_sz(map));
> > > +                       jsonw_name(json_wtr, "data");
> > > +                       print_hex_data_json((uint8_t *)mmap_data, mmap_size);
> > > +
> > > +               }
> > > +               jsonw_end_object(json_wtr);     /* map object */
> > > +       }
> > > +       jsonw_end_array(json_wtr);
> > > +
> > > +       /* print all progs */
> > > +       jsonw_name(json_wtr, "progs");
> > > +       jsonw_start_array(json_wtr);
> > > +       bpf_object__for_each_program(prog, obj) {
> > > +               jsonw_start_object(json_wtr);   /* prog object */
> > > +               jsonw_string_field(json_wtr, "name", bpf_program__name(prog));
> > > +               jsonw_string_field(json_wtr, "sec", bpf_program__section_name(prog));
> > > +               jsonw_end_object(json_wtr);     /* prog object */
> > > +       }
> > > +       jsonw_end_array(json_wtr);
> > > +
> > > +       /* print object data */
> > > +       if (use_loader) {
> > > +               DECLARE_LIBBPF_OPTS(gen_loader_opts, opts);
> > > +               int err = 0;
> > > +
> > > +               err = bpf_object__gen_loader(obj, &opts);
> > > +               if (err)
> > > +                       return err;
> > > +
> > > +               err = bpf_object__load(obj);
> > > +               if (err) {
> > > +                       p_err("failed to load object file");
> > > +                       return err;
> > > +               }
> > > +               /* If there was no error during load then gen_loader_opts
> > > +                * are populated with the loader program.
> > > +                */
> > > +
> > > +               jsonw_uint_field(json_wtr, "data_sz", opts.data_sz);
> > > +               jsonw_name(json_wtr, "data");
> > > +               print_hex_data_json((uint8_t *)opts.data, opts.data_sz);
> > > +
> > > +               jsonw_uint_field(json_wtr, "insns_sz", opts.insns_sz);
> > > +               jsonw_name(json_wtr, "insns");
> > > +               print_hex_data_json((uint8_t *)opts.insns, opts.insns_sz);
> > > +
> > > +       } else {
> > > +               jsonw_name(json_wtr, "data");
> > > +               print_hex_data_json(obj_data, file_sz);
> > > +       }
> > > +
> > > +       jsonw_end_object(json_wtr);     /* root object */
> > > +
> > > +       return 0;
> > > +}
> > > +
> > >  static int do_skeleton(int argc, char **argv)
> > >  {
> > >         char header_guard[MAX_OBJ_NAME_LEN + sizeof("__SKEL_H__")];
> > > @@ -986,6 +1076,11 @@ static int do_skeleton(int argc, char **argv)
> > >                 goto out;
> > >         }
> > >
> > > +       if (json_output) {
> > > +               err = gen_json(obj, obj_name, file_sz, (uint8_t *)obj_data);
> > > +               goto out;
> > > +       }
> > > +
> > >         bpf_object__for_each_map(map, obj) {
> > >                 if (!get_map_ident(map, ident, sizeof(ident))) {
> > >                         p_err("ignoring unrecognized internal map '%s'...",
> > > --
> > > 2.38.1
> > >
> >
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2022-12-01 18:21 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-24 15:15 [RFC hid v1 00/10] HID-BPF: add support for in-tree BPF programs Benjamin Tissoires
2022-11-24 15:15 ` [RFC hid v1 01/10] bpftool: generate json output of skeletons Benjamin Tissoires
2022-11-30 23:05   ` Andrii Nakryiko
2022-12-01 14:22     ` Benjamin Tissoires
2022-12-01 18:21       ` Andrii Nakryiko
2022-11-24 15:15 ` [RFC hid v1 02/10] WIP: bpf: allow to pin programs from the kernel when bpffs is mounted Benjamin Tissoires
2022-11-24 15:15 ` [RFC hid v1 03/10] HID: add a tool to convert a bpf source into a generic bpf loader Benjamin Tissoires
2022-11-24 15:15 ` [RFC hid v1 04/10] HID: add the bpf loader that can attach a generic hid-bpf program Benjamin Tissoires
2022-11-24 15:15 ` [RFC hid v1 05/10] HID: add report descriptor override for the X-Keys XK24 Benjamin Tissoires
2022-11-24 15:15 ` [RFC hid v1 06/10] selftests: hid: add vmtest.sh Benjamin Tissoires
2022-11-24 15:16 ` [RFC hid v1 07/10] selftests: hid: Add a variant parameter so we can emulate specific devices Benjamin Tissoires
2022-11-24 15:16 ` [RFC hid v1 08/10] selftests: hid: add XK-24 tests Benjamin Tissoires
2022-11-24 15:16 ` [RFC hid v1 09/10] selftests: hid: ensure the program is correctly pinned Benjamin Tissoires
2022-11-24 15:16 ` [RFC hid v1 10/10] wip: vmtest aarch64 Benjamin Tissoires

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).