diff options
253 files changed, 14934 insertions, 2502 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 026ea4f021..7efaccf5fb 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 diff --git a/Makefile b/Makefile index a28a3c8d49..bd9cd4fceb 100644 --- a/Makefile +++ b/Makefile @@ -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 \ @@ -398,7 +399,7 @@ endif $(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(qemu_datadir)/keymaps"; \ done for d in $(TARGET_DIRS); do \ - $(MAKE) -C $$d $@ || exit 1 ; \ + $(MAKE) $(SUBDIR_MAKEFLAGS) TARGET_DIR=$$d/ -C $$d $@ || exit 1 ; \ done # various test targets diff --git a/arch_init.c b/arch_init.c index 80574a090c..fe1727922c 100644 --- a/arch_init.c +++ b/arch_init.c @@ -122,7 +122,6 @@ static void check_guest_throttling(void); #define RAM_SAVE_FLAG_XBZRLE 0x40 /* 0x80 is reserved in migration.h start with 0x100 next */ - static struct defconfig_file { const char *filename; /* Indicates it is an user config file (disabled by -no-user-config) */ @@ -133,6 +132,7 @@ static struct defconfig_file { { NULL }, /* end of list */ }; +static const uint8_t ZERO_TARGET_PAGE[TARGET_PAGE_SIZE]; int qemu_read_default_config_files(bool userconfig) { @@ -273,6 +273,34 @@ static size_t save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset, return size; } +/* This is the last block that we have visited serching for dirty pages + */ +static RAMBlock *last_seen_block; +/* This is the last block from where we have sent data */ +static RAMBlock *last_sent_block; +static ram_addr_t last_offset; +static unsigned long *migration_bitmap; +static uint64_t migration_dirty_pages; +static uint32_t last_version; +static bool ram_bulk_stage; + +/* Update the xbzrle cache to reflect a page that's been sent as all 0. + * The important thing is that a stale (not-yet-0'd) page be replaced + * by the new data. + * As a bonus, if the page wasn't in the cache it gets added so that + * when a small write is made into the 0'd page it gets XBZRLE sent + */ +static void xbzrle_cache_zero_page(ram_addr_t current_addr) +{ + if (ram_bulk_stage || !migrate_use_xbzrle()) { + return; + } + + /* We don't care if this fails to allocate a new cache page + * as long as it updated an old one */ + cache_insert(XBZRLE.cache, current_addr, ZERO_TARGET_PAGE); +} + #define ENCODING_FLAG_XBZRLE 0x1 static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data, @@ -329,18 +357,6 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data, return bytes_sent; } - -/* This is the last block that we have visited serching for dirty pages - */ -static RAMBlock *last_seen_block; -/* This is the last block from where we have sent data */ -static RAMBlock *last_sent_block; -static ram_addr_t last_offset; -static unsigned long *migration_bitmap; -static uint64_t migration_dirty_pages; -static uint32_t last_version; -static bool ram_bulk_stage; - static inline ram_addr_t migration_bitmap_find_and_reset_dirty(MemoryRegion *mr, ram_addr_t start) @@ -512,6 +528,7 @@ static int ram_save_block(QEMUFile *f, bool last_stage) } else { int ret; uint8_t *p; + bool send_async = true; int cont = (block == last_sent_block) ? RAM_SAVE_FLAG_CONTINUE : 0; @@ -522,6 +539,7 @@ static int ram_save_block(QEMUFile *f, bool last_stage) ret = ram_control_save_page(f, block->offset, offset, TARGET_PAGE_SIZE, &bytes_sent); + current_addr = block->offset + offset; if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { if (ret != RAM_SAVE_CONTROL_DELAYED) { if (bytes_sent > 0) { @@ -536,19 +554,35 @@ static int ram_save_block(QEMUFile *f, bool last_stage) RAM_SAVE_FLAG_COMPRESS); qemu_put_byte(f, 0); bytes_sent++; + /* Must let xbzrle know, otherwise a previous (now 0'd) cached + * page would be stale + */ + xbzrle_cache_zero_page(current_addr); } else if (!ram_bulk_stage && migrate_use_xbzrle()) { - current_addr = block->offset + offset; bytes_sent = save_xbzrle_page(f, p, current_addr, block, offset, cont, last_stage); if (!last_stage) { + /* We must send exactly what's in the xbzrle cache + * even if the page wasn't xbzrle compressed, so that + * it's right next time. + */ p = get_cached_data(XBZRLE.cache, current_addr); + + /* Can't send this cached data async, since the cache page + * might get updated before it gets to the wire + */ + send_async = false; } } /* XBZRLE overflow or normal page */ if (bytes_sent == -1) { bytes_sent = save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_PAGE); - qemu_put_buffer_async(f, p, TARGET_PAGE_SIZE); + if (send_async) { + qemu_put_buffer_async(f, p, TARGET_PAGE_SIZE); + } else { + qemu_put_buffer(f, p, TARGET_PAGE_SIZE); + } bytes_sent += TARGET_PAGE_SIZE; acct_info.norm_pages++; } diff --git a/backends/baum.c b/backends/baum.c index 1132899026..665107fa9c 100644 --- a/backends/baum.c +++ b/backends/baum.c @@ -566,7 +566,7 @@ CharDriverState *chr_baum_init(void) BaumDriverState *baum; CharDriverState *chr; brlapi_handle_t *handle; -#ifdef CONFIG_SDL +#if defined(CONFIG_SDL) && SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0) SDL_SysWMinfo info; #endif int tty; @@ -595,7 +595,7 @@ CharDriverState *chr_baum_init(void) goto fail; } -#ifdef CONFIG_SDL +#if defined(CONFIG_SDL) && SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0) memset(&info, 0, sizeof(info)); SDL_VERSION(&info.version); if (SDL_GetWMInfo(&info)) diff --git a/block.c b/block.c index 2fd5482572..f1ef4b0109 100644 --- a/block.c +++ b/block.c @@ -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; } @@ -934,7 +935,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, bdrv_refresh_limits(bs); assert(bdrv_opt_mem_align(bs) != 0); - assert(bs->request_alignment != 0); + assert((bs->request_alignment != 0) || bs->sg); #ifndef _WIN32 if (bs->is_temporary) { @@ -1016,7 +1017,12 @@ static int bdrv_file_open(BlockDriverState *bs, const char *filename, ret = -EINVAL; goto fail; } - qdict_del(*options, "filename"); + + if (!drv->bdrv_needs_filename) { + qdict_del(*options, "filename"); + } else { + filename = qdict_get_str(*options, "filename"); + } } if (!drv->bdrv_file_open) { @@ -1228,6 +1234,7 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, ret = bdrv_file_open(bs, filename, &options, flags & ~BDRV_O_PROTOCOL, &local_err); if (!ret) { + drv = bs->drv; goto done; } else if (bs->drv) { goto close_and_fail; @@ -1846,11 +1853,6 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest, pstrcpy(bs_dest->device_name, sizeof(bs_dest->device_name), bs_src->device_name); bs_dest->device_list = bs_src->device_list; - - /* keep the same entry in graph_bdrv_states - * We do want to swap name but don't want to swap linked list entries - */ - bs_dest->node_list = bs_src->node_list; } /* @@ -1869,6 +1871,17 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old) { BlockDriverState tmp; + /* The code needs to swap the node_name but simply swapping node_list won't + * work so first remove the nodes from the graph list, do the swap then + * insert them back if needed. + */ + if (bs_new->node_name[0] != '\0') { + QTAILQ_REMOVE(&graph_bdrv_states, bs_new, node_list); + } + if (bs_old->node_name[0] != '\0') { + QTAILQ_REMOVE(&graph_bdrv_states, bs_old, node_list); + } + /* bs_new must be anonymous and shouldn't have anything fancy enabled */ assert(bs_new->device_name[0] == '\0'); assert(QLIST_EMPTY(&bs_new->dirty_bitmaps)); @@ -1897,6 +1910,14 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old) assert(bs_new->io_limits_enabled == false); assert(!throttle_have_timer(&bs_new->throttle_state)); + /* insert the nodes back into the graph node list if needed */ + if (bs_new->node_name[0] != '\0') { + QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs_new, node_list); + } + if (bs_old->node_name[0] != '\0') { + QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs_old, node_list); + } + bdrv_rebind(bs_new); bdrv_rebind(bs_old); } diff --git a/block/gluster.c b/block/gluster.c index 14d390b4c7..a44d612923 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -3,21 +3,12 @@ * * Copyright (C) 2012 Bharata B Rao <bharata@linux.vnet.ibm.com> * - * Pipe handling mechanism in AIO implementation is derived from - * block/rbd.c. Hence, + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. * - * Copyright (C) 2010-2011 Christian Brunner <chb@muc.de>, - * Josh Durgin <josh.durgin@dreamhost.com> - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. */ #include <glusterfs/api/glfs.h> #include "block/block_int.h" -#include "qemu/sockets.h" #include "qemu/uri.h" typedef struct GlusterAIOCB { @@ -32,9 +23,6 @@ typedef struct BDRVGlusterState { struct glfs_fd *fd; } BDRVGlusterState; -#define GLUSTER_FD_READ 0 -#define GLUSTER_FD_WRITE 1 - typedef struct GlusterConf { char *server; int port; @@ -45,11 +33,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 +262,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 +306,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 +328,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, errp); + 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 +708,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 +735,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 +762,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 +789,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 41ec09709d..b490e98c05 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) { @@ -1224,55 +1231,56 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, bs->total_sectors = sector_lun2qemu(iscsilun->num_blocks, iscsilun); bs->request_alignment = iscsilun->block_size; - /* Medium changer or tape. We dont have any emulation for this so this must - * be sg ioctl compatible. We force it to be sg, otherwise qemu will try - * to read from the device to guess the image format. + /* We don't have any emulation for devices other than disks and CD-ROMs, so + * this must be sg ioctl compatible. We force it to be sg, otherwise qemu + * will try to read from the device to guess the image format. */ - if (iscsilun->type == TYPE_MEDIUM_CHANGER || - iscsilun->type == TYPE_TAPE) { + if (iscsilun->type != TYPE_DISK && iscsilun->type != TYPE_ROM) { 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/mirror.c b/block/mirror.c index e683959570..dd5ee056b4 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -520,9 +520,6 @@ static void mirror_complete(BlockJob *job, Error **errp) ret = bdrv_open_backing_file(s->target, NULL, &local_err); if (ret < 0) { - char backing_filename[PATH_MAX]; - bdrv_get_full_backing_filename(s->target, backing_filename, - sizeof(backing_filename)); error_propagate(errp, local_err); return; } diff --git a/block/quorum.c b/block/quorum.c index 6c28239718..bd997b7322 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/raw-posix.c b/block/raw-posix.c index 161ea14812..e6b4c1fe02 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -336,6 +336,17 @@ error: } #endif +static void raw_parse_filename(const char *filename, QDict *options, + Error **errp) +{ + /* The filename does not have to be prefixed by the protocol name, since + * "file" is the default protocol; therefore, the return value of this + * function call can be ignored. */ + strstart(filename, "file:", &filename); + + qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename))); +} + static QemuOptsList raw_runtime_opts = { .name = "raw", .head = QTAILQ_HEAD_INITIALIZER(raw_runtime_opts.head), @@ -1230,6 +1241,8 @@ static int raw_create(const char *filename, QEMUOptionParameter *options, int result = 0; int64_t total_size = 0; + strstart(filename, "file:", &filename); + /* Read out options */ while (options && options->name) { if (!strcmp(options->name, BLOCK_OPT_SIZE)) { @@ -1412,6 +1425,7 @@ static BlockDriver bdrv_file = { .instance_size = sizeof(BDRVRawState), .bdrv_needs_filename = true, .bdrv_probe = NULL, /* no probe for protocols */ + .bdrv_parse_filename = raw_parse_filename, .bdrv_file_open = raw_open, .bdrv_reopen_prepare = raw_reopen_prepare, .bdrv_reopen_commit = raw_reopen_commit, diff --git a/block/raw-win32.c b/block/raw-win32.c index ae1c8e6cca..99547488e4 100644 --- a/block/raw-win32.c +++ b/block/raw-win32.c @@ -251,6 +251,17 @@ static void raw_parse_flags(int flags, int *access_flags, DWORD *overlapped) } } +static void raw_parse_filename(const char *filename, QDict *options, + Error **errp) +{ + /* The filename does not have to be prefixed by the protocol name, since + * "file" is the default protocol; therefore, the return value of this + * function call can be ignored. */ + strstart(filename, "file:", &filename); + + qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename))); +} + static QemuOptsList raw_runtime_opts = { .name = "raw", .head = QTAILQ_HEAD_INITIALIZER(raw_runtime_opts.head), @@ -470,6 +481,8 @@ static int raw_create(const char *filename, QEMUOptionParameter *options, int fd; int64_t total_size = 0; + strstart(filename, "file:", &filename); + /* Read out options */ while (options && options->name) { if (!strcmp(options->name, BLOCK_OPT_SIZE)) { @@ -504,6 +517,7 @@ static BlockDriver bdrv_file = { .protocol_name = "file", .instance_size = sizeof(BDRVRawState), .bdrv_needs_filename = true, + .bdrv_parse_filename = raw_parse_filename, .bdrv_file_open = raw_open, .bdrv_close = raw_close, .bdrv_create = raw_create, diff --git a/block/vmdk.c b/block/vmdk.c index 83839f9b7a..b69988d169 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; } diff --git a/blockdev.c b/blockdev.c index 357f7607ff..c3422a1d41 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2266,6 +2266,7 @@ void qmp_block_job_complete(const char *device, Error **errp) void qmp_blockdev_add(BlockdevOptions *options, Error **errp) { QmpOutputVisitor *ov = qmp_output_visitor_new(); + DriveInfo *dinfo; QObject *obj; QDict *qdict; Error *local_err = NULL; @@ -2282,8 +2283,10 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp) * * For now, simply forbidding the combination for all drivers will do. */ if (options->has_aio && options->aio == BLOCKDEV_AIO_OPTIONS_NATIVE) { - bool direct = options->cache->has_direct && options->cache->direct; - if (!options->has_cache && !direct) { + bool direct = options->has_cache && + options->cache->has_direct && + options->cache->direct; + if (!direct) { error_setg(errp, "aio=native requires cache.direct=true"); goto fail; } @@ -2301,12 +2304,18 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp) qdict_flatten(qdict); - blockdev_init(NULL, qdict, &local_err); + dinfo = blockdev_init(NULL, qdict, &local_err); if (local_err) { error_propagate(errp, local_err); goto fail; } + if (bdrv_key_required(dinfo->bdrv)) { + drive_uninit(dinfo); + error_setg(errp, "blockdev-add doesn't support encrypted devices"); + goto fail; + } + fail: qmp_output_visitor_cleanup(ov); } diff --git a/configure b/configure index 423f435550..8689435ccf 100755 --- a/configure +++ b/configure @@ -14,13 +14,14 @@ fi TMPC="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.c" TMPB="qemu-conf-${RANDOM}-$$-${RANDOM}" TMPO="${TMPDIR1}/${TMPB}.o" +TMPCXX="${TMPDIR1}/${TMPB}.cxx" TMPL="${TMPDIR1}/${TMPB}.lo" TMPA="${TMPDIR1}/lib${TMPB}.la" TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.exe" # NB: do not call "exit" in the trap handler; this is buggy with some shells; # see <1285349658-3122-1-git-send-email-loic.minier@linaro.org> -trap "rm -f $TMPC $TMPO $TMPE" EXIT INT QUIT TERM +trap "rm -f $TMPC $TMPO $TMPCXX $TMPE" EXIT INT QUIT TERM rm -f config.log # Print a helpful header at the top of config.log @@ -54,10 +55,13 @@ error_exit() { exit 1 } -do_cc() { - # Run the compiler, capturing its output to the log. - echo $cc "$@" >> config.log - $cc "$@" >> config.log 2>&1 || return $? +do_compiler() { + # Run the compiler, capturing its output to the log. First argument + # is compiler binary to execute. + local compiler="$1" + shift + echo $compiler "$@" >> config.log + $compiler "$@" >> config.log 2>&1 || return $? # Test passed. If this is an --enable-werror build, rerun # the test with -Werror and bail out if it fails. This # makes warning-generating-errors in configure test code @@ -71,14 +75,39 @@ do_cc() { return 0 ;; esac - echo $cc -Werror "$@" >> config.log - $cc -Werror "$@" >> config.log 2>&1 && return $? + echo $compiler -Werror "$@" >> config.log + $compiler -Werror "$@" >> config.log 2>&1 && return $? error_exit "configure test passed without -Werror but failed with -Werror." \ "This is probably a bug in the configure script. The failing command" \ "will be at the bottom of config.log." \ "You can run configure with --disable-werror to bypass this check." } +do_cc() { + do_compiler "$cc" "$@" +} + +do_cxx() { + do_compiler "$cxx" "$@" +} + +update_cxxflags() { + # Set QEMU_CXXFLAGS from QEMU_CFLAGS by filtering out those + # options which some versions of GCC's C++ compiler complain about + # because they only make sense for C programs. + QEMU_CXXFLAGS= + for arg in $QEMU_CFLAGS; do + case $arg in + -Wstrict-prototypes|-Wmissing-prototypes|-Wnested-externs|\ + -Wold-style-declaration|-Wold-style-definition|-Wredundant-decls) + ;; + *) + QEMU_CXXFLAGS=${QEMU_CXXFLAGS:+$QEMU_CXXFLAGS }$arg + ;; + esac + done +} + compile_object() { do_cc $QEMU_CFLAGS -c -o $TMPO $TMPC } @@ -207,6 +236,7 @@ fdt="" netmap="no" pixman="" sdl="" +sdlabi="1.2" virtfs="" vnc="yes" sparse="no" @@ -283,6 +313,8 @@ libusb="" usb_redir="" glx="" zlib="yes" +lzo="no" +snappy="no" guest_agent="" guest_agent_with_vss="no" vss_win32_sdk="" @@ -364,12 +396,13 @@ query_pkg_config() { } pkg_config=query_pkg_config sdl_config="${SDL_CONFIG-${cross_prefix}sdl-config}" +sdl2_config="${SDL2_CONFIG-${cross_prefix}sdl2-config}" # If the user hasn't specified ARFLAGS, default to 'rv', just as make does. ARFLAGS="${ARFLAGS-rv}" # default flags for all hosts -QEMU_CFLAGS="-fno-strict-aliasing $QEMU_CFLAGS" +QEMU_CFLAGS="-fno-strict-aliasing -fno-common $QEMU_CFLAGS" QEMU_CFLAGS="-Wall -Wundef -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS" QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS" QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS" @@ -773,6 +806,8 @@ for opt do ;; --enable-sdl) sdl="yes" ;; + --with-sdlabi=*) sdlabi="$optarg" + ;; --disable-qom-cast-debug) qom_cast_debug="no" ;; --enable-qom-cast-debug) qom_cast_debug="yes" @@ -995,6 +1030,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" @@ -1190,6 +1229,7 @@ Advanced options (experts only): --disable-werror disable compilation abort on warning --disable-sdl disable SDL --enable-sdl enable SDL + --with-sdlabi select preferred SDL ABI 1.2 or 2.0 --disable-gtk disable gtk UI --enable-gtk enable gtk UI --disable-virtfs disable VirtFS @@ -1289,6 +1329,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 @@ -1327,6 +1369,19 @@ if test "$ARCH" = "unknown"; then fi fi +# Consult white-list to determine whether to enable werror +# by default. Only enable by default for git builds +z_version=`cut -f3 -d. $source_path/VERSION` + +if test -z "$werror" ; then + if test -d "$source_path/.git" -a \ + "$linux" = "yes" ; then + werror="yes" + else + werror="no" + fi +fi + # check that the C compiler works. cat > $TMPC <<EOF int main(void) { return 0; } @@ -1347,14 +1402,16 @@ EOF compile_object - cat > $TMPC <<EOF + cat > $TMPCXX <<EOF extern "C" { int c_function(void); } int c_function(void) { return 42; } EOF - if (cc=$cxx do_cc $QEMU_CFLAGS -o $TMPE $TMPC $TMPO $LDFLAGS); then + update_cxxflags + + if do_cxx $QEMU_CXXFLAGS -o $TMPE $TMPCXX $TMPO $LDFLAGS; then # C++ compiler $cxx works ok with C compiler $cc : else @@ -1367,19 +1424,6 @@ else cxx= fi -# Consult white-list to determine whether to enable werror -# by default. Only enable by default for git builds -z_version=`cut -f3 -d. $source_path/VERSION` - -if test -z "$werror" ; then - if test -d "$source_path/.git" -a \ - "$linux" = "yes" ; then - werror="yes" - else - werror="no" - fi -fi - gcc_flags="-Wold-style-declaration -Wold-style-definition -Wtype-limits" gcc_flags="-Wformat-security -Wformat-y2k -Winit-self -Wignored-qualifiers $gcc_flags" gcc_flags="-Wmissing-include-dirs -Wempty-body -Wnested-externs $gcc_flags" @@ -1657,7 +1701,43 @@ EOF "Make sure to have the zlib libs and headers installed." fi fi -libs_softmmu="$libs_softmmu -lz" +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 @@ -1911,12 +1991,22 @@ fi # Look for sdl configuration program (pkg-config or sdl-config). Try # sdl-config even without cross prefix, and favour pkg-config over sdl-config. -if test "`basename $sdl_config`" != sdl-config && ! has ${sdl_config}; then - sdl_config=sdl-config + +if test $sdlabi = "2.0"; then + sdl_config=$sdl2_config + sdlname=sdl2 + sdlconfigname=sdl2_config +else + sdlname=sdl + sdlconfigname=sdl_config +fi + +if test "`basename $sdl_config`" != $sdlconfigname && ! has ${sdl_config}; then + sdl_config=$sdlconfigname fi -if $pkg_config sdl --exists; then - sdlconfig="$pkg_config sdl" +if $pkg_config $sdlname --exists; then + sdlconfig="$pkg_config $sdlname" _sdlversion=`$sdlconfig --modversion 2>/dev/null | sed 's/[^0-9]//g'` elif has ${sdl_config}; then sdlconfig="$sdl_config" @@ -2249,13 +2339,21 @@ EOF fi ########################################## -# netmap headers probe +# netmap support probe +# Apart from looking for netmap headers, we make sure that the host API version +# supports the netmap backend (>=11). The upper bound (15) is meant to simulate +# a minor/major version number. Minor new features will be marked with values up +# to 15, and if something happens that requires a change to the backend we will +# move above 15, submit the backend fixes and modify this two bounds. if test "$netmap" != "no" ; then cat > $TMPC << EOF #include <inttypes.h> #include <net/if.h> #include <net/netmap.h> #include <net/netmap_user.h> +#if (NETMAP_API < 11) || (NETMAP_API > 15) +#error +#endif int main(void) { return 0; } EOF if compile_prog "" "" ; then @@ -4037,6 +4135,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" @@ -4360,6 +4460,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/cpu-exec.c b/cpu-exec.c index 8943493001..1b0f617c19 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -477,7 +477,7 @@ int cpu_exec(CPUArchState *env) } #elif defined(TARGET_ARM) if (interrupt_request & CPU_INTERRUPT_FIQ - && !(env->uncached_cpsr & CPSR_F)) { + && !(env->daif & PSTATE_F)) { env->exception_index = EXCP_FIQ; cc->do_interrupt(cpu); next_tb = 0; @@ -493,7 +493,7 @@ int cpu_exec(CPUArchState *env) pc contains a magic address. */ if (interrupt_request & CPU_INTERRUPT_HARD && ((IS_M(env) && env->regs[15] < 0xfffffff0) - || !(env->uncached_cpsr & CPSR_I))) { + || !(env->daif & PSTATE_I))) { env->exception_index = EXCP_IRQ; cc->do_interrupt(cpu); next_tb = 0; diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index e5f9d36913..07c51ced1f 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -47,4 +47,5 @@ CONFIG_E500=y CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM)) # For PReP CONFIG_MC146818RTC=y +CONFIG_ETSEC=y CONFIG_ISA_TESTDEV=y diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak index 81fbc68654..d843dc0d57 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 8fc93dd643..ab796b3d4f 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 00f95154dd..145402e078 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 } } diff --git a/docs/rdma.txt b/docs/rdma.txt index 2aca63bd72..1f5d9e9fe4 100644 --- a/docs/rdma.txt +++ b/docs/rdma.txt @@ -66,7 +66,7 @@ bulk-phase round of the migration and can be enabled for extremely high-performance RDMA hardware using the following command: QEMU Monitor Command: -$ migrate_set_capability x-rdma-pin-all on # disabled by default +$ migrate_set_capability rdma-pin-all on # disabled by default Performing this action will cause all 8GB to be pinned, so if that's not what you want, then please ignore this step altogether. @@ -93,12 +93,12 @@ $ migrate_set_speed 40g # or whatever is the MAX of your RDMA device Next, on the destination machine, add the following to the QEMU command line: -qemu ..... -incoming x-rdma:host:port +qemu ..... -incoming rdma:host:port Finally, perform the actual migration on the source machine: QEMU Monitor Command: -$ migrate -d x-rdma:host:port +$ migrate -d rdma:host:port PERFORMANCE =========== @@ -120,8 +120,8 @@ For example, in the same 8GB RAM example with all 8GB of memory in active use and the VM itself is completely idle using the same 40 gbps infiniband link: -1. x-rdma-pin-all disabled total time: approximately 7.5 seconds @ 9.5 Gbps -2. x-rdma-pin-all enabled total time: approximately 4 seconds @ 26 Gbps +1. rdma-pin-all disabled total time: approximately 7.5 seconds @ 9.5 Gbps +2. rdma-pin-all enabled total time: approximately 4 seconds @ 26 Gbps These numbers would of course scale up to whatever size virtual machine you have to migrate using RDMA. @@ -407,18 +407,14 @@ socket is broken during a non-RDMA based migration. TODO: ===== -1. 'migrate x-rdma:host:port' and '-incoming x-rdma' options will be - renamed to 'rdma' after the experimental phase of this work has - completed upstream. -2. Currently, 'ulimit -l' mlock() limits as well as cgroups swap limits +1. Currently, 'ulimit -l' mlock() limits as well as cgroups swap limits are not compatible with infinband memory pinning and will result in an aborted migration (but with the source VM left unaffected). -3. Use of the recent /proc/<pid>/pagemap would likely speed up +2. Use of the recent /proc/<pid>/pagemap would likely speed up the use of KSM and ballooning while using RDMA. -4. Also, some form of balloon-device usage tracking would also +3. Also, some form of balloon-device usage tracking would also help alleviate some issues. -5. Move UNREGISTER requests to a separate thread. -6. Use LRU to provide more fine-grained direction of UNREGISTER +4. Use LRU to provide more fine-grained direction of UNREGISTER requests for unpinning memory in an overcommitted environment. -7. Expose UNREGISTER support to the user by way of workload-specific +5. Expose UNREGISTER support to the user by way of workload-specific hints about application behavior. diff --git a/dump.c b/dump.c index 80a9116c77..14b3d1d6ae 100644 --- a/dump.c +++ b/dump.c @@ -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; +} diff --git a/exec.c b/exec.c index b69fd295f9..680268a163 100644 --- a/exec.c +++ b/exec.c @@ -17,9 +17,7 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ #include "config.h" -#ifdef _WIN32 -#include <windows.h> -#else +#ifndef _WIN32 #include <sys/types.h> #include <sys/mman.h> #endif diff --git a/hmp.c b/hmp.c index e3ddd4654d..2f279c4ff2 100644 --- a/hmp.c +++ b/hmp.c @@ -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/arm/musicpal.c b/hw/arm/musicpal.c index 50a3b8fe4f..d10b5dbb49 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -92,8 +92,6 @@ #define MP_ETH_CRDP3 0x4AC #define MP_ETH_CTDP0 0x4E0 #define MP_ETH_CTDP1 0x4E4 -#define MP_ETH_CTDP2 0x4E8 -#define MP_ETH_CTDP3 0x4EC /* MII PHY access */ #define MP_ETH_SMIR_DATA 0x0000FFFF @@ -308,7 +306,7 @@ static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset, case MP_ETH_CRDP0 ... MP_ETH_CRDP3: return s->rx_queue[(offset - MP_ETH_CRDP0)/4]; - case MP_ETH_CTDP0 ... MP_ETH_CTDP3: + case MP_ETH_CTDP0 ... MP_ETH_CTDP1: return s->tx_queue[(offset - MP_ETH_CTDP0)/4]; default: @@ -362,7 +360,7 @@ static void mv88w8618_eth_write(void *opaque, hwaddr offset, s->cur_rx[(offset - MP_ETH_CRDP0)/4] = value; break; - case MP_ETH_CTDP0 ... MP_ETH_CTDP3: + case MP_ETH_CTDP0 ... MP_ETH_CTDP1: s->tx_queue[(offset - MP_ETH_CTDP0)/4] = value; break; } @@ -632,7 +630,7 @@ static int musicpal_lcd_init(SysBusDevice *sbd) "musicpal-lcd", MP_LCD_SIZE); sysbus_init_mmio(sbd, &s->iomem); - s->con = graphic_console_init(dev, &musicpal_gfx_ops, s); + s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s); qemu_console_resize(s->con, 128*3, 64*3); qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3); diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index 45a99c819d..904277a9da 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -272,11 +272,11 @@ static void pxa2xx_pwrmode_write(CPUARMState *env, const ARMCPRegInfo *ri, goto message; case 3: - s->cpu->env.uncached_cpsr = - ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I; + s->cpu->env.uncached_cpsr = ARM_CPU_MODE_SVC; + s->cpu->env.daif = PSTATE_A | PSTATE_F | PSTATE_I; s->cpu->env.cp15.c1_sys = 0; s->cpu->env.cp15.c1_coproc = 0; - s->cpu->env.cp15.c2_base0 = 0; + s->cpu->env.cp15.ttbr0_el1 = 0; s->cpu->env.cp15.c3 = 0; s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */ s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */ diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index 986f2a9c92..a67ca91ca7 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -157,6 +157,9 @@ struct HDAAudioStream { uint32_t bpos; }; +#define TYPE_HDA_AUDIO "hda-audio" +#define HDA_AUDIO(obj) OBJECT_CHECK(HDAAudioState, (obj), TYPE_HDA_AUDIO) + struct HDAAudioState { HDACodecDevice hda; const char *name; @@ -288,7 +291,7 @@ static void hda_audio_setup(HDAAudioStream *st) static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data) { - HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); + HDAAudioState *a = HDA_AUDIO(hda); HDAAudioStream *st; const desc_node *node = NULL; const desc_param *param; @@ -448,7 +451,7 @@ fail: static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, bool output) { - HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); + HDAAudioState *a = HDA_AUDIO(hda); int s; a->running_compat[stnr] = running; @@ -469,7 +472,7 @@ static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, b static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc) { - HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); + HDAAudioState *a = HDA_AUDIO(hda); HDAAudioStream *st; const desc_node *node; const desc_param *param; @@ -514,7 +517,7 @@ static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc) static int hda_audio_exit(HDACodecDevice *hda) { - HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); + HDAAudioState *a = HDA_AUDIO(hda); HDAAudioStream *st; int i; @@ -561,7 +564,7 @@ static int hda_audio_post_load(void *opaque, int version) static void hda_audio_reset(DeviceState *dev) { - HDAAudioState *a = DO_UPCAST(HDAAudioState, hda.qdev, dev); + HDAAudioState *a = HDA_AUDIO(dev); HDAAudioStream *st; int i; @@ -613,7 +616,7 @@ static Property hda_audio_properties[] = { static int hda_audio_init_output(HDACodecDevice *hda) { - HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); + HDAAudioState *a = HDA_AUDIO(hda); if (!a->mixer) { return hda_audio_init(hda, &output_nomixemu); @@ -624,7 +627,7 @@ static int hda_audio_init_output(HDACodecDevice *hda) static int hda_audio_init_duplex(HDACodecDevice *hda) { - HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); + HDAAudioState *a = HDA_AUDIO(hda); if (!a->mixer) { return hda_audio_init(hda, &duplex_nomixemu); @@ -635,7 +638,7 @@ static int hda_audio_init_duplex(HDACodecDevice *hda) static int hda_audio_init_micro(HDACodecDevice *hda) { - HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); + HDAAudioState *a = HDA_AUDIO(hda); if (!a->mixer) { return hda_audio_init(hda, µ_nomixemu); @@ -644,25 +647,39 @@ static int hda_audio_init_micro(HDACodecDevice *hda) } } -static void hda_audio_output_class_init(ObjectClass *klass, void *data) +static void hda_audio_base_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); - k->init = hda_audio_init_output; k->exit = hda_audio_exit; k->command = hda_audio_command; k->stream = hda_audio_stream; set_bit(DEVICE_CATEGORY_SOUND, dc->categories); - dc->desc = "HDA Audio Codec, output-only (line-out)"; dc->reset = hda_audio_reset; dc->vmsd = &vmstate_hda_audio; dc->props = hda_audio_properties; } +static const TypeInfo hda_audio_info = { + .name = TYPE_HDA_AUDIO, + .parent = TYPE_HDA_CODEC_DEVICE, + .class_init = hda_audio_base_class_init, + .abstract = true, +}; + +static void hda_audio_output_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); + + k->init = hda_audio_init_output; + dc->desc = "HDA Audio Codec, output-only (line-out)"; +} + static const TypeInfo hda_audio_output_info = { .name = "hda-output", - .parent = TYPE_HDA_CODEC_DEVICE, + .parent = TYPE_HDA_AUDIO, .instance_size = sizeof(HDAAudioState), .class_init = hda_audio_output_class_init, }; @@ -673,19 +690,12 @@ static void hda_audio_duplex_class_init(ObjectClass *klass, void *data) HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); k->init = hda_audio_init_duplex; - k->exit = hda_audio_exit; - k->command = hda_audio_command; - k->stream = hda_audio_stream; - set_bit(DEVICE_CATEGORY_SOUND, dc->categories); dc->desc = "HDA Audio Codec, duplex (line-out, line-in)"; - dc->reset = hda_audio_reset; - dc->vmsd = &vmstate_hda_audio; - dc->props = hda_audio_properties; } static const TypeInfo hda_audio_duplex_info = { .name = "hda-duplex", - .parent = TYPE_HDA_CODEC_DEVICE, + .parent = TYPE_HDA_AUDIO, .instance_size = sizeof(HDAAudioState), .class_init = hda_audio_duplex_class_init, }; @@ -696,25 +706,19 @@ static void hda_audio_micro_class_init(ObjectClass *klass, void *data) HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); k->init = hda_audio_init_micro; - k->exit = hda_audio_exit; - k->command = hda_audio_command; - k->stream = hda_audio_stream; - set_bit(DEVICE_CATEGORY_SOUND, dc->categories); dc->desc = "HDA Audio Codec, duplex (speaker, microphone)"; - dc->reset = hda_audio_reset; - dc->vmsd = &vmstate_hda_audio; - dc->props = hda_audio_properties; } static const TypeInfo hda_audio_micro_info = { .name = "hda-micro", - .parent = TYPE_HDA_CODEC_DEVICE, + .parent = TYPE_HDA_AUDIO, .instance_size = sizeof(HDAAudioState), .class_init = hda_audio_micro_class_init, }; static void hda_audio_register_types(void) { + type_register_static(&hda_audio_info); type_register_static(&hda_audio_output_info); type_register_static(&hda_audio_duplex_info); type_register_static(&hda_audio_micro_info); diff --git a/hw/core/loader.c b/hw/core/loader.c index e1c3f3a860..b323c0c7b8 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -284,12 +284,30 @@ static void *load_at(int fd, int offset, int size) #define SZ 64 #include "hw/elf_ops.h" +const char *load_elf_strerror(int error) +{ + switch (error) { + case 0: + return "No error"; + case ELF_LOAD_FAILED: + return "Failed to load ELF"; + case ELF_LOAD_NOT_ELF: + return "The image is not ELF"; + case ELF_LOAD_WRONG_ARCH: + return "The image is from incompatible architecture"; + case ELF_LOAD_WRONG_ENDIAN: + return "The image has incorrect endianness"; + default: + return "Unknown error"; + } +} + /* return < 0 if error, otherwise the number of bytes loaded in memory */ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, int big_endian, int elf_machine, int clear_lsb) { - int fd, data_order, target_data_order, must_swab, ret; + int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED; uint8_t e_ident[EI_NIDENT]; fd = open(filename, O_RDONLY | O_BINARY); @@ -302,8 +320,10 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), if (e_ident[0] != ELFMAG0 || e_ident[1] != ELFMAG1 || e_ident[2] != ELFMAG2 || - e_ident[3] != ELFMAG3) + e_ident[3] != ELFMAG3) { + ret = ELF_LOAD_NOT_ELF; goto fail; + } #ifdef HOST_WORDS_BIGENDIAN data_order = ELFDATA2MSB; #else @@ -317,6 +337,7 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), } if (target_data_order != e_ident[EI_DATA]) { + ret = ELF_LOAD_WRONG_ENDIAN; goto fail; } @@ -329,12 +350,9 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), pentry, lowaddr, highaddr, elf_machine, clear_lsb); } - close(fd); - return ret; - fail: close(fd); - return -1; + return ret; } static void bswap_uboot_header(uboot_image_header_t *hdr) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index c0b857fbd4..380976a066 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -440,27 +440,33 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id) static void qbus_realize(BusState *bus, DeviceState *parent, const char *name) { const char *typename = object_get_typename(OBJECT(bus)); + BusClass *bc; char *buf; - int i,len; + int i, len, bus_id; bus->parent = parent; if (name) { bus->name = g_strdup(name); } else if (bus->parent && bus->parent->id) { - /* parent device has id -> use it for bus name */ + /* parent device has id -> use it plus parent-bus-id for bus name */ + bus_id = bus->parent->num_child_bus; + len = strlen(bus->parent->id) + 16; buf = g_malloc(len); - snprintf(buf, len, "%s.%d", bus->parent->id, bus->parent->num_child_bus); + snprintf(buf, len, "%s.%d", bus->parent->id, bus_id); bus->name = buf; } else { - /* no id -> use lowercase bus type for bus name */ + /* no id -> use lowercase bus type plus global bus-id for bus name */ + bc = BUS_GET_CLASS(bus); + bus_id = bc->automatic_ids++; + len = strlen(typename) + 16; buf = g_malloc(len); - len = snprintf(buf, len, "%s.%d", typename, - bus->parent ? bus->parent->num_child_bus : 0); - for (i = 0; i < len; i++) + len = snprintf(buf, len, "%s.%d", typename, bus_id); + for (i = 0; i < len; i++) { buf[i] = qemu_tolower(buf[i]); + } bus->name = buf; } diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index 540df82600..7ed76a9c24 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/blizzard.c b/hw/display/blizzard.c index 4a466c8323..55c0ddf00b 100644 --- a/hw/display/blizzard.c +++ b/hw/display/blizzard.c @@ -956,7 +956,7 @@ void *s1d13745_init(qemu_irq gpio_int) s->fb = g_malloc(0x180000); - s->con = graphic_console_init(NULL, &blizzard_ops, s); + s->con = graphic_console_init(NULL, 0, &blizzard_ops, s); surface = qemu_console_surface(s->con); switch (surface_bits_per_pixel(surface)) { diff --git a/hw/display/cg3.c b/hw/display/cg3.c new file mode 100644 index 0000000000..a042b9ecbe --- /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), 0, &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/display/cirrus_vga.c b/hw/display/cirrus_vga.c index 3a8fc0bf8e..0d3127da21 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -2917,7 +2917,7 @@ static void isa_cirrus_vga_realizefn(DeviceState *dev, Error **errp) cirrus_init_common(&d->cirrus_vga, OBJECT(dev), CIRRUS_ID_CLGD5430, 0, isa_address_space(isadev), isa_address_space_io(isadev)); - s->con = graphic_console_init(dev, s->hw_ops, s); + s->con = graphic_console_init(dev, 0, s->hw_ops, s); rom_add_vga(VGABIOS_CIRRUS_FILENAME); /* XXX ISA-LFB support */ /* FIXME not qdev yet */ @@ -2963,7 +2963,7 @@ static int pci_cirrus_vga_initfn(PCIDevice *dev) vga_common_init(&s->vga, OBJECT(dev)); cirrus_init_common(s, OBJECT(dev), device_id, 1, pci_address_space(dev), pci_address_space_io(dev)); - s->vga.con = graphic_console_init(DEVICE(dev), s->vga.hw_ops, &s->vga); + s->vga.con = graphic_console_init(DEVICE(dev), 0, s->vga.hw_ops, &s->vga); /* setup PCI */ diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index 65cca1d707..9750330c25 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -1917,7 +1917,7 @@ static int exynos4210_fimd_init(SysBusDevice *dev) memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_fimd_mmio_ops, s, "exynos4210.fimd", FIMD_REGS_SIZE); sysbus_init_mmio(dev, &s->iomem); - s->console = graphic_console_init(DEVICE(dev), &exynos4210_fimd_ops, s); + s->console = graphic_console_init(DEVICE(dev), 0, &exynos4210_fimd_ops, s); return 0; } diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c index bc909bb3de..5c6a2d3605 100644 --- a/hw/display/g364fb.c +++ b/hw/display/g364fb.c @@ -484,7 +484,7 @@ static void g364fb_init(DeviceState *dev, G364State *s) { s->vram = g_malloc0(s->vram_size); - s->con = graphic_console_init(dev, &g364fb_ops, s); + s->con = graphic_console_init(dev, 0, &g364fb_ops, s); memory_region_init_io(&s->mem_ctrl, NULL, &g364fb_ctrl_ops, s, "ctrl", 0x180000); memory_region_init_ram_ptr(&s->mem_vram, NULL, "vram", diff --git a/hw/display/jazz_led.c b/hw/display/jazz_led.c index 8407e6c2ef..f9e7d7c981 100644 --- a/hw/display/jazz_led.c +++ b/hw/display/jazz_led.c @@ -271,7 +271,7 @@ static int jazz_led_init(SysBusDevice *dev) memory_region_init_io(&s->iomem, OBJECT(s), &led_ops, s, "led", 1); sysbus_init_mmio(dev, &s->iomem); - s->con = graphic_console_init(DEVICE(dev), &jazz_led_ops, s); + s->con = graphic_console_init(DEVICE(dev), 0, &jazz_led_ops, s); return 0; } diff --git a/hw/display/milkymist-vgafb.c b/hw/display/milkymist-vgafb.c index 5150cb48b7..603537aabb 100644 --- a/hw/display/milkymist-vgafb.c +++ b/hw/display/milkymist-vgafb.c @@ -290,7 +290,7 @@ static int milkymist_vgafb_init(SysBusDevice *dev) "milkymist-vgafb", R_MAX * 4); sysbus_init_mmio(dev, &s->regs_region); - s->con = graphic_console_init(DEVICE(dev), &vgafb_ops, s); + s->con = graphic_console_init(DEVICE(dev), 0, &vgafb_ops, s); return 0; } diff --git a/hw/display/omap_lcdc.c b/hw/display/omap_lcdc.c index c3b9b68971..fda81baff0 100644 --- a/hw/display/omap_lcdc.c +++ b/hw/display/omap_lcdc.c @@ -406,7 +406,7 @@ struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem, memory_region_init_io(&s->iomem, NULL, &omap_lcdc_ops, s, "omap.lcdc", 0x100); memory_region_add_subregion(sysmem, base, &s->iomem); - s->con = graphic_console_init(NULL, &omap_ops, s); + s->con = graphic_console_init(NULL, 0, &omap_ops, s); return s; } diff --git a/hw/display/pl110.c b/hw/display/pl110.c index ab689e9aae..c574cf1a81 100644 --- a/hw/display/pl110.c +++ b/hw/display/pl110.c @@ -464,7 +464,7 @@ static int pl110_initfn(SysBusDevice *sbd) sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1); - s->con = graphic_console_init(dev, &pl110_gfx_ops, s); + s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s); return 0; } diff --git a/hw/display/pxa2xx_lcd.c b/hw/display/pxa2xx_lcd.c index 990931ae45..09cdf17ab9 100644 --- a/hw/display/pxa2xx_lcd.c +++ b/hw/display/pxa2xx_lcd.c @@ -1013,7 +1013,7 @@ PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem, "pxa2xx-lcd-controller", 0x00100000); memory_region_add_subregion(sysmem, base, &s->iomem); - s->con = graphic_console_init(NULL, &pxa2xx_ops, s); + s->con = graphic_console_init(NULL, 0, &pxa2xx_ops, s); surface = qemu_console_surface(s->con); switch (surface_bits_per_pixel(surface)) { diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 2a559ebcc9..47bbf1f1fe 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -2069,7 +2069,7 @@ static int qxl_init_primary(PCIDevice *dev) portio_list_set_flush_coalesced(qxl_vga_port_list); portio_list_add(qxl_vga_port_list, pci_address_space_io(dev), 0x3b0); - vga->con = graphic_console_init(DEVICE(dev), &qxl_ops, qxl); + vga->con = graphic_console_init(DEVICE(dev), 0, &qxl_ops, qxl); qemu_spice_display_init_common(&qxl->ssd); rc = qxl_init_common(qxl); @@ -2094,7 +2094,7 @@ static int qxl_init_secondary(PCIDevice *dev) qxl->vga.vram_size); vmstate_register_ram(&qxl->vga.vram, &qxl->pci.qdev); qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram); - qxl->vga.con = graphic_console_init(DEVICE(dev), &qxl_ops, qxl); + qxl->vga.con = graphic_console_init(DEVICE(dev), 0, &qxl_ops, qxl); return qxl_init_common(qxl); } diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 0b5f993594..eedf2d48e0 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -1449,5 +1449,5 @@ void sm501_init(MemoryRegion *address_space_mem, uint32_t base, } /* create qemu graphic console */ - s->con = graphic_console_init(DEVICE(dev), &sm501_ops, s); + s->con = graphic_console_init(DEVICE(dev), 0, &sm501_ops, s); } diff --git a/hw/display/ssd0303.c b/hw/display/ssd0303.c index 89804e108b..c2eea04934 100644 --- a/hw/display/ssd0303.c +++ b/hw/display/ssd0303.c @@ -299,7 +299,7 @@ static int ssd0303_init(I2CSlave *i2c) { ssd0303_state *s = SSD0303(i2c); - s->con = graphic_console_init(DEVICE(i2c), &ssd0303_ops, s); + s->con = graphic_console_init(DEVICE(i2c), 0, &ssd0303_ops, s); qemu_console_resize(s->con, 96 * MAGNIFY, 16 * MAGNIFY); return 0; } diff --git a/hw/display/ssd0323.c b/hw/display/ssd0323.c index c3231c6116..46c3b40c79 100644 --- a/hw/display/ssd0323.c +++ b/hw/display/ssd0323.c @@ -342,7 +342,7 @@ static int ssd0323_init(SSISlave *dev) s->col_end = 63; s->row_end = 79; - s->con = graphic_console_init(DEVICE(dev), &ssd0323_ops, s); + s->con = graphic_console_init(DEVICE(dev), 0, &ssd0323_ops, s); qemu_console_resize(s->con, 128 * MAGNIFY, 64 * MAGNIFY); qdev_init_gpio_in(&dev->qdev, ssd0323_cd, 1); diff --git a/hw/display/tc6393xb.c b/hw/display/tc6393xb.c index 3dd9b98eca..f4011d2db0 100644 --- a/hw/display/tc6393xb.c +++ b/hw/display/tc6393xb.c @@ -587,7 +587,7 @@ TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq) memory_region_add_subregion(sysmem, base + 0x100000, &s->vram); s->scr_width = 480; s->scr_height = 640; - s->con = graphic_console_init(NULL, &tc6393xb_gfx_ops, s); + s->con = graphic_console_init(NULL, 0, &tc6393xb_gfx_ops, s); return s; } diff --git a/hw/display/tcx.c b/hw/display/tcx.c index e60769c2c9..2b37ffac4c 100644 --- a/hw/display/tcx.c +++ b/hw/display/tcx.c @@ -602,14 +602,14 @@ static int tcx_init1(SysBusDevice *dev) &s->vram_mem, vram_offset, size); sysbus_init_mmio(dev, &s->vram_cplane); - s->con = graphic_console_init(DEVICE(dev), &tcx24_ops, s); + s->con = graphic_console_init(DEVICE(dev), 0, &tcx24_ops, s); } else { /* THC 8 bit (dummy) */ memory_region_init_io(&s->thc8, OBJECT(s), &dummy_ops, s, "tcx.thc8", TCX_THC_NREGS_8); sysbus_init_mmio(dev, &s->thc8); - s->con = graphic_console_init(DEVICE(dev), &tcx_ops, s); + s->con = graphic_console_init(DEVICE(dev), 0, &tcx_ops, s); } qemu_console_resize(s->con, s->width, s->height); diff --git a/hw/display/vga-isa-mm.c b/hw/display/vga-isa-mm.c index 8b514cc39d..afc46b8c9d 100644 --- a/hw/display/vga-isa-mm.c +++ b/hw/display/vga-isa-mm.c @@ -135,7 +135,7 @@ int isa_vga_mm_init(hwaddr vram_base, vga_common_init(&s->vga, NULL); vga_mm_init(s, vram_base, ctrl_base, it_shift, address_space); - s->vga.con = graphic_console_init(NULL, s->vga.hw_ops, s); + s->vga.con = graphic_console_init(NULL, 0, s->vga.hw_ops, s); vga_init_vbe(&s->vga, NULL, address_space); return 0; diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c index c2a19ad6ba..1d9ea6b51d 100644 --- a/hw/display/vga-isa.c +++ b/hw/display/vga-isa.c @@ -67,7 +67,7 @@ static void vga_isa_realizefn(DeviceState *dev, Error **errp) isa_mem_base + 0x000a0000, vga_io_memory, 1); memory_region_set_coalescing(vga_io_memory); - s->con = graphic_console_init(DEVICE(dev), s->hw_ops, s); + s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s); vga_init_vbe(s, OBJECT(dev), isa_address_space(isadev)); /* ROM BIOS */ diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index f74fc43aa6..574ea0e7f9 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -151,7 +151,7 @@ static int pci_std_vga_initfn(PCIDevice *dev) vga_init(s, OBJECT(dev), pci_address_space(dev), pci_address_space_io(dev), true); - s->con = graphic_console_init(DEVICE(dev), s->hw_ops, s); + s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s); /* XXX: VGA_RAM_SIZE must be a power of two */ pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index 334e71856e..bd2c108c42 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1199,7 +1199,7 @@ static void vmsvga_init(DeviceState *dev, struct vmsvga_state_s *s, s->scratch_size = SVGA_SCRATCH_SIZE; s->scratch = g_malloc(s->scratch_size * 4); - s->vga.con = graphic_console_init(dev, &vmsvga_ops, s); + s->vga.con = graphic_console_init(dev, 0, &vmsvga_ops, s); s->fifo_size = SVGA_FIFO_SIZE; memory_region_init_ram(&s->fifo_ram, NULL, "vmsvga.fifo", s->fifo_size); diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index cb9d456814..032eb7a9a5 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -992,7 +992,7 @@ wait_more: /* vfb */ fb = container_of(xfb, struct XenFB, c.xendev); - fb->c.con = graphic_console_init(NULL, &xenfb_ops, fb); + fb->c.con = graphic_console_init(NULL, 0, &xenfb_ops, fb); fb->have_console = 1; /* vkbd */ diff --git a/hw/dma/pl330.c b/hw/dma/pl330.c index 401399d330..608a58c47d 100644 --- a/hw/dma/pl330.c +++ b/hw/dma/pl330.c @@ -227,7 +227,8 @@ static const VMStateDescription vmstate_pl330_queue = { }; struct PL330State { - SysBusDevice busdev; + SysBusDevice parent_obj; + MemoryRegion iomem; qemu_irq irq_abort; qemu_irq *irq; @@ -577,7 +578,7 @@ static inline void pl330_queue_remove_tagged(PL330Queue *s, uint8_t tag) static inline void pl330_fault(PL330Chan *ch, uint32_t flags) { - DB_PRINT("ch: %p, flags: %x\n", ch, flags); + DB_PRINT("ch: %p, flags: %" PRIx32 "\n", ch, flags); ch->fault_type |= flags; if (ch->state == pl330_chan_fault) { return; @@ -600,10 +601,12 @@ static inline void pl330_fault(PL330Chan *ch, uint32_t flags) * LEN - number of elements in ARGS array */ -static void pl330_dmaaddh(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) +static void pl330_dmaadxh(PL330Chan *ch, uint8_t *args, bool ra, bool neg) { - uint16_t im = (((uint16_t)args[1]) << 8) | ((uint16_t)args[0]); - uint8_t ra = (opcode >> 1) & 1; + uint32_t im = (args[1] << 8) | args[0]; + if (neg) { + im |= 0xffffu << 16; + } if (ch->is_manager) { pl330_fault(ch, PL330_FAULT_UNDEF_INSTR); @@ -616,6 +619,16 @@ static void pl330_dmaaddh(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) } } +static void pl330_dmaaddh(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) +{ + pl330_dmaadxh(ch, args, extract32(opcode, 1, 1), false); +} + +static void pl330_dmaadnh(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) +{ + pl330_dmaadxh(ch, args, extract32(opcode, 1, 1), true); +} + static void pl330_dmaend(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) { @@ -723,7 +736,8 @@ static void pl330_dmald(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) ch->stall = pl330_queue_put_insn(&ch->parent->read_queue, ch->src, size, num, inc, 0, ch->tag); if (!ch->stall) { - DB_PRINT("channel:%d address:%08x size:%d num:%d %c\n", + DB_PRINT("channel:%" PRId8 " address:%08" PRIx32 " size:%" PRIx32 + " num:%" PRId32 " %c\n", ch->tag, ch->src, size, num, inc ? 'Y' : 'N'); ch->src += inc ? size * num - (ch->src & (size - 1)) : 0; } @@ -868,9 +882,10 @@ static void pl330_dmasev(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) } if (ch->parent->inten & (1 << ev_id)) { ch->parent->int_status |= (1 << ev_id); - DB_PRINT("event interrupt raised %d\n", ev_id); + DB_PRINT("event interrupt raised %" PRId8 "\n", ev_id); qemu_irq_raise(ch->parent->irq[ev_id]); } + DB_PRINT("event raised %" PRId8 "\n", ev_id); ch->parent->ev_status |= (1 << ev_id); } @@ -895,7 +910,8 @@ static void pl330_dmast(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) ch->stall = pl330_queue_put_insn(&ch->parent->write_queue, ch->dst, size, num, inc, 0, ch->tag); if (!ch->stall) { - DB_PRINT("channel:%d address:%08x size:%d num:%d %c\n", + DB_PRINT("channel:%" PRId8 " address:%08" PRIx32 " size:%" PRIx32 + " num:%" PRId32 " %c\n", ch->tag, ch->dst, size, num, inc ? 'Y' : 'N'); ch->dst += inc ? size * num - (ch->dst & (size - 1)) : 0; } @@ -972,6 +988,7 @@ static void pl330_dmawfe(PL330Chan *ch, uint8_t opcode, } } ch->parent->ev_status &= ~(1 << ev_id); + DB_PRINT("event lowered %" PRIx8 "\n", ev_id); } else { ch->stall = 1; } @@ -1037,6 +1054,7 @@ static void pl330_dmawmb(PL330Chan *ch, uint8_t opcode, /* NULL terminated array of the instruction descriptions. */ static const PL330InsnDesc insn_desc[] = { { .opcode = 0x54, .opmask = 0xFD, .size = 3, .exec = pl330_dmaaddh, }, + { .opcode = 0x5c, .opmask = 0xFD, .size = 3, .exec = pl330_dmaadnh, }, { .opcode = 0x00, .opmask = 0xFF, .size = 1, .exec = pl330_dmaend, }, { .opcode = 0x35, .opmask = 0xFF, .size = 2, .exec = pl330_dmaflushp, }, { .opcode = 0xA0, .opmask = 0xFD, .size = 6, .exec = pl330_dmago, }, @@ -1108,7 +1126,6 @@ static int pl330_chan_exec(PL330Chan *ch) ch->state != pl330_chan_waiting_periph && ch->state != pl330_chan_at_barrier && ch->state != pl330_chan_waiting_event) { - DB_PRINT("%d\n", ch->state); return 0; } ch->stall = 0; @@ -1155,7 +1172,7 @@ static int pl330_exec_cycle(PL330Chan *channel) dma_memory_read(&address_space_memory, q->addr, buf, len); if (PL330_ERR_DEBUG > 1) { - DB_PRINT("PL330 read from memory @%08x (size = %08x):\n", + DB_PRINT("PL330 read from memory @%08" PRIx32 " (size = %08x):\n", q->addr, len); qemu_hexdump((char *)buf, stderr, "", len); } @@ -1187,8 +1204,8 @@ static int pl330_exec_cycle(PL330Chan *channel) if (fifo_res == PL330_FIFO_OK || q->z) { dma_memory_write(&address_space_memory, q->addr, buf, len); if (PL330_ERR_DEBUG > 1) { - DB_PRINT("PL330 read from memory @%08x (size = %08x):\n", - q->addr, len); + DB_PRINT("PL330 read from memory @%08" PRIx32 + " (size = %08x):\n", q->addr, len); qemu_hexdump((char *)buf, stderr, "", len); } if (q->inc) { @@ -1277,7 +1294,7 @@ static void pl330_debug_exec(PL330State *s) args[2] = (s->dbg[1] >> 8) & 0xff; args[3] = (s->dbg[1] >> 16) & 0xff; args[4] = (s->dbg[1] >> 24) & 0xff; - DB_PRINT("chan id: %d\n", chan_id); + DB_PRINT("chan id: %" PRIx8 "\n", chan_id); if (s->dbg[0] & 1) { ch = &s->chan[chan_id]; } else { @@ -1311,7 +1328,7 @@ static void pl330_iomem_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { PL330State *s = (PL330State *) opaque; - uint32_t i; + int i; DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)value); @@ -1467,8 +1484,8 @@ static inline uint32_t pl330_iomem_read_imp(void *opaque, static uint64_t pl330_iomem_read(void *opaque, hwaddr offset, unsigned size) { - int ret = pl330_iomem_read_imp(opaque, offset); - DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, ret); + uint32_t ret = pl330_iomem_read_imp(opaque, offset); + DB_PRINT("addr: %08" HWADDR_PRIx " data: %08" PRIx32 "\n", offset, ret); return ret; } @@ -1554,7 +1571,7 @@ static void pl330_realize(DeviceState *dev, Error **errp) s->cfg[1] |= 5; break; default: - error_setg(errp, "Bad value for i-cache_len property: %d\n", + error_setg(errp, "Bad value for i-cache_len property: %" PRIx8 "\n", s->i_cache_len); return; } @@ -1589,7 +1606,7 @@ static void pl330_realize(DeviceState *dev, Error **errp) s->cfg[CFG_CRD] |= 0x4; break; default: - error_setg(errp, "Bad value for data_width property: %d\n", + error_setg(errp, "Bad value for data_width property: %" PRIx8 "\n", s->data_width); return; } @@ -1602,7 +1619,7 @@ static void pl330_realize(DeviceState *dev, Error **errp) pl330_queue_init(&s->read_queue, s->rd_q_dep, s); pl330_queue_init(&s->write_queue, s->wr_q_dep, s); - pl330_fifo_init(&s->fifo, s->data_buffer_dep); + pl330_fifo_init(&s->fifo, s->data_width / 4 * s->data_buffer_dep); } static Property pl330_properties[] = { diff --git a/hw/i386/kvm/pci-assign.c b/hw/i386/kvm/pci-assign.c index 968680104b..a825871d8a 100644 --- a/hw/i386/kvm/pci-assign.c +++ b/hw/i386/kvm/pci-assign.c @@ -743,6 +743,7 @@ static void assign_failed_examine(AssignedDevice *dev) goto fail; } + driver[r] = 0; ns = strrchr(driver, '/'); if (!ns) { goto fail; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index d5dc1ef336..ae1699d6db 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -221,10 +221,16 @@ static void pc_init1(QEMUMachineInitArgs *args, } else { for(i = 0; i < MAX_IDE_BUS; i++) { ISADevice *dev; + char busname[] = "ide.0"; dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], ide_irq[i], hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); - idebus[i] = qdev_get_child_bus(DEVICE(dev), "ide.0"); + /* + * The ide bus name is ide.0 for the first bus and ide.1 for the + * second one. + */ + busname[4] = '0' + i; + idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); } } diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h index 20e412c240..9a4064f892 100644 --- a/hw/ide/ahci.h +++ b/hw/ide/ahci.h @@ -40,7 +40,7 @@ #define AHCI_PORT_PRIV_DMA_SZ (AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_AR_SZ + \ AHCI_RX_FIS_SZ) -#define AHCI_IRQ_ON_SG (1 << 31) +#define AHCI_IRQ_ON_SG (1U << 31) #define AHCI_CMD_ATAPI (1 << 5) #define AHCI_CMD_WRITE (1 << 6) #define AHCI_CMD_PREFETCH (1 << 7) @@ -61,7 +61,7 @@ /* HOST_CTL bits */ #define HOST_CTL_RESET (1 << 0) /* reset controller; self-clear */ #define HOST_CTL_IRQ_EN (1 << 1) /* global IRQ enable */ -#define HOST_CTL_AHCI_EN (1 << 31) /* AHCI enabled */ +#define HOST_CTL_AHCI_EN (1U << 31) /* AHCI enabled */ /* HOST_CAP bits */ #define HOST_CAP_SSC (1 << 14) /* Slumber capable */ @@ -69,7 +69,7 @@ #define HOST_CAP_CLO (1 << 24) /* Command List Override support */ #define HOST_CAP_SSS (1 << 27) /* Staggered Spin-up */ #define HOST_CAP_NCQ (1 << 30) /* Native Command Queueing */ -#define HOST_CAP_64 (1 << 31) /* PCI DAC (64-bit DMA) support */ +#define HOST_CAP_64 (1U << 31) /* PCI DAC (64-bit DMA) support */ /* registers for each SATA port */ #define PORT_LST_ADDR 0x00 /* command list DMA addr */ @@ -89,7 +89,7 @@ #define PORT_RESERVED 0x3c /* reserved */ /* PORT_IRQ_{STAT,MASK} bits */ -#define PORT_IRQ_COLD_PRES (1 << 31) /* cold presence detect */ +#define PORT_IRQ_COLD_PRES (1U << 31) /* cold presence detect */ #define PORT_IRQ_TF_ERR (1 << 30) /* task file error */ #define PORT_IRQ_HBUS_ERR (1 << 29) /* host bus fatal error */ #define PORT_IRQ_HBUS_DATA_ERR (1 << 28) /* host bus data error */ @@ -151,7 +151,7 @@ #define PORT_IRQ_STAT_HBDS (1 << 28) /* Host Bus Data Error Status */ #define PORT_IRQ_STAT_HBFS (1 << 29) /* Host Bus Fatal Error Status */ #define PORT_IRQ_STAT_TFES (1 << 30) /* Task File Error Status */ -#define PORT_IRQ_STAT_CPDS (1 << 31) /* Code Port Detect Status */ +#define PORT_IRQ_STAT_CPDS (1U << 31) /* Code Port Detect Status */ /* ap->flags bits */ #define AHCI_FLAG_NO_NCQ (1 << 24) diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 60eb936e0d..c8a2318d56 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/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index 59a3da5a6b..100b6bf3de 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -3,6 +3,7 @@ * * Copyright (c) 2012 Linaro Limited * Written by Peter Maydell + * Save/Restore logic added by Christoffer Dall. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +24,20 @@ #include "kvm_arm.h" #include "gic_internal.h" +//#define DEBUG_GIC_KVM + +#ifdef DEBUG_GIC_KVM +static const int debug_gic_kvm = 1; +#else +static const int debug_gic_kvm = 0; +#endif + +#define DPRINTF(fmt, ...) do { \ + if (debug_gic_kvm) { \ + printf("arm_gic: " fmt , ## __VA_ARGS__); \ + } \ + } while (0) + #define TYPE_KVM_ARM_GIC "kvm-arm-gic" #define KVM_ARM_GIC(obj) \ OBJECT_CHECK(GICState, (obj), TYPE_KVM_ARM_GIC) @@ -72,14 +87,419 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level) kvm_set_irq(kvm_state, kvm_irq, !!level); } +static bool kvm_arm_gic_can_save_restore(GICState *s) +{ + return s->dev_fd >= 0; +} + +static void kvm_gic_access(GICState *s, int group, int offset, + int cpu, uint32_t *val, bool write) +{ + struct kvm_device_attr attr; + int type; + int err; + + cpu = cpu & 0xff; + + attr.flags = 0; + attr.group = group; + attr.attr = (((uint64_t)cpu << KVM_DEV_ARM_VGIC_CPUID_SHIFT) & + KVM_DEV_ARM_VGIC_CPUID_MASK) | + (((uint64_t)offset << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) & + KVM_DEV_ARM_VGIC_OFFSET_MASK); + attr.addr = (uintptr_t)val; + + if (write) { + type = KVM_SET_DEVICE_ATTR; + } else { + type = KVM_GET_DEVICE_ATTR; + } + + err = kvm_device_ioctl(s->dev_fd, type, &attr); + if (err < 0) { + fprintf(stderr, "KVM_{SET/GET}_DEVICE_ATTR failed: %s\n", + strerror(-err)); + abort(); + } +} + +static void kvm_gicd_access(GICState *s, int offset, int cpu, + uint32_t *val, bool write) +{ + kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, + offset, cpu, val, write); +} + +static void kvm_gicc_access(GICState *s, int offset, int cpu, + uint32_t *val, bool write) +{ + kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_CPU_REGS, + offset, cpu, val, write); +} + +#define for_each_irq_reg(_ctr, _max_irq, _field_width) \ + for (_ctr = 0; _ctr < ((_max_irq) / (32 / (_field_width))); _ctr++) + +/* + * Translate from the in-kernel field for an IRQ value to/from the qemu + * representation. + */ +typedef void (*vgic_translate_fn)(GICState *s, int irq, int cpu, + uint32_t *field, bool to_kernel); + +/* synthetic translate function used for clear/set registers to completely + * clear a setting using a clear-register before setting the remaing bits + * using a set-register */ +static void translate_clear(GICState *s, int irq, int cpu, + uint32_t *field, bool to_kernel) +{ + if (to_kernel) { + *field = ~0; + } else { + /* does not make sense: qemu model doesn't use set/clear regs */ + abort(); + } +} + +static void translate_enabled(GICState *s, int irq, int cpu, + uint32_t *field, bool to_kernel) +{ + int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; + + if (to_kernel) { + *field = GIC_TEST_ENABLED(irq, cm); + } else { + if (*field & 1) { + GIC_SET_ENABLED(irq, cm); + } + } +} + +static void translate_pending(GICState *s, int irq, int cpu, + uint32_t *field, bool to_kernel) +{ + int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; + + if (to_kernel) { + *field = gic_test_pending(s, irq, cm); + } else { + if (*field & 1) { + GIC_SET_PENDING(irq, cm); + /* TODO: Capture is level-line is held high in the kernel */ + } + } +} + +static void translate_active(GICState *s, int irq, int cpu, + uint32_t *field, bool to_kernel) +{ + int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; + + if (to_kernel) { + *field = GIC_TEST_ACTIVE(irq, cm); + } else { + if (*field & 1) { + GIC_SET_ACTIVE(irq, cm); + } + } +} + +static void translate_trigger(GICState *s, int irq, int cpu, + uint32_t *field, bool to_kernel) +{ + if (to_kernel) { + *field = (GIC_TEST_EDGE_TRIGGER(irq)) ? 0x2 : 0x0; + } else { + if (*field & 0x2) { + GIC_SET_EDGE_TRIGGER(irq); + } + } +} + +static void translate_priority(GICState *s, int irq, int cpu, + uint32_t *field, bool to_kernel) +{ + if (to_kernel) { + *field = GIC_GET_PRIORITY(irq, cpu) & 0xff; + } else { + gic_set_priority(s, cpu, irq, *field & 0xff); + } +} + +static void translate_targets(GICState *s, int irq, int cpu, + uint32_t *field, bool to_kernel) +{ + if (to_kernel) { + *field = s->irq_target[irq] & 0xff; + } else { + s->irq_target[irq] = *field & 0xff; + } +} + +static void translate_sgisource(GICState *s, int irq, int cpu, + uint32_t *field, bool to_kernel) +{ + if (to_kernel) { + *field = s->sgi_pending[irq][cpu] & 0xff; + } else { + s->sgi_pending[irq][cpu] = *field & 0xff; + } +} + +/* Read a register group from the kernel VGIC */ +static void kvm_dist_get(GICState *s, uint32_t offset, int width, + int maxirq, vgic_translate_fn translate_fn) +{ + uint32_t reg; + int i; + int j; + int irq; + int cpu; + int regsz = 32 / width; /* irqs per kernel register */ + uint32_t field; + + for_each_irq_reg(i, maxirq, width) { + irq = i * regsz; + cpu = 0; + while ((cpu < s->num_cpu && irq < GIC_INTERNAL) || cpu == 0) { + kvm_gicd_access(s, offset, cpu, ®, false); + for (j = 0; j < regsz; j++) { + field = extract32(reg, j * width, width); + translate_fn(s, irq + j, cpu, &field, false); + } + + cpu++; + } + offset += 4; + } +} + +/* Write a register group to the kernel VGIC */ +static void kvm_dist_put(GICState *s, uint32_t offset, int width, + int maxirq, vgic_translate_fn translate_fn) +{ + uint32_t reg; + int i; + int j; + int irq; + int cpu; + int regsz = 32 / width; /* irqs per kernel register */ + uint32_t field; + + for_each_irq_reg(i, maxirq, width) { + irq = i * regsz; + cpu = 0; + while ((cpu < s->num_cpu && irq < GIC_INTERNAL) || cpu == 0) { + reg = 0; + for (j = 0; j < regsz; j++) { + translate_fn(s, irq + j, cpu, &field, true); + reg = deposit32(reg, j * width, width, field); + } + kvm_gicd_access(s, offset, cpu, ®, true); + + cpu++; + } + offset += 4; + } +} + static void kvm_arm_gic_put(GICState *s) { - /* TODO: there isn't currently a kernel interface to set the GIC state */ + uint32_t reg; + int i; + int cpu; + int num_cpu; + int num_irq; + + if (!kvm_arm_gic_can_save_restore(s)) { + DPRINTF("Cannot put kernel gic state, no kernel interface"); + return; + } + + /* Note: We do the restore in a slightly different order than the save + * (where the order doesn't matter and is simply ordered according to the + * register offset values */ + + /***************************************************************** + * Distributor State + */ + + /* s->enabled -> GICD_CTLR */ + reg = s->enabled; + kvm_gicd_access(s, 0x0, 0, ®, true); + + /* Sanity checking on GICD_TYPER and s->num_irq, s->num_cpu */ + kvm_gicd_access(s, 0x4, 0, ®, false); + num_irq = ((reg & 0x1f) + 1) * 32; + num_cpu = ((reg & 0xe0) >> 5) + 1; + + if (num_irq < s->num_irq) { + fprintf(stderr, "Restoring %u IRQs, but kernel supports max %d\n", + s->num_irq, num_irq); + abort(); + } else if (num_cpu != s->num_cpu) { + fprintf(stderr, "Restoring %u CPU interfaces, kernel only has %d\n", + s->num_cpu, num_cpu); + /* Did we not create the VCPUs in the kernel yet? */ + abort(); + } + + /* TODO: Consider checking compatibility with the IIDR ? */ + + /* irq_state[n].enabled -> GICD_ISENABLERn */ + kvm_dist_put(s, 0x180, 1, s->num_irq, translate_clear); + kvm_dist_put(s, 0x100, 1, s->num_irq, translate_enabled); + + /* s->irq_target[irq] -> GICD_ITARGETSRn + * (restore targets before pending to ensure the pending state is set on + * the appropriate CPU interfaces in the kernel) */ + kvm_dist_put(s, 0x800, 8, s->num_irq, translate_targets); + + /* irq_state[n].pending + irq_state[n].level -> GICD_ISPENDRn */ + kvm_dist_put(s, 0x280, 1, s->num_irq, translate_clear); + kvm_dist_put(s, 0x200, 1, s->num_irq, translate_pending); + + /* irq_state[n].active -> GICD_ISACTIVERn */ + kvm_dist_put(s, 0x380, 1, s->num_irq, translate_clear); + kvm_dist_put(s, 0x300, 1, s->num_irq, translate_active); + + /* irq_state[n].trigger -> GICD_ICFRn */ + kvm_dist_put(s, 0xc00, 2, s->num_irq, translate_trigger); + + /* s->priorityX[irq] -> ICD_IPRIORITYRn */ + kvm_dist_put(s, 0x400, 8, s->num_irq, translate_priority); + + /* s->sgi_pending -> ICD_CPENDSGIRn */ + kvm_dist_put(s, 0xf10, 8, GIC_NR_SGIS, translate_clear); + kvm_dist_put(s, 0xf20, 8, GIC_NR_SGIS, translate_sgisource); + + + /***************************************************************** + * CPU Interface(s) State + */ + + for (cpu = 0; cpu < s->num_cpu; cpu++) { + /* s->cpu_enabled[cpu] -> GICC_CTLR */ + reg = s->cpu_enabled[cpu]; + kvm_gicc_access(s, 0x00, cpu, ®, true); + + /* s->priority_mask[cpu] -> GICC_PMR */ + reg = (s->priority_mask[cpu] & 0xff); + kvm_gicc_access(s, 0x04, cpu, ®, true); + + /* s->bpr[cpu] -> GICC_BPR */ + reg = (s->bpr[cpu] & 0x7); + kvm_gicc_access(s, 0x08, cpu, ®, true); + + /* s->abpr[cpu] -> GICC_ABPR */ + reg = (s->abpr[cpu] & 0x7); + kvm_gicc_access(s, 0x1c, cpu, ®, true); + + /* s->apr[n][cpu] -> GICC_APRn */ + for (i = 0; i < 4; i++) { + reg = s->apr[i][cpu]; + kvm_gicc_access(s, 0xd0 + i * 4, cpu, ®, true); + } + } } static void kvm_arm_gic_get(GICState *s) { - /* TODO: there isn't currently a kernel interface to get the GIC state */ + uint32_t reg; + int i; + int cpu; + + if (!kvm_arm_gic_can_save_restore(s)) { + DPRINTF("Cannot get kernel gic state, no kernel interface"); + return; + } + + /***************************************************************** + * Distributor State + */ + + /* GICD_CTLR -> s->enabled */ + kvm_gicd_access(s, 0x0, 0, ®, false); + s->enabled = reg & 1; + + /* Sanity checking on GICD_TYPER -> s->num_irq, s->num_cpu */ + kvm_gicd_access(s, 0x4, 0, ®, false); + s->num_irq = ((reg & 0x1f) + 1) * 32; + s->num_cpu = ((reg & 0xe0) >> 5) + 1; + + if (s->num_irq > GIC_MAXIRQ) { + fprintf(stderr, "Too many IRQs reported from the kernel: %d\n", + s->num_irq); + abort(); + } + + /* GICD_IIDR -> ? */ + kvm_gicd_access(s, 0x8, 0, ®, false); + + /* Verify no GROUP 1 interrupts configured in the kernel */ + for_each_irq_reg(i, s->num_irq, 1) { + kvm_gicd_access(s, 0x80 + (i * 4), 0, ®, false); + if (reg != 0) { + fprintf(stderr, "Unsupported GICD_IGROUPRn value: %08x\n", + reg); + abort(); + } + } + + /* Clear all the IRQ settings */ + for (i = 0; i < s->num_irq; i++) { + memset(&s->irq_state[i], 0, sizeof(s->irq_state[0])); + } + + /* GICD_ISENABLERn -> irq_state[n].enabled */ + kvm_dist_get(s, 0x100, 1, s->num_irq, translate_enabled); + + /* GICD_ISPENDRn -> irq_state[n].pending + irq_state[n].level */ + kvm_dist_get(s, 0x200, 1, s->num_irq, translate_pending); + + /* GICD_ISACTIVERn -> irq_state[n].active */ + kvm_dist_get(s, 0x300, 1, s->num_irq, translate_active); + + /* GICD_ICFRn -> irq_state[n].trigger */ + kvm_dist_get(s, 0xc00, 2, s->num_irq, translate_trigger); + + /* GICD_IPRIORITYRn -> s->priorityX[irq] */ + kvm_dist_get(s, 0x400, 8, s->num_irq, translate_priority); + + /* GICD_ITARGETSRn -> s->irq_target[irq] */ + kvm_dist_get(s, 0x800, 8, s->num_irq, translate_targets); + + /* GICD_CPENDSGIRn -> s->sgi_pending */ + kvm_dist_get(s, 0xf10, 8, GIC_NR_SGIS, translate_sgisource); + + + /***************************************************************** + * CPU Interface(s) State + */ + + for (cpu = 0; cpu < s->num_cpu; cpu++) { + /* GICC_CTLR -> s->cpu_enabled[cpu] */ + kvm_gicc_access(s, 0x00, cpu, ®, false); + s->cpu_enabled[cpu] = (reg & 1); + + /* GICC_PMR -> s->priority_mask[cpu] */ + kvm_gicc_access(s, 0x04, cpu, ®, false); + s->priority_mask[cpu] = (reg & 0xff); + + /* GICC_BPR -> s->bpr[cpu] */ + kvm_gicc_access(s, 0x08, cpu, ®, false); + s->bpr[cpu] = (reg & 0x7); + + /* GICC_ABPR -> s->abpr[cpu] */ + kvm_gicc_access(s, 0x1c, cpu, ®, false); + s->abpr[cpu] = (reg & 0x7); + + /* GICC_APRn -> s->apr[n][cpu] */ + for (i = 0; i < 4; i++) { + kvm_gicc_access(s, 0xd0 + i * 4, cpu, ®, false); + s->apr[i][cpu] = reg; + } + } } static void kvm_arm_gic_reset(DeviceState *dev) @@ -97,6 +517,7 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) GICState *s = KVM_ARM_GIC(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s); + int ret; kgc->parent_realize(dev, errp); if (error_is_set(errp)) { @@ -119,13 +540,27 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) for (i = 0; i < s->num_cpu; i++) { sysbus_init_irq(sbd, &s->parent_irq[i]); } + + /* Try to create the device via the device control API */ + s->dev_fd = -1; + ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_V2, false); + if (ret >= 0) { + s->dev_fd = ret; + } else if (ret != -ENODEV && ret != -ENOTSUP) { + error_setg_errno(errp, -ret, "error creating in-kernel VGIC"); + return; + } + /* Distributor */ memory_region_init_reservation(&s->iomem, OBJECT(s), "kvm-gic_dist", 0x1000); sysbus_init_mmio(sbd, &s->iomem); kvm_arm_register_device(&s->iomem, (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT) - | KVM_VGIC_V2_ADDR_TYPE_DIST); + | KVM_VGIC_V2_ADDR_TYPE_DIST, + KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V2_ADDR_TYPE_DIST, + s->dev_fd); /* CPU interface for current core. Unlike arm_gic, we don't * provide the "interface for core #N" memory regions, because * cores with a VGIC don't have those. @@ -135,7 +570,10 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->cpuiomem[0]); kvm_arm_register_device(&s->cpuiomem[0], (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT) - | KVM_VGIC_V2_ADDR_TYPE_CPU); + | KVM_VGIC_V2_ADDR_TYPE_CPU, + KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V2_ADDR_TYPE_CPU, + s->dev_fd); } static void kvm_arm_gic_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/exynos4210_combiner.c b/hw/intc/exynos4210_combiner.c index ef5e8eb22f..3287479456 100644 --- a/hw/intc/exynos4210_combiner.c +++ b/hw/intc/exynos4210_combiner.c @@ -418,7 +418,7 @@ static int exynos4210_combiner_init(SysBusDevice *sbd) qdev_init_gpio_in(dev, exynos4210_combiner_handler, IIC_NIRQ); /* Connect SysBusDev irqs to device specific irqs */ - for (i = 0; i < IIC_NIRQ; i++) { + for (i = 0; i < IIC_NGRP; i++) { sysbus_init_irq(sbd, &s->output_irq[i]); } diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h index 92a6f7a3ff..48a58d7890 100644 --- a/hw/intc/gic_internal.h +++ b/hw/intc/gic_internal.h @@ -40,7 +40,7 @@ #define GIC_SET_MODEL(irq) s->irq_state[irq].model = true #define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = false #define GIC_TEST_MODEL(irq) s->irq_state[irq].model -#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm) +#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level |= (cm) #define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm) #define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0) #define GIC_SET_EDGE_TRIGGER(irq) s->irq_state[irq].edge_trigger = true diff --git a/hw/intc/openpic_kvm.c b/hw/intc/openpic_kvm.c index c7f7b8406c..87fdb126cf 100644 --- a/hw/intc/openpic_kvm.c +++ b/hw/intc/openpic_kvm.c @@ -228,7 +228,7 @@ int kvm_openpic_connect_vcpu(DeviceState *d, CPUState *cs) encap.cap = KVM_CAP_IRQ_MPIC; encap.args[0] = opp->fd; - encap.args[1] = cs->cpu_index; + encap.args[1] = kvm_arch_vcpu_id(cs); return kvm_vcpu_ioctl(cs, KVM_ENABLE_CAP, &encap); } diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c new file mode 100644 index 0000000000..b2ef3e3f8e --- /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/intc/xics.c b/hw/intc/xics.c index b437563fb9..64aabe753d 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -33,6 +33,17 @@ #include "qemu/error-report.h" #include "qapi/visitor.h" +static int get_cpu_index_by_dt_id(int cpu_dt_id) +{ + PowerPCCPU *cpu = ppc_get_vcpu_by_dt_id(cpu_dt_id); + + if (cpu) { + return cpu->parent_obj.cpu_index; + } + + return -1; +} + void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu) { CPUState *cs = CPU(cpu); @@ -659,7 +670,7 @@ static target_ulong h_cppr(PowerPCCPU *cpu, sPAPREnvironment *spapr, static target_ulong h_ipi(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { - target_ulong server = args[0]; + target_ulong server = get_cpu_index_by_dt_id(args[0]); target_ulong mfrr = args[1]; if (server >= spapr->icp->nr_servers) { @@ -728,7 +739,7 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr, } nr = rtas_ld(args, 0); - server = rtas_ld(args, 1); + server = get_cpu_index_by_dt_id(rtas_ld(args, 1)); priority = rtas_ld(args, 2); if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers) diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index c203646bd6..a5bbc2406d 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -65,7 +65,7 @@ static void icp_get_kvm_state(ICPState *ss) ret = kvm_vcpu_ioctl(ss->cs, KVM_GET_ONE_REG, ®); if (ret != 0) { error_report("Unable to retrieve KVM interrupt controller state" - " for CPU %d: %s", ss->cs->cpu_index, strerror(errno)); + " for CPU %ld: %s", kvm_arch_vcpu_id(ss->cs), strerror(errno)); exit(1); } @@ -97,7 +97,7 @@ static int icp_set_kvm_state(ICPState *ss, int version_id) ret = kvm_vcpu_ioctl(ss->cs, KVM_SET_ONE_REG, ®); if (ret != 0) { error_report("Unable to restore KVM interrupt controller state (0x%" - PRIx64 ") for CPU %d: %s", state, ss->cs->cpu_index, + PRIx64 ") for CPU %ld: %s", state, kvm_arch_vcpu_id(ss->cs), strerror(errno)); return ret; } @@ -325,15 +325,15 @@ static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu) struct kvm_enable_cap xics_enable_cap = { .cap = KVM_CAP_IRQ_XICS, .flags = 0, - .args = {icpkvm->kernel_xics_fd, cs->cpu_index, 0, 0}, + .args = {icpkvm->kernel_xics_fd, kvm_arch_vcpu_id(cs), 0, 0}, }; ss->cs = cs; ret = kvm_vcpu_ioctl(ss->cs, KVM_ENABLE_CAP, &xics_enable_cap); if (ret < 0) { - error_report("Unable to connect CPU%d to kernel XICS: %s", - cs->cpu_index, strerror(errno)); + error_report("Unable to connect CPU%ld to kernel XICS: %s", + kvm_arch_vcpu_id(cs), strerror(errno)); exit(1); } } diff --git a/hw/misc/arm_sysctl.c b/hw/misc/arm_sysctl.c index 0fc26d29a5..3fad6f86de 100644 --- a/hw/misc/arm_sysctl.c +++ b/hw/misc/arm_sysctl.c @@ -276,7 +276,7 @@ static bool vexpress_cfgctrl_read(arm_sysctl_state *s, unsigned int dcc, } break; case SYS_CFG_OSC: - if (site == SYS_CFG_SITE_MB && device < sizeof(s->mb_clock)) { + if (site == SYS_CFG_SITE_MB && device < ARRAY_SIZE(s->mb_clock)) { /* motherboard clock */ *val = s->mb_clock[device]; return true; @@ -324,7 +324,7 @@ static bool vexpress_cfgctrl_write(arm_sysctl_state *s, unsigned int dcc, switch (function) { case SYS_CFG_OSC: - if (site == SYS_CFG_SITE_MB && device < sizeof(s->mb_clock)) { + if (site == SYS_CFG_SITE_MB && device < ARRAY_SIZE(s->mb_clock)) { /* motherboard clock */ s->mb_clock[device] = val; return true; diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c index 8db182fa3d..c2c688c870 100644 --- a/hw/misc/vfio.c +++ b/hw/misc/vfio.c @@ -209,6 +209,29 @@ typedef struct VFIOGroup { QLIST_ENTRY(VFIOGroup) container_next; } VFIOGroup; +typedef struct VFIORomBlacklistEntry { + uint16_t vendor_id; + uint16_t device_id; +} VFIORomBlacklistEntry; + +/* + * List of device ids/vendor ids for which to disable + * option rom loading. This avoids the guest hangs during rom + * execution as noticed with the BCM 57810 card for lack of a + * more better way to handle such issues. + * The user can still override by specifying a romfile or + * rombar=1. + * Please see https://bugs.launchpad.net/qemu/+bug/1284874 + * for an analysis of the 57810 card hang. When adding + * a new vendor id/device id combination below, please also add + * your card/environment details and information that could + * help in debugging to the bug tracking this issue + */ +static const VFIORomBlacklistEntry romblacklist[] = { + /* Broadcom BCM 57810 */ + { 0x14e4, 0x168e } +}; + #define MSIX_CAP_LENGTH 12 static QLIST_HEAD(, VFIOContainer) @@ -1197,13 +1220,43 @@ static const MemoryRegionOps vfio_rom_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static bool vfio_blacklist_opt_rom(VFIODevice *vdev) +{ + PCIDevice *pdev = &vdev->pdev; + uint16_t vendor_id, device_id; + int count = 0; + + vendor_id = pci_get_word(pdev->config + PCI_VENDOR_ID); + device_id = pci_get_word(pdev->config + PCI_DEVICE_ID); + + while (count < ARRAY_SIZE(romblacklist)) { + if (romblacklist[count].vendor_id == vendor_id && + romblacklist[count].device_id == device_id) { + return true; + } + count++; + } + + return false; +} + static void vfio_pci_size_rom(VFIODevice *vdev) { uint32_t orig, size = cpu_to_le32((uint32_t)PCI_ROM_ADDRESS_MASK); off_t offset = vdev->config_offset + PCI_ROM_ADDRESS; + DeviceState *dev = DEVICE(vdev); char name[32]; if (vdev->pdev.romfile || !vdev->pdev.rom_bar) { + /* Since pci handles romfile, just print a message and return */ + if (vfio_blacklist_opt_rom(vdev) && vdev->pdev.romfile) { + error_printf("Warning : Device at %04x:%02x:%02x.%x " + "is known to cause system instability issues during " + "option rom execution. " + "Proceeding anyway since user specified romfile\n", + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function); + } return; } @@ -1227,6 +1280,26 @@ static void vfio_pci_size_rom(VFIODevice *vdev) return; } + if (vfio_blacklist_opt_rom(vdev)) { + if (dev->opts && qemu_opt_get(dev->opts, "rombar")) { + error_printf("Warning : Device at %04x:%02x:%02x.%x " + "is known to cause system instability issues during " + "option rom execution. " + "Proceeding anyway since user specified non zero value for " + "rombar\n", + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function); + } else { + error_printf("Warning : Rom loading for device at " + "%04x:%02x:%02x.%x has been disabled due to " + "system instability issues. " + "Specify rombar=1 or romfile to force\n", + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function); + return; + } + } + DPRINTF("%04x:%02x:%02x.%x ROM size 0x%x\n", vdev->host.domain, vdev->host.bus, vdev->host.slot, vdev->host.function, size); @@ -3681,10 +3754,10 @@ static int vfio_initfn(PCIDevice *pdev) strncat(path, "iommu_group", sizeof(path) - strlen(path) - 1); - len = readlink(path, iommu_group_path, PATH_MAX); - if (len <= 0) { + len = readlink(path, iommu_group_path, sizeof(path)); + if (len <= 0 || len >= sizeof(path)) { error_report("vfio: error no iommu_group for device"); - return -errno; + return len < 0 ? -errno : ENAMETOOLONG; } iommu_group_path[len] = 0; diff --git a/hw/moxie/moxiesim.c b/hw/moxie/moxiesim.c index ef4f3a84d7..a87ca6ddcc 100644 --- a/hw/moxie/moxiesim.c +++ b/hw/moxie/moxiesim.c @@ -55,7 +55,7 @@ static void load_kernel(MoxieCPU *cpu, LoaderParams *loader_params) &entry, &kernel_low, &kernel_high, 1, ELF_MACHINE, 0); - if (!kernel_size) { + if (kernel_size <= 0) { fprintf(stderr, "qemu: could not load kernel '%s'\n", loader_params->kernel_filename); exit(1); diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs index 75e80c2c48..ea93293122 100644 --- a/hw/net/Makefile.objs +++ b/hw/net/Makefile.objs @@ -32,3 +32,6 @@ obj-$(CONFIG_XILINX_ETHLITE) += xilinx_ethlite.o obj-$(CONFIG_VIRTIO) += virtio-net.o obj-y += vhost_net.o + +obj-$(CONFIG_ETSEC) += fsl_etsec/etsec.o fsl_etsec/registers.o \ + fsl_etsec/rings.o fsl_etsec/miim.o diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c new file mode 100644 index 0000000000..d4b4429446 --- /dev/null +++ b/hw/net/fsl_etsec/etsec.c @@ -0,0 +1,465 @@ +/* + * QEMU Freescale eTSEC Emulator + * + * Copyright (c) 2011-2013 AdaCore + * + * 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. + */ + +/* + * This implementation doesn't include ring priority, TCP/IP Off-Load, QoS. + */ + +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "trace.h" +#include "hw/ptimer.h" +#include "etsec.h" +#include "registers.h" + +/* #define HEX_DUMP */ +/* #define DEBUG_REGISTER */ + +#ifdef DEBUG_REGISTER +static const int debug_etsec = 1; +#else +static const int debug_etsec; +#endif + +#define DPRINTF(fmt, ...) do { \ + if (debug_etsec) { \ + qemu_log(fmt , ## __VA_ARGS__); \ + } \ + } while (0) + +static uint64_t etsec_read(void *opaque, hwaddr addr, unsigned size) +{ + eTSEC *etsec = opaque; + uint32_t reg_index = addr / 4; + eTSEC_Register *reg = NULL; + uint32_t ret = 0x0; + + assert(reg_index < ETSEC_REG_NUMBER); + + reg = &etsec->regs[reg_index]; + + + switch (reg->access) { + case ACC_WO: + ret = 0x00000000; + break; + + case ACC_RW: + case ACC_W1C: + case ACC_RO: + default: + ret = reg->value; + break; + } + + DPRINTF("Read 0x%08x @ 0x" TARGET_FMT_plx + " : %s (%s)\n", + ret, addr, reg->name, reg->desc); + + return ret; +} + +static void write_tstat(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + int i = 0; + + for (i = 0; i < 8; i++) { + /* Check THLTi flag in TSTAT */ + if (value & (1 << (31 - i))) { + etsec_walk_tx_ring(etsec, i); + } + } + + /* Write 1 to clear */ + reg->value &= ~value; +} + +static void write_rstat(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + int i = 0; + + for (i = 0; i < 8; i++) { + /* Check QHLTi flag in RSTAT */ + if (value & (1 << (23 - i)) && !(reg->value & (1 << (23 - i)))) { + etsec_walk_rx_ring(etsec, i); + } + } + + /* Write 1 to clear */ + reg->value &= ~value; +} + +static void write_tbasex(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + reg->value = value & ~0x7; + + /* Copy this value in the ring's TxBD pointer */ + etsec->regs[TBPTR0 + (reg_index - TBASE0)].value = value & ~0x7; +} + +static void write_rbasex(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + reg->value = value & ~0x7; + + /* Copy this value in the ring's RxBD pointer */ + etsec->regs[RBPTR0 + (reg_index - RBASE0)].value = value & ~0x7; +} + +static void write_ievent(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + /* Write 1 to clear */ + reg->value &= ~value; + + if (!(reg->value & (IEVENT_TXF | IEVENT_TXF))) { + qemu_irq_lower(etsec->tx_irq); + } + if (!(reg->value & (IEVENT_RXF | IEVENT_RXF))) { + qemu_irq_lower(etsec->rx_irq); + } + + if (!(reg->value & (IEVENT_MAG | IEVENT_GTSC | IEVENT_GRSC | IEVENT_TXC | + IEVENT_RXC | IEVENT_BABR | IEVENT_BABT | IEVENT_LC | + IEVENT_CRL | IEVENT_FGPI | IEVENT_FIR | IEVENT_FIQ | + IEVENT_DPE | IEVENT_PERR | IEVENT_EBERR | IEVENT_TXE | + IEVENT_XFUN | IEVENT_BSY | IEVENT_MSRO | IEVENT_MMRD | + IEVENT_MMRW))) { + qemu_irq_lower(etsec->err_irq); + } +} + +static void write_dmactrl(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + reg->value = value; + + if (value & DMACTRL_GRS) { + + if (etsec->rx_buffer_len != 0) { + /* Graceful receive stop delayed until end of frame */ + } else { + /* Graceful receive stop now */ + etsec->regs[IEVENT].value |= IEVENT_GRSC; + if (etsec->regs[IMASK].value & IMASK_GRSCEN) { + qemu_irq_raise(etsec->err_irq); + } + } + } + + if (value & DMACTRL_GTS) { + + if (etsec->tx_buffer_len != 0) { + /* Graceful transmit stop delayed until end of frame */ + } else { + /* Graceful transmit stop now */ + etsec->regs[IEVENT].value |= IEVENT_GTSC; + if (etsec->regs[IMASK].value & IMASK_GTSCEN) { + qemu_irq_raise(etsec->err_irq); + } + } + } + + if (!(value & DMACTRL_WOP)) { + /* Start polling */ + ptimer_stop(etsec->ptimer); + ptimer_set_count(etsec->ptimer, 1); + ptimer_run(etsec->ptimer, 1); + } +} + +static void etsec_write(void *opaque, + hwaddr addr, + uint64_t value, + unsigned size) +{ + eTSEC *etsec = opaque; + uint32_t reg_index = addr / 4; + eTSEC_Register *reg = NULL; + uint32_t before = 0x0; + + assert(reg_index < ETSEC_REG_NUMBER); + + reg = &etsec->regs[reg_index]; + before = reg->value; + + switch (reg_index) { + case IEVENT: + write_ievent(etsec, reg, reg_index, value); + break; + + case DMACTRL: + write_dmactrl(etsec, reg, reg_index, value); + break; + + case TSTAT: + write_tstat(etsec, reg, reg_index, value); + break; + + case RSTAT: + write_rstat(etsec, reg, reg_index, value); + break; + + case TBASE0 ... TBASE7: + write_tbasex(etsec, reg, reg_index, value); + break; + + case RBASE0 ... RBASE7: + write_rbasex(etsec, reg, reg_index, value); + break; + + case MIIMCFG ... MIIMIND: + etsec_write_miim(etsec, reg, reg_index, value); + break; + + default: + /* Default handling */ + switch (reg->access) { + + case ACC_RW: + case ACC_WO: + reg->value = value; + break; + + case ACC_W1C: + reg->value &= ~value; + break; + + case ACC_RO: + default: + /* Read Only or Unknown register */ + break; + } + } + + DPRINTF("Write 0x%08x @ 0x" TARGET_FMT_plx + " val:0x%08x->0x%08x : %s (%s)\n", + (unsigned int)value, addr, before, reg->value, + reg->name, reg->desc); +} + +static const MemoryRegionOps etsec_ops = { + .read = etsec_read, + .write = etsec_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void etsec_timer_hit(void *opaque) +{ + eTSEC *etsec = opaque; + + ptimer_stop(etsec->ptimer); + + if (!(etsec->regs[DMACTRL].value & DMACTRL_WOP)) { + + if (!(etsec->regs[DMACTRL].value & DMACTRL_GTS)) { + etsec_walk_tx_ring(etsec, 0); + } + ptimer_set_count(etsec->ptimer, 1); + ptimer_run(etsec->ptimer, 1); + } +} + +static void etsec_reset(DeviceState *d) +{ + eTSEC *etsec = ETSEC_COMMON(d); + int i = 0; + int reg_index = 0; + + /* Default value for all registers */ + for (i = 0; i < ETSEC_REG_NUMBER; i++) { + etsec->regs[i].name = "Reserved"; + etsec->regs[i].desc = ""; + etsec->regs[i].access = ACC_UNKNOWN; + etsec->regs[i].value = 0x00000000; + } + + /* Set-up known registers */ + for (i = 0; eTSEC_registers_def[i].name != NULL; i++) { + + reg_index = eTSEC_registers_def[i].offset / 4; + + etsec->regs[reg_index].name = eTSEC_registers_def[i].name; + etsec->regs[reg_index].desc = eTSEC_registers_def[i].desc; + etsec->regs[reg_index].access = eTSEC_registers_def[i].access; + etsec->regs[reg_index].value = eTSEC_registers_def[i].reset; + } + + etsec->tx_buffer = NULL; + etsec->tx_buffer_len = 0; + etsec->rx_buffer = NULL; + etsec->rx_buffer_len = 0; + + etsec->phy_status = + MII_SR_EXTENDED_CAPS | MII_SR_LINK_STATUS | MII_SR_AUTONEG_CAPS | + MII_SR_AUTONEG_COMPLETE | MII_SR_PREAMBLE_SUPPRESS | + MII_SR_EXTENDED_STATUS | MII_SR_100T2_HD_CAPS | MII_SR_100T2_FD_CAPS | + MII_SR_10T_HD_CAPS | MII_SR_10T_FD_CAPS | MII_SR_100X_HD_CAPS | + MII_SR_100X_FD_CAPS | MII_SR_100T4_CAPS; +} + +static void etsec_cleanup(NetClientState *nc) +{ + /* qemu_log("eTSEC cleanup\n"); */ +} + +static int etsec_can_receive(NetClientState *nc) +{ + eTSEC *etsec = qemu_get_nic_opaque(nc); + + return etsec->rx_buffer_len == 0; +} + +static ssize_t etsec_receive(NetClientState *nc, + const uint8_t *buf, + size_t size) +{ + eTSEC *etsec = qemu_get_nic_opaque(nc); + +#if defined(HEX_DUMP) + fprintf(stderr, "%s receive size:%d\n", etsec->nic->nc.name, size); + qemu_hexdump(buf, stderr, "", size); +#endif + etsec_rx_ring_write(etsec, buf, size); + return size; +} + + +static void etsec_set_link_status(NetClientState *nc) +{ + eTSEC *etsec = qemu_get_nic_opaque(nc); + + etsec_miim_link_status(etsec, nc); +} + +static NetClientInfo net_etsec_info = { + .type = NET_CLIENT_OPTIONS_KIND_NIC, + .size = sizeof(NICState), + .can_receive = etsec_can_receive, + .receive = etsec_receive, + .cleanup = etsec_cleanup, + .link_status_changed = etsec_set_link_status, +}; + +static void etsec_realize(DeviceState *dev, Error **errp) +{ + eTSEC *etsec = ETSEC_COMMON(dev); + + etsec->nic = qemu_new_nic(&net_etsec_info, &etsec->conf, + object_get_typename(OBJECT(dev)), dev->id, etsec); + qemu_format_nic_info_str(qemu_get_queue(etsec->nic), etsec->conf.macaddr.a); + + + etsec->bh = qemu_bh_new(etsec_timer_hit, etsec); + etsec->ptimer = ptimer_init(etsec->bh); + ptimer_set_freq(etsec->ptimer, 100); +} + +static void etsec_instance_init(Object *obj) +{ + eTSEC *etsec = ETSEC_COMMON(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&etsec->io_area, OBJECT(etsec), &etsec_ops, etsec, + "eTSEC", 0x1000); + sysbus_init_mmio(sbd, &etsec->io_area); + + sysbus_init_irq(sbd, &etsec->tx_irq); + sysbus_init_irq(sbd, &etsec->rx_irq); + sysbus_init_irq(sbd, &etsec->err_irq); +} + +static Property etsec_properties[] = { + DEFINE_NIC_PROPERTIES(eTSEC, conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static void etsec_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = etsec_realize; + dc->reset = etsec_reset; + dc->props = etsec_properties; +} + +static TypeInfo etsec_info = { + .name = "eTSEC", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(eTSEC), + .class_init = etsec_class_init, + .instance_init = etsec_instance_init, +}; + +static void etsec_register_types(void) +{ + type_register_static(&etsec_info); +} + +type_init(etsec_register_types) + +DeviceState *etsec_create(hwaddr base, + MemoryRegion * mr, + NICInfo * nd, + qemu_irq tx_irq, + qemu_irq rx_irq, + qemu_irq err_irq) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "eTSEC"); + qdev_set_nic_properties(dev, nd); + + if (qdev_init(dev)) { + return NULL; + } + + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, tx_irq); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, rx_irq); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, err_irq); + + memory_region_add_subregion(mr, base, + SYS_BUS_DEVICE(dev)->mmio[0].memory); + + return dev; +} diff --git a/hw/net/fsl_etsec/etsec.h b/hw/net/fsl_etsec/etsec.h new file mode 100644 index 0000000000..78d2c57ed3 --- /dev/null +++ b/hw/net/fsl_etsec/etsec.h @@ -0,0 +1,174 @@ +/* + * QEMU Freescale eTSEC Emulator + * + * Copyright (c) 2011-2013 AdaCore + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef _ETSEC_H_ +#define _ETSEC_H_ + +#include "hw/qdev.h" +#include "hw/sysbus.h" +#include "net/net.h" +#include "hw/ptimer.h" + +/* Buffer Descriptors */ + +typedef struct eTSEC_rxtx_bd { + uint16_t flags; + uint16_t length; + uint32_t bufptr; +} eTSEC_rxtx_bd; + +#define BD_WRAP (1 << 13) +#define BD_INTERRUPT (1 << 12) +#define BD_LAST (1 << 11) + +#define BD_TX_READY (1 << 15) +#define BD_TX_PADCRC (1 << 14) +#define BD_TX_TC (1 << 10) +#define BD_TX_PREDEF (1 << 9) +#define BD_TX_HFELC (1 << 7) +#define BD_TX_CFRL (1 << 6) +#define BD_TX_RC_MASK 0xF +#define BD_TX_RC_OFFSET 0x2 +#define BD_TX_TOEUN (1 << 1) +#define BD_TX_TR (1 << 0) + +#define BD_RX_EMPTY (1 << 15) +#define BD_RX_RO1 (1 << 14) +#define BD_RX_FIRST (1 << 10) +#define BD_RX_MISS (1 << 8) +#define BD_RX_BROADCAST (1 << 7) +#define BD_RX_MULTICAST (1 << 6) +#define BD_RX_LG (1 << 5) +#define BD_RX_NO (1 << 4) +#define BD_RX_SH (1 << 3) +#define BD_RX_CR (1 << 2) +#define BD_RX_OV (1 << 1) +#define BD_RX_TR (1 << 0) + +/* Tx FCB flags */ +#define FCB_TX_VLN (1 << 7) +#define FCB_TX_IP (1 << 6) +#define FCB_TX_IP6 (1 << 5) +#define FCB_TX_TUP (1 << 4) +#define FCB_TX_UDP (1 << 3) +#define FCB_TX_CIP (1 << 2) +#define FCB_TX_CTU (1 << 1) +#define FCB_TX_NPH (1 << 0) + +/* PHY Status Register */ +#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +/* eTSEC */ + +/* Number of register in the device */ +#define ETSEC_REG_NUMBER 1024 + +typedef struct eTSEC_Register { + const char *name; + const char *desc; + uint32_t access; + uint32_t value; +} eTSEC_Register; + +typedef struct eTSEC { + SysBusDevice busdev; + + MemoryRegion io_area; + + eTSEC_Register regs[ETSEC_REG_NUMBER]; + + NICState *nic; + NICConf conf; + + /* Tx */ + + uint8_t *tx_buffer; + uint32_t tx_buffer_len; + eTSEC_rxtx_bd first_bd; + + /* Rx */ + + uint8_t *rx_buffer; + uint32_t rx_buffer_len; + uint32_t rx_remaining_data; + uint8_t rx_first_in_frame; + uint8_t rx_fcb_size; + eTSEC_rxtx_bd rx_first_bd; + uint8_t rx_fcb[10]; + uint32_t rx_padding; + + /* IRQs */ + qemu_irq tx_irq; + qemu_irq rx_irq; + qemu_irq err_irq; + + + uint16_t phy_status; + uint16_t phy_control; + + /* Polling */ + QEMUBH *bh; + struct ptimer_state *ptimer; + +} eTSEC; + +#define TYPE_ETSEC_COMMON "eTSEC" +#define ETSEC_COMMON(obj) \ + OBJECT_CHECK(eTSEC, (obj), TYPE_ETSEC_COMMON) + +#define eTSEC_TRANSMIT 1 +#define eTSEC_RECEIVE 2 + +DeviceState *etsec_create(hwaddr base, + MemoryRegion *mr, + NICInfo *nd, + qemu_irq tx_irq, + qemu_irq rx_irq, + qemu_irq err_irq); + +void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr); +void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr); +void etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size); + +void etsec_write_miim(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value); + +void etsec_miim_link_status(eTSEC *etsec, NetClientState *nc); + +#endif /* ! _ETSEC_H_ */ diff --git a/hw/net/fsl_etsec/miim.c b/hw/net/fsl_etsec/miim.c new file mode 100644 index 0000000000..1931b74e6c --- /dev/null +++ b/hw/net/fsl_etsec/miim.c @@ -0,0 +1,146 @@ +/* + * QEMU Freescale eTSEC Emulator + * + * Copyright (c) 2011-2013 AdaCore + * + * 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 "etsec.h" +#include "registers.h" + +/* #define DEBUG_MIIM */ + +#define MIIM_CONTROL 0 +#define MIIM_STATUS 1 +#define MIIM_PHY_ID_1 2 +#define MIIM_PHY_ID_2 3 +#define MIIM_T2_STATUS 10 +#define MIIM_EXT_STATUS 15 + +static void miim_read_cycle(eTSEC *etsec) +{ + uint8_t phy; + uint8_t addr; + uint16_t value; + + phy = (etsec->regs[MIIMADD].value >> 8) & 0x1F; + (void)phy; /* Unreferenced */ + addr = etsec->regs[MIIMADD].value & 0x1F; + + switch (addr) { + case MIIM_CONTROL: + value = etsec->phy_control; + break; + case MIIM_STATUS: + value = etsec->phy_status; + break; + case MIIM_T2_STATUS: + value = 0x1800; /* Local and remote receivers OK */ + break; + default: + value = 0x0; + break; + }; + +#ifdef DEBUG_MIIM + qemu_log("%s phy:%d addr:0x%x value:0x%x\n", __func__, phy, addr, value); +#endif + + etsec->regs[MIIMSTAT].value = value; +} + +static void miim_write_cycle(eTSEC *etsec) +{ + uint8_t phy; + uint8_t addr; + uint16_t value; + + phy = (etsec->regs[MIIMADD].value >> 8) & 0x1F; + (void)phy; /* Unreferenced */ + addr = etsec->regs[MIIMADD].value & 0x1F; + value = etsec->regs[MIIMCON].value & 0xffff; + +#ifdef DEBUG_MIIM + qemu_log("%s phy:%d addr:0x%x value:0x%x\n", __func__, phy, addr, value); +#endif + + switch (addr) { + case MIIM_CONTROL: + etsec->phy_control = value & ~(0x8100); + break; + default: + break; + }; +} + +void etsec_write_miim(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + + switch (reg_index) { + + case MIIMCOM: + /* Read and scan cycle */ + + if ((!(reg->value & MIIMCOM_READ)) && (value & MIIMCOM_READ)) { + /* Read */ + miim_read_cycle(etsec); + } + reg->value = value; + break; + + case MIIMCON: + reg->value = value & 0xffff; + miim_write_cycle(etsec); + break; + + default: + /* Default handling */ + switch (reg->access) { + + case ACC_RW: + case ACC_WO: + reg->value = value; + break; + + case ACC_W1C: + reg->value &= ~value; + break; + + case ACC_RO: + default: + /* Read Only or Unknown register */ + break; + } + } + +} + +void etsec_miim_link_status(eTSEC *etsec, NetClientState *nc) +{ + /* Set link status */ + if (nc->link_down) { + etsec->phy_status &= ~MII_SR_LINK_STATUS; + } else { + etsec->phy_status |= MII_SR_LINK_STATUS; + } +} diff --git a/hw/net/fsl_etsec/registers.c b/hw/net/fsl_etsec/registers.c new file mode 100644 index 0000000000..a7bbfa113f --- /dev/null +++ b/hw/net/fsl_etsec/registers.c @@ -0,0 +1,295 @@ +/* + * QEMU Freescale eTSEC Emulator + * + * Copyright (c) 2011-2013 AdaCore + * + * 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 "registers.h" + +const eTSEC_Register_Definition eTSEC_registers_def[] = { +{0x000, "TSEC_ID", "Controller ID register", ACC_RO, 0x01240000}, +{0x004, "TSEC_ID2", "Controller ID register 2", ACC_RO, 0x003000F0}, +{0x010, "IEVENT", "Interrupt event register", ACC_W1C, 0x00000000}, +{0x014, "IMASK", "Interrupt mask register", ACC_RW, 0x00000000}, +{0x018, "EDIS", "Error disabled register", ACC_RW, 0x00000000}, +{0x020, "ECNTRL", "Ethernet control register", ACC_RW, 0x00000040}, +{0x028, "PTV", "Pause time value register", ACC_RW, 0x00000000}, +{0x02C, "DMACTRL", "DMA control register", ACC_RW, 0x00000000}, +{0x030, "TBIPA", "TBI PHY address register", ACC_RW, 0x00000000}, + +/* eTSEC FIFO Control and Status Registers */ + +{0x058, "FIFO_RX_ALARM", "FIFO receive alarm start threshold register", ACC_RW, 0x00000040}, +{0x05C, "FIFO_RX_ALARM_SHUTOFF", "FIFO receive alarm shut-off threshold register", ACC_RW, 0x00000080}, +{0x08C, "FIFO_TX_THR", "FIFO transmit threshold register", ACC_RW, 0x00000080}, +{0x098, "FIFO_TX_STARVE", "FIFO transmit starve register", ACC_RW, 0x00000040}, +{0x09C, "FIFO_TX_STARVE_SHUTOFF", "FIFO transmit starve shut-off register", ACC_RW, 0x00000080}, + +/* eTSEC Transmit Control and Status Registers */ + +{0x100, "TCTRL", "Transmit control register", ACC_RW, 0x00000000}, +{0x104, "TSTAT", "Transmit status register", ACC_W1C, 0x00000000}, +{0x108, "DFVLAN", "Default VLAN control word", ACC_RW, 0x81000000}, +{0x110, "TXIC", "Transmit interrupt coalescing register", ACC_RW, 0x00000000}, +{0x114, "TQUEUE", "Transmit queue control register", ACC_RW, 0x00008000}, +{0x140, "TR03WT", "TxBD Rings 0-3 round-robin weightings", ACC_RW, 0x00000000}, +{0x144, "TR47WT", "TxBD Rings 4-7 round-robin weightings", ACC_RW, 0x00000000}, +{0x180, "TBDBPH", "Tx data buffer pointer high bits", ACC_RW, 0x00000000}, +{0x184, "TBPTR0", "TxBD pointer for ring 0", ACC_RW, 0x00000000}, +{0x18C, "TBPTR1", "TxBD pointer for ring 1", ACC_RW, 0x00000000}, +{0x194, "TBPTR2", "TxBD pointer for ring 2", ACC_RW, 0x00000000}, +{0x19C, "TBPTR3", "TxBD pointer for ring 3", ACC_RW, 0x00000000}, +{0x1A4, "TBPTR4", "TxBD pointer for ring 4", ACC_RW, 0x00000000}, +{0x1AC, "TBPTR5", "TxBD pointer for ring 5", ACC_RW, 0x00000000}, +{0x1B4, "TBPTR6", "TxBD pointer for ring 6", ACC_RW, 0x00000000}, +{0x1BC, "TBPTR7", "TxBD pointer for ring 7", ACC_RW, 0x00000000}, +{0x200, "TBASEH", "TxBD base address high bits", ACC_RW, 0x00000000}, +{0x204, "TBASE0", "TxBD base address of ring 0", ACC_RW, 0x00000000}, +{0x20C, "TBASE1", "TxBD base address of ring 1", ACC_RW, 0x00000000}, +{0x214, "TBASE2", "TxBD base address of ring 2", ACC_RW, 0x00000000}, +{0x21C, "TBASE3", "TxBD base address of ring 3", ACC_RW, 0x00000000}, +{0x224, "TBASE4", "TxBD base address of ring 4", ACC_RW, 0x00000000}, +{0x22C, "TBASE5", "TxBD base address of ring 5", ACC_RW, 0x00000000}, +{0x234, "TBASE6", "TxBD base address of ring 6", ACC_RW, 0x00000000}, +{0x23C, "TBASE7", "TxBD base address of ring 7", ACC_RW, 0x00000000}, +{0x280, "TMR_TXTS1_ID", "Tx time stamp identification tag (set 1)", ACC_RO, 0x00000000}, +{0x284, "TMR_TXTS2_ID", "Tx time stamp identification tag (set 2)", ACC_RO, 0x00000000}, +{0x2C0, "TMR_TXTS1_H", "Tx time stamp high (set 1)", ACC_RO, 0x00000000}, +{0x2C4, "TMR_TXTS1_L", "Tx time stamp high (set 1)", ACC_RO, 0x00000000}, +{0x2C8, "TMR_TXTS2_H", "Tx time stamp high (set 2)", ACC_RO, 0x00000000}, +{0x2CC, "TMR_TXTS2_L", "Tx time stamp high (set 2)", ACC_RO, 0x00000000}, + +/* eTSEC Receive Control and Status Registers */ + +{0x300, "RCTRL", "Receive control register", ACC_RW, 0x00000000}, +{0x304, "RSTAT", "Receive status register", ACC_W1C, 0x00000000}, +{0x310, "RXIC", "Receive interrupt coalescing register", ACC_RW, 0x00000000}, +{0x314, "RQUEUE", "Receive queue control register.", ACC_RW, 0x00800080}, +{0x330, "RBIFX", "Receive bit field extract control register", ACC_RW, 0x00000000}, +{0x334, "RQFAR", "Receive queue filing table address register", ACC_RW, 0x00000000}, +{0x338, "RQFCR", "Receive queue filing table control register", ACC_RW, 0x00000000}, +{0x33C, "RQFPR", "Receive queue filing table property register", ACC_RW, 0x00000000}, +{0x340, "MRBLR", "Maximum receive buffer length register", ACC_RW, 0x00000000}, +{0x380, "RBDBPH", "Rx data buffer pointer high bits", ACC_RW, 0x00000000}, +{0x384, "RBPTR0", "RxBD pointer for ring 0", ACC_RW, 0x00000000}, +{0x38C, "RBPTR1", "RxBD pointer for ring 1", ACC_RW, 0x00000000}, +{0x394, "RBPTR2", "RxBD pointer for ring 2", ACC_RW, 0x00000000}, +{0x39C, "RBPTR3", "RxBD pointer for ring 3", ACC_RW, 0x00000000}, +{0x3A4, "RBPTR4", "RxBD pointer for ring 4", ACC_RW, 0x00000000}, +{0x3AC, "RBPTR5", "RxBD pointer for ring 5", ACC_RW, 0x00000000}, +{0x3B4, "RBPTR6", "RxBD pointer for ring 6", ACC_RW, 0x00000000}, +{0x3BC, "RBPTR7", "RxBD pointer for ring 7", ACC_RW, 0x00000000}, +{0x400, "RBASEH", "RxBD base address high bits", ACC_RW, 0x00000000}, +{0x404, "RBASE0", "RxBD base address of ring 0", ACC_RW, 0x00000000}, +{0x40C, "RBASE1", "RxBD base address of ring 1", ACC_RW, 0x00000000}, +{0x414, "RBASE2", "RxBD base address of ring 2", ACC_RW, 0x00000000}, +{0x41C, "RBASE3", "RxBD base address of ring 3", ACC_RW, 0x00000000}, +{0x424, "RBASE4", "RxBD base address of ring 4", ACC_RW, 0x00000000}, +{0x42C, "RBASE5", "RxBD base address of ring 5", ACC_RW, 0x00000000}, +{0x434, "RBASE6", "RxBD base address of ring 6", ACC_RW, 0x00000000}, +{0x43C, "RBASE7", "RxBD base address of ring 7", ACC_RW, 0x00000000}, +{0x4C0, "TMR_RXTS_H", "Rx timer time stamp register high", ACC_RW, 0x00000000}, +{0x4C4, "TMR_RXTS_L", "Rx timer time stamp register low", ACC_RW, 0x00000000}, + +/* eTSEC MAC Registers */ + +{0x500, "MACCFG1", "MAC configuration register 1", ACC_RW, 0x00000000}, +{0x504, "MACCFG2", "MAC configuration register 2", ACC_RW, 0x00007000}, +{0x508, "IPGIFG", "Inter-packet/inter-frame gap register", ACC_RW, 0x40605060}, +{0x50C, "HAFDUP", "Half-duplex control", ACC_RW, 0x00A1F037}, +{0x510, "MAXFRM", "Maximum frame length", ACC_RW, 0x00000600}, +{0x520, "MIIMCFG", "MII management configuration", ACC_RW, 0x00000007}, +{0x524, "MIIMCOM", "MII management command", ACC_RW, 0x00000000}, +{0x528, "MIIMADD", "MII management address", ACC_RW, 0x00000000}, +{0x52C, "MIIMCON", "MII management control", ACC_WO, 0x00000000}, +{0x530, "MIIMSTAT", "MII management status", ACC_RO, 0x00000000}, +{0x534, "MIIMIND", "MII management indicator", ACC_RO, 0x00000000}, +{0x53C, "IFSTAT", "Interface status", ACC_RO, 0x00000000}, +{0x540, "MACSTNADDR1", "MAC station address register 1", ACC_RW, 0x00000000}, +{0x544, "MACSTNADDR2", "MAC station address register 2", ACC_RW, 0x00000000}, +{0x548, "MAC01ADDR1", "MAC exact match address 1, part 1", ACC_RW, 0x00000000}, +{0x54C, "MAC01ADDR2", "MAC exact match address 1, part 2", ACC_RW, 0x00000000}, +{0x550, "MAC02ADDR1", "MAC exact match address 2, part 1", ACC_RW, 0x00000000}, +{0x554, "MAC02ADDR2", "MAC exact match address 2, part 2", ACC_RW, 0x00000000}, +{0x558, "MAC03ADDR1", "MAC exact match address 3, part 1", ACC_RW, 0x00000000}, +{0x55C, "MAC03ADDR2", "MAC exact match address 3, part 2", ACC_RW, 0x00000000}, +{0x560, "MAC04ADDR1", "MAC exact match address 4, part 1", ACC_RW, 0x00000000}, +{0x564, "MAC04ADDR2", "MAC exact match address 4, part 2", ACC_RW, 0x00000000}, +{0x568, "MAC05ADDR1", "MAC exact match address 5, part 1", ACC_RW, 0x00000000}, +{0x56C, "MAC05ADDR2", "MAC exact match address 5, part 2", ACC_RW, 0x00000000}, +{0x570, "MAC06ADDR1", "MAC exact match address 6, part 1", ACC_RW, 0x00000000}, +{0x574, "MAC06ADDR2", "MAC exact match address 6, part 2", ACC_RW, 0x00000000}, +{0x578, "MAC07ADDR1", "MAC exact match address 7, part 1", ACC_RW, 0x00000000}, +{0x57C, "MAC07ADDR2", "MAC exact match address 7, part 2", ACC_RW, 0x00000000}, +{0x580, "MAC08ADDR1", "MAC exact match address 8, part 1", ACC_RW, 0x00000000}, +{0x584, "MAC08ADDR2", "MAC exact match address 8, part 2", ACC_RW, 0x00000000}, +{0x588, "MAC09ADDR1", "MAC exact match address 9, part 1", ACC_RW, 0x00000000}, +{0x58C, "MAC09ADDR2", "MAC exact match address 9, part 2", ACC_RW, 0x00000000}, +{0x590, "MAC10ADDR1", "MAC exact match address 10, part 1", ACC_RW, 0x00000000}, +{0x594, "MAC10ADDR2", "MAC exact match address 10, part 2", ACC_RW, 0x00000000}, +{0x598, "MAC11ADDR1", "MAC exact match address 11, part 1", ACC_RW, 0x00000000}, +{0x59C, "MAC11ADDR2", "MAC exact match address 11, part 2", ACC_RW, 0x00000000}, +{0x5A0, "MAC12ADDR1", "MAC exact match address 12, part 1", ACC_RW, 0x00000000}, +{0x5A4, "MAC12ADDR2", "MAC exact match address 12, part 2", ACC_RW, 0x00000000}, +{0x5A8, "MAC13ADDR1", "MAC exact match address 13, part 1", ACC_RW, 0x00000000}, +{0x5AC, "MAC13ADDR2", "MAC exact match address 13, part 2", ACC_RW, 0x00000000}, +{0x5B0, "MAC14ADDR1", "MAC exact match address 14, part 1", ACC_RW, 0x00000000}, +{0x5B4, "MAC14ADDR2", "MAC exact match address 14, part 2", ACC_RW, 0x00000000}, +{0x5B8, "MAC15ADDR1", "MAC exact match address 15, part 1", ACC_RW, 0x00000000}, +{0x5BC, "MAC15ADDR2", "MAC exact match address 15, part 2", ACC_RW, 0x00000000}, + +/* eTSEC, "Transmit", "and", Receive, Counters */ + +{0x680, "TR64", "Transmit and receive 64-byte frame counter ", ACC_RW, 0x00000000}, +{0x684, "TR127", "Transmit and receive 65- to 127-byte frame counter", ACC_RW, 0x00000000}, +{0x688, "TR255", "Transmit and receive 128- to 255-byte frame counter", ACC_RW, 0x00000000}, +{0x68C, "TR511", "Transmit and receive 256- to 511-byte frame counter", ACC_RW, 0x00000000}, +{0x690, "TR1K", "Transmit and receive 512- to 1023-byte frame counter", ACC_RW, 0x00000000}, +{0x694, "TRMAX", "Transmit and receive 1024- to 1518-byte frame counter", ACC_RW, 0x00000000}, +{0x698, "TRMGV", "Transmit and receive 1519- to 1522-byte good VLAN frame count", ACC_RW, 0x00000000}, + +/* eTSEC Receive Counters */ + +{0x69C, "RBYT", "Receive byte counter", ACC_RW, 0x00000000}, +{0x6A0, "RPKT", "Receive packet counter", ACC_RW, 0x00000000}, +{0x6A4, "RFCS", "Receive FCS error counter", ACC_RW, 0x00000000}, +{0x6A8, "RMCA", "Receive multicast packet counter", ACC_RW, 0x00000000}, +{0x6AC, "RBCA", "Receive broadcast packet counter", ACC_RW, 0x00000000}, +{0x6B0, "RXCF", "Receive control frame packet counter ", ACC_RW, 0x00000000}, +{0x6B4, "RXPF", "Receive PAUSE frame packet counter", ACC_RW, 0x00000000}, +{0x6B8, "RXUO", "Receive unknown OP code counter ", ACC_RW, 0x00000000}, +{0x6BC, "RALN", "Receive alignment error counter ", ACC_RW, 0x00000000}, +{0x6C0, "RFLR", "Receive frame length error counter ", ACC_RW, 0x00000000}, +{0x6C4, "RCDE", "Receive code error counter ", ACC_RW, 0x00000000}, +{0x6C8, "RCSE", "Receive carrier sense error counter", ACC_RW, 0x00000000}, +{0x6CC, "RUND", "Receive undersize packet counter", ACC_RW, 0x00000000}, +{0x6D0, "ROVR", "Receive oversize packet counter ", ACC_RW, 0x00000000}, +{0x6D4, "RFRG", "Receive fragments counter", ACC_RW, 0x00000000}, +{0x6D8, "RJBR", "Receive jabber counter ", ACC_RW, 0x00000000}, +{0x6DC, "RDRP", "Receive drop counter", ACC_RW, 0x00000000}, + +/* eTSEC Transmit Counters */ + +{0x6E0, "TBYT", "Transmit byte counter", ACC_RW, 0x00000000}, +{0x6E4, "TPKT", "Transmit packet counter", ACC_RW, 0x00000000}, +{0x6E8, "TMCA", "Transmit multicast packet counter ", ACC_RW, 0x00000000}, +{0x6EC, "TBCA", "Transmit broadcast packet counter ", ACC_RW, 0x00000000}, +{0x6F0, "TXPF", "Transmit PAUSE control frame counter ", ACC_RW, 0x00000000}, +{0x6F4, "TDFR", "Transmit deferral packet counter ", ACC_RW, 0x00000000}, +{0x6F8, "TEDF", "Transmit excessive deferral packet counter ", ACC_RW, 0x00000000}, +{0x6FC, "TSCL", "Transmit single collision packet counter", ACC_RW, 0x00000000}, +{0x700, "TMCL", "Transmit multiple collision packet counter", ACC_RW, 0x00000000}, +{0x704, "TLCL", "Transmit late collision packet counter", ACC_RW, 0x00000000}, +{0x708, "TXCL", "Transmit excessive collision packet counter", ACC_RW, 0x00000000}, +{0x70C, "TNCL", "Transmit total collision counter ", ACC_RW, 0x00000000}, +{0x714, "TDRP", "Transmit drop frame counter", ACC_RW, 0x00000000}, +{0x718, "TJBR", "Transmit jabber frame counter ", ACC_RW, 0x00000000}, +{0x71C, "TFCS", "Transmit FCS error counter", ACC_RW, 0x00000000}, +{0x720, "TXCF", "Transmit control frame counter ", ACC_RW, 0x00000000}, +{0x724, "TOVR", "Transmit oversize frame counter", ACC_RW, 0x00000000}, +{0x728, "TUND", "Transmit undersize frame counter ", ACC_RW, 0x00000000}, +{0x72C, "TFRG", "Transmit fragments frame counter ", ACC_RW, 0x00000000}, + +/* eTSEC Counter Control and TOE Statistics Registers */ + +{0x730, "CAR1", "Carry register one register", ACC_W1C, 0x00000000}, +{0x734, "CAR2", "Carry register two register ", ACC_W1C, 0x00000000}, +{0x738, "CAM1", "Carry register one mask register ", ACC_RW, 0xFE03FFFF}, +{0x73C, "CAM2", "Carry register two mask register ", ACC_RW, 0x000FFFFD}, +{0x740, "RREJ", "Receive filer rejected packet counter", ACC_RW, 0x00000000}, + +/* Hash Function Registers */ + +{0x800, "IGADDR0", "Individual/group address register 0", ACC_RW, 0x00000000}, +{0x804, "IGADDR1", "Individual/group address register 1", ACC_RW, 0x00000000}, +{0x808, "IGADDR2", "Individual/group address register 2", ACC_RW, 0x00000000}, +{0x80C, "IGADDR3", "Individual/group address register 3", ACC_RW, 0x00000000}, +{0x810, "IGADDR4", "Individual/group address register 4", ACC_RW, 0x00000000}, +{0x814, "IGADDR5", "Individual/group address register 5", ACC_RW, 0x00000000}, +{0x818, "IGADDR6", "Individual/group address register 6", ACC_RW, 0x00000000}, +{0x81C, "IGADDR7", "Individual/group address register 7", ACC_RW, 0x00000000}, +{0x880, "GADDR0", "Group address register 0", ACC_RW, 0x00000000}, +{0x884, "GADDR1", "Group address register 1", ACC_RW, 0x00000000}, +{0x888, "GADDR2", "Group address register 2", ACC_RW, 0x00000000}, +{0x88C, "GADDR3", "Group address register 3", ACC_RW, 0x00000000}, +{0x890, "GADDR4", "Group address register 4", ACC_RW, 0x00000000}, +{0x894, "GADDR5", "Group address register 5", ACC_RW, 0x00000000}, +{0x898, "GADDR6", "Group address register 6", ACC_RW, 0x00000000}, +{0x89C, "GADDR7", "Group address register 7", ACC_RW, 0x00000000}, + +/* eTSEC DMA Attribute Registers */ + +{0xBF8, "ATTR", "Attribute register", ACC_RW, 0x00000000}, +{0xBFC, "ATTRELI", "Attribute extract length and extract index register", ACC_RW, 0x00000000}, + + +/* eTSEC Lossless Flow Control Registers */ + +{0xC00, "RQPRM0", "Receive Queue Parameters register 0 ", ACC_RW, 0x00000000}, +{0xC04, "RQPRM1", "Receive Queue Parameters register 1 ", ACC_RW, 0x00000000}, +{0xC08, "RQPRM2", "Receive Queue Parameters register 2 ", ACC_RW, 0x00000000}, +{0xC0C, "RQPRM3", "Receive Queue Parameters register 3 ", ACC_RW, 0x00000000}, +{0xC10, "RQPRM4", "Receive Queue Parameters register 4 ", ACC_RW, 0x00000000}, +{0xC14, "RQPRM5", "Receive Queue Parameters register 5 ", ACC_RW, 0x00000000}, +{0xC18, "RQPRM6", "Receive Queue Parameters register 6 ", ACC_RW, 0x00000000}, +{0xC1C, "RQPRM7", "Receive Queue Parameters register 7 ", ACC_RW, 0x00000000}, +{0xC44, "RFBPTR0", "Last Free RxBD pointer for ring 0", ACC_RW, 0x00000000}, +{0xC4C, "RFBPTR1", "Last Free RxBD pointer for ring 1", ACC_RW, 0x00000000}, +{0xC54, "RFBPTR2", "Last Free RxBD pointer for ring 2", ACC_RW, 0x00000000}, +{0xC5C, "RFBPTR3", "Last Free RxBD pointer for ring 3", ACC_RW, 0x00000000}, +{0xC64, "RFBPTR4", "Last Free RxBD pointer for ring 4", ACC_RW, 0x00000000}, +{0xC6C, "RFBPTR5", "Last Free RxBD pointer for ring 5", ACC_RW, 0x00000000}, +{0xC74, "RFBPTR6", "Last Free RxBD pointer for ring 6", ACC_RW, 0x00000000}, +{0xC7C, "RFBPTR7", "Last Free RxBD pointer for ring 7", ACC_RW, 0x00000000}, + +/* eTSEC Future Expansion Space */ + +/* Reserved*/ + +/* eTSEC IEEE 1588 Registers */ + +{0xE00, "TMR_CTRL", "Timer control register", ACC_RW, 0x00010001}, +{0xE04, "TMR_TEVENT", "time stamp event register", ACC_W1C, 0x00000000}, +{0xE08, "TMR_TEMASK", "Timer event mask register", ACC_RW, 0x00000000}, +{0xE0C, "TMR_PEVENT", "time stamp event register", ACC_RW, 0x00000000}, +{0xE10, "TMR_PEMASK", "Timer event mask register", ACC_RW, 0x00000000}, +{0xE14, "TMR_STAT", "time stamp status register", ACC_RW, 0x00000000}, +{0xE18, "TMR_CNT_H", "timer counter high register", ACC_RW, 0x00000000}, +{0xE1C, "TMR_CNT_L", "timer counter low register", ACC_RW, 0x00000000}, +{0xE20, "TMR_ADD", "Timer drift compensation addend register", ACC_RW, 0x00000000}, +{0xE24, "TMR_ACC", "Timer accumulator register", ACC_RW, 0x00000000}, +{0xE28, "TMR_PRSC", "Timer prescale", ACC_RW, 0x00000002}, +{0xE30, "TMROFF_H", "Timer offset high", ACC_RW, 0x00000000}, +{0xE34, "TMROFF_L", "Timer offset low", ACC_RW, 0x00000000}, +{0xE40, "TMR_ALARM1_H", "Timer alarm 1 high register", ACC_RW, 0xFFFFFFFF}, +{0xE44, "TMR_ALARM1_L", "Timer alarm 1 high register", ACC_RW, 0xFFFFFFFF}, +{0xE48, "TMR_ALARM2_H", "Timer alarm 2 high register", ACC_RW, 0xFFFFFFFF}, +{0xE4C, "TMR_ALARM2_L", "Timer alarm 2 high register", ACC_RW, 0xFFFFFFFF}, +{0xE80, "TMR_FIPER1", "Timer fixed period interval", ACC_RW, 0xFFFFFFFF}, +{0xE84, "TMR_FIPER2", "Timer fixed period interval", ACC_RW, 0xFFFFFFFF}, +{0xE88, "TMR_FIPER3", "Timer fixed period interval", ACC_RW, 0xFFFFFFFF}, +{0xEA0, "TMR_ETTS1_H", "Time stamp of general purpose external trigger ", ACC_RW, 0x00000000}, +{0xEA4, "TMR_ETTS1_L", "Time stamp of general purpose external trigger", ACC_RW, 0x00000000}, +{0xEA8, "TMR_ETTS2_H", "Time stamp of general purpose external trigger ", ACC_RW, 0x00000000}, +{0xEAC, "TMR_ETTS2_L", "Time stamp of general purpose external trigger", ACC_RW, 0x00000000}, + +/* End Of Table */ +{0x0, 0x0, 0x0, 0x0, 0x0} +}; diff --git a/hw/net/fsl_etsec/registers.h b/hw/net/fsl_etsec/registers.h new file mode 100644 index 0000000000..7ad7686470 --- /dev/null +++ b/hw/net/fsl_etsec/registers.h @@ -0,0 +1,320 @@ +/* + * QEMU Freescale eTSEC Emulator + * + * Copyright (c) 2011-2013 AdaCore + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef _ETSEC_REGISTERS_H_ +#define _ETSEC_REGISTERS_H_ + +#include <stdint.h> + +enum eTSEC_Register_Access_Type { + ACC_RW = 1, /* Read/Write */ + ACC_RO = 2, /* Read Only */ + ACC_WO = 3, /* Write Only */ + ACC_W1C = 4, /* Write 1 to clear */ + ACC_UNKNOWN = 5 /* Unknown register*/ +}; + +typedef struct eTSEC_Register_Definition { + uint32_t offset; + const char *name; + const char *desc; + enum eTSEC_Register_Access_Type access; + uint32_t reset; +} eTSEC_Register_Definition; + +extern const eTSEC_Register_Definition eTSEC_registers_def[]; + +#define DMACTRL_LE (1 << 15) +#define DMACTRL_GRS (1 << 4) +#define DMACTRL_GTS (1 << 3) +#define DMACTRL_WOP (1 << 0) + +#define IEVENT_PERR (1 << 0) +#define IEVENT_DPE (1 << 1) +#define IEVENT_FIQ (1 << 2) +#define IEVENT_FIR (1 << 3) +#define IEVENT_FGPI (1 << 4) +#define IEVENT_RXF (1 << 7) +#define IEVENT_GRSC (1 << 8) +#define IEVENT_MMRW (1 << 9) +#define IEVENT_MMRD (1 << 10) +#define IEVENT_MAG (1 << 11) +#define IEVENT_RXB (1 << 15) +#define IEVENT_XFUN (1 << 16) +#define IEVENT_CRL (1 << 17) +#define IEVENT_LC (1 << 18) +#define IEVENT_TXF (1 << 20) +#define IEVENT_TXB (1 << 21) +#define IEVENT_TXE (1 << 22) +#define IEVENT_TXC (1 << 23) +#define IEVENT_BABT (1 << 24) +#define IEVENT_GTSC (1 << 25) +#define IEVENT_MSRO (1 << 26) +#define IEVENT_EBERR (1 << 28) +#define IEVENT_BSY (1 << 29) +#define IEVENT_RXC (1 << 30) +#define IEVENT_BABR (1 << 31) + +#define IMASK_RXFEN (1 << 7) +#define IMASK_GRSCEN (1 << 8) +#define IMASK_RXBEN (1 << 15) +#define IMASK_TXFEN (1 << 20) +#define IMASK_TXBEN (1 << 21) +#define IMASK_GTSCEN (1 << 25) + +#define MACCFG1_TX_EN (1 << 0) +#define MACCFG1_RX_EN (1 << 2) + +#define MACCFG2_CRC_EN (1 << 1) +#define MACCFG2_PADCRC (1 << 2) + +#define MIIMCOM_READ (1 << 0) +#define MIIMCOM_SCAN (1 << 1) + +#define RCTRL_PRSDEP_MASK (0x3) +#define RCTRL_PRSDEP_OFFSET (6) +#define RCTRL_RSF (1 << 2) + +/* Index of each register */ + +#define TSEC_ID (0x000 / 4) +#define TSEC_ID2 (0x004 / 4) +#define IEVENT (0x010 / 4) +#define IMASK (0x014 / 4) +#define EDIS (0x018 / 4) +#define ECNTRL (0x020 / 4) +#define PTV (0x028 / 4) +#define DMACTRL (0x02C / 4) +#define TBIPA (0x030 / 4) +#define TCTRL (0x100 / 4) +#define TSTAT (0x104 / 4) +#define DFVLAN (0x108 / 4) +#define TXIC (0x110 / 4) +#define TQUEUE (0x114 / 4) +#define TR03WT (0x140 / 4) +#define TR47WT (0x144 / 4) +#define TBDBPH (0x180 / 4) +#define TBPTR0 (0x184 / 4) +#define TBPTR1 (0x18C / 4) +#define TBPTR2 (0x194 / 4) +#define TBPTR3 (0x19C / 4) +#define TBPTR4 (0x1A4 / 4) +#define TBPTR5 (0x1AC / 4) +#define TBPTR6 (0x1B4 / 4) +#define TBPTR7 (0x1BC / 4) +#define TBASEH (0x200 / 4) +#define TBASE0 (0x204 / 4) +#define TBASE1 (0x20C / 4) +#define TBASE2 (0x214 / 4) +#define TBASE3 (0x21C / 4) +#define TBASE4 (0x224 / 4) +#define TBASE5 (0x22C / 4) +#define TBASE6 (0x234 / 4) +#define TBASE7 (0x23C / 4) +#define TMR_TXTS1_ID (0x280 / 4) +#define TMR_TXTS2_ID (0x284 / 4) +#define TMR_TXTS1_H (0x2C0 / 4) +#define TMR_TXTS1_L (0x2C4 / 4) +#define TMR_TXTS2_H (0x2C8 / 4) +#define TMR_TXTS2_L (0x2CC / 4) +#define RCTRL (0x300 / 4) +#define RSTAT (0x304 / 4) +#define RXIC (0x310 / 4) +#define RQUEUE (0x314 / 4) +#define RBIFX (0x330 / 4) +#define RQFAR (0x334 / 4) +#define RQFCR (0x338 / 4) +#define RQFPR (0x33C / 4) +#define MRBLR (0x340 / 4) +#define RBDBPH (0x380 / 4) +#define RBPTR0 (0x384 / 4) +#define RBPTR1 (0x38C / 4) +#define RBPTR2 (0x394 / 4) +#define RBPTR3 (0x39C / 4) +#define RBPTR4 (0x3A4 / 4) +#define RBPTR5 (0x3AC / 4) +#define RBPTR6 (0x3B4 / 4) +#define RBPTR7 (0x3BC / 4) +#define RBASEH (0x400 / 4) +#define RBASE0 (0x404 / 4) +#define RBASE1 (0x40C / 4) +#define RBASE2 (0x414 / 4) +#define RBASE3 (0x41C / 4) +#define RBASE4 (0x424 / 4) +#define RBASE5 (0x42C / 4) +#define RBASE6 (0x434 / 4) +#define RBASE7 (0x43C / 4) +#define TMR_RXTS_H (0x4C0 / 4) +#define TMR_RXTS_L (0x4C4 / 4) +#define MACCFG1 (0x500 / 4) +#define MACCFG2 (0x504 / 4) +#define IPGIFG (0x508 / 4) +#define HAFDUP (0x50C / 4) +#define MAXFRM (0x510 / 4) +#define MIIMCFG (0x520 / 4) +#define MIIMCOM (0x524 / 4) +#define MIIMADD (0x528 / 4) +#define MIIMCON (0x52C / 4) +#define MIIMSTAT (0x530 / 4) +#define MIIMIND (0x534 / 4) +#define IFSTAT (0x53C / 4) +#define MACSTNADDR1 (0x540 / 4) +#define MACSTNADDR2 (0x544 / 4) +#define MAC01ADDR1 (0x548 / 4) +#define MAC01ADDR2 (0x54C / 4) +#define MAC02ADDR1 (0x550 / 4) +#define MAC02ADDR2 (0x554 / 4) +#define MAC03ADDR1 (0x558 / 4) +#define MAC03ADDR2 (0x55C / 4) +#define MAC04ADDR1 (0x560 / 4) +#define MAC04ADDR2 (0x564 / 4) +#define MAC05ADDR1 (0x568 / 4) +#define MAC05ADDR2 (0x56C / 4) +#define MAC06ADDR1 (0x570 / 4) +#define MAC06ADDR2 (0x574 / 4) +#define MAC07ADDR1 (0x578 / 4) +#define MAC07ADDR2 (0x57C / 4) +#define MAC08ADDR1 (0x580 / 4) +#define MAC08ADDR2 (0x584 / 4) +#define MAC09ADDR1 (0x588 / 4) +#define MAC09ADDR2 (0x58C / 4) +#define MAC10ADDR1 (0x590 / 4) +#define MAC10ADDR2 (0x594 / 4) +#define MAC11ADDR1 (0x598 / 4) +#define MAC11ADDR2 (0x59C / 4) +#define MAC12ADDR1 (0x5A0 / 4) +#define MAC12ADDR2 (0x5A4 / 4) +#define MAC13ADDR1 (0x5A8 / 4) +#define MAC13ADDR2 (0x5AC / 4) +#define MAC14ADDR1 (0x5B0 / 4) +#define MAC14ADDR2 (0x5B4 / 4) +#define MAC15ADDR1 (0x5B8 / 4) +#define MAC15ADDR2 (0x5BC / 4) +#define TR64 (0x680 / 4) +#define TR127 (0x684 / 4) +#define TR255 (0x688 / 4) +#define TR511 (0x68C / 4) +#define TR1K (0x690 / 4) +#define TRMAX (0x694 / 4) +#define TRMGV (0x698 / 4) +#define RBYT (0x69C / 4) +#define RPKT (0x6A0 / 4) +#define RFCS (0x6A4 / 4) +#define RMCA (0x6A8 / 4) +#define RBCA (0x6AC / 4) +#define RXCF (0x6B0 / 4) +#define RXPF (0x6B4 / 4) +#define RXUO (0x6B8 / 4) +#define RALN (0x6BC / 4) +#define RFLR (0x6C0 / 4) +#define RCDE (0x6C4 / 4) +#define RCSE (0x6C8 / 4) +#define RUND (0x6CC / 4) +#define ROVR (0x6D0 / 4) +#define RFRG (0x6D4 / 4) +#define RJBR (0x6D8 / 4) +#define RDRP (0x6DC / 4) +#define TBYT (0x6E0 / 4) +#define TPKT (0x6E4 / 4) +#define TMCA (0x6E8 / 4) +#define TBCA (0x6EC / 4) +#define TXPF (0x6F0 / 4) +#define TDFR (0x6F4 / 4) +#define TEDF (0x6F8 / 4) +#define TSCL (0x6FC / 4) +#define TMCL (0x700 / 4) +#define TLCL (0x704 / 4) +#define TXCL (0x708 / 4) +#define TNCL (0x70C / 4) +#define TDRP (0x714 / 4) +#define TJBR (0x718 / 4) +#define TFCS (0x71C / 4) +#define TXCF (0x720 / 4) +#define TOVR (0x724 / 4) +#define TUND (0x728 / 4) +#define TFRG (0x72C / 4) +#define CAR1 (0x730 / 4) +#define CAR2 (0x734 / 4) +#define CAM1 (0x738 / 4) +#define CAM2 (0x73C / 4) +#define RREJ (0x740 / 4) +#define IGADDR0 (0x800 / 4) +#define IGADDR1 (0x804 / 4) +#define IGADDR2 (0x808 / 4) +#define IGADDR3 (0x80C / 4) +#define IGADDR4 (0x810 / 4) +#define IGADDR5 (0x814 / 4) +#define IGADDR6 (0x818 / 4) +#define IGADDR7 (0x81C / 4) +#define GADDR0 (0x880 / 4) +#define GADDR1 (0x884 / 4) +#define GADDR2 (0x888 / 4) +#define GADDR3 (0x88C / 4) +#define GADDR4 (0x890 / 4) +#define GADDR5 (0x894 / 4) +#define GADDR6 (0x898 / 4) +#define GADDR7 (0x89C / 4) +#define ATTR (0xBF8 / 4) +#define ATTRELI (0xBFC / 4) +#define RQPRM0 (0xC00 / 4) +#define RQPRM1 (0xC04 / 4) +#define RQPRM2 (0xC08 / 4) +#define RQPRM3 (0xC0C / 4) +#define RQPRM4 (0xC10 / 4) +#define RQPRM5 (0xC14 / 4) +#define RQPRM6 (0xC18 / 4) +#define RQPRM7 (0xC1C / 4) +#define RFBPTR0 (0xC44 / 4) +#define RFBPTR1 (0xC4C / 4) +#define RFBPTR2 (0xC54 / 4) +#define RFBPTR3 (0xC5C / 4) +#define RFBPTR4 (0xC64 / 4) +#define RFBPTR5 (0xC6C / 4) +#define RFBPTR6 (0xC74 / 4) +#define RFBPTR7 (0xC7C / 4) +#define TMR_CTRL (0xE00 / 4) +#define TMR_TEVENT (0xE04 / 4) +#define TMR_TEMASK (0xE08 / 4) +#define TMR_PEVENT (0xE0C / 4) +#define TMR_PEMASK (0xE10 / 4) +#define TMR_STAT (0xE14 / 4) +#define TMR_CNT_H (0xE18 / 4) +#define TMR_CNT_L (0xE1C / 4) +#define TMR_ADD (0xE20 / 4) +#define TMR_ACC (0xE24 / 4) +#define TMR_PRSC (0xE28 / 4) +#define TMROFF_H (0xE30 / 4) +#define TMROFF_L (0xE34 / 4) +#define TMR_ALARM1_H (0xE40 / 4) +#define TMR_ALARM1_L (0xE44 / 4) +#define TMR_ALARM2_H (0xE48 / 4) +#define TMR_ALARM2_L (0xE4C / 4) +#define TMR_FIPER1 (0xE80 / 4) +#define TMR_FIPER2 (0xE84 / 4) +#define TMR_FIPER3 (0xE88 / 4) +#define TMR_ETTS1_H (0xEA0 / 4) +#define TMR_ETTS1_L (0xEA4 / 4) +#define TMR_ETTS2_H (0xEA8 / 4) +#define TMR_ETTS2_L (0xEAC / 4) + +#endif /* ! _ETSEC_REGISTERS_H_ */ diff --git a/hw/net/fsl_etsec/rings.c b/hw/net/fsl_etsec/rings.c new file mode 100644 index 0000000000..77602722b3 --- /dev/null +++ b/hw/net/fsl_etsec/rings.c @@ -0,0 +1,650 @@ +/* + * QEMU Freescale eTSEC Emulator + * + * Copyright (c) 2011-2013 AdaCore + * + * 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 "net/checksum.h" + +#include "etsec.h" +#include "registers.h" + +/* #define ETSEC_RING_DEBUG */ +/* #define HEX_DUMP */ +/* #define DEBUG_BD */ + +#ifdef ETSEC_RING_DEBUG +static const int debug_etsec = 1; +#else +static const int debug_etsec; +#endif + +#define RING_DEBUG(fmt, ...) do { \ + if (debug_etsec) { \ + qemu_log(fmt , ## __VA_ARGS__); \ + } \ + } while (0) + +#ifdef DEBUG_BD + +static void print_tx_bd_flags(uint16_t flags) +{ + qemu_log(" Ready: %d\n", !!(flags & BD_TX_READY)); + qemu_log(" PAD/CRC: %d\n", !!(flags & BD_TX_PADCRC)); + qemu_log(" Wrap: %d\n", !!(flags & BD_WRAP)); + qemu_log(" Interrupt: %d\n", !!(flags & BD_INTERRUPT)); + qemu_log(" Last in frame: %d\n", !!(flags & BD_LAST)); + qemu_log(" Tx CRC: %d\n", !!(flags & BD_TX_TC)); + qemu_log(" User-defined preamble / defer: %d\n", + !!(flags & BD_TX_PREDEF)); + qemu_log(" Huge frame enable / Late collision: %d\n", + !!(flags & BD_TX_HFELC)); + qemu_log(" Control frame / Retransmission Limit: %d\n", + !!(flags & BD_TX_CFRL)); + qemu_log(" Retry count: %d\n", + (flags >> BD_TX_RC_OFFSET) & BD_TX_RC_MASK); + qemu_log(" Underrun / TCP/IP off-load enable: %d\n", + !!(flags & BD_TX_TOEUN)); + qemu_log(" Truncation: %d\n", !!(flags & BD_TX_TR)); +} + +static void print_rx_bd_flags(uint16_t flags) +{ + qemu_log(" Empty: %d\n", !!(flags & BD_RX_EMPTY)); + qemu_log(" Receive software ownership: %d\n", !!(flags & BD_RX_RO1)); + qemu_log(" Wrap: %d\n", !!(flags & BD_WRAP)); + qemu_log(" Interrupt: %d\n", !!(flags & BD_INTERRUPT)); + qemu_log(" Last in frame: %d\n", !!(flags & BD_LAST)); + qemu_log(" First in frame: %d\n", !!(flags & BD_RX_FIRST)); + qemu_log(" Miss: %d\n", !!(flags & BD_RX_MISS)); + qemu_log(" Broadcast: %d\n", !!(flags & BD_RX_BROADCAST)); + qemu_log(" Multicast: %d\n", !!(flags & BD_RX_MULTICAST)); + qemu_log(" Rx frame length violation: %d\n", !!(flags & BD_RX_LG)); + qemu_log(" Rx non-octet aligned frame: %d\n", !!(flags & BD_RX_NO)); + qemu_log(" Short frame: %d\n", !!(flags & BD_RX_SH)); + qemu_log(" Rx CRC Error: %d\n", !!(flags & BD_RX_CR)); + qemu_log(" Overrun: %d\n", !!(flags & BD_RX_OV)); + qemu_log(" Truncation: %d\n", !!(flags & BD_RX_TR)); +} + + +static void print_bd(eTSEC_rxtx_bd bd, int mode, uint32_t index) +{ + qemu_log("eTSEC %s Data Buffer Descriptor (%u)\n", + mode == eTSEC_TRANSMIT ? "Transmit" : "Receive", + index); + qemu_log(" Flags : 0x%04x\n", bd.flags); + if (mode == eTSEC_TRANSMIT) { + print_tx_bd_flags(bd.flags); + } else { + print_rx_bd_flags(bd.flags); + } + qemu_log(" Length : 0x%04x\n", bd.length); + qemu_log(" Pointer : 0x%08x\n", bd.bufptr); +} + +#endif /* DEBUG_BD */ + +static void read_buffer_descriptor(eTSEC *etsec, + hwaddr addr, + eTSEC_rxtx_bd *bd) +{ + assert(bd != NULL); + + RING_DEBUG("READ Buffer Descriptor @ 0x" TARGET_FMT_plx"\n", addr); + cpu_physical_memory_read(addr, + bd, + sizeof(eTSEC_rxtx_bd)); + + if (etsec->regs[DMACTRL].value & DMACTRL_LE) { + bd->flags = lduw_le_p(&bd->flags); + bd->length = lduw_le_p(&bd->length); + bd->bufptr = ldl_le_p(&bd->bufptr); + } else { + bd->flags = lduw_be_p(&bd->flags); + bd->length = lduw_be_p(&bd->length); + bd->bufptr = ldl_be_p(&bd->bufptr); + } +} + +static void write_buffer_descriptor(eTSEC *etsec, + hwaddr addr, + eTSEC_rxtx_bd *bd) +{ + assert(bd != NULL); + + if (etsec->regs[DMACTRL].value & DMACTRL_LE) { + stw_le_p(&bd->flags, bd->flags); + stw_le_p(&bd->length, bd->length); + stl_le_p(&bd->bufptr, bd->bufptr); + } else { + stw_be_p(&bd->flags, bd->flags); + stw_be_p(&bd->length, bd->length); + stl_be_p(&bd->bufptr, bd->bufptr); + } + + RING_DEBUG("Write Buffer Descriptor @ 0x" TARGET_FMT_plx"\n", addr); + cpu_physical_memory_write(addr, + bd, + sizeof(eTSEC_rxtx_bd)); +} + +static void ievent_set(eTSEC *etsec, + uint32_t flags) +{ + etsec->regs[IEVENT].value |= flags; + + if ((flags & IEVENT_TXB && etsec->regs[IMASK].value & IMASK_TXBEN) + || (flags & IEVENT_TXF && etsec->regs[IMASK].value & IMASK_TXFEN)) { + qemu_irq_raise(etsec->tx_irq); + RING_DEBUG("%s Raise Tx IRQ\n", __func__); + } + + if ((flags & IEVENT_RXB && etsec->regs[IMASK].value & IMASK_RXBEN) + || (flags & IEVENT_RXF && etsec->regs[IMASK].value & IMASK_RXFEN)) { + qemu_irq_pulse(etsec->rx_irq); + RING_DEBUG("%s Raise Rx IRQ\n", __func__); + } +} + +static void tx_padding_and_crc(eTSEC *etsec, uint32_t min_frame_len) +{ + int add = min_frame_len - etsec->tx_buffer_len; + + /* Padding */ + if (add > 0) { + RING_DEBUG("pad:%u\n", add); + etsec->tx_buffer = g_realloc(etsec->tx_buffer, + etsec->tx_buffer_len + add); + + memset(etsec->tx_buffer + etsec->tx_buffer_len, 0x0, add); + etsec->tx_buffer_len += add; + } + + /* Never add CRC in QEMU */ +} + +static void process_tx_fcb(eTSEC *etsec) +{ + uint8_t flags = (uint8_t)(*etsec->tx_buffer); + /* L3 header offset from start of frame */ + uint8_t l3_header_offset = (uint8_t)*(etsec->tx_buffer + 3); + /* L4 header offset from start of L3 header */ + uint8_t l4_header_offset = (uint8_t)*(etsec->tx_buffer + 2); + /* L3 header */ + uint8_t *l3_header = etsec->tx_buffer + 8 + l3_header_offset; + /* L4 header */ + uint8_t *l4_header = l3_header + l4_header_offset; + + /* if packet is IP4 and IP checksum is requested */ + if (flags & FCB_TX_IP && flags & FCB_TX_CIP) { + /* do IP4 checksum (TODO This funtion does TCP/UDP checksum but not sure + * if it also does IP4 checksum. */ + net_checksum_calculate(etsec->tx_buffer + 8, + etsec->tx_buffer_len - 8); + } + /* TODO Check the correct usage of the PHCS field of the FCB in case the NPH + * flag is on */ + + /* if packet is IP4 and TCP or UDP */ + if (flags & FCB_TX_IP && flags & FCB_TX_TUP) { + /* if UDP */ + if (flags & FCB_TX_UDP) { + /* if checksum is requested */ + if (flags & FCB_TX_CTU) { + /* do UDP checksum */ + + net_checksum_calculate(etsec->tx_buffer + 8, + etsec->tx_buffer_len - 8); + } else { + /* set checksum field to 0 */ + l4_header[6] = 0; + l4_header[7] = 0; + } + } else if (flags & FCB_TX_CTU) { /* if TCP and checksum is requested */ + /* do TCP checksum */ + net_checksum_calculate(etsec->tx_buffer + 8, + etsec->tx_buffer_len - 8); + } + } +} + +static void process_tx_bd(eTSEC *etsec, + eTSEC_rxtx_bd *bd) +{ + uint8_t *tmp_buff = NULL; + hwaddr tbdbth = (hwaddr)(etsec->regs[TBDBPH].value & 0xF) << 32; + + if (bd->length == 0) { + /* ERROR */ + return; + } + + if (etsec->tx_buffer_len == 0) { + /* It's the first BD */ + etsec->first_bd = *bd; + } + + /* TODO: if TxBD[TOE/UN] skip the Tx Frame Control Block*/ + + /* Load this Data Buffer */ + etsec->tx_buffer = g_realloc(etsec->tx_buffer, + etsec->tx_buffer_len + bd->length); + tmp_buff = etsec->tx_buffer + etsec->tx_buffer_len; + cpu_physical_memory_read(bd->bufptr + tbdbth, tmp_buff, bd->length); + + /* Update buffer length */ + etsec->tx_buffer_len += bd->length; + + + if (etsec->tx_buffer_len != 0 && (bd->flags & BD_LAST)) { + if (etsec->regs[MACCFG1].value & MACCFG1_TX_EN) { + /* MAC Transmit enabled */ + + /* Process offload Tx FCB */ + if (etsec->first_bd.flags & BD_TX_TOEUN) { + process_tx_fcb(etsec); + } + + if (etsec->first_bd.flags & BD_TX_PADCRC + || etsec->regs[MACCFG2].value & MACCFG2_PADCRC) { + + /* Padding and CRC (Padding implies CRC) */ + tx_padding_and_crc(etsec, 64); + + } else if (etsec->first_bd.flags & BD_TX_TC + || etsec->regs[MACCFG2].value & MACCFG2_CRC_EN) { + + /* Only CRC */ + /* Never add CRC in QEMU */ + } + +#if defined(HEX_DUMP) + qemu_log("eTSEC Send packet size:%d\n", etsec->tx_buffer_len); + qemu_hexdump(etsec->tx_buffer, stderr, "", etsec->tx_buffer_len); +#endif /* ETSEC_RING_DEBUG */ + + if (etsec->first_bd.flags & BD_TX_TOEUN) { + qemu_send_packet(qemu_get_queue(etsec->nic), + etsec->tx_buffer + 8, + etsec->tx_buffer_len - 8); + } else { + qemu_send_packet(qemu_get_queue(etsec->nic), + etsec->tx_buffer, + etsec->tx_buffer_len); + } + + } + + etsec->tx_buffer_len = 0; + + if (bd->flags & BD_INTERRUPT) { + ievent_set(etsec, IEVENT_TXF); + } + } else { + if (bd->flags & BD_INTERRUPT) { + ievent_set(etsec, IEVENT_TXB); + } + } + + /* Update DB flags */ + + /* Clear Ready */ + bd->flags &= ~BD_TX_READY; + + /* Clear Defer */ + bd->flags &= ~BD_TX_PREDEF; + + /* Clear Late Collision */ + bd->flags &= ~BD_TX_HFELC; + + /* Clear Retransmission Limit */ + bd->flags &= ~BD_TX_CFRL; + + /* Clear Retry Count */ + bd->flags &= ~(BD_TX_RC_MASK << BD_TX_RC_OFFSET); + + /* Clear Underrun */ + bd->flags &= ~BD_TX_TOEUN; + + /* Clear Truncation */ + bd->flags &= ~BD_TX_TR; +} + +void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr) +{ + hwaddr ring_base = 0; + hwaddr bd_addr = 0; + eTSEC_rxtx_bd bd; + uint16_t bd_flags; + + if (!(etsec->regs[MACCFG1].value & MACCFG1_TX_EN)) { + RING_DEBUG("%s: MAC Transmit not enabled\n", __func__); + return; + } + + ring_base = (hwaddr)(etsec->regs[TBASEH].value & 0xF) << 32; + ring_base += etsec->regs[TBASE0 + ring_nbr].value & ~0x7; + bd_addr = etsec->regs[TBPTR0 + ring_nbr].value & ~0x7; + + do { + read_buffer_descriptor(etsec, bd_addr, &bd); + +#ifdef DEBUG_BD + print_bd(bd, + eTSEC_TRANSMIT, + (bd_addr - ring_base) / sizeof(eTSEC_rxtx_bd)); + +#endif /* DEBUG_BD */ + + /* Save flags before BD update */ + bd_flags = bd.flags; + + if (bd_flags & BD_TX_READY) { + process_tx_bd(etsec, &bd); + + /* Write back BD after update */ + write_buffer_descriptor(etsec, bd_addr, &bd); + } + + /* Wrap or next BD */ + if (bd_flags & BD_WRAP) { + bd_addr = ring_base; + } else { + bd_addr += sizeof(eTSEC_rxtx_bd); + } + + } while (bd_addr != ring_base); + + bd_addr = ring_base; + + /* Save the Buffer Descriptor Pointers to current bd */ + etsec->regs[TBPTR0 + ring_nbr].value = bd_addr; + + /* Set transmit halt THLTx */ + etsec->regs[TSTAT].value |= 1 << (31 - ring_nbr); +} + +static void fill_rx_bd(eTSEC *etsec, + eTSEC_rxtx_bd *bd, + const uint8_t **buf, + size_t *size) +{ + uint16_t to_write; + hwaddr bufptr = bd->bufptr + + ((hwaddr)(etsec->regs[TBDBPH].value & 0xF) << 32); + uint8_t padd[etsec->rx_padding]; + uint8_t rem; + + RING_DEBUG("eTSEC fill Rx buffer @ 0x%016" HWADDR_PRIx + " size:%zu(padding + crc:%u) + fcb:%u\n", + bufptr, *size, etsec->rx_padding, etsec->rx_fcb_size); + + bd->length = 0; + + /* This operation will only write FCB */ + if (etsec->rx_fcb_size != 0) { + + cpu_physical_memory_write(bufptr, etsec->rx_fcb, etsec->rx_fcb_size); + + bufptr += etsec->rx_fcb_size; + bd->length += etsec->rx_fcb_size; + etsec->rx_fcb_size = 0; + + } + + /* We remove padding from the computation of to_write because it is not + * allocated in the buffer. + */ + to_write = MIN(*size - etsec->rx_padding, + etsec->regs[MRBLR].value - etsec->rx_fcb_size); + + /* This operation can only write packet data and no padding */ + if (to_write > 0) { + cpu_physical_memory_write(bufptr, *buf, to_write); + + *buf += to_write; + bufptr += to_write; + *size -= to_write; + + bd->flags &= ~BD_RX_EMPTY; + bd->length += to_write; + } + + if (*size == etsec->rx_padding) { + /* The remaining bytes are only for padding which is not actually + * allocated in the data buffer. + */ + + rem = MIN(etsec->regs[MRBLR].value - bd->length, etsec->rx_padding); + + if (rem > 0) { + memset(padd, 0x0, sizeof(padd)); + etsec->rx_padding -= rem; + *size -= rem; + bd->length += rem; + cpu_physical_memory_write(bufptr, padd, rem); + } + } +} + +static void rx_init_frame(eTSEC *etsec, const uint8_t *buf, size_t size) +{ + uint32_t fcb_size = 0; + uint8_t prsdep = (etsec->regs[RCTRL].value >> RCTRL_PRSDEP_OFFSET) + & RCTRL_PRSDEP_MASK; + + if (prsdep != 0) { + /* Prepend FCB (FCB size + RCTRL[PAL]) */ + fcb_size = 8 + ((etsec->regs[RCTRL].value >> 16) & 0x1F); + + etsec->rx_fcb_size = fcb_size; + + /* TODO: fill_FCB(etsec); */ + memset(etsec->rx_fcb, 0x0, sizeof(etsec->rx_fcb)); + + } else { + etsec->rx_fcb_size = 0; + } + + if (etsec->rx_buffer != NULL) { + g_free(etsec->rx_buffer); + } + + /* Do not copy the frame for now */ + etsec->rx_buffer = (uint8_t *)buf; + etsec->rx_buffer_len = size; + + /* CRC padding (We don't have to compute the CRC) */ + etsec->rx_padding = 4; + + etsec->rx_first_in_frame = 1; + etsec->rx_remaining_data = etsec->rx_buffer_len; + RING_DEBUG("%s: rx_buffer_len:%u rx_padding+crc:%u\n", __func__, + etsec->rx_buffer_len, etsec->rx_padding); +} + +void etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size) +{ + int ring_nbr = 0; /* Always use ring0 (no filer) */ + + if (etsec->rx_buffer_len != 0) { + RING_DEBUG("%s: We can't receive now," + " a buffer is already in the pipe\n", __func__); + return; + } + + if (etsec->regs[RSTAT].value & 1 << (23 - ring_nbr)) { + RING_DEBUG("%s: The ring is halted\n", __func__); + return; + } + + if (etsec->regs[DMACTRL].value & DMACTRL_GRS) { + RING_DEBUG("%s: Graceful receive stop\n", __func__); + return; + } + + if (!(etsec->regs[MACCFG1].value & MACCFG1_RX_EN)) { + RING_DEBUG("%s: MAC Receive not enabled\n", __func__); + return; + } + + if ((etsec->regs[RCTRL].value & RCTRL_RSF) && (size < 60)) { + /* CRC is not in the packet yet, so short frame is below 60 bytes */ + RING_DEBUG("%s: Drop short frame\n", __func__); + return; + } + + rx_init_frame(etsec, buf, size); + + etsec_walk_rx_ring(etsec, ring_nbr); +} + +void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr) +{ + hwaddr ring_base = 0; + hwaddr bd_addr = 0; + hwaddr start_bd_addr = 0; + eTSEC_rxtx_bd bd; + uint16_t bd_flags; + size_t remaining_data; + const uint8_t *buf; + uint8_t *tmp_buf; + size_t size; + + if (etsec->rx_buffer_len == 0) { + /* No frame to send */ + RING_DEBUG("No frame to send\n"); + return; + } + + remaining_data = etsec->rx_remaining_data + etsec->rx_padding; + buf = etsec->rx_buffer + + (etsec->rx_buffer_len - etsec->rx_remaining_data); + size = etsec->rx_buffer_len + etsec->rx_padding; + + ring_base = (hwaddr)(etsec->regs[RBASEH].value & 0xF) << 32; + ring_base += etsec->regs[RBASE0 + ring_nbr].value & ~0x7; + start_bd_addr = bd_addr = etsec->regs[RBPTR0 + ring_nbr].value & ~0x7; + + do { + read_buffer_descriptor(etsec, bd_addr, &bd); + +#ifdef DEBUG_BD + print_bd(bd, + eTSEC_RECEIVE, + (bd_addr - ring_base) / sizeof(eTSEC_rxtx_bd)); + +#endif /* DEBUG_BD */ + + /* Save flags before BD update */ + bd_flags = bd.flags; + + if (bd_flags & BD_RX_EMPTY) { + fill_rx_bd(etsec, &bd, &buf, &remaining_data); + + if (etsec->rx_first_in_frame) { + bd.flags |= BD_RX_FIRST; + etsec->rx_first_in_frame = 0; + etsec->rx_first_bd = bd; + } + + /* Last in frame */ + if (remaining_data == 0) { + + /* Clear flags */ + + bd.flags &= ~0x7ff; + + bd.flags |= BD_LAST; + + /* NOTE: non-octet aligned frame is impossible in qemu */ + + if (size >= etsec->regs[MAXFRM].value) { + /* frame length violation */ + qemu_log("%s frame length violation: size:%zu MAXFRM:%d\n", + __func__, size, etsec->regs[MAXFRM].value); + + bd.flags |= BD_RX_LG; + } + + if (size < 64) { + /* Short frame */ + bd.flags |= BD_RX_SH; + } + + /* TODO: Broadcast and Multicast */ + + if (bd.flags | BD_INTERRUPT) { + /* Set RXFx */ + etsec->regs[RSTAT].value |= 1 << (7 - ring_nbr); + + /* Set IEVENT */ + ievent_set(etsec, IEVENT_RXF); + } + + } else { + if (bd.flags | BD_INTERRUPT) { + /* Set IEVENT */ + ievent_set(etsec, IEVENT_RXB); + } + } + + /* Write back BD after update */ + write_buffer_descriptor(etsec, bd_addr, &bd); + } + + /* Wrap or next BD */ + if (bd_flags & BD_WRAP) { + bd_addr = ring_base; + } else { + bd_addr += sizeof(eTSEC_rxtx_bd); + } + } while (remaining_data != 0 + && (bd_flags & BD_RX_EMPTY) + && bd_addr != start_bd_addr); + + /* Reset ring ptr */ + etsec->regs[RBPTR0 + ring_nbr].value = bd_addr; + + /* The frame is too large to fit in the Rx ring */ + if (remaining_data > 0) { + + /* Set RSTAT[QHLTx] */ + etsec->regs[RSTAT].value |= 1 << (23 - ring_nbr); + + /* Save remaining data to send the end of the frame when the ring will + * be restarted + */ + etsec->rx_remaining_data = remaining_data; + + /* Copy the frame */ + tmp_buf = g_malloc(size); + memcpy(tmp_buf, etsec->rx_buffer, size); + etsec->rx_buffer = tmp_buf; + + RING_DEBUG("no empty RxBD available any more\n"); + } else { + etsec->rx_buffer_len = 0; + etsec->rx_buffer = NULL; + } + + RING_DEBUG("eTSEC End of ring_write: remaining_data:%zu\n", remaining_data); +} diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index 1bd6f50aaa..f6fbcb56bf 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -405,6 +405,8 @@ static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu, dev->rx_bufs++; + qemu_flush_queued_packets(qemu_get_queue(dev->nic)); + DPRINTF("h_add_logical_lan_buffer(): Added buf ptr=%d rx_bufs=%d" " bd=0x%016llx\n", dev->add_buf_ptr, dev->rx_bufs, (unsigned long long)buf); diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c index 9dd77f7571..d04e6a46f8 100644 --- a/hw/net/stellaris_enet.c +++ b/hw/net/stellaris_enet.c @@ -176,7 +176,8 @@ static uint64_t stellaris_enet_read(void *opaque, hwaddr offset, return val; case 0x14: /* IA0 */ return s->conf.macaddr.a[0] | (s->conf.macaddr.a[1] << 8) - | (s->conf.macaddr.a[2] << 16) | (s->conf.macaddr.a[3] << 24); + | (s->conf.macaddr.a[2] << 16) + | ((uint32_t)s->conf.macaddr.a[3] << 24); case 0x18: /* IA1 */ return s->conf.macaddr.a[4] | (s->conf.macaddr.a[5] << 8); case 0x1c: /* THR */ diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 854997d9ba..a1de2f43a0 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -106,7 +106,7 @@ struct vhost_net *vhost_net_init(NetClientState *backend, int devfd, goto fail; } net->nc = backend; - net->dev.backend_features = tap_has_vnet_hdr(backend) ? 0 : + net->dev.backend_features = qemu_has_vnet_hdr(backend) ? 0 : (1 << VHOST_NET_F_VIRTIO_NET_HDR); net->backend = r; @@ -117,8 +117,8 @@ struct vhost_net *vhost_net_init(NetClientState *backend, int devfd, if (r < 0) { goto fail; } - if (!tap_has_vnet_hdr_len(backend, - sizeof(struct virtio_net_hdr_mrg_rxbuf))) { + if (!qemu_has_vnet_hdr_len(backend, + sizeof(struct virtio_net_hdr_mrg_rxbuf))) { net->dev.features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF); } if (~net->dev.features & net->dev.backend_features) { diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 36266083b2..3c0342e17a 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -325,11 +325,7 @@ static void peer_test_vnet_hdr(VirtIONet *n) return; } - if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { - return; - } - - n->has_vnet_hdr = tap_has_vnet_hdr(nc->peer); + n->has_vnet_hdr = qemu_has_vnet_hdr(nc->peer); } static int peer_has_vnet_hdr(VirtIONet *n) @@ -342,7 +338,7 @@ static int peer_has_ufo(VirtIONet *n) if (!peer_has_vnet_hdr(n)) return 0; - n->has_ufo = tap_has_ufo(qemu_get_queue(n->nic)->peer); + n->has_ufo = qemu_has_ufo(qemu_get_queue(n->nic)->peer); return n->has_ufo; } @@ -361,8 +357,8 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs) nc = qemu_get_subqueue(n->nic, i); if (peer_has_vnet_hdr(n) && - tap_has_vnet_hdr_len(nc->peer, n->guest_hdr_len)) { - tap_set_vnet_hdr_len(nc->peer, n->guest_hdr_len); + qemu_has_vnet_hdr_len(nc->peer, n->guest_hdr_len)) { + qemu_set_vnet_hdr_len(nc->peer, n->guest_hdr_len); n->host_hdr_len = n->guest_hdr_len; } } @@ -463,7 +459,7 @@ static uint32_t virtio_net_bad_features(VirtIODevice *vdev) static void virtio_net_apply_guest_offloads(VirtIONet *n) { - tap_set_offload(qemu_get_subqueue(n->nic, 0)->peer, + qemu_set_offload(qemu_get_queue(n->nic)->peer, !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_CSUM)), !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO4)), !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO6)), @@ -1544,7 +1540,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) peer_test_vnet_hdr(n); if (peer_has_vnet_hdr(n)) { for (i = 0; i < n->max_queues; i++) { - tap_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, true); + qemu_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, true); } n->host_hdr_len = sizeof(struct virtio_net_hdr); } else { diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 19687aa03c..5be807ce82 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -1290,12 +1290,12 @@ static void vmxnet3_update_features(VMXNET3State *s) s->lro_supported, rxcso_supported, s->rx_vlan_stripping); if (s->peer_has_vhdr) { - tap_set_offload(qemu_get_queue(s->nic)->peer, - rxcso_supported, - s->lro_supported, - s->lro_supported, - 0, - 0); + qemu_set_offload(qemu_get_queue(s->nic)->peer, + rxcso_supported, + s->lro_supported, + s->lro_supported, + 0, + 0); } } @@ -1883,11 +1883,9 @@ static NetClientInfo net_vmxnet3_info = { static bool vmxnet3_peer_has_vnet_hdr(VMXNET3State *s) { - NetClientState *peer = qemu_get_queue(s->nic)->peer; + NetClientState *nc = qemu_get_queue(s->nic); - if ((NULL != peer) && - (peer->info->type == NET_CLIENT_OPTIONS_KIND_TAP) && - tap_has_vnet_hdr(peer)) { + if (qemu_has_vnet_hdr(nc->peer)) { return true; } @@ -1935,10 +1933,10 @@ static void vmxnet3_net_init(VMXNET3State *s) s->lro_supported = false; if (s->peer_has_vhdr) { - tap_set_vnet_hdr_len(qemu_get_queue(s->nic)->peer, + qemu_set_vnet_hdr_len(qemu_get_queue(s->nic)->peer, sizeof(struct virtio_net_hdr)); - tap_using_vnet_hdr(qemu_get_queue(s->nic)->peer, 1); + qemu_using_vnet_hdr(qemu_get_queue(s->nic)->peer, 1); } qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index b37ce9d633..8a08752613 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -238,6 +238,7 @@ static int ppce500_load_device_tree(QEMUMachineInitArgs *args, the first node as boot node and be happy */ for (i = smp_cpus - 1; i >= 0; i--) { CPUState *cpu; + PowerPCCPU *pcpu; char cpu_name[128]; uint64_t cpu_release_addr = MPC8544_SPIN_BASE + (i * 0x20); @@ -246,14 +247,16 @@ static int ppce500_load_device_tree(QEMUMachineInitArgs *args, continue; } env = cpu->env_ptr; + pcpu = POWERPC_CPU(cpu); snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", - cpu->cpu_index); + ppc_get_vcpu_dt_id(pcpu)); qemu_fdt_add_subnode(fdt, cpu_name); qemu_fdt_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq); qemu_fdt_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu"); - qemu_fdt_setprop_cell(fdt, cpu_name, "reg", cpu->cpu_index); + qemu_fdt_setprop_cell(fdt, cpu_name, "reg", + ppc_get_vcpu_dt_id(pcpu)); qemu_fdt_setprop_cell(fdt, cpu_name, "d-cache-line-size", env->dcache_line_size); qemu_fdt_setprop_cell(fdt, cpu_name, "i-cache-line-size", diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 114be64480..0e82719b69 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -26,6 +26,7 @@ #include "hw/ppc/ppc_e500.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" +#include "sysemu/cpus.h" #include "hw/timer/m48t59.h" #include "qemu/log.h" #include "hw/loader.h" @@ -1362,3 +1363,24 @@ int PPC_NVRAM_set_params (nvram_t *nvram, uint16_t NVRAM_size, return 0; } + +/* CPU device-tree ID helpers */ +int ppc_get_vcpu_dt_id(PowerPCCPU *cpu) +{ + return cpu->cpu_dt_id; +} + +PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id) +{ + CPUState *cs; + + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + + if (cpu->cpu_dt_id == cpu_dt_id) { + return cpu; + } + } + + return NULL; +} diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 93d02c1e50..bf46c380ec 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -49,6 +49,7 @@ #include "exec/address-spaces.h" #include "hw/usb.h" #include "qemu/config-file.h" +#include "qemu/error-report.h" #include <libfdt.h> @@ -206,19 +207,20 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr) CPU_FOREACH(cpu) { DeviceClass *dc = DEVICE_GET_CLASS(cpu); + int index = ppc_get_vcpu_dt_id(POWERPC_CPU(cpu)); uint32_t associativity[] = {cpu_to_be32(0x5), cpu_to_be32(0x0), cpu_to_be32(0x0), cpu_to_be32(0x0), cpu_to_be32(cpu->numa_node), - cpu_to_be32(cpu->cpu_index)}; + cpu_to_be32(index)}; - if ((cpu->cpu_index % smt) != 0) { + if ((index % smt) != 0) { continue; } snprintf(cpu_model, 32, "/cpus/%s@%x", dc->fw_name, - cpu->cpu_index); + index); offset = fdt_path_offset(fdt, cpu_model); if (offset < 0) { @@ -367,7 +369,7 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base, CPUPPCState *env = &cpu->env; DeviceClass *dc = DEVICE_GET_CLASS(cs); PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); - int index = cs->cpu_index; + int index = ppc_get_vcpu_dt_id(cpu); uint32_t servers_prop[smp_threads]; uint32_t gservers_prop[smp_threads * 2]; char *nodename; @@ -685,6 +687,7 @@ static void spapr_reset_htab(sPAPREnvironment *spapr) if (shift > 0) { /* Kernel handles htab, we don't need to allocate one */ spapr->htab_shift = shift; + kvmppc_kern_htab = true; } else { if (!spapr->htab) { /* Allocate an htab if we don't yet have one */ @@ -740,8 +743,21 @@ static void spapr_cpu_reset(void *opaque) env->spr[SPR_HIOR] = 0; env->external_htab = (uint8_t *)spapr->htab; + if (kvm_enabled() && !env->external_htab) { + /* + * HV KVM, set external_htab to 1 so our ppc_hash64_load_hpte* + * functions do the right thing. + */ + env->external_htab = (void *)1; + } env->htab_base = -1; - env->htab_mask = HTAB_SIZE(spapr) - 1; + /* + * htab_mask is the mask used to normalize hash value to PTEG index. + * htab_shift is log2 of hash table size. + * We have 8 hpte per group, and each hpte is 16 bytes. + * ie have 128 bytes per hpte entry. + */ + env->htab_mask = (1ULL << ((spapr)->htab_shift - 7)) - 1; env->spr[SPR_SDR1] = (target_ulong)(uintptr_t)spapr->htab | (spapr->htab_shift - 18); } @@ -1305,20 +1321,15 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0); - if (kernel_size < 0) { + if (kernel_size == ELF_LOAD_WRONG_ENDIAN) { kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, NULL, &lowaddr, NULL, 0, ELF_MACHINE, 0); kernel_le = kernel_size > 0; } if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, - KERNEL_LOAD_ADDR, - load_limit - KERNEL_LOAD_ADDR); - } - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - kernel_filename); + fprintf(stderr, "qemu: error loading %s: %s\n", + kernel_filename, load_elf_strerror(kernel_size)); exit(1); } @@ -1366,6 +1377,24 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) assert(spapr->fdt_skel != NULL); } +static int spapr_kvm_type(const char *vm_type) +{ + if (!vm_type) { + return 0; + } + + if (!strcmp(vm_type, "HV")) { + return 1; + } + + if (!strcmp(vm_type, "PR")) { + return 2; + } + + error_report("Unknown kvm-type specified '%s'", vm_type); + exit(1); +} + static QEMUMachine spapr_machine = { .name = "pseries", .desc = "pSeries Logical Partition (PAPR compliant)", @@ -1376,6 +1405,7 @@ static QEMUMachine spapr_machine = { .max_cpus = MAX_CPUS, .no_parallel = 1, .default_boot_order = NULL, + .kvm_type = spapr_kvm_type, }; static void spapr_machine_init(void) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 3ffcc65f03..d918780746 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -40,6 +40,17 @@ static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r, return rb; } +static inline bool valid_pte_index(CPUPPCState *env, target_ulong pte_index) +{ + /* + * hash value/pteg group index is normalized by htab_mask + */ + if (((pte_index & ~7ULL) / HPTES_PER_GROUP) & ~env->htab_mask) { + return false; + } + return true; +} + static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { @@ -50,8 +61,8 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong ptel = args[3]; target_ulong page_shift = 12; target_ulong raddr; - target_ulong i; - hwaddr hpte; + target_ulong index; + uint64_t token; /* only handle 4k and 16M pages for now */ if (pteh & HPTE64_V_LARGE) { @@ -91,33 +102,37 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, pteh &= ~0x60ULL; - if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { + if (!valid_pte_index(env, pte_index)) { return H_PARAMETER; } + + index = 0; if (likely((flags & H_EXACT) == 0)) { pte_index &= ~7ULL; - hpte = pte_index * HASH_PTE_SIZE_64; - for (i = 0; ; ++i) { - if (i == 8) { + token = ppc_hash64_start_access(cpu, pte_index); + do { + if (index == 8) { + ppc_hash64_stop_access(token); return H_PTEG_FULL; } - if ((ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) == 0) { + if ((ppc_hash64_load_hpte0(env, token, index) & HPTE64_V_VALID) == 0) { break; } - hpte += HASH_PTE_SIZE_64; - } + } while (index++); + ppc_hash64_stop_access(token); } else { - i = 0; - hpte = pte_index * HASH_PTE_SIZE_64; - if (ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) { + token = ppc_hash64_start_access(cpu, pte_index); + if (ppc_hash64_load_hpte0(env, token, 0) & HPTE64_V_VALID) { + ppc_hash64_stop_access(token); return H_PTEG_FULL; } + ppc_hash64_stop_access(token); } - ppc_hash64_store_hpte1(env, hpte, ptel); - /* eieio(); FIXME: need some sort of barrier for smp? */ - ppc_hash64_store_hpte0(env, hpte, pteh | HPTE64_V_HPTE_DIRTY); - args[0] = pte_index + i; + ppc_hash64_store_hpte(env, pte_index + index, + pteh | HPTE64_V_HPTE_DIRTY, ptel); + + args[0] = pte_index + index; return H_SUCCESS; } @@ -133,17 +148,17 @@ static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex, target_ulong flags, target_ulong *vp, target_ulong *rp) { - hwaddr hpte; + uint64_t token; target_ulong v, r, rb; - if ((ptex * HASH_PTE_SIZE_64) & ~env->htab_mask) { + if (!valid_pte_index(env, ptex)) { return REMOVE_PARM; } - hpte = ptex * HASH_PTE_SIZE_64; - - v = ppc_hash64_load_hpte0(env, hpte); - r = ppc_hash64_load_hpte1(env, hpte); + token = ppc_hash64_start_access(ppc_env_get_cpu(env), ptex); + v = ppc_hash64_load_hpte0(env, token, 0); + r = ppc_hash64_load_hpte1(env, token, 0); + ppc_hash64_stop_access(token); if ((v & HPTE64_V_VALID) == 0 || ((flags & H_AVPN) && (v & ~0x7fULL) != avpn) || @@ -152,7 +167,7 @@ static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex, } *vp = v; *rp = r; - ppc_hash64_store_hpte0(env, hpte, HPTE64_V_HPTE_DIRTY); + ppc_hash64_store_hpte(env, ptex, HPTE64_V_HPTE_DIRTY, 0); rb = compute_tlbie_rb(v, r, ptex); ppc_tlb_invalidate_one(env, rb); return REMOVE_SUCCESS; @@ -259,17 +274,17 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong flags = args[0]; target_ulong pte_index = args[1]; target_ulong avpn = args[2]; - hwaddr hpte; + uint64_t token; target_ulong v, r, rb; - if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { + if (!valid_pte_index(env, pte_index)) { return H_PARAMETER; } - hpte = pte_index * HASH_PTE_SIZE_64; - - v = ppc_hash64_load_hpte0(env, hpte); - r = ppc_hash64_load_hpte1(env, hpte); + token = ppc_hash64_start_access(cpu, pte_index); + v = ppc_hash64_load_hpte0(env, token, 0); + r = ppc_hash64_load_hpte1(env, token, 0); + ppc_hash64_stop_access(token); if ((v & HPTE64_V_VALID) == 0 || ((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) { @@ -282,11 +297,11 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr, r |= (flags << 48) & HPTE64_R_KEY_HI; r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO); rb = compute_tlbie_rb(v, r, pte_index); - ppc_hash64_store_hpte0(env, hpte, (v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY); + ppc_hash64_store_hpte(env, pte_index, + (v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY, 0); ppc_tlb_invalidate_one(env, rb); - ppc_hash64_store_hpte1(env, hpte, r); /* Don't need a memory barrier, due to qemu's global lock */ - ppc_hash64_store_hpte0(env, hpte, v | HPTE64_V_HPTE_DIRTY); + ppc_hash64_store_hpte(env, pte_index, v | HPTE64_V_HPTE_DIRTY, r); return H_SUCCESS; } @@ -299,7 +314,7 @@ static target_ulong h_read(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint8_t *hpte; int i, ridx, n_entries = 1; - if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { + if (!valid_pte_index(env, pte_index)) { return H_PARAMETER; } @@ -467,13 +482,13 @@ static target_ulong h_register_vpa(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong vpa = args[2]; target_ulong ret = H_PARAMETER; CPUPPCState *tenv; - CPUState *tcpu; + PowerPCCPU *tcpu; - tcpu = qemu_get_cpu(procno); + tcpu = ppc_get_vcpu_by_dt_id(procno); if (!tcpu) { return H_PARAMETER; } - tenv = tcpu->env_ptr; + tenv = &tcpu->env; switch (flags) { case FLAGS_REGISTER_VPA: diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index ef45f4f0cc..d9fe946818 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -243,6 +243,42 @@ static target_ulong h_put_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr, return ret; } +static target_ulong get_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, + target_ulong *tce) +{ + if (ioba >= tcet->window_size) { + hcall_dprintf("spapr_iommu_get_tce on out-of-bounds IOBA 0x" + TARGET_FMT_lx "\n", ioba); + return H_PARAMETER; + } + + *tce = tcet->table[ioba >> SPAPR_TCE_PAGE_SHIFT]; + + return H_SUCCESS; +} + +static target_ulong h_get_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong liobn = args[0]; + target_ulong ioba = args[1]; + target_ulong tce = 0; + target_ulong ret = H_PARAMETER; + sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); + + ioba &= ~(SPAPR_TCE_PAGE_SIZE - 1); + + if (tcet) { + ret = get_tce_emu(tcet, ioba, &tce); + if (!ret) { + args[0] = tce; + } + } + trace_spapr_iommu_get(liobn, ioba, ret, tce); + + return ret; +} + int spapr_dma_dt(void *fdt, int node_off, const char *propname, uint32_t liobn, uint64_t window, uint32_t size) { @@ -295,6 +331,7 @@ static void spapr_tce_table_class_init(ObjectClass *klass, void *data) /* hcall-tce */ spapr_register_hypercall(H_PUT_TCE, h_put_tce); + spapr_register_hypercall(H_GET_TCE, h_get_tce); } static TypeInfo spapr_tce_table_info = { diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 4c7c3aec12..cea9469872 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -469,6 +469,8 @@ static const MemoryRegionOps spapr_msi_ops = { void spapr_pci_msi_init(sPAPREnvironment *spapr, hwaddr addr) { + uint64_t window_size = 4096; + /* * As MSI/MSIX interrupts trigger by writing at MSI/MSIX vectors, * we need to allocate some memory to catch those writes coming @@ -476,10 +478,19 @@ void spapr_pci_msi_init(sPAPREnvironment *spapr, hwaddr addr) * As MSIMessage:addr is going to be the same and MSIMessage:data * is going to be a VIRQ number, 4 bytes of the MSI MR will only * be used. + * + * For KVM we want to ensure that this memory is a full page so that + * our memory slot is of page size granularity. */ +#ifdef CONFIG_KVM + if (kvm_enabled()) { + window_size = getpagesize(); + } +#endif + spapr->msi_win_addr = addr; memory_region_init_io(&spapr->msiwindow, NULL, &spapr_msi_ops, spapr, - "msi", getpagesize()); + "msi", window_size); memory_region_add_subregion(get_system_memory(), spapr->msi_win_addr, &spapr->msiwindow); } @@ -728,6 +739,8 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data) dc->props = spapr_phb_properties; dc->reset = spapr_phb_reset; dc->vmsd = &vmstate_spapr_pci; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->cannot_instantiate_with_device_add_yet = false; } static const TypeInfo spapr_phb_info = { diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 1cb276de05..73860d0486 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -131,7 +131,7 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, uint32_t nret, target_ulong rets) { target_ulong id; - CPUState *cpu; + PowerPCCPU *cpu; if (nargs != 1 || nret != 2) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); @@ -139,9 +139,9 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, } id = rtas_ld(args, 0); - cpu = qemu_get_cpu(id); + cpu = ppc_get_vcpu_by_dt_id(id); if (cpu != NULL) { - if (cpu->halted) { + if (CPU(cpu)->halted) { rtas_st(rets, 1, 0); } else { rtas_st(rets, 1, 2); @@ -161,7 +161,7 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr, uint32_t nret, target_ulong rets) { target_ulong id, start, r3; - CPUState *cs; + PowerPCCPU *cpu; if (nargs != 3 || nret != 1) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); @@ -172,9 +172,9 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr, start = rtas_ld(args, 1); r3 = rtas_ld(args, 2); - cs = qemu_get_cpu(id); - if (cs != NULL) { - PowerPCCPU *cpu = POWERPC_CPU(cs); + cpu = ppc_get_vcpu_by_dt_id(id); + if (cpu != NULL) { + CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; if (!cs->halted) { diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index 85a0e537b9..ce8ea91e8b 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -174,6 +174,19 @@ static int xilinx_load_device_tree(hwaddr addr, if (!fdt) { return 0; } + + r = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", + initrd_base); + if (r < 0) { + error_report("couldn't set /chosen/linux,initrd-start"); + } + + r = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", + (initrd_base + initrd_size)); + if (r < 0) { + error_report("couldn't set /chosen/linux,initrd-end"); + } + r = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); if (r < 0) fprintf(stderr, "couldn't set /chosen/bootargs\n"); @@ -187,6 +200,8 @@ static void virtex_init(QEMUMachineInitArgs *args) const char *cpu_model = args->cpu_model; const char *kernel_filename = args->kernel_filename; const char *kernel_cmdline = args->kernel_cmdline; + hwaddr initrd_base = 0; + int initrd_size = 0; MemoryRegion *address_space_mem = get_system_memory(); DeviceState *dev; PowerPCCPU *cpu; @@ -259,10 +274,27 @@ static void virtex_init(QEMUMachineInitArgs *args) boot_info.ima_size = kernel_size; + /* Load initrd. */ + if (args->initrd_filename) { + initrd_base = high = ROUND_UP(high, 4); + initrd_size = load_image_targphys(args->initrd_filename, + high, ram_size - high); + + if (initrd_size < 0) { + error_report("couldn't load ram disk '%s'", + args->initrd_filename); + exit(1); + } + high = ROUND_UP(high + initrd_size, 4); + } + /* Provide a device-tree. */ boot_info.fdt = high + (8192 * 2); boot_info.fdt &= ~8191; - xilinx_load_device_tree(boot_info.fdt, ram_size, 0, 0, kernel_cmdline); + + xilinx_load_device_tree(boot_info.fdt, ram_size, + initrd_base, initrd_size, + kernel_cmdline); } env->load_info = &boot_info; } diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 75b04b45af..7074d2b3d5 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -116,6 +116,15 @@ void css_conditional_io_interrupt(SubchDev *sch) } } +void css_adapter_interrupt(uint8_t isc) +{ + S390CPU *cpu = s390_cpu_addr2state(0); + uint32_t io_int_word = (isc << 27) | IO_INT_WORD_AI; + + trace_css_adapter_interrupt(isc); + s390_io_interrupt(cpu, 0, 0, 0, io_int_word); +} + static void sch_handle_clear_func(SubchDev *sch) { PMCW *p = &sch->curr_status.pmcw; @@ -1259,6 +1268,7 @@ void css_reset_sch(SubchDev *sch) sch->channel_prog = 0x0; sch->last_cmd_valid = false; sch->orb = NULL; + sch->thinint_active = false; } void css_reset(void) diff --git a/hw/s390x/css.h b/hw/s390x/css.h index b536ab5957..e9b440540d 100644 --- a/hw/s390x/css.h +++ b/hw/s390x/css.h @@ -77,6 +77,7 @@ struct SubchDev { CCW1 last_cmd; bool last_cmd_valid; ORB *orb; + bool thinint_active; /* transport-provided data: */ int (*ccw_cb) (SubchDev *, CCW1); SenseId id; @@ -97,4 +98,5 @@ void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid); void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, int hotplugged, int add); void css_generate_chp_crws(uint8_t cssid, uint8_t chpid); +void css_adapter_interrupt(uint8_t isc); #endif diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index a73c0b924a..0777a93916 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 1a6397b88e..32d38a08f6 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -95,24 +95,29 @@ 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) { + if (kernel_size < 0) { kernel_size = load_image_targphys(ipl->kernel, 0, ram_size); } - if (kernel_size == -1) { + if (kernel_size < 0) { 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 733d988871..0d4f6ae2f3 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 ee626493c6..c7bdc2005d 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 9eeda97920..0f03fd18b9 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 4e0c564c5c..d8ddf35e58 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/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index f6e0e3e4ae..a01801e342 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -1,7 +1,7 @@ /* * virtio ccw target implementation * - * Copyright 2012 IBM Corp. + * Copyright 2012,2014 IBM Corp. * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> * * This work is licensed under the terms of the GNU GPL, version 2 or (at @@ -188,6 +188,13 @@ typedef struct VirtioFeatDesc { uint8_t index; } QEMU_PACKED VirtioFeatDesc; +typedef struct VirtioThinintInfo { + hwaddr summary_indicator; + hwaddr device_indicator; + uint64_t ind_bit; + uint8_t isc; +} QEMU_PACKED VirtioThinintInfo; + /* Specify where the virtqueues for the subchannel are in guest memory. */ static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, uint16_t index, uint16_t num) @@ -237,6 +244,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) bool check_len; int len; hwaddr hw_len; + VirtioThinintInfo *thinint; if (!dev) { return -EINVAL; @@ -428,6 +436,11 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ret = -EINVAL; break; } + if (sch->thinint_active) { + /* Trigger a command reject. */ + ret = -ENOSYS; + break; + } if (!ccw.cda) { ret = -EFAULT; } else { @@ -480,6 +493,42 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ret = 0; } break; + case CCW_CMD_SET_IND_ADAPTER: + if (check_len) { + if (ccw.count != sizeof(*thinint)) { + ret = -EINVAL; + break; + } + } else if (ccw.count < sizeof(*thinint)) { + /* Can't execute command. */ + ret = -EINVAL; + break; + } + len = sizeof(*thinint); + hw_len = len; + if (!ccw.cda) { + ret = -EFAULT; + } else if (dev->indicators && !sch->thinint_active) { + /* Trigger a command reject. */ + ret = -ENOSYS; + } else { + thinint = cpu_physical_memory_map(ccw.cda, &hw_len, 0); + if (!thinint) { + ret = -EFAULT; + } else { + len = hw_len; + dev->summary_indicator = thinint->summary_indicator; + dev->indicators = thinint->device_indicator; + dev->thinint_isc = thinint->isc; + dev->ind_bit = thinint->ind_bit; + cpu_physical_memory_unmap(thinint, hw_len, 0, hw_len); + sch->thinint_active = ((dev->indicators != 0) && + (dev->summary_indicator != 0)); + sch->curr_status.scsw.count = ccw.count - len; + ret = 0; + } + } + break; default: ret = -ENOSYS; break; @@ -511,6 +560,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) sch->channel_prog = 0x0; sch->last_cmd_valid = false; sch->orb = NULL; + sch->thinint_active = false; /* * Use a device number if provided. Otherwise, fall back to subchannel * number. @@ -858,6 +908,28 @@ static inline VirtioCcwDevice *to_virtio_ccw_dev_fast(DeviceState *d) return container_of(d, VirtioCcwDevice, parent_obj); } +static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc, + uint8_t to_be_set) +{ + uint8_t ind_old, ind_new; + hwaddr len = 1; + uint8_t *ind_addr; + + ind_addr = cpu_physical_memory_map(ind_loc, &len, 1); + if (!ind_addr) { + error_report("%s(%x.%x.%04x): unable to access indicator", + __func__, sch->cssid, sch->ssid, sch->schid); + return -1; + } + do { + ind_old = *ind_addr; + ind_new = ind_old | to_be_set; + } while (atomic_cmpxchg(ind_addr, ind_old, ind_new) != ind_old); + cpu_physical_memory_unmap(ind_addr, len, 1, len); + + return ind_old; +} + static void virtio_ccw_notify(DeviceState *d, uint16_t vector) { VirtioCcwDevice *dev = to_virtio_ccw_dev_fast(d); @@ -872,9 +944,26 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) if (!dev->indicators) { return; } - indicators = ldq_phys(&address_space_memory, dev->indicators); - indicators |= 1ULL << vector; - stq_phys(&address_space_memory, dev->indicators, indicators); + if (sch->thinint_active) { + /* + * In the adapter interrupt case, indicators points to a + * memory area that may be (way) larger than 64 bit and + * ind_bit indicates the start of the indicators in a big + * endian notation. + */ + virtio_set_ind_atomic(sch, dev->indicators + + (dev->ind_bit + vector) / 8, + 0x80 >> ((dev->ind_bit + vector) % 8)); + if (!virtio_set_ind_atomic(sch, dev->summary_indicator, + 0x01)) { + css_adapter_interrupt(dev->thinint_isc); + } + } else { + indicators = ldq_phys(&address_space_memory, dev->indicators); + indicators |= 1ULL << vector; + stq_phys(&address_space_memory, dev->indicators, indicators); + css_conditional_io_interrupt(sch); + } } else { if (!dev->indicators2) { return; @@ -883,10 +972,8 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) indicators = ldq_phys(&address_space_memory, dev->indicators2); indicators |= 1ULL << vector; stq_phys(&address_space_memory, dev->indicators2, indicators); + css_conditional_io_interrupt(sch); } - - css_conditional_io_interrupt(sch); - } static unsigned virtio_ccw_get_features(DeviceState *d) @@ -907,6 +994,7 @@ static void virtio_ccw_reset(DeviceState *d) css_reset_sch(dev->sch); dev->indicators = 0; dev->indicators2 = 0; + dev->summary_indicator = 0; } static void virtio_ccw_vmstate_change(DeviceState *d, bool running) diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 00932c746d..4393e44814 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -38,6 +38,7 @@ #define CCW_CMD_SET_IND 0x43 #define CCW_CMD_SET_CONF_IND 0x53 #define CCW_CMD_READ_VQ_CONF 0x32 +#define CCW_CMD_SET_IND_ADAPTER 0x73 #define TYPE_VIRTIO_CCW_DEVICE "virtio-ccw-device" #define VIRTIO_CCW_DEVICE(obj) \ @@ -83,9 +84,12 @@ struct VirtioCcwDevice { bool ioeventfd_started; bool ioeventfd_disabled; uint32_t flags; + uint8_t thinint_isc; /* Guest provided values: */ hwaddr indicators; hwaddr indicators2; + hwaddr summary_indicator; + uint64_t ind_bit; }; /* virtual css bus type */ diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 50b89ad4aa..50a0acf1fe 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 b4fadd2f24..48a28ae199 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 f08b64e177..8d92e0da15 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 c0c46d7f7c..b3835c821d 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -60,9 +60,10 @@ #define VSCSI_MAX_SECTORS 4096 #define VSCSI_REQ_LIMIT 24 -#define SCSI_SENSE_BUF_SIZE 96 #define SRP_RSP_SENSE_DATA_LEN 18 +#define SRP_REPORT_LUNS_WLUN 0xc10100000000000ULL + typedef union vscsi_crq { struct viosrp_crq s; uint8_t raw[16]; @@ -720,12 +721,70 @@ static void vscsi_inquiry_no_target(VSCSIState *s, vscsi_req *req) } } +static void vscsi_report_luns(VSCSIState *s, vscsi_req *req) +{ + BusChild *kid; + int i, len, n, rc; + uint8_t *resp_data; + bool found_lun0; + + n = 0; + found_lun0 = false; + QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { + SCSIDevice *dev = SCSI_DEVICE(kid->child); + + n += 8; + if (dev->channel == 0 && dev->id == 0 && dev->lun == 0) { + found_lun0 = true; + } + } + if (!found_lun0) { + n += 8; + } + len = n+8; + + resp_data = g_malloc0(len); + memset(resp_data, 0, len); + stl_be_p(resp_data, n); + i = found_lun0 ? 8 : 16; + QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { + DeviceState *qdev = kid->child; + SCSIDevice *dev = SCSI_DEVICE(qdev); + + if (dev->id == 0 && dev->channel == 0) { + resp_data[i] = 0; /* Use simple LUN for 0 (SAM5 4.7.7.1) */ + } else { + resp_data[i] = (2 << 6); /* Otherwise LUN addressing (4.7.7.4) */ + } + resp_data[i] |= dev->id; + resp_data[i+1] = (dev->channel << 5); + resp_data[i+1] |= dev->lun; + i += 8; + } + + vscsi_preprocess_desc(req); + rc = vscsi_srp_transfer_data(s, req, 0, resp_data, len); + g_free(resp_data); + if (rc < 0) { + vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0); + vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); + } else { + vscsi_send_rsp(s, req, 0, len - rc, 0); + } +} + static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) { union srp_iu *srp = &req->iu.srp; SCSIDevice *sdev; int n, lun; + if ((srp->cmd.lun == 0 || be64_to_cpu(srp->cmd.lun) == SRP_REPORT_LUNS_WLUN) + && srp->cmd.cdb[0] == REPORT_LUNS) { + vscsi_report_luns(s, req); + return 0; + } + sdev = vscsi_device_find(&s->bus, be64_to_cpu(srp->cmd.lun), &lun); if (!sdev) { DPRINTF("VSCSI: Command for lun %08" PRIx64 " with no drive\n", diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 2957d90177..75adb68abc 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/arm_timer.c b/hw/timer/arm_timer.c index a47afde23a..fb0a45c889 100644 --- a/hw/timer/arm_timer.c +++ b/hw/timer/arm_timer.c @@ -320,6 +320,7 @@ static uint64_t icp_pit_read(void *opaque, hwaddr offset, n = offset >> 8; if (n > 2) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad timer %d\n", __func__, n); + return 0; } return arm_timer_read(s->timer[n], offset & 0xff); @@ -334,6 +335,7 @@ static void icp_pit_write(void *opaque, hwaddr offset, n = offset >> 8; if (n > 2) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad timer %d\n", __func__, n); + return; } arm_timer_write(s->timer[n], offset & 0xff, value); diff --git a/hw/timer/slavio_timer.c b/hw/timer/slavio_timer.c index f75b914951..e4dcceaf23 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/hw/unicore32/puv3.c b/hw/unicore32/puv3.c index e05cbc131e..42913b6a5a 100644 --- a/hw/unicore32/puv3.c +++ b/hw/unicore32/puv3.c @@ -98,7 +98,7 @@ static void puv3_load_kernel(const char *kernel_filename) } /* cheat curses that we have a graphic console, only under ocd console */ - graphic_console_init(NULL, &no_ops, NULL); + graphic_console_init(NULL, 0, &no_ops, NULL); } static void puv3_init(QEMUMachineInitArgs *args) diff --git a/include/hw/boards.h b/include/hw/boards.h index 2151460f9e..c2096e6ba2 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -4,10 +4,9 @@ #define HW_BOARDS_H #include "sysemu/blockdev.h" +#include "sysemu/qemumachine.h" #include "hw/qdev.h" -typedef struct QEMUMachine QEMUMachine; - typedef struct QEMUMachineInitArgs { const QEMUMachine *machine; ram_addr_t ram_size; @@ -24,6 +23,8 @@ typedef void QEMUMachineResetFunc(void); typedef void QEMUMachineHotAddCPUFunc(const int64_t id, Error **errp); +typedef int QEMUMachineGetKvmtypeFunc(const char *arg); + struct QEMUMachine { const char *name; const char *alias; @@ -31,6 +32,7 @@ struct QEMUMachine { QEMUMachineInitFunc *init; QEMUMachineResetFunc *reset; QEMUMachineHotAddCPUFunc *hot_add_cpu; + QEMUMachineGetKvmtypeFunc *kvm_type; BlockInterfaceType block_default_type; int max_cpus; unsigned int no_serial:1, diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h index acc701e3a4..c6b5129bab 100644 --- a/include/hw/elf_ops.h +++ b/include/hw/elf_ops.h @@ -201,6 +201,7 @@ static int glue(load_elf, SZ)(const char *name, int fd, uint64_t addr, low = (uint64_t)-1, high = 0; uint8_t *data = NULL; char label[128]; + int ret = ELF_LOAD_FAILED; if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) goto fail; @@ -211,22 +212,30 @@ static int glue(load_elf, SZ)(const char *name, int fd, switch (elf_machine) { case EM_PPC64: if (EM_PPC64 != ehdr.e_machine) - if (EM_PPC != ehdr.e_machine) + if (EM_PPC != ehdr.e_machine) { + ret = ELF_LOAD_WRONG_ARCH; goto fail; + } break; case EM_X86_64: if (EM_X86_64 != ehdr.e_machine) - if (EM_386 != ehdr.e_machine) + if (EM_386 != ehdr.e_machine) { + ret = ELF_LOAD_WRONG_ARCH; goto fail; + } break; case EM_MICROBLAZE: if (EM_MICROBLAZE != ehdr.e_machine) - if (EM_MICROBLAZE_OLD != ehdr.e_machine) + if (EM_MICROBLAZE_OLD != ehdr.e_machine) { + ret = ELF_LOAD_WRONG_ARCH; goto fail; + } break; default: - if (elf_machine != ehdr.e_machine) + if (elf_machine != ehdr.e_machine) { + ret = ELF_LOAD_WRONG_ARCH; goto fail; + } } if (pentry) @@ -305,5 +314,5 @@ static int glue(load_elf, SZ)(const char *name, int fd, fail: g_free(data); g_free(phdr); - return -1; + return ret; } diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h index 89384c2bb4..f6887ed92b 100644 --- a/include/hw/intc/arm_gic_common.h +++ b/include/hw/intc/arm_gic_common.h @@ -104,6 +104,7 @@ typedef struct GICState { MemoryRegion cpuiomem[GIC_NCPU + 1]; /* CPU interfaces */ uint32_t num_irq; uint32_t revision; + int dev_fd; /* kvm device fd if backed by kvm vgic support */ } GICState; #define TYPE_ARM_GIC_COMMON "arm_gic_common" diff --git a/include/hw/loader.h b/include/hw/loader.h index 91b01224a3..aaf08c377e 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -15,6 +15,12 @@ int get_image_size(const char *filename); int load_image(const char *filename, uint8_t *addr); /* deprecated */ int load_image_targphys(const char *filename, hwaddr, uint64_t max_sz); + +#define ELF_LOAD_FAILED -1 +#define ELF_LOAD_NOT_ELF -2 +#define ELF_LOAD_WRONG_ARCH -3 +#define ELF_LOAD_WRONG_ENDIAN -4 +const char *load_elf_strerror(int error); int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, int big_endian, int elf_machine, diff --git a/include/hw/nvram/openbios_firmware_abi.h b/include/hw/nvram/openbios_firmware_abi.h index 5e6e5d4d34..c66ee22685 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/qdev-core.h b/include/hw/qdev-core.h index 276b336c09..1ed0691716 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -176,6 +176,8 @@ struct BusClass { void (*reset)(BusState *bus); /* maximum devices allowed on the bus, 0: no limit. */ int max_dev; + /* number of automatically allocated bus ids (e.g. ide.0) */ + int automatic_ids; }; typedef struct BusChild { diff --git a/include/hw/s390x/event-facility.h b/include/hw/s390x/event-facility.h index 870edd46f6..6a062b668a 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 0000000000..497b219e30 --- /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 35112d92b1..7ef16226df 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 bf6da3d632..e5fc39d504 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/hw/xen/xen.h b/include/hw/xen/xen.h index e1f88bf9cf..e1818213b2 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -10,6 +10,7 @@ #include "hw/irq.h" #include "qemu-common.h" +#include "sysemu/qemumachine.h" /* xen-machine.c */ enum xen_mode { @@ -36,7 +37,7 @@ void xen_cmos_set_s3_resume(void *opaque, int irq, int level); qemu_irq *xen_interrupt_controller_init(void); -int xen_init(void); +int xen_init(QEMUMachine *machine); int xen_hvm_init(MemoryRegion **ram_memory); void xenstore_store_pv_console_info(int i, struct CharDriverState *chr); diff --git a/include/migration/page_cache.h b/include/migration/page_cache.h index d156f0d398..2d5ce2dd7a 100644 --- a/include/migration/page_cache.h +++ b/include/migration/page_cache.h @@ -66,7 +66,7 @@ uint8_t *get_cached_data(const PageCache *cache, uint64_t addr); * @addr: page address * @pdata: pointer to the page */ -int cache_insert(PageCache *cache, uint64_t addr, uint8_t *pdata); +int cache_insert(PageCache *cache, uint64_t addr, const uint8_t *pdata); /** * cache_resize: resize the page cache. In case of size reduction the extra diff --git a/include/net/net.h b/include/net/net.h index 11e146888b..8166345a13 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -50,6 +50,12 @@ typedef void (NetCleanup) (NetClientState *); typedef void (LinkStatusChanged)(NetClientState *); typedef void (NetClientDestructor)(NetClientState *); typedef RxFilterInfo *(QueryRxFilter)(NetClientState *); +typedef bool (HasUfo)(NetClientState *); +typedef bool (HasVnetHdr)(NetClientState *); +typedef bool (HasVnetHdrLen)(NetClientState *, int); +typedef void (UsingVnetHdr)(NetClientState *, bool); +typedef void (SetOffload)(NetClientState *, int, int, int, int, int); +typedef void (SetVnetHdrLen)(NetClientState *, int); typedef struct NetClientInfo { NetClientOptionsKind type; @@ -62,6 +68,12 @@ typedef struct NetClientInfo { LinkStatusChanged *link_status_changed; QueryRxFilter *query_rx_filter; NetPoll *poll; + HasUfo *has_ufo; + HasVnetHdr *has_vnet_hdr; + HasVnetHdrLen *has_vnet_hdr_len; + UsingVnetHdr *using_vnet_hdr; + SetOffload *set_offload; + SetVnetHdrLen *set_vnet_hdr_len; } NetClientInfo; struct NetClientState { @@ -120,6 +132,13 @@ ssize_t qemu_send_packet_async(NetClientState *nc, const uint8_t *buf, void qemu_purge_queued_packets(NetClientState *nc); void qemu_flush_queued_packets(NetClientState *nc); void qemu_format_nic_info_str(NetClientState *nc, uint8_t macaddr[6]); +bool qemu_has_ufo(NetClientState *nc); +bool qemu_has_vnet_hdr(NetClientState *nc); +bool qemu_has_vnet_hdr_len(NetClientState *nc, int len); +void qemu_using_vnet_hdr(NetClientState *nc, bool enable); +void qemu_set_offload(NetClientState *nc, int csum, int tso4, int tso6, + int ecn, int ufo); +void qemu_set_vnet_hdr_len(NetClientState *nc, int len); void qemu_macaddr_default_if_unset(MACAddr *macaddr); int qemu_show_nic_models(const char *arg, const char *const *models); void qemu_check_nic_model(NICInfo *nd, const char *model); diff --git a/include/net/tap.h b/include/net/tap.h index a994f20447..6daeb42b0f 100644 --- a/include/net/tap.h +++ b/include/net/tap.h @@ -29,12 +29,6 @@ #include "qemu-common.h" #include "qapi-types.h" -bool tap_has_ufo(NetClientState *nc); -int tap_has_vnet_hdr(NetClientState *nc); -int tap_has_vnet_hdr_len(NetClientState *nc, int len); -void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr); -void tap_set_offload(NetClientState *nc, int csum, int tso4, int tso6, int ecn, int ufo); -void tap_set_vnet_hdr_len(NetClientState *nc, int len); int tap_enable(NetClientState *nc); int tap_disable(NetClientState *nc); diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index 73c67b70c2..25193c943b 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/qapi/qmp/qjson.h b/include/qapi/qmp/qjson.h index 73351ed6d6..ee4d31a46a 100644 --- a/include/qapi/qmp/qjson.h +++ b/include/qapi/qmp/qjson.h @@ -19,7 +19,7 @@ #include "qapi/qmp/qobject.h" #include "qapi/qmp/qstring.h" -QObject *qobject_from_json(const char *string) GCC_FMT_ATTR(1, 0); +QObject *qobject_from_json(const char *string); QObject *qobject_from_jsonf(const char *string, ...) GCC_FMT_ATTR(1, 2); QObject *qobject_from_jsonv(const char *string, va_list *ap) GCC_FMT_ATTR(1, 0); diff --git a/include/qemu/bswap.h b/include/qemu/bswap.h index 437b8e0a9e..0cb7c05554 100644 --- a/include/qemu/bswap.h +++ b/include/qemu/bswap.h @@ -228,7 +228,7 @@ static inline int ldsb_p(const void *ptr) return *(int8_t *)ptr; } -static inline void stb_p(void *ptr, int v) +static inline void stb_p(void *ptr, uint8_t v) { *(uint8_t *)ptr = v; } @@ -300,12 +300,12 @@ static inline uint64_t ldq_le_p(const void *ptr) return le_bswap(ldq_p(ptr), 64); } -static inline void stw_le_p(void *ptr, int v) +static inline void stw_le_p(void *ptr, uint16_t v) { stw_p(ptr, le_bswap(v, 16)); } -static inline void stl_le_p(void *ptr, int v) +static inline void stl_le_p(void *ptr, uint32_t v) { stl_p(ptr, le_bswap(v, 32)); } @@ -365,12 +365,12 @@ static inline uint64_t ldq_be_p(const void *ptr) return be_bswap(ldq_p(ptr), 64); } -static inline void stw_be_p(void *ptr, int v) +static inline void stw_be_p(void *ptr, uint16_t v) { stw_p(ptr, be_bswap(v, 16)); } -static inline void stl_be_p(void *ptr, int v) +static inline void stl_be_p(void *ptr, uint32_t v) { stl_p(ptr, be_bswap(v, 32)); } diff --git a/include/qemu/crc32c.h b/include/qemu/crc32c.h index 56d1c3bfde..dafb6a1ada 100644 --- a/include/qemu/crc32c.h +++ b/include/qemu/crc32c.h @@ -25,8 +25,8 @@ * */ -#ifndef QEMU_CRC32_H -#define QEMU_CRC32_H +#ifndef QEMU_CRC32C_H +#define QEMU_CRC32C_H #include "qemu-common.h" diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index de85d282d0..d4f21c947f 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -44,9 +44,37 @@ static inline void muls64(uint64_t *plow, uint64_t *phigh, *plow = r; *phigh = r >> 64; } + +static inline int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor) +{ + if (divisor == 0) { + return 1; + } else { + __uint128_t dividend = ((__uint128_t)*phigh << 64) | *plow; + __uint128_t result = dividend / divisor; + *plow = result; + *phigh = dividend % divisor; + return result > UINT64_MAX; + } +} + +static inline int divs128(int64_t *plow, int64_t *phigh, int64_t divisor) +{ + if (divisor == 0) { + return 1; + } else { + __int128_t dividend = ((__int128_t)*phigh << 64) | *plow; + __int128_t result = dividend / divisor; + *plow = result; + *phigh = dividend % divisor; + return result != *plow; + } +} #else void muls64(uint64_t *phigh, uint64_t *plow, int64_t a, int64_t b); void mulu64(uint64_t *phigh, uint64_t *plow, uint64_t a, uint64_t b); +int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor); +int divs128(int64_t *plow, int64_t *phigh, int64_t divisor); #endif /** @@ -217,7 +245,7 @@ static inline int ctz64(uint64_t val) } /** - * ctz64 - count trailing ones in a 64-bit value. + * cto64 - count trailing ones in a 64-bit value. * @val: The value to search * * Returns 64 if the value is -1. diff --git a/include/qom/cpu.h b/include/qom/cpu.h index 367eda17d1..d734be8a40 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 19fafb2cf9..efab7a32f3 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/kvm.h b/include/sysemu/kvm.h index 3b25f27a7c..ed01998aa8 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -18,6 +18,7 @@ #include "config-host.h" #include "qemu/queue.h" #include "qom/cpu.h" +#include "sysemu/qemumachine.h" #ifdef CONFIG_KVM #include <linux/kvm.h> @@ -152,7 +153,7 @@ extern KVMState *kvm_state; /* external API */ -int kvm_init(void); +int kvm_init(QEMUMachine *machine); int kvm_has_sync_mmu(void); int kvm_has_vcpu_events(void); @@ -194,6 +195,28 @@ int kvm_vm_ioctl(KVMState *s, int type, ...); int kvm_vcpu_ioctl(CPUState *cpu, int type, ...); +/** + * kvm_device_ioctl - call an ioctl on a kvm device + * @fd: The KVM device file descriptor as returned from KVM_CREATE_DEVICE + * @type: The device-ctrl ioctl number + * + * Returns: -errno on error, nonnegative on success + */ +int kvm_device_ioctl(int fd, int type, ...); + +/** + * kvm_create_device - create a KVM device for the device control API + * @KVMState: The KVMState pointer + * @type: The KVM device type (see Documentation/virtual/kvm/devices in the + * kernel source) + * @test: If true, only test if device can be created, but don't actually + * create the device. + * + * Returns: -errno on error, nonnegative on success: @test ? 0 : device fd; + */ +int kvm_create_device(KVMState *s, uint64_t type, bool test); + + /* Arch specific hooks */ extern const KVMCapabilityInfo kvm_arch_required_capabilities[]; @@ -319,4 +342,16 @@ int kvm_irqchip_remove_irqfd_notifier(KVMState *s, EventNotifier *n, int virq); void kvm_pc_gsi_handler(void *opaque, int n, int level); void kvm_pc_setup_irq_routing(bool pci_enabled); void kvm_init_irq_routing(KVMState *s); + +/** + * kvm_arch_irqchip_create: + * @KVMState: The KVMState pointer + * + * Allow architectures to create an in-kernel irq chip themselves. + * + * Returns: < 0: error + * 0: irq chip was not created + * > 0: irq chip was created + */ +int kvm_arch_irqchip_create(KVMState *s); #endif diff --git a/include/sysemu/qemumachine.h b/include/sysemu/qemumachine.h new file mode 100644 index 0000000000..4cefd56b67 --- /dev/null +++ b/include/sysemu/qemumachine.h @@ -0,0 +1,16 @@ +/* + * QEMU Machine typedef + * + * Copyright Alexander Graf <agraf@suse.de> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef QEMUMACHINE_H +#define QEMUMACHINE_H + +typedef struct QEMUMachine QEMUMachine; + +#endif /* !QEMUMACHINE_H */ diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h index 28f4875112..e62281d4bf 100644 --- a/include/sysemu/qtest.h +++ b/include/sysemu/qtest.h @@ -16,6 +16,7 @@ #include "qemu-common.h" #include "qapi/error.h" +#include "sysemu/qemumachine.h" extern bool qtest_allowed; @@ -26,7 +27,7 @@ static inline bool qtest_enabled(void) bool qtest_driver(void); -int qtest_init_accel(void); +int qtest_init_accel(QEMUMachine *machine); void qtest_init(const char *qtest_chrdev, const char *qtest_log, Error **errp); static inline int qtest_available(void) diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 495dae8c39..b90df9ada1 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/include/ui/console.h b/include/ui/console.h index 4156a876e1..08a38eab13 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -14,6 +14,8 @@ #define MOUSE_EVENT_LBUTTON 0x01 #define MOUSE_EVENT_RBUTTON 0x02 #define MOUSE_EVENT_MBUTTON 0x04 +#define MOUSE_EVENT_WHEELUP 0x08 +#define MOUSE_EVENT_WHEELDN 0x10 /* identical to the ps/2 keyboard bits */ #define QEMU_SCROLL_LOCK_LED (1 << 0) @@ -44,17 +46,7 @@ void qemu_activate_mouse_event_handler(QEMUPutMouseEntry *entry); QEMUPutLEDEntry *qemu_add_led_event_handler(QEMUPutLEDEvent *func, void *opaque); void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry); -void kbd_put_keycode(int keycode); void kbd_put_ledstate(int ledstate); -void kbd_mouse_event(int dx, int dy, int dz, int buttons_state); - -/* Does the current mouse generate absolute events */ -int kbd_mouse_is_absolute(void); -void qemu_add_mouse_mode_change_notifier(Notifier *notify); -void qemu_remove_mouse_mode_change_notifier(Notifier *notify); - -/* Of all the mice, is there one that generates absolute events */ -int kbd_mouse_has_absolute(void); struct MouseTransformInfo { /* Touchscreen resolution */ @@ -128,6 +120,14 @@ struct DisplaySurface { struct PixelFormat pf; }; +typedef struct QemuUIInfo { + /* geometry */ + int xoff; + int yoff; + uint32_t width; + uint32_t height; +} QemuUIInfo; + /* cursor data format is 32bit RGBA */ typedef struct QEMUCursor { int width, height; @@ -212,6 +212,8 @@ void update_displaychangelistener(DisplayChangeListener *dcl, uint64_t interval); void unregister_displaychangelistener(DisplayChangeListener *dcl); +int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info); + void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h); void dpy_gfx_replace_surface(QemuConsole *con, DisplaySurface *surface); @@ -274,9 +276,10 @@ typedef struct GraphicHwOps { void (*gfx_update)(void *opaque); void (*text_update)(void *opaque, console_ch_t *text); void (*update_interval)(void *opaque, uint64_t interval); + int (*ui_info)(void *opaque, uint32_t head, QemuUIInfo *info); } GraphicHwOps; -QemuConsole *graphic_console_init(DeviceState *dev, +QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, const GraphicHwOps *ops, void *opaque); @@ -285,10 +288,15 @@ void graphic_hw_invalidate(QemuConsole *con); void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata); QemuConsole *qemu_console_lookup_by_index(unsigned int index); -QemuConsole *qemu_console_lookup_by_device(DeviceState *dev); +QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head); bool qemu_console_is_visible(QemuConsole *con); bool qemu_console_is_graphic(QemuConsole *con); bool qemu_console_is_fixedsize(QemuConsole *con); +int qemu_console_get_index(QemuConsole *con); +uint32_t qemu_console_get_head(QemuConsole *con); +QemuUIInfo *qemu_console_get_ui_info(QemuConsole *con); +int qemu_console_get_width(QemuConsole *con, int fallback); +int qemu_console_get_height(QemuConsole *con, int fallback); void text_consoles_set_display(DisplayState *ds); void console_select(unsigned int index); @@ -334,7 +342,6 @@ void curses_display_init(DisplayState *ds, int full_screen); /* input.c */ int index_from_key(const char *key); -int index_from_keycode(int code); /* gtk.c */ void early_gtk_display_init(void); diff --git a/include/ui/input.h b/include/ui/input.h new file mode 100644 index 0000000000..4976f3da2c --- /dev/null +++ b/include/ui/input.h @@ -0,0 +1,56 @@ +#ifndef INPUT_H +#define INPUT_H + +#include "qapi-types.h" + +#define INPUT_EVENT_MASK_KEY (1<<INPUT_EVENT_KIND_KEY) +#define INPUT_EVENT_MASK_BTN (1<<INPUT_EVENT_KIND_BTN) +#define INPUT_EVENT_MASK_REL (1<<INPUT_EVENT_KIND_REL) +#define INPUT_EVENT_MASK_ABS (1<<INPUT_EVENT_KIND_ABS) + +#define INPUT_EVENT_ABS_SIZE 0x8000 + +typedef struct QemuInputHandler QemuInputHandler; +typedef struct QemuInputHandlerState QemuInputHandlerState; + +typedef void (*QemuInputHandlerEvent)(DeviceState *dev, QemuConsole *src, + InputEvent *evt); +typedef void (*QemuInputHandlerSync)(DeviceState *dev); + +struct QemuInputHandler { + const char *name; + uint32_t mask; + QemuInputHandlerEvent event; + QemuInputHandlerSync sync; +}; + +QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev, + QemuInputHandler *handler); +void qemu_input_handler_activate(QemuInputHandlerState *s); +void qemu_input_handler_unregister(QemuInputHandlerState *s); +void qemu_input_event_send(QemuConsole *src, InputEvent *evt); +void qemu_input_event_sync(void); + +InputEvent *qemu_input_event_new_key(KeyValue *key, bool down); +void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down); +void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down); +void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down); + +InputEvent *qemu_input_event_new_btn(InputButton btn, bool down); +void qemu_input_queue_btn(QemuConsole *src, InputButton btn, bool down); +void qemu_input_update_buttons(QemuConsole *src, uint32_t *button_map, + uint32_t button_old, uint32_t button_new); + +bool qemu_input_is_absolute(void); +int qemu_input_scale_axis(int value, int size_in, int size_out); +InputEvent *qemu_input_event_new_move(InputEventKind kind, + InputAxis axis, int value); +void qemu_input_queue_rel(QemuConsole *src, InputAxis axis, int value); +void qemu_input_queue_abs(QemuConsole *src, InputAxis axis, + int value, int size); + +void qemu_input_check_mode_change(void); +void qemu_add_mouse_mode_change_notifier(Notifier *notify); +void qemu_remove_mouse_mode_change_notifier(Notifier *notify); + +#endif /* INPUT_H */ diff --git a/kvm-all.c b/kvm-all.c index 2ca9143beb..87fe4821a6 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -36,6 +36,8 @@ #include "qemu/event_notifier.h" #include "trace.h" +#include "hw/boards.h" + /* This check must be after config-host.h is included */ #ifdef CONFIG_EVENTFD #include <sys/eventfd.h> @@ -1298,10 +1300,17 @@ static int kvm_irqchip_create(KVMState *s) return 0; } - ret = kvm_vm_ioctl(s, KVM_CREATE_IRQCHIP); + /* First probe and see if there's a arch-specific hook to create the + * in-kernel irqchip for us */ + ret = kvm_arch_irqchip_create(s); if (ret < 0) { - fprintf(stderr, "Create kernel irqchip failed\n"); return ret; + } else if (ret == 0) { + ret = kvm_vm_ioctl(s, KVM_CREATE_IRQCHIP); + if (ret < 0) { + fprintf(stderr, "Create kernel irqchip failed\n"); + return ret; + } } kvm_kernel_irqchip = true; @@ -1332,7 +1341,7 @@ static int kvm_max_vcpus(KVMState *s) return (ret) ? ret : kvm_recommended_vcpus(s); } -int kvm_init(void) +int kvm_init(QEMUMachine *machine) { static const char upgrade_note[] = "Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n" @@ -1349,7 +1358,8 @@ int kvm_init(void) KVMState *s; const KVMCapabilityInfo *missing_cap; int ret; - int i; + int i, type = 0; + const char *kvm_type; s = g_malloc0(sizeof(KVMState)); @@ -1423,12 +1433,20 @@ int kvm_init(void) nc++; } + kvm_type = qemu_opt_get(qemu_get_machine_opts(), "kvm-type"); + if (machine->kvm_type) { + type = machine->kvm_type(kvm_type); + } else if (kvm_type) { + fprintf(stderr, "Invalid argument kvm-type=%s\n", kvm_type); + goto err; + } + do { - ret = kvm_ioctl(s, KVM_CREATE_VM, 0); + ret = kvm_ioctl(s, KVM_CREATE_VM, type); } while (ret == -EINTR); if (ret < 0) { - fprintf(stderr, "ioctl(KVM_CREATE_VM) failed: %d %s\n", -s->vmfd, + fprintf(stderr, "ioctl(KVM_CREATE_VM) failed: %d %s\n", -ret, strerror(-ret)); #ifdef TARGET_S390X @@ -1777,6 +1795,24 @@ int kvm_vcpu_ioctl(CPUState *cpu, int type, ...) return ret; } +int kvm_device_ioctl(int fd, int type, ...) +{ + int ret; + void *arg; + va_list ap; + + va_start(ap, type); + arg = va_arg(ap, void *); + va_end(ap); + + trace_kvm_device_ioctl(fd, type, arg); + ret = ioctl(fd, type, arg); + if (ret == -1) { + ret = -errno; + } + return ret; +} + int kvm_has_sync_mmu(void) { return kvm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); @@ -2058,3 +2094,24 @@ int kvm_on_sigbus(int code, void *addr) { return kvm_arch_on_sigbus(code, addr); } + +int kvm_create_device(KVMState *s, uint64_t type, bool test) +{ + int ret; + struct kvm_create_device create_dev; + + create_dev.type = type; + create_dev.fd = -1; + create_dev.flags = test ? KVM_CREATE_DEVICE_TEST : 0; + + if (!kvm_check_extension(s, KVM_CAP_DEVICE_CTRL)) { + return -ENOTSUP; + } + + ret = kvm_vm_ioctl(s, KVM_CREATE_DEVICE, &create_dev); + if (ret) { + return ret; + } + + return test ? 0 : create_dev.fd; +} diff --git a/kvm-stub.c b/kvm-stub.c index e979f76d07..4ef084e84a 100644 --- a/kvm-stub.c +++ b/kvm-stub.c @@ -14,6 +14,7 @@ #include "hw/hw.h" #include "cpu.h" #include "sysemu/kvm.h" +#include "sysemu/qemumachine.h" #ifndef CONFIG_USER_ONLY #include "hw/pci/msi.h" @@ -34,7 +35,7 @@ int kvm_init_vcpu(CPUState *cpu) return -ENOSYS; } -int kvm_init(void) +int kvm_init(QEMUMachine *machine) { return -ENOSYS; } diff --git a/linux-headers/asm-arm/kvm.h b/linux-headers/asm-arm/kvm.h index c498b60c05..ef0c8785ba 100644 --- a/linux-headers/asm-arm/kvm.h +++ b/linux-headers/asm-arm/kvm.h @@ -119,6 +119,26 @@ struct kvm_arch_memory_slot { #define KVM_REG_ARM_32_CRN_MASK 0x0000000000007800 #define KVM_REG_ARM_32_CRN_SHIFT 11 +#define ARM_CP15_REG_SHIFT_MASK(x,n) \ + (((x) << KVM_REG_ARM_ ## n ## _SHIFT) & KVM_REG_ARM_ ## n ## _MASK) + +#define __ARM_CP15_REG(op1,crn,crm,op2) \ + (KVM_REG_ARM | (15 << KVM_REG_ARM_COPROC_SHIFT) | \ + ARM_CP15_REG_SHIFT_MASK(op1, OPC1) | \ + ARM_CP15_REG_SHIFT_MASK(crn, 32_CRN) | \ + ARM_CP15_REG_SHIFT_MASK(crm, CRM) | \ + ARM_CP15_REG_SHIFT_MASK(op2, 32_OPC2)) + +#define ARM_CP15_REG32(...) (__ARM_CP15_REG(__VA_ARGS__) | KVM_REG_SIZE_U32) + +#define __ARM_CP15_REG64(op1,crm) \ + (__ARM_CP15_REG(op1, 0, crm, 0) | KVM_REG_SIZE_U64) +#define ARM_CP15_REG64(...) __ARM_CP15_REG64(__VA_ARGS__) + +#define KVM_REG_ARM_TIMER_CTL ARM_CP15_REG32(0, 14, 3, 1) +#define KVM_REG_ARM_TIMER_CNT ARM_CP15_REG64(1, 14) +#define KVM_REG_ARM_TIMER_CVAL ARM_CP15_REG64(3, 14) + /* Normal registers are mapped as coprocessor 16. */ #define KVM_REG_ARM_CORE (0x0010 << KVM_REG_ARM_COPROC_SHIFT) #define KVM_REG_ARM_CORE_REG(name) (offsetof(struct kvm_regs, name) / 4) @@ -143,6 +163,14 @@ struct kvm_arch_memory_slot { #define KVM_REG_ARM_VFP_FPINST 0x1009 #define KVM_REG_ARM_VFP_FPINST2 0x100A +/* Device Control API: ARM VGIC */ +#define KVM_DEV_ARM_VGIC_GRP_ADDR 0 +#define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1 +#define KVM_DEV_ARM_VGIC_GRP_CPU_REGS 2 +#define KVM_DEV_ARM_VGIC_CPUID_SHIFT 32 +#define KVM_DEV_ARM_VGIC_CPUID_MASK (0xffULL << KVM_DEV_ARM_VGIC_CPUID_SHIFT) +#define KVM_DEV_ARM_VGIC_OFFSET_SHIFT 0 +#define KVM_DEV_ARM_VGIC_OFFSET_MASK (0xffffffffULL << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) /* KVM_IRQ_LINE irq field index values */ #define KVM_ARM_IRQ_TYPE_SHIFT 24 diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h index 5031f42639..eaf54a30be 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -55,8 +55,9 @@ struct kvm_regs { #define KVM_ARM_TARGET_AEM_V8 0 #define KVM_ARM_TARGET_FOUNDATION_V8 1 #define KVM_ARM_TARGET_CORTEX_A57 2 +#define KVM_ARM_TARGET_XGENE_POTENZA 3 -#define KVM_ARM_NUM_TARGETS 3 +#define KVM_ARM_NUM_TARGETS 4 /* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */ #define KVM_ARM_DEVICE_TYPE_SHIFT 0 @@ -129,6 +130,33 @@ struct kvm_arch_memory_slot { #define KVM_REG_ARM64_SYSREG_OP2_MASK 0x0000000000000007 #define KVM_REG_ARM64_SYSREG_OP2_SHIFT 0 +#define ARM64_SYS_REG_SHIFT_MASK(x,n) \ + (((x) << KVM_REG_ARM64_SYSREG_ ## n ## _SHIFT) & \ + KVM_REG_ARM64_SYSREG_ ## n ## _MASK) + +#define __ARM64_SYS_REG(op0,op1,crn,crm,op2) \ + (KVM_REG_ARM64 | KVM_REG_ARM64_SYSREG | \ + ARM64_SYS_REG_SHIFT_MASK(op0, OP0) | \ + ARM64_SYS_REG_SHIFT_MASK(op1, OP1) | \ + ARM64_SYS_REG_SHIFT_MASK(crn, CRN) | \ + ARM64_SYS_REG_SHIFT_MASK(crm, CRM) | \ + ARM64_SYS_REG_SHIFT_MASK(op2, OP2)) + +#define ARM64_SYS_REG(...) (__ARM64_SYS_REG(__VA_ARGS__) | KVM_REG_SIZE_U64) + +#define KVM_REG_ARM_TIMER_CTL ARM64_SYS_REG(3, 3, 14, 3, 1) +#define KVM_REG_ARM_TIMER_CNT ARM64_SYS_REG(3, 3, 14, 3, 2) +#define KVM_REG_ARM_TIMER_CVAL ARM64_SYS_REG(3, 3, 14, 0, 2) + +/* Device Control API: ARM VGIC */ +#define KVM_DEV_ARM_VGIC_GRP_ADDR 0 +#define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1 +#define KVM_DEV_ARM_VGIC_GRP_CPU_REGS 2 +#define KVM_DEV_ARM_VGIC_CPUID_SHIFT 32 +#define KVM_DEV_ARM_VGIC_CPUID_MASK (0xffULL << KVM_DEV_ARM_VGIC_CPUID_SHIFT) +#define KVM_DEV_ARM_VGIC_OFFSET_SHIFT 0 +#define KVM_DEV_ARM_VGIC_OFFSET_MASK (0xffffffffULL << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) + /* KVM_IRQ_LINE irq field index values */ #define KVM_ARM_IRQ_TYPE_SHIFT 24 #define KVM_ARM_IRQ_TYPE_MASK 0xff diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h index 6836ec79a8..a6665be4f3 100644 --- a/linux-headers/asm-powerpc/kvm.h +++ b/linux-headers/asm-powerpc/kvm.h @@ -545,6 +545,7 @@ struct kvm_get_htab_header { #define KVM_REG_PPC_TCSCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb1) #define KVM_REG_PPC_PID (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb2) #define KVM_REG_PPC_ACOP (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb3) +#define KVM_REG_PPC_WORT (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb4) #define KVM_REG_PPC_VRSAVE (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb4) #define KVM_REG_PPC_LPCR (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb5) @@ -553,6 +554,8 @@ struct kvm_get_htab_header { /* Architecture compatibility level */ #define KVM_REG_PPC_ARCH_COMPAT (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb7) +#define KVM_REG_PPC_DABRX (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb8) + /* Transactional Memory checkpointed state: * This is all GPRs, all VSX regs and a subset of SPRs */ diff --git a/linux-headers/asm-s390/kvm.h b/linux-headers/asm-s390/kvm.h index d25da598ec..cb4c1eb8a0 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/asm-x86/hyperv.h b/linux-headers/asm-x86/hyperv.h index 3b400ee9f7..462efe746d 100644 --- a/linux-headers/asm-x86/hyperv.h +++ b/linux-headers/asm-x86/hyperv.h @@ -28,6 +28,9 @@ /* Partition Reference Counter (HV_X64_MSR_TIME_REF_COUNT) available*/ #define HV_X64_MSR_TIME_REF_COUNT_AVAILABLE (1 << 1) +/* A partition's reference time stamp counter (TSC) page */ +#define HV_X64_MSR_REFERENCE_TSC 0x40000021 + /* * There is a single feature flag that signifies the presence of the MSR * that can be used to retrieve both the local APIC Timer frequency as @@ -149,9 +152,6 @@ /* MSR used to read the per-partition time reference counter */ #define HV_X64_MSR_TIME_REF_COUNT 0x40000020 -/* A partition's reference time stamp counter (TSC) page */ -#define HV_X64_MSR_REFERENCE_TSC 0x40000021 - /* MSR used to retrieve the TSC frequency */ #define HV_X64_MSR_TSC_FREQUENCY 0x40000022 @@ -201,6 +201,9 @@ #define HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_MASK \ (~((1ull << HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT) - 1)) +#define HV_X64_MSR_TSC_REFERENCE_ENABLE 0x00000001 +#define HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT 12 + #define HV_PROCESSOR_POWER_STATE_C0 0 #define HV_PROCESSOR_POWER_STATE_C1 1 #define HV_PROCESSOR_POWER_STATE_C2 2 @@ -213,4 +216,11 @@ #define HV_STATUS_INVALID_ALIGNMENT 4 #define HV_STATUS_INSUFFICIENT_BUFFERS 19 +typedef struct _HV_REFERENCE_TSC_PAGE { + __u32 tsc_sequence; + __u32 res1; + __u64 tsc_scale; + __s64 tsc_offset; +} HV_REFERENCE_TSC_PAGE, *PHV_REFERENCE_TSC_PAGE; + #endif diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 999fb135e1..e27a4b33cf 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 @@ -854,6 +919,8 @@ struct kvm_device_attr { #define KVM_DEV_VFIO_GROUP 1 #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 diff --git a/linux-user/main.c b/linux-user/main.c index 919297736c..be9491bc7d 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1492,7 +1492,7 @@ static int do_store_exclusive(CPUPPCState *env) { target_ulong addr; target_ulong page_addr; - target_ulong val; + target_ulong val, val2 __attribute__((unused)); int flags; int segv = 0; @@ -1515,6 +1515,13 @@ static int do_store_exclusive(CPUPPCState *env) case 4: segv = get_user_u32(val, addr); break; #if defined(TARGET_PPC64) case 8: segv = get_user_u64(val, addr); break; + case 16: { + segv = get_user_u64(val, addr); + if (!segv) { + segv = get_user_u64(val2, addr + 8); + } + break; + } #endif default: abort(); } @@ -1526,6 +1533,15 @@ static int do_store_exclusive(CPUPPCState *env) case 4: segv = put_user_u32(val, addr); break; #if defined(TARGET_PPC64) case 8: segv = put_user_u64(val, addr); break; + case 16: { + if (val2 == env->reserve_val2) { + segv = put_user_u64(val, addr); + if (!segv) { + segv = put_user_u64(val2, addr + 8); + } + } + break; + } #endif default: abort(); } diff --git a/migration-rdma.c b/migration-rdma.c index f94f3b4e3a..eeb4302215 100644 --- a/migration-rdma.c +++ b/migration-rdma.c @@ -3412,7 +3412,7 @@ void rdma_start_outgoing_migration(void *opaque, } ret = qemu_rdma_source_init(rdma, &local_err, - s->enabled_capabilities[MIGRATION_CAPABILITY_X_RDMA_PIN_ALL]); + s->enabled_capabilities[MIGRATION_CAPABILITY_RDMA_PIN_ALL]); if (ret) { goto err; diff --git a/migration.c b/migration.c index 25add6f9e2..14235b280a 100644 --- a/migration.c +++ b/migration.c @@ -82,7 +82,7 @@ void qemu_start_incoming_migration(const char *uri, Error **errp) if (strstart(uri, "tcp:", &p)) tcp_start_incoming_migration(p, errp); #ifdef CONFIG_RDMA - else if (strstart(uri, "x-rdma:", &p)) + else if (strstart(uri, "rdma:", &p)) rdma_start_incoming_migration(p, errp); #endif #if !defined(WIN32) @@ -438,7 +438,7 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, if (strstart(uri, "tcp:", &p)) { tcp_start_outgoing_migration(s, p, &local_err); #ifdef CONFIG_RDMA - } else if (strstart(uri, "x-rdma:", &p)) { + } else if (strstart(uri, "rdma:", &p)) { rdma_start_outgoing_migration(s, p, &local_err); #endif #if !defined(WIN32) @@ -532,7 +532,7 @@ bool migrate_rdma_pin_all(void) s = migrate_get_current(); - return s->enabled_capabilities[MIGRATION_CAPABILITY_X_RDMA_PIN_ALL]; + return s->enabled_capabilities[MIGRATION_CAPABILITY_RDMA_PIN_ALL]; } bool migrate_auto_converge(void) diff --git a/monitor.c b/monitor.c index aebcbd8beb..342e83baea 100644 --- a/monitor.c +++ b/monitor.c @@ -39,6 +39,7 @@ #include "monitor/monitor.h" #include "qemu/readline.h" #include "ui/console.h" +#include "ui/input.h" #include "sysemu/blockdev.h" #include "audio/audio.h" #include "disas/disas.h" @@ -1463,23 +1464,43 @@ static int mouse_button_state; static void do_mouse_move(Monitor *mon, const QDict *qdict) { - int dx, dy, dz; + int dx, dy, dz, button; const char *dx_str = qdict_get_str(qdict, "dx_str"); const char *dy_str = qdict_get_str(qdict, "dy_str"); const char *dz_str = qdict_get_try_str(qdict, "dz_str"); + dx = strtol(dx_str, NULL, 0); dy = strtol(dy_str, NULL, 0); - dz = 0; - if (dz_str) + qemu_input_queue_rel(NULL, INPUT_AXIS_X, dx); + qemu_input_queue_rel(NULL, INPUT_AXIS_Y, dy); + + if (dz_str) { dz = strtol(dz_str, NULL, 0); - kbd_mouse_event(dx, dy, dz, mouse_button_state); + if (dz != 0) { + button = (dz > 0) ? INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN; + qemu_input_queue_btn(NULL, button, true); + qemu_input_event_sync(); + qemu_input_queue_btn(NULL, button, false); + } + } + qemu_input_event_sync(); } static void do_mouse_button(Monitor *mon, const QDict *qdict) { + static uint32_t bmap[INPUT_BUTTON_MAX] = { + [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON, + [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON, + [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON, + }; int button_state = qdict_get_int(qdict, "button_state"); + + if (mouse_button_state == button_state) { + return; + } + qemu_input_update_buttons(NULL, bmap, mouse_button_state, button_state); + qemu_input_event_sync(); mouse_button_state = button_state; - kbd_mouse_event(0, 0, 0, mouse_button_state); } static void do_ioport_read(Monitor *mon, const QDict *qdict) @@ -2021,10 +2042,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/net/net.c b/net/net.c index 41b38830ea..e3ef1e4f1d 100644 --- a/net/net.c +++ b/net/net.c @@ -378,6 +378,61 @@ void qemu_foreach_nic(qemu_nic_foreach func, void *opaque) } } +bool qemu_has_ufo(NetClientState *nc) +{ + if (!nc || !nc->info->has_ufo) { + return false; + } + + return nc->info->has_ufo(nc); +} + +bool qemu_has_vnet_hdr(NetClientState *nc) +{ + if (!nc || !nc->info->has_vnet_hdr) { + return false; + } + + return nc->info->has_vnet_hdr(nc); +} + +bool qemu_has_vnet_hdr_len(NetClientState *nc, int len) +{ + if (!nc || !nc->info->has_vnet_hdr_len) { + return false; + } + + return nc->info->has_vnet_hdr_len(nc, len); +} + +void qemu_using_vnet_hdr(NetClientState *nc, bool enable) +{ + if (!nc || !nc->info->using_vnet_hdr) { + return; + } + + nc->info->using_vnet_hdr(nc, enable); +} + +void qemu_set_offload(NetClientState *nc, int csum, int tso4, int tso6, + int ecn, int ufo) +{ + if (!nc || !nc->info->set_offload) { + return; + } + + nc->info->set_offload(nc, csum, tso4, tso6, ecn, ufo); +} + +void qemu_set_vnet_hdr_len(NetClientState *nc, int len) +{ + if (!nc || !nc->info->set_vnet_hdr_len) { + return; + } + + nc->info->set_vnet_hdr_len(nc, len); +} + int qemu_can_send_packet(NetClientState *sender) { if (!sender->peer) { diff --git a/net/netmap.c b/net/netmap.c index 0ccc4976b5..8213304a5b 100644 --- a/net/netmap.c +++ b/net/netmap.c @@ -27,10 +27,13 @@ #include <net/if.h> #include <sys/mman.h> #include <stdint.h> +#include <stdio.h> +#define NETMAP_WITH_LIBS #include <net/netmap.h> #include <net/netmap_user.h> #include "net/net.h" +#include "net/tap.h" #include "clients.h" #include "sysemu/sysemu.h" #include "qemu/error-report.h" @@ -54,33 +57,9 @@ typedef struct NetmapState { bool read_poll; bool write_poll; struct iovec iov[IOV_MAX]; + int vnet_hdr_len; /* Current virtio-net header length. */ } NetmapState; -#define D(format, ...) \ - do { \ - struct timeval __xxts; \ - gettimeofday(&__xxts, NULL); \ - printf("%03d.%06d %s [%d] " format "\n", \ - (int)__xxts.tv_sec % 1000, (int)__xxts.tv_usec, \ - __func__, __LINE__, ##__VA_ARGS__); \ - } while (0) - -/* Rate limited version of "D", lps indicates how many per second */ -#define RD(lps, format, ...) \ - do { \ - static int t0, __cnt; \ - struct timeval __xxts; \ - gettimeofday(&__xxts, NULL); \ - if (t0 != __xxts.tv_sec) { \ - t0 = __xxts.tv_sec; \ - __cnt = 0; \ - } \ - if (__cnt++ < lps) { \ - D(format, ##__VA_ARGS__); \ - } \ - } while (0) - - #ifndef __FreeBSD__ #define pkt_copy bcopy #else @@ -237,7 +216,7 @@ static ssize_t netmap_receive(NetClientState *nc, return size; } - if (ring->avail == 0) { + if (nm_ring_empty(ring)) { /* No available slots in the netmap TX ring. */ netmap_write_poll(s, true); return 0; @@ -250,8 +229,7 @@ static ssize_t netmap_receive(NetClientState *nc, ring->slot[i].len = size; ring->slot[i].flags = 0; pkt_copy(buf, dst, size); - ring->cur = NETMAP_RING_NEXT(ring, i); - ring->avail--; + ring->cur = ring->head = nm_ring_next(ring, i); ioctl(s->me.fd, NIOCTXSYNC, NULL); return size; @@ -267,17 +245,15 @@ static ssize_t netmap_receive_iov(NetClientState *nc, uint8_t *dst; int j; uint32_t i; - uint32_t avail; if (unlikely(!ring)) { /* Drop the packet. */ return iov_size(iov, iovcnt); } - i = ring->cur; - avail = ring->avail; + last = i = ring->cur; - if (avail < iovcnt) { + if (nm_ring_space(ring) < iovcnt) { /* Not enough netmap slots. */ netmap_write_poll(s, true); return 0; @@ -293,7 +269,7 @@ static ssize_t netmap_receive_iov(NetClientState *nc, while (iov_frag_size) { nm_frag_size = MIN(iov_frag_size, ring->nr_buf_size); - if (unlikely(avail == 0)) { + if (unlikely(nm_ring_empty(ring))) { /* We run out of netmap slots while splitting the iovec fragments. */ netmap_write_poll(s, true); @@ -308,8 +284,7 @@ static ssize_t netmap_receive_iov(NetClientState *nc, pkt_copy(iov[j].iov_base + offset, dst, nm_frag_size); last = i; - i = NETMAP_RING_NEXT(ring, i); - avail--; + i = nm_ring_next(ring, i); offset += nm_frag_size; iov_frag_size -= nm_frag_size; @@ -318,9 +293,8 @@ static ssize_t netmap_receive_iov(NetClientState *nc, /* The last slot must not have NS_MOREFRAG set. */ ring->slot[last].flags &= ~NS_MOREFRAG; - /* Now update ring->cur and ring->avail. */ - ring->cur = i; - ring->avail = avail; + /* Now update ring->cur and ring->head. */ + ring->cur = ring->head = i; ioctl(s->me.fd, NIOCTXSYNC, NULL); @@ -343,7 +317,7 @@ static void netmap_send(void *opaque) /* Keep sending while there are available packets into the netmap RX ring and the forwarding path towards the peer is open. */ - while (ring->avail > 0 && qemu_can_send_packet(&s->nc)) { + while (!nm_ring_empty(ring) && qemu_can_send_packet(&s->nc)) { uint32_t i; uint32_t idx; bool morefrag; @@ -358,11 +332,10 @@ static void netmap_send(void *opaque) s->iov[iovcnt].iov_len = ring->slot[i].len; iovcnt++; - ring->cur = NETMAP_RING_NEXT(ring, i); - ring->avail--; - } while (ring->avail && morefrag); + ring->cur = ring->head = nm_ring_next(ring, i); + } while (!nm_ring_empty(ring) && morefrag); - if (unlikely(!ring->avail && morefrag)) { + if (unlikely(nm_ring_empty(ring) && morefrag)) { RD(5, "[netmap_send] ran out of slots, with a pending" "incomplete packet\n"); } @@ -394,6 +367,63 @@ static void netmap_cleanup(NetClientState *nc) s->me.fd = -1; } +/* Offloading manipulation support callbacks. */ +static bool netmap_has_ufo(NetClientState *nc) +{ + return true; +} + +static bool netmap_has_vnet_hdr(NetClientState *nc) +{ + return true; +} + +static bool netmap_has_vnet_hdr_len(NetClientState *nc, int len) +{ + return len == 0 || len == sizeof(struct virtio_net_hdr) || + len == sizeof(struct virtio_net_hdr_mrg_rxbuf); +} + +static void netmap_using_vnet_hdr(NetClientState *nc, bool enable) +{ +} + +static void netmap_set_vnet_hdr_len(NetClientState *nc, int len) +{ + NetmapState *s = DO_UPCAST(NetmapState, nc, nc); + int err; + struct nmreq req; + + /* Issue a NETMAP_BDG_VNET_HDR command to change the virtio-net header + * length for the netmap adapter associated to 'me->ifname'. + */ + memset(&req, 0, sizeof(req)); + pstrcpy(req.nr_name, sizeof(req.nr_name), s->me.ifname); + req.nr_version = NETMAP_API; + req.nr_cmd = NETMAP_BDG_VNET_HDR; + req.nr_arg1 = len; + err = ioctl(s->me.fd, NIOCREGIF, &req); + if (err) { + error_report("Unable to execute NETMAP_BDG_VNET_HDR on %s: %s", + s->me.ifname, strerror(errno)); + } else { + /* Keep track of the current length. */ + s->vnet_hdr_len = len; + } +} + +static void netmap_set_offload(NetClientState *nc, int csum, int tso4, int tso6, + int ecn, int ufo) +{ + NetmapState *s = DO_UPCAST(NetmapState, nc, nc); + + /* Setting a virtio-net header length greater than zero automatically + * enables the offloadings. + */ + if (!s->vnet_hdr_len) { + netmap_set_vnet_hdr_len(nc, sizeof(struct virtio_net_hdr)); + } +} /* NetClientInfo methods */ static NetClientInfo net_netmap_info = { @@ -403,6 +433,12 @@ static NetClientInfo net_netmap_info = { .receive_iov = netmap_receive_iov, .poll = netmap_poll, .cleanup = netmap_cleanup, + .has_ufo = netmap_has_ufo, + .has_vnet_hdr = netmap_has_vnet_hdr, + .has_vnet_hdr_len = netmap_has_vnet_hdr_len, + .using_vnet_hdr = netmap_using_vnet_hdr, + .set_offload = netmap_set_offload, + .set_vnet_hdr_len = netmap_set_vnet_hdr_len, }; /* The exported init function @@ -428,6 +464,7 @@ int net_init_netmap(const NetClientOptions *opts, nc = qemu_new_net_client(&net_netmap_info, peer, "netmap", name); s = DO_UPCAST(NetmapState, nc, nc); s->me = me; + s->vnet_hdr_len = 0; netmap_read_poll(s, true); /* Initially only poll for reads. */ return 0; diff --git a/net/tap-win32.c b/net/tap-win32.c index 91e9e844a0..8aee611f7d 100644 --- a/net/tap-win32.c +++ b/net/tap-win32.c @@ -669,11 +669,60 @@ static void tap_win32_send(void *opaque) } } +static bool tap_has_ufo(NetClientState *nc) +{ + return false; +} + +static bool tap_has_vnet_hdr(NetClientState *nc) +{ + return false; +} + +int tap_probe_vnet_hdr_len(int fd, int len) +{ + return 0; +} + +void tap_fd_set_vnet_hdr_len(int fd, int len) +{ +} + +static void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr) +{ +} + +static void tap_set_offload(NetClientState *nc, int csum, int tso4, + int tso6, int ecn, int ufo) +{ +} + +struct vhost_net *tap_get_vhost_net(NetClientState *nc) +{ + return NULL; +} + +static bool tap_has_vnet_hdr_len(NetClientState *nc, int len) +{ + return false; +} + +static void tap_set_vnet_hdr_len(NetClientState *nc, int len) +{ + abort(); +} + static NetClientInfo net_tap_win32_info = { .type = NET_CLIENT_OPTIONS_KIND_TAP, .size = sizeof(TAPState), .receive = tap_receive, .cleanup = tap_cleanup, + .has_ufo = tap_has_ufo, + .has_vnet_hdr = tap_has_vnet_hdr, + .has_vnet_hdr_len = tap_has_vnet_hdr_len, + .using_vnet_hdr = tap_using_vnet_hdr, + .set_offload = tap_set_offload, + .set_vnet_hdr_len = tap_set_vnet_hdr_len, }; static int tap_win32_init(NetClientState *peer, const char *model, @@ -722,49 +771,6 @@ int net_init_tap(const NetClientOptions *opts, const char *name, return 0; } -bool tap_has_ufo(NetClientState *nc) -{ - return false; -} - -int tap_has_vnet_hdr(NetClientState *nc) -{ - return 0; -} - -int tap_probe_vnet_hdr_len(int fd, int len) -{ - return 0; -} - -void tap_fd_set_vnet_hdr_len(int fd, int len) -{ -} - -void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr) -{ -} - -void tap_set_offload(NetClientState *nc, int csum, int tso4, - int tso6, int ecn, int ufo) -{ -} - -struct vhost_net *tap_get_vhost_net(NetClientState *nc) -{ - return NULL; -} - -int tap_has_vnet_hdr_len(NetClientState *nc, int len) -{ - return 0; -} - -void tap_set_vnet_hdr_len(NetClientState *nc, int len) -{ - abort(); -} - int tap_enable(NetClientState *nc) { abort(); diff --git a/net/tap.c b/net/tap.c index 39c1cda3e4..2d5099b9be 100644 --- a/net/tap.c +++ b/net/tap.c @@ -210,7 +210,7 @@ static void tap_send(void *opaque) } while (size > 0 && qemu_can_send_packet(&s->nc)); } -bool tap_has_ufo(NetClientState *nc) +static bool tap_has_ufo(NetClientState *nc) { TAPState *s = DO_UPCAST(TAPState, nc, nc); @@ -219,7 +219,7 @@ bool tap_has_ufo(NetClientState *nc) return s->has_ufo; } -int tap_has_vnet_hdr(NetClientState *nc) +static bool tap_has_vnet_hdr(NetClientState *nc) { TAPState *s = DO_UPCAST(TAPState, nc, nc); @@ -228,16 +228,16 @@ int tap_has_vnet_hdr(NetClientState *nc) return !!s->host_vnet_hdr_len; } -int tap_has_vnet_hdr_len(NetClientState *nc, int len) +static bool tap_has_vnet_hdr_len(NetClientState *nc, int len) { TAPState *s = DO_UPCAST(TAPState, nc, nc); assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP); - return tap_probe_vnet_hdr_len(s->fd, len); + return !!tap_probe_vnet_hdr_len(s->fd, len); } -void tap_set_vnet_hdr_len(NetClientState *nc, int len) +static void tap_set_vnet_hdr_len(NetClientState *nc, int len) { TAPState *s = DO_UPCAST(TAPState, nc, nc); @@ -249,7 +249,7 @@ void tap_set_vnet_hdr_len(NetClientState *nc, int len) s->host_vnet_hdr_len = len; } -void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr) +static void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr) { TAPState *s = DO_UPCAST(TAPState, nc, nc); @@ -259,7 +259,7 @@ void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr) s->using_vnet_hdr = using_vnet_hdr; } -void tap_set_offload(NetClientState *nc, int csum, int tso4, +static void tap_set_offload(NetClientState *nc, int csum, int tso4, int tso6, int ecn, int ufo) { TAPState *s = DO_UPCAST(TAPState, nc, nc); @@ -314,6 +314,12 @@ static NetClientInfo net_tap_info = { .receive_iov = tap_receive_iov, .poll = tap_poll, .cleanup = tap_cleanup, + .has_ufo = tap_has_ufo, + .has_vnet_hdr = tap_has_vnet_hdr, + .has_vnet_hdr_len = tap_has_vnet_hdr_len, + .using_vnet_hdr = tap_using_vnet_hdr, + .set_offload = tap_set_offload, + .set_vnet_hdr_len = tap_set_vnet_hdr_len, }; static TAPState *net_tap_fd_init(NetClientState *peer, diff --git a/page_cache.c b/page_cache.c index 3ef6ee7ad2..b033681a93 100644 --- a/page_cache.c +++ b/page_cache.c @@ -150,7 +150,7 @@ uint8_t *get_cached_data(const PageCache *cache, uint64_t addr) return cache_get_by_addr(cache, addr)->it_data; } -int cache_insert(PageCache *cache, uint64_t addr, uint8_t *pdata) +int cache_insert(PageCache *cache, uint64_t addr, const uint8_t *pdata) { CacheItem *it = NULL; diff --git a/pc-bios/QEMU,cgthree.bin b/pc-bios/QEMU,cgthree.bin new file mode 100644 index 0000000000..6fec946207 --- /dev/null +++ b/pc-bios/QEMU,cgthree.bin Binary files differdiff --git a/pc-bios/README b/pc-bios/README index f1900686dc..5914200b23 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 index 6727f0ca39..f6223e77c2 100644 --- a/pc-bios/s390-ccw.img +++ b/pc-bios/s390-ccw.img Binary files differdiff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index c5d533231b..5c33766533 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 4d6e48fcbe..a46914dd68 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 fcb2280053..6c381b7306 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -751,10 +751,9 @@ # This feature allows us to minimize migration traffic for certain work # loads, by sending compressed difference of the pages # -# @x-rdma-pin-all: Controls whether or not the entire VM memory footprint is +# @rdma-pin-all: Controls whether or not the entire VM memory footprint is # mlock()'d on demand or all at once. Refer to docs/rdma.txt for usage. -# Disabled by default. Experimental: may (or may not) be renamed after -# further testing is complete. (since 1.6) +# Disabled by default. (since 2.0) # # @zero-blocks: During storage migration encode blocks of zeroes efficiently. This # essentially saves 1MB of zeroes per block on the wire. Enabling requires @@ -768,7 +767,7 @@ # Since: 1.2 ## { 'enum': 'MigrationCapability', - 'data': ['xbzrle', 'x-rdma-pin-all', 'auto-converge', 'zero-blocks'] } + 'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks'] } ## # @MigrationCapabilityStatus @@ -2796,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 @@ -2831,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: @@ -3509,9 +3555,12 @@ # This is used by the send-key command. # # Since: 1.3.0 +# +# 'unmapped' and 'pause' since 2.0 ## { 'enum': 'QKeyCode', - 'data': [ 'shift', 'shift_r', 'alt', 'alt_r', 'altgr', 'altgr_r', 'ctrl', + 'data': [ 'unmapped', + 'shift', 'shift_r', 'alt', 'alt_r', 'altgr', 'altgr_r', 'ctrl', 'ctrl_r', 'menu', 'esc', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'minus', 'equal', 'backspace', 'tab', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', 'bracket_left', 'bracket_right', @@ -3525,7 +3574,7 @@ 'kp_9', 'less', 'f11', 'f12', 'print', 'home', 'pgup', 'pgdn', 'end', 'left', 'up', 'down', 'right', 'insert', 'delete', 'stop', 'again', 'props', 'undo', 'front', 'copy', 'open', 'paste', 'find', 'cut', - 'lf', 'help', 'meta_l', 'meta_r', 'compose' ] } + 'lf', 'help', 'meta_l', 'meta_r', 'compose', 'pause' ] } ## # @KeyValue @@ -4437,10 +4486,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 ## @@ -4519,3 +4569,79 @@ # Since: 1.7 ## { 'command': 'blockdev-add', 'data': { 'options': 'BlockdevOptions' } } + +## +# @InputButton +# +# Button of a pointer input device (mouse, tablet). +# +# Since: 2.0 +## +{ 'enum' : 'InputButton', + 'data' : [ 'Left', 'Middle', 'Right', 'WheelUp', 'WheelDown' ] } + +## +# @InputButton +# +# Position axis of a pointer input device (mouse, tablet). +# +# Since: 2.0 +## +{ 'enum' : 'InputAxis', + 'data' : [ 'X', 'Y' ] } + +## +# @InputKeyEvent +# +# Keyboard input event. +# +# @key: Which key this event is for. +# @down: True for key-down and false for key-up events. +# +# Since: 2.0 +## +{ 'type' : 'InputKeyEvent', + 'data' : { 'key' : 'KeyValue', + 'down' : 'bool' } } + +## +# @InputBtnEvent +# +# Pointer button input event. +# +# @button: Which button this event is for. +# @down: True for key-down and false for key-up events. +# +# Since: 2.0 +## +{ 'type' : 'InputBtnEvent', + 'data' : { 'button' : 'InputButton', + 'down' : 'bool' } } + +## +# @InputMoveEvent +# +# Pointer motion input event. +# +# @axis: Which axis is referenced by @value. +# @value: Pointer position. For absolute coordinates the +# valid range is 0 -> 0x7ffff +# +# Since: 2.0 +## +{ 'type' : 'InputMoveEvent', + 'data' : { 'axis' : 'InputAxis', + 'value' : 'int' } } + +## +# @InputEvent +# +# Input event union. +# +# Since: 2.0 +## +{ 'union' : 'InputEvent', + 'data' : { 'key' : 'InputKeyEvent', + 'btn' : 'InputBtnEvent', + 'rel' : 'InputMoveEvent', + 'abs' : 'InputMoveEvent' } } diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c index 96ed85899d..5d830a2b56 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 dc53545fa5..d0ea118fe3 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/qdev-monitor.c b/qdev-monitor.c index 3a7dc0d428..6673e3cb61 100644 --- a/qdev-monitor.c +++ b/qdev-monitor.c @@ -548,16 +548,18 @@ DeviceState *qdev_device_add(QemuOpts *opts) OBJECT(dev), NULL); g_free(name); } + + dev->opts = opts; object_property_set_bool(OBJECT(dev), true, "realized", &err); if (err != NULL) { qerror_report_err(err); error_free(err); + dev->opts = NULL; object_unparent(OBJECT(dev)); object_unref(OBJECT(dev)); qerror_report(QERR_DEVICE_INIT_FAILED, driver); return NULL; } - dev->opts = opts; return dev; } diff --git a/qemu-file.c b/qemu-file.c index 9473b674ba..f074af15c3 100644 --- a/qemu-file.c +++ b/qemu-file.c @@ -100,7 +100,14 @@ static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) { QEMUFileStdio *s = opaque; - return fwrite(buf, 1, size, s->stdio_file); + int res; + + res = fwrite(buf, 1, size, s->stdio_file); + + if (res != size) { + return -EIO; /* fake errno value */ + } + return res; } static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) diff --git a/qemu-img.c b/qemu-img.c index 78fc86826c..2e40cc1e69 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -32,12 +32,6 @@ #include "block/block_int.h" #include "block/qapi.h" #include <getopt.h> -#include <stdio.h> -#include <stdarg.h> - -#ifdef _WIN32 -#include <windows.h> -#endif typedef struct img_cmd_t { const char *name; @@ -1168,9 +1162,6 @@ static int img_convert(int argc, char **argv) Error *local_err = NULL; QemuOpts *sn_opts = NULL; - /* Initialize before goto out */ - qemu_progress_init(progress, 1.0); - fmt = NULL; out_fmt = "raw"; cache = "unsafe"; @@ -1203,17 +1194,17 @@ static int img_convert(int argc, char **argv) error_report("option -e is deprecated, please use \'-o " "encryption\' instead!"); ret = -1; - goto out; + goto fail_getopt; case '6': error_report("option -6 is deprecated, please use \'-o " "compat6\' instead!"); ret = -1; - goto out; + goto fail_getopt; case 'o': if (!is_valid_option_list(optarg)) { error_report("Invalid option list: %s", optarg); ret = -1; - goto out; + goto fail_getopt; } if (!options) { options = g_strdup(optarg); @@ -1233,7 +1224,7 @@ static int img_convert(int argc, char **argv) error_report("Failed in parsing snapshot param '%s'", optarg); ret = -1; - goto out; + goto fail_getopt; } } else { snapshot_name = optarg; @@ -1247,7 +1238,7 @@ static int img_convert(int argc, char **argv) if (sval < 0 || *end) { error_report("Invalid minimum zero buffer size for sparse output specified"); ret = -1; - goto out; + goto fail_getopt; } min_sparse = sval / BDRV_SECTOR_SIZE; @@ -1268,9 +1259,12 @@ static int img_convert(int argc, char **argv) } } + /* Initialize before goto out */ if (quiet) { progress = 0; } + qemu_progress_init(progress, 1.0); + bs_n = argc - optind - 1; out_filename = bs_n >= 1 ? argv[argc - 1] : NULL; @@ -1673,7 +1667,6 @@ out: free_option_parameters(create_options); free_option_parameters(param); qemu_vfree(buf); - g_free(options); if (sn_opts) { qemu_opts_del(sn_opts); } @@ -1688,6 +1681,9 @@ out: } g_free(bs); } +fail_getopt: + g_free(options); + if (ret) { return 1; } diff --git a/qga/main.c b/qga/main.c index cfca291b43..d838c3e32a 100644 --- a/qga/main.c +++ b/qga/main.c @@ -35,7 +35,6 @@ #ifdef _WIN32 #include "qga/service-win32.h" #include "qga/vss-win32.h" -#include <windows.h> #endif #ifdef __linux__ #include <linux/fs.h> diff --git a/qmp-commands.hx b/qmp-commands.hx index 8a0e8320c6..d982cd62b9 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/qmp.c b/qmp.c index d0d98e777b..f556a04d19 100644 --- a/qmp.c +++ b/qmp.c @@ -556,8 +556,8 @@ void object_add(const char *type, const char *id, const QDict *qdict, } if (!object_dynamic_cast(obj, TYPE_USER_CREATABLE)) { - error_setg(&local_err, "object '%s' isn't supported by object-add", - id); + error_setg(&local_err, "object type '%s' isn't supported by object-add", + type); goto out; } diff --git a/qtest.c b/qtest.c index ae941d6551..0ac9f429f5 100644 --- a/qtest.c +++ b/qtest.c @@ -500,7 +500,7 @@ static void qtest_event(void *opaque, int event) } } -int qtest_init_accel(void) +int qtest_init_accel(QEMUMachine *machine) { configure_icount("0"); diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index b12b6964ef..9734ab0a53 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 4a1652b56f..2c6e0dcd5c 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 65f1a54ee7..c6de9aeed4 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 9b3de4c7c3..f3c2a2037a 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 d374b357aa..e0e848bc30 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 5c9717594f..20b6ec795e 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/stubs/Makefile.objs b/stubs/Makefile.objs index df92fe5b15..df3aa7a64e 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -27,3 +27,4 @@ stub-obj-y += vm-stop.o stub-obj-y += vmstate.o stub-obj-$(CONFIG_WIN32) += fd-register.o stub-obj-y += cpus.o +stub-obj-y += kvm.o diff --git a/stubs/gdbstub.c b/stubs/gdbstub.c index c1dbfe7fb7..f6a4553a31 100644 --- a/stubs/gdbstub.c +++ b/stubs/gdbstub.c @@ -1,4 +1,6 @@ -#include "qemu-common.h" +#include "stdbool.h" /* bool (in exec/gdbstub.h) */ +#include "stddef.h" /* NULL */ +#include "exec/gdbstub.h" /* xml_builtin */ const char *const xml_builtin[][2] = { { NULL, NULL } diff --git a/stubs/kvm.c b/stubs/kvm.c new file mode 100644 index 0000000000..e7c60b6e0c --- /dev/null +++ b/stubs/kvm.c @@ -0,0 +1,7 @@ +#include "qemu-common.h" +#include "sysemu/kvm.h" + +int kvm_arch_irqchip_create(KVMState *s) +{ + return 0; +} diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index afbd4222c5..00234e1d3d 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -132,6 +132,16 @@ typedef struct ARMCPU { uint32_t id_isar3; uint32_t id_isar4; uint32_t id_isar5; + uint64_t id_aa64pfr0; + uint64_t id_aa64pfr1; + uint64_t id_aa64dfr0; + uint64_t id_aa64dfr1; + uint64_t id_aa64afr0; + uint64_t id_aa64afr1; + uint64_t id_aa64isar0; + uint64_t id_aa64isar1; + uint64_t id_aa64mmfr0; + uint64_t id_aa64mmfr1; uint32_t clidr; /* The elements of this array are the CCSIDR values for each cache, * in the order L1DCache, L1ICache, L2DCache, L2ICache, etc. diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 6e7ce8905e..1ce8a9bc38 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -60,7 +60,7 @@ static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque) return; } - if (ri->type & ARM_CP_64BIT) { + if (cpreg_field_is_64bit(ri)) { CPREG_FIELD64(&cpu->env, ri) = ri->resetvalue; } else { CPREG_FIELD32(&cpu->env, ri) = ri->resetvalue; @@ -91,9 +91,10 @@ static void arm_cpu_reset(CPUState *s) env->aarch64 = 1; #if defined(CONFIG_USER_ONLY) env->pstate = PSTATE_MODE_EL0t; + /* Userspace expects access to CTL_EL0 and the cache ops */ + env->cp15.c1_sys |= SCTLR_UCT | SCTLR_UCI; #else - env->pstate = PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F - | PSTATE_MODE_EL1h; + env->pstate = PSTATE_MODE_EL1h; #endif } @@ -108,13 +109,14 @@ static void arm_cpu_reset(CPUState *s) } #else /* SVC mode with interrupts disabled. */ - env->uncached_cpsr = ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I; + env->uncached_cpsr = ARM_CPU_MODE_SVC; + env->daif = PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F; /* On ARMv7-M the CPSR_I is the value of the PRIMASK register, and is clear at reset. Initial SP and PC are loaded from ROM. */ if (IS_M(env)) { uint32_t pc; uint8_t *rom; - env->uncached_cpsr &= ~CPSR_I; + env->daif &= ~PSTATE_I; rom = rom_ptr(0); if (rom) { /* We should really use ldl_phys here, in case the guest @@ -922,6 +924,7 @@ static void arm_any_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_ARM_DIV); set_feature(&cpu->env, ARM_FEATURE_V7MP); + set_feature(&cpu->env, ARM_FEATURE_CRC); #ifdef TARGET_AARCH64 set_feature(&cpu->env, ARM_FEATURE_AARCH64); #endif diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 3c8a2dba2f..49fef3fcbe 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -74,8 +74,10 @@ */ #ifdef HOST_WORDS_BIGENDIAN #define offsetoflow32(S, M) (offsetof(S, M) + sizeof(uint32_t)) +#define offsetofhigh32(S, M) offsetof(S, M) #else #define offsetoflow32(S, M) offsetof(S, M) +#define offsetofhigh32(S, M) (offsetof(S, M) + sizeof(uint32_t)) #endif /* Meanings of the ARMCPU object's two inbound GPIO lines */ @@ -102,7 +104,7 @@ struct arm_boot_info; /* CPU state for each instance of a generic timer (in cp15 c14) */ typedef struct ARMGenericTimer { uint64_t cval; /* Timer CompareValue register */ - uint32_t ctl; /* Timer Control register */ + uint64_t ctl; /* Timer Control register */ } ARMGenericTimer; #define GTIMER_PHYS 0 @@ -133,6 +135,7 @@ typedef struct CPUARMState { * NZCV are kept in the split out env->CF/VF/NF/ZF, (which have the same * semantics as for AArch32, as described in the comments on each field) * nRW (also known as M[4]) is kept, inverted, in env->aarch64 + * DAIF (exception masks) are kept in env->daif * all other bits are stored in their correct places in env->pstate */ uint32_t pstate; @@ -162,20 +165,19 @@ typedef struct CPUARMState { uint32_t GE; /* cpsr[19:16] */ uint32_t thumb; /* cpsr[5]. 0 = arm mode, 1 = thumb mode. */ uint32_t condexec_bits; /* IT bits. cpsr[15:10,26:25]. */ + uint32_t daif; /* exception masks, in the bits they are in in PSTATE */ /* System control coprocessor (cp15) */ struct { uint32_t c0_cpuid; - uint32_t c0_cssel; /* Cache size selection. */ - uint32_t c1_sys; /* System control register. */ - uint32_t c1_coproc; /* Coprocessor access register. */ + uint64_t c0_cssel; /* Cache size selection. */ + uint64_t c1_sys; /* System control register. */ + uint64_t c1_coproc; /* Coprocessor access register. */ uint32_t c1_xscaleauxcr; /* XScale auxiliary control register. */ uint32_t c1_scr; /* secure config register. */ - uint32_t c2_base0; /* MMU translation table base 0. */ - uint32_t c2_base0_hi; /* MMU translation table base 0, high 32 bits */ - uint32_t c2_base1; /* MMU translation table base 0. */ - uint32_t c2_base1_hi; /* MMU translation table base 1, high 32 bits */ - uint32_t c2_control; /* MMU translation table base control. */ + uint64_t ttbr0_el1; /* MMU translation table base 0. */ + uint64_t ttbr1_el1; /* MMU translation table base 1. */ + uint64_t c2_control; /* MMU translation table base control. */ uint32_t c2_mask; /* MMU translation table base selection mask. */ uint32_t c2_base_mask; /* MMU translation table base 0 mask. */ uint32_t c2_data; /* MPU data cachable bits. */ @@ -197,14 +199,15 @@ typedef struct CPUARMState { uint32_t c9_pmxevtyper; /* perf monitor event type */ uint32_t c9_pmuserenr; /* perf monitor user enable */ uint32_t c9_pminten; /* perf monitor interrupt enables */ - uint32_t c12_vbar; /* vector base address register */ + uint64_t mair_el1; + uint64_t c12_vbar; /* vector base address register */ uint32_t c13_fcse; /* FCSE PID. */ uint32_t c13_context; /* Context ID. */ uint64_t tpidr_el0; /* User RW Thread register. */ uint64_t tpidrro_el0; /* User RO Thread register. */ uint64_t tpidr_el1; /* Privileged Thread register. */ - uint32_t c14_cntfrq; /* Counter Frequency register */ - uint32_t c14_cntkctl; /* Timer Control register */ + uint64_t c14_cntfrq; /* Counter Frequency register */ + uint64_t c14_cntkctl; /* Timer Control register */ ARMGenericTimer c14_timer[NUM_GTIMERS]; uint32_t c15_cpar; /* XScale Coprocessor Access Register */ uint32_t c15_ticonfig; /* TI925T configuration byte. */ @@ -215,6 +218,10 @@ typedef struct CPUARMState { uint32_t c15_diagnostic; /* diagnostic register */ uint32_t c15_power_diagnostic; uint32_t c15_power_control; /* power control */ + uint64_t dbgbvr[16]; /* breakpoint value registers */ + uint64_t dbgbcr[16]; /* breakpoint control registers */ + uint64_t dbgwvr[16]; /* watchpoint value registers */ + uint64_t dbgwcr[16]; /* watchpoint control registers */ } cp15; struct { @@ -401,9 +408,11 @@ int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address, int rw, #define CPSR_Z (1U << 30) #define CPSR_N (1U << 31) #define CPSR_NZCV (CPSR_N | CPSR_Z | CPSR_C | CPSR_V) +#define CPSR_AIF (CPSR_A | CPSR_I | CPSR_F) #define CPSR_IT (CPSR_IT_0_1 | CPSR_IT_2_7) -#define CACHED_CPSR_BITS (CPSR_T | CPSR_GE | CPSR_IT | CPSR_Q | CPSR_NZCV) +#define CACHED_CPSR_BITS (CPSR_T | CPSR_AIF | CPSR_GE | CPSR_IT | CPSR_Q \ + | CPSR_NZCV) /* Bits writable in user mode. */ #define CPSR_USER (CPSR_NZCV | CPSR_Q | CPSR_GE) /* Execution state bits. MRS read as zero, MSR writes ignored. */ @@ -426,7 +435,8 @@ int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address, int rw, #define PSTATE_Z (1U << 30) #define PSTATE_N (1U << 31) #define PSTATE_NZCV (PSTATE_N | PSTATE_Z | PSTATE_C | PSTATE_V) -#define CACHED_PSTATE_BITS (PSTATE_NZCV) +#define PSTATE_DAIF (PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F) +#define CACHED_PSTATE_BITS (PSTATE_NZCV | PSTATE_DAIF) /* Mode values for AArch64 */ #define PSTATE_MODE_EL3h 13 #define PSTATE_MODE_EL3t 12 @@ -447,7 +457,7 @@ static inline uint32_t pstate_read(CPUARMState *env) ZF = (env->ZF == 0); return (env->NF & 0x80000000) | (ZF << 30) | (env->CF << 29) | ((env->VF & 0x80000000) >> 3) - | env->pstate; + | env->pstate | env->daif; } static inline void pstate_write(CPUARMState *env, uint32_t val) @@ -456,6 +466,7 @@ static inline void pstate_write(CPUARMState *env, uint32_t val) env->NF = val; env->CF = (val >> 29) & 1; env->VF = (val << 3) & 0x80000000; + env->daif = val & PSTATE_DAIF; env->pstate = val & ~CACHED_PSTATE_BITS; } @@ -615,6 +626,7 @@ enum arm_features { ARM_FEATURE_AARCH64, /* supports 64 bit mode */ ARM_FEATURE_V8_AES, /* implements AES part of v8 Crypto Extensions */ ARM_FEATURE_CBAR, /* has cp15 CBAR */ + ARM_FEATURE_CRC, /* ARMv8 CRC instructions */ }; static inline int arm_feature(CPUARMState *env, int feature) @@ -622,6 +634,22 @@ static inline int arm_feature(CPUARMState *env, int feature) return (env->features & (1ULL << feature)) != 0; } +/* Return true if the specified exception level is running in AArch64 state. */ +static inline bool arm_el_is_aa64(CPUARMState *env, int el) +{ + /* We don't currently support EL2 or EL3, and this isn't valid for EL0 + * (if we're in EL0, is_a64() is what you want, and if we're not in EL0 + * then the state of EL0 isn't well defined.) + */ + assert(el == 1); + /* AArch64-capable CPUs always run with EL1 in AArch64 mode. This + * is a QEMU-imposed simplification which we may wish to change later. + * If we in future support EL2 and/or EL3, then the state of lower + * exception levels is controlled by the HCR.RW and SCR.RW bits. + */ + return arm_feature(env, ARM_FEATURE_AARCH64); +} + void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf); /* Interface between CPU and Interrupt controller. */ @@ -731,7 +759,8 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid) #define ARM_CP_NOP (ARM_CP_SPECIAL | (1 << 8)) #define ARM_CP_WFI (ARM_CP_SPECIAL | (2 << 8)) #define ARM_CP_NZCV (ARM_CP_SPECIAL | (3 << 8)) -#define ARM_LAST_SPECIAL ARM_CP_NZCV +#define ARM_CP_CURRENTEL (ARM_CP_SPECIAL | (4 << 8)) +#define ARM_LAST_SPECIAL ARM_CP_CURRENTEL /* Used only as a terminator for ARMCPRegInfo lists */ #define ARM_CP_SENTINEL 0xffff /* Mask of only the flag bits in a type field */ @@ -959,6 +988,14 @@ uint64_t arm_cp_read_zero(CPUARMState *env, const ARMCPRegInfo *ri); */ void arm_cp_reset_ignore(CPUARMState *env, const ARMCPRegInfo *opaque); +/* Return true if this reginfo struct's field in the cpu state struct + * is 64 bits wide. + */ +static inline bool cpreg_field_is_64bit(const ARMCPRegInfo *ri) +{ + return (ri->state == ARM_CP_STATE_AA64) || (ri->type & ARM_CP_64BIT); +} + static inline bool cp_access_ok(int current_pl, const ARMCPRegInfo *ri, int isread) { @@ -1043,7 +1080,7 @@ static inline CPUARMState *cpu_init(const char *cpu_model) #define MMU_USER_IDX 1 static inline int cpu_mmu_index (CPUARMState *env) { - return (env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR ? 1 : 0; + return arm_current_pl(env) ? 0 : 1; } #include "exec/cpu-all.h" @@ -1070,7 +1107,9 @@ static inline int cpu_mmu_index (CPUARMState *env) #define ARM_TBFLAG_BSWAP_CODE_SHIFT 16 #define ARM_TBFLAG_BSWAP_CODE_MASK (1 << ARM_TBFLAG_BSWAP_CODE_SHIFT) -/* Bit usage when in AArch64 state: currently no bits defined */ +/* Bit usage when in AArch64 state */ +#define ARM_TBFLAG_AA64_EL_SHIFT 0 +#define ARM_TBFLAG_AA64_EL_MASK (0x3 << ARM_TBFLAG_AA64_EL_SHIFT) /* some convenience accessor macros */ #define ARM_TBFLAG_AARCH64_STATE(F) \ @@ -1089,13 +1128,16 @@ static inline int cpu_mmu_index (CPUARMState *env) (((F) & ARM_TBFLAG_CONDEXEC_MASK) >> ARM_TBFLAG_CONDEXEC_SHIFT) #define ARM_TBFLAG_BSWAP_CODE(F) \ (((F) & ARM_TBFLAG_BSWAP_CODE_MASK) >> ARM_TBFLAG_BSWAP_CODE_SHIFT) +#define ARM_TBFLAG_AA64_EL(F) \ + (((F) & ARM_TBFLAG_AA64_EL_MASK) >> ARM_TBFLAG_AA64_EL_SHIFT) static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, target_ulong *cs_base, int *flags) { if (is_a64(env)) { *pc = env->pc; - *flags = ARM_TBFLAG_AARCH64_STATE_MASK; + *flags = ARM_TBFLAG_AARCH64_STATE_MASK + | (arm_current_pl(env) << ARM_TBFLAG_AA64_EL_SHIFT); } else { int privmode; *pc = env->regs[15]; diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c index a639c2e476..8426bf1333 100644 --- a/target-arm/cpu64.c +++ b/target-arm/cpu64.c @@ -45,6 +45,7 @@ static void aarch64_any_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_ARM_DIV); set_feature(&cpu->env, ARM_FEATURE_V7MP); set_feature(&cpu->env, ARM_FEATURE_AARCH64); + cpu->ctr = 0x80030003; /* 32 byte I and D cacheline size, VIPT icache */ } #endif diff --git a/target-arm/helper.c b/target-arm/helper.c index 1b111b6e85..90f85f1899 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -5,6 +5,8 @@ #include "sysemu/arch_init.h" #include "sysemu/sysemu.h" #include "qemu/bitops.h" +#include "qemu/crc32c.h" +#include <zlib.h> /* For crc32 */ #ifndef CONFIG_USER_ONLY static inline int get_phys_addr(CPUARMState *env, uint32_t address, @@ -109,7 +111,7 @@ static int aarch64_fpu_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) static uint64_t raw_read(CPUARMState *env, const ARMCPRegInfo *ri) { - if (ri->type & ARM_CP_64BIT) { + if (cpreg_field_is_64bit(ri)) { return CPREG_FIELD64(env, ri); } else { return CPREG_FIELD32(env, ri); @@ -119,7 +121,7 @@ static uint64_t raw_read(CPUARMState *env, const ARMCPRegInfo *ri) static void raw_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - if (ri->type & ARM_CP_64BIT) { + if (cpreg_field_is_64bit(ri)) { CPREG_FIELD64(env, ri) = value; } else { CPREG_FIELD32(env, ri) = value; @@ -458,7 +460,8 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { */ { .name = "WFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 1, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, }, - { .name = "CPACR", .cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 2, + { .name = "CPACR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, + .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 2, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_coproc), .resetvalue = 0, .writefn = cpacr_write }, REGINFO_SENTINEL @@ -533,6 +536,12 @@ static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, static void vbar_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { + /* Note that even though the AArch64 view of this register has bits + * [10:0] all RES0 we can only mask the bottom 5, to comply with the + * architectural requirements for bits which are RES0 only in some + * contexts. (ARMv8 would permit us to do no masking at all, but ARMv7 + * requires the bottom five bits to be RAZ/WI because they're UNK/SBZP.) + */ env->cp15.c12_vbar = value & ~0x1Ful; } @@ -622,16 +631,19 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .access = PL1_RW, .type = ARM_CP_NO_MIGRATE, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), .resetvalue = 0, .writefn = pmintenclr_write, }, - { .name = "VBAR", .cp = 15, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0, + { .name = "VBAR", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .writefn = vbar_write, .fieldoffset = offsetof(CPUARMState, cp15.c12_vbar), .resetvalue = 0 }, { .name = "SCR", .cp = 15, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_scr), .resetvalue = 0, }, - { .name = "CCSIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 0, + { .name = "CCSIDR", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 0, .access = PL1_R, .readfn = ccsidr_read, .type = ARM_CP_NO_MIGRATE }, - { .name = "CSSELR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 2, .opc2 = 0, + { .name = "CSSELR", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 2, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c0_cssel), .writefn = csselr_write, .resetvalue = 0 }, /* Auxiliary ID register: this actually has an IMPDEF value but for now @@ -639,6 +651,26 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { */ { .name = "AIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + /* MAIR can just read-as-written because we don't implement caches + * and so don't need to care about memory attributes. + */ + { .name = "MAIR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.mair_el1), + .resetvalue = 0 }, + /* For non-long-descriptor page tables these are PRRR and NMRR; + * regardless they still act as reads-as-written for QEMU. + * The override is necessary because of the overly-broad TLB_LOCKDOWN + * definition. + */ + { .name = "MAIR0", .state = ARM_CP_STATE_AA32, .type = ARM_CP_OVERRIDE, + .cp = 15, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0, .access = PL1_RW, + .fieldoffset = offsetoflow32(CPUARMState, cp15.mair_el1), + .resetfn = arm_cp_reset_ignore }, + { .name = "MAIR1", .state = ARM_CP_STATE_AA32, .type = ARM_CP_OVERRIDE, + .cp = 15, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 1, .access = PL1_RW, + .fieldoffset = offsetofhigh32(CPUARMState, cp15.mair_el1), + .resetfn = arm_cp_reset_ignore }, REGINFO_SENTINEL }; @@ -872,30 +904,55 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { * Our reset value matches the fixed frequency we implement the timer at. */ { .name = "CNTFRQ", .cp = 15, .crn = 14, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW | PL0_R, + .type = ARM_CP_NO_MIGRATE, + .access = PL1_RW | PL0_R, .accessfn = gt_cntfrq_access, + .fieldoffset = offsetoflow32(CPUARMState, cp15.c14_cntfrq), + .resetfn = arm_cp_reset_ignore, + }, + { .name = "CNTFRQ_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 0, + .access = PL1_RW | PL0_R, .accessfn = gt_cntfrq_access, .fieldoffset = offsetof(CPUARMState, cp15.c14_cntfrq), .resetvalue = (1000 * 1000 * 1000) / GTIMER_SCALE, - .accessfn = gt_cntfrq_access, }, /* overall control: mostly access permissions */ - { .name = "CNTKCTL", .cp = 15, .crn = 14, .crm = 1, .opc1 = 0, .opc2 = 0, + { .name = "CNTKCTL", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 14, .crm = 1, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c14_cntkctl), .resetvalue = 0, }, /* per-timer control */ { .name = "CNTP_CTL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 1, + .type = ARM_CP_IO | ARM_CP_NO_MIGRATE, .access = PL1_RW | PL0_R, + .accessfn = gt_ptimer_access, + .fieldoffset = offsetoflow32(CPUARMState, + cp15.c14_timer[GTIMER_PHYS].ctl), + .resetfn = arm_cp_reset_ignore, + .writefn = gt_ctl_write, .raw_writefn = raw_write, + }, + { .name = "CNTP_CTL_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 1, .type = ARM_CP_IO, .access = PL1_RW | PL0_R, + .accessfn = gt_ptimer_access, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl), .resetvalue = 0, - .accessfn = gt_ptimer_access, .writefn = gt_ctl_write, .raw_writefn = raw_write, }, { .name = "CNTV_CTL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 1, + .type = ARM_CP_IO | ARM_CP_NO_MIGRATE, .access = PL1_RW | PL0_R, + .accessfn = gt_vtimer_access, + .fieldoffset = offsetoflow32(CPUARMState, + cp15.c14_timer[GTIMER_VIRT].ctl), + .resetfn = arm_cp_reset_ignore, + .writefn = gt_ctl_write, .raw_writefn = raw_write, + }, + { .name = "CNTV_CTL_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 1, .type = ARM_CP_IO, .access = PL1_RW | PL0_R, + .accessfn = gt_vtimer_access, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl), .resetvalue = 0, - .accessfn = gt_vtimer_access, .writefn = gt_ctl_write, .raw_writefn = raw_write, }, /* TimerValue views: a 32 bit downcounting view of the underlying state */ @@ -904,37 +961,73 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .accessfn = gt_ptimer_access, .readfn = gt_tval_read, .writefn = gt_tval_write, }, + { .name = "CNTP_TVAL_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 0, + .type = ARM_CP_NO_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R, + .readfn = gt_tval_read, .writefn = gt_tval_write, + }, { .name = "CNTV_TVAL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 0, .type = ARM_CP_NO_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R, .accessfn = gt_vtimer_access, .readfn = gt_tval_read, .writefn = gt_tval_write, }, + { .name = "CNTV_TVAL_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 0, + .type = ARM_CP_NO_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R, + .readfn = gt_tval_read, .writefn = gt_tval_write, + }, /* The counter itself */ { .name = "CNTPCT", .cp = 15, .crm = 14, .opc1 = 0, .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE | ARM_CP_IO, .accessfn = gt_pct_access, + .readfn = gt_cnt_read, .resetfn = arm_cp_reset_ignore, + }, + { .name = "CNTPCT_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 1, + .access = PL0_R, .type = ARM_CP_NO_MIGRATE | ARM_CP_IO, + .accessfn = gt_pct_access, .readfn = gt_cnt_read, .resetfn = gt_cnt_reset, }, { .name = "CNTVCT", .cp = 15, .crm = 14, .opc1 = 1, .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE | ARM_CP_IO, .accessfn = gt_vct_access, + .readfn = gt_cnt_read, .resetfn = arm_cp_reset_ignore, + }, + { .name = "CNTVCT_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 2, + .access = PL0_R, .type = ARM_CP_NO_MIGRATE | ARM_CP_IO, + .accessfn = gt_vct_access, .readfn = gt_cnt_read, .resetfn = gt_cnt_reset, }, /* Comparison value, indicating when the timer goes off */ { .name = "CNTP_CVAL", .cp = 15, .crm = 14, .opc1 = 2, .access = PL1_RW | PL0_R, - .type = ARM_CP_64BIT | ARM_CP_IO, + .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_NO_MIGRATE, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), - .resetvalue = 0, - .accessfn = gt_ptimer_access, + .accessfn = gt_ptimer_access, .resetfn = arm_cp_reset_ignore, + .writefn = gt_cval_write, .raw_writefn = raw_write, + }, + { .name = "CNTP_CVAL_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 2, + .access = PL1_RW | PL0_R, + .type = ARM_CP_IO, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), + .resetvalue = 0, .accessfn = gt_vtimer_access, .writefn = gt_cval_write, .raw_writefn = raw_write, }, { .name = "CNTV_CVAL", .cp = 15, .crm = 14, .opc1 = 3, .access = PL1_RW | PL0_R, - .type = ARM_CP_64BIT | ARM_CP_IO, + .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_NO_MIGRATE, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), - .resetvalue = 0, - .accessfn = gt_vtimer_access, + .accessfn = gt_vtimer_access, .resetfn = arm_cp_reset_ignore, + .writefn = gt_cval_write, .raw_writefn = raw_write, + }, + { .name = "CNTV_CVAL_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 2, + .access = PL1_RW | PL0_R, + .type = ARM_CP_IO, + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), + .resetvalue = 0, .accessfn = gt_vtimer_access, .writefn = gt_cval_write, .raw_writefn = raw_write, }, REGINFO_SENTINEL @@ -1031,8 +1124,8 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) env->cp15.c7_par = phys_addr & 0xfffff000; } } else { - env->cp15.c7_par = ((ret & (10 << 1)) >> 5) | - ((ret & (12 << 1)) >> 6) | + env->cp15.c7_par = ((ret & (1 << 10)) >> 5) | + ((ret & (1 << 12)) >> 6) | ((ret & 0xf) << 1) | 1; } env->cp15.c7_par_hi = 0; @@ -1193,6 +1286,26 @@ static void vmsa_ttbcr_reset(CPUARMState *env, const ARMCPRegInfo *ri) env->cp15.c2_mask = 0; } +static void vmsa_tcr_el1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* For AArch64 the A1 bit could result in a change of ASID, so TLB flush. */ + tlb_flush(env, 1); + env->cp15.c2_control = value; +} + +static void vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* 64 bit accesses to the TTBRs can change the ASID and so we + * must flush the TLB. + */ + if (cpreg_field_is_64bit(ri)) { + tlb_flush(env, 1); + } + raw_write(env, ri, value); +} + static const ARMCPRegInfo vmsa_cp_reginfo[] = { { .name = "DFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, @@ -1200,16 +1313,23 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { { .name = "IFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c5_insn), .resetvalue = 0, }, - { .name = "TTBR0", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, - .fieldoffset = offsetof(CPUARMState, cp15.c2_base0), .resetvalue = 0, }, - { .name = "TTBR1", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 1, - .access = PL1_RW, - .fieldoffset = offsetof(CPUARMState, cp15.c2_base1), .resetvalue = 0, }, - { .name = "TTBCR", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2, - .access = PL1_RW, .writefn = vmsa_ttbcr_write, - .resetfn = vmsa_ttbcr_reset, .raw_writefn = vmsa_ttbcr_raw_write, + { .name = "TTBR0_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el1), + .writefn = vmsa_ttbr_write, .resetvalue = 0 }, + { .name = "TTBR1_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 1, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.ttbr1_el1), + .writefn = vmsa_ttbr_write, .resetvalue = 0 }, + { .name = "TCR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2, + .access = PL1_RW, .writefn = vmsa_tcr_el1_write, + .resetfn = vmsa_ttbcr_reset, .raw_writefn = raw_write, .fieldoffset = offsetof(CPUARMState, cp15.c2_control) }, + { .name = "TTBCR", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2, + .access = PL1_RW, .type = ARM_CP_NO_MIGRATE, .writefn = vmsa_ttbcr_write, + .resetfn = arm_cp_reset_ignore, .raw_writefn = vmsa_ttbcr_raw_write, + .fieldoffset = offsetoflow32(CPUARMState, cp15.c2_control) }, { .name = "DFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c6_data), .resetvalue = 0, }, @@ -1379,7 +1499,8 @@ static uint64_t mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri) { CPUState *cs = CPU(arm_env_get_cpu(env)); uint32_t mpidr = cs->cpu_index; - /* We don't support setting cluster ID ([8..11]) + /* We don't support setting cluster ID ([8..11]) (known as Aff1 + * in later ARM ARM versions), or any of the higher affinity level fields, * so these bits always RAZ. */ if (arm_feature(env, ARM_FEATURE_V7MP)) { @@ -1394,7 +1515,8 @@ static uint64_t mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri) } static const ARMCPRegInfo mpidr_cp_reginfo[] = { - { .name = "MPIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 5, + { .name = "MPIDR", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 5, .access = PL1_R, .readfn = mpidr_read, .type = ARM_CP_NO_MIGRATE }, REGINFO_SENTINEL }; @@ -1417,57 +1539,15 @@ static void par64_reset(CPUARMState *env, const ARMCPRegInfo *ri) env->cp15.c7_par = 0; } -static uint64_t ttbr064_read(CPUARMState *env, const ARMCPRegInfo *ri) -{ - return ((uint64_t)env->cp15.c2_base0_hi << 32) | env->cp15.c2_base0; -} - -static void ttbr064_raw_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - env->cp15.c2_base0_hi = value >> 32; - env->cp15.c2_base0 = value; -} - -static void ttbr064_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* Writes to the 64 bit format TTBRs may change the ASID */ - tlb_flush(env, 1); - ttbr064_raw_write(env, ri, value); -} - -static void ttbr064_reset(CPUARMState *env, const ARMCPRegInfo *ri) -{ - env->cp15.c2_base0_hi = 0; - env->cp15.c2_base0 = 0; -} - -static uint64_t ttbr164_read(CPUARMState *env, const ARMCPRegInfo *ri) -{ - return ((uint64_t)env->cp15.c2_base1_hi << 32) | env->cp15.c2_base1; -} - -static void ttbr164_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - env->cp15.c2_base1_hi = value >> 32; - env->cp15.c2_base1 = value; -} - -static void ttbr164_reset(CPUARMState *env, const ARMCPRegInfo *ri) -{ - env->cp15.c2_base1_hi = 0; - env->cp15.c2_base1 = 0; -} - static const ARMCPRegInfo lpae_cp_reginfo[] = { /* NOP AMAIR0/1: the override is because these clash with the rather * broadly specified TLB_LOCKDOWN entry in the generic cp_reginfo. */ - { .name = "AMAIR0", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 0, + { .name = "AMAIR0", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE, .resetvalue = 0 }, + /* AMAIR1 is mapped to AMAIR_EL1[63:32] */ { .name = "AMAIR1", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 1, .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE, .resetvalue = 0 }, @@ -1480,12 +1560,13 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = { .access = PL1_RW, .type = ARM_CP_64BIT, .readfn = par64_read, .writefn = par64_write, .resetfn = par64_reset }, { .name = "TTBR0", .cp = 15, .crm = 2, .opc1 = 0, - .access = PL1_RW, .type = ARM_CP_64BIT, .readfn = ttbr064_read, - .writefn = ttbr064_write, .raw_writefn = ttbr064_raw_write, - .resetfn = ttbr064_reset }, + .access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE, + .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el1), + .writefn = vmsa_ttbr_write, .resetfn = arm_cp_reset_ignore }, { .name = "TTBR1", .cp = 15, .crm = 2, .opc1 = 1, - .access = PL1_RW, .type = ARM_CP_64BIT, .readfn = ttbr164_read, - .writefn = ttbr164_write, .resetfn = ttbr164_reset }, + .access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE, + .fieldoffset = offsetof(CPUARMState, cp15.ttbr1_el1), + .writefn = vmsa_ttbr_write, .resetfn = arm_cp_reset_ignore }, REGINFO_SENTINEL }; @@ -1511,6 +1592,42 @@ static void aa64_fpsr_write(CPUARMState *env, const ARMCPRegInfo *ri, vfp_set_fpsr(env, value); } +static CPAccessResult aa64_cacheop_access(CPUARMState *env, + const ARMCPRegInfo *ri) +{ + /* Cache invalidate/clean: NOP, but EL0 must UNDEF unless + * SCTLR_EL1.UCI is set. + */ + if (arm_current_pl(env) == 0 && !(env->cp15.c1_sys & SCTLR_UCI)) { + return CP_ACCESS_TRAP; + } + return CP_ACCESS_OK; +} + +static void tlbi_aa64_va_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate by VA (AArch64 version) */ + uint64_t pageaddr = value << 12; + tlb_flush_page(env, pageaddr); +} + +static void tlbi_aa64_vaa_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate by VA, all ASIDs (AArch64 version) */ + uint64_t pageaddr = value << 12; + tlb_flush_page(env, pageaddr); +} + +static void tlbi_aa64_asid_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate by ASID (AArch64 version) */ + int asid = extract64(value, 48, 16); + tlb_flush(env, asid == 0); +} + static const ARMCPRegInfo v8_cp_reginfo[] = { /* Minimal set of EL0-visible registers. This will need to be expanded * significantly for system emulation of AArch64 CPUs. @@ -1524,13 +1641,6 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "FPSR", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 4, .crm = 4, .access = PL0_RW, .readfn = aa64_fpsr_read, .writefn = aa64_fpsr_write }, - /* This claims a 32 byte cacheline size for icache and dcache, VIPT icache. - * It will eventually need to have a CPU-specified reset value. - */ - { .name = "CTR_EL0", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 0, .crm = 0, - .access = PL0_R, .type = ARM_CP_CONST, - .resetvalue = 0x80030003 }, /* Prohibit use of DC ZVA. OPTME: implement DC ZVA and allow its use. * For system mode the DZP bit here will need to be computed, not constant. */ @@ -1538,6 +1648,103 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .opc0 = 3, .opc1 = 3, .opc2 = 7, .crn = 0, .crm = 0, .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0x10 }, + { .name = "CURRENTEL", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .opc2 = 2, .crn = 4, .crm = 2, + .access = PL1_R, .type = ARM_CP_CURRENTEL }, + /* Cache ops: all NOPs since we don't emulate caches */ + { .name = "IC_IALLUIS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 1, .opc2 = 0, + .access = PL1_W, .type = ARM_CP_NOP }, + { .name = "IC_IALLU", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 5, .opc2 = 0, + .access = PL1_W, .type = ARM_CP_NOP }, + { .name = "IC_IVAU", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 5, .opc2 = 1, + .access = PL0_W, .type = ARM_CP_NOP, + .accessfn = aa64_cacheop_access }, + { .name = "DC_IVAC", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 1, + .access = PL1_W, .type = ARM_CP_NOP }, + { .name = "DC_ISW", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 2, + .access = PL1_W, .type = ARM_CP_NOP }, + { .name = "DC_CVAC", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 10, .opc2 = 1, + .access = PL0_W, .type = ARM_CP_NOP, + .accessfn = aa64_cacheop_access }, + { .name = "DC_CSW", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 10, .opc2 = 2, + .access = PL1_W, .type = ARM_CP_NOP }, + { .name = "DC_CVAU", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 11, .opc2 = 1, + .access = PL0_W, .type = ARM_CP_NOP, + .accessfn = aa64_cacheop_access }, + { .name = "DC_CIVAC", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 14, .opc2 = 1, + .access = PL0_W, .type = ARM_CP_NOP, + .accessfn = aa64_cacheop_access }, + { .name = "DC_CISW", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 2, + .access = PL1_W, .type = ARM_CP_NOP }, + /* TLBI operations */ + { .name = "TLBI_VMALLE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc2 = 0, .crn = 8, .crm = 3, .opc2 = 0, + .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .writefn = tlbiall_write }, + { .name = "TLBI_VAE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc2 = 0, .crn = 8, .crm = 3, .opc2 = 1, + .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .writefn = tlbi_aa64_va_write }, + { .name = "TLBI_ASIDE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc2 = 0, .crn = 8, .crm = 3, .opc2 = 2, + .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .writefn = tlbi_aa64_asid_write }, + { .name = "TLBI_VAAE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc2 = 0, .crn = 8, .crm = 3, .opc2 = 3, + .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .writefn = tlbi_aa64_vaa_write }, + { .name = "TLBI_VALE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc2 = 0, .crn = 8, .crm = 3, .opc2 = 5, + .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .writefn = tlbi_aa64_va_write }, + { .name = "TLBI_VAALE1IS", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc2 = 0, .crn = 8, .crm = 3, .opc2 = 7, + .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .writefn = tlbi_aa64_vaa_write }, + { .name = "TLBI_VMALLE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 0, + .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .writefn = tlbiall_write }, + { .name = "TLBI_VAE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 1, + .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .writefn = tlbi_aa64_va_write }, + { .name = "TLBI_ASIDE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 2, + .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .writefn = tlbi_aa64_asid_write }, + { .name = "TLBI_VAAE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 3, + .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .writefn = tlbi_aa64_vaa_write }, + { .name = "TLBI_VALE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 5, + .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .writefn = tlbi_aa64_va_write }, + { .name = "TLBI_VAALE1", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 7, + .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .writefn = tlbi_aa64_vaa_write }, + /* Dummy implementation of monitor debug system control register: + * we don't support debug. + */ + { .name = "MDSCR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + /* We define a dummy WI OSLAR_EL1, because Linux writes to it. */ + { .name = "OSLAR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 4, + .access = PL1_W, .type = ARM_CP_NOP }, REGINFO_SENTINEL }; @@ -1550,6 +1757,48 @@ static void sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush(env, 1); } +static CPAccessResult ctr_el0_access(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* Only accessible in EL0 if SCTLR.UCT is set (and only in AArch64, + * but the AArch32 CTR has its own reginfo struct) + */ + if (arm_current_pl(env) == 0 && !(env->cp15.c1_sys & SCTLR_UCT)) { + return CP_ACCESS_TRAP; + } + return CP_ACCESS_OK; +} + +static void define_aarch64_debug_regs(ARMCPU *cpu) +{ + /* Define breakpoint and watchpoint registers. These do nothing + * but read as written, for now. + */ + int i; + + for (i = 0; i < 16; i++) { + ARMCPRegInfo dbgregs[] = { + { .name = "DBGBVR", .state = ARM_CP_STATE_AA64, + .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 4, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.dbgbvr[i]) }, + { .name = "DBGBCR", .state = ARM_CP_STATE_AA64, + .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 5, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.dbgbcr[i]) }, + { .name = "DBGWVR", .state = ARM_CP_STATE_AA64, + .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 6, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.dbgwvr[i]) }, + { .name = "DBGWCR", .state = ARM_CP_STATE_AA64, + .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 7, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.dbgwcr[i]) }, + REGINFO_SENTINEL + }; + define_arm_cp_regs(cpu, dbgregs); + } +} + void register_cp_regs_for_features(ARMCPU *cpu) { /* Register all the coprocessor registers based on feature bits */ @@ -1634,7 +1883,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) .raw_writefn = raw_write, }; ARMCPRegInfo clidr = { - .name = "CLIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 1, + .name = "CLIDR", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->clidr }; define_one_arm_cp_reg(cpu, &pmcr); @@ -1644,7 +1894,53 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, not_v7_cp_reginfo); } if (arm_feature(env, ARM_FEATURE_V8)) { + /* AArch64 ID registers, which all have impdef reset values */ + ARMCPRegInfo v8_idregs[] = { + { .name = "ID_AA64PFR0_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 0, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_aa64pfr0 }, + { .name = "ID_AA64PFR1_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 1, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_aa64pfr1}, + { .name = "ID_AA64DFR0_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 0, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_aa64dfr0 }, + { .name = "ID_AA64DFR1_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 1, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_aa64dfr1 }, + { .name = "ID_AA64AFR0_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 4, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_aa64afr0 }, + { .name = "ID_AA64AFR1_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 5, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_aa64afr1 }, + { .name = "ID_AA64ISAR0_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 0, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_aa64isar0 }, + { .name = "ID_AA64ISAR1_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 1, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_aa64isar1 }, + { .name = "ID_AA64MMFR0_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 0, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_aa64mmfr0 }, + { .name = "ID_AA64MMFR1_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 1, + .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_aa64mmfr1 }, + REGINFO_SENTINEL + }; + define_arm_cp_regs(cpu, v8_idregs); define_arm_cp_regs(cpu, v8_cp_reginfo); + define_aarch64_debug_regs(cpu); } if (arm_feature(env, ARM_FEATURE_MPU)) { /* These are the MPU registers prior to PMSAv6. Any new @@ -1710,9 +2006,16 @@ void register_cp_regs_for_features(ARMCPU *cpu) .writefn = arm_cp_write_ignore, .raw_writefn = raw_write, .fieldoffset = offsetof(CPUARMState, cp15.c0_cpuid), .type = ARM_CP_OVERRIDE }, + { .name = "MIDR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .opc2 = 0, .crn = 0, .crm = 0, + .access = PL1_R, .resetvalue = cpu->midr, .type = ARM_CP_CONST }, { .name = "CTR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->ctr }, + { .name = "CTR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 0, .crm = 0, + .access = PL0_R, .accessfn = ctr_el0_access, + .type = ARM_CP_CONST, .resetvalue = cpu->ctr }, { .name = "TCMTR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, @@ -1783,7 +2086,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) /* Generic registers whose values depend on the implementation */ { ARMCPRegInfo sctlr = { - .name = "SCTLR", .cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, + .name = "SCTLR", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_sys), .writefn = sctlr_write, .resetvalue = cpu->reset_sctlr, .raw_writefn = raw_write, @@ -1962,6 +2266,10 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, if (opaque) { r2->opaque = opaque; } + /* reginfo passed to helpers is correct for the actual access, + * and is never ARM_CP_STATE_BOTH: + */ + r2->state = state; /* Make sure reginfo passed to helpers for wildcarded regs * has the correct crm/opc1/opc2 for this reg, not CP_ANY: */ @@ -2170,7 +2478,7 @@ uint32_t cpsr_read(CPUARMState *env) (env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27) | (env->thumb << 5) | ((env->condexec_bits & 3) << 25) | ((env->condexec_bits & 0xfc) << 8) - | (env->GE << 16); + | (env->GE << 16) | env->daif; } void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) @@ -2197,6 +2505,9 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) env->GE = (val >> 16) & 0xf; } + env->daif &= ~(CPSR_AIF & mask); + env->daif |= val & CPSR_AIF & mask; + if ((env->uncached_cpsr ^ val) & mask & CPSR_M) { if (bad_mode_switch(env, val & CPSR_M)) { /* Attempt to switch to an invalid mode: this is UNPREDICTABLE. @@ -2658,7 +2969,7 @@ void arm_cpu_do_interrupt(CPUState *cs) env->condexec_bits = 0; /* Switch to the new mode, and to the correct instruction set. */ env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode; - env->uncached_cpsr |= mask; + env->daif |= mask; /* this is a lie, as the was no c1_sys on V4T/V5, but who cares * and we should just guard the thumb mode on V4 */ if (arm_feature(env, ARM_FEATURE_V4T)) { @@ -2730,9 +3041,9 @@ static uint32_t get_level1_table_address(CPUARMState *env, uint32_t address) uint32_t table; if (address & env->cp15.c2_mask) - table = env->cp15.c2_base1 & 0xffffc000; + table = env->cp15.ttbr1_el1 & 0xffffc000; else - table = env->cp15.c2_base0 & env->cp15.c2_base_mask; + table = env->cp15.ttbr0_el1 & env->cp15.c2_base_mask; table |= (address >> 18) & 0x3ffc; return table; @@ -2798,7 +3109,7 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type, break; case 2: /* 4k page. */ phys_addr = (desc & 0xfffff000) | (address & 0xfff); - ap = (desc >> (4 + ((address >> 13) & 6))) & 3; + ap = (desc >> (4 + ((address >> 9) & 6))) & 3; *page_size = 0x1000; break; case 3: /* 1k page. */ @@ -3008,11 +3319,11 @@ static int get_phys_addr_lpae(CPUARMState *env, uint32_t address, * we will always flush the TLB any time the ASID is changed). */ if (ttbr_select == 0) { - ttbr = ((uint64_t)env->cp15.c2_base0_hi << 32) | env->cp15.c2_base0; + ttbr = env->cp15.ttbr0_el1; epd = extract32(env->cp15.c2_control, 7, 1); tsz = t0sz; } else { - ttbr = ((uint64_t)env->cp15.c2_base1_hi << 32) | env->cp15.c2_base1; + ttbr = env->cp15.ttbr1_el1; epd = extract32(env->cp15.c2_control, 23, 1); tsz = t1sz; } @@ -3331,12 +3642,12 @@ uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) case 9: /* PSP */ return env->v7m.current_sp ? env->regs[13] : env->v7m.other_sp; case 16: /* PRIMASK */ - return (env->uncached_cpsr & CPSR_I) != 0; + return (env->daif & PSTATE_I) != 0; case 17: /* BASEPRI */ case 18: /* BASEPRI_MAX */ return env->v7m.basepri; case 19: /* FAULTMASK */ - return (env->uncached_cpsr & CPSR_F) != 0; + return (env->daif & PSTATE_F) != 0; case 20: /* CONTROL */ return env->v7m.control; default: @@ -3383,10 +3694,11 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val) env->v7m.other_sp = val; break; case 16: /* PRIMASK */ - if (val & 1) - env->uncached_cpsr |= CPSR_I; - else - env->uncached_cpsr &= ~CPSR_I; + if (val & 1) { + env->daif |= PSTATE_I; + } else { + env->daif &= ~PSTATE_I; + } break; case 17: /* BASEPRI */ env->v7m.basepri = val & 0xff; @@ -3397,10 +3709,11 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val) env->v7m.basepri = val; break; case 19: /* FAULTMASK */ - if (val & 1) - env->uncached_cpsr |= CPSR_F; - else - env->uncached_cpsr &= ~CPSR_F; + if (val & 1) { + env->daif |= PSTATE_F; + } else { + env->daif &= ~PSTATE_F; + } break; case 20: /* CONTROL */ env->v7m.control = val & 3; @@ -4392,3 +4705,40 @@ int arm_rmode_to_sf(int rmode) } return rmode; } + +static void crc_init_buffer(uint8_t *buf, uint32_t val, uint32_t bytes) +{ + memset(buf, 0, 4); + + if (bytes == 1) { + buf[0] = val & 0xff; + } else if (bytes == 2) { + buf[0] = val & 0xff; + buf[1] = (val >> 8) & 0xff; + } else { + buf[0] = val & 0xff; + buf[1] = (val >> 8) & 0xff; + buf[2] = (val >> 16) & 0xff; + buf[3] = (val >> 24) & 0xff; + } +} + +uint32_t HELPER(crc32)(uint32_t acc, uint32_t val, uint32_t bytes) +{ + uint8_t buf[4]; + + crc_init_buffer(buf, val, bytes); + + /* zlib crc32 converts the accumulator and output to one's complement. */ + return crc32(acc ^ 0xffffffff, buf, bytes) ^ 0xffffffff; +} + +uint32_t HELPER(crc32c)(uint32_t acc, uint32_t val, uint32_t bytes) +{ + uint8_t buf[4]; + + crc_init_buffer(buf, val, bytes); + + /* Linux crc32c converts the output to one's complement. */ + return crc32c(acc, buf, bytes) ^ 0xffffffff; +} diff --git a/target-arm/helper.h b/target-arm/helper.h index 19bd620532..276f3a9149 100644 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -63,6 +63,8 @@ DEF_HELPER_2(get_cp_reg, i32, env, ptr) DEF_HELPER_3(set_cp_reg64, void, env, ptr, i64) DEF_HELPER_2(get_cp_reg64, i64, env, ptr) +DEF_HELPER_3(msr_i_pstate, void, env, i32, i32) + DEF_HELPER_2(get_r13_banked, i32, env, i32) DEF_HELPER_3(set_r13_banked, void, env, i32, i32) @@ -497,6 +499,9 @@ DEF_HELPER_3(neon_qzip32, void, env, i32, i32) DEF_HELPER_4(crypto_aese, void, env, i32, i32, i32) DEF_HELPER_4(crypto_aesmc, void, env, i32, i32, i32) +DEF_HELPER_FLAGS_3(crc32, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) +DEF_HELPER_FLAGS_3(crc32c, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) + #ifdef TARGET_AARCH64 #include "helper-a64.h" #endif diff --git a/target-arm/kvm.c b/target-arm/kvm.c index 1d2688dda7..39202d7eea 100644 --- a/target-arm/kvm.c +++ b/target-arm/kvm.c @@ -165,8 +165,10 @@ unsigned long kvm_arch_vcpu_id(CPUState *cpu) */ typedef struct KVMDevice { struct kvm_arm_device_addr kda; + struct kvm_device_attr kdattr; MemoryRegion *mr; QSLIST_ENTRY(KVMDevice) entries; + int dev_fd; } KVMDevice; static QSLIST_HEAD(kvm_devices_head, KVMDevice) kvm_devices_head; @@ -200,6 +202,29 @@ static MemoryListener devlistener = { .region_del = kvm_arm_devlistener_del, }; +static void kvm_arm_set_device_addr(KVMDevice *kd) +{ + struct kvm_device_attr *attr = &kd->kdattr; + int ret; + + /* If the device control API is available and we have a device fd on the + * KVMDevice struct, let's use the newer API + */ + if (kd->dev_fd >= 0) { + uint64_t addr = kd->kda.addr; + attr->addr = (uintptr_t)&addr; + ret = kvm_device_ioctl(kd->dev_fd, KVM_SET_DEVICE_ATTR, attr); + } else { + ret = kvm_vm_ioctl(kvm_state, KVM_ARM_SET_DEVICE_ADDR, &kd->kda); + } + + if (ret < 0) { + fprintf(stderr, "Failed to set device address: %s\n", + strerror(-ret)); + abort(); + } +} + static void kvm_arm_machine_init_done(Notifier *notifier, void *data) { KVMDevice *kd, *tkd; @@ -207,12 +232,7 @@ static void kvm_arm_machine_init_done(Notifier *notifier, void *data) memory_listener_unregister(&devlistener); QSLIST_FOREACH_SAFE(kd, &kvm_devices_head, entries, tkd) { if (kd->kda.addr != -1) { - if (kvm_vm_ioctl(kvm_state, KVM_ARM_SET_DEVICE_ADDR, - &kd->kda) < 0) { - fprintf(stderr, "KVM_ARM_SET_DEVICE_ADDRESS failed: %s\n", - strerror(errno)); - abort(); - } + kvm_arm_set_device_addr(kd); } memory_region_unref(kd->mr); g_free(kd); @@ -223,7 +243,8 @@ static Notifier notify = { .notify = kvm_arm_machine_init_done, }; -void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid) +void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group, + uint64_t attr, int dev_fd) { KVMDevice *kd; @@ -239,6 +260,10 @@ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid) kd->mr = mr; kd->kda.id = devid; kd->kda.addr = -1; + kd->kdattr.flags = 0; + kd->kdattr.group = group; + kd->kdattr.attr = attr; + kd->dev_fd = dev_fd; QSLIST_INSERT_HEAD(&kvm_devices_head, kd, entries); memory_region_ref(kd->mr); } @@ -389,3 +414,19 @@ void kvm_arch_remove_all_hw_breakpoints(void) void kvm_arch_init_irq_routing(KVMState *s) { } + +int kvm_arch_irqchip_create(KVMState *s) +{ + int ret; + + /* If we can create the VGIC using the newer device control API, we + * let the device do this when it initializes itself, otherwise we + * fall back to the old API */ + + ret = kvm_create_device(s, KVM_DEV_TYPE_ARM_VGIC_V2, true); + if (ret == 0) { + return 1; + } + + return 0; +} diff --git a/target-arm/kvm_arm.h b/target-arm/kvm_arm.h index cd3d13ca2d..137c5671e9 100644 --- a/target-arm/kvm_arm.h +++ b/target-arm/kvm_arm.h @@ -18,16 +18,21 @@ * kvm_arm_register_device: * @mr: memory region for this device * @devid: the KVM device ID + * @group: device control API group for setting addresses + * @attr: device control API address type + * @dev_fd: device control device file descriptor (or -1 if not supported) * * Remember the memory region @mr, and when it is mapped by the * machine model, tell the kernel that base address using the - * KVM_SET_DEVICE_ADDRESS ioctl. @devid should be the ID of - * the device as defined by KVM_SET_DEVICE_ADDRESS. - * The machine model may map and unmap the device multiple times; - * the kernel will only be told the final address at the point - * where machine init is complete. + * KVM_ARM_SET_DEVICE_ADDRESS ioctl or the newer device control API. @devid + * should be the ID of the device as defined by KVM_ARM_SET_DEVICE_ADDRESS or + * the arm-vgic device in the device control API. + * The machine model may map + * and unmap the device multiple times; the kernel will only be told the final + * address at the point where machine init is complete. */ -void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid); +void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group, + uint64_t attr, int dev_fd); /** * write_list_to_kvmstate: diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index eb0fccd98f..7d06d2f9a5 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -319,6 +319,31 @@ uint64_t HELPER(get_cp_reg64)(CPUARMState *env, void *rip) return ri->readfn(env, ri); } +void HELPER(msr_i_pstate)(CPUARMState *env, uint32_t op, uint32_t imm) +{ + /* MSR_i to update PSTATE. This is OK from EL0 only if UMA is set. + * Note that SPSel is never OK from EL0; we rely on handle_msr_i() + * to catch that case at translate time. + */ + if (arm_current_pl(env) == 0 && !(env->cp15.c1_sys & SCTLR_UMA)) { + raise_exception(env, EXCP_UDEF); + } + + switch (op) { + case 0x05: /* SPSel */ + env->pstate = deposit32(env->pstate, 0, 1, imm); + break; + case 0x1e: /* DAIFSet */ + env->daif |= (imm << 6) & PSTATE_DAIF; + break; + case 0x1f: /* DAIFClear */ + env->daif &= ~((imm << 6) & PSTATE_DAIF); + break; + default: + g_assert_not_reached(); + } +} + /* ??? Flag setting arithmetic is awkward because we need to do comparisons. The only way to do that in TCG is a conditional branch, which clobbers all our temporaries. For now implement these as helper functions. */ diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 8752e7e16c..08ac6591b6 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -1080,9 +1080,11 @@ static void handle_hint(DisasContext *s, uint32_t insn, switch (selector) { case 0: /* NOP */ return; + case 3: /* WFI */ + s->is_jmp = DISAS_WFI; + return; case 1: /* YIELD */ case 2: /* WFE */ - case 3: /* WFI */ case 4: /* SEV */ case 5: /* SEVL */ /* we treat all as NOP at least for now */ @@ -1126,7 +1128,30 @@ static void handle_sync(DisasContext *s, uint32_t insn, static void handle_msr_i(DisasContext *s, uint32_t insn, unsigned int op1, unsigned int op2, unsigned int crm) { - unsupported_encoding(s, insn); + int op = op1 << 3 | op2; + switch (op) { + case 0x05: /* SPSel */ + if (s->current_pl == 0) { + unallocated_encoding(s); + return; + } + /* fall through */ + case 0x1e: /* DAIFSet */ + case 0x1f: /* DAIFClear */ + { + TCGv_i32 tcg_imm = tcg_const_i32(crm); + TCGv_i32 tcg_op = tcg_const_i32(op); + gen_a64_set_pc_im(s->pc - 4); + gen_helper_msr_i_pstate(cpu_env, tcg_op, tcg_imm); + tcg_temp_free_i32(tcg_imm); + tcg_temp_free_i32(tcg_op); + s->is_jmp = DISAS_UPDATE; + break; + } + default: + unallocated_encoding(s); + return; + } } static void gen_get_nzcv(TCGv_i64 tcg_rt) @@ -1231,6 +1256,13 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, gen_set_nzcv(tcg_rt); } return; + case ARM_CP_CURRENTEL: + /* Reads as current EL value from pstate, which is + * guaranteed to be constant by the tb flags. + */ + tcg_rt = cpu_reg(s, rt); + tcg_gen_movi_i64(tcg_rt, s->current_pl << 2); + return; default: break; } @@ -9006,7 +9038,7 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, dc->condexec_mask = 0; dc->condexec_cond = 0; #if !defined(CONFIG_USER_ONLY) - dc->user = 0; + dc->user = (ARM_TBFLAG_AA64_EL(tb->flags) == 0); #endif dc->vfp_enabled = 0; dc->vec_len = 0; @@ -9117,6 +9149,7 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, /* This is a special case because we don't want to just halt the CPU * if trying to debug across a WFI. */ + gen_a64_set_pc_im(dc->pc); gen_helper_wfi(cpu_env); break; } diff --git a/target-arm/translate.c b/target-arm/translate.c index 6ccf0ba482..253d2a13eb 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -7561,6 +7561,36 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) store_reg(s, 14, tmp2); gen_bx(s, tmp); break; + case 0x4: + { + /* crc32/crc32c */ + uint32_t c = extract32(insn, 8, 4); + + /* Check this CPU supports ARMv8 CRC instructions. + * op1 == 3 is UNPREDICTABLE but handle as UNDEFINED. + * Bits 8, 10 and 11 should be zero. + */ + if (!arm_feature(env, ARM_FEATURE_CRC) || op1 == 0x3 || + (c & 0xd) != 0) { + goto illegal_op; + } + + rn = extract32(insn, 16, 4); + rd = extract32(insn, 12, 4); + + tmp = load_reg(s, rn); + tmp2 = load_reg(s, rm); + tmp3 = tcg_const_i32(1 << op1); + if (c & 0x2) { + gen_helper_crc32c(tmp, tmp, tmp2, tmp3); + } else { + gen_helper_crc32(tmp, tmp, tmp2, tmp3); + } + tcg_temp_free_i32(tmp2); + tcg_temp_free_i32(tmp3); + store_reg(s, rd, tmp); + break; + } case 0x5: /* saturating add/subtract */ ARCH(5TE); rd = (insn >> 12) & 0xf; @@ -9145,6 +9175,32 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw case 0x18: /* clz */ gen_helper_clz(tmp, tmp); break; + case 0x20: + case 0x21: + case 0x22: + case 0x28: + case 0x29: + case 0x2a: + { + /* crc32/crc32c */ + uint32_t sz = op & 0x3; + uint32_t c = op & 0x8; + + if (!arm_feature(env, ARM_FEATURE_CRC)) { + goto illegal_op; + } + + tmp2 = load_reg(s, rm); + tmp3 = tcg_const_i32(1 << sz); + if (c) { + gen_helper_crc32c(tmp, tmp, tmp2, tmp3); + } else { + gen_helper_crc32(tmp, tmp, tmp2, tmp3); + } + tcg_temp_free_i32(tmp2); + tcg_temp_free_i32(tmp3); + break; + } default: goto illegal_op; } diff --git a/target-i386/cc_helper.c b/target-i386/cc_helper.c index ee04092b4e..05dd12b5a7 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 1b94f0ffb7..0014acca7b 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 c0427fec7a..de7ba76a49 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 15bebeff89..d34e5355f7 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 5dd2450798..707ebd5ca0 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-ppc/STATUS b/target-ppc/STATUS index c8e9018bfc..a4d48a7ca2 100644 --- a/target-ppc/STATUS +++ b/target-ppc/STATUS @@ -377,15 +377,6 @@ MMU OK EXCP KO partially implemented Remarks: Should be able to boot but there is no hw platform currently emulated. -PowerPC 970GX: -INSN KO Altivec missing and more -SPR KO -MSR ? -IRQ OK -MMU OK -EXCP KO partially implemented -Remarks: Should be able to boot but there is no hw platform currently emulated. - PowerPC Cell: INSN KO Altivec missing and more SPR KO diff --git a/target-ppc/cpu-models.c b/target-ppc/cpu-models.c index 7c9466fc07..f6c9b3ab01 100644 --- a/target-ppc/cpu-models.c +++ b/target-ppc/cpu-models.c @@ -1156,8 +1156,6 @@ "PowerPC 970FX v3.0 (G5)") POWERPC_DEF("970fx_v3.1", CPU_POWERPC_970FX_v31, 970FX, "PowerPC 970FX v3.1 (G5)") - POWERPC_DEF("970gx", CPU_POWERPC_970GX, 970GX, - "PowerPC 970GX (G5)") POWERPC_DEF("970mp_v1.0", CPU_POWERPC_970MP_v10, 970MP, "PowerPC 970MP v1.0") POWERPC_DEF("970mp_v1.1", CPU_POWERPC_970MP_v11, 970MP, diff --git a/target-ppc/cpu-models.h b/target-ppc/cpu-models.h index 49ba4a4522..644a126459 100644 --- a/target-ppc/cpu-models.h +++ b/target-ppc/cpu-models.h @@ -570,7 +570,6 @@ enum { CPU_POWERPC_970FX_v21 = 0x003C0201, CPU_POWERPC_970FX_v30 = 0x003C0300, CPU_POWERPC_970FX_v31 = 0x003C0301, - CPU_POWERPC_970GX = 0x00450000, CPU_POWERPC_970MP_v10 = 0x00440100, CPU_POWERPC_970MP_v11 = 0x00440101, #define CPU_POWERPC_CELL CPU_POWERPC_CELL_v32 diff --git a/target-ppc/cpu-qom.h b/target-ppc/cpu-qom.h index 72b22329b0..b17c024543 100644 --- a/target-ppc/cpu-qom.h +++ b/target-ppc/cpu-qom.h @@ -79,6 +79,7 @@ typedef struct PowerPCCPUClass { /** * PowerPCCPU: * @env: #CPUPPCState + * @cpu_dt_id: CPU index used in the device tree. KVM uses this index too * * A PowerPC CPU. */ @@ -88,6 +89,7 @@ typedef struct PowerPCCPU { /*< public >*/ CPUPPCState env; + int cpu_dt_id; } PowerPCCPU; static inline PowerPCCPU *ppc_env_get_cpu(CPUPPCState *env) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index bb847676a5..afab267485 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -352,6 +352,10 @@ union ppc_avr_t { int16_t s16[8]; int32_t s32[4]; uint64_t u64[2]; + int64_t s64[2]; +#ifdef CONFIG_INT128 + __uint128_t u128; +#endif }; #if !defined(CONFIG_USER_ONLY) @@ -926,6 +930,7 @@ struct CPUPPCState { target_ulong reserve_addr; /* Reservation value */ target_ulong reserve_val; + target_ulong reserve_val2; /* Reservation store address */ target_ulong reserve_ea; /* Reserved store source register and size */ @@ -961,6 +966,7 @@ struct CPUPPCState { #endif /* segment registers */ hwaddr htab_base; + /* mask used to normalize hash value to PTEG index */ hwaddr htab_mask; target_ulong sr[32]; /* externally stored hash table */ @@ -1250,7 +1256,7 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define SPR_MPC_EIE (0x050) #define SPR_MPC_EID (0x051) #define SPR_MPC_NRI (0x052) -#define SPR_CTRL (0x088) +#define SPR_UCTRL (0x088) #define SPR_MPC_CMPA (0x090) #define SPR_MPC_CMPB (0x091) #define SPR_MPC_CMPC (0x092) @@ -1259,7 +1265,7 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define SPR_MPC_DER (0x095) #define SPR_MPC_COUNTA (0x096) #define SPR_MPC_COUNTB (0x097) -#define SPR_UCTRL (0x098) +#define SPR_CTRL (0x098) #define SPR_MPC_CMPE (0x098) #define SPR_MPC_CMPF (0x099) #define SPR_MPC_CMPG (0x09A) @@ -1322,12 +1328,12 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define SPR_BOOKE_IAC3 (0x13A) #define SPR_HSRR1 (0x13B) #define SPR_BOOKE_IAC4 (0x13B) -#define SPR_LPCR (0x13C) #define SPR_BOOKE_DAC1 (0x13C) #define SPR_LPIDR (0x13D) #define SPR_DABR2 (0x13D) #define SPR_BOOKE_DAC2 (0x13D) #define SPR_BOOKE_DVC1 (0x13E) +#define SPR_LPCR (0x13E) #define SPR_BOOKE_DVC2 (0x13F) #define SPR_BOOKE_TSR (0x150) #define SPR_BOOKE_TCR (0x154) @@ -1508,6 +1514,7 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define SPR_RCPU_L2U_RA2 (0x32A) #define SPR_MPC_MD_DBRAM1 (0x32A) #define SPR_RCPU_L2U_RA3 (0x32B) +#define SPR_TAR (0x32F) #define SPR_440_INV0 (0x370) #define SPR_440_INV1 (0x371) #define SPR_440_INV2 (0x372) @@ -1875,9 +1882,31 @@ enum { PPC2_DBRX = 0x0000000000000010ULL, /* Book I 2.05 PowerPC specification */ PPC2_ISA205 = 0x0000000000000020ULL, + /* VSX additions in ISA 2.07 */ + PPC2_VSX207 = 0x0000000000000040ULL, + /* ISA 2.06B bpermd */ + PPC2_PERM_ISA206 = 0x0000000000000080ULL, + /* ISA 2.06B divide extended variants */ + PPC2_DIVE_ISA206 = 0x0000000000000100ULL, + /* ISA 2.06B larx/stcx. instructions */ + PPC2_ATOMIC_ISA206 = 0x0000000000000200ULL, + /* ISA 2.06B floating point integer conversion */ + PPC2_FP_CVT_ISA206 = 0x0000000000000400ULL, + /* ISA 2.06B floating point test instructions */ + PPC2_FP_TST_ISA206 = 0x0000000000000800ULL, + /* ISA 2.07 bctar instruction */ + PPC2_BCTAR_ISA207 = 0x0000000000001000ULL, + /* ISA 2.07 load/store quadword */ + PPC2_LSQ_ISA207 = 0x0000000000002000ULL, + /* ISA 2.07 Altivec */ + PPC2_ALTIVEC_207 = 0x0000000000004000ULL, #define PPC_TCG_INSNS2 (PPC2_BOOKE206 | PPC2_VSX | PPC2_PRCNTL | PPC2_DBRX | \ - PPC2_ISA205) + PPC2_ISA205 | PPC2_VSX207 | PPC2_PERM_ISA206 | \ + PPC2_DIVE_ISA206 | PPC2_ATOMIC_ISA206 | \ + PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206 | \ + PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | \ + PPC2_ALTIVEC_207) }; /*****************************************************************************/ @@ -2154,4 +2183,22 @@ static inline bool cpu_has_work(CPUState *cpu) void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env); +/** + * ppc_get_vcpu_dt_id: + * @cs: a PowerPCCPU struct. + * + * Returns a device-tree ID for a CPU. + */ +int ppc_get_vcpu_dt_id(PowerPCCPU *cpu); + +/** + * ppc_get_vcpu_by_dt_id: + * @cpu_dt_id: a device tree id + * + * Searches for a CPU by @cpu_dt_id. + * + * Returns: a PowerPCCPU struct + */ +PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id); + #endif /* !defined (__CPU_PPC_H__) */ diff --git a/target-ppc/fpu_helper.c b/target-ppc/fpu_helper.c index 4f6021835f..e7f329566d 100644 --- a/target-ppc/fpu_helper.c +++ b/target-ppc/fpu_helper.c @@ -50,6 +50,16 @@ static inline int isden(float64 d) return ((u.ll >> 52) & 0x7FF) == 0; } +static inline int ppc_float32_get_unbiased_exp(float32 f) +{ + return ((f >> 23) & 0xFF) - 127; +} + +static inline int ppc_float64_get_unbiased_exp(float64 f) +{ + return ((f >> 52) & 0x7FF) - 1023; +} + uint32_t helper_compute_fprf(CPUPPCState *env, uint64_t arg, uint32_t set_fprf) { CPU_DoubleU farg; @@ -106,7 +116,8 @@ uint32_t helper_compute_fprf(CPUPPCState *env, uint64_t arg, uint32_t set_fprf) } /* Floating-point invalid operations exception */ -static inline uint64_t fload_invalid_op_excp(CPUPPCState *env, int op) +static inline uint64_t fload_invalid_op_excp(CPUPPCState *env, int op, + int set_fpcc) { uint64_t ret = 0; int ve; @@ -138,8 +149,10 @@ static inline uint64_t fload_invalid_op_excp(CPUPPCState *env, int op) case POWERPC_EXCP_FP_VXVC: /* Ordered comparison of NaN */ env->fpscr |= 1 << FPSCR_VXVC; - env->fpscr &= ~(0xF << FPSCR_FPCC); - env->fpscr |= 0x11 << FPSCR_FPCC; + if (set_fpcc) { + env->fpscr &= ~(0xF << FPSCR_FPCC); + env->fpscr |= 0x11 << FPSCR_FPCC; + } /* We must update the target FPR before raising the exception */ if (ve != 0) { env->exception_index = POWERPC_EXCP_PROGRAM; @@ -158,8 +171,10 @@ static inline uint64_t fload_invalid_op_excp(CPUPPCState *env, int op) if (ve == 0) { /* Set the result to quiet NaN */ ret = 0x7FF8000000000000ULL; - env->fpscr &= ~(0xF << FPSCR_FPCC); - env->fpscr |= 0x11 << FPSCR_FPCC; + if (set_fpcc) { + env->fpscr &= ~(0xF << FPSCR_FPCC); + env->fpscr |= 0x11 << FPSCR_FPCC; + } } break; case POWERPC_EXCP_FP_VXCVI: @@ -169,8 +184,10 @@ static inline uint64_t fload_invalid_op_excp(CPUPPCState *env, int op) if (ve == 0) { /* Set the result to quiet NaN */ ret = 0x7FF8000000000000ULL; - env->fpscr &= ~(0xF << FPSCR_FPCC); - env->fpscr |= 0x11 << FPSCR_FPCC; + if (set_fpcc) { + env->fpscr &= ~(0xF << FPSCR_FPCC); + env->fpscr |= 0x11 << FPSCR_FPCC; + } } break; } @@ -505,12 +522,12 @@ uint64_t helper_fadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2) if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && float64_is_neg(farg1.d) != float64_is_neg(farg2.d))) { /* Magnitude subtraction of infinities */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d))) { /* sNaN addition */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } farg1.d = float64_add(farg1.d, farg2.d, &env->fp_status); } @@ -529,12 +546,12 @@ uint64_t helper_fsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2) if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && float64_is_neg(farg1.d) == float64_is_neg(farg2.d))) { /* Magnitude subtraction of infinities */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d))) { /* sNaN subtraction */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } farg1.d = float64_sub(farg1.d, farg2.d, &env->fp_status); } @@ -553,12 +570,12 @@ uint64_t helper_fmul(CPUPPCState *env, uint64_t arg1, uint64_t arg2) if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d))) { /* sNaN multiplication */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } farg1.d = float64_mul(farg1.d, farg2.d, &env->fp_status); } @@ -577,15 +594,15 @@ uint64_t helper_fdiv(CPUPPCState *env, uint64_t arg1, uint64_t arg2) if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d))) { /* Division of infinity by infinity */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI, 1); } else if (unlikely(float64_is_zero(farg1.d) && float64_is_zero(farg2.d))) { /* Division of zero by zero */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ, 1); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d))) { /* sNaN division */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } farg1.d = float64_div(farg1.d, farg2.d, &env->fp_status); } @@ -593,107 +610,63 @@ uint64_t helper_fdiv(CPUPPCState *env, uint64_t arg1, uint64_t arg2) return farg1.ll; } -/* fctiw - fctiw. */ -uint64_t helper_fctiw(CPUPPCState *env, uint64_t arg) -{ - CPU_DoubleU farg; - - farg.ll = arg; - - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN conversion */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | - POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_quiet_nan(farg.d) || - float64_is_infinity(farg.d))) { - /* qNan / infinity conversion */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); - } else { - farg.ll = float64_to_int32(farg.d, &env->fp_status); - /* XXX: higher bits are not supposed to be significant. - * to make tests easier, return the same as a real PowerPC 750 - */ - farg.ll |= 0xFFF80000ULL << 32; - } - return farg.ll; -} - -/* fctiwz - fctiwz. */ -uint64_t helper_fctiwz(CPUPPCState *env, uint64_t arg) -{ - CPU_DoubleU farg; - - farg.ll = arg; - - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN conversion */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | - POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_quiet_nan(farg.d) || - float64_is_infinity(farg.d))) { - /* qNan / infinity conversion */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); - } else { - farg.ll = float64_to_int32_round_to_zero(farg.d, &env->fp_status); - /* XXX: higher bits are not supposed to be significant. - * to make tests easier, return the same as a real PowerPC 750 - */ - farg.ll |= 0xFFF80000ULL << 32; - } - return farg.ll; -} +#define FPU_FCTI(op, cvt, nanval) \ +uint64_t helper_##op(CPUPPCState *env, uint64_t arg) \ +{ \ + CPU_DoubleU farg; \ + \ + farg.ll = arg; \ + farg.ll = float64_to_##cvt(farg.d, &env->fp_status); \ + \ + if (unlikely(env->fp_status.float_exception_flags)) { \ + if (float64_is_any_nan(arg)) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 1); \ + if (float64_is_signaling_nan(arg)) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); \ + } \ + farg.ll = nanval; \ + } else if (env->fp_status.float_exception_flags & \ + float_flag_invalid) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 1); \ + } \ + helper_float_check_status(env); \ + } \ + return farg.ll; \ + } + +FPU_FCTI(fctiw, int32, 0x80000000U) +FPU_FCTI(fctiwz, int32_round_to_zero, 0x80000000U) +FPU_FCTI(fctiwu, uint32, 0x00000000U) +FPU_FCTI(fctiwuz, uint32_round_to_zero, 0x00000000U) #if defined(TARGET_PPC64) -/* fcfid - fcfid. */ -uint64_t helper_fcfid(CPUPPCState *env, uint64_t arg) -{ - CPU_DoubleU farg; - - farg.d = int64_to_float64(arg, &env->fp_status); - return farg.ll; -} - -/* fctid - fctid. */ -uint64_t helper_fctid(CPUPPCState *env, uint64_t arg) -{ - CPU_DoubleU farg; - - farg.ll = arg; - - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN conversion */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | - POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_quiet_nan(farg.d) || - float64_is_infinity(farg.d))) { - /* qNan / infinity conversion */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); - } else { - farg.ll = float64_to_int64(farg.d, &env->fp_status); - } - return farg.ll; -} - -/* fctidz - fctidz. */ -uint64_t helper_fctidz(CPUPPCState *env, uint64_t arg) -{ - CPU_DoubleU farg; +FPU_FCTI(fctid, int64, 0x8000000000000000ULL) +FPU_FCTI(fctidz, int64_round_to_zero, 0x8000000000000000ULL) +FPU_FCTI(fctidu, uint64, 0x0000000000000000ULL) +FPU_FCTI(fctiduz, uint64_round_to_zero, 0x0000000000000000ULL) +#endif - farg.ll = arg; +#if defined(TARGET_PPC64) - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN conversion */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | - POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_quiet_nan(farg.d) || - float64_is_infinity(farg.d))) { - /* qNan / infinity conversion */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); - } else { - farg.ll = float64_to_int64_round_to_zero(farg.d, &env->fp_status); - } - return farg.ll; -} +#define FPU_FCFI(op, cvtr, is_single) \ +uint64_t helper_##op(CPUPPCState *env, uint64_t arg) \ +{ \ + CPU_DoubleU farg; \ + \ + if (is_single) { \ + float32 tmp = cvtr(arg, &env->fp_status); \ + farg.d = float32_to_float64(tmp, &env->fp_status); \ + } else { \ + farg.d = cvtr(arg, &env->fp_status); \ + } \ + helper_float_check_status(env); \ + return farg.ll; \ +} + +FPU_FCFI(fcfid, int64_to_float64, 0) +FPU_FCFI(fcfids, int64_to_float32, 1) +FPU_FCFI(fcfidu, uint64_to_float64, 0) +FPU_FCFI(fcfidus, uint64_to_float32, 1) #endif @@ -706,24 +679,28 @@ static inline uint64_t do_fri(CPUPPCState *env, uint64_t arg, if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN round */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | - POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_quiet_nan(farg.d) || - float64_is_infinity(farg.d))) { - /* qNan / infinity round */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + farg.ll = arg | 0x0008000000000000ULL; } else { + int inexact = get_float_exception_flags(&env->fp_status) & + float_flag_inexact; set_float_rounding_mode(rounding_mode, &env->fp_status); farg.ll = float64_round_to_int(farg.d, &env->fp_status); /* Restore rounding mode from FPSCR */ fpscr_set_rounding_mode(env); + + /* fri* does not set FPSCR[XX] */ + if (!inexact) { + env->fp_status.float_exception_flags &= ~float_flag_inexact; + } } + helper_float_check_status(env); return farg.ll; } uint64_t helper_frin(CPUPPCState *env, uint64_t arg) { - return do_fri(env, arg, float_round_nearest_even); + return do_fri(env, arg, float_round_ties_away); } uint64_t helper_friz(CPUPPCState *env, uint64_t arg) @@ -754,13 +731,13 @@ uint64_t helper_fmadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2, if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d) || float64_is_signaling_nan(farg3.d))) { /* sNaN operation */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; @@ -772,7 +749,7 @@ uint64_t helper_fmadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2, float64_is_infinity(farg3.d) && float128_is_neg(ft0_128) != float64_is_neg(farg3.d))) { /* Magnitude subtraction of infinities */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); } else { ft1_128 = float64_to_float128(farg3.d, &env->fp_status); ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status); @@ -797,13 +774,13 @@ uint64_t helper_fmsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2, (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d) || float64_is_signaling_nan(farg3.d))) { /* sNaN operation */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; @@ -815,7 +792,7 @@ uint64_t helper_fmsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2, float64_is_infinity(farg3.d) && float128_is_neg(ft0_128) == float64_is_neg(farg3.d))) { /* Magnitude subtraction of infinities */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); } else { ft1_128 = float64_to_float128(farg3.d, &env->fp_status); ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status); @@ -838,13 +815,13 @@ uint64_t helper_fnmadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2, if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d) || float64_is_signaling_nan(farg3.d))) { /* sNaN operation */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; @@ -856,7 +833,7 @@ uint64_t helper_fnmadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2, float64_is_infinity(farg3.d) && float128_is_neg(ft0_128) != float64_is_neg(farg3.d))) { /* Magnitude subtraction of infinities */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); } else { ft1_128 = float64_to_float128(farg3.d, &env->fp_status); ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status); @@ -883,13 +860,13 @@ uint64_t helper_fnmsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2, (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d) || float64_is_signaling_nan(farg3.d))) { /* sNaN operation */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; @@ -901,7 +878,7 @@ uint64_t helper_fnmsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2, float64_is_infinity(farg3.d) && float128_is_neg(ft0_128) == float64_is_neg(farg3.d))) { /* Magnitude subtraction of infinities */ - farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); } else { ft1_128 = float64_to_float128(farg3.d, &env->fp_status); ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status); @@ -924,7 +901,7 @@ uint64_t helper_frsp(CPUPPCState *env, uint64_t arg) if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN square root */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } f32 = float64_to_float32(farg.d, &env->fp_status); farg.d = float32_to_float64(f32, &env->fp_status); @@ -941,11 +918,11 @@ uint64_t helper_fsqrt(CPUPPCState *env, uint64_t arg) if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { /* Square root of a negative nonzero number */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT); + farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1); } else { if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN square root */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } farg.d = float64_sqrt(farg.d, &env->fp_status); } @@ -961,7 +938,7 @@ uint64_t helper_fre(CPUPPCState *env, uint64_t arg) if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN reciprocal */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } farg.d = float64_div(float64_one, farg.d, &env->fp_status); return farg.d; @@ -977,7 +954,7 @@ uint64_t helper_fres(CPUPPCState *env, uint64_t arg) if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN reciprocal */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } farg.d = float64_div(float64_one, farg.d, &env->fp_status); f32 = float64_to_float32(farg.d, &env->fp_status); @@ -996,11 +973,11 @@ uint64_t helper_frsqrte(CPUPPCState *env, uint64_t arg) if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { /* Reciprocal square root of a negative nonzero number */ - farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT); + farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1); } else { if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN reciprocal square root */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } farg.d = float64_sqrt(farg.d, &env->fp_status); farg.d = float64_div(float64_one, farg.d, &env->fp_status); @@ -1026,6 +1003,73 @@ uint64_t helper_fsel(CPUPPCState *env, uint64_t arg1, uint64_t arg2, } } +uint32_t helper_ftdiv(uint64_t fra, uint64_t frb) +{ + int fe_flag = 0; + int fg_flag = 0; + + if (unlikely(float64_is_infinity(fra) || + float64_is_infinity(frb) || + float64_is_zero(frb))) { + fe_flag = 1; + fg_flag = 1; + } else { + int e_a = ppc_float64_get_unbiased_exp(fra); + int e_b = ppc_float64_get_unbiased_exp(frb); + + if (unlikely(float64_is_any_nan(fra) || + float64_is_any_nan(frb))) { + fe_flag = 1; + } else if ((e_b <= -1022) || (e_b >= 1021)) { + fe_flag = 1; + } else if (!float64_is_zero(fra) && + (((e_a - e_b) >= 1023) || + ((e_a - e_b) <= -1021) || + (e_a <= -970))) { + fe_flag = 1; + } + + if (unlikely(float64_is_zero_or_denormal(frb))) { + /* XB is not zero because of the above check and */ + /* so must be denormalized. */ + fg_flag = 1; + } + } + + return 0x8 | (fg_flag ? 4 : 0) | (fe_flag ? 2 : 0); +} + +uint32_t helper_ftsqrt(uint64_t frb) +{ + int fe_flag = 0; + int fg_flag = 0; + + if (unlikely(float64_is_infinity(frb) || float64_is_zero(frb))) { + fe_flag = 1; + fg_flag = 1; + } else { + int e_b = ppc_float64_get_unbiased_exp(frb); + + if (unlikely(float64_is_any_nan(frb))) { + fe_flag = 1; + } else if (unlikely(float64_is_zero(frb))) { + fe_flag = 1; + } else if (unlikely(float64_is_neg(frb))) { + fe_flag = 1; + } else if (!float64_is_zero(frb) && (e_b <= (-1022+52))) { + fe_flag = 1; + } + + if (unlikely(float64_is_zero_or_denormal(frb))) { + /* XB is not zero because of the above check and */ + /* therefore must be denormalized. */ + fg_flag = 1; + } + } + + return 0x8 | (fg_flag ? 4 : 0) | (fe_flag ? 2 : 0); +} + void helper_fcmpu(CPUPPCState *env, uint64_t arg1, uint64_t arg2, uint32_t crfD) { @@ -1053,7 +1097,7 @@ void helper_fcmpu(CPUPPCState *env, uint64_t arg1, uint64_t arg2, && (float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d)))) { /* sNaN comparison */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } } @@ -1085,10 +1129,10 @@ void helper_fcmpo(CPUPPCState *env, uint64_t arg1, uint64_t arg2, float64_is_signaling_nan(farg2.d)) { /* sNaN comparison */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | - POWERPC_EXCP_FP_VXVC); + POWERPC_EXCP_FP_VXVC, 1); } else { /* qNaN comparison */ - fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC); + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 1); } } } @@ -1710,3 +1754,969 @@ uint32_t helper_efdcmpeq(CPUPPCState *env, uint64_t op1, uint64_t op2) /* XXX: TODO: test special values (NaN, infinites, ...) */ return helper_efdtsteq(env, op1, op2); } + +#define DECODE_SPLIT(opcode, shift1, nb1, shift2, nb2) \ + (((((opcode) >> (shift1)) & ((1 << (nb1)) - 1)) << nb2) | \ + (((opcode) >> (shift2)) & ((1 << (nb2)) - 1))) + +#define xT(opcode) DECODE_SPLIT(opcode, 0, 1, 21, 5) +#define xA(opcode) DECODE_SPLIT(opcode, 2, 1, 16, 5) +#define xB(opcode) DECODE_SPLIT(opcode, 1, 1, 11, 5) +#define xC(opcode) DECODE_SPLIT(opcode, 3, 1, 6, 5) +#define BF(opcode) (((opcode) >> (31-8)) & 7) + +typedef union _ppc_vsr_t { + uint64_t u64[2]; + uint32_t u32[4]; + float32 f32[4]; + float64 f64[2]; +} ppc_vsr_t; + +static void getVSR(int n, ppc_vsr_t *vsr, CPUPPCState *env) +{ + if (n < 32) { + vsr->f64[0] = env->fpr[n]; + vsr->u64[1] = env->vsr[n]; + } else { + vsr->u64[0] = env->avr[n-32].u64[0]; + vsr->u64[1] = env->avr[n-32].u64[1]; + } +} + +static void putVSR(int n, ppc_vsr_t *vsr, CPUPPCState *env) +{ + if (n < 32) { + env->fpr[n] = vsr->f64[0]; + env->vsr[n] = vsr->u64[1]; + } else { + env->avr[n-32].u64[0] = vsr->u64[0]; + env->avr[n-32].u64[1] = vsr->u64[1]; + } +} + +#define float64_to_float64(x, env) x + + +/* VSX_ADD_SUB - VSX floating point add/subract + * name - instruction mnemonic + * op - operation (add or sub) + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * sfprf - set FPRF + */ +#define VSX_ADD_SUB(name, op, nels, tp, fld, sfprf, r2sp) \ +void helper_##name(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xa, xb; \ + int i; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + float_status tstat = env->fp_status; \ + set_float_exception_flags(0, &tstat); \ + xt.fld[i] = tp##_##op(xa.fld[i], xb.fld[i], &tstat); \ + env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ + \ + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ + if (tp##_is_infinity(xa.fld[i]) && tp##_is_infinity(xb.fld[i])) {\ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, sfprf); \ + } else if (tp##_is_signaling_nan(xa.fld[i]) || \ + tp##_is_signaling_nan(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + } \ + } \ + \ + if (r2sp) { \ + xt.fld[i] = helper_frsp(env, xt.fld[i]); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld[i], sfprf); \ + } \ + } \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_ADD_SUB(xsadddp, add, 1, float64, f64, 1, 0) +VSX_ADD_SUB(xsaddsp, add, 1, float64, f64, 1, 1) +VSX_ADD_SUB(xvadddp, add, 2, float64, f64, 0, 0) +VSX_ADD_SUB(xvaddsp, add, 4, float32, f32, 0, 0) +VSX_ADD_SUB(xssubdp, sub, 1, float64, f64, 1, 0) +VSX_ADD_SUB(xssubsp, sub, 1, float64, f64, 1, 1) +VSX_ADD_SUB(xvsubdp, sub, 2, float64, f64, 0, 0) +VSX_ADD_SUB(xvsubsp, sub, 4, float32, f32, 0, 0) + +/* VSX_MUL - VSX floating point multiply + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * sfprf - set FPRF + */ +#define VSX_MUL(op, nels, tp, fld, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xa, xb; \ + int i; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + float_status tstat = env->fp_status; \ + set_float_exception_flags(0, &tstat); \ + xt.fld[i] = tp##_mul(xa.fld[i], xb.fld[i], &tstat); \ + env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ + \ + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ + if ((tp##_is_infinity(xa.fld[i]) && tp##_is_zero(xb.fld[i])) || \ + (tp##_is_infinity(xb.fld[i]) && tp##_is_zero(xa.fld[i]))) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, sfprf); \ + } else if (tp##_is_signaling_nan(xa.fld[i]) || \ + tp##_is_signaling_nan(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + } \ + } \ + \ + if (r2sp) { \ + xt.fld[i] = helper_frsp(env, xt.fld[i]); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld[i], sfprf); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_MUL(xsmuldp, 1, float64, f64, 1, 0) +VSX_MUL(xsmulsp, 1, float64, f64, 1, 1) +VSX_MUL(xvmuldp, 2, float64, f64, 0, 0) +VSX_MUL(xvmulsp, 4, float32, f32, 0, 0) + +/* VSX_DIV - VSX floating point divide + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * sfprf - set FPRF + */ +#define VSX_DIV(op, nels, tp, fld, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xa, xb; \ + int i; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + float_status tstat = env->fp_status; \ + set_float_exception_flags(0, &tstat); \ + xt.fld[i] = tp##_div(xa.fld[i], xb.fld[i], &tstat); \ + env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ + \ + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ + if (tp##_is_infinity(xa.fld[i]) && tp##_is_infinity(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI, sfprf); \ + } else if (tp##_is_zero(xa.fld[i]) && \ + tp##_is_zero(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ, sfprf); \ + } else if (tp##_is_signaling_nan(xa.fld[i]) || \ + tp##_is_signaling_nan(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + } \ + } \ + \ + if (r2sp) { \ + xt.fld[i] = helper_frsp(env, xt.fld[i]); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld[i], sfprf); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_DIV(xsdivdp, 1, float64, f64, 1, 0) +VSX_DIV(xsdivsp, 1, float64, f64, 1, 1) +VSX_DIV(xvdivdp, 2, float64, f64, 0, 0) +VSX_DIV(xvdivsp, 4, float32, f32, 0, 0) + +/* VSX_RE - VSX floating point reciprocal estimate + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * sfprf - set FPRF + */ +#define VSX_RE(op, nels, tp, fld, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + if (unlikely(tp##_is_signaling_nan(xb.fld[i]))) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + } \ + xt.fld[i] = tp##_div(tp##_one, xb.fld[i], &env->fp_status); \ + \ + if (r2sp) { \ + xt.fld[i] = helper_frsp(env, xt.fld[i]); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld[0], sfprf); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_RE(xsredp, 1, float64, f64, 1, 0) +VSX_RE(xsresp, 1, float64, f64, 1, 1) +VSX_RE(xvredp, 2, float64, f64, 0, 0) +VSX_RE(xvresp, 4, float32, f32, 0, 0) + +/* VSX_SQRT - VSX floating point square root + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * sfprf - set FPRF + */ +#define VSX_SQRT(op, nels, tp, fld, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + float_status tstat = env->fp_status; \ + set_float_exception_flags(0, &tstat); \ + xt.fld[i] = tp##_sqrt(xb.fld[i], &tstat); \ + env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ + \ + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ + if (tp##_is_neg(xb.fld[i]) && !tp##_is_zero(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, sfprf); \ + } else if (tp##_is_signaling_nan(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + } \ + } \ + \ + if (r2sp) { \ + xt.fld[i] = helper_frsp(env, xt.fld[i]); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld[i], sfprf); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_SQRT(xssqrtdp, 1, float64, f64, 1, 0) +VSX_SQRT(xssqrtsp, 1, float64, f64, 1, 1) +VSX_SQRT(xvsqrtdp, 2, float64, f64, 0, 0) +VSX_SQRT(xvsqrtsp, 4, float32, f32, 0, 0) + +/* VSX_RSQRTE - VSX floating point reciprocal square root estimate + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * sfprf - set FPRF + */ +#define VSX_RSQRTE(op, nels, tp, fld, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + float_status tstat = env->fp_status; \ + set_float_exception_flags(0, &tstat); \ + xt.fld[i] = tp##_sqrt(xb.fld[i], &tstat); \ + xt.fld[i] = tp##_div(tp##_one, xt.fld[i], &tstat); \ + env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ + \ + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ + if (tp##_is_neg(xb.fld[i]) && !tp##_is_zero(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, sfprf); \ + } else if (tp##_is_signaling_nan(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + } \ + } \ + \ + if (r2sp) { \ + xt.fld[i] = helper_frsp(env, xt.fld[i]); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld[i], sfprf); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_RSQRTE(xsrsqrtedp, 1, float64, f64, 1, 0) +VSX_RSQRTE(xsrsqrtesp, 1, float64, f64, 1, 1) +VSX_RSQRTE(xvrsqrtedp, 2, float64, f64, 0, 0) +VSX_RSQRTE(xvrsqrtesp, 4, float32, f32, 0, 0) + +/* VSX_TDIV - VSX floating point test for divide + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * emin - minimum unbiased exponent + * emax - maximum unbiased exponent + * nbits - number of fraction bits + */ +#define VSX_TDIV(op, nels, tp, fld, emin, emax, nbits) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xa, xb; \ + int i; \ + int fe_flag = 0; \ + int fg_flag = 0; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + \ + for (i = 0; i < nels; i++) { \ + if (unlikely(tp##_is_infinity(xa.fld[i]) || \ + tp##_is_infinity(xb.fld[i]) || \ + tp##_is_zero(xb.fld[i]))) { \ + fe_flag = 1; \ + fg_flag = 1; \ + } else { \ + int e_a = ppc_##tp##_get_unbiased_exp(xa.fld[i]); \ + int e_b = ppc_##tp##_get_unbiased_exp(xb.fld[i]); \ + \ + if (unlikely(tp##_is_any_nan(xa.fld[i]) || \ + tp##_is_any_nan(xb.fld[i]))) { \ + fe_flag = 1; \ + } else if ((e_b <= emin) || (e_b >= (emax-2))) { \ + fe_flag = 1; \ + } else if (!tp##_is_zero(xa.fld[i]) && \ + (((e_a - e_b) >= emax) || \ + ((e_a - e_b) <= (emin+1)) || \ + (e_a <= (emin+nbits)))) { \ + fe_flag = 1; \ + } \ + \ + if (unlikely(tp##_is_zero_or_denormal(xb.fld[i]))) { \ + /* XB is not zero because of the above check and */ \ + /* so must be denormalized. */ \ + fg_flag = 1; \ + } \ + } \ + } \ + \ + env->crf[BF(opcode)] = 0x8 | (fg_flag ? 4 : 0) | (fe_flag ? 2 : 0); \ +} + +VSX_TDIV(xstdivdp, 1, float64, f64, -1022, 1023, 52) +VSX_TDIV(xvtdivdp, 2, float64, f64, -1022, 1023, 52) +VSX_TDIV(xvtdivsp, 4, float32, f32, -126, 127, 23) + +/* VSX_TSQRT - VSX floating point test for square root + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * emin - minimum unbiased exponent + * emax - maximum unbiased exponent + * nbits - number of fraction bits + */ +#define VSX_TSQRT(op, nels, tp, fld, emin, nbits) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xa, xb; \ + int i; \ + int fe_flag = 0; \ + int fg_flag = 0; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + \ + for (i = 0; i < nels; i++) { \ + if (unlikely(tp##_is_infinity(xb.fld[i]) || \ + tp##_is_zero(xb.fld[i]))) { \ + fe_flag = 1; \ + fg_flag = 1; \ + } else { \ + int e_b = ppc_##tp##_get_unbiased_exp(xb.fld[i]); \ + \ + if (unlikely(tp##_is_any_nan(xb.fld[i]))) { \ + fe_flag = 1; \ + } else if (unlikely(tp##_is_zero(xb.fld[i]))) { \ + fe_flag = 1; \ + } else if (unlikely(tp##_is_neg(xb.fld[i]))) { \ + fe_flag = 1; \ + } else if (!tp##_is_zero(xb.fld[i]) && \ + (e_b <= (emin+nbits))) { \ + fe_flag = 1; \ + } \ + \ + if (unlikely(tp##_is_zero_or_denormal(xb.fld[i]))) { \ + /* XB is not zero because of the above check and */ \ + /* therefore must be denormalized. */ \ + fg_flag = 1; \ + } \ + } \ + } \ + \ + env->crf[BF(opcode)] = 0x8 | (fg_flag ? 4 : 0) | (fe_flag ? 2 : 0); \ +} + +VSX_TSQRT(xstsqrtdp, 1, float64, f64, -1022, 52) +VSX_TSQRT(xvtsqrtdp, 2, float64, f64, -1022, 52) +VSX_TSQRT(xvtsqrtsp, 4, float32, f32, -126, 23) + +/* VSX_MADD - VSX floating point muliply/add variations + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * maddflgs - flags for the float*muladd routine that control the + * various forms (madd, msub, nmadd, nmsub) + * afrm - A form (1=A, 0=M) + * sfprf - set FPRF + */ +#define VSX_MADD(op, nels, tp, fld, maddflgs, afrm, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt_in, xa, xb, xt_out; \ + ppc_vsr_t *b, *c; \ + int i; \ + \ + if (afrm) { /* AxB + T */ \ + b = &xb; \ + c = &xt_in; \ + } else { /* AxT + B */ \ + b = &xt_in; \ + c = &xb; \ + } \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt_in, env); \ + \ + xt_out = xt_in; \ + \ + helper_reset_fpstatus(env); \ + \ + for (i = 0; i < nels; i++) { \ + float_status tstat = env->fp_status; \ + set_float_exception_flags(0, &tstat); \ + if (r2sp && (tstat.float_rounding_mode == float_round_nearest_even)) {\ + /* Avoid double rounding errors by rounding the intermediate */ \ + /* result to odd. */ \ + set_float_rounding_mode(float_round_to_zero, &tstat); \ + xt_out.fld[i] = tp##_muladd(xa.fld[i], b->fld[i], c->fld[i], \ + maddflgs, &tstat); \ + xt_out.fld[i] |= (get_float_exception_flags(&tstat) & \ + float_flag_inexact) != 0; \ + } else { \ + xt_out.fld[i] = tp##_muladd(xa.fld[i], b->fld[i], c->fld[i], \ + maddflgs, &tstat); \ + } \ + env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ + \ + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ + if (tp##_is_signaling_nan(xa.fld[i]) || \ + tp##_is_signaling_nan(b->fld[i]) || \ + tp##_is_signaling_nan(c->fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + tstat.float_exception_flags &= ~float_flag_invalid; \ + } \ + if ((tp##_is_infinity(xa.fld[i]) && tp##_is_zero(b->fld[i])) || \ + (tp##_is_zero(xa.fld[i]) && tp##_is_infinity(b->fld[i]))) { \ + xt_out.fld[i] = float64_to_##tp(fload_invalid_op_excp(env, \ + POWERPC_EXCP_FP_VXIMZ, sfprf), &env->fp_status); \ + tstat.float_exception_flags &= ~float_flag_invalid; \ + } \ + if ((tstat.float_exception_flags & float_flag_invalid) && \ + ((tp##_is_infinity(xa.fld[i]) || \ + tp##_is_infinity(b->fld[i])) && \ + tp##_is_infinity(c->fld[i]))) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, sfprf); \ + } \ + } \ + \ + if (r2sp) { \ + xt_out.fld[i] = helper_frsp(env, xt_out.fld[i]); \ + } \ + \ + if (sfprf) { \ + helper_compute_fprf(env, xt_out.fld[i], sfprf); \ + } \ + } \ + putVSR(xT(opcode), &xt_out, env); \ + helper_float_check_status(env); \ +} + +#define MADD_FLGS 0 +#define MSUB_FLGS float_muladd_negate_c +#define NMADD_FLGS float_muladd_negate_result +#define NMSUB_FLGS (float_muladd_negate_c | float_muladd_negate_result) + +VSX_MADD(xsmaddadp, 1, float64, f64, MADD_FLGS, 1, 1, 0) +VSX_MADD(xsmaddmdp, 1, float64, f64, MADD_FLGS, 0, 1, 0) +VSX_MADD(xsmsubadp, 1, float64, f64, MSUB_FLGS, 1, 1, 0) +VSX_MADD(xsmsubmdp, 1, float64, f64, MSUB_FLGS, 0, 1, 0) +VSX_MADD(xsnmaddadp, 1, float64, f64, NMADD_FLGS, 1, 1, 0) +VSX_MADD(xsnmaddmdp, 1, float64, f64, NMADD_FLGS, 0, 1, 0) +VSX_MADD(xsnmsubadp, 1, float64, f64, NMSUB_FLGS, 1, 1, 0) +VSX_MADD(xsnmsubmdp, 1, float64, f64, NMSUB_FLGS, 0, 1, 0) + +VSX_MADD(xsmaddasp, 1, float64, f64, MADD_FLGS, 1, 1, 1) +VSX_MADD(xsmaddmsp, 1, float64, f64, MADD_FLGS, 0, 1, 1) +VSX_MADD(xsmsubasp, 1, float64, f64, MSUB_FLGS, 1, 1, 1) +VSX_MADD(xsmsubmsp, 1, float64, f64, MSUB_FLGS, 0, 1, 1) +VSX_MADD(xsnmaddasp, 1, float64, f64, NMADD_FLGS, 1, 1, 1) +VSX_MADD(xsnmaddmsp, 1, float64, f64, NMADD_FLGS, 0, 1, 1) +VSX_MADD(xsnmsubasp, 1, float64, f64, NMSUB_FLGS, 1, 1, 1) +VSX_MADD(xsnmsubmsp, 1, float64, f64, NMSUB_FLGS, 0, 1, 1) + +VSX_MADD(xvmaddadp, 2, float64, f64, MADD_FLGS, 1, 0, 0) +VSX_MADD(xvmaddmdp, 2, float64, f64, MADD_FLGS, 0, 0, 0) +VSX_MADD(xvmsubadp, 2, float64, f64, MSUB_FLGS, 1, 0, 0) +VSX_MADD(xvmsubmdp, 2, float64, f64, MSUB_FLGS, 0, 0, 0) +VSX_MADD(xvnmaddadp, 2, float64, f64, NMADD_FLGS, 1, 0, 0) +VSX_MADD(xvnmaddmdp, 2, float64, f64, NMADD_FLGS, 0, 0, 0) +VSX_MADD(xvnmsubadp, 2, float64, f64, NMSUB_FLGS, 1, 0, 0) +VSX_MADD(xvnmsubmdp, 2, float64, f64, NMSUB_FLGS, 0, 0, 0) + +VSX_MADD(xvmaddasp, 4, float32, f32, MADD_FLGS, 1, 0, 0) +VSX_MADD(xvmaddmsp, 4, float32, f32, MADD_FLGS, 0, 0, 0) +VSX_MADD(xvmsubasp, 4, float32, f32, MSUB_FLGS, 1, 0, 0) +VSX_MADD(xvmsubmsp, 4, float32, f32, MSUB_FLGS, 0, 0, 0) +VSX_MADD(xvnmaddasp, 4, float32, f32, NMADD_FLGS, 1, 0, 0) +VSX_MADD(xvnmaddmsp, 4, float32, f32, NMADD_FLGS, 0, 0, 0) +VSX_MADD(xvnmsubasp, 4, float32, f32, NMSUB_FLGS, 1, 0, 0) +VSX_MADD(xvnmsubmsp, 4, float32, f32, NMSUB_FLGS, 0, 0, 0) + +#define VSX_SCALAR_CMP(op, ordered) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xa, xb; \ + uint32_t cc = 0; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + \ + if (unlikely(float64_is_any_nan(xa.f64[0]) || \ + float64_is_any_nan(xb.f64[0]))) { \ + if (float64_is_signaling_nan(xa.f64[0]) || \ + float64_is_signaling_nan(xb.f64[0])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + } \ + if (ordered) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0); \ + } \ + cc = 1; \ + } else { \ + if (float64_lt(xa.f64[0], xb.f64[0], &env->fp_status)) { \ + cc = 8; \ + } else if (!float64_le(xa.f64[0], xb.f64[0], &env->fp_status)) { \ + cc = 4; \ + } else { \ + cc = 2; \ + } \ + } \ + \ + env->fpscr &= ~(0x0F << FPSCR_FPRF); \ + env->fpscr |= cc << FPSCR_FPRF; \ + env->crf[BF(opcode)] = cc; \ + \ + helper_float_check_status(env); \ +} + +VSX_SCALAR_CMP(xscmpodp, 1) +VSX_SCALAR_CMP(xscmpudp, 0) + +#define float64_snan_to_qnan(x) ((x) | 0x0008000000000000ULL) +#define float32_snan_to_qnan(x) ((x) | 0x00400000) + +/* VSX_MAX_MIN - VSX floating point maximum/minimum + * name - instruction mnemonic + * op - operation (max or min) + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + */ +#define VSX_MAX_MIN(name, op, nels, tp, fld) \ +void helper_##name(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xa, xb; \ + int i; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + for (i = 0; i < nels; i++) { \ + xt.fld[i] = tp##_##op(xa.fld[i], xb.fld[i], &env->fp_status); \ + if (unlikely(tp##_is_signaling_nan(xa.fld[i]) || \ + tp##_is_signaling_nan(xb.fld[i]))) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_MAX_MIN(xsmaxdp, maxnum, 1, float64, f64) +VSX_MAX_MIN(xvmaxdp, maxnum, 2, float64, f64) +VSX_MAX_MIN(xvmaxsp, maxnum, 4, float32, f32) +VSX_MAX_MIN(xsmindp, minnum, 1, float64, f64) +VSX_MAX_MIN(xvmindp, minnum, 2, float64, f64) +VSX_MAX_MIN(xvminsp, minnum, 4, float32, f32) + +/* VSX_CMP - VSX floating point compare + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * cmp - comparison operation + * svxvc - set VXVC bit + */ +#define VSX_CMP(op, nels, tp, fld, cmp, svxvc) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xa, xb; \ + int i; \ + int all_true = 1; \ + int all_false = 1; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + for (i = 0; i < nels; i++) { \ + if (unlikely(tp##_is_any_nan(xa.fld[i]) || \ + tp##_is_any_nan(xb.fld[i]))) { \ + if (tp##_is_signaling_nan(xa.fld[i]) || \ + tp##_is_signaling_nan(xb.fld[i])) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + } \ + if (svxvc) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0); \ + } \ + xt.fld[i] = 0; \ + all_true = 0; \ + } else { \ + if (tp##_##cmp(xb.fld[i], xa.fld[i], &env->fp_status) == 1) { \ + xt.fld[i] = -1; \ + all_false = 0; \ + } else { \ + xt.fld[i] = 0; \ + all_true = 0; \ + } \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + if ((opcode >> (31-21)) & 1) { \ + env->crf[6] = (all_true ? 0x8 : 0) | (all_false ? 0x2 : 0); \ + } \ + helper_float_check_status(env); \ + } + +VSX_CMP(xvcmpeqdp, 2, float64, f64, eq, 0) +VSX_CMP(xvcmpgedp, 2, float64, f64, le, 1) +VSX_CMP(xvcmpgtdp, 2, float64, f64, lt, 1) +VSX_CMP(xvcmpeqsp, 4, float32, f32, eq, 0) +VSX_CMP(xvcmpgesp, 4, float32, f32, le, 1) +VSX_CMP(xvcmpgtsp, 4, float32, f32, lt, 1) + +#if defined(HOST_WORDS_BIGENDIAN) +#define JOFFSET 0 +#else +#define JOFFSET 1 +#endif + +/* VSX_CVT_FP_TO_FP - VSX floating point/floating point conversion + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * stp - source type (float32 or float64) + * ttp - target type (float32 or float64) + * sfld - source vsr_t field + * tfld - target vsr_t field (f32 or f64) + * sfprf - set FPRF + */ +#define VSX_CVT_FP_TO_FP(op, nels, stp, ttp, sfld, tfld, sfprf) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + for (i = 0; i < nels; i++) { \ + int j = 2*i + JOFFSET; \ + xt.tfld = stp##_to_##ttp(xb.sfld, &env->fp_status); \ + if (unlikely(stp##_is_signaling_nan(xb.sfld))) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + xt.tfld = ttp##_snan_to_qnan(xt.tfld); \ + } \ + if (sfprf) { \ + helper_compute_fprf(env, ttp##_to_float64(xt.tfld, \ + &env->fp_status), sfprf); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_CVT_FP_TO_FP(xscvdpsp, 1, float64, float32, f64[i], f32[j], 1) +VSX_CVT_FP_TO_FP(xscvspdp, 1, float32, float64, f32[j], f64[i], 1) +VSX_CVT_FP_TO_FP(xvcvdpsp, 2, float64, float32, f64[i], f32[j], 0) +VSX_CVT_FP_TO_FP(xvcvspdp, 2, float32, float64, f32[j], f64[i], 0) + +uint64_t helper_xscvdpspn(CPUPPCState *env, uint64_t xb) +{ + float_status tstat = env->fp_status; + set_float_exception_flags(0, &tstat); + + return (uint64_t)float64_to_float32(xb, &tstat) << 32; +} + +uint64_t helper_xscvspdpn(CPUPPCState *env, uint64_t xb) +{ + float_status tstat = env->fp_status; + set_float_exception_flags(0, &tstat); + + return float32_to_float64(xb >> 32, &tstat); +} + +/* VSX_CVT_FP_TO_INT - VSX floating point to integer conversion + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * stp - source type (float32 or float64) + * ttp - target type (int32, uint32, int64 or uint64) + * sfld - source vsr_t field + * tfld - target vsr_t field + * jdef - definition of the j index (i or 2*i) + * rnan - resulting NaN + */ +#define VSX_CVT_FP_TO_INT(op, nels, stp, ttp, sfld, tfld, jdef, rnan) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + for (i = 0; i < nels; i++) { \ + int j = jdef; \ + if (unlikely(stp##_is_any_nan(xb.sfld))) { \ + if (stp##_is_signaling_nan(xb.sfld)) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + } \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 0); \ + xt.tfld = rnan; \ + } else { \ + xt.tfld = stp##_to_##ttp(xb.sfld, &env->fp_status); \ + if (env->fp_status.float_exception_flags & float_flag_invalid) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 0); \ + } \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_CVT_FP_TO_INT(xscvdpsxds, 1, float64, int64, f64[j], u64[i], i, \ + 0x8000000000000000ULL) +VSX_CVT_FP_TO_INT(xscvdpsxws, 1, float64, int32, f64[i], u32[j], \ + 2*i + JOFFSET, 0x80000000U) +VSX_CVT_FP_TO_INT(xscvdpuxds, 1, float64, uint64, f64[j], u64[i], i, 0ULL) +VSX_CVT_FP_TO_INT(xscvdpuxws, 1, float64, uint32, f64[i], u32[j], \ + 2*i + JOFFSET, 0U) +VSX_CVT_FP_TO_INT(xvcvdpsxds, 2, float64, int64, f64[j], u64[i], i, \ + 0x8000000000000000ULL) +VSX_CVT_FP_TO_INT(xvcvdpsxws, 2, float64, int32, f64[i], u32[j], \ + 2*i + JOFFSET, 0x80000000U) +VSX_CVT_FP_TO_INT(xvcvdpuxds, 2, float64, uint64, f64[j], u64[i], i, 0ULL) +VSX_CVT_FP_TO_INT(xvcvdpuxws, 2, float64, uint32, f64[i], u32[j], \ + 2*i + JOFFSET, 0U) +VSX_CVT_FP_TO_INT(xvcvspsxds, 2, float32, int64, f32[j], u64[i], \ + 2*i + JOFFSET, 0x8000000000000000ULL) +VSX_CVT_FP_TO_INT(xvcvspsxws, 4, float32, int32, f32[j], u32[j], i, \ + 0x80000000U) +VSX_CVT_FP_TO_INT(xvcvspuxds, 2, float32, uint64, f32[j], u64[i], \ + 2*i + JOFFSET, 0ULL) +VSX_CVT_FP_TO_INT(xvcvspuxws, 4, float32, uint32, f32[j], u32[i], i, 0U) + +/* VSX_CVT_INT_TO_FP - VSX integer to floating point conversion + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * stp - source type (int32, uint32, int64 or uint64) + * ttp - target type (float32 or float64) + * sfld - source vsr_t field + * tfld - target vsr_t field + * jdef - definition of the j index (i or 2*i) + * sfprf - set FPRF + */ +#define VSX_CVT_INT_TO_FP(op, nels, stp, ttp, sfld, tfld, jdef, sfprf, r2sp) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + for (i = 0; i < nels; i++) { \ + int j = jdef; \ + xt.tfld = stp##_to_##ttp(xb.sfld, &env->fp_status); \ + if (r2sp) { \ + xt.tfld = helper_frsp(env, xt.tfld); \ + } \ + if (sfprf) { \ + helper_compute_fprf(env, xt.tfld, sfprf); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_CVT_INT_TO_FP(xscvsxddp, 1, int64, float64, u64[j], f64[i], i, 1, 0) +VSX_CVT_INT_TO_FP(xscvuxddp, 1, uint64, float64, u64[j], f64[i], i, 1, 0) +VSX_CVT_INT_TO_FP(xscvsxdsp, 1, int64, float64, u64[j], f64[i], i, 1, 1) +VSX_CVT_INT_TO_FP(xscvuxdsp, 1, uint64, float64, u64[j], f64[i], i, 1, 1) +VSX_CVT_INT_TO_FP(xvcvsxddp, 2, int64, float64, u64[j], f64[i], i, 0, 0) +VSX_CVT_INT_TO_FP(xvcvuxddp, 2, uint64, float64, u64[j], f64[i], i, 0, 0) +VSX_CVT_INT_TO_FP(xvcvsxwdp, 2, int32, float64, u32[j], f64[i], \ + 2*i + JOFFSET, 0, 0) +VSX_CVT_INT_TO_FP(xvcvuxwdp, 2, uint64, float64, u32[j], f64[i], \ + 2*i + JOFFSET, 0, 0) +VSX_CVT_INT_TO_FP(xvcvsxdsp, 2, int64, float32, u64[i], f32[j], \ + 2*i + JOFFSET, 0, 0) +VSX_CVT_INT_TO_FP(xvcvuxdsp, 2, uint64, float32, u64[i], f32[j], \ + 2*i + JOFFSET, 0, 0) +VSX_CVT_INT_TO_FP(xvcvsxwsp, 4, int32, float32, u32[j], f32[i], i, 0, 0) +VSX_CVT_INT_TO_FP(xvcvuxwsp, 4, uint32, float32, u32[j], f32[i], i, 0, 0) + +/* For "use current rounding mode", define a value that will not be one of + * the existing rounding model enums. + */ +#define FLOAT_ROUND_CURRENT (float_round_nearest_even + float_round_down + \ + float_round_up + float_round_to_zero) + +/* VSX_ROUND - VSX floating point round + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * tp - type (float32 or float64) + * fld - vsr_t field (f32 or f64) + * rmode - rounding mode + * sfprf - set FPRF + */ +#define VSX_ROUND(op, nels, tp, fld, rmode, sfprf) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + if (rmode != FLOAT_ROUND_CURRENT) { \ + set_float_rounding_mode(rmode, &env->fp_status); \ + } \ + \ + for (i = 0; i < nels; i++) { \ + if (unlikely(tp##_is_signaling_nan(xb.fld[i]))) { \ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + xt.fld[i] = tp##_snan_to_qnan(xb.fld[i]); \ + } else { \ + xt.fld[i] = tp##_round_to_int(xb.fld[i], &env->fp_status); \ + } \ + if (sfprf) { \ + helper_compute_fprf(env, xt.fld[i], sfprf); \ + } \ + } \ + \ + /* If this is not a "use current rounding mode" instruction, \ + * then inhibit setting of the XX bit and restore rounding \ + * mode from FPSCR */ \ + if (rmode != FLOAT_ROUND_CURRENT) { \ + fpscr_set_rounding_mode(env); \ + env->fp_status.float_exception_flags &= ~float_flag_inexact; \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_ROUND(xsrdpi, 1, float64, f64, float_round_nearest_even, 1) +VSX_ROUND(xsrdpic, 1, float64, f64, FLOAT_ROUND_CURRENT, 1) +VSX_ROUND(xsrdpim, 1, float64, f64, float_round_down, 1) +VSX_ROUND(xsrdpip, 1, float64, f64, float_round_up, 1) +VSX_ROUND(xsrdpiz, 1, float64, f64, float_round_to_zero, 1) + +VSX_ROUND(xvrdpi, 2, float64, f64, float_round_nearest_even, 0) +VSX_ROUND(xvrdpic, 2, float64, f64, FLOAT_ROUND_CURRENT, 0) +VSX_ROUND(xvrdpim, 2, float64, f64, float_round_down, 0) +VSX_ROUND(xvrdpip, 2, float64, f64, float_round_up, 0) +VSX_ROUND(xvrdpiz, 2, float64, f64, float_round_to_zero, 0) + +VSX_ROUND(xvrspi, 4, float32, f32, float_round_nearest_even, 0) +VSX_ROUND(xvrspic, 4, float32, f32, FLOAT_ROUND_CURRENT, 0) +VSX_ROUND(xvrspim, 4, float32, f32, float_round_down, 0) +VSX_ROUND(xvrspip, 4, float32, f32, float_round_up, 0) +VSX_ROUND(xvrspiz, 4, float32, f32, float_round_to_zero, 0) + +uint64_t helper_xsrsp(CPUPPCState *env, uint64_t xb) +{ + helper_reset_fpstatus(env); + + uint64_t xt = helper_frsp(env, xb); + + helper_compute_fprf(env, xt, 1); + helper_float_check_status(env); + return xt; +} diff --git a/target-ppc/helper.h b/target-ppc/helper.h index 6d282bb32d..99f10deee1 100644 --- a/target-ppc/helper.h +++ b/target-ppc/helper.h @@ -31,7 +31,11 @@ DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32) #if defined(TARGET_PPC64) DEF_HELPER_3(mulldo, i64, env, i64, i64) +DEF_HELPER_4(divdeu, i64, env, i64, i64, i32) +DEF_HELPER_4(divde, i64, env, i64, i64, i32) #endif +DEF_HELPER_4(divweu, tl, env, tl, tl, i32) +DEF_HELPER_4(divwe, tl, env, tl, tl, i32) DEF_HELPER_FLAGS_1(cntlzw, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_1(popcntb, TCG_CALL_NO_RWG_SE, tl, tl) @@ -41,6 +45,7 @@ DEF_HELPER_3(sraw, tl, env, tl, tl) #if defined(TARGET_PPC64) DEF_HELPER_FLAGS_1(cntlzd, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_1(popcntd, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_2(bpermd, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_3(srad, tl, env, tl, tl) #endif @@ -61,11 +66,18 @@ DEF_HELPER_4(fcmpo, void, env, i64, i64, i32) DEF_HELPER_4(fcmpu, void, env, i64, i64, i32) DEF_HELPER_2(fctiw, i64, env, i64) +DEF_HELPER_2(fctiwu, i64, env, i64) DEF_HELPER_2(fctiwz, i64, env, i64) +DEF_HELPER_2(fctiwuz, i64, env, i64) #if defined(TARGET_PPC64) DEF_HELPER_2(fcfid, i64, env, i64) +DEF_HELPER_2(fcfidu, i64, env, i64) +DEF_HELPER_2(fcfids, i64, env, i64) +DEF_HELPER_2(fcfidus, i64, env, i64) DEF_HELPER_2(fctid, i64, env, i64) +DEF_HELPER_2(fctidu, i64, env, i64) DEF_HELPER_2(fctidz, i64, env, i64) +DEF_HELPER_2(fctiduz, i64, env, i64) #endif DEF_HELPER_2(frsp, i64, env, i64) DEF_HELPER_2(frin, i64, env, i64) @@ -87,6 +99,9 @@ DEF_HELPER_2(fres, i64, env, i64) DEF_HELPER_2(frsqrte, i64, env, i64) DEF_HELPER_4(fsel, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_2(ftdiv, TCG_CALL_NO_RWG_SE, i32, i64, i64) +DEF_HELPER_FLAGS_1(ftsqrt, TCG_CALL_NO_RWG_SE, i32, i64) + #define dh_alias_avr ptr #define dh_ctype_avr ppc_avr_t * #define dh_is_signed_avr dh_is_signed_ptr @@ -94,9 +109,11 @@ DEF_HELPER_4(fsel, i64, env, i64, i64, i64) DEF_HELPER_3(vaddubm, void, avr, avr, avr) DEF_HELPER_3(vadduhm, void, avr, avr, avr) DEF_HELPER_3(vadduwm, void, avr, avr, avr) +DEF_HELPER_3(vaddudm, void, avr, avr, avr) DEF_HELPER_3(vsububm, void, avr, avr, avr) DEF_HELPER_3(vsubuhm, void, avr, avr, avr) DEF_HELPER_3(vsubuwm, void, avr, avr, avr) +DEF_HELPER_3(vsubudm, void, avr, avr, avr) DEF_HELPER_3(vavgub, void, avr, avr, avr) DEF_HELPER_3(vavguh, void, avr, avr, avr) DEF_HELPER_3(vavguw, void, avr, avr, avr) @@ -106,24 +123,31 @@ DEF_HELPER_3(vavgsw, void, avr, avr, avr) DEF_HELPER_3(vminsb, void, avr, avr, avr) DEF_HELPER_3(vminsh, void, avr, avr, avr) DEF_HELPER_3(vminsw, void, avr, avr, avr) +DEF_HELPER_3(vminsd, void, avr, avr, avr) DEF_HELPER_3(vmaxsb, void, avr, avr, avr) DEF_HELPER_3(vmaxsh, void, avr, avr, avr) DEF_HELPER_3(vmaxsw, void, avr, avr, avr) +DEF_HELPER_3(vmaxsd, void, avr, avr, avr) DEF_HELPER_3(vminub, void, avr, avr, avr) DEF_HELPER_3(vminuh, void, avr, avr, avr) DEF_HELPER_3(vminuw, void, avr, avr, avr) +DEF_HELPER_3(vminud, void, avr, avr, avr) DEF_HELPER_3(vmaxub, void, avr, avr, avr) DEF_HELPER_3(vmaxuh, void, avr, avr, avr) DEF_HELPER_3(vmaxuw, void, avr, avr, avr) +DEF_HELPER_3(vmaxud, void, avr, avr, avr) DEF_HELPER_4(vcmpequb, void, env, avr, avr, avr) DEF_HELPER_4(vcmpequh, void, env, avr, avr, avr) DEF_HELPER_4(vcmpequw, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpequd, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtub, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtuh, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtuw, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtud, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtsb, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtsh, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtsw, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtsd, void, env, avr, avr, avr) DEF_HELPER_4(vcmpeqfp, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgefp, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtfp, void, env, avr, avr, avr) @@ -131,12 +155,15 @@ DEF_HELPER_4(vcmpbfp, void, env, avr, avr, avr) DEF_HELPER_4(vcmpequb_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpequh_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpequw_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpequd_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtub_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtuh_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtuw_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtud_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtsb_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtsh_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtsw_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtsd_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpeqfp_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgefp_dot, void, env, avr, avr, avr) DEF_HELPER_4(vcmpgtfp_dot, void, env, avr, avr, avr) @@ -149,21 +176,29 @@ DEF_HELPER_3(vmrghh, void, avr, avr, avr) DEF_HELPER_3(vmrghw, void, avr, avr, avr) DEF_HELPER_3(vmulesb, void, avr, avr, avr) DEF_HELPER_3(vmulesh, void, avr, avr, avr) +DEF_HELPER_3(vmulesw, void, avr, avr, avr) DEF_HELPER_3(vmuleub, void, avr, avr, avr) DEF_HELPER_3(vmuleuh, void, avr, avr, avr) +DEF_HELPER_3(vmuleuw, void, avr, avr, avr) DEF_HELPER_3(vmulosb, void, avr, avr, avr) DEF_HELPER_3(vmulosh, void, avr, avr, avr) +DEF_HELPER_3(vmulosw, void, avr, avr, avr) DEF_HELPER_3(vmuloub, void, avr, avr, avr) DEF_HELPER_3(vmulouh, void, avr, avr, avr) +DEF_HELPER_3(vmulouw, void, avr, avr, avr) +DEF_HELPER_3(vmuluwm, void, avr, avr, avr) DEF_HELPER_3(vsrab, void, avr, avr, avr) DEF_HELPER_3(vsrah, void, avr, avr, avr) DEF_HELPER_3(vsraw, void, avr, avr, avr) +DEF_HELPER_3(vsrad, void, avr, avr, avr) DEF_HELPER_3(vsrb, void, avr, avr, avr) DEF_HELPER_3(vsrh, void, avr, avr, avr) DEF_HELPER_3(vsrw, void, avr, avr, avr) +DEF_HELPER_3(vsrd, void, avr, avr, avr) DEF_HELPER_3(vslb, void, avr, avr, avr) DEF_HELPER_3(vslh, void, avr, avr, avr) DEF_HELPER_3(vslw, void, avr, avr, avr) +DEF_HELPER_3(vsld, void, avr, avr, avr) DEF_HELPER_3(vslo, void, avr, avr, avr) DEF_HELPER_3(vsro, void, avr, avr, avr) DEF_HELPER_3(vaddcuw, void, avr, avr, avr) @@ -182,9 +217,18 @@ DEF_HELPER_4(vadduws, void, env, avr, avr, avr) DEF_HELPER_4(vsububs, void, env, avr, avr, avr) DEF_HELPER_4(vsubuhs, void, env, avr, avr, avr) DEF_HELPER_4(vsubuws, void, env, avr, avr, avr) +DEF_HELPER_3(vadduqm, void, avr, avr, avr) +DEF_HELPER_4(vaddecuq, void, avr, avr, avr, avr) +DEF_HELPER_4(vaddeuqm, void, avr, avr, avr, avr) +DEF_HELPER_3(vaddcuq, void, avr, avr, avr) +DEF_HELPER_3(vsubuqm, void, avr, avr, avr) +DEF_HELPER_4(vsubecuq, void, avr, avr, avr, avr) +DEF_HELPER_4(vsubeuqm, void, avr, avr, avr, avr) +DEF_HELPER_3(vsubcuq, void, avr, avr, avr) DEF_HELPER_3(vrlb, void, avr, avr, avr) DEF_HELPER_3(vrlh, void, avr, avr, avr) DEF_HELPER_3(vrlw, void, avr, avr, avr) +DEF_HELPER_3(vrld, void, avr, avr, avr) DEF_HELPER_3(vsl, void, avr, avr, avr) DEF_HELPER_3(vsr, void, avr, avr, avr) DEF_HELPER_4(vsldoi, void, avr, avr, avr, i32) @@ -198,8 +242,10 @@ DEF_HELPER_2(vupkhpx, void, avr, avr) DEF_HELPER_2(vupklpx, void, avr, avr) DEF_HELPER_2(vupkhsb, void, avr, avr) DEF_HELPER_2(vupkhsh, void, avr, avr) +DEF_HELPER_2(vupkhsw, void, avr, avr) DEF_HELPER_2(vupklsb, void, avr, avr) DEF_HELPER_2(vupklsh, void, avr, avr) +DEF_HELPER_2(vupklsw, void, avr, avr) DEF_HELPER_5(vmsumubm, void, env, avr, avr, avr, avr) DEF_HELPER_5(vmsummbm, void, env, avr, avr, avr, avr) DEF_HELPER_5(vsel, void, env, avr, avr, avr, avr) @@ -208,10 +254,14 @@ DEF_HELPER_4(vpkshss, void, env, avr, avr, avr) DEF_HELPER_4(vpkshus, void, env, avr, avr, avr) DEF_HELPER_4(vpkswss, void, env, avr, avr, avr) DEF_HELPER_4(vpkswus, void, env, avr, avr, avr) +DEF_HELPER_4(vpksdss, void, env, avr, avr, avr) +DEF_HELPER_4(vpksdus, void, env, avr, avr, avr) DEF_HELPER_4(vpkuhus, void, env, avr, avr, avr) DEF_HELPER_4(vpkuwus, void, env, avr, avr, avr) +DEF_HELPER_4(vpkudus, void, env, avr, avr, avr) DEF_HELPER_4(vpkuhum, void, env, avr, avr, avr) DEF_HELPER_4(vpkuwum, void, env, avr, avr, avr) +DEF_HELPER_4(vpkudum, void, env, avr, avr, avr) DEF_HELPER_3(vpkpx, void, avr, avr, avr) DEF_HELPER_5(vmhaddshs, void, env, avr, avr, avr, avr) DEF_HELPER_5(vmhraddshs, void, env, avr, avr, avr, avr) @@ -251,6 +301,163 @@ DEF_HELPER_4(vcfsx, void, env, avr, avr, i32) DEF_HELPER_4(vctuxs, void, env, avr, avr, i32) DEF_HELPER_4(vctsxs, void, env, avr, avr, i32) +DEF_HELPER_2(vclzb, void, avr, avr) +DEF_HELPER_2(vclzh, void, avr, avr) +DEF_HELPER_2(vclzw, void, avr, avr) +DEF_HELPER_2(vclzd, void, avr, avr) +DEF_HELPER_2(vpopcntb, void, avr, avr) +DEF_HELPER_2(vpopcnth, void, avr, avr) +DEF_HELPER_2(vpopcntw, void, avr, avr) +DEF_HELPER_2(vpopcntd, void, avr, avr) +DEF_HELPER_3(vbpermq, void, avr, avr, avr) +DEF_HELPER_2(vgbbd, void, avr, avr) +DEF_HELPER_3(vpmsumb, void, avr, avr, avr) +DEF_HELPER_3(vpmsumh, void, avr, avr, avr) +DEF_HELPER_3(vpmsumw, void, avr, avr, avr) +DEF_HELPER_3(vpmsumd, void, avr, avr, avr) + +DEF_HELPER_2(vsbox, void, avr, avr) +DEF_HELPER_3(vcipher, void, avr, avr, avr) +DEF_HELPER_3(vcipherlast, void, avr, avr, avr) +DEF_HELPER_3(vncipher, void, avr, avr, avr) +DEF_HELPER_3(vncipherlast, void, avr, avr, avr) +DEF_HELPER_3(vshasigmaw, void, avr, avr, i32) +DEF_HELPER_3(vshasigmad, void, avr, avr, i32) +DEF_HELPER_4(vpermxor, void, avr, avr, avr, avr) + +DEF_HELPER_4(bcdadd, i32, avr, avr, avr, i32) +DEF_HELPER_4(bcdsub, i32, avr, avr, avr, i32) + +DEF_HELPER_2(xsadddp, void, env, i32) +DEF_HELPER_2(xssubdp, void, env, i32) +DEF_HELPER_2(xsmuldp, void, env, i32) +DEF_HELPER_2(xsdivdp, void, env, i32) +DEF_HELPER_2(xsredp, void, env, i32) +DEF_HELPER_2(xssqrtdp, void, env, i32) +DEF_HELPER_2(xsrsqrtedp, void, env, i32) +DEF_HELPER_2(xstdivdp, void, env, i32) +DEF_HELPER_2(xstsqrtdp, void, env, i32) +DEF_HELPER_2(xsmaddadp, void, env, i32) +DEF_HELPER_2(xsmaddmdp, void, env, i32) +DEF_HELPER_2(xsmsubadp, void, env, i32) +DEF_HELPER_2(xsmsubmdp, void, env, i32) +DEF_HELPER_2(xsnmaddadp, void, env, i32) +DEF_HELPER_2(xsnmaddmdp, void, env, i32) +DEF_HELPER_2(xsnmsubadp, void, env, i32) +DEF_HELPER_2(xsnmsubmdp, void, env, i32) +DEF_HELPER_2(xscmpodp, void, env, i32) +DEF_HELPER_2(xscmpudp, void, env, i32) +DEF_HELPER_2(xsmaxdp, void, env, i32) +DEF_HELPER_2(xsmindp, void, env, i32) +DEF_HELPER_2(xscvdpsp, void, env, i32) +DEF_HELPER_2(xscvdpspn, i64, env, i64) +DEF_HELPER_2(xscvspdp, void, env, i32) +DEF_HELPER_2(xscvspdpn, i64, env, i64) +DEF_HELPER_2(xscvdpsxds, void, env, i32) +DEF_HELPER_2(xscvdpsxws, void, env, i32) +DEF_HELPER_2(xscvdpuxds, void, env, i32) +DEF_HELPER_2(xscvdpuxws, void, env, i32) +DEF_HELPER_2(xscvsxddp, void, env, i32) +DEF_HELPER_2(xscvuxdsp, void, env, i32) +DEF_HELPER_2(xscvsxdsp, void, env, i32) +DEF_HELPER_2(xscvuxddp, void, env, i32) +DEF_HELPER_2(xsrdpi, void, env, i32) +DEF_HELPER_2(xsrdpic, void, env, i32) +DEF_HELPER_2(xsrdpim, void, env, i32) +DEF_HELPER_2(xsrdpip, void, env, i32) +DEF_HELPER_2(xsrdpiz, void, env, i32) + +DEF_HELPER_2(xsaddsp, void, env, i32) +DEF_HELPER_2(xssubsp, void, env, i32) +DEF_HELPER_2(xsmulsp, void, env, i32) +DEF_HELPER_2(xsdivsp, void, env, i32) +DEF_HELPER_2(xsresp, void, env, i32) +DEF_HELPER_2(xsrsp, i64, env, i64) +DEF_HELPER_2(xssqrtsp, void, env, i32) +DEF_HELPER_2(xsrsqrtesp, void, env, i32) +DEF_HELPER_2(xsmaddasp, void, env, i32) +DEF_HELPER_2(xsmaddmsp, void, env, i32) +DEF_HELPER_2(xsmsubasp, void, env, i32) +DEF_HELPER_2(xsmsubmsp, void, env, i32) +DEF_HELPER_2(xsnmaddasp, void, env, i32) +DEF_HELPER_2(xsnmaddmsp, void, env, i32) +DEF_HELPER_2(xsnmsubasp, void, env, i32) +DEF_HELPER_2(xsnmsubmsp, void, env, i32) + +DEF_HELPER_2(xvadddp, void, env, i32) +DEF_HELPER_2(xvsubdp, void, env, i32) +DEF_HELPER_2(xvmuldp, void, env, i32) +DEF_HELPER_2(xvdivdp, void, env, i32) +DEF_HELPER_2(xvredp, void, env, i32) +DEF_HELPER_2(xvsqrtdp, void, env, i32) +DEF_HELPER_2(xvrsqrtedp, void, env, i32) +DEF_HELPER_2(xvtdivdp, void, env, i32) +DEF_HELPER_2(xvtsqrtdp, void, env, i32) +DEF_HELPER_2(xvmaddadp, void, env, i32) +DEF_HELPER_2(xvmaddmdp, void, env, i32) +DEF_HELPER_2(xvmsubadp, void, env, i32) +DEF_HELPER_2(xvmsubmdp, void, env, i32) +DEF_HELPER_2(xvnmaddadp, void, env, i32) +DEF_HELPER_2(xvnmaddmdp, void, env, i32) +DEF_HELPER_2(xvnmsubadp, void, env, i32) +DEF_HELPER_2(xvnmsubmdp, void, env, i32) +DEF_HELPER_2(xvmaxdp, void, env, i32) +DEF_HELPER_2(xvmindp, void, env, i32) +DEF_HELPER_2(xvcmpeqdp, void, env, i32) +DEF_HELPER_2(xvcmpgedp, void, env, i32) +DEF_HELPER_2(xvcmpgtdp, void, env, i32) +DEF_HELPER_2(xvcvdpsp, void, env, i32) +DEF_HELPER_2(xvcvdpsxds, void, env, i32) +DEF_HELPER_2(xvcvdpsxws, void, env, i32) +DEF_HELPER_2(xvcvdpuxds, void, env, i32) +DEF_HELPER_2(xvcvdpuxws, void, env, i32) +DEF_HELPER_2(xvcvsxddp, void, env, i32) +DEF_HELPER_2(xvcvuxddp, void, env, i32) +DEF_HELPER_2(xvcvsxwdp, void, env, i32) +DEF_HELPER_2(xvcvuxwdp, void, env, i32) +DEF_HELPER_2(xvrdpi, void, env, i32) +DEF_HELPER_2(xvrdpic, void, env, i32) +DEF_HELPER_2(xvrdpim, void, env, i32) +DEF_HELPER_2(xvrdpip, void, env, i32) +DEF_HELPER_2(xvrdpiz, void, env, i32) + +DEF_HELPER_2(xvaddsp, void, env, i32) +DEF_HELPER_2(xvsubsp, void, env, i32) +DEF_HELPER_2(xvmulsp, void, env, i32) +DEF_HELPER_2(xvdivsp, void, env, i32) +DEF_HELPER_2(xvresp, void, env, i32) +DEF_HELPER_2(xvsqrtsp, void, env, i32) +DEF_HELPER_2(xvrsqrtesp, void, env, i32) +DEF_HELPER_2(xvtdivsp, void, env, i32) +DEF_HELPER_2(xvtsqrtsp, void, env, i32) +DEF_HELPER_2(xvmaddasp, void, env, i32) +DEF_HELPER_2(xvmaddmsp, void, env, i32) +DEF_HELPER_2(xvmsubasp, void, env, i32) +DEF_HELPER_2(xvmsubmsp, void, env, i32) +DEF_HELPER_2(xvnmaddasp, void, env, i32) +DEF_HELPER_2(xvnmaddmsp, void, env, i32) +DEF_HELPER_2(xvnmsubasp, void, env, i32) +DEF_HELPER_2(xvnmsubmsp, void, env, i32) +DEF_HELPER_2(xvmaxsp, void, env, i32) +DEF_HELPER_2(xvminsp, void, env, i32) +DEF_HELPER_2(xvcmpeqsp, void, env, i32) +DEF_HELPER_2(xvcmpgesp, void, env, i32) +DEF_HELPER_2(xvcmpgtsp, void, env, i32) +DEF_HELPER_2(xvcvspdp, void, env, i32) +DEF_HELPER_2(xvcvspsxds, void, env, i32) +DEF_HELPER_2(xvcvspsxws, void, env, i32) +DEF_HELPER_2(xvcvspuxds, void, env, i32) +DEF_HELPER_2(xvcvspuxws, void, env, i32) +DEF_HELPER_2(xvcvsxdsp, void, env, i32) +DEF_HELPER_2(xvcvuxdsp, void, env, i32) +DEF_HELPER_2(xvcvsxwsp, void, env, i32) +DEF_HELPER_2(xvcvuxwsp, void, env, i32) +DEF_HELPER_2(xvrspi, void, env, i32) +DEF_HELPER_2(xvrspic, void, env, i32) +DEF_HELPER_2(xvrspim, void, env, i32) +DEF_HELPER_2(xvrspip, void, env, i32) +DEF_HELPER_2(xvrspiz, void, env, i32) + DEF_HELPER_2(efscfsi, i32, env, i32) DEF_HELPER_2(efscfui, i32, env, i32) DEF_HELPER_2(efscfuf, i32, env, i32) diff --git a/target-ppc/int_helper.c b/target-ppc/int_helper.c index e50bdd20ec..63dde94b04 100644 --- a/target-ppc/int_helper.c +++ b/target-ppc/int_helper.c @@ -41,6 +41,119 @@ uint64_t helper_mulldo(CPUPPCState *env, uint64_t arg1, uint64_t arg2) } #endif +target_ulong helper_divweu(CPUPPCState *env, target_ulong ra, target_ulong rb, + uint32_t oe) +{ + uint64_t rt = 0; + int overflow = 0; + + uint64_t dividend = (uint64_t)ra << 32; + uint64_t divisor = (uint32_t)rb; + + if (unlikely(divisor == 0)) { + overflow = 1; + } else { + rt = dividend / divisor; + overflow = rt > UINT32_MAX; + } + + if (unlikely(overflow)) { + rt = 0; /* Undefined */ + } + + if (oe) { + if (unlikely(overflow)) { + env->so = env->ov = 1; + } else { + env->ov = 0; + } + } + + return (target_ulong)rt; +} + +target_ulong helper_divwe(CPUPPCState *env, target_ulong ra, target_ulong rb, + uint32_t oe) +{ + int64_t rt = 0; + int overflow = 0; + + int64_t dividend = (int64_t)ra << 32; + int64_t divisor = (int64_t)((int32_t)rb); + + if (unlikely((divisor == 0) || + ((divisor == -1ull) && (dividend == INT64_MIN)))) { + overflow = 1; + } else { + rt = dividend / divisor; + overflow = rt != (int32_t)rt; + } + + if (unlikely(overflow)) { + rt = 0; /* Undefined */ + } + + if (oe) { + if (unlikely(overflow)) { + env->so = env->ov = 1; + } else { + env->ov = 0; + } + } + + return (target_ulong)rt; +} + +#if defined(TARGET_PPC64) + +uint64_t helper_divdeu(CPUPPCState *env, uint64_t ra, uint64_t rb, uint32_t oe) +{ + uint64_t rt = 0; + int overflow = 0; + + overflow = divu128(&rt, &ra, rb); + + if (unlikely(overflow)) { + rt = 0; /* Undefined */ + } + + if (oe) { + if (unlikely(overflow)) { + env->so = env->ov = 1; + } else { + env->ov = 0; + } + } + + return rt; +} + +uint64_t helper_divde(CPUPPCState *env, uint64_t rau, uint64_t rbu, uint32_t oe) +{ + int64_t rt = 0; + int64_t ra = (int64_t)rau; + int64_t rb = (int64_t)rbu; + int overflow = divs128(&rt, &ra, rb); + + if (unlikely(overflow)) { + rt = 0; /* Undefined */ + } + + if (oe) { + + if (unlikely(overflow)) { + env->so = env->ov = 1; + } else { + env->ov = 0; + } + } + + return rt; +} + +#endif + + target_ulong helper_cntlzw(target_ulong t) { return clz32(t); @@ -53,6 +166,26 @@ target_ulong helper_cntlzd(target_ulong t) } #endif +#if defined(TARGET_PPC64) + +uint64_t helper_bpermd(uint64_t rs, uint64_t rb) +{ + int i; + uint64_t ra = 0; + + for (i = 0; i < 8; i++) { + int index = (rs >> (i*8)) & 0xFF; + if (index < 64) { + if (rb & (1ull << (63-index))) { + ra |= 1 << i; + } + } + } + return ra; +} + +#endif + target_ulong helper_cmpb(target_ulong rs, target_ulong rb) { target_ulong mask = 0xff; @@ -371,6 +504,8 @@ void helper_vaddcuw(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) VARITH(ubm, u8) VARITH(uhm, u16) VARITH(uwm, u32) +VARITH(udm, u64) +VARITH_DO(muluwm, *, u32) #undef VARITH_DO #undef VARITH @@ -491,15 +626,18 @@ VCF(sx, int32_to_float32, s32) void helper_vcmp##suffix(CPUPPCState *env, ppc_avr_t *r, \ ppc_avr_t *a, ppc_avr_t *b) \ { \ - uint32_t ones = (uint32_t)-1; \ - uint32_t all = ones; \ - uint32_t none = 0; \ + uint64_t ones = (uint64_t)-1; \ + uint64_t all = ones; \ + uint64_t none = 0; \ int i; \ \ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - uint32_t result = (a->element[i] compare b->element[i] ? \ + uint64_t result = (a->element[i] compare b->element[i] ? \ ones : 0x0); \ switch (sizeof(a->element[0])) { \ + case 8: \ + r->u64[i] = result; \ + break; \ case 4: \ r->u32[i] = result; \ break; \ @@ -523,12 +661,15 @@ VCF(sx, int32_to_float32, s32) VCMP(equb, ==, u8) VCMP(equh, ==, u16) VCMP(equw, ==, u32) +VCMP(equd, ==, u64) VCMP(gtub, >, u8) VCMP(gtuh, >, u16) VCMP(gtuw, >, u32) +VCMP(gtud, >, u64) VCMP(gtsb, >, s8) VCMP(gtsh, >, s16) VCMP(gtsw, >, s32) +VCMP(gtsd, >, s64) #undef VCMP_DO #undef VCMP @@ -689,9 +830,11 @@ void helper_vmhraddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, VMINMAX(sb, s8) VMINMAX(sh, s16) VMINMAX(sw, s32) +VMINMAX(sd, s64) VMINMAX(ub, u8) VMINMAX(uh, u16) VMINMAX(uw, u32) +VMINMAX(ud, u64) #undef VMINMAX_DO #undef VMINMAX @@ -849,28 +992,32 @@ void helper_vmsumuhs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, } } -#define VMUL_DO(name, mul_element, prod_element, evenp) \ +#define VMUL_DO(name, mul_element, prod_element, cast, evenp) \ void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ { \ int i; \ \ VECTOR_FOR_INORDER_I(i, prod_element) { \ if (evenp) { \ - r->prod_element[i] = a->mul_element[i * 2 + HI_IDX] * \ - b->mul_element[i * 2 + HI_IDX]; \ + r->prod_element[i] = \ + (cast)a->mul_element[i * 2 + HI_IDX] * \ + (cast)b->mul_element[i * 2 + HI_IDX]; \ } else { \ - r->prod_element[i] = a->mul_element[i * 2 + LO_IDX] * \ - b->mul_element[i * 2 + LO_IDX]; \ + r->prod_element[i] = \ + (cast)a->mul_element[i * 2 + LO_IDX] * \ + (cast)b->mul_element[i * 2 + LO_IDX]; \ } \ } \ } -#define VMUL(suffix, mul_element, prod_element) \ - VMUL_DO(mule##suffix, mul_element, prod_element, 1) \ - VMUL_DO(mulo##suffix, mul_element, prod_element, 0) -VMUL(sb, s8, s16) -VMUL(sh, s16, s32) -VMUL(ub, u8, u16) -VMUL(uh, u16, u32) +#define VMUL(suffix, mul_element, prod_element, cast) \ + VMUL_DO(mule##suffix, mul_element, prod_element, cast, 1) \ + VMUL_DO(mulo##suffix, mul_element, prod_element, cast, 0) +VMUL(sb, s8, s16, int16_t) +VMUL(sh, s16, s32, int32_t) +VMUL(sw, s32, s64, int64_t) +VMUL(ub, u8, u16, uint16_t) +VMUL(uh, u16, u32, uint32_t) +VMUL(uw, u32, u64, uint64_t) #undef VMUL_DO #undef VMUL @@ -898,6 +1045,383 @@ void helper_vperm(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, } #if defined(HOST_WORDS_BIGENDIAN) +#define VBPERMQ_INDEX(avr, i) ((avr)->u8[(i)]) +#define VBPERMQ_DW(index) (((index) & 0x40) != 0) +#else +#define VBPERMQ_INDEX(avr, i) ((avr)->u8[15-(i)]) +#define VBPERMQ_DW(index) (((index) & 0x40) == 0) +#endif + +void helper_vbpermq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int i; + uint64_t perm = 0; + + VECTOR_FOR_INORDER_I(i, u8) { + int index = VBPERMQ_INDEX(b, i); + + if (index < 128) { + uint64_t mask = (1ull << (63-(index & 0x3F))); + if (a->u64[VBPERMQ_DW(index)] & mask) { + perm |= (0x8000 >> i); + } + } + } + + r->u64[HI_IDX] = perm; + r->u64[LO_IDX] = 0; +} + +#undef VBPERMQ_INDEX +#undef VBPERMQ_DW + +uint64_t VGBBD_MASKS[256] = { + 0x0000000000000000ull, /* 00 */ + 0x0000000000000080ull, /* 01 */ + 0x0000000000008000ull, /* 02 */ + 0x0000000000008080ull, /* 03 */ + 0x0000000000800000ull, /* 04 */ + 0x0000000000800080ull, /* 05 */ + 0x0000000000808000ull, /* 06 */ + 0x0000000000808080ull, /* 07 */ + 0x0000000080000000ull, /* 08 */ + 0x0000000080000080ull, /* 09 */ + 0x0000000080008000ull, /* 0A */ + 0x0000000080008080ull, /* 0B */ + 0x0000000080800000ull, /* 0C */ + 0x0000000080800080ull, /* 0D */ + 0x0000000080808000ull, /* 0E */ + 0x0000000080808080ull, /* 0F */ + 0x0000008000000000ull, /* 10 */ + 0x0000008000000080ull, /* 11 */ + 0x0000008000008000ull, /* 12 */ + 0x0000008000008080ull, /* 13 */ + 0x0000008000800000ull, /* 14 */ + 0x0000008000800080ull, /* 15 */ + 0x0000008000808000ull, /* 16 */ + 0x0000008000808080ull, /* 17 */ + 0x0000008080000000ull, /* 18 */ + 0x0000008080000080ull, /* 19 */ + 0x0000008080008000ull, /* 1A */ + 0x0000008080008080ull, /* 1B */ + 0x0000008080800000ull, /* 1C */ + 0x0000008080800080ull, /* 1D */ + 0x0000008080808000ull, /* 1E */ + 0x0000008080808080ull, /* 1F */ + 0x0000800000000000ull, /* 20 */ + 0x0000800000000080ull, /* 21 */ + 0x0000800000008000ull, /* 22 */ + 0x0000800000008080ull, /* 23 */ + 0x0000800000800000ull, /* 24 */ + 0x0000800000800080ull, /* 25 */ + 0x0000800000808000ull, /* 26 */ + 0x0000800000808080ull, /* 27 */ + 0x0000800080000000ull, /* 28 */ + 0x0000800080000080ull, /* 29 */ + 0x0000800080008000ull, /* 2A */ + 0x0000800080008080ull, /* 2B */ + 0x0000800080800000ull, /* 2C */ + 0x0000800080800080ull, /* 2D */ + 0x0000800080808000ull, /* 2E */ + 0x0000800080808080ull, /* 2F */ + 0x0000808000000000ull, /* 30 */ + 0x0000808000000080ull, /* 31 */ + 0x0000808000008000ull, /* 32 */ + 0x0000808000008080ull, /* 33 */ + 0x0000808000800000ull, /* 34 */ + 0x0000808000800080ull, /* 35 */ + 0x0000808000808000ull, /* 36 */ + 0x0000808000808080ull, /* 37 */ + 0x0000808080000000ull, /* 38 */ + 0x0000808080000080ull, /* 39 */ + 0x0000808080008000ull, /* 3A */ + 0x0000808080008080ull, /* 3B */ + 0x0000808080800000ull, /* 3C */ + 0x0000808080800080ull, /* 3D */ + 0x0000808080808000ull, /* 3E */ + 0x0000808080808080ull, /* 3F */ + 0x0080000000000000ull, /* 40 */ + 0x0080000000000080ull, /* 41 */ + 0x0080000000008000ull, /* 42 */ + 0x0080000000008080ull, /* 43 */ + 0x0080000000800000ull, /* 44 */ + 0x0080000000800080ull, /* 45 */ + 0x0080000000808000ull, /* 46 */ + 0x0080000000808080ull, /* 47 */ + 0x0080000080000000ull, /* 48 */ + 0x0080000080000080ull, /* 49 */ + 0x0080000080008000ull, /* 4A */ + 0x0080000080008080ull, /* 4B */ + 0x0080000080800000ull, /* 4C */ + 0x0080000080800080ull, /* 4D */ + 0x0080000080808000ull, /* 4E */ + 0x0080000080808080ull, /* 4F */ + 0x0080008000000000ull, /* 50 */ + 0x0080008000000080ull, /* 51 */ + 0x0080008000008000ull, /* 52 */ + 0x0080008000008080ull, /* 53 */ + 0x0080008000800000ull, /* 54 */ + 0x0080008000800080ull, /* 55 */ + 0x0080008000808000ull, /* 56 */ + 0x0080008000808080ull, /* 57 */ + 0x0080008080000000ull, /* 58 */ + 0x0080008080000080ull, /* 59 */ + 0x0080008080008000ull, /* 5A */ + 0x0080008080008080ull, /* 5B */ + 0x0080008080800000ull, /* 5C */ + 0x0080008080800080ull, /* 5D */ + 0x0080008080808000ull, /* 5E */ + 0x0080008080808080ull, /* 5F */ + 0x0080800000000000ull, /* 60 */ + 0x0080800000000080ull, /* 61 */ + 0x0080800000008000ull, /* 62 */ + 0x0080800000008080ull, /* 63 */ + 0x0080800000800000ull, /* 64 */ + 0x0080800000800080ull, /* 65 */ + 0x0080800000808000ull, /* 66 */ + 0x0080800000808080ull, /* 67 */ + 0x0080800080000000ull, /* 68 */ + 0x0080800080000080ull, /* 69 */ + 0x0080800080008000ull, /* 6A */ + 0x0080800080008080ull, /* 6B */ + 0x0080800080800000ull, /* 6C */ + 0x0080800080800080ull, /* 6D */ + 0x0080800080808000ull, /* 6E */ + 0x0080800080808080ull, /* 6F */ + 0x0080808000000000ull, /* 70 */ + 0x0080808000000080ull, /* 71 */ + 0x0080808000008000ull, /* 72 */ + 0x0080808000008080ull, /* 73 */ + 0x0080808000800000ull, /* 74 */ + 0x0080808000800080ull, /* 75 */ + 0x0080808000808000ull, /* 76 */ + 0x0080808000808080ull, /* 77 */ + 0x0080808080000000ull, /* 78 */ + 0x0080808080000080ull, /* 79 */ + 0x0080808080008000ull, /* 7A */ + 0x0080808080008080ull, /* 7B */ + 0x0080808080800000ull, /* 7C */ + 0x0080808080800080ull, /* 7D */ + 0x0080808080808000ull, /* 7E */ + 0x0080808080808080ull, /* 7F */ + 0x8000000000000000ull, /* 80 */ + 0x8000000000000080ull, /* 81 */ + 0x8000000000008000ull, /* 82 */ + 0x8000000000008080ull, /* 83 */ + 0x8000000000800000ull, /* 84 */ + 0x8000000000800080ull, /* 85 */ + 0x8000000000808000ull, /* 86 */ + 0x8000000000808080ull, /* 87 */ + 0x8000000080000000ull, /* 88 */ + 0x8000000080000080ull, /* 89 */ + 0x8000000080008000ull, /* 8A */ + 0x8000000080008080ull, /* 8B */ + 0x8000000080800000ull, /* 8C */ + 0x8000000080800080ull, /* 8D */ + 0x8000000080808000ull, /* 8E */ + 0x8000000080808080ull, /* 8F */ + 0x8000008000000000ull, /* 90 */ + 0x8000008000000080ull, /* 91 */ + 0x8000008000008000ull, /* 92 */ + 0x8000008000008080ull, /* 93 */ + 0x8000008000800000ull, /* 94 */ + 0x8000008000800080ull, /* 95 */ + 0x8000008000808000ull, /* 96 */ + 0x8000008000808080ull, /* 97 */ + 0x8000008080000000ull, /* 98 */ + 0x8000008080000080ull, /* 99 */ + 0x8000008080008000ull, /* 9A */ + 0x8000008080008080ull, /* 9B */ + 0x8000008080800000ull, /* 9C */ + 0x8000008080800080ull, /* 9D */ + 0x8000008080808000ull, /* 9E */ + 0x8000008080808080ull, /* 9F */ + 0x8000800000000000ull, /* A0 */ + 0x8000800000000080ull, /* A1 */ + 0x8000800000008000ull, /* A2 */ + 0x8000800000008080ull, /* A3 */ + 0x8000800000800000ull, /* A4 */ + 0x8000800000800080ull, /* A5 */ + 0x8000800000808000ull, /* A6 */ + 0x8000800000808080ull, /* A7 */ + 0x8000800080000000ull, /* A8 */ + 0x8000800080000080ull, /* A9 */ + 0x8000800080008000ull, /* AA */ + 0x8000800080008080ull, /* AB */ + 0x8000800080800000ull, /* AC */ + 0x8000800080800080ull, /* AD */ + 0x8000800080808000ull, /* AE */ + 0x8000800080808080ull, /* AF */ + 0x8000808000000000ull, /* B0 */ + 0x8000808000000080ull, /* B1 */ + 0x8000808000008000ull, /* B2 */ + 0x8000808000008080ull, /* B3 */ + 0x8000808000800000ull, /* B4 */ + 0x8000808000800080ull, /* B5 */ + 0x8000808000808000ull, /* B6 */ + 0x8000808000808080ull, /* B7 */ + 0x8000808080000000ull, /* B8 */ + 0x8000808080000080ull, /* B9 */ + 0x8000808080008000ull, /* BA */ + 0x8000808080008080ull, /* BB */ + 0x8000808080800000ull, /* BC */ + 0x8000808080800080ull, /* BD */ + 0x8000808080808000ull, /* BE */ + 0x8000808080808080ull, /* BF */ + 0x8080000000000000ull, /* C0 */ + 0x8080000000000080ull, /* C1 */ + 0x8080000000008000ull, /* C2 */ + 0x8080000000008080ull, /* C3 */ + 0x8080000000800000ull, /* C4 */ + 0x8080000000800080ull, /* C5 */ + 0x8080000000808000ull, /* C6 */ + 0x8080000000808080ull, /* C7 */ + 0x8080000080000000ull, /* C8 */ + 0x8080000080000080ull, /* C9 */ + 0x8080000080008000ull, /* CA */ + 0x8080000080008080ull, /* CB */ + 0x8080000080800000ull, /* CC */ + 0x8080000080800080ull, /* CD */ + 0x8080000080808000ull, /* CE */ + 0x8080000080808080ull, /* CF */ + 0x8080008000000000ull, /* D0 */ + 0x8080008000000080ull, /* D1 */ + 0x8080008000008000ull, /* D2 */ + 0x8080008000008080ull, /* D3 */ + 0x8080008000800000ull, /* D4 */ + 0x8080008000800080ull, /* D5 */ + 0x8080008000808000ull, /* D6 */ + 0x8080008000808080ull, /* D7 */ + 0x8080008080000000ull, /* D8 */ + 0x8080008080000080ull, /* D9 */ + 0x8080008080008000ull, /* DA */ + 0x8080008080008080ull, /* DB */ + 0x8080008080800000ull, /* DC */ + 0x8080008080800080ull, /* DD */ + 0x8080008080808000ull, /* DE */ + 0x8080008080808080ull, /* DF */ + 0x8080800000000000ull, /* E0 */ + 0x8080800000000080ull, /* E1 */ + 0x8080800000008000ull, /* E2 */ + 0x8080800000008080ull, /* E3 */ + 0x8080800000800000ull, /* E4 */ + 0x8080800000800080ull, /* E5 */ + 0x8080800000808000ull, /* E6 */ + 0x8080800000808080ull, /* E7 */ + 0x8080800080000000ull, /* E8 */ + 0x8080800080000080ull, /* E9 */ + 0x8080800080008000ull, /* EA */ + 0x8080800080008080ull, /* EB */ + 0x8080800080800000ull, /* EC */ + 0x8080800080800080ull, /* ED */ + 0x8080800080808000ull, /* EE */ + 0x8080800080808080ull, /* EF */ + 0x8080808000000000ull, /* F0 */ + 0x8080808000000080ull, /* F1 */ + 0x8080808000008000ull, /* F2 */ + 0x8080808000008080ull, /* F3 */ + 0x8080808000800000ull, /* F4 */ + 0x8080808000800080ull, /* F5 */ + 0x8080808000808000ull, /* F6 */ + 0x8080808000808080ull, /* F7 */ + 0x8080808080000000ull, /* F8 */ + 0x8080808080000080ull, /* F9 */ + 0x8080808080008000ull, /* FA */ + 0x8080808080008080ull, /* FB */ + 0x8080808080800000ull, /* FC */ + 0x8080808080800080ull, /* FD */ + 0x8080808080808000ull, /* FE */ + 0x8080808080808080ull, /* FF */ +}; + +void helper_vgbbd(ppc_avr_t *r, ppc_avr_t *b) +{ + int i; + uint64_t t[2] = { 0, 0 }; + + VECTOR_FOR_INORDER_I(i, u8) { +#if defined(HOST_WORDS_BIGENDIAN) + t[i>>3] |= VGBBD_MASKS[b->u8[i]] >> (i & 7); +#else + t[i>>3] |= VGBBD_MASKS[b->u8[i]] >> (7-(i & 7)); +#endif + } + + r->u64[0] = t[0]; + r->u64[1] = t[1]; +} + +#define PMSUM(name, srcfld, trgfld, trgtyp) \ +void helper_##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ +{ \ + int i, j; \ + trgtyp prod[sizeof(ppc_avr_t)/sizeof(a->srcfld[0])]; \ + \ + VECTOR_FOR_INORDER_I(i, srcfld) { \ + prod[i] = 0; \ + for (j = 0; j < sizeof(a->srcfld[0]) * 8; j++) { \ + if (a->srcfld[i] & (1ull<<j)) { \ + prod[i] ^= ((trgtyp)b->srcfld[i] << j); \ + } \ + } \ + } \ + \ + VECTOR_FOR_INORDER_I(i, trgfld) { \ + r->trgfld[i] = prod[2*i] ^ prod[2*i+1]; \ + } \ +} + +PMSUM(vpmsumb, u8, u16, uint16_t) +PMSUM(vpmsumh, u16, u32, uint32_t) +PMSUM(vpmsumw, u32, u64, uint64_t) + +void helper_vpmsumd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + +#ifdef CONFIG_INT128 + int i, j; + __uint128_t prod[2]; + + VECTOR_FOR_INORDER_I(i, u64) { + prod[i] = 0; + for (j = 0; j < 64; j++) { + if (a->u64[i] & (1ull<<j)) { + prod[i] ^= (((__uint128_t)b->u64[i]) << j); + } + } + } + + r->u128 = prod[0] ^ prod[1]; + +#else + int i, j; + ppc_avr_t prod[2]; + + VECTOR_FOR_INORDER_I(i, u64) { + prod[i].u64[LO_IDX] = prod[i].u64[HI_IDX] = 0; + for (j = 0; j < 64; j++) { + if (a->u64[i] & (1ull<<j)) { + ppc_avr_t bshift; + if (j == 0) { + bshift.u64[HI_IDX] = 0; + bshift.u64[LO_IDX] = b->u64[i]; + } else { + bshift.u64[HI_IDX] = b->u64[i] >> (64-j); + bshift.u64[LO_IDX] = b->u64[i] << j; + } + prod[i].u64[LO_IDX] ^= bshift.u64[LO_IDX]; + prod[i].u64[HI_IDX] ^= bshift.u64[HI_IDX]; + } + } + } + + r->u64[LO_IDX] = prod[0].u64[LO_IDX] ^ prod[1].u64[LO_IDX]; + r->u64[HI_IDX] = prod[0].u64[HI_IDX] ^ prod[1].u64[HI_IDX]; +#endif +} + + +#if defined(HOST_WORDS_BIGENDIAN) #define PKBIG 1 #else #define PKBIG 0 @@ -948,10 +1472,14 @@ VPK(shss, s16, s8, cvtshsb, 1) VPK(shus, s16, u8, cvtshub, 1) VPK(swss, s32, s16, cvtswsh, 1) VPK(swus, s32, u16, cvtswuh, 1) +VPK(sdss, s64, s32, cvtsdsw, 1) +VPK(sdus, s64, u32, cvtsduw, 1) VPK(uhus, u16, u8, cvtuhub, 1) VPK(uwus, u32, u16, cvtuwuh, 1) +VPK(udus, u64, u32, cvtuduw, 1) VPK(uhum, u16, u8, I, 0) VPK(uwum, u32, u16, I, 0) +VPK(udum, u64, u32, I, 0) #undef I #undef VPK #undef PKBIG @@ -983,23 +1511,21 @@ VRFI(p, float_round_up) VRFI(z, float_round_to_zero) #undef VRFI -#define VROTATE(suffix, element) \ +#define VROTATE(suffix, element, mask) \ void helper_vrl##suffix(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ { \ int i; \ \ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - unsigned int mask = ((1 << \ - (3 + (sizeof(a->element[0]) >> 1))) \ - - 1); \ unsigned int shift = b->element[i] & mask; \ r->element[i] = (a->element[i] << shift) | \ (a->element[i] >> (sizeof(a->element[0]) * 8 - shift)); \ } \ } -VROTATE(b, u8) -VROTATE(h, u16) -VROTATE(w, u32) +VROTATE(b, u8, 0x7) +VROTATE(h, u16, 0xF) +VROTATE(w, u32, 0x1F) +VROTATE(d, u64, 0x3F) #undef VROTATE void helper_vrsqrtefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) @@ -1080,23 +1606,21 @@ VSHIFT(r, RIGHT) #undef LEFT #undef RIGHT -#define VSL(suffix, element) \ +#define VSL(suffix, element, mask) \ void helper_vsl##suffix(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ { \ int i; \ \ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - unsigned int mask = ((1 << \ - (3 + (sizeof(a->element[0]) >> 1))) \ - - 1); \ unsigned int shift = b->element[i] & mask; \ \ r->element[i] = a->element[i] << shift; \ } \ } -VSL(b, u8) -VSL(h, u16) -VSL(w, u32) +VSL(b, u8, 0x7) +VSL(h, u16, 0x0F) +VSL(w, u32, 0x1F) +VSL(d, u64, 0x3F) #undef VSL void helper_vsldoi(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t shift) @@ -1180,26 +1704,24 @@ VSPLTI(h, s16, int16_t) VSPLTI(w, s32, int32_t) #undef VSPLTI -#define VSR(suffix, element) \ +#define VSR(suffix, element, mask) \ void helper_vsr##suffix(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ { \ int i; \ \ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - unsigned int mask = ((1 << \ - (3 + (sizeof(a->element[0]) >> 1))) \ - - 1); \ unsigned int shift = b->element[i] & mask; \ - \ r->element[i] = a->element[i] >> shift; \ } \ } -VSR(ab, s8) -VSR(ah, s16) -VSR(aw, s32) -VSR(b, u8) -VSR(h, u16) -VSR(w, u32) +VSR(ab, s8, 0x7) +VSR(ah, s16, 0xF) +VSR(aw, s32, 0x1F) +VSR(ad, s64, 0x3F) +VSR(b, u8, 0x7) +VSR(h, u16, 0xF) +VSR(w, u32, 0x1F) +VSR(d, u64, 0x3F) #undef VSR void helper_vsro(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) @@ -1379,12 +1901,819 @@ VUPKPX(hpx, UPKHI) } VUPK(hsb, s16, s8, UPKHI) VUPK(hsh, s32, s16, UPKHI) +VUPK(hsw, s64, s32, UPKHI) VUPK(lsb, s16, s8, UPKLO) VUPK(lsh, s32, s16, UPKLO) +VUPK(lsw, s64, s32, UPKLO) #undef VUPK #undef UPKHI #undef UPKLO +#define VGENERIC_DO(name, element) \ + void helper_v##name(ppc_avr_t *r, ppc_avr_t *b) \ + { \ + int i; \ + \ + VECTOR_FOR_INORDER_I(i, element) { \ + r->element[i] = name(b->element[i]); \ + } \ + } + +#define clzb(v) ((v) ? clz32((uint32_t)(v) << 24) : 8) +#define clzh(v) ((v) ? clz32((uint32_t)(v) << 16) : 16) +#define clzw(v) clz32((v)) +#define clzd(v) clz64((v)) + +VGENERIC_DO(clzb, u8) +VGENERIC_DO(clzh, u16) +VGENERIC_DO(clzw, u32) +VGENERIC_DO(clzd, u64) + +#undef clzb +#undef clzh +#undef clzw +#undef clzd + +#define popcntb(v) ctpop8(v) +#define popcnth(v) ctpop16(v) +#define popcntw(v) ctpop32(v) +#define popcntd(v) ctpop64(v) + +VGENERIC_DO(popcntb, u8) +VGENERIC_DO(popcnth, u16) +VGENERIC_DO(popcntw, u32) +VGENERIC_DO(popcntd, u64) + +#undef popcntb +#undef popcnth +#undef popcntw +#undef popcntd + +#undef VGENERIC_DO + +#if defined(HOST_WORDS_BIGENDIAN) +#define QW_ONE { .u64 = { 0, 1 } } +#else +#define QW_ONE { .u64 = { 1, 0 } } +#endif + +#ifndef CONFIG_INT128 + +static inline void avr_qw_not(ppc_avr_t *t, ppc_avr_t a) +{ + t->u64[0] = ~a.u64[0]; + t->u64[1] = ~a.u64[1]; +} + +static int avr_qw_cmpu(ppc_avr_t a, ppc_avr_t b) +{ + if (a.u64[HI_IDX] < b.u64[HI_IDX]) { + return -1; + } else if (a.u64[HI_IDX] > b.u64[HI_IDX]) { + return 1; + } else if (a.u64[LO_IDX] < b.u64[LO_IDX]) { + return -1; + } else if (a.u64[LO_IDX] > b.u64[LO_IDX]) { + return 1; + } else { + return 0; + } +} + +static void avr_qw_add(ppc_avr_t *t, ppc_avr_t a, ppc_avr_t b) +{ + t->u64[LO_IDX] = a.u64[LO_IDX] + b.u64[LO_IDX]; + t->u64[HI_IDX] = a.u64[HI_IDX] + b.u64[HI_IDX] + + (~a.u64[LO_IDX] < b.u64[LO_IDX]); +} + +static int avr_qw_addc(ppc_avr_t *t, ppc_avr_t a, ppc_avr_t b) +{ + ppc_avr_t not_a; + t->u64[LO_IDX] = a.u64[LO_IDX] + b.u64[LO_IDX]; + t->u64[HI_IDX] = a.u64[HI_IDX] + b.u64[HI_IDX] + + (~a.u64[LO_IDX] < b.u64[LO_IDX]); + avr_qw_not(¬_a, a); + return avr_qw_cmpu(not_a, b) < 0; +} + +#endif + +void helper_vadduqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ +#ifdef CONFIG_INT128 + r->u128 = a->u128 + b->u128; +#else + avr_qw_add(r, *a, *b); +#endif +} + +void helper_vaddeuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +{ +#ifdef CONFIG_INT128 + r->u128 = a->u128 + b->u128 + (c->u128 & 1); +#else + + if (c->u64[LO_IDX] & 1) { + ppc_avr_t tmp; + + tmp.u64[HI_IDX] = 0; + tmp.u64[LO_IDX] = c->u64[LO_IDX] & 1; + avr_qw_add(&tmp, *a, tmp); + avr_qw_add(r, tmp, *b); + } else { + avr_qw_add(r, *a, *b); + } +#endif +} + +void helper_vaddcuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ +#ifdef CONFIG_INT128 + r->u128 = (~a->u128 < b->u128); +#else + ppc_avr_t not_a; + + avr_qw_not(¬_a, *a); + + r->u64[HI_IDX] = 0; + r->u64[LO_IDX] = (avr_qw_cmpu(not_a, *b) < 0); +#endif +} + +void helper_vaddecuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +{ +#ifdef CONFIG_INT128 + int carry_out = (~a->u128 < b->u128); + if (!carry_out && (c->u128 & 1)) { + carry_out = ((a->u128 + b->u128 + 1) == 0) && + ((a->u128 != 0) || (b->u128 != 0)); + } + r->u128 = carry_out; +#else + + int carry_in = c->u64[LO_IDX] & 1; + int carry_out = 0; + ppc_avr_t tmp; + + carry_out = avr_qw_addc(&tmp, *a, *b); + + if (!carry_out && carry_in) { + ppc_avr_t one = QW_ONE; + carry_out = avr_qw_addc(&tmp, tmp, one); + } + r->u64[HI_IDX] = 0; + r->u64[LO_IDX] = carry_out; +#endif +} + +void helper_vsubuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ +#ifdef CONFIG_INT128 + r->u128 = a->u128 - b->u128; +#else + ppc_avr_t tmp; + ppc_avr_t one = QW_ONE; + + avr_qw_not(&tmp, *b); + avr_qw_add(&tmp, *a, tmp); + avr_qw_add(r, tmp, one); +#endif +} + +void helper_vsubeuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +{ +#ifdef CONFIG_INT128 + r->u128 = a->u128 + ~b->u128 + (c->u128 & 1); +#else + ppc_avr_t tmp, sum; + + avr_qw_not(&tmp, *b); + avr_qw_add(&sum, *a, tmp); + + tmp.u64[HI_IDX] = 0; + tmp.u64[LO_IDX] = c->u64[LO_IDX] & 1; + avr_qw_add(r, sum, tmp); +#endif +} + +void helper_vsubcuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ +#ifdef CONFIG_INT128 + r->u128 = (~a->u128 < ~b->u128) || + (a->u128 + ~b->u128 == (__uint128_t)-1); +#else + int carry = (avr_qw_cmpu(*a, *b) > 0); + if (!carry) { + ppc_avr_t tmp; + avr_qw_not(&tmp, *b); + avr_qw_add(&tmp, *a, tmp); + carry = ((tmp.s64[HI_IDX] == -1ull) && (tmp.s64[LO_IDX] == -1ull)); + } + r->u64[HI_IDX] = 0; + r->u64[LO_IDX] = carry; +#endif +} + +void helper_vsubecuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +{ +#ifdef CONFIG_INT128 + r->u128 = + (~a->u128 < ~b->u128) || + ((c->u128 & 1) && (a->u128 + ~b->u128 == (__uint128_t)-1)); +#else + int carry_in = c->u64[LO_IDX] & 1; + int carry_out = (avr_qw_cmpu(*a, *b) > 0); + if (!carry_out && carry_in) { + ppc_avr_t tmp; + avr_qw_not(&tmp, *b); + avr_qw_add(&tmp, *a, tmp); + carry_out = ((tmp.u64[HI_IDX] == -1ull) && (tmp.u64[LO_IDX] == -1ull)); + } + + r->u64[HI_IDX] = 0; + r->u64[LO_IDX] = carry_out; +#endif +} + +#define BCD_PLUS_PREF_1 0xC +#define BCD_PLUS_PREF_2 0xF +#define BCD_PLUS_ALT_1 0xA +#define BCD_NEG_PREF 0xD +#define BCD_NEG_ALT 0xB +#define BCD_PLUS_ALT_2 0xE + +#if defined(HOST_WORDS_BIGENDIAN) +#define BCD_DIG_BYTE(n) (15 - (n/2)) +#else +#define BCD_DIG_BYTE(n) (n/2) +#endif + +static int bcd_get_sgn(ppc_avr_t *bcd) +{ + switch (bcd->u8[BCD_DIG_BYTE(0)] & 0xF) { + case BCD_PLUS_PREF_1: + case BCD_PLUS_PREF_2: + case BCD_PLUS_ALT_1: + case BCD_PLUS_ALT_2: + { + return 1; + } + + case BCD_NEG_PREF: + case BCD_NEG_ALT: + { + return -1; + } + + default: + { + return 0; + } + } +} + +static int bcd_preferred_sgn(int sgn, int ps) +{ + if (sgn >= 0) { + return (ps == 0) ? BCD_PLUS_PREF_1 : BCD_PLUS_PREF_2; + } else { + return BCD_NEG_PREF; + } +} + +static uint8_t bcd_get_digit(ppc_avr_t *bcd, int n, int *invalid) +{ + uint8_t result; + if (n & 1) { + result = bcd->u8[BCD_DIG_BYTE(n)] >> 4; + } else { + result = bcd->u8[BCD_DIG_BYTE(n)] & 0xF; + } + + if (unlikely(result > 9)) { + *invalid = true; + } + return result; +} + +static void bcd_put_digit(ppc_avr_t *bcd, uint8_t digit, int n) +{ + if (n & 1) { + bcd->u8[BCD_DIG_BYTE(n)] &= 0x0F; + bcd->u8[BCD_DIG_BYTE(n)] |= (digit<<4); + } else { + bcd->u8[BCD_DIG_BYTE(n)] &= 0xF0; + bcd->u8[BCD_DIG_BYTE(n)] |= digit; + } +} + +static int bcd_cmp_mag(ppc_avr_t *a, ppc_avr_t *b) +{ + int i; + int invalid = 0; + for (i = 31; i > 0; i--) { + uint8_t dig_a = bcd_get_digit(a, i, &invalid); + uint8_t dig_b = bcd_get_digit(b, i, &invalid); + if (unlikely(invalid)) { + return 0; /* doesnt matter */ + } else if (dig_a > dig_b) { + return 1; + } else if (dig_a < dig_b) { + return -1; + } + } + + return 0; +} + +static int bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, + int *overflow) +{ + int carry = 0; + int i; + int is_zero = 1; + for (i = 1; i <= 31; i++) { + uint8_t digit = bcd_get_digit(a, i, invalid) + + bcd_get_digit(b, i, invalid) + carry; + is_zero &= (digit == 0); + if (digit > 9) { + carry = 1; + digit -= 10; + } else { + carry = 0; + } + + bcd_put_digit(t, digit, i); + + if (unlikely(*invalid)) { + return -1; + } + } + + *overflow = carry; + return is_zero; +} + +static int bcd_sub_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, + int *overflow) +{ + int carry = 0; + int i; + int is_zero = 1; + for (i = 1; i <= 31; i++) { + uint8_t digit = bcd_get_digit(a, i, invalid) - + bcd_get_digit(b, i, invalid) + carry; + is_zero &= (digit == 0); + if (digit & 0x80) { + carry = -1; + digit += 10; + } else { + carry = 0; + } + + bcd_put_digit(t, digit, i); + + if (unlikely(*invalid)) { + return -1; + } + } + + *overflow = carry; + return is_zero; +} + +uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) +{ + + int sgna = bcd_get_sgn(a); + int sgnb = bcd_get_sgn(b); + int invalid = (sgna == 0) || (sgnb == 0); + int overflow = 0; + int zero = 0; + uint32_t cr = 0; + ppc_avr_t result = { .u64 = { 0, 0 } }; + + if (!invalid) { + if (sgna == sgnb) { + result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps); + zero = bcd_add_mag(&result, a, b, &invalid, &overflow); + cr = (sgna > 0) ? 4 : 8; + } else if (bcd_cmp_mag(a, b) > 0) { + result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps); + zero = bcd_sub_mag(&result, a, b, &invalid, &overflow); + cr = (sgna > 0) ? 4 : 8; + } else { + result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgnb, ps); + zero = bcd_sub_mag(&result, b, a, &invalid, &overflow); + cr = (sgnb > 0) ? 4 : 8; + } + } + + if (unlikely(invalid)) { + result.u64[HI_IDX] = result.u64[LO_IDX] = -1; + cr = 1; + } else if (overflow) { + cr |= 1; + } else if (zero) { + cr = 2; + } + + *r = result; + + return cr; +} + +uint32_t helper_bcdsub(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) +{ + ppc_avr_t bcopy = *b; + int sgnb = bcd_get_sgn(b); + if (sgnb < 0) { + bcd_put_digit(&bcopy, BCD_PLUS_PREF_1, 0); + } else if (sgnb > 0) { + bcd_put_digit(&bcopy, BCD_NEG_PREF, 0); + } + /* else invalid ... defer to bcdadd code for proper handling */ + + return helper_bcdadd(r, a, &bcopy, ps); +} + +static uint8_t SBOX[256] = { +0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, +0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, +0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, +0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, +0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, +0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, +0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, +0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, +0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, +0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, +0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, +0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, +0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, +0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, +0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, +0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, +0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, +0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, +0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, +0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, +0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, +0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, +0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, +0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, +0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, +0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, +0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, +0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, +0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, +0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, +0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, +0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16, +}; + +static void SubBytes(ppc_avr_t *r, ppc_avr_t *a) +{ + int i; + VECTOR_FOR_INORDER_I(i, u8) { + r->u8[i] = SBOX[a->u8[i]]; + } +} + +static uint8_t InvSBOX[256] = { +0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, +0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, +0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, +0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, +0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, +0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, +0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, +0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, +0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, +0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, +0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, +0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, +0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, +0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, +0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, +0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, +0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, +0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, +0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, +0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, +0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, +0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, +0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, +0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, +0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, +0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, +0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, +0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, +0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, +0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, +0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, +0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D, +}; + +static void InvSubBytes(ppc_avr_t *r, ppc_avr_t *a) +{ + int i; + VECTOR_FOR_INORDER_I(i, u8) { + r->u8[i] = InvSBOX[a->u8[i]]; + } +} + +static uint8_t ROTL8(uint8_t x, int n) +{ + return (x << n) | (x >> (8-n)); +} + +static inline int BIT8(uint8_t x, int n) +{ + return (x & (0x80 >> n)) != 0; +} + +static uint8_t GFx02(uint8_t x) +{ + return ROTL8(x, 1) ^ (BIT8(x, 0) ? 0x1A : 0); +} + +static uint8_t GFx03(uint8_t x) +{ + return x ^ ROTL8(x, 1) ^ (BIT8(x, 0) ? 0x1A : 0); +} + +static uint8_t GFx09(uint8_t x) +{ + uint8_t term2 = ROTL8(x, 3); + uint8_t term3 = (BIT8(x, 0) ? 0x68 : 0) | (BIT8(x, 1) ? 0x14 : 0) | + (BIT8(x, 2) ? 0x02 : 0); + uint8_t term4 = (BIT8(x, 1) ? 0x20 : 0) | (BIT8(x, 2) ? 0x18 : 0); + return x ^ term2 ^ term3 ^ term4; +} + +static uint8_t GFx0B(uint8_t x) +{ + uint8_t term2 = ROTL8(x, 1); + uint8_t term3 = (x << 3) | (BIT8(x, 0) ? 0x06 : 0) | + (BIT8(x, 2) ? 0x01 : 0); + uint8_t term4 = (BIT8(x, 0) ? 0x70 : 0) | (BIT8(x, 1) ? 0x06 : 0) | + (BIT8(x, 2) ? 0x08 : 0); + uint8_t term5 = (BIT8(x, 1) ? 0x30 : 0) | (BIT8(x, 2) ? 0x02 : 0); + uint8_t term6 = BIT8(x, 2) ? 0x10 : 0; + return x ^ term2 ^ term3 ^ term4 ^ term5 ^ term6; +} + +static uint8_t GFx0D(uint8_t x) +{ + uint8_t term2 = ROTL8(x, 2); + uint8_t term3 = (x << 3) | (BIT8(x, 1) ? 0x04 : 0) | + (BIT8(x, 2) ? 0x03 : 0); + uint8_t term4 = (BIT8(x, 0) ? 0x58 : 0) | (BIT8(x, 1) ? 0x20 : 0); + uint8_t term5 = (BIT8(x, 1) ? 0x08 : 0) | (BIT8(x, 2) ? 0x10 : 0); + uint8_t term6 = BIT8(x, 2) ? 0x08 : 0; + return x ^ term2 ^ term3 ^ term4 ^ term5 ^ term6; +} + +static uint8_t GFx0E(uint8_t x) +{ + uint8_t term1 = ROTL8(x, 1); + uint8_t term2 = (x << 2) | (BIT8(x, 2) ? 0x02 : 0) | + (BIT8(x, 1) ? 0x01 : 0); + uint8_t term3 = (x << 3) | (BIT8(x, 1) ? 0x04 : 0) | + (BIT8(x, 2) ? 0x01 : 0); + uint8_t term4 = (BIT8(x, 0) ? 0x40 : 0) | (BIT8(x, 1) ? 0x28 : 0) | + (BIT8(x, 2) ? 0x10 : 0); + uint8_t term5 = (BIT8(x, 2) ? 0x08 : 0); + return term1 ^ term2 ^ term3 ^ term4 ^ term5; +} + +#if defined(HOST_WORDS_BIGENDIAN) +#define MCB(x, i, b) ((x)->u8[(i)*4 + (b)]) +#else +#define MCB(x, i, b) ((x)->u8[15 - ((i)*4 + (b))]) +#endif + +static void MixColumns(ppc_avr_t *r, ppc_avr_t *x) +{ + int i; + for (i = 0; i < 4; i++) { + MCB(r, i, 0) = GFx02(MCB(x, i, 0)) ^ GFx03(MCB(x, i, 1)) ^ + MCB(x, i, 2) ^ MCB(x, i, 3); + MCB(r, i, 1) = MCB(x, i, 0) ^ GFx02(MCB(x, i, 1)) ^ + GFx03(MCB(x, i, 2)) ^ MCB(x, i, 3); + MCB(r, i, 2) = MCB(x, i, 0) ^ MCB(x, i, 1) ^ + GFx02(MCB(x, i, 2)) ^ GFx03(MCB(x, i, 3)); + MCB(r, i, 3) = GFx03(MCB(x, i, 0)) ^ MCB(x, i, 1) ^ + MCB(x, i, 2) ^ GFx02(MCB(x, i, 3)); + } +} + +static void InvMixColumns(ppc_avr_t *r, ppc_avr_t *x) +{ + int i; + for (i = 0; i < 4; i++) { + MCB(r, i, 0) = GFx0E(MCB(x, i, 0)) ^ GFx0B(MCB(x, i, 1)) ^ + GFx0D(MCB(x, i, 2)) ^ GFx09(MCB(x, i, 3)); + MCB(r, i, 1) = GFx09(MCB(x, i, 0)) ^ GFx0E(MCB(x, i, 1)) ^ + GFx0B(MCB(x, i, 2)) ^ GFx0D(MCB(x, i, 3)); + MCB(r, i, 2) = GFx0D(MCB(x, i, 0)) ^ GFx09(MCB(x, i, 1)) ^ + GFx0E(MCB(x, i, 2)) ^ GFx0B(MCB(x, i, 3)); + MCB(r, i, 3) = GFx0B(MCB(x, i, 0)) ^ GFx0D(MCB(x, i, 1)) ^ + GFx09(MCB(x, i, 2)) ^ GFx0E(MCB(x, i, 3)); + } +} + +static void ShiftRows(ppc_avr_t *r, ppc_avr_t *x) +{ + MCB(r, 0, 0) = MCB(x, 0, 0); + MCB(r, 1, 0) = MCB(x, 1, 0); + MCB(r, 2, 0) = MCB(x, 2, 0); + MCB(r, 3, 0) = MCB(x, 3, 0); + + MCB(r, 0, 1) = MCB(x, 1, 1); + MCB(r, 1, 1) = MCB(x, 2, 1); + MCB(r, 2, 1) = MCB(x, 3, 1); + MCB(r, 3, 1) = MCB(x, 0, 1); + + MCB(r, 0, 2) = MCB(x, 2, 2); + MCB(r, 1, 2) = MCB(x, 3, 2); + MCB(r, 2, 2) = MCB(x, 0, 2); + MCB(r, 3, 2) = MCB(x, 1, 2); + + MCB(r, 0, 3) = MCB(x, 3, 3); + MCB(r, 1, 3) = MCB(x, 0, 3); + MCB(r, 2, 3) = MCB(x, 1, 3); + MCB(r, 3, 3) = MCB(x, 2, 3); +} + +static void InvShiftRows(ppc_avr_t *r, ppc_avr_t *x) +{ + MCB(r, 0, 0) = MCB(x, 0, 0); + MCB(r, 1, 0) = MCB(x, 1, 0); + MCB(r, 2, 0) = MCB(x, 2, 0); + MCB(r, 3, 0) = MCB(x, 3, 0); + + MCB(r, 0, 1) = MCB(x, 3, 1); + MCB(r, 1, 1) = MCB(x, 0, 1); + MCB(r, 2, 1) = MCB(x, 1, 1); + MCB(r, 3, 1) = MCB(x, 2, 1); + + MCB(r, 0, 2) = MCB(x, 2, 2); + MCB(r, 1, 2) = MCB(x, 3, 2); + MCB(r, 2, 2) = MCB(x, 0, 2); + MCB(r, 3, 2) = MCB(x, 1, 2); + + MCB(r, 0, 3) = MCB(x, 1, 3); + MCB(r, 1, 3) = MCB(x, 2, 3); + MCB(r, 2, 3) = MCB(x, 3, 3); + MCB(r, 3, 3) = MCB(x, 0, 3); +} + +#undef MCB + +void helper_vsbox(ppc_avr_t *r, ppc_avr_t *a) +{ + SubBytes(r, a); +} + +void helper_vcipher(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + ppc_avr_t vtemp1, vtemp2, vtemp3; + SubBytes(&vtemp1, a); + ShiftRows(&vtemp2, &vtemp1); + MixColumns(&vtemp3, &vtemp2); + r->u64[0] = vtemp3.u64[0] ^ b->u64[0]; + r->u64[1] = vtemp3.u64[1] ^ b->u64[1]; +} + +void helper_vcipherlast(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + ppc_avr_t vtemp1, vtemp2; + SubBytes(&vtemp1, a); + ShiftRows(&vtemp2, &vtemp1); + r->u64[0] = vtemp2.u64[0] ^ b->u64[0]; + r->u64[1] = vtemp2.u64[1] ^ b->u64[1]; +} + +void helper_vncipher(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + /* This differs from what is written in ISA V2.07. The RTL is */ + /* incorrect and will be fixed in V2.07B. */ + ppc_avr_t vtemp1, vtemp2, vtemp3; + InvShiftRows(&vtemp1, a); + InvSubBytes(&vtemp2, &vtemp1); + vtemp3.u64[0] = vtemp2.u64[0] ^ b->u64[0]; + vtemp3.u64[1] = vtemp2.u64[1] ^ b->u64[1]; + InvMixColumns(r, &vtemp3); +} + +void helper_vncipherlast(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + ppc_avr_t vtemp1, vtemp2; + InvShiftRows(&vtemp1, a); + InvSubBytes(&vtemp2, &vtemp1); + r->u64[0] = vtemp2.u64[0] ^ b->u64[0]; + r->u64[1] = vtemp2.u64[1] ^ b->u64[1]; +} + +#define ROTRu32(v, n) (((v) >> (n)) | ((v) << (32-n))) +#if defined(HOST_WORDS_BIGENDIAN) +#define EL_IDX(i) (i) +#else +#define EL_IDX(i) (3 - (i)) +#endif + +void helper_vshasigmaw(ppc_avr_t *r, ppc_avr_t *a, uint32_t st_six) +{ + int st = (st_six & 0x10) != 0; + int six = st_six & 0xF; + int i; + + VECTOR_FOR_INORDER_I(i, u32) { + if (st == 0) { + if ((six & (0x8 >> i)) == 0) { + r->u32[EL_IDX(i)] = ROTRu32(a->u32[EL_IDX(i)], 7) ^ + ROTRu32(a->u32[EL_IDX(i)], 18) ^ + (a->u32[EL_IDX(i)] >> 3); + } else { /* six.bit[i] == 1 */ + r->u32[EL_IDX(i)] = ROTRu32(a->u32[EL_IDX(i)], 17) ^ + ROTRu32(a->u32[EL_IDX(i)], 19) ^ + (a->u32[EL_IDX(i)] >> 10); + } + } else { /* st == 1 */ + if ((six & (0x8 >> i)) == 0) { + r->u32[EL_IDX(i)] = ROTRu32(a->u32[EL_IDX(i)], 2) ^ + ROTRu32(a->u32[EL_IDX(i)], 13) ^ + ROTRu32(a->u32[EL_IDX(i)], 22); + } else { /* six.bit[i] == 1 */ + r->u32[EL_IDX(i)] = ROTRu32(a->u32[EL_IDX(i)], 6) ^ + ROTRu32(a->u32[EL_IDX(i)], 11) ^ + ROTRu32(a->u32[EL_IDX(i)], 25); + } + } + } +} + +#undef ROTRu32 +#undef EL_IDX + +#define ROTRu64(v, n) (((v) >> (n)) | ((v) << (64-n))) +#if defined(HOST_WORDS_BIGENDIAN) +#define EL_IDX(i) (i) +#else +#define EL_IDX(i) (1 - (i)) +#endif + +void helper_vshasigmad(ppc_avr_t *r, ppc_avr_t *a, uint32_t st_six) +{ + int st = (st_six & 0x10) != 0; + int six = st_six & 0xF; + int i; + + VECTOR_FOR_INORDER_I(i, u64) { + if (st == 0) { + if ((six & (0x8 >> (2*i))) == 0) { + r->u64[EL_IDX(i)] = ROTRu64(a->u64[EL_IDX(i)], 1) ^ + ROTRu64(a->u64[EL_IDX(i)], 8) ^ + (a->u64[EL_IDX(i)] >> 7); + } else { /* six.bit[2*i] == 1 */ + r->u64[EL_IDX(i)] = ROTRu64(a->u64[EL_IDX(i)], 19) ^ + ROTRu64(a->u64[EL_IDX(i)], 61) ^ + (a->u64[EL_IDX(i)] >> 6); + } + } else { /* st == 1 */ + if ((six & (0x8 >> (2*i))) == 0) { + r->u64[EL_IDX(i)] = ROTRu64(a->u64[EL_IDX(i)], 28) ^ + ROTRu64(a->u64[EL_IDX(i)], 34) ^ + ROTRu64(a->u64[EL_IDX(i)], 39); + } else { /* six.bit[2*i] == 1 */ + r->u64[EL_IDX(i)] = ROTRu64(a->u64[EL_IDX(i)], 14) ^ + ROTRu64(a->u64[EL_IDX(i)], 18) ^ + ROTRu64(a->u64[EL_IDX(i)], 41); + } + } + } +} + +#undef ROTRu64 +#undef EL_IDX + +void helper_vpermxor(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +{ + int i; + VECTOR_FOR_INORDER_I(i, u8) { + int indexA = c->u8[i] >> 4; + int indexB = c->u8[i] & 0xF; +#if defined(HOST_WORDS_BIGENDIAN) + r->u8[i] = a->u8[indexA] ^ b->u8[indexB]; +#else + r->u8[i] = a->u8[15-indexA] ^ b->u8[15-indexB]; +#endif + } +} + #undef VECTOR_FOR_INORDER_I #undef HI_IDX #undef LO_IDX diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 7af3fe277d..32e7a8c0a7 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -36,6 +36,7 @@ #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" #include "sysemu/watchdog.h" +#include "trace.h" //#define DEBUG_KVM @@ -401,7 +402,7 @@ static inline void kvm_fixup_page_sizes(PowerPCCPU *cpu) unsigned long kvm_arch_vcpu_id(CPUState *cpu) { - return cpu->cpu_index; + return ppc_get_vcpu_dt_id(POWERPC_CPU(cpu)); } int kvm_arch_init_vcpu(CPUState *cs) @@ -480,8 +481,7 @@ static void kvm_get_one_spr(CPUState *cs, uint64_t id, int spr) ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); if (ret != 0) { - fprintf(stderr, "Warning: Unable to retrieve SPR %d from KVM: %s\n", - spr, strerror(errno)); + trace_kvm_failed_spr_get(spr, strerror(errno)); } else { switch (id & KVM_REG_SIZE_MASK) { case KVM_REG_SIZE_U32: @@ -529,8 +529,7 @@ static void kvm_put_one_spr(CPUState *cs, uint64_t id, int spr) ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); if (ret != 0) { - fprintf(stderr, "Warning: Unable to set SPR %d to KVM: %s\n", - spr, strerror(errno)); + trace_kvm_failed_spr_set(spr, strerror(errno)); } } @@ -820,6 +819,9 @@ int kvm_arch_put_registers(CPUState *cs, int level) #ifdef TARGET_PPC64 for (i = 0; i < ARRAY_SIZE(env->slb); i++) { sregs.u.s.ppc64.slb[i].slbe = env->slb[i].esid; + if (env->slb[i].esid & SLB_ESID_V) { + sregs.u.s.ppc64.slb[i].slbe |= i; + } sregs.u.s.ppc64.slb[i].slbv = env->slb[i].vsid; } #endif @@ -1029,7 +1031,9 @@ int kvm_arch_get_registers(CPUState *cs) return ret; } - ppc_store_sdr1(env, sregs.u.s.sdr1); + if (!env->external_htab) { + ppc_store_sdr1(env, sregs.u.s.sdr1); + } /* Sync SLB */ #ifdef TARGET_PPC64 @@ -1766,22 +1770,14 @@ static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data) } } -int kvmppc_fixup_cpu(PowerPCCPU *cpu) +bool kvmppc_has_cap_epr(void) { - CPUState *cs = CPU(cpu); - int smt; - - /* Adjust cpu index for SMT */ - smt = kvmppc_smt_threads(); - cs->cpu_index = (cs->cpu_index / smp_threads) * smt - + (cs->cpu_index % smp_threads); - - return 0; + return cap_epr; } -bool kvmppc_has_cap_epr(void) +bool kvmppc_has_cap_htab_fd(void) { - return cap_epr; + return cap_htab_fd; } static int kvm_ppc_register_host_cpu_type(void) @@ -1934,3 +1930,88 @@ void kvm_arch_remove_all_hw_breakpoints(void) void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg) { } + +struct kvm_get_htab_buf { + struct kvm_get_htab_header header; + /* + * We require one extra byte for read + */ + target_ulong hpte[(HPTES_PER_GROUP * 2) + 1]; +}; + +uint64_t kvmppc_hash64_read_pteg(PowerPCCPU *cpu, target_ulong pte_index) +{ + int htab_fd; + struct kvm_get_htab_fd ghf; + struct kvm_get_htab_buf *hpte_buf; + + ghf.flags = 0; + ghf.start_index = pte_index; + htab_fd = kvm_vm_ioctl(kvm_state, KVM_PPC_GET_HTAB_FD, &ghf); + if (htab_fd < 0) { + goto error_out; + } + + hpte_buf = g_malloc0(sizeof(*hpte_buf)); + /* + * Read the hpte group + */ + if (read(htab_fd, hpte_buf, sizeof(*hpte_buf)) < 0) { + goto out_close; + } + + close(htab_fd); + return (uint64_t)(uintptr_t) hpte_buf->hpte; + +out_close: + g_free(hpte_buf); + close(htab_fd); +error_out: + return 0; +} + +void kvmppc_hash64_free_pteg(uint64_t token) +{ + struct kvm_get_htab_buf *htab_buf; + + htab_buf = container_of((void *)(uintptr_t) token, struct kvm_get_htab_buf, + hpte); + g_free(htab_buf); + return; +} + +void kvmppc_hash64_write_pte(CPUPPCState *env, target_ulong pte_index, + target_ulong pte0, target_ulong pte1) +{ + int htab_fd; + struct kvm_get_htab_fd ghf; + struct kvm_get_htab_buf hpte_buf; + + ghf.flags = 0; + ghf.start_index = 0; /* Ignored */ + htab_fd = kvm_vm_ioctl(kvm_state, KVM_PPC_GET_HTAB_FD, &ghf); + if (htab_fd < 0) { + goto error_out; + } + + hpte_buf.header.n_valid = 1; + hpte_buf.header.n_invalid = 0; + hpte_buf.header.index = pte_index; + hpte_buf.hpte[0] = pte0; + hpte_buf.hpte[1] = pte1; + /* + * Write the hpte entry. + * CAUTION: write() has the warn_unused_result attribute. Hence we + * need to check the return value, even though we do nothing. + */ + if (write(htab_fd, &hpte_buf, sizeof(hpte_buf)) < 0) { + goto out_close; + } + +out_close: + close(htab_fd); + return; + +error_out: + return; +} diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h index 5f78e4be14..ff077ec502 100644 --- a/target-ppc/kvm_ppc.h +++ b/target-ppc/kvm_ppc.h @@ -36,13 +36,18 @@ int kvmppc_remove_spapr_tce(void *table, int pfd, uint32_t window_size); int kvmppc_reset_htab(int shift_hint); uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift); #endif /* !CONFIG_USER_ONLY */ -int kvmppc_fixup_cpu(PowerPCCPU *cpu); bool kvmppc_has_cap_epr(void); int kvmppc_define_rtas_kernel_token(uint32_t token, const char *function); +bool kvmppc_has_cap_htab_fd(void); int kvmppc_get_htab_fd(bool write); int kvmppc_save_htab(QEMUFile *f, int fd, size_t bufsize, int64_t max_ns); int kvmppc_load_htab_chunk(QEMUFile *f, int fd, uint32_t index, uint16_t n_valid, uint16_t n_invalid); +uint64_t kvmppc_hash64_read_pteg(PowerPCCPU *cpu, target_ulong pte_index); +void kvmppc_hash64_free_pteg(uint64_t token); + +void kvmppc_hash64_write_pte(CPUPPCState *env, target_ulong pte_index, + target_ulong pte0, target_ulong pte1); #else @@ -155,11 +160,6 @@ static inline int kvmppc_update_sdr1(CPUPPCState *env) #endif /* !CONFIG_USER_ONLY */ -static inline int kvmppc_fixup_cpu(PowerPCCPU *cpu) -{ - return -1; -} - static inline bool kvmppc_has_cap_epr(void) { return false; @@ -171,6 +171,11 @@ static inline int kvmppc_define_rtas_kernel_token(uint32_t token, return -1; } +static inline bool kvmppc_has_cap_htab_fd(void) +{ + return false; +} + static inline int kvmppc_get_htab_fd(bool write) { return -1; @@ -188,6 +193,24 @@ static inline int kvmppc_load_htab_chunk(QEMUFile *f, int fd, uint32_t index, abort(); } +static inline uint64_t kvmppc_hash64_read_pteg(PowerPCCPU *cpu, + target_ulong pte_index) +{ + abort(); +} + +static inline void kvmppc_hash64_free_pteg(uint64_t token) +{ + abort(); +} + +static inline void kvmppc_hash64_write_pte(CPUPPCState *env, + target_ulong pte_index, + target_ulong pte0, target_ulong pte1) +{ + abort(); +} + #endif #ifndef CONFIG_KVM diff --git a/target-ppc/machine.c b/target-ppc/machine.c index 12c174f7f3..2d46ceccca 100644 --- a/target-ppc/machine.c +++ b/target-ppc/machine.c @@ -70,7 +70,9 @@ static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) qemu_get_betls(f, &env->pb[i]); for (i = 0; i < 1024; i++) qemu_get_betls(f, &env->spr[i]); - ppc_store_sdr1(env, sdr1); + if (!env->external_htab) { + ppc_store_sdr1(env, sdr1); + } qemu_get_be32s(f, &env->vscr); qemu_get_be64s(f, &env->spe_acc); qemu_get_be32s(f, &env->spe_fscr); @@ -179,9 +181,10 @@ static int cpu_post_load(void *opaque, int version_id) env->IBAT[1][i+4] = env->spr[SPR_IBAT4U + 2*i + 1]; } - /* Restore htab_base and htab_mask variables */ - ppc_store_sdr1(env, env->spr[SPR_SDR1]); - + if (!env->external_htab) { + /* Restore htab_base and htab_mask variables */ + ppc_store_sdr1(env, env->spr[SPR_SDR1]); + } hreg_compute_hflags(env); hreg_compute_mem_idx(env); diff --git a/target-ppc/misc_helper.c b/target-ppc/misc_helper.c index 616aab6fb6..dc2ebfc452 100644 --- a/target-ppc/misc_helper.c +++ b/target-ppc/misc_helper.c @@ -38,7 +38,9 @@ void helper_store_dump_spr(CPUPPCState *env, uint32_t sprn) void helper_store_sdr1(CPUPPCState *env, target_ulong val) { - ppc_store_sdr1(env, val); + if (!env->external_htab) { + ppc_store_sdr1(env, val); + } } void helper_store_hid0_601(CPUPPCState *env, target_ulong val) diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 67fc1b5dec..f2af4fbaa7 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -41,6 +41,11 @@ #endif /* + * Used to indicate whether we have allocated htab in the + * host kernel + */ +bool kvmppc_kern_htab; +/* * SLB handling */ @@ -278,12 +283,12 @@ static int ppc_hash64_pte_prot(CPUPPCState *env, static int ppc_hash64_amr_prot(CPUPPCState *env, ppc_hash_pte64_t pte) { int key, amrbits; - int prot = PAGE_EXEC; + int prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; /* Only recent MMUs implement Virtual Page Class Key Protection */ if (!(env->mmu_model & POWERPC_MMU_AMR)) { - return PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return prot; } key = HPTE64_R_KEY(pte.pte1); @@ -292,39 +297,94 @@ static int ppc_hash64_amr_prot(CPUPPCState *env, ppc_hash_pte64_t pte) /* fprintf(stderr, "AMR protection: key=%d AMR=0x%" PRIx64 "\n", key, */ /* env->spr[SPR_AMR]); */ + /* + * A store is permitted if the AMR bit is 0. Remove write + * protection if it is set. + */ if (amrbits & 0x2) { - prot |= PAGE_WRITE; + prot &= ~PAGE_WRITE; } + /* + * A load is permitted if the AMR bit is 0. Remove read + * protection if it is set. + */ if (amrbits & 0x1) { - prot |= PAGE_READ; + prot &= ~PAGE_READ; } return prot; } -static hwaddr ppc_hash64_pteg_search(CPUPPCState *env, hwaddr pteg_off, +uint64_t ppc_hash64_start_access(PowerPCCPU *cpu, target_ulong pte_index) +{ + uint64_t token = 0; + hwaddr pte_offset; + + pte_offset = pte_index * HASH_PTE_SIZE_64; + if (kvmppc_kern_htab) { + /* + * HTAB is controlled by KVM. Fetch the PTEG into a new buffer. + */ + token = kvmppc_hash64_read_pteg(cpu, pte_index); + if (token) { + return token; + } + /* + * pteg read failed, even though we have allocated htab via + * kvmppc_reset_htab. + */ + return 0; + } + /* + * HTAB is controlled by QEMU. Just point to the internally + * accessible PTEG. + */ + if (cpu->env.external_htab) { + token = (uint64_t)(uintptr_t) cpu->env.external_htab + pte_offset; + } else if (cpu->env.htab_base) { + token = cpu->env.htab_base + pte_offset; + } + return token; +} + +void ppc_hash64_stop_access(uint64_t token) +{ + if (kvmppc_kern_htab) { + return kvmppc_hash64_free_pteg(token); + } +} + +static hwaddr ppc_hash64_pteg_search(CPUPPCState *env, hwaddr hash, bool secondary, target_ulong ptem, ppc_hash_pte64_t *pte) { - hwaddr pte_offset = pteg_off; - target_ulong pte0, pte1; int i; + uint64_t token; + target_ulong pte0, pte1; + target_ulong pte_index; + pte_index = (hash & env->htab_mask) * HPTES_PER_GROUP; + token = ppc_hash64_start_access(ppc_env_get_cpu(env), pte_index); + if (!token) { + return -1; + } for (i = 0; i < HPTES_PER_GROUP; i++) { - pte0 = ppc_hash64_load_hpte0(env, pte_offset); - pte1 = ppc_hash64_load_hpte1(env, pte_offset); + pte0 = ppc_hash64_load_hpte0(env, token, i); + pte1 = ppc_hash64_load_hpte1(env, token, i); if ((pte0 & HPTE64_V_VALID) && (secondary == !!(pte0 & HPTE64_V_SECONDARY)) && HPTE64_V_COMPARE(pte0, ptem)) { pte->pte0 = pte0; pte->pte1 = pte1; - return pte_offset; + ppc_hash64_stop_access(token); + return (pte_index + i) * HASH_PTE_SIZE_64; } - - pte_offset += HASH_PTE_SIZE_64; } - + ppc_hash64_stop_access(token); + /* + * We didn't find a valid entry. + */ return -1; } @@ -332,7 +392,7 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, ppc_slb_t *slb, target_ulong eaddr, ppc_hash_pte64_t *pte) { - hwaddr pteg_off, pte_offset; + hwaddr pte_offset; hwaddr hash; uint64_t vsid, epnshift, epnmask, epn, ptem; @@ -367,8 +427,7 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ptem, hash); - pteg_off = (hash * HASH_PTEG_SIZE_64) & env->htab_mask; - pte_offset = ppc_hash64_pteg_search(env, pteg_off, 0, ptem, pte); + pte_offset = ppc_hash64_pteg_search(env, hash, 0, ptem, pte); if (pte_offset == -1) { /* Secondary PTEG lookup */ @@ -377,8 +436,7 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ptem, ~hash); - pteg_off = (~hash * HASH_PTEG_SIZE_64) & env->htab_mask; - pte_offset = ppc_hash64_pteg_search(env, pteg_off, 1, ptem, pte); + pte_offset = ppc_hash64_pteg_search(env, ~hash, 1, ptem, pte); } return pte_offset; @@ -508,7 +566,8 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr, } if (new_pte1 != pte.pte1) { - ppc_hash64_store_hpte1(env, pte_offset, new_pte1); + ppc_hash64_store_hpte(env, pte_offset / HASH_PTE_SIZE_64, + pte.pte0, new_pte1); } /* 7. Determine the real address from the PTE */ @@ -544,3 +603,23 @@ hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr) return ppc_hash64_pte_raddr(slb, pte, addr) & TARGET_PAGE_MASK; } + +void ppc_hash64_store_hpte(CPUPPCState *env, + target_ulong pte_index, + target_ulong pte0, target_ulong pte1) +{ + CPUState *cs = ENV_GET_CPU(env); + + if (kvmppc_kern_htab) { + return kvmppc_hash64_write_pte(env, pte_index, pte0, pte1); + } + + pte_index *= HASH_PTE_SIZE_64; + if (env->external_htab) { + stq_p(env->external_htab + pte_index, pte0); + stq_p(env->external_htab + pte_index + HASH_PTE_SIZE_64/2, pte1); + } else { + stq_phys(cs->as, env->htab_base + pte_index, pte0); + stq_phys(cs->as, env->htab_base + pte_index + HASH_PTE_SIZE_64/2, pte1); + } +} diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h index a8da558ca2..1746b3e2d3 100644 --- a/target-ppc/mmu-hash64.h +++ b/target-ppc/mmu-hash64.h @@ -9,6 +9,8 @@ int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs); hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr); int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, int mmu_idx); +void ppc_hash64_store_hpte(CPUPPCState *env, target_ulong index, + target_ulong pte0, target_ulong pte1); #endif /* @@ -75,49 +77,34 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, #define HPTE64_V_1TB_SEG 0x4000000000000000ULL #define HPTE64_V_VRMA_MASK 0x4001ffffff000000ULL -static inline target_ulong ppc_hash64_load_hpte0(CPUPPCState *env, - hwaddr pte_offset) -{ - CPUState *cs = ENV_GET_CPU(env); - if (env->external_htab) { - return ldq_p(env->external_htab + pte_offset); - } else { - return ldq_phys(cs->as, env->htab_base + pte_offset); - } -} -static inline target_ulong ppc_hash64_load_hpte1(CPUPPCState *env, - hwaddr pte_offset) -{ - CPUState *cs = ENV_GET_CPU(env); - if (env->external_htab) { - return ldq_p(env->external_htab + pte_offset + HASH_PTE_SIZE_64/2); - } else { - return ldq_phys(cs->as, - env->htab_base + pte_offset + HASH_PTE_SIZE_64/2); - } -} +extern bool kvmppc_kern_htab; +uint64_t ppc_hash64_start_access(PowerPCCPU *cpu, target_ulong pte_index); +void ppc_hash64_stop_access(uint64_t token); -static inline void ppc_hash64_store_hpte0(CPUPPCState *env, - hwaddr pte_offset, target_ulong pte0) +static inline target_ulong ppc_hash64_load_hpte0(CPUPPCState *env, + uint64_t token, int index) { CPUState *cs = ENV_GET_CPU(env); + uint64_t addr; + addr = token + (index * HASH_PTE_SIZE_64); if (env->external_htab) { - stq_p(env->external_htab + pte_offset, pte0); + return ldq_p((const void *)(uintptr_t)addr); } else { - stq_phys(cs->as, env->htab_base + pte_offset, pte0); + return ldq_phys(cs->as, addr); } } -static inline void ppc_hash64_store_hpte1(CPUPPCState *env, - hwaddr pte_offset, target_ulong pte1) +static inline target_ulong ppc_hash64_load_hpte1(CPUPPCState *env, + uint64_t token, int index) { CPUState *cs = ENV_GET_CPU(env); + uint64_t addr; + addr = token + (index * HASH_PTE_SIZE_64) + HASH_PTE_SIZE_64/2; if (env->external_htab) { - stq_p(env->external_htab + pte_offset + HASH_PTE_SIZE_64/2, pte1); + return ldq_p((const void *)(uintptr_t)addr); } else { - stq_phys(cs->as, - env->htab_base + pte_offset + HASH_PTE_SIZE_64/2, pte1); + return ldq_phys(cs->as, addr); } } diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 04a840b016..8e2f8e736a 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -2014,6 +2014,7 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) void ppc_store_sdr1(CPUPPCState *env, target_ulong value) { LOG_MMU("%s: " TARGET_FMT_lx "\n", __func__, value); + assert(!env->external_htab); if (env->spr[SPR_SDR1] != value) { env->spr[SPR_SDR1] = value; #if defined(TARGET_PPC64) @@ -2025,7 +2026,7 @@ void ppc_store_sdr1(CPUPPCState *env, target_ulong value) " stored in SDR1\n", htabsize); htabsize = 28; } - env->htab_mask = (1ULL << (htabsize + 18)) - 1; + env->htab_mask = (1ULL << (htabsize + 18 - 7)) - 1; env->htab_base = value & SDR_64_HTABORG; } else #endif /* defined(TARGET_PPC64) */ diff --git a/target-ppc/translate.c b/target-ppc/translate.c index c5c1108e92..91c33dcd1d 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -387,6 +387,8 @@ EXTRACT_HELPER(opc2, 1, 5); EXTRACT_HELPER(opc3, 6, 5); /* Update Cr0 flags */ EXTRACT_HELPER(Rc, 0, 1); +/* Update Cr6 flags (Altivec) */ +EXTRACT_HELPER(Rc21, 10, 1); /* Destination */ EXTRACT_HELPER(rD, 21, 5); /* Source */ @@ -622,6 +624,20 @@ static opc_handler_t invalid_handler = { .handler = gen_invalid, }; +#if defined(TARGET_PPC64) +/* NOTE: as this time, the only use of is_user_mode() is in 64 bit code. And */ +/* so the function is wrapped in the standard 64-bit ifdef in order to */ +/* avoid compiler warnings in 32-bit implementations. */ +static bool is_user_mode(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + return true; +#else + return ctx->mem_idx == 0; +#endif +} +#endif + /*** Integer comparison ***/ static inline void gen_op_cmp(TCGv arg0, TCGv arg1, int s, int crf) @@ -984,6 +1000,25 @@ GEN_INT_ARITH_DIVW(divwuo, 0x1E, 0, 1); /* divw divw. divwo divwo. */ GEN_INT_ARITH_DIVW(divw, 0x0F, 1, 0); GEN_INT_ARITH_DIVW(divwo, 0x1F, 1, 1); + +/* div[wd]eu[o][.] */ +#define GEN_DIVE(name, hlpr, compute_ov) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv_i32 t0 = tcg_const_i32(compute_ov); \ + gen_helper_##hlpr(cpu_gpr[rD(ctx->opcode)], cpu_env, \ + cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0); \ + tcg_temp_free_i32(t0); \ + if (unlikely(Rc(ctx->opcode) != 0)) { \ + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); \ + } \ +} + +GEN_DIVE(divweu, divweu, 0); +GEN_DIVE(divweuo, divweu, 1); +GEN_DIVE(divwe, divwe, 0); +GEN_DIVE(divweo, divwe, 1); + #if defined(TARGET_PPC64) static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, TCGv arg1, TCGv arg2, int sign, int compute_ov) @@ -1032,6 +1067,11 @@ GEN_INT_ARITH_DIVD(divduo, 0x1E, 0, 1); /* divw divw. divwo divwo. */ GEN_INT_ARITH_DIVD(divd, 0x0F, 1, 0); GEN_INT_ARITH_DIVD(divdo, 0x1F, 1, 1); + +GEN_DIVE(divdeu, divdeu, 0); +GEN_DIVE(divdeuo, divdeu, 1); +GEN_DIVE(divde, divde, 0); +GEN_DIVE(divdeo, divde, 1); #endif /* mulhw mulhw. */ @@ -1525,6 +1565,15 @@ static void gen_prtyd(DisasContext *ctx) #endif #if defined(TARGET_PPC64) +/* bpermd */ +static void gen_bpermd(DisasContext *ctx) +{ + gen_helper_bpermd(cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); +} +#endif + +#if defined(TARGET_PPC64) /* extsw & extsw. */ GEN_LOGICAL1(extsw, tcg_gen_ext32s_tl, 0x1E, PPC_64B); @@ -2169,17 +2218,31 @@ GEN_FLOAT_ACB(nmsub, 0x1E, 1, PPC_FLOAT); /*** Floating-Point round & convert ***/ /* fctiw */ GEN_FLOAT_B(ctiw, 0x0E, 0x00, 0, PPC_FLOAT); +/* fctiwu */ +GEN_FLOAT_B(ctiwu, 0x0E, 0x04, 0, PPC2_FP_CVT_ISA206); /* fctiwz */ GEN_FLOAT_B(ctiwz, 0x0F, 0x00, 0, PPC_FLOAT); +/* fctiwuz */ +GEN_FLOAT_B(ctiwuz, 0x0F, 0x04, 0, PPC2_FP_CVT_ISA206); /* frsp */ GEN_FLOAT_B(rsp, 0x0C, 0x00, 1, PPC_FLOAT); #if defined(TARGET_PPC64) /* fcfid */ GEN_FLOAT_B(cfid, 0x0E, 0x1A, 1, PPC_64B); +/* fcfids */ +GEN_FLOAT_B(cfids, 0x0E, 0x1A, 0, PPC2_FP_CVT_ISA206); +/* fcfidu */ +GEN_FLOAT_B(cfidu, 0x0E, 0x1E, 0, PPC2_FP_CVT_ISA206); +/* fcfidus */ +GEN_FLOAT_B(cfidus, 0x0E, 0x1E, 0, PPC2_FP_CVT_ISA206); /* fctid */ GEN_FLOAT_B(ctid, 0x0E, 0x19, 0, PPC_64B); +/* fctidu */ +GEN_FLOAT_B(ctidu, 0x0E, 0x1D, 0, PPC2_FP_CVT_ISA206); /* fctidz */ GEN_FLOAT_B(ctidz, 0x0F, 0x19, 0, PPC_64B); +/* fctidu */ +GEN_FLOAT_B(ctiduz, 0x0F, 0x1D, 0, PPC2_FP_CVT_ISA206); #endif /* frin */ @@ -2191,6 +2254,27 @@ GEN_FLOAT_B(rip, 0x08, 0x0E, 1, PPC_FLOAT_EXT); /* frim */ GEN_FLOAT_B(rim, 0x08, 0x0F, 1, PPC_FLOAT_EXT); +static void gen_ftdiv(DisasContext *ctx) +{ + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_helper_ftdiv(cpu_crf[crfD(ctx->opcode)], cpu_fpr[rA(ctx->opcode)], + cpu_fpr[rB(ctx->opcode)]); +} + +static void gen_ftsqrt(DisasContext *ctx) +{ + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_helper_ftsqrt(cpu_crf[crfD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]); +} + + + /*** Floating-Point compare ***/ /* fcmpo */ @@ -2294,6 +2378,32 @@ static void gen_fcpsgn(DisasContext *ctx) gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 0, Rc(ctx->opcode) != 0); } +static void gen_fmrgew(DisasContext *ctx) +{ + TCGv_i64 b0; + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + b0 = tcg_temp_new_i64(); + tcg_gen_shri_i64(b0, cpu_fpr[rB(ctx->opcode)], 32); + tcg_gen_deposit_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rA(ctx->opcode)], + b0, 0, 32); + tcg_temp_free_i64(b0); +} + +static void gen_fmrgow(DisasContext *ctx) +{ + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + tcg_gen_deposit_i64(cpu_fpr[rD(ctx->opcode)], + cpu_fpr[rB(ctx->opcode)], + cpu_fpr[rA(ctx->opcode)], + 32, 32); +} + /*** Floating-Point status & ctrl register ***/ /* mcrfs */ @@ -2585,6 +2695,14 @@ static inline void gen_qemu_ld32s(DisasContext *ctx, TCGv arg1, TCGv arg2) tcg_gen_qemu_ld32s(arg1, arg2, ctx->mem_idx); } +static void gen_qemu_ld32s_i64(DisasContext *ctx, TCGv_i64 val, TCGv addr) +{ + TCGv tmp = tcg_temp_new(); + gen_qemu_ld32s(ctx, tmp, addr); + tcg_gen_ext_tl_i64(val, tmp); + tcg_temp_free(tmp); +} + static inline void gen_qemu_ld64(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2) { tcg_gen_qemu_ld64(arg1, arg2, ctx->mem_idx); @@ -2756,36 +2874,44 @@ static void gen_ld(DisasContext *ctx) /* lq */ static void gen_lq(DisasContext *ctx) { -#if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -#else int ra, rd; TCGv EA; - /* Restore CPU state */ - if (unlikely(ctx->mem_idx == 0)) { + /* lq is a legal user mode instruction starting in ISA 2.07 */ + bool legal_in_user_mode = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; + bool le_is_supported = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; + + if (!legal_in_user_mode && is_user_mode(ctx)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } + + if (!le_is_supported && ctx->le_mode) { + gen_exception_err(ctx, POWERPC_EXCP_ALIGN, POWERPC_EXCP_ALIGN_LE); + return; + } + ra = rA(ctx->opcode); rd = rD(ctx->opcode); if (unlikely((rd & 1) || rd == ra)) { gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); return; } - if (unlikely(ctx->le_mode)) { - /* Little-endian mode is not handled */ - gen_exception_err(ctx, POWERPC_EXCP_ALIGN, POWERPC_EXCP_ALIGN_LE); - return; - } + gen_set_access_type(ctx, ACCESS_INT); EA = tcg_temp_new(); gen_addr_imm_index(ctx, EA, 0x0F); - gen_qemu_ld64(ctx, cpu_gpr[rd], EA); - gen_addr_add(ctx, EA, EA, 8); - gen_qemu_ld64(ctx, cpu_gpr[rd+1], EA); + + if (unlikely(ctx->le_mode)) { + gen_qemu_ld64(ctx, cpu_gpr[rd+1], EA); + gen_addr_add(ctx, EA, EA, 8); + gen_qemu_ld64(ctx, cpu_gpr[rd], EA); + } else { + gen_qemu_ld64(ctx, cpu_gpr[rd], EA); + gen_addr_add(ctx, EA, EA, 8); + gen_qemu_ld64(ctx, cpu_gpr[rd+1], EA); + } tcg_temp_free(EA); -#endif } #endif @@ -2871,34 +2997,41 @@ static void gen_std(DisasContext *ctx) TCGv EA; rs = rS(ctx->opcode); - if ((ctx->opcode & 0x3) == 0x2) { -#if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -#else - /* stq */ - if (unlikely(ctx->mem_idx == 0)) { + if ((ctx->opcode & 0x3) == 0x2) { /* stq */ + + bool legal_in_user_mode = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; + bool le_is_supported = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; + + if (!legal_in_user_mode && is_user_mode(ctx)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } - if (unlikely(rs & 1)) { - gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + + if (!le_is_supported && ctx->le_mode) { + gen_exception_err(ctx, POWERPC_EXCP_ALIGN, POWERPC_EXCP_ALIGN_LE); return; } - if (unlikely(ctx->le_mode)) { - /* Little-endian mode is not handled */ - gen_exception_err(ctx, POWERPC_EXCP_ALIGN, POWERPC_EXCP_ALIGN_LE); + + if (unlikely(rs & 1)) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); return; } gen_set_access_type(ctx, ACCESS_INT); EA = tcg_temp_new(); gen_addr_imm_index(ctx, EA, 0x03); - gen_qemu_st64(ctx, cpu_gpr[rs], EA); - gen_addr_add(ctx, EA, EA, 8); - gen_qemu_st64(ctx, cpu_gpr[rs+1], EA); + + if (unlikely(ctx->le_mode)) { + gen_qemu_st64(ctx, cpu_gpr[rs+1], EA); + gen_addr_add(ctx, EA, EA, 8); + gen_qemu_st64(ctx, cpu_gpr[rs], EA); + } else { + gen_qemu_st64(ctx, cpu_gpr[rs], EA); + gen_addr_add(ctx, EA, EA, 8); + gen_qemu_st64(ctx, cpu_gpr[rs+1], EA); + } tcg_temp_free(EA); -#endif } else { - /* std / stdu */ + /* std / stdu*/ if (Rc(ctx->opcode)) { if (unlikely(rA(ctx->opcode) == 0)) { gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); @@ -3140,24 +3273,32 @@ static void gen_isync(DisasContext *ctx) gen_stop_exception(ctx); } -/* lwarx */ -static void gen_lwarx(DisasContext *ctx) -{ - TCGv t0; - TCGv gpr = cpu_gpr[rD(ctx->opcode)]; - gen_set_access_type(ctx, ACCESS_RES); - t0 = tcg_temp_local_new(); - gen_addr_reg_index(ctx, t0); - gen_check_align(ctx, t0, 0x03); - gen_qemu_ld32u(ctx, gpr, t0); - tcg_gen_mov_tl(cpu_reserve, t0); - tcg_gen_st_tl(gpr, cpu_env, offsetof(CPUPPCState, reserve_val)); - tcg_temp_free(t0); +#define LARX(name, len, loadop) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv t0; \ + TCGv gpr = cpu_gpr[rD(ctx->opcode)]; \ + gen_set_access_type(ctx, ACCESS_RES); \ + t0 = tcg_temp_local_new(); \ + gen_addr_reg_index(ctx, t0); \ + if ((len) > 1) { \ + gen_check_align(ctx, t0, (len)-1); \ + } \ + gen_qemu_##loadop(ctx, gpr, t0); \ + tcg_gen_mov_tl(cpu_reserve, t0); \ + tcg_gen_st_tl(gpr, cpu_env, offsetof(CPUPPCState, reserve_val)); \ + tcg_temp_free(t0); \ } +/* lwarx */ +LARX(lbarx, 1, ld8u); +LARX(lharx, 2, ld16u); +LARX(lwarx, 4, ld32u); + + #if defined(CONFIG_USER_ONLY) -static void gen_conditional_store (DisasContext *ctx, TCGv EA, - int reg, int size) +static void gen_conditional_store(DisasContext *ctx, TCGv EA, + int reg, int size) { TCGv t0 = tcg_temp_new(); uint32_t save_exception = ctx->exception; @@ -3171,74 +3312,115 @@ static void gen_conditional_store (DisasContext *ctx, TCGv EA, gen_exception(ctx, POWERPC_EXCP_STCX); ctx->exception = save_exception; } -#endif - -/* stwcx. */ -static void gen_stwcx_(DisasContext *ctx) -{ - TCGv t0; - gen_set_access_type(ctx, ACCESS_RES); - t0 = tcg_temp_local_new(); - gen_addr_reg_index(ctx, t0); - gen_check_align(ctx, t0, 0x03); -#if defined(CONFIG_USER_ONLY) - gen_conditional_store(ctx, t0, rS(ctx->opcode), 4); #else - { - int l1; +static void gen_conditional_store(DisasContext *ctx, TCGv EA, + int reg, int size) +{ + int l1; - tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); - l1 = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_NE, t0, cpu_reserve, l1); - tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ); - gen_qemu_st32(ctx, cpu_gpr[rS(ctx->opcode)], t0); - gen_set_label(l1); - tcg_gen_movi_tl(cpu_reserve, -1); + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); + l1 = gen_new_label(); + tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, l1); + tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ); +#if defined(TARGET_PPC64) + if (size == 8) { + gen_qemu_st64(ctx, cpu_gpr[reg], EA); + } else +#endif + if (size == 4) { + gen_qemu_st32(ctx, cpu_gpr[reg], EA); + } else if (size == 2) { + gen_qemu_st16(ctx, cpu_gpr[reg], EA); +#if defined(TARGET_PPC64) + } else if (size == 16) { + TCGv gpr1, gpr2 , EA8; + if (unlikely(ctx->le_mode)) { + gpr1 = cpu_gpr[reg+1]; + gpr2 = cpu_gpr[reg]; + } else { + gpr1 = cpu_gpr[reg]; + gpr2 = cpu_gpr[reg+1]; + } + gen_qemu_st64(ctx, gpr1, EA); + EA8 = tcg_temp_local_new(); + gen_addr_add(ctx, EA8, EA, 8); + gen_qemu_st64(ctx, gpr2, EA8); + tcg_temp_free(EA8); +#endif + } else { + gen_qemu_st8(ctx, cpu_gpr[reg], EA); } + gen_set_label(l1); + tcg_gen_movi_tl(cpu_reserve, -1); +} #endif - tcg_temp_free(t0); + +#define STCX(name, len) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv t0; \ + if (unlikely((len == 16) && (rD(ctx->opcode) & 1))) { \ + gen_inval_exception(ctx, \ + POWERPC_EXCP_INVAL_INVAL); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_RES); \ + t0 = tcg_temp_local_new(); \ + gen_addr_reg_index(ctx, t0); \ + if (len > 1) { \ + gen_check_align(ctx, t0, (len)-1); \ + } \ + gen_conditional_store(ctx, t0, rS(ctx->opcode), len); \ + tcg_temp_free(t0); \ } +STCX(stbcx_, 1); +STCX(sthcx_, 2); +STCX(stwcx_, 4); + #if defined(TARGET_PPC64) /* ldarx */ -static void gen_ldarx(DisasContext *ctx) -{ - TCGv t0; - TCGv gpr = cpu_gpr[rD(ctx->opcode)]; - gen_set_access_type(ctx, ACCESS_RES); - t0 = tcg_temp_local_new(); - gen_addr_reg_index(ctx, t0); - gen_check_align(ctx, t0, 0x07); - gen_qemu_ld64(ctx, gpr, t0); - tcg_gen_mov_tl(cpu_reserve, t0); - tcg_gen_st_tl(gpr, cpu_env, offsetof(CPUPPCState, reserve_val)); - tcg_temp_free(t0); -} +LARX(ldarx, 8, ld64); -/* stdcx. */ -static void gen_stdcx_(DisasContext *ctx) +/* lqarx */ +static void gen_lqarx(DisasContext *ctx) { - TCGv t0; + TCGv EA; + int rd = rD(ctx->opcode); + TCGv gpr1, gpr2; + + if (unlikely((rd & 1) || (rd == rA(ctx->opcode)) || + (rd == rB(ctx->opcode)))) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + return; + } + gen_set_access_type(ctx, ACCESS_RES); - t0 = tcg_temp_local_new(); - gen_addr_reg_index(ctx, t0); - gen_check_align(ctx, t0, 0x07); -#if defined(CONFIG_USER_ONLY) - gen_conditional_store(ctx, t0, rS(ctx->opcode), 8); -#else - { - int l1; - tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); - l1 = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_NE, t0, cpu_reserve, l1); - tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ); - gen_qemu_st64(ctx, cpu_gpr[rS(ctx->opcode)], t0); - gen_set_label(l1); - tcg_gen_movi_tl(cpu_reserve, -1); + EA = tcg_temp_local_new(); + gen_addr_reg_index(ctx, EA); + gen_check_align(ctx, EA, 15); + if (unlikely(ctx->le_mode)) { + gpr1 = cpu_gpr[rd+1]; + gpr2 = cpu_gpr[rd]; + } else { + gpr1 = cpu_gpr[rd]; + gpr2 = cpu_gpr[rd+1]; } -#endif - tcg_temp_free(t0); + gen_qemu_ld64(ctx, gpr1, EA); + tcg_gen_mov_tl(cpu_reserve, EA); + + gen_addr_add(ctx, EA, EA, 8); + gen_qemu_ld64(ctx, gpr2, EA); + + tcg_gen_st_tl(gpr1, cpu_env, offsetof(CPUPPCState, reserve_val)); + tcg_gen_st_tl(gpr2, cpu_env, offsetof(CPUPPCState, reserve_val2)); + + tcg_temp_free(EA); } + +/* stdcx. */ +STCX(stdcx_, 8); +STCX(stqcx_, 16); #endif /* defined(TARGET_PPC64) */ /* sync */ @@ -3415,6 +3597,20 @@ static void gen_lfiwax(DisasContext *ctx) tcg_temp_free(t0); } +/* lfiwzx */ +static void gen_lfiwzx(DisasContext *ctx) +{ + TCGv EA; + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_set_access_type(ctx, ACCESS_FLOAT); + EA = tcg_temp_new(); + gen_addr_reg_index(ctx, EA); + gen_qemu_ld32u_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + tcg_temp_free(EA); +} /*** Floating-point store ***/ #define GEN_STF(name, stop, opc, type) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -3638,6 +3834,7 @@ static void gen_b(DisasContext *ctx) #define BCOND_IM 0 #define BCOND_LR 1 #define BCOND_CTR 2 +#define BCOND_TAR 3 static inline void gen_bcond(DisasContext *ctx, int type) { @@ -3646,10 +3843,12 @@ static inline void gen_bcond(DisasContext *ctx, int type) TCGv target; ctx->exception = POWERPC_EXCP_BRANCH; - if (type == BCOND_LR || type == BCOND_CTR) { + if (type == BCOND_LR || type == BCOND_CTR || type == BCOND_TAR) { target = tcg_temp_local_new(); if (type == BCOND_CTR) tcg_gen_mov_tl(target, cpu_ctr); + else if (type == BCOND_TAR) + gen_load_spr(target, SPR_TAR); else tcg_gen_mov_tl(target, cpu_lr); } else { @@ -3731,6 +3930,11 @@ static void gen_bclr(DisasContext *ctx) gen_bcond(ctx, BCOND_LR); } +static void gen_bctar(DisasContext *ctx) +{ + gen_bcond(ctx, BCOND_TAR); +} + /*** Condition register logical ***/ #define GEN_CRLOGIC(name, tcg_op, opc) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -6650,6 +6854,9 @@ GEN_VX_LOGICAL(vandc, tcg_gen_andc_i64, 2, 17); GEN_VX_LOGICAL(vor, tcg_gen_or_i64, 2, 18); GEN_VX_LOGICAL(vxor, tcg_gen_xor_i64, 2, 19); GEN_VX_LOGICAL(vnor, tcg_gen_nor_i64, 2, 20); +GEN_VX_LOGICAL(veqv, tcg_gen_eqv_i64, 2, 26); +GEN_VX_LOGICAL(vnand, tcg_gen_nand_i64, 2, 22); +GEN_VX_LOGICAL(vorc, tcg_gen_orc_i64, 2, 21); #define GEN_VXFORM(name, opc2, opc3) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -6685,24 +6892,69 @@ static void glue(gen_, name)(DisasContext *ctx) \ tcg_temp_free_ptr(rd); \ } +#define GEN_VXFORM3(name, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + TCGv_ptr ra, rb, rc, rd; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + ra = gen_avr_ptr(rA(ctx->opcode)); \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rc = gen_avr_ptr(rC(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + gen_helper_##name(rd, ra, rb, rc); \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rc); \ + tcg_temp_free_ptr(rd); \ +} + +/* + * Support for Altivec instruction pairs that use bit 31 (Rc) as + * an opcode bit. In general, these pairs come from different + * versions of the ISA, so we must also support a pair of flags for + * each instruction. + */ +#define GEN_VXFORM_DUAL(name0, flg0, flg2_0, name1, flg1, flg2_1) \ +static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ +{ \ + if ((Rc(ctx->opcode) == 0) && \ + ((ctx->insns_flags & flg0) || (ctx->insns_flags2 & flg2_0))) { \ + gen_##name0(ctx); \ + } else if ((Rc(ctx->opcode) == 1) && \ + ((ctx->insns_flags & flg1) || (ctx->insns_flags2 & flg2_1))) { \ + gen_##name1(ctx); \ + } else { \ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ + } \ +} + GEN_VXFORM(vaddubm, 0, 0); GEN_VXFORM(vadduhm, 0, 1); GEN_VXFORM(vadduwm, 0, 2); +GEN_VXFORM(vaddudm, 0, 3); GEN_VXFORM(vsububm, 0, 16); GEN_VXFORM(vsubuhm, 0, 17); GEN_VXFORM(vsubuwm, 0, 18); +GEN_VXFORM(vsubudm, 0, 19); GEN_VXFORM(vmaxub, 1, 0); GEN_VXFORM(vmaxuh, 1, 1); GEN_VXFORM(vmaxuw, 1, 2); +GEN_VXFORM(vmaxud, 1, 3); GEN_VXFORM(vmaxsb, 1, 4); GEN_VXFORM(vmaxsh, 1, 5); GEN_VXFORM(vmaxsw, 1, 6); +GEN_VXFORM(vmaxsd, 1, 7); GEN_VXFORM(vminub, 1, 8); GEN_VXFORM(vminuh, 1, 9); GEN_VXFORM(vminuw, 1, 10); +GEN_VXFORM(vminud, 1, 11); GEN_VXFORM(vminsb, 1, 12); GEN_VXFORM(vminsh, 1, 13); GEN_VXFORM(vminsw, 1, 14); +GEN_VXFORM(vminsd, 1, 15); GEN_VXFORM(vavgub, 1, 16); GEN_VXFORM(vavguh, 1, 17); GEN_VXFORM(vavguw, 1, 18); @@ -6715,23 +6967,68 @@ GEN_VXFORM(vmrghw, 6, 2); GEN_VXFORM(vmrglb, 6, 4); GEN_VXFORM(vmrglh, 6, 5); GEN_VXFORM(vmrglw, 6, 6); + +static void gen_vmrgew(DisasContext *ctx) +{ + TCGv_i64 tmp; + int VT, VA, VB; + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + VT = rD(ctx->opcode); + VA = rA(ctx->opcode); + VB = rB(ctx->opcode); + tmp = tcg_temp_new_i64(); + tcg_gen_shri_i64(tmp, cpu_avrh[VB], 32); + tcg_gen_deposit_i64(cpu_avrh[VT], cpu_avrh[VA], tmp, 0, 32); + tcg_gen_shri_i64(tmp, cpu_avrl[VB], 32); + tcg_gen_deposit_i64(cpu_avrl[VT], cpu_avrl[VA], tmp, 0, 32); + tcg_temp_free_i64(tmp); +} + +static void gen_vmrgow(DisasContext *ctx) +{ + int VT, VA, VB; + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + VT = rD(ctx->opcode); + VA = rA(ctx->opcode); + VB = rB(ctx->opcode); + + tcg_gen_deposit_i64(cpu_avrh[VT], cpu_avrh[VB], cpu_avrh[VA], 32, 32); + tcg_gen_deposit_i64(cpu_avrl[VT], cpu_avrl[VB], cpu_avrl[VA], 32, 32); +} + GEN_VXFORM(vmuloub, 4, 0); GEN_VXFORM(vmulouh, 4, 1); +GEN_VXFORM(vmulouw, 4, 2); +GEN_VXFORM(vmuluwm, 4, 2); +GEN_VXFORM_DUAL(vmulouw, PPC_ALTIVEC, PPC_NONE, + vmuluwm, PPC_NONE, PPC2_ALTIVEC_207) GEN_VXFORM(vmulosb, 4, 4); GEN_VXFORM(vmulosh, 4, 5); +GEN_VXFORM(vmulosw, 4, 6); GEN_VXFORM(vmuleub, 4, 8); GEN_VXFORM(vmuleuh, 4, 9); +GEN_VXFORM(vmuleuw, 4, 10); GEN_VXFORM(vmulesb, 4, 12); GEN_VXFORM(vmulesh, 4, 13); +GEN_VXFORM(vmulesw, 4, 14); GEN_VXFORM(vslb, 2, 4); GEN_VXFORM(vslh, 2, 5); GEN_VXFORM(vslw, 2, 6); +GEN_VXFORM(vsld, 2, 23); GEN_VXFORM(vsrb, 2, 8); GEN_VXFORM(vsrh, 2, 9); GEN_VXFORM(vsrw, 2, 10); +GEN_VXFORM(vsrd, 2, 27); GEN_VXFORM(vsrab, 2, 12); GEN_VXFORM(vsrah, 2, 13); GEN_VXFORM(vsraw, 2, 14); +GEN_VXFORM(vsrad, 2, 15); GEN_VXFORM(vslo, 6, 16); GEN_VXFORM(vsro, 6, 17); GEN_VXFORM(vaddcuw, 0, 6); @@ -6748,19 +7045,36 @@ GEN_VXFORM_ENV(vsubuws, 0, 26); GEN_VXFORM_ENV(vsubsbs, 0, 28); GEN_VXFORM_ENV(vsubshs, 0, 29); GEN_VXFORM_ENV(vsubsws, 0, 30); +GEN_VXFORM(vadduqm, 0, 4); +GEN_VXFORM(vaddcuq, 0, 5); +GEN_VXFORM3(vaddeuqm, 30, 0); +GEN_VXFORM3(vaddecuq, 30, 0); +GEN_VXFORM_DUAL(vaddeuqm, PPC_NONE, PPC2_ALTIVEC_207, \ + vaddecuq, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM(vsubuqm, 0, 20); +GEN_VXFORM(vsubcuq, 0, 21); +GEN_VXFORM3(vsubeuqm, 31, 0); +GEN_VXFORM3(vsubecuq, 31, 0); +GEN_VXFORM_DUAL(vsubeuqm, PPC_NONE, PPC2_ALTIVEC_207, \ + vsubecuq, PPC_NONE, PPC2_ALTIVEC_207) GEN_VXFORM(vrlb, 2, 0); GEN_VXFORM(vrlh, 2, 1); GEN_VXFORM(vrlw, 2, 2); +GEN_VXFORM(vrld, 2, 3); GEN_VXFORM(vsl, 2, 7); GEN_VXFORM(vsr, 2, 11); GEN_VXFORM_ENV(vpkuhum, 7, 0); GEN_VXFORM_ENV(vpkuwum, 7, 1); +GEN_VXFORM_ENV(vpkudum, 7, 17); GEN_VXFORM_ENV(vpkuhus, 7, 2); GEN_VXFORM_ENV(vpkuwus, 7, 3); +GEN_VXFORM_ENV(vpkudus, 7, 19); GEN_VXFORM_ENV(vpkshus, 7, 4); GEN_VXFORM_ENV(vpkswus, 7, 5); +GEN_VXFORM_ENV(vpksdus, 7, 21); GEN_VXFORM_ENV(vpkshss, 7, 6); GEN_VXFORM_ENV(vpkswss, 7, 7); +GEN_VXFORM_ENV(vpksdss, 7, 23); GEN_VXFORM(vpkpx, 7, 12); GEN_VXFORM_ENV(vsum4ubs, 4, 24); GEN_VXFORM_ENV(vsum4sbs, 4, 28); @@ -6793,20 +7107,58 @@ static void glue(gen_, name)(DisasContext *ctx) \ GEN_VXRFORM1(name, name, #name, opc2, opc3) \ GEN_VXRFORM1(name##_dot, name##_, #name ".", opc2, (opc3 | (0x1 << 4))) +/* + * Support for Altivec instructions that use bit 31 (Rc) as an opcode + * bit but also use bit 21 as an actual Rc bit. In general, thse pairs + * come from different versions of the ISA, so we must also support a + * pair of flags for each instruction. + */ +#define GEN_VXRFORM_DUAL(name0, flg0, flg2_0, name1, flg1, flg2_1) \ +static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ +{ \ + if ((Rc(ctx->opcode) == 0) && \ + ((ctx->insns_flags & flg0) || (ctx->insns_flags2 & flg2_0))) { \ + if (Rc21(ctx->opcode) == 0) { \ + gen_##name0(ctx); \ + } else { \ + gen_##name0##_(ctx); \ + } \ + } else if ((Rc(ctx->opcode) == 1) && \ + ((ctx->insns_flags & flg1) || (ctx->insns_flags2 & flg2_1))) { \ + if (Rc21(ctx->opcode) == 0) { \ + gen_##name1(ctx); \ + } else { \ + gen_##name1##_(ctx); \ + } \ + } else { \ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ + } \ +} + GEN_VXRFORM(vcmpequb, 3, 0) GEN_VXRFORM(vcmpequh, 3, 1) GEN_VXRFORM(vcmpequw, 3, 2) +GEN_VXRFORM(vcmpequd, 3, 3) GEN_VXRFORM(vcmpgtsb, 3, 12) GEN_VXRFORM(vcmpgtsh, 3, 13) GEN_VXRFORM(vcmpgtsw, 3, 14) +GEN_VXRFORM(vcmpgtsd, 3, 15) GEN_VXRFORM(vcmpgtub, 3, 8) GEN_VXRFORM(vcmpgtuh, 3, 9) GEN_VXRFORM(vcmpgtuw, 3, 10) +GEN_VXRFORM(vcmpgtud, 3, 11) GEN_VXRFORM(vcmpeqfp, 3, 3) GEN_VXRFORM(vcmpgefp, 3, 7) GEN_VXRFORM(vcmpgtfp, 3, 11) GEN_VXRFORM(vcmpbfp, 3, 15) +GEN_VXRFORM_DUAL(vcmpeqfp, PPC_ALTIVEC, PPC_NONE, \ + vcmpequd, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXRFORM_DUAL(vcmpbfp, PPC_ALTIVEC, PPC_NONE, \ + vcmpgtsd, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXRFORM_DUAL(vcmpgtfp, PPC_ALTIVEC, PPC_NONE, \ + vcmpgtud, PPC_NONE, PPC2_ALTIVEC_207) + #define GEN_VXFORM_SIMM(name, opc2, opc3) \ static void glue(gen_, name)(DisasContext *ctx) \ { \ @@ -6860,8 +7212,10 @@ static void glue(gen_, name)(DisasContext *ctx) \ GEN_VXFORM_NOA(vupkhsb, 7, 8); GEN_VXFORM_NOA(vupkhsh, 7, 9); +GEN_VXFORM_NOA(vupkhsw, 7, 25); GEN_VXFORM_NOA(vupklsb, 7, 10); GEN_VXFORM_NOA(vupklsh, 7, 11); +GEN_VXFORM_NOA(vupklsw, 7, 27); GEN_VXFORM_NOA(vupkhpx, 7, 13); GEN_VXFORM_NOA(vupklpx, 7, 15); GEN_VXFORM_NOA_ENV(vrefp, 5, 4); @@ -7002,6 +7356,115 @@ GEN_VAFORM_PAIRED(vmsumshm, vmsumshs, 20) GEN_VAFORM_PAIRED(vsel, vperm, 21) GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23) +GEN_VXFORM_NOA(vclzb, 1, 28) +GEN_VXFORM_NOA(vclzh, 1, 29) +GEN_VXFORM_NOA(vclzw, 1, 30) +GEN_VXFORM_NOA(vclzd, 1, 31) +GEN_VXFORM_NOA(vpopcntb, 1, 28) +GEN_VXFORM_NOA(vpopcnth, 1, 29) +GEN_VXFORM_NOA(vpopcntw, 1, 30) +GEN_VXFORM_NOA(vpopcntd, 1, 31) +GEN_VXFORM_DUAL(vclzb, PPC_NONE, PPC2_ALTIVEC_207, \ + vpopcntb, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vclzh, PPC_NONE, PPC2_ALTIVEC_207, \ + vpopcnth, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vclzw, PPC_NONE, PPC2_ALTIVEC_207, \ + vpopcntw, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vclzd, PPC_NONE, PPC2_ALTIVEC_207, \ + vpopcntd, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM(vbpermq, 6, 21); +GEN_VXFORM_NOA(vgbbd, 6, 20); +GEN_VXFORM(vpmsumb, 4, 16) +GEN_VXFORM(vpmsumh, 4, 17) +GEN_VXFORM(vpmsumw, 4, 18) +GEN_VXFORM(vpmsumd, 4, 19) + +#define GEN_BCD(op) \ +static void gen_##op(DisasContext *ctx) \ +{ \ + TCGv_ptr ra, rb, rd; \ + TCGv_i32 ps; \ + \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + \ + ra = gen_avr_ptr(rA(ctx->opcode)); \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + \ + ps = tcg_const_i32((ctx->opcode & 0x200) != 0); \ + \ + gen_helper_##op(cpu_crf[6], rd, ra, rb, ps); \ + \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rd); \ + tcg_temp_free_i32(ps); \ +} + +GEN_BCD(bcdadd) +GEN_BCD(bcdsub) + +GEN_VXFORM_DUAL(vsububm, PPC_ALTIVEC, PPC_NONE, \ + bcdadd, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vsububs, PPC_ALTIVEC, PPC_NONE, \ + bcdadd, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vsubuhm, PPC_ALTIVEC, PPC_NONE, \ + bcdsub, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vsubuhs, PPC_ALTIVEC, PPC_NONE, \ + bcdsub, PPC_NONE, PPC2_ALTIVEC_207) + +static void gen_vsbox(DisasContext *ctx) +{ + TCGv_ptr ra, rd; + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + ra = gen_avr_ptr(rA(ctx->opcode)); + rd = gen_avr_ptr(rD(ctx->opcode)); + gen_helper_vsbox(rd, ra); + tcg_temp_free_ptr(ra); + tcg_temp_free_ptr(rd); +} + +GEN_VXFORM(vcipher, 4, 20) +GEN_VXFORM(vcipherlast, 4, 20) +GEN_VXFORM(vncipher, 4, 21) +GEN_VXFORM(vncipherlast, 4, 21) + +GEN_VXFORM_DUAL(vcipher, PPC_NONE, PPC2_ALTIVEC_207, + vcipherlast, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vncipher, PPC_NONE, PPC2_ALTIVEC_207, + vncipherlast, PPC_NONE, PPC2_ALTIVEC_207) + +#define VSHASIGMA(op) \ +static void gen_##op(DisasContext *ctx) \ +{ \ + TCGv_ptr ra, rd; \ + TCGv_i32 st_six; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + ra = gen_avr_ptr(rA(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + st_six = tcg_const_i32(rB(ctx->opcode)); \ + gen_helper_##op(rd, ra, st_six); \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_ptr(rd); \ + tcg_temp_free_i32(st_six); \ +} + +VSHASIGMA(vshasigmaw) +VSHASIGMA(vshasigmad) + +GEN_VXFORM3(vpermxor, 22, 0xFF) +GEN_VXFORM_DUAL(vsldoi, PPC_ALTIVEC, PPC_NONE, + vpermxor, PPC_NONE, PPC2_ALTIVEC_207) + /*** VSX extension ***/ static inline TCGv_i64 cpu_vsrh(int n) @@ -7022,21 +7485,27 @@ static inline TCGv_i64 cpu_vsrl(int n) } } -static void gen_lxsdx(DisasContext *ctx) -{ - TCGv EA; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } - gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); - gen_qemu_ld64(ctx, cpu_vsrh(xT(ctx->opcode)), EA); - /* NOTE: cpu_vsrl is undefined */ - tcg_temp_free(EA); +#define VSX_LOAD_SCALAR(name, operation) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + gen_qemu_##operation(ctx, cpu_vsrh(xT(ctx->opcode)), EA); \ + /* NOTE: cpu_vsrl is undefined */ \ + tcg_temp_free(EA); \ } +VSX_LOAD_SCALAR(lxsdx, ld64) +VSX_LOAD_SCALAR(lxsiwax, ld32s_i64) +VSX_LOAD_SCALAR(lxsiwzx, ld32u_i64) +VSX_LOAD_SCALAR(lxsspx, ld32fs) + static void gen_lxvd2x(DisasContext *ctx) { TCGv EA; @@ -7098,20 +7567,25 @@ static void gen_lxvw4x(DisasContext *ctx) tcg_temp_free_i64(tmp); } -static void gen_stxsdx(DisasContext *ctx) -{ - TCGv EA; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } - gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); - gen_qemu_st64(ctx, cpu_vsrh(xS(ctx->opcode)), EA); - tcg_temp_free(EA); +#define VSX_STORE_SCALAR(name, operation) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv EA; \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + gen_qemu_##operation(ctx, cpu_vsrh(xS(ctx->opcode)), EA); \ + tcg_temp_free(EA); \ } +VSX_STORE_SCALAR(stxsdx, st64) +VSX_STORE_SCALAR(stxsiwx, st32_i64) +VSX_STORE_SCALAR(stxsspx, st32fs) + static void gen_stxvd2x(DisasContext *ctx) { TCGv EA; @@ -7156,6 +7630,57 @@ static void gen_stxvw4x(DisasContext *ctx) tcg_temp_free_i64(tmp); } +#define MV_VSRW(name, tcgop1, tcgop2, target, source) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + if (xS(ctx->opcode) < 32) { \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + } else { \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + } \ + TCGv_i64 tmp = tcg_temp_new_i64(); \ + tcg_gen_##tcgop1(tmp, source); \ + tcg_gen_##tcgop2(target, tmp); \ + tcg_temp_free_i64(tmp); \ +} + + +MV_VSRW(mfvsrwz, ext32u_i64, trunc_i64_tl, cpu_gpr[rA(ctx->opcode)], \ + cpu_vsrh(xS(ctx->opcode))) +MV_VSRW(mtvsrwa, extu_tl_i64, ext32s_i64, cpu_vsrh(xT(ctx->opcode)), \ + cpu_gpr[rA(ctx->opcode)]) +MV_VSRW(mtvsrwz, extu_tl_i64, ext32u_i64, cpu_vsrh(xT(ctx->opcode)), \ + cpu_gpr[rA(ctx->opcode)]) + +#if defined(TARGET_PPC64) +#define MV_VSRD(name, target, source) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + if (xS(ctx->opcode) < 32) { \ + if (unlikely(!ctx->fpu_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_FPU); \ + return; \ + } \ + } else { \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + } \ + tcg_gen_mov_i64(target, source); \ +} + +MV_VSRD(mfvsrd, cpu_gpr[rA(ctx->opcode)], cpu_vsrh(xS(ctx->opcode))) +MV_VSRD(mtvsrd, cpu_vsrh(xT(ctx->opcode)), cpu_gpr[rA(ctx->opcode)]) + +#endif + static void gen_xxpermdi(DisasContext *ctx) { if (unlikely(!ctx->vsx_enabled)) { @@ -7163,15 +7688,40 @@ static void gen_xxpermdi(DisasContext *ctx) return; } - if ((DM(ctx->opcode) & 2) == 0) { - tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), cpu_vsrh(xA(ctx->opcode))); - } else { - tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), cpu_vsrl(xA(ctx->opcode))); - } - if ((DM(ctx->opcode) & 1) == 0) { - tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrh(xB(ctx->opcode))); + if (unlikely((xT(ctx->opcode) == xA(ctx->opcode)) || + (xT(ctx->opcode) == xB(ctx->opcode)))) { + TCGv_i64 xh, xl; + + xh = tcg_temp_new_i64(); + xl = tcg_temp_new_i64(); + + if ((DM(ctx->opcode) & 2) == 0) { + tcg_gen_mov_i64(xh, cpu_vsrh(xA(ctx->opcode))); + } else { + tcg_gen_mov_i64(xh, cpu_vsrl(xA(ctx->opcode))); + } + if ((DM(ctx->opcode) & 1) == 0) { + tcg_gen_mov_i64(xl, cpu_vsrh(xB(ctx->opcode))); + } else { + tcg_gen_mov_i64(xl, cpu_vsrl(xB(ctx->opcode))); + } + + tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), xh); + tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), xl); + + tcg_temp_free_i64(xh); + tcg_temp_free_i64(xl); } else { - tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrl(xB(ctx->opcode))); + if ((DM(ctx->opcode) & 2) == 0) { + tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), cpu_vsrh(xA(ctx->opcode))); + } else { + tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), cpu_vsrl(xA(ctx->opcode))); + } + if ((DM(ctx->opcode) & 1) == 0) { + tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrh(xB(ctx->opcode))); + } else { + tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrl(xB(ctx->opcode))); + } } } @@ -7179,8 +7729,8 @@ static void gen_xxpermdi(DisasContext *ctx) #define OP_NABS 2 #define OP_NEG 3 #define OP_CPSGN 4 -#define SGN_MASK_DP 0x8000000000000000ul -#define SGN_MASK_SP 0x8000000080000000ul +#define SGN_MASK_DP 0x8000000000000000ull +#define SGN_MASK_SP 0x8000000080000000ull #define VSX_SCALAR_MOVE(name, op, sgn_mask) \ static void glue(gen_, name)(DisasContext * ctx) \ @@ -7289,6 +7839,165 @@ VSX_VECTOR_MOVE(xvnabssp, OP_NABS, SGN_MASK_SP) VSX_VECTOR_MOVE(xvnegsp, OP_NEG, SGN_MASK_SP) VSX_VECTOR_MOVE(xvcpsgnsp, OP_CPSGN, SGN_MASK_SP) +#define GEN_VSX_HELPER_2(name, op1, op2, inval, type) \ +static void gen_##name(DisasContext * ctx) \ +{ \ + TCGv_i32 opc; \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + /* NIP cannot be restored if the memory exception comes from an helper */ \ + gen_update_nip(ctx, ctx->nip - 4); \ + opc = tcg_const_i32(ctx->opcode); \ + gen_helper_##name(cpu_env, opc); \ + tcg_temp_free_i32(opc); \ +} + +#define GEN_VSX_HELPER_XT_XB_ENV(name, op1, op2, inval, type) \ +static void gen_##name(DisasContext * ctx) \ +{ \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + /* NIP cannot be restored if the exception comes */ \ + /* from a helper. */ \ + gen_update_nip(ctx, ctx->nip - 4); \ + \ + gen_helper_##name(cpu_vsrh(xT(ctx->opcode)), cpu_env, \ + cpu_vsrh(xB(ctx->opcode))); \ +} + +GEN_VSX_HELPER_2(xsadddp, 0x00, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xssubdp, 0x00, 0x05, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmuldp, 0x00, 0x06, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsdivdp, 0x00, 0x07, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsredp, 0x14, 0x05, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xssqrtdp, 0x16, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsrsqrtedp, 0x14, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xstdivdp, 0x14, 0x07, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xstsqrtdp, 0x14, 0x06, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmaddadp, 0x04, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmaddmdp, 0x04, 0x05, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmsubadp, 0x04, 0x06, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmsubmdp, 0x04, 0x07, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsnmaddadp, 0x04, 0x14, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsnmaddmdp, 0x04, 0x15, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsnmsubadp, 0x04, 0x16, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsnmsubmdp, 0x04, 0x17, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscmpodp, 0x0C, 0x05, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscmpudp, 0x0C, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmaxdp, 0x00, 0x14, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmindp, 0x00, 0x15, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvdpsp, 0x12, 0x10, 0, PPC2_VSX) +GEN_VSX_HELPER_XT_XB_ENV(xscvdpspn, 0x16, 0x10, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xscvspdp, 0x12, 0x14, 0, PPC2_VSX) +GEN_VSX_HELPER_XT_XB_ENV(xscvspdpn, 0x16, 0x14, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xscvdpsxds, 0x10, 0x15, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvdpsxws, 0x10, 0x05, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvdpuxds, 0x10, 0x14, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvdpuxws, 0x10, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvsxddp, 0x10, 0x17, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvuxddp, 0x10, 0x16, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsrdpi, 0x12, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsrdpic, 0x16, 0x06, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsrdpim, 0x12, 0x07, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsrdpip, 0x12, 0x06, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsrdpiz, 0x12, 0x05, 0, PPC2_VSX) +GEN_VSX_HELPER_XT_XB_ENV(xsrsp, 0x12, 0x11, 0, PPC2_VSX207) + +GEN_VSX_HELPER_2(xsaddsp, 0x00, 0x00, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xssubsp, 0x00, 0x01, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsmulsp, 0x00, 0x02, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsdivsp, 0x00, 0x03, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsresp, 0x14, 0x01, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xssqrtsp, 0x16, 0x00, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsrsqrtesp, 0x14, 0x00, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsmaddasp, 0x04, 0x00, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsmaddmsp, 0x04, 0x01, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsmsubasp, 0x04, 0x02, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsmsubmsp, 0x04, 0x03, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsnmaddasp, 0x04, 0x10, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsnmaddmsp, 0x04, 0x11, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsnmsubasp, 0x04, 0x12, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xsnmsubmsp, 0x04, 0x13, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xscvsxdsp, 0x10, 0x13, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xscvuxdsp, 0x10, 0x12, 0, PPC2_VSX207) + +GEN_VSX_HELPER_2(xvadddp, 0x00, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvsubdp, 0x00, 0x0D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmuldp, 0x00, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvdivdp, 0x00, 0x0F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvredp, 0x14, 0x0D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvsqrtdp, 0x16, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrsqrtedp, 0x14, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvtdivdp, 0x14, 0x0F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvtsqrtdp, 0x14, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmaddadp, 0x04, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmaddmdp, 0x04, 0x0D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmsubadp, 0x04, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmsubmdp, 0x04, 0x0F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmaddadp, 0x04, 0x1C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmaddmdp, 0x04, 0x1D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmsubadp, 0x04, 0x1E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmsubmdp, 0x04, 0x1F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmaxdp, 0x00, 0x1C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmindp, 0x00, 0x1D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpeqdp, 0x0C, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpgtdp, 0x0C, 0x0D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpgedp, 0x0C, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvdpsp, 0x12, 0x18, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvdpsxds, 0x10, 0x1D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvdpsxws, 0x10, 0x0D, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvdpuxds, 0x10, 0x1C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvdpuxws, 0x10, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvsxddp, 0x10, 0x1F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvuxddp, 0x10, 0x1E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvsxwdp, 0x10, 0x0F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvuxwdp, 0x10, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrdpi, 0x12, 0x0C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrdpic, 0x16, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrdpim, 0x12, 0x0F, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrdpip, 0x12, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrdpiz, 0x12, 0x0D, 0, PPC2_VSX) + +GEN_VSX_HELPER_2(xvaddsp, 0x00, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvsubsp, 0x00, 0x09, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmulsp, 0x00, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvdivsp, 0x00, 0x0B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvresp, 0x14, 0x09, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvsqrtsp, 0x16, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrsqrtesp, 0x14, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvtdivsp, 0x14, 0x0B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvtsqrtsp, 0x14, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmaddasp, 0x04, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmaddmsp, 0x04, 0x09, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmsubasp, 0x04, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmsubmsp, 0x04, 0x0B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmaddasp, 0x04, 0x18, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmaddmsp, 0x04, 0x19, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmsubasp, 0x04, 0x1A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvnmsubmsp, 0x04, 0x1B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvmaxsp, 0x00, 0x18, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvminsp, 0x00, 0x19, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpeqsp, 0x0C, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpgtsp, 0x0C, 0x09, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpgesp, 0x0C, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvspdp, 0x12, 0x1C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvspsxds, 0x10, 0x19, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvspsxws, 0x10, 0x09, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvspuxds, 0x10, 0x18, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvspuxws, 0x10, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvsxdsp, 0x10, 0x1B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvuxdsp, 0x10, 0x1A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvsxwsp, 0x10, 0x0B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvuxwsp, 0x10, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrspi, 0x12, 0x08, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrspic, 0x16, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrspim, 0x12, 0x0B, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrspip, 0x12, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvrspiz, 0x12, 0x09, 0, PPC2_VSX) #define VSX_LOGICAL(name, tcg_op) \ static void glue(gen_, name)(DisasContext * ctx) \ @@ -7308,6 +8017,9 @@ VSX_LOGICAL(xxlandc, tcg_gen_andc_i64) VSX_LOGICAL(xxlor, tcg_gen_or_i64) VSX_LOGICAL(xxlxor, tcg_gen_xor_i64) VSX_LOGICAL(xxlnor, tcg_gen_nor_i64) +VSX_LOGICAL(xxleqv, tcg_gen_eqv_i64) +VSX_LOGICAL(xxlnand, tcg_gen_nand_i64) +VSX_LOGICAL(xxlorc, tcg_gen_orc_i64) #define VSX_XXMRG(name, high) \ static void glue(gen_, name)(DisasContext * ctx) \ @@ -9175,6 +9887,7 @@ GEN_HANDLER_E(prtyw, 0x1F, 0x1A, 0x04, 0x0000F801, PPC_NONE, PPC2_ISA205), GEN_HANDLER(popcntd, 0x1F, 0x1A, 0x0F, 0x0000F801, PPC_POPCNTWD), GEN_HANDLER(cntlzd, 0x1F, 0x1A, 0x01, 0x00000000, PPC_64B), GEN_HANDLER_E(prtyd, 0x1F, 0x1A, 0x05, 0x0000F801, PPC_NONE, PPC2_ISA205), +GEN_HANDLER_E(bpermd, 0x1F, 0x1C, 0x07, 0x00000001, PPC_NONE, PPC2_PERM_ISA206), #endif GEN_HANDLER(rlwimi, 0x14, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(rlwinm, 0x15, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), @@ -9200,6 +9913,8 @@ GEN_HANDLER(fmr, 0x3F, 0x08, 0x02, 0x001F0000, PPC_FLOAT), GEN_HANDLER(fnabs, 0x3F, 0x08, 0x04, 0x001F0000, PPC_FLOAT), GEN_HANDLER(fneg, 0x3F, 0x08, 0x01, 0x001F0000, PPC_FLOAT), GEN_HANDLER_E(fcpsgn, 0x3F, 0x08, 0x00, 0x00000000, PPC_NONE, PPC2_ISA205), +GEN_HANDLER_E(fmrgew, 0x3F, 0x06, 0x1E, 0x00000001, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(fmrgow, 0x3F, 0x06, 0x1A, 0x00000001, PPC_NONE, PPC2_VSX207), GEN_HANDLER(mcrfs, 0x3F, 0x00, 0x02, 0x0063F801, PPC_FLOAT), GEN_HANDLER(mffs, 0x3F, 0x07, 0x12, 0x001FF800, PPC_FLOAT), GEN_HANDLER(mtfsb0, 0x3F, 0x06, 0x02, 0x001FF800, PPC_FLOAT), @@ -9219,11 +9934,17 @@ GEN_HANDLER(stswi, 0x1F, 0x15, 0x16, 0x00000001, PPC_STRING), GEN_HANDLER(stswx, 0x1F, 0x15, 0x14, 0x00000001, PPC_STRING), GEN_HANDLER(eieio, 0x1F, 0x16, 0x1A, 0x03FFF801, PPC_MEM_EIEIO), GEN_HANDLER(isync, 0x13, 0x16, 0x04, 0x03FFF801, PPC_MEM), +GEN_HANDLER_E(lbarx, 0x1F, 0x14, 0x01, 0, PPC_NONE, PPC2_ATOMIC_ISA206), +GEN_HANDLER_E(lharx, 0x1F, 0x14, 0x03, 0, PPC_NONE, PPC2_ATOMIC_ISA206), GEN_HANDLER(lwarx, 0x1F, 0x14, 0x00, 0x00000000, PPC_RES), +GEN_HANDLER_E(stbcx_, 0x1F, 0x16, 0x15, 0, PPC_NONE, PPC2_ATOMIC_ISA206), +GEN_HANDLER_E(sthcx_, 0x1F, 0x16, 0x16, 0, PPC_NONE, PPC2_ATOMIC_ISA206), GEN_HANDLER2(stwcx_, "stwcx.", 0x1F, 0x16, 0x04, 0x00000000, PPC_RES), #if defined(TARGET_PPC64) GEN_HANDLER(ldarx, 0x1F, 0x14, 0x02, 0x00000000, PPC_64B), +GEN_HANDLER_E(lqarx, 0x1F, 0x14, 0x08, 0, PPC_NONE, PPC2_LSQ_ISA207), GEN_HANDLER2(stdcx_, "stdcx.", 0x1F, 0x16, 0x06, 0x00000000, PPC_64B), +GEN_HANDLER_E(stqcx_, 0x1F, 0x16, 0x05, 0, PPC_NONE, PPC2_LSQ_ISA207), #endif GEN_HANDLER(sync, 0x1F, 0x16, 0x12, 0x039FF801, PPC_MEM_SYNC), GEN_HANDLER(wait, 0x1F, 0x1E, 0x01, 0x03FFF801, PPC_WAIT), @@ -9231,6 +9952,7 @@ GEN_HANDLER(b, 0x12, 0xFF, 0xFF, 0x00000000, PPC_FLOW), GEN_HANDLER(bc, 0x10, 0xFF, 0xFF, 0x00000000, PPC_FLOW), GEN_HANDLER(bcctr, 0x13, 0x10, 0x10, 0x00000000, PPC_FLOW), GEN_HANDLER(bclr, 0x13, 0x10, 0x00, 0x00000000, PPC_FLOW), +GEN_HANDLER_E(bctar, 0x13, 0x10, 0x11, 0, PPC_NONE, PPC2_BCTAR_ISA207), GEN_HANDLER(mcrf, 0x13, 0x00, 0xFF, 0x00000001, PPC_INTEGER), GEN_HANDLER(rfi, 0x13, 0x12, 0x01, 0x03FF8001, PPC_FLOW), #if defined(TARGET_PPC64) @@ -9258,8 +9980,8 @@ GEN_HANDLER(mtspr, 0x1F, 0x13, 0x0E, 0x00000001, PPC_MISC), GEN_HANDLER(dcbf, 0x1F, 0x16, 0x02, 0x03C00001, PPC_CACHE), GEN_HANDLER(dcbi, 0x1F, 0x16, 0x0E, 0x03E00001, PPC_CACHE), GEN_HANDLER(dcbst, 0x1F, 0x16, 0x01, 0x03E00001, PPC_CACHE), -GEN_HANDLER(dcbt, 0x1F, 0x16, 0x08, 0x02000001, PPC_CACHE), -GEN_HANDLER(dcbtst, 0x1F, 0x16, 0x07, 0x02000001, PPC_CACHE), +GEN_HANDLER(dcbt, 0x1F, 0x16, 0x08, 0x00000001, PPC_CACHE), +GEN_HANDLER(dcbtst, 0x1F, 0x16, 0x07, 0x00000001, PPC_CACHE), GEN_HANDLER(dcbz, 0x1F, 0x16, 0x1F, 0x03C00001, PPC_CACHE_DCBZ), GEN_HANDLER(dst, 0x1F, 0x16, 0x0A, 0x01800001, PPC_ALTIVEC), GEN_HANDLER(dstst, 0x1F, 0x16, 0x0B, 0x02000001, PPC_ALTIVEC), @@ -9395,7 +10117,6 @@ GEN_HANDLER(lvsl, 0x1f, 0x06, 0x00, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC), GEN_HANDLER(mtvscr, 0x04, 0x2, 0x19, 0x03ff0000, PPC_ALTIVEC), -GEN_HANDLER(vsldoi, 0x04, 0x16, 0xFF, 0x00000400, PPC_ALTIVEC), GEN_HANDLER(vmladduhm, 0x04, 0x11, 0xFF, 0x00000000, PPC_ALTIVEC), GEN_HANDLER2(evsel0, "evsel", 0x04, 0x1c, 0x09, 0x00000000, PPC_SPE), GEN_HANDLER2(evsel1, "evsel", 0x04, 0x1d, 0x09, 0x00000000, PPC_SPE), @@ -9427,6 +10148,10 @@ GEN_INT_ARITH_DIVW(divwu, 0x0E, 0, 0), GEN_INT_ARITH_DIVW(divwuo, 0x1E, 0, 1), GEN_INT_ARITH_DIVW(divw, 0x0F, 1, 0), GEN_INT_ARITH_DIVW(divwo, 0x1F, 1, 1), +GEN_HANDLER_E(divwe, 0x1F, 0x0B, 0x0D, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(divweo, 0x1F, 0x0B, 0x1D, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(divweu, 0x1F, 0x0B, 0x0C, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(divweuo, 0x1F, 0x0B, 0x1C, 0, PPC_NONE, PPC2_DIVE_ISA206), #if defined(TARGET_PPC64) #undef GEN_INT_ARITH_DIVD @@ -9437,6 +10162,11 @@ GEN_INT_ARITH_DIVD(divduo, 0x1E, 0, 1), GEN_INT_ARITH_DIVD(divd, 0x0F, 1, 0), GEN_INT_ARITH_DIVD(divdo, 0x1F, 1, 1), +GEN_HANDLER_E(divdeu, 0x1F, 0x09, 0x0C, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(divdeuo, 0x1F, 0x09, 0x1C, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(divde, 0x1F, 0x09, 0x0D, 0, PPC_NONE, PPC2_DIVE_ISA206), +GEN_HANDLER_E(divdeo, 0x1F, 0x09, 0x1D, 0, PPC_NONE, PPC2_DIVE_ISA206), + #undef GEN_INT_ARITH_MUL_HELPER #define GEN_INT_ARITH_MUL_HELPER(name, opc3) \ GEN_HANDLER(name, 0x1F, 0x09, opc3, 0x00000000, PPC_64B) @@ -9544,13 +10274,22 @@ GEN_FLOAT_ACB(madd, 0x1D, 1, PPC_FLOAT), GEN_FLOAT_ACB(msub, 0x1C, 1, PPC_FLOAT), GEN_FLOAT_ACB(nmadd, 0x1F, 1, PPC_FLOAT), GEN_FLOAT_ACB(nmsub, 0x1E, 1, PPC_FLOAT), +GEN_HANDLER_E(ftdiv, 0x3F, 0x00, 0x04, 1, PPC_NONE, PPC2_FP_TST_ISA206), +GEN_HANDLER_E(ftsqrt, 0x3F, 0x00, 0x05, 1, PPC_NONE, PPC2_FP_TST_ISA206), GEN_FLOAT_B(ctiw, 0x0E, 0x00, 0, PPC_FLOAT), +GEN_HANDLER_E(fctiwu, 0x3F, 0x0E, 0x04, 0, PPC_NONE, PPC2_FP_CVT_ISA206), GEN_FLOAT_B(ctiwz, 0x0F, 0x00, 0, PPC_FLOAT), +GEN_HANDLER_E(fctiwuz, 0x3F, 0x0F, 0x04, 0, PPC_NONE, PPC2_FP_CVT_ISA206), GEN_FLOAT_B(rsp, 0x0C, 0x00, 1, PPC_FLOAT), #if defined(TARGET_PPC64) GEN_FLOAT_B(cfid, 0x0E, 0x1A, 1, PPC_64B), +GEN_HANDLER_E(fcfids, 0x3B, 0x0E, 0x1A, 0, PPC_NONE, PPC2_FP_CVT_ISA206), +GEN_HANDLER_E(fcfidu, 0x3F, 0x0E, 0x1E, 0, PPC_NONE, PPC2_FP_CVT_ISA206), +GEN_HANDLER_E(fcfidus, 0x3B, 0x0E, 0x1E, 0, PPC_NONE, PPC2_FP_CVT_ISA206), GEN_FLOAT_B(ctid, 0x0E, 0x19, 0, PPC_64B), +GEN_HANDLER_E(fctidu, 0x3F, 0x0E, 0x1D, 0, PPC_NONE, PPC2_FP_CVT_ISA206), GEN_FLOAT_B(ctidz, 0x0F, 0x19, 0, PPC_64B), +GEN_HANDLER_E(fctiduz, 0x3F, 0x0F, 0x1D, 0, PPC_NONE, PPC2_FP_CVT_ISA206), #endif GEN_FLOAT_B(rin, 0x08, 0x0C, 1, PPC_FLOAT_EXT), GEN_FLOAT_B(riz, 0x08, 0x0D, 1, PPC_FLOAT_EXT), @@ -9642,6 +10381,7 @@ GEN_LDXF(name, ldop, 0x17, op | 0x00, type) GEN_LDFS(lfd, ld64, 0x12, PPC_FLOAT) GEN_LDFS(lfs, ld32fs, 0x10, PPC_FLOAT) GEN_HANDLER_E(lfiwax, 0x1f, 0x17, 0x1a, 0x00000001, PPC_NONE, PPC2_ISA205), +GEN_HANDLER_E(lfiwzx, 0x1f, 0x17, 0x1b, 0x1, PPC_NONE, PPC2_FP_CVT_ISA206), GEN_HANDLER_E(lfdp, 0x39, 0xFF, 0xFF, 0x00200003, PPC_NONE, PPC2_ISA205), GEN_HANDLER_E(lfdpx, 0x1F, 0x17, 0x18, 0x00200001, PPC_NONE, PPC2_ISA205), @@ -9754,33 +10494,61 @@ GEN_VR_STVE(wx, 0x07, 0x06), #undef GEN_VX_LOGICAL #define GEN_VX_LOGICAL(name, tcg_op, opc2, opc3) \ GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_ALTIVEC) + +#undef GEN_VX_LOGICAL_207 +#define GEN_VX_LOGICAL_207(name, tcg_op, opc2, opc3) \ +GEN_HANDLER_E(name, 0x04, opc2, opc3, 0x00000000, PPC_NONE, PPC2_ALTIVEC_207) + GEN_VX_LOGICAL(vand, tcg_gen_and_i64, 2, 16), GEN_VX_LOGICAL(vandc, tcg_gen_andc_i64, 2, 17), GEN_VX_LOGICAL(vor, tcg_gen_or_i64, 2, 18), GEN_VX_LOGICAL(vxor, tcg_gen_xor_i64, 2, 19), GEN_VX_LOGICAL(vnor, tcg_gen_nor_i64, 2, 20), +GEN_VX_LOGICAL_207(veqv, tcg_gen_eqv_i64, 2, 26), +GEN_VX_LOGICAL_207(vnand, tcg_gen_nand_i64, 2, 22), +GEN_VX_LOGICAL_207(vorc, tcg_gen_orc_i64, 2, 21), #undef GEN_VXFORM #define GEN_VXFORM(name, opc2, opc3) \ GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_ALTIVEC) + +#undef GEN_VXFORM_207 +#define GEN_VXFORM_207(name, opc2, opc3) \ +GEN_HANDLER_E(name, 0x04, opc2, opc3, 0x00000000, PPC_NONE, PPC2_ALTIVEC_207) + +#undef GEN_VXFORM_DUAL +#define GEN_VXFORM_DUAL(name0, name1, opc2, opc3, type0, type1) \ +GEN_HANDLER_E(name0##_##name1, 0x4, opc2, opc3, 0x00000000, type0, type1) + +#undef GEN_VXRFORM_DUAL +#define GEN_VXRFORM_DUAL(name0, name1, opc2, opc3, tp0, tp1) \ +GEN_HANDLER_E(name0##_##name1, 0x4, opc2, opc3, 0x00000000, tp0, tp1), \ +GEN_HANDLER_E(name0##_##name1, 0x4, opc2, (opc3 | 0x10), 0x00000000, tp0, tp1), + GEN_VXFORM(vaddubm, 0, 0), GEN_VXFORM(vadduhm, 0, 1), GEN_VXFORM(vadduwm, 0, 2), -GEN_VXFORM(vsububm, 0, 16), -GEN_VXFORM(vsubuhm, 0, 17), +GEN_VXFORM_207(vaddudm, 0, 3), +GEN_VXFORM_DUAL(vsububm, bcdadd, 0, 16, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_DUAL(vsubuhm, bcdsub, 0, 17, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM(vsubuwm, 0, 18), +GEN_VXFORM_207(vsubudm, 0, 19), GEN_VXFORM(vmaxub, 1, 0), GEN_VXFORM(vmaxuh, 1, 1), GEN_VXFORM(vmaxuw, 1, 2), +GEN_VXFORM_207(vmaxud, 1, 3), GEN_VXFORM(vmaxsb, 1, 4), GEN_VXFORM(vmaxsh, 1, 5), GEN_VXFORM(vmaxsw, 1, 6), +GEN_VXFORM_207(vmaxsd, 1, 7), GEN_VXFORM(vminub, 1, 8), GEN_VXFORM(vminuh, 1, 9), GEN_VXFORM(vminuw, 1, 10), +GEN_VXFORM_207(vminud, 1, 11), GEN_VXFORM(vminsb, 1, 12), GEN_VXFORM(vminsh, 1, 13), GEN_VXFORM(vminsw, 1, 14), +GEN_VXFORM_207(vminsd, 1, 15), GEN_VXFORM(vavgub, 1, 16), GEN_VXFORM(vavguh, 1, 17), GEN_VXFORM(vavguw, 1, 18), @@ -9793,23 +10561,32 @@ GEN_VXFORM(vmrghw, 6, 2), GEN_VXFORM(vmrglb, 6, 4), GEN_VXFORM(vmrglh, 6, 5), GEN_VXFORM(vmrglw, 6, 6), +GEN_VXFORM_207(vmrgew, 6, 30), +GEN_VXFORM_207(vmrgow, 6, 26), GEN_VXFORM(vmuloub, 4, 0), GEN_VXFORM(vmulouh, 4, 1), +GEN_VXFORM_DUAL(vmulouw, vmuluwm, 4, 2, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM(vmulosb, 4, 4), GEN_VXFORM(vmulosh, 4, 5), +GEN_VXFORM_207(vmulosw, 4, 6), GEN_VXFORM(vmuleub, 4, 8), GEN_VXFORM(vmuleuh, 4, 9), +GEN_VXFORM_207(vmuleuw, 4, 10), GEN_VXFORM(vmulesb, 4, 12), GEN_VXFORM(vmulesh, 4, 13), +GEN_VXFORM_207(vmulesw, 4, 14), GEN_VXFORM(vslb, 2, 4), GEN_VXFORM(vslh, 2, 5), GEN_VXFORM(vslw, 2, 6), +GEN_VXFORM_207(vsld, 2, 23), GEN_VXFORM(vsrb, 2, 8), GEN_VXFORM(vsrh, 2, 9), GEN_VXFORM(vsrw, 2, 10), +GEN_VXFORM_207(vsrd, 2, 27), GEN_VXFORM(vsrab, 2, 12), GEN_VXFORM(vsrah, 2, 13), GEN_VXFORM(vsraw, 2, 14), +GEN_VXFORM_207(vsrad, 2, 15), GEN_VXFORM(vslo, 6, 16), GEN_VXFORM(vsro, 6, 17), GEN_VXFORM(vaddcuw, 0, 6), @@ -9820,25 +10597,36 @@ GEN_VXFORM(vadduws, 0, 10), GEN_VXFORM(vaddsbs, 0, 12), GEN_VXFORM(vaddshs, 0, 13), GEN_VXFORM(vaddsws, 0, 14), -GEN_VXFORM(vsububs, 0, 24), -GEN_VXFORM(vsubuhs, 0, 25), +GEN_VXFORM_DUAL(vsububs, bcdadd, 0, 24, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_DUAL(vsubuhs, bcdsub, 0, 25, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM(vsubuws, 0, 26), GEN_VXFORM(vsubsbs, 0, 28), GEN_VXFORM(vsubshs, 0, 29), GEN_VXFORM(vsubsws, 0, 30), +GEN_VXFORM_207(vadduqm, 0, 4), +GEN_VXFORM_207(vaddcuq, 0, 5), +GEN_VXFORM_DUAL(vaddeuqm, vaddecuq, 30, 0xFF, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM_207(vsubuqm, 0, 20), +GEN_VXFORM_207(vsubcuq, 0, 21), +GEN_VXFORM_DUAL(vsubeuqm, vsubecuq, 31, 0xFF, PPC_NONE, PPC2_ALTIVEC_207), GEN_VXFORM(vrlb, 2, 0), GEN_VXFORM(vrlh, 2, 1), GEN_VXFORM(vrlw, 2, 2), +GEN_VXFORM_207(vrld, 2, 3), GEN_VXFORM(vsl, 2, 7), GEN_VXFORM(vsr, 2, 11), GEN_VXFORM(vpkuhum, 7, 0), GEN_VXFORM(vpkuwum, 7, 1), +GEN_VXFORM_207(vpkudum, 7, 17), GEN_VXFORM(vpkuhus, 7, 2), GEN_VXFORM(vpkuwus, 7, 3), +GEN_VXFORM_207(vpkudus, 7, 19), GEN_VXFORM(vpkshus, 7, 4), GEN_VXFORM(vpkswus, 7, 5), +GEN_VXFORM_207(vpksdus, 7, 21), GEN_VXFORM(vpkshss, 7, 6), GEN_VXFORM(vpkswss, 7, 7), +GEN_VXFORM_207(vpksdss, 7, 23), GEN_VXFORM(vpkpx, 7, 12), GEN_VXFORM(vsum4ubs, 4, 24), GEN_VXFORM(vsum4sbs, 4, 28), @@ -9866,10 +10654,10 @@ GEN_VXRFORM(vcmpgtsw, 3, 14) GEN_VXRFORM(vcmpgtub, 3, 8) GEN_VXRFORM(vcmpgtuh, 3, 9) GEN_VXRFORM(vcmpgtuw, 3, 10) -GEN_VXRFORM(vcmpeqfp, 3, 3) +GEN_VXRFORM_DUAL(vcmpeqfp, vcmpequd, 3, 3, PPC_ALTIVEC, PPC_NONE) GEN_VXRFORM(vcmpgefp, 3, 7) -GEN_VXRFORM(vcmpgtfp, 3, 11) -GEN_VXRFORM(vcmpbfp, 3, 15) +GEN_VXRFORM_DUAL(vcmpgtfp, vcmpgtud, 3, 11, PPC_ALTIVEC, PPC_NONE) +GEN_VXRFORM_DUAL(vcmpbfp, vcmpgtsd, 3, 15, PPC_ALTIVEC, PPC_NONE) #undef GEN_VXFORM_SIMM #define GEN_VXFORM_SIMM(name, opc2, opc3) \ @@ -9883,8 +10671,10 @@ GEN_VXFORM_SIMM(vspltisw, 6, 14), GEN_HANDLER(name, 0x04, opc2, opc3, 0x001f0000, PPC_ALTIVEC) GEN_VXFORM_NOA(vupkhsb, 7, 8), GEN_VXFORM_NOA(vupkhsh, 7, 9), +GEN_VXFORM_207(vupkhsw, 7, 25), GEN_VXFORM_NOA(vupklsb, 7, 10), GEN_VXFORM_NOA(vupklsh, 7, 11), +GEN_VXFORM_207(vupklsw, 7, 27), GEN_VXFORM_NOA(vupkhpx, 7, 13), GEN_VXFORM_NOA(vupklpx, 7, 15), GEN_VXFORM_NOA(vrefp, 5, 4), @@ -9917,15 +10707,50 @@ GEN_VAFORM_PAIRED(vmsumshm, vmsumshs, 20), GEN_VAFORM_PAIRED(vsel, vperm, 21), GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23), +GEN_VXFORM_DUAL(vclzb, vpopcntb, 1, 28, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM_DUAL(vclzh, vpopcnth, 1, 29, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM_DUAL(vclzw, vpopcntw, 1, 30, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM_DUAL(vclzd, vpopcntd, 1, 31, PPC_NONE, PPC2_ALTIVEC_207), + +GEN_VXFORM_207(vbpermq, 6, 21), +GEN_VXFORM_207(vgbbd, 6, 20), +GEN_VXFORM_207(vpmsumb, 4, 16), +GEN_VXFORM_207(vpmsumh, 4, 17), +GEN_VXFORM_207(vpmsumw, 4, 18), +GEN_VXFORM_207(vpmsumd, 4, 19), + +GEN_VXFORM_207(vsbox, 4, 23), + +GEN_VXFORM_DUAL(vcipher, vcipherlast, 4, 20, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM_DUAL(vncipher, vncipherlast, 4, 21, PPC_NONE, PPC2_ALTIVEC_207), + +GEN_VXFORM_207(vshasigmaw, 1, 26), +GEN_VXFORM_207(vshasigmad, 1, 27), + +GEN_VXFORM_DUAL(vsldoi, vpermxor, 22, 0xFF, PPC_ALTIVEC, PPC_NONE), + GEN_HANDLER_E(lxsdx, 0x1F, 0x0C, 0x12, 0, PPC_NONE, PPC2_VSX), +GEN_HANDLER_E(lxsiwax, 0x1F, 0x0C, 0x02, 0, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(lxsiwzx, 0x1F, 0x0C, 0x00, 0, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(lxsspx, 0x1F, 0x0C, 0x10, 0, PPC_NONE, PPC2_VSX207), GEN_HANDLER_E(lxvd2x, 0x1F, 0x0C, 0x1A, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(lxvdsx, 0x1F, 0x0C, 0x0A, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(lxvw4x, 0x1F, 0x0C, 0x18, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(stxsdx, 0x1F, 0xC, 0x16, 0, PPC_NONE, PPC2_VSX), +GEN_HANDLER_E(stxsiwx, 0x1F, 0xC, 0x04, 0, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(stxsspx, 0x1F, 0xC, 0x14, 0, PPC_NONE, PPC2_VSX207), GEN_HANDLER_E(stxvd2x, 0x1F, 0xC, 0x1E, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(stxvw4x, 0x1F, 0xC, 0x1C, 0, PPC_NONE, PPC2_VSX), +GEN_HANDLER_E(mfvsrwz, 0x1F, 0x13, 0x03, 0x0000F800, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(mtvsrwa, 0x1F, 0x13, 0x06, 0x0000F800, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(mtvsrwz, 0x1F, 0x13, 0x07, 0x0000F800, PPC_NONE, PPC2_VSX207), +#if defined(TARGET_PPC64) +GEN_HANDLER_E(mfvsrd, 0x1F, 0x13, 0x01, 0x0000F800, PPC_NONE, PPC2_VSX207), +GEN_HANDLER_E(mtvsrd, 0x1F, 0x13, 0x05, 0x0000F800, PPC_NONE, PPC2_VSX207), +#endif + #undef GEN_XX2FORM #define GEN_XX2FORM(name, opc2, opc3, fl2) \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 0, PPC_NONE, fl2), \ @@ -9938,6 +10763,17 @@ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2), \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 2, opc3, 0, PPC_NONE, fl2), \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 3, opc3, 0, PPC_NONE, fl2) +#undef GEN_XX3_RC_FORM +#define GEN_XX3_RC_FORM(name, opc2, opc3, fl2) \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x00, opc3 | 0x00, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x01, opc3 | 0x00, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x02, opc3 | 0x00, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x03, opc3 | 0x00, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x00, opc3 | 0x10, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x01, opc3 | 0x10, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x02, opc3 | 0x10, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x03, opc3 | 0x10, 0, PPC_NONE, fl2) + #undef GEN_XX3FORM_DM #define GEN_XX3FORM_DM(name, opc2, opc3) \ GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x00, opc3|0x00, 0, PPC_NONE, PPC2_VSX),\ @@ -9971,6 +10807,136 @@ GEN_XX2FORM(xvnabssp, 0x12, 0x1A, PPC2_VSX), GEN_XX2FORM(xvnegsp, 0x12, 0x1B, PPC2_VSX), GEN_XX3FORM(xvcpsgnsp, 0x00, 0x1A, PPC2_VSX), +GEN_XX3FORM(xsadddp, 0x00, 0x04, PPC2_VSX), +GEN_XX3FORM(xssubdp, 0x00, 0x05, PPC2_VSX), +GEN_XX3FORM(xsmuldp, 0x00, 0x06, PPC2_VSX), +GEN_XX3FORM(xsdivdp, 0x00, 0x07, PPC2_VSX), +GEN_XX2FORM(xsredp, 0x14, 0x05, PPC2_VSX), +GEN_XX2FORM(xssqrtdp, 0x16, 0x04, PPC2_VSX), +GEN_XX2FORM(xsrsqrtedp, 0x14, 0x04, PPC2_VSX), +GEN_XX3FORM(xstdivdp, 0x14, 0x07, PPC2_VSX), +GEN_XX2FORM(xstsqrtdp, 0x14, 0x06, PPC2_VSX), +GEN_XX3FORM(xsmaddadp, 0x04, 0x04, PPC2_VSX), +GEN_XX3FORM(xsmaddmdp, 0x04, 0x05, PPC2_VSX), +GEN_XX3FORM(xsmsubadp, 0x04, 0x06, PPC2_VSX), +GEN_XX3FORM(xsmsubmdp, 0x04, 0x07, PPC2_VSX), +GEN_XX3FORM(xsnmaddadp, 0x04, 0x14, PPC2_VSX), +GEN_XX3FORM(xsnmaddmdp, 0x04, 0x15, PPC2_VSX), +GEN_XX3FORM(xsnmsubadp, 0x04, 0x16, PPC2_VSX), +GEN_XX3FORM(xsnmsubmdp, 0x04, 0x17, PPC2_VSX), +GEN_XX2FORM(xscmpodp, 0x0C, 0x05, PPC2_VSX), +GEN_XX2FORM(xscmpudp, 0x0C, 0x04, PPC2_VSX), +GEN_XX3FORM(xsmaxdp, 0x00, 0x14, PPC2_VSX), +GEN_XX3FORM(xsmindp, 0x00, 0x15, PPC2_VSX), +GEN_XX2FORM(xscvdpsp, 0x12, 0x10, PPC2_VSX), +GEN_XX2FORM(xscvdpspn, 0x16, 0x10, PPC2_VSX207), +GEN_XX2FORM(xscvspdp, 0x12, 0x14, PPC2_VSX), +GEN_XX2FORM(xscvspdpn, 0x16, 0x14, PPC2_VSX207), +GEN_XX2FORM(xscvdpsxds, 0x10, 0x15, PPC2_VSX), +GEN_XX2FORM(xscvdpsxws, 0x10, 0x05, PPC2_VSX), +GEN_XX2FORM(xscvdpuxds, 0x10, 0x14, PPC2_VSX), +GEN_XX2FORM(xscvdpuxws, 0x10, 0x04, PPC2_VSX), +GEN_XX2FORM(xscvsxddp, 0x10, 0x17, PPC2_VSX), +GEN_XX2FORM(xscvuxddp, 0x10, 0x16, PPC2_VSX), +GEN_XX2FORM(xsrdpi, 0x12, 0x04, PPC2_VSX), +GEN_XX2FORM(xsrdpic, 0x16, 0x06, PPC2_VSX), +GEN_XX2FORM(xsrdpim, 0x12, 0x07, PPC2_VSX), +GEN_XX2FORM(xsrdpip, 0x12, 0x06, PPC2_VSX), +GEN_XX2FORM(xsrdpiz, 0x12, 0x05, PPC2_VSX), + +GEN_XX3FORM(xsaddsp, 0x00, 0x00, PPC2_VSX207), +GEN_XX3FORM(xssubsp, 0x00, 0x01, PPC2_VSX207), +GEN_XX3FORM(xsmulsp, 0x00, 0x02, PPC2_VSX207), +GEN_XX3FORM(xsdivsp, 0x00, 0x03, PPC2_VSX207), +GEN_XX2FORM(xsresp, 0x14, 0x01, PPC2_VSX207), +GEN_XX2FORM(xsrsp, 0x12, 0x11, PPC2_VSX207), +GEN_XX2FORM(xssqrtsp, 0x16, 0x00, PPC2_VSX207), +GEN_XX2FORM(xsrsqrtesp, 0x14, 0x00, PPC2_VSX207), +GEN_XX3FORM(xsmaddasp, 0x04, 0x00, PPC2_VSX207), +GEN_XX3FORM(xsmaddmsp, 0x04, 0x01, PPC2_VSX207), +GEN_XX3FORM(xsmsubasp, 0x04, 0x02, PPC2_VSX207), +GEN_XX3FORM(xsmsubmsp, 0x04, 0x03, PPC2_VSX207), +GEN_XX3FORM(xsnmaddasp, 0x04, 0x10, PPC2_VSX207), +GEN_XX3FORM(xsnmaddmsp, 0x04, 0x11, PPC2_VSX207), +GEN_XX3FORM(xsnmsubasp, 0x04, 0x12, PPC2_VSX207), +GEN_XX3FORM(xsnmsubmsp, 0x04, 0x13, PPC2_VSX207), +GEN_XX2FORM(xscvsxdsp, 0x10, 0x13, PPC2_VSX207), +GEN_XX2FORM(xscvuxdsp, 0x10, 0x12, PPC2_VSX207), + +GEN_XX3FORM(xvadddp, 0x00, 0x0C, PPC2_VSX), +GEN_XX3FORM(xvsubdp, 0x00, 0x0D, PPC2_VSX), +GEN_XX3FORM(xvmuldp, 0x00, 0x0E, PPC2_VSX), +GEN_XX3FORM(xvdivdp, 0x00, 0x0F, PPC2_VSX), +GEN_XX2FORM(xvredp, 0x14, 0x0D, PPC2_VSX), +GEN_XX2FORM(xvsqrtdp, 0x16, 0x0C, PPC2_VSX), +GEN_XX2FORM(xvrsqrtedp, 0x14, 0x0C, PPC2_VSX), +GEN_XX3FORM(xvtdivdp, 0x14, 0x0F, PPC2_VSX), +GEN_XX2FORM(xvtsqrtdp, 0x14, 0x0E, PPC2_VSX), +GEN_XX3FORM(xvmaddadp, 0x04, 0x0C, PPC2_VSX), +GEN_XX3FORM(xvmaddmdp, 0x04, 0x0D, PPC2_VSX), +GEN_XX3FORM(xvmsubadp, 0x04, 0x0E, PPC2_VSX), +GEN_XX3FORM(xvmsubmdp, 0x04, 0x0F, PPC2_VSX), +GEN_XX3FORM(xvnmaddadp, 0x04, 0x1C, PPC2_VSX), +GEN_XX3FORM(xvnmaddmdp, 0x04, 0x1D, PPC2_VSX), +GEN_XX3FORM(xvnmsubadp, 0x04, 0x1E, PPC2_VSX), +GEN_XX3FORM(xvnmsubmdp, 0x04, 0x1F, PPC2_VSX), +GEN_XX3FORM(xvmaxdp, 0x00, 0x1C, PPC2_VSX), +GEN_XX3FORM(xvmindp, 0x00, 0x1D, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpeqdp, 0x0C, 0x0C, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpgtdp, 0x0C, 0x0D, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpgedp, 0x0C, 0x0E, PPC2_VSX), +GEN_XX2FORM(xvcvdpsp, 0x12, 0x18, PPC2_VSX), +GEN_XX2FORM(xvcvdpsxds, 0x10, 0x1D, PPC2_VSX), +GEN_XX2FORM(xvcvdpsxws, 0x10, 0x0D, PPC2_VSX), +GEN_XX2FORM(xvcvdpuxds, 0x10, 0x1C, PPC2_VSX), +GEN_XX2FORM(xvcvdpuxws, 0x10, 0x0C, PPC2_VSX), +GEN_XX2FORM(xvcvsxddp, 0x10, 0x1F, PPC2_VSX), +GEN_XX2FORM(xvcvuxddp, 0x10, 0x1E, PPC2_VSX), +GEN_XX2FORM(xvcvsxwdp, 0x10, 0x0F, PPC2_VSX), +GEN_XX2FORM(xvcvuxwdp, 0x10, 0x0E, PPC2_VSX), +GEN_XX2FORM(xvrdpi, 0x12, 0x0C, PPC2_VSX), +GEN_XX2FORM(xvrdpic, 0x16, 0x0E, PPC2_VSX), +GEN_XX2FORM(xvrdpim, 0x12, 0x0F, PPC2_VSX), +GEN_XX2FORM(xvrdpip, 0x12, 0x0E, PPC2_VSX), +GEN_XX2FORM(xvrdpiz, 0x12, 0x0D, PPC2_VSX), + +GEN_XX3FORM(xvaddsp, 0x00, 0x08, PPC2_VSX), +GEN_XX3FORM(xvsubsp, 0x00, 0x09, PPC2_VSX), +GEN_XX3FORM(xvmulsp, 0x00, 0x0A, PPC2_VSX), +GEN_XX3FORM(xvdivsp, 0x00, 0x0B, PPC2_VSX), +GEN_XX2FORM(xvresp, 0x14, 0x09, PPC2_VSX), +GEN_XX2FORM(xvsqrtsp, 0x16, 0x08, PPC2_VSX), +GEN_XX2FORM(xvrsqrtesp, 0x14, 0x08, PPC2_VSX), +GEN_XX3FORM(xvtdivsp, 0x14, 0x0B, PPC2_VSX), +GEN_XX2FORM(xvtsqrtsp, 0x14, 0x0A, PPC2_VSX), +GEN_XX3FORM(xvmaddasp, 0x04, 0x08, PPC2_VSX), +GEN_XX3FORM(xvmaddmsp, 0x04, 0x09, PPC2_VSX), +GEN_XX3FORM(xvmsubasp, 0x04, 0x0A, PPC2_VSX), +GEN_XX3FORM(xvmsubmsp, 0x04, 0x0B, PPC2_VSX), +GEN_XX3FORM(xvnmaddasp, 0x04, 0x18, PPC2_VSX), +GEN_XX3FORM(xvnmaddmsp, 0x04, 0x19, PPC2_VSX), +GEN_XX3FORM(xvnmsubasp, 0x04, 0x1A, PPC2_VSX), +GEN_XX3FORM(xvnmsubmsp, 0x04, 0x1B, PPC2_VSX), +GEN_XX3FORM(xvmaxsp, 0x00, 0x18, PPC2_VSX), +GEN_XX3FORM(xvminsp, 0x00, 0x19, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpeqsp, 0x0C, 0x08, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpgtsp, 0x0C, 0x09, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpgesp, 0x0C, 0x0A, PPC2_VSX), +GEN_XX2FORM(xvcvspdp, 0x12, 0x1C, PPC2_VSX), +GEN_XX2FORM(xvcvspsxds, 0x10, 0x19, PPC2_VSX), +GEN_XX2FORM(xvcvspsxws, 0x10, 0x09, PPC2_VSX), +GEN_XX2FORM(xvcvspuxds, 0x10, 0x18, PPC2_VSX), +GEN_XX2FORM(xvcvspuxws, 0x10, 0x08, PPC2_VSX), +GEN_XX2FORM(xvcvsxdsp, 0x10, 0x1B, PPC2_VSX), +GEN_XX2FORM(xvcvuxdsp, 0x10, 0x1A, PPC2_VSX), +GEN_XX2FORM(xvcvsxwsp, 0x10, 0x0B, PPC2_VSX), +GEN_XX2FORM(xvcvuxwsp, 0x10, 0x0A, PPC2_VSX), +GEN_XX2FORM(xvrspi, 0x12, 0x08, PPC2_VSX), +GEN_XX2FORM(xvrspic, 0x16, 0x0A, PPC2_VSX), +GEN_XX2FORM(xvrspim, 0x12, 0x0B, PPC2_VSX), +GEN_XX2FORM(xvrspip, 0x12, 0x0A, PPC2_VSX), +GEN_XX2FORM(xvrspiz, 0x12, 0x09, PPC2_VSX), + #undef VSX_LOGICAL #define VSX_LOGICAL(name, opc2, opc3, fl2) \ GEN_XX3FORM(name, opc2, opc3, fl2) @@ -9980,6 +10946,9 @@ VSX_LOGICAL(xxlandc, 0x8, 0x11, PPC2_VSX), VSX_LOGICAL(xxlor, 0x8, 0x12, PPC2_VSX), VSX_LOGICAL(xxlxor, 0x8, 0x13, PPC2_VSX), VSX_LOGICAL(xxlnor, 0x8, 0x14, PPC2_VSX), +VSX_LOGICAL(xxleqv, 0x8, 0x17, PPC2_VSX207), +VSX_LOGICAL(xxlnand, 0x8, 0x16, PPC2_VSX207), +VSX_LOGICAL(xxlorc, 0x8, 0x15, PPC2_VSX207), GEN_XX3FORM(xxmrghw, 0x08, 0x02, PPC2_VSX), GEN_XX3FORM(xxmrglw, 0x08, 0x06, PPC2_VSX), GEN_XX2FORM(xxspltw, 0x08, 0x0A, PPC2_VSX), @@ -10260,8 +11229,13 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, case POWERPC_MMU_SOFT_74xx: #if defined(TARGET_PPC64) case POWERPC_MMU_64B: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_06a: + case POWERPC_MMU_2_06d: #endif - cpu_fprintf(f, " SDR1 " TARGET_FMT_lx "\n", env->spr[SPR_SDR1]); + cpu_fprintf(f, " SDR1 " TARGET_FMT_lx " DAR " TARGET_FMT_lx + " DSISR " TARGET_FMT_lx "\n", env->spr[SPR_SDR1], + env->spr[SPR_DAR], env->spr[SPR_DSISR]); break; case POWERPC_MMU_BOOKE206: cpu_fprintf(f, " MAS0 " TARGET_FMT_lx " MAS1 " TARGET_FMT_lx diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 445c3606fe..3eafbb0d1a 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -628,6 +628,9 @@ static inline void _spr_register(CPUPPCState *env, int num, spr->oea_read = oea_read; spr->oea_write = oea_write; #endif +#if defined(CONFIG_KVM) + spr->one_reg_id = one_reg_id, +#endif env->spr[num] = initial_value; } @@ -1064,7 +1067,7 @@ static void gen_spr_amr (CPUPPCState *env) spr_register_kvm(env, SPR_AMR, "AMR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, - KVM_REG_PPC_AMR, 0xffffffffffffffffULL); + KVM_REG_PPC_AMR, 0); spr_register_kvm(env, SPR_UAMOR, "UAMOR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, @@ -2578,7 +2581,6 @@ static void gen_spr_8xx (CPUPPCState *env) * HRMOR => SPR 313 (Power 2.04 hypv) * HSRR0 => SPR 314 (Power 2.04 hypv) * HSRR1 => SPR 315 (Power 2.04 hypv) - * LPCR => SPR 316 (970) * LPIDR => SPR 317 (970) * EPR => SPR 702 (Power 2.04 emb) * perf => 768-783 (Power 2.04) @@ -4720,7 +4722,7 @@ POWERPC_FAMILY(e5500)(ObjectClass *oc, void *data) PPC_FLOAT_STFIWX | PPC_WAIT | PPC_MEM_TLBSYNC | PPC_TLBIVAX | PPC_MEM_SYNC | PPC_64B | PPC_POPCNTB | PPC_POPCNTWD; - pcc->insns_flags2 = PPC2_BOOKE206 | PPC2_PRCNTL; + pcc->insns_flags2 = PPC2_BOOKE206 | PPC2_PRCNTL | PPC2_PERM_ISA206; pcc->msr_mask = 0x000000009402FB36ULL; pcc->mmu_model = POWERPC_MMU_BOOKE206; pcc->excp_model = POWERPC_EXCP_BOOKE; @@ -6644,33 +6646,13 @@ static void init_proc_970 (CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* XXX : not implemented */ - spr_register(env, SPR_750FX_HID2, "HID2", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ spr_register(env, SPR_970_HID5, "HID5", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, POWERPC970_HID5_INIT); - /* XXX : not implemented */ - spr_register(env, SPR_L2CR, "L2CR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, spr_access_nop, - 0x00000000); /* Memory management */ /* XXX: not correct */ gen_low_BATs(env); - /* XXX : not implemented */ - spr_register(env, SPR_MMUCFG, "MMUCFG", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, SPR_NOACCESS, - 0x00000000); /* TOFIX */ - /* XXX : not implemented */ - spr_register(env, SPR_MMUCSR0, "MMUCSR0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); /* TOFIX */ spr_register(env, SPR_HIOR, "SPR_HIOR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_hior, &spr_write_hior, @@ -6744,44 +6726,24 @@ static void init_proc_970FX (CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* XXX : not implemented */ - spr_register(env, SPR_750FX_HID2, "HID2", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ spr_register(env, SPR_970_HID5, "HID5", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, POWERPC970_HID5_INIT); - /* XXX : not implemented */ - spr_register(env, SPR_L2CR, "L2CR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, spr_access_nop, - 0x00000000); /* Memory management */ /* XXX: not correct */ gen_low_BATs(env); - /* XXX : not implemented */ - spr_register(env, SPR_MMUCFG, "MMUCFG", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, SPR_NOACCESS, - 0x00000000); /* TOFIX */ - /* XXX : not implemented */ - spr_register(env, SPR_MMUCSR0, "MMUCSR0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); /* TOFIX */ spr_register(env, SPR_HIOR, "SPR_HIOR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_hior, &spr_write_hior, 0x00000000); spr_register(env, SPR_CTRL, "SPR_CTRL", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + SPR_NOACCESS, &spr_write_generic, 0x00000000); spr_register(env, SPR_UCTRL, "SPR_UCTRL", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, SPR_NOACCESS, 0x00000000); spr_register(env, SPR_VRSAVE, "SPR_VRSAVE", &spr_read_generic, &spr_write_generic, @@ -6830,106 +6792,6 @@ POWERPC_FAMILY(970FX)(ObjectClass *oc, void *data) POWERPC_FLAG_BUS_CLK; } -static int check_pow_970GX (CPUPPCState *env) -{ - if (env->spr[SPR_HID0] & 0x00600000) - return 1; - - return 0; -} - -static void init_proc_970GX (CPUPPCState *env) -{ - gen_spr_ne_601(env); - gen_spr_7xx(env); - /* Time base */ - gen_tbl(env); - /* Hardware implementation registers */ - /* XXX : not implemented */ - spr_register(env, SPR_HID0, "HID0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_clear, - 0x60000000); - /* XXX : not implemented */ - spr_register(env, SPR_HID1, "HID1", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_750FX_HID2, "HID2", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_970_HID5, "HID5", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - POWERPC970_HID5_INIT); - /* XXX : not implemented */ - spr_register(env, SPR_L2CR, "L2CR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, spr_access_nop, - 0x00000000); - /* Memory management */ - /* XXX: not correct */ - gen_low_BATs(env); - /* XXX : not implemented */ - spr_register(env, SPR_MMUCFG, "MMUCFG", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, SPR_NOACCESS, - 0x00000000); /* TOFIX */ - /* XXX : not implemented */ - spr_register(env, SPR_MMUCSR0, "MMUCSR0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); /* TOFIX */ - spr_register(env, SPR_HIOR, "SPR_HIOR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_hior, &spr_write_hior, - 0x00000000); -#if !defined(CONFIG_USER_ONLY) - env->slb_nr = 32; -#endif - init_excp_970(env); - env->dcache_line_size = 128; - env->icache_line_size = 128; - /* Allocate hardware IRQ controller */ - ppc970_irq_init(env); - /* Can't find information on what this should be on reset. This - * value is the one used by 74xx processors. */ - vscr_init(env, 0x00010000); -} - -POWERPC_FAMILY(970GX)(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); - - dc->desc = "PowerPC 970 GX"; - pcc->init_proc = init_proc_970GX; - pcc->check_pow = check_pow_970GX; - pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | - PPC_FLOAT_STFIWX | - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | - PPC_MEM_SYNC | PPC_MEM_EIEIO | - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | - PPC_64B | PPC_ALTIVEC | - PPC_SEGMENT_64B | PPC_SLBI; - pcc->msr_mask = 0x800000000204FF36ULL; - pcc->mmu_model = POWERPC_MMU_64B; -#if defined(CONFIG_SOFTMMU) - pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; -#endif - pcc->excp_model = POWERPC_EXCP_970; - pcc->bus_model = PPC_FLAGS_INPUT_970; - pcc->bfd_mach = bfd_mach_ppc64; - pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | - POWERPC_FLAG_BE | POWERPC_FLAG_PMM | - POWERPC_FLAG_BUS_CLK; -} - static int check_pow_970MP (CPUPPCState *env) { if (env->spr[SPR_HID0] & 0x01C00000) @@ -6956,37 +6818,23 @@ static void init_proc_970MP (CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* XXX : not implemented */ - spr_register(env, SPR_750FX_HID2, "HID2", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ spr_register(env, SPR_970_HID5, "HID5", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, POWERPC970_HID5_INIT); /* XXX : not implemented */ - spr_register(env, SPR_L2CR, "L2CR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, spr_access_nop, - 0x00000000); /* Memory management */ /* XXX: not correct */ gen_low_BATs(env); - /* XXX : not implemented */ - spr_register(env, SPR_MMUCFG, "MMUCFG", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, SPR_NOACCESS, - 0x00000000); /* TOFIX */ - /* XXX : not implemented */ - spr_register(env, SPR_MMUCSR0, "MMUCSR0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); /* TOFIX */ spr_register(env, SPR_HIOR, "SPR_HIOR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_hior, &spr_write_hior, 0x00000000); + /* Logical partitionning */ + spr_register_kvm(env, SPR_LPCR, "LPCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_LPCR, 0x00000000); #if !defined(CONFIG_USER_ONLY) env->slb_nr = 32; #endif @@ -7048,49 +6896,34 @@ static void init_proc_power5plus(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* XXX : not implemented */ - spr_register(env, SPR_750FX_HID2, "HID2", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ spr_register(env, SPR_970_HID5, "HID5", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, POWERPC970_HID5_INIT); - /* XXX : not implemented */ - spr_register(env, SPR_L2CR, "L2CR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, spr_access_nop, - 0x00000000); /* Memory management */ /* XXX: not correct */ gen_low_BATs(env); - /* XXX : not implemented */ - spr_register(env, SPR_MMUCFG, "MMUCFG", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, SPR_NOACCESS, - 0x00000000); /* TOFIX */ - /* XXX : not implemented */ - spr_register(env, SPR_MMUCSR0, "MMUCSR0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); /* TOFIX */ spr_register(env, SPR_HIOR, "SPR_HIOR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_hior, &spr_write_hior, 0x00000000); spr_register(env, SPR_CTRL, "SPR_CTRL", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + SPR_NOACCESS, &spr_write_generic, 0x00000000); spr_register(env, SPR_UCTRL, "SPR_UCTRL", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, SPR_NOACCESS, 0x00000000); spr_register(env, SPR_VRSAVE, "SPR_VRSAVE", &spr_read_generic, &spr_write_generic, &spr_read_generic, &spr_write_generic, 0x00000000); + /* Logical partitionning */ + spr_register_kvm(env, SPR_LPCR, "LPCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_LPCR, 0x00000000); #if !defined(CONFIG_USER_ONLY) env->slb_nr = 64; #endif @@ -7177,21 +7010,15 @@ static void init_proc_POWER7 (CPUPPCState *env) &spr_read_generic, &spr_write_generic, KVM_REG_PPC_PMC6, 0x00000000); #endif /* !CONFIG_USER_ONLY */ - /* Memory management */ - /* XXX : not implemented */ - spr_register(env, SPR_MMUCFG, "MMUCFG", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, SPR_NOACCESS, - 0x00000000); /* TOFIX */ gen_spr_amr(env); /* XXX : not implemented */ spr_register(env, SPR_CTRL, "SPR_CTRLT", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + SPR_NOACCESS, &spr_write_generic, 0x80800000); spr_register(env, SPR_UCTRL, "SPR_CTRLF", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, SPR_NOACCESS, 0x80800000); spr_register(env, SPR_VRSAVE, "SPR_VRSAVE", &spr_read_generic, &spr_write_generic, @@ -7201,6 +7028,11 @@ static void init_proc_POWER7 (CPUPPCState *env) &spr_read_generic, &spr_write_generic, &spr_read_generic, &spr_write_generic, 0x00000000); + /* Logical partitionning */ + spr_register_kvm(env, SPR_LPCR, "LPCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_LPCR, 0x00000000); #if !defined(CONFIG_USER_ONLY) env->slb_nr = 32; #endif @@ -7229,14 +7061,19 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_FRSQRTES | PPC_FLOAT_STFIWX | + PPC_FLOAT_EXT | PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_64B | PPC_ALTIVEC | PPC_SEGMENT_64B | PPC_SLBI | PPC_POPCNTB | PPC_POPCNTWD; - pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205; + pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205 | + PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | + PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 | + PPC2_FP_TST_ISA206; pcc->msr_mask = 0x800000000284FF37ULL; pcc->mmu_model = POWERPC_MMU_2_06; #if defined(CONFIG_SOFTMMU) @@ -7267,14 +7104,19 @@ POWERPC_FAMILY(POWER7P)(ObjectClass *oc, void *data) pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_FRSQRTES | PPC_FLOAT_STFIWX | + PPC_FLOAT_EXT | PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_64B | PPC_ALTIVEC | PPC_SEGMENT_64B | PPC_SLBI | PPC_POPCNTB | PPC_POPCNTWD; - pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205; + pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205 | + PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | + PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 | + PPC2_FP_TST_ISA206; pcc->msr_mask = 0x800000000204FF37ULL; pcc->mmu_model = POWERPC_MMU_2_06; #if defined(CONFIG_SOFTMMU) @@ -7291,6 +7133,18 @@ POWERPC_FAMILY(POWER7P)(ObjectClass *oc, void *data) pcc->l1_icache_size = 0x8000; } +static void init_proc_POWER8(CPUPPCState *env) +{ + /* inherit P7 */ + init_proc_POWER7(env); + + /* P8 supports the TAR */ + spr_register(env, SPR_TAR, "TAR", + &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -7300,19 +7154,25 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) dc->desc = "POWER8"; pcc->pvr = CPU_POWERPC_POWER8_BASE; pcc->pvr_mask = CPU_POWERPC_POWER8_MASK; - pcc->init_proc = init_proc_POWER7; + pcc->init_proc = init_proc_POWER8; pcc->check_pow = check_pow_nocheck; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_FRSQRTES | PPC_FLOAT_STFIWX | + PPC_FLOAT_EXT | PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | - PPC_64B | PPC_ALTIVEC | + PPC_64B | PPC_64BX | PPC_ALTIVEC | PPC_SEGMENT_64B | PPC_SLBI | PPC_POPCNTB | PPC_POPCNTWD; - pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX; + pcc->insns_flags2 = PPC2_VSX | PPC2_VSX207 | PPC2_DFP | PPC2_DBRX | + PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | + PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 | + PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 | + PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207; pcc->msr_mask = 0x800000000284FF36ULL; pcc->mmu_model = POWERPC_MMU_2_06; #if defined(CONFIG_SOFTMMU) @@ -7987,14 +7847,12 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) max_smt, kvm_enabled() ? "KVM" : "TCG"); return; } + + cpu->cpu_dt_id = (cs->cpu_index / smp_threads) * max_smt + + (cs->cpu_index % smp_threads); #endif - if (kvm_enabled()) { - if (kvmppc_fixup_cpu(cpu) != 0) { - error_setg(errp, "Unable to virtualize selected CPU with KVM"); - return; - } - } else if (tcg_enabled()) { + if (tcg_enabled()) { if (ppc_fixup_cpu(cpu) != 0) { error_setg(errp, "Unable to emulate selected CPU with TCG"); return; @@ -8149,9 +8007,10 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) } printf("PowerPC %-12s : PVR %08x MSR %016" PRIx64 "\n" " MMU model : %s\n", - pcc->name, pcc->pvr, pcc->msr_mask, mmu_model); + object_class_get_name(OBJECT_CLASS(pcc)), + pcc->pvr, pcc->msr_mask, mmu_model); #if !defined(CONFIG_USER_ONLY) - if (env->tlb != NULL) { + if (env->tlb.tlb6) { printf(" %d %s TLB in %d ways\n", env->nb_tlb, env->id_tlbs ? "splitted" : "merged", env->nb_ways); @@ -8598,6 +8457,7 @@ static void ppc_cpu_initfn(Object *obj) cs->env_ptr = env; cpu_exec_init(env); + cpu->cpu_dt_id = cs->cpu_index; env->msr_mask = pcc->msr_mask; env->mmu_model = pcc->mmu_model; diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c index ff57b806e4..1a8c1cc39f 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 96c2b4a7e9..effe84b102 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/ioinst.h b/target-s390x/ioinst.h index 613da49b3b..5bbc67d15e 100644 --- a/target-s390x/ioinst.h +++ b/target-s390x/ioinst.h @@ -212,6 +212,8 @@ typedef struct IOIntCode { #define IO_INT_WORD_ISC(_int_word) ((_int_word & 0x38000000) >> 24) #define ISC_TO_ISC_BITS(_isc) ((0x80 >> _isc) << 24) +#define IO_INT_WORD_AI 0x80000000 + int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid, int *schid); void ioinst_handle_xsch(S390CPU *cpu, uint64_t reg1); diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index f60ccdc326..56b9af7505 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); @@ -816,8 +891,12 @@ void kvm_s390_io_interrupt(S390CPU *cpu, uint16_t subchannel_id, { uint32_t type; - type = ((subchannel_id & 0xff00) << 24) | - ((subchannel_id & 0x00060) << 22) | (subchannel_nr << 16); + if (io_int_word & IO_INT_WORD_AI) { + type = KVM_S390_INT_IO(1, 0, 0, 0); + } else { + type = ((subchannel_id & 0xff00) << 24) | + ((subchannel_id & 0x00060) << 22) | (subchannel_nr << 16); + } kvm_s390_interrupt_internal(cpu, type, ((uint32_t)subchannel_id << 16) | subchannel_nr, ((uint64_t)io_int_parm << 32) | io_int_word, 1); diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c index 10d04252d5..728456f295 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/tcg/tcg.c b/tcg/tcg.c index ffc851e0c6..f1e0763432 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -41,7 +41,7 @@ #include "qemu/host-utils.h" #include "qemu/timer.h" -/* Note: the long term plan is to reduce the dependancies on the QEMU +/* Note: the long term plan is to reduce the dependencies on the QEMU CPU definitions. Currently they are used for qemu_ld/st instructions */ #define NO_CPU_IO_DEFS diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index fe5af756c5..471ba47dde 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 3851880de3..89b53d4d4d 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 cf90de0b8b..c449e8ab4a 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/051 b/tests/qemu-iotests/051 index 46345fb155..14694e176b 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -78,6 +78,15 @@ run_qemu -drive file="$TEST_IMG",format=qcow2,unknown_opt=1234 run_qemu -drive file="$TEST_IMG",format=qcow2,unknown_opt=foo echo +echo === Unknown protocol option === +echo + +run_qemu -drive file="$TEST_IMG",format=qcow2,file.unknown_opt= +run_qemu -drive file="$TEST_IMG",format=qcow2,file.unknown_opt=on +run_qemu -drive file="$TEST_IMG",format=qcow2,file.unknown_opt=1234 +run_qemu -drive file="$TEST_IMG",format=qcow2,file.unknown_opt=foo + +echo echo === Invalid format === echo diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index 7de18704f8..f5e33ff395 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -17,6 +17,21 @@ Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' +=== Unknown protocol option === + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt= +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=: could not open disk image TEST_DIR/t.qcow2: Block protocol 'file' doesn't support the option 'unknown_opt' + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=on +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=on: could not open disk image TEST_DIR/t.qcow2: Block protocol 'file' doesn't support the option 'unknown_opt' + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=1234 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=1234: could not open disk image TEST_DIR/t.qcow2: Block protocol 'file' doesn't support the option 'unknown_opt' + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=foo +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,file.unknown_opt=foo: could not open disk image TEST_DIR/t.qcow2: Block protocol 'file' doesn't support the option 'unknown_opt' + + === Invalid format === Testing: -drive file=TEST_DIR/t.qcow2,format=foo diff --git a/tests/qemu-iotests/081 b/tests/qemu-iotests/081 index f053f11942..b512d00cc8 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 4fe2f95f63..84aeb0c730 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/085 b/tests/qemu-iotests/085 new file mode 100755 index 0000000000..33c8dc410a --- /dev/null +++ b/tests/qemu-iotests/085 @@ -0,0 +1,192 @@ +#!/bin/bash +# +# Live snapshot tests +# +# This tests live snapshots of images on a running QEMU instance, using +# QMP commands. Both single disk snapshots, and transactional group +# snapshots are performed. +# +# Copyright (C) 2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=jcody@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! +qemu_pid= + +QMP_IN="${TEST_DIR}/qmp-in-$$" +QMP_OUT="${TEST_DIR}/qmp-out-$$" + +snapshot_virt0="snapshot-v0.qcow2" +snapshot_virt1="snapshot-v1.qcow2" + +MAX_SNAPSHOTS=10 + +_cleanup() +{ + kill -KILL ${qemu_pid} + wait ${qemu_pid} 2>/dev/null # silent kill + + rm -f "${QMP_IN}" "${QMP_OUT}" + for i in $(seq 1 ${MAX_SNAPSHOTS}) + do + rm -f "${TEST_DIR}/${i}-${snapshot_virt0}" + rm -f "${TEST_DIR}/${i}-${snapshot_virt1}" + done + _cleanup_test_img + +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +# Wait for expected QMP response from QEMU. Will time out +# after 10 seconds, which counts as failure. +# +# $1 is the string to expect +# +# If $silent is set to anything but an empty string, then +# response is not echoed out. +function timed_wait_for() +{ + while read -t 10 resp <&5 + do + if [ "${silent}" == "" ]; then + echo "${resp}" | _filter_testdir | _filter_qemu + fi + grep -q "${1}" < <(echo ${resp}) + if [ $? -eq 0 ]; then + return + fi + done + echo "Timeout waiting for ${1}" + exit 1 # Timeout means the test failed +} + +# Sends QMP command to QEMU, and waits for the expected response +# +# ${1}: String of the QMP command to send +# ${2}: String that the QEMU response should contain +function send_qmp_cmd() +{ + echo "${1}" >&6 + timed_wait_for "${2}" +} + +# ${1}: unique identifier for the snapshot filename +function create_single_snapshot() +{ + cmd="{ 'execute': 'blockdev-snapshot-sync', + 'arguments': { 'device': 'virtio0', + 'snapshot-file':'"${TEST_DIR}/${1}-${snapshot_virt0}"', + 'format': 'qcow2' } }" + send_qmp_cmd "${cmd}" "return" +} + +# ${1}: unique identifier for the snapshot filename +function create_group_snapshot() +{ + cmd="{ 'execute': 'transaction', 'arguments': + {'actions': [ + { 'type': 'blockdev-snapshot-sync', 'data' : + { 'device': 'virtio0', + 'snapshot-file': '"${TEST_DIR}/${1}-${snapshot_virt0}"' } }, + { 'type': 'blockdev-snapshot-sync', 'data' : + { 'device': 'virtio1', + 'snapshot-file': '"${TEST_DIR}/${1}-${snapshot_virt1}"' } } ] + } }" + + send_qmp_cmd "${cmd}" "return" +} + +size=128M + +mkfifo "${QMP_IN}" +mkfifo "${QMP_OUT}" + +_make_test_img $size +mv "${TEST_IMG}" "${TEST_IMG}.orig" +_make_test_img $size + +echo +echo === Running QEMU === +echo + +"${QEMU}" -nographic -monitor none -serial none -qmp stdio\ + -drive file="${TEST_IMG}.orig",if=virtio\ + -drive file="${TEST_IMG}",if=virtio 2>&1 >"${QMP_OUT}" <"${QMP_IN}"& +qemu_pid=$! + +# redirect fifos to file descriptors, to keep from blocking +exec 5<"${QMP_OUT}" +exec 6>"${QMP_IN}" + +# Don't print response, since it has version information in it +silent=yes timed_wait_for "capabilities" + +echo +echo === Sending capabilities === +echo + +send_qmp_cmd "{ 'execute': 'qmp_capabilities' }" "return" + +echo +echo === Create a single snapshot on virtio0 === +echo + +create_single_snapshot 1 + + +echo +echo === Invalid command - missing device and nodename === +echo + +send_qmp_cmd "{ 'execute': 'blockdev-snapshot-sync', + 'arguments': { 'snapshot-file':'"${TEST_DIR}"/1-${snapshot_virt0}', + 'format': 'qcow2' } }" "error" + +echo +echo === Invalid command - missing snapshot-file === +echo + +send_qmp_cmd "{ 'execute': 'blockdev-snapshot-sync', + 'arguments': { 'device': 'virtio0', + 'format': 'qcow2' } }" "error" +echo +echo +echo === Create several transactional group snapshots === +echo + +for i in $(seq 2 ${MAX_SNAPSHOTS}) +do + create_group_snapshot ${i} +done + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/085.out b/tests/qemu-iotests/085.out new file mode 100644 index 0000000000..0f2b17f99e --- /dev/null +++ b/tests/qemu-iotests/085.out @@ -0,0 +1,55 @@ +QA output created by 085 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 + +=== Running QEMU === + + +=== Sending capabilities === + +{"return": {}} + +=== Create a single snapshot on virtio0 === + +Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2.orig' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +{"return": {}} + +=== Invalid command - missing device and nodename === + +{"error": {"class": "GenericError", "desc": "Cannot find device= nor node_name="}} + +=== Invalid command - missing snapshot-file === + +{"error": {"class": "GenericError", "desc": "Parameter 'snapshot-file' is missing"}} + + +=== Create several transactional group snapshots === + +Formatting 'TEST_DIR/2-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/1-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/2-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/t.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +{"return": {}} +Formatting 'TEST_DIR/3-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/3-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/2-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +{"return": {}} +Formatting 'TEST_DIR/4-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/4-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/3-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +{"return": {}} +Formatting 'TEST_DIR/5-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/5-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/4-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +{"return": {}} +Formatting 'TEST_DIR/6-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/6-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/5-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +{"return": {}} +Formatting 'TEST_DIR/7-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/7-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/6-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +{"return": {}} +Formatting 'TEST_DIR/8-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/8-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/7-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +{"return": {}} +Formatting 'TEST_DIR/9-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/9-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/8-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +{"return": {}} +Formatting 'TEST_DIR/10-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v0.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +Formatting 'TEST_DIR/10-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file='TEST_DIR/9-snapshot-v1.qcow2' backing_fmt='qcow2' encryption=off cluster_size=65536 lazy_refcounts=off +{"return": {}} +*** done diff --git a/tests/qemu-iotests/086 b/tests/qemu-iotests/086 new file mode 100755 index 0000000000..48fe85bc43 --- /dev/null +++ b/tests/qemu-iotests/086 @@ -0,0 +1,65 @@ +#!/bin/bash +# +# Test qemu-img progress output +# +# Copyright (C) 2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +function run_qemu_img() +{ + echo + echo Testing: "$@" | _filter_testdir +} + +size=128M + +_make_test_img $size +$QEMU_IO -c 'write 0 1M' $TEST_IMG | _filter_qemu_io +$QEMU_IO -c 'write 2M 1M' $TEST_IMG | _filter_qemu_io +$QEMU_IO -c 'write 4M 1M' $TEST_IMG | _filter_qemu_io +$QEMU_IO -c 'write 32M 1M' $TEST_IMG | _filter_qemu_io + +$QEMU_IMG convert -p -O $IMGFMT -f $IMGFMT "$TEST_IMG" "$TEST_IMG".base 2>&1 |\ + _filter_testdir | sed -e 's/\r/\n/g' + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/086.out b/tests/qemu-iotests/086.out new file mode 100644 index 0000000000..9c0bf23718 --- /dev/null +++ b/tests/qemu-iotests/086.out @@ -0,0 +1,18 @@ +QA output created by 086 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1048576/1048576 bytes at offset 2097152 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1048576/1048576 bytes at offset 4194304 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1048576/1048576 bytes at offset 33554432 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + (0.00/100%) + (25.00/100%) + (50.00/100%) + (75.00/100%) + (100.00/100%) + (100.00/100%) + +*** done diff --git a/tests/qemu-iotests/087 b/tests/qemu-iotests/087 new file mode 100755 index 0000000000..53b6c43bff --- /dev/null +++ b/tests/qemu-iotests/087 @@ -0,0 +1,122 @@ +#!/bin/bash +# +# Test unsupported blockdev-add cases +# +# Copyright (C) 2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +function do_run_qemu() +{ + echo Testing: "$@" + $QEMU -nographic -qmp stdio -serial none "$@" + echo +} + +function run_qemu() +{ + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | sed -e 's/\("actual-size":\s*\)[0-9]\+/\1SIZE/g' +} + +size=128M + +_make_test_img $size + +echo +echo === Missing ID === +echo + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "blockdev-add", + "arguments": { + "options": { + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_IMG" + } + } + } + } +{ "execute": "quit" } +EOF + +echo +echo === aio=native without O_DIRECT === +echo + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "blockdev-add", + "arguments": { + "options": { + "driver": "$IMGFMT", + "id": "disk", + "aio": "native", + "file": { + "driver": "file", + "filename": "$TEST_IMG" + } + } + } + } +{ "execute": "quit" } +EOF + +echo +echo === Encrypted image === +echo + +_make_test_img -o encryption=on $size +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "blockdev-add", + "arguments": { + "options": { + "driver": "$IMGFMT", + "id": "disk", + "file": { + "driver": "file", + "filename": "$TEST_IMG" + } + } + } + } +{ "execute": "quit" } +EOF + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/087.out b/tests/qemu-iotests/087.out new file mode 100644 index 0000000000..b87103252e --- /dev/null +++ b/tests/qemu-iotests/087.out @@ -0,0 +1,40 @@ +QA output created by 087 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 + +=== Missing ID === + +Testing: +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "Block device needs an ID"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}} + + +=== aio=native without O_DIRECT === + +Testing: +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "aio=native requires cache.direct=true"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}} + + +=== Encrypted image === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on +Testing: +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "blockdev-add doesn't support encrypted devices"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}} + +*** done diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 71e9a7462d..881079bdb9 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 db127d924d..e96eafdf43 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -58,30 +58,33 @@ 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 +085 rw auto quick +086 rw auto quick +087 rw auto quick diff --git a/tests/test-int128.c b/tests/test-int128.c index 5aca032e0c..0772ef7538 100644 --- a/tests/test-int128.c +++ b/tests/test-int128.c @@ -11,6 +11,19 @@ #include "qemu/int128.h" #include "qemu/osdep.h" +/* clang doesn't support __noclone__ but it does have a mechanism for + * telling us this. We assume that if we don't have __has_attribute() + * then this is GCC and that GCC always supports __noclone__. + */ +#if defined(__has_attribute) +#if !__has_attribute(__noclone__) +#define ATTRIBUTE_NOCLONE +#endif +#endif +#ifndef ATTRIBUTE_NOCLONE +#define ATTRIBUTE_NOCLONE __attribute__((__noclone__)) +#endif + static uint32_t tests[8] = { 0x00000000, 0x00000001, 0x7FFFFFFE, 0x7FFFFFFF, 0x80000000, 0x80000001, 0xFFFFFFFE, 0xFFFFFFFF, @@ -164,7 +177,7 @@ static void test_gt(void) /* Make sure to test undefined behavior at runtime! */ -static void __attribute__((__noinline__, __noclone__)) +static void __attribute__((__noinline__)) ATTRIBUTE_NOCLONE test_rshift_one(uint32_t x, int n, uint64_t h, uint64_t l) { Int128 a = expand(x); diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c index 5a3e82a854..8e62c2d8ad 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 38bdf5ec7c..64d72f6f05 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 6eb7dc5bcf..2dffafc1f4 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 f31d168d63..105f4cf94b 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 6bff950eb6..8166cf1b05 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 37130638ff..aec420292c 100644 --- a/trace-events +++ b/trace-events @@ -1020,6 +1020,15 @@ gd_switch(int width, int height) "width=%d, height=%d" gd_update(int x, int y, int w, int h) "x=%d, y=%d, w=%d, h=%d" gd_key_event(int gdk_keycode, int qemu_keycode, const char *action) "translated GDK keycode %d to QEMU keycode %d (%s)" +# ui/input.c +input_event_key_number(int conidx, int number, bool down) "con %d, key number 0x%d, down %d" +input_event_key_qcode(int conidx, const char *qcode, bool down) "con %d, key qcode %s, down %d" +input_event_btn(int conidx, const char *btn, bool down) "con %d, button %s, down %d" +input_event_rel(int conidx, const char *axis, int value) "con %d, axis %s, value %d" +input_event_abs(int conidx, const char *axis, int value) "con %d, axis %s, value 0x%x" +input_event_sync(void) "" +input_mouse_mode(int absolute) "absolute %d" + # hw/display/vmware_vga.c vmware_value_read(uint32_t index, uint32_t value) "index %d, value 0x%x" vmware_value_write(uint32_t index, uint32_t value) "index %d, value 0x%x" @@ -1136,6 +1145,7 @@ xics_ics_eoi(int nr) "ics_eoi: irq %#x" # hw/ppc/spapr_iommu.c spapr_iommu_put(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tce=0x%"PRIx64" ret=%"PRId64 +spapr_iommu_get(uint64_t liobn, uint64_t ioba, uint64_t ret, uint64_t tce) "liobn=%"PRIx64" ioba=0x%"PRIx64" ret=%"PRId64" tce=0x%"PRIx64 spapr_iommu_xlate(uint64_t liobn, uint64_t ioba, uint64_t tce, unsigned perm, unsigned pgsize) "liobn=%"PRIx64" 0x%"PRIx64" -> 0x%"PRIx64" perm=%u mask=%x" spapr_iommu_new_table(uint64_t liobn, void *tcet, void *table, int fd) "liobn=%"PRIx64" tcet=%p table=%p fd=%d" @@ -1157,11 +1167,17 @@ css_chpid_add(uint8_t cssid, uint8_t chpid, uint8_t type) "CSS: add chpid %x.%02 css_new_image(uint8_t cssid, const char *default_cssid) "CSS: add css image %02x %s" css_assign_subch(const char *do_assign, uint8_t cssid, uint8_t ssid, uint16_t schid, uint16_t devno) "CSS: %s %x.%x.%04x (devno %04x)" css_io_interrupt(int cssid, int ssid, int schid, uint32_t intparm, uint8_t isc, const char *conditional) "CSS: I/O interrupt on sch %x.%x.%04x (intparm %08x, isc %x) %s" +css_adapter_interrupt(uint8_t isc) "CSS: adapter I/O interrupt (isc %x)" # hw/s390x/virtio-ccw.c 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" @@ -1170,6 +1186,9 @@ kvm_ioctl(int type, void *arg) "type 0x%x, arg %p" kvm_vm_ioctl(int type, void *arg) "type 0x%x, arg %p" kvm_vcpu_ioctl(int cpu_index, int type, void *arg) "cpu_index %d, type 0x%x, arg %p" kvm_run_exit(int cpu_index, uint32_t reason) "cpu_index %d, reason %d" +kvm_device_ioctl(int fd, int type, void *arg) "dev fd %d, type 0x%x, arg %p" +kvm_failed_spr_set(int str, const char *msg) "Warning: Unable to set SPR %d to KVM: %s" +kvm_failed_spr_get(int str, const char *msg) "Warning: Unable to retrieve SPR %d from KVM: %s" # memory.c memory_region_ops_read(void *mr, uint64_t addr, uint64_t value, unsigned size) "mr %p addr %#"PRIx64" value %#"PRIx64" size %u" diff --git a/ui/Makefile.objs b/ui/Makefile.objs index f33be47576..6f2294efda 100644 --- a/ui/Makefile.objs +++ b/ui/Makefile.objs @@ -7,14 +7,14 @@ vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o vnc-obj-$(CONFIG_VNC_WS) += vnc-ws.o vnc-obj-y += vnc-jobs.o -common-obj-y += keymaps.o console.o cursor.o input.o qemu-pixman.o +common-obj-y += keymaps.o console.o cursor.o input.o input-legacy.o qemu-pixman.o common-obj-$(CONFIG_SPICE) += spice-core.o spice-input.o spice-display.o -common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o +common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o sdl2.o common-obj-$(CONFIG_COCOA) += cocoa.o common-obj-$(CONFIG_CURSES) += curses.o common-obj-$(CONFIG_VNC) += $(vnc-obj-y) common-obj-$(CONFIG_GTK) += gtk.o x_keymap.o -$(obj)/sdl.o $(obj)/sdl_zoom.o: QEMU_CFLAGS += $(SDL_CFLAGS) +$(obj)/sdl.o $(obj)/sdl_zoom.o $(obj)/sdl2.o: QEMU_CFLAGS += $(SDL_CFLAGS) $(obj)/gtk.o: QEMU_CFLAGS += $(GTK_CFLAGS) $(VTE_CFLAGS) diff --git a/ui/cocoa.m b/ui/cocoa.m index 866177770a..f20fd1ffa2 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -27,6 +27,7 @@ #include "qemu-common.h" #include "ui/console.h" +#include "ui/input.h" #include "sysemu/sysemu.h" #ifndef MAC_OS_X_VERSION_10_4 @@ -49,14 +50,6 @@ #endif #define cgrect(nsrect) (*(CGRect *)&(nsrect)) -#define COCOA_MOUSE_EVENT \ - if (isTabletEnabled) { \ - kbd_mouse_event((int)(p.x * 0x7FFF / (screen.width - 1)), (int)((screen.height - p.y) * 0x7FFF / (screen.height - 1)), 0, buttons); \ - } else if (isMouseGrabbed) { \ - kbd_mouse_event((int)[event deltaX], (int)[event deltaY], 0, buttons); \ - } else { \ - [NSApp sendEvent:event]; \ - } typedef struct { int width; @@ -67,6 +60,7 @@ typedef struct { NSWindow *normalWindow; static DisplayChangeListener *dcl; +static int last_buttons; int gArgc; char **gArgv; @@ -501,6 +495,7 @@ QemuCocoaView *cocoaView; int buttons = 0; int keycode; + bool mouse_event = false; NSPoint p = [event locationInWindow]; switch ([event type]) { @@ -514,16 +509,14 @@ QemuCocoaView *cocoaView; if (keycode) { if (keycode == 58 || keycode == 69) { // emulate caps lock and num lock keydown and keyup - kbd_put_keycode(keycode); - kbd_put_keycode(keycode | 0x80); + qemu_input_event_send_key_number(dcl->con, keycode, true); + qemu_input_event_send_key_number(dcl->con, keycode, false); } else if (qemu_console_is_graphic(NULL)) { - if (keycode & 0x80) - kbd_put_keycode(0xe0); if (modifiers_state[keycode] == 0) { // keydown - kbd_put_keycode(keycode & 0x7f); + qemu_input_event_send_key_number(dcl->con, keycode, true); modifiers_state[keycode] = 1; } else { // keyup - kbd_put_keycode(keycode | 0x80); + qemu_input_event_send_key_number(dcl->con, keycode, false); modifiers_state[keycode] = 0; } } @@ -557,9 +550,7 @@ QemuCocoaView *cocoaView; // handle keys for graphic console } else if (qemu_console_is_graphic(NULL)) { - if (keycode & 0x80) //check bit for e0 in front - kbd_put_keycode(0xe0); - kbd_put_keycode(keycode & 0x7f); //remove e0 bit in front + qemu_input_event_send_key_number(dcl->con, keycode, true); // handlekeys for Monitor } else { @@ -607,9 +598,7 @@ QemuCocoaView *cocoaView; } if (qemu_console_is_graphic(NULL)) { - if (keycode & 0x80) - kbd_put_keycode(0xe0); - kbd_put_keycode(keycode | 0x80); //add 128 to signal release of key + qemu_input_event_send_key_number(dcl->con, keycode, false); } break; case NSMouseMoved: @@ -626,7 +615,7 @@ QemuCocoaView *cocoaView; } } } - COCOA_MOUSE_EVENT + mouse_event = true; break; case NSLeftMouseDown: if ([event modifierFlags] & NSCommandKeyMask) { @@ -634,15 +623,15 @@ QemuCocoaView *cocoaView; } else { buttons |= MOUSE_EVENT_LBUTTON; } - COCOA_MOUSE_EVENT + mouse_event = true; break; case NSRightMouseDown: buttons |= MOUSE_EVENT_RBUTTON; - COCOA_MOUSE_EVENT + mouse_event = true; break; case NSOtherMouseDown: buttons |= MOUSE_EVENT_MBUTTON; - COCOA_MOUSE_EVENT + mouse_event = true; break; case NSLeftMouseDragged: if ([event modifierFlags] & NSCommandKeyMask) { @@ -650,19 +639,19 @@ QemuCocoaView *cocoaView; } else { buttons |= MOUSE_EVENT_LBUTTON; } - COCOA_MOUSE_EVENT + mouse_event = true; break; case NSRightMouseDragged: buttons |= MOUSE_EVENT_RBUTTON; - COCOA_MOUSE_EVENT + mouse_event = true; break; case NSOtherMouseDragged: buttons |= MOUSE_EVENT_MBUTTON; - COCOA_MOUSE_EVENT + mouse_event = true; break; case NSLeftMouseUp: if (isTabletEnabled) { - COCOA_MOUSE_EVENT + mouse_event = true; } else if (!isMouseGrabbed) { if (p.x > -1 && p.x < screen.width && p.y > -1 && p.y < screen.height) { [self grabMouse]; @@ -670,18 +659,20 @@ QemuCocoaView *cocoaView; [NSApp sendEvent:event]; } } else { - COCOA_MOUSE_EVENT + mouse_event = true; } break; case NSRightMouseUp: - COCOA_MOUSE_EVENT + mouse_event = true; break; case NSOtherMouseUp: - COCOA_MOUSE_EVENT + mouse_event = true; break; case NSScrollWheel: if (isTabletEnabled || isMouseGrabbed) { - kbd_mouse_event(0, 0, -[event deltaY], 0); + buttons |= ([event deltaY] < 0) ? + MOUSE_EVENT_WHEELUP : MOUSE_EVENT_WHEELDN; + mouse_event = true; } else { [NSApp sendEvent:event]; } @@ -689,6 +680,30 @@ QemuCocoaView *cocoaView; default: [NSApp sendEvent:event]; } + + if (mouse_event) { + if (last_buttons != buttons) { + static uint32_t bmap[INPUT_BUTTON_MAX] = { + [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON, + [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON, + [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON, + [INPUT_BUTTON_WHEEL_UP] = MOUSE_EVENT_WHEELUP, + [INPUT_BUTTON_WHEEL_DOWN] = MOUSE_EVENT_WHEELDN, + }; + qemu_input_update_buttons(dcl->con, bmap, last_buttons, buttons); + last_buttons = buttons; + } + if (isTabletEnabled) { + qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, p.x, screen.width); + qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, p.y, screen.height); + } else if (isMouseGrabbed) { + qemu_input_queue_rel(dcl->con, INPUT_AXIS_X, (int)[event deltaX]); + qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, (int)[event deltaY]); + } else { + [NSApp sendEvent:event]; + } + qemu_input_event_sync(); + } } - (void) grabMouse @@ -1023,7 +1038,7 @@ static void cocoa_refresh(DisplayChangeListener *dcl) COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n"); - if (kbd_mouse_is_absolute()) { + if (qemu_input_is_absolute()) { if (![cocoaView isAbsoluteEnabled]) { if ([cocoaView isMouseGrabbed]) { [cocoaView ungrabMouse]; diff --git a/ui/console.c b/ui/console.c index 502e1600ab..4df251d579 100644 --- a/ui/console.c +++ b/ui/console.c @@ -124,6 +124,8 @@ struct QemuConsole { /* Graphic console state. */ Object *device; + uint32_t head; + QemuUIInfo ui_info; const GraphicHwOps *hw_ops; void *hw; @@ -1179,6 +1181,8 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type) s = QEMU_CONSOLE(obj); object_property_add_link(obj, "device", TYPE_DEVICE, (Object **)&s->device, &local_err); + object_property_add_uint32_ptr(obj, "head", + &s->head, &local_err); if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) && (console_type == GRAPHIC_CONSOLE))) { @@ -1344,6 +1348,16 @@ void unregister_displaychangelistener(DisplayChangeListener *dcl) gui_setup_refresh(ds); } +int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info) +{ + assert(con != NULL); + con->ui_info = *info; + if (con->hw_ops->ui_info) { + return con->hw_ops->ui_info(con->hw, con->head, info); + } + return -1; +} + void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h) { DisplayState *s = con->ds; @@ -1569,7 +1583,7 @@ DisplayState *init_displaystate(void) return display_state; } -QemuConsole *graphic_console_init(DeviceState *dev, +QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, const GraphicHwOps *hw_ops, void *opaque) { @@ -1587,6 +1601,8 @@ QemuConsole *graphic_console_init(DeviceState *dev, if (dev) { object_property_set_link(OBJECT(s), OBJECT(dev), "device", &local_err); + object_property_set_int(OBJECT(s), head, + "head", &local_err); } s->surface = qemu_create_displaysurface(width, height); @@ -1601,10 +1617,11 @@ QemuConsole *qemu_console_lookup_by_index(unsigned int index) return consoles[index]; } -QemuConsole *qemu_console_lookup_by_device(DeviceState *dev) +QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head) { Error *local_err = NULL; Object *obj; + uint32_t h; int i; for (i = 0; i < nb_consoles; i++) { @@ -1613,9 +1630,15 @@ QemuConsole *qemu_console_lookup_by_device(DeviceState *dev) } obj = object_property_get_link(OBJECT(consoles[i]), "device", &local_err); - if (DEVICE(obj) == dev) { - return consoles[i]; + if (DEVICE(obj) != dev) { + continue; } + h = object_property_get_int(OBJECT(consoles[i]), + "head", &local_err); + if (h != head) { + continue; + } + return consoles[i]; } return NULL; } @@ -1641,6 +1664,44 @@ bool qemu_console_is_fixedsize(QemuConsole *con) return con && (con->console_type != TEXT_CONSOLE); } +int qemu_console_get_index(QemuConsole *con) +{ + if (con == NULL) { + con = active_console; + } + return con ? con->index : -1; +} + +uint32_t qemu_console_get_head(QemuConsole *con) +{ + if (con == NULL) { + con = active_console; + } + return con ? con->head : -1; +} + +QemuUIInfo *qemu_console_get_ui_info(QemuConsole *con) +{ + assert(con != NULL); + return &con->ui_info; +} + +int qemu_console_get_width(QemuConsole *con, int fallback) +{ + if (con == NULL) { + con = active_console; + } + return con ? surface_width(con->surface) : fallback; +} + +int qemu_console_get_height(QemuConsole *con, int fallback) +{ + if (con == NULL) { + con = active_console; + } + return con ? surface_height(con->surface) : fallback; +} + static void text_console_set_echo(CharDriverState *chr, bool echo) { QemuConsole *s = chr->opaque; diff --git a/ui/curses.c b/ui/curses.c index dbc3d5ec73..b044790e43 100644 --- a/ui/curses.c +++ b/ui/curses.c @@ -30,6 +30,7 @@ #include "qemu-common.h" #include "ui/console.h" +#include "ui/input.h" #include "sysemu/sysemu.h" #define FONT_HEIGHT 16 @@ -274,32 +275,34 @@ static void curses_refresh(DisplayChangeListener *dcl) if (qemu_console_is_graphic(NULL)) { /* since terminals don't know about key press and release * events, we need to emit both for each key received */ - if (keycode & SHIFT) - kbd_put_keycode(SHIFT_CODE); - if (keycode & CNTRL) - kbd_put_keycode(CNTRL_CODE); - if (keycode & ALT) - kbd_put_keycode(ALT_CODE); + if (keycode & SHIFT) { + qemu_input_event_send_key_number(NULL, SHIFT_CODE, true); + } + if (keycode & CNTRL) { + qemu_input_event_send_key_number(NULL, CNTRL_CODE, true); + } + if (keycode & ALT) { + qemu_input_event_send_key_number(NULL, ALT_CODE, true); + } if (keycode & ALTGR) { - kbd_put_keycode(SCANCODE_EMUL0); - kbd_put_keycode(ALT_CODE); + qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true); } - if (keycode & GREY) - kbd_put_keycode(GREY_CODE); - kbd_put_keycode(keycode & KEY_MASK); - if (keycode & GREY) - kbd_put_keycode(GREY_CODE); - kbd_put_keycode((keycode & KEY_MASK) | KEY_RELEASE); + + qemu_input_event_send_key_number(NULL, keycode, true); + qemu_input_event_send_key_number(NULL, keycode, false); + if (keycode & ALTGR) { - kbd_put_keycode(SCANCODE_EMUL0); - kbd_put_keycode(ALT_CODE | KEY_RELEASE); + qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false); + } + if (keycode & ALT) { + qemu_input_event_send_key_number(NULL, ALT_CODE, false); + } + if (keycode & CNTRL) { + qemu_input_event_send_key_number(NULL, CNTRL_CODE, false); + } + if (keycode & SHIFT) { + qemu_input_event_send_key_number(NULL, SHIFT_CODE, false); } - if (keycode & ALT) - kbd_put_keycode(ALT_CODE | KEY_RELEASE); - if (keycode & CNTRL) - kbd_put_keycode(CNTRL_CODE | KEY_RELEASE); - if (keycode & SHIFT) - kbd_put_keycode(SHIFT_CODE | KEY_RELEASE); } else { keysym = curses2qemu[chr]; if (keysym == -1) diff --git a/ui/gtk.c b/ui/gtk.c index a633d89346..185149571e 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -59,6 +59,7 @@ #include "trace.h" #include "ui/console.h" +#include "ui/input.h" #include "sysemu/sysemu.h" #include "qmp-commands.h" #include "x_keymap.h" @@ -193,7 +194,7 @@ static void gd_update_cursor(GtkDisplayState *s, gboolean override) on_vga = gd_on_vga(s); if ((override || on_vga) && - (s->full_screen || kbd_mouse_is_absolute() || gd_is_grab_active(s))) { + (s->full_screen || qemu_input_is_absolute() || gd_is_grab_active(s))) { gdk_window_set_cursor(window, s->null_cursor); } else { gdk_window_set_cursor(window, NULL); @@ -280,10 +281,7 @@ static void gtk_release_modifiers(GtkDisplayState *s) if (!s->modifier_pressed[i]) { continue; } - if (keycode & SCANCODE_GREY) { - kbd_put_keycode(SCANCODE_EMUL0); - } - kbd_put_keycode(keycode | SCANCODE_UP); + qemu_input_event_send_key_number(s->dcl.con, keycode, false); s->modifier_pressed[i] = false; } } @@ -582,7 +580,6 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, void *opaque) { GtkDisplayState *s = opaque; - int dx, dy; int x, y; int mx, my; int fbh, fbw; @@ -610,25 +607,21 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, return TRUE; } - if (kbd_mouse_is_absolute()) { - dx = x * 0x7FFF / (surface_width(s->ds) - 1); - dy = y * 0x7FFF / (surface_height(s->ds) - 1); - } else if (s->last_x == -1 || s->last_y == -1) { - dx = 0; - dy = 0; - } else { - dx = x - s->last_x; - dy = y - s->last_y; + if (qemu_input_is_absolute()) { + qemu_input_queue_abs(s->dcl.con, INPUT_AXIS_X, x, + surface_width(s->ds)); + qemu_input_queue_abs(s->dcl.con, INPUT_AXIS_Y, y, + surface_height(s->ds)); + qemu_input_event_sync(); + } else if (s->last_x != -1 && s->last_y != -1 && gd_is_grab_active(s)) { + qemu_input_queue_rel(s->dcl.con, INPUT_AXIS_X, x - s->last_x); + qemu_input_queue_rel(s->dcl.con, INPUT_AXIS_Y, y - s->last_y); + qemu_input_event_sync(); } - s->last_x = x; s->last_y = y; - if (kbd_mouse_is_absolute() || gd_is_grab_active(s)) { - kbd_mouse_event(dx, dy, 0, s->button_mask); - } - - if (!kbd_mouse_is_absolute() && gd_is_grab_active(s)) { + if (!qemu_input_is_absolute() && gd_is_grab_active(s)) { GdkScreen *screen = gtk_widget_get_screen(s->drawing_area); int x = (int)motion->x_root; int y = (int)motion->y_root; @@ -673,35 +666,20 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, void *opaque) { GtkDisplayState *s = opaque; - int dx, dy; - int n; + InputButton btn; if (button->button == 1) { - n = 0x01; + btn = INPUT_BUTTON_LEFT; } else if (button->button == 2) { - n = 0x04; + btn = INPUT_BUTTON_MIDDLE; } else if (button->button == 3) { - n = 0x02; + btn = INPUT_BUTTON_RIGHT; } else { - n = 0x00; - } - - if (button->type == GDK_BUTTON_PRESS) { - s->button_mask |= n; - } else if (button->type == GDK_BUTTON_RELEASE) { - s->button_mask &= ~n; - } - - if (kbd_mouse_is_absolute()) { - dx = s->last_x * 0x7FFF / (surface_width(s->ds) - 1); - dy = s->last_y * 0x7FFF / (surface_height(s->ds) - 1); - } else { - dx = 0; - dy = 0; + return TRUE; } - kbd_mouse_event(dx, dy, 0, s->button_mask); - + qemu_input_queue_btn(s->dcl.con, btn, button->type == GDK_BUTTON_PRESS); + qemu_input_event_sync(); return TRUE; } @@ -745,17 +723,8 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) } } - if (qemu_keycode & SCANCODE_GREY) { - kbd_put_keycode(SCANCODE_EMUL0); - } - - if (key->type == GDK_KEY_PRESS) { - kbd_put_keycode(qemu_keycode & SCANCODE_KEYCODEMASK); - } else if (key->type == GDK_KEY_RELEASE) { - kbd_put_keycode(qemu_keycode | SCANCODE_UP); - } else { - g_assert_not_reached(); - } + qemu_input_event_send_key_number(s->dcl.con, qemu_keycode, + key->type == GDK_KEY_PRESS); return TRUE; } diff --git a/ui/input-legacy.c b/ui/input-legacy.c new file mode 100644 index 0000000000..f38984b192 --- /dev/null +++ b/ui/input-legacy.c @@ -0,0 +1,453 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysemu/sysemu.h" +#include "monitor/monitor.h" +#include "ui/console.h" +#include "qapi/error.h" +#include "qmp-commands.h" +#include "qapi-types.h" +#include "ui/keymaps.h" +#include "ui/input.h" + +struct QEMUPutMouseEntry { + QEMUPutMouseEvent *qemu_put_mouse_event; + void *qemu_put_mouse_event_opaque; + int qemu_put_mouse_event_absolute; + + /* new input core */ + QemuInputHandler h; + QemuInputHandlerState *s; + int axis[INPUT_AXIS_MAX]; + int buttons; +}; + +struct QEMUPutKbdEntry { + QEMUPutKBDEvent *put_kbd; + void *opaque; + QemuInputHandlerState *s; +}; + +struct QEMUPutLEDEntry { + QEMUPutLEDEvent *put_led; + void *opaque; + QTAILQ_ENTRY(QEMUPutLEDEntry) next; +}; + +static QTAILQ_HEAD(, QEMUPutLEDEntry) led_handlers = + QTAILQ_HEAD_INITIALIZER(led_handlers); +static QTAILQ_HEAD(, QEMUPutMouseEntry) mouse_handlers = + QTAILQ_HEAD_INITIALIZER(mouse_handlers); + +static const int key_defs[] = { + [Q_KEY_CODE_SHIFT] = 0x2a, + [Q_KEY_CODE_SHIFT_R] = 0x36, + + [Q_KEY_CODE_ALT] = 0x38, + [Q_KEY_CODE_ALT_R] = 0xb8, + [Q_KEY_CODE_ALTGR] = 0x64, + [Q_KEY_CODE_ALTGR_R] = 0xe4, + [Q_KEY_CODE_CTRL] = 0x1d, + [Q_KEY_CODE_CTRL_R] = 0x9d, + + [Q_KEY_CODE_MENU] = 0xdd, + + [Q_KEY_CODE_ESC] = 0x01, + + [Q_KEY_CODE_1] = 0x02, + [Q_KEY_CODE_2] = 0x03, + [Q_KEY_CODE_3] = 0x04, + [Q_KEY_CODE_4] = 0x05, + [Q_KEY_CODE_5] = 0x06, + [Q_KEY_CODE_6] = 0x07, + [Q_KEY_CODE_7] = 0x08, + [Q_KEY_CODE_8] = 0x09, + [Q_KEY_CODE_9] = 0x0a, + [Q_KEY_CODE_0] = 0x0b, + [Q_KEY_CODE_MINUS] = 0x0c, + [Q_KEY_CODE_EQUAL] = 0x0d, + [Q_KEY_CODE_BACKSPACE] = 0x0e, + + [Q_KEY_CODE_TAB] = 0x0f, + [Q_KEY_CODE_Q] = 0x10, + [Q_KEY_CODE_W] = 0x11, + [Q_KEY_CODE_E] = 0x12, + [Q_KEY_CODE_R] = 0x13, + [Q_KEY_CODE_T] = 0x14, + [Q_KEY_CODE_Y] = 0x15, + [Q_KEY_CODE_U] = 0x16, + [Q_KEY_CODE_I] = 0x17, + [Q_KEY_CODE_O] = 0x18, + [Q_KEY_CODE_P] = 0x19, + [Q_KEY_CODE_BRACKET_LEFT] = 0x1a, + [Q_KEY_CODE_BRACKET_RIGHT] = 0x1b, + [Q_KEY_CODE_RET] = 0x1c, + + [Q_KEY_CODE_A] = 0x1e, + [Q_KEY_CODE_S] = 0x1f, + [Q_KEY_CODE_D] = 0x20, + [Q_KEY_CODE_F] = 0x21, + [Q_KEY_CODE_G] = 0x22, + [Q_KEY_CODE_H] = 0x23, + [Q_KEY_CODE_J] = 0x24, + [Q_KEY_CODE_K] = 0x25, + [Q_KEY_CODE_L] = 0x26, + [Q_KEY_CODE_SEMICOLON] = 0x27, + [Q_KEY_CODE_APOSTROPHE] = 0x28, + [Q_KEY_CODE_GRAVE_ACCENT] = 0x29, + + [Q_KEY_CODE_BACKSLASH] = 0x2b, + [Q_KEY_CODE_Z] = 0x2c, + [Q_KEY_CODE_X] = 0x2d, + [Q_KEY_CODE_C] = 0x2e, + [Q_KEY_CODE_V] = 0x2f, + [Q_KEY_CODE_B] = 0x30, + [Q_KEY_CODE_N] = 0x31, + [Q_KEY_CODE_M] = 0x32, + [Q_KEY_CODE_COMMA] = 0x33, + [Q_KEY_CODE_DOT] = 0x34, + [Q_KEY_CODE_SLASH] = 0x35, + + [Q_KEY_CODE_ASTERISK] = 0x37, + + [Q_KEY_CODE_SPC] = 0x39, + [Q_KEY_CODE_CAPS_LOCK] = 0x3a, + [Q_KEY_CODE_F1] = 0x3b, + [Q_KEY_CODE_F2] = 0x3c, + [Q_KEY_CODE_F3] = 0x3d, + [Q_KEY_CODE_F4] = 0x3e, + [Q_KEY_CODE_F5] = 0x3f, + [Q_KEY_CODE_F6] = 0x40, + [Q_KEY_CODE_F7] = 0x41, + [Q_KEY_CODE_F8] = 0x42, + [Q_KEY_CODE_F9] = 0x43, + [Q_KEY_CODE_F10] = 0x44, + [Q_KEY_CODE_NUM_LOCK] = 0x45, + [Q_KEY_CODE_SCROLL_LOCK] = 0x46, + + [Q_KEY_CODE_KP_DIVIDE] = 0xb5, + [Q_KEY_CODE_KP_MULTIPLY] = 0x37, + [Q_KEY_CODE_KP_SUBTRACT] = 0x4a, + [Q_KEY_CODE_KP_ADD] = 0x4e, + [Q_KEY_CODE_KP_ENTER] = 0x9c, + [Q_KEY_CODE_KP_DECIMAL] = 0x53, + [Q_KEY_CODE_SYSRQ] = 0x54, + + [Q_KEY_CODE_KP_0] = 0x52, + [Q_KEY_CODE_KP_1] = 0x4f, + [Q_KEY_CODE_KP_2] = 0x50, + [Q_KEY_CODE_KP_3] = 0x51, + [Q_KEY_CODE_KP_4] = 0x4b, + [Q_KEY_CODE_KP_5] = 0x4c, + [Q_KEY_CODE_KP_6] = 0x4d, + [Q_KEY_CODE_KP_7] = 0x47, + [Q_KEY_CODE_KP_8] = 0x48, + [Q_KEY_CODE_KP_9] = 0x49, + + [Q_KEY_CODE_LESS] = 0x56, + + [Q_KEY_CODE_F11] = 0x57, + [Q_KEY_CODE_F12] = 0x58, + + [Q_KEY_CODE_PRINT] = 0xb7, + + [Q_KEY_CODE_HOME] = 0xc7, + [Q_KEY_CODE_PGUP] = 0xc9, + [Q_KEY_CODE_PGDN] = 0xd1, + [Q_KEY_CODE_END] = 0xcf, + + [Q_KEY_CODE_LEFT] = 0xcb, + [Q_KEY_CODE_UP] = 0xc8, + [Q_KEY_CODE_DOWN] = 0xd0, + [Q_KEY_CODE_RIGHT] = 0xcd, + + [Q_KEY_CODE_INSERT] = 0xd2, + [Q_KEY_CODE_DELETE] = 0xd3, +#ifdef NEED_CPU_H +#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64) + [Q_KEY_CODE_STOP] = 0xf0, + [Q_KEY_CODE_AGAIN] = 0xf1, + [Q_KEY_CODE_PROPS] = 0xf2, + [Q_KEY_CODE_UNDO] = 0xf3, + [Q_KEY_CODE_FRONT] = 0xf4, + [Q_KEY_CODE_COPY] = 0xf5, + [Q_KEY_CODE_OPEN] = 0xf6, + [Q_KEY_CODE_PASTE] = 0xf7, + [Q_KEY_CODE_FIND] = 0xf8, + [Q_KEY_CODE_CUT] = 0xf9, + [Q_KEY_CODE_LF] = 0xfa, + [Q_KEY_CODE_HELP] = 0xfb, + [Q_KEY_CODE_META_L] = 0xfc, + [Q_KEY_CODE_META_R] = 0xfd, + [Q_KEY_CODE_COMPOSE] = 0xfe, +#endif +#endif + [Q_KEY_CODE_MAX] = 0, +}; + +int index_from_key(const char *key) +{ + int i; + + for (i = 0; QKeyCode_lookup[i] != NULL; i++) { + if (!strcmp(key, QKeyCode_lookup[i])) { + break; + } + } + + /* Return Q_KEY_CODE_MAX if the key is invalid */ + return i; +} + +static int *keycodes; +static int keycodes_size; +static QEMUTimer *key_timer; + +static int keycode_from_keyvalue(const KeyValue *value) +{ + if (value->kind == KEY_VALUE_KIND_QCODE) { + return key_defs[value->qcode]; + } else { + assert(value->kind == KEY_VALUE_KIND_NUMBER); + return value->number; + } +} + +static void free_keycodes(void) +{ + g_free(keycodes); + keycodes = NULL; + keycodes_size = 0; +} + +static void release_keys(void *opaque) +{ + while (keycodes_size > 0) { + qemu_input_event_send_key_number(NULL, keycodes[--keycodes_size], + false); + } + + free_keycodes(); +} + +void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time, + Error **errp) +{ + int keycode; + KeyValueList *p; + + if (!key_timer) { + key_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, release_keys, NULL); + } + + if (keycodes != NULL) { + timer_del(key_timer); + release_keys(NULL); + } + + if (!has_hold_time) { + hold_time = 100; + } + + for (p = keys; p != NULL; p = p->next) { + /* key down events */ + keycode = keycode_from_keyvalue(p->value); + if (keycode < 0x01 || keycode > 0xff) { + error_setg(errp, "invalid hex keycode 0x%x", keycode); + free_keycodes(); + return; + } + + qemu_input_event_send_key_number(NULL, keycode, true); + + keycodes = g_realloc(keycodes, sizeof(int) * (keycodes_size + 1)); + keycodes[keycodes_size++] = keycode; + } + + /* delayed key up events */ + timer_mod(key_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + muldiv64(get_ticks_per_sec(), hold_time, 1000)); +} + +static void legacy_kbd_event(DeviceState *dev, QemuConsole *src, + InputEvent *evt) +{ + QEMUPutKbdEntry *entry = (QEMUPutKbdEntry *)dev; + int keycode = keycode_from_keyvalue(evt->key->key); + + if (!entry || !entry->put_kbd) { + return; + } + if (evt->key->key->kind == KEY_VALUE_KIND_QCODE && + evt->key->key->qcode == Q_KEY_CODE_PAUSE) { + /* specific case */ + int v = evt->key->down ? 0 : 0x80; + entry->put_kbd(entry->opaque, 0xe1); + entry->put_kbd(entry->opaque, 0x1d | v); + entry->put_kbd(entry->opaque, 0x45 | v); + return; + } + if (keycode & SCANCODE_GREY) { + entry->put_kbd(entry->opaque, SCANCODE_EMUL0); + keycode &= ~SCANCODE_GREY; + } + if (!evt->key->down) { + keycode |= SCANCODE_UP; + } + entry->put_kbd(entry->opaque, keycode); +} + +static QemuInputHandler legacy_kbd_handler = { + .name = "legacy-kbd", + .mask = INPUT_EVENT_MASK_KEY, + .event = legacy_kbd_event, +}; + +QEMUPutKbdEntry *qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) +{ + QEMUPutKbdEntry *entry; + + entry = g_new0(QEMUPutKbdEntry, 1); + entry->put_kbd = func; + entry->opaque = opaque; + entry->s = qemu_input_handler_register((DeviceState *)entry, + &legacy_kbd_handler); + return entry; +} + +void qemu_remove_kbd_event_handler(QEMUPutKbdEntry *entry) +{ + qemu_input_handler_unregister(entry->s); + g_free(entry); +} + +static void legacy_mouse_event(DeviceState *dev, QemuConsole *src, + InputEvent *evt) +{ + static const int bmap[INPUT_BUTTON_MAX] = { + [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON, + [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON, + [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON, + }; + QEMUPutMouseEntry *s = (QEMUPutMouseEntry *)dev; + + switch (evt->kind) { + case INPUT_EVENT_KIND_BTN: + if (evt->btn->down) { + s->buttons |= bmap[evt->btn->button]; + } else { + s->buttons &= ~bmap[evt->btn->button]; + } + break; + case INPUT_EVENT_KIND_ABS: + s->axis[evt->abs->axis] = evt->abs->value; + break; + case INPUT_EVENT_KIND_REL: + s->axis[evt->rel->axis] += evt->rel->value; + break; + default: + break; + } +} + +static void legacy_mouse_sync(DeviceState *dev) +{ + QEMUPutMouseEntry *s = (QEMUPutMouseEntry *)dev; + + s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque, + s->axis[INPUT_AXIS_X], + s->axis[INPUT_AXIS_Y], + 0, + s->buttons); + + if (!s->qemu_put_mouse_event_absolute) { + s->axis[INPUT_AXIS_X] = 0; + s->axis[INPUT_AXIS_Y] = 0; + } +} + +QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, + void *opaque, int absolute, + const char *name) +{ + QEMUPutMouseEntry *s; + + s = g_malloc0(sizeof(QEMUPutMouseEntry)); + + s->qemu_put_mouse_event = func; + s->qemu_put_mouse_event_opaque = opaque; + s->qemu_put_mouse_event_absolute = absolute; + + s->h.name = name; + s->h.mask = INPUT_EVENT_MASK_BTN | + (absolute ? INPUT_EVENT_MASK_ABS : INPUT_EVENT_MASK_REL); + s->h.event = legacy_mouse_event; + s->h.sync = legacy_mouse_sync; + s->s = qemu_input_handler_register((DeviceState *)s, + &s->h); + + return s; +} + +void qemu_activate_mouse_event_handler(QEMUPutMouseEntry *entry) +{ + qemu_input_handler_activate(entry->s); +} + +void qemu_remove_mouse_event_handler(QEMUPutMouseEntry *entry) +{ + qemu_input_handler_unregister(entry->s); + + g_free(entry); +} + +QEMUPutLEDEntry *qemu_add_led_event_handler(QEMUPutLEDEvent *func, + void *opaque) +{ + QEMUPutLEDEntry *s; + + s = g_malloc0(sizeof(QEMUPutLEDEntry)); + + s->put_led = func; + s->opaque = opaque; + QTAILQ_INSERT_TAIL(&led_handlers, s, next); + return s; +} + +void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry) +{ + if (entry == NULL) + return; + QTAILQ_REMOVE(&led_handlers, entry, next); + g_free(entry); +} + +void kbd_put_ledstate(int ledstate) +{ + QEMUPutLEDEntry *cursor; + + QTAILQ_FOREACH(cursor, &led_handlers, next) { + cursor->put_led(cursor->opaque, ledstate); + } +} diff --git a/ui/input.c b/ui/input.c index 1c70f60e0d..2761911f3c 100644 --- a/ui/input.c +++ b/ui/input.c @@ -1,520 +1,333 @@ -/* - * QEMU System Emulator - * - * Copyright (c) 2003-2008 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - #include "sysemu/sysemu.h" -#include "monitor/monitor.h" -#include "ui/console.h" -#include "qapi/error.h" -#include "qmp-commands.h" #include "qapi-types.h" -#include "ui/keymaps.h" - -struct QEMUPutMouseEntry { - QEMUPutMouseEvent *qemu_put_mouse_event; - void *qemu_put_mouse_event_opaque; - int qemu_put_mouse_event_absolute; - char *qemu_put_mouse_event_name; - - int index; - - /* used internally by qemu for handling mice */ - QTAILQ_ENTRY(QEMUPutMouseEntry) node; -}; - -struct QEMUPutKbdEntry { - QEMUPutKBDEvent *put_kbd; - void *opaque; - QTAILQ_ENTRY(QEMUPutKbdEntry) next; -}; +#include "qmp-commands.h" +#include "trace.h" +#include "ui/input.h" +#include "ui/console.h" -struct QEMUPutLEDEntry { - QEMUPutLEDEvent *put_led; - void *opaque; - QTAILQ_ENTRY(QEMUPutLEDEntry) next; +struct QemuInputHandlerState { + DeviceState *dev; + QemuInputHandler *handler; + int id; + int events; + QTAILQ_ENTRY(QemuInputHandlerState) node; }; - -static QTAILQ_HEAD(, QEMUPutLEDEntry) led_handlers = - QTAILQ_HEAD_INITIALIZER(led_handlers); -static QTAILQ_HEAD(, QEMUPutKbdEntry) kbd_handlers = - QTAILQ_HEAD_INITIALIZER(kbd_handlers); -static QTAILQ_HEAD(, QEMUPutMouseEntry) mouse_handlers = - QTAILQ_HEAD_INITIALIZER(mouse_handlers); +static QTAILQ_HEAD(, QemuInputHandlerState) handlers = + QTAILQ_HEAD_INITIALIZER(handlers); static NotifierList mouse_mode_notifiers = NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers); -static const int key_defs[] = { - [Q_KEY_CODE_SHIFT] = 0x2a, - [Q_KEY_CODE_SHIFT_R] = 0x36, - - [Q_KEY_CODE_ALT] = 0x38, - [Q_KEY_CODE_ALT_R] = 0xb8, - [Q_KEY_CODE_ALTGR] = 0x64, - [Q_KEY_CODE_ALTGR_R] = 0xe4, - [Q_KEY_CODE_CTRL] = 0x1d, - [Q_KEY_CODE_CTRL_R] = 0x9d, - - [Q_KEY_CODE_MENU] = 0xdd, - - [Q_KEY_CODE_ESC] = 0x01, - - [Q_KEY_CODE_1] = 0x02, - [Q_KEY_CODE_2] = 0x03, - [Q_KEY_CODE_3] = 0x04, - [Q_KEY_CODE_4] = 0x05, - [Q_KEY_CODE_5] = 0x06, - [Q_KEY_CODE_6] = 0x07, - [Q_KEY_CODE_7] = 0x08, - [Q_KEY_CODE_8] = 0x09, - [Q_KEY_CODE_9] = 0x0a, - [Q_KEY_CODE_0] = 0x0b, - [Q_KEY_CODE_MINUS] = 0x0c, - [Q_KEY_CODE_EQUAL] = 0x0d, - [Q_KEY_CODE_BACKSPACE] = 0x0e, - - [Q_KEY_CODE_TAB] = 0x0f, - [Q_KEY_CODE_Q] = 0x10, - [Q_KEY_CODE_W] = 0x11, - [Q_KEY_CODE_E] = 0x12, - [Q_KEY_CODE_R] = 0x13, - [Q_KEY_CODE_T] = 0x14, - [Q_KEY_CODE_Y] = 0x15, - [Q_KEY_CODE_U] = 0x16, - [Q_KEY_CODE_I] = 0x17, - [Q_KEY_CODE_O] = 0x18, - [Q_KEY_CODE_P] = 0x19, - [Q_KEY_CODE_BRACKET_LEFT] = 0x1a, - [Q_KEY_CODE_BRACKET_RIGHT] = 0x1b, - [Q_KEY_CODE_RET] = 0x1c, - - [Q_KEY_CODE_A] = 0x1e, - [Q_KEY_CODE_S] = 0x1f, - [Q_KEY_CODE_D] = 0x20, - [Q_KEY_CODE_F] = 0x21, - [Q_KEY_CODE_G] = 0x22, - [Q_KEY_CODE_H] = 0x23, - [Q_KEY_CODE_J] = 0x24, - [Q_KEY_CODE_K] = 0x25, - [Q_KEY_CODE_L] = 0x26, - [Q_KEY_CODE_SEMICOLON] = 0x27, - [Q_KEY_CODE_APOSTROPHE] = 0x28, - [Q_KEY_CODE_GRAVE_ACCENT] = 0x29, - - [Q_KEY_CODE_BACKSLASH] = 0x2b, - [Q_KEY_CODE_Z] = 0x2c, - [Q_KEY_CODE_X] = 0x2d, - [Q_KEY_CODE_C] = 0x2e, - [Q_KEY_CODE_V] = 0x2f, - [Q_KEY_CODE_B] = 0x30, - [Q_KEY_CODE_N] = 0x31, - [Q_KEY_CODE_M] = 0x32, - [Q_KEY_CODE_COMMA] = 0x33, - [Q_KEY_CODE_DOT] = 0x34, - [Q_KEY_CODE_SLASH] = 0x35, - - [Q_KEY_CODE_ASTERISK] = 0x37, - - [Q_KEY_CODE_SPC] = 0x39, - [Q_KEY_CODE_CAPS_LOCK] = 0x3a, - [Q_KEY_CODE_F1] = 0x3b, - [Q_KEY_CODE_F2] = 0x3c, - [Q_KEY_CODE_F3] = 0x3d, - [Q_KEY_CODE_F4] = 0x3e, - [Q_KEY_CODE_F5] = 0x3f, - [Q_KEY_CODE_F6] = 0x40, - [Q_KEY_CODE_F7] = 0x41, - [Q_KEY_CODE_F8] = 0x42, - [Q_KEY_CODE_F9] = 0x43, - [Q_KEY_CODE_F10] = 0x44, - [Q_KEY_CODE_NUM_LOCK] = 0x45, - [Q_KEY_CODE_SCROLL_LOCK] = 0x46, - - [Q_KEY_CODE_KP_DIVIDE] = 0xb5, - [Q_KEY_CODE_KP_MULTIPLY] = 0x37, - [Q_KEY_CODE_KP_SUBTRACT] = 0x4a, - [Q_KEY_CODE_KP_ADD] = 0x4e, - [Q_KEY_CODE_KP_ENTER] = 0x9c, - [Q_KEY_CODE_KP_DECIMAL] = 0x53, - [Q_KEY_CODE_SYSRQ] = 0x54, - - [Q_KEY_CODE_KP_0] = 0x52, - [Q_KEY_CODE_KP_1] = 0x4f, - [Q_KEY_CODE_KP_2] = 0x50, - [Q_KEY_CODE_KP_3] = 0x51, - [Q_KEY_CODE_KP_4] = 0x4b, - [Q_KEY_CODE_KP_5] = 0x4c, - [Q_KEY_CODE_KP_6] = 0x4d, - [Q_KEY_CODE_KP_7] = 0x47, - [Q_KEY_CODE_KP_8] = 0x48, - [Q_KEY_CODE_KP_9] = 0x49, - - [Q_KEY_CODE_LESS] = 0x56, - - [Q_KEY_CODE_F11] = 0x57, - [Q_KEY_CODE_F12] = 0x58, - - [Q_KEY_CODE_PRINT] = 0xb7, - - [Q_KEY_CODE_HOME] = 0xc7, - [Q_KEY_CODE_PGUP] = 0xc9, - [Q_KEY_CODE_PGDN] = 0xd1, - [Q_KEY_CODE_END] = 0xcf, - - [Q_KEY_CODE_LEFT] = 0xcb, - [Q_KEY_CODE_UP] = 0xc8, - [Q_KEY_CODE_DOWN] = 0xd0, - [Q_KEY_CODE_RIGHT] = 0xcd, - - [Q_KEY_CODE_INSERT] = 0xd2, - [Q_KEY_CODE_DELETE] = 0xd3, -#ifdef NEED_CPU_H -#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64) - [Q_KEY_CODE_STOP] = 0xf0, - [Q_KEY_CODE_AGAIN] = 0xf1, - [Q_KEY_CODE_PROPS] = 0xf2, - [Q_KEY_CODE_UNDO] = 0xf3, - [Q_KEY_CODE_FRONT] = 0xf4, - [Q_KEY_CODE_COPY] = 0xf5, - [Q_KEY_CODE_OPEN] = 0xf6, - [Q_KEY_CODE_PASTE] = 0xf7, - [Q_KEY_CODE_FIND] = 0xf8, - [Q_KEY_CODE_CUT] = 0xf9, - [Q_KEY_CODE_LF] = 0xfa, - [Q_KEY_CODE_HELP] = 0xfb, - [Q_KEY_CODE_META_L] = 0xfc, - [Q_KEY_CODE_META_R] = 0xfd, - [Q_KEY_CODE_COMPOSE] = 0xfe, -#endif -#endif - [Q_KEY_CODE_MAX] = 0, -}; - -int index_from_key(const char *key) +QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev, + QemuInputHandler *handler) { - int i; + QemuInputHandlerState *s = g_new0(QemuInputHandlerState, 1); + static int id = 1; - for (i = 0; QKeyCode_lookup[i] != NULL; i++) { - if (!strcmp(key, QKeyCode_lookup[i])) { - break; - } - } + s->dev = dev; + s->handler = handler; + s->id = id++; + QTAILQ_INSERT_TAIL(&handlers, s, node); - /* Return Q_KEY_CODE_MAX if the key is invalid */ - return i; + qemu_input_check_mode_change(); + return s; } -int index_from_keycode(int code) +void qemu_input_handler_activate(QemuInputHandlerState *s) { - int i; - - for (i = 0; i < Q_KEY_CODE_MAX; i++) { - if (key_defs[i] == code) { - break; - } - } - - /* Return Q_KEY_CODE_MAX if the code is invalid */ - return i; + QTAILQ_REMOVE(&handlers, s, node); + QTAILQ_INSERT_HEAD(&handlers, s, node); + qemu_input_check_mode_change(); } -static int *keycodes; -static int keycodes_size; -static QEMUTimer *key_timer; - -static int keycode_from_keyvalue(const KeyValue *value) +void qemu_input_handler_unregister(QemuInputHandlerState *s) { - if (value->kind == KEY_VALUE_KIND_QCODE) { - return key_defs[value->qcode]; - } else { - assert(value->kind == KEY_VALUE_KIND_NUMBER); - return value->number; - } + QTAILQ_REMOVE(&handlers, s, node); + g_free(s); + qemu_input_check_mode_change(); } -static void free_keycodes(void) +static QemuInputHandlerState* +qemu_input_find_handler(uint32_t mask) { - g_free(keycodes); - keycodes = NULL; - keycodes_size = 0; -} + QemuInputHandlerState *s; -static void release_keys(void *opaque) -{ - while (keycodes_size > 0) { - if (keycodes[--keycodes_size] & SCANCODE_GREY) { - kbd_put_keycode(SCANCODE_EMUL0); + QTAILQ_FOREACH(s, &handlers, node) { + if (mask & s->handler->mask) { + return s; } - kbd_put_keycode(keycodes[keycodes_size] | SCANCODE_UP); } - - free_keycodes(); + return NULL; } -void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time, - Error **errp) +static void qemu_input_transform_abs_rotate(InputEvent *evt) { - int keycode; - KeyValueList *p; - - if (!key_timer) { - key_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, release_keys, NULL); - } - - if (keycodes != NULL) { - timer_del(key_timer); - release_keys(NULL); - } - - if (!has_hold_time) { - hold_time = 100; - } - - for (p = keys; p != NULL; p = p->next) { - /* key down events */ - keycode = keycode_from_keyvalue(p->value); - if (keycode < 0x01 || keycode > 0xff) { - error_setg(errp, "invalid hex keycode 0x%x", keycode); - free_keycodes(); - return; + switch (graphic_rotate) { + case 90: + if (evt->abs->axis == INPUT_AXIS_X) { + evt->abs->axis = INPUT_AXIS_Y; + } else if (evt->abs->axis == INPUT_AXIS_Y) { + evt->abs->axis = INPUT_AXIS_X; + evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value; } - - if (keycode & SCANCODE_GREY) { - kbd_put_keycode(SCANCODE_EMUL0); + break; + case 180: + evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value; + break; + case 270: + if (evt->abs->axis == INPUT_AXIS_X) { + evt->abs->axis = INPUT_AXIS_Y; + evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value; + } else if (evt->abs->axis == INPUT_AXIS_Y) { + evt->abs->axis = INPUT_AXIS_X; } - kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK); - - keycodes = g_realloc(keycodes, sizeof(int) * (keycodes_size + 1)); - keycodes[keycodes_size++] = keycode; + break; } - - /* delayed key up events */ - timer_mod(key_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - muldiv64(get_ticks_per_sec(), hold_time, 1000)); } -QEMUPutKbdEntry *qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) +static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt) { - QEMUPutKbdEntry *entry; + const char *name; + int idx = -1; - entry = g_malloc0(sizeof(QEMUPutKbdEntry)); - entry->put_kbd = func; - entry->opaque = opaque; - QTAILQ_INSERT_HEAD(&kbd_handlers, entry, next); - return entry; + if (src) { + idx = qemu_console_get_index(src); + } + switch (evt->kind) { + case INPUT_EVENT_KIND_KEY: + switch (evt->key->key->kind) { + case KEY_VALUE_KIND_NUMBER: + trace_input_event_key_number(idx, evt->key->key->number, + evt->key->down); + break; + case KEY_VALUE_KIND_QCODE: + name = QKeyCode_lookup[evt->key->key->qcode]; + trace_input_event_key_qcode(idx, name, evt->key->down); + break; + case KEY_VALUE_KIND_MAX: + /* keep gcc happy */ + break; + } + break; + case INPUT_EVENT_KIND_BTN: + name = InputButton_lookup[evt->btn->button]; + trace_input_event_btn(idx, name, evt->btn->down); + break; + case INPUT_EVENT_KIND_REL: + name = InputAxis_lookup[evt->rel->axis]; + trace_input_event_rel(idx, name, evt->rel->value); + break; + case INPUT_EVENT_KIND_ABS: + name = InputAxis_lookup[evt->abs->axis]; + trace_input_event_abs(idx, name, evt->abs->value); + break; + case INPUT_EVENT_KIND_MAX: + /* keep gcc happy */ + break; + } } -void qemu_remove_kbd_event_handler(QEMUPutKbdEntry *entry) +void qemu_input_event_send(QemuConsole *src, InputEvent *evt) { - QTAILQ_REMOVE(&kbd_handlers, entry, next); -} + QemuInputHandlerState *s; -static void check_mode_change(void) -{ - static int current_is_absolute, current_has_absolute; - int is_absolute; - int has_absolute; + if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { + return; + } - is_absolute = kbd_mouse_is_absolute(); - has_absolute = kbd_mouse_has_absolute(); + qemu_input_event_trace(src, evt); - if (is_absolute != current_is_absolute || - has_absolute != current_has_absolute) { - notifier_list_notify(&mouse_mode_notifiers, NULL); + /* pre processing */ + if (graphic_rotate && (evt->kind == INPUT_EVENT_KIND_ABS)) { + qemu_input_transform_abs_rotate(evt); } - current_is_absolute = is_absolute; - current_has_absolute = has_absolute; + /* send event */ + s = qemu_input_find_handler(1 << evt->kind); + s->handler->event(s->dev, src, evt); + s->events++; } -QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, - void *opaque, int absolute, - const char *name) +void qemu_input_event_sync(void) { - QEMUPutMouseEntry *s; - static int mouse_index = 0; - - s = g_malloc0(sizeof(QEMUPutMouseEntry)); + QemuInputHandlerState *s; - s->qemu_put_mouse_event = func; - s->qemu_put_mouse_event_opaque = opaque; - s->qemu_put_mouse_event_absolute = absolute; - s->qemu_put_mouse_event_name = g_strdup(name); - s->index = mouse_index++; - - QTAILQ_INSERT_TAIL(&mouse_handlers, s, node); + if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { + return; + } - check_mode_change(); + trace_input_event_sync(); - return s; + QTAILQ_FOREACH(s, &handlers, node) { + if (!s->events) { + continue; + } + if (s->handler->sync) { + s->handler->sync(s->dev); + } + s->events = 0; + } } -void qemu_activate_mouse_event_handler(QEMUPutMouseEntry *entry) +InputEvent *qemu_input_event_new_key(KeyValue *key, bool down) { - QTAILQ_REMOVE(&mouse_handlers, entry, node); - QTAILQ_INSERT_HEAD(&mouse_handlers, entry, node); - - check_mode_change(); + InputEvent *evt = g_new0(InputEvent, 1); + evt->key = g_new0(InputKeyEvent, 1); + evt->kind = INPUT_EVENT_KIND_KEY; + evt->key->key = key; + evt->key->down = down; + return evt; } -void qemu_remove_mouse_event_handler(QEMUPutMouseEntry *entry) +void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down) { - QTAILQ_REMOVE(&mouse_handlers, entry, node); - - g_free(entry->qemu_put_mouse_event_name); - g_free(entry); - - check_mode_change(); + InputEvent *evt; + evt = qemu_input_event_new_key(key, down); + qemu_input_event_send(src, evt); + qemu_input_event_sync(); + qapi_free_InputEvent(evt); } -QEMUPutLEDEntry *qemu_add_led_event_handler(QEMUPutLEDEvent *func, - void *opaque) +void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down) { - QEMUPutLEDEntry *s; + KeyValue *key = g_new0(KeyValue, 1); + key->kind = KEY_VALUE_KIND_NUMBER; + key->number = num; + qemu_input_event_send_key(src, key, down); +} - s = g_malloc0(sizeof(QEMUPutLEDEntry)); +void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down) +{ + KeyValue *key = g_new0(KeyValue, 1); + key->kind = KEY_VALUE_KIND_QCODE; + key->qcode = q; + qemu_input_event_send_key(src, key, down); +} - s->put_led = func; - s->opaque = opaque; - QTAILQ_INSERT_TAIL(&led_handlers, s, next); - return s; +InputEvent *qemu_input_event_new_btn(InputButton btn, bool down) +{ + InputEvent *evt = g_new0(InputEvent, 1); + evt->btn = g_new0(InputBtnEvent, 1); + evt->kind = INPUT_EVENT_KIND_BTN; + evt->btn->button = btn; + evt->btn->down = down; + return evt; } -void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry) +void qemu_input_queue_btn(QemuConsole *src, InputButton btn, bool down) { - if (entry == NULL) - return; - QTAILQ_REMOVE(&led_handlers, entry, next); - g_free(entry); + InputEvent *evt; + evt = qemu_input_event_new_btn(btn, down); + qemu_input_event_send(src, evt); + qapi_free_InputEvent(evt); } -void kbd_put_keycode(int keycode) +void qemu_input_update_buttons(QemuConsole *src, uint32_t *button_map, + uint32_t button_old, uint32_t button_new) { - QEMUPutKbdEntry *entry = QTAILQ_FIRST(&kbd_handlers); + InputButton btn; + uint32_t mask; - if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { - return; - } - if (entry && entry->put_kbd) { - entry->put_kbd(entry->opaque, keycode); + for (btn = 0; btn < INPUT_BUTTON_MAX; btn++) { + mask = button_map[btn]; + if ((button_old & mask) == (button_new & mask)) { + continue; + } + qemu_input_queue_btn(src, btn, button_new & mask); } } -void kbd_put_ledstate(int ledstate) +bool qemu_input_is_absolute(void) { - QEMUPutLEDEntry *cursor; + QemuInputHandlerState *s; - QTAILQ_FOREACH(cursor, &led_handlers, next) { - cursor->put_led(cursor->opaque, ledstate); - } + s = qemu_input_find_handler(INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS); + return (s != NULL) && (s->handler->mask & INPUT_EVENT_MASK_ABS); } -void kbd_mouse_event(int dx, int dy, int dz, int buttons_state) +int qemu_input_scale_axis(int value, int size_in, int size_out) { - QEMUPutMouseEntry *entry; - QEMUPutMouseEvent *mouse_event; - void *mouse_event_opaque; - int width, height; - - if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { - return; + if (size_in < 2) { + return size_out / 2; } - if (QTAILQ_EMPTY(&mouse_handlers)) { - return; - } - - entry = QTAILQ_FIRST(&mouse_handlers); + return (int64_t)value * (size_out - 1) / (size_in - 1); +} - mouse_event = entry->qemu_put_mouse_event; - mouse_event_opaque = entry->qemu_put_mouse_event_opaque; +InputEvent *qemu_input_event_new_move(InputEventKind kind, + InputAxis axis, int value) +{ + InputEvent *evt = g_new0(InputEvent, 1); + InputMoveEvent *move = g_new0(InputMoveEvent, 1); + + evt->kind = kind; + evt->data = move; + move->axis = axis; + move->value = value; + return evt; +} - if (mouse_event) { - if (entry->qemu_put_mouse_event_absolute) { - width = 0x7fff; - height = 0x7fff; - } else { - width = graphic_width - 1; - height = graphic_height - 1; - } +void qemu_input_queue_rel(QemuConsole *src, InputAxis axis, int value) +{ + InputEvent *evt; + evt = qemu_input_event_new_move(INPUT_EVENT_KIND_REL, axis, value); + qemu_input_event_send(src, evt); + qapi_free_InputEvent(evt); +} - switch (graphic_rotate) { - case 0: - mouse_event(mouse_event_opaque, - dx, dy, dz, buttons_state); - break; - case 90: - mouse_event(mouse_event_opaque, - width - dy, dx, dz, buttons_state); - break; - case 180: - mouse_event(mouse_event_opaque, - width - dx, height - dy, dz, buttons_state); - break; - case 270: - mouse_event(mouse_event_opaque, - dy, height - dx, dz, buttons_state); - break; - } - } +void qemu_input_queue_abs(QemuConsole *src, InputAxis axis, int value, int size) +{ + InputEvent *evt; + int scaled = qemu_input_scale_axis(value, size, INPUT_EVENT_ABS_SIZE); + evt = qemu_input_event_new_move(INPUT_EVENT_KIND_ABS, axis, scaled); + qemu_input_event_send(src, evt); + qapi_free_InputEvent(evt); } -int kbd_mouse_is_absolute(void) +void qemu_input_check_mode_change(void) { - if (QTAILQ_EMPTY(&mouse_handlers)) { - return 0; + static int current_is_absolute; + int is_absolute; + + is_absolute = qemu_input_is_absolute(); + + if (is_absolute != current_is_absolute) { + trace_input_mouse_mode(is_absolute); + notifier_list_notify(&mouse_mode_notifiers, NULL); } - return QTAILQ_FIRST(&mouse_handlers)->qemu_put_mouse_event_absolute; + current_is_absolute = is_absolute; } -int kbd_mouse_has_absolute(void) +void qemu_add_mouse_mode_change_notifier(Notifier *notify) { - QEMUPutMouseEntry *entry; - - QTAILQ_FOREACH(entry, &mouse_handlers, node) { - if (entry->qemu_put_mouse_event_absolute) { - return 1; - } - } + notifier_list_add(&mouse_mode_notifiers, notify); +} - return 0; +void qemu_remove_mouse_mode_change_notifier(Notifier *notify) +{ + notifier_remove(notify); } MouseInfoList *qmp_query_mice(Error **errp) { MouseInfoList *mice_list = NULL; - QEMUPutMouseEntry *cursor; + MouseInfoList *info; + QemuInputHandlerState *s; bool current = true; - QTAILQ_FOREACH(cursor, &mouse_handlers, node) { - MouseInfoList *info = g_malloc0(sizeof(*info)); - info->value = g_malloc0(sizeof(*info->value)); - info->value->name = g_strdup(cursor->qemu_put_mouse_event_name); - info->value->index = cursor->index; - info->value->absolute = !!cursor->qemu_put_mouse_event_absolute; + QTAILQ_FOREACH(s, &handlers, node) { + if (!(s->handler->mask & + (INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS))) { + continue; + } + + info = g_new0(MouseInfoList, 1); + info->value = g_new0(MouseInfo, 1); + info->value->index = s->id; + info->value->name = g_strdup(s->handler->name); + info->value->absolute = s->handler->mask & INPUT_EVENT_MASK_ABS; info->value->current = current; current = false; - info->next = mice_list; mice_list = info; } @@ -524,19 +337,14 @@ MouseInfoList *qmp_query_mice(Error **errp) void do_mouse_set(Monitor *mon, const QDict *qdict) { - QEMUPutMouseEntry *cursor; + QemuInputHandlerState *s; int index = qdict_get_int(qdict, "index"); int found = 0; - if (QTAILQ_EMPTY(&mouse_handlers)) { - monitor_printf(mon, "No mouse devices connected\n"); - return; - } - - QTAILQ_FOREACH(cursor, &mouse_handlers, node) { - if (cursor->index == index) { + QTAILQ_FOREACH(s, &handlers, node) { + if (s->id == index) { found = 1; - qemu_activate_mouse_event_handler(cursor); + qemu_input_handler_activate(s); break; } } @@ -545,15 +353,5 @@ void do_mouse_set(Monitor *mon, const QDict *qdict) monitor_printf(mon, "Mouse at given index not found\n"); } - check_mode_change(); -} - -void qemu_add_mouse_mode_change_notifier(Notifier *notify) -{ - notifier_list_add(&mouse_mode_notifiers, notify); -} - -void qemu_remove_mouse_mode_change_notifier(Notifier *notify) -{ - notifier_remove(notify); + qemu_input_check_mode_change(); } diff --git a/ui/sdl.c b/ui/sdl.c index 9d8583c4e6..c1a16bebdc 100644 --- a/ui/sdl.c +++ b/ui/sdl.c @@ -26,10 +26,13 @@ #undef WIN32_LEAN_AND_MEAN #include <SDL.h> + +#if SDL_MAJOR_VERSION == 1 #include <SDL_syswm.h> #include "qemu-common.h" #include "ui/console.h" +#include "ui/input.h" #include "sysemu/sysemu.h" #include "x_keymap.h" #include "sdl_zoom.h" @@ -261,9 +264,7 @@ static void reset_keys(void) int i; for(i = 0; i < 256; i++) { if (modifiers_state[i]) { - if (i & SCANCODE_GREY) - kbd_put_keycode(SCANCODE_EMUL0); - kbd_put_keycode(i | SCANCODE_UP); + qemu_input_event_send_key_number(dcl->con, i, false); modifiers_state[i] = 0; } } @@ -271,16 +272,12 @@ static void reset_keys(void) static void sdl_process_key(SDL_KeyboardEvent *ev) { - int keycode, v; + int keycode; if (ev->keysym.sym == SDLK_PAUSE) { /* specific case */ - v = 0; - if (ev->type == SDL_KEYUP) - v |= SCANCODE_UP; - kbd_put_keycode(0xe1); - kbd_put_keycode(0x1d | v); - kbd_put_keycode(0x45 | v); + qemu_input_event_send_key_qcode(dcl->con, Q_KEY_CODE_PAUSE, + ev->type == SDL_KEYDOWN); return; } @@ -312,19 +309,15 @@ static void sdl_process_key(SDL_KeyboardEvent *ev) case 0x45: /* num lock */ case 0x3a: /* caps lock */ /* SDL does not send the key up event, so we generate it */ - kbd_put_keycode(keycode); - kbd_put_keycode(keycode | SCANCODE_UP); + qemu_input_event_send_key_number(dcl->con, keycode, true); + qemu_input_event_send_key_number(dcl->con, keycode, false); return; #endif } /* now send the key code */ - if (keycode & SCANCODE_GREY) - kbd_put_keycode(SCANCODE_EMUL0); - if (ev->type == SDL_KEYUP) - kbd_put_keycode(keycode | SCANCODE_UP); - else - kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK); + qemu_input_event_send_key_number(dcl->con, keycode, + ev->type == SDL_KEYDOWN); } static void sdl_update_caption(void) @@ -360,7 +353,7 @@ static void sdl_hide_cursor(void) if (!cursor_hide) return; - if (kbd_mouse_is_absolute()) { + if (qemu_input_is_absolute()) { SDL_ShowCursor(1); SDL_SetCursor(sdl_cursor_hidden); } else { @@ -373,10 +366,10 @@ static void sdl_show_cursor(void) if (!cursor_hide) return; - if (!kbd_mouse_is_absolute() || !qemu_console_is_graphic(NULL)) { + if (!qemu_input_is_absolute() || !qemu_console_is_graphic(NULL)) { SDL_ShowCursor(1); if (guest_cursor && - (gui_grab || kbd_mouse_is_absolute() || absolute_enabled)) + (gui_grab || qemu_input_is_absolute() || absolute_enabled)) SDL_SetCursor(guest_sprite); else SDL_SetCursor(sdl_cursor_normal); @@ -395,8 +388,9 @@ static void sdl_grab_start(void) } if (guest_cursor) { SDL_SetCursor(guest_sprite); - if (!kbd_mouse_is_absolute() && !absolute_enabled) + if (!qemu_input_is_absolute() && !absolute_enabled) { SDL_WarpMouse(guest_x, guest_y); + } } else sdl_hide_cursor(); SDL_WM_GrabInput(SDL_GRAB_ON); @@ -425,7 +419,7 @@ static void absolute_mouse_grab(void) static void sdl_mouse_mode_change(Notifier *notify, void *data) { - if (kbd_mouse_is_absolute()) { + if (qemu_input_is_absolute()) { if (!absolute_enabled) { absolute_enabled = 1; if (qemu_console_is_graphic(NULL)) { @@ -440,33 +434,36 @@ static void sdl_mouse_mode_change(Notifier *notify, void *data) } } -static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state) +static void sdl_send_mouse_event(int dx, int dy, int x, int y, int state) { - int buttons = 0; - - if (state & SDL_BUTTON(SDL_BUTTON_LEFT)) { - buttons |= MOUSE_EVENT_LBUTTON; - } - if (state & SDL_BUTTON(SDL_BUTTON_RIGHT)) { - buttons |= MOUSE_EVENT_RBUTTON; - } - if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) { - buttons |= MOUSE_EVENT_MBUTTON; - } - - if (kbd_mouse_is_absolute()) { - dx = x * 0x7FFF / (real_screen->w - 1); - dy = y * 0x7FFF / (real_screen->h - 1); + static uint32_t bmap[INPUT_BUTTON_MAX] = { + [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT), + [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE), + [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT), + [INPUT_BUTTON_WHEEL_UP] = SDL_BUTTON(SDL_BUTTON_WHEELUP), + [INPUT_BUTTON_WHEEL_DOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN), + }; + static uint32_t prev_state; + + if (prev_state != state) { + qemu_input_update_buttons(dcl->con, bmap, prev_state, state); + prev_state = state; + } + + if (qemu_input_is_absolute()) { + qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, x, + real_screen->w); + qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, y, + real_screen->h); } else if (guest_cursor) { x -= guest_x; y -= guest_y; guest_x += x; guest_y += y; - dx = x; - dy = y; + qemu_input_queue_rel(dcl->con, INPUT_AXIS_X, x); + qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, y); } - - kbd_mouse_event(dx, dy, dz, buttons); + qemu_input_event_sync(); } static void sdl_scale(int width, int height) @@ -694,7 +691,7 @@ static void handle_mousemotion(SDL_Event *ev) int max_x, max_y; if (qemu_console_is_graphic(NULL) && - (kbd_mouse_is_absolute() || absolute_enabled)) { + (qemu_input_is_absolute() || absolute_enabled)) { max_x = real_screen->w - 1; max_y = real_screen->h - 1; if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 || @@ -707,8 +704,8 @@ static void handle_mousemotion(SDL_Event *ev) sdl_grab_start(); } } - if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) { - sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel, 0, + if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { + sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel, ev->motion.x, ev->motion.y, ev->motion.state); } } @@ -717,35 +714,24 @@ static void handle_mousebutton(SDL_Event *ev) { int buttonstate = SDL_GetMouseState(NULL, NULL); SDL_MouseButtonEvent *bev; - int dz; if (!qemu_console_is_graphic(NULL)) { return; } bev = &ev->button; - if (!gui_grab && !kbd_mouse_is_absolute()) { + if (!gui_grab && !qemu_input_is_absolute()) { if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) { /* start grabbing all events */ sdl_grab_start(); } } else { - dz = 0; if (ev->type == SDL_MOUSEBUTTONDOWN) { buttonstate |= SDL_BUTTON(bev->button); } else { buttonstate &= ~SDL_BUTTON(bev->button); } -#ifdef SDL_BUTTON_WHEELUP - if (bev->button == SDL_BUTTON_WHEELUP && - ev->type == SDL_MOUSEBUTTONDOWN) { - dz = -1; - } else if (bev->button == SDL_BUTTON_WHEELDOWN && - ev->type == SDL_MOUSEBUTTONDOWN) { - dz = 1; - } -#endif - sdl_send_mouse_event(0, 0, dz, bev->x, bev->y, buttonstate); + sdl_send_mouse_event(0, 0, bev->x, bev->y, buttonstate); } } @@ -760,7 +746,7 @@ static void handle_activation(SDL_Event *ev) } #endif if (!gui_grab && ev->active.gain && qemu_console_is_graphic(NULL) && - (kbd_mouse_is_absolute() || absolute_enabled)) { + (qemu_input_is_absolute() || absolute_enabled)) { absolute_mouse_grab(); } if (ev->active.state & SDL_APPACTIVE) { @@ -832,10 +818,11 @@ static void sdl_mouse_warp(DisplayChangeListener *dcl, if (on) { if (!guest_cursor) sdl_show_cursor(); - if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) { + if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { SDL_SetCursor(guest_sprite); - if (!kbd_mouse_is_absolute() && !absolute_enabled) + if (!qemu_input_is_absolute() && !absolute_enabled) { SDL_WarpMouse(x, y); + } } } else if (gui_grab) sdl_hide_cursor(); @@ -863,7 +850,7 @@ static void sdl_mouse_define(DisplayChangeListener *dcl, g_free(mask); if (guest_cursor && - (gui_grab || kbd_mouse_is_absolute() || absolute_enabled)) + (gui_grab || qemu_input_is_absolute() || absolute_enabled)) SDL_SetCursor(guest_sprite); } @@ -966,3 +953,4 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) atexit(sdl_cleanup); } +#endif diff --git a/ui/sdl2-keymap.h b/ui/sdl2-keymap.h new file mode 100644 index 0000000000..5a12f4543a --- /dev/null +++ b/ui/sdl2-keymap.h @@ -0,0 +1,266 @@ + +/* map SDL2 scancodes to QKeyCode */ + +static const int sdl2_scancode_to_qcode[SDL_NUM_SCANCODES] = { + [SDL_SCANCODE_A] = Q_KEY_CODE_A, + [SDL_SCANCODE_B] = Q_KEY_CODE_B, + [SDL_SCANCODE_C] = Q_KEY_CODE_C, + [SDL_SCANCODE_D] = Q_KEY_CODE_D, + [SDL_SCANCODE_E] = Q_KEY_CODE_E, + [SDL_SCANCODE_F] = Q_KEY_CODE_F, + [SDL_SCANCODE_G] = Q_KEY_CODE_G, + [SDL_SCANCODE_H] = Q_KEY_CODE_H, + [SDL_SCANCODE_I] = Q_KEY_CODE_I, + [SDL_SCANCODE_J] = Q_KEY_CODE_J, + [SDL_SCANCODE_K] = Q_KEY_CODE_K, + [SDL_SCANCODE_L] = Q_KEY_CODE_L, + [SDL_SCANCODE_M] = Q_KEY_CODE_M, + [SDL_SCANCODE_N] = Q_KEY_CODE_N, + [SDL_SCANCODE_O] = Q_KEY_CODE_O, + [SDL_SCANCODE_P] = Q_KEY_CODE_P, + [SDL_SCANCODE_Q] = Q_KEY_CODE_Q, + [SDL_SCANCODE_R] = Q_KEY_CODE_R, + [SDL_SCANCODE_S] = Q_KEY_CODE_S, + [SDL_SCANCODE_T] = Q_KEY_CODE_T, + [SDL_SCANCODE_U] = Q_KEY_CODE_U, + [SDL_SCANCODE_V] = Q_KEY_CODE_V, + [SDL_SCANCODE_W] = Q_KEY_CODE_W, + [SDL_SCANCODE_X] = Q_KEY_CODE_X, + [SDL_SCANCODE_Y] = Q_KEY_CODE_Y, + [SDL_SCANCODE_Z] = Q_KEY_CODE_Z, + + [SDL_SCANCODE_1] = Q_KEY_CODE_1, + [SDL_SCANCODE_2] = Q_KEY_CODE_2, + [SDL_SCANCODE_3] = Q_KEY_CODE_3, + [SDL_SCANCODE_4] = Q_KEY_CODE_4, + [SDL_SCANCODE_5] = Q_KEY_CODE_5, + [SDL_SCANCODE_6] = Q_KEY_CODE_6, + [SDL_SCANCODE_7] = Q_KEY_CODE_7, + [SDL_SCANCODE_8] = Q_KEY_CODE_8, + [SDL_SCANCODE_9] = Q_KEY_CODE_9, + [SDL_SCANCODE_0] = Q_KEY_CODE_0, + + [SDL_SCANCODE_RETURN] = Q_KEY_CODE_RET, + [SDL_SCANCODE_ESCAPE] = Q_KEY_CODE_ESC, + [SDL_SCANCODE_BACKSPACE] = Q_KEY_CODE_BACKSPACE, + [SDL_SCANCODE_TAB] = Q_KEY_CODE_TAB, + [SDL_SCANCODE_SPACE] = Q_KEY_CODE_SPC, + [SDL_SCANCODE_MINUS] = Q_KEY_CODE_MINUS, + [SDL_SCANCODE_EQUALS] = Q_KEY_CODE_EQUAL, + [SDL_SCANCODE_LEFTBRACKET] = Q_KEY_CODE_BRACKET_LEFT, + [SDL_SCANCODE_RIGHTBRACKET] = Q_KEY_CODE_BRACKET_RIGHT, + [SDL_SCANCODE_BACKSLASH] = Q_KEY_CODE_BACKSLASH, +#if 0 + [SDL_SCANCODE_NONUSHASH] = Q_KEY_CODE_NONUSHASH, +#endif + [SDL_SCANCODE_SEMICOLON] = Q_KEY_CODE_SEMICOLON, + [SDL_SCANCODE_APOSTROPHE] = Q_KEY_CODE_APOSTROPHE, + [SDL_SCANCODE_GRAVE] = Q_KEY_CODE_GRAVE_ACCENT, + [SDL_SCANCODE_COMMA] = Q_KEY_CODE_COMMA, + [SDL_SCANCODE_PERIOD] = Q_KEY_CODE_DOT, + [SDL_SCANCODE_SLASH] = Q_KEY_CODE_SLASH, + [SDL_SCANCODE_CAPSLOCK] = Q_KEY_CODE_CAPS_LOCK, + + [SDL_SCANCODE_F1] = Q_KEY_CODE_F1, + [SDL_SCANCODE_F2] = Q_KEY_CODE_F2, + [SDL_SCANCODE_F3] = Q_KEY_CODE_F3, + [SDL_SCANCODE_F4] = Q_KEY_CODE_F4, + [SDL_SCANCODE_F5] = Q_KEY_CODE_F5, + [SDL_SCANCODE_F6] = Q_KEY_CODE_F6, + [SDL_SCANCODE_F7] = Q_KEY_CODE_F7, + [SDL_SCANCODE_F8] = Q_KEY_CODE_F8, + [SDL_SCANCODE_F9] = Q_KEY_CODE_F9, + [SDL_SCANCODE_F10] = Q_KEY_CODE_F10, + [SDL_SCANCODE_F11] = Q_KEY_CODE_F11, + [SDL_SCANCODE_F12] = Q_KEY_CODE_F12, + + [SDL_SCANCODE_PRINTSCREEN] = Q_KEY_CODE_PRINT, + [SDL_SCANCODE_SCROLLLOCK] = Q_KEY_CODE_SCROLL_LOCK, + [SDL_SCANCODE_PAUSE] = Q_KEY_CODE_PAUSE, + [SDL_SCANCODE_INSERT] = Q_KEY_CODE_INSERT, + [SDL_SCANCODE_HOME] = Q_KEY_CODE_HOME, + [SDL_SCANCODE_PAGEUP] = Q_KEY_CODE_PGUP, + [SDL_SCANCODE_DELETE] = Q_KEY_CODE_DELETE, + [SDL_SCANCODE_END] = Q_KEY_CODE_END, + [SDL_SCANCODE_PAGEDOWN] = Q_KEY_CODE_PGDN, + [SDL_SCANCODE_RIGHT] = Q_KEY_CODE_RIGHT, + [SDL_SCANCODE_LEFT] = Q_KEY_CODE_LEFT, + [SDL_SCANCODE_DOWN] = Q_KEY_CODE_DOWN, + [SDL_SCANCODE_UP] = Q_KEY_CODE_UP, + [SDL_SCANCODE_NUMLOCKCLEAR] = Q_KEY_CODE_NUM_LOCK, + + [SDL_SCANCODE_KP_DIVIDE] = Q_KEY_CODE_KP_DIVIDE, + [SDL_SCANCODE_KP_MULTIPLY] = Q_KEY_CODE_KP_MULTIPLY, + [SDL_SCANCODE_KP_MINUS] = Q_KEY_CODE_KP_SUBTRACT, + [SDL_SCANCODE_KP_PLUS] = Q_KEY_CODE_KP_ADD, + [SDL_SCANCODE_KP_ENTER] = Q_KEY_CODE_KP_ENTER, + [SDL_SCANCODE_KP_1] = Q_KEY_CODE_KP_1, + [SDL_SCANCODE_KP_2] = Q_KEY_CODE_KP_2, + [SDL_SCANCODE_KP_3] = Q_KEY_CODE_KP_3, + [SDL_SCANCODE_KP_4] = Q_KEY_CODE_KP_4, + [SDL_SCANCODE_KP_5] = Q_KEY_CODE_KP_5, + [SDL_SCANCODE_KP_6] = Q_KEY_CODE_KP_6, + [SDL_SCANCODE_KP_7] = Q_KEY_CODE_KP_7, + [SDL_SCANCODE_KP_8] = Q_KEY_CODE_KP_8, + [SDL_SCANCODE_KP_9] = Q_KEY_CODE_KP_9, + [SDL_SCANCODE_KP_0] = Q_KEY_CODE_KP_0, + [SDL_SCANCODE_KP_PERIOD] = Q_KEY_CODE_KP_DECIMAL, +#if 0 + [SDL_SCANCODE_NONUSBACKSLASH] = Q_KEY_CODE_NONUSBACKSLASH, + [SDL_SCANCODE_APPLICATION] = Q_KEY_CODE_APPLICATION, + [SDL_SCANCODE_POWER] = Q_KEY_CODE_POWER, + [SDL_SCANCODE_KP_EQUALS] = Q_KEY_CODE_KP_EQUALS, + + [SDL_SCANCODE_F13] = Q_KEY_CODE_F13, + [SDL_SCANCODE_F14] = Q_KEY_CODE_F14, + [SDL_SCANCODE_F15] = Q_KEY_CODE_F15, + [SDL_SCANCODE_F16] = Q_KEY_CODE_F16, + [SDL_SCANCODE_F17] = Q_KEY_CODE_F17, + [SDL_SCANCODE_F18] = Q_KEY_CODE_F18, + [SDL_SCANCODE_F19] = Q_KEY_CODE_F19, + [SDL_SCANCODE_F20] = Q_KEY_CODE_F20, + [SDL_SCANCODE_F21] = Q_KEY_CODE_F21, + [SDL_SCANCODE_F22] = Q_KEY_CODE_F22, + [SDL_SCANCODE_F23] = Q_KEY_CODE_F23, + [SDL_SCANCODE_F24] = Q_KEY_CODE_F24, + + [SDL_SCANCODE_EXECUTE] = Q_KEY_CODE_EXECUTE, +#endif + [SDL_SCANCODE_HELP] = Q_KEY_CODE_HELP, + [SDL_SCANCODE_MENU] = Q_KEY_CODE_MENU, +#if 0 + [SDL_SCANCODE_SELECT] = Q_KEY_CODE_SELECT, +#endif + [SDL_SCANCODE_STOP] = Q_KEY_CODE_STOP, + [SDL_SCANCODE_AGAIN] = Q_KEY_CODE_AGAIN, + [SDL_SCANCODE_UNDO] = Q_KEY_CODE_UNDO, + [SDL_SCANCODE_CUT] = Q_KEY_CODE_CUT, + [SDL_SCANCODE_COPY] = Q_KEY_CODE_COPY, + [SDL_SCANCODE_PASTE] = Q_KEY_CODE_PASTE, + [SDL_SCANCODE_FIND] = Q_KEY_CODE_FIND, +#if 0 + [SDL_SCANCODE_MUTE] = Q_KEY_CODE_MUTE, + [SDL_SCANCODE_VOLUMEUP] = Q_KEY_CODE_VOLUMEUP, + [SDL_SCANCODE_VOLUMEDOWN] = Q_KEY_CODE_VOLUMEDOWN, + + [SDL_SCANCODE_KP_COMMA] = Q_KEY_CODE_KP_COMMA, + [SDL_SCANCODE_KP_EQUALSAS400] = Q_KEY_CODE_KP_EQUALSAS400, + + [SDL_SCANCODE_INTERNATIONAL1] = Q_KEY_CODE_INTERNATIONAL1, + [SDL_SCANCODE_INTERNATIONAL2] = Q_KEY_CODE_INTERNATIONAL2, + [SDL_SCANCODE_INTERNATIONAL3] = Q_KEY_CODE_INTERNATIONAL3, + [SDL_SCANCODE_INTERNATIONAL4] = Q_KEY_CODE_INTERNATIONAL4, + [SDL_SCANCODE_INTERNATIONAL5] = Q_KEY_CODE_INTERNATIONAL5, + [SDL_SCANCODE_INTERNATIONAL6] = Q_KEY_CODE_INTERNATIONAL6, + [SDL_SCANCODE_INTERNATIONAL7] = Q_KEY_CODE_INTERNATIONAL7, + [SDL_SCANCODE_INTERNATIONAL8] = Q_KEY_CODE_INTERNATIONAL8, + [SDL_SCANCODE_INTERNATIONAL9] = Q_KEY_CODE_INTERNATIONAL9, + [SDL_SCANCODE_LANG1] = Q_KEY_CODE_LANG1, + [SDL_SCANCODE_LANG2] = Q_KEY_CODE_LANG2, + [SDL_SCANCODE_LANG3] = Q_KEY_CODE_LANG3, + [SDL_SCANCODE_LANG4] = Q_KEY_CODE_LANG4, + [SDL_SCANCODE_LANG5] = Q_KEY_CODE_LANG5, + [SDL_SCANCODE_LANG6] = Q_KEY_CODE_LANG6, + [SDL_SCANCODE_LANG7] = Q_KEY_CODE_LANG7, + [SDL_SCANCODE_LANG8] = Q_KEY_CODE_LANG8, + [SDL_SCANCODE_LANG9] = Q_KEY_CODE_LANG9, + [SDL_SCANCODE_ALTERASE] = Q_KEY_CODE_ALTERASE, +#endif + [SDL_SCANCODE_SYSREQ] = Q_KEY_CODE_SYSRQ, +#if 0 + [SDL_SCANCODE_CANCEL] = Q_KEY_CODE_CANCEL, + [SDL_SCANCODE_CLEAR] = Q_KEY_CODE_CLEAR, + [SDL_SCANCODE_PRIOR] = Q_KEY_CODE_PRIOR, + [SDL_SCANCODE_RETURN2] = Q_KEY_CODE_RETURN2, + [SDL_SCANCODE_SEPARATOR] = Q_KEY_CODE_SEPARATOR, + [SDL_SCANCODE_OUT] = Q_KEY_CODE_OUT, + [SDL_SCANCODE_OPER] = Q_KEY_CODE_OPER, + [SDL_SCANCODE_CLEARAGAIN] = Q_KEY_CODE_CLEARAGAIN, + [SDL_SCANCODE_CRSEL] = Q_KEY_CODE_CRSEL, + [SDL_SCANCODE_EXSEL] = Q_KEY_CODE_EXSEL, + [SDL_SCANCODE_KP_00] = Q_KEY_CODE_KP_00, + [SDL_SCANCODE_KP_000] = Q_KEY_CODE_KP_000, + [SDL_SCANCODE_THOUSANDSSEPARATOR] = Q_KEY_CODE_THOUSANDSSEPARATOR, + [SDL_SCANCODE_DECIMALSEPARATOR] = Q_KEY_CODE_DECIMALSEPARATOR, + [SDL_SCANCODE_CURRENCYUNIT] = Q_KEY_CODE_CURRENCYUNIT, + [SDL_SCANCODE_CURRENCYSUBUNIT] = Q_KEY_CODE_CURRENCYSUBUNIT, + [SDL_SCANCODE_KP_LEFTPAREN] = Q_KEY_CODE_KP_LEFTPAREN, + [SDL_SCANCODE_KP_RIGHTPAREN] = Q_KEY_CODE_KP_RIGHTPAREN, + [SDL_SCANCODE_KP_LEFTBRACE] = Q_KEY_CODE_KP_LEFTBRACE, + [SDL_SCANCODE_KP_RIGHTBRACE] = Q_KEY_CODE_KP_RIGHTBRACE, + [SDL_SCANCODE_KP_TAB] = Q_KEY_CODE_KP_TAB, + [SDL_SCANCODE_KP_BACKSPACE] = Q_KEY_CODE_KP_BACKSPACE, + [SDL_SCANCODE_KP_A] = Q_KEY_CODE_KP_A, + [SDL_SCANCODE_KP_B] = Q_KEY_CODE_KP_B, + [SDL_SCANCODE_KP_C] = Q_KEY_CODE_KP_C, + [SDL_SCANCODE_KP_D] = Q_KEY_CODE_KP_D, + [SDL_SCANCODE_KP_E] = Q_KEY_CODE_KP_E, + [SDL_SCANCODE_KP_F] = Q_KEY_CODE_KP_F, + [SDL_SCANCODE_KP_XOR] = Q_KEY_CODE_KP_XOR, + [SDL_SCANCODE_KP_POWER] = Q_KEY_CODE_KP_POWER, + [SDL_SCANCODE_KP_PERCENT] = Q_KEY_CODE_KP_PERCENT, + [SDL_SCANCODE_KP_LESS] = Q_KEY_CODE_KP_LESS, + [SDL_SCANCODE_KP_GREATER] = Q_KEY_CODE_KP_GREATER, + [SDL_SCANCODE_KP_AMPERSAND] = Q_KEY_CODE_KP_AMPERSAND, + [SDL_SCANCODE_KP_DBLAMPERSAND] = Q_KEY_CODE_KP_DBLAMPERSAND, + [SDL_SCANCODE_KP_VERTICALBAR] = Q_KEY_CODE_KP_VERTICALBAR, + [SDL_SCANCODE_KP_DBLVERTICALBAR] = Q_KEY_CODE_KP_DBLVERTICALBAR, + [SDL_SCANCODE_KP_COLON] = Q_KEY_CODE_KP_COLON, + [SDL_SCANCODE_KP_HASH] = Q_KEY_CODE_KP_HASH, + [SDL_SCANCODE_KP_SPACE] = Q_KEY_CODE_KP_SPACE, + [SDL_SCANCODE_KP_AT] = Q_KEY_CODE_KP_AT, + [SDL_SCANCODE_KP_EXCLAM] = Q_KEY_CODE_KP_EXCLAM, + [SDL_SCANCODE_KP_MEMSTORE] = Q_KEY_CODE_KP_MEMSTORE, + [SDL_SCANCODE_KP_MEMRECALL] = Q_KEY_CODE_KP_MEMRECALL, + [SDL_SCANCODE_KP_MEMCLEAR] = Q_KEY_CODE_KP_MEMCLEAR, + [SDL_SCANCODE_KP_MEMADD] = Q_KEY_CODE_KP_MEMADD, + [SDL_SCANCODE_KP_MEMSUBTRACT] = Q_KEY_CODE_KP_MEMSUBTRACT, + [SDL_SCANCODE_KP_MEMMULTIPLY] = Q_KEY_CODE_KP_MEMMULTIPLY, + [SDL_SCANCODE_KP_MEMDIVIDE] = Q_KEY_CODE_KP_MEMDIVIDE, + [SDL_SCANCODE_KP_PLUSMINUS] = Q_KEY_CODE_KP_PLUSMINUS, + [SDL_SCANCODE_KP_CLEAR] = Q_KEY_CODE_KP_CLEAR, + [SDL_SCANCODE_KP_CLEARENTRY] = Q_KEY_CODE_KP_CLEARENTRY, + [SDL_SCANCODE_KP_BINARY] = Q_KEY_CODE_KP_BINARY, + [SDL_SCANCODE_KP_OCTAL] = Q_KEY_CODE_KP_OCTAL, + [SDL_SCANCODE_KP_DECIMAL] = Q_KEY_CODE_KP_DECIMAL, + [SDL_SCANCODE_KP_HEXADECIMAL] = Q_KEY_CODE_KP_HEXADECIMAL, +#endif + [SDL_SCANCODE_LCTRL] = Q_KEY_CODE_CTRL, + [SDL_SCANCODE_LSHIFT] = Q_KEY_CODE_SHIFT, + [SDL_SCANCODE_LALT] = Q_KEY_CODE_ALT, + [SDL_SCANCODE_LGUI] = Q_KEY_CODE_META_L, + [SDL_SCANCODE_RCTRL] = Q_KEY_CODE_CTRL_R, + [SDL_SCANCODE_RSHIFT] = Q_KEY_CODE_SHIFT_R, + [SDL_SCANCODE_RALT] = Q_KEY_CODE_ALTGR, + [SDL_SCANCODE_RGUI] = Q_KEY_CODE_META_R, +#if 0 + [SDL_SCANCODE_MODE] = Q_KEY_CODE_MODE, + [SDL_SCANCODE_AUDIONEXT] = Q_KEY_CODE_AUDIONEXT, + [SDL_SCANCODE_AUDIOPREV] = Q_KEY_CODE_AUDIOPREV, + [SDL_SCANCODE_AUDIOSTOP] = Q_KEY_CODE_AUDIOSTOP, + [SDL_SCANCODE_AUDIOPLAY] = Q_KEY_CODE_AUDIOPLAY, + [SDL_SCANCODE_AUDIOMUTE] = Q_KEY_CODE_AUDIOMUTE, + [SDL_SCANCODE_MEDIASELECT] = Q_KEY_CODE_MEDIASELECT, + [SDL_SCANCODE_WWW] = Q_KEY_CODE_WWW, + [SDL_SCANCODE_MAIL] = Q_KEY_CODE_MAIL, + [SDL_SCANCODE_CALCULATOR] = Q_KEY_CODE_CALCULATOR, + [SDL_SCANCODE_COMPUTER] = Q_KEY_CODE_COMPUTER, + [SDL_SCANCODE_AC_SEARCH] = Q_KEY_CODE_AC_SEARCH, + [SDL_SCANCODE_AC_HOME] = Q_KEY_CODE_AC_HOME, + [SDL_SCANCODE_AC_BACK] = Q_KEY_CODE_AC_BACK, + [SDL_SCANCODE_AC_FORWARD] = Q_KEY_CODE_AC_FORWARD, + [SDL_SCANCODE_AC_STOP] = Q_KEY_CODE_AC_STOP, + [SDL_SCANCODE_AC_REFRESH] = Q_KEY_CODE_AC_REFRESH, + [SDL_SCANCODE_AC_BOOKMARKS] = Q_KEY_CODE_AC_BOOKMARKS, + [SDL_SCANCODE_BRIGHTNESSDOWN] = Q_KEY_CODE_BRIGHTNESSDOWN, + [SDL_SCANCODE_BRIGHTNESSUP] = Q_KEY_CODE_BRIGHTNESSUP, + [SDL_SCANCODE_DISPLAYSWITCH] = Q_KEY_CODE_DISPLAYSWITCH, + [SDL_SCANCODE_KBDILLUMTOGGLE] = Q_KEY_CODE_KBDILLUMTOGGLE, + [SDL_SCANCODE_KBDILLUMDOWN] = Q_KEY_CODE_KBDILLUMDOWN, + [SDL_SCANCODE_KBDILLUMUP] = Q_KEY_CODE_KBDILLUMUP, + [SDL_SCANCODE_EJECT] = Q_KEY_CODE_EJECT, + [SDL_SCANCODE_SLEEP] = Q_KEY_CODE_SLEEP, + [SDL_SCANCODE_APP1] = Q_KEY_CODE_APP1, + [SDL_SCANCODE_APP2] = Q_KEY_CODE_APP2, +#endif +}; diff --git a/ui/sdl2.c b/ui/sdl2.c new file mode 100644 index 0000000000..f1532e9d2c --- /dev/null +++ b/ui/sdl2.c @@ -0,0 +1,829 @@ +/* + * QEMU SDL display driver + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/* Ported SDL 1.2 code to 2.0 by Dave Airlie. */ + +/* Avoid compiler warning because macro is redefined in SDL_syswm.h. */ +#undef WIN32_LEAN_AND_MEAN + +#include <SDL.h> + +#if SDL_MAJOR_VERSION == 2 +#include <SDL_syswm.h> + +#include "qemu-common.h" +#include "ui/console.h" +#include "ui/input.h" +#include "sysemu/sysemu.h" +#include "sdl_zoom.h" + +#include "sdl2-keymap.h" + +static int sdl2_num_outputs; +static struct sdl2_state { + DisplayChangeListener dcl; + DisplaySurface *surface; + SDL_Texture *texture; + SDL_Window *real_window; + SDL_Renderer *real_renderer; + int idx; + int last_vm_running; /* per console for caption reasons */ + int x, y; +} *sdl2_console; + +static SDL_Surface *guest_sprite_surface; +static int gui_grab; /* if true, all keyboard/mouse events are grabbed */ + +static bool gui_saved_scaling; +static int gui_saved_width; +static int gui_saved_height; +static int gui_saved_grab; +static int gui_fullscreen; +static int gui_noframe; +static int gui_key_modifier_pressed; +static int gui_keysym; +static int gui_grab_code = KMOD_LALT | KMOD_LCTRL; +static uint8_t modifiers_state[SDL_NUM_SCANCODES]; +static SDL_Cursor *sdl_cursor_normal; +static SDL_Cursor *sdl_cursor_hidden; +static int absolute_enabled; +static int guest_cursor; +static int guest_x, guest_y; +static SDL_Cursor *guest_sprite; +static int scaling_active; +static Notifier mouse_mode_notifier; + +static void sdl_update_caption(struct sdl2_state *scon); + +static struct sdl2_state *get_scon_from_window(uint32_t window_id) +{ + int i; + for (i = 0; i < sdl2_num_outputs; i++) { + if (sdl2_console[i].real_window == SDL_GetWindowFromID(window_id)) { + return &sdl2_console[i]; + } + } + return NULL; +} + +static void sdl_update(DisplayChangeListener *dcl, + int x, int y, int w, int h) +{ + struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); + SDL_Rect rect; + DisplaySurface *surf = qemu_console_surface(dcl->con); + + if (!surf) { + return; + } + if (!scon->texture) { + return; + } + + rect.x = x; + rect.y = y; + rect.w = w; + rect.h = h; + + SDL_UpdateTexture(scon->texture, NULL, surface_data(surf), + surface_stride(surf)); + SDL_RenderCopy(scon->real_renderer, scon->texture, &rect, &rect); + SDL_RenderPresent(scon->real_renderer); +} + +static void do_sdl_resize(struct sdl2_state *scon, int width, int height, + int bpp) +{ + int flags; + + if (scon->real_window && scon->real_renderer) { + if (width && height) { + SDL_RenderSetLogicalSize(scon->real_renderer, width, height); + SDL_SetWindowSize(scon->real_window, width, height); + } else { + SDL_DestroyRenderer(scon->real_renderer); + SDL_DestroyWindow(scon->real_window); + scon->real_renderer = NULL; + scon->real_window = NULL; + } + } else { + if (!width || !height) { + return; + } + flags = 0; + if (gui_fullscreen) { + flags |= SDL_WINDOW_FULLSCREEN; + } else { + flags |= SDL_WINDOW_RESIZABLE; + } + + scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + width, height, flags); + scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0); + sdl_update_caption(scon); + } +} + +static void sdl_switch(DisplayChangeListener *dcl, + DisplaySurface *new_surface) +{ + struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); + int format = 0; + int idx = scon->idx; + DisplaySurface *old_surface = scon->surface; + + /* temporary hack: allows to call sdl_switch to handle scaling changes */ + if (new_surface) { + scon->surface = new_surface; + } + + if (!new_surface && idx > 0) { + scon->surface = NULL; + } + + if (new_surface == NULL) { + do_sdl_resize(scon, 0, 0, 0); + } else { + do_sdl_resize(scon, surface_width(scon->surface), + surface_height(scon->surface), 0); + } + + if (old_surface && scon->texture) { + SDL_DestroyTexture(scon->texture); + scon->texture = NULL; + } + + if (new_surface) { + if (!scon->texture) { + if (surface_bits_per_pixel(scon->surface) == 16) { + format = SDL_PIXELFORMAT_RGB565; + } else if (surface_bits_per_pixel(scon->surface) == 32) { + format = SDL_PIXELFORMAT_ARGB8888; + } + + scon->texture = SDL_CreateTexture(scon->real_renderer, format, + SDL_TEXTUREACCESS_STREAMING, + surface_width(new_surface), + surface_height(new_surface)); + } + } +} + +static void reset_keys(void) +{ + int i; + + for (i = 0; i < 256; i++) { + if (modifiers_state[i]) { + int qcode = sdl2_scancode_to_qcode[i]; + qemu_input_event_send_key_qcode(NULL, qcode, false); + modifiers_state[i] = 0; + } + } +} + +static void sdl_process_key(SDL_KeyboardEvent *ev) +{ + int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode]; + + switch (ev->keysym.scancode) { +#if 0 + case SDL_SCANCODE_NUMLOCKCLEAR: + case SDL_SCANCODE_CAPSLOCK: + /* SDL does not send the key up event, so we generate it */ + qemu_input_event_send_key_qcode(NULL, qcode, true); + qemu_input_event_send_key_qcode(NULL, qcode, false); + return; +#endif + case SDL_SCANCODE_LCTRL: + case SDL_SCANCODE_LSHIFT: + case SDL_SCANCODE_LALT: + case SDL_SCANCODE_LGUI: + case SDL_SCANCODE_RCTRL: + case SDL_SCANCODE_RSHIFT: + case SDL_SCANCODE_RALT: + case SDL_SCANCODE_RGUI: + if (ev->type == SDL_KEYUP) { + modifiers_state[ev->keysym.scancode] = 0; + } else { + modifiers_state[ev->keysym.scancode] = 1; + } + /* fall though */ + default: + qemu_input_event_send_key_qcode(NULL, qcode, + ev->type == SDL_KEYDOWN); + } +} + +static void sdl_update_caption(struct sdl2_state *scon) +{ + char win_title[1024]; + char icon_title[1024]; + const char *status = ""; + + if (!runstate_is_running()) { + status = " [Stopped]"; + } else if (gui_grab) { + if (alt_grab) { + status = " - Press Ctrl-Alt-Shift to exit mouse grab"; + } else if (ctrl_grab) { + status = " - Press Right-Ctrl to exit mouse grab"; + } else { + status = " - Press Ctrl-Alt to exit mouse grab"; + } + } + + if (qemu_name) { + snprintf(win_title, sizeof(win_title), "QEMU (%s-%d)%s", qemu_name, + scon->idx, status); + snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name); + } else { + snprintf(win_title, sizeof(win_title), "QEMU%s", status); + snprintf(icon_title, sizeof(icon_title), "QEMU"); + } + + if (scon->real_window) { + SDL_SetWindowTitle(scon->real_window, win_title); + } +} + +static void sdl_hide_cursor(void) +{ + if (!cursor_hide) { + return; + } + + if (qemu_input_is_absolute()) { + SDL_ShowCursor(1); + SDL_SetCursor(sdl_cursor_hidden); + } else { + SDL_ShowCursor(0); + } +} + +static void sdl_show_cursor(void) +{ + if (!cursor_hide) { + return; + } + + if (!qemu_input_is_absolute()) { + SDL_ShowCursor(1); + if (guest_cursor && + (gui_grab || qemu_input_is_absolute() || absolute_enabled)) { + SDL_SetCursor(guest_sprite); + } else { + SDL_SetCursor(sdl_cursor_normal); + } + } +} + +static void sdl_grab_start(struct sdl2_state *scon) +{ + /* + * If the application is not active, do not try to enter grab state. This + * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the + * application (SDL bug). + */ + if (!(SDL_GetWindowFlags(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) { + return; + } + if (guest_cursor) { + SDL_SetCursor(guest_sprite); + if (!qemu_input_is_absolute() && !absolute_enabled) { + SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y); + } + } else { + sdl_hide_cursor(); + } + SDL_SetWindowGrab(scon->real_window, SDL_TRUE); + gui_grab = 1; + sdl_update_caption(scon); +} + +static void sdl_grab_end(struct sdl2_state *scon) +{ + SDL_SetWindowGrab(scon->real_window, SDL_FALSE); + gui_grab = 0; + sdl_show_cursor(); + sdl_update_caption(scon); +} + +static void absolute_mouse_grab(struct sdl2_state *scon) +{ + int mouse_x, mouse_y; + int scr_w, scr_h; + SDL_GetMouseState(&mouse_x, &mouse_y); + SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); + if (mouse_x > 0 && mouse_x < scr_w - 1 && + mouse_y > 0 && mouse_y < scr_h - 1) { + sdl_grab_start(scon); + } +} + +static void sdl_mouse_mode_change(Notifier *notify, void *data) +{ + if (qemu_input_is_absolute()) { + if (!absolute_enabled) { + absolute_enabled = 1; + absolute_mouse_grab(&sdl2_console[0]); + } + } else if (absolute_enabled) { + if (!gui_fullscreen) { + sdl_grab_end(&sdl2_console[0]); + } + absolute_enabled = 0; + } +} + +static void sdl_send_mouse_event(struct sdl2_state *scon, int dx, int dy, + int dz, int x, int y, int state) +{ + static uint32_t bmap[INPUT_BUTTON_MAX] = { + [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT), + [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE), + [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT), +#if 0 + [INPUT_BUTTON_WHEEL_UP] = SDL_BUTTON(SDL_BUTTON_WHEELUP), + [INPUT_BUTTON_WHEEL_DOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN), +#endif + }; + static uint32_t prev_state; + + if (prev_state != state) { + qemu_input_update_buttons(scon->dcl.con, bmap, prev_state, state); + prev_state = state; + } + + if (qemu_input_is_absolute()) { + int scr_w, scr_h; + int max_w = 0, max_h = 0; + int off_x = 0, off_y = 0; + int cur_off_x = 0, cur_off_y = 0; + int i; + + for (i = 0; i < sdl2_num_outputs; i++) { + struct sdl2_state *thiscon = &sdl2_console[i]; + if (thiscon->real_window && thiscon->surface) { + SDL_GetWindowSize(thiscon->real_window, &scr_w, &scr_h); + cur_off_x = thiscon->x; + cur_off_y = thiscon->y; + if (scr_w + cur_off_x > max_w) { + max_w = scr_w + cur_off_x; + } + if (scr_h + cur_off_y > max_h) { + max_h = scr_h + cur_off_y; + } + if (i == scon->idx) { + off_x = cur_off_x; + off_y = cur_off_y; + } + } + } + qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, max_w); + qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, max_h); + } else if (guest_cursor) { + x -= guest_x; + y -= guest_y; + guest_x += x; + guest_y += y; + qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_X, x); + qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_Y, y); + } + qemu_input_event_sync(); +} + +static void sdl_scale(struct sdl2_state *scon, int width, int height) +{ + int bpp = 0; + do_sdl_resize(scon, width, height, bpp); + scaling_active = 1; +} + +static void toggle_full_screen(struct sdl2_state *scon) +{ + int width = surface_width(scon->surface); + int height = surface_height(scon->surface); + int bpp = surface_bits_per_pixel(scon->surface); + + gui_fullscreen = !gui_fullscreen; + if (gui_fullscreen) { + SDL_GetWindowSize(scon->real_window, + &gui_saved_width, &gui_saved_height); + gui_saved_scaling = scaling_active; + + do_sdl_resize(scon, width, height, bpp); + scaling_active = 0; + + gui_saved_grab = gui_grab; + sdl_grab_start(scon); + } else { + if (gui_saved_scaling) { + sdl_scale(scon, gui_saved_width, gui_saved_height); + } else { + do_sdl_resize(scon, width, height, 0); + } + if (!gui_saved_grab) { + sdl_grab_end(scon); + } + } + graphic_hw_invalidate(scon->dcl.con); + graphic_hw_update(scon->dcl.con); +} + +static void handle_keydown(SDL_Event *ev) +{ + int mod_state; + struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); + + if (alt_grab) { + mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) == + (gui_grab_code | KMOD_LSHIFT); + } else if (ctrl_grab) { + mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL; + } else { + mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code; + } + gui_key_modifier_pressed = mod_state; + + if (gui_key_modifier_pressed) { + switch (ev->key.keysym.scancode) { + case SDL_SCANCODE_F: + toggle_full_screen(scon); + gui_keysym = 1; + break; + case SDL_SCANCODE_U: + if (scaling_active) { + scaling_active = 0; + sdl_switch(&scon->dcl, NULL); + graphic_hw_invalidate(scon->dcl.con); + graphic_hw_update(scon->dcl.con); + } + gui_keysym = 1; + break; + case SDL_SCANCODE_KP_PLUS: + case SDL_SCANCODE_KP_MINUS: + if (!gui_fullscreen) { + int scr_w, scr_h; + int width, height; + SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); + + width = MAX(scr_w + (ev->key.keysym.scancode == + SDL_SCANCODE_KP_PLUS ? 50 : -50), + 160); + height = (surface_height(scon->surface) * width) / + surface_width(scon->surface); + + sdl_scale(scon, width, height); + graphic_hw_invalidate(NULL); + graphic_hw_update(NULL); + gui_keysym = 1; + } + default: + break; + } + } + if (!gui_keysym) { + sdl_process_key(&ev->key); + } +} + +static void handle_keyup(SDL_Event *ev) +{ + int mod_state; + struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); + + if (!alt_grab) { + mod_state = (ev->key.keysym.mod & gui_grab_code); + } else { + mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT)); + } + if (!mod_state && gui_key_modifier_pressed) { + gui_key_modifier_pressed = 0; + if (gui_keysym == 0) { + /* exit/enter grab if pressing Ctrl-Alt */ + if (!gui_grab) { + sdl_grab_start(scon); + } else if (!gui_fullscreen) { + sdl_grab_end(scon); + } + /* SDL does not send back all the modifiers key, so we must + * correct it. */ + reset_keys(); + return; + } + gui_keysym = 0; + } + if (!gui_keysym) { + sdl_process_key(&ev->key); + } +} + +static void handle_mousemotion(SDL_Event *ev) +{ + int max_x, max_y; + struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); + + if (qemu_input_is_absolute() || absolute_enabled) { + int scr_w, scr_h; + SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); + max_x = scr_w - 1; + max_y = scr_h - 1; + if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 || + ev->motion.x == max_x || ev->motion.y == max_y)) { + sdl_grab_end(scon); + } + if (!gui_grab && + (ev->motion.x > 0 && ev->motion.x < max_x && + ev->motion.y > 0 && ev->motion.y < max_y)) { + sdl_grab_start(scon); + } + } + if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { + sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel, 0, + ev->motion.x, ev->motion.y, ev->motion.state); + } +} + +static void handle_mousebutton(SDL_Event *ev) +{ + int buttonstate = SDL_GetMouseState(NULL, NULL); + SDL_MouseButtonEvent *bev; + struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); + int dz; + + bev = &ev->button; + if (!gui_grab && !qemu_input_is_absolute()) { + if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) { + /* start grabbing all events */ + sdl_grab_start(scon); + } + } else { + dz = 0; + if (ev->type == SDL_MOUSEBUTTONDOWN) { + buttonstate |= SDL_BUTTON(bev->button); + } else { + buttonstate &= ~SDL_BUTTON(bev->button); + } +#ifdef SDL_BUTTON_WHEELUP + if (bev->button == SDL_BUTTON_WHEELUP && + ev->type == SDL_MOUSEBUTTONDOWN) { + dz = -1; + } else if (bev->button == SDL_BUTTON_WHEELDOWN && + ev->type == SDL_MOUSEBUTTONDOWN) { + dz = 1; + } +#endif + sdl_send_mouse_event(scon, 0, 0, dz, bev->x, bev->y, buttonstate); + } +} + +static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev) +{ + int w, h; + struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); + + switch (ev->window.event) { + case SDL_WINDOWEVENT_RESIZED: + sdl_scale(scon, ev->window.data1, ev->window.data2); + graphic_hw_invalidate(scon->dcl.con); + graphic_hw_update(scon->dcl.con); + break; + case SDL_WINDOWEVENT_EXPOSED: + SDL_GetWindowSize(SDL_GetWindowFromID(ev->window.windowID), &w, &h); + sdl_update(dcl, 0, 0, w, h); + break; + case SDL_WINDOWEVENT_FOCUS_GAINED: + case SDL_WINDOWEVENT_ENTER: + if (!gui_grab && (qemu_input_is_absolute() || absolute_enabled)) { + absolute_mouse_grab(scon); + } + break; + case SDL_WINDOWEVENT_FOCUS_LOST: + if (gui_grab && !gui_fullscreen) { + sdl_grab_end(scon); + } + break; + case SDL_WINDOWEVENT_RESTORED: + update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT); + break; + case SDL_WINDOWEVENT_MINIMIZED: + update_displaychangelistener(dcl, 500); + break; + case SDL_WINDOWEVENT_CLOSE: + if (!no_quit) { + no_shutdown = 0; + qemu_system_shutdown_request(); + } + break; + } +} + +static void sdl_refresh(DisplayChangeListener *dcl) +{ + struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); + SDL_Event ev1, *ev = &ev1; + + if (scon->last_vm_running != runstate_is_running()) { + scon->last_vm_running = runstate_is_running(); + sdl_update_caption(scon); + } + + graphic_hw_update(dcl->con); + + while (SDL_PollEvent(ev)) { + switch (ev->type) { + case SDL_KEYDOWN: + handle_keydown(ev); + break; + case SDL_KEYUP: + handle_keyup(ev); + break; + case SDL_QUIT: + if (!no_quit) { + no_shutdown = 0; + qemu_system_shutdown_request(); + } + break; + case SDL_MOUSEMOTION: + handle_mousemotion(ev); + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + handle_mousebutton(ev); + break; + case SDL_WINDOWEVENT: + handle_windowevent(dcl, ev); + break; + default: + break; + } + } +} + +static void sdl_mouse_warp(DisplayChangeListener *dcl, + int x, int y, int on) +{ + struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); + if (on) { + if (!guest_cursor) { + sdl_show_cursor(); + } + if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { + SDL_SetCursor(guest_sprite); + if (!qemu_input_is_absolute() && !absolute_enabled) { + SDL_WarpMouseInWindow(scon->real_window, x, y); + } + } + } else if (gui_grab) { + sdl_hide_cursor(); + } + guest_cursor = on; + guest_x = x, guest_y = y; +} + +static void sdl_mouse_define(DisplayChangeListener *dcl, + QEMUCursor *c) +{ + + if (guest_sprite) { + SDL_FreeCursor(guest_sprite); + } + + if (guest_sprite_surface) { + SDL_FreeSurface(guest_sprite_surface); + } + + guest_sprite_surface = + SDL_CreateRGBSurfaceFrom(c->data, c->width, c->height, 32, c->width * 4, + 0xff0000, 0x00ff00, 0xff, 0xff000000); + + if (!guest_sprite_surface) { + fprintf(stderr, "Failed to make rgb surface from %p\n", c); + return; + } + guest_sprite = SDL_CreateColorCursor(guest_sprite_surface, + c->hot_x, c->hot_y); + if (!guest_sprite) { + fprintf(stderr, "Failed to make color cursor from %p\n", c); + return; + } + if (guest_cursor && + (gui_grab || qemu_input_is_absolute() || absolute_enabled)) { + SDL_SetCursor(guest_sprite); + } +} + +static void sdl_cleanup(void) +{ + if (guest_sprite) { + SDL_FreeCursor(guest_sprite); + } + SDL_QuitSubSystem(SDL_INIT_VIDEO); +} + +static const DisplayChangeListenerOps dcl_ops = { + .dpy_name = "sdl", + .dpy_gfx_update = sdl_update, + .dpy_gfx_switch = sdl_switch, + .dpy_refresh = sdl_refresh, + .dpy_mouse_set = sdl_mouse_warp, + .dpy_cursor_define = sdl_mouse_define, +}; + +void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) +{ + int flags; + uint8_t data = 0; + char *filename; + int i; + + if (no_frame) { + gui_noframe = 1; + } + +#ifdef __linux__ + /* on Linux, SDL may use fbcon|directfb|svgalib when run without + * accessible $DISPLAY to open X11 window. This is often the case + * when qemu is run using sudo. But in this case, and when actually + * run in X11 environment, SDL fights with X11 for the video card, + * making current display unavailable, often until reboot. + * So make x11 the default SDL video driver if this variable is unset. + * This is a bit hackish but saves us from bigger problem. + * Maybe it's a good idea to fix this in SDL instead. + */ + setenv("SDL_VIDEODRIVER", "x11", 0); +#endif + + flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE; + if (SDL_Init(flags)) { + fprintf(stderr, "Could not initialize SDL(%s) - exiting\n", + SDL_GetError()); + exit(1); + } + + for (i = 0;; i++) { + QemuConsole *con = qemu_console_lookup_by_index(i); + if (!con || !qemu_console_is_graphic(con)) { + break; + } + } + sdl2_num_outputs = i; + sdl2_console = g_new0(struct sdl2_state, sdl2_num_outputs); + for (i = 0; i < sdl2_num_outputs; i++) { + QemuConsole *con = qemu_console_lookup_by_index(i); + sdl2_console[i].dcl.ops = &dcl_ops; + sdl2_console[i].dcl.con = con; + register_displaychangelistener(&sdl2_console[i].dcl); + sdl2_console[i].idx = i; + } + + /* Load a 32x32x4 image. White pixels are transparent. */ + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp"); + if (filename) { + SDL_Surface *image = SDL_LoadBMP(filename); + if (image) { + uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255); + SDL_SetColorKey(image, SDL_TRUE, colorkey); + SDL_SetWindowIcon(sdl2_console[0].real_window, image); + } + g_free(filename); + } + + if (full_screen) { + gui_fullscreen = 1; + sdl_grab_start(0); + } + + mouse_mode_notifier.notify = sdl_mouse_mode_change; + qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier); + + gui_grab = 0; + + sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0); + sdl_cursor_normal = SDL_GetCursor(); + + atexit(sdl_cleanup); +} +#endif diff --git a/ui/sdl_keysym.h b/ui/sdl_keysym.h index ee904805da..599d9fc64d 100644 --- a/ui/sdl_keysym.h +++ b/ui/sdl_keysym.h @@ -200,6 +200,7 @@ static const name2keysym_t name2keysym[]={ { "yacute", 0x0fd}, { "thorn", 0x0fe}, { "ydiaeresis", 0x0ff}, +#if SDL_MAJOR_VERSION == 1 {"EuroSign", SDLK_EURO}, /* modifiers */ @@ -272,6 +273,6 @@ static const name2keysym_t name2keysym[]={ {"Num_Lock", SDLK_NUMLOCK}, {"Pause", SDLK_PAUSE}, {"Escape", SDLK_ESCAPE}, - +#endif {NULL, 0}, }; diff --git a/ui/spice-input.c b/ui/spice-input.c index 3beb8deadb..6dab23b75c 100644 --- a/ui/spice-input.c +++ b/ui/spice-input.c @@ -26,12 +26,15 @@ #include "qemu-common.h" #include "ui/qemu-spice.h" #include "ui/console.h" +#include "ui/keymaps.h" +#include "ui/input.h" /* keyboard bits */ typedef struct QemuSpiceKbd { SpiceKbdInstance sin; int ledstate; + bool emul0; } QemuSpiceKbd; static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag); @@ -47,9 +50,24 @@ static const SpiceKbdInterface kbd_interface = { .get_leds = kbd_get_leds, }; -static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag) +static void kbd_push_key(SpiceKbdInstance *sin, uint8_t scancode) { - kbd_put_keycode(frag); + QemuSpiceKbd *kbd = container_of(sin, QemuSpiceKbd, sin); + int keycode; + bool up; + + if (scancode == SCANCODE_EMUL0) { + kbd->emul0 = true; + return; + } + keycode = scancode & ~SCANCODE_UP; + up = scancode & SCANCODE_UP; + if (kbd->emul0) { + kbd->emul0 = false; + keycode |= SCANCODE_GREY; + } + + qemu_input_event_send_key_number(NULL, keycode, !up); } static uint8_t kbd_get_leds(SpiceKbdInstance *sin) @@ -80,41 +98,52 @@ static void kbd_leds(void *opaque, int ledstate) typedef struct QemuSpicePointer { SpiceMouseInstance mouse; SpiceTabletInstance tablet; - int width, height, x, y; + int width, height; + uint32_t last_bmask; Notifier mouse_mode; bool absolute; } QemuSpicePointer; -static int map_buttons(int spice_buttons) +static void spice_update_buttons(QemuSpicePointer *pointer, + int wheel, uint32_t button_mask) { - int qemu_buttons = 0; - - /* - * Note: SPICE_MOUSE_BUTTON_* specifies the wire protocol but this - * isn't what we get passed in via interface callbacks for the - * middle and right button ... - */ - if (spice_buttons & SPICE_MOUSE_BUTTON_MASK_LEFT) { - qemu_buttons |= MOUSE_EVENT_LBUTTON; + static uint32_t bmap[INPUT_BUTTON_MAX] = { + [INPUT_BUTTON_LEFT] = 0x01, + [INPUT_BUTTON_MIDDLE] = 0x04, + [INPUT_BUTTON_RIGHT] = 0x02, + [INPUT_BUTTON_WHEEL_UP] = 0x10, + [INPUT_BUTTON_WHEEL_DOWN] = 0x20, + }; + + if (wheel < 0) { + button_mask |= 0x10; } - if (spice_buttons & 0x04 /* SPICE_MOUSE_BUTTON_MASK_MIDDLE */) { - qemu_buttons |= MOUSE_EVENT_MBUTTON; + if (wheel > 0) { + button_mask |= 0x20; } - if (spice_buttons & 0x02 /* SPICE_MOUSE_BUTTON_MASK_RIGHT */) { - qemu_buttons |= MOUSE_EVENT_RBUTTON; + + if (pointer->last_bmask == button_mask) { + return; } - return qemu_buttons; + qemu_input_update_buttons(NULL, bmap, pointer->last_bmask, button_mask); + pointer->last_bmask = button_mask; } static void mouse_motion(SpiceMouseInstance *sin, int dx, int dy, int dz, uint32_t buttons_state) { - kbd_mouse_event(dx, dy, dz, map_buttons(buttons_state)); + QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, mouse); + spice_update_buttons(pointer, dz, buttons_state); + qemu_input_queue_rel(NULL, INPUT_AXIS_X, dx); + qemu_input_queue_rel(NULL, INPUT_AXIS_Y, dy); + qemu_input_event_sync(); } static void mouse_buttons(SpiceMouseInstance *sin, uint32_t buttons_state) { - kbd_mouse_event(0, 0, 0, map_buttons(buttons_state)); + QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, mouse); + spice_update_buttons(pointer, 0, buttons_state); + qemu_input_event_sync(); } static const SpiceMouseInterface mouse_interface = { @@ -145,9 +174,10 @@ static void tablet_position(SpiceTabletInstance* sin, int x, int y, { QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); - pointer->x = x * 0x7FFF / (pointer->width - 1); - pointer->y = y * 0x7FFF / (pointer->height - 1); - kbd_mouse_event(pointer->x, pointer->y, 0, map_buttons(buttons_state)); + spice_update_buttons(pointer, 0, buttons_state); + qemu_input_queue_abs(NULL, INPUT_AXIS_X, x, pointer->width); + qemu_input_queue_abs(NULL, INPUT_AXIS_Y, y, pointer->width); + qemu_input_event_sync(); } @@ -156,7 +186,8 @@ static void tablet_wheel(SpiceTabletInstance* sin, int wheel, { QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); - kbd_mouse_event(pointer->x, pointer->y, wheel, map_buttons(buttons_state)); + spice_update_buttons(pointer, wheel, buttons_state); + qemu_input_event_sync(); } static void tablet_buttons(SpiceTabletInstance *sin, @@ -164,7 +195,8 @@ static void tablet_buttons(SpiceTabletInstance *sin, { QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); - kbd_mouse_event(pointer->x, pointer->y, 0, map_buttons(buttons_state)); + spice_update_buttons(pointer, 0, buttons_state); + qemu_input_event_sync(); } static const SpiceTabletInterface tablet_interface = { @@ -181,7 +213,7 @@ static const SpiceTabletInterface tablet_interface = { static void mouse_mode_notifier(Notifier *notifier, void *data) { QemuSpicePointer *pointer = container_of(notifier, QemuSpicePointer, mouse_mode); - bool is_absolute = kbd_mouse_is_absolute(); + bool is_absolute = qemu_input_is_absolute(); if (pointer->absolute == is_absolute) { return; diff --git a/ui/vnc.c b/ui/vnc.c index 5601cc34ef..7dfc94a358 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -33,6 +33,7 @@ #include "qapi/qmp/types.h" #include "qmp-commands.h" #include "qemu/osdep.h" +#include "ui/input.h" #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT #define VNC_REFRESH_INTERVAL_INC 50 @@ -1483,7 +1484,7 @@ static void client_cut_text(VncState *vs, size_t len, uint8_t *text) static void check_pointer_type_change(Notifier *notifier, void *data) { VncState *vs = container_of(notifier, VncState, mouse_mode_notifier); - int absolute = kbd_mouse_is_absolute(); + int absolute = qemu_input_is_absolute(); if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) { vnc_lock_output(vs); @@ -1502,39 +1503,37 @@ static void check_pointer_type_change(Notifier *notifier, void *data) static void pointer_event(VncState *vs, int button_mask, int x, int y) { - int buttons = 0; - int dz = 0; + static uint32_t bmap[INPUT_BUTTON_MAX] = { + [INPUT_BUTTON_LEFT] = 0x01, + [INPUT_BUTTON_MIDDLE] = 0x02, + [INPUT_BUTTON_RIGHT] = 0x04, + [INPUT_BUTTON_WHEEL_UP] = 0x08, + [INPUT_BUTTON_WHEEL_DOWN] = 0x10, + }; + QemuConsole *con = vs->vd->dcl.con; int width = surface_width(vs->vd->ds); int height = surface_height(vs->vd->ds); - if (button_mask & 0x01) - buttons |= MOUSE_EVENT_LBUTTON; - if (button_mask & 0x02) - buttons |= MOUSE_EVENT_MBUTTON; - if (button_mask & 0x04) - buttons |= MOUSE_EVENT_RBUTTON; - if (button_mask & 0x08) - dz = -1; - if (button_mask & 0x10) - dz = 1; + if (vs->last_bmask != button_mask) { + qemu_input_update_buttons(con, bmap, vs->last_bmask, button_mask); + vs->last_bmask = button_mask; + } if (vs->absolute) { - kbd_mouse_event(width > 1 ? x * 0x7FFF / (width - 1) : 0x4000, - height > 1 ? y * 0x7FFF / (height - 1) : 0x4000, - dz, buttons); + qemu_input_queue_abs(con, INPUT_AXIS_X, x, width); + qemu_input_queue_abs(con, INPUT_AXIS_Y, y, height); } else if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE)) { - x -= 0x7FFF; - y -= 0x7FFF; - - kbd_mouse_event(x, y, dz, buttons); + qemu_input_queue_rel(con, INPUT_AXIS_X, x - 0x7FFF); + qemu_input_queue_rel(con, INPUT_AXIS_Y, y - 0x7FFF); } else { - if (vs->last_x != -1) - kbd_mouse_event(x - vs->last_x, - y - vs->last_y, - dz, buttons); + if (vs->last_x != -1) { + qemu_input_queue_rel(con, INPUT_AXIS_X, x - vs->last_x); + qemu_input_queue_rel(con, INPUT_AXIS_Y, y - vs->last_y); + } vs->last_x = x; vs->last_y = y; } + qemu_input_event_sync(); } static void reset_keys(VncState *vs) @@ -1542,9 +1541,7 @@ static void reset_keys(VncState *vs) int i; for(i = 0; i < 256; i++) { if (vs->modifiers_state[i]) { - if (i & SCANCODE_GREY) - kbd_put_keycode(SCANCODE_EMUL0); - kbd_put_keycode(i | SCANCODE_UP); + qemu_input_event_send_key_number(vs->vd->dcl.con, i, false); vs->modifiers_state[i] = 0; } } @@ -1553,12 +1550,8 @@ static void reset_keys(VncState *vs) static void press_key(VncState *vs, int keysym) { int keycode = keysym2scancode(vs->vd->kbd_layout, keysym) & SCANCODE_KEYMASK; - if (keycode & SCANCODE_GREY) - kbd_put_keycode(SCANCODE_EMUL0); - kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK); - if (keycode & SCANCODE_GREY) - kbd_put_keycode(SCANCODE_EMUL0); - kbd_put_keycode(keycode | SCANCODE_UP); + qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, true); + qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false); } static int current_led_state(VncState *vs) @@ -1700,12 +1693,7 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) } if (qemu_console_is_graphic(NULL)) { - if (keycode & SCANCODE_GREY) - kbd_put_keycode(SCANCODE_EMUL0); - if (down) - kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK); - else - kbd_put_keycode(keycode | SCANCODE_UP); + qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, down); } else { bool numlock = vs->modifiers_state[0x45]; bool control = (vs->modifiers_state[0x1d] || @@ -1826,10 +1814,7 @@ static void vnc_release_modifiers(VncState *vs) if (!vs->modifiers_state[keycode]) { continue; } - if (keycode & SCANCODE_GREY) { - kbd_put_keycode(SCANCODE_EMUL0); - } - kbd_put_keycode(keycode | SCANCODE_UP); + qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false); } } diff --git a/ui/vnc.h b/ui/vnc.h index 6e9921387f..e63c14284b 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -257,6 +257,7 @@ struct VncState int absolute; int last_x; int last_y; + uint32_t last_bmask; int client_width; int client_height; VncShareMode share_mode; diff --git a/util/host-utils.c b/util/host-utils.c index f0784d6335..ee57ef55f6 100644 --- a/util/host-utils.c +++ b/util/host-utils.c @@ -86,4 +86,79 @@ void muls64 (uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b) } *phigh = rh; } + +/* Unsigned 128x64 division. Returns 1 if overflow (divide by zero or */ +/* quotient exceeds 64 bits). Otherwise returns quotient via plow and */ +/* remainder via phigh. */ +int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor) +{ + uint64_t dhi = *phigh; + uint64_t dlo = *plow; + unsigned i; + uint64_t carry = 0; + + if (divisor == 0) { + return 1; + } else if (dhi == 0) { + *plow = dlo / divisor; + *phigh = dlo % divisor; + return 0; + } else if (dhi > divisor) { + return 1; + } else { + + for (i = 0; i < 64; i++) { + carry = dhi >> 63; + dhi = (dhi << 1) | (dlo >> 63); + if (carry || (dhi >= divisor)) { + dhi -= divisor; + carry = 1; + } else { + carry = 0; + } + dlo = (dlo << 1) | carry; + } + + *plow = dlo; + *phigh = dhi; + return 0; + } +} + +int divs128(int64_t *plow, int64_t *phigh, int64_t divisor) +{ + int sgn_dvdnd = *phigh < 0; + int sgn_divsr = divisor < 0; + int overflow = 0; + + if (sgn_dvdnd) { + *plow = ~(*plow); + *phigh = ~(*phigh); + if (*plow == (int64_t)-1) { + *plow = 0; + (*phigh)++; + } else { + (*plow)++; + } + } + + if (sgn_divsr) { + divisor = 0 - divisor; + } + + overflow = divu128((uint64_t *)plow, (uint64_t *)phigh, (uint64_t)divisor); + + if (sgn_dvdnd ^ sgn_divsr) { + *plow = 0 - *plow; + } + + if (!overflow) { + if ((*plow < 0) ^ (sgn_dvdnd ^ sgn_divsr)) { + overflow = 1; + } + } + + return overflow; +} + #endif /* !CONFIG_INT128 */ diff --git a/util/iov.c b/util/iov.c index 03934da74d..6569b5aae1 100644 --- a/util/iov.c +++ b/util/iov.c @@ -17,14 +17,7 @@ */ #include "qemu/iov.h" - -#ifdef _WIN32 -# include <windows.h> -# include <winsock2.h> -#else -# include <sys/types.h> -# include <sys/socket.h> -#endif +#include "qemu/sockets.h" size_t iov_from_buf(const struct iovec *iov, unsigned int iov_cnt, size_t offset, const void *buf, size_t bytes) diff --git a/util/module.c b/util/module.c index dc08c16111..863a8a32a3 100644 --- a/util/module.c +++ b/util/module.c @@ -14,10 +14,10 @@ */ #include <stdlib.h> +#include "qemu-common.h" #ifdef CONFIG_MODULES #include <gmodule.h> #endif -#include "qemu-common.h" #include "qemu/queue.h" #include "qemu/module.h" diff --git a/vl.c b/vl.c index 1d27b34301..41581c1c23 100644 --- a/vl.c +++ b/vl.c @@ -38,10 +38,6 @@ #include <libvdeplug.h> #endif -#ifdef _WIN32 -#include <windows.h> -#endif - #ifdef CONFIG_SDL #if defined(__APPLE__) || defined(main) #include <SDL.h> @@ -378,6 +374,10 @@ static QemuOptsList qemu_machine_opts = { .name = "firmware", .type = QEMU_OPT_STRING, .help = "firmware image", + },{ + .name = "kvm-type", + .type = QEMU_OPT_STRING, + .help = "Specifies the KVM virtualization mode (HV, PR)", }, { /* End of list */ } }, @@ -2031,6 +2031,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; @@ -2066,6 +2076,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); @@ -2558,7 +2582,7 @@ static QEMUMachine *machine_parse(const char *name) exit(!name || !is_help_option(name)); } -static int tcg_init(void) +static int tcg_init(QEMUMachine *machine) { tcg_exec_init(tcg_tb_size * 1024 * 1024); return 0; @@ -2568,7 +2592,7 @@ static struct { const char *opt_name; const char *name; int (*available)(void); - int (*init)(void); + int (*init)(QEMUMachine *); bool *allowed; } accel_list[] = { { "tcg", "tcg", tcg_available, tcg_init, &tcg_allowed }, @@ -2577,7 +2601,7 @@ static struct { { "qtest", "QTest", qtest_available, qtest_init_accel, &qtest_allowed }, }; -static int configure_accelerator(void) +static int configure_accelerator(QEMUMachine *machine) { const char *p; char buf[10]; @@ -2604,7 +2628,7 @@ static int configure_accelerator(void) continue; } *(accel_list[i].allowed) = true; - ret = accel_list[i].init(); + ret = accel_list[i].init(machine); if (ret < 0) { init_failed = true; fprintf(stderr, "failed to initialize %s: %s\n", @@ -4033,7 +4057,7 @@ int main(int argc, char **argv, char **envp) exit(0); } - configure_accelerator(); + configure_accelerator(machine); if (qtest_chrdev) { Error *local_err = NULL; diff --git a/vmstate.c b/vmstate.c index 284b080f46..d1f5eb0e6a 100644 --- a/vmstate.c +++ b/vmstate.c @@ -321,23 +321,24 @@ const VMStateInfo vmstate_info_int32_equal = { .put = put_int32, }; -/* 32 bit int. See that the received value is the less or the same - than the one in the field */ +/* 32 bit int. Check that the received value is less than or equal to + the one in the field */ static int get_int32_le(QEMUFile *f, void *pv, size_t size) { - int32_t *old = pv; - int32_t new; - qemu_get_sbe32s(f, &new); + int32_t *cur = pv; + int32_t loaded; + qemu_get_sbe32s(f, &loaded); - if (*old <= new) { + if (loaded <= *cur) { + *cur = loaded; return 0; } return -EINVAL; } const VMStateInfo vmstate_info_int32_le = { - .name = "int32 equal", + .name = "int32 le", .get = get_int32_le, .put = put_int32, }; diff --git a/xen-all.c b/xen-all.c index 4a594bdd9b..ba3473901e 100644 --- a/xen-all.c +++ b/xen-all.c @@ -1001,7 +1001,7 @@ static void xen_exit_notifier(Notifier *n, void *data) xs_daemon_close(state->xenstore); } -int xen_init(void) +int xen_init(QEMUMachine *machine) { xen_xc = xen_xc_interface_open(0, 0, 0); if (xen_xc == XC_HANDLER_INITIAL_VALUE) { diff --git a/xen-stub.c b/xen-stub.c index ad189a6df8..59927cb5d6 100644 --- a/xen-stub.c +++ b/xen-stub.c @@ -47,7 +47,7 @@ qemu_irq *xen_interrupt_controller_init(void) return NULL; } -int xen_init(void) +int xen_init(QEMUMachine *machine) { return -ENOSYS; } |