diff options
85 files changed, 1721 insertions, 717 deletions
diff --git a/block/gluster.c b/block/gluster.c index 8ba3bcc..031596a 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -964,29 +964,6 @@ static coroutine_fn int qemu_gluster_co_pwrite_zeroes(BlockDriverState *bs, qemu_coroutine_yield(); return acb.ret; } - -static inline bool gluster_supports_zerofill(void) -{ - return 1; -} - -static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset, - int64_t size) -{ - return glfs_zerofill(fd, offset, size); -} - -#else -static inline bool gluster_supports_zerofill(void) -{ - return 0; -} - -static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset, - int64_t size) -{ - return 0; -} #endif static int qemu_gluster_create(const char *filename, @@ -996,9 +973,10 @@ static int qemu_gluster_create(const char *filename, struct glfs *glfs; struct glfs_fd *fd; int ret = 0; - int prealloc = 0; + PreallocMode prealloc; int64_t total_size = 0; char *tmp = NULL; + Error *local_err = NULL; gconf = g_new0(BlockdevOptionsGluster, 1); gconf->debug = qemu_opt_get_number_del(opts, GLUSTER_OPT_DEBUG, @@ -1026,13 +1004,12 @@ static int qemu_gluster_create(const char *filename, BDRV_SECTOR_SIZE); tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); - if (!tmp || !strcmp(tmp, "off")) { - prealloc = 0; - } else if (!strcmp(tmp, "full") && gluster_supports_zerofill()) { - prealloc = 1; - } else { - error_setg(errp, "Invalid preallocation mode: '%s'" - " or GlusterFS doesn't support zerofill API", tmp); + prealloc = qapi_enum_parse(PreallocMode_lookup, tmp, + PREALLOC_MODE__MAX, PREALLOC_MODE_OFF, + &local_err); + g_free(tmp); + if (local_err) { + error_propagate(errp, local_err); ret = -EINVAL; goto out; } @@ -1041,21 +1018,48 @@ static int qemu_gluster_create(const char *filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR); if (!fd) { ret = -errno; - } else { + goto out; + } + + switch (prealloc) { +#ifdef CONFIG_GLUSTERFS_FALLOCATE + case PREALLOC_MODE_FALLOC: + if (glfs_fallocate(fd, 0, 0, total_size)) { + error_setg(errp, "Could not preallocate data for the new file"); + ret = -errno; + } + break; +#endif /* CONFIG_GLUSTERFS_FALLOCATE */ +#ifdef CONFIG_GLUSTERFS_ZEROFILL + case PREALLOC_MODE_FULL: if (!glfs_ftruncate(fd, total_size)) { - if (prealloc && qemu_gluster_zerofill(fd, 0, total_size)) { + if (glfs_zerofill(fd, 0, total_size)) { + error_setg(errp, "Could not zerofill the new file"); ret = -errno; } } else { + error_setg(errp, "Could not resize file"); ret = -errno; } - - if (glfs_close(fd) != 0) { + break; +#endif /* CONFIG_GLUSTERFS_ZEROFILL */ + case PREALLOC_MODE_OFF: + if (glfs_ftruncate(fd, total_size) != 0) { ret = -errno; + error_setg(errp, "Could not resize file"); } + break; + default: + ret = -EINVAL; + error_setg(errp, "Unsupported preallocation mode: %s", + PreallocMode_lookup[prealloc]); + break; + } + + if (glfs_close(fd) != 0) { + ret = -errno; } out: - g_free(tmp); qapi_free_BlockdevOptionsGluster(gconf); glfs_clear_preopened(glfs); return ret; @@ -300,6 +300,7 @@ seccomp="" glusterfs="" glusterfs_xlator_opt="no" glusterfs_discard="no" +glusterfs_fallocate="no" glusterfs_zerofill="no" gtk="" gtkabi="" @@ -3583,6 +3584,7 @@ if test "$glusterfs" != "no" ; then glusterfs_discard="yes" fi if $pkg_config --atleast-version=6 glusterfs-api; then + glusterfs_fallocate="yes" glusterfs_zerofill="yes" fi else @@ -5757,6 +5759,10 @@ if test "$glusterfs_discard" = "yes" ; then echo "CONFIG_GLUSTERFS_DISCARD=y" >> $config_host_mak fi +if test "$glusterfs_fallocate" = "yes" ; then + echo "CONFIG_GLUSTERFS_FALLOCATE=y" >> $config_host_mak +fi + if test "$glusterfs_zerofill" = "yes" ; then echo "CONFIG_GLUSTERFS_ZEROFILL=y" >> $config_host_mak fi diff --git a/disas/libvixl/Makefile.objs b/disas/libvixl/Makefile.objs index bbe7695..860fb7f 100644 --- a/disas/libvixl/Makefile.objs +++ b/disas/libvixl/Makefile.objs @@ -7,5 +7,8 @@ libvixl_OBJS = vixl/utils.o \ # The -Wno-sign-compare is needed only for gcc 4.6, which complains about # some signed-unsigned equality comparisons which later gcc versions do not. $(addprefix $(obj)/,$(libvixl_OBJS)): QEMU_CFLAGS := -I$(SRC_PATH)/disas/libvixl $(QEMU_CFLAGS) -Wno-sign-compare +# Ensure that C99 macros are defined regardless of the inclusion order of +# headers in vixl. This is required at least on NetBSD. +$(addprefix $(obj)/,$(libvixl_OBJS)): QEMU_CFLAGS += -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -D__STDC_FORMAT_MACROS common-obj-$(CONFIG_ARM_A64_DIS) += $(libvixl_OBJS) diff --git a/docs/specs/vhost-user.txt b/docs/specs/vhost-user.txt index 036890f..481ab56 100644 --- a/docs/specs/vhost-user.txt +++ b/docs/specs/vhost-user.txt @@ -97,6 +97,25 @@ Depending on the request type, payload can be: log offset: offset from start of supplied file descriptor where logging starts (i.e. where guest address 0 would be logged) + * An IOTLB message + --------------------------------------------------------- + | iova | size | user address | permissions flags | type | + --------------------------------------------------------- + + IOVA: a 64-bit I/O virtual address programmed by the guest + Size: a 64-bit size + User address: a 64-bit user address + Permissions: a 8-bit value: + - 0: No access + - 1: Read access + - 2: Write access + - 3: Read/Write access + Type: a 8-bit IOTLB message type: + - 1: IOTLB miss + - 2: IOTLB update + - 3: IOTLB invalidate + - 4: IOTLB access fail + In QEMU the vhost-user message is implemented with the following struct: typedef struct VhostUserMsg { @@ -109,6 +128,7 @@ typedef struct VhostUserMsg { struct vhost_vring_addr addr; VhostUserMemory memory; VhostUserLog log; + struct vhost_iotlb_msg iotlb; }; } QEMU_PACKED VhostUserMsg; @@ -139,6 +159,7 @@ in the ancillary data: * VHOST_USER_SET_VRING_KICK * VHOST_USER_SET_VRING_CALL * VHOST_USER_SET_VRING_ERR + * VHOST_USER_SET_SLAVE_REQ_FD If Master is unable to send the full message or receives a wrong reply it will close the connection. An optional reconnection mechanism can be implemented. @@ -252,6 +273,50 @@ Once the source has finished migration, rings will be stopped by the source. No further update must be done before rings are restarted. +IOMMU support +------------- + +When the VIRTIO_F_IOMMU_PLATFORM feature has been negotiated, the master +sends IOTLB entries update & invalidation by sending VHOST_USER_IOTLB_MSG +requests to the slave with a struct vhost_iotlb_msg as payload. For update +events, the iotlb payload has to be filled with the update message type (2), +the I/O virtual address, the size, the user virtual address, and the +permissions flags. Addresses and size must be within vhost memory regions set +via the VHOST_USER_SET_MEM_TABLE request. For invalidation events, the iotlb +payload has to be filled with the invalidation message type (3), the I/O virtual +address and the size. On success, the slave is expected to reply with a zero +payload, non-zero otherwise. + +The slave relies on the slave communcation channel (see "Slave communication" +section below) to send IOTLB miss and access failure events, by sending +VHOST_USER_SLAVE_IOTLB_MSG requests to the master with a struct vhost_iotlb_msg +as payload. For miss events, the iotlb payload has to be filled with the miss +message type (1), the I/O virtual address and the permissions flags. For access +failure event, the iotlb payload has to be filled with the access failure +message type (4), the I/O virtual address and the permissions flags. +For synchronization purpose, the slave may rely on the reply-ack feature, +so the master may send a reply when operation is completed if the reply-ack +feature is negotiated and slaves requests a reply. For miss events, completed +operation means either master sent an update message containing the IOTLB entry +containing requested address and permission, or master sent nothing if the IOTLB +miss message is invalid (invalid IOVA or permission). + +The master isn't expected to take the initiative to send IOTLB update messages, +as the slave sends IOTLB miss messages for the guest virtual memory areas it +needs to access. + +Slave communication +------------------- + +An optional communication channel is provided if the slave declares +VHOST_USER_PROTOCOL_F_SLAVE_REQ protocol feature, to allow the slave to make +requests to the master. + +The fd is provided via VHOST_USER_SET_SLAVE_REQ_FD ancillary data. + +A slave may then send VHOST_USER_SLAVE_* messages to the master +using this fd communication channel. + Protocol features ----------------- @@ -260,9 +325,10 @@ Protocol features #define VHOST_USER_PROTOCOL_F_RARP 2 #define VHOST_USER_PROTOCOL_F_REPLY_ACK 3 #define VHOST_USER_PROTOCOL_F_MTU 4 +#define VHOST_USER_PROTOCOL_F_SLAVE_REQ 5 -Message types -------------- +Master message types +-------------------- * VHOST_USER_GET_FEATURES @@ -486,6 +552,52 @@ Message types If VHOST_USER_PROTOCOL_F_REPLY_ACK is negotiated, slave must respond with zero in case the specified MTU is valid, or non-zero otherwise. + * VHOST_USER_SET_SLAVE_REQ_FD + + Id: 21 + Equivalent ioctl: N/A + Master payload: N/A + + Set the socket file descriptor for slave initiated requests. It is passed + in the ancillary data. + This request should be sent only when VHOST_USER_F_PROTOCOL_FEATURES + has been negotiated, and protocol feature bit VHOST_USER_PROTOCOL_F_SLAVE_REQ + bit is present in VHOST_USER_GET_PROTOCOL_FEATURES. + If VHOST_USER_PROTOCOL_F_REPLY_ACK is negotiated, slave must respond + with zero for success, non-zero otherwise. + + * VHOST_USER_IOTLB_MSG + + Id: 22 + Equivalent ioctl: N/A (equivalent to VHOST_IOTLB_MSG message type) + Master payload: struct vhost_iotlb_msg + Slave payload: u64 + + Send IOTLB messages with struct vhost_iotlb_msg as payload. + Master sends such requests to update and invalidate entries in the device + IOTLB. The slave has to acknowledge the request with sending zero as u64 + payload for success, non-zero otherwise. + This request should be send only when VIRTIO_F_IOMMU_PLATFORM feature + has been successfully negotiated. + +Slave message types +------------------- + + * VHOST_USER_SLAVE_IOTLB_MSG + + Id: 1 + Equivalent ioctl: N/A (equivalent to VHOST_IOTLB_MSG message type) + Slave payload: struct vhost_iotlb_msg + Master payload: N/A + + Send IOTLB messages with struct vhost_iotlb_msg as payload. + Slave sends such requests to notify of an IOTLB miss, or an IOTLB + access failure. If VHOST_USER_PROTOCOL_F_REPLY_ACK is negotiated, + and slave set the VHOST_USER_NEED_REPLY flag, master must respond with + zero when operation is successfully completed, or non-zero otherwise. + This request should be send only when VIRTIO_F_IOMMU_PLATFORM feature + has been successfully negotiated. + VHOST_USER_PROTOCOL_F_REPLY_ACK: ------------------------------- The original vhost-user specification only demands replies for certain @@ -42,6 +42,7 @@ #include "qemu/error-report.h" #include "exec/ramlist.h" #include "hw/intc/intc.h" +#include "migration/snapshot.h" #ifdef CONFIG_SPICE #include <spice/enums.h> @@ -1284,7 +1285,7 @@ void hmp_loadvm(Monitor *mon, const QDict *qdict) vm_stop(RUN_STATE_RESTORE_VM); - if (load_vmstate(name, &err) == 0 && saved_vm_running) { + if (load_snapshot(name, &err) == 0 && saved_vm_running) { vm_start(); } hmp_handle_error(mon, &err); @@ -1294,7 +1295,7 @@ void hmp_savevm(Monitor *mon, const QDict *qdict) { Error *err = NULL; - save_vmstate(qdict_get_try_str(qdict, "name"), &err); + save_snapshot(qdict_get_try_str(qdict, "name"), &err); hmp_handle_error(mon, &err); } diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 283c038..e824ea8 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -39,6 +39,7 @@ typedef struct AspeedBoardConfig { const char *fmc_model; const char *spi_model; uint32_t num_cs; + void (*i2c_init)(AspeedBoardState *bmc); } AspeedBoardConfig; enum { @@ -82,6 +83,9 @@ enum { SCU_AST2500_HW_STRAP_ACPI_ENABLE | \ SCU_HW_STRAP_SPI_MODE(SCU_HW_STRAP_SPI_MASTER)) +static void palmetto_bmc_i2c_init(AspeedBoardState *bmc); +static void ast2500_evb_i2c_init(AspeedBoardState *bmc); + static const AspeedBoardConfig aspeed_boards[] = { [PALMETTO_BMC] = { .soc_name = "ast2400-a1", @@ -89,6 +93,7 @@ static const AspeedBoardConfig aspeed_boards[] = { .fmc_model = "n25q256a", .spi_model = "mx25l25635e", .num_cs = 1, + .i2c_init = palmetto_bmc_i2c_init, }, [AST2500_EVB] = { .soc_name = "ast2500-a1", @@ -96,6 +101,7 @@ static const AspeedBoardConfig aspeed_boards[] = { .fmc_model = "n25q256a", .spi_model = "mx25l25635e", .num_cs = 1, + .i2c_init = ast2500_evb_i2c_init, }, [ROMULUS_BMC] = { .soc_name = "ast2500-a1", @@ -223,9 +229,22 @@ static void aspeed_board_init(MachineState *machine, aspeed_board_binfo.ram_size = ram_size; aspeed_board_binfo.loader_start = sc->info->sdram_base; + if (cfg->i2c_init) { + cfg->i2c_init(bmc); + } + arm_load_kernel(ARM_CPU(first_cpu), &aspeed_board_binfo); } +static void palmetto_bmc_i2c_init(AspeedBoardState *bmc) +{ + AspeedSoCState *soc = &bmc->soc; + + /* The palmetto platform expects a ds3231 RTC but a ds1338 is + * enough to provide basic RTC features. Alarms will be missing */ + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 0), "ds1338", 0x68); +} + static void palmetto_bmc_init(MachineState *machine) { aspeed_board_init(machine, &aspeed_boards[PALMETTO_BMC]); @@ -250,6 +269,14 @@ static const TypeInfo palmetto_bmc_type = { .class_init = palmetto_bmc_class_init, }; +static void ast2500_evb_i2c_init(AspeedBoardState *bmc) +{ + AspeedSoCState *soc = &bmc->soc; + + /* The AST2500 EVB expects a LM75 but a TMP105 is compatible */ + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 7), "tmp105", 0x4d); +} + static void ast2500_evb_init(MachineState *machine) { aspeed_board_init(machine, &aspeed_boards[AST2500_EVB]); diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index e585206..2079828 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -776,6 +776,10 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) if (nb_numa_nodes > 0) { acpi_add_table(table_offsets, tables_blob); build_srat(tables_blob, tables->linker, vms); + if (have_numa_distance) { + acpi_add_table(table_offsets, tables_blob); + build_slit(tables_blob, tables->linker); + } } if (its_class_name() && !vmc->no_its) { diff --git a/hw/arm/virt.c b/hw/arm/virt.c index c7c8159..4db2d42 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -219,6 +219,27 @@ static void create_fdt(VirtMachineState *vms) "clk24mhz"); qemu_fdt_setprop_cell(fdt, "/apb-pclk", "phandle", vms->clock_phandle); + if (have_numa_distance) { + int size = nb_numa_nodes * nb_numa_nodes * 3 * sizeof(uint32_t); + uint32_t *matrix = g_malloc0(size); + int idx, i, j; + + for (i = 0; i < nb_numa_nodes; i++) { + for (j = 0; j < nb_numa_nodes; j++) { + idx = (i * nb_numa_nodes + j) * 3; + matrix[idx + 0] = cpu_to_be32(i); + matrix[idx + 1] = cpu_to_be32(j); + matrix[idx + 2] = cpu_to_be32(numa_info[i].distance[j]); + } + } + + qemu_fdt_add_subnode(fdt, "/distance-map"); + qemu_fdt_setprop_string(fdt, "/distance-map", "compatible", + "numa-distance-map-v1"); + qemu_fdt_setprop(fdt, "/distance-map", "distance-matrix", + matrix, size); + g_free(matrix); + } } static void fdt_add_psci_node(const VirtMachineState *vms) diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index d797a67..f5bc173 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -186,6 +186,9 @@ static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, port->elem->out_sg[i].iov_base + port->iov_offset, buf_size); + if (!port->elem) { /* bail if we got disconnected */ + return; + } if (port->throttled) { port->iov_idx = i; if (ret > 0) { @@ -1121,6 +1124,9 @@ static void virtio_serial_device_unrealize(DeviceState *dev, Error **errp) timer_free(vser->post_load->timer); g_free(vser->post_load); } + + qbus_set_hotplug_handler(BUS(&vser->bus), NULL, errp); + virtio_cleanup(vdev); } diff --git a/hw/core/loader.c b/hw/core/loader.c index bf17b42..f72930c 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -611,8 +611,9 @@ static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr, return -1; size = read(fd, hdr, sizeof(uboot_image_header_t)); - if (size < 0) + if (size < sizeof(uboot_image_header_t)) { goto out; + } bswap_uboot_header(hdr); diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 71ff95f..0ce45a2 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -37,7 +37,6 @@ #include "hw/boards.h" #include "hw/sysbus.h" #include "qapi-event.h" -#include "migration/vmstate.h" bool qdev_hotplug = false; static bool qdev_hot_added = false; diff --git a/hw/dma/sparc32_dma.c b/hw/dma/sparc32_dma.c index 9c6bdc6..eb491b5 100644 --- a/hw/dma/sparc32_dma.c +++ b/hw/dma/sparc32_dma.c @@ -270,23 +270,28 @@ static const VMStateDescription vmstate_dma = { } }; -static int sparc32_dma_init1(SysBusDevice *sbd) +static void sparc32_dma_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - DMAState *s = SPARC32_DMA(dev); - int reg_size; + DeviceState *dev = DEVICE(obj); + DMAState *s = SPARC32_DMA(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); sysbus_init_irq(sbd, &s->irq); - reg_size = s->is_ledma ? DMA_ETH_SIZE : DMA_SIZE; - memory_region_init_io(&s->iomem, OBJECT(s), &dma_mem_ops, s, - "dma", reg_size); sysbus_init_mmio(sbd, &s->iomem); qdev_init_gpio_in(dev, dma_set_irq, 1); qdev_init_gpio_out(dev, s->gpio, 2); +} - return 0; +static void sparc32_dma_realize(DeviceState *dev, Error **errp) +{ + DMAState *s = SPARC32_DMA(dev); + int reg_size; + + reg_size = s->is_ledma ? DMA_ETH_SIZE : DMA_SIZE; + memory_region_init_io(&s->iomem, OBJECT(dev), &dma_mem_ops, s, + "dma", reg_size); } static Property sparc32_dma_properties[] = { @@ -298,12 +303,11 @@ static Property sparc32_dma_properties[] = { static void sparc32_dma_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = sparc32_dma_init1; dc->reset = dma_reset; dc->vmsd = &vmstate_dma; dc->props = sparc32_dma_properties; + dc->realize = sparc32_dma_realize; /* Reason: pointer property "iommu_opaque" */ dc->user_creatable = false; } @@ -312,6 +316,7 @@ static const TypeInfo sparc32_dma_info = { .name = TYPE_SPARC32_DMA, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(DMAState), + .instance_init = sparc32_dma_init, .class_init = sparc32_dma_class_init, }; diff --git a/hw/dma/sun4m_iommu.c b/hw/dma/sun4m_iommu.c index b3cbc54..335ef63 100644 --- a/hw/dma/sun4m_iommu.c +++ b/hw/dma/sun4m_iommu.c @@ -349,17 +349,16 @@ static void iommu_reset(DeviceState *d) s->regs[IOMMU_MASK_ID] = IOMMU_TS_MASK; } -static int iommu_init1(SysBusDevice *dev) +static void iommu_init(Object *obj) { - IOMMUState *s = SUN4M_IOMMU(dev); + IOMMUState *s = SUN4M_IOMMU(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); sysbus_init_irq(dev, &s->irq); - memory_region_init_io(&s->iomem, OBJECT(s), &iommu_mem_ops, s, "iommu", + memory_region_init_io(&s->iomem, obj, &iommu_mem_ops, s, "iommu", IOMMU_NREGS * sizeof(uint32_t)); sysbus_init_mmio(dev, &s->iomem); - - return 0; } static Property iommu_properties[] = { @@ -370,9 +369,7 @@ static Property iommu_properties[] = { static void iommu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = iommu_init1; dc->reset = iommu_reset; dc->vmsd = &vmstate_iommu; dc->props = iommu_properties; @@ -382,6 +379,7 @@ static const TypeInfo iommu_info = { .name = TYPE_SUN4M_IOMMU, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IOMMUState), + .instance_init = iommu_init, .class_init = iommu_class_init, }; diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index ce5b1f0..c762c73 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -169,12 +169,33 @@ static uint64_t aspeed_i2c_bus_read(void *opaque, hwaddr offset, } } +static void aspeed_i2c_set_state(AspeedI2CBus *bus, uint8_t state) +{ + bus->cmd &= ~(I2CD_TX_STATE_MASK << I2CD_TX_STATE_SHIFT); + bus->cmd |= (state & I2CD_TX_STATE_MASK) << I2CD_TX_STATE_SHIFT; +} + +static uint8_t aspeed_i2c_get_state(AspeedI2CBus *bus) +{ + return (bus->cmd >> I2CD_TX_STATE_SHIFT) & I2CD_TX_STATE_MASK; +} + +/* + * The state machine needs some refinement. It is only used to track + * invalid STOP commands for the moment. + */ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) { + bus->cmd &= ~0xFFFF; bus->cmd |= value & 0xFFFF; bus->intr_status = 0; if (bus->cmd & I2CD_M_START_CMD) { + uint8_t state = aspeed_i2c_get_state(bus) & I2CD_MACTIVE ? + I2CD_MSTARTR : I2CD_MSTART; + + aspeed_i2c_set_state(bus, state); + if (i2c_start_transfer(bus->bus, extract32(bus->buf, 1, 7), extract32(bus->buf, 0, 1))) { bus->intr_status |= I2CD_INTR_TX_NAK; @@ -182,16 +203,34 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) bus->intr_status |= I2CD_INTR_TX_ACK; } - } else if (bus->cmd & I2CD_M_TX_CMD) { + /* START command is also a TX command, as the slave address is + * sent on the bus */ + bus->cmd &= ~(I2CD_M_START_CMD | I2CD_M_TX_CMD); + + /* No slave found */ + if (!i2c_bus_busy(bus->bus)) { + return; + } + aspeed_i2c_set_state(bus, I2CD_MACTIVE); + } + + if (bus->cmd & I2CD_M_TX_CMD) { + aspeed_i2c_set_state(bus, I2CD_MTXD); if (i2c_send(bus->bus, bus->buf)) { - bus->intr_status |= (I2CD_INTR_TX_NAK | I2CD_INTR_ABNORMAL); + bus->intr_status |= (I2CD_INTR_TX_NAK); i2c_end_transfer(bus->bus); } else { bus->intr_status |= I2CD_INTR_TX_ACK; } + bus->cmd &= ~I2CD_M_TX_CMD; + aspeed_i2c_set_state(bus, I2CD_MACTIVE); + } - } else if (bus->cmd & I2CD_M_RX_CMD) { - int ret = i2c_recv(bus->bus); + if (bus->cmd & (I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST)) { + int ret; + + aspeed_i2c_set_state(bus, I2CD_MRXD); + ret = i2c_recv(bus->bus); if (ret < 0) { qemu_log_mask(LOG_GUEST_ERROR, "%s: read failed\n", __func__); ret = 0xff; @@ -199,20 +238,25 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) bus->intr_status |= I2CD_INTR_RX_DONE; } bus->buf = (ret & I2CD_BYTE_BUF_RX_MASK) << I2CD_BYTE_BUF_RX_SHIFT; + if (bus->cmd & I2CD_M_S_RX_CMD_LAST) { + i2c_nack(bus->bus); + } + bus->cmd &= ~(I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST); + aspeed_i2c_set_state(bus, I2CD_MACTIVE); } - if (bus->cmd & (I2CD_M_STOP_CMD | I2CD_M_S_RX_CMD_LAST)) { - if (!i2c_bus_busy(bus->bus)) { + if (bus->cmd & I2CD_M_STOP_CMD) { + if (!(aspeed_i2c_get_state(bus) & I2CD_MACTIVE)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: abnormal stop\n", __func__); bus->intr_status |= I2CD_INTR_ABNORMAL; } else { + aspeed_i2c_set_state(bus, I2CD_MSTOP); i2c_end_transfer(bus->bus); bus->intr_status |= I2CD_INTR_NORMAL_STOP; } + bus->cmd &= ~I2CD_M_STOP_CMD; + aspeed_i2c_set_state(bus, I2CD_IDLE); } - - /* command is handled, reset it and check for interrupts */ - bus->cmd &= ~0xFFFF; - aspeed_i2c_bus_raise_interrupt(bus); } static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, @@ -262,6 +306,7 @@ static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, } aspeed_i2c_bus_handle_cmd(bus, value); + aspeed_i2c_bus_raise_interrupt(bus); break; default: diff --git a/hw/i2c/i2c-ddc.c b/hw/i2c/i2c-ddc.c index 66899d7..6b92e95 100644 --- a/hw/i2c/i2c-ddc.c +++ b/hw/i2c/i2c-ddc.c @@ -17,6 +17,7 @@ */ #include "qemu/osdep.h" +#include "qemu-common.h" #include "qemu/log.h" #include "hw/i2c/i2c.h" #include "hw/i2c/i2c-ddc.h" diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 0b20856..09d8ba0 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -216,18 +216,35 @@ static uint32_t icv_gprio_mask(GICv3CPUState *cs, int group) { /* Return a mask word which clears the subpriority bits from * a priority value for a virtual interrupt in the specified group. - * This depends on the VBPR value: + * This depends on the VBPR value. + * If using VBPR0 then: * a BPR of 0 means the group priority bits are [7:1]; * a BPR of 1 means they are [7:2], and so on down to * a BPR of 7 meaning no group priority bits at all. + * If using VBPR1 then: + * a BPR of 0 is impossible (the minimum value is 1) + * a BPR of 1 means the group priority bits are [7:1]; + * a BPR of 2 means they are [7:2], and so on down to + * a BPR of 7 meaning the group priority is [7]. + * * Which BPR to use depends on the group of the interrupt and * the current ICH_VMCR_EL2.VCBPR settings. + * + * This corresponds to the VGroupBits() pseudocode. */ + int bpr; + if (group == GICV3_G1NS && cs->ich_vmcr_el2 & ICH_VMCR_EL2_VCBPR) { group = GICV3_G0; } - return ~0U << (read_vbpr(cs, group) + 1); + bpr = read_vbpr(cs, group); + if (group == GICV3_G1NS) { + assert(bpr > 0); + bpr--; + } + + return ~0U << (bpr + 1); } static bool icv_hppi_can_preempt(GICv3CPUState *cs, uint64_t lr) @@ -674,20 +691,37 @@ static uint32_t icc_gprio_mask(GICv3CPUState *cs, int group) { /* Return a mask word which clears the subpriority bits from * a priority value for an interrupt in the specified group. - * This depends on the BPR value: + * This depends on the BPR value. For CBPR0 (S or NS): * a BPR of 0 means the group priority bits are [7:1]; * a BPR of 1 means they are [7:2], and so on down to * a BPR of 7 meaning no group priority bits at all. + * For CBPR1 NS: + * a BPR of 0 is impossible (the minimum value is 1) + * a BPR of 1 means the group priority bits are [7:1]; + * a BPR of 2 means they are [7:2], and so on down to + * a BPR of 7 meaning the group priority is [7]. + * * Which BPR to use depends on the group of the interrupt and * the current ICC_CTLR.CBPR settings. + * + * This corresponds to the GroupBits() pseudocode. */ + int bpr; + if ((group == GICV3_G1 && cs->icc_ctlr_el1[GICV3_S] & ICC_CTLR_EL1_CBPR) || (group == GICV3_G1NS && cs->icc_ctlr_el1[GICV3_NS] & ICC_CTLR_EL1_CBPR)) { group = GICV3_G0; } - return ~0U << ((cs->icc_bpr[group] & 7) + 1); + bpr = cs->icc_bpr[group] & 7; + + if (group == GICV3_G1NS) { + assert(bpr > 0); + bpr--; + } + + return ~0U << (bpr + 1); } static bool icc_no_enabled_hppi(GICv3CPUState *cs) @@ -1388,6 +1422,7 @@ static void icc_bpr_write(CPUARMState *env, const ARMCPRegInfo *ri, { GICv3CPUState *cs = icc_cs_from_env(env); int grp = (ri->crm == 8) ? GICV3_G0 : GICV3_G1; + uint64_t minval; if (icv_access(env, grp == GICV3_G0 ? HCR_FMO : HCR_IMO)) { icv_bpr_write(env, ri, value); @@ -1415,6 +1450,11 @@ static void icc_bpr_write(CPUARMState *env, const ARMCPRegInfo *ri, return; } + minval = (grp == GICV3_G1NS) ? GIC_MIN_BPR_NS : GIC_MIN_BPR; + if (value < minval) { + value = minval; + } + cs->icc_bpr[grp] = value & 7; gicv3_cpuif_update(cs); } @@ -2014,7 +2054,7 @@ static void icc_reset(CPUARMState *env, const ARMCPRegInfo *ri) cs->ich_hcr_el2 = 0; memset(cs->ich_lr_el2, 0, sizeof(cs->ich_lr_el2)); cs->ich_vmcr_el2 = ICH_VMCR_EL2_VFIQEN | - (icv_min_vbpr(cs) << ICH_VMCR_EL2_VBPR1_SHIFT) | + ((icv_min_vbpr(cs) + 1) << ICH_VMCR_EL2_VBPR1_SHIFT) | (icv_min_vbpr(cs) << ICH_VMCR_EL2_VBPR0_SHIFT); } diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 32ffa0b..26a4b2d 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -19,6 +19,7 @@ #include "hw/arm/arm.h" #include "hw/arm/armv7m_nvic.h" #include "target/arm/cpu.h" +#include "exec/exec-all.h" #include "qemu/log.h" #include "trace.h" @@ -528,6 +529,39 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset) case 0xd70: /* ISAR4. */ return 0x01310102; /* TODO: Implement debug registers. */ + case 0xd90: /* MPU_TYPE */ + /* Unified MPU; if the MPU is not present this value is zero */ + return cpu->pmsav7_dregion << 8; + break; + case 0xd94: /* MPU_CTRL */ + return cpu->env.v7m.mpu_ctrl; + case 0xd98: /* MPU_RNR */ + return cpu->env.cp15.c6_rgnr; + case 0xd9c: /* MPU_RBAR */ + case 0xda4: /* MPU_RBAR_A1 */ + case 0xdac: /* MPU_RBAR_A2 */ + case 0xdb4: /* MPU_RBAR_A3 */ + { + int region = cpu->env.cp15.c6_rgnr; + + if (region >= cpu->pmsav7_dregion) { + return 0; + } + return (cpu->env.pmsav7.drbar[region] & 0x1f) | (region & 0xf); + } + case 0xda0: /* MPU_RASR */ + case 0xda8: /* MPU_RASR_A1 */ + case 0xdb0: /* MPU_RASR_A2 */ + case 0xdb8: /* MPU_RASR_A3 */ + { + int region = cpu->env.cp15.c6_rgnr; + + if (region >= cpu->pmsav7_dregion) { + return 0; + } + return ((cpu->env.pmsav7.dracr[region] & 0xffff) << 16) | + (cpu->env.pmsav7.drsr[region] & 0xffff); + } default: qemu_log_mask(LOG_GUEST_ERROR, "NVIC: Bad read offset 0x%x\n", offset); return 0; @@ -627,6 +661,76 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value) qemu_log_mask(LOG_UNIMP, "NVIC: Aux fault status registers unimplemented\n"); break; + case 0xd90: /* MPU_TYPE */ + return; /* RO */ + case 0xd94: /* MPU_CTRL */ + if ((value & + (R_V7M_MPU_CTRL_HFNMIENA_MASK | R_V7M_MPU_CTRL_ENABLE_MASK)) + == R_V7M_MPU_CTRL_HFNMIENA_MASK) { + qemu_log_mask(LOG_GUEST_ERROR, "MPU_CTRL: HFNMIENA and !ENABLE is " + "UNPREDICTABLE\n"); + } + cpu->env.v7m.mpu_ctrl = value & (R_V7M_MPU_CTRL_ENABLE_MASK | + R_V7M_MPU_CTRL_HFNMIENA_MASK | + R_V7M_MPU_CTRL_PRIVDEFENA_MASK); + tlb_flush(CPU(cpu)); + break; + case 0xd98: /* MPU_RNR */ + if (value >= cpu->pmsav7_dregion) { + qemu_log_mask(LOG_GUEST_ERROR, "MPU region out of range %" + PRIu32 "/%" PRIu32 "\n", + value, cpu->pmsav7_dregion); + } else { + cpu->env.cp15.c6_rgnr = value; + } + break; + case 0xd9c: /* MPU_RBAR */ + case 0xda4: /* MPU_RBAR_A1 */ + case 0xdac: /* MPU_RBAR_A2 */ + case 0xdb4: /* MPU_RBAR_A3 */ + { + int region; + + if (value & (1 << 4)) { + /* VALID bit means use the region number specified in this + * value and also update MPU_RNR.REGION with that value. + */ + region = extract32(value, 0, 4); + if (region >= cpu->pmsav7_dregion) { + qemu_log_mask(LOG_GUEST_ERROR, + "MPU region out of range %u/%" PRIu32 "\n", + region, cpu->pmsav7_dregion); + return; + } + cpu->env.cp15.c6_rgnr = region; + } else { + region = cpu->env.cp15.c6_rgnr; + } + + if (region >= cpu->pmsav7_dregion) { + return; + } + + cpu->env.pmsav7.drbar[region] = value & ~0x1f; + tlb_flush(CPU(cpu)); + break; + } + case 0xda0: /* MPU_RASR */ + case 0xda8: /* MPU_RASR_A1 */ + case 0xdb0: /* MPU_RASR_A2 */ + case 0xdb8: /* MPU_RASR_A3 */ + { + int region = cpu->env.cp15.c6_rgnr; + + if (region >= cpu->pmsav7_dregion) { + return; + } + + cpu->env.pmsav7.drsr[region] = value & 0xff3f; + cpu->env.pmsav7.dracr[region] = (value >> 16) & 0x173f; + tlb_flush(CPU(cpu)); + break; + } case 0xf00: /* Software Triggered Interrupt Register */ { /* user mode can only write to STIR if CCR.USERSETMPEND permits it */ diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index 711c114..a26e906 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "hw/sysbus.h" -#include "migration/qemu-file.h" #include "hw/s390x/s390_flic.h" #include "trace.h" #include "hw/qdev.h" diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c index cc44bc4..b4c61d8 100644 --- a/hw/intc/s390_flic_kvm.c +++ b/hw/intc/s390_flic_kvm.c @@ -17,7 +17,6 @@ #include "qemu/error-report.h" #include "hw/sysbus.h" #include "sysemu/kvm.h" -#include "migration/qemu-file.h" #include "hw/s390x/s390_flic.h" #include "hw/s390x/adapter.h" #include "trace.h" diff --git a/hw/misc/eccmemctl.c b/hw/misc/eccmemctl.c index a0071f3..bb7cc52 100644 --- a/hw/misc/eccmemctl.c +++ b/hw/misc/eccmemctl.c @@ -295,22 +295,29 @@ static void ecc_reset(DeviceState *d) s->regs[ECC_ECR1] = 0; } -static int ecc_init1(SysBusDevice *dev) +static void ecc_init(Object *obj) { - ECCState *s = ECC_MEMCTL(dev); + ECCState *s = ECC_MEMCTL(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); sysbus_init_irq(dev, &s->irq); - s->regs[0] = s->version; - memory_region_init_io(&s->iomem, OBJECT(dev), &ecc_mem_ops, s, "ecc", ECC_SIZE); + + memory_region_init_io(&s->iomem, obj, &ecc_mem_ops, s, "ecc", ECC_SIZE); sysbus_init_mmio(dev, &s->iomem); +} + +static void ecc_realize(DeviceState *dev, Error **errp) +{ + ECCState *s = ECC_MEMCTL(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + s->regs[0] = s->version; if (s->version == ECC_MCC) { // SS-600MP only memory_region_init_io(&s->iomem_diag, OBJECT(dev), &ecc_diag_mem_ops, s, "ecc.diag", ECC_DIAG_SIZE); - sysbus_init_mmio(dev, &s->iomem_diag); + sysbus_init_mmio(sbd, &s->iomem_diag); } - - return 0; } static Property ecc_properties[] = { @@ -321,9 +328,8 @@ static Property ecc_properties[] = { static void ecc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = ecc_init1; + dc->realize = ecc_realize; dc->reset = ecc_reset; dc->vmsd = &vmstate_ecc; dc->props = ecc_properties; @@ -333,6 +339,7 @@ static const TypeInfo ecc_info = { .name = TYPE_ECC_MEMCTL, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(ECCState), + .instance_init = ecc_init, .class_init = ecc_class_init, }; diff --git a/hw/misc/slavio_misc.c b/hw/misc/slavio_misc.c index 18ff677..0b33cdc 100644 --- a/hw/misc/slavio_misc.c +++ b/hw/misc/slavio_misc.c @@ -414,76 +414,73 @@ static const VMStateDescription vmstate_misc = { } }; -static int apc_init1(SysBusDevice *dev) +static void apc_init(Object *obj) { - APCState *s = APC(dev); + APCState *s = APC(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); sysbus_init_irq(dev, &s->cpu_halt); /* Power management (APC) XXX: not a Slavio device */ - memory_region_init_io(&s->iomem, OBJECT(s), &apc_mem_ops, s, + memory_region_init_io(&s->iomem, obj, &apc_mem_ops, s, "apc", MISC_SIZE); sysbus_init_mmio(dev, &s->iomem); - return 0; } -static int slavio_misc_init1(SysBusDevice *sbd) +static void slavio_misc_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - MiscState *s = SLAVIO_MISC(dev); + DeviceState *dev = DEVICE(obj); + MiscState *s = SLAVIO_MISC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); sysbus_init_irq(sbd, &s->irq); sysbus_init_irq(sbd, &s->fdc_tc); /* 8 bit registers */ /* Slavio control */ - memory_region_init_io(&s->cfg_iomem, OBJECT(s), &slavio_cfg_mem_ops, s, + memory_region_init_io(&s->cfg_iomem, obj, &slavio_cfg_mem_ops, s, "configuration", MISC_SIZE); sysbus_init_mmio(sbd, &s->cfg_iomem); /* Diagnostics */ - memory_region_init_io(&s->diag_iomem, OBJECT(s), &slavio_diag_mem_ops, s, + memory_region_init_io(&s->diag_iomem, obj, &slavio_diag_mem_ops, s, "diagnostic", MISC_SIZE); sysbus_init_mmio(sbd, &s->diag_iomem); /* Modem control */ - memory_region_init_io(&s->mdm_iomem, OBJECT(s), &slavio_mdm_mem_ops, s, + memory_region_init_io(&s->mdm_iomem, obj, &slavio_mdm_mem_ops, s, "modem", MISC_SIZE); sysbus_init_mmio(sbd, &s->mdm_iomem); /* 16 bit registers */ /* ss600mp diag LEDs */ - memory_region_init_io(&s->led_iomem, OBJECT(s), &slavio_led_mem_ops, s, + memory_region_init_io(&s->led_iomem, obj, &slavio_led_mem_ops, s, "leds", LED_SIZE); sysbus_init_mmio(sbd, &s->led_iomem); /* 32 bit registers */ /* System control */ - memory_region_init_io(&s->sysctrl_iomem, OBJECT(s), &slavio_sysctrl_mem_ops, s, + memory_region_init_io(&s->sysctrl_iomem, obj, &slavio_sysctrl_mem_ops, s, "system-control", SYSCTRL_SIZE); sysbus_init_mmio(sbd, &s->sysctrl_iomem); /* AUX 1 (Misc System Functions) */ - memory_region_init_io(&s->aux1_iomem, OBJECT(s), &slavio_aux1_mem_ops, s, + memory_region_init_io(&s->aux1_iomem, obj, &slavio_aux1_mem_ops, s, "misc-system-functions", MISC_SIZE); sysbus_init_mmio(sbd, &s->aux1_iomem); /* AUX 2 (Software Powerdown Control) */ - memory_region_init_io(&s->aux2_iomem, OBJECT(s), &slavio_aux2_mem_ops, s, + memory_region_init_io(&s->aux2_iomem, obj, &slavio_aux2_mem_ops, s, "software-powerdown-control", MISC_SIZE); sysbus_init_mmio(sbd, &s->aux2_iomem); qdev_init_gpio_in(dev, slavio_set_power_fail, 1); - - return 0; } static void slavio_misc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = slavio_misc_init1; dc->reset = slavio_misc_reset; dc->vmsd = &vmstate_misc; } @@ -492,21 +489,15 @@ static const TypeInfo slavio_misc_info = { .name = TYPE_SLAVIO_MISC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MiscState), + .instance_init = slavio_misc_init, .class_init = slavio_misc_class_init, }; -static void apc_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = apc_init1; -} - static const TypeInfo apc_info = { .name = TYPE_APC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MiscState), - .class_init = apc_class_init, + .instance_init = apc_init, }; static void slavio_misc_register_types(void) diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 22874a9..e037db6 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -77,6 +77,7 @@ static const int user_feature_bits[] = { VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_MTU, + VIRTIO_F_IOMMU_PLATFORM, /* This bit implies RARP isn't sent by QEMU out of band */ VIRTIO_NET_F_GUEST_ANNOUNCE, diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index e2d4e1a..619152c 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -12,7 +12,6 @@ #include "qemu/osdep.h" #include "hw/boards.h" #include "qmp-commands.h" -#include "migration/qemu-file.h" #include "hw/s390x/storage-keys.h" #include "qemu/error-report.h" #include "sysemu/kvm.h" diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 5f022cc..0faff46 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -585,30 +585,23 @@ typedef struct IDRegState { MemoryRegion mem; } IDRegState; -static int idreg_init1(SysBusDevice *dev) +static void idreg_init1(Object *obj) { - IDRegState *s = MACIO_ID_REGISTER(dev); + IDRegState *s = MACIO_ID_REGISTER(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - memory_region_init_ram(&s->mem, OBJECT(s), + memory_region_init_ram(&s->mem, obj, "sun4m.idreg", sizeof(idreg_data), &error_fatal); vmstate_register_ram_global(&s->mem); memory_region_set_readonly(&s->mem, true); sysbus_init_mmio(dev, &s->mem); - return 0; -} - -static void idreg_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = idreg_init1; } static const TypeInfo idreg_info = { .name = TYPE_MACIO_ID_REGISTER, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IDRegState), - .class_init = idreg_class_init, + .instance_init = idreg_init1, }; #define TYPE_TCX_AFX "tcx_afx" @@ -633,28 +626,21 @@ static void afx_init(hwaddr addr) sysbus_mmio_map(s, 0, addr); } -static int afx_init1(SysBusDevice *dev) +static void afx_init1(Object *obj) { - AFXState *s = TCX_AFX(dev); + AFXState *s = TCX_AFX(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - memory_region_init_ram(&s->mem, OBJECT(s), "sun4m.afx", 4, &error_fatal); + memory_region_init_ram(&s->mem, obj, "sun4m.afx", 4, &error_fatal); vmstate_register_ram_global(&s->mem); sysbus_init_mmio(dev, &s->mem); - return 0; -} - -static void afx_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = afx_init1; } static const TypeInfo afx_info = { .name = TYPE_TCX_AFX, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(AFXState), - .class_init = afx_class_init, + .instance_init = afx_init1, }; #define TYPE_OPENPROM "openprom" @@ -707,16 +693,16 @@ static void prom_init(hwaddr addr, const char *bios_name) } } -static int prom_init1(SysBusDevice *dev) +static void prom_init1(Object *obj) { - PROMState *s = OPENPROM(dev); + PROMState *s = OPENPROM(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - memory_region_init_ram(&s->prom, OBJECT(s), "sun4m.prom", PROM_SIZE_MAX, + memory_region_init_ram(&s->prom, obj, "sun4m.prom", PROM_SIZE_MAX, &error_fatal); vmstate_register_ram_global(&s->prom); memory_region_set_readonly(&s->prom, true); sysbus_init_mmio(dev, &s->prom); - return 0; } static Property prom_properties[] = { @@ -726,9 +712,7 @@ static Property prom_properties[] = { static void prom_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = prom_init1; dc->props = prom_properties; } @@ -737,6 +721,7 @@ static const TypeInfo prom_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PROMState), .class_init = prom_class_init, + .instance_init = prom_init1, }; #define TYPE_SUN4M_MEMORY "memory" @@ -750,14 +735,14 @@ typedef struct RamDevice { } RamDevice; /* System RAM */ -static int ram_init1(SysBusDevice *dev) +static void ram_realize(DeviceState *dev, Error **errp) { RamDevice *d = SUN4M_RAM(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); memory_region_allocate_system_memory(&d->ram, OBJECT(d), "sun4m.ram", d->size); - sysbus_init_mmio(dev, &d->ram); - return 0; + sysbus_init_mmio(sbd, &d->ram); } static void ram_init(hwaddr addr, ram_addr_t RAM_size, @@ -793,9 +778,8 @@ static Property ram_properties[] = { static void ram_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = ram_init1; + dc->realize = ram_realize; dc->props = ram_properties; } diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index d347b66..18b8f8b 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -329,16 +329,16 @@ static void prom_init(hwaddr addr, const char *bios_name) } } -static int prom_init1(SysBusDevice *dev) +static void prom_init1(Object *obj) { - PROMState *s = OPENPROM(dev); + PROMState *s = OPENPROM(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - memory_region_init_ram(&s->prom, OBJECT(s), "sun4u.prom", PROM_SIZE_MAX, + memory_region_init_ram(&s->prom, obj, "sun4u.prom", PROM_SIZE_MAX, &error_fatal); vmstate_register_ram_global(&s->prom); memory_region_set_readonly(&s->prom, true); sysbus_init_mmio(dev, &s->prom); - return 0; } static Property prom_properties[] = { @@ -348,9 +348,7 @@ static Property prom_properties[] = { static void prom_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = prom_init1; dc->props = prom_properties; } @@ -359,6 +357,7 @@ static const TypeInfo prom_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PROMState), .class_init = prom_class_init, + .instance_init = prom_init1, }; @@ -373,15 +372,15 @@ typedef struct RamDevice { } RamDevice; /* System RAM */ -static int ram_init1(SysBusDevice *dev) +static void ram_realize(DeviceState *dev, Error **errp) { RamDevice *d = SUN4U_RAM(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); memory_region_init_ram(&d->ram, OBJECT(d), "sun4u.ram", d->size, &error_fatal); vmstate_register_ram_global(&d->ram); - sysbus_init_mmio(dev, &d->ram); - return 0; + sysbus_init_mmio(sbd, &d->ram); } static void ram_init(hwaddr addr, ram_addr_t RAM_size) @@ -409,9 +408,8 @@ static Property ram_properties[] = { static void ram_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = ram_init1; + dc->realize = ram_realize; dc->props = ram_properties; } diff --git a/hw/timer/m48t59.c b/hw/timer/m48t59.c index 4a064fb..844aad5 100644 --- a/hw/timer/m48t59.c +++ b/hw/timer/m48t59.c @@ -640,34 +640,33 @@ void m48t59_realize_common(M48t59State *s, Error **errp) s->wd_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &watchdog_cb, s); } qemu_get_timedate(&s->alarm, 0); - - vmstate_register(NULL, -1, &vmstate_m48t59, s); } -static int m48t59_init1(SysBusDevice *dev) +static void m48t59_init1(Object *obj) { - M48txxSysBusDeviceClass *u = M48TXX_SYS_BUS_GET_CLASS(dev); - M48txxSysBusState *d = M48TXX_SYS_BUS(dev); - Object *o = OBJECT(dev); + M48txxSysBusDeviceClass *u = M48TXX_SYS_BUS_GET_CLASS(obj); + M48txxSysBusState *d = M48TXX_SYS_BUS(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); M48t59State *s = &d->state; - Error *err = NULL; s->model = u->info.model; s->size = u->info.size; sysbus_init_irq(dev, &s->IRQ); - memory_region_init_io(&s->iomem, o, &nvram_ops, s, "m48t59.nvram", + memory_region_init_io(&s->iomem, obj, &nvram_ops, s, "m48t59.nvram", s->size); - memory_region_init_io(&d->io, o, &m48t59_io_ops, s, "m48t59", 4); - sysbus_init_mmio(dev, &s->iomem); - sysbus_init_mmio(dev, &d->io); - m48t59_realize_common(s, &err); - if (err != NULL) { - error_free(err); - return -1; - } + memory_region_init_io(&d->io, obj, &m48t59_io_ops, s, "m48t59", 4); +} + +static void m48t59_realize(DeviceState *dev, Error **errp) +{ + M48txxSysBusState *d = M48TXX_SYS_BUS(dev); + M48t59State *s = &d->state; + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - return 0; + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_mmio(sbd, &d->io); + m48t59_realize_common(s, errp); } static uint32_t m48txx_sysbus_read(Nvram *obj, uint32_t addr) @@ -696,12 +695,12 @@ static Property m48t59_sysbus_properties[] = { static void m48txx_sysbus_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); NvramClass *nc = NVRAM_CLASS(klass); - k->init = m48t59_init1; + dc->realize = m48t59_realize; dc->reset = m48t59_reset_sysbus; dc->props = m48t59_sysbus_properties; + dc->vmsd = &vmstate_m48t59; nc->read = m48txx_sysbus_read; nc->write = m48txx_sysbus_write; nc->toggle_lock = m48txx_sysbus_toggle_lock; @@ -725,6 +724,7 @@ static const TypeInfo m48txx_sysbus_type_info = { .name = TYPE_M48TXX_SYS_BUS, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(M48txxSysBusState), + .instance_init = m48t59_init1, .abstract = true, .class_init = m48txx_sysbus_class_init, .interfaces = (InterfaceInfo[]) { diff --git a/hw/timer/slavio_timer.c b/hw/timer/slavio_timer.c index bfee1f3..a8cc9c0 100644 --- a/hw/timer/slavio_timer.c +++ b/hw/timer/slavio_timer.c @@ -373,9 +373,10 @@ static void slavio_timer_reset(DeviceState *d) s->cputimer_mode = 0; } -static int slavio_timer_init1(SysBusDevice *dev) +static void slavio_timer_init(Object *obj) { - SLAVIO_TIMERState *s = SLAVIO_TIMER(dev); + SLAVIO_TIMERState *s = SLAVIO_TIMER(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); QEMUBH *bh; unsigned int i; TimerContext *tc; @@ -394,14 +395,12 @@ static int slavio_timer_init1(SysBusDevice *dev) size = i == 0 ? SYS_TIMER_SIZE : CPU_TIMER_SIZE; snprintf(timer_name, sizeof(timer_name), "timer-%i", i); - memory_region_init_io(&tc->iomem, OBJECT(s), &slavio_timer_mem_ops, tc, + memory_region_init_io(&tc->iomem, obj, &slavio_timer_mem_ops, tc, timer_name, size); sysbus_init_mmio(dev, &tc->iomem); sysbus_init_irq(dev, &s->cputimer[i].irq); } - - return 0; } static Property slavio_timer_properties[] = { @@ -412,9 +411,7 @@ static Property slavio_timer_properties[] = { static void slavio_timer_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = slavio_timer_init1; dc->reset = slavio_timer_reset; dc->vmsd = &vmstate_slavio_timer; dc->props = slavio_timer_properties; @@ -424,6 +421,7 @@ static const TypeInfo slavio_timer_info = { .name = TYPE_SLAVIO_TIMER, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SLAVIO_TIMERState), + .instance_init = slavio_timer_init, .class_init = slavio_timer_class_init, }; diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 1f7a7c1..e24d8fa 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -1,6 +1,7 @@ # See docs/tracing.txt for syntax documentation. # hw/virtio/virtio.c +virtqueue_alloc_element(void *elem, size_t sz, unsigned in_num, unsigned out_num) "elem %p size %zd in_num %u out_num %u" virtqueue_fill(void *vq, const void *elem, unsigned int len, unsigned int idx) "vq %p elem %p len %u idx %u" virtqueue_flush(void *vq, unsigned int count) "vq %p count %u" virtqueue_pop(void *vq, void *elem, unsigned int in_num, unsigned int out_num) "vq %p elem %p in_num %u out_num %u" diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index be927b8..4e31de1 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -192,7 +192,6 @@ static void vhost_kernel_iotlb_read(void *opaque) ssize_t len; while ((len = read((uintptr_t)dev->opaque, &msg, sizeof msg)) > 0) { - struct vhost_iotlb_msg *imsg = &msg.iotlb; if (len < sizeof msg) { error_report("Wrong vhost message len: %d", (int)len); break; @@ -201,70 +200,21 @@ static void vhost_kernel_iotlb_read(void *opaque) error_report("Unknown vhost iotlb message type"); break; } - switch (imsg->type) { - case VHOST_IOTLB_MISS: - vhost_device_iotlb_miss(dev, imsg->iova, - imsg->perm != VHOST_ACCESS_RO); - break; - case VHOST_IOTLB_UPDATE: - case VHOST_IOTLB_INVALIDATE: - error_report("Unexpected IOTLB message type"); - break; - case VHOST_IOTLB_ACCESS_FAIL: - /* FIXME: report device iotlb error */ - break; - default: - break; - } - } -} -static int vhost_kernel_update_device_iotlb(struct vhost_dev *dev, - uint64_t iova, uint64_t uaddr, - uint64_t len, - IOMMUAccessFlags perm) -{ - struct vhost_msg msg; - msg.type = VHOST_IOTLB_MSG; - msg.iotlb.iova = iova; - msg.iotlb.uaddr = uaddr; - msg.iotlb.size = len; - msg.iotlb.type = VHOST_IOTLB_UPDATE; - - switch (perm) { - case IOMMU_RO: - msg.iotlb.perm = VHOST_ACCESS_RO; - break; - case IOMMU_WO: - msg.iotlb.perm = VHOST_ACCESS_WO; - break; - case IOMMU_RW: - msg.iotlb.perm = VHOST_ACCESS_RW; - break; - default: - g_assert_not_reached(); - } - - if (write((uintptr_t)dev->opaque, &msg, sizeof msg) != sizeof msg) { - error_report("Fail to update device iotlb"); - return -EFAULT; + vhost_backend_handle_iotlb_msg(dev, &msg.iotlb); } - - return 0; } -static int vhost_kernel_invalidate_device_iotlb(struct vhost_dev *dev, - uint64_t iova, uint64_t len) +static int vhost_kernel_send_device_iotlb_msg(struct vhost_dev *dev, + struct vhost_iotlb_msg *imsg) { struct vhost_msg msg; msg.type = VHOST_IOTLB_MSG; - msg.iotlb.iova = iova; - msg.iotlb.size = len; - msg.iotlb.type = VHOST_IOTLB_INVALIDATE; + msg.iotlb = *imsg; if (write((uintptr_t)dev->opaque, &msg, sizeof msg) != sizeof msg) { - error_report("Fail to invalidate device iotlb"); + error_report("Fail to update device iotlb"); return -EFAULT; } @@ -311,8 +261,7 @@ static const VhostOps kernel_ops = { .vhost_vsock_set_running = vhost_kernel_vsock_set_running, #endif /* CONFIG_VHOST_VSOCK */ .vhost_set_iotlb_callback = vhost_kernel_set_iotlb_callback, - .vhost_update_device_iotlb = vhost_kernel_update_device_iotlb, - .vhost_invalidate_device_iotlb = vhost_kernel_invalidate_device_iotlb, + .vhost_send_device_iotlb_msg = vhost_kernel_send_device_iotlb_msg, }; int vhost_set_backend_type(struct vhost_dev *dev, VhostBackendType backend_type) @@ -333,3 +282,70 @@ int vhost_set_backend_type(struct vhost_dev *dev, VhostBackendType backend_type) return r; } + +int vhost_backend_update_device_iotlb(struct vhost_dev *dev, + uint64_t iova, uint64_t uaddr, + uint64_t len, + IOMMUAccessFlags perm) +{ + struct vhost_iotlb_msg imsg; + + imsg.iova = iova; + imsg.uaddr = uaddr; + imsg.size = len; + imsg.type = VHOST_IOTLB_UPDATE; + + switch (perm) { + case IOMMU_RO: + imsg.perm = VHOST_ACCESS_RO; + break; + case IOMMU_WO: + imsg.perm = VHOST_ACCESS_WO; + break; + case IOMMU_RW: + imsg.perm = VHOST_ACCESS_RW; + break; + default: + return -EINVAL; + } + + return dev->vhost_ops->vhost_send_device_iotlb_msg(dev, &imsg); +} + +int vhost_backend_invalidate_device_iotlb(struct vhost_dev *dev, + uint64_t iova, uint64_t len) +{ + struct vhost_iotlb_msg imsg; + + imsg.iova = iova; + imsg.size = len; + imsg.type = VHOST_IOTLB_INVALIDATE; + + return dev->vhost_ops->vhost_send_device_iotlb_msg(dev, &imsg); +} + +int vhost_backend_handle_iotlb_msg(struct vhost_dev *dev, + struct vhost_iotlb_msg *imsg) +{ + int ret = 0; + + switch (imsg->type) { + case VHOST_IOTLB_MISS: + ret = vhost_device_iotlb_miss(dev, imsg->iova, + imsg->perm != VHOST_ACCESS_RO); + break; + case VHOST_IOTLB_ACCESS_FAIL: + /* FIXME: report device iotlb error */ + error_report("Access failure IOTLB message type not supported"); + ret = -ENOTSUP; + break; + case VHOST_IOTLB_UPDATE: + case VHOST_IOTLB_INVALIDATE: + default: + error_report("Unexpected IOTLB message type"); + ret = -EINVAL; + break; + } + + return ret; +} diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 0aa446e..958ee09 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -32,6 +32,7 @@ enum VhostUserProtocolFeature { VHOST_USER_PROTOCOL_F_RARP = 2, VHOST_USER_PROTOCOL_F_REPLY_ACK = 3, VHOST_USER_PROTOCOL_F_NET_MTU = 4, + VHOST_USER_PROTOCOL_F_SLAVE_REQ = 5, VHOST_USER_PROTOCOL_F_MAX }; @@ -60,9 +61,17 @@ typedef enum VhostUserRequest { VHOST_USER_SET_VRING_ENABLE = 18, VHOST_USER_SEND_RARP = 19, VHOST_USER_NET_SET_MTU = 20, + VHOST_USER_SET_SLAVE_REQ_FD = 21, + VHOST_USER_IOTLB_MSG = 22, VHOST_USER_MAX } VhostUserRequest; +typedef enum VhostUserSlaveRequest { + VHOST_USER_SLAVE_NONE = 0, + VHOST_USER_SLAVE_IOTLB_MSG = 1, + VHOST_USER_SLAVE_MAX +} VhostUserSlaveRequest; + typedef struct VhostUserMemoryRegion { uint64_t guest_phys_addr; uint64_t memory_size; @@ -97,6 +106,7 @@ typedef struct VhostUserMsg { struct vhost_vring_addr addr; VhostUserMemory memory; VhostUserLog log; + struct vhost_iotlb_msg iotlb; } payload; } QEMU_PACKED VhostUserMsg; @@ -110,6 +120,11 @@ static VhostUserMsg m __attribute__ ((unused)); /* The version of the protocol we support */ #define VHOST_USER_VERSION (0x1) +struct vhost_user { + CharBackend *chr; + int slave_fd; +}; + static bool ioeventfd_enabled(void) { return kvm_enabled() && kvm_eventfds_enabled(); @@ -117,7 +132,8 @@ static bool ioeventfd_enabled(void) static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) { - CharBackend *chr = dev->opaque; + struct vhost_user *u = dev->opaque; + CharBackend *chr = u->chr; uint8_t *p = (uint8_t *) msg; int r, size = VHOST_USER_HDR_SIZE; @@ -202,7 +218,8 @@ static bool vhost_user_one_time_request(VhostUserRequest request) static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg, int *fds, int fd_num) { - CharBackend *chr = dev->opaque; + struct vhost_user *u = dev->opaque; + CharBackend *chr = u->chr; int ret, size = VHOST_USER_HDR_SIZE + msg->size; /* @@ -572,14 +589,130 @@ static int vhost_user_reset_device(struct vhost_dev *dev) return 0; } +static void slave_read(void *opaque) +{ + struct vhost_dev *dev = opaque; + struct vhost_user *u = dev->opaque; + VhostUserMsg msg = { 0, }; + int size, ret = 0; + + /* Read header */ + size = read(u->slave_fd, &msg, VHOST_USER_HDR_SIZE); + if (size != VHOST_USER_HDR_SIZE) { + error_report("Failed to read from slave."); + goto err; + } + + if (msg.size > VHOST_USER_PAYLOAD_SIZE) { + error_report("Failed to read msg header." + " Size %d exceeds the maximum %zu.", msg.size, + VHOST_USER_PAYLOAD_SIZE); + goto err; + } + + /* Read payload */ + size = read(u->slave_fd, &msg.payload, msg.size); + if (size != msg.size) { + error_report("Failed to read payload from slave."); + goto err; + } + + switch (msg.request) { + case VHOST_USER_SLAVE_IOTLB_MSG: + ret = vhost_backend_handle_iotlb_msg(dev, &msg.payload.iotlb); + break; + default: + error_report("Received unexpected msg type."); + ret = -EINVAL; + } + + /* + * REPLY_ACK feature handling. Other reply types has to be managed + * directly in their request handlers. + */ + if (msg.flags & VHOST_USER_NEED_REPLY_MASK) { + msg.flags &= ~VHOST_USER_NEED_REPLY_MASK; + msg.flags |= VHOST_USER_REPLY_MASK; + + msg.payload.u64 = !!ret; + msg.size = sizeof(msg.payload.u64); + + size = write(u->slave_fd, &msg, VHOST_USER_HDR_SIZE + msg.size); + if (size != VHOST_USER_HDR_SIZE + msg.size) { + error_report("Failed to send msg reply to slave."); + goto err; + } + } + + return; + +err: + qemu_set_fd_handler(u->slave_fd, NULL, NULL, NULL); + close(u->slave_fd); + u->slave_fd = -1; + return; +} + +static int vhost_setup_slave_channel(struct vhost_dev *dev) +{ + VhostUserMsg msg = { + .request = VHOST_USER_SET_SLAVE_REQ_FD, + .flags = VHOST_USER_VERSION, + }; + struct vhost_user *u = dev->opaque; + int sv[2], ret = 0; + bool reply_supported = virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_REPLY_ACK); + + if (!virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_SLAVE_REQ)) { + return 0; + } + + if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { + error_report("socketpair() failed"); + return -1; + } + + u->slave_fd = sv[0]; + qemu_set_fd_handler(u->slave_fd, slave_read, NULL, dev); + + if (reply_supported) { + msg.flags |= VHOST_USER_NEED_REPLY_MASK; + } + + ret = vhost_user_write(dev, &msg, &sv[1], 1); + if (ret) { + goto out; + } + + if (reply_supported) { + ret = process_message_reply(dev, &msg); + } + +out: + close(sv[1]); + if (ret) { + qemu_set_fd_handler(u->slave_fd, NULL, NULL, NULL); + close(u->slave_fd); + u->slave_fd = -1; + } + + return ret; +} + static int vhost_user_init(struct vhost_dev *dev, void *opaque) { - uint64_t features; + uint64_t features, protocol_features; + struct vhost_user *u; int err; assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); - dev->opaque = opaque; + u = g_new0(struct vhost_user, 1); + u->chr = opaque; + u->slave_fd = -1; + dev->opaque = u; err = vhost_user_get_features(dev, &features); if (err < 0) { @@ -590,12 +723,13 @@ static int vhost_user_init(struct vhost_dev *dev, void *opaque) dev->backend_features |= 1ULL << VHOST_USER_F_PROTOCOL_FEATURES; err = vhost_user_get_u64(dev, VHOST_USER_GET_PROTOCOL_FEATURES, - &features); + &protocol_features); if (err < 0) { return err; } - dev->protocol_features = features & VHOST_USER_PROTOCOL_FEATURE_MASK; + dev->protocol_features = + protocol_features & VHOST_USER_PROTOCOL_FEATURE_MASK; err = vhost_user_set_protocol_features(dev, dev->protocol_features); if (err < 0) { return err; @@ -609,6 +743,16 @@ static int vhost_user_init(struct vhost_dev *dev, void *opaque) return err; } } + + if (virtio_has_feature(features, VIRTIO_F_IOMMU_PLATFORM) && + !(virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_SLAVE_REQ) && + virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_REPLY_ACK))) { + error_report("IOMMU support requires reply-ack and " + "slave-req protocol features."); + return -1; + } } if (dev->migration_blocker == NULL && @@ -619,13 +763,26 @@ static int vhost_user_init(struct vhost_dev *dev, void *opaque) "VHOST_USER_PROTOCOL_F_LOG_SHMFD feature."); } + err = vhost_setup_slave_channel(dev); + if (err < 0) { + return err; + } + return 0; } static int vhost_user_cleanup(struct vhost_dev *dev) { + struct vhost_user *u; + assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); + u = dev->opaque; + if (u->slave_fd >= 0) { + close(u->slave_fd); + u->slave_fd = -1; + } + g_free(u); dev->opaque = 0; return 0; @@ -722,6 +879,29 @@ static int vhost_user_net_set_mtu(struct vhost_dev *dev, uint16_t mtu) return 0; } +static int vhost_user_send_device_iotlb_msg(struct vhost_dev *dev, + struct vhost_iotlb_msg *imsg) +{ + VhostUserMsg msg = { + .request = VHOST_USER_IOTLB_MSG, + .size = sizeof(msg.payload.iotlb), + .flags = VHOST_USER_VERSION | VHOST_USER_NEED_REPLY_MASK, + .payload.iotlb = *imsg, + }; + + if (vhost_user_write(dev, &msg, NULL, 0) < 0) { + return -EFAULT; + } + + return process_message_reply(dev, &msg); +} + + +static void vhost_user_set_iotlb_callback(struct vhost_dev *dev, int enabled) +{ + /* No-op as the receive channel is not dedicated to IOTLB messages. */ +} + const VhostOps user_ops = { .backend_type = VHOST_BACKEND_TYPE_USER, .vhost_backend_init = vhost_user_init, @@ -746,4 +926,6 @@ const VhostOps user_ops = { .vhost_migration_done = vhost_user_migration_done, .vhost_backend_can_merge = vhost_user_can_merge, .vhost_net_set_mtu = vhost_user_net_set_mtu, + .vhost_set_iotlb_callback = vhost_user_set_iotlb_callback, + .vhost_send_device_iotlb_msg = vhost_user_send_device_iotlb_msg, }; diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 03a46a7..6eddb09 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -724,8 +724,8 @@ static void vhost_iommu_unmap_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) struct vhost_dev *hdev = iommu->hdev; hwaddr iova = iotlb->iova + iommu->iommu_offset; - if (hdev->vhost_ops->vhost_invalidate_device_iotlb(hdev, iova, - iotlb->addr_mask + 1)) { + if (vhost_backend_invalidate_device_iotlb(hdev, iova, + iotlb->addr_mask + 1)) { error_report("Fail to invalidate device iotlb"); } } @@ -971,18 +971,20 @@ static int vhost_memory_region_lookup(struct vhost_dev *hdev, return -EFAULT; } -void vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write) +int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write) { IOMMUTLBEntry iotlb; uint64_t uaddr, len; + int ret = -EFAULT; rcu_read_lock(); iotlb = address_space_get_iotlb_entry(dev->vdev->dma_as, iova, write); if (iotlb.target_as != NULL) { - if (vhost_memory_region_lookup(dev, iotlb.translated_addr, - &uaddr, &len)) { + ret = vhost_memory_region_lookup(dev, iotlb.translated_addr, + &uaddr, &len); + if (ret) { error_report("Fail to lookup the translated address " "%"PRIx64, iotlb.translated_addr); goto out; @@ -991,14 +993,17 @@ void vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write) len = MIN(iotlb.addr_mask + 1, len); iova = iova & ~iotlb.addr_mask; - if (dev->vhost_ops->vhost_update_device_iotlb(dev, iova, uaddr, - len, iotlb.perm)) { + ret = vhost_backend_update_device_iotlb(dev, iova, uaddr, + len, iotlb.perm); + if (ret) { error_report("Fail to update device iotlb"); goto out; } } out: rcu_read_unlock(); + + return ret; } static int vhost_virtqueue_start(struct vhost_dev *dev, diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index f99d99f..464947f 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -815,6 +815,7 @@ static void *virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_nu assert(sz >= sizeof(VirtQueueElement)); elem = g_malloc(out_sg_end); + trace_virtqueue_alloc_element(elem, sz, in_num, out_num); elem->out_num = out_num; elem->in_num = in_num; elem->in_addr = (void *)elem + in_addr_ofs; diff --git a/include/block/block_int.h b/include/block/block_int.h index e5eb473..cb78c4f 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -601,8 +601,8 @@ struct BlockDriverState { int copy_on_read; /* If we are reading a disk image, give its size in sectors. - * Generally read-only; it is written to by load_vmstate and save_vmstate, - * but the block layer is quiescent during those. + * Generally read-only; it is written to by load_snapshot and + * save_snaphost, but the block layer is quiescent during those. */ int64_t total_sectors; diff --git a/include/hw/acpi/memory_hotplug.h b/include/hw/acpi/memory_hotplug.h index db8ebc9..77c6576 100644 --- a/include/hw/acpi/memory_hotplug.h +++ b/include/hw/acpi/memory_hotplug.h @@ -3,7 +3,6 @@ #include "hw/qdev-core.h" #include "hw/acpi/acpi.h" -#include "migration/vmstate.h" #include "hw/acpi/aml-build.h" /** diff --git a/include/hw/acpi/pcihp.h b/include/hw/acpi/pcihp.h index 04528b7..8a65f99 100644 --- a/include/hw/acpi/pcihp.h +++ b/include/hw/acpi/pcihp.h @@ -28,7 +28,6 @@ #define HW_ACPI_PCIHP_H #include "hw/acpi/acpi.h" -#include "migration/vmstate.h" #include "hw/hotplug.h" #define ACPI_PCIHP_IO_BASE_PROP "acpi-pcihp-io-base" diff --git a/include/hw/hw.h b/include/hw/hw.h index af9eae1..ab4950c 100644 --- a/include/hw/hw.h +++ b/include/hw/hw.h @@ -11,7 +11,7 @@ #include "exec/memory.h" #include "hw/irq.h" #include "migration/vmstate.h" -#include "migration/qemu-file.h" +#include "migration/qemu-file-types.h" #include "qemu/module.h" #include "sysemu/reset.h" diff --git a/include/hw/pci/shpc.h b/include/hw/pci/shpc.h index b208554..71e836b 100644 --- a/include/hw/pci/shpc.h +++ b/include/hw/pci/shpc.h @@ -3,7 +3,6 @@ #include "qemu-common.h" #include "exec/memory.h" -#include "migration/vmstate.h" #include "hw/hotplug.h" #include "hw/pci/pci.h" diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h index c3cf4a7..a7a5f22 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -27,6 +27,7 @@ struct vhost_vring_file; struct vhost_vring_state; struct vhost_vring_addr; struct vhost_scsi_target; +struct vhost_iotlb_msg; typedef int (*vhost_backend_init)(struct vhost_dev *dev, void *opaque); typedef int (*vhost_backend_cleanup)(struct vhost_dev *dev); @@ -81,12 +82,8 @@ typedef int (*vhost_vsock_set_guest_cid_op)(struct vhost_dev *dev, typedef int (*vhost_vsock_set_running_op)(struct vhost_dev *dev, int start); typedef void (*vhost_set_iotlb_callback_op)(struct vhost_dev *dev, int enabled); -typedef int (*vhost_update_device_iotlb_op)(struct vhost_dev *dev, - uint64_t iova, uint64_t uaddr, - uint64_t len, - IOMMUAccessFlags perm); -typedef int (*vhost_invalidate_device_iotlb_op)(struct vhost_dev *dev, - uint64_t iova, uint64_t len); +typedef int (*vhost_send_device_iotlb_msg_op)(struct vhost_dev *dev, + struct vhost_iotlb_msg *imsg); typedef struct VhostOps { VhostBackendType backend_type; @@ -120,8 +117,7 @@ typedef struct VhostOps { vhost_vsock_set_guest_cid_op vhost_vsock_set_guest_cid; vhost_vsock_set_running_op vhost_vsock_set_running; vhost_set_iotlb_callback_op vhost_set_iotlb_callback; - vhost_update_device_iotlb_op vhost_update_device_iotlb; - vhost_invalidate_device_iotlb_op vhost_invalidate_device_iotlb; + vhost_send_device_iotlb_msg_op vhost_send_device_iotlb_msg; } VhostOps; extern const VhostOps user_ops; @@ -129,4 +125,15 @@ extern const VhostOps user_ops; int vhost_set_backend_type(struct vhost_dev *dev, VhostBackendType backend_type); +int vhost_backend_update_device_iotlb(struct vhost_dev *dev, + uint64_t iova, uint64_t uaddr, + uint64_t len, + IOMMUAccessFlags perm); + +int vhost_backend_invalidate_device_iotlb(struct vhost_dev *dev, + uint64_t iova, uint64_t len); + +int vhost_backend_handle_iotlb_msg(struct vhost_dev *dev, + struct vhost_iotlb_msg *imsg); + #endif /* VHOST_BACKEND_H */ diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index a450321..467dc77 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -105,5 +105,5 @@ bool vhost_has_free_slot(void); int vhost_net_set_backend(struct vhost_dev *hdev, struct vhost_vring_file *file); -void vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write); +int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write); #endif diff --git a/include/migration/migration.h b/include/migration/migration.h index 0e807b6..79b5484 100644 --- a/include/migration/migration.h +++ b/include/migration/migration.h @@ -18,7 +18,6 @@ #include "qemu-common.h" #include "qemu/thread.h" #include "qemu/notify.h" -#include "io/channel.h" #include "qapi-types.h" #include "exec/cpu-common.h" #include "qemu/coroutine_int.h" @@ -50,8 +49,6 @@ enum mig_rp_message_type { MIG_RP_MSG_MAX }; -typedef QLIST_HEAD(, LoadStateEntry) LoadStateEntry_Head; - /* State for the incoming migration */ struct MigrationIncomingState { QEMUFile *from_src_file; @@ -89,9 +86,6 @@ struct MigrationIncomingState { /* The coroutine we should enter (back) after failover */ Coroutine *migration_incoming_co; QemuSemaphore colo_incoming_sem; - - /* See savevm.c */ - LoadStateEntry_Head loadvm_handlers; }; MigrationIncomingState *migration_incoming_get_current(void); @@ -157,37 +151,8 @@ void migration_fd_process_incoming(QEMUFile *f); void qemu_start_incoming_migration(const char *uri, Error **errp); -void migration_tls_channel_process_incoming(MigrationState *s, - QIOChannel *ioc, - Error **errp); - -void migration_tls_channel_connect(MigrationState *s, - QIOChannel *ioc, - const char *hostname, - Error **errp); - uint64_t migrate_max_downtime(void); -void exec_start_incoming_migration(const char *host_port, Error **errp); - -void exec_start_outgoing_migration(MigrationState *s, const char *host_port, Error **errp); - -void tcp_start_incoming_migration(const char *host_port, Error **errp); - -void tcp_start_outgoing_migration(MigrationState *s, const char *host_port, Error **errp); - -void unix_start_incoming_migration(const char *path, Error **errp); - -void unix_start_outgoing_migration(MigrationState *s, const char *path, Error **errp); - -void fd_start_incoming_migration(const char *path, Error **errp); - -void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error **errp); - -void rdma_start_outgoing_migration(void *opaque, const char *host_port, Error **errp); - -void rdma_start_incoming_migration(const char *host_port, Error **errp); - void migrate_fd_error(MigrationState *s, const Error *error); void migrate_fd_connect(MigrationState *s); @@ -206,38 +171,6 @@ bool migration_in_postcopy(void); bool migration_in_postcopy_after_devices(MigrationState *); MigrationState *migrate_get_current(void); -void migrate_compress_threads_create(void); -void migrate_compress_threads_join(void); -void migrate_decompress_threads_create(void); -void migrate_decompress_threads_join(void); -uint64_t ram_bytes_remaining(void); -uint64_t ram_bytes_transferred(void); -uint64_t ram_bytes_total(void); -uint64_t ram_dirty_sync_count(void); -uint64_t ram_dirty_pages_rate(void); -uint64_t ram_postcopy_requests(void); -void free_xbzrle_decoded_buf(void); - -void acct_update_position(QEMUFile *f, size_t size, bool zero); - -uint64_t dup_mig_pages_transferred(void); -uint64_t norm_mig_pages_transferred(void); -uint64_t xbzrle_mig_bytes_transferred(void); -uint64_t xbzrle_mig_pages_transferred(void); -uint64_t xbzrle_mig_pages_overflow(void); -uint64_t xbzrle_mig_pages_cache_miss(void); -double xbzrle_mig_cache_miss_rate(void); - -void ram_handle_compressed(void *host, uint8_t ch, uint64_t size); -void ram_debug_dump_bitmap(unsigned long *todump, bool expected, - unsigned long pages); -/* For outgoing discard bitmap */ -int ram_postcopy_send_discard_bitmap(MigrationState *ms); -/* For incoming postcopy discard */ -int ram_discard_range(const char *block_name, uint64_t start, size_t length); -int ram_postcopy_incoming_init(MigrationIncomingState *mis); -void ram_postcopy_migrated_memory_release(MigrationState *ms); - bool migrate_release_ram(void); bool migrate_postcopy_ram(void); bool migrate_zero_blocks(void); @@ -248,8 +181,6 @@ int migrate_use_xbzrle(void); int64_t migrate_xbzrle_cache_size(void); bool migrate_colo_enabled(void); -int64_t xbzrle_cache_resize(int64_t new_size); - bool migrate_use_block(void); bool migrate_use_block_incremental(void); @@ -288,7 +219,6 @@ size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset, ram_addr_t offset, size_t size, uint64_t *bytes_sent); -void ram_mig_init(void); void savevm_skip_section_footers(void); void register_global_state(void); void global_state_set_optional(void); @@ -296,7 +226,4 @@ void savevm_skip_configuration(void); int global_state_store(void); void global_state_store_running(void); -void migration_page_queue_free(void); -int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len); -uint64_t ram_pagesize_summary(void); #endif diff --git a/include/migration/misc.h b/include/migration/misc.h new file mode 100644 index 0000000..d7892b7 --- /dev/null +++ b/include/migration/misc.h @@ -0,0 +1,29 @@ +/* + * QEMU migration miscellaneus exported functions + * + * Copyright IBM, Corp. 2008 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef MIGRATION_MISC_H +#define MIGRATION_MISC_H + +/* migration/ram.c */ + +void ram_mig_init(void); + +/* migration/block.c */ + +#ifdef CONFIG_LIVE_BLOCK_MIGRATION +void blk_mig_init(void); +#else +static inline void blk_mig_init(void) {} +#endif + +#endif diff --git a/include/migration/qemu-file-types.h b/include/migration/qemu-file-types.h new file mode 100644 index 0000000..bd6d7dd --- /dev/null +++ b/include/migration/qemu-file-types.h @@ -0,0 +1,164 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QEMU_FILE_H +#define QEMU_FILE_H + +void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, size_t size); +void qemu_put_byte(QEMUFile *f, int v); + +#define qemu_put_sbyte qemu_put_byte + +void qemu_put_be16(QEMUFile *f, unsigned int v); +void qemu_put_be32(QEMUFile *f, unsigned int v); +void qemu_put_be64(QEMUFile *f, uint64_t v); +size_t qemu_get_buffer(QEMUFile *f, uint8_t *buf, size_t size); + +int qemu_get_byte(QEMUFile *f); + +static inline unsigned int qemu_get_ubyte(QEMUFile *f) +{ + return (unsigned int)qemu_get_byte(f); +} + +#define qemu_get_sbyte qemu_get_byte + +unsigned int qemu_get_be16(QEMUFile *f); +unsigned int qemu_get_be32(QEMUFile *f); +uint64_t qemu_get_be64(QEMUFile *f); + +static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv) +{ + qemu_put_be64(f, *pv); +} + +static inline void qemu_put_be32s(QEMUFile *f, const uint32_t *pv) +{ + qemu_put_be32(f, *pv); +} + +static inline void qemu_put_be16s(QEMUFile *f, const uint16_t *pv) +{ + qemu_put_be16(f, *pv); +} + +static inline void qemu_put_8s(QEMUFile *f, const uint8_t *pv) +{ + qemu_put_byte(f, *pv); +} + +static inline void qemu_get_be64s(QEMUFile *f, uint64_t *pv) +{ + *pv = qemu_get_be64(f); +} + +static inline void qemu_get_be32s(QEMUFile *f, uint32_t *pv) +{ + *pv = qemu_get_be32(f); +} + +static inline void qemu_get_be16s(QEMUFile *f, uint16_t *pv) +{ + *pv = qemu_get_be16(f); +} + +static inline void qemu_get_8s(QEMUFile *f, uint8_t *pv) +{ + *pv = qemu_get_byte(f); +} + +/* Signed versions for type safety */ +static inline void qemu_put_sbe16(QEMUFile *f, int v) +{ + qemu_put_be16(f, (unsigned int)v); +} + +static inline void qemu_put_sbe32(QEMUFile *f, int v) +{ + qemu_put_be32(f, (unsigned int)v); +} + +static inline void qemu_put_sbe64(QEMUFile *f, int64_t v) +{ + qemu_put_be64(f, (uint64_t)v); +} + +static inline int qemu_get_sbe16(QEMUFile *f) +{ + return (int)qemu_get_be16(f); +} + +static inline int qemu_get_sbe32(QEMUFile *f) +{ + return (int)qemu_get_be32(f); +} + +static inline int64_t qemu_get_sbe64(QEMUFile *f) +{ + return (int64_t)qemu_get_be64(f); +} + +static inline void qemu_put_s8s(QEMUFile *f, const int8_t *pv) +{ + qemu_put_8s(f, (const uint8_t *)pv); +} + +static inline void qemu_put_sbe16s(QEMUFile *f, const int16_t *pv) +{ + qemu_put_be16s(f, (const uint16_t *)pv); +} + +static inline void qemu_put_sbe32s(QEMUFile *f, const int32_t *pv) +{ + qemu_put_be32s(f, (const uint32_t *)pv); +} + +static inline void qemu_put_sbe64s(QEMUFile *f, const int64_t *pv) +{ + qemu_put_be64s(f, (const uint64_t *)pv); +} + +static inline void qemu_get_s8s(QEMUFile *f, int8_t *pv) +{ + qemu_get_8s(f, (uint8_t *)pv); +} + +static inline void qemu_get_sbe16s(QEMUFile *f, int16_t *pv) +{ + qemu_get_be16s(f, (uint16_t *)pv); +} + +static inline void qemu_get_sbe32s(QEMUFile *f, int32_t *pv) +{ + qemu_get_be32s(f, (uint32_t *)pv); +} + +static inline void qemu_get_sbe64s(QEMUFile *f, int64_t *pv) +{ + qemu_get_be64s(f, (uint64_t *)pv); +} + +int qemu_file_rate_limit(QEMUFile *f); + +#endif diff --git a/include/migration/snapshot.h b/include/migration/snapshot.h new file mode 100644 index 0000000..c85b6ec --- /dev/null +++ b/include/migration/snapshot.h @@ -0,0 +1,21 @@ +/* + * QEMU snapshots + * + * Copyright (c) 2004-2008 Fabrice Bellard + * Copyright (c) 2009-2015 Red Hat Inc + * + * Authors: + * Juan Quintela <quintela@redhat.com> + * + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef QEMU_MIGRATION_SNAPSHOT_H +#define QEMU_MIGRATION_SNAPSHOT_H + +int save_snapshot(const char *name, Error **errp); +int load_snapshot(const char *name, Error **errp); + +#endif diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index f97411d..6689562 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -1020,8 +1020,6 @@ extern const VMStateInfo vmstate_info_qtailq; #define SELF_ANNOUNCE_ROUNDS 5 -void loadvm_free_handlers(MigrationIncomingState *mis); - int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, int version_id); void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 33a6aa1..51958bf 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -39,7 +39,6 @@ typedef struct I2SCodec I2SCodec; typedef struct ISABus ISABus; typedef struct ISADevice ISADevice; typedef struct IsaDma IsaDma; -typedef struct LoadStateEntry LoadStateEntry; typedef struct MACAddr MACAddr; typedef struct MachineClass MachineClass; typedef struct MachineState MachineState; diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 723c8dc..9841a52 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -92,9 +92,6 @@ void qemu_remove_exit_notifier(Notifier *notify); void qemu_add_machine_init_done_notifier(Notifier *notify); void qemu_remove_machine_init_done_notifier(Notifier *notify); -int save_vmstate(const char *name, Error **errp); -int load_vmstate(const char *name, Error **errp); - void qemu_announce_self(void); extern int autostart; diff --git a/migration/block.c b/migration/block.c index 13f90d3..4d8c2e9 100644 --- a/migration/block.c +++ b/migration/block.c @@ -23,10 +23,11 @@ #include "qemu/cutils.h" #include "qemu/queue.h" #include "qemu/timer.h" -#include "migration/block.h" +#include "block.h" +#include "migration/misc.h" #include "migration/migration.h" #include "sysemu/blockdev.h" -#include "migration/qemu-file.h" +#include "qemu-file.h" #include "migration/vmstate.h" #include "sysemu/block-backend.h" diff --git a/include/migration/block.h b/migration/block.h index 28cff53..22ebe94 100644 --- a/include/migration/block.h +++ b/migration/block.h @@ -15,14 +15,12 @@ #define MIGRATION_BLOCK_H #ifdef CONFIG_LIVE_BLOCK_MIGRATION -void blk_mig_init(void); int blk_mig_active(void); uint64_t blk_mig_bytes_transferred(void); uint64_t blk_mig_bytes_remaining(void); uint64_t blk_mig_bytes_total(void); #else -static inline void blk_mig_init(void) { } static inline int blk_mig_active(void) { return false; diff --git a/migration/channel.c b/migration/channel.c index 2e78905cc..eae1d9e 100644 --- a/migration/channel.c +++ b/migration/channel.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "channel.h" +#include "tls.h" #include "migration/migration.h" #include "qemu-file-channel.h" #include "trace.h" diff --git a/migration/colo.c b/migration/colo.c index 3dd1390..111b715 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -15,10 +15,10 @@ #include "sysemu/sysemu.h" #include "qemu-file-channel.h" #include "migration/migration.h" -#include "migration/qemu-file.h" +#include "qemu-file.h" #include "savevm.h" #include "migration/colo.h" -#include "migration/block.h" +#include "block.h" #include "io/channel-buffer.h" #include "trace.h" #include "qemu/error-report.h" diff --git a/migration/exec.c b/migration/exec.c index 57a9335..9077024 100644 --- a/migration/exec.c +++ b/migration/exec.c @@ -21,6 +21,7 @@ #include "qapi/error.h" #include "qemu-common.h" #include "channel.h" +#include "exec.h" #include "migration/migration.h" #include "io/channel-command.h" #include "trace.h" diff --git a/migration/exec.h b/migration/exec.h new file mode 100644 index 0000000..b210ffd --- /dev/null +++ b/migration/exec.h @@ -0,0 +1,26 @@ +/* + * QEMU live migration + * + * Copyright IBM, Corp. 2008 + * Copyright Dell MessageOne 2008 + * Copyright Red Hat, Inc. 2015-2016 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * Charles Duffy <charles_duffy@messageone.com> + * Daniel P. Berrange <berrange@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#ifndef QEMU_MIGRATION_EXEC_H +#define QEMU_MIGRATION_EXEC_H +void exec_start_incoming_migration(const char *host_port, Error **errp); + +void exec_start_outgoing_migration(MigrationState *s, const char *host_port, + Error **errp); +#endif diff --git a/migration/fd.c b/migration/fd.c index 05e0a5c..0077a50 100644 --- a/migration/fd.c +++ b/migration/fd.c @@ -18,6 +18,7 @@ #include "qapi/error.h" #include "qemu-common.h" #include "channel.h" +#include "fd.h" #include "migration/migration.h" #include "monitor/monitor.h" #include "io/channel-util.h" diff --git a/migration/fd.h b/migration/fd.h new file mode 100644 index 0000000..a14a63c --- /dev/null +++ b/migration/fd.h @@ -0,0 +1,23 @@ +/* + * QEMU live migration via generic fd + * + * Copyright Red Hat, Inc. 2009-2016 + * + * Authors: + * Chris Lalancette <clalance@redhat.com> + * Daniel P. Berrange <berrange@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#ifndef QEMU_MIGRATION_FD_H +#define QEMU_MIGRATION_FD_H +void fd_start_incoming_migration(const char *path, Error **errp); + +void fd_start_outgoing_migration(MigrationState *s, const char *fdname, + Error **errp); +#endif diff --git a/migration/migration.c b/migration/migration.c index 7087d1a..48c94c9 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -18,10 +18,15 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "migration/blocker.h" +#include "exec.h" +#include "fd.h" +#include "socket.h" +#include "rdma.h" +#include "ram.h" #include "migration/migration.h" #include "savevm.h" #include "qemu-file-channel.h" -#include "migration/qemu-file.h" +#include "qemu-file.h" #include "migration/vmstate.h" #include "sysemu/sysemu.h" #include "block/block.h" @@ -29,7 +34,7 @@ #include "qapi/util.h" #include "qemu/sockets.h" #include "qemu/rcu.h" -#include "migration/block.h" +#include "block.h" #include "postcopy-ram.h" #include "qemu/thread.h" #include "qmp-commands.h" @@ -40,7 +45,6 @@ #include "exec/address-spaces.h" #include "exec/target_page.h" #include "io/channel-buffer.h" -#include "io/channel-tls.h" #include "migration/colo.h" #define MAX_THROTTLE (32 << 20) /* Migration transfer speed throttling */ @@ -122,7 +126,6 @@ MigrationIncomingState *migration_incoming_get_current(void) if (!once) { mis_current.state = MIGRATION_STATUS_NONE; memset(&mis_current, 0, sizeof(MigrationIncomingState)); - QLIST_INIT(&mis_current.loadvm_handlers); qemu_mutex_init(&mis_current.rp_mutex); qemu_event_init(&mis_current.main_thread_load_event, false); once = true; @@ -134,8 +137,19 @@ void migration_incoming_state_destroy(void) { struct MigrationIncomingState *mis = migration_incoming_get_current(); + if (mis->to_src_file) { + /* Tell source that we are done */ + migrate_send_rp_shut(mis, qemu_file_get_error(mis->from_src_file) != 0); + qemu_fclose(mis->to_src_file); + mis->to_src_file = NULL; + } + + if (mis->from_src_file) { + qemu_fclose(mis->from_src_file); + mis->from_src_file = NULL; + } + qemu_event_destroy(&mis->main_thread_load_event); - loadvm_free_handlers(mis); } @@ -432,7 +446,6 @@ static void process_incoming_migration_co(void *opaque) exit(EXIT_FAILURE); } - qemu_fclose(f); free_xbzrle_decoded_buf(); mis->bh = qemu_bh_new(process_incoming_migration_bh, mis); diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 3f9ae1b..9c41887 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -21,9 +21,10 @@ #include "qemu-common.h" #include "exec/target_page.h" #include "migration/migration.h" -#include "migration/qemu-file.h" +#include "qemu-file.h" #include "savevm.h" #include "postcopy-ram.h" +#include "ram.h" #include "sysemu/sysemu.h" #include "sysemu/balloon.h" #include "qemu/error-report.h" @@ -333,7 +334,6 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) } postcopy_state_set(POSTCOPY_INCOMING_END); - migrate_send_rp_shut(mis, qemu_file_get_error(mis->from_src_file) != 0); if (mis->postcopy_tmp_page) { munmap(mis->postcopy_tmp_page, mis->largest_page_size); diff --git a/migration/qemu-file-channel.c b/migration/qemu-file-channel.c index dc991c9..e202d73 100644 --- a/migration/qemu-file-channel.c +++ b/migration/qemu-file-channel.c @@ -24,7 +24,8 @@ #include "qemu/osdep.h" #include "qemu-file-channel.h" -#include "migration/qemu-file.h" +#include "exec/cpu-common.h" +#include "qemu-file.h" #include "io/channel-socket.h" #include "qemu/iov.h" diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 195fa94..ab26f4e 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -29,7 +29,7 @@ #include "qemu/sockets.h" #include "qemu/coroutine.h" #include "migration/migration.h" -#include "migration/qemu-file.h" +#include "qemu-file.h" #include "trace.h" #define IO_BUF_SIZE 32768 diff --git a/include/migration/qemu-file.h b/migration/qemu-file.h index b5ac800..49fd697 100644 --- a/include/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -22,11 +22,8 @@ * THE SOFTWARE. */ -#ifndef QEMU_FILE_H -#define QEMU_FILE_H - -#include "qemu-common.h" -#include "exec/cpu-common.h" +#ifndef MIGRATION_QEMU_FILE_H +#define MIGRATION_QEMU_FILE_H /* Read a chunk of data from a file at the given position. The pos argument * can be ignored if the file is only be used for streaming. The number of @@ -122,8 +119,6 @@ int qemu_get_fd(QEMUFile *f); int qemu_fclose(QEMUFile *f); int64_t qemu_ftell(QEMUFile *f); int64_t qemu_ftell_fast(QEMUFile *f); -void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, size_t size); -void qemu_put_byte(QEMUFile *f, int v); /* * put_buffer without copying the buffer. * The buffer should be available till it is sent asynchronously. @@ -133,19 +128,9 @@ void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, size_t size, bool qemu_file_mode_is_not_valid(const char *mode); bool qemu_file_is_writable(QEMUFile *f); +#include "migration/qemu-file-types.h" -static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v) -{ - qemu_put_byte(f, (int)v); -} - -#define qemu_put_sbyte qemu_put_byte - -void qemu_put_be16(QEMUFile *f, unsigned int v); -void qemu_put_be32(QEMUFile *f, unsigned int v); -void qemu_put_be64(QEMUFile *f, uint64_t v); size_t qemu_peek_buffer(QEMUFile *f, uint8_t **buf, size_t size, size_t offset); -size_t qemu_get_buffer(QEMUFile *f, uint8_t *buf, size_t size); size_t qemu_get_buffer_in_place(QEMUFile *f, uint8_t **buf, size_t size); ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size, int level); @@ -157,22 +142,8 @@ int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src); * previously peeked +n-1. */ int qemu_peek_byte(QEMUFile *f, int offset); -int qemu_get_byte(QEMUFile *f); void qemu_file_skip(QEMUFile *f, int size); void qemu_update_position(QEMUFile *f, size_t size); - -static inline unsigned int qemu_get_ubyte(QEMUFile *f) -{ - return (unsigned int)qemu_get_byte(f); -} - -#define qemu_get_sbyte qemu_get_byte - -unsigned int qemu_get_be16(QEMUFile *f); -unsigned int qemu_get_be32(QEMUFile *f); -uint64_t qemu_get_be64(QEMUFile *f); - -int qemu_file_rate_limit(QEMUFile *f); void qemu_file_reset_rate_limit(QEMUFile *f); void qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate); int64_t qemu_file_get_rate_limit(QEMUFile *f); @@ -183,127 +154,7 @@ QEMUFile *qemu_file_get_return_path(QEMUFile *f); void qemu_fflush(QEMUFile *f); void qemu_file_set_blocking(QEMUFile *f, bool block); -static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv) -{ - qemu_put_be64(f, *pv); -} - -static inline void qemu_put_be32s(QEMUFile *f, const uint32_t *pv) -{ - qemu_put_be32(f, *pv); -} - -static inline void qemu_put_be16s(QEMUFile *f, const uint16_t *pv) -{ - qemu_put_be16(f, *pv); -} - -static inline void qemu_put_8s(QEMUFile *f, const uint8_t *pv) -{ - qemu_put_byte(f, *pv); -} - -static inline void qemu_get_be64s(QEMUFile *f, uint64_t *pv) -{ - *pv = qemu_get_be64(f); -} - -static inline void qemu_get_be32s(QEMUFile *f, uint32_t *pv) -{ - *pv = qemu_get_be32(f); -} - -static inline void qemu_get_be16s(QEMUFile *f, uint16_t *pv) -{ - *pv = qemu_get_be16(f); -} - -static inline void qemu_get_8s(QEMUFile *f, uint8_t *pv) -{ - *pv = qemu_get_byte(f); -} - -// Signed versions for type safety -static inline void qemu_put_sbuffer(QEMUFile *f, const int8_t *buf, size_t size) -{ - qemu_put_buffer(f, (const uint8_t *)buf, size); -} - -static inline void qemu_put_sbe16(QEMUFile *f, int v) -{ - qemu_put_be16(f, (unsigned int)v); -} - -static inline void qemu_put_sbe32(QEMUFile *f, int v) -{ - qemu_put_be32(f, (unsigned int)v); -} - -static inline void qemu_put_sbe64(QEMUFile *f, int64_t v) -{ - qemu_put_be64(f, (uint64_t)v); -} - -static inline size_t qemu_get_sbuffer(QEMUFile *f, int8_t *buf, int size) -{ - return qemu_get_buffer(f, (uint8_t *)buf, size); -} - -static inline int qemu_get_sbe16(QEMUFile *f) -{ - return (int)qemu_get_be16(f); -} - -static inline int qemu_get_sbe32(QEMUFile *f) -{ - return (int)qemu_get_be32(f); -} - -static inline int64_t qemu_get_sbe64(QEMUFile *f) -{ - return (int64_t)qemu_get_be64(f); -} - -static inline void qemu_put_s8s(QEMUFile *f, const int8_t *pv) -{ - qemu_put_8s(f, (const uint8_t *)pv); -} - -static inline void qemu_put_sbe16s(QEMUFile *f, const int16_t *pv) -{ - qemu_put_be16s(f, (const uint16_t *)pv); -} - -static inline void qemu_put_sbe32s(QEMUFile *f, const int32_t *pv) -{ - qemu_put_be32s(f, (const uint32_t *)pv); -} - -static inline void qemu_put_sbe64s(QEMUFile *f, const int64_t *pv) -{ - qemu_put_be64s(f, (const uint64_t *)pv); -} - -static inline void qemu_get_s8s(QEMUFile *f, int8_t *pv) -{ - qemu_get_8s(f, (uint8_t *)pv); -} - -static inline void qemu_get_sbe16s(QEMUFile *f, int16_t *pv) -{ - qemu_get_be16s(f, (uint16_t *)pv); -} - -static inline void qemu_get_sbe32s(QEMUFile *f, int32_t *pv) -{ - qemu_get_be32s(f, (uint32_t *)pv); -} - -static inline void qemu_get_sbe64s(QEMUFile *f, int64_t *pv) -{ - qemu_get_be64s(f, (uint64_t *)pv); -} - size_t qemu_get_counted_string(QEMUFile *f, char buf[256]); + #endif diff --git a/migration/ram.c b/migration/ram.c index 26e03a5..f387e9c 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -36,8 +36,10 @@ #include "qemu/timer.h" #include "qemu/main-loop.h" #include "xbzrle.h" +#include "ram.h" #include "migration/migration.h" -#include "migration/qemu-file.h" +#include "migration/misc.h" +#include "qemu-file.h" #include "migration/vmstate.h" #include "postcopy-ram.h" #include "exec/address-spaces.h" diff --git a/migration/ram.h b/migration/ram.h new file mode 100644 index 0000000..c9563d1 --- /dev/null +++ b/migration/ram.h @@ -0,0 +1,70 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2011-2015 Red Hat Inc + * + * Authors: + * Juan Quintela <quintela@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QEMU_MIGRATION_RAM_H +#define QEMU_MIGRATION_RAM_H + +#include "qemu-common.h" +#include "exec/cpu-common.h" + +int64_t xbzrle_cache_resize(int64_t new_size); +uint64_t dup_mig_pages_transferred(void); +uint64_t norm_mig_pages_transferred(void); +uint64_t xbzrle_mig_bytes_transferred(void); +uint64_t xbzrle_mig_pages_transferred(void); +uint64_t xbzrle_mig_pages_cache_miss(void); +double xbzrle_mig_cache_miss_rate(void); +uint64_t xbzrle_mig_pages_overflow(void); +uint64_t ram_bytes_transferred(void); +uint64_t ram_bytes_remaining(void); +uint64_t ram_dirty_sync_count(void); +uint64_t ram_dirty_pages_rate(void); +uint64_t ram_postcopy_requests(void); +uint64_t ram_bytes_total(void); + +void migrate_compress_threads_create(void); +void migrate_compress_threads_join(void); +void migrate_decompress_threads_create(void); +void migrate_decompress_threads_join(void); + +uint64_t ram_pagesize_summary(void); +void migration_page_queue_free(void); +int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len); +void acct_update_position(QEMUFile *f, size_t size, bool zero); +void free_xbzrle_decoded_buf(void); +void ram_debug_dump_bitmap(unsigned long *todump, bool expected, + unsigned long pages); +void ram_postcopy_migrated_memory_release(MigrationState *ms); +/* For outgoing discard bitmap */ +int ram_postcopy_send_discard_bitmap(MigrationState *ms); +/* For incoming postcopy discard */ +int ram_discard_range(const char *block_name, uint64_t start, size_t length); +int ram_postcopy_incoming_init(MigrationIncomingState *mis); + +void ram_handle_compressed(void *host, uint8_t ch, uint64_t size); +#endif diff --git a/migration/rdma.c b/migration/rdma.c index 166cd60..e446c6f 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -17,9 +17,10 @@ #include "qapi/error.h" #include "qemu-common.h" #include "qemu/cutils.h" +#include "rdma.h" #include "migration/migration.h" -#include "migration/qemu-file.h" -#include "exec/cpu-common.h" +#include "qemu-file.h" +#include "ram.h" #include "qemu-file-channel.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" diff --git a/migration/rdma.h b/migration/rdma.h new file mode 100644 index 0000000..de2ba09 --- /dev/null +++ b/migration/rdma.h @@ -0,0 +1,25 @@ +/* + * RDMA protocol and interfaces + * + * Copyright IBM, Corp. 2010-2013 + * Copyright Red Hat, Inc. 2015-2016 + * + * Authors: + * Michael R. Hines <mrhines@us.ibm.com> + * Jiuxing Liu <jl@us.ibm.com> + * Daniel P. Berrange <berrange@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + * + */ + +#ifndef QEMU_MIGRATION_RDMA_H +#define QEMU_MIGRATION_RDMA_H + +void rdma_start_outgoing_migration(void *opaque, const char *host_port, + Error **errp); + +void rdma_start_incoming_migration(const char *host_port, Error **errp); + +#endif diff --git a/migration/savevm.c b/migration/savevm.c index a2d4f9c..9c320f5 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -35,7 +35,10 @@ #include "sysemu/sysemu.h" #include "qemu/timer.h" #include "migration/migration.h" +#include "migration/snapshot.h" +#include "ram.h" #include "qemu-file-channel.h" +#include "qemu-file.h" #include "savevm.h" #include "postcopy-ram.h" #include "qapi/qmp/qerror.h" @@ -272,7 +275,11 @@ typedef struct SaveStateEntry { int instance_id; int alias_id; int version_id; + /* version id read from the stream */ + int load_version_id; int section_id; + /* section id read from the stream */ + int load_section_id; SaveVMHandlers *ops; const VMStateDescription *vmsd; void *opaque; @@ -742,13 +749,13 @@ void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd, } } -static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id) +static int vmstate_load(QEMUFile *f, SaveStateEntry *se) { trace_vmstate_load(se->idstr, se->vmsd ? se->vmsd->name : "(old)"); if (!se->vmsd) { /* Old style */ - return se->ops->load_state(f, se->opaque, version_id); + return se->ops->load_state(f, se->opaque, se->load_version_id); } - return vmstate_load_state(f, se->vmsd, se->opaque, version_id); + return vmstate_load_state(f, se->vmsd, se->opaque, se->load_version_id); } static void vmstate_save_old_style(QEMUFile *f, SaveStateEntry *se, QJSON *vmdesc) @@ -1800,20 +1807,13 @@ static int loadvm_process_command(QEMUFile *f) return 0; } -struct LoadStateEntry { - QLIST_ENTRY(LoadStateEntry) entry; - SaveStateEntry *se; - int section_id; - int version_id; -}; - /* * Read a footer off the wire and check that it matches the expected section * * Returns: true if the footer was good * false if there is a problem (and calls error_report to say why) */ -static bool check_section_footer(QEMUFile *f, LoadStateEntry *le) +static bool check_section_footer(QEMUFile *f, SaveStateEntry *se) { uint8_t read_mark; uint32_t read_section_id; @@ -1826,15 +1826,15 @@ static bool check_section_footer(QEMUFile *f, LoadStateEntry *le) read_mark = qemu_get_byte(f); if (read_mark != QEMU_VM_SECTION_FOOTER) { - error_report("Missing section footer for %s", le->se->idstr); + error_report("Missing section footer for %s", se->idstr); return false; } read_section_id = qemu_get_be32(f); - if (read_section_id != le->section_id) { + if (read_section_id != se->load_section_id) { error_report("Mismatched section id in footer for %s -" " read 0x%x expected 0x%x", - le->se->idstr, read_section_id, le->section_id); + se->idstr, read_section_id, se->load_section_id); return false; } @@ -1842,22 +1842,11 @@ static bool check_section_footer(QEMUFile *f, LoadStateEntry *le) return true; } -void loadvm_free_handlers(MigrationIncomingState *mis) -{ - LoadStateEntry *le, *new_le; - - QLIST_FOREACH_SAFE(le, &mis->loadvm_handlers, entry, new_le) { - QLIST_REMOVE(le, entry); - g_free(le); - } -} - static int qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis) { uint32_t instance_id, version_id, section_id; SaveStateEntry *se; - LoadStateEntry *le; char idstr[256]; int ret; @@ -1887,6 +1876,8 @@ qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis) version_id, idstr, se->version_id); return -EINVAL; } + se->load_version_id = version_id; + se->load_section_id = section_id; /* Validate if it is a device's state */ if (xen_enabled() && se->is_ram) { @@ -1894,21 +1885,13 @@ qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis) return -EINVAL; } - /* Add entry */ - le = g_malloc0(sizeof(*le)); - - le->se = se; - le->section_id = section_id; - le->version_id = version_id; - QLIST_INSERT_HEAD(&mis->loadvm_handlers, le, entry); - - ret = vmstate_load(f, le->se, le->version_id); + ret = vmstate_load(f, se); if (ret < 0) { error_report("error while loading state for instance 0x%x of" " device '%s'", instance_id, idstr); return ret; } - if (!check_section_footer(f, le)) { + if (!check_section_footer(f, se)) { return -EINVAL; } @@ -1919,29 +1902,29 @@ static int qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis) { uint32_t section_id; - LoadStateEntry *le; + SaveStateEntry *se; int ret; section_id = qemu_get_be32(f); trace_qemu_loadvm_state_section_partend(section_id); - QLIST_FOREACH(le, &mis->loadvm_handlers, entry) { - if (le->section_id == section_id) { + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { + if (se->load_section_id == section_id) { break; } } - if (le == NULL) { + if (se == NULL) { error_report("Unknown savevm section %d", section_id); return -EINVAL; } - ret = vmstate_load(f, le->se, le->version_id); + ret = vmstate_load(f, se); if (ret < 0) { error_report("error while loading state section id %d(%s)", - section_id, le->se->idstr); + section_id, se->idstr); return ret; } - if (!check_section_footer(f, le)) { + if (!check_section_footer(f, se)) { return -EINVAL; } @@ -2086,7 +2069,7 @@ int qemu_loadvm_state(QEMUFile *f) return ret; } -int save_vmstate(const char *name, Error **errp) +int save_snapshot(const char *name, Error **errp) { BlockDriverState *bs, *bs1; QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1; @@ -2243,7 +2226,7 @@ void qmp_xen_load_devices_state(const char *filename, Error **errp) migration_incoming_state_destroy(); } -int load_vmstate(const char *name, Error **errp) +int load_snapshot(const char *name, Error **errp) { BlockDriverState *bs, *bs_vm_state; QEMUSnapshotInfo sn; diff --git a/migration/socket.c b/migration/socket.c index 53f9d61..85bfdcc 100644 --- a/migration/socket.c +++ b/migration/socket.c @@ -20,8 +20,9 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "channel.h" +#include "socket.h" #include "migration/migration.h" -#include "migration/qemu-file.h" +#include "qemu-file.h" #include "io/channel-socket.h" #include "trace.h" diff --git a/migration/socket.h b/migration/socket.h new file mode 100644 index 0000000..6b91e9d --- /dev/null +++ b/migration/socket.h @@ -0,0 +1,28 @@ +/* + * QEMU live migration via socket + * + * Copyright Red Hat, Inc. 2009-2016 + * + * Authors: + * Chris Lalancette <clalance@redhat.com> + * Daniel P. Berrange <berrange@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#ifndef QEMU_MIGRATION_SOCKET_H +#define QEMU_MIGRATION_SOCKET_H +void tcp_start_incoming_migration(const char *host_port, Error **errp); + +void tcp_start_outgoing_migration(MigrationState *s, const char *host_port, + Error **errp); + +void unix_start_incoming_migration(const char *path, Error **errp); + +void unix_start_outgoing_migration(MigrationState *s, const char *path, + Error **errp); +#endif diff --git a/migration/tls.c b/migration/tls.c index 34ad121..bae9aca 100644 --- a/migration/tls.c +++ b/migration/tls.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "channel.h" #include "migration/migration.h" +#include "tls.h" #include "io/channel-tls.h" #include "crypto/tlscreds.h" #include "qemu/error-report.h" diff --git a/migration/tls.h b/migration/tls.h new file mode 100644 index 0000000..cdd7000 --- /dev/null +++ b/migration/tls.h @@ -0,0 +1,34 @@ +/* + * QEMU migration TLS support + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef QEMU_MIGRATION_TLS_H +#define QEMU_MIGRATION_TLS_H + +#include "io/channel.h" + +void migration_tls_channel_process_incoming(MigrationState *s, + QIOChannel *ioc, + Error **errp); + +void migration_tls_channel_connect(MigrationState *s, + QIOChannel *ioc, + const char *hostname, + Error **errp); +#endif diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c index cc95e47..7287c6b 100644 --- a/migration/vmstate-types.c +++ b/migration/vmstate-types.c @@ -12,8 +12,9 @@ #include "qemu/osdep.h" #include "qemu-common.h" +#include "exec/cpu-common.h" +#include "qemu-file.h" #include "migration/migration.h" -#include "migration/qemu-file.h" #include "migration/vmstate.h" #include "qemu/error-report.h" #include "qemu/queue.h" diff --git a/migration/vmstate.c b/migration/vmstate.c index ff54531..51a19b6 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -13,8 +13,8 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "migration/migration.h" -#include "migration/qemu-file.h" #include "migration/vmstate.h" +#include "qemu-file.h" #include "qemu/bitops.h" #include "qemu/error-report.h" #include "trace.h" diff --git a/replay/replay-snapshot.c b/replay/replay-snapshot.c index c75cd38..a4ded29 100644 --- a/replay/replay-snapshot.c +++ b/replay/replay-snapshot.c @@ -19,6 +19,7 @@ #include "qapi/qmp/qstring.h" #include "qemu/error-report.h" #include "migration/vmstate.h" +#include "migration/snapshot.h" static void replay_pre_save(void *opaque) { @@ -66,13 +67,13 @@ void replay_vmstate_init(void) if (replay_snapshot) { if (replay_mode == REPLAY_MODE_RECORD) { - if (save_vmstate(replay_snapshot, &err) != 0) { + if (save_snapshot(replay_snapshot, &err) != 0) { error_report_err(err); error_report("Could not create snapshot for icount record"); exit(1); } } else if (replay_mode == REPLAY_MODE_PLAY) { - if (load_vmstate(replay_snapshot, &err) != 0) { + if (load_snapshot(replay_snapshot, &err) != 0) { error_report_err(err); error_report("Could not load snapshot for icount replay"); exit(1); diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index b4f9798..8186c9d 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -23,7 +23,6 @@ #include "qapi/error.h" #include "cpu.h" #include "qemu-common.h" -#include "migration/vmstate.h" #include "exec/exec-all.h" diff --git a/target/arm/cpu.c b/target/arm/cpu.c index c185eb1..e748097 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -550,6 +550,14 @@ static void arm_cpu_post_init(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + /* M profile implies PMSA. We have to do this here rather than + * in realize with the other feature-implication checks because + * we look at the PMSA bit to see if we should add some properties. + */ + if (arm_feature(&cpu->env, ARM_FEATURE_M)) { + set_feature(&cpu->env, ARM_FEATURE_PMSA); + } + if (arm_feature(&cpu->env, ARM_FEATURE_CBAR) || arm_feature(&cpu->env, ARM_FEATURE_CBAR_RO)) { qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_cbar_property, @@ -593,7 +601,7 @@ static void arm_cpu_post_init(Object *obj) &error_abort); } - if (arm_feature(&cpu->env, ARM_FEATURE_MPU)) { + if (arm_feature(&cpu->env, ARM_FEATURE_PMSA)) { qdev_property_add_static(DEVICE(obj), &arm_cpu_has_mpu_property, &error_abort); if (arm_feature(&cpu->env, ARM_FEATURE_V7)) { @@ -689,7 +697,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) if (arm_feature(env, ARM_FEATURE_V7) && !arm_feature(env, ARM_FEATURE_M) && - !arm_feature(env, ARM_FEATURE_MPU)) { + !arm_feature(env, ARM_FEATURE_PMSA)) { /* v7VMSA drops support for the old ARMv5 tiny pages, so we * can use 4K pages. */ @@ -750,8 +758,8 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } if (!cpu->has_pmu) { - cpu->has_pmu = false; unset_feature(env, ARM_FEATURE_PMU); + cpu->id_aa64dfr0 &= ~0xf00; } if (!arm_feature(env, ARM_FEATURE_EL2)) { @@ -763,11 +771,17 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) cpu->id_pfr1 &= ~0xf000; } + /* MPU can be configured out of a PMSA CPU either by setting has-mpu + * to false or by setting pmsav7-dregion to 0. + */ if (!cpu->has_mpu) { - unset_feature(env, ARM_FEATURE_MPU); + cpu->pmsav7_dregion = 0; + } + if (cpu->pmsav7_dregion == 0) { + cpu->has_mpu = false; } - if (arm_feature(env, ARM_FEATURE_MPU) && + if (arm_feature(env, ARM_FEATURE_PMSA) && arm_feature(env, ARM_FEATURE_V7)) { uint32_t nr = cpu->pmsav7_dregion; @@ -867,7 +881,7 @@ static void arm946_initfn(Object *obj) cpu->dtb_compatible = "arm,arm946"; set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_MPU); + set_feature(&cpu->env, ARM_FEATURE_PMSA); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); cpu->midr = 0x41059461; cpu->ctr = 0x0f004006; @@ -1079,7 +1093,7 @@ static void cortex_r5_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_THUMB_DIV); set_feature(&cpu->env, ARM_FEATURE_ARM_DIV); set_feature(&cpu->env, ARM_FEATURE_V7MP); - set_feature(&cpu->env, ARM_FEATURE_MPU); + set_feature(&cpu->env, ARM_FEATURE_PMSA); cpu->midr = 0x411fc153; /* r1p3 */ cpu->id_pfr0 = 0x0131; cpu->id_pfr1 = 0x001; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 048faed..13da503 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -418,6 +418,7 @@ typedef struct CPUARMState { uint32_t dfsr; /* Debug Fault Status Register */ uint32_t mmfar; /* MemManage Fault Address */ uint32_t bfar; /* BusFault Address */ + unsigned mpu_ctrl; /* MPU_CTRL (some bits kept in sctlr_el[1]) */ int exception; } v7m; @@ -1168,6 +1169,11 @@ FIELD(V7M_DFSR, DWTTRAP, 2, 1) FIELD(V7M_DFSR, VCATCH, 3, 1) FIELD(V7M_DFSR, EXTERNAL, 4, 1) +/* v7M MPU_CTRL bits */ +FIELD(V7M_MPU_CTRL, ENABLE, 0, 1) +FIELD(V7M_MPU_CTRL, HFNMIENA, 1, 1) +FIELD(V7M_MPU_CTRL, PRIVDEFENA, 2, 1) + /* If adding a feature bit which corresponds to a Linux ELF * HWCAP bit, remember to update the feature-bit-to-hwcap * mapping in linux-user/elfload.c:get_elf_hwcap(). @@ -1181,7 +1187,7 @@ enum arm_features { ARM_FEATURE_V6K, ARM_FEATURE_V7, ARM_FEATURE_THUMB2, - ARM_FEATURE_MPU, /* Only has Memory Protection Unit, not full MMU. */ + ARM_FEATURE_PMSA, /* no MMU; may have Memory Protection Unit */ ARM_FEATURE_VFP3, ARM_FEATURE_VFP_FP16, ARM_FEATURE_NEON, @@ -2039,6 +2045,28 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, * for the accesses done as part of a stage 1 page table walk, rather than * having to walk the stage 2 page table over and over.) * + * R profile CPUs have an MPU, but can use the same set of MMU indexes + * as A profile. They only need to distinguish NS EL0 and NS EL1 (and + * NS EL2 if we ever model a Cortex-R52). + * + * M profile CPUs are rather different as they do not have a true MMU. + * They have the following different MMU indexes: + * User + * Privileged + * Execution priority negative (this is like privileged, but the + * MPU HFNMIENA bit means that it may have different access permission + * check results to normal privileged code, so can't share a TLB). + * + * The ARMMMUIdx and the mmu index value used by the core QEMU TLB code + * are not quite the same -- different CPU types (most notably M profile + * vs A/R profile) would like to use MMU indexes with different semantics, + * but since we don't ever need to use all of those in a single CPU we + * can avoid setting NB_MMU_MODES to more than 8. The lower bits of + * ARMMMUIdx are the core TLB mmu index, and the higher bits are always + * the same for any particular CPU. + * Variables of type ARMMUIdx are always full values, and the core + * index values are in variables of type 'int'. + * * Our enumeration includes at the end some entries which are not "true" * mmu_idx values in that they don't have corresponding TLBs and are only * valid for doing slow path page table walks. @@ -2047,28 +2075,74 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, * of the AT/ATS operations. * The values used are carefully arranged to make mmu_idx => EL lookup easy. */ +#define ARM_MMU_IDX_A 0x10 /* A profile */ +#define ARM_MMU_IDX_NOTLB 0x20 /* does not have a TLB */ +#define ARM_MMU_IDX_M 0x40 /* M profile */ + +#define ARM_MMU_IDX_TYPE_MASK (~0x7) +#define ARM_MMU_IDX_COREIDX_MASK 0x7 + typedef enum ARMMMUIdx { - ARMMMUIdx_S12NSE0 = 0, - ARMMMUIdx_S12NSE1 = 1, - ARMMMUIdx_S1E2 = 2, - ARMMMUIdx_S1E3 = 3, - ARMMMUIdx_S1SE0 = 4, - ARMMMUIdx_S1SE1 = 5, - ARMMMUIdx_S2NS = 6, + ARMMMUIdx_S12NSE0 = 0 | ARM_MMU_IDX_A, + ARMMMUIdx_S12NSE1 = 1 | ARM_MMU_IDX_A, + ARMMMUIdx_S1E2 = 2 | ARM_MMU_IDX_A, + ARMMMUIdx_S1E3 = 3 | ARM_MMU_IDX_A, + ARMMMUIdx_S1SE0 = 4 | ARM_MMU_IDX_A, + ARMMMUIdx_S1SE1 = 5 | ARM_MMU_IDX_A, + ARMMMUIdx_S2NS = 6 | ARM_MMU_IDX_A, + ARMMMUIdx_MUser = 0 | ARM_MMU_IDX_M, + ARMMMUIdx_MPriv = 1 | ARM_MMU_IDX_M, + ARMMMUIdx_MNegPri = 2 | ARM_MMU_IDX_M, /* Indexes below here don't have TLBs and are used only for AT system * instructions or for the first stage of an S12 page table walk. */ - ARMMMUIdx_S1NSE0 = 7, - ARMMMUIdx_S1NSE1 = 8, + ARMMMUIdx_S1NSE0 = 0 | ARM_MMU_IDX_NOTLB, + ARMMMUIdx_S1NSE1 = 1 | ARM_MMU_IDX_NOTLB, } ARMMMUIdx; +/* Bit macros for the core-mmu-index values for each index, + * for use when calling tlb_flush_by_mmuidx() and friends. + */ +typedef enum ARMMMUIdxBit { + ARMMMUIdxBit_S12NSE0 = 1 << 0, + ARMMMUIdxBit_S12NSE1 = 1 << 1, + ARMMMUIdxBit_S1E2 = 1 << 2, + ARMMMUIdxBit_S1E3 = 1 << 3, + ARMMMUIdxBit_S1SE0 = 1 << 4, + ARMMMUIdxBit_S1SE1 = 1 << 5, + ARMMMUIdxBit_S2NS = 1 << 6, + ARMMMUIdxBit_MUser = 1 << 0, + ARMMMUIdxBit_MPriv = 1 << 1, + ARMMMUIdxBit_MNegPri = 1 << 2, +} ARMMMUIdxBit; + #define MMU_USER_IDX 0 +static inline int arm_to_core_mmu_idx(ARMMMUIdx mmu_idx) +{ + return mmu_idx & ARM_MMU_IDX_COREIDX_MASK; +} + +static inline ARMMMUIdx core_to_arm_mmu_idx(CPUARMState *env, int mmu_idx) +{ + if (arm_feature(env, ARM_FEATURE_M)) { + return mmu_idx | ARM_MMU_IDX_M; + } else { + return mmu_idx | ARM_MMU_IDX_A; + } +} + /* Return the exception level we're running at if this is our mmu_idx */ static inline int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx) { - assert(mmu_idx < ARMMMUIdx_S2NS); - return mmu_idx & 3; + switch (mmu_idx & ARM_MMU_IDX_TYPE_MASK) { + case ARM_MMU_IDX_A: + return mmu_idx & 3; + case ARM_MMU_IDX_M: + return mmu_idx == ARMMMUIdx_MUser ? 0 : 1; + default: + g_assert_not_reached(); + } } /* Determine the current mmu_idx to use for normal loads/stores */ @@ -2076,8 +2150,22 @@ static inline int cpu_mmu_index(CPUARMState *env, bool ifetch) { int el = arm_current_el(env); + if (arm_feature(env, ARM_FEATURE_M)) { + ARMMMUIdx mmu_idx = el == 0 ? ARMMMUIdx_MUser : ARMMMUIdx_MPriv; + + /* Execution priority is negative if FAULTMASK is set or + * we're in a HardFault or NMI handler. + */ + if ((env->v7m.exception > 0 && env->v7m.exception <= 3) + || env->daif & PSTATE_F) { + return arm_to_core_mmu_idx(ARMMMUIdx_MNegPri); + } + + return arm_to_core_mmu_idx(mmu_idx); + } + if (el < 2 && arm_is_secure_below_el3(env)) { - return ARMMMUIdx_S1SE0 + el; + return arm_to_core_mmu_idx(ARMMMUIdx_S1SE0 + el); } return el; } @@ -2473,7 +2561,7 @@ static inline uint32_t arm_regime_tbi1(CPUARMState *env, ARMMMUIdx mmu_idx) static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, target_ulong *cs_base, uint32_t *flags) { - ARMMMUIdx mmu_idx = cpu_mmu_index(env, false); + ARMMMUIdx mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false)); if (is_a64(env)) { *pc = env->pc; *flags = ARM_TBFLAG_AARCH64_STATE_MASK; @@ -2498,7 +2586,7 @@ static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, << ARM_TBFLAG_XSCALE_CPAR_SHIFT); } - *flags |= (mmu_idx << ARM_TBFLAG_MMUIDX_SHIFT); + *flags |= (arm_to_core_mmu_idx(mmu_idx) << ARM_TBFLAG_MMUIDX_SHIFT); /* The SS_ACTIVE and PSTATE_SS bits correspond to the state machine * states defined in the ARM ARM for software singlestep: diff --git a/target/arm/helper.c b/target/arm/helper.c index 8a3e448..2594faa 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -485,7 +485,7 @@ static void contextidr_write(CPUARMState *env, const ARMCPRegInfo *ri, { ARMCPU *cpu = arm_env_get_cpu(env); - if (raw_read(env, ri) != value && !arm_feature(env, ARM_FEATURE_MPU) + if (raw_read(env, ri) != value && !arm_feature(env, ARM_FEATURE_PMSA) && !extended_addresses_enabled(env)) { /* For VMSA (when not using the LPAE long descriptor page table * format) this register includes the ASID, so do a TLB flush. @@ -571,9 +571,9 @@ static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri, CPUState *cs = ENV_GET_CPU(env); tlb_flush_by_mmuidx(cs, - (1 << ARMMMUIdx_S12NSE1) | - (1 << ARMMMUIdx_S12NSE0) | - (1 << ARMMMUIdx_S2NS)); + ARMMMUIdxBit_S12NSE1 | + ARMMMUIdxBit_S12NSE0 | + ARMMMUIdxBit_S2NS); } static void tlbiall_nsnh_is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -582,9 +582,9 @@ static void tlbiall_nsnh_is_write(CPUARMState *env, const ARMCPRegInfo *ri, CPUState *cs = ENV_GET_CPU(env); tlb_flush_by_mmuidx_all_cpus_synced(cs, - (1 << ARMMMUIdx_S12NSE1) | - (1 << ARMMMUIdx_S12NSE0) | - (1 << ARMMMUIdx_S2NS)); + ARMMMUIdxBit_S12NSE1 | + ARMMMUIdxBit_S12NSE0 | + ARMMMUIdxBit_S2NS); } static void tlbiipas2_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -605,7 +605,7 @@ static void tlbiipas2_write(CPUARMState *env, const ARMCPRegInfo *ri, pageaddr = sextract64(value << 12, 0, 40); - tlb_flush_page_by_mmuidx(cs, pageaddr, (1 << ARMMMUIdx_S2NS)); + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_S2NS); } static void tlbiipas2_is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -621,7 +621,7 @@ static void tlbiipas2_is_write(CPUARMState *env, const ARMCPRegInfo *ri, pageaddr = sextract64(value << 12, 0, 40); tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, - (1 << ARMMMUIdx_S2NS)); + ARMMMUIdxBit_S2NS); } static void tlbiall_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -629,7 +629,7 @@ static void tlbiall_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, { CPUState *cs = ENV_GET_CPU(env); - tlb_flush_by_mmuidx(cs, (1 << ARMMMUIdx_S1E2)); + tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_S1E2); } static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -637,7 +637,7 @@ static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, { CPUState *cs = ENV_GET_CPU(env); - tlb_flush_by_mmuidx_all_cpus_synced(cs, (1 << ARMMMUIdx_S1E2)); + tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_S1E2); } static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -646,7 +646,7 @@ static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, CPUState *cs = ENV_GET_CPU(env); uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); - tlb_flush_page_by_mmuidx(cs, pageaddr, (1 << ARMMMUIdx_S1E2)); + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_S1E2); } static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -656,7 +656,7 @@ static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, - (1 << ARMMMUIdx_S1E2)); + ARMMMUIdxBit_S1E2); } static const ARMCPRegInfo cp_reginfo[] = { @@ -2596,9 +2596,9 @@ static void vttbr_write(CPUARMState *env, const ARMCPRegInfo *ri, /* Accesses to VTTBR may change the VMID so we must flush the TLB. */ if (raw_read(env, ri) != value) { tlb_flush_by_mmuidx(cs, - (1 << ARMMMUIdx_S12NSE1) | - (1 << ARMMMUIdx_S12NSE0) | - (1 << ARMMMUIdx_S2NS)); + ARMMMUIdxBit_S12NSE1 | + ARMMMUIdxBit_S12NSE0 | + ARMMMUIdxBit_S2NS); raw_write(env, ri, value); } } @@ -2957,12 +2957,12 @@ static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri, if (arm_is_secure_below_el3(env)) { tlb_flush_by_mmuidx(cs, - (1 << ARMMMUIdx_S1SE1) | - (1 << ARMMMUIdx_S1SE0)); + ARMMMUIdxBit_S1SE1 | + ARMMMUIdxBit_S1SE0); } else { tlb_flush_by_mmuidx(cs, - (1 << ARMMMUIdx_S12NSE1) | - (1 << ARMMMUIdx_S12NSE0)); + ARMMMUIdxBit_S12NSE1 | + ARMMMUIdxBit_S12NSE0); } } @@ -2974,12 +2974,12 @@ static void tlbi_aa64_vmalle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, if (sec) { tlb_flush_by_mmuidx_all_cpus_synced(cs, - (1 << ARMMMUIdx_S1SE1) | - (1 << ARMMMUIdx_S1SE0)); + ARMMMUIdxBit_S1SE1 | + ARMMMUIdxBit_S1SE0); } else { tlb_flush_by_mmuidx_all_cpus_synced(cs, - (1 << ARMMMUIdx_S12NSE1) | - (1 << ARMMMUIdx_S12NSE0)); + ARMMMUIdxBit_S12NSE1 | + ARMMMUIdxBit_S12NSE0); } } @@ -2995,18 +2995,18 @@ static void tlbi_aa64_alle1_write(CPUARMState *env, const ARMCPRegInfo *ri, if (arm_is_secure_below_el3(env)) { tlb_flush_by_mmuidx(cs, - (1 << ARMMMUIdx_S1SE1) | - (1 << ARMMMUIdx_S1SE0)); + ARMMMUIdxBit_S1SE1 | + ARMMMUIdxBit_S1SE0); } else { if (arm_feature(env, ARM_FEATURE_EL2)) { tlb_flush_by_mmuidx(cs, - (1 << ARMMMUIdx_S12NSE1) | - (1 << ARMMMUIdx_S12NSE0) | - (1 << ARMMMUIdx_S2NS)); + ARMMMUIdxBit_S12NSE1 | + ARMMMUIdxBit_S12NSE0 | + ARMMMUIdxBit_S2NS); } else { tlb_flush_by_mmuidx(cs, - (1 << ARMMMUIdx_S12NSE1) | - (1 << ARMMMUIdx_S12NSE0)); + ARMMMUIdxBit_S12NSE1 | + ARMMMUIdxBit_S12NSE0); } } } @@ -3017,7 +3017,7 @@ static void tlbi_aa64_alle2_write(CPUARMState *env, const ARMCPRegInfo *ri, ARMCPU *cpu = arm_env_get_cpu(env); CPUState *cs = CPU(cpu); - tlb_flush_by_mmuidx(cs, (1 << ARMMMUIdx_S1E2)); + tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_S1E2); } static void tlbi_aa64_alle3_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -3026,7 +3026,7 @@ static void tlbi_aa64_alle3_write(CPUARMState *env, const ARMCPRegInfo *ri, ARMCPU *cpu = arm_env_get_cpu(env); CPUState *cs = CPU(cpu); - tlb_flush_by_mmuidx(cs, (1 << ARMMMUIdx_S1E3)); + tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_S1E3); } static void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -3042,17 +3042,17 @@ static void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, if (sec) { tlb_flush_by_mmuidx_all_cpus_synced(cs, - (1 << ARMMMUIdx_S1SE1) | - (1 << ARMMMUIdx_S1SE0)); + ARMMMUIdxBit_S1SE1 | + ARMMMUIdxBit_S1SE0); } else if (has_el2) { tlb_flush_by_mmuidx_all_cpus_synced(cs, - (1 << ARMMMUIdx_S12NSE1) | - (1 << ARMMMUIdx_S12NSE0) | - (1 << ARMMMUIdx_S2NS)); + ARMMMUIdxBit_S12NSE1 | + ARMMMUIdxBit_S12NSE0 | + ARMMMUIdxBit_S2NS); } else { tlb_flush_by_mmuidx_all_cpus_synced(cs, - (1 << ARMMMUIdx_S12NSE1) | - (1 << ARMMMUIdx_S12NSE0)); + ARMMMUIdxBit_S12NSE1 | + ARMMMUIdxBit_S12NSE0); } } @@ -3061,7 +3061,7 @@ static void tlbi_aa64_alle2is_write(CPUARMState *env, const ARMCPRegInfo *ri, { CPUState *cs = ENV_GET_CPU(env); - tlb_flush_by_mmuidx_all_cpus_synced(cs, (1 << ARMMMUIdx_S1E2)); + tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_S1E2); } static void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -3069,7 +3069,7 @@ static void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri, { CPUState *cs = ENV_GET_CPU(env); - tlb_flush_by_mmuidx_all_cpus_synced(cs, (1 << ARMMMUIdx_S1E3)); + tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_S1E3); } static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -3086,12 +3086,12 @@ static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri, if (arm_is_secure_below_el3(env)) { tlb_flush_page_by_mmuidx(cs, pageaddr, - (1 << ARMMMUIdx_S1SE1) | - (1 << ARMMMUIdx_S1SE0)); + ARMMMUIdxBit_S1SE1 | + ARMMMUIdxBit_S1SE0); } else { tlb_flush_page_by_mmuidx(cs, pageaddr, - (1 << ARMMMUIdx_S12NSE1) | - (1 << ARMMMUIdx_S12NSE0)); + ARMMMUIdxBit_S12NSE1 | + ARMMMUIdxBit_S12NSE0); } } @@ -3106,7 +3106,7 @@ static void tlbi_aa64_vae2_write(CPUARMState *env, const ARMCPRegInfo *ri, CPUState *cs = CPU(cpu); uint64_t pageaddr = sextract64(value << 12, 0, 56); - tlb_flush_page_by_mmuidx(cs, pageaddr, (1 << ARMMMUIdx_S1E2)); + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_S1E2); } static void tlbi_aa64_vae3_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -3120,7 +3120,7 @@ static void tlbi_aa64_vae3_write(CPUARMState *env, const ARMCPRegInfo *ri, CPUState *cs = CPU(cpu); uint64_t pageaddr = sextract64(value << 12, 0, 56); - tlb_flush_page_by_mmuidx(cs, pageaddr, (1 << ARMMMUIdx_S1E3)); + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_S1E3); } static void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -3133,12 +3133,12 @@ static void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, if (sec) { tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, - (1 << ARMMMUIdx_S1SE1) | - (1 << ARMMMUIdx_S1SE0)); + ARMMMUIdxBit_S1SE1 | + ARMMMUIdxBit_S1SE0); } else { tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, - (1 << ARMMMUIdx_S12NSE1) | - (1 << ARMMMUIdx_S12NSE0)); + ARMMMUIdxBit_S12NSE1 | + ARMMMUIdxBit_S12NSE0); } } @@ -3149,7 +3149,7 @@ static void tlbi_aa64_vae2is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t pageaddr = sextract64(value << 12, 0, 56); tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, - (1 << ARMMMUIdx_S1E2)); + ARMMMUIdxBit_S1E2); } static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -3159,7 +3159,7 @@ static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t pageaddr = sextract64(value << 12, 0, 56); tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, - (1 << ARMMMUIdx_S1E3)); + ARMMMUIdxBit_S1E3); } static void tlbi_aa64_ipas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -3181,7 +3181,7 @@ static void tlbi_aa64_ipas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri, pageaddr = sextract64(value << 12, 0, 48); - tlb_flush_page_by_mmuidx(cs, pageaddr, (1 << ARMMMUIdx_S2NS)); + tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_S2NS); } static void tlbi_aa64_ipas2e1is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -3197,7 +3197,7 @@ static void tlbi_aa64_ipas2e1is_write(CPUARMState *env, const ARMCPRegInfo *ri, pageaddr = sextract64(value << 12, 0, 48); tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, - (1 << ARMMMUIdx_S2NS)); + ARMMMUIdxBit_S2NS); } static CPAccessResult aa64_zva_access(CPUARMState *env, const ARMCPRegInfo *ri, @@ -3258,6 +3258,11 @@ static void sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, return; } + if (arm_feature(env, ARM_FEATURE_PMSA) && !cpu->has_mpu) { + /* M bit is RAZ/WI for PMSA with no MPU implemented */ + value &= ~SCTLR_M; + } + raw_write(env, ri, value); /* ??? Lots of these bits are not implemented. */ /* This may enable/disable the MMU, so do a TLB flush. */ @@ -4615,7 +4620,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, v6k_cp_reginfo); } if (arm_feature(env, ARM_FEATURE_V7MP) && - !arm_feature(env, ARM_FEATURE_MPU)) { + !arm_feature(env, ARM_FEATURE_PMSA)) { define_arm_cp_regs(cpu, v7mp_cp_reginfo); } if (arm_feature(env, ARM_FEATURE_V7)) { @@ -4969,7 +4974,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) } } - if (arm_feature(env, ARM_FEATURE_MPU)) { + if (arm_feature(env, ARM_FEATURE_PMSA)) { if (arm_feature(env, ARM_FEATURE_V6)) { /* PMSAv6 not implemented */ assert(arm_feature(env, ARM_FEATURE_V7)); @@ -5131,7 +5136,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, id_pre_v8_midr_cp_reginfo); } define_arm_cp_regs(cpu, id_cp_reginfo); - if (!arm_feature(env, ARM_FEATURE_MPU)) { + if (!arm_feature(env, ARM_FEATURE_PMSA)) { define_one_arm_cp_reg(cpu, &id_tlbtr_reginfo); } else if (arm_feature(env, ARM_FEATURE_V7)) { define_one_arm_cp_reg(cpu, &id_mpuir_reginfo); @@ -6337,10 +6342,49 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) break; case EXCP_PREFETCH_ABORT: case EXCP_DATA_ABORT: - /* TODO: if we implemented the MPU registers, this is where we - * should set the MMFAR, etc from exception.fsr and exception.vaddress. + /* Note that for M profile we don't have a guest facing FSR, but + * the env->exception.fsr will be populated by the code that + * raises the fault, in the A profile short-descriptor format. */ - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM); + switch (env->exception.fsr & 0xf) { + case 0x8: /* External Abort */ + switch (cs->exception_index) { + case EXCP_PREFETCH_ABORT: + env->v7m.cfsr |= R_V7M_CFSR_PRECISERR_MASK; + qemu_log_mask(CPU_LOG_INT, "...with CFSR.PRECISERR\n"); + break; + case EXCP_DATA_ABORT: + env->v7m.cfsr |= + (R_V7M_CFSR_IBUSERR_MASK | R_V7M_CFSR_BFARVALID_MASK); + env->v7m.bfar = env->exception.vaddress; + qemu_log_mask(CPU_LOG_INT, + "...with CFSR.IBUSERR and BFAR 0x%x\n", + env->v7m.bfar); + break; + } + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_BUS); + break; + default: + /* All other FSR values are either MPU faults or "can't happen + * for M profile" cases. + */ + switch (cs->exception_index) { + case EXCP_PREFETCH_ABORT: + env->v7m.cfsr |= R_V7M_CFSR_IACCVIOL_MASK; + qemu_log_mask(CPU_LOG_INT, "...with CFSR.IACCVIOL\n"); + break; + case EXCP_DATA_ABORT: + env->v7m.cfsr |= + (R_V7M_CFSR_DACCVIOL_MASK | R_V7M_CFSR_MMARVALID_MASK); + env->v7m.mmfar = env->exception.vaddress; + qemu_log_mask(CPU_LOG_INT, + "...with CFSR.DACCVIOL and MMFAR 0x%x\n", + env->v7m.mmfar); + break; + } + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM); + break; + } break; case EXCP_BKPT: if (semihosting_enabled()) { @@ -6992,6 +7036,9 @@ static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx) case ARMMMUIdx_S1SE1: case ARMMMUIdx_S1NSE0: case ARMMMUIdx_S1NSE1: + case ARMMMUIdx_MPriv: + case ARMMMUIdx_MNegPri: + case ARMMMUIdx_MUser: return 1; default: g_assert_not_reached(); @@ -7008,6 +7055,9 @@ static inline bool regime_is_secure(CPUARMState *env, ARMMMUIdx mmu_idx) case ARMMMUIdx_S1NSE1: case ARMMMUIdx_S1E2: case ARMMMUIdx_S2NS: + case ARMMMUIdx_MPriv: + case ARMMMUIdx_MNegPri: + case ARMMMUIdx_MUser: return false; case ARMMMUIdx_S1E3: case ARMMMUIdx_S1SE0: @@ -7028,6 +7078,24 @@ static inline uint32_t regime_sctlr(CPUARMState *env, ARMMMUIdx mmu_idx) static inline bool regime_translation_disabled(CPUARMState *env, ARMMMUIdx mmu_idx) { + if (arm_feature(env, ARM_FEATURE_M)) { + switch (env->v7m.mpu_ctrl & + (R_V7M_MPU_CTRL_ENABLE_MASK | R_V7M_MPU_CTRL_HFNMIENA_MASK)) { + case R_V7M_MPU_CTRL_ENABLE_MASK: + /* Enabled, but not for HardFault and NMI */ + return mmu_idx == ARMMMUIdx_MNegPri; + case R_V7M_MPU_CTRL_ENABLE_MASK | R_V7M_MPU_CTRL_HFNMIENA_MASK: + /* Enabled for all cases */ + return false; + case 0: + default: + /* HFNMIENA set and ENABLE clear is UNPREDICTABLE, but + * we warned about that in armv7m_nvic.c when the guest set it. + */ + return true; + } + } + if (mmu_idx == ARMMMUIdx_S2NS) { return (env->cp15.hcr_el2 & HCR_VM) == 0; } @@ -7049,6 +7117,17 @@ static inline TCR *regime_tcr(CPUARMState *env, ARMMMUIdx mmu_idx) return &env->cp15.tcr_el[regime_el(env, mmu_idx)]; } +/* Convert a possible stage1+2 MMU index into the appropriate + * stage 1 MMU index + */ +static inline ARMMMUIdx stage_1_mmu_idx(ARMMMUIdx mmu_idx) +{ + if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) { + mmu_idx += (ARMMMUIdx_S1NSE0 - ARMMMUIdx_S12NSE0); + } + return mmu_idx; +} + /* Returns TBI0 value for current regime el */ uint32_t arm_regime_tbi0(CPUARMState *env, ARMMMUIdx mmu_idx) { @@ -7056,11 +7135,9 @@ uint32_t arm_regime_tbi0(CPUARMState *env, ARMMMUIdx mmu_idx) uint32_t el; /* For EL0 and EL1, TBI is controlled by stage 1's TCR, so convert - * a stage 1+2 mmu index into the appropriate stage 1 mmu index. - */ - if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) { - mmu_idx += ARMMMUIdx_S1NSE0; - } + * a stage 1+2 mmu index into the appropriate stage 1 mmu index. + */ + mmu_idx = stage_1_mmu_idx(mmu_idx); tcr = regime_tcr(env, mmu_idx); el = regime_el(env, mmu_idx); @@ -7079,11 +7156,9 @@ uint32_t arm_regime_tbi1(CPUARMState *env, ARMMMUIdx mmu_idx) uint32_t el; /* For EL0 and EL1, TBI is controlled by stage 1's TCR, so convert - * a stage 1+2 mmu index into the appropriate stage 1 mmu index. - */ - if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) { - mmu_idx += ARMMMUIdx_S1NSE0; - } + * a stage 1+2 mmu index into the appropriate stage 1 mmu index. + */ + mmu_idx = stage_1_mmu_idx(mmu_idx); tcr = regime_tcr(env, mmu_idx); el = regime_el(env, mmu_idx); @@ -7129,9 +7204,7 @@ static inline bool regime_using_lpae_format(CPUARMState *env, * on whether the long or short descriptor format is in use. */ bool arm_s1_regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx) { - if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) { - mmu_idx += ARMMMUIdx_S1NSE0; - } + mmu_idx = stage_1_mmu_idx(mmu_idx); return regime_using_lpae_format(env, mmu_idx); } @@ -7141,6 +7214,7 @@ static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx) switch (mmu_idx) { case ARMMMUIdx_S1SE0: case ARMMMUIdx_S1NSE0: + case ARMMMUIdx_MUser: return true; default: return false; @@ -8114,18 +8188,60 @@ static inline void get_phys_addr_pmsav7_default(CPUARMState *env, ARMMMUIdx mmu_idx, int32_t address, int *prot) { - *prot = PAGE_READ | PAGE_WRITE; - switch (address) { - case 0xF0000000 ... 0xFFFFFFFF: - if (regime_sctlr(env, mmu_idx) & SCTLR_V) { /* hivecs execing is ok */ + if (!arm_feature(env, ARM_FEATURE_M)) { + *prot = PAGE_READ | PAGE_WRITE; + switch (address) { + case 0xF0000000 ... 0xFFFFFFFF: + if (regime_sctlr(env, mmu_idx) & SCTLR_V) { + /* hivecs execing is ok */ + *prot |= PAGE_EXEC; + } + break; + case 0x00000000 ... 0x7FFFFFFF: *prot |= PAGE_EXEC; + break; + } + } else { + /* Default system address map for M profile cores. + * The architecture specifies which regions are execute-never; + * at the MPU level no other checks are defined. + */ + switch (address) { + case 0x00000000 ... 0x1fffffff: /* ROM */ + case 0x20000000 ... 0x3fffffff: /* SRAM */ + case 0x60000000 ... 0x7fffffff: /* RAM */ + case 0x80000000 ... 0x9fffffff: /* RAM */ + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + break; + case 0x40000000 ... 0x5fffffff: /* Peripheral */ + case 0xa0000000 ... 0xbfffffff: /* Device */ + case 0xc0000000 ... 0xdfffffff: /* Device */ + case 0xe0000000 ... 0xffffffff: /* System */ + *prot = PAGE_READ | PAGE_WRITE; + break; + default: + g_assert_not_reached(); } - break; - case 0x00000000 ... 0x7FFFFFFF: - *prot |= PAGE_EXEC; - break; + } +} + +static bool pmsav7_use_background_region(ARMCPU *cpu, + ARMMMUIdx mmu_idx, bool is_user) +{ + /* Return true if we should use the default memory map as a + * "background" region if there are no hits against any MPU regions. + */ + CPUARMState *env = &cpu->env; + + if (is_user) { + return false; } + if (arm_feature(env, ARM_FEATURE_M)) { + return env->v7m.mpu_ctrl & R_V7M_MPU_CTRL_PRIVDEFENA_MASK; + } else { + return regime_sctlr(env, mmu_idx) & SCTLR_BR; + } } static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, @@ -8154,16 +8270,18 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, } if (!rsize) { - qemu_log_mask(LOG_GUEST_ERROR, "DRSR.Rsize field can not be 0"); + qemu_log_mask(LOG_GUEST_ERROR, + "DRSR[%d]: Rsize field cannot be 0\n", n); continue; } rsize++; rmask = (1ull << rsize) - 1; if (base & rmask) { - qemu_log_mask(LOG_GUEST_ERROR, "DRBAR %" PRIx32 " misaligned " - "to DRSR region size, mask = %" PRIx32, - base, rmask); + qemu_log_mask(LOG_GUEST_ERROR, + "DRBAR[%d]: 0x%" PRIx32 " misaligned " + "to DRSR region size, mask = 0x%" PRIx32 "\n", + n, base, rmask); continue; } @@ -8200,9 +8318,10 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, } } if (rsize < TARGET_PAGE_BITS) { - qemu_log_mask(LOG_UNIMP, "No support for MPU (sub)region" + qemu_log_mask(LOG_UNIMP, + "DRSR[%d]: No support for MPU (sub)region " "alignment of %" PRIu32 " bits. Minimum is %d\n", - rsize, TARGET_PAGE_BITS); + n, rsize, TARGET_PAGE_BITS); continue; } if (srdis) { @@ -8212,8 +8331,7 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, } if (n == -1) { /* no hits */ - if (cpu->pmsav7_dregion && - (is_user || !(regime_sctlr(env, mmu_idx) & SCTLR_BR))) { + if (!pmsav7_use_background_region(cpu, mmu_idx, is_user)) { /* background fault */ *fsr = 0; return true; @@ -8237,8 +8355,8 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, break; default: qemu_log_mask(LOG_GUEST_ERROR, - "Bad value for AP bits in DRACR %" - PRIx32 "\n", ap); + "DRACR[%d]: Bad value for AP bits: 0x%" + PRIx32 "\n", n, ap); } } else { /* Priv. mode AP bits decoding */ switch (ap) { @@ -8255,8 +8373,8 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, break; default: qemu_log_mask(LOG_GUEST_ERROR, - "Bad value for AP bits in DRACR %" - PRIx32 "\n", ap); + "DRACR[%d]: Bad value for AP bits: 0x%" + PRIx32 "\n", n, ap); } } @@ -8385,7 +8503,7 @@ static bool get_phys_addr(CPUARMState *env, target_ulong address, int ret; ret = get_phys_addr(env, address, access_type, - mmu_idx + ARMMMUIdx_S1NSE0, &ipa, attrs, + stage_1_mmu_idx(mmu_idx), &ipa, attrs, prot, page_size, fsr, fi); /* If S1 fails or S2 is disabled, return early. */ @@ -8406,7 +8524,7 @@ static bool get_phys_addr(CPUARMState *env, target_ulong address, /* * For non-EL2 CPUs a stage1+stage2 translation is just stage 1. */ - mmu_idx += ARMMMUIdx_S1NSE0; + mmu_idx = stage_1_mmu_idx(mmu_idx); } } @@ -8432,11 +8550,23 @@ static bool get_phys_addr(CPUARMState *env, target_ulong address, /* pmsav7 has special handling for when MPU is disabled so call it before * the common MMU/MPU disabled check below. */ - if (arm_feature(env, ARM_FEATURE_MPU) && + if (arm_feature(env, ARM_FEATURE_PMSA) && arm_feature(env, ARM_FEATURE_V7)) { + bool ret; *page_size = TARGET_PAGE_SIZE; - return get_phys_addr_pmsav7(env, address, access_type, mmu_idx, - phys_ptr, prot, fsr); + ret = get_phys_addr_pmsav7(env, address, access_type, mmu_idx, + phys_ptr, prot, fsr); + qemu_log_mask(CPU_LOG_MMU, "PMSAv7 MPU lookup for %s at 0x%08" PRIx32 + " mmu_idx %u -> %s (prot %c%c%c)\n", + access_type == 1 ? "reading" : + (access_type == 2 ? "writing" : "execute"), + (uint32_t)address, mmu_idx, + ret ? "Miss" : "Hit", + *prot & PAGE_READ ? 'r' : '-', + *prot & PAGE_WRITE ? 'w' : '-', + *prot & PAGE_EXEC ? 'x' : '-'); + + return ret; } if (regime_translation_disabled(env, mmu_idx)) { @@ -8447,7 +8577,7 @@ static bool get_phys_addr(CPUARMState *env, target_ulong address, return 0; } - if (arm_feature(env, ARM_FEATURE_MPU)) { + if (arm_feature(env, ARM_FEATURE_PMSA)) { /* Pre-v7 MPU */ *page_size = TARGET_PAGE_SIZE; return get_phys_addr_pmsav5(env, address, access_type, mmu_idx, @@ -8482,7 +8612,8 @@ bool arm_tlb_fill(CPUState *cs, vaddr address, int ret; MemTxAttrs attrs = {}; - ret = get_phys_addr(env, address, access_type, mmu_idx, &phys_addr, + ret = get_phys_addr(env, address, access_type, + core_to_arm_mmu_idx(env, mmu_idx), &phys_addr, &attrs, &prot, &page_size, fsr, fi); if (!ret) { /* Map a single [sub]page. */ @@ -8507,10 +8638,11 @@ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr, bool ret; uint32_t fsr; ARMMMUFaultInfo fi = {}; + ARMMMUIdx mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false)); *attrs = (MemTxAttrs) {}; - ret = get_phys_addr(env, addr, 0, cpu_mmu_index(env, false), &phys_addr, + ret = get_phys_addr(env, addr, 0, mmu_idx, &phys_addr, attrs, &prot, &page_size, &fsr, &fi); if (ret) { diff --git a/target/arm/machine.c b/target/arm/machine.c index d8094a8..1a40469 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -99,8 +99,8 @@ static bool m_needed(void *opaque) static const VMStateDescription vmstate_m = { .name = "cpu/m", - .version_id = 3, - .minimum_version_id = 3, + .version_id = 4, + .minimum_version_id = 4, .needed = m_needed, .fields = (VMStateField[]) { VMSTATE_UINT32(env.v7m.vecbase, ARMCPU), @@ -112,6 +112,7 @@ static const VMStateDescription vmstate_m = { VMSTATE_UINT32(env.v7m.dfsr, ARMCPU), VMSTATE_UINT32(env.v7m.mmfar, ARMCPU), VMSTATE_UINT32(env.v7m.bfar, ARMCPU), + VMSTATE_UINT32(env.v7m.mpu_ctrl, ARMCPU), VMSTATE_INT32(env.v7m.exception, ARMCPU), VMSTATE_END_OF_LIST() } @@ -142,7 +143,7 @@ static bool pmsav7_needed(void *opaque) ARMCPU *cpu = opaque; CPUARMState *env = &cpu->env; - return arm_feature(env, ARM_FEATURE_MPU) && + return arm_feature(env, ARM_FEATURE_PMSA) && arm_feature(env, ARM_FEATURE_V7); } diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c index 156b825..2a85666 100644 --- a/target/arm/op_helper.c +++ b/target/arm/op_helper.c @@ -194,6 +194,7 @@ void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, int target_el; bool same_el; uint32_t syn; + ARMMMUIdx arm_mmu_idx = core_to_arm_mmu_idx(env, mmu_idx); if (retaddr) { /* now we have a real cpu fault */ @@ -208,7 +209,7 @@ void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, /* the DFSR for an alignment fault depends on whether we're using * the LPAE long descriptor format, or the short descriptor format */ - if (arm_s1_regime_using_lpae_format(env, cpu_mmu_index(env, false))) { + if (arm_s1_regime_using_lpae_format(env, arm_mmu_idx)) { env->exception.fsr = (1 << 9) | 0x21; } else { env->exception.fsr = 0x1; diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 24de30d..a82ab49 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -101,21 +101,27 @@ void a64_translate_init(void) offsetof(CPUARMState, exclusive_high), "exclusive_high"); } -static inline ARMMMUIdx get_a64_user_mem_index(DisasContext *s) +static inline int get_a64_user_mem_index(DisasContext *s) { - /* Return the mmu_idx to use for A64 "unprivileged load/store" insns: + /* Return the core mmu_idx to use for A64 "unprivileged load/store" insns: * if EL1, access as if EL0; otherwise access at current EL */ + ARMMMUIdx useridx; + switch (s->mmu_idx) { case ARMMMUIdx_S12NSE1: - return ARMMMUIdx_S12NSE0; + useridx = ARMMMUIdx_S12NSE0; + break; case ARMMMUIdx_S1SE1: - return ARMMMUIdx_S1SE0; + useridx = ARMMMUIdx_S1SE0; + break; case ARMMMUIdx_S2NS: g_assert_not_reached(); default: - return s->mmu_idx; + useridx = s->mmu_idx; + break; } + return arm_to_core_mmu_idx(useridx); } void aarch64_cpu_dump_state(CPUState *cs, FILE *f, @@ -11212,7 +11218,7 @@ void gen_intermediate_code_a64(ARMCPU *cpu, TranslationBlock *tb) dc->be_data = ARM_TBFLAG_BE_DATA(tb->flags) ? MO_BE : MO_LE; dc->condexec_mask = 0; dc->condexec_cond = 0; - dc->mmu_idx = ARM_TBFLAG_MMUIDX(tb->flags); + dc->mmu_idx = core_to_arm_mmu_idx(env, ARM_TBFLAG_MMUIDX(tb->flags)); dc->tbi0 = ARM_TBFLAG_TBI0(tb->flags); dc->tbi1 = ARM_TBFLAG_TBI1(tb->flags); dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); diff --git a/target/arm/translate.c b/target/arm/translate.c index 0b5a0bc..ae6646c 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -145,9 +145,9 @@ static void disas_set_da_iss(DisasContext *s, TCGMemOp memop, ISSInfo issinfo) disas_set_insn_syndrome(s, syn); } -static inline ARMMMUIdx get_a32_user_mem_index(DisasContext *s) +static inline int get_a32_user_mem_index(DisasContext *s) { - /* Return the mmu_idx to use for A32/T32 "unprivileged load/store" + /* Return the core mmu_idx to use for A32/T32 "unprivileged load/store" * insns: * if PL2, UNPREDICTABLE (we choose to implement as if PL0) * otherwise, access as if at PL0. @@ -156,11 +156,15 @@ static inline ARMMMUIdx get_a32_user_mem_index(DisasContext *s) case ARMMMUIdx_S1E2: /* this one is UNPREDICTABLE */ case ARMMMUIdx_S12NSE0: case ARMMMUIdx_S12NSE1: - return ARMMMUIdx_S12NSE0; + return arm_to_core_mmu_idx(ARMMMUIdx_S12NSE0); case ARMMMUIdx_S1E3: case ARMMMUIdx_S1SE0: case ARMMMUIdx_S1SE1: - return ARMMMUIdx_S1SE0; + return arm_to_core_mmu_idx(ARMMMUIdx_S1SE0); + case ARMMMUIdx_MUser: + case ARMMMUIdx_MPriv: + case ARMMMUIdx_MNegPri: + return arm_to_core_mmu_idx(ARMMMUIdx_MUser); case ARMMMUIdx_S2NS: default: g_assert_not_reached(); @@ -11816,7 +11820,7 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb) dc->be_data = ARM_TBFLAG_BE_DATA(tb->flags) ? MO_BE : MO_LE; dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1; dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4; - dc->mmu_idx = ARM_TBFLAG_MMUIDX(tb->flags); + dc->mmu_idx = core_to_arm_mmu_idx(env, ARM_TBFLAG_MMUIDX(tb->flags)); dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); #if !defined(CONFIG_USER_ONLY) dc->user = (dc->current_el == 0); diff --git a/target/arm/translate.h b/target/arm/translate.h index 629dab9..6b2cc34 100644 --- a/target/arm/translate.h +++ b/target/arm/translate.h @@ -88,7 +88,7 @@ static inline int arm_dc_feature(DisasContext *dc, int feature) static inline int get_mem_index(DisasContext *s) { - return s->mmu_idx; + return arm_to_core_mmu_idx(s->mmu_idx); } /* Function used to determine the target exception EL when otherwise not known diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 1d791d0..30299e9 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -22,7 +22,6 @@ #include "qapi/error.h" #include "cpu.h" #include "qemu-common.h" -#include "migration/vmstate.h" #include "exec/exec-all.h" diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index a69005d..accef03 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -32,7 +32,6 @@ #include "qemu/error-report.h" #include "trace.h" #include "qapi/visitor.h" -#include "migration/vmstate.h" #include "exec/exec-all.h" #ifndef CONFIG_USER_ONLY #include "hw/hw.h" diff --git a/target/tilegx/cpu.c b/target/tilegx/cpu.c index d90e38e..4532639 100644 --- a/target/tilegx/cpu.c +++ b/target/tilegx/cpu.c @@ -23,7 +23,6 @@ #include "cpu.h" #include "qemu-common.h" #include "hw/qdev-properties.h" -#include "migration/vmstate.h" #include "linux-user/syscall_defs.h" #include "exec/exec-all.h" diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c index 25389bc..c52aff9 100644 --- a/tests/test-vmstate.c +++ b/tests/test-vmstate.c @@ -27,7 +27,8 @@ #include "qemu-common.h" #include "migration/migration.h" #include "migration/vmstate.h" -#include "migration/qemu-file.h" +#include "migration/qemu-file-types.h" +#include "../migration/qemu-file.h" #include "../migration/qemu-file-channel.h" #include "qemu/coroutine.h" #include "io/channel-file.h" @@ -86,7 +86,8 @@ int main(int argc, char **argv) #include "qemu/log.h" #include "sysemu/blockdev.h" #include "hw/block/block.h" -#include "migration/block.h" +#include "migration/misc.h" +#include "migration/snapshot.h" #include "sysemu/tpm.h" #include "sysemu/dma.h" #include "hw/audio/soundhw.h" @@ -4722,7 +4723,7 @@ int main(int argc, char **argv, char **envp) replay_vmstate_init(); } else if (loadvm) { Error *local_err = NULL; - if (load_vmstate(loadvm, &local_err) < 0) { + if (load_snapshot(loadvm, &local_err) < 0) { error_report_err(local_err); autostart = 0; } |