Re: [RFC] MIPS: R5900: Workaround for saving and restoring FPU registers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Fredrik,

> Would you be able to elaborate on the following change with a workaround for
> saving and restoring R5900 FPU registers? Is this problem documented in your
> copy of Sony's Linux Toolkit Restriction manual?
>     
>     Fixed saving and restoring of FPU registers. Odd FPU registers were
>     lost on exceptions and when simulating 64 bit FPU. Debian 5.0 mipsel
>     uses MOV.D to move FPU registers. This is not supported by R5900 and
>     failed in the simulation because of the bug above.

 I thought we agreed the R5900 FPU is unusable for regular Linux software 
and decided to go for full FPU emulation unconditionally.

 We could add a special R5900 mode, denoted with a dedicated 
Tag_GNU_MIPS_ABI_FP attribute and MIPS ABI Flags FP ABI setting, which 
would then enable hardware FPU for the selected task, but I suggest we 
defer any actual code proposals until we have all the design details 
settled.

> diff --git a/arch/mips/include/asm/asmmacro.h b/arch/mips/include/asm/asmmacro.h
> index 8d1e30b94c2d..a67ef7964bc1 100644
> --- a/arch/mips/include/asm/asmmacro.h
> +++ b/arch/mips/include/asm/asmmacro.h
> @@ -141,6 +141,52 @@
>  	.set	pop
>  	.endm
>  
> +#ifdef CONFIG_CPU_R5900
> +	/*
> +	 * Kernel expects that floating point registers are saved as 64-bit
> +	 * with the sdc1 instruction, but this is not working with R5900.
> +	 * The 64-bit write is simulated as two 32-bit writes.
> +	 */
> +	.macro fpu_save_double thread status tmp1=t0
> +	.set push
> +	SET_HARDFLOAT
> +	cfc1	\tmp1,  fcr31
> +	swc1	$f0,  THREAD_FPR0(\thread)
> +	swc1	$f1,  (THREAD_FPR0 + 4)(\thread)
> +	swc1	$f2,  THREAD_FPR2(\thread)
> +	swc1	$f3,  (THREAD_FPR2 + 4)(\thread)

 Etc. -- can you reuse MIPS I code here, i.e. use S.D?  GAS should be 
doing the right thing with `-march=r5900' (if not, then it has a bug).

> @@ -200,6 +247,52 @@
>  	.set	pop
>  	.endm
>  
> +#ifdef CONFIG_CPU_R5900
> +	/*
> +	 * Kernel expects that floating point registers are read as 64-bit
> +	 * with the ldc1 instruction, but this is not working with R5900.
> +	 * The 64-bit read is simulated as two 32-bit reads.
> +	 */
> +	.macro	fpu_restore_double thread status tmp=t0
> +	.set push
> +	SET_HARDFLOAT
> +	lw	\tmp, THREAD_FCR31(\thread)
> +	lwc1	$f0,  THREAD_FPR0(\thread)
> +	lwc1	$f1,  (THREAD_FPR0 + 4)(\thread)
> +	lwc1	$f2,  THREAD_FPR2(\thread)
> +	lwc1	$f3,  (THREAD_FPR2 + 4)(\thread)

 Likewise L.D.

> diff --git a/arch/mips/kernel/r5900_fpu.S b/arch/mips/kernel/r5900_fpu.S
> new file mode 100644
> index 000000000000..d4fdc823444d
> --- /dev/null
> +++ b/arch/mips/kernel/r5900_fpu.S
> @@ -0,0 +1,389 @@
> +/*
> + * FPU handling on MIPS r5900. Copied from r4k_fpu.c.
> + *
> + * Copyright (C) 2010-2013 Jürgen Urban
> + *
> + * SPDX-License-Identifier: GPL-2.0
> + */
> +
> +#include <asm/asm.h>
> +#include <asm/asmmacro.h>
> +#include <asm/errno.h>
> +#include <asm/fpregdef.h>
> +#include <asm/mipsregs.h>
> +#include <asm/asm-offsets.h>
> +#include <asm/regdef.h>
> +
> +	.macro	EX insn, reg, src
> +	.set	push
> +	SET_HARDFLOAT
> +	.set	nomacro
> +	/* In an error exception handler the user space could be uncached. */
> +	sync.l
> +.ex\@:	\insn	\reg, \src
> +	.set	pop
> +	.section __ex_table,"a"
> +	PTR	.ex\@, fault
> +	.previous
> +	.endm
> +
> +	.set	noreorder
> +	.set	arch=r5900
> +
> +/*
> + * Save a thread's fp context.
> + */
> +LEAF(_save_fp)
> +	fpu_save_double a0 t0 t1		# clobbers t1
> +	jr	ra
> +	END(_save_fp)
> +
> +/*
> + * Restore a thread's fp context.
> + */
> +LEAF(_restore_fp)
> +	fpu_restore_double a0 t0 t1		# clobbers t1
> +	jr	ra
> +	END(_restore_fp)
> +
> +LEAF(_save_fp_context)
> +	.set	push
> +	SET_HARDFLOAT
> +	cfc1	t1, fcr31
> +	.set	pop
> +
> +	/* Store the 32 32-bit registers */
> +	EX	swc1 $f0, SC_FPREGS+0(a0)
> +	EX	swc1 $f1, SC_FPREGS+4(a0)
> +	EX	swc1 $f2, SC_FPREGS+16(a0)
> +	EX	swc1 $f3, SC_FPREGS+20(a0)

 Likewise.

> +/*
> + * Restore FPU state:
> + *  - fp gp registers
> + *  - cp1 status/control register
> + */
> +LEAF(_restore_fp_context)
> +	EX	lw t0, SC_FPC_CSR(a0)
> +	EX	lwc1 $f0, SC_FPREGS+0(a0)
> +	EX	lwc1 $f1, SC_FPREGS+4(a0)
> +	EX	lwc1 $f2, SC_FPREGS+16(a0)
> +	EX	lwc1 $f3, SC_FPREGS+20(a0)

 Likewise.

> +#if defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_CPU_MIPS32_R6)
> +	.set    push
> +	.set    MIPS_ISA_LEVEL_RAW
> +	.set	fp=64
> +	sll     t0, t0, 5			# is Status.FR set?
> +	bgez    t0, 1f				# no: skip setting upper 32b
> +
> +	mthc1   t1, $f0
> +	mthc1   t1, $f1
> +	mthc1   t1, $f2
> +	mthc1   t1, $f3

 You surely do not want all this MIPS32r2 stuff, or do you?

  Maciej


[Index of Archives]     [Linux MIPS Home]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Linux]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux