@@ -47,6 +47,7 @@ struct XenOverlayState {
MemoryRegion shinfo_mem;
void *shinfo_ptr;
uint64_t shinfo_gpa;
+ bool long_mode;
};
struct XenOverlayState *xen_overlay_singleton;
@@ -64,9 +65,19 @@ static void xen_overlay_realize(DeviceState *dev, Error **errp)
memory_region_set_enabled(&s->shinfo_mem, true);
s->shinfo_ptr = memory_region_get_ram_ptr(&s->shinfo_mem);
s->shinfo_gpa = INVALID_GPA;
+ s->long_mode = false;
memset(s->shinfo_ptr, 0, XEN_PAGE_SIZE);
}
+static int xen_overlay_pre_save(void *opaque)
+{
+ /* Fetch the kernel's idea of long_mode to avoid the race condition where
+ * the guest has set the hypercall page up in 64-bit mode but not yet
+ * made a hypercall by the time migration happens, so qemu hasn't yet
+ * noticed. */
+ return xen_sync_long_mode();
+}
+
static int xen_overlay_post_load(void *opaque, int version_id)
{
XenOverlayState *s = opaque;
@@ -74,6 +85,9 @@ static int xen_overlay_post_load(void *opaque, int version_id)
if (s->shinfo_gpa != INVALID_GPA) {
xen_overlay_map_page_locked(XENMAPSPACE_shared_info, 0, s->shinfo_gpa);
}
+ if (s->long_mode) {
+ xen_set_long_mode(true);
+ }
return 0;
}
@@ -88,9 +102,11 @@ static const VMStateDescription xen_overlay_vmstate = {
.version_id = 1,
.minimum_version_id = 1,
.needed = xen_overlay_is_needed,
+ .pre_save = xen_overlay_pre_save,
.post_load = xen_overlay_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT64(shinfo_gpa, XenOverlayState),
+ VMSTATE_BOOL(long_mode, XenOverlayState),
VMSTATE_END_OF_LIST()
}
};
@@ -196,3 +212,50 @@ void *xen_overlay_page_ptr(uint32_t space, uint64_t idx)
return xen_overlay_singleton->shinfo_ptr;
}
+
+int xen_sync_long_mode(void)
+{
+ int ret;
+ struct kvm_xen_hvm_attr xa = {
+ .type = KVM_XEN_ATTR_TYPE_LONG_MODE,
+ };
+
+ if (!xen_overlay_singleton) {
+ return -ENOENT;
+ }
+
+ ret = kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_GET_ATTR, &xa);
+ if (!ret) {
+ xen_overlay_singleton->long_mode = xa.u.long_mode;
+ }
+
+ return ret;
+}
+
+int xen_set_long_mode(bool long_mode)
+{
+ int ret;
+ struct kvm_xen_hvm_attr xa = {
+ .type = KVM_XEN_ATTR_TYPE_LONG_MODE,
+ .u.long_mode = long_mode,
+ };
+
+ if (!xen_overlay_singleton) {
+ return -ENOENT;
+ }
+
+ ret = kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &xa);
+ if (!ret) {
+ xen_overlay_singleton->long_mode = xa.u.long_mode;
+ }
+
+ return ret;
+}
+
+bool xen_is_long_mode(void)
+{
+ if (xen_overlay_singleton) {
+ return xen_overlay_singleton->long_mode;
+ }
+ return false;
+}
@@ -12,3 +12,7 @@
void xen_overlay_create(void);
int xen_overlay_map_page(uint32_t space, uint64_t idx, uint64_t gpa);
void *xen_overlay_page_ptr(uint32_t space, uint64_t idx);
+
+int xen_sync_long_mode(void);
+int xen_set_long_mode(bool long_mode);
+bool xen_is_long_mode(void);
@@ -17,7 +17,7 @@
#include "xen-emu.h"
#include "xen.h"
#include "trace.h"
-
+#include "hw/i386/kvm/xen_overlay.h"
#include "standard-headers/xen/version.h"
static int kvm_gva_rw(CPUState *cs, uint64_t gva, void *_buf, size_t sz,
@@ -157,6 +157,14 @@ int kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit)
if (exit->type != KVM_EXIT_XEN_HCALL)
return -1;
+ /* The kernel latches the guest 32/64 mode when the MSR is used to fill
+ * the hypercall page. So if we see a hypercall in a mode that doesn't
+ * match our own idea of the guest mode, fetch the kernel's idea of the
+ * "long mode" to remain in sync. */
+ if (exit->u.hcall.longmode != xen_is_long_mode()) {
+ xen_sync_long_mode();
+ }
+
if (!do_kvm_xen_handle_exit(cpu, exit)) {
/* Some hypercalls will be deliberately "implemented" by returning
* -ENOSYS. This case is for hypercalls which are unexpected. */