Add BTF_KIND_LOC_PARAM, BTF_KIND_LOC_PROTO and BTF_KIND_LOCSEC
to help represent location information for functions.

BTF_KIND_LOC_PARAM is used to represent how we retrieve data at a
location; either via register(s), or register+offset, a dereference
of a register+offset or a constant value.

BTF_KIND_LOC_PROTO represents location information about a location
with multiple BTF_KIND_LOC_PARAMs.

And finally BTF_KIND_LOCSEC is a set of location sites, each
of which has

- a BTF_KIND_FUNC function associated with the inline site
- a location prototype specifying where to find the function
  parameters
- an address offset relative to the kernel base address

This can be used to support representing

- a fully-inlined function at potentially multiple inline sites
  with potentially different parameter availability
- a partially-inlined function where some _LOC_PROTOs represent
  inlined sites as above and others have normal _FUNC representations

Also BTF_KIND_LOCSEC struct btf_loc will have two type id
references; one for the associated func, the other for the loc_proto.
Accordingly increase the number of m_offs references in btf_field_desc
to 2.

Signed-off-by: Alan Maguire <[email protected]>
---
 include/linux/btf.h            |  17 ++-
 include/uapi/linux/btf.h       |  65 ++++++++-
 kernel/bpf/btf.c               | 238 ++++++++++++++++++++++++++++++++-
 tools/include/uapi/linux/btf.h |  65 ++++++++-
 4 files changed, 379 insertions(+), 6 deletions(-)

diff --git a/include/linux/btf.h b/include/linux/btf.h
index ddd0f4f32d24..a4412bc16688 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -329,6 +329,21 @@ static inline u64 btf_enum64_value(const struct btf_enum64 
*e)
        return ((u64)e->val_hi32 << 32) | e->val_lo32;
 }
 
