| /* |
| * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * vineetg: May 2011 |
| * -user-space unaligned access emulation |
| * |
| * Vineetg: June 10th 2008 |
| * -Added show_callee_regs to display CALLEE REGS |
| * -Added show_fault_diagnostics as a common function to display all |
| * the REGS, trigger event logging etc |
| * |
| * Rahul Trivedi: Codito Technologies 2004 |
| */ |
| |
| #include <linux/sched.h> |
| #include <linux/kdebug.h> |
| #include <asm/event-log.h> |
| #include <asm/uaccess.h> |
| |
| #include <asm/board/unaligned_accounting.h> |
| |
| extern int fixup_exception(struct pt_regs *regs); |
| void show_kernel_fault_diag(const char *str, struct pt_regs *regs, |
| unsigned long address, unsigned long cause_reg); |
| |
| |
| /* "volatile" because it causes compiler to optimize away code. |
| * Since running_on_hw is init to 1 at compile time, with -O2 compiler |
| * throws away the code in die( ) which is a problem on ISS |
| */ |
| volatile int running_on_hw = 1; |
| |
| void die(const char *str, struct pt_regs *regs, unsigned long address, |
| unsigned long cause_reg) |
| { |
| if (running_on_hw) { |
| show_kernel_fault_diag(str, regs, address, cause_reg); |
| } |
| |
| // DEAD END |
| __asm__("flag 1"); |
| } |
| |
| static int noinline do_fatal_exception(unsigned long cause, char *str, |
| struct pt_regs *regs, siginfo_t * info) |
| { |
| if (user_mode(regs)) { |
| struct task_struct *tsk = current; |
| |
| tsk->thread.fault_address = (unsigned int)info->si_addr; |
| tsk->thread.cause_code = cause; |
| |
| force_sig_info(info->si_signo, info, tsk); |
| |
| } else { |
| /* Are we prepared to handle this kernel fault? |
| * |
| * (The kernel has valid exception-points in the source |
| * when it acesses user-memory. When it fails in one |
| * of those points, we find it in a table and do a jump |
| * to some fixup code that loads an appropriate error |
| * code) |
| */ |
| if (fixup_exception(regs)) |
| return 0; |
| |
| /* |
| * Oops. The kernel tried to access some bad page. |
| * We'll have to terminate things with extreme prejudice. |
| */ |
| die(str, regs, (unsigned long)info->si_addr, cause); |
| } |
| |
| return 1; |
| } |
| |
| #define DO_ERROR_INFO(signr, str, name, sicode) \ |
| int name(unsigned long cause, unsigned long address, struct pt_regs *regs) \ |
| { \ |
| siginfo_t info;\ |
| info.si_signo = signr;\ |
| info.si_errno = 0;\ |
| info.si_code = sicode;\ |
| info.si_addr = (void *)address;\ |
| return do_fatal_exception(cause,str,regs,&info);\ |
| } |
| |
| |
| #ifdef CONFIG_ARC_MISALIGNED_ACCESS |
| |
| //#define DBG_MISALIGNED_FIXUP |
| #ifdef DBG_MISALIGNED_FIXUP |
| #define DBG_MISALIGNED_FIXUP_BUFSIZE 1024 |
| #define DBG_BUF(args...) \ |
| if ((__dbg_p - __debug_buf) < DBG_MISALIGNED_FIXUP_BUFSIZE - 80) { \ |
| __dbg_p += sprintf(__dbg_p, "%s %d: ", __FUNCTION__, __LINE__); \ |
| __dbg_p += sprintf(__dbg_p, args); \ |
| } |
| #define DBG(args...) DBG_BUF(args) |
| |
| #else |
| |
| #define DBG_PRINT(args...) do { \ |
| printk(KERN_INFO "%s %d: ", __FUNCTION__, __LINE__); \ |
| printk(args); \ |
| } while (0) |
| |
| //#define DBG(args...) DBG_PRINT(args) |
| #define DBG(args...) |
| |
| #endif |
| |
| |
| |
| #define INST16_OPCODE_START 0xc |
| #define REG_LIMM 62 |
| |
| #define __get8_unaligned_check(val,addr,err) \ |
| __asm__( \ |
| "1: ldb.ab %1, [%2, 1]\n" \ |
| "2:\n" \ |
| " .section .fixup,\"ax\"\n" \ |
| " .align 4\n" \ |
| "3: mov %0, 1\n" \ |
| " b 2b\n" \ |
| " .previous\n" \ |
| " .section __ex_table,\"a\"\n" \ |
| " .align 4\n" \ |
| " .long 1b, 3b\n" \ |
| " .previous\n" \ |
| : "=r" (err), "=&r" (val), "=r" (addr) \ |
| : "0" (err), "2" (addr)) |
| |
| #define get16_unaligned_check(val,addr) \ |
| do { \ |
| unsigned int err = 0, v, a = addr; \ |
| __get8_unaligned_check(v,a,err); \ |
| val = v ; \ |
| __get8_unaligned_check(v,a,err); \ |
| val |= v << 8; \ |
| if (err) \ |
| goto fault; \ |
| } while (0) |
| |
| #define get32_unaligned_check(val,addr) \ |
| do { \ |
| unsigned int err = 0, v, a = addr; \ |
| __get8_unaligned_check(v,a,err); \ |
| val = v << 0; \ |
| __get8_unaligned_check(v,a,err); \ |
| val |= v << 8; \ |
| __get8_unaligned_check(v,a,err); \ |
| val |= v << 16; \ |
| __get8_unaligned_check(v,a,err); \ |
| val |= v << 24; \ |
| if (err) \ |
| goto fault; \ |
| } while (0) |
| |
| #define put16_unaligned_check(val,addr) \ |
| do { \ |
| unsigned int err = 0, v = val, a = addr; \ |
| __asm__( \ |
| "1: stb.ab %1, [%2, 1]\n" \ |
| " lsr %1, %1, 8\n" \ |
| "2: stb %1, [%2]\n" \ |
| "3:\n" \ |
| " .section .fixup,\"ax\"\n" \ |
| " .align 4\n" \ |
| "4: mov %0, 1\n" \ |
| " b 3b\n" \ |
| " .previous\n" \ |
| " .section __ex_table,\"a\"\n" \ |
| " .align 4\n" \ |
| " .long 1b, 4b\n" \ |
| " .long 2b, 4b\n" \ |
| " .previous\n" \ |
| : "=r" (err), "=&r" (v), "=&r" (a) \ |
| : "0" (err), "1" (v), "2" (a)); \ |
| if (err) \ |
| goto fault; \ |
| } while (0) |
| |
| #define put32_unaligned_check(val,addr) \ |
| do { \ |
| unsigned int err = 0, v = val, a = addr; \ |
| __asm__( \ |
| "1: stb.ab %1, [%2, 1]\n" \ |
| " lsr %1, %1, 8\n" \ |
| "2: stb.ab %1, [%2, 1]\n" \ |
| " lsr %1, %1, 8\n" \ |
| "3: stb.ab %1, [%2, 1]\n" \ |
| " lsr %1, %1, 8\n" \ |
| "4: stb %1, [%2]\n" \ |
| "5:\n" \ |
| " .section .fixup,\"ax\"\n" \ |
| " .align 4\n" \ |
| "6: mov %0, 1\n" \ |
| " b 5b\n" \ |
| " .previous\n" \ |
| " .section __ex_table,\"a\"\n" \ |
| " .align 4\n" \ |
| " .long 1b, 6b\n" \ |
| " .long 2b, 6b\n" \ |
| " .long 3b, 6b\n" \ |
| " .long 4b, 6b\n" \ |
| " .previous\n" \ |
| : "=r" (err), "=&r" (v), "=&r" (a) \ |
| : "0" (err), "1" (v), "2" (a)); \ |
| if (err) \ |
| goto fault; \ |
| } while (0) |
| |
| /* 16 bit instructions use the registers r0 r1 r2 r3 r12 r13 r14 r15. */ |
| /* e.g. if reg from the instruction = 5, we want r13 */ |
| static inline unsigned long fix_reg16(const unsigned int reg) |
| { |
| if (reg & 0x04) |
| return reg | 0x08; |
| return reg; |
| } |
| |
| static int get_reg(unsigned int reg, struct pt_regs *regs, struct callee_regs *cregs, long *val, bool inst_16) |
| { |
| long *p; |
| |
| if (inst_16) |
| reg = fix_reg16(reg); |
| |
| if (reg <= 12) { |
| p = ®s->r0; |
| *val = p[-reg]; |
| return 0; |
| } |
| |
| if (reg <= 25) { |
| p = &cregs->r13; |
| *val = p[13-reg]; |
| return 0; |
| } |
| |
| if (reg == 26) { |
| *val = regs->r26; |
| return 0; |
| } |
| |
| if (reg == 27) { |
| *val = regs->fp; |
| return 0; |
| } |
| |
| if (reg == 28) { |
| *val = regs->sp; |
| return 0; |
| } |
| |
| if (reg == 31) { |
| *val = regs->blink; |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| static int set_reg(unsigned int reg, long val, struct pt_regs *regs, struct callee_regs *cregs, bool inst_16) |
| { |
| long *p; |
| |
| if (inst_16) |
| reg = fix_reg16(reg); |
| |
| if (reg <= 12) { |
| p = ®s->r0; |
| p[-reg] = val; |
| return 0; |
| } |
| |
| if (reg <= 25) { |
| p = &cregs->r13; |
| p[13-reg] = val; |
| return 0; |
| } |
| |
| if (reg == 26) { |
| regs->r26 = val; |
| return 0; |
| } |
| |
| if (reg == 27) { |
| regs->fp = val; |
| return 0; |
| } |
| |
| if (reg == 28) { |
| regs->sp = val; |
| return 0; |
| } |
| |
| if (reg == 31) { |
| regs->blink = val; |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| #define get_limm(x) \ |
| do { \ |
| if (usermode) { \ |
| if (copy_from_user(&x, (u32 *)(instrptr + 4), 4)) \ |
| goto fault; \ |
| } else { \ |
| x = *((unsigned*)(instrptr + 4)); \ |
| } \ |
| x = ((x & 0xffff) << 16) | (x >> 16); \ |
| } while(0) |
| |
| /* accounting for unaligned accesses */ |
| struct unaligned_access_accounting unaligned_access_stats = {0}; |
| EXPORT_SYMBOL(unaligned_access_stats); |
| |
| static int misaligned_fixup(const unsigned long address, |
| struct pt_regs *regs, const unsigned long cause, struct callee_regs *cregs) |
| { |
| unsigned long instrptr; |
| unsigned long instr, instr_lo; |
| bool inst_16 = true; |
| long src1, src2, src3 = 0, dest = 0, val; |
| unsigned zz, aa, write, x, pref, limm, di, wb_reg = 0; |
| |
| #ifdef DBG_MISALIGNED_FIXUP |
| char __debug_buf[DBG_MISALIGNED_FIXUP_BUFSIZE] = {0}; |
| char *__dbg_p = __debug_buf; |
| src1 = 0, src2 = 0, src3 = 0, dest = 0, val = 0; |
| zz = 0, aa = 0, write = 0, x = 0, pref = 0, limm = 0, di = 0, wb_reg = 0; |
| #endif |
| |
| struct unaligned_access_accounting* stats = &unaligned_access_stats; |
| |
| /* set cregs for fault printout */ |
| current->thread.callee_reg = (unsigned long)cregs; |
| |
| const int usermode = user_mode(regs); |
| |
| di = 0; |
| aa = 0; |
| write = 0; |
| x = 0; |
| pref = 0; |
| limm = 0; |
| instr = 0; |
| |
| instrptr = instruction_pointer(regs); |
| |
| if (usermode) { |
| if (get_user(instr, (u16 *)(instrptr & ~1))) { |
| goto fault; |
| } |
| } else { |
| instr = *((u16 *)(instrptr & ~1)); |
| } |
| |
| inst_16 = (((instr >> 11) & 0x1f) >= INST16_OPCODE_START); |
| if (!inst_16) { |
| if (usermode) { |
| if (get_user(instr_lo, (u16 *)((instrptr & ~1) + 2))) { |
| goto fault; |
| } |
| } else { |
| instr_lo = *((u16 *)((instrptr & ~1) + 2)); |
| } |
| instr = (instr << 16) | instr_lo; |
| } |
| |
| DBG(KERN_ERR "alignment fix: 0x%0*lx at [<0x%08lx>] usermode: %d delay slot: %d\n", |
| inst_16 ? 4 : 8, |
| instr, instrptr, usermode, delay_mode(regs)); |
| |
| /* rate limited complaint about faulting userland programs */ |
| if (usermode && printk_ratelimit()) |
| printk(KERN_ERR "Misaligned access trap for user program '%s' (parent '%s'): instr: 0x%0*lx at [<0x%08lx>]\n", |
| current->comm, current->parent ? current->parent->comm : "<none>", |
| inst_16 ? 4 : 8, instr, instrptr); |
| |
| /* accounting */ |
| if (usermode) { |
| stats->user++; |
| } else { |
| stats->kernel_iptr[stats->kernel % UNALIGNED_INSTPTR_BUFSIZE] = instrptr; |
| stats->kernel++; |
| } |
| |
| if (inst_16) |
| stats->inst_16++; |
| else |
| stats->inst_32++; |
| |
| /* instruction decoding */ |
| if (inst_16) { |
| /* 16 bit instruction */ |
| const int decode = (instr >> 11) & 0x1f; |
| DBG("decode=0x%02x\n", decode); |
| stats->inst[decode % UNALIGNED_INST_OPCODES]++; |
| switch (decode) { |
| case 0x0c: /* LD_S|LDB_S|LDW_S a,[b,c] */ |
| zz = (instr >> 3) & 3; |
| src1 = (instr >> 8) & 7; |
| src2 = (instr >> 5) & 7; |
| dest = instr & 7; |
| if ((get_reg(src1, regs, cregs, &src1, inst_16)) || |
| (get_reg(src2, regs, cregs, &src2, inst_16))) |
| goto bad; |
| break; |
| |
| case 0x10: /* LD_S c,[b,u7] */ |
| zz = 0; |
| src1 = (instr >> 8) & 7; |
| src2 = (instr & 0x1f) << 2; |
| dest = (instr >> 5) & 7; |
| //DBG("0x10: src1 %ld src2 %ld dest %ld\n", src1, src2, dest); |
| if (get_reg(src1, regs, cregs, &src1, inst_16)) |
| goto bad; |
| //DBG("0x10: src1 0x%08lx\n", src1); |
| break; |
| |
| case 0x11: /* LDB_S c,[b,u5] */ |
| zz = 1; |
| break; |
| |
| case 0x12: /* LDW_S c,[b,u6] */ |
| zz = 2; |
| src1 = (instr >> 8) & 7; |
| src2 = (instr & 0x1f) << 1; |
| dest = (instr >> 5) & 7; |
| if (get_reg(src1, regs, cregs, &src1, inst_16)) |
| goto bad; |
| break; |
| |
| case 0x13: /* LDW_S.X c,[b,u6] */ |
| x = 1; |
| zz = 2; |
| src1 = (instr >> 8) & 7; |
| src2 = (instr & 0x1f) << 1; |
| dest = (instr >> 5) & 7; |
| if (get_reg(src1, regs, cregs, &src1, inst_16)) |
| goto bad; |
| break; |
| |
| case 0x14: /* ST_S c,[b,u7] */ |
| write = 1; |
| zz = 0; |
| src1 = (instr >> 5) & 7; |
| src2 = (instr >> 8) & 7; |
| src3 = (instr & 0x1f) << 2; |
| if ((get_reg(src1, regs, cregs, &src1, inst_16)) || |
| (get_reg(src2, regs, cregs, &src2, inst_16))) |
| goto bad; |
| break; |
| |
| case 0x15: /* STB_S c,[b,u6] */ |
| zz = 1; /* STB should not have unaligned exception */ |
| break; |
| |
| case 0x16: /* STW_S c,[b,u6] */ |
| write = 1; |
| zz = 2; |
| src1 = (instr >> 5) & 7; |
| src2 = (instr >> 8) & 7; |
| src3 = (instr & 0x1f) << 1; |
| if ((get_reg(src1, regs, cregs, &src1, inst_16)) || |
| (get_reg(src2, regs, cregs, &src2, inst_16))) |
| goto bad; |
| break; |
| |
| case 0x18: /* LD_S|LDB_S b,[sp,u7], ST_S|STB_S b,[sp,u7] */ |
| write = (instr >> 6) & 1; |
| zz = (instr >> 5) & 1; |
| if (zz == 1) |
| break; |
| if (write == 0) { |
| src1 = 28; |
| src2 = (instr & 0x1f) << 2; |
| dest = (instr >> 8) & 7; |
| if (get_reg(src1, regs, cregs, &src1, inst_16)) |
| goto bad; |
| |
| } else { |
| src1 = (instr >> 8) & 7; |
| src2 = 28; |
| src3 = (instr & 0x1f) << 2; |
| if ((get_reg(src1, regs, cregs, &src1, inst_16)) || |
| (get_reg(src2, regs, cregs, &src2, inst_16))) |
| goto bad; |
| } |
| break; |
| |
| case 0x19: /* LD_S|LDB_S|LDW_S r0,[gp,s11/s9/s10] */ |
| zz = (instr >> 9) & 3; |
| src1 = 26; |
| src2 = instr & 0x1ff; |
| if (zz == 0) |
| src2 = (src2 << 23) >> 21; /* s11 */ |
| else if (zz == 1) |
| break; |
| else if (zz == 2) |
| src2 = (src2 << 23) >> 22; /* s10 */ |
| dest = 0; |
| if (get_reg(src1, regs, cregs, &src1, inst_16)) |
| goto bad; |
| break; |
| |
| case 0x1a: /* LD_S b,[pcl,u10] */ |
| zz = 0; |
| src1 = regs->ret; |
| src2 = (instr & 0xff) << 2; |
| dest = (instr >> 8) & 7; |
| break; |
| |
| default: |
| goto bad; |
| } |
| |
| } else { |
| /* 32 bit instruction */ |
| const int decode = (instr >> 27) & 0x1f; |
| DBG("decode=0x%02x\n", decode); |
| stats->inst[decode % UNALIGNED_INST_OPCODES]++; |
| switch (decode) { |
| case 0x02: /* LD<zz> a,[b,s9] */ |
| di = (instr >> 11) & 1; |
| if (di) |
| break; |
| x = (instr >> 6) & 1; |
| zz = (instr >> 7) & 3; |
| aa = (instr >> 9) & 3; |
| wb_reg = (((instr >> 12) & 7) << 3) | ((instr >> 24) & 7); |
| if (wb_reg == REG_LIMM) { |
| limm = 1; |
| aa = 0; |
| get_limm(src1); |
| } else { |
| if (get_reg(wb_reg, regs, cregs, &src1, inst_16)) |
| goto bad; |
| } |
| src2 = ((instr >> 16) & 0xff) | (((instr >> 15) & 1) << 8); |
| src2 = (src2 << 23) >> 23; |
| dest = instr & 0x3f; |
| if (dest == REG_LIMM) { |
| pref = 1; |
| break; |
| } |
| break; |
| |
| case 0x03: /* ST<zz> c,[b,s9] */ |
| write = 1; |
| di = (instr >> 5) & 1; |
| if (di) |
| break; |
| aa = (instr >> 3) & 3; |
| zz = (instr >> 1) & 3; |
| src1 = (instr >> 6) & 0x3f; |
| //DBG("32 bit st: src1 reg %ld\n", src1); |
| if (src1 == REG_LIMM) { |
| limm = 1; |
| get_limm(src1); |
| } else { |
| if (get_reg(src1, regs, cregs, &src1, inst_16)) |
| goto bad; |
| } |
| //DBG("32 bit st: src1 val %ld\n", src1); |
| wb_reg = (((instr >> 12) & 7) << 3) | ((instr >> 24) & 7); |
| if (wb_reg == REG_LIMM) { |
| aa = 0; |
| limm = 1; |
| get_limm(src2); |
| } else { |
| if (get_reg(wb_reg, regs, cregs, &src2, inst_16)) |
| goto bad; |
| } |
| src3 = ((instr >> 16) & 0xff) | (((instr >> 15) & 1) << 8); |
| src3 = (src3 << 23) >> 23; |
| break; |
| |
| case 0x04: /* LD<zz> a,[b,c] */ |
| di = (instr >> 15) & 1; |
| if (di) |
| break; |
| x = (instr >> 16) & 1; |
| zz = (instr >> 17) & 3; |
| aa = (instr >> 22) & 3; |
| wb_reg = (((instr >> 12) & 7) << 3) | ((instr >> 24) & 7); |
| if (wb_reg == REG_LIMM) { |
| limm = 1; |
| get_limm(src1); |
| } else { |
| if (get_reg(wb_reg, regs, cregs, &src1, inst_16)) |
| goto bad; |
| } |
| src2 = (instr >> 6) & 0x3f; |
| if (src2 == REG_LIMM) { |
| limm = 1; |
| get_limm(src2); |
| } else { |
| if (get_reg(src2, regs, cregs, &src2, inst_16)) |
| goto bad; |
| } |
| dest = instr & 0x3f; |
| if (dest == REG_LIMM) |
| pref = 1; |
| break; |
| |
| default: |
| goto bad; |
| } |
| } |
| |
| /* ldb/stb should not have unaligned exception */ |
| if ((zz == 1) || (di)) |
| goto bad; |
| |
| DBG("write %d aa %d zz %d pref %d wb_reg %d src1 0x%lx src2 0x%lx src3 %ld dest %ld val 0x%08lx\n", |
| write, aa, zz, pref, wb_reg, src1, src2, src3, dest, val); |
| |
| /* ld instructions */ |
| if (write == 0) { |
| /* register write back */ |
| if ((aa == 1) || (aa == 2)) { |
| if (set_reg(wb_reg, src1 + src2, regs, cregs, inst_16)) |
| goto bad; |
| if (aa == 2) |
| src2 = 0; |
| } |
| |
| unsigned long faulting_address = src1 + src2; |
| if (aa == 3 && zz == 2) |
| faulting_address = src1 + (src2 << 1); |
| else if (aa == 3 && zz == 0) |
| faulting_address = src1 + (src2 << 2); |
| |
| if (faulting_address != address) |
| printk(KERN_ERR "%s ld address 0x%lx does not match those of operands: s1/s2: 0x%lx 0x%lx\n", |
| __FUNCTION__, address, src1, src2); |
| |
| if (zz == 0) |
| get32_unaligned_check(val, faulting_address); |
| else { |
| get16_unaligned_check(val, faulting_address); |
| |
| if (x) |
| val = (val << 16) >> 16; |
| } |
| |
| if (pref == 0) |
| if (set_reg(dest, val, regs, cregs, inst_16)) |
| goto bad; |
| |
| /* store instructions */ |
| } else { |
| /* register write back */ |
| if ((aa == 1) || (aa == 2)) { |
| if (set_reg(wb_reg, src2 + src3, regs, cregs, inst_16)) |
| goto bad; |
| if (aa == 2) |
| src3 = 0; |
| } |
| |
| unsigned long faulting_address = src2 + src3; |
| if (aa == 3 && zz == 2) |
| faulting_address = src2 + (src3 << 1); |
| else if (aa == 3 && zz == 0) |
| faulting_address = src2 + (src3 << 2); |
| |
| if (faulting_address != address) |
| printk(KERN_ERR "%s st address 0x%lx does not match those of operands: s2/s3: 0x%lx 0x%lx\n", |
| __FUNCTION__, address, src2, src3); |
| |
| /* write fix-up */ |
| if (zz == 0) |
| put32_unaligned_check(src1, faulting_address); |
| else |
| put16_unaligned_check(src1, faulting_address); |
| } |
| |
| /* accounting */ |
| if (write) |
| stats->write++; |
| else |
| stats->read++; |
| if (zz == 0) |
| stats->word++; |
| else |
| stats->half++; |
| |
| if (delay_mode(regs)) { |
| regs->ret = regs->bta & ~0x1; |
| regs->status32 &= ~STATUS_DE_MASK; |
| } else { |
| regs->ret += (inst_16) ? 2 : |
| (limm) ? 8 : 4; |
| } |
| |
| return 0; |
| |
| bad: |
| #ifdef DBG_MISALIGNED_FIXUP |
| printk(KERN_ERR "%s\n", __debug_buf); |
| #endif |
| /* |
| * Oops, we didn't handle the instruction. |
| */ |
| printk(KERN_ERR "Alignment trap: not handling instruction " |
| "0x%0*lx at [<0x%08lx>] usermode: %d\n", |
| inst_16 ? 4 : 8, |
| instr, instrptr, usermode); |
| |
| stats->skipped++; |
| |
| return 1; |
| |
| fault: |
| #ifdef DBG_MISALIGNED_FIXUP |
| printk(KERN_ERR "%s\n", __debug_buf); |
| #endif |
| printk(KERN_ERR "Alignment trap: fault in fix-up " |
| "0x%0*lx at [<0x%08lx>] usermode: %d\n", |
| inst_16 ? 4 : 8, |
| instr, instrptr, usermode); |
| |
| stats->skipped++; |
| |
| return 1; |
| } |
| |
| int do_misaligned_access(unsigned long cause, unsigned long address, |
| struct pt_regs *regs, struct callee_regs *cregs) |
| { |
| if (misaligned_fixup(address, regs, cause, cregs) != 0) { |
| siginfo_t info; |
| |
| info.si_signo = SIGSEGV; |
| info.si_errno = 0; |
| info.si_code = SEGV_ACCERR; |
| info.si_addr = (void *)address; |
| return do_fatal_exception(cause,"Misaligned Access", regs,&info); |
| } |
| return 0; |
| } |
| |
| #else |
| DO_ERROR_INFO(SIGSEGV, "Misaligned Access", do_misaligned_access, SEGV_ACCERR) |
| #endif |
| |
| DO_ERROR_INFO(SIGILL, "Privileged Operation/Disabled Extension/Actionpoint Hit", |
| do_privilege_fault, ILL_PRVOPC) |
| DO_ERROR_INFO(SIGILL, "Extenion Instruction Exception", |
| do_extension_fault, ILL_ILLOPC) |
| DO_ERROR_INFO(SIGILL, "Illegal Instruction/Illegal Instruction Sequence", |
| do_instruction_error, ILL_ILLOPC) |
| DO_ERROR_INFO(SIGBUS, "Access to Invalid Memory", do_memory_error, BUS_ADRERR) |
| DO_ERROR_INFO(SIGTRAP, "Breakpoint Set", do_trap_is_brkpt, TRAP_BRKPT) |
| |
| |
| void do_machine_check_fault( unsigned long cause, unsigned long address, |
| struct pt_regs *regs) |
| { |
| die("Machine Check Exception",regs, address, cause); |
| } |
| |
| void __init trap_init(void) |
| { |
| return; |
| } |
| |
| |
| asmlinkage void do_trap_is_kprobe(unsigned long cause, unsigned long address, |
| struct pt_regs *regs) |
| { |
| notify_die(DIE_TRAP, "kprobe_trap", regs, address, cause, SIGTRAP); |
| } |
| |
| asmlinkage void do_trap(unsigned long cause, unsigned long address, |
| struct pt_regs *regs) |
| { |
| unsigned int param = cause & 0xff; |
| |
| switch(param) |
| { |
| case 1: |
| do_trap_is_brkpt(cause, address, regs); |
| break; |
| |
| case 2: |
| do_trap_is_kprobe(param, address, regs); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| asmlinkage void do_insterror_or_kprobe(unsigned long cause, |
| unsigned long address, struct pt_regs *regs) |
| { |
| /* Check if this exception is caused by kprobes */ |
| if(notify_die(DIE_IERR, "kprobe_ierr", regs, address, |
| cause, SIGILL) == NOTIFY_STOP) |
| return; |
| |
| do_instruction_error(cause, address, regs); |
| } |
| |
| |