Merge remote-tracking branch 'kwolf/for-anthony' into staging

* kwolf/for-anthony:
  qemu-iotests: add backing file smaller than image test case
  stream: complete early if end of backing file is reached
  qed: refuse unaligned zero writes with a backing file
diff --git a/VERSION b/VERSION
index 069199b..99f6a00 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.1.91
+1.1.92
diff --git a/block/iscsi.c b/block/iscsi.c
index 4828b83..0b96165 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -721,7 +721,12 @@
     }
 
     itask->iscsilun->block_size = rc10->block_size;
-    itask->iscsilun->num_blocks = rc10->lba + 1;
+    if (rc10->lba == 0) {
+        /* blank disk loaded */
+        itask->iscsilun->num_blocks = 0;
+    } else {
+        itask->iscsilun->num_blocks = rc10->lba + 1;
+    }
     itask->bs->total_sectors    = itask->iscsilun->num_blocks *
                                itask->iscsilun->block_size / BDRV_SECTOR_SIZE ;
 
diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c
index 72d6e5c..c56220b 100644
--- a/hw/acpi_piix4.c
+++ b/hw/acpi_piix4.c
@@ -352,6 +352,9 @@
     pci_conf[0x5a] = 0;
     pci_conf[0x5b] = 0;
 
+    pci_conf[0x40] = 0x01; /* PM io base read only bit */
+    pci_conf[0x80] = 0;
+
     if (s->kvm_enabled) {
         /* Mark SMM as already inited (until KVM supports SMM). */
         pci_conf[0x5B] = 0x02;
@@ -391,8 +394,6 @@
     pci_conf[0x09] = 0x00;
     pci_conf[0x3d] = 0x01; // interrupt pin 1
 
-    pci_conf[0x40] = 0x01; /* PM io base read only bit */
-
     /* APM */
     apm_init(&s->apm, apm_ctrl_changed, s);
 
diff --git a/hw/arm_gic.c b/hw/arm_gic.c
index 186ac66..55871fa 100644
--- a/hw/arm_gic.c
+++ b/hw/arm_gic.c
@@ -703,6 +703,7 @@
     .parent = TYPE_ARM_GIC_COMMON,
     .instance_size = sizeof(gic_state),
     .class_init = arm_gic_class_init,
+    .class_size = sizeof(ARMGICClass),
 };
 
 static void arm_gic_register_types(void)
diff --git a/hw/esp.c b/hw/esp.c
index 52c46e6..84a4e74 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -87,7 +87,9 @@
 
     target = s->wregs[ESP_WBUSID] & BUSID_DID;
     if (s->dma) {
-        dmalen = s->rregs[ESP_TCLO] | (s->rregs[ESP_TCMID] << 8);
+        dmalen = s->rregs[ESP_TCLO];
+        dmalen |= s->rregs[ESP_TCMID] << 8;
+        dmalen |= s->rregs[ESP_TCHI] << 16;
         s->dma_memory_read(s->dma_opaque, buf, dmalen);
     } else {
         dmalen = s->ti_size;
@@ -226,6 +228,7 @@
     s->rregs[ESP_RFLAGS] = 0;
     s->rregs[ESP_TCLO] = 0;
     s->rregs[ESP_TCMID] = 0;
+    s->rregs[ESP_TCHI] = 0;
     esp_raise_irq(s);
 }
 
@@ -328,7 +331,9 @@
         return;
     }
 
-    dmalen = s->rregs[ESP_TCLO] | (s->rregs[ESP_TCMID] << 8);
+    dmalen = s->rregs[ESP_TCLO];
+    dmalen |= s->rregs[ESP_TCMID] << 8;
+    dmalen |= s->rregs[ESP_TCHI] << 16;
     if (dmalen==0) {
       dmalen=0x10000;
     }
