Skip to content

Commit c6d4267

Browse files
committed
[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>
1 parent 09a5410 commit c6d4267

File tree

1 file changed

+49
-8
lines changed

1 file changed

+49
-8
lines changed

arch/powerpc/kernel/align.c

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ struct aligninfo {
3838
/* Bits in the flags field */
3939
#define LD 0 /* load */
4040
#define ST 1 /* store */
41-
#define SE 2 /* sign-extend value */
41+
#define SE 2 /* sign-extend value, or FP ld/st as word */
4242
#define F 4 /* to/from fp regs */
4343
#define U 8 /* update index register */
4444
#define M 0x10 /* multiple load/store */
@@ -87,9 +87,9 @@ static struct aligninfo aligninfo[128] = {
8787
{ 8, LD+F+U }, /* 00 1 1001: lfdu */
8888
{ 4, ST+F+S+U }, /* 00 1 1010: stfsu */
8989
{ 8, ST+F+U }, /* 00 1 1011: stfdu */
90-
INVALID, /* 00 1 1100 */
90+
{ 16, LD+F }, /* 00 1 1100: lfdp */
9191
INVALID, /* 00 1 1101 */
92-
INVALID, /* 00 1 1110 */
92+
{ 16, ST+F }, /* 00 1 1110: stfdp */
9393
INVALID, /* 00 1 1111 */
9494
{ 8, LD }, /* 01 0 0000: ldx */
9595
INVALID, /* 01 0 0001 */
@@ -167,10 +167,10 @@ static struct aligninfo aligninfo[128] = {
167167
{ 8, LD+F }, /* 11 0 1001: lfdx */
168168
{ 4, ST+F+S }, /* 11 0 1010: stfsx */
169169
{ 8, ST+F }, /* 11 0 1011: stfdx */
170-
INVALID, /* 11 0 1100 */
171-
{ 8, LD+M }, /* 11 0 1101: lmd */
172-
INVALID, /* 11 0 1110 */
173-
{ 8, ST+M }, /* 11 0 1111: stmd */
170+
{ 16, LD+F }, /* 11 0 1100: lfdpx */
171+
{ 4, LD+F+SE }, /* 11 0 1101: lfiwax */
172+
{ 16, ST+F }, /* 11 0 1110: stfdpx */
173+
{ 4, ST+F }, /* 11 0 1111: stfiwx */
174174
{ 4, LD+U }, /* 11 1 0000: lwzux */
175175
INVALID, /* 11 1 0001 */
176176
{ 4, ST+U }, /* 11 1 0010: stwux */
@@ -356,6 +356,42 @@ static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr,
356356
return 1;
357357
}
358358

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 *) &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+
359395

360396
/*
361397
* Called on alignment exception. Attempts to fixup
@@ -471,6 +507,10 @@ int fix_alignment(struct pt_regs *regs)
471507
flush_fp_to_thread(current);
472508
}
473509

510+
/* Special case for 16-byte FP loads and stores */
511+
if (nb == 16)
512+
return emulate_fp_pair(regs, addr, reg, flags);
513+
474514
/* If we are loading, get the data from user space, else
475515
* get it from register values
476516
*/
@@ -531,7 +571,8 @@ int fix_alignment(struct pt_regs *regs)
531571
* or floating point single precision conversion
532572
*/
533573
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 */
535576
if ( nb == 2 )
536577
data.ll = data.x16.low16;
537578
else /* nb must be 4 */

0 commit comments

Comments
 (0)