1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ 3 4 #define BPF_NO_KFUNC_PROTOTYPES 5 #include <vmlinux.h> 6 #include <bpf/bpf_helpers.h> 7 #include "bpf_experimental.h" 8 #include <bpf_arena_common.h> 9 #include "../test_kmods/bpf_testmod.h" 10 #include "../test_kmods/bpf_testmod_kfunc.h" 11 12 char _license[] SEC("license") = "GPL"; 13 14 struct { 15 __uint(type, BPF_MAP_TYPE_ARENA); 16 __uint(map_flags, BPF_F_MMAPABLE); 17 /* page 0 hosts the arena globals, page 1 is for allocations */ 18 __uint(max_entries, 2); 19 } arena SEC(".maps"); 20 21 /* also associates the callbacks with the arena */ 22 u64 __arena arena_touch; 23 /* raw value of the last __arena ctx argument, captured by test_arena_cb */ 24 u64 __arena cb_ptr_val; 25 26 SEC("struct_ops/test_arena") 27 int test_arena_cb(unsigned long long *ctx) 28 { 29 u64 __arena *ptr = (u64 __arena *)ctx[0]; 30 31 arena_touch++; 32 cb_ptr_val = ctx[0]; 33 *ptr += 1; 34 return 0; 35 } 36 37 SEC("struct_ops/test_arena_nullable") 38 int test_arena_nullable_cb(unsigned long long *ctx) 39 { 40 u64 __arena *ptr = (u64 __arena *)ctx[0]; 41 42 arena_touch++; 43 if (!ptr) 44 return 0xbee; 45 *ptr += 1; 46 return 0; 47 } 48 49 SEC("struct_ops/test_arena_stack") 50 int test_arena_stack_cb(unsigned long long *ctx) 51 { 52 u64 __arena *ptr = (u64 __arena *)ctx[8]; 53 54 arena_touch++; 55 /* pin the slot layout: the leading args fill ctx[0]..ctx[7] */ 56 if (ctx[0] != 1 || ctx[7] != 8) 57 return 0xbad; 58 *ptr += 1; 59 return 0; 60 } 61 62 SEC("struct_ops/test_arena_multislot") 63 int test_arena_multislot_cb(unsigned long long *ctx) 64 { 65 u64 __arena *ptr = (u64 __arena *)ctx[2]; 66 67 arena_touch++; 68 /* 69 * The 16-byte struct occupies ctx[0] and ctx[1], so @ptr is argument 70 * one but slot two. Getting that wrong hands the callback a scalar. 71 */ 72 if (ctx[0] != 11 || ctx[1] != 22) 73 return 0xbad; 74 *ptr += 1; 75 return 0; 76 } 77 78 SEC(".struct_ops.link") 79 struct bpf_testmod_ops3 testmod_arena = { 80 .test_arena = (void *)test_arena_cb, 81 .test_arena_nullable = (void *)test_arena_nullable_cb, 82 .test_arena_stack = (void *)test_arena_stack_cb, 83 .test_arena_multislot = (void *)test_arena_multislot_cb, 84 }; 85 86 SEC("syscall") 87 int trigger(void *ctx) 88 { 89 #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) 90 u64 __arena *val; 91 int ret; 92 93 val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0); 94 if (!val) 95 return 1; 96 97 *val = 41; 98 ret = bpf_testmod_ops3_call_test_arena((u64 *)val); 99 if (ret) 100 return 2; 101 if (*val != 42) 102 return 3; 103 104 /* 105 * The callback must have seen exactly (u32)(kaddr - kern_vm_start), 106 * which is the arena offset of val with the upper 32 bits clear. 107 */ 108 if (cb_ptr_val != (u32)(u64)val) 109 return 4; 110 111 ret = bpf_testmod_ops3_call_test_arena_nullable((u64 *)val); 112 if (ret) 113 return 5; 114 if (*val != 43) 115 return 6; 116 117 /* NULL survives the nullable kfunc and the trampoline as NULL */ 118 ret = bpf_testmod_ops3_call_test_arena_nullable(NULL); 119 if (ret != 0xbee) 120 return 7; 121 122 /* the arena pointer is stack-passed into the trampoline here */ 123 ret = bpf_testmod_ops3_call_test_arena_stack((u64 *)val); 124 if (ret) 125 return 8; 126 if (*val != 44) 127 return 9; 128 129 /* a multi-slot arg precedes the arena pointer here */ 130 ret = bpf_testmod_ops3_call_test_arena_multislot((u64 *)val); 131 if (ret) 132 return 10; 133 if (*val != 45) 134 return 11; 135 136 bpf_arena_free_pages(&arena, (void __arena *)val, 1); 137 #endif 138 return 0; 139 } 140