diff options
Diffstat (limited to 'arch/arm/mm/alignment.c')
| -rw-r--r-- | arch/arm/mm/alignment.c | 70 |
1 files changed, 56 insertions, 14 deletions
diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c index 81f4a8a2d34b..4b39d867ac14 100644 --- a/arch/arm/mm/alignment.c +++ b/arch/arm/mm/alignment.c | |||
| @@ -45,7 +45,7 @@ | |||
| 45 | 45 | ||
| 46 | #define LDST_P_EQ_U(i) ((((i) ^ ((i) >> 1)) & (1 << 23)) == 0) | 46 | #define LDST_P_EQ_U(i) ((((i) ^ ((i) >> 1)) & (1 << 23)) == 0) |
| 47 | 47 | ||
| 48 | #define LDSTH_I_BIT(i) (i & (1 << 22)) /* half-word immed */ | 48 | #define LDSTHD_I_BIT(i) (i & (1 << 22)) /* double/half-word immed */ |
| 49 | #define LDM_S_BIT(i) (i & (1 << 22)) /* write CPSR from SPSR */ | 49 | #define LDM_S_BIT(i) (i & (1 << 22)) /* write CPSR from SPSR */ |
| 50 | 50 | ||
| 51 | #define RN_BITS(i) ((i >> 16) & 15) /* Rn */ | 51 | #define RN_BITS(i) ((i >> 16) & 15) /* Rn */ |
| @@ -68,6 +68,7 @@ static unsigned long ai_sys; | |||
| 68 | static unsigned long ai_skipped; | 68 | static unsigned long ai_skipped; |
| 69 | static unsigned long ai_half; | 69 | static unsigned long ai_half; |
| 70 | static unsigned long ai_word; | 70 | static unsigned long ai_word; |
| 71 | static unsigned long ai_dword; | ||
| 71 | static unsigned long ai_multi; | 72 | static unsigned long ai_multi; |
| 72 | static int ai_usermode; | 73 | static int ai_usermode; |
| 73 | 74 | ||
| @@ -93,6 +94,8 @@ proc_alignment_read(char *page, char **start, off_t off, int count, int *eof, | |||
| 93 | p += sprintf(p, "Skipped:\t%lu\n", ai_skipped); | 94 | p += sprintf(p, "Skipped:\t%lu\n", ai_skipped); |
| 94 | p += sprintf(p, "Half:\t\t%lu\n", ai_half); | 95 | p += sprintf(p, "Half:\t\t%lu\n", ai_half); |
| 95 | p += sprintf(p, "Word:\t\t%lu\n", ai_word); | 96 | p += sprintf(p, "Word:\t\t%lu\n", ai_word); |
| 97 | if (cpu_architecture() >= CPU_ARCH_ARMv5TE) | ||
| 98 | p += sprintf(p, "DWord:\t\t%lu\n", ai_dword); | ||
| 96 | p += sprintf(p, "Multi:\t\t%lu\n", ai_multi); | 99 | p += sprintf(p, "Multi:\t\t%lu\n", ai_multi); |
| 97 | p += sprintf(p, "User faults:\t%i (%s)\n", ai_usermode, | 100 | p += sprintf(p, "User faults:\t%i (%s)\n", ai_usermode, |
| 98 | usermode_action[ai_usermode]); | 101 | usermode_action[ai_usermode]); |
| @@ -283,12 +286,6 @@ do_alignment_ldrhstrh(unsigned long addr, unsigned long instr, struct pt_regs *r | |||
| 283 | { | 286 | { |
| 284 | unsigned int rd = RD_BITS(instr); | 287 | unsigned int rd = RD_BITS(instr); |
| 285 | 288 | ||
| 286 | if ((instr & 0x01f00ff0) == 0x01000090) | ||
| 287 | goto swp; | ||
| 288 | |||
| 289 | if ((instr & 0x90) != 0x90 || (instr & 0x60) == 0) | ||
| 290 | goto bad; | ||
| 291 | |||
| 292 | ai_half += 1; | 289 | ai_half += 1; |
| 293 | 290 | ||
| 294 | if (user_mode(regs)) | 291 | if (user_mode(regs)) |
| @@ -323,10 +320,47 @@ do_alignment_ldrhstrh(unsigned long addr, unsigned long instr, struct pt_regs *r | |||
| 323 | 320 | ||
| 324 | return TYPE_LDST; | 321 | return TYPE_LDST; |
| 325 | 322 | ||
| 326 | swp: | 323 | fault: |
| 327 | printk(KERN_ERR "Alignment trap: not handling swp instruction\n"); | 324 | return TYPE_FAULT; |
| 328 | bad: | 325 | } |
| 329 | return TYPE_ERROR; | 326 | |
| 327 | static int | ||
| 328 | do_alignment_ldrdstrd(unsigned long addr, unsigned long instr, | ||
| 329 | struct pt_regs *regs) | ||
| 330 | { | ||
| 331 | unsigned int rd = RD_BITS(instr); | ||
| 332 | |||
| 333 | ai_dword += 1; | ||
| 334 | |||
| 335 | if (user_mode(regs)) | ||
| 336 | goto user; | ||
| 337 | |||
| 338 | if ((instr & 0xf0) == 0xd0) { | ||
| 339 | unsigned long val; | ||
| 340 | get32_unaligned_check(val, addr); | ||
| 341 | regs->uregs[rd] = val; | ||
| 342 | get32_unaligned_check(val, addr+4); | ||
| 343 | regs->uregs[rd+1] = val; | ||
| 344 | } else { | ||
| 345 | put32_unaligned_check(regs->uregs[rd], addr); | ||
| 346 | put32_unaligned_check(regs->uregs[rd+1], addr+4); | ||
| 347 | } | ||
| 348 | |||
| 349 | return TYPE_LDST; | ||
| 350 | |||
| 351 | user: | ||
| 352 | if ((instr & 0xf0) == 0xd0) { | ||
| 353 | unsigned long val; | ||
| 354 | get32t_unaligned_check(val, addr); | ||
| 355 | regs->uregs[rd] = val; | ||
| 356 | get32t_unaligned_check(val, addr+4); | ||
| 357 | regs->uregs[rd+1] = val; | ||
| 358 | } else { | ||
| 359 | put32t_unaligned_check(regs->uregs[rd], addr); | ||
| 360 | put32t_unaligned_check(regs->uregs[rd+1], addr+4); | ||
| 361 | } | ||
| 362 | |||
| 363 | return TYPE_LDST; | ||
| 330 | 364 | ||
| 331 | fault: | 365 | fault: |
| 332 | return TYPE_FAULT; | 366 | return TYPE_FAULT; |
| @@ -617,12 +651,20 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | |||
| 617 | regs->ARM_pc += thumb_mode(regs) ? 2 : 4; | 651 | regs->ARM_pc += thumb_mode(regs) ? 2 : 4; |
| 618 | 652 | ||
| 619 | switch (CODING_BITS(instr)) { | 653 | switch (CODING_BITS(instr)) { |
| 620 | case 0x00000000: /* ldrh or strh */ | 654 | case 0x00000000: /* 3.13.4 load/store instruction extensions */ |
| 621 | if (LDSTH_I_BIT(instr)) | 655 | if (LDSTHD_I_BIT(instr)) |
| 622 | offset.un = (instr & 0xf00) >> 4 | (instr & 15); | 656 | offset.un = (instr & 0xf00) >> 4 | (instr & 15); |
| 623 | else | 657 | else |
| 624 | offset.un = regs->uregs[RM_BITS(instr)]; | 658 | offset.un = regs->uregs[RM_BITS(instr)]; |
| 625 | handler = do_alignment_ldrhstrh; | 659 | |
| 660 | if ((instr & 0x000000f0) == 0x000000b0 || /* LDRH, STRH */ | ||
| 661 | (instr & 0x001000f0) == 0x001000f0) /* LDRSH */ | ||
| 662 | handler = do_alignment_ldrhstrh; | ||
| 663 | else if ((instr & 0x001000f0) == 0x000000d0 || /* LDRD */ | ||
| 664 | (instr & 0x001000f0) == 0x000000f0) /* STRD */ | ||
| 665 | handler = do_alignment_ldrdstrd; | ||
| 666 | else | ||
| 667 | goto bad; | ||
| 626 | break; | 668 | break; |
| 627 | 669 | ||
| 628 | case 0x04000000: /* ldr or str immediate */ | 670 | case 0x04000000: /* ldr or str immediate */ |
