ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / 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 }