// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ #include #include #include "bpf_misc.h" #include "../test_kmods/bpf_testmod_kfunc.h" /* * Reference kfunc addresses to force those BTF to be emitted. Taking the address * (rather than calling) avoids any dependence on the compiler lowering an * __int128 or struct return value, which the BPF backend only supports from * LLVM 23 on. */ void __kfunc_btf_root(void) { asm volatile ("" : : "r"(&bpf_kfunc_call_test_i128), "r"(&bpf_kfunc_call_test_ret_fastcall), "r"(&bpf_kfunc_call_test_ret_ptr), "r"(&bpf_kfunc_call_test_ret_ii), "r"(&bpf_kfunc_call_test_ret_nested), "r"(&bpf_kfunc_call_test_ret_ptr_arr), "r"(&bpf_kfunc_call_test_ret_deep), "r"(&bpf_kfunc_call_test_ret_arr_struct), "r"(&bpf_kfunc_call_test_ret_arr2d), "r"(&bpf_kfunc_call_test_ret_big)); } SEC("tc") __arch_x86_64 __arch_arm64 __load_if_JITed() __success __retval(0) __log_level(2) __msg("mark_precise: frame0: last_idx 7 first_idx 0 subseq_idx -1") __msg("mark_precise: frame0: regs=r6 stack= before 6: (07) r1 += -8") __msg("mark_precise: frame0: regs=r6 stack= before 5: (bf) r1 = r10") __msg("mark_precise: frame0: regs=r6 stack= before 4: (57) r6 &= 7") __msg("mark_precise: frame0: regs=r6 stack= before 3: (bf) r6 = r2") __msg("mark_precise: frame0: regs=r2 stack= before 2: (85) call bpf_kfunc_call_test_i128") __naked int aggregate_ret_kfunc_precise(void) { asm volatile ( "r1 = 1;" "r2 = 2;" "call %[bpf_kfunc_call_test_i128];" "r6 = r2;" /* second return half */ "r6 &= 7;" /* keep it in [0, 7] to index the stack */ "r1 = r10;" "r1 += -8;" "r1 += r6;" /* ptr += scalar marks r6 (hence R2) precise */ "r0 = 0;" "*(u8 *)(r1 + 0) = r0;" "r0 = 0;" "exit;" : : __imm(bpf_kfunc_call_test_i128) : __clobber_all); } SEC("tc") __arch_x86_64 __arch_arm64 __failure __msg("kfunc bpf_kfunc_call_test_ret_fastcall with >8-byte return is not supported with KF_FASTCALL") __naked int aggregate_ret_kfunc_fastcall_fail(void) { asm volatile ( "r1 = 1;" "r2 = 2;" "call %[bpf_kfunc_call_test_ret_fastcall];" "r0 = 0;" "exit;" : : __imm(bpf_kfunc_call_test_ret_fastcall) : __clobber_all); } SEC("tc") __arch_x86_64 __arch_arm64 __failure __msg("is not composed of scalars or arena pointers") __msg("member 'p' has type PTR") __naked int aggregate_ret_kfunc_ptr_fail(void) { asm volatile ( "r1 = 0;" "call %[bpf_kfunc_call_test_ret_ptr];" "r0 = 0;" "exit;" : : __imm(bpf_kfunc_call_test_ret_ptr) : __clobber_all); } SEC("tc") __arch_x86_64 __arch_arm64 __failure __msg("is not composed of scalars or arena pointers") __msg("member 'in.p' has type PTR") __naked int aggregate_ret_kfunc_nested_ptr_fail(void) { asm volatile ( "r1 = 0;" "call %[bpf_kfunc_call_test_ret_nested];" "r0 = 0;" "exit;" : : __imm(bpf_kfunc_call_test_ret_nested) : __clobber_all); } SEC("tc") __arch_x86_64 __arch_arm64 __failure __msg("is not composed of scalars or arena pointers") __msg("member 'p[]' has type PTR") __naked int aggregate_ret_kfunc_ptr_arr_fail(void) { asm volatile ( "call %[bpf_kfunc_call_test_ret_ptr_arr];" "r0 = 0;" "exit;" : : __imm(bpf_kfunc_call_test_ret_ptr_arr) : __clobber_all); } SEC("tc") __arch_x86_64 __arch_arm64 __failure __msg("is not composed of scalars or arena pointers") __msg("member 'in1[].in2.p' has type PTR") __naked int aggregate_ret_kfunc_arr_struct_fail(void) { asm volatile ( "call %[bpf_kfunc_call_test_ret_arr_struct];" "r0 = 0;" "exit;" : : __imm(bpf_kfunc_call_test_ret_arr_struct) : __clobber_all); } SEC("tc") __arch_x86_64 __arch_arm64 __failure __msg("is not composed of scalars or arena pointers") __msg("member 'a[].p' has type PTR") __naked int aggregate_ret_kfunc_arr2d_fail(void) { asm volatile ( "call %[bpf_kfunc_call_test_ret_arr2d];" "r0 = 0;" "exit;" : : __imm(bpf_kfunc_call_test_ret_arr2d) : __clobber_all); } SEC("tc") __arch_x86_64 __arch_arm64 __failure __msg("max struct nesting depth exceeded") __naked int aggregate_ret_kfunc_too_deep_fail(void) { asm volatile ( "r1 = 0;" "call %[bpf_kfunc_call_test_ret_deep];" "r0 = 0;" "exit;" : : __imm(bpf_kfunc_call_test_ret_deep) : __clobber_all); } SEC("tc") __arch_x86_64 __arch_arm64 __failure __msg("R2 !read_ok") __naked int aggregate_ret_kfunc_small_no_r2(void) { asm volatile ( "r1 = 0;" "r2 = 0;" "call %[bpf_kfunc_call_test_ret_ii];" "r0 = r2;" /* R2 is not a return register for a <=8 byte struct */ "exit;" : : __imm(bpf_kfunc_call_test_ret_ii) : __clobber_all); } /* * A return value larger than 16 bytes does not fit in R0:R2 and is rejected by * btf_distill_func_proto() before the KF_FASTCALL and JIT-capability checks. */ SEC("tc") __arch_x86_64 __arch_arm64 __failure __msg("The function bpf_kfunc_call_test_ret_big return type STRUCT is unsupported") __naked int aggregate_ret_kfunc_too_big_fail(void) { asm volatile ( "call %[bpf_kfunc_call_test_ret_big];" "r0 = 0;" "exit;" : : __imm(bpf_kfunc_call_test_ret_big) : __clobber_all); } char _license[] SEC("license") = "GPL";