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