Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

MIPS: Loongson-3: Add some unaligned instructions emulation

1, Add unaligned gslq, gssq, gslqc1, gssqc1 emulation;
2, Add unaligned gsl{h, w, d}x, gss{h, w, d}x emulation;
3, Add unaligned gslwxc1, gsswxc1, gsldxc1, gssdxc1 emulation.

Signed-off-by: Huacai Chen <chenhc@lemote.com>
Signed-off-by: Pei Huang <huangpei@loongson.cn>
Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>

authored by

Huacai Chen and committed by
Thomas Bogendoerfer
f83e4f98 d339cd02

+314 -1
+26
arch/mips/include/uapi/asm/inst.h
··· 989 989 }; 990 990 991 991 /* 992 + * Loongson-3 overridden COP2 instruction formats (32-bit length) 993 + */ 994 + struct loongson3_lswc2_format { /* Loongson-3 overridden lwc2/swc2 Load/Store format */ 995 + __BITFIELD_FIELD(unsigned int opcode : 6, 996 + __BITFIELD_FIELD(unsigned int base : 5, 997 + __BITFIELD_FIELD(unsigned int rt : 5, 998 + __BITFIELD_FIELD(unsigned int fr : 1, 999 + __BITFIELD_FIELD(unsigned int offset : 9, 1000 + __BITFIELD_FIELD(unsigned int ls : 1, 1001 + __BITFIELD_FIELD(unsigned int rq : 5, 1002 + ;))))))) 1003 + }; 1004 + 1005 + struct loongson3_lsdc2_format { /* Loongson-3 overridden ldc2/sdc2 Load/Store format */ 1006 + __BITFIELD_FIELD(unsigned int opcode : 6, 1007 + __BITFIELD_FIELD(unsigned int base : 5, 1008 + __BITFIELD_FIELD(unsigned int rt : 5, 1009 + __BITFIELD_FIELD(unsigned int index : 5, 1010 + __BITFIELD_FIELD(unsigned int offset : 8, 1011 + __BITFIELD_FIELD(unsigned int opcode1 : 3, 1012 + ;)))))) 1013 + }; 1014 + 1015 + /* 992 1016 * MIPS16e instruction formats (16-bit length) 993 1017 */ 994 1018 struct m16e_rr { ··· 1112 1088 struct mm16_rb_format mm16_rb_format; 1113 1089 struct mm16_r3_format mm16_r3_format; 1114 1090 struct mm16_r5_format mm16_r5_format; 1091 + struct loongson3_lswc2_format loongson3_lswc2_format; 1092 + struct loongson3_lsdc2_format loongson3_lsdc2_format; 1115 1093 }; 1116 1094 1117 1095 union mips16e_instruction {
+288 -1
arch/mips/loongson64/cop2-ex.c
··· 14 14 #include <linux/sched.h> 15 15 #include <linux/notifier.h> 16 16 #include <linux/ptrace.h> 17 + #include <linux/uaccess.h> 18 + #include <linux/sched/signal.h> 17 19 18 20 #include <asm/fpu.h> 19 21 #include <asm/cop2.h> 22 + #include <asm/inst.h> 23 + #include <asm/branch.h> 20 24 #include <asm/current.h> 21 25 #include <asm/mipsregs.h> 22 26 23 27 static int loongson_cu2_call(struct notifier_block *nfb, unsigned long action, 24 28 void *data) 25 29 { 26 - int fpu_owned; 30 + unsigned int res, fpu_owned; 31 + unsigned long ra, value, value_next; 32 + union mips_instruction insn; 27 33 int fr = !test_thread_flag(TIF_32BIT_FPREGS); 34 + struct pt_regs *regs = (struct pt_regs *)data; 35 + void __user *addr = (void __user *)regs->cp0_badvaddr; 36 + unsigned int __user *pc = (unsigned int __user *)exception_epc(regs); 37 + 38 + ra = regs->regs[31]; 39 + __get_user(insn.word, pc); 28 40 29 41 switch (action) { 30 42 case CU2_EXCEPTION: ··· 61 49 preempt_enable(); 62 50 63 51 return NOTIFY_STOP; /* Don't call default notifier */ 52 + 53 + case CU2_LWC2_OP: 54 + if (insn.loongson3_lswc2_format.ls == 0) 55 + goto sigbus; 56 + 57 + if (insn.loongson3_lswc2_format.fr == 0) { /* gslq */ 58 + if (!access_ok(addr, 16)) 59 + goto sigbus; 60 + 61 + LoadDW(addr, value, res); 62 + if (res) 63 + goto fault; 64 + 65 + LoadDW(addr + 8, value_next, res); 66 + if (res) 67 + goto fault; 68 + 69 + regs->regs[insn.loongson3_lswc2_format.rt] = value; 70 + regs->regs[insn.loongson3_lswc2_format.rq] = value_next; 71 + compute_return_epc(regs); 72 + } else { /* gslqc1 */ 73 + if (!access_ok(addr, 16)) 74 + goto sigbus; 75 + 76 + lose_fpu(1); 77 + LoadDW(addr, value, res); 78 + if (res) 79 + goto fault; 80 + 81 + LoadDW(addr + 8, value_next, res); 82 + if (res) 83 + goto fault; 84 + 85 + set_fpr64(current->thread.fpu.fpr, 86 + insn.loongson3_lswc2_format.rt, value); 87 + set_fpr64(current->thread.fpu.fpr, 88 + insn.loongson3_lswc2_format.rq, value_next); 89 + compute_return_epc(regs); 90 + own_fpu(1); 91 + } 92 + return NOTIFY_STOP; /* Don't call default notifier */ 93 + 94 + case CU2_SWC2_OP: 95 + if (insn.loongson3_lswc2_format.ls == 0) 96 + goto sigbus; 97 + 98 + if (insn.loongson3_lswc2_format.fr == 0) { /* gssq */ 99 + if (!access_ok(addr, 16)) 100 + goto sigbus; 101 + 102 + /* write upper 8 bytes first */ 103 + value_next = regs->regs[insn.loongson3_lswc2_format.rq]; 104 + 105 + StoreDW(addr + 8, value_next, res); 106 + if (res) 107 + goto fault; 108 + value = regs->regs[insn.loongson3_lswc2_format.rt]; 109 + 110 + StoreDW(addr, value, res); 111 + if (res) 112 + goto fault; 113 + 114 + compute_return_epc(regs); 115 + } else { /* gssqc1 */ 116 + if (!access_ok(addr, 16)) 117 + goto sigbus; 118 + 119 + lose_fpu(1); 120 + value_next = get_fpr64(current->thread.fpu.fpr, 121 + insn.loongson3_lswc2_format.rq); 122 + 123 + StoreDW(addr + 8, value_next, res); 124 + if (res) 125 + goto fault; 126 + 127 + value = get_fpr64(current->thread.fpu.fpr, 128 + insn.loongson3_lswc2_format.rt); 129 + 130 + StoreDW(addr, value, res); 131 + if (res) 132 + goto fault; 133 + 134 + compute_return_epc(regs); 135 + own_fpu(1); 136 + } 137 + return NOTIFY_STOP; /* Don't call default notifier */ 138 + 139 + case CU2_LDC2_OP: 140 + switch (insn.loongson3_lsdc2_format.opcode1) { 141 + /* 142 + * Loongson-3 overridden ldc2 instructions. 143 + * opcode1 instruction 144 + * 0x1 gslhx: load 2 bytes to GPR 145 + * 0x2 gslwx: load 4 bytes to GPR 146 + * 0x3 gsldx: load 8 bytes to GPR 147 + * 0x6 gslwxc1: load 4 bytes to FPR 148 + * 0x7 gsldxc1: load 8 bytes to FPR 149 + */ 150 + case 0x1: 151 + if (!access_ok(addr, 2)) 152 + goto sigbus; 153 + 154 + LoadHW(addr, value, res); 155 + if (res) 156 + goto fault; 157 + 158 + compute_return_epc(regs); 159 + regs->regs[insn.loongson3_lsdc2_format.rt] = value; 160 + break; 161 + case 0x2: 162 + if (!access_ok(addr, 4)) 163 + goto sigbus; 164 + 165 + LoadW(addr, value, res); 166 + if (res) 167 + goto fault; 168 + 169 + compute_return_epc(regs); 170 + regs->regs[insn.loongson3_lsdc2_format.rt] = value; 171 + break; 172 + case 0x3: 173 + if (!access_ok(addr, 8)) 174 + goto sigbus; 175 + 176 + LoadDW(addr, value, res); 177 + if (res) 178 + goto fault; 179 + 180 + compute_return_epc(regs); 181 + regs->regs[insn.loongson3_lsdc2_format.rt] = value; 182 + break; 183 + case 0x6: 184 + die_if_kernel("Unaligned FP access in kernel code", regs); 185 + BUG_ON(!used_math()); 186 + if (!access_ok(addr, 4)) 187 + goto sigbus; 188 + 189 + lose_fpu(1); 190 + LoadW(addr, value, res); 191 + if (res) 192 + goto fault; 193 + 194 + set_fpr64(current->thread.fpu.fpr, 195 + insn.loongson3_lsdc2_format.rt, value); 196 + compute_return_epc(regs); 197 + own_fpu(1); 198 + 199 + break; 200 + case 0x7: 201 + die_if_kernel("Unaligned FP access in kernel code", regs); 202 + BUG_ON(!used_math()); 203 + if (!access_ok(addr, 8)) 204 + goto sigbus; 205 + 206 + lose_fpu(1); 207 + LoadDW(addr, value, res); 208 + if (res) 209 + goto fault; 210 + 211 + set_fpr64(current->thread.fpu.fpr, 212 + insn.loongson3_lsdc2_format.rt, value); 213 + compute_return_epc(regs); 214 + own_fpu(1); 215 + break; 216 + 217 + } 218 + return NOTIFY_STOP; /* Don't call default notifier */ 219 + 220 + case CU2_SDC2_OP: 221 + switch (insn.loongson3_lsdc2_format.opcode1) { 222 + /* 223 + * Loongson-3 overridden sdc2 instructions. 224 + * opcode1 instruction 225 + * 0x1 gsshx: store 2 bytes from GPR 226 + * 0x2 gsswx: store 4 bytes from GPR 227 + * 0x3 gssdx: store 8 bytes from GPR 228 + * 0x6 gsswxc1: store 4 bytes from FPR 229 + * 0x7 gssdxc1: store 8 bytes from FPR 230 + */ 231 + case 0x1: 232 + if (!access_ok(addr, 2)) 233 + goto sigbus; 234 + 235 + compute_return_epc(regs); 236 + value = regs->regs[insn.loongson3_lsdc2_format.rt]; 237 + 238 + StoreHW(addr, value, res); 239 + if (res) 240 + goto fault; 241 + 242 + break; 243 + case 0x2: 244 + if (!access_ok(addr, 4)) 245 + goto sigbus; 246 + 247 + compute_return_epc(regs); 248 + value = regs->regs[insn.loongson3_lsdc2_format.rt]; 249 + 250 + StoreW(addr, value, res); 251 + if (res) 252 + goto fault; 253 + 254 + break; 255 + case 0x3: 256 + if (!access_ok(addr, 8)) 257 + goto sigbus; 258 + 259 + compute_return_epc(regs); 260 + value = regs->regs[insn.loongson3_lsdc2_format.rt]; 261 + 262 + StoreDW(addr, value, res); 263 + if (res) 264 + goto fault; 265 + 266 + break; 267 + 268 + case 0x6: 269 + die_if_kernel("Unaligned FP access in kernel code", regs); 270 + BUG_ON(!used_math()); 271 + 272 + if (!access_ok(addr, 4)) 273 + goto sigbus; 274 + 275 + lose_fpu(1); 276 + value = get_fpr64(current->thread.fpu.fpr, 277 + insn.loongson3_lsdc2_format.rt); 278 + 279 + StoreW(addr, value, res); 280 + if (res) 281 + goto fault; 282 + 283 + compute_return_epc(regs); 284 + own_fpu(1); 285 + 286 + break; 287 + case 0x7: 288 + die_if_kernel("Unaligned FP access in kernel code", regs); 289 + BUG_ON(!used_math()); 290 + 291 + if (!access_ok(addr, 8)) 292 + goto sigbus; 293 + 294 + lose_fpu(1); 295 + value = get_fpr64(current->thread.fpu.fpr, 296 + insn.loongson3_lsdc2_format.rt); 297 + 298 + StoreDW(addr, value, res); 299 + if (res) 300 + goto fault; 301 + 302 + compute_return_epc(regs); 303 + own_fpu(1); 304 + 305 + break; 306 + } 307 + return NOTIFY_STOP; /* Don't call default notifier */ 64 308 } 65 309 66 310 return NOTIFY_OK; /* Let default notifier send signals */ 311 + 312 + fault: 313 + /* roll back jump/branch */ 314 + regs->regs[31] = ra; 315 + regs->cp0_epc = (unsigned long)pc; 316 + /* Did we have an exception handler installed? */ 317 + if (fixup_exception(regs)) 318 + return NOTIFY_STOP; /* Don't call default notifier */ 319 + 320 + die_if_kernel("Unhandled kernel unaligned access", regs); 321 + force_sig(SIGSEGV); 322 + 323 + return NOTIFY_STOP; /* Don't call default notifier */ 324 + 325 + sigbus: 326 + die_if_kernel("Unhandled kernel unaligned access", regs); 327 + force_sig(SIGBUS); 328 + 329 + return NOTIFY_STOP; /* Don't call default notifier */ 67 330 } 68 331 69 332 static int __init loongson_cu2_setup(void)