diff options
79 files changed, 3153 insertions, 509 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 026ea4f..7efaccf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -726,7 +726,7 @@ F: vl.c Human Monitor (HMP) M: Luiz Capitulino <lcapitulino@redhat.com> -S: Supported +S: Maintained F: monitor.c F: hmp.c F: hmp-commands.hx @@ -758,7 +758,7 @@ T: git git://github.com/bonzini/qemu.git nbd-next QAPI M: Luiz Capitulino <lcapitulino@redhat.com> M: Michael Roth <mdroth@linux.vnet.ibm.com> -S: Supported +S: Maintained F: qapi/ T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp @@ -772,7 +772,7 @@ T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp QMP M: Luiz Capitulino <lcapitulino@redhat.com> -S: Supported +S: Maintained F: qmp.c F: monitor.c F: qmp-commands.hx @@ -159,6 +159,7 @@ qemu-options.def: $(SRC_PATH)/qemu-options.hx SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS)) SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES)) +$(SOFTMMU_SUBDIR_RULES): $(block-obj-y) $(SOFTMMU_SUBDIR_RULES): config-all-devices.mak subdir-%: @@ -319,7 +320,7 @@ ifdef INSTALL_BLOBS BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \ vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \ acpi-dsdt.aml q35-acpi-dsdt.aml \ -ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin \ +ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \ pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \ pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \ efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \ @@ -547,8 +547,9 @@ int get_tmp_filename(char *filename, int size) int fd; const char *tmpdir; tmpdir = getenv("TMPDIR"); - if (!tmpdir) - tmpdir = "/tmp"; + if (!tmpdir) { + tmpdir = "/var/tmp"; + } if (snprintf(filename, size, "%s/vl.XXXXXX", tmpdir) >= size) { return -EOVERFLOW; } diff --git a/block/gluster.c b/block/gluster.c index 14d390b..54ee9b7 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -45,11 +45,13 @@ typedef struct GlusterConf { static void qemu_gluster_gconf_free(GlusterConf *gconf) { - g_free(gconf->server); - g_free(gconf->volname); - g_free(gconf->image); - g_free(gconf->transport); - g_free(gconf); + if (gconf) { + g_free(gconf->server); + g_free(gconf->volname); + g_free(gconf->image); + g_free(gconf->transport); + g_free(gconf); + } } static int parse_volume_options(GlusterConf *gconf, char *path) @@ -272,11 +274,28 @@ static QemuOptsList runtime_opts = { }, }; +static void qemu_gluster_parse_flags(int bdrv_flags, int *open_flags) +{ + assert(open_flags != NULL); + + *open_flags |= O_BINARY; + + if (bdrv_flags & BDRV_O_RDWR) { + *open_flags |= O_RDWR; + } else { + *open_flags |= O_RDONLY; + } + + if ((bdrv_flags & BDRV_O_NOCACHE)) { + *open_flags |= O_DIRECT; + } +} + static int qemu_gluster_open(BlockDriverState *bs, QDict *options, int bdrv_flags, Error **errp) { BDRVGlusterState *s = bs->opaque; - int open_flags = O_BINARY; + int open_flags = 0; int ret = 0; GlusterConf *gconf = g_malloc0(sizeof(GlusterConf)); QemuOpts *opts; @@ -299,15 +318,7 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options, goto out; } - if (bdrv_flags & BDRV_O_RDWR) { - open_flags |= O_RDWR; - } else { - open_flags |= O_RDONLY; - } - - if ((bdrv_flags & BDRV_O_NOCACHE)) { - open_flags |= O_DIRECT; - } + qemu_gluster_parse_flags(bdrv_flags, &open_flags); s->fd = glfs_open(s->glfs, gconf->image, open_flags); if (!s->fd) { @@ -329,6 +340,96 @@ out: return ret; } +typedef struct BDRVGlusterReopenState { + struct glfs *glfs; + struct glfs_fd *fd; +} BDRVGlusterReopenState; + + +static int qemu_gluster_reopen_prepare(BDRVReopenState *state, + BlockReopenQueue *queue, Error **errp) +{ + int ret = 0; + BDRVGlusterReopenState *reop_s; + GlusterConf *gconf = NULL; + int open_flags = 0; + + assert(state != NULL); + assert(state->bs != NULL); + + state->opaque = g_malloc0(sizeof(BDRVGlusterReopenState)); + reop_s = state->opaque; + + qemu_gluster_parse_flags(state->flags, &open_flags); + + gconf = g_malloc0(sizeof(GlusterConf)); + + reop_s->glfs = qemu_gluster_init(gconf, state->bs->filename); + if (reop_s->glfs == NULL) { + ret = -errno; + goto exit; + } + + reop_s->fd = glfs_open(reop_s->glfs, gconf->image, open_flags); + if (reop_s->fd == NULL) { + /* reops->glfs will be cleaned up in _abort */ + ret = -errno; + goto exit; + } + +exit: + /* state->opaque will be freed in either the _abort or _commit */ + qemu_gluster_gconf_free(gconf); + return ret; +} + +static void qemu_gluster_reopen_commit(BDRVReopenState *state) +{ + BDRVGlusterReopenState *reop_s = state->opaque; + BDRVGlusterState *s = state->bs->opaque; + + + /* close the old */ + if (s->fd) { + glfs_close(s->fd); + } + if (s->glfs) { + glfs_fini(s->glfs); + } + + /* use the newly opened image / connection */ + s->fd = reop_s->fd; + s->glfs = reop_s->glfs; + + g_free(state->opaque); + state->opaque = NULL; + + return; +} + + +static void qemu_gluster_reopen_abort(BDRVReopenState *state) +{ + BDRVGlusterReopenState *reop_s = state->opaque; + + if (reop_s == NULL) { + return; + } + + if (reop_s->fd) { + glfs_close(reop_s->fd); + } + + if (reop_s->glfs) { + glfs_fini(reop_s->glfs); + } + + g_free(state->opaque); + state->opaque = NULL; + + return; +} + #ifdef CONFIG_GLUSTERFS_ZEROFILL static coroutine_fn int qemu_gluster_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, int nb_sectors, BdrvRequestFlags flags) @@ -619,6 +720,9 @@ static BlockDriver bdrv_gluster = { .instance_size = sizeof(BDRVGlusterState), .bdrv_needs_filename = true, .bdrv_file_open = qemu_gluster_open, + .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, + .bdrv_reopen_commit = qemu_gluster_reopen_commit, + .bdrv_reopen_abort = qemu_gluster_reopen_abort, .bdrv_close = qemu_gluster_close, .bdrv_create = qemu_gluster_create, .bdrv_getlength = qemu_gluster_getlength, @@ -643,6 +747,9 @@ static BlockDriver bdrv_gluster_tcp = { .instance_size = sizeof(BDRVGlusterState), .bdrv_needs_filename = true, .bdrv_file_open = qemu_gluster_open, + .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, + .bdrv_reopen_commit = qemu_gluster_reopen_commit, + .bdrv_reopen_abort = qemu_gluster_reopen_abort, .bdrv_close = qemu_gluster_close, .bdrv_create = qemu_gluster_create, .bdrv_getlength = qemu_gluster_getlength, @@ -667,6 +774,9 @@ static BlockDriver bdrv_gluster_unix = { .instance_size = sizeof(BDRVGlusterState), .bdrv_needs_filename = true, .bdrv_file_open = qemu_gluster_open, + .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, + .bdrv_reopen_commit = qemu_gluster_reopen_commit, + .bdrv_reopen_abort = qemu_gluster_reopen_abort, .bdrv_close = qemu_gluster_close, .bdrv_create = qemu_gluster_create, .bdrv_getlength = qemu_gluster_getlength, @@ -691,6 +801,9 @@ static BlockDriver bdrv_gluster_rdma = { .instance_size = sizeof(BDRVGlusterState), .bdrv_needs_filename = true, .bdrv_file_open = qemu_gluster_open, + .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, + .bdrv_reopen_commit = qemu_gluster_reopen_commit, + .bdrv_reopen_abort = qemu_gluster_reopen_abort, .bdrv_close = qemu_gluster_close, .bdrv_create = qemu_gluster_create, .bdrv_getlength = qemu_gluster_getlength, diff --git a/block/iscsi.c b/block/iscsi.c index 41ec097..0a15f53 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -145,12 +145,13 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, if (iTask->retries-- > 0 && status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_UNIT_ATTENTION) { + error_report("iSCSI CheckCondition: %s", iscsi_get_error(iscsi)); iTask->do_retry = 1; goto out; } if (status != SCSI_STATUS_GOOD) { - error_report("iSCSI: Failure. %s", iscsi_get_error(iscsi)); + error_report("iSCSI Failure: %s", iscsi_get_error(iscsi)); } out: @@ -325,6 +326,7 @@ retry: } if (iTask.do_retry) { + iTask.complete = 0; goto retry; } @@ -399,6 +401,7 @@ retry: } if (iTask.do_retry) { + iTask.complete = 0; goto retry; } @@ -433,6 +436,7 @@ retry: } if (iTask.do_retry) { + iTask.complete = 0; goto retry; } @@ -683,6 +687,7 @@ retry: scsi_free_scsi_task(iTask.task); iTask.task = NULL; } + iTask.complete = 0; goto retry; } @@ -767,6 +772,7 @@ retry: } if (iTask.do_retry) { + iTask.complete = 0; goto retry; } @@ -830,24 +836,26 @@ retry: qemu_coroutine_yield(); } + if (iTask.status == SCSI_STATUS_CHECK_CONDITION && + iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && + iTask.task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) { + /* WRITE SAME is not supported by the target */ + iscsilun->has_write_same = false; + scsi_free_scsi_task(iTask.task); + return -ENOTSUP; + } + if (iTask.task != NULL) { scsi_free_scsi_task(iTask.task); iTask.task = NULL; } if (iTask.do_retry) { + iTask.complete = 0; goto retry; } if (iTask.status != SCSI_STATUS_GOOD) { - if (iTask.status == SCSI_STATUS_CHECK_CONDITION && - iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && - iTask.task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) { - /* WRITE SAME is not supported by the target */ - iscsilun->has_write_same = false; - return -ENOTSUP; - } - return -EIO; } @@ -1060,7 +1068,7 @@ static QemuOptsList runtime_opts = { }; static struct scsi_task *iscsi_do_inquiry(struct iscsi_context *iscsi, int lun, - int evpd, int pc, Error **errp) + int evpd, int pc, void **inq, Error **errp) { int full_size; struct scsi_task *task = NULL; @@ -1079,14 +1087,19 @@ static struct scsi_task *iscsi_do_inquiry(struct iscsi_context *iscsi, int lun, } } + *inq = scsi_datain_unmarshall(task); + if (*inq == NULL) { + error_setg(errp, "iSCSI: failed to unmarshall inquiry datain blob"); + goto fail; + } + return task; fail: error_setg(errp, "iSCSI: Inquiry command failed : %s", iscsi_get_error(iscsi)); - if (task) { + if (task != NULL) { scsi_free_scsi_task(task); - return NULL; } return NULL; } @@ -1107,11 +1120,12 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, struct iscsi_url *iscsi_url = NULL; struct scsi_task *task = NULL; struct scsi_inquiry_standard *inq = NULL; + struct scsi_inquiry_supported_pages *inq_vpd; char *initiator_name = NULL; QemuOpts *opts; Error *local_err = NULL; const char *filename; - int ret; + int i, ret; if ((BDRV_SECTOR_SIZE % 512) != 0) { error_setg(errp, "iSCSI: Invalid BDRV_SECTOR_SIZE. " @@ -1197,24 +1211,17 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, iscsilun->iscsi = iscsi; iscsilun->lun = iscsi_url->lun; + iscsilun->has_write_same = true; - task = iscsi_inquiry_sync(iscsi, iscsilun->lun, 0, 0, 36); - - if (task == NULL || task->status != SCSI_STATUS_GOOD) { - error_setg(errp, "iSCSI: failed to send inquiry command."); - ret = -EINVAL; - goto out; - } - - inq = scsi_datain_unmarshall(task); - if (inq == NULL) { - error_setg(errp, "iSCSI: Failed to unmarshall inquiry data."); + task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 0, 0, + (void **) &inq, errp); + if (task == NULL) { ret = -EINVAL; goto out; } - iscsilun->type = inq->periperal_device_type; - iscsilun->has_write_same = true; + scsi_free_scsi_task(task); + task = NULL; iscsi_readcapacity_sync(iscsilun, &local_err); if (local_err != NULL) { @@ -1233,46 +1240,48 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, bs->sg = 1; } - if (iscsilun->lbpme) { - struct scsi_inquiry_logical_block_provisioning *inq_lbp; - task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, - SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING, - errp); - if (task == NULL) { - ret = -EINVAL; - goto out; - } - inq_lbp = scsi_datain_unmarshall(task); - if (inq_lbp == NULL) { - error_setg(errp, "iSCSI: failed to unmarshall inquiry datain blob"); - ret = -EINVAL; - goto out; - } - memcpy(&iscsilun->lbp, inq_lbp, - sizeof(struct scsi_inquiry_logical_block_provisioning)); - scsi_free_scsi_task(task); - task = NULL; + task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, + SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES, + (void **) &inq_vpd, errp); + if (task == NULL) { + ret = -EINVAL; + goto out; } - - if (iscsilun->lbp.lbpu || iscsilun->lbp.lbpws) { + for (i = 0; i < inq_vpd->num_pages; i++) { + struct scsi_task *inq_task; + struct scsi_inquiry_logical_block_provisioning *inq_lbp; struct scsi_inquiry_block_limits *inq_bl; - task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, - SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS, errp); - if (task == NULL) { - ret = -EINVAL; - goto out; - } - inq_bl = scsi_datain_unmarshall(task); - if (inq_bl == NULL) { - error_setg(errp, "iSCSI: failed to unmarshall inquiry datain blob"); - ret = -EINVAL; - goto out; + switch (inq_vpd->pages[i]) { + case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING: + inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, + SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING, + (void **) &inq_lbp, errp); + if (inq_task == NULL) { + ret = -EINVAL; + goto out; + } + memcpy(&iscsilun->lbp, inq_lbp, + sizeof(struct scsi_inquiry_logical_block_provisioning)); + scsi_free_scsi_task(inq_task); + break; + case SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS: + inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, + SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS, + (void **) &inq_bl, errp); + if (inq_task == NULL) { + ret = -EINVAL; + goto out; + } + memcpy(&iscsilun->bl, inq_bl, + sizeof(struct scsi_inquiry_block_limits)); + scsi_free_scsi_task(inq_task); + break; + default: + break; } - memcpy(&iscsilun->bl, inq_bl, - sizeof(struct scsi_inquiry_block_limits)); - scsi_free_scsi_task(task); - task = NULL; } + scsi_free_scsi_task(task); + task = NULL; #if defined(LIBISCSI_FEATURE_NOP_COUNTER) /* Set up a timer for sending out iSCSI NOPs */ diff --git a/block/quorum.c b/block/quorum.c index 6c28239..bd997b7 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -200,11 +200,14 @@ static void quorum_report_bad(QuorumAIOCB *acb, char *node_name, int ret) { QObject *data; assert(node_name); - data = qobject_from_jsonf("{ 'ret': %d" - ", 'node-name': %s" + data = qobject_from_jsonf("{ 'node-name': %s" ", 'sector-num': %" PRId64 ", 'sectors-count': %d }", - ret, node_name, acb->sector_num, acb->nb_sectors); + node_name, acb->sector_num, acb->nb_sectors); + if (ret < 0) { + QDict *dict = qobject_to_qdict(data); + qdict_put(dict, "error", qstring_from_str(strerror(-ret))); + } monitor_protocol_event(QEVENT_QUORUM_REPORT_BAD, data); qobject_decref(data); } diff --git a/block/vmdk.c b/block/vmdk.c index 83839f9..b69988d 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1184,7 +1184,7 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs, break; case VMDK_OK: ret = BDRV_BLOCK_DATA; - if (extent->file == bs->file) { + if (extent->file == bs->file && !extent->compressed) { ret |= BDRV_BLOCK_OFFSET_VALID | offset; } @@ -283,6 +283,8 @@ libusb="" usb_redir="" glx="" zlib="yes" +lzo="no" +snappy="no" guest_agent="" guest_agent_with_vss="no" vss_win32_sdk="" @@ -995,6 +997,10 @@ for opt do ;; --disable-zlib-test) zlib="no" ;; + --enable-lzo) lzo="yes" + ;; + --enable-snappy) snappy="yes" + ;; --enable-guest-agent) guest_agent="yes" ;; --disable-guest-agent) guest_agent="no" @@ -1289,6 +1295,8 @@ Advanced options (experts only): --enable-libusb enable libusb (for usb passthrough) --disable-usb-redir disable usb network redirection support --enable-usb-redir enable usb network redirection support + --enable-lzo enable the support of lzo compression library + --enable-snappy enable the support of snappy compression library --disable-guest-agent disable building of the QEMU Guest Agent --enable-guest-agent enable building of the QEMU Guest Agent --with-vss-sdk=SDK-path enable Windows VSS support in QEMU Guest Agent @@ -1660,6 +1668,42 @@ fi LIBS="$LIBS -lz" ########################################## +# lzo check + +if test "$lzo" != "no" ; then + cat > $TMPC << EOF +#include <lzo/lzo1x.h> +int main(void) { lzo_version(); return 0; } +EOF + if compile_prog "" "-llzo2" ; then + : + else + error_exit "lzo check failed" \ + "Make sure to have the lzo libs and headers installed." + fi + + libs_softmmu="$libs_softmmu -llzo2" +fi + +########################################## +# snappy check + +if test "$snappy" != "no" ; then + cat > $TMPC << EOF +#include <snappy-c.h> +int main(void) { snappy_max_compressed_length(4096); return 0; } +EOF + if compile_prog "" "-lsnappy" ; then + : + else + error_exit "snappy check failed" \ + "Make sure to have the snappy libs and headers installed." + fi + + libs_softmmu="$libs_softmmu -lsnappy" +fi + +########################################## # libseccomp check if test "$seccomp" != "no" ; then @@ -4045,6 +4089,8 @@ echo "TPM passthrough $tpm_passthrough" echo "QOM debugging $qom_cast_debug" echo "vhdx $vhdx" echo "Quorum $quorum" +echo "lzo support $lzo" +echo "snappy support $snappy" if test "$sdl_too_old" = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -4368,6 +4414,14 @@ if test "$glx" = "yes" ; then echo "GLX_LIBS=$glx_libs" >> $config_host_mak fi +if test "$lzo" = "yes" ; then + echo "CONFIG_LZO=y" >> $config_host_mak +fi + +if test "$snappy" = "yes" ; then + echo "CONFIG_SNAPPY=y" >> $config_host_mak +fi + if test "$libiscsi" = "yes" ; then echo "CONFIG_LIBISCSI=m" >> $config_host_mak if test "$libiscsi_version" = "1.4.0"; then diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak index 81fbc68..d843dc0 100644 --- a/default-configs/s390x-softmmu.mak +++ b/default-configs/s390x-softmmu.mak @@ -1,2 +1,3 @@ CONFIG_VIRTIO=y CONFIG_SCLPCONSOLE=y +CONFIG_S390_FLIC=$(CONFIG_KVM) diff --git a/default-configs/sparc-softmmu.mak b/default-configs/sparc-softmmu.mak index 8fc93dd..ab796b3 100644 --- a/default-configs/sparc-softmmu.mak +++ b/default-configs/sparc-softmmu.mak @@ -10,6 +10,7 @@ CONFIG_EMPTY_SLOT=y CONFIG_PCNET_COMMON=y CONFIG_LANCE=y CONFIG_TCX=y +CONFIG_CG3=y CONFIG_SLAVIO=y CONFIG_CS4231=y CONFIG_GRLIB=y diff --git a/docs/qmp/qmp-events.txt b/docs/qmp/qmp-events.txt index 00f9515..145402e 100644 --- a/docs/qmp/qmp-events.txt +++ b/docs/qmp/qmp-events.txt @@ -225,6 +225,45 @@ Data: "timestamp": { "seconds": 1368697518, "microseconds": 326866 } } } +QUORUM_FAILURE +-------------- + +Emitted by the Quorum block driver if it fails to establish a quorum. + +Data: + +- "reference": device name if defined else node name. +- "sector-num": Number of the first sector of the failed read operation. +- "sector-count": Failed read operation sector count. + +Example: + +{ "event": "QUORUM_FAILURE", + "data": { "reference": "usr1", "sector-num": 345435, "sector-count": 5 }, + "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } + +QUORUM_REPORT_BAD +----------------- + +Emitted to report a corruption of a Quorum file. + +Data: + +- "error": Error message (json-string, optional) + Only present on failure. This field contains a human-readable + error message. There are no semantics other than that the + block layer reported an error and clients should not try to + interpret the error string. +- "node-name": The graph node name of the block driver state. +- "sector-num": Number of the first sector of the failed read operation. +- "sector-count": Failed read operation sector count. + +Example: + +{ "event": "QUORUM_REPORT_BAD", + "data": { "node-name": "1.raw", "sector-num": 345435, "sector-count": 5 }, + "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } + RESET ----- @@ -500,39 +539,3 @@ Example: Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is followed respectively by the RESET, SHUTDOWN, or STOP events. - -QUORUM_FAILURE --------------- - -Emitted by the Quorum block driver if it fails to establish a quorum. - -Data: - -- "reference": device name if defined else node name. -- "sector-num": Number of the first sector of the failed read operation. -- "sector-count": Failed read operation sector count. - -Example: - -{ "event": "QUORUM_FAILURE", - "data": { "reference": "usr1", "sector-num": 345435, "sector-count": 5 }, - "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } - -QUORUM_REPORT_BAD ------------------ - -Emitted to report a corruption of a Quorum file. - -Data: - -- "ret": The IO return code. -- "node-name": The graph node name of the block driver state. -- "sector-num": Number of the first sector of the failed read operation. -- "sector-count": Failed read operation sector count. - -Example: - -{ "event": "QUORUM_REPORT_BAD", - "data": { "ret": 0, "node-name": "1.raw", "sector-num": 345435, - "sector-count": 5 }, - "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } @@ -25,6 +25,17 @@ #include "qapi/error.h" #include "qmp-commands.h" +#include <zlib.h> +#ifdef CONFIG_LZO +#include <lzo/lzo1x.h> +#endif +#ifdef CONFIG_SNAPPY +#include <snappy-c.h> +#endif +#ifndef ELF_MACHINE_UNAME +#define ELF_MACHINE_UNAME "Unknown" +#endif + static uint16_t cpu_convert_to_target16(uint16_t val, int endian) { if (endian == ELFDATA2LSB) { @@ -76,6 +87,19 @@ typedef struct DumpState { int64_t begin; int64_t length; Error **errp; + + uint8_t *note_buf; /* buffer for notes */ + size_t note_buf_offset; /* the writing place in note_buf */ + uint32_t nr_cpus; /* number of guest's cpu */ + size_t page_size; /* guest's page size */ + uint32_t page_shift; /* guest's page shift */ + uint64_t max_mapnr; /* the biggest guest's phys-mem's number */ + size_t len_dump_bitmap; /* the size of the place used to store + dump_bitmap in vmcore */ + off_t offset_dump_bitmap; /* offset of dump_bitmap part in vmcore */ + off_t offset_page; /* offset of page part in vmcore */ + size_t num_dumpable; /* number of page that can be dumped */ + uint32_t flag_compress; /* indicate the compression format */ } DumpState; static int dump_cleanup(DumpState *s) @@ -99,7 +123,7 @@ static void dump_error(DumpState *s, const char *reason) dump_cleanup(s); } -static int fd_write_vmcore(void *buf, size_t size, void *opaque) +static int fd_write_vmcore(const void *buf, size_t size, void *opaque) { DumpState *s = opaque; size_t written_size; @@ -271,7 +295,7 @@ static inline int cpu_index(CPUState *cpu) return cpu->cpu_index + 1; } -static int write_elf64_notes(DumpState *s) +static int write_elf64_notes(WriteCoreDumpFunction f, DumpState *s) { CPUState *cpu; int ret; @@ -279,7 +303,7 @@ static int write_elf64_notes(DumpState *s) CPU_FOREACH(cpu) { id = cpu_index(cpu); - ret = cpu_write_elf64_note(fd_write_vmcore, cpu, id, s); + ret = cpu_write_elf64_note(f, cpu, id, s); if (ret < 0) { dump_error(s, "dump: failed to write elf notes.\n"); return -1; @@ -287,7 +311,7 @@ static int write_elf64_notes(DumpState *s) } CPU_FOREACH(cpu) { - ret = cpu_write_elf64_qemunote(fd_write_vmcore, cpu, s); + ret = cpu_write_elf64_qemunote(f, cpu, s); if (ret < 0) { dump_error(s, "dump: failed to write CPU status.\n"); return -1; @@ -321,7 +345,7 @@ static int write_elf32_note(DumpState *s) return 0; } -static int write_elf32_notes(DumpState *s) +static int write_elf32_notes(WriteCoreDumpFunction f, DumpState *s) { CPUState *cpu; int ret; @@ -329,7 +353,7 @@ static int write_elf32_notes(DumpState *s) CPU_FOREACH(cpu) { id = cpu_index(cpu); - ret = cpu_write_elf32_note(fd_write_vmcore, cpu, id, s); + ret = cpu_write_elf32_note(f, cpu, id, s); if (ret < 0) { dump_error(s, "dump: failed to write elf notes.\n"); return -1; @@ -337,7 +361,7 @@ static int write_elf32_notes(DumpState *s) } CPU_FOREACH(cpu) { - ret = cpu_write_elf32_qemunote(fd_write_vmcore, cpu, s); + ret = cpu_write_elf32_qemunote(f, cpu, s); if (ret < 0) { dump_error(s, "dump: failed to write CPU status.\n"); return -1; @@ -574,7 +598,7 @@ static int dump_begin(DumpState *s) } /* write notes to vmcore */ - if (write_elf64_notes(s) < 0) { + if (write_elf64_notes(fd_write_vmcore, s) < 0) { return -1; } @@ -597,7 +621,7 @@ static int dump_begin(DumpState *s) } /* write notes to vmcore */ - if (write_elf32_notes(s) < 0) { + if (write_elf32_notes(fd_write_vmcore, s) < 0) { return -1; } } @@ -686,6 +710,800 @@ static int create_vmcore(DumpState *s) return 0; } +static int write_start_flat_header(int fd) +{ + uint8_t *buf; + MakedumpfileHeader mh; + int ret = 0; + + memset(&mh, 0, sizeof(mh)); + strncpy(mh.signature, MAKEDUMPFILE_SIGNATURE, + strlen(MAKEDUMPFILE_SIGNATURE)); + + mh.type = cpu_to_be64(TYPE_FLAT_HEADER); + mh.version = cpu_to_be64(VERSION_FLAT_HEADER); + + buf = g_malloc0(MAX_SIZE_MDF_HEADER); + memcpy(buf, &mh, sizeof(mh)); + + size_t written_size; + written_size = qemu_write_full(fd, buf, MAX_SIZE_MDF_HEADER); + if (written_size != MAX_SIZE_MDF_HEADER) { + ret = -1; + } + + g_free(buf); + return ret; +} + +static int write_end_flat_header(int fd) +{ + MakedumpfileDataHeader mdh; + + mdh.offset = END_FLAG_FLAT_HEADER; + mdh.buf_size = END_FLAG_FLAT_HEADER; + + size_t written_size; + written_size = qemu_write_full(fd, &mdh, sizeof(mdh)); + if (written_size != sizeof(mdh)) { + return -1; + } + + return 0; +} + +static int write_buffer(int fd, off_t offset, const void *buf, size_t size) +{ + size_t written_size; + MakedumpfileDataHeader mdh; + + mdh.offset = cpu_to_be64(offset); + mdh.buf_size = cpu_to_be64(size); + + written_size = qemu_write_full(fd, &mdh, sizeof(mdh)); + if (written_size != sizeof(mdh)) { + return -1; + } + + written_size = qemu_write_full(fd, buf, size); + if (written_size != size) { + return -1; + } + + return 0; +} + +static int buf_write_note(const void *buf, size_t size, void *opaque) +{ + DumpState *s = opaque; + + /* note_buf is not enough */ + if (s->note_buf_offset + size > s->note_size) { + return -1; + } + + memcpy(s->note_buf + s->note_buf_offset, buf, size); + + s->note_buf_offset += size; + + return 0; +} + +/* write common header, sub header and elf note to vmcore */ +static int create_header32(DumpState *s) +{ + int ret = 0; + DiskDumpHeader32 *dh = NULL; + KdumpSubHeader32 *kh = NULL; + size_t size; + int endian = s->dump_info.d_endian; + uint32_t block_size; + uint32_t sub_hdr_size; + uint32_t bitmap_blocks; + uint32_t status = 0; + uint64_t offset_note; + + /* write common header, the version of kdump-compressed format is 6th */ + size = sizeof(DiskDumpHeader32); + dh = g_malloc0(size); + + strncpy(dh->signature, KDUMP_SIGNATURE, strlen(KDUMP_SIGNATURE)); + dh->header_version = cpu_convert_to_target32(6, endian); + block_size = s->page_size; + dh->block_size = cpu_convert_to_target32(block_size, endian); + sub_hdr_size = sizeof(struct KdumpSubHeader32) + s->note_size; + sub_hdr_size = DIV_ROUND_UP(sub_hdr_size, block_size); + dh->sub_hdr_size = cpu_convert_to_target32(sub_hdr_size, endian); + /* dh->max_mapnr may be truncated, full 64bit is in kh.max_mapnr_64 */ + dh->max_mapnr = cpu_convert_to_target32(MIN(s->max_mapnr, UINT_MAX), + endian); + dh->nr_cpus = cpu_convert_to_target32(s->nr_cpus, endian); + bitmap_blocks = DIV_ROUND_UP(s->len_dump_bitmap, block_size) * 2; + dh->bitmap_blocks = cpu_convert_to_target32(bitmap_blocks, endian); + strncpy(dh->utsname.machine, ELF_MACHINE_UNAME, sizeof(dh->utsname.machine)); + + if (s->flag_compress & DUMP_DH_COMPRESSED_ZLIB) { + status |= DUMP_DH_COMPRESSED_ZLIB; + } +#ifdef CONFIG_LZO + if (s->flag_compress & DUMP_DH_COMPRESSED_LZO) { + status |= DUMP_DH_COMPRESSED_LZO; + } +#endif +#ifdef CONFIG_SNAPPY + if (s->flag_compress & DUMP_DH_COMPRESSED_SNAPPY) { + status |= DUMP_DH_COMPRESSED_SNAPPY; + } +#endif + dh->status = cpu_convert_to_target32(status, endian); + + if (write_buffer(s->fd, 0, dh, size) < 0) { + dump_error(s, "dump: failed to write disk dump header.\n"); + ret = -1; + goto out; + } + + /* write sub header */ + size = sizeof(KdumpSubHeader32); + kh = g_malloc0(size); + + /* 64bit max_mapnr_64 */ + kh->max_mapnr_64 = cpu_convert_to_target64(s->max_mapnr, endian); + kh->phys_base = cpu_convert_to_target32(PHYS_BASE, endian); + kh->dump_level = cpu_convert_to_target32(DUMP_LEVEL, endian); + + offset_note = DISKDUMP_HEADER_BLOCKS * block_size + size; + kh->offset_note = cpu_convert_to_target64(offset_note, endian); + kh->note_size = cpu_convert_to_target32(s->note_size, endian); + + if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS * + block_size, kh, size) < 0) { + dump_error(s, "dump: failed to write kdump sub header.\n"); + ret = -1; + goto out; + } + + /* write note */ + s->note_buf = g_malloc0(s->note_size); + s->note_buf_offset = 0; + + /* use s->note_buf to store notes temporarily */ + if (write_elf32_notes(buf_write_note, s) < 0) { + ret = -1; + goto out; + } + + if (write_buffer(s->fd, offset_note, s->note_buf, + s->note_size) < 0) { + dump_error(s, "dump: failed to write notes"); + ret = -1; + goto out; + } + + /* get offset of dump_bitmap */ + s->offset_dump_bitmap = (DISKDUMP_HEADER_BLOCKS + sub_hdr_size) * + block_size; + + /* get offset of page */ + s->offset_page = (DISKDUMP_HEADER_BLOCKS + sub_hdr_size + bitmap_blocks) * + block_size; + +out: + g_free(dh); + g_free(kh); + g_free(s->note_buf); + + return ret; +} + +/* write common header, sub header and elf note to vmcore */ +static int create_header64(DumpState *s) +{ + int ret = 0; + DiskDumpHeader64 *dh = NULL; + KdumpSubHeader64 *kh = NULL; + size_t size; + int endian = s->dump_info.d_endian; + uint32_t block_size; + uint32_t sub_hdr_size; + uint32_t bitmap_blocks; + uint32_t status = 0; + uint64_t offset_note; + + /* write common header, the version of kdump-compressed format is 6th */ + size = sizeof(DiskDumpHeader64); + dh = g_malloc0(size); + + strncpy(dh->signature, KDUMP_SIGNATURE, strlen(KDUMP_SIGNATURE)); + dh->header_version = cpu_convert_to_target32(6, endian); + block_size = s->page_size; + dh->block_size = cpu_convert_to_target32(block_size, endian); + sub_hdr_size = sizeof(struct KdumpSubHeader64) + s->note_size; + sub_hdr_size = DIV_ROUND_UP(sub_hdr_size, block_size); + dh->sub_hdr_size = cpu_convert_to_target32(sub_hdr_size, endian); + /* dh->max_mapnr may be truncated, full 64bit is in kh.max_mapnr_64 */ + dh->max_mapnr = cpu_convert_to_target32(MIN(s->max_mapnr, UINT_MAX), + endian); + dh->nr_cpus = cpu_convert_to_target32(s->nr_cpus, endian); + bitmap_blocks = DIV_ROUND_UP(s->len_dump_bitmap, block_size) * 2; + dh->bitmap_blocks = cpu_convert_to_target32(bitmap_blocks, endian); + strncpy(dh->utsname.machine, ELF_MACHINE_UNAME, sizeof(dh->utsname.machine)); + + if (s->flag_compress & DUMP_DH_COMPRESSED_ZLIB) { + status |= DUMP_DH_COMPRESSED_ZLIB; + } +#ifdef CONFIG_LZO + if (s->flag_compress & DUMP_DH_COMPRESSED_LZO) { + status |= DUMP_DH_COMPRESSED_LZO; + } +#endif +#ifdef CONFIG_SNAPPY + if (s->flag_compress & DUMP_DH_COMPRESSED_SNAPPY) { + status |= DUMP_DH_COMPRESSED_SNAPPY; + } +#endif + dh->status = cpu_convert_to_target32(status, endian); + + if (write_buffer(s->fd, 0, dh, size) < 0) { + dump_error(s, "dump: failed to write disk dump header.\n"); + ret = -1; + goto out; + } + + /* write sub header */ + size = sizeof(KdumpSubHeader64); + kh = g_malloc0(size); + + /* 64bit max_mapnr_64 */ + kh->max_mapnr_64 = cpu_convert_to_target64(s->max_mapnr, endian); + kh->phys_base = cpu_convert_to_target64(PHYS_BASE, endian); + kh->dump_level = cpu_convert_to_target32(DUMP_LEVEL, endian); + + offset_note = DISKDUMP_HEADER_BLOCKS * block_size + size; + kh->offset_note = cpu_convert_to_target64(offset_note, endian); + kh->note_size = cpu_convert_to_target64(s->note_size, endian); + + if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS * + block_size, kh, size) < 0) { + dump_error(s, "dump: failed to write kdump sub header.\n"); + ret = -1; + goto out; + } + + /* write note */ + s->note_buf = g_malloc0(s->note_size); + s->note_buf_offset = 0; + + /* use s->note_buf to store notes temporarily */ + if (write_elf64_notes(buf_write_note, s) < 0) { + ret = -1; + goto out; + } + + if (write_buffer(s->fd, offset_note, s->note_buf, + s->note_size) < 0) { + dump_error(s, "dump: failed to write notes"); + ret = -1; + goto out; + } + + /* get offset of dump_bitmap */ + s->offset_dump_bitmap = (DISKDUMP_HEADER_BLOCKS + sub_hdr_size) * + block_size; + + /* get offset of page */ + s->offset_page = (DISKDUMP_HEADER_BLOCKS + sub_hdr_size + bitmap_blocks) * + block_size; + +out: + g_free(dh); + g_free(kh); + g_free(s->note_buf); + + return ret; +} + +static int write_dump_header(DumpState *s) +{ + if (s->dump_info.d_machine == EM_386) { + return create_header32(s); + } else { + return create_header64(s); + } +} + +/* + * set dump_bitmap sequencely. the bit before last_pfn is not allowed to be + * rewritten, so if need to set the first bit, set last_pfn and pfn to 0. + * set_dump_bitmap will always leave the recently set bit un-sync. And setting + * (last bit + sizeof(buf) * 8) to 0 will do flushing the content in buf into + * vmcore, ie. synchronizing un-sync bit into vmcore. + */ +static int set_dump_bitmap(uint64_t last_pfn, uint64_t pfn, bool value, + uint8_t *buf, DumpState *s) +{ + off_t old_offset, new_offset; + off_t offset_bitmap1, offset_bitmap2; + uint32_t byte, bit; + + /* should not set the previous place */ + assert(last_pfn <= pfn); + + /* + * if the bit needed to be set is not cached in buf, flush the data in buf + * to vmcore firstly. + * making new_offset be bigger than old_offset can also sync remained data + * into vmcore. + */ + old_offset = BUFSIZE_BITMAP * (last_pfn / PFN_BUFBITMAP); + new_offset = BUFSIZE_BITMAP * (pfn / PFN_BUFBITMAP); + + while (old_offset < new_offset) { + /* calculate the offset and write dump_bitmap */ + offset_bitmap1 = s->offset_dump_bitmap + old_offset; + if (write_buffer(s->fd, offset_bitmap1, buf, + BUFSIZE_BITMAP) < 0) { + return -1; + } + + /* dump level 1 is chosen, so 1st and 2nd bitmap are same */ + offset_bitmap2 = s->offset_dump_bitmap + s->len_dump_bitmap + + old_offset; + if (write_buffer(s->fd, offset_bitmap2, buf, + BUFSIZE_BITMAP) < 0) { + return -1; + } + + memset(buf, 0, BUFSIZE_BITMAP); + old_offset += BUFSIZE_BITMAP; + } + + /* get the exact place of the bit in the buf, and set it */ + byte = (pfn % PFN_BUFBITMAP) / CHAR_BIT; + bit = (pfn % PFN_BUFBITMAP) % CHAR_BIT; + if (value) { + buf[byte] |= 1u << bit; + } else { + buf[byte] &= ~(1u << bit); + } + + return 0; +} + +/* + * exam every page and return the page frame number and the address of the page. + * bufptr can be NULL. note: the blocks here is supposed to reflect guest-phys + * blocks, so block->target_start and block->target_end should be interal + * multiples of the target page size. + */ +static bool get_next_page(GuestPhysBlock **blockptr, uint64_t *pfnptr, + uint8_t **bufptr, DumpState *s) +{ + GuestPhysBlock *block = *blockptr; + hwaddr addr; + uint8_t *buf; + + /* block == NULL means the start of the iteration */ + if (!block) { + block = QTAILQ_FIRST(&s->guest_phys_blocks.head); + *blockptr = block; + assert(block->target_start % s->page_size == 0); + assert(block->target_end % s->page_size == 0); + *pfnptr = paddr_to_pfn(block->target_start, s->page_shift); + if (bufptr) { + *bufptr = block->host_addr; + } + return true; + } + + *pfnptr = *pfnptr + 1; + addr = pfn_to_paddr(*pfnptr, s->page_shift); + + if ((addr >= block->target_start) && + (addr + s->page_size <= block->target_end)) { + buf = block->host_addr + (addr - block->target_start); + } else { + /* the next page is in the next block */ + block = QTAILQ_NEXT(block, next); + *blockptr = block; + if (!block) { + return false; + } + assert(block->target_start % s->page_size == 0); + assert(block->target_end % s->page_size == 0); + *pfnptr = paddr_to_pfn(block->target_start, s->page_shift); + buf = block->host_addr; + } + + if (bufptr) { + *bufptr = buf; + } + + return true; +} + +static int write_dump_bitmap(DumpState *s) +{ + int ret = 0; + uint64_t last_pfn, pfn; + void *dump_bitmap_buf; + size_t num_dumpable; + GuestPhysBlock *block_iter = NULL; + + /* dump_bitmap_buf is used to store dump_bitmap temporarily */ + dump_bitmap_buf = g_malloc0(BUFSIZE_BITMAP); + + num_dumpable = 0; + last_pfn = 0; + + /* + * exam memory page by page, and set the bit in dump_bitmap corresponded + * to the existing page. + */ + while (get_next_page(&block_iter, &pfn, NULL, s)) { + ret = set_dump_bitmap(last_pfn, pfn, true, dump_bitmap_buf, s); + if (ret < 0) { + dump_error(s, "dump: failed to set dump_bitmap.\n"); + ret = -1; + goto out; + } + + last_pfn = pfn; + num_dumpable++; + } + + /* + * set_dump_bitmap will always leave the recently set bit un-sync. Here we + * set last_pfn + PFN_BUFBITMAP to 0 and those set but un-sync bit will be + * synchronized into vmcore. + */ + if (num_dumpable > 0) { + ret = set_dump_bitmap(last_pfn, last_pfn + PFN_BUFBITMAP, false, + dump_bitmap_buf, s); + if (ret < 0) { + dump_error(s, "dump: failed to sync dump_bitmap.\n"); + ret = -1; + goto out; + } + } + + /* number of dumpable pages that will be dumped later */ + s->num_dumpable = num_dumpable; + +out: + g_free(dump_bitmap_buf); + + return ret; +} + +static void prepare_data_cache(DataCache *data_cache, DumpState *s, + off_t offset) +{ + data_cache->fd = s->fd; + data_cache->data_size = 0; + data_cache->buf_size = BUFSIZE_DATA_CACHE; + data_cache->buf = g_malloc0(BUFSIZE_DATA_CACHE); + data_cache->offset = offset; +} + +static int write_cache(DataCache *dc, const void *buf, size_t size, + bool flag_sync) +{ + /* + * dc->buf_size should not be less than size, otherwise dc will never be + * enough + */ + assert(size <= dc->buf_size); + + /* + * if flag_sync is set, synchronize data in dc->buf into vmcore. + * otherwise check if the space is enough for caching data in buf, if not, + * write the data in dc->buf to dc->fd and reset dc->buf + */ + if ((!flag_sync && dc->data_size + size > dc->buf_size) || + (flag_sync && dc->data_size > 0)) { + if (write_buffer(dc->fd, dc->offset, dc->buf, dc->data_size) < 0) { + return -1; + } + + dc->offset += dc->data_size; + dc->data_size = 0; + } + + if (!flag_sync) { + memcpy(dc->buf + dc->data_size, buf, size); + dc->data_size += size; + } + + return 0; +} + +static void free_data_cache(DataCache *data_cache) +{ + g_free(data_cache->buf); +} + +static size_t get_len_buf_out(size_t page_size, uint32_t flag_compress) +{ + size_t len_buf_out_zlib, len_buf_out_lzo, len_buf_out_snappy; + size_t len_buf_out; + + /* init buf_out */ + len_buf_out_zlib = len_buf_out_lzo = len_buf_out_snappy = 0; + + /* buf size for zlib */ + len_buf_out_zlib = compressBound(page_size); + + /* buf size for lzo */ +#ifdef CONFIG_LZO + if (flag_compress & DUMP_DH_COMPRESSED_LZO) { + if (lzo_init() != LZO_E_OK) { + /* return 0 to indicate lzo is unavailable */ + return 0; + } + } + + /* + * LZO will expand incompressible data by a little amount. please check the + * following URL to see the expansion calculation: + * http://www.oberhumer.com/opensource/lzo/lzofaq.php + */ + len_buf_out_lzo = page_size + page_size / 16 + 64 + 3; +#endif + +#ifdef CONFIG_SNAPPY + /* buf size for snappy */ + len_buf_out_snappy = snappy_max_compressed_length(page_size); +#endif + + /* get the biggest that can store all kinds of compressed page */ + len_buf_out = MAX(len_buf_out_zlib, + MAX(len_buf_out_lzo, len_buf_out_snappy)); + + return len_buf_out; +} + +/* + * check if the page is all 0 + */ +static inline bool is_zero_page(const uint8_t *buf, size_t page_size) +{ + return buffer_is_zero(buf, page_size); +} + +static int write_dump_pages(DumpState *s) +{ + int ret = 0; + DataCache page_desc, page_data; + size_t len_buf_out, size_out; +#ifdef CONFIG_LZO + lzo_bytep wrkmem = NULL; +#endif + uint8_t *buf_out = NULL; + off_t offset_desc, offset_data; + PageDescriptor pd, pd_zero; + uint8_t *buf; + int endian = s->dump_info.d_endian; + GuestPhysBlock *block_iter = NULL; + uint64_t pfn_iter; + + /* get offset of page_desc and page_data in dump file */ + offset_desc = s->offset_page; + offset_data = offset_desc + sizeof(PageDescriptor) * s->num_dumpable; + + prepare_data_cache(&page_desc, s, offset_desc); + prepare_data_cache(&page_data, s, offset_data); + + /* prepare buffer to store compressed data */ + len_buf_out = get_len_buf_out(s->page_size, s->flag_compress); + if (len_buf_out == 0) { + dump_error(s, "dump: failed to get length of output buffer.\n"); + goto out; + } + +#ifdef CONFIG_LZO + wrkmem = g_malloc(LZO1X_1_MEM_COMPRESS); +#endif + + buf_out = g_malloc(len_buf_out); + + /* + * init zero page's page_desc and page_data, because every zero page + * uses the same page_data + */ + pd_zero.size = cpu_convert_to_target32(s->page_size, endian); + pd_zero.flags = cpu_convert_to_target32(0, endian); + pd_zero.offset = cpu_convert_to_target64(offset_data, endian); + pd_zero.page_flags = cpu_convert_to_target64(0, endian); + buf = g_malloc0(s->page_size); + ret = write_cache(&page_data, buf, s->page_size, false); + g_free(buf); + if (ret < 0) { + dump_error(s, "dump: failed to write page data(zero page).\n"); + goto out; + } + + offset_data += s->page_size; + + /* + * dump memory to vmcore page by page. zero page will all be resided in the + * first page of page section + */ + while (get_next_page(&block_iter, &pfn_iter, &buf, s)) { + /* check zero page */ + if (is_zero_page(buf, s->page_size)) { + ret = write_cache(&page_desc, &pd_zero, sizeof(PageDescriptor), + false); + if (ret < 0) { + dump_error(s, "dump: failed to write page desc.\n"); + goto out; + } + } else { + /* + * not zero page, then: + * 1. compress the page + * 2. write the compressed page into the cache of page_data + * 3. get page desc of the compressed page and write it into the + * cache of page_desc + * + * only one compression format will be used here, for + * s->flag_compress is set. But when compression fails to work, + * we fall back to save in plaintext. + */ + size_out = len_buf_out; + if ((s->flag_compress & DUMP_DH_COMPRESSED_ZLIB) && + (compress2(buf_out, (uLongf *)&size_out, buf, s->page_size, + Z_BEST_SPEED) == Z_OK) && (size_out < s->page_size)) { + pd.flags = cpu_convert_to_target32(DUMP_DH_COMPRESSED_ZLIB, + endian); + pd.size = cpu_convert_to_target32(size_out, endian); + + ret = write_cache(&page_data, buf_out, size_out, false); + if (ret < 0) { + dump_error(s, "dump: failed to write page data.\n"); + goto out; + } +#ifdef CONFIG_LZO + } else if ((s->flag_compress & DUMP_DH_COMPRESSED_LZO) && + (lzo1x_1_compress(buf, s->page_size, buf_out, + (lzo_uint *)&size_out, wrkmem) == LZO_E_OK) && + (size_out < s->page_size)) { + pd.flags = cpu_convert_to_target32(DUMP_DH_COMPRESSED_LZO, + endian); + pd.size = cpu_convert_to_target32(size_out, endian); + + ret = write_cache(&page_data, buf_out, size_out, false); + if (ret < 0) { + dump_error(s, "dump: failed to write page data.\n"); + goto out; + } +#endif +#ifdef CONFIG_SNAPPY + } else if ((s->flag_compress & DUMP_DH_COMPRESSED_SNAPPY) && + (snappy_compress((char *)buf, s->page_size, + (char *)buf_out, &size_out) == SNAPPY_OK) && + (size_out < s->page_size)) { + pd.flags = cpu_convert_to_target32( + DUMP_DH_COMPRESSED_SNAPPY, endian); + pd.size = cpu_convert_to_target32(size_out, endian); + + ret = write_cache(&page_data, buf_out, size_out, false); + if (ret < 0) { + dump_error(s, "dump: failed to write page data.\n"); + goto out; + } +#endif + } else { + /* + * fall back to save in plaintext, size_out should be + * assigned to s->page_size + */ + pd.flags = cpu_convert_to_target32(0, endian); + size_out = s->page_size; + pd.size = cpu_convert_to_target32(size_out, endian); + + ret = write_cache(&page_data, buf, s->page_size, false); + if (ret < 0) { + dump_error(s, "dump: failed to write page data.\n"); + goto out; + } + } + + /* get and write page desc here */ + pd.page_flags = cpu_convert_to_target64(0, endian); + pd.offset = cpu_convert_to_target64(offset_data, endian); + offset_data += size_out; + + ret = write_cache(&page_desc, &pd, sizeof(PageDescriptor), false); + if (ret < 0) { + dump_error(s, "dump: failed to write page desc.\n"); + goto out; + } + } + } + + ret = write_cache(&page_desc, NULL, 0, true); + if (ret < 0) { + dump_error(s, "dump: failed to sync cache for page_desc.\n"); + goto out; + } + ret = write_cache(&page_data, NULL, 0, true); + if (ret < 0) { + dump_error(s, "dump: failed to sync cache for page_data.\n"); + goto out; + } + +out: + free_data_cache(&page_desc); + free_data_cache(&page_data); + +#ifdef CONFIG_LZO + g_free(wrkmem); +#endif + + g_free(buf_out); + + return ret; +} + +static int create_kdump_vmcore(DumpState *s) +{ + int ret; + + /* + * the kdump-compressed format is: + * File offset + * +------------------------------------------+ 0x0 + * | main header (struct disk_dump_header) | + * |------------------------------------------+ block 1 + * | sub header (struct kdump_sub_header) | + * |------------------------------------------+ block 2 + * | 1st-dump_bitmap | + * |------------------------------------------+ block 2 + X blocks + * | 2nd-dump_bitmap | (aligned by block) + * |------------------------------------------+ block 2 + 2 * X blocks + * | page desc for pfn 0 (struct page_desc) | (aligned by block) + * | page desc for pfn 1 (struct page_desc) | + * | : | + * |------------------------------------------| (not aligned by block) + * | page data (pfn 0) | + * | page data (pfn 1) | + * | : | + * +------------------------------------------+ + */ + + ret = write_start_flat_header(s->fd); + if (ret < 0) { + dump_error(s, "dump: failed to write start flat header.\n"); + return -1; + } + + ret = write_dump_header(s); + if (ret < 0) { + return -1; + } + + ret = write_dump_bitmap(s); + if (ret < 0) { + return -1; + } + + ret = write_dump_pages(s); + if (ret < 0) { + return -1; + } + + ret = write_end_flat_header(s->fd); + if (ret < 0) { + dump_error(s, "dump: failed to write end flat header.\n"); + return -1; + } + + dump_completed(s); + + return 0; +} + static ram_addr_t get_start_block(DumpState *s) { GuestPhysBlock *block; @@ -714,7 +1532,16 @@ static ram_addr_t get_start_block(DumpState *s) return -1; } -static int dump_init(DumpState *s, int fd, bool paging, bool has_filter, +static void get_max_mapnr(DumpState *s) +{ + GuestPhysBlock *last_block; + + last_block = QTAILQ_LAST(&s->guest_phys_blocks.head, GuestPhysBlockHead); + s->max_mapnr = paddr_to_pfn(last_block->target_end, s->page_shift); +} + +static int dump_init(DumpState *s, int fd, bool has_format, + DumpGuestMemoryFormat format, bool paging, bool has_filter, int64_t begin, int64_t length, Error **errp) { CPUState *cpu; @@ -722,6 +1549,11 @@ static int dump_init(DumpState *s, int fd, bool paging, bool has_filter, Error *err = NULL; int ret; + /* kdump-compressed is conflict with paging and filter */ + if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) { + assert(!paging && !has_filter); + } + if (runstate_is_running()) { vm_stop(RUN_STATE_SAVE_VM); s->resume = true; @@ -782,6 +1614,38 @@ static int dump_init(DumpState *s, int fd, bool paging, bool has_filter, qemu_get_guest_simple_memory_mapping(&s->list, &s->guest_phys_blocks); } + s->nr_cpus = nr_cpus; + s->page_size = TARGET_PAGE_SIZE; + s->page_shift = ffs(s->page_size) - 1; + + get_max_mapnr(s); + + uint64_t tmp; + tmp = DIV_ROUND_UP(DIV_ROUND_UP(s->max_mapnr, CHAR_BIT), s->page_size); + s->len_dump_bitmap = tmp * s->page_size; + + /* init for kdump-compressed format */ + if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) { + switch (format) { + case DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB: + s->flag_compress = DUMP_DH_COMPRESSED_ZLIB; + break; + + case DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO: + s->flag_compress = DUMP_DH_COMPRESSED_LZO; + break; + + case DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY: + s->flag_compress = DUMP_DH_COMPRESSED_SNAPPY; + break; + + default: + s->flag_compress = 0; + } + + return 0; + } + if (s->has_filter) { memory_mapping_filter(&s->list, s->begin, s->length); } @@ -841,14 +1705,25 @@ cleanup: } void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin, - int64_t begin, bool has_length, int64_t length, - Error **errp) + int64_t begin, bool has_length, + int64_t length, bool has_format, + DumpGuestMemoryFormat format, Error **errp) { const char *p; int fd = -1; DumpState *s; int ret; + /* + * kdump-compressed format need the whole memory dumped, so paging or + * filter is not supported here. + */ + if ((has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) && + (paging || has_begin || has_length)) { + error_setg(errp, "kdump-compressed format doesn't support paging or " + "filter"); + return; + } if (has_begin && !has_length) { error_set(errp, QERR_MISSING_PARAMETER, "length"); return; @@ -858,6 +1733,21 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin, return; } + /* check whether lzo/snappy is supported */ +#ifndef CONFIG_LZO + if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO) { + error_setg(errp, "kdump-lzo is not available now"); + return; + } +#endif + +#ifndef CONFIG_SNAPPY + if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY) { + error_setg(errp, "kdump-snappy is not available now"); + return; + } +#endif + #if !defined(WIN32) if (strstart(file, "fd:", &p)) { fd = monitor_get_fd(cur_mon, p, errp); @@ -882,15 +1772,55 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin, s = g_malloc0(sizeof(DumpState)); - ret = dump_init(s, fd, paging, has_begin, begin, length, errp); + ret = dump_init(s, fd, has_format, format, paging, has_begin, + begin, length, errp); if (ret < 0) { g_free(s); return; } - if (create_vmcore(s) < 0 && !error_is_set(s->errp)) { - error_set(errp, QERR_IO_ERROR); + if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) { + if (create_kdump_vmcore(s) < 0 && !error_is_set(s->errp)) { + error_set(errp, QERR_IO_ERROR); + } + } else { + if (create_vmcore(s) < 0 && !error_is_set(s->errp)) { + error_set(errp, QERR_IO_ERROR); + } } g_free(s); } + +DumpGuestMemoryCapability *qmp_query_dump_guest_memory_capability(Error **errp) +{ + DumpGuestMemoryFormatList *item; + DumpGuestMemoryCapability *cap = + g_malloc0(sizeof(DumpGuestMemoryCapability)); + + /* elf is always available */ + item = g_malloc0(sizeof(DumpGuestMemoryFormatList)); + cap->formats = item; + item->value = DUMP_GUEST_MEMORY_FORMAT_ELF; + + /* kdump-zlib is always available */ + item->next = g_malloc0(sizeof(DumpGuestMemoryFormatList)); + item = item->next; + item->value = DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB; + + /* add new item if kdump-lzo is available */ +#ifdef CONFIG_LZO + item->next = g_malloc0(sizeof(DumpGuestMemoryFormatList)); + item = item->next; + item->value = DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO; +#endif + + /* add new item if kdump-snappy is available */ +#ifdef CONFIG_SNAPPY + item->next = g_malloc0(sizeof(DumpGuestMemoryFormatList)); + item = item->next; + item->value = DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY; +#endif + + return cap; +} @@ -1311,8 +1311,11 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) const char *file = qdict_get_str(qdict, "filename"); bool has_begin = qdict_haskey(qdict, "begin"); bool has_length = qdict_haskey(qdict, "length"); + /* kdump-compressed format is not supported for HMP */ + bool has_format = false; int64_t begin = 0; int64_t length = 0; + enum DumpGuestMemoryFormat dump_format = DUMP_GUEST_MEMORY_FORMAT_ELF; char *prot; if (has_begin) { @@ -1325,7 +1328,7 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) prot = g_strconcat("file:", file, NULL); qmp_dump_guest_memory(paging, prot, has_begin, begin, has_length, length, - &errp); + has_format, dump_format, &errp); hmp_handle_error(mon, &errp); g_free(prot); } diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index 540df82..7ed76a9 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -28,6 +28,7 @@ obj-$(CONFIG_OMAP) += omap_lcdc.o obj-$(CONFIG_PXA2XX) += pxa2xx_lcd.o obj-$(CONFIG_SM501) += sm501.o obj-$(CONFIG_TCX) += tcx.o +obj-$(CONFIG_CG3) += cg3.o obj-$(CONFIG_VGA) += vga.o diff --git a/hw/display/cg3.c b/hw/display/cg3.c new file mode 100644 index 0000000..6db8ca3 --- /dev/null +++ b/hw/display/cg3.c @@ -0,0 +1,385 @@ +/* + * QEMU CG3 Frame buffer + * + * Copyright (c) 2012 Bob Breuer + * Copyright (c) 2013 Mark Cave-Ayland + * + * 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. + */ + +#include "qemu-common.h" +#include "qemu/error-report.h" +#include "ui/console.h" +#include "hw/sysbus.h" +#include "hw/loader.h" + +/* Change to 1 to enable debugging */ +#define DEBUG_CG3 0 + +#define CG3_ROM_FILE "QEMU,cgthree.bin" +#define FCODE_MAX_ROM_SIZE 0x10000 + +#define CG3_REG_SIZE 0x20 + +#define CG3_REG_BT458_ADDR 0x0 +#define CG3_REG_BT458_COLMAP 0x4 +#define CG3_REG_FBC_CTRL 0x10 +#define CG3_REG_FBC_STATUS 0x11 +#define CG3_REG_FBC_CURSTART 0x12 +#define CG3_REG_FBC_CUREND 0x13 +#define CG3_REG_FBC_VCTRL 0x14 + +/* Control register flags */ +#define CG3_CR_ENABLE_INTS 0x80 + +/* Status register flags */ +#define CG3_SR_PENDING_INT 0x80 +#define CG3_SR_1152_900_76_B 0x60 +#define CG3_SR_ID_COLOR 0x01 + +#define CG3_VRAM_SIZE 0x100000 +#define CG3_VRAM_OFFSET 0x800000 + +#define DPRINTF(fmt, ...) do { \ + if (DEBUG_CG3) { \ + printf("CG3: " fmt , ## __VA_ARGS__); \ + } \ +} while (0); + +#define TYPE_CG3 "cgthree" +#define CG3(obj) OBJECT_CHECK(CG3State, (obj), TYPE_CG3) + +typedef struct CG3State { + SysBusDevice parent_obj; + + QemuConsole *con; + qemu_irq irq; + hwaddr prom_addr; + MemoryRegion vram_mem; + MemoryRegion rom; + MemoryRegion reg; + uint32_t vram_size; + int full_update; + uint8_t regs[16]; + uint8_t r[256], g[256], b[256]; + uint16_t width, height, depth; + uint8_t dac_index, dac_state; +} CG3State; + +static void cg3_update_display(void *opaque) +{ + CG3State *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->con); + const uint8_t *pix; + uint32_t *data; + uint32_t dval; + int x, y, y_start; + unsigned int width, height; + ram_addr_t page, page_min, page_max; + + if (surface_bits_per_pixel(surface) != 32) { + return; + } + width = s->width; + height = s->height; + + y_start = -1; + page_min = -1; + page_max = 0; + page = 0; + pix = memory_region_get_ram_ptr(&s->vram_mem); + data = (uint32_t *)surface_data(surface); + + for (y = 0; y < height; y++) { + int update = s->full_update; + + page = (y * width) & TARGET_PAGE_MASK; + update |= memory_region_get_dirty(&s->vram_mem, page, page + width, + DIRTY_MEMORY_VGA); + if (update) { + if (y_start < 0) { + y_start = y; + } + if (page < page_min) { + page_min = page; + } + if (page > page_max) { + page_max = page; + } + + for (x = 0; x < width; x++) { + dval = *pix++; + dval = (s->r[dval] << 16) | (s->g[dval] << 8) | s->b[dval]; + *data++ = dval; + } + } else { + if (y_start >= 0) { + dpy_gfx_update(s->con, 0, y_start, s->width, y - y_start); + y_start = -1; + } + pix += width; + data += width; + } + } + s->full_update = 0; + if (y_start >= 0) { + dpy_gfx_update(s->con, 0, y_start, s->width, y - y_start); + } + if (page_max >= page_min) { + memory_region_reset_dirty(&s->vram_mem, + page_min, page_max - page_min + TARGET_PAGE_SIZE, + DIRTY_MEMORY_VGA); + } + /* vsync interrupt? */ + if (s->regs[0] & CG3_CR_ENABLE_INTS) { + s->regs[1] |= CG3_SR_PENDING_INT; + qemu_irq_raise(s->irq); + } +} + +static void cg3_invalidate_display(void *opaque) +{ + CG3State *s = opaque; + + memory_region_set_dirty(&s->vram_mem, 0, CG3_VRAM_SIZE); +} + +static uint64_t cg3_reg_read(void *opaque, hwaddr addr, unsigned size) +{ + CG3State *s = opaque; + int val; + + switch (addr) { + case CG3_REG_BT458_ADDR: + case CG3_REG_BT458_COLMAP: + val = 0; + break; + case CG3_REG_FBC_CTRL: + val = s->regs[0]; + break; + case CG3_REG_FBC_STATUS: + /* monitor ID 6, board type = 1 (color) */ + val = s->regs[1] | CG3_SR_1152_900_76_B | CG3_SR_ID_COLOR; + break; + case CG3_REG_FBC_CURSTART ... CG3_REG_SIZE: + val = s->regs[addr - 0x10]; + break; + default: + qemu_log_mask(LOG_UNIMP, + "cg3: Unimplemented register read " + "reg 0x%" HWADDR_PRIx " size 0x%x\n", + addr, size); + val = 0; + break; + } + DPRINTF("read %02x from reg %" HWADDR_PRIx "\n", val, addr); + return val; +} + +static void cg3_reg_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + CG3State *s = opaque; + uint8_t regval; + int i; + + DPRINTF("write %" PRIx64 " to reg %" HWADDR_PRIx " size %d\n", + val, addr, size); + + switch (addr) { + case CG3_REG_BT458_ADDR: + s->dac_index = val; + s->dac_state = 0; + break; + case CG3_REG_BT458_COLMAP: + /* This register can be written to as either a long word or a byte */ + if (size == 1) { + val <<= 24; + } + + for (i = 0; i < size; i++) { + regval = val >> 24; + + switch (s->dac_state) { + case 0: + s->r[s->dac_index] = regval; + s->dac_state++; + break; + case 1: + s->g[s->dac_index] = regval; + s->dac_state++; + break; + case 2: + s->b[s->dac_index] = regval; + /* Index autoincrement */ + s->dac_index = (s->dac_index + 1) & 0xff; + default: + s->dac_state = 0; + break; + } + val <<= 8; + } + s->full_update = 1; + break; + case CG3_REG_FBC_CTRL: + s->regs[0] = val; + break; + case CG3_REG_FBC_STATUS: + if (s->regs[1] & CG3_SR_PENDING_INT) { + /* clear interrupt */ + s->regs[1] &= ~CG3_SR_PENDING_INT; + qemu_irq_lower(s->irq); + } + break; + case CG3_REG_FBC_CURSTART ... CG3_REG_SIZE: + s->regs[addr - 0x10] = val; + break; + default: + qemu_log_mask(LOG_UNIMP, + "cg3: Unimplemented register write " + "reg 0x%" HWADDR_PRIx " size 0x%x value 0x%" PRIx64 "\n", + addr, size, val); + break; + } +} + +static const MemoryRegionOps cg3_reg_ops = { + .read = cg3_reg_read, + .write = cg3_reg_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static const GraphicHwOps cg3_ops = { + .invalidate = cg3_invalidate_display, + .gfx_update = cg3_update_display, +}; + +static void cg3_realizefn(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + CG3State *s = CG3(dev); + int ret; + char *fcode_filename; + + /* FCode ROM */ + memory_region_init_ram(&s->rom, NULL, "cg3.prom", FCODE_MAX_ROM_SIZE); + vmstate_register_ram_global(&s->rom); + memory_region_set_readonly(&s->rom, true); + sysbus_init_mmio(sbd, &s->rom); + + fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, CG3_ROM_FILE); + if (fcode_filename) { + ret = load_image_targphys(fcode_filename, s->prom_addr, + FCODE_MAX_ROM_SIZE); + if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) { + error_report("cg3: could not load prom '%s'", CG3_ROM_FILE); + } + } + + memory_region_init_io(&s->reg, NULL, &cg3_reg_ops, s, "cg3.reg", + CG3_REG_SIZE); + sysbus_init_mmio(sbd, &s->reg); + + memory_region_init_ram(&s->vram_mem, NULL, "cg3.vram", s->vram_size); + vmstate_register_ram_global(&s->vram_mem); + sysbus_init_mmio(sbd, &s->vram_mem); + + sysbus_init_irq(sbd, &s->irq); + + s->con = graphic_console_init(DEVICE(dev), &cg3_ops, s); + qemu_console_resize(s->con, s->width, s->height); +} + +static int vmstate_cg3_post_load(void *opaque, int version_id) +{ + CG3State *s = opaque; + + cg3_invalidate_display(s); + + return 0; +} + +static const VMStateDescription vmstate_cg3 = { + .name = "cg3", + .version_id = 1, + .minimum_version_id = 1, + .post_load = vmstate_cg3_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT16(height, CG3State), + VMSTATE_UINT16(width, CG3State), + VMSTATE_UINT16(depth, CG3State), + VMSTATE_BUFFER(r, CG3State), + VMSTATE_BUFFER(g, CG3State), + VMSTATE_BUFFER(b, CG3State), + VMSTATE_UINT8(dac_index, CG3State), + VMSTATE_UINT8(dac_state, CG3State), + VMSTATE_END_OF_LIST() + } +}; + +static void cg3_reset(DeviceState *d) +{ + CG3State *s = CG3(d); + + /* Initialize palette */ + memset(s->r, 0, 256); + memset(s->g, 0, 256); + memset(s->b, 0, 256); + + s->dac_state = 0; + s->full_update = 1; + qemu_irq_lower(s->irq); +} + +static Property cg3_properties[] = { + DEFINE_PROP_UINT32("vram-size", CG3State, vram_size, -1), + DEFINE_PROP_UINT16("width", CG3State, width, -1), + DEFINE_PROP_UINT16("height", CG3State, height, -1), + DEFINE_PROP_UINT16("depth", CG3State, depth, -1), + DEFINE_PROP_UINT64("prom-addr", CG3State, prom_addr, -1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void cg3_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = cg3_realizefn; + dc->reset = cg3_reset; + dc->vmsd = &vmstate_cg3; + dc->props = cg3_properties; +} + +static const TypeInfo cg3_info = { + .name = TYPE_CG3, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CG3State), + .class_init = cg3_class_init, +}; + +static void cg3_register_types(void) +{ + type_register_static(&cg3_info); +} + +type_init(cg3_register_types) diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 60eb936..c8a2318 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -25,3 +25,4 @@ obj-$(CONFIG_SH4) += sh_intc.o obj-$(CONFIG_XICS) += xics.o obj-$(CONFIG_XICS_KVM) += xics_kvm.o obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o +obj-$(CONFIG_S390_FLIC) += s390_flic.o diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c new file mode 100644 index 0000000..b2ef3e3 --- /dev/null +++ b/hw/intc/s390_flic.c @@ -0,0 +1,322 @@ +/* + * QEMU S390x KVM floating interrupt controller (flic) + * + * Copyright 2014 IBM Corp. + * Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include <sys/ioctl.h> +#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 "trace.h" + +#define FLIC_SAVE_INITIAL_SIZE getpagesize() +#define FLIC_FAILED (-1UL) +#define FLIC_SAVEVM_VERSION 1 + +void s390_flic_init(void) +{ + DeviceState *dev; + int r; + + if (kvm_enabled()) { + dev = qdev_create(NULL, "s390-flic"); + object_property_add_child(qdev_get_machine(), "s390-flic", + OBJECT(dev), NULL); + r = qdev_init(dev); + if (r) { + error_report("flic: couldn't create qdev"); + } + } +} + +/** + * flic_get_all_irqs - store all pending irqs in buffer + * @buf: pointer to buffer which is passed to kernel + * @len: length of buffer + * @flic: pointer to flic device state + * + * Returns: -ENOMEM if buffer is too small, + * -EINVAL if attr.group is invalid, + * -EFAULT if copying to userspace failed, + * on success return number of stored interrupts + */ +static int flic_get_all_irqs(KVMS390FLICState *flic, + void *buf, int len) +{ + struct kvm_device_attr attr = { + .group = KVM_DEV_FLIC_GET_ALL_IRQS, + .addr = (uint64_t) buf, + .attr = len, + }; + int rc; + + rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr); + + return rc == -1 ? -errno : rc; +} + +static void flic_enable_pfault(KVMS390FLICState *flic) +{ + struct kvm_device_attr attr = { + .group = KVM_DEV_FLIC_APF_ENABLE, + }; + int rc; + + rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); + + if (rc) { + fprintf(stderr, "flic: couldn't enable pfault\n"); + } +} + +static void flic_disable_wait_pfault(KVMS390FLICState *flic) +{ + struct kvm_device_attr attr = { + .group = KVM_DEV_FLIC_APF_DISABLE_WAIT, + }; + int rc; + + rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); + + if (rc) { + fprintf(stderr, "flic: couldn't disable pfault\n"); + } +} + +/** flic_enqueue_irqs - returns 0 on success + * @buf: pointer to buffer which is passed to kernel + * @len: length of buffer + * @flic: pointer to flic device state + * + * Returns: -EINVAL if attr.group is unknown + */ +static int flic_enqueue_irqs(void *buf, uint64_t len, + KVMS390FLICState *flic) +{ + int rc; + struct kvm_device_attr attr = { + .group = KVM_DEV_FLIC_ENQUEUE, + .addr = (uint64_t) buf, + .attr = len, + }; + + rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); + + return rc ? -errno : 0; +} + +/** + * __get_all_irqs - store all pending irqs in buffer + * @flic: pointer to flic device state + * @buf: pointer to pointer to a buffer + * @len: length of buffer + * + * Returns: return value of flic_get_all_irqs + * Note: Retry and increase buffer size until flic_get_all_irqs + * either returns a value >= 0 or a negative error code. + * -ENOMEM is an exception, which means the buffer is too small + * and we should try again. Other negative error codes can be + * -EFAULT and -EINVAL which we ignore at this point + */ +static int __get_all_irqs(KVMS390FLICState *flic, + void **buf, int len) +{ + int r; + + do { + /* returns -ENOMEM if buffer is too small and number + * of queued interrupts on success */ + r = flic_get_all_irqs(flic, *buf, len); + if (r >= 0) { + break; + } + len *= 2; + *buf = g_try_realloc(*buf, len); + if (!buf) { + return -ENOMEM; + } + } while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER); + + return r; +} + +/** + * kvm_flic_save - Save pending floating interrupts + * @f: QEMUFile containing migration state + * @opaque: pointer to flic device state + * + * Note: Pass buf and len to kernel. Start with one page and + * increase until buffer is sufficient or maxium size is + * reached + */ +static void kvm_flic_save(QEMUFile *f, void *opaque) +{ + KVMS390FLICState *flic = opaque; + int len = FLIC_SAVE_INITIAL_SIZE; + void *buf; + int count; + + flic_disable_wait_pfault((struct KVMS390FLICState *) opaque); + + buf = g_try_malloc0(len); + if (!buf) { + /* Storing FLIC_FAILED into the count field here will cause the + * target system to fail when attempting to load irqs from the + * migration state */ + error_report("flic: couldn't allocate memory"); + qemu_put_be64(f, FLIC_FAILED); + return; + } + + count = __get_all_irqs(flic, &buf, len); + if (count < 0) { + error_report("flic: couldn't retrieve irqs from kernel, rc %d", + count); + /* Storing FLIC_FAILED into the count field here will cause the + * target system to fail when attempting to load irqs from the + * migration state */ + qemu_put_be64(f, FLIC_FAILED); + } else { + qemu_put_be64(f, count); + qemu_put_buffer(f, (uint8_t *) buf, + count * sizeof(struct kvm_s390_irq)); + } + g_free(buf); +} + +/** + * kvm_flic_load - Load pending floating interrupts + * @f: QEMUFile containing migration state + * @opaque: pointer to flic device state + * @version_id: version id for migration + * + * Returns: value of flic_enqueue_irqs, -EINVAL on error + * Note: Do nothing when no interrupts where stored + * in QEMUFile + */ +static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id) +{ + uint64_t len = 0; + uint64_t count = 0; + void *buf = NULL; + int r = 0; + + if (version_id != FLIC_SAVEVM_VERSION) { + r = -EINVAL; + goto out; + } + + flic_enable_pfault((struct KVMS390FLICState *) opaque); + + count = qemu_get_be64(f); + len = count * sizeof(struct kvm_s390_irq); + if (count == FLIC_FAILED) { + r = -EINVAL; + goto out; + } + if (count == 0) { + r = 0; + goto out; + } + buf = g_try_malloc0(len); + if (!buf) { + r = -ENOMEM; + goto out; + } + + if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) { + r = -EINVAL; + goto out_free; + } + r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque); + +out_free: + g_free(buf); +out: + return r; +} + +static void kvm_s390_flic_realize(DeviceState *dev, Error **errp) +{ + KVMS390FLICState *flic_state = KVM_S390_FLIC(dev); + struct kvm_create_device cd = {0}; + int ret; + + flic_state->fd = -1; + if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) { + trace_flic_no_device_api(errno); + return; + } + + cd.type = KVM_DEV_TYPE_FLIC; + ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd); + if (ret < 0) { + trace_flic_create_device(errno); + return; + } + flic_state->fd = cd.fd; + + /* Register savevm handler for floating interrupts */ + register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save, + kvm_flic_load, (void *) flic_state); +} + +static void kvm_s390_flic_unrealize(DeviceState *dev, Error **errp) +{ + KVMS390FLICState *flic_state = KVM_S390_FLIC(dev); + + unregister_savevm(DEVICE(flic_state), "s390-flic", flic_state); +} + +static void kvm_s390_flic_reset(DeviceState *dev) +{ + KVMS390FLICState *flic = KVM_S390_FLIC(dev); + struct kvm_device_attr attr = { + .group = KVM_DEV_FLIC_CLEAR_IRQS, + }; + int rc = 0; + + if (flic->fd == -1) { + return; + } + + flic_disable_wait_pfault(flic); + + rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); + if (rc) { + trace_flic_reset_failed(errno); + } + + flic_enable_pfault(flic); +} + +static void kvm_s390_flic_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = kvm_s390_flic_realize; + dc->unrealize = kvm_s390_flic_unrealize; + dc->reset = kvm_s390_flic_reset; +} + +static const TypeInfo kvm_s390_flic_info = { + .name = TYPE_KVM_S390_FLIC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(KVMS390FLICState), + .class_init = kvm_s390_flic_class_init, +}; + +static void kvm_s390_flic_register_types(void) +{ + type_register_static(&kvm_s390_flic_info); +} + +type_init(kvm_s390_flic_register_types) diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index a73c0b9..0777a93 100644 --- a/hw/s390x/event-facility.c +++ b/hw/s390x/event-facility.c @@ -21,13 +21,13 @@ #include "hw/s390x/sclp.h" #include "hw/s390x/event-facility.h" -typedef struct EventTypesBus { +typedef struct SCLPEventsBus { BusState qbus; -} EventTypesBus; +} SCLPEventsBus; struct SCLPEventFacility { - EventTypesBus sbus; - DeviceState *qdev; + SysBusDevice parent_obj; + SCLPEventsBus sbus; /* guest' receive mask */ unsigned int receive_mask; }; @@ -291,7 +291,7 @@ static void sclp_events_bus_class_init(ObjectClass *klass, void *data) { } -static const TypeInfo s390_sclp_events_bus_info = { +static const TypeInfo sclp_events_bus_info = { .name = TYPE_SCLP_EVENTS_BUS, .parent = TYPE_BUS, .class_init = sclp_events_bus_class_init, @@ -299,7 +299,7 @@ static const TypeInfo s390_sclp_events_bus_info = { static void command_handler(SCLPEventFacility *ef, SCCB *sccb, uint64_t code) { - switch (code) { + switch (code & SCLP_CMD_CODE_MASK) { case SCLP_CMD_READ_EVENT_DATA: read_event_data(ef, sccb); break; @@ -315,21 +315,26 @@ static void command_handler(SCLPEventFacility *ef, SCCB *sccb, uint64_t code) } } -static int init_event_facility(S390SCLPDevice *sdev) +static const VMStateDescription vmstate_event_facility = { + .name = "vmstate-event-facility", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(receive_mask, SCLPEventFacility), + VMSTATE_END_OF_LIST() + } +}; + +static int init_event_facility(SCLPEventFacility *event_facility) { - SCLPEventFacility *event_facility; + DeviceState *sdev = DEVICE(event_facility); DeviceState *quiesce; - event_facility = g_malloc0(sizeof(SCLPEventFacility)); - sdev->ef = event_facility; - sdev->sclp_command_handler = command_handler; - sdev->event_pending = event_pending; - - /* Spawn a new sclp-events facility */ + /* Spawn a new bus for SCLP events */ qbus_create_inplace(&event_facility->sbus, sizeof(event_facility->sbus), - TYPE_SCLP_EVENTS_BUS, DEVICE(sdev), NULL); + TYPE_SCLP_EVENTS_BUS, sdev, NULL); event_facility->sbus.qbus.allow_hotplug = 0; - event_facility->qdev = (DeviceState *) sdev; quiesce = qdev_create(&event_facility->sbus.qbus, "sclpquiesce"); if (!quiesce) { @@ -346,43 +351,57 @@ static int init_event_facility(S390SCLPDevice *sdev) static void reset_event_facility(DeviceState *dev) { - S390SCLPDevice *sdev = SCLP_S390_DEVICE(dev); + SCLPEventFacility *sdev = EVENT_FACILITY(dev); - sdev->ef->receive_mask = 0; + sdev->receive_mask = 0; } static void init_event_facility_class(ObjectClass *klass, void *data) { - DeviceClass *dc = DEVICE_CLASS(klass); - S390SCLPDeviceClass *k = SCLP_S390_DEVICE_CLASS(klass); + SysBusDeviceClass *sbdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(sbdc); + SCLPEventFacilityClass *k = EVENT_FACILITY_CLASS(dc); dc->reset = reset_event_facility; + dc->vmsd = &vmstate_event_facility; k->init = init_event_facility; + k->command_handler = command_handler; + k->event_pending = event_pending; } -static const TypeInfo s390_sclp_event_facility_info = { - .name = "s390-sclp-event-facility", - .parent = TYPE_DEVICE_S390_SCLP, - .instance_size = sizeof(S390SCLPDevice), +static const TypeInfo sclp_event_facility_info = { + .name = TYPE_SCLP_EVENT_FACILITY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SCLPEventFacility), .class_init = init_event_facility_class, + .class_size = sizeof(SCLPEventFacilityClass), }; -static int event_qdev_init(DeviceState *qdev) +static void event_realize(DeviceState *qdev, Error **errp) { - SCLPEvent *event = DO_UPCAST(SCLPEvent, qdev, qdev); + SCLPEvent *event = SCLP_EVENT(qdev); SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event); - return child->init(event); + if (child->init) { + int rc = child->init(event); + if (rc < 0) { + error_setg(errp, "SCLP event initialization failed."); + return; + } + } } -static int event_qdev_exit(DeviceState *qdev) +static void event_unrealize(DeviceState *qdev, Error **errp) { - SCLPEvent *event = DO_UPCAST(SCLPEvent, qdev, qdev); + SCLPEvent *event = SCLP_EVENT(qdev); SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event); if (child->exit) { - child->exit(event); + int rc = child->exit(event); + if (rc < 0) { + error_setg(errp, "SCLP event exit failed."); + return; + } } - return 0; } static void event_class_init(ObjectClass *klass, void *data) @@ -391,11 +410,11 @@ static void event_class_init(ObjectClass *klass, void *data) dc->bus_type = TYPE_SCLP_EVENTS_BUS; dc->unplug = qdev_simple_unplug_cb; - dc->init = event_qdev_init; - dc->exit = event_qdev_exit; + dc->realize = event_realize; + dc->unrealize = event_unrealize; } -static const TypeInfo s390_sclp_event_type_info = { +static const TypeInfo sclp_event_type_info = { .name = TYPE_SCLP_EVENT, .parent = TYPE_DEVICE, .instance_size = sizeof(SCLPEvent), @@ -406,9 +425,9 @@ static const TypeInfo s390_sclp_event_type_info = { static void register_types(void) { - type_register_static(&s390_sclp_events_bus_info); - type_register_static(&s390_sclp_event_facility_info); - type_register_static(&s390_sclp_event_type_info); + type_register_static(&sclp_events_bus_info); + type_register_static(&sclp_event_facility_info); + type_register_static(&sclp_event_type_info); } type_init(register_types) diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 1a6397b..04fb1a8 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -95,7 +95,8 @@ static int s390_ipl_init(SysBusDevice *dev) } return 0; } else { - kernel_size = load_elf(ipl->kernel, NULL, NULL, NULL, NULL, + uint64_t pentry = KERN_IMAGE_START; + kernel_size = load_elf(ipl->kernel, NULL, NULL, &pentry, NULL, NULL, 1, ELF_MACHINE, 0); if (kernel_size == -1) { kernel_size = load_image_targphys(ipl->kernel, 0, ram_size); @@ -104,15 +105,19 @@ static int s390_ipl_init(SysBusDevice *dev) fprintf(stderr, "could not load kernel '%s'\n", ipl->kernel); return -1; } - /* we have to overwrite values in the kernel image, which are "rom" */ - strcpy(rom_ptr(KERN_PARM_AREA), ipl->cmdline); - /* - * we can not rely on the ELF entry point, since up to 3.2 this - * value was 0x800 (the SALIPL loader) and it wont work. For - * all (Linux) cases 0x10000 (KERN_IMAGE_START) should be fine. + * Is it a Linux kernel (starting at 0x10000)? If yes, we fill in the + * kernel parameters here as well. Note: For old kernels (up to 3.2) + * we can not rely on the ELF entry point - it was 0x800 (the SALIPL + * loader) and it won't work. For this case we force it to 0x10000, too. */ - ipl->start_addr = KERN_IMAGE_START; + if (pentry == KERN_IMAGE_START || pentry == 0x800) { + ipl->start_addr = KERN_IMAGE_START; + /* Overwrite parameters in the kernel image, which are "rom" */ + strcpy(rom_ptr(KERN_PARM_AREA), ipl->cmdline); + } else { + ipl->start_addr = pentry; + } } if (ipl->initrd) { ram_addr_t initrd_offset; diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 733d988..0d4f6ae 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -13,13 +13,14 @@ #include "exec/address-spaces.h" #include "s390-virtio.h" #include "hw/s390x/sclp.h" +#include "hw/s390x/s390_flic.h" #include "ioinst.h" #include "css.h" #include "virtio-ccw.h" void io_subsystem_reset(void) { - DeviceState *css, *sclp; + DeviceState *css, *sclp, *flic; css = DEVICE(object_resolve_path_type("", "virtual-css-bridge", NULL)); if (css) { @@ -30,6 +31,10 @@ void io_subsystem_reset(void) if (sclp) { qdev_reset_all(sclp); } + flic = DEVICE(object_resolve_path_type("", "s390-flic", NULL)); + if (flic) { + qdev_reset_all(flic); + } } static int virtio_ccw_hcall_notify(const uint64_t *args) @@ -99,6 +104,7 @@ static void ccw_init(QEMUMachineInitArgs *args) s390_sclp_init(); s390_init_ipl_dev(args->kernel_filename, args->kernel_cmdline, args->initrd_filename, "s390-ccw.img"); + s390_flic_init(); /* register hypercalls */ virtio_ccw_register_hcalls(); diff --git a/hw/s390x/s390-virtio-hcall.c b/hw/s390x/s390-virtio-hcall.c index ee62649..c7bdc20 100644 --- a/hw/s390x/s390-virtio-hcall.c +++ b/hw/s390x/s390-virtio-hcall.c @@ -26,11 +26,15 @@ void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn) int s390_virtio_hypercall(CPUS390XState *env) { - s390_virtio_fn fn = s390_diag500_table[env->regs[1]]; - - if (!fn) { - return -EINVAL; + s390_virtio_fn fn; + + if (env->regs[1] < MAX_DIAG_SUBCODES) { + fn = s390_diag500_table[env->regs[1]]; + if (fn) { + env->regs[2] = fn(&env->regs[2]); + return 0; + } } - return fn(&env->regs[2]); + return -EINVAL; } diff --git a/hw/s390x/s390-virtio.c b/hw/s390x/s390-virtio.c index 9eeda97..0f03fd1 100644 --- a/hw/s390x/s390-virtio.c +++ b/hw/s390x/s390-virtio.c @@ -36,6 +36,7 @@ #include "hw/s390x/s390-virtio-bus.h" #include "hw/s390x/sclp.h" +#include "hw/s390x/s390_flic.h" #include "hw/s390x/s390-virtio.h" //#define DEBUG_S390 @@ -251,6 +252,7 @@ static void s390_init(QEMUMachineInitArgs *args) s390_sclp_init(); s390_init_ipl_dev(args->kernel_filename, args->kernel_cmdline, args->initrd_filename, ZIPL_FILENAME); + s390_flic_init(); /* register hypercalls */ s390_virtio_register_hcalls(); diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index 4e0c564..d8ddf35 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -18,11 +18,12 @@ #include "sysemu/sysemu.h" #include "hw/s390x/sclp.h" +#include "hw/s390x/event-facility.h" -static inline S390SCLPDevice *get_event_facility(void) +static inline SCLPEventFacility *get_event_facility(void) { ObjectProperty *op = object_property_find(qdev_get_machine(), - "s390-sclp-event-facility", + TYPE_SCLP_EVENT_FACILITY, NULL); assert(op); return op->opaque; @@ -89,9 +90,10 @@ static void sclp_read_cpu_info(SCCB *sccb) sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); } -static void sclp_execute(SCCB *sccb, uint64_t code) +static void sclp_execute(SCCB *sccb, uint32_t code) { - S390SCLPDevice *sdev = get_event_facility(); + SCLPEventFacility *ef = get_event_facility(); + SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef); switch (code & SCLP_CMD_CODE_MASK) { case SCLP_CMDW_READ_SCP_INFO: @@ -102,12 +104,12 @@ static void sclp_execute(SCCB *sccb, uint64_t code) sclp_read_cpu_info(sccb); break; default: - sdev->sclp_command_handler(sdev->ef, sccb, code); + efc->command_handler(ef, sccb, code); break; } } -int sclp_service_call(uint32_t sccb, uint64_t code) +int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code) { int r = 0; SCCB work_sccb; @@ -115,11 +117,16 @@ int sclp_service_call(uint32_t sccb, uint64_t code) hwaddr sccb_len = sizeof(SCCB); /* first some basic checks on program checks */ + if (env->psw.mask & PSW_MASK_PSTATE) { + r = -PGM_PRIVILEGED; + goto out; + } if (cpu_physical_memory_is_io(sccb)) { r = -PGM_ADDRESSING; goto out; } - if (sccb & ~0x7ffffff8ul) { + if ((sccb & ~0x1fffUL) == 0 || (sccb & ~0x1fffUL) == env->psa + || (sccb & ~0x7ffffff8UL) != 0) { r = -PGM_SPECIFICATION; goto out; } @@ -151,11 +158,13 @@ out: void sclp_service_interrupt(uint32_t sccb) { - S390SCLPDevice *sdev = get_event_facility(); + SCLPEventFacility *ef = get_event_facility(); + SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef); + uint32_t param = sccb & ~3; /* Indicate whether an event is still pending */ - param |= sdev->event_pending(sdev->ef) ? 1 : 0; + param |= efc->event_pending(ef) ? 1 : 0; if (!param) { /* No need to send an interrupt, there's nothing to be notified about */ @@ -168,47 +177,9 @@ void sclp_service_interrupt(uint32_t sccb) void s390_sclp_init(void) { - DeviceState *dev = qdev_create(NULL, "s390-sclp-event-facility"); + DeviceState *dev = qdev_create(NULL, TYPE_SCLP_EVENT_FACILITY); - object_property_add_child(qdev_get_machine(), "s390-sclp-event-facility", + object_property_add_child(qdev_get_machine(), TYPE_SCLP_EVENT_FACILITY, OBJECT(dev), NULL); qdev_init_nofail(dev); } - -static int s390_sclp_dev_init(SysBusDevice *dev) -{ - int r; - S390SCLPDevice *sdev = (S390SCLPDevice *)dev; - S390SCLPDeviceClass *sclp = SCLP_S390_DEVICE_GET_CLASS(dev); - - r = sclp->init(sdev); - if (!r) { - assert(sdev->event_pending); - assert(sdev->sclp_command_handler); - } - - return r; -} - -static void s390_sclp_device_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *dc = SYS_BUS_DEVICE_CLASS(klass); - - dc->init = s390_sclp_dev_init; -} - -static const TypeInfo s390_sclp_device_info = { - .name = TYPE_DEVICE_S390_SCLP, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(S390SCLPDevice), - .class_init = s390_sclp_device_class_init, - .class_size = sizeof(S390SCLPDeviceClass), - .abstract = true, -}; - -static void s390_sclp_register_types(void) -{ - type_register_static(&s390_sclp_device_info); -} - -type_init(s390_sclp_register_types) diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 50b89ad..50a0acf 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -909,7 +909,7 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) case VERIFY_16: if ((buf[1] & 2) == 0) { cmd->xfer = 0; - } else if ((buf[1] & 4) == 1) { + } else if ((buf[1] & 4) != 0) { cmd->xfer = 1; } cmd->xfer *= dev->blocksize; @@ -1367,6 +1367,11 @@ const struct SCSISense sense_code_WRITE_PROTECTED = { .key = DATA_PROTECT, .asc = 0x27, .ascq = 0x00 }; +/* Data Protection, Space Allocation Failed Write Protect */ +const struct SCSISense sense_code_SPACE_ALLOC_FAILED = { + .key = DATA_PROTECT, .asc = 0x27, .ascq = 0x07 +}; + /* * scsi_build_sense * diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index b4fadd2..48a28ae 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -75,6 +75,8 @@ struct SCSIDiskState bool media_event; bool eject_request; uint64_t wwn; + uint64_t port_wwn; + uint16_t port_index; uint64_t max_unmap_size; QEMUBH *bh; char *version; @@ -428,6 +430,9 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error) case EINVAL: scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); break; + case ENOSPC: + scsi_check_condition(r, SENSE_CODE(SPACE_ALLOC_FAILED)); + break; default: scsi_check_condition(r, SENSE_CODE(IO_ERROR)); break; @@ -617,6 +622,24 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) stq_be_p(&outbuf[buflen], s->wwn); buflen += 8; } + + if (s->port_wwn) { + outbuf[buflen++] = 0x61; // SAS / Binary + outbuf[buflen++] = 0x93; // PIV / Target port / NAA + outbuf[buflen++] = 0; // reserved + outbuf[buflen++] = 8; + stq_be_p(&outbuf[buflen], s->port_wwn); + buflen += 8; + } + + if (s->port_index) { + outbuf[buflen++] = 0x61; // SAS / Binary + outbuf[buflen++] = 0x94; // PIV / Target port / relative target port + outbuf[buflen++] = 0; // reserved + outbuf[buflen++] = 4; + stw_be_p(&outbuf[buflen + 2], s->port_index); + buflen += 4; + } break; } case 0xb0: /* block limits */ @@ -2536,6 +2559,8 @@ static Property scsi_hd_properties[] = { DEFINE_PROP_BIT("dpofua", SCSIDiskState, features, SCSI_DISK_F_DPOFUA, false), DEFINE_PROP_UINT64("wwn", SCSIDiskState, wwn, 0), + DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, port_wwn, 0), + DEFINE_PROP_UINT16("port_index", SCSIDiskState, port_index, 0), DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size, DEFAULT_MAX_UNMAP_SIZE), DEFINE_BLOCK_CHS_PROPERTIES(SCSIDiskState, qdev.conf), @@ -2584,6 +2609,8 @@ static const TypeInfo scsi_hd_info = { static Property scsi_cd_properties[] = { DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_PROP_UINT64("wwn", SCSIDiskState, wwn, 0), + DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, port_wwn, 0), + DEFINE_PROP_UINT16("port_index", SCSIDiskState, port_index, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -2647,6 +2674,8 @@ static Property scsi_disk_properties[] = { DEFINE_PROP_BIT("dpofua", SCSIDiskState, features, SCSI_DISK_F_DPOFUA, false), DEFINE_PROP_UINT64("wwn", SCSIDiskState, wwn, 0), + DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, port_wwn, 0), + DEFINE_PROP_UINT16("port_index", SCSIDiskState, port_index, 0), DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size, DEFAULT_MAX_UNMAP_SIZE), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index f08b64e1..8d92e0d 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -37,8 +37,6 @@ do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0) #include <scsi/sg.h> #include "block/scsi.h" -#define SCSI_SENSE_BUF_SIZE 96 - #define SG_ERR_DRIVER_TIMEOUT 0x06 #define SG_ERR_DRIVER_SENSE 0x08 diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index c0c46d7..e8bca39 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -60,7 +60,6 @@ #define VSCSI_MAX_SECTORS 4096 #define VSCSI_REQ_LIMIT 24 -#define SCSI_SENSE_BUF_SIZE 96 #define SRP_RSP_SENSE_DATA_LEN 18 typedef union vscsi_crq { diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 2957d90..75adb68 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "hw/sysbus.h" +#include "qemu/error-report.h" #include "qemu/timer.h" #include "hw/sparc/sun4m.h" #include "hw/timer/m48t59.h" @@ -561,6 +562,31 @@ static void tcx_init(hwaddr addr, int vram_size, int width, } } +static void cg3_init(hwaddr addr, qemu_irq irq, int vram_size, int width, + int height, int depth) +{ + DeviceState *dev; + SysBusDevice *s; + + dev = qdev_create(NULL, "cgthree"); + qdev_prop_set_uint32(dev, "vram-size", vram_size); + qdev_prop_set_uint16(dev, "width", width); + qdev_prop_set_uint16(dev, "height", height); + qdev_prop_set_uint16(dev, "depth", depth); + qdev_prop_set_uint64(dev, "prom-addr", addr); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + + /* FCode ROM */ + sysbus_mmio_map(s, 0, addr); + /* DAC */ + sysbus_mmio_map(s, 1, addr + 0x400000ULL); + /* 8-bit plane */ + sysbus_mmio_map(s, 2, addr + 0x800000ULL); + + sysbus_connect_irq(s, 0, irq); +} + /* NCR89C100/MACIO Internal ID register */ #define TYPE_MACIO_ID_REGISTER "macio_idreg" @@ -914,13 +940,43 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, slavio_irq[16], iommu, &ledma_irq, 1); if (graphic_depth != 8 && graphic_depth != 24) { - fprintf(stderr, "qemu: Unsupported depth: %d\n", graphic_depth); + error_report("Unsupported depth: %d", graphic_depth); exit (1); } num_vsimms = 0; if (num_vsimms == 0) { - tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height, - graphic_depth); + if (vga_interface_type == VGA_CG3) { + if (graphic_depth != 8) { + error_report("Unsupported depth: %d", graphic_depth); + exit(1); + } + + if (!(graphic_width == 1024 && graphic_height == 768) && + !(graphic_width == 1152 && graphic_height == 900)) { + error_report("Unsupported resolution: %d x %d", graphic_width, + graphic_height); + exit(1); + } + + /* sbus irq 5 */ + cg3_init(hwdef->tcx_base, slavio_irq[11], 0x00100000, + graphic_width, graphic_height, graphic_depth); + } else { + /* If no display specified, default to TCX */ + if (graphic_depth != 8 && graphic_depth != 24) { + error_report("Unsupported depth: %d", graphic_depth); + exit(1); + } + + if (!(graphic_width == 1024 && graphic_height == 768)) { + error_report("Unsupported resolution: %d x %d", + graphic_width, graphic_height); + exit(1); + } + + tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height, + graphic_depth); + } } for (i = num_vsimms; i < MAX_VSIMMS; i++) { diff --git a/hw/timer/slavio_timer.c b/hw/timer/slavio_timer.c index f75b914..e4dccea 100644 --- a/hw/timer/slavio_timer.c +++ b/hw/timer/slavio_timer.c @@ -51,7 +51,7 @@ typedef struct CPUTimerState { ptimer_state *timer; uint32_t count, counthigh, reached; /* processor only */ - uint32_t running; + uint32_t run; uint64_t limit; } CPUTimerState; @@ -177,7 +177,7 @@ static uint64_t slavio_timer_mem_readl(void *opaque, hwaddr addr, // only available in processor counter/timer // read start/stop status if (timer_index > 0) { - ret = t->running; + ret = t->run; } else { ret = 0; } @@ -260,16 +260,15 @@ static void slavio_timer_mem_writel(void *opaque, hwaddr addr, case TIMER_STATUS: if (slavio_timer_is_user(tc)) { // start/stop user counter - if ((val & 1) && !t->running) { + if (val & 1) { trace_slavio_timer_mem_writel_status_start(timer_index); ptimer_run(t->timer, 0); - t->running = 1; - } else if (!(val & 1) && t->running) { + } else { trace_slavio_timer_mem_writel_status_stop(timer_index); ptimer_stop(t->timer); - t->running = 0; } } + t->run = val & 1; break; case TIMER_MODE: if (timer_index == 0) { @@ -284,8 +283,9 @@ static void slavio_timer_mem_writel(void *opaque, hwaddr addr, if (val & processor) { // counter -> user timer qemu_irq_lower(curr_timer->irq); // counters are always running - ptimer_stop(curr_timer->timer); - curr_timer->running = 0; + if (!curr_timer->run) { + ptimer_stop(curr_timer->timer); + } // user timer limit is always the same curr_timer->limit = TIMER_MAX_COUNT64; ptimer_set_limit(curr_timer->timer, @@ -296,13 +296,8 @@ static void slavio_timer_mem_writel(void *opaque, hwaddr addr, s->cputimer_mode |= processor; trace_slavio_timer_mem_writel_mode_user(timer_index); } else { // user timer -> counter - // stop the user timer if it is running - if (curr_timer->running) { - ptimer_stop(curr_timer->timer); - } // start the counter ptimer_run(curr_timer->timer, 0); - curr_timer->running = 1; // clear this processors user timer bit in config // register s->cputimer_mode &= ~processor; @@ -340,7 +335,7 @@ static const VMStateDescription vmstate_timer = { VMSTATE_UINT32(count, CPUTimerState), VMSTATE_UINT32(counthigh, CPUTimerState), VMSTATE_UINT32(reached, CPUTimerState), - VMSTATE_UINT32(running, CPUTimerState), + VMSTATE_UINT32(run , CPUTimerState), VMSTATE_PTIMER(timer, CPUTimerState), VMSTATE_END_OF_LIST() } @@ -373,7 +368,7 @@ static void slavio_timer_reset(DeviceState *d) ptimer_set_limit(curr_timer->timer, LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1); ptimer_run(curr_timer->timer, 0); - curr_timer->running = 1; + curr_timer->run = 1; } } s->cputimer_mode = 0; diff --git a/include/hw/nvram/openbios_firmware_abi.h b/include/hw/nvram/openbios_firmware_abi.h index 5e6e5d4..c66ee22 100644 --- a/include/hw/nvram/openbios_firmware_abi.h +++ b/include/hw/nvram/openbios_firmware_abi.h @@ -62,6 +62,8 @@ Sun_init_header(struct Sun_nvram *header, const uint8_t *macaddr, int machine_id header->type = 1; header->machine_id = machine_id & 0xff; memcpy(&header->macaddr, macaddr, 6); + memcpy(&header->hostid , &macaddr[3], 3); + /* Calculate checksum */ tmp = 0; tmpptr = (uint8_t *)header; diff --git a/include/hw/s390x/event-facility.h b/include/hw/s390x/event-facility.h index 870edd4..6a062b6 100644 --- a/include/hw/s390x/event-facility.h +++ b/include/hw/s390x/event-facility.h @@ -176,4 +176,23 @@ typedef struct SCLPEventClass { bool (*can_handle_event)(uint8_t type); } SCLPEventClass; +#define TYPE_SCLP_EVENT_FACILITY "s390-sclp-event-facility" +#define EVENT_FACILITY(obj) \ + OBJECT_CHECK(SCLPEventFacility, (obj), TYPE_SCLP_EVENT_FACILITY) +#define EVENT_FACILITY_CLASS(klass) \ + OBJECT_CLASS_CHECK(SCLPEventFacilityClass, (klass), \ + TYPE_SCLP_EVENT_FACILITY) +#define EVENT_FACILITY_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SCLPEventFacilityClass, (obj), \ + TYPE_SCLP_EVENT_FACILITY) + +typedef struct SCLPEventFacility SCLPEventFacility; + +typedef struct SCLPEventFacilityClass { + DeviceClass parent_class; + int (*init)(SCLPEventFacility *ef); + void (*command_handler)(SCLPEventFacility *ef, SCCB *sccb, uint64_t code); + bool (*event_pending)(SCLPEventFacility *ef); +} SCLPEventFacilityClass; + #endif diff --git a/include/hw/s390x/s390_flic.h b/include/hw/s390x/s390_flic.h new file mode 100644 index 0000000..497b219 --- /dev/null +++ b/include/hw/s390x/s390_flic.h @@ -0,0 +1,33 @@ +/* + * QEMU S390x KVM floating interrupt controller (flic) + * + * Copyright 2014 IBM Corp. + * Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#ifndef __KVM_S390_FLIC_H +#define __KVM_S390_FLIC_H + +#include "hw/sysbus.h" + +#define TYPE_KVM_S390_FLIC "s390-flic" +#define KVM_S390_FLIC(obj) \ + OBJECT_CHECK(KVMS390FLICState, (obj), TYPE_KVM_S390_FLIC) + +typedef struct KVMS390FLICState { + SysBusDevice parent_obj; + + uint32_t fd; +} KVMS390FLICState; + +#ifdef CONFIG_KVM +void s390_flic_init(void); +#else +static inline void s390_flic_init(void) { } +#endif + +#endif /* __KVM_S390_FLIC_H */ diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h index 35112d9..7ef1622 100644 --- a/include/hw/s390x/sclp.h +++ b/include/hw/s390x/sclp.h @@ -161,30 +161,6 @@ static inline int sccb_data_len(SCCB *sccb) return be16_to_cpu(sccb->h.length) - sizeof(sccb->h); } -#define TYPE_DEVICE_S390_SCLP "s390-sclp-device" -#define SCLP_S390_DEVICE(obj) \ - OBJECT_CHECK(S390SCLPDevice, (obj), TYPE_DEVICE_S390_SCLP) -#define SCLP_S390_DEVICE_CLASS(klass) \ - OBJECT_CLASS_CHECK(S390SCLPDeviceClass, (klass), \ - TYPE_DEVICE_S390_SCLP) -#define SCLP_S390_DEVICE_GET_CLASS(obj) \ - OBJECT_GET_CLASS(S390SCLPDeviceClass, (obj), \ - TYPE_DEVICE_S390_SCLP) - -typedef struct SCLPEventFacility SCLPEventFacility; - -typedef struct S390SCLPDevice { - SysBusDevice busdev; - SCLPEventFacility *ef; - void (*sclp_command_handler)(SCLPEventFacility *ef, SCCB *sccb, - uint64_t code); - bool (*event_pending)(SCLPEventFacility *ef); -} S390SCLPDevice; - -typedef struct S390SCLPDeviceClass { - DeviceClass qdev; - int (*init)(S390SCLPDevice *sdev); -} S390SCLPDeviceClass; void s390_sclp_init(void); void sclp_service_interrupt(uint32_t sccb); diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index bf6da3d..e5fc39d 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -31,7 +31,7 @@ typedef struct SCSISense { uint8_t ascq; } SCSISense; -#define SCSI_SENSE_BUF_SIZE 96 +#define SCSI_SENSE_BUF_SIZE 252 struct SCSICommand { uint8_t buf[SCSI_CMD_BUF_SIZE]; @@ -223,6 +223,8 @@ extern const struct SCSISense sense_code_REPORTED_LUNS_CHANGED; extern const struct SCSISense sense_code_DEVICE_INTERNAL_RESET; /* Data Protection, Write Protected */ extern const struct SCSISense sense_code_WRITE_PROTECTED; +/* Data Protection, Space Allocation Failed Write Protect */ +extern const struct SCSISense sense_code_SPACE_ALLOC_FAILED; #define SENSE_CODE(x) sense_code_ ## x diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index 73c67b7..25193c9 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -105,7 +105,7 @@ void qerror_report_err(Error *err); ERROR_CLASS_GENERIC_ERROR, "Device '%s' does not support hotplugging" #define QERR_DEVICE_NOT_ACTIVE \ - ERROR_CLASS_DEVICE_NOT_ACTIVE, "Device '%s' has not been activated" + ERROR_CLASS_DEVICE_NOT_ACTIVE, "No %s device has been activated" #define QERR_DEVICE_NOT_ENCRYPTED \ ERROR_CLASS_GENERIC_ERROR, "Device '%s' is not encrypted" diff --git a/include/qom/cpu.h b/include/qom/cpu.h index 367eda1..d734be8 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -28,7 +28,8 @@ #include "qemu/tls.h" #include "qemu/typedefs.h" -typedef int (*WriteCoreDumpFunction)(void *buf, size_t size, void *opaque); +typedef int (*WriteCoreDumpFunction)(const void *buf, size_t size, + void *opaque); /** * vaddr: diff --git a/include/sysemu/dump.h b/include/sysemu/dump.h index 19fafb2..efab7a3 100644 --- a/include/sysemu/dump.h +++ b/include/sysemu/dump.h @@ -14,12 +14,150 @@ #ifndef DUMP_H #define DUMP_H +#define MAKEDUMPFILE_SIGNATURE "makedumpfile" +#define MAX_SIZE_MDF_HEADER (4096) /* max size of makedumpfile_header */ +#define TYPE_FLAT_HEADER (1) /* type of flattened format */ +#define VERSION_FLAT_HEADER (1) /* version of flattened format */ +#define END_FLAG_FLAT_HEADER (-1) + +#define ARCH_PFN_OFFSET (0) + +#define paddr_to_pfn(X, page_shift) \ + (((unsigned long long)(X) >> (page_shift)) - ARCH_PFN_OFFSET) +#define pfn_to_paddr(X, page_shift) \ + (((unsigned long long)(X) + ARCH_PFN_OFFSET) << (page_shift)) + +/* + * flag for compressed format + */ +#define DUMP_DH_COMPRESSED_ZLIB (0x1) +#define DUMP_DH_COMPRESSED_LZO (0x2) +#define DUMP_DH_COMPRESSED_SNAPPY (0x4) + +#define KDUMP_SIGNATURE "KDUMP " +#define SIG_LEN (sizeof(KDUMP_SIGNATURE) - 1) +#define PHYS_BASE (0) +#define DUMP_LEVEL (1) +#define DISKDUMP_HEADER_BLOCKS (1) +#define BUFSIZE_BITMAP (TARGET_PAGE_SIZE) +#define PFN_BUFBITMAP (CHAR_BIT * BUFSIZE_BITMAP) +#define BUFSIZE_DATA_CACHE (TARGET_PAGE_SIZE * 4) + typedef struct ArchDumpInfo { int d_machine; /* Architecture */ int d_endian; /* ELFDATA2LSB or ELFDATA2MSB */ int d_class; /* ELFCLASS32 or ELFCLASS64 */ } ArchDumpInfo; +typedef struct QEMU_PACKED MakedumpfileHeader { + char signature[16]; /* = "makedumpfile" */ + int64_t type; + int64_t version; +} MakedumpfileHeader; + +typedef struct QEMU_PACKED MakedumpfileDataHeader { + int64_t offset; + int64_t buf_size; +} MakedumpfileDataHeader; + +typedef struct QEMU_PACKED NewUtsname { + char sysname[65]; + char nodename[65]; + char release[65]; + char version[65]; + char machine[65]; + char domainname[65]; +} NewUtsname; + +typedef struct QEMU_PACKED DiskDumpHeader32 { + char signature[SIG_LEN]; /* = "KDUMP " */ + uint32_t header_version; /* Dump header version */ + NewUtsname utsname; /* copy of system_utsname */ + char timestamp[10]; /* Time stamp */ + uint32_t status; /* Above flags */ + uint32_t block_size; /* Size of a block in byte */ + uint32_t sub_hdr_size; /* Size of arch dependent header in block */ + uint32_t bitmap_blocks; /* Size of Memory bitmap in block */ + uint32_t max_mapnr; /* = max_mapnr , + obsoleted in header_version 6 */ + uint32_t total_ram_blocks; /* Number of blocks should be written */ + uint32_t device_blocks; /* Number of total blocks in dump device */ + uint32_t written_blocks; /* Number of written blocks */ + uint32_t current_cpu; /* CPU# which handles dump */ + uint32_t nr_cpus; /* Number of CPUs */ +} DiskDumpHeader32; + +typedef struct QEMU_PACKED DiskDumpHeader64 { + char signature[SIG_LEN]; /* = "KDUMP " */ + uint32_t header_version; /* Dump header version */ + NewUtsname utsname; /* copy of system_utsname */ + char timestamp[22]; /* Time stamp */ + uint32_t status; /* Above flags */ + uint32_t block_size; /* Size of a block in byte */ + uint32_t sub_hdr_size; /* Size of arch dependent header in block */ + uint32_t bitmap_blocks; /* Size of Memory bitmap in block */ + uint32_t max_mapnr; /* = max_mapnr, + obsoleted in header_version 6 */ + uint32_t total_ram_blocks; /* Number of blocks should be written */ + uint32_t device_blocks; /* Number of total blocks in dump device */ + uint32_t written_blocks; /* Number of written blocks */ + uint32_t current_cpu; /* CPU# which handles dump */ + uint32_t nr_cpus; /* Number of CPUs */ +} DiskDumpHeader64; + +typedef struct QEMU_PACKED KdumpSubHeader32 { + uint32_t phys_base; + uint32_t dump_level; /* header_version 1 and later */ + uint32_t split; /* header_version 2 and later */ + uint32_t start_pfn; /* header_version 2 and later, + obsoleted in header_version 6 */ + uint32_t end_pfn; /* header_version 2 and later, + obsoleted in header_version 6 */ + uint64_t offset_vmcoreinfo; /* header_version 3 and later */ + uint32_t size_vmcoreinfo; /* header_version 3 and later */ + uint64_t offset_note; /* header_version 4 and later */ + uint32_t note_size; /* header_version 4 and later */ + uint64_t offset_eraseinfo; /* header_version 5 and later */ + uint32_t size_eraseinfo; /* header_version 5 and later */ + uint64_t start_pfn_64; /* header_version 6 and later */ + uint64_t end_pfn_64; /* header_version 6 and later */ + uint64_t max_mapnr_64; /* header_version 6 and later */ +} KdumpSubHeader32; + +typedef struct QEMU_PACKED KdumpSubHeader64 { + uint64_t phys_base; + uint32_t dump_level; /* header_version 1 and later */ + uint32_t split; /* header_version 2 and later */ + uint64_t start_pfn; /* header_version 2 and later, + obsoleted in header_version 6 */ + uint64_t end_pfn; /* header_version 2 and later, + obsoleted in header_version 6 */ + uint64_t offset_vmcoreinfo; /* header_version 3 and later */ + uint64_t size_vmcoreinfo; /* header_version 3 and later */ + uint64_t offset_note; /* header_version 4 and later */ + uint64_t note_size; /* header_version 4 and later */ + uint64_t offset_eraseinfo; /* header_version 5 and later */ + uint64_t size_eraseinfo; /* header_version 5 and later */ + uint64_t start_pfn_64; /* header_version 6 and later */ + uint64_t end_pfn_64; /* header_version 6 and later */ + uint64_t max_mapnr_64; /* header_version 6 and later */ +} KdumpSubHeader64; + +typedef struct DataCache { + int fd; /* fd of the file where to write the cached data */ + uint8_t *buf; /* buffer for cached data */ + size_t buf_size; /* size of the buf */ + size_t data_size; /* size of cached data in buf */ + off_t offset; /* offset of the file */ +} DataCache; + +typedef struct QEMU_PACKED PageDescriptor { + uint64_t offset; /* the offset of the page data*/ + uint32_t size; /* the size of this dump page */ + uint32_t flags; /* flags */ + uint64_t page_flags; /* page flags */ +} PageDescriptor; + struct GuestPhysBlockList; /* memory_mapping.h */ int cpu_get_dump_info(ArchDumpInfo *info, const struct GuestPhysBlockList *guest_phys_blocks); diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 495dae8..b90df9a 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -104,6 +104,7 @@ extern int autostart; typedef enum { VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB, VGA_QXL, + VGA_TCX, VGA_CG3, } VGAInterfaceType; extern int vga_interface_type; diff --git a/linux-headers/asm-s390/kvm.h b/linux-headers/asm-s390/kvm.h index d25da59..cb4c1eb 100644 --- a/linux-headers/asm-s390/kvm.h +++ b/linux-headers/asm-s390/kvm.h @@ -16,6 +16,22 @@ #define __KVM_S390 +/* Device control API: s390-specific devices */ +#define KVM_DEV_FLIC_GET_ALL_IRQS 1 +#define KVM_DEV_FLIC_ENQUEUE 2 +#define KVM_DEV_FLIC_CLEAR_IRQS 3 +#define KVM_DEV_FLIC_APF_ENABLE 4 +#define KVM_DEV_FLIC_APF_DISABLE_WAIT 5 +/* + * We can have up to 4*64k pending subchannels + 8 adapter interrupts, + * as well as up to ASYNC_PF_PER_VCPU*KVM_MAX_VCPUS pfault done interrupts. + * There are also sclp and machine checks. This gives us + * sizeof(kvm_s390_irq)*(4*65536+8+64*64+1+1) = 72 * 266250 = 19170000 + * Lets round up to 8192 pages. + */ +#define KVM_S390_MAX_FLOAT_IRQS 266250 +#define KVM_S390_FLIC_MAX_BUFFER 0x2000000 + /* for KVM_GET_REGS and KVM_SET_REGS */ struct kvm_regs { /* general purpose regs for s390 */ @@ -57,4 +73,7 @@ struct kvm_sync_regs { #define KVM_REG_S390_EPOCHDIFF (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x2) #define KVM_REG_S390_CPU_TIMER (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x3) #define KVM_REG_S390_CLOCK_COMP (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x4) +#define KVM_REG_S390_PFTOKEN (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x5) +#define KVM_REG_S390_PFCOMPARE (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x6) +#define KVM_REG_S390_PFSELECT (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x7) #endif diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 77ad35c..e27a4b3 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -413,6 +413,8 @@ struct kvm_s390_psw { #define KVM_S390_PROGRAM_INT 0xfffe0001u #define KVM_S390_SIGP_SET_PREFIX 0xfffe0002u #define KVM_S390_RESTART 0xfffe0003u +#define KVM_S390_INT_PFAULT_INIT 0xfffe0004u +#define KVM_S390_INT_PFAULT_DONE 0xfffe0005u #define KVM_S390_MCHK 0xfffe1000u #define KVM_S390_INT_VIRTIO 0xffff2603u #define KVM_S390_INT_SERVICE 0xffff2401u @@ -434,6 +436,69 @@ struct kvm_s390_interrupt { __u64 parm64; }; +struct kvm_s390_io_info { + __u16 subchannel_id; + __u16 subchannel_nr; + __u32 io_int_parm; + __u32 io_int_word; +}; + +struct kvm_s390_ext_info { + __u32 ext_params; + __u32 pad; + __u64 ext_params2; +}; + +struct kvm_s390_pgm_info { + __u64 trans_exc_code; + __u64 mon_code; + __u64 per_address; + __u32 data_exc_code; + __u16 code; + __u16 mon_class_nr; + __u8 per_code; + __u8 per_atmid; + __u8 exc_access_id; + __u8 per_access_id; + __u8 op_access_id; + __u8 pad[3]; +}; + +struct kvm_s390_prefix_info { + __u32 address; +}; + +struct kvm_s390_extcall_info { + __u16 code; +}; + +struct kvm_s390_emerg_info { + __u16 code; +}; + +struct kvm_s390_mchk_info { + __u64 cr14; + __u64 mcic; + __u64 failing_storage_address; + __u32 ext_damage_code; + __u32 pad; + __u8 fixed_logout[16]; +}; + +struct kvm_s390_irq { + __u64 type; + union { + struct kvm_s390_io_info io; + struct kvm_s390_ext_info ext; + struct kvm_s390_pgm_info pgm; + struct kvm_s390_emerg_info emerg; + struct kvm_s390_extcall_info extcall; + struct kvm_s390_prefix_info prefix; + struct kvm_s390_mchk_info mchk; + char reserved[64]; + } u; +}; + /* for KVM_SET_GUEST_DEBUG */ #define KVM_GUESTDBG_ENABLE 0x00000001 @@ -855,6 +920,7 @@ struct kvm_device_attr { #define KVM_DEV_VFIO_GROUP_ADD 1 #define KVM_DEV_VFIO_GROUP_DEL 2 #define KVM_DEV_TYPE_ARM_VGIC_V2 5 +#define KVM_DEV_TYPE_FLIC 6 /* * ioctls for VM fds @@ -2021,10 +2021,6 @@ int64_t dev_time; static void do_info_profile(Monitor *mon, const QDict *qdict) { - int64_t total; - total = qemu_time; - if (total == 0) - total = 1; monitor_printf(mon, "async time %" PRId64 " (%0.3f)\n", dev_time, dev_time / (double)get_ticks_per_sec()); monitor_printf(mon, "qemu time %" PRId64 " (%0.3f)\n", diff --git a/pc-bios/QEMU,cgthree.bin b/pc-bios/QEMU,cgthree.bin Binary files differnew file mode 100644 index 0000000..6fec946 --- /dev/null +++ b/pc-bios/QEMU,cgthree.bin diff --git a/pc-bios/README b/pc-bios/README index f190068..5914200 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -11,8 +11,8 @@ firmware implementation. The goal is to implement a 100% IEEE 1275-1994 (referred to as Open Firmware) compliant firmware. The included images for PowerPC (for 32 and 64 bit PPC CPUs), - Sparc32 (including QEMU,tcx.bin) and Sparc64 are built from OpenBIOS SVN - revision 1246. + Sparc32 (including QEMU,tcx.bin and QEMU,cgthree.bin) and Sparc64 are built + from OpenBIOS SVN revision 1246. - SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware implementation for certain IBM POWER hardware. The sources are at diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img Binary files differindex 6727f0c..f6223e7 100644 --- a/pc-bios/s390-ccw.img +++ b/pc-bios/s390-ccw.img diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index c5d5332..5c33766 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -10,7 +10,6 @@ #include "s390-ccw.h" -struct subchannel_id blk_schid; char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); uint64_t boot_value; @@ -23,13 +22,13 @@ void virtio_panic(const char *string) static void virtio_setup(uint64_t dev_info) { + struct subchannel_id blk_schid = { .one = 1 }; struct schib schib; int i; int r; bool found = false; bool check_devno = false; uint16_t dev_no = -1; - blk_schid.one = 1; if (dev_info != -1) { check_devno = true; diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c index 4d6e48f..a46914d 100644 --- a/pc-bios/s390-ccw/virtio.c +++ b/pc-bios/s390-ccw/virtio.c @@ -124,6 +124,7 @@ static void vring_init(struct vring *vr, unsigned int num, void *p, vr->used->flags = VRING_USED_F_NO_NOTIFY; vr->used->idx = 0; vr->used_idx = 0; + vr->next_idx = 0; debug_print_addr("init vr", vr); } diff --git a/qapi-schema.json b/qapi-schema.json index ac8ad24..193e7e4 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2795,6 +2795,24 @@ { 'command': 'device_del', 'data': {'id': 'str'} } ## +# @DumpGuestMemoryFormat: +# +# An enumeration of guest-memory-dump's format. +# +# @elf: elf format +# +# @kdump-zlib: kdump-compressed format with zlib-compressed +# +# @kdump-lzo: kdump-compressed format with lzo-compressed +# +# @kdump-snappy: kdump-compressed format with snappy-compressed +# +# Since: 2.0 +## +{ 'enum': 'DumpGuestMemoryFormat', + 'data': [ 'elf', 'kdump-zlib', 'kdump-lzo', 'kdump-snappy' ] } + +## # @dump-guest-memory # # Dump guest's memory to vmcore. It is a synchronous operation that can take @@ -2830,13 +2848,42 @@ # want to dump all guest's memory, please specify the start @begin # and @length # +# @format: #optional if specified, the format of guest memory dump. But non-elf +# format is conflict with paging and filter, ie. @paging, @begin and +# @length is not allowed to be specified with non-elf @format at the +# same time (since 2.0) +# # Returns: nothing on success # # Since: 1.2 ## { 'command': 'dump-guest-memory', 'data': { 'paging': 'bool', 'protocol': 'str', '*begin': 'int', - '*length': 'int' } } + '*length': 'int', '*format': 'DumpGuestMemoryFormat' } } + +## +# @DumpGuestMemoryCapability: +# +# A list of the available formats for dump-guest-memory +# +# Since: 2.0 +## +{ 'type': 'DumpGuestMemoryCapability', + 'data': { + 'formats': ['DumpGuestMemoryFormat'] } } + +## +# @query-dump-guest-memory-capability: +# +# Returns the available formats for dump-guest-memory +# +# Returns: A @DumpGuestMemoryCapability object listing available formats for +# dump-guest-memory +# +# Since: 2.0 +## +{ 'command': 'query-dump-guest-memory-capability', + 'returns': 'DumpGuestMemoryCapability' } ## # @netdev_add: @@ -4436,10 +4483,11 @@ # Driver specific block device options for Quorum # # @blkverify: #optional true if the driver must print content mismatch +# set to false by default # -# @children: the children block device to use +# @children: the children block devices to use # -# @vote_threshold: the vote limit under which a read will fail +# @vote-threshold: the vote limit under which a read will fail # # Since: 2.0 ## diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c index 96ed858..5d830a2 100644 --- a/qapi/opts-visitor.c +++ b/qapi/opts-visitor.c @@ -124,7 +124,9 @@ opts_start_struct(Visitor *v, void **obj, const char *kind, OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); const QemuOpt *opt; - *obj = g_malloc0(size > 0 ? size : 1); + if (obj) { + *obj = g_malloc0(size > 0 ? size : 1); + } if (ov->depth++ > 0) { return; } diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c index dc53545..d0ea118 100644 --- a/qapi/qapi-dealloc-visitor.c +++ b/qapi/qapi-dealloc-visitor.c @@ -131,9 +131,7 @@ static void qapi_dealloc_end_list(Visitor *v, Error **errp) static void qapi_dealloc_type_str(Visitor *v, char **obj, const char *name, Error **errp) { - if (obj) { - g_free(*obj); - } + g_free(*obj); } static void qapi_dealloc_type_int(Visitor *v, int64_t *obj, const char *name, diff --git a/qmp-commands.hx b/qmp-commands.hx index 8a0e832..d982cd6 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -791,8 +791,8 @@ EQMP { .name = "dump-guest-memory", - .args_type = "paging:b,protocol:s,begin:i?,end:i?", - .params = "-p protocol [begin] [length]", + .args_type = "paging:b,protocol:s,begin:i?,end:i?,format:s?", + .params = "-p protocol [begin] [length] [format]", .help = "dump guest memory to file", .user_print = monitor_user_noop, .mhandler.cmd_new = qmp_marshal_input_dump_guest_memory, @@ -813,6 +813,9 @@ Arguments: with length together (json-int) - "length": the memory size, in bytes. It's optional, and should be specified with begin together (json-int) +- "format": the format of guest memory dump. It's optional, and can be + elf|kdump-zlib|kdump-lzo|kdump-snappy, but non-elf formats will + conflict with paging and filter, ie. begin and length (json-string) Example: @@ -826,6 +829,26 @@ Notes: EQMP { + .name = "query-dump-guest-memory-capability", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_input_query_dump_guest_memory_capability, + }, + +SQMP +query-dump-guest-memory-capability +---------- + +Show available formats for 'dump-guest-memory' + +Example: + +-> { "execute": "query-dump-guest-memory-capability" } +<- { "return": { "formats": + ["elf", "kdump-zlib", "kdump-lzo", "kdump-snappy"] } + +EQMP + + { .name = "netdev_add", .args_type = "netdev:O", .mhandler.cmd_new = qmp_netdev_add, diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index b12b696..9734ab0 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -7,8 +7,8 @@ # Anthony Liguori <aliguori@us.ibm.com> # Michael Roth <mdroth@linux.vnet.ibm.com> # -# This work is licensed under the terms of the GNU GPLv2. -# See the COPYING.LIB file in the top-level directory. +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. from ordereddict import OrderedDict from qapi import * @@ -23,13 +23,6 @@ def type_visitor(name): else: return 'visit_type_%s' % name -def generate_decl_enum(name, members, genlist=True): - return mcgen(''' - -void %(visitor)s(Visitor *m, %(name)s * obj, const char *name, Error **errp); -''', - visitor=type_visitor(name)) - def generate_command_decl(name, args, ret_type): arglist="" for argname, argtype, optional, structured in parse_args(args): @@ -76,19 +69,6 @@ def gen_marshal_output_call(name, ret_type): return "" return "qmp_marshal_output_%s(retval, ret, errp);" % c_fun(name) -def gen_visitor_output_containers_decl(ret_type): - ret = "" - push_indent() - if ret_type: - ret += mcgen(''' -QmpOutputVisitor *mo; -QapiDeallocVisitor *md; -Visitor *v; -''') - pop_indent() - - return ret - def gen_visitor_input_containers_decl(args): ret = "" diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index 4a1652b..2c6e0dc 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -6,8 +6,8 @@ # Authors: # Anthony Liguori <aliguori@us.ibm.com> # -# This work is licensed under the terms of the GNU GPLv2. -# See the COPYING.LIB file in the top-level directory. +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. from ordereddict import OrderedDict from qapi import * diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 65f1a54..c6de9ae 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -7,8 +7,8 @@ # Anthony Liguori <aliguori@us.ibm.com> # Michael Roth <mdroth@linux.vnet.ibm.com> # -# This work is licensed under the terms of the GNU GPLv2. -# See the COPYING.LIB file in the top-level directory. +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. from ordereddict import OrderedDict from qapi import * @@ -47,9 +47,9 @@ static void visit_type_%(full_name)s_fields(Visitor *m, %(name)s ** obj, Error * if base: ret += mcgen(''' -visit_start_implicit_struct(m, obj ? (void**) &(*obj)->%(c_name)s : NULL, sizeof(%(type)s), &err); +visit_start_implicit_struct(m, (void**) &(*obj)->%(c_name)s, sizeof(%(type)s), &err); if (!err) { - visit_type_%(type)s_fields(m, obj ? &(*obj)->%(c_prefix)s%(c_name)s : NULL, &err); + visit_type_%(type)s_fields(m, &(*obj)->%(c_prefix)s%(c_name)s, &err); error_propagate(errp, err); err = NULL; visit_end_implicit_struct(m, &err); @@ -61,8 +61,8 @@ if (!err) { for argname, argentry, optional, structured in parse_args(members): if optional: ret += mcgen(''' -visit_start_optional(m, obj ? &(*obj)->%(c_prefix)shas_%(c_name)s : NULL, "%(name)s", &err); -if (obj && (*obj)->%(prefix)shas_%(c_name)s) { +visit_start_optional(m, &(*obj)->%(c_prefix)shas_%(c_name)s, "%(name)s", &err); +if ((*obj)->%(prefix)shas_%(c_name)s) { ''', c_prefix=c_var(field_prefix), prefix=field_prefix, c_name=c_var(argname), name=argname) @@ -72,7 +72,7 @@ if (obj && (*obj)->%(prefix)shas_%(c_name)s) { ret += generate_visit_struct_body(full_name, argname, argentry) else: ret += mcgen(''' -visit_type_%(type)s(m, obj ? &(*obj)->%(c_prefix)s%(c_name)s : NULL, "%(name)s", &err); +visit_type_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, "%(name)s", &err); ''', c_prefix=c_var(field_prefix), prefix=field_prefix, type=type_name(argentry), c_name=c_var(argname), @@ -121,7 +121,7 @@ visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); ret += mcgen(''' if (!err) { - if (!obj || *obj) { + if (*obj) { visit_type_%(name)s_fields(m, obj, &err); error_propagate(errp, err); err = NULL; @@ -273,7 +273,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** if (!error_is_set(errp)) { visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); if (!err) { - if (obj && *obj) { + if (*obj) { ''', name=name) @@ -494,10 +494,8 @@ fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL")) # have the functions defined, so we use -b option to provide control # over these cases if do_builtins: - fdef.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DEF")) for typename in builtin_types: fdef.write(generate_visit_list(typename, None)) - fdef.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DEF")) for expr in exprs: if expr.has_key('type'): diff --git a/scripts/qapi.py b/scripts/qapi.py index 9b3de4c..f3c2a20 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -8,8 +8,8 @@ # Anthony Liguori <aliguori@us.ibm.com> # Markus Armbruster <armbru@redhat.com> # -# This work is licensed under the terms of the GNU GPLv2. -# See the COPYING.LIB file in the top-level directory. +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. from ordereddict import OrderedDict import sys diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell index d374b35..e0e848b 100755 --- a/scripts/qmp/qmp-shell +++ b/scripts/qmp/qmp-shell @@ -112,13 +112,29 @@ class QMPShell(qmp.QEMUMonitorProtocol): value = json.loads(opt[1]) else: value = opt[1] - qmpcmd['arguments'][opt[0]] = value + optpath = opt[0].split('.') + parent = qmpcmd['arguments'] + curpath = [] + for p in optpath[:-1]: + curpath.append(p) + d = parent.get(p, {}) + if type(d) is not dict: + raise QMPShellError('Cannot use "%s" as both leaf and non-leaf key' % '.'.join(curpath)) + parent[p] = d + parent = d + if optpath[-1] in parent: + if type(parent[optpath[-1]]) is dict: + raise QMPShellError('Cannot use "%s" as both leaf and non-leaf key' % '.'.join(curpath)) + else: + raise QMPShellError('Cannot set "%s" multiple times' % opt[0]) + parent[optpath[-1]] = value return qmpcmd def _execute_cmd(self, cmdline): try: qmpcmd = self.__build_cmd(cmdline) - except: + except Exception, e: + print 'Error while parsing command line: %s' % e print 'command format: <command-name> ', print '[arg-name1=arg1] ... [arg-nameN=argN]' return True diff --git a/scripts/qmp/qmp.py b/scripts/qmp/qmp.py index 5c97175..20b6ec7 100644 --- a/scripts/qmp/qmp.py +++ b/scripts/qmp/qmp.py @@ -171,7 +171,12 @@ class QEMUMonitorProtocol: pass self.__sock.setblocking(1) if not self.__events and wait: - self.__json_read(only_event=True) + ret = self.__json_read(only_event=True) + if ret == None: + # We are in blocking mode, if don't get anything, something + # went wrong + raise QMPConnectError("Error while reading from socket") + return self.__events def clear_events(self): diff --git a/target-i386/cc_helper.c b/target-i386/cc_helper.c index ee04092..05dd12b 100644 --- a/target-i386/cc_helper.c +++ b/target-i386/cc_helper.c @@ -103,7 +103,7 @@ target_ulong helper_cc_compute_all(target_ulong dst, target_ulong src1, case CC_OP_EFLAGS: return src1; case CC_OP_CLR: - return CC_Z; + return CC_Z | CC_P; case CC_OP_MULB: return compute_all_mulb(dst, src1); diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 1b94f0f..0014acc 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -38,8 +38,10 @@ #ifdef TARGET_X86_64 #define ELF_MACHINE EM_X86_64 +#define ELF_MACHINE_UNAME "x86_64" #else #define ELF_MACHINE EM_386 +#define ELF_MACHINE_UNAME "i686" #endif #define CPUArchState struct CPUX86State @@ -1259,6 +1261,9 @@ static inline void cpu_load_efer(CPUX86State *env, uint64_t val) } } +/* fpu_helper.c */ +void cpu_set_mxcsr(CPUX86State *env, uint32_t val); + /* svm_helper.c */ void cpu_svm_check_intercept_param(CPUX86State *env1, uint32_t type, uint64_t param); diff --git a/target-i386/fpu_helper.c b/target-i386/fpu_helper.c index c0427fe..de7ba76 100644 --- a/target-i386/fpu_helper.c +++ b/target-i386/fpu_helper.c @@ -1179,7 +1179,7 @@ void helper_fxrstor(CPUX86State *env, target_ulong ptr, int data64) if (env->cr[4] & CR4_OSFXSR_MASK) { /* XXX: finish it */ - env->mxcsr = cpu_ldl_data(env, ptr + 0x18); + cpu_set_mxcsr(env, cpu_ldl_data(env, ptr + 0x18)); /* cpu_ldl_data(env, ptr + 0x1c); */ if (env->hflags & HF_CS64_MASK) { nb_xmm_regs = 16; @@ -1229,12 +1229,14 @@ floatx80 cpu_set_fp80(uint64_t mant, uint16_t upper) #define SSE_RC_CHOP 0x6000 #define SSE_FZ 0x8000 -static void update_sse_status(CPUX86State *env) +void cpu_set_mxcsr(CPUX86State *env, uint32_t mxcsr) { int rnd_type; + env->mxcsr = mxcsr; + /* set rounding mode */ - switch (env->mxcsr & SSE_RC_MASK) { + switch (mxcsr & SSE_RC_MASK) { default: case SSE_RC_NEAR: rnd_type = float_round_nearest_even; @@ -1252,16 +1254,15 @@ static void update_sse_status(CPUX86State *env) set_float_rounding_mode(rnd_type, &env->sse_status); /* set denormals are zero */ - set_flush_inputs_to_zero((env->mxcsr & SSE_DAZ) ? 1 : 0, &env->sse_status); + set_flush_inputs_to_zero((mxcsr & SSE_DAZ) ? 1 : 0, &env->sse_status); /* set flush to zero */ - set_flush_to_zero((env->mxcsr & SSE_FZ) ? 1 : 0, &env->fp_status); + set_flush_to_zero((mxcsr & SSE_FZ) ? 1 : 0, &env->fp_status); } void helper_ldmxcsr(CPUX86State *env, uint32_t val) { - env->mxcsr = val; - update_sse_status(env); + cpu_set_mxcsr(env, val); } void helper_enter_mmx(CPUX86State *env) diff --git a/target-i386/gdbstub.c b/target-i386/gdbstub.c index 15bebef..d34e535 100644 --- a/target-i386/gdbstub.c +++ b/target-i386/gdbstub.c @@ -222,7 +222,7 @@ int x86_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return 4; case IDX_MXCSR_REG: - env->mxcsr = ldl_p(mem_buf); + cpu_set_mxcsr(env, ldl_p(mem_buf)); return 4; } } diff --git a/target-i386/translate.c b/target-i386/translate.c index 5dd2450..707ebd5 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -748,7 +748,7 @@ static void gen_compute_eflags(DisasContext *s) return; } if (s->cc_op == CC_OP_CLR) { - tcg_gen_movi_tl(cpu_cc_src, CC_Z); + tcg_gen_movi_tl(cpu_cc_src, CC_Z | CC_P); set_cc_op(s, CC_OP_EFLAGS); return; } @@ -4284,22 +4284,48 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, if (is_xmm) { op1_offset = offsetof(CPUX86State,xmm_regs[reg]); if (mod != 3) { + int sz = 4; + gen_lea_modrm(env, s, modrm); op2_offset = offsetof(CPUX86State,xmm_t0); - if (b1 >= 2 && ((b >= 0x50 && b <= 0x5f && b != 0x5b) || - b == 0xc2)) { - /* specific case for SSE single instructions */ + + switch (b) { + case 0x50 ... 0x5a: + case 0x5c ... 0x5f: + case 0xc2: + /* Most sse scalar operations. */ if (b1 == 2) { - /* 32 bit access */ - gen_op_ld_v(s, MO_32, cpu_T[0], cpu_A0); - tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(0))); + sz = 2; + } else if (b1 == 3) { + sz = 3; + } + break; + + case 0x2e: /* ucomis[sd] */ + case 0x2f: /* comis[sd] */ + if (b1 == 0) { + sz = 2; } else { - /* 64 bit access */ - gen_ldq_env_A0(s, offsetof(CPUX86State, - xmm_t0.XMM_D(0))); + sz = 3; } - } else { + break; + } + + switch (sz) { + case 2: + /* 32 bit access */ + gen_op_ld_v(s, MO_32, cpu_T[0], cpu_A0); + tcg_gen_st32_tl(cpu_T[0], cpu_env, + offsetof(CPUX86State,xmm_t0.XMM_L(0))); + break; + case 3: + /* 64 bit access */ + gen_ldq_env_A0(s, offsetof(CPUX86State, xmm_t0.XMM_D(0))); + break; + default: + /* 128 bit access */ gen_ldo_env_A0(s, op2_offset); + break; } } else { rm = (modrm & 7) | REX_B(s); diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c index ff57b80..1a8c1cc 100644 --- a/target-s390x/cpu.c +++ b/target-s390x/cpu.c @@ -83,6 +83,7 @@ static void s390_cpu_reset(CPUState *s) S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); CPUS390XState *env = &cpu->env; + env->pfault_token = -1UL; s390_del_running_cpu(cpu); scc->parent_reset(s); #if !defined(CONFIG_USER_ONLY) @@ -105,6 +106,17 @@ static void s390_cpu_initial_reset(CPUState *s) /* architectured initial values for CR 0 and 14 */ env->cregs[0] = CR0_RESET; env->cregs[14] = CR14_RESET; + + env->pfault_token = -1UL; + +#if defined(CONFIG_KVM) + /* Reset state inside the kernel that we cannot access yet from QEMU. */ + if (kvm_enabled()) { + if (kvm_vcpu_ioctl(s, KVM_S390_INITIAL_RESET, NULL)) { + perror("Initial CPU reset failed"); + } + } +#endif } /* CPUClass:reset() */ @@ -123,6 +135,9 @@ static void s390_cpu_full_reset(CPUState *s) /* architectured initial values for CR 0 and 14 */ env->cregs[0] = CR0_RESET; env->cregs[14] = CR14_RESET; + + env->pfault_token = -1UL; + /* set halted to 1 to make sure we can add the cpu in * s390_ipl_cpu code, where CPUState::halted is set back to 0 * after incrementing the cpu counter */ diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h index 96c2b4a..effe84b 100644 --- a/target-s390x/cpu.h +++ b/target-s390x/cpu.h @@ -28,6 +28,7 @@ #define TARGET_LONG_BITS 64 #define ELF_MACHINE EM_S390 +#define ELF_MACHINE_UNAME "S390X" #define CPUArchState struct CPUS390XState @@ -121,6 +122,10 @@ typedef struct CPUS390XState { uint64_t cputm; uint32_t todpr; + uint64_t pfault_token; + uint64_t pfault_compare; + uint64_t pfault_select; + CPU_COMMON /* reset does memset(0) up to here */ @@ -959,7 +964,7 @@ struct sysib_322 { void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr); int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, target_ulong *raddr, int *flags); -int sclp_service_call(uint32_t sccb, uint64_t code); +int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code); uint32_t calc_cc(CPUS390XState *env, uint32_t cc_op, uint64_t src, uint64_t dst, uint64_t vr); diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index f60ccdc..11feda9 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -53,25 +53,28 @@ #define IPA0_B9 0xb900 #define IPA0_EB 0xeb00 -#define PRIV_SCLP_CALL 0x20 -#define PRIV_CSCH 0x30 -#define PRIV_HSCH 0x31 -#define PRIV_MSCH 0x32 -#define PRIV_SSCH 0x33 -#define PRIV_STSCH 0x34 -#define PRIV_TSCH 0x35 -#define PRIV_TPI 0x36 -#define PRIV_SAL 0x37 -#define PRIV_RSCH 0x38 -#define PRIV_STCRW 0x39 -#define PRIV_STCPS 0x3a -#define PRIV_RCHP 0x3b -#define PRIV_SCHM 0x3c -#define PRIV_CHSC 0x5f -#define PRIV_SIGA 0x74 -#define PRIV_XSCH 0x76 -#define PRIV_SQBS 0x8a -#define PRIV_EQBS 0x9c +#define PRIV_B2_SCLP_CALL 0x20 +#define PRIV_B2_CSCH 0x30 +#define PRIV_B2_HSCH 0x31 +#define PRIV_B2_MSCH 0x32 +#define PRIV_B2_SSCH 0x33 +#define PRIV_B2_STSCH 0x34 +#define PRIV_B2_TSCH 0x35 +#define PRIV_B2_TPI 0x36 +#define PRIV_B2_SAL 0x37 +#define PRIV_B2_RSCH 0x38 +#define PRIV_B2_STCRW 0x39 +#define PRIV_B2_STCPS 0x3a +#define PRIV_B2_RCHP 0x3b +#define PRIV_B2_SCHM 0x3c +#define PRIV_B2_CHSC 0x5f +#define PRIV_B2_SIGA 0x74 +#define PRIV_B2_XSCH 0x76 + +#define PRIV_EB_SQBS 0x8a + +#define PRIV_B9_EQBS 0x9c + #define DIAG_IPL 0x308 #define DIAG_KVM_HYPERCALL 0x500 #define DIAG_KVM_BREAKPOINT 0x501 @@ -87,12 +90,14 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { }; static int cap_sync_regs; +static int cap_async_pf; static void *legacy_s390_alloc(size_t size); int kvm_arch_init(KVMState *s) { cap_sync_regs = kvm_check_extension(s, KVM_CAP_SYNC_REGS); + cap_async_pf = kvm_check_extension(s, KVM_CAP_ASYNC_PF); if (!kvm_check_extension(s, KVM_CAP_S390_GMAP) || !kvm_check_extension(s, KVM_CAP_S390_COW)) { phys_mem_set_alloc(legacy_s390_alloc); @@ -178,6 +183,29 @@ int kvm_arch_put_registers(CPUState *cs, int level) return ret; } + if (cap_async_pf) { + reg.id = KVM_REG_S390_PFTOKEN; + reg.addr = (__u64)&(env->pfault_token); + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret < 0) { + return ret; + } + + reg.id = KVM_REG_S390_PFCOMPARE; + reg.addr = (__u64)&(env->pfault_compare); + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret < 0) { + return ret; + } + + reg.id = KVM_REG_S390_PFSELECT; + reg.addr = (__u64)&(env->pfault_select); + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret < 0) { + return ret; + } + } + if (cap_sync_regs && cs->kvm_run->kvm_valid_regs & KVM_SYNC_ACRS && cs->kvm_run->kvm_valid_regs & KVM_SYNC_CRS) { @@ -282,6 +310,29 @@ int kvm_arch_get_registers(CPUState *cs) return r; } + if (cap_async_pf) { + reg.id = KVM_REG_S390_PFTOKEN; + reg.addr = (__u64)&(env->pfault_token); + r = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (r < 0) { + return r; + } + + reg.id = KVM_REG_S390_PFCOMPARE; + reg.addr = (__u64)&(env->pfault_compare); + r = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (r < 0) { + return r; + } + + reg.id = KVM_REG_S390_PFSELECT; + reg.addr = (__u64)&(env->pfault_select); + r = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (r < 0) { + return r; + } + } + return 0; } @@ -392,117 +443,128 @@ static int kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run, uint16_t ipbh0) { CPUS390XState *env = &cpu->env; - uint32_t sccb; - uint64_t code; + uint64_t sccb; + uint32_t code; int r = 0; cpu_synchronize_state(CPU(cpu)); - if (env->psw.mask & PSW_MASK_PSTATE) { - enter_pgmcheck(cpu, PGM_PRIVILEGED); - return 0; - } sccb = env->regs[ipbh0 & 0xf]; code = env->regs[(ipbh0 & 0xf0) >> 4]; - r = sclp_service_call(sccb, code); + r = sclp_service_call(env, sccb, code); if (r < 0) { enter_pgmcheck(cpu, -r); + } else { + setcc(cpu, r); } - setcc(cpu, r); return 0; } -static int kvm_handle_css_inst(S390CPU *cpu, struct kvm_run *run, - uint8_t ipa0, uint8_t ipa1, uint8_t ipb) +static int handle_b2(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) { CPUS390XState *env = &cpu->env; - - if (ipa0 != 0xb2) { - /* Not handled for now. */ - return -1; - } + int rc = 0; + uint16_t ipbh0 = (run->s390_sieic.ipb & 0xffff0000) >> 16; cpu_synchronize_state(CPU(cpu)); switch (ipa1) { - case PRIV_XSCH: + case PRIV_B2_XSCH: ioinst_handle_xsch(cpu, env->regs[1]); break; - case PRIV_CSCH: + case PRIV_B2_CSCH: ioinst_handle_csch(cpu, env->regs[1]); break; - case PRIV_HSCH: + case PRIV_B2_HSCH: ioinst_handle_hsch(cpu, env->regs[1]); break; - case PRIV_MSCH: + case PRIV_B2_MSCH: ioinst_handle_msch(cpu, env->regs[1], run->s390_sieic.ipb); break; - case PRIV_SSCH: + case PRIV_B2_SSCH: ioinst_handle_ssch(cpu, env->regs[1], run->s390_sieic.ipb); break; - case PRIV_STCRW: + case PRIV_B2_STCRW: ioinst_handle_stcrw(cpu, run->s390_sieic.ipb); break; - case PRIV_STSCH: + case PRIV_B2_STSCH: ioinst_handle_stsch(cpu, env->regs[1], run->s390_sieic.ipb); break; - case PRIV_TSCH: + case PRIV_B2_TSCH: /* We should only get tsch via KVM_EXIT_S390_TSCH. */ fprintf(stderr, "Spurious tsch intercept\n"); break; - case PRIV_CHSC: + case PRIV_B2_CHSC: ioinst_handle_chsc(cpu, run->s390_sieic.ipb); break; - case PRIV_TPI: + case PRIV_B2_TPI: /* This should have been handled by kvm already. */ fprintf(stderr, "Spurious tpi intercept\n"); break; - case PRIV_SCHM: + case PRIV_B2_SCHM: ioinst_handle_schm(cpu, env->regs[1], env->regs[2], run->s390_sieic.ipb); break; - case PRIV_RSCH: + case PRIV_B2_RSCH: ioinst_handle_rsch(cpu, env->regs[1]); break; - case PRIV_RCHP: + case PRIV_B2_RCHP: ioinst_handle_rchp(cpu, env->regs[1]); break; - case PRIV_STCPS: + case PRIV_B2_STCPS: /* We do not provide this instruction, it is suppressed. */ break; - case PRIV_SAL: + case PRIV_B2_SAL: ioinst_handle_sal(cpu, env->regs[1]); break; - case PRIV_SIGA: + case PRIV_B2_SIGA: /* Not provided, set CC = 3 for subchannel not operational */ setcc(cpu, 3); break; + case PRIV_B2_SCLP_CALL: + rc = kvm_sclp_service_call(cpu, run, ipbh0); + break; default: - return -1; + rc = -1; + DPRINTF("KVM: unhandled PRIV: 0xb2%x\n", ipa1); + break; } - return 0; + return rc; } -static int handle_priv(S390CPU *cpu, struct kvm_run *run, - uint8_t ipa0, uint8_t ipa1) +static int handle_b9(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) { int r = 0; - uint16_t ipbh0 = (run->s390_sieic.ipb & 0xffff0000) >> 16; - uint8_t ipb = run->s390_sieic.ipb & 0xff; - DPRINTF("KVM: PRIV: %d\n", ipa1); switch (ipa1) { - case PRIV_SCLP_CALL: - r = kvm_sclp_service_call(cpu, run, ipbh0); - break; - default: - r = kvm_handle_css_inst(cpu, run, ipa0, ipa1, ipb); - if (r == -1) { - DPRINTF("KVM: unhandled PRIV: 0x%x\n", ipa1); - } - break; + case PRIV_B9_EQBS: + /* just inject exception */ + r = -1; + break; + default: + r = -1; + DPRINTF("KVM: unhandled PRIV: 0xb9%x\n", ipa1); + break; + } + + return r; +} + +static int handle_eb(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) +{ + int r = 0; + + switch (ipa1) { + case PRIV_EB_SQBS: + /* just inject exception */ + r = -1; + break; + default: + r = -1; + DPRINTF("KVM: unhandled PRIV: 0xeb%x\n", ipa1); + break; } return r; @@ -511,11 +573,16 @@ static int handle_priv(S390CPU *cpu, struct kvm_run *run, static int handle_hypercall(S390CPU *cpu, struct kvm_run *run) { CPUS390XState *env = &cpu->env; + int ret; cpu_synchronize_state(CPU(cpu)); - env->regs[2] = s390_virtio_hypercall(env); + ret = s390_virtio_hypercall(env); + if (ret == -EINVAL) { + enter_pgmcheck(cpu, PGM_SPECIFICATION); + return 0; + } - return 0; + return ret; } static void kvm_handle_diag_308(S390CPU *cpu, struct kvm_run *run) @@ -576,25 +643,22 @@ int kvm_s390_cpu_restart(S390CPU *cpu) return 0; } -static int s390_cpu_initial_reset(S390CPU *cpu) +static void sigp_initial_cpu_reset(void *arg) { - CPUState *cs = CPU(cpu); - CPUS390XState *env = &cpu->env; - int i; + CPUState *cpu = arg; + S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); - s390_del_running_cpu(cpu); - if (kvm_vcpu_ioctl(cs, KVM_S390_INITIAL_RESET, NULL) < 0) { - perror("cannot init reset vcpu"); - } + cpu_synchronize_state(cpu); + scc->initial_cpu_reset(cpu); +} - /* Manually zero out all registers */ - cpu_synchronize_state(cs); - for (i = 0; i < 16; i++) { - env->regs[i] = 0; - } +static void sigp_cpu_reset(void *arg) +{ + CPUState *cpu = arg; + S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); - DPRINTF("DONE: SIGP initial reset: %p\n", env); - return 0; + cpu_synchronize_state(cpu); + scc->cpu_reset(cpu); } #define SIGP_ORDER_MASK 0x000000ff @@ -628,10 +692,17 @@ static int handle_sigp(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) cc = kvm_s390_cpu_restart(target_cpu); break; case SIGP_SET_ARCH: - /* make the caller panic */ - return -1; + *statusreg &= 0xffffffff00000000UL; + *statusreg |= SIGP_STAT_INVALID_PARAMETER; + cc = 1; /* status stored */ + break; case SIGP_INITIAL_CPU_RESET: - cc = s390_cpu_initial_reset(target_cpu); + run_on_cpu(CPU(target_cpu), sigp_initial_cpu_reset, CPU(target_cpu)); + cc = 0; + break; + case SIGP_CPU_RESET: + run_on_cpu(CPU(target_cpu), sigp_cpu_reset, CPU(target_cpu)); + cc = 0; break; default: DPRINTF("KVM: unknown SIGP: 0x%x\n", order_code); @@ -656,9 +727,13 @@ static void handle_instruction(S390CPU *cpu, struct kvm_run *run) run->s390_sieic.ipa, run->s390_sieic.ipb); switch (ipa0) { case IPA0_B2: + r = handle_b2(cpu, run, ipa1); + break; case IPA0_B9: + r = handle_b9(cpu, run, ipa1); + break; case IPA0_EB: - r = handle_priv(cpu, run, ipa0 >> 8, ipa1); + r = handle_eb(cpu, run, ipa1); break; case IPA0_DIAG: r = handle_diag(cpu, run, run->s390_sieic.ipb); diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c index 10d0425..728456f 100644 --- a/target-s390x/misc_helper.c +++ b/target-s390x/misc_helper.c @@ -93,7 +93,7 @@ void program_interrupt(CPUS390XState *env, uint32_t code, int ilen) /* SCLP service call */ uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2) { - int r = sclp_service_call(r1, r2); + int r = sclp_service_call(env, r1, r2); if (r < 0) { program_interrupt(env, -r, 4); return 0; diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index fe5af75..471ba47 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -7,8 +7,12 @@ 'data': { 'enum1': 'EnumOne', '*enum2': 'EnumOne', 'enum3': 'EnumOne', '*enum4': 'EnumOne' } } # for testing nested structs +{ 'type': 'UserDefZero', + 'data': { 'integer': 'int' } } + { 'type': 'UserDefOne', - 'data': { 'integer': 'int', 'string': 'str', '*enum1': 'EnumOne' } } + 'base': 'UserDefZero', + 'data': { 'string': 'str', '*enum1': 'EnumOne' } } { 'type': 'UserDefTwo', 'data': { 'string': 'str', @@ -30,8 +34,20 @@ 'data': { 'integer': 'int' } } { 'union': 'UserDefUnion', + 'base': 'UserDefZero', 'data': { 'a' : 'UserDefA', 'b' : 'UserDefB' } } +{ 'union': 'UserDefFlatUnion', + 'base': 'UserDefOne', + 'discriminator': 'string', + 'data': { 'a' : 'UserDefA', 'b' : 'UserDefB' } } +# FIXME generated struct UserDefFlatUnion has members for direct base +# UserDefOne, but lacks members for indirect base UserDefZero + +{ 'union': 'UserDefAnonUnion', + 'discriminator': {}, + 'data': { 'uda': 'UserDefA', 's': 'str', 'i': 'int' } } + # for testing native lists { 'union': 'UserDefNativeListUnion', 'data': { 'integer': ['int'], @@ -50,7 +66,11 @@ # testing commands { 'command': 'user_def_cmd', 'data': {} } { 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} } -{ 'command': 'user_def_cmd2', 'data': {'ud1a': 'UserDefOne', 'ud1b': 'UserDefOne'}, 'returns': 'UserDefTwo' } +{ 'command': 'user_def_cmd2', + 'data': {'ud1a': 'UserDefOne', '*ud1b': 'UserDefOne'}, + 'returns': 'UserDefTwo' } +{ 'command': 'user_def_cmd3', 'data': {'a': 'int', '*b': 'int' }, + 'returns': 'int' } # For testing integer range flattening in opts-visitor. The following schema # corresponds to the option format: diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 3851880..89b53d4 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -1,19 +1,28 @@ [OrderedDict([('enum', 'EnumOne'), ('data', ['value1', 'value2', 'value3'])]), OrderedDict([('type', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]), - OrderedDict([('type', 'UserDefOne'), ('data', OrderedDict([('integer', 'int'), ('string', 'str'), ('*enum1', 'EnumOne')]))]), + OrderedDict([('type', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]), + OrderedDict([('type', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]), OrderedDict([('type', 'UserDefTwo'), ('data', OrderedDict([('string', 'str'), ('dict', OrderedDict([('string', 'str'), ('dict', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')])), ('*dict2', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]))]))]), OrderedDict([('type', 'UserDefNested'), ('data', OrderedDict([('string0', 'str'), ('dict1', OrderedDict([('string1', 'str'), ('dict2', OrderedDict([('userdef1', 'UserDefOne'), ('string2', 'str')])), ('*dict3', OrderedDict([('userdef2', 'UserDefOne'), ('string3', 'str')]))]))]))]), OrderedDict([('type', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]), OrderedDict([('type', 'UserDefB'), ('data', OrderedDict([('integer', 'int')]))]), - OrderedDict([('union', 'UserDefUnion'), ('data', OrderedDict([('a', 'UserDefA'), ('b', 'UserDefB')]))]), + OrderedDict([('union', 'UserDefUnion'), ('base', 'UserDefZero'), ('data', OrderedDict([('a', 'UserDefA'), ('b', 'UserDefB')]))]), + OrderedDict([('union', 'UserDefFlatUnion'), ('base', 'UserDefOne'), ('discriminator', 'string'), ('data', OrderedDict([('a', 'UserDefA'), ('b', 'UserDefB')]))]), + OrderedDict([('union', 'UserDefAnonUnion'), ('discriminator', OrderedDict()), ('data', OrderedDict([('uda', 'UserDefA'), ('s', 'str'), ('i', 'int')]))]), OrderedDict([('union', 'UserDefNativeListUnion'), ('data', OrderedDict([('integer', ['int']), ('s8', ['int8']), ('s16', ['int16']), ('s32', ['int32']), ('s64', ['int64']), ('u8', ['uint8']), ('u16', ['uint16']), ('u32', ['uint32']), ('u64', ['uint64']), ('number', ['number']), ('boolean', ['bool']), ('string', ['str'])]))]), OrderedDict([('command', 'user_def_cmd'), ('data', OrderedDict())]), OrderedDict([('command', 'user_def_cmd1'), ('data', OrderedDict([('ud1a', 'UserDefOne')]))]), - OrderedDict([('command', 'user_def_cmd2'), ('data', OrderedDict([('ud1a', 'UserDefOne'), ('ud1b', 'UserDefOne')])), ('returns', 'UserDefTwo')]), + OrderedDict([('command', 'user_def_cmd2'), ('data', OrderedDict([('ud1a', 'UserDefOne'), ('*ud1b', 'UserDefOne')])), ('returns', 'UserDefTwo')]), + OrderedDict([('command', 'user_def_cmd3'), ('data', OrderedDict([('a', 'int'), ('*b', 'int')])), ('returns', 'int')]), OrderedDict([('type', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))])] -['EnumOne', 'UserDefUnionKind', 'UserDefNativeListUnionKind'] +['EnumOne', + 'UserDefUnionKind', + 'UserDefFlatUnionKind', + 'UserDefAnonUnionKind', + 'UserDefNativeListUnionKind'] [OrderedDict([('type', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]), - OrderedDict([('type', 'UserDefOne'), ('data', OrderedDict([('integer', 'int'), ('string', 'str'), ('*enum1', 'EnumOne')]))]), + OrderedDict([('type', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]), + OrderedDict([('type', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]), OrderedDict([('type', 'UserDefTwo'), ('data', OrderedDict([('string', 'str'), ('dict', OrderedDict([('string', 'str'), ('dict', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')])), ('*dict2', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]))]))]), OrderedDict([('type', 'UserDefNested'), ('data', OrderedDict([('string0', 'str'), ('dict1', OrderedDict([('string1', 'str'), ('dict2', OrderedDict([('userdef1', 'UserDefOne'), ('string2', 'str')])), ('*dict3', OrderedDict([('userdef2', 'UserDefOne'), ('string3', 'str')]))]))]))]), OrderedDict([('type', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]), diff --git a/tests/qemu-iotests-quick.sh b/tests/qemu-iotests-quick.sh index cf90de0..c449e8a 100755 --- a/tests/qemu-iotests-quick.sh +++ b/tests/qemu-iotests-quick.sh @@ -8,6 +8,7 @@ export QEMU_PROG="this_should_be_unused" export QEMU_IMG_PROG="$(pwd)/qemu-img" export QEMU_IO_PROG="$(pwd)/qemu-io" +export QEMU_NBD_PROG="$(pwd)/qemu-nbd" cd $SRC_PATH/tests/qemu-iotests diff --git a/tests/qemu-iotests/081 b/tests/qemu-iotests/081 index f053f11..b512d00 100755 --- a/tests/qemu-iotests/081 +++ b/tests/qemu-iotests/081 @@ -56,6 +56,9 @@ function run_qemu() do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | _filter_qemu_io } +test_quorum=$($QEMU_IMG --help|grep quorum) +[ "$test_quorum" = "" ] && _supported_fmt quorum + quorum="file.driver=quorum,file.children.0.file.filename=$TEST_DIR/1.raw" quorum="$quorum,file.children.1.file.filename=$TEST_DIR/2.raw" quorum="$quorum,file.children.2.file.filename=$TEST_DIR/3.raw,file.vote-threshold=2" diff --git a/tests/qemu-iotests/081.out b/tests/qemu-iotests/081.out index 4fe2f95..84aeb0c 100644 --- a/tests/qemu-iotests/081.out +++ b/tests/qemu-iotests/081.out @@ -30,7 +30,7 @@ Testing: -drive file=TEST_DIR/2.IMGFMT,format=IMGFMT,if=none,id=drive2 QMP_VERSION {"return": {}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "QUORUM_REPORT_BAD", "data": {"node-name": "", "ret": 0, "sectors-count": 20480, "sector-num": 0}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "QUORUM_REPORT_BAD", "data": {"node-name": "", "sectors-count": 20480, "sector-num": 0}} read 10485760/10485760 bytes at offset 0 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": ""} diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 71e9a74..881079b 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -203,7 +203,7 @@ _cleanup_test_img() ;; rbd) - rbd rm "$TEST_DIR/t.$IMGFMT" > /dev/null + rbd --no-progress rm "$TEST_DIR/t.$IMGFMT" > /dev/null ;; sheepdog) diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index db127d9..8dd8553 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -58,30 +58,30 @@ 049 rw auto 050 rw auto backing quick 051 rw auto -052 rw auto backing -053 rw auto -054 rw auto +052 rw auto backing quick +053 rw auto quick +054 rw auto quick 055 rw auto 056 rw auto backing 057 rw auto -058 rw auto -059 rw auto -060 rw auto -061 rw auto -062 rw auto -063 rw auto -064 rw auto +058 rw auto quick +059 rw auto quick +060 rw auto quick +061 rw auto quick +062 rw auto quick +063 rw auto quick +064 rw auto quick 065 rw auto -066 rw auto +066 rw auto quick 067 rw auto 068 rw auto -069 rw auto -070 rw auto +069 rw auto quick +070 rw auto quick 071 rw auto -072 rw auto -073 rw auto -074 rw auto -077 rw auto +072 rw auto quick +073 rw auto quick +074 rw auto quick +077 rw auto quick 079 rw auto 081 rw auto 082 rw auto quick diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c index 5a3e82a..8e62c2d 100644 --- a/tests/test-qmp-commands.c +++ b/tests/test-qmp-commands.c @@ -16,16 +16,20 @@ void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp) { } -UserDefTwo * qmp_user_def_cmd2(UserDefOne * ud1a, UserDefOne * ud1b, Error **errp) +UserDefTwo *qmp_user_def_cmd2(UserDefOne *ud1a, + bool has_udb1, UserDefOne *ud1b, + Error **errp) { UserDefTwo *ret; UserDefOne *ud1c = g_malloc0(sizeof(UserDefOne)); UserDefOne *ud1d = g_malloc0(sizeof(UserDefOne)); ud1c->string = strdup(ud1a->string); - ud1c->integer = ud1a->integer; - ud1d->string = strdup(ud1b->string); - ud1d->integer = ud1b->integer; + ud1c->base = g_new0(UserDefZero, 1); + ud1c->base->integer = ud1a->base->integer; + ud1d->string = strdup(has_udb1 ? ud1b->string : "blah0"); + ud1d->base = g_new0(UserDefZero, 1); + ud1d->base->integer = has_udb1 ? ud1b->base->integer : 0; ret = g_malloc0(sizeof(UserDefTwo)); ret->string = strdup("blah1"); @@ -39,6 +43,11 @@ UserDefTwo * qmp_user_def_cmd2(UserDefOne * ud1a, UserDefOne * ud1b, Error **err return ret; } +int64_t qmp_user_def_cmd3(int64_t a, bool has_b, int64_t b, Error **errp) +{ + return a + (has_b ? b : 0); +} + /* test commands with no input and no return value */ static void test_dispatch_cmd(void) { @@ -71,14 +80,34 @@ static void test_dispatch_cmd_error(void) QDECREF(req); } +static QObject *test_qmp_dispatch(QDict *req) +{ + QObject *resp_obj; + QDict *resp; + QObject *ret; + + resp_obj = qmp_dispatch(QOBJECT(req)); + assert(resp_obj); + resp = qobject_to_qdict(resp_obj); + assert(resp && !qdict_haskey(resp, "error")); + ret = qdict_get(resp, "return"); + assert(ret); + qobject_incref(ret); + qobject_decref(resp_obj); + return ret; +} + /* test commands that involve both input parameters and return values */ static void test_dispatch_cmd_io(void) { QDict *req = qdict_new(); QDict *args = qdict_new(); + QDict *args3 = qdict_new(); QDict *ud1a = qdict_new(); QDict *ud1b = qdict_new(); - QObject *resp; + QDict *ret, *ret_dict, *ret_dict_dict, *ret_dict_dict_userdef; + QDict *ret_dict_dict2, *ret_dict_dict2_userdef; + QInt *ret3; qdict_put_obj(ud1a, "integer", QOBJECT(qint_from_int(42))); qdict_put_obj(ud1a, "string", QOBJECT(qstring_from_str("hello"))); @@ -87,15 +116,33 @@ static void test_dispatch_cmd_io(void) qdict_put_obj(args, "ud1a", QOBJECT(ud1a)); qdict_put_obj(args, "ud1b", QOBJECT(ud1b)); qdict_put_obj(req, "arguments", QOBJECT(args)); - qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd2"))); - /* TODO: put in full payload and check for errors */ - resp = qmp_dispatch(QOBJECT(req)); - assert(resp != NULL); - assert(!qdict_haskey(qobject_to_qdict(resp), "error")); + ret = qobject_to_qdict(test_qmp_dispatch(req)); + + assert(!strcmp(qdict_get_str(ret, "string"), "blah1")); + ret_dict = qdict_get_qdict(ret, "dict"); + assert(!strcmp(qdict_get_str(ret_dict, "string"), "blah2")); + ret_dict_dict = qdict_get_qdict(ret_dict, "dict"); + ret_dict_dict_userdef = qdict_get_qdict(ret_dict_dict, "userdef"); + assert(qdict_get_int(ret_dict_dict_userdef, "integer") == 42); + assert(!strcmp(qdict_get_str(ret_dict_dict_userdef, "string"), "hello")); + assert(!strcmp(qdict_get_str(ret_dict_dict, "string"), "blah3")); + ret_dict_dict2 = qdict_get_qdict(ret_dict, "dict2"); + ret_dict_dict2_userdef = qdict_get_qdict(ret_dict_dict2, "userdef"); + assert(qdict_get_int(ret_dict_dict2_userdef, "integer") == 422); + assert(!strcmp(qdict_get_str(ret_dict_dict2_userdef, "string"), "hello2")); + assert(!strcmp(qdict_get_str(ret_dict_dict2, "string"), "blah4")); + QDECREF(ret); + + qdict_put(args3, "a", qint_from_int(66)); + qdict_put(req, "arguments", args3); + qdict_put(req, "execute", qstring_from_str("user_def_cmd3")); + + ret3 = qobject_to_qint(test_qmp_dispatch(req)); + assert(qint_get_int(ret3) == 66); + QDECREF(ret); - qobject_decref(resp); QDECREF(req); } @@ -106,17 +153,20 @@ static void test_dealloc_types(void) UserDefOneList *ud1list; ud1test = g_malloc0(sizeof(UserDefOne)); - ud1test->integer = 42; + ud1test->base = g_new0(UserDefZero, 1); + ud1test->base->integer = 42; ud1test->string = g_strdup("hi there 42"); qapi_free_UserDefOne(ud1test); ud1a = g_malloc0(sizeof(UserDefOne)); - ud1a->integer = 43; + ud1a->base = g_new0(UserDefZero, 1); + ud1a->base->integer = 43; ud1a->string = g_strdup("hi there 43"); ud1b = g_malloc0(sizeof(UserDefOne)); - ud1b->integer = 44; + ud1b->base = g_new0(UserDefZero, 1); + ud1b->base->integer = 44; ud1b->string = g_strdup("hi there 44"); ud1list = g_malloc0(sizeof(UserDefOneList)); diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c index 38bdf5e..64d72f6 100644 --- a/tests/test-qmp-input-strict.c +++ b/tests/test-qmp-input-strict.c @@ -132,13 +132,42 @@ static void test_validate_union(TestInputVisitorData *data, Visitor *v; Error *errp = NULL; - v = validate_test_init(data, "{ 'type': 'b', 'data' : { 'integer': 42 } }"); + v = validate_test_init(data, "{ 'type': 'b', 'integer': 41, 'data' : { 'integer': 42 } }"); visit_type_UserDefUnion(v, &tmp, NULL, &errp); g_assert(!errp); qapi_free_UserDefUnion(tmp); } +static void test_validate_union_flat(TestInputVisitorData *data, + const void *unused) +{ + UserDefFlatUnion *tmp = NULL; + Visitor *v; + Error *errp = NULL; + + v = validate_test_init(data, "{ 'string': 'a', 'boolean': true }"); + /* TODO when generator bug is fixed, add 'integer': 41 */ + + visit_type_UserDefFlatUnion(v, &tmp, NULL, &errp); + g_assert(!error_is_set(&errp)); + qapi_free_UserDefFlatUnion(tmp); +} + +static void test_validate_union_anon(TestInputVisitorData *data, + const void *unused) +{ + UserDefAnonUnion *tmp = NULL; + Visitor *v; + Error *errp = NULL; + + v = validate_test_init(data, "42"); + + visit_type_UserDefAnonUnion(v, &tmp, NULL, &errp); + g_assert(!error_is_set(&errp)); + qapi_free_UserDefAnonUnion(tmp); +} + static void test_validate_fail_struct(TestInputVisitorData *data, const void *unused) { @@ -191,13 +220,41 @@ static void test_validate_fail_union(TestInputVisitorData *data, Error *errp = NULL; Visitor *v; - v = validate_test_init(data, "{ 'type': 'b', 'data' : { 'integer': 42 }, 'extra': 'yyy' }"); + v = validate_test_init(data, "{ 'type': 'b', 'data' : { 'integer': 42 } }"); visit_type_UserDefUnion(v, &tmp, NULL, &errp); g_assert(errp); qapi_free_UserDefUnion(tmp); } +static void test_validate_fail_union_flat(TestInputVisitorData *data, + const void *unused) +{ + UserDefFlatUnion *tmp = NULL; + Error *errp = NULL; + Visitor *v; + + v = validate_test_init(data, "{ 'string': 'c', 'integer': 41, 'boolean': true }"); + + visit_type_UserDefFlatUnion(v, &tmp, NULL, &errp); + g_assert(error_is_set(&errp)); + qapi_free_UserDefFlatUnion(tmp); +} + +static void test_validate_fail_union_anon(TestInputVisitorData *data, + const void *unused) +{ + UserDefAnonUnion *tmp = NULL; + Visitor *v; + Error *errp = NULL; + + v = validate_test_init(data, "3.14"); + + visit_type_UserDefAnonUnion(v, &tmp, NULL, &errp); + g_assert(error_is_set(&errp)); + qapi_free_UserDefAnonUnion(tmp); +} + static void validate_test_add(const char *testpath, TestInputVisitorData *data, void (*test_func)(TestInputVisitorData *data, const void *user_data)) @@ -220,6 +277,10 @@ int main(int argc, char **argv) &testdata, test_validate_list); validate_test_add("/visitor/input-strict/pass/union", &testdata, test_validate_union); + validate_test_add("/visitor/input-strict/pass/union-flat", + &testdata, test_validate_union_flat); + validate_test_add("/visitor/input-strict/pass/union-anon", + &testdata, test_validate_union_anon); validate_test_add("/visitor/input-strict/fail/struct", &testdata, test_validate_fail_struct); validate_test_add("/visitor/input-strict/fail/struct-nested", @@ -228,6 +289,10 @@ int main(int argc, char **argv) &testdata, test_validate_fail_list); validate_test_add("/visitor/input-strict/fail/union", &testdata, test_validate_fail_union); + validate_test_add("/visitor/input-strict/fail/union-flat", + &testdata, test_validate_fail_union_flat); + validate_test_add("/visitor/input-strict/fail/union-anon", + &testdata, test_validate_fail_union_anon); g_test_run(); diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c index 6eb7dc5..2dffafc 100644 --- a/tests/test-qmp-input-visitor.c +++ b/tests/test-qmp-input-visitor.c @@ -252,7 +252,7 @@ static void test_visitor_in_struct_nested(TestInputVisitorData *data, check_and_free_str(udp->string0, "string0"); check_and_free_str(udp->dict1.string1, "string1"); - g_assert_cmpint(udp->dict1.dict2.userdef1->integer, ==, 42); + g_assert_cmpint(udp->dict1.dict2.userdef1->base->integer, ==, 42); check_and_free_str(udp->dict1.dict2.userdef1->string, "string"); check_and_free_str(udp->dict1.dict2.string2, "string2"); g_assert(udp->dict1.has_dict3 == false); @@ -280,7 +280,7 @@ static void test_visitor_in_list(TestInputVisitorData *data, snprintf(string, sizeof(string), "string%d", i); g_assert_cmpstr(item->value->string, ==, string); - g_assert_cmpint(item->value->integer, ==, 42 + i); + g_assert_cmpint(item->value->base->integer, ==, 42 + i); } qapi_free_UserDefOneList(head); @@ -293,15 +293,50 @@ static void test_visitor_in_union(TestInputVisitorData *data, Error *err = NULL; UserDefUnion *tmp; - v = visitor_input_test_init(data, "{ 'type': 'b', 'data' : { 'integer': 42 } }"); + v = visitor_input_test_init(data, "{ 'type': 'b', 'integer': 41, 'data' : { 'integer': 42 } }"); visit_type_UserDefUnion(v, &tmp, NULL, &err); g_assert(err == NULL); g_assert_cmpint(tmp->kind, ==, USER_DEF_UNION_KIND_B); + g_assert_cmpint(tmp->integer, ==, 41); g_assert_cmpint(tmp->b->integer, ==, 42); qapi_free_UserDefUnion(tmp); } +static void test_visitor_in_union_flat(TestInputVisitorData *data, + const void *unused) +{ + Visitor *v; + Error *err = NULL; + UserDefFlatUnion *tmp; + + v = visitor_input_test_init(data, "{ 'string': 'a', 'boolean': true }"); + /* TODO when generator bug is fixed, add 'integer': 41 */ + + visit_type_UserDefFlatUnion(v, &tmp, NULL, &err); + g_assert(err == NULL); + g_assert_cmpint(tmp->kind, ==, USER_DEF_UNION_KIND_A); + /* TODO g_assert_cmpint(tmp->integer, ==, 41); */ + g_assert_cmpint(tmp->a->boolean, ==, true); + qapi_free_UserDefFlatUnion(tmp); +} + +static void test_visitor_in_union_anon(TestInputVisitorData *data, + const void *unused) +{ + Visitor *v; + Error *err = NULL; + UserDefAnonUnion *tmp; + + v = visitor_input_test_init(data, "42"); + + visit_type_UserDefAnonUnion(v, &tmp, NULL, &err); + g_assert(err == NULL); + g_assert_cmpint(tmp->kind, ==, USER_DEF_ANON_UNION_KIND_I); + g_assert_cmpint(tmp->i, ==, 42); + qapi_free_UserDefAnonUnion(tmp); +} + static void test_native_list_integer_helper(TestInputVisitorData *data, const void *unused, UserDefNativeListUnionKind kind) @@ -635,6 +670,10 @@ int main(int argc, char **argv) &in_visitor_data, test_visitor_in_list); input_visitor_test_add("/visitor/input/union", &in_visitor_data, test_visitor_in_union); + input_visitor_test_add("/visitor/input/union-flat", + &in_visitor_data, test_visitor_in_union_flat); + input_visitor_test_add("/visitor/input/union-anon", + &in_visitor_data, test_visitor_in_union_anon); input_visitor_test_add("/visitor/input/errors", &in_visitor_data, test_visitor_in_errors); input_visitor_test_add("/visitor/input/native_list/int", diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c index f31d168..105f4cf 100644 --- a/tests/test-qmp-output-visitor.c +++ b/tests/test-qmp-output-visitor.c @@ -231,13 +231,15 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data, ud2->dict1.string1 = g_strdup(strings[1]); ud2->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne)); ud2->dict1.dict2.userdef1->string = g_strdup(string); - ud2->dict1.dict2.userdef1->integer = value; + ud2->dict1.dict2.userdef1->base = g_new0(UserDefZero, 1); + ud2->dict1.dict2.userdef1->base->integer = value; ud2->dict1.dict2.string2 = g_strdup(strings[2]); ud2->dict1.has_dict3 = true; ud2->dict1.dict3.userdef2 = g_malloc0(sizeof(UserDefOne)); ud2->dict1.dict3.userdef2->string = g_strdup(string); - ud2->dict1.dict3.userdef2->integer = value; + ud2->dict1.dict3.userdef2->base = g_new0(UserDefZero, 1); + ud2->dict1.dict3.userdef2->base->integer = value; ud2->dict1.dict3.string3 = g_strdup(strings[3]); visit_type_UserDefNested(data->ov, &ud2, "unused", &errp); @@ -279,7 +281,8 @@ static void test_visitor_out_struct_errors(TestOutputVisitorData *data, const void *unused) { EnumOne bad_values[] = { ENUM_ONE_MAX, -1 }; - UserDefOne u = { 0 }, *pu = &u; + UserDefZero b; + UserDefOne u = { .base = &b }, *pu = &u; Error *errp; int i; @@ -391,7 +394,8 @@ static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data, p->value->dict1.string1 = g_strdup(string); p->value->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne)); p->value->dict1.dict2.userdef1->string = g_strdup(string); - p->value->dict1.dict2.userdef1->integer = 42; + p->value->dict1.dict2.userdef1->base = g_new0(UserDefZero, 1); + p->value->dict1.dict2.userdef1->base->integer = 42; p->value->dict1.dict2.string2 = g_strdup(string); p->value->dict1.has_dict3 = false; @@ -412,6 +416,7 @@ static void test_visitor_out_union(TestOutputVisitorData *data, UserDefUnion *tmp = g_malloc0(sizeof(UserDefUnion)); tmp->kind = USER_DEF_UNION_KIND_A; + tmp->integer = 41; tmp->a = g_malloc0(sizeof(UserDefA)); tmp->a->boolean = true; @@ -423,6 +428,7 @@ static void test_visitor_out_union(TestOutputVisitorData *data, qdict = qobject_to_qdict(arg); g_assert_cmpstr(qdict_get_str(qdict, "type"), ==, "a"); + g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 41); qvalue = qdict_get(qdict, "data"); g_assert(data != NULL); @@ -434,6 +440,55 @@ static void test_visitor_out_union(TestOutputVisitorData *data, QDECREF(qdict); } +static void test_visitor_out_union_flat(TestOutputVisitorData *data, + const void *unused) +{ + QObject *arg; + QDict *qdict; + + Error *err = NULL; + + UserDefFlatUnion *tmp = g_malloc0(sizeof(UserDefFlatUnion)); + tmp->kind = USER_DEF_UNION_KIND_A; + tmp->a = g_malloc0(sizeof(UserDefA)); + /* TODO when generator bug is fixed: tmp->integer = 41; */ + tmp->a->boolean = true; + + visit_type_UserDefFlatUnion(data->ov, &tmp, NULL, &err); + g_assert(err == NULL); + arg = qmp_output_get_qobject(data->qov); + + g_assert(qobject_type(arg) == QTYPE_QDICT); + qdict = qobject_to_qdict(arg); + + g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "a"); + /* TODO g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 41); */ + g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true); + + qapi_free_UserDefFlatUnion(tmp); + QDECREF(qdict); +} + +static void test_visitor_out_union_anon(TestOutputVisitorData *data, + const void *unused) +{ + QObject *arg; + Error *err = NULL; + + UserDefAnonUnion *tmp = g_malloc0(sizeof(UserDefAnonUnion)); + tmp->kind = USER_DEF_ANON_UNION_KIND_I; + tmp->i = 42; + + visit_type_UserDefAnonUnion(data->ov, &tmp, NULL, &err); + g_assert(err == NULL); + arg = qmp_output_get_qobject(data->qov); + + g_assert(qobject_type(arg) == QTYPE_QINT); + g_assert_cmpint(qint_get_int(qobject_to_qint(arg)), ==, 42); + + qapi_free_UserDefAnonUnion(tmp); +} + static void init_native_list(UserDefNativeListUnion *cvalue) { int i; @@ -782,6 +837,10 @@ int main(int argc, char **argv) &out_visitor_data, test_visitor_out_list_qapi_free); output_visitor_test_add("/visitor/output/union", &out_visitor_data, test_visitor_out_union); + output_visitor_test_add("/visitor/output/union-flat", + &out_visitor_data, test_visitor_out_union_flat); + output_visitor_test_add("/visitor/output/union-anon", + &out_visitor_data, test_visitor_out_union_anon); output_visitor_test_add("/visitor/output/native_list/int", &out_visitor_data, test_visitor_out_native_list_int); output_visitor_test_add("/visitor/output/native_list/int8", diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c index 6bff950..8166cf1 100644 --- a/tests/test-visitor-serialization.c +++ b/tests/test-visitor-serialization.c @@ -239,12 +239,14 @@ static UserDefNested *nested_struct_create(void) udnp->string0 = strdup("test_string0"); udnp->dict1.string1 = strdup("test_string1"); udnp->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne)); - udnp->dict1.dict2.userdef1->integer = 42; + udnp->dict1.dict2.userdef1->base = g_new0(UserDefZero, 1); + udnp->dict1.dict2.userdef1->base->integer = 42; udnp->dict1.dict2.userdef1->string = strdup("test_string"); udnp->dict1.dict2.string2 = strdup("test_string2"); udnp->dict1.has_dict3 = true; udnp->dict1.dict3.userdef2 = g_malloc0(sizeof(UserDefOne)); - udnp->dict1.dict3.userdef2->integer = 43; + udnp->dict1.dict3.userdef2->base = g_new0(UserDefZero, 1); + udnp->dict1.dict3.userdef2->base->integer = 43; udnp->dict1.dict3.userdef2->string = strdup("test_string"); udnp->dict1.dict3.string3 = strdup("test_string3"); return udnp; @@ -256,14 +258,14 @@ static void nested_struct_compare(UserDefNested *udnp1, UserDefNested *udnp2) g_assert(udnp2); g_assert_cmpstr(udnp1->string0, ==, udnp2->string0); g_assert_cmpstr(udnp1->dict1.string1, ==, udnp2->dict1.string1); - g_assert_cmpint(udnp1->dict1.dict2.userdef1->integer, ==, - udnp2->dict1.dict2.userdef1->integer); + g_assert_cmpint(udnp1->dict1.dict2.userdef1->base->integer, ==, + udnp2->dict1.dict2.userdef1->base->integer); g_assert_cmpstr(udnp1->dict1.dict2.userdef1->string, ==, udnp2->dict1.dict2.userdef1->string); g_assert_cmpstr(udnp1->dict1.dict2.string2, ==, udnp2->dict1.dict2.string2); g_assert(udnp1->dict1.has_dict3 == udnp2->dict1.has_dict3); - g_assert_cmpint(udnp1->dict1.dict3.userdef2->integer, ==, - udnp2->dict1.dict3.userdef2->integer); + g_assert_cmpint(udnp1->dict1.dict3.userdef2->base->integer, ==, + udnp2->dict1.dict3.userdef2->base->integer); g_assert_cmpstr(udnp1->dict1.dict3.userdef2->string, ==, udnp2->dict1.dict3.userdef2->string); g_assert_cmpstr(udnp1->dict1.dict3.string3, ==, udnp2->dict1.dict3.string3); diff --git a/trace-events b/trace-events index 580281d..d86f98c 100644 --- a/trace-events +++ b/trace-events @@ -1162,6 +1162,11 @@ css_io_interrupt(int cssid, int ssid, int schid, uint32_t intparm, uint8_t isc, virtio_ccw_interpret_ccw(int cssid, int ssid, int schid, int cmd_code) "VIRTIO-CCW: %x.%x.%04x: interpret command %x" virtio_ccw_new_device(int cssid, int ssid, int schid, int devno, const char *devno_mode) "VIRTIO-CCW: add subchannel %x.%x.%04x, devno %04x (%s)" +# hw/intc/s390_flic.c +flic_create_device(int err) "flic: create device failed %d" +flic_no_device_api(int err) "flic: no Device Contral API support %d" +flic_reset_failed(int err) "flic: reset failed %d" + # migration.c migrate_set_state(int new_state) "new state %d" @@ -2027,6 +2027,16 @@ static bool qxl_vga_available(void) return object_class_by_name("qxl-vga"); } +static bool tcx_vga_available(void) +{ + return object_class_by_name("SUNW,tcx"); +} + +static bool cg3_vga_available(void) +{ + return object_class_by_name("cgthree"); +} + static void select_vgahw (const char *p) { const char *opts; @@ -2062,6 +2072,20 @@ static void select_vgahw (const char *p) fprintf(stderr, "Error: QXL VGA not available\n"); exit(0); } + } else if (strstart(p, "tcx", &opts)) { + if (tcx_vga_available()) { + vga_interface_type = VGA_TCX; + } else { + fprintf(stderr, "Error: TCX framebuffer not available\n"); + exit(0); + } + } else if (strstart(p, "cg3", &opts)) { + if (cg3_vga_available()) { + vga_interface_type = VGA_CG3; + } else { + fprintf(stderr, "Error: CG3 framebuffer not available\n"); + exit(0); + } } else if (!strstart(p, "none", &opts)) { invalid_vga: fprintf(stderr, "Unknown vga type: %s\n", p); |