162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-or-later */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci NetWinder Floating Point Emulator 462306a36Sopenharmony_ci (c) Rebel.COM, 1998 562306a36Sopenharmony_ci (c) 1998, 1999 Philip Blundell 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci Direct questions, comments to Scott Bambrough <scottb@netwinder.org> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci*/ 1062306a36Sopenharmony_ci#include <linux/linkage.h> 1162306a36Sopenharmony_ci#include <asm/assembler.h> 1262306a36Sopenharmony_ci#include <asm/opcodes.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* This is the kernel's entry point into the floating point emulator. 1562306a36Sopenharmony_ciIt is called from the kernel with code similar to this: 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci sub r4, r5, #4 1862306a36Sopenharmony_ci ldrt r0, [r4] @ r0 = instruction 1962306a36Sopenharmony_ci adrsvc al, r9, ret_from_exception @ r9 = normal FP return 2062306a36Sopenharmony_ci adrsvc al, lr, fpundefinstr @ lr = undefined instr return 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci get_current_task r10 2362306a36Sopenharmony_ci mov r8, #1 2462306a36Sopenharmony_ci strb r8, [r10, #TSK_USED_MATH] @ set current->used_math 2562306a36Sopenharmony_ci add r10, r10, #TSS_FPESAVE @ r10 = workspace 2662306a36Sopenharmony_ci ldr r4, .LC2 2762306a36Sopenharmony_ci ldr pc, [r4] @ Call FP emulator entry point 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ciThe kernel expects the emulator to return via one of two possible 3062306a36Sopenharmony_cipoints of return it passes to the emulator. The emulator, if 3162306a36Sopenharmony_cisuccessful in its emulation, jumps to ret_from_exception (passed in 3262306a36Sopenharmony_cir9) and the kernel takes care of returning control from the trap to 3362306a36Sopenharmony_cithe user code. If the emulator is unable to emulate the instruction, 3462306a36Sopenharmony_ciit returns via _fpundefinstr (passed via lr) and the kernel halts the 3562306a36Sopenharmony_ciuser program with a core dump. 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ciOn entry to the emulator r10 points to an area of private FP workspace 3862306a36Sopenharmony_cireserved in the thread structure for this process. This is where the 3962306a36Sopenharmony_ciemulator saves its registers across calls. The first word of this area 4062306a36Sopenharmony_ciis used as a flag to detect the first time a process uses floating point, 4162306a36Sopenharmony_ciso that the emulator startup cost can be avoided for tasks that don't 4262306a36Sopenharmony_ciwant it. 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ciThis routine does three things: 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci1) The kernel has created a struct pt_regs on the stack and saved the 4762306a36Sopenharmony_ciuser registers into it. See /usr/include/asm/proc/ptrace.h for details. 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci2) It calls EmulateAll to emulate a floating point instruction. 5062306a36Sopenharmony_ciEmulateAll returns 1 if the emulation was successful, or 0 if not. 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci3) If an instruction has been emulated successfully, it looks ahead at 5362306a36Sopenharmony_cithe next instruction. If it is a floating point instruction, it 5462306a36Sopenharmony_ciexecutes the instruction, without returning to user space. In this 5562306a36Sopenharmony_ciway it repeatedly looks ahead and executes floating point instructions 5662306a36Sopenharmony_ciuntil it encounters a non floating point instruction, at which time it 5762306a36Sopenharmony_cireturns via _fpreturn. 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ciThis is done to reduce the effect of the trap overhead on each 6062306a36Sopenharmony_cifloating point instructions. GCC attempts to group floating point 6162306a36Sopenharmony_ciinstructions to allow the emulator to spread the cost of the trap over 6262306a36Sopenharmony_ciseveral floating point instructions. */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#include <asm/asm-offsets.h> 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci .globl nwfpe_enter 6762306a36Sopenharmony_cinwfpe_enter: 6862306a36Sopenharmony_ci mov r4, lr @ save the failure-return addresses 6962306a36Sopenharmony_ci mov sl, sp @ we access the registers via 'sl' 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci ldr r5, [sp, #S_PC] @ get contents of PC; 7262306a36Sopenharmony_ci mov r6, r0 @ save the opcode 7362306a36Sopenharmony_ciemulate: 7462306a36Sopenharmony_ci ldr r1, [sp, #S_PSR] @ fetch the PSR 7562306a36Sopenharmony_ci bl arm_check_condition @ check the condition 7662306a36Sopenharmony_ci cmp r0, #ARM_OPCODE_CONDTEST_PASS @ condition passed? 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci @ if condition code failed to match, next insn 7962306a36Sopenharmony_ci bne next @ get the next instruction; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci mov r0, r6 @ prepare for EmulateAll() 8262306a36Sopenharmony_ci bl EmulateAll @ emulate the instruction 8362306a36Sopenharmony_ci cmp r0, #0 @ was emulation successful 8462306a36Sopenharmony_ci reteq r4 @ no, return failure 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cinext: 8762306a36Sopenharmony_ci uaccess_enable r3 8862306a36Sopenharmony_ci.Lx1: ldrt r6, [r5], #4 @ get the next instruction and 8962306a36Sopenharmony_ci @ increment PC 9062306a36Sopenharmony_ci uaccess_disable r3 9162306a36Sopenharmony_ci and r2, r6, #0x0F000000 @ test for FP insns 9262306a36Sopenharmony_ci teq r2, #0x0C000000 9362306a36Sopenharmony_ci teqne r2, #0x0D000000 9462306a36Sopenharmony_ci teqne r2, #0x0E000000 9562306a36Sopenharmony_ci retne r9 @ return ok if not a fp insn 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci str r5, [sp, #S_PC] @ update PC copy in regs 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci mov r0, r6 @ save a copy 10062306a36Sopenharmony_ci b emulate @ check condition and emulate 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci @ We need to be prepared for the instructions at .Lx1 and .Lx2 10362306a36Sopenharmony_ci @ to fault. Emit the appropriate exception gunk to fix things up. 10462306a36Sopenharmony_ci @ ??? For some reason, faults can happen at .Lx2 even with a 10562306a36Sopenharmony_ci @ plain LDR instruction. Weird, but it seems harmless. 10662306a36Sopenharmony_ci .pushsection .text.fixup,"ax" 10762306a36Sopenharmony_ci .align 2 10862306a36Sopenharmony_ci.Lrep: str r4, [sp, #S_PC] @ retry current instruction 10962306a36Sopenharmony_ci.Lfix: ret r9 @ let the user eat segfaults 11062306a36Sopenharmony_ci .popsection 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci .pushsection __ex_table,"a" 11362306a36Sopenharmony_ci .align 3 11462306a36Sopenharmony_ci .long .Lx1, .Lfix 11562306a36Sopenharmony_ci .popsection 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci @ 11862306a36Sopenharmony_ci @ Check whether the instruction is a co-processor instruction. 11962306a36Sopenharmony_ci @ If yes, we need to call the relevant co-processor handler. 12062306a36Sopenharmony_ci @ Only FPE instructions are dispatched here, everything else 12162306a36Sopenharmony_ci @ is handled by undef hooks. 12262306a36Sopenharmony_ci @ 12362306a36Sopenharmony_ci @ Emulators may wish to make use of the following registers: 12462306a36Sopenharmony_ci @ r4 = PC value to resume execution after successful emulation 12562306a36Sopenharmony_ci @ r9 = normal "successful" return address 12662306a36Sopenharmony_ci @ lr = unrecognised instruction return address 12762306a36Sopenharmony_ci @ IRQs enabled, FIQs enabled. 12862306a36Sopenharmony_ci @ 12962306a36Sopenharmony_ciENTRY(call_fpe) 13062306a36Sopenharmony_ci mov r2, r4 13162306a36Sopenharmony_ci sub r4, r4, #4 @ ARM instruction at user PC - 4 13262306a36Sopenharmony_ciUSERL( .Lrep, ldrt r0, [r4]) @ load opcode from user space 13362306a36Sopenharmony_ciARM_BE8(rev r0, r0) @ little endian instruction 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci uaccess_disable ip 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci get_thread_info r10 @ get current thread 13862306a36Sopenharmony_ci tst r0, #0x08000000 @ only CDP/CPRT/LDC/STC have bit 27 13962306a36Sopenharmony_ci reteq lr 14062306a36Sopenharmony_ci and r8, r0, #0x00000f00 @ mask out CP number 14162306a36Sopenharmony_ci#ifdef CONFIG_IWMMXT 14262306a36Sopenharmony_ci @ Test if we need to give access to iWMMXt coprocessors 14362306a36Sopenharmony_ci ldr r5, [r10, #TI_FLAGS] 14462306a36Sopenharmony_ci rsbs r7, r8, #(1 << 8) @ CP 0 or 1 only 14562306a36Sopenharmony_ci movscs r7, r5, lsr #(TIF_USING_IWMMXT + 1) 14662306a36Sopenharmony_ci movcs r0, sp @ pass struct pt_regs 14762306a36Sopenharmony_ci bcs iwmmxt_task_enable 14862306a36Sopenharmony_ci#endif 14962306a36Sopenharmony_ci add pc, pc, r8, lsr #6 15062306a36Sopenharmony_ci nop 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci ret lr @ CP#0 15362306a36Sopenharmony_ci b do_fpe @ CP#1 (FPE) 15462306a36Sopenharmony_ci b do_fpe @ CP#2 (FPE) 15562306a36Sopenharmony_ci ret lr @ CP#3 15662306a36Sopenharmony_ci ret lr @ CP#4 15762306a36Sopenharmony_ci ret lr @ CP#5 15862306a36Sopenharmony_ci ret lr @ CP#6 15962306a36Sopenharmony_ci ret lr @ CP#7 16062306a36Sopenharmony_ci ret lr @ CP#8 16162306a36Sopenharmony_ci ret lr @ CP#9 16262306a36Sopenharmony_ci ret lr @ CP#10 (VFP) 16362306a36Sopenharmony_ci ret lr @ CP#11 (VFP) 16462306a36Sopenharmony_ci ret lr @ CP#12 16562306a36Sopenharmony_ci ret lr @ CP#13 16662306a36Sopenharmony_ci ret lr @ CP#14 (Debug) 16762306a36Sopenharmony_ci ret lr @ CP#15 (Control) 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cido_fpe: 17062306a36Sopenharmony_ci add r10, r10, #TI_FPSTATE @ r10 = workspace 17162306a36Sopenharmony_ci ldr_va pc, fp_enter, tmp=r4 @ Call FP module USR entry point 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci @ 17462306a36Sopenharmony_ci @ The FP module is called with these registers set: 17562306a36Sopenharmony_ci @ r0 = instruction 17662306a36Sopenharmony_ci @ r2 = PC+4 17762306a36Sopenharmony_ci @ r9 = normal "successful" return address 17862306a36Sopenharmony_ci @ r10 = FP workspace 17962306a36Sopenharmony_ci @ lr = unrecognised FP instruction return address 18062306a36Sopenharmony_ci @ 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci .pushsection .data 18362306a36Sopenharmony_ci .align 2 18462306a36Sopenharmony_ciENTRY(fp_enter) 18562306a36Sopenharmony_ci .word no_fp 18662306a36Sopenharmony_ci .popsection 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cino_fp: 18962306a36Sopenharmony_ci ret lr 19062306a36Sopenharmony_ciENDPROC(no_fp) 191