18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-or-later */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci NetWinder Floating Point Emulator 48c2ecf20Sopenharmony_ci (c) Rebel.COM, 1998 58c2ecf20Sopenharmony_ci (c) 1998, 1999 Philip Blundell 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci Direct questions, comments to Scott Bambrough <scottb@netwinder.org> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci*/ 108c2ecf20Sopenharmony_ci#include <asm/assembler.h> 118c2ecf20Sopenharmony_ci#include <asm/extable.h> 128c2ecf20Sopenharmony_ci#include <asm/opcodes.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* This is the kernel's entry point into the floating point emulator. 158c2ecf20Sopenharmony_ciIt is called from the kernel with code similar to this: 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci sub r4, r5, #4 188c2ecf20Sopenharmony_ci ldrt r0, [r4] @ r0 = instruction 198c2ecf20Sopenharmony_ci adrsvc al, r9, ret_from_exception @ r9 = normal FP return 208c2ecf20Sopenharmony_ci adrsvc al, lr, fpundefinstr @ lr = undefined instr return 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci get_current_task r10 238c2ecf20Sopenharmony_ci mov r8, #1 248c2ecf20Sopenharmony_ci strb r8, [r10, #TSK_USED_MATH] @ set current->used_math 258c2ecf20Sopenharmony_ci add r10, r10, #TSS_FPESAVE @ r10 = workspace 268c2ecf20Sopenharmony_ci ldr r4, .LC2 278c2ecf20Sopenharmony_ci ldr pc, [r4] @ Call FP emulator entry point 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciThe kernel expects the emulator to return via one of two possible 308c2ecf20Sopenharmony_cipoints of return it passes to the emulator. The emulator, if 318c2ecf20Sopenharmony_cisuccessful in its emulation, jumps to ret_from_exception (passed in 328c2ecf20Sopenharmony_cir9) and the kernel takes care of returning control from the trap to 338c2ecf20Sopenharmony_cithe user code. If the emulator is unable to emulate the instruction, 348c2ecf20Sopenharmony_ciit returns via _fpundefinstr (passed via lr) and the kernel halts the 358c2ecf20Sopenharmony_ciuser program with a core dump. 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ciOn entry to the emulator r10 points to an area of private FP workspace 388c2ecf20Sopenharmony_cireserved in the thread structure for this process. This is where the 398c2ecf20Sopenharmony_ciemulator saves its registers across calls. The first word of this area 408c2ecf20Sopenharmony_ciis used as a flag to detect the first time a process uses floating point, 418c2ecf20Sopenharmony_ciso that the emulator startup cost can be avoided for tasks that don't 428c2ecf20Sopenharmony_ciwant it. 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ciThis routine does three things: 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci1) The kernel has created a struct pt_regs on the stack and saved the 478c2ecf20Sopenharmony_ciuser registers into it. See /usr/include/asm/proc/ptrace.h for details. 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci2) It calls EmulateAll to emulate a floating point instruction. 508c2ecf20Sopenharmony_ciEmulateAll returns 1 if the emulation was successful, or 0 if not. 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci3) If an instruction has been emulated successfully, it looks ahead at 538c2ecf20Sopenharmony_cithe next instruction. If it is a floating point instruction, it 548c2ecf20Sopenharmony_ciexecutes the instruction, without returning to user space. In this 558c2ecf20Sopenharmony_ciway it repeatedly looks ahead and executes floating point instructions 568c2ecf20Sopenharmony_ciuntil it encounters a non floating point instruction, at which time it 578c2ecf20Sopenharmony_cireturns via _fpreturn. 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ciThis is done to reduce the effect of the trap overhead on each 608c2ecf20Sopenharmony_cifloating point instructions. GCC attempts to group floating point 618c2ecf20Sopenharmony_ciinstructions to allow the emulator to spread the cost of the trap over 628c2ecf20Sopenharmony_ciseveral floating point instructions. */ 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#include <asm/asm-offsets.h> 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci .globl nwfpe_enter 678c2ecf20Sopenharmony_cinwfpe_enter: 688c2ecf20Sopenharmony_ci mov r4, lr @ save the failure-return addresses 698c2ecf20Sopenharmony_ci mov sl, sp @ we access the registers via 'sl' 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci ldr r5, [sp, #S_PC] @ get contents of PC; 728c2ecf20Sopenharmony_ci mov r6, r0 @ save the opcode 738c2ecf20Sopenharmony_ciemulate: 748c2ecf20Sopenharmony_ci ldr r1, [sp, #S_PSR] @ fetch the PSR 758c2ecf20Sopenharmony_ci bl arm_check_condition @ check the condition 768c2ecf20Sopenharmony_ci cmp r0, #ARM_OPCODE_CONDTEST_PASS @ condition passed? 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci @ if condition code failed to match, next insn 798c2ecf20Sopenharmony_ci bne next @ get the next instruction; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci mov r0, r6 @ prepare for EmulateAll() 828c2ecf20Sopenharmony_ci bl EmulateAll @ emulate the instruction 838c2ecf20Sopenharmony_ci cmp r0, #0 @ was emulation successful 848c2ecf20Sopenharmony_ci reteq r4 @ no, return failure 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cinext: 878c2ecf20Sopenharmony_ci uaccess_enable r3 888c2ecf20Sopenharmony_ci.Lx1: ldrt r6, [r5], #4 @ get the next instruction and 898c2ecf20Sopenharmony_ci @ increment PC 908c2ecf20Sopenharmony_ci uaccess_disable r3 918c2ecf20Sopenharmony_ci and r2, r6, #0x0F000000 @ test for FP insns 928c2ecf20Sopenharmony_ci teq r2, #0x0C000000 938c2ecf20Sopenharmony_ci teqne r2, #0x0D000000 948c2ecf20Sopenharmony_ci teqne r2, #0x0E000000 958c2ecf20Sopenharmony_ci retne r9 @ return ok if not a fp insn 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci str r5, [sp, #S_PC] @ update PC copy in regs 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci mov r0, r6 @ save a copy 1008c2ecf20Sopenharmony_ci b emulate @ check condition and emulate 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci @ We need to be prepared for the instructions at .Lx1 and .Lx2 1038c2ecf20Sopenharmony_ci @ to fault. Emit the appropriate exception gunk to fix things up. 1048c2ecf20Sopenharmony_ci @ ??? For some reason, faults can happen at .Lx2 even with a 1058c2ecf20Sopenharmony_ci @ plain LDR instruction. Weird, but it seems harmless. 1068c2ecf20Sopenharmony_ci .pushsection .text.fixup,"ax" 1078c2ecf20Sopenharmony_ci .align 2 1088c2ecf20Sopenharmony_ci.Lfix: ret r9 @ let the user eat segfaults 1098c2ecf20Sopenharmony_ci .popsection 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci ex_entry .Lx1, .Lfix 112