diff --git a/Documentation/arm64/ilp32.txt b/Documentation/arm64/ilp32.txt new file mode 100644 index 00000000000000..5f01a61c92af59 --- /dev/null +++ b/Documentation/arm64/ilp32.txt @@ -0,0 +1,52 @@ +ILP32 AARCH64 SYSCALL ABI +========================= + +This document describes the ILP32 syscall ABI and where it differs +from the generic compat linux syscall interface. + +ILP32 is acronym for memory model which stands for "Integers, Longs and +Pointers are 32-bit". The main purpose of ILP32 in Linux kernel is providing +compatibility with 32-bit legacy code. Also, ILP32 binaries look better in some +performance tests. ARM has AN490 document which coves ILP32 details for ARM64 +platform: +http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0490a/ar01s01.html + +AARCH64/ILP32 userspace may pass garbage in the top halve of w0-w7 registers +(syscall arguments). So top 32 bits are zeroed for them. + +Comparing to AARCH32, AARCH64/ILP32 has 64-bit length of following types: +ino_t is u64 type. +off_t is s64 type. +blkcnt_t is s64 type. +fsblkcnt_t is u64 type. +fsfilcnt_t is u64 type. +rlim_t is u64 type. + +AARCH64/ILP32 ABI uses standard syscall table which can be found at +include/uapi/asm-generic/unistd.h, with the exceptions listed below. + +Syscalls which pass 64-bit values are handled by the code shared from +AARCH32 and pass that value as a pair. Following syscalls are affected: +fadvise64_64() +fallocate() +ftruncate64() +pread64() +pwrite64() +readahead() +sync_file_range() +truncate64() + +ptrace() syscall is handled by compat version. + +shmat() syscall is handled by non-compat handler as aarch64/ilp32 has no +limitation on 4-pages alignment for shared memory. + +statfs() and fstatfs() take the size of struct statfs as an argument. +It is calculated differently in kernel and user spaces. So AARCH32 handlers +are taken to handle it. + +struct rt_sigframe is redefined and contains struct compat_siginfo, +as compat syscalls expect, and struct ilp32_ucontext, to handle +AARCH64 register set and 32-bit userspace register representation. + +elf_gregset_t is taken from lp64 to handle registers properly. diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 697ea05107298b..89a8721c5f4bb3 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -431,7 +431,7 @@ config ARM64_ERRATUM_834220 config ARM64_ERRATUM_845719 bool "Cortex-A53: 845719: a load might read incorrect data" - depends on COMPAT + depends on AARCH32_EL0 default y help This option adds an alternative code sequence to work around ARM @@ -1107,10 +1107,17 @@ config ARM64_SW_TTBR0_PAN zeroed area and reserved ASID. The user access routines restore the valid TTBR0_EL1 temporarily. -menuconfig COMPAT +config ARM64_ILP32 + bool "Kernel support for ILP32" + default y + help + This option enables support for AArch64 ILP32 user space. ILP32 + is an ABI where long and pointers are 32bits but it uses the AARCH64 + instruction set. + +menuconfig AARCH32_EL0 bool "Kernel support for 32-bit EL0" depends on ARM64_4K_PAGES || EXPERT - select COMPAT_BINFMT_ELF if BINFMT_ELF select HAVE_UID16 select OLD_SIGSUSPEND3 select COMPAT_OLD_SIGACTION @@ -1126,7 +1133,7 @@ menuconfig COMPAT If you want to execute 32-bit userspace applications, say Y. -if COMPAT +if AARCH32_EL0 config KUSER_HELPERS bool "Enable kuser helpers page for 32 bit applications" @@ -1158,6 +1165,7 @@ config KUSER_HELPERS menuconfig ARMV8_DEPRECATED bool "Emulate deprecated/obsolete ARMv8 instructions" + depends on AARCH32_EL0 depends on SYSCTL help Legacy software support may require certain instructions @@ -1552,6 +1560,10 @@ config DMI endmenu +config COMPAT + def_bool y + depends on AARCH32_EL0 || ARM64_ILP32 + config SYSVIPC_COMPAT def_bool y depends on COMPAT && SYSVIPC diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index e9d2e578cbe67b..ebe15a9ab75ffc 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -164,6 +164,9 @@ ifeq ($(KBUILD_EXTMOD),) prepare: vdso_prepare vdso_prepare: prepare0 $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso include/generated/vdso-offsets.h +ifeq ($(CONFIG_ARM64_ILP32), y) + $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso-ilp32 include/generated/vdso-ilp32-offsets.h +endif endif define archhelp diff --git a/arch/arm64/include/asm/compat.h b/arch/arm64/include/asm/compat.h index fb8ad4616b3bff..b3268fce4cbd39 100644 --- a/arch/arm64/include/asm/compat.h +++ b/arch/arm64/include/asm/compat.h @@ -15,6 +15,7 @@ #include #include +#include #define COMPAT_USER_HZ 100 #ifdef __AARCH64EB__ @@ -197,23 +198,6 @@ struct compat_shmid64_ds { compat_ulong_t __unused5; }; -static inline int is_compat_task(void) -{ - return test_thread_flag(TIF_32BIT); -} - -static inline int is_compat_thread(struct thread_info *thread) -{ - return test_ti_thread_flag(thread, TIF_32BIT); -} - -#else /* !CONFIG_COMPAT */ - -static inline int is_compat_thread(struct thread_info *thread) -{ - return 0; -} - #endif /* CONFIG_COMPAT */ #endif /* __KERNEL__ */ #endif /* __ASM_COMPAT_H */ diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h index 325d9515c0f8fe..6da09cf7f8df84 100644 --- a/arch/arm64/include/asm/elf.h +++ b/arch/arm64/include/asm/elf.h @@ -5,6 +5,10 @@ #ifndef __ASM_ELF_H #define __ASM_ELF_H +#ifndef __ASSEMBLY__ +#include +#endif + #include /* @@ -135,6 +139,7 @@ typedef struct user_fpsimd_state elf_fpregset_t; #define SET_PERSONALITY(ex) \ ({ \ + clear_thread_flag(TIF_32BIT_AARCH64); \ clear_thread_flag(TIF_32BIT); \ current->personality &= ~READ_IMPLIES_EXEC; \ }) @@ -162,13 +167,9 @@ extern int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp); /* 1GB of VA */ -#ifdef CONFIG_COMPAT -#define STACK_RND_MASK (test_thread_flag(TIF_32BIT) ? \ +#define STACK_RND_MASK (is_compat_task() ? \ 0x7ff >> (PAGE_SHIFT - 12) : \ 0x3ffff >> (PAGE_SHIFT - 12)) -#else -#define STACK_RND_MASK (0x3ffff >> (PAGE_SHIFT - 12)) -#endif #ifdef __AARCH64EB__ #define COMPAT_ELF_PLATFORM ("v8b") @@ -180,35 +181,16 @@ extern int arch_setup_additional_pages(struct linux_binprm *bprm, /* PIE load location for compat arm. Must match ARM ELF_ET_DYN_BASE. */ #define COMPAT_ELF_ET_DYN_BASE 0x000400000UL +#endif /*CONFIG_COMPAT */ +#ifdef CONFIG_AARCH32_EL0 /* AArch32 registers. */ #define COMPAT_ELF_NGREG 18 typedef unsigned int compat_elf_greg_t; typedef compat_elf_greg_t compat_elf_gregset_t[COMPAT_ELF_NGREG]; - -/* AArch32 EABI. */ -#define EF_ARM_EABI_MASK 0xff000000 -#define compat_elf_check_arch(x) (system_supports_32bit_el0() && \ - ((x)->e_machine == EM_ARM) && \ - ((x)->e_flags & EF_ARM_EABI_MASK)) - -#define compat_start_thread compat_start_thread -/* - * Unlike the native SET_PERSONALITY macro, the compat version maintains - * READ_IMPLIES_EXEC across an execve() since this is the behaviour on - * arch/arm/. - */ -#define COMPAT_SET_PERSONALITY(ex) \ -({ \ - set_thread_flag(TIF_32BIT); \ - }) -#define COMPAT_ARCH_DLINFO extern int aarch32_setup_additional_pages(struct linux_binprm *bprm, - int uses_interp); -#define compat_arch_setup_additional_pages \ - aarch32_setup_additional_pages - -#endif /* CONFIG_COMPAT */ + int uses_interp); +#endif /* CONFIG_AARCH32_EL0 */ #endif /* !__ASSEMBLY__ */ diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.h index 897029c8e9b548..8e6a7a64b88179 100644 --- a/arch/arm64/include/asm/fpsimd.h +++ b/arch/arm64/include/asm/fpsimd.h @@ -21,7 +21,7 @@ #include #include -#if defined(__KERNEL__) && defined(CONFIG_COMPAT) +#if defined(__KERNEL__) && defined(CONFIG_AARCH32_EL0) /* Masks for extracting the FPSR and FPCR from the FPSCR */ #define VFP_FPSCR_STAT_MASK 0xf800009f #define VFP_FPSCR_CTRL_MASK 0x07f79f00 diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index 5ab5200b2bdc8e..5cf6c1315203f1 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -52,7 +52,7 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) #define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs) { - return is_compat_task(); + return is_a32_compat_task(); } #define ARCH_HAS_SYSCALL_MATCH_SYM_NAME diff --git a/arch/arm64/include/asm/hwcap.h b/arch/arm64/include/asm/hwcap.h index e5d9420cd25829..16101c358c7c6e 100644 --- a/arch/arm64/include/asm/hwcap.h +++ b/arch/arm64/include/asm/hwcap.h @@ -92,15 +92,13 @@ #define ELF_HWCAP cpu_get_elf_hwcap() #define ELF_HWCAP2 cpu_get_elf_hwcap2() -#ifdef CONFIG_COMPAT -#define COMPAT_ELF_HWCAP (compat_elf_hwcap) -#define COMPAT_ELF_HWCAP2 (compat_elf_hwcap2) -extern unsigned int compat_elf_hwcap, compat_elf_hwcap2; +#ifdef CONFIG_AARCH32_EL0 +extern unsigned int a32_elf_hwcap, a32_elf_hwcap2; #endif enum { CAP_HWCAP = 1, -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 CAP_COMPAT_HWCAP, CAP_COMPAT_HWCAP2, #endif diff --git a/arch/arm64/include/asm/is_compat.h b/arch/arm64/include/asm/is_compat.h new file mode 100644 index 00000000000000..2c2d1f4c26bdd7 --- /dev/null +++ b/arch/arm64/include/asm/is_compat.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __ASM_IS_COMPAT_H +#define __ASM_IS_COMPAT_H +#ifndef __ASSEMBLY__ + +#include + +#ifdef CONFIG_AARCH32_EL0 + +static inline int is_a32_compat_task(void) +{ + return test_thread_flag(TIF_32BIT); +} + +static inline int is_a32_compat_thread(struct thread_info *thread) +{ + return test_ti_thread_flag(thread, TIF_32BIT); +} + +#else + +static inline int is_a32_compat_task(void) + +{ + return 0; +} + +static inline int is_a32_compat_thread(struct thread_info *thread) +{ + return 0; +} + +#endif /* CONFIG_AARCH32_EL0 */ + +#ifdef CONFIG_ARM64_ILP32 + +static inline int is_ilp32_compat_task(void) +{ + return test_thread_flag(TIF_32BIT_AARCH64); +} + +static inline int is_ilp32_compat_thread(struct thread_info *thread) +{ + return test_ti_thread_flag(thread, TIF_32BIT_AARCH64); +} + +#else + +static inline int is_ilp32_compat_task(void) +{ + return 0; +} + +static inline int is_ilp32_compat_thread(struct thread_info *thread) +{ + return 0; +} + +#endif /* CONFIG_ARM64_ILP32 */ + +#ifdef CONFIG_COMPAT + +static inline int is_compat_task(void) +{ + return is_a32_compat_task() || is_ilp32_compat_task(); +} + +#endif /* CONFIG_COMPAT */ + +static inline int is_compat_thread(struct thread_info *thread) +{ + return is_a32_compat_thread(thread) || is_ilp32_compat_thread(thread); +} + + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_IS_COMPAT_H */ diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h index fd5b1a4efc70ed..9ca8372beb07ac 100644 --- a/arch/arm64/include/asm/processor.h +++ b/arch/arm64/include/asm/processor.h @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -55,11 +56,11 @@ #else #define TASK_SIZE_32 (UL(0x100000000) - PAGE_SIZE) #endif /* CONFIG_ARM64_64K_PAGES */ -#define TASK_SIZE (test_thread_flag(TIF_32BIT) ? \ +#define TASK_SIZE (is_compat_task() ? \ TASK_SIZE_32 : TASK_SIZE_64) -#define TASK_SIZE_OF(tsk) (test_tsk_thread_flag(tsk, TIF_32BIT) ? \ +#define TASK_SIZE_OF(tsk) (is_compat_thread(tsk) ? \ TASK_SIZE_32 : TASK_SIZE_64) -#define DEFAULT_MAP_WINDOW (test_thread_flag(TIF_32BIT) ? \ +#define DEFAULT_MAP_WINDOW (is_compat_task() ? \ TASK_SIZE_32 : DEFAULT_MAP_WINDOW_64) #else #define TASK_SIZE TASK_SIZE_64 @@ -76,7 +77,7 @@ #ifdef CONFIG_COMPAT #define AARCH32_VECTORS_BASE 0xffff0000 -#define STACK_TOP (test_thread_flag(TIF_32BIT) ? \ +#define STACK_TOP (is_compat_task() ? \ AARCH32_VECTORS_BASE : STACK_TOP_MAX) #else #define STACK_TOP STACK_TOP_MAX @@ -162,11 +163,11 @@ static inline void arch_thread_struct_whitelist(unsigned long *offset, *size = sizeof_field(struct thread_struct, uw); } -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 #define task_user_tls(t) \ ({ \ unsigned long *__tls; \ - if (is_compat_thread(task_thread_info(t))) \ + if (is_a32_compat_thread(task_thread_info(t))) \ __tls = &(t)->thread.uw.tp2_value; \ else \ __tls = &(t)->thread.uw.tp_value; \ @@ -205,7 +206,7 @@ static inline void start_thread(struct pt_regs *regs, unsigned long pc, regs->sp = sp; } -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 static inline void compat_start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp) { diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h index dad858b6adc6da..9c8d32b698dce9 100644 --- a/arch/arm64/include/asm/ptrace.h +++ b/arch/arm64/include/asm/ptrace.h @@ -189,17 +189,17 @@ static inline void forget_syscall(struct pt_regs *regs) #define arch_has_single_step() (1) -#ifdef CONFIG_COMPAT -#define compat_thumb_mode(regs) \ +#ifdef CONFIG_AARCH32_EL0 +#define a32_thumb_mode(regs) \ (((regs)->pstate & PSR_AA32_T_BIT)) #else -#define compat_thumb_mode(regs) (0) +#define a32_thumb_mode(regs) (0) #endif #define user_mode(regs) \ (((regs)->pstate & PSR_MODE_MASK) == PSR_MODE_EL0t) -#define compat_user_mode(regs) \ +#define a32_user_mode(regs) \ (((regs)->pstate & (PSR_MODE32_BIT | PSR_MODE_MASK)) == \ (PSR_MODE32_BIT | PSR_MODE_EL0t)) @@ -218,10 +218,10 @@ static inline void forget_syscall(struct pt_regs *regs) (!((regs)->pstate & PSR_F_BIT)) #define GET_USP(regs) \ - (!compat_user_mode(regs) ? (regs)->sp : (regs)->compat_sp) + (!a32_user_mode(regs) ? (regs)->sp : (regs)->compat_sp) #define SET_USP(ptregs, value) \ - (!compat_user_mode(regs) ? ((regs)->sp = value) : ((regs)->compat_sp = value)) + (!a32_user_mode(regs) ? ((regs)->sp = value) : ((regs)->compat_sp = value)) extern int regs_query_register_offset(const char *name); extern unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, diff --git a/arch/arm64/include/asm/seccomp.h b/arch/arm64/include/asm/seccomp.h index c363871709369c..11c2720404f2d5 100644 --- a/arch/arm64/include/asm/seccomp.h +++ b/arch/arm64/include/asm/seccomp.h @@ -10,11 +10,40 @@ #include -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 #define __NR_seccomp_read_32 __NR_compat_read #define __NR_seccomp_write_32 __NR_compat_write #define __NR_seccomp_exit_32 __NR_compat_exit #define __NR_seccomp_sigreturn_32 __NR_compat_rt_sigreturn +#endif /* CONFIG_AARCH32_EL0 */ + +#ifdef CONFIG_COMPAT +#ifndef __COMPAT_SYSCALL_NR +static inline const int *get_compat_mode1_syscalls(void) +{ +#ifdef CONFIG_AARCH32_EL0 + static const int mode1_syscalls_a32[] = { + __NR_compat_read, __NR_compat_write, + __NR_compat_read, __NR_compat_sigreturn, + 0, /* null terminated */ + }; +#endif + static const int mode1_syscalls_ilp32[] = { + __NR_read, __NR_write, + __NR_exit, __NR_rt_sigreturn, + 0, /* null terminated */ + }; + +#ifdef CONFIG_AARCH32_EL0 + if (is_a32_compat_task()) + return mode1_syscalls_a32; +#endif + return mode1_syscalls_ilp32; +} + +#define get_compat_mode1_syscalls get_compat_mode1_syscalls + +#endif /* __COMPAT_SYSCALL_NR */ #endif /* CONFIG_COMPAT */ #include diff --git a/arch/arm64/include/asm/signal32.h b/arch/arm64/include/asm/signal32.h index 0418c67f2b8b6f..5e57450c8889ac 100644 --- a/arch/arm64/include/asm/signal32.h +++ b/arch/arm64/include/asm/signal32.h @@ -6,32 +6,35 @@ #define __ASM_SIGNAL32_H #ifdef __KERNEL__ -#ifdef CONFIG_COMPAT + +#ifdef CONFIG_AARCH32_EL0 + #include -int compat_setup_frame(int usig, struct ksignal *ksig, sigset_t *set, +int a32_setup_frame(int usig, struct ksignal *ksig, sigset_t *set, struct pt_regs *regs); -int compat_setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set, + +int a32_setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set, struct pt_regs *regs); -void compat_setup_restart_syscall(struct pt_regs *regs); +void a32_setup_restart_syscall(struct pt_regs *regs); #else -static inline int compat_setup_frame(int usid, struct ksignal *ksig, +static inline int a32_setup_frame(int usid, struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { return -ENOSYS; } -static inline int compat_setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set, +static inline int a32_setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { return -ENOSYS; } -static inline void compat_setup_restart_syscall(struct pt_regs *regs) +static inline void a32_setup_restart_syscall(struct pt_regs *regs) { } -#endif /* CONFIG_COMPAT */ +#endif /* CONFIG_AARCH32_EL0 */ #endif /* __KERNEL__ */ #endif /* __ASM_SIGNAL32_H */ diff --git a/arch/arm64/include/asm/signal32_common.h b/arch/arm64/include/asm/signal32_common.h new file mode 100644 index 00000000000000..4b365b31d37e5c --- /dev/null +++ b/arch/arm64/include/asm/signal32_common.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __ASM_SIGNAL32_COMMON_H +#define __ASM_SIGNAL32_COMMON_H + +#ifdef CONFIG_COMPAT + +int put_sigset_t(compat_sigset_t __user *uset, sigset_t *set); +int get_sigset_t(sigset_t *set, const compat_sigset_t __user *uset); + +#endif /* CONFIG_COMPAT*/ + +#endif /* __ASM_SIGNAL32_COMMON_H */ diff --git a/arch/arm64/include/asm/signal_common.h b/arch/arm64/include/asm/signal_common.h new file mode 100644 index 00000000000000..81f2a53030e38c --- /dev/null +++ b/arch/arm64/include/asm/signal_common.h @@ -0,0 +1,303 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (C) 1995-2009 Russell King + * Copyright (C) 2012 ARM Ltd. + * Copyright (C) 2018 Cavium Networks. + */ + +#ifndef __ASM_SIGNAL_COMMON_H +#define __ASM_SIGNAL_COMMON_H + +#include +#include +#include + +#define EXTRA_CONTEXT_SIZE round_up(sizeof(struct extra_context), 16) +#define TERMINATOR_SIZE round_up(sizeof(struct _aarch64_ctx), 16) +#define SIGCONTEXT_RESERVED_SIZE sizeof(((struct sigcontext *)0)->__reserved) +#define RT_SIGFRAME_RESERVED_OFFSET \ + offsetof(struct rt_sigframe, uc.uc_mcontext.__reserved) + +/* + * Sanity limit on the approximate maximum size of signal frame we'll + * try to generate. Stack alignment padding and the frame record are + * not taken into account. This limit is not a guarantee and is + * NOT ABI. + */ +#define SIGFRAME_MAXSZ SZ_64K + +struct rt_sigframe_user_layout { + void __user *sigframe; + struct frame_record __user *next_frame; + + unsigned long size; /* size of allocated sigframe data */ + unsigned long limit; /* largest allowed size */ + + unsigned long fpsimd_offset; + unsigned long esr_offset; + unsigned long sve_offset; + unsigned long extra_offset; + unsigned long end_offset; +}; + +struct user_ctxs { + struct fpsimd_context __user *fpsimd; + struct sve_context __user *sve; +}; + +struct frame_record { + u64 fp; + u64 lr; +}; + +void __user *apply_user_offset(struct rt_sigframe_user_layout const *user, + unsigned long offset); + +int setup_sigframe_layout(struct rt_sigframe_user_layout *user, bool add_all); +int setup_extra_context(char __user *sfp, unsigned long sf_size, + char __user *exprap); +int __parse_user_sigcontext(struct user_ctxs *user, + struct sigcontext __user const *sc, + void __user const *sigframe_base); +#define parse_user_sigcontext(user, sf) \ + __parse_user_sigcontext(user, &(sf)->uc.uc_mcontext, sf) + +int preserve_fpsimd_context(struct fpsimd_context __user *ctx); +int restore_fpsimd_context(struct fpsimd_context __user *ctx); + +#ifdef CONFIG_ARM64_SVE +int preserve_sve_context(struct sve_context __user *ctx); +int restore_sve_fpsimd_context(struct user_ctxs *user); +#else /* ! CONFIG_ARM64_SVE */ + +/* Turn any non-optimised out attempts to use these into a link error: */ +extern int preserve_sve_context(void __user *ctx); +extern int restore_sve_fpsimd_context(struct user_ctxs *user); + +#endif /* ! CONFIG_ARM64_SVE */ + +int sigframe_alloc(struct rt_sigframe_user_layout *user, + unsigned long *offset, size_t size); +int sigframe_alloc_end(struct rt_sigframe_user_layout *user); + +void __setup_return(struct pt_regs *regs, struct k_sigaction *ka, + struct rt_sigframe_user_layout *user, int usig); + +static void init_user_layout(struct rt_sigframe_user_layout *user) +{ + memset(user, 0, sizeof(*user)); + user->size = RT_SIGFRAME_RESERVED_OFFSET; + + user->limit = user->size + SIGCONTEXT_RESERVED_SIZE; + + user->limit -= TERMINATOR_SIZE; + user->limit -= EXTRA_CONTEXT_SIZE; + /* Reserve space for extension and terminator ^ */ +} + +static size_t sigframe_size(struct rt_sigframe_user_layout const *user) +{ + return round_up(max(user->size, sizeof(struct rt_sigframe)), 16); +} + +static int get_sigframe(struct rt_sigframe_user_layout *user, + struct ksignal *ksig, struct pt_regs *regs) +{ + unsigned long sp, sp_top; + int err; + + init_user_layout(user); + err = setup_sigframe_layout(user, false); + if (err) + return err; + + sp = sp_top = sigsp(regs->sp, ksig); + + sp = round_down(sp - sizeof(struct frame_record), 16); + user->next_frame = (struct frame_record __user *)sp; + + sp = round_down(sp, 16) - sigframe_size(user); + user->sigframe = (void __user *)sp; + + /* + * Check that we can actually write to the signal frame. + */ + if (!access_ok(user->sigframe, sp_top - sp)) + return -EFAULT; + + return 0; +} + +static int restore_sigframe(struct pt_regs *regs, + struct rt_sigframe __user *sf) +{ + sigset_t set; + int i, err; + struct user_ctxs user; + + err = get_sigset(&set, &sf->uc.uc_sigmask); + if (err == 0) + set_current_blocked(&set); + + for (i = 0; i < 31; i++) + __get_user_error(regs->regs[i], &sf->uc.uc_mcontext.regs[i], + err); + __get_user_error(regs->sp, &sf->uc.uc_mcontext.sp, err); + __get_user_error(regs->pc, &sf->uc.uc_mcontext.pc, err); + __get_user_error(regs->pstate, &sf->uc.uc_mcontext.pstate, err); + + /* + * Avoid sys_rt_sigreturn() restarting. + */ + forget_syscall(regs); + + err |= !valid_user_regs(®s->user_regs, current); + if (err == 0) + err = parse_user_sigcontext(&user, sf); + + if (err == 0) { + if (!user.fpsimd) + return -EINVAL; + + if (user.sve) { + if (!system_supports_sve()) + return -EINVAL; + + err = restore_sve_fpsimd_context(&user); + } else { + err = restore_fpsimd_context(user.fpsimd); + } + } + + return err; +} + +static int setup_sigframe(struct rt_sigframe_user_layout *user, + struct pt_regs *regs, sigset_t *set) +{ + int i, err = 0; + struct rt_sigframe __user *sf = user->sigframe; + + /* set up the stack frame for unwinding */ + __put_user_error(regs->regs[29], &user->next_frame->fp, err); + __put_user_error(regs->regs[30], &user->next_frame->lr, err); + + for (i = 0; i < 31; i++) + __put_user_error(regs->regs[i], &sf->uc.uc_mcontext.regs[i], + err); + __put_user_error(regs->sp, &sf->uc.uc_mcontext.sp, err); + __put_user_error(regs->pc, &sf->uc.uc_mcontext.pc, err); + __put_user_error(regs->pstate, &sf->uc.uc_mcontext.pstate, err); + + __put_user_error(current->thread.fault_address, + &sf->uc.uc_mcontext.fault_address, err); + + err |= put_sigset(set, &sf->uc.uc_sigmask); + + if (err == 0) { + struct fpsimd_context __user *fpsimd_ctx = + apply_user_offset(user, user->fpsimd_offset); + err |= preserve_fpsimd_context(fpsimd_ctx); + } + + /* fault information, if valid */ + if (err == 0 && user->esr_offset) { + struct esr_context __user *esr_ctx = + apply_user_offset(user, user->esr_offset); + + __put_user_error(ESR_MAGIC, &esr_ctx->head.magic, err); + __put_user_error(sizeof(*esr_ctx), &esr_ctx->head.size, err); + __put_user_error(current->thread.fault_code, + &esr_ctx->esr, err); + } + + /* Scalable Vector Extension state, if present */ + if (system_supports_sve() && err == 0 && user->sve_offset) { + struct sve_context __user *sve_ctx = + apply_user_offset(user, user->sve_offset); + err |= preserve_sve_context(sve_ctx); + } + + if (err == 0 && user->extra_offset) + setup_extra_context((char __user *)user->sigframe, user->size, + (char __user *)apply_user_offset(user, + user->extra_offset)); + + /* set the "end" magic */ + if (err == 0) { + struct _aarch64_ctx __user *end = + apply_user_offset(user, user->end_offset); + + __put_user_error(0, &end->magic, err); + __put_user_error(0, &end->size, err); + } + + return err; +} + +static long __sys_rt_sigreturn(struct pt_regs *regs) +{ + struct rt_sigframe __user *frame; + + /* Always make any pending restarted system calls return -EINTR */ + current->restart_block.fn = do_no_restart_syscall; + + /* + * Since we stacked the signal on a 128-bit boundary, then 'sp' should + * be word aligned here. + */ + if (regs->sp & 15) + goto badframe; + + frame = (struct rt_sigframe __user *)regs->sp; + + if (!access_ok(frame, sizeof(*frame))) + goto badframe; + + if (restore_sigframe(regs, frame)) + goto badframe; + + if (restore_altstack(&frame->uc.uc_stack)) + goto badframe; + + return regs->regs[0]; + +badframe: + arm64_notify_segfault(regs->sp); + return 0; +} + +static int __setup_rt_frame(int usig, struct ksignal *ksig, + sigset_t *set, struct pt_regs *regs) +{ + struct rt_sigframe_user_layout user; + struct rt_sigframe __user *frame; + int err = 0; + + fpsimd_signal_preserve_current_state(); + + if (get_sigframe(&user, ksig, regs)) + return 1; + + frame = user.sigframe; + + __put_user_error(0, &frame->uc.uc_flags, err); + __put_user_error((typeof(frame->uc.uc_link)) 0, + &frame->uc.uc_link, err); + + err |= __save_altstack(&frame->uc.uc_stack, regs->sp); + err |= setup_sigframe(&user, regs, set); + if (err == 0) { + setup_return(regs, &ksig->ka, &user, usig); + if (ksig->ka.sa.sa_flags & SA_SIGINFO) { + err |= copy_siginfo_to_user(&frame->info, &ksig->info); + regs->regs[1] = (unsigned long)&frame->info; + regs->regs[2] = (unsigned long)&frame->uc; + } + } + + return err; +} + +#endif /* __ASM_SIGNAL_COMMON_H */ diff --git a/arch/arm64/include/asm/signal_ilp32.h b/arch/arm64/include/asm/signal_ilp32.h new file mode 100644 index 00000000000000..64333dfaeaa2e1 --- /dev/null +++ b/arch/arm64/include/asm/signal_ilp32.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __ASM_SIGNAL_ILP32_H +#define __ASM_SIGNAL_ILP32_H + +#ifdef CONFIG_ARM64_ILP32 + +#include + +int ilp32_setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set, + struct pt_regs *regs); + +#else + +static inline int ilp32_setup_rt_frame(int usig, struct ksignal *ksig, + sigset_t *set, struct pt_regs *regs) +{ + return -ENOSYS; +} + +#endif /* CONFIG_ARM64_ILP32 */ + +#endif /* __ASM_SIGNAL_ILP32_H */ diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h index 65299a2dcf9cd3..235737f2d33c61 100644 --- a/arch/arm64/include/asm/syscall.h +++ b/arch/arm64/include/asm/syscall.h @@ -13,8 +13,12 @@ typedef long (*syscall_fn_t)(const struct pt_regs *regs); extern const syscall_fn_t sys_call_table[]; -#ifdef CONFIG_COMPAT -extern const syscall_fn_t compat_sys_call_table[]; +#ifdef CONFIG_AARCH32_EL0 +extern const syscall_fn_t a32_sys_call_table[]; +#endif + +#ifdef CONFIG_ARM64_ILP32 +extern const syscall_fn_t ilp32_sys_call_table[]; #endif static inline int syscall_get_nr(struct task_struct *task, @@ -78,9 +82,12 @@ static inline void syscall_set_arguments(struct task_struct *task, */ static inline int syscall_get_arch(struct task_struct *task) { - if (is_compat_thread(task_thread_info(task))) + if (is_a32_compat_thread(task_thread_info(task))) return AUDIT_ARCH_ARM; + else if (is_ilp32_compat_task()) + return AUDIT_ARCH_AARCH64ILP32; + return AUDIT_ARCH_AARCH64; } diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h index 2372e97db29cc8..0b9677d2707fe5 100644 --- a/arch/arm64/include/asm/thread_info.h +++ b/arch/arm64/include/asm/thread_info.h @@ -84,10 +84,11 @@ void arch_release_task_struct(struct task_struct *tsk); #define TIF_FREEZE 19 #define TIF_RESTORE_SIGMASK 20 #define TIF_SINGLESTEP 21 -#define TIF_32BIT 22 /* 32bit process */ +#define TIF_32BIT 22 /* AARCH32 process */ #define TIF_SVE 23 /* Scalable Vector Extension in use */ #define TIF_SVE_VL_INHERIT 24 /* Inherit sve_vl_onexec across exec */ #define TIF_SSBD 25 /* Wants SSB mitigation */ +#define TIF_32BIT_AARCH64 26 /* 32 bit process on AArch64(ILP32) */ #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) @@ -102,6 +103,7 @@ void arch_release_task_struct(struct task_struct *tsk); #define _TIF_FSCHECK (1 << TIF_FSCHECK) #define _TIF_32BIT (1 << TIF_32BIT) #define _TIF_SVE (1 << TIF_SVE) +#define _TIF_32BIT_AARCH64 (1 << TIF_32BIT_AARCH64) #define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \ _TIF_NOTIFY_RESUME | _TIF_FOREIGN_FPSTATE | \ diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h index c9f8dd421c5fb9..5aa0d3f835aed8 100644 --- a/arch/arm64/include/asm/unistd.h +++ b/arch/arm64/include/asm/unistd.h @@ -2,8 +2,13 @@ /* * Copyright (C) 2012 ARM Ltd. */ + #ifdef CONFIG_COMPAT #define __ARCH_WANT_COMPAT_STAT64 +#define __ARCH_WANT_SYS_LLSEEK +#endif + +#ifdef CONFIG_AARCH32_EL0 #define __ARCH_WANT_SYS_GETHOSTNAME #define __ARCH_WANT_SYS_PAUSE #define __ARCH_WANT_SYS_GETPGRP diff --git a/arch/arm64/include/asm/vdso.h b/arch/arm64/include/asm/vdso.h index 1f94ec19903c59..3f5b977b026c1b 100644 --- a/arch/arm64/include/asm/vdso.h +++ b/arch/arm64/include/asm/vdso.h @@ -18,6 +18,12 @@ #include +#ifdef CONFIG_ARM64_ILP32 +#include +#else +#define vdso_offset_sigtramp_ilp32 ({ BUILD_BUG(); 0; }) +#endif + #define VDSO_SYMBOL(base, name) \ ({ \ (void *)(vdso_offset_##name - VDSO_LBASE + (unsigned long)(base)); \ diff --git a/arch/arm64/include/uapi/asm/bitsperlong.h b/arch/arm64/include/uapi/asm/bitsperlong.h index 485d60bee26ca3..9a05a9659e761a 100644 --- a/arch/arm64/include/uapi/asm/bitsperlong.h +++ b/arch/arm64/include/uapi/asm/bitsperlong.h @@ -17,7 +17,14 @@ #ifndef __ASM_BITSPERLONG_H #define __ASM_BITSPERLONG_H -#define __BITS_PER_LONG 64 +#if defined(__LP64__) +/* Assuming __LP64__ will be defined for native ELF64's and not for ILP32. */ +# define __BITS_PER_LONG 64 +#elif defined(__ILP32__) +# define __BITS_PER_LONG 32 +#else +# error "Neither LP64 nor ILP32: unsupported ABI in asm/bitsperlong.h" +#endif #include diff --git a/arch/arm64/include/uapi/asm/unistd.h b/arch/arm64/include/uapi/asm/unistd.h index 4703d218663a2a..78f7f157f2c7a5 100644 --- a/arch/arm64/include/uapi/asm/unistd.h +++ b/arch/arm64/include/uapi/asm/unistd.h @@ -15,9 +15,22 @@ * along with this program. If not, see . */ +/* + * AARCH32 interface for ILP32 syscalls. + */ +#if defined(__ILP32__) || defined(__SYSCALL_COMPAT) +#define __ARCH_WANT_SYNC_FILE_RANGE2 +#endif + +/* + * AARCH64/ILP32 is introduced after the following syscalls were deprecated. + */ +#if !(defined(__ILP32__) || defined(__SYSCALL_COMPAT)) #define __ARCH_WANT_RENAMEAT -#define __ARCH_WANT_NEW_STAT #define __ARCH_WANT_SET_GET_RLIMIT +#endif + +#define __ARCH_WANT_NEW_STAT #define __ARCH_WANT_TIME32_SYSCALLS #include diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 9e7dcb2c31c7a6..b3d0d1ce07182e 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -27,9 +27,12 @@ OBJCOPYFLAGS := --prefix-symbols=__efistub_ $(obj)/%.stub.o: $(obj)/%.o FORCE $(call if_changed,objcopy) -obj-$(CONFIG_COMPAT) += sys32.o signal32.o \ +obj-$(CONFIG_AARCH32_EL0) += binfmt_elf32.o sys32.o signal32.o \ sigreturn32.o sys_compat.o obj-$(CONFIG_KUSER_HELPERS) += kuser32.o +obj-$(CONFIG_ARM64_ILP32) += binfmt_ilp32.o sys_ilp32.o \ + signal_ilp32.o +obj-$(CONFIG_COMPAT) += sys32_common.o signal32_common.o obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_ARM64_MODULE_PLTS) += module-plts.o @@ -62,6 +65,7 @@ obj-$(CONFIG_ARM64_SSBD) += ssbd.o obj-$(CONFIG_ARM64_PTR_AUTH) += pointer_auth.o obj-y += vdso/ probes/ +obj-$(CONFIG_ARM64_ILP32) += vdso-ilp32/ head-y := head.o extra-y += $(head-y) vmlinux.lds diff --git a/arch/arm64/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c index 2ec09debc2bb1d..39a137bdcf46f3 100644 --- a/arch/arm64/kernel/armv8_deprecated.c +++ b/arch/arm64/kernel/armv8_deprecated.c @@ -551,7 +551,7 @@ static int setend_set_hw_mode(bool enable) return 0; } -static int compat_setend_handler(struct pt_regs *regs, u32 big_endian) +static int __a32_setend_handler(struct pt_regs *regs, u32 big_endian) { char *insn; @@ -574,14 +574,14 @@ static int compat_setend_handler(struct pt_regs *regs, u32 big_endian) static int a32_setend_handler(struct pt_regs *regs, u32 instr) { - int rc = compat_setend_handler(regs, (instr >> 9) & 1); + int rc = __a32_setend_handler(regs, (instr >> 9) & 1); arm64_skip_faulting_instruction(regs, 4); return rc; } static int t16_setend_handler(struct pt_regs *regs, u32 instr) { - int rc = compat_setend_handler(regs, (instr >> 3) & 1); + int rc = __a32_setend_handler(regs, (instr >> 3) & 1); arm64_skip_faulting_instruction(regs, 2); return rc; } diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index 02f08768c29846..2bdf2cc52b3ef8 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -104,6 +104,13 @@ int main(void) DEFINE(TVAL_TV_SEC, offsetof(struct timeval, tv_sec)); DEFINE(TSPEC_TV_SEC, offsetof(struct timespec, tv_sec)); BLANK(); +#ifdef CONFIG_COMPAT + DEFINE(COMPAT_TVAL_TV_SEC, offsetof(struct compat_timeval, tv_sec)); + DEFINE(COMPAT_TVAL_TV_USEC, offsetof(struct compat_timeval, tv_usec)); + DEFINE(COMPAT_TSPEC_TV_SEC, offsetof(struct compat_timespec, tv_sec)); + DEFINE(COMPAT_TSPEC_TV_NSEC, offsetof(struct compat_timespec, tv_nsec)); + BLANK(); +#endif DEFINE(TZ_MINWEST, offsetof(struct timezone, tz_minuteswest)); DEFINE(TZ_DSTTIME, offsetof(struct timezone, tz_dsttime)); BLANK(); diff --git a/arch/arm64/kernel/binfmt_elf32.c b/arch/arm64/kernel/binfmt_elf32.c new file mode 100644 index 00000000000000..6f36ae1d418469 --- /dev/null +++ b/arch/arm64/kernel/binfmt_elf32.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Support for AArch32 Linux ELF binaries. + */ + +/* AArch32 EABI. */ +#define EF_ARM_EABI_MASK 0xff000000 + +#define compat_start_thread compat_start_thread +/* + * Unlike the native SET_PERSONALITY macro, the compat version inherits + * READ_IMPLIES_EXEC across a fork() since this is the behaviour on + * arch/arm/. + */ +#define COMPAT_SET_PERSONALITY(ex) \ +({ \ + clear_thread_flag(TIF_32BIT_AARCH64); \ + set_thread_flag(TIF_32BIT); \ +}) + +#define COMPAT_ARCH_DLINFO +#define COMPAT_ELF_HWCAP (a32_elf_hwcap) +#define COMPAT_ELF_HWCAP2 (a32_elf_hwcap2) + +#define compat_arch_setup_additional_pages \ + aarch32_setup_additional_pages + +/* AArch32 EABI. */ +#define compat_elf_check_arch(x) (system_supports_32bit_el0() && \ + ((x)->e_machine == EM_ARM) && \ + ((x)->e_flags & EF_ARM_EABI_MASK)) + +#include "../../../fs/compat_binfmt_elf.c" diff --git a/arch/arm64/kernel/binfmt_ilp32.c b/arch/arm64/kernel/binfmt_ilp32.c new file mode 100644 index 00000000000000..9d69660edef3aa --- /dev/null +++ b/arch/arm64/kernel/binfmt_ilp32.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Support for ILP32 Linux/aarch64 ELF binaries. + */ +#undef CONFIG_AARCH32_EL0 +#define compat_elf_gregset_t elf_gregset_t + +#include +#include +#include + +#undef ELF_CLASS +#define ELF_CLASS ELFCLASS32 + +#undef elfhdr +#undef elf_phdr +#undef elf_shdr +#undef elf_note +#undef elf_addr_t +#define elfhdr elf32_hdr +#define elf_phdr elf32_phdr +#define elf_shdr elf32_shdr +#define elf_note elf32_note +#define elf_addr_t Elf32_Addr + +/* + * Some data types as stored in coredump. + */ +#define user_long_t compat_long_t +#define user_siginfo_t compat_siginfo_t +#define copy_siginfo_to_user copy_siginfo_to_user32 + +/* + * The machine-dependent core note format types are defined in elfcore-compat.h, + * which requires asm/elf.h to define compat_elf_gregset_t et al. + */ +#define elf_prstatus compat_elf_prstatus +#define elf_prpsinfo compat_elf_prpsinfo + +/* AARCH64 ILP32 EABI. */ +#undef elf_check_arch +#define elf_check_arch(x) (((x)->e_machine == EM_AARCH64) \ + && (x)->e_ident[EI_CLASS] == ELFCLASS32) + +#undef SET_PERSONALITY +#define SET_PERSONALITY(ex) \ +do { \ + set_bit(TIF_32BIT, ¤t->mm->context.flags); \ + set_thread_flag(TIF_32BIT_AARCH64); \ + clear_thread_flag(TIF_32BIT); \ +} while (0) + +#undef ARCH_DLINFO +#define ARCH_DLINFO \ +do { \ + NEW_AUX_ENT(AT_SYSINFO_EHDR, \ + (elf_addr_t)(long)current->mm->context.vdso); \ +} while (0) + +#undef ELF_PLATFORM +#ifdef __AARCH64EB__ +#define ELF_PLATFORM ("aarch64_be:ilp32") +#else +#define ELF_PLATFORM ("aarch64:ilp32") +#endif + +#undef ELF_ET_DYN_BASE +#define ELF_ET_DYN_BASE COMPAT_ELF_ET_DYN_BASE + +#undef ELF_HWCAP +#undef ELF_HWCAP2 +#define ELF_HWCAP cpu_get_elf_hwcap() +#define ELF_HWCAP2 cpu_get_elf_hwcap2() + +/* + * Rename a few of the symbols that binfmt_elf.c will define. + * These are all local so the names don't really matter, but it + * might make some debugging less confusing not to duplicate them. + */ +#define elf_format compat_elf_format +#define init_elf_binfmt init_compat_elf_binfmt +#define exit_elf_binfmt exit_compat_elf_binfmt + +#undef ns_to_timeval +#define ns_to_timeval ns_to_compat_timeval + +#include "../../../fs/binfmt_elf.c" diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index aabdabf52fdb5c..2d9b52aa4c64d5 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -28,16 +28,16 @@ /* Kernel representation of AT_HWCAP and AT_HWCAP2 */ static unsigned long elf_hwcap __read_mostly; -#ifdef CONFIG_COMPAT -#define COMPAT_ELF_HWCAP_DEFAULT \ +#ifdef CONFIG_AARCH32_EL0 +#define AARCH32_EL0_ELF_HWCAP_DEFAULT \ (COMPAT_HWCAP_HALF|COMPAT_HWCAP_THUMB|\ COMPAT_HWCAP_FAST_MULT|COMPAT_HWCAP_EDSP|\ COMPAT_HWCAP_TLS|COMPAT_HWCAP_VFP|\ COMPAT_HWCAP_VFPv3|COMPAT_HWCAP_VFPv4|\ COMPAT_HWCAP_NEON|COMPAT_HWCAP_IDIV|\ COMPAT_HWCAP_LPAE) -unsigned int compat_elf_hwcap __read_mostly = COMPAT_ELF_HWCAP_DEFAULT; -unsigned int compat_elf_hwcap2 __read_mostly; +unsigned int a32_elf_hwcap __read_mostly = AARCH32_EL0_ELF_HWCAP_DEFAULT; +unsigned int a32_elf_hwcap2 __read_mostly; #endif DECLARE_BITMAP(cpu_hwcaps, ARM64_NCAPS); @@ -1648,8 +1648,8 @@ static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = { {}, }; -static const struct arm64_cpu_capabilities compat_elf_hwcaps[] = { -#ifdef CONFIG_COMPAT +static const struct arm64_cpu_capabilities a32_elf_hwcaps[] = { +#ifdef CONFIG_AARCH32_EL0 HWCAP_CAP(SYS_ID_ISAR5_EL1, ID_ISAR5_AES_SHIFT, FTR_UNSIGNED, 2, CAP_COMPAT_HWCAP2, COMPAT_HWCAP2_PMULL), HWCAP_CAP(SYS_ID_ISAR5_EL1, ID_ISAR5_AES_SHIFT, FTR_UNSIGNED, 1, CAP_COMPAT_HWCAP2, COMPAT_HWCAP2_AES), HWCAP_CAP(SYS_ID_ISAR5_EL1, ID_ISAR5_SHA1_SHIFT, FTR_UNSIGNED, 1, CAP_COMPAT_HWCAP2, COMPAT_HWCAP2_SHA1), @@ -1665,12 +1665,12 @@ static void __init cap_set_elf_hwcap(const struct arm64_cpu_capabilities *cap) case CAP_HWCAP: cpu_set_feature(cap->hwcap); break; -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 case CAP_COMPAT_HWCAP: - compat_elf_hwcap |= (u32)cap->hwcap; + a32_elf_hwcap |= (u32)cap->hwcap; break; case CAP_COMPAT_HWCAP2: - compat_elf_hwcap2 |= (u32)cap->hwcap; + a32_elf_hwcap2 |= (u32)cap->hwcap; break; #endif default: @@ -1688,12 +1688,12 @@ static bool cpus_have_elf_hwcap(const struct arm64_cpu_capabilities *cap) case CAP_HWCAP: rc = cpu_have_feature(cap->hwcap); break; -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 case CAP_COMPAT_HWCAP: - rc = (compat_elf_hwcap & (u32)cap->hwcap) != 0; + rc = (a32_elf_hwcap & (u32)cap->hwcap) != 0; break; case CAP_COMPAT_HWCAP2: - rc = (compat_elf_hwcap2 & (u32)cap->hwcap) != 0; + rc = (a32_elf_hwcap2 & (u32)cap->hwcap) != 0; break; #endif default: @@ -1933,7 +1933,7 @@ static void verify_local_cpu_capabilities(void) verify_local_elf_hwcaps(arm64_elf_hwcaps); if (system_supports_32bit_el0()) - verify_local_elf_hwcaps(compat_elf_hwcaps); + verify_local_elf_hwcaps(a32_elf_hwcaps); if (system_supports_sve()) verify_sve_features(); @@ -2037,7 +2037,7 @@ void __init setup_cpu_features(void) setup_elf_hwcaps(arm64_elf_hwcaps); if (system_supports_32bit_el0()) - setup_elf_hwcaps(compat_elf_hwcaps); + setup_elf_hwcaps(a32_elf_hwcaps); if (system_uses_ttbr0_pan()) pr_info("emulated: Privileged Access Never (PAN) using TTBR0_EL1 switching\n"); diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c index 0593665fc7b481..adbd69bffe6623 100644 --- a/arch/arm64/kernel/cpuinfo.c +++ b/arch/arm64/kernel/cpuinfo.c @@ -85,7 +85,7 @@ static const char *const hwcap_str[] = { NULL }; -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 static const char *const compat_hwcap_str[] = { "swp", "half", @@ -120,12 +120,12 @@ static const char *const compat_hwcap2_str[] = { "crc32", NULL }; -#endif /* CONFIG_COMPAT */ +#endif /* CONFIG_AARCH32_EL0 */ static int c_show(struct seq_file *m, void *v) { int i, j; - bool compat = personality(current->personality) == PER_LINUX32; + bool aarch32 = personality(current->personality) == PER_LINUX32; for_each_online_cpu(i) { struct cpuinfo_arm64 *cpuinfo = &per_cpu(cpu_data, i); @@ -137,7 +137,7 @@ static int c_show(struct seq_file *m, void *v) * "processor". Give glibc what it expects. */ seq_printf(m, "processor\t: %d\n", i); - if (compat) + if (aarch32) seq_printf(m, "model name\t: ARMv8 Processor rev %d (%s)\n", MIDR_REVISION(midr), COMPAT_ELF_PLATFORM); @@ -152,16 +152,16 @@ static int c_show(struct seq_file *m, void *v) * software which does already (at least for 32-bit). */ seq_puts(m, "Features\t:"); - if (compat) { -#ifdef CONFIG_COMPAT + if (aarch32) { +#ifdef CONFIG_AARCH32_EL0 for (j = 0; compat_hwcap_str[j]; j++) - if (compat_elf_hwcap & (1 << j)) + if (a32_elf_hwcap & (1 << j)) seq_printf(m, " %s", compat_hwcap_str[j]); for (j = 0; compat_hwcap2_str[j]; j++) - if (compat_elf_hwcap2 & (1 << j)) + if (a32_elf_hwcap2 & (1 << j)) seq_printf(m, " %s", compat_hwcap2_str[j]); -#endif /* CONFIG_COMPAT */ +#endif /* CONFIG_AARCH32_EL0 */ } else { for (j = 0; hwcap_str[j]; j++) if (cpu_have_feature(j)) diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c index f8719bd308501e..6ed637cd10b523 100644 --- a/arch/arm64/kernel/debug-monitors.c +++ b/arch/arm64/kernel/debug-monitors.c @@ -342,10 +342,10 @@ int aarch32_break_handler(struct pt_regs *regs) bool bp = false; void __user *pc = (void __user *)instruction_pointer(regs); - if (!compat_user_mode(regs)) + if (!a32_user_mode(regs)) return -EFAULT; - if (compat_thumb_mode(regs)) { + if (a32_thumb_mode(regs)) { /* get 16-bit Thumb instruction */ __le16 instr; get_user(instr, (__le16 __user *)pc); diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 2df8d0a1d980bb..fc530d736d4d77 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -448,7 +448,7 @@ ENTRY(vectors) kernel_ventry 0, fiq_invalid // FIQ 64-bit EL0 kernel_ventry 0, error // Error 64-bit EL0 -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 kernel_ventry 0, sync_compat, 32 // Synchronous 32-bit EL0 kernel_ventry 0, irq_compat, 32 // IRQ 32-bit EL0 kernel_ventry 0, fiq_invalid_compat, 32 // FIQ 32-bit EL0 @@ -517,7 +517,7 @@ el0_error_invalid: inv_entry 0, BAD_ERROR ENDPROC(el0_error_invalid) -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 el0_fiq_invalid_compat: inv_entry 0, BAD_FIQ, 32 ENDPROC(el0_fiq_invalid_compat) @@ -700,7 +700,7 @@ el0_sync: b.ge el0_dbg b el0_inv -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 .align 6 el0_sync_compat: kernel_entry 0, 32 diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S index 2cdacd1c141b97..ffe8f2f24dfbc7 100644 --- a/arch/arm64/kernel/head.S +++ b/arch/arm64/kernel/head.S @@ -548,7 +548,7 @@ set_hcr: msr vpidr_el2, x0 msr vmpidr_el2, x1 -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 msr hstr_el2, xzr // Disable CP15 traps to EL2 #endif diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c index dceb8452094876..2a79e72db25d00 100644 --- a/arch/arm64/kernel/hw_breakpoint.c +++ b/arch/arm64/kernel/hw_breakpoint.c @@ -157,7 +157,7 @@ enum hw_breakpoint_ops { HW_BREAKPOINT_RESTORE }; -static int is_compat_bp(struct perf_event *bp) +static int is_a32_compat_bp(struct perf_event *bp) { struct task_struct *tsk = bp->hw.target; @@ -168,7 +168,7 @@ static int is_compat_bp(struct perf_event *bp) * deprecated behaviour if we use unaligned watchpoints in * AArch64 state. */ - return tsk && is_compat_thread(task_thread_info(tsk)); + return tsk && is_a32_compat_thread(task_thread_info(tsk)); } /** @@ -467,7 +467,7 @@ static int arch_build_bp_info(struct perf_event *bp, * Watchpoints can be of length 1, 2, 4 or 8 bytes. */ if (hw->ctrl.type == ARM_BREAKPOINT_EXECUTE) { - if (is_compat_bp(bp)) { + if (is_a32_compat_bp(bp)) { if (hw->ctrl.len != ARM_BREAKPOINT_LEN_2 && hw->ctrl.len != ARM_BREAKPOINT_LEN_4) return -EINVAL; @@ -525,7 +525,7 @@ int hw_breakpoint_arch_parse(struct perf_event *bp, * AArch32 tasks expect some simple alignment fixups, so emulate * that here. */ - if (is_compat_bp(bp)) { + if (is_a32_compat_bp(bp)) { if (hw->ctrl.len == ARM_BREAKPOINT_LEN_8) alignment_mask = 0x7; else diff --git a/arch/arm64/kernel/perf_callchain.c b/arch/arm64/kernel/perf_callchain.c index 9d63514b983673..0e71ff609a5dcd 100644 --- a/arch/arm64/kernel/perf_callchain.c +++ b/arch/arm64/kernel/perf_callchain.c @@ -52,26 +52,26 @@ user_backtrace(struct frame_tail __user *tail, return buftail.fp; } -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 /* * The registers we're interested in are at the end of the variable * length saved register structure. The fp points at the end of this * structure so the address of this struct is: - * (struct compat_frame_tail *)(xxx->fp)-1 + * (struct a32_frame_tail *)(xxx->fp)-1 * * This code has been adapted from the ARM OProfile support. */ -struct compat_frame_tail { - compat_uptr_t fp; /* a (struct compat_frame_tail *) in compat mode */ +struct a32_frame_tail { + compat_uptr_t fp; /* a (struct a32_frame_tail *) in compat mode */ u32 sp; u32 lr; } __attribute__((packed)); -static struct compat_frame_tail __user * -compat_user_backtrace(struct compat_frame_tail __user *tail, +static struct a32_frame_tail __user * +compat_user_backtrace(struct a32_frame_tail __user *tail, struct perf_callchain_entry_ctx *entry) { - struct compat_frame_tail buftail; + struct a32_frame_tail buftail; unsigned long err; /* Also check accessibility of one struct frame_tail beyond */ @@ -91,13 +91,13 @@ compat_user_backtrace(struct compat_frame_tail __user *tail, * Frame pointers should strictly progress back up the stack * (towards higher addresses). */ - if (tail + 1 >= (struct compat_frame_tail __user *) + if (tail + 1 >= (struct a32_frame_tail __user *) compat_ptr(buftail.fp)) return NULL; - return (struct compat_frame_tail __user *)compat_ptr(buftail.fp) - 1; + return (struct a32_frame_tail __user *)compat_ptr(buftail.fp) - 1; } -#endif /* CONFIG_COMPAT */ +#endif /* CONFIG_AARCH32_EL0 */ void perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs) @@ -109,7 +109,7 @@ void perf_callchain_user(struct perf_callchain_entry_ctx *entry, perf_callchain_store(entry, regs->pc); - if (!compat_user_mode(regs)) { + if (!a32_user_mode(regs)) { /* AARCH64 mode */ struct frame_tail __user *tail; @@ -119,11 +119,11 @@ void perf_callchain_user(struct perf_callchain_entry_ctx *entry, tail && !((unsigned long)tail & 0xf)) tail = user_backtrace(tail, entry); } else { -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 /* AARCH32 compat mode */ - struct compat_frame_tail __user *tail; + struct a32_frame_tail __user *tail; - tail = (struct compat_frame_tail __user *)regs->compat_fp - 1; + tail = (struct a32_frame_tail __user *)regs->compat_fp - 1; while ((entry->nr < entry->max_stack) && tail && !((unsigned long)tail & 0x3)) diff --git a/arch/arm64/kernel/perf_regs.c b/arch/arm64/kernel/perf_regs.c index 0bbac612146eab..f64bcee5b5a626 100644 --- a/arch/arm64/kernel/perf_regs.c +++ b/arch/arm64/kernel/perf_regs.c @@ -19,7 +19,7 @@ u64 perf_reg_value(struct pt_regs *regs, int idx) * - PC has been set in the pt_regs struct in kernel_entry, * - Handle SP and LR here. */ - if (compat_user_mode(regs)) { + if (a32_user_mode(regs)) { if ((u32)idx == PERF_REG_ARM64_SP) return regs->compat_sp; if ((u32)idx == PERF_REG_ARM64_LR) @@ -47,7 +47,7 @@ int perf_reg_validate(u64 mask) u64 perf_reg_abi(struct task_struct *task) { - if (is_compat_thread(task_thread_info(task))) + if (is_a32_compat_thread(task_thread_info(task))) return PERF_SAMPLE_REGS_ABI_32; else return PERF_SAMPLE_REGS_ABI_64; diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 9856395ccdb708..af67c2c122cf90 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -41,7 +41,6 @@ #include #include -#include #include #include #include @@ -209,7 +208,7 @@ static void print_pstate(struct pt_regs *regs) { u64 pstate = regs->pstate; - if (compat_user_mode(regs)) { + if (a32_user_mode(regs)) { printk("pstate: %08llx (%c%c%c%c %c %s %s %c%c%c)\n", pstate, pstate & PSR_AA32_N_BIT ? 'N' : 'n', @@ -243,7 +242,7 @@ void __show_regs(struct pt_regs *regs) int i, top_reg; u64 lr, sp; - if (compat_user_mode(regs)) { + if (a32_user_mode(regs)) { lr = regs->compat_lr; sp = regs->compat_sp; top_reg = 12; @@ -294,7 +293,7 @@ static void tls_thread_flush(void) { write_sysreg(0, tpidr_el0); - if (is_compat_task()) { + if (is_a32_compat_task()) { current->thread.uw.tp_value = 0; /* @@ -378,7 +377,7 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start, *task_user_tls(p) = read_sysreg(tpidr_el0); if (stack_start) { - if (is_compat_thread(task_thread_info(p))) + if (is_a32_compat_thread(task_thread_info(p))) childregs->compat_sp = stack_start; else childregs->sp = stack_start; @@ -423,7 +422,7 @@ static void tls_thread_switch(struct task_struct *next) { tls_preserve_current_state(); - if (is_compat_thread(task_thread_info(next))) + if (is_a32_compat_thread(task_thread_info(next))) write_sysreg(next->thread.uw.tp_value, tpidrro_el0); else if (!arm64_kernel_unmapped_at_el0()) write_sysreg(0, tpidrro_el0); diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index da2441d7b0660c..0d4f08318f3f01 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -30,7 +30,6 @@ #include #include -#include #include #include #include @@ -174,8 +173,8 @@ static void ptrace_hbptriggered(struct perf_event *bp, struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp); const char *desc = "Hardware breakpoint trap (ptrace)"; -#ifdef CONFIG_COMPAT - if (is_compat_task()) { +#ifdef CONFIG_AARCH32_EL0 + if (is_a32_compat_task()) { int si_errno = 0; int i; @@ -1219,6 +1218,10 @@ static const struct user_regset_view user_aarch64_view = { }; #ifdef CONFIG_COMPAT +#include +#endif + +#ifdef CONFIG_AARCH32_EL0 enum compat_regset { REGSET_COMPAT_GPR, REGSET_COMPAT_VFP, @@ -1677,7 +1680,7 @@ static int compat_ptrace_sethbpregs(struct task_struct *tsk, compat_long_t num, } #endif /* CONFIG_HAVE_HW_BREAKPOINT */ -long compat_arch_ptrace(struct task_struct *child, compat_long_t request, +static long compat_a32_ptrace(struct task_struct *child, compat_long_t request, compat_ulong_t caddr, compat_ulong_t cdata) { unsigned long addr = caddr; @@ -1754,20 +1757,35 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, return ret; } -#endif /* CONFIG_COMPAT */ + +#else +#define compat_a32_ptrace(child, request, caddr, cdata) (0) +#endif /* CONFIG_AARCH32_EL0 */ + +#ifdef CONFIG_COMPAT +long compat_arch_ptrace(struct task_struct *child, compat_long_t request, + compat_ulong_t caddr, compat_ulong_t cdata) +{ + if (is_a32_compat_task()) + return compat_a32_ptrace(child, request, caddr, cdata); + + /* ILP32 */ + return compat_ptrace_request(child, request, caddr, cdata); +} +#endif const struct user_regset_view *task_user_regset_view(struct task_struct *task) { -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 /* * Core dumping of 32-bit tasks or compat ptrace requests must use the * user_aarch32_view compatible with arm32. Native ptrace requests on * 32-bit children use an extended user_aarch32_ptrace_view to allow * access to the TLS register. */ - if (is_compat_task()) + if (is_a32_compat_task()) return &user_aarch32_view; - else if (is_compat_thread(task_thread_info(task))) + else if (is_a32_compat_thread(task_thread_info(task))) return &user_aarch32_ptrace_view; #endif return &user_aarch64_view; @@ -1794,7 +1812,7 @@ static void tracehook_report_syscall(struct pt_regs *regs, * A scratch register (ip(r12) on AArch32, x7 on AArch64) is * used to denote syscall entry/exit: */ - regno = (is_compat_task() ? 12 : 7); + regno = (is_a32_compat_task() ? 12 : 7); saved_reg = regs->regs[regno]; regs->regs[regno] = dir; @@ -1912,7 +1930,7 @@ int valid_user_regs(struct user_pt_regs *regs, struct task_struct *task) if (!test_tsk_thread_flag(task, TIF_SINGLESTEP)) regs->pstate &= ~DBG_SPSR_SS; - if (is_compat_thread(task_thread_info(task))) + if (is_a32_compat_thread(task_thread_info(task))) return valid_compat_regs(regs); else return valid_native_regs(regs); diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c index dd2cdc0d5be20c..6a4a94eee97816 100644 --- a/arch/arm64/kernel/signal.c +++ b/arch/arm64/kernel/signal.c @@ -32,6 +32,10 @@ #include #include #include +#include + +#define get_sigset(s, m) __copy_from_user(s, m, sizeof(*s)) +#define put_sigset(s, m) __copy_to_user(m, s, sizeof(*s)) /* * Do a signal return; undo the signal stack. These are aligned to 128-bit. @@ -40,57 +44,12 @@ struct rt_sigframe { struct siginfo info; struct ucontext uc; }; +struct rt_sigframe_user_layout; -struct frame_record { - u64 fp; - u64 lr; -}; - -struct rt_sigframe_user_layout { - struct rt_sigframe __user *sigframe; - struct frame_record __user *next_frame; - - unsigned long size; /* size of allocated sigframe data */ - unsigned long limit; /* largest allowed size */ - - unsigned long fpsimd_offset; - unsigned long esr_offset; - unsigned long sve_offset; - unsigned long extra_offset; - unsigned long end_offset; -}; - -#define BASE_SIGFRAME_SIZE round_up(sizeof(struct rt_sigframe), 16) -#define TERMINATOR_SIZE round_up(sizeof(struct _aarch64_ctx), 16) -#define EXTRA_CONTEXT_SIZE round_up(sizeof(struct extra_context), 16) - -static void init_user_layout(struct rt_sigframe_user_layout *user) -{ - const size_t reserved_size = - sizeof(user->sigframe->uc.uc_mcontext.__reserved); - - memset(user, 0, sizeof(*user)); - user->size = offsetof(struct rt_sigframe, uc.uc_mcontext.__reserved); - - user->limit = user->size + reserved_size; - - user->limit -= TERMINATOR_SIZE; - user->limit -= EXTRA_CONTEXT_SIZE; - /* Reserve space for extension and terminator ^ */ -} - -static size_t sigframe_size(struct rt_sigframe_user_layout const *user) -{ - return round_up(max(user->size, sizeof(struct rt_sigframe)), 16); -} +static void setup_return(struct pt_regs *regs, struct k_sigaction *ka, + struct rt_sigframe_user_layout *user, int usig); -/* - * Sanity limit on the approximate maximum size of signal frame we'll - * try to generate. Stack alignment padding and the frame record are - * not taken into account. This limit is not a guarantee and is - * NOT ABI. - */ -#define SIGFRAME_MAXSZ SZ_64K +#include static int __sigframe_alloc(struct rt_sigframe_user_layout *user, unsigned long *offset, size_t size, bool extend) @@ -135,14 +94,14 @@ static int __sigframe_alloc(struct rt_sigframe_user_layout *user, * signal frame. The offset from the signal frame base address to the * allocated block is assigned to *offset. */ -static int sigframe_alloc(struct rt_sigframe_user_layout *user, +int sigframe_alloc(struct rt_sigframe_user_layout *user, unsigned long *offset, size_t size) { return __sigframe_alloc(user, offset, size, true); } /* Allocate the null terminator record and prevent further allocations */ -static int sigframe_alloc_end(struct rt_sigframe_user_layout *user) +int sigframe_alloc_end(struct rt_sigframe_user_layout *user) { int ret; @@ -159,7 +118,7 @@ static int sigframe_alloc_end(struct rt_sigframe_user_layout *user) return 0; } -static void __user *apply_user_offset( +void __user *apply_user_offset( struct rt_sigframe_user_layout const *user, unsigned long offset) { char __user *base = (char __user *)user->sigframe; @@ -167,7 +126,7 @@ static void __user *apply_user_offset( return base + offset; } -static int preserve_fpsimd_context(struct fpsimd_context __user *ctx) +int preserve_fpsimd_context(struct fpsimd_context __user *ctx) { struct user_fpsimd_state const *fpsimd = ¤t->thread.uw.fpsimd_state; @@ -185,7 +144,7 @@ static int preserve_fpsimd_context(struct fpsimd_context __user *ctx) return err ? -EFAULT : 0; } -static int restore_fpsimd_context(struct fpsimd_context __user *ctx) +int restore_fpsimd_context(struct fpsimd_context __user *ctx) { struct user_fpsimd_state fpsimd; __u32 magic, size; @@ -214,15 +173,9 @@ static int restore_fpsimd_context(struct fpsimd_context __user *ctx) return err ? -EFAULT : 0; } - -struct user_ctxs { - struct fpsimd_context __user *fpsimd; - struct sve_context __user *sve; -}; - #ifdef CONFIG_ARM64_SVE -static int preserve_sve_context(struct sve_context __user *ctx) +int preserve_sve_context(struct sve_context __user *ctx) { int err = 0; u16 reserved[ARRAY_SIZE(ctx->__reserved)]; @@ -254,7 +207,7 @@ static int preserve_sve_context(struct sve_context __user *ctx) return err ? -EFAULT : 0; } -static int restore_sve_fpsimd_context(struct user_ctxs *user) +int restore_sve_fpsimd_context(struct user_ctxs *user) { int err; unsigned int vq; @@ -312,25 +265,18 @@ static int restore_sve_fpsimd_context(struct user_ctxs *user) return err ? -EFAULT : 0; } -#else /* ! CONFIG_ARM64_SVE */ - -/* Turn any non-optimised out attempts to use these into a link error: */ -extern int preserve_sve_context(void __user *ctx); -extern int restore_sve_fpsimd_context(struct user_ctxs *user); - #endif /* ! CONFIG_ARM64_SVE */ - -static int parse_user_sigframe(struct user_ctxs *user, - struct rt_sigframe __user *sf) +int __parse_user_sigcontext(struct user_ctxs *user, + struct sigcontext __user const *sc, + void __user const *sigframe_base) { - struct sigcontext __user *const sc = &sf->uc.uc_mcontext; struct _aarch64_ctx __user *head; char __user *base = (char __user *)&sc->__reserved; size_t offset = 0; size_t limit = sizeof(sc->__reserved); bool have_extra_context = false; - char const __user *const sfp = (char const __user *)sf; + char const __user *const sfp = (char const __user *)sigframe_base; user->fpsimd = NULL; user->sve = NULL; @@ -479,81 +425,11 @@ static int parse_user_sigframe(struct user_ctxs *user, return -EINVAL; } -static int restore_sigframe(struct pt_regs *regs, - struct rt_sigframe __user *sf) -{ - sigset_t set; - int i, err; - struct user_ctxs user; - - err = __copy_from_user(&set, &sf->uc.uc_sigmask, sizeof(set)); - if (err == 0) - set_current_blocked(&set); - - for (i = 0; i < 31; i++) - __get_user_error(regs->regs[i], &sf->uc.uc_mcontext.regs[i], - err); - __get_user_error(regs->sp, &sf->uc.uc_mcontext.sp, err); - __get_user_error(regs->pc, &sf->uc.uc_mcontext.pc, err); - __get_user_error(regs->pstate, &sf->uc.uc_mcontext.pstate, err); - - /* - * Avoid sys_rt_sigreturn() restarting. - */ - forget_syscall(regs); - - err |= !valid_user_regs(®s->user_regs, current); - if (err == 0) - err = parse_user_sigframe(&user, sf); - - if (err == 0) { - if (!user.fpsimd) - return -EINVAL; - - if (user.sve) { - if (!system_supports_sve()) - return -EINVAL; - - err = restore_sve_fpsimd_context(&user); - } else { - err = restore_fpsimd_context(user.fpsimd); - } - } - - return err; -} - SYSCALL_DEFINE0(rt_sigreturn) { struct pt_regs *regs = current_pt_regs(); - struct rt_sigframe __user *frame; - - /* Always make any pending restarted system calls return -EINTR */ - current->restart_block.fn = do_no_restart_syscall; - - /* - * Since we stacked the signal on a 128-bit boundary, then 'sp' should - * be word aligned here. - */ - if (regs->sp & 15) - goto badframe; - - frame = (struct rt_sigframe __user *)regs->sp; - - if (!access_ok(frame, sizeof (*frame))) - goto badframe; - if (restore_sigframe(regs, frame)) - goto badframe; - - if (restore_altstack(&frame->uc.uc_stack)) - goto badframe; - - return regs->regs[0]; - -badframe: - arm64_notify_segfault(regs->sp); - return 0; + return __sys_rt_sigreturn(regs); } /* @@ -563,8 +439,7 @@ SYSCALL_DEFINE0(rt_sigreturn) * this task; otherwise, generates a layout for the current state * of the task. */ -static int setup_sigframe_layout(struct rt_sigframe_user_layout *user, - bool add_all) +int setup_sigframe_layout(struct rt_sigframe_user_layout *user, bool add_all) { int err; @@ -602,122 +477,49 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user, return sigframe_alloc_end(user); } -static int setup_sigframe(struct rt_sigframe_user_layout *user, - struct pt_regs *regs, sigset_t *set) +int setup_extra_context(char __user *sfp, unsigned long sf_size, + char __user *extrap) { - int i, err = 0; - struct rt_sigframe __user *sf = user->sigframe; - - /* set up the stack frame for unwinding */ - __put_user_error(regs->regs[29], &user->next_frame->fp, err); - __put_user_error(regs->regs[30], &user->next_frame->lr, err); - - for (i = 0; i < 31; i++) - __put_user_error(regs->regs[i], &sf->uc.uc_mcontext.regs[i], - err); - __put_user_error(regs->sp, &sf->uc.uc_mcontext.sp, err); - __put_user_error(regs->pc, &sf->uc.uc_mcontext.pc, err); - __put_user_error(regs->pstate, &sf->uc.uc_mcontext.pstate, err); - - __put_user_error(current->thread.fault_address, &sf->uc.uc_mcontext.fault_address, err); - - err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(*set)); - - if (err == 0) { - struct fpsimd_context __user *fpsimd_ctx = - apply_user_offset(user, user->fpsimd_offset); - err |= preserve_fpsimd_context(fpsimd_ctx); - } - - /* fault information, if valid */ - if (err == 0 && user->esr_offset) { - struct esr_context __user *esr_ctx = - apply_user_offset(user, user->esr_offset); - - __put_user_error(ESR_MAGIC, &esr_ctx->head.magic, err); - __put_user_error(sizeof(*esr_ctx), &esr_ctx->head.size, err); - __put_user_error(current->thread.fault_code, &esr_ctx->esr, err); - } - - /* Scalable Vector Extension state, if present */ - if (system_supports_sve() && err == 0 && user->sve_offset) { - struct sve_context __user *sve_ctx = - apply_user_offset(user, user->sve_offset); - err |= preserve_sve_context(sve_ctx); - } - - if (err == 0 && user->extra_offset) { - char __user *sfp = (char __user *)user->sigframe; - char __user *userp = - apply_user_offset(user, user->extra_offset); - - struct extra_context __user *extra; - struct _aarch64_ctx __user *end; - u64 extra_datap; - u32 extra_size; - - extra = (struct extra_context __user *)userp; - userp += EXTRA_CONTEXT_SIZE; + int err = 0; + struct extra_context __user *extra; + struct _aarch64_ctx __user *end; + u64 extra_datap; + u32 extra_size; - end = (struct _aarch64_ctx __user *)userp; - userp += TERMINATOR_SIZE; + extra = (struct extra_context __user *)extrap; + extrap += EXTRA_CONTEXT_SIZE; - /* - * extra_datap is just written to the signal frame. - * The value gets cast back to a void __user * - * during sigreturn. - */ - extra_datap = (__force u64)userp; - extra_size = sfp + round_up(user->size, 16) - userp; + end = (struct _aarch64_ctx __user *)extrap; + extrap += TERMINATOR_SIZE; - __put_user_error(EXTRA_MAGIC, &extra->head.magic, err); - __put_user_error(EXTRA_CONTEXT_SIZE, &extra->head.size, err); - __put_user_error(extra_datap, &extra->datap, err); - __put_user_error(extra_size, &extra->size, err); - - /* Add the terminator */ - __put_user_error(0, &end->magic, err); - __put_user_error(0, &end->size, err); - } + /* + * extra_datap is just written to the signal frame. + * The value gets cast back to a void __user * + * during sigreturn. + */ + extra_datap = (__force u64)extrap; + extra_size = sfp + round_up(sf_size, 16) - extrap; - /* set the "end" magic */ - if (err == 0) { - struct _aarch64_ctx __user *end = - apply_user_offset(user, user->end_offset); + __put_user_error(EXTRA_MAGIC, &extra->head.magic, err); + __put_user_error(EXTRA_CONTEXT_SIZE, &extra->head.size, err); + __put_user_error(extra_datap, &extra->datap, err); + __put_user_error(extra_size, &extra->size, err); - __put_user_error(0, &end->magic, err); - __put_user_error(0, &end->size, err); - } + /* Add the terminator */ + __put_user_error(0, &end->magic, err); + __put_user_error(0, &end->size, err); return err; } -static int get_sigframe(struct rt_sigframe_user_layout *user, - struct ksignal *ksig, struct pt_regs *regs) +void __setup_return(struct pt_regs *regs, struct k_sigaction *ka, + struct rt_sigframe_user_layout *user, int usig) { - unsigned long sp, sp_top; - int err; - - init_user_layout(user); - err = setup_sigframe_layout(user, false); - if (err) - return err; - - sp = sp_top = sigsp(regs->sp, ksig); - - sp = round_down(sp - sizeof(struct frame_record), 16); - user->next_frame = (struct frame_record __user *)sp; - - sp = round_down(sp, 16) - sigframe_size(user); - user->sigframe = (struct rt_sigframe __user *)sp; - - /* - * Check that we can actually write to the signal frame. - */ - if (!access_ok(user->sigframe, sp_top - sp)) - return -EFAULT; + regs->regs[0] = usig; + regs->sp = (unsigned long)user->sigframe; + regs->regs[29] = (unsigned long)&user->next_frame->fp; + regs->pc = (unsigned long)ka->sa.sa_handler; - return 0; } static void setup_return(struct pt_regs *regs, struct k_sigaction *ka, @@ -725,10 +527,7 @@ static void setup_return(struct pt_regs *regs, struct k_sigaction *ka, { __sigrestore_t sigtramp; - regs->regs[0] = usig; - regs->sp = (unsigned long)user->sigframe; - regs->regs[29] = (unsigned long)&user->next_frame->fp; - regs->pc = (unsigned long)ka->sa.sa_handler; + __setup_return(regs, ka, user, usig); if (ka->sa.sa_flags & SA_RESTORER) sigtramp = ka->sa.sa_restorer; @@ -741,38 +540,13 @@ static void setup_return(struct pt_regs *regs, struct k_sigaction *ka, static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { - struct rt_sigframe_user_layout user; - struct rt_sigframe __user *frame; - int err = 0; - - fpsimd_signal_preserve_current_state(); - - if (get_sigframe(&user, ksig, regs)) - return 1; - - frame = user.sigframe; - - __put_user_error(0, &frame->uc.uc_flags, err); - __put_user_error(NULL, &frame->uc.uc_link, err); - - err |= __save_altstack(&frame->uc.uc_stack, regs->sp); - err |= setup_sigframe(&user, regs, set); - if (err == 0) { - setup_return(regs, &ksig->ka, &user, usig); - if (ksig->ka.sa.sa_flags & SA_SIGINFO) { - err |= copy_siginfo_to_user(&frame->info, &ksig->info); - regs->regs[1] = (unsigned long)&frame->info; - regs->regs[2] = (unsigned long)&frame->uc; - } - } - - return err; + return __setup_rt_frame(usig, ksig, set, regs); } static void setup_restart_syscall(struct pt_regs *regs) { - if (is_compat_task()) - compat_setup_restart_syscall(regs); + if (is_a32_compat_task()) + a32_setup_restart_syscall(regs); else regs->regs[8] = __NR_restart_syscall; } @@ -792,11 +566,13 @@ static void handle_signal(struct ksignal *ksig, struct pt_regs *regs) /* * Set up the stack frame */ - if (is_compat_task()) { + if (is_a32_compat_task()) { if (ksig->ka.sa.sa_flags & SA_SIGINFO) - ret = compat_setup_rt_frame(usig, ksig, oldset, regs); + ret = a32_setup_rt_frame(usig, ksig, oldset, regs); else - ret = compat_setup_frame(usig, ksig, oldset, regs); + ret = a32_setup_frame(usig, ksig, oldset, regs); + } else if (is_ilp32_compat_task()) { + ret = ilp32_setup_rt_frame(usig, ksig, oldset, regs); } else { ret = setup_rt_frame(usig, ksig, oldset, regs); } @@ -837,7 +613,7 @@ static void do_signal(struct pt_regs *regs) */ if (syscall) { continue_addr = regs->pc; - restart_addr = continue_addr - (compat_thumb_mode(regs) ? 2 : 4); + restart_addr = continue_addr - (a32_thumb_mode(regs) ? 2 : 4); retval = regs->regs[0]; /* diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c index 331d1e5acad4aa..e6962fba5131de 100644 --- a/arch/arm64/kernel/signal32.c +++ b/arch/arm64/kernel/signal32.c @@ -16,10 +16,11 @@ #include #include #include +#include #include #include -struct compat_sigcontext { +struct a32_sigcontext { /* We always set these two fields to 0 */ compat_ulong_t trap_no; compat_ulong_t error_code; @@ -45,17 +46,17 @@ struct compat_sigcontext { compat_ulong_t fault_address; }; -struct compat_ucontext { +struct a32_ucontext { compat_ulong_t uc_flags; compat_uptr_t uc_link; compat_stack_t uc_stack; - struct compat_sigcontext uc_mcontext; + struct a32_sigcontext uc_mcontext; compat_sigset_t uc_sigmask; int __unused[32 - (sizeof (compat_sigset_t) / sizeof (int))]; compat_ulong_t uc_regspace[128] __attribute__((__aligned__(8))); }; -struct compat_vfp_sigframe { +struct a32_vfp_sigframe { compat_ulong_t magic; compat_ulong_t size; struct compat_user_vfp { @@ -70,56 +71,34 @@ struct compat_vfp_sigframe { } __attribute__((__aligned__(8))); #define VFP_MAGIC 0x56465001 -#define VFP_STORAGE_SIZE sizeof(struct compat_vfp_sigframe) +#define VFP_STORAGE_SIZE sizeof(struct a32_vfp_sigframe) #define FSR_WRITE_SHIFT (11) -struct compat_aux_sigframe { - struct compat_vfp_sigframe vfp; +struct a32_aux_sigframe { + struct a32_vfp_sigframe vfp; /* Something that isn't a valid magic number for any coprocessor. */ unsigned long end_magic; } __attribute__((__aligned__(8))); -struct compat_sigframe { - struct compat_ucontext uc; +struct a32_sigframe { + struct a32_ucontext uc; compat_ulong_t retcode[2]; }; -struct compat_rt_sigframe { +struct a32_rt_sigframe { struct compat_siginfo info; - struct compat_sigframe sig; + struct a32_sigframe sig; }; #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) -static inline int put_sigset_t(compat_sigset_t __user *uset, sigset_t *set) -{ - compat_sigset_t cset; - - cset.sig[0] = set->sig[0] & 0xffffffffull; - cset.sig[1] = set->sig[0] >> 32; - - return copy_to_user(uset, &cset, sizeof(*uset)); -} - -static inline int get_sigset_t(sigset_t *set, - const compat_sigset_t __user *uset) -{ - compat_sigset_t s32; - - if (copy_from_user(&s32, uset, sizeof(*uset))) - return -EFAULT; - - set->sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); - return 0; -} - /* * VFP save/restore code. * * We have to be careful with endianness, since the fpsimd context-switch - * code operates on 128-bit (Q) register values whereas the compat ABI + * code operates on 128-bit (Q) register values whereas the a32 ABI * uses an array of 64-bit (D) registers. Consequently, we need to swap * the two halves of each Q register when running on a big-endian CPU. */ @@ -136,7 +115,7 @@ union __fpsimd_vreg { }; }; -static int compat_preserve_vfp_context(struct compat_vfp_sigframe __user *frame) +static int a32_preserve_vfp_context(struct a32_vfp_sigframe __user *frame) { struct user_fpsimd_state const *fpsimd = ¤t->thread.uw.fpsimd_state; @@ -186,7 +165,7 @@ static int compat_preserve_vfp_context(struct compat_vfp_sigframe __user *frame) return err ? -EFAULT : 0; } -static int compat_restore_vfp_context(struct compat_vfp_sigframe __user *frame) +static int a32_restore_vfp_context(struct a32_vfp_sigframe __user *frame) { struct user_fpsimd_state fpsimd; compat_ulong_t magic = VFP_MAGIC; @@ -226,12 +205,12 @@ static int compat_restore_vfp_context(struct compat_vfp_sigframe __user *frame) return err ? -EFAULT : 0; } -static int compat_restore_sigframe(struct pt_regs *regs, - struct compat_sigframe __user *sf) +static int a32_restore_sigframe(struct pt_regs *regs, + struct a32_sigframe __user *sf) { int err; sigset_t set; - struct compat_aux_sigframe __user *aux; + struct a32_aux_sigframe __user *aux; unsigned long psr; err = get_sigset_t(&set, &sf->uc.uc_sigmask); @@ -267,9 +246,9 @@ static int compat_restore_sigframe(struct pt_regs *regs, err |= !valid_user_regs(®s->user_regs, current); - aux = (struct compat_aux_sigframe __user *) sf->uc.uc_regspace; + aux = (struct a32_aux_sigframe __user *) sf->uc.uc_regspace; if (err == 0) - err |= compat_restore_vfp_context(&aux->vfp); + err |= a32_restore_vfp_context(&aux->vfp); return err; } @@ -277,7 +256,7 @@ static int compat_restore_sigframe(struct pt_regs *regs, COMPAT_SYSCALL_DEFINE0(sigreturn) { struct pt_regs *regs = current_pt_regs(); - struct compat_sigframe __user *frame; + struct a32_sigframe __user *frame; /* Always make any pending restarted system calls return -EINTR */ current->restart_block.fn = do_no_restart_syscall; @@ -290,12 +269,12 @@ COMPAT_SYSCALL_DEFINE0(sigreturn) if (regs->compat_sp & 7) goto badframe; - frame = (struct compat_sigframe __user *)regs->compat_sp; + frame = (struct a32_sigframe __user *)regs->compat_sp; if (!access_ok(frame, sizeof (*frame))) goto badframe; - if (compat_restore_sigframe(regs, frame)) + if (a32_restore_sigframe(regs, frame)) goto badframe; return regs->regs[0]; @@ -308,7 +287,7 @@ COMPAT_SYSCALL_DEFINE0(sigreturn) COMPAT_SYSCALL_DEFINE0(rt_sigreturn) { struct pt_regs *regs = current_pt_regs(); - struct compat_rt_sigframe __user *frame; + struct a32_rt_sigframe __user *frame; /* Always make any pending restarted system calls return -EINTR */ current->restart_block.fn = do_no_restart_syscall; @@ -321,12 +300,12 @@ COMPAT_SYSCALL_DEFINE0(rt_sigreturn) if (regs->compat_sp & 7) goto badframe; - frame = (struct compat_rt_sigframe __user *)regs->compat_sp; + frame = (struct a32_rt_sigframe __user *)regs->compat_sp; if (!access_ok(frame, sizeof (*frame))) goto badframe; - if (compat_restore_sigframe(regs, &frame->sig)) + if (a32_restore_sigframe(regs, &frame->sig)) goto badframe; if (compat_restore_altstack(&frame->sig.uc.uc_stack)) @@ -339,7 +318,7 @@ COMPAT_SYSCALL_DEFINE0(rt_sigreturn) return 0; } -static void __user *compat_get_sigframe(struct ksignal *ksig, +static void __user *a32_get_sigframe(struct ksignal *ksig, struct pt_regs *regs, int framesize) { @@ -360,7 +339,7 @@ static void __user *compat_get_sigframe(struct ksignal *ksig, return frame; } -static void compat_setup_return(struct pt_regs *regs, struct k_sigaction *ka, +static void a32_setup_return(struct pt_regs *regs, struct k_sigaction *ka, compat_ulong_t __user *rc, void __user *frame, int usig) { @@ -403,10 +382,10 @@ static void compat_setup_return(struct pt_regs *regs, struct k_sigaction *ka, regs->pstate = spsr; } -static int compat_setup_sigframe(struct compat_sigframe __user *sf, +static int a32_setup_sigframe(struct a32_sigframe __user *sf, struct pt_regs *regs, sigset_t *set) { - struct compat_aux_sigframe __user *aux; + struct a32_aux_sigframe __user *aux; unsigned long psr = pstate_to_compat_psr(regs->pstate); int err = 0; @@ -429,7 +408,7 @@ static int compat_setup_sigframe(struct compat_sigframe __user *sf, __put_user_error(psr, &sf->uc.uc_mcontext.arm_cpsr, err); __put_user_error((compat_ulong_t)0, &sf->uc.uc_mcontext.trap_no, err); - /* set the compat FSR WnR */ + /* set the aarch32 FSR WnR */ __put_user_error(!!(current->thread.fault_code & ESR_ELx_WNR) << FSR_WRITE_SHIFT, &sf->uc.uc_mcontext.error_code, err); __put_user_error(current->thread.fault_address, &sf->uc.uc_mcontext.fault_address, err); @@ -437,25 +416,25 @@ static int compat_setup_sigframe(struct compat_sigframe __user *sf, err |= put_sigset_t(&sf->uc.uc_sigmask, set); - aux = (struct compat_aux_sigframe __user *) sf->uc.uc_regspace; + aux = (struct a32_aux_sigframe __user *) sf->uc.uc_regspace; if (err == 0) - err |= compat_preserve_vfp_context(&aux->vfp); + err |= a32_preserve_vfp_context(&aux->vfp); __put_user_error(0, &aux->end_magic, err); return err; } /* - * 32-bit signal handling routines called from signal.c + * aarch32-bit signal handling routines called from signal.c */ -int compat_setup_rt_frame(int usig, struct ksignal *ksig, +int a32_setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { - struct compat_rt_sigframe __user *frame; + struct a32_rt_sigframe __user *frame; int err = 0; - frame = compat_get_sigframe(ksig, regs, sizeof(*frame)); + frame = a32_get_sigframe(ksig, regs, sizeof(*frame)); if (!frame) return 1; @@ -467,10 +446,10 @@ int compat_setup_rt_frame(int usig, struct ksignal *ksig, err |= __compat_save_altstack(&frame->sig.uc.uc_stack, regs->compat_sp); - err |= compat_setup_sigframe(&frame->sig, regs, set); + err |= a32_setup_sigframe(&frame->sig, regs, set); if (err == 0) { - compat_setup_return(regs, &ksig->ka, frame->sig.retcode, frame, usig); + a32_setup_return(regs, &ksig->ka, frame->sig.retcode, frame, usig); regs->regs[1] = (compat_ulong_t)(unsigned long)&frame->info; regs->regs[2] = (compat_ulong_t)(unsigned long)&frame->sig.uc; } @@ -478,27 +457,27 @@ int compat_setup_rt_frame(int usig, struct ksignal *ksig, return err; } -int compat_setup_frame(int usig, struct ksignal *ksig, sigset_t *set, +int a32_setup_frame(int usig, struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { - struct compat_sigframe __user *frame; + struct a32_sigframe __user *frame; int err = 0; - frame = compat_get_sigframe(ksig, regs, sizeof(*frame)); + frame = a32_get_sigframe(ksig, regs, sizeof(*frame)); if (!frame) return 1; __put_user_error(0x5ac3c35a, &frame->uc.uc_flags, err); - err |= compat_setup_sigframe(frame, regs, set); + err |= a32_setup_sigframe(frame, regs, set); if (err == 0) - compat_setup_return(regs, &ksig->ka, frame->retcode, frame, usig); + a32_setup_return(regs, &ksig->ka, frame->retcode, frame, usig); return err; } -void compat_setup_restart_syscall(struct pt_regs *regs) +void a32_setup_restart_syscall(struct pt_regs *regs) { regs->regs[7] = __NR_compat_restart_syscall; } diff --git a/arch/arm64/kernel/signal32_common.c b/arch/arm64/kernel/signal32_common.c new file mode 100644 index 00000000000000..4844d2c5fd8966 --- /dev/null +++ b/arch/arm64/kernel/signal32_common.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Based on arch/arm/kernel/signal.c + * + * Copyright (C) 1995-2009 Russell King + * Copyright (C) 2012 ARM Ltd. + * Modified by Will Deacon + */ + +#include +#include +#include + +#include +#include + +int put_sigset_t(compat_sigset_t __user *uset, sigset_t *set) +{ + compat_sigset_t cset; + + cset.sig[0] = set->sig[0] & 0xffffffffull; + cset.sig[1] = set->sig[0] >> 32; + + return copy_to_user(uset, &cset, sizeof(*uset)); +} + +int get_sigset_t(sigset_t *set, const compat_sigset_t __user *uset) +{ + compat_sigset_t s32; + + if (copy_from_user(&s32, uset, sizeof(*uset))) + return -EFAULT; + + set->sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + return 0; +} diff --git a/arch/arm64/kernel/signal_ilp32.c b/arch/arm64/kernel/signal_ilp32.c new file mode 100644 index 00000000000000..64090e2addb27e --- /dev/null +++ b/arch/arm64/kernel/signal_ilp32.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (C) 1995-2009 Russell King + * Copyright (C) 2012 ARM Ltd. + * Copyright (C) 2018 Cavium Networks. + * Yury Norov + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define get_sigset(s, m) get_sigset_t(s, m) +#define put_sigset(s, m) put_sigset_t(m, s) + +#define restore_altstack(stack) compat_restore_altstack(stack) +#define __save_altstack(stack, sp) __compat_save_altstack(stack, sp) +#define copy_siginfo_to_user(frame_info, ksig_info) \ + copy_siginfo_to_user32(frame_info, ksig_info) + +#define setup_return(regs, ka, user_layout, usig) \ +{ \ + __setup_return(regs, ka, user_layout, usig); \ + regs->regs[30] = \ + (unsigned long)VDSO_SYMBOL(current->mm->context.vdso, \ + sigtramp_ilp32); \ +} + +struct ilp32_ucontext { + u32 uc_flags; + u32 uc_link; + compat_stack_t uc_stack; + compat_sigset_t uc_sigmask; + /* glibc uses a 1024-bit sigset_t */ + __u8 __unused[1024 / 8 - sizeof(compat_sigset_t)]; + /* last for future expansion */ + struct sigcontext uc_mcontext; +}; + +struct rt_sigframe { + struct compat_siginfo info; + struct ilp32_ucontext uc; +}; + +#include + +COMPAT_SYSCALL_DEFINE0(ilp32_rt_sigreturn) +{ + struct pt_regs *regs = current_pt_regs(); + + return __sys_rt_sigreturn(regs); +} + +int ilp32_setup_rt_frame(int usig, struct ksignal *ksig, + sigset_t *set, struct pt_regs *regs) +{ + return __setup_rt_frame(usig, ksig, set, regs); +} diff --git a/arch/arm64/kernel/sys32.c b/arch/arm64/kernel/sys32.c index fc40386afb1bba..7577acaae9117b 100644 --- a/arch/arm64/kernel/sys32.c +++ b/arch/arm64/kernel/sys32.c @@ -20,108 +20,6 @@ asmlinkage long compat_sys_sigreturn(void); asmlinkage long compat_sys_rt_sigreturn(void); -COMPAT_SYSCALL_DEFINE3(aarch32_statfs64, const char __user *, pathname, - compat_size_t, sz, struct compat_statfs64 __user *, buf) -{ - /* - * 32-bit ARM applies an OABI compatibility fixup to statfs64 and - * fstatfs64 regardless of whether OABI is in use, and therefore - * arbitrary binaries may rely upon it, so we must do the same. - * For more details, see commit: - * - * 713c481519f19df9 ("[ARM] 3108/2: old ABI compat: statfs64 and - * fstatfs64") - */ - if (sz == 88) - sz = 84; - - return kcompat_sys_statfs64(pathname, sz, buf); -} - -COMPAT_SYSCALL_DEFINE3(aarch32_fstatfs64, unsigned int, fd, compat_size_t, sz, - struct compat_statfs64 __user *, buf) -{ - /* see aarch32_statfs64 */ - if (sz == 88) - sz = 84; - - return kcompat_sys_fstatfs64(fd, sz, buf); -} - -/* - * Note: off_4k is always in units of 4K. If we can't do the - * requested offset because it is not page-aligned, we return -EINVAL. - */ -COMPAT_SYSCALL_DEFINE6(aarch32_mmap2, unsigned long, addr, unsigned long, len, - unsigned long, prot, unsigned long, flags, - unsigned long, fd, unsigned long, off_4k) -{ - if (off_4k & (~PAGE_MASK >> 12)) - return -EINVAL; - - off_4k >>= (PAGE_SHIFT - 12); - - return ksys_mmap_pgoff(addr, len, prot, flags, fd, off_4k); -} - -#ifdef CONFIG_CPU_BIG_ENDIAN -#define arg_u32p(name) u32, name##_hi, u32, name##_lo -#else -#define arg_u32p(name) u32, name##_lo, u32, name##_hi -#endif - -#define arg_u64(name) (((u64)name##_hi << 32) | name##_lo) - -COMPAT_SYSCALL_DEFINE6(aarch32_pread64, unsigned int, fd, char __user *, buf, - size_t, count, u32, __pad, arg_u32p(pos)) -{ - return ksys_pread64(fd, buf, count, arg_u64(pos)); -} - -COMPAT_SYSCALL_DEFINE6(aarch32_pwrite64, unsigned int, fd, - const char __user *, buf, size_t, count, u32, __pad, - arg_u32p(pos)) -{ - return ksys_pwrite64(fd, buf, count, arg_u64(pos)); -} - -COMPAT_SYSCALL_DEFINE4(aarch32_truncate64, const char __user *, pathname, - u32, __pad, arg_u32p(length)) -{ - return ksys_truncate(pathname, arg_u64(length)); -} - -COMPAT_SYSCALL_DEFINE4(aarch32_ftruncate64, unsigned int, fd, u32, __pad, - arg_u32p(length)) -{ - return ksys_ftruncate(fd, arg_u64(length)); -} - -COMPAT_SYSCALL_DEFINE5(aarch32_readahead, int, fd, u32, __pad, - arg_u32p(offset), size_t, count) -{ - return ksys_readahead(fd, arg_u64(offset), count); -} - -COMPAT_SYSCALL_DEFINE6(aarch32_fadvise64_64, int, fd, int, advice, - arg_u32p(offset), arg_u32p(len)) -{ - return ksys_fadvise64_64(fd, arg_u64(offset), arg_u64(len), advice); -} - -COMPAT_SYSCALL_DEFINE6(aarch32_sync_file_range2, int, fd, unsigned int, flags, - arg_u32p(offset), arg_u32p(nbytes)) -{ - return ksys_sync_file_range(fd, arg_u64(offset), arg_u64(nbytes), - flags); -} - -COMPAT_SYSCALL_DEFINE6(aarch32_fallocate, int, fd, int, mode, - arg_u32p(offset), arg_u32p(len)) -{ - return ksys_fallocate(fd, mode, arg_u64(offset), arg_u64(len)); -} - #undef __SYSCALL #define __SYSCALL(nr, sym) asmlinkage long __arm64_##sym(const struct pt_regs *); #include @@ -129,7 +27,7 @@ COMPAT_SYSCALL_DEFINE6(aarch32_fallocate, int, fd, int, mode, #undef __SYSCALL #define __SYSCALL(nr, sym) [nr] = __arm64_##sym, -const syscall_fn_t compat_sys_call_table[__NR_compat_syscalls] = { +const syscall_fn_t a32_sys_call_table[__NR_compat_syscalls] = { [0 ... __NR_compat_syscalls - 1] = __arm64_sys_ni_syscall, #include }; diff --git a/arch/arm64/kernel/sys32_common.c b/arch/arm64/kernel/sys32_common.c new file mode 100644 index 00000000000000..bbf81d8991babd --- /dev/null +++ b/arch/arm64/kernel/sys32_common.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +COMPAT_SYSCALL_DEFINE3(aarch32_statfs64, const char __user *, pathname, + compat_size_t, sz, struct compat_statfs64 __user *, buf) +{ + /* + * 32-bit ARM applies an OABI compatibility fixup to statfs64 and + * fstatfs64 regardless of whether OABI is in use, and therefore + * arbitrary binaries may rely upon it, so we must do the same. + * For more details, see commit: + * + * 713c481519f19df9 ("[ARM] 3108/2: old ABI compat: statfs64 and + * fstatfs64") + */ + if (sz == 88) + sz = 84; + + return kcompat_sys_statfs64(pathname, sz, buf); +} + +COMPAT_SYSCALL_DEFINE3(aarch32_fstatfs64, unsigned int, fd, compat_size_t, sz, + struct compat_statfs64 __user *, buf) +{ + /* see aarch32_statfs64 */ + if (sz == 88) + sz = 84; + + return kcompat_sys_fstatfs64(fd, sz, buf); +} + +/* + * Note: off_4k is always in units of 4K. If we can't do the + * requested offset because it is not page-aligned, we return -EINVAL. + */ +COMPAT_SYSCALL_DEFINE6(aarch32_mmap2, unsigned long, addr, unsigned long, len, + unsigned long, prot, unsigned long, flags, + unsigned long, fd, unsigned long, off_4k) +{ + if (off_4k & (~PAGE_MASK >> 12)) + return -EINVAL; + + off_4k >>= (PAGE_SHIFT - 12); + + return ksys_mmap_pgoff(addr, len, prot, flags, fd, off_4k); +} + +#ifdef CONFIG_CPU_BIG_ENDIAN +#define arg_u32p(name) u32, name##_hi, u32, name##_lo +#else +#define arg_u32p(name) u32, name##_lo, u32, name##_hi +#endif + +#define arg_u64(name) (((u64)name##_hi << 32) | name##_lo) + +COMPAT_SYSCALL_DEFINE6(aarch32_pread64, unsigned int, fd, char __user *, buf, + size_t, count, u32, __pad, arg_u32p(pos)) +{ + return ksys_pread64(fd, buf, count, arg_u64(pos)); +} + +COMPAT_SYSCALL_DEFINE6(aarch32_pwrite64, unsigned int, fd, + const char __user *, buf, size_t, count, u32, __pad, + arg_u32p(pos)) +{ + return ksys_pwrite64(fd, buf, count, arg_u64(pos)); +} + +COMPAT_SYSCALL_DEFINE4(aarch32_truncate64, const char __user *, pathname, + u32, __pad, arg_u32p(length)) +{ + return ksys_truncate(pathname, arg_u64(length)); +} + +COMPAT_SYSCALL_DEFINE4(aarch32_ftruncate64, unsigned int, fd, u32, __pad, + arg_u32p(length)) +{ + return ksys_ftruncate(fd, arg_u64(length)); +} + +COMPAT_SYSCALL_DEFINE5(aarch32_readahead, int, fd, u32, __pad, + arg_u32p(offset), size_t, count) +{ + return ksys_readahead(fd, arg_u64(offset), count); +} + +COMPAT_SYSCALL_DEFINE6(aarch32_fadvise64_64, int, fd, int, advice, + arg_u32p(offset), arg_u32p(len)) +{ + return ksys_fadvise64_64(fd, arg_u64(offset), arg_u64(len), advice); +} + +COMPAT_SYSCALL_DEFINE6(aarch32_sync_file_range2, int, fd, unsigned int, flags, + arg_u32p(offset), arg_u32p(nbytes)) +{ + return ksys_sync_file_range(fd, arg_u64(offset), arg_u64(nbytes), + flags); +} + +COMPAT_SYSCALL_DEFINE6(aarch32_fallocate, int, fd, int, mode, + arg_u32p(offset), arg_u32p(len)) +{ + return ksys_fallocate(fd, mode, arg_u64(offset), arg_u64(len)); +} diff --git a/arch/arm64/kernel/sys_compat.c b/arch/arm64/kernel/sys_compat.c index f1cb649594271c..19f7b8b6db4f9c 100644 --- a/arch/arm64/kernel/sys_compat.c +++ b/arch/arm64/kernel/sys_compat.c @@ -20,7 +20,7 @@ #include static long -__do_compat_cache_op(unsigned long start, unsigned long end) +__do_a32_cache_op(unsigned long start, unsigned long end) { long ret; @@ -42,7 +42,7 @@ __do_compat_cache_op(unsigned long start, unsigned long end) } static inline long -do_compat_cache_op(unsigned long start, unsigned long end, int flags) +do_a32_cache_op(unsigned long start, unsigned long end, int flags) { if (end < start || flags) return -EINVAL; @@ -50,12 +50,12 @@ do_compat_cache_op(unsigned long start, unsigned long end, int flags) if (!access_ok((const void __user *)start, end - start)) return -EFAULT; - return __do_compat_cache_op(start, end); + return __do_a32_cache_op(start, end); } /* * Handle all unrecognised system calls. */ -long compat_arm_syscall(struct pt_regs *regs, int scno) +long a32_arm_syscall(struct pt_regs *regs, int scno) { void __user *addr; @@ -75,7 +75,7 @@ long compat_arm_syscall(struct pt_regs *regs, int scno) * the specified region). */ case __ARM_NR_compat_cacheflush: - return do_compat_cache_op(regs->regs[0], regs->regs[1], regs->regs[2]); + return do_a32_cache_op(regs->regs[0], regs->regs[1], regs->regs[2]); case __ARM_NR_compat_set_tls: current->thread.uw.tp_value = regs->regs[0]; @@ -101,7 +101,7 @@ long compat_arm_syscall(struct pt_regs *regs, int scno) } addr = (void __user *)instruction_pointer(regs) - - (compat_thumb_mode(regs) ? 2 : 4); + (a32_thumb_mode(regs) ? 2 : 4); arm64_notify_die("Oops - bad compat syscall(2)", regs, SIGILL, ILL_ILLTRP, addr, scno); diff --git a/arch/arm64/kernel/sys_ilp32.c b/arch/arm64/kernel/sys_ilp32.c new file mode 100644 index 00000000000000..1c8db8f8341a68 --- /dev/null +++ b/arch/arm64/kernel/sys_ilp32.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * AArch64- ILP32 specific system calls implementation + * Copyright (C) 2018 Marvell. + */ + +#define __SYSCALL_COMPAT + +#include +#include +#include + +#include + +/* + * AARCH32 requires 4-page alignment for shared memory, + * but AARCH64 - only 1 page. This is the only difference + * between compat and native sys_shmat(). So ILP32 just pick + * AARCH64 version. + */ +#define __arm64_compat_sys_shmat __arm64_sys_shmat + +/* + * ILP32 needs special handling for some ptrace requests. + */ +#define __arm64_sys_ptrace __arm64_compat_sys_ptrace + +/* + * Using AARCH32 interface for syscalls that take 64-bit + * parameters in registers. + */ +#define __arm64_compat_sys_fadvise64_64 __arm64_compat_sys_aarch32_fadvise64_64 +#define __arm64_compat_sys_fallocate __arm64_compat_sys_aarch32_fallocate +#define __arm64_compat_sys_ftruncate64 __arm64_compat_sys_aarch32_ftruncate64 +#define __arm64_compat_sys_pread64 __arm64_compat_sys_aarch32_pread64 +#define __arm64_compat_sys_pwrite64 __arm64_compat_sys_aarch32_pwrite64 +#define __arm64_compat_sys_readahead __arm64_compat_sys_aarch32_readahead +#define __arm64_compat_sys_sync_file_range2 __arm64_compat_sys_aarch32_sync_file_range2 +#define __arm64_compat_sys_truncate64 __arm64_compat_sys_aarch32_truncate64 +#define __arm64_sys_mmap2 __arm64_compat_sys_aarch32_mmap2 + +/* + * Using AARCH32 interface for syscalls that take the size of + * struct statfs as an argument, as it's calculated differently + * in kernel and user spaces. + */ +#define __arm64_compat_sys_fstatfs64 __arm64_compat_sys_aarch32_fstatfs64 +#define __arm64_compat_sys_statfs64 __arm64_compat_sys_aarch32_statfs64 + +/* + * Using old interface for IPC syscalls that should handle IPC_64 flag. + */ +#define __arm64_compat_sys_semctl __arm64_compat_sys_old_semctl +#define __arm64_compat_sys_msgctl __arm64_compat_sys_old_msgctl +#define __arm64_compat_sys_shmctl __arm64_compat_sys_old_shmctl + +/* + * Using custom wrapper for rt_sigreturn() to handle custom + * struct rt_sigframe. + */ +#define __arm64_compat_sys_rt_sigreturn __arm64_compat_sys_ilp32_rt_sigreturn + +/* + * Wrappers to pass the pt_regs argument. + */ +#define sys_personality sys_arm64_personality + +asmlinkage long sys_ni_syscall(const struct pt_regs *); +#define __arm64_sys_ni_syscall sys_ni_syscall + +#undef __SYSCALL +#define __SYSCALL(nr, sym) asmlinkage long __arm64_##sym(const struct pt_regs *); +#include + +#undef __SYSCALL +#define __SYSCALL(nr, sym) [nr] = (syscall_fn_t)__arm64_##sym, + +const syscall_fn_t ilp32_sys_call_table[__NR_syscalls] = { + [0 ... __NR_syscalls - 1] = __arm64_sys_ni_syscall, +#include +}; diff --git a/arch/arm64/kernel/syscall.c b/arch/arm64/kernel/syscall.c index 871c739f060aca..49caf0df531663 100644 --- a/arch/arm64/kernel/syscall.c +++ b/arch/arm64/kernel/syscall.c @@ -14,15 +14,15 @@ #include #include -long compat_arm_syscall(struct pt_regs *regs, int scno); +long a32_arm_syscall(struct pt_regs *regs, int scno); long sys_ni_syscall(void); static long do_ni_syscall(struct pt_regs *regs, int scno) { -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 long ret; - if (is_compat_task()) { - ret = compat_arm_syscall(regs, scno); + if (is_a32_compat_task()) { + ret = a32_arm_syscall(regs, scno); if (ret != -ENOSYS) return ret; } @@ -154,16 +154,39 @@ static inline void sve_user_discard(void) sve_user_disable(); } +#ifdef CONFIG_ARM64_ILP32 +static inline void delouse_pt_regs(struct pt_regs *regs) +{ + regs->regs[0] &= UINT_MAX; + regs->regs[1] &= UINT_MAX; + regs->regs[2] &= UINT_MAX; + regs->regs[3] &= UINT_MAX; + regs->regs[4] &= UINT_MAX; + regs->regs[5] &= UINT_MAX; + regs->regs[6] &= UINT_MAX; + regs->regs[7] &= UINT_MAX; +} +#endif + asmlinkage void el0_svc_handler(struct pt_regs *regs) { + const syscall_fn_t *t = sys_call_table; + +#ifdef CONFIG_ARM64_ILP32 + if (is_ilp32_compat_task()) { + t = ilp32_sys_call_table; + delouse_pt_regs(regs); + } +#endif + sve_user_discard(); - el0_svc_common(regs, regs->regs[8], __NR_syscalls, sys_call_table); + el0_svc_common(regs, regs->regs[8], __NR_syscalls, t); } -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 asmlinkage void el0_svc_compat_handler(struct pt_regs *regs) { el0_svc_common(regs, regs->regs[7], __NR_compat_syscalls, - compat_sys_call_table); + a32_sys_call_table); } #endif diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index 985721a1264c90..604e99d9f5b487 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -322,7 +323,7 @@ static int call_undef_hook(struct pt_regs *regs) if (probe_kernel_address((__force __le32 *)pc, instr_le)) goto exit; instr = le32_to_cpu(instr_le); - } else if (compat_thumb_mode(regs)) { + } else if (a32_thumb_mode(regs)) { /* 16-bit Thumb instruction */ __le16 instr_le; if (get_user(instr_le, (__le16 __user *)pc)) diff --git a/arch/arm64/kernel/vdso-ilp32/.gitignore b/arch/arm64/kernel/vdso-ilp32/.gitignore new file mode 100644 index 00000000000000..61806c3fd68b06 --- /dev/null +++ b/arch/arm64/kernel/vdso-ilp32/.gitignore @@ -0,0 +1,2 @@ +vdso-ilp32.lds +vdso-ilp32-offsets.h diff --git a/arch/arm64/kernel/vdso-ilp32/Makefile b/arch/arm64/kernel/vdso-ilp32/Makefile new file mode 100644 index 00000000000000..a0942f60e2a8cc --- /dev/null +++ b/arch/arm64/kernel/vdso-ilp32/Makefile @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: GPL-2.0 + +# +# Building a vDSO image for AArch64. +# +# Author: Will Deacon +# Heavily based on the vDSO Makefiles for other archs. +# + +obj-ilp32-vdso := gettimeofday-ilp32.o note-ilp32.o sigreturn-ilp32.o + +# Build rules +targets := $(obj-ilp32-vdso) vdso-ilp32.so vdso-ilp32.so.dbg +obj-ilp32-vdso := $(addprefix $(obj)/, $(obj-ilp32-vdso)) + +ccflags-y := -shared -fno-common -fno-builtin +ccflags-y += -nostdlib -Wl,-soname=linux-ilp32-vdso.so.1 \ + $(call cc-ldoption, -Wl$(comma)--hash-style=sysv) + +# Disable gcov profiling for VDSO code +GCOV_PROFILE := n + +# Workaround for bare-metal (ELF) toolchains that neglect to pass -shared +# down to collect2, resulting in silent corruption of the vDSO image. +ccflags-y += -Wl,-shared + +obj-y += vdso-ilp32.o +extra-y += vdso-ilp32.lds +CPPFLAGS_vdso-ilp32.lds += -P -C -U$(ARCH) -mabi=ilp32 + +# Force dependency (incbin is bad) +$(obj)/vdso-ilp32.o : $(obj)/vdso-ilp32.so + +# Link rule for the .so file, .lds has to be first +$(obj)/vdso-ilp32.so.dbg: $(src)/vdso-ilp32.lds $(obj-ilp32-vdso) + $(call if_changed,vdso-ilp32ld) + +# Strip rule for the .so file +$(obj)/%.so: OBJCOPYFLAGS := -S +$(obj)/%.so: $(obj)/%.so.dbg FORCE + $(call if_changed,objcopy) + +# Generate VDSO offsets using helper script +gen-vdsosym := $(srctree)/$(src)/../vdso/gen_vdso_offsets.sh +quiet_cmd_vdsosym = VDSOSYM $@ +define cmd_vdsosym + $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@ +endef + +include/generated/vdso-ilp32-offsets.h: $(obj)/vdso-ilp32.so.dbg FORCE + $(call if_changed,vdsosym) + +# Assembly rules for the .S files +#$(obj-ilp32-vdso): %.o: $(src)/../vdso/$(subst -ilp32,,%.S) +# $(call if_changed_dep,vdso-ilp32as) + +$(obj)/gettimeofday-ilp32.o: $(src)/../vdso/gettimeofday.S + $(call if_changed_dep,vdso-ilp32as) + +$(obj)/note-ilp32.o: $(src)/../vdso/note.S + $(call if_changed_dep,vdso-ilp32as) + +# This one should be fine because ILP32 uses the same generic +# __NR_rt_sigreturn syscall number. +$(obj)/sigreturn-ilp32.o: $(src)/../vdso/sigreturn.S + $(call if_changed_dep,vdso-ilp32as) + +# Actual build commands +quiet_cmd_vdso-ilp32ld = VDSOILP32L $@ + cmd_vdso-ilp32ld = $(CC) $(c_flags) -mabi=ilp32 -Wl,-n -Wl,-T $^ -o $@ +quiet_cmd_vdso-ilp32as = VDSOILP32A $@ + cmd_vdso-ilp32as = $(CC) $(a_flags) -mabi=ilp32 -c -o $@ $< + +# Install commands for the unstripped file +quiet_cmd_vdso_install = INSTALL $@ + cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@ + +vdso-ilp32.so: $(obj)/vdso-ilp32.so.dbg + @mkdir -p $(MODLIB)/vdso + $(call cmd,vdso_install) + +vdso_install: vdso-ilp32.so diff --git a/arch/arm64/kernel/vdso-ilp32/vdso-ilp32.S b/arch/arm64/kernel/vdso-ilp32/vdso-ilp32.S new file mode 100644 index 00000000000000..52509a507d266c --- /dev/null +++ b/arch/arm64/kernel/vdso-ilp32/vdso-ilp32.S @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (C) 2012 ARM Limited + * Author: Will Deacon + */ + +#include +#include +#include +#include + + __PAGE_ALIGNED_DATA + + .globl vdso_ilp32_start, vdso_ilp32_end + .balign PAGE_SIZE +vdso_ilp32_start: + .incbin "arch/arm64/kernel/vdso-ilp32/vdso-ilp32.so" + .balign PAGE_SIZE +vdso_ilp32_end: + + .previous diff --git a/arch/arm64/kernel/vdso-ilp32/vdso-ilp32.lds.S b/arch/arm64/kernel/vdso-ilp32/vdso-ilp32.lds.S new file mode 100644 index 00000000000000..4508bd83af123b --- /dev/null +++ b/arch/arm64/kernel/vdso-ilp32/vdso-ilp32.lds.S @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * GNU linker script for the VDSO library. + * + * Copyright (C) 2012 ARM Limited + * Author: Will Deacon + * Heavily based on the vDSO linker scripts for other archs. + */ + +#include +#include +#include + +SECTIONS +{ + PROVIDE(_vdso_data = . - PAGE_SIZE); + . = VDSO_LBASE + SIZEOF_HEADERS; + + .hash : { *(.hash) } :text + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + + .note : { *(.note.*) } :text :note + + . = ALIGN(16); + + .text : { *(.text*) } :text =0xd503201f + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + + .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr + .eh_frame : { KEEP (*(.eh_frame)) } :text + + .dynamic : { *(.dynamic) } :text :dynamic + + .rodata : { *(.rodata*) } :text + + _end = .; + PROVIDE(end = .); + + /DISCARD/ : { + *(.note.GNU-stack) + *(.data .data.* .gnu.linkonce.d.* .sdata*) + *(.bss .sbss .dynbss .dynsbss) + } +} + +/* + * We must supply the ELF program headers explicitly to get just one + * PT_LOAD segment, and set the flags explicitly to make segments read-only. + */ +PHDRS +{ + text PT_LOAD FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */ + dynamic PT_DYNAMIC FLAGS(4); /* PF_R */ + note PT_NOTE FLAGS(4); /* PF_R */ + eh_frame_hdr PT_GNU_EH_FRAME; +} + +/* + * This controls what symbols we export from the DSO. + */ +VERSION +{ + LINUX_4.12 { + global: + __kernel_rt_sigreturn; + __kernel_gettimeofday; + __kernel_clock_gettime; + __kernel_clock_getres; + local: *; + }; +} + +/* + * Make the sigreturn code visible to the kernel. + */ +VDSO_sigtramp_ilp32 = __kernel_rt_sigreturn; diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c index 663b166241d07a..ebea7e069226a1 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -26,8 +26,13 @@ #include #include -extern char vdso_start[], vdso_end[]; -static unsigned long vdso_pages __ro_after_init; +extern char vdso_lp64_start[], vdso_lp64_end[]; +static unsigned long vdso_lp64_pages __ro_after_init; + +#ifdef CONFIG_ARM64_ILP32 +extern char vdso_ilp32_start[], vdso_ilp32_end[]; +static unsigned long vdso_ilp32_pages __ro_after_init; +#endif /* * The vDSO data page. @@ -38,7 +43,7 @@ static union { } vdso_data_store __page_aligned_data; struct vdso_data *vdso_data = &vdso_data_store.data; -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 /* * Create and map the vectors page for AArch32 tasks. */ @@ -165,13 +170,13 @@ int aarch32_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) up_write(&mm->mmap_sem); return ret; } -#endif /* CONFIG_COMPAT */ +#endif /* CONFIG_AARCH32_EL0 */ static int vdso_mremap(const struct vm_special_mapping *sm, struct vm_area_struct *new_vma) { unsigned long new_size = new_vma->vm_end - new_vma->vm_start; - unsigned long vdso_size = vdso_end - vdso_start; + unsigned long vdso_size = vdso_lp64_end - vdso_lp64_start; if (vdso_size != new_size) return -EINVAL; @@ -181,7 +186,7 @@ static int vdso_mremap(const struct vm_special_mapping *sm, return 0; } -static struct vm_special_mapping vdso_spec[2] __ro_after_init = { +static struct vm_special_mapping vdso_lp64_spec[2] __ro_after_init = { { .name = "[vvar]", }, @@ -191,9 +196,23 @@ static struct vm_special_mapping vdso_spec[2] __ro_after_init = { }, }; -static int __init vdso_init(void) +#ifdef CONFIG_ARM64_ILP32 +static struct vm_special_mapping vdso_ilp32_spec[2] __ro_after_init = { + { + .name = "[vvar]", + }, + { + .name = "[vdso]", + }, +}; +#endif + +static int __init vdso_init(char *vdso_start, char *vdso_end, + unsigned long *vdso_pagesp, + struct vm_special_mapping *vdso_spec) { int i; + unsigned long vdso_pages; struct page **vdso_pagelist; unsigned long pfn; @@ -203,6 +222,7 @@ static int __init vdso_init(void) } vdso_pages = (vdso_end - vdso_start) >> PAGE_SHIFT; + *vdso_pagesp = vdso_pages; /* Allocate the vDSO pagelist, plus a page for the data. */ vdso_pagelist = kcalloc(vdso_pages + 1, sizeof(struct page *), @@ -225,7 +245,22 @@ static int __init vdso_init(void) return 0; } -arch_initcall(vdso_init); + +static int __init vdso_lp64_init(void) +{ + return vdso_init(vdso_lp64_start, vdso_lp64_end, + &vdso_lp64_pages, vdso_lp64_spec); +} +arch_initcall(vdso_lp64_init); + +#ifdef CONFIG_ARM64_ILP32 +static int __init vdso_ilp32_init(void) +{ + return vdso_init(vdso_ilp32_start, vdso_ilp32_end, + &vdso_ilp32_pages, vdso_ilp32_spec); +} +arch_initcall(vdso_ilp32_init); +#endif int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) @@ -233,8 +268,17 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, struct mm_struct *mm = current->mm; unsigned long vdso_base, vdso_text_len, vdso_mapping_len; void *ret; + unsigned long pages = vdso_lp64_pages; + struct vm_special_mapping *vdso_spec = vdso_lp64_spec; + +#ifdef CONFIG_ARM64_ILP32 + if (is_ilp32_compat_task()) { + pages = vdso_ilp32_pages; + vdso_spec = vdso_ilp32_spec; + } +#endif - vdso_text_len = vdso_pages << PAGE_SHIFT; + vdso_text_len = pages << PAGE_SHIFT; /* Be sure to map the data page */ vdso_mapping_len = vdso_text_len + PAGE_SIZE; diff --git a/arch/arm64/kernel/vdso/gettimeofday.S b/arch/arm64/kernel/vdso/gettimeofday.S index 80f780f56e0d25..a5e9ae7be88cc3 100644 --- a/arch/arm64/kernel/vdso/gettimeofday.S +++ b/arch/arm64/kernel/vdso/gettimeofday.S @@ -14,6 +14,16 @@ #define NSEC_PER_SEC_LO16 0xca00 #define NSEC_PER_SEC_HI16 0x3b9a +#ifdef __LP64__ +#define PTR_REG(n) x##n +#define OFFSET(n) n +#define DELOUSE(n) +#else +#define PTR_REG(n) w##n +#define OFFSET(n) COMPAT_##n +#define DELOUSE(n) mov w##n, w##n +#endif + vdso_data .req x6 seqcnt .req w7 w_tmp .req w8 @@ -115,7 +125,7 @@ x_tmp .req x8 .if \shift == 1 lsr x11, x11, x12 .endif - stp x10, x11, [x1, #TSPEC_TV_SEC] + stp PTR_REG(10), PTR_REG(11), [x1, #OFFSET(TSPEC_TV_SEC)] mov x0, xzr ret .endm @@ -132,6 +142,8 @@ x_tmp .req x8 /* int __kernel_gettimeofday(struct timeval *tv, struct timezone *tz); */ ENTRY(__kernel_gettimeofday) .cfi_startproc + DELOUSE(0) + DELOUSE(1) adr vdso_data, _vdso_data /* If tv is NULL, skip to the timezone code. */ cbz x0, 2f @@ -156,7 +168,7 @@ ENTRY(__kernel_gettimeofday) mov x13, #1000 lsl x13, x13, x12 udiv x11, x11, x13 - stp x10, x11, [x0, #TVAL_TV_SEC] + stp PTR_REG(10), PTR_REG(11), [x0, #OFFSET(TVAL_TV_SEC)] 2: /* If tz is NULL, return 0. */ cbz x1, 3f @@ -178,6 +190,7 @@ ENDPROC(__kernel_gettimeofday) /* int __kernel_clock_gettime(clockid_t clock_id, struct timespec *tp); */ ENTRY(__kernel_clock_gettime) .cfi_startproc + DELOUSE(1) cmp w0, #JUMPSLOT_MAX b.hi syscall adr vdso_data, _vdso_data @@ -292,6 +305,7 @@ ENDPROC(__kernel_clock_gettime) /* int __kernel_clock_getres(clockid_t clock_id, struct timespec *res); */ ENTRY(__kernel_clock_getres) .cfi_startproc + DELOUSE(1) cmp w0, #CLOCK_REALTIME ccmp w0, #CLOCK_MONOTONIC, #0x4, ne ccmp w0, #CLOCK_MONOTONIC_RAW, #0x4, ne @@ -307,7 +321,7 @@ ENTRY(__kernel_clock_getres) ldr x2, 5f 2: cbz x1, 3f - stp xzr, x2, [x1] + stp PTR_REG(zr), PTR_REG(2), [x1] 3: /* res == NULL. */ mov w0, wzr diff --git a/arch/arm64/kernel/vdso/vdso.S b/arch/arm64/kernel/vdso/vdso.S index d1414fee5274b7..c581d8aec7134a 100644 --- a/arch/arm64/kernel/vdso/vdso.S +++ b/arch/arm64/kernel/vdso/vdso.S @@ -10,12 +10,12 @@ #include #include - .globl vdso_start, vdso_end + .globl vdso_lp64_start, vdso_lp64_end .section .rodata .balign PAGE_SIZE -vdso_start: +vdso_lp64_start: .incbin "arch/arm64/kernel/vdso/vdso.so" .balign PAGE_SIZE -vdso_end: +vdso_lp64_end: .previous diff --git a/arch/arm64/mm/mmap.c b/arch/arm64/mm/mmap.c index b050641b51392b..bb0140afed6641 100644 --- a/arch/arm64/mm/mmap.c +++ b/arch/arm64/mm/mmap.c @@ -43,7 +43,7 @@ unsigned long arch_mmap_rnd(void) unsigned long rnd; #ifdef CONFIG_COMPAT - if (test_thread_flag(TIF_32BIT)) + if (is_compat_task()) rnd = get_random_long() & ((1UL << mmap_rnd_compat_bits) - 1); else #endif diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 07e57a49d1e8d1..15dd64e231ffda 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -806,8 +806,8 @@ static void arch_timer_evtstrm_enable(int divider) #else elf_hwcap |= HWCAP_EVTSTRM; #endif -#ifdef CONFIG_COMPAT - compat_elf_hwcap |= COMPAT_HWCAP_EVTSTRM; +#ifdef CONFIG_AARCH32_EL0 + a32_elf_hwcap |= COMPAT_HWCAP_EVTSTRM; #endif cpumask_set_cpu(smp_processor_id(), &evtstrm_available); } diff --git a/include/linux/sched.h b/include/linux/sched.h index 11837410690f01..5b43bd25bdfa9d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -29,6 +29,7 @@ #include #include #include +#include /* task_struct member predeclarations (sorted alphabetically): */ struct audit_context; diff --git a/include/linux/thread_bits.h b/include/linux/thread_bits.h new file mode 100644 index 00000000000000..0f6fe55744f1c5 --- /dev/null +++ b/include/linux/thread_bits.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* Common low-level thread bits accessors */ + +#ifndef _LINUX_THREAD_BITS_H +#define _LINUX_THREAD_BITS_H + +#ifndef __ASSEMBLY__ + +/* + * For per-arch arch_within_stack_frames() implementations, defined in + * asm/thread_info.h. + */ +enum { + BAD_STACK = -1, + NOT_STACK = 0, + GOOD_FRAME, + GOOD_STACK, +}; + +#ifdef CONFIG_THREAD_INFO_IN_TASK +/* + * For CONFIG_THREAD_INFO_IN_TASK kernels we need for the + * definition of current, but for !CONFIG_THREAD_INFO_IN_TASK kernels, + * including can cause a circular dependency on some platforms. + */ +#include +#define current_thread_info() ((struct thread_info *)current) +#endif + +#include +#include + +/* + * flag set/clear/test wrappers + * - pass TIF_xxxx constants to these functions + */ + +static inline void set_ti_thread_flag(struct thread_info *ti, int flag) +{ + set_bit(flag, (unsigned long *)&ti->flags); +} + +static inline void clear_ti_thread_flag(struct thread_info *ti, int flag) +{ + clear_bit(flag, (unsigned long *)&ti->flags); +} + +static inline void update_ti_thread_flag(struct thread_info *ti, int flag, + bool value) +{ + if (value) + set_ti_thread_flag(ti, flag); + else + clear_ti_thread_flag(ti, flag); +} + +static inline int test_and_set_ti_thread_flag(struct thread_info *ti, int flag) +{ + return test_and_set_bit(flag, (unsigned long *)&ti->flags); +} + +static inline int test_and_clear_ti_thread_flag(struct thread_info *ti, int flag) +{ + return test_and_clear_bit(flag, (unsigned long *)&ti->flags); +} + +static inline int test_ti_thread_flag(struct thread_info *ti, int flag) +{ + return test_bit(flag, (unsigned long *)&ti->flags); +} + +#define set_thread_flag(flag) \ + set_ti_thread_flag(current_thread_info(), flag) +#define clear_thread_flag(flag) \ + clear_ti_thread_flag(current_thread_info(), flag) +#define update_thread_flag(flag, value) \ + update_ti_thread_flag(current_thread_info(), flag, value) +#define test_and_set_thread_flag(flag) \ + test_and_set_ti_thread_flag(current_thread_info(), flag) +#define test_and_clear_thread_flag(flag) \ + test_and_clear_ti_thread_flag(current_thread_info(), flag) +#define test_thread_flag(flag) \ + test_ti_thread_flag(current_thread_info(), flag) + +#endif /* !__ASSEMBLY__ */ +#endif /* _LINUX_THREAD_BITS_H */ diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h index 8d8821b3689a2e..06ca9c157980b9 100644 --- a/include/linux/thread_info.h +++ b/include/linux/thread_info.h @@ -11,30 +11,9 @@ #include #include #include - -#ifdef CONFIG_THREAD_INFO_IN_TASK -/* - * For CONFIG_THREAD_INFO_IN_TASK kernels we need for the - * definition of current, but for !CONFIG_THREAD_INFO_IN_TASK kernels, - * including can cause a circular dependency on some platforms. - */ -#include -#define current_thread_info() ((struct thread_info *)current) -#endif +#include #include - -/* - * For per-arch arch_within_stack_frames() implementations, defined in - * asm/thread_info.h. - */ -enum { - BAD_STACK = -1, - NOT_STACK = 0, - GOOD_FRAME, - GOOD_STACK, -}; - #include #ifdef __KERNEL__ @@ -45,58 +24,6 @@ enum { #define THREADINFO_GFP (GFP_KERNEL_ACCOUNT | __GFP_ZERO) -/* - * flag set/clear/test wrappers - * - pass TIF_xxxx constants to these functions - */ - -static inline void set_ti_thread_flag(struct thread_info *ti, int flag) -{ - set_bit(flag, (unsigned long *)&ti->flags); -} - -static inline void clear_ti_thread_flag(struct thread_info *ti, int flag) -{ - clear_bit(flag, (unsigned long *)&ti->flags); -} - -static inline void update_ti_thread_flag(struct thread_info *ti, int flag, - bool value) -{ - if (value) - set_ti_thread_flag(ti, flag); - else - clear_ti_thread_flag(ti, flag); -} - -static inline int test_and_set_ti_thread_flag(struct thread_info *ti, int flag) -{ - return test_and_set_bit(flag, (unsigned long *)&ti->flags); -} - -static inline int test_and_clear_ti_thread_flag(struct thread_info *ti, int flag) -{ - return test_and_clear_bit(flag, (unsigned long *)&ti->flags); -} - -static inline int test_ti_thread_flag(struct thread_info *ti, int flag) -{ - return test_bit(flag, (unsigned long *)&ti->flags); -} - -#define set_thread_flag(flag) \ - set_ti_thread_flag(current_thread_info(), flag) -#define clear_thread_flag(flag) \ - clear_ti_thread_flag(current_thread_info(), flag) -#define update_thread_flag(flag, value) \ - update_ti_thread_flag(current_thread_info(), flag, value) -#define test_and_set_thread_flag(flag) \ - test_and_set_ti_thread_flag(current_thread_info(), flag) -#define test_and_clear_thread_flag(flag) \ - test_and_clear_ti_thread_flag(current_thread_info(), flag) -#define test_thread_flag(flag) \ - test_ti_thread_flag(current_thread_info(), flag) - #define tif_need_resched() test_thread_flag(TIF_NEED_RESCHED) #ifndef CONFIG_HAVE_ARCH_WITHIN_STACK_FRAMES diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index a1280af20336dc..55acae47707d35 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -376,6 +376,7 @@ enum { #define __AUDIT_ARCH_LE 0x40000000 #define AUDIT_ARCH_AARCH64 (EM_AARCH64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) +#define AUDIT_ARCH_AARCH64ILP32 (EM_AARCH64|__AUDIT_ARCH_LE) #define AUDIT_ARCH_ALPHA (EM_ALPHA|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) #define AUDIT_ARCH_ARCOMPACT (EM_ARCOMPACT|__AUDIT_ARCH_LE) #define AUDIT_ARCH_ARCOMPACTBE (EM_ARCOMPACT) diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 705887f63288d4..b84f0f2cb9c797 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -896,6 +896,24 @@ static int ptrace_regset(struct task_struct *task, int req, unsigned int type, EXPORT_SYMBOL_GPL(task_user_regset_view); #endif +static int ptrace_setsigmask(struct task_struct *child, sigset_t *new_set) +{ + sigdelsetmask(new_set, sigmask(SIGKILL)|sigmask(SIGSTOP)); + + /* + * Every thread does recalc_sigpending() after resume, so + * retarget_shared_pending() and recalc_sigpending() are not + * called here. + */ + spin_lock_irq(&child->sighand->siglock); + child->blocked = *new_set; + spin_unlock_irq(&child->sighand->siglock); + + clear_tsk_restore_sigmask(child); + + return 0; +} + int ptrace_request(struct task_struct *child, long request, unsigned long addr, unsigned long data) { @@ -974,20 +992,7 @@ int ptrace_request(struct task_struct *child, long request, break; } - sigdelsetmask(&new_set, sigmask(SIGKILL)|sigmask(SIGSTOP)); - - /* - * Every thread does recalc_sigpending() after resume, so - * retarget_shared_pending() and recalc_sigpending() are not - * called here. - */ - spin_lock_irq(&child->sighand->siglock); - child->blocked = new_set; - spin_unlock_irq(&child->sighand->siglock); - - clear_tsk_restore_sigmask(child); - - ret = 0; + ret = ptrace_setsigmask(child, &new_set); break; } @@ -1206,6 +1211,7 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request, { compat_ulong_t __user *datap = compat_ptr(data); compat_ulong_t word; + sigset_t new_set; kernel_siginfo_t siginfo; int ret; @@ -1245,6 +1251,24 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request, if (!ret) ret = ptrace_setsiginfo(child, &siginfo); break; + case PTRACE_GETSIGMASK: + if (addr != sizeof(compat_sigset_t)) + return -EINVAL; + + ret = put_compat_sigset((compat_sigset_t __user *) datap, + &child->blocked, sizeof(compat_sigset_t)); + break; + case PTRACE_SETSIGMASK: + if (addr != sizeof(compat_sigset_t)) + return -EINVAL; + + ret = get_compat_sigset(&new_set, + (compat_sigset_t __user *) datap); + if (ret) + break; + + ret = ptrace_setsigmask(child, &new_set); + break; #ifdef CONFIG_HAVE_ARCH_TRACEHOOK case PTRACE_GETREGSET: case PTRACE_SETREGSET: