Merge tag 'for-linus' of https://github.com/openrisc/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 1 May 2023 18:52:32 +0000 (11:52 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 1 May 2023 18:52:32 +0000 (11:52 -0700)
Pull OpenRISC updates from Stafford Horne:
 "Two things for OpenRISC this cycle:

   - Small cleanup for device tree cpu iteration from Rob Herring

   - Add support for storing, restoring and accessing user space FPU
     state, to allow for libc to support the FPU on OpenRISC"

* tag 'for-linus' of https://github.com/openrisc/linux:
  openrisc: Add floating point regset
  openrisc: Support floating point user api
  openrisc: Support storing and restoring fpu state
  openrisc: Properly store r31 to pt_regs on unhandled exceptions
  openrisc: Use common of_get_cpu_node() instead of open-coding

arch/openrisc/include/asm/ptrace.h
arch/openrisc/include/uapi/asm/elf.h
arch/openrisc/include/uapi/asm/ptrace.h
arch/openrisc/include/uapi/asm/sigcontext.h
arch/openrisc/kernel/entry.S
arch/openrisc/kernel/head.S
arch/openrisc/kernel/ptrace.c
arch/openrisc/kernel/setup.c
arch/openrisc/kernel/signal.c
arch/openrisc/kernel/traps.c

index 01f81d4..375147f 100644 (file)
@@ -59,7 +59,7 @@ struct pt_regs {
         * -1 for all other exceptions.
         */
        long  orig_gpr11;       /* For restarting system calls */
-       long dummy;             /* Cheap alignment fix */
+       long fpcsr;             /* Floating point control status register. */
        long dummy2;            /* Cheap alignment fix */
 };
 
@@ -115,6 +115,6 @@ static inline long regs_return_value(struct pt_regs *regs)
 #define PT_GPR31      124
 #define PT_PC        128
 #define PT_ORIG_GPR11 132
-#define PT_SYSCALLNO  136
+#define PT_FPCSR      136
 
 #endif /* __ASM_OPENRISC_PTRACE_H */
index e892d50..6868f81 100644 (file)
@@ -53,8 +53,7 @@ typedef unsigned long elf_greg_t;
 #define ELF_NGREG (sizeof(struct user_regs_struct) / sizeof(elf_greg_t))
 typedef elf_greg_t elf_gregset_t[ELF_NGREG];
 
-/* A placeholder; OR32 does not have fp support yes, so no fp regs for now.  */
-typedef unsigned long elf_fpregset_t;
+typedef struct __or1k_fpu_state elf_fpregset_t;
 
 /* EM_OPENRISC is defined in linux/elf-em.h */
 #define EM_OR32         0x8472
index d4fab26..a77cc99 100644 (file)
@@ -30,6 +30,10 @@ struct user_regs_struct {
        unsigned long pc;
        unsigned long sr;
 };
+
+struct __or1k_fpu_state {
+       unsigned long fpcsr;
+};
 #endif
 
 
index 8ab775f..ca585e4 100644 (file)
@@ -28,6 +28,7 @@
 
 struct sigcontext {
        struct user_regs_struct regs;  /* needs to be first */
+       struct __or1k_fpu_state fpu;
        unsigned long oldmask;
 };
 
index 54a87bb..c9f48e7 100644 (file)
        l.mtspr r0,r3,SPR_EPCR_BASE                             ;\
        l.lwz   r3,PT_SR(r1)                                    ;\
        l.mtspr r0,r3,SPR_ESR_BASE                              ;\
+       l.lwz   r3,PT_FPCSR(r1)                                 ;\
+       l.mtspr r0,r3,SPR_FPCSR                                 ;\
        l.lwz   r2,PT_GPR2(r1)                                  ;\
        l.lwz   r3,PT_GPR3(r1)                                  ;\
        l.lwz   r4,PT_GPR4(r1)                                  ;\
@@ -173,9 +175,10 @@ handler:                                                   ;\
        l.sw    PT_GPR28(r1),r28                                        ;\
        l.sw    PT_GPR29(r1),r29                                        ;\
        /* r30 already save */                                  ;\
-/*        l.sw    PT_GPR30(r1),r30*/                                   ;\
        l.sw    PT_GPR31(r1),r31                                        ;\
        TRACE_IRQS_OFF_ENTRY                                            ;\
+       l.mfspr r30,r0,SPR_FPCSR                                ;\
+       l.sw    PT_FPCSR(r1),r30                                ;\
        /* Store -1 in orig_gpr11 for non-syscall exceptions */ ;\
        l.addi  r30,r0,-1                                       ;\
        l.sw    PT_ORIG_GPR11(r1),r30
@@ -211,12 +214,13 @@ handler:                                                  ;\
        l.sw    PT_GPR27(r1),r27                                        ;\
        l.sw    PT_GPR28(r1),r28                                        ;\
        l.sw    PT_GPR29(r1),r29                                        ;\
