Commit ff171d8f authored by David S. Miller's avatar David S. Miller

[SPARC64]: Handle little-endian unaligned loads/stores correctly.

Because we use byte loads/stores to cons up the value
in and out of registers, we can't expect the ASI endianness
setting to take care of this for us.  So do it by hand.

This case is triggered by drivers/block/aoe/aoecmd.c in the
ataid_complete() function where it goes:

		/* word 100: number lba48 sectors */
		ssize = le64_to_cpup((__le64 *) &id[100<<1]);

This &id[100<<1] address is 4 byte, rather than 8 byte aligned,
thus triggering the unaligned exception.
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 875bd5ab
...@@ -17,7 +17,7 @@ kernel_unaligned_trap_fault: ...@@ -17,7 +17,7 @@ kernel_unaligned_trap_fault:
__do_int_store: __do_int_store:
rd %asi, %o4 rd %asi, %o4
wr %o3, 0, %asi wr %o3, 0, %asi
ldx [%o2], %g3 mov %o2, %g3
cmp %o1, 2 cmp %o1, 2
be,pn %icc, 2f be,pn %icc, 2f
cmp %o1, 4 cmp %o1, 4
......
...@@ -184,13 +184,14 @@ extern void do_int_load(unsigned long *dest_reg, int size, ...@@ -184,13 +184,14 @@ extern void do_int_load(unsigned long *dest_reg, int size,
unsigned long *saddr, int is_signed, int asi); unsigned long *saddr, int is_signed, int asi);
extern void __do_int_store(unsigned long *dst_addr, int size, extern void __do_int_store(unsigned long *dst_addr, int size,
unsigned long *src_val, int asi); unsigned long src_val, int asi);
static inline void do_int_store(int reg_num, int size, unsigned long *dst_addr, static inline void do_int_store(int reg_num, int size, unsigned long *dst_addr,
struct pt_regs *regs, int asi) struct pt_regs *regs, int asi, int orig_asi)
{ {
unsigned long zero = 0; unsigned long zero = 0;
unsigned long *src_val = &zero; unsigned long *src_val_p = &zero;
unsigned long src_val;
if (size == 16) { if (size == 16) {
size = 8; size = 8;
...@@ -198,7 +199,25 @@ static inline void do_int_store(int reg_num, int size, unsigned long *dst_addr, ...@@ -198,7 +199,25 @@ static inline void do_int_store(int reg_num, int size, unsigned long *dst_addr,
(unsigned)fetch_reg(reg_num, regs) : 0)) << 32) | (unsigned)fetch_reg(reg_num, regs) : 0)) << 32) |
(unsigned)fetch_reg(reg_num + 1, regs); (unsigned)fetch_reg(reg_num + 1, regs);
} else if (reg_num) { } else if (reg_num) {
src_val = fetch_reg_addr(reg_num, regs); src_val_p = fetch_reg_addr(reg_num, regs);
}
src_val = *src_val_p;
if (unlikely(asi != orig_asi)) {
switch (size) {
case 2:
src_val = swab16(src_val);
break;
case 4:
src_val = swab32(src_val);
break;
case 8:
src_val = swab64(src_val);
break;
case 16:
default:
BUG();
break;
};
} }
__do_int_store(dst_addr, size, src_val, asi); __do_int_store(dst_addr, size, src_val, asi);
} }
...@@ -276,6 +295,7 @@ asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn, u ...@@ -276,6 +295,7 @@ asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn, u
kernel_mna_trap_fault(); kernel_mna_trap_fault();
} else { } else {
unsigned long addr; unsigned long addr;
int orig_asi, asi;
addr = compute_effective_address(regs, insn, addr = compute_effective_address(regs, insn,
((insn >> 25) & 0x1f)); ((insn >> 25) & 0x1f));
...@@ -285,18 +305,48 @@ asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn, u ...@@ -285,18 +305,48 @@ asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn, u
regs->tpc, dirstrings[dir], addr, size, regs->tpc, dirstrings[dir], addr, size,
regs->u_regs[UREG_RETPC]); regs->u_regs[UREG_RETPC]);
#endif #endif
orig_asi = asi = decode_asi(insn, regs);
switch (asi) {
case ASI_NL:
case ASI_AIUPL:
case ASI_AIUSL:
case ASI_PL:
case ASI_SL:
case ASI_PNFL:
case ASI_SNFL:
asi &= ~0x08;
break;
};
switch (dir) { switch (dir) {
case load: case load:
do_int_load(fetch_reg_addr(((insn>>25)&0x1f), regs), do_int_load(fetch_reg_addr(((insn>>25)&0x1f), regs),
size, (unsigned long *) addr, size, (unsigned long *) addr,
decode_signedness(insn), decode_signedness(insn), asi);
decode_asi(insn, regs)); if (unlikely(asi != orig_asi)) {
unsigned long val_in = *(unsigned long *) addr;
switch (size) {
case 2:
val_in = swab16(val_in);
break;
case 4:
val_in = swab32(val_in);
break;
case 8:
val_in = swab64(val_in);
break;
case 16:
default:
BUG();
break;
};
*(unsigned long *) addr = val_in;
}
break; break;
case store: case store:
do_int_store(((insn>>25)&0x1f), size, do_int_store(((insn>>25)&0x1f), size,
(unsigned long *) addr, regs, (unsigned long *) addr, regs,
decode_asi(insn, regs)); asi, orig_asi);
break; break;
default: default:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment