diff options
author | Paul Mackerras <paulus@samba.org> | 2007-08-10 00:07:38 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-08-16 21:01:55 -0400 |
commit | c6d4267eced79775399f256fbb4adb671e9b597e (patch) | |
tree | 211acc4464342ba2d87365367c3afb4ce509038d /arch/powerpc/kernel/align.c | |
parent | 09a54101e15f43607722dee55f33d1962653c6cb (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.c | 57 |
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 | */ | ||
364 | static int emulate_fp_pair(struct pt_regs *regs, unsigned char __user *addr, | ||
365 | unsigned int reg, unsigned int flags) | ||
366 | { | ||
367 | char *ptr = (char *) ¤t->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 */ |