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