blob: c87e886423f4ff38b40012937b4ede37c6a193a6 [file] [edit]
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#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";