10996e936STejun Heo // SPDX-License-Identifier: GPL-2.0
20996e936STejun Heo /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
30996e936STejun Heo
40996e936STejun Heo #define BPF_NO_KFUNC_PROTOTYPES
50996e936STejun Heo #include <vmlinux.h>
60996e936STejun Heo #include <bpf/bpf_helpers.h>
70996e936STejun Heo #include "bpf_misc.h"
80996e936STejun Heo #include "bpf_experimental.h"
90996e936STejun Heo #include <bpf_arena_common.h>
100996e936STejun Heo #include "../test_kmods/bpf_testmod_kfunc.h"
110996e936STejun Heo
120996e936STejun Heo struct {
130996e936STejun Heo __uint(type, BPF_MAP_TYPE_ARENA);
140996e936STejun Heo __uint(map_flags, BPF_F_MMAPABLE);
150996e936STejun Heo /* page 0 hosts the arena global, page 1 is for allocations */
160996e936STejun Heo __uint(max_entries, 2);
170996e936STejun Heo } arena SEC(".maps");
180996e936STejun Heo
190996e936STejun Heo /*
200996e936STejun Heo * Occupies page 0 so no allocation lands at arena offset 0, which the
210996e936STejun Heo * nullable tests below must be able to tell apart from NULL.
220996e936STejun Heo */
230996e936STejun Heo u64 __arena arena_pad;
240996e936STejun Heo
250996e936STejun Heo /* volatile to force the scalar reloads below */
260996e936STejun Heo volatile u64 stash;
270996e936STejun Heo
280996e936STejun Heo SEC("syscall")
290996e936STejun Heo __arch_x86_64
30*05a3575fSPuranjay Mohan __arch_arm64
310996e936STejun Heo __success __retval(0)
arena_arg_forms(void * ctx)320996e936STejun Heo int arena_arg_forms(void *ctx)
330996e936STejun Heo {
340996e936STejun Heo #if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
350996e936STejun Heo u64 __arena *val;
360996e936STejun Heo u64 ret;
370996e936STejun Heo
380996e936STejun Heo val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
390996e936STejun Heo if (!val)
400996e936STejun Heo return 1;
410996e936STejun Heo
420996e936STejun Heo /* PTR_TO_ARENA argument */
430996e936STejun Heo *val = 41;
440996e936STejun Heo ret = bpf_kfunc_arena_arg_test((u64 *)val);
450996e936STejun Heo if (ret != 41 || *val != 42)
460996e936STejun Heo return 2;
470996e936STejun Heo
480996e936STejun Heo /* the low 32 bits as a scalar */
490996e936STejun Heo stash = (u32)(u64)val;
500996e936STejun Heo ret = bpf_kfunc_arena_arg_test((u64 *)stash);
510996e936STejun Heo if (ret != 42 || *val != 43)
520996e936STejun Heo return 3;
530996e936STejun Heo
540996e936STejun Heo /* the full user address as a scalar */
550996e936STejun Heo stash = (u64)val;
560996e936STejun Heo bpf_addr_space_cast(stash, 1, 0);
570996e936STejun Heo ret = bpf_kfunc_arena_arg_test((u64 *)stash);
580996e936STejun Heo if (ret != 43 || *val != 44)
590996e936STejun Heo return 4;
600996e936STejun Heo
610996e936STejun Heo bpf_arena_free_pages(&arena, (void __arena *)val, 1);
620996e936STejun Heo #endif
630996e936STejun Heo return 0;
640996e936STejun Heo }
650996e936STejun Heo
660996e936STejun Heo /*
670996e936STejun Heo * Pin the rebase semantics using the capture kfuncs, which return the raw
680996e936STejun Heo * argument value: __arena rebases unconditionally, so zero low 32 bits
690996e936STejun Heo * arrive as the arena kernel base, while __arena__nullable turns them into
700996e936STejun Heo * NULL.
710996e936STejun Heo */
720996e936STejun Heo SEC("syscall")
730996e936STejun Heo __arch_x86_64
74*05a3575fSPuranjay Mohan __arch_arm64
750996e936STejun Heo __success __retval(0)
arena_arg_rebase(void * ctx)760996e936STejun Heo int arena_arg_rebase(void *ctx)
770996e936STejun Heo {
780996e936STejun Heo #if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
790996e936STejun Heo u64 __arena *val;
800996e936STejun Heo u64 base, off;
810996e936STejun Heo
820996e936STejun Heo val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
830996e936STejun Heo if (!val)
840996e936STejun Heo return 1;
850996e936STejun Heo
860996e936STejun Heo base = bpf_kfunc_arena_cap_test(NULL);
870996e936STejun Heo if (!base)
880996e936STejun Heo return 2;
890996e936STejun Heo
900996e936STejun Heo /* only the low 32 bits contribute */
910996e936STejun Heo stash = 0xbadc0ffe00000000;
920996e936STejun Heo if (bpf_kfunc_arena_cap_test((u64 *)stash) != base)
930996e936STejun Heo return 3;
940996e936STejun Heo
950996e936STejun Heo off = (u32)(u64)val;
960996e936STejun Heo if (bpf_kfunc_arena_cap_test((u64 *)val) != base + off)
970996e936STejun Heo return 4;
980996e936STejun Heo
990996e936STejun Heo if (bpf_kfunc_arena_cap_nullable_test(NULL) != 0)
1000996e936STejun Heo return 5;
1010996e936STejun Heo
1020996e936STejun Heo stash = 0xbadc0ffe00000000;
1030996e936STejun Heo if (bpf_kfunc_arena_cap_nullable_test((u64 *)stash) != 0)
1040996e936STejun Heo return 6;
1050996e936STejun Heo
1060996e936STejun Heo if (bpf_kfunc_arena_cap_nullable_test((u64 *)val) != base + off)
1070996e936STejun Heo return 7;
1080996e936STejun Heo
1090996e936STejun Heo bpf_arena_free_pages(&arena, (void __arena *)val, 1);
1100996e936STejun Heo #endif
1110996e936STejun Heo return 0;
1120996e936STejun Heo }
1130996e936STejun Heo
1140996e936STejun Heo SEC("syscall")
1150996e936STejun Heo __arch_x86_64
116*05a3575fSPuranjay Mohan __arch_arm64
1170996e936STejun Heo __success __retval(0)
arena_args5(void * ctx)1180996e936STejun Heo int arena_args5(void *ctx)
1190996e936STejun Heo {
1200996e936STejun Heo #if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
1210996e936STejun Heo u64 __arena *val;
1220996e936STejun Heo
1230996e936STejun Heo val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
1240996e936STejun Heo if (!val)
1250996e936STejun Heo return 1;
1260996e936STejun Heo
1270996e936STejun Heo val[0] = 1;
1280996e936STejun Heo val[1] = 2;
1290996e936STejun Heo val[2] = 4;
1300996e936STejun Heo val[3] = 8;
1310996e936STejun Heo val[4] = 16;
1320996e936STejun Heo
1330996e936STejun Heo if (bpf_kfunc_arena_args5_test((u64 *)&val[0], (u64 *)&val[1],
1340996e936STejun Heo (u64 *)&val[2], (u64 *)&val[3],
1350996e936STejun Heo (u64 *)&val[4]) != 31)
1360996e936STejun Heo return 2;
1370996e936STejun Heo if (bpf_kfunc_arena_args5_test((u64 *)&val[0], (u64 *)&val[1],
1380996e936STejun Heo (u64 *)&val[2], (u64 *)&val[3], NULL) != 15)
1390996e936STejun Heo return 3;
1400996e936STejun Heo
1410996e936STejun Heo bpf_arena_free_pages(&arena, (void __arena *)val, 1);
1420996e936STejun Heo #endif
1430996e936STejun Heo return 0;
1440996e936STejun Heo }
1450996e936STejun Heo
1460996e936STejun Heo SEC("syscall")
1470996e936STejun Heo __arch_x86_64
148*05a3575fSPuranjay Mohan __arch_arm64
1490996e936STejun Heo __success __retval(0)
arena_arg_mixed(void * ctx)1500996e936STejun Heo int arena_arg_mixed(void *ctx)
1510996e936STejun Heo {
1520996e936STejun Heo #if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
1530996e936STejun Heo u64 __arena *val;
1540996e936STejun Heo
1550996e936STejun Heo val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
1560996e936STejun Heo if (!val)
1570996e936STejun Heo return 1;
1580996e936STejun Heo
1590996e936STejun Heo val[0] = 7;
1600996e936STejun Heo val[1] = 5;
1610996e936STejun Heo
1620996e936STejun Heo if (bpf_kfunc_arena_mixed_test((u64 *)&val[0], NULL) != 7)
1630996e936STejun Heo return 2;
1640996e936STejun Heo
1650996e936STejun Heo if (bpf_kfunc_arena_mixed_test((u64 *)&val[0], (u64 *)&val[1]) != 12)
1660996e936STejun Heo return 3;
1670996e936STejun Heo
1680996e936STejun Heo bpf_arena_free_pages(&arena, (void __arena *)val, 1);
1690996e936STejun Heo #endif
1700996e936STejun Heo return 0;
1710996e936STejun Heo }
1720996e936STejun Heo
1730996e936STejun Heo /* kernel-side faults on unpopulated pages recover via the scratch page */
1740996e936STejun Heo SEC("syscall")
1750996e936STejun Heo __arch_x86_64
176*05a3575fSPuranjay Mohan __arch_arm64
1770996e936STejun Heo __success __retval(0)
arena_arg_unpopulated(void * ctx)1780996e936STejun Heo int arena_arg_unpopulated(void *ctx)
1790996e936STejun Heo {
1800996e936STejun Heo #if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
1810996e936STejun Heo u64 __arena *val;
1820996e936STejun Heo
1830996e936STejun Heo val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
1840996e936STejun Heo if (!val)
1850996e936STejun Heo return 1;
1860996e936STejun Heo
1870996e936STejun Heo stash = (u64)val + PAGE_SIZE;
1880996e936STejun Heo bpf_kfunc_arena_arg_test((u64 *)stash);
1890996e936STejun Heo
1900996e936STejun Heo bpf_arena_free_pages(&arena, (void __arena *)val, 1);
1910996e936STejun Heo #endif
1920996e936STejun Heo return 0;
1930996e936STejun Heo }
1940996e936STejun Heo
1950996e936STejun Heo SEC("syscall")
1960996e936STejun Heo __arch_x86_64
197*05a3575fSPuranjay Mohan __arch_arm64
1980996e936STejun Heo __failure __msg("arena pointer requires a program with an associated arena")
arena_arg_no_arena(void * ctx)1990996e936STejun Heo int arena_arg_no_arena(void *ctx)
2000996e936STejun Heo {
2010996e936STejun Heo bpf_kfunc_arena_arg_test((u64 *)1);
2020996e936STejun Heo return 0;
2030996e936STejun Heo }
2040996e936STejun Heo
2050996e936STejun Heo SEC("syscall")
2060996e936STejun Heo __arch_x86_64
207*05a3575fSPuranjay Mohan __arch_arm64
2080996e936STejun Heo __failure __msg("is not a pointer to arena or scalar")
arena_arg_bad_reg(void * ctx)2090996e936STejun Heo int arena_arg_bad_reg(void *ctx)
2100996e936STejun Heo {
2110996e936STejun Heo u64 buf = 0;
2120996e936STejun Heo
2130996e936STejun Heo /* use the arena so the program passes the arena presence check */
2140996e936STejun Heo bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
2150996e936STejun Heo bpf_kfunc_arena_arg_test(&buf);
2160996e936STejun Heo return 0;
2170996e936STejun Heo }
2180996e936STejun Heo
2190996e936STejun Heo #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) && \
2200996e936STejun Heo defined(__BPF_FEATURE_STACK_ARGUMENT)
2210996e936STejun Heo SEC("syscall")
2220996e936STejun Heo __arch_x86_64
223*05a3575fSPuranjay Mohan __arch_arm64
2240996e936STejun Heo __failure __msg("arena pointer cannot be a stack argument")
arena_arg_stack(void * ctx)2250996e936STejun Heo int arena_arg_stack(void *ctx)
2260996e936STejun Heo {
2270996e936STejun Heo bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
2280996e936STejun Heo bpf_kfunc_arena_stack_arg_test(1, 2, 3, 4, 5, (u64 *)1);
2290996e936STejun Heo return 0;
2300996e936STejun Heo }
2310996e936STejun Heo #else
2320996e936STejun Heo SEC("syscall")
2330996e936STejun Heo __arch_x86_64
234*05a3575fSPuranjay Mohan __arch_arm64
2350996e936STejun Heo __description("arena_arg_stack: not supported, dummy test")
2360996e936STejun Heo __success
arena_arg_stack(void * ctx)2370996e936STejun Heo int arena_arg_stack(void *ctx)
2380996e936STejun Heo {
2390996e936STejun Heo return 0;
2400996e936STejun Heo }
2410996e936STejun Heo #endif
2420996e936STejun Heo
2430996e936STejun Heo char _license[] SEC("license") = "GPL";
244