diff options
-rw-r--r-- | default-configs/pci.mak | 1 | ||||
-rw-r--r-- | docs/specs/pci-testdev.txt | 26 | ||||
-rw-r--r-- | hw/misc/Makefile.objs | 1 | ||||
-rw-r--r-- | hw/misc/pci-testdev.c | 325 | ||||
-rw-r--r-- | include/hw/pci/pci.h | 1 | ||||
-rw-r--r-- | include/sysemu/kvm.h | 4 | ||||
-rw-r--r-- | kvm-all.c | 133 | ||||
-rw-r--r-- | kvm-stub.c | 10 | ||||
-rwxr-xr-x | scripts/kvm/vmxcap | 26 | ||||
-rw-r--r-- | target-i386/cpu.h | 1 | ||||
-rw-r--r-- | target-i386/kvm.c | 13 | ||||
-rw-r--r-- | target-i386/machine.c | 21 |
12 files changed, 480 insertions, 82 deletions
diff --git a/default-configs/pci.mak b/default-configs/pci.mak index f5f100ecb0..b608f31c9a 100644 --- a/default-configs/pci.mak +++ b/default-configs/pci.mak @@ -24,3 +24,4 @@ CONFIG_SERIAL=y CONFIG_SERIAL_PCI=y CONFIG_IPACK=y CONFIG_WDT_IB6300ESB=y +CONFIG_PCI_TESTDEV=y diff --git a/docs/specs/pci-testdev.txt b/docs/specs/pci-testdev.txt new file mode 100644 index 0000000000..128ae222ef --- /dev/null +++ b/docs/specs/pci-testdev.txt @@ -0,0 +1,26 @@ +pci-test is a device used for testing low level IO + +device implements up to two BARs: BAR0 and BAR1. +Each BAR can be memory or IO. Guests must detect +BAR type and act accordingly. + +Each BAR size is up to 4K bytes. +Each BAR starts with the following header: + +typedef struct PCITestDevHdr { + uint8_t test; <- write-only, starts a given test number + uint8_t width_type; <- read-only, type and width of access for a given test. + 1,2,4 for byte,word or long write. + any other value if test not supported on this BAR + uint8_t pad0[2]; + uint32_t offset; <- read-only, offset in this BAR for a given test + uint32_t data; <- read-only, data to use for a given test + uint32_t count; <- for debugging. number of writes detected. + uint8_t name[]; <- for debugging. 0-terminated ASCII string. +} PCITestDevHdr; + +All registers are little endian. + +device is expected to always implement tests 0 to N on each BAR, and to add new +tests with higher numbers. In this way a guest can scan test numbers until it +detects an access type that it does not support on this BAR, then stop. diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 03699c3365..11b18a4196 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -4,6 +4,7 @@ common-obj-$(CONFIG_TMP105) += tmp105.o common-obj-$(CONFIG_ISA_DEBUG) += debugexit.o common-obj-$(CONFIG_SGA) += sga.o common-obj-$(CONFIG_ISA_TESTDEV) += pc-testdev.o +common-obj-$(CONFIG_PCI_TESTDEV) += pci-testdev.o obj-$(CONFIG_VMPORT) += vmport.o diff --git a/hw/misc/pci-testdev.c b/hw/misc/pci-testdev.c new file mode 100644 index 0000000000..71ce5a3e08 --- /dev/null +++ b/hw/misc/pci-testdev.c @@ -0,0 +1,325 @@ +/* + * QEMU PCI test device + * + * Copyright (c) 2012 Red Hat Inc. + * Author: Michael S. Tsirkin <mst@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "qemu/event_notifier.h" +#include "qemu/osdep.h" + +typedef struct PCITestDevHdr { + uint8_t test; + uint8_t width; + uint8_t pad0[2]; + uint32_t offset; + uint8_t data; + uint8_t pad1[3]; + uint32_t count; + uint8_t name[]; +} PCITestDevHdr; + +typedef struct IOTest { + MemoryRegion *mr; + EventNotifier notifier; + bool hasnotifier; + unsigned size; + bool match_data; + PCITestDevHdr *hdr; + unsigned bufsize; +} IOTest; + +#define IOTEST_DATAMATCH 0xFA +#define IOTEST_NOMATCH 0xCE + +#define IOTEST_IOSIZE 128 +#define IOTEST_MEMSIZE 2048 + +static const char *iotest_test[] = { + "no-eventfd", + "wildcard-eventfd", + "datamatch-eventfd" +}; + +static const char *iotest_type[] = { + "mmio", + "portio" +}; + +#define IOTEST_TEST(i) (iotest_test[((i) % ARRAY_SIZE(iotest_test))]) +#define IOTEST_TYPE(i) (iotest_type[((i) / ARRAY_SIZE(iotest_test))]) +#define IOTEST_MAX_TEST (ARRAY_SIZE(iotest_test)) +#define IOTEST_MAX_TYPE (ARRAY_SIZE(iotest_type)) +#define IOTEST_MAX (IOTEST_MAX_TEST * IOTEST_MAX_TYPE) + +enum { + IOTEST_ACCESS_NAME, + IOTEST_ACCESS_DATA, + IOTEST_ACCESS_MAX, +}; + +#define IOTEST_ACCESS_TYPE uint8_t +#define IOTEST_ACCESS_WIDTH (sizeof(uint8_t)) + +typedef struct PCITestDevState { + PCIDevice dev; + MemoryRegion mmio; + MemoryRegion portio; + IOTest *tests; + int current; +} PCITestDevState; + +#define IOTEST_IS_MEM(i) (strcmp(IOTEST_TYPE(i), "portio")) +#define IOTEST_REGION(d, i) (IOTEST_IS_MEM(i) ? &(d)->mmio : &(d)->portio) +#define IOTEST_SIZE(i) (IOTEST_IS_MEM(i) ? IOTEST_MEMSIZE : IOTEST_IOSIZE) +#define IOTEST_PCI_BAR(i) (IOTEST_IS_MEM(i) ? PCI_BASE_ADDRESS_SPACE_MEMORY : \ + PCI_BASE_ADDRESS_SPACE_IO) + +static int pci_testdev_start(IOTest *test) +{ + test->hdr->count = 0; + if (!test->hasnotifier) { + return 0; + } + event_notifier_test_and_clear(&test->notifier); + memory_region_add_eventfd(test->mr, + le32_to_cpu(test->hdr->offset), + test->size, + test->match_data, + test->hdr->data, + &test->notifier); + return 0; +} + +static void pci_testdev_stop(IOTest *test) +{ + if (!test->hasnotifier) { + return; + } + memory_region_del_eventfd(test->mr, + le32_to_cpu(test->hdr->offset), + test->size, + test->match_data, + test->hdr->data, + &test->notifier); +} + +static void +pci_testdev_reset(PCITestDevState *d) +{ + if (d->current == -1) { + return; + } + pci_testdev_stop(&d->tests[d->current]); + d->current = -1; +} + +static void pci_testdev_inc(IOTest *test, unsigned inc) +{ + uint32_t c = le32_to_cpu(test->hdr->count); + test->hdr->count = cpu_to_le32(c + inc); +} + +static void +pci_testdev_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size, int type) +{ + PCITestDevState *d = opaque; + IOTest *test; + int t, r; + + if (addr == offsetof(PCITestDevHdr, test)) { + pci_testdev_reset(d); + if (val >= IOTEST_MAX_TEST) { + return; + } + t = type * IOTEST_MAX_TEST + val; + r = pci_testdev_start(&d->tests[t]); + if (r < 0) { + return; + } + d->current = t; + return; + } + if (d->current < 0) { + return; + } + test = &d->tests[d->current]; + if (addr != le32_to_cpu(test->hdr->offset)) { + return; + } + if (test->match_data && test->size != size) { + return; + } + if (test->match_data && val != test->hdr->data) { + return; + } + pci_testdev_inc(test, 1); +} + +static uint64_t +pci_testdev_read(void *opaque, hwaddr addr, unsigned size) +{ + PCITestDevState *d = opaque; + const char *buf; + IOTest *test; + if (d->current < 0) { + return 0; + } + test = &d->tests[d->current]; + buf = (const char *)test->hdr; + if (addr + size >= test->bufsize) { + return 0; + } + if (test->hasnotifier) { + event_notifier_test_and_clear(&test->notifier); + } + return buf[addr]; +} + +static void +pci_testdev_mmio_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + pci_testdev_write(opaque, addr, val, size, 0); +} + +static void +pci_testdev_pio_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + pci_testdev_write(opaque, addr, val, size, 1); +} + +static const MemoryRegionOps pci_testdev_mmio_ops = { + .read = pci_testdev_read, + .write = pci_testdev_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +static const MemoryRegionOps pci_testdev_pio_ops = { + .read = pci_testdev_read, + .write = pci_testdev_pio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +static int pci_testdev_init(PCIDevice *pci_dev) +{ + PCITestDevState *d = DO_UPCAST(PCITestDevState, dev, pci_dev); + uint8_t *pci_conf; + char *name; + int r, i; + + pci_conf = d->dev.config; + + pci_conf[PCI_INTERRUPT_PIN] = 0; /* no interrupt pin */ + + memory_region_init_io(&d->mmio, &pci_testdev_mmio_ops, d, + "pci-testdev-mmio", IOTEST_MEMSIZE * 2); + memory_region_init_io(&d->portio, &pci_testdev_pio_ops, d, + "pci-testdev-portio", IOTEST_IOSIZE * 2); + pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); + pci_register_bar(&d->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->portio); + + d->current = -1; + d->tests = g_malloc0(IOTEST_MAX * sizeof *d->tests); + for (i = 0; i < IOTEST_MAX; ++i) { + IOTest *test = &d->tests[i]; + name = g_strdup_printf("%s-%s", IOTEST_TYPE(i), IOTEST_TEST(i)); + test->bufsize = sizeof(PCITestDevHdr) + strlen(name) + 1; + test->hdr = g_malloc0(test->bufsize); + memcpy(test->hdr->name, name, strlen(name) + 1); + g_free(name); + test->hdr->offset = cpu_to_le32(IOTEST_SIZE(i) + i * IOTEST_ACCESS_WIDTH); + test->size = IOTEST_ACCESS_WIDTH; + test->match_data = strcmp(IOTEST_TEST(i), "wildcard-eventfd"); + test->hdr->test = i; + test->hdr->data = test->match_data ? IOTEST_DATAMATCH : IOTEST_NOMATCH; + test->hdr->width = IOTEST_ACCESS_WIDTH; + test->mr = IOTEST_REGION(d, i); + if (!strcmp(IOTEST_TEST(i), "no-eventfd")) { + test->hasnotifier = false; + continue; + } + r = event_notifier_init(&test->notifier, 0); + assert(r >= 0); + test->hasnotifier = true; + } + + return 0; +} + +static void +pci_testdev_uninit(PCIDevice *dev) +{ + PCITestDevState *d = DO_UPCAST(PCITestDevState, dev, dev); + int i; + + pci_testdev_reset(d); + for (i = 0; i < IOTEST_MAX; ++i) { + if (d->tests[i].hasnotifier) { + event_notifier_cleanup(&d->tests[i].notifier); + } + g_free(d->tests[i].hdr); + } + g_free(d->tests); + memory_region_destroy(&d->mmio); + memory_region_destroy(&d->portio); +} + +static void qdev_pci_testdev_reset(DeviceState *dev) +{ + PCITestDevState *d = DO_UPCAST(PCITestDevState, dev.qdev, dev); + pci_testdev_reset(d); +} + +static void pci_testdev_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = pci_testdev_init; + k->exit = pci_testdev_uninit; + k->vendor_id = PCI_VENDOR_ID_REDHAT; + k->device_id = PCI_DEVICE_ID_REDHAT_TEST; + k->revision = 0x00; + k->class_id = PCI_CLASS_OTHERS; + dc->desc = "PCI Test Device"; + dc->reset = qdev_pci_testdev_reset; +} + +static const TypeInfo pci_testdev_info = { + .name = "pci-testdev", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCITestDevState), + .class_init = pci_testdev_class_init, +}; + +static void pci_testdev_register_types(void) +{ + type_register_static(&pci_testdev_info); +} + +type_init(pci_testdev_register_types) diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 05315c0475..7053d5bb4b 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -85,6 +85,7 @@ #define PCI_DEVICE_ID_REDHAT_SERIAL 0x0002 #define PCI_DEVICE_ID_REDHAT_SERIAL2 0x0003 #define PCI_DEVICE_ID_REDHAT_SERIAL4 0x0004 +#define PCI_DEVICE_ID_REDHAT_TEST 0x0005 #define PCI_DEVICE_ID_REDHAT_QXL 0x0100 #define FMT_PCIBUS PRIx64 diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 495e6f8dbd..75bd7d9934 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -283,10 +283,6 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram_addr, #endif #endif -int kvm_set_ioeventfd_mmio(int fd, uint32_t adr, uint32_t val, bool assign, - uint32_t size); - -int kvm_set_ioeventfd_pio_word(int fd, uint16_t adr, uint16_t val, bool assign); int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg); int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg); @@ -500,6 +500,66 @@ int kvm_check_extension(KVMState *s, unsigned int extension) return ret; } +static int kvm_set_ioeventfd_mmio(int fd, uint32_t addr, uint32_t val, + bool assign, uint32_t size, bool datamatch) +{ + int ret; + struct kvm_ioeventfd iofd; + + iofd.datamatch = datamatch ? val : 0; + iofd.addr = addr; + iofd.len = size; + iofd.flags = 0; + iofd.fd = fd; + + if (!kvm_enabled()) { + return -ENOSYS; + } + + if (datamatch) { + iofd.flags |= KVM_IOEVENTFD_FLAG_DATAMATCH; + } + if (!assign) { + iofd.flags |= KVM_IOEVENTFD_FLAG_DEASSIGN; + } + + ret = kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &iofd); + + if (ret < 0) { + return -errno; + } + + return 0; +} + +static int kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint16_t val, + bool assign, uint32_t size, bool datamatch) +{ + struct kvm_ioeventfd kick = { + .datamatch = datamatch ? val : 0, + .addr = addr, + .flags = KVM_IOEVENTFD_FLAG_PIO, + .len = size, + .fd = fd, + }; + int r; + if (!kvm_enabled()) { + return -ENOSYS; + } + if (datamatch) { + kick.flags |= KVM_IOEVENTFD_FLAG_DATAMATCH; + } + if (!assign) { + kick.flags |= KVM_IOEVENTFD_FLAG_DEASSIGN; + } + r = kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &kick); + if (r < 0) { + return r; + } + return 0; +} + + static int kvm_check_many_ioeventfds(void) { /* Userspace can use ioeventfd for io notification. This requires a host @@ -517,7 +577,7 @@ static int kvm_check_many_ioeventfds(void) if (ioeventfds[i] < 0) { break; } - ret = kvm_set_ioeventfd_pio_word(ioeventfds[i], 0, i, true); + ret = kvm_set_ioeventfd_pio(ioeventfds[i], 0, i, true, 2, true); if (ret < 0) { close(ioeventfds[i]); break; @@ -528,7 +588,7 @@ static int kvm_check_many_ioeventfds(void) ret = i == ARRAY_SIZE(ioeventfds); while (i-- > 0) { - kvm_set_ioeventfd_pio_word(ioeventfds[i], 0, i, false); + kvm_set_ioeventfd_pio(ioeventfds[i], 0, i, false, 2, true); close(ioeventfds[i]); } return ret; @@ -748,10 +808,8 @@ static void kvm_mem_ioeventfd_add(MemoryListener *listener, int fd = event_notifier_get_fd(e); int r; - assert(match_data && section->size <= 8); - r = kvm_set_ioeventfd_mmio(fd, section->offset_within_address_space, - data, true, section->size); + data, true, section->size, match_data); if (r < 0) { abort(); } @@ -766,7 +824,7 @@ static void kvm_mem_ioeventfd_del(MemoryListener *listener, int r; r = kvm_set_ioeventfd_mmio(fd, section->offset_within_address_space, - data, false, section->size); + data, false, section->size, match_data); if (r < 0) { abort(); } @@ -780,10 +838,8 @@ static void kvm_io_ioeventfd_add(MemoryListener *listener, int fd = event_notifier_get_fd(e); int r; - assert(match_data && section->size == 2); - - r = kvm_set_ioeventfd_pio_word(fd, section->offset_within_address_space, - data, true); + r = kvm_set_ioeventfd_pio(fd, section->offset_within_address_space, + data, true, section->size, match_data); if (r < 0) { abort(); } @@ -798,8 +854,8 @@ static void kvm_io_ioeventfd_del(MemoryListener *listener, int fd = event_notifier_get_fd(e); int r; - r = kvm_set_ioeventfd_pio_word(fd, section->offset_within_address_space, - data, false); + r = kvm_set_ioeventfd_pio(fd, section->offset_within_address_space, + data, false, section->size, match_data); if (r < 0) { abort(); } @@ -1967,59 +2023,6 @@ int kvm_set_signal_mask(CPUArchState *env, const sigset_t *sigset) return r; } - -int kvm_set_ioeventfd_mmio(int fd, uint32_t addr, uint32_t val, bool assign, - uint32_t size) -{ - int ret; - struct kvm_ioeventfd iofd; - - iofd.datamatch = val; - iofd.addr = addr; - iofd.len = size; - iofd.flags = KVM_IOEVENTFD_FLAG_DATAMATCH; - iofd.fd = fd; - - if (!kvm_enabled()) { - return -ENOSYS; - } - - if (!assign) { - iofd.flags |= KVM_IOEVENTFD_FLAG_DEASSIGN; - } - - ret = kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &iofd); - - if (ret < 0) { - return -errno; - } - - return 0; -} - -int kvm_set_ioeventfd_pio_word(int fd, uint16_t addr, uint16_t val, bool assign) -{ - struct kvm_ioeventfd kick = { - .datamatch = val, - .addr = addr, - .len = 2, - .flags = KVM_IOEVENTFD_FLAG_DATAMATCH | KVM_IOEVENTFD_FLAG_PIO, - .fd = fd, - }; - int r; - if (!kvm_enabled()) { - return -ENOSYS; - } - if (!assign) { - kick.flags |= KVM_IOEVENTFD_FLAG_DEASSIGN; - } - r = kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &kick); - if (r < 0) { - return r; - } - return 0; -} - int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr) { return kvm_arch_on_sigbus_vcpu(cpu, code, addr); diff --git a/kvm-stub.c b/kvm-stub.c index 723a813735..5f52186ae7 100644 --- a/kvm-stub.c +++ b/kvm-stub.c @@ -101,16 +101,6 @@ int kvm_set_signal_mask(CPUArchState *env, const sigset_t *sigset) } #endif -int kvm_set_ioeventfd_pio_word(int fd, uint16_t addr, uint16_t val, bool assign) -{ - return -ENOSYS; -} - -int kvm_set_ioeventfd_mmio(int fd, uint32_t adr, uint32_t val, bool assign, uint32_t len) -{ - return -ENOSYS; -} - int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr) { return 1; diff --git a/scripts/kvm/vmxcap b/scripts/kvm/vmxcap index 0b23f7795a..c90eda497a 100755 --- a/scripts/kvm/vmxcap +++ b/scripts/kvm/vmxcap @@ -27,9 +27,9 @@ MSR_IA32_VMX_VMFUNC = 0x491 class msr(object): def __init__(self): try: - self.f = file('/dev/cpu/0/msr') + self.f = open('/dev/cpu/0/msr', 'r', 0) except: - self.f = file('/dev/msr0') + self.f = open('/dev/msr0', 'r', 0) def read(self, index, default = None): import struct self.f.seek(index) @@ -96,6 +96,19 @@ class Misc(object): print ' %-40s %s' % (self.bits[bits], fmt(v)) controls = [ + Misc( + name = 'Basic VMX Information', + bits = { + (0, 31): 'Revision', + (32,44): 'VMCS size', + 48: 'VMCS restricted to 32 bit addresses', + 49: 'Dual-monitor support', + (50, 53): 'VMCS memory type', + 54: 'INS/OUTS instruction information', + 55: 'IA32_VMX_TRUE_*_CTLS support', + }, + msr = MSR_IA32_VMX_BASIC, + ), Control( name = 'pin-based controls', bits = { @@ -103,6 +116,7 @@ controls = [ 3: 'NMI exiting', 5: 'Virtual NMIs', 6: 'Activate VMX-preemption timer', + 7: 'Process posted interrupts', }, cap_msr = MSR_IA32_VMX_PINBASED_CTLS, true_cap_msr = MSR_IA32_VMX_TRUE_PINBASED_CTLS, @@ -143,15 +157,19 @@ controls = [ 0: 'Virtualize APIC accesses', 1: 'Enable EPT', 2: 'Descriptor-table exiting', + 3: 'Enable RDTSCP', 4: 'Virtualize x2APIC mode', 5: 'Enable VPID', 6: 'WBINVD exiting', 7: 'Unrestricted guest', + 8: 'APIC register emulation', 9: 'Virtual interrupt delivery', 10: 'PAUSE-loop exiting', 11: 'RDRAND exiting', 12: 'Enable INVPCID', 13: 'Enable VM functions', + 14: 'VMCS shadowing', + 18: 'EPT-violation #VE' }, cap_msr = MSR_IA32_VMX_PROCBASED_CTLS2, ), @@ -196,10 +214,12 @@ controls = [ 6: 'HLT activity state', 7: 'Shutdown activity state', 8: 'Wait-for-SIPI activity state', + 15: 'IA32_SMBASE support', (16,24): 'Number of CR3-target values', (25,27): 'MSR-load/store count recommenation', 28: 'IA32_SMM_MONITOR_CTL[2] can be set to 1', - (32,62): 'MSEG revision identifier', + 29: 'VMWRITE to VM-exit information fields', + (32,63): 'MSEG revision identifier', }, msr = MSR_IA32_VMX_MISC_CTLS, ), diff --git a/target-i386/cpu.h b/target-i386/cpu.h index cf1b05c28c..a1614e8e50 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -803,6 +803,7 @@ typedef struct CPUX86State { #endif uint64_t system_time_msr; uint64_t wall_clock_msr; + uint64_t steal_time_msr; uint64_t async_pf_en_msr; uint64_t pv_eoi_en_msr; diff --git a/target-i386/kvm.c b/target-i386/kvm.c index 397afebecb..0e7cc8113f 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -68,6 +68,7 @@ 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 bool has_msr_kvm_steal_time; static int lm_capable_kernel; bool kvm_allows_irq0_override(void) @@ -507,6 +508,8 @@ int kvm_arch_init_vcpu(CPUState *cs) has_msr_pv_eoi_en = c->eax & (1 << KVM_FEATURE_PV_EOI); + has_msr_kvm_steal_time = c->eax & (1 << KVM_FEATURE_STEAL_TIME); + cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused); for (i = 0; i <= limit; i++) { @@ -1107,6 +1110,10 @@ static int kvm_put_msrs(X86CPU *cpu, int level) kvm_msr_entry_set(&msrs[n++], MSR_KVM_PV_EOI_EN, env->pv_eoi_en_msr); } + if (has_msr_kvm_steal_time) { + kvm_msr_entry_set(&msrs[n++], MSR_KVM_STEAL_TIME, + env->steal_time_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); @@ -1360,6 +1367,9 @@ static int kvm_get_msrs(X86CPU *cpu) if (has_msr_pv_eoi_en) { msrs[n++].index = MSR_KVM_PV_EOI_EN; } + if (has_msr_kvm_steal_time) { + msrs[n++].index = MSR_KVM_STEAL_TIME; + } if (env->mcg_cap) { msrs[n++].index = MSR_MCG_STATUS; @@ -1445,6 +1455,9 @@ static int kvm_get_msrs(X86CPU *cpu) case MSR_KVM_PV_EOI_EN: env->pv_eoi_en_msr = msrs[i].data; break; + case MSR_KVM_STEAL_TIME: + env->steal_time_msr = msrs[i].data; + break; } } diff --git a/target-i386/machine.c b/target-i386/machine.c index ee85e57435..3659db9e94 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -292,6 +292,24 @@ static bool pv_eoi_msr_needed(void *opaque) return cpu->env.pv_eoi_en_msr != 0; } +static bool steal_time_msr_needed(void *opaque) +{ + CPUX86State *cpu = opaque; + + return cpu->steal_time_msr != 0; +} + +static const VMStateDescription vmstate_steal_time_msr = { + .name = "cpu/steal_time_msr", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_UINT64(steal_time_msr, CPUX86State), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_async_pf_msr = { .name = "cpu/async_pf_msr", .version_id = 1, @@ -503,6 +521,9 @@ const VMStateDescription vmstate_x86_cpu = { .vmsd = &vmstate_pv_eoi_msr, .needed = pv_eoi_msr_needed, } , { + .vmsd = &vmstate_steal_time_msr, + .needed = steal_time_msr_needed, + } , { .vmsd = &vmstate_fpop_ip_dp, .needed = fpop_ip_dp_needed, }, { |