@@ -429,6 +434,7 @@
     switch (saddr) {
     case ESP_TCLO:
     case ESP_TCMID:
+    case ESP_TCHI:
         s->rregs[ESP_RSTAT] &= ~STAT_TC;
         break;
     case ESP_FIFO:
@@ -448,6 +454,7 @@
             /* Reload DMA counter.  */
             s->rregs[ESP_TCLO] = s->wregs[ESP_TCLO];
             s->rregs[ESP_TCMID] = s->wregs[ESP_TCMID];
+            s->rregs[ESP_TCHI] = s->wregs[ESP_TCHI];
         } else {
             s->dma = 0;
         }
@@ -530,13 +537,12 @@
     case ESP_WBUSID ... ESP_WSYNO:
         break;
     case ESP_CFG1:
+    case ESP_CFG2: case ESP_CFG3:
+    case ESP_RES3: case ESP_RES4:
         s->rregs[saddr] = val;
         break;
     case ESP_WCCF ... ESP_WTEST:
         break;
-    case ESP_CFG2 ... ESP_RES4:
-        s->rregs[saddr] = val;
-        break;
     default:
         trace_esp_error_invalid_write(val, saddr);
         return;
diff --git a/hw/ivshmem.c b/hw/ivshmem.c
index 47f2a16..62fe53a 100644
--- a/hw/ivshmem.c
+++ b/hw/ivshmem.c
@@ -385,17 +385,6 @@
     s->peers[posn].nb_eventfds = 0;
 }
 
-static void setup_ioeventfds(IVShmemState *s) {
-
-    int i, j;
-
-    for (i = 0; i <= s->max_peer; i++) {
-        for (j = 0; j < s->peers[i].nb_eventfds; j++) {
-            ivshmem_add_eventfd(s, i, j);
-        }
-    }
-}
-
 /* this function increase the dynamic storage need to store data about other
  * guests */
 static void increase_dynamic_storage(IVShmemState *s, int new_min_size) {
@@ -696,10 +685,6 @@
     memory_region_init_io(&s->ivshmem_mmio, &ivshmem_mmio_ops, s,
                           "ivshmem-mmio", IVSHMEM_REG_BAR_SIZE);
 
-    if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
-        setup_ioeventfds(s);
-    }
-
     /* region for registers*/
     pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY,
                      &s->ivshmem_mmio);
diff --git a/hw/megasas.c b/hw/megasas.c
index c35a15d..c728aea 100644
--- a/hw/megasas.c
+++ b/hw/megasas.c
@@ -38,6 +38,7 @@
 #define MEGASAS_MAX_SECTORS 0xFFFF      /* No real limit */
 #define MEGASAS_MAX_ARRAYS 128
 
+#define MEGASAS_HBA_SERIAL "QEMU123456"
 #define NAA_LOCALLY_ASSIGNED_ID 0x3ULL
 #define IEEE_COMPANY_LOCALLY_ASSIGNED 0x525400
 
@@ -93,6 +94,7 @@
     int boot_event;
 
     uint64_t sas_addr;
+    char *hba_serial;
 
     uint64_t reply_queue_pa;
     void *reply_queue;
@@ -698,8 +700,7 @@
     }
 
     memcpy(info.product_name, "MegaRAID SAS 8708EM2", 20);
-    snprintf(info.serial_number, 32, "QEMU%08lx",
-             (unsigned long)s & 0xFFFFFFFF);
+    snprintf(info.serial_number, 32, "%s", s->hba_serial);
     snprintf(info.package_version, 0x60, "%s-QEMU", QEMU_VERSION);
     memcpy(info.image_component[0].name, "APP", 3);
     memcpy(info.image_component[0].version, MEGASAS_VERSION "-QEMU", 9);
@@ -2132,6 +2133,9 @@
         s->sas_addr |= (PCI_SLOT(dev->devfn) << 8);
         s->sas_addr |= PCI_FUNC(dev->devfn);
     }
+    if (!s->hba_serial) {
+	s->hba_serial = g_strdup(MEGASAS_HBA_SERIAL);
+    }
     if (s->fw_sge >= MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE) {
         s->fw_sge = MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE;
     } else if (s->fw_sge >= 128 - MFI_PASS_FRAME_SIZE) {
@@ -2166,6 +2170,7 @@
                        MEGASAS_DEFAULT_SGE),
     DEFINE_PROP_UINT32("max_cmds", MegasasState, fw_cmds,
                        MEGASAS_DEFAULT_FRAMES),
