qemu/hw/core
Hogan Wang 2ebc21216f hw/pci-host: save/restore pci host config register
The pci host config register is used to save PCI address for
read/write config data. If guest writes a value to config register,
and then QEMU pauses the vcpu to migrate, after the migration, the guest
will continue to write pci config data, and the write data will be ignored
because of new qemu process losing the config register state.

To trigger the bug:
1. guest is booting in seabios.
2. guest enables the SMRAM in seabios:piix4_apmc_smm_setup, and then
   expects to disable the SMRAM by pci_config_writeb.
3. after guest writes the pci host config register, QEMU pauses vcpu
   to finish migration.
4. guest write of config data(0x0A) fails to disable the SMRAM because
   the config register state is lost.
5. guest continues to boot and crashes in ipxe option ROM due to SMRAM
   in enabled state.

Example Reproducer:

step 1. Make modifications to seabios and qemu for increase reproduction
efficiency, write 0xf0 to 0x402 port notify qemu to stop vcpu after
0x0cf8 port wrote i440 configure register. qemu stop vcpu when catch
0x402 port wrote 0xf0.

seabios:/src/hw/pci.c
@@ -52,6 +52,11 @@ void pci_config_writeb(u16 bdf, u32 addr, u8 val)
         writeb(mmconfig_addr(bdf, addr), val);
     } else {
         outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
+       if (bdf == 0 && addr == 0x72 && val == 0xa) {
+            dprintf(1, "stop vcpu\n");
+            outb(0xf0, 0x402); // notify qemu to stop vcpu
+            dprintf(1, "resume vcpu\n");
+        }
         outb(val, PORT_PCI_DATA + (addr & 3));
     }
 }

qemu:hw/char/debugcon.c
@@ -60,6 +61,9 @@ static void debugcon_ioport_write(void *opaque, hwaddr addr, uint64_t val,
     printf(" [debugcon: write addr=0x%04" HWADDR_PRIx " val=0x%02" PRIx64 "]\n", addr, val);
 #endif

+    if (ch == 0xf0) {
+        vm_stop(RUN_STATE_PAUSED);
+    }
     /* XXX this blocks entire thread. Rewrite to use
      * qemu_chr_fe_write and background I/O callbacks */
     qemu_chr_fe_write_all(&s->chr, &ch, 1);

step 2. start vm1 by the following command line, and then vm stopped.
$ qemu-system-x86_64 -machine pc-i440fx-5.0,accel=kvm\
 -netdev tap,ifname=tap-test,id=hostnet0,vhost=on,downscript=no,script=no\
 -device virtio-net-pci,netdev=hostnet0,id=net0,bus=pci.0,addr=0x13,bootindex=3\
 -device cirrus-vga,id=video0,vgamem_mb=16,bus=pci.0,addr=0x2\
 -chardev file,id=seabios,path=/var/log/test.seabios,append=on\
 -device isa-debugcon,iobase=0x402,chardev=seabios\
 -monitor stdio

step 3. start vm2 to accept vm1 state.
$ qemu-system-x86_64 -machine pc-i440fx-5.0,accel=kvm\
 -netdev tap,ifname=tap-test1,id=hostnet0,vhost=on,downscript=no,script=no\
 -device virtio-net-pci,netdev=hostnet0,id=net0,bus=pci.0,addr=0x13,bootindex=3\
 -device cirrus-vga,id=video0,vgamem_mb=16,bus=pci.0,addr=0x2\
 -chardev file,id=seabios,path=/var/log/test.seabios,append=on\
 -device isa-debugcon,iobase=0x402,chardev=seabios\
 -monitor stdio \
 -incoming tcp:127.0.0.1:8000

step 4. execute the following qmp command in vm1 to migrate.
(qemu) migrate tcp:127.0.0.1:8000

step 5. execute the following qmp command in vm2 to resume vcpu.
(qemu) cont
Before this patch, we get KVM "emulation failure" error on vm2.
This patch fixes it.

Cc: qemu-stable@nongnu.org
Signed-off-by: Hogan Wang <hogan.wang@huawei.com>
Message-Id: <20200727084621.3279-1-hogan.wang@huawei.com>
Reported-by: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2020-07-27 10:24:39 -04:00
..
bus.c qom: Use returned bool to check for failure, manual part 2020-07-10 15:18:08 +02:00
clock-vmstate.c hw/core/clock-vmstate: define a vmstate entry for clock state 2020-04-30 15:35:40 +01:00
clock.c hw/core/clock: introduce clock object 2020-04-30 11:52:28 +01:00
cpu.c qdev: Use returned bool to check for qdev_realize() etc. failure 2020-07-10 15:01:06 +02:00
fw-path-provider.c
generic-loader.c qdev: Unrealize must not fail 2020-05-15 07:08:14 +02:00
hotplug.c
irq.c
Kconfig
loader-fit.c
loader.c
machine-hmp-cmds.c qapi: Only input visitors can actually fail 2020-04-30 07:26:40 +02:00
machine-qmp-cmds.c qom: Change object_get_canonical_path_component() not to malloc 2020-07-21 16:23:43 +02:00
machine.c hw/pci-host: save/restore pci host config register 2020-07-27 10:24:39 -04:00
Makefile.objs hw/misc/empty_slot: Move the 'hw/misc' and cover in MAINTAINERS 2020-06-09 06:59:44 +02:00
nmi.c
null-machine.c hw/core/null-machine: Do not initialize unused chardev backends 2020-07-10 18:02:16 -04:00
numa.c qapi: Smooth another visitor error checking pattern 2020-07-10 15:18:08 +02:00
or-irq.c
platform-bus.c qom: Crash more nicely on object_property_get_link() failure 2020-07-10 15:18:08 +02:00
ptimer.c
qdev-clock.c qom: Drop parameter @errp of object_property_add() & friends 2020-05-15 07:07:58 +02:00
qdev-fw.c
qdev-properties-system.c error: Eliminate error_propagate() with Coccinelle, part 1 2020-07-10 15:18:08 +02:00
qdev-properties.c qdev: Ignore Error objects where the return value suffices 2020-07-10 15:18:09 +02:00
qdev.c qdev: Move doc comments from qdev.c to qdev-core.h 2020-07-20 11:35:17 +01:00
register.c hw/core/register: Add register_init_block8 helper. 2020-05-05 13:37:51 -07:00
reset.c
resettable.c
split-irq.c
stream.c hw/core: stream: Add an end-of-packet flag 2020-05-14 13:44:35 +02:00
sysbus.c sysbus: sysbus_init_child_obj() is now unused, drop 2020-06-15 22:06:04 +02:00
trace-events hw/misc/empty_slot: Move the 'hw/misc' and cover in MAINTAINERS 2020-06-09 06:59:44 +02:00
uboot_image.h
vm-change-state-handler.c
vmstate-if.c