root/tools/testing/selftests/bpf/progs/verifier_bswap.c
// SPDX-License-Identifier: GPL-2.0

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include "bpf_misc.h"

#if (defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86) || \
        (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64) || \
        defined(__TARGET_ARCH_arm) || defined(__TARGET_ARCH_s390) || \
        defined(__TARGET_ARCH_loongarch)) && \
        __clang_major__ >= 18

SEC("socket")
__description("BSWAP, 16")
__success __success_unpriv __retval(0x23ff)
__naked void bswap_16(void)
{
        asm volatile ("                                 \
        r0 = 0xff23;                                    \
        r0 = bswap16 r0;                                \
        exit;                                           \
"       ::: __clobber_all);
}

SEC("socket")
__description("BSWAP, 32")
__success __success_unpriv __retval(0x23ff0000)
__naked void bswap_32(void)
{
        asm volatile ("                                 \
        r0 = 0xff23;                                    \
        r0 = bswap32 r0;                                \
        exit;                                           \
"       ::: __clobber_all);
}

SEC("socket")
__description("BSWAP, 64")
__success __success_unpriv __retval(0x34ff12ff)
__naked void bswap_64(void)
{
        asm volatile ("                                 \
        r0 = %[u64_val] ll;                                     \
        r0 = bswap64 r0;                                \
        exit;                                           \
"       :
        : [u64_val]"i"(0xff12ff34ff56ff78ull)
        : __clobber_all);
}

#define BSWAP_RANGE_TEST(name, op, in_value, out_value) \
        SEC("socket") \
        __success __log_level(2) \
        __msg("r0 &= {{.*}}; R0=scalar({{.*}},var_off=(0x0; " #in_value "))") \
        __msg("r0 = " op " r0 {{.*}}; R0=scalar({{.*}},var_off=(0x0; " #out_value "))") \
        __naked void name(void) \
        { \
                asm volatile (                          \
                "call %[bpf_get_prandom_u32];"          \
                "r0 &= " #in_value ";"                  \
                "r0 =  " op " r0;"                      \
                "r2 =  " #out_value " ll;"              \
                "if r0 > r2 goto trap_%=;"              \
                "r0 = 0;"                               \
                "exit;"                                 \
        "trap_%=:"                                      \
                "r1 = 42;"                              \
                "r0 = *(u64 *)(r1 + 0);"                \
                "exit;"                                 \
        :                                               \
        : __imm(bpf_get_prandom_u32)                    \
        : __clobber_all);                               \
        }

BSWAP_RANGE_TEST(bswap16_range, "bswap16", 0x3f00, 0x3f)
BSWAP_RANGE_TEST(bswap32_range, "bswap32", 0x3f00, 0x3f0000)
BSWAP_RANGE_TEST(bswap64_range, "bswap64", 0x3f00, 0x3f000000000000)
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
BSWAP_RANGE_TEST(be16_range, "be16", 0x3f00, 0x3f)
BSWAP_RANGE_TEST(be32_range, "be32", 0x3f00, 0x3f0000)
BSWAP_RANGE_TEST(be64_range, "be64", 0x3f00, 0x3f000000000000)
BSWAP_RANGE_TEST(le16_range, "le16", 0x3f00, 0x3f00)
BSWAP_RANGE_TEST(le32_range, "le32", 0x3f00, 0x3f00)
BSWAP_RANGE_TEST(le64_range, "le64", 0x3f00, 0x3f00)
#else
BSWAP_RANGE_TEST(be16_range, "be16", 0x3f00, 0x3f00)
BSWAP_RANGE_TEST(be32_range, "be32", 0x3f00, 0x3f00)
BSWAP_RANGE_TEST(be64_range, "be64", 0x3f00, 0x3f00)
BSWAP_RANGE_TEST(le16_range, "le16", 0x3f00, 0x3f)
BSWAP_RANGE_TEST(le32_range, "le32", 0x3f00, 0x3f0000)
BSWAP_RANGE_TEST(le64_range, "le64", 0x3f00, 0x3f000000000000)
#endif

SEC("socket")
__description("BSWAP, reset reg id")
__failure __msg("math between fp pointer and register with unbounded min value is not allowed")
__naked void bswap_reset_reg_id(void)
{
        asm volatile ("                                 \
        call %[bpf_ktime_get_ns];                       \
        r1 = r0;                                        \
        r0 = be16 r0;                                   \
        if r0 != 1 goto l0_%=;                          \
        r2 = r10;                                       \
        r2 += -512;                                     \
        r2 += r1;                                       \
        *(u8 *)(r2 + 0) = 0;                            \
l0_%=:                                                  \
        r0 = 0;                                         \
        exit;                                           \
"       :
        : __imm(bpf_ktime_get_ns)
        : __clobber_all);
}

#else

SEC("socket")
__description("cpuv4 is not supported by compiler or jit, use a dummy test")
__success
int dummy_test(void)
{
        return 0;
}

#endif

char _license[] SEC("license") = "GPL";