Commit 6d615c78 authored by David Howells's avatar David Howells Committed by Linus Torvalds

MN10300: Handle misaligned postinc-with-imm addressing mode correctly

Correctly handle misalignment in MOV instructions with postinc-with-immediate
addressing mode operands.  In these, the immediate value is the increment to
be applied the address register, not the displacement to the address.
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent ddb6d05c
...@@ -44,7 +44,8 @@ ...@@ -44,7 +44,8 @@
static int misalignment_addr(unsigned long *registers, unsigned params, static int misalignment_addr(unsigned long *registers, unsigned params,
unsigned opcode, unsigned long disp, unsigned opcode, unsigned long disp,
void **_address, unsigned long **_postinc); void **_address, unsigned long **_postinc,
unsigned long *_inc);
static int misalignment_reg(unsigned long *registers, unsigned params, static int misalignment_reg(unsigned long *registers, unsigned params,
unsigned opcode, unsigned long disp, unsigned opcode, unsigned long disp,
...@@ -150,7 +151,7 @@ enum value_id { ...@@ -150,7 +151,7 @@ enum value_id {
}; };
struct mn10300_opcode { struct mn10300_opcode {
const char *name; const char name[8];
u_int32_t opcode; u_int32_t opcode;
u_int32_t opmask; u_int32_t opmask;
unsigned exclusion; unsigned exclusion;
...@@ -310,7 +311,7 @@ static const struct mn10300_opcode mn10300_opcodes[] = { ...@@ -310,7 +311,7 @@ static const struct mn10300_opcode mn10300_opcodes[] = {
{ "mov_lne", 0xf7e00009, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, { "mov_lne", 0xf7e00009, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}},
{ "mov_lra", 0xf7e0000a, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, { "mov_lra", 0xf7e0000a, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}},
{ 0, 0, 0, 0, 0, 0, {0}}, { "", 0, 0, 0, 0, 0, {0}},
}; };
/* /*
...@@ -321,11 +322,11 @@ asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code) ...@@ -321,11 +322,11 @@ asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code)
const struct exception_table_entry *fixup; const struct exception_table_entry *fixup;
const struct mn10300_opcode *pop; const struct mn10300_opcode *pop;
unsigned long *registers = (unsigned long *) regs; unsigned long *registers = (unsigned long *) regs;
unsigned long data, *store, *postinc, disp; unsigned long data, *store, *postinc, disp, inc;
mm_segment_t seg; mm_segment_t seg;
siginfo_t info; siginfo_t info;
uint32_t opcode, noc, xo, xm; uint32_t opcode, noc, xo, xm;
uint8_t *pc, byte; uint8_t *pc, byte, datasz;
void *address; void *address;
unsigned tmp, npop, dispsz, loop; unsigned tmp, npop, dispsz, loop;
...@@ -347,7 +348,7 @@ asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code) ...@@ -347,7 +348,7 @@ asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code)
opcode = byte; opcode = byte;
noc = 8; noc = 8;
for (pop = mn10300_opcodes; pop->name; pop++) { for (pop = mn10300_opcodes; pop->name[0]; pop++) {
npop = ilog2(pop->opcode | pop->opmask); npop = ilog2(pop->opcode | pop->opmask);
if (npop <= 0 || npop > 31) if (npop <= 0 || npop > 31)
continue; continue;
...@@ -484,32 +485,31 @@ asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code) ...@@ -484,32 +485,31 @@ asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code)
goto failed; goto failed;
} }
/* determine the data transfer size of the move */
if (pop->name[3] == 0 || /* "mov" */
pop->name[4] == 'l') /* mov_lcc */
inc = datasz = 4;
else if (pop->name[3] == 'h') /* movhu */
inc = datasz = 2;
else
goto unsupported_instruction;
if (pop->params[0] & 0x80000000) { if (pop->params[0] & 0x80000000) {
/* move memory to register */ /* move memory to register */
if (!misalignment_addr(registers, pop->params[0], opcode, disp, if (!misalignment_addr(registers, pop->params[0], opcode, disp,
&address, &postinc)) &address, &postinc, &inc))
goto bad_addr_mode; goto bad_addr_mode;
if (!misalignment_reg(registers, pop->params[1], opcode, disp, if (!misalignment_reg(registers, pop->params[1], opcode, disp,
&store)) &store))
goto bad_reg_mode; goto bad_reg_mode;
if (strcmp(pop->name, "mov") == 0 || kdebug("mov%u (%p),DARn", datasz, address);
memcmp(pop->name, "mov_l", 5) == 0) { if (copy_from_user(&data, (void *) address, datasz) != 0)
kdebug("mov (%p),DARn", address);
if (copy_from_user(&data, (void *) address, 4) != 0)
goto transfer_failed;
if (pop->params[0] & 0x1000000)
*postinc += 4;
} else if (strcmp(pop->name, "movhu") == 0) {
kdebug("movhu (%p),DARn", address);
data = 0;
if (copy_from_user(&data, (void *) address, 2) != 0)
goto transfer_failed; goto transfer_failed;
if (pop->params[0] & 0x1000000) if (pop->params[0] & 0x1000000) {
*postinc += 2; kdebug("inc=%lx", inc);
} else { *postinc += inc;
goto unsupported_instruction;
} }
*store = data; *store = data;
...@@ -521,26 +521,16 @@ asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code) ...@@ -521,26 +521,16 @@ asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code)
goto bad_reg_mode; goto bad_reg_mode;
if (!misalignment_addr(registers, pop->params[1], opcode, disp, if (!misalignment_addr(registers, pop->params[1], opcode, disp,
&address, &postinc)) &address, &postinc, &inc))
goto bad_addr_mode; goto bad_addr_mode;
data = *store; data = *store;
if (strcmp(pop->name, "mov") == 0) { kdebug("mov%u %lx,(%p)", datasz, data, address);
kdebug("mov %lx,(%p)", data, address); if (copy_to_user((void *) address, &data, datasz) != 0)
if (copy_to_user((void *) address, &data, 4) != 0)
goto transfer_failed; goto transfer_failed;
if (pop->params[1] & 0x1000000) if (pop->params[1] & 0x1000000)
*postinc += 4; *postinc += inc;
} else if (strcmp(pop->name, "movhu") == 0) {
kdebug("movhu %hx,(%p)", (uint16_t) data, address);
if (copy_to_user((void *) address, &data, 2) != 0)
goto transfer_failed;
if (pop->params[1] & 0x1000000)
*postinc += 2;
} else {
goto unsupported_instruction;
}
} }
tmp = format_tbl[pop->format].opsz + format_tbl[pop->format].dispsz; tmp = format_tbl[pop->format].opsz + format_tbl[pop->format].dispsz;
...@@ -560,10 +550,17 @@ asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code) ...@@ -560,10 +550,17 @@ asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code)
*/ */
static int misalignment_addr(unsigned long *registers, unsigned params, static int misalignment_addr(unsigned long *registers, unsigned params,
unsigned opcode, unsigned long disp, unsigned opcode, unsigned long disp,
void **_address, unsigned long **_postinc) void **_address, unsigned long **_postinc,
unsigned long *_inc)
{ {
unsigned long *postinc = NULL, address = 0, tmp; unsigned long *postinc = NULL, address = 0, tmp;
if (!(params & 0x1000000)) {
kdebug("noinc");
*_inc = 0;
_inc = NULL;
}
params &= 0x00ffffff; params &= 0x00ffffff;
do { do {
...@@ -624,32 +621,40 @@ static int misalignment_addr(unsigned long *registers, unsigned params, ...@@ -624,32 +621,40 @@ static int misalignment_addr(unsigned long *registers, unsigned params,
address += registers[REG_SP >> 2]; address += registers[REG_SP >> 2];
break; break;
/* displacements are either to be added to the address
* before use, or, in the case of post-inc addressing,
* to be added into the base register after use */
case SD8: case SD8:
case SIMM8: case SIMM8:
address += (int32_t) (int8_t) (disp & 0xff); disp = (long) (int8_t) (disp & 0xff);
break; goto displace_or_inc;
case SD16: case SD16:
address += (int32_t) (int16_t) (disp & 0xffff); disp = (long) (int16_t) (disp & 0xffff);
break; goto displace_or_inc;
case SD24: case SD24:
tmp = disp << 8; tmp = disp << 8;
asm("asr 8,%0" : "=r"(tmp) : "0"(tmp)); asm("asr 8,%0" : "=r"(tmp) : "0"(tmp));
address += tmp; disp = (long) tmp;
break; goto displace_or_inc;
case SIMM4_2: case SIMM4_2:
tmp = opcode >> 4 & 0x0f; tmp = opcode >> 4 & 0x0f;
tmp <<= 28; tmp <<= 28;
asm("asr 28,%0" : "=r"(tmp) : "0"(tmp)); asm("asr 28,%0" : "=r"(tmp) : "0"(tmp));
address += tmp; disp = (long) tmp;
break; goto displace_or_inc;
case IMM24: case IMM24:
address += disp & 0x00ffffff; disp &= 0x00ffffff;
break; goto displace_or_inc;
case IMM32: case IMM32:
case IMM32_MEM: case IMM32_MEM:
case IMM32_HIGH8: case IMM32_HIGH8:
case IMM32_HIGH8_MEM: case IMM32_HIGH8_MEM:
displace_or_inc:
kdebug("%s %lx", _inc ? "incr" : "disp", disp);
if (!_inc)
address += disp; address += disp;
else
*_inc = disp;
break; break;
default: default:
BUG(); BUG();
......
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