diff options
-rw-r--r-- | hw/i386/pc_piix.c | 24 | ||||
-rw-r--r-- | hw/s390x/css.c | 2 | ||||
-rw-r--r-- | hw/s390x/css.h | 1 | ||||
-rw-r--r-- | hw/s390x/virtio-ccw.c | 207 | ||||
-rw-r--r-- | hw/s390x/virtio-ccw.h | 8 | ||||
-rw-r--r-- | include/hw/i386/pc.h | 3 | ||||
-rw-r--r-- | target-arm/Makefile.objs | 1 | ||||
-rw-r--r-- | target-arm/cpu-qom.h | 24 | ||||
-rw-r--r-- | target-arm/cpu.c | 4 | ||||
-rw-r--r-- | target-arm/cpu.h | 89 | ||||
-rw-r--r-- | target-arm/helper.c | 327 | ||||
-rw-r--r-- | target-arm/kvm-stub.c | 23 | ||||
-rw-r--r-- | target-arm/kvm.c | 292 | ||||
-rw-r--r-- | target-arm/kvm_arm.h | 33 | ||||
-rw-r--r-- | target-arm/machine.c | 134 | ||||
-rw-r--r-- | target-s390x/cpu.h | 16 | ||||
-rw-r--r-- | target-s390x/kvm.c | 19 | ||||
-rw-r--r-- | xen-all.c | 12 | ||||
-rw-r--r-- | xen-stub.c | 5 |
19 files changed, 1038 insertions, 186 deletions
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 7972443374..fa59a0cb26 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -91,6 +91,11 @@ static void pc_init1(MemoryRegion *system_memory, DeviceState *icc_bridge; FWCfgState *fw_cfg = NULL; + if (xen_enabled() && xen_hvm_init() != 0) { + fprintf(stderr, "xen hardware virtual machine initialisation failed\n"); + exit(1); + } + icc_bridge = qdev_create(NULL, TYPE_ICC_BRIDGE); object_property_add_child(qdev_get_machine(), "icc-bridge", OBJECT(icc_bridge), NULL); @@ -102,9 +107,9 @@ static void pc_init1(MemoryRegion *system_memory, kvmclock_create(); } - if (ram_size >= QEMU_BELOW_4G_RAM_END ) { - above_4g_mem_size = ram_size - QEMU_BELOW_4G_RAM_END; - below_4g_mem_size = QEMU_BELOW_4G_RAM_END; + if (ram_size >= 0xe0000000 ) { + above_4g_mem_size = ram_size - 0xe0000000; + below_4g_mem_size = 0xe0000000; } else { above_4g_mem_size = 0; below_4g_mem_size = ram_size; @@ -174,9 +179,6 @@ static void pc_init1(MemoryRegion *system_memory, pc_register_ferr_irq(gsi[13]); pc_vga_init(isa_bus, pci_enabled ? pci_bus : NULL); - if (xen_enabled()) { - pci_create_simple(pci_bus, -1, "xen-platform"); - } /* init basic PC hardware */ pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy, xen_enabled()); @@ -320,10 +322,14 @@ static void pc_init_isa(QEMUMachineInitArgs *args) #ifdef CONFIG_XEN static void pc_xen_hvm_init(QEMUMachineInitArgs *args) { - if (xen_hvm_init() != 0) { - hw_error("xen hardware virtual machine initialisation failed"); - } + PCIBus *bus; + pc_init_pci(args); + + bus = pci_find_root_bus(0); + if (bus != NULL) { + pci_create_simple(bus, -1, "xen-platform"); + } } #endif diff --git a/hw/s390x/css.c b/hw/s390x/css.c index f82abfe029..93b0b9733b 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -68,7 +68,7 @@ int css_create_css_image(uint8_t cssid, bool default_image) return 0; } -static uint16_t css_build_subchannel_id(SubchDev *sch) +uint16_t css_build_subchannel_id(SubchDev *sch) { if (channel_subsys->max_cssid > 0) { return (sch->cssid << 8) | (1 << 3) | (sch->ssid << 1) | 1; diff --git a/hw/s390x/css.h b/hw/s390x/css.h index 85ed05d0f5..b536ab5957 100644 --- a/hw/s390x/css.h +++ b/hw/s390x/css.h @@ -90,6 +90,7 @@ bool css_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno); void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, uint16_t devno, SubchDev *sch); void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type); +uint16_t css_build_subchannel_id(SubchDev *sch); void css_reset(void); void css_reset_sch(SubchDev *sch); void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid); diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 201a635607..faef5ddf57 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -63,6 +63,91 @@ VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch) return vdev; } +static int virtio_ccw_set_guest2host_notifier(VirtioCcwDevice *dev, int n, + bool assign, bool set_handler) +{ + VirtQueue *vq = virtio_get_queue(dev->vdev, n); + EventNotifier *notifier = virtio_queue_get_host_notifier(vq); + int r = 0; + SubchDev *sch = dev->sch; + uint32_t sch_id = (css_build_subchannel_id(sch) << 16) | sch->schid; + + if (assign) { + r = event_notifier_init(notifier, 1); + if (r < 0) { + error_report("%s: unable to init event notifier: %d", __func__, r); + return r; + } + virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler); + r = s390_assign_subch_ioeventfd(event_notifier_get_fd(notifier), sch_id, + n, assign); + if (r < 0) { + error_report("%s: unable to assign ioeventfd: %d", __func__, r); + virtio_queue_set_host_notifier_fd_handler(vq, false, false); + event_notifier_cleanup(notifier); + return r; + } + } else { + virtio_queue_set_host_notifier_fd_handler(vq, false, false); + s390_assign_subch_ioeventfd(event_notifier_get_fd(notifier), sch_id, + n, assign); + event_notifier_cleanup(notifier); + } + return r; +} + +static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev) +{ + int n, r; + + if (!(dev->flags & VIRTIO_CCW_FLAG_USE_IOEVENTFD) || + dev->ioeventfd_disabled || + dev->ioeventfd_started) { + return; + } + for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) { + if (!virtio_queue_get_num(dev->vdev, n)) { + continue; + } + r = virtio_ccw_set_guest2host_notifier(dev, n, true, true); + if (r < 0) { + goto assign_error; + } + } + dev->ioeventfd_started = true; + return; + + assign_error: + while (--n >= 0) { + if (!virtio_queue_get_num(dev->vdev, n)) { + continue; + } + r = virtio_ccw_set_guest2host_notifier(dev, n, false, false); + assert(r >= 0); + } + dev->ioeventfd_started = false; + /* Disable ioeventfd for this device. */ + dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD; + error_report("%s: failed. Fallback to userspace (slower).", __func__); +} + +static void virtio_ccw_stop_ioeventfd(VirtioCcwDevice *dev) +{ + int n, r; + + if (!dev->ioeventfd_started) { + return; + } + for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) { + if (!virtio_queue_get_num(dev->vdev, n)) { + continue; + } + r = virtio_ccw_set_guest2host_notifier(dev, n, false, false); + assert(r >= 0); + } + dev->ioeventfd_started = false; +} + VirtualCssBus *virtual_css_bus_init(void) { VirtualCssBus *cbus; @@ -187,6 +272,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) } break; case CCW_CMD_VDEV_RESET: + virtio_ccw_stop_ioeventfd(dev); virtio_reset(dev->vdev); ret = 0; break; @@ -309,10 +395,16 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ret = -EFAULT; } else { status = ldub_phys(ccw.cda); + if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) { + virtio_ccw_stop_ioeventfd(dev); + } virtio_set_status(dev->vdev, status); if (dev->vdev->status == 0) { virtio_reset(dev->vdev); } + if (status & VIRTIO_CONFIG_S_DRIVER_OK) { + virtio_ccw_start_ioeventfd(dev); + } sch->curr_status.scsw.count = ccw.count - sizeof(status); ret = 0; } @@ -540,6 +632,7 @@ static int virtio_ccw_exit(VirtioCcwDevice *dev) { SubchDev *sch = dev->sch; + virtio_ccw_stop_ioeventfd(dev); if (sch) { css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); g_free(sch); @@ -801,8 +894,106 @@ static void virtio_ccw_reset(DeviceState *d) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); + virtio_ccw_stop_ioeventfd(dev); virtio_reset(dev->vdev); css_reset_sch(dev->sch); + dev->indicators = 0; + dev->indicators2 = 0; +} + +static void virtio_ccw_vmstate_change(DeviceState *d, bool running) +{ + VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); + + if (running) { + virtio_ccw_start_ioeventfd(dev); + } else { + virtio_ccw_stop_ioeventfd(dev); + } +} + +static bool virtio_ccw_query_guest_notifiers(DeviceState *d) +{ + VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); + + return !!(dev->sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA); +} + +static int virtio_ccw_set_host_notifier(DeviceState *d, int n, bool assign) +{ + VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); + + /* Stop using the generic ioeventfd, we are doing eventfd handling + * ourselves below */ + dev->ioeventfd_disabled = assign; + if (assign) { + virtio_ccw_stop_ioeventfd(dev); + } + return virtio_ccw_set_guest2host_notifier(dev, n, assign, false); +} + +static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n, + bool assign, bool with_irqfd) +{ + VirtQueue *vq = virtio_get_queue(dev->vdev, n); + EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(dev->vdev); + + if (assign) { + int r = event_notifier_init(notifier, 0); + + if (r < 0) { + return r; + } + virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd); + /* We do not support irqfd for classic I/O interrupts, because the + * classic interrupts are intermixed with the subchannel status, that + * is queried with test subchannel. We want to use vhost, though. + * Lets make sure to have vhost running and wire up the irq fd to + * land in qemu (and only the irq fd) in this code. + */ + if (k->guest_notifier_mask) { + k->guest_notifier_mask(dev->vdev, n, false); + } + /* get lost events and re-inject */ + if (k->guest_notifier_pending && + k->guest_notifier_pending(dev->vdev, n)) { + event_notifier_set(notifier); + } + } else { + if (k->guest_notifier_mask) { + k->guest_notifier_mask(dev->vdev, n, true); + } + virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd); + event_notifier_cleanup(notifier); + } + return 0; +} + +static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs, + bool assigned) +{ + VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); + VirtIODevice *vdev = dev->vdev; + int r, n; + + for (n = 0; n < nvqs; n++) { + if (!virtio_queue_get_num(vdev, n)) { + break; + } + /* false -> true, as soon as irqfd works */ + r = virtio_ccw_set_guest_notifier(dev, n, assigned, false); + if (r < 0) { + goto assign_error; + } + } + return 0; + +assign_error: + while (--n >= 0) { + virtio_ccw_set_guest_notifier(dev, n, !assigned, false); + } + return r; } /**************** Virtio-ccw Bus Device Descriptions *******************/ @@ -812,6 +1003,8 @@ static Property virtio_ccw_net_properties[] = { DEFINE_VIRTIO_NET_FEATURES(VirtioCcwDevice, host_features[0]), DEFINE_VIRTIO_NET_PROPERTIES(VirtIONetCcw, vdev.net_conf), DEFINE_NIC_PROPERTIES(VirtIONetCcw, vdev.nic_conf), + DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, + VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_END_OF_LIST(), }; @@ -838,6 +1031,8 @@ static Property virtio_ccw_blk_properties[] = { DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), DEFINE_VIRTIO_BLK_FEATURES(VirtioCcwDevice, host_features[0]), DEFINE_VIRTIO_BLK_PROPERTIES(VirtIOBlkCcw, blk), + DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, + VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_END_OF_LIST(), }; @@ -864,6 +1059,8 @@ static Property virtio_ccw_serial_properties[] = { DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), DEFINE_VIRTIO_SERIAL_PROPERTIES(VirtioSerialCcw, vdev.serial), DEFINE_VIRTIO_COMMON_FEATURES(VirtioCcwDevice, host_features[0]), + DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, + VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_END_OF_LIST(), }; @@ -889,6 +1086,8 @@ static const TypeInfo virtio_ccw_serial = { static Property virtio_ccw_balloon_properties[] = { DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), DEFINE_VIRTIO_COMMON_FEATURES(VirtioCcwDevice, host_features[0]), + DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, + VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_END_OF_LIST(), }; @@ -915,6 +1114,8 @@ static Property virtio_ccw_scsi_properties[] = { DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOSCSICcw, vdev.parent_obj.conf), DEFINE_VIRTIO_SCSI_FEATURES(VirtioCcwDevice, host_features[0]), + DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, + VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_END_OF_LIST(), }; @@ -978,6 +1179,8 @@ static Property virtio_ccw_rng_properties[] = { DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), DEFINE_VIRTIO_COMMON_FEATURES(VirtioCcwDevice, host_features[0]), DEFINE_VIRTIO_RNG_PROPERTIES(VirtIORNGCcw, vdev.conf), + DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, + VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_END_OF_LIST(), }; @@ -1105,6 +1308,10 @@ static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data) bus_class->max_dev = 1; k->notify = virtio_ccw_notify; k->get_features = virtio_ccw_get_features; + k->vmstate_change = virtio_ccw_vmstate_change; + k->query_guest_notifiers = virtio_ccw_query_guest_notifiers; + k->set_host_notifier = virtio_ccw_set_host_notifier; + k->set_guest_notifiers = virtio_ccw_set_guest_notifiers; } static const TypeInfo virtio_ccw_bus_info = { diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 121a5f8c4a..96d6f5d5b7 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -69,6 +69,11 @@ typedef struct VirtIOCCWDeviceClass { /* Change here if we want to support more feature bits. */ #define VIRTIO_CCW_FEATURE_SIZE 1 +/* Performance improves when virtqueue kick processing is decoupled from the + * vcpu thread using ioeventfd for some devices. */ +#define VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT 1 +#define VIRTIO_CCW_FLAG_USE_IOEVENTFD (1 << VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT) + struct VirtioCcwDevice { DeviceState parent_obj; SubchDev *sch; @@ -76,6 +81,9 @@ struct VirtioCcwDevice { char *bus_id; uint32_t host_features[VIRTIO_CCW_FEATURE_SIZE]; VirtioBusState bus; + bool ioeventfd_started; + bool ioeventfd_disabled; + uint32_t flags; /* Guest provided values: */ hwaddr indicators; hwaddr indicators2; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 7f0496764c..a417402bbb 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -79,9 +79,6 @@ extern int fd_bootchk; void pc_register_ferr_irq(qemu_irq irq); void pc_acpi_smi_interrupt(void *opaque, int irq, int level); -#define QEMU_BELOW_4G_RAM_END 0xe0000000 -#define QEMU_BELOW_4G_MMIO_LENGTH ((1ULL << 32) - QEMU_BELOW_4G_RAM_END) - void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge); void pc_hot_add_cpu(const int64_t id, Error **errp); void pc_acpi_init(const char *default_dsdt); diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs index d89b57c114..4a6e52e528 100644 --- a/target-arm/Makefile.objs +++ b/target-arm/Makefile.objs @@ -1,5 +1,6 @@ obj-y += arm-semi.o obj-$(CONFIG_SOFTMMU) += machine.o obj-$(CONFIG_KVM) += kvm.o +obj-$(CONFIG_NO_KVM) += kvm-stub.o obj-y += translate.o op_helper.o helper.o cpu.o obj-y += neon_helper.o iwmmxt_helper.o diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index 12fcefe0c6..25239b8952 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -62,6 +62,29 @@ typedef struct ARMCPU { /* Coprocessor information */ GHashTable *cp_regs; + /* For marshalling (mostly coprocessor) register state between the + * kernel and QEMU (for KVM) and between two QEMUs (for migration), + * we use these arrays. + */ + /* List of register indexes managed via these arrays; (full KVM style + * 64 bit indexes, not CPRegInfo 32 bit indexes) + */ + uint64_t *cpreg_indexes; + /* Values of the registers (cpreg_indexes[i]'s value is cpreg_values[i]) */ + uint64_t *cpreg_values; + /* When using KVM, keeps a copy of the initial state of the VCPU, + * so that on reset we can feed the reset values back into the kernel. + */ + uint64_t *cpreg_reset_values; + /* Length of the indexes, values, reset_values arrays */ + int32_t cpreg_array_len; + /* These are used only for migration: incoming data arrives in + * these fields and is sanity checked in post_load before copying + * to the working data structures above. + */ + uint64_t *cpreg_vmstate_indexes; + uint64_t *cpreg_vmstate_values; + int32_t cpreg_vmstate_array_len; /* The instance init functions for implementation-specific subclasses * set these fields to specify the implementation-dependent values of @@ -116,6 +139,7 @@ extern const struct VMStateDescription vmstate_arm_cpu; #endif void register_cp_regs_for_features(ARMCPU *cpu); +void init_cpreg_list(ARMCPU *cpu); void arm_cpu_do_interrupt(CPUState *cpu); void arm_v7m_cpu_do_interrupt(CPUState *cpu); diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 496a59f5c0..2371f48057 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -198,12 +198,15 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) set_feature(env, ARM_FEATURE_VFP); } if (arm_feature(env, ARM_FEATURE_LPAE)) { + set_feature(env, ARM_FEATURE_V7MP); set_feature(env, ARM_FEATURE_PXN); } register_cp_regs_for_features(cpu); arm_cpu_register_gdb_regs_for_features(cpu); + init_cpreg_list(cpu); + cpu_reset(CPU(cpu)); qemu_init_vcpu(env); @@ -571,7 +574,6 @@ static void cortex_a15_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_ARM_DIV); - set_feature(&cpu->env, ARM_FEATURE_V7MP); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_LPAE); diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 54384446b4..abcc0b4b1c 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -424,6 +424,43 @@ void armv7m_nvic_complete_irq(void *opaque, int irq); (((cp) << 16) | ((is64) << 15) | ((crn) << 11) | \ ((crm) << 7) | ((opc1) << 3) | (opc2)) +/* Note that these must line up with the KVM/ARM register + * ID field definitions (kvm.c will check this, but we + * can't just use the KVM defines here as the kvm headers + * are unavailable to non-KVM-specific files) + */ +#define CP_REG_SIZE_SHIFT 52 +#define CP_REG_SIZE_MASK 0x00f0000000000000ULL +#define CP_REG_SIZE_U32 0x0020000000000000ULL +#define CP_REG_SIZE_U64 0x0030000000000000ULL +#define CP_REG_ARM 0x4000000000000000ULL + +/* Convert a full 64 bit KVM register ID to the truncated 32 bit + * version used as a key for the coprocessor register hashtable + */ +static inline uint32_t kvm_to_cpreg_id(uint64_t kvmid) +{ + uint32_t cpregid = kvmid; + if ((kvmid & CP_REG_SIZE_MASK) == CP_REG_SIZE_U64) { + cpregid |= (1 << 15); + } + return cpregid; +} + +/* Convert a truncated 32 bit hashtable key into the full + * 64 bit KVM register ID. + */ +static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid) +{ + uint64_t kvmid = cpregid & ~(1 << 15); + if (cpregid & (1 << 15)) { + kvmid |= CP_REG_SIZE_U64 | CP_REG_ARM; + } else { + kvmid |= CP_REG_SIZE_U32 | CP_REG_ARM; + } + return kvmid; +} + /* ARMCPRegInfo type field bits. If the SPECIAL bit is set this is a * special-behaviour cp reg and bits [15..8] indicate what behaviour * it has. Otherwise it is a simple cp reg, where CONST indicates that @@ -434,19 +471,22 @@ void armv7m_nvic_complete_irq(void *opaque, int irq); * a register definition to override a previous definition for the * same (cp, is64, crn, crm, opc1, opc2) tuple: either the new or the * old must have the OVERRIDE bit set. + * NO_MIGRATE indicates that this register should be ignored for migration; + * (eg because any state is accessed via some other coprocessor register). */ #define ARM_CP_SPECIAL 1 #define ARM_CP_CONST 2 #define ARM_CP_64BIT 4 #define ARM_CP_SUPPRESS_TB_END 8 #define ARM_CP_OVERRIDE 16 +#define ARM_CP_NO_MIGRATE 32 #define ARM_CP_NOP (ARM_CP_SPECIAL | (1 << 8)) #define ARM_CP_WFI (ARM_CP_SPECIAL | (2 << 8)) #define ARM_LAST_SPECIAL ARM_CP_WFI /* Used only as a terminator for ARMCPRegInfo lists */ #define ARM_CP_SENTINEL 0xffff /* Mask of only the flag bits in a type field */ -#define ARM_CP_FLAG_MASK 0x1f +#define ARM_CP_FLAG_MASK 0x3f /* Return true if cptype is a valid type field. This is used to try to * catch errors where the sentinel has been accidentally left off the end @@ -456,7 +496,7 @@ static inline bool cptype_valid(int cptype) { return ((cptype & ~ARM_CP_FLAG_MASK) == 0) || ((cptype & ARM_CP_SPECIAL) && - (cptype <= ARM_LAST_SPECIAL)); + ((cptype & ~ARM_CP_FLAG_MASK) <= ARM_LAST_SPECIAL)); } /* Access rights: @@ -562,6 +602,19 @@ struct ARMCPRegInfo { * by fieldoffset. */ CPWriteFn *writefn; + /* Function for doing a "raw" read; used when we need to copy + * coprocessor state to the kernel for KVM or out for + * migration. This only needs to be provided if there is also a + * readfn and it makes an access permission check. + */ + CPReadFn *raw_readfn; + /* Function for doing a "raw" write; used when we need to copy KVM + * kernel coprocessor state into userspace, or for inbound + * migration. This only needs to be provided if there is also a + * writefn and it makes an access permission check or masks out + * "unwritable" bits or has write-one-to-clear or similar behaviour. + */ + CPWriteFn *raw_writefn; /* Function for resetting the register. If NULL, then reset will be done * by writing resetvalue to the field specified in fieldoffset. If * fieldoffset is 0 then no reset will be done. @@ -605,6 +658,38 @@ static inline bool cp_access_ok(CPUARMState *env, return (ri->access >> ((arm_current_pl(env) * 2) + isread)) & 1; } +/** + * write_list_to_cpustate + * @cpu: ARMCPU + * + * For each register listed in the ARMCPU cpreg_indexes list, write + * its value from the cpreg_values list into the ARMCPUState structure. + * This updates TCG's working data structures from KVM data or + * from incoming migration state. + * + * Returns: true if all register values were updated correctly, + * false if some register was unknown or could not be written. + * Note that we do not stop early on failure -- we will attempt + * writing all registers in the list. + */ +bool write_list_to_cpustate(ARMCPU *cpu); + +/** + * write_cpustate_to_list: + * @cpu: ARMCPU + * + * For each register listed in the ARMCPU cpreg_indexes list, write + * its value from the ARMCPUState structure into the cpreg_values list. + * This is used to copy info from TCG's working data structures into + * KVM or for outbound migration. + * + * Returns: true if all register values were read correctly, + * false if some register was unknown or could not be read. + * Note that we do not stop early on failure -- we will attempt + * reading all registers in the list. + */ +bool write_cpustate_to_list(ARMCPU *cpu); + /* Does the core conform to the the "MicroController" profile. e.g. Cortex-M3. Note the M in older cores (eg. ARM7TDMI) stands for Multiply. These are conventional cores (ie. Application or Realtime profile). */ diff --git a/target-arm/helper.c b/target-arm/helper.c index fd055e89f2..5f639fdeb8 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -64,6 +64,194 @@ static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) return 0; } +static int raw_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) +{ + *value = CPREG_FIELD32(env, ri); + return 0; +} + +static int raw_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPREG_FIELD32(env, ri) = value; + return 0; +} + +static bool read_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *v) +{ + /* Raw read of a coprocessor register (as needed for migration, etc) + * return true on success, false if the read is impossible for some reason. + */ + if (ri->type & ARM_CP_CONST) { + *v = ri->resetvalue; + } else if (ri->raw_readfn) { + return (ri->raw_readfn(env, ri, v) == 0); + } else if (ri->readfn) { + return (ri->readfn(env, ri, v) == 0); + } else { + if (ri->type & ARM_CP_64BIT) { + *v = CPREG_FIELD64(env, ri); + } else { + *v = CPREG_FIELD32(env, ri); + } + } + return true; +} + +static bool write_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri, + int64_t v) +{ + /* Raw write of a coprocessor register (as needed for migration, etc). + * Return true on success, false if the write is impossible for some reason. + * Note that constant registers are treated as write-ignored; the + * caller should check for success by whether a readback gives the + * value written. + */ + if (ri->type & ARM_CP_CONST) { + return true; + } else if (ri->raw_writefn) { + return (ri->raw_writefn(env, ri, v) == 0); + } else if (ri->writefn) { + return (ri->writefn(env, ri, v) == 0); + } else { + if (ri->type & ARM_CP_64BIT) { + CPREG_FIELD64(env, ri) = v; + } else { + CPREG_FIELD32(env, ri) = v; + } + } + return true; +} + +bool write_cpustate_to_list(ARMCPU *cpu) +{ + /* Write the coprocessor state from cpu->env to the (index,value) list. */ + int i; + bool ok = true; + + for (i = 0; i < cpu->cpreg_array_len; i++) { + uint32_t regidx = kvm_to_cpreg_id(cpu->cpreg_indexes[i]); + const ARMCPRegInfo *ri; + uint64_t v; + ri = get_arm_cp_reginfo(cpu, regidx); + if (!ri) { + ok = false; + continue; + } + if (ri->type & ARM_CP_NO_MIGRATE) { + continue; + } + if (!read_raw_cp_reg(&cpu->env, ri, &v)) { + ok = false; + continue; + } + cpu->cpreg_values[i] = v; + } + return ok; +} + +bool write_list_to_cpustate(ARMCPU *cpu) +{ + int i; + bool ok = true; + + for (i = 0; i < cpu->cpreg_array_len; i++) { + uint32_t regidx = kvm_to_cpreg_id(cpu->cpreg_indexes[i]); + uint64_t v = cpu->cpreg_values[i]; + uint64_t readback; + const ARMCPRegInfo *ri; + + ri = get_arm_cp_reginfo(cpu, regidx); + if (!ri) { + ok = false; + continue; + } + if (ri->type & ARM_CP_NO_MIGRATE) { + continue; + } + /* Write value and confirm it reads back as written + * (to catch read-only registers and partially read-only + * registers where the incoming migration value doesn't match) + */ + if (!write_raw_cp_reg(&cpu->env, ri, v) || + !read_raw_cp_reg(&cpu->env, ri, &readback) || + readback != v) { + ok = false; + } + } + return ok; +} + +static void add_cpreg_to_list(gpointer key, gpointer opaque) +{ + ARMCPU *cpu = opaque; + uint64_t regidx; + const ARMCPRegInfo *ri; + + regidx = *(uint32_t *)key; + ri = get_arm_cp_reginfo(cpu, regidx); + + if (!(ri->type & ARM_CP_NO_MIGRATE)) { + cpu->cpreg_indexes[cpu->cpreg_array_len] = cpreg_to_kvm_id(regidx); + /* The value array need not be initialized at this point */ + cpu->cpreg_array_len++; + } +} + +static void count_cpreg(gpointer key, gpointer opaque) +{ + ARMCPU *cpu = opaque; + uint64_t regidx; + const ARMCPRegInfo *ri; + + regidx = *(uint32_t *)key; + ri = get_arm_cp_reginfo(cpu, regidx); + + if (!(ri->type & ARM_CP_NO_MIGRATE)) { + cpu->cpreg_array_len++; + } +} + +static gint cpreg_key_compare(gconstpointer a, gconstpointer b) +{ + uint32_t aidx = *(uint32_t *)a; + uint32_t bidx = *(uint32_t *)b; + + return aidx - bidx; +} + +void init_cpreg_list(ARMCPU *cpu) +{ + /* Initialise the cpreg_tuples[] array based on the cp_regs hash. + * Note that we require cpreg_tuples[] to be sorted by key ID. + */ + GList *keys; + int arraylen; + + keys = g_hash_table_get_keys(cpu->cp_regs); + keys = g_list_sort(keys, cpreg_key_compare); + + cpu->cpreg_array_len = 0; + + g_list_foreach(keys, count_cpreg, cpu); + + arraylen = cpu->cpreg_array_len; + cpu->cpreg_indexes = g_new(uint64_t, arraylen); + cpu->cpreg_values = g_new(uint64_t, arraylen); + cpu->cpreg_vmstate_indexes = g_new(uint64_t, arraylen); + cpu->cpreg_vmstate_values = g_new(uint64_t, arraylen); + cpu->cpreg_vmstate_array_len = cpu->cpreg_array_len; + cpu->cpreg_array_len = 0; + + g_list_foreach(keys, add_cpreg_to_list, cpu); + + assert(cpu->cpreg_array_len == arraylen); + + g_list_free(keys); +} + static int dacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { env->cp15.c3 = value; @@ -139,13 +327,13 @@ static const ARMCPRegInfo cp_reginfo[] = { { .name = "DACR", .cp = 15, .crn = 3, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c3), - .resetvalue = 0, .writefn = dacr_write }, + .resetvalue = 0, .writefn = dacr_write, .raw_writefn = raw_write, }, { .name = "FCSEIDR", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c13_fcse), - .resetvalue = 0, .writefn = fcse_write }, + .resetvalue = 0, .writefn = fcse_write, .raw_writefn = raw_write, }, { .name = "CONTEXTIDR", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 1, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c13_fcse), - .resetvalue = 0, .writefn = contextidr_write }, + .resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, }, /* ??? This covers not just the impdef TLB lockdown registers but also * some v7VMSA registers relating to TEX remap, so it is overly broad. */ @@ -155,13 +343,17 @@ static const ARMCPRegInfo cp_reginfo[] = { * the unified TLB ops but also the dside/iside/inner-shareable variants. */ { .name = "TLBIALL", .cp = 15, .crn = 8, .crm = CP_ANY, - .opc1 = CP_ANY, .opc2 = 0, .access = PL1_W, .writefn = tlbiall_write, }, + .opc1 = CP_ANY, .opc2 = 0, .access = PL1_W, .writefn = tlbiall_write, + .type = ARM_CP_NO_MIGRATE }, { .name = "TLBIMVA", .cp = 15, .crn = 8, .crm = CP_ANY, - .opc1 = CP_ANY, .opc2 = 1, .access = PL1_W, .writefn = tlbimva_write, }, + .opc1 = CP_ANY, .opc2 = 1, .access = PL1_W, .writefn = tlbimva_write, + .type = ARM_CP_NO_MIGRATE }, { .name = "TLBIASID", .cp = 15, .crn = 8, .crm = CP_ANY, - .opc1 = CP_ANY, .opc2 = 2, .access = PL1_W, .writefn = tlbiasid_write, }, + .opc1 = CP_ANY, .opc2 = 2, .access = PL1_W, .writefn = tlbiasid_write, + .type = ARM_CP_NO_MIGRATE }, { .name = "TLBIMVAA", .cp = 15, .crn = 8, .crm = CP_ANY, - .opc1 = CP_ANY, .opc2 = 3, .access = PL1_W, .writefn = tlbimvaa_write, }, + .opc1 = CP_ANY, .opc2 = 3, .access = PL1_W, .writefn = tlbimvaa_write, + .type = ARM_CP_NO_MIGRATE }, /* Cache maintenance ops; some of this space may be overridden later. */ { .name = "CACHEMAINT", .cp = 15, .crn = 7, .crm = CP_ANY, .opc1 = 0, .opc2 = CP_ANY, .access = PL1_W, @@ -196,7 +388,8 @@ static const ARMCPRegInfo not_v7_cp_reginfo[] = { .resetvalue = 0 }, /* v6 doesn't have the cache ID registers but Linux reads them anyway */ { .name = "DUMMY", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = CP_ANY, - .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + .access = PL1_R, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, + .resetvalue = 0 }, REGINFO_SENTINEL }; @@ -235,6 +428,7 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { REGINFO_SENTINEL }; + static int pmreg_read(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value) { @@ -366,13 +560,16 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { { .name = "PMCNTENSET", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 1, .access = PL0_RW, .resetvalue = 0, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), - .readfn = pmreg_read, .writefn = pmcntenset_write }, + .readfn = pmreg_read, .writefn = pmcntenset_write, + .raw_readfn = raw_read, .raw_writefn = raw_write }, { .name = "PMCNTENCLR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 2, .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), - .readfn = pmreg_read, .writefn = pmcntenclr_write }, + .readfn = pmreg_read, .writefn = pmcntenclr_write, + .type = ARM_CP_NO_MIGRATE }, { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3, .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), - .readfn = pmreg_read, .writefn = pmovsr_write }, + .readfn = pmreg_read, .writefn = pmovsr_write, + .raw_readfn = raw_read, .raw_writefn = raw_write }, /* Unimplemented so WI. Strictly speaking write accesses in PL0 should * respect PMUSERENR. */ @@ -389,7 +586,8 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 1, .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmxevtyper), - .readfn = pmreg_read, .writefn = pmxevtyper_write }, + .readfn = pmreg_read, .writefn = pmxevtyper_write, + .raw_readfn = raw_read, .raw_writefn = raw_write }, /* Unimplemented, RAZ/WI. XXX PMUSERENR */ { .name = "PMXEVCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 2, .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, @@ -397,22 +595,21 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .access = PL0_R | PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr), .resetvalue = 0, - .writefn = pmuserenr_write }, + .writefn = pmuserenr_write, .raw_writefn = raw_write }, { .name = "PMINTENSET", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 1, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), .resetvalue = 0, - .writefn = pmintenset_write }, + .writefn = pmintenset_write, .raw_writefn = raw_write }, { .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2, - .access = PL1_RW, + .access = PL1_RW, .type = ARM_CP_NO_MIGRATE, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), - .resetvalue = 0, - .writefn = pmintenclr_write }, + .resetvalue = 0, .writefn = pmintenclr_write, }, { .name = "SCR", .cp = 15, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_scr), .resetvalue = 0, }, { .name = "CCSIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 0, - .access = PL1_R, .readfn = ccsidr_read }, + .access = PL1_R, .readfn = ccsidr_read, .type = ARM_CP_NO_MIGRATE }, { .name = "CSSELR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 2, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c0_cssel), .writefn = csselr_write, .resetvalue = 0 }, @@ -461,7 +658,7 @@ static const ARMCPRegInfo t2ee_cp_reginfo[] = { .writefn = teecr_write }, { .name = "TEEHBR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 6, .opc2 = 0, .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, teehbr), - .resetvalue = 0, + .resetvalue = 0, .raw_readfn = raw_read, .raw_writefn = raw_write, .readfn = teehbr_read, .writefn = teehbr_write }, REGINFO_SENTINEL }; @@ -486,7 +683,8 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { /* Dummy implementation: RAZ/WI the whole crn=14 space */ { .name = "GENERIC_TIMER", .cp = 15, .crn = 14, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, + .resetvalue = 0 }, REGINFO_SENTINEL }; @@ -579,7 +777,7 @@ static const ARMCPRegInfo vapa_cp_reginfo[] = { .writefn = par_write }, #ifndef CONFIG_USER_ONLY { .name = "ATS", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = CP_ANY, - .access = PL1_W, .writefn = ats_write }, + .access = PL1_W, .writefn = ats_write, .type = ARM_CP_NO_MIGRATE }, #endif REGINFO_SENTINEL }; @@ -664,11 +862,11 @@ static int arm946_prbs_write(CPUARMState *env, const ARMCPRegInfo *ri, static const ARMCPRegInfo pmsav5_cp_reginfo[] = { { .name = "DATA_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, + .access = PL1_RW, .type = ARM_CP_NO_MIGRATE, .fieldoffset = offsetof(CPUARMState, cp15.c5_data), .resetvalue = 0, .readfn = pmsav5_data_ap_read, .writefn = pmsav5_data_ap_write, }, { .name = "INSN_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1, - .access = PL1_RW, + .access = PL1_RW, .type = ARM_CP_NO_MIGRATE, .fieldoffset = offsetof(CPUARMState, cp15.c5_insn), .resetvalue = 0, .readfn = pmsav5_insn_ap_read, .writefn = pmsav5_insn_ap_write, }, { .name = "DATA_EXT_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 2, @@ -690,15 +888,11 @@ static const ARMCPRegInfo pmsav5_cp_reginfo[] = { REGINFO_SENTINEL }; -static int vmsa_ttbcr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +static int vmsa_ttbcr_raw_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { if (arm_feature(env, ARM_FEATURE_LPAE)) { value &= ~((7 << 19) | (3 << 14) | (0xf << 3)); - /* With LPAE the TTBCR could result in a change of ASID - * via the TTBCR.A1 bit, so do a TLB flush. - */ - tlb_flush(env, 1); } else { value &= 7; } @@ -713,6 +907,18 @@ static int vmsa_ttbcr_write(CPUARMState *env, const ARMCPRegInfo *ri, return 0; } +static int vmsa_ttbcr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + if (arm_feature(env, ARM_FEATURE_LPAE)) { + /* With LPAE the TTBCR could result in a change of ASID + * via the TTBCR.A1 bit, so do a TLB flush. + */ + tlb_flush(env, 1); + } + return vmsa_ttbcr_raw_write(env, ri, value); +} + static void vmsa_ttbcr_reset(CPUARMState *env, const ARMCPRegInfo *ri) { env->cp15.c2_base_mask = 0xffffc000u; @@ -735,7 +941,7 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.c2_base1), .resetvalue = 0, }, { .name = "TTBCR", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2, .access = PL1_RW, .writefn = vmsa_ttbcr_write, - .resetfn = vmsa_ttbcr_reset, + .resetfn = vmsa_ttbcr_reset, .raw_writefn = vmsa_ttbcr_raw_write, .fieldoffset = offsetof(CPUARMState, cp15.c2_control) }, { .name = "DFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c6_data), @@ -801,6 +1007,7 @@ static const ARMCPRegInfo omap_cp_reginfo[] = { .writefn = omap_threadid_write }, { .name = "TI925T_STATUS", .cp = 15, .crn = 15, .crm = 8, .opc1 = 0, .opc2 = 0, .access = PL1_RW, + .type = ARM_CP_NO_MIGRATE, .readfn = arm_cp_read_zero, .writefn = omap_wfi_write, }, /* TODO: Peripheral port remap register: * On OMAP2 mcr p15, 0, rn, c15, c2, 4 sets up the interrupt controller @@ -808,7 +1015,8 @@ static const ARMCPRegInfo omap_cp_reginfo[] = { * when MMU is off. */ { .name = "OMAP_CACHEMAINT", .cp = 15, .crn = 7, .crm = CP_ANY, - .opc1 = 0, .opc2 = CP_ANY, .access = PL1_W, .type = ARM_CP_OVERRIDE, + .opc1 = 0, .opc2 = CP_ANY, .access = PL1_W, + .type = ARM_CP_OVERRIDE | ARM_CP_NO_MIGRATE, .writefn = omap_cachemaint_write }, { .name = "C9", .cp = 15, .crn = 9, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, @@ -848,21 +1056,24 @@ static const ARMCPRegInfo dummy_c15_cp_reginfo[] = { */ { .name = "C15_IMPDEF", .cp = 15, .crn = 15, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, + .resetvalue = 0 }, REGINFO_SENTINEL }; static const ARMCPRegInfo cache_dirty_status_cp_reginfo[] = { /* Cache status: RAZ because we have no cache so it's always clean */ { .name = "CDSR", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 6, - .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + .access = PL1_R, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, + .resetvalue = 0 }, REGINFO_SENTINEL }; static const ARMCPRegInfo cache_block_ops_cp_reginfo[] = { /* We never have a a block transfer operation in progress */ { .name = "BXSR", .cp = 15, .crn = 7, .crm = 12, .opc1 = 0, .opc2 = 4, - .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, + .resetvalue = 0 }, /* The cache ops themselves: these all NOP for QEMU */ { .name = "IICR", .cp = 15, .crm = 5, .opc1 = 0, .access = PL1_W, .type = ARM_CP_NOP|ARM_CP_64BIT }, @@ -884,9 +1095,11 @@ static const ARMCPRegInfo cache_test_clean_cp_reginfo[] = { * to indicate that there are no dirty cache lines. */ { .name = "TC_DCACHE", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 3, - .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = (1 << 30) }, + .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, + .resetvalue = (1 << 30) }, { .name = "TCI_DCACHE", .cp = 15, .crn = 7, .crm = 14, .opc1 = 0, .opc2 = 3, - .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = (1 << 30) }, + .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, + .resetvalue = (1 << 30) }, REGINFO_SENTINEL }; @@ -894,8 +1107,8 @@ static const ARMCPRegInfo strongarm_cp_reginfo[] = { /* Ignore ReadBuffer accesses */ { .name = "C9_READBUFFER", .cp = 15, .crn = 9, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, - .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE, - .resetvalue = 0 }, + .access = PL1_RW, .resetvalue = 0, + .type = ARM_CP_CONST | ARM_CP_OVERRIDE | ARM_CP_NO_MIGRATE }, REGINFO_SENTINEL }; @@ -921,7 +1134,7 @@ static int mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri, static const ARMCPRegInfo mpidr_cp_reginfo[] = { { .name = "MPIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 5, - .access = PL1_R, .readfn = mpidr_read }, + .access = PL1_R, .readfn = mpidr_read, .type = ARM_CP_NO_MIGRATE }, REGINFO_SENTINEL }; @@ -951,14 +1164,20 @@ static int ttbr064_read(CPUARMState *env, const ARMCPRegInfo *ri, return 0; } -static int ttbr064_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) +static int ttbr064_raw_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { env->cp15.c2_base0_hi = value >> 32; env->cp15.c2_base0 = value; + return 0; +} + +static int ttbr064_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ /* Writes to the 64 bit format TTBRs may change the ASID */ tlb_flush(env, 1); - return 0; + return ttbr064_raw_write(env, ri, value); } static void ttbr064_reset(CPUARMState *env, const ARMCPRegInfo *ri) @@ -1008,7 +1227,8 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = { .readfn = par64_read, .writefn = par64_write, .resetfn = par64_reset }, { .name = "TTBR0", .cp = 15, .crm = 2, .opc1 = 0, .access = PL1_RW, .type = ARM_CP_64BIT, .readfn = ttbr064_read, - .writefn = ttbr064_write, .resetfn = ttbr064_reset }, + .writefn = ttbr064_write, .raw_writefn = ttbr064_raw_write, + .resetfn = ttbr064_reset }, { .name = "TTBR1", .cp = 15, .crm = 2, .opc1 = 1, .access = PL1_RW, .type = ARM_CP_64BIT, .readfn = ttbr164_read, .writefn = ttbr164_write, .resetfn = ttbr164_reset }, @@ -1104,7 +1324,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) .name = "PMCR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 0, .access = PL0_RW, .resetvalue = cpu->midr & 0xff000000, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr), - .readfn = pmreg_read, .writefn = pmcr_write + .readfn = pmreg_read, .writefn = pmcr_write, + .raw_readfn = raw_read, .raw_writefn = raw_write, }; ARMCPRegInfo clidr = { .name = "CLIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 1, @@ -1176,7 +1397,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "MIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_R, .resetvalue = cpu->midr, - .writefn = arm_cp_write_ignore, + .writefn = arm_cp_write_ignore, .raw_writefn = raw_write, .fieldoffset = offsetof(CPUARMState, cp15.c0_cpuid) }, { .name = "CTR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 1, @@ -1245,7 +1466,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) ARMCPRegInfo sctlr = { .name = "SCTLR", .cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_sys), - .writefn = sctlr_write, .resetvalue = cpu->reset_sctlr + .writefn = sctlr_write, .resetvalue = cpu->reset_sctlr, + .raw_writefn = raw_write, }; if (arm_feature(env, ARM_FEATURE_XSCALE)) { /* Normally we would always end the TB on an SCTLR write, but Linux @@ -1392,6 +1614,19 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, r2->crm = crm; r2->opc1 = opc1; r2->opc2 = opc2; + /* By convention, for wildcarded registers only the first + * entry is used for migration; the others are marked as + * NO_MIGRATE so we don't try to transfer the register + * multiple times. Special registers (ie NOP/WFI) are + * never migratable. + */ + if ((r->type & ARM_CP_SPECIAL) || + ((r->crm == CP_ANY) && crm != 0) || + ((r->opc1 == CP_ANY) && opc1 != 0) || + ((r->opc2 == CP_ANY) && opc2 != 0)) { + r2->type |= ARM_CP_NO_MIGRATE; + } + /* Overriding of an existing definition must be explicitly * requested. */ diff --git a/target-arm/kvm-stub.c b/target-arm/kvm-stub.c new file mode 100644 index 0000000000..cd1849f72c --- /dev/null +++ b/target-arm/kvm-stub.c @@ -0,0 +1,23 @@ +/* + * QEMU KVM ARM specific function stubs + * + * Copyright Linaro Limited 2013 + * + * Author: Peter Maydell <peter.maydell@linaro.org> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ +#include "qemu-common.h" +#include "kvm_arm.h" + +bool write_kvmstate_to_list(ARMCPU *cpu) +{ + abort(); +} + +bool write_list_to_kvmstate(ARMCPU *cpu) +{ + abort(); +} diff --git a/target-arm/kvm.c b/target-arm/kvm.c index 27dcab93f6..d3937a260f 100644 --- a/target-arm/kvm.c +++ b/target-arm/kvm.c @@ -23,6 +23,15 @@ #include "cpu.h" #include "hw/arm/arm.h" +/* Check that cpu.h's idea of coprocessor fields matches KVM's */ +#if (CP_REG_SIZE_SHIFT != KVM_REG_SIZE_SHIFT) || \ + (CP_REG_SIZE_MASK != KVM_REG_SIZE_MASK) || \ + (CP_REG_SIZE_U32 != KVM_REG_SIZE_U32) || \ + (CP_REG_SIZE_U64 != KVM_REG_SIZE_U64) || \ + (CP_REG_ARM != KVM_REG_ARM) +#error mismatch between cpu.h and KVM header definitions +#endif + const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_LAST_INFO }; @@ -41,12 +50,35 @@ unsigned long kvm_arch_vcpu_id(CPUState *cpu) return cpu->cpu_index; } +static bool reg_syncs_via_tuple_list(uint64_t regidx) +{ + /* Return true if the regidx is a register we should synchronize + * via the cpreg_tuples array (ie is not a core reg we sync by + * hand in kvm_arch_get/put_registers()) + */ + switch (regidx & KVM_REG_ARM_COPROC_MASK) { + case KVM_REG_ARM_CORE: + case KVM_REG_ARM_VFP: + return false; + default: + return true; + } +} + +static int compare_u64(const void *a, const void *b) +{ + return *(uint64_t *)a - *(uint64_t *)b; +} + int kvm_arch_init_vcpu(CPUState *cs) { struct kvm_vcpu_init init; - int ret; + int i, ret, arraylen; uint64_t v; struct kvm_one_reg r; + struct kvm_reg_list rl; + struct kvm_reg_list *rlp; + ARMCPU *cpu = ARM_CPU(cs); init.target = KVM_ARM_TARGET_CORTEX_A15; memset(init.features, 0, sizeof(init.features)); @@ -65,6 +97,80 @@ int kvm_arch_init_vcpu(CPUState *cs) if (ret == -ENOENT) { return -EINVAL; } + + /* Populate the cpreg list based on the kernel's idea + * of what registers exist (and throw away the TCG-created list). + */ + rl.n = 0; + ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, &rl); + if (ret != -E2BIG) { + return ret; + } + rlp = g_malloc(sizeof(struct kvm_reg_list) + rl.n * sizeof(uint64_t)); + rlp->n = rl.n; + ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, rlp); + if (ret) { + goto out; + } + /* Sort the list we get back from the kernel, since cpreg_tuples + * must be in strictly ascending order. + */ + qsort(&rlp->reg, rlp->n, sizeof(rlp->reg[0]), compare_u64); + + for (i = 0, arraylen = 0; i < rlp->n; i++) { + if (!reg_syncs_via_tuple_list(rlp->reg[i])) { + continue; + } + switch (rlp->reg[i] & KVM_REG_SIZE_MASK) { + case KVM_REG_SIZE_U32: + case KVM_REG_SIZE_U64: + break; + default: + fprintf(stderr, "Can't handle size of register in kernel list\n"); + ret = -EINVAL; + goto out; + } + + arraylen++; + } + + cpu->cpreg_indexes = g_renew(uint64_t, cpu->cpreg_indexes, arraylen); + cpu->cpreg_values = g_renew(uint64_t, cpu->cpreg_values, arraylen); + cpu->cpreg_vmstate_indexes = g_renew(uint64_t, cpu->cpreg_vmstate_indexes, + arraylen); + cpu->cpreg_vmstate_values = g_renew(uint64_t, cpu->cpreg_vmstate_values, + arraylen); + cpu->cpreg_array_len = arraylen; + cpu->cpreg_vmstate_array_len = arraylen; + + for (i = 0, arraylen = 0; i < rlp->n; i++) { + uint64_t regidx = rlp->reg[i]; + if (!reg_syncs_via_tuple_list(regidx)) { + continue; + } + cpu->cpreg_indexes[arraylen] = regidx; + arraylen++; + } + assert(cpu->cpreg_array_len == arraylen); + + if (!write_kvmstate_to_list(cpu)) { + /* Shouldn't happen unless kernel is inconsistent about + * what registers exist. + */ + fprintf(stderr, "Initial read of kernel register state failed\n"); + ret = -EINVAL; + goto out; + } + + /* Save a copy of the initial register values so that we can + * feed it back to the kernel on VCPU reset. + */ + cpu->cpreg_reset_values = g_memdup(cpu->cpreg_values, + cpu->cpreg_array_len * + sizeof(cpu->cpreg_values[0])); + +out: + g_free(rlp); return ret; } @@ -154,6 +260,78 @@ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid) QSLIST_INSERT_HEAD(&kvm_devices_head, kd, entries); } +bool write_kvmstate_to_list(ARMCPU *cpu) +{ + CPUState *cs = CPU(cpu); + int i; + bool ok = true; + + for (i = 0; i < cpu->cpreg_array_len; i++) { + struct kvm_one_reg r; + uint64_t regidx = cpu->cpreg_indexes[i]; + uint32_t v32; + int ret; + + r.id = regidx; + + switch (regidx & KVM_REG_SIZE_MASK) { + case KVM_REG_SIZE_U32: + r.addr = (uintptr_t)&v32; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); + if (!ret) { + cpu->cpreg_values[i] = v32; + } + break; + case KVM_REG_SIZE_U64: + r.addr = (uintptr_t)(cpu->cpreg_values + i); + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); + break; + default: + abort(); + } + if (ret) { + ok = false; + } + } + return ok; +} + +bool write_list_to_kvmstate(ARMCPU *cpu) +{ + CPUState *cs = CPU(cpu); + int i; + bool ok = true; + + for (i = 0; i < cpu->cpreg_array_len; i++) { + struct kvm_one_reg r; + uint64_t regidx = cpu->cpreg_indexes[i]; + uint32_t v32; + int ret; + + r.id = regidx; + switch (regidx & KVM_REG_SIZE_MASK) { + case KVM_REG_SIZE_U32: + v32 = cpu->cpreg_values[i]; + r.addr = (uintptr_t)&v32; + break; + case KVM_REG_SIZE_U64: + r.addr = (uintptr_t)(cpu->cpreg_values + i); + break; + default: + abort(); + } + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); + if (ret) { + /* We might fail for "unknown register" and also for + * "you tried to set a register which is constant with + * a different value from what it actually contains". + */ + ok = false; + } + } + return ok; +} + typedef struct Reg { uint64_t id; int offset; @@ -166,17 +344,6 @@ typedef struct Reg { offsetof(CPUARMState, QEMUFIELD) \ } -#define CP15REG(CRN, CRM, OPC1, OPC2, QEMUFIELD) \ - { \ - KVM_REG_ARM | KVM_REG_SIZE_U32 | \ - (15 << KVM_REG_ARM_COPROC_SHIFT) | \ - ((CRN) << KVM_REG_ARM_32_CRN_SHIFT) | \ - ((CRM) << KVM_REG_ARM_CRM_SHIFT) | \ - ((OPC1) << KVM_REG_ARM_OPC1_SHIFT) | \ - ((OPC2) << KVM_REG_ARM_32_OPC2_SHIFT), \ - offsetof(CPUARMState, QEMUFIELD) \ - } - #define VFPSYSREG(R) \ { \ KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | \ @@ -225,12 +392,6 @@ static const Reg regs[] = { COREREG(fiq_regs[7], banked_spsr[5]), /* R15 */ COREREG(usr_regs.uregs[15], regs[15]), - /* A non-comprehensive set of cp15 registers. - * TODO: drive this from the cp_regs hashtable instead. - */ - CP15REG(1, 0, 0, 0, cp15.c1_sys), /* SCTLR */ - CP15REG(2, 0, 0, 2, cp15.c2_control), /* TTBCR */ - CP15REG(3, 0, 0, 0, cp15.c3), /* DACR */ /* VFP system registers */ VFPSYSREG(FPSID), VFPSYSREG(MVFR1), @@ -248,7 +409,6 @@ int kvm_arch_put_registers(CPUState *cs, int level) int mode, bn; int ret, i; uint32_t cpsr, fpscr; - uint64_t ttbr; /* Make sure the banked regs are properly set */ mode = env->uncached_cpsr & CPSR_M; @@ -282,26 +442,6 @@ int kvm_arch_put_registers(CPUState *cs, int level) return ret; } - /* TTBR0: cp15 crm=2 opc1=0 */ - ttbr = ((uint64_t)env->cp15.c2_base0_hi << 32) | env->cp15.c2_base0; - r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | (15 << KVM_REG_ARM_COPROC_SHIFT) | - (2 << KVM_REG_ARM_CRM_SHIFT) | (0 << KVM_REG_ARM_OPC1_SHIFT); - r.addr = (uintptr_t)(&ttbr); - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); - if (ret) { - return ret; - } - - /* TTBR1: cp15 crm=2 opc1=1 */ - ttbr = ((uint64_t)env->cp15.c2_base1_hi << 32) | env->cp15.c2_base1; - r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | (15 << KVM_REG_ARM_COPROC_SHIFT) | - (2 << KVM_REG_ARM_CRM_SHIFT) | (1 << KVM_REG_ARM_OPC1_SHIFT); - r.addr = (uintptr_t)(&ttbr); - ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); - if (ret) { - return ret; - } - /* VFP registers */ r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP; for (i = 0; i < 32; i++) { @@ -318,6 +458,31 @@ int kvm_arch_put_registers(CPUState *cs, int level) fpscr = vfp_get_fpscr(env); r.addr = (uintptr_t)&fpscr; ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); + if (ret) { + return ret; + } + + /* Note that we do not call write_cpustate_to_list() + * here, so we are only writing the tuple list back to + * KVM. This is safe because nothing can change the + * CPUARMState cp15 fields (in particular gdb accesses cannot) + * and so there are no changes to sync. In fact syncing would + * be wrong at this point: for a constant register where TCG and + * KVM disagree about its value, the preceding write_list_to_cpustate() + * would not have had any effect on the CPUARMState value (since the + * register is read-only), and a write_cpustate_to_list() here would + * then try to write the TCG value back into KVM -- this would either + * fail or incorrectly change the value the guest sees. + * + * If we ever want to allow the user to modify cp15 registers via + * the gdb stub, we would need to be more clever here (for instance + * tracking the set of registers kvm_arch_get_registers() successfully + * managed to update the CPUARMState with, and only allowing those + * to be written back up into the kernel). + */ + if (!write_list_to_kvmstate(cpu)) { + return EINVAL; + } return ret; } @@ -330,7 +495,6 @@ int kvm_arch_get_registers(CPUState *cs) int mode, bn; int ret, i; uint32_t cpsr, fpscr; - uint64_t ttbr; for (i = 0; i < ARRAY_SIZE(regs); i++) { r.id = regs[i].id; @@ -351,28 +515,6 @@ int kvm_arch_get_registers(CPUState *cs) } cpsr_write(env, cpsr, 0xffffffff); - /* TTBR0: cp15 crm=2 opc1=0 */ - r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | (15 << KVM_REG_ARM_COPROC_SHIFT) | - (2 << KVM_REG_ARM_CRM_SHIFT) | (0 << KVM_REG_ARM_OPC1_SHIFT); - r.addr = (uintptr_t)(&ttbr); - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); - if (ret) { - return ret; - } - env->cp15.c2_base0_hi = ttbr >> 32; - env->cp15.c2_base0 = ttbr; - - /* TTBR1: cp15 crm=2 opc1=1 */ - r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | (15 << KVM_REG_ARM_COPROC_SHIFT) | - (2 << KVM_REG_ARM_CRM_SHIFT) | (1 << KVM_REG_ARM_OPC1_SHIFT); - r.addr = (uintptr_t)(&ttbr); - ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); - if (ret) { - return ret; - } - env->cp15.c2_base1_hi = ttbr >> 32; - env->cp15.c2_base1 = ttbr; - /* Make sure the current mode regs are properly set */ mode = env->uncached_cpsr & CPSR_M; bn = bank_number(mode); @@ -385,15 +527,6 @@ int kvm_arch_get_registers(CPUState *cs) env->regs[14] = env->banked_r14[bn]; env->spsr = env->banked_spsr[bn]; - /* The main GET_ONE_REG loop above set c2_control, but we need to - * update some extra cached precomputed values too. - * When this is driven from the cp_regs hashtable then this ugliness - * can disappear because we'll use the access function which sets - * these values automatically. - */ - env->cp15.c2_mask = ~(0xffffffffu >> env->cp15.c2_control); - env->cp15.c2_base_mask = ~(0x3fffu >> env->cp15.c2_control); - /* VFP registers */ r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP; for (i = 0; i < 32; i++) { @@ -414,6 +547,14 @@ int kvm_arch_get_registers(CPUState *cs) } vfp_set_fpscr(env, fpscr); + if (!write_kvmstate_to_list(cpu)) { + return EINVAL; + } + /* Note that it's OK to have registers which aren't in CPUState, + * so we can ignore a failure return here. + */ + write_list_to_cpustate(cpu); + return 0; } @@ -432,6 +573,15 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) void kvm_arch_reset_vcpu(CPUState *cs) { + /* Feed the kernel back its initial register state */ + ARMCPU *cpu = ARM_CPU(cs); + + memmove(cpu->cpreg_values, cpu->cpreg_reset_values, + cpu->cpreg_array_len * sizeof(cpu->cpreg_values[0])); + + if (!write_list_to_kvmstate(cpu)) { + abort(); + } } bool kvm_arch_stop_on_emulation_error(CPUState *cs) diff --git a/target-arm/kvm_arm.h b/target-arm/kvm_arm.h index b1c54ffb5d..5d14887e66 100644 --- a/target-arm/kvm_arm.h +++ b/target-arm/kvm_arm.h @@ -29,4 +29,37 @@ */ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid); +/** + * write_list_to_kvmstate: + * @cpu: ARMCPU + * + * For each register listed in the ARMCPU cpreg_indexes list, write + * its value from the cpreg_values list into the kernel (via ioctl). + * This updates KVM's working data structures from TCG data or + * from incoming migration state. + * + * Returns: true if all register values were updated correctly, + * false if some register was unknown to the kernel or could not + * be written (eg constant register with the wrong value). + * Note that we do not stop early on failure -- we will attempt + * writing all registers in the list. + */ +bool write_list_to_kvmstate(ARMCPU *cpu); + +/** + * write_kvmstate_to_list: + * @cpu: ARMCPU + * + * For each register listed in the ARMCPU cpreg_indexes list, write + * its value from the kernel into the cpreg_values list. This is used to + * copy info from KVM's working data structures into TCG or + * for outbound migration. + * + * Returns: true if all register values were read correctly, + * false if some register was unknown or could not be read. + * Note that we do not stop early on failure -- we will attempt + * reading all registers in the list. + */ +bool write_kvmstate_to_list(ARMCPU *cpu); + #endif diff --git a/target-arm/machine.c b/target-arm/machine.c index 4dd057c488..6d4c2d4ed0 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -1,5 +1,7 @@ #include "hw/hw.h" #include "hw/boards.h" +#include "sysemu/kvm.h" +#include "kvm_arm.h" static bool vfp_needed(void *opaque) { @@ -148,11 +150,83 @@ static const VMStateInfo vmstate_cpsr = { .put = put_cpsr, }; +static void cpu_pre_save(void *opaque) +{ + ARMCPU *cpu = opaque; + + if (kvm_enabled()) { + if (!write_kvmstate_to_list(cpu)) { + /* This should never fail */ + abort(); + } + } else { + if (!write_cpustate_to_list(cpu)) { + /* This should never fail. */ + abort(); + } + } + + cpu->cpreg_vmstate_array_len = cpu->cpreg_array_len; + memcpy(cpu->cpreg_vmstate_indexes, cpu->cpreg_indexes, + cpu->cpreg_array_len * sizeof(uint64_t)); + memcpy(cpu->cpreg_vmstate_values, cpu->cpreg_values, + cpu->cpreg_array_len * sizeof(uint64_t)); +} + +static int cpu_post_load(void *opaque, int version_id) +{ + ARMCPU *cpu = opaque; + int i, v; + + /* Update the values list from the incoming migration data. + * Anything in the incoming data which we don't know about is + * a migration failure; anything we know about but the incoming + * data doesn't specify retains its current (reset) value. + * The indexes list remains untouched -- we only inspect the + * incoming migration index list so we can match the values array + * entries with the right slots in our own values array. + */ + + for (i = 0, v = 0; i < cpu->cpreg_array_len + && v < cpu->cpreg_vmstate_array_len; i++) { + if (cpu->cpreg_vmstate_indexes[v] > cpu->cpreg_indexes[i]) { + /* register in our list but not incoming : skip it */ + continue; + } + if (cpu->cpreg_vmstate_indexes[v] < cpu->cpreg_indexes[i]) { + /* register in their list but not ours: fail migration */ + return -1; + } + /* matching register, copy the value over */ + cpu->cpreg_values[i] = cpu->cpreg_vmstate_values[v]; + v++; + } + + if (kvm_enabled()) { + if (!write_list_to_kvmstate(cpu)) { + return -1; + } + /* Note that it's OK for the TCG side not to know about + * every register in the list; KVM is authoritative if + * we're using it. + */ + write_list_to_cpustate(cpu); + } else { + if (!write_list_to_cpustate(cpu)) { + return -1; + } + } + + return 0; +} + const VMStateDescription vmstate_arm_cpu = { .name = "cpu", - .version_id = 11, - .minimum_version_id = 11, - .minimum_version_id_old = 11, + .version_id = 12, + .minimum_version_id = 12, + .minimum_version_id_old = 12, + .pre_save = cpu_pre_save, + .post_load = cpu_post_load, .fields = (VMStateField[]) { VMSTATE_UINT32_ARRAY(env.regs, ARMCPU, 16), { @@ -169,50 +243,16 @@ const VMStateDescription vmstate_arm_cpu = { VMSTATE_UINT32_ARRAY(env.banked_r14, ARMCPU, 6), VMSTATE_UINT32_ARRAY(env.usr_regs, ARMCPU, 5), VMSTATE_UINT32_ARRAY(env.fiq_regs, ARMCPU, 5), - VMSTATE_UINT32(env.cp15.c0_cpuid, ARMCPU), - VMSTATE_UINT32(env.cp15.c0_cssel, ARMCPU), - VMSTATE_UINT32(env.cp15.c1_sys, ARMCPU), - VMSTATE_UINT32(env.cp15.c1_coproc, ARMCPU), - VMSTATE_UINT32(env.cp15.c1_xscaleauxcr, ARMCPU), - VMSTATE_UINT32(env.cp15.c1_scr, ARMCPU), - VMSTATE_UINT32(env.cp15.c2_base0, ARMCPU), - VMSTATE_UINT32(env.cp15.c2_base0_hi, ARMCPU), - VMSTATE_UINT32(env.cp15.c2_base1, ARMCPU), - VMSTATE_UINT32(env.cp15.c2_base1_hi, ARMCPU), - VMSTATE_UINT32(env.cp15.c2_control, ARMCPU), - VMSTATE_UINT32(env.cp15.c2_mask, ARMCPU), - VMSTATE_UINT32(env.cp15.c2_base_mask, ARMCPU), - VMSTATE_UINT32(env.cp15.c2_data, ARMCPU), - VMSTATE_UINT32(env.cp15.c2_insn, ARMCPU), - VMSTATE_UINT32(env.cp15.c3, ARMCPU), - VMSTATE_UINT32(env.cp15.c5_insn, ARMCPU), - VMSTATE_UINT32(env.cp15.c5_data, ARMCPU), - VMSTATE_UINT32_ARRAY(env.cp15.c6_region, ARMCPU, 8), - VMSTATE_UINT32(env.cp15.c6_insn, ARMCPU), - VMSTATE_UINT32(env.cp15.c6_data, ARMCPU), - VMSTATE_UINT32(env.cp15.c7_par, ARMCPU), - VMSTATE_UINT32(env.cp15.c7_par_hi, ARMCPU), - VMSTATE_UINT32(env.cp15.c9_insn, ARMCPU), - VMSTATE_UINT32(env.cp15.c9_data, ARMCPU), - VMSTATE_UINT32(env.cp15.c9_pmcr, ARMCPU), - VMSTATE_UINT32(env.cp15.c9_pmcnten, ARMCPU), - VMSTATE_UINT32(env.cp15.c9_pmovsr, ARMCPU), - VMSTATE_UINT32(env.cp15.c9_pmxevtyper, ARMCPU), - VMSTATE_UINT32(env.cp15.c9_pmuserenr, ARMCPU), - VMSTATE_UINT32(env.cp15.c9_pminten, ARMCPU), - VMSTATE_UINT32(env.cp15.c13_fcse, ARMCPU), - VMSTATE_UINT32(env.cp15.c13_context, ARMCPU), - VMSTATE_UINT32(env.cp15.c13_tls1, ARMCPU), - VMSTATE_UINT32(env.cp15.c13_tls2, ARMCPU), - VMSTATE_UINT32(env.cp15.c13_tls3, ARMCPU), - VMSTATE_UINT32(env.cp15.c15_cpar, ARMCPU), - VMSTATE_UINT32(env.cp15.c15_ticonfig, ARMCPU), - VMSTATE_UINT32(env.cp15.c15_i_max, ARMCPU), - VMSTATE_UINT32(env.cp15.c15_i_min, ARMCPU), - VMSTATE_UINT32(env.cp15.c15_threadid, ARMCPU), - VMSTATE_UINT32(env.cp15.c15_power_control, ARMCPU), - VMSTATE_UINT32(env.cp15.c15_diagnostic, ARMCPU), - VMSTATE_UINT32(env.cp15.c15_power_diagnostic, ARMCPU), + /* The length-check must come before the arrays to avoid + * incoming data possibly overflowing the array. + */ + VMSTATE_INT32_LE(cpreg_vmstate_array_len, ARMCPU), + VMSTATE_VARRAY_INT32(cpreg_vmstate_indexes, ARMCPU, + cpreg_vmstate_array_len, + 0, vmstate_info_uint64, uint64_t), + VMSTATE_VARRAY_INT32(cpreg_vmstate_values, ARMCPU, + cpreg_vmstate_array_len, + 0, vmstate_info_uint64, uint64_t), VMSTATE_UINT32(env.exclusive_addr, ARMCPU), VMSTATE_UINT32(env.exclusive_val, ARMCPU), VMSTATE_UINT32(env.exclusive_high, ARMCPU), diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h index 0ce82cf830..918c819c64 100644 --- a/target-s390x/cpu.h +++ b/target-s390x/cpu.h @@ -1081,6 +1081,7 @@ void kvm_s390_io_interrupt(S390CPU *cpu, uint16_t subchannel_id, void kvm_s390_crw_mchk(S390CPU *cpu); void kvm_s390_enable_css_support(S390CPU *cpu); int kvm_s390_get_registers_partial(CPUState *cpu); +int kvm_s390_assign_subch_ioeventfd(int fd, uint32_t sch, int vq, bool assign); #else static inline void kvm_s390_io_interrupt(S390CPU *cpu, uint16_t subchannel_id, @@ -1099,6 +1100,11 @@ static inline int kvm_s390_get_registers_partial(CPUState *cpu) { return -ENOSYS; } +static inline int kvm_s390_assign_subch_ioeventfd(int fd, uint32_t sch, int vq, + bool assign) +{ + return -ENOSYS; +} #endif static inline void s390_io_interrupt(S390CPU *cpu, @@ -1125,4 +1131,14 @@ static inline void s390_crw_mchk(S390CPU *cpu) } } +static inline int s390_assign_subch_ioeventfd(int fd, uint32_t sch_id, int vq, + bool assign) +{ + if (kvm_enabled()) { + return kvm_s390_assign_subch_ioeventfd(fd, sch_id, vq, assign); + } else { + return -ENOSYS; + } +} + #endif diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index 4d9ac4ad0c..650d3a5da9 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -932,3 +932,22 @@ void kvm_s390_enable_css_support(S390CPU *cpu) void kvm_arch_init_irq_routing(KVMState *s) { } + +int kvm_s390_assign_subch_ioeventfd(int fd, uint32_t sch, int vq, bool assign) +{ + struct kvm_ioeventfd kick = { + .flags = KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY | + KVM_IOEVENTFD_FLAG_DATAMATCH, + .fd = fd, + .datamatch = vq, + .addr = sch, + .len = 8, + }; + if (!kvm_check_extension(kvm_state, KVM_CAP_IOEVENTFD)) { + return -ENOSYS; + } + if (!assign) { + kick.flags |= KVM_IOEVENTFD_FLAG_DEASSIGN; + } + return kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &kick); +} @@ -161,18 +161,18 @@ static void xen_ram_init(ram_addr_t ram_size) ram_addr_t block_len; block_len = ram_size; - if (ram_size >= QEMU_BELOW_4G_RAM_END) { + if (ram_size >= HVM_BELOW_4G_RAM_END) { /* Xen does not allocate the memory continuously, and keep a hole at - * QEMU_BELOW_4G_RAM_END of QEMU_BELOW_4G_MMIO_LENGTH + * HVM_BELOW_4G_MMIO_START of HVM_BELOW_4G_MMIO_LENGTH */ - block_len += QEMU_BELOW_4G_MMIO_LENGTH; + block_len += HVM_BELOW_4G_MMIO_LENGTH; } memory_region_init_ram(&ram_memory, "xen.ram", block_len); vmstate_register_ram_global(&ram_memory); - if (ram_size >= QEMU_BELOW_4G_RAM_END) { - above_4g_mem_size = ram_size - QEMU_BELOW_4G_RAM_END; - below_4g_mem_size = QEMU_BELOW_4G_RAM_END; + if (ram_size >= HVM_BELOW_4G_RAM_END) { + above_4g_mem_size = ram_size - HVM_BELOW_4G_RAM_END; + below_4g_mem_size = HVM_BELOW_4G_RAM_END; } else { below_4g_mem_size = ram_size; } diff --git a/xen-stub.c b/xen-stub.c index 6f0516aa3e..47c8e73e0f 100644 --- a/xen-stub.c +++ b/xen-stub.c @@ -63,3 +63,8 @@ void qmp_xen_set_global_dirty_log(bool enable, Error **errp) void xen_modified_memory(ram_addr_t start, ram_addr_t length) { } + +int xen_hvm_init(void) +{ + return 0; +} |