diff options
Diffstat (limited to 'arch/powerpc/kernel/signal_64.c')
-rw-r--r-- | arch/powerpc/kernel/signal_64.c | 105 |
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, ¤t->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(¤t->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 | */ |
236 | int sys_swapcontext(struct ucontext __user *old_ctx, | 280 | int 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) |