MIPS: Add probing & defs for VZ & guest features
authorJames Hogan <james.hogan@imgtec.com>
Wed, 11 May 2016 14:50:30 +0000 (15:50 +0100)
committerRalf Baechle <ralf@linux-mips.org>
Fri, 13 May 2016 13:30:25 +0000 (15:30 +0200)
Add a few new cpu-features.h definitions for VZ sub-features, namely the
existence of the CP0_GuestCtl0Ext, CP0_GuestCtl1, and CP0_GuestCtl2
registers, and support for GuestID to dialias TLB entries belonging to
different guests.

Also add certain features present in the guest, with the naming scheme
cpu_guest_has_*. These are added separately to the main options bitfield
since they generally parallel similar features in the root context. A
few of these (FPU, MSA, watchpoints, perf counters, CP0_[X]ContextConfig
registers, MAAR registers, and probably others in future) can be
dynamically configured in the guest context, for which the
cpu_guest_has_dyn_* macros are added.

[ralf@linux-mips.org: Resolve merge conflict.]

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/13231/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
arch/mips/include/asm/cpu-features.h
arch/mips/include/asm/cpu-info.h
arch/mips/include/asm/cpu.h
arch/mips/kernel/cpu-probe.c

index 232b0377346a88010a349efc5e78324c6f4af2fc..e6f19fc61bf254d6fa05fdbec1164f76dca11c39 100644 (file)
 #ifndef kernel_uses_llsc
 #define kernel_uses_llsc       cpu_has_llsc
 #endif
+#ifndef cpu_has_guestctl0ext
+#define cpu_has_guestctl0ext   (cpu_data[0].options & MIPS_CPU_GUESTCTL0EXT)
+#endif
+#ifndef cpu_has_guestctl1
+#define cpu_has_guestctl1      (cpu_data[0].options & MIPS_CPU_GUESTCTL1)
+#endif
+#ifndef cpu_has_guestctl2
+#define cpu_has_guestctl2      (cpu_data[0].options & MIPS_CPU_GUESTCTL2)
+#endif
+#ifndef cpu_has_guestid
+#define cpu_has_guestid                (cpu_data[0].options & MIPS_CPU_GUESTID)
+#endif
+#ifndef cpu_has_drg
+#define cpu_has_drg            (cpu_data[0].options & MIPS_CPU_DRG)
+#endif
 #ifndef cpu_has_mips16
 #define cpu_has_mips16         (cpu_data[0].ases & MIPS_ASE_MIPS16)
 #endif
 # define cpu_has_perf          (cpu_data[0].options & MIPS_CPU_PERF)
 #endif
 
