/* btf.pk - BTF implementation for GNU poke.  */

/* Copyright (C) 2019, 2020, 2021, 2022, 2023, 2024 Jose E. Marchesi.  */

/* This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/* This file contains a description of the Linux BPF Type Format, as
   documented in https://www.kernel.org/doc/html/latest/bpf/btf.html
   (fetched 2019-10-25) */

var BTF_KIND_UNKNOWN = 0 as uint<5>,
    BTF_KIND_INT = 1 as uint<5>,
    BTF_KIND_PTR = 2 as uint<5>,
    BTF_KIND_ARRAY = 3 as uint<5>,
    BTF_KIND_STRUCT = 4 as uint<5>,
    BTF_KIND_UNION = 5 as uint<5>,
    BTF_KIND_ENUM = 6 as uint<5>,
    BTF_KIND_FWD = 7 as uint<5>,
    BTF_KIND_TYPEDEF = 8 as uint<5>,
    BTF_KIND_VOLATILE = 9 as uint<5>,
    BTF_KIND_CONST = 10 as uint<5>,
    BTF_KIND_RESTRICT = 11 as uint<5>,
    BTF_KIND_FUNC = 12 as uint<5>,
    BTF_KIND_FUNC_PROTO = 13 as uint<5>,
    BTF_KIND_VAR = 14 as uint<5>,
    BTF_KIND_DATASEC = 15 as uint<5>,
    BTF_KIND_FLOAT = 16 as uint<5>,
    BTF_KIND_DECL_TAG = 17 as uint<5>,
    BTF_KIND_TYPE_TAG = 18 as uint<5>,
    BTF_KIND_ENUM64 = 19 as uint<5>,
    BTF_KIND_LAST = BTF_KIND_ENUM64;

var btf_kind_names =
  ["unkn", "int", "ptr", "array", "struct", "union", "enum",
   "fwd", "typedef", "volatile", "const", "restrict", "func",
   "func_proto", "var", "datasec", "float", "decl_tag", "type_tag",
   "enum64"];

