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