162306a36Sopenharmony_ci// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#ifndef _GNU_SOURCE
462306a36Sopenharmony_ci#define _GNU_SOURCE
562306a36Sopenharmony_ci#endif
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <errno.h>
862306a36Sopenharmony_ci#include <stdlib.h>
962306a36Sopenharmony_ci#include <linux/err.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <bpf/bpf.h>
1262306a36Sopenharmony_ci#include "bpf-utils.h"
1362306a36Sopenharmony_ci#include "debug.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistruct bpil_array_desc {
1662306a36Sopenharmony_ci	int	array_offset;	/* e.g. offset of jited_prog_insns */
1762306a36Sopenharmony_ci	int	count_offset;	/* e.g. offset of jited_prog_len */
1862306a36Sopenharmony_ci	int	size_offset;	/* > 0: offset of rec size,
1962306a36Sopenharmony_ci				 * < 0: fix size of -size_offset
2062306a36Sopenharmony_ci				 */
2162306a36Sopenharmony_ci};
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic struct bpil_array_desc bpil_array_desc[] = {
2462306a36Sopenharmony_ci	[PERF_BPIL_JITED_INSNS] = {
2562306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, jited_prog_insns),
2662306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, jited_prog_len),
2762306a36Sopenharmony_ci		-1,
2862306a36Sopenharmony_ci	},
2962306a36Sopenharmony_ci	[PERF_BPIL_XLATED_INSNS] = {
3062306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, xlated_prog_insns),
3162306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, xlated_prog_len),
3262306a36Sopenharmony_ci		-1,
3362306a36Sopenharmony_ci	},
3462306a36Sopenharmony_ci	[PERF_BPIL_MAP_IDS] = {
3562306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, map_ids),
3662306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, nr_map_ids),
3762306a36Sopenharmony_ci		-(int)sizeof(__u32),
3862306a36Sopenharmony_ci	},
3962306a36Sopenharmony_ci	[PERF_BPIL_JITED_KSYMS] = {
4062306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, jited_ksyms),
4162306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, nr_jited_ksyms),
4262306a36Sopenharmony_ci		-(int)sizeof(__u64),
4362306a36Sopenharmony_ci	},
4462306a36Sopenharmony_ci	[PERF_BPIL_JITED_FUNC_LENS] = {
4562306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, jited_func_lens),
4662306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, nr_jited_func_lens),
4762306a36Sopenharmony_ci		-(int)sizeof(__u32),
4862306a36Sopenharmony_ci	},
4962306a36Sopenharmony_ci	[PERF_BPIL_FUNC_INFO] = {
5062306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, func_info),
5162306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, nr_func_info),
5262306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, func_info_rec_size),
5362306a36Sopenharmony_ci	},
5462306a36Sopenharmony_ci	[PERF_BPIL_LINE_INFO] = {
5562306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, line_info),
5662306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, nr_line_info),
5762306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, line_info_rec_size),
5862306a36Sopenharmony_ci	},
5962306a36Sopenharmony_ci	[PERF_BPIL_JITED_LINE_INFO] = {
6062306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, jited_line_info),
6162306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, nr_jited_line_info),
6262306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, jited_line_info_rec_size),
6362306a36Sopenharmony_ci	},
6462306a36Sopenharmony_ci	[PERF_BPIL_PROG_TAGS] = {
6562306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, prog_tags),
6662306a36Sopenharmony_ci		offsetof(struct bpf_prog_info, nr_prog_tags),
6762306a36Sopenharmony_ci		-(int)sizeof(__u8) * BPF_TAG_SIZE,
6862306a36Sopenharmony_ci	},
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic __u32 bpf_prog_info_read_offset_u32(struct bpf_prog_info *info,
7362306a36Sopenharmony_ci					   int offset)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	__u32 *array = (__u32 *)info;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (offset >= 0)
7862306a36Sopenharmony_ci		return array[offset / sizeof(__u32)];
7962306a36Sopenharmony_ci	return -(int)offset;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic __u64 bpf_prog_info_read_offset_u64(struct bpf_prog_info *info,
8362306a36Sopenharmony_ci					   int offset)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	__u64 *array = (__u64 *)info;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (offset >= 0)
8862306a36Sopenharmony_ci		return array[offset / sizeof(__u64)];
8962306a36Sopenharmony_ci	return -(int)offset;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void bpf_prog_info_set_offset_u32(struct bpf_prog_info *info, int offset,
9362306a36Sopenharmony_ci					 __u32 val)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	__u32 *array = (__u32 *)info;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (offset >= 0)
9862306a36Sopenharmony_ci		array[offset / sizeof(__u32)] = val;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic void bpf_prog_info_set_offset_u64(struct bpf_prog_info *info, int offset,
10262306a36Sopenharmony_ci					 __u64 val)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	__u64 *array = (__u64 *)info;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (offset >= 0)
10762306a36Sopenharmony_ci		array[offset / sizeof(__u64)] = val;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistruct perf_bpil *
11162306a36Sopenharmony_ciget_bpf_prog_info_linear(int fd, __u64 arrays)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct bpf_prog_info info = {};
11462306a36Sopenharmony_ci	struct perf_bpil *info_linear;
11562306a36Sopenharmony_ci	__u32 info_len = sizeof(info);
11662306a36Sopenharmony_ci	__u32 data_len = 0;
11762306a36Sopenharmony_ci	int i, err;
11862306a36Sopenharmony_ci	void *ptr;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (arrays >> PERF_BPIL_LAST_ARRAY)
12162306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/* step 1: get array dimensions */
12462306a36Sopenharmony_ci	err = bpf_obj_get_info_by_fd(fd, &info, &info_len);
12562306a36Sopenharmony_ci	if (err) {
12662306a36Sopenharmony_ci		pr_debug("can't get prog info: %s", strerror(errno));
12762306a36Sopenharmony_ci		return ERR_PTR(-EFAULT);
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/* step 2: calculate total size of all arrays */
13162306a36Sopenharmony_ci	for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) {
13262306a36Sopenharmony_ci		bool include_array = (arrays & (1UL << i)) > 0;
13362306a36Sopenharmony_ci		struct bpil_array_desc *desc;
13462306a36Sopenharmony_ci		__u32 count, size;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		desc = bpil_array_desc + i;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci		/* kernel is too old to support this field */
13962306a36Sopenharmony_ci		if (info_len < desc->array_offset + sizeof(__u32) ||
14062306a36Sopenharmony_ci		    info_len < desc->count_offset + sizeof(__u32) ||
14162306a36Sopenharmony_ci		    (desc->size_offset > 0 && info_len < (__u32)desc->size_offset))
14262306a36Sopenharmony_ci			include_array = false;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci		if (!include_array) {
14562306a36Sopenharmony_ci			arrays &= ~(1UL << i);	/* clear the bit */
14662306a36Sopenharmony_ci			continue;
14762306a36Sopenharmony_ci		}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		count = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
15062306a36Sopenharmony_ci		size  = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci		data_len += roundup(count * size, sizeof(__u64));
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* step 3: allocate continuous memory */
15662306a36Sopenharmony_ci	info_linear = malloc(sizeof(struct perf_bpil) + data_len);
15762306a36Sopenharmony_ci	if (!info_linear)
15862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* step 4: fill data to info_linear->info */
16162306a36Sopenharmony_ci	info_linear->arrays = arrays;
16262306a36Sopenharmony_ci	memset(&info_linear->info, 0, sizeof(info));
16362306a36Sopenharmony_ci	ptr = info_linear->data;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) {
16662306a36Sopenharmony_ci		struct bpil_array_desc *desc;
16762306a36Sopenharmony_ci		__u32 count, size;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci		if ((arrays & (1UL << i)) == 0)
17062306a36Sopenharmony_ci			continue;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci		desc  = bpil_array_desc + i;
17362306a36Sopenharmony_ci		count = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
17462306a36Sopenharmony_ci		size  = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
17562306a36Sopenharmony_ci		bpf_prog_info_set_offset_u32(&info_linear->info,
17662306a36Sopenharmony_ci					     desc->count_offset, count);
17762306a36Sopenharmony_ci		bpf_prog_info_set_offset_u32(&info_linear->info,
17862306a36Sopenharmony_ci					     desc->size_offset, size);
17962306a36Sopenharmony_ci		bpf_prog_info_set_offset_u64(&info_linear->info,
18062306a36Sopenharmony_ci					     desc->array_offset,
18162306a36Sopenharmony_ci					     ptr_to_u64(ptr));
18262306a36Sopenharmony_ci		ptr += roundup(count * size, sizeof(__u64));
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	/* step 5: call syscall again to get required arrays */
18662306a36Sopenharmony_ci	err = bpf_obj_get_info_by_fd(fd, &info_linear->info, &info_len);
18762306a36Sopenharmony_ci	if (err) {
18862306a36Sopenharmony_ci		pr_debug("can't get prog info: %s", strerror(errno));
18962306a36Sopenharmony_ci		free(info_linear);
19062306a36Sopenharmony_ci		return ERR_PTR(-EFAULT);
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	/* step 6: verify the data */
19462306a36Sopenharmony_ci	for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) {
19562306a36Sopenharmony_ci		struct bpil_array_desc *desc;
19662306a36Sopenharmony_ci		__u32 v1, v2;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		if ((arrays & (1UL << i)) == 0)
19962306a36Sopenharmony_ci			continue;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci		desc = bpil_array_desc + i;
20262306a36Sopenharmony_ci		v1 = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
20362306a36Sopenharmony_ci		v2 = bpf_prog_info_read_offset_u32(&info_linear->info,
20462306a36Sopenharmony_ci						   desc->count_offset);
20562306a36Sopenharmony_ci		if (v1 != v2)
20662306a36Sopenharmony_ci			pr_warning("%s: mismatch in element count\n", __func__);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci		v1 = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
20962306a36Sopenharmony_ci		v2 = bpf_prog_info_read_offset_u32(&info_linear->info,
21062306a36Sopenharmony_ci						   desc->size_offset);
21162306a36Sopenharmony_ci		if (v1 != v2)
21262306a36Sopenharmony_ci			pr_warning("%s: mismatch in rec size\n", __func__);
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* step 7: update info_len and data_len */
21662306a36Sopenharmony_ci	info_linear->info_len = sizeof(struct bpf_prog_info);
21762306a36Sopenharmony_ci	info_linear->data_len = data_len;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	return info_linear;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_civoid bpil_addr_to_offs(struct perf_bpil *info_linear)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	int i;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) {
22762306a36Sopenharmony_ci		struct bpil_array_desc *desc;
22862306a36Sopenharmony_ci		__u64 addr, offs;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci		if ((info_linear->arrays & (1UL << i)) == 0)
23162306a36Sopenharmony_ci			continue;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci		desc = bpil_array_desc + i;
23462306a36Sopenharmony_ci		addr = bpf_prog_info_read_offset_u64(&info_linear->info,
23562306a36Sopenharmony_ci						     desc->array_offset);
23662306a36Sopenharmony_ci		offs = addr - ptr_to_u64(info_linear->data);
23762306a36Sopenharmony_ci		bpf_prog_info_set_offset_u64(&info_linear->info,
23862306a36Sopenharmony_ci					     desc->array_offset, offs);
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_civoid bpil_offs_to_addr(struct perf_bpil *info_linear)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	int i;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) {
24762306a36Sopenharmony_ci		struct bpil_array_desc *desc;
24862306a36Sopenharmony_ci		__u64 addr, offs;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci		if ((info_linear->arrays & (1UL << i)) == 0)
25162306a36Sopenharmony_ci			continue;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		desc = bpil_array_desc + i;
25462306a36Sopenharmony_ci		offs = bpf_prog_info_read_offset_u64(&info_linear->info,
25562306a36Sopenharmony_ci						     desc->array_offset);
25662306a36Sopenharmony_ci		addr = offs + ptr_to_u64(info_linear->data);
25762306a36Sopenharmony_ci		bpf_prog_info_set_offset_u64(&info_linear->info,
25862306a36Sopenharmony_ci					     desc->array_offset, addr);
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci}
261