On Fri, Apr 29, 2016 at 02:46:00PM +0100, James Hogan wrote: > 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. Okay, so it turns out EBase.WG can be present on MIPS32 too, to allow writing of bits 31:30 (thanks Matt!), so this needs a little more thought. Cheers James > > 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 >
Attachment:
signature.asc
Description: Digital signature