1b74baf4aSHuacai Chen // SPDX-License-Identifier: GPL-2.0+ 2b74baf4aSHuacai Chen /* 3b74baf4aSHuacai Chen * Author: Hanlu Li <lihanlu@loongson.cn> 4b74baf4aSHuacai Chen * Huacai Chen <chenhuacai@loongson.cn> 5b74baf4aSHuacai Chen * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 6b74baf4aSHuacai Chen * 7b74baf4aSHuacai Chen * Derived from MIPS: 8b74baf4aSHuacai Chen * Copyright (C) 1991, 1992 Linus Torvalds 9b74baf4aSHuacai Chen * Copyright (C) 1994 - 2000 Ralf Baechle 10b74baf4aSHuacai Chen * Copyright (C) 1999, 2000 Silicon Graphics, Inc. 11b74baf4aSHuacai Chen * Copyright (C) 2014, Imagination Technologies Ltd. 12b74baf4aSHuacai Chen */ 13b74baf4aSHuacai Chen #include <linux/audit.h> 14b74baf4aSHuacai Chen #include <linux/cache.h> 15b74baf4aSHuacai Chen #include <linux/context_tracking.h> 16b74baf4aSHuacai Chen #include <linux/irqflags.h> 17b74baf4aSHuacai Chen #include <linux/sched.h> 18b74baf4aSHuacai Chen #include <linux/mm.h> 19b74baf4aSHuacai Chen #include <linux/personality.h> 20b74baf4aSHuacai Chen #include <linux/smp.h> 21b74baf4aSHuacai Chen #include <linux/kernel.h> 22b74baf4aSHuacai Chen #include <linux/signal.h> 23b74baf4aSHuacai Chen #include <linux/errno.h> 24b74baf4aSHuacai Chen #include <linux/wait.h> 25b74baf4aSHuacai Chen #include <linux/ptrace.h> 26b74baf4aSHuacai Chen #include <linux/unistd.h> 27b74baf4aSHuacai Chen #include <linux/compiler.h> 28b74baf4aSHuacai Chen #include <linux/syscalls.h> 29b74baf4aSHuacai Chen #include <linux/uaccess.h> 30b74baf4aSHuacai Chen 31b74baf4aSHuacai Chen #include <asm/asm.h> 32b74baf4aSHuacai Chen #include <asm/cacheflush.h> 33b74baf4aSHuacai Chen #include <asm/cpu-features.h> 34b74baf4aSHuacai Chen #include <asm/fpu.h> 35b74baf4aSHuacai Chen #include <asm/ucontext.h> 36b74baf4aSHuacai Chen #include <asm/vdso.h> 37b74baf4aSHuacai Chen 38b74baf4aSHuacai Chen #ifdef DEBUG_SIG 39b74baf4aSHuacai Chen # define DEBUGP(fmt, args...) printk("%s: " fmt, __func__, ##args) 40b74baf4aSHuacai Chen #else 41b74baf4aSHuacai Chen # define DEBUGP(fmt, args...) 42b74baf4aSHuacai Chen #endif 43b74baf4aSHuacai Chen 44b74baf4aSHuacai Chen /* Make sure we will not lose FPU ownership */ 45b74baf4aSHuacai Chen #define lock_fpu_owner() ({ preempt_disable(); pagefault_disable(); }) 46b74baf4aSHuacai Chen #define unlock_fpu_owner() ({ pagefault_enable(); preempt_enable(); }) 47b74baf4aSHuacai Chen 48b74baf4aSHuacai Chen /* Assembly functions to move context to/from the FPU */ 49b74baf4aSHuacai Chen extern asmlinkage int 50b74baf4aSHuacai Chen _save_fp_context(void __user *fpregs, void __user *fcc, void __user *csr); 51b74baf4aSHuacai Chen extern asmlinkage int 52b74baf4aSHuacai Chen _restore_fp_context(void __user *fpregs, void __user *fcc, void __user *csr); 53*61650023SHuacai Chen extern asmlinkage int 54*61650023SHuacai Chen _save_lsx_context(void __user *fpregs, void __user *fcc, void __user *fcsr); 55*61650023SHuacai Chen extern asmlinkage int 56*61650023SHuacai Chen _restore_lsx_context(void __user *fpregs, void __user *fcc, void __user *fcsr); 57*61650023SHuacai Chen extern asmlinkage int 58*61650023SHuacai Chen _save_lasx_context(void __user *fpregs, void __user *fcc, void __user *fcsr); 59*61650023SHuacai Chen extern asmlinkage int 60*61650023SHuacai Chen _restore_lasx_context(void __user *fpregs, void __user *fcc, void __user *fcsr); 61b74baf4aSHuacai Chen 62b74baf4aSHuacai Chen struct rt_sigframe { 63b74baf4aSHuacai Chen struct siginfo rs_info; 64b74baf4aSHuacai Chen struct ucontext rs_uctx; 65b74baf4aSHuacai Chen }; 66b74baf4aSHuacai Chen 67b74baf4aSHuacai Chen struct _ctx_layout { 68b74baf4aSHuacai Chen struct sctx_info *addr; 69b74baf4aSHuacai Chen unsigned int size; 70b74baf4aSHuacai Chen }; 71b74baf4aSHuacai Chen 72b74baf4aSHuacai Chen struct extctx_layout { 73b74baf4aSHuacai Chen unsigned long size; 74b74baf4aSHuacai Chen unsigned int flags; 75b74baf4aSHuacai Chen struct _ctx_layout fpu; 76*61650023SHuacai Chen struct _ctx_layout lsx; 77*61650023SHuacai Chen struct _ctx_layout lasx; 78b74baf4aSHuacai Chen struct _ctx_layout end; 79b74baf4aSHuacai Chen }; 80b74baf4aSHuacai Chen 81b74baf4aSHuacai Chen static void __user *get_ctx_through_ctxinfo(struct sctx_info *info) 82b74baf4aSHuacai Chen { 83b74baf4aSHuacai Chen return (void __user *)((char *)info + sizeof(struct sctx_info)); 84b74baf4aSHuacai Chen } 85b74baf4aSHuacai Chen 86b74baf4aSHuacai Chen /* 87b74baf4aSHuacai Chen * Thread saved context copy to/from a signal context presumed to be on the 88b74baf4aSHuacai Chen * user stack, and therefore accessed with appropriate macros from uaccess.h. 89b74baf4aSHuacai Chen */ 90b74baf4aSHuacai Chen static int copy_fpu_to_sigcontext(struct fpu_context __user *ctx) 91b74baf4aSHuacai Chen { 92b74baf4aSHuacai Chen int i; 93b74baf4aSHuacai Chen int err = 0; 94b74baf4aSHuacai Chen uint64_t __user *regs = (uint64_t *)&ctx->regs; 95b74baf4aSHuacai Chen uint64_t __user *fcc = &ctx->fcc; 96b74baf4aSHuacai Chen uint32_t __user *fcsr = &ctx->fcsr; 97b74baf4aSHuacai Chen 98b74baf4aSHuacai Chen for (i = 0; i < NUM_FPU_REGS; i++) { 99b74baf4aSHuacai Chen err |= 100b74baf4aSHuacai Chen __put_user(get_fpr64(¤t->thread.fpu.fpr[i], 0), 101b74baf4aSHuacai Chen ®s[i]); 102b74baf4aSHuacai Chen } 103b74baf4aSHuacai Chen err |= __put_user(current->thread.fpu.fcc, fcc); 104b74baf4aSHuacai Chen err |= __put_user(current->thread.fpu.fcsr, fcsr); 105b74baf4aSHuacai Chen 106b74baf4aSHuacai Chen return err; 107b74baf4aSHuacai Chen } 108b74baf4aSHuacai Chen 109b74baf4aSHuacai Chen static int copy_fpu_from_sigcontext(struct fpu_context __user *ctx) 110b74baf4aSHuacai Chen { 111b74baf4aSHuacai Chen int i; 112b74baf4aSHuacai Chen int err = 0; 113b74baf4aSHuacai Chen u64 fpr_val; 114b74baf4aSHuacai Chen uint64_t __user *regs = (uint64_t *)&ctx->regs; 115b74baf4aSHuacai Chen uint64_t __user *fcc = &ctx->fcc; 116b74baf4aSHuacai Chen uint32_t __user *fcsr = &ctx->fcsr; 117b74baf4aSHuacai Chen 118b74baf4aSHuacai Chen for (i = 0; i < NUM_FPU_REGS; i++) { 119b74baf4aSHuacai Chen err |= __get_user(fpr_val, ®s[i]); 120b74baf4aSHuacai Chen set_fpr64(¤t->thread.fpu.fpr[i], 0, fpr_val); 121b74baf4aSHuacai Chen } 122b74baf4aSHuacai Chen err |= __get_user(current->thread.fpu.fcc, fcc); 123b74baf4aSHuacai Chen err |= __get_user(current->thread.fpu.fcsr, fcsr); 124b74baf4aSHuacai Chen 125b74baf4aSHuacai Chen return err; 126b74baf4aSHuacai Chen } 127b74baf4aSHuacai Chen 128*61650023SHuacai Chen static int copy_lsx_to_sigcontext(struct lsx_context __user *ctx) 129*61650023SHuacai Chen { 130*61650023SHuacai Chen int i; 131*61650023SHuacai Chen int err = 0; 132*61650023SHuacai Chen uint64_t __user *regs = (uint64_t *)&ctx->regs; 133*61650023SHuacai Chen uint64_t __user *fcc = &ctx->fcc; 134*61650023SHuacai Chen uint32_t __user *fcsr = &ctx->fcsr; 135*61650023SHuacai Chen 136*61650023SHuacai Chen for (i = 0; i < NUM_FPU_REGS; i++) { 137*61650023SHuacai Chen err |= __put_user(get_fpr64(¤t->thread.fpu.fpr[i], 0), 138*61650023SHuacai Chen ®s[2*i]); 139*61650023SHuacai Chen err |= __put_user(get_fpr64(¤t->thread.fpu.fpr[i], 1), 140*61650023SHuacai Chen ®s[2*i+1]); 141*61650023SHuacai Chen } 142*61650023SHuacai Chen err |= __put_user(current->thread.fpu.fcc, fcc); 143*61650023SHuacai Chen err |= __put_user(current->thread.fpu.fcsr, fcsr); 144*61650023SHuacai Chen 145*61650023SHuacai Chen return err; 146*61650023SHuacai Chen } 147*61650023SHuacai Chen 148*61650023SHuacai Chen static int copy_lsx_from_sigcontext(struct lsx_context __user *ctx) 149*61650023SHuacai Chen { 150*61650023SHuacai Chen int i; 151*61650023SHuacai Chen int err = 0; 152*61650023SHuacai Chen u64 fpr_val; 153*61650023SHuacai Chen uint64_t __user *regs = (uint64_t *)&ctx->regs; 154*61650023SHuacai Chen uint64_t __user *fcc = &ctx->fcc; 155*61650023SHuacai Chen uint32_t __user *fcsr = &ctx->fcsr; 156*61650023SHuacai Chen 157*61650023SHuacai Chen for (i = 0; i < NUM_FPU_REGS; i++) { 158*61650023SHuacai Chen err |= __get_user(fpr_val, ®s[2*i]); 159*61650023SHuacai Chen set_fpr64(¤t->thread.fpu.fpr[i], 0, fpr_val); 160*61650023SHuacai Chen err |= __get_user(fpr_val, ®s[2*i+1]); 161*61650023SHuacai Chen set_fpr64(¤t->thread.fpu.fpr[i], 1, fpr_val); 162*61650023SHuacai Chen } 163*61650023SHuacai Chen err |= __get_user(current->thread.fpu.fcc, fcc); 164*61650023SHuacai Chen err |= __get_user(current->thread.fpu.fcsr, fcsr); 165*61650023SHuacai Chen 166*61650023SHuacai Chen return err; 167*61650023SHuacai Chen } 168*61650023SHuacai Chen 169*61650023SHuacai Chen static int copy_lasx_to_sigcontext(struct lasx_context __user *ctx) 170*61650023SHuacai Chen { 171*61650023SHuacai Chen int i; 172*61650023SHuacai Chen int err = 0; 173*61650023SHuacai Chen uint64_t __user *regs = (uint64_t *)&ctx->regs; 174*61650023SHuacai Chen uint64_t __user *fcc = &ctx->fcc; 175*61650023SHuacai Chen uint32_t __user *fcsr = &ctx->fcsr; 176*61650023SHuacai Chen 177*61650023SHuacai Chen for (i = 0; i < NUM_FPU_REGS; i++) { 178*61650023SHuacai Chen err |= __put_user(get_fpr64(¤t->thread.fpu.fpr[i], 0), 179*61650023SHuacai Chen ®s[4*i]); 180*61650023SHuacai Chen err |= __put_user(get_fpr64(¤t->thread.fpu.fpr[i], 1), 181*61650023SHuacai Chen ®s[4*i+1]); 182*61650023SHuacai Chen err |= __put_user(get_fpr64(¤t->thread.fpu.fpr[i], 2), 183*61650023SHuacai Chen ®s[4*i+2]); 184*61650023SHuacai Chen err |= __put_user(get_fpr64(¤t->thread.fpu.fpr[i], 3), 185*61650023SHuacai Chen ®s[4*i+3]); 186*61650023SHuacai Chen } 187*61650023SHuacai Chen err |= __put_user(current->thread.fpu.fcc, fcc); 188*61650023SHuacai Chen err |= __put_user(current->thread.fpu.fcsr, fcsr); 189*61650023SHuacai Chen 190*61650023SHuacai Chen return err; 191*61650023SHuacai Chen } 192*61650023SHuacai Chen 193*61650023SHuacai Chen static int copy_lasx_from_sigcontext(struct lasx_context __user *ctx) 194*61650023SHuacai Chen { 195*61650023SHuacai Chen int i; 196*61650023SHuacai Chen int err = 0; 197*61650023SHuacai Chen u64 fpr_val; 198*61650023SHuacai Chen uint64_t __user *regs = (uint64_t *)&ctx->regs; 199*61650023SHuacai Chen uint64_t __user *fcc = &ctx->fcc; 200*61650023SHuacai Chen uint32_t __user *fcsr = &ctx->fcsr; 201*61650023SHuacai Chen 202*61650023SHuacai Chen for (i = 0; i < NUM_FPU_REGS; i++) { 203*61650023SHuacai Chen err |= __get_user(fpr_val, ®s[4*i]); 204*61650023SHuacai Chen set_fpr64(¤t->thread.fpu.fpr[i], 0, fpr_val); 205*61650023SHuacai Chen err |= __get_user(fpr_val, ®s[4*i+1]); 206*61650023SHuacai Chen set_fpr64(¤t->thread.fpu.fpr[i], 1, fpr_val); 207*61650023SHuacai Chen err |= __get_user(fpr_val, ®s[4*i+2]); 208*61650023SHuacai Chen set_fpr64(¤t->thread.fpu.fpr[i], 2, fpr_val); 209*61650023SHuacai Chen err |= __get_user(fpr_val, ®s[4*i+3]); 210*61650023SHuacai Chen set_fpr64(¤t->thread.fpu.fpr[i], 3, fpr_val); 211*61650023SHuacai Chen } 212*61650023SHuacai Chen err |= __get_user(current->thread.fpu.fcc, fcc); 213*61650023SHuacai Chen err |= __get_user(current->thread.fpu.fcsr, fcsr); 214*61650023SHuacai Chen 215*61650023SHuacai Chen return err; 216*61650023SHuacai Chen } 217*61650023SHuacai Chen 218b74baf4aSHuacai Chen /* 219b74baf4aSHuacai Chen * Wrappers for the assembly _{save,restore}_fp_context functions. 220b74baf4aSHuacai Chen */ 221b74baf4aSHuacai Chen static int save_hw_fpu_context(struct fpu_context __user *ctx) 222b74baf4aSHuacai Chen { 223b74baf4aSHuacai Chen uint64_t __user *regs = (uint64_t *)&ctx->regs; 224b74baf4aSHuacai Chen uint64_t __user *fcc = &ctx->fcc; 225b74baf4aSHuacai Chen uint32_t __user *fcsr = &ctx->fcsr; 226b74baf4aSHuacai Chen 227b74baf4aSHuacai Chen return _save_fp_context(regs, fcc, fcsr); 228b74baf4aSHuacai Chen } 229b74baf4aSHuacai Chen 230b74baf4aSHuacai Chen static int restore_hw_fpu_context(struct fpu_context __user *ctx) 231b74baf4aSHuacai Chen { 232b74baf4aSHuacai Chen uint64_t __user *regs = (uint64_t *)&ctx->regs; 233b74baf4aSHuacai Chen uint64_t __user *fcc = &ctx->fcc; 234b74baf4aSHuacai Chen uint32_t __user *fcsr = &ctx->fcsr; 235b74baf4aSHuacai Chen 236b74baf4aSHuacai Chen return _restore_fp_context(regs, fcc, fcsr); 237b74baf4aSHuacai Chen } 238b74baf4aSHuacai Chen 239*61650023SHuacai Chen static int save_hw_lsx_context(struct lsx_context __user *ctx) 240*61650023SHuacai Chen { 241*61650023SHuacai Chen uint64_t __user *regs = (uint64_t *)&ctx->regs; 242*61650023SHuacai Chen uint64_t __user *fcc = &ctx->fcc; 243*61650023SHuacai Chen uint32_t __user *fcsr = &ctx->fcsr; 244*61650023SHuacai Chen 245*61650023SHuacai Chen return _save_lsx_context(regs, fcc, fcsr); 246*61650023SHuacai Chen } 247*61650023SHuacai Chen 248*61650023SHuacai Chen static int restore_hw_lsx_context(struct lsx_context __user *ctx) 249*61650023SHuacai Chen { 250*61650023SHuacai Chen uint64_t __user *regs = (uint64_t *)&ctx->regs; 251*61650023SHuacai Chen uint64_t __user *fcc = &ctx->fcc; 252*61650023SHuacai Chen uint32_t __user *fcsr = &ctx->fcsr; 253*61650023SHuacai Chen 254*61650023SHuacai Chen return _restore_lsx_context(regs, fcc, fcsr); 255*61650023SHuacai Chen } 256*61650023SHuacai Chen 257*61650023SHuacai Chen static int save_hw_lasx_context(struct lasx_context __user *ctx) 258*61650023SHuacai Chen { 259*61650023SHuacai Chen uint64_t __user *regs = (uint64_t *)&ctx->regs; 260*61650023SHuacai Chen uint64_t __user *fcc = &ctx->fcc; 261*61650023SHuacai Chen uint32_t __user *fcsr = &ctx->fcsr; 262*61650023SHuacai Chen 263*61650023SHuacai Chen return _save_lasx_context(regs, fcc, fcsr); 264*61650023SHuacai Chen } 265*61650023SHuacai Chen 266*61650023SHuacai Chen static int restore_hw_lasx_context(struct lasx_context __user *ctx) 267*61650023SHuacai Chen { 268*61650023SHuacai Chen uint64_t __user *regs = (uint64_t *)&ctx->regs; 269*61650023SHuacai Chen uint64_t __user *fcc = &ctx->fcc; 270*61650023SHuacai Chen uint32_t __user *fcsr = &ctx->fcsr; 271*61650023SHuacai Chen 272*61650023SHuacai Chen return _restore_lasx_context(regs, fcc, fcsr); 273*61650023SHuacai Chen } 274*61650023SHuacai Chen 275b74baf4aSHuacai Chen static int fcsr_pending(unsigned int __user *fcsr) 276b74baf4aSHuacai Chen { 277b74baf4aSHuacai Chen int err, sig = 0; 278b74baf4aSHuacai Chen unsigned int csr, enabled; 279b74baf4aSHuacai Chen 280b74baf4aSHuacai Chen err = __get_user(csr, fcsr); 281b74baf4aSHuacai Chen enabled = ((csr & FPU_CSR_ALL_E) << 24); 282b74baf4aSHuacai Chen /* 283b74baf4aSHuacai Chen * If the signal handler set some FPU exceptions, clear it and 284b74baf4aSHuacai Chen * send SIGFPE. 285b74baf4aSHuacai Chen */ 286b74baf4aSHuacai Chen if (csr & enabled) { 287b74baf4aSHuacai Chen csr &= ~enabled; 288b74baf4aSHuacai Chen err |= __put_user(csr, fcsr); 289b74baf4aSHuacai Chen sig = SIGFPE; 290b74baf4aSHuacai Chen } 291b74baf4aSHuacai Chen return err ?: sig; 292b74baf4aSHuacai Chen } 293b74baf4aSHuacai Chen 294b74baf4aSHuacai Chen /* 295b74baf4aSHuacai Chen * Helper routines 296b74baf4aSHuacai Chen */ 297b74baf4aSHuacai Chen static int protected_save_fpu_context(struct extctx_layout *extctx) 298b74baf4aSHuacai Chen { 299b74baf4aSHuacai Chen int err = 0; 300b74baf4aSHuacai Chen struct sctx_info __user *info = extctx->fpu.addr; 301b74baf4aSHuacai Chen struct fpu_context __user *fpu_ctx = (struct fpu_context *)get_ctx_through_ctxinfo(info); 302b74baf4aSHuacai Chen uint64_t __user *regs = (uint64_t *)&fpu_ctx->regs; 303b74baf4aSHuacai Chen uint64_t __user *fcc = &fpu_ctx->fcc; 304b74baf4aSHuacai Chen uint32_t __user *fcsr = &fpu_ctx->fcsr; 305b74baf4aSHuacai Chen 306b74baf4aSHuacai Chen while (1) { 307b74baf4aSHuacai Chen lock_fpu_owner(); 308b74baf4aSHuacai Chen if (is_fpu_owner()) 309b74baf4aSHuacai Chen err = save_hw_fpu_context(fpu_ctx); 310b74baf4aSHuacai Chen else 311b74baf4aSHuacai Chen err = copy_fpu_to_sigcontext(fpu_ctx); 312b74baf4aSHuacai Chen unlock_fpu_owner(); 313b74baf4aSHuacai Chen 314b74baf4aSHuacai Chen err |= __put_user(FPU_CTX_MAGIC, &info->magic); 315b74baf4aSHuacai Chen err |= __put_user(extctx->fpu.size, &info->size); 316b74baf4aSHuacai Chen 317b74baf4aSHuacai Chen if (likely(!err)) 318b74baf4aSHuacai Chen break; 319b74baf4aSHuacai Chen /* Touch the FPU context and try again */ 320b74baf4aSHuacai Chen err = __put_user(0, ®s[0]) | 321b74baf4aSHuacai Chen __put_user(0, ®s[31]) | 322b74baf4aSHuacai Chen __put_user(0, fcc) | 323b74baf4aSHuacai Chen __put_user(0, fcsr); 324b74baf4aSHuacai Chen if (err) 325b74baf4aSHuacai Chen return err; /* really bad sigcontext */ 326b74baf4aSHuacai Chen } 327b74baf4aSHuacai Chen 328b74baf4aSHuacai Chen return err; 329b74baf4aSHuacai Chen } 330b74baf4aSHuacai Chen 331b74baf4aSHuacai Chen static int protected_restore_fpu_context(struct extctx_layout *extctx) 332b74baf4aSHuacai Chen { 333b74baf4aSHuacai Chen int err = 0, sig = 0, tmp __maybe_unused; 334b74baf4aSHuacai Chen struct sctx_info __user *info = extctx->fpu.addr; 335b74baf4aSHuacai Chen struct fpu_context __user *fpu_ctx = (struct fpu_context *)get_ctx_through_ctxinfo(info); 336b74baf4aSHuacai Chen uint64_t __user *regs = (uint64_t *)&fpu_ctx->regs; 337b74baf4aSHuacai Chen uint64_t __user *fcc = &fpu_ctx->fcc; 338b74baf4aSHuacai Chen uint32_t __user *fcsr = &fpu_ctx->fcsr; 339b74baf4aSHuacai Chen 340b74baf4aSHuacai Chen err = sig = fcsr_pending(fcsr); 341b74baf4aSHuacai Chen if (err < 0) 342b74baf4aSHuacai Chen return err; 343b74baf4aSHuacai Chen 344b74baf4aSHuacai Chen while (1) { 345b74baf4aSHuacai Chen lock_fpu_owner(); 346b74baf4aSHuacai Chen if (is_fpu_owner()) 347b74baf4aSHuacai Chen err = restore_hw_fpu_context(fpu_ctx); 348b74baf4aSHuacai Chen else 349b74baf4aSHuacai Chen err = copy_fpu_from_sigcontext(fpu_ctx); 350b74baf4aSHuacai Chen unlock_fpu_owner(); 351b74baf4aSHuacai Chen 352b74baf4aSHuacai Chen if (likely(!err)) 353b74baf4aSHuacai Chen break; 354b74baf4aSHuacai Chen /* Touch the FPU context and try again */ 355b74baf4aSHuacai Chen err = __get_user(tmp, ®s[0]) | 356b74baf4aSHuacai Chen __get_user(tmp, ®s[31]) | 357b74baf4aSHuacai Chen __get_user(tmp, fcc) | 358b74baf4aSHuacai Chen __get_user(tmp, fcsr); 359b74baf4aSHuacai Chen if (err) 360b74baf4aSHuacai Chen break; /* really bad sigcontext */ 361b74baf4aSHuacai Chen } 362b74baf4aSHuacai Chen 363b74baf4aSHuacai Chen return err ?: sig; 364b74baf4aSHuacai Chen } 365b74baf4aSHuacai Chen 366*61650023SHuacai Chen static int protected_save_lsx_context(struct extctx_layout *extctx) 367*61650023SHuacai Chen { 368*61650023SHuacai Chen int err = 0; 369*61650023SHuacai Chen struct sctx_info __user *info = extctx->lsx.addr; 370*61650023SHuacai Chen struct lsx_context __user *lsx_ctx = (struct lsx_context *)get_ctx_through_ctxinfo(info); 371*61650023SHuacai Chen uint64_t __user *regs = (uint64_t *)&lsx_ctx->regs; 372*61650023SHuacai Chen uint64_t __user *fcc = &lsx_ctx->fcc; 373*61650023SHuacai Chen uint32_t __user *fcsr = &lsx_ctx->fcsr; 374*61650023SHuacai Chen 375*61650023SHuacai Chen while (1) { 376*61650023SHuacai Chen lock_fpu_owner(); 377*61650023SHuacai Chen if (is_lsx_enabled()) 378*61650023SHuacai Chen err = save_hw_lsx_context(lsx_ctx); 379*61650023SHuacai Chen else { 380*61650023SHuacai Chen if (is_fpu_owner()) 381*61650023SHuacai Chen save_fp(current); 382*61650023SHuacai Chen err = copy_lsx_to_sigcontext(lsx_ctx); 383*61650023SHuacai Chen } 384*61650023SHuacai Chen unlock_fpu_owner(); 385*61650023SHuacai Chen 386*61650023SHuacai Chen err |= __put_user(LSX_CTX_MAGIC, &info->magic); 387*61650023SHuacai Chen err |= __put_user(extctx->lsx.size, &info->size); 388*61650023SHuacai Chen 389*61650023SHuacai Chen if (likely(!err)) 390*61650023SHuacai Chen break; 391*61650023SHuacai Chen /* Touch the LSX context and try again */ 392*61650023SHuacai Chen err = __put_user(0, ®s[0]) | 393*61650023SHuacai Chen __put_user(0, ®s[32*2-1]) | 394*61650023SHuacai Chen __put_user(0, fcc) | 395*61650023SHuacai Chen __put_user(0, fcsr); 396*61650023SHuacai Chen if (err) 397*61650023SHuacai Chen return err; /* really bad sigcontext */ 398*61650023SHuacai Chen } 399*61650023SHuacai Chen 400*61650023SHuacai Chen return err; 401*61650023SHuacai Chen } 402*61650023SHuacai Chen 403*61650023SHuacai Chen static int protected_restore_lsx_context(struct extctx_layout *extctx) 404*61650023SHuacai Chen { 405*61650023SHuacai Chen int err = 0, sig = 0, tmp __maybe_unused; 406*61650023SHuacai Chen struct sctx_info __user *info = extctx->lsx.addr; 407*61650023SHuacai Chen struct lsx_context __user *lsx_ctx = (struct lsx_context *)get_ctx_through_ctxinfo(info); 408*61650023SHuacai Chen uint64_t __user *regs = (uint64_t *)&lsx_ctx->regs; 409*61650023SHuacai Chen uint64_t __user *fcc = &lsx_ctx->fcc; 410*61650023SHuacai Chen uint32_t __user *fcsr = &lsx_ctx->fcsr; 411*61650023SHuacai Chen 412*61650023SHuacai Chen err = sig = fcsr_pending(fcsr); 413*61650023SHuacai Chen if (err < 0) 414*61650023SHuacai Chen return err; 415*61650023SHuacai Chen 416*61650023SHuacai Chen while (1) { 417*61650023SHuacai Chen lock_fpu_owner(); 418*61650023SHuacai Chen if (is_lsx_enabled()) 419*61650023SHuacai Chen err = restore_hw_lsx_context(lsx_ctx); 420*61650023SHuacai Chen else { 421*61650023SHuacai Chen err = copy_lsx_from_sigcontext(lsx_ctx); 422*61650023SHuacai Chen if (is_fpu_owner()) 423*61650023SHuacai Chen restore_fp(current); 424*61650023SHuacai Chen } 425*61650023SHuacai Chen unlock_fpu_owner(); 426*61650023SHuacai Chen 427*61650023SHuacai Chen if (likely(!err)) 428*61650023SHuacai Chen break; 429*61650023SHuacai Chen /* Touch the LSX context and try again */ 430*61650023SHuacai Chen err = __get_user(tmp, ®s[0]) | 431*61650023SHuacai Chen __get_user(tmp, ®s[32*2-1]) | 432*61650023SHuacai Chen __get_user(tmp, fcc) | 433*61650023SHuacai Chen __get_user(tmp, fcsr); 434*61650023SHuacai Chen if (err) 435*61650023SHuacai Chen break; /* really bad sigcontext */ 436*61650023SHuacai Chen } 437*61650023SHuacai Chen 438*61650023SHuacai Chen return err ?: sig; 439*61650023SHuacai Chen } 440*61650023SHuacai Chen 441*61650023SHuacai Chen static int protected_save_lasx_context(struct extctx_layout *extctx) 442*61650023SHuacai Chen { 443*61650023SHuacai Chen int err = 0; 444*61650023SHuacai Chen struct sctx_info __user *info = extctx->lasx.addr; 445*61650023SHuacai Chen struct lasx_context __user *lasx_ctx = 446*61650023SHuacai Chen (struct lasx_context *)get_ctx_through_ctxinfo(info); 447*61650023SHuacai Chen uint64_t __user *regs = (uint64_t *)&lasx_ctx->regs; 448*61650023SHuacai Chen uint64_t __user *fcc = &lasx_ctx->fcc; 449*61650023SHuacai Chen uint32_t __user *fcsr = &lasx_ctx->fcsr; 450*61650023SHuacai Chen 451*61650023SHuacai Chen while (1) { 452*61650023SHuacai Chen lock_fpu_owner(); 453*61650023SHuacai Chen if (is_lasx_enabled()) 454*61650023SHuacai Chen err = save_hw_lasx_context(lasx_ctx); 455*61650023SHuacai Chen else { 456*61650023SHuacai Chen if (is_lsx_enabled()) 457*61650023SHuacai Chen save_lsx(current); 458*61650023SHuacai Chen else if (is_fpu_owner()) 459*61650023SHuacai Chen save_fp(current); 460*61650023SHuacai Chen err = copy_lasx_to_sigcontext(lasx_ctx); 461*61650023SHuacai Chen } 462*61650023SHuacai Chen unlock_fpu_owner(); 463*61650023SHuacai Chen 464*61650023SHuacai Chen err |= __put_user(LASX_CTX_MAGIC, &info->magic); 465*61650023SHuacai Chen err |= __put_user(extctx->lasx.size, &info->size); 466*61650023SHuacai Chen 467*61650023SHuacai Chen if (likely(!err)) 468*61650023SHuacai Chen break; 469*61650023SHuacai Chen /* Touch the LASX context and try again */ 470*61650023SHuacai Chen err = __put_user(0, ®s[0]) | 471*61650023SHuacai Chen __put_user(0, ®s[32*4-1]) | 472*61650023SHuacai Chen __put_user(0, fcc) | 473*61650023SHuacai Chen __put_user(0, fcsr); 474*61650023SHuacai Chen if (err) 475*61650023SHuacai Chen return err; /* really bad sigcontext */ 476*61650023SHuacai Chen } 477*61650023SHuacai Chen 478*61650023SHuacai Chen return err; 479*61650023SHuacai Chen } 480*61650023SHuacai Chen 481*61650023SHuacai Chen static int protected_restore_lasx_context(struct extctx_layout *extctx) 482*61650023SHuacai Chen { 483*61650023SHuacai Chen int err = 0, sig = 0, tmp __maybe_unused; 484*61650023SHuacai Chen struct sctx_info __user *info = extctx->lasx.addr; 485*61650023SHuacai Chen struct lasx_context __user *lasx_ctx = 486*61650023SHuacai Chen (struct lasx_context *)get_ctx_through_ctxinfo(info); 487*61650023SHuacai Chen uint64_t __user *regs = (uint64_t *)&lasx_ctx->regs; 488*61650023SHuacai Chen uint64_t __user *fcc = &lasx_ctx->fcc; 489*61650023SHuacai Chen uint32_t __user *fcsr = &lasx_ctx->fcsr; 490*61650023SHuacai Chen 491*61650023SHuacai Chen err = sig = fcsr_pending(fcsr); 492*61650023SHuacai Chen if (err < 0) 493*61650023SHuacai Chen return err; 494*61650023SHuacai Chen 495*61650023SHuacai Chen while (1) { 496*61650023SHuacai Chen lock_fpu_owner(); 497*61650023SHuacai Chen if (is_lasx_enabled()) 498*61650023SHuacai Chen err = restore_hw_lasx_context(lasx_ctx); 499*61650023SHuacai Chen else { 500*61650023SHuacai Chen err = copy_lasx_from_sigcontext(lasx_ctx); 501*61650023SHuacai Chen if (is_lsx_enabled()) 502*61650023SHuacai Chen restore_lsx(current); 503*61650023SHuacai Chen else if (is_fpu_owner()) 504*61650023SHuacai Chen restore_fp(current); 505*61650023SHuacai Chen } 506*61650023SHuacai Chen unlock_fpu_owner(); 507*61650023SHuacai Chen 508*61650023SHuacai Chen if (likely(!err)) 509*61650023SHuacai Chen break; 510*61650023SHuacai Chen /* Touch the LASX context and try again */ 511*61650023SHuacai Chen err = __get_user(tmp, ®s[0]) | 512*61650023SHuacai Chen __get_user(tmp, ®s[32*4-1]) | 513*61650023SHuacai Chen __get_user(tmp, fcc) | 514*61650023SHuacai Chen __get_user(tmp, fcsr); 515*61650023SHuacai Chen if (err) 516*61650023SHuacai Chen break; /* really bad sigcontext */ 517*61650023SHuacai Chen } 518*61650023SHuacai Chen 519*61650023SHuacai Chen return err ?: sig; 520*61650023SHuacai Chen } 521*61650023SHuacai Chen 522b74baf4aSHuacai Chen static int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, 523b74baf4aSHuacai Chen struct extctx_layout *extctx) 524b74baf4aSHuacai Chen { 525b74baf4aSHuacai Chen int i, err = 0; 526b74baf4aSHuacai Chen struct sctx_info __user *info; 527b74baf4aSHuacai Chen 528b74baf4aSHuacai Chen err |= __put_user(regs->csr_era, &sc->sc_pc); 529b74baf4aSHuacai Chen err |= __put_user(extctx->flags, &sc->sc_flags); 530b74baf4aSHuacai Chen 531b74baf4aSHuacai Chen err |= __put_user(0, &sc->sc_regs[0]); 532b74baf4aSHuacai Chen for (i = 1; i < 32; i++) 533b74baf4aSHuacai Chen err |= __put_user(regs->regs[i], &sc->sc_regs[i]); 534b74baf4aSHuacai Chen 535*61650023SHuacai Chen if (extctx->lasx.addr) 536*61650023SHuacai Chen err |= protected_save_lasx_context(extctx); 537*61650023SHuacai Chen else if (extctx->lsx.addr) 538*61650023SHuacai Chen err |= protected_save_lsx_context(extctx); 539*61650023SHuacai Chen else if (extctx->fpu.addr) 540b74baf4aSHuacai Chen err |= protected_save_fpu_context(extctx); 541b74baf4aSHuacai Chen 542b74baf4aSHuacai Chen /* Set the "end" magic */ 543b74baf4aSHuacai Chen info = (struct sctx_info *)extctx->end.addr; 544b74baf4aSHuacai Chen err |= __put_user(0, &info->magic); 545b74baf4aSHuacai Chen err |= __put_user(0, &info->size); 546b74baf4aSHuacai Chen 547b74baf4aSHuacai Chen return err; 548b74baf4aSHuacai Chen } 549b74baf4aSHuacai Chen 550b74baf4aSHuacai Chen static int parse_extcontext(struct sigcontext __user *sc, struct extctx_layout *extctx) 551b74baf4aSHuacai Chen { 552b74baf4aSHuacai Chen int err = 0; 553b74baf4aSHuacai Chen unsigned int magic, size; 554b74baf4aSHuacai Chen struct sctx_info __user *info = (struct sctx_info __user *)&sc->sc_extcontext; 555b74baf4aSHuacai Chen 556b74baf4aSHuacai Chen while(1) { 557b74baf4aSHuacai Chen err |= __get_user(magic, &info->magic); 558b74baf4aSHuacai Chen err |= __get_user(size, &info->size); 559b74baf4aSHuacai Chen if (err) 560b74baf4aSHuacai Chen return err; 561b74baf4aSHuacai Chen 562b74baf4aSHuacai Chen switch (magic) { 563b74baf4aSHuacai Chen case 0: /* END */ 564b74baf4aSHuacai Chen goto done; 565b74baf4aSHuacai Chen 566b74baf4aSHuacai Chen case FPU_CTX_MAGIC: 567b74baf4aSHuacai Chen if (size < (sizeof(struct sctx_info) + 568b74baf4aSHuacai Chen sizeof(struct fpu_context))) 569b74baf4aSHuacai Chen goto invalid; 570b74baf4aSHuacai Chen extctx->fpu.addr = info; 571b74baf4aSHuacai Chen break; 572b74baf4aSHuacai Chen 573*61650023SHuacai Chen case LSX_CTX_MAGIC: 574*61650023SHuacai Chen if (size < (sizeof(struct sctx_info) + 575*61650023SHuacai Chen sizeof(struct lsx_context))) 576*61650023SHuacai Chen goto invalid; 577*61650023SHuacai Chen extctx->lsx.addr = info; 578*61650023SHuacai Chen break; 579*61650023SHuacai Chen 580*61650023SHuacai Chen case LASX_CTX_MAGIC: 581*61650023SHuacai Chen if (size < (sizeof(struct sctx_info) + 582*61650023SHuacai Chen sizeof(struct lasx_context))) 583*61650023SHuacai Chen goto invalid; 584*61650023SHuacai Chen extctx->lasx.addr = info; 585*61650023SHuacai Chen break; 586*61650023SHuacai Chen 587b74baf4aSHuacai Chen default: 588b74baf4aSHuacai Chen goto invalid; 589b74baf4aSHuacai Chen } 590b74baf4aSHuacai Chen 591b74baf4aSHuacai Chen info = (struct sctx_info *)((char *)info + size); 592b74baf4aSHuacai Chen } 593b74baf4aSHuacai Chen 594b74baf4aSHuacai Chen done: 595b74baf4aSHuacai Chen return 0; 596b74baf4aSHuacai Chen 597b74baf4aSHuacai Chen invalid: 598b74baf4aSHuacai Chen return -EINVAL; 599b74baf4aSHuacai Chen } 600b74baf4aSHuacai Chen 601b74baf4aSHuacai Chen static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) 602b74baf4aSHuacai Chen { 603b74baf4aSHuacai Chen int i, err = 0; 604b74baf4aSHuacai Chen struct extctx_layout extctx; 605b74baf4aSHuacai Chen 606b74baf4aSHuacai Chen memset(&extctx, 0, sizeof(struct extctx_layout)); 607b74baf4aSHuacai Chen 608b74baf4aSHuacai Chen err = __get_user(extctx.flags, &sc->sc_flags); 609b74baf4aSHuacai Chen if (err) 610b74baf4aSHuacai Chen goto bad; 611b74baf4aSHuacai Chen 612b74baf4aSHuacai Chen err = parse_extcontext(sc, &extctx); 613b74baf4aSHuacai Chen if (err) 614b74baf4aSHuacai Chen goto bad; 615b74baf4aSHuacai Chen 616b74baf4aSHuacai Chen conditional_used_math(extctx.flags & SC_USED_FP); 617b74baf4aSHuacai Chen 618b74baf4aSHuacai Chen /* 619b74baf4aSHuacai Chen * The signal handler may have used FPU; give it up if the program 620b74baf4aSHuacai Chen * doesn't want it following sigreturn. 621b74baf4aSHuacai Chen */ 622b74baf4aSHuacai Chen if (!(extctx.flags & SC_USED_FP)) 623b74baf4aSHuacai Chen lose_fpu(0); 624b74baf4aSHuacai Chen 625b74baf4aSHuacai Chen /* Always make any pending restarted system calls return -EINTR */ 626b74baf4aSHuacai Chen current->restart_block.fn = do_no_restart_syscall; 627b74baf4aSHuacai Chen 628b74baf4aSHuacai Chen err |= __get_user(regs->csr_era, &sc->sc_pc); 629b74baf4aSHuacai Chen for (i = 1; i < 32; i++) 630b74baf4aSHuacai Chen err |= __get_user(regs->regs[i], &sc->sc_regs[i]); 631b74baf4aSHuacai Chen 632*61650023SHuacai Chen if (extctx.lasx.addr) 633*61650023SHuacai Chen err |= protected_restore_lasx_context(&extctx); 634*61650023SHuacai Chen else if (extctx.lsx.addr) 635*61650023SHuacai Chen err |= protected_restore_lsx_context(&extctx); 636*61650023SHuacai Chen else if (extctx.fpu.addr) 637b74baf4aSHuacai Chen err |= protected_restore_fpu_context(&extctx); 638b74baf4aSHuacai Chen 639b74baf4aSHuacai Chen bad: 640b74baf4aSHuacai Chen return err; 641b74baf4aSHuacai Chen } 642b74baf4aSHuacai Chen 643b74baf4aSHuacai Chen static unsigned int handle_flags(void) 644b74baf4aSHuacai Chen { 645b74baf4aSHuacai Chen unsigned int flags = 0; 646b74baf4aSHuacai Chen 647b74baf4aSHuacai Chen flags = used_math() ? SC_USED_FP : 0; 648b74baf4aSHuacai Chen 649b74baf4aSHuacai Chen switch (current->thread.error_code) { 650b74baf4aSHuacai Chen case 1: 651b74baf4aSHuacai Chen flags |= SC_ADDRERR_RD; 652b74baf4aSHuacai Chen break; 653b74baf4aSHuacai Chen case 2: 654b74baf4aSHuacai Chen flags |= SC_ADDRERR_WR; 655b74baf4aSHuacai Chen break; 656b74baf4aSHuacai Chen } 657b74baf4aSHuacai Chen 658b74baf4aSHuacai Chen return flags; 659b74baf4aSHuacai Chen } 660b74baf4aSHuacai Chen 661b74baf4aSHuacai Chen static unsigned long extframe_alloc(struct extctx_layout *extctx, 662b74baf4aSHuacai Chen struct _ctx_layout *layout, 663b74baf4aSHuacai Chen size_t size, unsigned int align, unsigned long base) 664b74baf4aSHuacai Chen { 665b74baf4aSHuacai Chen unsigned long new_base = base - size; 666b74baf4aSHuacai Chen 667b74baf4aSHuacai Chen new_base = round_down(new_base, (align < 16 ? 16 : align)); 668b74baf4aSHuacai Chen new_base -= sizeof(struct sctx_info); 669b74baf4aSHuacai Chen 670b74baf4aSHuacai Chen layout->addr = (void *)new_base; 671b74baf4aSHuacai Chen layout->size = (unsigned int)(base - new_base); 672b74baf4aSHuacai Chen extctx->size += layout->size; 673b74baf4aSHuacai Chen 674b74baf4aSHuacai Chen return new_base; 675b74baf4aSHuacai Chen } 676b74baf4aSHuacai Chen 677b74baf4aSHuacai Chen static unsigned long setup_extcontext(struct extctx_layout *extctx, unsigned long sp) 678b74baf4aSHuacai Chen { 679b74baf4aSHuacai Chen unsigned long new_sp = sp; 680b74baf4aSHuacai Chen 681b74baf4aSHuacai Chen memset(extctx, 0, sizeof(struct extctx_layout)); 682b74baf4aSHuacai Chen 683b74baf4aSHuacai Chen extctx->flags = handle_flags(); 684b74baf4aSHuacai Chen 685b74baf4aSHuacai Chen /* Grow down, alloc "end" context info first. */ 686b74baf4aSHuacai Chen new_sp -= sizeof(struct sctx_info); 687b74baf4aSHuacai Chen extctx->end.addr = (void *)new_sp; 688b74baf4aSHuacai Chen extctx->end.size = (unsigned int)sizeof(struct sctx_info); 689b74baf4aSHuacai Chen extctx->size += extctx->end.size; 690b74baf4aSHuacai Chen 691b74baf4aSHuacai Chen if (extctx->flags & SC_USED_FP) { 692*61650023SHuacai Chen if (cpu_has_lasx && thread_lasx_context_live()) 693*61650023SHuacai Chen new_sp = extframe_alloc(extctx, &extctx->lasx, 694*61650023SHuacai Chen sizeof(struct lasx_context), LASX_CTX_ALIGN, new_sp); 695*61650023SHuacai Chen else if (cpu_has_lsx && thread_lsx_context_live()) 696*61650023SHuacai Chen new_sp = extframe_alloc(extctx, &extctx->lsx, 697*61650023SHuacai Chen sizeof(struct lsx_context), LSX_CTX_ALIGN, new_sp); 698*61650023SHuacai Chen else if (cpu_has_fpu) 699b74baf4aSHuacai Chen new_sp = extframe_alloc(extctx, &extctx->fpu, 700b74baf4aSHuacai Chen sizeof(struct fpu_context), FPU_CTX_ALIGN, new_sp); 701b74baf4aSHuacai Chen } 702b74baf4aSHuacai Chen 703b74baf4aSHuacai Chen return new_sp; 704b74baf4aSHuacai Chen } 705b74baf4aSHuacai Chen 706b74baf4aSHuacai Chen void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, 707b74baf4aSHuacai Chen struct extctx_layout *extctx) 708b74baf4aSHuacai Chen { 709b74baf4aSHuacai Chen unsigned long sp; 710b74baf4aSHuacai Chen 711b74baf4aSHuacai Chen /* Default to using normal stack */ 712b74baf4aSHuacai Chen sp = regs->regs[3]; 713b74baf4aSHuacai Chen 714b74baf4aSHuacai Chen /* 715b74baf4aSHuacai Chen * If we are on the alternate signal stack and would overflow it, don't. 716b74baf4aSHuacai Chen * Return an always-bogus address instead so we will die with SIGSEGV. 717b74baf4aSHuacai Chen */ 718b74baf4aSHuacai Chen if (on_sig_stack(sp) && 719b74baf4aSHuacai Chen !likely(on_sig_stack(sp - sizeof(struct rt_sigframe)))) 720b74baf4aSHuacai Chen return (void __user __force *)(-1UL); 721b74baf4aSHuacai Chen 722b74baf4aSHuacai Chen sp = sigsp(sp, ksig); 723b74baf4aSHuacai Chen sp = round_down(sp, 16); 724b74baf4aSHuacai Chen sp = setup_extcontext(extctx, sp); 725b74baf4aSHuacai Chen sp -= sizeof(struct rt_sigframe); 726b74baf4aSHuacai Chen 727b74baf4aSHuacai Chen if (!IS_ALIGNED(sp, 16)) 728b74baf4aSHuacai Chen BUG(); 729b74baf4aSHuacai Chen 730b74baf4aSHuacai Chen return (void __user *)sp; 731b74baf4aSHuacai Chen } 732b74baf4aSHuacai Chen 733b74baf4aSHuacai Chen /* 734b74baf4aSHuacai Chen * Atomically swap in the new signal mask, and wait for a signal. 735b74baf4aSHuacai Chen */ 736b74baf4aSHuacai Chen 737b74baf4aSHuacai Chen asmlinkage long sys_rt_sigreturn(void) 738b74baf4aSHuacai Chen { 739b74baf4aSHuacai Chen int sig; 740b74baf4aSHuacai Chen sigset_t set; 741b74baf4aSHuacai Chen struct pt_regs *regs; 742b74baf4aSHuacai Chen struct rt_sigframe __user *frame; 743b74baf4aSHuacai Chen 744b74baf4aSHuacai Chen regs = current_pt_regs(); 745b74baf4aSHuacai Chen frame = (struct rt_sigframe __user *)regs->regs[3]; 746b74baf4aSHuacai Chen if (!access_ok(frame, sizeof(*frame))) 747b74baf4aSHuacai Chen goto badframe; 748b74baf4aSHuacai Chen if (__copy_from_user(&set, &frame->rs_uctx.uc_sigmask, sizeof(set))) 749b74baf4aSHuacai Chen goto badframe; 750b74baf4aSHuacai Chen 751b74baf4aSHuacai Chen set_current_blocked(&set); 752b74baf4aSHuacai Chen 753b74baf4aSHuacai Chen sig = restore_sigcontext(regs, &frame->rs_uctx.uc_mcontext); 754b74baf4aSHuacai Chen if (sig < 0) 755b74baf4aSHuacai Chen goto badframe; 756b74baf4aSHuacai Chen else if (sig) 757b74baf4aSHuacai Chen force_sig(sig); 758b74baf4aSHuacai Chen 759b74baf4aSHuacai Chen regs->regs[0] = 0; /* No syscall restarting */ 760b74baf4aSHuacai Chen if (restore_altstack(&frame->rs_uctx.uc_stack)) 761b74baf4aSHuacai Chen goto badframe; 762b74baf4aSHuacai Chen 763b74baf4aSHuacai Chen return regs->regs[4]; 764b74baf4aSHuacai Chen 765b74baf4aSHuacai Chen badframe: 766b74baf4aSHuacai Chen force_sig(SIGSEGV); 767b74baf4aSHuacai Chen return 0; 768b74baf4aSHuacai Chen } 769b74baf4aSHuacai Chen 770b74baf4aSHuacai Chen static int setup_rt_frame(void *sig_return, struct ksignal *ksig, 771b74baf4aSHuacai Chen struct pt_regs *regs, sigset_t *set) 772b74baf4aSHuacai Chen { 773b74baf4aSHuacai Chen int err = 0; 774b74baf4aSHuacai Chen struct extctx_layout extctx; 775b74baf4aSHuacai Chen struct rt_sigframe __user *frame; 776b74baf4aSHuacai Chen 777b74baf4aSHuacai Chen frame = get_sigframe(ksig, regs, &extctx); 778b74baf4aSHuacai Chen if (!access_ok(frame, sizeof(*frame) + extctx.size)) 779b74baf4aSHuacai Chen return -EFAULT; 780b74baf4aSHuacai Chen 781b74baf4aSHuacai Chen /* Create siginfo. */ 782b74baf4aSHuacai Chen err |= copy_siginfo_to_user(&frame->rs_info, &ksig->info); 783b74baf4aSHuacai Chen 784b74baf4aSHuacai Chen /* Create the ucontext. */ 785b74baf4aSHuacai Chen err |= __put_user(0, &frame->rs_uctx.uc_flags); 786b74baf4aSHuacai Chen err |= __put_user(NULL, &frame->rs_uctx.uc_link); 787b74baf4aSHuacai Chen err |= __save_altstack(&frame->rs_uctx.uc_stack, regs->regs[3]); 788b74baf4aSHuacai Chen err |= setup_sigcontext(regs, &frame->rs_uctx.uc_mcontext, &extctx); 789b74baf4aSHuacai Chen err |= __copy_to_user(&frame->rs_uctx.uc_sigmask, set, sizeof(*set)); 790b74baf4aSHuacai Chen 791b74baf4aSHuacai Chen if (err) 792b74baf4aSHuacai Chen return -EFAULT; 793b74baf4aSHuacai Chen 794b74baf4aSHuacai Chen /* 795b74baf4aSHuacai Chen * Arguments to signal handler: 796b74baf4aSHuacai Chen * 797b74baf4aSHuacai Chen * a0 = signal number 798b74baf4aSHuacai Chen * a1 = pointer to siginfo 799b74baf4aSHuacai Chen * a2 = pointer to ucontext 800b74baf4aSHuacai Chen * 801b74baf4aSHuacai Chen * c0_era point to the signal handler, $r3 (sp) points to 802b74baf4aSHuacai Chen * the struct rt_sigframe. 803b74baf4aSHuacai Chen */ 804b74baf4aSHuacai Chen regs->regs[4] = ksig->sig; 805b74baf4aSHuacai Chen regs->regs[5] = (unsigned long) &frame->rs_info; 806b74baf4aSHuacai Chen regs->regs[6] = (unsigned long) &frame->rs_uctx; 807b74baf4aSHuacai Chen regs->regs[3] = (unsigned long) frame; 808b74baf4aSHuacai Chen regs->regs[1] = (unsigned long) sig_return; 809b74baf4aSHuacai Chen regs->csr_era = (unsigned long) ksig->ka.sa.sa_handler; 810b74baf4aSHuacai Chen 811b74baf4aSHuacai Chen DEBUGP("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%lx\n", 812b74baf4aSHuacai Chen current->comm, current->pid, 813b74baf4aSHuacai Chen frame, regs->csr_era, regs->regs[1]); 814b74baf4aSHuacai Chen 815b74baf4aSHuacai Chen return 0; 816b74baf4aSHuacai Chen } 817b74baf4aSHuacai Chen 818b74baf4aSHuacai Chen static void handle_signal(struct ksignal *ksig, struct pt_regs *regs) 819b74baf4aSHuacai Chen { 820b74baf4aSHuacai Chen int ret; 821b74baf4aSHuacai Chen sigset_t *oldset = sigmask_to_save(); 822b74baf4aSHuacai Chen void *vdso = current->mm->context.vdso; 823b74baf4aSHuacai Chen 824b74baf4aSHuacai Chen /* Are we from a system call? */ 825b74baf4aSHuacai Chen if (regs->regs[0]) { 826b74baf4aSHuacai Chen switch (regs->regs[4]) { 827b74baf4aSHuacai Chen case -ERESTART_RESTARTBLOCK: 828b74baf4aSHuacai Chen case -ERESTARTNOHAND: 829b74baf4aSHuacai Chen regs->regs[4] = -EINTR; 830b74baf4aSHuacai Chen break; 831b74baf4aSHuacai Chen case -ERESTARTSYS: 832b74baf4aSHuacai Chen if (!(ksig->ka.sa.sa_flags & SA_RESTART)) { 833b74baf4aSHuacai Chen regs->regs[4] = -EINTR; 834b74baf4aSHuacai Chen break; 835b74baf4aSHuacai Chen } 836b74baf4aSHuacai Chen fallthrough; 837b74baf4aSHuacai Chen case -ERESTARTNOINTR: 838b74baf4aSHuacai Chen regs->regs[4] = regs->orig_a0; 839b74baf4aSHuacai Chen regs->csr_era -= 4; 840b74baf4aSHuacai Chen } 841b74baf4aSHuacai Chen 842b74baf4aSHuacai Chen regs->regs[0] = 0; /* Don't deal with this again. */ 843b74baf4aSHuacai Chen } 844b74baf4aSHuacai Chen 845b74baf4aSHuacai Chen rseq_signal_deliver(ksig, regs); 846b74baf4aSHuacai Chen 847b74baf4aSHuacai Chen ret = setup_rt_frame(vdso + current->thread.vdso->offset_sigreturn, ksig, regs, oldset); 848b74baf4aSHuacai Chen 849b74baf4aSHuacai Chen signal_setup_done(ret, ksig, 0); 850b74baf4aSHuacai Chen } 851b74baf4aSHuacai Chen 85201630053SHuacai Chen void arch_do_signal_or_restart(struct pt_regs *regs) 853b74baf4aSHuacai Chen { 854b74baf4aSHuacai Chen struct ksignal ksig; 855b74baf4aSHuacai Chen 85601630053SHuacai Chen if (get_signal(&ksig)) { 857b74baf4aSHuacai Chen /* Whee! Actually deliver the signal. */ 858b74baf4aSHuacai Chen handle_signal(&ksig, regs); 859b74baf4aSHuacai Chen return; 860b74baf4aSHuacai Chen } 861b74baf4aSHuacai Chen 862b74baf4aSHuacai Chen /* Are we from a system call? */ 863b74baf4aSHuacai Chen if (regs->regs[0]) { 864b74baf4aSHuacai Chen switch (regs->regs[4]) { 865b74baf4aSHuacai Chen case -ERESTARTNOHAND: 866b74baf4aSHuacai Chen case -ERESTARTSYS: 867b74baf4aSHuacai Chen case -ERESTARTNOINTR: 868b74baf4aSHuacai Chen regs->regs[4] = regs->orig_a0; 869b74baf4aSHuacai Chen regs->csr_era -= 4; 870b74baf4aSHuacai Chen break; 871b74baf4aSHuacai Chen 872b74baf4aSHuacai Chen case -ERESTART_RESTARTBLOCK: 873b74baf4aSHuacai Chen regs->regs[4] = regs->orig_a0; 874b74baf4aSHuacai Chen regs->regs[11] = __NR_restart_syscall; 875b74baf4aSHuacai Chen regs->csr_era -= 4; 876b74baf4aSHuacai Chen break; 877b74baf4aSHuacai Chen } 878b74baf4aSHuacai Chen regs->regs[0] = 0; /* Don't deal with this again. */ 879b74baf4aSHuacai Chen } 880b74baf4aSHuacai Chen 881b74baf4aSHuacai Chen /* 882b74baf4aSHuacai Chen * If there's no signal to deliver, we just put the saved sigmask 883b74baf4aSHuacai Chen * back 884b74baf4aSHuacai Chen */ 885b74baf4aSHuacai Chen restore_saved_sigmask(); 886b74baf4aSHuacai Chen } 887