[POWERPC] 8xx: Move softemu8xx.c from arch/ppc
authorScott Wood <scottwood@freescale.com>
Tue, 18 Sep 2007 20:29:35 +0000 (15:29 -0500)
committerKumar Gala <galak@kernel.crashing.org>
Thu, 4 Oct 2007 16:02:54 +0000 (11:02 -0500)
Previously, Soft_emulate_8xx was called with no implementation, resulting in
build failures whenever building 8xx without math emulation.  The
implementation is copied from arch/ppc to resolve this issue.

However, this sort of minimal emulation is not a very good idea other than
for compatibility with existing userspaces, as it's less efficient than
soft-float and can mislead users into believing they have soft-float.  Thus,
it is made a configurable option, off by default.

Signed-off-by: Scott Wood <scottwood@freescale.com>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
arch/powerpc/Kconfig
arch/powerpc/kernel/Makefile
arch/powerpc/kernel/softemu8xx.c [new file with mode: 0644]
arch/powerpc/kernel/traps.c

index 6f5155d8c32428b7004504c3b8311754fcb42feb..31804575dd5e5811d530fdeee62412eb6de8b4bd 100644 (file)
@@ -198,6 +198,17 @@ config MATH_EMULATION
          unit, which will allow programs that use floating-point
          instructions to run.
 
+config 8XX_MINIMAL_FPEMU
+       bool "Minimal math emulation for 8xx"
+       depends on 8xx && !MATH_EMULATION
+       help
+         Older arch/ppc kernels still emulated a few floating point
+         instructions such as load and store, even when full math
+         emulation is disabled.  Say "Y" here if you want to preserve
+         this behavior.
+
+         It is recommended that you build a soft-float userspace instead.
+
 config IOMMU_VMERGE
        bool "Enable IOMMU virtual merging"
        depends on PPC64
index 8327d92eeca8baa6c936abbdccde94ecb09d3cdf..ca51f0cf27ab3a77661ca9ff02e1e1de03196d12 100644 (file)
@@ -75,6 +75,8 @@ obj-$(CONFIG_KEXEC)           += machine_kexec.o crash.o \
 obj-$(CONFIG_AUDIT)            += audit.o
 obj64-$(CONFIG_AUDIT)          += compat_audit.o
 
+obj-$(CONFIG_8XX_MINIMAL_FPEMU) += softemu8xx.o
+
 ifneq ($(CONFIG_PPC_INDIRECT_IO),y)
 obj-y                          += iomap.o
 endif
