diff options
author | Steve Longerbeam <stevel@mwwireless.net> | 2005-08-31 16:22:20 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2005-08-31 16:22:20 -0400 |
commit | f21ee2d4245293ee6906eb7afd0a701f40e839b9 (patch) | |
tree | a7c1339ca77ecc664c53ffa16864788ee41f3933 /arch/arm/mm/alignment.c | |
parent | 3618886f645c2ede45742d3e3d22a96b2ee2f527 (diff) |
[ARM] 2867/2: unaligned ldrd/strd fixups
Patch from Steve Longerbeam
Adds an implementation of unaligned LDRD and STRD fixups.
Also fixes a bug where do_alignment() would misinterpret and
fixup an unaligned LDRD/STRD as LDRH/STRH, causing memory
corruption.
This is the same as Patch #2867/1, but with minor whitespace
and comments changes, plus a check for arch-level >= v5TE
before printing ai_dword count in proc_alignment_read().
Signed-off-by: Steve Longerbeam <stevel@mwwireless.net>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
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 */ |