18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci/*
48c2ecf20Sopenharmony_ci    NetWinder Floating Point Emulator
58c2ecf20Sopenharmony_ci    (c) Rebel.com, 1998-1999
68c2ecf20Sopenharmony_ci    (c) Philip Blundell, 1998-1999
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci    Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci*/
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "fpa11.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/* XXX */
188c2ecf20Sopenharmony_ci#include <linux/errno.h>
198c2ecf20Sopenharmony_ci#include <linux/types.h>
208c2ecf20Sopenharmony_ci#include <linux/kernel.h>
218c2ecf20Sopenharmony_ci#include <linux/signal.h>
228c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
238c2ecf20Sopenharmony_ci#include <linux/init.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <asm/thread_notify.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include "softfloat.h"
288c2ecf20Sopenharmony_ci#include "fpopcode.h"
298c2ecf20Sopenharmony_ci#include "fpmodule.h"
308c2ecf20Sopenharmony_ci#include "fpa11.inl"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/* kernel symbols required for signal handling */
338c2ecf20Sopenharmony_ci#ifdef CONFIG_FPE_NWFPE_XP
348c2ecf20Sopenharmony_ci#define NWFPE_BITS "extended"
358c2ecf20Sopenharmony_ci#else
368c2ecf20Sopenharmony_ci#define NWFPE_BITS "double"
378c2ecf20Sopenharmony_ci#endif
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#ifdef MODULE
408c2ecf20Sopenharmony_civoid fp_send_sig(unsigned long sig, struct task_struct *p, int priv);
418c2ecf20Sopenharmony_ci#else
428c2ecf20Sopenharmony_ci#define fp_send_sig	send_sig
438c2ecf20Sopenharmony_ci#define kern_fp_enter	fp_enter
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ciextern char fpe_type[];
468c2ecf20Sopenharmony_ci#endif
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int nwfpe_notify(struct notifier_block *self, unsigned long cmd, void *v)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct thread_info *thread = v;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	if (cmd == THREAD_NOTIFY_FLUSH)
538c2ecf20Sopenharmony_ci		nwfpe_init_fpa(&thread->fpstate);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic struct notifier_block nwfpe_notifier_block = {
598c2ecf20Sopenharmony_ci	.notifier_call = nwfpe_notify,
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* kernel function prototypes required */
638c2ecf20Sopenharmony_civoid fp_setup(void);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/* external declarations for saved kernel symbols */
668c2ecf20Sopenharmony_ciextern void (*kern_fp_enter)(void);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* Original value of fp_enter from kernel before patched by fpe_init. */
698c2ecf20Sopenharmony_cistatic void (*orig_fp_enter)(void);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/* forward declarations */
728c2ecf20Sopenharmony_ciextern void nwfpe_enter(void);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic int __init fpe_init(void)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	if (sizeof(FPA11) > sizeof(union fp_state)) {
778c2ecf20Sopenharmony_ci		pr_err("nwfpe: bad structure size\n");
788c2ecf20Sopenharmony_ci		return -EINVAL;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (sizeof(FPREG) != 12) {
828c2ecf20Sopenharmony_ci		pr_err("nwfpe: bad register size\n");
838c2ecf20Sopenharmony_ci		return -EINVAL;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci	if (fpe_type[0] && strcmp(fpe_type, "nwfpe"))
868c2ecf20Sopenharmony_ci		return 0;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	/* Display title, version and copyright information. */
898c2ecf20Sopenharmony_ci	pr_info("NetWinder Floating Point Emulator V0.97 ("
908c2ecf20Sopenharmony_ci	        NWFPE_BITS " precision)\n");
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	thread_register_notifier(&nwfpe_notifier_block);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* Save pointer to the old FP handler and then patch ourselves in */
958c2ecf20Sopenharmony_ci	orig_fp_enter = kern_fp_enter;
968c2ecf20Sopenharmony_ci	kern_fp_enter = nwfpe_enter;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return 0;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic void __exit fpe_exit(void)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	thread_unregister_notifier(&nwfpe_notifier_block);
1048c2ecf20Sopenharmony_ci	/* Restore the values we saved earlier. */
1058c2ecf20Sopenharmony_ci	kern_fp_enter = orig_fp_enter;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/*
1098c2ecf20Sopenharmony_ciScottB:  November 4, 1998
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ciMoved this function out of softfloat-specialize into fpmodule.c.
1128c2ecf20Sopenharmony_ciThis effectively isolates all the changes required for integrating with the
1138c2ecf20Sopenharmony_ciLinux kernel into fpmodule.c.  Porting to NetBSD should only require modifying
1148c2ecf20Sopenharmony_cifpmodule.c to integrate with the NetBSD kernel (I hope!).
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci[1/1/99: Not quite true any more unfortunately.  There is Linux-specific
1178c2ecf20Sopenharmony_cicode to access data in user space in some other source files at the
1188c2ecf20Sopenharmony_cimoment (grep for get_user / put_user calls).  --philb]
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ciThis function is called by the SoftFloat routines to raise a floating
1218c2ecf20Sopenharmony_cipoint exception.  We check the trap enable byte in the FPSR, and raise
1228c2ecf20Sopenharmony_cia SIGFPE exception if necessary.  If not the relevant bits in the
1238c2ecf20Sopenharmony_cicumulative exceptions flag byte are set and we return.
1248c2ecf20Sopenharmony_ci*/
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_USER
1278c2ecf20Sopenharmony_ci/* By default, ignore inexact errors as there are far too many of them to log */
1288c2ecf20Sopenharmony_cistatic int debug = ~BIT_IXC;
1298c2ecf20Sopenharmony_ci#endif
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_civoid float_raise(signed char flags)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	register unsigned int fpsr, cumulativeTraps;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_USER
1368c2ecf20Sopenharmony_ci	if (flags & debug)
1378c2ecf20Sopenharmony_ci 		printk(KERN_DEBUG
1388c2ecf20Sopenharmony_ci		       "NWFPE: %s[%d] takes exception %08x at %ps from %08lx\n",
1398c2ecf20Sopenharmony_ci		       current->comm, current->pid, flags,
1408c2ecf20Sopenharmony_ci		       __builtin_return_address(0), GET_USERREG()->ARM_pc);
1418c2ecf20Sopenharmony_ci#endif
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/* Read fpsr and initialize the cumulativeTraps.  */
1448c2ecf20Sopenharmony_ci	fpsr = readFPSR();
1458c2ecf20Sopenharmony_ci	cumulativeTraps = 0;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	/* For each type of exception, the cumulative trap exception bit is only
1488c2ecf20Sopenharmony_ci	   set if the corresponding trap enable bit is not set.  */
1498c2ecf20Sopenharmony_ci	if ((!(fpsr & BIT_IXE)) && (flags & BIT_IXC))
1508c2ecf20Sopenharmony_ci		cumulativeTraps |= BIT_IXC;
1518c2ecf20Sopenharmony_ci	if ((!(fpsr & BIT_UFE)) && (flags & BIT_UFC))
1528c2ecf20Sopenharmony_ci		cumulativeTraps |= BIT_UFC;
1538c2ecf20Sopenharmony_ci	if ((!(fpsr & BIT_OFE)) && (flags & BIT_OFC))
1548c2ecf20Sopenharmony_ci		cumulativeTraps |= BIT_OFC;
1558c2ecf20Sopenharmony_ci	if ((!(fpsr & BIT_DZE)) && (flags & BIT_DZC))
1568c2ecf20Sopenharmony_ci		cumulativeTraps |= BIT_DZC;
1578c2ecf20Sopenharmony_ci	if ((!(fpsr & BIT_IOE)) && (flags & BIT_IOC))
1588c2ecf20Sopenharmony_ci		cumulativeTraps |= BIT_IOC;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	/* Set the cumulative exceptions flags.  */
1618c2ecf20Sopenharmony_ci	if (cumulativeTraps)
1628c2ecf20Sopenharmony_ci		writeFPSR(fpsr | cumulativeTraps);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	/* Raise an exception if necessary.  */
1658c2ecf20Sopenharmony_ci	if (fpsr & (flags << 16))
1668c2ecf20Sopenharmony_ci		fp_send_sig(SIGFPE, current, 1);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cimodule_init(fpe_init);
1708c2ecf20Sopenharmony_cimodule_exit(fpe_exit);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ciMODULE_AUTHOR("Scott Bambrough <scottb@rebel.com>");
1738c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NWFPE floating point emulator (" NWFPE_BITS " precision)");
1748c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_USER
1778c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
1788c2ecf20Sopenharmony_ci#endif
179