[PATCH 2/5] MIPS: Add defs & probing of 64-bit CP0_EBase

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

 



MIPS64r2 and later cores may optionally have a 64-bit CP0_EBase
register, with a write gate (WG) bit to allow the upper half to be
written. The presence of this feature will need to be known about for VZ
support in order to correctly save and restore the guest CP0_EBase
register, so add CPU feature definitions and probing for this
capability.

Probing the WG bit is a bit fiddly, since 64-bit COP0 register access
instructions were UNDEFINED for 32-bit registers prior to MIPS r6, and
it'd be nice to be able to probe without clobbering the existing state,
so there are 3 potential paths:

- If we do a 32-bit read of CP0_EBase and the WG bit is already set, the
  register must be 64-bit.

- On MIPS r6 we can do a 64-bit read-modify-write to set CP0_EBase.WG,
  since the upper bits will read 0 and be ignored on write if the
  register is 32-bit.

- On pre-r6 cores, we do a 32-bit read-modify-write of CP0_EBase. This
  avoids the potentially UNDEFINED behaviour, but will clobber the upper
  32-bits of CP0_EBase if it isn't a simple sign extension (which also
  requires us to ensure BEV=1 or modifying the exception base would be
  UNDEFINED too). It is hopefully unlikely a bootloader would set up
  CP0_EBase to a 64-bit segment and leave WG=0.

Signed-off-by: James Hogan <james.hogan@xxxxxxxxxx>
Cc: Ralf Baechle <ralf@xxxxxxxxxxxxxx>
Cc: linux-mips@xxxxxxxxxxxxxx
---
 arch/mips/include/asm/cpu-features.h |  4 ++++
 arch/mips/include/asm/cpu.h          |  1 +
 arch/mips/include/asm/mipsregs.h     |  3 +++
 arch/mips/kernel/cpu-probe.c         | 35 +++++++++++++++++++++++++++++++++++
 4 files changed, 43 insertions(+)

diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h
index da92d513a395..a3d3de8ab145 100644
--- a/arch/mips/include/asm/cpu-features.h
+++ b/arch/mips/include/asm/cpu-features.h
@@ -432,4 +432,8 @@
 #define cpu_has_nan_2008	(cpu_data[0].options & MIPS_CPU_NAN_2008)
 #endif
 
+#ifndef cpu_has_ebase64
+# define cpu_has_ebase64	(cpu_data[0].options & MIPS_CPU_EBASE64)
+#endif
+
 #endif /* __ASM_CPU_FEATURES_H */
diff --git a/arch/mips/include/asm/cpu.h b/arch/mips/include/asm/cpu.h
index 79ce9ae0a3c7..11a7f78bb1f7 100644
--- a/arch/mips/include/asm/cpu.h
+++ b/arch/mips/include/asm/cpu.h
@@ -403,6 +403,7 @@ enum cpu_type_enum {
 #define MIPS_CPU_NAN_2008	MBIT_ULL(39)	/* 2008 NaN implemented */
 #define MIPS_CPU_VP		MBIT_ULL(40)	/* MIPSr6 Virtual Processors (multi-threading) */
 #define MIPS_CPU_LDPTE		MBIT_ULL(41)	 /* CPU has ldpte/lddir instructions */
+#define MIPS_CPU_EBASE64	MBIT_ULL(42)	/* CPU has 64-bit EBase */
 
 /*
  * CPU ASE encodings
diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h
index 24904f1f6fe1..cc17f4886f00 100644
--- a/arch/mips/include/asm/mipsregs.h
+++ b/arch/mips/include/asm/mipsregs.h
@@ -1456,6 +1456,9 @@ do {									\
 #define read_c0_ebase()		__read_32bit_c0_register($15, 1)
 #define write_c0_ebase(val)	__write_32bit_c0_register($15, 1, val)
 
+#define read_c0_ebase_64()	__read_64bit_c0_register($15, 1)
+#define write_c0_ebase_64(val)	__write_64bit_c0_register($15, 1, val)
+
 #define read_c0_cdmmbase()	__read_ulong_c0_register($15, 2)
 #define write_c0_cdmmbase(val)	__write_ulong_c0_register($15, 2, val)
 
diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c
index b766952afc5c..eb62c730b97f 100644
--- a/arch/mips/kernel/cpu-probe.c
+++ b/arch/mips/kernel/cpu-probe.c
@@ -845,6 +845,41 @@ static void decode_configs(struct cpuinfo_mips *c)
 	if (ok)
 		ok = decode_config5(c);
 
+	/* Probe the size of the EBase register */
+	if (config_enabled(CONFIG_64BIT) && cpu_has_mips_r2_r6) {
+		unsigned long ebase;
+		unsigned int status;
+
+		/* {read,write}_c0_ebase_64() may be UNDEFINED prior to r6 */
+		ebase = cpu_has_mips64r6 ? read_c0_ebase_64()
+					 : (s32)read_c0_ebase();
+		if (ebase & MIPS_EBASE_WG) {
+			/* WG bit already set, we can avoid the clumsy probe */
+			c->options |= MIPS_CPU_EBASE64;
+		} else {
+			/* Its UNDEFINED to change EBase while BEV=0 */
+			status = read_c0_status();
+			write_c0_status(status | ST0_BEV);
+			irq_enable_hazard();
+			/*
+			 * On pre-r6 cores, this may well clobber the upper bits
+			 * of EBase. This is hard to avoid without potentially
+			 * hitting UNDEFINED dm*c0 behaviour if EBase is 32-bit.
+			 */
+			if (cpu_has_mips64r6)
+				write_c0_ebase_64(ebase | MIPS_EBASE_WG);
+			else
+				write_c0_ebase(ebase | MIPS_EBASE_WG);
+			back_to_back_c0_hazard();
+			/* Restore BEV */
+			write_c0_status(status);
+			if (read_c0_ebase() & MIPS_EBASE_WG) {
+				c->options |= MIPS_CPU_EBASE64;
+				write_c0_ebase(ebase);
+			}
+		}
+	}
+
 	mips_probe_watch_registers(c);
 
 #ifndef CONFIG_MIPS_CPS
-- 
2.4.10





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

  Powered by Linux