aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorGeorge G. Davis <gdavis@mvista.com>2009-06-04 12:16:04 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2009-06-19 11:35:34 -0400
commitc2860d43f5dfab599fc1308ab61b1d3e30801ceb (patch)
tree16c350b23b657c2bb22123d86010cc4e725a2e39 /arch/arm
parent41184f6a5ef0d88529904d54f06f88b67fb76f4a (diff)
[ARM] 5540/1: 32-bit Thumb-2 {ld,st}{m,rd} alignment fault fixup support
From: Min Zhang <mzhang@mvista.com> Add alignment fault fixup support for 32-bit Thumb-2 LDM, LDRD, POP, PUSH, STM and STRD instructions. Alignment fault fixup support for the remaining 32-bit Thumb-2 load/store instruction cases is not included since ARMv6 and later processors include hardware support for loads and stores of unaligned words and halfwords. Signed-off-by: Min Zhang <mzhang@mvista.com> Signed-off-by: George G. Davis <gdavis@mvista.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mm/alignment.c139
1 files changed, 119 insertions, 20 deletions
diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c
index 3a398befed41..03cd27d917b9 100644
--- a/arch/arm/mm/alignment.c
+++ b/arch/arm/mm/alignment.c
@@ -62,6 +62,12 @@
62#define SHIFT_ASR 0x40 62#define SHIFT_ASR 0x40
63#define SHIFT_RORRRX 0x60 63#define SHIFT_RORRRX 0x60
64 64
65#define BAD_INSTR 0xdeadc0de
66
67/* Thumb-2 32 bit format per ARMv7 DDI0406A A6.3, either f800h,e800h,f800h */
68#define IS_T32(hi16) \
69 (((hi16) & 0xe000) == 0xe000 && ((hi16) & 0x1800))
70
65static unsigned long ai_user; 71static unsigned long ai_user;
66static unsigned long ai_sys; 72static unsigned long ai_sys;
67static unsigned long ai_skipped; 73static unsigned long ai_skipped;
@@ -332,38 +338,48 @@ do_alignment_ldrdstrd(unsigned long addr, unsigned long instr,
332 struct pt_regs *regs) 338 struct pt_regs *regs)
333{ 339{
334 unsigned int rd = RD_BITS(instr); 340 unsigned int rd = RD_BITS(instr);
335 341 unsigned int rd2;
336 if (((rd & 1) == 1) || (rd == 14)) 342 int load;
343
344 if ((instr & 0xfe000000) == 0xe8000000) {
345 /* ARMv7 Thumb-2 32-bit LDRD/STRD */
346 rd2 = (instr >> 8) & 0xf;
347 load = !!(LDST_L_BIT(instr));
348 } else if (((rd & 1) == 1) || (rd == 14))
337 goto bad; 349 goto bad;
350 else {
351 load = ((instr & 0xf0) == 0xd0);
352 rd2 = rd + 1;
353 }
338 354
339 ai_dword += 1; 355 ai_dword += 1;
340 356
341 if (user_mode(regs)) 357 if (user_mode(regs))
342 goto user; 358 goto user;
343 359
344 if ((instr & 0xf0) == 0xd0) { 360 if (load) {
345 unsigned long val; 361 unsigned long val;
346 get32_unaligned_check(val, addr); 362 get32_unaligned_check(val, addr);
347 regs->uregs[rd] = val; 363 regs->uregs[rd] = val;
348 get32_unaligned_check(val, addr + 4); 364 get32_unaligned_check(val, addr + 4);
349 regs->uregs[rd + 1] = val; 365 regs->uregs[rd2] = val;
350 } else { 366 } else {
351 put32_unaligned_check(regs->uregs[rd], addr); 367 put32_unaligned_check(regs->uregs[rd], addr);
352 put32_unaligned_check(regs->uregs[rd + 1], addr + 4); 368 put32_unaligned_check(regs->uregs[rd2], addr + 4);
353 } 369 }
354 370
355 return TYPE_LDST; 371 return TYPE_LDST;
356 372
357 user: 373 user:
358 if ((instr & 0xf0) == 0xd0) { 374 if (load) {
359 unsigned long val; 375 unsigned long val;
360 get32t_unaligned_check(val, addr); 376 get32t_unaligned_check(val, addr);
361 regs->uregs[rd] = val; 377 regs->uregs[rd] = val;
362 get32t_unaligned_check(val, addr + 4); 378 get32t_unaligned_check(val, addr + 4);
363 regs->uregs[rd + 1] = val; 379 regs->uregs[rd2] = val;
364 } else { 380 } else {
365 put32t_unaligned_check(regs->uregs[rd], addr); 381 put32t_unaligned_check(regs->uregs[rd], addr);
366 put32t_unaligned_check(regs->uregs[rd + 1], addr + 4); 382 put32t_unaligned_check(regs->uregs[rd2], addr + 4);
367 } 383 }
368 384
369 return TYPE_LDST; 385 return TYPE_LDST;
@@ -616,8 +632,72 @@ thumb2arm(u16 tinstr)
616 /* Else fall through for illegal instruction case */ 632 /* Else fall through for illegal instruction case */
617 633
618 default: 634 default:
619 return 0xdeadc0de; 635 return BAD_INSTR;
636 }
637}
638
639/*
640 * Convert Thumb-2 32 bit LDM, STM, LDRD, STRD to equivalent instruction
641 * handlable by ARM alignment handler, also find the corresponding handler,
642 * so that we can reuse ARM userland alignment fault fixups for Thumb.
643 *
644 * @pinstr: original Thumb-2 instruction; returns new handlable instruction
645 * @regs: register context.
646 * @poffset: return offset from faulted addr for later writeback
647 *
648 * NOTES:
649 * 1. Comments below refer to ARMv7 DDI0406A Thumb Instruction sections.
650 * 2. Register name Rt from ARMv7 is same as Rd from ARMv6 (Rd is Rt)
651 */
652static void *
653do_alignment_t32_to_handler(unsigned long *pinstr, struct pt_regs *regs,
654 union offset_union *poffset)
655{
656 unsigned long instr = *pinstr;
657 u16 tinst1 = (instr >> 16) & 0xffff;
658 u16 tinst2 = instr & 0xffff;
659 poffset->un = 0;
660
661 switch (tinst1 & 0xffe0) {
662 /* A6.3.5 Load/Store multiple */
663 case 0xe880: /* STM/STMIA/STMEA,LDM/LDMIA, PUSH/POP T2 */
664 case 0xe8a0: /* ...above writeback version */
665 case 0xe900: /* STMDB/STMFD, LDMDB/LDMEA */
666 case 0xe920: /* ...above writeback version */
667 /* no need offset decision since handler calculates it */
668 return do_alignment_ldmstm;
669
670 case 0xf840: /* POP/PUSH T3 (single register) */
671 if (RN_BITS(instr) == 13 && (tinst2 & 0x09ff) == 0x0904) {
672 u32 L = !!(LDST_L_BIT(instr));
673 const u32 subset[2] = {
674 0xe92d0000, /* STMDB sp!,{registers} */
675 0xe8bd0000, /* LDMIA sp!,{registers} */
676 };
677 *pinstr = subset[L] | (1<<RD_BITS(instr));
678 return do_alignment_ldmstm;
679 }
680 /* Else fall through for illegal instruction case */
681 break;
682
683 /* A6.3.6 Load/store double, STRD/LDRD(immed, lit, reg) */
684 case 0xe860:
685 case 0xe960:
686 case 0xe8e0:
687 case 0xe9e0:
688 poffset->un = (tinst2 & 0xff) << 2;
689 case 0xe940:
690 case 0xe9c0:
691 return do_alignment_ldrdstrd;
692
693 /*
694 * No need to handle load/store instructions up to word size
695 * since ARMv6 and later CPUs can perform unaligned accesses.
696 */
697 default:
698 break;
620 } 699 }
700 return NULL;
621} 701}
622 702
623static int 703static int
@@ -630,6 +710,8 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
630 mm_segment_t fs; 710 mm_segment_t fs;
631 unsigned int fault; 711 unsigned int fault;
632 u16 tinstr = 0; 712 u16 tinstr = 0;
713 int isize = 4;
714 int thumb2_32b = 0;
633 715
634 instrptr = instruction_pointer(regs); 716 instrptr = instruction_pointer(regs);
635 717
@@ -637,8 +719,19 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
637 set_fs(KERNEL_DS); 719 set_fs(KERNEL_DS);
638 if (thumb_mode(regs)) { 720 if (thumb_mode(regs)) {
639 fault = __get_user(tinstr, (u16 *)(instrptr & ~1)); 721 fault = __get_user(tinstr, (u16 *)(instrptr & ~1));
640 if (!(fault)) 722 if (!fault) {
641 instr = thumb2arm(tinstr); 723 if (cpu_architecture() >= CPU_ARCH_ARMv7 &&
724 IS_T32(tinstr)) {
725 /* Thumb-2 32-bit */
726 u16 tinst2 = 0;
727 fault = __get_user(tinst2, (u16 *)(instrptr+2));
728 instr = (tinstr << 16) | tinst2;
729 thumb2_32b = 1;
730 } else {
731 isize = 2;
732 instr = thumb2arm(tinstr);
733 }
734 }
642 } else 735 } else
643 fault = __get_user(instr, (u32 *)instrptr); 736 fault = __get_user(instr, (u32 *)instrptr);
644 set_fs(fs); 737 set_fs(fs);
@@ -655,7 +748,7 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
655 748
656 fixup: 749 fixup:
657 750
658 regs->ARM_pc += thumb_mode(regs) ? 2 : 4; 751 regs->ARM_pc += isize;
659 752
660 switch (CODING_BITS(instr)) { 753 switch (CODING_BITS(instr)) {
661 case 0x00000000: /* 3.13.4 load/store instruction extensions */ 754 case 0x00000000: /* 3.13.4 load/store instruction extensions */
@@ -714,18 +807,25 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
714 handler = do_alignment_ldrstr; 807 handler = do_alignment_ldrstr;
715 break; 808 break;
716 809
717 case 0x08000000: /* ldm or stm */ 810 case 0x08000000: /* ldm or stm, or thumb-2 32bit instruction */
718 handler = do_alignment_ldmstm; 811 if (thumb2_32b)
812 handler = do_alignment_t32_to_handler(&instr, regs, &offset);
813 else
814 handler = do_alignment_ldmstm;
719 break; 815 break;
720 816
721 default: 817 default:
722 goto bad; 818 goto bad;
723 } 819 }
724 820
821 if (!handler)
822 goto bad;
725 type = handler(addr, instr, regs); 823 type = handler(addr, instr, regs);
726 824
727 if (type == TYPE_ERROR || type == TYPE_FAULT) 825 if (type == TYPE_ERROR || type == TYPE_FAULT) {
826 regs->ARM_pc -= isize;
728 goto bad_or_fault; 827 goto bad_or_fault;
828 }
729 829
730 if (type == TYPE_LDST) 830 if (type == TYPE_LDST)
731 do_alignment_finish_ldst(addr, instr, regs, offset); 831 do_alignment_finish_ldst(addr, instr, regs, offset);
@@ -735,7 +835,6 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
735 bad_or_fault: 835 bad_or_fault:
736 if (type == TYPE_ERROR) 836 if (type == TYPE_ERROR)
737 goto bad; 837 goto bad;
738 regs->ARM_pc -= thumb_mode(regs) ? 2 : 4;
739 /* 838 /*
740 * We got a fault - fix it up, or die. 839 * We got a fault - fix it up, or die.
741 */ 840 */
@@ -751,8 +850,8 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
751 */ 850 */
752 printk(KERN_ERR "Alignment trap: not handling instruction " 851 printk(KERN_ERR "Alignment trap: not handling instruction "
753 "%0*lx at [<%08lx>]\n", 852 "%0*lx at [<%08lx>]\n",
754 thumb_mode(regs) ? 4 : 8, 853 isize << 1,
755 thumb_mode(regs) ? tinstr : instr, instrptr); 854 isize == 2 ? tinstr : instr, instrptr);
756 ai_skipped += 1; 855 ai_skipped += 1;
757 return 1; 856 return 1;
758 857
@@ -763,8 +862,8 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
763 printk("Alignment trap: %s (%d) PC=0x%08lx Instr=0x%0*lx " 862 printk("Alignment trap: %s (%d) PC=0x%08lx Instr=0x%0*lx "
764 "Address=0x%08lx FSR 0x%03x\n", current->comm, 863 "Address=0x%08lx FSR 0x%03x\n", current->comm,
765 task_pid_nr(current), instrptr, 864 task_pid_nr(current), instrptr,
766 thumb_mode(regs) ? 4 : 8, 865 isize << 1,
767 thumb_mode(regs) ? tinstr : instr, 866 isize == 2 ? tinstr : instr,
768 addr, fsr); 867 addr, fsr);
769 868
770 if (ai_usermode & UM_FIXUP) 869 if (ai_usermode & UM_FIXUP)