@@ -1082,7 +1082,20 @@ static int xen_write_msr_safe(unsigned int msr, unsigned low, unsigned high)
if (smp_processor_id() == 0)
xen_set_pat(((u64)high << 32) | low);
break;
-
+#if defined(CONFIG_X86_64)
+ case MSR_EFER:
+ /* Piggyback on the fact that in powers/cpu.c we do
+ * a wrmsr before the paravirt write_cr3. The cr3 value at that
+ * stage in saved_context.cr3 is a machine address instead of
+ * the physical address (this is done in drivers/xen/acpi.c to
+ * compensate for 'return_point' in wakeup_64.S doing an:
+ * movw %ebx, cr3). Anyhow, we piggy back here to reload the
+ * cr3 value of the saved_context. This is done b/c otherwise
+ * xen_read_cr3 will try to find the cr3 for the user-space
+ * case - and feed it to the hypercall (which would fail).
+ */
+ xen_acpi_reload_cr3_value();
+#endif
default:
ret = native_write_msr_safe(msr, low, high);
}
@@ -40,8 +40,22 @@
#ifdef CONFIG_X86_64
#include <asm/suspend_64.h>
extern struct saved_context saved_context;
-#endif
+/*
+ * This is a quirk to work-around the discontinuity of the
+ * x86_lowlevel_suspend code on x86_64. In it, after calling
+ * acpi_suspend it calls resume_point, which restores the registers
+ * and one of them is a movq X, %cr3. The movq X in that case needs
+ * to be machine address. Later on it calls to restore_processor_state()
+ * which uses the pvops variant (write_cr3) - which expects X to be
+ * physical address. This function restores the cr3 value to be a
+ * physical address to allow the pvops variant (write_cr3) to work.
+ */
+void xen_acpi_reload_cr3_value(void)
+{
+ saved_context.cr3 = read_cr3();
+}
+#endif
int xen_acpi_notify_hypervisor_state(u8 sleep_state,
u32 pm1a_cnt, u32 pm1b_cnt)
{
@@ -71,6 +85,11 @@ int xen_acpi_notify_hypervisor_state(u8 sleep_state,
{
unsigned long mfn;
+ /* Flushes out any multi-calls. */
+ arch_flush_lazy_mmu_mode();
+
+ /* Re-reads the CR3 in case of pending multicalls */
+ saved_context.cr3 = read_cr3();
/* resume_point in wakeup_64.s barrels through and does:
* movq saved_context_cr3(%rax), %rbx
* movq %rbx, %cr3
@@ -80,6 +99,14 @@ int xen_acpi_notify_hypervisor_state(u8 sleep_state,
/* and back to a physical address */
mfn = PFN_PHYS(mfn);
saved_context.cr3 = mfn;
+
+ /* Sadly, this has the end result that if we the resume code
+ * does the movq X, %cr3 and then later uses the X value to do
+ * an pvops call (write_cr3), we have a discontinuity.
+ * The movq expects a machine frame address while the pvops call
+ * expects a physical frame address. We fix this up with
+ * xen_acpi_reload_cr3_quirk which we put in wrmsr code.
+ */
}
#endif
HYPERVISOR_dom0_op(&op);
@@ -42,7 +42,9 @@
int xen_acpi_notify_hypervisor_state(u8 sleep_state,
u32 pm1a_cnt, u32 pm1b_cnd);
-
+#ifdef CONFIG_X86_64
+void xen_acpi_reload_cr3_value(void);
+#endif
static inline void xen_acpi_sleep_register(void)
{
if (xen_initial_domain())
@@ -53,6 +55,11 @@ static inline void xen_acpi_sleep_register(void)
static inline void xen_acpi_sleep_register(void)
{
}
+#ifdef CONFIG_X86_64
+static inline void xen_acpi_reload_cr3_value(void)
+{
+}
+#endif
#endif
#endif /* _XEN_ACPI_H */