diff --git a/arch/powerpc/kernel/softemu8xx.c b/arch/powerpc/kernel/softemu8xx.c
new file mode 100644 (file)
index 0000000..67d6f68
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Software emulation of some PPC instructions for the 8xx core.
+ *
+ * Copyright (C) 1998 Dan Malek (dmalek@jlc.net)
+ *
+ * Software floating emuation for the MPC8xx processor.  I did this mostly
+ * because it was easier than trying to get the libraries compiled for
+ * software floating point.  The goal is still to get the libraries done,
+ * but I lost patience and needed some hacks to at least get init and
+ * shells running.  The first problem is the setjmp/longjmp that save
+ * and restore the floating point registers.
+ *
+ * For this emulation, our working registers are found on the register
+ * save area.
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/interrupt.h>
+
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+/* Eventually we may need a look-up table, but this works for now.
+*/
+#define LFS    48
+#define LFD    50
+#define LFDU   51
+#define STFD   54
+#define STFDU  55
+#define FMR    63
+
+void print_8xx_pte(struct mm_struct *mm, unsigned long addr)
+{
+       pgd_t *pgd;
+       pmd_t *pmd;
+       pte_t *pte;
+
+       printk(" pte @ 0x%8lx: ", addr);
+       pgd = pgd_offset(mm, addr & PAGE_MASK);
+       if (pgd) {
+               pmd = pmd_offset(pud_offset(pgd, addr & PAGE_MASK),
+                                addr & PAGE_MASK);
+               if (pmd && pmd_present(*pmd)) {
+                       pte = pte_offset_kernel(pmd, addr & PAGE_MASK);
+                       if (pte) {
+                               printk(" (0x%08lx)->(0x%08lx)->0x%08lx\n",
+                                       (long)pgd, (long)pte, (long)pte_val(*pte));
+#define pp ((long)pte_val(*pte))
+                               printk(" RPN: %05lx PP: %lx SPS: %lx SH: %lx "
+                                      "CI: %lx v: %lx\n",
+                                      pp>>12,    /* rpn */
+                                      (pp>>10)&3, /* pp */
+                                      (pp>>3)&1, /* small */
+                                      (pp>>2)&1, /* shared */
+                                      (pp>>1)&1, /* cache inhibit */
+                                      pp&1       /* valid */
+                                      );
+#undef pp
+                       }
+                       else {
+                               printk("no pte\n");
+                       }
+               }
+               else {
+                       printk("no pmd\n");
+               }
+       }
+       else {
+               printk("no pgd\n");
+       }
+}
+
+int get_8xx_pte(struct mm_struct *mm, unsigned long addr)
+{
+       pgd_t *pgd;
+       pmd_t *pmd;
+       pte_t *pte;
+       int retval = 0;
+
+       pgd = pgd_offset(mm, addr & PAGE_MASK);
+       if (pgd) {
+               pmd = pmd_offset(pud_offset(pgd, addr & PAGE_MASK),
+                                addr & PAGE_MASK);
+               if (pmd && pmd_present(*pmd)) {
+                       pte = pte_offset_kernel(pmd, addr & PAGE_MASK);
+                       if (pte) {
+                               retval = (int)pte_val(*pte);
+                       }
+               }
+       }
+       return retval;
+}
+
+/*
+ * We return 0 on success, 1 on unimplemented instruction, and EFAULT
+ * if a load/store faulted.
+ */
+int Soft_emulate_8xx(struct pt_regs *regs)
+{
+       u32 inst, instword;
+       u32 flreg, idxreg, disp;
+       int retval;
+       s16 sdisp;
+       u32 *ea, *ip;
+
+       retval = 0;
+
+       instword = *((u32 *)regs->nip);
+       inst = instword >> 26;
+
+       flreg = (instword >> 21) & 0x1f;
+       idxreg = (instword >> 16) & 0x1f;
+       disp = instword & 0xffff;
+
+       ea = (u32 *)(regs->gpr[idxreg] + disp);
+       ip = (u32 *)&current->thread.fpr[flreg];
+
+       switch ( inst )
+       {
+       case LFD:
+               /* this is a 16 bit quantity that is sign extended
+                * so use a signed short here -- Cort
+                */
+               sdisp = (instword & 0xffff);
+               ea = (u32 *)(regs->gpr[idxreg] + sdisp);
+               if (copy_from_user(ip, ea, sizeof(double)))
+                       retval = -EFAULT;
+               break;
+
+       case LFDU:
+               if (copy_from_user(ip, ea, sizeof(double)))
+                       retval = -EFAULT;
+               else
+                       regs->gpr[idxreg] = (u32)ea;
+               break;
+       case LFS:
+               sdisp = (instword & 0xffff);
+               ea = (u32 *)(regs->gpr[idxreg] + sdisp);
+               if (copy_from_user(ip, ea, sizeof(float)))
+                       retval = -EFAULT;
+               break;
+       case STFD:
+               /* this is a 16 bit quantity that is sign extended
+                * so use a signed short here -- Cort
+                */
+               sdisp = (instword & 0xffff);
+               ea = (u32 *)(regs->gpr[idxreg] + sdisp);
+               if (copy_to_user(ea, ip, sizeof(double)))
+                       retval = -EFAULT;
+               break;
+
+       case STFDU:
+               if (copy_to_user(ea, ip, sizeof(double)))
+                       retval = -EFAULT;
+               else
+                       regs->gpr[idxreg] = (u32)ea;
+               break;
+       case FMR:
+               /* assume this is a fp move -- Cort */
+               memcpy(ip, &current->thread.fpr[(instword>>11)&0x1f],
+                      sizeof(double));
+               break;
+       default:
+               retval = 1;
+               printk("Bad emulation %s/%d\n"
+                      " NIP: %08lx instruction: %08x opcode: %x "
+                      "A: %x B: %x C: %x code: %x rc: %x\n",
+                      current->comm,current->pid,
+                      regs->nip,
+                      instword,inst,
+                      (instword>>16)&0x1f,
+                      (instword>>11)&0x1f,
+                      (instword>>6)&0x1f,
+                      (instword>>1)&0x3ff,
+                      instword&1);
+               {
+                       int pa;
+                       print_8xx_pte(current->mm,regs->nip);
+                       pa = get_8xx_pte(current->mm,regs->nip) & PAGE_MASK;
+                       pa |= (regs->nip & ~PAGE_MASK);
+                       pa = (unsigned long)__va(pa);
+                       printk("Kernel VA for NIP %x ", pa);
+                       print_8xx_pte(current->mm,pa);
+               }
+       }
+
+       if (retval == 0)
+               regs->nip += 4;
+
+       return retval;
+}
index 5a49eabd492e5616a5b50b8ed4d0cfab40c63477..2f1857c9819fa09a47704cbedc0ad98bb4c6c00c 100644 (file)
@@ -906,7 +906,9 @@ void SoftwareEmulation(struct pt_regs *regs)
 {
        extern int do_mathemu(struct pt_regs *);
        extern int Soft_emulate_8xx(struct pt_regs *);
+#if defined(CONFIG_MATH_EMULATION) || defined(CONFIG_8XX_MINIMAL_FPEMU)
        int errcode;
+#endif
 
        CHECK_FULL_REGS(regs);
 
@@ -936,7 +938,7 @@ void SoftwareEmulation(struct pt_regs *regs)
                return;
        }
 
-#else
+#elif defined(CONFIG_8XX_MINIMAL_FPEMU)
        errcode = Soft_emulate_8xx(regs);
        switch (errcode) {
        case 0:
@@ -949,6 +951,8 @@ void SoftwareEmulation(struct pt_regs *regs)
                _exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip);
                return;
        }
+#else
+       _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
 #endif
 }
 #endif /* CONFIG_8xx */