162306a36Sopenharmony_ci// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
262306a36Sopenharmony_ci/* Copyright (c) 2019 Netronome Systems, Inc. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <errno.h>
562306a36Sopenharmony_ci#include <fcntl.h>
662306a36Sopenharmony_ci#include <string.h>
762306a36Sopenharmony_ci#include <stdlib.h>
862306a36Sopenharmony_ci#include <unistd.h>
962306a36Sopenharmony_ci#include <net/if.h>
1062306a36Sopenharmony_ci#include <sys/utsname.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/btf.h>
1362306a36Sopenharmony_ci#include <linux/filter.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/version.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "bpf.h"
1862306a36Sopenharmony_ci#include "libbpf.h"
1962306a36Sopenharmony_ci#include "libbpf_internal.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/* On Ubuntu LINUX_VERSION_CODE doesn't correspond to info.release,
2262306a36Sopenharmony_ci * but Ubuntu provides /proc/version_signature file, as described at
2362306a36Sopenharmony_ci * https://ubuntu.com/kernel, with an example contents below, which we
2462306a36Sopenharmony_ci * can use to get a proper LINUX_VERSION_CODE.
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci *   Ubuntu 5.4.0-12.15-generic 5.4.8
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * In the above, 5.4.8 is what kernel is actually expecting, while
2962306a36Sopenharmony_ci * uname() call will return 5.4.0 in info.release.
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_cistatic __u32 get_ubuntu_kernel_version(void)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	const char *ubuntu_kver_file = "/proc/version_signature";
3462306a36Sopenharmony_ci	__u32 major, minor, patch;
3562306a36Sopenharmony_ci	int ret;
3662306a36Sopenharmony_ci	FILE *f;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (faccessat(AT_FDCWD, ubuntu_kver_file, R_OK, AT_EACCESS) != 0)
3962306a36Sopenharmony_ci		return 0;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	f = fopen(ubuntu_kver_file, "re");
4262306a36Sopenharmony_ci	if (!f)
4362306a36Sopenharmony_ci		return 0;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	ret = fscanf(f, "%*s %*s %u.%u.%u\n", &major, &minor, &patch);
4662306a36Sopenharmony_ci	fclose(f);
4762306a36Sopenharmony_ci	if (ret != 3)
4862306a36Sopenharmony_ci		return 0;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	return KERNEL_VERSION(major, minor, patch);
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* On Debian LINUX_VERSION_CODE doesn't correspond to info.release.
5462306a36Sopenharmony_ci * Instead, it is provided in info.version. An example content of
5562306a36Sopenharmony_ci * Debian 10 looks like the below.
5662306a36Sopenharmony_ci *
5762306a36Sopenharmony_ci *   utsname::release   4.19.0-22-amd64
5862306a36Sopenharmony_ci *   utsname::version   #1 SMP Debian 4.19.260-1 (2022-09-29)
5962306a36Sopenharmony_ci *
6062306a36Sopenharmony_ci * In the above, 4.19.260 is what kernel is actually expecting, while
6162306a36Sopenharmony_ci * uname() call will return 4.19.0 in info.release.
6262306a36Sopenharmony_ci */
6362306a36Sopenharmony_cistatic __u32 get_debian_kernel_version(struct utsname *info)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	__u32 major, minor, patch;
6662306a36Sopenharmony_ci	char *p;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	p = strstr(info->version, "Debian ");
6962306a36Sopenharmony_ci	if (!p) {
7062306a36Sopenharmony_ci		/* This is not a Debian kernel. */
7162306a36Sopenharmony_ci		return 0;
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (sscanf(p, "Debian %u.%u.%u", &major, &minor, &patch) != 3)
7562306a36Sopenharmony_ci		return 0;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return KERNEL_VERSION(major, minor, patch);
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci__u32 get_kernel_version(void)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	__u32 major, minor, patch, version;
8362306a36Sopenharmony_ci	struct utsname info;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/* Check if this is an Ubuntu kernel. */
8662306a36Sopenharmony_ci	version = get_ubuntu_kernel_version();
8762306a36Sopenharmony_ci	if (version != 0)
8862306a36Sopenharmony_ci		return version;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	uname(&info);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/* Check if this is a Debian kernel. */
9362306a36Sopenharmony_ci	version = get_debian_kernel_version(&info);
9462306a36Sopenharmony_ci	if (version != 0)
9562306a36Sopenharmony_ci		return version;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (sscanf(info.release, "%u.%u.%u", &major, &minor, &patch) != 3)
9862306a36Sopenharmony_ci		return 0;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	return KERNEL_VERSION(major, minor, patch);
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic int probe_prog_load(enum bpf_prog_type prog_type,
10462306a36Sopenharmony_ci			   const struct bpf_insn *insns, size_t insns_cnt,
10562306a36Sopenharmony_ci			   char *log_buf, size_t log_buf_sz)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	LIBBPF_OPTS(bpf_prog_load_opts, opts,
10862306a36Sopenharmony_ci		.log_buf = log_buf,
10962306a36Sopenharmony_ci		.log_size = log_buf_sz,
11062306a36Sopenharmony_ci		.log_level = log_buf ? 1 : 0,
11162306a36Sopenharmony_ci	);
11262306a36Sopenharmony_ci	int fd, err, exp_err = 0;
11362306a36Sopenharmony_ci	const char *exp_msg = NULL;
11462306a36Sopenharmony_ci	char buf[4096];
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	switch (prog_type) {
11762306a36Sopenharmony_ci	case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
11862306a36Sopenharmony_ci		opts.expected_attach_type = BPF_CGROUP_INET4_CONNECT;
11962306a36Sopenharmony_ci		break;
12062306a36Sopenharmony_ci	case BPF_PROG_TYPE_CGROUP_SOCKOPT:
12162306a36Sopenharmony_ci		opts.expected_attach_type = BPF_CGROUP_GETSOCKOPT;
12262306a36Sopenharmony_ci		break;
12362306a36Sopenharmony_ci	case BPF_PROG_TYPE_SK_LOOKUP:
12462306a36Sopenharmony_ci		opts.expected_attach_type = BPF_SK_LOOKUP;
12562306a36Sopenharmony_ci		break;
12662306a36Sopenharmony_ci	case BPF_PROG_TYPE_KPROBE:
12762306a36Sopenharmony_ci		opts.kern_version = get_kernel_version();
12862306a36Sopenharmony_ci		break;
12962306a36Sopenharmony_ci	case BPF_PROG_TYPE_LIRC_MODE2:
13062306a36Sopenharmony_ci		opts.expected_attach_type = BPF_LIRC_MODE2;
13162306a36Sopenharmony_ci		break;
13262306a36Sopenharmony_ci	case BPF_PROG_TYPE_TRACING:
13362306a36Sopenharmony_ci	case BPF_PROG_TYPE_LSM:
13462306a36Sopenharmony_ci		opts.log_buf = buf;
13562306a36Sopenharmony_ci		opts.log_size = sizeof(buf);
13662306a36Sopenharmony_ci		opts.log_level = 1;
13762306a36Sopenharmony_ci		if (prog_type == BPF_PROG_TYPE_TRACING)
13862306a36Sopenharmony_ci			opts.expected_attach_type = BPF_TRACE_FENTRY;
13962306a36Sopenharmony_ci		else
14062306a36Sopenharmony_ci			opts.expected_attach_type = BPF_MODIFY_RETURN;
14162306a36Sopenharmony_ci		opts.attach_btf_id = 1;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		exp_err = -EINVAL;
14462306a36Sopenharmony_ci		exp_msg = "attach_btf_id 1 is not a function";
14562306a36Sopenharmony_ci		break;
14662306a36Sopenharmony_ci	case BPF_PROG_TYPE_EXT:
14762306a36Sopenharmony_ci		opts.log_buf = buf;
14862306a36Sopenharmony_ci		opts.log_size = sizeof(buf);
14962306a36Sopenharmony_ci		opts.log_level = 1;
15062306a36Sopenharmony_ci		opts.attach_btf_id = 1;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci		exp_err = -EINVAL;
15362306a36Sopenharmony_ci		exp_msg = "Cannot replace kernel functions";
15462306a36Sopenharmony_ci		break;
15562306a36Sopenharmony_ci	case BPF_PROG_TYPE_SYSCALL:
15662306a36Sopenharmony_ci		opts.prog_flags = BPF_F_SLEEPABLE;
15762306a36Sopenharmony_ci		break;
15862306a36Sopenharmony_ci	case BPF_PROG_TYPE_STRUCT_OPS:
15962306a36Sopenharmony_ci		exp_err = -524; /* -ENOTSUPP */
16062306a36Sopenharmony_ci		break;
16162306a36Sopenharmony_ci	case BPF_PROG_TYPE_UNSPEC:
16262306a36Sopenharmony_ci	case BPF_PROG_TYPE_SOCKET_FILTER:
16362306a36Sopenharmony_ci	case BPF_PROG_TYPE_SCHED_CLS:
16462306a36Sopenharmony_ci	case BPF_PROG_TYPE_SCHED_ACT:
16562306a36Sopenharmony_ci	case BPF_PROG_TYPE_TRACEPOINT:
16662306a36Sopenharmony_ci	case BPF_PROG_TYPE_XDP:
16762306a36Sopenharmony_ci	case BPF_PROG_TYPE_PERF_EVENT:
16862306a36Sopenharmony_ci	case BPF_PROG_TYPE_CGROUP_SKB:
16962306a36Sopenharmony_ci	case BPF_PROG_TYPE_CGROUP_SOCK:
17062306a36Sopenharmony_ci	case BPF_PROG_TYPE_LWT_IN:
17162306a36Sopenharmony_ci	case BPF_PROG_TYPE_LWT_OUT:
17262306a36Sopenharmony_ci	case BPF_PROG_TYPE_LWT_XMIT:
17362306a36Sopenharmony_ci	case BPF_PROG_TYPE_SOCK_OPS:
17462306a36Sopenharmony_ci	case BPF_PROG_TYPE_SK_SKB:
17562306a36Sopenharmony_ci	case BPF_PROG_TYPE_CGROUP_DEVICE:
17662306a36Sopenharmony_ci	case BPF_PROG_TYPE_SK_MSG:
17762306a36Sopenharmony_ci	case BPF_PROG_TYPE_RAW_TRACEPOINT:
17862306a36Sopenharmony_ci	case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE:
17962306a36Sopenharmony_ci	case BPF_PROG_TYPE_LWT_SEG6LOCAL:
18062306a36Sopenharmony_ci	case BPF_PROG_TYPE_SK_REUSEPORT:
18162306a36Sopenharmony_ci	case BPF_PROG_TYPE_FLOW_DISSECTOR:
18262306a36Sopenharmony_ci	case BPF_PROG_TYPE_CGROUP_SYSCTL:
18362306a36Sopenharmony_ci		break;
18462306a36Sopenharmony_ci	case BPF_PROG_TYPE_NETFILTER:
18562306a36Sopenharmony_ci		opts.expected_attach_type = BPF_NETFILTER;
18662306a36Sopenharmony_ci		break;
18762306a36Sopenharmony_ci	default:
18862306a36Sopenharmony_ci		return -EOPNOTSUPP;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	fd = bpf_prog_load(prog_type, NULL, "GPL", insns, insns_cnt, &opts);
19262306a36Sopenharmony_ci	err = -errno;
19362306a36Sopenharmony_ci	if (fd >= 0)
19462306a36Sopenharmony_ci		close(fd);
19562306a36Sopenharmony_ci	if (exp_err) {
19662306a36Sopenharmony_ci		if (fd >= 0 || err != exp_err)
19762306a36Sopenharmony_ci			return 0;
19862306a36Sopenharmony_ci		if (exp_msg && !strstr(buf, exp_msg))
19962306a36Sopenharmony_ci			return 0;
20062306a36Sopenharmony_ci		return 1;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci	return fd >= 0 ? 1 : 0;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ciint libbpf_probe_bpf_prog_type(enum bpf_prog_type prog_type, const void *opts)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct bpf_insn insns[] = {
20862306a36Sopenharmony_ci		BPF_MOV64_IMM(BPF_REG_0, 0),
20962306a36Sopenharmony_ci		BPF_EXIT_INSN()
21062306a36Sopenharmony_ci	};
21162306a36Sopenharmony_ci	const size_t insn_cnt = ARRAY_SIZE(insns);
21262306a36Sopenharmony_ci	int ret;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (opts)
21562306a36Sopenharmony_ci		return libbpf_err(-EINVAL);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	ret = probe_prog_load(prog_type, insns, insn_cnt, NULL, 0);
21862306a36Sopenharmony_ci	return libbpf_err(ret);
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ciint libbpf__load_raw_btf(const char *raw_types, size_t types_len,
22262306a36Sopenharmony_ci			 const char *str_sec, size_t str_len)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct btf_header hdr = {
22562306a36Sopenharmony_ci		.magic = BTF_MAGIC,
22662306a36Sopenharmony_ci		.version = BTF_VERSION,
22762306a36Sopenharmony_ci		.hdr_len = sizeof(struct btf_header),
22862306a36Sopenharmony_ci		.type_len = types_len,
22962306a36Sopenharmony_ci		.str_off = types_len,
23062306a36Sopenharmony_ci		.str_len = str_len,
23162306a36Sopenharmony_ci	};
23262306a36Sopenharmony_ci	int btf_fd, btf_len;
23362306a36Sopenharmony_ci	__u8 *raw_btf;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	btf_len = hdr.hdr_len + hdr.type_len + hdr.str_len;
23662306a36Sopenharmony_ci	raw_btf = malloc(btf_len);
23762306a36Sopenharmony_ci	if (!raw_btf)
23862306a36Sopenharmony_ci		return -ENOMEM;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	memcpy(raw_btf, &hdr, sizeof(hdr));
24162306a36Sopenharmony_ci	memcpy(raw_btf + hdr.hdr_len, raw_types, hdr.type_len);
24262306a36Sopenharmony_ci	memcpy(raw_btf + hdr.hdr_len + hdr.type_len, str_sec, hdr.str_len);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	btf_fd = bpf_btf_load(raw_btf, btf_len, NULL);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	free(raw_btf);
24762306a36Sopenharmony_ci	return btf_fd;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic int load_local_storage_btf(void)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	const char strs[] = "\0bpf_spin_lock\0val\0cnt\0l";
25362306a36Sopenharmony_ci	/* struct bpf_spin_lock {
25462306a36Sopenharmony_ci	 *   int val;
25562306a36Sopenharmony_ci	 * };
25662306a36Sopenharmony_ci	 * struct val {
25762306a36Sopenharmony_ci	 *   int cnt;
25862306a36Sopenharmony_ci	 *   struct bpf_spin_lock l;
25962306a36Sopenharmony_ci	 * };
26062306a36Sopenharmony_ci	 */
26162306a36Sopenharmony_ci	__u32 types[] = {
26262306a36Sopenharmony_ci		/* int */
26362306a36Sopenharmony_ci		BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
26462306a36Sopenharmony_ci		/* struct bpf_spin_lock */                      /* [2] */
26562306a36Sopenharmony_ci		BTF_TYPE_ENC(1, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 1), 4),
26662306a36Sopenharmony_ci		BTF_MEMBER_ENC(15, 1, 0), /* int val; */
26762306a36Sopenharmony_ci		/* struct val */                                /* [3] */
26862306a36Sopenharmony_ci		BTF_TYPE_ENC(15, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 2), 8),
26962306a36Sopenharmony_ci		BTF_MEMBER_ENC(19, 1, 0), /* int cnt; */
27062306a36Sopenharmony_ci		BTF_MEMBER_ENC(23, 2, 32),/* struct bpf_spin_lock l; */
27162306a36Sopenharmony_ci	};
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	return libbpf__load_raw_btf((char *)types, sizeof(types),
27462306a36Sopenharmony_ci				     strs, sizeof(strs));
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic int probe_map_create(enum bpf_map_type map_type)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	LIBBPF_OPTS(bpf_map_create_opts, opts);
28062306a36Sopenharmony_ci	int key_size, value_size, max_entries;
28162306a36Sopenharmony_ci	__u32 btf_key_type_id = 0, btf_value_type_id = 0;
28262306a36Sopenharmony_ci	int fd = -1, btf_fd = -1, fd_inner = -1, exp_err = 0, err = 0;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	key_size	= sizeof(__u32);
28562306a36Sopenharmony_ci	value_size	= sizeof(__u32);
28662306a36Sopenharmony_ci	max_entries	= 1;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	switch (map_type) {
28962306a36Sopenharmony_ci	case BPF_MAP_TYPE_STACK_TRACE:
29062306a36Sopenharmony_ci		value_size	= sizeof(__u64);
29162306a36Sopenharmony_ci		break;
29262306a36Sopenharmony_ci	case BPF_MAP_TYPE_LPM_TRIE:
29362306a36Sopenharmony_ci		key_size	= sizeof(__u64);
29462306a36Sopenharmony_ci		value_size	= sizeof(__u64);
29562306a36Sopenharmony_ci		opts.map_flags	= BPF_F_NO_PREALLOC;
29662306a36Sopenharmony_ci		break;
29762306a36Sopenharmony_ci	case BPF_MAP_TYPE_CGROUP_STORAGE:
29862306a36Sopenharmony_ci	case BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE:
29962306a36Sopenharmony_ci		key_size	= sizeof(struct bpf_cgroup_storage_key);
30062306a36Sopenharmony_ci		value_size	= sizeof(__u64);
30162306a36Sopenharmony_ci		max_entries	= 0;
30262306a36Sopenharmony_ci		break;
30362306a36Sopenharmony_ci	case BPF_MAP_TYPE_QUEUE:
30462306a36Sopenharmony_ci	case BPF_MAP_TYPE_STACK:
30562306a36Sopenharmony_ci		key_size	= 0;
30662306a36Sopenharmony_ci		break;
30762306a36Sopenharmony_ci	case BPF_MAP_TYPE_SK_STORAGE:
30862306a36Sopenharmony_ci	case BPF_MAP_TYPE_INODE_STORAGE:
30962306a36Sopenharmony_ci	case BPF_MAP_TYPE_TASK_STORAGE:
31062306a36Sopenharmony_ci	case BPF_MAP_TYPE_CGRP_STORAGE:
31162306a36Sopenharmony_ci		btf_key_type_id = 1;
31262306a36Sopenharmony_ci		btf_value_type_id = 3;
31362306a36Sopenharmony_ci		value_size = 8;
31462306a36Sopenharmony_ci		max_entries = 0;
31562306a36Sopenharmony_ci		opts.map_flags = BPF_F_NO_PREALLOC;
31662306a36Sopenharmony_ci		btf_fd = load_local_storage_btf();
31762306a36Sopenharmony_ci		if (btf_fd < 0)
31862306a36Sopenharmony_ci			return btf_fd;
31962306a36Sopenharmony_ci		break;
32062306a36Sopenharmony_ci	case BPF_MAP_TYPE_RINGBUF:
32162306a36Sopenharmony_ci	case BPF_MAP_TYPE_USER_RINGBUF:
32262306a36Sopenharmony_ci		key_size = 0;
32362306a36Sopenharmony_ci		value_size = 0;
32462306a36Sopenharmony_ci		max_entries = sysconf(_SC_PAGE_SIZE);
32562306a36Sopenharmony_ci		break;
32662306a36Sopenharmony_ci	case BPF_MAP_TYPE_STRUCT_OPS:
32762306a36Sopenharmony_ci		/* we'll get -ENOTSUPP for invalid BTF type ID for struct_ops */
32862306a36Sopenharmony_ci		opts.btf_vmlinux_value_type_id = 1;
32962306a36Sopenharmony_ci		exp_err = -524; /* -ENOTSUPP */
33062306a36Sopenharmony_ci		break;
33162306a36Sopenharmony_ci	case BPF_MAP_TYPE_BLOOM_FILTER:
33262306a36Sopenharmony_ci		key_size = 0;
33362306a36Sopenharmony_ci		max_entries = 1;
33462306a36Sopenharmony_ci		break;
33562306a36Sopenharmony_ci	case BPF_MAP_TYPE_HASH:
33662306a36Sopenharmony_ci	case BPF_MAP_TYPE_ARRAY:
33762306a36Sopenharmony_ci	case BPF_MAP_TYPE_PROG_ARRAY:
33862306a36Sopenharmony_ci	case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
33962306a36Sopenharmony_ci	case BPF_MAP_TYPE_PERCPU_HASH:
34062306a36Sopenharmony_ci	case BPF_MAP_TYPE_PERCPU_ARRAY:
34162306a36Sopenharmony_ci	case BPF_MAP_TYPE_CGROUP_ARRAY:
34262306a36Sopenharmony_ci	case BPF_MAP_TYPE_LRU_HASH:
34362306a36Sopenharmony_ci	case BPF_MAP_TYPE_LRU_PERCPU_HASH:
34462306a36Sopenharmony_ci	case BPF_MAP_TYPE_ARRAY_OF_MAPS:
34562306a36Sopenharmony_ci	case BPF_MAP_TYPE_HASH_OF_MAPS:
34662306a36Sopenharmony_ci	case BPF_MAP_TYPE_DEVMAP:
34762306a36Sopenharmony_ci	case BPF_MAP_TYPE_DEVMAP_HASH:
34862306a36Sopenharmony_ci	case BPF_MAP_TYPE_SOCKMAP:
34962306a36Sopenharmony_ci	case BPF_MAP_TYPE_CPUMAP:
35062306a36Sopenharmony_ci	case BPF_MAP_TYPE_XSKMAP:
35162306a36Sopenharmony_ci	case BPF_MAP_TYPE_SOCKHASH:
35262306a36Sopenharmony_ci	case BPF_MAP_TYPE_REUSEPORT_SOCKARRAY:
35362306a36Sopenharmony_ci		break;
35462306a36Sopenharmony_ci	case BPF_MAP_TYPE_UNSPEC:
35562306a36Sopenharmony_ci	default:
35662306a36Sopenharmony_ci		return -EOPNOTSUPP;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||
36062306a36Sopenharmony_ci	    map_type == BPF_MAP_TYPE_HASH_OF_MAPS) {
36162306a36Sopenharmony_ci		fd_inner = bpf_map_create(BPF_MAP_TYPE_HASH, NULL,
36262306a36Sopenharmony_ci					  sizeof(__u32), sizeof(__u32), 1, NULL);
36362306a36Sopenharmony_ci		if (fd_inner < 0)
36462306a36Sopenharmony_ci			goto cleanup;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci		opts.inner_map_fd = fd_inner;
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	if (btf_fd >= 0) {
37062306a36Sopenharmony_ci		opts.btf_fd = btf_fd;
37162306a36Sopenharmony_ci		opts.btf_key_type_id = btf_key_type_id;
37262306a36Sopenharmony_ci		opts.btf_value_type_id = btf_value_type_id;
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	fd = bpf_map_create(map_type, NULL, key_size, value_size, max_entries, &opts);
37662306a36Sopenharmony_ci	err = -errno;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cicleanup:
37962306a36Sopenharmony_ci	if (fd >= 0)
38062306a36Sopenharmony_ci		close(fd);
38162306a36Sopenharmony_ci	if (fd_inner >= 0)
38262306a36Sopenharmony_ci		close(fd_inner);
38362306a36Sopenharmony_ci	if (btf_fd >= 0)
38462306a36Sopenharmony_ci		close(btf_fd);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (exp_err)
38762306a36Sopenharmony_ci		return fd < 0 && err == exp_err ? 1 : 0;
38862306a36Sopenharmony_ci	else
38962306a36Sopenharmony_ci		return fd >= 0 ? 1 : 0;
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ciint libbpf_probe_bpf_map_type(enum bpf_map_type map_type, const void *opts)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	int ret;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	if (opts)
39762306a36Sopenharmony_ci		return libbpf_err(-EINVAL);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	ret = probe_map_create(map_type);
40062306a36Sopenharmony_ci	return libbpf_err(ret);
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ciint libbpf_probe_bpf_helper(enum bpf_prog_type prog_type, enum bpf_func_id helper_id,
40462306a36Sopenharmony_ci			    const void *opts)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	struct bpf_insn insns[] = {
40762306a36Sopenharmony_ci		BPF_EMIT_CALL((__u32)helper_id),
40862306a36Sopenharmony_ci		BPF_EXIT_INSN(),
40962306a36Sopenharmony_ci	};
41062306a36Sopenharmony_ci	const size_t insn_cnt = ARRAY_SIZE(insns);
41162306a36Sopenharmony_ci	char buf[4096];
41262306a36Sopenharmony_ci	int ret;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	if (opts)
41562306a36Sopenharmony_ci		return libbpf_err(-EINVAL);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	/* we can't successfully load all prog types to check for BPF helper
41862306a36Sopenharmony_ci	 * support, so bail out with -EOPNOTSUPP error
41962306a36Sopenharmony_ci	 */
42062306a36Sopenharmony_ci	switch (prog_type) {
42162306a36Sopenharmony_ci	case BPF_PROG_TYPE_TRACING:
42262306a36Sopenharmony_ci	case BPF_PROG_TYPE_EXT:
42362306a36Sopenharmony_ci	case BPF_PROG_TYPE_LSM:
42462306a36Sopenharmony_ci	case BPF_PROG_TYPE_STRUCT_OPS:
42562306a36Sopenharmony_ci		return -EOPNOTSUPP;
42662306a36Sopenharmony_ci	default:
42762306a36Sopenharmony_ci		break;
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	buf[0] = '\0';
43162306a36Sopenharmony_ci	ret = probe_prog_load(prog_type, insns, insn_cnt, buf, sizeof(buf));
43262306a36Sopenharmony_ci	if (ret < 0)
43362306a36Sopenharmony_ci		return libbpf_err(ret);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	/* If BPF verifier doesn't recognize BPF helper ID (enum bpf_func_id)
43662306a36Sopenharmony_ci	 * at all, it will emit something like "invalid func unknown#181".
43762306a36Sopenharmony_ci	 * If BPF verifier recognizes BPF helper but it's not supported for
43862306a36Sopenharmony_ci	 * given BPF program type, it will emit "unknown func bpf_sys_bpf#166".
43962306a36Sopenharmony_ci	 * In both cases, provided combination of BPF program type and BPF
44062306a36Sopenharmony_ci	 * helper is not supported by the kernel.
44162306a36Sopenharmony_ci	 * In all other cases, probe_prog_load() above will either succeed (e.g.,
44262306a36Sopenharmony_ci	 * because BPF helper happens to accept no input arguments or it
44362306a36Sopenharmony_ci	 * accepts one input argument and initial PTR_TO_CTX is fine for
44462306a36Sopenharmony_ci	 * that), or we'll get some more specific BPF verifier error about
44562306a36Sopenharmony_ci	 * some unsatisfied conditions.
44662306a36Sopenharmony_ci	 */
44762306a36Sopenharmony_ci	if (ret == 0 && (strstr(buf, "invalid func ") || strstr(buf, "unknown func ")))
44862306a36Sopenharmony_ci		return 0;
44962306a36Sopenharmony_ci	return 1; /* assume supported */
45062306a36Sopenharmony_ci}
451