// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Signal handling for 32bit PPC and 32bit tasks on 64bit PPC
*
* PowerPC version
* Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
* Copyright (C) 2001 IBM
* Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
*
* Derived from "arch/i386/kernel/signal.c"
* Copyright (C) 1991, 1992 Linus Torvalds
* 1997-11-28 Modified for POSIX.1b signals by Richard Henderson
*/
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/elf.h>
#include <linux/ptrace.h>
#include <linux/pagemap.h>
#include <linux/ratelimit.h>
#include <linux/syscalls.h>
#ifdef CONFIG_PPC64
#include <linux/compat.h>
#else
#include <linux/wait.h>
#include <linux/unistd.h>
#include <linux/stddef.h>
#include <linux/tty.h>
#include <linux/binfmts.h>
#endif
#include <linux/uaccess.h>
#include <asm/cacheflush.h>
#include <asm/syscalls.h>
#include <asm/sigcontext.h>
#include <asm/vdso.h>
#include <asm/switch_to.h>
#include <asm/tm.h>
#include <asm/asm-prototypes.h>
#ifdef CONFIG_PPC64
#include "ppc32.h"
#include <asm/unistd.h>
#else
#include <asm/ucontext.h>
#include <asm/pgtable.h>
#endif
#include "signal.h"
#ifdef CONFIG_PPC64
#define old_sigaction old_sigaction32
#define sigcontext sigcontext32
#define mcontext mcontext32
#define ucontext ucontext32
#define __save_altstack __compat_save_altstack
/*
* Userspace code may pass a ucontext which doesn't include VSX added
* at the end. We need to check for this case.
*/
#define UCONTEXTSIZEWITHOUTVSX \
(sizeof(struct ucontext) - sizeof(elf_vsrreghalf_t32))
/*
* Returning 0 means we return to userspace via
* ret_from_except and thus restore all user
* registers from *regs. This is what we need
* to do when a signal has been delivered.
*/
#define GP_REGS_SIZE min(sizeof(elf_gregset_t32), sizeof(struct pt_regs32))
#undef __SIGNAL_FRAMESIZE
#define __SIGNAL_FRAMESIZE __SIGNAL_FRAMESIZE32
#undef ELF_NVRREG
#define ELF_NVRREG ELF_NVRREG32
/*
* Functions for flipping sigsets (thanks to brain dead generic
* implementation that makes things simple for little endian only)
*/
static inline int put_sigset_t(compat_sigset_t __user *uset, sigset_t *set)
{
return put_compat_sigset(uset, set, sizeof(*uset));
}
static inline int get_sigset_t(sigset_t *set,
const compat_sigset_t __user *uset)
{
return get_compat_sigset(set, uset);
}
#define to_user_ptr(p) ptr_to_compat(p)
#define from_user_ptr(p) compat_ptr(p)
static inline int save_general_regs(struct pt_regs *regs,
struct mcontext __user *frame)
{
elf_greg_t64 *gregs = (elf_greg_t64 *)regs;
int i;
/* Force usr to alway see softe as 1 (interrupts enabled) */
elf_greg_t64 softe = 0x1;
WARN_ON(!FULL_REGS(regs));
for (i = 0; i <= PT_RESULT; i ++) {
if (i == 14 && !FULL_REGS(regs))
i = 32;
if ( i == PT_SOFTE) {
if(__put_user((unsigned int)softe, &frame->mc_gregs[i]))
return -EFAULT;
else
continue;
}
if (__put_user((unsigned int)gregs[i], &frame->mc_gregs[i]))
return -EFAULT;
}
return 0;
}
static inline int restore_general_regs(struct pt_regs *regs,
struct mcontext __user *sr)
{
elf_greg_t64 *gregs = (elf_greg_t64 *)regs;
int i;
for (i = 0; i <= PT_RESULT; i++) {
if ((i == PT_MSR) || (i == PT_SOFTE))
continue;
if (__get_user(gregs[i], &sr->mc_gregs[i]))
return -EFAULT;
}
return 0;
}
#else /* CONFIG_PPC64 */
#define GP_REGS_SIZE min(sizeof(elf_gregset_t), sizeof(struct pt_regs))
static inline int put_sigset_t(sigset_t __user *uset, sigset_t *set)
{
return copy_to_user(uset, set, sizeof(*uset));
}
static inline int get_sigset_t(sigset_t *set, const sigset_t __user *uset)
{
return copy_from_user(set, uset, sizeof(*uset));
}
#define to_user_ptr(p) ((unsigned long)(p))
#define from_user_ptr(p) ((void __user *)(p))
static inline int save_general_regs(struct pt_regs *regs,
struct mcontext __user *frame)
{
WARN_ON(!FULL_REGS(regs));
return __copy_to_user(&frame->mc_gregs, regs, GP_REGS_SIZE);
}
static inline int restore_general_regs(struct pt_regs *regs,
struct mcontext __user *sr)
{
/* copy up to but not including MSR */
if (__copy_from_user(regs, &sr->mc_gregs,
PT_MSR * sizeof(elf_greg_t)))
return -EFAULT;
/* copy from orig_r3 (the word after the MSR) up to the end */
if (__copy_from_user(®s->orig_gpr3, &sr->mc_gregs[PT_ORIG_R3],
GP_REGS_SIZE - PT_ORIG_R3 * sizeof(elf_greg_t)))
return -EFAULT;
return 0;
}
#endif
/*
* When we have signals to deliver, we set up on the
* user stack, going down from the original stack pointer:
* an ABI gap of 56 words
* an mcontext struct
* a sigcontext struct
* a gap of __SIGNAL_FRAMESIZE bytes
*
* Each of these things must be a multiple of 16 bytes in size. The following
* structure represent all of this except the __SIGNAL_FRAMESIZE gap
*
*/
struct sigframe {
struct sigcontext sctx; /* the sigcontext */
struct mcontext mctx; /* all the register values */
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
struct sigcontext sctx_transact;
struct mcontext mctx_transact;
#endif
/*
* Programs using the rs6000/xcoff abi can save up to 19 gp
* regs and 18 fp regs below sp before decrementing it.
*/
int abigap[56];
};
/* We use the mc_pad field for the signal return trampoline. */
#define tramp mc_pad
/*
* When we have rt signals to deliver, we set up on the
* user stack, going down from the original stack pointer:
* one rt_sigframe struct (siginfo + ucontext + ABI gap)
* a gap of __SIGNAL_FRAMESIZE+16 bytes
* (the +16 is to get the siginfo and ucontext in the same
* positions as in older kernels).
*
* Each of these things must be a multiple of 16 bytes in size.
*
*/
struct rt_sigframe {
#ifdef CONFIG_PPC64
compat_siginfo_t info;
#else
struct siginfo info;
#endif
struct ucontext uc;
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
struct ucontext uc_transact;
#endif
/*
* Programs using the rs6000/xcoff abi can save up to 19 gp
* regs and 18 fp regs below sp before decrementing it.
*/
int abigap[56];
};
#ifdef CONFIG_VSX
unsigned long copy_fpr_to_user(void __user *to,
struct task_struct *task)
{
u64 buf[ELF_NFPREG];
int i;
/* save FPR copy to local buffer then write to the thread_struct */
for (i = 0; i < (ELF_NFPREG - 1) ; i++)
buf[i] = task->thread.TS_FPR(i);
buf[i] = task->thread.fp_state.fpscr;
return __copy_to_user(to, buf, ELF_NFPREG * sizeof(double));
}
unsigned long copy_fpr_from_user(struct task_struct *task,
void __user *from)
{
u64 buf[ELF_NFPREG];
int i;
if (__copy_from_user(buf, from, ELF_NFPREG * sizeof(double)))
return 1;
for (i = 0; i < (ELF_NFPREG - 1) ; i++)
task->thread.TS_FPR(i) = buf[i];
task->thread.fp_state.fpscr = buf[i];
return 0;
}
unsigned long copy_vsx_to_user(void __user *to,
struct task_struct *task)
{
u64 buf[ELF_NVSRHALFREG];
int i;
/* save FPR copy to local buffer then write to the thread_struct */
for (i = 0; i < ELF_NVSRHALFREG; i++)
buf[i] = task->thread.fp_state.fpr[i][TS_VSRLOWOFFSET];
return __copy_to_user(to, buf, ELF_NVSRHALFREG * sizeof(double));
}
unsigned long copy_vsx_from_user(struct task_struct *task,
void __user *from)
{
u64 buf[ELF_NVSRHALFREG];
int i;
if (__copy_from_user(buf, from, ELF_NVSRHALFREG * sizeof(double)))
return 1;
for (i = 0; i < ELF_NVSRHALFREG ; i++)
task->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = buf[i];
return 0;
}
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
unsigned long copy_ckfpr_to_user(void __user *to,
struct task_
|