diff options
Diffstat (limited to 'arch/powerpc/kernel/align.c')
-rw-r--r-- | arch/powerpc/kernel/align.c | 52 |
1 files changed, 44 insertions, 8 deletions
diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c index de91f3ae631e..94908af308d8 100644 --- a/arch/powerpc/kernel/align.c +++ b/arch/powerpc/kernel/align.c | |||
@@ -73,7 +73,7 @@ static struct aligninfo aligninfo[128] = { | |||
73 | { 8, LD+F }, /* 00 0 1001: lfd */ | 73 | { 8, LD+F }, /* 00 0 1001: lfd */ |
74 | { 4, ST+F+S }, /* 00 0 1010: stfs */ | 74 | { 4, ST+F+S }, /* 00 0 1010: stfs */ |
75 | { 8, ST+F }, /* 00 0 1011: stfd */ | 75 | { 8, ST+F }, /* 00 0 1011: stfd */ |
76 | INVALID, /* 00 0 1100 */ | 76 | { 16, LD }, /* 00 0 1100: lq */ |
77 | { 8, LD }, /* 00 0 1101: ld/ldu/lwa */ | 77 | { 8, LD }, /* 00 0 1101: ld/ldu/lwa */ |
78 | INVALID, /* 00 0 1110 */ | 78 | INVALID, /* 00 0 1110 */ |
79 | { 8, ST }, /* 00 0 1111: std/stdu */ | 79 | { 8, ST }, /* 00 0 1111: std/stdu */ |
@@ -140,7 +140,7 @@ static struct aligninfo aligninfo[128] = { | |||
140 | { 2, LD+SW }, /* 10 0 1100: lhbrx */ | 140 | { 2, LD+SW }, /* 10 0 1100: lhbrx */ |
141 | { 4, LD+SE }, /* 10 0 1101 lwa */ | 141 | { 4, LD+SE }, /* 10 0 1101 lwa */ |
142 | { 2, ST+SW }, /* 10 0 1110: sthbrx */ | 142 | { 2, ST+SW }, /* 10 0 1110: sthbrx */ |
143 | INVALID, /* 10 0 1111 */ | 143 | { 16, ST }, /* 10 0 1111: stq */ |
144 | INVALID, /* 10 1 0000 */ | 144 | INVALID, /* 10 1 0000 */ |
145 | INVALID, /* 10 1 0001 */ | 145 | INVALID, /* 10 1 0001 */ |
146 | INVALID, /* 10 1 0010 */ | 146 | INVALID, /* 10 1 0010 */ |
@@ -385,8 +385,6 @@ static int emulate_fp_pair(unsigned char __user *addr, unsigned int reg, | |||
385 | char *ptr1 = (char *) ¤t->thread.TS_FPR(reg+1); | 385 | char *ptr1 = (char *) ¤t->thread.TS_FPR(reg+1); |
386 | int i, ret, sw = 0; | 386 | int i, ret, sw = 0; |
387 | 387 | ||
388 | if (!(flags & F)) | ||
389 | return 0; | ||
390 | if (reg & 1) | 388 | if (reg & 1) |
391 | return 0; /* invalid form: FRS/FRT must be even */ | 389 | return 0; /* invalid form: FRS/FRT must be even */ |
392 | if (flags & SW) | 390 | if (flags & SW) |
@@ -406,6 +404,34 @@ static int emulate_fp_pair(unsigned char __user *addr, unsigned int reg, | |||
406 | return 1; /* exception handled and fixed up */ | 404 | return 1; /* exception handled and fixed up */ |
407 | } | 405 | } |
408 | 406 | ||
407 | #ifdef CONFIG_PPC64 | ||
408 | static int emulate_lq_stq(struct pt_regs *regs, unsigned char __user *addr, | ||
409 | unsigned int reg, unsigned int flags) | ||
410 | { | ||
411 | char *ptr0 = (char *)®s->gpr[reg]; | ||
412 | char *ptr1 = (char *)®s->gpr[reg+1]; | ||
413 | int i, ret, sw = 0; | ||
414 | |||
415 | if (reg & 1) | ||
416 | return 0; /* invalid form: GPR must be even */ | ||
417 | if (flags & SW) | ||
418 | sw = 7; | ||
419 | ret = 0; | ||
420 | for (i = 0; i < 8; ++i) { | ||
421 | if (!(flags & ST)) { | ||
422 | ret |= __get_user(ptr0[i^sw], addr + i); | ||
423 | ret |= __get_user(ptr1[i^sw], addr + i + 8); | ||
424 | } else { | ||
425 | ret |= __put_user(ptr0[i^sw], addr + i); | ||
426 | ret |= __put_user(ptr1[i^sw], addr + i + 8); | ||
427 | } | ||
428 | } | ||
429 | if (ret) | ||
430 | return -EFAULT; | ||
431 | return 1; /* exception handled and fixed up */ | ||
432 | } | ||
433 | #endif /* CONFIG_PPC64 */ | ||
434 | |||
409 | #ifdef CONFIG_SPE | 435 | #ifdef CONFIG_SPE |
410 | 436 | ||
411 | static struct aligninfo spe_aligninfo[32] = { | 437 | static struct aligninfo spe_aligninfo[32] = { |
@@ -914,10 +940,20 @@ int fix_alignment(struct pt_regs *regs) | |||
914 | flush_fp_to_thread(current); | 940 | flush_fp_to_thread(current); |
915 | } | 941 | } |
916 | 942 | ||
917 | /* Special case for 16-byte FP loads and stores */ | 943 | if ((nb == 16)) { |
918 | if (nb == 16) { | 944 | if (flags & F) { |
919 | PPC_WARN_ALIGNMENT(fp_pair, regs); | 945 | /* Special case for 16-byte FP loads and stores */ |
920 | return emulate_fp_pair(addr, reg, flags); | 946 | PPC_WARN_ALIGNMENT(fp_pair, regs); |
947 | return emulate_fp_pair(addr, reg, flags); | ||
948 | } else { | ||
949 | #ifdef CONFIG_PPC64 | ||
950 | /* Special case for 16-byte loads and stores */ | ||
951 | PPC_WARN_ALIGNMENT(lq_stq, regs); | ||
952 | return emulate_lq_stq(regs, addr, reg, flags); | ||
953 | #else | ||
954 | return 0; | ||
955 | #endif | ||
956 | } | ||
921 | } | 957 | } |
922 | 958 | ||
923 | PPC_WARN_ALIGNMENT(unaligned, regs); | 959 | PPC_WARN_ALIGNMENT(unaligned, regs); |