xref: /openbmc/linux/arch/arm/nwfpe/fpmodule.c (revision 58e16d792a6a8c6b750f637a4649967fcac853dc)
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