1 1.27 skrll /* $NetBSD: fpu.c,v 1.27 2020/04/16 05:44:43 skrll Exp $ */ 2 1.1 fredette 3 1.1 fredette /* 4 1.1 fredette * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 1.1 fredette * All rights reserved. 6 1.1 fredette * 7 1.1 fredette * This code is derived from software contributed to The NetBSD Foundation 8 1.1 fredette * by Matthew Fredette. 9 1.1 fredette * 10 1.1 fredette * Redistribution and use in source and binary forms, with or without 11 1.1 fredette * modification, are permitted provided that the following conditions 12 1.1 fredette * are met: 13 1.1 fredette * 1. Redistributions of source code must retain the above copyright 14 1.1 fredette * notice, this list of conditions and the following disclaimer. 15 1.1 fredette * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 fredette * notice, this list of conditions and the following disclaimer in the 17 1.1 fredette * documentation and/or other materials provided with the distribution. 18 1.1 fredette * 19 1.1 fredette * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 fredette * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 fredette * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 fredette * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 fredette * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 fredette * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 fredette * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 fredette * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 fredette * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 fredette * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 fredette * POSSIBILITY OF SUCH DAMAGE. 30 1.1 fredette */ 31 1.1 fredette 32 1.1 fredette /* 33 1.1 fredette * FPU handling for NetBSD/hppa. 34 1.1 fredette */ 35 1.1 fredette 36 1.1 fredette #include <sys/cdefs.h> 37 1.27 skrll __KERNEL_RCSID(0, "$NetBSD: fpu.c,v 1.27 2020/04/16 05:44:43 skrll Exp $"); 38 1.1 fredette 39 1.16 skrll #include <sys/param.h> 40 1.1 fredette #include <sys/systm.h> 41 1.1 fredette #include <sys/proc.h> 42 1.1 fredette #include <sys/signalvar.h> 43 1.1 fredette 44 1.17 skrll #include <uvm/uvm_extern.h> 45 1.17 skrll 46 1.1 fredette #include <machine/cpufunc.h> 47 1.1 fredette #include <machine/frame.h> 48 1.1 fredette #include <machine/reg.h> 49 1.22 rmind #include <machine/pcb.h> 50 1.17 skrll #include <machine/pmap.h> 51 1.1 fredette 52 1.1 fredette #include <hppa/hppa/machdep.h> 53 1.1 fredette 54 1.1 fredette #include "../spmath/float.h" 55 1.1 fredette #include "../spmath/fpudispatch.h" 56 1.1 fredette 57 1.1 fredette /* Some macros representing opcodes. */ 58 1.1 fredette #define OPCODE_NOP 0x08000240 59 1.1 fredette #define OPCODE_COPR_0_0 0x30000000 60 1.1 fredette 61 1.1 fredette /* Some macros representing fields in load/store opcodes. */ 62 1.1 fredette #define OPCODE_CMPLT_S 0x00002000 63 1.1 fredette #define OPCODE_CMPLT_M 0x00000020 64 1.1 fredette #define OPCODE_CMPLT_SM (OPCODE_CMPLT_S | OPCODE_CMPLT_M) 65 1.1 fredette #define OPCODE_CMPLT_MB OPCODE_CMPLT_M 66 1.1 fredette #define OPCODE_CMPLT_MA (OPCODE_CMPLT_S | OPCODE_CMPLT_M) 67 1.1 fredette #define OPCODE_CMPLT (OPCODE_CMPLT_S | OPCODE_CMPLT_M) 68 1.1 fredette #define OPCODE_DOUBLE 0x08000000 69 1.1 fredette #define OPCODE_STORE 0x00000200 70 1.1 fredette #define OPCODE_INDEXED 0x00001000 71 1.1 fredette 72 1.1 fredette /* This is nonzero iff we're using a hardware FPU. */ 73 1.1 fredette int fpu_present; 74 1.1 fredette 75 1.1 fredette /* If we have any FPU, this is its version. */ 76 1.1 fredette u_int fpu_version; 77 1.1 fredette 78 1.1 fredette /* The number of times we have had to switch the FPU context. */ 79 1.1 fredette u_int fpu_csw; 80 1.1 fredette 81 1.1 fredette /* In locore.S, this swaps states in and out of the FPU. */ 82 1.20 skrll void hppa_fpu_swapout(struct pcb *); 83 1.20 skrll void hppa_fpu_swap(struct fpreg *, struct fpreg *); 84 1.1 fredette 85 1.24 skrll static int hppa_fpu_ls(struct trapframe *, struct lwp *); 86 1.24 skrll 87 1.1 fredette /* 88 1.26 skrll * Given a trapframe and a general register number, the 89 1.1 fredette * FRAME_REG macro returns a pointer to that general 90 1.26 skrll * register. The _frame_reg_positions array is a lookup 91 1.26 skrll * table, since the general registers aren't in order 92 1.1 fredette * in a trapframe. 93 1.26 skrll * 94 1.26 skrll * NB: this more or less assumes that all members of 95 1.1 fredette * struct trapframe are u_ints. 96 1.1 fredette */ 97 1.1 fredette #define FRAME_REG(f, reg, r0) \ 98 1.1 fredette ((reg) == 0 ? (&r0) : ((&(f)->tf_t1) + _frame_reg_positions[reg])) 99 1.1 fredette #define _FRAME_POSITION(f) \ 100 1.1 fredette ((&((struct trapframe *) 0)->f) - (&((struct trapframe *) 0)->tf_t1)) 101 1.1 fredette const int _frame_reg_positions[32] = { 102 1.1 fredette -1, /* r0 */ 103 1.1 fredette _FRAME_POSITION(tf_r1), 104 1.1 fredette _FRAME_POSITION(tf_rp), /* r2 */ 105 1.1 fredette _FRAME_POSITION(tf_r3), 106 1.1 fredette _FRAME_POSITION(tf_r4), 107 1.1 fredette _FRAME_POSITION(tf_r5), 108 1.1 fredette _FRAME_POSITION(tf_r6), 109 1.1 fredette _FRAME_POSITION(tf_r7), 110 1.1 fredette _FRAME_POSITION(tf_r8), 111 1.1 fredette _FRAME_POSITION(tf_r9), 112 1.1 fredette _FRAME_POSITION(tf_r10), 113 1.1 fredette _FRAME_POSITION(tf_r11), 114 1.1 fredette _FRAME_POSITION(tf_r12), 115 1.1 fredette _FRAME_POSITION(tf_r13), 116 1.1 fredette _FRAME_POSITION(tf_r14), 117 1.1 fredette _FRAME_POSITION(tf_r15), 118 1.1 fredette _FRAME_POSITION(tf_r16), 119 1.1 fredette _FRAME_POSITION(tf_r17), 120 1.1 fredette _FRAME_POSITION(tf_r18), 121 1.1 fredette _FRAME_POSITION(tf_t4), /* r19 */ 122 1.1 fredette _FRAME_POSITION(tf_t3), /* r20 */ 123 1.1 fredette _FRAME_POSITION(tf_t2), /* r21 */ 124 1.1 fredette _FRAME_POSITION(tf_t1), /* r22 */ 125 1.1 fredette _FRAME_POSITION(tf_arg3), /* r23 */ 126 1.1 fredette _FRAME_POSITION(tf_arg2), /* r24 */ 127 1.1 fredette _FRAME_POSITION(tf_arg1), /* r25 */ 128 1.1 fredette _FRAME_POSITION(tf_arg0), /* r26 */ 129 1.1 fredette _FRAME_POSITION(tf_dp), /* r27 */ 130 1.1 fredette _FRAME_POSITION(tf_ret0), /* r28 */ 131 1.1 fredette _FRAME_POSITION(tf_ret1), /* r29 */ 132 1.1 fredette _FRAME_POSITION(tf_sp), /* r30 */ 133 1.1 fredette _FRAME_POSITION(tf_r31), 134 1.1 fredette }; 135 1.1 fredette 136 1.1 fredette /* 137 1.1 fredette * Bootstraps the FPU. 138 1.1 fredette */ 139 1.1 fredette void 140 1.1 fredette hppa_fpu_bootstrap(u_int ccr_enable) 141 1.1 fredette { 142 1.18 skrll uint32_t junk[2]; 143 1.18 skrll uint32_t vers[2]; 144 1.1 fredette 145 1.1 fredette /* See if we have a present and functioning hardware FPU. */ 146 1.1 fredette fpu_present = (ccr_enable & HPPA_FPUS) == HPPA_FPUS; 147 1.25 skrll if (!fpu_present) { 148 1.25 skrll fpu_csw = 0; 149 1.25 skrll curcpu()->ci_fpu_state = 0; 150 1.25 skrll 151 1.25 skrll return; 152 1.25 skrll } 153 1.1 fredette 154 1.24 skrll KASSERT(fpu_present); 155 1.1 fredette /* Initialize the FPU and get its version. */ 156 1.1 fredette 157 1.24 skrll /* 158 1.24 skrll * We track what process has the FPU, 159 1.24 skrll * and how many times we have to swap 160 1.24 skrll * in and out. 161 1.24 skrll */ 162 1.24 skrll 163 1.24 skrll /* 164 1.26 skrll * The PA-RISC 1.1 Architecture manual is 165 1.26 skrll * pretty clear that the copr,0,0 must be 166 1.26 skrll * wrapped in double word stores of fr0, 167 1.24 skrll * otherwise its operation is undefined. 168 1.24 skrll */ 169 1.24 skrll __asm volatile( 170 1.24 skrll " ldo %0, %%r22 \n" 171 1.24 skrll " fstds %%fr0, 0(%%r22) \n" 172 1.24 skrll " ldo %1, %%r22 \n" 173 1.24 skrll " copr,0,0 \n" 174 1.24 skrll " fstds %%fr0, 0(%%r22) \n" 175 1.24 skrll : "=m" (junk), "=m" (vers) : : "r22"); 176 1.24 skrll 177 1.24 skrll /* 178 1.24 skrll * Now mark that no process has the FPU, 179 1.24 skrll * and disable it, so the first time it 180 1.24 skrll * gets used the process' state gets 181 1.24 skrll * swapped in. 182 1.24 skrll */ 183 1.24 skrll fpu_csw = 0; 184 1.24 skrll curcpu()->ci_fpu_state = 0; 185 1.26 skrll mtctl(ccr_enable & (CCR_MASK ^ HPPA_FPUS), CR_CCR); 186 1.24 skrll 187 1.10 chs fpu_version = vers[0]; 188 1.1 fredette } 189 1.1 fredette 190 1.1 fredette /* 191 1.2 chs * If the given LWP has its state in the FPU, 192 1.2 chs * flush that state out into the LWP's PCB. 193 1.1 fredette */ 194 1.1 fredette void 195 1.2 chs hppa_fpu_flush(struct lwp *l) 196 1.1 fredette { 197 1.2 chs struct trapframe *tf = l->l_md.md_regs; 198 1.19 rmind struct pcb *pcb = lwp_getpcb(l); 199 1.23 skrll struct cpu_info *ci = curcpu(); 200 1.23 skrll 201 1.27 skrll if (!fpu_present) 202 1.27 skrll return; 203 1.1 fredette 204 1.1 fredette /* 205 1.24 skrll * If this process' state is currently in hardware, swap it out. 206 1.2 chs */ 207 1.26 skrll 208 1.23 skrll if (ci->ci_fpu_state == 0 || 209 1.23 skrll ci->ci_fpu_state != tf->tf_cr30) { 210 1.6 chs return; 211 1.6 chs } 212 1.6 chs 213 1.20 skrll hppa_fpu_swapout(pcb); 214 1.23 skrll ci->ci_fpu_state = 0; 215 1.1 fredette } 216 1.1 fredette 217 1.1 fredette /* 218 1.1 fredette * This emulates a coprocessor load/store instruction. 219 1.1 fredette */ 220 1.26 skrll static int 221 1.2 chs hppa_fpu_ls(struct trapframe *frame, struct lwp *l) 222 1.1 fredette { 223 1.19 rmind struct pcb *pcb = lwp_getpcb(l); 224 1.1 fredette u_int inst, inst_b, inst_x, inst_s, inst_t; 225 1.1 fredette int log2size; 226 1.1 fredette u_int *base; 227 1.1 fredette u_int offset, index, im5; 228 1.1 fredette void *fpreg; 229 1.1 fredette u_int r0 = 0; 230 1.6 chs int error; 231 1.6 chs 232 1.1 fredette /* 233 1.1 fredette * Get the instruction that we're emulating, 234 1.1 fredette * and break it down. Using HP bit notation, 235 1.26 skrll * b is a five-bit field starting at bit 10, 236 1.1 fredette * x is a five-bit field starting at bit 15, 237 1.26 skrll * s is a two-bit field starting at bit 17, 238 1.7 chs * and t is a five-bit field starting at bit 31. 239 1.1 fredette */ 240 1.1 fredette inst = frame->tf_iir; 241 1.12 perry __asm volatile( 242 1.1 fredette " extru %4, 10, 5, %1 \n" 243 1.1 fredette " extru %4, 15, 5, %2 \n" 244 1.1 fredette " extru %4, 17, 2, %3 \n" 245 1.1 fredette " extru %4, 31, 5, %4 \n" 246 1.1 fredette : "=r" (inst_b), "=r" (inst_x), "=r" (inst_s), "=r" (inst_t) 247 1.1 fredette : "r" (inst)); 248 1.1 fredette 249 1.1 fredette /* 250 1.1 fredette * The space must be the user's space, else we 251 1.1 fredette * segfault. 252 1.1 fredette */ 253 1.19 rmind if (inst_s != pcb->pcb_space) 254 1.21 skrll return EFAULT; 255 1.1 fredette 256 1.1 fredette /* See whether or not this is a doubleword load/store. */ 257 1.1 fredette log2size = (inst & OPCODE_DOUBLE) ? 3 : 2; 258 1.1 fredette 259 1.1 fredette /* Get the floating point register. */ 260 1.19 rmind fpreg = ((char *)pcb->pcb_fpregs) + (inst_t << log2size); 261 1.1 fredette 262 1.1 fredette /* Get the base register. */ 263 1.1 fredette base = FRAME_REG(frame, inst_b, r0); 264 1.26 skrll 265 1.1 fredette /* Dispatch on whether or not this is an indexed load/store. */ 266 1.1 fredette if (inst & OPCODE_INDEXED) { 267 1.26 skrll 268 1.1 fredette /* Get the index register value. */ 269 1.1 fredette index = *FRAME_REG(frame, inst_x, r0); 270 1.26 skrll 271 1.1 fredette /* Dispatch on the completer. */ 272 1.1 fredette switch (inst & OPCODE_CMPLT) { 273 1.1 fredette case OPCODE_CMPLT_S: 274 1.1 fredette offset = *base + (index << log2size); 275 1.1 fredette break; 276 1.1 fredette case OPCODE_CMPLT_M: 277 1.1 fredette offset = *base; 278 1.1 fredette *base = *base + index; 279 1.1 fredette break; 280 1.1 fredette case OPCODE_CMPLT_SM: 281 1.1 fredette offset = *base; 282 1.1 fredette *base = *base + (index << log2size); 283 1.1 fredette break; 284 1.1 fredette default: 285 1.1 fredette offset = *base + index; 286 1.1 fredette break; 287 1.1 fredette } 288 1.1 fredette } else { 289 1.1 fredette 290 1.1 fredette /* Do a low_sign_ext(x, 5). */ 291 1.1 fredette im5 = inst_x >> 1; 292 1.1 fredette if (inst_x & 1) 293 1.1 fredette im5 |= 0xfffffff0; 294 1.1 fredette 295 1.1 fredette /* Dispatch on the completer. */ 296 1.1 fredette switch (inst & OPCODE_CMPLT) { 297 1.1 fredette case OPCODE_CMPLT_MB: 298 1.1 fredette offset = *base + im5; 299 1.1 fredette *base = *base + im5; 300 1.1 fredette break; 301 1.1 fredette case OPCODE_CMPLT_MA: 302 1.1 fredette offset = *base; 303 1.1 fredette *base = *base + im5; 304 1.1 fredette break; 305 1.1 fredette default: 306 1.1 fredette offset = *base + im5; 307 1.1 fredette break; 308 1.1 fredette } 309 1.1 fredette } 310 1.1 fredette 311 1.1 fredette /* 312 1.1 fredette * The offset we calculated must be the same as the 313 1.1 fredette * offset in the IOR. 314 1.1 fredette */ 315 1.1 fredette KASSERT(offset == frame->tf_ior); 316 1.1 fredette 317 1.1 fredette /* Perform the load or store. */ 318 1.6 chs error = (inst & OPCODE_STORE) ? 319 1.1 fredette copyout(fpreg, (void *) offset, 1 << log2size) : 320 1.1 fredette copyin((const void *) offset, fpreg, 1 << log2size); 321 1.6 chs return error; 322 1.1 fredette } 323 1.1 fredette 324 1.1 fredette /* 325 1.1 fredette * This is called to emulate an instruction. 326 1.1 fredette */ 327 1.26 skrll void 328 1.7 chs hppa_fpu_emulate(struct trapframe *frame, struct lwp *l, u_int inst) 329 1.1 fredette { 330 1.19 rmind struct pcb *pcb = lwp_getpcb(l); 331 1.7 chs u_int opcode, class, sub; 332 1.1 fredette u_int *fpregs; 333 1.1 fredette int exception; 334 1.8 chs ksiginfo_t ksi; 335 1.1 fredette 336 1.1 fredette /* 337 1.26 skrll * If the process' state is in any hardware FPU, 338 1.1 fredette * flush it out - we need to operate on it. 339 1.1 fredette */ 340 1.2 chs hppa_fpu_flush(l); 341 1.1 fredette 342 1.1 fredette /* 343 1.1 fredette * Get the instruction that we're emulating, 344 1.1 fredette * and break it down. Using HP bit notation, 345 1.1 fredette * the class is a two-bit field starting at 346 1.1 fredette * bit 22, the opcode is a 6-bit field starting 347 1.1 fredette * at bit 5, and sub for a class 1 instruction 348 1.1 fredette * is a two bit field starting at bit 16, else 349 1.1 fredette * it is a three bit field starting at bit 18. 350 1.1 fredette */ 351 1.7 chs #if 0 352 1.12 perry __asm volatile( 353 1.1 fredette " extru %3, 22, 2, %1 \n" 354 1.1 fredette " extru %3, 5, 6, %0 \n" 355 1.1 fredette " extru %3, 18, 3, %2 \n" 356 1.1 fredette " comib,<> 1, %1, 0 \n" 357 1.1 fredette " extru %3, 16, 2, %2 \n" 358 1.1 fredette : "=r" (opcode), "=r" (class), "=r" (sub) 359 1.1 fredette : "r" (inst)); 360 1.7 chs #else 361 1.7 chs opcode = (inst >> (31 - 5)) & 0x3f; 362 1.7 chs class = (inst >> (31 - 22)) & 0x3; 363 1.7 chs if (class == 1) { 364 1.7 chs sub = (inst >> (31 - 16)) & 3; 365 1.7 chs } else { 366 1.7 chs sub = (inst >> (31 - 18)) & 7; 367 1.7 chs } 368 1.7 chs #endif 369 1.1 fredette 370 1.2 chs /* Get this LWP's FPU registers. */ 371 1.19 rmind fpregs = (u_int *)pcb->pcb_fpregs; 372 1.1 fredette 373 1.1 fredette /* Dispatch on the opcode. */ 374 1.1 fredette switch (opcode) { 375 1.1 fredette case 0x09: 376 1.1 fredette case 0x0b: 377 1.8 chs if (hppa_fpu_ls(frame, l) != 0) { 378 1.8 chs KSI_INIT_TRAP(&ksi); 379 1.8 chs ksi.ksi_signo = SIGSEGV; 380 1.8 chs ksi.ksi_code = SEGV_MAPERR; 381 1.8 chs ksi.ksi_trap = T_DTLBMISS; 382 1.8 chs ksi.ksi_addr = (void *)frame->tf_iioq_head; 383 1.8 chs trapsignal(l, &ksi); 384 1.8 chs } 385 1.1 fredette return; 386 1.1 fredette case 0x0c: 387 1.1 fredette exception = decode_0c(inst, class, sub, fpregs); 388 1.1 fredette break; 389 1.1 fredette case 0x0e: 390 1.1 fredette exception = decode_0e(inst, class, sub, fpregs); 391 1.1 fredette break; 392 1.1 fredette case 0x06: 393 1.1 fredette exception = decode_06(inst, fpregs); 394 1.1 fredette break; 395 1.26 skrll case 0x26: 396 1.1 fredette exception = decode_26(inst, fpregs); 397 1.1 fredette break; 398 1.26 skrll default: 399 1.1 fredette exception = UNIMPLEMENTEDEXCEPTION; 400 1.1 fredette break; 401 1.1 fredette } 402 1.1 fredette 403 1.8 chs if (exception) { 404 1.8 chs KSI_INIT_TRAP(&ksi); 405 1.8 chs if (exception & UNIMPLEMENTEDEXCEPTION) { 406 1.8 chs ksi.ksi_signo = SIGILL; 407 1.8 chs ksi.ksi_code = ILL_COPROC; 408 1.8 chs } else { 409 1.8 chs ksi.ksi_signo = SIGFPE; 410 1.8 chs if (exception & INVALIDEXCEPTION) { 411 1.8 chs ksi.ksi_code = FPE_FLTINV; 412 1.8 chs } else if (exception & DIVISIONBYZEROEXCEPTION) { 413 1.8 chs ksi.ksi_code = FPE_FLTDIV; 414 1.8 chs } else if (exception & OVERFLOWEXCEPTION) { 415 1.8 chs ksi.ksi_code = FPE_FLTOVF; 416 1.8 chs } else if (exception & UNDERFLOWEXCEPTION) { 417 1.8 chs ksi.ksi_code = FPE_FLTUND; 418 1.8 chs } else if (exception & INEXACTEXCEPTION) { 419 1.8 chs ksi.ksi_code = FPE_FLTRES; 420 1.8 chs } 421 1.8 chs } 422 1.8 chs ksi.ksi_trap = T_EMULATION; 423 1.8 chs ksi.ksi_addr = (void *)frame->tf_iioq_head; 424 1.8 chs trapsignal(l, &ksi); 425 1.8 chs } 426 1.1 fredette } 427