|
@@ -495,37 +495,123 @@ long machine_check_early(struct pt_regs *regs)
|
|
|
return handled;
|
|
|
}
|
|
|
|
|
|
-long hmi_exception_realmode(struct pt_regs *regs)
|
|
|
+/* Possible meanings for HMER_DEBUG_TRIG bit being set on POWER9 */
|
|
|
+static enum {
|
|
|
+ DTRIG_UNKNOWN,
|
|
|
+ DTRIG_VECTOR_CI, /* need to emulate vector CI load instr */
|
|
|
+ DTRIG_SUSPEND_ESCAPE, /* need to escape from TM suspend mode */
|
|
|
+} hmer_debug_trig_function;
|
|
|
+
|
|
|
+static int init_debug_trig_function(void)
|
|
|
{
|
|
|
- __this_cpu_inc(irq_stat.hmi_exceptions);
|
|
|
-
|
|
|
-#ifdef CONFIG_PPC_BOOK3S_64
|
|
|
- /* Workaround for P9 vector CI loads (see p9_hmi_special_emu) */
|
|
|
- if (pvr_version_is(PVR_POWER9)) {
|
|
|
- unsigned long hmer = mfspr(SPRN_HMER);
|
|
|
-
|
|
|
- /* Do we have the debug bit set */
|
|
|
- if (hmer & PPC_BIT(17)) {
|
|
|
- hmer &= ~PPC_BIT(17);
|
|
|
- mtspr(SPRN_HMER, hmer);
|
|
|
-
|
|
|
- /*
|
|
|
- * Now to avoid problems with soft-disable we
|
|
|
- * only do the emulation if we are coming from
|
|
|
- * user space
|
|
|
- */
|
|
|
- if (user_mode(regs))
|
|
|
- local_paca->hmi_p9_special_emu = 1;
|
|
|
-
|
|
|
- /*
|
|
|
- * Don't bother going to OPAL if that's the
|
|
|
- * only relevant bit.
|
|
|
- */
|
|
|
- if (!(hmer & mfspr(SPRN_HMEER)))
|
|
|
- return local_paca->hmi_p9_special_emu;
|
|
|
+ int pvr;
|
|
|
+ struct device_node *cpun;
|
|
|
+ struct property *prop = NULL;
|
|
|
+ const char *str;
|
|
|
+
|
|
|
+ /* First look in the device tree */
|
|
|
+ preempt_disable();
|
|
|
+ cpun = of_get_cpu_node(smp_processor_id(), NULL);
|
|
|
+ if (cpun) {
|
|
|
+ of_property_for_each_string(cpun, "ibm,hmi-special-triggers",
|
|
|
+ prop, str) {
|
|
|
+ if (strcmp(str, "bit17-vector-ci-load") == 0)
|
|
|
+ hmer_debug_trig_function = DTRIG_VECTOR_CI;
|
|
|
+ else if (strcmp(str, "bit17-tm-suspend-escape") == 0)
|
|
|
+ hmer_debug_trig_function = DTRIG_SUSPEND_ESCAPE;
|
|
|
}
|
|
|
+ of_node_put(cpun);
|
|
|
+ }
|
|
|
+ preempt_enable();
|
|
|
+
|
|
|
+ /* If we found the property, don't look at PVR */
|
|
|
+ if (prop)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ pvr = mfspr(SPRN_PVR);
|
|
|
+ /* Check for POWER9 Nimbus (scale-out) */
|
|
|
+ if ((PVR_VER(pvr) == PVR_POWER9) && (pvr & 0xe000) == 0) {
|
|
|
+ /* DD2.2 and later */
|
|
|
+ if ((pvr & 0xfff) >= 0x202)
|
|
|
+ hmer_debug_trig_function = DTRIG_SUSPEND_ESCAPE;
|
|
|
+ /* DD2.0 and DD2.1 - used for vector CI load emulation */
|
|
|
+ else if ((pvr & 0xfff) >= 0x200)
|
|
|
+ hmer_debug_trig_function = DTRIG_VECTOR_CI;
|
|
|
+ }
|
|
|
+
|
|
|
+ out:
|
|
|
+ switch (hmer_debug_trig_function) {
|
|
|
+ case DTRIG_VECTOR_CI:
|
|
|
+ pr_debug("HMI debug trigger used for vector CI load\n");
|
|
|
+ break;
|
|
|
+ case DTRIG_SUSPEND_ESCAPE:
|
|
|
+ pr_debug("HMI debug trigger used for TM suspend escape\n");
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
}
|
|
|
-#endif /* CONFIG_PPC_BOOK3S_64 */
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+__initcall(init_debug_trig_function);
|
|
|
+
|
|
|
+/*
|
|
|
+ * Handle HMIs that occur as a result of a debug trigger.
|
|
|
+ * Return values:
|
|
|
+ * -1 means this is not a HMI cause that we know about
|
|
|
+ * 0 means no further handling is required
|
|
|
+ * 1 means further handling is required
|
|
|
+ */
|
|
|
+long hmi_handle_debugtrig(struct pt_regs *regs)
|
|
|
+{
|
|
|
+ unsigned long hmer = mfspr(SPRN_HMER);
|
|
|
+ long ret = 0;
|
|
|
+
|
|
|
+ /* HMER_DEBUG_TRIG bit is used for various workarounds on P9 */
|
|
|
+ if (!((hmer & HMER_DEBUG_TRIG)
|
|
|
+ && hmer_debug_trig_function != DTRIG_UNKNOWN))
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ hmer &= ~HMER_DEBUG_TRIG;
|
|
|
+ /* HMER is a write-AND register */
|
|
|
+ mtspr(SPRN_HMER, ~HMER_DEBUG_TRIG);
|
|
|
+
|
|
|
+ switch (hmer_debug_trig_function) {
|
|
|
+ case DTRIG_VECTOR_CI:
|
|
|
+ /*
|
|
|
+ * Now to avoid problems with soft-disable we
|
|
|
+ * only do the emulation if we are coming from
|
|
|
+ * host user space
|
|
|
+ */
|
|
|
+ if (regs && user_mode(regs))
|
|
|
+ ret = local_paca->hmi_p9_special_emu = 1;
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * See if any other HMI causes remain to be handled
|
|
|
+ */
|
|
|
+ if (hmer & mfspr(SPRN_HMEER))
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Return values:
|
|
|
+ */
|
|
|
+long hmi_exception_realmode(struct pt_regs *regs)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ __this_cpu_inc(irq_stat.hmi_exceptions);
|
|
|
+
|
|
|
+ ret = hmi_handle_debugtrig(regs);
|
|
|
+ if (ret >= 0)
|
|
|
+ return ret;
|
|
|
|
|
|
wait_for_subcore_guest_exit();
|
|
|
|