aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel/signal_64.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/kernel/signal_64.c')
-rw-r--r--arch/powerpc/kernel/signal_64.c105
1 files changed, 85 insertions, 20 deletions
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
index faeb8f207ea4..65ad925c3a8f 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -87,6 +87,7 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
87#ifdef CONFIG_ALTIVEC 87#ifdef CONFIG_ALTIVEC
88 elf_vrreg_t __user *v_regs = (elf_vrreg_t __user *)(((unsigned long)sc->vmx_reserve + 15) & ~0xful); 88 elf_vrreg_t __user *v_regs = (elf_vrreg_t __user *)(((unsigned long)sc->vmx_reserve + 15) & ~0xful);
89#endif 89#endif
90 unsigned long msr = regs->msr;
90 long err = 0; 91 long err = 0;
91 92
92 flush_fp_to_thread(current); 93 flush_fp_to_thread(current);
@@ -102,7 +103,7 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
102 /* set MSR_VEC in the MSR value in the frame to indicate that sc->v_reg) 103 /* set MSR_VEC in the MSR value in the frame to indicate that sc->v_reg)
103 * contains valid data. 104 * contains valid data.
104 */ 105 */
105 regs->msr |= MSR_VEC; 106 msr |= MSR_VEC;
106 } 107 }
107 /* We always copy to/from vrsave, it's 0 if we don't have or don't 108 /* We always copy to/from vrsave, it's 0 if we don't have or don't
108 * use altivec. 109 * use altivec.
@@ -111,10 +112,29 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
111#else /* CONFIG_ALTIVEC */ 112#else /* CONFIG_ALTIVEC */
112 err |= __put_user(0, &sc->v_regs); 113 err |= __put_user(0, &sc->v_regs);
113#endif /* CONFIG_ALTIVEC */ 114#endif /* CONFIG_ALTIVEC */
115 flush_fp_to_thread(current);
116 /* copy fpr regs and fpscr */
117 err |= copy_fpr_to_user(&sc->fp_regs, current);
118#ifdef CONFIG_VSX
119 /*
120 * Copy VSX low doubleword to local buffer for formatting,
121 * then out to userspace. Update v_regs to point after the
122 * VMX data.
123 */
124 if (current->thread.used_vsr) {
125 __giveup_vsx(current);
126 v_regs += ELF_NVRREG;
127 err |= copy_vsx_to_user(v_regs, current);
128 /* set MSR_VSX in the MSR value in the frame to
129 * indicate that sc->vs_reg) contains valid data.
130 */
131 msr |= MSR_VSX;
132 }
133#endif /* CONFIG_VSX */
114 err |= __put_user(&sc->gp_regs, &sc->regs); 134 err |= __put_user(&sc->gp_regs, &sc->regs);
115 WARN_ON(!FULL_REGS(regs)); 135 WARN_ON(!FULL_REGS(regs));
116 err |= __copy_to_user(&sc->gp_regs, regs, GP_REGS_SIZE); 136 err |= __copy_to_user(&sc->gp_regs, regs, GP_REGS_SIZE);
117 err |= __copy_to_user(&sc->fp_regs, &current->thread.fpr, FP_REGS_SIZE); 137 err |= __put_user(msr, &sc->gp_regs[PT_MSR]);
118 err |= __put_user(signr, &sc->signal); 138 err |= __put_user(signr, &sc->signal);
119 err |= __put_user(handler, &sc->handler); 139 err |= __put_user(handler, &sc->handler);
120 if (set != NULL) 140 if (set != NULL)
@@ -135,29 +155,32 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig,
135#endif 155#endif
136 unsigned long err = 0; 156 unsigned long err = 0;
137 unsigned long save_r13 = 0; 157 unsigned long save_r13 = 0;
138 elf_greg_t *gregs = (elf_greg_t *)regs;
139 unsigned long msr; 158 unsigned long msr;
159#ifdef CONFIG_VSX
140 int i; 160 int i;
161#endif
141 162
142 /* If this is not a signal return, we preserve the TLS in r13 */ 163 /* If this is not a signal return, we preserve the TLS in r13 */
143 if (!sig) 164 if (!sig)
144 save_r13 = regs->gpr[13]; 165 save_r13 = regs->gpr[13];
145 166
146 /* copy everything before MSR */ 167 /* copy the GPRs */
147 err |= __copy_from_user(regs, &sc->gp_regs, 168 err |= __copy_from_user(regs->gpr, sc->gp_regs, sizeof(regs->gpr));
148 PT_MSR*sizeof(unsigned long)); 169 err |= __get_user(regs->nip, &sc->gp_regs[PT_NIP]);
149
150 /* get MSR separately, transfer the LE bit if doing signal return */ 170 /* get MSR separately, transfer the LE bit if doing signal return */
151 err |= __get_user(msr, &sc->gp_regs[PT_MSR]); 171 err |= __get_user(msr, &sc->gp_regs[PT_MSR]);
152 if (sig) 172 if (sig)
153 regs->msr = (regs->msr & ~MSR_LE) | (msr & MSR_LE); 173 regs->msr = (regs->msr & ~MSR_LE) | (msr & MSR_LE);
154 174 err |= __get_user(regs->orig_gpr3, &sc->gp_regs[PT_ORIG_R3]);
175 err |= __get_user(regs->ctr, &sc->gp_regs[PT_CTR]);
176 err |= __get_user(regs->link, &sc->gp_regs[PT_LNK]);
177 err |= __get_user(regs->xer, &sc->gp_regs[PT_XER]);
178 err |= __get_user(regs->ccr, &sc->gp_regs[PT_CCR]);
155 /* skip SOFTE */ 179 /* skip SOFTE */
156 for (i = PT_MSR+1; i <= PT_RESULT; i++) { 180 err |= __get_user(regs->trap, &sc->gp_regs[PT_TRAP]);
157 if (i == PT_SOFTE) 181 err |= __get_user(regs->dar, &sc->gp_regs[PT_DAR]);
158 continue; 182 err |= __get_user(regs->dsisr, &sc->gp_regs[PT_DSISR]);
159 err |= __get_user(gregs[i], &sc->gp_regs[i]); 183 err |= __get_user(regs->result, &sc->gp_regs[PT_RESULT]);
160 }
161 184
162 if (!sig) 185 if (!sig)
163 regs->gpr[13] = save_r13; 186 regs->gpr[13] = save_r13;
@@ -178,9 +201,7 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig,
178 * This has to be done before copying stuff into current->thread.fpr/vr 201 * This has to be done before copying stuff into current->thread.fpr/vr
179 * for the reasons explained in the previous comment. 202 * for the reasons explained in the previous comment.
180 */ 203 */
181 regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1 | MSR_VEC); 204 regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1 | MSR_VEC | MSR_VSX);
182
183 err |= __copy_from_user(&current->thread.fpr, &sc->fp_regs, FP_REGS_SIZE);
184 205
185#ifdef CONFIG_ALTIVEC 206#ifdef CONFIG_ALTIVEC
186 err |= __get_user(v_regs, &sc->v_regs); 207 err |= __get_user(v_regs, &sc->v_regs);
@@ -200,7 +221,23 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig,
200 else 221 else
201 current->thread.vrsave = 0; 222 current->thread.vrsave = 0;
202#endif /* CONFIG_ALTIVEC */ 223#endif /* CONFIG_ALTIVEC */
224 /* restore floating point */
225 err |= copy_fpr_from_user(current, &sc->fp_regs);
226#ifdef CONFIG_VSX
227 /*
228 * Get additional VSX data. Update v_regs to point after the
229 * VMX data. Copy VSX low doubleword from userspace to local
230 * buffer for formatting, then into the taskstruct.
231 */
232 v_regs += ELF_NVRREG;
233 if ((msr & MSR_VSX) != 0)
234 err |= copy_vsx_from_user(current, v_regs);
235 else
236 for (i = 0; i < 32 ; i++)
237 current->thread.fpr[i][TS_VSRLOWOFFSET] = 0;
203 238
239#else
240#endif
204 return err; 241 return err;
205} 242}
206 243
@@ -231,6 +268,13 @@ static long setup_trampoline(unsigned int syscall, unsigned int __user *tramp)
231} 268}
232 269
233/* 270/*
271 * Userspace code may pass a ucontext which doesn't include VSX added
272 * at the end. We need to check for this case.
273 */
274#define UCONTEXTSIZEWITHOUTVSX \
275 (sizeof(struct ucontext) - 32*sizeof(long))
276
277/*
234 * Handle {get,set,swap}_context operations 278 * Handle {get,set,swap}_context operations
235 */ 279 */
236int sys_swapcontext(struct ucontext __user *old_ctx, 280int sys_swapcontext(struct ucontext __user *old_ctx,
@@ -239,13 +283,34 @@ int sys_swapcontext(struct ucontext __user *old_ctx,
239{ 283{
240 unsigned char tmp; 284 unsigned char tmp;
241 sigset_t set; 285 sigset_t set;
286 unsigned long new_msr = 0;
242 287
243 /* Context size is for future use. Right now, we only make sure 288 if (new_ctx &&
244 * we are passed something we understand 289 __get_user(new_msr, &new_ctx->uc_mcontext.gp_regs[PT_MSR]))
290 return -EFAULT;
291 /*
292 * Check that the context is not smaller than the original
293 * size (with VMX but without VSX)
245 */ 294 */
246 if (ctx_size < sizeof(struct ucontext)) 295 if (ctx_size < UCONTEXTSIZEWITHOUTVSX)
247 return -EINVAL; 296 return -EINVAL;
248 297 /*
298 * If the new context state sets the MSR VSX bits but
299 * it doesn't provide VSX state.
300 */
301 if ((ctx_size < sizeof(struct ucontext)) &&
302 (new_msr & MSR_VSX))
303 return -EINVAL;
304#ifdef CONFIG_VSX
305 /*
306 * If userspace doesn't provide enough room for VSX data,
307 * but current thread has used VSX, we don't have anywhere
308 * to store the full context back into.
309 */
310 if ((ctx_size < sizeof(struct ucontext)) &&
311 (current->thread.used_vsr && old_ctx))
312 return -EINVAL;
313#endif
249 if (old_ctx != NULL) { 314 if (old_ctx != NULL) {
250 if (!access_ok(VERIFY_WRITE, old_ctx, sizeof(*old_ctx)) 315 if (!access_ok(VERIFY_WRITE, old_ctx, sizeof(*old_ctx))
251 || setup_sigcontext(&old_ctx->uc_mcontext, regs, 0, NULL, 0) 316 || setup_sigcontext(&old_ctx->uc_mcontext, regs, 0, NULL, 0)