+    DEFINE_PROP_STRING("hba_serial", MegasasState, hba_serial),
     DEFINE_PROP_HEX64("sas_address", MegasasState, sas_addr, 0),
 #ifdef USE_MSIX
     DEFINE_PROP_BIT("use_msix", MegasasState, flags,
diff --git a/hw/msix.c b/hw/msix.c
index aea340b..d812094 100644
--- a/hw/msix.c
+++ b/hw/msix.c
@@ -336,6 +336,15 @@
     }
 }
 
+static void msix_clear_all_vectors(PCIDevice *dev)
+{
+    int vector;
+
+    for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
+        msix_clr_pending(dev, vector);
+    }
+}
+
 /* Clean up resources for the device. */
 void msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, MemoryRegion *pba_bar)
 {
@@ -390,7 +399,7 @@
         return;
     }
 
-    msix_free_irq_entries(dev);
+    msix_clear_all_vectors(dev);
     qemu_get_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE);
     qemu_get_buffer(f, dev->msix_pba, (n + 7) / 8);
     msix_update_function_masked(dev);
@@ -436,7 +445,7 @@
     if (!msix_present(dev)) {
         return;
     }
-    msix_free_irq_entries(dev);
+    msix_clear_all_vectors(dev);
     dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &=
 	    ~dev->wmask[dev->msix_cap + MSIX_CONTROL_OFFSET];
     memset(dev->msix_table, 0, dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE);
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 409f760..1585683 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -1461,7 +1461,7 @@
     SCSIDiskReq *r = data->r;
     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
     uint64_t sector_num;
