aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel/align.c
diff options
context:
space:
mode:
authorPaul Mackerras <paulus@samba.org>2007-08-10 00:07:38 -0400
committerPaul Mackerras <paulus@samba.org>2007-08-16 21:01:55 -0400
commitc6d4267eced79775399f256fbb4adb671e9b597e (patch)
tree211acc4464342ba2d87365367c3afb4ce509038d /arch/powerpc/kernel/align.c
parent09a54101e15f43607722dee55f33d1962653c6cb (diff)
[POWERPC] Handle alignment faults on new FP load/store instructions
This adds code to handle alignment traps generated by the following new floating-point load/store instructions, by emulating the instruction in the kernel (as is done for other instructions that generate alignment traps): lfiwax load floating-point as integer word algebraic indexed stfiwx store floating-point as integer word indexed lfdp load floating-point double pair lfdpx load floating-point double pair indexed stfdp store floating-point double pair stfdpx store floating-point double pair indexed All these except stfiwx are new in POWER6. lfdp/lfdpx/stfdp/stfdpx load and store 16 bytes of memory into an even/odd FP register pair. In little-endian mode each 8-byte value is byte-reversed separately (i.e. not as a 16-byte unit). lfiwax/stfiwx load or store the lower 4 bytes of a floating-point register from/to memory; lfiwax sets the upper 4 bytes of the FP register to the sign extension of the value loaded. Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/kernel/align.c')
-rw-r--r--arch/powerpc/kernel/align.c57
1 files changed, 49 insertions, 8 deletions
diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c
index 5c9ff7f5c44e..4c47f9cc0d9a 100644
--- a/arch/powerpc/kernel/align.c
+++ b/arch/powerpc/kernel/align.c
@@ -38,7 +38,7 @@ struct aligninfo {
38/* Bits in the flags field */ 38/* Bits in the flags field */
39#define LD 0 /* load */ 39#define LD 0 /* load */
40#define ST 1 /* store */ 40#define ST 1 /* store */
41#define SE 2 /* sign-extend value */ 41#define SE 2 /* sign-extend value, or FP ld/st as word */
42#define F 4 /* to/from fp regs */ 42#define F 4 /* to/from fp regs */
43#define U 8 /* update index register */ 43#define U 8 /* update index register */
44#define M 0x10 /* multiple load/store */ 44#define M 0x10 /* multiple load/store */
@@ -87,9 +87,9 @@ static struct aligninfo aligninfo[128] = {
87 { 8, LD+F+U }, /* 00 1 1001: lfdu */ 87 { 8, LD+F+U }, /* 00 1 1001: lfdu */
88 { 4, ST+F+S+U }, /* 00 1 1010: stfsu */ 88 { 4, ST+F+S+U }, /* 00 1 1010: stfsu */
89 { 8, ST+F+U }, /* 00 1 1011: stfdu */ 89 { 8, ST+F+U }, /* 00 1 1011: stfdu */
90 INVALID, /* 00 1 1100 */ 90 { 16, LD+F }, /* 00 1 1100: lfdp */
91 INVALID, /* 00 1 1101 */ 91 INVALID, /* 00 1 1101 */
92 INVALID, /* 00 1 1110 */ 92 { 16, ST+F }, /* 00 1 1110: stfdp */
93 INVALID, /* 00 1 1111 */ 93 INVALID, /* 00 1 1111 */
94 { 8, LD }, /* 01 0 0000: ldx */ 94 { 8, LD }, /* 01 0 0000: ldx */
95 INVALID, /* 01 0 0001 */ 95 INVALID, /* 01 0 0001 */
@@ -167,10 +167,10 @@ static struct aligninfo aligninfo[128] = {
167 { 8, LD+F }, /* 11 0 1001: lfdx */ 167 { 8, LD+F }, /* 11 0 1001: lfdx */
168 { 4, ST+F+S }, /* 11 0 1010: stfsx */ 168 { 4, ST+F+S }, /* 11 0 1010: stfsx */
169 { 8, ST+F }, /* 11 0 1011: stfdx */ 169 { 8, ST+F }, /* 11 0 1011: stfdx */
170 INVALID, /* 11 0 1100 */ 170 { 16, LD+F }, /* 11 0 1100: lfdpx */
171 { 8, LD+M }, /* 11 0 1101: lmd */ 171 { 4, LD+F+SE }, /* 11 0 1101: lfiwax */
172 INVALID, /* 11 0 1110 */ 172 { 16, ST+F }, /* 11 0 1110: stfdpx */
173 { 8, ST+M }, /* 11 0 1111: stmd */ 173 { 4, ST+F }, /* 11 0 1111: stfiwx */
174 { 4, LD+U }, /* 11 1 0000: lwzux */ 174 { 4, LD+U }, /* 11 1 0000: lwzux */
175 INVALID, /* 11 1 0001 */ 175 INVALID, /* 11 1 0001 */
176 { 4, ST+U }, /* 11 1 0010: stwux */ 176 { 4, ST+U }, /* 11 1 0010: stwux */
@@ -356,6 +356,42 @@ static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr,
356 return 1; 356 return 1;
357} 357}
358 358
359/*
360 * Emulate floating-point pair loads and stores.
361 * Only POWER6 has these instructions, and it does true little-endian,
362 * so we don't need the address swizzling.
363 */
364static int emulate_fp_pair(struct pt_regs *regs, unsigned char __user *addr,
365 unsigned int reg, unsigned int flags)
366{
367 char *ptr = (char *) &current->thread.fpr[reg];
368 int i, ret;
369
370 if (!(flags & F))
371 return 0;
372 if (reg & 1)
373 return 0; /* invalid form: FRS/FRT must be even */
374 if (!(flags & SW)) {
375 /* not byte-swapped - easy */
376 if (!(flags & ST))
377 ret = __copy_from_user(ptr, addr, 16);
378 else
379 ret = __copy_to_user(addr, ptr, 16);
380 } else {
381 /* each FPR value is byte-swapped separately */
382 ret = 0;
383 for (i = 0; i < 16; ++i) {
384 if (!(flags & ST))
385 ret |= __get_user(ptr[i^7], addr + i);
386 else
387 ret |= __put_user(ptr[i^7], addr + i);
388 }
389 }
390 if (ret)
391 return -EFAULT;
392 return 1; /* exception handled and fixed up */
393}
394
359 395
360/* 396/*
361 * Called on alignment exception. Attempts to fixup 397 * Called on alignment exception. Attempts to fixup
@@ -471,6 +507,10 @@ int fix_alignment(struct pt_regs *regs)
471 flush_fp_to_thread(current); 507 flush_fp_to_thread(current);
472 } 508 }
473 509
510 /* Special case for 16-byte FP loads and stores */
511 if (nb == 16)
512 return emulate_fp_pair(regs, addr, reg, flags);
513
474 /* If we are loading, get the data from user space, else 514 /* If we are loading, get the data from user space, else
475 * get it from register values 515 * get it from register values
476 */ 516 */
@@ -531,7 +571,8 @@ int fix_alignment(struct pt_regs *regs)
531 * or floating point single precision conversion 571 * or floating point single precision conversion
532 */ 572 */
533 switch (flags & ~(U|SW)) { 573 switch (flags & ~(U|SW)) {
534 case LD+SE: /* sign extend */ 574 case LD+SE: /* sign extending integer loads */
575 case LD+F+SE: /* sign extend for lfiwax */
535 if ( nb == 2 ) 576 if ( nb == 2 )
536 data.ll = data.x16.low16; 577 data.ll = data.x16.low16;
537 else /* nb must be 4 */ 578 else /* nb must be 4 */