diff options
Diffstat (limited to 'tools/include')
32 files changed, 993 insertions, 124 deletions
diff --git a/tools/include/asm-generic/io.h b/tools/include/asm-generic/io.h index e5a0b07ad452..0d89decdafb8 100644 --- a/tools/include/asm-generic/io.h +++ b/tools/include/asm-generic/io.h @@ -479,4 +479,32 @@ static inline void writesq(volatile void __iomem *addr, const void *buffer, } #endif +/* + * ioread/iowrite for big-endian MMIO registers. + */ + +#ifndef ioread32be +#define ioread32be ioread32be +static inline u32 ioread32be(const volatile void __iomem *addr) +{ + return bswap_32(readl(addr)); +} +#endif + +#ifndef iowrite32be +#define iowrite32be iowrite32be +static inline void iowrite32be(u32 value, volatile void __iomem *addr) +{ + writel(bswap_32(value), addr); +} +#endif + +#ifndef iowrite64be +#define iowrite64be iowrite64be +static inline void iowrite64be(u64 value, volatile void __iomem *addr) +{ + writeq(bswap_64(value), addr); +} +#endif + #endif /* _TOOLS_ASM_GENERIC_IO_H */ diff --git a/tools/include/asm/barrier.h b/tools/include/asm/barrier.h index 0c21678ac5e6..e7e0c7de5a2f 100644 --- a/tools/include/asm/barrier.h +++ b/tools/include/asm/barrier.h @@ -47,6 +47,14 @@ # define smp_mb() mb() #endif +#ifndef dma_rmb +# define dma_rmb() rmb() +#endif + +#ifndef dma_wmb +# define dma_wmb() wmb() +#endif + #ifndef smp_store_release # define smp_store_release(p, v) \ do { \ diff --git a/tools/include/linux/bitfield-fix-width.h b/tools/include/linux/bitfield-fix-width.h new file mode 100644 index 000000000000..8c2a0fa224e9 --- /dev/null +++ b/tools/include/linux/bitfield-fix-width.h @@ -0,0 +1,236 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _LINUX_BITFIELD_FIX_WIDTH_H +#define _LINUX_BITFIELD_FIX_WIDTH_H + +#include <linux/compiler.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <asm/byteorder.h> + +extern void __compiletime_error("value doesn't fit into mask") +__field_overflow(void); + +extern void __compiletime_error("bad bitfield mask") +__bad_mask(void); + +static __always_inline +u64 field_multiplier(u64 field) +{ + if ((field | (field - 1)) & ((field | (field - 1)) + 1)) + __bad_mask(); + return field & -field; +} + +static __always_inline +u64 field_mask(u64 field) +{ + return field / field_multiplier(field); +} + +#define field_max(field) ((typeof(field))field_mask(field)) + +#define __assert_field(v, field) \ + do { \ + if (__builtin_constant_p(v) && \ + ((v) & ~field_mask(field))) \ + __field_overflow(); \ + } while (0) + +static __always_inline +__u8 u8_encode_bits(u8 v, u8 field) +{ + __assert_field(v, field); + return (v & field_mask(field)) * field_multiplier(field); +} + +static __always_inline +__u8 u8_replace_bits(__u8 old, u8 val, u8 field) +{ + return (old & ~field) | u8_encode_bits(val, field); +} + +static __always_inline +void u8p_replace_bits(__u8 *p, u8 val, u8 field) +{ + *p = (*p & ~field) | u8_encode_bits(val, field); +} + +static __always_inline +u8 u8_get_bits(__u8 v, u8 field) +{ + return (v & field) / field_multiplier(field); +} + +static __always_inline +__le16 le16_encode_bits(u16 v, u16 field) +{ + __assert_field(v, field); + return cpu_to_le16((v & field_mask(field)) * field_multiplier(field)); +} + +static __always_inline +void le16p_replace_bits(__le16 *p, u16 val, u16 field) +{ + *p = (*p & ~cpu_to_le16(field)) | le16_encode_bits(val, field); +} + +static __always_inline +u16 le16_get_bits(__le16 v, u16 field) +{ + return (le16_to_cpu(v) & field) / field_multiplier(field); +} + +static __always_inline +__be16 be16_encode_bits(u16 v, u16 field) +{ + __assert_field(v, field); + return cpu_to_be16((v & field_mask(field)) * field_multiplier(field)); +} + +static __always_inline +u16 be16_get_bits(__be16 v, u16 field) +{ + return (be16_to_cpu(v) & field) / field_multiplier(field); +} + +static __always_inline +__u16 u16_encode_bits(u16 v, u16 field) +{ + __assert_field(v, field); + return (v & field_mask(field)) * field_multiplier(field); +} + +static __always_inline +__u16 u16_replace_bits(__u16 old, u16 val, u16 field) +{ + return (old & ~field) | u16_encode_bits(val, field); +} + +static __always_inline +void u16p_replace_bits(__u16 *p, u16 val, u16 field) +{ + *p = (*p & ~field) | u16_encode_bits(val, field); +} + +static __always_inline +u16 u16_get_bits(__u16 v, u16 field) +{ + return (v & field) / field_multiplier(field); +} + +static __always_inline +__le32 le32_encode_bits(u32 v, u32 field) +{ + __assert_field(v, field); + return cpu_to_le32((v & field_mask(field)) * field_multiplier(field)); +} + +static __always_inline +void le32p_replace_bits(__le32 *p, u32 val, u32 field) +{ + *p = (*p & ~cpu_to_le32(field)) | le32_encode_bits(val, field); +} + +static __always_inline +u32 le32_get_bits(__le32 v, u32 field) +{ + return (le32_to_cpu(v) & field) / field_multiplier(field); +} + +static __always_inline +__be32 be32_encode_bits(u32 v, u32 field) +{ + __assert_field(v, field); + return cpu_to_be32((v & field_mask(field)) * field_multiplier(field)); +} + +static __always_inline +void be32p_replace_bits(__be32 *p, u32 val, u32 field) +{ + *p = (*p & ~cpu_to_be32(field)) | be32_encode_bits(val, field); +} + +static __always_inline +u32 be32_get_bits(__be32 v, u32 field) +{ + return (be32_to_cpu(v) & field) / field_multiplier(field); +} + +static __always_inline +__u32 u32_encode_bits(u32 v, u32 field) +{ + __assert_field(v, field); + return (v & field_mask(field)) * field_multiplier(field); +} + +static __always_inline +__u32 u32_replace_bits(__u32 old, u32 val, u32 field) +{ + return (old & ~field) | u32_encode_bits(val, field); +} + +static __always_inline +void u32p_replace_bits(__u32 *p, u32 val, u32 field) +{ + *p = (*p & ~field) | u32_encode_bits(val, field); +} + +static __always_inline +u32 u32_get_bits(__u32 v, u32 field) +{ + return (v & field) / field_multiplier(field); +} + +static __always_inline +__le64 le64_encode_bits(u64 v, u64 field) +{ + __assert_field(v, field); + return cpu_to_le64((v & field_mask(field)) * field_multiplier(field)); +} + +static __always_inline +u64 le64_get_bits(__le64 v, u64 field) +{ + return (le64_to_cpu(v) & field) / field_multiplier(field); +} + +static __always_inline +__be64 be64_encode_bits(u64 v, u64 field) +{ + __assert_field(v, field); + return cpu_to_be64((v & field_mask(field)) * field_multiplier(field)); +} + +static __always_inline +u64 be64_get_bits(__be64 v, u64 field) +{ + return (be64_to_cpu(v) & field) / field_multiplier(field); +} + +static __always_inline +__u64 u64_encode_bits(u64 v, u64 field) +{ + __assert_field(v, field); + return (v & field_mask(field)) * field_multiplier(field); +} + +static __always_inline +__u64 u64_replace_bits(__u64 old, u64 val, u64 field) +{ + return (old & ~field) | u64_encode_bits(val, field); +} + +static __always_inline +void u64p_replace_bits(__u64 *p, u64 val, u64 field) +{ + *p = (*p & ~field) | u64_encode_bits(val, field); +} + +static __always_inline +u64 u64_get_bits(__u64 v, u64 field) +{ + return (v & field) / field_multiplier(field); +} + +#endif /* _LINUX_BITFIELD_FIX_WIDTH_H */ diff --git a/tools/include/linux/bitfield.h b/tools/include/linux/bitfield.h index ddf81f24956b..4508cefabec9 100644 --- a/tools/include/linux/bitfield.h +++ b/tools/include/linux/bitfield.h @@ -9,7 +9,7 @@ #include <linux/build_bug.h> #include <linux/kernel.h> -#include <asm/byteorder.h> +#include <linux/bitfield-fix-width.h> /* * Bitfield access macros @@ -127,51 +127,4 @@ (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \ }) -extern void __compiletime_error("value doesn't fit into mask") -__field_overflow(void); -extern void __compiletime_error("bad bitfield mask") -__bad_mask(void); -static __always_inline u64 field_multiplier(u64 field) -{ - if ((field | (field - 1)) & ((field | (field - 1)) + 1)) - __bad_mask(); - return field & -field; -} -static __always_inline u64 field_mask(u64 field) -{ - return field / field_multiplier(field); -} -#define field_max(field) ((typeof(field))field_mask(field)) -#define ____MAKE_OP(type,base,to,from) \ -static __always_inline __##type type##_encode_bits(base v, base field) \ -{ \ - if (__builtin_constant_p(v) && (v & ~field_mask(field))) \ - __field_overflow(); \ - return to((v & field_mask(field)) * field_multiplier(field)); \ -} \ -static __always_inline __##type type##_replace_bits(__##type old, \ - base val, base field) \ -{ \ - return (old & ~to(field)) | type##_encode_bits(val, field); \ -} \ -static __always_inline void type##p_replace_bits(__##type *p, \ - base val, base field) \ -{ \ - *p = (*p & ~to(field)) | type##_encode_bits(val, field); \ -} \ -static __always_inline base type##_get_bits(__##type v, base field) \ -{ \ - return (from(v) & field)/field_multiplier(field); \ -} -#define __MAKE_OP(size) \ - ____MAKE_OP(le##size,u##size,cpu_to_le##size,le##size##_to_cpu) \ - ____MAKE_OP(be##size,u##size,cpu_to_be##size,be##size##_to_cpu) \ - ____MAKE_OP(u##size,u##size,,) -____MAKE_OP(u8,u8,,) -__MAKE_OP(16) -__MAKE_OP(32) -__MAKE_OP(64) -#undef __MAKE_OP -#undef ____MAKE_OP - #endif diff --git a/tools/include/linux/compiler.h b/tools/include/linux/compiler.h index f2f54b038168..03ecf9086643 100644 --- a/tools/include/linux/compiler.h +++ b/tools/include/linux/compiler.h @@ -73,6 +73,11 @@ # define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) #endif +/* Enforce static storage duration. */ +#define ASSERT_STATIC_STORAGE(name) \ + static typeof(name) * const __always_unused \ + name##_storage_check = &(name) + /* * This returns a constant expression while determining if an argument is * a constant expression, most importantly without evaluating the argument. diff --git a/tools/include/linux/mm.h b/tools/include/linux/mm.h index 84b5954f66c3..bce7616cd7c4 100644 --- a/tools/include/linux/mm.h +++ b/tools/include/linux/mm.h @@ -30,11 +30,7 @@ static inline void *phys_to_virt(unsigned long address) #define virt_to_phys virt_to_phys static inline phys_addr_t virt_to_phys(volatile void *address) { - return (phys_addr_t)address; -} - -static inline void totalram_pages_inc(void) -{ + return __pa(address); } static inline void totalram_pages_add(long count) diff --git a/tools/include/linux/stddef.h b/tools/include/linux/stddef.h new file mode 100644 index 000000000000..99182ea4a141 --- /dev/null +++ b/tools/include/linux/stddef.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _TOOLS_LINUX_STDDEF_H +#define _TOOLS_LINUX_STDDEF_H + +#include_next <linux/stddef.h> + +#define DECLARE_FLEX_ARRAY(TYPE, NAME) \ + __DECLARE_FLEX_ARRAY(TYPE, NAME) + +#endif /* _TOOLS_LINUX_STDDEF_H */ diff --git a/tools/include/nolibc/Makefile b/tools/include/nolibc/Makefile index 6d213d372bf8..0880be0f6ef5 100644 --- a/tools/include/nolibc/Makefile +++ b/tools/include/nolibc/Makefile @@ -17,7 +17,23 @@ endif # it defaults to this nolibc directory. OUTPUT ?= $(CURDIR)/ -architectures := alpha arm arm64 loongarch m68k mips openrisc parisc powerpc riscv s390 sh sparc x86 +architectures := \ + alpha \ + arm \ + arm64 \ + hexagon \ + loongarch \ + m68k \ + mips \ + openrisc \ + parisc \ + powerpc \ + riscv \ + s390 \ + sh \ + sparc \ + x86 \ + arch_files := arch.h $(addsuffix .h, $(addprefix arch-, $(architectures))) all_files := \ alloca.h \ @@ -59,6 +75,7 @@ all_files := \ sys/reboot.h \ sys/resource.h \ sys/select.h \ + sys/sendfile.h \ sys/stat.h \ sys/syscall.h \ sys/sysmacros.h \ diff --git a/tools/include/nolibc/arch-arm.h b/tools/include/nolibc/arch-arm.h index b88686fb19c4..b5b3456c75cb 100644 --- a/tools/include/nolibc/arch-arm.h +++ b/tools/include/nolibc/arch-arm.h @@ -196,11 +196,6 @@ void __attribute__((weak, noreturn)) __nolibc_entrypoint __nolibc_no_stack_prote } #endif /* NOLIBC_NO_RUNTIME */ -static __attribute__((unused)) -int _sys_ftruncate64(int fd, uint32_t length0, uint32_t length1) -{ - return __nolibc_syscall4(__NR_ftruncate64, fd, 0, length0, length1); -} -#define _sys_ftruncate64 _sys_ftruncate64 +#define __NOLIBC_PAD_64BIT_SYSCALL_ARGUMENT_PAIR #endif /* _NOLIBC_ARCH_ARM_H */ diff --git a/tools/include/nolibc/arch-hexagon.h b/tools/include/nolibc/arch-hexagon.h new file mode 100644 index 000000000000..68f73529a6dd --- /dev/null +++ b/tools/include/nolibc/arch-hexagon.h @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: LGPL-2.1 OR MIT */ +/* + * hexagon specific definitions for NOLIBC + * Copyright (C) 2026 Thomas Weißschuh <linux@weissschuh.net> + */ + +#ifndef _NOLIBC_ARCH_HEXAGON_H +#define _NOLIBC_ARCH_HEXAGON_H + +#include <linux/unistd.h> + +#include "compiler.h" +#include "crt.h" + +/* + * Syscalls for hexagon: + * - syscall number is passed in r6 + * - arguments are in r0, r1, r2, r3, r4, r5 + * - the system call is performed by calling trap0(#1) + * - syscall return value is in r0 + */ + +#define _NOLIBC_SYSCALL_CLOBBERLIST "memory" + +#define __nolibc_syscall0(num) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0"); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "=r"(_arg1) \ + : "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#define __nolibc_syscall1(num, arg1) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0") = (long)(arg1); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "+r"(_arg1) \ + : "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#define __nolibc_syscall2(num, arg1, arg2) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0") = (long)(arg1); \ + register long _arg2 __asm__ ("r1") = (long)(arg2); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "+r"(_arg1) \ + : "r"(_arg2), \ + "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#define __nolibc_syscall3(num, arg1, arg2, arg3) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0") = (long)(arg1); \ + register long _arg2 __asm__ ("r1") = (long)(arg2); \ + register long _arg3 __asm__ ("r2") = (long)(arg3); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "+r"(_arg1) \ + : "r"(_arg2), "r"(_arg3), \ + "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#define __nolibc_syscall4(num, arg1, arg2, arg3, arg4) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0") = (long)(arg1); \ + register long _arg2 __asm__ ("r1") = (long)(arg2); \ + register long _arg3 __asm__ ("r2") = (long)(arg3); \ + register long _arg4 __asm__ ("r3") = (long)(arg4); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "+r"(_arg1) \ + : "r"(_arg2), "r"(_arg3), "r"(_arg4), \ + "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#define __nolibc_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0") = (long)(arg1); \ + register long _arg2 __asm__ ("r1") = (long)(arg2); \ + register long _arg3 __asm__ ("r2") = (long)(arg3); \ + register long _arg4 __asm__ ("r3") = (long)(arg4); \ + register long _arg5 __asm__ ("r4") = (long)(arg5); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "+r"(_arg1) \ + : "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ + "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#define __nolibc_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0") = (long)(arg1); \ + register long _arg2 __asm__ ("r1") = (long)(arg2); \ + register long _arg3 __asm__ ("r2") = (long)(arg3); \ + register long _arg4 __asm__ ("r3") = (long)(arg4); \ + register long _arg5 __asm__ ("r4") = (long)(arg5); \ + register long _arg6 __asm__ ("r5") = (long)(arg6); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "+r"(_arg1) \ + : "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), "r"(_arg6), \ + "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#ifndef NOLIBC_NO_RUNTIME +/* startup code */ +void __attribute__((weak, noreturn)) +__nolibc_entrypoint __nolibc_no_stack_protector +_start(void) +{ + __asm__ volatile ( + "r0 = sp\n" /* save stack pointer to r0, as arg1 of _start_c */ + "call _start_c\n" /* transfer to c runtime */ + ); + __nolibc_entrypoint_epilogue(); +} +#endif /* NOLIBC_NO_RUNTIME */ + +#define __NOLIBC_PAD_64BIT_SYSCALL_ARGUMENT_PAIR + +#endif /* _NOLIBC_ARCH_HEXAGON_H */ diff --git a/tools/include/nolibc/arch-mips.h b/tools/include/nolibc/arch-mips.h index 26ad413cec62..c0da0cc309ce 100644 --- a/tools/include/nolibc/arch-mips.h +++ b/tools/include/nolibc/arch-mips.h @@ -72,6 +72,7 @@ #define _NOLIBC_SYSCALL_STACK_UNRESERVE #define _NOLIBC_SYSCALL_REG register long long +#define __NOLIBC_BITS_PER_SYSCALL_ARG 64 #endif /* _ABIO32 */ @@ -286,12 +287,7 @@ void __attribute__((weak, noreturn)) __nolibc_entrypoint __nolibc_no_stack_prote #endif /* NOLIBC_NO_RUNTIME */ #if defined(_ABIO32) -static __attribute__((unused)) -int _sys_ftruncate64(int fd, uint32_t length0, uint32_t length1) -{ - return __nolibc_syscall4(__NR_ftruncate64, fd, 0, length0, length1); -} -#define _sys_ftruncate64 _sys_ftruncate64 +#define __NOLIBC_PAD_64BIT_SYSCALL_ARGUMENT_PAIR #endif #endif /* _NOLIBC_ARCH_MIPS_H */ diff --git a/tools/include/nolibc/arch-parisc.h b/tools/include/nolibc/arch-parisc.h index 417043ecbc53..bf57ba16055c 100644 --- a/tools/include/nolibc/arch-parisc.h +++ b/tools/include/nolibc/arch-parisc.h @@ -7,9 +7,7 @@ #ifndef _NOLIBC_ARCH_PARISC_H #define _NOLIBC_ARCH_PARISC_H -#if defined(__LP64__) -#error 64-bit not supported -#endif +#include <linux/unistd.h> #include "compiler.h" #include "crt.h" @@ -167,9 +165,15 @@ void __attribute__((weak, noreturn)) __nolibc_entrypoint __nolibc_no_stack_protector _start(void) { __asm__ volatile ( +#ifdef __LP64__ + ".import __gp\n" /* Set up the dp register */ + "ldil L%__gp, %dp\n" + "ldo R%__gp(%dp), %dp\n" +#else ".import $global$\n" /* Set up the dp register */ "ldil L%$global$, %dp\n" - "ldo R%$global$(%r27), %dp\n" + "ldo R%$global$(%dp), %dp\n" +#endif "b _start_c\n" /* Call _start_c, the load below is executed first */ @@ -182,4 +186,20 @@ void __attribute__((weak, noreturn)) __nolibc_entrypoint __nolibc_no_stack_prote } #endif /* NOLIBC_NO_RUNTIME */ +#ifdef __LP64__ +/* + * This override is normally only meant for 32-bit architectures. + * But parisc 64-bit has __NR_ftruncate64, so this path is taken. + * Add an unused second argument to satisfy the calling convention. + */ +static __attribute__((unused)) +int _sys_ftruncate64(int fd, uint64_t length, uint64_t unused) +{ + (void)unused; + + return __nolibc_syscall2(__NR_ftruncate64, fd, length); +} +#define _sys_ftruncate64 _sys_ftruncate64 +#endif + #endif /* _NOLIBC_ARCH_PARISC_H */ diff --git a/tools/include/nolibc/arch-powerpc.h b/tools/include/nolibc/arch-powerpc.h index dbe2e5205aaa..a4711638cb66 100644 --- a/tools/include/nolibc/arch-powerpc.h +++ b/tools/include/nolibc/arch-powerpc.h @@ -222,12 +222,7 @@ void __attribute__((weak, noreturn)) __nolibc_entrypoint __nolibc_no_stack_prote #endif /* NOLIBC_NO_RUNTIME */ #if !defined(__powerpc64__) -static __attribute__((unused)) -int _sys_ftruncate64(int fd, uint32_t length0, uint32_t length1) -{ - return __nolibc_syscall4(__NR_ftruncate64, fd, 0, length0, length1); -} -#define _sys_ftruncate64 _sys_ftruncate64 +#define __NOLIBC_PAD_64BIT_SYSCALL_ARGUMENT_PAIR #endif #endif /* _NOLIBC_ARCH_POWERPC_H */ diff --git a/tools/include/nolibc/arch-sh.h b/tools/include/nolibc/arch-sh.h index a32378fd621f..996cfbe1bee3 100644 --- a/tools/include/nolibc/arch-sh.h +++ b/tools/include/nolibc/arch-sh.h @@ -10,6 +10,8 @@ #include "compiler.h" #include "crt.h" +#include <linux/unistd.h> + /* * Syscalls for SuperH: * - registers are 32bit wide @@ -161,4 +163,20 @@ void __attribute__((weak, noreturn)) __nolibc_entrypoint __nolibc_no_stack_prote } #endif /* NOLIBC_NO_RUNTIME */ +static __attribute__((unused)) +ssize_t _sys_pread(int fd, void *buf, size_t count, off_t offset) +{ + return __nolibc_syscall6(__NR_pread64, fd, buf, count, 0, + __NOLIBC_LLARGPART(offset, 0), __NOLIBC_LLARGPART(offset, 1)); +} +#define _sys_pread _sys_pread + +static __attribute__((unused)) +ssize_t _sys_pwrite(int fd, const void *buf, size_t count, off_t offset) +{ + return __nolibc_syscall6(__NR_pwrite64, fd, buf, count, 0, + __NOLIBC_LLARGPART(offset, 0), __NOLIBC_LLARGPART(offset, 1)); +} +#define _sys_pwrite _sys_pwrite + #endif /* _NOLIBC_ARCH_SH_H */ diff --git a/tools/include/nolibc/arch-x86.h b/tools/include/nolibc/arch-x86.h index 577e9b682eb3..0638f9316749 100644 --- a/tools/include/nolibc/arch-x86.h +++ b/tools/include/nolibc/arch-x86.h @@ -386,5 +386,7 @@ __asm__ ( ".popsection\n" ); +#define __NOLIBC_BITS_PER_SYSCALL_ARG 64 + #endif /* !defined(__x86_64__) */ #endif /* _NOLIBC_ARCH_X86_H */ diff --git a/tools/include/nolibc/arch.h b/tools/include/nolibc/arch.h index 06cc2dbe7a33..79ed5900f2a7 100644 --- a/tools/include/nolibc/arch.h +++ b/tools/include/nolibc/arch.h @@ -34,8 +34,15 @@ #include "arch-parisc.h" #elif defined(__alpha__) #include "arch-alpha.h" +#elif defined(__hexagon__) +#include "arch-hexagon.h" #else #error Unsupported Architecture #endif + +#ifndef __NOLIBC_BITS_PER_SYSCALL_ARG +#define __NOLIBC_BITS_PER_SYSCALL_ARG __BITS_PER_LONG +#endif + #endif /* _NOLIBC_ARCH_H */ diff --git a/tools/include/nolibc/compiler.h b/tools/include/nolibc/compiler.h index f2d7a81d0d7c..375c45ce81c8 100644 --- a/tools/include/nolibc/compiler.h +++ b/tools/include/nolibc/compiler.h @@ -90,4 +90,13 @@ # define __nolibc_no_sanitize_undefined #endif +/* + * Helper for 32-bit machines where a 64-bit syscall arg needs to be split into + * two 32-bit parts while making sure the order of the low/high parts are correct + * for the endianness: + * __NOLIBC_LLARGPART(x, 0), __NOLIBC_LLARGPART(x, 1) + */ +#define __NOLIBC_LLARGPART(_arg, _part) \ + (((union { long long ll; long l[2]; }) { .ll = _arg }).l[_part]) + #endif /* _NOLIBC_COMPILER_H */ diff --git a/tools/include/nolibc/dirent.h b/tools/include/nolibc/dirent.h index 4e02ef25e72d..19857369de2a 100644 --- a/tools/include/nolibc/dirent.h +++ b/tools/include/nolibc/dirent.h @@ -30,10 +30,23 @@ typedef struct { static __attribute__((unused)) DIR *fdopendir(int fd) { + struct stat buf; + int ret; + if (fd < 0) { SET_ERRNO(EBADF); return NULL; } + + ret = fstat(fd, &buf); + if (ret < 0) + return NULL; + + if (!S_ISDIR(buf.st_mode)) { + SET_ERRNO(ENOTDIR); + return NULL; + } + return (DIR *)(intptr_t)~fd; } @@ -42,10 +55,11 @@ DIR *opendir(const char *name) { int fd; - fd = open(name, O_RDONLY); + fd = open(name, O_RDONLY | O_DIRECTORY); if (fd == -1) return NULL; - return fdopendir(fd); + + return (DIR *)(intptr_t)~fd; } static __attribute__((unused)) @@ -67,6 +81,7 @@ int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) struct linux_dirent64 *ldir = (void *)buf; intptr_t i = (intptr_t)dirp; int fd, ret; + off_t off; if (i >= 0) return EBADF; @@ -86,9 +101,9 @@ int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) * readdir() can only return one entry at a time. * Make sure the non-returned ones are not skipped. */ - ret = _sys_lseek(fd, ldir->d_off, SEEK_SET); - if (ret < 0) - return -ret; + off = _sys_lseek(fd, ldir->d_off, SEEK_SET); + if (off < 0) + return -off; entry->d_ino = ldir->d_ino; /* the destination should always be big enough */ diff --git a/tools/include/nolibc/err.h b/tools/include/nolibc/err.h index e22ae87a7289..61978d537ed8 100644 --- a/tools/include/nolibc/err.h +++ b/tools/include/nolibc/err.h @@ -60,7 +60,7 @@ void verr(int eval, const char *fmt, va_list args) static __attribute__((noreturn, unused)) void verrx(int eval, const char *fmt, va_list args) { - warnx(fmt, args); + vwarnx(fmt, args); exit(eval); } diff --git a/tools/include/nolibc/nolibc.h b/tools/include/nolibc/nolibc.h index faa94f247281..352a207bbedc 100644 --- a/tools/include/nolibc/nolibc.h +++ b/tools/include/nolibc/nolibc.h @@ -106,6 +106,7 @@ #include "sys/reboot.h" #include "sys/resource.h" #include "sys/select.h" +#include "sys/sendfile.h" #include "sys/stat.h" #include "sys/syscall.h" #include "sys/sysmacros.h" diff --git a/tools/include/nolibc/stdlib.h b/tools/include/nolibc/stdlib.h index 1816c2368b68..1c5a914c0f23 100644 --- a/tools/include/nolibc/stdlib.h +++ b/tools/include/nolibc/stdlib.h @@ -130,9 +130,11 @@ void *malloc(size_t len) { struct nolibc_heap *heap; - /* Always allocate memory with size multiple of 4096. */ - len = sizeof(*heap) + len; - len = (len + 4095UL) & -4096UL; + if (__builtin_expect(__builtin_add_overflow(len, sizeof(*heap), &len), 0)) { + SET_ERRNO(ENOMEM); + return NULL; + } + heap = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); if (__builtin_expect(heap == MAP_FAILED, 0)) @@ -143,7 +145,7 @@ void *malloc(size_t len) } static __attribute__((unused)) -void *calloc(size_t size, size_t nmemb) +void *calloc(size_t nmemb, size_t size) { size_t x; @@ -230,7 +232,7 @@ int _nolibc_u64toa_base(uint64_t in, char *buffer, unsigned int base, uint64_t r /* Generate least significant digit first */ do { -#if defined(__SIZEOF_INT128__) && !defined(__mips__) && !defined(__sparc__) +#if defined(__SIZEOF_INT128__) && !defined(__mips__) && !defined(__sparc__) && !defined(__hppa__) q = ((unsigned __int128)in * recip) >> 64; #else uint64_t p = (uint32_t)in * (recip >> 32); diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h index 548f94d96ed2..b63fa6c618c5 100644 --- a/tools/include/nolibc/sys.h +++ b/tools/include/nolibc/sys.h @@ -71,16 +71,6 @@ static __inline__ int __nolibc_enosys(const char *syscall, ...) #endif -/* - * Helper for 32-bit machines where a 64-bit syscall arg needs to be split into - * two 32-bit parts while making sure the order of the low/high parts are correct - * for the endianness: - * __NOLIBC_LLARGPART(x, 0), __NOLIBC_LLARGPART(x, 1) - */ -#define __NOLIBC_LLARGPART(_arg, _part) \ - (((union { long long ll; long l[2]; }) { .ll = _arg }).l[_part]) - - /* Functions in this file only describe syscalls. They're declared static so * that the compiler usually decides to inline them while still being allowed * to pass a pointer to one of their instances. Each syscall exists in two @@ -756,6 +746,56 @@ int pivot_root(const char *new, const char *old) /* + * ssize_t pread(int fd, void *buf, size_t count, off_t offset); + * ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset); + */ + +#ifndef _sys_pread +static __attribute__((unused)) +ssize_t _sys_pread(int fd, void *buf, size_t count, off_t offset) +{ +#if __NOLIBC_BITS_PER_SYSCALL_ARG == 64 + return __nolibc_syscall4(__NR_pread64, fd, buf, count, offset); +#elif defined(__NOLIBC_PAD_64BIT_SYSCALL_ARGUMENT_PAIR) + return __nolibc_syscall6(__NR_pread64, fd, buf, count, 0, + __NOLIBC_LLARGPART(offset, 0), __NOLIBC_LLARGPART(offset, 1)); +#else + return __nolibc_syscall5(__NR_pread64, fd, buf, count, + __NOLIBC_LLARGPART(offset, 0), __NOLIBC_LLARGPART(offset, 1)); +#endif +} +#endif /* _sys_pread */ + +static __attribute__((unused)) +ssize_t pread(int fd, void *buf, size_t count, off_t offset) +{ + return __sysret(_sys_pread(fd, buf, count, offset)); +} + +#ifndef _sys_pwrite +static __attribute__((unused)) +ssize_t _sys_pwrite(int fd, const void *buf, size_t count, off_t offset) +{ +#if __NOLIBC_BITS_PER_SYSCALL_ARG == 64 + return __nolibc_syscall4(__NR_pwrite64, fd, buf, count, offset); +#elif defined(__NOLIBC_PAD_64BIT_SYSCALL_ARGUMENT_PAIR) + return __nolibc_syscall6(__NR_pwrite64, fd, buf, count, 0, + __NOLIBC_LLARGPART(offset, 0), __NOLIBC_LLARGPART(offset, 1)); +#else + return __nolibc_syscall5(__NR_pwrite64, fd, buf, count, + __NOLIBC_LLARGPART(offset, 0), __NOLIBC_LLARGPART(offset, 1)); +#endif +} +#endif /* _sys_pwrite */ + +static __attribute__((unused)) +ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset) +{ + return __sysret(_sys_pwrite(fd, buf, count, offset)); +} + + +/* * ssize_t read(int fd, void *buf, size_t count); */ diff --git a/tools/include/nolibc/sys/select.h b/tools/include/nolibc/sys/select.h index 6d65d9ef3d6a..f299c79939f5 100644 --- a/tools/include/nolibc/sys/select.h +++ b/tools/include/nolibc/sys/select.h @@ -26,7 +26,7 @@ typedef struct { int __fd = (fd); \ if (__fd >= 0) \ __set->fds[__fd / FD_SETIDXMASK] &= \ - ~(1U << (__fd & FD_SETBITMASK)); \ + ~(1UL << (__fd & FD_SETBITMASK)); \ } while (0) #define FD_SET(fd, set) do { \ @@ -34,7 +34,7 @@ typedef struct { int __fd = (fd); \ if (__fd >= 0) \ __set->fds[__fd / FD_SETIDXMASK] |= \ - 1 << (__fd & FD_SETBITMASK); \ + 1UL << (__fd & FD_SETBITMASK); \ } while (0) #define FD_ISSET(fd, set) ({ \ @@ -43,7 +43,7 @@ typedef struct { int __r = 0; \ if (__fd >= 0) \ __r = !!(__set->fds[__fd / FD_SETIDXMASK] & \ -1U << (__fd & FD_SETBITMASK)); \ +1UL << (__fd & FD_SETBITMASK)); \ __r; \ }) diff --git a/tools/include/nolibc/sys/sendfile.h b/tools/include/nolibc/sys/sendfile.h new file mode 100644 index 000000000000..ac7d85847718 --- /dev/null +++ b/tools/include/nolibc/sys/sendfile.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: LGPL-2.1 OR MIT */ +/* + * sendfile for NOLIBC + * Copyright (C) 2026 Daniel Palmer <daniel@thingy.jp> + */ + +/* make sure to include all global symbols */ +#include "../nolibc.h" + +#ifndef _NOLIBC_SYS_SENDFILE_H +#define _NOLIBC_SYS_SENDFILE_H + +#include "../sys.h" +#include <linux/unistd.h> + +/* + * ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count); + */ + +static __attribute__((unused)) +ssize_t _sys_sendfile(int out_fd, int in_fd, off_t *offset, size_t count) +{ + __nolibc_static_assert(sizeof(*offset) == sizeof(__kernel_loff_t)); + +#ifdef __NR_sendfile64 + /* 32-bit applications use sendfile64 so offset is treated as a __kernel_loff_t */ + return __nolibc_syscall4(__NR_sendfile64, out_fd, in_fd, offset, count); +#else + __nolibc_static_assert(sizeof(*offset) == sizeof(__kernel_off_t)); + + return __nolibc_syscall4(__NR_sendfile, out_fd, in_fd, offset, count); +#endif +} + +static __attribute__((unused)) +ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count) +{ + return __sysret(_sys_sendfile(out_fd, in_fd, offset, count)); +} + +#endif /* _NOLIBC_SYS_SENDFILE_H */ diff --git a/tools/include/nolibc/types.h b/tools/include/nolibc/types.h index 8f3cb18df7f1..4373bd8a5b5f 100644 --- a/tools/include/nolibc/types.h +++ b/tools/include/nolibc/types.h @@ -119,7 +119,7 @@ struct timeval { #define WEXITSTATUS(status) (((status) & 0xff00) >> 8) #define WIFEXITED(status) (((status) & 0x7f) == 0) #define WTERMSIG(status) ((status) & 0x7f) -#define WIFSIGNALED(status) ((status) - 1 < 0xff) +#define WIFSIGNALED(status) ((status) - 1U < 0xff) /* standard exit() codes */ #define EXIT_SUCCESS 0 diff --git a/tools/include/nolibc/unistd.h b/tools/include/nolibc/unistd.h index a264a20da13d..939d4e05a55d 100644 --- a/tools/include/nolibc/unistd.h +++ b/tools/include/nolibc/unistd.h @@ -52,7 +52,11 @@ int access(const char *path, int amode) static __attribute__((unused)) int _sys_ftruncate64(int fd, uint32_t length0, uint32_t length1) { +#if defined(__NOLIBC_PAD_64BIT_SYSCALL_ARGUMENT_PAIR) + return __nolibc_syscall4(__NR_ftruncate64, fd, 0, length0, length1); +#else return __nolibc_syscall3(__NR_ftruncate64, fd, length0, length1); +#endif } #define _sys_ftruncate64 _sys_ftruncate64 #endif diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 732b35cc08d1..8d7228834305 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -56,6 +56,19 @@ enum bpf_cond_pseudo_jmp { BPF_MAY_GOTO = 0, + /* + * Follows a call of a bpf subprog. Taken when the subprog left with + * BPF_EXIT that has src_reg == BPF_UNWIND_EXIT. + */ + BPF_UNWIND_GOTO = 1, +}; + +enum bpf_pseudo_exit { + /* + * When bpf_exit->src_reg == BPF_UNWIND_EXIT, it's the same as bpf_exit, + * except that BPF_UNWIND_GOTO after the call is taken. + */ + BPF_UNWIND_EXIT = 1, }; /* Register numbers */ @@ -936,6 +949,33 @@ union bpf_iter_link_info { * 0 on success or -1 if an error occurred (in which case, * *errno* is set appropriately). * + * BPF_PROG_STREAM_OPEN + * Description + * Open a file descriptor for one of the BPF streams associated + * with the program identified by *prog_fd*. The stream is selected + * by *stream_id*. + * + * The returned file descriptor supports **read**\ (2) and + * **poll**\ (2). Reads block while the stream is empty unless + * **BPF_F_STREAM_NONBLOCK** is specified in *flags*. A non-blocking + * read of an empty stream fails with **EAGAIN**. + * + * **poll**\ (2) reports **POLLIN** when data is available and + * **POLLHUP** once the program has been freed, that is, after every + * reference to it, including links and other file descriptors, has + * been dropped. Hangup may lag the final release because program + * teardown is deferred. Buffered data remains readable after + * **POLLHUP** and a read returns zero after all such data has been + * consumed. + * + * The file descriptor is read-only and has the close-on-exec flag + * set. It is not seekable and **lseek**\ (2) fails with **ESPIPE**. + * *flags* may only contain **BPF_F_STREAM_NONBLOCK**. + * + * Return + * A new file descriptor (a nonnegative integer), or -1 if an + * error occurred (in which case, *errno* is set appropriately). + * * NOTES * eBPF objects (maps and programs) can be shared between processes. * @@ -993,6 +1033,7 @@ enum bpf_cmd { BPF_TOKEN_CREATE, BPF_PROG_STREAM_READ_BY_FD, BPF_PROG_ASSOC_STRUCT_OPS, + BPF_PROG_STREAM_OPEN, __MAX_BPF_CMD, BPF_COMMON_ATTRS = 1 << 16, /* Indicate carrying syscall common attrs. */ }; @@ -1524,6 +1565,11 @@ enum { BPF_STREAM_STDERR = 2, }; +/* flags for BPF_PROG_STREAM_OPEN command */ +enum { + BPF_F_STREAM_NONBLOCK = (1U << 0), +}; + union bpf_attr { struct { /* anonymous struct used by BPF_MAP_CREATE command */ __u32 map_type; /* one of enum bpf_map_type */ @@ -1756,7 +1802,7 @@ union bpf_attr { __u32 prog_cnt; __u32 count; }; - __u32 :32; + __u32 type_id; /* output: per-program attach_flags. * not allowed to be set during effective query. */ @@ -1950,6 +1996,12 @@ union bpf_attr { __u32 flags; } prog_assoc_struct_ops; + struct { + __u32 prog_fd; + __u32 stream_id; + __u32 flags; + } prog_stream_open; + } __attribute__((aligned(8))); /* The description below is an attempt at providing documentation to eBPF @@ -4867,15 +4919,18 @@ union bpf_attr { * The non-negative copied *buf* length equal to or less than * *size* on success, or a negative error in case of failure. * - * long bpf_load_hdr_opt(struct bpf_sock_ops *skops, void *searchby_res, u32 len, u64 flags) + * long bpf_load_hdr_opt(void *ctx, void *searchby_res, u32 len, u64 flags) * Description * Load header option. Support reading a particular TCP header - * option for bpf program (**BPF_PROG_TYPE_SOCK_OPS**). + * option for bpf program (**BPF_PROG_TYPE_SOCK_OPS**). For the + * **bpf_tcp_ops** struct_ops, this helper can be called from the + * **parse_hdr**\ () and **write_hdr_opt**\ () operators. * - * If *flags* is 0, it will search the option from the - * *skops*\ **->skb_data**. The comment in **struct bpf_sock_ops** - * has details on what skb_data contains under different - * *skops*\ **->op**. + * If *flags* is 0, it will search the option from the packet + * associated with the current operation. For + * **BPF_PROG_TYPE_SOCK_OPS**, the comment in + * **struct bpf_sock_ops** has details on what skb_data + * contains under different *op*. * * The first byte of the *searchby_res* specifies the * kind that it wants to search. @@ -4908,6 +4963,8 @@ union bpf_attr { * * * **BPF_LOAD_HDR_OPT_TCP_SYN** to search from the * saved_syn packet or the just-received syn packet. + * Not supported by the **bpf_tcp_ops** struct_ops, which + * rejects all flags. * * Return * > 0 when found, the header option is copied to *searchby_res*. @@ -4928,9 +4985,9 @@ union bpf_attr { * packet. * * **-EPERM** if the helper cannot be used under the current - * *skops*\ **->op**. + * operation. * - * long bpf_store_hdr_opt(struct bpf_sock_ops *skops, const void *from, u32 len, u64 flags) + * long bpf_store_hdr_opt(void *ctx, const void *from, u32 len, u64 flags) * Description * Store header option. The data will be copied * from buffer *from* with length *len* to the TCP header. @@ -4946,7 +5003,9 @@ union bpf_attr { * by searching the same option in the outgoing skb. * * This helper can only be called during - * **BPF_SOCK_OPS_WRITE_HDR_OPT_CB**. + * **BPF_SOCK_OPS_WRITE_HDR_OPT_CB**, or from the + * **write_hdr_opt**\ () operator of the **bpf_tcp_ops** + * struct_ops. * * Return * 0 on success, or negative error in case of failure: @@ -4961,9 +5020,9 @@ union bpf_attr { * **-EFAULT** on failure to parse the existing header options. * * **-EPERM** if the helper cannot be used under the current - * *skops*\ **->op**. + * operation. * - * long bpf_reserve_hdr_opt(struct bpf_sock_ops *skops, u32 len, u64 flags) + * long bpf_reserve_hdr_opt(void *ctx, u32 len, u64 flags) * Description * Reserve *len* bytes for the bpf header option. The * space will be used by **bpf_store_hdr_opt**\ () later in @@ -4973,7 +5032,9 @@ union bpf_attr { * the total number of bytes will be reserved. * * This helper can only be called during - * **BPF_SOCK_OPS_HDR_OPT_LEN_CB**. + * **BPF_SOCK_OPS_HDR_OPT_LEN_CB**, or from the + * **hdr_opt_len**\ () operator of the **bpf_tcp_ops** + * struct_ops. * * Return * 0 on success, or negative error in case of failure: @@ -4983,7 +5044,7 @@ union bpf_attr { * **-ENOSPC** if there is not enough space in the header. * * **-EPERM** if the helper cannot be used under the current - * *skops*\ **->op**. + * operation. * * void *bpf_inode_storage_get(struct bpf_map *map, void *inode, void *value, u64 flags) * Description @@ -6890,6 +6951,8 @@ struct bpf_link_info { } xdp; struct { __u32 map_id; + __u32 :32; + __u64 cgroup_id; } struct_ops; struct { __u32 pf; @@ -7284,6 +7347,31 @@ enum { */ }; +/* + * Most callbacks of struct bpf_tcp_ops are placed in the slow + * path (e.g., one-shot connection setup or unlikely events like + * timers) and are invoked simply by defining non-NULL callbacks. + * + * Callbacks in the fast path, however, would incur noticeable + * overhead even when set to NULL, so they are disabled by default + * and must be explicitly enabled per socket via bpf_tcp_ops_set_flags(). + * + * The flags are per-socket and shared by all effective bpf_tcp_ops + * programs. + */ +enum { + /* .rtt() */ + BPF_TCP_OPS_FLAG_RTT = (1 << 0), + /* .parse_hdr() */ + BPF_TCP_OPS_FLAG_PARSE_HDR_OPT_ALL = (1 << 1), + BPF_TCP_OPS_FLAG_PARSE_HDR_OPT_UNKNOWN = (1 << 2), + /* .hdr_opt_len() and .write_hdr_opt() */ + BPF_TCP_OPS_FLAG_WRITE_HDR_OPT = (1 << 3), + /* .enqueue_rcvq() and .dequeue_rcvq() */ + BPF_TCP_OPS_FLAG_RCVQ = (1 << 4), + BPF_TCP_OPS_FLAG_ALL = (1 << 5) - 1, +}; + /* List of TCP states. There is a build check in net/ipv4/tcp.c to detect * changes between the TCP and BPF versions. Ideally this should never happen. * If it does, we need to add code to convert them before calling @@ -7600,6 +7688,10 @@ struct bpf_task_work { __u64 __opaque; } __attribute__((aligned(8))); +struct bpf_rcu_head { + __u64 __opaque[6]; +} __attribute__((aligned(8))); + struct bpf_wq { __u64 __opaque[2]; } __attribute__((aligned(8))); diff --git a/tools/include/uapi/linux/btf.h b/tools/include/uapi/linux/btf.h index 618167cab4e6..11d65871d163 100644 --- a/tools/include/uapi/linux/btf.h +++ b/tools/include/uapi/linux/btf.h @@ -54,8 +54,8 @@ struct btf_type { * decl_tag and type_tag */ __u32 info; - /* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64. - * "size" tells the size of the type it is describing. + /* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC, ENUM64 + * and LOC_PARAM. "size" tells the size of the type it is describing. * * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT, * FUNC, FUNC_PROTO, VAR, DECL_TAG and TYPE_TAG. @@ -92,7 +92,9 @@ enum { BTF_KIND_DECL_TAG = 17, /* Decl Tag */ BTF_KIND_TYPE_TAG = 18, /* Type Tag */ BTF_KIND_ENUM64 = 19, /* Enumeration up to 64-bit values */ - + BTF_KIND_LOC_PARAM = 20, /* Location parameter information */ + BTF_KIND_LOC_PROTO = 21, /* Location prototype for site */ + BTF_KIND_LOCSEC = 22, /* Location section */ NR_BTF_KINDS, BTF_KIND_MAX = NR_BTF_KINDS - 1, }; @@ -212,4 +214,79 @@ struct btf_enum64 { __u32 val_hi32; }; +/* + * BTF_KIND_LOC_PARAM is followed by a single "struct btf_loc_param" + * that contains flags specifying the contents of the vlen-specified + * number of 4-byte values that follow. + */ +struct btf_loc_param { + __u32 flags; + __u32 values[]; +}; + +/* + * The combination of size, vlen and flags gives us the means to interpret + * the following vlen-specified set of 4-byte values: + * + * - a BTF_LOC_PARAM_CONST is a constant value; combination + * of size, vlen and _SIGNED flag determines it. If the value requires + * 64 bits it is stored in {lo,hi} order. + * - a BTF_LOC_PARAM_ADDR|BTF_LOC_PARAM_CONST is an address that should be + * normalized with respect to kernel/module base address. + * - a BTF_LOC_PARAM_REG with vlen 1 is a simple register number; + * with vlen 2 it is a multi-register parameter. Register numbers are + * numbers derived from DW_OP_reg values, i.e. 0 is DW_OP_reg0; since + * DW_OP_fbreg has its own special DW_OP_value and DW_OP_regx can refer + * to an arbitrary register number, we reserve BTF_LOC_PARAM_FBREG for + * the frame base register to avoid collisions. + * - a _REG | OFFSET describes an address without dereferencing it. + * - a _REG | DEREF with vlen 1 dereferences the value in the register + * number specified. + * - a REG | DEREF | OFFSET with vlen > 1 specifies the register number + * in the first 4-byte value and the offset in the remainder. + * In the case of REG and OFFSET combinations, the OFFSET has the width + * of the value words while the type size describes the represented parameter, + * so for example a REG | DEREF | OFFSET with size 8 and vlen 2 would be + * an 8-byte register dereference with signed 4-byte offset, since the vlen 2 + * values consist of a register value and the signed value. + */ +enum btf_loc_param_flags { + BTF_LOC_PARAM_SIGNED = 0x1, + BTF_LOC_PARAM_CONST = 0x2, + BTF_LOC_PARAM_ADDR = 0x4, + BTF_LOC_PARAM_REG = 0x8, + BTF_LOC_PARAM_DEREF = 0x10, + BTF_LOC_PARAM_OFFSET = 0x20, +}; + +enum { + BTF_LOC_PARAM_FBREG = 0xffffffff +}; + +/* + * BTF_KIND_LOC_PROTO specifies location prototypes; i.e. how locations relate + * to parameters; a struct btf_type of BTF_KIND_LOC_PROTO is followed by a + * vlen-specified number of __u32 BTF type ids which specify the associated + * BTF_KIND_LOC_PARAM for each function parameter associated with the + * location. The type should either be 0 (no location info) or point at + * a BTF_KIND_LOC_PARAM. + */ + +/* + * BTF_KIND_LOCSEC consists of vlen-specified number of "struct btf_loc" + * containing location site-specific information for a specific ELF section; + * for example locations in ".text" are in a LOCSEC named "inline.text". + * + * - function (func) + * - location prototype type id (loc_proto) + * - address offset (offset) relative to the runtime base address of the + * ELF section associated with the LOCSEC + */ + +struct btf_loc { + __u32 func; + __u32 loc_proto; + __u32 offset; +}; + #endif /* _UAPI__LINUX_BTF_H__ */ diff --git a/tools/include/uapi/linux/coredump.h b/tools/include/uapi/linux/coredump.h index dc3789b78af0..6d0c53b534ea 100644 --- a/tools/include/uapi/linux/coredump.h +++ b/tools/include/uapi/linux/coredump.h @@ -11,12 +11,53 @@ * @COREDUMP_USERSPACE: userspace writes coredump * @COREDUMP_REJECT: don't generate coredump * @COREDUMP_WAIT: wait for coredump server + * @COREDUMP_RECORDS: send the coredump as a sequence of records instead of + * as a plain byte stream, see struct coredump_record_header; + * requires COREDUMP_KERNEL + * @COREDUMP_SPARSE: describe the holes in the coredump as zero records + * instead of transferring them; requires COREDUMP_RECORDS + * @COREDUMP_MEMORY_TYPES: dump the memory types in + * coredump_ack->memory_types instead of the ones + * the task selected; requires COREDUMP_KERNEL */ enum { COREDUMP_KERNEL = (1ULL << 0), COREDUMP_USERSPACE = (1ULL << 1), COREDUMP_REJECT = (1ULL << 2), COREDUMP_WAIT = (1ULL << 3), + COREDUMP_RECORDS = (1ULL << 4), + COREDUMP_SPARSE = (1ULL << 5), + COREDUMP_MEMORY_TYPES = (1ULL << 6), +}; + +/** + * coredump memory types + * @COREDUMP_MEMORY_ANON_PRIVATE: anonymous private memory + * @COREDUMP_MEMORY_ANON_SHARED: anonymous shared memory + * @COREDUMP_MEMORY_FILE_PRIVATE: file-backed private memory + * @COREDUMP_MEMORY_FILE_SHARED: file-backed shared memory + * @COREDUMP_MEMORY_ELF_HEADERS: the first page of a file-backed private + * mapping that starts an ELF file + * @COREDUMP_MEMORY_HUGETLB_PRIVATE: hugetlb private memory + * @COREDUMP_MEMORY_HUGETLB_SHARED: hugetlb shared memory + * @COREDUMP_MEMORY_DAX_PRIVATE: DAX private memory + * @COREDUMP_MEMORY_DAX_SHARED: DAX shared memory + * + * A bitmask of memory types a coredump may request to be included. New + * memory type bits must ensure that they do not steal memory from an + * existing one so a coredump server will continue to get the same + * coredumps even if a new bit is introduced. + */ +enum { + COREDUMP_MEMORY_ANON_PRIVATE = (1ULL << 0), + COREDUMP_MEMORY_ANON_SHARED = (1ULL << 1), + COREDUMP_MEMORY_FILE_PRIVATE = (1ULL << 2), + COREDUMP_MEMORY_FILE_SHARED = (1ULL << 3), + COREDUMP_MEMORY_ELF_HEADERS = (1ULL << 4), + COREDUMP_MEMORY_HUGETLB_PRIVATE = (1ULL << 5), + COREDUMP_MEMORY_HUGETLB_SHARED = (1ULL << 6), + COREDUMP_MEMORY_DAX_PRIVATE = (1ULL << 7), + COREDUMP_MEMORY_DAX_SHARED = (1ULL << 8), }; /** @@ -24,17 +65,19 @@ enum { * @size: size of struct coredump_req * @size_ack: known size of struct coredump_ack on this kernel * @mask: supported features + * @memory_types: the memory types the task selected + * @memory_types_mask: the memory types this kernel knows * * When a coredump happens the kernel will connect to the coredump * socket and send a coredump request to the coredump server. The @size * member is set to the size of struct coredump_req and provides a hint * to userspace how much data can be read. Userspace may use MSG_PEEK to * peek the size of struct coredump_req and then choose to consume it in - * one go. Userspace may also simply read a COREDUMP_ACK_SIZE_VER0 + * one go. Userspace may also simply read a COREDUMP_REQ_SIZE_VER0 * request. If the size the kernel sends is larger userspace simply * discards any remaining data. * - * The coredump_req->mask member is set to the currently know features. + * The coredump_req->mask member is set to the currently known features. * Userspace may only set coredump_ack->mask to the bits raised by the * kernel in coredump_req->mask. * @@ -42,15 +85,27 @@ enum { * struct coredump_ack the kernel knows. Userspace may only send up to * coredump_req->size_ack bytes to the kernel and must set * coredump_ack->size accordingly. + * + * @memory_types is set to the default memory types that are included in + * the coredump. This can be overridden by raising bits in + * coredump_ack->memory_types. + * + * @memory_types_mask contains a bitmask of all memory types the kernel + * knows about. A coredump server may only raise bits in + * coredump_ack->memory_types that are raised in + * coredump_req->memory_types_mask. */ struct coredump_req { __u32 size; __u32 size_ack; __u64 mask; + __u64 memory_types; + __u64 memory_types_mask; }; enum { COREDUMP_REQ_SIZE_VER0 = 16U, /* size of first published struct */ + COREDUMP_REQ_SIZE_VER1 = 32U, /* memory_types and memory_types_mask added */ }; /** @@ -58,6 +113,8 @@ enum { * @size: size of the struct * @spare: unused * @mask: features kernel is supposed to use + * @memory_types: memory types to dump, only with COREDUMP_MEMORY_TYPES + * in @mask * * The @size member must be set to the size of struct coredump_ack. It * may never exceed what the kernel returned in coredump_req->size_ack @@ -67,15 +124,30 @@ enum { * The @mask member must be set to the features the coredump server * wants the kernel to use. Only bits the kernel returned in * coredump_req->mask may be set. + * + * If COREDUMP_MEMORY_TYPES is raised in @mask the kernel dumps the + * memory types set in the @memory_types mask. Zero is valid and dumps + * no memory apart from the mappings that are always dumped. + * + * Note that memory a task excluded via MADV_DONTDUMP is always left + * out. A coredump server wanting to add or drop memory types instead of + * outright replacing it should simply copy coredump_req->memory_types + * and then mask off or raise types as needed. + * + * Note that @memory_types must be zero if COREDUMP_MEMORY_TYPES isn't + * raised. COREDUMP_MEMORY_TYPES requires COREDUMP_KERNEL and an ack of + * at least COREDUMP_ACK_SIZE_VER1 bytes. */ struct coredump_ack { __u32 size; __u32 spare; __u64 mask; + __u64 memory_types; }; enum { COREDUMP_ACK_SIZE_VER0 = 16U, /* size of first published struct */ + COREDUMP_ACK_SIZE_VER1 = 24U, /* memory_types added */ }; /** @@ -83,11 +155,12 @@ enum { * * The kernel will place a single byte on the coredump socket. The * markers notify userspace whether the coredump ack succeeded or - * failed. + * failed. After any marker other than COREDUMP_MARK_REQACK the kernel + * closes the connection and no coredump is generated. * * @COREDUMP_MARK_MINSIZE: the provided coredump_ack size was too small * @COREDUMP_MARK_MAXSIZE: the provided coredump_ack size was too big - * @COREDUMP_MARK_UNSUPPORTED: the provided coredump_ack mask was invalid + * @COREDUMP_MARK_UNSUPPORTED: the provided coredump_ack mask or memory types were invalid * @COREDUMP_MARK_CONFLICTING: the provided coredump_ack mask has conflicting options * @COREDUMP_MARK_REQACK: the coredump request and ack was successful * @__COREDUMP_MARK_MAX: the maximum coredump mark value @@ -101,4 +174,72 @@ enum coredump_mark { __COREDUMP_MARK_MAX = (1U << 31), }; +/** + * enum coredump_record_type - Type of a coredump record + * + * @COREDUMP_RECORD_DATA: the header is followed by ->len bytes of data + * @COREDUMP_RECORD_END: the coredump ends here, the header is not followed + * by any data and no further record is sent + * @COREDUMP_RECORD_ZERO: the header stands for ->len zero bytes and is not + * followed by any data + * @__COREDUMP_RECORD_TYPE_MAX: the maximum coredump record type value + */ +enum coredump_record_type { + COREDUMP_RECORD_DATA = 0U, + COREDUMP_RECORD_END = 1U, + COREDUMP_RECORD_ZERO = 2U, + __COREDUMP_RECORD_TYPE_MAX = (1U << 31), +}; + +/** + * struct coredump_record_header - header of a coredump record + * @size: size of struct coredump_record_header + * @type: one of enum coredump_record_type + * @flags: modifiers for this record + * @offset: offset in the coredump this record starts at + * @len: number of coredump bytes this record accounts for + * + * If the coredump server raises COREDUMP_RECORDS in coredump_ack->mask + * the kernel doesn't send the coredump as a plain byte stream. It sends + * a sequence of records instead. A COREDUMP_RECORD_DATA record is + * followed by @len bytes of actual coredump data. A + * COREDUMP_RECORD_ZERO record is followed by nothing and stands for + * @len zero bytes. A server that didn't raise COREDUMP_SPARSE never + * sees a zero record. Records arrive in order and leave no gaps. So + * @offset is the sum of the @len of all records before it. + * + * The last record is a COREDUMP_RECORD_END record. It is followed by + * nothing. Its @len is zero. Its @offset is the size of the coredump. + * The kernel only sends it once it has written the whole coredump. A + * server that hits end-of-file without having seen an end record must + * treat the coredump as incomplete. + * + * The @size member is set to the size of struct coredump_record_header + * the kernel knows and lets the header grow later. It comes first so it + * can be peeked. Userspace must consume @size bytes and discard + * anything beyond what it knows. It must refuse a @size smaller than + * COREDUMP_RECORD_HEADER_SIZE_VER0. @size covers the header alone. + * @offset and @len count coredump bytes. + * + * The @flags member carries modifiers that change how the record is to + * be interpreted. No flag is defined yet. Userspace must refuse a + * record carrying a flag or a type it doesn't know. Every new record + * type is raised in coredump_req->mask as a feature of its own. A + * server only ever sees the types it asked for. + * + * COREDUMP_RECORDS must be combined with COREDUMP_KERNEL, and + * COREDUMP_SPARSE with COREDUMP_RECORDS. + */ +struct coredump_record_header { + __u32 size; + __u32 type; + __u64 flags; + __u64 offset; + __u64 len; +}; + +enum { + COREDUMP_RECORD_HEADER_SIZE_VER0 = 32U, /* size of first published struct */ +}; + #endif /* _UAPI_LINUX_COREDUMP_H */ diff --git a/tools/include/uapi/linux/netdev.h b/tools/include/uapi/linux/netdev.h index 35ff083221c7..9026e0414fbe 100644 --- a/tools/include/uapi/linux/netdev.h +++ b/tools/include/uapi/linux/netdev.h @@ -58,7 +58,7 @@ enum netdev_xdp_rx_metadata { * enum netdev_xsk_flags * @NETDEV_XSK_FLAGS_TX_TIMESTAMP: HW timestamping egress packets is supported * by the driver. - * @NETDEV_XSK_FLAGS_TX_CHECKSUM: L3 checksum HW offload is supported by the + * @NETDEV_XSK_FLAGS_TX_CHECKSUM: L4 checksum HW offload is supported by the * driver. * @NETDEV_XSK_FLAGS_TX_LAUNCH_TIME_FIFO: Launch time HW offload is supported * by the driver. diff --git a/tools/include/uapi/linux/seccomp.h b/tools/include/uapi/linux/seccomp.h index dbfc9b37fcae..3a0ee0551261 100644 --- a/tools/include/uapi/linux/seccomp.h +++ b/tools/include/uapi/linux/seccomp.h @@ -25,6 +25,8 @@ #define SECCOMP_FILTER_FLAG_TSYNC_ESRCH (1UL << 4) /* Received notifications wait in killable state (only respond to fatal signals) */ #define SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV (1UL << 5) +/* Restart syscalls interrupted before their notification is received */ +#define SECCOMP_FILTER_FLAG_RESTART_BEFORE_RECV (1UL << 6) /* * All BPF programs must return a 32-bit value. diff --git a/tools/include/uapi/linux/types.h b/tools/include/uapi/linux/types.h index 85aa327245c6..6acf2059085a 100644 --- a/tools/include/uapi/linux/types.h +++ b/tools/include/uapi/linux/types.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef _UAPI_LINUX_TYPES_H #define _UAPI_LINUX_TYPES_H |
