aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc
diff options
context:
space:
mode:
authorRob Gardner <rob.gardner@oracle.com>2015-10-31 00:36:23 -0400
committerDavid S. Miller <davem@davemloft.net>2015-11-04 15:00:49 -0500
commitcae9af6a820b47d3e5e0da4edf23cf6fa69b18d8 (patch)
tree2fbeac9dfdcb152b65dda84aaf7331d53a291bce /arch/sparc
parentd618382ba5f1a4905db63f4980bf7b0a5826de9d (diff)
sparc64: Don't restrict fp regs for no-fault loads
The function handle_ldf_stq() deals with no-fault ASI loads and stores, but restricts fp registers to quad word regs (ie, %f0, %f4 etc). This is valid for the STQ case, but unnecessarily restricts loads, which may be single precision, double, or quad. This results in SIGFPE being raised for this instruction when the source address is invalid: ldda [%g1] ASI_PNF, %f2 but not for this one: ldda [%g1] ASI_PNF, %f4 The validation check for quad register is moved to within the STQ block so that loads are not affected by the check. An additional problem is that the calculation for freg is incorrect when a single precision load is being handled. This causes %f1 to be seen as %f32 etc, and the incorrect register ends up being overwritten. This code sequence demonstrates the problem: ldd [%g1], %f32 ! g1 = valid address lda [%i3] ASI_PNF, %f1 ! i3 = invalid address std %f32, [%g1] This is corrected by basing the freg calculation on the load size. Signed-off-by: Rob Gardner <rob.gardner@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc')
-rw-r--r--arch/sparc/kernel/unaligned_64.c22
1 files changed, 15 insertions, 7 deletions
diff --git a/arch/sparc/kernel/unaligned_64.c b/arch/sparc/kernel/unaligned_64.c
index 62098a89bbbf..d89e97b374cf 100644
--- a/arch/sparc/kernel/unaligned_64.c
+++ b/arch/sparc/kernel/unaligned_64.c
@@ -436,24 +436,26 @@ extern void sun4v_data_access_exception(struct pt_regs *regs,
436int handle_ldf_stq(u32 insn, struct pt_regs *regs) 436int handle_ldf_stq(u32 insn, struct pt_regs *regs)
437{ 437{
438 unsigned long addr = compute_effective_address(regs, insn, 0); 438 unsigned long addr = compute_effective_address(regs, insn, 0);
439 int freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20); 439 int freg;
440 struct fpustate *f = FPUSTATE; 440 struct fpustate *f = FPUSTATE;
441 int asi = decode_asi(insn, regs); 441 int asi = decode_asi(insn, regs);
442 int flag = (freg < 32) ? FPRS_DL : FPRS_DU; 442 int flag;
443 443
444 perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0); 444 perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
445 445
446 save_and_clear_fpu(); 446 save_and_clear_fpu();
447 current_thread_info()->xfsr[0] &= ~0x1c000; 447 current_thread_info()->xfsr[0] &= ~0x1c000;
448 if (freg & 3) {
449 current_thread_info()->xfsr[0] |= (6 << 14) /* invalid_fp_register */;
450 do_fpother(regs);
451 return 0;
452 }
453 if (insn & 0x200000) { 448 if (insn & 0x200000) {
454 /* STQ */ 449 /* STQ */
455 u64 first = 0, second = 0; 450 u64 first = 0, second = 0;
456 451
452 freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20);
453 flag = (freg < 32) ? FPRS_DL : FPRS_DU;
454 if (freg & 3) {
455 current_thread_info()->xfsr[0] |= (6 << 14) /* invalid_fp_register */;
456 do_fpother(regs);
457 return 0;
458 }
457 if (current_thread_info()->fpsaved[0] & flag) { 459 if (current_thread_info()->fpsaved[0] & flag) {
458 first = *(u64 *)&f->regs[freg]; 460 first = *(u64 *)&f->regs[freg];
459 second = *(u64 *)&f->regs[freg+2]; 461 second = *(u64 *)&f->regs[freg+2];
@@ -513,6 +515,12 @@ int handle_ldf_stq(u32 insn, struct pt_regs *regs)
513 case 0x100000: size = 4; break; 515 case 0x100000: size = 4; break;
514 default: size = 2; break; 516 default: size = 2; break;
515 } 517 }
518 if (size == 1)
519 freg = (insn >> 25) & 0x1f;
520 else
521 freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20);
522 flag = (freg < 32) ? FPRS_DL : FPRS_DU;
523
516 for (i = 0; i < size; i++) 524 for (i = 0; i < size; i++)
517 data[i] = 0; 525 data[i] = 0;
518 526