diff options
author | David Howells <dhowells@redhat.com> | 2008-11-12 10:35:35 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-11-12 13:41:17 -0500 |
commit | 6d615c78fb92fbd80e52ba7acb2d4c4d503006c3 (patch) | |
tree | 8ea928819e0e5efe9b112ce97f1e41ba0fa8e6a2 | |
parent | ddb6d05cbaea76eddbee52585152ab801a8aedc7 (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>
-rw-r--r-- | arch/mn10300/mm/misalignment.c | 107 |
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 | ||
45 | static int misalignment_addr(unsigned long *registers, unsigned params, | 45 | static 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 | ||
49 | static int misalignment_reg(unsigned long *registers, unsigned params, | 50 | static 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 | ||
152 | struct mn10300_opcode { | 153 | struct 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 | */ |
561 | static int misalignment_addr(unsigned long *registers, unsigned params, | 551 | static 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(); |