+static inline struct btf_loc_param *btf_loc_param(const struct btf_type *t)
+{
+       return (struct btf_loc_param *)(t + 1);
+}
+
+static inline __u32 *btf_loc_params(const struct btf_type *t)
+{
+       return (__u32 *)(t + 1);
+}
+
+static inline struct btf_loc *btf_type_loc_secinfo(const struct btf_type *t)
+{
+       return (struct btf_loc *)(t + 1);
+}
+
 static inline bool btf_is_composite(const struct btf_type *t)
 {
        u16 kind = btf_kind(t);
@@ -559,7 +574,7 @@ struct btf_field_desc {
        /* member struct size, or zero, if no members */
        int m_sz;
        /* repeated per-member offsets */
-       int m_off_cnt, m_offs[1];
+       int m_off_cnt, m_offs[2];
 };
 
 struct btf_field_iter {
diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
index 618167cab4e6..6062c9958034 100644
--- a/include/uapi/linux/btf.h
+++ b/include/uapi/linux/btf.h
@@ -92,7 +92,9 @@ enum {
        BTF_KIND_DECL_TAG       = 17,   /* Decl Tag */
        BTF_KIND_TYPE_TAG       = 18,   /* Type Tag */
        BTF_KIND_ENUM64         = 19,   /* Enumeration up to 64-bit values */
-
+       BTF_KIND_LOC_PARAM      = 20,   /* Location parameter information */
+       BTF_KIND_LOC_PROTO      = 21,   /* Location prototype for site */
+       BTF_KIND_LOCSEC         = 22,   /* Location section */
        NR_BTF_KINDS,
        BTF_KIND_MAX            = NR_BTF_KINDS - 1,
 };
@@ -212,4 +214,65 @@ struct btf_enum64 {
        __u32   val_hi32;
 };
 
+/*
+ * BTF_KIND_LOC_PARAM is followed by a single "struct btf_loc_param"
+ * that contains flags specifying the contents of the vlen-specified
+ * number of 4-byte values that follow.
+ */
+struct btf_loc_param {
+       __u32 flags;
+};
+
+/*
+ * The combination of size, vlen and flags gives us the means to interpret
+ * the following vlen-specified set of 4-byte values:
+ *
+ * - a BTF_LOC_PARAM_CONST is a constant value; combination
+ *   of size, vlen and _SIGNED flag determines it. If the value requires
+ *   64 bits it is stored in {lo,hi} order.
+ * - a BTF_LOC_PARAM_ADDR is an address that will be normalized with
+ *   respect to kernel base address.
+ * - a BTF_LOC_PARAM_REG with vlen 1 is a simple register number;
+ *   with vlen 2 it is a multi-register parameter.
+ * - a _REG | DEREF with vlen 1 dereferences the value in the register
+ *   number specified.
+ * - a REG | DEREF | OFFSET with vlen specifies the register value in
+ *   the first 4-byte value and the offset in the remainder.
+ * - binary logical operators operate on a combination of register
+ *   number and constant value, aside from _NOT which operates on
+ *   a register
+ */
+enum btf_loc_param_flags {
+       BTF_LOC_PARAM_SIGNED            =       0x1,
+       BTF_LOC_PARAM_CONST             =       0x2,
+       BTF_LOC_PARAM_ADDR              =       0x4,
+       BTF_LOC_PARAM_REG               =       0x8,
+       BTF_LOC_PARAM_DEREF             =       0x10,
+       BTF_LOC_PARAM_OFFSET            =       0x20,
+};
+
+/*
+ * BTF_KIND_LOC_PROTO specifies location prototypes; i.e. how locations relate
+ * to parameters; a struct btf_type of BTF_KIND_LOC_PROTO is followed by a
+ * a vlen-specified number of __u32 BTF type ids which specify the associated
+ * BTF_KIND_LOC_PARAM for each function parameter associated with the
+ * location.  The type should either be 0 (no location info) or point at
+ * a BTF_KIND_LOC_PARAM.
+ */
+
+/*
+ * BTF_KIND_LOCSEC consists of vlen-specified number of "struct btf_loc"
+ * containing location site-specific information;
+ *
+ * - function (func)
+ * - location prototype type id (loc_proto)
+ * - address offset (offset) relative to kernel base address
+ */
+
+struct btf_loc {
+       __u32 func;
+       __u32 loc_proto;
+       __u32 offset;
+};
+
 #endif /* _UAPI__LINUX_BTF_H__ */
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 9c2cab08bb79..d74c8668aa3f 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -345,6 +345,9 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
        [BTF_KIND_DECL_TAG]     = "DECL_TAG",
        [BTF_KIND_TYPE_TAG]     = "TYPE_TAG",
        [BTF_KIND_ENUM64]       = "ENUM64",
+       [BTF_KIND_LOC_PARAM]    = "LOC_PARAM",
+       [BTF_KIND_LOC_PROTO]    = "LOC_PROTO",
+       [BTF_KIND_LOCSEC]       = "LOCSEC",
 };
 
 const char *btf_type_str(const struct btf_type *t)
@@ -517,11 +520,27 @@ static bool btf_type_is_decl_tag(const struct btf_type *t)
        return BTF_INFO_KIND(t->info) == BTF_KIND_DECL_TAG;
 }
 
+static bool btf_type_is_loc_param(const struct btf_type *t)
+{
+       return BTF_INFO_KIND(t->info) == BTF_KIND_LOC_PARAM;
+}
+
+static bool btf_type_is_loc_proto(const struct btf_type *t)
+{
+       return BTF_INFO_KIND(t->info) == BTF_KIND_LOC_PROTO;
+}
+
+static bool btf_type_is_locsec(const struct btf_type *t)
+{
+       return BTF_INFO_KIND(t->info) == BTF_KIND_LOCSEC;
+}
+
 static bool btf_type_nosize(const struct btf_type *t)
 {
        return btf_type_is_void(t) || btf_type_is_fwd(t) ||
               btf_type_is_func(t) || btf_type_is_func_proto(t) ||
-              btf_type_is_decl_tag(t);
+              btf_type_is_decl_tag(t) || btf_type_is_loc_param(t) ||
+              btf_type_is_loc_proto(t) || btf_type_is_locsec(t);
 }
 
 static bool btf_type_nosize_or_null(const struct btf_type *t)
@@ -796,7 +815,9 @@ static bool btf_type_needs_resolve(const struct btf_type *t)
               btf_type_is_var(t) ||
               btf_type_is_func(t) ||
               btf_type_is_decl_tag(t) ||
-              btf_type_is_datasec(t);
+              btf_type_is_datasec(t) ||
+              btf_type_is_loc_proto(t) ||
+              btf_type_is_locsec(t);
 }
 
 /* t->size can be used */
