diff options
author | Andrey Konovalov <andrey.konovalov@linaro.org> | 2015-10-03 14:37:47 +0300 |
---|---|---|
committer | Andrey Konovalov <andrey.konovalov@linaro.org> | 2015-10-03 14:37:47 +0300 |
commit | f04d218910803a422631d89b90f36d76857f0997 (patch) | |
tree | d21dc9a5f082e660fba318437860a4a144216745 | |
parent | 8609d8d8da08156fd0bd41bf4122a1b8345feba6 (diff) | |
parent | c3014bf62c6cf61bc78f6b96f92263b6fa94e52f (diff) |
Merge branch 'tracking-ilp32' into merge-linux-linaro-extralinux-linaro-extra-20151003.0
40 files changed, 990 insertions, 90 deletions
diff --git a/Documentation/arm64/ilp32.txt b/Documentation/arm64/ilp32.txt new file mode 100644 index 000000000000..1049510c79f5 --- /dev/null +++ b/Documentation/arm64/ilp32.txt @@ -0,0 +1,64 @@ +ILP32 AARCH64 SYSCALL ABI +========================= +Written by Andrew Pinski <apinski@cavium.com> +Updated by Philipp Tomsich <philipp.tomsich@theobroma-systems.com> + + +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. This +affects the following type definitions for ILP32 client programs: + * 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, including: + * 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 + * 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 Conversion is handled in the userland (glibc), as the + userland data structures are defined in glibc anyway. + * fd_set This is done to avoid endian issues between ILP32 and + LP64. Syscalls consuming fd_set use timespec. + +A number of structures differ between ILP32 and LP64, including: + * struct msgbuf The specification of 'struct msgbuf' defines the 'mtype' + field as a 'long' (i.e. 32bit for ILP32, but 64bit for + LP64). Functions that operate on 'struct msgbuf' need + to be passed through the compat-syscalls to resolve + this. + * stack_t contains pointers (handled in the compatibility layer) + +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 LP64 syscalls reused by ILP32 clients is: + * fcntl + * statfs + * fstatfs + * truncate + * ftruncate + * lseek + * sendfile + * newfstatat + * fstat + * mmap + * fadvise64 + diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 3aa5028607ae..9cfbabf3f7a5 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -727,6 +727,11 @@ 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 || EXPERT select COMPAT_BINFMT_ELF @@ -745,9 +750,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 33700a4867df..3e102703ee12 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -15,7 +15,6 @@ CPPFLAGS_vmlinux.lds = -DTEXT_OFFSET=$(TEXT_OFFSET) OBJCOPYFLAGS :=-O binary -R .note -R .note.gnu.build-id -R .comment -S GZFLAGS :=-9 -KBUILD_DEFCONFIG := defconfig # Check for binutils support for specific extensions lseinstr := $(call as-instr,.arch_extension lse,-DCONFIG_AS_LSE=1) @@ -30,14 +29,19 @@ KBUILD_CFLAGS += -mgeneral-regs-only $(lseinstr) KBUILD_AFLAGS += $(lseinstr) KBUILD_CFLAGS += -fno-pic +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 7fbed6919b54..47f2b7a84810 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; @@ -214,6 +217,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 @@ -299,15 +305,45 @@ struct compat_shmid64_ds { compat_ulong_t __unused5; }; -static inline int is_compat_task(void) +#ifdef CONFIG_AARCH32_EL0 +static inline int is_a32_compat_task(void) { return test_thread_flag(TIF_32BIT); } - -static inline int is_compat_thread(struct thread_info *thread) +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 + +#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 #else /* !CONFIG_COMPAT */ @@ -316,6 +352,29 @@ static inline int is_compat_thread(struct thread_info *thread) return 0; } +static inline int is_a32_compat_task(void) +{ + return 0; +} +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_COMPAT */ + +static inline int is_compat_task(void) +{ + return is_a32_compat_task() || is_ilp32_compat_task(); +} + +static inline int is_compat_thread(struct thread_info *thread) +{ + return is_a32_compat_thread(thread) || is_ilp32_compat_thread(thread); +} #endif /* __KERNEL__ */ #endif /* __ASM_COMPAT_H */ diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h index faad6df49e5b..8ef8fc5483f6 100644 --- a/arch/arm64/include/asm/elf.h +++ b/arch/arm64/include/asm/elf.h @@ -134,7 +134,11 @@ typedef struct user_fpsimd_state elf_fpregset_t; */ #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_32BIT_AARCH64); \ + clear_thread_flag(TIF_32BIT); \ +} while (0) #define ARCH_DLINFO \ do { \ @@ -149,7 +153,7 @@ extern int arch_setup_additional_pages(struct linux_binprm *bprm, /* 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 @@ -159,30 +163,103 @@ extern int arch_setup_additional_pages(struct linux_binprm *bprm, #ifdef CONFIG_COMPAT #ifdef __AARCH64EB__ -#define COMPAT_ELF_PLATFORM ("v8b") +#define COMPAT_ELF_PLATFORM (is_ilp32_compat_task() ? "aarch64_be:ilp32" : "v8b") #else -#define COMPAT_ELF_PLATFORM ("v8l") +#define COMPAT_ELF_PLATFORM (is_ilp32_compat_task() ? "aarch64:ilp32" : "v8l") #endif #define COMPAT_ELF_ET_DYN_BASE (2 * TASK_SIZE_32 / 3) + +extern int aarch32_setup_vectors_page(struct linux_binprm *bprm, + int uses_interp); + +#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 -extern int aarch32_setup_vectors_page(struct linux_binprm *bprm, - int uses_interp); -#define compat_arch_setup_additional_pages \ - aarch32_setup_vectors_page +#define COMPAT_A32_SET_PERSONALITY(ex) \ +do { \ + clear_thread_flag(TIF_32BIT_AARCH64); \ + set_thread_flag(TIF_32BIT); \ +} while (0) +#define COMPAT_A32_ARCH_DLINFO do {} while (0) + +#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) +#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. Note also the definition of the macros have to be correct for + LP64 as this file is included in the standard binfmt_elf.c. */ +#ifdef CONFIG_ARM64_ILP32 +typedef elf_greg_t compat_elf_greg_t; +typedef elf_gregset_t compat_elf_gregset_t; +#define PR_REG_SIZE(S) (is_a32_compat_task() ? 72 : 272) +#define PRSTATUS_SIZE(S) (is_a32_compat_task() ? 124 : (is_ilp32_compat_task() ? 352 : 392)) +#define 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 { \ + set_thread_flag(TIF_32BIT_AARCH64); \ + clear_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..1e5361ebf39f 100644 --- a/arch/arm64/include/asm/hwcap.h +++ b/arch/arm64/include/asm/hwcap.h @@ -47,9 +47,17 @@ #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; +#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) + #endif extern unsigned long elf_hwcap; diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h index 6b4c3ad75a2a..337f8e19d69d 100644 --- a/arch/arm64/include/asm/memory.h +++ b/arch/arm64/include/asm/memory.h @@ -58,7 +58,7 @@ #ifdef CONFIG_COMPAT #define TASK_SIZE_32 UL(0x100000000) -#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) ? \ TASK_SIZE_32 : TASK_SIZE_64) diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h index 98f32355dc97..d766f29f22c5 100644 --- a/arch/arm64/include/asm/processor.h +++ b/arch/arm64/include/asm/processor.h @@ -28,6 +28,7 @@ #ifdef __KERNEL__ #include <linux/string.h> +#include <linux/thread_info.h> #include <asm/fpsimd.h> #include <asm/hw_breakpoint.h> @@ -39,7 +40,7 @@ #define STACK_TOP_MAX TASK_SIZE_64 #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 @@ -123,6 +124,15 @@ 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_32BIT_AARCH64)) { + 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 536274ed292e..1059b3fcbf46 100644 --- a/arch/arm64/include/asm/ptrace.h +++ b/arch/arm64/include/asm/ptrace.h @@ -120,7 +120,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..430290985db6 100644 --- a/arch/arm64/include/asm/signal32.h +++ b/arch/arm64/include/asm/signal32.h @@ -20,6 +20,7 @@ #ifdef CONFIG_COMPAT #include <linux/compat.h> +#ifdef CONFIG_AARCH32_EL0 #define AARCH32_KERN_SIGRET_CODE_OFFSET 0x500 extern const compat_ulong_t aarch32_sigret_code[6]; @@ -47,6 +48,7 @@ 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_AARCH32_EL0 */ #endif /* CONFIG_COMPAT */ #endif /* __KERNEL__ */ #endif /* __ASM_SIGNAL32_H */ diff --git a/arch/arm64/include/asm/stat.h b/arch/arm64/include/asm/stat.h index 15e35598ac40..af0427627f59 100644 --- a/arch/arm64/include/asm/stat.h +++ b/arch/arm64/include/asm/stat.h @@ -22,6 +22,7 @@ #include <asm/compat.h> +#ifdef CONFIG_AARCH32_EL0 /* * struct stat64 is needed for compat tasks only. Its definition is different * from the generic struct stat64. @@ -59,3 +60,4 @@ struct stat64 { #endif #endif +#endif diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h index dcd06d18a42a..e72de7403a4c 100644 --- a/arch/arm64/include/asm/thread_info.h +++ b/arch/arm64/include/asm/thread_info.h @@ -110,8 +110,9 @@ static inline struct thread_info *current_thread_info(void) #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_SWITCH_MM 23 /* deferred switch_mm */ +#define TIF_32BIT_AARCH64 24 /* 32 bit process on AArch64(ILP32) */ #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 3bc498c250dc..31eabf19718a 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 @@ -26,7 +30,9 @@ #define __ARCH_WANT_COMPAT_SYS_SENDFILE #define __ARCH_WANT_SYS_FORK #define __ARCH_WANT_SYS_VFORK +#endif +#ifdef CONFIG_COMPAT /* * Compat syscall numbers used by the AArch64 kernel. */ 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..4265243e326f 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 +#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 <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..d6a7cb5e9182 100644 --- a/arch/arm64/include/uapi/asm/posix_types.h +++ b/arch/arm64/include/uapi/asm/posix_types.h @@ -5,6 +5,16 @@ typedef unsigned short __kernel_old_uid_t; typedef unsigned short __kernel_old_gid_t; #define __kernel_old_uid_t __kernel_old_uid_t +#if defined(__ILP32__) +/* The ILP32 kernel ABI reuses LP64 system calls where possible; to + this end, the equivalent ILP32 type definitions are used (a 64bit + 'long'-type in LP64 corresponds to a 'long long' in LP64). */ + +typedef long long __kernel_long_t; +typedef unsigned long long __kernel_ulong_t; +#define __kernel_long_t __kernel_long_t +#endif /* defined(__ILP32__) */ + #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..1a6aa323284d 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,26 @@ #define __ARCH_SI_PREAMBLE_SIZE (4 * sizeof(int)) +#ifdef __ILP32__ +# 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_VOIDPOINTER +# define __SIGINFO_VOIDPOINTER(field) \ + __SIGINFO_INNER(void __user*, field) +# undef __SIGINFO_BAND + +# define __SIGINFO_BAND(field) \ + __SIGINFO_INNER(long, field) +#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..d90d53bddbd1 100644 --- a/arch/arm64/include/uapi/asm/signal.h +++ b/arch/arm64/include/uapi/asm/signal.h @@ -19,6 +19,38 @@ /* Required for AArch32 compatibility. */ #define SA_RESTORER 0x04000000 +/* For ILP32, sigset should be the same size fields as LP64 so use + unsigned long long. */ +#ifdef __ILP32__ +#define __SIGSET_INNER_TYPE __extension__ unsigned long long +#define _NSIG_BPW 64 + +# ifdef __AARCH64EB__ +# define __SIGNAL_INNER(type, field) \ + __extension__ struct { \ + int __pad_##field; \ + type field; \ + } __attribute__((aligned(8))) +# else +# define __SIGNAL_INNER(type, field) \ + __extension__ struct { \ + type field; \ + int __pad_##field; \ + } __attribute__((aligned(8))) +# endif + +# define __SIGACTION_HANDLER(field) \ + __SIGNAL_INNER(__sighandler_t, field) + + +#define __SIGACTION_FLAGS(field) \ + __extension__ unsigned long long 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..00fed6252fb1 100644 --- a/arch/arm64/include/uapi/asm/unistd.h +++ b/arch/arm64/include/uapi/asm/unistd.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 ARM Ltd. + * Copyright (C) 2015 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 @@ -13,4 +14,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 AARCH64, we want to use the non compat names. */ +#if defined(__aarch64__) && defined(__ILP32__) +#define __SYSCALL_NONCOMPAT +#endif + #include <asm-generic/unistd.h> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 22dc9bc781be..a1cbb110c173 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -20,10 +20,11 @@ arm64-obj-y := debug-monitors.o entry.o irq.o fpsimd.o \ cpufeature.o alternative.o cacheinfo.o \ smp.o smp_spin_table.o topology.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 entry32.o \ ../../arm/kernel/opcodes.o arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o +arm64-obj-$(CONFIG_ARM64_ILP32) += sys_ilp32.o arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o perf_callchain.o arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o @@ -38,6 +39,7 @@ arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o arm64-obj-$(CONFIG_ACPI) += acpi.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 @@ -45,3 +47,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 8d89cf8dae55..e3bcf77f1924 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -51,7 +51,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 4306c937b1ff..bcd921a0fbc6 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -212,7 +212,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 @@ -252,7 +252,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) @@ -414,7 +414,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 @@ -664,9 +664,13 @@ 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 + ldr x16, [tsk, #TI_FLAGS] + tbnz x16, #TIF_32BIT_AARCH64, 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 @@ -686,6 +690,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 90d09eddd5b2..d11d0b2c87d9 100644 --- a/arch/arm64/kernel/head.S +++ b/arch/arm64/kernel/head.S @@ -519,7 +519,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 bba85c8f8037..917f6e1d2a60 100644 --- a/arch/arm64/kernel/hw_breakpoint.c +++ b/arch/arm64/kernel/hw_breakpoint.c @@ -420,7 +420,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; @@ -477,7 +477,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 @@ -664,7 +664,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 b147e5cb4b25..94125001fd62 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -325,7 +325,7 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start, asm("mrs %0, tpidr_el0" : "=r" (*task_user_tls(p))); 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; /* 16-byte aligned stack mandatory on AArch64 */ else if (stack_start & 15) @@ -362,7 +362,7 @@ static void tls_thread_switch(struct task_struct *next) *task_user_tls(current) = tpidr; tpidr = *task_user_tls(next); - tpidrro = is_compat_thread(task_thread_info(next)) ? + tpidrro = is_a32_compat_thread(task_thread_info(next)) ? next->thread.tp_value : 0; asm( diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 1971f491bb90..1f5c38cfc3c9 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -76,10 +76,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) { @@ -651,7 +651,7 @@ static const struct user_regset_view user_aarch64_view = { .regsets = aarch64_regsets, .n = ARRAY_SIZE(aarch64_regsets) }; -#ifdef CONFIG_COMPAT +#ifdef CONFIG_AARCH32_EL0 #include <linux/compat.h> enum compat_regset { @@ -853,9 +853,9 @@ static int compat_tls_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 }, @@ -946,7 +946,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); @@ -967,7 +967,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); @@ -1109,8 +1109,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; @@ -1130,7 +1130,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; @@ -1138,7 +1138,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; @@ -1186,20 +1186,37 @@ 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 -1; +} +#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_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 +#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; @@ -1226,7 +1243,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 e18c48cb6db1..8eb8ed92eb9e 100644 --- a/arch/arm64/kernel/signal.c +++ b/arch/arm64/kernel/signal.c @@ -241,6 +241,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 +280,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; @@ -295,7 +299,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 @@ -414,3 +418,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..09605be3cd8a --- /dev/null +++ b/arch/arm64/kernel/sys_ilp32.c @@ -0,0 +1,223 @@ +/* + * 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/msg.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. + */ +asmlinkage long sys_rt_sigreturn_wrapper(void); +#define sys_rt_sigreturn sys_rt_sigreturn_wrapper + +/* 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 + +#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 + +/* Pointer in struct */ +#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 */ +/* Pointer to Pointer */ +#define sys_io_setup compat_sys_io_setup +/* Array of pointers */ +#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 + +struct ilp32_msgbuf { + s32 mtype; /* type of message */ + char mtext[1]; /* message text */ +}; + +long ilp32_sys_msgsnd(s32 msqid, struct ilp32_msgbuf __user *msgp, s32 msgsz, s32 msgflg) +{ + long mtype; + + if (msgsz < 0) + return -EINVAL; + + if (get_user(mtype, &msgp->mtype)) + return -EFAULT; + return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg); +} + +#define sys_msgsnd ilp32_sys_msgsnd + +extern int store_msg(void __user *dest, struct msg_msg *msg, size_t len); +static long ilp32_do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz) +{ + struct ilp32_msgbuf __user *msgp = dest; + size_t msgsz; + + if (put_user(msg->m_type, &msgp->mtype)) + return -EFAULT; + + msgsz = (bufsz > msg->m_ts) ? msg->m_ts : bufsz; + if (store_msg(msgp->mtext, msg, msgsz)) + return -EFAULT; + return msgsz; +} + +long ilp32_sys_msgrcv(s32 msqid, struct ilp32_msgbuf __user *msgp, s32 msgsz, + s32 msgtyp, s32 msgflg) +{ + if (msgsz < 0) + return -EINVAL; + + return do_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg, ilp32_do_msg_fill); +} + +#define sys_msgrcv ilp32_sys_msgrcv + +#include <asm/syscall.h> + +#undef __SYSCALL +#define __SYSCALL(nr, sym) [nr] = sym, + +asmlinkage long sys_mmap(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, off_t off); + +/* + * 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 f93aae5e4307..bc973d078534 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -363,9 +363,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 97bc68f4c689..bed6cf1c0a65 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -40,6 +40,12 @@ 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 +55,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 +113,39 @@ int aarch32_setup_vectors_page(struct linux_binprm *bprm, int uses_interp) return PTR_ERR_OR_ZERO(ret); } -#endif /* CONFIG_COMPAT */ +#else +int aarch32_setup_vectors_page(struct linux_binprm *bprm, int uses_interp) +{ + (void) bprm; + (void) uses_interp; -static struct vm_special_mapping vdso_spec[2]; + return -EINVAL; +} +#endif /* CONFIG_AARCH32_EL0 */ -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, + struct vm_special_mapping* vdso_spec) { 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,7 +154,7 @@ 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) { @@ -150,16 +169,46 @@ static int __init vdso_init(void) return 0; } + +static struct vm_special_mapping vdso_spec[2]; + +static int __init vdso_init(void) +{ + return vdso_init_common(&vdso_start, &vdso_end, + &vdso_pages, &vdso_pagelist, + vdso_spec); +} arch_initcall(vdso_init); +#ifdef CONFIG_ARM64_ILP32 +static struct vm_special_mapping vdso_ilp32_spec[2]; + +static int __init vdso_ilp32_init(void) +{ + return vdso_init_common(&vdso_ilp32_start, &vdso_ilp32_end, + &vdso_ilp32_pages, &vdso_ilp32_pagelist, + vdso_ilp32_spec); +} +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; + void* ret; + unsigned long pages = vdso_pages; + struct vm_special_mapping* spec = vdso_spec; + +#ifdef CONFIG_ARM64_ILP32 + if (is_ilp32_compat_task()) { + pages = vdso_ilp32_pages; + 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; @@ -171,7 +220,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]); + &spec[0]); if (IS_ERR(ret)) goto up_fail; @@ -180,7 +229,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]); + &spec[1]); if (IS_ERR(ret)) goto up_fail; diff --git a/include/linux/compat.h b/include/linux/compat.h index a76c9172b2eb..0a25d9041b8b 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..be640a90071a 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_VOIDPOINTER +#define __SIGINFO_VOIDPOINTER(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_VOIDPOINTER(sival_ptr); } sigval_t; /* @@ -86,7 +94,7 @@ typedef struct siginfo { /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ struct { - void __user *_addr; /* faulting insn/memory ref. */ + __SIGINFO_VOIDPOINTER(_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_VOIDPOINTER(_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 ee124009e12a..97b04381ada3 100644 --- a/include/uapi/asm-generic/unistd.h +++ b/include/uapi/asm-generic/unistd.h @@ -889,8 +889,11 @@ __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(__SYSCALL_NONCOMPAT) #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 787320de68e0..04799aa638fe 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -652,7 +652,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) || @@ -1140,16 +1140,26 @@ 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); |