diff options
author | Andrey Konovalov <andrey.konovalov@linaro.org> | 2015-01-09 20:55:59 +0300 |
---|---|---|
committer | Andrey Konovalov <andrey.konovalov@linaro.org> | 2015-01-09 20:55:59 +0300 |
commit | ad8b904ccdb1d223e938d039218f8d9b9ff4fe72 (patch) | |
tree | 97dd28f5330b42c97f5fda5786fa178bc3bb3c1f | |
parent | 1f10c105e2b67903430a1e5b674796daaaf867a6 (diff) | |
parent | 0b5ed2617ffc868673c7916e7469294e5cc2128d (diff) |
Merge branch 'tracking-ilp32' into merge-linux-linaroll-20150109.0
42 files changed, 989 insertions, 86 deletions
diff --git a/Documentation/arm64/ilp32.txt b/Documentation/arm64/ilp32.txt new file mode 100644 index 000000000000..0863aa4eac36 --- /dev/null +++ b/Documentation/arm64/ilp32.txt @@ -0,0 +1,57 @@ + ILP32 AARCH64 SYSCALL ABI + ===================== + +Author: Andrew Pinski <apinski@cavium.com> +Date: May 23, 2014 + +This document describes the ILP32 syscall ABI and where it differs +from the generic linux syscall interface. +ILP32 sets __kernel_long_t and __kernel_ulong_t both to 64bit +(long long). This effects the following types: +* time_t: unsigned long long +* clock_t: unsigned long long +* fsword_t: long long +* suseconds_t: long long +* swblk_t: long long +* fd_mask_t: long long + +Some structures are changed to reduce the difference in the code path +for both ILP32 and LP64 ABIs for signal handling. + +The following structures have been changed so the layout of the structures are the same between ILP32 and LP64 ABIs. +* timespec: Uses time_t and suseconds_t +* timeval: Uses time_t and suseconds_t +* stat: Uses timespec/time_t. +* semid64_ds: Uses time_t. +* msqid64_ds: Uses time_t. +* shmid64_ds: Uses time_t. +* rt_sigframe: Uses siginfo and ucontext. +* siginfo_t: Uses clock_t and sigval_t +* ucontext: Uses stack_t and sigset_t +* stack_t: NOTE special handling inside the kernel is done to make sure + the pointers are zero extended +* sigval_t: Contains pointers +* sigevent: Uses sigval_t which causes it to be the same. Special + handing is needed for reading; in the mq_notify syscall +* sigaction: NOTE the userland structure inside glibc does + not match the kernel structure here (this causes issues with LTP). + Uses sigset_t. +* fd_set: This is done to avoid endian issues between ILP32 and LP64 + Also the syscall which uses fd_set uses timespec + + +Also the syscalls which normally would pass 64bit values as two +arguments; now pass the 64bit value as one argument. Also they have +been renamed (removing the 64 from the name) to avoid confusion. +The list of these syscalls: +* fcntl +* statfs +* fstatfs +* truncate +* ftruncate +* lseek +* sendfile +* newfstatat +* fstat +* mmap +* fadvise64 diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index b1f9a20a3677..03ce54ecdc84 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -600,9 +600,13 @@ menu "Userspace binary formats" source "fs/Kconfig.binfmt" config COMPAT + def_bool y + depends on AARCH32_EL0 || ARM64_ILP32 + select COMPAT_BINFMT_ELF + +config AARCH32_EL0 bool "Kernel support for 32-bit EL0" depends on !ARM64_64K_PAGES - select COMPAT_BINFMT_ELF select HAVE_UID16 select OLD_SIGSUSPEND3 select COMPAT_OLD_SIGACTION @@ -614,9 +618,16 @@ config COMPAT If you want to execute 32-bit userspace applications, say Y. +config ARM64_ILP32 + bool "Kernel support for ILP32" + 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. + config SYSVIPC_COMPAT def_bool y - depends on COMPAT && SYSVIPC + depends on AARCH32_EL0 && SYSVIPC endmenu diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 1c43cec971b5..51f00c4a58b4 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -20,14 +20,18 @@ LIBGCC := $(shell $(CC) $(KBUILD_CFLAGS) -print-libgcc-file-name) KBUILD_DEFCONFIG := defconfig KBUILD_CFLAGS += -mgeneral-regs-only +KBUILD_CFLAGS += $(call cc-option,-mabi=lp64) +KBUILD_AFLAGS += $(call cc-option,-mabi=lp64) ifeq ($(CONFIG_CPU_BIG_ENDIAN), y) KBUILD_CPPFLAGS += -mbig-endian AS += -EB LD += -EB +LDFLAGS += -maarch64linuxb else KBUILD_CPPFLAGS += -mlittle-endian AS += -EL LD += -EL +LDFLAGS += -maarch64linux endif CHECKFLAGS += -D__aarch64__ diff --git a/arch/arm64/include/asm/compat.h b/arch/arm64/include/asm/compat.h index 3fb053fa6e98..e74a629570da 100644 --- a/arch/arm64/include/asm/compat.h +++ b/arch/arm64/include/asm/compat.h @@ -76,6 +76,9 @@ struct compat_timeval { s32 tv_usec; }; +/* ILP32 uses 64bit time_t and not the above compat structures */ +#define COMPAT_USE_64BIT_TIME (!is_a32_compat_task()) + struct compat_stat { #ifdef __AARCH64EB__ short st_dev; @@ -215,6 +218,9 @@ typedef struct compat_siginfo { } _sifields; } compat_siginfo_t; +/* ILP32 uses the native siginfo and not the compat struct */ +#define COMPAT_USE_NATIVE_SIGINFO (!is_a32_compat_task()) + #define COMPAT_OFF_T_MAX 0x7fffffff #define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL @@ -300,6 +306,16 @@ struct compat_shmid64_ds { compat_ulong_t __unused5; }; +#define COMPAT_ELF_HWCAP \ + (is_a32_compat_task() \ + ? compat_elf_hwcap \ + : elf_hwcap) + +#define COMPAT_ELF_HWCAP2 \ + (is_a32_compat_task() \ + ? compat_elf_hwcap2 \ + : 0) + static inline int is_compat_task(void) { return test_thread_flag(TIF_32BIT); @@ -318,5 +334,48 @@ static inline int is_compat_thread(struct thread_info *thread) } #endif /* CONFIG_COMPAT */ + +#ifdef CONFIG_AARCH32_EL0 +static inline int is_a32_compat_task(void) +{ + return test_thread_flag(TIF_AARCH32); +} +static inline int is_a32_compat_thread(struct thread_info *thread) +{ + return test_ti_thread_flag(thread, TIF_AARCH32); +} +#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 + +#ifdef CONFIG_ARM64_ILP32 +static inline int is_ilp32_compat_task(void) +{ + return test_thread_flag(TIF_32BIT) && !is_a32_compat_task(); +} +static inline int is_ilp32_compat_thread(struct thread_info *thread) +{ + return test_ti_thread_flag(thread, TIF_32BIT) && + !is_a32_compat_thread(thread); +} +#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 + + #endif /* __KERNEL__ */ #endif /* __ASM_COMPAT_H */ diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h index 1f65be393139..e9f3891fd68a 100644 --- a/arch/arm64/include/asm/elf.h +++ b/arch/arm64/include/asm/elf.h @@ -16,6 +16,7 @@ #ifndef __ASM_ELF_H #define __ASM_ELF_H +#include <asm/errno.h> #include <asm/hwcap.h> /* @@ -135,7 +136,11 @@ extern unsigned long randomize_et_dyn(unsigned long base); */ #define ELF_PLAT_INIT(_r, load_addr) (_r)->regs[0] = 0 -#define SET_PERSONALITY(ex) clear_thread_flag(TIF_32BIT); +#define SET_PERSONALITY(ex) \ +do { \ + clear_thread_flag(TIF_AARCH32); \ + clear_thread_flag(TIF_32BIT); \ +} while (0) #define ARCH_DLINFO \ do { \ @@ -171,23 +176,100 @@ extern unsigned long arch_randomize_brk(struct mm_struct *mm); #define COMPAT_ELF_ET_DYN_BASE (2 * TASK_SIZE_32 / 3) +#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]; +#define COMPAT_A32_ELF_NGREG 18 +typedef unsigned int compat_a32_elf_greg_t; +typedef compat_a32_elf_greg_t compat_a32_elf_gregset_t[COMPAT_A32_ELF_NGREG]; /* AArch32 EABI. */ #define EF_ARM_EABI_MASK 0xff000000 -#define compat_elf_check_arch(x) (((x)->e_machine == EM_ARM) && \ +#define compat_a32_elf_check_arch(x) (((x)->e_machine == EM_ARM) && \ ((x)->e_flags & EF_ARM_EABI_MASK)) #define compat_start_thread compat_start_thread -#define COMPAT_SET_PERSONALITY(ex) set_thread_flag(TIF_32BIT); -#define COMPAT_ARCH_DLINFO +#define COMPAT_A32_SET_PERSONALITY(ex) \ +do { \ + set_thread_flag(TIF_AARCH32); \ + set_thread_flag(TIF_32BIT); \ +} while (0) +#define COMPAT_A32_ARCH_DLINFO do {} while (0) extern int aarch32_setup_vectors_page(struct linux_binprm *bprm, int uses_interp); -#define compat_arch_setup_additional_pages \ - aarch32_setup_vectors_page + +#else +typedef elf_greg_t compat_elf_greg_t; +typedef elf_gregset_t compat_elf_gregset_t; +#define compat_a32_elf_check_arch(x) 0 +#define COMPAT_A32_SET_PERSONALITY(ex) do {} while (0) +#define COMPAT_A32_ARCH_DLINFO do {} while (0) +static inline int aarch32_setup_vectors_page(struct linux_binprm *bprm, + int uses_interp) +{ + return -EINVAL; +} +#endif + +/* + * If ILP32 is turned on, we want to define the compat_elf_greg_t to the non compat + * one and define PR_REG_SIZE/PRSTATUS_SIZE/SET_PR_FPVALID so we pick up the correct + * ones for AARCH32. + */ +#ifdef CONFIG_ARM64_ILP32 +typedef elf_greg_t compat_elf_greg_t; +typedef elf_gregset_t compat_elf_gregset_t; +#define COMPAT_PR_REG_SIZE(S) (is_a32_compat_task() ? 72 : 272) +#define COMPAT_PRSTATUS_SIZE(S) (is_a32_compat_task() ? 124 : 352) +#define COMPAT_SET_PR_FPVALID(S, V) \ +do { \ + *(int *) (((void *) &((S)->pr_reg)) + PR_REG_SIZE((S)->pr_reg)) = (V); \ +} while (0) +#else +typedef compat_a32_elf_greg_t compat_elf_greg_t; +typedef compat_a32_elf_gregset_t compat_elf_gregset_t; +#endif + +#ifdef CONFIG_ARM64_ILP32 +#define compat_ilp32_elf_check_arch(x) ((x)->e_machine == EM_AARCH64) +#define COMPAT_ILP32_SET_PERSONALITY(ex) \ +do { \ + clear_thread_flag(TIF_AARCH32); \ + set_thread_flag(TIF_32BIT); \ +} while (0) +#define COMPAT_ILP32_ARCH_DLINFO \ +do { \ + NEW_AUX_ENT(AT_SYSINFO_EHDR, \ + (elf_addr_t)(long)current->mm->context.vdso); \ +} while (0) +#else +#define compat_ilp32_elf_check_arch(x) 0 +#define COMPAT_ILP32_SET_PERSONALITY(ex) do {} while (0) +#define COMPAT_ILP32_ARCH_DLINFO do {} while (0) +#endif + +#define compat_elf_check_arch(x) (compat_a32_elf_check_arch(x) || compat_ilp32_elf_check_arch(x)) +#define COMPAT_SET_PERSONALITY(ex) \ +do { \ + if (compat_a32_elf_check_arch(&ex)) \ + COMPAT_A32_SET_PERSONALITY(ex); \ + else \ + COMPAT_ILP32_SET_PERSONALITY(ex); \ +} while (0) + +/* ILP32 uses the "LP64-like" vdso pages */ +#define compat_arch_setup_additional_pages \ + (is_a32_compat_task() \ + ? &aarch32_setup_vectors_page \ + : &(arch_setup_additional_pages)) + +#define COMPAT_ARCH_DLINFO \ +do { \ + if (is_a32_compat_task()) \ + COMPAT_A32_ARCH_DLINFO; \ + else \ + COMPAT_ILP32_ARCH_DLINFO; \ +} while (0) #endif /* CONFIG_COMPAT */ diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.h index 50f559f574fe..63b19f128c6c 100644 --- a/arch/arm64/include/asm/fpsimd.h +++ b/arch/arm64/include/asm/fpsimd.h @@ -52,7 +52,7 @@ struct fpsimd_partial_state { }; -#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/hwcap.h b/arch/arm64/include/asm/hwcap.h index 0ad735166d9f..8b09fd1620dd 100644 --- a/arch/arm64/include/asm/hwcap.h +++ b/arch/arm64/include/asm/hwcap.h @@ -47,8 +47,6 @@ #define ELF_HWCAP (elf_hwcap) #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; #endif diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h index 1e894f7d5e22..cd532822d8b8 100644 --- a/arch/arm64/include/asm/processor.h +++ b/arch/arm64/include/asm/processor.h @@ -104,6 +104,17 @@ static inline void start_thread(struct pt_regs *regs, unsigned long pc, static inline void compat_start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp) { +#ifdef CONFIG_ARM64_ILP32 + /* + * ILP32 thread are started the same way as LP64 threads. + * Note we cannot use is_ilp32_compat_task here as that + * would introduce a header depency issue. + */ + if (!test_thread_flag(TIF_AARCH32)) { + start_thread(regs, pc, sp); + return; + } +#endif start_thread_common(regs, pc); regs->pstate = COMPAT_PSR_MODE_USR; if (pc & 1) diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h index 41ed9e13795e..690a380e2cdf 100644 --- a/arch/arm64/include/asm/ptrace.h +++ b/arch/arm64/include/asm/ptrace.h @@ -113,7 +113,7 @@ struct pt_regs { #define arch_has_single_step() (1) -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 #define compat_thumb_mode(regs) \ (((regs)->pstate & COMPAT_PSR_T_BIT)) #else diff --git a/arch/arm64/include/asm/signal32.h b/arch/arm64/include/asm/signal32.h index eeaa97559bab..522c3456141e 100644 --- a/arch/arm64/include/asm/signal32.h +++ b/arch/arm64/include/asm/signal32.h @@ -17,7 +17,7 @@ #define __ASM_SIGNAL32_H #ifdef __KERNEL__ -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 #include <linux/compat.h> #define AARCH32_KERN_SIGRET_CODE_OFFSET 0x500 @@ -47,6 +47,6 @@ static inline int compat_setup_rt_frame(int usig, struct ksignal *ksig, sigset_t static inline void compat_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/stat.h b/arch/arm64/include/asm/stat.h index 15e35598ac40..e3b5865be66d 100644 --- a/arch/arm64/include/asm/stat.h +++ b/arch/arm64/include/asm/stat.h @@ -18,7 +18,7 @@ #include <uapi/asm/stat.h> -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 #include <asm/compat.h> @@ -57,5 +57,5 @@ struct stat64 { compat_u64 st_ino; }; -#endif +#endif /* CONFIG_AARCH32_EL0 */ #endif diff --git a/arch/arm64/include/asm/syscalls.h b/arch/arm64/include/asm/syscalls.h index 48fe7c600e98..b58dad785c29 100644 --- a/arch/arm64/include/asm/syscalls.h +++ b/arch/arm64/include/asm/syscalls.h @@ -25,6 +25,10 @@ */ asmlinkage long sys_rt_sigreturn_wrapper(void); +#ifdef CONFIG_ARM64_ILP32 +long ilp32_sys_sigaltstack(const stack_t __user *, stack_t __user *); +#endif + #include <asm-generic/syscalls.h> #endif /* __ASM_SYSCALLS_H */ diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h index 459bf8e53208..a1851057162b 100644 --- a/arch/arm64/include/asm/thread_info.h +++ b/arch/arm64/include/asm/thread_info.h @@ -119,6 +119,7 @@ static inline struct thread_info *current_thread_info(void) #define TIF_SINGLESTEP 21 #define TIF_32BIT 22 /* 32bit process */ #define TIF_SWITCH_MM 23 /* deferred switch_mm */ +#define TIF_AARCH32 24 /* AARCH32 process */ #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h index 49c9aefd24a5..43de71f0700f 100644 --- a/arch/arm64/include/asm/unistd.h +++ b/arch/arm64/include/asm/unistd.h @@ -13,7 +13,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifdef CONFIG_COMPAT +#ifdef CONFIG_ARM64_ILP32 +#define __ARCH_WANT_COMPAT_SYS_PREADV64 +#define __ARCH_WANT_COMPAT_SYS_PWRITEV64 +#endif +#ifdef CONFIG_AARCH32_EL0 #define __ARCH_WANT_COMPAT_SYS_GETDENTS64 #define __ARCH_WANT_COMPAT_STAT64 #define __ARCH_WANT_SYS_GETHOSTNAME diff --git a/arch/arm64/include/asm/vdso.h b/arch/arm64/include/asm/vdso.h index 839ce0031bd5..84050c613cc3 100644 --- a/arch/arm64/include/asm/vdso.h +++ b/arch/arm64/include/asm/vdso.h @@ -29,6 +29,10 @@ #include <generated/vdso-offsets.h> +#ifdef CONFIG_ARM64_ILP32 +#include <generated/vdso-ilp32-offsets.h> +#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 fce9c2924fa3..bb716d04a9b5 100644 --- a/arch/arm64/include/uapi/asm/bitsperlong.h +++ b/arch/arm64/include/uapi/asm/bitsperlong.h @@ -16,7 +16,14 @@ #ifndef __ASM_BITSPERLONG_H #define __ASM_BITSPERLONG_H -#define __BITS_PER_LONG 64 +/* Assuming __LP64__ will be defined for native ELF64's and not for ILP32. */ +#ifdef __LP64__ +# define __BITS_PER_LONG 64 +#elif defined(__ILP32__) +# define __BITS_PER_LONG 32 +#else +# error "Unknown ABI; not ILP32 or LP64" +#endif #include <asm-generic/bitsperlong.h> diff --git a/arch/arm64/include/uapi/asm/posix_types.h b/arch/arm64/include/uapi/asm/posix_types.h index 7985ff60ca3f..09619459bb1a 100644 --- a/arch/arm64/include/uapi/asm/posix_types.h +++ b/arch/arm64/include/uapi/asm/posix_types.h @@ -5,6 +5,12 @@ typedef unsigned short __kernel_old_uid_t; typedef unsigned short __kernel_old_gid_t; #define __kernel_old_uid_t __kernel_old_uid_t +#ifdef __ILP32__ +typedef long long __kernel_long_t; +typedef unsigned long long __kernel_ulong_t; +#define __kernel_long_t __kernel_long_t +#endif + #include <asm-generic/posix_types.h> -#endif /* __ASM_POSIX_TYPES_H */ +#endif /* __ASM_POSIX_TYPES_H */ diff --git a/arch/arm64/include/uapi/asm/siginfo.h b/arch/arm64/include/uapi/asm/siginfo.h index 5a74a0853db0..c80c612885ae 100644 --- a/arch/arm64/include/uapi/asm/siginfo.h +++ b/arch/arm64/include/uapi/asm/siginfo.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 ARM Ltd. + * Copyright (C) 2014 Cavium Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -18,6 +19,38 @@ #define __ARCH_SI_PREAMBLE_SIZE (4 * sizeof(int)) +#ifdef __ILP32__ + +/* + * For ILP32, the siginfo structures should share the same layout and + * alignement requirements as LP64 ABI. + * To do this, use an extra pad field and add aligned attribute + * to the structure. + */ + +# ifdef __AARCH64EB__ +# define __SIGINFO_INNER(type, field) \ + int __pad#field; \ + type field +# else +# define __SIGINFO_INNER(type, field) \ + type field; \ + int __pad#field +# endif + +# undef __SIGINFO_VOIDPTR +# define __SIGINFO_VOIDPTR(field) \ + __SIGINFO_INNER(void __user*, field) +# undef __SIGINFO_BAND + +# define __SIGINFO_BAND(field) \ + __SIGINFO_INNER(long, field) + +/* Make the alignment of siginfo always 8 byte aligned. */ +#define __ARCH_SI_ATTRIBUTES __attribute__((aligned(8))) + +#endif + #include <asm-generic/siginfo.h> #endif diff --git a/arch/arm64/include/uapi/asm/signal.h b/arch/arm64/include/uapi/asm/signal.h index 8d1e7236431b..f378ff4f93d7 100644 --- a/arch/arm64/include/uapi/asm/signal.h +++ b/arch/arm64/include/uapi/asm/signal.h @@ -16,9 +16,43 @@ #ifndef __ASM_SIGNAL_H #define __ASM_SIGNAL_H +#include <asm/posix_types.h> + /* Required for AArch32 compatibility. */ #define SA_RESTORER 0x04000000 +/* + * Since sigset is a bitmask, we need the same size fields for ILP32 + * and LP64. With big-endian, 32bit bitmask does not match up to + * 64bit bitmask (unlike with little-endian). + */ +#ifdef __ILP32__ + +#define __SIGSET_INNER_TYPE __kernel_ulong_t +#define _NSIG_BPW 64 + +# ifdef __AARCH64EB__ +# define __SIGNAL_INNER(type, field) \ + int __pad_##field; \ + type field; +# else +# define __SIGNAL_INNER(type, field) \ + type field; \ + int __pad_##field; +# endif + +# define __SIGACTION_HANDLER(field) \ + __SIGNAL_INNER(__sighandler_t, field) + + +#define __SIGACTION_FLAGS(field) \ + __kernel_ulong_t field + +#define __SIGACTION_RESTORER(field) \ + __SIGNAL_INNER(__sigrestore_t, field) + +#endif + #include <asm-generic/signal.h> #endif diff --git a/arch/arm64/include/uapi/asm/unistd.h b/arch/arm64/include/uapi/asm/unistd.h index 1caadc24e3fe..13cbf8d6cdd0 100644 --- a/arch/arm64/include/uapi/asm/unistd.h +++ b/arch/arm64/include/uapi/asm/unistd.h @@ -13,4 +13,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* For ILP32, we want to use the non compat names. */ +#if defined(__ILP32__) +#define __ARCH_WANT_64BIT_SYSCALLS +#endif + #include <asm-generic/unistd.h> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index eaa77ed7766a..f2b4e1e81987 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -18,9 +18,10 @@ arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ hyp-stub.o psci.o cpu_ops.o insn.o return_address.o \ cpuinfo.o cpu_errata.o alternative.o -arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ +arm64-obj-$(CONFIG_AARCH32_EL0) += sys32.o kuser32.o signal32.o \ sys_compat.o \ ../../arm/kernel/opcodes.o +arm64-obj-$(CONFIG_ARM64_ILP32) += sys_ilp32.o arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o topology.o @@ -36,6 +37,7 @@ arm64-obj-$(CONFIG_PCI) += pci.o arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o obj-y += $(arm64-obj-y) vdso/ +obj-$(CONFIG_ARM64_ILP32) += vdso-ilp32/ obj-m += $(arm64-obj-m) head-y := head.o extra-y := $(head-y) vmlinux.lds @@ -43,3 +45,7 @@ extra-y := $(head-y) vmlinux.lds # vDSO - this must be built first to generate the symbol offsets $(call objectify,$(arm64-obj-y)): $(obj)/vdso/vdso-offsets.h $(obj)/vdso/vdso-offsets.h: $(obj)/vdso + +# vDSO - this must be built first to generate the symbol offsets +$(call objectify,$(arm64-obj-y)): $(obj)/vdso-ilp32/vdso-ilp32-offsets.h +$(obj)/vdso-ilp32/vdso-ilp32-offsets.h: $(obj)/vdso-ilp32 diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index 9a9fce090d58..d9c429c073e7 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -53,7 +53,7 @@ int main(void) DEFINE(S_X7, offsetof(struct pt_regs, regs[7])); DEFINE(S_LR, offsetof(struct pt_regs, regs[30])); DEFINE(S_SP, offsetof(struct pt_regs, sp)); -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 DEFINE(S_COMPAT_SP, offsetof(struct pt_regs, compat_sp)); #endif DEFINE(S_PSTATE, offsetof(struct pt_regs, pstate)); diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index fd4fa374e5d2..c0615ecd4b4f 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -196,7 +196,7 @@ ENTRY(vectors) ventry el0_fiq_invalid // FIQ 64-bit EL0 ventry el0_error_invalid // Error 64-bit EL0 -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 ventry el0_sync_compat // Synchronous 32-bit EL0 ventry el0_irq_compat // IRQ 32-bit EL0 ventry el0_fiq_invalid_compat // FIQ 32-bit EL0 @@ -236,7 +236,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) @@ -398,7 +398,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 @@ -642,9 +642,14 @@ ENDPROC(ret_from_fork) */ .align 6 el0_svc: - adrp stbl, sys_call_table // load syscall table pointer uxtw scno, w8 // syscall number in w8 mov sc_nr, #__NR_syscalls +#ifdef CONFIG_ARM64_ILP32 + get_thread_info tsk + ldr x16, [tsk, #TI_FLAGS] + tbnz x16, #TIF_32BIT, el0_ilp32_svc // We are using ILP32 +#endif + adrp stbl, sys_call_table // load syscall table pointer el0_svc_naked: // compat entry point stp x0, scno, [sp, #S_ORIG_X0] // save the original x0 and syscall number enable_dbg_and_irq @@ -664,6 +669,12 @@ ni_sys: b ret_fast_syscall ENDPROC(el0_svc) +#ifdef CONFIG_ARM64_ILP32 +el0_ilp32_svc: + adrp stbl, sys_call_ilp32_table // load syscall table pointer + b el0_svc_naked +#endif + /* * This is the really slow path. We're going to be doing context * switches, and waiting for our parent to respond. diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S index 8ce88e08c030..47097ee2cded 100644 --- a/arch/arm64/kernel/head.S +++ b/arch/arm64/kernel/head.S @@ -540,7 +540,7 @@ CPU_LE( movk x0, #0x30d0, lsl #16 ) // Clear EE and E0E on LE systems mov x0, #0x33ff msr cptr_el2, x0 // Disable copro. traps to EL2 -#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 df1cf15377b4..8e46a67a28d1 100644 --- a/arch/arm64/kernel/hw_breakpoint.c +++ b/arch/arm64/kernel/hw_breakpoint.c @@ -28,6 +28,7 @@ #include <linux/ptrace.h> #include <linux/smp.h> +#include <asm/compat.h> #include <asm/current.h> #include <asm/debug-monitors.h> #include <asm/hw_breakpoint.h> @@ -433,7 +434,7 @@ static int arch_build_bp_info(struct perf_event *bp) * Watchpoints can be of length 1, 2, 4 or 8 bytes. */ if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) { - if (is_compat_task()) { + if (is_a32_compat_task()) { if (info->ctrl.len != ARM_BREAKPOINT_LEN_2 && info->ctrl.len != ARM_BREAKPOINT_LEN_4) return -EINVAL; @@ -490,7 +491,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) * AArch32 tasks expect some simple alignment fixups, so emulate * that here. */ - if (is_compat_task()) { + if (is_a32_compat_task()) { if (info->ctrl.len == ARM_BREAKPOINT_LEN_8) alignment_mask = 0x7; else @@ -677,7 +678,7 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr, info = counter_arch_bp(wp); /* AArch32 watchpoints are either 4 or 8 bytes aligned. */ - if (is_compat_task()) { + if (is_a32_compat_task()) { if (info->ctrl.len == ARM_BREAKPOINT_LEN_8) alignment_mask = 0x7; else diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index fde9923af859..4ae5562ba6e6 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -253,7 +253,7 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start, if (likely(!(p->flags & PF_KTHREAD))) { *childregs = *current_pt_regs(); childregs->regs[0] = 0; - if (is_compat_thread(task_thread_info(p))) { + if (is_a32_compat_thread(task_thread_info(p))) { if (stack_start) childregs->compat_sp = stack_start; } else { @@ -294,12 +294,12 @@ static void tls_thread_switch(struct task_struct *next) { unsigned long tpidr, tpidrro; - if (!is_compat_task()) { + if (!is_a32_compat_task()) { asm("mrs %0, tpidr_el0" : "=r" (tpidr)); current->thread.tp_value = tpidr; } - if (is_compat_thread(task_thread_info(next))) { + if (is_a32_compat_thread(task_thread_info(next))) { tpidr = 0; tpidrro = next->thread.tp_value; } else { diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index d882b833dbdb..2d4d67af1d38 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -37,6 +37,7 @@ #include <linux/regset.h> #include <linux/tracehook.h> #include <linux/elf.h> +#include <linux/errno.h> #include <asm/compat.h> #include <asm/debug-monitors.h> @@ -76,10 +77,10 @@ static void ptrace_hbptriggered(struct perf_event *bp, .si_addr = (void __user *)(bkpt->trigger), }; -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 int i; - if (!is_compat_task()) + if (!is_a32_compat_task()) goto send_sig; for (i = 0; i < ARM_MAX_BRP; ++i) { @@ -653,6 +654,7 @@ static const struct user_regset_view user_aarch64_view = { #ifdef CONFIG_COMPAT #include <linux/compat.h> +#ifdef CONFIG_AARCH32_EL0 enum compat_regset { REGSET_COMPAT_GPR, @@ -829,9 +831,9 @@ static int compat_vfp_set(struct task_struct *target, static const struct user_regset aarch32_regsets[] = { [REGSET_COMPAT_GPR] = { .core_note_type = NT_PRSTATUS, - .n = COMPAT_ELF_NGREG, - .size = sizeof(compat_elf_greg_t), - .align = sizeof(compat_elf_greg_t), + .n = COMPAT_A32_ELF_NGREG, + .size = sizeof(compat_a32_elf_greg_t), + .align = sizeof(compat_a32_elf_greg_t), .get = compat_gpr_get, .set = compat_gpr_set }, @@ -864,7 +866,7 @@ static int compat_ptrace_read_user(struct task_struct *tsk, compat_ulong_t off, tmp = tsk->mm->start_data; else if (off == COMPAT_PT_TEXT_END_ADDR) tmp = tsk->mm->end_code; - else if (off < sizeof(compat_elf_gregset_t)) + else if (off < sizeof(compat_a32_elf_gregset_t)) return copy_regset_to_user(tsk, &user_aarch32_view, REGSET_COMPAT_GPR, off, sizeof(compat_ulong_t), ret); @@ -885,7 +887,7 @@ static int compat_ptrace_write_user(struct task_struct *tsk, compat_ulong_t off, if (off & 3 || off >= COMPAT_USER_SZ) return -EIO; - if (off >= sizeof(compat_elf_gregset_t)) + if (off >= sizeof(compat_a32_elf_gregset_t)) return 0; set_fs(KERNEL_DS); @@ -1027,8 +1029,8 @@ 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, - compat_ulong_t caddr, compat_ulong_t cdata) +long compat_a32_arch_ptrace(struct task_struct *child, compat_long_t request, + compat_ulong_t caddr, compat_ulong_t cdata) { unsigned long addr = caddr; unsigned long data = cdata; @@ -1048,7 +1050,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, ret = copy_regset_to_user(child, &user_aarch32_view, REGSET_COMPAT_GPR, - 0, sizeof(compat_elf_gregset_t), + 0, sizeof(compat_a32_elf_gregset_t), datap); break; @@ -1056,7 +1058,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, ret = copy_regset_from_user(child, &user_aarch32_view, REGSET_COMPAT_GPR, - 0, sizeof(compat_elf_gregset_t), + 0, sizeof(compat_a32_elf_gregset_t), datap); break; @@ -1104,12 +1106,32 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, return ret; } -#endif /* CONFIG_COMPAT */ +#else /* !CONFIG_AARCH32_EL0 */ +long compat_a32_arch_ptrace(struct task_struct *child, compat_long_t request, + compat_ulong_t caddr, compat_ulong_t cdata) +{ + return -EINVAL; +} +#endif /* !CONFIG_AARCH32_EL0 */ + +/* + * In ILP32, compat_arch_ptrace is used via the compat syscall, we don't need + * to do anything special for ILP32 though; only for AARCH32. + */ +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_arch_ptrace(child, request, caddr, cdata); + 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 - if (is_compat_thread(task_thread_info(task))) +#ifdef CONFIG_AARCH32_EL0 + if (is_a32_compat_thread(task_thread_info(task))) return &user_aarch32_view; #endif return &user_aarch64_view; @@ -1136,7 +1158,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; diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c index 6fa792137eda..6316d54b8875 100644 --- a/arch/arm64/kernel/signal.c +++ b/arch/arm64/kernel/signal.c @@ -26,6 +26,7 @@ #include <linux/tracehook.h> #include <linux/ratelimit.h> +#include <asm/compat.h> #include <asm/debug-monitors.h> #include <asm/elf.h> #include <asm/cacheflush.h> @@ -34,6 +35,7 @@ #include <asm/fpsimd.h> #include <asm/signal32.h> #include <asm/vdso.h> +#include <asm/syscalls.h> /* * Do a signal return; undo the signal stack. These are aligned to 128-bit. @@ -148,6 +150,19 @@ asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) if (restore_sigframe(regs, frame)) goto badframe; +#ifdef CONFIG_ARM64_ILP32 + /* + * ILP32 has to be handled "special" due to maybe not zeroing out + * the upper 32bits of the pointer if the user changed the frame. + */ + if (is_ilp32_compat_task()) { + if (ilp32_sys_sigaltstack(&frame->uc.uc_stack, + NULL) == -EFAULT) + goto badframe; + return regs->regs[0]; + } +#endif + if (restore_altstack(&frame->uc.uc_stack)) goto badframe; @@ -241,6 +256,10 @@ static void setup_return(struct pt_regs *regs, struct k_sigaction *ka, if (ka->sa.sa_flags & SA_RESTORER) sigtramp = ka->sa.sa_restorer; +#ifdef CONFIG_ARM64_ILP32 + else if (is_ilp32_compat_task()) + sigtramp = VDSO_SYMBOL(current->mm->context.vdso, sigtramp_ilp32); +#endif else sigtramp = VDSO_SYMBOL(current->mm->context.vdso, sigtramp); @@ -276,7 +295,7 @@ static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set, static void setup_restart_syscall(struct pt_regs *regs) { - if (is_compat_task()) + if (is_a32_compat_task()) compat_setup_restart_syscall(regs); else regs->regs[8] = __NR_restart_syscall; @@ -302,7 +321,7 @@ 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); else @@ -421,3 +440,18 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, fpsimd_restore_current_state(); } + +/* + * Some functions are needed for compat ptrace but we don't define + * them if we don't have AARCH32 support compiled in + */ +#if defined CONFIG_COMPAT && !defined CONFIG_AARCH32_EL0 +int copy_siginfo_to_user32(compat_siginfo_t __user *to, const siginfo_t *from) +{ + return -EFAULT; +} +int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from) +{ + return -EFAULT; +} +#endif diff --git a/arch/arm64/kernel/sys_ilp32.c b/arch/arm64/kernel/sys_ilp32.c new file mode 100644 index 000000000000..893f7d6a28c8 --- /dev/null +++ b/arch/arm64/kernel/sys_ilp32.c @@ -0,0 +1,195 @@ +/* + * AArch64- ILP32 specific system calls implementation + * + * Copyright (C) 2013 Cavium Inc. + * Author: Andrew Pinski <apinski@cavium.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/compiler.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/export.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/syscalls.h> +#include <linux/compat.h> + +/* + * Wrappers to pass the pt_regs argument. + */ +#define sys_rt_sigreturn sys_rt_sigreturn_wrapper + +/* + * Note places where mention unsigned long bitmaps, could + * use the non compat version for little-endian but big-endian + * has issues to do layout of the bits in the bitmaps. + */ + +/* Using Compat syscalls where necessary */ +#define sys_ioctl compat_sys_ioctl +/* iovec */ +#define sys_readv compat_sys_readv +#define sys_writev compat_sys_writev +#define sys_preadv compat_sys_preadv64 +#define sys_pwritev compat_sys_pwritev64 +#define sys_vmsplice compat_sys_vmsplice +/* robust_list_head */ +#define sys_set_robust_list compat_sys_set_robust_list +#define sys_get_robust_list compat_sys_get_robust_list + +/* kexec_segment */ +#define sys_kexec_load compat_sys_kexec_load + +/* Ptrace has some structures which are different between ILP32 and LP64 */ +#define sys_ptrace compat_sys_ptrace + +/* struct msghdr */ +#define sys_recvfrom compat_sys_recvfrom +#define sys_recvmmsg compat_sys_recvmmsg +#define sys_sendmmsg compat_sys_sendmmsg +#define sys_sendmsg compat_sys_sendmsg +#define sys_recvmsg compat_sys_recvmsg + +/* + * Note the timeval is taken care by COMPAT_USE_64BIT_TIME + * being true. + */ +#define sys_setsockopt compat_sys_setsockopt +#define sys_getsockopt compat_sys_getsockopt + +/* Array of pointers */ +#define sys_execve compat_sys_execve +#define sys_move_pages compat_sys_move_pages + +/* iovec */ +#define sys_process_vm_readv compat_sys_process_vm_readv +#define sys_process_vm_writev compat_sys_process_vm_writev + +/* + * The NFSv4 and ncpfs structures are depend on the long and + * pointer sizes. + */ +#define sys_mount compat_sys_mount + +/* NUMA */ +/* unsigned long bitmaps */ +#define sys_get_mempolicy compat_sys_get_mempolicy +#define sys_set_mempolicy compat_sys_set_mempolicy +#define sys_mbind compat_sys_mbind + +/* array of pointers */ +/* unsigned long bitmaps */ +#define sys_migrate_pages compat_sys_migrate_pages + +/* Scheduler */ +/* unsigned long bitmaps */ +#define sys_sched_setaffinity compat_sys_sched_setaffinity +#define sys_sched_getaffinity compat_sys_sched_getaffinity + +/* iov usage */ +#define sys_keyctl compat_sys_keyctl + +/* aio */ +/* Array of pointers (iocb's) */ +#define sys_io_submit compat_sys_io_submit + +/* We need to make sure the pointer gets copied correctly. */ +asmlinkage long ilp32_sys_mq_notify(mqd_t mqdes, + const struct sigevent __user *u_notification) +{ + struct sigevent __user *p = NULL; + + if (u_notification) { + struct sigevent n; + + p = compat_alloc_user_space(sizeof(*p)); + if (copy_from_user(&n, u_notification, sizeof(*p))) + return -EFAULT; + if (n.sigev_notify == SIGEV_THREAD) + n.sigev_value.sival_ptr = compat_ptr((uintptr_t)n.sigev_value.sival_ptr); + if (copy_to_user(p, &n, sizeof(*p))) + return -EFAULT; + } + return sys_mq_notify(mqdes, p); +} + +/* + * sigevent contains sigval_t which is now 64bit always + * but need special handling due to padding for SIGEV_THREAD. + */ +#define sys_mq_notify ilp32_sys_mq_notify + + +/* + * sigaltstack needs some special handling as the + * padding for stack_t might not be non-zero. + */ +long ilp32_sys_sigaltstack(const stack_t __user *uss_ptr, + stack_t __user *uoss_ptr) +{ + stack_t uss, uoss; + int ret; + mm_segment_t seg; + + if (uss_ptr) { + if (!access_ok(VERIFY_READ, uss_ptr, sizeof(*uss_ptr))) + return -EFAULT; + if (__get_user(uss.ss_sp, &uss_ptr->ss_sp) | + __get_user(uss.ss_flags, &uss_ptr->ss_flags) | + __get_user(uss.ss_size, &uss_ptr->ss_size)) + return -EFAULT; + /* Zero extend the sp address and the size. */ + uss.ss_sp = (void *)(uintptr_t)(unsigned int)(uintptr_t)uss.ss_sp; + uss.ss_size = (size_t)(unsigned int)uss.ss_size; + } + seg = get_fs(); + set_fs(KERNEL_DS); + /* + * Note we need to use uoss as we have changed the segment to the + * kernel one so passing an user one around is wrong. + */ + ret = sys_sigaltstack((stack_t __force __user *) (uss_ptr ? &uss : NULL), + (stack_t __force __user *) &uoss); + set_fs(seg); + if (ret >= 0 && uoss_ptr) { + if (!access_ok(VERIFY_WRITE, uoss_ptr, sizeof(stack_t)) || + __put_user(uoss.ss_sp, &uoss_ptr->ss_sp) || + __put_user(uoss.ss_flags, &uoss_ptr->ss_flags) || + __put_user(uoss.ss_size, &uoss_ptr->ss_size)) + ret = -EFAULT; + } + return ret; +} + +/* + * sigaltstack needs some special handling as the padding + * for stack_t might not be non-zero. + */ +#define sys_sigaltstack ilp32_sys_sigaltstack + +#include <asm/syscalls.h> + +#undef __SYSCALL +#define __SYSCALL(nr, sym) [nr] = sym, + +/* + * The sys_call_ilp32_table array must be 4K aligned to be accessible from + * kernel/entry.S. + */ +void *sys_call_ilp32_table[__NR_syscalls] __aligned(4096) = { + [0 ... __NR_syscalls - 1] = sys_ni_syscall, +#include <asm/unistd.h> +}; diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index 0a801e3743d5..5d754d45ff1b 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -353,9 +353,9 @@ long compat_arm_syscall(struct pt_regs *regs); asmlinkage long do_ni_syscall(struct pt_regs *regs) { -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 long ret; - if (is_compat_task()) { + if (is_a32_compat_task()) { ret = compat_arm_syscall(regs); if (ret != -ENOSYS) return ret; diff --git a/arch/arm64/kernel/vdso-ilp32/.gitignore b/arch/arm64/kernel/vdso-ilp32/.gitignore new file mode 100644 index 000000000000..61806c3fd68b --- /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 000000000000..c8f54727fbb2 --- /dev/null +++ b/arch/arm64/kernel/vdso-ilp32/Makefile @@ -0,0 +1,72 @@ +# +# Building a vDSO image for AArch64. +# +# Author: Will Deacon <will.deacon@arm.com> +# 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) + +obj-y += vdso-ilp32.o +extra-y += vdso-ilp32.lds vdso-ilp32-offsets.h +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 > $@ && \ + cp $@ include/generated/ +endef + +$(obj)/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) + +$(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 000000000000..46ac0728443c --- /dev/null +++ b/arch/arm64/kernel/vdso-ilp32/vdso-ilp32.S @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2012 ARM Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Author: Will Deacon <will.deacon@arm.com> + */ + +#include <linux/init.h> +#include <linux/linkage.h> +#include <linux/const.h> +#include <asm/page.h> + + __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 000000000000..ac8029bb03c8 --- /dev/null +++ b/arch/arm64/kernel/vdso-ilp32/vdso-ilp32.lds.S @@ -0,0 +1,98 @@ +/* + * GNU linker script for the VDSO library. + * + * Copyright (C) 2012 ARM Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Author: Will Deacon <will.deacon@arm.com> + * Heavily based on the vDSO linker scripts for other archs. + */ + +#include <linux/const.h> +#include <asm/page.h> +#include <asm/vdso.h> + +/*OUTPUT_FORMAT("elf32-littleaarch64", "elf32-bigaarch64", "elf32-littleaarch64") +OUTPUT_ARCH(aarch64) +*/ +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_2.6.39 { + 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 32aeea083d93..21b3726bd751 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -35,11 +35,18 @@ #include <asm/signal32.h> #include <asm/vdso.h> #include <asm/vdso_datapage.h> +#include <asm/compat.h> extern char vdso_start, vdso_end; static unsigned long vdso_pages; static struct page **vdso_pagelist; +#ifdef CONFIG_ARM64_ILP32 +extern char vdso_ilp32_start, vdso_ilp32_end; +static unsigned long vdso_ilp32_pages; +static struct page **vdso_ilp32_pagelist; +#endif + /* * The vDSO data page. */ @@ -49,7 +56,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. */ @@ -107,26 +114,33 @@ int aarch32_setup_vectors_page(struct linux_binprm *bprm, int uses_interp) return PTR_ERR_OR_ZERO(ret); } -#endif /* CONFIG_COMPAT */ +#endif /* CONFIG_AARCH32_EL0 */ -static struct vm_special_mapping vdso_spec[2]; +static struct vm_special_mapping vdso_spec[2][2]; -static int __init vdso_init(void) +static inline int __init vdso_init_common(char *vdso_start, char *vdso_end, + unsigned long *vdso_pagesp, + struct page ***vdso_pagelistp, + bool ilp32) { int i; + unsigned long vdso_pages; + struct page **vdso_pagelist; - if (memcmp(&vdso_start, "\177ELF", 4)) { + if (memcmp(vdso_start, "\177ELF", 4)) { pr_err("vDSO is not a valid ELF object!\n"); return -EINVAL; } - vdso_pages = (&vdso_end - &vdso_start) >> PAGE_SHIFT; + vdso_pages = (vdso_end - vdso_start) >> PAGE_SHIFT; + *vdso_pagesp = vdso_pages; pr_info("vdso: %ld pages (%ld code @ %p, %ld data @ %p)\n", - vdso_pages + 1, vdso_pages, &vdso_start, 1L, vdso_data); + vdso_pages + 1, vdso_pages, vdso_start, 1L, vdso_data); /* Allocate the vDSO pagelist, plus a page for the data. */ vdso_pagelist = kcalloc(vdso_pages + 1, sizeof(struct page *), GFP_KERNEL); + *vdso_pagelistp = vdso_pagelist; if (vdso_pagelist == NULL) return -ENOMEM; @@ -135,33 +149,63 @@ static int __init vdso_init(void) /* Grab the vDSO code pages. */ for (i = 0; i < vdso_pages; i++) - vdso_pagelist[i + 1] = virt_to_page(&vdso_start + i * PAGE_SIZE); + vdso_pagelist[i + 1] = virt_to_page(vdso_start + i * PAGE_SIZE); /* Populate the special mapping structures */ - vdso_spec[0] = (struct vm_special_mapping) { + vdso_spec[ilp32][0] = (struct vm_special_mapping) { .name = "[vvar]", .pages = vdso_pagelist, }; - vdso_spec[1] = (struct vm_special_mapping) { + vdso_spec[ilp32][1] = (struct vm_special_mapping) { .name = "[vdso]", .pages = &vdso_pagelist[1], }; return 0; } + +static int __init vdso_init(void) +{ + return vdso_init_common(&vdso_start, &vdso_end, + &vdso_pages, &vdso_pagelist, 0); +} arch_initcall(vdso_init); +#ifdef CONFIG_ARM64_ILP32 +static int __init vdso_ilp32_init(void) +{ + return vdso_init_common(&vdso_ilp32_start, &vdso_ilp32_end, + &vdso_ilp32_pages, &vdso_ilp32_pagelist, 1); +} +arch_initcall(vdso_ilp32_init); +#endif + int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) { struct mm_struct *mm = current->mm; unsigned long vdso_base, vdso_text_len, vdso_mapping_len; void *ret; - - vdso_text_len = vdso_pages << PAGE_SHIFT; + struct page **pagelist; + unsigned long pages; + bool ilp32; + +#ifdef CONFIG_ARM64_ILP32 + ilp32 = is_ilp32_compat_task(); + if (is_ilp32_compat_task()) { + pages = vdso_ilp32_pages; + pagelist = vdso_ilp32_pagelist; + } else +#endif + { + ilp32 = false; + pages = vdso_pages; + pagelist = vdso_pagelist; + } + vdso_text_len = pages << PAGE_SHIFT; /* Be sure to map the data page */ - vdso_mapping_len = vdso_text_len + PAGE_SIZE; + vdso_mapping_len = (pages + 1) << PAGE_SHIFT; down_write(&mm->mmap_sem); vdso_base = get_unmapped_area(NULL, 0, vdso_mapping_len, 0, 0); @@ -171,7 +215,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, } ret = _install_special_mapping(mm, vdso_base, PAGE_SIZE, VM_READ|VM_MAYREAD, - &vdso_spec[0]); + &vdso_spec[ilp32][0]); if (IS_ERR(ret)) goto up_fail; @@ -180,7 +224,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, ret = _install_special_mapping(mm, vdso_base, vdso_text_len, VM_READ|VM_EXEC| VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, - &vdso_spec[1]); + &vdso_spec[ilp32][1]); if (IS_ERR(ret)) goto up_fail; diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 095c1774592c..c2ea5131fd80 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -309,7 +309,7 @@ static void arch_timer_evtstrm_enable(int divider) | ARCH_TIMER_VIRT_EVT_EN; arch_timer_set_cntkctl(cntkctl); elf_hwcap |= HWCAP_EVTSTRM; -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 compat_elf_hwcap |= COMPAT_HWCAP_EVTSTRM; #endif } diff --git a/fs/compat_binfmt_elf.c b/fs/compat_binfmt_elf.c index 4d24d17bcfc1..83079272ec85 100644 --- a/fs/compat_binfmt_elf.c +++ b/fs/compat_binfmt_elf.c @@ -130,6 +130,23 @@ static void cputime_to_compat_timeval(const cputime_t cputime, #define arch_setup_additional_pages compat_arch_setup_additional_pages #endif + +#ifdef COMPAT_PR_REG_SIZE +#define PR_REG_SIZE COMPAT_PR_REG_SIZE +#endif + +#ifdef COMPAT_PRSTATUS_SIZE +#define PRSTATUS_SIZE COMPAT_PRSTATUS_SIZE +#endif + +#ifdef COMPAT_PR_REG_PTR +#define PR_REG_PTR COMPAT_PR_REG_PTR +#endif + +#ifdef COMPAT_SET_PR_FPVALID +#define SET_PR_FPVALID COMPAT_SET_PR_FPVALID +#endif + /* * 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 diff --git a/include/linux/compat.h b/include/linux/compat.h index 7450ca2ac1fc..1e7865218a00 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -24,6 +24,10 @@ #define COMPAT_USE_64BIT_TIME 0 #endif +#ifndef COMPAT_USE_NATIVE_SIGINFO +#define COMPAT_USE_NATIVE_SIGINFO 0 +#endif + #ifndef __SC_DELOUSE #define __SC_DELOUSE(t,v) ((t)(unsigned long)(v)) #endif diff --git a/include/uapi/asm-generic/siginfo.h b/include/uapi/asm-generic/siginfo.h index 1e3552037a5a..e109ee086afc 100644 --- a/include/uapi/asm-generic/siginfo.h +++ b/include/uapi/asm-generic/siginfo.h @@ -4,9 +4,17 @@ #include <linux/compiler.h> #include <linux/types.h> +#ifndef __SIGINFO_VOIDPTR +#define __SIGINFO_VOIDPTR(field) void __user *field +#endif + +#ifndef __SIGINFO_BAND +#define __SIGINFO_BAND(field) __ARCH_SI_BAND_T field +#endif + typedef union sigval { int sival_int; - void __user *sival_ptr; + __SIGINFO_VOIDPTR(sival_ptr); } sigval_t; /* @@ -86,7 +94,7 @@ typedef struct siginfo { /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ struct { - void __user *_addr; /* faulting insn/memory ref. */ + __SIGINFO_VOIDPTR(_addr); /* faulting insn/memory ref. */ #ifdef __ARCH_SI_TRAPNO int _trapno; /* TRAP # which caused the signal */ #endif @@ -99,13 +107,13 @@ typedef struct siginfo { /* SIGPOLL */ struct { - __ARCH_SI_BAND_T _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + __SIGINFO_BAND(_band); /* POLL_IN, POLL_OUT, POLL_MSG */ int _fd; } _sigpoll; /* SIGSYS */ struct { - void __user *_call_addr; /* calling user insn */ + __SIGINFO_VOIDPTR(_call_addr); /* calling user insn */ int _syscall; /* triggering system call number */ unsigned int _arch; /* AUDIT_ARCH_* of syscall */ } _sigsys; @@ -290,6 +298,7 @@ typedef struct sigevent { int _pad[SIGEV_PAD_SIZE]; int _tid; + /* Note these two are handled only in userspace */ struct { void (*_function)(sigval_t); void *_attribute; /* really pthread_attr_t */ diff --git a/include/uapi/asm-generic/signal.h b/include/uapi/asm-generic/signal.h index 9df61f1edb0f..c4ce2387fe9b 100644 --- a/include/uapi/asm-generic/signal.h +++ b/include/uapi/asm-generic/signal.h @@ -4,7 +4,9 @@ #include <linux/types.h> #define _NSIG 64 +#ifndef _NSIG_BPW #define _NSIG_BPW __BITS_PER_LONG +#endif #define _NSIG_WORDS (_NSIG / _NSIG_BPW) #define SIGHUP 1 @@ -83,9 +85,13 @@ #define MINSIGSTKSZ 2048 #define SIGSTKSZ 8192 +#ifndef __SIGSET_INNER_TYPE +#define __SIGSET_INNER_TYPE unsigned long +#endif + #ifndef __ASSEMBLY__ typedef struct { - unsigned long sig[_NSIG_WORDS]; + __SIGSET_INNER_TYPE sig[_NSIG_WORDS]; } sigset_t; /* not actually used, but required for linux/syscalls.h */ @@ -98,11 +104,24 @@ typedef unsigned long old_sigset_t; #endif #ifndef __KERNEL__ + +#ifndef __SIGACTION_HANDLER +#define __SIGACTION_HANDLER(field) __sighandler_t field +#endif + +#ifndef __SIGACTION_FLAGS +#define __SIGACTION_FLAGS(field) unsigned long field +#endif + +#ifndef __SIGACTION_RESTORER +#define __SIGACTION_RESTORER(field) __sigrestore_t field +#endif + struct sigaction { - __sighandler_t sa_handler; - unsigned long sa_flags; + __SIGACTION_HANDLER(sa_handler); + __SIGACTION_FLAGS(sa_flags); #ifdef SA_RESTORER - __sigrestore_t sa_restorer; + __SIGACTION_RESTORER(sa_restorer); #endif sigset_t sa_mask; /* mask last for extensibility */ }; diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h index e016bd9b1a04..d0fe88f68acf 100644 --- a/include/uapi/asm-generic/unistd.h +++ b/include/uapi/asm-generic/unistd.h @@ -885,8 +885,12 @@ __SYSCALL(__NR_fork, sys_ni_syscall) * they take different names. * Here we map the numbers so that both versions * use the same syscall table layout. + * For 32bit abis where 64bit can be passed via one + * register, use the same naming as the 64bit ones + * as they will only have a 64 bit off_t. */ -#if __BITS_PER_LONG == 64 && !defined(__SYSCALL_COMPAT) +#if (__BITS_PER_LONG == 64 && !defined(__SYSCALL_COMPAT)) || \ + defined(__ARCH_WANT_64BIT_SYSCALLS) #define __NR_fcntl __NR3264_fcntl #define __NR_statfs __NR3264_statfs #define __NR_fstatfs __NR3264_fstatfs diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 1eb9d90c3af9..eb3e492a2aae 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -640,7 +640,7 @@ static int ptrace_peek_siginfo(struct task_struct *child, break; #ifdef CONFIG_COMPAT - if (unlikely(is_compat_task())) { + if (unlikely(is_compat_task() && !COMPAT_USE_NATIVE_SIGINFO)) { compat_siginfo_t __user *uinfo = compat_ptr(data); if (copy_siginfo_to_user32(uinfo, &info) || @@ -1109,16 +1109,27 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request, case PTRACE_GETSIGINFO: ret = ptrace_getsiginfo(child, &siginfo); - if (!ret) - ret = copy_siginfo_to_user32( - (struct compat_siginfo __user *) datap, - &siginfo); + if (!ret) { + if (COMPAT_USE_NATIVE_SIGINFO) + ret = copy_siginfo_to_user( + (struct siginfo __user *) datap, + &siginfo); + else + ret = copy_siginfo_to_user32( + (struct compat_siginfo __user *) datap, + &siginfo); + } break; case PTRACE_SETSIGINFO: memset(&siginfo, 0, sizeof siginfo); - if (copy_siginfo_from_user32( - &siginfo, (struct compat_siginfo __user *) datap)) + if (COMPAT_USE_NATIVE_SIGINFO) + ret = copy_from_user(&siginfo, datap, sizeof(siginfo)); + else + ret = copy_siginfo_from_user32( + &siginfo, + (struct compat_siginfo __user *) datap); + if (ret) ret = -EFAULT; else ret = ptrace_setsiginfo(child, &siginfo); |