1 /*
2  * muldiv.c: Hardware multiply/division illegal instruction trap
3  *		for sun4c/sun4 (which do not have those instructions)
4  *
5  * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
6  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
7  *
8  * 2004-12-25	Krzysztof Helt (krzysztof.h1@wp.pl)
9  *		- fixed registers constrains in inline assembly declarations
10  */
11 
12 #include <linux/kernel.h>
13 #include <linux/sched.h>
14 #include <linux/mm.h>
15 #include <asm/ptrace.h>
16 #include <asm/processor.h>
17 #include <asm/uaccess.h>
18 
19 #include "kernel.h"
20 
21 /* #define DEBUG_MULDIV */
22 
has_imm13(int insn)23 static inline int has_imm13(int insn)
24 {
25 	return (insn & 0x2000);
26 }
27 
is_foocc(int insn)28 static inline int is_foocc(int insn)
29 {
30 	return (insn & 0x800000);
31 }
32 
sign_extend_imm13(int imm)33 static inline int sign_extend_imm13(int imm)
34 {
35 	return imm << 19 >> 19;
36 }
37 
advance(struct pt_regs * regs)38 static inline void advance(struct pt_regs *regs)
39 {
40 	regs->pc   = regs->npc;
41 	regs->npc += 4;
42 }
43 
maybe_flush_windows(unsigned int rs1,unsigned int rs2,unsigned int rd)44 static inline void maybe_flush_windows(unsigned int rs1, unsigned int rs2,
45 				       unsigned int rd)
46 {
47 	if(rs2 >= 16 || rs1 >= 16 || rd >= 16) {
48 		/* Wheee... */
49 		__asm__ __volatile__("save %sp, -0x40, %sp\n\t"
50 				     "save %sp, -0x40, %sp\n\t"
51 				     "save %sp, -0x40, %sp\n\t"
52 				     "save %sp, -0x40, %sp\n\t"
53 				     "save %sp, -0x40, %sp\n\t"
54 				     "save %sp, -0x40, %sp\n\t"
55 				     "save %sp, -0x40, %sp\n\t"
56 				     "restore; restore; restore; restore;\n\t"
57 				     "restore; restore; restore;\n\t");
58 	}
59 }
60 
61 #define fetch_reg(reg, regs) ({						\
62 	struct reg_window32 __user *win;					\
63 	register unsigned long ret;					\
64 									\
65 	if (!(reg)) ret = 0;						\
66 	else if ((reg) < 16) {						\
67 		ret = regs->u_regs[(reg)];				\
68 	} else {							\
69 		/* Ho hum, the slightly complicated case. */		\
70 		win = (struct reg_window32 __user *)regs->u_regs[UREG_FP];\
71 		if (get_user (ret, &win->locals[(reg) - 16])) return -1;\
72 	}								\
73 	ret;								\
74 })
75 
76 static inline int
store_reg(unsigned int result,unsigned int reg,struct pt_regs * regs)77 store_reg(unsigned int result, unsigned int reg, struct pt_regs *regs)
78 {
79 	struct reg_window32 __user *win;
80 
81 	if (!reg)
82 		return 0;
83 	if (reg < 16) {
84 		regs->u_regs[reg] = result;
85 		return 0;
86 	} else {
87 		/* need to use put_user() in this case: */
88 		win = (struct reg_window32 __user *) regs->u_regs[UREG_FP];
89 		return (put_user(result, &win->locals[reg - 16]));
90 	}
91 }
92 
93 /* Should return 0 if mul/div emulation succeeded and SIGILL should
94  * not be issued.
95  */
do_user_muldiv(struct pt_regs * regs,unsigned long pc)96 int do_user_muldiv(struct pt_regs *regs, unsigned long pc)
97 {
98 	unsigned int insn;
99 	int inst;
100 	unsigned int rs1, rs2, rdv;
101 
102 	if (!pc)
103 		return -1; /* This happens to often, I think */
104 	if (get_user (insn, (unsigned int __user *)pc))
105 		return -1;
106 	if ((insn & 0xc1400000) != 0x80400000)
107 		return -1;
108 	inst = ((insn >> 19) & 0xf);
109 	if ((inst & 0xe) != 10 && (inst & 0xe) != 14)
110 		return -1;
111 
112 	/* Now we know we have to do something with umul, smul, udiv or sdiv */
113 	rs1 = (insn >> 14) & 0x1f;
114 	rs2 = insn & 0x1f;
115 	rdv = (insn >> 25) & 0x1f;
116 	if (has_imm13(insn)) {
117 		maybe_flush_windows(rs1, 0, rdv);
118 		rs2 = sign_extend_imm13(insn);
119 	} else {
120 		maybe_flush_windows(rs1, rs2, rdv);
121 		rs2 = fetch_reg(rs2, regs);
122 	}
123 	rs1 = fetch_reg(rs1, regs);
124 	switch (inst) {
125 	case 10: /* umul */
126 #ifdef DEBUG_MULDIV
127 		printk ("unsigned muldiv: 0x%x * 0x%x = ", rs1, rs2);
128 #endif
129 		__asm__ __volatile__ ("\n\t"
130 			"mov	%0, %%o0\n\t"
131 			"call	.umul\n\t"
132 			" mov	%1, %%o1\n\t"
133 			"mov	%%o0, %0\n\t"
134 			"mov	%%o1, %1\n\t"
135 			: "=r" (rs1), "=r" (rs2)
136 		        : "0" (rs1), "1" (rs2)
137 			: "o0", "o1", "o2", "o3", "o4", "o5", "o7", "cc");
138 #ifdef DEBUG_MULDIV
139 		printk ("0x%x%08x\n", rs2, rs1);
140 #endif
141 		if (store_reg(rs1, rdv, regs))
142 			return -1;
143 		regs->y = rs2;
144 		break;
145 	case 11: /* smul */
146 #ifdef DEBUG_MULDIV
147 		printk ("signed muldiv: 0x%x * 0x%x = ", rs1, rs2);
148 #endif
149 		__asm__ __volatile__ ("\n\t"
150 			"mov	%0, %%o0\n\t"
151 			"call	.mul\n\t"
152 			" mov	%1, %%o1\n\t"
153 			"mov	%%o0, %0\n\t"
154 			"mov	%%o1, %1\n\t"
155 			: "=r" (rs1), "=r" (rs2)
156 		        : "0" (rs1), "1" (rs2)
157 			: "o0", "o1", "o2", "o3", "o4", "o5", "o7", "cc");
158 #ifdef DEBUG_MULDIV
159 		printk ("0x%x%08x\n", rs2, rs1);
160 #endif
161 		if (store_reg(rs1, rdv, regs))
162 			return -1;
163 		regs->y = rs2;
164 		break;
165 	case 14: /* udiv */
166 #ifdef DEBUG_MULDIV
167 		printk ("unsigned muldiv: 0x%x%08x / 0x%x = ", regs->y, rs1, rs2);
168 #endif
169 		if (!rs2) {
170 #ifdef DEBUG_MULDIV
171 			printk ("DIVISION BY ZERO\n");
172 #endif
173 			handle_hw_divzero (regs, pc, regs->npc, regs->psr);
174 			return 0;
175 		}
176 		__asm__ __volatile__ ("\n\t"
177 			"mov	%2, %%o0\n\t"
178 			"mov	%0, %%o1\n\t"
179 			"mov	%%g0, %%o2\n\t"
180 			"call	__udivdi3\n\t"
181 			" mov	%1, %%o3\n\t"
182 			"mov	%%o1, %0\n\t"
183 			"mov	%%o0, %1\n\t"
184 			: "=r" (rs1), "=r" (rs2)
185 			: "r" (regs->y), "0" (rs1), "1" (rs2)
186 			: "o0", "o1", "o2", "o3", "o4", "o5", "o7",
187 			  "g1", "g2", "g3", "cc");
188 #ifdef DEBUG_MULDIV
189 		printk ("0x%x\n", rs1);
190 #endif
191 		if (store_reg(rs1, rdv, regs))
192 			return -1;
193 		break;
194 	case 15: /* sdiv */
195 #ifdef DEBUG_MULDIV
196 		printk ("signed muldiv: 0x%x%08x / 0x%x = ", regs->y, rs1, rs2);
197 #endif
198 		if (!rs2) {
199 #ifdef DEBUG_MULDIV
200 			printk ("DIVISION BY ZERO\n");
201 #endif
202 			handle_hw_divzero (regs, pc, regs->npc, regs->psr);
203 			return 0;
204 		}
205 		__asm__ __volatile__ ("\n\t"
206 			"mov	%2, %%o0\n\t"
207 			"mov	%0, %%o1\n\t"
208 			"mov	%%g0, %%o2\n\t"
209 			"call	__divdi3\n\t"
210 			" mov	%1, %%o3\n\t"
211 			"mov	%%o1, %0\n\t"
212 			"mov	%%o0, %1\n\t"
213 			: "=r" (rs1), "=r" (rs2)
214 			: "r" (regs->y), "0" (rs1), "1" (rs2)
215 			: "o0", "o1", "o2", "o3", "o4", "o5", "o7",
216 			  "g1", "g2", "g3", "cc");
217 #ifdef DEBUG_MULDIV
218 		printk ("0x%x\n", rs1);
219 #endif
220 		if (store_reg(rs1, rdv, regs))
221 			return -1;
222 		break;
223 	}
224 	if (is_foocc (insn)) {
225 		regs->psr &= ~PSR_ICC;
226 		if ((inst & 0xe) == 14) {
227 			/* ?div */
228 			if (rs2) regs->psr |= PSR_V;
229 		}
230 		if (!rs1) regs->psr |= PSR_Z;
231 		if (((int)rs1) < 0) regs->psr |= PSR_N;
232 #ifdef DEBUG_MULDIV
233 		printk ("psr muldiv: %08x\n", regs->psr);
234 #endif
235 	}
236 	advance(regs);
237 	return 0;
238 }
239