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>

authored by David Howells and committed by Linus Torvalds 6d615c78 ddb6d05c

+56 -51
+56 -51
arch/mn10300/mm/misalignment.c
··· 44 44 45 45 static int misalignment_addr(unsigned long *registers, unsigned params, 46 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 50 static int misalignment_reg(unsigned long *registers, unsigned params, 50 51 unsigned opcode, unsigned long disp, ··· 151 150 }; 152 151 153 152 struct mn10300_opcode { 154 - const char *name; 153 + const char name[8]; 155 154 u_int32_t opcode; 156 155 u_int32_t opmask; 157 156 unsigned exclusion; ··· 311 310 { "mov_lne", 0xf7e00009, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, 312 311 { "mov_lra", 0xf7e0000a, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, 313 312 314 - { 0, 0, 0, 0, 0, 0, {0}}, 313 + { "", 0, 0, 0, 0, 0, {0}}, 315 314 }; 316 315 317 316 /* ··· 322 321 const struct exception_table_entry *fixup; 323 322 const struct mn10300_opcode *pop; 324 323 unsigned long *registers = (unsigned long *) regs; 325 - unsigned long data, *store, *postinc, disp; 324 + unsigned long data, *store, *postinc, disp, inc; 326 325 mm_segment_t seg; 327 326 siginfo_t info; 328 327 uint32_t opcode, noc, xo, xm; 329 - uint8_t *pc, byte; 328 + uint8_t *pc, byte, datasz; 330 329 void *address; 331 330 unsigned tmp, npop, dispsz, loop; 332 331 ··· 348 347 opcode = byte; 349 348 noc = 8; 350 349 351 - for (pop = mn10300_opcodes; pop->name; pop++) { 350 + for (pop = mn10300_opcodes; pop->name[0]; pop++) { 352 351 npop = ilog2(pop->opcode | pop->opmask); 353 352 if (npop <= 0 || npop > 31) 354 353 continue; ··· 485 484 goto failed; 486 485 } 487 486 487 + /* determine the data transfer size of the move */ 488 + if (pop->name[3] == 0 || /* "mov" */ 489 + pop->name[4] == 'l') /* mov_lcc */ 490 + inc = datasz = 4; 491 + else if (pop->name[3] == 'h') /* movhu */ 492 + inc = datasz = 2; 493 + else 494 + goto unsupported_instruction; 495 + 488 496 if (pop->params[0] & 0x80000000) { 489 497 /* move memory to register */ 490 498 if (!misalignment_addr(registers, pop->params[0], opcode, disp, 491 - &address, &postinc)) 499 + &address, &postinc, &inc)) 492 500 goto bad_addr_mode; 493 501 494 502 if (!misalignment_reg(registers, pop->params[1], opcode, disp, 495 503 &store)) 496 504 goto bad_reg_mode; 497 505 498 - if (strcmp(pop->name, "mov") == 0 || 499 - memcmp(pop->name, "mov_l", 5) == 0) { 500 - kdebug("mov (%p),DARn", address); 501 - if (copy_from_user(&data, (void *) address, 4) != 0) 502 - goto transfer_failed; 503 - if (pop->params[0] & 0x1000000) 504 - *postinc += 4; 505 - } else if (strcmp(pop->name, "movhu") == 0) { 506 - kdebug("movhu (%p),DARn", address); 507 - data = 0; 508 - if (copy_from_user(&data, (void *) address, 2) != 0) 509 - goto transfer_failed; 510 - if (pop->params[0] & 0x1000000) 511 - *postinc += 2; 512 - } else { 513 - goto unsupported_instruction; 506 + kdebug("mov%u (%p),DARn", datasz, address); 507 + if (copy_from_user(&data, (void *) address, datasz) != 0) 508 + goto transfer_failed; 509 + if (pop->params[0] & 0x1000000) { 510 + kdebug("inc=%lx", inc); 511 + *postinc += inc; 514 512 } 515 513 516 514 *store = data; ··· 521 521 goto bad_reg_mode; 522 522 523 523 if (!misalignment_addr(registers, pop->params[1], opcode, disp, 524 - &address, &postinc)) 524 + &address, &postinc, &inc)) 525 525 goto bad_addr_mode; 526 526 527 527 data = *store; 528 528 529 - if (strcmp(pop->name, "mov") == 0) { 530 - kdebug("mov %lx,(%p)", data, address); 531 - if (copy_to_user((void *) address, &data, 4) != 0) 532 - goto transfer_failed; 533 - if (pop->params[1] & 0x1000000) 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 - } 529 + kdebug("mov%u %lx,(%p)", datasz, data, address); 530 + if (copy_to_user((void *) address, &data, datasz) != 0) 531 + goto transfer_failed; 532 + if (pop->params[1] & 0x1000000) 533 + *postinc += inc; 544 534 } 545 535 546 536 tmp = format_tbl[pop->format].opsz + format_tbl[pop->format].dispsz; ··· 550 560 */ 551 561 static int misalignment_addr(unsigned long *registers, unsigned params, 552 562 unsigned opcode, unsigned long disp, 553 - void **_address, unsigned long **_postinc) 563 + void **_address, unsigned long **_postinc, 564 + unsigned long *_inc) 554 565 { 555 566 unsigned long *postinc = NULL, address = 0, tmp; 567 + 568 + if (!(params & 0x1000000)) { 569 + kdebug("noinc"); 570 + *_inc = 0; 571 + _inc = NULL; 572 + } 556 573 557 574 params &= 0x00ffffff; 558 575 ··· 621 624 address += registers[REG_SP >> 2]; 622 625 break; 623 626 627 + /* displacements are either to be added to the address 628 + * before use, or, in the case of post-inc addressing, 629 + * to be added into the base register after use */ 624 630 case SD8: 625 631 case SIMM8: 626 - address += (int32_t) (int8_t) (disp & 0xff); 627 - break; 632 + disp = (long) (int8_t) (disp & 0xff); 633 + goto displace_or_inc; 628 634 case SD16: 629 - address += (int32_t) (int16_t) (disp & 0xffff); 630 - break; 635 + disp = (long) (int16_t) (disp & 0xffff); 636 + goto displace_or_inc; 631 637 case SD24: 632 638 tmp = disp << 8; 633 639 asm("asr 8,%0" : "=r"(tmp) : "0"(tmp)); 634 - address += tmp; 635 - break; 640 + disp = (long) tmp; 641 + goto displace_or_inc; 636 642 case SIMM4_2: 637 643 tmp = opcode >> 4 & 0x0f; 638 644 tmp <<= 28; 639 645 asm("asr 28,%0" : "=r"(tmp) : "0"(tmp)); 640 - address += tmp; 641 - break; 646 + disp = (long) tmp; 647 + goto displace_or_inc; 642 648 case IMM24: 643 - address += disp & 0x00ffffff; 644 - break; 649 + disp &= 0x00ffffff; 650 + goto displace_or_inc; 645 651 case IMM32: 646 652 case IMM32_MEM: 647 653 case IMM32_HIGH8: 648 654 case IMM32_HIGH8_MEM: 649 - address += disp; 655 + displace_or_inc: 656 + kdebug("%s %lx", _inc ? "incr" : "disp", disp); 657 + if (!_inc) 658 + address += disp; 659 + else 660 + *_inc = disp; 650 661 break; 651 662 default: 652 663 BUG();