File: /usr/src/linux/arch/sparc/kernel/muldiv.c

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