diff --git a/.cspell.json b/.cspell.json index 2c219799..f54cda7d 100644 --- a/.cspell.json +++ b/.cspell.json @@ -51,6 +51,7 @@ "ignorePaths": [ "target-specs/**", "tools/valgrind/**", - "tests/asm-test/asm/**" + "tests/asm-test/asm/**", + "tests/bpf/LICENSE-*" ] } diff --git a/.github/.cspell/project-dictionary.txt b/.github/.cspell/project-dictionary.txt index e26fede9..9f6652d9 100644 --- a/.github/.cspell/project-dictionary.txt +++ b/.github/.cspell/project-dictionary.txt @@ -15,6 +15,7 @@ balign beqz beqzc Bicc +bindeps bnez boundedly brne @@ -45,6 +46,7 @@ DWCAS eors espup fild +filosottile fistp fstat gaisler @@ -53,9 +55,11 @@ gettime GRLIB Halfword hwsync +iface IMAFD inequal insertnopload +insn ishld isync kuser @@ -89,8 +93,10 @@ lwarx lwsync mcpu membar +memcg memcheck memd +memlock memw mfcr mfence @@ -118,6 +124,8 @@ qbsp quadword rcpc risbg +rlim +rlimit rsbegin rsend RVWMO @@ -128,6 +136,7 @@ SCWP SCWPE seqz sete +setrlimit signedness simavr simio @@ -169,6 +178,7 @@ versatilepb virt vmovdqa wokwi +wreg xadd xchg xgetbv diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1822d5e8..026b2590 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,6 +6,7 @@ updates: # crates with [workspace] table are not recognized by the above 'directory: /' - /tests/asm-test - /tests/avr + - /tests/bpf - /tests/msp430 - /tests/no-std-linux - /tests/no-std-qemu diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b39464ea..7984a2af 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -698,6 +698,10 @@ jobs: tool: cargo-hack,espup fallback: none if: matrix.rust == 'stable' + - uses: taiki-e/cache-cargo-install-action@a8b9ecf8e0c0ea09d7481cfc583a5203ecd585b5 # v3.0.5 + with: + tool: bpf-linker + if: matrix.rust == 'nightly' - run: | retry() { for i in {1..10}; do diff --git a/README.md b/README.md index 44eeb5fa..2081bcb3 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ This crate provides a way to soundly perform such operations. ## Platform Support -Currently, all CPU architectures supported by Rust (x86, x86_64, Arm, AArch64, Arm64EC, RISC-V, LoongArch, s390x, PowerPC, MIPS, SPARC, AVR, MSP430, Hexagon, M68k, C-SKY, and Xtensa) are supported. +Currently, all CPU architectures supported by Rust (x86, x86_64, Arm, AArch64, Arm64EC, RISC-V, LoongArch, s390x, PowerPC, MIPS, SPARC, AVR, MSP430, Hexagon, M68k, C-SKY, and Xtensa) and BPF are supported. (You can use `cfg_{has,no}_*` macros to write code based on which primitive sizes are available for the current target and Rust version.) | target_arch | primitives | load/store | swap/CAS | @@ -53,6 +53,8 @@ Currently, all CPU architectures supported by Rust (x86, x86_64, Arm, AArch64, A | m68k (+isa-68020) \[9] \[13] (experimental) | i64,u64 | ✓ | ✓ | | csky \[13] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓\[1] | | xtensa \[13] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓\[1] | +| bpf \[13] (experimental) | isize,usize,i64,u64 | ✓ | ✓ | +| bpf (+alu32) \[13] \[14] (experimental) | i32,u32 | ✓ | ✓ | \[1] Arm's RMW operations are not available on Armv6-M (thumbv6m). RISC-V's RMW operations are not available on targets without the A (or G which means IMAFD) or Zalrsc or Zacas extension, such as riscv32i, riscv32imc, etc. 32-bit SPARC's RMW operations requires `v9` or `leoncasa` target feature (enabled by default on Linux). M68k's atomic RMW operations requires target-cpu M68020+ (enabled by default on Linux). C-SKY's atomic RMW operations requires target-cpu ck860\* or c860\* (enabled by default on the hard-float target). Xtensa's atomic RMW operations are not available on esp32s2.
\[2] Requires `cmpxchg16b` target feature (enabled by default on Apple, Windows (except Windows 7), and Fuchsia targets).
@@ -65,6 +67,7 @@ Currently, all CPU architectures supported by Rust (x86, x86_64, Arm, AArch64, A \[11] Requires Rust 1.91+.
\[12] Requires Rust 1.95+.
\[13] Requires nightly due to `#![feature(asm_experimental_arch)]`.
+\[14] Requires `alu32` target feature.
diff --git a/build.rs b/build.rs index f9f6a0b6..4214050e 100644 --- a/build.rs +++ b/build.rs @@ -122,7 +122,8 @@ fn main() { } } } - "avr" | "m68k" | "mips" | "mips32r6" | "mips64" | "mips64r6" | "msp430" | "xtensa" => { + "avr" | "bpf" | "m68k" | "mips" | "mips32r6" | "mips64" | "mips64r6" | "msp430" + | "xtensa" => { if version.nightly && is_allowed_feature("asm_experimental_arch") { println!("cargo:rustc-cfg=atomic_maybe_uninit_unstable_asm_experimental_arch"); } diff --git a/src/arch/README.md b/src/arch/README.md index c303643e..4e17d804 100644 --- a/src/arch/README.md +++ b/src/arch/README.md @@ -8,6 +8,7 @@ This document describes the operations that are considered atomic by architectur - [AArch64](#aarch64) - [Arm](#arm) - [AVR](#avr) +- [BPF](#bpf) - [C-SKY](#c-sky) - [Hexagon](#hexagon) - [LoongArch](#loongarch) @@ -64,6 +65,13 @@ This architecture is always single-core and the following operations are atomic: disabling and restoring implementation must imply compiler fences, e.g., asm without nomem/readonly) may be moved out of the critical section by compiler optimizations. +## BPF + +target_arch: bpf
+Implementation: [bpf.rs](bpf.rs)
+ +TODO: reference and overview + ## C-SKY target_arch: csky
diff --git a/src/arch/bpf.rs b/src/arch/bpf.rs new file mode 100644 index 00000000..b7aaac7a --- /dev/null +++ b/src/arch/bpf.rs @@ -0,0 +1,492 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +/* +BPF + +Refs: +- BPF Instruction Set Architecture (ISA) + https://github.com/torvalds/linux/blob/v6.16/Documentation/bpf/standardization/instruction-set.rst + +Generated asm: +- bpf https://godbolt.org/z/oEGqWY1f4 +*/ + +delegate_size!(delegate_all); + +use core::{ + arch::asm, + mem::{self, MaybeUninit}, + sync::atomic::Ordering, +}; + +use crate::raw::{AtomicCompareExchange, AtomicLoad, AtomicStore, AtomicSwap}; + +macro_rules! atomic32 { + ($ty:ident) => { + #[cfg(target_feature = "alu32")] + delegate_signed!(delegate_all, $ty); + #[cfg(target_feature = "alu32")] + impl AtomicLoad for $ty { + #[inline] + unsafe fn atomic_load( + src: *const MaybeUninit, + order: Ordering, + ) -> MaybeUninit { + let out: MaybeUninit; + + // SAFETY: the caller must uphold the safety contract. + unsafe { + match order { + Ordering::Relaxed => { + asm!( + concat!("{out} = *(", stringify!($ty), " *) ({src} + 0)"), // atomic { out = *src } + src = in(reg) src, + out = lateout(wreg) out, + options(nostack, preserves_flags), + ); + } + Ordering::Acquire | Ordering::SeqCst => { + // TODO: v4 has load_acquire: https://github.com/llvm/llvm-project/pull/108636 + asm!( + "w0 = cmpxchg32_32({src} + 0, w0, w0)", // atomic { _x = *dst; if _x == r0 { *dst = new }; r0 = _x } + src = in(reg) src, + inout("w0") 0_u32 => out, + options(nostack, preserves_flags), + ); + } + _ => unreachable!(), + } + } + out + } + } + #[cfg(target_feature = "alu32")] + impl AtomicStore for $ty { + #[inline] + unsafe fn atomic_store( + dst: *mut MaybeUninit, + val: MaybeUninit, + order: Ordering, + ) { + // SAFETY: the caller must uphold the safety contract. + unsafe { + match order { + Ordering::Relaxed => { + asm!( + concat!("*(", stringify!($ty), " *) ({dst} + 0) = w0"), // atomic { *dst = val } + dst = in(reg) dst, + in("w0") val, // TODO: rustc or LLVM bug: wreg sometimes allocated to reg + options(nostack, preserves_flags), + ); + } + Ordering::Release | Ordering::SeqCst => { + // TODO: v4 has store_release: https://github.com/llvm/llvm-project/pull/108636 + <$ty as AtomicSwap>::atomic_swap( + dst, + val, + Ordering::SeqCst, // swap is always SeqCst + ); + } + _ => unreachable!(), + } + } + } + } + #[cfg(target_feature = "alu32")] + impl AtomicSwap for $ty { + #[inline] + unsafe fn atomic_swap( + dst: *mut MaybeUninit, + val: MaybeUninit, + _order: Ordering, + ) -> MaybeUninit { + let mut out: MaybeUninit; + + // SAFETY: the caller must uphold the safety contract. + unsafe { + asm!( + "{val} = xchg32_32({dst} + 0, {val})", // atomic { _x = *dst; *dst = val; out = _x } + dst = in(reg) dst, + val = inout(wreg) val => out, + options(nostack, preserves_flags), + ); + } + out + } + } + #[cfg(target_feature = "alu32")] + impl AtomicCompareExchange for $ty { + #[inline] + unsafe fn atomic_compare_exchange( + dst: *mut MaybeUninit, + old: MaybeUninit, + new: MaybeUninit, + _success: Ordering, + _failure: Ordering, + ) -> (MaybeUninit, bool) { + let mut out: MaybeUninit; + + // SAFETY: the caller must uphold the safety contract. + unsafe { + let mut r: u64 = 1; + asm!( + "w0 = cmpxchg32_32({dst} + 0, w0, {new})", // atomic { _x = *dst; if _x == r0 { *dst = new }; r0 = _x } + "if w0 == {old} goto 2f", // if r0 == old { jump 'success } + "{r} = 0", // r = 0 + "2:", // 'success + dst = in(reg) dst, + old = in(wreg) old, + new = in(wreg) new, + r = inout(reg) r, + inout("w0") old => out, + options(nostack, preserves_flags), + ); + // crate::utils::assert_unchecked(r == 0 || r == 1); // may help remove extra test + (out, r != 0) + } + } + } + }; +} + +macro_rules! atomic64 { + ($ty:ident) => { + delegate_signed!(delegate_all, $ty); + impl AtomicLoad for $ty { + #[inline] + unsafe fn atomic_load( + src: *const MaybeUninit, + order: Ordering, + ) -> MaybeUninit { + let out: MaybeUninit; + + // SAFETY: the caller must uphold the safety contract. + unsafe { + match order { + Ordering::Relaxed => { + asm!( + concat!("{out} = *(", stringify!($ty), " *) ({src} + 0)"), // atomic { out = *src } + src = in(reg) src, + out = lateout(reg) out, + options(nostack, preserves_flags), + ); + } + Ordering::Acquire | Ordering::SeqCst => { + // TODO: v4 has load_acquire: https://github.com/llvm/llvm-project/pull/108636 + asm!( + "r0 = cmpxchg_64({src} + 0, r0, r0)", // atomic { _x = *dst; if _x == r0 { *dst = new }; r0 = _x } + src = in(reg) src, + inout("r0") 0_u64 => out, + options(nostack, preserves_flags), + ); + } + _ => unreachable!(), + } + } + out + } + } + impl AtomicStore for $ty { + #[inline] + unsafe fn atomic_store( + dst: *mut MaybeUninit, + val: MaybeUninit, + order: Ordering, + ) { + // SAFETY: the caller must uphold the safety contract. + unsafe { + match order { + Ordering::Relaxed => { + asm!( + concat!("*(", stringify!($ty), " *) ({dst} + 0) = {val}"), // atomic { *dst = val } + dst = in(reg) dst, + val = in(reg) val, + options(nostack, preserves_flags), + ); + } + Ordering::Release | Ordering::SeqCst => { + // TODO: v4 has store_release: https://github.com/llvm/llvm-project/pull/108636 + <$ty as AtomicSwap>::atomic_swap( + dst, + val, + Ordering::SeqCst, // swap is always SeqCst + ); + } + _ => unreachable!(), + } + } + } + } + impl AtomicSwap for $ty { + #[inline] + unsafe fn atomic_swap( + dst: *mut MaybeUninit, + val: MaybeUninit, + _order: Ordering, + ) -> MaybeUninit { + let mut out: MaybeUninit; + + // SAFETY: the caller must uphold the safety contract. + unsafe { + asm!( + "{val} = xchg_64({dst} + 0, {val})", // atomic { _x = *dst; *dst = val; out = _x } + dst = in(reg) dst, + val = inout(reg) val => out, + options(nostack, preserves_flags), + ); + } + out + } + } + impl AtomicCompareExchange for $ty { + #[inline] + unsafe fn atomic_compare_exchange( + dst: *mut MaybeUninit, + old: MaybeUninit, + new: MaybeUninit, + _success: Ordering, + _failure: Ordering, + ) -> (MaybeUninit, bool) { + let mut out: MaybeUninit; + + // SAFETY: the caller must uphold the safety contract. + unsafe { + let mut r: u64 = 1; + asm!( + "r0 = cmpxchg_64({dst} + 0, r0, {new})", // atomic { _x = *dst; if _x == r0 { *dst = new }; r0 = _x } + "if r0 == {old} goto 2f", // if r0 == old { jump 'success } + "{r} = 0", // r = 0 + "2:", // 'success + dst = in(reg) dst, + old = in(reg) old, + new = in(reg) new, + r = inout(reg) r, + inout("r0") old => out, + options(nostack, preserves_flags), + ); + // crate::utils::assert_unchecked(r == 0 || r == 1); // may help remove extra test + (out, r != 0) + } + } + } + }; +} + +/* +macro_rules! atomic_sub_word { + ($ty:ident) => { + #[cfg(target_feature = "alu32")] + delegate_signed!(delegate_load_store, $ty); + #[cfg(target_feature = "alu32")] + impl AtomicLoad for $ty { + #[inline] + unsafe fn atomic_load( + src: *const MaybeUninit, + order: Ordering, + ) -> MaybeUninit { + let out: MaybeUninit; + + // SAFETY: the caller must uphold the safety contract. + unsafe { + match order { + Ordering::Relaxed => { + asm!( + concat!("{out} = *(", stringify!($ty), " *) ({src} + 0)"), // atomic { out = *src } + src = in(reg) src, + out = lateout(wreg) out, + options(nostack, preserves_flags), + ); + } + Ordering::Acquire | Ordering::SeqCst => { + // TODO: v4 has load_acquire: https://github.com/llvm/llvm-project/pull/108636 + <$ty as AtomicCompareExchange>::atomic_compare_exchange( + src, + MaybeUninit::new(0), + MaybeUninit::new(0), + Ordering::SeqCst, // CAS is always SeqCst + Ordering::SeqCst, + ); + } + _ => unreachable!(), + } + } + out + } + } + #[cfg(target_feature = "alu32")] + impl AtomicStore for $ty { + #[inline] + unsafe fn atomic_store( + dst: *mut MaybeUninit, + val: MaybeUninit, + order: Ordering, + ) { + // SAFETY: the caller must uphold the safety contract. + unsafe { + match order { + Ordering::Relaxed => { + asm!( + concat!("*(", stringify!($ty), " *) ({dst} + 0) = w0"), // atomic { *dst = val } + dst = in(reg) dst, + in("w0") val, // TODO: rustc or LLVM bug: wreg sometimes allocated to reg + options(nostack, preserves_flags), + ); + } + Ordering::Release | Ordering::SeqCst => { + // TODO: v4 has store_release: https://github.com/llvm/llvm-project/pull/108636 + <$ty as AtomicSwap>::atomic_swap( + dst, + val, + Ordering::SeqCst, // swap is always SeqCst + ); + } + _ => unreachable!(), + } + } + } + } + // TODO: doesn't work due to "bitwise operator &= on pointer prohibited" verification error. + #[cfg(target_feature = "alu32")] + impl AtomicSwap for $ty { + #[inline] + unsafe fn atomic_swap( + dst: *mut MaybeUninit, + val: MaybeUninit, + _order: Ordering, + ) -> MaybeUninit { + debug_assert!(dst as usize % mem::size_of::<$ty>() == 0); + let (dst, shift, mask) = crate::utils::create_sub_word_mask_values(dst); + let mut out: MaybeUninit; + + // SAFETY: the caller must uphold the safety contract. + unsafe { + // Implement sub-word atomic operations using word-sized LL/SC loop. + // See also create_sub_word_mask_values. + asm!( + "{mask} <<= {shift}", // mask <<= shift & 63 + "{val} <<= {shift}", // val <<= shift & 63 + "w0 = *(u32 *) ({dst} + 0)", // atomic { r0 = *dst } + "2:", // 'retry: + "{prev} = w0", // prev = r0 + "{new} = {val}", // new = val + "{new} ^= w0", // new ^= r0 + "{new} &= {mask}", // new &= mask + "{new} ^= w0", // new ^= r0 + "w0 = cmpxchg32_32({dst} + 0, w0, {new})", // atomic { _x = *dst; if _x == r0 { *dst = new }; r0 = _x } + "if w0 != {prev} goto 2b", // if r0 != prev { jump 'retry } + "w0 >>= {shift}", // r0 >>= shift & 63 + dst = in(reg) dst, + val = inout(wreg) crate::utils::zero_extend32::$ty(val) => _, + shift = in(wreg) shift, + mask = inout(wreg) mask => _, + prev = out(wreg) _, + new = out(wreg) _, + out("w0") out, + options(nostack, preserves_flags), + ); + } + out + } + } + /* + #[cfg(target_feature = "alu32")] + impl AtomicCompareExchange for $ty { + #[inline] + unsafe fn atomic_compare_exchange( + dst: *mut MaybeUninit, + old: MaybeUninit, + new: MaybeUninit, + _success: Ordering, + _failure: Ordering, + ) -> (MaybeUninit, bool) { + debug_assert!(dst as usize % mem::size_of::<$ty>() == 0); + let (dst, shift, mask) = crate::utils::create_sub_word_mask_values(dst); + let mut out: MaybeUninit; + + // SAFETY: the caller must uphold the safety contract. + unsafe { + let mut r: i32 = 1; + // Implement sub-word atomic operations using word-sized LL/SC loop. + // See also create_sub_word_mask_values. + asm!( + "TODO", + options(nostack, preserves_flags), + ); + crate::utils::assert_unchecked(r == 0 || r == 1); // may help remove extra test + (out, r != 0) + } + } + } + */ + }; +} +*/ + +// atomic_sub_word!(u8); +// atomic_sub_word!(u16); +atomic32!(u32); +atomic64!(u64); + +// ----------------------------------------------------------------------------- +// cfg macros + +#[macro_export] +macro_rules! cfg_has_atomic_8 { + ($($tt:tt)*) => {}; +} +#[macro_export] +macro_rules! cfg_no_atomic_8 { + ($($tt:tt)*) => { $($tt)* }; +} +#[macro_export] +macro_rules! cfg_has_atomic_16 { + ($($tt:tt)*) => {}; +} +#[macro_export] +macro_rules! cfg_no_atomic_16 { + ($($tt:tt)*) => { $($tt)* }; +} +#[cfg(not(target_feature = "alu32"))] +#[macro_export] +macro_rules! cfg_has_atomic_32 { + ($($tt:tt)*) => {}; +} +#[cfg(not(target_feature = "alu32"))] +#[macro_export] +macro_rules! cfg_no_atomic_32 { + ($($tt:tt)*) => { $($tt)* }; +} +#[cfg(target_feature = "alu32")] +#[macro_export] +macro_rules! cfg_has_atomic_32 { + ($($tt:tt)*) => { $($tt)* }; +} +#[cfg(target_feature = "alu32")] +#[macro_export] +macro_rules! cfg_no_atomic_32 { + ($($tt:tt)*) => {}; +} +#[macro_export] +macro_rules! cfg_has_atomic_64 { + ($($tt:tt)*) => { $($tt)* }; +} +#[macro_export] +macro_rules! cfg_no_atomic_64 { + ($($tt:tt)*) => {}; +} +#[macro_export] +macro_rules! cfg_has_atomic_128 { + ($($tt:tt)*) => {}; +} +#[macro_export] +macro_rules! cfg_no_atomic_128 { + ($($tt:tt)*) => { $($tt)* }; +} +#[macro_export] +macro_rules! cfg_has_atomic_cas { + ($($tt:tt)*) => { $($tt)* }; +} +#[macro_export] +macro_rules! cfg_no_atomic_cas { + ($($tt:tt)*) => {}; +} diff --git a/src/lib.rs b/src/lib.rs index fa49556e..21c4dfe0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ This crate provides a way to soundly perform such operations. ## Platform Support -Currently, all CPU architectures supported by Rust (x86, x86_64, Arm, AArch64, Arm64EC, RISC-V, LoongArch, s390x, PowerPC, MIPS, SPARC, AVR, MSP430, Hexagon, M68k, C-SKY, and Xtensa) are supported. +Currently, all CPU architectures supported by Rust (x86, x86_64, Arm, AArch64, Arm64EC, RISC-V, LoongArch, s390x, PowerPC, MIPS, SPARC, AVR, MSP430, Hexagon, M68k, C-SKY, and Xtensa) and BPF are supported. (You can use `cfg_{has,no}_*` macros to write code based on which primitive sizes are available for the current target and Rust version.) | target_arch | primitives | load/store | swap/CAS | @@ -50,6 +50,8 @@ Currently, all CPU architectures supported by Rust (x86, x86_64, Arm, AArch64, A | m68k (+isa-68020) \[9] \[13] (experimental) | i64,u64 | ✓ | ✓ | | csky \[13] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓\[1] | | xtensa \[13] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓\[1] | +| bpf \[13] (experimental) | isize,usize,i64,u64 | ✓ | ✓ | +| bpf (+alu32) \[13] \[14] (experimental) | i32,u32 | ✓ | ✓ | \[1] Arm's RMW operations are not available on Armv6-M (thumbv6m). RISC-V's RMW operations are not available on targets without the A (or G which means IMAFD) or Zalrsc or Zacas extension, such as riscv32i, riscv32imc, etc. 32-bit SPARC's RMW operations requires `v9` or `leoncasa` target feature (enabled by default on Linux). M68k's atomic RMW operations requires target-cpu M68020+ (enabled by default on Linux). C-SKY's atomic RMW operations requires target-cpu ck860\* or c860\* (enabled by default on the hard-float target). Xtensa's atomic RMW operations are not available on esp32s2.
\[2] Requires `cmpxchg16b` target feature (enabled by default on Apple, Windows (except Windows 7), and Fuchsia targets).
@@ -62,6 +64,7 @@ Currently, all CPU architectures supported by Rust (x86, x86_64, Arm, AArch64, A \[11] Requires Rust 1.91+.
\[12] Requires Rust 1.95+.
\[13] Requires nightly due to `#![feature(asm_experimental_arch)]`.
+\[14] Requires `alu32` target feature.
@@ -867,6 +870,10 @@ pub use {cfg_has_atomic_128 as cfg_has_atomic_ptr, cfg_no_atomic_128 as cfg_no_a all(target_arch = "avr", atomic_maybe_uninit_unstable_asm_experimental_arch), path = "arch/avr.rs" )] +#[cfg_attr( + all(target_arch = "bpf", atomic_maybe_uninit_unstable_asm_experimental_arch), + path = "arch/bpf.rs" +)] #[cfg_attr( all(target_arch = "csky", atomic_maybe_uninit_unstable_asm_experimental_arch), path = "arch/csky.rs" diff --git a/src/utils.rs b/src/utils.rs index d371ee4f..b1afb14b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -609,12 +609,16 @@ pair!(MaybeUninit64, u64, u32); pair!(MaybeUninit16, u16, u8); #[cfg(not(target_pointer_width = "16"))] +#[cfg(all(target_arch = "bpf", not(target_feature = "alu32")))] +type MinWord = u64; +#[cfg(not(target_pointer_width = "16"))] +#[cfg(not(all(target_arch = "bpf", not(target_feature = "alu32"))))] type MinWord = u32; #[cfg(not(target_pointer_width = "16"))] -#[cfg(target_arch = "s390x")] +#[cfg(any(target_arch = "s390x", all(target_arch = "bpf", target_feature = "alu32")))] type RetInt = u32; #[cfg(not(target_pointer_width = "16"))] -#[cfg(not(target_arch = "s390x"))] +#[cfg(not(any(target_arch = "s390x", all(target_arch = "bpf", target_feature = "alu32"))))] type RetInt = RegSize; // Helper for implementing sub-word atomic operations using word-sized LL/SC loop or CAS loop. // diff --git a/tests/bpf/.gitignore b/tests/bpf/.gitignore new file mode 100644 index 00000000..9db7029f --- /dev/null +++ b/tests/bpf/.gitignore @@ -0,0 +1,9 @@ +### https://raw.github.com/github/gitignore/master/Rust.gitignore + +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# These are backup files generated by rustfmt +**/*.rs.bk diff --git a/tests/bpf/Cargo.toml b/tests/bpf/Cargo.toml new file mode 100644 index 00000000..6cc15a79 --- /dev/null +++ b/tests/bpf/Cargo.toml @@ -0,0 +1,35 @@ +[workspace] +resolver = "2" +members = [ + "bpf-test", + "bpf-test-common", + "bpf-test-ebpf", +] +default-members = ["bpf-test", "bpf-test-common"] + +[workspace.package] +license = "MIT OR Apache-2.0" +edition = "2024" + +[workspace.dependencies] +# Use patched aya-build for Cargo features and target features support. +aya = { git = "https://github.com/taiki-e/aya", rev = "9b2559f", default-features = false } +aya-build = { git = "https://github.com/taiki-e/aya", rev = "9b2559f", default-features = false } +aya-ebpf = { git = "https://github.com/taiki-e/aya", rev = "9b2559f", default-features = false } +aya-log = { git = "https://github.com/taiki-e/aya", rev = "9b2559f", default-features = false } +aya-log-ebpf = { git = "https://github.com/taiki-e/aya", rev = "9b2559f", default-features = false } + +anyhow = { version = "1", default-features = false } +# `std` feature is currently required to build `clap`. +# +# See https://github.com/clap-rs/clap/blob/61f5ee5/clap_builder/src/lib.rs#L15. +clap = { version = "4.5.20", default-features = false, features = ["std"] } +env_logger = { version = "0.11.5", default-features = false } +libc = { version = "0.2.159", default-features = false } +log = { version = "0.4.22", default-features = false } +tokio = { version = "1.40.0", default-features = false } +which = { version = "6.0.0", default-features = false } + +[profile.release.package.bpf-test-ebpf] +debug = 2 +codegen-units = 1 diff --git a/tests/bpf/LICENSE-APACHE b/tests/bpf/LICENSE-APACHE new file mode 100644 index 00000000..bf9834cc --- /dev/null +++ b/tests/bpf/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright {yyyy} Authors of bpfman. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and + limitations under the License. diff --git a/tests/bpf/LICENSE-GPL2 b/tests/bpf/LICENSE-GPL2 new file mode 100644 index 00000000..ecbc0593 --- /dev/null +++ b/tests/bpf/LICENSE-GPL2 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. \ No newline at end of file diff --git a/tests/bpf/LICENSE-MIT b/tests/bpf/LICENSE-MIT new file mode 100644 index 00000000..623121eb --- /dev/null +++ b/tests/bpf/LICENSE-MIT @@ -0,0 +1,21 @@ +Copyright (c) 2021 Alessandro Decina + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/tests/bpf/README.md b/tests/bpf/README.md new file mode 100644 index 00000000..863aea65 --- /dev/null +++ b/tests/bpf/README.md @@ -0,0 +1,58 @@ +# bpf-test + +## Prerequisites + +1. stable rust toolchains: `rustup toolchain install stable` +1. nightly rust toolchains: `rustup toolchain install nightly --component rust-src` +1. (if cross-compiling) rustup target: `rustup target add ${ARCH}-unknown-linux-musl` +1. (if cross-compiling) LLVM: (e.g.) `brew install llvm` (on macOS) +1. (if cross-compiling) C toolchain: (e.g.) [`brew install filosottile/musl-cross/musl-cross`](https://github.com/FiloSottile/homebrew-musl-cross) (on macOS) +1. bpf-linker: `cargo install bpf-linker` (`--no-default-features` on macOS) + +## Build & Run + +Use `cargo build`, `cargo check`, etc. as normal. Run your program with: + +```shell +cargo run --release --config 'target."cfg(all())".runner="sudo -E"' +``` + +Cargo build scripts are used to automatically build the eBPF correctly and include it in the +program. + +## Cross-compiling on macOS + +Cross compilation should work on both Intel and Apple Silicon Macs. + +```shell +CC=${ARCH}-linux-musl-gcc cargo build --package bpf-test --release \ + --target=${ARCH}-unknown-linux-musl \ + --config=target.${ARCH}-unknown-linux-musl.linker=\"${ARCH}-linux-musl-gcc\" +``` + +The cross-compiled program `target/${ARCH}-unknown-linux-musl/release/bpf-test` can be +copied to a Linux server or VM and run there. + +## License + +With the exception of eBPF code, bpf-test is distributed under the terms +of either the [MIT license] or the [Apache License] (version 2.0), at your +option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + +### eBPF + +All eBPF code is distributed under either the terms of the +[GNU General Public License, Version 2] or the [MIT license], at your +option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this project by you, as defined in the GPL-2 license, shall be +dual licensed as above, without any additional terms or conditions. + +[Apache license]: LICENSE-APACHE +[MIT license]: LICENSE-MIT +[GNU General Public License, Version 2]: LICENSE-GPL2 diff --git a/tests/bpf/bpf-test-common/Cargo.toml b/tests/bpf/bpf-test-common/Cargo.toml new file mode 100644 index 00000000..0f95f818 --- /dev/null +++ b/tests/bpf/bpf-test-common/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "bpf-test-common" +version = "0.1.0" +edition.workspace = true + +license.workspace = true + +[features] +default = [] +user = ["aya"] + +[dependencies] +aya = { workspace = true, optional = true } + +[lib] +path = "src/lib.rs" diff --git a/tests/bpf/bpf-test-common/src/lib.rs b/tests/bpf/bpf-test-common/src/lib.rs new file mode 100644 index 00000000..6bcb8392 --- /dev/null +++ b/tests/bpf/bpf-test-common/src/lib.rs @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#![no_std] diff --git a/tests/bpf/bpf-test-ebpf/Cargo.toml b/tests/bpf/bpf-test-ebpf/Cargo.toml new file mode 100644 index 00000000..4f5fb789 --- /dev/null +++ b/tests/bpf/bpf-test-ebpf/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "bpf-test-ebpf" +version = "0.1.0" +edition.workspace = true + +[features] +default = [] +i32 = [] +u32 = [] + +[dependencies] +bpf-test-common = { path = "../bpf-test-common" } +atomic-maybe-uninit = { path = "../../.." } + +aya-ebpf = { workspace = true } +aya-log-ebpf = { workspace = true } +paste = "1" + +[build-dependencies] +which = { workspace = true } + +[[bin]] +name = "bpf-test" +path = "src/main.rs" diff --git a/tests/bpf/bpf-test-ebpf/build.rs b/tests/bpf/bpf-test-ebpf/build.rs new file mode 100644 index 00000000..50f21fd8 --- /dev/null +++ b/tests/bpf/bpf-test-ebpf/build.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use which::which; + +/// Building this crate has an undeclared dependency on the `bpf-linker` binary. This would be +/// better expressed by [artifact-dependencies][bindeps] but issues such as +/// https://github.com/rust-lang/cargo/issues/12385 make their use impractical for the time being. +/// +/// This file implements an imperfect solution: it causes cargo to rebuild the crate whenever the +/// mtime of `which bpf-linker` changes. Note that possibility that a new bpf-linker is added to +/// $PATH ahead of the one used as the cache key still exists. Solving this in the general case +/// would require rebuild-if-changed-env=PATH *and* rebuild-if-changed={every-directory-in-PATH} +/// which would likely mean far too much cache invalidation. +/// +/// [bindeps]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html?highlight=feature#artifact-dependencies +fn main() { + let bpf_linker = which("bpf-linker").unwrap(); + println!("cargo:rerun-if-changed={}", bpf_linker.to_str().unwrap()); +} diff --git a/tests/bpf/bpf-test-ebpf/src/lib.rs b/tests/bpf/bpf-test-ebpf/src/lib.rs new file mode 100644 index 00000000..b6f52d03 --- /dev/null +++ b/tests/bpf/bpf-test-ebpf/src/lib.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#![no_std] + +// This file exists to enable the library target. diff --git a/tests/bpf/bpf-test-ebpf/src/main.rs b/tests/bpf/bpf-test-ebpf/src/main.rs new file mode 100644 index 00000000..bac96baf --- /dev/null +++ b/tests/bpf/bpf-test-ebpf/src/main.rs @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#![no_std] +#![no_main] + +use aya_ebpf::{bindings::xdp_action, macros::xdp, programs::XdpContext}; +use aya_log_ebpf::info; + +#[xdp] +pub fn bpf_test(ctx: XdpContext) -> u32 { + match try_bpf_test(ctx) { + Ok(ret) => ret, + Err(_) => xdp_action::XDP_ABORTED, + } +} + +fn try_bpf_test(ctx: XdpContext) -> Result { + info!(&ctx, "received a packet"); + run(ctx)?; + Ok(xdp_action::XDP_PASS) +} + +#[cfg(not(test))] +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[unsafe(link_section = "license")] +#[unsafe(no_mangle)] +static LICENSE: [u8; 13] = *b"Dual MIT/GPL\0"; + +use core::{ + mem::{self, MaybeUninit}, + sync::atomic::Ordering, +}; + +use atomic_maybe_uninit::*; + +fn run(ctx: XdpContext) -> Result { + macro_rules! print { + ($($tt:tt)*) => {{ + info!(&ctx, $($tt)*); + }}; + } + macro_rules! println { + ($($tt:tt)*) => {{ + info!(&ctx, $($tt)*); + }}; + } + macro_rules! assert_eq { + ($a:expr, $b:expr $(,)?) => {{ + let a = $a; + let b = $b; + if a != b { + info!(&ctx, "assertion failed: left={},right={}", a, b); + return Err(xdp_action::XDP_ABORTED); + } + }}; + } + + macro_rules! __test_atomic_load_store { + ($ty:ident) => { + // load_store + { + unsafe { + // static VAR: AtomicMaybeUninit<$ty> = + // AtomicMaybeUninit::new(MaybeUninit::new(10)); + for (load_order, store_order) in LOAD_ORDERINGS.into_iter().zip(STORE_ORDERINGS) + { + // TODO: the call stack of 9 frames is too deep + // assert_eq!(VAR.load(load_order).assume_init(), 10); + // VAR.store(MaybeUninit::new(5), store_order); + // assert_eq!(VAR.load(load_order).assume_init(), 5); + // VAR.store(MaybeUninit::uninit(), store_order); + // let _v = VAR.load(load_order); + // VAR.store(MaybeUninit::new(10), store_order); + + let a = AtomicMaybeUninit::<$ty>::new(MaybeUninit::new(1)); + assert_eq!(a.load(load_order).assume_init(), 1); + a.store(MaybeUninit::new($ty::MIN), store_order); + // TODO: assertion failed: left=-2147483648,right=-2147483648 + if mem::size_of::<$ty>() != 4 { + assert_eq!(a.load(load_order).assume_init(), $ty::MIN); + } + a.store(MaybeUninit::new($ty::MAX), store_order); + // TODO: assertion failed: left=4294967295,right=4294967295 + if mem::size_of::<$ty>() != 4 { + assert_eq!(a.load(load_order).assume_init(), $ty::MAX); + } + let a = AtomicMaybeUninit::<$ty>::new(MaybeUninit::uninit()); + let _v = a.load(load_order); + a.store(MaybeUninit::new(2), store_order); + assert_eq!(a.load(load_order).assume_init(), 2); + a.store(MaybeUninit::uninit(), store_order); + let _v = a.load(load_order); + } + } + } + }; + } + macro_rules! __test_atomic { + ($ty:ident) => { + __test_atomic_load_store!($ty); + // swap + { + unsafe { + for order in SWAP_ORDERINGS { + let a = AtomicMaybeUninit::<$ty>::new(MaybeUninit::new(5)); + // TODO: assertion failed: left=-2147483648,right=-2147483648 + if mem::size_of::<$ty>() != 4 { + assert_eq!(a.swap(MaybeUninit::new($ty::MIN), order).assume_init(), 5); + assert_eq!( + a.swap(MaybeUninit::new($ty::MAX), order).assume_init(), + $ty::MIN + ); + assert_eq!(a.swap(MaybeUninit::new(10), order).assume_init(), $ty::MAX); + } else { + assert_eq!(a.swap(MaybeUninit::new(10), order).assume_init(), 5); + } + assert_eq!(a.swap(MaybeUninit::uninit(), order).assume_init(), 10); + let _v = a.swap(MaybeUninit::new(15), order); + let a = AtomicMaybeUninit::<$ty>::new(MaybeUninit::uninit()); + let _v = a.swap(MaybeUninit::new(10), order); + assert_eq!(a.swap(MaybeUninit::uninit(), order).assume_init(), 10); + } + } + } + // compare_exchange + { + unsafe { + let (success, failure) = COMPARE_EXCHANGE_ORDERINGS[14]; + let a = AtomicMaybeUninit::<$ty>::new(MaybeUninit::new(5)); + assert_eq!( + a.compare_exchange( + MaybeUninit::new(5), + MaybeUninit::new(10), + success, + failure + ) + .unwrap_unchecked() + .assume_init(), + 5 + ); + assert_eq!(a.load(Ordering::Relaxed).assume_init(), 10); + assert_eq!( + a.compare_exchange( + MaybeUninit::new(6), + MaybeUninit::new(12), + success, + failure + ) + .unwrap_err_unchecked() + .assume_init(), + 10 + ); + assert_eq!(a.load(Ordering::Relaxed).assume_init(), 10); + } + } + // compare_exchange_weak + { + unsafe { + for (success, failure) in COMPARE_EXCHANGE_ORDERINGS { + let a = AtomicMaybeUninit::<$ty>::new(MaybeUninit::new(4)); + assert_eq!( + a.compare_exchange_weak( + MaybeUninit::new(6), + MaybeUninit::new(8), + success, + failure + ) + .unwrap_err_unchecked() + .assume_init(), + 4 + ); + let mut old = a.load(Ordering::Relaxed); + // TODO: infinite loop detected at insn 649 + // loop { + // let new = MaybeUninit::new(old.assume_init() * 2); + // match a.compare_exchange_weak(old, new, success, failure) { + // Ok(_) => break, + // Err(x) => old = x, + // } + // } + // assert_eq!(a.load(Ordering::Relaxed).assume_init(), 8); + } + } + } + // fetch_update + { + unsafe { + for (success, failure) in COMPARE_EXCHANGE_ORDERINGS { + let a = AtomicMaybeUninit::<$ty>::new(MaybeUninit::new(7)); + assert_eq!( + a.fetch_update(success, failure, |_| None) + .unwrap_err_unchecked() + .assume_init(), + 7 + ); + // TODO: R3 !read_ok + // assert_eq!( + // a.fetch_update(success, failure, |x| Some(MaybeUninit::new( + // x.assume_init() + 1 + // ))) + // .unwrap_unchecked() + // .assume_init(), + // 7 + // ); + // assert_eq!( + // a.fetch_update(success, failure, |x| Some(MaybeUninit::new( + // x.assume_init() + 1 + // ))) + // .unwrap_unchecked() + // .assume_init(), + // 8 + // ); + // assert_eq!(a.load(Ordering::Relaxed).assume_init(), 9); + } + } + } + }; + } + + macro_rules! test_atomic_load_store { + ($ty:ident) => { + paste::paste! { + print!("test test_atomic_{} ... ", stringify!($ty)); + { + __test_atomic_load_store!($ty); + } + println!("ok"); + } + }; + } + macro_rules! test_atomic { + ($ty:ident) => { + paste::paste! { + print!("test test_atomic_{} ... ", stringify!($ty)); + { + __test_atomic!($ty); + } + println!("ok"); + } + }; + } + + #[cfg(feature = "default")] + test_atomic!(isize); + #[cfg(feature = "default")] + test_atomic!(usize); + #[cfg(target_feature = "alu32")] + { + // test_atomic_load_store!(i8); + // test_atomic_load_store!(u8); + // test_atomic_load_store!(i16); + // test_atomic_load_store!(u16); + #[cfg(feature = "i32")] + test_atomic!(i32); + #[cfg(feature = "u32")] + test_atomic!(u32); + } + #[cfg(feature = "default")] + test_atomic!(i64); + #[cfg(feature = "default")] + test_atomic!(u64); + + info!(&ctx, "Tests finished successfully"); + Ok(xdp_action::XDP_PASS) +} + +const LOAD_ORDERINGS: [Ordering; 3] = [Ordering::Relaxed, Ordering::Acquire, Ordering::SeqCst]; +const STORE_ORDERINGS: [Ordering; 3] = [Ordering::Relaxed, Ordering::Release, Ordering::SeqCst]; +const SWAP_ORDERINGS: [Ordering; 5] = + [Ordering::Relaxed, Ordering::Release, Ordering::Acquire, Ordering::AcqRel, Ordering::SeqCst]; +const COMPARE_EXCHANGE_ORDERINGS: [(Ordering, Ordering); 15] = [ + (Ordering::Relaxed, Ordering::Relaxed), + (Ordering::Relaxed, Ordering::Acquire), + (Ordering::Relaxed, Ordering::SeqCst), + (Ordering::Acquire, Ordering::Relaxed), + (Ordering::Acquire, Ordering::Acquire), + (Ordering::Acquire, Ordering::SeqCst), + (Ordering::Release, Ordering::Relaxed), + (Ordering::Release, Ordering::Acquire), + (Ordering::Release, Ordering::SeqCst), + (Ordering::AcqRel, Ordering::Relaxed), + (Ordering::AcqRel, Ordering::Acquire), + (Ordering::AcqRel, Ordering::SeqCst), + (Ordering::SeqCst, Ordering::Relaxed), + (Ordering::SeqCst, Ordering::Acquire), + (Ordering::SeqCst, Ordering::SeqCst), +]; diff --git a/tests/bpf/bpf-test/Cargo.toml b/tests/bpf/bpf-test/Cargo.toml new file mode 100644 index 00000000..27812fce --- /dev/null +++ b/tests/bpf/bpf-test/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "bpf-test" +version = "0.1.0" +edition.workspace = true + +license.workspace = true + +[features] +default = [] +i32 = [] +u32 = [] + +[dependencies] +bpf-test-common = { path = "../bpf-test-common", features = ["user"] } + +anyhow = { workspace = true, default-features = true } +aya = { workspace = true } +aya-log = { workspace = true } +env_logger = { workspace = true } +libc = { workspace = true } +log = { workspace = true } +tokio = { workspace = true, features = [ + "macros", + "rt", + "rt-multi-thread", + "net", + "signal", +] } +clap = { workspace = true, features = ["derive"] } +[build-dependencies] +anyhow = { workspace = true } +aya-build = { workspace = true } +# TODO(https://github.com/rust-lang/cargo/issues/12375): this should be an artifact dependency, but +# it's not possible to tell cargo to use `-Z build-std` to build it. We cargo-in-cargo in the build +# script to build this, but we want to teach cargo about the dependency so that cache invalidation +# works properly. +# +# Note also that https://github.com/rust-lang/cargo/issues/10593 occurs when `target = ...` is added +# to an artifact dependency; it seems possible to work around that by setting `resolver = "1"` in +# Cargo.toml in the workspace root. +# +# Finally note that *any* usage of `artifact = ...` in *any* Cargo.toml in the workspace breaks +# workflows with stable cargo; stable cargo outright refuses to load manifests that use unstable +# features. +bpf-test-ebpf = { path = "../bpf-test-ebpf" } + +[[bin]] +name = "bpf-test" +path = "src/main.rs" diff --git a/tests/bpf/bpf-test/build.rs b/tests/bpf/bpf-test/build.rs new file mode 100644 index 00000000..2c1471b6 --- /dev/null +++ b/tests/bpf/bpf-test/build.rs @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use anyhow::{Context as _, anyhow}; +use aya_build::{Toolchain, cargo_metadata}; + +fn main() -> anyhow::Result<()> { + let cargo_metadata::Metadata { packages, .. } = + cargo_metadata::MetadataCommand::new().no_deps().exec().context("MetadataCommand::exec")?; + let ebpf_package = packages + .into_iter() + .find(|cargo_metadata::Package { name, .. }| name.as_str() == "bpf-test-ebpf") + .ok_or_else(|| anyhow!("bpf-test-ebpf package not found"))?; + let no_default_features = !cfg!(feature = "default"); + let features = match (cfg!(feature = "i32"), cfg!(feature = "u32")) { + (true, false) => &["i32"][..], + (false, true) => &["u32"], + (false, false) => &[], + _ => unreachable!(), + }; + aya_build::build_ebpf([ebpf_package], Toolchain::default(), features, no_default_features) +} diff --git a/tests/bpf/bpf-test/src/main.rs b/tests/bpf/bpf-test/src/main.rs new file mode 100644 index 00000000..d7960d72 --- /dev/null +++ b/tests/bpf/bpf-test/src/main.rs @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use anyhow::Context as _; +use aya::programs::{Xdp, XdpFlags}; +use clap::Parser; +#[rustfmt::skip] +use log::{debug, warn}; +use tokio::signal; + +#[derive(Debug, Parser)] +struct Opt { + #[clap(short, long, default_value = "eth0")] + iface: String, +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let opt = Opt::parse(); + + env_logger::builder().filter(Some("bpf_test"), log::LevelFilter::Info).init(); + + // Bump the memlock rlimit. This is needed for older kernels that don't use the + // new memcg based accounting, see https://lwn.net/Articles/837122/ + let rlim = libc::rlimit { rlim_cur: libc::RLIM_INFINITY, rlim_max: libc::RLIM_INFINITY }; + let ret = unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlim) }; + if ret != 0 { + debug!("remove limit on locked memory failed, ret is: {ret}"); + } + + // This will include your eBPF object file as raw bytes at compile-time and load it at + // runtime. This approach is recommended for most real-world use cases. If you would + // like to specify the eBPF program at runtime rather than at compile-time, you can + // reach for `Bpf::load_file` instead. + let mut ebpf = + aya::Ebpf::load(aya::include_bytes_aligned!(concat!(env!("OUT_DIR"), "/bpf-test")))?; + match aya_log::EbpfLogger::init(&mut ebpf) { + Err(e) => { + // This can happen if you remove all log statements from your eBPF program. + warn!("failed to initialize eBPF logger: {e}"); + } + Ok(logger) => { + let mut logger = + tokio::io::unix::AsyncFd::with_interest(logger, tokio::io::Interest::READABLE)?; + tokio::task::spawn(async move { + loop { + let mut guard = logger.readable_mut().await.unwrap(); + guard.get_inner_mut().flush(); + guard.clear_ready(); + } + }); + } + } + let Opt { iface } = opt; + let program: &mut Xdp = ebpf.program_mut("bpf_test").unwrap().try_into()?; + program.load()?; + program.attach(&iface, XdpFlags::default()) + .context("failed to attach the XDP program with default flags - try changing XdpFlags::default() to XdpFlags::SKB_MODE")?; + + let ctrl_c = signal::ctrl_c(); + println!("Waiting for Ctrl-C..."); + assert!( + std::process::Command::new("curl") + .args(["-fsS", "-o", "/dev/null", "https://example.com"]) + .status() + .unwrap() + .success() + ); + ctrl_c.await?; + println!("Exiting..."); + + Ok(()) +} diff --git a/tools/build.sh b/tools/build.sh index 9bd65212..fb615299 100755 --- a/tools/build.sh +++ b/tools/build.sh @@ -75,6 +75,11 @@ default_targets=( # rustc -Z unstable-options --print all-target-specs-json | jq -r '. | to_entries[] | if .value.arch == "avr" then .key else empty end' avr-none + # bpf + # rustc -Z unstable-options --print all-target-specs-json | jq -r '. | to_entries[] | if .value.arch == "bpf" then .key else empty end' + bpfeb-unknown-none + bpfel-unknown-none + # csky # rustc -Z unstable-options --print all-target-specs-json | jq -r '. | to_entries[] | if .value.arch == "csky" then .key else empty end' csky-unknown-linux-gnuabiv2 @@ -535,6 +540,11 @@ build() { RUSTFLAGS="${target_rustflags} -C target-feature=+rmw --cfg atomic_maybe_uninit_target_feature=\"lowbytefirst\"" \ x_cargo "${args[@]}" "$@" ;; + bpf*) + CARGO_TARGET_DIR="${target_dir}/alu32" \ + RUSTFLAGS="${target_rustflags} -C target-feature=+alu32" \ + x_cargo "${args[@]}" "$@" + ;; csky-unknown-linux-gnuabiv2) CARGO_TARGET_DIR="${target_dir}/ck860" \ RUSTFLAGS="${target_rustflags} -C target-cpu=ck860" \ diff --git a/tools/no-std.sh b/tools/no-std.sh index 523b156b..957971f3 100755 --- a/tools/no-std.sh +++ b/tools/no-std.sh @@ -57,6 +57,9 @@ default_targets=( # m68k m68k-unknown-linux-gnu + + # bpf + bpfel-unknown-none ) x() { @@ -216,11 +219,18 @@ run() { fi ;; esac - local args=("${target_flags[@]}") + local args=() + case "${target}" in + bpf*) ;; + *) args+=("${target_flags[@]}") ;; + esac if grep -Eq "^${target}$" <<<"${rustup_target_list}"; then retry rustup ${pre_args[@]+"${pre_args[@]}"} target add "${target}" &>/dev/null elif [[ -n "${nightly}" ]]; then - args+=(-Z build-std="core") + case "${target}" in + bpf*) ;; + *) args+=(-Z build-std="core") ;; + esac else info "target '${target}' requires nightly compiler (skipped)" return 0 @@ -326,18 +336,34 @@ run() { fi test_dir=tests/no-std-linux ;; + bpf*) + case "${commit_date}" in + 2023-08-23) + # bpf-test uses 2024 edition + printf '%s\n' "target '${target}' is not supported on this version (skipped)" + return 0 + ;; + esac + test_dir=tests/bpf + args+=(--config "target.\"cfg(all())\".runner=\"${workspace_dir}/tools/runner.sh sudo ${target}\"") + ;; *) bail "unrecognized target '${target}'" ;; esac case "${target}" in - m68k*) ;; + m68k* | bpf*) ;; *) args+=(--all-features) ;; esac ( cd -- "${test_dir}" - CARGO_TARGET_DIR="${target_dir}/no-std-test" \ - RUSTFLAGS="${target_rustflags}" \ - x_cargo "${args[@]}" "$@" + case "${target}" in + bpf*) ;; + *) + CARGO_TARGET_DIR="${target_dir}/no-std-test" \ + RUSTFLAGS="${target_rustflags}" \ + x_cargo "${args[@]}" "$@" + ;; + esac CARGO_TARGET_DIR="${target_dir}/no-std-test" \ RUSTFLAGS="${target_rustflags}" \ x_cargo "${args[@]}" --release "$@" @@ -500,6 +526,19 @@ run() { RUSTFLAGS="${target_rustflags}" \ x_cargo "${args[@]}" --no-default-features --features "${feature}" --release "$@" ;; + bpf*) + # Note: We cannot test everything at once due to size. + # isize, usize, i64, u64 are covered by the run with the default feature. + # NB: Sync feature list with tests/bpf/Cargo.toml + for feature in i32 u32; do + # CARGO_TARGET_DIR="${target_dir}/no-std-test" \ + # RUSTFLAGS="${target_rustflags}" \ + # x_cargo "${args[@]}" --no-default-features --features "${feature}" "$@" + CARGO_TARGET_DIR="${target_dir}/no-std-test" \ + RUSTFLAGS="${target_rustflags}" \ + x_cargo "${args[@]}" --no-default-features --features "${feature}" --release "$@" + done + ;; esac ) } diff --git a/tools/runner.sh b/tools/runner.sh index b38829d0..ae8964aa 100755 --- a/tools/runner.sh +++ b/tools/runner.sh @@ -102,6 +102,17 @@ EOF *) bail "unrecognized runner '${runner}'" ;; esac ;; + bpf*) + case "${runner}" in + sudo) + start_simulator() { + # shellcheck disable=SC2024 + sudo -E "${bin}" &>>"${stdout}" & + } + ;; + *) bail "unrecognized runner '${runner}'" ;; + esac + ;; *) bail "unrecognized target '${target}'" ;; esac