lkml.org 
[lkml]   [2019]   [Aug]   [8]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH 1/2] riscv: Correct the initialized flow of FP register
Date
  The following two reasons cause FP registers are sometimes not
initialized before starting the user program.
1. Currently, the FP context is initialized in flush_thread() function
and we expect these initial values to be restored to FP register when
doing FP context switch. However, the FP context switch only occurs in
switch_to function. Hence, if this process does not be scheduled out
and scheduled in before entering the user space, the FP registers
have no chance to initialize.
2. In flush_thread(), the state of reg->sstatus.FS inherits from the
parent. Hence, the state of reg->sstatus.FS may be dirty. If this
process is scheduled out during flush_thread() and initializing the
FP register, the fstate_save() in switch_to will corrupt the FP context
which has been initialized until flush_thread().

To solve the 1st case, the initialization of the FP register will be
completed in start_thread(). It makes sure all FP registers are initialized
before starting the user program. For the 2nd case, the state of
reg->sstatus.FS in start_thread will be set to SR_FS_OFF to prevent this
process from corrupting FP context in doing context save. The FP state is
set to SR_FS_INITIAL in start_trhead().

Tested on both QEMU and HiFive Unleashed using BBL + Linux.

Signed-off-by: Vincent Chen <vincent.chen@sifive.com>
---
arch/riscv/include/asm/switch_to.h | 6 ++++++
arch/riscv/kernel/process.c | 13 +++++++++++--
2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/arch/riscv/include/asm/switch_to.h b/arch/riscv/include/asm/switch_to.h
index 853b65e..d5fe573 100644
--- a/arch/riscv/include/asm/switch_to.h
+++ b/arch/riscv/include/asm/switch_to.h
@@ -19,6 +19,12 @@ static inline void __fstate_clean(struct pt_regs *regs)
regs->sstatus |= (regs->sstatus & ~(SR_FS)) | SR_FS_CLEAN;
}

+static inline void fstate_off(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ regs->sstatus = (regs->sstatus & ~(SR_FS)) | SR_FS_OFF;
+}
+
static inline void fstate_save(struct task_struct *task,
struct pt_regs *regs)
{
diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c
index f23794b..e3077ee 100644
--- a/arch/riscv/kernel/process.c
+++ b/arch/riscv/kernel/process.c
@@ -64,8 +64,16 @@ void start_thread(struct pt_regs *regs, unsigned long pc,
unsigned long sp)
{
regs->sstatus = SR_SPIE;
- if (has_fpu)
+ if (has_fpu) {
regs->sstatus |= SR_FS_INITIAL;
+#ifdef CONFIG_FPU
+ /*
+ * Restore the initial value to the FP register
+ * before starting the user program.
+ */
+ fstate_restore(current, regs);
+#endif
+ }
regs->sepc = pc;
regs->sp = sp;
set_fs(USER_DS);
@@ -75,10 +83,11 @@ void flush_thread(void)
{
#ifdef CONFIG_FPU
/*
- * Reset FPU context
+ * Reset FPU state and context
* frm: round to nearest, ties to even (IEEE default)
* fflags: accrued exceptions cleared
*/
+ fstate_off(current, task_pt_regs(current));
memset(&current->thread.fstate, 0, sizeof(current->thread.fstate));
#endif
}
--
2.7.4
\
 
 \ /
  Last update: 2019-08-08 10:01    [W:0.139 / U:0.520 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site