1*74ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds
31da177e4SLinus Torvalds /*
41da177e4SLinus Torvalds NetWinder Floating Point Emulator
51da177e4SLinus Torvalds (c) Rebel.com, 1998-1999
61da177e4SLinus Torvalds (c) Philip Blundell, 1998-1999
71da177e4SLinus Torvalds
81da177e4SLinus Torvalds Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
91da177e4SLinus Torvalds
101da177e4SLinus Torvalds */
111da177e4SLinus Torvalds
121da177e4SLinus Torvalds #include "fpa11.h"
131da177e4SLinus Torvalds
141da177e4SLinus Torvalds #include <linux/module.h>
1549aea0fdSRussell King #include <linux/moduleparam.h>
161da177e4SLinus Torvalds
171da177e4SLinus Torvalds /* XXX */
181da177e4SLinus Torvalds #include <linux/errno.h>
191da177e4SLinus Torvalds #include <linux/types.h>
201da177e4SLinus Torvalds #include <linux/kernel.h>
211da177e4SLinus Torvalds #include <linux/signal.h>
22c3edc401SIngo Molnar #include <linux/sched/signal.h>
231da177e4SLinus Torvalds #include <linux/init.h>
24d6551e88SRussell King
25d6551e88SRussell King #include <asm/thread_notify.h>
261da177e4SLinus Torvalds
271da177e4SLinus Torvalds #include "softfloat.h"
281da177e4SLinus Torvalds #include "fpopcode.h"
291da177e4SLinus Torvalds #include "fpmodule.h"
301da177e4SLinus Torvalds #include "fpa11.inl"
311da177e4SLinus Torvalds
321da177e4SLinus Torvalds /* kernel symbols required for signal handling */
331da177e4SLinus Torvalds #ifdef CONFIG_FPE_NWFPE_XP
341da177e4SLinus Torvalds #define NWFPE_BITS "extended"
351da177e4SLinus Torvalds #else
361da177e4SLinus Torvalds #define NWFPE_BITS "double"
371da177e4SLinus Torvalds #endif
381da177e4SLinus Torvalds
391da177e4SLinus Torvalds #ifdef MODULE
401da177e4SLinus Torvalds void fp_send_sig(unsigned long sig, struct task_struct *p, int priv);
411da177e4SLinus Torvalds #else
421da177e4SLinus Torvalds #define fp_send_sig send_sig
431da177e4SLinus Torvalds #define kern_fp_enter fp_enter
441da177e4SLinus Torvalds
451da177e4SLinus Torvalds extern char fpe_type[];
461da177e4SLinus Torvalds #endif
471da177e4SLinus Torvalds
nwfpe_notify(struct notifier_block * self,unsigned long cmd,void * v)48d6551e88SRussell King static int nwfpe_notify(struct notifier_block *self, unsigned long cmd, void *v)
49d6551e88SRussell King {
50d6551e88SRussell King struct thread_info *thread = v;
51d6551e88SRussell King
52d6551e88SRussell King if (cmd == THREAD_NOTIFY_FLUSH)
53d6551e88SRussell King nwfpe_init_fpa(&thread->fpstate);
54d6551e88SRussell King
55d6551e88SRussell King return NOTIFY_DONE;
56d6551e88SRussell King }
57d6551e88SRussell King
58d6551e88SRussell King static struct notifier_block nwfpe_notifier_block = {
59d6551e88SRussell King .notifier_call = nwfpe_notify,
60d6551e88SRussell King };
61d6551e88SRussell King
621da177e4SLinus Torvalds /* kernel function prototypes required */
631da177e4SLinus Torvalds void fp_setup(void);
641da177e4SLinus Torvalds
651da177e4SLinus Torvalds /* external declarations for saved kernel symbols */
661da177e4SLinus Torvalds extern void (*kern_fp_enter)(void);
671da177e4SLinus Torvalds
681da177e4SLinus Torvalds /* Original value of fp_enter from kernel before patched by fpe_init. */
691da177e4SLinus Torvalds static void (*orig_fp_enter)(void);
701da177e4SLinus Torvalds
711da177e4SLinus Torvalds /* forward declarations */
721da177e4SLinus Torvalds extern void nwfpe_enter(void);
731da177e4SLinus Torvalds
fpe_init(void)741da177e4SLinus Torvalds static int __init fpe_init(void)
751da177e4SLinus Torvalds {
761da177e4SLinus Torvalds if (sizeof(FPA11) > sizeof(union fp_state)) {
774ed89f22SRussell King pr_err("nwfpe: bad structure size\n");
781da177e4SLinus Torvalds return -EINVAL;
791da177e4SLinus Torvalds }
801da177e4SLinus Torvalds
811da177e4SLinus Torvalds if (sizeof(FPREG) != 12) {
824ed89f22SRussell King pr_err("nwfpe: bad register size\n");
831da177e4SLinus Torvalds return -EINVAL;
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds if (fpe_type[0] && strcmp(fpe_type, "nwfpe"))
861da177e4SLinus Torvalds return 0;
871da177e4SLinus Torvalds
881da177e4SLinus Torvalds /* Display title, version and copyright information. */
898a2ab42bSRussell King pr_info("NetWinder Floating Point Emulator V0.97 ("
901da177e4SLinus Torvalds NWFPE_BITS " precision)\n");
911da177e4SLinus Torvalds
92d6551e88SRussell King thread_register_notifier(&nwfpe_notifier_block);
93d6551e88SRussell King
941da177e4SLinus Torvalds /* Save pointer to the old FP handler and then patch ourselves in */
951da177e4SLinus Torvalds orig_fp_enter = kern_fp_enter;
961da177e4SLinus Torvalds kern_fp_enter = nwfpe_enter;
971da177e4SLinus Torvalds
981da177e4SLinus Torvalds return 0;
991da177e4SLinus Torvalds }
1001da177e4SLinus Torvalds
fpe_exit(void)1011da177e4SLinus Torvalds static void __exit fpe_exit(void)
1021da177e4SLinus Torvalds {
103d6551e88SRussell King thread_unregister_notifier(&nwfpe_notifier_block);
1041da177e4SLinus Torvalds /* Restore the values we saved earlier. */
1051da177e4SLinus Torvalds kern_fp_enter = orig_fp_enter;
1061da177e4SLinus Torvalds }
1071da177e4SLinus Torvalds
1081da177e4SLinus Torvalds /*
1091da177e4SLinus Torvalds ScottB: November 4, 1998
1101da177e4SLinus Torvalds
1111da177e4SLinus Torvalds Moved this function out of softfloat-specialize into fpmodule.c.
1121da177e4SLinus Torvalds This effectively isolates all the changes required for integrating with the
1131da177e4SLinus Torvalds Linux kernel into fpmodule.c. Porting to NetBSD should only require modifying
1141da177e4SLinus Torvalds fpmodule.c to integrate with the NetBSD kernel (I hope!).
1151da177e4SLinus Torvalds
1161da177e4SLinus Torvalds [1/1/99: Not quite true any more unfortunately. There is Linux-specific
1171da177e4SLinus Torvalds code to access data in user space in some other source files at the
1181da177e4SLinus Torvalds moment (grep for get_user / put_user calls). --philb]
1191da177e4SLinus Torvalds
1201da177e4SLinus Torvalds This function is called by the SoftFloat routines to raise a floating
1211da177e4SLinus Torvalds point exception. We check the trap enable byte in the FPSR, and raise
1221da177e4SLinus Torvalds a SIGFPE exception if necessary. If not the relevant bits in the
1231da177e4SLinus Torvalds cumulative exceptions flag byte are set and we return.
1241da177e4SLinus Torvalds */
1251da177e4SLinus Torvalds
12649aea0fdSRussell King #ifdef CONFIG_DEBUG_USER
12749aea0fdSRussell King /* By default, ignore inexact errors as there are far too many of them to log */
12849aea0fdSRussell King static int debug = ~BIT_IXC;
12949aea0fdSRussell King #endif
13049aea0fdSRussell King
float_raise(signed char flags)1311da177e4SLinus Torvalds void float_raise(signed char flags)
1321da177e4SLinus Torvalds {
1331da177e4SLinus Torvalds register unsigned int fpsr, cumulativeTraps;
1341da177e4SLinus Torvalds
1351da177e4SLinus Torvalds #ifdef CONFIG_DEBUG_USER
13649aea0fdSRussell King if (flags & debug)
1371da177e4SLinus Torvalds printk(KERN_DEBUG
138d75f773cSSakari Ailus "NWFPE: %s[%d] takes exception %08x at %ps from %08lx\n",
1391da177e4SLinus Torvalds current->comm, current->pid, flags,
140b66da4a4SRussell King __builtin_return_address(0), GET_USERREG()->ARM_pc);
1411da177e4SLinus Torvalds #endif
1421da177e4SLinus Torvalds
1431da177e4SLinus Torvalds /* Read fpsr and initialize the cumulativeTraps. */
1441da177e4SLinus Torvalds fpsr = readFPSR();
1451da177e4SLinus Torvalds cumulativeTraps = 0;
1461da177e4SLinus Torvalds
1471da177e4SLinus Torvalds /* For each type of exception, the cumulative trap exception bit is only
1481da177e4SLinus Torvalds set if the corresponding trap enable bit is not set. */
1491da177e4SLinus Torvalds if ((!(fpsr & BIT_IXE)) && (flags & BIT_IXC))
1501da177e4SLinus Torvalds cumulativeTraps |= BIT_IXC;
1511da177e4SLinus Torvalds if ((!(fpsr & BIT_UFE)) && (flags & BIT_UFC))
1521da177e4SLinus Torvalds cumulativeTraps |= BIT_UFC;
1531da177e4SLinus Torvalds if ((!(fpsr & BIT_OFE)) && (flags & BIT_OFC))
1541da177e4SLinus Torvalds cumulativeTraps |= BIT_OFC;
1551da177e4SLinus Torvalds if ((!(fpsr & BIT_DZE)) && (flags & BIT_DZC))
1561da177e4SLinus Torvalds cumulativeTraps |= BIT_DZC;
1571da177e4SLinus Torvalds if ((!(fpsr & BIT_IOE)) && (flags & BIT_IOC))
1581da177e4SLinus Torvalds cumulativeTraps |= BIT_IOC;
1591da177e4SLinus Torvalds
1601da177e4SLinus Torvalds /* Set the cumulative exceptions flags. */
1611da177e4SLinus Torvalds if (cumulativeTraps)
1621da177e4SLinus Torvalds writeFPSR(fpsr | cumulativeTraps);
1631da177e4SLinus Torvalds
1641da177e4SLinus Torvalds /* Raise an exception if necessary. */
1651da177e4SLinus Torvalds if (fpsr & (flags << 16))
1661da177e4SLinus Torvalds fp_send_sig(SIGFPE, current, 1);
1671da177e4SLinus Torvalds }
1681da177e4SLinus Torvalds
1691da177e4SLinus Torvalds module_init(fpe_init);
1701da177e4SLinus Torvalds module_exit(fpe_exit);
1711da177e4SLinus Torvalds
1721da177e4SLinus Torvalds MODULE_AUTHOR("Scott Bambrough <scottb@rebel.com>");
1731da177e4SLinus Torvalds MODULE_DESCRIPTION("NWFPE floating point emulator (" NWFPE_BITS " precision)");
1741da177e4SLinus Torvalds MODULE_LICENSE("GPL");
17549aea0fdSRussell King
17649aea0fdSRussell King #ifdef CONFIG_DEBUG_USER
17749aea0fdSRussell King module_param(debug, int, 0644);
17849aea0fdSRussell King #endif
179