-       /* r31 already saved */                                 ;\
-       l.sw    PT_GPR30(r1),r30                                        ;\
-/*        l.sw    PT_GPR31(r1),r31     */                              ;\
+       /* r30 already saved */                                         ;\
+       l.sw    PT_GPR31(r1),r31                                        ;\
        /* Store -1 in orig_gpr11 for non-syscall exceptions */ ;\
        l.addi  r30,r0,-1                                       ;\
        l.sw    PT_ORIG_GPR11(r1),r30                           ;\
+       l.mfspr r30,r0,SPR_FPCSR                                ;\
+       l.sw    PT_FPCSR(r1),r30                                ;\
        l.addi  r3,r1,0                                         ;\
        /* r4 is exception EA */                                ;\
        l.addi  r5,r0,vector                                    ;\
@@ -844,9 +848,16 @@ _syscall_badsys:
 
 /******* END SYSCALL HANDLING *******/
 
-/* ---[ 0xd00: Trap exception ]------------------------------------------ */
+/* ---[ 0xd00: Floating Point exception ]-------------------------------- */
 
-UNHANDLED_EXCEPTION(_vector_0xd00,0xd00)
+EXCEPTION_ENTRY(_fpe_trap_handler)
+       CLEAR_LWA_FLAG(r3)
+       /* r4: EA of fault (set by EXCEPTION_HANDLE) */
+       l.jal   do_fpe_trap
+        l.addi  r3,r1,0 /* pt_regs */
+
+       l.j     _ret_from_exception
+        l.nop
 
 /* ---[ 0xe00: Trap exception ]------------------------------------------ */
 
@@ -1089,6 +1100,10 @@ ENTRY(_switch)
        l.sw    PT_GPR28(r1),r28
        l.sw    PT_GPR30(r1),r30
 
+       /* Store the old FPU state to new pt_regs */
+       l.mfspr r29,r0,SPR_FPCSR
+       l.sw    PT_FPCSR(r1),r29
+
        l.addi  r11,r10,0                       /* Save old 'current' to 'last' return value*/
 
        /* We use thread_info->ksp for storing the address of the above
@@ -1111,6 +1126,10 @@ ENTRY(_switch)
        l.lwz   r29,PT_SP(r1)
        l.sw    TI_KSP(r10),r29
 
+       /* Restore the old value of FPCSR */
+       l.lwz   r29,PT_FPCSR(r1)
+       l.mtspr r0,r29,SPR_FPCSR
+
        /* ...and restore the registers, except r11 because the return value
         * has already been set above.
         */
index e11699f..439e00f 100644 (file)
@@ -424,9 +424,9 @@ _dispatch_do_ipage_fault:
     .org 0xc00
        EXCEPTION_HANDLE(_sys_call_handler)
 
-/* ---[ 0xd00: Trap exception ]------------------------------------------ */
+/* ---[ 0xd00: Floating point exception ]--------------------------------- */
     .org 0xd00
-       UNHANDLED_EXCEPTION(_vector_0xd00)
+       EXCEPTION_HANDLE(_fpe_trap_handler)
 
 /* ---[ 0xe00: Trap exception ]------------------------------------------ */
     .org 0xe00
index 85ace93..0b7d2ca 100644 (file)
@@ -85,10 +85,39 @@ static int genregs_set(struct task_struct *target,
 }
 
 /*
+ * As OpenRISC shares GPRs and floating point registers we don't need to export
+ * the floating point registers again.  So here we only export the fpcsr special
+ * purpose register.
+ */
+static int fpregs_get(struct task_struct *target,
+                      const struct user_regset *regset,
+                      struct membuf to)
+{
+       const struct pt_regs *regs = task_pt_regs(target);
+
+       return membuf_store(&to, regs->fpcsr);
+}
+
+static int fpregs_set(struct task_struct *target,
+                      const struct user_regset *regset,
+                      unsigned int pos, unsigned int count,
+                      const void *kbuf, const void __user *ubuf)
+{
+       struct pt_regs *regs = task_pt_regs(target);
+       int ret;
+
+       /* FPCSR */
+       ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+                                &regs->fpcsr, 0, 4);
+       return ret;
+}
+
+/*
  * Define the register sets available on OpenRISC under Linux
  */
 enum or1k_regset {
        REGSET_GENERAL,
+       REGSET_FPU,
 };
 
 static const struct user_regset or1k_regsets[] = {
@@ -100,6 +129,14 @@ static const struct user_regset or1k_regsets[] = {
                            .regset_get = genregs_get,
                            .set = genregs_set,
                            },
+       [REGSET_FPU] = {
+                           .core_note_type = NT_PRFPREG,
+                           .n = sizeof(struct __or1k_fpu_state) / sizeof(long),
+                           .size = sizeof(long),
+                           .align = sizeof(long),
+                           .regset_get = fpregs_get,
+                           .set = fpregs_set,
+                           },
 };
 
 static const struct user_regset_view user_or1k_native_view = {
index 0cd04d9..9cf7fb6 100644 (file)
@@ -152,21 +152,6 @@ static void print_cpuinfo(void)
                printk(KERN_INFO "-- custom unit(s)\n");
 }
 
