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