-    uint32 nb_sectors;
+    uint32_t nb_sectors;
 
     r->req.aiocb = NULL;
     if (ret < 0) {
@@ -2421,6 +2421,7 @@
 #ifdef __linux__
 static Property scsi_block_properties[] = {
     DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.bs),
+    DEFINE_PROP_INT32("bootindex", SCSIDiskState, qdev.conf.bootindex, -1),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 8d51060..a5eb663 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -479,7 +479,8 @@
 }
 
 static Property scsi_generic_properties[] = {
-    DEFINE_BLOCK_PROPERTIES(SCSIDevice, conf),
+    DEFINE_PROP_DRIVE("drive", SCSIDevice, conf.bs),
+    DEFINE_PROP_INT32("bootindex", SCSIDevice, conf.bootindex, -1),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index 2a3d86f..b3f0710 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -131,6 +131,7 @@
     if (ret) {
         return ret;
     }
+    msix_unuse_all_vectors(&proxy->pci_dev);
     msix_load(&proxy->pci_dev, f);
     if (msix_present(&proxy->pci_dev)) {
         qemu_get_be16s(f, &proxy->vdev->config_vector);
@@ -246,6 +247,7 @@
     VirtIOPCIProxy *proxy = container_of(d, VirtIOPCIProxy, pci_dev.qdev);
     virtio_pci_stop_ioeventfd(proxy);
     virtio_reset(proxy->vdev);
+    msix_unuse_all_vectors(&proxy->pci_dev);
     proxy->flags &= ~VIRTIO_PCI_FLAG_BUS_MASTER_BUG;
 }
 
diff --git a/linux-headers/asm-s390/kvm.h b/linux-headers/asm-s390/kvm.h
index bdcbe0f..d25da59 100644
--- a/linux-headers/asm-s390/kvm.h
+++ b/linux-headers/asm-s390/kvm.h
@@ -1,7 +1,7 @@
 #ifndef __LINUX_KVM_S390_H
 #define __LINUX_KVM_S390_H
 /*
- * asm-s390/kvm.h - KVM s390 specific structures and definitions
+ * KVM s390 specific structures and definitions
  *
  * Copyright IBM Corp. 2008
  *
diff --git a/linux-headers/asm-s390/kvm_para.h b/linux-headers/asm-s390/kvm_para.h
index 8e2dd67..870051f 100644
--- a/linux-headers/asm-s390/kvm_para.h
+++ b/linux-headers/asm-s390/kvm_para.h
@@ -1,5 +1,5 @@
 /*
- * asm-s390/kvm_para.h - definition for paravirtual devices on s390
+ * definition for paravirtual devices on s390
  *
  * Copyright IBM Corp. 2008
  *
diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h
index e7d1c19..246617e 100644
--- a/linux-headers/asm-x86/kvm.h
+++ b/linux-headers/asm-x86/kvm.h
@@ -12,6 +12,7 @@
 /* Select x86 specific features in <linux/kvm.h> */
 #define __KVM_HAVE_PIT
 #define __KVM_HAVE_IOAPIC
+#define __KVM_HAVE_IRQ_LINE
 #define __KVM_HAVE_DEVICE_ASSIGNMENT
 #define __KVM_HAVE_MSI
 #define __KVM_HAVE_USER_NMI
diff --git a/linux-headers/asm-x86/kvm_para.h b/linux-headers/asm-x86/kvm_para.h
index f2ac46a..a1c3d72 100644
--- a/linux-headers/asm-x86/kvm_para.h
+++ b/linux-headers/asm-x86/kvm_para.h
@@ -22,6 +22,7 @@
 #define KVM_FEATURE_CLOCKSOURCE2        3
 #define KVM_FEATURE_ASYNC_PF		4
 #define KVM_FEATURE_STEAL_TIME		5
+#define KVM_FEATURE_PV_EOI		6
 
 /* The last 8 bits are used to indicate how to interpret the flags field
  * in pvclock structure. If no bits are set, all flags are ignored.
@@ -37,6 +38,7 @@
 #define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01
 #define MSR_KVM_ASYNC_PF_EN 0x4b564d02
 #define MSR_KVM_STEAL_TIME  0x4b564d03
+#define MSR_KVM_PV_EOI_EN      0x4b564d04
 
 struct kvm_steal_time {
 	__u64 steal;
@@ -89,5 +91,10 @@
 	__u32 enabled;
 };
 
+#define KVM_PV_EOI_BIT 0
+#define KVM_PV_EOI_MASK (0x1 << KVM_PV_EOI_BIT)
+#define KVM_PV_EOI_ENABLED KVM_PV_EOI_MASK
+#define KVM_PV_EOI_DISABLED 0x0
+
 
 #endif /* _ASM_X86_KVM_PARA_H */
diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index 5a9d4e3..4b9e575 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -617,6 +617,7 @@
 #define KVM_CAP_SIGNAL_MSI 77
 #define KVM_CAP_PPC_GET_SMMU_INFO 78
 #define KVM_CAP_S390_COW 79
+#define KVM_CAP_PPC_ALLOC_HTAB 80
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -828,6 +829,8 @@
 #define KVM_SIGNAL_MSI            _IOW(KVMIO,  0xa5, struct kvm_msi)
 /* Available with KVM_CAP_PPC_GET_SMMU_INFO */
 #define KVM_PPC_GET_SMMU_INFO	  _IOR(KVMIO,  0xa6, struct kvm_ppc_smmu_info)
+/* Available with KVM_CAP_PPC_ALLOC_HTAB */
+#define KVM_PPC_ALLOCATE_HTAB	  _IOWR(KVMIO, 0xa7, __u32)
 
 /*
  * ioctls for vcpu fds
diff --git a/linux-user/main.c b/linux-user/main.c
index 7dea084..1a1c661 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -3222,7 +3222,7 @@
     const char *help;
 };
 
-struct qemu_argument arg_table[] = {
+static const struct qemu_argument arg_table[] = {
     {"h",          "",                 false, handle_arg_help,
      "",           "print this help"},
     {"g",          "QEMU_GDB",         true,  handle_arg_gdb,
@@ -3264,7 +3264,7 @@
 
 static void usage(void)
 {
-    struct qemu_argument *arginfo;
+    const struct qemu_argument *arginfo;
     int maxarglen;
     int maxenvlen;
 
@@ -3330,7 +3330,7 @@
 {
     const char *r;
     int optind;
-    struct qemu_argument *arginfo;
+    const struct qemu_argument *arginfo;
 
     for (arginfo = arg_table; arginfo->handle_opt != NULL; arginfo++) {
         if (arginfo->env == NULL) {
@@ -3519,7 +3519,10 @@
         guest_base = init_guest_space(guest_base, reserved_va, 0,
                                       have_guest_base);
         if (guest_base == (unsigned long)-1) {
-            fprintf(stderr, "Unable to reserve guest address space\n");
+            fprintf(stderr, "Unable to reserve 0x%lx bytes of virtual address "
+                    "space for use as guest address space (check your virtual "
+                    "memory ulimit setting or reserve less using -R option)\n",
+                    reserved_va);
             exit(1);
         }
 
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 1174306..6257a04 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -7025,15 +7025,14 @@
 		tde = target_dirp;
                 while (len > 0) {
                     reclen = de->d_reclen;
-		    treclen = reclen - (2 * (sizeof(long) - sizeof(abi_long)));
+                    tnamelen = reclen - offsetof(struct linux_dirent, d_name);
+                    assert(tnamelen >= 0);
+                    treclen = tnamelen + offsetof(struct target_dirent, d_name);
+                    assert(count1 + treclen <= count);
                     tde->d_reclen = tswap16(treclen);
                     tde->d_ino = tswapal(de->d_ino);
                     tde->d_off = tswapal(de->d_off);
-		    tnamelen = treclen - (2 * sizeof(abi_long) + 2);
-		    if (tnamelen > 256)
-                        tnamelen = 256;
-                    /* XXX: may not be correct */
-                    pstrcpy(tde->d_name, tnamelen, de->d_name);
+                    memcpy(tde->d_name, de->d_name, tnamelen);
                     de = (struct linux_dirent *)((char *)de + reclen);
                     len -= reclen;
                     tde = (struct target_dirent *)((char *)tde + treclen);
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index 2cfda5a..a98cbf7 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -255,10 +255,10 @@
 };
 
 struct target_dirent {
-	abi_long	d_ino;
-	abi_long	d_off;
-	unsigned short	d_reclen;
-	char		d_name[256]; /* We must not include limits.h! */
+        abi_long        d_ino;
+        abi_long        d_off;
+        unsigned short  d_reclen;
+        char            d_name[];
 };
 
 struct target_dirent64 {
diff --git a/memory.c b/memory.c
index 643871b..d528d1f 100644
--- a/memory.c
+++ b/memory.c
@@ -426,7 +426,7 @@
         if (mrp) {
             mrp->write(mr->opaque, offset, data);
         } else if (width == 2) {
-            mrp = find_portio(mr, offset - mrio->offset, 1, false);
+            mrp = find_portio(mr, offset - mrio->offset, 1, true);
             assert(mrp);
             mrp->write(mr->opaque, offset, data & 0xff);
             mrp->write(mr->opaque, offset + 1, data >> 8);
diff --git a/qemu-ga.c b/qemu-ga.c
index 26671fe..7623079 100644
--- a/qemu-ga.c
+++ b/qemu-ga.c
@@ -438,7 +438,9 @@
     return;
 
 fail:
-    unlink(pidfile);
+    if (pidfile) {
+        unlink(pidfile);
+    }
     g_critical("failed to daemonize");
     exit(EXIT_FAILURE);
 #endif
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 120a2e3..423e009 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -33,6 +33,9 @@
 #include "hyperv.h"
 
 #include "hw/hw.h"
+#if defined(CONFIG_KVM)
+#include <linux/kvm_para.h>
+#endif
 
 /* feature flags taken from "Intel Processor Identification and the CPUID
  * Instruction" and AMD's "CPUID Specification".  In cases of disagreement
@@ -887,7 +890,17 @@
         memcpy(x86_cpu_def, def, sizeof(*def));
     }
 
-    plus_kvm_features = ~0; /* not supported bits will be filtered out later */
+#if defined(CONFIG_KVM)
+    plus_kvm_features = (1 << KVM_FEATURE_CLOCKSOURCE) |
+        (1 << KVM_FEATURE_NOP_IO_DELAY) | 
+        (1 << KVM_FEATURE_MMU_OP) |
+        (1 << KVM_FEATURE_CLOCKSOURCE2) |
+        (1 << KVM_FEATURE_ASYNC_PF) | 
+        (1 << KVM_FEATURE_STEAL_TIME) |
+        (1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT);
+#else
+    plus_kvm_features = 0;
+#endif
 
     add_flagname_to_bitmaps("hypervisor", &plus_features,
         &plus_ext_features, &plus_ext2_features, &plus_ext3_features,
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 60f9e97..0677502 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -699,6 +699,7 @@
     uint64_t system_time_msr;
     uint64_t wall_clock_msr;
     uint64_t async_pf_en_msr;
+    uint64_t pv_eoi_en_msr;
 
     uint64_t tsc;
     uint64_t tsc_deadline;
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index 696b14a..ffc294e 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -63,6 +63,7 @@
 static bool has_msr_hsave_pa;
 static bool has_msr_tsc_deadline;
 static bool has_msr_async_pf_en;
+static bool has_msr_pv_eoi_en;
 static bool has_msr_misc_enable;
 static int lm_capable_kernel;
 
@@ -455,6 +456,8 @@
 
     has_msr_async_pf_en = c->eax & (1 << KVM_FEATURE_ASYNC_PF);
 
+    has_msr_pv_eoi_en = c->eax & (1 << KVM_FEATURE_PV_EOI);
+
     cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused);
 
     for (i = 0; i <= limit; i++) {
@@ -1017,6 +1020,10 @@
             kvm_msr_entry_set(&msrs[n++], MSR_KVM_ASYNC_PF_EN,
                               env->async_pf_en_msr);
         }
+        if (has_msr_pv_eoi_en) {
+            kvm_msr_entry_set(&msrs[n++], MSR_KVM_PV_EOI_EN,
+                              env->pv_eoi_en_msr);
+        }
         if (hyperv_hypercall_available()) {
             kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_GUEST_OS_ID, 0);
             kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_HYPERCALL, 0);
@@ -1259,6 +1266,9 @@
     if (has_msr_async_pf_en) {
         msrs[n++].index = MSR_KVM_ASYNC_PF_EN;
     }
+    if (has_msr_pv_eoi_en) {
+        msrs[n++].index = MSR_KVM_PV_EOI_EN;
+    }
 
     if (env->mcg_cap) {
         msrs[n++].index = MSR_MCG_STATUS;
@@ -1338,6 +1348,9 @@
         case MSR_KVM_ASYNC_PF_EN:
             env->async_pf_en_msr = msrs[i].data;
             break;
+        case MSR_KVM_PV_EOI_EN:
+            env->pv_eoi_en_msr = msrs[i].data;
+            break;
         }
     }
 