+/*
+ * Guest capabilities
+ */
+#ifndef cpu_guest_has_conf1
+#define cpu_guest_has_conf1    (cpu_data[0].guest.conf & (1 << 1))
+#endif
+#ifndef cpu_guest_has_conf2
+#define cpu_guest_has_conf2    (cpu_data[0].guest.conf & (1 << 2))
+#endif
+#ifndef cpu_guest_has_conf3
+#define cpu_guest_has_conf3    (cpu_data[0].guest.conf & (1 << 3))
+#endif
+#ifndef cpu_guest_has_conf4
+#define cpu_guest_has_conf4    (cpu_data[0].guest.conf & (1 << 4))
+#endif
+#ifndef cpu_guest_has_conf5
+#define cpu_guest_has_conf5    (cpu_data[0].guest.conf & (1 << 5))
+#endif
+#ifndef cpu_guest_has_conf6
+#define cpu_guest_has_conf6    (cpu_data[0].guest.conf & (1 << 6))
+#endif
+#ifndef cpu_guest_has_conf7
+#define cpu_guest_has_conf7    (cpu_data[0].guest.conf & (1 << 7))
+#endif
+#ifndef cpu_guest_has_fpu
+#define cpu_guest_has_fpu      (cpu_data[0].guest.options & MIPS_CPU_FPU)
+#endif
+#ifndef cpu_guest_has_watch
+#define cpu_guest_has_watch    (cpu_data[0].guest.options & MIPS_CPU_WATCH)
+#endif
+#ifndef cpu_guest_has_contextconfig
+#define cpu_guest_has_contextconfig (cpu_data[0].guest.options & MIPS_CPU_CTXTC)
+#endif
+#ifndef cpu_guest_has_segments
+#define cpu_guest_has_segments (cpu_data[0].guest.options & MIPS_CPU_SEGMENTS)
+#endif
+#ifndef cpu_guest_has_badinstr
+#define cpu_guest_has_badinstr (cpu_data[0].guest.options & MIPS_CPU_BADINSTR)
+#endif
+#ifndef cpu_guest_has_badinstrp
+#define cpu_guest_has_badinstrp        (cpu_data[0].guest.options & MIPS_CPU_BADINSTRP)
+#endif
+#ifndef cpu_guest_has_htw
+#define cpu_guest_has_htw      (cpu_data[0].guest.options & MIPS_CPU_HTW)
+#endif
+#ifndef cpu_guest_has_msa
+#define cpu_guest_has_msa      (cpu_data[0].guest.ases & MIPS_ASE_MSA)
+#endif
+#ifndef cpu_guest_has_kscr
+#define cpu_guest_has_kscr(n)  (cpu_data[0].guest.kscratch_mask & (1u << (n)))
+#endif
+#ifndef cpu_guest_has_rw_llb
+#define cpu_guest_has_rw_llb   (cpu_has_mips_r6 || (cpu_data[0].guest.options & MIPS_CPU_RW_LLB))
+#endif
+#ifndef cpu_guest_has_perf
+#define cpu_guest_has_perf     (cpu_data[0].guest.options & MIPS_CPU_PERF)
+#endif
+#ifndef cpu_guest_has_maar
+#define cpu_guest_has_maar     (cpu_data[0].guest.options & MIPS_CPU_MAAR)
+#endif
+
+/*
+ * Guest dynamic capabilities
+ */
+#ifndef cpu_guest_has_dyn_fpu
+#define cpu_guest_has_dyn_fpu  (cpu_data[0].guest.options_dyn & MIPS_CPU_FPU)
+#endif
+#ifndef cpu_guest_has_dyn_watch
+#define cpu_guest_has_dyn_watch        (cpu_data[0].guest.options_dyn & MIPS_CPU_WATCH)
+#endif
+#ifndef cpu_guest_has_dyn_contextconfig
+#define cpu_guest_has_dyn_contextconfig (cpu_data[0].guest.options_dyn & MIPS_CPU_CTXTC)
+#endif
+#ifndef cpu_guest_has_dyn_perf
+#define cpu_guest_has_dyn_perf (cpu_data[0].guest.options_dyn & MIPS_CPU_PERF)
+#endif
+#ifndef cpu_guest_has_dyn_msa
+#define cpu_guest_has_dyn_msa  (cpu_data[0].guest.ases_dyn & MIPS_ASE_MSA)
+#endif
+#ifndef cpu_guest_has_dyn_maar
+#define cpu_guest_has_dyn_maar (cpu_data[0].guest.options_dyn & MIPS_CPU_MAAR)
+#endif
+
 #endif /* __ASM_CPU_FEATURES_H */
index 392da7e6fc720dd7228e685d75a582ff8578e34a..edbe2734a1bf07ff6bef041881282d0ab968fb7c 100644 (file)
@@ -28,6 +28,15 @@ struct cache_desc {
        unsigned char flags;    /* Flags describing cache properties */
 };
 
+struct guest_info {
+       unsigned long           ases;
+       unsigned long           ases_dyn;
+       unsigned long long      options;
+       unsigned long long      options_dyn;
+       u8                      conf;
+       u8                      kscratch_mask;
+};
+
 /*
  * Flag definitions
  */
