174ba9207SThomas Gleixner/* SPDX-License-Identifier: GPL-2.0-or-later */ 21da177e4SLinus Torvalds/* 31da177e4SLinus Torvalds NetWinder Floating Point Emulator 41da177e4SLinus Torvalds (c) Rebel.COM, 1998 51da177e4SLinus Torvalds (c) 1998, 1999 Philip Blundell 61da177e4SLinus Torvalds 71da177e4SLinus Torvalds Direct questions, comments to Scott Bambrough <scottb@netwinder.org> 81da177e4SLinus Torvalds 91da177e4SLinus Torvalds*/ 10*47ba5f39SArd Biesheuvel#include <linux/linkage.h> 116ebbf2ceSRussell King#include <asm/assembler.h> 12e7f626dbSLeif Lindholm#include <asm/opcodes.h> 13e7f626dbSLeif Lindholm 141da177e4SLinus Torvalds/* This is the kernel's entry point into the floating point emulator. 151da177e4SLinus TorvaldsIt is called from the kernel with code similar to this: 161da177e4SLinus Torvalds 171da177e4SLinus Torvalds sub r4, r5, #4 181da177e4SLinus Torvalds ldrt r0, [r4] @ r0 = instruction 191da177e4SLinus Torvalds adrsvc al, r9, ret_from_exception @ r9 = normal FP return 201da177e4SLinus Torvalds adrsvc al, lr, fpundefinstr @ lr = undefined instr return 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds get_current_task r10 231da177e4SLinus Torvalds mov r8, #1 241da177e4SLinus Torvalds strb r8, [r10, #TSK_USED_MATH] @ set current->used_math 251da177e4SLinus Torvalds add r10, r10, #TSS_FPESAVE @ r10 = workspace 261da177e4SLinus Torvalds ldr r4, .LC2 271da177e4SLinus Torvalds ldr pc, [r4] @ Call FP emulator entry point 281da177e4SLinus Torvalds 291da177e4SLinus TorvaldsThe kernel expects the emulator to return via one of two possible 301da177e4SLinus Torvaldspoints of return it passes to the emulator. The emulator, if 311da177e4SLinus Torvaldssuccessful in its emulation, jumps to ret_from_exception (passed in 321da177e4SLinus Torvaldsr9) and the kernel takes care of returning control from the trap to 331da177e4SLinus Torvaldsthe user code. If the emulator is unable to emulate the instruction, 341da177e4SLinus Torvaldsit returns via _fpundefinstr (passed via lr) and the kernel halts the 351da177e4SLinus Torvaldsuser program with a core dump. 361da177e4SLinus Torvalds 371da177e4SLinus TorvaldsOn entry to the emulator r10 points to an area of private FP workspace 381da177e4SLinus Torvaldsreserved in the thread structure for this process. This is where the 391da177e4SLinus Torvaldsemulator saves its registers across calls. The first word of this area 401da177e4SLinus Torvaldsis used as a flag to detect the first time a process uses floating point, 411da177e4SLinus Torvaldsso that the emulator startup cost can be avoided for tasks that don't 421da177e4SLinus Torvaldswant it. 431da177e4SLinus Torvalds 441da177e4SLinus TorvaldsThis routine does three things: 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds1) The kernel has created a struct pt_regs on the stack and saved the 471da177e4SLinus Torvaldsuser registers into it. See /usr/include/asm/proc/ptrace.h for details. 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds2) It calls EmulateAll to emulate a floating point instruction. 501da177e4SLinus TorvaldsEmulateAll returns 1 if the emulation was successful, or 0 if not. 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds3) If an instruction has been emulated successfully, it looks ahead at 531da177e4SLinus Torvaldsthe next instruction. If it is a floating point instruction, it 541da177e4SLinus Torvaldsexecutes the instruction, without returning to user space. In this 551da177e4SLinus Torvaldsway it repeatedly looks ahead and executes floating point instructions 561da177e4SLinus Torvaldsuntil it encounters a non floating point instruction, at which time it 571da177e4SLinus Torvaldsreturns via _fpreturn. 581da177e4SLinus Torvalds 591da177e4SLinus TorvaldsThis is done to reduce the effect of the trap overhead on each 601da177e4SLinus Torvaldsfloating point instructions. GCC attempts to group floating point 611da177e4SLinus Torvaldsinstructions to allow the emulator to spread the cost of the trap over 621da177e4SLinus Torvaldsseveral floating point instructions. */ 631da177e4SLinus Torvalds 64c1f438f5SCatalin Marinas#include <asm/asm-offsets.h> 65c1f438f5SCatalin Marinas 661da177e4SLinus Torvalds .globl nwfpe_enter 671da177e4SLinus Torvaldsnwfpe_enter: 681da177e4SLinus Torvalds mov r4, lr @ save the failure-return addresses 691da177e4SLinus Torvalds mov sl, sp @ we access the registers via 'sl' 701da177e4SLinus Torvalds 71c1f438f5SCatalin Marinas ldr r5, [sp, #S_PC] @ get contents of PC; 72c1f438f5SCatalin Marinas mov r6, r0 @ save the opcode 731da177e4SLinus Torvaldsemulate: 74c1f438f5SCatalin Marinas ldr r1, [sp, #S_PSR] @ fetch the PSR 75e7f626dbSLeif Lindholm bl arm_check_condition @ check the condition 76e7f626dbSLeif Lindholm cmp r0, #ARM_OPCODE_CONDTEST_PASS @ condition passed? 77c1f438f5SCatalin Marinas 78c1f438f5SCatalin Marinas @ if condition code failed to match, next insn 79e7f626dbSLeif Lindholm bne next @ get the next instruction; 80c1f438f5SCatalin Marinas 81c1f438f5SCatalin Marinas mov r0, r6 @ prepare for EmulateAll() 821da177e4SLinus Torvalds bl EmulateAll @ emulate the instruction 831da177e4SLinus Torvalds cmp r0, #0 @ was emulation successful 846ebbf2ceSRussell King reteq r4 @ no, return failure 851da177e4SLinus Torvalds 861da177e4SLinus Torvaldsnext: 8739dc53deSRussell King uaccess_enable r3 881da177e4SLinus Torvalds.Lx1: ldrt r6, [r5], #4 @ get the next instruction and 891da177e4SLinus Torvalds @ increment PC 9039dc53deSRussell King uaccess_disable r3 911da177e4SLinus Torvalds and r2, r6, #0x0F000000 @ test for FP insns 921da177e4SLinus Torvalds teq r2, #0x0C000000 931da177e4SLinus Torvalds teqne r2, #0x0D000000 941da177e4SLinus Torvalds teqne r2, #0x0E000000 956ebbf2ceSRussell King retne r9 @ return ok if not a fp insn 961da177e4SLinus Torvalds 97c1f438f5SCatalin Marinas str r5, [sp, #S_PC] @ update PC copy in regs 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds mov r0, r6 @ save a copy 100c1f438f5SCatalin Marinas b emulate @ check condition and emulate 1011da177e4SLinus Torvalds 1021da177e4SLinus Torvalds @ We need to be prepared for the instructions at .Lx1 and .Lx2 1031da177e4SLinus Torvalds @ to fault. Emit the appropriate exception gunk to fix things up. 1041da177e4SLinus Torvalds @ ??? For some reason, faults can happen at .Lx2 even with a 1051da177e4SLinus Torvalds @ plain LDR instruction. Weird, but it seems harmless. 106c4a84ae3SArd Biesheuvel .pushsection .text.fixup,"ax" 1071da177e4SLinus Torvalds .align 2 108*47ba5f39SArd Biesheuvel.Lrep: str r4, [sp, #S_PC] @ retry current instruction 1096ebbf2ceSRussell King.Lfix: ret r9 @ let the user eat segfaults 1104260415fSRussell King .popsection 1111da177e4SLinus Torvalds 1124260415fSRussell King .pushsection __ex_table,"a" 1131da177e4SLinus Torvalds .align 3 1141da177e4SLinus Torvalds .long .Lx1, .Lfix 1154260415fSRussell King .popsection 116*47ba5f39SArd Biesheuvel 117*47ba5f39SArd Biesheuvel @ 118*47ba5f39SArd Biesheuvel @ Check whether the instruction is a co-processor instruction. 119*47ba5f39SArd Biesheuvel @ If yes, we need to call the relevant co-processor handler. 120*47ba5f39SArd Biesheuvel @ Only FPE instructions are dispatched here, everything else 121*47ba5f39SArd Biesheuvel @ is handled by undef hooks. 122*47ba5f39SArd Biesheuvel @ 123*47ba5f39SArd Biesheuvel @ Emulators may wish to make use of the following registers: 124*47ba5f39SArd Biesheuvel @ r4 = PC value to resume execution after successful emulation 125*47ba5f39SArd Biesheuvel @ r9 = normal "successful" return address 126*47ba5f39SArd Biesheuvel @ lr = unrecognised instruction return address 127*47ba5f39SArd Biesheuvel @ IRQs enabled, FIQs enabled. 128*47ba5f39SArd Biesheuvel @ 129*47ba5f39SArd BiesheuvelENTRY(call_fpe) 130*47ba5f39SArd Biesheuvel mov r2, r4 131*47ba5f39SArd Biesheuvel sub r4, r4, #4 @ ARM instruction at user PC - 4 132*47ba5f39SArd BiesheuvelUSERL( .Lrep, ldrt r0, [r4]) @ load opcode from user space 133*47ba5f39SArd BiesheuvelARM_BE8(rev r0, r0) @ little endian instruction 134*47ba5f39SArd Biesheuvel 135*47ba5f39SArd Biesheuvel uaccess_disable ip 136*47ba5f39SArd Biesheuvel 137*47ba5f39SArd Biesheuvel get_thread_info r10 @ get current thread 138*47ba5f39SArd Biesheuvel tst r0, #0x08000000 @ only CDP/CPRT/LDC/STC have bit 27 139*47ba5f39SArd Biesheuvel reteq lr 140*47ba5f39SArd Biesheuvel and r8, r0, #0x00000f00 @ mask out CP number 141*47ba5f39SArd Biesheuvel#ifdef CONFIG_IWMMXT 142*47ba5f39SArd Biesheuvel @ Test if we need to give access to iWMMXt coprocessors 143*47ba5f39SArd Biesheuvel ldr r5, [r10, #TI_FLAGS] 144*47ba5f39SArd Biesheuvel rsbs r7, r8, #(1 << 8) @ CP 0 or 1 only 145*47ba5f39SArd Biesheuvel movscs r7, r5, lsr #(TIF_USING_IWMMXT + 1) 146*47ba5f39SArd Biesheuvel movcs r0, sp @ pass struct pt_regs 147*47ba5f39SArd Biesheuvel bcs iwmmxt_task_enable 148*47ba5f39SArd Biesheuvel#endif 149*47ba5f39SArd Biesheuvel add pc, pc, r8, lsr #6 150*47ba5f39SArd Biesheuvel nop 151*47ba5f39SArd Biesheuvel 152*47ba5f39SArd Biesheuvel ret lr @ CP#0 153*47ba5f39SArd Biesheuvel b do_fpe @ CP#1 (FPE) 154*47ba5f39SArd Biesheuvel b do_fpe @ CP#2 (FPE) 155*47ba5f39SArd Biesheuvel ret lr @ CP#3 156*47ba5f39SArd Biesheuvel ret lr @ CP#4 157*47ba5f39SArd Biesheuvel ret lr @ CP#5 158*47ba5f39SArd Biesheuvel ret lr @ CP#6 159*47ba5f39SArd Biesheuvel ret lr @ CP#7 160*47ba5f39SArd Biesheuvel ret lr @ CP#8 161*47ba5f39SArd Biesheuvel ret lr @ CP#9 162*47ba5f39SArd Biesheuvel ret lr @ CP#10 (VFP) 163*47ba5f39SArd Biesheuvel ret lr @ CP#11 (VFP) 164*47ba5f39SArd Biesheuvel ret lr @ CP#12 165*47ba5f39SArd Biesheuvel ret lr @ CP#13 166*47ba5f39SArd Biesheuvel ret lr @ CP#14 (Debug) 167*47ba5f39SArd Biesheuvel ret lr @ CP#15 (Control) 168*47ba5f39SArd Biesheuvel 169*47ba5f39SArd Biesheuveldo_fpe: 170*47ba5f39SArd Biesheuvel add r10, r10, #TI_FPSTATE @ r10 = workspace 171*47ba5f39SArd Biesheuvel ldr_va pc, fp_enter, tmp=r4 @ Call FP module USR entry point 172*47ba5f39SArd Biesheuvel 173*47ba5f39SArd Biesheuvel @ 174*47ba5f39SArd Biesheuvel @ The FP module is called with these registers set: 175*47ba5f39SArd Biesheuvel @ r0 = instruction 176*47ba5f39SArd Biesheuvel @ r2 = PC+4 177*47ba5f39SArd Biesheuvel @ r9 = normal "successful" return address 178*47ba5f39SArd Biesheuvel @ r10 = FP workspace 179*47ba5f39SArd Biesheuvel @ lr = unrecognised FP instruction return address 180*47ba5f39SArd Biesheuvel @ 181*47ba5f39SArd Biesheuvel 182*47ba5f39SArd Biesheuvel .pushsection .data 183*47ba5f39SArd Biesheuvel .align 2 184*47ba5f39SArd BiesheuvelENTRY(fp_enter) 185*47ba5f39SArd Biesheuvel .word no_fp 186*47ba5f39SArd Biesheuvel .popsection 187*47ba5f39SArd Biesheuvel 188*47ba5f39SArd Biesheuvelno_fp: 189*47ba5f39SArd Biesheuvel ret lr 190*47ba5f39SArd BiesheuvelENDPROC(no_fp) 191