diff --git a/target-i386/machine.c b/target-i386/machine.c
index a8be058..4771508 100644
--- a/target-i386/machine.c
+++ b/target-i386/machine.c
@@ -279,6 +279,13 @@
     return cpu->async_pf_en_msr != 0;
 }
 
+static bool pv_eoi_msr_needed(void *opaque)
+{
+    CPUX86State *cpu = opaque;
+
+    return cpu->pv_eoi_en_msr != 0;
+}
+
 static const VMStateDescription vmstate_async_pf_msr = {
     .name = "cpu/async_pf_msr",
     .version_id = 1,
@@ -290,6 +297,17 @@
     }
 };
 
+static const VMStateDescription vmstate_pv_eoi_msr = {
+    .name = "cpu/async_pv_eoi_msr",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT64(pv_eoi_en_msr, CPUX86State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static bool fpop_ip_dp_needed(void *opaque)
 {
     CPUX86State *env = opaque;
@@ -454,6 +472,9 @@
             .vmsd = &vmstate_async_pf_msr,
             .needed = async_pf_msr_needed,
         } , {
+            .vmsd = &vmstate_pv_eoi_msr,
+            .needed = pv_eoi_msr_needed,
+        } , {
             .vmsd = &vmstate_fpop_ip_dp,
             .needed = fpop_ip_dp_needed,
         }, {