-static struct device_node *setup_find_cpu_node(int cpu)
-{
-       u32 hwid;
-       struct device_node *cpun;
-
-       for_each_of_cpu_node(cpun) {
-               if (of_property_read_u32(cpun, "reg", &hwid))
-                       continue;
-               if (hwid == cpu)
-                       return cpun;
-       }
-
-       return NULL;
-}
-
 void __init setup_cpuinfo(void)
 {
        struct device_node *cpu;
@@ -175,7 +160,7 @@ void __init setup_cpuinfo(void)
        int cpu_id = smp_processor_id();
        struct cpuinfo_or1k *cpuinfo = &cpuinfo_or1k[cpu_id];
 
-       cpu = setup_find_cpu_node(cpu_id);
+       cpu = of_get_cpu_node(cpu_id, NULL);
        if (!cpu)
                panic("Couldn't find CPU%d in device tree...\n", cpu_id);
 
@@ -255,7 +240,7 @@ static inline unsigned long extract_value(unsigned long reg, unsigned long mask)
 void calibrate_delay(void)
 {
        const int *val;
-       struct device_node *cpu = setup_find_cpu_node(smp_processor_id());
+       struct device_node *cpu = of_get_cpu_node(smp_processor_id(), NULL);
 
        val = of_get_property(cpu, "clock-frequency", NULL);
        if (!val)
index 80f6974..4664a18 100644 (file)
@@ -50,6 +50,7 @@ static int restore_sigcontext(struct pt_regs *regs,
        err |= __copy_from_user(regs, sc->regs.gpr, 32 * sizeof(unsigned long));
        err |= __copy_from_user(&regs->pc, &sc->regs.pc, sizeof(unsigned long));
        err |= __copy_from_user(&regs->sr, &sc->regs.sr, sizeof(unsigned long));
+       err |= __copy_from_user(&regs->fpcsr, &sc->fpu.fpcsr, sizeof(unsigned long));
 
        /* make sure the SM-bit is cleared so user-mode cannot fool us */
        regs->sr &= ~SPR_SR_SM;
@@ -112,6 +113,7 @@ static int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
        err |= __copy_to_user(sc->regs.gpr, regs, 32 * sizeof(unsigned long));
        err |= __copy_to_user(&sc->regs.pc, &regs->pc, sizeof(unsigned long));
        err |= __copy_to_user(&sc->regs.sr, &regs->sr, sizeof(unsigned long));
+       err |= __copy_to_user(&sc->fpu.fpcsr, &regs->fpcsr, sizeof(unsigned long));
 
        return err;
 }
index fd9a0f2..0aa6b07 100644 (file)
@@ -75,8 +75,9 @@ void show_registers(struct pt_regs *regs)
                in_kernel = 0;
 
        printk("CPU #: %d\n"
-              "   PC: %08lx    SR: %08lx    SP: %08lx\n",
-              smp_processor_id(), regs->pc, regs->sr, regs->sp);
+              "   PC: %08lx    SR: %08lx    SP: %08lx FPCSR: %08lx\n",
+              smp_processor_id(), regs->pc, regs->sr, regs->sp,
+              regs->fpcsr);
        printk("GPR00: %08lx GPR01: %08lx GPR02: %08lx GPR03: %08lx\n",
               0L, regs->gpr[1], regs->gpr[2], regs->gpr[3]);
        printk("GPR04: %08lx GPR05: %08lx GPR06: %08lx GPR07: %08lx\n",
@@ -242,6 +243,28 @@ asmlinkage void unhandled_exception(struct pt_regs *regs, int ea, int vector)
        die("Oops", regs, 9);
 }
 
+asmlinkage void do_fpe_trap(struct pt_regs *regs, unsigned long address)
+{
+       int code = FPE_FLTUNK;
+       unsigned long fpcsr = regs->fpcsr;
+
+       if (fpcsr & SPR_FPCSR_IVF)
+               code = FPE_FLTINV;
+       else if (fpcsr & SPR_FPCSR_OVF)
+               code = FPE_FLTOVF;
+       else if (fpcsr & SPR_FPCSR_UNF)
+               code = FPE_FLTUND;
+       else if (fpcsr & SPR_FPCSR_DZF)
+               code = FPE_FLTDIV;
+       else if (fpcsr & SPR_FPCSR_IXF)
+               code = FPE_FLTRES;
+
+       /* Clear all flags */
+       regs->fpcsr &= ~SPR_FPCSR_ALLF;
+
+       force_sig_fault(SIGFPE, code, (void __user *)regs->pc);
+}
+
 asmlinkage void do_trap(struct pt_regs *regs, unsigned long address)
 {
        force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->pc);