diff mbox series

[022/120] MIPS: R5900: Support 64-bit inq() and outq() macros in 32-bit kernels

Message ID 7ea8b1211bceb8193154b51ad4241e7c0b86547d.1567326213.git.noring@nocrew.org (mailing list archive)
State RFC
Headers show
Series Linux for the PlayStation 2 | expand

Commit Message

Fredrik Noring Sept. 1, 2019, 3:46 p.m. UTC
PlayStation 2 hardware such as the Graphics Synthesizer requires 64-bit
register reads and writes[1], also in 32-bit kernels. Interrupts must be
disabled when manipulating 64-bit registers unless the kernel saves and
restores 64-bit registers in the interrupt and context switch handlers.

References:

[1] "EE User's Manual", version 6.0, Sony Computer Entertainment Inc.,
    p. 26: GS privileged registers must be accessed using LD/SD
    instructions.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/io.h | 54 +++++++++++++++++++++++++++++++++-----
 1 file changed, 47 insertions(+), 7 deletions(-)

Comments

Jiaxun Yang Sept. 4, 2019, 1:04 a.m. UTC | #1
在 2019/9/1 23:46, Fredrik Noring 写道:
> PlayStation 2 hardware such as the Graphics Synthesizer requires 64-bit
> register reads and writes[1], also in 32-bit kernels. Interrupts must be
> disabled when manipulating 64-bit registers unless the kernel saves and
> restores 64-bit registers in the interrupt and context switch handlers.

Hi Fredrik,

Why don't we just build a 64bit kernel rather than do these hacks?

Any hardware/firmware issue blocks 64bit kernel?

--

Jiaxun Yang

> References:
>
> [1] "EE User's Manual", version 6.0, Sony Computer Entertainment Inc.,
>      p. 26: GS privileged registers must be accessed using LD/SD
>      instructions.
>
> Signed-off-by: Fredrik Noring<noring@nocrew.org>
> ---
Maciej W. Rozycki Sept. 4, 2019, 4 p.m. UTC | #2
On Wed, 4 Sep 2019, Jiaxun Yang wrote:

> > PlayStation 2 hardware such as the Graphics Synthesizer requires 64-bit
> > register reads and writes[1], also in 32-bit kernels. Interrupts must be
> > disabled when manipulating 64-bit registers unless the kernel saves and
> > restores 64-bit registers in the interrupt and context switch handlers.
> 
> Hi Fredrik,
> 
> Why don't we just build a 64bit kernel rather than do these hacks?
> 
> Any hardware/firmware issue blocks 64bit kernel?

 One issue I recall with the R5900 is $pc is 32-bit (as if with 
CP0.Status.PX=1 on modern hardware) and is zero- rather than sign-extended 
when copied to a GPR with instructions like JALR, BLTZAL, etc.  This is 
contrary to MIPS architecture requirements and unlike other 64-bit MIPS 
processors.

 This does not cause an issue in the user mode (because USEG addresses 
have bit #31 set to 0 anyway) or with the JR and JALR instructions 
(because these ignore the upper 32 bits in the source GPR).  This however 
breaks in the kernel mode whenever the value retrieved from $pc is used 
for any kind of calculation, meaning that the kernel will have to be 
audited for such use before we can run 64-bit kernel code.

 This means it's much easier to have a 32-bit kernel running, with a small 
and simple sanitanisation change posted as 003/120, and have all the 
platform and driver infrastructure verified before this processor quirk is 
addressed.

  Maciej
diff mbox series

Patch

diff --git a/arch/mips/include/asm/io.h b/arch/mips/include/asm/io.h
index 97a280640daf..f7115472f530 100644
--- a/arch/mips/include/asm/io.h
+++ b/arch/mips/include/asm/io.h
@@ -420,10 +420,32 @@  static inline void pfx##out##bwlq##p(type val, unsigned long port)	\
 									\
 	__val = pfx##ioswab##bwlq(__addr, val);				\
 									\
-	/* Really, we want this to be atomic */				\
-	BUILD_BUG_ON(sizeof(type) > sizeof(unsigned long));		\
+	if (sizeof(type) != sizeof(u64) || sizeof(u64) == sizeof(long))	{ \
+		*__addr = __val;					\
+	} else if (cpu_has_64bits) {					\
+		unsigned long __flags;					\
+		type __tmp;						\
 									\
-	*__addr = __val;						\
+		/* Manipulating 64-bit registers in a 32-bit kernel */	\
+		/* requires disabling interrupts, since only 32-bit */	\
+		/* registers are saved/restored by interrupts. */	\
+		local_irq_save(__flags);				\
+		__asm__ __volatile__(					\
+			".set	push"		"\t\t# __writeq""\n\t"	\
+			".set	mips3"				"\n\t"	\
+			"dsll32	%L0, %L0, 0"			"\n\t"	\
+			"dsrl32	%L0, %L0, 0"			"\n\t"	\
+			"dsll32	%M0, %M0, 0"			"\n\t"	\
+			"or	%L0, %L0, %M0"			"\n\t"	\
+			"sd	%L0, %2"			"\n\t"	\
+			"sll	%L0, %L0, 0"			"\n\t"	\
+			"sll	%M0, %M0, 0"			"\n\t"	\
+			".set	pop"				"\n"	\
+			: "=r" (__tmp)					\
+			: "0" (__val), "m" (*__addr));			\
+		local_irq_restore(__flags);				\
+	} else								\
+		BUG();							\
 }									\
 									\
 static inline type pfx##in##bwlq##p(unsigned long port)			\
@@ -433,12 +455,30 @@  static inline type pfx##in##bwlq##p(unsigned long port)			\
 									\
 	__addr = (void *)__swizzle_addr_##bwlq(mips_io_port_base + port); \
 									\
-	BUILD_BUG_ON(sizeof(type) > sizeof(unsigned long));		\
-									\
 	if (barrier)							\
 		iobarrier_rw();						\
 									\
-	__val = *__addr;						\
+	if (sizeof(type) != sizeof(u64) || sizeof(u64) == sizeof(long))	{ \
+		__val = *__addr;					\
+	} else if (cpu_has_64bits) {					\
+		unsigned long __flags;					\
+									\
+		/* Manipulating 64-bit registers in a 32-bit kernel */	\
+		/* requires disabling interrupts, since only 32-bit */	\
+		/* registers are saved/restored by interrupts. */	\
+		local_irq_save(__flags);				\
+		__asm__ __volatile__(					\
+			".set	push"		"\t\t# __outq"	"\n\t"	\
+			".set	mips3"				"\n\t"	\
+			"ld	%L0, %1"			"\n\t"	\
+			"dsra32	%M0, %L0, 0"			"\n\t"	\
+			"sll	%L0, %L0, 0"			"\n\t"	\
+			".set	pop"				"\n"	\
+			: "=r" (__val)					\
+			: "m" (*__addr));				\
+		local_irq_restore(__flags);				\
+	} else								\
+		BUG();							\
 									\
 	/* prevent prefetching of coherent DMA data prematurely */	\
 	if (!relax)							\
@@ -478,7 +518,7 @@  __BUILD_MEMORY_PFX(__mem_, q, u64, 0)
 BUILDIO_IOPORT(b, u8)
 BUILDIO_IOPORT(w, u16)
 BUILDIO_IOPORT(l, u32)
-#ifdef CONFIG_64BIT
+#if defined(CONFIG_64BIT) || defined(CONFIG_CPU_R5900)
 BUILDIO_IOPORT(q, u64)
 #endif