aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mn10300/mm
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2008-11-12 10:35:35 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2008-11-12 13:41:17 -0500
commit6d615c78fb92fbd80e52ba7acb2d4c4d503006c3 (patch)
tree8ea928819e0e5efe9b112ce97f1e41ba0fa8e6a2 /arch/mn10300/mm
parentddb6d05cbaea76eddbee52585152ab801a8aedc7 (diff)
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: David Howells <dhowells@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/mn10300/mm')
-rw-r--r--arch/mn10300/mm/misalignment.c107
1 files changed, 56 insertions, 51 deletions
diff --git a/arch/mn10300/mm/misalignment.c b/arch/mn10300/mm/misalignment.c
index d5b45bb7d108..61e65ec47db8 100644
--- a/arch/mn10300/mm/misalignment.c
+++ b/arch/mn10300/mm/misalignment.c
@@ -44,7 +44,8 @@
44 44
45static int misalignment_addr(unsigned long *registers, unsigned params, 45static int misalignment_addr(unsigned long *registers, unsigned params,
46 unsigned opcode, unsigned long disp, 46 unsigned opcode, unsigned long disp,
47 void **_address, unsigned long **_postinc); 47 void **_address, unsigned long **_postinc,
48 unsigned long *_inc);
48 49
49static int misalignment_reg(unsigned long *registers, unsigned params, 50static int misalignment_reg(unsigned long *registers, unsigned params,
50 unsigned opcode, unsigned long disp, 51 unsigned opcode, unsigned long disp,
@@ -150,7 +151,7 @@ enum value_id {
150}; 151};
151 152
152struct mn10300_opcode { 153struct mn10300_opcode {
153 const char *name; 154 const char name[8];
154 u_int32_t opcode; 155 u_int32_t opcode;
155 u_int32_t opmask; 156 u_int32_t opmask;
156 unsigned exclusion; 157 unsigned exclusion;
@@ -310,7 +311,7 @@ static const struct mn10300_opcode mn10300_opcodes[] = {
310{ "mov_lne", 0xf7e00009, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, 311{ "mov_lne", 0xf7e00009, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}},
311{ "mov_lra", 0xf7e0000a, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, 312{ "mov_lra", 0xf7e0000a, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}},
312 313
313{ 0, 0, 0, 0, 0, 0, {0}}, 314{ "", 0, 0, 0, 0, 0, {0}},
314}; 315};
315 316
316/* 317/*
@@ -321,11 +322,11 @@ asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code)
321 const struct exception_table_entry *fixup; 322 const struct exception_table_entry *fixup;
322 const struct mn10300_opcode *pop; 323 const struct mn10300_opcode *pop;
323 unsigned long *registers = (unsigned long *) regs; 324 unsigned long *registers = (unsigned long *) regs;
324 unsigned long data, *store, *postinc, disp; 325 unsigned long data, *store, *postinc, disp, inc;
325 mm_segment_t seg; 326 mm_segment_t seg;
326 siginfo_t info; 327 siginfo_t info;
327 uint32_t opcode, noc, xo, xm; 328 uint32_t opcode, noc, xo, xm;
328 uint8_t *pc, byte; 329 uint8_t *pc, byte, datasz;
329 void *address; 330 void *address;
330 unsigned tmp, npop, dispsz, loop; 331 unsigned tmp, npop, dispsz, loop;
331 332
@@ -347,7 +348,7 @@ asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code)
347 opcode = byte; 348 opcode = byte;
348 noc = 8; 349 noc = 8;
349 350
350 for (pop = mn10300_opcodes; pop->name; pop++) { 351 for (pop = mn10300_opcodes; pop->name[0]; pop++) {
351 npop = ilog2(pop->opcode | pop->opmask); 352 npop = ilog2(pop->opcode | pop->opmask);
352 if (npop <= 0 || npop > 31) 353 if (npop <= 0 || npop > 31)
353 continue; 354 continue;
@@ -484,32 +485,31 @@ found_opcode:
484 goto failed; 485 goto failed;
485 } 486 }
486 487
488 /* determine the data transfer size of the move */
489 if (pop->name[3] == 0 || /* "mov" */
490 pop->name[4] == 'l') /* mov_lcc */
491 inc = datasz = 4;
492 else if (pop->name[3] == 'h') /* movhu */
493 inc = datasz = 2;
494 else
495 goto unsupported_instruction;
496
487 if (pop->params[0] & 0x80000000) { 497 if (pop->params[0] & 0x80000000) {
488 /* move memory to register */ 498 /* move memory to register */
489 if (!misalignment_addr(registers, pop->params[0], opcode, disp, 499 if (!misalignment_addr(registers, pop->params[0], opcode, disp,
490 &address, &postinc)) 500 &address, &postinc, &inc))
491 goto bad_addr_mode; 501 goto bad_addr_mode;
492 502
493 if (!misalignment_reg(registers, pop->params[1], opcode, disp, 503 if (!misalignment_reg(registers, pop->params[1], opcode, disp,
494 &store)) 504 &store))
495 goto bad_reg_mode; 505 goto bad_reg_mode;
496 506
497 if (strcmp(pop->name, "mov") == 0 || 507 kdebug("mov%u (%p),DARn", datasz, address);
498 memcmp(pop->name, "mov_l", 5) == 0) { 508 if (copy_from_user(&data, (void *) address, datasz) != 0)
499 kdebug("mov (%p),DARn", address); 509 goto transfer_failed;
500 if (copy_from_user(&data, (void *) address, 4) != 0) 510 if (pop->params[0] & 0x1000000) {
501 goto transfer_failed; 511 kdebug("inc=%lx", inc);
502 if (pop->params[0] & 0x1000000) 512 *postinc += inc;
503 *postinc += 4;
504 } else if (strcmp(pop->name, "movhu") == 0) {
505 kdebug("movhu (%p),DARn", address);
506 data = 0;
507 if (copy_from_user(&data, (void *) address, 2) != 0)
508 goto transfer_failed;
509 if (pop->params[0] & 0x1000000)
510 *postinc += 2;
511 } else {
512 goto unsupported_instruction;
513 } 513 }
514 514
515 *store = data; 515 *store = data;
@@ -521,26 +521,16 @@ found_opcode:
521 goto bad_reg_mode; 521 goto bad_reg_mode;
522 522
523 if (!misalignment_addr(registers, pop->params[1], opcode, disp, 523 if (!misalignment_addr(registers, pop->params[1], opcode, disp,
524 &address, &postinc)) 524 &address, &postinc, &inc))
525 goto bad_addr_mode; 525 goto bad_addr_mode;
526 526
527 data = *store; 527 data = *store;
528 528
529 if (strcmp(pop->name, "mov") == 0) { 529 kdebug("mov%u %lx,(%p)", datasz, data, address);
530 kdebug("mov %lx,(%p)", data, address); 530 if (copy_to_user((void *) address, &data, datasz) != 0)
531 if (copy_to_user((void *) address, &data, 4) != 0) 531 goto transfer_failed;
532 goto transfer_failed; 532 if (pop->params[1] & 0x1000000)
533 if (pop->params[1] & 0x1000000) 533 *postinc += inc;
534 *postinc += 4;
535 } else if (strcmp(pop->name, "movhu") == 0) {
536 kdebug("movhu %hx,(%p)", (uint16_t) data, address);
537 if (copy_to_user((void *) address, &data, 2) != 0)
538 goto transfer_failed;
539 if (pop->params[1] & 0x1000000)
540 *postinc += 2;
541 } else {
542 goto unsupported_instruction;
543 }
544 } 534 }
545 535
546 tmp = format_tbl[pop->format].opsz + format_tbl[pop->format].dispsz; 536 tmp = format_tbl[pop->format].opsz + format_tbl[pop->format].dispsz;
@@ -560,10 +550,17 @@ found_opcode:
560 */ 550 */
561static int misalignment_addr(unsigned long *registers, unsigned params, 551static int misalignment_addr(unsigned long *registers, unsigned params,
562 unsigned opcode, unsigned long disp, 552 unsigned opcode, unsigned long disp,
563 void **_address, unsigned long **_postinc) 553 void **_address, unsigned long **_postinc,
554 unsigned long *_inc)
564{ 555{
565 unsigned long *postinc = NULL, address = 0, tmp; 556 unsigned long *postinc = NULL, address = 0, tmp;
566 557
558 if (!(params & 0x1000000)) {
559 kdebug("noinc");
560 *_inc = 0;
561 _inc = NULL;
562 }
563
567 params &= 0x00ffffff; 564 params &= 0x00ffffff;
568 565
569 do { 566 do {
@@ -624,32 +621,40 @@ static int misalignment_addr(unsigned long *registers, unsigned params,
624 address += registers[REG_SP >> 2]; 621 address += registers[REG_SP >> 2];
625 break; 622 break;
626 623
624 /* displacements are either to be added to the address
625 * before use, or, in the case of post-inc addressing,
626 * to be added into the base register after use */
627 case SD8: 627 case SD8:
628 case SIMM8: 628 case SIMM8:
629 address += (int32_t) (int8_t) (disp & 0xff); 629 disp = (long) (int8_t) (disp & 0xff);
630 break; 630 goto displace_or_inc;
631 case SD16: 631 case SD16:
632 address += (int32_t) (int16_t) (disp & 0xffff); 632 disp = (long) (int16_t) (disp & 0xffff);
633 break; 633 goto displace_or_inc;
634 case SD24: 634 case SD24:
635 tmp = disp << 8; 635 tmp = disp << 8;
636 asm("asr 8,%0" : "=r"(tmp) : "0"(tmp)); 636 asm("asr 8,%0" : "=r"(tmp) : "0"(tmp));
637 address += tmp; 637 disp = (long) tmp;
638 break; 638 goto displace_or_inc;
639 case SIMM4_2: 639 case SIMM4_2:
640 tmp = opcode >> 4 & 0x0f; 640 tmp = opcode >> 4 & 0x0f;
641 tmp <<= 28; 641 tmp <<= 28;
642 asm("asr 28,%0" : "=r"(tmp) : "0"(tmp)); 642 asm("asr 28,%0" : "=r"(tmp) : "0"(tmp));
643 address += tmp; 643 disp = (long) tmp;
644 break; 644 goto displace_or_inc;
645 case IMM24: 645 case IMM24:
646 address += disp & 0x00ffffff; 646 disp &= 0x00ffffff;
647 break; 647 goto displace_or_inc;
648 case IMM32: 648 case IMM32:
649 case IMM32_MEM: 649 case IMM32_MEM:
650 case IMM32_HIGH8: 650 case IMM32_HIGH8:
651 case IMM32_HIGH8_MEM: 651 case IMM32_HIGH8_MEM:
652 address += disp; 652 displace_or_inc:
653 kdebug("%s %lx", _inc ? "incr" : "disp", disp);
654 if (!_inc)
655 address += disp;
656 else
657 *_inc = disp;
653 break; 658 break;
654 default: 659 default:
655 BUG(); 660 BUG();