@@ -95,6 +104,11 @@ struct cpuinfo_mips {
         * htw_start/htw_stop calls
         */
        unsigned int            htw_seq;
+
+       /* VZ & Guest features */
+       struct guest_info       guest;
+       unsigned int            gtoffset_mask;
+       unsigned int            guestid_mask;
 } __attribute__((aligned(SMP_CACHE_BYTES)));
 
 extern struct cpuinfo_mips cpu_data[];
index 3971a25e07a69e17e9c588c1774a5cbfb8b29fc5..f672df8b26d0126c7dd9ffd783fad8102fd1d24c 100644 (file)
@@ -409,6 +409,11 @@ enum cpu_type_enum {
 #define MIPS_CPU_BADINSTRP     MBIT_ULL(45)    /* CPU has BadInstrP register */
 #define MIPS_CPU_CTXTC         MBIT_ULL(46)    /* CPU has [X]ConfigContext registers */
 #define MIPS_CPU_PERF          MBIT_ULL(47)    /* CPU has MIPS performance counters */
+#define MIPS_CPU_GUESTCTL0EXT  MBIT_ULL(48)    /* CPU has VZ GuestCtl0Ext register */
+#define MIPS_CPU_GUESTCTL1     MBIT_ULL(49)    /* CPU has VZ GuestCtl1 register */
+#define MIPS_CPU_GUESTCTL2     MBIT_ULL(50)    /* CPU has VZ GuestCtl2 register */
+#define MIPS_CPU_GUESTID       MBIT_ULL(51)    /* CPU uses VZ ASE GuestID feature */
+#define MIPS_CPU_DRG           MBIT_ULL(52)    /* CPU has VZ Direct Root to Guest (DRG) */
 
 /*
  * CPU ASE encodings
index 31c27576a931072214a0aebab3fbb812bd89d6cf..5ac5c3e23460b72036ccd96d25296b7848841920 100644 (file)
@@ -915,6 +915,235 @@ static void decode_configs(struct cpuinfo_mips *c)
 #endif
 }
 
+/*
+ * Probe for certain guest capabilities by writing config bits and reading back.
+ * Finally write back the original value.
+ */
+#define probe_gc0_config(name, maxconf, bits)                          \
+do {                                                                   \
+       unsigned int tmp;                                               \
+       tmp = read_gc0_##name();                                        \
+       write_gc0_##name(tmp | (bits));                                 \
+       back_to_back_c0_hazard();                                       \
+       maxconf = read_gc0_##name();                                    \
+       write_gc0_##name(tmp);                                          \
+} while (0)
+
+/*
+ * Probe for dynamic guest capabilities by changing certain config bits and
+ * reading back to see if they change. Finally write back the original value.
+ */
+#define probe_gc0_config_dyn(name, maxconf, dynconf, bits)             \
+do {                                                                   \
+       maxconf = read_gc0_##name();                                    \
+       write_gc0_##name(maxconf ^ (bits));                             \
+       back_to_back_c0_hazard();                                       \
+       dynconf = maxconf ^ read_gc0_##name();                          \
+       write_gc0_##name(maxconf);                                      \
+       maxconf |= dynconf;                                             \
+} while (0)
+
+static inline unsigned int decode_guest_config0(struct cpuinfo_mips *c)
+{
+       unsigned int config0;
+
+       probe_gc0_config(config, config0, MIPS_CONF_M);
+
+       if (config0 & MIPS_CONF_M)
+               c->guest.conf |= BIT(1);
+       return config0 & MIPS_CONF_M;
+}
+
+static inline unsigned int decode_guest_config1(struct cpuinfo_mips *c)
+{
+       unsigned int config1, config1_dyn;
+
+       probe_gc0_config_dyn(config1, config1, config1_dyn,
+                            MIPS_CONF_M | MIPS_CONF1_PC | MIPS_CONF1_WR |
+                            MIPS_CONF1_FP);
+
+       if (config1 & MIPS_CONF1_FP)
+               c->guest.options |= MIPS_CPU_FPU;
+       if (config1_dyn & MIPS_CONF1_FP)
+               c->guest.options_dyn |= MIPS_CPU_FPU;
+
+       if (config1 & MIPS_CONF1_WR)
+               c->guest.options |= MIPS_CPU_WATCH;
+       if (config1_dyn & MIPS_CONF1_WR)
+               c->guest.options_dyn |= MIPS_CPU_WATCH;
+
+       if (config1 & MIPS_CONF1_PC)
+               c->guest.options |= MIPS_CPU_PERF;
+       if (config1_dyn & MIPS_CONF1_PC)
+               c->guest.options_dyn |= MIPS_CPU_PERF;
+
+       if (config1 & MIPS_CONF_M)
+               c->guest.conf |= BIT(2);
+       return config1 & MIPS_CONF_M;
+}
+
+static inline unsigned int decode_guest_config2(struct cpuinfo_mips *c)
+{
+       unsigned int config2;
+
+       probe_gc0_config(config2, config2, MIPS_CONF_M);
+
+       if (config2 & MIPS_CONF_M)
+               c->guest.conf |= BIT(3);
+       return config2 & MIPS_CONF_M;
+}
+
+static inline unsigned int decode_guest_config3(struct cpuinfo_mips *c)
+{
+       unsigned int config3, config3_dyn;
+
+       probe_gc0_config_dyn(config3, config3, config3_dyn,
+                            MIPS_CONF_M | MIPS_CONF3_MSA | MIPS_CONF3_CTXTC);
+
+       if (config3 & MIPS_CONF3_CTXTC)
+               c->guest.options |= MIPS_CPU_CTXTC;
+       if (config3_dyn & MIPS_CONF3_CTXTC)
+               c->guest.options_dyn |= MIPS_CPU_CTXTC;
+
+       if (config3 & MIPS_CONF3_PW)
+               c->guest.options |= MIPS_CPU_HTW;
+
+       if (config3 & MIPS_CONF3_SC)
+               c->guest.options |= MIPS_CPU_SEGMENTS;
+
+       if (config3 & MIPS_CONF3_BI)
+               c->guest.options |= MIPS_CPU_BADINSTR;
+       if (config3 & MIPS_CONF3_BP)
+               c->guest.options |= MIPS_CPU_BADINSTRP;
+
+       if (config3 & MIPS_CONF3_MSA)
+               c->guest.ases |= MIPS_ASE_MSA;
+       if (config3_dyn & MIPS_CONF3_MSA)
+               c->guest.ases_dyn |= MIPS_ASE_MSA;
+
+       if (config3 & MIPS_CONF_M)
+               c->guest.conf |= BIT(4);
+       return config3 & MIPS_CONF_M;
+}
+
+static inline unsigned int decode_guest_config4(struct cpuinfo_mips *c)
+{
+       unsigned int config4;
+
+       probe_gc0_config(config4, config4,
+                        MIPS_CONF_M | MIPS_CONF4_KSCREXIST);
+
+       c->guest.kscratch_mask = (config4 & MIPS_CONF4_KSCREXIST)
+                               >> MIPS_CONF4_KSCREXIST_SHIFT;
+
+       if (config4 & MIPS_CONF_M)
+               c->guest.conf |= BIT(5);
+       return config4 & MIPS_CONF_M;
+}
+
+static inline unsigned int decode_guest_config5(struct cpuinfo_mips *c)
+{
+       unsigned int config5, config5_dyn;
+
+       probe_gc0_config_dyn(config5, config5, config5_dyn,
+                        MIPS_CONF_M | MIPS_CONF5_MRP);
+
+       if (config5 & MIPS_CONF5_MRP)
+               c->guest.options |= MIPS_CPU_MAAR;
+       if (config5_dyn & MIPS_CONF5_MRP)
+               c->guest.options_dyn |= MIPS_CPU_MAAR;
+
+       if (config5 & MIPS_CONF5_LLB)
+               c->guest.options |= MIPS_CPU_RW_LLB;
+
+       if (config5 & MIPS_CONF_M)
+               c->guest.conf |= BIT(6);
+       return config5 & MIPS_CONF_M;
+}
+
+static inline void decode_guest_configs(struct cpuinfo_mips *c)
+{
+       unsigned int ok;
+
+       ok = decode_guest_config0(c);
+       if (ok)
+               ok = decode_guest_config1(c);
+       if (ok)
+               ok = decode_guest_config2(c);
+       if (ok)
+               ok = decode_guest_config3(c);
+       if (ok)
+               ok = decode_guest_config4(c);
+       if (ok)
+               decode_guest_config5(c);
+}
+
+static inline void cpu_probe_guestctl0(struct cpuinfo_mips *c)
+{
+       unsigned int guestctl0, temp;
+
+       guestctl0 = read_c0_guestctl0();
+
+       if (guestctl0 & MIPS_GCTL0_G0E)
+               c->options |= MIPS_CPU_GUESTCTL0EXT;
+       if (guestctl0 & MIPS_GCTL0_G1)
+               c->options |= MIPS_CPU_GUESTCTL1;
+       if (guestctl0 & MIPS_GCTL0_G2)
+               c->options |= MIPS_CPU_GUESTCTL2;
+       if (!(guestctl0 & MIPS_GCTL0_RAD)) {
+               c->options |= MIPS_CPU_GUESTID;
+
+               /*
+                * Probe for Direct Root to Guest (DRG). Set GuestCtl1.RID = 0
+                * first, otherwise all data accesses will be fully virtualised
+                * as if they were performed by guest mode.
+                */
+               write_c0_guestctl1(0);
+               tlbw_use_hazard();
+
+               write_c0_guestctl0(guestctl0 | MIPS_GCTL0_DRG);
+               back_to_back_c0_hazard();
+               temp = read_c0_guestctl0();
+
+               if (temp & MIPS_GCTL0_DRG) {
+                       write_c0_guestctl0(guestctl0);
+                       c->options |= MIPS_CPU_DRG;
+               }
+       }
+}
+
+static inline void cpu_probe_guestctl1(struct cpuinfo_mips *c)
+{
+       if (cpu_has_guestid) {
+               /* determine the number of bits of GuestID available */
+               write_c0_guestctl1(MIPS_GCTL1_ID);
+               back_to_back_c0_hazard();
+               c->guestid_mask = (read_c0_guestctl1() & MIPS_GCTL1_ID)
+                                               >> MIPS_GCTL1_ID_SHIFT;
+               write_c0_guestctl1(0);
+       }
+}
+
+static inline void cpu_probe_gtoffset(struct cpuinfo_mips *c)
+{
+       /* determine the number of bits of GTOffset available */
+       write_c0_gtoffset(0xffffffff);
+       back_to_back_c0_hazard();
+       c->gtoffset_mask = read_c0_gtoffset();
+       write_c0_gtoffset(0);
+}
+
+static inline void cpu_probe_vz(struct cpuinfo_mips *c)
+{
+       cpu_probe_guestctl0(c);
+       if (cpu_has_guestctl1)
+               cpu_probe_guestctl1(c);
+
+       cpu_probe_gtoffset(c);
+
+       decode_guest_configs(c);
+}
+
 #define R4K_OPTS (MIPS_CPU_TLB | MIPS_CPU_4KEX | MIPS_CPU_4K_CACHE \
                | MIPS_CPU_COUNTER)
 
@@ -1817,6 +2046,9 @@ void cpu_probe(void)
                elf_hwcap |= HWCAP_MIPS_MSA;
        }
 
+       if (cpu_has_vz)
+               cpu_probe_vz(c);
+
        cpu_probe_vmbits(c);
 
 #ifdef CONFIG_64BIT