From: David H. <dho...@re...> - 2000-11-22 17:32:00
|
I've attached a patch to do fixups on all instructions that can incur an unaligned access fault, barring "multiply from postinc memory operands". It also handles faults in jump, branch and return delay slots. David Howells ========== diff -uNr linuxsh/arch/sh/kernel/traps.c my-linuxsh/arch/sh/kernel/traps.c --- linuxsh/arch/sh/kernel/traps.c Mon Nov 20 13:27:44 2000 +++ my-linuxsh/arch/sh/kernel/traps.c Wed Nov 22 17:02:07 2000 @@ -29,6 +29,8 @@ #include <asm/atomic.h> #include <asm/processor.h> +#define kdebug(...) /*printk(__VA_ARGS__)*/ + static inline void console_verbose(void) { extern int console_loglevel; @@ -46,7 +48,6 @@ sti(); \ tsk->thread.error_code = error_code; \ tsk->thread.trap_no = trapnr; \ -show_regs(®s); \ force_sig(signr, tsk); \ die_if_no_fixup(str,®s,error_code); \ } @@ -77,7 +78,17 @@ die(str, regs, err); } -static void die_if_no_fixup(const char * str, struct pt_regs * regs, long err) +static int handle_unaligned_notify_count = 10; + +/*****************************************************************************/ +/* + * try and fix up kernelspace address errors + * - userspace errors just cause EFAULT to be returned, resulting in SEGV + * - kernel/userspace interfaces cause a jump to an appropriate handler + * - other kernel errors are bad + * - return 0 if fixed-up, -EFAULT if non-fatal (to the kernel) fault + */ +static int die_if_no_fixup(const char * str, struct pt_regs * regs, long err) { if (!user_mode(regs)) { @@ -85,27 +96,39 @@ fixup = search_exception_table(regs->pc); if (fixup) { regs->pc = fixup; - return; + return 0; } die(str, regs, err); } -} + return -EFAULT; +} /* end die_if_no_fixup() */ -static void handle_unaligned(u16 instruction, struct pt_regs *regs) +/*****************************************************************************/ +/* + * handle an instruction that does an unaligned memory access by emulating the + * desired behaviour + * - note that PC _may not_ point to the faulting instruction (if that instruction + * is in a branch delay slot + * - return 0 if emulation okay, -EFAULT on existential error + */ +static int handle_unaligned_ins(u16 instruction, struct pt_regs *regs) { - int index, count; + int ret, index, count; unsigned long *rm, *rn; unsigned char *src, *dst; - index = (instruction>>8)&15; + index = (instruction>>8)&15; /* 0x0F00 */ rn = ®s->regs[index]; - index = (instruction>>4)&15; + index = (instruction>>4)&15; /* 0x00F0 */ rm = ®s->regs[index]; count = 1<<(instruction&3); - switch(instruction>>12) { + kdebug("handle_unaligned_ins(%p,%04hx)\n",(u16*)regs->pc,instruction); + + ret = -EFAULT; + switch (instruction>>12) { case 0: /* mov.[bwl] to/from memory via r0+rn */ if (instruction & 8) { /* from memory */ @@ -114,102 +137,399 @@ dst = (unsigned char*) rn; *(unsigned long*)dst = 0; -#ifdef __LITTLE_ENDIAN__ - memcpy(dst, src, count); + kdebug("mov (%p),dst\n",src); - if ((count == 2) && dst[1] & 0x80) { - dst[2] = 0xff; - dst[3] = 0xff; - } -#else +#ifdef __BIG_ENDIAN dst += 4-count; - - memcpy(dst, src, count); + + /* We might be trying to fix up an access to a non-existent user page */ + if (__copy_user(dst, src, count)) + goto fetch_fault; if ((count == 2) && dst[2] & 0x80) { dst[0] = 0xff; dst[1] = 0xff; } +#else + /* We might be trying to fix up an access to a non-existent user page */ + if (__copy_user(dst, src, count)) + goto fetch_fault; + + if ((count == 2) && dst[1] & 0x80) { + dst[2] = 0xff; + dst[3] = 0xff; + } #endif } else { /* to memory */ src = (unsigned char*) rm; -#if !defined(__LITTLE_ENDIAN__) +#ifdef __BIG_ENDIAN src += 4-count; #endif dst = (unsigned char*) *rn; dst += regs->regs[0]; - memcpy(dst, src, count); + kdebug("mov src,(%p)\n",dst); + + /* We might be trying to fix up an access to a non-existent user page */ + if (__copy_user(dst, src, count)) + goto fetch_fault; } + + ret = 0; + break; + + case 1: /* mov.l Rm,@(disp,Rn) */ + src = (unsigned char*) rm; + dst = (unsigned char*) *rn; + dst += (instruction&0x000F)<<2; + + /* We might be trying to fix up an access to a non-existent user page */ + if (__copy_user(dst,src,4)) + goto fetch_fault; + ret = 0; break; case 2: /* mov.[bwl] to memory, possibly with pre-decrement */ + kdebug("mov--\n"); if(instruction & 4) *rn -= count; src = (unsigned char*) rm; dst = (unsigned char*) *rn; -#if !defined(__LITTLE_ENDIAN__) +#ifdef __BIG_ENDIAN src += 4-count; #endif - memcpy(dst, src, count); + /* We might be trying to fix up an access to a non-existent user page */ + if (__copy_user(dst, src, count)) + goto fetch_fault; + ret = 0; + break; + + case 5: /* mov.l @(disp,Rm),Rn */ + src = (unsigned char*) *rm; + src += (instruction&0x000F)<<2; + dst = (unsigned char*) rn; + *(unsigned long*)dst = 0; + /* we might be trying to fix up an access to a non-existent user page */ + if (__copy_user(dst,src,4)) + goto fetch_fault; + ret = 0; break; - case 6: - /* mov.[bwl] from memory, possibly with post-increment */ + + case 6: /* mov.[bwl] from memory, possibly with post-increment */ + kdebug("mov++\n"); src = (unsigned char*) *rm; if(instruction & 4) *rm += count; dst = (unsigned char*) rn; *(unsigned long*)dst = 0; - -#ifdef __LITTLE_ENDIAN__ - memcpy(dst, src, count); - if ((count == 2) && dst[1] & 0x80) { - dst[2] = 0xff; - dst[3] = 0xff; - } -#else +#ifdef __BIG_ENDIAN dst += 4-count; - - memcpy(dst, src, count); + + /* We might be trying to fix up an access to a non-existent user page */ + if (__copy_user(dst, src, count)) + goto fetch_fault; if ((count == 2) && dst[2] & 0x80) { dst[0] = 0xff; dst[1] = 0xff; } +#else + /* We might be trying to fix up an access to a non-existent user page */ + if (__copy_user(dst, src, count)) + goto fetch_fault; + + if ((count == 2) && dst[1] & 0x80) { + dst[2] = 0xff; + dst[3] = 0xff; + } +#endif + ret = 0; + break; + + case 8: + switch ((instruction&0xFF00)>>8) { + case 0x81: + /* mov.w R0,@(disp,Rn) */ + src = (unsigned char*) ®s->regs[0]; +#ifdef __BIG_ENDIAN + src += 2; +#endif + dst = (unsigned char*) *rm; /* called Rn in the spec */ + dst += (instruction&0x000F)<<1; + + /* we might be trying to fix up an access to a non-existent user page */ + if (__copy_user(dst, src, 2)) + goto fetch_fault; + ret = 0; + break; + + case 0x85: + /* mov.w @(disp,Rm),R0 */ + src = (unsigned char*) *rm; + src += (instruction&0x000F)<<1; + dst = (unsigned char*) ®s->regs[0]; + *(unsigned long*)dst = 0; + +#ifdef __BIG_ENDIAN + dst += 2; +#endif + + /* we might be trying to fix up an access to a non-existent user page */ + if (__copy_user(dst, src, 2)) + goto fetch_fault; + +#ifdef __BIG_ENDIAN + if (dst[2] & 0x80) { + dst[0] = 0xff; + dst[1] = 0xff; + } +#else + if (dst[1] & 0x80) { + dst[2] = 0xff; + dst[3] = 0xff; + } #endif + ret = 0; + break; + } + break; } - regs->pc += 2; -} + return ret; + + fetch_fault: + /* Argh. Address not only misaligned but also non-existent. + * Raise an EFAULT and see if it's trapped + */ + return die_if_no_fixup("Fault in unaligned fixup", regs, 0); +} /* end handle_unaligned_ins() */ + +/*****************************************************************************/ +/* + * emulate the instruction in the delay slot + * - fetches the instruction from PC+2 + */ +static inline int handle_unaligned_delayslot(struct pt_regs *regs) +{ + u16 instruction; + + if (__copy_user(&instruction, (u16 *)(regs->pc+2), 2)) { + /* the instruction-fetch faulted */ + if (user_mode(regs)) + return -EFAULT; + + /* kernel */ + die("delay-slot-insn faulting in handle_unaligned_delayslot", regs, 0); + } + + return handle_unaligned_ins(instruction,regs); + +} /* end handle_unaligned_delayslot() */ + +/*****************************************************************************/ +/* + * handle an instruction that does an unaligned memory access + * - have to be careful of branch delay-slot instructions that fault + * - if the branch would be taken PC points to the branch + * - if the branch would not be taken, PC points to delay-slot + * - return 0 if handled, -EFAULT if failed (may not return if in kernel) + */ +static int handle_unaligned_access(u16 instruction, struct pt_regs *regs) +{ + u_int rm; + int ret, index; + + index = (instruction>>8)&15; /* 0x0F00 */ + rm = regs->regs[index]; + + /* shout about the first ten userspace fixups */ + if (user_mode(regs) && handle_unaligned_notify_count>0) { + handle_unaligned_notify_count--; + + printk("Fixing up unaligned userspace access in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", + current->comm,current->pid,(u16*)regs->pc,instruction); + } + + kdebug("handle_unaligned_access(%p,%04hx)\n",(u16*)regs->pc,instruction); + + ret = -EFAULT; + switch (instruction&0xF000) { + case 0x0000: + if (instruction==0x000B) { + /* rts */ + kdebug("rts\n"); + ret = handle_unaligned_delayslot(regs); + if (ret==0) + regs->pc = regs->pr; + } + else if ((instruction&0x00FF)==0x0023) { + /* braf @Rm */ + kdebug("braf @Rm\n"); + ret = handle_unaligned_delayslot(regs); + if (ret==0) + regs->pc += rm + 4; + } + else if ((instruction&0x00FF)==0x0003) { + /* bsrf @Rm */ + kdebug("bsrf @Rm\n"); + ret = handle_unaligned_delayslot(regs); + if (ret==0) { + regs->pr = regs->pc + 4; + regs->pc += rm + 4; + } + } + else { + /* mov.[bwl] to/from memory via r0+rn */ + goto simple; + } + break; + + case 0x1000: /* mov.l Rm,@(disp,Rn) */ + goto simple; + + case 0x2000: /* mov.[bwl] to memory, possibly with pre-decrement */ + goto simple; -asmlinkage void do_address_error(struct pt_regs *regs, + case 0x4000: + if ((instruction&0x00FF)==0x002B) { + /* jmp @Rm */ + kdebug("jmp @Rm\n"); + ret = handle_unaligned_delayslot(regs); + if (ret==0) + regs->pc = rm; + } + else if ((instruction&0x00FF)==0x000B) { + /* jsr @Rm */ + kdebug("jsr @Rm\n"); + ret = handle_unaligned_delayslot(regs); + if (ret==0) { + regs->pr = regs->pc + 4; + regs->pc = rm; + } + } + else { + /* mov.[bwl] to/from memory via r0+rn */ + goto simple; + } + break; + + case 0x5000: /* mov.l @(disp,Rm),Rn */ + goto simple; + + case 0x6000: /* mov.[bwl] from memory, possibly with post-increment */ + goto simple; + + case 0x8000: /* bf lab, bf/s lab, bt lab, bt/s lab */ + switch (instruction&0x0F00) { + case 0x0100: /* mov.w R0,@(disp,Rm) */ + goto simple; + case 0x0500: /* mov.w @(disp,Rm),R0 */ + goto simple; + case 0x0B00: /* bf lab - no delayslot*/ + kdebug("bf label\n"); + break; + case 0x0F00: /* bf/s lab */ + kdebug("bf/s label\n"); + ret = handle_unaligned_delayslot(regs); + if (ret==0) + regs->pc += (instruction&0x00FF)*2 + 4; + break; + case 0x0900: /* bt lab - no delayslot */ + kdebug("bt label\n"); + break; + case 0x0D00: /* bt/s lab */ + kdebug("bt/s label\n"); + ret = handle_unaligned_delayslot(regs); + if (ret==0) + regs->pc += (instruction&0x00FF)*2 + 4; + break; + } + break; + + case 0xA000: /* bra label */ + kdebug("bra label\n"); + ret = handle_unaligned_delayslot(regs); + if (ret==0) + regs->pc += (instruction&0x0FFF)*2 + 4; + break; + + case 0xB000: /* bsr label */ + kdebug("bsr label\n"); + ret = handle_unaligned_delayslot(regs); + if (ret==0) { + regs->pr = regs->pc + 4; + regs->pc += (instruction&0x0FFF)*2 + 4; + } + break; + } + return ret; + + /* handle non-delay-slot instruction */ + simple: + ret = handle_unaligned_ins(instruction,regs); + if (ret==0) + regs->pc += 2; + return ret; +} /* end handle_unaligned_access() */ + +/*****************************************************************************/ +/* + * handle various address error exceptions + */ +asmlinkage void do_address_error(struct pt_regs *regs, unsigned long writeaccess, unsigned long address) { unsigned long error_code; + u16 instruction; + int tmp; asm volatile("stc r2_bank,%0": "=r" (error_code)); - if(user_mode(regs)) { + if (user_mode(regs)) { sti(); + current->thread.error_code = error_code; current->thread.trap_no = (writeaccess) ? 8 : 7; + + /* bad PC is not something we can fix */ + if (regs->pc & 1) + goto uspace_segv; + + if (__copy_user(&instruction, (u16 *)(regs->pc), 2)) { + /* Argh. Fault on the instruction itself. + This should never happen non-SMP + */ + goto uspace_segv; + } + + tmp = handle_unaligned_access(instruction, regs); + + if (tmp==0) + return; /* sorted */ + + uspace_segv: + printk(KERN_NOTICE "Killing process \"%s\" due to unaligned access\n", current->comm); force_sig(SIGSEGV, current); - } else { - u16 instruction; - - if(regs->pc & 1) + } + else { + if (regs->pc & 1) die("unaligned program counter", regs, error_code); - - instruction = *(u16 *)(regs->pc); - - handle_unaligned(instruction, regs); + + if (__copy_user(&instruction, (u16 *)(regs->pc), 2)) { + /* Argh. Fault on the instruction itself. + This should never happen non-SMP + */ + die("insn faulting in do_address_error", regs, 0); + } + + handle_unaligned_access(instruction, regs); } -} +} /* end do_address_error() */ DO_ERROR(12, SIGILL, "reserved instruction", reserved_inst, current) DO_ERROR(13, SIGILL, "illegal slot instruction", illegal_slot_inst, current) |