@@ -4714,6 +4735,213 @@ static const struct btf_kind_operations enum64_ops = {
        .show = btf_enum64_show,
 };
 
+static s32 btf_loc_param_check_meta(struct btf_verifier_env *env,
+                                   const struct btf_type *t,
+                                   u32 meta_left)
+{
+       const struct btf_loc_param *p = btf_loc_param(t);
+       u32 meta_needed;
+       u32 size;
+
+       meta_needed = sizeof(*p) + sizeof(__u32) * btf_vlen(t);
+       if (meta_left < meta_needed) {
+               btf_verifier_log_basic(env, t,
+                                      "meta_left:%u meta_needed:%u",
+                                     meta_left, meta_needed);
+               return -EINVAL;
+       }
+
+       if (t->name_off) {
+               btf_verifier_log_type(env, t, "Invalid name");
+               return -EINVAL;
+       }
+       size = t->size;
+       if (size > 16 || !is_power_of_2(size)) {
+               btf_verifier_log_type(env, t, "Unexpected size");
+               return -EINVAL;
+       }
+
+       return meta_needed;
+}
+
+static void btf_loc_param_log(struct btf_verifier_env *env,
+                        const struct btf_type *t)
+{
+       const struct btf_loc_param *p = btf_loc_param(t);
+       u32 *d = (u32 *)(p + 1);
+       u32 i, vlen = btf_vlen(t);
+
+       btf_verifier_log(env, "size=%u flags=0x%x param_data(", t->size, 
p->flags);
+       for (i = 0; i < vlen; i++, d++)
+               btf_verifier_log(env, "%u%s", *d,
+                                i + 1 == vlen ? ")" : ", ");
+}
+
+static const struct btf_kind_operations loc_param_ops = {
+       .check_meta = btf_loc_param_check_meta,
+       .resolve = btf_df_resolve,
+       .check_member = btf_df_check_member,
+       .check_kflag_member = btf_df_check_kflag_member,
+       .log_details = btf_loc_param_log,
+       .show = btf_df_show,
+};
+
+static s32 btf_loc_proto_check_meta(struct btf_verifier_env *env,
+                                   const struct btf_type *t,
+                                   u32 meta_left)
+{
+       u32 meta_needed;
+
+       meta_needed = sizeof(__u32) * btf_type_vlen(t);
+
+       if (meta_left < meta_needed) {
+               btf_verifier_log_basic(env, t,
+                                      "meta_left:%u meta_needed:%u",
+                                     meta_left, meta_needed);
+               return -EINVAL;
+       }
+
+       if (t->name_off) {
+               btf_verifier_log_type(env, t, "Invalid name");
+               return -EINVAL;
+       }
+       return meta_needed;
+}
+
+static void btf_loc_proto_log(struct btf_verifier_env *env,
+                             const struct btf_type *t)
+{
+       const __u32 *params = btf_loc_params(t);
+       u32 nr_params = btf_type_vlen(t), i;
+
+       btf_verifier_log(env, "loc_proto locs=(");
+       for (i = 0; i < nr_params; i++, params++) {
+               btf_verifier_log(env, "type=%u%s", *params,
+                                i + 1 == nr_params ? ")" : ", ");
+       }
+}
+
+static int btf_loc_proto_resolve(struct btf_verifier_env *env,
+                                const struct resolve_vertex *v)
+{
+       const __u32 *params = btf_loc_params(v->t);
+       u32 i, nr_params = btf_type_vlen(v->t);
+       struct btf *btf = env->btf;
+
+       for (i = 0; i < nr_params; i++) {
+               const struct btf_type *param_type;
+               u32 param_type_id = params[i];
+
+               if (!param_type_id)
+                       continue;
+
+               param_type = btf_type_by_id(btf, param_type_id);
+               if (!param_type || !btf_type_is_loc_param(param_type)) {
+                       btf_verifier_log_type(env, v->t,
+                                             "Invalid loc_param#%u", i + 1);
+                       return -EINVAL;
+               }
+       }
+
+       env_stack_pop_resolved(env, 0, 0);
+       return 0;
+}
+
+static const struct btf_kind_operations loc_proto_ops = {
+       .check_meta = btf_loc_proto_check_meta,
+       .resolve = btf_loc_proto_resolve,
+       .check_member = btf_df_check_member,
+       .check_kflag_member = btf_df_check_kflag_member,
+       .log_details = btf_loc_proto_log,
+       .show = btf_df_show,
+};
+
+static s32 btf_locsec_check_meta(struct btf_verifier_env *env,
+                                const struct btf_type *t,
+                                u32 meta_left)
+{
+       u32 meta_needed;
+
+       meta_needed = sizeof(struct btf_loc) * btf_type_vlen(t);
+
+       if (meta_left < meta_needed) {
+               btf_verifier_log_basic(env, t,
+                                      "meta_left:%u meta_needed:%u",
+                                      meta_left, meta_needed);
+               return -EINVAL;
+       }
+       return meta_needed;
+}
+
+static void btf_locsec_log(struct btf_verifier_env *env,
+                          const struct btf_type *t)
+{
+       const struct btf_loc *loc = btf_type_loc_secinfo(t);
+       u32 nr_locs = btf_type_vlen(t), i;
+       const struct btf *btf = env->btf;
+
+       btf_verifier_log(env, "locsec %s locs=(",
+                        __btf_name_by_offset(btf, t->name_off));
+       for (i = 0; i < nr_locs; i++, loc++) {
+               btf_verifier_log(env, "\n\tfunc %u loc_proto %u offset 0x%x%s",
+                                loc->func, loc->loc_proto, loc->offset,
+                                i + 1 == nr_locs ? ")" : ", ");
+       }
+}
+
+static int btf_locsec_resolve(struct btf_verifier_env *env,
+                             const struct resolve_vertex *v)
+{
+       const struct btf_loc *loc;
+       struct btf *btf = env->btf;
+       u32 i;
+
+       env->resolve_mode = RESOLVE_TBD;
+       for (i = v->next_member, loc = btf_type_loc_secinfo(v->t) + i;
+            i < btf_type_vlen(v->t); i++, loc++) {
+               const struct btf_type *func_type, *loc_proto_type;
+               u32 func_type_id = loc->func;
+               u32 loc_proto_type_id = loc->loc_proto;
+
+               func_type = btf_type_by_id(btf, func_type_id);
+               if (!func_type || !btf_type_is_func(func_type)) {
+                       btf_verifier_log_type(env, v->t,
+                                             "Invalid func#%u", i + 1);
+                       return -EINVAL;
+               }
+
+               if (!env_type_is_resolved(env, func_type_id)) {
+                       env_stack_set_next_member(env, i);
+                       return env_stack_push(env, func_type, func_type_id);
+               }
+
+               loc_proto_type = btf_type_by_id(btf, loc_proto_type_id);
+               if (!loc_proto_type || !btf_type_is_loc_proto(loc_proto_type)) {
+                       btf_verifier_log_type(env, v->t,
+                                             "Invalid loc_proto#%u", i + 1);
+                       return -EINVAL;
+               }
+
+               if (!env_type_is_resolved(env, loc_proto_type_id)) {
+                       env_stack_set_next_member(env, i + 1);
+                       return env_stack_push(env, loc_proto_type,
+                                             loc_proto_type_id);
+               }
+       }
+
+       env_stack_pop_resolved(env, 0, 0);
+       return 0;
+}
+
+static const struct btf_kind_operations locsec_ops = {
+       .check_meta = btf_locsec_check_meta,
+       .resolve = btf_locsec_resolve,
+       .check_member = btf_df_check_member,
+       .check_kflag_member = btf_df_check_kflag_member,
+       .log_details = btf_locsec_log,
+       .show = btf_df_show,
+};
+
 static s32 btf_func_proto_check_meta(struct btf_verifier_env *env,
                                     const struct btf_type *t,
                                     u32 meta_left)
