// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ #include #include #include "../test_kmods/bpf_testmod_kfunc.h" #include "bpf_misc.h" #ifdef __SIZEOF_INT128__ typedef unsigned __int128 u128; #endif #define MIX_A 0xdeadbeefcafef00dULL #define MIX_B 0x0123456789abcdefULL #if defined(__clang__) SEC("tc") __arch_x86_64 __arch_arm64 __load_if_JITed() __success __retval(0) int aggregate_arg_kfunc_struct(struct __sk_buff *skb) { __u64 a = skb->len ^ MIX_A; __u64 b = skb->len ^ MIX_B; struct prog_test_pair_arg s = { .lo = a, .hi = b }; if (bpf_kfunc_call_test_pair_arg(1, s, 2) != 2 * a + 3 * b + 9) return 1; return 0; } #endif #ifdef __SIZEOF_INT128__ SEC("tc") __arch_x86_64 __arch_arm64 __load_if_JITed() __success __retval(0) int aggregate_arg_kfunc_int128(struct __sk_buff *skb) { __u64 a = skb->len ^ MIX_A; __u64 b = skb->len ^ MIX_B; u128 v = ((u128)a << 64) | b; if (bpf_kfunc_call_test_i128_arg(1, 2, v) != 4 * a + 3 * b + 5) return 1; return 0; } /* * arm64 rounds the register number up to an even one for an argument * aligned to 16 bytes, so it wants this __int128 in x2 and x3, leaving x1 * empty, and the last argument in x4. The x86-64 ABI has no such rule. */ SEC("tc") __arch_x86_64 __arch_arm64 __load_if_JITed() __success __retval(0) int aggregate_arg_kfunc_int128_odd(struct __sk_buff *skb) { __u64 a = skb->len ^ MIX_A; __u64 b = skb->len ^ MIX_B; u128 v = ((u128)a << 64) | b; if (bpf_kfunc_call_test_i128_arg_odd(1, v, 2) != 1 + 2 * b + 3 * a + 4 * 2) return 1; return 0; } #endif /* __SIZEOF_INT128__ */ #if defined(__clang__) && defined(__BPF_FEATURE_STACK_ARGUMENT) SEC("tc") __arch_x86_64 __arch_arm64 __load_if_JITed() __success __retval(0) int aggregate_arg_kfunc_last_regs(struct __sk_buff *skb) { __u64 a = skb->len ^ MIX_A; __u64 b = skb->len ^ MIX_B; struct prog_test_pair_arg s = { .lo = a, .hi = b }; if (bpf_kfunc_call_test_pair_arg_nofit(1, 2, 3, 4, s) != 5 * a + 6 * b + 30) return 1; return 0; } /* * The x86-64 ABI moves an argument its six remaining registers cannot hold * wholly onto the stack. arm64, with eight argument registers, still has a * pair for it. */ SEC("tc") __arch_x86_64 __arch_arm64 __load_if_JITed() __success __retval(0) int aggregate_arg_kfunc_straddle(struct __sk_buff *skb) { __u64 a = skb->len ^ MIX_A; __u64 b = skb->len ^ MIX_B; struct prog_test_big_arg s = { .a = a, .b = b }; if (bpf_kfunc_call_stack_arg_big(1, 2, 3, 4, 5, s) != 6 * a + 7 * b + 55) return 1; return 0; } /* The same, with an argument after the struct to take the eighth register. */ SEC("tc") __arch_x86_64 __arch_arm64 __load_if_JITed() __success __retval(0) int aggregate_arg_kfunc_tail(struct __sk_buff *skb) { __u64 a = skb->len ^ MIX_A; __u64 b = skb->len ^ MIX_B; struct prog_test_pair_arg s = { .lo = a, .hi = b }; if (bpf_kfunc_call_test_pair_arg_tail(1, 2, 3, 4, 5, s, 6) != 6 * a + 7 * b + 103) return 1; return 0; } /* * arm64 gives no register to an argument its eight registers cannot hold, * nor to anything after it. Past its six registers the x86-64 ABI has both * eightbytes on the stack either way. */ SEC("tc") __arch_x86_64 __arch_arm64 __load_if_JITed() __success __retval(0) int aggregate_arg_kfunc_split8(struct __sk_buff *skb) { __u64 a = skb->len ^ MIX_A; __u64 b = skb->len ^ MIX_B; struct prog_test_pair_arg s = { .lo = a, .hi = b }; if (bpf_kfunc_call_test_pair_arg_split8(1, 2, 3, 4, 5, 6, 7, s) != 8 * a + 9 * b + 140) return 1; return 0; } #ifdef __SIZEOF_INT128__ /* * The hole of aggregate_arg_kfunc_int128_odd, with arguments after it that * arm64 shifts through x5 to x7, where its JIT keeps the BPF stack slots, * and onto its stack. The second __int128 then leaves the second arm64 * stack slot empty. x86-64 packs the eightbytes like the BPF convention, * so nothing moves there. */ SEC("tc") __arch_x86_64 __arch_arm64 __load_if_JITed() __success __retval(0) int aggregate_arg_kfunc_int128_odd_many(struct __sk_buff *skb) { __u64 a = skb->len ^ MIX_A; __u64 b = skb->len ^ MIX_B; u128 v = ((u128)a << 64) | b; u128 w = ((u128)~a << 64) | ~b; if (bpf_kfunc_call_test_i128_arg_odd_many(1, v, 2, 3, 4, 5, 6, w) != 1 + 2 * b + 3 * a + 4 * 2 + 5 * 3 + 6 * 4 + 7 * 5 + 8 * 6 + 9 * ~b + 10 * ~a) return 1; return 0; } /* * x86-64 moves s to the stack and gives R9 back to the seventh argument. * w, aligned to 16 bytes by the __int128 it holds, then leaves a stack slot * empty before it: the fourth on x86-64, the second on arm64. */ SEC("tc") __arch_x86_64 __arch_arm64 __load_if_JITed() __success __retval(0) int aggregate_arg_kfunc_int128_stack_hole(struct __sk_buff *skb) { __u64 a = skb->len ^ MIX_A; __u64 b = skb->len ^ MIX_B; struct prog_test_pair_arg s = { .lo = a, .hi = b }; struct prog_test_i128_arg w = { .v = ((u128)~a << 64) | ~b }; if (bpf_kfunc_call_test_i128_arg_stack_hole(1, 2, 3, 4, 5, s, 6, 7, w) != 1 + 2 * 2 + 3 * 3 + 4 * 4 + 5 * 5 + 6 * a + 7 * b + 8 * 6 + 9 * 7 + 10 * ~b + 11 * ~a) return 1; return 0; } /* * Both conventions pad the stack to align this __int128, and the BPF * convention pads for neither, so both JITs move it up an eightbyte. */ SEC("tc") __arch_x86_64 __arch_arm64 __load_if_JITed() __success __retval(0) int aggregate_arg_kfunc_int128_pad(struct __sk_buff *skb) { __u64 a = skb->len ^ MIX_A; __u64 b = skb->len ^ MIX_B; u128 v = ((u128)a << 64) | b; if (bpf_kfunc_call_test_i128_arg_pad(1, 2, 3, 4, 5, 6, 7, v) != 9 * a + 8 * b + 140) return 1; return 0; } #endif /* __SIZEOF_INT128__ */ #endif SEC("tc") __arch_x86_64 __arch_arm64 __failure __msg("R1 type STRUCT is not composed of scalars") int aggregate_arg_kfunc_ptr_member(struct __sk_buff *skb) { struct prog_test_ptr_arg s = { .p = skb, .x = 1 }; return bpf_kfunc_call_test_ptr_arg(s); } char _license[] SEC("license") = "GPL";