assert (btf_kind_names'length == BTF_KIND_LAST + 1);

var BTF_VAR_STATIC = 0,
    BTF_VAR_GLOBAL_ALLOCATED = 1,
    BTF_VAR_GLOBAL_EXTERN = 2;

var btf_var_linkage_names =
  ["static", "global allocated", "global extern"];

var BTF_FUNC_STATIC = 0,
    BTF_FUNC_GLOBAL = 1,
    BTF_FUNC_EXTERN = 2;

var btf_func_linkage_names =
  ["static", "global", "extern"];

type BTF_Type_Id = uint<32>;

type BTF_Int =
  struct uint<32>
  {
    uint<4>;
    struct uint<4>
    {
      uint<1>;
      uint<1> bool_p;
      uint<1> char_p;
      uint<1> signed_p;
    } encoding;
    offset<uint<8>,b> offset;
    uint<8>;
    offset<uint<8>,b> bits;
  };

type BTF_Array =
  struct
  {
    BTF_Type_Id elem_type;
    BTF_Type_Id index_type;
    uint<32> nelems;
  };

type BTF_Enum =
  struct
  {
    offset<uint<32>,B> name;
    int<32> val;
  };

type BTF_Enum64 =
  struct
  {
    offset<uint<32>,B> name;
    int<32> val_lo32;           /* Low 32 bits of 64-bit value.  */
    int<32> val_hi32;           /* High 32 bits of 64-bit value.  */

    /* Convenience method to re-assemble the value.  */
    method value = int<64>:
    {
        return (((val_hi32 as uint<64>) <<. 32) | (val_lo32 as uint<32>)) as int<64>;
    }
  };

type BTF_Param =
  struct
  {
    offset<uint<32>,B> name;
    BTF_Type_Id param_type;
  };

type BTF_Variable =
  struct
  {
    uint<32> linkage;

    method _print = void:
    {
      printf ("#<linkage:%s>", btf_var_linkage_names[linkage]);
    }
  };

type BTF_Var_SecInfo =
  struct
  {
    BTF_Type_Id var_type;
    offset<uint<32>,B> offset;
    offset<uint<32>,B> size;
  };

var BTF_DECL_TAG_SELF = -1;

type BTF_Decl_Tag =
  struct
  {
    /* -1 if the tag is applied to the type itself,
       else the index of the tagged field/parameter/etc.  */
    int<32> component_idx;
  };

type BTF_Type =
  struct
  {
    offset<uint<32>,B> name;

    struct uint<32>
    {
      uint<1> kind_flag;
      uint<2>;
      uint<5> kind : kind <= BTF_KIND_LAST;
      uint<8>;
      uint<16> vlen; /* Note that vlen encodes the linkage of
                        BTF_KIND_FUNC entries.  (yes, pfff.)  */

      method _print = void:
        {
          printf ("#<%s,kind_flag:%u32d,%s>",
                  btf_kind_names[kind], kind_flag,
                  kind == BTF_KIND_FUNC
                  ? format ("linkage:%s", btf_func_linkage_names[vlen])
                  : format ("vlen:%v", vlen));
        }
    } info;

    union
    {
      offset<uint<32>,B> size : (info.kind in [BTF_KIND_INT,
                                               BTF_KIND_FLOAT,
                                               BTF_KIND_ENUM,
                                               BTF_KIND_ENUM64,
                                               BTF_KIND_STRUCT,
                                               BTF_KIND_UNION,
                                               BTF_KIND_DATASEC]);
      BTF_Type_Id type_id;
    } attrs;

    /* XXX move these types to the top-level once we get support to
       pass arguments to struct types.  */

    type BTF_Member =
      struct
      {
        offset<uint<32>,B> name;
        BTF_Type_Id type_id;
        union
        {
          offset<uint<32>,b> member_offset : !info.kind_flag;
          struct uint<32>
          {
            offset<uint<8>,b> bitfield_size;
            offset<uint<24>,b> bit_offset;
          } bitfield;
        } offset;
      };

    type BTF_Func_Proto =
      struct
      {
        BTF_Param[info.vlen] params;
      };

    union
    {
      BTF_Int integer                    : info.kind == BTF_KIND_INT;
      BTF_Array array                    : info.kind == BTF_KIND_ARRAY;
      BTF_Enum[info.vlen] _enum          : info.kind == BTF_KIND_ENUM;
      BTF_Enum64[info.vlen] enum64       : info.kind == BTF_KIND_ENUM64;
      BTF_Func_Proto func_proto          : info.kind == BTF_KIND_FUNC_PROTO;
      BTF_Variable variable              : info.kind == BTF_KIND_VAR;
      BTF_Member[info.vlen] members      : (info.kind == BTF_KIND_UNION
                                            || info.kind == BTF_KIND_STRUCT);
      BTF_Var_SecInfo[info.vlen] datasec : info.kind == BTF_KIND_DATASEC;
      BTF_Decl_Tag decl_tag              : info.kind == BTF_KIND_DECL_TAG;

      struct {} nothing;
    } data;

    method vararg_p = int<32>:
      {
        var last_param = data.func_proto.params[info.vlen - 1];
        return (last_param.name == 0#B && last_param.param_type == 0);
      }

    method get_kind_name = string:
      {
        return btf_kind_names[info.kind];
      }
  };

type BTF_Header =
  struct
  {
    uint<16> magic : (magic == 0xeb9f)
                       || (magic == 0x9feb && set_endian (!get_endian))
                   = 0xeb9f;
    uint<8> version;
    uint<8> flags;
    offset<uint<32>,B> hdr_len; /* Size of this header.  */

    /* The offsets below are relative to the end of this header.  */
    offset<uint<32>,B> type_off;
    offset<uint<32>,B> type_len;
    offset<uint<32>,B> str_off;
    offset<uint<32>,B> str_len : hdr_len == OFFSET + str_len'size;
  };

type BTF_Section =
  struct
  {
    BTF_Header header : header.hdr_len == header'size;

    BTF_Type[header.type_len] types @ header.hdr_len + header.type_off;
    string[header.str_len] strings @ header.hdr_len + header.str_off;

    /* Given an offset into the BTF strings section, return the string.  */

    method get_string = (offset<uint<32>,B> off) string:
      {
        return strings[off];
      }
  };
