17c2aad20Sopenharmony_ci// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 27c2aad20Sopenharmony_ci/* Copyright (c) 2021 Facebook */ 37c2aad20Sopenharmony_ci#include <stdio.h> 47c2aad20Sopenharmony_ci#include <stdlib.h> 57c2aad20Sopenharmony_ci#include <string.h> 67c2aad20Sopenharmony_ci#include <errno.h> 77c2aad20Sopenharmony_ci#include <linux/filter.h> 87c2aad20Sopenharmony_ci#include <sys/param.h> 97c2aad20Sopenharmony_ci#include "btf.h" 107c2aad20Sopenharmony_ci#include "bpf.h" 117c2aad20Sopenharmony_ci#include "libbpf.h" 127c2aad20Sopenharmony_ci#include "libbpf_internal.h" 137c2aad20Sopenharmony_ci#include "hashmap.h" 147c2aad20Sopenharmony_ci#include "bpf_gen_internal.h" 157c2aad20Sopenharmony_ci#include "skel_internal.h" 167c2aad20Sopenharmony_ci#include <asm/byteorder.h> 177c2aad20Sopenharmony_ci 187c2aad20Sopenharmony_ci#define MAX_USED_MAPS 64 197c2aad20Sopenharmony_ci#define MAX_USED_PROGS 32 207c2aad20Sopenharmony_ci#define MAX_KFUNC_DESCS 256 217c2aad20Sopenharmony_ci#define MAX_FD_ARRAY_SZ (MAX_USED_MAPS + MAX_KFUNC_DESCS) 227c2aad20Sopenharmony_ci 237c2aad20Sopenharmony_ci/* The following structure describes the stack layout of the loader program. 247c2aad20Sopenharmony_ci * In addition R6 contains the pointer to context. 257c2aad20Sopenharmony_ci * R7 contains the result of the last sys_bpf command (typically error or FD). 267c2aad20Sopenharmony_ci * R9 contains the result of the last sys_close command. 277c2aad20Sopenharmony_ci * 287c2aad20Sopenharmony_ci * Naming convention: 297c2aad20Sopenharmony_ci * ctx - bpf program context 307c2aad20Sopenharmony_ci * stack - bpf program stack 317c2aad20Sopenharmony_ci * blob - bpf_attr-s, strings, insns, map data. 327c2aad20Sopenharmony_ci * All the bytes that loader prog will use for read/write. 337c2aad20Sopenharmony_ci */ 347c2aad20Sopenharmony_cistruct loader_stack { 357c2aad20Sopenharmony_ci __u32 btf_fd; 367c2aad20Sopenharmony_ci __u32 inner_map_fd; 377c2aad20Sopenharmony_ci __u32 prog_fd[MAX_USED_PROGS]; 387c2aad20Sopenharmony_ci}; 397c2aad20Sopenharmony_ci 407c2aad20Sopenharmony_ci#define stack_off(field) \ 417c2aad20Sopenharmony_ci (__s16)(-sizeof(struct loader_stack) + offsetof(struct loader_stack, field)) 427c2aad20Sopenharmony_ci 437c2aad20Sopenharmony_ci#define attr_field(attr, field) (attr + offsetof(union bpf_attr, field)) 447c2aad20Sopenharmony_ci 457c2aad20Sopenharmony_cistatic int blob_fd_array_off(struct bpf_gen *gen, int index) 467c2aad20Sopenharmony_ci{ 477c2aad20Sopenharmony_ci return gen->fd_array + index * sizeof(int); 487c2aad20Sopenharmony_ci} 497c2aad20Sopenharmony_ci 507c2aad20Sopenharmony_cistatic int realloc_insn_buf(struct bpf_gen *gen, __u32 size) 517c2aad20Sopenharmony_ci{ 527c2aad20Sopenharmony_ci size_t off = gen->insn_cur - gen->insn_start; 537c2aad20Sopenharmony_ci void *insn_start; 547c2aad20Sopenharmony_ci 557c2aad20Sopenharmony_ci if (gen->error) 567c2aad20Sopenharmony_ci return gen->error; 577c2aad20Sopenharmony_ci if (size > INT32_MAX || off + size > INT32_MAX) { 587c2aad20Sopenharmony_ci gen->error = -ERANGE; 597c2aad20Sopenharmony_ci return -ERANGE; 607c2aad20Sopenharmony_ci } 617c2aad20Sopenharmony_ci insn_start = realloc(gen->insn_start, off + size); 627c2aad20Sopenharmony_ci if (!insn_start) { 637c2aad20Sopenharmony_ci gen->error = -ENOMEM; 647c2aad20Sopenharmony_ci free(gen->insn_start); 657c2aad20Sopenharmony_ci gen->insn_start = NULL; 667c2aad20Sopenharmony_ci return -ENOMEM; 677c2aad20Sopenharmony_ci } 687c2aad20Sopenharmony_ci gen->insn_start = insn_start; 697c2aad20Sopenharmony_ci gen->insn_cur = insn_start + off; 707c2aad20Sopenharmony_ci return 0; 717c2aad20Sopenharmony_ci} 727c2aad20Sopenharmony_ci 737c2aad20Sopenharmony_cistatic int realloc_data_buf(struct bpf_gen *gen, __u32 size) 747c2aad20Sopenharmony_ci{ 757c2aad20Sopenharmony_ci size_t off = gen->data_cur - gen->data_start; 767c2aad20Sopenharmony_ci void *data_start; 777c2aad20Sopenharmony_ci 787c2aad20Sopenharmony_ci if (gen->error) 797c2aad20Sopenharmony_ci return gen->error; 807c2aad20Sopenharmony_ci if (size > INT32_MAX || off + size > INT32_MAX) { 817c2aad20Sopenharmony_ci gen->error = -ERANGE; 827c2aad20Sopenharmony_ci return -ERANGE; 837c2aad20Sopenharmony_ci } 847c2aad20Sopenharmony_ci data_start = realloc(gen->data_start, off + size); 857c2aad20Sopenharmony_ci if (!data_start) { 867c2aad20Sopenharmony_ci gen->error = -ENOMEM; 877c2aad20Sopenharmony_ci free(gen->data_start); 887c2aad20Sopenharmony_ci gen->data_start = NULL; 897c2aad20Sopenharmony_ci return -ENOMEM; 907c2aad20Sopenharmony_ci } 917c2aad20Sopenharmony_ci gen->data_start = data_start; 927c2aad20Sopenharmony_ci gen->data_cur = data_start + off; 937c2aad20Sopenharmony_ci return 0; 947c2aad20Sopenharmony_ci} 957c2aad20Sopenharmony_ci 967c2aad20Sopenharmony_cistatic void emit(struct bpf_gen *gen, struct bpf_insn insn) 977c2aad20Sopenharmony_ci{ 987c2aad20Sopenharmony_ci if (realloc_insn_buf(gen, sizeof(insn))) 997c2aad20Sopenharmony_ci return; 1007c2aad20Sopenharmony_ci memcpy(gen->insn_cur, &insn, sizeof(insn)); 1017c2aad20Sopenharmony_ci gen->insn_cur += sizeof(insn); 1027c2aad20Sopenharmony_ci} 1037c2aad20Sopenharmony_ci 1047c2aad20Sopenharmony_cistatic void emit2(struct bpf_gen *gen, struct bpf_insn insn1, struct bpf_insn insn2) 1057c2aad20Sopenharmony_ci{ 1067c2aad20Sopenharmony_ci emit(gen, insn1); 1077c2aad20Sopenharmony_ci emit(gen, insn2); 1087c2aad20Sopenharmony_ci} 1097c2aad20Sopenharmony_ci 1107c2aad20Sopenharmony_cistatic int add_data(struct bpf_gen *gen, const void *data, __u32 size); 1117c2aad20Sopenharmony_cistatic void emit_sys_close_blob(struct bpf_gen *gen, int blob_off); 1127c2aad20Sopenharmony_ci 1137c2aad20Sopenharmony_civoid bpf_gen__init(struct bpf_gen *gen, int log_level, int nr_progs, int nr_maps) 1147c2aad20Sopenharmony_ci{ 1157c2aad20Sopenharmony_ci size_t stack_sz = sizeof(struct loader_stack), nr_progs_sz; 1167c2aad20Sopenharmony_ci int i; 1177c2aad20Sopenharmony_ci 1187c2aad20Sopenharmony_ci gen->fd_array = add_data(gen, NULL, MAX_FD_ARRAY_SZ * sizeof(int)); 1197c2aad20Sopenharmony_ci gen->log_level = log_level; 1207c2aad20Sopenharmony_ci /* save ctx pointer into R6 */ 1217c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_REG(BPF_REG_6, BPF_REG_1)); 1227c2aad20Sopenharmony_ci 1237c2aad20Sopenharmony_ci /* bzero stack */ 1247c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_REG(BPF_REG_1, BPF_REG_10)); 1257c2aad20Sopenharmony_ci emit(gen, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -stack_sz)); 1267c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_IMM(BPF_REG_2, stack_sz)); 1277c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_IMM(BPF_REG_3, 0)); 1287c2aad20Sopenharmony_ci emit(gen, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel)); 1297c2aad20Sopenharmony_ci 1307c2aad20Sopenharmony_ci /* amount of stack actually used, only used to calculate iterations, not stack offset */ 1317c2aad20Sopenharmony_ci nr_progs_sz = offsetof(struct loader_stack, prog_fd[nr_progs]); 1327c2aad20Sopenharmony_ci /* jump over cleanup code */ 1337c2aad20Sopenharmony_ci emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 1347c2aad20Sopenharmony_ci /* size of cleanup code below (including map fd cleanup) */ 1357c2aad20Sopenharmony_ci (nr_progs_sz / 4) * 3 + 2 + 1367c2aad20Sopenharmony_ci /* 6 insns for emit_sys_close_blob, 1377c2aad20Sopenharmony_ci * 6 insns for debug_regs in emit_sys_close_blob 1387c2aad20Sopenharmony_ci */ 1397c2aad20Sopenharmony_ci nr_maps * (6 + (gen->log_level ? 6 : 0)))); 1407c2aad20Sopenharmony_ci 1417c2aad20Sopenharmony_ci /* remember the label where all error branches will jump to */ 1427c2aad20Sopenharmony_ci gen->cleanup_label = gen->insn_cur - gen->insn_start; 1437c2aad20Sopenharmony_ci /* emit cleanup code: close all temp FDs */ 1447c2aad20Sopenharmony_ci for (i = 0; i < nr_progs_sz; i += 4) { 1457c2aad20Sopenharmony_ci emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_10, -stack_sz + i)); 1467c2aad20Sopenharmony_ci emit(gen, BPF_JMP_IMM(BPF_JSLE, BPF_REG_1, 0, 1)); 1477c2aad20Sopenharmony_ci emit(gen, BPF_EMIT_CALL(BPF_FUNC_sys_close)); 1487c2aad20Sopenharmony_ci } 1497c2aad20Sopenharmony_ci for (i = 0; i < nr_maps; i++) 1507c2aad20Sopenharmony_ci emit_sys_close_blob(gen, blob_fd_array_off(gen, i)); 1517c2aad20Sopenharmony_ci /* R7 contains the error code from sys_bpf. Copy it into R0 and exit. */ 1527c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_REG(BPF_REG_0, BPF_REG_7)); 1537c2aad20Sopenharmony_ci emit(gen, BPF_EXIT_INSN()); 1547c2aad20Sopenharmony_ci} 1557c2aad20Sopenharmony_ci 1567c2aad20Sopenharmony_cistatic int add_data(struct bpf_gen *gen, const void *data, __u32 size) 1577c2aad20Sopenharmony_ci{ 1587c2aad20Sopenharmony_ci __u32 size8 = roundup(size, 8); 1597c2aad20Sopenharmony_ci __u64 zero = 0; 1607c2aad20Sopenharmony_ci void *prev; 1617c2aad20Sopenharmony_ci 1627c2aad20Sopenharmony_ci if (realloc_data_buf(gen, size8)) 1637c2aad20Sopenharmony_ci return 0; 1647c2aad20Sopenharmony_ci prev = gen->data_cur; 1657c2aad20Sopenharmony_ci if (data) { 1667c2aad20Sopenharmony_ci memcpy(gen->data_cur, data, size); 1677c2aad20Sopenharmony_ci memcpy(gen->data_cur + size, &zero, size8 - size); 1687c2aad20Sopenharmony_ci } else { 1697c2aad20Sopenharmony_ci memset(gen->data_cur, 0, size8); 1707c2aad20Sopenharmony_ci } 1717c2aad20Sopenharmony_ci gen->data_cur += size8; 1727c2aad20Sopenharmony_ci return prev - gen->data_start; 1737c2aad20Sopenharmony_ci} 1747c2aad20Sopenharmony_ci 1757c2aad20Sopenharmony_ci/* Get index for map_fd/btf_fd slot in reserved fd_array, or in data relative 1767c2aad20Sopenharmony_ci * to start of fd_array. Caller can decide if it is usable or not. 1777c2aad20Sopenharmony_ci */ 1787c2aad20Sopenharmony_cistatic int add_map_fd(struct bpf_gen *gen) 1797c2aad20Sopenharmony_ci{ 1807c2aad20Sopenharmony_ci if (gen->nr_maps == MAX_USED_MAPS) { 1817c2aad20Sopenharmony_ci pr_warn("Total maps exceeds %d\n", MAX_USED_MAPS); 1827c2aad20Sopenharmony_ci gen->error = -E2BIG; 1837c2aad20Sopenharmony_ci return 0; 1847c2aad20Sopenharmony_ci } 1857c2aad20Sopenharmony_ci return gen->nr_maps++; 1867c2aad20Sopenharmony_ci} 1877c2aad20Sopenharmony_ci 1887c2aad20Sopenharmony_cistatic int add_kfunc_btf_fd(struct bpf_gen *gen) 1897c2aad20Sopenharmony_ci{ 1907c2aad20Sopenharmony_ci int cur; 1917c2aad20Sopenharmony_ci 1927c2aad20Sopenharmony_ci if (gen->nr_fd_array == MAX_KFUNC_DESCS) { 1937c2aad20Sopenharmony_ci cur = add_data(gen, NULL, sizeof(int)); 1947c2aad20Sopenharmony_ci return (cur - gen->fd_array) / sizeof(int); 1957c2aad20Sopenharmony_ci } 1967c2aad20Sopenharmony_ci return MAX_USED_MAPS + gen->nr_fd_array++; 1977c2aad20Sopenharmony_ci} 1987c2aad20Sopenharmony_ci 1997c2aad20Sopenharmony_cistatic int insn_bytes_to_bpf_size(__u32 sz) 2007c2aad20Sopenharmony_ci{ 2017c2aad20Sopenharmony_ci switch (sz) { 2027c2aad20Sopenharmony_ci case 8: return BPF_DW; 2037c2aad20Sopenharmony_ci case 4: return BPF_W; 2047c2aad20Sopenharmony_ci case 2: return BPF_H; 2057c2aad20Sopenharmony_ci case 1: return BPF_B; 2067c2aad20Sopenharmony_ci default: return -1; 2077c2aad20Sopenharmony_ci } 2087c2aad20Sopenharmony_ci} 2097c2aad20Sopenharmony_ci 2107c2aad20Sopenharmony_ci/* *(u64 *)(blob + off) = (u64)(void *)(blob + data) */ 2117c2aad20Sopenharmony_cistatic void emit_rel_store(struct bpf_gen *gen, int off, int data) 2127c2aad20Sopenharmony_ci{ 2137c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_0, BPF_PSEUDO_MAP_IDX_VALUE, 2147c2aad20Sopenharmony_ci 0, 0, 0, data)); 2157c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE, 2167c2aad20Sopenharmony_ci 0, 0, 0, off)); 2177c2aad20Sopenharmony_ci emit(gen, BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 0)); 2187c2aad20Sopenharmony_ci} 2197c2aad20Sopenharmony_ci 2207c2aad20Sopenharmony_cistatic void move_blob2blob(struct bpf_gen *gen, int off, int size, int blob_off) 2217c2aad20Sopenharmony_ci{ 2227c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_2, BPF_PSEUDO_MAP_IDX_VALUE, 2237c2aad20Sopenharmony_ci 0, 0, 0, blob_off)); 2247c2aad20Sopenharmony_ci emit(gen, BPF_LDX_MEM(insn_bytes_to_bpf_size(size), BPF_REG_0, BPF_REG_2, 0)); 2257c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE, 2267c2aad20Sopenharmony_ci 0, 0, 0, off)); 2277c2aad20Sopenharmony_ci emit(gen, BPF_STX_MEM(insn_bytes_to_bpf_size(size), BPF_REG_1, BPF_REG_0, 0)); 2287c2aad20Sopenharmony_ci} 2297c2aad20Sopenharmony_ci 2307c2aad20Sopenharmony_cistatic void move_blob2ctx(struct bpf_gen *gen, int ctx_off, int size, int blob_off) 2317c2aad20Sopenharmony_ci{ 2327c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE, 2337c2aad20Sopenharmony_ci 0, 0, 0, blob_off)); 2347c2aad20Sopenharmony_ci emit(gen, BPF_LDX_MEM(insn_bytes_to_bpf_size(size), BPF_REG_0, BPF_REG_1, 0)); 2357c2aad20Sopenharmony_ci emit(gen, BPF_STX_MEM(insn_bytes_to_bpf_size(size), BPF_REG_6, BPF_REG_0, ctx_off)); 2367c2aad20Sopenharmony_ci} 2377c2aad20Sopenharmony_ci 2387c2aad20Sopenharmony_cistatic void move_ctx2blob(struct bpf_gen *gen, int off, int size, int ctx_off, 2397c2aad20Sopenharmony_ci bool check_non_zero) 2407c2aad20Sopenharmony_ci{ 2417c2aad20Sopenharmony_ci emit(gen, BPF_LDX_MEM(insn_bytes_to_bpf_size(size), BPF_REG_0, BPF_REG_6, ctx_off)); 2427c2aad20Sopenharmony_ci if (check_non_zero) 2437c2aad20Sopenharmony_ci /* If value in ctx is zero don't update the blob. 2447c2aad20Sopenharmony_ci * For example: when ctx->map.max_entries == 0, keep default max_entries from bpf.c 2457c2aad20Sopenharmony_ci */ 2467c2aad20Sopenharmony_ci emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3)); 2477c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE, 2487c2aad20Sopenharmony_ci 0, 0, 0, off)); 2497c2aad20Sopenharmony_ci emit(gen, BPF_STX_MEM(insn_bytes_to_bpf_size(size), BPF_REG_1, BPF_REG_0, 0)); 2507c2aad20Sopenharmony_ci} 2517c2aad20Sopenharmony_ci 2527c2aad20Sopenharmony_cistatic void move_stack2blob(struct bpf_gen *gen, int off, int size, int stack_off) 2537c2aad20Sopenharmony_ci{ 2547c2aad20Sopenharmony_ci emit(gen, BPF_LDX_MEM(insn_bytes_to_bpf_size(size), BPF_REG_0, BPF_REG_10, stack_off)); 2557c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE, 2567c2aad20Sopenharmony_ci 0, 0, 0, off)); 2577c2aad20Sopenharmony_ci emit(gen, BPF_STX_MEM(insn_bytes_to_bpf_size(size), BPF_REG_1, BPF_REG_0, 0)); 2587c2aad20Sopenharmony_ci} 2597c2aad20Sopenharmony_ci 2607c2aad20Sopenharmony_cistatic void move_stack2ctx(struct bpf_gen *gen, int ctx_off, int size, int stack_off) 2617c2aad20Sopenharmony_ci{ 2627c2aad20Sopenharmony_ci emit(gen, BPF_LDX_MEM(insn_bytes_to_bpf_size(size), BPF_REG_0, BPF_REG_10, stack_off)); 2637c2aad20Sopenharmony_ci emit(gen, BPF_STX_MEM(insn_bytes_to_bpf_size(size), BPF_REG_6, BPF_REG_0, ctx_off)); 2647c2aad20Sopenharmony_ci} 2657c2aad20Sopenharmony_ci 2667c2aad20Sopenharmony_cistatic void emit_sys_bpf(struct bpf_gen *gen, int cmd, int attr, int attr_size) 2677c2aad20Sopenharmony_ci{ 2687c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_IMM(BPF_REG_1, cmd)); 2697c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_2, BPF_PSEUDO_MAP_IDX_VALUE, 2707c2aad20Sopenharmony_ci 0, 0, 0, attr)); 2717c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_IMM(BPF_REG_3, attr_size)); 2727c2aad20Sopenharmony_ci emit(gen, BPF_EMIT_CALL(BPF_FUNC_sys_bpf)); 2737c2aad20Sopenharmony_ci /* remember the result in R7 */ 2747c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_REG(BPF_REG_7, BPF_REG_0)); 2757c2aad20Sopenharmony_ci} 2767c2aad20Sopenharmony_ci 2777c2aad20Sopenharmony_cistatic bool is_simm16(__s64 value) 2787c2aad20Sopenharmony_ci{ 2797c2aad20Sopenharmony_ci return value == (__s64)(__s16)value; 2807c2aad20Sopenharmony_ci} 2817c2aad20Sopenharmony_ci 2827c2aad20Sopenharmony_cistatic void emit_check_err(struct bpf_gen *gen) 2837c2aad20Sopenharmony_ci{ 2847c2aad20Sopenharmony_ci __s64 off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 1; 2857c2aad20Sopenharmony_ci 2867c2aad20Sopenharmony_ci /* R7 contains result of last sys_bpf command. 2877c2aad20Sopenharmony_ci * if (R7 < 0) goto cleanup; 2887c2aad20Sopenharmony_ci */ 2897c2aad20Sopenharmony_ci if (is_simm16(off)) { 2907c2aad20Sopenharmony_ci emit(gen, BPF_JMP_IMM(BPF_JSLT, BPF_REG_7, 0, off)); 2917c2aad20Sopenharmony_ci } else { 2927c2aad20Sopenharmony_ci gen->error = -ERANGE; 2937c2aad20Sopenharmony_ci emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, -1)); 2947c2aad20Sopenharmony_ci } 2957c2aad20Sopenharmony_ci} 2967c2aad20Sopenharmony_ci 2977c2aad20Sopenharmony_ci/* reg1 and reg2 should not be R1 - R5. They can be R0, R6 - R10 */ 2987c2aad20Sopenharmony_cistatic void emit_debug(struct bpf_gen *gen, int reg1, int reg2, 2997c2aad20Sopenharmony_ci const char *fmt, va_list args) 3007c2aad20Sopenharmony_ci{ 3017c2aad20Sopenharmony_ci char buf[1024]; 3027c2aad20Sopenharmony_ci int addr, len, ret; 3037c2aad20Sopenharmony_ci 3047c2aad20Sopenharmony_ci if (!gen->log_level) 3057c2aad20Sopenharmony_ci return; 3067c2aad20Sopenharmony_ci ret = vsnprintf(buf, sizeof(buf), fmt, args); 3077c2aad20Sopenharmony_ci if (ret < 1024 - 7 && reg1 >= 0 && reg2 < 0) 3087c2aad20Sopenharmony_ci /* The special case to accommodate common debug_ret(): 3097c2aad20Sopenharmony_ci * to avoid specifying BPF_REG_7 and adding " r=%%d" to 3107c2aad20Sopenharmony_ci * prints explicitly. 3117c2aad20Sopenharmony_ci */ 3127c2aad20Sopenharmony_ci strcat(buf, " r=%d"); 3137c2aad20Sopenharmony_ci len = strlen(buf) + 1; 3147c2aad20Sopenharmony_ci addr = add_data(gen, buf, len); 3157c2aad20Sopenharmony_ci 3167c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE, 3177c2aad20Sopenharmony_ci 0, 0, 0, addr)); 3187c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_IMM(BPF_REG_2, len)); 3197c2aad20Sopenharmony_ci if (reg1 >= 0) 3207c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_REG(BPF_REG_3, reg1)); 3217c2aad20Sopenharmony_ci if (reg2 >= 0) 3227c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_REG(BPF_REG_4, reg2)); 3237c2aad20Sopenharmony_ci emit(gen, BPF_EMIT_CALL(BPF_FUNC_trace_printk)); 3247c2aad20Sopenharmony_ci} 3257c2aad20Sopenharmony_ci 3267c2aad20Sopenharmony_cistatic void debug_regs(struct bpf_gen *gen, int reg1, int reg2, const char *fmt, ...) 3277c2aad20Sopenharmony_ci{ 3287c2aad20Sopenharmony_ci va_list args; 3297c2aad20Sopenharmony_ci 3307c2aad20Sopenharmony_ci va_start(args, fmt); 3317c2aad20Sopenharmony_ci emit_debug(gen, reg1, reg2, fmt, args); 3327c2aad20Sopenharmony_ci va_end(args); 3337c2aad20Sopenharmony_ci} 3347c2aad20Sopenharmony_ci 3357c2aad20Sopenharmony_cistatic void debug_ret(struct bpf_gen *gen, const char *fmt, ...) 3367c2aad20Sopenharmony_ci{ 3377c2aad20Sopenharmony_ci va_list args; 3387c2aad20Sopenharmony_ci 3397c2aad20Sopenharmony_ci va_start(args, fmt); 3407c2aad20Sopenharmony_ci emit_debug(gen, BPF_REG_7, -1, fmt, args); 3417c2aad20Sopenharmony_ci va_end(args); 3427c2aad20Sopenharmony_ci} 3437c2aad20Sopenharmony_ci 3447c2aad20Sopenharmony_cistatic void __emit_sys_close(struct bpf_gen *gen) 3457c2aad20Sopenharmony_ci{ 3467c2aad20Sopenharmony_ci emit(gen, BPF_JMP_IMM(BPF_JSLE, BPF_REG_1, 0, 3477c2aad20Sopenharmony_ci /* 2 is the number of the following insns 3487c2aad20Sopenharmony_ci * * 6 is additional insns in debug_regs 3497c2aad20Sopenharmony_ci */ 3507c2aad20Sopenharmony_ci 2 + (gen->log_level ? 6 : 0))); 3517c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_REG(BPF_REG_9, BPF_REG_1)); 3527c2aad20Sopenharmony_ci emit(gen, BPF_EMIT_CALL(BPF_FUNC_sys_close)); 3537c2aad20Sopenharmony_ci debug_regs(gen, BPF_REG_9, BPF_REG_0, "close(%%d) = %%d"); 3547c2aad20Sopenharmony_ci} 3557c2aad20Sopenharmony_ci 3567c2aad20Sopenharmony_cistatic void emit_sys_close_stack(struct bpf_gen *gen, int stack_off) 3577c2aad20Sopenharmony_ci{ 3587c2aad20Sopenharmony_ci emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_10, stack_off)); 3597c2aad20Sopenharmony_ci __emit_sys_close(gen); 3607c2aad20Sopenharmony_ci} 3617c2aad20Sopenharmony_ci 3627c2aad20Sopenharmony_cistatic void emit_sys_close_blob(struct bpf_gen *gen, int blob_off) 3637c2aad20Sopenharmony_ci{ 3647c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_0, BPF_PSEUDO_MAP_IDX_VALUE, 3657c2aad20Sopenharmony_ci 0, 0, 0, blob_off)); 3667c2aad20Sopenharmony_ci emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0)); 3677c2aad20Sopenharmony_ci __emit_sys_close(gen); 3687c2aad20Sopenharmony_ci} 3697c2aad20Sopenharmony_ci 3707c2aad20Sopenharmony_ciint bpf_gen__finish(struct bpf_gen *gen, int nr_progs, int nr_maps) 3717c2aad20Sopenharmony_ci{ 3727c2aad20Sopenharmony_ci int i; 3737c2aad20Sopenharmony_ci 3747c2aad20Sopenharmony_ci if (nr_progs < gen->nr_progs || nr_maps != gen->nr_maps) { 3757c2aad20Sopenharmony_ci pr_warn("nr_progs %d/%d nr_maps %d/%d mismatch\n", 3767c2aad20Sopenharmony_ci nr_progs, gen->nr_progs, nr_maps, gen->nr_maps); 3777c2aad20Sopenharmony_ci gen->error = -EFAULT; 3787c2aad20Sopenharmony_ci return gen->error; 3797c2aad20Sopenharmony_ci } 3807c2aad20Sopenharmony_ci emit_sys_close_stack(gen, stack_off(btf_fd)); 3817c2aad20Sopenharmony_ci for (i = 0; i < gen->nr_progs; i++) 3827c2aad20Sopenharmony_ci move_stack2ctx(gen, 3837c2aad20Sopenharmony_ci sizeof(struct bpf_loader_ctx) + 3847c2aad20Sopenharmony_ci sizeof(struct bpf_map_desc) * gen->nr_maps + 3857c2aad20Sopenharmony_ci sizeof(struct bpf_prog_desc) * i + 3867c2aad20Sopenharmony_ci offsetof(struct bpf_prog_desc, prog_fd), 4, 3877c2aad20Sopenharmony_ci stack_off(prog_fd[i])); 3887c2aad20Sopenharmony_ci for (i = 0; i < gen->nr_maps; i++) 3897c2aad20Sopenharmony_ci move_blob2ctx(gen, 3907c2aad20Sopenharmony_ci sizeof(struct bpf_loader_ctx) + 3917c2aad20Sopenharmony_ci sizeof(struct bpf_map_desc) * i + 3927c2aad20Sopenharmony_ci offsetof(struct bpf_map_desc, map_fd), 4, 3937c2aad20Sopenharmony_ci blob_fd_array_off(gen, i)); 3947c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_IMM(BPF_REG_0, 0)); 3957c2aad20Sopenharmony_ci emit(gen, BPF_EXIT_INSN()); 3967c2aad20Sopenharmony_ci pr_debug("gen: finish %d\n", gen->error); 3977c2aad20Sopenharmony_ci if (!gen->error) { 3987c2aad20Sopenharmony_ci struct gen_loader_opts *opts = gen->opts; 3997c2aad20Sopenharmony_ci 4007c2aad20Sopenharmony_ci opts->insns = gen->insn_start; 4017c2aad20Sopenharmony_ci opts->insns_sz = gen->insn_cur - gen->insn_start; 4027c2aad20Sopenharmony_ci opts->data = gen->data_start; 4037c2aad20Sopenharmony_ci opts->data_sz = gen->data_cur - gen->data_start; 4047c2aad20Sopenharmony_ci } 4057c2aad20Sopenharmony_ci return gen->error; 4067c2aad20Sopenharmony_ci} 4077c2aad20Sopenharmony_ci 4087c2aad20Sopenharmony_civoid bpf_gen__free(struct bpf_gen *gen) 4097c2aad20Sopenharmony_ci{ 4107c2aad20Sopenharmony_ci if (!gen) 4117c2aad20Sopenharmony_ci return; 4127c2aad20Sopenharmony_ci free(gen->data_start); 4137c2aad20Sopenharmony_ci free(gen->insn_start); 4147c2aad20Sopenharmony_ci free(gen); 4157c2aad20Sopenharmony_ci} 4167c2aad20Sopenharmony_ci 4177c2aad20Sopenharmony_civoid bpf_gen__load_btf(struct bpf_gen *gen, const void *btf_raw_data, 4187c2aad20Sopenharmony_ci __u32 btf_raw_size) 4197c2aad20Sopenharmony_ci{ 4207c2aad20Sopenharmony_ci int attr_size = offsetofend(union bpf_attr, btf_log_level); 4217c2aad20Sopenharmony_ci int btf_data, btf_load_attr; 4227c2aad20Sopenharmony_ci union bpf_attr attr; 4237c2aad20Sopenharmony_ci 4247c2aad20Sopenharmony_ci memset(&attr, 0, attr_size); 4257c2aad20Sopenharmony_ci pr_debug("gen: load_btf: size %d\n", btf_raw_size); 4267c2aad20Sopenharmony_ci btf_data = add_data(gen, btf_raw_data, btf_raw_size); 4277c2aad20Sopenharmony_ci 4287c2aad20Sopenharmony_ci attr.btf_size = btf_raw_size; 4297c2aad20Sopenharmony_ci btf_load_attr = add_data(gen, &attr, attr_size); 4307c2aad20Sopenharmony_ci 4317c2aad20Sopenharmony_ci /* populate union bpf_attr with user provided log details */ 4327c2aad20Sopenharmony_ci move_ctx2blob(gen, attr_field(btf_load_attr, btf_log_level), 4, 4337c2aad20Sopenharmony_ci offsetof(struct bpf_loader_ctx, log_level), false); 4347c2aad20Sopenharmony_ci move_ctx2blob(gen, attr_field(btf_load_attr, btf_log_size), 4, 4357c2aad20Sopenharmony_ci offsetof(struct bpf_loader_ctx, log_size), false); 4367c2aad20Sopenharmony_ci move_ctx2blob(gen, attr_field(btf_load_attr, btf_log_buf), 8, 4377c2aad20Sopenharmony_ci offsetof(struct bpf_loader_ctx, log_buf), false); 4387c2aad20Sopenharmony_ci /* populate union bpf_attr with a pointer to the BTF data */ 4397c2aad20Sopenharmony_ci emit_rel_store(gen, attr_field(btf_load_attr, btf), btf_data); 4407c2aad20Sopenharmony_ci /* emit BTF_LOAD command */ 4417c2aad20Sopenharmony_ci emit_sys_bpf(gen, BPF_BTF_LOAD, btf_load_attr, attr_size); 4427c2aad20Sopenharmony_ci debug_ret(gen, "btf_load size %d", btf_raw_size); 4437c2aad20Sopenharmony_ci emit_check_err(gen); 4447c2aad20Sopenharmony_ci /* remember btf_fd in the stack, if successful */ 4457c2aad20Sopenharmony_ci emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_7, stack_off(btf_fd))); 4467c2aad20Sopenharmony_ci} 4477c2aad20Sopenharmony_ci 4487c2aad20Sopenharmony_civoid bpf_gen__map_create(struct bpf_gen *gen, 4497c2aad20Sopenharmony_ci enum bpf_map_type map_type, 4507c2aad20Sopenharmony_ci const char *map_name, 4517c2aad20Sopenharmony_ci __u32 key_size, __u32 value_size, __u32 max_entries, 4527c2aad20Sopenharmony_ci struct bpf_map_create_opts *map_attr, int map_idx) 4537c2aad20Sopenharmony_ci{ 4547c2aad20Sopenharmony_ci int attr_size = offsetofend(union bpf_attr, map_extra); 4557c2aad20Sopenharmony_ci bool close_inner_map_fd = false; 4567c2aad20Sopenharmony_ci int map_create_attr, idx; 4577c2aad20Sopenharmony_ci union bpf_attr attr; 4587c2aad20Sopenharmony_ci 4597c2aad20Sopenharmony_ci memset(&attr, 0, attr_size); 4607c2aad20Sopenharmony_ci attr.map_type = map_type; 4617c2aad20Sopenharmony_ci attr.key_size = key_size; 4627c2aad20Sopenharmony_ci attr.value_size = value_size; 4637c2aad20Sopenharmony_ci attr.map_flags = map_attr->map_flags; 4647c2aad20Sopenharmony_ci attr.map_extra = map_attr->map_extra; 4657c2aad20Sopenharmony_ci if (map_name) 4667c2aad20Sopenharmony_ci libbpf_strlcpy(attr.map_name, map_name, sizeof(attr.map_name)); 4677c2aad20Sopenharmony_ci attr.numa_node = map_attr->numa_node; 4687c2aad20Sopenharmony_ci attr.map_ifindex = map_attr->map_ifindex; 4697c2aad20Sopenharmony_ci attr.max_entries = max_entries; 4707c2aad20Sopenharmony_ci attr.btf_key_type_id = map_attr->btf_key_type_id; 4717c2aad20Sopenharmony_ci attr.btf_value_type_id = map_attr->btf_value_type_id; 4727c2aad20Sopenharmony_ci 4737c2aad20Sopenharmony_ci pr_debug("gen: map_create: %s idx %d type %d value_type_id %d\n", 4747c2aad20Sopenharmony_ci attr.map_name, map_idx, map_type, attr.btf_value_type_id); 4757c2aad20Sopenharmony_ci 4767c2aad20Sopenharmony_ci map_create_attr = add_data(gen, &attr, attr_size); 4777c2aad20Sopenharmony_ci if (attr.btf_value_type_id) 4787c2aad20Sopenharmony_ci /* populate union bpf_attr with btf_fd saved in the stack earlier */ 4797c2aad20Sopenharmony_ci move_stack2blob(gen, attr_field(map_create_attr, btf_fd), 4, 4807c2aad20Sopenharmony_ci stack_off(btf_fd)); 4817c2aad20Sopenharmony_ci switch (attr.map_type) { 4827c2aad20Sopenharmony_ci case BPF_MAP_TYPE_ARRAY_OF_MAPS: 4837c2aad20Sopenharmony_ci case BPF_MAP_TYPE_HASH_OF_MAPS: 4847c2aad20Sopenharmony_ci move_stack2blob(gen, attr_field(map_create_attr, inner_map_fd), 4, 4857c2aad20Sopenharmony_ci stack_off(inner_map_fd)); 4867c2aad20Sopenharmony_ci close_inner_map_fd = true; 4877c2aad20Sopenharmony_ci break; 4887c2aad20Sopenharmony_ci default: 4897c2aad20Sopenharmony_ci break; 4907c2aad20Sopenharmony_ci } 4917c2aad20Sopenharmony_ci /* conditionally update max_entries */ 4927c2aad20Sopenharmony_ci if (map_idx >= 0) 4937c2aad20Sopenharmony_ci move_ctx2blob(gen, attr_field(map_create_attr, max_entries), 4, 4947c2aad20Sopenharmony_ci sizeof(struct bpf_loader_ctx) + 4957c2aad20Sopenharmony_ci sizeof(struct bpf_map_desc) * map_idx + 4967c2aad20Sopenharmony_ci offsetof(struct bpf_map_desc, max_entries), 4977c2aad20Sopenharmony_ci true /* check that max_entries != 0 */); 4987c2aad20Sopenharmony_ci /* emit MAP_CREATE command */ 4997c2aad20Sopenharmony_ci emit_sys_bpf(gen, BPF_MAP_CREATE, map_create_attr, attr_size); 5007c2aad20Sopenharmony_ci debug_ret(gen, "map_create %s idx %d type %d value_size %d value_btf_id %d", 5017c2aad20Sopenharmony_ci attr.map_name, map_idx, map_type, value_size, 5027c2aad20Sopenharmony_ci attr.btf_value_type_id); 5037c2aad20Sopenharmony_ci emit_check_err(gen); 5047c2aad20Sopenharmony_ci /* remember map_fd in the stack, if successful */ 5057c2aad20Sopenharmony_ci if (map_idx < 0) { 5067c2aad20Sopenharmony_ci /* This bpf_gen__map_create() function is called with map_idx >= 0 5077c2aad20Sopenharmony_ci * for all maps that libbpf loading logic tracks. 5087c2aad20Sopenharmony_ci * It's called with -1 to create an inner map. 5097c2aad20Sopenharmony_ci */ 5107c2aad20Sopenharmony_ci emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_7, 5117c2aad20Sopenharmony_ci stack_off(inner_map_fd))); 5127c2aad20Sopenharmony_ci } else if (map_idx != gen->nr_maps) { 5137c2aad20Sopenharmony_ci gen->error = -EDOM; /* internal bug */ 5147c2aad20Sopenharmony_ci return; 5157c2aad20Sopenharmony_ci } else { 5167c2aad20Sopenharmony_ci /* add_map_fd does gen->nr_maps++ */ 5177c2aad20Sopenharmony_ci idx = add_map_fd(gen); 5187c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE, 5197c2aad20Sopenharmony_ci 0, 0, 0, blob_fd_array_off(gen, idx))); 5207c2aad20Sopenharmony_ci emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_7, 0)); 5217c2aad20Sopenharmony_ci } 5227c2aad20Sopenharmony_ci if (close_inner_map_fd) 5237c2aad20Sopenharmony_ci emit_sys_close_stack(gen, stack_off(inner_map_fd)); 5247c2aad20Sopenharmony_ci} 5257c2aad20Sopenharmony_ci 5267c2aad20Sopenharmony_civoid bpf_gen__record_attach_target(struct bpf_gen *gen, const char *attach_name, 5277c2aad20Sopenharmony_ci enum bpf_attach_type type) 5287c2aad20Sopenharmony_ci{ 5297c2aad20Sopenharmony_ci const char *prefix; 5307c2aad20Sopenharmony_ci int kind, ret; 5317c2aad20Sopenharmony_ci 5327c2aad20Sopenharmony_ci btf_get_kernel_prefix_kind(type, &prefix, &kind); 5337c2aad20Sopenharmony_ci gen->attach_kind = kind; 5347c2aad20Sopenharmony_ci ret = snprintf(gen->attach_target, sizeof(gen->attach_target), "%s%s", 5357c2aad20Sopenharmony_ci prefix, attach_name); 5367c2aad20Sopenharmony_ci if (ret >= sizeof(gen->attach_target)) 5377c2aad20Sopenharmony_ci gen->error = -ENOSPC; 5387c2aad20Sopenharmony_ci} 5397c2aad20Sopenharmony_ci 5407c2aad20Sopenharmony_cistatic void emit_find_attach_target(struct bpf_gen *gen) 5417c2aad20Sopenharmony_ci{ 5427c2aad20Sopenharmony_ci int name, len = strlen(gen->attach_target) + 1; 5437c2aad20Sopenharmony_ci 5447c2aad20Sopenharmony_ci pr_debug("gen: find_attach_tgt %s %d\n", gen->attach_target, gen->attach_kind); 5457c2aad20Sopenharmony_ci name = add_data(gen, gen->attach_target, len); 5467c2aad20Sopenharmony_ci 5477c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE, 5487c2aad20Sopenharmony_ci 0, 0, 0, name)); 5497c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_IMM(BPF_REG_2, len)); 5507c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_IMM(BPF_REG_3, gen->attach_kind)); 5517c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_IMM(BPF_REG_4, 0)); 5527c2aad20Sopenharmony_ci emit(gen, BPF_EMIT_CALL(BPF_FUNC_btf_find_by_name_kind)); 5537c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_REG(BPF_REG_7, BPF_REG_0)); 5547c2aad20Sopenharmony_ci debug_ret(gen, "find_by_name_kind(%s,%d)", 5557c2aad20Sopenharmony_ci gen->attach_target, gen->attach_kind); 5567c2aad20Sopenharmony_ci emit_check_err(gen); 5577c2aad20Sopenharmony_ci /* if successful, btf_id is in lower 32-bit of R7 and 5587c2aad20Sopenharmony_ci * btf_obj_fd is in upper 32-bit 5597c2aad20Sopenharmony_ci */ 5607c2aad20Sopenharmony_ci} 5617c2aad20Sopenharmony_ci 5627c2aad20Sopenharmony_civoid bpf_gen__record_extern(struct bpf_gen *gen, const char *name, bool is_weak, 5637c2aad20Sopenharmony_ci bool is_typeless, bool is_ld64, int kind, int insn_idx) 5647c2aad20Sopenharmony_ci{ 5657c2aad20Sopenharmony_ci struct ksym_relo_desc *relo; 5667c2aad20Sopenharmony_ci 5677c2aad20Sopenharmony_ci relo = libbpf_reallocarray(gen->relos, gen->relo_cnt + 1, sizeof(*relo)); 5687c2aad20Sopenharmony_ci if (!relo) { 5697c2aad20Sopenharmony_ci gen->error = -ENOMEM; 5707c2aad20Sopenharmony_ci return; 5717c2aad20Sopenharmony_ci } 5727c2aad20Sopenharmony_ci gen->relos = relo; 5737c2aad20Sopenharmony_ci relo += gen->relo_cnt; 5747c2aad20Sopenharmony_ci relo->name = name; 5757c2aad20Sopenharmony_ci relo->is_weak = is_weak; 5767c2aad20Sopenharmony_ci relo->is_typeless = is_typeless; 5777c2aad20Sopenharmony_ci relo->is_ld64 = is_ld64; 5787c2aad20Sopenharmony_ci relo->kind = kind; 5797c2aad20Sopenharmony_ci relo->insn_idx = insn_idx; 5807c2aad20Sopenharmony_ci gen->relo_cnt++; 5817c2aad20Sopenharmony_ci} 5827c2aad20Sopenharmony_ci 5837c2aad20Sopenharmony_ci/* returns existing ksym_desc with ref incremented, or inserts a new one */ 5847c2aad20Sopenharmony_cistatic struct ksym_desc *get_ksym_desc(struct bpf_gen *gen, struct ksym_relo_desc *relo) 5857c2aad20Sopenharmony_ci{ 5867c2aad20Sopenharmony_ci struct ksym_desc *kdesc; 5877c2aad20Sopenharmony_ci int i; 5887c2aad20Sopenharmony_ci 5897c2aad20Sopenharmony_ci for (i = 0; i < gen->nr_ksyms; i++) { 5907c2aad20Sopenharmony_ci kdesc = &gen->ksyms[i]; 5917c2aad20Sopenharmony_ci if (kdesc->kind == relo->kind && kdesc->is_ld64 == relo->is_ld64 && 5927c2aad20Sopenharmony_ci !strcmp(kdesc->name, relo->name)) { 5937c2aad20Sopenharmony_ci kdesc->ref++; 5947c2aad20Sopenharmony_ci return kdesc; 5957c2aad20Sopenharmony_ci } 5967c2aad20Sopenharmony_ci } 5977c2aad20Sopenharmony_ci kdesc = libbpf_reallocarray(gen->ksyms, gen->nr_ksyms + 1, sizeof(*kdesc)); 5987c2aad20Sopenharmony_ci if (!kdesc) { 5997c2aad20Sopenharmony_ci gen->error = -ENOMEM; 6007c2aad20Sopenharmony_ci return NULL; 6017c2aad20Sopenharmony_ci } 6027c2aad20Sopenharmony_ci gen->ksyms = kdesc; 6037c2aad20Sopenharmony_ci kdesc = &gen->ksyms[gen->nr_ksyms++]; 6047c2aad20Sopenharmony_ci kdesc->name = relo->name; 6057c2aad20Sopenharmony_ci kdesc->kind = relo->kind; 6067c2aad20Sopenharmony_ci kdesc->ref = 1; 6077c2aad20Sopenharmony_ci kdesc->off = 0; 6087c2aad20Sopenharmony_ci kdesc->insn = 0; 6097c2aad20Sopenharmony_ci kdesc->is_ld64 = relo->is_ld64; 6107c2aad20Sopenharmony_ci return kdesc; 6117c2aad20Sopenharmony_ci} 6127c2aad20Sopenharmony_ci 6137c2aad20Sopenharmony_ci/* Overwrites BPF_REG_{0, 1, 2, 3, 4, 7} 6147c2aad20Sopenharmony_ci * Returns result in BPF_REG_7 6157c2aad20Sopenharmony_ci */ 6167c2aad20Sopenharmony_cistatic void emit_bpf_find_by_name_kind(struct bpf_gen *gen, struct ksym_relo_desc *relo) 6177c2aad20Sopenharmony_ci{ 6187c2aad20Sopenharmony_ci int name_off, len = strlen(relo->name) + 1; 6197c2aad20Sopenharmony_ci 6207c2aad20Sopenharmony_ci name_off = add_data(gen, relo->name, len); 6217c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE, 6227c2aad20Sopenharmony_ci 0, 0, 0, name_off)); 6237c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_IMM(BPF_REG_2, len)); 6247c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_IMM(BPF_REG_3, relo->kind)); 6257c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_IMM(BPF_REG_4, 0)); 6267c2aad20Sopenharmony_ci emit(gen, BPF_EMIT_CALL(BPF_FUNC_btf_find_by_name_kind)); 6277c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_REG(BPF_REG_7, BPF_REG_0)); 6287c2aad20Sopenharmony_ci debug_ret(gen, "find_by_name_kind(%s,%d)", relo->name, relo->kind); 6297c2aad20Sopenharmony_ci} 6307c2aad20Sopenharmony_ci 6317c2aad20Sopenharmony_ci/* Overwrites BPF_REG_{0, 1, 2, 3, 4, 7} 6327c2aad20Sopenharmony_ci * Returns result in BPF_REG_7 6337c2aad20Sopenharmony_ci * Returns u64 symbol addr in BPF_REG_9 6347c2aad20Sopenharmony_ci */ 6357c2aad20Sopenharmony_cistatic void emit_bpf_kallsyms_lookup_name(struct bpf_gen *gen, struct ksym_relo_desc *relo) 6367c2aad20Sopenharmony_ci{ 6377c2aad20Sopenharmony_ci int name_off, len = strlen(relo->name) + 1, res_off; 6387c2aad20Sopenharmony_ci 6397c2aad20Sopenharmony_ci name_off = add_data(gen, relo->name, len); 6407c2aad20Sopenharmony_ci res_off = add_data(gen, NULL, 8); /* res is u64 */ 6417c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE, 6427c2aad20Sopenharmony_ci 0, 0, 0, name_off)); 6437c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_IMM(BPF_REG_2, len)); 6447c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_IMM(BPF_REG_3, 0)); 6457c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_4, BPF_PSEUDO_MAP_IDX_VALUE, 6467c2aad20Sopenharmony_ci 0, 0, 0, res_off)); 6477c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_REG(BPF_REG_7, BPF_REG_4)); 6487c2aad20Sopenharmony_ci emit(gen, BPF_EMIT_CALL(BPF_FUNC_kallsyms_lookup_name)); 6497c2aad20Sopenharmony_ci emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0)); 6507c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_REG(BPF_REG_7, BPF_REG_0)); 6517c2aad20Sopenharmony_ci debug_ret(gen, "kallsyms_lookup_name(%s,%d)", relo->name, relo->kind); 6527c2aad20Sopenharmony_ci} 6537c2aad20Sopenharmony_ci 6547c2aad20Sopenharmony_ci/* Expects: 6557c2aad20Sopenharmony_ci * BPF_REG_8 - pointer to instruction 6567c2aad20Sopenharmony_ci * 6577c2aad20Sopenharmony_ci * We need to reuse BTF fd for same symbol otherwise each relocation takes a new 6587c2aad20Sopenharmony_ci * index, while kernel limits total kfunc BTFs to 256. For duplicate symbols, 6597c2aad20Sopenharmony_ci * this would mean a new BTF fd index for each entry. By pairing symbol name 6607c2aad20Sopenharmony_ci * with index, we get the insn->imm, insn->off pairing that kernel uses for 6617c2aad20Sopenharmony_ci * kfunc_tab, which becomes the effective limit even though all of them may 6627c2aad20Sopenharmony_ci * share same index in fd_array (such that kfunc_btf_tab has 1 element). 6637c2aad20Sopenharmony_ci */ 6647c2aad20Sopenharmony_cistatic void emit_relo_kfunc_btf(struct bpf_gen *gen, struct ksym_relo_desc *relo, int insn) 6657c2aad20Sopenharmony_ci{ 6667c2aad20Sopenharmony_ci struct ksym_desc *kdesc; 6677c2aad20Sopenharmony_ci int btf_fd_idx; 6687c2aad20Sopenharmony_ci 6697c2aad20Sopenharmony_ci kdesc = get_ksym_desc(gen, relo); 6707c2aad20Sopenharmony_ci if (!kdesc) 6717c2aad20Sopenharmony_ci return; 6727c2aad20Sopenharmony_ci /* try to copy from existing bpf_insn */ 6737c2aad20Sopenharmony_ci if (kdesc->ref > 1) { 6747c2aad20Sopenharmony_ci move_blob2blob(gen, insn + offsetof(struct bpf_insn, imm), 4, 6757c2aad20Sopenharmony_ci kdesc->insn + offsetof(struct bpf_insn, imm)); 6767c2aad20Sopenharmony_ci move_blob2blob(gen, insn + offsetof(struct bpf_insn, off), 2, 6777c2aad20Sopenharmony_ci kdesc->insn + offsetof(struct bpf_insn, off)); 6787c2aad20Sopenharmony_ci goto log; 6797c2aad20Sopenharmony_ci } 6807c2aad20Sopenharmony_ci /* remember insn offset, so we can copy BTF ID and FD later */ 6817c2aad20Sopenharmony_ci kdesc->insn = insn; 6827c2aad20Sopenharmony_ci emit_bpf_find_by_name_kind(gen, relo); 6837c2aad20Sopenharmony_ci if (!relo->is_weak) 6847c2aad20Sopenharmony_ci emit_check_err(gen); 6857c2aad20Sopenharmony_ci /* get index in fd_array to store BTF FD at */ 6867c2aad20Sopenharmony_ci btf_fd_idx = add_kfunc_btf_fd(gen); 6877c2aad20Sopenharmony_ci if (btf_fd_idx > INT16_MAX) { 6887c2aad20Sopenharmony_ci pr_warn("BTF fd off %d for kfunc %s exceeds INT16_MAX, cannot process relocation\n", 6897c2aad20Sopenharmony_ci btf_fd_idx, relo->name); 6907c2aad20Sopenharmony_ci gen->error = -E2BIG; 6917c2aad20Sopenharmony_ci return; 6927c2aad20Sopenharmony_ci } 6937c2aad20Sopenharmony_ci kdesc->off = btf_fd_idx; 6947c2aad20Sopenharmony_ci /* jump to success case */ 6957c2aad20Sopenharmony_ci emit(gen, BPF_JMP_IMM(BPF_JSGE, BPF_REG_7, 0, 3)); 6967c2aad20Sopenharmony_ci /* set value for imm, off as 0 */ 6977c2aad20Sopenharmony_ci emit(gen, BPF_ST_MEM(BPF_W, BPF_REG_8, offsetof(struct bpf_insn, imm), 0)); 6987c2aad20Sopenharmony_ci emit(gen, BPF_ST_MEM(BPF_H, BPF_REG_8, offsetof(struct bpf_insn, off), 0)); 6997c2aad20Sopenharmony_ci /* skip success case for ret < 0 */ 7007c2aad20Sopenharmony_ci emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 10)); 7017c2aad20Sopenharmony_ci /* store btf_id into insn[insn_idx].imm */ 7027c2aad20Sopenharmony_ci emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_8, BPF_REG_7, offsetof(struct bpf_insn, imm))); 7037c2aad20Sopenharmony_ci /* obtain fd in BPF_REG_9 */ 7047c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_REG(BPF_REG_9, BPF_REG_7)); 7057c2aad20Sopenharmony_ci emit(gen, BPF_ALU64_IMM(BPF_RSH, BPF_REG_9, 32)); 7067c2aad20Sopenharmony_ci /* load fd_array slot pointer */ 7077c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_0, BPF_PSEUDO_MAP_IDX_VALUE, 7087c2aad20Sopenharmony_ci 0, 0, 0, blob_fd_array_off(gen, btf_fd_idx))); 7097c2aad20Sopenharmony_ci /* store BTF fd in slot, 0 for vmlinux */ 7107c2aad20Sopenharmony_ci emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_0, BPF_REG_9, 0)); 7117c2aad20Sopenharmony_ci /* jump to insn[insn_idx].off store if fd denotes module BTF */ 7127c2aad20Sopenharmony_ci emit(gen, BPF_JMP_IMM(BPF_JNE, BPF_REG_9, 0, 2)); 7137c2aad20Sopenharmony_ci /* set the default value for off */ 7147c2aad20Sopenharmony_ci emit(gen, BPF_ST_MEM(BPF_H, BPF_REG_8, offsetof(struct bpf_insn, off), 0)); 7157c2aad20Sopenharmony_ci /* skip BTF fd store for vmlinux BTF */ 7167c2aad20Sopenharmony_ci emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 1)); 7177c2aad20Sopenharmony_ci /* store index into insn[insn_idx].off */ 7187c2aad20Sopenharmony_ci emit(gen, BPF_ST_MEM(BPF_H, BPF_REG_8, offsetof(struct bpf_insn, off), btf_fd_idx)); 7197c2aad20Sopenharmony_cilog: 7207c2aad20Sopenharmony_ci if (!gen->log_level) 7217c2aad20Sopenharmony_ci return; 7227c2aad20Sopenharmony_ci emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_8, 7237c2aad20Sopenharmony_ci offsetof(struct bpf_insn, imm))); 7247c2aad20Sopenharmony_ci emit(gen, BPF_LDX_MEM(BPF_H, BPF_REG_9, BPF_REG_8, 7257c2aad20Sopenharmony_ci offsetof(struct bpf_insn, off))); 7267c2aad20Sopenharmony_ci debug_regs(gen, BPF_REG_7, BPF_REG_9, " func (%s:count=%d): imm: %%d, off: %%d", 7277c2aad20Sopenharmony_ci relo->name, kdesc->ref); 7287c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_0, BPF_PSEUDO_MAP_IDX_VALUE, 7297c2aad20Sopenharmony_ci 0, 0, 0, blob_fd_array_off(gen, kdesc->off))); 7307c2aad20Sopenharmony_ci emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_9, BPF_REG_0, 0)); 7317c2aad20Sopenharmony_ci debug_regs(gen, BPF_REG_9, -1, " func (%s:count=%d): btf_fd", 7327c2aad20Sopenharmony_ci relo->name, kdesc->ref); 7337c2aad20Sopenharmony_ci} 7347c2aad20Sopenharmony_ci 7357c2aad20Sopenharmony_cistatic void emit_ksym_relo_log(struct bpf_gen *gen, struct ksym_relo_desc *relo, 7367c2aad20Sopenharmony_ci int ref) 7377c2aad20Sopenharmony_ci{ 7387c2aad20Sopenharmony_ci if (!gen->log_level) 7397c2aad20Sopenharmony_ci return; 7407c2aad20Sopenharmony_ci emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_8, 7417c2aad20Sopenharmony_ci offsetof(struct bpf_insn, imm))); 7427c2aad20Sopenharmony_ci emit(gen, BPF_LDX_MEM(BPF_H, BPF_REG_9, BPF_REG_8, sizeof(struct bpf_insn) + 7437c2aad20Sopenharmony_ci offsetof(struct bpf_insn, imm))); 7447c2aad20Sopenharmony_ci debug_regs(gen, BPF_REG_7, BPF_REG_9, " var t=%d w=%d (%s:count=%d): imm[0]: %%d, imm[1]: %%d", 7457c2aad20Sopenharmony_ci relo->is_typeless, relo->is_weak, relo->name, ref); 7467c2aad20Sopenharmony_ci emit(gen, BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_8, offsetofend(struct bpf_insn, code))); 7477c2aad20Sopenharmony_ci debug_regs(gen, BPF_REG_9, -1, " var t=%d w=%d (%s:count=%d): insn.reg", 7487c2aad20Sopenharmony_ci relo->is_typeless, relo->is_weak, relo->name, ref); 7497c2aad20Sopenharmony_ci} 7507c2aad20Sopenharmony_ci 7517c2aad20Sopenharmony_ci/* Expects: 7527c2aad20Sopenharmony_ci * BPF_REG_8 - pointer to instruction 7537c2aad20Sopenharmony_ci */ 7547c2aad20Sopenharmony_cistatic void emit_relo_ksym_typeless(struct bpf_gen *gen, 7557c2aad20Sopenharmony_ci struct ksym_relo_desc *relo, int insn) 7567c2aad20Sopenharmony_ci{ 7577c2aad20Sopenharmony_ci struct ksym_desc *kdesc; 7587c2aad20Sopenharmony_ci 7597c2aad20Sopenharmony_ci kdesc = get_ksym_desc(gen, relo); 7607c2aad20Sopenharmony_ci if (!kdesc) 7617c2aad20Sopenharmony_ci return; 7627c2aad20Sopenharmony_ci /* try to copy from existing ldimm64 insn */ 7637c2aad20Sopenharmony_ci if (kdesc->ref > 1) { 7647c2aad20Sopenharmony_ci move_blob2blob(gen, insn + offsetof(struct bpf_insn, imm), 4, 7657c2aad20Sopenharmony_ci kdesc->insn + offsetof(struct bpf_insn, imm)); 7667c2aad20Sopenharmony_ci move_blob2blob(gen, insn + sizeof(struct bpf_insn) + offsetof(struct bpf_insn, imm), 4, 7677c2aad20Sopenharmony_ci kdesc->insn + sizeof(struct bpf_insn) + offsetof(struct bpf_insn, imm)); 7687c2aad20Sopenharmony_ci goto log; 7697c2aad20Sopenharmony_ci } 7707c2aad20Sopenharmony_ci /* remember insn offset, so we can copy ksym addr later */ 7717c2aad20Sopenharmony_ci kdesc->insn = insn; 7727c2aad20Sopenharmony_ci /* skip typeless ksym_desc in fd closing loop in cleanup_relos */ 7737c2aad20Sopenharmony_ci kdesc->typeless = true; 7747c2aad20Sopenharmony_ci emit_bpf_kallsyms_lookup_name(gen, relo); 7757c2aad20Sopenharmony_ci emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_7, -ENOENT, 1)); 7767c2aad20Sopenharmony_ci emit_check_err(gen); 7777c2aad20Sopenharmony_ci /* store lower half of addr into insn[insn_idx].imm */ 7787c2aad20Sopenharmony_ci emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_8, BPF_REG_9, offsetof(struct bpf_insn, imm))); 7797c2aad20Sopenharmony_ci /* store upper half of addr into insn[insn_idx + 1].imm */ 7807c2aad20Sopenharmony_ci emit(gen, BPF_ALU64_IMM(BPF_RSH, BPF_REG_9, 32)); 7817c2aad20Sopenharmony_ci emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_8, BPF_REG_9, 7827c2aad20Sopenharmony_ci sizeof(struct bpf_insn) + offsetof(struct bpf_insn, imm))); 7837c2aad20Sopenharmony_cilog: 7847c2aad20Sopenharmony_ci emit_ksym_relo_log(gen, relo, kdesc->ref); 7857c2aad20Sopenharmony_ci} 7867c2aad20Sopenharmony_ci 7877c2aad20Sopenharmony_cistatic __u32 src_reg_mask(void) 7887c2aad20Sopenharmony_ci{ 7897c2aad20Sopenharmony_ci#if defined(__LITTLE_ENDIAN_BITFIELD) 7907c2aad20Sopenharmony_ci return 0x0f; /* src_reg,dst_reg,... */ 7917c2aad20Sopenharmony_ci#elif defined(__BIG_ENDIAN_BITFIELD) 7927c2aad20Sopenharmony_ci return 0xf0; /* dst_reg,src_reg,... */ 7937c2aad20Sopenharmony_ci#else 7947c2aad20Sopenharmony_ci#error "Unsupported bit endianness, cannot proceed" 7957c2aad20Sopenharmony_ci#endif 7967c2aad20Sopenharmony_ci} 7977c2aad20Sopenharmony_ci 7987c2aad20Sopenharmony_ci/* Expects: 7997c2aad20Sopenharmony_ci * BPF_REG_8 - pointer to instruction 8007c2aad20Sopenharmony_ci */ 8017c2aad20Sopenharmony_cistatic void emit_relo_ksym_btf(struct bpf_gen *gen, struct ksym_relo_desc *relo, int insn) 8027c2aad20Sopenharmony_ci{ 8037c2aad20Sopenharmony_ci struct ksym_desc *kdesc; 8047c2aad20Sopenharmony_ci __u32 reg_mask; 8057c2aad20Sopenharmony_ci 8067c2aad20Sopenharmony_ci kdesc = get_ksym_desc(gen, relo); 8077c2aad20Sopenharmony_ci if (!kdesc) 8087c2aad20Sopenharmony_ci return; 8097c2aad20Sopenharmony_ci /* try to copy from existing ldimm64 insn */ 8107c2aad20Sopenharmony_ci if (kdesc->ref > 1) { 8117c2aad20Sopenharmony_ci move_blob2blob(gen, insn + sizeof(struct bpf_insn) + offsetof(struct bpf_insn, imm), 4, 8127c2aad20Sopenharmony_ci kdesc->insn + sizeof(struct bpf_insn) + offsetof(struct bpf_insn, imm)); 8137c2aad20Sopenharmony_ci move_blob2blob(gen, insn + offsetof(struct bpf_insn, imm), 4, 8147c2aad20Sopenharmony_ci kdesc->insn + offsetof(struct bpf_insn, imm)); 8157c2aad20Sopenharmony_ci /* jump over src_reg adjustment if imm (btf_id) is not 0, reuse BPF_REG_0 from move_blob2blob 8167c2aad20Sopenharmony_ci * If btf_id is zero, clear BPF_PSEUDO_BTF_ID flag in src_reg of ld_imm64 insn 8177c2aad20Sopenharmony_ci */ 8187c2aad20Sopenharmony_ci emit(gen, BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 3)); 8197c2aad20Sopenharmony_ci goto clear_src_reg; 8207c2aad20Sopenharmony_ci } 8217c2aad20Sopenharmony_ci /* remember insn offset, so we can copy BTF ID and FD later */ 8227c2aad20Sopenharmony_ci kdesc->insn = insn; 8237c2aad20Sopenharmony_ci emit_bpf_find_by_name_kind(gen, relo); 8247c2aad20Sopenharmony_ci if (!relo->is_weak) 8257c2aad20Sopenharmony_ci emit_check_err(gen); 8267c2aad20Sopenharmony_ci /* jump to success case */ 8277c2aad20Sopenharmony_ci emit(gen, BPF_JMP_IMM(BPF_JSGE, BPF_REG_7, 0, 3)); 8287c2aad20Sopenharmony_ci /* set values for insn[insn_idx].imm, insn[insn_idx + 1].imm as 0 */ 8297c2aad20Sopenharmony_ci emit(gen, BPF_ST_MEM(BPF_W, BPF_REG_8, offsetof(struct bpf_insn, imm), 0)); 8307c2aad20Sopenharmony_ci emit(gen, BPF_ST_MEM(BPF_W, BPF_REG_8, sizeof(struct bpf_insn) + offsetof(struct bpf_insn, imm), 0)); 8317c2aad20Sopenharmony_ci /* skip success case for ret < 0 */ 8327c2aad20Sopenharmony_ci emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 4)); 8337c2aad20Sopenharmony_ci /* store btf_id into insn[insn_idx].imm */ 8347c2aad20Sopenharmony_ci emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_8, BPF_REG_7, offsetof(struct bpf_insn, imm))); 8357c2aad20Sopenharmony_ci /* store btf_obj_fd into insn[insn_idx + 1].imm */ 8367c2aad20Sopenharmony_ci emit(gen, BPF_ALU64_IMM(BPF_RSH, BPF_REG_7, 32)); 8377c2aad20Sopenharmony_ci emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_8, BPF_REG_7, 8387c2aad20Sopenharmony_ci sizeof(struct bpf_insn) + offsetof(struct bpf_insn, imm))); 8397c2aad20Sopenharmony_ci /* skip src_reg adjustment */ 8407c2aad20Sopenharmony_ci emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 3)); 8417c2aad20Sopenharmony_ciclear_src_reg: 8427c2aad20Sopenharmony_ci /* clear bpf_object__relocate_data's src_reg assignment, otherwise we get a verifier failure */ 8437c2aad20Sopenharmony_ci reg_mask = src_reg_mask(); 8447c2aad20Sopenharmony_ci emit(gen, BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_8, offsetofend(struct bpf_insn, code))); 8457c2aad20Sopenharmony_ci emit(gen, BPF_ALU32_IMM(BPF_AND, BPF_REG_9, reg_mask)); 8467c2aad20Sopenharmony_ci emit(gen, BPF_STX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, offsetofend(struct bpf_insn, code))); 8477c2aad20Sopenharmony_ci 8487c2aad20Sopenharmony_ci emit_ksym_relo_log(gen, relo, kdesc->ref); 8497c2aad20Sopenharmony_ci} 8507c2aad20Sopenharmony_ci 8517c2aad20Sopenharmony_civoid bpf_gen__record_relo_core(struct bpf_gen *gen, 8527c2aad20Sopenharmony_ci const struct bpf_core_relo *core_relo) 8537c2aad20Sopenharmony_ci{ 8547c2aad20Sopenharmony_ci struct bpf_core_relo *relos; 8557c2aad20Sopenharmony_ci 8567c2aad20Sopenharmony_ci relos = libbpf_reallocarray(gen->core_relos, gen->core_relo_cnt + 1, sizeof(*relos)); 8577c2aad20Sopenharmony_ci if (!relos) { 8587c2aad20Sopenharmony_ci gen->error = -ENOMEM; 8597c2aad20Sopenharmony_ci return; 8607c2aad20Sopenharmony_ci } 8617c2aad20Sopenharmony_ci gen->core_relos = relos; 8627c2aad20Sopenharmony_ci relos += gen->core_relo_cnt; 8637c2aad20Sopenharmony_ci memcpy(relos, core_relo, sizeof(*relos)); 8647c2aad20Sopenharmony_ci gen->core_relo_cnt++; 8657c2aad20Sopenharmony_ci} 8667c2aad20Sopenharmony_ci 8677c2aad20Sopenharmony_cistatic void emit_relo(struct bpf_gen *gen, struct ksym_relo_desc *relo, int insns) 8687c2aad20Sopenharmony_ci{ 8697c2aad20Sopenharmony_ci int insn; 8707c2aad20Sopenharmony_ci 8717c2aad20Sopenharmony_ci pr_debug("gen: emit_relo (%d): %s at %d %s\n", 8727c2aad20Sopenharmony_ci relo->kind, relo->name, relo->insn_idx, relo->is_ld64 ? "ld64" : "call"); 8737c2aad20Sopenharmony_ci insn = insns + sizeof(struct bpf_insn) * relo->insn_idx; 8747c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_8, BPF_PSEUDO_MAP_IDX_VALUE, 0, 0, 0, insn)); 8757c2aad20Sopenharmony_ci if (relo->is_ld64) { 8767c2aad20Sopenharmony_ci if (relo->is_typeless) 8777c2aad20Sopenharmony_ci emit_relo_ksym_typeless(gen, relo, insn); 8787c2aad20Sopenharmony_ci else 8797c2aad20Sopenharmony_ci emit_relo_ksym_btf(gen, relo, insn); 8807c2aad20Sopenharmony_ci } else { 8817c2aad20Sopenharmony_ci emit_relo_kfunc_btf(gen, relo, insn); 8827c2aad20Sopenharmony_ci } 8837c2aad20Sopenharmony_ci} 8847c2aad20Sopenharmony_ci 8857c2aad20Sopenharmony_cistatic void emit_relos(struct bpf_gen *gen, int insns) 8867c2aad20Sopenharmony_ci{ 8877c2aad20Sopenharmony_ci int i; 8887c2aad20Sopenharmony_ci 8897c2aad20Sopenharmony_ci for (i = 0; i < gen->relo_cnt; i++) 8907c2aad20Sopenharmony_ci emit_relo(gen, gen->relos + i, insns); 8917c2aad20Sopenharmony_ci} 8927c2aad20Sopenharmony_ci 8937c2aad20Sopenharmony_cistatic void cleanup_core_relo(struct bpf_gen *gen) 8947c2aad20Sopenharmony_ci{ 8957c2aad20Sopenharmony_ci if (!gen->core_relo_cnt) 8967c2aad20Sopenharmony_ci return; 8977c2aad20Sopenharmony_ci free(gen->core_relos); 8987c2aad20Sopenharmony_ci gen->core_relo_cnt = 0; 8997c2aad20Sopenharmony_ci gen->core_relos = NULL; 9007c2aad20Sopenharmony_ci} 9017c2aad20Sopenharmony_ci 9027c2aad20Sopenharmony_cistatic void cleanup_relos(struct bpf_gen *gen, int insns) 9037c2aad20Sopenharmony_ci{ 9047c2aad20Sopenharmony_ci struct ksym_desc *kdesc; 9057c2aad20Sopenharmony_ci int i, insn; 9067c2aad20Sopenharmony_ci 9077c2aad20Sopenharmony_ci for (i = 0; i < gen->nr_ksyms; i++) { 9087c2aad20Sopenharmony_ci kdesc = &gen->ksyms[i]; 9097c2aad20Sopenharmony_ci /* only close fds for typed ksyms and kfuncs */ 9107c2aad20Sopenharmony_ci if (kdesc->is_ld64 && !kdesc->typeless) { 9117c2aad20Sopenharmony_ci /* close fd recorded in insn[insn_idx + 1].imm */ 9127c2aad20Sopenharmony_ci insn = kdesc->insn; 9137c2aad20Sopenharmony_ci insn += sizeof(struct bpf_insn) + offsetof(struct bpf_insn, imm); 9147c2aad20Sopenharmony_ci emit_sys_close_blob(gen, insn); 9157c2aad20Sopenharmony_ci } else if (!kdesc->is_ld64) { 9167c2aad20Sopenharmony_ci emit_sys_close_blob(gen, blob_fd_array_off(gen, kdesc->off)); 9177c2aad20Sopenharmony_ci if (kdesc->off < MAX_FD_ARRAY_SZ) 9187c2aad20Sopenharmony_ci gen->nr_fd_array--; 9197c2aad20Sopenharmony_ci } 9207c2aad20Sopenharmony_ci } 9217c2aad20Sopenharmony_ci if (gen->nr_ksyms) { 9227c2aad20Sopenharmony_ci free(gen->ksyms); 9237c2aad20Sopenharmony_ci gen->nr_ksyms = 0; 9247c2aad20Sopenharmony_ci gen->ksyms = NULL; 9257c2aad20Sopenharmony_ci } 9267c2aad20Sopenharmony_ci if (gen->relo_cnt) { 9277c2aad20Sopenharmony_ci free(gen->relos); 9287c2aad20Sopenharmony_ci gen->relo_cnt = 0; 9297c2aad20Sopenharmony_ci gen->relos = NULL; 9307c2aad20Sopenharmony_ci } 9317c2aad20Sopenharmony_ci cleanup_core_relo(gen); 9327c2aad20Sopenharmony_ci} 9337c2aad20Sopenharmony_ci 9347c2aad20Sopenharmony_civoid bpf_gen__prog_load(struct bpf_gen *gen, 9357c2aad20Sopenharmony_ci enum bpf_prog_type prog_type, const char *prog_name, 9367c2aad20Sopenharmony_ci const char *license, struct bpf_insn *insns, size_t insn_cnt, 9377c2aad20Sopenharmony_ci struct bpf_prog_load_opts *load_attr, int prog_idx) 9387c2aad20Sopenharmony_ci{ 9397c2aad20Sopenharmony_ci int prog_load_attr, license_off, insns_off, func_info, line_info, core_relos; 9407c2aad20Sopenharmony_ci int attr_size = offsetofend(union bpf_attr, core_relo_rec_size); 9417c2aad20Sopenharmony_ci union bpf_attr attr; 9427c2aad20Sopenharmony_ci 9437c2aad20Sopenharmony_ci memset(&attr, 0, attr_size); 9447c2aad20Sopenharmony_ci pr_debug("gen: prog_load: type %d insns_cnt %zd progi_idx %d\n", 9457c2aad20Sopenharmony_ci prog_type, insn_cnt, prog_idx); 9467c2aad20Sopenharmony_ci /* add license string to blob of bytes */ 9477c2aad20Sopenharmony_ci license_off = add_data(gen, license, strlen(license) + 1); 9487c2aad20Sopenharmony_ci /* add insns to blob of bytes */ 9497c2aad20Sopenharmony_ci insns_off = add_data(gen, insns, insn_cnt * sizeof(struct bpf_insn)); 9507c2aad20Sopenharmony_ci 9517c2aad20Sopenharmony_ci attr.prog_type = prog_type; 9527c2aad20Sopenharmony_ci attr.expected_attach_type = load_attr->expected_attach_type; 9537c2aad20Sopenharmony_ci attr.attach_btf_id = load_attr->attach_btf_id; 9547c2aad20Sopenharmony_ci attr.prog_ifindex = load_attr->prog_ifindex; 9557c2aad20Sopenharmony_ci attr.kern_version = 0; 9567c2aad20Sopenharmony_ci attr.insn_cnt = (__u32)insn_cnt; 9577c2aad20Sopenharmony_ci attr.prog_flags = load_attr->prog_flags; 9587c2aad20Sopenharmony_ci 9597c2aad20Sopenharmony_ci attr.func_info_rec_size = load_attr->func_info_rec_size; 9607c2aad20Sopenharmony_ci attr.func_info_cnt = load_attr->func_info_cnt; 9617c2aad20Sopenharmony_ci func_info = add_data(gen, load_attr->func_info, 9627c2aad20Sopenharmony_ci attr.func_info_cnt * attr.func_info_rec_size); 9637c2aad20Sopenharmony_ci 9647c2aad20Sopenharmony_ci attr.line_info_rec_size = load_attr->line_info_rec_size; 9657c2aad20Sopenharmony_ci attr.line_info_cnt = load_attr->line_info_cnt; 9667c2aad20Sopenharmony_ci line_info = add_data(gen, load_attr->line_info, 9677c2aad20Sopenharmony_ci attr.line_info_cnt * attr.line_info_rec_size); 9687c2aad20Sopenharmony_ci 9697c2aad20Sopenharmony_ci attr.core_relo_rec_size = sizeof(struct bpf_core_relo); 9707c2aad20Sopenharmony_ci attr.core_relo_cnt = gen->core_relo_cnt; 9717c2aad20Sopenharmony_ci core_relos = add_data(gen, gen->core_relos, 9727c2aad20Sopenharmony_ci attr.core_relo_cnt * attr.core_relo_rec_size); 9737c2aad20Sopenharmony_ci 9747c2aad20Sopenharmony_ci libbpf_strlcpy(attr.prog_name, prog_name, sizeof(attr.prog_name)); 9757c2aad20Sopenharmony_ci prog_load_attr = add_data(gen, &attr, attr_size); 9767c2aad20Sopenharmony_ci 9777c2aad20Sopenharmony_ci /* populate union bpf_attr with a pointer to license */ 9787c2aad20Sopenharmony_ci emit_rel_store(gen, attr_field(prog_load_attr, license), license_off); 9797c2aad20Sopenharmony_ci 9807c2aad20Sopenharmony_ci /* populate union bpf_attr with a pointer to instructions */ 9817c2aad20Sopenharmony_ci emit_rel_store(gen, attr_field(prog_load_attr, insns), insns_off); 9827c2aad20Sopenharmony_ci 9837c2aad20Sopenharmony_ci /* populate union bpf_attr with a pointer to func_info */ 9847c2aad20Sopenharmony_ci emit_rel_store(gen, attr_field(prog_load_attr, func_info), func_info); 9857c2aad20Sopenharmony_ci 9867c2aad20Sopenharmony_ci /* populate union bpf_attr with a pointer to line_info */ 9877c2aad20Sopenharmony_ci emit_rel_store(gen, attr_field(prog_load_attr, line_info), line_info); 9887c2aad20Sopenharmony_ci 9897c2aad20Sopenharmony_ci /* populate union bpf_attr with a pointer to core_relos */ 9907c2aad20Sopenharmony_ci emit_rel_store(gen, attr_field(prog_load_attr, core_relos), core_relos); 9917c2aad20Sopenharmony_ci 9927c2aad20Sopenharmony_ci /* populate union bpf_attr fd_array with a pointer to data where map_fds are saved */ 9937c2aad20Sopenharmony_ci emit_rel_store(gen, attr_field(prog_load_attr, fd_array), gen->fd_array); 9947c2aad20Sopenharmony_ci 9957c2aad20Sopenharmony_ci /* populate union bpf_attr with user provided log details */ 9967c2aad20Sopenharmony_ci move_ctx2blob(gen, attr_field(prog_load_attr, log_level), 4, 9977c2aad20Sopenharmony_ci offsetof(struct bpf_loader_ctx, log_level), false); 9987c2aad20Sopenharmony_ci move_ctx2blob(gen, attr_field(prog_load_attr, log_size), 4, 9997c2aad20Sopenharmony_ci offsetof(struct bpf_loader_ctx, log_size), false); 10007c2aad20Sopenharmony_ci move_ctx2blob(gen, attr_field(prog_load_attr, log_buf), 8, 10017c2aad20Sopenharmony_ci offsetof(struct bpf_loader_ctx, log_buf), false); 10027c2aad20Sopenharmony_ci /* populate union bpf_attr with btf_fd saved in the stack earlier */ 10037c2aad20Sopenharmony_ci move_stack2blob(gen, attr_field(prog_load_attr, prog_btf_fd), 4, 10047c2aad20Sopenharmony_ci stack_off(btf_fd)); 10057c2aad20Sopenharmony_ci if (gen->attach_kind) { 10067c2aad20Sopenharmony_ci emit_find_attach_target(gen); 10077c2aad20Sopenharmony_ci /* populate union bpf_attr with btf_id and btf_obj_fd found by helper */ 10087c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_0, BPF_PSEUDO_MAP_IDX_VALUE, 10097c2aad20Sopenharmony_ci 0, 0, 0, prog_load_attr)); 10107c2aad20Sopenharmony_ci emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_0, BPF_REG_7, 10117c2aad20Sopenharmony_ci offsetof(union bpf_attr, attach_btf_id))); 10127c2aad20Sopenharmony_ci emit(gen, BPF_ALU64_IMM(BPF_RSH, BPF_REG_7, 32)); 10137c2aad20Sopenharmony_ci emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_0, BPF_REG_7, 10147c2aad20Sopenharmony_ci offsetof(union bpf_attr, attach_btf_obj_fd))); 10157c2aad20Sopenharmony_ci } 10167c2aad20Sopenharmony_ci emit_relos(gen, insns_off); 10177c2aad20Sopenharmony_ci /* emit PROG_LOAD command */ 10187c2aad20Sopenharmony_ci emit_sys_bpf(gen, BPF_PROG_LOAD, prog_load_attr, attr_size); 10197c2aad20Sopenharmony_ci debug_ret(gen, "prog_load %s insn_cnt %d", attr.prog_name, attr.insn_cnt); 10207c2aad20Sopenharmony_ci /* successful or not, close btf module FDs used in extern ksyms and attach_btf_obj_fd */ 10217c2aad20Sopenharmony_ci cleanup_relos(gen, insns_off); 10227c2aad20Sopenharmony_ci if (gen->attach_kind) { 10237c2aad20Sopenharmony_ci emit_sys_close_blob(gen, 10247c2aad20Sopenharmony_ci attr_field(prog_load_attr, attach_btf_obj_fd)); 10257c2aad20Sopenharmony_ci gen->attach_kind = 0; 10267c2aad20Sopenharmony_ci } 10277c2aad20Sopenharmony_ci emit_check_err(gen); 10287c2aad20Sopenharmony_ci /* remember prog_fd in the stack, if successful */ 10297c2aad20Sopenharmony_ci emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_7, 10307c2aad20Sopenharmony_ci stack_off(prog_fd[gen->nr_progs]))); 10317c2aad20Sopenharmony_ci gen->nr_progs++; 10327c2aad20Sopenharmony_ci} 10337c2aad20Sopenharmony_ci 10347c2aad20Sopenharmony_civoid bpf_gen__map_update_elem(struct bpf_gen *gen, int map_idx, void *pvalue, 10357c2aad20Sopenharmony_ci __u32 value_size) 10367c2aad20Sopenharmony_ci{ 10377c2aad20Sopenharmony_ci int attr_size = offsetofend(union bpf_attr, flags); 10387c2aad20Sopenharmony_ci int map_update_attr, value, key; 10397c2aad20Sopenharmony_ci union bpf_attr attr; 10407c2aad20Sopenharmony_ci int zero = 0; 10417c2aad20Sopenharmony_ci 10427c2aad20Sopenharmony_ci memset(&attr, 0, attr_size); 10437c2aad20Sopenharmony_ci pr_debug("gen: map_update_elem: idx %d\n", map_idx); 10447c2aad20Sopenharmony_ci 10457c2aad20Sopenharmony_ci value = add_data(gen, pvalue, value_size); 10467c2aad20Sopenharmony_ci key = add_data(gen, &zero, sizeof(zero)); 10477c2aad20Sopenharmony_ci 10487c2aad20Sopenharmony_ci /* if (map_desc[map_idx].initial_value) { 10497c2aad20Sopenharmony_ci * if (ctx->flags & BPF_SKEL_KERNEL) 10507c2aad20Sopenharmony_ci * bpf_probe_read_kernel(value, value_size, initial_value); 10517c2aad20Sopenharmony_ci * else 10527c2aad20Sopenharmony_ci * bpf_copy_from_user(value, value_size, initial_value); 10537c2aad20Sopenharmony_ci * } 10547c2aad20Sopenharmony_ci */ 10557c2aad20Sopenharmony_ci emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6, 10567c2aad20Sopenharmony_ci sizeof(struct bpf_loader_ctx) + 10577c2aad20Sopenharmony_ci sizeof(struct bpf_map_desc) * map_idx + 10587c2aad20Sopenharmony_ci offsetof(struct bpf_map_desc, initial_value))); 10597c2aad20Sopenharmony_ci emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0, 8)); 10607c2aad20Sopenharmony_ci emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE, 10617c2aad20Sopenharmony_ci 0, 0, 0, value)); 10627c2aad20Sopenharmony_ci emit(gen, BPF_MOV64_IMM(BPF_REG_2, value_size)); 10637c2aad20Sopenharmony_ci emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 10647c2aad20Sopenharmony_ci offsetof(struct bpf_loader_ctx, flags))); 10657c2aad20Sopenharmony_ci emit(gen, BPF_JMP_IMM(BPF_JSET, BPF_REG_0, BPF_SKEL_KERNEL, 2)); 10667c2aad20Sopenharmony_ci emit(gen, BPF_EMIT_CALL(BPF_FUNC_copy_from_user)); 10677c2aad20Sopenharmony_ci emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 1)); 10687c2aad20Sopenharmony_ci emit(gen, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel)); 10697c2aad20Sopenharmony_ci 10707c2aad20Sopenharmony_ci map_update_attr = add_data(gen, &attr, attr_size); 10717c2aad20Sopenharmony_ci move_blob2blob(gen, attr_field(map_update_attr, map_fd), 4, 10727c2aad20Sopenharmony_ci blob_fd_array_off(gen, map_idx)); 10737c2aad20Sopenharmony_ci emit_rel_store(gen, attr_field(map_update_attr, key), key); 10747c2aad20Sopenharmony_ci emit_rel_store(gen, attr_field(map_update_attr, value), value); 10757c2aad20Sopenharmony_ci /* emit MAP_UPDATE_ELEM command */ 10767c2aad20Sopenharmony_ci emit_sys_bpf(gen, BPF_MAP_UPDATE_ELEM, map_update_attr, attr_size); 10777c2aad20Sopenharmony_ci debug_ret(gen, "update_elem idx %d value_size %d", map_idx, value_size); 10787c2aad20Sopenharmony_ci emit_check_err(gen); 10797c2aad20Sopenharmony_ci} 10807c2aad20Sopenharmony_ci 10817c2aad20Sopenharmony_civoid bpf_gen__populate_outer_map(struct bpf_gen *gen, int outer_map_idx, int slot, 10827c2aad20Sopenharmony_ci int inner_map_idx) 10837c2aad20Sopenharmony_ci{ 10847c2aad20Sopenharmony_ci int attr_size = offsetofend(union bpf_attr, flags); 10857c2aad20Sopenharmony_ci int map_update_attr, key; 10867c2aad20Sopenharmony_ci union bpf_attr attr; 10877c2aad20Sopenharmony_ci 10887c2aad20Sopenharmony_ci memset(&attr, 0, attr_size); 10897c2aad20Sopenharmony_ci pr_debug("gen: populate_outer_map: outer %d key %d inner %d\n", 10907c2aad20Sopenharmony_ci outer_map_idx, slot, inner_map_idx); 10917c2aad20Sopenharmony_ci 10927c2aad20Sopenharmony_ci key = add_data(gen, &slot, sizeof(slot)); 10937c2aad20Sopenharmony_ci 10947c2aad20Sopenharmony_ci map_update_attr = add_data(gen, &attr, attr_size); 10957c2aad20Sopenharmony_ci move_blob2blob(gen, attr_field(map_update_attr, map_fd), 4, 10967c2aad20Sopenharmony_ci blob_fd_array_off(gen, outer_map_idx)); 10977c2aad20Sopenharmony_ci emit_rel_store(gen, attr_field(map_update_attr, key), key); 10987c2aad20Sopenharmony_ci emit_rel_store(gen, attr_field(map_update_attr, value), 10997c2aad20Sopenharmony_ci blob_fd_array_off(gen, inner_map_idx)); 11007c2aad20Sopenharmony_ci 11017c2aad20Sopenharmony_ci /* emit MAP_UPDATE_ELEM command */ 11027c2aad20Sopenharmony_ci emit_sys_bpf(gen, BPF_MAP_UPDATE_ELEM, map_update_attr, attr_size); 11037c2aad20Sopenharmony_ci debug_ret(gen, "populate_outer_map outer %d key %d inner %d", 11047c2aad20Sopenharmony_ci outer_map_idx, slot, inner_map_idx); 11057c2aad20Sopenharmony_ci emit_check_err(gen); 11067c2aad20Sopenharmony_ci} 11077c2aad20Sopenharmony_ci 11087c2aad20Sopenharmony_civoid bpf_gen__map_freeze(struct bpf_gen *gen, int map_idx) 11097c2aad20Sopenharmony_ci{ 11107c2aad20Sopenharmony_ci int attr_size = offsetofend(union bpf_attr, map_fd); 11117c2aad20Sopenharmony_ci int map_freeze_attr; 11127c2aad20Sopenharmony_ci union bpf_attr attr; 11137c2aad20Sopenharmony_ci 11147c2aad20Sopenharmony_ci memset(&attr, 0, attr_size); 11157c2aad20Sopenharmony_ci pr_debug("gen: map_freeze: idx %d\n", map_idx); 11167c2aad20Sopenharmony_ci map_freeze_attr = add_data(gen, &attr, attr_size); 11177c2aad20Sopenharmony_ci move_blob2blob(gen, attr_field(map_freeze_attr, map_fd), 4, 11187c2aad20Sopenharmony_ci blob_fd_array_off(gen, map_idx)); 11197c2aad20Sopenharmony_ci /* emit MAP_FREEZE command */ 11207c2aad20Sopenharmony_ci emit_sys_bpf(gen, BPF_MAP_FREEZE, map_freeze_attr, attr_size); 11217c2aad20Sopenharmony_ci debug_ret(gen, "map_freeze"); 11227c2aad20Sopenharmony_ci emit_check_err(gen); 11237c2aad20Sopenharmony_ci} 1124