ARCv2: fpu: preserve userspace fpu state
authorVineet Gupta <vgupta@synopsys.com>
Fri, 17 Jan 2020 23:04:03 +0000 (15:04 -0800)
committerVineet Gupta <vgupta@synopsys.com>
Sat, 18 Jan 2020 00:53:44 +0000 (16:53 -0800)
Signed-off-by: Vineet Gupta <vgupta@synopsys.com>
arch/arc/Kconfig
arch/arc/include/asm/arcregs.h
arch/arc/include/asm/fpu.h
arch/arc/kernel/Makefile
arch/arc/kernel/fpu.c
arch/arc/kernel/process.c

index c4409ea..fd35100 100644 (file)
@@ -351,9 +351,8 @@ config NODES_SHIFT
          Accessing memory beyond 1GB (with or w/o PAE) requires 2 memory
          zones.
 
-if ISA_ARCOMPACT
-
 config ARC_COMPACT_IRQ_LEVELS
+       depends on ISA_ARCOMPACT
        bool "Setup Timer IRQ as high Priority"
        # if SMP, LV2 enabled ONLY if ARC implementation has LV2 re-entrancy
        depends on !SMP
@@ -361,14 +360,10 @@ config ARC_COMPACT_IRQ_LEVELS
 config ARC_FPU_SAVE_RESTORE
        bool "Enable FPU state persistence across context switch"
        help
-         Double Precision Floating Point unit had dedicated regs which
-         need to be saved/restored across context-switch.
-         Note that ARC FPU is overly simplistic, unlike say x86, which has
-         hardware pieces to allow software to conditionally save/restore,
-         based on actual usage of FPU by a task. Thus our implemn does
-         this for all tasks in system.
-
-endif #ISA_ARCOMPACT
+         ARCompact FPU has internal registers to assist with Double precision
+         Floating Point operations. There are control and stauts registers
+         for floating point exceptions and rounding modes. These are
+         preserved across task context switch when enabled.
 
 config ARC_CANT_LLSC
        def_bool n
index 5134f0b..f7e4324 100644 (file)
@@ -39,6 +39,8 @@
 #define ARC_REG_CLUSTER_BCR    0xcf
 #define ARC_REG_AUX_ICCM       0x208   /* ICCM Base Addr (ARCv2) */
 #define ARC_REG_LPB_CTRL       0x488   /* ARCv2 Loop Buffer control */
+#define ARC_REG_FPU_CTRL       0x300
+#define ARC_REG_FPU_STATUS     0x301
 
 /* Common for ARCompact and ARCv2 status register */
 #define ARC_REG_STATUS32       0x0A
index de46742..6434725 100644 (file)
@@ -11,6 +11,8 @@
 
 #include <asm/ptrace.h>
 
+#ifdef CONFIG_ISA_ARCOMPACT
+
 /* These DPFP regs need to be saved/restored across ctx-sw */
 struct arc_fpu {
        struct {
@@ -18,11 +20,35 @@ struct arc_fpu {
        } aux_dpfp[2];
 };
 
-extern void fpu_save_restore(struct task_struct *p, struct task_struct *n);
+#define fpu_init_task(regs)
 
 #else
 
+/*
+ * ARCv2 FPU Control aux register
+ *   - bits to enable Traps on Exceptions
+ *   - Rounding mode
+ *
+ * ARCv2 FPU Status aux register
+ *   - FPU exceptions flags (Inv, Div-by-Zero, overflow, underflow, inexact)
+ *   - Flag Write Enable to clear flags explicitly (vs. by fpu instructions
+ *     only
+ */
+
+struct arc_fpu {
+       unsigned int ctrl, status;
+};
+
+extern void fpu_init_task(struct pt_regs *regs);
+
+#endif /* !CONFIG_ISA_ARCOMPACT */
+
+extern void fpu_save_restore(struct task_struct *p, struct task_struct *n);
+
+#else  /* !CONFIG_ARC_FPU_SAVE_RESTORE */
+
 #define fpu_save_restore(p, n)
+#define fpu_init_task(regs)
 
 #endif /* CONFIG_ARC_FPU_SAVE_RESTORE */
 
index e784f53..7553967 100644 (file)
@@ -23,7 +23,9 @@ obj-$(CONFIG_PERF_EVENTS)             += perf_event.o
 obj-$(CONFIG_JUMP_LABEL)               += jump_label.o
 
 obj-$(CONFIG_ARC_FPU_SAVE_RESTORE)     += fpu.o
+ifdef CONFIG_ISA_ARCOMPACT
 CFLAGS_fpu.o   += -mdpfp
+endif
 
 ifdef CONFIG_ARC_DW2_UNWIND
 CFLAGS_ctx_sw.o += -fno-omit-frame-pointer
index 7a3b56c..c67c0f0 100644 (file)
@@ -8,6 +8,8 @@
 #include <linux/sched.h>
 #include <asm/fpu.h>
 
+#ifdef CONFIG_ISA_ARCOMPACT
+
 /*
  * To save/restore FPU regs, simplest scheme would use LR/SR insns.
  * However since SR serializes the pipeline, an alternate "hack" can be used
@@ -50,3 +52,28 @@ void fpu_save_restore(struct task_struct *prev, struct task_struct *next)
                : "r" (zero), "r" (*(readfrom + 3)), "r" (*(readfrom + 2))
        );
 }
+
+#else
+
+void fpu_init_task(struct pt_regs *regs)
+{
+       /* default rounding mode */
+       write_aux_reg(ARC_REG_FPU_CTRL, 0x100);
+
+       /* set "Write enable" to allow explicit write to exception flags */
+       write_aux_reg(ARC_REG_FPU_STATUS, 0x80000000);
+}
+
+void fpu_save_restore(struct task_struct *prev, struct task_struct *next)
+{
+       struct arc_fpu *save = &prev->thread.fpu;
+       struct arc_fpu *restore = &next->thread.fpu;
+
+       save->ctrl = read_aux_reg(ARC_REG_FPU_CTRL);
+       save->status = read_aux_reg(ARC_REG_FPU_STATUS);
+
+       write_aux_reg(ARC_REG_FPU_CTRL, restore->ctrl);
+       write_aux_reg(ARC_REG_FPU_STATUS, restore->status);
+}
+
+#endif
index bfd4cbe..315528f 100644 (file)
@@ -20,6 +20,8 @@
 #include <linux/elf.h>
 #include <linux/tick.h>
 
+#include <asm/fpu.h>
+
 SYSCALL_DEFINE1(arc_settls, void *, user_tls_data_ptr)
 {
        task_thread_info(current)->thr_ptr = (unsigned int)user_tls_data_ptr;
@@ -263,7 +265,7 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long usp,
 /*
  * Do necessary setup to start up a new user task
  */
-void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long usp)
+void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long usp)
 {
        regs->sp = usp;
        regs->ret = pc;
@@ -279,6 +281,8 @@ void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long usp)
        regs->eflags = 0;
 #endif
 
+       fpu_init_task(regs);
+
        /* bogus seed values for debugging */
        regs->lp_start = 0x10;
        regs->lp_end = 0x80;