|  | @@ -269,3 +269,193 @@ do_entIF(unsigned long type, struct pt_regs *regs)
 | 
	
		
			
				|  |  |  		send_sig_info(SIGTRAP, &info, current);
 | 
	
		
			
				|  |  |  		return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	      case 1: /* bugcheck */
 | 
	
		
			
				|  |  | +		info.si_signo = SIGTRAP;
 | 
	
		
			
				|  |  | +		info.si_errno = 0;
 | 
	
		
			
				|  |  | +		info.si_code = __SI_FAULT;
 | 
	
		
			
				|  |  | +		info.si_addr = (void __user *) regs->pc;
 | 
	
		
			
				|  |  | +		info.si_trapno = 0;
 | 
	
		
			
				|  |  | +		send_sig_info(SIGTRAP, &info, current);
 | 
	
		
			
				|  |  | +		return;
 | 
	
		
			
				|  |  | +		
 | 
	
		
			
				|  |  | +	      case 2: /* gentrap */
 | 
	
		
			
				|  |  | +		info.si_addr = (void __user *) regs->pc;
 | 
	
		
			
				|  |  | +		info.si_trapno = regs->r16;
 | 
	
		
			
				|  |  | +		switch ((long) regs->r16) {
 | 
	
		
			
				|  |  | +		case GEN_INTOVF:
 | 
	
		
			
				|  |  | +			signo = SIGFPE;
 | 
	
		
			
				|  |  | +			code = FPE_INTOVF;
 | 
	
		
			
				|  |  | +			break;
 | 
	
		
			
				|  |  | +		case GEN_INTDIV:
 | 
	
		
			
				|  |  | +			signo = SIGFPE;
 | 
	
		
			
				|  |  | +			code = FPE_INTDIV;
 | 
	
		
			
				|  |  | +			break;
 | 
	
		
			
				|  |  | +		case GEN_FLTOVF:
 | 
	
		
			
				|  |  | +			signo = SIGFPE;
 | 
	
		
			
				|  |  | +			code = FPE_FLTOVF;
 | 
	
		
			
				|  |  | +			break;
 | 
	
		
			
				|  |  | +		case GEN_FLTDIV:
 | 
	
		
			
				|  |  | +			signo = SIGFPE;
 | 
	
		
			
				|  |  | +			code = FPE_FLTDIV;
 | 
	
		
			
				|  |  | +			break;
 | 
	
		
			
				|  |  | +		case GEN_FLTUND:
 | 
	
		
			
				|  |  | +			signo = SIGFPE;
 | 
	
		
			
				|  |  | +			code = FPE_FLTUND;
 | 
	
		
			
				|  |  | +			break;
 | 
	
		
			
				|  |  | +		case GEN_FLTINV:
 | 
	
		
			
				|  |  | +			signo = SIGFPE;
 | 
	
		
			
				|  |  | +			code = FPE_FLTINV;
 | 
	
		
			
				|  |  | +			break;
 | 
	
		
			
				|  |  | +		case GEN_FLTINE:
 | 
	
		
			
				|  |  | +			signo = SIGFPE;
 | 
	
		
			
				|  |  | +			code = FPE_FLTRES;
 | 
	
		
			
				|  |  | +			break;
 | 
	
		
			
				|  |  | +		case GEN_ROPRAND:
 | 
	
		
			
				|  |  | +			signo = SIGFPE;
 | 
	
		
			
				|  |  | +			code = __SI_FAULT;
 | 
	
		
			
				|  |  | +			break;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		case GEN_DECOVF:
 | 
	
		
			
				|  |  | +		case GEN_DECDIV:
 | 
	
		
			
				|  |  | +		case GEN_DECINV:
 | 
	
		
			
				|  |  | +		case GEN_ASSERTERR:
 | 
	
		
			
				|  |  | +		case GEN_NULPTRERR:
 | 
	
		
			
				|  |  | +		case GEN_STKOVF:
 | 
	
		
			
				|  |  | +		case GEN_STRLENERR:
 | 
	
		
			
				|  |  | +		case GEN_SUBSTRERR:
 | 
	
		
			
				|  |  | +		case GEN_RANGERR:
 | 
	
		
			
				|  |  | +		case GEN_SUBRNG:
 | 
	
		
			
				|  |  | +		case GEN_SUBRNG1:
 | 
	
		
			
				|  |  | +		case GEN_SUBRNG2:
 | 
	
		
			
				|  |  | +		case GEN_SUBRNG3:
 | 
	
		
			
				|  |  | +		case GEN_SUBRNG4:
 | 
	
		
			
				|  |  | +		case GEN_SUBRNG5:
 | 
	
		
			
				|  |  | +		case GEN_SUBRNG6:
 | 
	
		
			
				|  |  | +		case GEN_SUBRNG7:
 | 
	
		
			
				|  |  | +		default:
 | 
	
		
			
				|  |  | +			signo = SIGTRAP;
 | 
	
		
			
				|  |  | +			code = __SI_FAULT;
 | 
	
		
			
				|  |  | +			break;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		info.si_signo = signo;
 | 
	
		
			
				|  |  | +		info.si_errno = 0;
 | 
	
		
			
				|  |  | +		info.si_code = code;
 | 
	
		
			
				|  |  | +		info.si_addr = (void __user *) regs->pc;
 | 
	
		
			
				|  |  | +		send_sig_info(signo, &info, current);
 | 
	
		
			
				|  |  | +		return;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	      case 4: /* opDEC */
 | 
	
		
			
				|  |  | +		if (implver() == IMPLVER_EV4) {
 | 
	
		
			
				|  |  | +			long si_code;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			/* The some versions of SRM do not handle
 | 
	
		
			
				|  |  | +			   the opDEC properly - they return the PC of the
 | 
	
		
			
				|  |  | +			   opDEC fault, not the instruction after as the
 | 
	
		
			
				|  |  | +			   Alpha architecture requires.  Here we fix it up.
 | 
	
		
			
				|  |  | +			   We do this by intentionally causing an opDEC
 | 
	
		
			
				|  |  | +			   fault during the boot sequence and testing if
 | 
	
		
			
				|  |  | +			   we get the correct PC.  If not, we set a flag
 | 
	
		
			
				|  |  | +			   to correct it every time through.  */
 | 
	
		
			
				|  |  | +			regs->pc += opDEC_fix; 
 | 
	
		
			
				|  |  | +			
 | 
	
		
			
				|  |  | +			/* EV4 does not implement anything except normal
 | 
	
		
			
				|  |  | +			   rounding.  Everything else will come here as
 | 
	
		
			
				|  |  | +			   an illegal instruction.  Emulate them.  */
 | 
	
		
			
				|  |  | +			si_code = alpha_fp_emul(regs->pc - 4);
 | 
	
		
			
				|  |  | +			if (si_code == 0)
 | 
	
		
			
				|  |  | +				return;
 | 
	
		
			
				|  |  | +			if (si_code > 0) {
 | 
	
		
			
				|  |  | +				info.si_signo = SIGFPE;
 | 
	
		
			
				|  |  | +				info.si_errno = 0;
 | 
	
		
			
				|  |  | +				info.si_code = si_code;
 | 
	
		
			
				|  |  | +				info.si_addr = (void __user *) regs->pc;
 | 
	
		
			
				|  |  | +				send_sig_info(SIGFPE, &info, current);
 | 
	
		
			
				|  |  | +				return;
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		break;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	      case 3: /* FEN fault */
 | 
	
		
			
				|  |  | +		/* Irritating users can call PAL_clrfen to disable the
 | 
	
		
			
				|  |  | +		   FPU for the process.  The kernel will then trap in
 | 
	
		
			
				|  |  | +		   do_switch_stack and undo_switch_stack when we try
 | 
	
		
			
				|  |  | +		   to save and restore the FP registers.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		   Given that GCC by default generates code that uses the
 | 
	
		
			
				|  |  | +		   FP registers, PAL_clrfen is not useful except for DoS
 | 
	
		
			
				|  |  | +		   attacks.  So turn the bleeding FPU back on and be done
 | 
	
		
			
				|  |  | +		   with it.  */
 | 
	
		
			
				|  |  | +		current_thread_info()->pcb.flags |= 1;
 | 
	
		
			
				|  |  | +		__reload_thread(¤t_thread_info()->pcb);
 | 
	
		
			
				|  |  | +		return;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	      case 5: /* illoc */
 | 
	
		
			
				|  |  | +	      default: /* unexpected instruction-fault type */
 | 
	
		
			
				|  |  | +		      ;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	info.si_signo = SIGILL;
 | 
	
		
			
				|  |  | +	info.si_errno = 0;
 | 
	
		
			
				|  |  | +	info.si_code = ILL_ILLOPC;
 | 
	
		
			
				|  |  | +	info.si_addr = (void __user *) regs->pc;
 | 
	
		
			
				|  |  | +	send_sig_info(SIGILL, &info, current);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* There is an ifdef in the PALcode in MILO that enables a 
 | 
	
		
			
				|  |  | +   "kernel debugging entry point" as an unprivileged call_pal.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +   We don't want to have anything to do with it, but unfortunately
 | 
	
		
			
				|  |  | +   several versions of MILO included in distributions have it enabled,
 | 
	
		
			
				|  |  | +   and if we don't put something on the entry point we'll oops.  */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +asmlinkage void
 | 
	
		
			
				|  |  | +do_entDbg(struct pt_regs *regs)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	siginfo_t info;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	die_if_kernel("Instruction fault", regs, 0, NULL);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	info.si_signo = SIGILL;
 | 
	
		
			
				|  |  | +	info.si_errno = 0;
 | 
	
		
			
				|  |  | +	info.si_code = ILL_ILLOPC;
 | 
	
		
			
				|  |  | +	info.si_addr = (void __user *) regs->pc;
 | 
	
		
			
				|  |  | +	force_sig_info(SIGILL, &info, current);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + * entUna has a different register layout to be reasonably simple. It
 | 
	
		
			
				|  |  | + * needs access to all the integer registers (the kernel doesn't use
 | 
	
		
			
				|  |  | + * fp-regs), and it needs to have them in order for simpler access.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Due to the non-standard register layout (and because we don't want
 | 
	
		
			
				|  |  | + * to handle floating-point regs), user-mode unaligned accesses are
 | 
	
		
			
				|  |  | + * handled separately by do_entUnaUser below.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Oh, btw, we don't handle the "gp" register correctly, but if we fault
 | 
	
		
			
				|  |  | + * on a gp-register unaligned load/store, something is _very_ wrong
 | 
	
		
			
				|  |  | + * in the kernel anyway..
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +struct allregs {
 | 
	
		
			
				|  |  | +	unsigned long regs[32];
 | 
	
		
			
				|  |  | +	unsigned long ps, pc, gp, a0, a1, a2;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +struct unaligned_stat {
 | 
	
		
			
				|  |  | +	unsigned long count, va, pc;
 | 
	
		
			
				|  |  | +} unaligned[2];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* Macro for exception fixup code to access integer registers.  */
 | 
	
		
			
				|  |  | +#define una_reg(r)  (_regs[(r) >= 16 && (r) <= 18 ? (r)+19 : (r)])
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +asmlinkage void
 | 
	
		
			
				|  |  | +do_entUna(void * va, unsigned long opcode, unsigned long reg,
 | 
	
		
			
				|  |  | +	  struct allregs *regs)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	long error, tmp1, tmp2, tmp3, tmp4;
 | 
	
		
			
				|  |  | +	unsigned long pc = regs->pc - 4;
 | 
	
		
			
				|  |  | +	unsigned long *_regs = regs->regs;
 | 
	
		
			
				|  |  | +	const struct exception_table_entry *fixup;
 |