1 /* 1 2 * This file is subject to the terms and condi 3 * License. See the file "COPYING" in the mai 4 * for more details. 5 * 6 * Copyright (C) 2014 Lemote Corporation. 7 * written by Huacai Chen <chenhc@lemote.com 8 * 9 * based on arch/mips/cavium-octeon/cpu.c 10 * Copyright (C) 2009 Wind River Systems, 11 * written by Ralf Baechle <ralf@linux-mips. 12 */ 13 #include <linux/init.h> 14 #include <linux/sched.h> 15 #include <linux/notifier.h> 16 #include <linux/ptrace.h> 17 #include <linux/uaccess.h> 18 #include <linux/sched/signal.h> 19 20 #include <asm/fpu.h> 21 #include <asm/cop2.h> 22 #include <asm/inst.h> 23 #include <asm/branch.h> 24 #include <asm/current.h> 25 #include <asm/mipsregs.h> 26 #include <asm/unaligned-emul.h> 27 28 static int loongson_cu2_call(struct notifier_b 29 void *data) 30 { 31 unsigned int res, fpu_owned; 32 unsigned long ra, value, value_next; 33 union mips_instruction insn; 34 int fr = !test_thread_flag(TIF_32BIT_F 35 struct pt_regs *regs = (struct pt_regs 36 void __user *addr = (void __user *)reg 37 unsigned int __user *pc = (unsigned in 38 39 ra = regs->regs[31]; 40 __get_user(insn.word, pc); 41 42 switch (action) { 43 case CU2_EXCEPTION: 44 preempt_disable(); 45 fpu_owned = __is_fpu_owner(); 46 if (!fr) 47 set_c0_status(ST0_CU1 48 else 49 set_c0_status(ST0_CU1 50 enable_fpu_hazard(); 51 KSTK_STATUS(current) |= (ST0_C 52 if (fr) 53 KSTK_STATUS(current) | 54 else 55 KSTK_STATUS(current) & 56 /* If FPU is owned, we needn't 57 if (!fpu_owned) { 58 set_thread_flag(TIF_US 59 init_fp_ctx(current); 60 _restore_fp(current); 61 } 62 preempt_enable(); 63 64 return NOTIFY_STOP; /* Don 65 66 case CU2_LWC2_OP: 67 if (insn.loongson3_lswc2_forma 68 goto sigbus; 69 70 if (insn.loongson3_lswc2_forma 71 if (!access_ok(addr, 1 72 goto sigbus; 73 74 LoadDW(addr, value, re 75 if (res) 76 goto fault; 77 78 LoadDW(addr + 8, value 79 if (res) 80 goto fault; 81 82 regs->regs[insn.loongs 83 regs->regs[insn.loongs 84 compute_return_epc(reg 85 } else { 86 if (!access_ok(addr, 1 87 goto sigbus; 88 89 lose_fpu(1); 90 LoadDW(addr, value, re 91 if (res) 92 goto fault; 93 94 LoadDW(addr + 8, value 95 if (res) 96 goto fault; 97 98 set_fpr64(¤t->th 99 set_fpr64(¤t->th 100 compute_return_epc(reg 101 own_fpu(1); 102 } 103 return NOTIFY_STOP; /* Don 104 105 case CU2_SWC2_OP: 106 if (insn.loongson3_lswc2_forma 107 goto sigbus; 108 109 if (insn.loongson3_lswc2_forma 110 if (!access_ok(addr, 1 111 goto sigbus; 112 113 /* write upper 8 bytes 114 value_next = regs->reg 115 116 StoreDW(addr + 8, valu 117 if (res) 118 goto fault; 119 value = regs->regs[ins 120 121 StoreDW(addr, value, r 122 if (res) 123 goto fault; 124 125 compute_return_epc(reg 126 } else { 127 if (!access_ok(addr, 1 128 goto sigbus; 129 130 lose_fpu(1); 131 value_next = get_fpr64 132 133 StoreDW(addr + 8, valu 134 if (res) 135 goto fault; 136 137 value = get_fpr64(&cur 138 139 StoreDW(addr, value, r 140 if (res) 141 goto fault; 142 143 compute_return_epc(reg 144 own_fpu(1); 145 } 146 return NOTIFY_STOP; /* Don 147 148 case CU2_LDC2_OP: 149 switch (insn.loongson3_lsdc2_f 150 /* 151 * Loongson-3 overridden ldc2 152 * opcode1 instru 153 * 0x1 gslhx: load 154 * 0x2 gslwx: load 155 * 0x3 gsldx: load 156 * 0x6 gslwxc1: loa 157 * 0x7 gsldxc1: loa 158 */ 159 case 0x1: 160 if (!access_ok(addr, 2 161 goto sigbus; 162 163 LoadHW(addr, value, re 164 if (res) 165 goto fault; 166 167 compute_return_epc(reg 168 regs->regs[insn.loongs 169 break; 170 case 0x2: 171 if (!access_ok(addr, 4 172 goto sigbus; 173 174 LoadW(addr, value, res 175 if (res) 176 goto fault; 177 178 compute_return_epc(reg 179 regs->regs[insn.loongs 180 break; 181 case 0x3: 182 if (!access_ok(addr, 8 183 goto sigbus; 184 185 LoadDW(addr, value, re 186 if (res) 187 goto fault; 188 189 compute_return_epc(reg 190 regs->regs[insn.loongs 191 break; 192 case 0x6: 193 die_if_kernel("Unalign 194 BUG_ON(!used_math()); 195 if (!access_ok(addr, 4 196 goto sigbus; 197 198 lose_fpu(1); 199 LoadW(addr, value, res 200 if (res) 201 goto fault; 202 203 set_fpr64(¤t->th 204 compute_return_epc(reg 205 own_fpu(1); 206 207 break; 208 case 0x7: 209 die_if_kernel("Unalign 210 BUG_ON(!used_math()); 211 if (!access_ok(addr, 8 212 goto sigbus; 213 214 lose_fpu(1); 215 LoadDW(addr, value, re 216 if (res) 217 goto fault; 218 219 set_fpr64(¤t->th 220 compute_return_epc(reg 221 own_fpu(1); 222 break; 223 224 } 225 return NOTIFY_STOP; /* Don 226 227 case CU2_SDC2_OP: 228 switch (insn.loongson3_lsdc2_f 229 /* 230 * Loongson-3 overridden sdc2 231 * opcode1 instru 232 * 0x1 gsshx: store 233 * 0x2 gsswx: store 234 * 0x3 gssdx: store 235 * 0x6 gsswxc1: sto 236 * 0x7 gssdxc1: sto 237 */ 238 case 0x1: 239 if (!access_ok(addr, 2 240 goto sigbus; 241 242 compute_return_epc(reg 243 value = regs->regs[ins 244 245 StoreHW(addr, value, r 246 if (res) 247 goto fault; 248 249 break; 250 case 0x2: 251 if (!access_ok(addr, 4 252 goto sigbus; 253 254 compute_return_epc(reg 255 value = regs->regs[ins 256 257 StoreW(addr, value, re 258 if (res) 259 goto fault; 260 261 break; 262 case 0x3: 263 if (!access_ok(addr, 8 264 goto sigbus; 265 266 compute_return_epc(reg 267 value = regs->regs[ins 268 269 StoreDW(addr, value, r 270 if (res) 271 goto fault; 272 273 break; 274 275 case 0x6: 276 die_if_kernel("Unalign 277 BUG_ON(!used_math()); 278 279 if (!access_ok(addr, 4 280 goto sigbus; 281 282 lose_fpu(1); 283 value = get_fpr64(&cur 284 285 StoreW(addr, value, re 286 if (res) 287 goto fault; 288 289 compute_return_epc(reg 290 own_fpu(1); 291 292 break; 293 case 0x7: 294 die_if_kernel("Unalign 295 BUG_ON(!used_math()); 296 297 if (!access_ok(addr, 8 298 goto sigbus; 299 300 lose_fpu(1); 301 value = get_fpr64(&cur 302 303 StoreDW(addr, value, r 304 if (res) 305 goto fault; 306 307 compute_return_epc(reg 308 own_fpu(1); 309 310 break; 311 } 312 return NOTIFY_STOP; /* Don 313 } 314 315 return NOTIFY_OK; /* Let 316 317 fault: 318 /* roll back jump/branch */ 319 regs->regs[31] = ra; 320 regs->cp0_epc = (unsigned long)pc; 321 /* Did we have an exception handler in 322 if (fixup_exception(regs)) 323 return NOTIFY_STOP; /* Don 324 325 die_if_kernel("Unhandled kernel unalig 326 force_sig(SIGSEGV); 327 328 return NOTIFY_STOP; /* Don't call 329 330 sigbus: 331 die_if_kernel("Unhandled kernel unalig 332 force_sig(SIGBUS); 333 334 return NOTIFY_STOP; /* Don't call 335 } 336 337 static int __init loongson_cu2_setup(void) 338 { 339 return cu2_notifier(loongson_cu2_call, 340 } 341 early_initcall(loongson_cu2_setup); 342
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.