diff options
Diffstat (limited to 'arch/powerpc/kernel/traps.c')
-rw-r--r-- | arch/powerpc/kernel/traps.c | 62 |
1 files changed, 55 insertions, 7 deletions
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index f5def6cf5cd6..5457e9575685 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c | |||
@@ -1160,37 +1160,85 @@ void CacheLockingException(struct pt_regs *regs, unsigned long address, | |||
1160 | #ifdef CONFIG_SPE | 1160 | #ifdef CONFIG_SPE |
1161 | void SPEFloatingPointException(struct pt_regs *regs) | 1161 | void SPEFloatingPointException(struct pt_regs *regs) |
1162 | { | 1162 | { |
1163 | extern int do_spe_mathemu(struct pt_regs *regs); | ||
1163 | unsigned long spefscr; | 1164 | unsigned long spefscr; |
1164 | int fpexc_mode; | 1165 | int fpexc_mode; |
1165 | int code = 0; | 1166 | int code = 0; |
1167 | int err; | ||
1168 | |||
1169 | preempt_disable(); | ||
1170 | if (regs->msr & MSR_SPE) | ||
1171 | giveup_spe(current); | ||
1172 | preempt_enable(); | ||
1166 | 1173 | ||
1167 | spefscr = current->thread.spefscr; | 1174 | spefscr = current->thread.spefscr; |
1168 | fpexc_mode = current->thread.fpexc_mode; | 1175 | fpexc_mode = current->thread.fpexc_mode; |
1169 | 1176 | ||
1170 | /* Hardware does not neccessarily set sticky | ||
1171 | * underflow/overflow/invalid flags */ | ||
1172 | if ((spefscr & SPEFSCR_FOVF) && (fpexc_mode & PR_FP_EXC_OVF)) { | 1177 | if ((spefscr & SPEFSCR_FOVF) && (fpexc_mode & PR_FP_EXC_OVF)) { |
1173 | code = FPE_FLTOVF; | 1178 | code = FPE_FLTOVF; |
1174 | spefscr |= SPEFSCR_FOVFS; | ||
1175 | } | 1179 | } |
1176 | else if ((spefscr & SPEFSCR_FUNF) && (fpexc_mode & PR_FP_EXC_UND)) { | 1180 | else if ((spefscr & SPEFSCR_FUNF) && (fpexc_mode & PR_FP_EXC_UND)) { |
1177 | code = FPE_FLTUND; | 1181 | code = FPE_FLTUND; |
1178 | spefscr |= SPEFSCR_FUNFS; | ||
1179 | } | 1182 | } |
1180 | else if ((spefscr & SPEFSCR_FDBZ) && (fpexc_mode & PR_FP_EXC_DIV)) | 1183 | else if ((spefscr & SPEFSCR_FDBZ) && (fpexc_mode & PR_FP_EXC_DIV)) |
1181 | code = FPE_FLTDIV; | 1184 | code = FPE_FLTDIV; |
1182 | else if ((spefscr & SPEFSCR_FINV) && (fpexc_mode & PR_FP_EXC_INV)) { | 1185 | else if ((spefscr & SPEFSCR_FINV) && (fpexc_mode & PR_FP_EXC_INV)) { |
1183 | code = FPE_FLTINV; | 1186 | code = FPE_FLTINV; |
1184 | spefscr |= SPEFSCR_FINVS; | ||
1185 | } | 1187 | } |
1186 | else if ((spefscr & (SPEFSCR_FG | SPEFSCR_FX)) && (fpexc_mode & PR_FP_EXC_RES)) | 1188 | else if ((spefscr & (SPEFSCR_FG | SPEFSCR_FX)) && (fpexc_mode & PR_FP_EXC_RES)) |
1187 | code = FPE_FLTRES; | 1189 | code = FPE_FLTRES; |
1188 | 1190 | ||
1189 | current->thread.spefscr = spefscr; | 1191 | err = do_spe_mathemu(regs); |
1192 | if (err == 0) { | ||
1193 | regs->nip += 4; /* skip emulated instruction */ | ||
1194 | emulate_single_step(regs); | ||
1195 | return; | ||
1196 | } | ||
1197 | |||
1198 | if (err == -EFAULT) { | ||
1199 | /* got an error reading the instruction */ | ||
1200 | _exception(SIGSEGV, regs, SEGV_ACCERR, regs->nip); | ||
1201 | } else if (err == -EINVAL) { | ||
1202 | /* didn't recognize the instruction */ | ||
1203 | printk(KERN_ERR "unrecognized spe instruction " | ||
1204 | "in %s at %lx\n", current->comm, regs->nip); | ||
1205 | } else { | ||
1206 | _exception(SIGFPE, regs, code, regs->nip); | ||
1207 | } | ||
1190 | 1208 | ||
1191 | _exception(SIGFPE, regs, code, regs->nip); | ||
1192 | return; | 1209 | return; |
1193 | } | 1210 | } |
1211 | |||
1212 | void SPEFloatingPointRoundException(struct pt_regs *regs) | ||
1213 | { | ||
1214 | extern int speround_handler(struct pt_regs *regs); | ||
1215 | int err; | ||
1216 | |||
1217 | preempt_disable(); | ||
1218 | if (regs->msr & MSR_SPE) | ||
1219 | giveup_spe(current); | ||
1220 | preempt_enable(); | ||
1221 | |||
1222 | regs->nip -= 4; | ||
1223 | err = speround_handler(regs); | ||
1224 | if (err == 0) { | ||
1225 | regs->nip += 4; /* skip emulated instruction */ | ||
1226 | emulate_single_step(regs); | ||
1227 | return; | ||
1228 | } | ||
1229 | |||
1230 | if (err == -EFAULT) { | ||
1231 | /* got an error reading the instruction */ | ||
1232 | _exception(SIGSEGV, regs, SEGV_ACCERR, regs->nip); | ||
1233 | } else if (err == -EINVAL) { | ||
1234 | /* didn't recognize the instruction */ | ||
1235 | printk(KERN_ERR "unrecognized spe instruction " | ||
1236 | "in %s at %lx\n", current->comm, regs->nip); | ||
1237 | } else { | ||
1238 | _exception(SIGFPE, regs, 0, regs->nip); | ||
1239 | return; | ||
1240 | } | ||
1241 | } | ||
1194 | #endif | 1242 | #endif |
1195 | 1243 | ||
1196 | /* | 1244 | /* |