diff options
-rw-r--r-- | sys/alpha/alpha/db_instruction.h | 19 | ||||
-rw-r--r-- | sys/alpha/alpha/trap.c | 170 |
2 files changed, 187 insertions, 2 deletions
diff --git a/sys/alpha/alpha/db_instruction.h b/sys/alpha/alpha/db_instruction.h index 172b651..19133d2 100644 --- a/sys/alpha/alpha/db_instruction.h +++ b/sys/alpha/alpha/db_instruction.h @@ -62,6 +62,15 @@ typedef union { unsigned int bits; /* + * Generic instruction pseudo format; look at + * opcode to see how to interpret the rest. + */ + struct { + unsigned bits : 26, + opcode : 6; + } generic_format; + + /* * Memory instructions contain a 16 bit * signed immediate value and two register * specifiers @@ -111,6 +120,16 @@ typedef union { * specifier. Bit 12 sez which is which. */ struct { + unsigned rc : 5, + function : 7, + is_lit : 1, + sbz_or_litlo : 3, + rb_or_lithi : 5, + ra : 5, + opcode : 6; + } operate_generic_format; + + struct { unsigned rd : 5, function : 7, sbz : 4, diff --git a/sys/alpha/alpha/trap.c b/sys/alpha/alpha/trap.c index 0941a9f..9800090 100644 --- a/sys/alpha/alpha/trap.c +++ b/sys/alpha/alpha/trap.c @@ -69,6 +69,7 @@ #ifdef DDB #include <ddb/ddb.h> #endif +#include <alpha/alpha/db_instruction.h> /* for handle_opdec() */ unsigned long Sfloat_to_reg __P((unsigned int)); unsigned int reg_to_Sfloat __P((unsigned long)); @@ -81,6 +82,7 @@ unsigned long Gfloat_reg_cvt __P((unsigned long)); int unaligned_fixup __P((unsigned long, unsigned long, unsigned long, struct proc *)); +int handle_opdec(struct proc *p, u_int64_t *ucodep); static void printtrap __P((const unsigned long, const unsigned long, const unsigned long, const unsigned long, struct trapframe *, int, int)); @@ -346,8 +348,9 @@ trap(a0, a1, a2, entry, framep) break; case ALPHA_IF_CODE_OPDEC: - ucode = a0; /* trap type */ - i = SIGILL; + i = handle_opdec(p, &ucode); + if (i == 0) + goto out; break; case ALPHA_IF_CODE_FEN: @@ -1141,3 +1144,166 @@ unaligned_fixup(va, opcode, reg, p) out: return (signal); } + + +/* + * Reserved/unimplemented instruction (opDec fault) handler + * + * Argument is the process that caused it. No useful information + * is passed to the trap handler other than the fault type. The + * address of the instruction that caused the fault is 4 less than + * the PC stored in the trap frame. + * + * If the instruction is emulated successfully, this function returns 0. + * Otherwise, this function returns the signal to deliver to the process, + * and fills in *ucodep with the code to be delivered. + */ +int +handle_opdec(p, ucodep) + struct proc *p; + u_int64_t *ucodep; +{ + alpha_instruction inst; + register_t *regptr, memaddr; + u_int64_t inst_pc; + int sig; + + /* + * Read USP into frame in case it's going to be used or modified. + * This keeps us from having to check for it in lots of places + * later. + */ + p->p_md.md_tf->tf_regs[FRAME_SP] = alpha_pal_rdusp(); + + inst_pc = memaddr = p->p_md.md_tf->tf_regs[FRAME_PC] - 4; + if (copyin((caddr_t)inst_pc, &inst, sizeof (inst)) != 0) { + /* + * really, this should never happen, but in case it + * does we handle it. + */ + printf("WARNING: handle_opdec() couldn't fetch instruction\n"); + goto sigsegv; + } + + switch (inst.generic_format.opcode) { + case op_ldbu: + case op_ldwu: + case op_stw: + case op_stb: + regptr = irp(p, inst.mem_format.rs); + if (regptr != NULL) + memaddr = *regptr; + else + memaddr = 0; + memaddr += inst.mem_format.displacement; + + regptr = irp(p, inst.mem_format.rd); + + if (inst.mem_format.opcode == op_ldwu || + inst.mem_format.opcode == op_stw) { + if (memaddr & 0x01) { + sig = unaligned_fixup(memaddr, + inst.mem_format.opcode, + inst.mem_format.rd, p); + if (sig) + goto unaligned_fixup_sig; + break; + } + } + + if (inst.mem_format.opcode == op_ldbu) { + u_int8_t b; + + /* XXX ONLY WORKS ON LITTLE-ENDIAN ALPHA */ + if (copyin((caddr_t)memaddr, &b, sizeof (b)) != 0) + goto sigsegv; + if (regptr != NULL) + *regptr = b; + } else if (inst.mem_format.opcode == op_ldwu) { + u_int16_t w; + + /* XXX ONLY WORKS ON LITTLE-ENDIAN ALPHA */ + if (copyin((caddr_t)memaddr, &w, sizeof (w)) != 0) + goto sigsegv; + if (regptr != NULL) + *regptr = w; + } else if (inst.mem_format.opcode == op_stw) { + u_int16_t w; + + /* XXX ONLY WORKS ON LITTLE-ENDIAN ALPHA */ + w = (regptr != NULL) ? *regptr : 0; + if (copyout(&w, (caddr_t)memaddr, sizeof (w)) != 0) + goto sigsegv; + } else if (inst.mem_format.opcode == op_stb) { + u_int8_t b; + + /* XXX ONLY WORKS ON LITTLE-ENDIAN ALPHA */ + b = (regptr != NULL) ? *regptr : 0; + if (copyout(&b, (caddr_t)memaddr, sizeof (b)) != 0) + goto sigsegv; + } + break; + + case op_intmisc: + if (inst.operate_generic_format.function == op_sextb && + inst.operate_generic_format.ra == 31) { + int8_t b; + + if (inst.operate_generic_format.is_lit) { + b = inst.operate_lit_format.literal; + } else { + if (inst.operate_reg_format.sbz != 0) + goto sigill; + regptr = irp(p, inst.operate_reg_format.rt); + b = (regptr != NULL) ? *regptr : 0; + } + + regptr = irp(p, inst.operate_generic_format.rc); + if (regptr != NULL) + *regptr = b; + break; + } + if (inst.operate_generic_format.function == op_sextw && + inst.operate_generic_format.ra == 31) { + int16_t w; + + if (inst.operate_generic_format.is_lit) { + w = inst.operate_lit_format.literal; + } else { + if (inst.operate_reg_format.sbz != 0) + goto sigill; + regptr = irp(p, inst.operate_reg_format.rt); + w = (regptr != NULL) ? *regptr : 0; + } + + regptr = irp(p, inst.operate_generic_format.rc); + if (regptr != NULL) + *regptr = w; + break; + } + goto sigill; + + default: + goto sigill; + } + + /* + * Write back USP. Note that in the error cases below, + * nothing will have been successfully modified so we don't + * have to write it out. + */ + alpha_pal_wrusp(p->p_md.md_tf->tf_regs[FRAME_SP]); + + return (0); + +sigill: + *ucodep = ALPHA_IF_CODE_OPDEC; /* trap type */ + return (SIGILL); + +sigsegv: + sig = SIGSEGV; + p->p_md.md_tf->tf_regs[FRAME_PC] = inst_pc; /* re-run instr. */ +unaligned_fixup_sig: + *ucodep = memaddr; /* faulting address */ + return (sig); +} |