@@ -5383,6 +5611,9 @@ static const struct btf_kind_operations * const 
kind_ops[NR_BTF_KINDS] = {
        [BTF_KIND_DECL_TAG] = &decl_tag_ops,
        [BTF_KIND_TYPE_TAG] = &modifier_ops,
        [BTF_KIND_ENUM64] = &enum64_ops,
+       [BTF_KIND_LOC_PARAM] = &loc_param_ops,
+       [BTF_KIND_LOC_PROTO] = &loc_proto_ops,
+       [BTF_KIND_LOCSEC] = &locsec_ops,
 };
 
 static s32 btf_check_meta(struct btf_verifier_env *env,
@@ -5457,7 +5688,8 @@ static bool btf_resolve_valid(struct btf_verifier_env 
*env,
        if (!env_type_is_resolved(env, type_id))
                return false;
 
-       if (btf_type_is_struct(t) || btf_type_is_datasec(t))
+       if (btf_type_is_struct(t) || btf_type_is_datasec(t) ||
+           btf_type_is_loc_proto(t) || btf_type_is_locsec(t))
                return !btf_resolved_type_id(btf, type_id) &&
                       !btf_resolved_type_size(btf, type_id);
 
diff --git a/tools/include/uapi/linux/btf.h b/tools/include/uapi/linux/btf.h
index 618167cab4e6..6062c9958034 100644
--- a/tools/include/uapi/linux/btf.h
+++ b/tools/include/uapi/linux/btf.h
@@ -92,7 +92,9 @@ enum {
        BTF_KIND_DECL_TAG       = 17,   /* Decl Tag */
        BTF_KIND_TYPE_TAG       = 18,   /* Type Tag */
        BTF_KIND_ENUM64         = 19,   /* Enumeration up to 64-bit values */
-
+       BTF_KIND_LOC_PARAM      = 20,   /* Location parameter information */
+       BTF_KIND_LOC_PROTO      = 21,   /* Location prototype for site */
+       BTF_KIND_LOCSEC         = 22,   /* Location section */
        NR_BTF_KINDS,
        BTF_KIND_MAX            = NR_BTF_KINDS - 1,
 };
@@ -212,4 +214,65 @@ struct btf_enum64 {
        __u32   val_hi32;
 };
 
+/*
+ * BTF_KIND_LOC_PARAM is followed by a single "struct btf_loc_param"
+ * that contains flags specifying the contents of the vlen-specified
+ * number of 4-byte values that follow.
+ */
+struct btf_loc_param {
+       __u32 flags;
+};
+
+/*
+ * The combination of size, vlen and flags gives us the means to interpret
+ * the following vlen-specified set of 4-byte values:
+ *
+ * - a BTF_LOC_PARAM_CONST is a constant value; combination
+ *   of size, vlen and _SIGNED flag determines it. If the value requires
+ *   64 bits it is stored in {lo,hi} order.
+ * - a BTF_LOC_PARAM_ADDR is an address that will be normalized with
+ *   respect to kernel base address.
+ * - a BTF_LOC_PARAM_REG with vlen 1 is a simple register number;
+ *   with vlen 2 it is a multi-register parameter.
+ * - a _REG | DEREF with vlen 1 dereferences the value in the register
+ *   number specified.
+ * - a REG | DEREF | OFFSET with vlen specifies the register value in
+ *   the first 4-byte value and the offset in the remainder.
+ * - binary logical operators operate on a combination of register
+ *   number and constant value, aside from _NOT which operates on
+ *   a register
+ */
+enum btf_loc_param_flags {
+       BTF_LOC_PARAM_SIGNED            =       0x1,
+       BTF_LOC_PARAM_CONST             =       0x2,
+       BTF_LOC_PARAM_ADDR              =       0x4,
+       BTF_LOC_PARAM_REG               =       0x8,
+       BTF_LOC_PARAM_DEREF             =       0x10,
+       BTF_LOC_PARAM_OFFSET            =       0x20,
+};
+
+/*
+ * BTF_KIND_LOC_PROTO specifies location prototypes; i.e. how locations relate
+ * to parameters; a struct btf_type of BTF_KIND_LOC_PROTO is followed by a
+ * a vlen-specified number of __u32 BTF type ids which specify the associated
+ * BTF_KIND_LOC_PARAM for each function parameter associated with the
+ * location.  The type should either be 0 (no location info) or point at
+ * a BTF_KIND_LOC_PARAM.
+ */
+
+/*
+ * BTF_KIND_LOCSEC consists of vlen-specified number of "struct btf_loc"
+ * containing location site-specific information;
+ *
+ * - function (func)
+ * - location prototype type id (loc_proto)
+ * - address offset (offset) relative to kernel base address
+ */
+
+struct btf_loc {
+       __u32 func;
+       __u32 loc_proto;
+       __u32 offset;
+};
+
 #endif /* _UAPI__LINUX_BTF_H__ */
-- 
2.43.5


Reply via email to