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