162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci    NetWinder Floating Point Emulator
562306a36Sopenharmony_ci    (c) Rebel.com, 1998-1999
662306a36Sopenharmony_ci    (c) Philip Blundell, 1998-1999
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci    Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci*/
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "fpa11.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/moduleparam.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* XXX */
1862306a36Sopenharmony_ci#include <linux/errno.h>
1962306a36Sopenharmony_ci#include <linux/types.h>
2062306a36Sopenharmony_ci#include <linux/kernel.h>
2162306a36Sopenharmony_ci#include <linux/signal.h>
2262306a36Sopenharmony_ci#include <linux/sched/signal.h>
2362306a36Sopenharmony_ci#include <linux/init.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <asm/thread_notify.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include "softfloat.h"
2862306a36Sopenharmony_ci#include "fpopcode.h"
2962306a36Sopenharmony_ci#include "fpmodule.h"
3062306a36Sopenharmony_ci#include "fpa11.inl"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/* kernel symbols required for signal handling */
3362306a36Sopenharmony_ci#ifdef CONFIG_FPE_NWFPE_XP
3462306a36Sopenharmony_ci#define NWFPE_BITS "extended"
3562306a36Sopenharmony_ci#else
3662306a36Sopenharmony_ci#define NWFPE_BITS "double"
3762306a36Sopenharmony_ci#endif
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#ifdef MODULE
4062306a36Sopenharmony_civoid fp_send_sig(unsigned long sig, struct task_struct *p, int priv);
4162306a36Sopenharmony_ci#else
4262306a36Sopenharmony_ci#define fp_send_sig	send_sig
4362306a36Sopenharmony_ci#define kern_fp_enter	fp_enter
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ciextern char fpe_type[];
4662306a36Sopenharmony_ci#endif
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int nwfpe_notify(struct notifier_block *self, unsigned long cmd, void *v)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct thread_info *thread = v;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (cmd == THREAD_NOTIFY_FLUSH)
5362306a36Sopenharmony_ci		nwfpe_init_fpa(&thread->fpstate);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return NOTIFY_DONE;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic struct notifier_block nwfpe_notifier_block = {
5962306a36Sopenharmony_ci	.notifier_call = nwfpe_notify,
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* kernel function prototypes required */
6362306a36Sopenharmony_civoid fp_setup(void);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/* external declarations for saved kernel symbols */
6662306a36Sopenharmony_ciextern void (*kern_fp_enter)(void);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/* Original value of fp_enter from kernel before patched by fpe_init. */
6962306a36Sopenharmony_cistatic void (*orig_fp_enter)(void);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* forward declarations */
7262306a36Sopenharmony_ciextern void nwfpe_enter(void);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic int __init fpe_init(void)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	if (sizeof(FPA11) > sizeof(union fp_state)) {
7762306a36Sopenharmony_ci		pr_err("nwfpe: bad structure size\n");
7862306a36Sopenharmony_ci		return -EINVAL;
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (sizeof(FPREG) != 12) {
8262306a36Sopenharmony_ci		pr_err("nwfpe: bad register size\n");
8362306a36Sopenharmony_ci		return -EINVAL;
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci	if (fpe_type[0] && strcmp(fpe_type, "nwfpe"))
8662306a36Sopenharmony_ci		return 0;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* Display title, version and copyright information. */
8962306a36Sopenharmony_ci	pr_info("NetWinder Floating Point Emulator V0.97 ("
9062306a36Sopenharmony_ci	        NWFPE_BITS " precision)\n");
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	thread_register_notifier(&nwfpe_notifier_block);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* Save pointer to the old FP handler and then patch ourselves in */
9562306a36Sopenharmony_ci	orig_fp_enter = kern_fp_enter;
9662306a36Sopenharmony_ci	kern_fp_enter = nwfpe_enter;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return 0;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic void __exit fpe_exit(void)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	thread_unregister_notifier(&nwfpe_notifier_block);
10462306a36Sopenharmony_ci	/* Restore the values we saved earlier. */
10562306a36Sopenharmony_ci	kern_fp_enter = orig_fp_enter;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/*
10962306a36Sopenharmony_ciScottB:  November 4, 1998
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ciMoved this function out of softfloat-specialize into fpmodule.c.
11262306a36Sopenharmony_ciThis effectively isolates all the changes required for integrating with the
11362306a36Sopenharmony_ciLinux kernel into fpmodule.c.  Porting to NetBSD should only require modifying
11462306a36Sopenharmony_cifpmodule.c to integrate with the NetBSD kernel (I hope!).
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci[1/1/99: Not quite true any more unfortunately.  There is Linux-specific
11762306a36Sopenharmony_cicode to access data in user space in some other source files at the
11862306a36Sopenharmony_cimoment (grep for get_user / put_user calls).  --philb]
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ciThis function is called by the SoftFloat routines to raise a floating
12162306a36Sopenharmony_cipoint exception.  We check the trap enable byte in the FPSR, and raise
12262306a36Sopenharmony_cia SIGFPE exception if necessary.  If not the relevant bits in the
12362306a36Sopenharmony_cicumulative exceptions flag byte are set and we return.
12462306a36Sopenharmony_ci*/
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_USER
12762306a36Sopenharmony_ci/* By default, ignore inexact errors as there are far too many of them to log */
12862306a36Sopenharmony_cistatic int debug = ~BIT_IXC;
12962306a36Sopenharmony_ci#endif
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_civoid float_raise(signed char flags)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	register unsigned int fpsr, cumulativeTraps;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_USER
13662306a36Sopenharmony_ci	if (flags & debug)
13762306a36Sopenharmony_ci 		printk(KERN_DEBUG
13862306a36Sopenharmony_ci		       "NWFPE: %s[%d] takes exception %08x at %ps from %08lx\n",
13962306a36Sopenharmony_ci		       current->comm, current->pid, flags,
14062306a36Sopenharmony_ci		       __builtin_return_address(0), GET_USERREG()->ARM_pc);
14162306a36Sopenharmony_ci#endif
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	/* Read fpsr and initialize the cumulativeTraps.  */
14462306a36Sopenharmony_ci	fpsr = readFPSR();
14562306a36Sopenharmony_ci	cumulativeTraps = 0;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	/* For each type of exception, the cumulative trap exception bit is only
14862306a36Sopenharmony_ci	   set if the corresponding trap enable bit is not set.  */
14962306a36Sopenharmony_ci	if ((!(fpsr & BIT_IXE)) && (flags & BIT_IXC))
15062306a36Sopenharmony_ci		cumulativeTraps |= BIT_IXC;
15162306a36Sopenharmony_ci	if ((!(fpsr & BIT_UFE)) && (flags & BIT_UFC))
15262306a36Sopenharmony_ci		cumulativeTraps |= BIT_UFC;
15362306a36Sopenharmony_ci	if ((!(fpsr & BIT_OFE)) && (flags & BIT_OFC))
15462306a36Sopenharmony_ci		cumulativeTraps |= BIT_OFC;
15562306a36Sopenharmony_ci	if ((!(fpsr & BIT_DZE)) && (flags & BIT_DZC))
15662306a36Sopenharmony_ci		cumulativeTraps |= BIT_DZC;
15762306a36Sopenharmony_ci	if ((!(fpsr & BIT_IOE)) && (flags & BIT_IOC))
15862306a36Sopenharmony_ci		cumulativeTraps |= BIT_IOC;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* Set the cumulative exceptions flags.  */
16162306a36Sopenharmony_ci	if (cumulativeTraps)
16262306a36Sopenharmony_ci		writeFPSR(fpsr | cumulativeTraps);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* Raise an exception if necessary.  */
16562306a36Sopenharmony_ci	if (fpsr & (flags << 16))
16662306a36Sopenharmony_ci		fp_send_sig(SIGFPE, current, 1);
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cimodule_init(fpe_init);
17062306a36Sopenharmony_cimodule_exit(fpe_exit);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ciMODULE_AUTHOR("Scott Bambrough <scottb@rebel.com>");
17362306a36Sopenharmony_ciMODULE_DESCRIPTION("NWFPE floating point emulator (" NWFPE_BITS " precision)");
17462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_USER
17762306a36Sopenharmony_cimodule_param(debug, int, 0644);
17862306a36Sopenharmony_ci#endif
179