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