From patchwork Wed May 18 20:59:08 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12854099 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id F104AC4332F for ; Wed, 18 May 2022 20:59:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S242885AbiERU7s (ORCPT ); Wed, 18 May 2022 16:59:48 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55480 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S242856AbiERU7n (ORCPT ); Wed, 18 May 2022 16:59:43 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id E7240230229 for ; Wed, 18 May 2022 13:59:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1652907580; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=tG6J7OXecG2aHQWxhTipqfUbGWLrMU/iiBvmaxNqXfU=; b=WR7iRY+v/grf/Ls26RP5B765Pj0RSwV/CzTlqrfDUAQLLsgkSeMZxc2b0afCrnci6OV1Kc ABXxPZMwrzpkNN5+fVEhEnMQKxsa5b1XZl6hYatd18QoeKV1t6w07BcTdQ6SpvBHzsAoE/ P/3MbopjwflI54DKA/JkOYfd1EWo8aw= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-650-40hpmLLpPA-9qBBY7Stp_g-1; Wed, 18 May 2022 16:59:35 -0400 X-MC-Unique: 40hpmLLpPA-9qBBY7Stp_g-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id D9A27101AA44; Wed, 18 May 2022 20:59:34 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.58]) by smtp.corp.redhat.com (Postfix) with ESMTP id 633502166B25; Wed, 18 May 2022 20:59:31 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v5 01/17] bpf/btf: also allow kfunc in tracing and syscall programs Date: Wed, 18 May 2022 22:59:08 +0200 Message-Id: <20220518205924.399291-2-benjamin.tissoires@redhat.com> In-Reply-To: <20220518205924.399291-1-benjamin.tissoires@redhat.com> References: <20220518205924.399291-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Tracing and syscall BPF program types are very convenient to add BPF capabilities to subsystem otherwise not BPF capable. When we add kfuncs capabilities to those program types, we can add BPF features to subsystems without having to touch BPF core. Signed-off-by: Benjamin Tissoires --- changes in v5: - also add syscalls new in v4: - I think this is where I need to add my new kfuncs, though in the end I need to be able to change the incoming data, so maybe only fmod_ret is the one we need to be able to be RW. --- kernel/bpf/btf.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 2f0b0440131c..7bccaa4646e5 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -202,6 +202,8 @@ enum btf_kfunc_hook { BTF_KFUNC_HOOK_XDP, BTF_KFUNC_HOOK_TC, BTF_KFUNC_HOOK_STRUCT_OPS, + BTF_KFUNC_HOOK_TRACING, + BTF_KFUNC_HOOK_SYSCALL, BTF_KFUNC_HOOK_MAX, }; @@ -7110,6 +7112,10 @@ static int bpf_prog_type_to_kfunc_hook(enum bpf_prog_type prog_type) return BTF_KFUNC_HOOK_TC; case BPF_PROG_TYPE_STRUCT_OPS: return BTF_KFUNC_HOOK_STRUCT_OPS; + case BPF_PROG_TYPE_TRACING: + return BTF_KFUNC_HOOK_TRACING; + case BPF_PROG_TYPE_SYSCALL: + return BTF_KFUNC_HOOK_SYSCALL; default: return BTF_KFUNC_HOOK_MAX; } From patchwork Wed May 18 20:59:09 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12854100 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1E18FC433EF for ; Wed, 18 May 2022 20:59:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S242892AbiERU7u (ORCPT ); Wed, 18 May 2022 16:59:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55928 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S242875AbiERU7q (ORCPT ); Wed, 18 May 2022 16:59:46 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id E9B6F231CB1 for ; Wed, 18 May 2022 13:59:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1652907585; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=5gCJospTTN2t8SEpfo9x05ijIEkJgmfk9aeuVxOhH7U=; b=iSync380tqwFhZuZdyWjdiSFE9QDf/pCp9PrHk9YnZE70nrjDSrQ2znhunYBXroZtGtRjh NpAo+OL5iWW3tq2Dtw28l+R7CHXMSZi6HEO9dYLB72q9GoiXXjG9WkEYd3pWc0t+3epbyj 9eTKUTNh1tKkWLIB3LX+lUGKs5z/8Tk= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-660-qwsD7YX3N_Cq1NUwFmM2qw-1; Wed, 18 May 2022 16:59:40 -0400 X-MC-Unique: qwsD7YX3N_Cq1NUwFmM2qw-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 903B5800B21; Wed, 18 May 2022 20:59:39 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.58]) by smtp.corp.redhat.com (Postfix) with ESMTP id 3B7F02166B25; Wed, 18 May 2022 20:59:35 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v5 02/17] bpf/verifier: allow kfunc to return an allocated mem Date: Wed, 18 May 2022 22:59:09 +0200 Message-Id: <20220518205924.399291-3-benjamin.tissoires@redhat.com> In-Reply-To: <20220518205924.399291-1-benjamin.tissoires@redhat.com> References: <20220518205924.399291-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net When a kfunc is not returning a pointer to a struct but to a plain type, we can consider it is a valid allocated memory assuming that: - one of the arguments is called rdonly_buf_size - or one of the arguments is called rdwr_buf_size - and this argument is a const from the caller point of view We can then use this parameter as the size of the allocated memory. The memory is either read-only or read-write based on the name of the size parameter. Signed-off-by: Benjamin Tissoires --- changes in v5: - updated PTR_TO_MEM comment in btf.c to match upstream - make it read-only or read-write based on the name of size new in v4 --- include/linux/btf.h | 7 +++++ kernel/bpf/btf.c | 41 +++++++++++++++++++++++- kernel/bpf/verifier.c | 72 +++++++++++++++++++++++++++++++++---------- 3 files changed, 102 insertions(+), 18 deletions(-) diff --git a/include/linux/btf.h b/include/linux/btf.h index 2611cea2c2b6..2a4feafc083e 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -343,6 +343,13 @@ static inline struct btf_param *btf_params(const struct btf_type *t) return (struct btf_param *)(t + 1); } +struct bpf_reg_state; + +bool btf_is_kfunc_arg_mem_size(const struct btf *btf, + const struct btf_param *arg, + const struct bpf_reg_state *reg, + const char *name); + #ifdef CONFIG_BPF_SYSCALL struct bpf_prog; diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 7bccaa4646e5..2d11d178807c 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -6049,6 +6049,31 @@ static bool is_kfunc_arg_mem_size(const struct btf *btf, return true; } +bool btf_is_kfunc_arg_mem_size(const struct btf *btf, + const struct btf_param *arg, + const struct bpf_reg_state *reg, + const char *name) +{ + int len, target_len = strlen(name); + const struct btf_type *t; + const char *param_name; + + t = btf_type_skip_modifiers(btf, arg->type, NULL); + if (!btf_type_is_scalar(t) || reg->type != SCALAR_VALUE) + return false; + + param_name = btf_name_by_offset(btf, arg->name_off); + if (str_is_empty(param_name)) + return false; + len = strlen(param_name); + if (len != target_len) + return false; + if (strncmp(param_name, name, target_len)) + return false; + + return true; +} + static int btf_check_func_arg_match(struct bpf_verifier_env *env, const struct btf *btf, u32 func_id, struct bpf_reg_state *regs, @@ -6198,7 +6223,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, if (reg->type == PTR_TO_BTF_ID) { reg_btf = reg->btf; reg_ref_id = reg->btf_id; - /* Ensure only one argument is referenced PTR_TO_BTF_ID */ + /* Ensure only one argument is reference PTR_TO_BTF_ID or PTR_TO_MEM */ if (reg->ref_obj_id) { if (ref_obj_id) { bpf_log(log, "verifier internal error: more than one arg with ref_obj_id R%d %u %u\n", @@ -6258,6 +6283,20 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, i++; continue; } + + if (rel && reg->ref_obj_id) { + /* Ensure only one argument is referenced PTR_TO_BTF_ID or PTR_TO_MEM */ + if (ref_obj_id) { + bpf_log(log, + "verifier internal error: more than one arg with ref_obj_id R%d %u %u\n", + regno, + reg->ref_obj_id, + ref_obj_id); + return -EFAULT; + } + ref_regno = regno; + ref_obj_id = reg->ref_obj_id; + } } resolve_ret = btf_resolve_size(btf, ref_t, &type_size); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 9b59581026f8..084319073064 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -7219,13 +7219,14 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, int *insn_idx_p) { const struct btf_type *t, *func, *func_proto, *ptr_type; - struct bpf_reg_state *regs = cur_regs(env); + struct bpf_reg_state *reg, *regs = cur_regs(env); const char *func_name, *ptr_type_name; - u32 i, nargs, func_id, ptr_type_id; + u32 i, nargs, func_id, ptr_type_id, regno; int err, insn_idx = *insn_idx_p; const struct btf_param *args; struct btf *desc_btf; bool acq; + size_t reg_rw_size = 0, reg_ro_size = 0; /* skip for now, but return error when we find this in fixup_kfunc_call */ if (!insn->imm) @@ -7266,8 +7267,8 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, } } - for (i = 0; i < CALLER_SAVED_REGS; i++) - mark_reg_not_init(env, regs, caller_saved[i]); + /* reset REG_0 */ + mark_reg_not_init(env, regs, BPF_REG_0); /* Check return type */ t = btf_type_skip_modifiers(desc_btf, func_proto->type, NULL); @@ -7277,6 +7278,9 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, return -EINVAL; } + nargs = btf_type_vlen(func_proto); + args = btf_params(func_proto); + if (btf_type_is_scalar(t)) { mark_reg_unknown(env, regs, BPF_REG_0); mark_btf_func_reg_size(env, BPF_REG_0, t->size); @@ -7284,24 +7288,57 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, ptr_type = btf_type_skip_modifiers(desc_btf, t->type, &ptr_type_id); if (!btf_type_is_struct(ptr_type)) { - ptr_type_name = btf_name_by_offset(desc_btf, - ptr_type->name_off); - verbose(env, "kernel function %s returns pointer type %s %s is not supported\n", - func_name, btf_type_str(ptr_type), - ptr_type_name); - return -EINVAL; + /* if we have an array, look for the arguments */ + for (i = 0; i < nargs; i++) { + regno = i + BPF_REG_1; + reg = ®s[regno]; + + /* look for any const scalar parameter of name "rdonly_buf_size" + * or "rdwr_buf_size" + */ + if (!check_reg_arg(env, regno, SRC_OP) && + tnum_is_const(regs[regno].var_off)) { + if (btf_is_kfunc_arg_mem_size(desc_btf, &args[i], reg, + "rdonly_buf_size")) + reg_ro_size = regs[regno].var_off.value; + else if (btf_is_kfunc_arg_mem_size(desc_btf, &args[i], reg, + "rdwr_buf_size")) + reg_rw_size = regs[regno].var_off.value; + } + } + + if (!reg_rw_size && !reg_ro_size) { + ptr_type_name = btf_name_by_offset(desc_btf, + ptr_type->name_off); + verbose(env, + "kernel function %s returns pointer type %s %s is not supported\n", + func_name, + btf_type_str(ptr_type), + ptr_type_name); + return -EINVAL; + } + + mark_reg_known_zero(env, regs, BPF_REG_0); + regs[BPF_REG_0].type = PTR_TO_MEM; + regs[BPF_REG_0].mem_size = reg_ro_size + reg_rw_size; + + if (reg_ro_size) + regs[BPF_REG_0].type |= MEM_RDONLY; + } else { + mark_reg_known_zero(env, regs, BPF_REG_0); + regs[BPF_REG_0].type = PTR_TO_BTF_ID; + regs[BPF_REG_0].btf = desc_btf; + regs[BPF_REG_0].btf_id = ptr_type_id; + mark_btf_func_reg_size(env, BPF_REG_0, sizeof(void *)); } - mark_reg_known_zero(env, regs, BPF_REG_0); - regs[BPF_REG_0].btf = desc_btf; - regs[BPF_REG_0].type = PTR_TO_BTF_ID; - regs[BPF_REG_0].btf_id = ptr_type_id; + if (btf_kfunc_id_set_contains(desc_btf, resolve_prog_type(env->prog), BTF_KFUNC_TYPE_RET_NULL, func_id)) { regs[BPF_REG_0].type |= PTR_MAYBE_NULL; /* For mark_ptr_or_null_reg, see 93c230e3f5bd6 */ regs[BPF_REG_0].id = ++env->id_gen; } - mark_btf_func_reg_size(env, BPF_REG_0, sizeof(void *)); + if (acq) { int id = acquire_reference_state(env, insn_idx); @@ -7312,8 +7349,9 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, } } /* else { add_kfunc_call() ensures it is btf_type_is_void(t) } */ - nargs = btf_type_vlen(func_proto); - args = (const struct btf_param *)(func_proto + 1); + for (i = 1 ; i < CALLER_SAVED_REGS; i++) + mark_reg_not_init(env, regs, caller_saved[i]); + for (i = 0; i < nargs; i++) { u32 regno = i + 1; From patchwork Wed May 18 20:59:10 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12854101 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 75830C433F5 for ; Wed, 18 May 2022 20:59:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S242899AbiERU7x (ORCPT ); Wed, 18 May 2022 16:59:53 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56418 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S242795AbiERU7u (ORCPT ); Wed, 18 May 2022 16:59:50 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 1ACC3236755 for ; Wed, 18 May 2022 13:59:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1652907588; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=3kr3uQhH8H7v2Urw3FpWKFbWd5aXnjEzAqaG3LQw9fE=; b=eTnxu3uipGTFoOt2HW3kWa8r6LZI+4WSbf4ClxzPUUrUwYiv1lUdUx54qt3ftvftqLBBiJ bfxJJCnPPfT8eYyIhqxnFNSKahkZHmd/6kQb2x4q2ecrJnOk7XhvNFPpkL6P6jq5jl8HMH xtxkKIgEU3xh/ZQJ0wNdK0X1yJ2Si9o= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-587-OPEu05RCM36iCdv6qps4Fg-1; Wed, 18 May 2022 16:59:44 -0400 X-MC-Unique: OPEu05RCM36iCdv6qps4Fg-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 870953C0D18D; Wed, 18 May 2022 20:59:43 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.58]) by smtp.corp.redhat.com (Postfix) with ESMTP id DA4CE2166B25; Wed, 18 May 2022 20:59:39 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v5 03/17] bpf: prepare for more bpf syscall to be used from kernel and user space. Date: Wed, 18 May 2022 22:59:10 +0200 Message-Id: <20220518205924.399291-4-benjamin.tissoires@redhat.com> In-Reply-To: <20220518205924.399291-1-benjamin.tissoires@redhat.com> References: <20220518205924.399291-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Add BPF_MAP_GET_FD_BY_ID and BPF_MAP_DELETE_PROG. Signed-off-by: Benjamin Tissoires --- new in v5 --- kernel/bpf/syscall.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 72e53489165d..807e5596f56c 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1417,9 +1417,9 @@ static int map_update_elem(union bpf_attr *attr, bpfptr_t uattr) #define BPF_MAP_DELETE_ELEM_LAST_FIELD key -static int map_delete_elem(union bpf_attr *attr) +static int map_delete_elem(union bpf_attr *attr, bpfptr_t uattr) { - void __user *ukey = u64_to_user_ptr(attr->key); + bpfptr_t ukey = make_bpfptr(attr->key, uattr.is_kernel); int ufd = attr->map_fd; struct bpf_map *map; struct fd f; @@ -1439,7 +1439,7 @@ static int map_delete_elem(union bpf_attr *attr) goto err_put; } - key = __bpf_copy_key(ukey, map->key_size); + key = ___bpf_copy_key(ukey, map->key_size); if (IS_ERR(key)) { err = PTR_ERR(key); goto err_put; @@ -4893,7 +4893,7 @@ static int __sys_bpf(int cmd, bpfptr_t uattr, unsigned int size) err = map_update_elem(&attr, uattr); break; case BPF_MAP_DELETE_ELEM: - err = map_delete_elem(&attr); + err = map_delete_elem(&attr, uattr); break; case BPF_MAP_GET_NEXT_KEY: err = map_get_next_key(&attr); @@ -5028,8 +5028,10 @@ BPF_CALL_3(bpf_sys_bpf, int, cmd, union bpf_attr *, attr, u32, attr_size) switch (cmd) { case BPF_MAP_CREATE: + case BPF_MAP_DELETE_ELEM: case BPF_MAP_UPDATE_ELEM: case BPF_MAP_FREEZE: + case BPF_MAP_GET_FD_BY_ID: case BPF_PROG_LOAD: case BPF_BTF_LOAD: case BPF_LINK_CREATE: From patchwork Wed May 18 20:59:11 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12854102 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C84C7C43217 for ; Wed, 18 May 2022 21:00:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S242918AbiERU76 (ORCPT ); Wed, 18 May 2022 16:59:58 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56844 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S242905AbiERU7z (ORCPT ); Wed, 18 May 2022 16:59:55 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 37725254716 for ; Wed, 18 May 2022 13:59:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1652907593; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=U+niHEoMuTDRFqut4OdKlEaLEgHKW232XIT+7USRZJg=; b=df4eZodCnEj1cPvbH4bXiQqY8csZAasx89mD0LnBToowxbVRYmtB0xu/bfLxfZlrEBOBNn O4L0NGS0uSInVSchjLBx2sCJYUMYTyA806iyRQIx6nUloWpGnRZ145jRKS53fv9HzLO6M5 TOiYXrPz7o80KjnBqUeSdy9Gpeuz4+g= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-32-cMpIVQCjNICd9pEeRpcK1g-1; Wed, 18 May 2022 16:59:49 -0400 X-MC-Unique: cMpIVQCjNICd9pEeRpcK1g-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 3F7AE811E83; Wed, 18 May 2022 20:59:48 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.58]) by smtp.corp.redhat.com (Postfix) with ESMTP id C9A402166B25; Wed, 18 May 2022 20:59:43 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v5 04/17] libbpf: add map_get_fd_by_id and map_delete_elem in light skeleton Date: Wed, 18 May 2022 22:59:11 +0200 Message-Id: <20220518205924.399291-5-benjamin.tissoires@redhat.com> In-Reply-To: <20220518205924.399291-1-benjamin.tissoires@redhat.com> References: <20220518205924.399291-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net This allows to have a better control over maps from the kernel when preloading eBPF programs. Signed-off-by: Benjamin Tissoires --- new in v5 --- tools/lib/bpf/skel_internal.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tools/lib/bpf/skel_internal.h b/tools/lib/bpf/skel_internal.h index bd6f4505e7b1..bc1db60ad744 100644 --- a/tools/lib/bpf/skel_internal.h +++ b/tools/lib/bpf/skel_internal.h @@ -251,6 +251,29 @@ static inline int skel_map_update_elem(int fd, const void *key, return skel_sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, attr_sz); } +static inline int skel_map_delete_elem(int fd, const void *key) +{ + const size_t attr_sz = offsetofend(union bpf_attr, flags); + union bpf_attr attr; + + memset(&attr, 0, attr_sz); + attr.map_fd = fd; + attr.key = (long)key; + + return skel_sys_bpf(BPF_MAP_DELETE_ELEM, &attr, attr_sz); +} + +static inline int skel_map_get_fd_by_id(__u32 id) +{ + const size_t attr_sz = offsetofend(union bpf_attr, flags); + union bpf_attr attr; + + memset(&attr, 0, attr_sz); + attr.map_id = id; + + return skel_sys_bpf(BPF_MAP_GET_FD_BY_ID, &attr, attr_sz); +} + static inline int skel_raw_tracepoint_open(const char *name, int prog_fd) { const size_t attr_sz = offsetofend(union bpf_attr, raw_tracepoint.prog_fd); From patchwork Wed May 18 20:59:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12854103 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 42B73C43219 for ; Wed, 18 May 2022 21:00:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S242960AbiERVAS (ORCPT ); Wed, 18 May 2022 17:00:18 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58884 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S242896AbiERVAQ (ORCPT ); Wed, 18 May 2022 17:00:16 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 49F842550AE for ; Wed, 18 May 2022 13:59:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1652907598; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=AlA+M9AznC3l4SAV4drMKMtvnKO+BZZzFpZxWrqpDx0=; b=SQF60QgKRFhRaAQ7MAoOALp+1QbvCYTWzftIYYCCa5MkPAd2M1bqMYVzeQpi8mdC3Uh5vO rJG3feyjkh27qdJBPrmM1SfIsNb+r8xYU0YEkMjIGMCCkSlIshSNOepqsZ1LZtPUVzAUwK SH7GDg3gLhYwj6XVgV631vZgTkFmBls= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-191-7HM3s6NEMJ-KR3c7GhaLdA-1; Wed, 18 May 2022 16:59:54 -0400 X-MC-Unique: 7HM3s6NEMJ-KR3c7GhaLdA-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 7DA2880B90A; Wed, 18 May 2022 20:59:52 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.58]) by smtp.corp.redhat.com (Postfix) with ESMTP id A21652166B25; Wed, 18 May 2022 20:59:48 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v5 05/17] HID: core: store the unique system identifier in hid_device Date: Wed, 18 May 2022 22:59:12 +0200 Message-Id: <20220518205924.399291-6-benjamin.tissoires@redhat.com> In-Reply-To: <20220518205924.399291-1-benjamin.tissoires@redhat.com> References: <20220518205924.399291-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net This unique identifier is currently used only for ensuring uniqueness in sysfs. However, this could be handful for userspace to refer to a specific hid_device by this id. 2 use cases are in my mind: LEDs (and their naming convention), and HID-BPF. Signed-off-by: Benjamin Tissoires --- new in v5 --- drivers/hid/hid-core.c | 4 +++- include/linux/hid.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index db925794fbe6..e78f35cfd2d1 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2735,10 +2735,12 @@ int hid_add_device(struct hid_device *hdev) hid_warn(hdev, "bad device descriptor (%d)\n", ret); } + hdev->id = atomic_inc_return(&id); + /* XXX hack, any other cleaner solution after the driver core * is converted to allow more than 20 bytes as the device name? */ dev_set_name(&hdev->dev, "%04X:%04X:%04X.%04X", hdev->bus, - hdev->vendor, hdev->product, atomic_inc_return(&id)); + hdev->vendor, hdev->product, hdev->id); hid_debug_register(hdev, dev_name(&hdev->dev)); ret = device_add(&hdev->dev); diff --git a/include/linux/hid.h b/include/linux/hid.h index 4363a63b9775..a43dd17bc78f 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -658,6 +658,8 @@ struct hid_device { /* device report descriptor */ struct list_head debug_list; spinlock_t debug_list_lock; wait_queue_head_t debug_wait; + + unsigned int id; /* system unique id */ }; #define to_hid_device(pdev) \ From patchwork Wed May 18 20:59:13 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12854106 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0595CC4321E for ; Wed, 18 May 2022 21:00:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S243006AbiERVAe (ORCPT ); Wed, 18 May 2022 17:00:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58620 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S242935AbiERVAR (ORCPT ); Wed, 18 May 2022 17:00:17 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id A3A1425558D for ; Wed, 18 May 2022 14:00:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1652907604; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=HyLA47mfumfZ9QcCnOg90pvPRSwM7bsvvU8Qb5kpzTc=; b=Flna7r+Junj0xSkklXudB873MpZVhGHVZBHMif4Jtnmqvnixgl8w7h//ESvfntYDcQIl+C KbHb0eloSg5xv5M2KkvIQeg5ziVaa/7dOSCJ+D8j0aqhjYbZdZE6JNHNjCB4u2nakxYJ1/ KzUnzrYFp91FpXL1+4KUbhzBJygQO1s= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-513-FVBM64qoM32NaL_Nugt3RA-1; Wed, 18 May 2022 16:59:57 -0400 X-MC-Unique: FVBM64qoM32NaL_Nugt3RA-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 6E3EA101A52C; Wed, 18 May 2022 20:59:56 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.58]) by smtp.corp.redhat.com (Postfix) with ESMTP id D4E712166B25; Wed, 18 May 2022 20:59:52 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v5 06/17] HID: export hid_report_type to uapi Date: Wed, 18 May 2022 22:59:13 +0200 Message-Id: <20220518205924.399291-7-benjamin.tissoires@redhat.com> In-Reply-To: <20220518205924.399291-1-benjamin.tissoires@redhat.com> References: <20220518205924.399291-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net When we are dealing with eBPF, we need to have access to the report type. Currently our implementation differs from the USB standard, making it impossible for users to know the exact value besides hardcoding it themselves. And instead of a blank define, convert it as an enum. Signed-off-by: Benjamin Tissoires --- new in v5 --- drivers/hid/hid-core.c | 11 ++++++----- include/linux/hid.h | 22 +++++++--------------- include/uapi/linux/hid.h | 12 ++++++++++++ 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index e78f35cfd2d1..36f4aa749cea 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -55,7 +55,7 @@ MODULE_PARM_DESC(ignore_special_drivers, "Ignore any special drivers and handle */ struct hid_report *hid_register_report(struct hid_device *device, - unsigned int type, unsigned int id, + enum hid_report_type type, unsigned int id, unsigned int application) { struct hid_report_enum *report_enum = device->report_enum + type; @@ -967,7 +967,7 @@ static const char * const hid_report_names[] = { * parsing. */ struct hid_report *hid_validate_values(struct hid_device *hid, - unsigned int type, unsigned int id, + enum hid_report_type type, unsigned int id, unsigned int field_index, unsigned int report_counts) { @@ -1954,8 +1954,8 @@ int __hid_request(struct hid_device *hid, struct hid_report *report, } EXPORT_SYMBOL_GPL(__hid_request); -int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size, - int interrupt) +int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, + int interrupt) { struct hid_report_enum *report_enum = hid->report_enum + type; struct hid_report *report; @@ -2019,7 +2019,8 @@ EXPORT_SYMBOL_GPL(hid_report_raw_event); * * This is data entry for lower layers. */ -int hid_input_report(struct hid_device *hid, int type, u8 *data, u32 size, int interrupt) +int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, + int interrupt) { struct hid_report_enum *report_enum; struct hid_driver *hdrv; diff --git a/include/linux/hid.h b/include/linux/hid.h index a43dd17bc78f..6a2a6f166bd3 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -314,15 +314,6 @@ struct hid_item { #define HID_BAT_ABSOLUTESTATEOFCHARGE 0x00850065 #define HID_VD_ASUS_CUSTOM_MEDIA_KEYS 0xff310076 -/* - * HID report types --- Ouch! HID spec says 1 2 3! - */ - -#define HID_INPUT_REPORT 0 -#define HID_OUTPUT_REPORT 1 -#define HID_FEATURE_REPORT 2 - -#define HID_REPORT_TYPES 3 /* * HID connect requests @@ -509,7 +500,7 @@ struct hid_report { struct list_head hidinput_list; struct list_head field_entry_list; /* ordered list of input fields */ unsigned int id; /* id of this report */ - unsigned int type; /* report type */ + enum hid_report_type type; /* report type */ unsigned int application; /* application usage for this report */ struct hid_field *field[HID_MAX_FIELDS]; /* fields of the report */ struct hid_field_entry *field_entries; /* allocated memory of input field_entry */ @@ -926,7 +917,8 @@ extern int hidinput_connect(struct hid_device *hid, unsigned int force); extern void hidinput_disconnect(struct hid_device *); int hid_set_field(struct hid_field *, unsigned, __s32); -int hid_input_report(struct hid_device *, int type, u8 *, u32, int); +int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, + int interrupt); struct hid_field *hidinput_get_led_field(struct hid_device *hid); unsigned int hidinput_count_leds(struct hid_device *hid); __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code); @@ -935,11 +927,11 @@ int __hid_request(struct hid_device *hid, struct hid_report *rep, int reqtype); u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags); struct hid_device *hid_allocate_device(void); struct hid_report *hid_register_report(struct hid_device *device, - unsigned int type, unsigned int id, + enum hid_report_type type, unsigned int id, unsigned int application); int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size); struct hid_report *hid_validate_values(struct hid_device *hid, - unsigned int type, unsigned int id, + enum hid_report_type type, unsigned int id, unsigned int field_index, unsigned int report_counts); @@ -1184,8 +1176,8 @@ static inline u32 hid_report_len(struct hid_report *report) return DIV_ROUND_UP(report->size, 8) + (report->id > 0); } -int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size, - int interrupt); +int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, + int interrupt); /* HID quirks API */ unsigned long hid_lookup_quirk(const struct hid_device *hdev); diff --git a/include/uapi/linux/hid.h b/include/uapi/linux/hid.h index b34492a87a8a..8d17371097c2 100644 --- a/include/uapi/linux/hid.h +++ b/include/uapi/linux/hid.h @@ -42,6 +42,18 @@ #define USB_INTERFACE_PROTOCOL_KEYBOARD 1 #define USB_INTERFACE_PROTOCOL_MOUSE 2 +/* + * HID report types --- Ouch! HID spec says 1 2 3! + */ + +enum hid_report_type { + HID_INPUT_REPORT = 0, + HID_OUTPUT_REPORT = 1, + HID_FEATURE_REPORT = 2, + + HID_REPORT_TYPES, +}; + /* * HID class requests */ From patchwork Wed May 18 20:59:14 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12854107 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 60DBBC433F5 for ; Wed, 18 May 2022 21:00:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S242941AbiERVAd (ORCPT ); Wed, 18 May 2022 17:00:33 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58964 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S242943AbiERVAR (ORCPT ); Wed, 18 May 2022 17:00:17 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 358492555A5 for ; Wed, 18 May 2022 14:00:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1652907609; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=p7B55HA+ZrmYTEPinuVVypoSI8E1fGgG5tf/yLQB6bI=; b=KUQAe9o8syljmYnL1RYzCTRefbR7atFJ2UFk78zNM5c53MDy7a3SHf7Cj38WSNJvZ+63F6 aP2z2i2Ki+mE1R/Su5xjnYiRvMDXSXaJSpeAZpnHPA5+ipUg9PBHXTZiTYMcVUHUxn9CRA HjYQRkaDcGev+kY0M4CVVk6IcarDuf0= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-139-E1WWERhJPy6326ScBgRUFg-1; Wed, 18 May 2022 17:00:01 -0400 X-MC-Unique: E1WWERhJPy6326ScBgRUFg-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 5AEF6101AA45; Wed, 18 May 2022 21:00:00 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.58]) by smtp.corp.redhat.com (Postfix) with ESMTP id B04102166B25; Wed, 18 May 2022 20:59:56 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v5 07/17] HID: initial BPF implementation Date: Wed, 18 May 2022 22:59:14 +0200 Message-Id: <20220518205924.399291-8-benjamin.tissoires@redhat.com> In-Reply-To: <20220518205924.399291-1-benjamin.tissoires@redhat.com> References: <20220518205924.399291-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Declare an entry point that can use fmod_ret BPF programs, and also an API to access and change the incoming data. A simpler implementation would consist in just calling hid_bpf_device_event() for any incoming event and let users deal with the fact that they will be called for any event of any device. The goal of HID-BPF is to partially replace drivers, so this situation can be problematic because we might have programs which will step on each other toes. For that, we add a new API hid_bpf_attach_prog() that can be called from a syscall and we manually deal with a jump table in hid-bpf. Whenever we add a program to the jump table (in other words, when we attach a program to a HID device), we keep the number of time we added this program in the jump table so we can release it whenever there are no other users. HID devices have an RCU protected list of available programs in the jump table, and those programs are called one after the other thanks to bpf_tail_call(). To achieve the detection of users losing their fds on the programs we attached, we add 2 tracing facilities on bpf_prog_release() (for when a fd is closed) and bpf_free_inode() (for when a pinned program gets unpinned). Signed-off-by: Benjamin Tissoires --- changes in v5: - all the HID bpf operations are in their dedicated module - a bpf program is preloaded on startup so we can call subsequent calls with bpf_tail_call - make hid_bpf_ctx more compact - add a dedicated hid_bpf_attach_prog() API - store the list of progs in each hdev - monitor the calls to bpf_prog_release to automatically release attached progs when there are no other users - add kernel docs directly when functions are defined new-ish in v4: - far from complete, but gives an overview of what we can do now. --- drivers/hid/Kconfig | 2 + drivers/hid/Makefile | 2 + drivers/hid/bpf/Kconfig | 19 + drivers/hid/bpf/Makefile | 11 + drivers/hid/bpf/entrypoints/Makefile | 88 +++ drivers/hid/bpf/entrypoints/README | 4 + drivers/hid/bpf/entrypoints/entrypoints.bpf.c | 72 ++ .../hid/bpf/entrypoints/entrypoints.lskel.h | 733 ++++++++++++++++++ drivers/hid/bpf/hid_bpf_dispatch.c | 235 ++++++ drivers/hid/bpf/hid_bpf_dispatch.h | 27 + drivers/hid/bpf/hid_bpf_jmp_table.c | 577 ++++++++++++++ drivers/hid/hid-core.c | 15 + include/linux/hid.h | 5 + include/linux/hid_bpf.h | 99 +++ include/uapi/linux/hid_bpf.h | 25 + 15 files changed, 1914 insertions(+) create mode 100644 drivers/hid/bpf/Kconfig create mode 100644 drivers/hid/bpf/Makefile create mode 100644 drivers/hid/bpf/entrypoints/Makefile create mode 100644 drivers/hid/bpf/entrypoints/README create mode 100644 drivers/hid/bpf/entrypoints/entrypoints.bpf.c create mode 100644 drivers/hid/bpf/entrypoints/entrypoints.lskel.h create mode 100644 drivers/hid/bpf/hid_bpf_dispatch.c create mode 100644 drivers/hid/bpf/hid_bpf_dispatch.h create mode 100644 drivers/hid/bpf/hid_bpf_jmp_table.c create mode 100644 include/linux/hid_bpf.h create mode 100644 include/uapi/linux/hid_bpf.h diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index a95a7cbc4a59..09dd5ecaa9a0 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1302,6 +1302,8 @@ endmenu endif # HID +source "drivers/hid/bpf/Kconfig" + source "drivers/hid/usbhid/Kconfig" source "drivers/hid/i2c-hid/Kconfig" diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 345ac5581bd8..9e21f9799876 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -5,6 +5,8 @@ hid-y := hid-core.o hid-input.o hid-quirks.o hid-$(CONFIG_DEBUG_FS) += hid-debug.o +obj-$(CONFIG_HID_BPF) += bpf/ + obj-$(CONFIG_HID) += hid.o obj-$(CONFIG_UHID) += uhid.o diff --git a/drivers/hid/bpf/Kconfig b/drivers/hid/bpf/Kconfig new file mode 100644 index 000000000000..c54a2f07b8d7 --- /dev/null +++ b/drivers/hid/bpf/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only +menu "HID-BPF support" + #depends on x86_64 + +config HID_BPF + bool "HID-BPF support" + default y + depends on BPF && BPF_SYSCALL + select HID + help + This option allows to support eBPF programs on the HID subsystem. + eBPF programs can fix HID devices in a lighter way than a full + kernel patch and allow a lot more flexibility. + + For documentation, see Documentation/hid/hid-bpf.rst + + If unsure, say Y. + +endmenu diff --git a/drivers/hid/bpf/Makefile b/drivers/hid/bpf/Makefile new file mode 100644 index 000000000000..cf55120cf7d6 --- /dev/null +++ b/drivers/hid/bpf/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for HID-BPF +# + +LIBBPF_INCLUDE = $(srctree)/tools/lib + +obj-$(CONFIG_HID_BPF) += hid_bpf.o +CFLAGS_hid_bpf_dispatch.o += -I$(LIBBPF_INCLUDE) +CFLAGS_hid_bpf_jmp_table.o += -I$(LIBBPF_INCLUDE) +hid_bpf-objs += hid_bpf_dispatch.o hid_bpf_jmp_table.o diff --git a/drivers/hid/bpf/entrypoints/Makefile b/drivers/hid/bpf/entrypoints/Makefile new file mode 100644 index 000000000000..dd60a460c6c4 --- /dev/null +++ b/drivers/hid/bpf/entrypoints/Makefile @@ -0,0 +1,88 @@ +# 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 := $(OUTPUT)/sbin/bpftool +BPFTOOL ?= $(DEFAULT_BPFTOOL) + +LIBBPF_SRC := $(TOOLS_PATH)/lib/bpf +LIBBPF_OUTPUT := $(abs_out)/libbpf +LIBBPF_DESTDIR := $(LIBBPF_OUTPUT) +LIBBPF_INCLUDE := $(LIBBPF_DESTDIR)/include +BPFOBJ := $(LIBBPF_OUTPUT)/libbpf.a + +INCLUDES := -I$(OUTPUT) -I$(LIBBPF_INCLUDE) -I$(TOOLS_PATH)/include/uapi +CFLAGS := -g -Wall + +VMLINUX_BTF_PATHS ?= $(if $(O),$(O)/vmlinux) \ + $(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/vmlinux) \ + ../../../../vmlinux \ + /sys/kernel/btf/vmlinux \ + /boot/vmlinux-$(shell uname -r) +VMLINUX_BTF ?= $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS)))) +ifeq ($(VMLINUX_BTF),) +$(error Cannot find a vmlinux for VMLINUX_BTF at any of "$(VMLINUX_BTF_PATHS)") +endif + +ifeq ($(V),1) +Q = +msg = +else +Q = @ +msg = @printf ' %-8s %s%s\n' "$(1)" "$(notdir $(2))" "$(if $(3), $(3))"; +MAKEFLAGS += --no-print-directory +submake_extras := feature_display=0 +endif + +.DELETE_ON_ERROR: + +.PHONY: all clean + +all: entrypoints.lskel.h + +clean: + $(call msg,CLEAN) + $(Q)rm -rf $(OUTPUT) entrypoints + +entrypoints.lskel.h: $(OUTPUT)/entrypoints.bpf.o | $(BPFTOOL) + $(call msg,GEN-SKEL,$@) + $(Q)$(BPFTOOL) gen skeleton -L $< > $@ + + +$(OUTPUT)/entrypoints.bpf.o: entrypoints.bpf.c $(OUTPUT)/vmlinux.h $(BPFOBJ) | $(OUTPUT) + $(call msg,BPF,$@) + $(Q)$(CLANG) -g -O2 -target bpf $(INCLUDES) \ + -c $(filter %.c,$^) -o $@ && \ + $(LLVM_STRIP) -g $@ + +$(OUTPUT)/vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL) | $(INCLUDE_DIR) +ifeq ($(VMLINUX_H),) + $(call msg,GEN,,$@) + $(Q)$(BPFTOOL) btf dump file $(VMLINUX_BTF) format c > $@ +else + $(call msg,CP,,$@) + $(Q)cp "$(VMLINUX_H)" $@ +endif + +$(OUTPUT) $(LIBBPF_OUTPUT) $(BPFTOOL_OUTPUT): + $(call msg,MKDIR,$@) + $(Q)mkdir -p $@ + +$(BPFOBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(LIBBPF_OUTPUT) + $(Q)$(MAKE) $(submake_extras) -C $(LIBBPF_SRC) \ + OUTPUT=$(abspath $(dir $@))/ prefix= \ + DESTDIR=$(LIBBPF_DESTDIR) $(abspath $@) install_headers + +$(DEFAULT_BPFTOOL): $(BPFOBJ) | $(BPFTOOL_OUTPUT) + $(Q)$(MAKE) $(submake_extras) -C $(BPFTOOL_SRC) \ + OUTPUT=$(BPFTOOL_OUTPUT)/ \ + LIBBPF_OUTPUT=$(LIBBPF_OUTPUT)/ \ + LIBBPF_DESTDIR=$(LIBBPF_DESTDIR)/ \ + prefix= DESTDIR=$(abs_out)/ install-bin diff --git a/drivers/hid/bpf/entrypoints/README b/drivers/hid/bpf/entrypoints/README new file mode 100644 index 000000000000..147e0d41509f --- /dev/null +++ b/drivers/hid/bpf/entrypoints/README @@ -0,0 +1,4 @@ +WARNING: +If you change "entrypoints.bpf.c" do "make -j" in this directory to rebuild "entrypoints.skel.h". +Make sure to have clang 10 installed. +See Documentation/bpf/bpf_devel_QA.rst diff --git a/drivers/hid/bpf/entrypoints/entrypoints.bpf.c b/drivers/hid/bpf/entrypoints/entrypoints.bpf.c new file mode 100644 index 000000000000..edabbafc06b7 --- /dev/null +++ b/drivers/hid/bpf/entrypoints/entrypoints.bpf.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Benjamin Tissoires */ + +#include ".output/vmlinux.h" +#include +#include + +#define HID_BPF_MAX_PROGS 1024 + +extern bool call_hid_bpf_prog_release(u64 prog, int table_cnt) __ksym; + +struct { + __uint(type, BPF_MAP_TYPE_PROG_ARRAY); + __uint(max_entries, HID_BPF_MAX_PROGS); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(__u32)); +} hid_jmp_table SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, HID_BPF_MAX_PROGS * HID_BPF_PROG_TYPE_MAX); + __type(key, void *); + __type(value, __u8); +} progs_map SEC(".maps"); + +SEC("fmod_ret/__hid_bpf_tail_call") +int BPF_PROG(hid_tail_call, struct hid_bpf_ctx *hctx) +{ + bpf_tail_call(ctx, &hid_jmp_table, hctx->index); + + return 0; +} + +SEC("fmod_ret/hid_bpf_device_event") +int BPF_PROG(hid_device_event, struct hid_bpf_ctx *hctx) +{ + return 0; +} + +static void release_prog(u64 prog) +{ + u8 *value; + + value = bpf_map_lookup_elem(&progs_map, &prog); + if (!value) + return; + + if (call_hid_bpf_prog_release(prog, *value)) + bpf_map_delete_elem(&progs_map, &prog); +} + +SEC("fexit/bpf_prog_release") +int BPF_PROG(hid_prog_release, struct inode *inode, struct file *filp) +{ + u64 prog = (u64)filp->private_data; + + release_prog(prog); + + return 0; +} + +SEC("fexit/bpf_free_inode") +int BPF_PROG(hid_free_inode, struct inode *inode) +{ + u64 prog = (u64)inode->i_private; + + release_prog(prog); + + return 0; +} + +char LICENSE[] SEC("license") = "GPL"; diff --git a/drivers/hid/bpf/entrypoints/entrypoints.lskel.h b/drivers/hid/bpf/entrypoints/entrypoints.lskel.h new file mode 100644 index 000000000000..002d4ced9b38 --- /dev/null +++ b/drivers/hid/bpf/entrypoints/entrypoints.lskel.h @@ -0,0 +1,733 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +/* THIS FILE IS AUTOGENERATED BY BPFTOOL! */ +#ifndef __ENTRYPOINTS_BPF_SKEL_H__ +#define __ENTRYPOINTS_BPF_SKEL_H__ + +#include + +struct entrypoints_bpf { + struct bpf_loader_ctx ctx; + struct { + struct bpf_map_desc hid_jmp_table; + struct bpf_map_desc progs_map; + } maps; + struct { + struct bpf_prog_desc hid_tail_call; + struct bpf_prog_desc hid_device_event; + struct bpf_prog_desc hid_prog_release; + struct bpf_prog_desc hid_free_inode; + } progs; + struct { + int hid_tail_call_fd; + int hid_device_event_fd; + int hid_prog_release_fd; + int hid_free_inode_fd; + } links; +}; + +static inline int +entrypoints_bpf__hid_tail_call__attach(struct entrypoints_bpf *skel) +{ + int prog_fd = skel->progs.hid_tail_call.prog_fd; + int fd = skel_raw_tracepoint_open(NULL, prog_fd); + + if (fd > 0) + skel->links.hid_tail_call_fd = fd; + return fd; +} + +static inline int +entrypoints_bpf__hid_device_event__attach(struct entrypoints_bpf *skel) +{ + int prog_fd = skel->progs.hid_device_event.prog_fd; + int fd = skel_raw_tracepoint_open(NULL, prog_fd); + + if (fd > 0) + skel->links.hid_device_event_fd = fd; + return fd; +} + +static inline int +entrypoints_bpf__hid_prog_release__attach(struct entrypoints_bpf *skel) +{ + int prog_fd = skel->progs.hid_prog_release.prog_fd; + int fd = skel_raw_tracepoint_open(NULL, prog_fd); + + if (fd > 0) + skel->links.hid_prog_release_fd = fd; + return fd; +} + +static inline int +entrypoints_bpf__hid_free_inode__attach(struct entrypoints_bpf *skel) +{ + int prog_fd = skel->progs.hid_free_inode.prog_fd; + int fd = skel_raw_tracepoint_open(NULL, prog_fd); + + if (fd > 0) + skel->links.hid_free_inode_fd = fd; + return fd; +} + +static inline int +entrypoints_bpf__attach(struct entrypoints_bpf *skel) +{ + int ret = 0; + + ret = ret < 0 ? ret : entrypoints_bpf__hid_tail_call__attach(skel); + ret = ret < 0 ? ret : entrypoints_bpf__hid_device_event__attach(skel); + ret = ret < 0 ? ret : entrypoints_bpf__hid_prog_release__attach(skel); + ret = ret < 0 ? ret : entrypoints_bpf__hid_free_inode__attach(skel); + return ret < 0 ? ret : 0; +} + +static inline void +entrypoints_bpf__detach(struct entrypoints_bpf *skel) +{ + skel_closenz(skel->links.hid_tail_call_fd); + skel_closenz(skel->links.hid_device_event_fd); + skel_closenz(skel->links.hid_prog_release_fd); + skel_closenz(skel->links.hid_free_inode_fd); +} +static void +entrypoints_bpf__destroy(struct entrypoints_bpf *skel) +{ + if (!skel) + return; + entrypoints_bpf__detach(skel); + skel_closenz(skel->progs.hid_tail_call.prog_fd); + skel_closenz(skel->progs.hid_device_event.prog_fd); + skel_closenz(skel->progs.hid_prog_release.prog_fd); + skel_closenz(skel->progs.hid_free_inode.prog_fd); + skel_closenz(skel->maps.hid_jmp_table.map_fd); + skel_closenz(skel->maps.progs_map.map_fd); + skel_free(skel); +} +static inline struct entrypoints_bpf * +entrypoints_bpf__open(void) +{ + struct entrypoints_bpf *skel; + + skel = skel_alloc(sizeof(*skel)); + if (!skel) + goto cleanup; + skel->ctx.sz = (void *)&skel->links - (void *)skel; + return skel; +cleanup: + entrypoints_bpf__destroy(skel); + return NULL; +} + +static inline int +entrypoints_bpf__load(struct entrypoints_bpf *skel) +{ + struct bpf_load_and_run_opts opts = {}; + int err; + + opts.ctx = (struct bpf_loader_ctx *)skel; + opts.data_sz = 11856; + opts.data = (void *)"\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\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\x98\x14\0\0\x98\x14\0\0\x5c\x0d\0\0\0\0\0\0\0\0\0\x02\x03\0\ +\0\0\x01\0\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\x01\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\ +\0\0\x04\0\0\0\x03\0\0\0\x05\0\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\ +\x02\x06\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\0\x04\0\0\0\0\0\0\ +\0\0\0\x02\x08\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x04\0\0\0\0\ +\0\0\0\x04\0\0\x04\x20\0\0\0\x19\0\0\0\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\ +\x40\0\0\0\x2a\0\0\0\x07\0\0\0\x80\0\0\0\x33\0\0\0\x07\0\0\0\xc0\0\0\0\x3e\0\0\ +\0\0\0\0\x0e\x09\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x02\x0c\0\0\0\0\0\0\0\0\0\0\x03\ +\0\0\0\0\x02\0\0\0\x04\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x02\x0e\0\0\0\0\0\0\0\0\0\ +\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\x02\x10\0\0\0\0\0\0\ +\0\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\x02\x12\0\0\0\x4c\0\0\0\0\0\0\x08\x13\0\0\0\ +\x51\0\0\0\0\0\0\x01\x01\0\0\0\x08\0\0\0\0\0\0\0\x04\0\0\x04\x20\0\0\0\x19\0\0\ +\0\x0b\0\0\0\0\0\0\0\x1e\0\0\0\x0d\0\0\0\x40\0\0\0\x5f\0\0\0\x0f\0\0\0\x80\0\0\ +\0\x63\0\0\0\x11\0\0\0\xc0\0\0\0\x69\0\0\0\0\0\0\x0e\x14\0\0\0\x01\0\0\0\0\0\0\ +\0\0\0\0\x02\x17\0\0\0\x73\0\0\0\0\0\0\x01\x08\0\0\0\x40\0\0\0\0\0\0\0\x01\0\0\ +\x0d\x02\0\0\0\x86\0\0\0\x16\0\0\0\x8a\0\0\0\x01\0\0\x0c\x18\0\0\0\x30\x01\0\0\ +\x05\0\0\x04\x20\0\0\0\x3c\x01\0\0\x1b\0\0\0\0\0\0\0\x42\x01\0\0\x1d\0\0\0\x40\ +\0\0\0\x46\x01\0\0\x1b\0\0\0\x80\0\0\0\x55\x01\0\0\x1f\0\0\0\xa0\0\0\0\0\0\0\0\ +\x20\0\0\0\xc0\0\0\0\x61\x01\0\0\0\0\0\x08\x1c\0\0\0\x67\x01\0\0\0\0\0\x01\x04\ +\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\x02\x1e\0\0\0\0\0\0\0\0\0\0\x0a\xb7\0\0\0\x74\ +\x01\0\0\x04\0\0\x06\x04\0\0\0\x84\x01\0\0\0\0\0\0\x95\x01\0\0\x01\0\0\0\xa7\ +\x01\0\0\x02\0\0\0\xba\x01\0\0\x03\0\0\0\0\0\0\0\x02\0\0\x05\x04\0\0\0\xcb\x01\ +\0\0\x21\0\0\0\0\0\0\0\xd2\x01\0\0\x21\0\0\0\0\0\0\0\xd7\x01\0\0\0\0\0\x08\x02\ +\0\0\0\0\0\0\0\x01\0\0\x0d\x02\0\0\0\x86\0\0\0\x16\0\0\0\x13\x02\0\0\x01\0\0\ +\x0c\x22\0\0\0\0\0\0\0\x01\0\0\x0d\x02\0\0\0\x86\0\0\0\x16\0\0\0\x7b\x02\0\0\ +\x01\0\0\x0c\x24\0\0\0\xea\x02\0\0\x14\0\0\x04\xc8\x01\0\0\xef\x02\0\0\x27\0\0\ +\0\0\0\0\0\xf3\x02\0\0\x2e\0\0\0\x80\0\0\0\xfa\x02\0\0\x31\0\0\0\0\x01\0\0\x02\ +\x03\0\0\x32\0\0\0\x40\x01\0\0\x07\x03\0\0\x34\0\0\0\x80\x01\0\0\x0e\x03\0\0\ +\x5b\0\0\0\x80\x03\0\0\x16\x03\0\0\x1c\0\0\0\xc0\x03\0\0\x1e\x03\0\0\x61\0\0\0\ +\xe0\x03\0\0\x25\x03\0\0\x62\0\0\0\0\x04\0\0\x30\x03\0\0\x65\0\0\0\x80\x08\0\0\ +\x36\x03\0\0\x67\0\0\0\xc0\x08\0\0\x3e\x03\0\0\x75\0\0\0\x80\x0b\0\0\x45\x03\0\ +\0\x77\0\0\0\xc0\x0b\0\0\x4a\x03\0\0\x78\0\0\0\xc0\x0c\0\0\x54\x03\0\0\x10\0\0\ +\0\0\x0d\0\0\x5f\x03\0\0\x10\0\0\0\x40\x0d\0\0\x6c\x03\0\0\x7a\0\0\0\x80\x0d\0\ +\0\x71\x03\0\0\x7b\0\0\0\xc0\x0d\0\0\x7b\x03\0\0\x7c\0\0\0\0\x0e\0\0\x84\x03\0\ +\0\x7c\0\0\0\x20\x0e\0\0\0\0\0\0\x02\0\0\x05\x10\0\0\0\x8d\x03\0\0\x28\0\0\0\0\ +\0\0\0\x96\x03\0\0\x2a\0\0\0\0\0\0\0\xa1\x03\0\0\x01\0\0\x04\x08\0\0\0\xac\x03\ +\0\0\x29\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\x28\0\0\0\xb1\x03\0\0\x02\0\0\x04\x10\ +\0\0\0\xac\x03\0\0\x2b\0\0\0\0\0\0\0\xbf\x03\0\0\x2c\0\0\0\x40\0\0\0\0\0\0\0\0\ +\0\0\x02\x2a\0\0\0\0\0\0\0\0\0\0\x02\x2d\0\0\0\0\0\0\0\x01\0\0\x0d\0\0\0\0\0\0\ +\0\0\x2b\0\0\0\xc4\x03\0\0\x02\0\0\x04\x10\0\0\0\xc9\x03\0\0\x2f\0\0\0\0\0\0\0\ +\xcd\x03\0\0\x30\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\x02\xc0\0\0\0\0\0\0\0\0\0\0\x02\ +\xb3\0\0\0\0\0\0\0\0\0\0\x02\x83\0\0\0\0\0\0\0\0\0\0\x02\x33\0\0\0\0\0\0\0\0\0\ +\0\x0a\xb5\0\0\0\xd4\x03\0\0\0\0\0\x08\x35\0\0\0\xdf\x03\0\0\x01\0\0\x04\x40\0\ +\0\0\0\0\0\0\x36\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\x05\x40\0\0\0\xe8\x03\0\0\x37\0\ +\0\0\0\0\0\0\0\0\0\0\x59\0\0\0\0\0\0\0\xee\x03\0\0\x05\0\0\x04\x40\0\0\0\xfb\ +\x03\0\0\x38\0\0\0\0\0\0\0\x04\x04\0\0\x1c\0\0\0\x20\0\0\0\x0a\x04\0\0\x1c\0\0\ +\0\x40\0\0\0\x14\x04\0\0\x10\0\0\0\x80\0\0\0\x1a\x04\0\0\x43\0\0\0\xc0\0\0\0\ +\x22\x04\0\0\0\0\0\x08\x39\0\0\0\x32\x04\0\0\x01\0\0\x04\x04\0\0\0\0\0\0\0\x3a\ +\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\x05\x04\0\0\0\x3c\x04\0\0\x3b\0\0\0\0\0\0\0\0\0\ +\0\0\x3d\0\0\0\0\0\0\0\0\0\0\0\x3f\0\0\0\0\0\0\0\x40\x04\0\0\0\0\0\x08\x3c\0\0\ +\0\0\0\0\0\x01\0\0\x04\x04\0\0\0\x49\x04\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\ +\x04\x02\0\0\0\x51\x04\0\0\x3e\0\0\0\0\0\0\0\x58\x04\0\0\x3e\0\0\0\x08\0\0\0\ +\x60\x04\0\0\0\0\0\x08\x12\0\0\0\0\0\0\0\x02\0\0\x04\x04\0\0\0\x63\x04\0\0\x40\ +\0\0\0\0\0\0\0\x72\x04\0\0\x40\0\0\0\x10\0\0\0\x77\x04\0\0\0\0\0\x08\x41\0\0\0\ +\x7b\x04\0\0\0\0\0\x08\x42\0\0\0\x81\x04\0\0\0\0\0\x01\x02\0\0\0\x10\0\0\0\x90\ +\x04\0\0\x06\0\0\x04\x28\0\0\0\x5f\0\0\0\x44\0\0\0\0\0\0\0\x9c\x04\0\0\x58\0\0\ +\0\x40\0\0\0\xa8\x04\0\0\x55\0\0\0\xc0\0\0\0\xad\x04\0\0\x3e\0\0\0\0\x01\0\0\ +\xbd\x04\0\0\x3e\0\0\0\x08\x01\0\0\xcd\x04\0\0\x3e\0\0\0\x10\x01\0\0\0\0\0\0\0\ +\0\0\x02\xb9\0\0\0\0\0\0\0\0\0\0\x02\x46\0\0\0\xd7\x04\0\0\x0e\0\0\x04\xc0\0\0\ +\0\xe2\x04\0\0\x47\0\0\0\0\0\0\0\xed\x04\0\0\x4a\0\0\0\x80\0\0\0\xf8\x04\0\0\ +\x4a\0\0\0\0\x01\0\0\x04\x05\0\0\x4a\0\0\0\x80\x01\0\0\x5f\0\0\0\x4c\0\0\0\0\ +\x02\0\0\x11\x05\0\0\x1c\0\0\0\x40\x02\0\0\x1a\x05\0\0\x1c\0\0\0\x60\x02\0\0\ +\x25\x05\0\0\x4e\0\0\0\x80\x02\0\0\x30\x05\0\0\x54\0\0\0\xc0\x02\0\0\x3d\x05\0\ +\0\x02\0\0\0\x40\x05\0\0\xa8\x04\0\0\x55\0\0\0\x80\x05\0\0\xbd\x04\0\0\x3e\0\0\ +\0\xc0\x05\0\0\xad\x04\0\0\x3e\0\0\0\xc8\x05\0\0\xcd\x04\0\0\x3e\0\0\0\xd0\x05\ +\0\0\x4a\x05\0\0\x02\0\0\x04\x10\0\0\0\xac\x03\0\0\x48\0\0\0\0\0\0\0\x55\x05\0\ +\0\x49\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\x02\x47\0\0\0\0\0\0\0\0\0\0\x02\x48\0\0\0\ +\x5b\x05\0\0\x02\0\0\x04\x10\0\0\0\xac\x03\0\0\x4b\0\0\0\0\0\0\0\x65\x05\0\0\ +\x4b\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\x02\x4a\0\0\0\0\0\0\0\0\0\0\x02\x4d\0\0\0\0\ +\0\0\0\0\0\0\x0a\xba\0\0\0\x6a\x05\0\0\0\0\0\x01\x08\0\0\0\x40\0\0\0\0\0\0\0\0\ +\0\0\x02\x50\0\0\0\0\0\0\0\0\0\0\x0a\x51\0\0\0\x78\x05\0\0\x04\0\0\x04\x18\0\0\ +\0\xe2\x04\0\0\x47\0\0\0\0\0\0\0\x83\x05\0\0\x52\0\0\0\x80\0\0\0\x88\x05\0\0\ +\x52\0\0\0\xa0\0\0\0\x93\x05\0\0\x53\0\0\0\xc0\0\0\0\x9b\x05\0\0\0\0\0\x08\x1b\ +\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x4e\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\ +\0\0\0\0\x4f\0\0\0\x04\0\0\0\x0a\0\0\0\0\0\0\0\0\0\0\x02\x56\0\0\0\0\0\0\0\0\0\ +\0\x0a\x57\0\0\0\x9f\x05\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\x45\0\0\0\x04\0\0\0\x02\0\0\0\0\0\0\0\x02\0\0\x04\x40\0\0\0\xa4\x05\0\ +\0\x5a\0\0\0\0\0\0\0\x1a\x04\0\0\x43\0\0\0\xc0\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\ +\x3e\0\0\0\x04\0\0\0\x18\0\0\0\xae\x05\0\0\0\0\0\x08\x5c\0\0\0\xbc\x05\0\0\0\0\ +\0\x08\x5d\0\0\0\0\0\0\0\x01\0\0\x04\x08\0\0\0\x49\x04\0\0\x5e\0\0\0\0\0\0\0\ +\xc7\x05\0\0\0\0\0\x08\x5f\0\0\0\xcb\x05\0\0\0\0\0\x08\x60\0\0\0\xd1\x05\0\0\0\ +\0\0\x01\x08\0\0\0\x40\0\0\x01\xdb\x05\0\0\0\0\0\x08\x1c\0\0\0\xe3\x05\0\0\x06\ +\0\0\x04\x90\0\0\0\x14\x04\0\0\x5b\0\0\0\0\0\0\0\xe9\x05\0\0\x63\0\0\0\x40\0\0\ +\0\xf3\x05\0\0\x64\0\0\0\x40\x02\0\0\xf7\x05\0\0\x4a\0\0\0\x80\x02\0\0\x04\x04\ +\0\0\x10\0\0\0\0\x03\0\0\x1a\x04\0\0\x43\0\0\0\x40\x03\0\0\x01\x06\0\0\0\0\0\ +\x08\x37\0\0\0\x10\x06\0\0\x01\0\0\x04\x04\0\0\0\x72\x04\0\0\x3b\0\0\0\0\0\0\0\ +\x26\x06\0\0\0\0\0\x08\x66\0\0\0\x2d\x06\0\0\0\0\0\x08\x60\0\0\0\x3d\x06\0\0\ +\x06\0\0\x04\x58\0\0\0\x49\x06\0\0\x68\0\0\0\0\0\0\0\x4e\x06\0\0\x6f\0\0\0\0\ +\x02\0\0\x52\x06\0\0\x70\0\0\0\x40\x02\0\0\x5b\x06\0\0\x71\0\0\0\x60\x02\0\0\ +\x5f\x06\0\0\x71\0\0\0\x80\x02\0\0\x64\x06\0\0\x02\0\0\0\xa0\x02\0\0\x6b\x06\0\ +\0\0\0\0\x08\x69\0\0\0\0\0\0\0\x05\0\0\x04\x40\0\0\0\xfb\x03\0\0\x6a\0\0\0\0\0\ +\0\0\x04\x04\0\0\x1c\0\0\0\x40\0\0\0\x0a\x04\0\0\x1c\0\0\0\x60\0\0\0\x14\x04\0\ +\0\x10\0\0\0\x80\0\0\0\x1a\x04\0\0\x43\0\0\0\xc0\0\0\0\x74\x06\0\0\0\0\0\x08\ +\x6b\0\0\0\x82\x06\0\0\x02\0\0\x04\x08\0\0\0\0\0\0\0\x6c\0\0\0\0\0\0\0\xe9\x05\ +\0\0\x38\0\0\0\x20\0\0\0\0\0\0\0\x02\0\0\x05\x04\0\0\0\x8a\x06\0\0\x3b\0\0\0\0\ +\0\0\0\0\0\0\0\x6d\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\x04\x04\0\0\0\x8f\x06\0\0\x3e\ +\0\0\0\0\0\0\0\x97\x06\0\0\x6e\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x3e\0\ +\0\0\x04\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\x02\xbb\0\0\0\x52\x06\0\0\x05\0\0\x06\ +\x04\0\0\0\xa0\x06\0\0\0\0\0\0\xac\x06\0\0\x01\0\0\0\xb9\x06\0\0\x02\0\0\0\xc6\ +\x06\0\0\x03\0\0\0\xd2\x06\0\0\x04\0\0\0\xde\x06\0\0\0\0\0\x08\x72\0\0\0\0\0\0\ +\0\x01\0\0\x04\x04\0\0\0\x3c\x04\0\0\x73\0\0\0\0\0\0\0\xe5\x06\0\0\0\0\0\x08\ +\x74\0\0\0\xeb\x06\0\0\0\0\0\x08\x1c\0\0\0\0\0\0\0\0\0\0\x02\x76\0\0\0\0\0\0\0\ +\0\0\0\x0a\xb2\0\0\0\xfc\x06\0\0\x06\0\0\x04\x20\0\0\0\x0a\x07\0\0\x4e\0\0\0\0\ +\0\0\0\xd2\x01\0\0\x1c\0\0\0\x40\0\0\0\x10\x07\0\0\x1c\0\0\0\x60\0\0\0\x1b\x07\ +\0\0\x1c\0\0\0\x80\0\0\0\x24\x07\0\0\x1c\0\0\0\xa0\0\0\0\x2e\x07\0\0\x65\0\0\0\ +\xc0\0\0\0\x37\x07\0\0\0\0\0\x08\x79\0\0\0\x3b\x07\0\0\0\0\0\x08\x17\0\0\0\0\0\ +\0\0\0\0\0\x02\x98\0\0\0\0\0\0\0\0\0\0\x02\x9d\0\0\0\x41\x07\0\0\0\0\0\x08\x52\ +\0\0\0\0\0\0\0\x02\0\0\x0d\x7e\0\0\0\x51\x0d\0\0\x78\0\0\0\x51\x0d\0\0\x02\0\0\ +\0\x0a\x08\0\0\0\0\0\x08\x7f\0\0\0\x0f\x08\0\0\0\0\0\x01\x01\0\0\0\x08\0\0\x04\ +\x15\x08\0\0\x01\0\0\x0c\x7d\0\0\0\0\0\0\0\x01\0\0\x0d\x02\0\0\0\x86\0\0\0\x16\ +\0\0\0\x2f\x08\0\0\x01\0\0\x0c\x81\0\0\0\x85\x08\0\0\x34\0\0\x04\x78\x04\0\0\ +\x8b\x08\0\0\x84\0\0\0\0\0\0\0\x92\x08\0\0\x42\0\0\0\x10\0\0\0\x9c\x08\0\0\x71\ +\0\0\0\x20\0\0\0\xa2\x08\0\0\x85\0\0\0\x40\0\0\0\xa8\x08\0\0\x1c\0\0\0\x60\0\0\ +\0\xb0\x08\0\0\x89\0\0\0\x80\0\0\0\xb6\x08\0\0\x89\0\0\0\xc0\0\0\0\xc4\x08\0\0\ +\x8a\0\0\0\0\x01\0\0\xc9\x08\0\0\x8c\0\0\0\x40\x01\0\0\xce\x08\0\0\x7b\0\0\0\ +\x80\x01\0\0\xd8\x08\0\0\x10\0\0\0\xc0\x01\0\0\xe3\x08\0\0\x4e\0\0\0\0\x02\0\0\ +\0\0\0\0\x8d\0\0\0\x40\x02\0\0\xe9\x08\0\0\x8f\0\0\0\x60\x02\0\0\xf0\x08\0\0\ +\x65\0\0\0\x80\x02\0\0\xf7\x08\0\0\x91\0\0\0\xc0\x02\0\0\xff\x08\0\0\x91\0\0\0\ +\x40\x03\0\0\x07\x09\0\0\x91\0\0\0\xc0\x03\0\0\x0f\x09\0\0\x34\0\0\0\x40\x04\0\ +\0\x16\x09\0\0\x42\0\0\0\x40\x06\0\0\x1e\x09\0\0\x3e\0\0\0\x50\x06\0\0\x28\x09\ +\0\0\x3e\0\0\0\x58\x06\0\0\x35\x09\0\0\x94\0\0\0\x80\x06\0\0\x3e\x09\0\0\x4e\0\ +\0\0\xc0\x06\0\0\x46\x09\0\0\x95\0\0\0\0\x07\0\0\x4e\x09\0\0\x4e\0\0\0\xc0\x0b\ +\0\0\x5b\x09\0\0\x4e\0\0\0\0\x0c\0\0\x6d\x09\0\0\x47\0\0\0\x40\x0c\0\0\x74\x09\ +\0\0\x4a\0\0\0\xc0\x0c\0\0\x7e\x09\0\0\x96\0\0\0\x40\x0d\0\0\x83\x09\0\0\x02\0\ +\0\0\x80\x0d\0\0\x93\x09\0\0\x40\0\0\0\xa0\x0d\0\0\xa5\x09\0\0\x40\0\0\0\xb0\ +\x0d\0\0\xb6\x09\0\0\x4a\0\0\0\xc0\x0d\0\0\xbc\x09\0\0\x4a\0\0\0\x40\x0e\0\0\ +\xc6\x09\0\0\x4a\0\0\0\xc0\x0e\0\0\0\0\0\0\x97\0\0\0\x40\x0f\0\0\xd0\x09\0\0\ +\x5c\0\0\0\xc0\x0f\0\0\xda\x09\0\0\x5c\0\0\0\0\x10\0\0\xe5\x09\0\0\x3b\0\0\0\ +\x40\x10\0\0\xed\x09\0\0\x3b\0\0\0\x60\x10\0\0\xf9\x09\0\0\x3b\0\0\0\x80\x10\0\ +\0\x06\x0a\0\0\x3b\0\0\0\xa0\x10\0\0\0\0\0\0\x99\0\0\0\xc0\x10\0\0\x12\x0a\0\0\ +\x9c\0\0\0\0\x11\0\0\x1a\x0a\0\0\x9d\0\0\0\x40\x11\0\0\x21\x0a\0\0\x4a\0\0\0\ +\x40\x22\0\0\0\0\0\0\xa5\0\0\0\xc0\x22\0\0\x2b\x0a\0\0\x1b\0\0\0\0\x23\0\0\x38\ +\x0a\0\0\x1b\0\0\0\x20\x23\0\0\x48\x0a\0\0\xa9\0\0\0\x40\x23\0\0\x59\x0a\0\0\ +\x10\0\0\0\x80\x23\0\0\x63\x0a\0\0\0\0\0\x08\x42\0\0\0\x6b\x0a\0\0\0\0\0\x08\ +\x86\0\0\0\0\0\0\0\x01\0\0\x04\x04\0\0\0\x3c\x04\0\0\x87\0\0\0\0\0\0\0\x72\x0a\ +\0\0\0\0\0\x08\x88\0\0\0\x78\x0a\0\0\0\0\0\x08\x1c\0\0\0\0\0\0\0\0\0\0\x02\xbd\ +\0\0\0\0\0\0\0\0\0\0\x02\x8b\0\0\0\0\0\0\0\0\0\0\x0a\xb8\0\0\0\0\0\0\0\0\0\0\ +\x02\xbf\0\0\0\0\0\0\0\x02\0\0\x05\x04\0\0\0\x89\x0a\0\0\x8e\0\0\0\0\0\0\0\x91\ +\x0a\0\0\x1c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\x1c\0\0\0\x9b\x0a\0\0\0\0\0\x08\ +\x90\0\0\0\xa1\x0a\0\0\0\0\0\x08\x52\0\0\0\xb0\x0a\0\0\x02\0\0\x04\x10\0\0\0\ +\xbb\x0a\0\0\x92\0\0\0\0\0\0\0\xc2\x0a\0\0\x93\0\0\0\x40\0\0\0\xca\x0a\0\0\0\0\ +\0\x08\x5f\0\0\0\xd3\x0a\0\0\0\0\0\x01\x08\0\0\0\x40\0\0\x01\xd8\x0a\0\0\0\0\0\ +\x08\x78\0\0\0\xe1\x0a\0\0\x07\0\0\x04\x98\0\0\0\xee\x0a\0\0\x5b\0\0\0\0\0\0\0\ +\x14\x04\0\0\x5b\0\0\0\x40\0\0\0\xf3\x05\0\0\x64\0\0\0\x80\0\0\0\xe9\x05\0\0\ +\x63\0\0\0\xc0\0\0\0\xf7\x05\0\0\x4a\0\0\0\xc0\x02\0\0\x04\x04\0\0\x10\0\0\0\ +\x40\x03\0\0\x1a\x04\0\0\x43\0\0\0\x80\x03\0\0\0\0\0\0\0\0\0\x02\xb0\0\0\0\0\0\ +\0\0\x02\0\0\x05\x10\0\0\0\xf4\x0a\0\0\x98\0\0\0\0\0\0\0\xfd\x0a\0\0\x2a\0\0\0\ +\0\0\0\0\x03\x0b\0\0\x01\0\0\x04\x08\0\0\0\x0e\x0b\0\0\x48\0\0\0\0\0\0\0\0\0\0\ +\0\x02\0\0\x05\x08\0\0\0\x14\x0b\0\0\x32\0\0\0\0\0\0\0\x1a\x0b\0\0\x9a\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\x02\x9b\0\0\0\0\0\0\0\x01\0\0\x0d\0\0\0\0\0\0\0\0\x31\0\0\ +\0\0\0\0\0\0\0\0\x02\xb4\0\0\0\x25\x0b\0\0\x0f\0\0\x04\x20\x02\0\0\x33\x0b\0\0\ +\x31\0\0\0\0\0\0\0\x38\x0b\0\0\x9e\0\0\0\x40\0\0\0\x40\x0b\0\0\x95\0\0\0\xc0\ +\x02\0\0\x50\x0b\0\0\x9f\0\0\0\x80\x07\0\0\x59\x0b\0\0\x3b\0\0\0\xa0\x07\0\0\ +\x69\x0b\0\0\xa0\0\0\0\xc0\x07\0\0\x70\x0b\0\0\x95\0\0\0\x40\x08\0\0\x7d\x0b\0\ +\0\x4e\0\0\0\0\x0d\0\0\x85\x0b\0\0\x4e\0\0\0\x40\x0d\0\0\x95\x0b\0\0\xa3\0\0\0\ +\x80\x0d\0\0\x9b\x0b\0\0\x4e\0\0\0\xc0\x0d\0\0\xa1\x0b\0\0\x7c\0\0\0\0\x0e\0\0\ +\xa8\x0b\0\0\x34\0\0\0\x40\x0e\0\0\xb5\x0b\0\0\x4a\0\0\0\x40\x10\0\0\x5f\x03\0\ +\0\x10\0\0\0\xc0\x10\0\0\xc2\x0b\0\0\x03\0\0\x04\x50\0\0\0\xc9\x0b\0\0\x34\0\0\ +\0\0\0\0\0\xd1\x0b\0\0\x9f\0\0\0\0\x02\0\0\xda\x0b\0\0\x10\0\0\0\x40\x02\0\0\ +\xe2\x0b\0\0\0\0\0\x08\x1c\0\0\0\xe8\x0b\0\0\x02\0\0\x04\x10\0\0\0\xf7\x0b\0\0\ +\xa1\0\0\0\0\0\0\0\xff\x0b\0\0\xa2\0\0\0\x40\0\0\0\xf7\x0b\0\0\x01\0\0\x04\x08\ +\0\0\0\x0b\x0c\0\0\xa2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\xbe\0\0\0\0\0\0\0\0\0\0\ +\x02\xa4\0\0\0\0\0\0\0\0\0\0\x0a\xaf\0\0\0\0\0\0\0\x04\0\0\x05\x08\0\0\0\x13\ +\x0c\0\0\xa6\0\0\0\0\0\0\0\x1a\x0c\0\0\xa7\0\0\0\0\0\0\0\x21\x0c\0\0\xa8\0\0\0\ +\0\0\0\0\x28\x0c\0\0\x1c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\xbc\0\0\0\0\0\0\0\0\0\ +\0\x02\xb1\0\0\0\0\0\0\0\0\0\0\x02\x57\0\0\0\0\0\0\0\0\0\0\x02\xb6\0\0\0\0\0\0\ +\0\0\0\0\x03\0\0\0\0\x57\0\0\0\x04\0\0\0\x04\0\0\0\x5a\x0c\0\0\0\0\0\x0e\xaa\0\ +\0\0\x01\0\0\0\x62\x0c\0\0\x01\0\0\x0f\x04\0\0\0\xc1\0\0\0\0\0\0\0\x04\0\0\0\ +\x69\x0c\0\0\x02\0\0\x0f\x40\0\0\0\x0a\0\0\0\0\0\0\0\x20\0\0\0\x15\0\0\0\x20\0\ +\0\0\x20\0\0\0\x6f\x0c\0\0\x01\0\0\x0f\x04\0\0\0\xab\0\0\0\0\0\0\0\x04\0\0\0\ +\x77\x0c\0\0\0\0\0\x07\0\0\0\0\x90\x0c\0\0\0\0\0\x07\0\0\0\0\x9e\x0c\0\0\0\0\0\ +\x07\0\0\0\0\xa3\x0c\0\0\0\0\0\x07\0\0\0\0\xcd\x03\0\0\0\0\0\x07\0\0\0\0\xa8\ +\x0c\0\0\0\0\0\x07\0\0\0\0\xba\x0c\0\0\0\0\0\x07\0\0\0\0\xca\x0c\0\0\0\0\0\x07\ +\0\0\0\0\xe2\x0c\0\0\0\0\0\x07\0\0\0\0\xed\x0c\0\0\0\0\0\x07\0\0\0\0\xfe\x0c\0\ +\0\0\0\0\x07\0\0\0\0\x0d\x0d\0\0\0\0\0\x07\0\0\0\0\x4e\x06\0\0\0\0\0\x07\0\0\0\ +\0\x22\x0d\0\0\0\0\0\x07\0\0\0\0\x32\x0d\0\0\0\0\0\x07\0\0\0\0\x0b\x0c\0\0\0\0\ +\0\x07\0\0\0\0\x3c\x0d\0\0\0\0\0\x07\0\0\0\0\x48\x0d\0\0\0\0\0\x07\0\0\0\0\x51\ +\x0d\0\0\0\0\0\x0e\x02\0\0\0\x01\0\0\0\0\x69\x6e\x74\0\x5f\x5f\x41\x52\x52\x41\ +\x59\x5f\x53\x49\x5a\x45\x5f\x54\x59\x50\x45\x5f\x5f\0\x74\x79\x70\x65\0\x6d\ +\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\0\x6b\x65\x79\x5f\x73\x69\x7a\x65\0\ +\x76\x61\x6c\x75\x65\x5f\x73\x69\x7a\x65\0\x68\x69\x64\x5f\x6a\x6d\x70\x5f\x74\ +\x61\x62\x6c\x65\0\x5f\x5f\x75\x38\0\x75\x6e\x73\x69\x67\x6e\x65\x64\x20\x63\ +\x68\x61\x72\0\x6b\x65\x79\0\x76\x61\x6c\x75\x65\0\x70\x72\x6f\x67\x73\x5f\x6d\ +\x61\x70\0\x75\x6e\x73\x69\x67\x6e\x65\x64\x20\x6c\x6f\x6e\x67\x20\x6c\x6f\x6e\ +\x67\0\x63\x74\x78\0\x68\x69\x64\x5f\x74\x61\x69\x6c\x5f\x63\x61\x6c\x6c\0\x66\ +\x6d\x6f\x64\x5f\x72\x65\x74\x2f\x5f\x5f\x68\x69\x64\x5f\x62\x70\x66\x5f\x74\ +\x61\x69\x6c\x5f\x63\x61\x6c\x6c\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\x65\x6e\x74\x72\x79\x70\x6f\x69\x6e\x74\ +\x73\x2f\x65\x6e\x74\x72\x79\x70\x6f\x69\x6e\x74\x73\x2e\x62\x70\x66\x2e\x63\0\ +\x69\x6e\x74\x20\x42\x50\x46\x5f\x50\x52\x4f\x47\x28\x68\x69\x64\x5f\x74\x61\ +\x69\x6c\x5f\x63\x61\x6c\x6c\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\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\x30\x3a\x30\0\x09\x62\x70\x66\x5f\x74\x61\x69\x6c\ +\x5f\x63\x61\x6c\x6c\x28\x63\x74\x78\x2c\x20\x26\x68\x69\x64\x5f\x6a\x6d\x70\ +\x5f\x74\x61\x62\x6c\x65\x2c\x20\x68\x63\x74\x78\x2d\x3e\x69\x6e\x64\x65\x78\ +\x29\x3b\0\x68\x69\x64\x5f\x64\x65\x76\x69\x63\x65\x5f\x65\x76\x65\x6e\x74\0\ +\x66\x6d\x6f\x64\x5f\x72\x65\x74\x2f\x68\x69\x64\x5f\x62\x70\x66\x5f\x64\x65\ +\x76\x69\x63\x65\x5f\x65\x76\x65\x6e\x74\0\x69\x6e\x74\x20\x42\x50\x46\x5f\x50\ +\x52\x4f\x47\x28\x68\x69\x64\x5f\x64\x65\x76\x69\x63\x65\x5f\x65\x76\x65\x6e\ +\x74\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\x68\x69\x64\x5f\x70\x72\x6f\x67\x5f\x72\ +\x65\x6c\x65\x61\x73\x65\0\x66\x65\x78\x69\x74\x2f\x62\x70\x66\x5f\x70\x72\x6f\ +\x67\x5f\x72\x65\x6c\x65\x61\x73\x65\0\x69\x6e\x74\x20\x42\x50\x46\x5f\x50\x52\ +\x4f\x47\x28\x68\x69\x64\x5f\x70\x72\x6f\x67\x5f\x72\x65\x6c\x65\x61\x73\x65\ +\x2c\x20\x73\x74\x72\x75\x63\x74\x20\x69\x6e\x6f\x64\x65\x20\x2a\x69\x6e\x6f\ +\x64\x65\x2c\x20\x73\x74\x72\x75\x63\x74\x20\x66\x69\x6c\x65\x20\x2a\x66\x69\ +\x6c\x70\x29\0\x66\x69\x6c\x65\0\x66\x5f\x75\0\x66\x5f\x70\x61\x74\x68\0\x66\ +\x5f\x69\x6e\x6f\x64\x65\0\x66\x5f\x6f\x70\0\x66\x5f\x6c\x6f\x63\x6b\0\x66\x5f\ +\x63\x6f\x75\x6e\x74\0\x66\x5f\x66\x6c\x61\x67\x73\0\x66\x5f\x6d\x6f\x64\x65\0\ +\x66\x5f\x70\x6f\x73\x5f\x6c\x6f\x63\x6b\0\x66\x5f\x70\x6f\x73\0\x66\x5f\x6f\ +\x77\x6e\x65\x72\0\x66\x5f\x63\x72\x65\x64\0\x66\x5f\x72\x61\0\x66\x5f\x76\x65\ +\x72\x73\x69\x6f\x6e\0\x66\x5f\x73\x65\x63\x75\x72\x69\x74\x79\0\x70\x72\x69\ +\x76\x61\x74\x65\x5f\x64\x61\x74\x61\0\x66\x5f\x65\x70\0\x66\x5f\x6d\x61\x70\ +\x70\x69\x6e\x67\0\x66\x5f\x77\x62\x5f\x65\x72\x72\0\x66\x5f\x73\x62\x5f\x65\ +\x72\x72\0\x66\x75\x5f\x6c\x6c\x69\x73\x74\0\x66\x75\x5f\x72\x63\x75\x68\x65\ +\x61\x64\0\x6c\x6c\x69\x73\x74\x5f\x6e\x6f\x64\x65\0\x6e\x65\x78\x74\0\x63\x61\ +\x6c\x6c\x62\x61\x63\x6b\x5f\x68\x65\x61\x64\0\x66\x75\x6e\x63\0\x70\x61\x74\ +\x68\0\x6d\x6e\x74\0\x64\x65\x6e\x74\x72\x79\0\x73\x70\x69\x6e\x6c\x6f\x63\x6b\ +\x5f\x74\0\x73\x70\x69\x6e\x6c\x6f\x63\x6b\0\x72\x6c\x6f\x63\x6b\0\x72\x61\x77\ +\x5f\x73\x70\x69\x6e\x6c\x6f\x63\x6b\0\x72\x61\x77\x5f\x6c\x6f\x63\x6b\0\x6d\ +\x61\x67\x69\x63\0\x6f\x77\x6e\x65\x72\x5f\x63\x70\x75\0\x6f\x77\x6e\x65\x72\0\ +\x64\x65\x70\x5f\x6d\x61\x70\0\x61\x72\x63\x68\x5f\x73\x70\x69\x6e\x6c\x6f\x63\ +\x6b\x5f\x74\0\x71\x73\x70\x69\x6e\x6c\x6f\x63\x6b\0\x76\x61\x6c\0\x61\x74\x6f\ +\x6d\x69\x63\x5f\x74\0\x63\x6f\x75\x6e\x74\x65\x72\0\x6c\x6f\x63\x6b\x65\x64\0\ +\x70\x65\x6e\x64\x69\x6e\x67\0\x75\x38\0\x6c\x6f\x63\x6b\x65\x64\x5f\x70\x65\ +\x6e\x64\x69\x6e\x67\0\x74\x61\x69\x6c\0\x75\x31\x36\0\x5f\x5f\x75\x31\x36\0\ +\x75\x6e\x73\x69\x67\x6e\x65\x64\x20\x73\x68\x6f\x72\x74\0\x6c\x6f\x63\x6b\x64\ +\x65\x70\x5f\x6d\x61\x70\0\x63\x6c\x61\x73\x73\x5f\x63\x61\x63\x68\x65\0\x6e\ +\x61\x6d\x65\0\x77\x61\x69\x74\x5f\x74\x79\x70\x65\x5f\x6f\x75\x74\x65\x72\0\ +\x77\x61\x69\x74\x5f\x74\x79\x70\x65\x5f\x69\x6e\x6e\x65\x72\0\x6c\x6f\x63\x6b\ +\x5f\x74\x79\x70\x65\0\x6c\x6f\x63\x6b\x5f\x63\x6c\x61\x73\x73\0\x68\x61\x73\ +\x68\x5f\x65\x6e\x74\x72\x79\0\x6c\x6f\x63\x6b\x5f\x65\x6e\x74\x72\x79\0\x6c\ +\x6f\x63\x6b\x73\x5f\x61\x66\x74\x65\x72\0\x6c\x6f\x63\x6b\x73\x5f\x62\x65\x66\ +\x6f\x72\x65\0\x73\x75\x62\x63\x6c\x61\x73\x73\0\x64\x65\x70\x5f\x67\x65\x6e\ +\x5f\x69\x64\0\x75\x73\x61\x67\x65\x5f\x6d\x61\x73\x6b\0\x75\x73\x61\x67\x65\ +\x5f\x74\x72\x61\x63\x65\x73\0\x6e\x61\x6d\x65\x5f\x76\x65\x72\x73\x69\x6f\x6e\ +\0\x68\x6c\x69\x73\x74\x5f\x6e\x6f\x64\x65\0\x70\x70\x72\x65\x76\0\x6c\x69\x73\ +\x74\x5f\x68\x65\x61\x64\0\x70\x72\x65\x76\0\x75\x6e\x73\x69\x67\x6e\x65\x64\ +\x20\x6c\x6f\x6e\x67\0\x6c\x6f\x63\x6b\x5f\x74\x72\x61\x63\x65\0\x68\x61\x73\ +\x68\0\x6e\x72\x5f\x65\x6e\x74\x72\x69\x65\x73\0\x65\x6e\x74\x72\x69\x65\x73\0\ +\x75\x33\x32\0\x63\x68\x61\x72\0\x5f\x5f\x70\x61\x64\x64\x69\x6e\x67\0\x61\x74\ +\x6f\x6d\x69\x63\x5f\x6c\x6f\x6e\x67\x5f\x74\0\x61\x74\x6f\x6d\x69\x63\x36\x34\ +\x5f\x74\0\x73\x36\x34\0\x5f\x5f\x73\x36\x34\0\x6c\x6f\x6e\x67\x20\x6c\x6f\x6e\ +\x67\0\x66\x6d\x6f\x64\x65\x5f\x74\0\x6d\x75\x74\x65\x78\0\x77\x61\x69\x74\x5f\ +\x6c\x6f\x63\x6b\0\x6f\x73\x71\0\x77\x61\x69\x74\x5f\x6c\x69\x73\x74\0\x72\x61\ +\x77\x5f\x73\x70\x69\x6e\x6c\x6f\x63\x6b\x5f\x74\0\x6f\x70\x74\x69\x6d\x69\x73\ +\x74\x69\x63\x5f\x73\x70\x69\x6e\x5f\x71\x75\x65\x75\x65\0\x6c\x6f\x66\x66\x5f\ +\x74\0\x5f\x5f\x6b\x65\x72\x6e\x65\x6c\x5f\x6c\x6f\x66\x66\x5f\x74\0\x66\x6f\ +\x77\x6e\x5f\x73\x74\x72\x75\x63\x74\0\x6c\x6f\x63\x6b\0\x70\x69\x64\0\x70\x69\ +\x64\x5f\x74\x79\x70\x65\0\x75\x69\x64\0\x65\x75\x69\x64\0\x73\x69\x67\x6e\x75\ +\x6d\0\x72\x77\x6c\x6f\x63\x6b\x5f\x74\0\x61\x72\x63\x68\x5f\x72\x77\x6c\x6f\ +\x63\x6b\x5f\x74\0\x71\x72\x77\x6c\x6f\x63\x6b\0\x63\x6e\x74\x73\0\x77\x6c\x6f\ +\x63\x6b\x65\x64\0\x5f\x5f\x6c\x73\x74\x61\x74\x65\0\x50\x49\x44\x54\x59\x50\ +\x45\x5f\x50\x49\x44\0\x50\x49\x44\x54\x59\x50\x45\x5f\x54\x47\x49\x44\0\x50\ +\x49\x44\x54\x59\x50\x45\x5f\x50\x47\x49\x44\0\x50\x49\x44\x54\x59\x50\x45\x5f\ +\x53\x49\x44\0\x50\x49\x44\x54\x59\x50\x45\x5f\x4d\x41\x58\0\x6b\x75\x69\x64\ +\x5f\x74\0\x75\x69\x64\x5f\x74\0\x5f\x5f\x6b\x65\x72\x6e\x65\x6c\x5f\x75\x69\ +\x64\x33\x32\x5f\x74\0\x66\x69\x6c\x65\x5f\x72\x61\x5f\x73\x74\x61\x74\x65\0\ +\x73\x74\x61\x72\x74\0\x61\x73\x79\x6e\x63\x5f\x73\x69\x7a\x65\0\x72\x61\x5f\ +\x70\x61\x67\x65\x73\0\x6d\x6d\x61\x70\x5f\x6d\x69\x73\x73\0\x70\x72\x65\x76\ +\x5f\x70\x6f\x73\0\x75\x36\x34\0\x5f\x5f\x75\x36\x34\0\x65\x72\x72\x73\x65\x71\ +\x5f\x74\0\x30\x3a\x31\x35\0\x09\x75\x36\x34\x20\x70\x72\x6f\x67\x20\x3d\x20\ +\x28\x75\x36\x34\x29\x66\x69\x6c\x70\x2d\x3e\x70\x72\x69\x76\x61\x74\x65\x5f\ +\x64\x61\x74\x61\x3b\0\x09\x76\x61\x6c\x75\x65\x20\x3d\x20\x62\x70\x66\x5f\x6d\ +\x61\x70\x5f\x6c\x6f\x6f\x6b\x75\x70\x5f\x65\x6c\x65\x6d\x28\x26\x70\x72\x6f\ +\x67\x73\x5f\x6d\x61\x70\x2c\x20\x26\x70\x72\x6f\x67\x29\x3b\0\x09\x69\x66\x20\ +\x28\x21\x76\x61\x6c\x75\x65\x29\0\x09\x69\x66\x20\x28\x63\x61\x6c\x6c\x5f\x68\ +\x69\x64\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\x5f\x72\x65\x6c\x65\x61\x73\x65\ +\x28\x70\x72\x6f\x67\x2c\x20\x2a\x76\x61\x6c\x75\x65\x29\x29\0\x09\x09\x62\x70\ +\x66\x5f\x6d\x61\x70\x5f\x64\x65\x6c\x65\x74\x65\x5f\x65\x6c\x65\x6d\x28\x26\ +\x70\x72\x6f\x67\x73\x5f\x6d\x61\x70\x2c\x20\x26\x70\x72\x6f\x67\x29\x3b\0\x62\ +\x6f\x6f\x6c\0\x5f\x42\x6f\x6f\x6c\0\x63\x61\x6c\x6c\x5f\x68\x69\x64\x5f\x62\ +\x70\x66\x5f\x70\x72\x6f\x67\x5f\x72\x65\x6c\x65\x61\x73\x65\0\x68\x69\x64\x5f\ +\x66\x72\x65\x65\x5f\x69\x6e\x6f\x64\x65\0\x66\x65\x78\x69\x74\x2f\x62\x70\x66\ +\x5f\x66\x72\x65\x65\x5f\x69\x6e\x6f\x64\x65\0\x69\x6e\x74\x20\x42\x50\x46\x5f\ +\x50\x52\x4f\x47\x28\x68\x69\x64\x5f\x66\x72\x65\x65\x5f\x69\x6e\x6f\x64\x65\ +\x2c\x20\x73\x74\x72\x75\x63\x74\x20\x69\x6e\x6f\x64\x65\x20\x2a\x69\x6e\x6f\ +\x64\x65\x29\0\x69\x6e\x6f\x64\x65\0\x69\x5f\x6d\x6f\x64\x65\0\x69\x5f\x6f\x70\ +\x66\x6c\x61\x67\x73\0\x69\x5f\x75\x69\x64\0\x69\x5f\x67\x69\x64\0\x69\x5f\x66\ +\x6c\x61\x67\x73\0\x69\x5f\x61\x63\x6c\0\x69\x5f\x64\x65\x66\x61\x75\x6c\x74\ +\x5f\x61\x63\x6c\0\x69\x5f\x6f\x70\0\x69\x5f\x73\x62\0\x69\x5f\x6d\x61\x70\x70\ +\x69\x6e\x67\0\x69\x5f\x73\x65\x63\x75\x72\x69\x74\x79\0\x69\x5f\x69\x6e\x6f\0\ +\x69\x5f\x72\x64\x65\x76\0\x69\x5f\x73\x69\x7a\x65\0\x69\x5f\x61\x74\x69\x6d\ +\x65\0\x69\x5f\x6d\x74\x69\x6d\x65\0\x69\x5f\x63\x74\x69\x6d\x65\0\x69\x5f\x6c\ +\x6f\x63\x6b\0\x69\x5f\x62\x79\x74\x65\x73\0\x69\x5f\x62\x6c\x6b\x62\x69\x74\ +\x73\0\x69\x5f\x77\x72\x69\x74\x65\x5f\x68\x69\x6e\x74\0\x69\x5f\x62\x6c\x6f\ +\x63\x6b\x73\0\x69\x5f\x73\x74\x61\x74\x65\0\x69\x5f\x72\x77\x73\x65\x6d\0\x64\ +\x69\x72\x74\x69\x65\x64\x5f\x77\x68\x65\x6e\0\x64\x69\x72\x74\x69\x65\x64\x5f\ +\x74\x69\x6d\x65\x5f\x77\x68\x65\x6e\0\x69\x5f\x68\x61\x73\x68\0\x69\x5f\x69\ +\x6f\x5f\x6c\x69\x73\x74\0\x69\x5f\x77\x62\0\x69\x5f\x77\x62\x5f\x66\x72\x6e\ +\x5f\x77\x69\x6e\x6e\x65\x72\0\x69\x5f\x77\x62\x5f\x66\x72\x6e\x5f\x61\x76\x67\ +\x5f\x74\x69\x6d\x65\0\x69\x5f\x77\x62\x5f\x66\x72\x6e\x5f\x68\x69\x73\x74\x6f\ +\x72\x79\0\x69\x5f\x6c\x72\x75\0\x69\x5f\x73\x62\x5f\x6c\x69\x73\x74\0\x69\x5f\ +\x77\x62\x5f\x6c\x69\x73\x74\0\x69\x5f\x76\x65\x72\x73\x69\x6f\x6e\0\x69\x5f\ +\x73\x65\x71\x75\x65\x6e\x63\x65\0\x69\x5f\x63\x6f\x75\x6e\x74\0\x69\x5f\x64\ +\x69\x6f\x5f\x63\x6f\x75\x6e\x74\0\x69\x5f\x77\x72\x69\x74\x65\x63\x6f\x75\x6e\ +\x74\0\x69\x5f\x72\x65\x61\x64\x63\x6f\x75\x6e\x74\0\x69\x5f\x66\x6c\x63\x74\ +\x78\0\x69\x5f\x64\x61\x74\x61\0\x69\x5f\x64\x65\x76\x69\x63\x65\x73\0\x69\x5f\ +\x67\x65\x6e\x65\x72\x61\x74\x69\x6f\x6e\0\x69\x5f\x66\x73\x6e\x6f\x74\x69\x66\ +\x79\x5f\x6d\x61\x73\x6b\0\x69\x5f\x66\x73\x6e\x6f\x74\x69\x66\x79\x5f\x6d\x61\ +\x72\x6b\x73\0\x69\x5f\x70\x72\x69\x76\x61\x74\x65\0\x75\x6d\x6f\x64\x65\x5f\ +\x74\0\x6b\x67\x69\x64\x5f\x74\0\x67\x69\x64\x5f\x74\0\x5f\x5f\x6b\x65\x72\x6e\ +\x65\x6c\x5f\x67\x69\x64\x33\x32\x5f\x74\0\x69\x5f\x6e\x6c\x69\x6e\x6b\0\x5f\ +\x5f\x69\x5f\x6e\x6c\x69\x6e\x6b\0\x64\x65\x76\x5f\x74\0\x5f\x5f\x6b\x65\x72\ +\x6e\x65\x6c\x5f\x64\x65\x76\x5f\x74\0\x74\x69\x6d\x65\x73\x70\x65\x63\x36\x34\ +\0\x74\x76\x5f\x73\x65\x63\0\x74\x76\x5f\x6e\x73\x65\x63\0\x74\x69\x6d\x65\x36\ +\x34\x5f\x74\0\x6c\x6f\x6e\x67\0\x62\x6c\x6b\x63\x6e\x74\x5f\x74\0\x72\x77\x5f\ +\x73\x65\x6d\x61\x70\x68\x6f\x72\x65\0\x63\x6f\x75\x6e\x74\0\x69\x5f\x64\x65\ +\x6e\x74\x72\x79\0\x69\x5f\x72\x63\x75\0\x68\x6c\x69\x73\x74\x5f\x68\x65\x61\ +\x64\0\x66\x69\x72\x73\x74\0\x69\x5f\x66\x6f\x70\0\x66\x72\x65\x65\x5f\x69\x6e\ +\x6f\x64\x65\0\x61\x64\x64\x72\x65\x73\x73\x5f\x73\x70\x61\x63\x65\0\x68\x6f\ +\x73\x74\0\x69\x5f\x70\x61\x67\x65\x73\0\x69\x6e\x76\x61\x6c\x69\x64\x61\x74\ +\x65\x5f\x6c\x6f\x63\x6b\0\x67\x66\x70\x5f\x6d\x61\x73\x6b\0\x69\x5f\x6d\x6d\ +\x61\x70\x5f\x77\x72\x69\x74\x61\x62\x6c\x65\0\x69\x5f\x6d\x6d\x61\x70\0\x69\ +\x5f\x6d\x6d\x61\x70\x5f\x72\x77\x73\x65\x6d\0\x6e\x72\x70\x61\x67\x65\x73\0\ +\x77\x72\x69\x74\x65\x62\x61\x63\x6b\x5f\x69\x6e\x64\x65\x78\0\x61\x5f\x6f\x70\ +\x73\0\x66\x6c\x61\x67\x73\0\x77\x62\x5f\x65\x72\x72\0\x70\x72\x69\x76\x61\x74\ +\x65\x5f\x6c\x6f\x63\x6b\0\x70\x72\x69\x76\x61\x74\x65\x5f\x6c\x69\x73\x74\0\ +\x78\x61\x72\x72\x61\x79\0\x78\x61\x5f\x6c\x6f\x63\x6b\0\x78\x61\x5f\x66\x6c\ +\x61\x67\x73\0\x78\x61\x5f\x68\x65\x61\x64\0\x67\x66\x70\x5f\x74\0\x72\x62\x5f\ +\x72\x6f\x6f\x74\x5f\x63\x61\x63\x68\x65\x64\0\x72\x62\x5f\x72\x6f\x6f\x74\0\ +\x72\x62\x5f\x6c\x65\x66\x74\x6d\x6f\x73\x74\0\x72\x62\x5f\x6e\x6f\x64\x65\0\ +\x69\x5f\x70\x69\x70\x65\0\x69\x5f\x63\x64\x65\x76\0\x69\x5f\x6c\x69\x6e\x6b\0\ +\x69\x5f\x64\x69\x72\x5f\x73\x65\x71\0\x30\x3a\x35\x31\0\x09\x75\x36\x34\x20\ +\x70\x72\x6f\x67\x20\x3d\x20\x28\x75\x36\x34\x29\x69\x6e\x6f\x64\x65\x2d\x3e\ +\x69\x5f\x70\x72\x69\x76\x61\x74\x65\x3b\0\x4c\x49\x43\x45\x4e\x53\x45\0\x2e\ +\x6b\x73\x79\x6d\x73\0\x2e\x6d\x61\x70\x73\0\x6c\x69\x63\x65\x6e\x73\x65\0\x61\ +\x64\x64\x72\x65\x73\x73\x5f\x73\x70\x61\x63\x65\x5f\x6f\x70\x65\x72\x61\x74\ +\x69\x6f\x6e\x73\0\x62\x64\x69\x5f\x77\x72\x69\x74\x65\x62\x61\x63\x6b\0\x63\ +\x64\x65\x76\0\x63\x72\x65\x64\0\x66\x69\x6c\x65\x5f\x6c\x6f\x63\x6b\x5f\x63\ +\x6f\x6e\x74\x65\x78\x74\0\x66\x69\x6c\x65\x5f\x6f\x70\x65\x72\x61\x74\x69\x6f\ +\x6e\x73\0\x66\x73\x6e\x6f\x74\x69\x66\x79\x5f\x6d\x61\x72\x6b\x5f\x63\x6f\x6e\ +\x6e\x65\x63\x74\x6f\x72\0\x68\x69\x64\x5f\x64\x65\x76\x69\x63\x65\0\x69\x6e\ +\x6f\x64\x65\x5f\x6f\x70\x65\x72\x61\x74\x69\x6f\x6e\x73\0\x6c\x6f\x63\x6b\x5f\ +\x63\x6c\x61\x73\x73\x5f\x6b\x65\x79\0\x6c\x6f\x63\x6b\x64\x65\x70\x5f\x73\x75\ +\x62\x63\x6c\x61\x73\x73\x5f\x6b\x65\x79\0\x70\x69\x70\x65\x5f\x69\x6e\x6f\x64\ +\x65\x5f\x69\x6e\x66\x6f\0\x70\x6f\x73\x69\x78\x5f\x61\x63\x6c\0\x73\x75\x70\ +\x65\x72\x5f\x62\x6c\x6f\x63\x6b\0\x76\x66\x73\x6d\x6f\x75\x6e\x74\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\x0c\ +\x22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\x04\0\0\0\x04\0\0\0\0\x04\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\x68\x69\x64\x5f\x6a\x6d\x70\x5f\x74\x61\x62\x6c\x65\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x08\0\0\0\x01\ +\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x70\x72\x6f\x67\x73\x5f\x6d\x61\x70\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x47\ +\x50\x4c\0\0\0\0\0\x79\x12\0\0\0\0\0\0\x61\x23\0\0\0\0\0\0\x18\x52\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\x85\0\0\0\x0c\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\0\0\0\ +\0\x19\0\0\0\0\0\0\0\xb5\0\0\0\xfa\0\0\0\x05\x6c\0\0\x01\0\0\0\xb5\0\0\0\xe1\ +\x01\0\0\x02\x74\0\0\x05\0\0\0\xb5\0\0\0\xfa\0\0\0\x05\x6c\0\0\x08\0\0\0\x1a\0\ +\0\0\xdd\x01\0\0\0\0\0\0\x1a\0\0\0\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\x74\x61\x69\x6c\ +\x5f\x63\x61\x6c\x6c\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\x03\0\0\0\x01\0\0\0\0\0\0\0\x01\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\x5f\x5f\x68\x69\x64\x5f\x62\x70\ +\x66\x5f\x74\x61\x69\x6c\x5f\x63\x61\x6c\x6c\0\0\0\0\0\x47\x50\x4c\0\0\0\0\0\ +\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\0\0\0\0\x23\0\0\0\0\0\0\0\xb5\0\0\0\x42\ +\x02\0\0\x05\x8c\0\0\x1a\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\x64\x65\x76\x69\x63\ +\x65\x5f\x65\x76\x65\x6e\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\x01\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\ +\x64\x65\x76\x69\x63\x65\x5f\x65\x76\x65\x6e\x74\0\0\0\0\x47\x50\x4c\0\0\0\0\0\ +\x79\x11\x08\0\0\0\0\0\x79\x11\xa8\x01\0\0\0\0\x7b\x1a\xf8\xff\0\0\0\0\xbf\xa2\ +\0\0\0\0\0\0\x07\x02\0\0\xf8\xff\xff\xff\x18\x51\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\ +\x85\0\0\0\x01\0\0\0\x15\0\x09\0\0\0\0\0\x71\x02\0\0\0\0\0\0\x79\xa1\xf8\xff\0\ +\0\0\0\x85\x20\0\0\0\0\0\0\x15\0\x05\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\ +\xf8\xff\xff\xff\x18\x51\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x03\0\0\0\xb7\ +\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\0\0\0\0\x25\0\0\0\0\0\0\0\xb5\0\0\0\xa3\x02\0\ +\0\x05\xd4\0\0\x01\0\0\0\xb5\0\0\0\x4f\x07\0\0\x18\xdc\0\0\x04\0\0\0\xb5\0\0\0\ +\0\0\0\0\0\0\0\0\x05\0\0\0\xb5\0\0\0\x74\x07\0\0\x0a\xb0\0\0\x08\0\0\0\xb5\0\0\ +\0\xa5\x07\0\0\x06\xb4\0\0\x09\0\0\0\xb5\0\0\0\xb2\x07\0\0\x26\xc0\0\0\x0a\0\0\ +\0\xb5\0\0\0\xb2\x07\0\0\x20\xc0\0\0\x0b\0\0\0\xb5\0\0\0\xb2\x07\0\0\x06\xc0\0\ +\0\x0c\0\0\0\xb5\0\0\0\xb2\x07\0\0\x06\xc0\0\0\x0e\0\0\0\xb5\0\0\0\0\0\0\0\0\0\ +\0\0\x0f\0\0\0\xb5\0\0\0\xe0\x07\0\0\x03\xc4\0\0\x12\0\0\0\xb5\0\0\0\xa3\x02\0\ +\0\x05\xd4\0\0\x08\0\0\0\x26\0\0\0\x4a\x07\0\0\0\0\0\0\x1a\0\0\0\x14\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\x70\x72\x6f\x67\x5f\x72\x65\x6c\x65\x61\x73\0\0\0\0\0\x19\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\x0c\0\ +\0\0\x01\0\0\0\0\0\0\0\x01\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\x62\x70\x66\x5f\x70\x72\x6f\x67\x5f\x72\x65\x6c\x65\x61\x73\x65\0\0\0\0\0\ +\0\0\0\x63\x61\x6c\x6c\x5f\x68\x69\x64\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\x5f\ +\x72\x65\x6c\x65\x61\x73\x65\0\0\0\0\0\0\0\x47\x50\x4c\0\0\0\0\0\x79\x11\0\0\0\ +\0\0\0\x79\x11\x70\x04\0\0\0\0\x7b\x1a\xf8\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\ +\x02\0\0\xf8\xff\xff\xff\x18\x51\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x01\0\ +\0\0\x15\0\x09\0\0\0\0\0\x71\x02\0\0\0\0\0\0\x79\xa1\xf8\xff\0\0\0\0\x85\x20\0\ +\0\0\0\0\0\x15\0\x05\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xf8\xff\xff\xff\ +\x18\x51\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x03\0\0\0\xb7\0\0\0\0\0\0\0\ +\x95\0\0\0\0\0\0\0\0\0\0\0\x82\0\0\0\0\0\0\0\xb5\0\0\0\x53\x08\0\0\x05\xfc\0\0\ +\x01\0\0\0\xb5\0\0\0\x37\x0c\0\0\x19\x04\x01\0\x04\0\0\0\xb5\0\0\0\0\0\0\0\0\0\ +\0\0\x05\0\0\0\xb5\0\0\0\x74\x07\0\0\x0a\xb0\0\0\x08\0\0\0\xb5\0\0\0\xa5\x07\0\ +\0\x06\xb4\0\0\x09\0\0\0\xb5\0\0\0\xb2\x07\0\0\x26\xc0\0\0\x0a\0\0\0\xb5\0\0\0\ +\xb2\x07\0\0\x20\xc0\0\0\x0b\0\0\0\xb5\0\0\0\xb2\x07\0\0\x06\xc0\0\0\x0c\0\0\0\ +\xb5\0\0\0\xb2\x07\0\0\x06\xc0\0\0\x0e\0\0\0\xb5\0\0\0\0\0\0\0\0\0\0\0\x0f\0\0\ +\0\xb5\0\0\0\xe0\x07\0\0\x03\xc4\0\0\x12\0\0\0\xb5\0\0\0\x53\x08\0\0\x05\xfc\0\ +\0\x08\0\0\0\x83\0\0\0\x32\x0c\0\0\0\0\0\0\x1a\0\0\0\x14\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\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\x72\x65\x65\x5f\x69\x6e\x6f\x64\x65\0\0\0\0\0\0\x19\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\x0c\0\0\0\x01\0\0\0\ +\0\0\0\0\x01\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\x62\x70\ +\x66\x5f\x66\x72\x65\x65\x5f\x69\x6e\x6f\x64\x65\0\0\x63\x61\x6c\x6c\x5f\x68\ +\x69\x64\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\x5f\x72\x65\x6c\x65\x61\x73\x65\0\ +\0\0\0\0\0\0"; + opts.insns_sz = 3776; + opts.insns = (void *)"\ +\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\x20\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\x61\xa1\x88\xff\0\0\0\0\xd5\x01\x01\0\0\0\0\0\x85\0\ +\0\0\xa8\0\0\0\x61\xa1\x8c\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\x18\x60\0\0\0\0\0\0\0\0\0\0\x04\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\x28\x27\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\x24\x27\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\x18\x27\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\x10\x27\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\x10\x27\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\xc8\xff\0\0\0\0\x63\x7a\ +\x78\xff\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\x3c\x27\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\x30\x27\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\xbb\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\x61\xa0\x78\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa8\x27\0\0\x63\ +\x01\0\0\0\0\0\0\x61\x60\x2c\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\x84\x27\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\x78\x27\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\xa8\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\x63\x71\0\ +\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xc0\x27\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\ +\x58\x28\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xc8\x27\0\0\x18\ +\x61\0\0\0\0\0\0\0\0\0\0\x50\x28\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\ +\0\0\0\0\x28\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x98\x28\0\0\x7b\x01\0\0\0\0\0\0\ +\x18\x60\0\0\0\0\0\0\0\0\0\0\x08\x28\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa8\x28\0\ +\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x38\x28\0\0\x18\x61\0\0\0\0\ +\0\0\0\0\0\0\xc8\x28\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\xc0\x28\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\x60\x28\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\x64\x28\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\x68\x28\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\x90\x28\0\0\x63\x01\0\0\0\0\0\ +\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xd8\x28\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\ +\x6f\xff\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x48\x28\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\xb7\x01\0\0\x05\0\0\0\x18\x62\0\0\ +\0\0\0\0\0\0\0\0\x48\x28\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\xb8\x28\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\x5d\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\xf0\x28\0\0\x18\x61\0\ +\0\0\0\0\0\0\0\0\0\x30\x29\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\ +\xf8\x28\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x28\x29\0\0\x7b\x01\0\0\0\0\0\0\x18\ +\x60\0\0\0\0\0\0\0\0\0\0\x08\x29\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x70\x29\0\0\ +\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x10\x29\0\0\x18\x61\0\0\0\0\0\ +\0\0\0\0\0\x80\x29\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x20\x29\ +\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa0\x29\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\x98\x29\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\x38\x29\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\x3c\x29\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\x40\x29\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\x68\x29\0\ +\0\x63\x01\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb0\x29\0\0\xb7\x02\0\0\x15\ +\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\x26\xff\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x20\x29\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\xb7\x01\0\0\x05\ +\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x20\x29\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\x90\x29\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\x14\xff\0\0\0\0\x63\x7a\x84\xff\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xc8\ +\x29\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x58\x2b\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\ +\0\0\0\0\0\0\0\0\0\xd0\x29\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x50\x2b\0\0\x7b\x01\ +\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x70\x2a\0\0\x18\x61\0\0\0\0\0\0\0\0\0\ +\0\x98\x2b\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x78\x2a\0\0\x18\ +\x61\0\0\0\0\0\0\0\0\0\0\xa8\x2b\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\ +\0\0\0\x38\x2b\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xc8\x2b\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\xc0\x2b\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\x60\x2b\ +\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\x64\ +\x2b\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\ +\x68\x2b\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\x90\x2b\0\0\x63\x01\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xd8\x2b\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\xdd\xfe\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\ +\x48\x2b\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\x28\x2a\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xf0\x2b\0\ +\0\xb7\x02\0\0\x1a\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\xce\xfe\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\x48\x2b\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\xb8\x2b\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\xad\xfe\0\0\0\0\x63\x7a\x88\xff\0\0\0\0\x18\x60\0\ +\0\0\0\0\0\0\0\0\0\x10\x2c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa0\x2d\0\0\x7b\x01\ +\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x18\x2c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\ +\0\x98\x2d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xb8\x2c\0\0\x18\ +\x61\0\0\0\0\0\0\0\0\0\0\xe0\x2d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\ +\0\0\0\xc0\x2c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xf0\x2d\0\0\x7b\x01\0\0\0\0\0\0\ +\x18\x60\0\0\0\0\0\0\0\0\0\0\x80\x2d\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x10\x2e\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\x08\x2e\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\xa8\x2d\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\xac\x2d\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\xb0\x2d\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\xd8\x2d\0\0\x63\x01\0\0\0\0\0\0\x18\x61\0\0\0\0\0\ +\0\0\0\0\0\x20\x2e\0\0\xb7\x02\0\0\x0f\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\x76\xfe\0\0\0\0\x18\ +\x60\0\0\0\0\0\0\0\0\0\0\x90\x2d\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\x70\x2c\0\0\x18\x61\0\0\0\ +\0\0\0\0\0\0\0\x30\x2e\0\0\xb7\x02\0\0\x1a\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\x67\xfe\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\x90\x2d\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\0\x2e\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\x46\xfe\0\0\0\0\x63\x7a\x8c\ +\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\x38\0\0\0\0\0\x61\xa0\x84\ +\xff\0\0\0\0\x63\x06\x3c\0\0\0\0\0\x61\xa0\x88\xff\0\0\0\0\x63\x06\x40\0\0\0\0\ +\0\x61\xa0\x8c\xff\0\0\0\0\x63\x06\x44\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\x18\x61\0\0\0\0\0\0\0\0\0\0\ +\x04\0\0\0\x61\x10\0\0\0\0\0\0\x63\x06\x28\0\0\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\ +\0\0\0\0\0"; + err = bpf_load_and_run(&opts); + if (err < 0) + return err; + return 0; +} + +static inline struct entrypoints_bpf * +entrypoints_bpf__open_and_load(void) +{ + struct entrypoints_bpf *skel; + + skel = entrypoints_bpf__open(); + if (!skel) + return NULL; + if (entrypoints_bpf__load(skel)) { + entrypoints_bpf__destroy(skel); + return NULL; + } + return skel; +} + +__attribute__((unused)) static void +entrypoints_bpf__assert(struct entrypoints_bpf *s __attribute__((unused))) +{ +#ifdef __cplusplus +#define _Static_assert static_assert +#endif +#ifdef __cplusplus +#undef _Static_assert +#endif +} + +#endif /* __ENTRYPOINTS_BPF_SKEL_H__ */ diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c new file mode 100644 index 000000000000..574e0a627861 --- /dev/null +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * HID-BPF support for Linux + * + * Copyright (c) 2022 Benjamin Tissoires + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hid_bpf_dispatch.h" +#include "entrypoints/entrypoints.lskel.h" + +struct hid_bpf_ops *hid_bpf_ops; +EXPORT_SYMBOL(hid_bpf_ops); + +/** + * hid_bpf_device_event - Called whenever an event is coming in from the device + * + * @ctx: The HID-BPF context + * + * @return %0 on success and keep processing; a negative error code to interrupt + * the processing of this event + * + * Declare an %fmod_ret tracing bpf program to this function and attach this + * program through hid_bpf_attach_prog() to have this helper called for + * any incoming event from the device itself. + * + * The function is called while on IRQ context, so we can not sleep. + */ +/* never used by the kernel but declared so we can load and attach a tracepoint */ +__weak noinline int hid_bpf_device_event(struct hid_bpf_ctx *ctx) +{ + return 0; +} +ALLOW_ERROR_INJECTION(hid_bpf_device_event, ERRNO); + +int +dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type, u8 *data, + u32 size, int interrupt) +{ + struct hid_bpf_ctx_kern ctx_kern = { + .ctx = { + .hid = hdev, + .report_type = type, + .size = size, + }, + .data = data, + }; + + if (type >= HID_REPORT_TYPES) + return -EINVAL; + + return hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_DEVICE_EVENT, &ctx_kern); +} +EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event); + +/** + * hid_bpf_get_data - Get the kernel memory pointer associated with the context @ctx + * + * @ctx: The HID-BPF context + * @offset: The offset within the memory + * @rdwr_buf_size: the const size of the buffer + * + * @returns %NULL on error, an %__u8 memory pointer on success + */ +noinline __u8 * +hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t rdwr_buf_size) +{ + struct hid_bpf_ctx_kern *ctx_kern; + + if (!ctx) + return NULL; + + ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); + + if (rdwr_buf_size + offset > ctx->size) + return NULL; + + return ctx_kern->data + offset; +} + +/* + * The following set contains all functions we agree BPF programs + * can use. + */ +BTF_SET_START(hid_bpf_kfunc_ids) +BTF_ID(func, call_hid_bpf_prog_release) +BTF_ID(func, hid_bpf_get_data) +BTF_SET_END(hid_bpf_kfunc_ids) + +/* + * The following set contains all functions to provide a kernel + * resource to the BPF program. + * We need to add a counterpart release function. + */ +BTF_SET_START(hid_bpf_kfunc_ret_null_ids) +BTF_ID(func, hid_bpf_get_data) +BTF_SET_END(hid_bpf_kfunc_ret_null_ids) + +static const struct btf_kfunc_id_set hid_bpf_kfunc_set = { + .owner = THIS_MODULE, + .check_set = &hid_bpf_kfunc_ids, + .ret_null_set = &hid_bpf_kfunc_ret_null_ids, /* force BPF programs to check the validity + * of the returned pointer. + */ +}; + +static int device_match_id(struct device *dev, const void *id) +{ + struct hid_device *hdev = to_hid_device(dev); + + return hdev->id == *(int *)id; +} + +/** + * hid_bpf_attach_prog - Attach the given @prog_fd to the given HID device + * + * @hid_id: the system unique identifier of the HID device + * @prog_fd: an fd in the user process representing the program to attach + * @flags: any logical OR combination of &enum hid_bpf_attach_flags + * + * @returns %0 on success, an error code otherwise. + */ +/* called from syscall */ +noinline int +hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags) +{ + struct hid_device *hdev; + struct device *dev; + int prog_type = hid_bpf_get_prog_attach_type(prog_fd); + + if (!hid_bpf_ops) + return -EINVAL; + + if (prog_type < 0) + return prog_type; + + if (prog_type >= HID_BPF_PROG_TYPE_MAX) + return -EINVAL; + + if ((flags & ~HID_BPF_FLAG_MASK)) + return -EINVAL; + + dev = bus_find_device(hid_bpf_ops->bus_type, NULL, &hid_id, device_match_id); + if (!dev) + return -EINVAL; + + hdev = to_hid_device(dev); + + return __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags); +} + +/* for syscall HID-BPF */ +BTF_SET_START(hid_bpf_syscall_kfunc_ids) +BTF_ID(func, hid_bpf_attach_prog) +BTF_SET_END(hid_bpf_syscall_kfunc_ids) + +static const struct btf_kfunc_id_set hid_bpf_syscall_kfunc_set = { + .owner = THIS_MODULE, + .check_set = &hid_bpf_syscall_kfunc_ids, +}; + +void hid_bpf_destroy_device(struct hid_device *hdev) +{ + if (!hdev) + return; + + /* mark the device as destroyed in bpf so we don't reattach it */ + hdev->bpf.destroyed = true; + + __hid_bpf_destroy_device(hdev); +} +EXPORT_SYMBOL_GPL(hid_bpf_destroy_device); + +void hid_bpf_device_init(struct hid_device *hdev) +{ + spin_lock_init(&hdev->bpf.progs_lock); +} +EXPORT_SYMBOL_GPL(hid_bpf_device_init); + +int __init hid_bpf_init(void) +{ + int err; + + /* Note: if we exit with an error any time here, we would entirely break HID, which + * is probably not something we want. So we log an error and return success. + * + * This is not a big deal: the syscall allowing to attach a BPF program to a HID device + * will not be available, so nobody will be able to use the functionality. + */ + + err = register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &hid_bpf_kfunc_set); + if (err) { + pr_warn("error while setting HID BPF tracing kfuncs: %d", err); + return 0; + } + + err = hid_bpf_preload_skel(); + if (err) { + pr_warn("error while preloading HID BPF dispatcher: %d", err); + return 0; + } + + /* register syscalls after we are sure we can load our preloaded bpf program */ + err = register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &hid_bpf_syscall_kfunc_set); + if (err) { + pr_warn("error while setting HID BPF syscall kfuncs: %d", err); + return 0; + } + + return 0; +} + +void __exit hid_bpf_exit(void) +{ + /* HID depends on us, so if we hit that code, we are guaranteed that hid + * has been removed and thus we do not need to clear the HID devices + */ + hid_bpf_free_links_and_skel(); +} + +late_initcall(hid_bpf_init); +module_exit(hid_bpf_exit); +MODULE_AUTHOR("Benjamin Tissoires"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/bpf/hid_bpf_dispatch.h b/drivers/hid/bpf/hid_bpf_dispatch.h new file mode 100644 index 000000000000..98c378e18b2b --- /dev/null +++ b/drivers/hid/bpf/hid_bpf_dispatch.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _BPF_HID_BPF_DISPATCH_H +#define _BPF_HID_BPF_DISPATCH_H + +#include + +struct hid_bpf_ctx_kern { + struct hid_bpf_ctx ctx; + u8 *data; +}; + +int hid_bpf_preload_skel(void); +void hid_bpf_free_links_and_skel(void); +int hid_bpf_get_prog_attach_type(int prog_fd); +int __hid_bpf_attach_prog(struct hid_device *hdev, enum hid_bpf_prog_type prog_type, int prog_fd, + __u32 flags); +void __hid_bpf_destroy_device(struct hid_device *hdev); +int hid_bpf_prog_run(struct hid_device *hdev, enum hid_bpf_prog_type type, + struct hid_bpf_ctx_kern *ctx_kern); + +struct bpf_prog; + +/* HID-BPF internal kfuncs API */ +bool call_hid_bpf_prog_release(u64 prog, int table_cnt); + +#endif diff --git a/drivers/hid/bpf/hid_bpf_jmp_table.c b/drivers/hid/bpf/hid_bpf_jmp_table.c new file mode 100644 index 000000000000..27c78753effd --- /dev/null +++ b/drivers/hid/bpf/hid_bpf_jmp_table.c @@ -0,0 +1,577 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * HID-BPF support for Linux + * + * Copyright (c) 2022 Benjamin Tissoires + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hid_bpf_dispatch.h" +#include "entrypoints/entrypoints.lskel.h" + +#define HID_BPF_MAX_PROGS 1024 /* keep this in sync with preloaded bpf, + * needs to be a power of 2 as we use it as + * a circular buffer + */ + +#define NEXT(idx) (((idx) + 1) & (HID_BPF_MAX_PROGS - 1)) +#define PREV(idx) (((idx) - 1) & (HID_BPF_MAX_PROGS - 1)) + +/* + * represents one attached program stored in the hid jump table + */ +struct hid_bpf_prog_entry { + struct bpf_prog *prog; + struct hid_device *hdev; + enum hid_bpf_prog_type type; + u16 idx; +}; + +struct hid_bpf_jmp_table { + struct bpf_map *map; + struct bpf_map *prog_keys; + struct hid_bpf_prog_entry entries[HID_BPF_MAX_PROGS]; /* compacted list, circular buffer */ + int tail, head; + struct bpf_prog *progs[HID_BPF_MAX_PROGS]; /* idx -> progs mapping */ + unsigned long enabled[BITS_TO_LONGS(HID_BPF_MAX_PROGS)]; +}; + +#define FOR_ENTRIES(__i, __start, __end) \ + for (__i = __start; CIRC_CNT(__end, __i, HID_BPF_MAX_PROGS); __i = NEXT(__i)) + +static struct hid_bpf_jmp_table jmp_table; + +static DEFINE_MUTEX(hid_bpf_attach_lock); /* held when attaching/detaching programs */ + +static void hid_bpf_release_progs(struct work_struct *work); + +static DECLARE_WORK(release_work, hid_bpf_release_progs); + +static u32 hid_bpf_btf_ids[HID_BPF_PROG_TYPE_MAX]; + +static int hid_bpf_max_programs(enum hid_bpf_prog_type type) +{ + switch (type) { + case HID_BPF_PROG_TYPE_DEVICE_EVENT: + return HID_BPF_MAX_PROGS_PER_DEV; + default: + return -EINVAL; + } +} + +static int hid_bpf_program_count(struct hid_device *hdev, + struct bpf_prog *prog, + enum hid_bpf_prog_type type) +{ + int i, n = 0; + + if (type >= HID_BPF_PROG_TYPE_MAX) + return -EINVAL; + + FOR_ENTRIES(i, jmp_table.tail, jmp_table.head) { + struct hid_bpf_prog_entry *entry = &jmp_table.entries[i]; + + if (type != HID_BPF_PROG_TYPE_UNDEF && entry->type != type) + continue; + + if (hdev && entry->hdev != hdev) + continue; + + if (prog && entry->prog != prog) + continue; + + n++; + } + + return n; +} + +__weak noinline int __hid_bpf_tail_call(struct hid_bpf_ctx *ctx) +{ + return 0; +} +ALLOW_ERROR_INJECTION(__hid_bpf_tail_call, ERRNO); + +int hid_bpf_prog_run(struct hid_device *hdev, enum hid_bpf_prog_type type, + struct hid_bpf_ctx_kern *ctx_kern) +{ + struct hid_bpf_prog_list *prog_list; + int i, idx, err = 0; + + rcu_read_lock(); + prog_list = rcu_dereference(hdev->bpf.progs[type]); + + if (!prog_list) + goto out_unlock; + + for (i = 0; i < prog_list->prog_cnt; i++) { + idx = prog_list->prog_idx[i]; + + if (!test_bit(idx, jmp_table.enabled)) + continue; + + ctx_kern->ctx.index = idx; + err = __hid_bpf_tail_call(&ctx_kern->ctx); + if (err) + break; + } + + out_unlock: + rcu_read_unlock(); + + return err; +} + +/* + * assign the list of programs attached to a given hid device. + */ +static void __hid_bpf_set_hdev_progs(struct hid_device *hdev, struct hid_bpf_prog_list *new_list, + enum hid_bpf_prog_type type) +{ + struct hid_bpf_prog_list *old_list; + + spin_lock(&hdev->bpf.progs_lock); + old_list = rcu_dereference_protected(hdev->bpf.progs[type], + lockdep_is_held(&hdev->bpf.progs_lock)); + rcu_assign_pointer(hdev->bpf.progs[type], new_list); + spin_unlock(&hdev->bpf.progs_lock); + synchronize_rcu(); + + kfree(old_list); +} + +/* + * allocate and populate the list of programs attached to a given hid device. + * + * Must be called under lock. + */ +static int hid_bpf_populate_hdev(struct hid_device *hdev, enum hid_bpf_prog_type type) +{ + struct hid_bpf_prog_list *new_list; + int i; + + if (type >= HID_BPF_PROG_TYPE_MAX || !hdev) + return -EINVAL; + + if (hdev->bpf.destroyed) + return 0; + + new_list = kzalloc(sizeof(*new_list), GFP_KERNEL); + if (!new_list) + return -ENOMEM; + + FOR_ENTRIES(i, jmp_table.tail, jmp_table.head) { + struct hid_bpf_prog_entry *entry = &jmp_table.entries[i]; + + if (entry->type == type && entry->hdev == hdev && + test_bit(entry->idx, jmp_table.enabled)) + new_list->prog_idx[new_list->prog_cnt++] = entry->idx; + } + + __hid_bpf_set_hdev_progs(hdev, new_list, type); + + return 0; +} + +static void __hid_bpf_do_release_prog(int map_fd, unsigned int idx) +{ + skel_map_delete_elem(map_fd, &idx); + jmp_table.progs[idx] = NULL; +} + +static void hid_bpf_release_progs(struct work_struct *work) +{ + int i, j, n, map_fd = -1; + + if (!jmp_table.map) + return; + + /* retrieve a fd of our prog_array map in BPF */ + map_fd = skel_map_get_fd_by_id(jmp_table.map->id); + if (map_fd < 0) + return; + + mutex_lock(&hid_bpf_attach_lock); /* protects against attaching new programs */ + + /* detach unused progs from HID devices */ + FOR_ENTRIES(i, jmp_table.tail, jmp_table.head) { + struct hid_bpf_prog_entry *entry = &jmp_table.entries[i]; + enum hid_bpf_prog_type type; + struct hid_device *hdev; + + if (test_bit(entry->idx, jmp_table.enabled)) + continue; + + /* we have an attached prog */ + if (entry->hdev) { + hdev = entry->hdev; + type = entry->type; + + hid_bpf_populate_hdev(hdev, type); + + /* mark all other disabled progs from hdev of the given type as detached */ + FOR_ENTRIES(j, i, jmp_table.head) { + struct hid_bpf_prog_entry *next; + + next = &jmp_table.entries[j]; + + if (test_bit(next->idx, jmp_table.enabled)) + continue; + + if (next->hdev == hdev && next->type == type) + next->hdev = NULL; + } + } + } + + /* remove all unused progs from the jump table */ + FOR_ENTRIES(i, jmp_table.tail, jmp_table.head) { + struct hid_bpf_prog_entry *entry = &jmp_table.entries[i]; + + if (test_bit(entry->idx, jmp_table.enabled)) + continue; + + if (entry->prog) + __hid_bpf_do_release_prog(map_fd, entry->idx); + } + + /* compact the entry list */ + n = jmp_table.tail; + FOR_ENTRIES(i, jmp_table.tail, jmp_table.head) { + struct hid_bpf_prog_entry *entry = &jmp_table.entries[i]; + + if (!test_bit(entry->idx, jmp_table.enabled)) + continue; + + jmp_table.entries[n] = jmp_table.entries[i]; + n = NEXT(n); + } + + jmp_table.head = n; + + mutex_unlock(&hid_bpf_attach_lock); + + if (map_fd >= 0) + close_fd(map_fd); +} + +static void hid_bpf_release_prog_at(int idx) +{ + int map_fd = -1; + + /* retrieve a fd of our prog_array map in BPF */ + map_fd = skel_map_get_fd_by_id(jmp_table.map->id); + if (map_fd < 0) + return; + + __hid_bpf_do_release_prog(map_fd, idx); + + close(map_fd); +} + +/* + * Insert the given BPF program represented by its fd in the jmp table. + * Returns the index in the jump table or a negative error. + */ +static int hid_bpf_insert_prog(int prog_fd, struct bpf_prog *prog) +{ + int i, cnt, index = -1, map_fd = -1, progs_map_fd = -1, err = -EINVAL; + + /* retrieve a fd of our prog_array map in BPF */ + map_fd = skel_map_get_fd_by_id(jmp_table.map->id); + /* take an fd for the table of progs we monitor with SEC("fexit/bpf_prog_release") */ + progs_map_fd = skel_map_get_fd_by_id(jmp_table.prog_keys->id); + + if (map_fd < 0 || progs_map_fd < 0) { + err = -EINVAL; + goto out; + } + + cnt = 0; + /* find the first available index in the jmp_table + * and count how many time this program has been inserted + */ + for (i = 0; i < HID_BPF_MAX_PROGS; i++) { + if (!jmp_table.progs[i] && index < 0) { + /* mark the index as used */ + jmp_table.progs[i] = prog; + index = i; + __set_bit(i, jmp_table.enabled); + cnt++; + } else { + if (jmp_table.progs[i] == prog) + cnt++; + } + } + if (index < 0) { + err = -ENOMEM; + goto out; + } + + /* insert the program in the jump table */ + err = skel_map_update_elem(map_fd, &index, &prog_fd, 0); + if (err) + goto out; + + /* insert the program in the prog list table */ + err = skel_map_update_elem(progs_map_fd, &prog, &cnt, 0); + if (err) + goto out; + + /* return the index */ + err = index; + + out: + if (err < 0) + __hid_bpf_do_release_prog(map_fd, index); + if (map_fd >= 0) + close_fd(map_fd); + if (progs_map_fd >= 0) + close_fd(progs_map_fd); + return err; +} + +int hid_bpf_get_prog_attach_type(int prog_fd) +{ + struct bpf_prog *prog = NULL; + int i; + int prog_type = HID_BPF_PROG_TYPE_UNDEF; + + prog = bpf_prog_get(prog_fd); + if (IS_ERR(prog)) + return PTR_ERR(prog); + + for (i = 0; i < HID_BPF_PROG_TYPE_MAX; i++) { + if (hid_bpf_btf_ids[i] == prog->aux->attach_btf_id) { + prog_type = i; + break; + } + } + + bpf_prog_put(prog); + + return prog_type; +} + +/* called from syscall */ +noinline int +__hid_bpf_attach_prog(struct hid_device *hdev, enum hid_bpf_prog_type prog_type, + int prog_fd, __u32 flags) +{ + struct bpf_prog *prog = NULL; + struct hid_bpf_prog_entry *prog_entry; + int cnt, err = -EINVAL, prog_idx = -1; + + /* take a ref on the prog itself */ + prog = bpf_prog_get(prog_fd); + if (IS_ERR(prog)) + return PTR_ERR(prog); + + mutex_lock(&hid_bpf_attach_lock); + + /* do not attach too many programs to a given HID device */ + cnt = hid_bpf_program_count(hdev, NULL, prog_type); + if (cnt < 0) { + err = cnt; + goto out_unlock; + } + + if (cnt >= hid_bpf_max_programs(prog_type)) { + err = -E2BIG; + goto out_unlock; + } + + prog_idx = hid_bpf_insert_prog(prog_fd, prog); + /* if the jmp table is full, abort */ + if (prog_idx < 0) { + err = prog_idx; + goto out_unlock; + } + + if (flags & HID_BPF_FLAG_INSERT_HEAD) { + /* take the previous prog_entry slot */ + jmp_table.tail = PREV(jmp_table.tail); + prog_entry = &jmp_table.entries[jmp_table.tail]; + } else { + /* take the next prog_entry slot */ + prog_entry = &jmp_table.entries[jmp_table.head]; + jmp_table.head = NEXT(jmp_table.head); + } + + /* we steal the ref here */ + prog_entry->prog = prog; + prog_entry->idx = prog_idx; + prog_entry->hdev = hdev; + prog_entry->type = prog_type; + + /* finally store the index in the device list */ + err = hid_bpf_populate_hdev(hdev, prog_type); + if (err) + hid_bpf_release_prog_at(prog_idx); + + out_unlock: + mutex_unlock(&hid_bpf_attach_lock); + + /* we only use prog as a key in the various tables, so we don't need to actually + * increment the ref count. + */ + bpf_prog_put(prog); + + return err; +} + +void __hid_bpf_destroy_device(struct hid_device *hdev) +{ + int type, i; + struct hid_bpf_prog_list *prog_list; + + rcu_read_lock(); + + for (type = 0; type < HID_BPF_PROG_TYPE_MAX; type++) { + prog_list = rcu_dereference(hdev->bpf.progs[type]); + + if (!prog_list) + continue; + + for (i = 0; i < prog_list->prog_cnt; i++) + __clear_bit(prog_list->prog_idx[i], jmp_table.enabled); + } + + rcu_read_unlock(); + + for (type = 0; type < HID_BPF_PROG_TYPE_MAX; type++) + __hid_bpf_set_hdev_progs(hdev, NULL, type); + + /* schedule release of all detached progs */ + schedule_work(&release_work); +} + +noinline bool +call_hid_bpf_prog_release(u64 prog_key, int table_cnt) +{ + /* compare with how many refs are left in the bpf program */ + struct bpf_prog *prog = (struct bpf_prog *)prog_key; + int idx; + + if (!prog) + return false; + + if (atomic64_read(&prog->aux->refcnt) != table_cnt) + return false; + + /* we don't need locking here because the entries in the progs table + * are stable: + * if there are other users (and the progs entries might change), we + * would return in the statement above. + */ + for (idx = 0; idx < HID_BPF_MAX_PROGS; idx++) { + if (jmp_table.progs[idx] == prog) { + __clear_bit(idx, jmp_table.enabled); + break; + } + } + if (idx >= HID_BPF_MAX_PROGS) { + /* should never happen if we get our refcount right */ + idx = -1; + } + + /* schedule release of all detached progs */ + schedule_work(&release_work); + return idx >= 0; +} + +#define HID_BPF_PROGS_COUNT 3 + +static struct bpf_link *links[HID_BPF_PROGS_COUNT]; +static struct entrypoints_bpf *skel; + +void hid_bpf_free_links_and_skel(void) +{ + int i; + + /* the following is enough to release all programs attached to hid */ + if (jmp_table.prog_keys) + bpf_map_put_with_uref(jmp_table.prog_keys); + + if (jmp_table.map) + bpf_map_put_with_uref(jmp_table.map); + + for (i = 0; i < ARRAY_SIZE(links); i++) { + if (!IS_ERR_OR_NULL(links[i])) + bpf_link_put(links[i]); + } + entrypoints_bpf__destroy(skel); +} + +#define ATTACH_AND_STORE_LINK(__name) do { \ + err = entrypoints_bpf__##__name##__attach(skel); \ + if (err) \ + goto out; \ + \ + links[idx] = bpf_link_get_from_fd(skel->links.__name##_fd); \ + if (IS_ERR(links[idx])) { \ + err = PTR_ERR(links[idx]); \ + goto out; \ + } \ + \ + /* Avoid taking over stdin/stdout/stderr of init process. Zeroing out \ + * makes skel_closenz() a no-op later in iterators_bpf__destroy(). \ + */ \ + close_fd(skel->links.__name##_fd); \ + skel->links.__name##_fd = 0; \ + idx++; \ +} while (0) + +#define FIND_AND_STORE_BTF_ID(__prog_name__, prog_type) do { \ + prog = bpf_prog_get(skel->progs.__prog_name__.prog_fd); \ + hid_bpf_btf_ids[prog_type] = prog->aux->attach_btf_id; \ + bpf_prog_put(prog); \ + close_fd(skel->progs.__prog_name__.prog_fd); \ + skel->progs.__prog_name__.prog_fd = 0; \ +} while (0) + +int hid_bpf_preload_skel(void) +{ + struct bpf_prog *prog; + int err, idx = 0; + + skel = entrypoints_bpf__open(); + if (!skel) + return -ENOMEM; + + err = entrypoints_bpf__load(skel); + if (err) + goto out; + + jmp_table.map = bpf_map_get_with_uref(skel->maps.hid_jmp_table.map_fd); + if (IS_ERR(jmp_table.map)) { + err = PTR_ERR(jmp_table.map); + goto out; + } + + jmp_table.prog_keys = bpf_map_get_with_uref(skel->maps.progs_map.map_fd); + if (IS_ERR(jmp_table.prog_keys)) { + err = PTR_ERR(jmp_table.prog_keys); + goto out; + } + + FIND_AND_STORE_BTF_ID(hid_device_event, HID_BPF_PROG_TYPE_DEVICE_EVENT); + ATTACH_AND_STORE_LINK(hid_tail_call); + ATTACH_AND_STORE_LINK(hid_prog_release); + ATTACH_AND_STORE_LINK(hid_free_inode); + + return 0; +out: + hid_bpf_free_links_and_skel(); + return err; +} diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 36f4aa749cea..bdda415e051b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2040,6 +2040,10 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data report_enum = hid->report_enum + type; hdrv = hid->driver; + ret = dispatch_hid_bpf_device_event(hid, type, data, size, interrupt); + if (ret) + goto unlock; + if (!size) { dbg_hid("empty report\n"); ret = -1; @@ -2785,6 +2789,8 @@ struct hid_device *hid_allocate_device(void) sema_init(&hdev->driver_input_lock, 1); mutex_init(&hdev->ll_open_lock); + hid_bpf_device_init(hdev); + return hdev; } EXPORT_SYMBOL_GPL(hid_allocate_device); @@ -2811,6 +2817,7 @@ static void hid_remove_device(struct hid_device *hdev) */ void hid_destroy_device(struct hid_device *hdev) { + hid_bpf_destroy_device(hdev); hid_remove_device(hdev); put_device(&hdev->dev); } @@ -2897,6 +2904,11 @@ int hid_check_keys_pressed(struct hid_device *hid) } EXPORT_SYMBOL_GPL(hid_check_keys_pressed); +static struct hid_bpf_ops hid_ops = { + .owner = THIS_MODULE, + .bus_type = &hid_bus_type, +}; + static int __init hid_init(void) { int ret; @@ -2911,6 +2923,8 @@ static int __init hid_init(void) goto err; } + hid_bpf_ops = &hid_ops; + ret = hidraw_init(); if (ret) goto err_bus; @@ -2926,6 +2940,7 @@ static int __init hid_init(void) static void __exit hid_exit(void) { + hid_bpf_ops = NULL; hid_debug_exit(); hidraw_exit(); bus_unregister(&hid_bus_type); diff --git a/include/linux/hid.h b/include/linux/hid.h index 6a2a6f166bd3..25bdc7ad3b86 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -26,6 +26,7 @@ #include #include #include +#include /* * We parse each description item into this structure. Short items data @@ -651,6 +652,10 @@ struct hid_device { /* device report descriptor */ wait_queue_head_t debug_wait; unsigned int id; /* system unique id */ + +#ifdef CONFIG_BPF + struct hid_bpf bpf; /* hid-bpf data */ +#endif /* CONFIG_BPF */ }; #define to_hid_device(pdev) \ diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h new file mode 100644 index 000000000000..9d893c14a0f2 --- /dev/null +++ b/include/linux/hid_bpf.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ + +#ifndef __HID_BPF_H +#define __HID_BPF_H + +#include +#include +#include + +struct hid_device; + +/* + * The following is the HID BPF API. + * + * It should be treated as UAPI, so extra care is required + * when making change to this file. + */ + +/** + * struct hid_bpf_ctx - User accessible data for all HID programs + * + * ``data`` is not directly accessible from the context. We need to issue + * a call to ``hid_bpf_get_data()`` in order to get a pointer to that field. + * + * All of these fields are currently read-only. + * + * @index: program index in the jump table. No special meaning (a smaller index + * doesn't mean the program will be executed before another program with + * a bigger index). + * @hid: the ``struct hid_device`` representing the device itself + * @report_type: used for ``hid_bpf_device_event()`` + * @size: Valid data in the data field. + * + * Programs can get the available valid size in data by fetching this field. + */ +struct hid_bpf_ctx { + __u32 index; + const struct hid_device *hid; + enum hid_report_type report_type; + __s32 size; +}; + +/* Following functions are tracepoints that BPF programs can attach to */ +int hid_bpf_device_event(struct hid_bpf_ctx *ctx); + +/* Following functions are kfunc that we export to BPF programs */ +/* only available in tracing */ +__u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t __sz); + +/* only available in syscall */ +int hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags); + +/* + * Below is HID internal + */ + +#define HID_BPF_MAX_PROGS_PER_DEV 64 +#define HID_BPF_FLAG_MASK (((HID_BPF_FLAG_MAX - 1) << 1) - 1) + +/* types of HID programs to attach to */ +enum hid_bpf_prog_type { + HID_BPF_PROG_TYPE_UNDEF = -1, + HID_BPF_PROG_TYPE_DEVICE_EVENT, /* an event is emitted from the device */ + HID_BPF_PROG_TYPE_MAX, +}; + +struct hid_bpf_ops { + struct module *owner; + struct bus_type *bus_type; +}; + +extern struct hid_bpf_ops *hid_bpf_ops; + +struct hid_bpf_prog_list { + u16 prog_idx[HID_BPF_MAX_PROGS_PER_DEV]; + u8 prog_cnt; +}; + +/* stored in each device */ +struct hid_bpf { + struct hid_bpf_prog_list __rcu *progs[HID_BPF_PROG_TYPE_MAX]; /* attached BPF progs */ + bool destroyed; /* prevents the assignment of any progs */ + + spinlock_t progs_lock; /* protects RCU update of progs */ +}; + +#ifdef CONFIG_BPF +int dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data, + u32 size, int interrupt); +void hid_bpf_destroy_device(struct hid_device *hid); +void hid_bpf_device_init(struct hid_device *hid); +#else /* CONFIG_BPF */ +static inline int dispatch_hid_bpf_device_event(struct hid_device *hid, int type, u8 *data, + u32 size, int interrupt) { return 0; } +static inline void hid_bpf_destroy_device(struct hid_device *hid) {} +static inline void hid_bpf_device_init(struct hid_device *hid) {} +#endif /* CONFIG_BPF */ + +#endif /* __HID_BPF_H */ diff --git a/include/uapi/linux/hid_bpf.h b/include/uapi/linux/hid_bpf.h new file mode 100644 index 000000000000..ba8caf9b60ee --- /dev/null +++ b/include/uapi/linux/hid_bpf.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ + +#ifndef _UAPI_HID_BPF_H +#define _UAPI_HID_BPF_H + +#include +#include + +/** + * enum hid_bpf_attach_flags - flags used when attaching a HIF-BPF program + * + * @HID_BPF_FLAG_NONE: no specific flag is used, the kernel choses where to + * insert the program + * @HID_BPF_FLAG_INSERT_HEAD: insert the given program before any other program + * currently attached to the device. This doesn't + * guarantee that this program will always be first + * @HID_BPF_FLAG_MAX: sentinel value, not to be used by the callers + */ +enum hid_bpf_attach_flags { + HID_BPF_FLAG_NONE = 0, + HID_BPF_FLAG_INSERT_HEAD = _BITUL(0), + HID_BPF_FLAG_MAX, +}; + +#endif /* _UAPI_HID_BPF_H */ From patchwork Wed May 18 20:59:15 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12854104 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B379AC433FE for ; Wed, 18 May 2022 21:00:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S242939AbiERVAT (ORCPT ); Wed, 18 May 2022 17:00:19 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58962 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S242942AbiERVAR (ORCPT ); Wed, 18 May 2022 17:00:17 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 68B092555AE for ; Wed, 18 May 2022 14:00:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1652907610; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=utPrP6JnzauY93f/JVepwrHkDuqpCCHQhqdMoXeaDbw=; b=YIuENrUWljhW4NSEEVdvfmaOcGztfYxgD5E3U6PQRfC4OOWV5beDoMgb9L3boWTsd6DiRl A5wB8icLB6g6vj0cFLRVPomSHZZf802uKk3sytbVd3f98hDHXEyzSiYKnCG06/P2IJMYEu S6Mbb+ff7ZSLf3oRYUHU5mV2uQ/2pjU= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-148-J_zL0byLNGCbJWTEnDPAYw-1; Wed, 18 May 2022 17:00:06 -0400 X-MC-Unique: J_zL0byLNGCbJWTEnDPAYw-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 6B57080419C; Wed, 18 May 2022 21:00:05 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.58]) by smtp.corp.redhat.com (Postfix) with ESMTP id 9DF4B2166B26; Wed, 18 May 2022 21:00:00 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v5 08/17] selftests/bpf: add tests for the HID-bpf initial implementation Date: Wed, 18 May 2022 22:59:15 +0200 Message-Id: <20220518205924.399291-9-benjamin.tissoires@redhat.com> In-Reply-To: <20220518205924.399291-1-benjamin.tissoires@redhat.com> References: <20220518205924.399291-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net The test is pretty basic: - create a virtual uhid device that no userspace will like (to not mess up the running system) - attach a BPF prog to it - open the matching hidraw node - inject one event and check: * that the BPF program can do something on the event stream * can modify the event stream - add another test where we attach/detach BPF programs to see if we get errors Signed-off-by: Benjamin Tissoires --- changes in v5: - use of the HID device system id instead of fd - attach to HID device with the new API - add attach/detach test changes in v4: - manually retrieve the hidraw node from the sysfs (we can't get it for free from BPF) - use the new API changes in v3: - squashed "hid: rely on uhid event to know if a test device is ready" into this one - add selftests bpf VM config changes - s/hidraw_ino/hidraw_number/ changes in v2: - split the series by bpf/libbpf/hid/selftests and samples --- tools/testing/selftests/bpf/config | 3 + tools/testing/selftests/bpf/prog_tests/hid.c | 669 +++++++++++++++++++ tools/testing/selftests/bpf/progs/hid.c | 45 ++ 3 files changed, 717 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/hid.c create mode 100644 tools/testing/selftests/bpf/progs/hid.c diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config index 08c6e5a66d87..64205150627d 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -54,3 +54,6 @@ CONFIG_NF_DEFRAG_IPV6=y CONFIG_NF_CONNTRACK=y CONFIG_USERFAULTFD=y CONFIG_FPROBE=y +CONFIG_HID=y +CONFIG_HIDRAW=y +CONFIG_UHID=y diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c new file mode 100644 index 000000000000..719d220c8d86 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/hid.c @@ -0,0 +1,669 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Red Hat */ +#include +#include +#include "hid.skel.h" + +#include +#include +#include +#include +#include +#include + +static unsigned char rdesc[] = { + 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ + 0x09, 0x21, /* Usage (Vendor Usage 0x21) */ + 0xa1, 0x01, /* COLLECTION (Application) */ + 0x09, 0x01, /* Usage (Vendor Usage 0x01) */ + 0xa1, 0x00, /* COLLECTION (Physical) */ + 0x85, 0x01, /* REPORT_ID (1) */ + 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ + 0x19, 0x01, /* USAGE_MINIMUM (1) */ + 0x29, 0x03, /* USAGE_MAXIMUM (3) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x81, 0x02, /* INPUT (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x05, /* REPORT_SIZE (5) */ + 0x81, 0x01, /* INPUT (Cnst,Var,Abs) */ + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x30, /* USAGE (X) */ + 0x09, 0x31, /* USAGE (Y) */ + 0x15, 0x81, /* LOGICAL_MINIMUM (-127) */ + 0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */ + 0x75, 0x10, /* REPORT_SIZE (16) */ + 0x95, 0x02, /* REPORT_COUNT (2) */ + 0x81, 0x06, /* INPUT (Data,Var,Rel) */ + + 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ + 0x19, 0x01, /* USAGE_MINIMUM (1) */ + 0x29, 0x03, /* USAGE_MAXIMUM (3) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x91, 0x02, /* Output (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x05, /* REPORT_SIZE (5) */ + 0x91, 0x01, /* Output (Cnst,Var,Abs) */ + + 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ + 0x19, 0x06, /* USAGE_MINIMUM (6) */ + 0x29, 0x08, /* USAGE_MAXIMUM (8) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0xb1, 0x02, /* Feature (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x05, /* REPORT_SIZE (5) */ + 0x91, 0x01, /* Output (Cnst,Var,Abs) */ + + 0xc0, /* END_COLLECTION */ + 0xc0, /* END_COLLECTION */ +}; + +struct attach_prog_args { + int prog_fd; + unsigned int hid; + int retval; +}; + +static pthread_mutex_t uhid_started_mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t uhid_started = PTHREAD_COND_INITIALIZER; + +/* no need to protect uhid_stopped, only one thread accesses it */ +static bool uhid_stopped; + +static int uhid_write(int fd, const struct uhid_event *ev) +{ + ssize_t ret; + + ret = write(fd, ev, sizeof(*ev)); + if (ret < 0) { + fprintf(stderr, "Cannot write to uhid: %m\n"); + return -errno; + } else if (ret != sizeof(*ev)) { + fprintf(stderr, "Wrong size written to uhid: %zd != %zu\n", + ret, sizeof(ev)); + return -EFAULT; + } else { + return 0; + } +} + +static int create(int fd, int rand_nb) +{ + struct uhid_event ev; + char buf[25]; + + sprintf(buf, "test-uhid-device-%d", rand_nb); + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_CREATE; + strcpy((char *)ev.u.create.name, buf); + ev.u.create.rd_data = rdesc; + ev.u.create.rd_size = sizeof(rdesc); + ev.u.create.bus = BUS_USB; + ev.u.create.vendor = 0x0001; + ev.u.create.product = 0x0a37; + ev.u.create.version = 0; + ev.u.create.country = 0; + + sprintf(buf, "%d", rand_nb); + strcpy((char *)ev.u.create.phys, buf); + + return uhid_write(fd, &ev); +} + +static void destroy(int fd) +{ + struct uhid_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_DESTROY; + + uhid_write(fd, &ev); +} + +static int uhid_event(int fd) +{ + struct uhid_event ev; + ssize_t ret; + + memset(&ev, 0, sizeof(ev)); + ret = read(fd, &ev, sizeof(ev)); + if (ret == 0) { + fprintf(stderr, "Read HUP on uhid-cdev\n"); + return -EFAULT; + } else if (ret < 0) { + fprintf(stderr, "Cannot read uhid-cdev: %m\n"); + return -errno; + } else if (ret != sizeof(ev)) { + fprintf(stderr, "Invalid size read from uhid-dev: %zd != %zu\n", + ret, sizeof(ev)); + return -EFAULT; + } + + switch (ev.type) { + case UHID_START: + pthread_mutex_lock(&uhid_started_mtx); + pthread_cond_signal(&uhid_started); + pthread_mutex_unlock(&uhid_started_mtx); + + fprintf(stderr, "UHID_START from uhid-dev\n"); + break; + case UHID_STOP: + uhid_stopped = true; + + fprintf(stderr, "UHID_STOP from uhid-dev\n"); + break; + case UHID_OPEN: + fprintf(stderr, "UHID_OPEN from uhid-dev\n"); + break; + case UHID_CLOSE: + fprintf(stderr, "UHID_CLOSE from uhid-dev\n"); + break; + case UHID_OUTPUT: + fprintf(stderr, "UHID_OUTPUT from uhid-dev\n"); + break; + case UHID_GET_REPORT: + fprintf(stderr, "UHID_GET_REPORT from uhid-dev\n"); + break; + case UHID_SET_REPORT: + fprintf(stderr, "UHID_SET_REPORT from uhid-dev\n"); + break; + default: + fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type); + } + + return 0; +} + +static void *read_uhid_events_thread(void *arg) +{ + int fd = *(int *)arg; + struct pollfd pfds[1]; + int ret = 0; + + pfds[0].fd = fd; + pfds[0].events = POLLIN; + + uhid_stopped = false; + + while (!uhid_stopped) { + ret = poll(pfds, 1, 100); + if (ret < 0) { + fprintf(stderr, "Cannot poll for fds: %m\n"); + break; + } + if (pfds[0].revents & POLLIN) { + ret = uhid_event(fd); + if (ret) + break; + } + } + + return (void *)(long)ret; +} + +static int uhid_start_listener(pthread_t *tid, int uhid_fd) +{ + int fd = uhid_fd; + + pthread_mutex_lock(&uhid_started_mtx); + if (CHECK_FAIL(pthread_create(tid, NULL, read_uhid_events_thread, + (void *)&fd))) { + pthread_mutex_unlock(&uhid_started_mtx); + close(fd); + return -EIO; + } + pthread_cond_wait(&uhid_started, &uhid_started_mtx); + pthread_mutex_unlock(&uhid_started_mtx); + + return 0; +} + +static int send_event(int fd, u8 *buf, size_t size) +{ + struct uhid_event ev; + + if (size > sizeof(ev.u.input.data)) + return -E2BIG; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_INPUT2; + ev.u.input2.size = size; + + memcpy(ev.u.input2.data, buf, size); + + return uhid_write(fd, &ev); +} + +static int setup_uhid(int rand_nb) +{ + int fd; + const char *path = "/dev/uhid"; + int ret; + + fd = open(path, O_RDWR | O_CLOEXEC); + if (!ASSERT_GE(fd, 0, "open uhid-cdev")) + return -EPERM; + + ret = create(fd, rand_nb); + if (!ASSERT_OK(ret, "create uhid device")) { + close(fd); + return -EPERM; + } + + return fd; +} + +static bool match_sysfs_device(int dev_id, const char *workdir, struct dirent *dir) +{ + const char *target = "0003:0001:0A37.*"; + char phys[512]; + char uevent[1024]; + char temp[512]; + int fd, nread; + bool found = false; + + if (fnmatch(target, dir->d_name, 0)) + return false; + + /* we found the correct VID/PID, now check for phys */ + sprintf(uevent, "%s/%s/uevent", workdir, dir->d_name); + + fd = open(uevent, O_RDONLY | O_NONBLOCK); + if (fd < 0) + return false; + + sprintf(phys, "PHYS=%d", dev_id); + + nread = read(fd, temp, ARRAY_SIZE(temp)); + if (nread > 0 && (strstr(temp, phys)) != NULL) + found = true; + + close(fd); + + return found; +} + +static int get_hid_id(int dev_id) +{ + const char *workdir = "/sys/devices/virtual/misc/uhid"; + const char *str_id; + DIR *d; + struct dirent *dir; + int found = -1; + + /* it would be nice to be able to use nftw, but the no_alu32 target doesn't support it */ + + d = opendir(workdir); + if (d) { + while ((dir = readdir(d)) != NULL) { + if (!match_sysfs_device(dev_id, workdir, dir)) + continue; + + str_id = dir->d_name + sizeof("0003:0001:0A37."); + found = (int)strtol(str_id, NULL, 16); + + break; + } + closedir(d); + } + + return found; +} + +static int get_hidraw(int dev_id) +{ + const char *workdir = "/sys/devices/virtual/misc/uhid"; + char sysfs[1024]; + DIR *d, *subd; + struct dirent *dir, *subdir; + int i, found = -1; + + /* retry 5 times in case the system is loaded */ + for (i = 5; i > 0; i--) { + usleep(10); + d = opendir(workdir); + + if (!d) + continue; + + while ((dir = readdir(d)) != NULL) { + if (!match_sysfs_device(dev_id, workdir, dir)) + continue; + + sprintf(sysfs, "%s/%s/hidraw", workdir, dir->d_name); + + subd = opendir(sysfs); + if (!subd) + continue; + + while ((subdir = readdir(subd)) != NULL) { + if (fnmatch("hidraw*", subdir->d_name, 0)) + continue; + + found = atoi(subdir->d_name + strlen("hidraw")); + } + + closedir(subd); + + if (found > 0) + break; + } + closedir(d); + } + + return found; +} + +static int open_hidraw(int dev_id) +{ + int hidraw_number; + char hidraw_path[64] = { 0 }; + + hidraw_number = get_hidraw(dev_id); + if (hidraw_number < 0) + return hidraw_number; + + /* open hidraw node to check the other side of the pipe */ + sprintf(hidraw_path, "/dev/hidraw%d", hidraw_number); + return open(hidraw_path, O_RDWR | O_NONBLOCK); +} + +struct test_params { + struct hid *skel; + int hidraw_fd; +}; + +static int prep_test(int dev_id, const char *prog_name, struct test_params *test_data) +{ + struct hid *hid_skel = NULL; + struct bpf_program *prog = NULL; + char buf[64] = {0}; + int hidraw_fd = -1; + int hid_id, attach_fd, err = -EINVAL; + struct attach_prog_args args = { + .retval = -1, + }; + DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattr, + .ctx_in = &args, + .ctx_size_in = sizeof(args), + ); + + /* locate the uevent file of the created device */ + hid_id = get_hid_id(dev_id); + if (!ASSERT_GE(hid_id, 0, "locate uhid device id")) + goto cleanup; + + args.hid = hid_id; + + hid_skel = hid__open(); + if (!ASSERT_OK_PTR(hid_skel, "hid_skel_open")) + goto cleanup; + + prog = bpf_object__find_program_by_name(*hid_skel->skeleton->obj, prog_name); + if (!ASSERT_OK_PTR(prog, "find_prog_by_name")) + goto cleanup; + + bpf_program__set_autoload(prog, true); + + err = hid__load(hid_skel); + if (!ASSERT_OK(err, "hid_skel_load")) + goto cleanup; + + attach_fd = bpf_program__fd(hid_skel->progs.attach_prog); + if (!ASSERT_GE(attach_fd, 0, "locate attach_prog")) { + err = attach_fd; + goto cleanup; + } + + args.prog_fd = bpf_program__fd(prog); + err = bpf_prog_test_run_opts(attach_fd, &tattr); + snprintf(buf, sizeof(buf), "attach_hid(%s)", prog_name); + if (!ASSERT_EQ(args.retval, 0, buf)) + goto cleanup; + + hidraw_fd = open_hidraw(dev_id); + if (!ASSERT_GE(hidraw_fd, 0, "open_hidraw")) + goto cleanup; + + test_data->skel = hid_skel; + test_data->hidraw_fd = hidraw_fd; + + return 0; + + cleanup: + if (hidraw_fd >= 0) + close(hidraw_fd); + + hid__destroy(hid_skel); + + memset(test_data, 0, sizeof(*test_data)); + + return err; +} + +static void cleanup_test(struct test_params *test_data) +{ + if (!test_data) + return; + + if (test_data->hidraw_fd) + close(test_data->hidraw_fd); + + hid__destroy(test_data->skel); + + memset(test_data, 0, sizeof(*test_data)); +} + +/* + * Attach hid_first_event to the given uhid device, + * retrieve and open the matching hidraw node, + * inject one event in the uhid device, + * check that the program sees it and can change the data + */ +static int test_hid_raw_event(int uhid_fd, int dev_id) +{ + struct hid *hid_skel = NULL; + struct test_params params; + int err, hidraw_fd = -1; + u8 buf[10] = {0}; + int ret = -1; + + err = prep_test(dev_id, "hid_first_event", ¶ms); + if (!ASSERT_EQ(err, 0, "prep_test(hid_first_event)")) + goto cleanup; + + hid_skel = params.skel; + hidraw_fd = params.hidraw_fd; + + /* check that the program is correctly loaded */ + ASSERT_EQ(hid_skel->data->callback_check, 52, "callback_check1"); + ASSERT_EQ(hid_skel->data->callback2_check, 52, "callback2_check1"); + + /* inject one event */ + buf[0] = 1; + buf[1] = 42; + send_event(uhid_fd, buf, 6); + + /* check that hid_first_event() was executed */ + ASSERT_EQ(hid_skel->data->callback_check, 42, "callback_check1"); + + /* read the data from hidraw */ + memset(buf, 0, sizeof(buf)); + err = read(hidraw_fd, buf, sizeof(buf)); + if (!ASSERT_EQ(err, 6, "read_hidraw")) + goto cleanup; + + if (!ASSERT_EQ(buf[0], 1, "hid_first_event")) + goto cleanup; + + if (!ASSERT_EQ(buf[2], 47, "hid_first_event")) + goto cleanup; + + /* inject another event */ + memset(buf, 0, sizeof(buf)); + buf[0] = 1; + buf[1] = 47; + send_event(uhid_fd, buf, 6); + + /* check that hid_first_event() was executed */ + ASSERT_EQ(hid_skel->data->callback_check, 47, "callback_check1"); + + /* read the data from hidraw */ + memset(buf, 0, sizeof(buf)); + err = read(hidraw_fd, buf, sizeof(buf)); + if (!ASSERT_EQ(err, 6, "read_hidraw")) + goto cleanup; + + if (!ASSERT_EQ(buf[2], 52, "hid_first_event")) + goto cleanup; + + ret = 0; + +cleanup: + cleanup_test(¶ms); + + return ret; +} + +/* + * Ensures that we can attach/detach programs + */ +static int test_attach_detach(int uhid_fd, int dev_id) +{ + struct test_params params; + int err, hidraw_fd = -1; + u8 buf[10] = {0}; + int ret = -1; + + err = prep_test(dev_id, "hid_first_event", ¶ms); + if (!ASSERT_EQ(err, 0, "prep_test(hid_first_event)")) + goto cleanup; + + /* inject one event */ + buf[0] = 1; + buf[1] = 42; + send_event(uhid_fd, buf, 6); + + /* read the data from hidraw */ + memset(buf, 0, sizeof(buf)); + err = read(params.hidraw_fd, buf, sizeof(buf)); + if (!ASSERT_EQ(err, 6, "read_hidraw_with_bpf")) + goto cleanup; + + if (!ASSERT_EQ(buf[0], 1, "hid_first_event")) + goto cleanup; + + if (!ASSERT_EQ(buf[2], 47, "hid_first_event")) + goto cleanup; + + /* pin the program and immediately unpin it */ +#define PIN_PATH "/sys/fs/bpf/hid_first_event" + bpf_program__pin(params.skel->progs.hid_first_event, PIN_PATH); + remove(PIN_PATH); +#undef PIN_PATH + usleep(100000); + + /* detach the program */ + cleanup_test(¶ms); + + hidraw_fd = open_hidraw(dev_id); + if (!ASSERT_GE(hidraw_fd, 0, "open_hidraw")) + goto cleanup; + + /* inject another event */ + memset(buf, 0, sizeof(buf)); + buf[0] = 1; + buf[1] = 47; + send_event(uhid_fd, buf, 6); + + /* read the data from hidraw */ + memset(buf, 0, sizeof(buf)); + err = read(hidraw_fd, buf, sizeof(buf)); + if (!ASSERT_EQ(err, 6, "read_hidraw_no_bpf")) + goto cleanup; + + if (!ASSERT_EQ(buf[0], 1, "event_no_bpf")) + goto cleanup; + + if (!ASSERT_EQ(buf[1], 47, "event_no_bpf")) + goto cleanup; + + if (!ASSERT_EQ(buf[2], 0, "event_no_bpf")) + goto cleanup; + + /* re-attach our program */ + + err = prep_test(dev_id, "hid_first_event", ¶ms); + if (!ASSERT_EQ(err, 0, "prep_test(hid_first_event)")) + goto cleanup; + + /* inject one event */ + memset(buf, 0, sizeof(buf)); + buf[0] = 1; + buf[1] = 42; + send_event(uhid_fd, buf, 6); + + /* read the data from hidraw */ + memset(buf, 0, sizeof(buf)); + err = read(params.hidraw_fd, buf, sizeof(buf)); + if (!ASSERT_EQ(err, 6, "read_hidraw")) + goto cleanup; + + if (!ASSERT_EQ(buf[0], 1, "hid_first_event")) + goto cleanup; + + if (!ASSERT_EQ(buf[2], 47, "hid_first_event")) + goto cleanup; + + ret = 0; + +cleanup: + if (hidraw_fd >= 0) + close(hidraw_fd); + + cleanup_test(¶ms); + + return ret; +} + +void serial_test_hid_bpf(void) +{ + int err, uhid_fd; + void *uhid_err; + time_t t; + pthread_t tid; + int dev_id; + + /* initialize random number generator */ + srand((unsigned int)time(&t)); + + dev_id = rand() % 1024; + + uhid_fd = setup_uhid(dev_id); + if (!ASSERT_GE(uhid_fd, 0, "setup uhid")) + return; + + err = uhid_start_listener(&tid, uhid_fd); + ASSERT_OK(err, "uhid_start_listener"); + + /* start the tests! */ + err = test_hid_raw_event(uhid_fd, dev_id); + ASSERT_OK(err, "hid"); + err = test_attach_detach(uhid_fd, dev_id); + ASSERT_OK(err, "hid_attach_detach"); + + destroy(uhid_fd); + + pthread_join(tid, &uhid_err); + err = (int)(long)uhid_err; + CHECK_FAIL(err); +} diff --git a/tools/testing/selftests/bpf/progs/hid.c b/tools/testing/selftests/bpf/progs/hid.c new file mode 100644 index 000000000000..fc0a4241643a --- /dev/null +++ b/tools/testing/selftests/bpf/progs/hid.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Red hat */ +#include "vmlinux.h" +#include +#include + +char _license[] SEC("license") = "GPL"; + +extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx, + unsigned int offset, + const size_t __sz) __ksym; +extern int hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, u32 flags) __ksym; + +struct attach_prog_args { + int prog_fd; + unsigned int hid; + int retval; +}; + +__u64 callback_check = 52; +__u64 callback2_check = 52; + +SEC("?fmod_ret/hid_bpf_device_event") +int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx) +{ + __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */); + + if (!rw_data) + return 0; /* EPERM check */ + + callback_check = rw_data[1]; + + rw_data[2] = rw_data[1] + 5; + + return 0; +} + +SEC("syscall") +int attach_prog(struct attach_prog_args *ctx) +{ + ctx->retval = hid_bpf_attach_prog(ctx->hid, + ctx->prog_fd, + 0); + return 0; +} From patchwork Wed May 18 20:59:16 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12854105 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3BDC7C433EF for ; Wed, 18 May 2022 21:00:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S242919AbiERVAX (ORCPT ); Wed, 18 May 2022 17:00:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58942 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S242965AbiERVAT (ORCPT ); Wed, 18 May 2022 17:00:19 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 8591B254736 for ; Wed, 18 May 2022 14:00:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1652907616; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=TvYf3NySHm7+YmMy1F6wfGAhpXSXypM6VA8rV/Mt9VA=; b=DYb45nyIzmftuglD1nl2tL75XPHEySQgZhoZ307SdwxDkLeGovAHAuTi+uwKSxjP6B7Dxq s80ghXeM3MCuixbNwy+qi6b/D7RVNDFQ8jAZrzmzNY8ms4VEibBfZwS8zGEZ9RwTPBF9M+ KWHfZ0lEHUWC9Dq08xXRLxUlimAzURM= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-103-BPWfa_hiNcibSukMH01WTA-1; Wed, 18 May 2022 17:00:11 -0400 X-MC-Unique: BPWfa_hiNcibSukMH01WTA-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 3798C18E6C41; Wed, 18 May 2022 21:00:10 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.58]) by smtp.corp.redhat.com (Postfix) with ESMTP id BA9732166B25; Wed, 18 May 2022 21:00:05 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v5 09/17] HID: bpf: allocate data memory for device_event BPF programs Date: Wed, 18 May 2022 22:59:16 +0200 Message-Id: <20220518205924.399291-10-benjamin.tissoires@redhat.com> In-Reply-To: <20220518205924.399291-1-benjamin.tissoires@redhat.com> References: <20220518205924.399291-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net We need to also be able to change the size of the report. Reducing it is easy, because we already have the incoming buffer that is big enough, but extending it is harder. Pre-allocate a buffer that is big enough to handle all reports of the device, and use that as the primary buffer for BPF programs. To be able to change the size of the buffer, we change the device_event API and request it to return the size of the buffer. Signed-off-by: Benjamin Tissoires Reported-by: kernel test robot --- new-ish in v5 --- drivers/hid/bpf/hid_bpf_dispatch.c | 116 +++++++++++++++++++++++++--- drivers/hid/bpf/hid_bpf_jmp_table.c | 4 +- drivers/hid/hid-core.c | 12 ++- include/linux/hid_bpf.h | 37 +++++++-- 4 files changed, 151 insertions(+), 18 deletions(-) diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index 574e0a627861..87fd11539213 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -28,8 +28,9 @@ EXPORT_SYMBOL(hid_bpf_ops); * * @ctx: The HID-BPF context * - * @return %0 on success and keep processing; a negative error code to interrupt - * the processing of this event + * @return %0 on success and keep processing; a positive value to change the + * incoming size buffer; a negative error code to interrupt the processing + * of this event * * Declare an %fmod_ret tracing bpf program to this function and attach this * program through hid_bpf_attach_prog() to have this helper called for @@ -44,23 +45,43 @@ __weak noinline int hid_bpf_device_event(struct hid_bpf_ctx *ctx) } ALLOW_ERROR_INJECTION(hid_bpf_device_event, ERRNO); -int +u8 * dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type, u8 *data, - u32 size, int interrupt) + u32 *size, int interrupt) { struct hid_bpf_ctx_kern ctx_kern = { .ctx = { .hid = hdev, .report_type = type, - .size = size, + .allocated_size = hdev->bpf.allocated_data, + .size = *size, }, - .data = data, + .data = hdev->bpf.device_data, }; + int ret; if (type >= HID_REPORT_TYPES) - return -EINVAL; + return ERR_PTR(-EINVAL); + + /* no program has been attached yet */ + if (!hdev->bpf.device_data) + return data; + + memset(ctx_kern.data, 0, hdev->bpf.allocated_data); + memcpy(ctx_kern.data, data, *size); + + ret = hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_DEVICE_EVENT, &ctx_kern); + if (ret < 0) + return ERR_PTR(ret); + + if (ret) { + if (ret > ctx_kern.ctx.allocated_size) + return ERR_PTR(-EINVAL); - return hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_DEVICE_EVENT, &ctx_kern); + *size = ret; + } + + return ctx_kern.data; } EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event); @@ -83,7 +104,7 @@ hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t rdwr ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); - if (rdwr_buf_size + offset > ctx->size) + if (rdwr_buf_size + offset > ctx->allocated_size) return NULL; return ctx_kern->data + offset; @@ -122,6 +143,51 @@ static int device_match_id(struct device *dev, const void *id) return hdev->id == *(int *)id; } +static int __hid_bpf_allocate_data(struct hid_device *hdev, u8 **data, u32 *size) +{ + u8 *alloc_data; + unsigned int i, j, max_report_len = 0; + size_t alloc_size = 0; + + /* compute the maximum report length for this device */ + for (i = 0; i < HID_REPORT_TYPES; i++) { + struct hid_report_enum *report_enum = hdev->report_enum + i; + + for (j = 0; j < HID_MAX_IDS; j++) { + struct hid_report *report = report_enum->report_id_hash[j]; + + if (report) + max_report_len = max(max_report_len, hid_report_len(report)); + } + } + + /* + * Give us a little bit of extra space and some predictability in the + * buffer length we create. This way, we can tell users that they can + * work on chunks of 64 bytes of memory without having the bpf verifier + * scream at them. + */ + alloc_size = DIV_ROUND_UP(max_report_len, 64) * 64; + + alloc_data = kzalloc(alloc_size, GFP_KERNEL); + if (!alloc_data) + return -ENOMEM; + + *data = alloc_data; + *size = alloc_size; + + return 0; +} + +static int hid_bpf_allocate_event_data(struct hid_device *hdev) +{ + /* hdev->bpf.device_data is already allocated, abort */ + if (hdev->bpf.device_data) + return 0; + + return __hid_bpf_allocate_data(hdev, &hdev->bpf.device_data, &hdev->bpf.allocated_data); +} + /** * hid_bpf_attach_prog - Attach the given @prog_fd to the given HID device * @@ -137,7 +203,7 @@ hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags) { struct hid_device *hdev; struct device *dev; - int prog_type = hid_bpf_get_prog_attach_type(prog_fd); + int err, prog_type = hid_bpf_get_prog_attach_type(prog_fd); if (!hid_bpf_ops) return -EINVAL; @@ -157,6 +223,12 @@ hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags) hdev = to_hid_device(dev); + if (prog_type == HID_BPF_PROG_TYPE_DEVICE_EVENT) { + err = hid_bpf_allocate_event_data(hdev); + if (err) + return err; + } + return __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags); } @@ -170,6 +242,30 @@ static const struct btf_kfunc_id_set hid_bpf_syscall_kfunc_set = { .check_set = &hid_bpf_syscall_kfunc_ids, }; +int hid_bpf_connect_device(struct hid_device *hdev) +{ + struct hid_bpf_prog_list *prog_list; + + rcu_read_lock(); + prog_list = rcu_dereference(hdev->bpf.progs[HID_BPF_PROG_TYPE_DEVICE_EVENT]); + rcu_read_unlock(); + + /* only allocate BPF data if there are programs attached */ + if (!prog_list) + return 0; + + return hid_bpf_allocate_event_data(hdev); +} +EXPORT_SYMBOL_GPL(hid_bpf_connect_device); + +void hid_bpf_disconnect_device(struct hid_device *hdev) +{ + kfree(hdev->bpf.device_data); + hdev->bpf.device_data = NULL; + hdev->bpf.allocated_data = 0; +} +EXPORT_SYMBOL_GPL(hid_bpf_disconnect_device); + void hid_bpf_destroy_device(struct hid_device *hdev) { if (!hdev) diff --git a/drivers/hid/bpf/hid_bpf_jmp_table.c b/drivers/hid/bpf/hid_bpf_jmp_table.c index 27c78753effd..aed88b4b52ad 100644 --- a/drivers/hid/bpf/hid_bpf_jmp_table.c +++ b/drivers/hid/bpf/hid_bpf_jmp_table.c @@ -122,8 +122,10 @@ int hid_bpf_prog_run(struct hid_device *hdev, enum hid_bpf_prog_type type, ctx_kern->ctx.index = idx; err = __hid_bpf_tail_call(&ctx_kern->ctx); - if (err) + if (err < 0) break; + if (err) + ctx_kern->ctx.retval = err; } out_unlock: diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index bdda415e051b..831ef57ddbeb 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2040,9 +2040,11 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data report_enum = hid->report_enum + type; hdrv = hid->driver; - ret = dispatch_hid_bpf_device_event(hid, type, data, size, interrupt); - if (ret) + data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt); + if (IS_ERR(data)) { + ret = PTR_ERR(data); goto unlock; + } if (!size) { dbg_hid("empty report\n"); @@ -2157,6 +2159,10 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) int len; int ret; + ret = hid_bpf_connect_device(hdev); + if (ret) + return ret; + if (hdev->quirks & HID_QUIRK_HIDDEV_FORCE) connect_mask |= (HID_CONNECT_HIDDEV_FORCE | HID_CONNECT_HIDDEV); if (hdev->quirks & HID_QUIRK_HIDINPUT_FORCE) @@ -2254,6 +2260,8 @@ void hid_disconnect(struct hid_device *hdev) if (hdev->claimed & HID_CLAIMED_HIDRAW) hidraw_disconnect(hdev); hdev->claimed = 0; + + hid_bpf_disconnect_device(hdev); } EXPORT_SYMBOL_GPL(hid_disconnect); diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h index 9d893c14a0f2..c9684de18f3f 100644 --- a/include/linux/hid_bpf.h +++ b/include/linux/hid_bpf.h @@ -29,15 +29,32 @@ struct hid_device; * a bigger index). * @hid: the ``struct hid_device`` representing the device itself * @report_type: used for ``hid_bpf_device_event()`` + * @allocated_size: Allocated size of data. + * + * This is how much memory is available and can be requested + * by the HID program. + * Note that for ``HID_BPF_RDESC_FIXUP``, that memory is set to + * ``4096`` (4 KB) * @size: Valid data in the data field. * * Programs can get the available valid size in data by fetching this field. + * Programs can also change this value by returning a positive number in the + * program. + * To discard the event, return a negative error code. + * + * ``size`` must always be less or equal than ``allocated_size`` (it is enforced + * once all BPF programs have been run). + * @retval: Return value of the previous program. */ struct hid_bpf_ctx { __u32 index; const struct hid_device *hid; + __u32 allocated_size; enum hid_report_type report_type; - __s32 size; + union { + __s32 retval; + __s32 size; + }; }; /* Following functions are tracepoints that BPF programs can attach to */ @@ -78,6 +95,12 @@ struct hid_bpf_prog_list { /* stored in each device */ struct hid_bpf { + u8 *device_data; /* allocated when a bpf program of type + * SEC(f.../hid_bpf_device_event) has been attached + * to this HID device + */ + u32 allocated_data; + struct hid_bpf_prog_list __rcu *progs[HID_BPF_PROG_TYPE_MAX]; /* attached BPF progs */ bool destroyed; /* prevents the assignment of any progs */ @@ -85,13 +108,17 @@ struct hid_bpf { }; #ifdef CONFIG_BPF -int dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data, - u32 size, int interrupt); +u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data, + u32 *size, int interrupt); +int hid_bpf_connect_device(struct hid_device *hdev); +void hid_bpf_disconnect_device(struct hid_device *hdev); void hid_bpf_destroy_device(struct hid_device *hid); void hid_bpf_device_init(struct hid_device *hid); #else /* CONFIG_BPF */ -static inline int dispatch_hid_bpf_device_event(struct hid_device *hid, int type, u8 *data, - u32 size, int interrupt) { return 0; } +static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, int type, u8 *data, + u32 *size, int interrupt) { return 0; } +static inline int hid_bpf_connect_device(struct hid_device *hdev) { return 0; } +static inline void hid_bpf_disconnect_device(struct hid_device *hdev) {} static inline void hid_bpf_destroy_device(struct hid_device *hid) {} static inline void hid_bpf_device_init(struct hid_device *hid) {} #endif /* CONFIG_BPF */ From patchwork Wed May 18 20:59:17 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12854110 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5195CC433EF for ; Wed, 18 May 2022 21:01:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S243136AbiERVBW (ORCPT ); Wed, 18 May 2022 17:01:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59404 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S242973AbiERVAW (ORCPT ); Wed, 18 May 2022 17:00:22 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id CE1B72555A0 for ; Wed, 18 May 2022 14:00:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1652907619; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=tEF+66qA0wUsA595/mSAmCFMMreNnzteme04lvAe/m8=; b=Z0AJJfnScmDURFBDYmXQ5G9DVRAVbTo6i/tmtI6v0mHWOeCLf4gDtg2RMGqjQY/WX69rWK HtgBGMg3IpFMYVCgIhGBnHobXUy3vycjTsH8V+KgDPEwILB/KaO+iwfJ1OYm3eIGvS3b4z YG1F1GqWBj3S8WbjZWgGgasn6Sg+Pfc= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-381-flzaXyZLOFOsu1bS3crdLA-1; Wed, 18 May 2022 17:00:16 -0400 X-MC-Unique: flzaXyZLOFOsu1bS3crdLA-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id E83131D96CAB; Wed, 18 May 2022 21:00:14 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.58]) by smtp.corp.redhat.com (Postfix) with ESMTP id 87F7A2166B25; Wed, 18 May 2022 21:00:10 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v5 10/17] selftests/bpf/hid: add test to change the report size Date: Wed, 18 May 2022 22:59:17 +0200 Message-Id: <20220518205924.399291-11-benjamin.tissoires@redhat.com> In-Reply-To: <20220518205924.399291-1-benjamin.tissoires@redhat.com> References: <20220518205924.399291-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Use a different report with a bigger size and ensures we are doing things properly. Signed-off-by: Benjamin Tissoires --- new in v5 --- tools/testing/selftests/bpf/prog_tests/hid.c | 60 ++++++++++++++++++++ tools/testing/selftests/bpf/progs/hid.c | 15 ++++- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c index 719d220c8d86..47bc0a30c275 100644 --- a/tools/testing/selftests/bpf/prog_tests/hid.c +++ b/tools/testing/selftests/bpf/prog_tests/hid.c @@ -17,6 +17,17 @@ static unsigned char rdesc[] = { 0xa1, 0x01, /* COLLECTION (Application) */ 0x09, 0x01, /* Usage (Vendor Usage 0x01) */ 0xa1, 0x00, /* COLLECTION (Physical) */ + 0x85, 0x02, /* REPORT_ID (2) */ + 0x19, 0x01, /* USAGE_MINIMUM (1) */ + 0x29, 0x08, /* USAGE_MAXIMUM (3) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0xff, /* LOGICAL_MAXIMUM (255) */ + 0x95, 0x08, /* REPORT_COUNT (8) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x81, 0x02, /* INPUT (Data,Var,Abs) */ + 0xc0, /* END_COLLECTION */ + 0x09, 0x01, /* Usage (Vendor Usage 0x01) */ + 0xa1, 0x00, /* COLLECTION (Physical) */ 0x85, 0x01, /* REPORT_ID (1) */ 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ 0x19, 0x01, /* USAGE_MINIMUM (1) */ @@ -635,6 +646,53 @@ static int test_attach_detach(int uhid_fd, int dev_id) return ret; } +/* + * Attach hid_change_report_id to the given uhid device, + * retrieve and open the matching hidraw node, + * inject one event in the uhid device, + * check that the program sees it and can change the data + */ +static int test_hid_change_report(int uhid_fd, int dev_id) +{ + struct test_params params; + int err, hidraw_fd = -1; + u8 buf[10] = {0}; + int ret = -1; + + err = prep_test(dev_id, "hid_change_report_id", ¶ms); + if (!ASSERT_EQ(err, 0, "prep_test(hid_change_report_id)")) + goto cleanup; + + hidraw_fd = params.hidraw_fd; + + /* inject one event */ + buf[0] = 1; + buf[1] = 42; + send_event(uhid_fd, buf, 6); + + /* read the data from hidraw */ + memset(buf, 0, sizeof(buf)); + err = read(hidraw_fd, buf, sizeof(buf)); + if (!ASSERT_EQ(err, 9, "read_hidraw")) + goto cleanup; + + if (!ASSERT_EQ(buf[0], 2, "hid_change_report_id")) + goto cleanup; + + if (!ASSERT_EQ(buf[1], 42, "hid_change_report_id")) + goto cleanup; + + if (!ASSERT_EQ(buf[2], 0, "leftovers_from_previous_test")) + goto cleanup; + + ret = 0; + +cleanup: + cleanup_test(¶ms); + + return ret; +} + void serial_test_hid_bpf(void) { int err, uhid_fd; @@ -660,6 +718,8 @@ void serial_test_hid_bpf(void) ASSERT_OK(err, "hid"); err = test_attach_detach(uhid_fd, dev_id); ASSERT_OK(err, "hid_attach_detach"); + err = test_hid_change_report(uhid_fd, dev_id); + ASSERT_OK(err, "hid_change_report"); destroy(uhid_fd); diff --git a/tools/testing/selftests/bpf/progs/hid.c b/tools/testing/selftests/bpf/progs/hid.c index fc0a4241643a..ee7529c47ad8 100644 --- a/tools/testing/selftests/bpf/progs/hid.c +++ b/tools/testing/selftests/bpf/progs/hid.c @@ -32,7 +32,20 @@ int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx) rw_data[2] = rw_data[1] + 5; - return 0; + return hid_ctx->size; +} + +SEC("?fmod_ret/hid_bpf_device_event") +int BPF_PROG(hid_change_report_id, struct hid_bpf_ctx *hid_ctx) +{ + __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */); + + if (!rw_data) + return 0; /* EPERM check */ + + rw_data[0] = 2; + + return 9; } SEC("syscall") From patchwork Wed May 18 20:59:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12854112 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AAB95C4167D for ; Wed, 18 May 2022 21:01:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S243148AbiERVBc (ORCPT ); Wed, 18 May 2022 17:01:32 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59768 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S242979AbiERVA0 (ORCPT ); Wed, 18 May 2022 17:00:26 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id ECDB42555BF for ; Wed, 18 May 2022 14:00:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1652907622; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=goLoEzqznPudxrGIzFVJ0qKOzLiCfYk59NUHHPtLC6c=; b=Acloeda9ogOUvnkJ9a/ZuLa/LtW+/Vtvya2c6V/zfEOf9DM3hfmp7CpqjVRC1O1Z+KK70w /+k3E1nVwobXienapT4Lv3rgJTmHJ1r7tJGu7z8B3T1eeem8/fbUzfe3Q4zpjqJWp/0qXg lmh82gxQAexW/Yv1wZbjUn/dS+CVJyA= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-634-YdPQqsIsOdu8LdPUyHkVNw-1; Wed, 18 May 2022 17:00:19 -0400 X-MC-Unique: YdPQqsIsOdu8LdPUyHkVNw-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 5729C803D45; Wed, 18 May 2022 21:00:18 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.58]) by smtp.corp.redhat.com (Postfix) with ESMTP id 3B4872166B25; Wed, 18 May 2022 21:00:15 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v5 11/17] HID: bpf: introduce hid_hw_request() Date: Wed, 18 May 2022 22:59:18 +0200 Message-Id: <20220518205924.399291-12-benjamin.tissoires@redhat.com> In-Reply-To: <20220518205924.399291-1-benjamin.tissoires@redhat.com> References: <20220518205924.399291-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net This function can not be called under IRQ, thus it is only available while in SEC("syscall"). This function requires a memory buffer to work with, and so we also provide a helper to create a HID-BPF context based on the HID unique ID. Signed-off-by: Benjamin Tissoires --- new-ish in v5 --- drivers/hid/bpf/hid_bpf_dispatch.c | 159 +++++++++++++++++++++++++++++ drivers/hid/hid-core.c | 2 + include/linux/hid_bpf.h | 12 ++- 3 files changed, 172 insertions(+), 1 deletion(-) diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index 87fd11539213..01158d7a14ae 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -232,14 +232,173 @@ hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags) return __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags); } +/** + * hid_bpf_allocate_context - Allocate a context to the given HID device + * + * @hid_id: the system unique identifier of the HID device + * + * @returns A pointer to &struct hid_bpf_ctx on success, %NULL on error. + */ +noinline struct hid_bpf_ctx * +hid_bpf_allocate_context(unsigned int hid_id) +{ + struct hid_device *hdev; + struct hid_bpf_ctx_kern *ctx_kern = NULL; + struct device *dev; + int err; + + if (!hid_bpf_ops) + return NULL; + + dev = bus_find_device(hid_bpf_ops->bus_type, NULL, &hid_id, device_match_id); + if (!dev) + return NULL; + + hdev = to_hid_device(dev); + + ctx_kern = kzalloc(sizeof(*ctx_kern), GFP_KERNEL); + if (!ctx_kern) + return NULL; + + ctx_kern->ctx.hid = hdev; + + err = __hid_bpf_allocate_data(hdev, &ctx_kern->data, &ctx_kern->ctx.allocated_size); + if (err) + goto out_err; + + return &ctx_kern->ctx; + + out_err: + kfree(ctx_kern); + return NULL; +} + +/** + * hid_bpf_release_context - Release the previously allocated context @ctx + * + * @ctx: the HID-BPF context to release + * + */ +noinline void +hid_bpf_release_context(struct hid_bpf_ctx *ctx) +{ + struct hid_bpf_ctx_kern *ctx_kern; + + if (!ctx) + return; + + ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); + + kfree(ctx_kern->data); + kfree(ctx_kern); +} + +/** + * hid_bpf_hw_request - Communicate with a HID device + * + * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context() + * @buf: a %PTR_TO_MEM buffer + * @size: the size of the data to transfer + * @rtype: the type of the report (%HID_INPUT_REPORT, %HID_FEATURE_REPORT, %HID_OUTPUT_REPORT) + * @reqtype: the type of the request (%HID_REQ_GET_REPORT, %HID_REQ_SET_REPORT, ...) + * + * @returns %0 on success, a negative error code otherwise. + */ +noinline int +hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t size, + enum hid_report_type rtype, int reqtype) +{ + struct hid_device *hdev = (struct hid_device *)ctx->hid; /* discard const */ + struct hid_report *report; + struct hid_report_enum *report_enum; + u8 *dma_data; + u32 report_len; + int ret; + + /* check arguments */ + if (!ctx || !hid_bpf_ops) + return -EINVAL; + + switch (rtype) { + case HID_INPUT_REPORT: + case HID_OUTPUT_REPORT: + case HID_FEATURE_REPORT: + break; + default: + return -EINVAL; + } + + switch (reqtype) { + case HID_REQ_GET_REPORT: + case HID_REQ_GET_IDLE: + case HID_REQ_GET_PROTOCOL: + case HID_REQ_SET_REPORT: + case HID_REQ_SET_IDLE: + case HID_REQ_SET_PROTOCOL: + break; + default: + return -EINVAL; + } + + if (size < 1) + return -EINVAL; + + report_enum = hdev->report_enum + rtype; + report = hid_bpf_ops->hid_get_report(report_enum, buf); + if (!report) + return -EINVAL; + + report_len = hid_report_len(report); + + if (size > report_len) + size = report_len; + + dma_data = kmemdup(buf, size, GFP_KERNEL); + if (!dma_data) + return -ENOMEM; + + ret = hid_bpf_ops->hid_hw_raw_request(hdev, + dma_data[0], + dma_data, + size, + rtype, + reqtype); + + if (ret > 0) + memcpy(buf, dma_data, ret); + + kfree(dma_data); + return ret; +} + /* for syscall HID-BPF */ BTF_SET_START(hid_bpf_syscall_kfunc_ids) BTF_ID(func, hid_bpf_attach_prog) +BTF_ID(func, hid_bpf_allocate_context) +BTF_ID(func, hid_bpf_release_context) +BTF_ID(func, hid_bpf_get_data) +BTF_ID(func, hid_bpf_hw_request) BTF_SET_END(hid_bpf_syscall_kfunc_ids) +BTF_SET_START(hid_bpf_syscall_kfunc_ret_null_ids) +BTF_ID(func, hid_bpf_get_data) +BTF_ID(func, hid_bpf_allocate_context) +BTF_SET_END(hid_bpf_syscall_kfunc_ret_null_ids) + +BTF_SET_START(hid_bpf_syscall_kfunc_alloc_ids) +BTF_ID(func, hid_bpf_allocate_context) +BTF_SET_END(hid_bpf_syscall_kfunc_alloc_ids) + +BTF_SET_START(hid_bpf_syscall_kfunc_release_ids) +BTF_ID(func, hid_bpf_release_context) +BTF_SET_END(hid_bpf_syscall_kfunc_release_ids) + static const struct btf_kfunc_id_set hid_bpf_syscall_kfunc_set = { .owner = THIS_MODULE, .check_set = &hid_bpf_syscall_kfunc_ids, + .ret_null_set = &hid_bpf_syscall_kfunc_ret_null_ids, + .acquire_set = &hid_bpf_syscall_kfunc_alloc_ids, + .release_set = &hid_bpf_syscall_kfunc_release_ids, }; int hid_bpf_connect_device(struct hid_device *hdev) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 831ef57ddbeb..70b3790f4595 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2913,6 +2913,8 @@ int hid_check_keys_pressed(struct hid_device *hid) EXPORT_SYMBOL_GPL(hid_check_keys_pressed); static struct hid_bpf_ops hid_ops = { + .hid_get_report = hid_get_report, + .hid_hw_raw_request = hid_hw_raw_request, .owner = THIS_MODULE, .bus_type = &hid_bus_type, }; diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h index c9684de18f3f..62478a53af22 100644 --- a/include/linux/hid_bpf.h +++ b/include/linux/hid_bpf.h @@ -61,11 +61,15 @@ struct hid_bpf_ctx { int hid_bpf_device_event(struct hid_bpf_ctx *ctx); /* Following functions are kfunc that we export to BPF programs */ -/* only available in tracing */ +/* available everywhere in HID-BPF */ __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t __sz); /* only available in syscall */ int hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags); +int hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t size, + enum hid_report_type rtype, int reqtype); +struct hid_bpf_ctx *hid_bpf_allocate_context(unsigned int hid_id); +void hid_bpf_release_context(struct hid_bpf_ctx *ctx); /* * Below is HID internal @@ -81,7 +85,13 @@ enum hid_bpf_prog_type { HID_BPF_PROG_TYPE_MAX, }; +struct hid_report_enum; + struct hid_bpf_ops { + struct hid_report *(*hid_get_report)(struct hid_report_enum *report_enum, const u8 *data); + int (*hid_hw_raw_request)(struct hid_device *hdev, + unsigned char reportnum, __u8 *buf, + size_t len, unsigned char rtype, int reqtype); struct module *owner; struct bus_type *bus_type; }; From patchwork Wed May 18 20:59:19 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12854114 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6D1AFC4321E for ; Wed, 18 May 2022 21:02:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S243085AbiERVB5 (ORCPT ); Wed, 18 May 2022 17:01:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34638 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S243058AbiERVBH (ORCPT ); Wed, 18 May 2022 17:01:07 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 57E71259F87 for ; Wed, 18 May 2022 14:00:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1652907635; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=gC0dx605l5WeGGnTBbF9J16eadz0PSMt0gS7tJITGSE=; b=Rwa0Pdlr8KsOTUrI5dMpYjbwjwZprevClEQZj0nsmJ1luBFhkXql32IrZZZDbKNVZjYcX/ pI2pUpW+4LSymx4Ul0k4YWbCnVQNFe+PlZqG+DKkg/X3GgMl14hEh+wKvfvGDQTM4inbdK U66k27ZBrjr/MwymJUGiyhrAQfmTtEc= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-211-pVi_G_J8Nyy_ic7mB6udZg-1; Wed, 18 May 2022 17:00:29 -0400 X-MC-Unique: pVi_G_J8Nyy_ic7mB6udZg-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id CB9E8381078F; Wed, 18 May 2022 21:00:27 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.58]) by smtp.corp.redhat.com (Postfix) with ESMTP id 94DB82166B25; Wed, 18 May 2022 21:00:18 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v5 12/17] selftests/bpf: add tests for bpf_hid_hw_request Date: Wed, 18 May 2022 22:59:19 +0200 Message-Id: <20220518205924.399291-13-benjamin.tissoires@redhat.com> In-Reply-To: <20220518205924.399291-1-benjamin.tissoires@redhat.com> References: <20220518205924.399291-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Add tests for the newly implemented function. We test here only the GET_REPORT part because the other calls are pure HID protocol and won't infer the result of the test of the bpf hook. Signed-off-by: Benjamin Tissoires --- changes in v5: - use the new hid_bpf_allocate_context() API - remove the need for ctx_in for syscall TEST_RUN changes in v3: - use the new hid_get_data API - directly use HID_FEATURE_REPORT and HID_REQ_GET_REPORT from uapi changes in v2: - split the series by bpf/libbpf/hid/selftests and samples --- tools/testing/selftests/bpf/prog_tests/hid.c | 114 ++++++++++++++++--- tools/testing/selftests/bpf/progs/hid.c | 59 ++++++++++ 2 files changed, 155 insertions(+), 18 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c index 47bc0a30c275..54c0a0fcd54d 100644 --- a/tools/testing/selftests/bpf/prog_tests/hid.c +++ b/tools/testing/selftests/bpf/prog_tests/hid.c @@ -77,12 +77,23 @@ static unsigned char rdesc[] = { 0xc0, /* END_COLLECTION */ }; +static u8 feature_data[] = { 1, 2 }; + struct attach_prog_args { int prog_fd; unsigned int hid; int retval; }; +struct hid_hw_request_syscall_args { + __u8 data[10]; + unsigned int hid; + int retval; + size_t size; + enum hid_report_type type; + __u8 request_type; +}; + static pthread_mutex_t uhid_started_mtx = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t uhid_started = PTHREAD_COND_INITIALIZER; @@ -142,7 +153,7 @@ static void destroy(int fd) static int uhid_event(int fd) { - struct uhid_event ev; + struct uhid_event ev, answer; ssize_t ret; memset(&ev, 0, sizeof(ev)); @@ -183,6 +194,15 @@ static int uhid_event(int fd) break; case UHID_GET_REPORT: fprintf(stderr, "UHID_GET_REPORT from uhid-dev\n"); + + answer.type = UHID_GET_REPORT_REPLY; + answer.u.get_report_reply.id = ev.u.get_report.id; + answer.u.get_report_reply.err = ev.u.get_report.rnum == 1 ? 0 : -EIO; + answer.u.get_report_reply.size = sizeof(feature_data); + memcpy(answer.u.get_report_reply.data, feature_data, sizeof(feature_data)); + + uhid_write(fd, &answer); + break; case UHID_SET_REPORT: fprintf(stderr, "UHID_SET_REPORT from uhid-dev\n"); @@ -391,6 +411,7 @@ static int open_hidraw(int dev_id) struct test_params { struct hid *skel; int hidraw_fd; + int hid_id; }; static int prep_test(int dev_id, const char *prog_name, struct test_params *test_data) @@ -419,27 +440,33 @@ static int prep_test(int dev_id, const char *prog_name, struct test_params *test if (!ASSERT_OK_PTR(hid_skel, "hid_skel_open")) goto cleanup; - prog = bpf_object__find_program_by_name(*hid_skel->skeleton->obj, prog_name); - if (!ASSERT_OK_PTR(prog, "find_prog_by_name")) - goto cleanup; + if (prog_name) { + prog = bpf_object__find_program_by_name(*hid_skel->skeleton->obj, prog_name); + if (!ASSERT_OK_PTR(prog, "find_prog_by_name")) + goto cleanup; - bpf_program__set_autoload(prog, true); + bpf_program__set_autoload(prog, true); - err = hid__load(hid_skel); - if (!ASSERT_OK(err, "hid_skel_load")) - goto cleanup; + err = hid__load(hid_skel); + if (!ASSERT_OK(err, "hid_skel_load")) + goto cleanup; - attach_fd = bpf_program__fd(hid_skel->progs.attach_prog); - if (!ASSERT_GE(attach_fd, 0, "locate attach_prog")) { - err = attach_fd; - goto cleanup; - } + attach_fd = bpf_program__fd(hid_skel->progs.attach_prog); + if (!ASSERT_GE(attach_fd, 0, "locate attach_prog")) { + err = attach_fd; + goto cleanup; + } - args.prog_fd = bpf_program__fd(prog); - err = bpf_prog_test_run_opts(attach_fd, &tattr); - snprintf(buf, sizeof(buf), "attach_hid(%s)", prog_name); - if (!ASSERT_EQ(args.retval, 0, buf)) - goto cleanup; + args.prog_fd = bpf_program__fd(prog); + err = bpf_prog_test_run_opts(attach_fd, &tattr); + snprintf(buf, sizeof(buf), "attach_hid(%s)", prog_name); + if (!ASSERT_EQ(args.retval, 0, buf)) + goto cleanup; + } else { + err = hid__load(hid_skel); + if (!ASSERT_OK(err, "hid_skel_load")) + goto cleanup; + } hidraw_fd = open_hidraw(dev_id); if (!ASSERT_GE(hidraw_fd, 0, "open_hidraw")) @@ -447,6 +474,7 @@ static int prep_test(int dev_id, const char *prog_name, struct test_params *test test_data->skel = hid_skel; test_data->hidraw_fd = hidraw_fd; + test_data->hid_id = hid_id; return 0; @@ -693,6 +721,54 @@ static int test_hid_change_report(int uhid_fd, int dev_id) return ret; } +/* + * Attach hid_user_raw_request to the given uhid device, + * call the bpf program from userspace + * check that the program is called and does the expected. + */ +static int test_hid_user_raw_request_call(int uhid_fd, int dev_id) +{ + struct test_params params; + int err, prog_fd; + int ret = -1; + struct hid_hw_request_syscall_args args = { + .retval = -1, + .type = HID_FEATURE_REPORT, + .request_type = HID_REQ_GET_REPORT, + .size = 10, + }; + DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs, + .ctx_in = &args, + .ctx_size_in = sizeof(args), + ); + + err = prep_test(dev_id, NULL, ¶ms); + if (!ASSERT_EQ(err, 0, "prep_test()")) + goto cleanup; + + args.hid = params.hid_id; + args.data[0] = 1; /* report ID */ + + prog_fd = bpf_program__fd(params.skel->progs.hid_user_raw_request); + + err = bpf_prog_test_run_opts(prog_fd, &tattrs); + if (!ASSERT_EQ(err, 0, "bpf_prog_test_run_opts")) + goto cleanup; + + if (!ASSERT_EQ(args.retval, 2, "bpf_prog_test_run_opts_retval")) + goto cleanup; + + if (!ASSERT_EQ(args.data[1], 2, "hid_user_raw_request_check_in")) + goto cleanup; + + ret = 0; + +cleanup: + cleanup_test(¶ms); + + return ret; +} + void serial_test_hid_bpf(void) { int err, uhid_fd; @@ -720,6 +796,8 @@ void serial_test_hid_bpf(void) ASSERT_OK(err, "hid_attach_detach"); err = test_hid_change_report(uhid_fd, dev_id); ASSERT_OK(err, "hid_change_report"); + err = test_hid_user_raw_request_call(uhid_fd, dev_id); + ASSERT_OK(err, "hid_change_report"); destroy(uhid_fd); diff --git a/tools/testing/selftests/bpf/progs/hid.c b/tools/testing/selftests/bpf/progs/hid.c index ee7529c47ad8..e3444d444303 100644 --- a/tools/testing/selftests/bpf/progs/hid.c +++ b/tools/testing/selftests/bpf/progs/hid.c @@ -10,6 +10,13 @@ extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t __sz) __ksym; extern int hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, u32 flags) __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 len, + enum hid_report_type type, + int reqtype) __ksym; struct attach_prog_args { int prog_fd; @@ -56,3 +63,55 @@ int attach_prog(struct attach_prog_args *ctx) 0); return 0; } + +struct hid_hw_request_syscall_args { + /* data needs to come at offset 0 so we can do a memcpy into it */ + __u8 data[10]; + unsigned int hid; + int retval; + size_t size; + enum hid_report_type type; + __u8 request_type; +}; + +SEC("syscall") +int hid_user_raw_request(struct hid_hw_request_syscall_args *args) +{ + struct hid_bpf_ctx *ctx; + int i, ret = 0; + __u8 *data; + + ctx = hid_bpf_allocate_context(args->hid); + if (!ctx) + return 0; /* EPERM check */ + + /* We can not use the context data memory directly in the hid_bpf call, + * so we rely on the PTR_TO_MEM allocated in the hid_bpf_context + */ + data = hid_bpf_get_data(ctx, 0 /* offset */, 10 /* size */); + if (!data) + goto out; /* EPERM check */ + + __builtin_memcpy(data, args->data, sizeof(args->data)); + + if (args->size <= sizeof(args->data)) { + ret = hid_bpf_hw_request(ctx, + data, + args->size, + args->type, + args->request_type); + args->retval = ret; + if (ret < 0) + goto out; + } else { + ret = -7; /* -E2BIG */ + goto out; + } + + __builtin_memcpy(args->data, data, sizeof(args->data)); + + out: + hid_bpf_release_context(ctx); + + return ret; +} From patchwork Wed May 18 20:59:20 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12854109 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0D163C433F5 for ; Wed, 18 May 2022 21:01:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S242986AbiERVBT (ORCPT ); Wed, 18 May 2022 17:01:19 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34416 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S243033AbiERVBE (ORCPT ); Wed, 18 May 2022 17:01:04 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 6C3B22573C1 for ; Wed, 18 May 2022 14:00:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1652907628; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=96Aq04wo2PMnxCw2ShL9C8oqiXUq1W07PI3UmbhqwYQ=; b=W2KhmQ1WhNOKJ1LdETclIH8ZxwVtjuhhKf4zCLFThhOr1vNV87ZMUpd7MK3ughtRfgSXg5 9LmH/p35UNlou6DGEMCgGRb7PjNSJ98sLAhlLTW2acocyfyhysNwxU0ygFchMc/7fPHGyw osqSfWwXzA0zwlggUi81xyjZazeu02Y= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-356-tcp4IhB2NqaMwlH1_zABdg-1; Wed, 18 May 2022 17:00:27 -0400 X-MC-Unique: tcp4IhB2NqaMwlH1_zABdg-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 628A6811E81; Wed, 18 May 2022 21:00:26 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.58]) by smtp.corp.redhat.com (Postfix) with ESMTP id 136262166B3F; Wed, 18 May 2022 21:00:22 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v5 13/17] HID: bpf: allow to change the report descriptor Date: Wed, 18 May 2022 22:59:20 +0200 Message-Id: <20220518205924.399291-14-benjamin.tissoires@redhat.com> In-Reply-To: <20220518205924.399291-1-benjamin.tissoires@redhat.com> References: <20220518205924.399291-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Add a new tracepoint hid_bpf_rdesc_fixup() so we can trigger a report descriptor fixup in the bpf world. Whenever the program gets attached/detached, the device is reconnected meaning that userspace will see it disappearing and reappearing with the new report descriptor. Signed-off-by: Benjamin Tissoires --- changes in v5: - adapted for new API not in v4 changes in v3: - ensure the ctx.size is properly bounded by allocated size - s/link_attached/post_link_attach/ - removed the switch statement with only one case changes in v2: - split the series by bpf/libbpf/hid/selftests and samples --- drivers/hid/bpf/entrypoints/entrypoints.bpf.c | 6 + .../hid/bpf/entrypoints/entrypoints.lskel.h | 965 +++++++++--------- drivers/hid/bpf/hid_bpf_dispatch.c | 77 +- drivers/hid/bpf/hid_bpf_dispatch.h | 1 + drivers/hid/bpf/hid_bpf_jmp_table.c | 8 + drivers/hid/hid-core.c | 3 +- include/linux/hid_bpf.h | 8 + 7 files changed, 608 insertions(+), 460 deletions(-) diff --git a/drivers/hid/bpf/entrypoints/entrypoints.bpf.c b/drivers/hid/bpf/entrypoints/entrypoints.bpf.c index edabbafc06b7..95127ace3af4 100644 --- a/drivers/hid/bpf/entrypoints/entrypoints.bpf.c +++ b/drivers/hid/bpf/entrypoints/entrypoints.bpf.c @@ -37,6 +37,12 @@ int BPF_PROG(hid_device_event, struct hid_bpf_ctx *hctx) return 0; } +SEC("fmod_ret/hid_bpf_rdesc_fixup") +int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hctx) +{ + return 0; +} + static void release_prog(u64 prog) { u8 *value; diff --git a/drivers/hid/bpf/entrypoints/entrypoints.lskel.h b/drivers/hid/bpf/entrypoints/entrypoints.lskel.h index 002d4ced9b38..7cf0ef0da038 100644 --- a/drivers/hid/bpf/entrypoints/entrypoints.lskel.h +++ b/drivers/hid/bpf/entrypoints/entrypoints.lskel.h @@ -14,12 +14,14 @@ struct entrypoints_bpf { struct { struct bpf_prog_desc hid_tail_call; struct bpf_prog_desc hid_device_event; + struct bpf_prog_desc hid_rdesc_fixup; struct bpf_prog_desc hid_prog_release; struct bpf_prog_desc hid_free_inode; } progs; struct { int hid_tail_call_fd; int hid_device_event_fd; + int hid_rdesc_fixup_fd; int hid_prog_release_fd; int hid_free_inode_fd; } links; @@ -47,6 +49,17 @@ entrypoints_bpf__hid_device_event__attach(struct entrypoints_bpf *skel) return fd; } +static inline int +entrypoints_bpf__hid_rdesc_fixup__attach(struct entrypoints_bpf *skel) +{ + int prog_fd = skel->progs.hid_rdesc_fixup.prog_fd; + int fd = skel_raw_tracepoint_open(NULL, prog_fd); + + if (fd > 0) + skel->links.hid_rdesc_fixup_fd = fd; + return fd; +} + static inline int entrypoints_bpf__hid_prog_release__attach(struct entrypoints_bpf *skel) { @@ -76,6 +89,7 @@ entrypoints_bpf__attach(struct entrypoints_bpf *skel) ret = ret < 0 ? ret : entrypoints_bpf__hid_tail_call__attach(skel); ret = ret < 0 ? ret : entrypoints_bpf__hid_device_event__attach(skel); + ret = ret < 0 ? ret : entrypoints_bpf__hid_rdesc_fixup__attach(skel); ret = ret < 0 ? ret : entrypoints_bpf__hid_prog_release__attach(skel); ret = ret < 0 ? ret : entrypoints_bpf__hid_free_inode__attach(skel); return ret < 0 ? ret : 0; @@ -86,6 +100,7 @@ entrypoints_bpf__detach(struct entrypoints_bpf *skel) { skel_closenz(skel->links.hid_tail_call_fd); skel_closenz(skel->links.hid_device_event_fd); + skel_closenz(skel->links.hid_rdesc_fixup_fd); skel_closenz(skel->links.hid_prog_release_fd); skel_closenz(skel->links.hid_free_inode_fd); } @@ -97,6 +112,7 @@ entrypoints_bpf__destroy(struct entrypoints_bpf *skel) entrypoints_bpf__detach(skel); skel_closenz(skel->progs.hid_tail_call.prog_fd); skel_closenz(skel->progs.hid_device_event.prog_fd); + skel_closenz(skel->progs.hid_rdesc_fixup.prog_fd); skel_closenz(skel->progs.hid_prog_release.prog_fd); skel_closenz(skel->progs.hid_free_inode.prog_fd); skel_closenz(skel->maps.hid_jmp_table.map_fd); @@ -125,7 +141,7 @@ entrypoints_bpf__load(struct entrypoints_bpf *skel) int err; opts.ctx = (struct bpf_loader_ctx *)skel; - opts.data_sz = 11856; + opts.data_sz = 12208; opts.data = (void *)"\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ @@ -160,7 +176,7 @@ entrypoints_bpf__load(struct entrypoints_bpf *skel) \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\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\x98\x14\0\0\x98\x14\0\0\x5c\x0d\0\0\0\0\0\0\0\0\0\x02\x03\0\ +\x18\0\0\0\0\0\0\0\xb8\x14\0\0\xb8\x14\0\0\xc1\x0d\0\0\0\0\0\0\0\0\0\x02\x03\0\ \0\0\x01\0\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\x01\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\ \0\0\x04\0\0\0\x03\0\0\0\x05\0\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\ \x02\x06\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\0\x04\0\0\0\0\0\0\ @@ -179,164 +195,165 @@ entrypoints_bpf__load(struct entrypoints_bpf *skel) \x05\0\0\x04\x20\0\0\0\x3c\x01\0\0\x1b\0\0\0\0\0\0\0\x42\x01\0\0\x1d\0\0\0\x40\ \0\0\0\x46\x01\0\0\x1b\0\0\0\x80\0\0\0\x55\x01\0\0\x1f\0\0\0\xa0\0\0\0\0\0\0\0\ \x20\0\0\0\xc0\0\0\0\x61\x01\0\0\0\0\0\x08\x1c\0\0\0\x67\x01\0\0\0\0\0\x01\x04\ -\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\x02\x1e\0\0\0\0\0\0\0\0\0\0\x0a\xb7\0\0\0\x74\ +\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\x02\x1e\0\0\0\0\0\0\0\0\0\0\x0a\xb9\0\0\0\x74\ \x01\0\0\x04\0\0\x06\x04\0\0\0\x84\x01\0\0\0\0\0\0\x95\x01\0\0\x01\0\0\0\xa7\ \x01\0\0\x02\0\0\0\xba\x01\0\0\x03\0\0\0\0\0\0\0\x02\0\0\x05\x04\0\0\0\xcb\x01\ \0\0\x21\0\0\0\0\0\0\0\xd2\x01\0\0\x21\0\0\0\0\0\0\0\xd7\x01\0\0\0\0\0\x08\x02\ \0\0\0\0\0\0\0\x01\0\0\x0d\x02\0\0\0\x86\0\0\0\x16\0\0\0\x13\x02\0\0\x01\0\0\ \x0c\x22\0\0\0\0\0\0\0\x01\0\0\x0d\x02\0\0\0\x86\0\0\0\x16\0\0\0\x7b\x02\0\0\ -\x01\0\0\x0c\x24\0\0\0\xea\x02\0\0\x14\0\0\x04\xc8\x01\0\0\xef\x02\0\0\x27\0\0\ -\0\0\0\0\0\xf3\x02\0\0\x2e\0\0\0\x80\0\0\0\xfa\x02\0\0\x31\0\0\0\0\x01\0\0\x02\ -\x03\0\0\x32\0\0\0\x40\x01\0\0\x07\x03\0\0\x34\0\0\0\x80\x01\0\0\x0e\x03\0\0\ -\x5b\0\0\0\x80\x03\0\0\x16\x03\0\0\x1c\0\0\0\xc0\x03\0\0\x1e\x03\0\0\x61\0\0\0\ -\xe0\x03\0\0\x25\x03\0\0\x62\0\0\0\0\x04\0\0\x30\x03\0\0\x65\0\0\0\x80\x08\0\0\ -\x36\x03\0\0\x67\0\0\0\xc0\x08\0\0\x3e\x03\0\0\x75\0\0\0\x80\x0b\0\0\x45\x03\0\ -\0\x77\0\0\0\xc0\x0b\0\0\x4a\x03\0\0\x78\0\0\0\xc0\x0c\0\0\x54\x03\0\0\x10\0\0\ -\0\0\x0d\0\0\x5f\x03\0\0\x10\0\0\0\x40\x0d\0\0\x6c\x03\0\0\x7a\0\0\0\x80\x0d\0\ -\0\x71\x03\0\0\x7b\0\0\0\xc0\x0d\0\0\x7b\x03\0\0\x7c\0\0\0\0\x0e\0\0\x84\x03\0\ -\0\x7c\0\0\0\x20\x0e\0\0\0\0\0\0\x02\0\0\x05\x10\0\0\0\x8d\x03\0\0\x28\0\0\0\0\ -\0\0\0\x96\x03\0\0\x2a\0\0\0\0\0\0\0\xa1\x03\0\0\x01\0\0\x04\x08\0\0\0\xac\x03\ -\0\0\x29\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\x28\0\0\0\xb1\x03\0\0\x02\0\0\x04\x10\ -\0\0\0\xac\x03\0\0\x2b\0\0\0\0\0\0\0\xbf\x03\0\0\x2c\0\0\0\x40\0\0\0\0\0\0\0\0\ -\0\0\x02\x2a\0\0\0\0\0\0\0\0\0\0\x02\x2d\0\0\0\0\0\0\0\x01\0\0\x0d\0\0\0\0\0\0\ -\0\0\x2b\0\0\0\xc4\x03\0\0\x02\0\0\x04\x10\0\0\0\xc9\x03\0\0\x2f\0\0\0\0\0\0\0\ -\xcd\x03\0\0\x30\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\x02\xc0\0\0\0\0\0\0\0\0\0\0\x02\ -\xb3\0\0\0\0\0\0\0\0\0\0\x02\x83\0\0\0\0\0\0\0\0\0\0\x02\x33\0\0\0\0\0\0\0\0\0\ -\0\x0a\xb5\0\0\0\xd4\x03\0\0\0\0\0\x08\x35\0\0\0\xdf\x03\0\0\x01\0\0\x04\x40\0\ -\0\0\0\0\0\0\x36\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\x05\x40\0\0\0\xe8\x03\0\0\x37\0\ -\0\0\0\0\0\0\0\0\0\0\x59\0\0\0\0\0\0\0\xee\x03\0\0\x05\0\0\x04\x40\0\0\0\xfb\ -\x03\0\0\x38\0\0\0\0\0\0\0\x04\x04\0\0\x1c\0\0\0\x20\0\0\0\x0a\x04\0\0\x1c\0\0\ -\0\x40\0\0\0\x14\x04\0\0\x10\0\0\0\x80\0\0\0\x1a\x04\0\0\x43\0\0\0\xc0\0\0\0\ -\x22\x04\0\0\0\0\0\x08\x39\0\0\0\x32\x04\0\0\x01\0\0\x04\x04\0\0\0\0\0\0\0\x3a\ -\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\x05\x04\0\0\0\x3c\x04\0\0\x3b\0\0\0\0\0\0\0\0\0\ -\0\0\x3d\0\0\0\0\0\0\0\0\0\0\0\x3f\0\0\0\0\0\0\0\x40\x04\0\0\0\0\0\x08\x3c\0\0\ -\0\0\0\0\0\x01\0\0\x04\x04\0\0\0\x49\x04\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\ -\x04\x02\0\0\0\x51\x04\0\0\x3e\0\0\0\0\0\0\0\x58\x04\0\0\x3e\0\0\0\x08\0\0\0\ -\x60\x04\0\0\0\0\0\x08\x12\0\0\0\0\0\0\0\x02\0\0\x04\x04\0\0\0\x63\x04\0\0\x40\ -\0\0\0\0\0\0\0\x72\x04\0\0\x40\0\0\0\x10\0\0\0\x77\x04\0\0\0\0\0\x08\x41\0\0\0\ -\x7b\x04\0\0\0\0\0\x08\x42\0\0\0\x81\x04\0\0\0\0\0\x01\x02\0\0\0\x10\0\0\0\x90\ -\x04\0\0\x06\0\0\x04\x28\0\0\0\x5f\0\0\0\x44\0\0\0\0\0\0\0\x9c\x04\0\0\x58\0\0\ -\0\x40\0\0\0\xa8\x04\0\0\x55\0\0\0\xc0\0\0\0\xad\x04\0\0\x3e\0\0\0\0\x01\0\0\ -\xbd\x04\0\0\x3e\0\0\0\x08\x01\0\0\xcd\x04\0\0\x3e\0\0\0\x10\x01\0\0\0\0\0\0\0\ -\0\0\x02\xb9\0\0\0\0\0\0\0\0\0\0\x02\x46\0\0\0\xd7\x04\0\0\x0e\0\0\x04\xc0\0\0\ -\0\xe2\x04\0\0\x47\0\0\0\0\0\0\0\xed\x04\0\0\x4a\0\0\0\x80\0\0\0\xf8\x04\0\0\ -\x4a\0\0\0\0\x01\0\0\x04\x05\0\0\x4a\0\0\0\x80\x01\0\0\x5f\0\0\0\x4c\0\0\0\0\ -\x02\0\0\x11\x05\0\0\x1c\0\0\0\x40\x02\0\0\x1a\x05\0\0\x1c\0\0\0\x60\x02\0\0\ -\x25\x05\0\0\x4e\0\0\0\x80\x02\0\0\x30\x05\0\0\x54\0\0\0\xc0\x02\0\0\x3d\x05\0\ -\0\x02\0\0\0\x40\x05\0\0\xa8\x04\0\0\x55\0\0\0\x80\x05\0\0\xbd\x04\0\0\x3e\0\0\ -\0\xc0\x05\0\0\xad\x04\0\0\x3e\0\0\0\xc8\x05\0\0\xcd\x04\0\0\x3e\0\0\0\xd0\x05\ -\0\0\x4a\x05\0\0\x02\0\0\x04\x10\0\0\0\xac\x03\0\0\x48\0\0\0\0\0\0\0\x55\x05\0\ -\0\x49\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\x02\x47\0\0\0\0\0\0\0\0\0\0\x02\x48\0\0\0\ -\x5b\x05\0\0\x02\0\0\x04\x10\0\0\0\xac\x03\0\0\x4b\0\0\0\0\0\0\0\x65\x05\0\0\ -\x4b\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\x02\x4a\0\0\0\0\0\0\0\0\0\0\x02\x4d\0\0\0\0\ -\0\0\0\0\0\0\x0a\xba\0\0\0\x6a\x05\0\0\0\0\0\x01\x08\0\0\0\x40\0\0\0\0\0\0\0\0\ -\0\0\x02\x50\0\0\0\0\0\0\0\0\0\0\x0a\x51\0\0\0\x78\x05\0\0\x04\0\0\x04\x18\0\0\ -\0\xe2\x04\0\0\x47\0\0\0\0\0\0\0\x83\x05\0\0\x52\0\0\0\x80\0\0\0\x88\x05\0\0\ -\x52\0\0\0\xa0\0\0\0\x93\x05\0\0\x53\0\0\0\xc0\0\0\0\x9b\x05\0\0\0\0\0\x08\x1b\ -\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x4e\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\ -\0\0\0\0\x4f\0\0\0\x04\0\0\0\x0a\0\0\0\0\0\0\0\0\0\0\x02\x56\0\0\0\0\0\0\0\0\0\ -\0\x0a\x57\0\0\0\x9f\x05\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\x45\0\0\0\x04\0\0\0\x02\0\0\0\0\0\0\0\x02\0\0\x04\x40\0\0\0\xa4\x05\0\ -\0\x5a\0\0\0\0\0\0\0\x1a\x04\0\0\x43\0\0\0\xc0\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\ -\x3e\0\0\0\x04\0\0\0\x18\0\0\0\xae\x05\0\0\0\0\0\x08\x5c\0\0\0\xbc\x05\0\0\0\0\ -\0\x08\x5d\0\0\0\0\0\0\0\x01\0\0\x04\x08\0\0\0\x49\x04\0\0\x5e\0\0\0\0\0\0\0\ -\xc7\x05\0\0\0\0\0\x08\x5f\0\0\0\xcb\x05\0\0\0\0\0\x08\x60\0\0\0\xd1\x05\0\0\0\ -\0\0\x01\x08\0\0\0\x40\0\0\x01\xdb\x05\0\0\0\0\0\x08\x1c\0\0\0\xe3\x05\0\0\x06\ -\0\0\x04\x90\0\0\0\x14\x04\0\0\x5b\0\0\0\0\0\0\0\xe9\x05\0\0\x63\0\0\0\x40\0\0\ -\0\xf3\x05\0\0\x64\0\0\0\x40\x02\0\0\xf7\x05\0\0\x4a\0\0\0\x80\x02\0\0\x04\x04\ -\0\0\x10\0\0\0\0\x03\0\0\x1a\x04\0\0\x43\0\0\0\x40\x03\0\0\x01\x06\0\0\0\0\0\ -\x08\x37\0\0\0\x10\x06\0\0\x01\0\0\x04\x04\0\0\0\x72\x04\0\0\x3b\0\0\0\0\0\0\0\ -\x26\x06\0\0\0\0\0\x08\x66\0\0\0\x2d\x06\0\0\0\0\0\x08\x60\0\0\0\x3d\x06\0\0\ -\x06\0\0\x04\x58\0\0\0\x49\x06\0\0\x68\0\0\0\0\0\0\0\x4e\x06\0\0\x6f\0\0\0\0\ -\x02\0\0\x52\x06\0\0\x70\0\0\0\x40\x02\0\0\x5b\x06\0\0\x71\0\0\0\x60\x02\0\0\ -\x5f\x06\0\0\x71\0\0\0\x80\x02\0\0\x64\x06\0\0\x02\0\0\0\xa0\x02\0\0\x6b\x06\0\ -\0\0\0\0\x08\x69\0\0\0\0\0\0\0\x05\0\0\x04\x40\0\0\0\xfb\x03\0\0\x6a\0\0\0\0\0\ -\0\0\x04\x04\0\0\x1c\0\0\0\x40\0\0\0\x0a\x04\0\0\x1c\0\0\0\x60\0\0\0\x14\x04\0\ -\0\x10\0\0\0\x80\0\0\0\x1a\x04\0\0\x43\0\0\0\xc0\0\0\0\x74\x06\0\0\0\0\0\x08\ -\x6b\0\0\0\x82\x06\0\0\x02\0\0\x04\x08\0\0\0\0\0\0\0\x6c\0\0\0\0\0\0\0\xe9\x05\ -\0\0\x38\0\0\0\x20\0\0\0\0\0\0\0\x02\0\0\x05\x04\0\0\0\x8a\x06\0\0\x3b\0\0\0\0\ -\0\0\0\0\0\0\0\x6d\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\x04\x04\0\0\0\x8f\x06\0\0\x3e\ -\0\0\0\0\0\0\0\x97\x06\0\0\x6e\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x3e\0\ -\0\0\x04\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\x02\xbb\0\0\0\x52\x06\0\0\x05\0\0\x06\ -\x04\0\0\0\xa0\x06\0\0\0\0\0\0\xac\x06\0\0\x01\0\0\0\xb9\x06\0\0\x02\0\0\0\xc6\ -\x06\0\0\x03\0\0\0\xd2\x06\0\0\x04\0\0\0\xde\x06\0\0\0\0\0\x08\x72\0\0\0\0\0\0\ -\0\x01\0\0\x04\x04\0\0\0\x3c\x04\0\0\x73\0\0\0\0\0\0\0\xe5\x06\0\0\0\0\0\x08\ -\x74\0\0\0\xeb\x06\0\0\0\0\0\x08\x1c\0\0\0\0\0\0\0\0\0\0\x02\x76\0\0\0\0\0\0\0\ -\0\0\0\x0a\xb2\0\0\0\xfc\x06\0\0\x06\0\0\x04\x20\0\0\0\x0a\x07\0\0\x4e\0\0\0\0\ -\0\0\0\xd2\x01\0\0\x1c\0\0\0\x40\0\0\0\x10\x07\0\0\x1c\0\0\0\x60\0\0\0\x1b\x07\ -\0\0\x1c\0\0\0\x80\0\0\0\x24\x07\0\0\x1c\0\0\0\xa0\0\0\0\x2e\x07\0\0\x65\0\0\0\ -\xc0\0\0\0\x37\x07\0\0\0\0\0\x08\x79\0\0\0\x3b\x07\0\0\0\0\0\x08\x17\0\0\0\0\0\ -\0\0\0\0\0\x02\x98\0\0\0\0\0\0\0\0\0\0\x02\x9d\0\0\0\x41\x07\0\0\0\0\0\x08\x52\ -\0\0\0\0\0\0\0\x02\0\0\x0d\x7e\0\0\0\x51\x0d\0\0\x78\0\0\0\x51\x0d\0\0\x02\0\0\ -\0\x0a\x08\0\0\0\0\0\x08\x7f\0\0\0\x0f\x08\0\0\0\0\0\x01\x01\0\0\0\x08\0\0\x04\ -\x15\x08\0\0\x01\0\0\x0c\x7d\0\0\0\0\0\0\0\x01\0\0\x0d\x02\0\0\0\x86\0\0\0\x16\ -\0\0\0\x2f\x08\0\0\x01\0\0\x0c\x81\0\0\0\x85\x08\0\0\x34\0\0\x04\x78\x04\0\0\ -\x8b\x08\0\0\x84\0\0\0\0\0\0\0\x92\x08\0\0\x42\0\0\0\x10\0\0\0\x9c\x08\0\0\x71\ -\0\0\0\x20\0\0\0\xa2\x08\0\0\x85\0\0\0\x40\0\0\0\xa8\x08\0\0\x1c\0\0\0\x60\0\0\ -\0\xb0\x08\0\0\x89\0\0\0\x80\0\0\0\xb6\x08\0\0\x89\0\0\0\xc0\0\0\0\xc4\x08\0\0\ -\x8a\0\0\0\0\x01\0\0\xc9\x08\0\0\x8c\0\0\0\x40\x01\0\0\xce\x08\0\0\x7b\0\0\0\ -\x80\x01\0\0\xd8\x08\0\0\x10\0\0\0\xc0\x01\0\0\xe3\x08\0\0\x4e\0\0\0\0\x02\0\0\ -\0\0\0\0\x8d\0\0\0\x40\x02\0\0\xe9\x08\0\0\x8f\0\0\0\x60\x02\0\0\xf0\x08\0\0\ -\x65\0\0\0\x80\x02\0\0\xf7\x08\0\0\x91\0\0\0\xc0\x02\0\0\xff\x08\0\0\x91\0\0\0\ -\x40\x03\0\0\x07\x09\0\0\x91\0\0\0\xc0\x03\0\0\x0f\x09\0\0\x34\0\0\0\x40\x04\0\ -\0\x16\x09\0\0\x42\0\0\0\x40\x06\0\0\x1e\x09\0\0\x3e\0\0\0\x50\x06\0\0\x28\x09\ -\0\0\x3e\0\0\0\x58\x06\0\0\x35\x09\0\0\x94\0\0\0\x80\x06\0\0\x3e\x09\0\0\x4e\0\ -\0\0\xc0\x06\0\0\x46\x09\0\0\x95\0\0\0\0\x07\0\0\x4e\x09\0\0\x4e\0\0\0\xc0\x0b\ -\0\0\x5b\x09\0\0\x4e\0\0\0\0\x0c\0\0\x6d\x09\0\0\x47\0\0\0\x40\x0c\0\0\x74\x09\ -\0\0\x4a\0\0\0\xc0\x0c\0\0\x7e\x09\0\0\x96\0\0\0\x40\x0d\0\0\x83\x09\0\0\x02\0\ -\0\0\x80\x0d\0\0\x93\x09\0\0\x40\0\0\0\xa0\x0d\0\0\xa5\x09\0\0\x40\0\0\0\xb0\ -\x0d\0\0\xb6\x09\0\0\x4a\0\0\0\xc0\x0d\0\0\xbc\x09\0\0\x4a\0\0\0\x40\x0e\0\0\ -\xc6\x09\0\0\x4a\0\0\0\xc0\x0e\0\0\0\0\0\0\x97\0\0\0\x40\x0f\0\0\xd0\x09\0\0\ -\x5c\0\0\0\xc0\x0f\0\0\xda\x09\0\0\x5c\0\0\0\0\x10\0\0\xe5\x09\0\0\x3b\0\0\0\ -\x40\x10\0\0\xed\x09\0\0\x3b\0\0\0\x60\x10\0\0\xf9\x09\0\0\x3b\0\0\0\x80\x10\0\ -\0\x06\x0a\0\0\x3b\0\0\0\xa0\x10\0\0\0\0\0\0\x99\0\0\0\xc0\x10\0\0\x12\x0a\0\0\ -\x9c\0\0\0\0\x11\0\0\x1a\x0a\0\0\x9d\0\0\0\x40\x11\0\0\x21\x0a\0\0\x4a\0\0\0\ -\x40\x22\0\0\0\0\0\0\xa5\0\0\0\xc0\x22\0\0\x2b\x0a\0\0\x1b\0\0\0\0\x23\0\0\x38\ -\x0a\0\0\x1b\0\0\0\x20\x23\0\0\x48\x0a\0\0\xa9\0\0\0\x40\x23\0\0\x59\x0a\0\0\ -\x10\0\0\0\x80\x23\0\0\x63\x0a\0\0\0\0\0\x08\x42\0\0\0\x6b\x0a\0\0\0\0\0\x08\ -\x86\0\0\0\0\0\0\0\x01\0\0\x04\x04\0\0\0\x3c\x04\0\0\x87\0\0\0\0\0\0\0\x72\x0a\ -\0\0\0\0\0\x08\x88\0\0\0\x78\x0a\0\0\0\0\0\x08\x1c\0\0\0\0\0\0\0\0\0\0\x02\xbd\ -\0\0\0\0\0\0\0\0\0\0\x02\x8b\0\0\0\0\0\0\0\0\0\0\x0a\xb8\0\0\0\0\0\0\0\0\0\0\ -\x02\xbf\0\0\0\0\0\0\0\x02\0\0\x05\x04\0\0\0\x89\x0a\0\0\x8e\0\0\0\0\0\0\0\x91\ -\x0a\0\0\x1c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\x1c\0\0\0\x9b\x0a\0\0\0\0\0\x08\ -\x90\0\0\0\xa1\x0a\0\0\0\0\0\x08\x52\0\0\0\xb0\x0a\0\0\x02\0\0\x04\x10\0\0\0\ -\xbb\x0a\0\0\x92\0\0\0\0\0\0\0\xc2\x0a\0\0\x93\0\0\0\x40\0\0\0\xca\x0a\0\0\0\0\ -\0\x08\x5f\0\0\0\xd3\x0a\0\0\0\0\0\x01\x08\0\0\0\x40\0\0\x01\xd8\x0a\0\0\0\0\0\ -\x08\x78\0\0\0\xe1\x0a\0\0\x07\0\0\x04\x98\0\0\0\xee\x0a\0\0\x5b\0\0\0\0\0\0\0\ -\x14\x04\0\0\x5b\0\0\0\x40\0\0\0\xf3\x05\0\0\x64\0\0\0\x80\0\0\0\xe9\x05\0\0\ -\x63\0\0\0\xc0\0\0\0\xf7\x05\0\0\x4a\0\0\0\xc0\x02\0\0\x04\x04\0\0\x10\0\0\0\ -\x40\x03\0\0\x1a\x04\0\0\x43\0\0\0\x80\x03\0\0\0\0\0\0\0\0\0\x02\xb0\0\0\0\0\0\ -\0\0\x02\0\0\x05\x10\0\0\0\xf4\x0a\0\0\x98\0\0\0\0\0\0\0\xfd\x0a\0\0\x2a\0\0\0\ -\0\0\0\0\x03\x0b\0\0\x01\0\0\x04\x08\0\0\0\x0e\x0b\0\0\x48\0\0\0\0\0\0\0\0\0\0\ -\0\x02\0\0\x05\x08\0\0\0\x14\x0b\0\0\x32\0\0\0\0\0\0\0\x1a\x0b\0\0\x9a\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\x02\x9b\0\0\0\0\0\0\0\x01\0\0\x0d\0\0\0\0\0\0\0\0\x31\0\0\ -\0\0\0\0\0\0\0\0\x02\xb4\0\0\0\x25\x0b\0\0\x0f\0\0\x04\x20\x02\0\0\x33\x0b\0\0\ -\x31\0\0\0\0\0\0\0\x38\x0b\0\0\x9e\0\0\0\x40\0\0\0\x40\x0b\0\0\x95\0\0\0\xc0\ -\x02\0\0\x50\x0b\0\0\x9f\0\0\0\x80\x07\0\0\x59\x0b\0\0\x3b\0\0\0\xa0\x07\0\0\ -\x69\x0b\0\0\xa0\0\0\0\xc0\x07\0\0\x70\x0b\0\0\x95\0\0\0\x40\x08\0\0\x7d\x0b\0\ -\0\x4e\0\0\0\0\x0d\0\0\x85\x0b\0\0\x4e\0\0\0\x40\x0d\0\0\x95\x0b\0\0\xa3\0\0\0\ -\x80\x0d\0\0\x9b\x0b\0\0\x4e\0\0\0\xc0\x0d\0\0\xa1\x0b\0\0\x7c\0\0\0\0\x0e\0\0\ -\xa8\x0b\0\0\x34\0\0\0\x40\x0e\0\0\xb5\x0b\0\0\x4a\0\0\0\x40\x10\0\0\x5f\x03\0\ -\0\x10\0\0\0\xc0\x10\0\0\xc2\x0b\0\0\x03\0\0\x04\x50\0\0\0\xc9\x0b\0\0\x34\0\0\ -\0\0\0\0\0\xd1\x0b\0\0\x9f\0\0\0\0\x02\0\0\xda\x0b\0\0\x10\0\0\0\x40\x02\0\0\ -\xe2\x0b\0\0\0\0\0\x08\x1c\0\0\0\xe8\x0b\0\0\x02\0\0\x04\x10\0\0\0\xf7\x0b\0\0\ -\xa1\0\0\0\0\0\0\0\xff\x0b\0\0\xa2\0\0\0\x40\0\0\0\xf7\x0b\0\0\x01\0\0\x04\x08\ -\0\0\0\x0b\x0c\0\0\xa2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\xbe\0\0\0\0\0\0\0\0\0\0\ -\x02\xa4\0\0\0\0\0\0\0\0\0\0\x0a\xaf\0\0\0\0\0\0\0\x04\0\0\x05\x08\0\0\0\x13\ -\x0c\0\0\xa6\0\0\0\0\0\0\0\x1a\x0c\0\0\xa7\0\0\0\0\0\0\0\x21\x0c\0\0\xa8\0\0\0\ -\0\0\0\0\x28\x0c\0\0\x1c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\xbc\0\0\0\0\0\0\0\0\0\ -\0\x02\xb1\0\0\0\0\0\0\0\0\0\0\x02\x57\0\0\0\0\0\0\0\0\0\0\x02\xb6\0\0\0\0\0\0\ -\0\0\0\0\x03\0\0\0\0\x57\0\0\0\x04\0\0\0\x04\0\0\0\x5a\x0c\0\0\0\0\0\x0e\xaa\0\ -\0\0\x01\0\0\0\x62\x0c\0\0\x01\0\0\x0f\x04\0\0\0\xc1\0\0\0\0\0\0\0\x04\0\0\0\ -\x69\x0c\0\0\x02\0\0\x0f\x40\0\0\0\x0a\0\0\0\0\0\0\0\x20\0\0\0\x15\0\0\0\x20\0\ -\0\0\x20\0\0\0\x6f\x0c\0\0\x01\0\0\x0f\x04\0\0\0\xab\0\0\0\0\0\0\0\x04\0\0\0\ -\x77\x0c\0\0\0\0\0\x07\0\0\0\0\x90\x0c\0\0\0\0\0\x07\0\0\0\0\x9e\x0c\0\0\0\0\0\ -\x07\0\0\0\0\xa3\x0c\0\0\0\0\0\x07\0\0\0\0\xcd\x03\0\0\0\0\0\x07\0\0\0\0\xa8\ -\x0c\0\0\0\0\0\x07\0\0\0\0\xba\x0c\0\0\0\0\0\x07\0\0\0\0\xca\x0c\0\0\0\0\0\x07\ -\0\0\0\0\xe2\x0c\0\0\0\0\0\x07\0\0\0\0\xed\x0c\0\0\0\0\0\x07\0\0\0\0\xfe\x0c\0\ -\0\0\0\0\x07\0\0\0\0\x0d\x0d\0\0\0\0\0\x07\0\0\0\0\x4e\x06\0\0\0\0\0\x07\0\0\0\ -\0\x22\x0d\0\0\0\0\0\x07\0\0\0\0\x32\x0d\0\0\0\0\0\x07\0\0\0\0\x0b\x0c\0\0\0\0\ -\0\x07\0\0\0\0\x3c\x0d\0\0\0\0\0\x07\0\0\0\0\x48\x0d\0\0\0\0\0\x07\0\0\0\0\x51\ +\x01\0\0\x0c\x24\0\0\0\0\0\0\0\x01\0\0\x0d\x02\0\0\0\x86\0\0\0\x16\0\0\0\xe0\ +\x02\0\0\x01\0\0\x0c\x26\0\0\0\x4f\x03\0\0\x14\0\0\x04\xc8\x01\0\0\x54\x03\0\0\ +\x29\0\0\0\0\0\0\0\x58\x03\0\0\x30\0\0\0\x80\0\0\0\x5f\x03\0\0\x33\0\0\0\0\x01\ +\0\0\x67\x03\0\0\x34\0\0\0\x40\x01\0\0\x6c\x03\0\0\x36\0\0\0\x80\x01\0\0\x73\ +\x03\0\0\x5d\0\0\0\x80\x03\0\0\x7b\x03\0\0\x1c\0\0\0\xc0\x03\0\0\x83\x03\0\0\ +\x63\0\0\0\xe0\x03\0\0\x8a\x03\0\0\x64\0\0\0\0\x04\0\0\x95\x03\0\0\x67\0\0\0\ +\x80\x08\0\0\x9b\x03\0\0\x69\0\0\0\xc0\x08\0\0\xa3\x03\0\0\x77\0\0\0\x80\x0b\0\ +\0\xaa\x03\0\0\x79\0\0\0\xc0\x0b\0\0\xaf\x03\0\0\x7a\0\0\0\xc0\x0c\0\0\xb9\x03\ +\0\0\x10\0\0\0\0\x0d\0\0\xc4\x03\0\0\x10\0\0\0\x40\x0d\0\0\xd1\x03\0\0\x7c\0\0\ +\0\x80\x0d\0\0\xd6\x03\0\0\x7d\0\0\0\xc0\x0d\0\0\xe0\x03\0\0\x7e\0\0\0\0\x0e\0\ +\0\xe9\x03\0\0\x7e\0\0\0\x20\x0e\0\0\0\0\0\0\x02\0\0\x05\x10\0\0\0\xf2\x03\0\0\ +\x2a\0\0\0\0\0\0\0\xfb\x03\0\0\x2c\0\0\0\0\0\0\0\x06\x04\0\0\x01\0\0\x04\x08\0\ +\0\0\x11\x04\0\0\x2b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\x2a\0\0\0\x16\x04\0\0\x02\ +\0\0\x04\x10\0\0\0\x11\x04\0\0\x2d\0\0\0\0\0\0\0\x24\x04\0\0\x2e\0\0\0\x40\0\0\ +\0\0\0\0\0\0\0\0\x02\x2c\0\0\0\0\0\0\0\0\0\0\x02\x2f\0\0\0\0\0\0\0\x01\0\0\x0d\ +\0\0\0\0\0\0\0\0\x2d\0\0\0\x29\x04\0\0\x02\0\0\x04\x10\0\0\0\x2e\x04\0\0\x31\0\ +\0\0\0\0\0\0\x32\x04\0\0\x32\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\x02\xc2\0\0\0\0\0\0\ +\0\0\0\0\x02\xb5\0\0\0\0\0\0\0\0\0\0\x02\x85\0\0\0\0\0\0\0\0\0\0\x02\x35\0\0\0\ +\0\0\0\0\0\0\0\x0a\xb7\0\0\0\x39\x04\0\0\0\0\0\x08\x37\0\0\0\x44\x04\0\0\x01\0\ +\0\x04\x40\0\0\0\0\0\0\0\x38\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\x05\x40\0\0\0\x4d\ +\x04\0\0\x39\0\0\0\0\0\0\0\0\0\0\0\x5b\0\0\0\0\0\0\0\x53\x04\0\0\x05\0\0\x04\ +\x40\0\0\0\x60\x04\0\0\x3a\0\0\0\0\0\0\0\x69\x04\0\0\x1c\0\0\0\x20\0\0\0\x6f\ +\x04\0\0\x1c\0\0\0\x40\0\0\0\x79\x04\0\0\x10\0\0\0\x80\0\0\0\x7f\x04\0\0\x45\0\ +\0\0\xc0\0\0\0\x87\x04\0\0\0\0\0\x08\x3b\0\0\0\x97\x04\0\0\x01\0\0\x04\x04\0\0\ +\0\0\0\0\0\x3c\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\x05\x04\0\0\0\xa1\x04\0\0\x3d\0\0\ +\0\0\0\0\0\0\0\0\0\x3f\0\0\0\0\0\0\0\0\0\0\0\x41\0\0\0\0\0\0\0\xa5\x04\0\0\0\0\ +\0\x08\x3e\0\0\0\0\0\0\0\x01\0\0\x04\x04\0\0\0\xae\x04\0\0\x02\0\0\0\0\0\0\0\0\ +\0\0\0\x02\0\0\x04\x02\0\0\0\xb6\x04\0\0\x40\0\0\0\0\0\0\0\xbd\x04\0\0\x40\0\0\ +\0\x08\0\0\0\xc5\x04\0\0\0\0\0\x08\x12\0\0\0\0\0\0\0\x02\0\0\x04\x04\0\0\0\xc8\ +\x04\0\0\x42\0\0\0\0\0\0\0\xd7\x04\0\0\x42\0\0\0\x10\0\0\0\xdc\x04\0\0\0\0\0\ +\x08\x43\0\0\0\xe0\x04\0\0\0\0\0\x08\x44\0\0\0\xe6\x04\0\0\0\0\0\x01\x02\0\0\0\ +\x10\0\0\0\xf5\x04\0\0\x06\0\0\x04\x28\0\0\0\x5f\0\0\0\x46\0\0\0\0\0\0\0\x01\ +\x05\0\0\x5a\0\0\0\x40\0\0\0\x0d\x05\0\0\x57\0\0\0\xc0\0\0\0\x12\x05\0\0\x40\0\ +\0\0\0\x01\0\0\x22\x05\0\0\x40\0\0\0\x08\x01\0\0\x32\x05\0\0\x40\0\0\0\x10\x01\ +\0\0\0\0\0\0\0\0\0\x02\xbb\0\0\0\0\0\0\0\0\0\0\x02\x48\0\0\0\x3c\x05\0\0\x0e\0\ +\0\x04\xc0\0\0\0\x47\x05\0\0\x49\0\0\0\0\0\0\0\x52\x05\0\0\x4c\0\0\0\x80\0\0\0\ +\x5d\x05\0\0\x4c\0\0\0\0\x01\0\0\x69\x05\0\0\x4c\0\0\0\x80\x01\0\0\x5f\0\0\0\ +\x4e\0\0\0\0\x02\0\0\x76\x05\0\0\x1c\0\0\0\x40\x02\0\0\x7f\x05\0\0\x1c\0\0\0\ +\x60\x02\0\0\x8a\x05\0\0\x50\0\0\0\x80\x02\0\0\x95\x05\0\0\x56\0\0\0\xc0\x02\0\ +\0\xa2\x05\0\0\x02\0\0\0\x40\x05\0\0\x0d\x05\0\0\x57\0\0\0\x80\x05\0\0\x22\x05\ +\0\0\x40\0\0\0\xc0\x05\0\0\x12\x05\0\0\x40\0\0\0\xc8\x05\0\0\x32\x05\0\0\x40\0\ +\0\0\xd0\x05\0\0\xaf\x05\0\0\x02\0\0\x04\x10\0\0\0\x11\x04\0\0\x4a\0\0\0\0\0\0\ +\0\xba\x05\0\0\x4b\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\x02\x49\0\0\0\0\0\0\0\0\0\0\ +\x02\x4a\0\0\0\xc0\x05\0\0\x02\0\0\x04\x10\0\0\0\x11\x04\0\0\x4d\0\0\0\0\0\0\0\ +\xca\x05\0\0\x4d\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\x02\x4c\0\0\0\0\0\0\0\0\0\0\x02\ +\x4f\0\0\0\0\0\0\0\0\0\0\x0a\xbc\0\0\0\xcf\x05\0\0\0\0\0\x01\x08\0\0\0\x40\0\0\ +\0\0\0\0\0\0\0\0\x02\x52\0\0\0\0\0\0\0\0\0\0\x0a\x53\0\0\0\xdd\x05\0\0\x04\0\0\ +\x04\x18\0\0\0\x47\x05\0\0\x49\0\0\0\0\0\0\0\xe8\x05\0\0\x54\0\0\0\x80\0\0\0\ +\xed\x05\0\0\x54\0\0\0\xa0\0\0\0\xf8\x05\0\0\x55\0\0\0\xc0\0\0\0\0\x06\0\0\0\0\ +\0\x08\x1b\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x50\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\x03\0\0\0\0\x51\0\0\0\x04\0\0\0\x0a\0\0\0\0\0\0\0\0\0\0\x02\x58\0\0\0\0\ +\0\0\0\0\0\0\x0a\x59\0\0\0\x04\x06\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\x47\0\0\0\x04\0\0\0\x02\0\0\0\0\0\0\0\x02\0\0\x04\x40\0\0\0\ +\x09\x06\0\0\x5c\0\0\0\0\0\0\0\x7f\x04\0\0\x45\0\0\0\xc0\0\0\0\0\0\0\0\0\0\0\ +\x03\0\0\0\0\x40\0\0\0\x04\0\0\0\x18\0\0\0\x13\x06\0\0\0\0\0\x08\x5e\0\0\0\x21\ +\x06\0\0\0\0\0\x08\x5f\0\0\0\0\0\0\0\x01\0\0\x04\x08\0\0\0\xae\x04\0\0\x60\0\0\ +\0\0\0\0\0\x2c\x06\0\0\0\0\0\x08\x61\0\0\0\x30\x06\0\0\0\0\0\x08\x62\0\0\0\x36\ +\x06\0\0\0\0\0\x01\x08\0\0\0\x40\0\0\x01\x40\x06\0\0\0\0\0\x08\x1c\0\0\0\x48\ +\x06\0\0\x06\0\0\x04\x90\0\0\0\x79\x04\0\0\x5d\0\0\0\0\0\0\0\x4e\x06\0\0\x65\0\ +\0\0\x40\0\0\0\x58\x06\0\0\x66\0\0\0\x40\x02\0\0\x5c\x06\0\0\x4c\0\0\0\x80\x02\ +\0\0\x69\x04\0\0\x10\0\0\0\0\x03\0\0\x7f\x04\0\0\x45\0\0\0\x40\x03\0\0\x66\x06\ +\0\0\0\0\0\x08\x39\0\0\0\x75\x06\0\0\x01\0\0\x04\x04\0\0\0\xd7\x04\0\0\x3d\0\0\ +\0\0\0\0\0\x8b\x06\0\0\0\0\0\x08\x68\0\0\0\x92\x06\0\0\0\0\0\x08\x62\0\0\0\xa2\ +\x06\0\0\x06\0\0\x04\x58\0\0\0\xae\x06\0\0\x6a\0\0\0\0\0\0\0\xb3\x06\0\0\x71\0\ +\0\0\0\x02\0\0\xb7\x06\0\0\x72\0\0\0\x40\x02\0\0\xc0\x06\0\0\x73\0\0\0\x60\x02\ +\0\0\xc4\x06\0\0\x73\0\0\0\x80\x02\0\0\xc9\x06\0\0\x02\0\0\0\xa0\x02\0\0\xd0\ +\x06\0\0\0\0\0\x08\x6b\0\0\0\0\0\0\0\x05\0\0\x04\x40\0\0\0\x60\x04\0\0\x6c\0\0\ +\0\0\0\0\0\x69\x04\0\0\x1c\0\0\0\x40\0\0\0\x6f\x04\0\0\x1c\0\0\0\x60\0\0\0\x79\ +\x04\0\0\x10\0\0\0\x80\0\0\0\x7f\x04\0\0\x45\0\0\0\xc0\0\0\0\xd9\x06\0\0\0\0\0\ +\x08\x6d\0\0\0\xe7\x06\0\0\x02\0\0\x04\x08\0\0\0\0\0\0\0\x6e\0\0\0\0\0\0\0\x4e\ +\x06\0\0\x3a\0\0\0\x20\0\0\0\0\0\0\0\x02\0\0\x05\x04\0\0\0\xef\x06\0\0\x3d\0\0\ +\0\0\0\0\0\0\0\0\0\x6f\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\x04\x04\0\0\0\xf4\x06\0\0\ +\x40\0\0\0\0\0\0\0\xfc\x06\0\0\x70\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\ +\x40\0\0\0\x04\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\x02\xbd\0\0\0\xb7\x06\0\0\x05\0\0\ +\x06\x04\0\0\0\x05\x07\0\0\0\0\0\0\x11\x07\0\0\x01\0\0\0\x1e\x07\0\0\x02\0\0\0\ +\x2b\x07\0\0\x03\0\0\0\x37\x07\0\0\x04\0\0\0\x43\x07\0\0\0\0\0\x08\x74\0\0\0\0\ +\0\0\0\x01\0\0\x04\x04\0\0\0\xa1\x04\0\0\x75\0\0\0\0\0\0\0\x4a\x07\0\0\0\0\0\ +\x08\x76\0\0\0\x50\x07\0\0\0\0\0\x08\x1c\0\0\0\0\0\0\0\0\0\0\x02\x78\0\0\0\0\0\ +\0\0\0\0\0\x0a\xb4\0\0\0\x61\x07\0\0\x06\0\0\x04\x20\0\0\0\x6f\x07\0\0\x50\0\0\ +\0\0\0\0\0\xd2\x01\0\0\x1c\0\0\0\x40\0\0\0\x75\x07\0\0\x1c\0\0\0\x60\0\0\0\x80\ +\x07\0\0\x1c\0\0\0\x80\0\0\0\x89\x07\0\0\x1c\0\0\0\xa0\0\0\0\x93\x07\0\0\x67\0\ +\0\0\xc0\0\0\0\x9c\x07\0\0\0\0\0\x08\x7b\0\0\0\xa0\x07\0\0\0\0\0\x08\x17\0\0\0\ +\0\0\0\0\0\0\0\x02\x9a\0\0\0\0\0\0\0\0\0\0\x02\x9f\0\0\0\xa6\x07\0\0\0\0\0\x08\ +\x54\0\0\0\0\0\0\0\x02\0\0\x0d\x80\0\0\0\xb6\x0d\0\0\x7a\0\0\0\xb6\x0d\0\0\x02\ +\0\0\0\x6f\x08\0\0\0\0\0\x08\x81\0\0\0\x74\x08\0\0\0\0\0\x01\x01\0\0\0\x08\0\0\ +\x04\x7a\x08\0\0\x01\0\0\x0c\x7f\0\0\0\0\0\0\0\x01\0\0\x0d\x02\0\0\0\x86\0\0\0\ +\x16\0\0\0\x94\x08\0\0\x01\0\0\x0c\x83\0\0\0\xea\x08\0\0\x34\0\0\x04\x78\x04\0\ +\0\xf0\x08\0\0\x86\0\0\0\0\0\0\0\xf7\x08\0\0\x44\0\0\0\x10\0\0\0\x01\x09\0\0\ +\x73\0\0\0\x20\0\0\0\x07\x09\0\0\x87\0\0\0\x40\0\0\0\x0d\x09\0\0\x1c\0\0\0\x60\ +\0\0\0\x15\x09\0\0\x8b\0\0\0\x80\0\0\0\x1b\x09\0\0\x8b\0\0\0\xc0\0\0\0\x29\x09\ +\0\0\x8c\0\0\0\0\x01\0\0\x2e\x09\0\0\x8e\0\0\0\x40\x01\0\0\x33\x09\0\0\x7d\0\0\ +\0\x80\x01\0\0\x3d\x09\0\0\x10\0\0\0\xc0\x01\0\0\x48\x09\0\0\x50\0\0\0\0\x02\0\ +\0\0\0\0\0\x8f\0\0\0\x40\x02\0\0\x4e\x09\0\0\x91\0\0\0\x60\x02\0\0\x55\x09\0\0\ +\x67\0\0\0\x80\x02\0\0\x5c\x09\0\0\x93\0\0\0\xc0\x02\0\0\x64\x09\0\0\x93\0\0\0\ +\x40\x03\0\0\x6c\x09\0\0\x93\0\0\0\xc0\x03\0\0\x74\x09\0\0\x36\0\0\0\x40\x04\0\ +\0\x7b\x09\0\0\x44\0\0\0\x40\x06\0\0\x83\x09\0\0\x40\0\0\0\x50\x06\0\0\x8d\x09\ +\0\0\x40\0\0\0\x58\x06\0\0\x9a\x09\0\0\x96\0\0\0\x80\x06\0\0\xa3\x09\0\0\x50\0\ +\0\0\xc0\x06\0\0\xab\x09\0\0\x97\0\0\0\0\x07\0\0\xb3\x09\0\0\x50\0\0\0\xc0\x0b\ +\0\0\xc0\x09\0\0\x50\0\0\0\0\x0c\0\0\xd2\x09\0\0\x49\0\0\0\x40\x0c\0\0\xd9\x09\ +\0\0\x4c\0\0\0\xc0\x0c\0\0\xe3\x09\0\0\x98\0\0\0\x40\x0d\0\0\xe8\x09\0\0\x02\0\ +\0\0\x80\x0d\0\0\xf8\x09\0\0\x42\0\0\0\xa0\x0d\0\0\x0a\x0a\0\0\x42\0\0\0\xb0\ +\x0d\0\0\x1b\x0a\0\0\x4c\0\0\0\xc0\x0d\0\0\x21\x0a\0\0\x4c\0\0\0\x40\x0e\0\0\ +\x2b\x0a\0\0\x4c\0\0\0\xc0\x0e\0\0\0\0\0\0\x99\0\0\0\x40\x0f\0\0\x35\x0a\0\0\ +\x5e\0\0\0\xc0\x0f\0\0\x3f\x0a\0\0\x5e\0\0\0\0\x10\0\0\x4a\x0a\0\0\x3d\0\0\0\ +\x40\x10\0\0\x52\x0a\0\0\x3d\0\0\0\x60\x10\0\0\x5e\x0a\0\0\x3d\0\0\0\x80\x10\0\ +\0\x6b\x0a\0\0\x3d\0\0\0\xa0\x10\0\0\0\0\0\0\x9b\0\0\0\xc0\x10\0\0\x77\x0a\0\0\ +\x9e\0\0\0\0\x11\0\0\x7f\x0a\0\0\x9f\0\0\0\x40\x11\0\0\x86\x0a\0\0\x4c\0\0\0\ +\x40\x22\0\0\0\0\0\0\xa7\0\0\0\xc0\x22\0\0\x90\x0a\0\0\x1b\0\0\0\0\x23\0\0\x9d\ +\x0a\0\0\x1b\0\0\0\x20\x23\0\0\xad\x0a\0\0\xab\0\0\0\x40\x23\0\0\xbe\x0a\0\0\ +\x10\0\0\0\x80\x23\0\0\xc8\x0a\0\0\0\0\0\x08\x44\0\0\0\xd0\x0a\0\0\0\0\0\x08\ +\x88\0\0\0\0\0\0\0\x01\0\0\x04\x04\0\0\0\xa1\x04\0\0\x89\0\0\0\0\0\0\0\xd7\x0a\ +\0\0\0\0\0\x08\x8a\0\0\0\xdd\x0a\0\0\0\0\0\x08\x1c\0\0\0\0\0\0\0\0\0\0\x02\xbf\ +\0\0\0\0\0\0\0\0\0\0\x02\x8d\0\0\0\0\0\0\0\0\0\0\x0a\xba\0\0\0\0\0\0\0\0\0\0\ +\x02\xc1\0\0\0\0\0\0\0\x02\0\0\x05\x04\0\0\0\xee\x0a\0\0\x90\0\0\0\0\0\0\0\xf6\ +\x0a\0\0\x1c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\x1c\0\0\0\0\x0b\0\0\0\0\0\x08\x92\ +\0\0\0\x06\x0b\0\0\0\0\0\x08\x54\0\0\0\x15\x0b\0\0\x02\0\0\x04\x10\0\0\0\x20\ +\x0b\0\0\x94\0\0\0\0\0\0\0\x27\x0b\0\0\x95\0\0\0\x40\0\0\0\x2f\x0b\0\0\0\0\0\ +\x08\x61\0\0\0\x38\x0b\0\0\0\0\0\x01\x08\0\0\0\x40\0\0\x01\x3d\x0b\0\0\0\0\0\ +\x08\x7a\0\0\0\x46\x0b\0\0\x07\0\0\x04\x98\0\0\0\x53\x0b\0\0\x5d\0\0\0\0\0\0\0\ +\x79\x04\0\0\x5d\0\0\0\x40\0\0\0\x58\x06\0\0\x66\0\0\0\x80\0\0\0\x4e\x06\0\0\ +\x65\0\0\0\xc0\0\0\0\x5c\x06\0\0\x4c\0\0\0\xc0\x02\0\0\x69\x04\0\0\x10\0\0\0\ +\x40\x03\0\0\x7f\x04\0\0\x45\0\0\0\x80\x03\0\0\0\0\0\0\0\0\0\x02\xb2\0\0\0\0\0\ +\0\0\x02\0\0\x05\x10\0\0\0\x59\x0b\0\0\x9a\0\0\0\0\0\0\0\x62\x0b\0\0\x2c\0\0\0\ +\0\0\0\0\x68\x0b\0\0\x01\0\0\x04\x08\0\0\0\x73\x0b\0\0\x4a\0\0\0\0\0\0\0\0\0\0\ +\0\x02\0\0\x05\x08\0\0\0\x79\x0b\0\0\x34\0\0\0\0\0\0\0\x7f\x0b\0\0\x9c\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\x02\x9d\0\0\0\0\0\0\0\x01\0\0\x0d\0\0\0\0\0\0\0\0\x33\0\0\ +\0\0\0\0\0\0\0\0\x02\xb6\0\0\0\x8a\x0b\0\0\x0f\0\0\x04\x20\x02\0\0\x98\x0b\0\0\ +\x33\0\0\0\0\0\0\0\x9d\x0b\0\0\xa0\0\0\0\x40\0\0\0\xa5\x0b\0\0\x97\0\0\0\xc0\ +\x02\0\0\xb5\x0b\0\0\xa1\0\0\0\x80\x07\0\0\xbe\x0b\0\0\x3d\0\0\0\xa0\x07\0\0\ +\xce\x0b\0\0\xa2\0\0\0\xc0\x07\0\0\xd5\x0b\0\0\x97\0\0\0\x40\x08\0\0\xe2\x0b\0\ +\0\x50\0\0\0\0\x0d\0\0\xea\x0b\0\0\x50\0\0\0\x40\x0d\0\0\xfa\x0b\0\0\xa5\0\0\0\ +\x80\x0d\0\0\0\x0c\0\0\x50\0\0\0\xc0\x0d\0\0\x06\x0c\0\0\x7e\0\0\0\0\x0e\0\0\ +\x0d\x0c\0\0\x36\0\0\0\x40\x0e\0\0\x1a\x0c\0\0\x4c\0\0\0\x40\x10\0\0\xc4\x03\0\ +\0\x10\0\0\0\xc0\x10\0\0\x27\x0c\0\0\x03\0\0\x04\x50\0\0\0\x2e\x0c\0\0\x36\0\0\ +\0\0\0\0\0\x36\x0c\0\0\xa1\0\0\0\0\x02\0\0\x3f\x0c\0\0\x10\0\0\0\x40\x02\0\0\ +\x47\x0c\0\0\0\0\0\x08\x1c\0\0\0\x4d\x0c\0\0\x02\0\0\x04\x10\0\0\0\x5c\x0c\0\0\ +\xa3\0\0\0\0\0\0\0\x64\x0c\0\0\xa4\0\0\0\x40\0\0\0\x5c\x0c\0\0\x01\0\0\x04\x08\ +\0\0\0\x70\x0c\0\0\xa4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\xc0\0\0\0\0\0\0\0\0\0\0\ +\x02\xa6\0\0\0\0\0\0\0\0\0\0\x0a\xb1\0\0\0\0\0\0\0\x04\0\0\x05\x08\0\0\0\x78\ +\x0c\0\0\xa8\0\0\0\0\0\0\0\x7f\x0c\0\0\xa9\0\0\0\0\0\0\0\x86\x0c\0\0\xaa\0\0\0\ +\0\0\0\0\x8d\x0c\0\0\x1c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\xbe\0\0\0\0\0\0\0\0\0\ +\0\x02\xb3\0\0\0\0\0\0\0\0\0\0\x02\x59\0\0\0\0\0\0\0\0\0\0\x02\xb8\0\0\0\0\0\0\ +\0\0\0\0\x03\0\0\0\0\x59\0\0\0\x04\0\0\0\x04\0\0\0\xbf\x0c\0\0\0\0\0\x0e\xac\0\ +\0\0\x01\0\0\0\xc7\x0c\0\0\x01\0\0\x0f\x04\0\0\0\xc3\0\0\0\0\0\0\0\x04\0\0\0\ +\xce\x0c\0\0\x02\0\0\x0f\x40\0\0\0\x0a\0\0\0\0\0\0\0\x20\0\0\0\x15\0\0\0\x20\0\ +\0\0\x20\0\0\0\xd4\x0c\0\0\x01\0\0\x0f\x04\0\0\0\xad\0\0\0\0\0\0\0\x04\0\0\0\ +\xdc\x0c\0\0\0\0\0\x07\0\0\0\0\xf5\x0c\0\0\0\0\0\x07\0\0\0\0\x03\x0d\0\0\0\0\0\ +\x07\0\0\0\0\x08\x0d\0\0\0\0\0\x07\0\0\0\0\x32\x04\0\0\0\0\0\x07\0\0\0\0\x0d\ +\x0d\0\0\0\0\0\x07\0\0\0\0\x1f\x0d\0\0\0\0\0\x07\0\0\0\0\x2f\x0d\0\0\0\0\0\x07\ +\0\0\0\0\x47\x0d\0\0\0\0\0\x07\0\0\0\0\x52\x0d\0\0\0\0\0\x07\0\0\0\0\x63\x0d\0\ +\0\0\0\0\x07\0\0\0\0\x72\x0d\0\0\0\0\0\x07\0\0\0\0\xb3\x06\0\0\0\0\0\x07\0\0\0\ +\0\x87\x0d\0\0\0\0\0\x07\0\0\0\0\x97\x0d\0\0\0\0\0\x07\0\0\0\0\x70\x0c\0\0\0\0\ +\0\x07\0\0\0\0\xa1\x0d\0\0\0\0\0\x07\0\0\0\0\xad\x0d\0\0\0\0\0\x07\0\0\0\0\xb6\ \x0d\0\0\0\0\0\x0e\x02\0\0\0\x01\0\0\0\0\x69\x6e\x74\0\x5f\x5f\x41\x52\x52\x41\ \x59\x5f\x53\x49\x5a\x45\x5f\x54\x59\x50\x45\x5f\x5f\0\x74\x79\x70\x65\0\x6d\ \x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\0\x6b\x65\x79\x5f\x73\x69\x7a\x65\0\ @@ -369,128 +386,133 @@ entrypoints_bpf__load(struct entrypoints_bpf *skel) \x76\x69\x63\x65\x5f\x65\x76\x65\x6e\x74\0\x69\x6e\x74\x20\x42\x50\x46\x5f\x50\ \x52\x4f\x47\x28\x68\x69\x64\x5f\x64\x65\x76\x69\x63\x65\x5f\x65\x76\x65\x6e\ \x74\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\x68\x69\x64\x5f\x70\x72\x6f\x67\x5f\x72\ -\x65\x6c\x65\x61\x73\x65\0\x66\x65\x78\x69\x74\x2f\x62\x70\x66\x5f\x70\x72\x6f\ -\x67\x5f\x72\x65\x6c\x65\x61\x73\x65\0\x69\x6e\x74\x20\x42\x50\x46\x5f\x50\x52\ -\x4f\x47\x28\x68\x69\x64\x5f\x70\x72\x6f\x67\x5f\x72\x65\x6c\x65\x61\x73\x65\ -\x2c\x20\x73\x74\x72\x75\x63\x74\x20\x69\x6e\x6f\x64\x65\x20\x2a\x69\x6e\x6f\ -\x64\x65\x2c\x20\x73\x74\x72\x75\x63\x74\x20\x66\x69\x6c\x65\x20\x2a\x66\x69\ -\x6c\x70\x29\0\x66\x69\x6c\x65\0\x66\x5f\x75\0\x66\x5f\x70\x61\x74\x68\0\x66\ -\x5f\x69\x6e\x6f\x64\x65\0\x66\x5f\x6f\x70\0\x66\x5f\x6c\x6f\x63\x6b\0\x66\x5f\ -\x63\x6f\x75\x6e\x74\0\x66\x5f\x66\x6c\x61\x67\x73\0\x66\x5f\x6d\x6f\x64\x65\0\ -\x66\x5f\x70\x6f\x73\x5f\x6c\x6f\x63\x6b\0\x66\x5f\x70\x6f\x73\0\x66\x5f\x6f\ -\x77\x6e\x65\x72\0\x66\x5f\x63\x72\x65\x64\0\x66\x5f\x72\x61\0\x66\x5f\x76\x65\ -\x72\x73\x69\x6f\x6e\0\x66\x5f\x73\x65\x63\x75\x72\x69\x74\x79\0\x70\x72\x69\ -\x76\x61\x74\x65\x5f\x64\x61\x74\x61\0\x66\x5f\x65\x70\0\x66\x5f\x6d\x61\x70\ -\x70\x69\x6e\x67\0\x66\x5f\x77\x62\x5f\x65\x72\x72\0\x66\x5f\x73\x62\x5f\x65\ -\x72\x72\0\x66\x75\x5f\x6c\x6c\x69\x73\x74\0\x66\x75\x5f\x72\x63\x75\x68\x65\ -\x61\x64\0\x6c\x6c\x69\x73\x74\x5f\x6e\x6f\x64\x65\0\x6e\x65\x78\x74\0\x63\x61\ -\x6c\x6c\x62\x61\x63\x6b\x5f\x68\x65\x61\x64\0\x66\x75\x6e\x63\0\x70\x61\x74\ -\x68\0\x6d\x6e\x74\0\x64\x65\x6e\x74\x72\x79\0\x73\x70\x69\x6e\x6c\x6f\x63\x6b\ -\x5f\x74\0\x73\x70\x69\x6e\x6c\x6f\x63\x6b\0\x72\x6c\x6f\x63\x6b\0\x72\x61\x77\ -\x5f\x73\x70\x69\x6e\x6c\x6f\x63\x6b\0\x72\x61\x77\x5f\x6c\x6f\x63\x6b\0\x6d\ -\x61\x67\x69\x63\0\x6f\x77\x6e\x65\x72\x5f\x63\x70\x75\0\x6f\x77\x6e\x65\x72\0\ -\x64\x65\x70\x5f\x6d\x61\x70\0\x61\x72\x63\x68\x5f\x73\x70\x69\x6e\x6c\x6f\x63\ -\x6b\x5f\x74\0\x71\x73\x70\x69\x6e\x6c\x6f\x63\x6b\0\x76\x61\x6c\0\x61\x74\x6f\ -\x6d\x69\x63\x5f\x74\0\x63\x6f\x75\x6e\x74\x65\x72\0\x6c\x6f\x63\x6b\x65\x64\0\ -\x70\x65\x6e\x64\x69\x6e\x67\0\x75\x38\0\x6c\x6f\x63\x6b\x65\x64\x5f\x70\x65\ -\x6e\x64\x69\x6e\x67\0\x74\x61\x69\x6c\0\x75\x31\x36\0\x5f\x5f\x75\x31\x36\0\ -\x75\x6e\x73\x69\x67\x6e\x65\x64\x20\x73\x68\x6f\x72\x74\0\x6c\x6f\x63\x6b\x64\ -\x65\x70\x5f\x6d\x61\x70\0\x63\x6c\x61\x73\x73\x5f\x63\x61\x63\x68\x65\0\x6e\ -\x61\x6d\x65\0\x77\x61\x69\x74\x5f\x74\x79\x70\x65\x5f\x6f\x75\x74\x65\x72\0\ -\x77\x61\x69\x74\x5f\x74\x79\x70\x65\x5f\x69\x6e\x6e\x65\x72\0\x6c\x6f\x63\x6b\ -\x5f\x74\x79\x70\x65\0\x6c\x6f\x63\x6b\x5f\x63\x6c\x61\x73\x73\0\x68\x61\x73\ -\x68\x5f\x65\x6e\x74\x72\x79\0\x6c\x6f\x63\x6b\x5f\x65\x6e\x74\x72\x79\0\x6c\ -\x6f\x63\x6b\x73\x5f\x61\x66\x74\x65\x72\0\x6c\x6f\x63\x6b\x73\x5f\x62\x65\x66\ -\x6f\x72\x65\0\x73\x75\x62\x63\x6c\x61\x73\x73\0\x64\x65\x70\x5f\x67\x65\x6e\ -\x5f\x69\x64\0\x75\x73\x61\x67\x65\x5f\x6d\x61\x73\x6b\0\x75\x73\x61\x67\x65\ -\x5f\x74\x72\x61\x63\x65\x73\0\x6e\x61\x6d\x65\x5f\x76\x65\x72\x73\x69\x6f\x6e\ -\0\x68\x6c\x69\x73\x74\x5f\x6e\x6f\x64\x65\0\x70\x70\x72\x65\x76\0\x6c\x69\x73\ -\x74\x5f\x68\x65\x61\x64\0\x70\x72\x65\x76\0\x75\x6e\x73\x69\x67\x6e\x65\x64\ -\x20\x6c\x6f\x6e\x67\0\x6c\x6f\x63\x6b\x5f\x74\x72\x61\x63\x65\0\x68\x61\x73\ -\x68\0\x6e\x72\x5f\x65\x6e\x74\x72\x69\x65\x73\0\x65\x6e\x74\x72\x69\x65\x73\0\ -\x75\x33\x32\0\x63\x68\x61\x72\0\x5f\x5f\x70\x61\x64\x64\x69\x6e\x67\0\x61\x74\ -\x6f\x6d\x69\x63\x5f\x6c\x6f\x6e\x67\x5f\x74\0\x61\x74\x6f\x6d\x69\x63\x36\x34\ -\x5f\x74\0\x73\x36\x34\0\x5f\x5f\x73\x36\x34\0\x6c\x6f\x6e\x67\x20\x6c\x6f\x6e\ -\x67\0\x66\x6d\x6f\x64\x65\x5f\x74\0\x6d\x75\x74\x65\x78\0\x77\x61\x69\x74\x5f\ -\x6c\x6f\x63\x6b\0\x6f\x73\x71\0\x77\x61\x69\x74\x5f\x6c\x69\x73\x74\0\x72\x61\ -\x77\x5f\x73\x70\x69\x6e\x6c\x6f\x63\x6b\x5f\x74\0\x6f\x70\x74\x69\x6d\x69\x73\ -\x74\x69\x63\x5f\x73\x70\x69\x6e\x5f\x71\x75\x65\x75\x65\0\x6c\x6f\x66\x66\x5f\ -\x74\0\x5f\x5f\x6b\x65\x72\x6e\x65\x6c\x5f\x6c\x6f\x66\x66\x5f\x74\0\x66\x6f\ -\x77\x6e\x5f\x73\x74\x72\x75\x63\x74\0\x6c\x6f\x63\x6b\0\x70\x69\x64\0\x70\x69\ -\x64\x5f\x74\x79\x70\x65\0\x75\x69\x64\0\x65\x75\x69\x64\0\x73\x69\x67\x6e\x75\ -\x6d\0\x72\x77\x6c\x6f\x63\x6b\x5f\x74\0\x61\x72\x63\x68\x5f\x72\x77\x6c\x6f\ -\x63\x6b\x5f\x74\0\x71\x72\x77\x6c\x6f\x63\x6b\0\x63\x6e\x74\x73\0\x77\x6c\x6f\ -\x63\x6b\x65\x64\0\x5f\x5f\x6c\x73\x74\x61\x74\x65\0\x50\x49\x44\x54\x59\x50\ -\x45\x5f\x50\x49\x44\0\x50\x49\x44\x54\x59\x50\x45\x5f\x54\x47\x49\x44\0\x50\ -\x49\x44\x54\x59\x50\x45\x5f\x50\x47\x49\x44\0\x50\x49\x44\x54\x59\x50\x45\x5f\ -\x53\x49\x44\0\x50\x49\x44\x54\x59\x50\x45\x5f\x4d\x41\x58\0\x6b\x75\x69\x64\ -\x5f\x74\0\x75\x69\x64\x5f\x74\0\x5f\x5f\x6b\x65\x72\x6e\x65\x6c\x5f\x75\x69\ -\x64\x33\x32\x5f\x74\0\x66\x69\x6c\x65\x5f\x72\x61\x5f\x73\x74\x61\x74\x65\0\ -\x73\x74\x61\x72\x74\0\x61\x73\x79\x6e\x63\x5f\x73\x69\x7a\x65\0\x72\x61\x5f\ -\x70\x61\x67\x65\x73\0\x6d\x6d\x61\x70\x5f\x6d\x69\x73\x73\0\x70\x72\x65\x76\ -\x5f\x70\x6f\x73\0\x75\x36\x34\0\x5f\x5f\x75\x36\x34\0\x65\x72\x72\x73\x65\x71\ -\x5f\x74\0\x30\x3a\x31\x35\0\x09\x75\x36\x34\x20\x70\x72\x6f\x67\x20\x3d\x20\ -\x28\x75\x36\x34\x29\x66\x69\x6c\x70\x2d\x3e\x70\x72\x69\x76\x61\x74\x65\x5f\ -\x64\x61\x74\x61\x3b\0\x09\x76\x61\x6c\x75\x65\x20\x3d\x20\x62\x70\x66\x5f\x6d\ -\x61\x70\x5f\x6c\x6f\x6f\x6b\x75\x70\x5f\x65\x6c\x65\x6d\x28\x26\x70\x72\x6f\ -\x67\x73\x5f\x6d\x61\x70\x2c\x20\x26\x70\x72\x6f\x67\x29\x3b\0\x09\x69\x66\x20\ -\x28\x21\x76\x61\x6c\x75\x65\x29\0\x09\x69\x66\x20\x28\x63\x61\x6c\x6c\x5f\x68\ -\x69\x64\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\x5f\x72\x65\x6c\x65\x61\x73\x65\ -\x28\x70\x72\x6f\x67\x2c\x20\x2a\x76\x61\x6c\x75\x65\x29\x29\0\x09\x09\x62\x70\ -\x66\x5f\x6d\x61\x70\x5f\x64\x65\x6c\x65\x74\x65\x5f\x65\x6c\x65\x6d\x28\x26\ -\x70\x72\x6f\x67\x73\x5f\x6d\x61\x70\x2c\x20\x26\x70\x72\x6f\x67\x29\x3b\0\x62\ -\x6f\x6f\x6c\0\x5f\x42\x6f\x6f\x6c\0\x63\x61\x6c\x6c\x5f\x68\x69\x64\x5f\x62\ -\x70\x66\x5f\x70\x72\x6f\x67\x5f\x72\x65\x6c\x65\x61\x73\x65\0\x68\x69\x64\x5f\ -\x66\x72\x65\x65\x5f\x69\x6e\x6f\x64\x65\0\x66\x65\x78\x69\x74\x2f\x62\x70\x66\ -\x5f\x66\x72\x65\x65\x5f\x69\x6e\x6f\x64\x65\0\x69\x6e\x74\x20\x42\x50\x46\x5f\ -\x50\x52\x4f\x47\x28\x68\x69\x64\x5f\x66\x72\x65\x65\x5f\x69\x6e\x6f\x64\x65\ -\x2c\x20\x73\x74\x72\x75\x63\x74\x20\x69\x6e\x6f\x64\x65\x20\x2a\x69\x6e\x6f\ -\x64\x65\x29\0\x69\x6e\x6f\x64\x65\0\x69\x5f\x6d\x6f\x64\x65\0\x69\x5f\x6f\x70\ -\x66\x6c\x61\x67\x73\0\x69\x5f\x75\x69\x64\0\x69\x5f\x67\x69\x64\0\x69\x5f\x66\ -\x6c\x61\x67\x73\0\x69\x5f\x61\x63\x6c\0\x69\x5f\x64\x65\x66\x61\x75\x6c\x74\ -\x5f\x61\x63\x6c\0\x69\x5f\x6f\x70\0\x69\x5f\x73\x62\0\x69\x5f\x6d\x61\x70\x70\ -\x69\x6e\x67\0\x69\x5f\x73\x65\x63\x75\x72\x69\x74\x79\0\x69\x5f\x69\x6e\x6f\0\ -\x69\x5f\x72\x64\x65\x76\0\x69\x5f\x73\x69\x7a\x65\0\x69\x5f\x61\x74\x69\x6d\ -\x65\0\x69\x5f\x6d\x74\x69\x6d\x65\0\x69\x5f\x63\x74\x69\x6d\x65\0\x69\x5f\x6c\ -\x6f\x63\x6b\0\x69\x5f\x62\x79\x74\x65\x73\0\x69\x5f\x62\x6c\x6b\x62\x69\x74\ -\x73\0\x69\x5f\x77\x72\x69\x74\x65\x5f\x68\x69\x6e\x74\0\x69\x5f\x62\x6c\x6f\ -\x63\x6b\x73\0\x69\x5f\x73\x74\x61\x74\x65\0\x69\x5f\x72\x77\x73\x65\x6d\0\x64\ -\x69\x72\x74\x69\x65\x64\x5f\x77\x68\x65\x6e\0\x64\x69\x72\x74\x69\x65\x64\x5f\ -\x74\x69\x6d\x65\x5f\x77\x68\x65\x6e\0\x69\x5f\x68\x61\x73\x68\0\x69\x5f\x69\ -\x6f\x5f\x6c\x69\x73\x74\0\x69\x5f\x77\x62\0\x69\x5f\x77\x62\x5f\x66\x72\x6e\ -\x5f\x77\x69\x6e\x6e\x65\x72\0\x69\x5f\x77\x62\x5f\x66\x72\x6e\x5f\x61\x76\x67\ -\x5f\x74\x69\x6d\x65\0\x69\x5f\x77\x62\x5f\x66\x72\x6e\x5f\x68\x69\x73\x74\x6f\ -\x72\x79\0\x69\x5f\x6c\x72\x75\0\x69\x5f\x73\x62\x5f\x6c\x69\x73\x74\0\x69\x5f\ -\x77\x62\x5f\x6c\x69\x73\x74\0\x69\x5f\x76\x65\x72\x73\x69\x6f\x6e\0\x69\x5f\ -\x73\x65\x71\x75\x65\x6e\x63\x65\0\x69\x5f\x63\x6f\x75\x6e\x74\0\x69\x5f\x64\ -\x69\x6f\x5f\x63\x6f\x75\x6e\x74\0\x69\x5f\x77\x72\x69\x74\x65\x63\x6f\x75\x6e\ -\x74\0\x69\x5f\x72\x65\x61\x64\x63\x6f\x75\x6e\x74\0\x69\x5f\x66\x6c\x63\x74\ -\x78\0\x69\x5f\x64\x61\x74\x61\0\x69\x5f\x64\x65\x76\x69\x63\x65\x73\0\x69\x5f\ -\x67\x65\x6e\x65\x72\x61\x74\x69\x6f\x6e\0\x69\x5f\x66\x73\x6e\x6f\x74\x69\x66\ -\x79\x5f\x6d\x61\x73\x6b\0\x69\x5f\x66\x73\x6e\x6f\x74\x69\x66\x79\x5f\x6d\x61\ -\x72\x6b\x73\0\x69\x5f\x70\x72\x69\x76\x61\x74\x65\0\x75\x6d\x6f\x64\x65\x5f\ -\x74\0\x6b\x67\x69\x64\x5f\x74\0\x67\x69\x64\x5f\x74\0\x5f\x5f\x6b\x65\x72\x6e\ -\x65\x6c\x5f\x67\x69\x64\x33\x32\x5f\x74\0\x69\x5f\x6e\x6c\x69\x6e\x6b\0\x5f\ -\x5f\x69\x5f\x6e\x6c\x69\x6e\x6b\0\x64\x65\x76\x5f\x74\0\x5f\x5f\x6b\x65\x72\ -\x6e\x65\x6c\x5f\x64\x65\x76\x5f\x74\0\x74\x69\x6d\x65\x73\x70\x65\x63\x36\x34\ -\0\x74\x76\x5f\x73\x65\x63\0\x74\x76\x5f\x6e\x73\x65\x63\0\x74\x69\x6d\x65\x36\ -\x34\x5f\x74\0\x6c\x6f\x6e\x67\0\x62\x6c\x6b\x63\x6e\x74\x5f\x74\0\x72\x77\x5f\ -\x73\x65\x6d\x61\x70\x68\x6f\x72\x65\0\x63\x6f\x75\x6e\x74\0\x69\x5f\x64\x65\ -\x6e\x74\x72\x79\0\x69\x5f\x72\x63\x75\0\x68\x6c\x69\x73\x74\x5f\x68\x65\x61\ -\x64\0\x66\x69\x72\x73\x74\0\x69\x5f\x66\x6f\x70\0\x66\x72\x65\x65\x5f\x69\x6e\ -\x6f\x64\x65\0\x61\x64\x64\x72\x65\x73\x73\x5f\x73\x70\x61\x63\x65\0\x68\x6f\ -\x73\x74\0\x69\x5f\x70\x61\x67\x65\x73\0\x69\x6e\x76\x61\x6c\x69\x64\x61\x74\ -\x65\x5f\x6c\x6f\x63\x6b\0\x67\x66\x70\x5f\x6d\x61\x73\x6b\0\x69\x5f\x6d\x6d\ -\x61\x70\x5f\x77\x72\x69\x74\x61\x62\x6c\x65\0\x69\x5f\x6d\x6d\x61\x70\0\x69\ -\x5f\x6d\x6d\x61\x70\x5f\x72\x77\x73\x65\x6d\0\x6e\x72\x70\x61\x67\x65\x73\0\ -\x77\x72\x69\x74\x65\x62\x61\x63\x6b\x5f\x69\x6e\x64\x65\x78\0\x61\x5f\x6f\x70\ -\x73\0\x66\x6c\x61\x67\x73\0\x77\x62\x5f\x65\x72\x72\0\x70\x72\x69\x76\x61\x74\ -\x65\x5f\x6c\x6f\x63\x6b\0\x70\x72\x69\x76\x61\x74\x65\x5f\x6c\x69\x73\x74\0\ -\x78\x61\x72\x72\x61\x79\0\x78\x61\x5f\x6c\x6f\x63\x6b\0\x78\x61\x5f\x66\x6c\ -\x61\x67\x73\0\x78\x61\x5f\x68\x65\x61\x64\0\x67\x66\x70\x5f\x74\0\x72\x62\x5f\ -\x72\x6f\x6f\x74\x5f\x63\x61\x63\x68\x65\x64\0\x72\x62\x5f\x72\x6f\x6f\x74\0\ -\x72\x62\x5f\x6c\x65\x66\x74\x6d\x6f\x73\x74\0\x72\x62\x5f\x6e\x6f\x64\x65\0\ +\x74\x78\x20\x2a\x68\x63\x74\x78\x29\0\x68\x69\x64\x5f\x72\x64\x65\x73\x63\x5f\ +\x66\x69\x78\x75\x70\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\x69\x6e\x74\x20\x42\ +\x50\x46\x5f\x50\x52\x4f\x47\x28\x68\x69\x64\x5f\x72\x64\x65\x73\x63\x5f\x66\ +\x69\x78\x75\x70\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\x68\x69\x64\x5f\x70\x72\x6f\ +\x67\x5f\x72\x65\x6c\x65\x61\x73\x65\0\x66\x65\x78\x69\x74\x2f\x62\x70\x66\x5f\ +\x70\x72\x6f\x67\x5f\x72\x65\x6c\x65\x61\x73\x65\0\x69\x6e\x74\x20\x42\x50\x46\ +\x5f\x50\x52\x4f\x47\x28\x68\x69\x64\x5f\x70\x72\x6f\x67\x5f\x72\x65\x6c\x65\ +\x61\x73\x65\x2c\x20\x73\x74\x72\x75\x63\x74\x20\x69\x6e\x6f\x64\x65\x20\x2a\ +\x69\x6e\x6f\x64\x65\x2c\x20\x73\x74\x72\x75\x63\x74\x20\x66\x69\x6c\x65\x20\ +\x2a\x66\x69\x6c\x70\x29\0\x66\x69\x6c\x65\0\x66\x5f\x75\0\x66\x5f\x70\x61\x74\ +\x68\0\x66\x5f\x69\x6e\x6f\x64\x65\0\x66\x5f\x6f\x70\0\x66\x5f\x6c\x6f\x63\x6b\ +\0\x66\x5f\x63\x6f\x75\x6e\x74\0\x66\x5f\x66\x6c\x61\x67\x73\0\x66\x5f\x6d\x6f\ +\x64\x65\0\x66\x5f\x70\x6f\x73\x5f\x6c\x6f\x63\x6b\0\x66\x5f\x70\x6f\x73\0\x66\ +\x5f\x6f\x77\x6e\x65\x72\0\x66\x5f\x63\x72\x65\x64\0\x66\x5f\x72\x61\0\x66\x5f\ +\x76\x65\x72\x73\x69\x6f\x6e\0\x66\x5f\x73\x65\x63\x75\x72\x69\x74\x79\0\x70\ +\x72\x69\x76\x61\x74\x65\x5f\x64\x61\x74\x61\0\x66\x5f\x65\x70\0\x66\x5f\x6d\ +\x61\x70\x70\x69\x6e\x67\0\x66\x5f\x77\x62\x5f\x65\x72\x72\0\x66\x5f\x73\x62\ +\x5f\x65\x72\x72\0\x66\x75\x5f\x6c\x6c\x69\x73\x74\0\x66\x75\x5f\x72\x63\x75\ +\x68\x65\x61\x64\0\x6c\x6c\x69\x73\x74\x5f\x6e\x6f\x64\x65\0\x6e\x65\x78\x74\0\ +\x63\x61\x6c\x6c\x62\x61\x63\x6b\x5f\x68\x65\x61\x64\0\x66\x75\x6e\x63\0\x70\ +\x61\x74\x68\0\x6d\x6e\x74\0\x64\x65\x6e\x74\x72\x79\0\x73\x70\x69\x6e\x6c\x6f\ +\x63\x6b\x5f\x74\0\x73\x70\x69\x6e\x6c\x6f\x63\x6b\0\x72\x6c\x6f\x63\x6b\0\x72\ +\x61\x77\x5f\x73\x70\x69\x6e\x6c\x6f\x63\x6b\0\x72\x61\x77\x5f\x6c\x6f\x63\x6b\ +\0\x6d\x61\x67\x69\x63\0\x6f\x77\x6e\x65\x72\x5f\x63\x70\x75\0\x6f\x77\x6e\x65\ +\x72\0\x64\x65\x70\x5f\x6d\x61\x70\0\x61\x72\x63\x68\x5f\x73\x70\x69\x6e\x6c\ +\x6f\x63\x6b\x5f\x74\0\x71\x73\x70\x69\x6e\x6c\x6f\x63\x6b\0\x76\x61\x6c\0\x61\ +\x74\x6f\x6d\x69\x63\x5f\x74\0\x63\x6f\x75\x6e\x74\x65\x72\0\x6c\x6f\x63\x6b\ +\x65\x64\0\x70\x65\x6e\x64\x69\x6e\x67\0\x75\x38\0\x6c\x6f\x63\x6b\x65\x64\x5f\ +\x70\x65\x6e\x64\x69\x6e\x67\0\x74\x61\x69\x6c\0\x75\x31\x36\0\x5f\x5f\x75\x31\ +\x36\0\x75\x6e\x73\x69\x67\x6e\x65\x64\x20\x73\x68\x6f\x72\x74\0\x6c\x6f\x63\ +\x6b\x64\x65\x70\x5f\x6d\x61\x70\0\x63\x6c\x61\x73\x73\x5f\x63\x61\x63\x68\x65\ +\0\x6e\x61\x6d\x65\0\x77\x61\x69\x74\x5f\x74\x79\x70\x65\x5f\x6f\x75\x74\x65\ +\x72\0\x77\x61\x69\x74\x5f\x74\x79\x70\x65\x5f\x69\x6e\x6e\x65\x72\0\x6c\x6f\ +\x63\x6b\x5f\x74\x79\x70\x65\0\x6c\x6f\x63\x6b\x5f\x63\x6c\x61\x73\x73\0\x68\ +\x61\x73\x68\x5f\x65\x6e\x74\x72\x79\0\x6c\x6f\x63\x6b\x5f\x65\x6e\x74\x72\x79\ +\0\x6c\x6f\x63\x6b\x73\x5f\x61\x66\x74\x65\x72\0\x6c\x6f\x63\x6b\x73\x5f\x62\ +\x65\x66\x6f\x72\x65\0\x73\x75\x62\x63\x6c\x61\x73\x73\0\x64\x65\x70\x5f\x67\ +\x65\x6e\x5f\x69\x64\0\x75\x73\x61\x67\x65\x5f\x6d\x61\x73\x6b\0\x75\x73\x61\ +\x67\x65\x5f\x74\x72\x61\x63\x65\x73\0\x6e\x61\x6d\x65\x5f\x76\x65\x72\x73\x69\ +\x6f\x6e\0\x68\x6c\x69\x73\x74\x5f\x6e\x6f\x64\x65\0\x70\x70\x72\x65\x76\0\x6c\ +\x69\x73\x74\x5f\x68\x65\x61\x64\0\x70\x72\x65\x76\0\x75\x6e\x73\x69\x67\x6e\ +\x65\x64\x20\x6c\x6f\x6e\x67\0\x6c\x6f\x63\x6b\x5f\x74\x72\x61\x63\x65\0\x68\ +\x61\x73\x68\0\x6e\x72\x5f\x65\x6e\x74\x72\x69\x65\x73\0\x65\x6e\x74\x72\x69\ +\x65\x73\0\x75\x33\x32\0\x63\x68\x61\x72\0\x5f\x5f\x70\x61\x64\x64\x69\x6e\x67\ +\0\x61\x74\x6f\x6d\x69\x63\x5f\x6c\x6f\x6e\x67\x5f\x74\0\x61\x74\x6f\x6d\x69\ +\x63\x36\x34\x5f\x74\0\x73\x36\x34\0\x5f\x5f\x73\x36\x34\0\x6c\x6f\x6e\x67\x20\ +\x6c\x6f\x6e\x67\0\x66\x6d\x6f\x64\x65\x5f\x74\0\x6d\x75\x74\x65\x78\0\x77\x61\ +\x69\x74\x5f\x6c\x6f\x63\x6b\0\x6f\x73\x71\0\x77\x61\x69\x74\x5f\x6c\x69\x73\ +\x74\0\x72\x61\x77\x5f\x73\x70\x69\x6e\x6c\x6f\x63\x6b\x5f\x74\0\x6f\x70\x74\ +\x69\x6d\x69\x73\x74\x69\x63\x5f\x73\x70\x69\x6e\x5f\x71\x75\x65\x75\x65\0\x6c\ +\x6f\x66\x66\x5f\x74\0\x5f\x5f\x6b\x65\x72\x6e\x65\x6c\x5f\x6c\x6f\x66\x66\x5f\ +\x74\0\x66\x6f\x77\x6e\x5f\x73\x74\x72\x75\x63\x74\0\x6c\x6f\x63\x6b\0\x70\x69\ +\x64\0\x70\x69\x64\x5f\x74\x79\x70\x65\0\x75\x69\x64\0\x65\x75\x69\x64\0\x73\ +\x69\x67\x6e\x75\x6d\0\x72\x77\x6c\x6f\x63\x6b\x5f\x74\0\x61\x72\x63\x68\x5f\ +\x72\x77\x6c\x6f\x63\x6b\x5f\x74\0\x71\x72\x77\x6c\x6f\x63\x6b\0\x63\x6e\x74\ +\x73\0\x77\x6c\x6f\x63\x6b\x65\x64\0\x5f\x5f\x6c\x73\x74\x61\x74\x65\0\x50\x49\ +\x44\x54\x59\x50\x45\x5f\x50\x49\x44\0\x50\x49\x44\x54\x59\x50\x45\x5f\x54\x47\ +\x49\x44\0\x50\x49\x44\x54\x59\x50\x45\x5f\x50\x47\x49\x44\0\x50\x49\x44\x54\ +\x59\x50\x45\x5f\x53\x49\x44\0\x50\x49\x44\x54\x59\x50\x45\x5f\x4d\x41\x58\0\ +\x6b\x75\x69\x64\x5f\x74\0\x75\x69\x64\x5f\x74\0\x5f\x5f\x6b\x65\x72\x6e\x65\ +\x6c\x5f\x75\x69\x64\x33\x32\x5f\x74\0\x66\x69\x6c\x65\x5f\x72\x61\x5f\x73\x74\ +\x61\x74\x65\0\x73\x74\x61\x72\x74\0\x61\x73\x79\x6e\x63\x5f\x73\x69\x7a\x65\0\ +\x72\x61\x5f\x70\x61\x67\x65\x73\0\x6d\x6d\x61\x70\x5f\x6d\x69\x73\x73\0\x70\ +\x72\x65\x76\x5f\x70\x6f\x73\0\x75\x36\x34\0\x5f\x5f\x75\x36\x34\0\x65\x72\x72\ +\x73\x65\x71\x5f\x74\0\x30\x3a\x31\x35\0\x09\x75\x36\x34\x20\x70\x72\x6f\x67\ +\x20\x3d\x20\x28\x75\x36\x34\x29\x66\x69\x6c\x70\x2d\x3e\x70\x72\x69\x76\x61\ +\x74\x65\x5f\x64\x61\x74\x61\x3b\0\x09\x76\x61\x6c\x75\x65\x20\x3d\x20\x62\x70\ +\x66\x5f\x6d\x61\x70\x5f\x6c\x6f\x6f\x6b\x75\x70\x5f\x65\x6c\x65\x6d\x28\x26\ +\x70\x72\x6f\x67\x73\x5f\x6d\x61\x70\x2c\x20\x26\x70\x72\x6f\x67\x29\x3b\0\x09\ +\x69\x66\x20\x28\x21\x76\x61\x6c\x75\x65\x29\0\x09\x69\x66\x20\x28\x63\x61\x6c\ +\x6c\x5f\x68\x69\x64\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\x5f\x72\x65\x6c\x65\ +\x61\x73\x65\x28\x70\x72\x6f\x67\x2c\x20\x2a\x76\x61\x6c\x75\x65\x29\x29\0\x09\ +\x09\x62\x70\x66\x5f\x6d\x61\x70\x5f\x64\x65\x6c\x65\x74\x65\x5f\x65\x6c\x65\ +\x6d\x28\x26\x70\x72\x6f\x67\x73\x5f\x6d\x61\x70\x2c\x20\x26\x70\x72\x6f\x67\ +\x29\x3b\0\x62\x6f\x6f\x6c\0\x5f\x42\x6f\x6f\x6c\0\x63\x61\x6c\x6c\x5f\x68\x69\ +\x64\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\x5f\x72\x65\x6c\x65\x61\x73\x65\0\x68\ +\x69\x64\x5f\x66\x72\x65\x65\x5f\x69\x6e\x6f\x64\x65\0\x66\x65\x78\x69\x74\x2f\ +\x62\x70\x66\x5f\x66\x72\x65\x65\x5f\x69\x6e\x6f\x64\x65\0\x69\x6e\x74\x20\x42\ +\x50\x46\x5f\x50\x52\x4f\x47\x28\x68\x69\x64\x5f\x66\x72\x65\x65\x5f\x69\x6e\ +\x6f\x64\x65\x2c\x20\x73\x74\x72\x75\x63\x74\x20\x69\x6e\x6f\x64\x65\x20\x2a\ +\x69\x6e\x6f\x64\x65\x29\0\x69\x6e\x6f\x64\x65\0\x69\x5f\x6d\x6f\x64\x65\0\x69\ +\x5f\x6f\x70\x66\x6c\x61\x67\x73\0\x69\x5f\x75\x69\x64\0\x69\x5f\x67\x69\x64\0\ +\x69\x5f\x66\x6c\x61\x67\x73\0\x69\x5f\x61\x63\x6c\0\x69\x5f\x64\x65\x66\x61\ +\x75\x6c\x74\x5f\x61\x63\x6c\0\x69\x5f\x6f\x70\0\x69\x5f\x73\x62\0\x69\x5f\x6d\ +\x61\x70\x70\x69\x6e\x67\0\x69\x5f\x73\x65\x63\x75\x72\x69\x74\x79\0\x69\x5f\ +\x69\x6e\x6f\0\x69\x5f\x72\x64\x65\x76\0\x69\x5f\x73\x69\x7a\x65\0\x69\x5f\x61\ +\x74\x69\x6d\x65\0\x69\x5f\x6d\x74\x69\x6d\x65\0\x69\x5f\x63\x74\x69\x6d\x65\0\ +\x69\x5f\x6c\x6f\x63\x6b\0\x69\x5f\x62\x79\x74\x65\x73\0\x69\x5f\x62\x6c\x6b\ +\x62\x69\x74\x73\0\x69\x5f\x77\x72\x69\x74\x65\x5f\x68\x69\x6e\x74\0\x69\x5f\ +\x62\x6c\x6f\x63\x6b\x73\0\x69\x5f\x73\x74\x61\x74\x65\0\x69\x5f\x72\x77\x73\ +\x65\x6d\0\x64\x69\x72\x74\x69\x65\x64\x5f\x77\x68\x65\x6e\0\x64\x69\x72\x74\ +\x69\x65\x64\x5f\x74\x69\x6d\x65\x5f\x77\x68\x65\x6e\0\x69\x5f\x68\x61\x73\x68\ +\0\x69\x5f\x69\x6f\x5f\x6c\x69\x73\x74\0\x69\x5f\x77\x62\0\x69\x5f\x77\x62\x5f\ +\x66\x72\x6e\x5f\x77\x69\x6e\x6e\x65\x72\0\x69\x5f\x77\x62\x5f\x66\x72\x6e\x5f\ +\x61\x76\x67\x5f\x74\x69\x6d\x65\0\x69\x5f\x77\x62\x5f\x66\x72\x6e\x5f\x68\x69\ +\x73\x74\x6f\x72\x79\0\x69\x5f\x6c\x72\x75\0\x69\x5f\x73\x62\x5f\x6c\x69\x73\ +\x74\0\x69\x5f\x77\x62\x5f\x6c\x69\x73\x74\0\x69\x5f\x76\x65\x72\x73\x69\x6f\ +\x6e\0\x69\x5f\x73\x65\x71\x75\x65\x6e\x63\x65\0\x69\x5f\x63\x6f\x75\x6e\x74\0\ +\x69\x5f\x64\x69\x6f\x5f\x63\x6f\x75\x6e\x74\0\x69\x5f\x77\x72\x69\x74\x65\x63\ +\x6f\x75\x6e\x74\0\x69\x5f\x72\x65\x61\x64\x63\x6f\x75\x6e\x74\0\x69\x5f\x66\ +\x6c\x63\x74\x78\0\x69\x5f\x64\x61\x74\x61\0\x69\x5f\x64\x65\x76\x69\x63\x65\ +\x73\0\x69\x5f\x67\x65\x6e\x65\x72\x61\x74\x69\x6f\x6e\0\x69\x5f\x66\x73\x6e\ +\x6f\x74\x69\x66\x79\x5f\x6d\x61\x73\x6b\0\x69\x5f\x66\x73\x6e\x6f\x74\x69\x66\ +\x79\x5f\x6d\x61\x72\x6b\x73\0\x69\x5f\x70\x72\x69\x76\x61\x74\x65\0\x75\x6d\ +\x6f\x64\x65\x5f\x74\0\x6b\x67\x69\x64\x5f\x74\0\x67\x69\x64\x5f\x74\0\x5f\x5f\ +\x6b\x65\x72\x6e\x65\x6c\x5f\x67\x69\x64\x33\x32\x5f\x74\0\x69\x5f\x6e\x6c\x69\ +\x6e\x6b\0\x5f\x5f\x69\x5f\x6e\x6c\x69\x6e\x6b\0\x64\x65\x76\x5f\x74\0\x5f\x5f\ +\x6b\x65\x72\x6e\x65\x6c\x5f\x64\x65\x76\x5f\x74\0\x74\x69\x6d\x65\x73\x70\x65\ +\x63\x36\x34\0\x74\x76\x5f\x73\x65\x63\0\x74\x76\x5f\x6e\x73\x65\x63\0\x74\x69\ +\x6d\x65\x36\x34\x5f\x74\0\x6c\x6f\x6e\x67\0\x62\x6c\x6b\x63\x6e\x74\x5f\x74\0\ +\x72\x77\x5f\x73\x65\x6d\x61\x70\x68\x6f\x72\x65\0\x63\x6f\x75\x6e\x74\0\x69\ +\x5f\x64\x65\x6e\x74\x72\x79\0\x69\x5f\x72\x63\x75\0\x68\x6c\x69\x73\x74\x5f\ +\x68\x65\x61\x64\0\x66\x69\x72\x73\x74\0\x69\x5f\x66\x6f\x70\0\x66\x72\x65\x65\ +\x5f\x69\x6e\x6f\x64\x65\0\x61\x64\x64\x72\x65\x73\x73\x5f\x73\x70\x61\x63\x65\ +\0\x68\x6f\x73\x74\0\x69\x5f\x70\x61\x67\x65\x73\0\x69\x6e\x76\x61\x6c\x69\x64\ +\x61\x74\x65\x5f\x6c\x6f\x63\x6b\0\x67\x66\x70\x5f\x6d\x61\x73\x6b\0\x69\x5f\ +\x6d\x6d\x61\x70\x5f\x77\x72\x69\x74\x61\x62\x6c\x65\0\x69\x5f\x6d\x6d\x61\x70\ +\0\x69\x5f\x6d\x6d\x61\x70\x5f\x72\x77\x73\x65\x6d\0\x6e\x72\x70\x61\x67\x65\ +\x73\0\x77\x72\x69\x74\x65\x62\x61\x63\x6b\x5f\x69\x6e\x64\x65\x78\0\x61\x5f\ +\x6f\x70\x73\0\x66\x6c\x61\x67\x73\0\x77\x62\x5f\x65\x72\x72\0\x70\x72\x69\x76\ +\x61\x74\x65\x5f\x6c\x6f\x63\x6b\0\x70\x72\x69\x76\x61\x74\x65\x5f\x6c\x69\x73\ +\x74\0\x78\x61\x72\x72\x61\x79\0\x78\x61\x5f\x6c\x6f\x63\x6b\0\x78\x61\x5f\x66\ +\x6c\x61\x67\x73\0\x78\x61\x5f\x68\x65\x61\x64\0\x67\x66\x70\x5f\x74\0\x72\x62\ +\x5f\x72\x6f\x6f\x74\x5f\x63\x61\x63\x68\x65\x64\0\x72\x62\x5f\x72\x6f\x6f\x74\ +\0\x72\x62\x5f\x6c\x65\x66\x74\x6d\x6f\x73\x74\0\x72\x62\x5f\x6e\x6f\x64\x65\0\ \x69\x5f\x70\x69\x70\x65\0\x69\x5f\x63\x64\x65\x76\0\x69\x5f\x6c\x69\x6e\x6b\0\ \x69\x5f\x64\x69\x72\x5f\x73\x65\x71\0\x30\x3a\x35\x31\0\x09\x75\x36\x34\x20\ \x70\x72\x6f\x67\x20\x3d\x20\x28\x75\x36\x34\x29\x69\x6e\x6f\x64\x65\x2d\x3e\ @@ -507,197 +529,224 @@ entrypoints_bpf__load(struct entrypoints_bpf *skel) \x62\x63\x6c\x61\x73\x73\x5f\x6b\x65\x79\0\x70\x69\x70\x65\x5f\x69\x6e\x6f\x64\ \x65\x5f\x69\x6e\x66\x6f\0\x70\x6f\x73\x69\x78\x5f\x61\x63\x6c\0\x73\x75\x70\ \x65\x72\x5f\x62\x6c\x6f\x63\x6b\0\x76\x66\x73\x6d\x6f\x75\x6e\x74\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\x0c\ -\x22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\x04\0\0\0\x04\0\0\0\0\x04\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\x68\x69\x64\x5f\x6a\x6d\x70\x5f\x74\x61\x62\x6c\x65\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x08\0\0\0\x01\ -\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x70\x72\x6f\x67\x73\x5f\x6d\x61\x70\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x47\ -\x50\x4c\0\0\0\0\0\x79\x12\0\0\0\0\0\0\x61\x23\0\0\0\0\0\0\x18\x52\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\x85\0\0\0\x0c\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\0\0\0\ -\0\x19\0\0\0\0\0\0\0\xb5\0\0\0\xfa\0\0\0\x05\x6c\0\0\x01\0\0\0\xb5\0\0\0\xe1\ -\x01\0\0\x02\x74\0\0\x05\0\0\0\xb5\0\0\0\xfa\0\0\0\x05\x6c\0\0\x08\0\0\0\x1a\0\ -\0\0\xdd\x01\0\0\0\0\0\0\x1a\0\0\0\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\x74\x61\x69\x6c\ -\x5f\x63\x61\x6c\x6c\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\x03\0\0\0\x01\0\0\0\0\0\0\0\x01\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\x5f\x5f\x68\x69\x64\x5f\x62\x70\ -\x66\x5f\x74\x61\x69\x6c\x5f\x63\x61\x6c\x6c\0\0\0\0\0\x47\x50\x4c\0\0\0\0\0\ -\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\0\0\0\0\x23\0\0\0\0\0\0\0\xb5\0\0\0\x42\ -\x02\0\0\x05\x8c\0\0\x1a\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\x64\x65\x76\x69\x63\ -\x65\x5f\x65\x76\x65\x6e\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\x01\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\ -\x64\x65\x76\x69\x63\x65\x5f\x65\x76\x65\x6e\x74\0\0\0\0\x47\x50\x4c\0\0\0\0\0\ -\x79\x11\x08\0\0\0\0\0\x79\x11\xa8\x01\0\0\0\0\x7b\x1a\xf8\xff\0\0\0\0\xbf\xa2\ -\0\0\0\0\0\0\x07\x02\0\0\xf8\xff\xff\xff\x18\x51\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\ -\x85\0\0\0\x01\0\0\0\x15\0\x09\0\0\0\0\0\x71\x02\0\0\0\0\0\0\x79\xa1\xf8\xff\0\ -\0\0\0\x85\x20\0\0\0\0\0\0\x15\0\x05\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\ -\xf8\xff\xff\xff\x18\x51\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x03\0\0\0\xb7\ -\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\0\0\0\0\x25\0\0\0\0\0\0\0\xb5\0\0\0\xa3\x02\0\ -\0\x05\xd4\0\0\x01\0\0\0\xb5\0\0\0\x4f\x07\0\0\x18\xdc\0\0\x04\0\0\0\xb5\0\0\0\ -\0\0\0\0\0\0\0\0\x05\0\0\0\xb5\0\0\0\x74\x07\0\0\x0a\xb0\0\0\x08\0\0\0\xb5\0\0\ -\0\xa5\x07\0\0\x06\xb4\0\0\x09\0\0\0\xb5\0\0\0\xb2\x07\0\0\x26\xc0\0\0\x0a\0\0\ -\0\xb5\0\0\0\xb2\x07\0\0\x20\xc0\0\0\x0b\0\0\0\xb5\0\0\0\xb2\x07\0\0\x06\xc0\0\ -\0\x0c\0\0\0\xb5\0\0\0\xb2\x07\0\0\x06\xc0\0\0\x0e\0\0\0\xb5\0\0\0\0\0\0\0\0\0\ -\0\0\x0f\0\0\0\xb5\0\0\0\xe0\x07\0\0\x03\xc4\0\0\x12\0\0\0\xb5\0\0\0\xa3\x02\0\ -\0\x05\xd4\0\0\x08\0\0\0\x26\0\0\0\x4a\x07\0\0\0\0\0\0\x1a\0\0\0\x14\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\x70\x72\x6f\x67\x5f\x72\x65\x6c\x65\x61\x73\0\0\0\0\0\x19\0\0\ +\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\0\0\ +\0\x91\x22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\x04\0\0\0\x04\0\0\0\0\x04\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\x68\x69\x64\x5f\x6a\x6d\x70\x5f\x74\x61\x62\x6c\x65\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x08\0\0\ +\0\x01\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x70\x72\x6f\x67\x73\x5f\x6d\x61\ +\x70\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\x47\x50\x4c\0\0\0\0\0\x79\x12\0\0\0\0\0\0\x61\x23\0\0\0\0\0\0\x18\x52\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x0c\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\0\ +\0\0\0\x19\0\0\0\0\0\0\0\xb5\0\0\0\xfa\0\0\0\x05\x6c\0\0\x01\0\0\0\xb5\0\0\0\ +\xe1\x01\0\0\x02\x74\0\0\x05\0\0\0\xb5\0\0\0\xfa\0\0\0\x05\x6c\0\0\x08\0\0\0\ +\x1a\0\0\0\xdd\x01\0\0\0\0\0\0\x1a\0\0\0\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\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\x74\x61\ +\x69\x6c\x5f\x63\x61\x6c\x6c\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\x03\0\0\0\x01\0\0\0\0\0\0\0\x01\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\x5f\x5f\x68\x69\x64\x5f\ +\x62\x70\x66\x5f\x74\x61\x69\x6c\x5f\x63\x61\x6c\x6c\0\0\0\0\0\x47\x50\x4c\0\0\ +\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\0\0\0\0\x23\0\0\0\0\0\0\0\xb5\0\0\0\ +\x42\x02\0\0\x05\x8c\0\0\x1a\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\x64\x65\x76\x69\ +\x63\x65\x5f\x65\x76\x65\x6e\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\x01\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\x64\x65\x76\x69\x63\x65\x5f\x65\x76\x65\x6e\x74\0\0\0\0\x47\x50\x4c\0\0\0\ +\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\0\0\0\0\x25\0\0\0\0\0\0\0\xb5\0\0\0\ +\xa8\x02\0\0\x05\xa4\0\0\x1a\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\x72\x64\x65\x73\ +\x63\x5f\x66\x69\x78\x75\x70\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\x01\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\x47\x50\x4c\0\0\0\0\ +\0\x79\x11\x08\0\0\0\0\0\x79\x11\xa8\x01\0\0\0\0\x7b\x1a\xf8\xff\0\0\0\0\xbf\ +\xa2\0\0\0\0\0\0\x07\x02\0\0\xf8\xff\xff\xff\x18\x51\0\0\x01\0\0\0\0\0\0\0\0\0\ +\0\0\x85\0\0\0\x01\0\0\0\x15\0\x09\0\0\0\0\0\x71\x02\0\0\0\0\0\0\x79\xa1\xf8\ +\xff\0\0\0\0\x85\x20\0\0\0\0\0\0\x15\0\x05\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\ +\x02\0\0\xf8\xff\xff\xff\x18\x51\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x03\0\ +\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\0\0\0\0\x27\0\0\0\0\0\0\0\xb5\0\0\0\ +\x08\x03\0\0\x05\xec\0\0\x01\0\0\0\xb5\0\0\0\xb4\x07\0\0\x18\xf4\0\0\x04\0\0\0\ +\xb5\0\0\0\0\0\0\0\0\0\0\0\x05\0\0\0\xb5\0\0\0\xd9\x07\0\0\x0a\xc8\0\0\x08\0\0\ +\0\xb5\0\0\0\x0a\x08\0\0\x06\xcc\0\0\x09\0\0\0\xb5\0\0\0\x17\x08\0\0\x26\xd8\0\ +\0\x0a\0\0\0\xb5\0\0\0\x17\x08\0\0\x20\xd8\0\0\x0b\0\0\0\xb5\0\0\0\x17\x08\0\0\ +\x06\xd8\0\0\x0c\0\0\0\xb5\0\0\0\x17\x08\0\0\x06\xd8\0\0\x0e\0\0\0\xb5\0\0\0\0\ +\0\0\0\0\0\0\0\x0f\0\0\0\xb5\0\0\0\x45\x08\0\0\x03\xdc\0\0\x12\0\0\0\xb5\0\0\0\ +\x08\x03\0\0\x05\xec\0\0\x08\0\0\0\x28\0\0\0\xaf\x07\0\0\0\0\0\0\x1a\0\0\0\x14\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\x70\x72\x6f\x67\x5f\x72\x65\x6c\x65\x61\x73\0\0\0\0\0\ +\x19\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\x0c\0\0\0\x01\0\0\0\0\0\0\0\x01\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\x62\x70\x66\x5f\x70\x72\x6f\x67\x5f\x72\x65\x6c\x65\x61\x73\x65\0\ +\0\0\0\0\0\0\0\x63\x61\x6c\x6c\x5f\x68\x69\x64\x5f\x62\x70\x66\x5f\x70\x72\x6f\ +\x67\x5f\x72\x65\x6c\x65\x61\x73\x65\0\0\0\0\0\0\0\x47\x50\x4c\0\0\0\0\0\x79\ +\x11\0\0\0\0\0\0\x79\x11\x70\x04\0\0\0\0\x7b\x1a\xf8\xff\0\0\0\0\xbf\xa2\0\0\0\ +\0\0\0\x07\x02\0\0\xf8\xff\xff\xff\x18\x51\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\x85\0\ +\0\0\x01\0\0\0\x15\0\x09\0\0\0\0\0\x71\x02\0\0\0\0\0\0\x79\xa1\xf8\xff\0\0\0\0\ +\x85\x20\0\0\0\0\0\0\x15\0\x05\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xf8\ +\xff\xff\xff\x18\x51\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x03\0\0\0\xb7\0\0\ +\0\0\0\0\0\x95\0\0\0\0\0\0\0\0\0\0\0\x84\0\0\0\0\0\0\0\xb5\0\0\0\xb8\x08\0\0\ +\x05\x14\x01\0\x01\0\0\0\xb5\0\0\0\x9c\x0c\0\0\x19\x1c\x01\0\x04\0\0\0\xb5\0\0\ +\0\0\0\0\0\0\0\0\0\x05\0\0\0\xb5\0\0\0\xd9\x07\0\0\x0a\xc8\0\0\x08\0\0\0\xb5\0\ +\0\0\x0a\x08\0\0\x06\xcc\0\0\x09\0\0\0\xb5\0\0\0\x17\x08\0\0\x26\xd8\0\0\x0a\0\ +\0\0\xb5\0\0\0\x17\x08\0\0\x20\xd8\0\0\x0b\0\0\0\xb5\0\0\0\x17\x08\0\0\x06\xd8\ +\0\0\x0c\0\0\0\xb5\0\0\0\x17\x08\0\0\x06\xd8\0\0\x0e\0\0\0\xb5\0\0\0\0\0\0\0\0\ +\0\0\0\x0f\0\0\0\xb5\0\0\0\x45\x08\0\0\x03\xdc\0\0\x12\0\0\0\xb5\0\0\0\xb8\x08\ +\0\0\x05\x14\x01\0\x08\0\0\0\x85\0\0\0\x97\x0c\0\0\0\0\0\0\x1a\0\0\0\x14\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\x72\x65\x65\x5f\x69\x6e\x6f\x64\x65\0\0\0\0\0\0\x19\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\x0c\0\ \0\0\x01\0\0\0\0\0\0\0\x01\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\x62\x70\x66\x5f\x70\x72\x6f\x67\x5f\x72\x65\x6c\x65\x61\x73\x65\0\0\0\0\0\ -\0\0\0\x63\x61\x6c\x6c\x5f\x68\x69\x64\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\x5f\ -\x72\x65\x6c\x65\x61\x73\x65\0\0\0\0\0\0\0\x47\x50\x4c\0\0\0\0\0\x79\x11\0\0\0\ -\0\0\0\x79\x11\x70\x04\0\0\0\0\x7b\x1a\xf8\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\ -\x02\0\0\xf8\xff\xff\xff\x18\x51\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x01\0\ -\0\0\x15\0\x09\0\0\0\0\0\x71\x02\0\0\0\0\0\0\x79\xa1\xf8\xff\0\0\0\0\x85\x20\0\ -\0\0\0\0\0\x15\0\x05\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xf8\xff\xff\xff\ -\x18\x51\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x03\0\0\0\xb7\0\0\0\0\0\0\0\ -\x95\0\0\0\0\0\0\0\0\0\0\0\x82\0\0\0\0\0\0\0\xb5\0\0\0\x53\x08\0\0\x05\xfc\0\0\ -\x01\0\0\0\xb5\0\0\0\x37\x0c\0\0\x19\x04\x01\0\x04\0\0\0\xb5\0\0\0\0\0\0\0\0\0\ -\0\0\x05\0\0\0\xb5\0\0\0\x74\x07\0\0\x0a\xb0\0\0\x08\0\0\0\xb5\0\0\0\xa5\x07\0\ -\0\x06\xb4\0\0\x09\0\0\0\xb5\0\0\0\xb2\x07\0\0\x26\xc0\0\0\x0a\0\0\0\xb5\0\0\0\ -\xb2\x07\0\0\x20\xc0\0\0\x0b\0\0\0\xb5\0\0\0\xb2\x07\0\0\x06\xc0\0\0\x0c\0\0\0\ -\xb5\0\0\0\xb2\x07\0\0\x06\xc0\0\0\x0e\0\0\0\xb5\0\0\0\0\0\0\0\0\0\0\0\x0f\0\0\ -\0\xb5\0\0\0\xe0\x07\0\0\x03\xc4\0\0\x12\0\0\0\xb5\0\0\0\x53\x08\0\0\x05\xfc\0\ -\0\x08\0\0\0\x83\0\0\0\x32\x0c\0\0\0\0\0\0\x1a\0\0\0\x14\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\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\x72\x65\x65\x5f\x69\x6e\x6f\x64\x65\0\0\0\0\0\0\x19\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\x0c\0\0\0\x01\0\0\0\ -\0\0\0\0\x01\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\x62\x70\ -\x66\x5f\x66\x72\x65\x65\x5f\x69\x6e\x6f\x64\x65\0\0\x63\x61\x6c\x6c\x5f\x68\ -\x69\x64\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\x5f\x72\x65\x6c\x65\x61\x73\x65\0\ -\0\0\0\0\0\0"; - opts.insns_sz = 3776; +\0\0\x62\x70\x66\x5f\x66\x72\x65\x65\x5f\x69\x6e\x6f\x64\x65\0\0\x63\x61\x6c\ +\x6c\x5f\x68\x69\x64\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\x5f\x72\x65\x6c\x65\ +\x61\x73\x65\0\0\0\0\0\0\0"; + opts.insns_sz = 4400; opts.insns = (void *)"\ \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\x20\0\0\0\0\0\x61\ +\0\x88\0\0\0\xb7\x03\0\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x05\0\x23\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\x61\xa1\x88\xff\0\0\0\0\xd5\x01\x01\0\0\0\0\0\x85\0\ \0\0\xa8\0\0\0\x61\xa1\x8c\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\x18\x60\0\0\0\0\0\0\0\0\0\0\x04\0\0\ +\0\x61\xa1\x90\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\x18\x60\0\0\0\0\0\0\0\0\0\0\x04\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\xb0\x27\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\xac\x27\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\xa0\x27\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\x98\x27\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\x98\x27\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\xc5\xff\0\0\0\0\x63\x7a\x78\xff\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\ +\xc4\x27\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\xb8\x27\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\xb8\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\x61\xa0\x78\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x30\x28\0\0\x63\x01\0\0\ +\0\0\0\0\x61\x60\x2c\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\ +\x0c\x28\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\0\x28\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\xa5\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\x63\x71\0\0\0\0\0\0\ +\x18\x60\0\0\0\0\0\0\0\0\0\0\x48\x28\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xe0\x28\0\ +\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x50\x28\0\0\x18\x61\0\0\0\0\ +\0\0\0\0\0\0\xd8\x28\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x88\ +\x28\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x20\x29\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\ +\0\0\0\0\0\0\0\0\0\x90\x28\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x30\x29\0\0\x7b\x01\ +\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xc0\x28\0\0\x18\x61\0\0\0\0\0\0\0\0\0\ +\0\x50\x29\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\x48\x29\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\xe8\x28\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\xec\x28\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\xf0\x28\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\x18\x29\0\0\x63\x01\0\0\0\0\0\0\x18\ +\x61\0\0\0\0\0\0\0\0\0\0\x60\x29\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\x6c\xff\ +\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xd0\x28\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\xb7\x01\0\0\x05\0\0\0\x18\x62\0\0\0\0\0\0\ +\0\0\0\0\xd0\x28\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\x40\x29\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\x5a\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\x78\x29\0\0\x18\x61\0\0\0\0\0\ +\0\0\0\0\0\xb8\x29\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x80\x29\ +\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb0\x29\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\ +\0\0\0\0\0\0\0\x90\x29\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xf8\x29\0\0\x7b\x01\0\0\ +\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x98\x29\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\ +\x08\x2a\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xa8\x29\0\0\x18\ +\x61\0\0\0\0\0\0\0\0\0\0\x28\x2a\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\x20\x2a\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\xc0\x29\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\xc4\x29\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\xc8\x29\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\xf0\x29\0\0\x63\ +\x01\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x38\x2a\0\0\xb7\x02\0\0\x15\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\x23\xff\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xa8\x29\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\xb7\x01\0\0\x05\0\0\ +\0\x18\x62\0\0\0\0\0\0\0\0\0\0\xa8\x29\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\x18\x2a\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\ +\x11\xff\0\0\0\0\x63\x7a\x84\xff\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x50\x2a\0\ +\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\x2a\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\ +\0\0\0\0\0\0\x58\x2a\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x88\x2a\0\0\x7b\x01\0\0\0\ +\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x68\x2a\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xd0\ +\x2a\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x70\x2a\0\0\x18\x61\0\ +\0\0\0\0\0\0\0\0\0\xe0\x2a\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\ +\x80\x2a\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\0\x2b\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\xf8\x2a\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\x98\x2a\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\x9c\x2a\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\xa0\x2a\ +\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\ +\xc8\x2a\0\0\x63\x01\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x10\x2b\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\xda\xfe\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x80\ +\x2a\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\xb7\ +\x01\0\0\x05\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x80\x2a\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\xf0\x2a\ +\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\xc8\xfe\0\0\0\0\x63\x7a\x88\xff\0\0\0\0\x18\x60\0\0\0\0\0\0\ +\0\0\0\0\x28\x2b\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb8\x2c\0\0\x7b\x01\0\0\0\0\0\ +\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x30\x2b\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb0\x2c\ +\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xd0\x2b\0\0\x18\x61\0\0\0\ +\0\0\0\0\0\0\0\xf8\x2c\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xd8\ +\x2b\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x08\x2d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\ +\0\0\0\0\0\0\0\0\0\x98\x2c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x28\x2d\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\ +\x20\x2d\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\xc0\x2c\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\xc4\x2c\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\xc8\x2c\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\xf0\x2c\0\0\x63\x01\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\ +\x38\x2d\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\x91\xfe\0\0\0\0\x18\x60\0\0\0\ +\0\0\0\0\0\0\0\xa8\x2c\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\x88\x2b\0\0\x18\x61\0\0\0\0\0\0\0\0\ +\0\0\x50\x2d\0\0\xb7\x02\0\0\x1a\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\x82\xfe\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\xa8\x2c\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\x18\x2d\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\x61\xfe\0\0\0\0\x63\x7a\x8c\xff\0\0\0\0\ +\x18\x60\0\0\0\0\0\0\0\0\0\0\x70\x2d\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\0\x2f\0\0\ +\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x78\x2d\0\0\x18\x61\0\0\0\0\0\ +\0\0\0\0\0\xf8\x2e\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x18\x2e\ +\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x40\x2f\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\ +\0\0\0\0\0\0\0\x20\x2e\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x50\x2f\0\0\x7b\x01\0\0\ +\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xe0\x2e\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\ +\x70\x2f\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\x68\x2f\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\x08\x2f\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\x0c\x2f\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\x10\x2f\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\x38\x2f\0\0\x63\x01\0\0\0\0\0\0\x18\ +\x61\0\0\0\0\0\0\0\0\0\0\x80\x2f\0\0\xb7\x02\0\0\x0f\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\x2a\xfe\ +\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xf0\x2e\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\xd0\x2d\0\0\ +\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\x2f\0\0\xb7\x02\0\0\x1a\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\x1b\ +\xfe\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\xf0\x2e\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\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\x28\x27\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\x24\x27\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\x18\x27\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\x10\x27\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\x10\x27\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\xc8\xff\0\0\0\0\x63\x7a\ -\x78\xff\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\x3c\x27\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\x30\x27\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\xbb\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\x61\xa0\x78\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa8\x27\0\0\x63\ -\x01\0\0\0\0\0\0\x61\x60\x2c\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\x84\x27\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\x78\x27\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\xa8\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\x63\x71\0\ -\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xc0\x27\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\ -\x58\x28\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xc8\x27\0\0\x18\ -\x61\0\0\0\0\0\0\0\0\0\0\x50\x28\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\ -\0\0\0\0\x28\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x98\x28\0\0\x7b\x01\0\0\0\0\0\0\ -\x18\x60\0\0\0\0\0\0\0\0\0\0\x08\x28\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa8\x28\0\ -\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x38\x28\0\0\x18\x61\0\0\0\0\ -\0\0\0\0\0\0\xc8\x28\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\xc0\x28\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\x60\x28\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\x64\x28\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\x68\x28\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\x90\x28\0\0\x63\x01\0\0\0\0\0\ -\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xd8\x28\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\ -\x6f\xff\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x48\x28\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\xb7\x01\0\0\x05\0\0\0\x18\x62\0\0\ -\0\0\0\0\0\0\0\0\x48\x28\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\xb8\x28\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\x5d\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\xf0\x28\0\0\x18\x61\0\ -\0\0\0\0\0\0\0\0\0\x30\x29\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\ -\xf8\x28\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x28\x29\0\0\x7b\x01\0\0\0\0\0\0\x18\ -\x60\0\0\0\0\0\0\0\0\0\0\x08\x29\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x70\x29\0\0\ -\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x10\x29\0\0\x18\x61\0\0\0\0\0\ -\0\0\0\0\0\x80\x29\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x20\x29\ -\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa0\x29\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\x98\x29\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\x38\x29\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\x3c\x29\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\x40\x29\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\x68\x29\0\ -\0\x63\x01\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xb0\x29\0\0\xb7\x02\0\0\x15\ -\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\x26\xff\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x20\x29\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\xb7\x01\0\0\x05\ -\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x20\x29\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\x90\x29\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\x14\xff\0\0\0\0\x63\x7a\x84\xff\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xc8\ -\x29\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x58\x2b\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\ -\0\0\0\0\0\0\0\0\0\xd0\x29\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x50\x2b\0\0\x7b\x01\ -\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x70\x2a\0\0\x18\x61\0\0\0\0\0\0\0\0\0\ -\0\x98\x2b\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x78\x2a\0\0\x18\ -\x61\0\0\0\0\0\0\0\0\0\0\xa8\x2b\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\ -\0\0\0\x38\x2b\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xc8\x2b\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\xc0\x2b\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\x60\x2b\ -\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\x64\ -\x2b\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\ -\x68\x2b\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\x90\x2b\0\0\x63\x01\0\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xd8\x2b\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\xdd\xfe\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\ -\x48\x2b\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\x28\x2a\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xf0\x2b\0\ -\0\xb7\x02\0\0\x1a\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\xce\xfe\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\x48\x2b\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\xb8\x2b\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\xad\xfe\0\0\0\0\x63\x7a\x88\xff\0\0\0\0\x18\x60\0\ -\0\0\0\0\0\0\0\0\0\x10\x2c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa0\x2d\0\0\x7b\x01\ -\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x18\x2c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\ -\0\x98\x2d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xb8\x2c\0\0\x18\ -\x61\0\0\0\0\0\0\0\0\0\0\xe0\x2d\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\ -\0\0\0\xc0\x2c\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xf0\x2d\0\0\x7b\x01\0\0\0\0\0\0\ -\x18\x60\0\0\0\0\0\0\0\0\0\0\x80\x2d\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x10\x2e\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\x08\x2e\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\xa8\x2d\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\xac\x2d\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\xb0\x2d\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\xd8\x2d\0\0\x63\x01\0\0\0\0\0\0\x18\x61\0\0\0\0\0\ -\0\0\0\0\0\x20\x2e\0\0\xb7\x02\0\0\x0f\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\x76\xfe\0\0\0\0\x18\ -\x60\0\0\0\0\0\0\0\0\0\0\x90\x2d\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\x70\x2c\0\0\x18\x61\0\0\0\ -\0\0\0\0\0\0\0\x30\x2e\0\0\xb7\x02\0\0\x1a\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\x67\xfe\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\x90\x2d\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\0\x2e\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\x46\xfe\0\0\0\0\x63\x7a\x8c\ -\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\x38\0\0\0\0\0\x61\xa0\x84\ -\xff\0\0\0\0\x63\x06\x3c\0\0\0\0\0\x61\xa0\x88\xff\0\0\0\0\x63\x06\x40\0\0\0\0\ -\0\x61\xa0\x8c\xff\0\0\0\0\x63\x06\x44\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\x18\x61\0\0\0\0\0\0\0\0\0\0\ -\x04\0\0\0\x61\x10\0\0\0\0\0\0\x63\x06\x28\0\0\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\ -\0\0\0\0\0"; +\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x60\x2f\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\xfa\xfd\0\0\0\0\x63\ +\x7a\x90\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\x38\0\0\0\0\0\x61\ +\xa0\x84\xff\0\0\0\0\x63\x06\x3c\0\0\0\0\0\x61\xa0\x88\xff\0\0\0\0\x63\x06\x40\ +\0\0\0\0\0\x61\xa0\x8c\xff\0\0\0\0\x63\x06\x44\0\0\0\0\0\x61\xa0\x90\xff\0\0\0\ +\0\x63\x06\x48\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\x18\x61\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\x61\x10\0\0\0\0\ +\0\0\x63\x06\x28\0\0\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0"; err = bpf_load_and_run(&opts); if (err < 0) return err; diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index 01158d7a14ae..0b1356b70c68 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -85,6 +85,63 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type } EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event); +/** + * hid_bpf_rdesc_fixup - Called when the probe function parses the report + * descriptor of the HID device + * + * @ctx: The HID-BPF context + * + * @return 0 on success and keep processing; a positive value to change the + * incoming size buffer; a negative error code to interrupt the processing + * of this event + * + * Declare an %fmod_ret tracing bpf program to this function and attach this + * program through hid_bpf_attach_prog() to have this helper called before any + * parsing of the report descriptor by HID. + */ +/* never used by the kernel but declared so we can load and attach a tracepoint */ +__weak noinline int hid_bpf_rdesc_fixup(struct hid_bpf_ctx *ctx) +{ + return 0; +} +ALLOW_ERROR_INJECTION(hid_bpf_rdesc_fixup, ERRNO); + +u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size) +{ + int ret; + struct hid_bpf_ctx_kern ctx_kern = { + .ctx = { + .hid = hdev, + .size = *size, + .allocated_size = HID_MAX_DESCRIPTOR_SIZE, + }, + }; + + ctx_kern.data = kmemdup(rdesc, ctx_kern.ctx.allocated_size, GFP_KERNEL); + if (!ctx_kern.data) + goto ignore_bpf; + + ret = hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_RDESC_FIXUP, &ctx_kern); + if (ret < 0) + goto ignore_bpf; + + if (ret) { + if (ret > ctx_kern.ctx.allocated_size) + goto ignore_bpf; + + *size = ret; + } + + rdesc = krealloc(ctx_kern.data, *size, GFP_KERNEL); + + return rdesc; + + ignore_bpf: + kfree(ctx_kern.data); + return kmemdup(rdesc, *size, GFP_KERNEL); +} +EXPORT_SYMBOL_GPL(call_hid_bpf_rdesc_fixup); + /** * hid_bpf_get_data - Get the kernel memory pointer associated with the context @ctx * @@ -188,6 +245,14 @@ static int hid_bpf_allocate_event_data(struct hid_device *hdev) return __hid_bpf_allocate_data(hdev, &hdev->bpf.device_data, &hdev->bpf.allocated_data); } +int hid_bpf_reconnect(struct hid_device *hdev) +{ + if (!test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status)) + return device_reprobe(&hdev->dev); + + return 0; +} + /** * hid_bpf_attach_prog - Attach the given @prog_fd to the given HID device * @@ -229,7 +294,17 @@ hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags) return err; } - return __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags); + err = __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags); + if (err) + return err; + + if (prog_type == HID_BPF_PROG_TYPE_RDESC_FIXUP) { + err = hid_bpf_reconnect(hdev); + if (err) + return err; + } + + return 0; } /** diff --git a/drivers/hid/bpf/hid_bpf_dispatch.h b/drivers/hid/bpf/hid_bpf_dispatch.h index 98c378e18b2b..1d1d5bcccbd7 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.h +++ b/drivers/hid/bpf/hid_bpf_dispatch.h @@ -18,6 +18,7 @@ int __hid_bpf_attach_prog(struct hid_device *hdev, enum hid_bpf_prog_type prog_t void __hid_bpf_destroy_device(struct hid_device *hdev); int hid_bpf_prog_run(struct hid_device *hdev, enum hid_bpf_prog_type type, struct hid_bpf_ctx_kern *ctx_kern); +int hid_bpf_reconnect(struct hid_device *hdev); struct bpf_prog; diff --git a/drivers/hid/bpf/hid_bpf_jmp_table.c b/drivers/hid/bpf/hid_bpf_jmp_table.c index aed88b4b52ad..e81a6d33f43b 100644 --- a/drivers/hid/bpf/hid_bpf_jmp_table.c +++ b/drivers/hid/bpf/hid_bpf_jmp_table.c @@ -64,6 +64,8 @@ static int hid_bpf_max_programs(enum hid_bpf_prog_type type) switch (type) { case HID_BPF_PROG_TYPE_DEVICE_EVENT: return HID_BPF_MAX_PROGS_PER_DEV; + case HID_BPF_PROG_TYPE_RDESC_FIXUP: + return 1; default: return -EINVAL; } @@ -233,6 +235,10 @@ static void hid_bpf_release_progs(struct work_struct *work) if (next->hdev == hdev && next->type == type) next->hdev = NULL; } + + /* if type was rdesc fixup, reconnect device */ + if (type == HID_BPF_PROG_TYPE_RDESC_FIXUP) + hid_bpf_reconnect(hdev); } } @@ -568,6 +574,8 @@ int hid_bpf_preload_skel(void) } FIND_AND_STORE_BTF_ID(hid_device_event, HID_BPF_PROG_TYPE_DEVICE_EVENT); + FIND_AND_STORE_BTF_ID(hid_rdesc_fixup, HID_BPF_PROG_TYPE_RDESC_FIXUP); + ATTACH_AND_STORE_LINK(hid_tail_call); ATTACH_AND_STORE_LINK(hid_prog_release); ATTACH_AND_STORE_LINK(hid_free_inode); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 70b3790f4595..9b1207320095 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1218,7 +1218,8 @@ int hid_open_report(struct hid_device *device) return -ENODEV; size = device->dev_rsize; - buf = kmemdup(start, size, GFP_KERNEL); + /* call_hid_bpf_rdesc_fixup() ensures we work on a copy of rdesc */ + buf = call_hid_bpf_rdesc_fixup(device, start, &size); if (buf == NULL) return -ENOMEM; diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h index 62478a53af22..62725a5613e2 100644 --- a/include/linux/hid_bpf.h +++ b/include/linux/hid_bpf.h @@ -59,6 +59,7 @@ struct hid_bpf_ctx { /* Following functions are tracepoints that BPF programs can attach to */ int hid_bpf_device_event(struct hid_bpf_ctx *ctx); +int hid_bpf_rdesc_fixup(struct hid_bpf_ctx *ctx); /* Following functions are kfunc that we export to BPF programs */ /* available everywhere in HID-BPF */ @@ -82,6 +83,7 @@ void hid_bpf_release_context(struct hid_bpf_ctx *ctx); enum hid_bpf_prog_type { HID_BPF_PROG_TYPE_UNDEF = -1, HID_BPF_PROG_TYPE_DEVICE_EVENT, /* an event is emitted from the device */ + HID_BPF_PROG_TYPE_RDESC_FIXUP, HID_BPF_PROG_TYPE_MAX, }; @@ -124,6 +126,7 @@ int hid_bpf_connect_device(struct hid_device *hdev); void hid_bpf_disconnect_device(struct hid_device *hdev); void hid_bpf_destroy_device(struct hid_device *hid); void hid_bpf_device_init(struct hid_device *hid); +u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size); #else /* CONFIG_BPF */ static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, int type, u8 *data, u32 *size, int interrupt) { return 0; } @@ -131,6 +134,11 @@ static inline int hid_bpf_connect_device(struct hid_device *hdev) { return 0; } static inline void hid_bpf_disconnect_device(struct hid_device *hdev) {} static inline void hid_bpf_destroy_device(struct hid_device *hid) {} static inline void hid_bpf_device_init(struct hid_device *hid) {} +static inline u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size) +{ + return kmemdup(rdesc, *size, GFP_KERNEL); +} + #endif /* CONFIG_BPF */ #endif /* __HID_BPF_H */ From patchwork Wed May 18 20:59:21 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12854115 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5BCBBC433F5 for ; Wed, 18 May 2022 21:02:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S243000AbiERVB7 (ORCPT ); Wed, 18 May 2022 17:01:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34674 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S243067AbiERVBI (ORCPT ); Wed, 18 May 2022 17:01:08 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id C9DB8259F92 for ; Wed, 18 May 2022 14:00:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1652907636; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=xk7ljzbPk/v3aanbwMMPBz7B7Czft8RGF93W6eyzeUg=; b=fEGrWfu3gJmxh8dI8Hf/jo/4c0tgaT1jl/9OrKOsv3lKZeN3LlP1OliA0YXI6Zm/M17cy1 S7HzCsftCMsmr32oPmpx2TicmZjPNf7YsfhaplbZQSjHFacE04SlnrhRdM2asdS743PQJ9 y55IqZ7+xxKtYlANGyiawHQkDiG7RFI= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-42-isc7IjVFMiKqhxN7xFeA9A-1; Wed, 18 May 2022 17:00:31 -0400 X-MC-Unique: isc7IjVFMiKqhxN7xFeA9A-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 5828D85A5A8; Wed, 18 May 2022 21:00:30 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.58]) by smtp.corp.redhat.com (Postfix) with ESMTP id A658C2166B3F; Wed, 18 May 2022 21:00:26 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v5 14/17] selftests/bpf: add report descriptor fixup tests Date: Wed, 18 May 2022 22:59:21 +0200 Message-Id: <20220518205924.399291-15-benjamin.tissoires@redhat.com> In-Reply-To: <20220518205924.399291-1-benjamin.tissoires@redhat.com> References: <20220518205924.399291-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Simple report descriptor override in HID: replace part of the report descriptor from a static definition in the bpf kernel program. Note that this test should be run last because we disconnect/reconnect the device, meaning that it changes the overall uhid device. Signed-off-by: Benjamin Tissoires --- changes in v5: - amended for the new API not in v4 changes in v3: - added a comment to mention that this test needs to be run last changes in v2: - split the series by bpf/libbpf/hid/selftests and samples --- tools/testing/selftests/bpf/prog_tests/hid.c | 76 ++++++++++++++++++++ tools/testing/selftests/bpf/progs/hid.c | 53 ++++++++++++++ 2 files changed, 129 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c index 54c0a0fcd54d..4375c1cc00dc 100644 --- a/tools/testing/selftests/bpf/prog_tests/hid.c +++ b/tools/testing/selftests/bpf/prog_tests/hid.c @@ -9,6 +9,7 @@ #include #include #include +#include #include static unsigned char rdesc[] = { @@ -769,6 +770,73 @@ static int test_hid_user_raw_request_call(int uhid_fd, int dev_id) return ret; } +/* + * Attach hid_rdesc_fixup to the given uhid device, + * retrieve and open the matching hidraw node, + * check that the hidraw report descriptor has been updated. + */ +static int test_rdesc_fixup(void) +{ + struct hidraw_report_descriptor rpt_desc = {0}; + struct test_params params; + int err, uhid_fd, desc_size, hidraw_fd = -1, ret = -1; + int dev_id; + void *uhid_err; + pthread_t tid; + bool started = false; + + dev_id = rand() % 1024; + + uhid_fd = setup_uhid(dev_id); + if (!ASSERT_GE(uhid_fd, 0, "setup uhid")) + return uhid_fd; + + err = prep_test(dev_id, "hid_rdesc_fixup", ¶ms); + if (!ASSERT_EQ(err, 0, "prep_test(hid_rdesc_fixup)")) + goto cleanup; + + err = uhid_start_listener(&tid, uhid_fd); + ASSERT_OK(err, "uhid_start_listener"); + + started = true; + + hidraw_fd = params.hidraw_fd; + + /* check that hid_rdesc_fixup() was executed */ + ASSERT_EQ(params.skel->data->callback2_check, 0x21, "callback_check2"); + + /* read the exposed report descriptor from hidraw */ + err = ioctl(hidraw_fd, HIDIOCGRDESCSIZE, &desc_size); + if (!ASSERT_GE(err, 0, "HIDIOCGRDESCSIZE")) + goto cleanup; + + /* ensure the new size of the rdesc is bigger than the old one */ + if (!ASSERT_GT(desc_size, sizeof(rdesc), "new_rdesc_size")) + goto cleanup; + + rpt_desc.size = desc_size; + err = ioctl(hidraw_fd, HIDIOCGRDESC, &rpt_desc); + if (!ASSERT_GE(err, 0, "HIDIOCGRDESC")) + goto cleanup; + + if (!ASSERT_EQ(rpt_desc.value[4], 0x42, "hid_rdesc_fixup")) + goto cleanup; + + ret = 0; + +cleanup: + cleanup_test(¶ms); + + if (started) { + destroy(uhid_fd); + pthread_join(tid, &uhid_err); + err = (int)(long)uhid_err; + CHECK_FAIL(err); + } + + return ret; +} + void serial_test_hid_bpf(void) { int err, uhid_fd; @@ -799,6 +867,14 @@ void serial_test_hid_bpf(void) err = test_hid_user_raw_request_call(uhid_fd, dev_id); ASSERT_OK(err, "hid_change_report"); + /* + * this test should be run last because we disconnect/reconnect + * the device, meaning that it changes the overall uhid device + * and messes up with the thread that reads uhid events. + */ + err = test_rdesc_fixup(); + ASSERT_OK(err, "hid_rdesc_fixup"); + destroy(uhid_fd); pthread_join(tid, &uhid_err); diff --git a/tools/testing/selftests/bpf/progs/hid.c b/tools/testing/selftests/bpf/progs/hid.c index e3444d444303..eda9306e6d7f 100644 --- a/tools/testing/selftests/bpf/progs/hid.c +++ b/tools/testing/selftests/bpf/progs/hid.c @@ -115,3 +115,56 @@ int hid_user_raw_request(struct hid_hw_request_syscall_args *args) return ret; } + +static const __u8 rdesc[] = { + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x32, /* USAGE (Z) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x81, 0x06, /* INPUT (Data,Var,Rel) */ + + 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ + 0x19, 0x01, /* USAGE_MINIMUM (1) */ + 0x29, 0x03, /* USAGE_MAXIMUM (3) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x91, 0x02, /* Output (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x05, /* REPORT_SIZE (5) */ + 0x91, 0x01, /* Output (Cnst,Var,Abs) */ + + 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ + 0x19, 0x06, /* USAGE_MINIMUM (6) */ + 0x29, 0x08, /* USAGE_MAXIMUM (8) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0xb1, 0x02, /* Feature (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x05, /* REPORT_SIZE (5) */ + 0x91, 0x01, /* Output (Cnst,Var,Abs) */ + + 0xc0, /* END_COLLECTION */ + 0xc0, /* END_COLLECTION */ +}; + +SEC("?fmod_ret/hid_bpf_rdesc_fixup") +int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hid_ctx) +{ + __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4096 /* size */); + + if (!data) + return 0; /* EPERM check */ + + callback2_check = data[4]; + + /* insert rdesc at offset 73 */ + __builtin_memcpy(&data[73], rdesc, sizeof(rdesc)); + + /* Change Usage Vendor globally */ + data[4] = 0x42; + + return sizeof(rdesc) + 73; +} From patchwork Wed May 18 20:59:22 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12854108 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4988BC433EF for ; Wed, 18 May 2022 21:01:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S243079AbiERVBV (ORCPT ); Wed, 18 May 2022 17:01:21 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34690 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S243076AbiERVBJ (ORCPT ); Wed, 18 May 2022 17:01:09 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 5CE622573F9 for ; Wed, 18 May 2022 14:00:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1652907640; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=dOD7EgUrZMs2w5AxhmsuB8o7QpPaiPeR39zzsEEn6xM=; b=fMvfJ0Y81Pm8Kaexpl2lfI181HWNDKjpTAdohJG1v/7sR7FbxNR8SNZak8lQOLjcWEUzyx To7MzqhO+z1MuuQALfQ7hq8zxz9a09JvOqQX0mJnD8PXeRuFuzhcbgGN28G1Ly5kbLwXg+ IVs+v7+cs9k4KUFRZSsjvsI6BPoT8fo= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-664-Li_dvkYfMUegNwaQ0nNNTA-1; Wed, 18 May 2022 17:00:34 -0400 X-MC-Unique: Li_dvkYfMUegNwaQ0nNNTA-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id C3D18811E75; Wed, 18 May 2022 21:00:33 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.58]) by smtp.corp.redhat.com (Postfix) with ESMTP id 94BF22166B25; Wed, 18 May 2022 21:00:30 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v5 15/17] samples/bpf: add new hid_mouse example Date: Wed, 18 May 2022 22:59:22 +0200 Message-Id: <20220518205924.399291-16-benjamin.tissoires@redhat.com> In-Reply-To: <20220518205924.399291-1-benjamin.tissoires@redhat.com> References: <20220518205924.399291-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Everything should be available in the selftest part of the tree, but providing an example without uhid and hidraw will be more easy to follow for users. Signed-off-by: Benjamin Tissoires --- changes in v5: - bring back same features than v3, with the new API changes in v4: - dropped the not-yet-implemented rdesc_fixup - use the new API changes in v3: - use the new hid_get_data API - add a comment for the report descriptor fixup to explain what is done changes in v2: - split the series by bpf/libbpf/hid/selftests and samples --- samples/bpf/.gitignore | 1 + samples/bpf/Makefile | 23 ++++++ samples/bpf/hid_mouse.bpf.c | 134 ++++++++++++++++++++++++++++++ samples/bpf/hid_mouse.c | 157 ++++++++++++++++++++++++++++++++++++ 4 files changed, 315 insertions(+) create mode 100644 samples/bpf/hid_mouse.bpf.c create mode 100644 samples/bpf/hid_mouse.c diff --git a/samples/bpf/.gitignore b/samples/bpf/.gitignore index 0e7bfdbff80a..65440bd618b2 100644 --- a/samples/bpf/.gitignore +++ b/samples/bpf/.gitignore @@ -2,6 +2,7 @@ cpustat fds_example hbm +hid_mouse ibumad lathist lwt_len_hist diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 03e3d3529ac9..dc74e26111f4 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -60,6 +60,8 @@ tprogs-y += xdp_redirect_map tprogs-y += xdp_redirect tprogs-y += xdp_monitor +tprogs-y += hid_mouse + # Libbpf dependencies LIBBPF_SRC = $(TOOLS_PATH)/lib/bpf LIBBPF_OUTPUT = $(abspath $(BPF_SAMPLES_PATH))/libbpf @@ -125,6 +127,8 @@ xdp_redirect-objs := xdp_redirect_user.o $(XDP_SAMPLE) xdp_monitor-objs := xdp_monitor_user.o $(XDP_SAMPLE) xdp_router_ipv4-objs := xdp_router_ipv4_user.o $(XDP_SAMPLE) +hid_mouse-objs := hid_mouse.o + # Tell kbuild to always build the programs always-y := $(tprogs-y) always-y += sockex1_kern.o @@ -349,6 +353,8 @@ $(obj)/hbm_out_kern.o: $(src)/hbm.h $(src)/hbm_kern.h $(obj)/hbm.o: $(src)/hbm.h $(obj)/hbm_edt_kern.o: $(src)/hbm.h $(src)/hbm_kern.h +$(obj)/hid_mouse.o: $(obj)/hid_mouse.skel.h + # Override includes for xdp_sample_user.o because $(srctree)/usr/include in # TPROGS_CFLAGS causes conflicts XDP_SAMPLE_CFLAGS += -Wall -O2 \ @@ -433,6 +439,23 @@ $(BPF_SKELS_LINKED): $(BPF_OBJS_LINKED) $(BPFTOOL) @echo " BPF GEN-SKEL" $(@:.skel.h=) $(Q)$(BPFTOOL) gen skeleton $(@:.skel.h=.lbpf.o) name $(notdir $(@:.skel.h=)) > $@ +# Generate BPF skeletons for non XDP progs +OTHER_BPF_SKELS := hid_mouse.skel.h + +hid_mouse.skel.h-deps := hid_mouse.bpf.o + +OTHER_BPF_SRCS_LINKED := $(patsubst %.skel.h,%.bpf.c, $(OTHER_BPF_SKELS)) +OTHER_BPF_OBJS_LINKED := $(patsubst %.bpf.c,$(obj)/%.bpf.o, $(OTHER_BPF_SRCS_LINKED)) +OTHER_BPF_SKELS_LINKED := $(addprefix $(obj)/,$(OTHER_BPF_SKELS)) + +$(OTHER_BPF_SKELS_LINKED): $(OTHER_BPF_OBJS_LINKED) $(BPFTOOL) + @echo " BPF GEN-OBJ " $(@:.skel.h=) + $(Q)$(BPFTOOL) gen object $(@:.skel.h=.lbpf.o) $(addprefix $(obj)/,$($(@F)-deps)) + @echo " BPF GEN-SKEL" $(@:.skel.h=) + $(Q)$(BPFTOOL) gen skeleton $(@:.skel.h=.lbpf.o) name $(notdir $(@:.skel.h=_lskel)) > $@ +# $(call msg,GEN-SKEL,$@) +# $(Q)$(BPFTOOL) gen skeleton $< > $@ + # asm/sysreg.h - inline assembly used by it is incompatible with llvm. # But, there is no easy way to fix it, so just exclude it since it is # useless for BPF samples. diff --git a/samples/bpf/hid_mouse.bpf.c b/samples/bpf/hid_mouse.bpf.c new file mode 100644 index 000000000000..0113e603f7a7 --- /dev/null +++ b/samples/bpf/hid_mouse.bpf.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "vmlinux.h" +#include +#include + +/* following are kfuncs exported by HID for HID-BPF */ +extern int hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, u32 flags) __ksym; +extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx, + unsigned int offset, + const size_t __sz) __ksym; +extern void hid_bpf_data_release(__u8 *data) __ksym; +extern int hid_bpf_hw_request(struct hid_bpf_ctx *ctx) __ksym; + +struct attach_prog_args { + int prog_fd; + unsigned int hid; + int retval; +}; + +SEC("syscall") +int attach_prog(struct attach_prog_args *ctx) +{ + ctx->retval = hid_bpf_attach_prog(ctx->hid, + ctx->prog_fd, + 0); + return 0; +} + +SEC("fmod_ret/hid_bpf_device_event") +int BPF_PROG(hid_y_event, struct hid_bpf_ctx *hctx) +{ + s16 y; + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 9 /* size */); + + if (!data) + return 0; /* EPERM check */ + + bpf_printk("event: size: %d", hctx->size); + bpf_printk("incoming event: %02x %02x %02x", + data[0], + data[1], + data[2]); + bpf_printk(" %02x %02x %02x", + data[3], + data[4], + data[5]); + bpf_printk(" %02x %02x %02x", + data[6], + data[7], + data[8]); + + y = data[3] | (data[4] << 8); + + y = -y; + + data[3] = y & 0xFF; + data[4] = (y >> 8) & 0xFF; + + bpf_printk("modified event: %02x %02x %02x", + data[0], + data[1], + data[2]); + bpf_printk(" %02x %02x %02x", + data[3], + data[4], + data[5]); + bpf_printk(" %02x %02x %02x", + data[6], + data[7], + data[8]); + + return 0; +} + +SEC("fmod_ret/hid_bpf_device_event") +int BPF_PROG(hid_x_event, struct hid_bpf_ctx *hctx) +{ + s16 x; + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 9 /* size */); + + if (!data) + return 0; /* EPERM check */ + + x = data[1] | (data[2] << 8); + + x = -x; + + data[1] = x & 0xFF; + data[2] = (x >> 8) & 0xFF; + return 0; +} + +SEC("fmod_ret/hid_bpf_rdesc_fixup") +int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */); + + if (!data) + return 0; /* EPERM check */ + + bpf_printk("rdesc: %02x %02x %02x", + data[0], + data[1], + data[2]); + bpf_printk(" %02x %02x %02x", + data[3], + data[4], + data[5]); + bpf_printk(" %02x %02x %02x ...", + data[6], + data[7], + data[8]); + + /* + * The original report descriptor contains: + * + * 0x05, 0x01, // Usage Page (Generic Desktop) 30 + * 0x16, 0x01, 0x80, // Logical Minimum (-32767) 32 + * 0x26, 0xff, 0x7f, // Logical Maximum (32767) 35 + * 0x09, 0x30, // Usage (X) 38 + * 0x09, 0x31, // Usage (Y) 40 + * + * So byte 39 contains Usage X and byte 41 Usage Y. + * + * We simply swap the axes here. + */ + data[39] = 0x31; + data[41] = 0x30; + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/samples/bpf/hid_mouse.c b/samples/bpf/hid_mouse.c new file mode 100644 index 000000000000..a74e0dc45755 --- /dev/null +++ b/samples/bpf/hid_mouse.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2022 Benjamin Tissoires + */ + +/* not sure why but this doesn't get preoperly imported */ +#define __must_check + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "bpf_util.h" +#include +#include + +#include "hid_mouse.skel.h" + +static bool running = true; + +struct attach_prog_args { + int prog_fd; + unsigned int hid; + int retval; +}; + +static void int_exit(int sig) +{ + running = false; + exit(0); +} + +static void usage(const char *prog) +{ + fprintf(stderr, + "%s: %s /sys/bus/hid/devices/0BUS:0VID:0PID:00ID\n\n", + __func__, prog); +} + +static int get_hid_id(const char *path) +{ + const char *str_id, *dir; + char uevent[1024]; + int fd; + + memset(uevent, 0, sizeof(uevent)); + snprintf(uevent, sizeof(uevent) - 1, "%s/uevent", path); + + fd = open(uevent, O_RDONLY | O_NONBLOCK); + if (fd < 0) + return -ENOENT; + + close(fd); + + dir = basename((char *)path); + + str_id = dir + sizeof("0003:0001:0A37."); + return (int)strtol(str_id, NULL, 16); +} + +int main(int argc, char **argv) +{ + struct hid_mouse_lskel *skel; + struct bpf_program *prog; + int err; + const char *optstr = ""; + const char *sysfs_path; + int opt, hid_id, attach_fd; + struct attach_prog_args args = { + .retval = -1, + }; + DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattr, + .ctx_in = &args, + .ctx_size_in = sizeof(args), + ); + + while ((opt = getopt(argc, argv, optstr)) != -1) { + switch (opt) { + default: + usage(basename(argv[0])); + return 1; + } + } + + if (optind == argc) { + usage(basename(argv[0])); + return 1; + } + + sysfs_path = argv[optind]; + if (!sysfs_path) { + perror("sysfs"); + return 1; + } + + skel = hid_mouse_lskel__open_and_load(); + if (!skel) { + fprintf(stderr, "%s %s:%d", __func__, __FILE__, __LINE__); + return -1; + } + + hid_id = get_hid_id(sysfs_path); + + if (hid_id < 0) { + fprintf(stderr, "can not open HID device: %m\n"); + return 1; + } + args.hid = hid_id; + + attach_fd = bpf_program__fd(skel->progs.attach_prog); + if (attach_fd < 0) { + fprintf(stderr, "can't locate attach prog: %m\n"); + return 1; + } + + bpf_object__for_each_program(prog, *skel->skeleton->obj) { + /* ignore syscalls */ + if (bpf_program__get_type(prog) != BPF_PROG_TYPE_TRACING) + continue; + + args.retval = -1; + args.prog_fd = bpf_program__fd(prog); + err = bpf_prog_test_run_opts(attach_fd, &tattr); + if (err) { + fprintf(stderr, "can't attach prog to hid device %d: %m (err: %d)\n", + hid_id, err); + return 1; + } + } + + signal(SIGINT, int_exit); + signal(SIGTERM, int_exit); +// +//// err = bpf_obj_get_info_by_fd(progs[0].fd, &info, &info_len); +//// if (err) { +//// printf("can't get prog info - %s\n", strerror(errno)); +//// goto cleanup; +//// } +// + while (running) + ; + +// cleanup: +// hid_mouse_lskel__destroy(skel); +// +// return err; +} From patchwork Wed May 18 20:59:23 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12854111 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C4C94C433EF for ; Wed, 18 May 2022 21:01:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S243142AbiERVB0 (ORCPT ); Wed, 18 May 2022 17:01:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59952 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S243093AbiERVBJ (ORCPT ); Wed, 18 May 2022 17:01:09 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 7EA8213D51 for ; Wed, 18 May 2022 14:00:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1652907647; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=CbRZnUCJWXlkLRg2AjtZuY/qz6b3pvkpQQTZSQJdd6U=; b=XB47Za6u70BmTDLlcr0rTt71ib2fbDwXuB6sVOOr6R/WWHN51QOZx2Z+EAUSMcgfSsIrP3 /J+nbSeFEtx6RRIDVTFsQEzNvH0pgKGaSjsVRTSSB304nvMkVv52gOX02eAodP78WSpdUg yGtINhOjYyDvCNOd+nKynOkshVxvNm0= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-377-oHOXO-5qOVyRwFMnnnffrw-1; Wed, 18 May 2022 17:00:44 -0400 X-MC-Unique: oHOXO-5qOVyRwFMnnnffrw-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 2AA0118E6C50; Wed, 18 May 2022 21:00:37 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.58]) by smtp.corp.redhat.com (Postfix) with ESMTP id 0F5882166B25; Wed, 18 May 2022 21:00:33 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v5 16/17] selftests/bpf: Add a test for BPF_F_INSERT_HEAD Date: Wed, 18 May 2022 22:59:23 +0200 Message-Id: <20220518205924.399291-17-benjamin.tissoires@redhat.com> In-Reply-To: <20220518205924.399291-1-benjamin.tissoires@redhat.com> References: <20220518205924.399291-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Insert 3 programs to check that we are doing the correct thing: '2', '1', '3' are inserted, but '1' is supposed to be executed first. Signed-off-by: Benjamin Tissoires --- changes in v5: - use the new API not in v4 changes in v3: - use the new hid_get_data API new in v2 --- tools/testing/selftests/bpf/prog_tests/hid.c | 107 +++++++++++++++++++ tools/testing/selftests/bpf/progs/hid.c | 54 +++++++++- 2 files changed, 160 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c index 4375c1cc00dc..c2b3f580605e 100644 --- a/tools/testing/selftests/bpf/prog_tests/hid.c +++ b/tools/testing/selftests/bpf/prog_tests/hid.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -83,6 +84,7 @@ static u8 feature_data[] = { 1, 2 }; struct attach_prog_args { int prog_fd; unsigned int hid; + unsigned int flags; int retval; }; @@ -770,6 +772,109 @@ static int test_hid_user_raw_request_call(int uhid_fd, int dev_id) return ret; } +/* + * Attach hid_insert{0,1,2} to the given uhid device, + * retrieve and open the matching hidraw node, + * inject one event in the uhid device, + * check that the programs have been inserted in the correct order. + */ +static int test_hid_attach_flags(int uhid_fd, int dev_id) +{ + struct hid *hid_skel = NULL; + u8 buf[64] = {0}; + int hidraw_fd = -1; + int hid_id, attach_fd, err = -EINVAL; + struct attach_prog_args args = { + .retval = -1, + }; + DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattr, + .ctx_in = &args, + .ctx_size_in = sizeof(args), + ); + + /* locate the uevent file of the created device */ + hid_id = get_hid_id(dev_id); + if (!ASSERT_GE(hid_id, 0, "locate uhid device id")) + goto cleanup; + + args.hid = hid_id; + + hid_skel = hid__open(); + if (!ASSERT_OK_PTR(hid_skel, "hid_skel_open")) + goto cleanup; + + bpf_program__set_autoload(hid_skel->progs.hid_test_insert1, true); + bpf_program__set_autoload(hid_skel->progs.hid_test_insert2, true); + bpf_program__set_autoload(hid_skel->progs.hid_test_insert3, true); + + err = hid__load(hid_skel); + if (!ASSERT_OK(err, "hid_skel_load")) + goto cleanup; + + attach_fd = bpf_program__fd(hid_skel->progs.attach_prog); + if (!ASSERT_GE(attach_fd, 0, "locate attach_prog")) { + err = attach_fd; + goto cleanup; + } + + /* attach hid_test_insert2 program */ + args.prog_fd = bpf_program__fd(hid_skel->progs.hid_test_insert2); + args.flags = HID_BPF_FLAG_NONE; + args.retval = 1; + err = bpf_prog_test_run_opts(attach_fd, &tattr); + if (!ASSERT_EQ(args.retval, 0, "attach_hid_test_insert2")) + goto cleanup; + + /* then attach hid_test_insert1 program before the previous*/ + args.prog_fd = bpf_program__fd(hid_skel->progs.hid_test_insert1); + args.flags = HID_BPF_FLAG_INSERT_HEAD; + args.retval = 1; + err = bpf_prog_test_run_opts(attach_fd, &tattr); + if (!ASSERT_EQ(args.retval, 0, "attach_hid_test_insert1")) + goto cleanup; + + /* finally attach hid_test_insert3 at the end */ + args.prog_fd = bpf_program__fd(hid_skel->progs.hid_test_insert3); + args.flags = HID_BPF_FLAG_NONE; + args.retval = 1; + err = bpf_prog_test_run_opts(attach_fd, &tattr); + if (!ASSERT_EQ(args.retval, 0, "attach_hid_test_insert3")) + goto cleanup; + + hidraw_fd = open_hidraw(dev_id); + if (!ASSERT_GE(hidraw_fd, 0, "open_hidraw")) + goto cleanup; + + /* inject one event */ + buf[0] = 1; + send_event(uhid_fd, buf, 6); + + /* read the data from hidraw */ + memset(buf, 0, sizeof(buf)); + err = read(hidraw_fd, buf, sizeof(buf)); + if (!ASSERT_EQ(err, 6, "read_hidraw")) + goto cleanup; + + if (!ASSERT_EQ(buf[1], 1, "hid_test_insert1")) + goto cleanup; + + if (!ASSERT_EQ(buf[2], 2, "hid_test_insert2")) + goto cleanup; + + if (!ASSERT_EQ(buf[3], 3, "hid_test_insert3")) + goto cleanup; + + err = 0; + + cleanup: + if (hidraw_fd >= 0) + close(hidraw_fd); + + hid__destroy(hid_skel); + + return err; +} + /* * Attach hid_rdesc_fixup to the given uhid device, * retrieve and open the matching hidraw node, @@ -866,6 +971,8 @@ void serial_test_hid_bpf(void) ASSERT_OK(err, "hid_change_report"); err = test_hid_user_raw_request_call(uhid_fd, dev_id); ASSERT_OK(err, "hid_change_report"); + err = test_hid_attach_flags(uhid_fd, dev_id); + ASSERT_OK(err, "hid_user_raw_request"); /* * this test should be run last because we disconnect/reconnect diff --git a/tools/testing/selftests/bpf/progs/hid.c b/tools/testing/selftests/bpf/progs/hid.c index eda9306e6d7f..43724fd26fb9 100644 --- a/tools/testing/selftests/bpf/progs/hid.c +++ b/tools/testing/selftests/bpf/progs/hid.c @@ -21,6 +21,7 @@ extern int hid_bpf_hw_request(struct hid_bpf_ctx *ctx, struct attach_prog_args { int prog_fd; unsigned int hid; + unsigned int flags; int retval; }; @@ -60,7 +61,7 @@ int attach_prog(struct attach_prog_args *ctx) { ctx->retval = hid_bpf_attach_prog(ctx->hid, ctx->prog_fd, - 0); + ctx->flags); return 0; } @@ -168,3 +169,54 @@ int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hid_ctx) return sizeof(rdesc) + 73; } + +SEC("?fmod_ret/hid_bpf_device_event") +int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx) +{ + __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); + + if (!data) + return 0; /* EPERM check */ + + /* we need to be run first */ + if (data[2] || data[3]) + return -1; + + data[1] = 1; + + return 0; +} + +SEC("?fmod_ret/hid_bpf_device_event") +int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx) +{ + __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); + + if (!data) + return 0; /* EPERM check */ + + /* after insert0 and before insert2 */ + if (!data[1] || data[3]) + return -1; + + data[2] = 2; + + return 0; +} + +SEC("?fmod_ret/hid_bpf_device_event") +int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx) +{ + __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); + + if (!data) + return 0; /* EPERM check */ + + /* at the end */ + if (!data[1] || !data[2]) + return -1; + + data[3] = 3; + + return 0; +} From patchwork Wed May 18 20:59:24 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Tissoires X-Patchwork-Id: 12854113 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id F0457C43217 for ; Wed, 18 May 2022 21:01:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S243068AbiERVBz (ORCPT ); Wed, 18 May 2022 17:01:55 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34736 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S243091AbiERVBJ (ORCPT ); Wed, 18 May 2022 17:01:09 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 7E58313CEF for ; Wed, 18 May 2022 14:00:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1652907647; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=giY7Otcl1eOFxaYtRhGHE8IaPTXBsTDzw6ErsizqXsg=; b=d/Hy9/DpaOx+xGRFbsFbGfE6MvyviJ0tiBrpodNE3NSI2/9zgxen8e+xN2/Em5S+MPq705 4+nPmeghwScaCP5H/Az4uULfyjjH7w2+4BLP9lBYPOrOXhKB3zP291guDP5bItY44tQRrt pGTQa4hLBwBHeJ5cRIVD8LaWSgMyyNI= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-231-kwtze9R2NLO_UAwLcmlYAA-1; Wed, 18 May 2022 17:00:42 -0400 X-MC-Unique: kwtze9R2NLO_UAwLcmlYAA-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id C2BDC380670A; Wed, 18 May 2022 21:00:40 +0000 (UTC) Received: from plouf.redhat.com (unknown [10.39.192.58]) by smtp.corp.redhat.com (Postfix) with ESMTP id 69ACD2166B40; Wed, 18 May 2022 21:00:37 +0000 (UTC) From: Benjamin Tissoires To: Greg KH , Jiri Kosina , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Shuah Khan , Dave Marchevsky , Joe Stringer , Jonathan Corbet Cc: Tero Kristo , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Benjamin Tissoires Subject: [PATCH bpf-next v5 17/17] Documentation: add HID-BPF docs Date: Wed, 18 May 2022 22:59:24 +0200 Message-Id: <20220518205924.399291-18-benjamin.tissoires@redhat.com> In-Reply-To: <20220518205924.399291-1-benjamin.tissoires@redhat.com> References: <20220518205924.399291-1-benjamin.tissoires@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Gives a primer on HID-BPF. Signed-off-by: Benjamin Tissoires --- changes in v5: - amended for new API - reworded most of the sentences (thanks to Peter Hutterer for the review) changes in v4: - fixed typos new in v3 --- Documentation/hid/hid-bpf.rst | 528 ++++++++++++++++++++++++++++++++++ Documentation/hid/index.rst | 1 + 2 files changed, 529 insertions(+) create mode 100644 Documentation/hid/hid-bpf.rst diff --git a/Documentation/hid/hid-bpf.rst b/Documentation/hid/hid-bpf.rst new file mode 100644 index 000000000000..af05667e72bb --- /dev/null +++ b/Documentation/hid/hid-bpf.rst @@ -0,0 +1,528 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======= +HID-BPF +======= + +HID is a standard protocol for input devices but some devices may require +custom tweaks, traditionally done with a kernel driver fix. Using the eBPF +capabilities instead speeds up development and adds new capabilities to the +existing HID interfaces. + +.. contents:: + :local: + :depth: 2 + + +When (and why) to use HID-BPF +============================= + +We can enumerate several use cases for when using HID-BPF is better than +using a standard kernel driver fix: + +Dead zone of a joystick +----------------------- + +Assuming you have a joystick that is getting older, it is common to see it +wobbling around its neutral point. This is usually filtered at the application +level by adding a *dead zone* for this specific axis. + +With HID-BPF, we can apply this filtering in the kernel directly so userspace +does not get woken up when nothing else is happening on the input controller. + +Of course, given that this dead zone is specific to an individual device, we +can not create a generic fix for all of the same joysticks. Adding a custom +kernel API for this (e.g. by adding a sysfs entry) does not guarantee this new +kernel API will be broadly adopted and maintained. + +HID-BPF allows the userspace program to load the program itself, ensuring we +only load the custom API when we have a user. + +Simple fixup of report descriptor +--------------------------------- + +In the HID tree, half of the drivers only fix one key or one byte +in the report descriptor. These fixes all require a kernel patch and the +subsequent shepherding into a release, a long and painful process for users. + +We can reduce this burden by providing an eBPF program instead. Once such a +program has been verified by the user, we can embed the source code into the +kernel tree and ship the eBPF program and load it directly instead of loading +a specific kernel module for it. + +Note: distribution of eBPF programs and their inclusion in the kernel is not +yet fully implemented + +Add a new feature that requires a new kernel API +------------------------------------------------ + +An example for such a feature are the Universal Stylus Interface (USI) pens. +Basically, USI pens require a new kernel API because there are new +channels of communication that our HID and input stack do not support. +Instead of using hidraw or creating new sysfs entries or ioctls, we can rely +on eBPF to have the kernel API controlled by the consumer and to not +impact the performances by waking up userspace every time there is an +event. + +Morph a device into something else and control that from userspace +------------------------------------------------------------------ + +The kernel has a relatively static mapping of HID items to evdev bits. +It cannot decide to dynamically transform a given device into something else +as it does not have the required context and any such transformation cannot be +undone (or even discovered) by userspace. + +However, some devices are useless with that static way of defining devices. For +example, the Microsoft Surface Dial is a pushbutton with haptic feedback that +is barely usable as of today. + +With eBPF, userspace can morph that device into a mouse, and convert the dial +events into wheel events. Also, the userspace program can set/unset the haptic +feedback depending on the context. For example, if a menu is visible on the +screen we likely need to have a haptic click every 15 degrees. But when +scrolling in a web page the user experience is better when the device emits +events at the highest resolution. + +Firewall +-------- + +What if we want to prevent other users to access a specific feature of a +device? (think a possibly broken firmware update entry point) + +With eBPF, we can intercept any HID command emitted to the device and +validate it or not. + +This also allows to sync the state between the userspace and the +kernel/bpf program because we can intercept any incoming command. + +Tracing +------- + +The last usage is tracing events and all the fun we can do we BPF to summarize +and analyze events. + +Right now, tracing relies on hidraw. It works well except for a couple +of issues: + +1. if the driver doesn't export a hidraw node, we can't trace anything + (eBPF will be a "god-mode" there, so this may raise some eyebrows) +2. hidraw doesn't catch other processes' requests to the device, which + means that we have cases where we need to add printks to the kernel + to understand what is happening. + +High-level view of HID-BPF +========================== + +The main idea behind HID-BPF is that it works at an array of bytes level. +Thus, all of the parsing of the HID report and the HID report descriptor +must be implemented in the userspace component that loads the eBPF +program. + +For example, in the dead zone joystick from above, knowing which fields +in the data stream needs to be set to ``0`` needs to be computed by userspace. + +A corollary of this is that HID-BPF doesn't know about the other subsystems +available in the kernel. *You can not directly emit input event through the +input API from eBPF*. + +When a BPF program needs to emit input events, it needs to talk HID, and rely +on the HID kernel processing to translate the HID data into input events. + +Available types of programs +=========================== + +HID-BPF is built "on top" of BPF, meaning that we use tracing method to +declare our programs. + +HID-BPF has the following attachment types available: + +1. event processing/filtering with ``SEC("fmod_ret/hid_bpf_device_event")`` in libbpf +2. actions coming from userspace with ``SEC("syscall")`` in libbpf +3. change of the report descriptor with ``SEC("fmod_ret/hid_bpf_rdesc_fixup")`` in libbpf + +A ``hid_bpf_device_event`` is calling a BPF program when an event is received from +the device. Thus we are in IRQ context and can act on the data or notify userspace. +And given that we are in IRQ context, we can not talk back to the device. + +A ``syscall`` means that userspace called the syscall ``BPF_PROG_RUN`` facility. +This time, we can do any operations allowed by HID-BPF, and talking to the device is +allowed. + +Last, ``hid_bpf_rdesc_fixup`` is different from the others as there can be only one +BPF program of this type. This is called on ``probe`` from the driver and allows to +change the report descriptor from the BPF program. Once a ``hid_bpf_rdesc_fixup`` +program has been loaded, it is not possible to overwrite it unless the program which +inserted it allows us by pinning the program and closing all of its fds pointing to it. + +Developer API: +============== + +User API data structures available in programs: +----------------------------------------------- + +.. kernel-doc:: include/uapi/linux/hid_bpf.h +.. kernel-doc:: include/linux/hid_bpf.h + +Available tracing functions to attach a HID-BPF program: +-------------------------------------------------------- + +.. kernel-doc:: drivers/hid/bpf/hid_bpf_dispatch.c + :functions: hid_bpf_device_event hid_bpf_rdesc_fixup + +Available API that can be used in all HID-BPF programs: +------------------------------------------------------- + +.. kernel-doc:: drivers/hid/bpf/hid_bpf_dispatch.c + :functions: hid_bpf_get_data + +Available API that can be used in syscall HID-BPF programs: +----------------------------------------------------------- + +.. kernel-doc:: drivers/hid/bpf/hid_bpf_dispatch.c + :functions: hid_bpf_attach_prog hid_bpf_hw_request hid_bpf_allocate_context hid_bpf_release_context + +General overview of a HID-BPF program +===================================== + +Accessing the data attached to the context +------------------------------------------ + +The ``struct hid_bpf_ctx`` doesn't export the ``data`` fields directly and to access +it, a bpf program needs to first call :c:func:`hid_bpf_get_data`. + +``offset`` can be any integer, but ``size`` needs to be constant, known at compile +time. + +This allows the following: + +1. for a given device, if we know that the report length will always be of a certain value, + we can request the ``data`` pointer to point at the full report length. + + The kernel will ensure we are using a correct size and offset and eBPF will ensure + the code will not attempt to read or write outside of the boundaries:: + + __u8 *data = hid_bpf_get_data(ctx, 0 /* offset */, 256 /* size */); + + if (!data) + return 0; /* ensure data is correct, now the verifier knows we + * have 256 bytes available */ + + bpf_printk("hello world: %02x %02x %02x", data[0], data[128], data[255]); + +2. if the report length is variable, but we know the value of ``X`` is always a 16-bit + integer, we can then have a pointer to that value only:: + + __u16 *x = hid_bpf_get_data(ctx, offset, sizeof(*x)); + + if (!x) + return 0; /* something went wrong */ + + *x += 1; /* increment X by one */ + +Effect of a HID-BPF program +--------------------------- + +For all HID-BPF attachment types except for :c:func:`hid_bpf_rdesc_fixup`, several eBPF +programs can be attached to the same device. + +Unless ``HID_BPF_FLAG_INSERT_HEAD`` is added to the flags while attaching the +program, the new program is appended at the end of the list. +``HID_BPF_FLAG_INSERT_HEAD`` will insert the new program at the beginning of the +list which is useful for e.g. tracing where we need to get the unprocessed events +from the device. + +Note that if there are multiple programs using the ``HID_BPF_FLAG_INSERT_HEAD`` flag, +only the most recently loaded one is actually the first in the list. + +``SEC("fmod_ret/hid_bpf_device_event")`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Whenever a matching event is raised, the eBPF programs are called one after the other +and are working on the same data buffer. + +If a program changes the data associated with the context, the next one will see +the modified data but it will have *no* idea of what the original data was. + +Once all the programs are run and return ``0`` or a positive value, the rest of the +HID stack will work on the modified data, with the ``size`` field of the last hid_bpf_ctx +being the new size of the input stream of data. + +A BPF program returning a negative error discards the event, i.e. this event will not be +processed by the HID stack. Clients (hidraw, input, LEDs) will **not** see this event. + +``SEC("syscall")`` +~~~~~~~~~~~~~~~~~~ + +``syscall`` are not attached to a given device. To tell which device we are working +with, userspace needs to refer to the device by its unique system id (the last 4 numbers +in the sysfs path: ``/sys/bus/hid/devices/xxxx:yyyy:zzzz:0000``). + +To retrieve a context associated with the device, the program must call +:c:func:`hid_bpf_allocate_context` and must release it with :c:func:`hid_bpf_release_context` +before returning. +Once the context is retrieved, one can also request a pointer to kernel memory with +:c:func:`hid_bpf_get_data`. This memory is big enough to support all input/output/feature +reports of the given device. + +``SEC("fmod_ret/hid_bpf_rdesc_fixup")`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``hid_bpf_rdesc_fixup`` program works in a similar manner to +``.report_fixup`` of ``struct hid_driver``. + +When the device is probed, the kernel sets the data buffer of the context with the +content of the report descriptor. The memory associated with that buffer is +``HID_MAX_DESCRIPTOR_SIZE`` (currently 4kB). + +The eBPF program can modify the data buffer at-will and the kernel uses the +modified content and size as the report descriptor. + +Whenever a ``SEC("fmod_ret/hid_bpf_rdesc_fixup")`` program is attached (if no +program was attached before), the kernel immediately disconnects the HID device +and does a reprobe. + +In the same way, when the ``SEC("fmod_ret/hid_bpf_rdesc_fixup")`` program is +detached, the kernel issues a disconnect on the device. + +There is no ``detach`` facility in HID-BPF. Detaching a program happens when +all the user space file descriptors pointing at a program are closed. +Thus, if we need to replace a report descriptor fixup, some cooperation is +required from the owner of the original report descriptor fixup. +The previous owner will likely pin the program in the bpffs, and we can then +replace it through normal bpf operations. + +Attaching a bpf program to a device +=================================== + +``libbpf`` does not export any helper to attach a HID-BPF program. +Users need to use a dedicated ``syscall`` program which will call +``hid_bpf_attach_prog(hid_id, program_fd, flags)``. + +``hid_id`` is the unique system ID of the HID device (the last 4 numbers in the +sysfs path: ``/sys/bus/hid/devices/xxxx:yyyy:zzzz:0000``) + +``progam_fd`` is the opened file descriptor of the program to attach. + +``flags`` is of type ``enum hid_bpf_attach_flags``. + +We can not rely on hidraw to bind a BPF program to a HID device. hidraw is an +artefact of the processing of the HID device, and is not stable. Some drivers +even disable it, so that removes the tracing capabilies on those devices +(where it is interesting to get the non-hidraw traces). + +On the other hand, the ``hid_id`` is stable for the entire life of the HID device, +even if we change its report descriptor. + +Given that hidraw is not stable when the device disconnects/reconnects, we recommend +accessing the current report descriptor of the device through the sysfs. +This is available at ``/sys/bus/hid/devices/BUS:VID:PID.000N/report_descriptor`` as a +binary stream. + +Parsing the report descriptor is the responsibility of the BPF programmer or the userspace +component that loads the eBPF program. + +An (almost) complete example of a BPF enhanced HID device +========================================================= + +*Foreword: for most parts, this could be implemented as a kernel driver* + +Let's imagine we have a new tablet device that has some haptic capabilities +to simulate the surface the user is scratching on. This device would also have +a specific 3 positions switch to toggle between *pencil on paper*, *cray on a wall* +and *brush on a painting canvas*. To make things even better, we can control the +physical position of the switch through a feature report. + +And of course, the switch is relying on some userspace component to control the +haptic feature of the device itself. + +Filtering events +---------------- + +The first step consists in filtering events from the device. Given that the switch +position is actually reported in the flow of the pen events, using hidraw to implement +that filtering would mean that we wake up userspace for every single event. + +This is OK for libinput, but having an external library that is just interested in +one byte in the report is less than ideal. + +For that, we can create a basic skeleton for our BPF program:: + + #include "vmlinux.h" + #include + #include + + /* HID programs need to be GPL */ + char _license[] SEC("license") = "GPL"; + + /* HID-BPF kfunc API definitions */ + extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx, + unsigned int offset, + const size_t __sz) __ksym; + extern int hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, u32 flags) __ksym; + + struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 4096 * 64); + } ringbuf SEC(".maps"); + + struct attach_prog_args { + int prog_fd; + unsigned int hid; + unsigned int flags; + int retval; + }; + + SEC("syscall") + int attach_prog(struct attach_prog_args *ctx) + { + ctx->retval = hid_bpf_attach_prog(ctx->hid, + ctx->prog_fd, + ctx->flags); + return 0; + } + + __u8 current_value = 0; + + SEC("?fmod_ret/hid_bpf_device_event") + int BPF_PROG(filter_switch, struct hid_bpf_ctx *hid_ctx) + { + __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 192 /* size */); + __u8 *buf; + + if (!data) + return 0; /* EPERM check */ + + if (current_value != data[152]) { + buf = bpf_ringbuf_reserve(&ringbuf, 1, 0); + if (!buf) + return 0; + + *buf = data[152]; + + bpf_ringbuf_commit(buf, 0); + + current_value = data[152]; + } + + return 0; + } + +To attach ``filter_switch``, userspace needs to call the ``attach_prog`` syscall +program first:: + + static int attach_filter(struct hid *hid_skel, int hid_id) + { + int err, prog_fd; + int ret = -1; + struct attach_prog_args args = { + .hid = hid_id, + }; + DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs, + .ctx_in = &args, + .ctx_size_in = sizeof(args), + ); + + args.prog_fd = bpf_program__fd(hid_skel->progs.filter_switch); + + prog_fd = bpf_program__fd(hid_skel->progs.attach_prog); + + err = bpf_prog_test_run_opts(prog_fd, &tattrs); + return err; + } + +Our userspace program can now listen to notifications on the ring buffer, and +is awaken only when the value changes. + +Controlling the device +---------------------- + +To be able to change the haptic feedback from the tablet, the userspace program +needs to emit a feature report on the device itself. + +Instead of using hidraw for that, we can create a ``SEC("syscall")`` program +that talks to the device:: + + /* some more HID-BPF kfunc API definitions */ + 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 len, + enum hid_report_type type, + int reqtype) __ksym; + + + struct hid_send_haptics_args { + /* data needs to come at offset 0 so we can do a memcpy into it */ + __u8 data[10]; + unsigned int hid; + }; + + SEC("syscall") + int send_haptic(struct hid_send_haptics_args *args) + { + struct hid_bpf_ctx *ctx; + int i, ret = 0; + __u8 *data; + + ctx = hid_bpf_allocate_context(args->hid); + if (!ctx) + return 0; /* EPERM check */ + + /* We can not use the context data memory directly in the hid_bpf call, + * so we rely on the PTR_TO_MEM allocated in the hid_bpf_context + */ + data = hid_bpf_get_data(ctx, 0 /* offset */, 10 /* size */); + if (!data) + goto out; /* EPERM check */ + + __builtin_memcpy(data, args->data, sizeof(args->data)); + + ret = hid_bpf_hw_request(ctx, + data, + 10, + HID_FEATURE_REPORT, + HID_REQ_SET_REPORT); + if (ret < 0) + goto out; + + /* not strictly required here because we did a SET_REPORT */ + __builtin_memcpy(args->data, data, sizeof(args->data)); + + out: + hid_bpf_release_context(ctx); + + return ret; + } + +And then userspace needs to call that program directly:: + + static int set_haptic(struct hid *hid_skel, int hid_id, __u8 haptic_value) + { + int err, prog_fd; + int ret = -1; + struct hid_send_haptics_args args = { + .hid = hid_id, + }; + DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs, + .ctx_in = &args, + .ctx_size_in = sizeof(args), + ); + + args.data[0] = 0x02; /* report ID of the feature on our device */ + args.data[1] = haptic_value; + + prog_fd = bpf_program__fd(hid_skel->progs.set_haptic); + + err = bpf_prog_test_run_opts(prog_fd, &tattrs); + return err; + } + +Now our userspace program is aware of the haptic state and can control it. The +program could make this state further available to other userspace programs +(e.g. via a DBus API). + +The interesting bit here is that we did not created a new kernel API for this. +Which means that if there is a bug in our implementation, we can change the +interface with the kernel at-will, because the userspace application is +responsible for its own usage. diff --git a/Documentation/hid/index.rst b/Documentation/hid/index.rst index e50f513c579c..b2028f382f11 100644 --- a/Documentation/hid/index.rst +++ b/Documentation/hid/index.rst @@ -11,6 +11,7 @@ Human Interface Devices (HID) hidraw hid-sensor hid-transport + hid-bpf uhid