1b4ce5923SAnton Protopopov // SPDX-License-Identifier: GPL-2.0-only 2b4ce5923SAnton Protopopov /* Copyright (c) 2025 Isovalent */ 3b4ce5923SAnton Protopopov 4b4ce5923SAnton Protopopov #include <linux/bpf.h> 5b4ce5923SAnton Protopopov 6b4ce5923SAnton Protopopov struct bpf_insn_array { 7b4ce5923SAnton Protopopov struct bpf_map map; 8b4ce5923SAnton Protopopov atomic_t used; 9b4ce5923SAnton Protopopov long *ips; 10b4ce5923SAnton Protopopov DECLARE_FLEX_ARRAY(struct bpf_insn_array_value, values); 11b4ce5923SAnton Protopopov }; 12b4ce5923SAnton Protopopov 13b4ce5923SAnton Protopopov #define cast_insn_array(MAP_PTR) \ 14b4ce5923SAnton Protopopov container_of((MAP_PTR), struct bpf_insn_array, map) 15b4ce5923SAnton Protopopov 16b4ce5923SAnton Protopopov #define INSN_DELETED ((u32)-1) 17b4ce5923SAnton Protopopov 18b4ce5923SAnton Protopopov static inline u64 insn_array_alloc_size(u32 max_entries) 19b4ce5923SAnton Protopopov { 20b4ce5923SAnton Protopopov const u64 base_size = sizeof(struct bpf_insn_array); 21b4ce5923SAnton Protopopov const u64 entry_size = sizeof(struct bpf_insn_array_value); 22b4ce5923SAnton Protopopov 23b4ce5923SAnton Protopopov return base_size + max_entries * (entry_size + sizeof(long)); 24b4ce5923SAnton Protopopov } 25b4ce5923SAnton Protopopov 26b4ce5923SAnton Protopopov static int insn_array_alloc_check(union bpf_attr *attr) 27b4ce5923SAnton Protopopov { 28b4ce5923SAnton Protopopov u32 value_size = sizeof(struct bpf_insn_array_value); 29b4ce5923SAnton Protopopov 30b4ce5923SAnton Protopopov if (attr->max_entries == 0 || attr->key_size != 4 || 31b4ce5923SAnton Protopopov attr->value_size != value_size || attr->map_flags != 0) 32b4ce5923SAnton Protopopov return -EINVAL; 33b4ce5923SAnton Protopopov 34b4ce5923SAnton Protopopov return 0; 35b4ce5923SAnton Protopopov } 36b4ce5923SAnton Protopopov 37b4ce5923SAnton Protopopov static void insn_array_free(struct bpf_map *map) 38b4ce5923SAnton Protopopov { 39b4ce5923SAnton Protopopov struct bpf_insn_array *insn_array = cast_insn_array(map); 40b4ce5923SAnton Protopopov 41b4ce5923SAnton Protopopov bpf_map_area_free(insn_array); 42b4ce5923SAnton Protopopov } 43b4ce5923SAnton Protopopov 44b4ce5923SAnton Protopopov static struct bpf_map *insn_array_alloc(union bpf_attr *attr) 45b4ce5923SAnton Protopopov { 46b4ce5923SAnton Protopopov u64 size = insn_array_alloc_size(attr->max_entries); 47b4ce5923SAnton Protopopov struct bpf_insn_array *insn_array; 48b4ce5923SAnton Protopopov 49b4ce5923SAnton Protopopov insn_array = bpf_map_area_alloc(size, NUMA_NO_NODE); 50b4ce5923SAnton Protopopov if (!insn_array) 51b4ce5923SAnton Protopopov return ERR_PTR(-ENOMEM); 52b4ce5923SAnton Protopopov 53b4ce5923SAnton Protopopov /* ips are allocated right after the insn_array->values[] array */ 54b4ce5923SAnton Protopopov insn_array->ips = (void *)&insn_array->values[attr->max_entries]; 55b4ce5923SAnton Protopopov 56b4ce5923SAnton Protopopov bpf_map_init_from_attr(&insn_array->map, attr); 57b4ce5923SAnton Protopopov 58*7feff23cSAnton Protopopov /* BPF programs aren't allowed to write to the map */ 59*7feff23cSAnton Protopopov insn_array->map.map_flags |= BPF_F_RDONLY_PROG; 60*7feff23cSAnton Protopopov 61b4ce5923SAnton Protopopov return &insn_array->map; 62b4ce5923SAnton Protopopov } 63b4ce5923SAnton Protopopov 64b4ce5923SAnton Protopopov static void *insn_array_lookup_elem(struct bpf_map *map, void *key) 65b4ce5923SAnton Protopopov { 66b4ce5923SAnton Protopopov struct bpf_insn_array *insn_array = cast_insn_array(map); 67b4ce5923SAnton Protopopov u32 index = *(u32 *)key; 68b4ce5923SAnton Protopopov 69b4ce5923SAnton Protopopov if (unlikely(index >= insn_array->map.max_entries)) 70b4ce5923SAnton Protopopov return NULL; 71b4ce5923SAnton Protopopov 72b4ce5923SAnton Protopopov return &insn_array->values[index]; 73b4ce5923SAnton Protopopov } 74b4ce5923SAnton Protopopov 75b4ce5923SAnton Protopopov static long insn_array_update_elem(struct bpf_map *map, void *key, void *value, u64 map_flags) 76b4ce5923SAnton Protopopov { 77b4ce5923SAnton Protopopov struct bpf_insn_array *insn_array = cast_insn_array(map); 78b4ce5923SAnton Protopopov u32 index = *(u32 *)key; 79b4ce5923SAnton Protopopov struct bpf_insn_array_value val = {}; 80b4ce5923SAnton Protopopov 81b4ce5923SAnton Protopopov if (unlikely(index >= insn_array->map.max_entries)) 82b4ce5923SAnton Protopopov return -E2BIG; 83b4ce5923SAnton Protopopov 84b4ce5923SAnton Protopopov if (unlikely(map_flags & BPF_NOEXIST)) 85b4ce5923SAnton Protopopov return -EEXIST; 86b4ce5923SAnton Protopopov 87b4ce5923SAnton Protopopov copy_map_value(map, &val, value); 88b4ce5923SAnton Protopopov if (val.jitted_off || val.xlated_off) 89b4ce5923SAnton Protopopov return -EINVAL; 90b4ce5923SAnton Protopopov 91b4ce5923SAnton Protopopov insn_array->values[index].orig_off = val.orig_off; 92b4ce5923SAnton Protopopov 93b4ce5923SAnton Protopopov return 0; 94b4ce5923SAnton Protopopov } 95b4ce5923SAnton Protopopov 96b4ce5923SAnton Protopopov static long insn_array_delete_elem(struct bpf_map *map, void *key) 97b4ce5923SAnton Protopopov { 98b4ce5923SAnton Protopopov return -EINVAL; 99b4ce5923SAnton Protopopov } 100b4ce5923SAnton Protopopov 101b4ce5923SAnton Protopopov static int insn_array_check_btf(const struct bpf_map *map, 102b4ce5923SAnton Protopopov const struct btf *btf, 103b4ce5923SAnton Protopopov const struct btf_type *key_type, 104b4ce5923SAnton Protopopov const struct btf_type *value_type) 105b4ce5923SAnton Protopopov { 106b4ce5923SAnton Protopopov if (!btf_type_is_i32(key_type)) 107b4ce5923SAnton Protopopov return -EINVAL; 108b4ce5923SAnton Protopopov 109b4ce5923SAnton Protopopov if (!btf_type_is_i64(value_type)) 110b4ce5923SAnton Protopopov return -EINVAL; 111b4ce5923SAnton Protopopov 112b4ce5923SAnton Protopopov return 0; 113b4ce5923SAnton Protopopov } 114b4ce5923SAnton Protopopov 115b4ce5923SAnton Protopopov static u64 insn_array_mem_usage(const struct bpf_map *map) 116b4ce5923SAnton Protopopov { 117b4ce5923SAnton Protopopov return insn_array_alloc_size(map->max_entries); 118b4ce5923SAnton Protopopov } 119b4ce5923SAnton Protopopov 120493d9e0dSAnton Protopopov static int insn_array_map_direct_value_addr(const struct bpf_map *map, u64 *imm, u32 off) 121493d9e0dSAnton Protopopov { 122493d9e0dSAnton Protopopov struct bpf_insn_array *insn_array = cast_insn_array(map); 123493d9e0dSAnton Protopopov 124493d9e0dSAnton Protopopov if ((off % sizeof(long)) != 0 || 125493d9e0dSAnton Protopopov (off / sizeof(long)) >= map->max_entries) 126493d9e0dSAnton Protopopov return -EINVAL; 127493d9e0dSAnton Protopopov 128493d9e0dSAnton Protopopov /* from BPF's point of view, this map is a jump table */ 129493d9e0dSAnton Protopopov *imm = (unsigned long)insn_array->ips + off; 130493d9e0dSAnton Protopopov 131493d9e0dSAnton Protopopov return 0; 132493d9e0dSAnton Protopopov } 133493d9e0dSAnton Protopopov 134b4ce5923SAnton Protopopov BTF_ID_LIST_SINGLE(insn_array_btf_ids, struct, bpf_insn_array) 135b4ce5923SAnton Protopopov 136b4ce5923SAnton Protopopov const struct bpf_map_ops insn_array_map_ops = { 137b4ce5923SAnton Protopopov .map_alloc_check = insn_array_alloc_check, 138b4ce5923SAnton Protopopov .map_alloc = insn_array_alloc, 139b4ce5923SAnton Protopopov .map_free = insn_array_free, 140b4ce5923SAnton Protopopov .map_get_next_key = bpf_array_get_next_key, 141b4ce5923SAnton Protopopov .map_lookup_elem = insn_array_lookup_elem, 142b4ce5923SAnton Protopopov .map_update_elem = insn_array_update_elem, 143b4ce5923SAnton Protopopov .map_delete_elem = insn_array_delete_elem, 144b4ce5923SAnton Protopopov .map_check_btf = insn_array_check_btf, 145b4ce5923SAnton Protopopov .map_mem_usage = insn_array_mem_usage, 146493d9e0dSAnton Protopopov .map_direct_value_addr = insn_array_map_direct_value_addr, 147b4ce5923SAnton Protopopov .map_btf_id = &insn_array_btf_ids[0], 148b4ce5923SAnton Protopopov }; 149b4ce5923SAnton Protopopov 150b4ce5923SAnton Protopopov static inline bool is_frozen(struct bpf_map *map) 151b4ce5923SAnton Protopopov { 152b4ce5923SAnton Protopopov guard(mutex)(&map->freeze_mutex); 153b4ce5923SAnton Protopopov 154b4ce5923SAnton Protopopov return map->frozen; 155b4ce5923SAnton Protopopov } 156b4ce5923SAnton Protopopov 157b4ce5923SAnton Protopopov static bool is_insn_array(const struct bpf_map *map) 158b4ce5923SAnton Protopopov { 159b4ce5923SAnton Protopopov return map->map_type == BPF_MAP_TYPE_INSN_ARRAY; 160b4ce5923SAnton Protopopov } 161b4ce5923SAnton Protopopov 162b4ce5923SAnton Protopopov static inline bool valid_offsets(const struct bpf_insn_array *insn_array, 163b4ce5923SAnton Protopopov const struct bpf_prog *prog) 164b4ce5923SAnton Protopopov { 165b4ce5923SAnton Protopopov u32 off; 166b4ce5923SAnton Protopopov int i; 167b4ce5923SAnton Protopopov 168b4ce5923SAnton Protopopov for (i = 0; i < insn_array->map.max_entries; i++) { 169b4ce5923SAnton Protopopov off = insn_array->values[i].orig_off; 170b4ce5923SAnton Protopopov 171b4ce5923SAnton Protopopov if (off >= prog->len) 172b4ce5923SAnton Protopopov return false; 173b4ce5923SAnton Protopopov 174b4ce5923SAnton Protopopov if (off > 0) { 175b4ce5923SAnton Protopopov if (prog->insnsi[off-1].code == (BPF_LD | BPF_DW | BPF_IMM)) 176b4ce5923SAnton Protopopov return false; 177b4ce5923SAnton Protopopov } 178b4ce5923SAnton Protopopov } 179b4ce5923SAnton Protopopov 180b4ce5923SAnton Protopopov return true; 181b4ce5923SAnton Protopopov } 182b4ce5923SAnton Protopopov 183b4ce5923SAnton Protopopov int bpf_insn_array_init(struct bpf_map *map, const struct bpf_prog *prog) 184b4ce5923SAnton Protopopov { 185b4ce5923SAnton Protopopov struct bpf_insn_array *insn_array = cast_insn_array(map); 186b4ce5923SAnton Protopopov struct bpf_insn_array_value *values = insn_array->values; 187b4ce5923SAnton Protopopov int i; 188b4ce5923SAnton Protopopov 189b4ce5923SAnton Protopopov if (!is_frozen(map)) 190b4ce5923SAnton Protopopov return -EINVAL; 191b4ce5923SAnton Protopopov 192b4ce5923SAnton Protopopov if (!valid_offsets(insn_array, prog)) 193b4ce5923SAnton Protopopov return -EINVAL; 194b4ce5923SAnton Protopopov 195b4ce5923SAnton Protopopov /* 196b4ce5923SAnton Protopopov * There can be only one program using the map 197b4ce5923SAnton Protopopov */ 198b4ce5923SAnton Protopopov if (atomic_xchg(&insn_array->used, 1)) 199b4ce5923SAnton Protopopov return -EBUSY; 200b4ce5923SAnton Protopopov 201b4ce5923SAnton Protopopov /* 202b4ce5923SAnton Protopopov * Reset all the map indexes to the original values. This is needed, 203b4ce5923SAnton Protopopov * e.g., when a replay of verification with different log level should 204b4ce5923SAnton Protopopov * be performed. 205b4ce5923SAnton Protopopov */ 206b4ce5923SAnton Protopopov for (i = 0; i < map->max_entries; i++) 207b4ce5923SAnton Protopopov values[i].xlated_off = values[i].orig_off; 208b4ce5923SAnton Protopopov 209b4ce5923SAnton Protopopov return 0; 210b4ce5923SAnton Protopopov } 211b4ce5923SAnton Protopopov 212b4ce5923SAnton Protopopov int bpf_insn_array_ready(struct bpf_map *map) 213b4ce5923SAnton Protopopov { 214b4ce5923SAnton Protopopov struct bpf_insn_array *insn_array = cast_insn_array(map); 215b4ce5923SAnton Protopopov int i; 216b4ce5923SAnton Protopopov 217b4ce5923SAnton Protopopov for (i = 0; i < map->max_entries; i++) { 218b4ce5923SAnton Protopopov if (insn_array->values[i].xlated_off == INSN_DELETED) 219b4ce5923SAnton Protopopov continue; 220b4ce5923SAnton Protopopov if (!insn_array->ips[i]) 221b4ce5923SAnton Protopopov return -EFAULT; 222b4ce5923SAnton Protopopov } 223b4ce5923SAnton Protopopov 224b4ce5923SAnton Protopopov return 0; 225b4ce5923SAnton Protopopov } 226b4ce5923SAnton Protopopov 227b4ce5923SAnton Protopopov void bpf_insn_array_release(struct bpf_map *map) 228b4ce5923SAnton Protopopov { 229b4ce5923SAnton Protopopov struct bpf_insn_array *insn_array = cast_insn_array(map); 230b4ce5923SAnton Protopopov 231b4ce5923SAnton Protopopov atomic_set(&insn_array->used, 0); 232b4ce5923SAnton Protopopov } 233b4ce5923SAnton Protopopov 234b4ce5923SAnton Protopopov void bpf_insn_array_adjust(struct bpf_map *map, u32 off, u32 len) 235b4ce5923SAnton Protopopov { 236b4ce5923SAnton Protopopov struct bpf_insn_array *insn_array = cast_insn_array(map); 237b4ce5923SAnton Protopopov int i; 238b4ce5923SAnton Protopopov 239b4ce5923SAnton Protopopov if (len <= 1) 240b4ce5923SAnton Protopopov return; 241b4ce5923SAnton Protopopov 242b4ce5923SAnton Protopopov for (i = 0; i < map->max_entries; i++) { 243b4ce5923SAnton Protopopov if (insn_array->values[i].xlated_off <= off) 244b4ce5923SAnton Protopopov continue; 245b4ce5923SAnton Protopopov if (insn_array->values[i].xlated_off == INSN_DELETED) 246b4ce5923SAnton Protopopov continue; 247b4ce5923SAnton Protopopov insn_array->values[i].xlated_off += len - 1; 248b4ce5923SAnton Protopopov } 249b4ce5923SAnton Protopopov } 250b4ce5923SAnton Protopopov 251b4ce5923SAnton Protopopov void bpf_insn_array_adjust_after_remove(struct bpf_map *map, u32 off, u32 len) 252b4ce5923SAnton Protopopov { 253b4ce5923SAnton Protopopov struct bpf_insn_array *insn_array = cast_insn_array(map); 254b4ce5923SAnton Protopopov int i; 255b4ce5923SAnton Protopopov 256b4ce5923SAnton Protopopov for (i = 0; i < map->max_entries; i++) { 257b4ce5923SAnton Protopopov if (insn_array->values[i].xlated_off < off) 258b4ce5923SAnton Protopopov continue; 259b4ce5923SAnton Protopopov if (insn_array->values[i].xlated_off == INSN_DELETED) 260b4ce5923SAnton Protopopov continue; 261b4ce5923SAnton Protopopov if (insn_array->values[i].xlated_off < off + len) 262b4ce5923SAnton Protopopov insn_array->values[i].xlated_off = INSN_DELETED; 263b4ce5923SAnton Protopopov else 264b4ce5923SAnton Protopopov insn_array->values[i].xlated_off -= len; 265b4ce5923SAnton Protopopov } 266b4ce5923SAnton Protopopov } 267b4ce5923SAnton Protopopov 268b4ce5923SAnton Protopopov /* 269b4ce5923SAnton Protopopov * This function is called by JITs. The image is the real program 270b4ce5923SAnton Protopopov * image, the offsets array set up the xlated -> jitted mapping. 271b4ce5923SAnton Protopopov * The offsets[xlated] offset should point to the beginning of 272b4ce5923SAnton Protopopov * the jitted instruction. 273b4ce5923SAnton Protopopov */ 274b4ce5923SAnton Protopopov void bpf_prog_update_insn_ptrs(struct bpf_prog *prog, u32 *offsets, void *image) 275b4ce5923SAnton Protopopov { 276b4ce5923SAnton Protopopov struct bpf_insn_array *insn_array; 277b4ce5923SAnton Protopopov struct bpf_map *map; 278b4ce5923SAnton Protopopov u32 xlated_off; 279b4ce5923SAnton Protopopov int i, j; 280b4ce5923SAnton Protopopov 281b4ce5923SAnton Protopopov if (!offsets || !image) 282b4ce5923SAnton Protopopov return; 283b4ce5923SAnton Protopopov 284b4ce5923SAnton Protopopov for (i = 0; i < prog->aux->used_map_cnt; i++) { 285b4ce5923SAnton Protopopov map = prog->aux->used_maps[i]; 286b4ce5923SAnton Protopopov if (!is_insn_array(map)) 287b4ce5923SAnton Protopopov continue; 288b4ce5923SAnton Protopopov 289b4ce5923SAnton Protopopov insn_array = cast_insn_array(map); 290b4ce5923SAnton Protopopov for (j = 0; j < map->max_entries; j++) { 291b4ce5923SAnton Protopopov xlated_off = insn_array->values[j].xlated_off; 292b4ce5923SAnton Protopopov if (xlated_off == INSN_DELETED) 293b4ce5923SAnton Protopopov continue; 294b4ce5923SAnton Protopopov if (xlated_off < prog->aux->subprog_start) 295b4ce5923SAnton Protopopov continue; 296b4ce5923SAnton Protopopov xlated_off -= prog->aux->subprog_start; 297b4ce5923SAnton Protopopov if (xlated_off >= prog->len) 298b4ce5923SAnton Protopopov continue; 299b4ce5923SAnton Protopopov 300b4ce5923SAnton Protopopov insn_array->values[j].jitted_off = offsets[xlated_off]; 301b4ce5923SAnton Protopopov insn_array->ips[j] = (long)(image + offsets[xlated_off]); 302b4ce5923SAnton Protopopov } 303b4ce5923SAnton Protopopov } 304b4ce5923SAnton Protopopov } 305