Skip to content

Commit

Permalink
[ARM] 2867/2: unaligned ldrd/strd fixups
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Steve Longerbeam authored and Russell King committed Aug 31, 2005
1 parent 3618886 commit f21ee2d
Showing 1 changed file with 56 additions and 14 deletions.
70 changes: 56 additions & 14 deletions arch/arm/mm/alignment.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@

#define LDST_P_EQ_U(i) ((((i) ^ ((i) >> 1)) & (1 << 23)) == 0)

#define LDSTH_I_BIT(i) (i & (1 << 22)) /* half-word immed */
#define LDSTHD_I_BIT(i) (i & (1 << 22)) /* double/half-word immed */
#define LDM_S_BIT(i) (i & (1 << 22)) /* write CPSR from SPSR */

#define RN_BITS(i) ((i >> 16) & 15) /* Rn */
Expand All @@ -68,6 +68,7 @@ static unsigned long ai_sys;
static unsigned long ai_skipped;
static unsigned long ai_half;
static unsigned long ai_word;
static unsigned long ai_dword;
static unsigned long ai_multi;
static int ai_usermode;

Expand All @@ -93,6 +94,8 @@ proc_alignment_read(char *page, char **start, off_t off, int count, int *eof,
p += sprintf(p, "Skipped:\t%lu\n", ai_skipped);
p += sprintf(p, "Half:\t\t%lu\n", ai_half);
p += sprintf(p, "Word:\t\t%lu\n", ai_word);
if (cpu_architecture() >= CPU_ARCH_ARMv5TE)
p += sprintf(p, "DWord:\t\t%lu\n", ai_dword);
p += sprintf(p, "Multi:\t\t%lu\n", ai_multi);
p += sprintf(p, "User faults:\t%i (%s)\n", ai_usermode,
usermode_action[ai_usermode]);
Expand Down Expand Up @@ -283,12 +286,6 @@ do_alignment_ldrhstrh(unsigned long addr, unsigned long instr, struct pt_regs *r
{
unsigned int rd = RD_BITS(instr);

if ((instr & 0x01f00ff0) == 0x01000090)
goto swp;

if ((instr & 0x90) != 0x90 || (instr & 0x60) == 0)
goto bad;

ai_half += 1;

if (user_mode(regs))
Expand Down Expand Up @@ -323,10 +320,47 @@ do_alignment_ldrhstrh(unsigned long addr, unsigned long instr, struct pt_regs *r

return TYPE_LDST;

swp:
printk(KERN_ERR "Alignment trap: not handling swp instruction\n");
bad:
return TYPE_ERROR;
fault:
return TYPE_FAULT;
}

static int
do_alignment_ldrdstrd(unsigned long addr, unsigned long instr,
struct pt_regs *regs)
{
unsigned int rd = RD_BITS(instr);

ai_dword += 1;

if (user_mode(regs))
goto user;

if ((instr & 0xf0) == 0xd0) {
unsigned long val;
get32_unaligned_check(val, addr);
regs->uregs[rd] = val;
get32_unaligned_check(val, addr+4);
regs->uregs[rd+1] = val;
} else {
put32_unaligned_check(regs->uregs[rd], addr);
put32_unaligned_check(regs->uregs[rd+1], addr+4);
}

return TYPE_LDST;

user:
if ((instr & 0xf0) == 0xd0) {
unsigned long val;
get32t_unaligned_check(val, addr);
regs->uregs[rd] = val;
get32t_unaligned_check(val, addr+4);
regs->uregs[rd+1] = val;
} else {
put32t_unaligned_check(regs->uregs[rd], addr);
put32t_unaligned_check(regs->uregs[rd+1], addr+4);
}

return TYPE_LDST;

fault:
return TYPE_FAULT;
Expand Down Expand Up @@ -617,12 +651,20 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
regs->ARM_pc += thumb_mode(regs) ? 2 : 4;

switch (CODING_BITS(instr)) {
case 0x00000000: /* ldrh or strh */
if (LDSTH_I_BIT(instr))
case 0x00000000: /* 3.13.4 load/store instruction extensions */
if (LDSTHD_I_BIT(instr))
offset.un = (instr & 0xf00) >> 4 | (instr & 15);
else
offset.un = regs->uregs[RM_BITS(instr)];
handler = do_alignment_ldrhstrh;

if ((instr & 0x000000f0) == 0x000000b0 || /* LDRH, STRH */
(instr & 0x001000f0) == 0x001000f0) /* LDRSH */
handler = do_alignment_ldrhstrh;
else if ((instr & 0x001000f0) == 0x000000d0 || /* LDRD */
(instr & 0x001000f0) == 0x000000f0) /* STRD */
handler = do_alignment_ldrdstrd;
else
goto bad;
break;

case 0x04000000: /* ldr or str immediate */
Expand Down

0 comments on commit f21ee2d

Please sign in to comment.