diff options
175 files changed, 10959 insertions, 1713 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 94366ef011..bcb69e80d2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -583,12 +583,13 @@ Xtensa Machines sim M: Max Filippov <jcmvbkbc@gmail.com> S: Maintained -F: hw/xtensa/xtensa_sim.c +F: hw/xtensa/sim.c -Avnet LX60 +XTFPGA (LX60, LX200, ML605, KC705) M: Max Filippov <jcmvbkbc@gmail.com> S: Maintained -F: hw/xtensa/xtensa_lx60.c +F: hw/xtensa/xtfpga.c +F: hw/net/opencores_eth.c Devices ------- @@ -879,6 +880,12 @@ S: Maintained F: qobject/ T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp +QEMU Guest Agent +M: Michael Roth <mdroth@linux.vnet.ibm.com> +S: Maintained +F: qga/ +T: git git://github.com/mdroth/qemu.git qga + QOM M: Anthony Liguori <aliguori@amazon.com> M: Andreas Färber <afaerber@suse.de> @@ -919,6 +926,15 @@ M: Blue Swirl <blauwirbel@gmail.com> S: Odd Fixes F: scripts/checkpatch.pl +Migration +M: Juan Quintela <quintela@redhat.com> +S: Maintained +F: include/migration/ +F: migration* +F: savevm.c +F: arch_init.c +F: vmstate.c + Seccomp M: Eduardo Otubo <eduardo.otubo@profitbricks.com> S: Supported @@ -1077,3 +1093,8 @@ M: Chrysostomos Nanakos <cnanakos@grnet.gr> M: Chrysostomos Nanakos <chris@include.gr> S: Maintained F: block/archipelago.c + +Bootdevice +M: Gonglei <arei.gonglei@huawei.com> +S: Maintained +F: bootdevice.c @@ -1 +1 @@ -2.1.50 +2.1.90 @@ -519,6 +519,7 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) return; } bs->bl.opt_transfer_length = bs->file->bl.opt_transfer_length; + bs->bl.max_transfer_length = bs->file->bl.max_transfer_length; bs->bl.opt_mem_alignment = bs->file->bl.opt_mem_alignment; } else { bs->bl.opt_mem_alignment = 512; @@ -533,6 +534,9 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) bs->bl.opt_transfer_length = MAX(bs->bl.opt_transfer_length, bs->backing_hd->bl.opt_transfer_length); + bs->bl.max_transfer_length = + MIN_NON_ZERO(bs->bl.max_transfer_length, + bs->backing_hd->bl.max_transfer_length); bs->bl.opt_mem_alignment = MAX(bs->bl.opt_mem_alignment, bs->backing_hd->bl.opt_mem_alignment); @@ -1900,6 +1904,34 @@ static bool bdrv_requests_pending(BlockDriverState *bs) return false; } +static bool bdrv_drain_one(BlockDriverState *bs) +{ + bool bs_busy; + + bdrv_flush_io_queue(bs); + bdrv_start_throttled_reqs(bs); + bs_busy = bdrv_requests_pending(bs); + bs_busy |= aio_poll(bdrv_get_aio_context(bs), bs_busy); + return bs_busy; +} + +/* + * Wait for pending requests to complete on a single BlockDriverState subtree + * + * See the warning in bdrv_drain_all(). This function can only be called if + * you are sure nothing can generate I/O because you have op blockers + * installed. + * + * Note that unlike bdrv_drain_all(), the caller must hold the BlockDriverState + * AioContext. + */ +void bdrv_drain(BlockDriverState *bs) +{ + while (bdrv_drain_one(bs)) { + /* Keep iterating */ + } +} + /* * Wait for pending requests to complete across all BlockDriverStates * @@ -1923,16 +1955,10 @@ void bdrv_drain_all(void) QTAILQ_FOREACH(bs, &bdrv_states, device_list) { AioContext *aio_context = bdrv_get_aio_context(bs); - bool bs_busy; aio_context_acquire(aio_context); - bdrv_flush_io_queue(bs); - bdrv_start_throttled_reqs(bs); - bs_busy = bdrv_requests_pending(bs); - bs_busy |= aio_poll(aio_context, bs_busy); + busy |= bdrv_drain_one(bs); aio_context_release(aio_context); - - busy |= bs_busy; } } } @@ -3540,10 +3566,10 @@ static void send_qmp_error_event(BlockDriverState *bs, BlockErrorAction action, bool is_read, int error) { - BlockErrorAction ac; + IoOperationType optype; - ac = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE; - qapi_event_send_block_io_error(bdrv_get_device_name(bs), ac, action, + optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE; + qapi_event_send_block_io_error(bdrv_get_device_name(bs), optype, action, bdrv_iostatus_is_enabled(bs), error == ENOSPC, strerror(error), &error_abort); @@ -4442,6 +4468,11 @@ static int multiwrite_merge(BlockDriverState *bs, BlockRequest *reqs, merge = 0; } + if (bs->bl.max_transfer_length && reqs[outidx].nb_sectors + + reqs[i].nb_sectors > bs->bl.max_transfer_length) { + merge = 0; + } + if (merge) { size_t size; QEMUIOVector *qiov = g_malloc0(sizeof(*qiov)); @@ -5577,11 +5608,6 @@ void bdrv_img_create(const char *filename, const char *fmt, ret = bdrv_open(&bs, backing_file, NULL, NULL, back_flags, backing_drv, &local_err); if (ret < 0) { - error_setg_errno(errp, -ret, "Could not open '%s': %s", - backing_file, - error_get_pretty(local_err)); - error_free(local_err); - local_err = NULL; goto out; } size = bdrv_getlength(bs); @@ -5750,12 +5776,13 @@ void bdrv_add_before_write_notifier(BlockDriverState *bs, notifier_with_return_list_add(&bs->before_write_notifiers, notifier); } -int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts) +int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts, + BlockDriverAmendStatusCB *status_cb) { if (!bs->drv->bdrv_amend_options) { return -ENOTSUP; } - return bs->drv->bdrv_amend_options(bs, opts); + return bs->drv->bdrv_amend_options(bs, opts, status_cb); } /* This function will be called by the bdrv_recurse_is_first_non_filter method @@ -5818,13 +5845,19 @@ bool bdrv_is_first_non_filter(BlockDriverState *candidate) BlockDriverState *check_to_replace_node(const char *node_name, Error **errp) { BlockDriverState *to_replace_bs = bdrv_find_node(node_name); + AioContext *aio_context; + if (!to_replace_bs) { error_setg(errp, "Node name '%s' not found", node_name); return NULL; } + aio_context = bdrv_get_aio_context(to_replace_bs); + aio_context_acquire(aio_context); + if (bdrv_op_is_blocked(to_replace_bs, BLOCK_OP_TYPE_REPLACE, errp)) { - return NULL; + to_replace_bs = NULL; + goto out; } /* We don't want arbitrary node of the BDS chain to be replaced only the top @@ -5834,9 +5867,12 @@ BlockDriverState *check_to_replace_node(const char *node_name, Error **errp) */ if (!bdrv_is_first_non_filter(to_replace_bs)) { error_setg(errp, "Only top most non filter can be replaced"); - return NULL; + to_replace_bs = NULL; + goto out; } +out: + aio_context_release(aio_context); return to_replace_bs; } diff --git a/block/Makefile.objs b/block/Makefile.objs index 27911b6b88..04b0e43eb1 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -9,7 +9,7 @@ block-obj-y += block-backend.o snapshot.o qapi.o block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o block-obj-$(CONFIG_POSIX) += raw-posix.o block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o -block-obj-y += null.o +block-obj-y += null.o mirror.o block-obj-y += nbd.o nbd-client.o sheepdog.o block-obj-$(CONFIG_LIBISCSI) += iscsi.o @@ -23,7 +23,6 @@ block-obj-y += accounting.o common-obj-y += stream.o common-obj-y += commit.o -common-obj-y += mirror.o common-obj-y += backup.o iscsi.o-cflags := $(LIBISCSI_CFLAGS) diff --git a/block/backup.c b/block/backup.c index e334740161..792e65514b 100644 --- a/block/backup.c +++ b/block/backup.c @@ -227,9 +227,25 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job, } } +typedef struct { + int ret; +} BackupCompleteData; + +static void backup_complete(BlockJob *job, void *opaque) +{ + BackupBlockJob *s = container_of(job, BackupBlockJob, common); + BackupCompleteData *data = opaque; + + bdrv_unref(s->target); + + block_job_completed(job, data->ret); + g_free(data); +} + static void coroutine_fn backup_run(void *opaque) { BackupBlockJob *job = opaque; + BackupCompleteData *data; BlockDriverState *bs = job->common.bs; BlockDriverState *target = job->target; BlockdevOnError on_target_error = job->on_target_error; @@ -344,9 +360,10 @@ static void coroutine_fn backup_run(void *opaque) hbitmap_free(job->bitmap); bdrv_iostatus_disable(target); - bdrv_unref(target); - block_job_completed(&job->common, ret); + data = g_malloc(sizeof(*data)); + data->ret = ret; + block_job_defer_to_main_loop(&job->common, backup_complete, data); } void backup_start(BlockDriverState *bs, BlockDriverState *target, diff --git a/block/blkdebug.c b/block/blkdebug.c index e046b920fb..862d93b59b 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -195,6 +195,8 @@ static const char *event_names[BLKDBG_EVENT_MAX] = { [BLKDBG_PWRITEV] = "pwritev", [BLKDBG_PWRITEV_ZERO] = "pwritev_zero", [BLKDBG_PWRITEV_DONE] = "pwritev_done", + + [BLKDBG_EMPTY_IMAGE_PREPARE] = "empty_image_prepare", }; static int get_event_by_name(const char *name, BlkDebugEvent *event) diff --git a/block/commit.c b/block/commit.c index 60a2accf04..cfa2bbebc2 100644 --- a/block/commit.c +++ b/block/commit.c @@ -60,17 +60,50 @@ static int coroutine_fn commit_populate(BlockDriverState *bs, return 0; } -static void coroutine_fn commit_run(void *opaque) +typedef struct { + int ret; +} CommitCompleteData; + +static void commit_complete(BlockJob *job, void *opaque) { - CommitBlockJob *s = opaque; + CommitBlockJob *s = container_of(job, CommitBlockJob, common); + CommitCompleteData *data = opaque; BlockDriverState *active = s->active; BlockDriverState *top = s->top; BlockDriverState *base = s->base; BlockDriverState *overlay_bs; + int ret = data->ret; + + if (!block_job_is_cancelled(&s->common) && ret == 0) { + /* success */ + ret = bdrv_drop_intermediate(active, top, base, s->backing_file_str); + } + + /* restore base open flags here if appropriate (e.g., change the base back + * to r/o). These reopens do not need to be atomic, since we won't abort + * even on failure here */ + if (s->base_flags != bdrv_get_flags(base)) { + bdrv_reopen(base, s->base_flags, NULL); + } + overlay_bs = bdrv_find_overlay(active, top); + if (overlay_bs && s->orig_overlay_flags != bdrv_get_flags(overlay_bs)) { + bdrv_reopen(overlay_bs, s->orig_overlay_flags, NULL); + } + g_free(s->backing_file_str); + block_job_completed(&s->common, ret); + g_free(data); +} + +static void coroutine_fn commit_run(void *opaque) +{ + CommitBlockJob *s = opaque; + CommitCompleteData *data; + BlockDriverState *top = s->top; + BlockDriverState *base = s->base; int64_t sector_num, end; int ret = 0; int n = 0; - void *buf; + void *buf = NULL; int bytes_written = 0; int64_t base_len; @@ -78,18 +111,18 @@ static void coroutine_fn commit_run(void *opaque) if (s->common.len < 0) { - goto exit_restore_reopen; + goto out; } ret = base_len = bdrv_getlength(base); if (base_len < 0) { - goto exit_restore_reopen; + goto out; } if (base_len < s->common.len) { ret = bdrv_truncate(base, s->common.len); if (ret) { - goto exit_restore_reopen; + goto out; } } @@ -128,7 +161,7 @@ wait: if (s->on_error == BLOCKDEV_ON_ERROR_STOP || s->on_error == BLOCKDEV_ON_ERROR_REPORT|| (s->on_error == BLOCKDEV_ON_ERROR_ENOSPC && ret == -ENOSPC)) { - goto exit_free_buf; + goto out; } else { n = 0; continue; @@ -140,27 +173,12 @@ wait: ret = 0; - if (!block_job_is_cancelled(&s->common) && sector_num == end) { - /* success */ - ret = bdrv_drop_intermediate(active, top, base, s->backing_file_str); - } - -exit_free_buf: +out: qemu_vfree(buf); -exit_restore_reopen: - /* restore base open flags here if appropriate (e.g., change the base back - * to r/o). These reopens do not need to be atomic, since we won't abort - * even on failure here */ - if (s->base_flags != bdrv_get_flags(base)) { - bdrv_reopen(base, s->base_flags, NULL); - } - overlay_bs = bdrv_find_overlay(active, top); - if (overlay_bs && s->orig_overlay_flags != bdrv_get_flags(overlay_bs)) { - bdrv_reopen(overlay_bs, s->orig_overlay_flags, NULL); - } - g_free(s->backing_file_str); - block_job_completed(&s->common, ret); + data = g_malloc(sizeof(*data)); + data->ret = ret; + block_job_defer_to_main_loop(&s->common, commit_complete, data); } static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp) diff --git a/block/curl.c b/block/curl.c index b4157cc8b3..bbee3ca179 100644 --- a/block/curl.c +++ b/block/curl.c @@ -64,6 +64,7 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle, #define SECTOR_SIZE 512 #define READ_AHEAD_DEFAULT (256 * 1024) #define CURL_TIMEOUT_DEFAULT 5 +#define CURL_TIMEOUT_MAX 10000 #define FIND_RET_NONE 0 #define FIND_RET_OK 1 @@ -112,7 +113,7 @@ typedef struct BDRVCURLState { char *url; size_t readahead_size; bool sslverify; - int timeout; + uint64_t timeout; char *cookie; bool accept_range; AioContext *aio_context; @@ -390,7 +391,7 @@ static CURLState *curl_init_state(BlockDriverState *bs, BDRVCURLState *s) if (s->cookie) { curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie); } - curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, s->timeout); + curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, (long)s->timeout); curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb); curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state); @@ -546,6 +547,10 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, s->timeout = qemu_opt_get_number(opts, CURL_BLOCK_OPT_TIMEOUT, CURL_TIMEOUT_DEFAULT); + if (s->timeout > CURL_TIMEOUT_MAX) { + error_setg(errp, "timeout parameter is too large or negative"); + goto out_noclean; + } s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true); diff --git a/block/iscsi.c b/block/iscsi.c index 3485d622ec..ed375fc30e 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -362,6 +362,12 @@ static int coroutine_fn iscsi_co_writev(BlockDriverState *bs, return -EINVAL; } + if (bs->bl.max_transfer_length && nb_sectors > bs->bl.max_transfer_length) { + error_report("iSCSI Error: Write of %d sectors exceeds max_xfer_len " + "of %d sectors", nb_sectors, bs->bl.max_transfer_length); + return -EINVAL; + } + lba = sector_qemu2lun(sector_num, iscsilun); num_sectors = sector_qemu2lun(nb_sectors, iscsilun); iscsi_co_init_iscsitask(iscsilun, &iTask); @@ -529,6 +535,12 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, return -EINVAL; } + if (bs->bl.max_transfer_length && nb_sectors > bs->bl.max_transfer_length) { + error_report("iSCSI Error: Read of %d sectors exceeds max_xfer_len " + "of %d sectors", nb_sectors, bs->bl.max_transfer_length); + return -EINVAL; + } + if (iscsilun->lbprz && nb_sectors >= ISCSI_CHECKALLOC_THRES && !iscsi_allocationmap_is_allocated(iscsilun, sector_num, nb_sectors)) { int64_t ret; @@ -1489,31 +1501,44 @@ static void iscsi_close(BlockDriverState *bs) memset(iscsilun, 0, sizeof(IscsiLun)); } -static void iscsi_refresh_limits(BlockDriverState *bs, Error **errp) +static int sector_limits_lun2qemu(int64_t sector, IscsiLun *iscsilun) { - IscsiLun *iscsilun = bs->opaque; + return MIN(sector_lun2qemu(sector, iscsilun), INT_MAX / 2 + 1); +} +static void iscsi_refresh_limits(BlockDriverState *bs, Error **errp) +{ /* We don't actually refresh here, but just return data queried in * iscsi_open(): iscsi targets don't change their limits. */ + + IscsiLun *iscsilun = bs->opaque; + uint32_t max_xfer_len = iscsilun->use_16_for_rw ? 0xffffffff : 0xffff; + + if (iscsilun->bl.max_xfer_len) { + max_xfer_len = MIN(max_xfer_len, iscsilun->bl.max_xfer_len); + } + + bs->bl.max_transfer_length = sector_limits_lun2qemu(max_xfer_len, iscsilun); + if (iscsilun->lbp.lbpu) { if (iscsilun->bl.max_unmap < 0xffffffff) { - bs->bl.max_discard = sector_lun2qemu(iscsilun->bl.max_unmap, - iscsilun); + bs->bl.max_discard = + sector_limits_lun2qemu(iscsilun->bl.max_unmap, iscsilun); } - bs->bl.discard_alignment = sector_lun2qemu(iscsilun->bl.opt_unmap_gran, - iscsilun); + bs->bl.discard_alignment = + sector_limits_lun2qemu(iscsilun->bl.opt_unmap_gran, iscsilun); } if (iscsilun->bl.max_ws_len < 0xffffffff) { - bs->bl.max_write_zeroes = sector_lun2qemu(iscsilun->bl.max_ws_len, - iscsilun); + bs->bl.max_write_zeroes = + sector_limits_lun2qemu(iscsilun->bl.max_ws_len, iscsilun); } if (iscsilun->lbp.lbpws) { - bs->bl.write_zeroes_alignment = sector_lun2qemu(iscsilun->bl.opt_unmap_gran, - iscsilun); + bs->bl.write_zeroes_alignment = + sector_limits_lun2qemu(iscsilun->bl.opt_unmap_gran, iscsilun); } - bs->bl.opt_transfer_length = sector_lun2qemu(iscsilun->bl.opt_xfer_len, - iscsilun); + bs->bl.opt_transfer_length = + sector_limits_lun2qemu(iscsilun->bl.opt_xfer_len, iscsilun); } /* Since iscsi_open() ignores bdrv_flags, there is nothing to do here in diff --git a/block/mirror.c b/block/mirror.c index e8a43eb39e..2c6dd2a4c1 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -45,6 +45,7 @@ typedef struct MirrorBlockJob { int64_t sector_num; int64_t granularity; size_t buf_size; + int64_t bdev_length; unsigned long *cow_bitmap; BdrvDirtyBitmap *dirty_bitmap; HBitmapIter hbi; @@ -54,6 +55,7 @@ typedef struct MirrorBlockJob { unsigned long *in_flight_bitmap; int in_flight; + int sectors_in_flight; int ret; } MirrorBlockJob; @@ -87,6 +89,7 @@ static void mirror_iteration_done(MirrorOp *op, int ret) trace_mirror_iteration_done(s, op->sector_num, op->nb_sectors, ret); s->in_flight--; + s->sectors_in_flight -= op->nb_sectors; iov = op->qiov.iov; for (i = 0; i < op->qiov.niov; i++) { MirrorBuffer *buf = (MirrorBuffer *) iov[i].iov_base; @@ -98,8 +101,11 @@ static void mirror_iteration_done(MirrorOp *op, int ret) chunk_num = op->sector_num / sectors_per_chunk; nb_chunks = op->nb_sectors / sectors_per_chunk; bitmap_clear(s->in_flight_bitmap, chunk_num, nb_chunks); - if (s->cow_bitmap && ret >= 0) { - bitmap_set(s->cow_bitmap, chunk_num, nb_chunks); + if (ret >= 0) { + if (s->cow_bitmap) { + bitmap_set(s->cow_bitmap, chunk_num, nb_chunks); + } + s->common.offset += (uint64_t)op->nb_sectors * BDRV_SECTOR_SIZE; } qemu_iovec_destroy(&op->qiov); @@ -172,7 +178,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) hbitmap_next_sector = s->sector_num; sector_num = s->sector_num; sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS; - end = s->common.len >> BDRV_SECTOR_BITS; + end = s->bdev_length / BDRV_SECTOR_SIZE; /* Extend the QEMUIOVector to include all adjacent blocks that will * be copied in this operation. @@ -284,6 +290,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) /* Copy the dirty cluster. */ s->in_flight++; + s->sectors_in_flight += nb_sectors; trace_mirror_one_iteration(s, sector_num, nb_sectors); bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors, mirror_read_complete, op); @@ -314,9 +321,56 @@ static void mirror_drain(MirrorBlockJob *s) } } +typedef struct { + int ret; +} MirrorExitData; + +static void mirror_exit(BlockJob *job, void *opaque) +{ + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + MirrorExitData *data = opaque; + AioContext *replace_aio_context = NULL; + + if (s->to_replace) { + replace_aio_context = bdrv_get_aio_context(s->to_replace); + aio_context_acquire(replace_aio_context); + } + + if (s->should_complete && data->ret == 0) { + BlockDriverState *to_replace = s->common.bs; + if (s->to_replace) { + to_replace = s->to_replace; + } + if (bdrv_get_flags(s->target) != bdrv_get_flags(to_replace)) { + bdrv_reopen(s->target, bdrv_get_flags(to_replace), NULL); + } + bdrv_swap(s->target, to_replace); + if (s->common.driver->job_type == BLOCK_JOB_TYPE_COMMIT) { + /* drop the bs loop chain formed by the swap: break the loop then + * trigger the unref from the top one */ + BlockDriverState *p = s->base->backing_hd; + bdrv_set_backing_hd(s->base, NULL); + bdrv_unref(p); + } + } + if (s->to_replace) { + bdrv_op_unblock_all(s->to_replace, s->replace_blocker); + error_free(s->replace_blocker); + bdrv_unref(s->to_replace); + } + if (replace_aio_context) { + aio_context_release(replace_aio_context); + } + g_free(s->replaces); + bdrv_unref(s->target); + block_job_completed(&s->common, data->ret); + g_free(data); +} + static void coroutine_fn mirror_run(void *opaque) { MirrorBlockJob *s = opaque; + MirrorExitData *data; BlockDriverState *bs = s->common.bs; int64_t sector_num, end, sectors_per_chunk, length; uint64_t last_pause_ns; @@ -329,11 +383,11 @@ static void coroutine_fn mirror_run(void *opaque) goto immediate_exit; } - s->common.len = bdrv_getlength(bs); - if (s->common.len < 0) { - ret = s->common.len; + s->bdev_length = bdrv_getlength(bs); + if (s->bdev_length < 0) { + ret = s->bdev_length; goto immediate_exit; - } else if (s->common.len == 0) { + } else if (s->bdev_length == 0) { /* Report BLOCK_JOB_READY and wait for complete. */ block_job_event_ready(&s->common); s->synced = true; @@ -344,7 +398,7 @@ static void coroutine_fn mirror_run(void *opaque) goto immediate_exit; } - length = DIV_ROUND_UP(s->common.len, s->granularity); + length = DIV_ROUND_UP(s->bdev_length, s->granularity); s->in_flight_bitmap = bitmap_new(length); /* If we have no backing file yet in the destination, we cannot let @@ -364,7 +418,7 @@ static void coroutine_fn mirror_run(void *opaque) } } - end = s->common.len >> BDRV_SECTOR_BITS; + end = s->bdev_length / BDRV_SECTOR_SIZE; s->buf = qemu_try_blockalign(bs, s->buf_size); if (s->buf == NULL) { ret = -ENOMEM; @@ -409,6 +463,12 @@ static void coroutine_fn mirror_run(void *opaque) } cnt = bdrv_get_dirty_count(bs, s->dirty_bitmap); + /* s->common.offset contains the number of bytes already processed so + * far, cnt is the number of dirty sectors remaining and + * s->sectors_in_flight is the number of sectors currently being + * processed; together those are the current total operation length */ + s->common.len = s->common.offset + + (cnt + s->sectors_in_flight) * BDRV_SECTOR_SIZE; /* Note that even when no rate limit is applied we need to yield * periodically with no pending I/O so that qemu_aio_flush() returns. @@ -445,7 +505,6 @@ static void coroutine_fn mirror_run(void *opaque) * report completion. This way, block-job-cancel will leave * the target in a consistent state. */ - s->common.offset = end * BDRV_SECTOR_SIZE; if (!s->synced) { block_job_event_ready(&s->common); s->synced = true; @@ -467,15 +526,13 @@ static void coroutine_fn mirror_run(void *opaque) * mirror_populate runs. */ trace_mirror_before_drain(s, cnt); - bdrv_drain_all(); + bdrv_drain(bs); cnt = bdrv_get_dirty_count(bs, s->dirty_bitmap); } ret = 0; trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); if (!s->synced) { - /* Publish progress */ - s->common.offset = (end - cnt) * BDRV_SECTOR_SIZE; block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, delay_ns); if (block_job_is_cancelled(&s->common)) { break; @@ -510,31 +567,10 @@ immediate_exit: g_free(s->in_flight_bitmap); bdrv_release_dirty_bitmap(bs, s->dirty_bitmap); bdrv_iostatus_disable(s->target); - if (s->should_complete && ret == 0) { - BlockDriverState *to_replace = s->common.bs; - if (s->to_replace) { - to_replace = s->to_replace; - } - if (bdrv_get_flags(s->target) != bdrv_get_flags(to_replace)) { - bdrv_reopen(s->target, bdrv_get_flags(to_replace), NULL); - } - bdrv_swap(s->target, to_replace); - if (s->common.driver->job_type == BLOCK_JOB_TYPE_COMMIT) { - /* drop the bs loop chain formed by the swap: break the loop then - * trigger the unref from the top one */ - BlockDriverState *p = s->base->backing_hd; - bdrv_set_backing_hd(s->base, NULL); - bdrv_unref(p); - } - } - if (s->to_replace) { - bdrv_op_unblock_all(s->to_replace, s->replace_blocker); - error_free(s->replace_blocker); - bdrv_unref(s->to_replace); - } - g_free(s->replaces); - bdrv_unref(s->target); - block_job_completed(&s->common, ret); + + data = g_malloc(sizeof(*data)); + data->ret = ret; + block_job_defer_to_main_loop(&s->common, mirror_exit, data); } static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp) @@ -574,16 +610,23 @@ static void mirror_complete(BlockJob *job, Error **errp) /* check the target bs is not blocked and block all operations on it */ if (s->replaces) { + AioContext *replace_aio_context; + s->to_replace = check_to_replace_node(s->replaces, &local_err); if (!s->to_replace) { error_propagate(errp, local_err); return; } + replace_aio_context = bdrv_get_aio_context(s->to_replace); + aio_context_acquire(replace_aio_context); + error_setg(&s->replace_blocker, "block device is in use by block-job-complete"); bdrv_op_block_all(s->to_replace, s->replace_blocker); bdrv_ref(s->to_replace); + + aio_context_release(replace_aio_context); } s->should_complete = true; diff --git a/block/parallels.c b/block/parallels.c index 2a814f3db4..4f9cd8dd23 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -155,7 +155,7 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) offset = sector_num % s->tracks; /* not allocated */ - if ((index > s->catalog_size) || (s->catalog_bitmap[index] == 0)) + if ((index >= s->catalog_size) || (s->catalog_bitmap[index] == 0)) return -1; return ((uint64_t)s->catalog_bitmap[index] * s->off_multiplier + offset) * 512; diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 4d888c785f..df0b2c9cec 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1414,7 +1414,7 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) * clusters. */ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, - unsigned int nb_clusters, enum qcow2_discard_type type) + unsigned int nb_clusters, enum qcow2_discard_type type, bool full_discard) { BDRVQcowState *s = bs->opaque; uint64_t *l2_table; @@ -1436,23 +1436,30 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, old_l2_entry = be64_to_cpu(l2_table[l2_index + i]); /* - * Make sure that a discarded area reads back as zeroes for v3 images - * (we cannot do it for v2 without actually writing a zero-filled - * buffer). We can skip the operation if the cluster is already marked - * as zero, or if it's unallocated and we don't have a backing file. + * If full_discard is false, make sure that a discarded area reads back + * as zeroes for v3 images (we cannot do it for v2 without actually + * writing a zero-filled buffer). We can skip the operation if the + * cluster is already marked as zero, or if it's unallocated and we + * don't have a backing file. * * TODO We might want to use bdrv_get_block_status(bs) here, but we're * holding s->lock, so that doesn't work today. + * + * If full_discard is true, the sector should not read back as zeroes, + * but rather fall through to the backing file. */ switch (qcow2_get_cluster_type(old_l2_entry)) { case QCOW2_CLUSTER_UNALLOCATED: - if (!bs->backing_hd) { + if (full_discard || !bs->backing_hd) { continue; } break; case QCOW2_CLUSTER_ZERO: - continue; + if (!full_discard) { + continue; + } + break; case QCOW2_CLUSTER_NORMAL: case QCOW2_CLUSTER_COMPRESSED: @@ -1464,7 +1471,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, /* First remove L2 entries */ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); - if (s->qcow_version >= 3) { + if (!full_discard && s->qcow_version >= 3) { l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); } else { l2_table[l2_index + i] = cpu_to_be64(0); @@ -1483,7 +1490,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, } int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, - int nb_sectors, enum qcow2_discard_type type) + int nb_sectors, enum qcow2_discard_type type, bool full_discard) { BDRVQcowState *s = bs->opaque; uint64_t end_offset; @@ -1506,7 +1513,7 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, /* Each L2 table is handled by its own loop iteration */ while (nb_clusters > 0) { - ret = discard_single_l2(bs, offset, nb_clusters, type); + ret = discard_single_l2(bs, offset, nb_clusters, type, full_discard); if (ret < 0) { goto fail; } @@ -1606,15 +1613,14 @@ fail: * Expands all zero clusters in a specific L1 table (or deallocates them, for * non-backed non-pre-allocated zero clusters). * - * expanded_clusters is a bitmap where every bit corresponds to one cluster in - * the image file; a bit gets set if the corresponding cluster has been used for - * zero expansion (i.e., has been filled with zeroes and is referenced from an - * L2 table). nb_clusters contains the total cluster count of the image file, - * i.e., the number of bits in expanded_clusters. + * l1_entries and *visited_l1_entries are used to keep track of progress for + * status_cb(). l1_entries contains the total number of L1 entries and + * *visited_l1_entries counts all visited L1 entries. */ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, - int l1_size, uint8_t **expanded_clusters, - uint64_t *nb_clusters) + int l1_size, int64_t *visited_l1_entries, + int64_t l1_entries, + BlockDriverAmendStatusCB *status_cb) { BDRVQcowState *s = bs->opaque; bool is_active_l1 = (l1_table == s->l1_table); @@ -1634,9 +1640,14 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, for (i = 0; i < l1_size; i++) { uint64_t l2_offset = l1_table[i] & L1E_OFFSET_MASK; bool l2_dirty = false; + int l2_refcount; if (!l2_offset) { /* unallocated */ + (*visited_l1_entries)++; + if (status_cb) { + status_cb(bs, *visited_l1_entries, l1_entries); + } continue; } @@ -1653,33 +1664,19 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, goto fail; } + l2_refcount = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits); + if (l2_refcount < 0) { + ret = l2_refcount; + goto fail; + } + for (j = 0; j < s->l2_size; j++) { uint64_t l2_entry = be64_to_cpu(l2_table[j]); - int64_t offset = l2_entry & L2E_OFFSET_MASK, cluster_index; + int64_t offset = l2_entry & L2E_OFFSET_MASK; int cluster_type = qcow2_get_cluster_type(l2_entry); bool preallocated = offset != 0; - if (cluster_type == QCOW2_CLUSTER_NORMAL) { - cluster_index = offset >> s->cluster_bits; - assert((cluster_index >= 0) && (cluster_index < *nb_clusters)); - if ((*expanded_clusters)[cluster_index / 8] & - (1 << (cluster_index % 8))) { - /* Probably a shared L2 table; this cluster was a zero - * cluster which has been expanded, its refcount - * therefore most likely requires an update. */ - ret = qcow2_update_cluster_refcount(bs, cluster_index, 1, - QCOW2_DISCARD_NEVER); - if (ret < 0) { - goto fail; - } - /* Since we just increased the refcount, the COPIED flag may - * no longer be set. */ - l2_table[j] = cpu_to_be64(l2_entry & ~QCOW_OFLAG_COPIED); - l2_dirty = true; - } - continue; - } - else if (qcow2_get_cluster_type(l2_entry) != QCOW2_CLUSTER_ZERO) { + if (cluster_type != QCOW2_CLUSTER_ZERO) { continue; } @@ -1697,6 +1694,19 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, ret = offset; goto fail; } + + if (l2_refcount > 1) { + /* For shared L2 tables, set the refcount accordingly (it is + * already 1 and needs to be l2_refcount) */ + ret = qcow2_update_cluster_refcount(bs, + offset >> s->cluster_bits, l2_refcount - 1, + QCOW2_DISCARD_OTHER); + if (ret < 0) { + qcow2_free_clusters(bs, offset, s->cluster_size, + QCOW2_DISCARD_OTHER); + goto fail; + } + } } ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size); @@ -1718,29 +1728,12 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, goto fail; } - l2_table[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED); - l2_dirty = true; - - cluster_index = offset >> s->cluster_bits; - - if (cluster_index >= *nb_clusters) { - uint64_t old_bitmap_size = (*nb_clusters + 7) / 8; - uint64_t new_bitmap_size; - /* The offset may lie beyond the old end of the underlying image - * file for growable files only */ - assert(bs->file->growable); - *nb_clusters = size_to_clusters(s, bs->file->total_sectors * - BDRV_SECTOR_SIZE); - new_bitmap_size = (*nb_clusters + 7) / 8; - *expanded_clusters = g_realloc(*expanded_clusters, - new_bitmap_size); - /* clear the newly allocated space */ - memset(&(*expanded_clusters)[old_bitmap_size], 0, - new_bitmap_size - old_bitmap_size); + if (l2_refcount == 1) { + l2_table[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED); + } else { + l2_table[j] = cpu_to_be64(offset); } - - assert((cluster_index >= 0) && (cluster_index < *nb_clusters)); - (*expanded_clusters)[cluster_index / 8] |= 1 << (cluster_index % 8); + l2_dirty = true; } if (is_active_l1) { @@ -1769,6 +1762,11 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, } } } + + (*visited_l1_entries)++; + if (status_cb) { + status_cb(bs, *visited_l1_entries, l1_entries); + } } ret = 0; @@ -1795,25 +1793,25 @@ fail: * allocation for pre-allocated ones). This is important for downgrading to a * qcow2 version which doesn't yet support metadata zero clusters. */ -int qcow2_expand_zero_clusters(BlockDriverState *bs) +int qcow2_expand_zero_clusters(BlockDriverState *bs, + BlockDriverAmendStatusCB *status_cb) { BDRVQcowState *s = bs->opaque; uint64_t *l1_table = NULL; - uint64_t nb_clusters; - uint8_t *expanded_clusters; + int64_t l1_entries = 0, visited_l1_entries = 0; int ret; int i, j; - nb_clusters = size_to_clusters(s, bs->file->total_sectors * - BDRV_SECTOR_SIZE); - expanded_clusters = g_try_malloc0((nb_clusters + 7) / 8); - if (expanded_clusters == NULL) { - ret = -ENOMEM; - goto fail; + if (status_cb) { + l1_entries = s->l1_size; + for (i = 0; i < s->nb_snapshots; i++) { + l1_entries += s->snapshots[i].l1_size; + } } ret = expand_zero_clusters_in_l1(bs, s->l1_table, s->l1_size, - &expanded_clusters, &nb_clusters); + &visited_l1_entries, l1_entries, + status_cb); if (ret < 0) { goto fail; } @@ -1847,7 +1845,8 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs) } ret = expand_zero_clusters_in_l1(bs, l1_table, s->snapshots[i].l1_size, - &expanded_clusters, &nb_clusters); + &visited_l1_entries, l1_entries, + status_cb); if (ret < 0) { goto fail; } @@ -1856,7 +1855,6 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs) ret = 0; fail: - g_free(expanded_clusters); g_free(l1_table); return ret; } diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 1477031206..9afdb40b40 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -91,7 +91,7 @@ static int load_refcount_block(BlockDriverState *bs, * return value is the refcount of the cluster, negative values are -errno * and indicate an error. */ -static int get_refcount(BlockDriverState *bs, int64_t cluster_index) +int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index) { BDRVQcowState *s = bs->opaque; uint64_t refcount_table_index, block_index; @@ -629,8 +629,7 @@ fail: } /* - * Increases or decreases the refcount of a given cluster by one. - * addend must be 1 or -1. + * Increases or decreases the refcount of a given cluster. * * If the return value is non-negative, it is the new refcount of the cluster. * If it is negative, it is -errno and indicates an error. @@ -649,7 +648,7 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, return ret; } - return get_refcount(bs, cluster_index); + return qcow2_get_refcount(bs, cluster_index); } @@ -670,7 +669,7 @@ static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size) retry: for(i = 0; i < nb_clusters; i++) { uint64_t next_cluster_index = s->free_cluster_index++; - refcount = get_refcount(bs, next_cluster_index); + refcount = qcow2_get_refcount(bs, next_cluster_index); if (refcount < 0) { return refcount; @@ -734,7 +733,7 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, /* Check how many clusters there are free */ cluster_index = offset >> s->cluster_bits; for(i = 0; i < nb_clusters; i++) { - refcount = get_refcount(bs, cluster_index++); + refcount = qcow2_get_refcount(bs, cluster_index++); if (refcount < 0) { return refcount; @@ -981,7 +980,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, cluster_index, addend, QCOW2_DISCARD_SNAPSHOT); } else { - refcount = get_refcount(bs, cluster_index); + refcount = qcow2_get_refcount(bs, cluster_index); } if (refcount < 0) { @@ -1021,7 +1020,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, refcount = qcow2_update_cluster_refcount(bs, l2_offset >> s->cluster_bits, addend, QCOW2_DISCARD_SNAPSHOT); } else { - refcount = get_refcount(bs, l2_offset >> s->cluster_bits); + refcount = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits); } if (refcount < 0) { ret = refcount; @@ -1332,8 +1331,8 @@ fail: * Checks the OFLAG_COPIED flag for all L1 and L2 entries. * * This function does not print an error message nor does it increment - * check_errors if get_refcount fails (this is because such an error will have - * been already detected and sufficiently signaled by the calling function + * check_errors if qcow2_get_refcount fails (this is because such an error will + * have been already detected and sufficiently signaled by the calling function * (qcow2_check_refcounts) by the time this function is called). */ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, @@ -1354,7 +1353,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, continue; } - refcount = get_refcount(bs, l2_offset >> s->cluster_bits); + refcount = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits); if (refcount < 0) { /* don't print message nor increment check_errors */ continue; @@ -1396,7 +1395,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, if ((cluster_type == QCOW2_CLUSTER_NORMAL) || ((cluster_type == QCOW2_CLUSTER_ZERO) && (data_offset != 0))) { - refcount = get_refcount(bs, data_offset >> s->cluster_bits); + refcount = qcow2_get_refcount(bs, + data_offset >> s->cluster_bits); if (refcount < 0) { /* don't print message nor increment check_errors */ continue; @@ -1632,7 +1632,7 @@ static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, int refcount1, refcount2, ret; for (i = 0, *highest_cluster = 0; i < nb_clusters; i++) { - refcount1 = get_refcount(bs, i); + refcount1 = qcow2_get_refcount(bs, i); if (refcount1 < 0) { fprintf(stderr, "Can't get refcount for cluster %" PRId64 ": %s\n", i, strerror(-refcount1)); diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index f52d7fdd22..5b3903cf22 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -441,7 +441,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) qcow2_discard_clusters(bs, qcow2_vm_state_offset(s), align_offset(sn->vm_state_size, s->cluster_size) >> BDRV_SECTOR_BITS, - QCOW2_DISCARD_NEVER); + QCOW2_DISCARD_NEVER, false); #ifdef DEBUG_ALLOC { diff --git a/block/qcow2.c b/block/qcow2.c index d031515838..d12049451a 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -2089,7 +2089,7 @@ static coroutine_fn int qcow2_co_discard(BlockDriverState *bs, qemu_co_mutex_lock(&s->lock); ret = qcow2_discard_clusters(bs, sector_num << BDRV_SECTOR_BITS, - nb_sectors, QCOW2_DISCARD_REQUEST); + nb_sectors, QCOW2_DISCARD_REQUEST, false); qemu_co_mutex_unlock(&s->lock); return ret; } @@ -2230,6 +2230,195 @@ fail: return ret; } +static int make_completely_empty(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + int ret, l1_clusters; + int64_t offset; + uint64_t *new_reftable = NULL; + uint64_t rt_entry, l1_size2; + struct { + uint64_t l1_offset; + uint64_t reftable_offset; + uint32_t reftable_clusters; + } QEMU_PACKED l1_ofs_rt_ofs_cls; + + ret = qcow2_cache_empty(bs, s->l2_table_cache); + if (ret < 0) { + goto fail; + } + + ret = qcow2_cache_empty(bs, s->refcount_block_cache); + if (ret < 0) { + goto fail; + } + + /* Refcounts will be broken utterly */ + ret = qcow2_mark_dirty(bs); + if (ret < 0) { + goto fail; + } + + BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); + + l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / sizeof(uint64_t)); + l1_size2 = (uint64_t)s->l1_size * sizeof(uint64_t); + + /* After this call, neither the in-memory nor the on-disk refcount + * information accurately describe the actual references */ + + ret = bdrv_write_zeroes(bs->file, s->l1_table_offset / BDRV_SECTOR_SIZE, + l1_clusters * s->cluster_sectors, 0); + if (ret < 0) { + goto fail_broken_refcounts; + } + memset(s->l1_table, 0, l1_size2); + + BLKDBG_EVENT(bs->file, BLKDBG_EMPTY_IMAGE_PREPARE); + + /* Overwrite enough clusters at the beginning of the sectors to place + * the refcount table, a refcount block and the L1 table in; this may + * overwrite parts of the existing refcount and L1 table, which is not + * an issue because the dirty flag is set, complete data loss is in fact + * desired and partial data loss is consequently fine as well */ + ret = bdrv_write_zeroes(bs->file, s->cluster_size / BDRV_SECTOR_SIZE, + (2 + l1_clusters) * s->cluster_size / + BDRV_SECTOR_SIZE, 0); + /* This call (even if it failed overall) may have overwritten on-disk + * refcount structures; in that case, the in-memory refcount information + * will probably differ from the on-disk information which makes the BDS + * unusable */ + if (ret < 0) { + goto fail_broken_refcounts; + } + + BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); + BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_UPDATE); + + /* "Create" an empty reftable (one cluster) directly after the image + * header and an empty L1 table three clusters after the image header; + * the cluster between those two will be used as the first refblock */ + cpu_to_be64w(&l1_ofs_rt_ofs_cls.l1_offset, 3 * s->cluster_size); + cpu_to_be64w(&l1_ofs_rt_ofs_cls.reftable_offset, s->cluster_size); + cpu_to_be32w(&l1_ofs_rt_ofs_cls.reftable_clusters, 1); + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_table_offset), + &l1_ofs_rt_ofs_cls, sizeof(l1_ofs_rt_ofs_cls)); + if (ret < 0) { + goto fail_broken_refcounts; + } + + s->l1_table_offset = 3 * s->cluster_size; + + new_reftable = g_try_new0(uint64_t, s->cluster_size / sizeof(uint64_t)); + if (!new_reftable) { + ret = -ENOMEM; + goto fail_broken_refcounts; + } + + s->refcount_table_offset = s->cluster_size; + s->refcount_table_size = s->cluster_size / sizeof(uint64_t); + + g_free(s->refcount_table); + s->refcount_table = new_reftable; + new_reftable = NULL; + + /* Now the in-memory refcount information again corresponds to the on-disk + * information (reftable is empty and no refblocks (the refblock cache is + * empty)); however, this means some clusters (e.g. the image header) are + * referenced, but not refcounted, but the normal qcow2 code assumes that + * the in-memory information is always correct */ + + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC); + + /* Enter the first refblock into the reftable */ + rt_entry = cpu_to_be64(2 * s->cluster_size); + ret = bdrv_pwrite_sync(bs->file, s->cluster_size, + &rt_entry, sizeof(rt_entry)); + if (ret < 0) { + goto fail_broken_refcounts; + } + s->refcount_table[0] = 2 * s->cluster_size; + + s->free_cluster_index = 0; + assert(3 + l1_clusters <= s->refcount_block_size); + offset = qcow2_alloc_clusters(bs, 3 * s->cluster_size + l1_size2); + if (offset < 0) { + ret = offset; + goto fail_broken_refcounts; + } else if (offset > 0) { + error_report("First cluster in emptied image is in use"); + abort(); + } + + /* Now finally the in-memory information corresponds to the on-disk + * structures and is correct */ + ret = qcow2_mark_clean(bs); + if (ret < 0) { + goto fail; + } + + ret = bdrv_truncate(bs->file, (3 + l1_clusters) * s->cluster_size); + if (ret < 0) { + goto fail; + } + + return 0; + +fail_broken_refcounts: + /* The BDS is unusable at this point. If we wanted to make it usable, we + * would have to call qcow2_refcount_close(), qcow2_refcount_init(), + * qcow2_check_refcounts(), qcow2_refcount_close() and qcow2_refcount_init() + * again. However, because the functions which could have caused this error + * path to be taken are used by those functions as well, it's very likely + * that that sequence will fail as well. Therefore, just eject the BDS. */ + bs->drv = NULL; + +fail: + g_free(new_reftable); + return ret; +} + +static int qcow2_make_empty(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + uint64_t start_sector; + int sector_step = INT_MAX / BDRV_SECTOR_SIZE; + int l1_clusters, ret = 0; + + l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / sizeof(uint64_t)); + + if (s->qcow_version >= 3 && !s->snapshots && + 3 + l1_clusters <= s->refcount_block_size) { + /* The following function only works for qcow2 v3 images (it requires + * the dirty flag) and only as long as there are no snapshots (because + * it completely empties the image). Furthermore, the L1 table and three + * additional clusters (image header, refcount table, one refcount + * block) have to fit inside one refcount block. */ + return make_completely_empty(bs); + } + + /* This fallback code simply discards every active cluster; this is slow, + * but works in all cases */ + for (start_sector = 0; start_sector < bs->total_sectors; + start_sector += sector_step) + { + /* As this function is generally used after committing an external + * snapshot, QCOW2_DISCARD_SNAPSHOT seems appropriate. Also, the + * default action for this kind of discard is to pass the discard, + * which will ideally result in an actually smaller image file, as + * is probably desired. */ + ret = qcow2_discard_clusters(bs, start_sector * BDRV_SECTOR_SIZE, + MIN(sector_step, + bs->total_sectors - start_sector), + QCOW2_DISCARD_SNAPSHOT, true); + if (ret < 0) { + break; + } + } + + return ret; +} + static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; @@ -2361,7 +2550,8 @@ static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf, * Downgrades an image's version. To achieve this, any incompatible features * have to be removed. */ -static int qcow2_downgrade(BlockDriverState *bs, int target_version) +static int qcow2_downgrade(BlockDriverState *bs, int target_version, + BlockDriverAmendStatusCB *status_cb) { BDRVQcowState *s = bs->opaque; int current_version = s->qcow_version; @@ -2410,7 +2600,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version) /* clearing autoclear features is trivial */ s->autoclear_features = 0; - ret = qcow2_expand_zero_clusters(bs); + ret = qcow2_expand_zero_clusters(bs, status_cb); if (ret < 0) { return ret; } @@ -2424,7 +2614,8 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version) return 0; } -static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts) +static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + BlockDriverAmendStatusCB *status_cb) { BDRVQcowState *s = bs->opaque; int old_version = s->qcow_version, new_version = old_version; @@ -2502,7 +2693,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts) return ret; } } else { - ret = qcow2_downgrade(bs, new_version); + ret = qcow2_downgrade(bs, new_version, status_cb); if (ret < 0) { return ret; } @@ -2676,6 +2867,7 @@ static BlockDriver bdrv_qcow2 = { .bdrv_co_discard = qcow2_co_discard, .bdrv_truncate = qcow2_truncate, .bdrv_write_compressed = qcow2_write_compressed, + .bdrv_make_empty = qcow2_make_empty, .bdrv_snapshot_create = qcow2_snapshot_create, .bdrv_snapshot_goto = qcow2_snapshot_goto, diff --git a/block/qcow2.h b/block/qcow2.h index 577ccd1bf0..6e39a1b639 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -487,6 +487,8 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, int qcow2_refcount_init(BlockDriverState *bs); void qcow2_refcount_close(BlockDriverState *bs); +int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index); + int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, int addend, enum qcow2_discard_type type); @@ -534,10 +536,11 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, - int nb_sectors, enum qcow2_discard_type type); + int nb_sectors, enum qcow2_discard_type type, bool full_discard); int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors); -int qcow2_expand_zero_clusters(BlockDriverState *bs); +int qcow2_expand_zero_clusters(BlockDriverState *bs, + BlockDriverAmendStatusCB *status_cb); /* qcow2-snapshot.c functions */ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); diff --git a/block/raw-posix.c b/block/raw-posix.c index 475cf74655..e100ae2046 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -1481,12 +1481,12 @@ out: return result; } -static int64_t try_fiemap(BlockDriverState *bs, off_t start, off_t *data, - off_t *hole, int nb_sectors, int *pnum) +static int try_fiemap(BlockDriverState *bs, off_t start, off_t *data, + off_t *hole, int nb_sectors) { #ifdef CONFIG_FIEMAP BDRVRawState *s = bs->opaque; - int64_t ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; + int ret = 0; struct { struct fiemap fm; struct fiemap_extent fe; @@ -1527,18 +1527,14 @@ static int64_t try_fiemap(BlockDriverState *bs, off_t start, off_t *data, #endif } -static int64_t try_seek_hole(BlockDriverState *bs, off_t start, off_t *data, - off_t *hole, int *pnum) +static int try_seek_hole(BlockDriverState *bs, off_t start, off_t *data, + off_t *hole) { #if defined SEEK_HOLE && defined SEEK_DATA BDRVRawState *s = bs->opaque; *hole = lseek(s->fd, start, SEEK_HOLE); if (*hole == -1) { - /* -ENXIO indicates that sector_num was past the end of the file. - * There is a virtual hole there. */ - assert(errno != -ENXIO); - return -errno; } @@ -1552,7 +1548,7 @@ static int64_t try_seek_hole(BlockDriverState *bs, off_t start, off_t *data, } } - return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; + return 0; #else return -ENOTSUP; #endif @@ -1578,7 +1574,8 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, int nb_sectors, int *pnum) { off_t start, data = 0, hole = 0; - int64_t ret; + int64_t total_size; + int ret; ret = fd_open(bs); if (ret < 0) { @@ -1586,29 +1583,38 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, } start = sector_num * BDRV_SECTOR_SIZE; + total_size = bdrv_getlength(bs); + if (total_size < 0) { + return total_size; + } else if (start >= total_size) { + *pnum = 0; + return 0; + } else if (start + nb_sectors * BDRV_SECTOR_SIZE > total_size) { + nb_sectors = DIV_ROUND_UP(total_size - start, BDRV_SECTOR_SIZE); + } - ret = try_seek_hole(bs, start, &data, &hole, pnum); + ret = try_seek_hole(bs, start, &data, &hole); if (ret < 0) { - ret = try_fiemap(bs, start, &data, &hole, nb_sectors, pnum); + ret = try_fiemap(bs, start, &data, &hole, nb_sectors); if (ret < 0) { /* Assume everything is allocated. */ data = 0; hole = start + nb_sectors * BDRV_SECTOR_SIZE; - ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; + ret = 0; } } + assert(ret >= 0); + if (data <= start) { /* On a data extent, compute sectors to the end of the extent. */ *pnum = MIN(nb_sectors, (hole - start) / BDRV_SECTOR_SIZE); + return ret | BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; } else { /* On a hole, compute sectors to the beginning of the next extent. */ *pnum = MIN(nb_sectors, (data - start) / BDRV_SECTOR_SIZE); - ret &= ~BDRV_BLOCK_DATA; - ret |= BDRV_BLOCK_ZERO; + return ret | BDRV_BLOCK_ZERO | BDRV_BLOCK_OFFSET_VALID | start; } - - return ret; } static coroutine_fn BlockAIOCB *raw_aio_discard(BlockDriverState *bs, diff --git a/block/rbd.c b/block/rbd.c index 47cab8be94..5b5a64a27a 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -887,6 +887,18 @@ static BlockAIOCB* qemu_rbd_aio_discard(BlockDriverState *bs, } #endif +#ifdef LIBRBD_SUPPORTS_INVALIDATE +static void qemu_rbd_invalidate_cache(BlockDriverState *bs, + Error **errp) +{ + BDRVRBDState *s = bs->opaque; + int r = rbd_invalidate_cache(s->image); + if (r < 0) { + error_setg_errno(errp, -r, "Failed to invalidate the cache"); + } +} +#endif + static QemuOptsList qemu_rbd_create_opts = { .name = "rbd-create-opts", .head = QTAILQ_HEAD_INITIALIZER(qemu_rbd_create_opts.head), @@ -936,6 +948,9 @@ static BlockDriver bdrv_rbd = { .bdrv_snapshot_delete = qemu_rbd_snap_remove, .bdrv_snapshot_list = qemu_rbd_snap_list, .bdrv_snapshot_goto = qemu_rbd_snap_rollback, +#ifdef LIBRBD_SUPPORTS_INVALIDATE + .bdrv_invalidate_cache = qemu_rbd_invalidate_cache, +#endif }; static void bdrv_rbd_init(void) diff --git a/block/snapshot.c b/block/snapshot.c index 85c52ff455..698e1a1d58 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -236,6 +236,10 @@ int bdrv_snapshot_delete(BlockDriverState *bs, error_setg(errp, "snapshot_id and name are both NULL"); return -EINVAL; } + + /* drain all pending i/o before deleting snapshot */ + bdrv_drain_all(); + if (drv->bdrv_snapshot_delete) { return drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp); } diff --git a/block/stream.c b/block/stream.c index a1dc8da484..a628901f69 100644 --- a/block/stream.c +++ b/block/stream.c @@ -79,9 +79,39 @@ static void close_unused_images(BlockDriverState *top, BlockDriverState *base, bdrv_refresh_limits(top, NULL); } +typedef struct { + int ret; + bool reached_end; +} StreamCompleteData; + +static void stream_complete(BlockJob *job, void *opaque) +{ + StreamBlockJob *s = container_of(job, StreamBlockJob, common); + StreamCompleteData *data = opaque; + BlockDriverState *base = s->base; + + if (!block_job_is_cancelled(&s->common) && data->reached_end && + data->ret == 0) { + const char *base_id = NULL, *base_fmt = NULL; + if (base) { + base_id = s->backing_file_str; + if (base->drv) { + base_fmt = base->drv->format_name; + } + } + data->ret = bdrv_change_backing_file(job->bs, base_id, base_fmt); + close_unused_images(job->bs, base, base_id); + } + + g_free(s->backing_file_str); + block_job_completed(&s->common, data->ret); + g_free(data); +} + static void coroutine_fn stream_run(void *opaque) { StreamBlockJob *s = opaque; + StreamCompleteData *data; BlockDriverState *bs = s->common.bs; BlockDriverState *base = s->base; int64_t sector_num, end; @@ -183,21 +213,13 @@ wait: /* Do not remove the backing file if an error was there but ignored. */ ret = error; - if (!block_job_is_cancelled(&s->common) && sector_num == end && ret == 0) { - const char *base_id = NULL, *base_fmt = NULL; - if (base) { - base_id = s->backing_file_str; - if (base->drv) { - base_fmt = base->drv->format_name; - } - } - ret = bdrv_change_backing_file(bs, base_id, base_fmt); - close_unused_images(bs, base, base_id); - } - qemu_vfree(buf); - g_free(s->backing_file_str); - block_job_completed(&s->common, ret); + + /* Modify backing chain and close BDSes in main loop */ + data = g_malloc(sizeof(*data)); + data->ret = ret; + data->reached_end = sector_num == end; + block_job_defer_to_main_loop(&s->common, stream_complete, data); } static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp) diff --git a/block/vdi.c b/block/vdi.c index e1d211c9f7..39070b75e8 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -120,8 +120,18 @@ typedef unsigned char uuid_t[16]; #define VDI_IS_ALLOCATED(X) ((X) < VDI_DISCARDED) -/* max blocks in image is (0xffffffff / 4) */ -#define VDI_BLOCKS_IN_IMAGE_MAX 0x3fffffff +/* The bmap will take up VDI_BLOCKS_IN_IMAGE_MAX * sizeof(uint32_t) bytes; since + * the bmap is read and written in a single operation, its size needs to be + * limited to INT_MAX; furthermore, when opening an image, the bmap size is + * rounded up to be aligned on BDRV_SECTOR_SIZE. + * Therefore this should satisfy the following: + * VDI_BLOCKS_IN_IMAGE_MAX * sizeof(uint32_t) + BDRV_SECTOR_SIZE == INT_MAX + 1 + * (INT_MAX + 1 is the first value not representable as an int) + * This guarantees that any value below or equal to the constant will, when + * multiplied by sizeof(uint32_t) and rounded up to a BDRV_SECTOR_SIZE boundary, + * still be below or equal to INT_MAX. */ +#define VDI_BLOCKS_IN_IMAGE_MAX \ + ((unsigned)((INT_MAX + 1u - BDRV_SECTOR_SIZE) / sizeof(uint32_t))) #define VDI_DISK_SIZE_MAX ((uint64_t)VDI_BLOCKS_IN_IMAGE_MAX * \ (uint64_t)DEFAULT_CLUSTER_SIZE) diff --git a/blockdev.c b/blockdev.c index eb743a956c..57910b82c7 100644 --- a/blockdev.c +++ b/blockdev.c @@ -115,14 +115,21 @@ void blockdev_mark_auto_del(BlockBackend *blk) { DriveInfo *dinfo = blk_legacy_dinfo(blk); BlockDriverState *bs = blk_bs(blk); + AioContext *aio_context; if (!dinfo) { return; } + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + if (bs->job) { block_job_cancel(bs->job); } + + aio_context_release(aio_context); + dinfo->auto_del = 1; } @@ -1922,6 +1929,11 @@ out: static void block_job_cb(void *opaque, int ret) { + /* Note that this function may be executed from another AioContext besides + * the QEMU main loop. If you need to access anything that assumes the + * QEMU global mutex, use a BH or introduce a mutex. + */ + BlockDriverState *bs = opaque; const char *msg = NULL; @@ -1951,6 +1963,7 @@ void qmp_block_stream(const char *device, { BlockDriverState *bs; BlockDriverState *base_bs = NULL; + AioContext *aio_context; Error *local_err = NULL; const char *base_name = NULL; @@ -1964,16 +1977,20 @@ void qmp_block_stream(const char *device, return; } + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_STREAM, errp)) { - return; + goto out; } if (has_base) { base_bs = bdrv_find_backing_image(bs, base); if (base_bs == NULL) { error_set(errp, QERR_BASE_NOT_FOUND, base); - return; + goto out; } + assert(bdrv_get_aio_context(base_bs) == aio_context); base_name = base; } @@ -1982,7 +1999,7 @@ void qmp_block_stream(const char *device, if (base_bs == NULL && has_backing_file) { error_setg(errp, "backing file specified, but streaming the " "entire chain"); - return; + goto out; } /* backing_file string overrides base bs filename */ @@ -1992,10 +2009,13 @@ void qmp_block_stream(const char *device, on_error, block_job_cb, bs, &local_err); if (local_err) { error_propagate(errp, local_err); - return; + goto out; } trace_qmp_block_stream(bs, bs->job); + +out: + aio_context_release(aio_context); } void qmp_block_commit(const char *device, @@ -2007,6 +2027,7 @@ void qmp_block_commit(const char *device, { BlockDriverState *bs; BlockDriverState *base_bs, *top_bs; + AioContext *aio_context; Error *local_err = NULL; /* This will be part of the QMP command, if/when the * BlockdevOnError change for blkmirror makes it in @@ -2017,9 +2038,6 @@ void qmp_block_commit(const char *device, speed = 0; } - /* drain all i/o before commits */ - bdrv_drain_all(); - /* Important Note: * libvirt relies on the DeviceNotFound error class in order to probe for * live commit feature versions; for this to work, we must make sure to @@ -2031,8 +2049,14 @@ void qmp_block_commit(const char *device, return; } + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + /* drain all i/o before commits */ + bdrv_drain_all(); + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT, errp)) { - return; + goto out; } /* default top_bs is the active layer */ @@ -2046,9 +2070,11 @@ void qmp_block_commit(const char *device, if (top_bs == NULL) { error_setg(errp, "Top image file %s not found", top ? top : "NULL"); - return; + goto out; } + assert(bdrv_get_aio_context(top_bs) == aio_context); + if (has_base && base) { base_bs = bdrv_find_backing_image(top_bs, base); } else { @@ -2057,20 +2083,22 @@ void qmp_block_commit(const char *device, if (base_bs == NULL) { error_set(errp, QERR_BASE_NOT_FOUND, base ? base : "NULL"); - return; + goto out; } + assert(bdrv_get_aio_context(base_bs) == aio_context); + /* Do not allow attempts to commit an image into itself */ if (top_bs == base_bs) { error_setg(errp, "cannot commit an image into itself"); - return; + goto out; } if (top_bs == bs) { if (has_backing_file) { error_setg(errp, "'backing-file' specified," " but 'top' is the active layer"); - return; + goto out; } commit_active_start(bs, base_bs, speed, on_error, block_job_cb, bs, &local_err); @@ -2080,8 +2108,11 @@ void qmp_block_commit(const char *device, } if (local_err != NULL) { error_propagate(errp, local_err); - return; + goto out; } + +out: + aio_context_release(aio_context); } void qmp_drive_backup(const char *device, const char *target, @@ -2096,6 +2127,7 @@ void qmp_drive_backup(const char *device, const char *target, BlockDriverState *bs; BlockDriverState *target_bs; BlockDriverState *source = NULL; + AioContext *aio_context; BlockDriver *drv = NULL; Error *local_err = NULL; int flags; @@ -2121,9 +2153,12 @@ void qmp_drive_backup(const char *device, const char *target, return; } + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + if (!bdrv_is_inserted(bs)) { error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); - return; + goto out; } if (!has_format) { @@ -2133,12 +2168,12 @@ void qmp_drive_backup(const char *device, const char *target, drv = bdrv_find_format(format); if (!drv) { error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); - return; + goto out; } } if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { - return; + goto out; } flags = bs->open_flags | BDRV_O_RDWR; @@ -2158,7 +2193,7 @@ void qmp_drive_backup(const char *device, const char *target, size = bdrv_getlength(bs); if (size < 0) { error_setg_errno(errp, -size, "bdrv_getlength failed"); - return; + goto out; } if (mode != NEW_IMAGE_MODE_EXISTING) { @@ -2175,23 +2210,28 @@ void qmp_drive_backup(const char *device, const char *target, if (local_err) { error_propagate(errp, local_err); - return; + goto out; } target_bs = NULL; ret = bdrv_open(&target_bs, target, NULL, NULL, flags, drv, &local_err); if (ret < 0) { error_propagate(errp, local_err); - return; + goto out; } + bdrv_set_aio_context(target_bs, aio_context); + backup_start(bs, target_bs, speed, sync, on_source_error, on_target_error, block_job_cb, bs, &local_err); if (local_err != NULL) { bdrv_unref(target_bs); error_propagate(errp, local_err); - return; + goto out; } + +out: + aio_context_release(aio_context); } BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) @@ -2216,6 +2256,7 @@ void qmp_drive_mirror(const char *device, const char *target, { BlockDriverState *bs; BlockDriverState *source, *target_bs; + AioContext *aio_context; BlockDriver *drv = NULL; Error *local_err = NULL; QDict *options = NULL; @@ -2258,9 +2299,12 @@ void qmp_drive_mirror(const char *device, const char *target, return; } + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + if (!bdrv_is_inserted(bs)) { error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); - return; + goto out; } if (!has_format) { @@ -2270,12 +2314,12 @@ void qmp_drive_mirror(const char *device, const char *target, drv = bdrv_find_format(format); if (!drv) { error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); - return; + goto out; } } if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR, errp)) { - return; + goto out; } flags = bs->open_flags | BDRV_O_RDWR; @@ -2290,29 +2334,36 @@ void qmp_drive_mirror(const char *device, const char *target, size = bdrv_getlength(bs); if (size < 0) { error_setg_errno(errp, -size, "bdrv_getlength failed"); - return; + goto out; } if (has_replaces) { BlockDriverState *to_replace_bs; + AioContext *replace_aio_context; + int64_t replace_size; if (!has_node_name) { error_setg(errp, "a node-name must be provided when replacing a" " named node of the graph"); - return; + goto out; } to_replace_bs = check_to_replace_node(replaces, &local_err); if (!to_replace_bs) { error_propagate(errp, local_err); - return; + goto out; } - if (size != bdrv_getlength(to_replace_bs)) { + replace_aio_context = bdrv_get_aio_context(to_replace_bs); + aio_context_acquire(replace_aio_context); + replace_size = bdrv_getlength(to_replace_bs); + aio_context_release(replace_aio_context); + + if (size != replace_size) { error_setg(errp, "cannot replace image with a mirror image of " "different size"); - return; + goto out; } } @@ -2341,7 +2392,7 @@ void qmp_drive_mirror(const char *device, const char *target, if (local_err) { error_propagate(errp, local_err); - return; + goto out; } if (has_node_name) { @@ -2357,9 +2408,11 @@ void qmp_drive_mirror(const char *device, const char *target, flags | BDRV_O_NO_BACKING, drv, &local_err); if (ret < 0) { error_propagate(errp, local_err); - return; + goto out; } + bdrv_set_aio_context(target_bs, aio_context); + /* pass the node name to replace to mirror start since it's loose coupling * and will allow to check whether the node still exist at mirror completion */ @@ -2371,24 +2424,42 @@ void qmp_drive_mirror(const char *device, const char *target, if (local_err != NULL) { bdrv_unref(target_bs); error_propagate(errp, local_err); - return; + goto out; } + +out: + aio_context_release(aio_context); } -static BlockJob *find_block_job(const char *device) +/* Get the block job for a given device name and acquire its AioContext */ +static BlockJob *find_block_job(const char *device, AioContext **aio_context) { BlockDriverState *bs; bs = bdrv_find(device); - if (!bs || !bs->job) { - return NULL; + if (!bs) { + goto notfound; } + + *aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(*aio_context); + + if (!bs->job) { + aio_context_release(*aio_context); + goto notfound; + } + return bs->job; + +notfound: + *aio_context = NULL; + return NULL; } void qmp_block_job_set_speed(const char *device, int64_t speed, Error **errp) { - BlockJob *job = find_block_job(device); + AioContext *aio_context; + BlockJob *job = find_block_job(device, &aio_context); if (!job) { error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device); @@ -2396,34 +2467,40 @@ void qmp_block_job_set_speed(const char *device, int64_t speed, Error **errp) } block_job_set_speed(job, speed, errp); + aio_context_release(aio_context); } void qmp_block_job_cancel(const char *device, bool has_force, bool force, Error **errp) { - BlockJob *job = find_block_job(device); - - if (!has_force) { - force = false; - } + AioContext *aio_context; + BlockJob *job = find_block_job(device, &aio_context); if (!job) { error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device); return; } + + if (!has_force) { + force = false; + } + if (job->paused && !force) { error_setg(errp, "The block job for device '%s' is currently paused", device); - return; + goto out; } trace_qmp_block_job_cancel(job); block_job_cancel(job); +out: + aio_context_release(aio_context); } void qmp_block_job_pause(const char *device, Error **errp) { - BlockJob *job = find_block_job(device); + AioContext *aio_context; + BlockJob *job = find_block_job(device, &aio_context); if (!job) { error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device); @@ -2432,11 +2509,13 @@ void qmp_block_job_pause(const char *device, Error **errp) trace_qmp_block_job_pause(job); block_job_pause(job); + aio_context_release(aio_context); } void qmp_block_job_resume(const char *device, Error **errp) { - BlockJob *job = find_block_job(device); + AioContext *aio_context; + BlockJob *job = find_block_job(device, &aio_context); if (!job) { error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device); @@ -2445,11 +2524,13 @@ void qmp_block_job_resume(const char *device, Error **errp) trace_qmp_block_job_resume(job); block_job_resume(job); + aio_context_release(aio_context); } void qmp_block_job_complete(const char *device, Error **errp) { - BlockJob *job = find_block_job(device); + AioContext *aio_context; + BlockJob *job = find_block_job(device, &aio_context); if (!job) { error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device); @@ -2458,6 +2539,7 @@ void qmp_block_job_complete(const char *device, Error **errp) trace_qmp_block_job_complete(job); block_job_complete(job, errp); + aio_context_release(aio_context); } void qmp_change_backing_file(const char *device, @@ -2602,12 +2684,18 @@ BlockJobInfoList *qmp_query_block_jobs(Error **errp) BlockDriverState *bs; for (bs = bdrv_next(NULL); bs; bs = bdrv_next(bs)) { + AioContext *aio_context = bdrv_get_aio_context(bs); + + aio_context_acquire(aio_context); + if (bs->job) { BlockJobInfoList *elem = g_new0(BlockJobInfoList, 1); elem->value = block_job_query(bs->job); *p_next = elem; p_next = &elem->next; } + + aio_context_release(aio_context); } return head; diff --git a/blockjob.c b/blockjob.c index ff0908a653..ba2255d91f 100644 --- a/blockjob.c +++ b/blockjob.c @@ -50,6 +50,7 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs, error_setg(&job->blocker, "block device is in use by block job: %s", BlockJobType_lookup[driver->job_type]); bdrv_op_block_all(bs, job->blocker); + bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker); job->driver = driver; job->bs = bs; @@ -153,7 +154,7 @@ void block_job_iostatus_reset(BlockJob *job) } } -struct BlockCancelData { +struct BlockFinishData { BlockJob *job; BlockCompletionFunc *cb; void *opaque; @@ -161,19 +162,22 @@ struct BlockCancelData { int ret; }; -static void block_job_cancel_cb(void *opaque, int ret) +static void block_job_finish_cb(void *opaque, int ret) { - struct BlockCancelData *data = opaque; + struct BlockFinishData *data = opaque; data->cancelled = block_job_is_cancelled(data->job); data->ret = ret; data->cb(data->opaque, ret); } -int block_job_cancel_sync(BlockJob *job) +static int block_job_finish_sync(BlockJob *job, + void (*finish)(BlockJob *, Error **errp), + Error **errp) { - struct BlockCancelData data; + struct BlockFinishData data; BlockDriverState *bs = job->bs; + Error *local_err = NULL; assert(bs->job == job); @@ -184,15 +188,37 @@ int block_job_cancel_sync(BlockJob *job) data.cb = job->cb; data.opaque = job->opaque; data.ret = -EINPROGRESS; - job->cb = block_job_cancel_cb; + job->cb = block_job_finish_cb; job->opaque = &data; - block_job_cancel(job); + finish(job, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return -EBUSY; + } while (data.ret == -EINPROGRESS) { aio_poll(bdrv_get_aio_context(bs), true); } return (data.cancelled && data.ret == 0) ? -ECANCELED : data.ret; } +/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be + * used with block_job_finish_sync() without the need for (rather nasty) + * function pointer casts there. */ +static void block_job_cancel_err(BlockJob *job, Error **errp) +{ + block_job_cancel(job); +} + +int block_job_cancel_sync(BlockJob *job) +{ + return block_job_finish_sync(job, &block_job_cancel_err, NULL); +} + +int block_job_complete_sync(BlockJob *job, Error **errp) +{ + return block_job_finish_sync(job, &block_job_complete, errp); +} + void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns) { assert(job->busy); @@ -236,6 +262,7 @@ BlockJobInfo *block_job_query(BlockJob *job) info->offset = job->offset; info->speed = job->speed; info->io_status = job->iostatus; + info->ready = job->ready; return info; } @@ -271,6 +298,8 @@ void block_job_event_completed(BlockJob *job, const char *msg) void block_job_event_ready(BlockJob *job) { + job->ready = true; + qapi_event_send_block_job_ready(job->driver->job_type, bdrv_get_device_name(job->bs), job->len, @@ -314,3 +343,48 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs, } return action; } + +typedef struct { + BlockJob *job; + QEMUBH *bh; + AioContext *aio_context; + BlockJobDeferToMainLoopFn *fn; + void *opaque; +} BlockJobDeferToMainLoopData; + +static void block_job_defer_to_main_loop_bh(void *opaque) +{ + BlockJobDeferToMainLoopData *data = opaque; + AioContext *aio_context; + + qemu_bh_delete(data->bh); + + /* Prevent race with block_job_defer_to_main_loop() */ + aio_context_acquire(data->aio_context); + + /* Fetch BDS AioContext again, in case it has changed */ + aio_context = bdrv_get_aio_context(data->job->bs); + aio_context_acquire(aio_context); + + data->fn(data->job, data->opaque); + + aio_context_release(aio_context); + + aio_context_release(data->aio_context); + + g_free(data); +} + +void block_job_defer_to_main_loop(BlockJob *job, + BlockJobDeferToMainLoopFn *fn, + void *opaque) +{ + BlockJobDeferToMainLoopData *data = g_malloc(sizeof(*data)); + data->job = job; + data->bh = qemu_bh_new(block_job_defer_to_main_loop_bh, data); + data->aio_context = bdrv_get_aio_context(job->bs); + data->fn = fn; + data->opaque = opaque; + + qemu_bh_schedule(data->bh); +} diff --git a/default-configs/mips-softmmu.mak b/default-configs/mips-softmmu.mak index 71177efdff..2a80b04dc0 100644 --- a/default-configs/mips-softmmu.mak +++ b/default-configs/mips-softmmu.mak @@ -32,6 +32,5 @@ CONFIG_G364FB=y CONFIG_I8259=y CONFIG_JAZZ_LED=y CONFIG_MC146818RTC=y -CONFIG_VT82C686=y CONFIG_ISA_TESTDEV=y CONFIG_EMPTY_SLOT=y diff --git a/default-configs/mips64-softmmu.mak b/default-configs/mips64-softmmu.mak index 617301b753..f1f933bc44 100644 --- a/default-configs/mips64-softmmu.mak +++ b/default-configs/mips64-softmmu.mak @@ -32,6 +32,5 @@ CONFIG_G364FB=y CONFIG_I8259=y CONFIG_JAZZ_LED=y CONFIG_MC146818RTC=y -CONFIG_VT82C686=y CONFIG_ISA_TESTDEV=y CONFIG_EMPTY_SLOT=y diff --git a/default-configs/mipsel-softmmu.mak b/default-configs/mipsel-softmmu.mak index 532a9aefbd..7708185f6b 100644 --- a/default-configs/mipsel-softmmu.mak +++ b/default-configs/mipsel-softmmu.mak @@ -32,6 +32,5 @@ CONFIG_G364FB=y CONFIG_I8259=y CONFIG_JAZZ_LED=y CONFIG_MC146818RTC=y -CONFIG_VT82C686=y CONFIG_ISA_TESTDEV=y CONFIG_EMPTY_SLOT=y diff --git a/disas/mips.c b/disas/mips.c index 7297825138..2614c52a4b 100644 --- a/disas/mips.c +++ b/disas/mips.c @@ -220,6 +220,28 @@ see <http://www.gnu.org/licenses/>. */ #define OP_SH_MTACC_D 13 #define OP_MASK_MTACC_D 0x3 +/* MSA */ +#define OP_MASK_1BIT 0x1 +#define OP_SH_1BIT 16 +#define OP_MASK_2BIT 0x3 +#define OP_SH_2BIT 16 +#define OP_MASK_3BIT 0x7 +#define OP_SH_3BIT 16 +#define OP_MASK_4BIT 0xf +#define OP_SH_4BIT 16 +#define OP_MASK_5BIT 0x1f +#define OP_SH_5BIT 16 +#define OP_MASK_10BIT 0x3ff +#define OP_SH_10BIT 11 +#define OP_MASK_MSACR11 0x1f +#define OP_SH_MSACR11 11 +#define OP_MASK_MSACR6 0x1f +#define OP_SH_MSACR6 6 +#define OP_MASK_GPR 0x1f +#define OP_SH_GPR 6 +#define OP_MASK_1_TO_4 0x3 +#define OP_SH_1_TO_4 6 + #define OP_OP_COP0 0x10 #define OP_OP_COP1 0x11 #define OP_OP_COP2 0x12 @@ -510,6 +532,9 @@ struct mips_opcode /* Instruction writes MDMX accumulator. */ #define INSN2_WRITE_MDMX_ACC 0x00000004 +/* Reads the general purpose register in OP_*_RD. */ +#define INSN2_READ_GPR_D 0x00000200 + /* Instruction is actually a macro. It should be ignored by the disassembler, and requires special treatment by the assembler. */ #define INSN_MACRO 0xffffffff @@ -567,7 +592,12 @@ struct mips_opcode #define INSN_5500 0x02000000 /* MDMX ASE */ -#define INSN_MDMX 0x04000000 +#define INSN_MDMX 0x00000000 /* Deprecated */ + +/* MIPS MSA Extension */ +#define INSN_MSA 0x04000000 +#define INSN_MSA64 0x04000000 + /* MT ASE */ #define INSN_MT 0x08000000 /* SmartMIPS ASE */ @@ -1204,6 +1234,17 @@ extern const int bfd_mips16_num_opcodes; /* MIPS MT ASE support. */ #define MT32 INSN_MT +/* MSA */ +#define MSA INSN_MSA +#define MSA64 INSN_MSA64 +#define WR_VD INSN_WRITE_FPR_D /* Reuse INSN_WRITE_FPR_D */ +#define RD_VD WR_VD /* Reuse WR_VD */ +#define RD_VT INSN_READ_FPR_T /* Reuse INSN_READ_FPR_T */ +#define RD_VS INSN_READ_FPR_S /* Reuse INSN_READ_FPR_S */ +#define RD_d INSN2_READ_GPR_D /* Reuse INSN2_READ_GPR_D */ + +#define RD_rd6 0 + /* The order of overloaded instructions matters. Label arguments and register arguments look the same. Instructions that can have either for arguments must apear in the correct order in this table for the @@ -1363,6 +1404,541 @@ const struct mips_opcode mips_builtin_opcodes[] = {"cmp.sor.d", "D,S,T", 0x46a00019, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, {"cmp.sune.d", "D,S,T", 0x46a0001a, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, {"cmp.sne.d", "D,S,T", 0x46a0001b, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6}, + +/* MSA */ +{"sll.b", "+d,+e,+f", 0x7800000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"sll.h", "+d,+e,+f", 0x7820000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"sll.w", "+d,+e,+f", 0x7840000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"sll.d", "+d,+e,+f", 0x7860000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"slli.b", "+d,+e,+7", 0x78700009, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"slli.h", "+d,+e,+8", 0x78600009, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"slli.w", "+d,+e,+9", 0x78400009, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"slli.d", "+d,+e,'", 0x78000009, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"sra.b", "+d,+e,+f", 0x7880000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"sra.h", "+d,+e,+f", 0x78a0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"sra.w", "+d,+e,+f", 0x78c0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"sra.d", "+d,+e,+f", 0x78e0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srai.b", "+d,+e,+7", 0x78f00009, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"srai.h", "+d,+e,+8", 0x78e00009, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"srai.w", "+d,+e,+9", 0x78c00009, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"srai.d", "+d,+e,'", 0x78800009, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"srl.b", "+d,+e,+f", 0x7900000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srl.h", "+d,+e,+f", 0x7920000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srl.w", "+d,+e,+f", 0x7940000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srl.d", "+d,+e,+f", 0x7960000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srli.b", "+d,+e,+7", 0x79700009, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"srli.h", "+d,+e,+8", 0x79600009, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"srli.w", "+d,+e,+9", 0x79400009, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"srli.d", "+d,+e,'", 0x79000009, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"bclr.b", "+d,+e,+f", 0x7980000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bclr.h", "+d,+e,+f", 0x79a0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bclr.w", "+d,+e,+f", 0x79c0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bclr.d", "+d,+e,+f", 0x79e0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bclri.b", "+d,+e,+7", 0x79f00009, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"bclri.h", "+d,+e,+8", 0x79e00009, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"bclri.w", "+d,+e,+9", 0x79c00009, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"bclri.d", "+d,+e,'", 0x79800009, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"bset.b", "+d,+e,+f", 0x7a00000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bset.h", "+d,+e,+f", 0x7a20000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bset.w", "+d,+e,+f", 0x7a40000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bset.d", "+d,+e,+f", 0x7a60000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bseti.b", "+d,+e,+7", 0x7a700009, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"bseti.h", "+d,+e,+8", 0x7a600009, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"bseti.w", "+d,+e,+9", 0x7a400009, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"bseti.d", "+d,+e,'", 0x7a000009, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"bneg.b", "+d,+e,+f", 0x7a80000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bneg.h", "+d,+e,+f", 0x7aa0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bneg.w", "+d,+e,+f", 0x7ac0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bneg.d", "+d,+e,+f", 0x7ae0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bnegi.b", "+d,+e,+7", 0x7af00009, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"bnegi.h", "+d,+e,+8", 0x7ae00009, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"bnegi.w", "+d,+e,+9", 0x7ac00009, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"bnegi.d", "+d,+e,'", 0x7a800009, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"binsl.b", "+d,+e,+f", 0x7b00000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"binsl.h", "+d,+e,+f", 0x7b20000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"binsl.w", "+d,+e,+f", 0x7b40000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"binsl.d", "+d,+e,+f", 0x7b60000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"binsli.b", "+d,+e,+7", 0x7b700009, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"binsli.h", "+d,+e,+8", 0x7b600009, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"binsli.w", "+d,+e,+9", 0x7b400009, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"binsli.d", "+d,+e,'", 0x7b000009, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"binsr.b", "+d,+e,+f", 0x7b80000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"binsr.h", "+d,+e,+f", 0x7ba0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"binsr.w", "+d,+e,+f", 0x7bc0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"binsr.d", "+d,+e,+f", 0x7be0000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"binsri.b", "+d,+e,+7", 0x7bf00009, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"binsri.h", "+d,+e,+8", 0x7be00009, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"binsri.w", "+d,+e,+9", 0x7bc00009, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"binsri.d", "+d,+e,'", 0x7b800009, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"addv.b", "+d,+e,+f", 0x7800000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"addv.h", "+d,+e,+f", 0x7820000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"addv.w", "+d,+e,+f", 0x7840000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"addv.d", "+d,+e,+f", 0x7860000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"addvi.b", "+d,+e,k", 0x78000006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"addvi.h", "+d,+e,k", 0x78200006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"addvi.w", "+d,+e,k", 0x78400006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"addvi.d", "+d,+e,k", 0x78600006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"subv.b", "+d,+e,+f", 0x7880000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subv.h", "+d,+e,+f", 0x78a0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subv.w", "+d,+e,+f", 0x78c0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subv.d", "+d,+e,+f", 0x78e0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subvi.b", "+d,+e,k", 0x78800006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"subvi.h", "+d,+e,k", 0x78a00006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"subvi.w", "+d,+e,k", 0x78c00006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"subvi.d", "+d,+e,k", 0x78e00006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"max_s.b", "+d,+e,+f", 0x7900000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"max_s.h", "+d,+e,+f", 0x7920000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"max_s.w", "+d,+e,+f", 0x7940000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"max_s.d", "+d,+e,+f", 0x7960000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"maxi_s.b", "+d,+e,+5", 0x79000006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"maxi_s.h", "+d,+e,+5", 0x79200006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"maxi_s.w", "+d,+e,+5", 0x79400006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"maxi_s.d", "+d,+e,+5", 0x79600006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"max_u.b", "+d,+e,+f", 0x7980000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"max_u.h", "+d,+e,+f", 0x79a0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"max_u.w", "+d,+e,+f", 0x79c0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"max_u.d", "+d,+e,+f", 0x79e0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"maxi_u.b", "+d,+e,k", 0x79800006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"maxi_u.h", "+d,+e,k", 0x79a00006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"maxi_u.w", "+d,+e,k", 0x79c00006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"maxi_u.d", "+d,+e,k", 0x79e00006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"min_s.b", "+d,+e,+f", 0x7a00000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_s.h", "+d,+e,+f", 0x7a20000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_s.w", "+d,+e,+f", 0x7a40000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_s.d", "+d,+e,+f", 0x7a60000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mini_s.b", "+d,+e,+5", 0x7a000006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"mini_s.h", "+d,+e,+5", 0x7a200006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"mini_s.w", "+d,+e,+5", 0x7a400006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"mini_s.d", "+d,+e,+5", 0x7a600006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"min_u.b", "+d,+e,+f", 0x7a80000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_u.h", "+d,+e,+f", 0x7aa0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_u.w", "+d,+e,+f", 0x7ac0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_u.d", "+d,+e,+f", 0x7ae0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mini_u.b", "+d,+e,k", 0x7a800006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"mini_u.h", "+d,+e,k", 0x7aa00006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"mini_u.w", "+d,+e,k", 0x7ac00006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"mini_u.d", "+d,+e,k", 0x7ae00006, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"max_a.b", "+d,+e,+f", 0x7b00000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"max_a.h", "+d,+e,+f", 0x7b20000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"max_a.w", "+d,+e,+f", 0x7b40000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"max_a.d", "+d,+e,+f", 0x7b60000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_a.b", "+d,+e,+f", 0x7b80000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_a.h", "+d,+e,+f", 0x7ba0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_a.w", "+d,+e,+f", 0x7bc0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"min_a.d", "+d,+e,+f", 0x7be0000e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ceq.b", "+d,+e,+f", 0x7800000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ceq.h", "+d,+e,+f", 0x7820000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ceq.w", "+d,+e,+f", 0x7840000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ceq.d", "+d,+e,+f", 0x7860000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ceqi.b", "+d,+e,+5", 0x78000007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"ceqi.h", "+d,+e,+5", 0x78200007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"ceqi.w", "+d,+e,+5", 0x78400007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"ceqi.d", "+d,+e,+5", 0x78600007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clt_s.b", "+d,+e,+f", 0x7900000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clt_s.h", "+d,+e,+f", 0x7920000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clt_s.w", "+d,+e,+f", 0x7940000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clt_s.d", "+d,+e,+f", 0x7960000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clti_s.b", "+d,+e,+5", 0x79000007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clti_s.h", "+d,+e,+5", 0x79200007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clti_s.w", "+d,+e,+5", 0x79400007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clti_s.d", "+d,+e,+5", 0x79600007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clt_u.b", "+d,+e,+f", 0x7980000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clt_u.h", "+d,+e,+f", 0x79a0000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clt_u.w", "+d,+e,+f", 0x79c0000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clt_u.d", "+d,+e,+f", 0x79e0000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clti_u.b", "+d,+e,k", 0x79800007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clti_u.h", "+d,+e,k", 0x79a00007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clti_u.w", "+d,+e,k", 0x79c00007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clti_u.d", "+d,+e,k", 0x79e00007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"cle_s.b", "+d,+e,+f", 0x7a00000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"cle_s.h", "+d,+e,+f", 0x7a20000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"cle_s.w", "+d,+e,+f", 0x7a40000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"cle_s.d", "+d,+e,+f", 0x7a60000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clei_s.b", "+d,+e,+5", 0x7a000007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clei_s.h", "+d,+e,+5", 0x7a200007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clei_s.w", "+d,+e,+5", 0x7a400007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clei_s.d", "+d,+e,+5", 0x7a600007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"cle_u.b", "+d,+e,+f", 0x7a80000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"cle_u.h", "+d,+e,+f", 0x7aa0000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"cle_u.w", "+d,+e,+f", 0x7ac0000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"cle_u.d", "+d,+e,+f", 0x7ae0000f, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"clei_u.b", "+d,+e,k", 0x7a800007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clei_u.h", "+d,+e,k", 0x7aa00007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clei_u.w", "+d,+e,k", 0x7ac00007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"clei_u.d", "+d,+e,k", 0x7ae00007, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"ld.b", "+d,+^(d)", 0x78000020, 0xfc00003f, WR_VD|LDD, RD_d, MSA}, +{"ld.h", "+d,+#(d)", 0x78000021, 0xfc00003f, WR_VD|LDD, RD_d, MSA}, +{"ld.w", "+d,+$(d)", 0x78000022, 0xfc00003f, WR_VD|LDD, RD_d, MSA}, +{"ld.d", "+d,+%(d)", 0x78000023, 0xfc00003f, WR_VD|LDD, RD_d, MSA}, +{"st.b", "+d,+^(d)", 0x78000024, 0xfc00003f, RD_VD|SM, RD_d, MSA}, +{"st.h", "+d,+#(d)", 0x78000025, 0xfc00003f, RD_VD|SM, RD_d, MSA}, +{"st.w", "+d,+$(d)", 0x78000026, 0xfc00003f, RD_VD|SM, RD_d, MSA}, +{"st.d", "+d,+%(d)", 0x78000027, 0xfc00003f, RD_VD|SM, RD_d, MSA}, +{"sat_s.b", "+d,+e,+7", 0x7870000a, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"sat_s.h", "+d,+e,+8", 0x7860000a, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"sat_s.w", "+d,+e,+9", 0x7840000a, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"sat_s.d", "+d,+e,'", 0x7800000a, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"sat_u.b", "+d,+e,+7", 0x78f0000a, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"sat_u.h", "+d,+e,+8", 0x78e0000a, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"sat_u.w", "+d,+e,+9", 0x78c0000a, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"sat_u.d", "+d,+e,'", 0x7880000a, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"add_a.b", "+d,+e,+f", 0x78000010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"add_a.h", "+d,+e,+f", 0x78200010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"add_a.w", "+d,+e,+f", 0x78400010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"add_a.d", "+d,+e,+f", 0x78600010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_a.b", "+d,+e,+f", 0x78800010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_a.h", "+d,+e,+f", 0x78a00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_a.w", "+d,+e,+f", 0x78c00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_a.d", "+d,+e,+f", 0x78e00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_s.b", "+d,+e,+f", 0x79000010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_s.h", "+d,+e,+f", 0x79200010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_s.w", "+d,+e,+f", 0x79400010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_s.d", "+d,+e,+f", 0x79600010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_u.b", "+d,+e,+f", 0x79800010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_u.h", "+d,+e,+f", 0x79a00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_u.w", "+d,+e,+f", 0x79c00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"adds_u.d", "+d,+e,+f", 0x79e00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ave_s.b", "+d,+e,+f", 0x7a000010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ave_s.h", "+d,+e,+f", 0x7a200010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ave_s.w", "+d,+e,+f", 0x7a400010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ave_s.d", "+d,+e,+f", 0x7a600010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ave_u.b", "+d,+e,+f", 0x7a800010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ave_u.h", "+d,+e,+f", 0x7aa00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ave_u.w", "+d,+e,+f", 0x7ac00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ave_u.d", "+d,+e,+f", 0x7ae00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"aver_s.b", "+d,+e,+f", 0x7b000010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"aver_s.h", "+d,+e,+f", 0x7b200010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"aver_s.w", "+d,+e,+f", 0x7b400010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"aver_s.d", "+d,+e,+f", 0x7b600010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"aver_u.b", "+d,+e,+f", 0x7b800010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"aver_u.h", "+d,+e,+f", 0x7ba00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"aver_u.w", "+d,+e,+f", 0x7bc00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"aver_u.d", "+d,+e,+f", 0x7be00010, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subs_s.b", "+d,+e,+f", 0x78000011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subs_s.h", "+d,+e,+f", 0x78200011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subs_s.w", "+d,+e,+f", 0x78400011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subs_s.d", "+d,+e,+f", 0x78600011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subs_u.b", "+d,+e,+f", 0x78800011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subs_u.h", "+d,+e,+f", 0x78a00011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subs_u.w", "+d,+e,+f", 0x78c00011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subs_u.d", "+d,+e,+f", 0x78e00011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subsus_u.b", "+d,+e,+f", 0x79000011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subsus_u.h", "+d,+e,+f", 0x79200011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subsus_u.w", "+d,+e,+f", 0x79400011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subsus_u.d", "+d,+e,+f", 0x79600011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subsuu_s.b", "+d,+e,+f", 0x79800011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subsuu_s.h", "+d,+e,+f", 0x79a00011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subsuu_s.w", "+d,+e,+f", 0x79c00011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"subsuu_s.d", "+d,+e,+f", 0x79e00011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"asub_s.b", "+d,+e,+f", 0x7a000011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"asub_s.h", "+d,+e,+f", 0x7a200011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"asub_s.w", "+d,+e,+f", 0x7a400011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"asub_s.d", "+d,+e,+f", 0x7a600011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"asub_u.b", "+d,+e,+f", 0x7a800011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"asub_u.h", "+d,+e,+f", 0x7aa00011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"asub_u.w", "+d,+e,+f", 0x7ac00011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"asub_u.d", "+d,+e,+f", 0x7ae00011, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mulv.b", "+d,+e,+f", 0x78000012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mulv.h", "+d,+e,+f", 0x78200012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mulv.w", "+d,+e,+f", 0x78400012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mulv.d", "+d,+e,+f", 0x78600012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"maddv.b", "+d,+e,+f", 0x78800012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"maddv.h", "+d,+e,+f", 0x78a00012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"maddv.w", "+d,+e,+f", 0x78c00012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"maddv.d", "+d,+e,+f", 0x78e00012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"msubv.b", "+d,+e,+f", 0x79000012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"msubv.h", "+d,+e,+f", 0x79200012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"msubv.w", "+d,+e,+f", 0x79400012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"msubv.d", "+d,+e,+f", 0x79600012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"div_s.b", "+d,+e,+f", 0x7a000012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"div_s.h", "+d,+e,+f", 0x7a200012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"div_s.w", "+d,+e,+f", 0x7a400012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"div_s.d", "+d,+e,+f", 0x7a600012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"div_u.b", "+d,+e,+f", 0x7a800012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"div_u.h", "+d,+e,+f", 0x7aa00012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"div_u.w", "+d,+e,+f", 0x7ac00012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"div_u.d", "+d,+e,+f", 0x7ae00012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mod_s.b", "+d,+e,+f", 0x7b000012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mod_s.h", "+d,+e,+f", 0x7b200012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mod_s.w", "+d,+e,+f", 0x7b400012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mod_s.d", "+d,+e,+f", 0x7b600012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mod_u.b", "+d,+e,+f", 0x7b800012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mod_u.h", "+d,+e,+f", 0x7ba00012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mod_u.w", "+d,+e,+f", 0x7bc00012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mod_u.d", "+d,+e,+f", 0x7be00012, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dotp_s.h", "+d,+e,+f", 0x78200013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dotp_s.w", "+d,+e,+f", 0x78400013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dotp_s.d", "+d,+e,+f", 0x78600013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dotp_u.h", "+d,+e,+f", 0x78a00013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dotp_u.w", "+d,+e,+f", 0x78c00013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dotp_u.d", "+d,+e,+f", 0x78e00013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpadd_s.h", "+d,+e,+f", 0x79200013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpadd_s.w", "+d,+e,+f", 0x79400013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpadd_s.d", "+d,+e,+f", 0x79600013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpadd_u.h", "+d,+e,+f", 0x79a00013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpadd_u.w", "+d,+e,+f", 0x79c00013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpadd_u.d", "+d,+e,+f", 0x79e00013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpsub_s.h", "+d,+e,+f", 0x7a200013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpsub_s.w", "+d,+e,+f", 0x7a400013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpsub_s.d", "+d,+e,+f", 0x7a600013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpsub_u.h", "+d,+e,+f", 0x7aa00013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpsub_u.w", "+d,+e,+f", 0x7ac00013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"dpsub_u.d", "+d,+e,+f", 0x7ae00013, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"sld.b", "+d,+e[t]", 0x78000014, 0xffe0003f, WR_VD|RD_VS|RD_t, 0, MSA}, +{"sld.h", "+d,+e[t]", 0x78200014, 0xffe0003f, WR_VD|RD_VS|RD_t, 0, MSA}, +{"sld.w", "+d,+e[t]", 0x78400014, 0xffe0003f, WR_VD|RD_VS|RD_t, 0, MSA}, +{"sld.d", "+d,+e[t]", 0x78600014, 0xffe0003f, WR_VD|RD_VS|RD_t, 0, MSA}, +{"sldi.b", "+d,+e[+9]", 0x78000019, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"sldi.h", "+d,+e[+8]", 0x78200019, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"sldi.w", "+d,+e[+7]", 0x78300019, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"sldi.d", "+d,+e[+6]", 0x78380019, 0xfffc003f, WR_VD|RD_VS, 0, MSA}, +{"splat.b", "+d,+e[t]", 0x78800014, 0xffe0003f, WR_VD|RD_VS|RD_t, 0, MSA}, +{"splat.h", "+d,+e[t]", 0x78a00014, 0xffe0003f, WR_VD|RD_VS|RD_t, 0, MSA}, +{"splat.w", "+d,+e[t]", 0x78c00014, 0xffe0003f, WR_VD|RD_VS|RD_t, 0, MSA}, +{"splat.d", "+d,+e[t]", 0x78e00014, 0xffe0003f, WR_VD|RD_VS|RD_t, 0, MSA}, +{"splati.b", "+d,+e[+9]", 0x78400019, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"splati.h", "+d,+e[+8]", 0x78600019, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"splati.w", "+d,+e[+7]", 0x78700019, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"splati.d", "+d,+e[+6]", 0x78780019, 0xfffc003f, WR_VD|RD_VS, 0, MSA}, +{"pckev.b", "+d,+e,+f", 0x79000014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"pckev.h", "+d,+e,+f", 0x79200014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"pckev.w", "+d,+e,+f", 0x79400014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"pckev.d", "+d,+e,+f", 0x79600014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"pckod.b", "+d,+e,+f", 0x79800014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"pckod.h", "+d,+e,+f", 0x79a00014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"pckod.w", "+d,+e,+f", 0x79c00014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"pckod.d", "+d,+e,+f", 0x79e00014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvl.b", "+d,+e,+f", 0x7a000014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvl.h", "+d,+e,+f", 0x7a200014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvl.w", "+d,+e,+f", 0x7a400014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvl.d", "+d,+e,+f", 0x7a600014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvr.b", "+d,+e,+f", 0x7a800014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvr.h", "+d,+e,+f", 0x7aa00014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvr.w", "+d,+e,+f", 0x7ac00014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvr.d", "+d,+e,+f", 0x7ae00014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvev.b", "+d,+e,+f", 0x7b000014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvev.h", "+d,+e,+f", 0x7b200014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvev.w", "+d,+e,+f", 0x7b400014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvev.d", "+d,+e,+f", 0x7b600014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvod.b", "+d,+e,+f", 0x7b800014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvod.h", "+d,+e,+f", 0x7ba00014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvod.w", "+d,+e,+f", 0x7bc00014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ilvod.d", "+d,+e,+f", 0x7be00014, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"vshf.b", "+d,+e,+f", 0x78000015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"vshf.h", "+d,+e,+f", 0x78200015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"vshf.w", "+d,+e,+f", 0x78400015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"vshf.d", "+d,+e,+f", 0x78600015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srar.b", "+d,+e,+f", 0x78800015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srar.h", "+d,+e,+f", 0x78a00015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srar.w", "+d,+e,+f", 0x78c00015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srar.d", "+d,+e,+f", 0x78e00015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srari.b", "+d,+e,+7", 0x7970000a, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"srari.h", "+d,+e,+8", 0x7960000a, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"srari.w", "+d,+e,+9", 0x7940000a, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"srari.d", "+d,+e,'", 0x7900000a, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"srlr.b", "+d,+e,+f", 0x79000015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srlr.h", "+d,+e,+f", 0x79200015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srlr.w", "+d,+e,+f", 0x79400015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srlr.d", "+d,+e,+f", 0x79600015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"srlri.b", "+d,+e,+7", 0x79f0000a, 0xfff8003f, WR_VD|RD_VS, 0, MSA}, +{"srlri.h", "+d,+e,+8", 0x79e0000a, 0xfff0003f, WR_VD|RD_VS, 0, MSA}, +{"srlri.w", "+d,+e,+9", 0x79c0000a, 0xffe0003f, WR_VD|RD_VS, 0, MSA}, +{"srlri.d", "+d,+e,'", 0x7980000a, 0xffc0003f, WR_VD|RD_VS, 0, MSA}, +{"hadd_s.h", "+d,+e,+f", 0x7a200015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hadd_s.w", "+d,+e,+f", 0x7a400015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hadd_s.d", "+d,+e,+f", 0x7a600015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hadd_u.h", "+d,+e,+f", 0x7aa00015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hadd_u.w", "+d,+e,+f", 0x7ac00015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hadd_u.d", "+d,+e,+f", 0x7ae00015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hsub_s.h", "+d,+e,+f", 0x7b200015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hsub_s.w", "+d,+e,+f", 0x7b400015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hsub_s.d", "+d,+e,+f", 0x7b600015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hsub_u.h", "+d,+e,+f", 0x7ba00015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hsub_u.w", "+d,+e,+f", 0x7bc00015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"hsub_u.d", "+d,+e,+f", 0x7be00015, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"and.v", "+d,+e,+f", 0x7800001e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"andi.b", "+d,+e,5", 0x78000000, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"or.v", "+d,+e,+f", 0x7820001e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ori.b", "+d,+e,5", 0x79000000, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"nor.v", "+d,+e,+f", 0x7840001e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"nori.b", "+d,+e,5", 0x7a000000, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"xor.v", "+d,+e,+f", 0x7860001e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"xori.b", "+d,+e,5", 0x7b000000, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"bmnz.v", "+d,+e,+f", 0x7880001e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bmnzi.b", "+d,+e,5", 0x78000001, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"bmz.v", "+d,+e,+f", 0x78a0001e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bmzi.b", "+d,+e,5", 0x79000001, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"bsel.v", "+d,+e,+f", 0x78c0001e, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"bseli.b", "+d,+e,5", 0x7a000001, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"shf.b", "+d,+e,5", 0x78000002, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"shf.h", "+d,+e,5", 0x79000002, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"shf.w", "+d,+e,5", 0x7a000002, 0xff00003f, WR_VD|RD_VS, 0, MSA}, +{"bnz.v", "+f,p", 0x45e00000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"bz.v", "+f,p", 0x45600000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"fill.b", "+d,d", 0x7b00001e, 0xffff003f, WR_VD, RD_d, MSA}, +{"fill.h", "+d,d", 0x7b01001e, 0xffff003f, WR_VD, RD_d, MSA}, +{"fill.w", "+d,d", 0x7b02001e, 0xffff003f, WR_VD, RD_d, MSA}, +{"fill.d", "+d,d", 0x7b03001e, 0xffff003f, WR_VD, RD_d, MSA64}, +{"pcnt.b", "+d,+e", 0x7b04001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"pcnt.h", "+d,+e", 0x7b05001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"pcnt.w", "+d,+e", 0x7b06001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"pcnt.d", "+d,+e", 0x7b07001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"nloc.b", "+d,+e", 0x7b08001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"nloc.h", "+d,+e", 0x7b09001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"nloc.w", "+d,+e", 0x7b0a001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"nloc.d", "+d,+e", 0x7b0b001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"nlzc.b", "+d,+e", 0x7b0c001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"nlzc.h", "+d,+e", 0x7b0d001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"nlzc.w", "+d,+e", 0x7b0e001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"nlzc.d", "+d,+e", 0x7b0f001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"copy_s.b", "+i,+e[+9]", 0x78800019, 0xffe0003f, RD_VS, RD_rd6, MSA}, +{"copy_s.h", "+i,+e[+8]", 0x78a00019, 0xfff0003f, RD_VS, RD_rd6, MSA}, +{"copy_s.w", "+i,+e[+7]", 0x78b00019, 0xfff8003f, RD_VS, RD_rd6, MSA}, +{"copy_s.d", "+i,+e[+6]", 0x78b80019, 0xfffc003f, RD_VS, RD_rd6, MSA64}, +{"copy_u.b", "+i,+e[+9]", 0x78c00019, 0xffe0003f, RD_VS, RD_rd6, MSA}, +{"copy_u.h", "+i,+e[+8]", 0x78e00019, 0xfff0003f, RD_VS, RD_rd6, MSA}, +{"copy_u.w", "+i,+e[+7]", 0x78f00019, 0xfff8003f, RD_VS, RD_rd6, MSA}, +{"copy_u.d", "+i,+e[+6]", 0x78f80019, 0xfffc003f, RD_VS, RD_rd6, MSA64}, +{"insert.b", "+d[+9],d", 0x79000019, 0xffe0003f, WR_VD|RD_VD, RD_d, MSA}, +{"insert.h", "+d[+8],d", 0x79200019, 0xfff0003f, WR_VD|RD_VD, RD_d, MSA}, +{"insert.w", "+d[+7],d", 0x79300019, 0xfff8003f, WR_VD|RD_VD, RD_d, MSA}, +{"insert.d", "+d[+6],d", 0x79380019, 0xfffc003f, WR_VD|RD_VD, RD_d, MSA64}, +{"insve.b", "+d[+9],+e[+~]", 0x79400019, 0xffe0003f, WR_VD|RD_VD|RD_VS, 0, MSA}, +{"insve.h", "+d[+8],+e[+~]", 0x79600019, 0xfff0003f, WR_VD|RD_VD|RD_VS, 0, MSA}, +{"insve.w", "+d[+7],+e[+~]", 0x79700019, 0xfff8003f, WR_VD|RD_VD|RD_VS, 0, MSA}, +{"insve.d", "+d[+6],+e[+~]", 0x79780019, 0xfffc003f, WR_VD|RD_VD|RD_VS, 0, MSA}, +{"bnz.b", "+f,p", 0x47800000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"bnz.h", "+f,p", 0x47a00000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"bnz.w", "+f,p", 0x47c00000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"bnz.d", "+f,p", 0x47e00000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"bz.b", "+f,p", 0x47000000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"bz.h", "+f,p", 0x47200000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"bz.w", "+f,p", 0x47400000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"bz.d", "+f,p", 0x47600000, 0xffe00000, CBD|RD_VT, 0, MSA}, +{"ldi.b", "+d,+0", 0x7b000007, 0xffe0003f, WR_VD, 0, MSA}, +{"ldi.h", "+d,+0", 0x7b200007, 0xffe0003f, WR_VD, 0, MSA}, +{"ldi.w", "+d,+0", 0x7b400007, 0xffe0003f, WR_VD, 0, MSA}, +{"ldi.d", "+d,+0", 0x7b600007, 0xffe0003f, WR_VD, 0, MSA}, +{"fcaf.w", "+d,+e,+f", 0x7800001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcaf.d", "+d,+e,+f", 0x7820001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcun.w", "+d,+e,+f", 0x7840001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcun.d", "+d,+e,+f", 0x7860001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fceq.w", "+d,+e,+f", 0x7880001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fceq.d", "+d,+e,+f", 0x78a0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcueq.w", "+d,+e,+f", 0x78c0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcueq.d", "+d,+e,+f", 0x78e0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fclt.w", "+d,+e,+f", 0x7900001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fclt.d", "+d,+e,+f", 0x7920001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcult.w", "+d,+e,+f", 0x7940001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcult.d", "+d,+e,+f", 0x7960001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcle.w", "+d,+e,+f", 0x7980001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcle.d", "+d,+e,+f", 0x79a0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcule.w", "+d,+e,+f", 0x79c0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcule.d", "+d,+e,+f", 0x79e0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsaf.w", "+d,+e,+f", 0x7a00001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsaf.d", "+d,+e,+f", 0x7a20001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsun.w", "+d,+e,+f", 0x7a40001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsun.d", "+d,+e,+f", 0x7a60001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fseq.w", "+d,+e,+f", 0x7a80001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fseq.d", "+d,+e,+f", 0x7aa0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsueq.w", "+d,+e,+f", 0x7ac0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsueq.d", "+d,+e,+f", 0x7ae0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fslt.w", "+d,+e,+f", 0x7b00001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fslt.d", "+d,+e,+f", 0x7b20001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsult.w", "+d,+e,+f", 0x7b40001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsult.d", "+d,+e,+f", 0x7b60001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsle.w", "+d,+e,+f", 0x7b80001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsle.d", "+d,+e,+f", 0x7ba0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsule.w", "+d,+e,+f", 0x7bc0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsule.d", "+d,+e,+f", 0x7be0001a, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fadd.w", "+d,+e,+f", 0x7800001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fadd.d", "+d,+e,+f", 0x7820001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsub.w", "+d,+e,+f", 0x7840001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsub.d", "+d,+e,+f", 0x7860001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmul.w", "+d,+e,+f", 0x7880001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmul.d", "+d,+e,+f", 0x78a0001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fdiv.w", "+d,+e,+f", 0x78c0001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fdiv.d", "+d,+e,+f", 0x78e0001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmadd.w", "+d,+e,+f", 0x7900001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmadd.d", "+d,+e,+f", 0x7920001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmsub.w", "+d,+e,+f", 0x7940001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmsub.d", "+d,+e,+f", 0x7960001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fexp2.w", "+d,+e,+f", 0x79c0001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fexp2.d", "+d,+e,+f", 0x79e0001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fexdo.h", "+d,+e,+f", 0x7a00001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fexdo.w", "+d,+e,+f", 0x7a20001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ftq.h", "+d,+e,+f", 0x7a80001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"ftq.w", "+d,+e,+f", 0x7aa0001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmin.w", "+d,+e,+f", 0x7b00001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmin.d", "+d,+e,+f", 0x7b20001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmin_a.w", "+d,+e,+f", 0x7b40001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmin_a.d", "+d,+e,+f", 0x7b60001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmax.w", "+d,+e,+f", 0x7b80001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmax.d", "+d,+e,+f", 0x7ba0001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmax_a.w", "+d,+e,+f", 0x7bc0001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fmax_a.d", "+d,+e,+f", 0x7be0001b, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcor.w", "+d,+e,+f", 0x7840001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcor.d", "+d,+e,+f", 0x7860001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcune.w", "+d,+e,+f", 0x7880001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcune.d", "+d,+e,+f", 0x78a0001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcne.w", "+d,+e,+f", 0x78c0001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fcne.d", "+d,+e,+f", 0x78e0001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mul_q.h", "+d,+e,+f", 0x7900001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mul_q.w", "+d,+e,+f", 0x7920001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"madd_q.h", "+d,+e,+f", 0x7940001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"madd_q.w", "+d,+e,+f", 0x7960001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"msub_q.h", "+d,+e,+f", 0x7980001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"msub_q.w", "+d,+e,+f", 0x79a0001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsor.w", "+d,+e,+f", 0x7a40001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsor.d", "+d,+e,+f", 0x7a60001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsune.w", "+d,+e,+f", 0x7a80001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsune.d", "+d,+e,+f", 0x7aa0001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsne.w", "+d,+e,+f", 0x7ac0001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fsne.d", "+d,+e,+f", 0x7ae0001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mulr_q.h", "+d,+e,+f", 0x7b00001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"mulr_q.w", "+d,+e,+f", 0x7b20001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"maddr_q.h", "+d,+e,+f", 0x7b40001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"maddr_q.w", "+d,+e,+f", 0x7b60001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"msubr_q.h", "+d,+e,+f", 0x7b80001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"msubr_q.w", "+d,+e,+f", 0x7ba0001c, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, +{"fclass.w", "+d,+e", 0x7b20001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"fclass.d", "+d,+e", 0x7b21001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"fsqrt.w", "+d,+e", 0x7b26001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"fsqrt.d", "+d,+e", 0x7b27001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"frsqrt.w", "+d,+e", 0x7b28001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"frsqrt.d", "+d,+e", 0x7b29001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"frcp.w", "+d,+e", 0x7b2a001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"frcp.d", "+d,+e", 0x7b2b001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"frint.w", "+d,+e", 0x7b2c001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"frint.d", "+d,+e", 0x7b2d001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"flog2.w", "+d,+e", 0x7b2e001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"flog2.d", "+d,+e", 0x7b2f001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"fexupl.w", "+d,+e", 0x7b30001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"fexupl.d", "+d,+e", 0x7b31001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"fexupr.w", "+d,+e", 0x7b32001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"fexupr.d", "+d,+e", 0x7b33001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ffql.w", "+d,+e", 0x7b34001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ffql.d", "+d,+e", 0x7b35001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ffqr.w", "+d,+e", 0x7b36001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ffqr.d", "+d,+e", 0x7b37001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ftint_s.w", "+d,+e", 0x7b38001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ftint_s.d", "+d,+e", 0x7b39001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ftint_u.w", "+d,+e", 0x7b3a001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ftint_u.d", "+d,+e", 0x7b3b001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ffint_s.w", "+d,+e", 0x7b3c001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ffint_s.d", "+d,+e", 0x7b3d001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ffint_u.w", "+d,+e", 0x7b3e001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ffint_u.d", "+d,+e", 0x7b3f001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ftrunc_s.w", "+d,+e", 0x7b40001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ftrunc_s.d", "+d,+e", 0x7b41001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ftrunc_u.w", "+d,+e", 0x7b42001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ftrunc_u.d", "+d,+e", 0x7b43001e, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"ctcmsa", "+h,d", 0x783e0019, 0xffff003f, COD, RD_d, MSA}, +{"cfcmsa", "+i,+g", 0x787e0019, 0xffff003f, COD, 0, MSA}, +{"move.v", "+d,+e", 0x78be0019, 0xffff003f, WR_VD|RD_VS, 0, MSA}, +{"lsa", "d,v,t,+@", 0x00000005, 0xfc00073f, WR_d|RD_s|RD_t, 0, MSA}, +{"dlsa", "d,v,t,+@", 0x00000015, 0xfc00073f, WR_d|RD_s|RD_t, 0, MSA64}, + {"pref", "k,o(b)", 0xcc000000, 0xfc000000, RD_b, 0, I4|I32|G3 }, {"prefx", "h,t(b)", 0x4c00000f, 0xfc0007ff, RD_b|RD_t, 0, I4|I33 }, {"nop", "", 0x00000000, 0xffffffff, 0, INSN2_ALIAS, I1 }, /* sll */ @@ -2410,6 +2986,8 @@ const struct mips_opcode mips_builtin_opcodes[] = {"tlbp", "", 0x42000008, 0xffffffff, INSN_TLB, 0, I1 }, {"tlbr", "", 0x42000001, 0xffffffff, INSN_TLB, 0, I1 }, {"tlbwi", "", 0x42000002, 0xffffffff, INSN_TLB, 0, I1 }, +{"tlbinv", "", 0x42000003, 0xffffffff, INSN_TLB, 0, I32 }, +{"tlbinvf", "", 0x42000004, 0xffffffff, INSN_TLB, 0, I32 }, {"tlbwr", "", 0x42000006, 0xffffffff, INSN_TLB, 0, I1 }, {"tlti", "s,j", 0x040a0000, 0xfc1f0000, RD_s|TRAP, 0, I2 }, {"tlt", "s,t", 0x00000032, 0xfc00ffff, RD_s|RD_t|TRAP, 0, I2 }, @@ -2998,6 +3576,13 @@ static const char * const mips_fpr_names_64[32] = "fs0", "fs1", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7" }; +static const char * const mips_wr_names[32] = { + "w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7", + "w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15", + "w16", "w17", "w18", "w19", "w20", "w21", "w22", "w23", + "w24", "w25", "w26", "w27", "w28", "w29", "w30", "w31" +}; + static const char * const mips_cp0_names_numeric[32] = { "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7", @@ -3216,6 +3801,20 @@ static const char * const mips_hwr_names_mips3264r2[32] = "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31" }; +static const char * const mips_msa_control_names_numeric[32] = { + "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7", + "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", + "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23", + "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31" +}; + +static const char * const mips_msa_control_names_mips3264r2[32] = { + "MSAIR", "MSACSR", "$2", "$3", "$4", "$5", "$6", "$7", + "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", + "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23", + "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31" +}; + struct mips_abi_choice { const char *name; @@ -3333,7 +3932,7 @@ static const struct mips_arch_choice mips_arch_choices[] = { "mips32r2", 1, bfd_mach_mipsisa32r2, CPU_MIPS32R2, (ISA_MIPS32R2 | INSN_MIPS16 | INSN_SMARTMIPS | INSN_DSP | INSN_DSPR2 - | INSN_MIPS3D | INSN_MT), + | INSN_MIPS3D | INSN_MT | INSN_MSA), mips_cp0_names_mips3264r2, mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2), mips_hwr_names_mips3264r2 }, @@ -3687,6 +4286,89 @@ print_insn_args (const char *d, (l >> OP_SH_UDI4) & OP_MASK_UDI4); break; + case '5': /* 5-bit signed immediate in bit 16 */ + delta = ((l >> OP_SH_RT) & OP_MASK_RT); + if (delta & 0x10) { /* test sign bit */ + delta |= ~OP_MASK_RT; + } + (*info->fprintf_func) (info->stream, "%d", delta); + break; + + case '6': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_2BIT) & OP_MASK_2BIT); + break; + + case '7': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_3BIT) & OP_MASK_3BIT); + break; + + case '8': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_4BIT) & OP_MASK_4BIT); + break; + + case '9': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_5BIT) & OP_MASK_5BIT); + break; + + case ':': + (*info->fprintf_func) (info->stream, "0x%lx", + (l >> OP_SH_1BIT) & OP_MASK_1BIT); + break; + + case '!': /* 10-bit pc-relative target in bit 11 */ + delta = ((l >> OP_SH_10BIT) & OP_MASK_10BIT); + if (delta & 0x200) { /* test sign bit */ + delta |= ~OP_MASK_10BIT; + } + info->target = (delta << 2) + pc + INSNLEN; + (*info->print_address_func) (info->target, info); + break; + + case '~': + (*info->fprintf_func) (info->stream, "0"); + break; + + case '@': + (*info->fprintf_func) (info->stream, "0x%lx", + ((l >> OP_SH_1_TO_4) & OP_MASK_1_TO_4)+1); + break; + + case '^': /* 10-bit signed immediate << 0 in bit 16 */ + delta = ((l >> OP_SH_IMM10) & OP_MASK_IMM10); + if (delta & 0x200) { /* test sign bit */ + delta |= ~OP_MASK_IMM10; + } + (*info->fprintf_func) (info->stream, "%d", delta); + break; + + case '#': /* 10-bit signed immediate << 1 in bit 16 */ + delta = ((l >> OP_SH_IMM10) & OP_MASK_IMM10); + if (delta & 0x200) { /* test sign bit */ + delta |= ~OP_MASK_IMM10; + } + (*info->fprintf_func) (info->stream, "%d", delta << 1); + break; + + case '$': /* 10-bit signed immediate << 2 in bit 16 */ + delta = ((l >> OP_SH_IMM10) & OP_MASK_IMM10); + if (delta & 0x200) { /* test sign bit */ + delta |= ~OP_MASK_IMM10; + } + (*info->fprintf_func) (info->stream, "%d", delta << 2); + break; + + case '%': /* 10-bit signed immediate << 3 in bit 16 */ + delta = ((l >> OP_SH_IMM10) & OP_MASK_IMM10); + if (delta & 0x200) { /* test sign bit */ + delta |= ~OP_MASK_IMM10; + } + (*info->fprintf_func) (info->stream, "%d", delta << 3); + break; + case 'C': case 'H': msbd = (l >> OP_SH_EXTMSBD) & OP_MASK_EXTMSBD; @@ -3794,6 +4476,38 @@ print_insn_args (const char *d, break; } + case 'd': + (*info->fprintf_func) (info->stream, "%s", + mips_wr_names[(l >> OP_SH_FD) & OP_MASK_FD]); + break; + + case 'e': + (*info->fprintf_func) (info->stream, "%s", + mips_wr_names[(l >> OP_SH_FS) & OP_MASK_FS]); + break; + + case 'f': + (*info->fprintf_func) (info->stream, "%s", + mips_wr_names[(l >> OP_SH_FT) & OP_MASK_FT]); + break; + + case 'g': + (*info->fprintf_func) (info->stream, "%s", + mips_msa_control_names_mips3264r2[(l >> OP_SH_MSACR11) + & OP_MASK_MSACR11]); + break; + + case 'h': + (*info->fprintf_func) (info->stream, "%s", + mips_msa_control_names_mips3264r2[(l >> OP_SH_MSACR6) + & OP_MASK_MSACR6]); + break; + + case 'i': + (*info->fprintf_func) (info->stream, "%s", + mips_gpr_names[(l >> OP_SH_GPR) & OP_MASK_GPR]); + break; + default: /* xgettext:c-format */ (*info->fprintf_func) (info->stream, diff --git a/hw/acpi/cpu_hotplug.c b/hw/acpi/cpu_hotplug.c index 2ad83a0ede..b8ebfadc30 100644 --- a/hw/acpi/cpu_hotplug.c +++ b/hw/acpi/cpu_hotplug.c @@ -36,28 +36,40 @@ static const MemoryRegionOps AcpiCpuHotplug_ops = { }, }; -void AcpiCpuHotplug_add(ACPIGPE *gpe, AcpiCpuHotplug *g, CPUState *cpu) +static void acpi_set_cpu_present_bit(AcpiCpuHotplug *g, CPUState *cpu, + Error **errp) { CPUClass *k = CPU_GET_CLASS(cpu); int64_t cpu_id; - *gpe->sts = *gpe->sts | ACPI_CPU_HOTPLUG_STATUS; - cpu_id = k->get_arch_id(CPU(cpu)); - g_assert((cpu_id / 8) < ACPI_GPE_PROC_LEN); + cpu_id = k->get_arch_id(cpu); + if ((cpu_id / 8) >= ACPI_GPE_PROC_LEN) { + error_setg(errp, "acpi: invalid cpu id: %" PRIi64, cpu_id); + return; + } + g->sts[cpu_id / 8] |= (1 << (cpu_id % 8)); } -void AcpiCpuHotplug_init(MemoryRegion *parent, Object *owner, - AcpiCpuHotplug *gpe_cpu, uint16_t base) +void acpi_cpu_plug_cb(ACPIREGS *ar, qemu_irq irq, + AcpiCpuHotplug *g, DeviceState *dev, Error **errp) +{ + acpi_set_cpu_present_bit(g, CPU(dev), errp); + if (*errp != NULL) { + return; + } + + ar->gpe.sts[0] |= ACPI_CPU_HOTPLUG_STATUS; + acpi_update_sci(ar, irq); +} + +void acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner, + AcpiCpuHotplug *gpe_cpu, uint16_t base) { CPUState *cpu; CPU_FOREACH(cpu) { - CPUClass *cc = CPU_GET_CLASS(cpu); - int64_t id = cc->get_arch_id(cpu); - - g_assert((id / 8) < ACPI_GPE_PROC_LEN); - gpe_cpu->sts[id / 8] |= (1 << (id % 8)); + acpi_set_cpu_present_bit(gpe_cpu, cpu, &error_abort); } memory_region_init_io(&gpe_cpu->io, owner, &AcpiCpuHotplug_ops, gpe_cpu, "acpi-cpu-hotplug", ACPI_GPE_PROC_LEN); diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 7b14bbbee1..ea991a3c65 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -209,15 +209,6 @@ static void pm_powerdown_req(Notifier *n, void *opaque) acpi_pm1_evt_power_down(&pm->acpi_regs); } -static void ich9_cpu_added_req(Notifier *n, void *opaque) -{ - ICH9LPCPMRegs *pm = container_of(n, ICH9LPCPMRegs, cpu_added_notifier); - - assert(pm != NULL); - AcpiCpuHotplug_add(&pm->acpi_regs.gpe, &pm->gpe_cpu, CPU(opaque)); - acpi_update_sci(&pm->acpi_regs, pm->irq); -} - void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, qemu_irq sci_irq) { @@ -244,10 +235,8 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, pm->powerdown_notifier.notify = pm_powerdown_req; qemu_register_powerdown_notifier(&pm->powerdown_notifier); - AcpiCpuHotplug_init(pci_address_space_io(lpc_pci), OBJECT(lpc_pci), - &pm->gpe_cpu, ICH9_CPU_HOTPLUG_IO_BASE); - pm->cpu_added_notifier.notify = ich9_cpu_added_req; - qemu_register_cpu_added_notifier(&pm->cpu_added_notifier); + acpi_cpu_hotplug_init(pci_address_space_io(lpc_pci), OBJECT(lpc_pci), + &pm->gpe_cpu, ICH9_CPU_HOTPLUG_IO_BASE); if (pm->acpi_memory_hotplug.is_enabled) { acpi_memory_hotplug_init(pci_address_space_io(lpc_pci), OBJECT(lpc_pci), @@ -304,6 +293,8 @@ void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error **errp) object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { acpi_memory_plug_cb(&pm->acpi_regs, pm->irq, &pm->acpi_memory_hotplug, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + acpi_cpu_plug_cb(&pm->acpi_regs, pm->irq, &pm->gpe_cpu, dev, errp); } else { error_setg(errp, "acpi: device plug request for not supported device" " type: %s", object_get_typename(OBJECT(dev))); diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 0bfa814f71..78c0a6d323 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -83,7 +83,6 @@ typedef struct PIIX4PMState { uint8_t s4_val; AcpiCpuHotplug gpe_cpu; - Notifier cpu_added_notifier; MemHotplugState acpi_memory_hotplug; } PIIX4PMState; @@ -348,6 +347,8 @@ static void piix4_device_plug_cb(HotplugHandler *hotplug_dev, } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { acpi_pcihp_device_plug_cb(&s->ar, s->irq, &s->acpi_pci_hotplug, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + acpi_cpu_plug_cb(&s->ar, s->irq, &s->gpe_cpu, dev, errp); } else { error_setg(errp, "acpi: device plug request for not supported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -544,15 +545,6 @@ static const MemoryRegionOps piix4_gpe_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static void piix4_cpu_added_req(Notifier *n, void *opaque) -{ - PIIX4PMState *s = container_of(n, PIIX4PMState, cpu_added_notifier); - - assert(s != NULL); - AcpiCpuHotplug_add(&s->ar.gpe, &s->gpe_cpu, CPU(opaque)); - acpi_update_sci(&s->ar, s->irq); -} - static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, PCIBus *bus, PIIX4PMState *s) { @@ -563,10 +555,8 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, acpi_pcihp_init(&s->acpi_pci_hotplug, bus, parent, s->use_acpi_pci_hotplug); - AcpiCpuHotplug_init(parent, OBJECT(s), &s->gpe_cpu, - PIIX4_CPU_HOTPLUG_IO_BASE); - s->cpu_added_notifier.notify = piix4_cpu_added_req; - qemu_register_cpu_added_notifier(&s->cpu_added_notifier); + acpi_cpu_hotplug_init(parent, OBJECT(s), &s->gpe_cpu, + PIIX4_CPU_HOTPLUG_IO_BASE); if (s->acpi_memory_hotplug.is_enabled) { acpi_memory_hotplug_init(parent, OBJECT(s), &s->acpi_memory_hotplug); diff --git a/hw/arm/boot.c b/hw/arm/boot.c index bffbea5e0e..0014c34ddd 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -580,7 +580,7 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(info->kernel_filename, &entry, NULL, - &is_linux); + &is_linux, NULL, NULL); } /* On aarch64, it's the bootloader's job to uncompress the kernel. */ if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && kernel_size < 0) { diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index 45b1164c3e..1222a37f4f 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -196,6 +196,11 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, blk_op_block_all(conf->conf.blk, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_RESIZE, s->blocker); blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker); + blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_BACKUP_SOURCE, s->blocker); + blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT, s->blocker); + blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_MIRROR, s->blocker); + blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_STREAM, s->blocker); + blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_REPLACE, s->blocker); *dataplane = s; } diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index 80dd0a9e13..a9f5e62f24 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -52,7 +52,8 @@ typedef struct SCLPConsoleLM { * event_pending is set when a newline character is encountered * * The maximum command line length is limited by the maximum - * space available in an SCCB + * space available in an SCCB. Line mode console input is sent + * truncated to the guest in case it doesn't fit into the SCCB. */ static int chr_can_read(void *opaque) @@ -61,10 +62,8 @@ static int chr_can_read(void *opaque) if (scon->event.event_pending) { return 0; - } else if (SIZE_CONSOLE_BUFFER - scon->length) { - return 1; } - return 0; + return 1; } static void chr_read(void *opaque, const uint8_t *buf, int size) @@ -78,6 +77,10 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) sclp_service_interrupt(0); return; } + if (scon->length == SIZE_CONSOLE_BUFFER) { + /* Eat the character, but still process CR and LF. */ + return; + } scon->buf[scon->length] = *buf; scon->length += 1; if (scon->echo) { @@ -125,6 +128,7 @@ static int get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size, cons->length = 0; /* data provided and no more data pending */ event->event_pending = false; + qemu_notify_event(); return 0; } diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c index fca105db4e..79891dfc58 100644 --- a/hw/char/sclpconsole.c +++ b/hw/char/sclpconsole.c @@ -36,6 +36,7 @@ typedef struct SCLPConsole { uint32_t iov_bs; /* offset in buf for char layer read operation */ uint32_t iov_data_len; /* length of byte stream in buffer */ uint32_t iov_sclp_rest; /* length of byte stream not read via SCLP */ + bool notify; /* qemu_notify_event() req'd if true */ } SCLPConsole; /* character layer call-back functions */ @@ -44,8 +45,12 @@ typedef struct SCLPConsole { static int chr_can_read(void *opaque) { SCLPConsole *scon = opaque; + int avail = SIZE_BUFFER_VT220 - scon->iov_data_len; - return SIZE_BUFFER_VT220 - scon->iov_data_len; + if (avail == 0) { + scon->notify = true; + } + return avail; } /* Send data from a char device over to the guest */ @@ -113,6 +118,10 @@ static void get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size, cons->iov_sclp += avail; /* more data pending */ } + if (cons->notify) { + cons->notify = false; + qemu_notify_event(); + } } static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, @@ -229,6 +238,7 @@ static void console_reset(DeviceState *dev) scon->iov_bs = 0; scon->iov_data_len = 0; scon->iov_sclp_rest = 0; + scon->notify = false; } static int console_exit(SCLPEvent *event) diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index c6870f19e1..a7b1b68984 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -871,7 +871,7 @@ static void virtser_port_device_realize(DeviceState *dev, Error **errp) return; } - if (find_port_by_name(port->name)) { + if (port->name != NULL && find_port_by_name(port->name)) { error_setg(errp, "virtio-serial-bus: A port already exists by name %s", port->name); return; diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs index 17845df3f0..9dce1bc53c 100644 --- a/hw/core/Makefile.objs +++ b/hw/core/Makefile.objs @@ -14,3 +14,4 @@ common-obj-$(CONFIG_SOFTMMU) += machine.o common-obj-$(CONFIG_SOFTMMU) += null-machine.o common-obj-$(CONFIG_SOFTMMU) += loader.o common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o +common-obj-$(CONFIG_SOFTMMU) += platform-bus.o diff --git a/hw/core/loader.c b/hw/core/loader.c index 5f3a8598c5..bbe6eb3d82 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -477,7 +477,9 @@ static ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, /* Load a U-Boot image. */ static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr, - int *is_linux, uint8_t image_type) + int *is_linux, uint8_t image_type, + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque) { int fd; int size; @@ -511,6 +513,9 @@ static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr, switch (hdr->ih_type) { case IH_TYPE_KERNEL: address = hdr->ih_load; + if (translate_fn) { + address = translate_fn(translate_opaque, address); + } if (loadaddr) { *loadaddr = hdr->ih_load; } @@ -587,15 +592,19 @@ out: } int load_uimage(const char *filename, hwaddr *ep, hwaddr *loadaddr, - int *is_linux) + int *is_linux, + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque) { - return load_uboot_image(filename, ep, loadaddr, is_linux, IH_TYPE_KERNEL); + return load_uboot_image(filename, ep, loadaddr, is_linux, IH_TYPE_KERNEL, + translate_fn, translate_opaque); } /* Load a ramdisk. */ int load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz) { - return load_uboot_image(filename, NULL, &addr, NULL, IH_TYPE_RAMDISK); + return load_uboot_image(filename, NULL, &addr, NULL, IH_TYPE_RAMDISK, + NULL, NULL); } /* This simply prevents g_malloc in the function below from allocating diff --git a/hw/core/machine.c b/hw/core/machine.c index 7f3418c5af..19d3e3a707 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -12,6 +12,9 @@ #include "hw/boards.h" #include "qapi/visitor.h" +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "qemu/error-report.h" static char *machine_get_accel(Object *obj, Error **errp) { @@ -257,8 +260,35 @@ static void machine_set_iommu(Object *obj, bool value, Error **errp) ms->iommu = value; } +static int error_on_sysbus_device(SysBusDevice *sbdev, void *opaque) +{ + error_report("Option '-device %s' cannot be handled by this machine", + object_class_get_name(object_get_class(OBJECT(sbdev)))); + exit(1); +} + +static void machine_init_notify(Notifier *notifier, void *data) +{ + Object *machine = qdev_get_machine(); + ObjectClass *oc = object_get_class(machine); + MachineClass *mc = MACHINE_CLASS(oc); + + if (mc->has_dynamic_sysbus) { + /* Our machine can handle dynamic sysbus devices, we're all good */ + return; + } + + /* + * Loop through all dynamically created devices and check whether there + * are sysbus devices among them. If there are, error out. + */ + foreach_dynamic_sysbus_device(error_on_sysbus_device, NULL); +} + static void machine_initfn(Object *obj) { + MachineState *ms = MACHINE(obj); + object_property_add_str(obj, "accel", machine_get_accel, machine_set_accel, NULL); object_property_add_bool(obj, "kernel-irqchip", @@ -303,6 +333,10 @@ static void machine_initfn(Object *obj) object_property_add_bool(obj, "iommu", machine_get_iommu, machine_set_iommu, NULL); + + /* Register notifier when init is done for sysbus sanity checks */ + ms->sysbus_notifier.notify = machine_init_notify; + qemu_add_machine_init_done_notifier(&ms->sysbus_notifier); } static void machine_finalize(Object *obj) diff --git a/hw/core/platform-bus.c b/hw/core/platform-bus.c new file mode 100644 index 0000000000..0f052b3338 --- /dev/null +++ b/hw/core/platform-bus.c @@ -0,0 +1,253 @@ +/* + * Platform Bus device to support dynamic Sysbus devices + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Alexander Graf, <agraf@suse.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "hw/platform-bus.h" +#include "monitor/monitor.h" +#include "exec/address-spaces.h" +#include "sysemu/sysemu.h" + + +/* + * Returns the PlatformBus IRQ number for a SysBusDevice irq number or -1 if + * the IRQ is not mapped on this Platform bus. + */ +int platform_bus_get_irqn(PlatformBusDevice *pbus, SysBusDevice *sbdev, + int n) +{ + qemu_irq sbirq = sysbus_get_connected_irq(sbdev, n); + int i; + + for (i = 0; i < pbus->num_irqs; i++) { + if (pbus->irqs[i] == sbirq) { + return i; + } + } + + /* IRQ not mapped on platform bus */ + return -1; +} + +/* + * Returns the PlatformBus MMIO region offset for Region n of a SysBusDevice or + * -1 if the region is not mapped on this Platform bus. + */ +hwaddr platform_bus_get_mmio_addr(PlatformBusDevice *pbus, SysBusDevice *sbdev, + int n) +{ + MemoryRegion *pbus_mr = &pbus->mmio; + MemoryRegion *sbdev_mr = sysbus_mmio_get_region(sbdev, n); + Object *pbus_mr_obj = OBJECT(pbus_mr); + Object *parent_mr; + + if (!memory_region_is_mapped(sbdev_mr)) { + /* Region is not mapped? */ + return -1; + } + + parent_mr = object_property_get_link(OBJECT(sbdev_mr), "container", NULL); + + assert(parent_mr); + if (parent_mr != pbus_mr_obj) { + /* MMIO region is not mapped on platform bus */ + return -1; + } + + return object_property_get_int(OBJECT(sbdev_mr), "addr", NULL); +} + +static int platform_bus_count_irqs(SysBusDevice *sbdev, void *opaque) +{ + PlatformBusDevice *pbus = opaque; + qemu_irq sbirq; + int n, i; + + for (n = 0; ; n++) { + if (!sysbus_has_irq(sbdev, n)) { + break; + } + + sbirq = sysbus_get_connected_irq(sbdev, n); + for (i = 0; i < pbus->num_irqs; i++) { + if (pbus->irqs[i] == sbirq) { + bitmap_set(pbus->used_irqs, i, 1); + break; + } + } + } + + return 0; +} + +/* + * Loop through all sysbus devices and look for unassigned IRQ lines as well as + * unassociated MMIO regions. Connect them to the platform bus if available. + */ +static void plaform_bus_refresh_irqs(PlatformBusDevice *pbus) +{ + bitmap_zero(pbus->used_irqs, pbus->num_irqs); + foreach_dynamic_sysbus_device(platform_bus_count_irqs, pbus); + pbus->done_gathering = true; +} + +static int platform_bus_map_irq(PlatformBusDevice *pbus, SysBusDevice *sbdev, + int n) +{ + int max_irqs = pbus->num_irqs; + int irqn; + + if (sysbus_is_irq_connected(sbdev, n)) { + /* IRQ is already mapped, nothing to do */ + return 0; + } + + irqn = find_first_zero_bit(pbus->used_irqs, max_irqs); + if (irqn >= max_irqs) { + hw_error("Platform Bus: Can not fit IRQ line"); + return -1; + } + + set_bit(irqn, pbus->used_irqs); + sysbus_connect_irq(sbdev, n, pbus->irqs[irqn]); + + return 0; +} + +static int platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev, + int n) +{ + MemoryRegion *sbdev_mr = sysbus_mmio_get_region(sbdev, n); + uint64_t size = memory_region_size(sbdev_mr); + uint64_t alignment = (1ULL << (63 - clz64(size + size - 1))); + uint64_t off; + bool found_region = false; + + if (memory_region_is_mapped(sbdev_mr)) { + /* Region is already mapped, nothing to do */ + return 0; + } + + /* + * Look for empty space in the MMIO space that is naturally aligned with + * the target device's memory region + */ + for (off = 0; off < pbus->mmio_size; off += alignment) { + if (!memory_region_find(&pbus->mmio, off, size).mr) { + found_region = true; + break; + } + } + + if (!found_region) { + hw_error("Platform Bus: Can not fit MMIO region of size %"PRIx64, size); + } + + /* Map the device's region into our Platform Bus MMIO space */ + memory_region_add_subregion(&pbus->mmio, off, sbdev_mr); + + return 0; +} + +/* + * For each sysbus device, look for unassigned IRQ lines as well as + * unassociated MMIO regions. Connect them to the platform bus if available. + */ +static int link_sysbus_device(SysBusDevice *sbdev, void *opaque) +{ + PlatformBusDevice *pbus = opaque; + int i; + + for (i = 0; sysbus_has_irq(sbdev, i); i++) { + platform_bus_map_irq(pbus, sbdev, i); + } + + for (i = 0; sysbus_has_mmio(sbdev, i); i++) { + platform_bus_map_mmio(pbus, sbdev, i); + } + + return 0; +} + +static void platform_bus_init_notify(Notifier *notifier, void *data) +{ + PlatformBusDevice *pb = container_of(notifier, PlatformBusDevice, notifier); + + /* + * Generate a bitmap of used IRQ lines, as the user might have specified + * them on the command line. + */ + plaform_bus_refresh_irqs(pb); + + foreach_dynamic_sysbus_device(link_sysbus_device, pb); +} + +static void platform_bus_realize(DeviceState *dev, Error **errp) +{ + PlatformBusDevice *pbus; + SysBusDevice *d; + int i; + + d = SYS_BUS_DEVICE(dev); + pbus = PLATFORM_BUS_DEVICE(dev); + + memory_region_init(&pbus->mmio, NULL, "platform bus", pbus->mmio_size); + sysbus_init_mmio(d, &pbus->mmio); + + pbus->used_irqs = bitmap_new(pbus->num_irqs); + pbus->irqs = g_new0(qemu_irq, pbus->num_irqs); + for (i = 0; i < pbus->num_irqs; i++) { + sysbus_init_irq(d, &pbus->irqs[i]); + } + + /* + * Register notifier that allows us to gather dangling devices once the + * machine is completely assembled + */ + pbus->notifier.notify = platform_bus_init_notify; + qemu_add_machine_init_done_notifier(&pbus->notifier); +} + +static Property platform_bus_properties[] = { + DEFINE_PROP_UINT32("num_irqs", PlatformBusDevice, num_irqs, 0), + DEFINE_PROP_UINT32("mmio_size", PlatformBusDevice, mmio_size, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void platform_bus_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = platform_bus_realize; + dc->props = platform_bus_properties; +} + +static const TypeInfo platform_bus_info = { + .name = TYPE_PLATFORM_BUS_DEVICE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PlatformBusDevice), + .class_init = platform_bus_class_init, +}; + +static void platform_bus_register_types(void) +{ + type_register_static(&platform_bus_info); +} + +type_init(platform_bus_register_types) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index b3d519645a..413b41376f 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -453,6 +453,17 @@ void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n, g_free(propname); } +qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n) +{ + char *propname = g_strdup_printf("%s[%d]", + name ? name : "unnamed-gpio-out", n); + + qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname, + NULL); + + return ret; +} + /* disconnect a GPIO ouput, returning the disconnected input (if any) */ static qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev, diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index e55c3c1d6d..84af59379d 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -24,6 +24,51 @@ static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent); static char *sysbus_get_fw_dev_path(DeviceState *dev); +typedef struct SysBusFind { + void *opaque; + FindSysbusDeviceFunc *func; +} SysBusFind; + +/* Run func() for every sysbus device, traverse the tree for everything else */ +static int find_sysbus_device(Object *obj, void *opaque) +{ + SysBusFind *find = opaque; + Object *dev; + SysBusDevice *sbdev; + + dev = object_dynamic_cast(obj, TYPE_SYS_BUS_DEVICE); + sbdev = (SysBusDevice *)dev; + + if (!sbdev) { + /* Container, traverse it for children */ + return object_child_foreach(obj, find_sysbus_device, opaque); + } + + find->func(sbdev, find->opaque); + + return 0; +} + +/* + * Loop through all dynamically created sysbus devices and call + * func() for each instance. + */ +void foreach_dynamic_sysbus_device(FindSysbusDeviceFunc *func, void *opaque) +{ + Object *container; + SysBusFind find = { + .func = func, + .opaque = opaque, + }; + + /* Loop through all sysbus devices that were spawened outside the machine */ + container = container_get(qdev_get_machine(), "/peripheral"); + find_sysbus_device(container, &find); + container = container_get(qdev_get_machine(), "/peripheral-anon"); + find_sysbus_device(container, &find); +} + + static void system_bus_class_init(ObjectClass *klass, void *data) { BusClass *k = BUS_CLASS(klass); @@ -39,11 +84,38 @@ static const TypeInfo system_bus_info = { .class_init = system_bus_class_init, }; +/* Check whether an IRQ source exists */ +bool sysbus_has_irq(SysBusDevice *dev, int n) +{ + char *prop = g_strdup_printf("%s[%d]", SYSBUS_DEVICE_GPIO_IRQ, n); + ObjectProperty *r; + + r = object_property_find(OBJECT(dev), prop, NULL); + return (r != NULL); +} + +bool sysbus_is_irq_connected(SysBusDevice *dev, int n) +{ + return !!sysbus_get_connected_irq(dev, n); +} + +qemu_irq sysbus_get_connected_irq(SysBusDevice *dev, int n) +{ + DeviceState *d = DEVICE(dev); + return qdev_get_gpio_out_connector(d, SYSBUS_DEVICE_GPIO_IRQ, n); +} + void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq) { qdev_connect_gpio_out_named(DEVICE(dev), SYSBUS_DEVICE_GPIO_IRQ, n, irq); } +/* Check whether an MMIO region exists */ +bool sysbus_has_mmio(SysBusDevice *dev, unsigned int n) +{ + return (n < dev->num_mmio); +} + static void sysbus_mmio_map_common(SysBusDevice *dev, int n, hwaddr addr, bool may_overlap, int priority) { @@ -238,13 +310,6 @@ static void sysbus_device_class_init(ObjectClass *klass, void *data) DeviceClass *k = DEVICE_CLASS(klass); k->init = sysbus_device_init; k->bus_type = TYPE_SYSTEM_BUS; - /* - * device_add plugs devices into suitable bus. For "real" buses, - * that actually connects the device. For sysbus, the connections - * need to be made separately, and device_add can't do that. The - * device would be left unconnected, and could not possibly work. - */ - k->cannot_instantiate_with_device_add_yet = true; } static const TypeInfo sysbus_device_type_info = { diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs index 2c8b51f09a..1abcf17988 100644 --- a/hw/gpio/Makefile.objs +++ b/hw/gpio/Makefile.objs @@ -2,5 +2,6 @@ common-obj-$(CONFIG_MAX7310) += max7310.o common-obj-$(CONFIG_PL061) += pl061.o common-obj-$(CONFIG_PUV3) += puv3_gpio.o common-obj-$(CONFIG_ZAURUS) += zaurus.o +common-obj-$(CONFIG_E500) += mpc8xxx.o obj-$(CONFIG_OMAP) += omap_gpio.o diff --git a/hw/gpio/mpc8xxx.c b/hw/gpio/mpc8xxx.c new file mode 100644 index 0000000000..1aeaaaaf03 --- /dev/null +++ b/hw/gpio/mpc8xxx.c @@ -0,0 +1,217 @@ +/* + * GPIO Controller for a lot of Freescale SoCs + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Alexander Graf, <agraf@suse.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "hw/sysbus.h" + +#define TYPE_MPC8XXX_GPIO "mpc8xxx_gpio" +#define MPC8XXX_GPIO(obj) OBJECT_CHECK(MPC8XXXGPIOState, (obj), TYPE_MPC8XXX_GPIO) + +typedef struct MPC8XXXGPIOState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + qemu_irq irq; + qemu_irq out[32]; + + uint32_t dir; + uint32_t odr; + uint32_t dat; + uint32_t ier; + uint32_t imr; + uint32_t icr; +} MPC8XXXGPIOState; + +static const VMStateDescription vmstate_mpc8xxx_gpio = { + .name = "mpc8xxx_gpio", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(dir, MPC8XXXGPIOState), + VMSTATE_UINT32(odr, MPC8XXXGPIOState), + VMSTATE_UINT32(dat, MPC8XXXGPIOState), + VMSTATE_UINT32(ier, MPC8XXXGPIOState), + VMSTATE_UINT32(imr, MPC8XXXGPIOState), + VMSTATE_UINT32(icr, MPC8XXXGPIOState), + VMSTATE_END_OF_LIST() + } +}; + +static void mpc8xxx_gpio_update(MPC8XXXGPIOState *s) +{ + qemu_set_irq(s->irq, !!(s->ier & s->imr)); +} + +static uint64_t mpc8xxx_gpio_read(void *opaque, hwaddr offset, + unsigned size) +{ + MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque; + + if (size != 4) { + /* All registers are 32bit */ + return 0; + } + + switch (offset) { + case 0x0: /* Direction */ + return s->dir; + case 0x4: /* Open Drain */ + return s->odr; + case 0x8: /* Data */ + return s->dat; + case 0xC: /* Interrupt Event */ + return s->ier; + case 0x10: /* Interrupt Mask */ + return s->imr; + case 0x14: /* Interrupt Control */ + return s->icr; + default: + return 0; + } +} + +static void mpc8xxx_write_data(MPC8XXXGPIOState *s, uint32_t new_data) +{ + uint32_t old_data = s->dat; + uint32_t diff = old_data ^ new_data; + int i; + + for (i = 0; i < 32; i++) { + uint32_t mask = 0x80000000 >> i; + if (!(diff & mask)) { + continue; + } + + if (s->dir & mask) { + /* Output */ + qemu_set_irq(s->out[i], (new_data & mask) != 0); + } + } + + s->dat = new_data; +} + +static void mpc8xxx_gpio_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque; + + if (size != 4) { + /* All registers are 32bit */ + return; + } + + switch (offset) { + case 0x0: /* Direction */ + s->dir = value; + break; + case 0x4: /* Open Drain */ + s->odr = value; + break; + case 0x8: /* Data */ + mpc8xxx_write_data(s, value); + break; + case 0xC: /* Interrupt Event */ + s->ier &= ~value; + break; + case 0x10: /* Interrupt Mask */ + s->imr = value; + break; + case 0x14: /* Interrupt Control */ + s->icr = value; + break; + } + + mpc8xxx_gpio_update(s); +} + +static void mpc8xxx_gpio_reset(MPC8XXXGPIOState *s) +{ + s->dir = 0; + s->odr = 0; + s->dat = 0; + s->ier = 0; + s->imr = 0; + s->icr = 0; +} + +static void mpc8xxx_gpio_set_irq(void * opaque, int irq, int level) +{ + MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque; + uint32_t mask; + + mask = 0x80000000 >> irq; + if ((s->dir & mask) == 0) { + uint32_t old_value = s->dat & mask; + + s->dat &= ~mask; + if (level) + s->dat |= mask; + + if (!(s->icr & irq) || (old_value && !level)) { + s->ier |= mask; + } + + mpc8xxx_gpio_update(s); + } +} + +static const MemoryRegionOps mpc8xxx_gpio_ops = { + .read = mpc8xxx_gpio_read, + .write = mpc8xxx_gpio_write, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static int mpc8xxx_gpio_initfn(SysBusDevice *sbd) +{ + DeviceState *dev = DEVICE(sbd); + MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &mpc8xxx_gpio_ops, s, "mpc8xxx_gpio", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); + qdev_init_gpio_in(dev, mpc8xxx_gpio_set_irq, 32); + qdev_init_gpio_out(dev, s->out, 32); + mpc8xxx_gpio_reset(s); + return 0; +} + +static void mpc8xxx_gpio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = mpc8xxx_gpio_initfn; + dc->vmsd = &vmstate_mpc8xxx_gpio; +} + +static const TypeInfo mpc8xxx_gpio_info = { + .name = TYPE_MPC8XXX_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MPC8XXXGPIOState), + .class_init = mpc8xxx_gpio_class_init, +}; + +static void mpc8xxx_gpio_register_types(void) +{ + type_register_static(&mpc8xxx_gpio_info); +} + +type_init(mpc8xxx_gpio_register_types) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 00be4bb12a..4003b6bf9b 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -249,6 +249,7 @@ static void acpi_get_pci_info(PcPciInfo *info) #define ACPI_BUILD_TABLE_FILE "etc/acpi/tables" #define ACPI_BUILD_RSDP_FILE "etc/acpi/rsdp" +#define ACPI_BUILD_TPMLOG_FILE "etc/tpm/log" static void build_header(GArray *linker, GArray *table_data, @@ -1214,27 +1215,28 @@ build_hpet(GArray *table_data, GArray *linker) } static void -build_tpm_tcpa(GArray *table_data, GArray *linker) +build_tpm_tcpa(GArray *table_data, GArray *linker, GArray *tcpalog) { Acpi20Tcpa *tcpa = acpi_data_push(table_data, sizeof *tcpa); - /* the log area will come right after the TCPA table */ - uint64_t log_area_start_address = acpi_data_len(table_data); + uint64_t log_area_start_address = acpi_data_len(tcpalog); tcpa->platform_class = cpu_to_le16(TPM_TCPA_ACPI_CLASS_CLIENT); tcpa->log_area_minimum_length = cpu_to_le32(TPM_LOG_AREA_MINIMUM_SIZE); tcpa->log_area_start_address = cpu_to_le64(log_area_start_address); + bios_linker_loader_alloc(linker, ACPI_BUILD_TPMLOG_FILE, 1, + false /* high memory */); + /* log area start address to be filled by Guest linker */ bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, - ACPI_BUILD_TABLE_FILE, + ACPI_BUILD_TPMLOG_FILE, table_data, &tcpa->log_area_start_address, sizeof(tcpa->log_area_start_address)); build_header(linker, table_data, (void *)tcpa, "TCPA", sizeof(*tcpa), 2); - /* now only get the log area and with that modify table_data */ - acpi_data_push(table_data, TPM_LOG_AREA_MINIMUM_SIZE); + acpi_data_push(tcpalog, TPM_LOG_AREA_MINIMUM_SIZE); } static void @@ -1485,6 +1487,7 @@ typedef struct AcpiBuildTables { GArray *table_data; GArray *rsdp; + GArray *tcpalog; GArray *linker; } AcpiBuildTables; @@ -1492,17 +1495,17 @@ static inline void acpi_build_tables_init(AcpiBuildTables *tables) { tables->rsdp = g_array_new(false, true /* clear */, 1); tables->table_data = g_array_new(false, true /* clear */, 1); + tables->tcpalog = g_array_new(false, true /* clear */, 1); tables->linker = bios_linker_loader_init(); } static inline void acpi_build_tables_cleanup(AcpiBuildTables *tables, bool mfre) { void *linker_data = bios_linker_loader_cleanup(tables->linker); - if (mfre) { - g_free(linker_data); - } + g_free(linker_data); g_array_free(tables->rsdp, mfre); - g_array_free(tables->table_data, mfre); + g_array_free(tables->table_data, true); + g_array_free(tables->tcpalog, mfre); } typedef @@ -1612,7 +1615,7 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) } if (misc.has_tpm) { acpi_add_table(table_offsets, tables->table_data); - build_tpm_tcpa(tables->table_data, tables->linker); + build_tpm_tcpa(tables->table_data, tables->linker, tables->tcpalog); acpi_add_table(table_offsets, tables->table_data); build_tpm_ssdt(tables->table_data, tables->linker); @@ -1778,6 +1781,9 @@ void acpi_setup(PcGuestInfo *guest_info) acpi_add_rom_blob(NULL, tables.linker, "etc/table-loader"); + fw_cfg_add_file(guest_info->fw_cfg, ACPI_BUILD_TPMLOG_FILE, + tables.tcpalog->data, acpi_data_len(tables.tcpalog)); + /* * RSDP is small so it's easy to keep it immutable, no need to * bother with ROM blobs. diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 889e888c6a..1205db83bc 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -355,30 +355,15 @@ static void pc_cmos_init_late(void *opaque) qemu_unregister_reset(pc_cmos_init_late, opaque); } -typedef struct RTCCPUHotplugArg { - Notifier cpu_added_notifier; - ISADevice *rtc_state; -} RTCCPUHotplugArg; - -static void rtc_notify_cpu_added(Notifier *notifier, void *data) -{ - RTCCPUHotplugArg *arg = container_of(notifier, RTCCPUHotplugArg, - cpu_added_notifier); - ISADevice *s = arg->rtc_state; - - /* increment the number of CPUs */ - rtc_set_memory(s, 0x5f, rtc_get_memory(s, 0x5f) + 1); -} - void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, - const char *boot_device, + const char *boot_device, MachineState *machine, ISADevice *floppy, BusState *idebus0, BusState *idebus1, ISADevice *s) { int val, nb, i; FDriveType fd_type[2] = { FDRIVE_DRV_NONE, FDRIVE_DRV_NONE }; static pc_cmos_init_late_arg arg; - static RTCCPUHotplugArg cpu_hotplug_cb; + PCMachineState *pc_machine = PC_MACHINE(machine); /* various important CMOS locations needed by PC/Bochs bios */ @@ -417,10 +402,14 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, /* set the number of CPU */ rtc_set_memory(s, 0x5f, smp_cpus - 1); - /* init CPU hotplug notifier */ - cpu_hotplug_cb.rtc_state = s; - cpu_hotplug_cb.cpu_added_notifier.notify = rtc_notify_cpu_added; - qemu_register_cpu_added_notifier(&cpu_hotplug_cb.cpu_added_notifier); + + object_property_add_link(OBJECT(machine), "rtc_state", + TYPE_ISA_DEVICE, + (Object **)&pc_machine->rtc, + object_property_allow_set_link, + OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); + object_property_set_link(OBJECT(machine), OBJECT(s), + "rtc_state", &error_abort); if (set_boot_dev(s, boot_device)) { exit(1); @@ -1516,6 +1505,7 @@ static void pc_generic_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); QEMUMachine *qm = data; + mc->family = qm->family; mc->name = qm->name; mc->alias = qm->alias; mc->desc = qm->desc; @@ -1536,6 +1526,7 @@ static void pc_generic_machine_class_init(ObjectClass *oc, void *data) mc->is_default = qm->is_default; mc->default_machine_opts = qm->default_machine_opts; mc->default_boot_order = qm->default_boot_order; + mc->default_display = qm->default_display; mc->compat_props = qm->compat_props; mc->hw_version = qm->hw_version; } @@ -1617,11 +1608,42 @@ out: error_propagate(errp, local_err); } +static void pc_cpu_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + HotplugHandlerClass *hhc; + Error *local_err = NULL; + PCMachineState *pcms = PC_MACHINE(hotplug_dev); + + if (!dev->hotplugged) { + goto out; + } + + if (!pcms->acpi_dev) { + error_setg(&local_err, + "cpu hotplug is not enabled: missing acpi device"); + goto out; + } + + hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev); + hhc->plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); + if (local_err) { + goto out; + } + + /* increment the number of CPUs */ + rtc_set_memory(pcms->rtc, 0x5f, rtc_get_memory(pcms->rtc, 0x5f) + 1); +out: + error_propagate(errp, local_err); +} + static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { pc_dimm_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + pc_cpu_plug(hotplug_dev, dev, errp); } } @@ -1630,7 +1652,8 @@ static HotplugHandler *pc_get_hotpug_handler(MachineState *machine, { PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(machine); - if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) || + object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { return HOTPLUG_HANDLER(machine); } diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 1cda5dd068..b559181ba7 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -63,6 +63,7 @@ static bool has_acpi_build = true; static int legacy_acpi_table_size; static bool smbios_defaults = true; static bool smbios_legacy_mode; +static bool smbios_uuid_encoded = true; /* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to * host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte * pages in the host. @@ -172,7 +173,7 @@ static void pc_init1(MachineState *machine, MachineClass *mc = MACHINE_GET_CLASS(machine); /* These values are guest ABI, do not change */ smbios_set_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)", - mc->name, smbios_legacy_mode); + mc->name, smbios_legacy_mode, smbios_uuid_encoded); } /* allocate ram and load rom/bios */ @@ -266,7 +267,7 @@ static void pc_init1(MachineState *machine, } pc_cmos_init(below_4g_mem_size, above_4g_mem_size, machine->boot_order, - floppy, idebus[0], idebus[1], rtc_state); + machine, floppy, idebus[0], idebus[1], rtc_state); if (pci_enabled && usb_enabled(false)) { pci_create_simple(pci_bus, piix3_devfn + 2, "piix3-usb-uhci"); @@ -302,8 +303,17 @@ static void pc_init_pci(MachineState *machine) pc_init1(machine, 1, 1); } +static void pc_compat_2_1(MachineState *machine) +{ + smbios_uuid_encoded = false; + x86_cpu_compat_set_features("coreduo", FEAT_1_ECX, CPUID_EXT_VMX, 0); + x86_cpu_compat_set_features("core2duo", FEAT_1_ECX, CPUID_EXT_VMX, 0); + x86_cpu_compat_kvm_no_autodisable(FEAT_8000_0001_ECX, CPUID_EXT3_SVM); +} + static void pc_compat_2_0(MachineState *machine) { + pc_compat_2_1(machine); /* This value depends on the actual DSDT and SSDT compiled into * the source QEMU; unfortunately it depends on the binary and * not on the machine type, so we cannot make pc-i440fx-1.7 work on @@ -333,7 +343,7 @@ static void pc_compat_1_7(MachineState *machine) gigabyte_align = false; option_rom_has_mr = true; legacy_acpi_table_size = 6414; - x86_cpu_compat_disable_kvm_features(FEAT_1_ECX, CPUID_EXT_X2APIC); + x86_cpu_compat_kvm_no_autoenable(FEAT_1_ECX, CPUID_EXT_X2APIC); } static void pc_compat_1_6(MachineState *machine) @@ -365,7 +375,13 @@ static void pc_compat_1_3(MachineState *machine) static void pc_compat_1_2(MachineState *machine) { pc_compat_1_3(machine); - x86_cpu_compat_disable_kvm_features(FEAT_KVM, KVM_FEATURE_PV_EOI); + x86_cpu_compat_kvm_no_autoenable(FEAT_KVM, KVM_FEATURE_PV_EOI); +} + +static void pc_init_pci_2_1(MachineState *machine) +{ + pc_compat_2_1(machine); + pc_init_pci(machine); } static void pc_init_pci_2_0(MachineState *machine) @@ -430,7 +446,7 @@ static void pc_init_isa(MachineState *machine) if (!machine->cpu_model) { machine->cpu_model = "486"; } - x86_cpu_compat_disable_kvm_features(FEAT_KVM, KVM_FEATURE_PV_EOI); + x86_cpu_compat_kvm_no_autoenable(FEAT_KVM, KVM_FEATURE_PV_EOI); enable_compat_apic_id_mode(); pc_init1(machine, 0, 1); } @@ -451,12 +467,14 @@ static void pc_xen_hvm_init(MachineState *machine) #define PC_I440FX_MACHINE_OPTIONS \ PC_DEFAULT_MACHINE_OPTIONS, \ + .family = "pc_piix", \ .desc = "Standard PC (i440FX + PIIX, 1996)", \ .hot_add_cpu = pc_hot_add_cpu #define PC_I440FX_2_2_MACHINE_OPTIONS \ PC_I440FX_MACHINE_OPTIONS, \ - .default_machine_opts = "firmware=bios-256k.bin" + .default_machine_opts = "firmware=bios-256k.bin", \ + .default_display = "std" static QEMUMachine pc_i440fx_machine_v2_2 = { PC_I440FX_2_2_MACHINE_OPTIONS, @@ -466,14 +484,16 @@ static QEMUMachine pc_i440fx_machine_v2_2 = { .is_default = 1, }; -#define PC_I440FX_2_1_MACHINE_OPTIONS PC_I440FX_2_2_MACHINE_OPTIONS +#define PC_I440FX_2_1_MACHINE_OPTIONS \ + PC_I440FX_MACHINE_OPTIONS, \ + .default_machine_opts = "firmware=bios-256k.bin" static QEMUMachine pc_i440fx_machine_v2_1 = { PC_I440FX_2_1_MACHINE_OPTIONS, .name = "pc-i440fx-2.1", - .init = pc_init_pci, + .init = pc_init_pci_2_1, .compat_props = (GlobalProperty[]) { - PC_COMPAT_2_1, + HW_COMPAT_2_1, { /* end of list */ } }, }; @@ -661,7 +681,7 @@ static QEMUMachine pc_machine_v1_1 = { .property = "class",\ .value = stringify(PCI_CLASS_MEMORY_RAM),\ },{\ - .driver = "apic",\ + .driver = "apic-common",\ .property = "vapic",\ .value = "off",\ },{\ diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 4d9e3cd69d..598e679749 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -52,6 +52,7 @@ static bool has_acpi_build = true; static bool smbios_defaults = true; static bool smbios_legacy_mode; +static bool smbios_uuid_encoded = true; /* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to * host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte * pages in the host. @@ -163,7 +164,7 @@ static void pc_q35_init(MachineState *machine) MachineClass *mc = MACHINE_GET_CLASS(machine); /* These values are guest ABI, do not change */ smbios_set_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)", - mc->name, smbios_legacy_mode); + mc->name, smbios_legacy_mode, smbios_uuid_encoded); } /* allocate ram and load rom/bios */ @@ -271,7 +272,7 @@ static void pc_q35_init(MachineState *machine) 8, NULL, 0); pc_cmos_init(below_4g_mem_size, above_4g_mem_size, machine->boot_order, - floppy, idebus[0], idebus[1], rtc_state); + machine, floppy, idebus[0], idebus[1], rtc_state); /* the rest devices to which pci devfn is automatically assigned */ pc_vga_init(isa_bus, host_bus); @@ -281,8 +282,17 @@ static void pc_q35_init(MachineState *machine) } } +static void pc_compat_2_1(MachineState *machine) +{ + smbios_uuid_encoded = false; + x86_cpu_compat_set_features("coreduo", FEAT_1_ECX, CPUID_EXT_VMX, 0); + x86_cpu_compat_set_features("core2duo", FEAT_1_ECX, CPUID_EXT_VMX, 0); + x86_cpu_compat_kvm_no_autodisable(FEAT_8000_0001_ECX, CPUID_EXT3_SVM); +} + static void pc_compat_2_0(MachineState *machine) { + pc_compat_2_1(machine); smbios_legacy_mode = true; has_reserved_memory = false; pc_set_legacy_acpi_data_size(); @@ -294,7 +304,7 @@ static void pc_compat_1_7(MachineState *machine) smbios_defaults = false; gigabyte_align = false; option_rom_has_mr = true; - x86_cpu_compat_disable_kvm_features(FEAT_1_ECX, CPUID_EXT_X2APIC); + x86_cpu_compat_kvm_no_autoenable(FEAT_1_ECX, CPUID_EXT_X2APIC); } static void pc_compat_1_6(MachineState *machine) @@ -316,6 +326,12 @@ static void pc_compat_1_4(MachineState *machine) x86_cpu_compat_set_features("Westmere", FEAT_1_ECX, 0, CPUID_EXT_PCLMULQDQ); } +static void pc_q35_init_2_1(MachineState *machine) +{ + pc_compat_2_1(machine); + pc_q35_init(machine); +} + static void pc_q35_init_2_0(MachineState *machine) { pc_compat_2_0(machine); @@ -348,13 +364,15 @@ static void pc_q35_init_1_4(MachineState *machine) #define PC_Q35_MACHINE_OPTIONS \ PC_DEFAULT_MACHINE_OPTIONS, \ + .family = "pc_q35", \ .desc = "Standard PC (Q35 + ICH9, 2009)", \ .hot_add_cpu = pc_hot_add_cpu, \ .units_per_default_bus = 1 #define PC_Q35_2_2_MACHINE_OPTIONS \ PC_Q35_MACHINE_OPTIONS, \ - .default_machine_opts = "firmware=bios-256k.bin" + .default_machine_opts = "firmware=bios-256k.bin", \ + .default_display = "std" static QEMUMachine pc_q35_machine_v2_2 = { PC_Q35_2_2_MACHINE_OPTIONS, @@ -363,14 +381,16 @@ static QEMUMachine pc_q35_machine_v2_2 = { .init = pc_q35_init, }; -#define PC_Q35_2_1_MACHINE_OPTIONS PC_Q35_2_2_MACHINE_OPTIONS +#define PC_Q35_2_1_MACHINE_OPTIONS \ + PC_Q35_MACHINE_OPTIONS, \ + .default_machine_opts = "firmware=bios-256k.bin" static QEMUMachine pc_q35_machine_v2_1 = { PC_Q35_2_1_MACHINE_OPTIONS, .name = "pc-q35-2.1", - .init = pc_q35_init, + .init = pc_q35_init_2_1, .compat_props = (GlobalProperty[]) { - PC_COMPAT_2_1, + HW_COMPAT_2_1, { /* end of list */ } }, }; diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index e3fa1b2fc1..8a7ad48921 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -48,6 +48,7 @@ struct smbios_table { static uint8_t *smbios_entries; static size_t smbios_entries_len; static bool smbios_legacy = true; +static bool smbios_uuid_encoded = true; /* end: legacy structures & constants for <= 2.0 machines */ @@ -391,6 +392,11 @@ static void smbios_build_type_1_fields(void) smbios_maybe_add_str(1, offsetof(struct smbios_type_1, family_str), type1.family); if (qemu_uuid_set) { + /* We don't encode the UUID in the "wire format" here because this + * function is for legacy mode and needs to keep the guest ABI, and + * because we don't know what's the SMBIOS version advertised by the + * BIOS. + */ smbios_add_field(1, offsetof(struct smbios_type_1, uuid), qemu_uuid, 16); } @@ -523,6 +529,19 @@ static void smbios_build_type_0_table(void) SMBIOS_BUILD_TABLE_POST; } +/* Encode UUID from the big endian encoding described on RFC4122 to the wire + * format specified by SMBIOS version 2.6. + */ +static void smbios_encode_uuid(struct smbios_uuid *uuid, const uint8_t *buf) +{ + memcpy(uuid, buf, 16); + if (smbios_uuid_encoded) { + uuid->time_low = bswap32(uuid->time_low); + uuid->time_mid = bswap16(uuid->time_mid); + uuid->time_hi_and_version = bswap16(uuid->time_hi_and_version); + } +} + static void smbios_build_type_1_table(void) { SMBIOS_BUILD_TABLE_PRE(1, 0x100, true); /* required */ @@ -532,9 +551,9 @@ static void smbios_build_type_1_table(void) SMBIOS_TABLE_SET_STR(1, version_str, type1.version); SMBIOS_TABLE_SET_STR(1, serial_number_str, type1.serial); if (qemu_uuid_set) { - memcpy(t->uuid, qemu_uuid, 16); + smbios_encode_uuid(&t->uuid, qemu_uuid); } else { - memset(t->uuid, 0, 16); + memset(&t->uuid, 0, 16); } t->wake_up_type = 0x06; /* power switch */ SMBIOS_TABLE_SET_STR(1, sku_number_str, type1.sku); @@ -746,10 +765,12 @@ void smbios_set_cpuid(uint32_t version, uint32_t features) } void smbios_set_defaults(const char *manufacturer, const char *product, - const char *version, bool legacy_mode) + const char *version, bool legacy_mode, + bool uuid_encoded) { smbios_have_defaults = true; smbios_legacy = legacy_mode; + smbios_uuid_encoded = uuid_encoded; /* drop unwanted version of command-line file blob(s) */ if (smbios_legacy) { @@ -821,7 +842,7 @@ void smbios_get_tables(uint8_t **tables, size_t *tables_len, smbios_build_type_2_table(); smbios_build_type_3_table(); - smbios_smp_sockets = smp_cpus / (smp_cores * smp_threads); + smbios_smp_sockets = DIV_ROUND_UP(smp_cpus, smp_cores * smp_threads); assert(smbios_smp_sockets >= 1); for (i = 0; i < smbios_smp_sockets; i++) { diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 70958e3655..61dbed1b97 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -49,6 +49,9 @@ static int handle_cmd(AHCIState *s,int port,int slot); static void ahci_reset_port(AHCIState *s, int port); static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis); static void ahci_init_d2h(AHCIDevice *ad); +static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write); +static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes); + static uint32_t ahci_port_read(AHCIState *s, int port, int offset) { @@ -567,24 +570,24 @@ static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished) AHCIDevice *ad = &s->dev[port]; AHCIPortRegs *pr = &ad->port_regs; IDEState *ide_state; - uint8_t *sdb_fis; + SDBFIS *sdb_fis; if (!s->dev[port].res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) { return; } - sdb_fis = &ad->res_fis[RES_FIS_SDBFIS]; + sdb_fis = (SDBFIS *)&ad->res_fis[RES_FIS_SDBFIS]; ide_state = &ad->port.ifs[0]; - /* clear memory */ - *(uint32_t*)sdb_fis = 0; - - /* write values */ - sdb_fis[0] = ide_state->error; - sdb_fis[2] = ide_state->status & 0x77; + sdb_fis->type = 0xA1; + /* Interrupt pending & Notification bit */ + sdb_fis->flags = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0); + sdb_fis->status = ide_state->status & 0x77; + sdb_fis->error = ide_state->error; + /* update SAct field in SDB_FIS */ s->dev[port].finished |= finished; - *(uint32_t*)(sdb_fis + 4) = cpu_to_le32(ad->finished); + sdb_fis->payload = cpu_to_le32(ad->finished); /* Update shadow registers (except BSY 0x80 and DRQ 0x08) */ pr->tfdata = (ad->port.ifs[0].error << 8) | @@ -600,6 +603,7 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) uint8_t *pio_fis, *cmd_fis; uint64_t tbl_addr; dma_addr_t cmd_len = 0x80; + IDEState *s = &ad->port.ifs[0]; if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) { return; @@ -629,21 +633,21 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) pio_fis[0] = 0x5f; pio_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0); - pio_fis[2] = ad->port.ifs[0].status; - pio_fis[3] = ad->port.ifs[0].error; - - pio_fis[4] = cmd_fis[4]; - pio_fis[5] = cmd_fis[5]; - pio_fis[6] = cmd_fis[6]; - pio_fis[7] = cmd_fis[7]; - pio_fis[8] = cmd_fis[8]; - pio_fis[9] = cmd_fis[9]; - pio_fis[10] = cmd_fis[10]; - pio_fis[11] = cmd_fis[11]; + pio_fis[2] = s->status; + pio_fis[3] = s->error; + + pio_fis[4] = s->sector; + pio_fis[5] = s->lcyl; + pio_fis[6] = s->hcyl; + pio_fis[7] = s->select; + pio_fis[8] = s->hob_sector; + pio_fis[9] = s->hob_lcyl; + pio_fis[10] = s->hob_hcyl; + pio_fis[11] = 0; pio_fis[12] = cmd_fis[12]; pio_fis[13] = cmd_fis[13]; pio_fis[14] = 0; - pio_fis[15] = ad->port.ifs[0].status; + pio_fis[15] = s->status; pio_fis[16] = len & 255; pio_fis[17] = len >> 8; pio_fis[18] = 0; @@ -670,6 +674,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis) int i; dma_addr_t cmd_len = 0x80; int cmd_mapped = 0; + IDEState *s = &ad->port.ifs[0]; if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) { return; @@ -687,17 +692,17 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis) d2h_fis[0] = 0x34; d2h_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0); - d2h_fis[2] = ad->port.ifs[0].status; - d2h_fis[3] = ad->port.ifs[0].error; - - d2h_fis[4] = cmd_fis[4]; - d2h_fis[5] = cmd_fis[5]; - d2h_fis[6] = cmd_fis[6]; - d2h_fis[7] = cmd_fis[7]; - d2h_fis[8] = cmd_fis[8]; - d2h_fis[9] = cmd_fis[9]; - d2h_fis[10] = cmd_fis[10]; - d2h_fis[11] = cmd_fis[11]; + d2h_fis[2] = s->status; + d2h_fis[3] = s->error; + + d2h_fis[4] = s->sector; + d2h_fis[5] = s->lcyl; + d2h_fis[6] = s->hcyl; + d2h_fis[7] = s->select; + d2h_fis[8] = s->hob_sector; + d2h_fis[9] = s->hob_lcyl; + d2h_fis[10] = s->hob_hcyl; + d2h_fis[11] = 0; d2h_fis[12] = cmd_fis[12]; d2h_fis[13] = cmd_fis[13]; for (i = 14; i < 20; i++) { @@ -1103,16 +1108,12 @@ static void ahci_start_transfer(IDEDMA *dma) } } - /* update number of transferred bytes */ - ad->cur_cmd->status = cpu_to_le32(le32_to_cpu(ad->cur_cmd->status) + size); - out: /* declare that we processed everything */ s->data_ptr = s->data_end; - if (has_sglist) { - qemu_sglist_destroy(&s->sg); - } + /* Update number of transferred bytes, destroy sglist */ + ahci_commit_buf(dma, size); s->end_transfer_func(s); @@ -1133,6 +1134,11 @@ static void ahci_start_dma(IDEDMA *dma, IDEState *s, dma_cb(s, 0); } +/** + * Called in DMA R/W chains to read the PRDT, utilizing ahci_populate_sglist. + * Not currently invoked by PIO R/W chains, + * which invoke ahci_populate_sglist via ahci_start_transfer. + */ static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); @@ -1145,6 +1151,24 @@ static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write) return s->io_buffer_size != 0; } +/** + * Destroys the scatter-gather list, + * and updates the command header with a bytes-read value. + * called explicitly via ahci_dma_rw_buf (ATAPI DMA), + * and ahci_start_transfer (PIO R/W), + * and called via callback from ide_dma_cb for DMA R/W paths. + */ +static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes) +{ + AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); + IDEState *s = &ad->port.ifs[0]; + + tx_bytes += le32_to_cpu(ad->cur_cmd->status); + ad->cur_cmd->status = cpu_to_le32(tx_bytes); + + qemu_sglist_destroy(&s->sg); +} + static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); @@ -1162,11 +1186,9 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) dma_buf_write(p, l, &s->sg); } - /* free sglist that was created in ahci_populate_sglist() */ - qemu_sglist_destroy(&s->sg); + /* free sglist, update byte count */ + ahci_commit_buf(dma, l); - /* update number of transferred bytes */ - ad->cur_cmd->status = cpu_to_le32(le32_to_cpu(ad->cur_cmd->status) + l); s->io_buffer_index += l; s->io_buffer_offset += l; @@ -1209,6 +1231,7 @@ static const IDEDMAOps ahci_dma_ops = { .start_dma = ahci_start_dma, .start_transfer = ahci_start_transfer, .prepare_buf = ahci_dma_prepare_buf, + .commit_buf = ahci_commit_buf, .rw_buf = ahci_dma_rw_buf, .set_unit = ahci_dma_set_unit, .cmd_done = ahci_cmd_done, diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h index e8ea34a1df..b12323730b 100644 --- a/hw/ide/ahci.h +++ b/hw/ide/ahci.h @@ -327,6 +327,14 @@ typedef struct NCQFrame { uint8_t reserved10; } QEMU_PACKED NCQFrame; +typedef struct SDBFIS { + uint8_t type; + uint8_t flags; + uint8_t status; + uint8_t error; + uint32_t payload; +} QEMU_PACKED SDBFIS; + void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports); void ahci_uninit(AHCIState *s); diff --git a/hw/ide/core.c b/hw/ide/core.c index 44e3d502d4..d316ccf961 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -634,8 +634,11 @@ void ide_sector_read(IDEState *s) ide_sector_read_cb, s); } -static void dma_buf_commit(IDEState *s) +static void dma_buf_commit(IDEState *s, uint32_t tx_bytes) { + if (s->bus->dma->ops->commit_buf) { + s->bus->dma->ops->commit_buf(s->bus->dma, tx_bytes); + } qemu_sglist_destroy(&s->sg); } @@ -650,6 +653,7 @@ void ide_set_inactive(IDEState *s, bool more) void ide_dma_error(IDEState *s) { + dma_buf_commit(s, 0); ide_abort_command(s); ide_set_inactive(s, false); ide_set_irq(s->bus); @@ -665,7 +669,6 @@ static int ide_handle_rw_error(IDEState *s, int error, int op) s->bus->error_status = op; } else if (action == BLOCK_ERROR_ACTION_REPORT) { if (op & IDE_RETRY_DMA) { - dma_buf_commit(s); ide_dma_error(s); } else { ide_rw_error(s); @@ -709,7 +712,8 @@ void ide_dma_cb(void *opaque, int ret) sector_num = ide_get_sector(s); if (n > 0) { - dma_buf_commit(s); + assert(s->io_buffer_size == s->sg.size); + dma_buf_commit(s, s->io_buffer_size); sector_num += n; ide_set_sector(s, sector_num); s->nsector -= n; @@ -740,7 +744,6 @@ void ide_dma_cb(void *opaque, int ret) if ((s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) && !ide_sect_range_ok(s, sector_num, n)) { - dma_buf_commit(s); ide_dma_error(s); return; } diff --git a/hw/ide/internal.h b/hw/ide/internal.h index 68ac610d6f..907493d0f8 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -322,6 +322,7 @@ typedef void EndTransferFunc(IDEState *); typedef void DMAStartFunc(IDEDMA *, IDEState *, BlockCompletionFunc *); typedef void DMAVoidFunc(IDEDMA *); typedef int DMAIntFunc(IDEDMA *, int); +typedef void DMAu32Func(IDEDMA *, uint32_t); typedef void DMAStopFunc(IDEDMA *, bool); typedef void DMARestartFunc(void *, int, RunState); @@ -430,6 +431,7 @@ struct IDEDMAOps { DMAStartFunc *start_dma; DMAVoidFunc *start_transfer; DMAIntFunc *prepare_buf; + DMAu32Func *commit_buf; DMAIntFunc *rw_buf; DMAIntFunc *set_unit; DMAStopFunc *set_inactive; diff --git a/hw/intc/openpic_kvm.c b/hw/intc/openpic_kvm.c index e3bce043a3..3e2cd189ff 100644 --- a/hw/intc/openpic_kvm.c +++ b/hw/intc/openpic_kvm.c @@ -45,6 +45,7 @@ typedef struct KVMOpenPICState { MemoryListener mem_listener; uint32_t fd; uint32_t model; + hwaddr mapped; } KVMOpenPICState; static void kvm_openpic_set_irq(void *opaque, int n_IRQ, int level) @@ -128,7 +129,16 @@ static void kvm_openpic_region_add(MemoryListener *listener, return; } + if (opp->mapped) { + /* + * We can only map the MPIC once. Since we are already mapped, + * the best we can do is ignore new maps. + */ + return; + } + reg_base = section->offset_within_address_space; + opp->mapped = reg_base; attr.group = KVM_DEV_MPIC_GRP_MISC; attr.attr = KVM_DEV_MPIC_BASE_ADDR; @@ -155,6 +165,15 @@ static void kvm_openpic_region_del(MemoryListener *listener, return; } + if (section->offset_within_address_space != opp->mapped) { + /* + * We can only map the MPIC once. This mapping was a secondary + * one that we couldn't fulfill. Ignore it. + */ + return; + } + opp->mapped = 0; + attr.group = KVM_DEV_MPIC_GRP_MISC; attr.attr = KVM_DEV_MPIC_BASE_ADDR; attr.addr = (uint64_t)(unsigned long)®_base; diff --git a/hw/m68k/an5206.c b/hw/m68k/an5206.c index a9ac27089b..f1f13504c0 100644 --- a/hw/m68k/an5206.c +++ b/hw/m68k/an5206.c @@ -74,7 +74,8 @@ static void an5206_init(MachineState *machine) NULL, NULL, 1, ELF_MACHINE, 0); entry = elf_entry; if (kernel_size < 0) { - kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL); + kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, + NULL, NULL); } if (kernel_size < 0) { kernel_size = load_image_targphys(kernel_filename, KERNEL_LOAD_ADDR, diff --git a/hw/m68k/dummy_m68k.c b/hw/m68k/dummy_m68k.c index 957ef82f88..facd561efa 100644 --- a/hw/m68k/dummy_m68k.c +++ b/hw/m68k/dummy_m68k.c @@ -50,7 +50,8 @@ static void dummy_m68k_init(MachineState *machine) NULL, NULL, 1, ELF_MACHINE, 0); entry = elf_entry; if (kernel_size < 0) { - kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL); + kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, + NULL, NULL); } if (kernel_size < 0) { kernel_size = load_image_targphys(kernel_filename, diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index 188230f901..a01a4458e6 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -279,7 +279,8 @@ static void mcf5208evb_init(MachineState *machine) NULL, NULL, 1, ELF_MACHINE, 0); entry = elf_entry; if (kernel_size < 0) { - kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL); + kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, + NULL, NULL); } if (kernel_size < 0) { kernel_size = load_image_targphys(kernel_filename, 0x40000000, diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c index 6bf36d046f..a2843cdf99 100644 --- a/hw/microblaze/boot.c +++ b/hw/microblaze/boot.c @@ -154,7 +154,8 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, if (kernel_size < 0) { hwaddr uentry, loadaddr; - kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0); + kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0, + NULL, NULL); boot_info.bootstrap_pc = uentry; high = (loadaddr + kernel_size + 3) & ~3; } diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index 10b5b2e86b..35dc6d5684 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -52,7 +52,6 @@ static void rtas_nvram_fetch(PowerPCCPU *cpu, sPAPREnvironment *spapr, { sPAPRNVRAM *nvram = spapr->nvram; hwaddr offset, buffer, len; - int alen; void *membuf; if ((nargs != 3) || (nret != 2)) { @@ -77,19 +76,14 @@ static void rtas_nvram_fetch(PowerPCCPU *cpu, sPAPREnvironment *spapr, return; } - membuf = cpu_physical_memory_map(buffer, &len, 1); - if (nvram->blk) { - alen = blk_pread(nvram->blk, offset, membuf, len); - } else { - assert(nvram->buf); + assert(nvram->buf); - memcpy(membuf, nvram->buf + offset, len); - alen = len; - } + membuf = cpu_physical_memory_map(buffer, &len, 1); + memcpy(membuf, nvram->buf + offset, len); cpu_physical_memory_unmap(membuf, len, 1, len); - rtas_st(rets, 0, (alen < len) ? RTAS_OUT_HW_ERROR : RTAS_OUT_SUCCESS); - rtas_st(rets, 1, (alen < 0) ? 0 : alen); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); + rtas_st(rets, 1, len); } static void rtas_nvram_store(PowerPCCPU *cpu, sPAPREnvironment *spapr, @@ -123,14 +117,15 @@ static void rtas_nvram_store(PowerPCCPU *cpu, sPAPREnvironment *spapr, } membuf = cpu_physical_memory_map(buffer, &len, 0); + + alen = len; if (nvram->blk) { alen = blk_pwrite(nvram->blk, offset, membuf, len); - } else { - assert(nvram->buf); - - memcpy(nvram->buf + offset, membuf, len); - alen = len; } + + assert(nvram->buf); + memcpy(nvram->buf + offset, membuf, len); + cpu_physical_memory_unmap(membuf, len, 0, len); rtas_st(rets, 0, (alen < len) ? RTAS_OUT_HW_ERROR : RTAS_OUT_SUCCESS); @@ -145,15 +140,24 @@ static int spapr_nvram_init(VIOsPAPRDevice *dev) nvram->size = blk_getlength(nvram->blk); } else { nvram->size = DEFAULT_NVRAM_SIZE; - nvram->buf = g_malloc0(nvram->size); } + nvram->buf = g_malloc0(nvram->size); + if ((nvram->size < MIN_NVRAM_SIZE) || (nvram->size > MAX_NVRAM_SIZE)) { fprintf(stderr, "spapr-nvram must be between %d and %d bytes in size\n", MIN_NVRAM_SIZE, MAX_NVRAM_SIZE); return -1; } + if (nvram->blk) { + int alen = blk_pread(nvram->blk, 0, nvram->buf, nvram->size); + + if (alen != nvram->size) { + return -1; + } + } + spapr_rtas_register(RTAS_NVRAM_FETCH, "nvram-fetch", rtas_nvram_fetch); spapr_rtas_register(RTAS_NVRAM_STORE, "nvram-store", rtas_nvram_store); @@ -167,6 +171,48 @@ static int spapr_nvram_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) return fdt_setprop_cell(fdt, node_off, "#bytes", nvram->size); } +static int spapr_nvram_pre_load(void *opaque) +{ + sPAPRNVRAM *nvram = VIO_SPAPR_NVRAM(opaque); + + g_free(nvram->buf); + nvram->buf = NULL; + nvram->size = 0; + + return 0; +} + +static int spapr_nvram_post_load(void *opaque, int version_id) +{ + sPAPRNVRAM *nvram = VIO_SPAPR_NVRAM(opaque); + + if (nvram->blk) { + int alen = blk_pwrite(nvram->blk, 0, nvram->buf, nvram->size); + + if (alen < 0) { + return alen; + } + if (alen != nvram->size) { + return -1; + } + } + + return 0; +} + +static const VMStateDescription vmstate_spapr_nvram = { + .name = "spapr_nvram", + .version_id = 1, + .minimum_version_id = 1, + .pre_load = spapr_nvram_pre_load, + .post_load = spapr_nvram_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(size, sPAPRNVRAM), + VMSTATE_VBUFFER_ALLOC_UINT32(buf, sPAPRNVRAM, 1, NULL, 0, size), + VMSTATE_END_OF_LIST() + }, +}; + static Property spapr_nvram_properties[] = { DEFINE_SPAPR_PROPERTIES(sPAPRNVRAM, sdev), DEFINE_PROP_DRIVE("drive", sPAPRNVRAM, blk), @@ -185,6 +231,7 @@ static void spapr_nvram_class_init(ObjectClass *klass, void *data) k->dt_compatible = "qemu,spapr-nvram"; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->props = spapr_nvram_properties; + dc->vmsd = &vmstate_spapr_nvram; } static const TypeInfo spapr_nvram_type_info = { diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index c110033c2e..1da0657ddf 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -72,7 +72,7 @@ static void cpu_openrisc_load_kernel(ram_addr_t ram_size, entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, - &entry, NULL, NULL); + &entry, NULL, NULL, NULL, NULL); } if (kernel_size < 0) { kernel_size = load_image_targphys(kernel_filename, diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 6ce75aa940..371699cf86 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1776,7 +1776,12 @@ static int pci_qdev_init(DeviceState *qdev) pci_dev->romfile = g_strdup(pc->romfile); is_default_rom = true; } - pci_add_option_rom(pci_dev, is_default_rom); + + rc = pci_add_option_rom(pci_dev, is_default_rom); + if (rc != 0) { + pci_unregister_device(DEVICE(pci_dev)); + return rc; + } return 0; } @@ -1937,6 +1942,15 @@ static int pci_add_option_rom(PCIDevice *pdev, bool is_default_rom) * for 0.11 compatibility. */ int class = pci_get_word(pdev->config + PCI_CLASS_DEVICE); + + /* + * Hot-plugged devices can't use the option ROM + * if the rom bar is disabled. + */ + if (DEVICE(pdev)->hotplugged) { + return -1; + } + if (class == 0x0300) { rom_add_vga(pdev->romfile); } else { diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index b64a004631..58455bd854 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -529,7 +529,7 @@ bool pcie_cap_is_arifwd_enabled(const PCIDevice *dev) } /************************************************************************** - * pci express extended capability allocation functions + * pci express extended capability list management functions * uint16_t ext_cap_id (16 bit) * uint8_t cap_ver (4 bit) * uint16_t cap_offset (12 bit) diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index edd44d03e7..19d99200ae 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -20,4 +20,4 @@ obj-$(CONFIG_MAC) += mac_newworld.o obj-$(CONFIG_E500) += e500.o mpc8544ds.o e500plat.o obj-$(CONFIG_E500) += mpc8544_guts.o ppce500_spin.o # PowerPC 440 Xilinx ML507 reference board. -obj-y += virtex_ml507.o +obj-$(CONFIG_XILINX) += virtex_ml507.o diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 16c85efab5..2832fc0da4 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -36,6 +36,9 @@ #include "exec/address-spaces.h" #include "qemu/host-utils.h" #include "hw/pci-host/ppce500.h" +#include "qemu/error-report.h" +#include "hw/platform-bus.h" +#include "hw/net/fsl_etsec/etsec.h" #define EPAPR_MAGIC (0x45504150) #define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb" @@ -61,6 +64,8 @@ #define MPC8544_PCI_IO 0xE1000000ULL #define MPC8544_UTIL_OFFSET 0xe0000ULL #define MPC8544_SPIN_BASE 0xEF000000ULL +#define MPC8XXX_GPIO_OFFSET 0x000FF000ULL +#define MPC8XXX_GPIO_IRQ 43 struct boot_info { @@ -122,6 +127,142 @@ static void dt_serial_create(void *fdt, unsigned long long offset, } } +static void create_dt_mpc8xxx_gpio(void *fdt, const char *soc, const char *mpic) +{ + hwaddr mmio0 = MPC8XXX_GPIO_OFFSET; + int irq0 = MPC8XXX_GPIO_IRQ; + gchar *node = g_strdup_printf("%s/gpio@%"PRIx64, soc, mmio0); + gchar *poweroff = g_strdup_printf("%s/power-off", soc); + int gpio_ph; + + qemu_fdt_add_subnode(fdt, node); + qemu_fdt_setprop_string(fdt, node, "compatible", "fsl,qoriq-gpio"); + qemu_fdt_setprop_cells(fdt, node, "reg", mmio0, 0x1000); + qemu_fdt_setprop_cells(fdt, node, "interrupts", irq0, 0x2); + qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", mpic); + qemu_fdt_setprop_cells(fdt, node, "#gpio-cells", 2); + qemu_fdt_setprop(fdt, node, "gpio-controller", NULL, 0); + gpio_ph = qemu_fdt_alloc_phandle(fdt); + qemu_fdt_setprop_cell(fdt, node, "phandle", gpio_ph); + qemu_fdt_setprop_cell(fdt, node, "linux,phandle", gpio_ph); + + /* Power Off Pin */ + qemu_fdt_add_subnode(fdt, poweroff); + qemu_fdt_setprop_string(fdt, poweroff, "compatible", "gpio-poweroff"); + qemu_fdt_setprop_cells(fdt, poweroff, "gpios", gpio_ph, 0, 0); + + g_free(node); + g_free(poweroff); +} + +typedef struct PlatformDevtreeData { + void *fdt; + const char *mpic; + int irq_start; + const char *node; + PlatformBusDevice *pbus; +} PlatformDevtreeData; + +static int create_devtree_etsec(SysBusDevice *sbdev, PlatformDevtreeData *data) +{ + eTSEC *etsec = ETSEC_COMMON(sbdev); + PlatformBusDevice *pbus = data->pbus; + hwaddr mmio0 = platform_bus_get_mmio_addr(pbus, sbdev, 0); + int irq0 = platform_bus_get_irqn(pbus, sbdev, 0); + int irq1 = platform_bus_get_irqn(pbus, sbdev, 1); + int irq2 = platform_bus_get_irqn(pbus, sbdev, 2); + gchar *node = g_strdup_printf("/platform/ethernet@%"PRIx64, mmio0); + gchar *group = g_strdup_printf("%s/queue-group", node); + void *fdt = data->fdt; + + assert((int64_t)mmio0 >= 0); + assert(irq0 >= 0); + assert(irq1 >= 0); + assert(irq2 >= 0); + + qemu_fdt_add_subnode(fdt, node); + qemu_fdt_setprop_string(fdt, node, "device_type", "network"); + qemu_fdt_setprop_string(fdt, node, "compatible", "fsl,etsec2"); + qemu_fdt_setprop_string(fdt, node, "model", "eTSEC"); + qemu_fdt_setprop(fdt, node, "local-mac-address", etsec->conf.macaddr.a, 6); + qemu_fdt_setprop_cells(fdt, node, "fixed-link", 0, 1, 1000, 0, 0); + + qemu_fdt_add_subnode(fdt, group); + qemu_fdt_setprop_cells(fdt, group, "reg", mmio0, 0x1000); + qemu_fdt_setprop_cells(fdt, group, "interrupts", + data->irq_start + irq0, 0x2, + data->irq_start + irq1, 0x2, + data->irq_start + irq2, 0x2); + + g_free(node); + g_free(group); + + return 0; +} + +static int sysbus_device_create_devtree(SysBusDevice *sbdev, void *opaque) +{ + PlatformDevtreeData *data = opaque; + bool matched = false; + + if (object_dynamic_cast(OBJECT(sbdev), TYPE_ETSEC_COMMON)) { + create_devtree_etsec(sbdev, data); + matched = true; + } + + if (!matched) { + error_report("Device %s is not supported by this machine yet.", + qdev_fw_name(DEVICE(sbdev))); + exit(1); + } + + return 0; +} + +static void platform_bus_create_devtree(PPCE500Params *params, void *fdt, + const char *mpic) +{ + gchar *node = g_strdup_printf("/platform@%"PRIx64, params->platform_bus_base); + const char platcomp[] = "qemu,platform\0simple-bus"; + uint64_t addr = params->platform_bus_base; + uint64_t size = params->platform_bus_size; + int irq_start = params->platform_bus_first_irq; + PlatformBusDevice *pbus; + DeviceState *dev; + + /* Create a /platform node that we can put all devices into */ + + qemu_fdt_add_subnode(fdt, node); + qemu_fdt_setprop(fdt, node, "compatible", platcomp, sizeof(platcomp)); + + /* Our platform bus region is less than 32bit big, so 1 cell is enough for + address and size */ + qemu_fdt_setprop_cells(fdt, node, "#size-cells", 1); + qemu_fdt_setprop_cells(fdt, node, "#address-cells", 1); + qemu_fdt_setprop_cells(fdt, node, "ranges", 0, addr >> 32, addr, size); + + qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", mpic); + + dev = qdev_find_recursive(sysbus_get_default(), TYPE_PLATFORM_BUS_DEVICE); + pbus = PLATFORM_BUS_DEVICE(dev); + + /* We can only create dt nodes for dynamic devices when they're ready */ + if (pbus->done_gathering) { + PlatformDevtreeData data = { + .fdt = fdt, + .mpic = mpic, + .irq_start = irq_start, + .node = node, + .pbus = pbus, + }; + + /* Loop through all dynamic sysbus devices and create nodes for them */ + foreach_dynamic_sysbus_device(sysbus_device_create_devtree, &data); + } + + g_free(node); +} + static int ppce500_load_device_tree(MachineState *machine, PPCE500Params *params, hwaddr addr, @@ -379,6 +520,14 @@ static int ppce500_load_device_tree(MachineState *machine, qemu_fdt_setprop_cell(fdt, pci, "#address-cells", 3); qemu_fdt_setprop_string(fdt, "/aliases", "pci0", pci); + if (params->has_mpc8xxx_gpio) { + create_dt_mpc8xxx_gpio(fdt, soc, mpic); + } + + if (params->has_platform_bus) { + platform_bus_create_devtree(params, fdt, mpic); + } + params->fixup_devtree(params, fdt); if (toplevel_compat) { @@ -407,6 +556,7 @@ typedef struct DeviceTreeParams { hwaddr initrd_size; hwaddr kernel_base; hwaddr kernel_size; + Notifier notifier; } DeviceTreeParams; static void ppce500_reset_device_tree(void *opaque) @@ -417,6 +567,12 @@ static void ppce500_reset_device_tree(void *opaque) false); } +static void ppce500_init_notify(Notifier *notifier, void *data) +{ + DeviceTreeParams *p = container_of(notifier, DeviceTreeParams, notifier); + ppce500_reset_device_tree(p); +} + static int ppce500_prep_device_tree(MachineState *machine, PPCE500Params *params, hwaddr addr, @@ -435,6 +591,8 @@ static int ppce500_prep_device_tree(MachineState *machine, p->kernel_size = kernel_size; qemu_register_reset(ppce500_reset_device_tree, p); + p->notifier.notify = ppce500_init_notify; + qemu_add_machine_init_done_notifier(&p->notifier); /* Issue the device tree loader once, so that we get the size of the blob */ return ppce500_load_device_tree(machine, params, addr, initrd_base, @@ -618,6 +776,13 @@ static qemu_irq *ppce500_init_mpic(PPCE500Params *params, MemoryRegion *ccsr, return mpic; } +static void ppce500_power_off(void *opaque, int line, int on) +{ + if (on) { + qemu_system_shutdown_request(); + } +} + void ppce500_init(MachineState *machine, PPCE500Params *params) { MemoryRegion *address_space_mem = get_system_memory(); @@ -769,6 +934,40 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) cur_base = (32 * 1024 * 1024); } + if (params->has_mpc8xxx_gpio) { + qemu_irq poweroff_irq; + + dev = qdev_create(NULL, "mpc8xxx_gpio"); + s = SYS_BUS_DEVICE(dev); + qdev_init_nofail(dev); + sysbus_connect_irq(s, 0, mpic[MPC8XXX_GPIO_IRQ]); + memory_region_add_subregion(ccsr_addr_space, MPC8XXX_GPIO_OFFSET, + sysbus_mmio_get_region(s, 0)); + + /* Power Off GPIO at Pin 0 */ + poweroff_irq = qemu_allocate_irq(ppce500_power_off, NULL, 0); + qdev_connect_gpio_out(dev, 0, poweroff_irq); + } + + /* Platform Bus Device */ + if (params->has_platform_bus) { + dev = qdev_create(NULL, TYPE_PLATFORM_BUS_DEVICE); + dev->id = TYPE_PLATFORM_BUS_DEVICE; + qdev_prop_set_uint32(dev, "num_irqs", params->platform_bus_num_irqs); + qdev_prop_set_uint32(dev, "mmio_size", params->platform_bus_size); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + + for (i = 0; i < params->platform_bus_num_irqs; i++) { + int irqn = params->platform_bus_first_irq + i; + sysbus_connect_irq(s, i, mpic[irqn]); + } + + memory_region_add_subregion(address_space_mem, + params->platform_bus_base, + sysbus_mmio_get_region(s, 0)); + } + /* Load kernel. */ if (machine->kernel_filename) { kernel_base = cur_base; @@ -830,7 +1029,8 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) * Hrm. No ELF image? Try a uImage, maybe someone is giving us an * ePAPR compliant kernel */ - kernel_size = load_uimage(filename, &bios_entry, &loadaddr, NULL); + kernel_size = load_uimage(filename, &bios_entry, &loadaddr, NULL, + NULL, NULL); if (kernel_size < 0) { fprintf(stderr, "qemu: could not load firmware '%s'\n", filename); exit(1); diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h index 08b25fab49..9f61ab2b1c 100644 --- a/hw/ppc/e500.h +++ b/hw/ppc/e500.h @@ -11,6 +11,12 @@ typedef struct PPCE500Params { void (*fixup_devtree)(struct PPCE500Params *params, void *fdt); int mpic_version; + bool has_mpc8xxx_gpio; + bool has_platform_bus; + hwaddr platform_bus_base; + hwaddr platform_bus_size; + int platform_bus_first_irq; + int platform_bus_num_irqs; } PPCE500Params; void ppce500_init(MachineState *machine, PPCE500Params *params); diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index 27df31ddb0..d50ae000ee 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -35,6 +35,12 @@ static void e500plat_init(MachineState *machine) .pci_nr_slots = PCI_SLOT_MAX - 1, .fixup_devtree = e500plat_fixup_devtree, .mpic_version = OPENPIC_MODEL_FSL_MPIC_42, + .has_mpc8xxx_gpio = true, + .has_platform_bus = true, + .platform_bus_base = 0xf00000000ULL, + .platform_bus_size = (128ULL * 1024 * 1024), + .platform_bus_first_irq = 5, + .platform_bus_num_irqs = 10, }; /* Older KVM versions don't support EPR which breaks guests when we announce @@ -51,6 +57,7 @@ static QEMUMachine e500plat_machine = { .desc = "generic paravirt e500 platform", .init = e500plat_init, .max_cpus = 32, + .has_dynamic_sysbus = true, }; static void e500plat_machine_init(void) diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 81a06d310d..778970aa9b 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -253,7 +253,8 @@ static void bamboo_init(MachineState *machine) /* Load kernel. */ if (kernel_filename) { - success = load_uimage(kernel_filename, &entry, &loadaddr, NULL); + success = load_uimage(kernel_filename, &entry, &loadaddr, NULL, + NULL, NULL); if (success < 0) { success = load_elf(kernel_filename, NULL, NULL, &elf_entry, &elf_lowaddr, NULL, 1, ELF_MACHINE, 0); diff --git a/hw/ppc/ppc4xx_pci.c b/hw/ppc/ppc4xx_pci.c index 55a3cabee5..0bb3cdb46e 100644 --- a/hw/ppc/ppc4xx_pci.c +++ b/hw/ppc/ppc4xx_pci.c @@ -92,30 +92,6 @@ typedef struct PPC4xxPCIState PPC4xxPCIState; #define PCI_ALL_SIZE (PCI_REG_BASE + PCI_REG_SIZE) -static uint64_t pci4xx_cfgaddr_read(void *opaque, hwaddr addr, - unsigned size) -{ - PPC4xxPCIState *ppc4xx_pci = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(ppc4xx_pci); - - return phb->config_reg; -} - -static void pci4xx_cfgaddr_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - PPC4xxPCIState *ppc4xx_pci = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(ppc4xx_pci); - - phb->config_reg = value & ~0x3; -} - -static const MemoryRegionOps pci4xx_cfgaddr_ops = { - .read = pci4xx_cfgaddr_read, - .write = pci4xx_cfgaddr_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - static void ppc4xx_pci_reg_write4(void *opaque, hwaddr offset, uint64_t value, unsigned size) { diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 45764a7032..30de25de5c 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -57,6 +57,8 @@ #include "trace.h" #include "hw/nmi.h" +#include "hw/compat.h" + #include <libfdt.h> /* SLOF memory layout: @@ -848,11 +850,31 @@ static void spapr_reset_htab(sPAPREnvironment *spapr) } } +static int find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque) +{ + bool matched = false; + + if (object_dynamic_cast(OBJECT(sbdev), TYPE_SPAPR_PCI_HOST_BRIDGE)) { + matched = true; + } + + if (!matched) { + error_report("Device %s is not supported by this machine yet.", + qdev_fw_name(DEVICE(sbdev))); + exit(1); + } + + return 0; +} + static void ppc_spapr_reset(void) { PowerPCCPU *first_ppc_cpu; uint32_t rtas_limit; + /* Check for unknown sysbus devices */ + foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL); + /* Reset the hash table & recalc the RMA */ spapr_reset_htab(spapr); @@ -1658,9 +1680,6 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); - mc->name = "pseries"; - mc->desc = "pSeries Logical Partition (PAPR compliant)"; - mc->is_default = 1; mc->init = ppc_spapr_init; mc->reset = ppc_spapr_reset; mc->block_default_type = IF_SCSI; @@ -1668,6 +1687,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) mc->no_parallel = 1; mc->default_boot_order = NULL; mc->kvm_type = spapr_kvm_type; + mc->has_dynamic_sysbus = true; fwc->get_dev_path = spapr_get_fw_dev_path; nc->nmi_monitor_handler = spapr_nmi; @@ -1676,6 +1696,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) static const TypeInfo spapr_machine_info = { .name = TYPE_SPAPR_MACHINE, .parent = TYPE_MACHINE, + .abstract = true, .instance_size = sizeof(sPAPRMachineState), .instance_init = spapr_machine_initfn, .class_init = spapr_machine_class_init, @@ -1689,10 +1710,14 @@ static const TypeInfo spapr_machine_info = { static void spapr_machine_2_1_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + static GlobalProperty compat_props[] = { + HW_COMPAT_2_1, + { /* end of list */ } + }; mc->name = "pseries-2.1"; mc->desc = "pSeries Logical Partition (PAPR compliant) v2.1"; - mc->is_default = 0; + mc->compat_props = compat_props; } static const TypeInfo spapr_machine_2_1_info = { @@ -1701,10 +1726,27 @@ static const TypeInfo spapr_machine_2_1_info = { .class_init = spapr_machine_2_1_class_init, }; +static void spapr_machine_2_2_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->name = "pseries-2.2"; + mc->desc = "pSeries Logical Partition (PAPR compliant) v2.2"; + mc->alias = "pseries"; + mc->is_default = 1; +} + +static const TypeInfo spapr_machine_2_2_info = { + .name = TYPE_SPAPR_MACHINE "2.2", + .parent = TYPE_SPAPR_MACHINE, + .class_init = spapr_machine_2_2_class_init, +}; + static void spapr_machine_register_types(void) { type_register_static(&spapr_machine_info); type_register_static(&spapr_machine_2_1_info); + type_register_static(&spapr_machine_2_2_info); } type_init(spapr_machine_register_types) diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index ad0da7fdc4..21b95b342c 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -704,28 +704,34 @@ static const VMStateDescription vmstate_spapr_pci_msi = { }, }; +static void spapr_pci_fill_msi_devs(gpointer key, gpointer value, + gpointer opaque) +{ + sPAPRPHBState *sphb = opaque; + + sphb->msi_devs[sphb->msi_devs_num].key = *(uint32_t *)key; + sphb->msi_devs[sphb->msi_devs_num].value = *(spapr_pci_msi *)value; + sphb->msi_devs_num++; +} + static void spapr_pci_pre_save(void *opaque) { sPAPRPHBState *sphb = opaque; - GHashTableIter iter; - gpointer key, value; - int i; + int msi_devs_num; if (sphb->msi_devs) { g_free(sphb->msi_devs); sphb->msi_devs = NULL; } - sphb->msi_devs_num = g_hash_table_size(sphb->msi); - if (!sphb->msi_devs_num) { + sphb->msi_devs_num = 0; + msi_devs_num = g_hash_table_size(sphb->msi); + if (!msi_devs_num) { return; } - sphb->msi_devs = g_malloc(sphb->msi_devs_num * sizeof(spapr_pci_msi_mig)); + sphb->msi_devs = g_malloc(msi_devs_num * sizeof(spapr_pci_msi_mig)); - g_hash_table_iter_init(&iter, sphb->msi); - for (i = 0; g_hash_table_iter_next(&iter, &key, &value); ++i) { - sphb->msi_devs[i].key = *(uint32_t *) key; - sphb->msi_devs[i].value = *(spapr_pci_msi *) value; - } + g_hash_table_foreach(sphb->msi, spapr_pci_fill_msi_devs, sphb); + assert(sphb->msi_devs_num == msi_devs_num); } static int spapr_pci_post_load(void *opaque, int version_id) diff --git a/hw/virtio/dataplane/vring.c b/hw/virtio/dataplane/vring.c index 372706a234..61f6d83686 100644 --- a/hw/virtio/dataplane/vring.c +++ b/hw/virtio/dataplane/vring.c @@ -352,10 +352,6 @@ int vring_pop(VirtIODevice *vdev, Vring *vring, goto out; } - if (vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { - vring_avail_event(&vring->vr) = vring->vr.avail->idx; - } - i = head; do { if (unlikely(i >= num)) { @@ -392,6 +388,10 @@ int vring_pop(VirtIODevice *vdev, Vring *vring, /* On success, increment avail index. */ vring->last_avail_idx++; + if (vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { + vring_avail_event(&vring->vr) = vring->last_avail_idx; + } + return head; out: diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 4e88d9c5e9..aefe0bbaaf 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -226,7 +226,7 @@ static int vhost_user_call(struct vhost_dev *dev, unsigned long int request, msg.memory.regions[fd_num].memory_size = reg->memory_size; msg.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr; msg.memory.regions[fd_num].mmap_offset = reg->userspace_addr - - (uintptr_t) qemu_get_ram_block_host_ptr(reg->guest_phys_addr); + (uintptr_t) qemu_get_ram_block_host_ptr(ram_addr); assert(fd_num < VHOST_MEMORY_MAX_NREGIONS); fds[fd_num++] = fd; } diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index e490adeb80..dde1d73b56 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -86,9 +86,6 @@ * 12 is historical, and due to x86 page size. */ #define VIRTIO_PCI_QUEUE_ADDR_SHIFT 12 -/* Flags track per-device state like workarounds for quirks in older guests. */ -#define VIRTIO_PCI_FLAG_BUS_MASTER_BUG (1 << 0) - static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size, VirtIOPCIProxy *dev); @@ -323,14 +320,6 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) proxy->pci_dev.config[PCI_COMMAND] | PCI_COMMAND_MASTER, 1); } - - /* Linux before 2.6.34 sets the device as OK without enabling - the PCI device bus master bit. In this case we need to disable - some safety checks. */ - if ((val & VIRTIO_CONFIG_S_DRIVER_OK) && - !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { - proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG; - } break; case VIRTIO_MSI_CONFIG_VECTOR: msix_vector_unuse(&proxy->pci_dev, vdev->config_vector); @@ -483,8 +472,7 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, pci_default_write_config(pci_dev, address, val, len); if (range_covers_byte(address, len, PCI_COMMAND) && - !(pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER) && - !(proxy->flags & VIRTIO_PCI_FLAG_BUS_MASTER_BUG)) { + !(pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { virtio_pci_stop_ioeventfd(proxy); virtio_set_status(vdev, vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK); } @@ -895,11 +883,15 @@ static void virtio_pci_vmstate_change(DeviceState *d, bool running) VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); if (running) { - /* Try to find out if the guest has bus master disabled, but is - in ready state. Then we have a buggy guest OS. */ - if ((vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) && + /* Old QEMU versions did not set bus master enable on status write. + * Detect DRIVER set and enable it. + */ + if ((proxy->flags & VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION) && + (vdev->status & VIRTIO_CONFIG_S_DRIVER) && !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { - proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG; + pci_default_write_config(&proxy->pci_dev, PCI_COMMAND, + proxy->pci_dev.config[PCI_COMMAND] | + PCI_COMMAND_MASTER, 1); } virtio_pci_start_ioeventfd(proxy); } else { @@ -1040,10 +1032,11 @@ static void virtio_pci_reset(DeviceState *qdev) virtio_pci_stop_ioeventfd(proxy); virtio_bus_reset(bus); msix_unuse_all_vectors(&proxy->pci_dev); - proxy->flags &= ~VIRTIO_PCI_FLAG_BUS_MASTER_BUG; } static Property virtio_pci_properties[] = { + DEFINE_PROP_BIT("virtio-pci-bus-master-bug-migration", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT, false), DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h index 1cea157a47..8873b6d138 100644 --- a/hw/virtio/virtio-pci.h +++ b/hw/virtio/virtio-pci.h @@ -53,6 +53,11 @@ typedef struct VirtioBusClass VirtioPCIBusClass; #define VIRTIO_PCI_BUS_CLASS(klass) \ OBJECT_CLASS_CHECK(VirtioPCIBusClass, klass, TYPE_VIRTIO_PCI_BUS) +/* Need to activate work-arounds for buggy guests at vmstate load. */ +#define VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT 0 +#define VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION \ + (1 << VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT) + /* Performance improves when virtqueue kick processing is decoupled from the * vcpu thread using ioeventfd for some devices. */ #define VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT 1 diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 2c236bf271..013979a6b8 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -469,7 +469,7 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) i = head = virtqueue_get_head(vq, vq->last_avail_idx++); if (vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { - vring_avail_event(vq, vring_avail_idx(vq)); + vring_avail_event(vq, vq->last_avail_idx); } if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_INDIRECT) { diff --git a/hw/xtensa/pic_cpu.c b/hw/xtensa/pic_cpu.c index e2005bd981..18825d19f0 100644 --- a/hw/xtensa/pic_cpu.c +++ b/hw/xtensa/pic_cpu.c @@ -31,14 +31,14 @@ void xtensa_advance_ccount(CPUXtensaState *env, uint32_t d) { - uint32_t old_ccount = env->sregs[CCOUNT]; + uint32_t old_ccount = env->sregs[CCOUNT] + 1; env->sregs[CCOUNT] += d; if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) { int i; for (i = 0; i < env->config->nccompare; ++i) { - if (env->sregs[CCOMPARE + i] - old_ccount <= d) { + if (env->sregs[CCOMPARE + i] - old_ccount < d) { xtensa_timer_irq(env, i, 1); } } diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 0100c3115b..e5a6bba783 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -328,7 +328,8 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) } else { hwaddr ep; int is_linux; - success = load_uimage(kernel_filename, &ep, NULL, &is_linux); + success = load_uimage(kernel_filename, &ep, NULL, &is_linux, + translate_phys_addr, cpu); if (success > 0 && is_linux) { entry_point = ep; } else { diff --git a/include/block/block.h b/include/block/block.h index 603d4b4392..5450610bc1 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -270,7 +270,13 @@ typedef enum { int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix); -int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts); +/* The units of offset and total_work_size may be chosen arbitrarily by the + * block driver; total_work_size may change during the course of the amendment + * operation */ +typedef void BlockDriverAmendStatusCB(BlockDriverState *bs, int64_t offset, + int64_t total_work_size); +int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts, + BlockDriverAmendStatusCB *status_cb); /* external snapshots */ bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs, @@ -330,6 +336,7 @@ int bdrv_flush(BlockDriverState *bs); int coroutine_fn bdrv_co_flush(BlockDriverState *bs); int bdrv_flush_all(void); void bdrv_close_all(void); +void bdrv_drain(BlockDriverState *bs); void bdrv_drain_all(void); int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors); @@ -500,6 +507,8 @@ typedef enum { BLKDBG_PWRITEV_ZERO, BLKDBG_PWRITEV_DONE, + BLKDBG_EMPTY_IMAGE_PREPARE, + BLKDBG_EVENT_MAX, } BlkDebugEvent; diff --git a/include/block/block_int.h b/include/block/block_int.h index 8898c6c51b..a1c17b9578 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -232,7 +232,8 @@ struct BlockDriver { int (*bdrv_check)(BlockDriverState* bs, BdrvCheckResult *result, BdrvCheckMode fix); - int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts); + int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts, + BlockDriverAmendStatusCB *status_cb); void (*bdrv_debug_event)(BlockDriverState *bs, BlkDebugEvent event); @@ -289,6 +290,9 @@ typedef struct BlockLimits { /* optimal transfer length in sectors */ int opt_transfer_length; + /* maximal transfer length in sectors */ + int max_transfer_length; + /* memory alignment so that no bounce buffer is needed */ size_t opt_mem_alignment; } BlockLimits; diff --git a/include/block/blockjob.h b/include/block/blockjob.h index acb399f87d..b6d4ebbe03 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -91,6 +91,11 @@ struct BlockJob { */ bool busy; + /** + * Set to true when the job is ready to be completed. + */ + bool ready; + /** Status that is published by the query-block-jobs QMP API */ BlockDeviceIoStatus iostatus; @@ -273,6 +278,21 @@ bool block_job_is_paused(BlockJob *job); int block_job_cancel_sync(BlockJob *job); /** + * block_job_complete_sync: + * @job: The job to be completed. + * @errp: Error object which may be set by block_job_complete(); this is not + * necessarily set on every error, the job return value has to be + * checked as well. + * + * Synchronously complete the job. The completion callback is called before the + * function returns, unless it is NULL (which is permissible when using this + * function). + * + * Returns the return value from the job. + */ +int block_job_complete_sync(BlockJob *job, Error **errp); + +/** * block_job_iostatus_reset: * @job: The job whose I/O status should be reset. * @@ -295,4 +315,23 @@ void block_job_iostatus_reset(BlockJob *job); BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs, BlockdevOnError on_err, int is_read, int error); + +typedef void BlockJobDeferToMainLoopFn(BlockJob *job, void *opaque); + +/** + * block_job_defer_to_main_loop: + * @job: The job + * @fn: The function to run in the main loop + * @opaque: The opaque value that is passed to @fn + * + * Execute a given function in the main loop with the BlockDriverState + * AioContext acquired. Block jobs must call bdrv_unref(), bdrv_close(), and + * anything that uses bdrv_drain_all() in the main loop. + * + * The @job AioContext is held while @fn executes. + */ +void block_job_defer_to_main_loop(BlockJob *job, + BlockJobDeferToMainLoopFn *fn, + void *opaque); + #endif diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index e3ec4c8e0c..427b8515a3 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -26,6 +26,12 @@ typedef struct CPUListState { FILE *file; } CPUListState; +typedef enum MMUAccessType { + MMU_DATA_LOAD = 0, + MMU_DATA_STORE = 1, + MMU_INST_FETCH = 2 +} MMUAccessType; + #if !defined(CONFIG_USER_ONLY) enum device_endian { diff --git a/include/hw/acpi/cpu_hotplug.h b/include/hw/acpi/cpu_hotplug.h index 9e5d30c9df..f6d358def1 100644 --- a/include/hw/acpi/cpu_hotplug.h +++ b/include/hw/acpi/cpu_hotplug.h @@ -20,8 +20,9 @@ typedef struct AcpiCpuHotplug { uint8_t sts[ACPI_GPE_PROC_LEN]; } AcpiCpuHotplug; -void AcpiCpuHotplug_add(ACPIGPE *gpe, AcpiCpuHotplug *g, CPUState *cpu); +void acpi_cpu_plug_cb(ACPIREGS *ar, qemu_irq irq, + AcpiCpuHotplug *g, DeviceState *dev, Error **errp); -void AcpiCpuHotplug_init(MemoryRegion *parent, Object *owner, - AcpiCpuHotplug *gpe_cpu, uint16_t base); +void acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner, + AcpiCpuHotplug *gpe_cpu, uint16_t base); #endif diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h index 7e42448ef9..fe975e6624 100644 --- a/include/hw/acpi/ich9.h +++ b/include/hw/acpi/ich9.h @@ -47,7 +47,6 @@ typedef struct ICH9LPCPMRegs { Notifier powerdown_notifier; AcpiCpuHotplug gpe_cpu; - Notifier cpu_added_notifier; MemHotplugState acpi_memory_hotplug; } ICH9LPCPMRegs; diff --git a/include/hw/boards.h b/include/hw/boards.h index e07c03f846..e0a67903ef 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -19,6 +19,7 @@ typedef void QEMUMachineHotAddCPUFunc(const int64_t id, Error **errp); typedef int QEMUMachineGetKvmtypeFunc(const char *arg); struct QEMUMachine { + const char *family; /* NULL iff @name identifies a standalone machtype */ const char *name; const char *alias; const char *desc; @@ -35,10 +36,12 @@ struct QEMUMachine { use_sclp:1, no_floppy:1, no_cdrom:1, - no_sdcard:1; + no_sdcard:1, + has_dynamic_sysbus:1; int is_default; const char *default_machine_opts; const char *default_boot_order; + const char *default_display; GlobalProperty *compat_props; const char *hw_version; }; @@ -76,6 +79,7 @@ struct MachineClass { ObjectClass parent_class; /*< public >*/ + const char *family; /* NULL iff @name identifies a standalone machtype */ const char *name; const char *alias; const char *desc; @@ -94,10 +98,12 @@ struct MachineClass { use_sclp:1, no_floppy:1, no_cdrom:1, - no_sdcard:1; + no_sdcard:1, + has_dynamic_sysbus:1; int is_default; const char *default_machine_opts; const char *default_boot_order; + const char *default_display; GlobalProperty *compat_props; const char *hw_version; @@ -111,6 +117,8 @@ struct MachineClass { struct MachineState { /*< private >*/ Object parent_obj; + Notifier sysbus_notifier; + /*< public >*/ char *accel; diff --git a/include/hw/compat.h b/include/hw/compat.h new file mode 100644 index 0000000000..313682a708 --- /dev/null +++ b/include/hw/compat.h @@ -0,0 +1,35 @@ +#ifndef HW_COMPAT_H +#define HW_COMPAT_H + +#define HW_COMPAT_2_1 \ + {\ + .driver = "intel-hda",\ + .property = "old_msi_addr",\ + .value = "on",\ + },{\ + .driver = "VGA",\ + .property = "qemu-extended-regs",\ + .value = "off",\ + },{\ + .driver = "secondary-vga",\ + .property = "qemu-extended-regs",\ + .value = "off",\ + },{\ + .driver = "virtio-scsi-pci",\ + .property = "any_layout",\ + .value = "off",\ + },{\ + .driver = "usb-mouse",\ + .property = "usb_version",\ + .value = stringify(1),\ + },{\ + .driver = "usb-kbd",\ + .property = "usb_version",\ + .value = stringify(1),\ + },{\ + .driver = "virtio-pci",\ + .property = "virtio-pci-bus-master-bug-migration",\ + .value = "on",\ + } + +#endif /* HW_COMPAT_H */ diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index f4701e1c60..e321ee4fbc 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -37,7 +37,7 @@ #define VTD_PCI_DEVFN_MAX 256 #define VTD_PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) #define VTD_PCI_FUNC(devfn) ((devfn) & 0x07) -#define VTD_SID_TO_BUS(sid) (((sid) >> 8) && 0xff) +#define VTD_SID_TO_BUS(sid) (((sid) >> 8) & 0xff) #define VTD_SID_TO_DEVFN(sid) ((sid) & 0xff) #define DMAR_REG_SIZE 0x230 diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 2545268a6c..7c3731f1b0 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -14,6 +14,7 @@ #include "sysemu/sysemu.h" #include "hw/pci/pci.h" #include "hw/boards.h" +#include "hw/compat.h" #define HPET_INTCAP "hpet-intcap" @@ -33,6 +34,7 @@ struct PCMachineState { MemoryRegion hotplug_memory; HotplugHandler *acpi_dev; + ISADevice *rtc; uint64_t max_ram_below_4g; bool vmport; @@ -212,7 +214,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, uint32 hpet_irqs); void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd); void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, - const char *boot_device, + const char *boot_device, MachineState *machine, ISADevice *floppy, BusState *ide0, BusState *ide1, ISADevice *s); void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus); @@ -303,31 +305,8 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t); int e820_get_num_entries(void); bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); -#define PC_COMPAT_2_1 \ - {\ - .driver = "intel-hda",\ - .property = "old_msi_addr",\ - .value = "on",\ - },{\ - .driver = "VGA",\ - .property = "qemu-extended-regs",\ - .value = "off",\ - },{\ - .driver = "secondary-vga",\ - .property = "qemu-extended-regs",\ - .value = "off",\ - },{\ - .driver = "usb-mouse",\ - .property = "usb_version",\ - .value = stringify(1),\ - },{\ - .driver = "usb-kbd",\ - .property = "usb_version",\ - .value = stringify(1),\ - } - #define PC_COMPAT_2_0 \ - PC_COMPAT_2_1, \ + HW_COMPAT_2_1, \ {\ .driver = "virtio-scsi-pci",\ .property = "any_layout",\ diff --git a/include/hw/i386/smbios.h b/include/hw/i386/smbios.h index a3f4d88bf0..d2850bed2c 100644 --- a/include/hw/i386/smbios.h +++ b/include/hw/i386/smbios.h @@ -20,7 +20,8 @@ void smbios_entry_add(QemuOpts *opts); void smbios_set_cpuid(uint32_t version, uint32_t features); void smbios_set_defaults(const char *manufacturer, const char *product, - const char *version, bool legacy_mode); + const char *version, bool legacy_mode, + bool uuid_encoded); uint8_t *smbios_get_table_legacy(size_t *length); void smbios_get_tables(uint8_t **tables, size_t *tables_len, uint8_t **anchor, size_t *anchor_len); @@ -72,6 +73,18 @@ struct smbios_type_0 { uint8_t embedded_controller_minor_release; } QEMU_PACKED; +/* UUID encoding. The time_* fields are little-endian, as specified by SMBIOS + * version 2.6. + */ +struct smbios_uuid { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_hi_and_reserved; + uint8_t clock_seq_low; + uint8_t node[6]; +} QEMU_PACKED; + /* SMBIOS type 1 - System Information */ struct smbios_type_1 { struct smbios_structure_header header; @@ -79,7 +92,7 @@ struct smbios_type_1 { uint8_t product_name_str; uint8_t version_str; uint8_t serial_number_str; - uint8_t uuid[16]; + struct smbios_uuid uuid; uint8_t wake_up_type; uint8_t sku_number_str; uint8_t family_str; diff --git a/include/hw/loader.h b/include/hw/loader.h index 9190387b8e..054c6a22b5 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -30,7 +30,9 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), int load_aout(const char *filename, hwaddr addr, int max_sz, int bswap_needed, hwaddr target_page_size); int load_uimage(const char *filename, hwaddr *ep, - hwaddr *loadaddr, int *is_linux); + hwaddr *loadaddr, int *is_linux, + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque); /** * load_ramdisk: diff --git a/include/hw/platform-bus.h b/include/hw/platform-bus.h new file mode 100644 index 0000000000..bd42b83809 --- /dev/null +++ b/include/hw/platform-bus.h @@ -0,0 +1,57 @@ +#ifndef HW_PLATFORM_BUS_H +#define HW_PLATFORM_BUS_H 1 + +/* + * Platform Bus device to support dynamic Sysbus devices + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Alexander Graf, <agraf@suse.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "hw/sysbus.h" + +typedef struct PlatformBusDevice PlatformBusDevice; + +#define TYPE_PLATFORM_BUS_DEVICE "platform-bus-device" +#define PLATFORM_BUS_DEVICE(obj) \ + OBJECT_CHECK(PlatformBusDevice, (obj), TYPE_PLATFORM_BUS_DEVICE) +#define PLATFORM_BUS_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(PlatformBusDeviceClass, (klass), TYPE_PLATFORM_BUS_DEVICE) +#define PLATFORM_BUS_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PlatformBusDeviceClass, (obj), TYPE_PLATFORM_BUS_DEVICE) + +struct PlatformBusDevice { + /*< private >*/ + SysBusDevice parent_obj; + Notifier notifier; + bool done_gathering; + + /*< public >*/ + uint32_t mmio_size; + MemoryRegion mmio; + + uint32_t num_irqs; + qemu_irq *irqs; + unsigned long *used_irqs; +}; + +int platform_bus_get_irqn(PlatformBusDevice *platform_bus, SysBusDevice *sbdev, + int n); +hwaddr platform_bus_get_mmio_addr(PlatformBusDevice *pbus, SysBusDevice *sbdev, + int n); + +#endif /* !HW_PLATFORM_BUS_H */ diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 00a15a3977..d3a29408d4 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -272,6 +272,7 @@ qemu_irq qdev_get_gpio_in_named(DeviceState *dev, const char *name, int n); void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin); void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n, qemu_irq pin); +qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n); qemu_irq qdev_intercept_gpio_out(DeviceState *dev, qemu_irq icpt, const char *name, int n); diff --git a/include/hw/sysbus.h b/include/hw/sysbus.h index 9bddfdec42..d1f3f000f9 100644 --- a/include/hw/sysbus.h +++ b/include/hw/sysbus.h @@ -57,6 +57,8 @@ struct SysBusDevice { pio_addr_t pio[QDEV_MAX_PIO]; }; +typedef int FindSysbusDeviceFunc(SysBusDevice *sbdev, void *opaque); + void sysbus_init_mmio(SysBusDevice *dev, MemoryRegion *memory); MemoryRegion *sysbus_mmio_get_region(SysBusDevice *dev, int n); void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p); @@ -64,7 +66,11 @@ void sysbus_pass_irq(SysBusDevice *dev, SysBusDevice *target); void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size); +bool sysbus_has_irq(SysBusDevice *dev, int n); +bool sysbus_has_mmio(SysBusDevice *dev, unsigned int n); void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq); +bool sysbus_is_irq_connected(SysBusDevice *dev, int n); +qemu_irq sysbus_get_connected_irq(SysBusDevice *dev, int n); void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr); void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr, int priority); @@ -72,6 +78,9 @@ void sysbus_add_io(SysBusDevice *dev, hwaddr addr, MemoryRegion *mem); MemoryRegion *sysbus_address_space(SysBusDevice *dev); +/* Call func for every dynamically created sysbus device in the system */ +void foreach_dynamic_sysbus_device(FindSysbusDeviceFunc *func, void *opaque); + /* Legacy helper function for creating devices. */ DeviceState *sysbus_create_varargs(const char *name, hwaddr addr, ...); diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 1565404f7e..c0324344d5 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -68,6 +68,12 @@ typedef signed int int_fast16_t; #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif +/* Minimum function that returns zero only iff both values are zero. + * Intended for use with unsigned values only. */ +#ifndef MIN_NON_ZERO +#define MIN_NON_ZERO(a, b) (((a) != 0 && (a) < (b)) ? (a) : (b)) +#endif + #ifndef ROUND_UP #define ROUND_UP(n,d) (((n) + (d) - 1) & -(d)) #endif diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 50de1d36b1..9fea3bc3af 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -185,9 +185,6 @@ void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict); /* generic hotplug */ void drive_hot_add(Monitor *mon, const QDict *qdict); -/* CPU hotplug */ -void qemu_register_cpu_added_notifier(Notifier *notifier); - /* pcie aer error injection */ void pcie_aer_inject_error_print(Monitor *mon, const QObject *data); int do_pcie_aer_inject_error(Monitor *mon, diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 1c04fcf3cc..e2596a4201 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1539,7 +1539,6 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, * Generate 16 random bytes for userspace PRNG seeding (not * cryptically secure but it's not the aim of QEMU). */ - srand((unsigned int) time(NULL)); for (i = 0; i < 16; i++) { k_rand_bytes[i] = rand(); } @@ -1821,11 +1820,11 @@ static void load_elf_image(const char *image_name, int image_fd, loaddr = -1, hiaddr = 0; for (i = 0; i < ehdr->e_phnum; ++i) { if (phdr[i].p_type == PT_LOAD) { - abi_ulong a = phdr[i].p_vaddr; + abi_ulong a = phdr[i].p_vaddr - phdr[i].p_offset; if (a < loaddr) { loaddr = a; } - a += phdr[i].p_memsz; + a = phdr[i].p_vaddr + phdr[i].p_memsz; if (a > hiaddr) { hiaddr = a; } diff --git a/linux-user/main.c b/linux-user/main.c index 483eb3fec2..5c14c1e874 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1006,7 +1006,6 @@ void cpu_loop(CPUARMState *env) CPUState *cs = CPU(arm_env_get_cpu(env)); int trapnr, sig; target_siginfo_t info; - uint32_t addr; for (;;) { cpu_exec_start(cs); @@ -1042,12 +1041,11 @@ void cpu_loop(CPUARMState *env) /* fall through for segv */ case EXCP_PREFETCH_ABORT: case EXCP_DATA_ABORT: - addr = env->exception.vaddress; info.si_signo = SIGSEGV; info.si_errno = 0; /* XXX: check env->error_code */ info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = addr; + info._sifields._sigfault._addr = env->exception.vaddress; queue_signal(env, info.si_signo, &info); break; case EXCP_DEBUG: @@ -3546,6 +3544,17 @@ static void handle_arg_pagesize(const char *arg) } } +static void handle_arg_randseed(const char *arg) +{ + unsigned long long seed; + + if (parse_uint_full(arg, &seed, 0) != 0 || seed > UINT_MAX) { + fprintf(stderr, "Invalid seed number: %s\n", arg); + exit(1); + } + srand(seed); +} + static void handle_arg_gdb(const char *arg) { gdbstub_port = atoi(arg); @@ -3674,6 +3683,8 @@ static const struct qemu_argument arg_table[] = { "", "run in singlestep mode"}, {"strace", "QEMU_STRACE", false, handle_arg_strace, "", "log system calls"}, + {"seed", "QEMU_RAND_SEED", true, handle_arg_randseed, + "", "Seed for pseudo-random number generator"}, {"version", "QEMU_VERSION", false, handle_arg_version, "", "display version information and exit"}, {NULL, NULL, false, NULL, NULL, NULL} @@ -3856,6 +3867,8 @@ int main(int argc, char **argv, char **envp) cpudef_setup(); /* parse cpu definitions in target config file (TBD) */ #endif + srand(time(NULL)); + optind = parse_args(argc, argv); /* Zero out regs */ @@ -3926,6 +3939,10 @@ int main(int argc, char **argv, char **envp) do_strace = 1; } + if (getenv("QEMU_RAND_SEED")) { + handle_arg_randseed(getenv("QEMU_RAND_SEED")); + } + target_environ = envlist_to_environ(envlist, NULL); envlist_free(envlist); diff --git a/linux-user/syscall.c b/linux-user/syscall.c index a175cc15f8..aaac6a25ce 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -5473,6 +5473,27 @@ static int do_openat(void *cpu_env, int dirfd, const char *pathname, int flags, return get_errno(sys_openat(dirfd, path(pathname), flags, mode)); } +#define TIMER_MAGIC 0x0caf0000 +#define TIMER_MAGIC_MASK 0xffff0000 + +/* Convert QEMU provided timer ID back to internal 16bit index format */ +static target_timer_t get_timer_id(abi_long arg) +{ + target_timer_t timerid = arg; + + if ((timerid & TIMER_MAGIC_MASK) != TIMER_MAGIC) { + return -TARGET_EINVAL; + } + + timerid &= 0xffff; + + if (timerid >= ARRAY_SIZE(g_posix_timers)) { + return -TARGET_EINVAL; + } + + return timerid; +} + /* do_syscall() should always have a single exit point at the end so that actions, such as logging of syscall results, can be performed. All errnos that do_syscall() returns must be -TARGET_<errcode>. */ @@ -9579,7 +9600,6 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, /* args: clockid_t clockid, struct sigevent *sevp, timer_t *timerid */ struct sigevent host_sevp = { {0}, }, *phost_sevp = NULL; - struct target_timer_t *ptarget_timer; int clkid = arg1; int timer_index = next_free_host_timer(); @@ -9601,11 +9621,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (ret) { phtimer = NULL; } else { - if (!lock_user_struct(VERIFY_WRITE, ptarget_timer, arg3, 1)) { + if (put_user(TIMER_MAGIC | timer_index, arg3, target_timer_t)) { goto efault; } - ptarget_timer->ptr = tswap32(0xcafe0000 | timer_index); - unlock_user_struct(ptarget_timer, arg3, 1); } } break; @@ -9617,9 +9635,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, { /* args: timer_t timerid, int flags, const struct itimerspec *new_value, * struct itimerspec * old_value */ - target_ulong timerid = arg1; + target_timer_t timerid = get_timer_id(arg1); - if (arg3 == 0 || timerid >= ARRAY_SIZE(g_posix_timers)) { + if (timerid < 0) { + ret = timerid; + } else if (arg3 == 0) { ret = -TARGET_EINVAL; } else { timer_t htimer = g_posix_timers[timerid]; @@ -9638,12 +9658,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_timer_gettime: { /* args: timer_t timerid, struct itimerspec *curr_value */ - target_ulong timerid = arg1; + target_timer_t timerid = get_timer_id(arg1); - if (!arg2) { - return -TARGET_EFAULT; - } else if (timerid >= ARRAY_SIZE(g_posix_timers)) { - ret = -TARGET_EINVAL; + if (timerid < 0) { + ret = timerid; + } else if (!arg2) { + ret = -TARGET_EFAULT; } else { timer_t htimer = g_posix_timers[timerid]; struct itimerspec hspec; @@ -9661,10 +9681,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_timer_getoverrun: { /* args: timer_t timerid */ - target_ulong timerid = arg1; + target_timer_t timerid = get_timer_id(arg1); - if (timerid >= ARRAY_SIZE(g_posix_timers)) { - ret = -TARGET_EINVAL; + if (timerid < 0) { + ret = timerid; } else { timer_t htimer = g_posix_timers[timerid]; ret = get_errno(timer_getoverrun(htimer)); @@ -9677,10 +9697,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_timer_delete: { /* args: timer_t timerid */ - target_ulong timerid = arg1; + target_timer_t timerid = get_timer_id(arg1); - if (timerid >= ARRAY_SIZE(g_posix_timers)) { - ret = -TARGET_EINVAL; + if (timerid < 0) { + ret = timerid; } else { timer_t htimer = g_posix_timers[timerid]; ret = get_errno(timer_delete(htimer)); diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index c9e6323905..ebb3be1196 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2564,10 +2564,7 @@ struct target_ucred { #endif - -struct target_timer_t { - abi_ulong ptr; -}; +typedef int32_t target_timer_t; #define TARGET_SIGEV_MAX_SIZE 64 @@ -2972,7 +2972,7 @@ static target_long monitor_get_ccr (const struct MonitorDef *md, int val) u = 0; for (i = 0; i < 8; i++) - u |= env->crf[i] << (32 - (4 * i)); + u |= env->crf[i] << (32 - (4 * (i + 1))); return u; } diff --git a/pc-bios/QEMU,tcx.bin b/pc-bios/QEMU,tcx.bin Binary files differindex de35986dfe..d79cc1fea1 100644 --- a/pc-bios/QEMU,tcx.bin +++ b/pc-bios/QEMU,tcx.bin diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc Binary files differindex df867f6f5a..070399a879 100644 --- a/pc-bios/openbios-ppc +++ b/pc-bios/openbios-ppc diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32 Binary files differindex 4fa504d146..1cf6ea7dc8 100644 --- a/pc-bios/openbios-sparc32 +++ b/pc-bios/openbios-sparc32 diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64 Binary files differindex 402ea78fe4..2ed0dffc23 100644 --- a/pc-bios/openbios-sparc64 +++ b/pc-bios/openbios-sparc64 diff --git a/qapi/block-core.json b/qapi/block-core.json index 8f7089e054..8c3e45d4c3 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -514,12 +514,14 @@ # # @io-status: the status of the job (since 1.3) # +# @ready: true if the job may be completed (since 2.2) +# # Since: 1.1 ## { 'type': 'BlockJobInfo', 'data': {'type': 'str', 'device': 'str', 'len': 'int', 'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int', - 'io-status': 'BlockDeviceIoStatus'} } + 'io-status': 'BlockDeviceIoStatus', 'ready': 'bool'} } ## # @query-block-jobs: @@ -1420,7 +1422,9 @@ 'refblock_alloc.write_blocks', 'refblock_alloc.write_table', 'refblock_alloc.switch_table', 'cluster_alloc', 'cluster_alloc_bytes', 'cluster_free', 'flush_to_os', - 'flush_to_disk' ] } + 'flush_to_disk', 'pwritev_rmw.head', 'pwritev_rmw.after_head', + 'pwritev_rmw.tail', 'pwritev_rmw.after_tail', 'pwritev', + 'pwritev_zero', 'pwritev_done', 'empty_image_prepare' ] } ## # @BlkdebugInjectErrorOptions diff --git a/qdev-monitor.c b/qdev-monitor.c index fac7d179fe..ebfa701a9d 100644 --- a/qdev-monitor.c +++ b/qdev-monitor.c @@ -180,6 +180,44 @@ static const char *find_typename_by_alias(const char *alias) return NULL; } +static DeviceClass *qdev_get_device_class(const char **driver, Error **errp) +{ + ObjectClass *oc; + DeviceClass *dc; + + oc = object_class_by_name(*driver); + if (!oc) { + const char *typename = find_typename_by_alias(*driver); + + if (typename) { + *driver = typename; + oc = object_class_by_name(*driver); + } + } + + if (!object_class_dynamic_cast(oc, TYPE_DEVICE)) { + error_setg(errp, "'%s' is not a valid device model name", *driver); + return NULL; + } + + if (object_class_is_abstract(oc)) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, "driver", + "non-abstract device type"); + return NULL; + } + + dc = DEVICE_CLASS(oc); + if (dc->cannot_instantiate_with_device_add_yet || + (qdev_hotplug && !dc->hotpluggable)) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, "driver", + "pluggable device type"); + return NULL; + } + + return dc; +} + + int qdev_device_help(QemuOpts *opts) { Error *local_err = NULL; @@ -197,19 +235,14 @@ int qdev_device_help(QemuOpts *opts) return 0; } - if (!object_class_by_name(driver)) { - const char *typename = find_typename_by_alias(driver); - - if (typename) { - driver = typename; - } + qdev_get_device_class(&driver, &local_err); + if (local_err) { + goto error; } prop_list = qmp_device_list_properties(driver, &local_err); if (local_err) { - error_printf("%s\n", error_get_pretty(local_err)); - error_free(local_err); - return 1; + goto error; } for (prop = prop_list; prop; prop = prop->next) { @@ -225,6 +258,11 @@ int qdev_device_help(QemuOpts *opts) qapi_free_DevicePropertyInfoList(prop_list); return 1; + +error: + error_printf("%s\n", error_get_pretty(local_err)); + error_free(local_err); + return 1; } static Object *qdev_get_peripheral(void) @@ -455,7 +493,6 @@ static BusState *qbus_find(const char *path) DeviceState *qdev_device_add(QemuOpts *opts) { - ObjectClass *oc; DeviceClass *dc; const char *driver, *path, *id; DeviceState *dev; @@ -469,33 +506,10 @@ DeviceState *qdev_device_add(QemuOpts *opts) } /* find driver */ - oc = object_class_by_name(driver); - if (!oc) { - const char *typename = find_typename_by_alias(driver); - - if (typename) { - driver = typename; - oc = object_class_by_name(driver); - } - } - - if (!object_class_dynamic_cast(oc, TYPE_DEVICE)) { - qerror_report(ERROR_CLASS_GENERIC_ERROR, - "'%s' is not a valid device model name", driver); - return NULL; - } - - if (object_class_is_abstract(oc)) { - qerror_report(QERR_INVALID_PARAMETER_VALUE, "driver", - "non-abstract device type"); - return NULL; - } - - dc = DEVICE_CLASS(oc); - if (dc->cannot_instantiate_with_device_add_yet || - (qdev_hotplug && !dc->hotpluggable)) { - qerror_report(QERR_INVALID_PARAMETER_VALUE, "driver", - "pluggable device type"); + dc = qdev_get_device_class(&driver, &err); + if (err) { + qerror_report_err(err); + error_free(err); return NULL; } diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 55aec6bded..95677745f9 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -22,9 +22,9 @@ STEXI ETEXI DEF("commit", img_commit, - "commit [-q] [-f fmt] [-t cache] filename") + "commit [-q] [-f fmt] [-t cache] [-b base] [-d] [-p] filename") STEXI -@item commit [-q] [-f @var{fmt}] [-t @var{cache}] @var{filename} +@item commit [-q] [-f @var{fmt}] [-t @var{cache}] [-b @var{base}] [-d] [-p] @var{filename} ETEXI DEF("compare", img_compare, @@ -70,8 +70,8 @@ STEXI ETEXI DEF("amend", img_amend, - "amend [-q] [-f fmt] [-t cache] -o options filename") + "amend [-p] [-q] [-f fmt] [-t cache] -o options filename") STEXI -@item amend [-q] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename} +@item amend [-p] [-q] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename} @end table ETEXI diff --git a/qemu-img.c b/qemu-img.c index 731502c2ce..a42335c632 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -31,6 +31,7 @@ #include "sysemu/sysemu.h" #include "sysemu/block-backend.h" #include "block/block_int.h" +#include "block/blockjob.h" #include "block/qapi.h" #include <getopt.h> @@ -722,18 +723,54 @@ fail: return ret; } +typedef struct CommonBlockJobCBInfo { + BlockDriverState *bs; + Error **errp; +} CommonBlockJobCBInfo; + +static void common_block_job_cb(void *opaque, int ret) +{ + CommonBlockJobCBInfo *cbi = opaque; + + if (ret < 0) { + error_setg_errno(cbi->errp, -ret, "Block job failed"); + } + + /* Drop this block job's reference */ + bdrv_unref(cbi->bs); +} + +static void run_block_job(BlockJob *job, Error **errp) +{ + AioContext *aio_context = bdrv_get_aio_context(job->bs); + + do { + aio_poll(aio_context, true); + qemu_progress_print((float)job->offset / job->len * 100.f, 0); + } while (!job->ready); + + block_job_complete_sync(job, errp); + + /* A block job may finish instantaneously without publishing any progress, + * so just signal completion here */ + qemu_progress_print(100.f, 0); +} + static int img_commit(int argc, char **argv) { int c, ret, flags; - const char *filename, *fmt, *cache; + const char *filename, *fmt, *cache, *base; BlockBackend *blk; - BlockDriverState *bs; - bool quiet = false; + BlockDriverState *bs, *base_bs; + bool progress = false, quiet = false, drop = false; + Error *local_err = NULL; + CommonBlockJobCBInfo cbi; fmt = NULL; cache = BDRV_DEFAULT_CACHE; + base = NULL; for(;;) { - c = getopt(argc, argv, "f:ht:q"); + c = getopt(argc, argv, "f:ht:b:dpq"); if (c == -1) { break; } @@ -748,17 +785,34 @@ static int img_commit(int argc, char **argv) case 't': cache = optarg; break; + case 'b': + base = optarg; + /* -b implies -d */ + drop = true; + break; + case 'd': + drop = true; + break; + case 'p': + progress = true; + break; case 'q': quiet = true; break; } } + + /* Progress is not shown in Quiet mode */ + if (quiet) { + progress = false; + } + if (optind != argc - 1) { error_exit("Expecting one image file name"); } filename = argv[optind++]; - flags = BDRV_O_RDWR; + flags = BDRV_O_RDWR | BDRV_O_UNMAP; ret = bdrv_parse_cache_flags(cache, &flags); if (ret < 0) { error_report("Invalid cache option: %s", cache); @@ -771,29 +825,76 @@ static int img_commit(int argc, char **argv) } bs = blk_bs(blk); - ret = bdrv_commit(bs); - switch(ret) { - case 0: - qprintf(quiet, "Image committed.\n"); - break; - case -ENOENT: - error_report("No disk inserted"); - break; - case -EACCES: - error_report("Image is read-only"); - break; - case -ENOTSUP: - error_report("Image is already committed"); - break; - default: - error_report("Error while committing image"); - break; + qemu_progress_init(progress, 1.f); + qemu_progress_print(0.f, 100); + + if (base) { + base_bs = bdrv_find_backing_image(bs, base); + if (!base_bs) { + error_set(&local_err, QERR_BASE_NOT_FOUND, base); + goto done; + } + } else { + /* This is different from QMP, which by default uses the deepest file in + * the backing chain (i.e., the very base); however, the traditional + * behavior of qemu-img commit is using the immediate backing file. */ + base_bs = bs->backing_hd; + if (!base_bs) { + error_setg(&local_err, "Image does not have a backing file"); + goto done; + } } + cbi = (CommonBlockJobCBInfo){ + .errp = &local_err, + .bs = bs, + }; + + commit_active_start(bs, base_bs, 0, BLOCKDEV_ON_ERROR_REPORT, + common_block_job_cb, &cbi, &local_err); + if (local_err) { + goto done; + } + + /* The block job will swap base_bs and bs (which is not what we really want + * here, but okay) and unref base_bs (after the swap, i.e., the old top + * image). In order to still be able to empty that top image afterwards, + * increment the reference counter here preemptively. */ + if (!drop) { + bdrv_ref(base_bs); + } + + run_block_job(bs->job, &local_err); + if (local_err) { + goto unref_backing; + } + + if (!drop && base_bs->drv->bdrv_make_empty) { + ret = base_bs->drv->bdrv_make_empty(base_bs); + if (ret) { + error_setg_errno(&local_err, -ret, "Could not empty %s", + filename); + goto unref_backing; + } + } + +unref_backing: + if (!drop) { + bdrv_unref(base_bs); + } + +done: + qemu_progress_end(); + blk_unref(blk); - if (ret) { + + if (local_err) { + qerror_report_err(local_err); + error_free(local_err); return 1; } + + qprintf(quiet, "Image committed.\n"); return 0; } @@ -1025,7 +1126,6 @@ static int img_compare(int argc, char **argv) blk1 = img_open("image_1", filename1, fmt1, flags, true, quiet); if (!blk1) { - error_report("Can't open file %s", filename1); ret = 2; goto out3; } @@ -1033,7 +1133,6 @@ static int img_compare(int argc, char **argv) blk2 = img_open("image_2", filename2, fmt2, flags, true, quiet); if (!blk2) { - error_report("Can't open file %s", filename2); ret = 2; goto out2; } @@ -1381,7 +1480,6 @@ static int img_convert(int argc, char **argv) true, quiet); g_free(id); if (!blk[bs_i]) { - error_report("Could not open '%s'", argv[optind + bs_i]); ret = -1; goto out; } @@ -2770,6 +2868,12 @@ out: return 0; } +static void amend_status_cb(BlockDriverState *bs, + int64_t offset, int64_t total_work_size) +{ + qemu_progress_print(100.f * offset / total_work_size, 0); +} + static int img_amend(int argc, char **argv) { int c, ret = 0; @@ -2778,13 +2882,13 @@ static int img_amend(int argc, char **argv) QemuOpts *opts = NULL; const char *fmt = NULL, *filename, *cache; int flags; - bool quiet = false; + bool quiet = false, progress = false; BlockBackend *blk = NULL; BlockDriverState *bs = NULL; cache = BDRV_DEFAULT_CACHE; for (;;) { - c = getopt(argc, argv, "ho:f:t:q"); + c = getopt(argc, argv, "ho:f:t:pq"); if (c == -1) { break; } @@ -2814,6 +2918,9 @@ static int img_amend(int argc, char **argv) case 't': cache = optarg; break; + case 'p': + progress = true; + break; case 'q': quiet = true; break; @@ -2824,6 +2931,11 @@ static int img_amend(int argc, char **argv) error_exit("Must specify options (-o)"); } + if (quiet) { + progress = false; + } + qemu_progress_init(progress, 1.0); + filename = (optind == argc - 1) ? argv[argc - 1] : NULL; if (fmt && has_help_option(options)) { /* If a format is explicitly specified (and possibly no filename is @@ -2833,7 +2945,9 @@ static int img_amend(int argc, char **argv) } if (optind != argc - 1) { - error_exit("Expecting one image file name"); + error_report("Expecting one image file name"); + ret = -1; + goto out; } flags = BDRV_O_FLAGS | BDRV_O_RDWR; @@ -2845,7 +2959,6 @@ static int img_amend(int argc, char **argv) blk = img_open("image", filename, fmt, flags, true, quiet); if (!blk) { - error_report("Could not open image '%s'", filename); ret = -1; goto out; } @@ -2867,13 +2980,18 @@ static int img_amend(int argc, char **argv) goto out; } - ret = bdrv_amend_options(bs, opts); + /* In case the driver does not call amend_status_cb() */ + qemu_progress_print(0.f, 0); + ret = bdrv_amend_options(bs, opts, &amend_status_cb); + qemu_progress_print(100.f, 0); if (ret < 0) { error_report("Error while amending options: %s", strerror(-ret)); goto out; } out: + qemu_progress_end(); + blk_unref(blk); qemu_opts_del(opts); qemu_opts_free(create_opts); diff --git a/qemu-img.texi b/qemu-img.texi index e3ef4a00e9..0a1ab35989 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -167,7 +167,7 @@ this case. @var{backing_file} will never be modified unless you use the The size can also be specified using the @var{size} option with @code{-o}, it doesn't need to be specified separately in this case. -@item commit [-f @var{fmt}] [-t @var{cache}] @var{filename} +@item commit [-q] [-f @var{fmt}] [-t @var{cache}] [-b @var{base}] [-d] [-p] @var{filename} Commit the changes recorded in @var{filename} in its base image or backing file. If the backing file is smaller than the snapshot, then the backing file will be @@ -176,6 +176,20 @@ the backing file, the backing file will not be truncated. If you want the backing file to match the size of the smaller snapshot, you can safely truncate it yourself once the commit operation successfully completes. +The image @var{filename} is emptied after the operation has succeeded. If you do +not need @var{filename} afterwards and intend to drop it, you may skip emptying +@var{filename} by specifying the @code{-d} flag. + +If the backing chain of the given image file @var{filename} has more than one +layer, the backing file into which the changes will be committed may be +specified as @var{base} (which has to be part of @var{filename}'s backing +chain). If @var{base} is not specified, the immediate backing file of the top +image (which is @var{filename}) will be used. For reasons of consistency, +explicitly specifying @var{base} will always imply @code{-d} (since emptying an +image after committing to an indirect backing file would lead to different data +being read from the image due to content in the intermediate backing chain +overruling the commit target). + @item compare [-f @var{fmt}] [-F @var{fmt}] [-T @var{src_cache}] [-p] [-s] [-q] @var{filename1} @var{filename2} Check if two images have the same content. You can compare images with @@ -398,7 +412,7 @@ After using this command to grow a disk image, you must use file system and partitioning tools inside the VM to actually begin using the new space on the device. -@item amend [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename} +@item amend [-p] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename} Amends the image format specific @var{options} for the image file @var{filename}. Not all file formats support this operation. @@ -107,15 +107,6 @@ static void cpu_common_get_memory_mapping(CPUState *cpu, error_setg(errp, "Obtaining memory mappings is unsupported on this CPU."); } -/* CPU hot-plug notifiers */ -static NotifierList cpu_added_notifiers = - NOTIFIER_LIST_INITIALIZER(cpu_add_notifiers); - -void qemu_register_cpu_added_notifier(Notifier *notifier) -{ - notifier_list_add(&cpu_added_notifiers, notifier); -} - void cpu_reset_interrupt(CPUState *cpu, int mask) { cpu->interrupt_request &= ~mask; @@ -312,7 +303,6 @@ static void cpu_common_realizefn(DeviceState *dev, Error **errp) if (dev->hotplugged) { cpu_synchronize_post_init(cpu); - notifier_list_notify(&cpu_added_notifiers, dev); cpu_resume(cpu); } } diff --git a/roms/openbios b/roms/openbios -Subproject 5387e764743e86c527cc7feb2823a309754f2e5 +Subproject b95d5338f321498a11937ce52ecce510414d458 @@ -1246,7 +1246,7 @@ int load_vmstate(const char *name) void do_delvm(Monitor *mon, const QDict *qdict) { BlockDriverState *bs; - Error *err = NULL; + Error *err; const char *name = qdict_get_str(qdict, "name"); if (!find_vmstate_bs()) { @@ -1257,6 +1257,7 @@ void do_delvm(Monitor *mon, const QDict *qdict) bs = NULL; while ((bs = bdrv_next(bs))) { if (bdrv_can_snapshot(bs)) { + err = NULL; bdrv_snapshot_delete_by_id_or_name(bs, name, &err); if (err) { monitor_printf(mon, diff --git a/scripts/acpi_extract.py b/scripts/acpi_extract.py index 22ea468102..10c1ffb368 100755 --- a/scripts/acpi_extract.py +++ b/scripts/acpi_extract.py @@ -139,13 +139,16 @@ def aml_name_string(offset): offset += 1 return offset; -# Given data offset, find 8 byte buffer offset -def aml_data_buffer8(offset): - #0x08 NameOp NameString DataRef - expect = [0x11, 0x0B, 0x0A, 0x08] +# Given data offset, find variable length byte buffer offset +def aml_data_buffer(offset, length): + #0x11 PkgLength BufferSize ByteList + if (length > 63): + die( "Name offset 0x%x: expected a one byte PkgLength (length<=63)" % + (offset)); + expect = [0x11, length+3, 0x0A, length] if (aml[offset:offset+4] != expect): die( "Name offset 0x%x: expected %s actual %s" % - (offset, aml[offset:offset+4], expect)) + (offset, expect, aml[offset:offset+4])) return offset + len(expect) # Given data offset, find dword const offset @@ -172,9 +175,9 @@ def aml_data_byte_const(offset): (offset, aml[offset])); return offset + 1; -# Find name'd buffer8 -def aml_name_buffer8(offset): - return aml_data_buffer8(aml_name_string(offset) + 4) +# Find name'd buffer +def aml_name_buffer(offset, length): + return aml_data_buffer(aml_name_string(offset) + 4, length) # Given name offset, find dword const offset def aml_name_dword_const(offset): @@ -308,7 +311,9 @@ for i in range(len(asl)): output[array] = aml continue if (directive == "ACPI_EXTRACT_NAME_BUFFER8"): - offset = aml_name_buffer8(offset) + offset = aml_name_buffer(offset, 8) + elif (directive == "ACPI_EXTRACT_NAME_BUFFER16"): + offset = aml_name_buffer(offset, 16) elif (directive == "ACPI_EXTRACT_NAME_DWORD_CONST"): offset = aml_name_dword_const(offset) elif (directive == "ACPI_EXTRACT_NAME_WORD_CONST"): diff --git a/softmmu_template.h b/softmmu_template.h index 88e33900b6..6b4e615dbf 100644 --- a/softmmu_template.h +++ b/softmmu_template.h @@ -67,10 +67,10 @@ #endif #ifdef SOFTMMU_CODE_ACCESS -#define READ_ACCESS_TYPE 2 +#define READ_ACCESS_TYPE MMU_INST_FETCH #define ADDR_READ addr_code #else -#define READ_ACCESS_TYPE 0 +#define READ_ACCESS_TYPE MMU_DATA_LOAD #define ADDR_READ addr_read #endif @@ -396,11 +396,12 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { #ifdef ALIGNED_ONLY if ((addr & (DATA_SIZE - 1)) != 0) { - cpu_unaligned_access(ENV_GET_CPU(env), addr, 1, mmu_idx, retaddr); + cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE, + mmu_idx, retaddr); } #endif if (!VICTIM_TLB_HIT(addr_write)) { - tlb_fill(ENV_GET_CPU(env), addr, 1, mmu_idx, retaddr); + tlb_fill(ENV_GET_CPU(env), addr, MMU_DATA_STORE, mmu_idx, retaddr); } tlb_addr = env->tlb_table[mmu_idx][index].addr_write; } @@ -427,7 +428,8 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, int i; do_unaligned_access: #ifdef ALIGNED_ONLY - cpu_unaligned_access(ENV_GET_CPU(env), addr, 1, mmu_idx, retaddr); + cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE, + mmu_idx, retaddr); #endif /* XXX: not efficient, but simple */ /* Note: relies on the fact that tlb_fill() does not remove the @@ -446,7 +448,8 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, /* Handle aligned access or unaligned access in the same page. */ #ifdef ALIGNED_ONLY if ((addr & (DATA_SIZE - 1)) != 0) { - cpu_unaligned_access(ENV_GET_CPU(env), addr, 1, mmu_idx, retaddr); + cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE, + mmu_idx, retaddr); } #endif @@ -474,11 +477,12 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { #ifdef ALIGNED_ONLY if ((addr & (DATA_SIZE - 1)) != 0) { - cpu_unaligned_access(ENV_GET_CPU(env), addr, 1, mmu_idx, retaddr); + cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE, + mmu_idx, retaddr); } #endif if (!VICTIM_TLB_HIT(addr_write)) { - tlb_fill(ENV_GET_CPU(env), addr, 1, mmu_idx, retaddr); + tlb_fill(ENV_GET_CPU(env), addr, MMU_DATA_STORE, mmu_idx, retaddr); } tlb_addr = env->tlb_table[mmu_idx][index].addr_write; } @@ -505,7 +509,8 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, int i; do_unaligned_access: #ifdef ALIGNED_ONLY - cpu_unaligned_access(ENV_GET_CPU(env), addr, 1, mmu_idx, retaddr); + cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE, + mmu_idx, retaddr); #endif /* XXX: not efficient, but simple */ /* Note: relies on the fact that tlb_fill() does not remove the @@ -524,7 +529,8 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, /* Handle aligned access or unaligned access in the same page. */ #ifdef ALIGNED_ONLY if ((addr & (DATA_SIZE - 1)) != 0) { - cpu_unaligned_access(ENV_GET_CPU(env), addr, 1, mmu_idx, retaddr); + cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE, + mmu_idx, retaddr); } #endif diff --git a/target-arm/cpu.c b/target-arm/cpu.c index e0b82a6704..5ce7350ce6 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -203,15 +203,6 @@ bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request) cc->do_interrupt(cs); ret = true; } - /* ARMv7-M interrupt return works by loading a magic value - into the PC. On real hardware the load causes the - return to occur. The qemu implementation performs the - jump normally, then does the exception return when the - CPU tries to execute code at the magic address. - This will cause the magic PC value to be pushed to - the stack if an interrupt occurred at the wrong time. - We avoid this by disabling interrupts when - pc contains a magic address. */ if (interrupt_request & CPU_INTERRUPT_HARD && arm_excp_unmasked(cs, EXCP_IRQ)) { cs->exception_index = EXCP_IRQ; @@ -234,6 +225,42 @@ bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request) return ret; } +#if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) +static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + CPUClass *cc = CPU_GET_CLASS(cs); + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + bool ret = false; + + + if (interrupt_request & CPU_INTERRUPT_FIQ + && !(env->daif & PSTATE_F)) { + cs->exception_index = EXCP_FIQ; + cc->do_interrupt(cs); + ret = true; + } + /* ARMv7-M interrupt return works by loading a magic value + * into the PC. On real hardware the load causes the + * return to occur. The qemu implementation performs the + * jump normally, then does the exception return when the + * CPU tries to execute code at the magic address. + * This will cause the magic PC value to be pushed to + * the stack if an interrupt occurred at the wrong time. + * We avoid this by disabling interrupts when + * pc contains a magic address. + */ + if (interrupt_request & CPU_INTERRUPT_HARD + && !(env->daif & PSTATE_I) + && (env->regs[15] < 0xfffffff0)) { + cs->exception_index = EXCP_IRQ; + cc->do_interrupt(cs); + ret = true; + } + return ret; +} +#endif + #ifndef CONFIG_USER_ONLY static void arm_cpu_set_irq(void *opaque, int irq, int level) { @@ -670,11 +697,13 @@ static void cortex_m3_initfn(Object *obj) static void arm_v7m_class_init(ObjectClass *oc, void *data) { -#ifndef CONFIG_USER_ONLY CPUClass *cc = CPU_CLASS(oc); +#ifndef CONFIG_USER_ONLY cc->do_interrupt = arm_v7m_cpu_do_interrupt; #endif + + cc->cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt; } static const ARMCPRegInfo cortexa8_cp_reginfo[] = { diff --git a/target-arm/cpu.h b/target-arm/cpu.h index cb6ec5c59f..7f800908f4 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -1251,18 +1251,6 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx) bool secure = false; /* If in EL1/0, Physical IRQ routing to EL2 only happens from NS state. */ bool irq_can_hyp = !secure && cur_el < 2 && target_el == 2; - /* ARMv7-M interrupt return works by loading a magic value - * into the PC. On real hardware the load causes the - * return to occur. The qemu implementation performs the - * jump normally, then does the exception return when the - * CPU tries to execute code at the magic address. - * This will cause the magic PC value to be pushed to - * the stack if an interrupt occurred at the wrong time. - * We avoid this by disabling interrupts when - * pc contains a magic address. - */ - bool irq_unmasked = !(env->daif & PSTATE_I) - && (!IS_M(env) || env->regs[15] < 0xfffffff0); /* Don't take exceptions if they target a lower EL. */ if (cur_el > target_el) { @@ -1279,19 +1267,19 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx) if (irq_can_hyp && (env->cp15.hcr_el2 & HCR_IMO)) { return true; } - return irq_unmasked; + return !(env->daif & PSTATE_I); case EXCP_VFIQ: - if (!secure && !(env->cp15.hcr_el2 & HCR_FMO)) { + if (secure || !(env->cp15.hcr_el2 & HCR_FMO)) { /* VFIQs are only taken when hypervized and non-secure. */ return false; } return !(env->daif & PSTATE_F); case EXCP_VIRQ: - if (!secure && !(env->cp15.hcr_el2 & HCR_IMO)) { + if (secure || !(env->cp15.hcr_el2 & HCR_IMO)) { /* VIRQs are only taken when hypervized and non-secure. */ return false; } - return irq_unmasked; + return !(env->daif & PSTATE_I); default: g_assert_not_reached(); } diff --git a/target-arm/translate.c b/target-arm/translate.c index 1d52e4774f..af5156857e 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -38,16 +38,16 @@ #include "trace-tcg.h" -#define ENABLE_ARCH_4T arm_feature(env, ARM_FEATURE_V4T) -#define ENABLE_ARCH_5 arm_feature(env, ARM_FEATURE_V5) +#define ENABLE_ARCH_4T arm_dc_feature(s, ARM_FEATURE_V4T) +#define ENABLE_ARCH_5 arm_dc_feature(s, ARM_FEATURE_V5) /* currently all emulated v5 cores are also v5TE, so don't bother */ -#define ENABLE_ARCH_5TE arm_feature(env, ARM_FEATURE_V5) +#define ENABLE_ARCH_5TE arm_dc_feature(s, ARM_FEATURE_V5) #define ENABLE_ARCH_5J 0 -#define ENABLE_ARCH_6 arm_feature(env, ARM_FEATURE_V6) -#define ENABLE_ARCH_6K arm_feature(env, ARM_FEATURE_V6K) -#define ENABLE_ARCH_6T2 arm_feature(env, ARM_FEATURE_THUMB2) -#define ENABLE_ARCH_7 arm_feature(env, ARM_FEATURE_V7) -#define ENABLE_ARCH_8 arm_feature(env, ARM_FEATURE_V8) +#define ENABLE_ARCH_6 arm_dc_feature(s, ARM_FEATURE_V6) +#define ENABLE_ARCH_6K arm_dc_feature(s, ARM_FEATURE_V6K) +#define ENABLE_ARCH_6T2 arm_dc_feature(s, ARM_FEATURE_THUMB2) +#define ENABLE_ARCH_7 arm_dc_feature(s, ARM_FEATURE_V7) +#define ENABLE_ARCH_8 arm_dc_feature(s, ARM_FEATURE_V8) #define ARCH(x) do { if (!ENABLE_ARCH_##x) goto illegal_op; } while(0) @@ -834,8 +834,7 @@ static inline void gen_bx(DisasContext *s, TCGv_i32 var) /* Variant of store_reg which uses branch&exchange logic when storing to r15 in ARM architecture v7 and above. The source must be a temporary and will be marked as dead. */ -static inline void store_reg_bx(CPUARMState *env, DisasContext *s, - int reg, TCGv_i32 var) +static inline void store_reg_bx(DisasContext *s, int reg, TCGv_i32 var) { if (reg == 15 && ENABLE_ARCH_7) { gen_bx(s, var); @@ -848,8 +847,7 @@ static inline void store_reg_bx(CPUARMState *env, DisasContext *s, * to r15 in ARM architecture v5T and above. This is used for storing * the results of a LDR/LDM/POP into r15, and corresponds to the cases * in the ARM ARM which use the LoadWritePC() pseudocode function. */ -static inline void store_reg_from_load(CPUARMState *env, DisasContext *s, - int reg, TCGv_i32 var) +static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var) { if (reg == 15 && ENABLE_ARCH_5) { gen_bx(s, var); @@ -1541,7 +1539,7 @@ static inline int gen_iwmmxt_shift(uint32_t insn, uint32_t mask, TCGv_i32 dest) /* Disassemble an iwMMXt instruction. Returns nonzero if an error occurred (ie. an undefined instruction). */ -static int disas_iwmmxt_insn(CPUARMState *env, DisasContext *s, uint32_t insn) +static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) { int rd, wrd; int rdhi, rdlo, rd0, rd1, i; @@ -2547,7 +2545,7 @@ static int disas_iwmmxt_insn(CPUARMState *env, DisasContext *s, uint32_t insn) /* Disassemble an XScale DSP instruction. Returns nonzero if an error occurred (ie. an undefined instruction). */ -static int disas_dsp_insn(CPUARMState *env, DisasContext *s, uint32_t insn) +static int disas_dsp_insn(DisasContext *s, uint32_t insn) { int acc, rd0, rd1, rdhi, rdlo; TCGv_i32 tmp, tmp2; @@ -2619,7 +2617,7 @@ static int disas_dsp_insn(CPUARMState *env, DisasContext *s, uint32_t insn) #define VFP_SREG(insn, bigbit, smallbit) \ ((VFP_REG_SHR(insn, bigbit - 1) & 0x1e) | (((insn) >> (smallbit)) & 1)) #define VFP_DREG(reg, insn, bigbit, smallbit) do { \ - if (arm_feature(env, ARM_FEATURE_VFP3)) { \ + if (arm_dc_feature(s, ARM_FEATURE_VFP3)) { \ reg = (((insn) >> (bigbit)) & 0x0f) \ | (((insn) >> ((smallbit) - 4)) & 0x10); \ } else { \ @@ -2966,11 +2964,11 @@ static const uint8_t fp_decode_rm[] = { FPROUNDING_NEGINF, }; -static int disas_vfp_v8_insn(CPUARMState *env, DisasContext *s, uint32_t insn) +static int disas_vfp_v8_insn(DisasContext *s, uint32_t insn) { uint32_t rd, rn, rm, dp = extract32(insn, 8, 1); - if (!arm_feature(env, ARM_FEATURE_V8)) { + if (!arm_dc_feature(s, ARM_FEATURE_V8)) { return 1; } @@ -3002,7 +3000,7 @@ static int disas_vfp_v8_insn(CPUARMState *env, DisasContext *s, uint32_t insn) /* Disassemble a VFP instruction. Returns nonzero if an error occurred (ie. an undefined instruction). */ -static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) +static int disas_vfp_insn(DisasContext *s, uint32_t insn) { uint32_t rd, rn, rm, op, i, n, offset, delta_d, delta_m, bank_mask; int dp, veclen; @@ -3010,8 +3008,9 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) TCGv_i32 tmp; TCGv_i32 tmp2; - if (!arm_feature(env, ARM_FEATURE_VFP)) + if (!arm_dc_feature(s, ARM_FEATURE_VFP)) { return 1; + } /* FIXME: this access check should not take precedence over UNDEF * for invalid encodings; we will generate incorrect syndrome information @@ -3038,7 +3037,7 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) /* Encodings with T=1 (Thumb) or unconditional (ARM): * only used in v8 and above. */ - return disas_vfp_v8_insn(env, s, insn); + return disas_vfp_v8_insn(s, insn); } dp = ((insn & 0xf00) == 0xb00); @@ -3055,8 +3054,9 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) if (insn & 0xf) return 1; if (insn & 0x00c00060 - && !arm_feature(env, ARM_FEATURE_NEON)) + && !arm_dc_feature(s, ARM_FEATURE_NEON)) { return 1; + } pass = (insn >> 21) & 1; if (insn & (1 << 22)) { @@ -3151,8 +3151,9 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) VFP3 restricts all id registers to privileged accesses. */ if (IS_USER(s) - && arm_feature(env, ARM_FEATURE_VFP3)) + && arm_dc_feature(s, ARM_FEATURE_VFP3)) { return 1; + } tmp = load_cpu_field(vfp.xregs[rn]); break; case ARM_VFP_FPEXC: @@ -3164,8 +3165,9 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) case ARM_VFP_FPINST2: /* Not present in VFP3. */ if (IS_USER(s) - || arm_feature(env, ARM_FEATURE_VFP3)) + || arm_dc_feature(s, ARM_FEATURE_VFP3)) { return 1; + } tmp = load_cpu_field(vfp.xregs[rn]); break; case ARM_VFP_FPSCR: @@ -3178,15 +3180,16 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) } break; case ARM_VFP_MVFR2: - if (!arm_feature(env, ARM_FEATURE_V8)) { + if (!arm_dc_feature(s, ARM_FEATURE_V8)) { return 1; } /* fall through */ case ARM_VFP_MVFR0: case ARM_VFP_MVFR1: if (IS_USER(s) - || !arm_feature(env, ARM_FEATURE_MVFR)) + || !arm_dc_feature(s, ARM_FEATURE_MVFR)) { return 1; + } tmp = load_cpu_field(vfp.xregs[rn]); break; default: @@ -3367,8 +3370,8 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) * UNPREDICTABLE if bit 8 is set prior to ARMv8 * (we choose to UNDEF) */ - if ((dp && !arm_feature(env, ARM_FEATURE_V8)) || - !arm_feature(env, ARM_FEATURE_VFP_FP16)) { + if ((dp && !arm_dc_feature(s, ARM_FEATURE_V8)) || + !arm_dc_feature(s, ARM_FEATURE_VFP_FP16)) { return 1; } if (!extract32(rn, 1, 1)) { @@ -3447,7 +3450,7 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) * correct : an input NaN should come out with its sign bit * flipped if it is a negated-input. */ - if (!arm_feature(env, ARM_FEATURE_VFP4)) { + if (!arm_dc_feature(s, ARM_FEATURE_VFP4)) { return 1; } if (dp) { @@ -3488,8 +3491,9 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) } break; case 14: /* fconst */ - if (!arm_feature(env, ARM_FEATURE_VFP3)) - return 1; + if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { + return 1; + } n = (insn << 12) & 0x80000000; i = ((insn >> 12) & 0x70) | (insn & 0xf); @@ -3644,23 +3648,27 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) gen_vfp_sito(dp, 0); break; case 20: /* fshto */ - if (!arm_feature(env, ARM_FEATURE_VFP3)) - return 1; + if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { + return 1; + } gen_vfp_shto(dp, 16 - rm, 0); break; case 21: /* fslto */ - if (!arm_feature(env, ARM_FEATURE_VFP3)) - return 1; + if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { + return 1; + } gen_vfp_slto(dp, 32 - rm, 0); break; case 22: /* fuhto */ - if (!arm_feature(env, ARM_FEATURE_VFP3)) - return 1; + if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { + return 1; + } gen_vfp_uhto(dp, 16 - rm, 0); break; case 23: /* fulto */ - if (!arm_feature(env, ARM_FEATURE_VFP3)) - return 1; + if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { + return 1; + } gen_vfp_ulto(dp, 32 - rm, 0); break; case 24: /* ftoui */ @@ -3676,23 +3684,27 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) gen_vfp_tosiz(dp, 0); break; case 28: /* ftosh */ - if (!arm_feature(env, ARM_FEATURE_VFP3)) - return 1; + if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { + return 1; + } gen_vfp_tosh(dp, 16 - rm, 0); break; case 29: /* ftosl */ - if (!arm_feature(env, ARM_FEATURE_VFP3)) - return 1; + if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { + return 1; + } gen_vfp_tosl(dp, 32 - rm, 0); break; case 30: /* ftouh */ - if (!arm_feature(env, ARM_FEATURE_VFP3)) - return 1; + if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { + return 1; + } gen_vfp_touh(dp, 16 - rm, 0); break; case 31: /* ftoul */ - if (!arm_feature(env, ARM_FEATURE_VFP3)) - return 1; + if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { + return 1; + } gen_vfp_toul(dp, 32 - rm, 0); break; default: /* undefined */ @@ -3948,7 +3960,8 @@ static inline void gen_mulxy(TCGv_i32 t0, TCGv_i32 t1, int x, int y) } /* Return the mask of PSR bits set by a MSR instruction. */ -static uint32_t msr_mask(CPUARMState *env, DisasContext *s, int flags, int spsr) { +static uint32_t msr_mask(DisasContext *s, int flags, int spsr) +{ uint32_t mask; mask = 0; @@ -3963,14 +3976,18 @@ static uint32_t msr_mask(CPUARMState *env, DisasContext *s, int flags, int spsr) /* Mask out undefined bits. */ mask &= ~CPSR_RESERVED; - if (!arm_feature(env, ARM_FEATURE_V4T)) + if (!arm_dc_feature(s, ARM_FEATURE_V4T)) { mask &= ~CPSR_T; - if (!arm_feature(env, ARM_FEATURE_V5)) + } + if (!arm_dc_feature(s, ARM_FEATURE_V5)) { mask &= ~CPSR_Q; /* V5TE in reality*/ - if (!arm_feature(env, ARM_FEATURE_V6)) + } + if (!arm_dc_feature(s, ARM_FEATURE_V6)) { mask &= ~(CPSR_E | CPSR_GE); - if (!arm_feature(env, ARM_FEATURE_THUMB2)) + } + if (!arm_dc_feature(s, ARM_FEATURE_THUMB2)) { mask &= ~CPSR_IT; + } /* Mask out execution state and reserved bits. */ if (!spsr) { mask &= ~(CPSR_EXEC | CPSR_RESERVED); @@ -4294,7 +4311,7 @@ static struct { /* Translate a NEON load/store element instruction. Return nonzero if the instruction is invalid. */ -static int disas_neon_ls_insn(CPUARMState * env, DisasContext *s, uint32_t insn) +static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) { int rd, rn, rm; int op; @@ -5036,7 +5053,7 @@ static const uint8_t neon_2rm_sizes[] = { We process data in a mixture of 32-bit and 64-bit chunks. Mostly we use 32-bit chunks so we can use normal scalar instructions. */ -static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t insn) +static int disas_neon_data_insn(DisasContext *s, uint32_t insn) { int op; int q; @@ -5092,7 +5109,7 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins return 1; } if (!u) { /* SHA-1 */ - if (!arm_feature(env, ARM_FEATURE_V8_SHA1)) { + if (!arm_dc_feature(s, ARM_FEATURE_V8_SHA1)) { return 1; } tmp = tcg_const_i32(rd); @@ -5102,7 +5119,7 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins gen_helper_crypto_sha1_3reg(cpu_env, tmp, tmp2, tmp3, tmp4); tcg_temp_free_i32(tmp4); } else { /* SHA-256 */ - if (!arm_feature(env, ARM_FEATURE_V8_SHA256) || size == 3) { + if (!arm_dc_feature(s, ARM_FEATURE_V8_SHA256) || size == 3) { return 1; } tmp = tcg_const_i32(rd); @@ -5237,7 +5254,7 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins break; case NEON_3R_FLOAT_MISC: /* VMAXNM/VMINNM in ARMv8 */ - if (u && !arm_feature(env, ARM_FEATURE_V8)) { + if (u && !arm_dc_feature(s, ARM_FEATURE_V8)) { return 1; } break; @@ -5248,7 +5265,7 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins } break; case NEON_3R_VFM: - if (!arm_feature(env, ARM_FEATURE_VFP4) || u) { + if (!arm_dc_feature(s, ARM_FEATURE_VFP4) || u) { return 1; } break; @@ -6067,7 +6084,7 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins if (op == 14 && size == 2) { TCGv_i64 tcg_rn, tcg_rm, tcg_rd; - if (!arm_feature(env, ARM_FEATURE_V8_PMULL)) { + if (!arm_dc_feature(s, ARM_FEATURE_V8_PMULL)) { return 1; } tcg_rn = tcg_temp_new_i64(); @@ -6555,7 +6572,7 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins } break; case NEON_2RM_VCVT_F16_F32: - if (!arm_feature(env, ARM_FEATURE_VFP_FP16) || + if (!arm_dc_feature(s, ARM_FEATURE_VFP_FP16) || q || (rm & 1)) { return 1; } @@ -6579,7 +6596,7 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins tcg_temp_free_i32(tmp); break; case NEON_2RM_VCVT_F32_F16: - if (!arm_feature(env, ARM_FEATURE_VFP_FP16) || + if (!arm_dc_feature(s, ARM_FEATURE_VFP_FP16) || q || (rd & 1)) { return 1; } @@ -6603,7 +6620,7 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins tcg_temp_free_i32(tmp3); break; case NEON_2RM_AESE: case NEON_2RM_AESMC: - if (!arm_feature(env, ARM_FEATURE_V8_AES) + if (!arm_dc_feature(s, ARM_FEATURE_V8_AES) || ((rm | rd) & 1)) { return 1; } @@ -6625,7 +6642,7 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins tcg_temp_free_i32(tmp3); break; case NEON_2RM_SHA1H: - if (!arm_feature(env, ARM_FEATURE_V8_SHA1) + if (!arm_dc_feature(s, ARM_FEATURE_V8_SHA1) || ((rm | rd) & 1)) { return 1; } @@ -6643,10 +6660,10 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins } /* bit 6 (q): set -> SHA256SU0, cleared -> SHA1SU1 */ if (q) { - if (!arm_feature(env, ARM_FEATURE_V8_SHA256)) { + if (!arm_dc_feature(s, ARM_FEATURE_V8_SHA256)) { return 1; } - } else if (!arm_feature(env, ARM_FEATURE_V8_SHA1)) { + } else if (!arm_dc_feature(s, ARM_FEATURE_V8_SHA1)) { return 1; } tmp = tcg_const_i32(rd); @@ -7031,7 +7048,7 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins return 0; } -static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn) +static int disas_coproc_insn(DisasContext *s, uint32_t insn) { int cpnum, is64, crn, crm, opc1, opc2, isread, rt, rt2; const ARMCPRegInfo *ri; @@ -7039,14 +7056,14 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn) cpnum = (insn >> 8) & 0xf; /* First check for coprocessor space used for XScale/iwMMXt insns */ - if (arm_feature(env, ARM_FEATURE_XSCALE) && (cpnum < 2)) { + if (arm_dc_feature(s, ARM_FEATURE_XSCALE) && (cpnum < 2)) { if (extract32(s->c15_cpar, cpnum, 1) == 0) { return 1; } - if (arm_feature(env, ARM_FEATURE_IWMMXT)) { - return disas_iwmmxt_insn(env, s, insn); - } else if (arm_feature(env, ARM_FEATURE_XSCALE)) { - return disas_dsp_insn(env, s, insn); + if (arm_dc_feature(s, ARM_FEATURE_IWMMXT)) { + return disas_iwmmxt_insn(s, insn); + } else if (arm_dc_feature(s, ARM_FEATURE_XSCALE)) { + return disas_dsp_insn(s, insn); } return 1; } @@ -7082,7 +7099,7 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn) } if (ri->accessfn || - (arm_feature(env, ARM_FEATURE_XSCALE) && cpnum < 14)) { + (arm_dc_feature(s, ARM_FEATURE_XSCALE) && cpnum < 14)) { /* Emit code to perform further access permissions checks at * runtime; this may result in an exception. * Note that on XScale all cp0..c13 registers do an access check @@ -7125,7 +7142,7 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn) * in which case the syndrome information won't actually be * guest visible. */ - assert(!arm_feature(env, ARM_FEATURE_V8)); + assert(!arm_dc_feature(s, ARM_FEATURE_V8)); syndrome = syn_uncategorized(); break; } @@ -7543,21 +7560,19 @@ static void gen_srs(DisasContext *s, tcg_temp_free_i32(addr); } -static void disas_arm_insn(CPUARMState * env, DisasContext *s) +static void disas_arm_insn(DisasContext *s, unsigned int insn) { - unsigned int cond, insn, val, op1, i, shift, rm, rs, rn, rd, sh; + unsigned int cond, val, op1, i, shift, rm, rs, rn, rd, sh; TCGv_i32 tmp; TCGv_i32 tmp2; TCGv_i32 tmp3; TCGv_i32 addr; TCGv_i64 tmp64; - insn = arm_ldl_code(env, s->pc, s->bswap_code); - s->pc += 4; - /* M variants do not implement ARM mode. */ - if (IS_M(env)) + if (arm_dc_feature(s, ARM_FEATURE_M)) { goto illegal_op; + } cond = insn >> 28; if (cond == 0xf){ /* In ARMv3 and v4 the NV condition is UNPREDICTABLE; we @@ -7569,25 +7584,29 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) /* Unconditional instructions. */ if (((insn >> 25) & 7) == 1) { /* NEON Data processing. */ - if (!arm_feature(env, ARM_FEATURE_NEON)) + if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { goto illegal_op; + } - if (disas_neon_data_insn(env, s, insn)) + if (disas_neon_data_insn(s, insn)) { goto illegal_op; + } return; } if ((insn & 0x0f100000) == 0x04000000) { /* NEON load/store. */ - if (!arm_feature(env, ARM_FEATURE_NEON)) + if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { goto illegal_op; + } - if (disas_neon_ls_insn(env, s, insn)) + if (disas_neon_ls_insn(s, insn)) { goto illegal_op; + } return; } if ((insn & 0x0f000e10) == 0x0e000a00) { /* VFP. */ - if (disas_vfp_insn(env, s, insn)) { + if (disas_vfp_insn(s, insn)) { goto illegal_op; } return; @@ -7596,7 +7615,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) ((insn & 0x0f30f010) == 0x0710f000)) { if ((insn & (1 << 22)) == 0) { /* PLDW; v7MP */ - if (!arm_feature(env, ARM_FEATURE_V7MP)) { + if (!arm_dc_feature(s, ARM_FEATURE_V7MP)) { goto illegal_op; } } @@ -7611,7 +7630,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) } if (((insn & 0x0f700000) == 0x04100000) || ((insn & 0x0f700010) == 0x06100000)) { - if (!arm_feature(env, ARM_FEATURE_V7MP)) { + if (!arm_dc_feature(s, ARM_FEATURE_V7MP)) { goto illegal_op; } return; /* v7MP: Unallocated memory hint: must NOP */ @@ -7708,10 +7727,10 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) gen_bx_im(s, val); return; } else if ((insn & 0x0e000f00) == 0x0c000100) { - if (arm_feature(env, ARM_FEATURE_IWMMXT)) { + if (arm_dc_feature(s, ARM_FEATURE_IWMMXT)) { /* iWMMXt register transfer. */ if (extract32(s->c15_cpar, 1, 1)) { - if (!disas_iwmmxt_insn(env, s, insn)) { + if (!disas_iwmmxt_insn(s, insn)) { return; } } @@ -7784,8 +7803,10 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) if (shift) val = (val >> shift) | (val << (32 - shift)); i = ((insn & (1 << 22)) != 0); - if (gen_set_psr_im(s, msr_mask(env, s, (insn >> 16) & 0xf, i), i, val)) + if (gen_set_psr_im(s, msr_mask(s, (insn >> 16) & 0xf, i), + i, val)) { goto illegal_op; + } } } } else if ((insn & 0x0f900000) == 0x01000000 @@ -7800,7 +7821,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) /* PSR = reg */ tmp = load_reg(s, rm); i = ((op1 & 2) != 0); - if (gen_set_psr(s, msr_mask(env, s, (insn >> 16) & 0xf, i), i, tmp)) + if (gen_set_psr(s, msr_mask(s, (insn >> 16) & 0xf, i), i, tmp)) goto illegal_op; } else { /* reg = PSR */ @@ -7864,7 +7885,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) * 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 || + if (!arm_dc_feature(s, ARM_FEATURE_CRC) || op1 == 0x3 || (c & 0xd) != 0) { goto illegal_op; } @@ -8038,14 +8059,14 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) if (logic_cc) { gen_logic_CC(tmp); } - store_reg_bx(env, s, rd, tmp); + store_reg_bx(s, rd, tmp); break; case 0x01: tcg_gen_xor_i32(tmp, tmp, tmp2); if (logic_cc) { gen_logic_CC(tmp); } - store_reg_bx(env, s, rd, tmp); + store_reg_bx(s, rd, tmp); break; case 0x02: if (set_cc && rd == 15) { @@ -8061,7 +8082,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) } else { tcg_gen_sub_i32(tmp, tmp, tmp2); } - store_reg_bx(env, s, rd, tmp); + store_reg_bx(s, rd, tmp); } break; case 0x03: @@ -8070,7 +8091,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) } else { tcg_gen_sub_i32(tmp, tmp2, tmp); } - store_reg_bx(env, s, rd, tmp); + store_reg_bx(s, rd, tmp); break; case 0x04: if (set_cc) { @@ -8078,7 +8099,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) } else { tcg_gen_add_i32(tmp, tmp, tmp2); } - store_reg_bx(env, s, rd, tmp); + store_reg_bx(s, rd, tmp); break; case 0x05: if (set_cc) { @@ -8086,7 +8107,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) } else { gen_add_carry(tmp, tmp, tmp2); } - store_reg_bx(env, s, rd, tmp); + store_reg_bx(s, rd, tmp); break; case 0x06: if (set_cc) { @@ -8094,7 +8115,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) } else { gen_sub_carry(tmp, tmp, tmp2); } - store_reg_bx(env, s, rd, tmp); + store_reg_bx(s, rd, tmp); break; case 0x07: if (set_cc) { @@ -8102,7 +8123,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) } else { gen_sub_carry(tmp, tmp2, tmp); } - store_reg_bx(env, s, rd, tmp); + store_reg_bx(s, rd, tmp); break; case 0x08: if (set_cc) { @@ -8135,7 +8156,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) if (logic_cc) { gen_logic_CC(tmp); } - store_reg_bx(env, s, rd, tmp); + store_reg_bx(s, rd, tmp); break; case 0x0d: if (logic_cc && rd == 15) { @@ -8148,7 +8169,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) if (logic_cc) { gen_logic_CC(tmp2); } - store_reg_bx(env, s, rd, tmp2); + store_reg_bx(s, rd, tmp2); } break; case 0x0e: @@ -8156,7 +8177,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) if (logic_cc) { gen_logic_CC(tmp); } - store_reg_bx(env, s, rd, tmp); + store_reg_bx(s, rd, tmp); break; default: case 0x0f: @@ -8164,7 +8185,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) if (logic_cc) { gen_logic_CC(tmp2); } - store_reg_bx(env, s, rd, tmp2); + store_reg_bx(s, rd, tmp2); break; } if (op1 != 0x0f && op1 != 0x0d) { @@ -8673,7 +8694,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) case 1: case 3: /* SDIV, UDIV */ - if (!arm_feature(env, ARM_FEATURE_ARM_DIV)) { + if (!arm_dc_feature(s, ARM_FEATURE_ARM_DIV)) { goto illegal_op; } if (((insn >> 5) & 7) || (rd != 15)) { @@ -8802,7 +8823,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) } if (insn & (1 << 20)) { /* Complete the load. */ - store_reg_from_load(env, s, rd, tmp); + store_reg_from_load(s, rd, tmp); } break; case 0x08: @@ -8865,7 +8886,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) loaded_var = tmp; loaded_base = 1; } else { - store_reg_from_load(env, s, i, tmp); + store_reg_from_load(s, i, tmp); } } else { /* store */ @@ -8948,10 +8969,10 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) case 0xe: if (((insn >> 8) & 0xe) == 10) { /* VFP. */ - if (disas_vfp_insn(env, s, insn)) { + if (disas_vfp_insn(s, insn)) { goto illegal_op; } - } else if (disas_coproc_insn(env, s, insn)) { + } else if (disas_coproc_insn(s, insn)) { /* Coprocessor. */ goto illegal_op; } @@ -9069,8 +9090,8 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw int conds; int logic_cc; - if (!(arm_feature(env, ARM_FEATURE_THUMB2) - || arm_feature (env, ARM_FEATURE_M))) { + if (!(arm_dc_feature(s, ARM_FEATURE_THUMB2) + || arm_dc_feature(s, ARM_FEATURE_M))) { /* Thumb-1 cores may need to treat bl and blx as a pair of 16-bit instructions to get correct prefetch abort behavior. */ insn = insn_hw1; @@ -9280,7 +9301,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw /* Load/store multiple, RFE, SRS. */ if (((insn >> 23) & 1) == ((insn >> 24) & 1)) { /* RFE, SRS: not available in user mode or on M profile */ - if (IS_USER(s) || IS_M(env)) { + if (IS_USER(s) || arm_dc_feature(s, ARM_FEATURE_M)) { goto illegal_op; } if (insn & (1 << 20)) { @@ -9432,7 +9453,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw gen_arm_shift_reg(tmp, op, tmp2, logic_cc); if (logic_cc) gen_logic_CC(tmp); - store_reg_bx(env, s, rd, tmp); + store_reg_bx(s, rd, tmp); break; case 1: /* Sign/zero extend. */ tmp = load_reg(s, rm); @@ -9523,7 +9544,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw uint32_t sz = op & 0x3; uint32_t c = op & 0x8; - if (!arm_feature(env, ARM_FEATURE_CRC)) { + if (!arm_dc_feature(s, ARM_FEATURE_CRC)) { goto illegal_op; } @@ -9651,7 +9672,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp2 = load_reg(s, rm); if ((op & 0x50) == 0x10) { /* sdiv, udiv */ - if (!arm_feature(env, ARM_FEATURE_THUMB_DIV)) { + if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DIV)) { goto illegal_op; } if (op & 0x20) @@ -9714,17 +9735,19 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw if (((insn >> 24) & 3) == 3) { /* Translate into the equivalent ARM encoding. */ insn = (insn & 0xe2ffffff) | ((insn & (1 << 28)) >> 4) | (1 << 28); - if (disas_neon_data_insn(env, s, insn)) + if (disas_neon_data_insn(s, insn)) { goto illegal_op; + } } else if (((insn >> 8) & 0xe) == 10) { - if (disas_vfp_insn(env, s, insn)) { + if (disas_vfp_insn(s, insn)) { goto illegal_op; } } else { if (insn & (1 << 28)) goto illegal_op; - if (disas_coproc_insn (env, s, insn)) + if (disas_coproc_insn(s, insn)) { goto illegal_op; + } } break; case 8: case 9: case 10: case 11: @@ -9784,7 +9807,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw op = (insn >> 20) & 7; switch (op) { case 0: /* msr cpsr. */ - if (IS_M(env)) { + if (arm_dc_feature(s, ARM_FEATURE_M)) { tmp = load_reg(s, rn); addr = tcg_const_i32(insn & 0xff); gen_helper_v7m_msr(cpu_env, addr, tmp); @@ -9795,11 +9818,12 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw } /* fall through */ case 1: /* msr spsr. */ - if (IS_M(env)) + if (arm_dc_feature(s, ARM_FEATURE_M)) { goto illegal_op; + } tmp = load_reg(s, rn); if (gen_set_psr(s, - msr_mask(env, s, (insn >> 8) & 0xf, op == 1), + msr_mask(s, (insn >> 8) & 0xf, op == 1), op == 1, tmp)) goto illegal_op; break; @@ -9864,7 +9888,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw break; case 6: /* mrs cpsr. */ tmp = tcg_temp_new_i32(); - if (IS_M(env)) { + if (arm_dc_feature(s, ARM_FEATURE_M)) { addr = tcg_const_i32(insn & 0xff); gen_helper_v7m_mrs(tmp, cpu_env, addr); tcg_temp_free_i32(addr); @@ -9875,8 +9899,9 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw break; case 7: /* mrs spsr. */ /* Not accessible in user mode. */ - if (IS_USER(s) || IS_M(env)) + if (IS_USER(s) || arm_dc_feature(s, ARM_FEATURE_M)) { goto illegal_op; + } tmp = load_cpu_field(spsr); store_reg(s, rd, tmp); break; @@ -10064,8 +10089,9 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw int writeback = 0; int memidx; if ((insn & 0x01100000) == 0x01000000) { - if (disas_neon_ls_insn(env, s, insn)) + if (disas_neon_ls_insn(s, insn)) { goto illegal_op; + } break; } op = ((insn >> 21) & 3) | ((insn >> 22) & 4); @@ -10761,7 +10787,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) store_reg(s, 13, addr); /* set the new PC value */ if ((insn & 0x0900) == 0x0900) { - store_reg_from_load(env, s, 15, tmp); + store_reg_from_load(s, 15, tmp); } break; @@ -10831,7 +10857,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (IS_USER(s)) { break; } - if (IS_M(env)) { + if (arm_dc_feature(s, ARM_FEATURE_M)) { tmp = tcg_const_i32((insn & (1 << 4)) != 0); /* FAULTMASK */ if (insn & 1) { @@ -11103,7 +11129,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, break; } #else - if (dc->pc >= 0xfffffff0 && IS_M(env)) { + if (dc->pc >= 0xfffffff0 && arm_dc_feature(dc, ARM_FEATURE_M)) { /* We always get here via a jump, so know we are not in a conditional execution block. */ gen_exception_internal(EXCP_EXCEPTION_EXIT); @@ -11170,7 +11196,9 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, } } } else { - disas_arm_insn(env, dc); + unsigned int insn = arm_ldl_code(env, dc->pc, dc->bswap_code); + dc->pc += 4; + disas_arm_insn(dc, insn); } if (dc->condjmp && !dc->is_jmp) { diff --git a/target-i386/cpu.c b/target-i386/cpu.c index 236bbeeeee..3f13dfe5f5 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -459,14 +459,21 @@ static uint32_t kvm_default_features[FEATURE_WORDS] = { /* Features that are not added by default to any CPU model when KVM is enabled. */ static uint32_t kvm_default_unset_features[FEATURE_WORDS] = { + [FEAT_1_EDX] = CPUID_ACPI, [FEAT_1_ECX] = CPUID_EXT_MONITOR, + [FEAT_8000_0001_ECX] = CPUID_EXT3_SVM, }; -void x86_cpu_compat_disable_kvm_features(FeatureWord w, uint32_t features) +void x86_cpu_compat_kvm_no_autoenable(FeatureWord w, uint32_t features) { kvm_default_features[w] &= ~features; } +void x86_cpu_compat_kvm_no_autodisable(FeatureWord w, uint32_t features) +{ + kvm_default_unset_features[w] &= ~features; +} + /* * Returns the set of feature flags that are supported and migratable by * QEMU, for a given FeatureWord. @@ -678,10 +685,11 @@ static X86CPUDefinition builtin_x86_defs[] = { .family = 16, .model = 2, .stepping = 3, + /* Missing: CPUID_HT */ .features[FEAT_1_EDX] = PPRO_FEATURES | CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | - CPUID_PSE36 | CPUID_VME | CPUID_HT, + CPUID_PSE36 | CPUID_VME, .features[FEAT_1_ECX] = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_CX16 | CPUID_EXT_POPCNT, @@ -697,8 +705,9 @@ static X86CPUDefinition builtin_x86_defs[] = { .features[FEAT_8000_0001_ECX] = CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A, + /* Missing: CPUID_SVM_LBRV */ .features[FEAT_SVM] = - CPUID_SVM_NPT | CPUID_SVM_LBRV, + CPUID_SVM_NPT, .xlevel = 0x8000001A, .model_id = "AMD Phenom(tm) 9550 Quad-Core Processor" }, @@ -709,15 +718,16 @@ static X86CPUDefinition builtin_x86_defs[] = { .family = 6, .model = 15, .stepping = 11, + /* Missing: CPUID_DTS, CPUID_HT, CPUID_TM, CPUID_PBE */ .features[FEAT_1_EDX] = PPRO_FEATURES | CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | - CPUID_PSE36 | CPUID_VME | CPUID_DTS | CPUID_ACPI | CPUID_SS | - CPUID_HT | CPUID_TM | CPUID_PBE, + CPUID_PSE36 | CPUID_VME | CPUID_ACPI | CPUID_SS, + /* Missing: CPUID_EXT_DTES64, CPUID_EXT_DSCPL, CPUID_EXT_EST, + * CPUID_EXT_TM2, CPUID_EXT_XTPR, CPUID_EXT_PDCM, CPUID_EXT_VMX */ .features[FEAT_1_ECX] = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_SSSE3 | - CPUID_EXT_DTES64 | CPUID_EXT_DSCPL | CPUID_EXT_VMX | CPUID_EXT_EST | - CPUID_EXT_TM2 | CPUID_EXT_CX16 | CPUID_EXT_XTPR | CPUID_EXT_PDCM, + CPUID_EXT_CX16, .features[FEAT_8000_0001_EDX] = CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, .features[FEAT_8000_0001_ECX] = @@ -792,13 +802,15 @@ static X86CPUDefinition builtin_x86_defs[] = { .family = 6, .model = 14, .stepping = 8, + /* Missing: CPUID_DTS, CPUID_HT, CPUID_TM, CPUID_PBE */ .features[FEAT_1_EDX] = PPRO_FEATURES | CPUID_VME | - CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_DTS | CPUID_ACPI | - CPUID_SS | CPUID_HT | CPUID_TM | CPUID_PBE, + CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_ACPI | + CPUID_SS, + /* Missing: CPUID_EXT_EST, CPUID_EXT_TM2 , CPUID_EXT_XTPR, + * CPUID_EXT_PDCM, CPUID_EXT_VMX */ .features[FEAT_1_ECX] = - CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_VMX | - CPUID_EXT_EST | CPUID_EXT_TM2 | CPUID_EXT_XTPR | CPUID_EXT_PDCM, + CPUID_EXT_SSE3 | CPUID_EXT_MONITOR, .features[FEAT_8000_0001_EDX] = CPUID_EXT2_NX, .xlevel = 0x80000008, @@ -871,14 +883,16 @@ static X86CPUDefinition builtin_x86_defs[] = { .family = 6, .model = 28, .stepping = 2, + /* Missing: CPUID_DTS, CPUID_HT, CPUID_TM, CPUID_PBE */ .features[FEAT_1_EDX] = PPRO_FEATURES | - CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_VME | CPUID_DTS | - CPUID_ACPI | CPUID_SS | CPUID_HT | CPUID_TM | CPUID_PBE, + CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_VME | + CPUID_ACPI | CPUID_SS, /* Some CPUs got no CPUID_SEP */ + /* Missing: CPUID_EXT_DSCPL, CPUID_EXT_EST, CPUID_EXT_TM2, + * CPUID_EXT_XTPR */ .features[FEAT_1_ECX] = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_SSSE3 | - CPUID_EXT_DSCPL | CPUID_EXT_EST | CPUID_EXT_TM2 | CPUID_EXT_XTPR | CPUID_EXT_MOVBE, .features[FEAT_8000_0001_EDX] = (PPRO_FEATURES & CPUID_EXT2_AMD_ALIASES) | diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 9f018312b0..1b2c12ad94 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -1365,7 +1365,8 @@ void cpu_report_tpr_access(CPUX86State *env, TPRAccess access); void x86_cpu_compat_set_features(const char *cpu_model, FeatureWord w, uint32_t feat_add, uint32_t feat_remove); -void x86_cpu_compat_disable_kvm_features(FeatureWord w, uint32_t features); +void x86_cpu_compat_kvm_no_autoenable(FeatureWord w, uint32_t features); +void x86_cpu_compat_kvm_no_autodisable(FeatureWord w, uint32_t features); /* Return name of 32-bit register, from a R_* constant */ diff --git a/target-mips/Makefile.objs b/target-mips/Makefile.objs index 716244f3d5..108fd9b501 100644 --- a/target-mips/Makefile.objs +++ b/target-mips/Makefile.objs @@ -1,4 +1,4 @@ obj-y += translate.o dsp_helper.o op_helper.o lmi_helper.o helper.o cpu.o -obj-y += gdbstub.o +obj-y += gdbstub.o msa_helper.o obj-$(CONFIG_SOFTMMU) += machine.o obj-$(CONFIG_KVM) += kvm.o diff --git a/target-mips/cpu.h b/target-mips/cpu.h index 26e7894eaf..c01bbdac2d 100644 --- a/target-mips/cpu.h +++ b/target-mips/cpu.h @@ -30,6 +30,11 @@ struct r4k_tlb_t { uint_fast16_t V1:1; uint_fast16_t D0:1; uint_fast16_t D1:1; + uint_fast16_t XI0:1; + uint_fast16_t XI1:1; + uint_fast16_t RI0:1; + uint_fast16_t RI1:1; + uint_fast16_t EHINV:1; target_ulong PFN[2]; }; @@ -43,6 +48,8 @@ struct CPUMIPSTLBContext { void (*helper_tlbwr)(struct CPUMIPSState *env); void (*helper_tlbp)(struct CPUMIPSState *env); void (*helper_tlbr)(struct CPUMIPSState *env); + void (*helper_tlbinv)(struct CPUMIPSState *env); + void (*helper_tlbinvf)(struct CPUMIPSState *env); union { struct { r4k_tlb_t tlb[MIPS_TLB_MAX]; @@ -51,12 +58,32 @@ struct CPUMIPSTLBContext { }; #endif +/* MSA Context */ +#define MSA_WRLEN (128) + +enum CPUMIPSMSADataFormat { + DF_BYTE = 0, + DF_HALF, + DF_WORD, + DF_DOUBLE +}; + +typedef union wr_t wr_t; +union wr_t { + int8_t b[MSA_WRLEN/8]; + int16_t h[MSA_WRLEN/16]; + int32_t w[MSA_WRLEN/32]; + int64_t d[MSA_WRLEN/64]; +}; + typedef union fpr_t fpr_t; union fpr_t { float64 fd; /* ieee double precision */ float32 fs[2];/* ieee single precision */ uint64_t d; /* binary double fixed-point */ uint32_t w[2]; /* binary single fixed-point */ +/* FPU/MSA register mapping is not tested on big-endian hosts. */ + wr_t wr; /* vector data */ }; /* define FP_ENDIAN_IDX to access the same location * in the fpr_t union regardless of the host endianness @@ -136,6 +163,7 @@ typedef struct mips_def_t mips_def_t; #define MIPS_TC_MAX 5 #define MIPS_FPU_MAX 1 #define MIPS_DSP_ACC 4 +#define MIPS_KSCRATCH_NUM 6 typedef struct TCState TCState; struct TCState { @@ -169,6 +197,21 @@ struct TCState { target_ulong CP0_TCScheFBack; int32_t CP0_Debug_tcstatus; target_ulong CP0_UserLocal; + + int32_t msacsr; + +#define MSACSR_FS 24 +#define MSACSR_FS_MASK (1 << MSACSR_FS) +#define MSACSR_NX 18 +#define MSACSR_NX_MASK (1 << MSACSR_NX) +#define MSACSR_CEF 2 +#define MSACSR_CEF_MASK (0xffff << MSACSR_CEF) +#define MSACSR_RM 0 +#define MSACSR_RM_MASK (0x3 << MSACSR_RM) +#define MSACSR_MASK (MSACSR_RM_MASK | MSACSR_CEF_MASK | MSACSR_NX_MASK | \ + MSACSR_FS_MASK) + + float_status msa_fp_status; }; typedef struct CPUMIPSState CPUMIPSState; @@ -184,6 +227,10 @@ struct CPUMIPSState { target_ulong SEGMask; target_ulong PAMask; + int32_t msair; +#define MSAIR_ProcID 8 +#define MSAIR_Rev 0 + int32_t CP0_Index; /* CP0_MVP* are per MVP registers. */ int32_t CP0_Random; @@ -228,9 +275,21 @@ struct CPUMIPSState { #define CP0VPEOpt_DWX0 0 target_ulong CP0_EntryLo0; target_ulong CP0_EntryLo1; +#if defined(TARGET_MIPS64) +# define CP0EnLo_RI 63 +# define CP0EnLo_XI 62 +#else +# define CP0EnLo_RI 31 +# define CP0EnLo_XI 30 +#endif target_ulong CP0_Context; + target_ulong CP0_KScratch[MIPS_KSCRATCH_NUM]; int32_t CP0_PageMask; + int32_t CP0_PageGrain_rw_bitmask; int32_t CP0_PageGrain; +#define CP0PG_RIE 31 +#define CP0PG_XIE 30 +#define CP0PG_IEC 27 int32_t CP0_Wired; int32_t CP0_SRSConf0_rw_bitmask; int32_t CP0_SRSConf0; @@ -263,8 +322,11 @@ struct CPUMIPSState { #define CP0SRSC4_SRS13 0 int32_t CP0_HWREna; target_ulong CP0_BadVAddr; + uint32_t CP0_BadInstr; + uint32_t CP0_BadInstrP; int32_t CP0_Count; target_ulong CP0_EntryHi; +#define CP0EnHi_EHINV 10 int32_t CP0_Compare; int32_t CP0_Status; #define CP0St_CU3 31 @@ -362,19 +424,38 @@ struct CPUMIPSState { #define CP0C2_SA 0 int32_t CP0_Config3; #define CP0C3_M 31 +#define CP0C3_BPG 30 +#define CP0C3_CMCGR 29 +#define CP0C3_MSAP 28 +#define CP0C3_BP 27 +#define CP0C3_BI 26 +#define CP0C3_IPLW 21 +#define CP0C3_MMAR 18 +#define CP0C3_MCU 17 #define CP0C3_ISA_ON_EXC 16 +#define CP0C3_ISA 14 #define CP0C3_ULRI 13 +#define CP0C3_RXI 12 +#define CP0C3_DSP2P 11 #define CP0C3_DSPP 10 #define CP0C3_LPA 7 #define CP0C3_VEIC 6 #define CP0C3_VInt 5 #define CP0C3_SP 4 +#define CP0C3_CDMM 3 #define CP0C3_MT 2 #define CP0C3_SM 1 #define CP0C3_TL 0 uint32_t CP0_Config4; uint32_t CP0_Config4_rw_bitmask; #define CP0C4_M 31 +#define CP0C4_IE 29 +#define CP0C4_KScrExist 16 +#define CP0C4_MMUExtDef 14 +#define CP0C4_FTLBPageSize 8 +#define CP0C4_FTLBWays 4 +#define CP0C4_FTLBSets 0 +#define CP0C4_MMUSizeExt 0 uint32_t CP0_Config5; uint32_t CP0_Config5_rw_bitmask; #define CP0C5_M 31 @@ -382,6 +463,7 @@ struct CPUMIPSState { #define CP0C5_CV 29 #define CP0C5_EVA 28 #define CP0C5_MSAEn 27 +#define CP0C5_SBRI 6 #define CP0C5_UFR 2 #define CP0C5_NFExists 0 int32_t CP0_Config6; @@ -429,9 +511,11 @@ struct CPUMIPSState { CPUMIPSFPUContext fpus[MIPS_FPU_MAX]; /* QEMU */ int error_code; +#define EXCP_TLB_NOMATCH 0x1 +#define EXCP_INST_NOTAVAIL 0x2 /* No valid instruction word for BadInstr */ uint32_t hflags; /* CPU State */ /* TMASK defines different execution modes */ -#define MIPS_HFLAG_TMASK 0x1807FF +#define MIPS_HFLAG_TMASK 0x15807FF #define MIPS_HFLAG_MODE 0x00007 /* execution modes */ /* The KSU flags must be the lowest bits in hflags. The flag order must be the same as defined for CP0 Status. This allows to use @@ -457,7 +541,7 @@ struct CPUMIPSState { * the delay slot, record what type of branch it is so that we can * resume translation properly. It might be possible to reduce * this from three bits to two. */ -#define MIPS_HFLAG_BMASK_BASE 0x03800 +#define MIPS_HFLAG_BMASK_BASE 0x803800 #define MIPS_HFLAG_B 0x00800 /* Unconditional branch */ #define MIPS_HFLAG_BC 0x01000 /* Conditional branch */ #define MIPS_HFLAG_BL 0x01800 /* Likely branch */ @@ -475,6 +559,9 @@ struct CPUMIPSState { #define MIPS_HFLAG_DSPR2 0x100000 /* Enable access to MIPS DSPR2 resources. */ /* Extra flag about HWREna register. */ #define MIPS_HFLAG_HWRENA_ULR 0x200000 /* ULR bit from HWREna is set. */ +#define MIPS_HFLAG_SBRI 0x400000 /* R6 SDBBP causes RI excpt. in user mode */ +#define MIPS_HFLAG_FBNSLOT 0x800000 /* Forbidden slot */ +#define MIPS_HFLAG_MSA 0x1000000 target_ulong btarget; /* Jump / branch target */ target_ulong bcond; /* Branch condition (if needed) */ @@ -510,6 +597,8 @@ void r4k_helper_tlbwi(CPUMIPSState *env); void r4k_helper_tlbwr(CPUMIPSState *env); void r4k_helper_tlbp(CPUMIPSState *env); void r4k_helper_tlbr(CPUMIPSState *env); +void r4k_helper_tlbinv(CPUMIPSState *env); +void r4k_helper_tlbinvf(CPUMIPSState *env); void mips_cpu_unassigned_access(CPUState *cpu, hwaddr addr, bool is_write, bool is_exec, int unused, @@ -526,7 +615,7 @@ void mips_cpu_list (FILE *f, fprintf_function cpu_fprintf); extern void cpu_wrdsp(uint32_t rs, uint32_t mask_num, CPUMIPSState *env); extern uint32_t cpu_rddsp(uint32_t mask_num, CPUMIPSState *env); -#define CPU_SAVE_VERSION 4 +#define CPU_SAVE_VERSION 5 /* MMU modes definitions. We carefully match the indices with our hflags layout. */ @@ -628,8 +717,12 @@ enum { EXCP_C2E, EXCP_CACHE, /* 32 */ EXCP_DSPDIS, + EXCP_MSADIS, + EXCP_MSAFPE, + EXCP_TLBXI, + EXCP_TLBRI, - EXCP_LAST = EXCP_DSPDIS, + EXCP_LAST = EXCP_TLBRI, }; /* Dummy exception for conditional stores. */ #define EXCP_SC 0x100 @@ -680,6 +773,10 @@ hwaddr cpu_mips_translate_address (CPUMIPSState *env, target_ulong address, #endif target_ulong exception_resume_pc (CPUMIPSState *env); +/* op_helper.c */ +extern unsigned int ieee_rm[]; +int ieee_ex_to_mips(int xcpt); + static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, target_ulong *pc, target_ulong *cs_base, int *flags) { @@ -726,7 +823,8 @@ static inline void compute_hflags(CPUMIPSState *env) { env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 | MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU | - MIPS_HFLAG_AWRAP | MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2); + MIPS_HFLAG_AWRAP | MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2 | + MIPS_HFLAG_SBRI | MIPS_HFLAG_MSA); if (!(env->CP0_Status & (1 << CP0St_EXL)) && !(env->CP0_Status & (1 << CP0St_ERL)) && !(env->hflags & MIPS_HFLAG_DM)) { @@ -752,7 +850,8 @@ static inline void compute_hflags(CPUMIPSState *env) } } #endif - if ((env->CP0_Status & (1 << CP0St_CU0)) || + if (((env->CP0_Status & (1 << CP0St_CU0)) && + !(env->insn_flags & ISA_MIPS32R6)) || !(env->hflags & MIPS_HFLAG_KSU)) { env->hflags |= MIPS_HFLAG_CP0; } @@ -762,6 +861,10 @@ static inline void compute_hflags(CPUMIPSState *env) if (env->CP0_Status & (1 << CP0St_FR)) { env->hflags |= MIPS_HFLAG_F64; } + if (((env->hflags & MIPS_HFLAG_KSU) != MIPS_HFLAG_KM) && + (env->CP0_Config5 & (1 << CP0C5_SBRI))) { + env->hflags |= MIPS_HFLAG_SBRI; + } if (env->insn_flags & ASE_DSPR2) { /* Enables access MIPS DSP resources, now our cpu is DSP ASER2, so enable to access DSPR2 resources. */ @@ -794,6 +897,11 @@ static inline void compute_hflags(CPUMIPSState *env) env->hflags |= MIPS_HFLAG_COP1X; } } + if (env->insn_flags & ASE_MSA) { + if (env->CP0_Config5 & (1 << CP0C5_MSAEn)) { + env->hflags |= MIPS_HFLAG_MSA; + } + } } #endif /* !defined (__MIPS_CPU_H__) */ diff --git a/target-mips/gdbstub.c b/target-mips/gdbstub.c index 5b72d58a44..f65fec23cc 100644 --- a/target-mips/gdbstub.c +++ b/target-mips/gdbstub.c @@ -73,13 +73,6 @@ int mips_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) return 0; } -/* convert MIPS rounding mode in FCR31 to IEEE library */ -static unsigned int ieee_rm[] = { - float_round_nearest_even, - float_round_to_zero, - float_round_up, - float_round_down -}; #define RESTORE_ROUNDING_MODE \ set_float_rounding_mode(ieee_rm[env->active_fpu.fcr31 & 3], \ &env->active_fpu.fp_status) diff --git a/target-mips/helper.c b/target-mips/helper.c index fe16820885..3a93c206e4 100644 --- a/target-mips/helper.c +++ b/target-mips/helper.c @@ -25,8 +25,11 @@ #include "cpu.h" #include "sysemu/kvm.h" +#include "exec/cpu_ldst.h" enum { + TLBRET_XI = -6, + TLBRET_RI = -5, TLBRET_DIRTY = -4, TLBRET_INVALID = -3, TLBRET_NOMATCH = -2, @@ -81,13 +84,20 @@ int r4k_map_address (CPUMIPSState *env, hwaddr *physical, int *prot, #endif /* Check ASID, virtual page number & size */ - if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) { + if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag && !tlb->EHINV) { /* TLB match */ int n = !!(address & mask & ~(mask >> 1)); /* Check access rights */ - if (!(n ? tlb->V1 : tlb->V0)) + if (!(n ? tlb->V1 : tlb->V0)) { return TLBRET_INVALID; - if (rw == 0 || (n ? tlb->D1 : tlb->D0)) { + } + if (rw == MMU_INST_FETCH && (n ? tlb->XI1 : tlb->XI0)) { + return TLBRET_XI; + } + if (rw == MMU_DATA_LOAD && (n ? tlb->RI1 : tlb->RI0)) { + return TLBRET_RI; + } + if (rw != MMU_DATA_STORE || (n ? tlb->D1 : tlb->D0)) { *physical = tlb->PFN[n] | (address & (mask >> 1)); *prot = PAGE_READ; if (n ? tlb->D1 : tlb->D0) @@ -232,36 +242,58 @@ static void raise_mmu_exception(CPUMIPSState *env, target_ulong address, CPUState *cs = CPU(mips_env_get_cpu(env)); int exception = 0, error_code = 0; + if (rw == MMU_INST_FETCH) { + error_code |= EXCP_INST_NOTAVAIL; + } + switch (tlb_error) { default: case TLBRET_BADADDR: /* Reference to kernel address from user mode or supervisor mode */ /* Reference to supervisor address from user mode */ - if (rw) + if (rw == MMU_DATA_STORE) { exception = EXCP_AdES; - else + } else { exception = EXCP_AdEL; + } break; case TLBRET_NOMATCH: /* No TLB match for a mapped address */ - if (rw) + if (rw == MMU_DATA_STORE) { exception = EXCP_TLBS; - else + } else { exception = EXCP_TLBL; - error_code = 1; + } + error_code |= EXCP_TLB_NOMATCH; break; case TLBRET_INVALID: /* TLB match with no valid bit */ - if (rw) + if (rw == MMU_DATA_STORE) { exception = EXCP_TLBS; - else + } else { exception = EXCP_TLBL; + } break; case TLBRET_DIRTY: /* TLB match but 'D' bit is cleared */ exception = EXCP_LTLBL; break; - + case TLBRET_XI: + /* Execute-Inhibit Exception */ + if (env->CP0_PageGrain & (1 << CP0PG_IEC)) { + exception = EXCP_TLBXI; + } else { + exception = EXCP_TLBL; + } + break; + case TLBRET_RI: + /* Read-Inhibit Exception */ + if (env->CP0_PageGrain & (1 << CP0PG_IEC)) { + exception = EXCP_TLBRI; + } else { + exception = EXCP_TLBL; + } + break; } /* Raise exception */ env->CP0_BadVAddr = address; @@ -312,8 +344,6 @@ int mips_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, qemu_log("%s pc " TARGET_FMT_lx " ad %" VADDR_PRIx " rw %d mmu_idx %d\n", __func__, env->active_tc.PC, address, rw, mmu_idx); - rw &= 1; - /* data access */ #if !defined(CONFIG_USER_ONLY) /* XXX: put correct access by using cpu_restore_state() @@ -347,8 +377,6 @@ hwaddr cpu_mips_translate_address(CPUMIPSState *env, target_ulong address, int r int access_type; int ret = 0; - rw &= 1; - /* data access */ access_type = ACCESS_INT; ret = get_physical_address(env, &physical, &prot, @@ -396,6 +424,10 @@ static const char * const excp_names[EXCP_LAST + 1] = { [EXCP_MDMX] = "MDMX", [EXCP_C2E] = "precise coprocessor 2", [EXCP_CACHE] = "cache error", + [EXCP_TLBXI] = "TLB execute-inhibit", + [EXCP_TLBRI] = "TLB read-inhibit", + [EXCP_MSADIS] = "MSA disabled", + [EXCP_MSAFPE] = "MSA floating point", }; target_ulong exception_resume_pc (CPUMIPSState *env) @@ -426,6 +458,21 @@ static void set_hflags_for_handler (CPUMIPSState *env) << MIPS_HFLAG_M16_SHIFT); } } + +static inline void set_badinstr_registers(CPUMIPSState *env) +{ + if (env->hflags & MIPS_HFLAG_M16) { + /* TODO: add BadInstr support for microMIPS */ + return; + } + if (env->CP0_Config3 & (1 << CP0C3_BI)) { + env->CP0_BadInstr = cpu_ldl_code(env, env->active_tc.PC); + } + if ((env->CP0_Config3 & (1 << CP0C3_BP)) && + (env->hflags & MIPS_HFLAG_BMASK)) { + env->CP0_BadInstrP = cpu_ldl_code(env, env->active_tc.PC - 4); + } +} #endif void mips_cpu_do_interrupt(CPUState *cs) @@ -433,6 +480,7 @@ void mips_cpu_do_interrupt(CPUState *cs) #if !defined(CONFIG_USER_ONLY) MIPSCPU *cpu = MIPS_CPU(cs); CPUMIPSState *env = &cpu->env; + bool update_badinstr = 0; target_ulong offset; int cause = -1; const char *name; @@ -541,10 +589,13 @@ void mips_cpu_do_interrupt(CPUState *cs) goto set_EPC; case EXCP_LTLBL: cause = 1; + update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); goto set_EPC; case EXCP_TLBL: cause = 2; - if (env->error_code == 1 && !(env->CP0_Status & (1 << CP0St_EXL))) { + update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); + if ((env->error_code & EXCP_TLB_NOMATCH) && + !(env->CP0_Status & (1 << CP0St_EXL))) { #if defined(TARGET_MIPS64) int R = env->CP0_BadVAddr >> 62; int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; @@ -561,7 +612,9 @@ void mips_cpu_do_interrupt(CPUState *cs) goto set_EPC; case EXCP_TLBS: cause = 3; - if (env->error_code == 1 && !(env->CP0_Status & (1 << CP0St_EXL))) { + update_badinstr = 1; + if ((env->error_code & EXCP_TLB_NOMATCH) && + !(env->CP0_Status & (1 << CP0St_EXL))) { #if defined(TARGET_MIPS64) int R = env->CP0_BadVAddr >> 62; int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; @@ -578,9 +631,11 @@ void mips_cpu_do_interrupt(CPUState *cs) goto set_EPC; case EXCP_AdEL: cause = 4; + update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); goto set_EPC; case EXCP_AdES: cause = 5; + update_badinstr = 1; goto set_EPC; case EXCP_IBE: cause = 6; @@ -590,30 +645,52 @@ void mips_cpu_do_interrupt(CPUState *cs) goto set_EPC; case EXCP_SYSCALL: cause = 8; + update_badinstr = 1; goto set_EPC; case EXCP_BREAK: cause = 9; + update_badinstr = 1; goto set_EPC; case EXCP_RI: cause = 10; + update_badinstr = 1; goto set_EPC; case EXCP_CpU: cause = 11; + update_badinstr = 1; env->CP0_Cause = (env->CP0_Cause & ~(0x3 << CP0Ca_CE)) | (env->error_code << CP0Ca_CE); goto set_EPC; case EXCP_OVERFLOW: cause = 12; + update_badinstr = 1; goto set_EPC; case EXCP_TRAP: cause = 13; + update_badinstr = 1; + goto set_EPC; + case EXCP_MSAFPE: + cause = 14; + update_badinstr = 1; goto set_EPC; case EXCP_FPE: cause = 15; + update_badinstr = 1; goto set_EPC; case EXCP_C2E: cause = 18; goto set_EPC; + case EXCP_TLBRI: + cause = 19; + update_badinstr = 1; + goto set_EPC; + case EXCP_TLBXI: + cause = 20; + goto set_EPC; + case EXCP_MSADIS: + cause = 21; + update_badinstr = 1; + goto set_EPC; case EXCP_MDMX: cause = 22; goto set_EPC; @@ -640,6 +717,9 @@ void mips_cpu_do_interrupt(CPUState *cs) set_EPC: if (!(env->CP0_Status & (1 << CP0St_EXL))) { env->CP0_EPC = exception_resume_pc(env); + if (update_badinstr) { + set_badinstr_registers(env); + } if (env->hflags & MIPS_HFLAG_BMASK) { env->CP0_Cause |= (1U << CP0Ca_BD); } else { diff --git a/target-mips/helper.h b/target-mips/helper.h index a127db55c2..9d0275891c 100644 --- a/target-mips/helper.h +++ b/target-mips/helper.h @@ -152,6 +152,11 @@ DEF_HELPER_2(mtc0_datalo, void, env, tl) DEF_HELPER_2(mtc0_taghi, void, env, tl) DEF_HELPER_2(mtc0_datahi, void, env, tl) +#if defined(TARGET_MIPS64) +DEF_HELPER_2(dmtc0_entrylo0, void, env, i64) +DEF_HELPER_2(dmtc0_entrylo1, void, env, i64) +#endif + /* MIPS MT functions */ DEF_HELPER_2(mftgpr, tl, env, i32) DEF_HELPER_2(mftlo, tl, env, i32) @@ -337,6 +342,8 @@ DEF_HELPER_1(tlbwi, void, env) DEF_HELPER_1(tlbwr, void, env) DEF_HELPER_1(tlbp, void, env) DEF_HELPER_1(tlbr, void, env) +DEF_HELPER_1(tlbinv, void, env) +DEF_HELPER_1(tlbinvf, void, env) DEF_HELPER_1(di, tl, env) DEF_HELPER_1(ei, tl, env) DEF_HELPER_1(eret, void, env) @@ -741,3 +748,187 @@ DEF_HELPER_FLAGS_3(dmthlip, 0, void, tl, tl, env) #endif DEF_HELPER_FLAGS_3(wrdsp, 0, void, tl, tl, env) DEF_HELPER_FLAGS_2(rddsp, 0, tl, tl, env) + +/* MIPS SIMD Architecture */ +DEF_HELPER_4(msa_andi_b, void, env, i32, i32, i32) +DEF_HELPER_4(msa_ori_b, void, env, i32, i32, i32) +DEF_HELPER_4(msa_nori_b, void, env, i32, i32, i32) +DEF_HELPER_4(msa_xori_b, void, env, i32, i32, i32) +DEF_HELPER_4(msa_bmnzi_b, void, env, i32, i32, i32) +DEF_HELPER_4(msa_bmzi_b, void, env, i32, i32, i32) +DEF_HELPER_4(msa_bseli_b, void, env, i32, i32, i32) +DEF_HELPER_5(msa_shf_df, void, env, i32, i32, i32, i32) + +DEF_HELPER_5(msa_addvi_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_subvi_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_maxi_s_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_maxi_u_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_mini_s_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_mini_u_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_ceqi_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_clti_s_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_clti_u_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_clei_s_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_clei_u_df, void, env, i32, i32, i32, s32) +DEF_HELPER_4(msa_ldi_df, void, env, i32, i32, s32) + +DEF_HELPER_5(msa_slli_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_srai_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_srli_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_bclri_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_bseti_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_bnegi_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_binsli_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_binsri_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_sat_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_sat_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_srari_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_srlri_df, void, env, i32, i32, i32, i32) + +DEF_HELPER_5(msa_sll_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_sra_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_srl_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_bclr_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_bset_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_bneg_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_binsl_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_binsr_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_addv_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_subv_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_max_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_max_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_min_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_min_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_max_a_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_min_a_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_ceq_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_clt_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_clt_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_cle_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_cle_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_add_a_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_adds_a_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_adds_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_adds_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_ave_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_ave_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_aver_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_aver_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_subs_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_subs_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_subsus_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_subsuu_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_asub_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_asub_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_mulv_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_maddv_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_msubv_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_div_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_div_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_mod_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_mod_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_dotp_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_dotp_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_dpadd_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_dpadd_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_dpsub_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_dpsub_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_sld_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_splat_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_pckev_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_pckod_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_ilvl_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_ilvr_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_ilvev_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_ilvod_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_vshf_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_srar_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_srlr_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_hadd_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_hadd_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_hsub_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_hsub_u_df, void, env, i32, i32, i32, i32) + +DEF_HELPER_5(msa_sldi_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_splati_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_copy_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_copy_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_insert_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_insve_df, void, env, i32, i32, i32, i32) +DEF_HELPER_3(msa_ctcmsa, void, env, tl, i32) +DEF_HELPER_2(msa_cfcmsa, tl, env, i32) +DEF_HELPER_3(msa_move_v, void, env, i32, i32) + +DEF_HELPER_5(msa_fcaf_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fcun_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fceq_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fcueq_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fclt_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fcult_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fcle_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fcule_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsaf_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsun_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fseq_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsueq_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fslt_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsult_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsle_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsule_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fadd_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsub_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fmul_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fdiv_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fmadd_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fmsub_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fexp2_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fexdo_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_ftq_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fmin_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fmin_a_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fmax_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fmax_a_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fcor_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fcune_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fcne_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_mul_q_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_madd_q_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_msub_q_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsor_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsune_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsne_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_mulr_q_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_maddr_q_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_msubr_q_df, void, env, i32, i32, i32, i32) + +DEF_HELPER_4(msa_and_v, void, env, i32, i32, i32) +DEF_HELPER_4(msa_or_v, void, env, i32, i32, i32) +DEF_HELPER_4(msa_nor_v, void, env, i32, i32, i32) +DEF_HELPER_4(msa_xor_v, void, env, i32, i32, i32) +DEF_HELPER_4(msa_bmnz_v, void, env, i32, i32, i32) +DEF_HELPER_4(msa_bmz_v, void, env, i32, i32, i32) +DEF_HELPER_4(msa_bsel_v, void, env, i32, i32, i32) +DEF_HELPER_4(msa_fill_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_pcnt_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_nloc_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_nlzc_df, void, env, i32, i32, i32) + +DEF_HELPER_4(msa_fclass_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_ftrunc_s_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_ftrunc_u_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_fsqrt_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_frsqrt_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_frcp_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_frint_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_flog2_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_fexupl_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_fexupr_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_ffql_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_ffqr_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_ftint_s_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_ftint_u_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_ffint_s_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_ffint_u_df, void, env, i32, i32, i32) + +DEF_HELPER_5(msa_ld_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_st_df, void, env, i32, i32, i32, s32) diff --git a/target-mips/machine.c b/target-mips/machine.c index 0496faa910..0ba7d736db 100644 --- a/target-mips/machine.c +++ b/target-mips/machine.c @@ -61,7 +61,12 @@ void cpu_save(QEMUFile *f, void *opaque) qemu_put_be32s(f, &env->tlb->nb_tlb); qemu_put_be32s(f, &env->tlb->tlb_in_use); for(i = 0; i < MIPS_TLB_MAX; i++) { - uint16_t flags = ((env->tlb->mmu.r4k.tlb[i].G << 10) | + uint16_t flags = ((env->tlb->mmu.r4k.tlb[i].EHINV << 15) | + (env->tlb->mmu.r4k.tlb[i].RI1 << 14) | + (env->tlb->mmu.r4k.tlb[i].RI0 << 13) | + (env->tlb->mmu.r4k.tlb[i].XI1 << 12) | + (env->tlb->mmu.r4k.tlb[i].XI0 << 11) | + (env->tlb->mmu.r4k.tlb[i].G << 10) | (env->tlb->mmu.r4k.tlb[i].C0 << 7) | (env->tlb->mmu.r4k.tlb[i].C1 << 4) | (env->tlb->mmu.r4k.tlb[i].V0 << 3) | @@ -111,6 +116,8 @@ void cpu_save(QEMUFile *f, void *opaque) qemu_put_sbe32s(f, &env->CP0_SRSConf4); qemu_put_sbe32s(f, &env->CP0_HWREna); qemu_put_betls(f, &env->CP0_BadVAddr); + qemu_put_be32s(f, &env->CP0_BadInstr); + qemu_put_be32s(f, &env->CP0_BadInstrP); qemu_put_sbe32s(f, &env->CP0_Count); qemu_put_betls(f, &env->CP0_EntryHi); qemu_put_sbe32s(f, &env->CP0_Compare); @@ -144,6 +151,9 @@ void cpu_save(QEMUFile *f, void *opaque) qemu_put_sbe32s(f, &env->CP0_DataHi); qemu_put_betls(f, &env->CP0_ErrorEPC); qemu_put_sbe32s(f, &env->CP0_DESAVE); + for (i = 0; i < MIPS_KSCRATCH_NUM; i++) { + qemu_put_betls(f, &env->CP0_KScratch[i]); + } /* Save inactive TC state */ for (i = 0; i < MIPS_SHADOW_SET_MAX; i++) @@ -232,6 +242,13 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id) env->tlb->mmu.r4k.tlb[i].V1 = (flags >> 2) & 1; env->tlb->mmu.r4k.tlb[i].D0 = (flags >> 1) & 1; env->tlb->mmu.r4k.tlb[i].D1 = (flags >> 0) & 1; + if (version_id >= 5) { + env->tlb->mmu.r4k.tlb[i].EHINV = (flags >> 15) & 1; + env->tlb->mmu.r4k.tlb[i].RI1 = (flags >> 14) & 1; + env->tlb->mmu.r4k.tlb[i].RI0 = (flags >> 13) & 1; + env->tlb->mmu.r4k.tlb[i].XI1 = (flags >> 12) & 1; + env->tlb->mmu.r4k.tlb[i].XI0 = (flags >> 11) & 1; + } qemu_get_betls(f, &env->tlb->mmu.r4k.tlb[i].PFN[0]); qemu_get_betls(f, &env->tlb->mmu.r4k.tlb[i].PFN[1]); } @@ -301,6 +318,13 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id) qemu_get_sbe32s(f, &env->CP0_DataHi); qemu_get_betls(f, &env->CP0_ErrorEPC); qemu_get_sbe32s(f, &env->CP0_DESAVE); + if (version_id >= 5) { + qemu_get_be32s(f, &env->CP0_BadInstr); + qemu_get_be32s(f, &env->CP0_BadInstrP); + for (i = 0; i < MIPS_KSCRATCH_NUM; i++) { + qemu_get_betls(f, &env->CP0_KScratch[i]); + } + } /* Load inactive TC state */ for (i = 0; i < MIPS_SHADOW_SET_MAX; i++) { diff --git a/target-mips/mips-defs.h b/target-mips/mips-defs.h index 6cb62b2df0..1784227494 100644 --- a/target-mips/mips-defs.h +++ b/target-mips/mips-defs.h @@ -45,6 +45,7 @@ #define ASE_MT 0x00200000 #define ASE_SMARTMIPS 0x00400000 #define ASE_MICROMIPS 0x00800000 +#define ASE_MSA 0x01000000 /* Chip specific instructions. */ #define INSN_LOONGSON2E 0x20000000 diff --git a/target-mips/msa_helper.c b/target-mips/msa_helper.c new file mode 100644 index 0000000000..b08f37f787 --- /dev/null +++ b/target-mips/msa_helper.c @@ -0,0 +1,3436 @@ +/* + * MIPS SIMD Architecture Module Instruction emulation helpers for QEMU. + * + * Copyright (c) 2014 Imagination Technologies + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "exec/helper-proto.h" + +/* Data format min and max values */ +#define DF_BITS(df) (1 << ((df) + 3)) + +#define DF_MAX_INT(df) (int64_t)((1LL << (DF_BITS(df) - 1)) - 1) +#define M_MAX_INT(m) (int64_t)((1LL << ((m) - 1)) - 1) + +#define DF_MIN_INT(df) (int64_t)(-(1LL << (DF_BITS(df) - 1))) +#define M_MIN_INT(m) (int64_t)(-(1LL << ((m) - 1))) + +#define DF_MAX_UINT(df) (uint64_t)(-1ULL >> (64 - DF_BITS(df))) +#define M_MAX_UINT(m) (uint64_t)(-1ULL >> (64 - (m))) + +#define UNSIGNED(x, df) ((x) & DF_MAX_UINT(df)) +#define SIGNED(x, df) \ + ((((int64_t)x) << (64 - DF_BITS(df))) >> (64 - DF_BITS(df))) + +/* Element-by-element access macros */ +#define DF_ELEMENTS(df) (MSA_WRLEN / DF_BITS(df)) + +static inline void msa_move_v(wr_t *pwd, wr_t *pws) +{ + uint32_t i; + + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + pwd->d[i] = pws->d[i]; + } +} + +#define MSA_FN_IMM8(FUNC, DEST, OPERATION) \ +void helper_msa_ ## FUNC(CPUMIPSState *env, uint32_t wd, uint32_t ws, \ + uint32_t i8) \ +{ \ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); \ + wr_t *pws = &(env->active_fpu.fpr[ws].wr); \ + uint32_t i; \ + for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) { \ + DEST = OPERATION; \ + } \ +} + +MSA_FN_IMM8(andi_b, pwd->b[i], pws->b[i] & i8) +MSA_FN_IMM8(ori_b, pwd->b[i], pws->b[i] | i8) +MSA_FN_IMM8(nori_b, pwd->b[i], ~(pws->b[i] | i8)) +MSA_FN_IMM8(xori_b, pwd->b[i], pws->b[i] ^ i8) + +#define BIT_MOVE_IF_NOT_ZERO(dest, arg1, arg2, df) \ + UNSIGNED(((dest & (~arg2)) | (arg1 & arg2)), df) +MSA_FN_IMM8(bmnzi_b, pwd->b[i], + BIT_MOVE_IF_NOT_ZERO(pwd->b[i], pws->b[i], i8, DF_BYTE)) + +#define BIT_MOVE_IF_ZERO(dest, arg1, arg2, df) \ + UNSIGNED((dest & arg2) | (arg1 & (~arg2)), df) +MSA_FN_IMM8(bmzi_b, pwd->b[i], + BIT_MOVE_IF_ZERO(pwd->b[i], pws->b[i], i8, DF_BYTE)) + +#define BIT_SELECT(dest, arg1, arg2, df) \ + UNSIGNED((arg1 & (~dest)) | (arg2 & dest), df) +MSA_FN_IMM8(bseli_b, pwd->b[i], + BIT_SELECT(pwd->b[i], pws->b[i], i8, DF_BYTE)) + +#undef MSA_FN_IMM8 + +#define SHF_POS(i, imm) (((i) & 0xfc) + (((imm) >> (2 * ((i) & 0x03))) & 0x03)) + +void helper_msa_shf_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t imm) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t wx, *pwx = &wx; + uint32_t i; + + switch (df) { + case DF_BYTE: + for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) { + pwx->b[i] = pws->b[SHF_POS(i, imm)]; + } + break; + case DF_HALF: + for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) { + pwx->h[i] = pws->h[SHF_POS(i, imm)]; + } + break; + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + pwx->w[i] = pws->w[SHF_POS(i, imm)]; + } + break; + default: + assert(0); + } + msa_move_v(pwd, pwx); +} + +#define MSA_FN_VECTOR(FUNC, DEST, OPERATION) \ +void helper_msa_ ## FUNC(CPUMIPSState *env, uint32_t wd, uint32_t ws, \ + uint32_t wt) \ +{ \ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); \ + wr_t *pws = &(env->active_fpu.fpr[ws].wr); \ + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); \ + uint32_t i; \ + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { \ + DEST = OPERATION; \ + } \ +} + +MSA_FN_VECTOR(and_v, pwd->d[i], pws->d[i] & pwt->d[i]) +MSA_FN_VECTOR(or_v, pwd->d[i], pws->d[i] | pwt->d[i]) +MSA_FN_VECTOR(nor_v, pwd->d[i], ~(pws->d[i] | pwt->d[i])) +MSA_FN_VECTOR(xor_v, pwd->d[i], pws->d[i] ^ pwt->d[i]) +MSA_FN_VECTOR(bmnz_v, pwd->d[i], + BIT_MOVE_IF_NOT_ZERO(pwd->d[i], pws->d[i], pwt->d[i], DF_DOUBLE)) +MSA_FN_VECTOR(bmz_v, pwd->d[i], + BIT_MOVE_IF_ZERO(pwd->d[i], pws->d[i], pwt->d[i], DF_DOUBLE)) +MSA_FN_VECTOR(bsel_v, pwd->d[i], + BIT_SELECT(pwd->d[i], pws->d[i], pwt->d[i], DF_DOUBLE)) +#undef BIT_MOVE_IF_NOT_ZERO +#undef BIT_MOVE_IF_ZERO +#undef BIT_SELECT +#undef MSA_FN_VECTOR + +static inline int64_t msa_addv_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + return arg1 + arg2; +} + +static inline int64_t msa_subv_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + return arg1 - arg2; +} + +static inline int64_t msa_ceq_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + return arg1 == arg2 ? -1 : 0; +} + +static inline int64_t msa_cle_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + return arg1 <= arg2 ? -1 : 0; +} + +static inline int64_t msa_cle_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + uint64_t u_arg1 = UNSIGNED(arg1, df); + uint64_t u_arg2 = UNSIGNED(arg2, df); + return u_arg1 <= u_arg2 ? -1 : 0; +} + +static inline int64_t msa_clt_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + return arg1 < arg2 ? -1 : 0; +} + +static inline int64_t msa_clt_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + uint64_t u_arg1 = UNSIGNED(arg1, df); + uint64_t u_arg2 = UNSIGNED(arg2, df); + return u_arg1 < u_arg2 ? -1 : 0; +} + +static inline int64_t msa_max_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + return arg1 > arg2 ? arg1 : arg2; +} + +static inline int64_t msa_max_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + uint64_t u_arg1 = UNSIGNED(arg1, df); + uint64_t u_arg2 = UNSIGNED(arg2, df); + return u_arg1 > u_arg2 ? arg1 : arg2; +} + +static inline int64_t msa_min_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + return arg1 < arg2 ? arg1 : arg2; +} + +static inline int64_t msa_min_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + uint64_t u_arg1 = UNSIGNED(arg1, df); + uint64_t u_arg2 = UNSIGNED(arg2, df); + return u_arg1 < u_arg2 ? arg1 : arg2; +} + +#define MSA_BINOP_IMM_DF(helper, func) \ +void helper_msa_ ## helper ## _df(CPUMIPSState *env, uint32_t df, \ + uint32_t wd, uint32_t ws, int32_t u5) \ +{ \ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); \ + wr_t *pws = &(env->active_fpu.fpr[ws].wr); \ + uint32_t i; \ + \ + switch (df) { \ + case DF_BYTE: \ + for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) { \ + pwd->b[i] = msa_ ## func ## _df(df, pws->b[i], u5); \ + } \ + break; \ + case DF_HALF: \ + for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) { \ + pwd->h[i] = msa_ ## func ## _df(df, pws->h[i], u5); \ + } \ + break; \ + case DF_WORD: \ + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { \ + pwd->w[i] = msa_ ## func ## _df(df, pws->w[i], u5); \ + } \ + break; \ + case DF_DOUBLE: \ + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { \ + pwd->d[i] = msa_ ## func ## _df(df, pws->d[i], u5); \ + } \ + break; \ + default: \ + assert(0); \ + } \ +} + +MSA_BINOP_IMM_DF(addvi, addv) +MSA_BINOP_IMM_DF(subvi, subv) +MSA_BINOP_IMM_DF(ceqi, ceq) +MSA_BINOP_IMM_DF(clei_s, cle_s) +MSA_BINOP_IMM_DF(clei_u, cle_u) +MSA_BINOP_IMM_DF(clti_s, clt_s) +MSA_BINOP_IMM_DF(clti_u, clt_u) +MSA_BINOP_IMM_DF(maxi_s, max_s) +MSA_BINOP_IMM_DF(maxi_u, max_u) +MSA_BINOP_IMM_DF(mini_s, min_s) +MSA_BINOP_IMM_DF(mini_u, min_u) +#undef MSA_BINOP_IMM_DF + +void helper_msa_ldi_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + int32_t s10) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + uint32_t i; + + switch (df) { + case DF_BYTE: + for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) { + pwd->b[i] = (int8_t)s10; + } + break; + case DF_HALF: + for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) { + pwd->h[i] = (int16_t)s10; + } + break; + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + pwd->w[i] = (int32_t)s10; + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + pwd->d[i] = (int64_t)s10; + } + break; + default: + assert(0); + } +} + +/* Data format bit position and unsigned values */ +#define BIT_POSITION(x, df) ((uint64_t)(x) % DF_BITS(df)) + +static inline int64_t msa_sll_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + int32_t b_arg2 = BIT_POSITION(arg2, df); + return arg1 << b_arg2; +} + +static inline int64_t msa_sra_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + int32_t b_arg2 = BIT_POSITION(arg2, df); + return arg1 >> b_arg2; +} + +static inline int64_t msa_srl_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + uint64_t u_arg1 = UNSIGNED(arg1, df); + int32_t b_arg2 = BIT_POSITION(arg2, df); + return u_arg1 >> b_arg2; +} + +static inline int64_t msa_bclr_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + int32_t b_arg2 = BIT_POSITION(arg2, df); + return UNSIGNED(arg1 & (~(1LL << b_arg2)), df); +} + +static inline int64_t msa_bset_df(uint32_t df, int64_t arg1, + int64_t arg2) +{ + int32_t b_arg2 = BIT_POSITION(arg2, df); + return UNSIGNED(arg1 | (1LL << b_arg2), df); +} + +static inline int64_t msa_bneg_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + int32_t b_arg2 = BIT_POSITION(arg2, df); + return UNSIGNED(arg1 ^ (1LL << b_arg2), df); +} + +static inline int64_t msa_binsl_df(uint32_t df, int64_t dest, int64_t arg1, + int64_t arg2) +{ + uint64_t u_arg1 = UNSIGNED(arg1, df); + uint64_t u_dest = UNSIGNED(dest, df); + int32_t sh_d = BIT_POSITION(arg2, df) + 1; + int32_t sh_a = DF_BITS(df) - sh_d; + if (sh_d == DF_BITS(df)) { + return u_arg1; + } else { + return UNSIGNED(UNSIGNED(u_dest << sh_d, df) >> sh_d, df) | + UNSIGNED(UNSIGNED(u_arg1 >> sh_a, df) << sh_a, df); + } +} + +static inline int64_t msa_binsr_df(uint32_t df, int64_t dest, int64_t arg1, + int64_t arg2) +{ + uint64_t u_arg1 = UNSIGNED(arg1, df); + uint64_t u_dest = UNSIGNED(dest, df); + int32_t sh_d = BIT_POSITION(arg2, df) + 1; + int32_t sh_a = DF_BITS(df) - sh_d; + if (sh_d == DF_BITS(df)) { + return u_arg1; + } else { + return UNSIGNED(UNSIGNED(u_dest >> sh_d, df) << sh_d, df) | + UNSIGNED(UNSIGNED(u_arg1 << sh_a, df) >> sh_a, df); + } +} + +static inline int64_t msa_sat_s_df(uint32_t df, int64_t arg, uint32_t m) +{ + return arg < M_MIN_INT(m+1) ? M_MIN_INT(m+1) : + arg > M_MAX_INT(m+1) ? M_MAX_INT(m+1) : + arg; +} + +static inline int64_t msa_sat_u_df(uint32_t df, int64_t arg, uint32_t m) +{ + uint64_t u_arg = UNSIGNED(arg, df); + return u_arg < M_MAX_UINT(m+1) ? u_arg : + M_MAX_UINT(m+1); +} + +static inline int64_t msa_srar_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + int32_t b_arg2 = BIT_POSITION(arg2, df); + if (b_arg2 == 0) { + return arg1; + } else { + int64_t r_bit = (arg1 >> (b_arg2 - 1)) & 1; + return (arg1 >> b_arg2) + r_bit; + } +} + +static inline int64_t msa_srlr_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + uint64_t u_arg1 = UNSIGNED(arg1, df); + int32_t b_arg2 = BIT_POSITION(arg2, df); + if (b_arg2 == 0) { + return u_arg1; + } else { + uint64_t r_bit = (u_arg1 >> (b_arg2 - 1)) & 1; + return (u_arg1 >> b_arg2) + r_bit; + } +} + +#define MSA_BINOP_IMMU_DF(helper, func) \ +void helper_msa_ ## helper ## _df(CPUMIPSState *env, uint32_t df, uint32_t wd, \ + uint32_t ws, uint32_t u5) \ +{ \ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); \ + wr_t *pws = &(env->active_fpu.fpr[ws].wr); \ + uint32_t i; \ + \ + switch (df) { \ + case DF_BYTE: \ + for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) { \ + pwd->b[i] = msa_ ## func ## _df(df, pws->b[i], u5); \ + } \ + break; \ + case DF_HALF: \ + for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) { \ + pwd->h[i] = msa_ ## func ## _df(df, pws->h[i], u5); \ + } \ + break; \ + case DF_WORD: \ + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { \ + pwd->w[i] = msa_ ## func ## _df(df, pws->w[i], u5); \ + } \ + break; \ + case DF_DOUBLE: \ + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { \ + pwd->d[i] = msa_ ## func ## _df(df, pws->d[i], u5); \ + } \ + break; \ + default: \ + assert(0); \ + } \ +} + +MSA_BINOP_IMMU_DF(slli, sll) +MSA_BINOP_IMMU_DF(srai, sra) +MSA_BINOP_IMMU_DF(srli, srl) +MSA_BINOP_IMMU_DF(bclri, bclr) +MSA_BINOP_IMMU_DF(bseti, bset) +MSA_BINOP_IMMU_DF(bnegi, bneg) +MSA_BINOP_IMMU_DF(sat_s, sat_s) +MSA_BINOP_IMMU_DF(sat_u, sat_u) +MSA_BINOP_IMMU_DF(srari, srar) +MSA_BINOP_IMMU_DF(srlri, srlr) +#undef MSA_BINOP_IMMU_DF + +#define MSA_TEROP_IMMU_DF(helper, func) \ +void helper_msa_ ## helper ## _df(CPUMIPSState *env, uint32_t df, \ + uint32_t wd, uint32_t ws, uint32_t u5) \ +{ \ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); \ + wr_t *pws = &(env->active_fpu.fpr[ws].wr); \ + uint32_t i; \ + \ + switch (df) { \ + case DF_BYTE: \ + for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) { \ + pwd->b[i] = msa_ ## func ## _df(df, pwd->b[i], pws->b[i], \ + u5); \ + } \ + break; \ + case DF_HALF: \ + for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) { \ + pwd->h[i] = msa_ ## func ## _df(df, pwd->h[i], pws->h[i], \ + u5); \ + } \ + break; \ + case DF_WORD: \ + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { \ + pwd->w[i] = msa_ ## func ## _df(df, pwd->w[i], pws->w[i], \ + u5); \ + } \ + break; \ + case DF_DOUBLE: \ + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { \ + pwd->d[i] = msa_ ## func ## _df(df, pwd->d[i], pws->d[i], \ + u5); \ + } \ + break; \ + default: \ + assert(0); \ + } \ +} + +MSA_TEROP_IMMU_DF(binsli, binsl) +MSA_TEROP_IMMU_DF(binsri, binsr) +#undef MSA_TEROP_IMMU_DF + +static inline int64_t msa_max_a_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + uint64_t abs_arg1 = arg1 >= 0 ? arg1 : -arg1; + uint64_t abs_arg2 = arg2 >= 0 ? arg2 : -arg2; + return abs_arg1 > abs_arg2 ? arg1 : arg2; +} + +static inline int64_t msa_min_a_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + uint64_t abs_arg1 = arg1 >= 0 ? arg1 : -arg1; + uint64_t abs_arg2 = arg2 >= 0 ? arg2 : -arg2; + return abs_arg1 < abs_arg2 ? arg1 : arg2; +} + +static inline int64_t msa_add_a_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + uint64_t abs_arg1 = arg1 >= 0 ? arg1 : -arg1; + uint64_t abs_arg2 = arg2 >= 0 ? arg2 : -arg2; + return abs_arg1 + abs_arg2; +} + +static inline int64_t msa_adds_a_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + uint64_t max_int = (uint64_t)DF_MAX_INT(df); + uint64_t abs_arg1 = arg1 >= 0 ? arg1 : -arg1; + uint64_t abs_arg2 = arg2 >= 0 ? arg2 : -arg2; + if (abs_arg1 > max_int || abs_arg2 > max_int) { + return (int64_t)max_int; + } else { + return (abs_arg1 < max_int - abs_arg2) ? abs_arg1 + abs_arg2 : max_int; + } +} + +static inline int64_t msa_adds_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + int64_t max_int = DF_MAX_INT(df); + int64_t min_int = DF_MIN_INT(df); + if (arg1 < 0) { + return (min_int - arg1 < arg2) ? arg1 + arg2 : min_int; + } else { + return (arg2 < max_int - arg1) ? arg1 + arg2 : max_int; + } +} + +static inline uint64_t msa_adds_u_df(uint32_t df, uint64_t arg1, uint64_t arg2) +{ + uint64_t max_uint = DF_MAX_UINT(df); + uint64_t u_arg1 = UNSIGNED(arg1, df); + uint64_t u_arg2 = UNSIGNED(arg2, df); + return (u_arg1 < max_uint - u_arg2) ? u_arg1 + u_arg2 : max_uint; +} + +static inline int64_t msa_ave_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + /* signed shift */ + return (arg1 >> 1) + (arg2 >> 1) + (arg1 & arg2 & 1); +} + +static inline uint64_t msa_ave_u_df(uint32_t df, uint64_t arg1, uint64_t arg2) +{ + uint64_t u_arg1 = UNSIGNED(arg1, df); + uint64_t u_arg2 = UNSIGNED(arg2, df); + /* unsigned shift */ + return (u_arg1 >> 1) + (u_arg2 >> 1) + (u_arg1 & u_arg2 & 1); +} + +static inline int64_t msa_aver_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + /* signed shift */ + return (arg1 >> 1) + (arg2 >> 1) + ((arg1 | arg2) & 1); +} + +static inline uint64_t msa_aver_u_df(uint32_t df, uint64_t arg1, uint64_t arg2) +{ + uint64_t u_arg1 = UNSIGNED(arg1, df); + uint64_t u_arg2 = UNSIGNED(arg2, df); + /* unsigned shift */ + return (u_arg1 >> 1) + (u_arg2 >> 1) + ((u_arg1 | u_arg2) & 1); +} + +static inline int64_t msa_subs_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + int64_t max_int = DF_MAX_INT(df); + int64_t min_int = DF_MIN_INT(df); + if (arg2 > 0) { + return (min_int + arg2 < arg1) ? arg1 - arg2 : min_int; + } else { + return (arg1 < max_int + arg2) ? arg1 - arg2 : max_int; + } +} + +static inline int64_t msa_subs_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + uint64_t u_arg1 = UNSIGNED(arg1, df); + uint64_t u_arg2 = UNSIGNED(arg2, df); + return (u_arg1 > u_arg2) ? u_arg1 - u_arg2 : 0; +} + +static inline int64_t msa_subsus_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + uint64_t u_arg1 = UNSIGNED(arg1, df); + uint64_t max_uint = DF_MAX_UINT(df); + if (arg2 >= 0) { + uint64_t u_arg2 = (uint64_t)arg2; + return (u_arg1 > u_arg2) ? + (int64_t)(u_arg1 - u_arg2) : + 0; + } else { + uint64_t u_arg2 = (uint64_t)(-arg2); + return (u_arg1 < max_uint - u_arg2) ? + (int64_t)(u_arg1 + u_arg2) : + (int64_t)max_uint; + } +} + +static inline int64_t msa_subsuu_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + uint64_t u_arg1 = UNSIGNED(arg1, df); + uint64_t u_arg2 = UNSIGNED(arg2, df); + int64_t max_int = DF_MAX_INT(df); + int64_t min_int = DF_MIN_INT(df); + if (u_arg1 > u_arg2) { + return u_arg1 - u_arg2 < (uint64_t)max_int ? + (int64_t)(u_arg1 - u_arg2) : + max_int; + } else { + return u_arg2 - u_arg1 < (uint64_t)(-min_int) ? + (int64_t)(u_arg1 - u_arg2) : + min_int; + } +} + +static inline int64_t msa_asub_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + /* signed compare */ + return (arg1 < arg2) ? + (uint64_t)(arg2 - arg1) : (uint64_t)(arg1 - arg2); +} + +static inline uint64_t msa_asub_u_df(uint32_t df, uint64_t arg1, uint64_t arg2) +{ + uint64_t u_arg1 = UNSIGNED(arg1, df); + uint64_t u_arg2 = UNSIGNED(arg2, df); + /* unsigned compare */ + return (u_arg1 < u_arg2) ? + (uint64_t)(u_arg2 - u_arg1) : (uint64_t)(u_arg1 - u_arg2); +} + +static inline int64_t msa_mulv_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + return arg1 * arg2; +} + +static inline int64_t msa_div_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + if (arg1 == DF_MIN_INT(df) && arg2 == -1) { + return DF_MIN_INT(df); + } + return arg2 ? arg1 / arg2 : 0; +} + +static inline int64_t msa_div_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + uint64_t u_arg1 = UNSIGNED(arg1, df); + uint64_t u_arg2 = UNSIGNED(arg2, df); + return u_arg2 ? u_arg1 / u_arg2 : 0; +} + +static inline int64_t msa_mod_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + if (arg1 == DF_MIN_INT(df) && arg2 == -1) { + return 0; + } + return arg2 ? arg1 % arg2 : 0; +} + +static inline int64_t msa_mod_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + uint64_t u_arg1 = UNSIGNED(arg1, df); + uint64_t u_arg2 = UNSIGNED(arg2, df); + return u_arg2 ? u_arg1 % u_arg2 : 0; +} + +#define SIGNED_EVEN(a, df) \ + ((((int64_t)(a)) << (64 - DF_BITS(df)/2)) >> (64 - DF_BITS(df)/2)) + +#define UNSIGNED_EVEN(a, df) \ + ((((uint64_t)(a)) << (64 - DF_BITS(df)/2)) >> (64 - DF_BITS(df)/2)) + +#define SIGNED_ODD(a, df) \ + ((((int64_t)(a)) << (64 - DF_BITS(df))) >> (64 - DF_BITS(df)/2)) + +#define UNSIGNED_ODD(a, df) \ + ((((uint64_t)(a)) << (64 - DF_BITS(df))) >> (64 - DF_BITS(df)/2)) + +#define SIGNED_EXTRACT(e, o, a, df) \ + do { \ + e = SIGNED_EVEN(a, df); \ + o = SIGNED_ODD(a, df); \ + } while (0); + +#define UNSIGNED_EXTRACT(e, o, a, df) \ + do { \ + e = UNSIGNED_EVEN(a, df); \ + o = UNSIGNED_ODD(a, df); \ + } while (0); + +static inline int64_t msa_dotp_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + int64_t even_arg1; + int64_t even_arg2; + int64_t odd_arg1; + int64_t odd_arg2; + SIGNED_EXTRACT(even_arg1, odd_arg1, arg1, df); + SIGNED_EXTRACT(even_arg2, odd_arg2, arg2, df); + return (even_arg1 * even_arg2) + (odd_arg1 * odd_arg2); +} + +static inline int64_t msa_dotp_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + int64_t even_arg1; + int64_t even_arg2; + int64_t odd_arg1; + int64_t odd_arg2; + UNSIGNED_EXTRACT(even_arg1, odd_arg1, arg1, df); + UNSIGNED_EXTRACT(even_arg2, odd_arg2, arg2, df); + return (even_arg1 * even_arg2) + (odd_arg1 * odd_arg2); +} + +#define CONCATENATE_AND_SLIDE(s, k) \ + do { \ + for (i = 0; i < s; i++) { \ + v[i] = pws->b[s * k + i]; \ + v[i + s] = pwd->b[s * k + i]; \ + } \ + for (i = 0; i < s; i++) { \ + pwd->b[s * k + i] = v[i + n]; \ + } \ + } while (0) + +static inline void msa_sld_df(uint32_t df, wr_t *pwd, + wr_t *pws, target_ulong rt) +{ + uint32_t n = rt % DF_ELEMENTS(df); + uint8_t v[64]; + uint32_t i, k; + + switch (df) { + case DF_BYTE: + CONCATENATE_AND_SLIDE(DF_ELEMENTS(DF_BYTE), 0); + break; + case DF_HALF: + for (k = 0; k < 2; k++) { + CONCATENATE_AND_SLIDE(DF_ELEMENTS(DF_HALF), k); + } + break; + case DF_WORD: + for (k = 0; k < 4; k++) { + CONCATENATE_AND_SLIDE(DF_ELEMENTS(DF_WORD), k); + } + break; + case DF_DOUBLE: + for (k = 0; k < 8; k++) { + CONCATENATE_AND_SLIDE(DF_ELEMENTS(DF_DOUBLE), k); + } + break; + default: + assert(0); + } +} + +static inline int64_t msa_hadd_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + return SIGNED_ODD(arg1, df) + SIGNED_EVEN(arg2, df); +} + +static inline int64_t msa_hadd_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + return UNSIGNED_ODD(arg1, df) + UNSIGNED_EVEN(arg2, df); +} + +static inline int64_t msa_hsub_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + return SIGNED_ODD(arg1, df) - SIGNED_EVEN(arg2, df); +} + +static inline int64_t msa_hsub_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + return UNSIGNED_ODD(arg1, df) - UNSIGNED_EVEN(arg2, df); +} + +static inline int64_t msa_mul_q_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + int64_t q_min = DF_MIN_INT(df); + int64_t q_max = DF_MAX_INT(df); + + if (arg1 == q_min && arg2 == q_min) { + return q_max; + } + return (arg1 * arg2) >> (DF_BITS(df) - 1); +} + +static inline int64_t msa_mulr_q_df(uint32_t df, int64_t arg1, int64_t arg2) +{ + int64_t q_min = DF_MIN_INT(df); + int64_t q_max = DF_MAX_INT(df); + int64_t r_bit = 1 << (DF_BITS(df) - 2); + + if (arg1 == q_min && arg2 == q_min) { + return q_max; + } + return (arg1 * arg2 + r_bit) >> (DF_BITS(df) - 1); +} + +#define MSA_BINOP_DF(func) \ +void helper_msa_ ## func ## _df(CPUMIPSState *env, uint32_t df, \ + uint32_t wd, uint32_t ws, uint32_t wt) \ +{ \ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); \ + wr_t *pws = &(env->active_fpu.fpr[ws].wr); \ + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); \ + uint32_t i; \ + \ + switch (df) { \ + case DF_BYTE: \ + for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) { \ + pwd->b[i] = msa_ ## func ## _df(df, pws->b[i], pwt->b[i]); \ + } \ + break; \ + case DF_HALF: \ + for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) { \ + pwd->h[i] = msa_ ## func ## _df(df, pws->h[i], pwt->h[i]); \ + } \ + break; \ + case DF_WORD: \ + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { \ + pwd->w[i] = msa_ ## func ## _df(df, pws->w[i], pwt->w[i]); \ + } \ + break; \ + case DF_DOUBLE: \ + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { \ + pwd->d[i] = msa_ ## func ## _df(df, pws->d[i], pwt->d[i]); \ + } \ + break; \ + default: \ + assert(0); \ + } \ +} + +MSA_BINOP_DF(sll) +MSA_BINOP_DF(sra) +MSA_BINOP_DF(srl) +MSA_BINOP_DF(bclr) +MSA_BINOP_DF(bset) +MSA_BINOP_DF(bneg) +MSA_BINOP_DF(addv) +MSA_BINOP_DF(subv) +MSA_BINOP_DF(max_s) +MSA_BINOP_DF(max_u) +MSA_BINOP_DF(min_s) +MSA_BINOP_DF(min_u) +MSA_BINOP_DF(max_a) +MSA_BINOP_DF(min_a) +MSA_BINOP_DF(ceq) +MSA_BINOP_DF(clt_s) +MSA_BINOP_DF(clt_u) +MSA_BINOP_DF(cle_s) +MSA_BINOP_DF(cle_u) +MSA_BINOP_DF(add_a) +MSA_BINOP_DF(adds_a) +MSA_BINOP_DF(adds_s) +MSA_BINOP_DF(adds_u) +MSA_BINOP_DF(ave_s) +MSA_BINOP_DF(ave_u) +MSA_BINOP_DF(aver_s) +MSA_BINOP_DF(aver_u) +MSA_BINOP_DF(subs_s) +MSA_BINOP_DF(subs_u) +MSA_BINOP_DF(subsus_u) +MSA_BINOP_DF(subsuu_s) +MSA_BINOP_DF(asub_s) +MSA_BINOP_DF(asub_u) +MSA_BINOP_DF(mulv) +MSA_BINOP_DF(div_s) +MSA_BINOP_DF(div_u) +MSA_BINOP_DF(mod_s) +MSA_BINOP_DF(mod_u) +MSA_BINOP_DF(dotp_s) +MSA_BINOP_DF(dotp_u) +MSA_BINOP_DF(srar) +MSA_BINOP_DF(srlr) +MSA_BINOP_DF(hadd_s) +MSA_BINOP_DF(hadd_u) +MSA_BINOP_DF(hsub_s) +MSA_BINOP_DF(hsub_u) + +MSA_BINOP_DF(mul_q) +MSA_BINOP_DF(mulr_q) +#undef MSA_BINOP_DF + +void helper_msa_sld_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t rt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + + msa_sld_df(df, pwd, pws, env->active_tc.gpr[rt]); +} + +static inline int64_t msa_maddv_df(uint32_t df, int64_t dest, int64_t arg1, + int64_t arg2) +{ + return dest + arg1 * arg2; +} + +static inline int64_t msa_msubv_df(uint32_t df, int64_t dest, int64_t arg1, + int64_t arg2) +{ + return dest - arg1 * arg2; +} + +static inline int64_t msa_dpadd_s_df(uint32_t df, int64_t dest, int64_t arg1, + int64_t arg2) +{ + int64_t even_arg1; + int64_t even_arg2; + int64_t odd_arg1; + int64_t odd_arg2; + SIGNED_EXTRACT(even_arg1, odd_arg1, arg1, df); + SIGNED_EXTRACT(even_arg2, odd_arg2, arg2, df); + return dest + (even_arg1 * even_arg2) + (odd_arg1 * odd_arg2); +} + +static inline int64_t msa_dpadd_u_df(uint32_t df, int64_t dest, int64_t arg1, + int64_t arg2) +{ + int64_t even_arg1; + int64_t even_arg2; + int64_t odd_arg1; + int64_t odd_arg2; + UNSIGNED_EXTRACT(even_arg1, odd_arg1, arg1, df); + UNSIGNED_EXTRACT(even_arg2, odd_arg2, arg2, df); + return dest + (even_arg1 * even_arg2) + (odd_arg1 * odd_arg2); +} + +static inline int64_t msa_dpsub_s_df(uint32_t df, int64_t dest, int64_t arg1, + int64_t arg2) +{ + int64_t even_arg1; + int64_t even_arg2; + int64_t odd_arg1; + int64_t odd_arg2; + SIGNED_EXTRACT(even_arg1, odd_arg1, arg1, df); + SIGNED_EXTRACT(even_arg2, odd_arg2, arg2, df); + return dest - ((even_arg1 * even_arg2) + (odd_arg1 * odd_arg2)); +} + +static inline int64_t msa_dpsub_u_df(uint32_t df, int64_t dest, int64_t arg1, + int64_t arg2) +{ + int64_t even_arg1; + int64_t even_arg2; + int64_t odd_arg1; + int64_t odd_arg2; + UNSIGNED_EXTRACT(even_arg1, odd_arg1, arg1, df); + UNSIGNED_EXTRACT(even_arg2, odd_arg2, arg2, df); + return dest - ((even_arg1 * even_arg2) + (odd_arg1 * odd_arg2)); +} + +static inline int64_t msa_madd_q_df(uint32_t df, int64_t dest, int64_t arg1, + int64_t arg2) +{ + int64_t q_prod, q_ret; + + int64_t q_max = DF_MAX_INT(df); + int64_t q_min = DF_MIN_INT(df); + + q_prod = arg1 * arg2; + q_ret = ((dest << (DF_BITS(df) - 1)) + q_prod) >> (DF_BITS(df) - 1); + + return (q_ret < q_min) ? q_min : (q_max < q_ret) ? q_max : q_ret; +} + +static inline int64_t msa_msub_q_df(uint32_t df, int64_t dest, int64_t arg1, + int64_t arg2) +{ + int64_t q_prod, q_ret; + + int64_t q_max = DF_MAX_INT(df); + int64_t q_min = DF_MIN_INT(df); + + q_prod = arg1 * arg2; + q_ret = ((dest << (DF_BITS(df) - 1)) - q_prod) >> (DF_BITS(df) - 1); + + return (q_ret < q_min) ? q_min : (q_max < q_ret) ? q_max : q_ret; +} + +static inline int64_t msa_maddr_q_df(uint32_t df, int64_t dest, int64_t arg1, + int64_t arg2) +{ + int64_t q_prod, q_ret; + + int64_t q_max = DF_MAX_INT(df); + int64_t q_min = DF_MIN_INT(df); + int64_t r_bit = 1 << (DF_BITS(df) - 2); + + q_prod = arg1 * arg2; + q_ret = ((dest << (DF_BITS(df) - 1)) + q_prod + r_bit) >> (DF_BITS(df) - 1); + + return (q_ret < q_min) ? q_min : (q_max < q_ret) ? q_max : q_ret; +} + +static inline int64_t msa_msubr_q_df(uint32_t df, int64_t dest, int64_t arg1, + int64_t arg2) +{ + int64_t q_prod, q_ret; + + int64_t q_max = DF_MAX_INT(df); + int64_t q_min = DF_MIN_INT(df); + int64_t r_bit = 1 << (DF_BITS(df) - 2); + + q_prod = arg1 * arg2; + q_ret = ((dest << (DF_BITS(df) - 1)) - q_prod + r_bit) >> (DF_BITS(df) - 1); + + return (q_ret < q_min) ? q_min : (q_max < q_ret) ? q_max : q_ret; +} + +#define MSA_TEROP_DF(func) \ +void helper_msa_ ## func ## _df(CPUMIPSState *env, uint32_t df, uint32_t wd, \ + uint32_t ws, uint32_t wt) \ +{ \ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); \ + wr_t *pws = &(env->active_fpu.fpr[ws].wr); \ + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); \ + uint32_t i; \ + \ + switch (df) { \ + case DF_BYTE: \ + for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) { \ + pwd->b[i] = msa_ ## func ## _df(df, pwd->b[i], pws->b[i], \ + pwt->b[i]); \ + } \ + break; \ + case DF_HALF: \ + for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) { \ + pwd->h[i] = msa_ ## func ## _df(df, pwd->h[i], pws->h[i], \ + pwt->h[i]); \ + } \ + break; \ + case DF_WORD: \ + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { \ + pwd->w[i] = msa_ ## func ## _df(df, pwd->w[i], pws->w[i], \ + pwt->w[i]); \ + } \ + break; \ + case DF_DOUBLE: \ + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { \ + pwd->d[i] = msa_ ## func ## _df(df, pwd->d[i], pws->d[i], \ + pwt->d[i]); \ + } \ + break; \ + default: \ + assert(0); \ + } \ +} + +MSA_TEROP_DF(maddv) +MSA_TEROP_DF(msubv) +MSA_TEROP_DF(dpadd_s) +MSA_TEROP_DF(dpadd_u) +MSA_TEROP_DF(dpsub_s) +MSA_TEROP_DF(dpsub_u) +MSA_TEROP_DF(binsl) +MSA_TEROP_DF(binsr) +MSA_TEROP_DF(madd_q) +MSA_TEROP_DF(msub_q) +MSA_TEROP_DF(maddr_q) +MSA_TEROP_DF(msubr_q) +#undef MSA_TEROP_DF + +static inline void msa_splat_df(uint32_t df, wr_t *pwd, + wr_t *pws, target_ulong rt) +{ + uint32_t n = rt % DF_ELEMENTS(df); + uint32_t i; + + switch (df) { + case DF_BYTE: + for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) { + pwd->b[i] = pws->b[n]; + } + break; + case DF_HALF: + for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) { + pwd->h[i] = pws->h[n]; + } + break; + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + pwd->w[i] = pws->w[n]; + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + pwd->d[i] = pws->d[n]; + } + break; + default: + assert(0); + } +} + +void helper_msa_splat_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t rt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + + msa_splat_df(df, pwd, pws, env->active_tc.gpr[rt]); +} + +#define MSA_DO_B MSA_DO(b) +#define MSA_DO_H MSA_DO(h) +#define MSA_DO_W MSA_DO(w) +#define MSA_DO_D MSA_DO(d) + +#define MSA_LOOP_B MSA_LOOP(B) +#define MSA_LOOP_H MSA_LOOP(H) +#define MSA_LOOP_W MSA_LOOP(W) +#define MSA_LOOP_D MSA_LOOP(D) + +#define MSA_LOOP_COND_B MSA_LOOP_COND(DF_BYTE) +#define MSA_LOOP_COND_H MSA_LOOP_COND(DF_HALF) +#define MSA_LOOP_COND_W MSA_LOOP_COND(DF_WORD) +#define MSA_LOOP_COND_D MSA_LOOP_COND(DF_DOUBLE) + +#define MSA_LOOP(DF) \ + for (i = 0; i < (MSA_LOOP_COND_ ## DF) ; i++) { \ + MSA_DO_ ## DF \ + } + +#define MSA_FN_DF(FUNC) \ +void helper_msa_##FUNC(CPUMIPSState *env, uint32_t df, uint32_t wd, \ + uint32_t ws, uint32_t wt) \ +{ \ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); \ + wr_t *pws = &(env->active_fpu.fpr[ws].wr); \ + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); \ + wr_t wx, *pwx = &wx; \ + uint32_t i; \ + switch (df) { \ + case DF_BYTE: \ + MSA_LOOP_B \ + break; \ + case DF_HALF: \ + MSA_LOOP_H \ + break; \ + case DF_WORD: \ + MSA_LOOP_W \ + break; \ + case DF_DOUBLE: \ + MSA_LOOP_D \ + break; \ + default: \ + assert(0); \ + } \ + msa_move_v(pwd, pwx); \ +} + +#define MSA_LOOP_COND(DF) \ + (DF_ELEMENTS(DF) / 2) + +#define Rb(pwr, i) (pwr->b[i]) +#define Lb(pwr, i) (pwr->b[i + DF_ELEMENTS(DF_BYTE)/2]) +#define Rh(pwr, i) (pwr->h[i]) +#define Lh(pwr, i) (pwr->h[i + DF_ELEMENTS(DF_HALF)/2]) +#define Rw(pwr, i) (pwr->w[i]) +#define Lw(pwr, i) (pwr->w[i + DF_ELEMENTS(DF_WORD)/2]) +#define Rd(pwr, i) (pwr->d[i]) +#define Ld(pwr, i) (pwr->d[i + DF_ELEMENTS(DF_DOUBLE)/2]) + +#define MSA_DO(DF) \ + do { \ + R##DF(pwx, i) = pwt->DF[2*i]; \ + L##DF(pwx, i) = pws->DF[2*i]; \ + } while (0); +MSA_FN_DF(pckev_df) +#undef MSA_DO + +#define MSA_DO(DF) \ + do { \ + R##DF(pwx, i) = pwt->DF[2*i+1]; \ + L##DF(pwx, i) = pws->DF[2*i+1]; \ + } while (0); +MSA_FN_DF(pckod_df) +#undef MSA_DO + +#define MSA_DO(DF) \ + do { \ + pwx->DF[2*i] = L##DF(pwt, i); \ + pwx->DF[2*i+1] = L##DF(pws, i); \ + } while (0); +MSA_FN_DF(ilvl_df) +#undef MSA_DO + +#define MSA_DO(DF) \ + do { \ + pwx->DF[2*i] = R##DF(pwt, i); \ + pwx->DF[2*i+1] = R##DF(pws, i); \ + } while (0); +MSA_FN_DF(ilvr_df) +#undef MSA_DO + +#define MSA_DO(DF) \ + do { \ + pwx->DF[2*i] = pwt->DF[2*i]; \ + pwx->DF[2*i+1] = pws->DF[2*i]; \ + } while (0); +MSA_FN_DF(ilvev_df) +#undef MSA_DO + +#define MSA_DO(DF) \ + do { \ + pwx->DF[2*i] = pwt->DF[2*i+1]; \ + pwx->DF[2*i+1] = pws->DF[2*i+1]; \ + } while (0); +MSA_FN_DF(ilvod_df) +#undef MSA_DO +#undef MSA_LOOP_COND + +#define MSA_LOOP_COND(DF) \ + (DF_ELEMENTS(DF)) + +#define MSA_DO(DF) \ + do { \ + uint32_t n = DF_ELEMENTS(df); \ + uint32_t k = (pwd->DF[i] & 0x3f) % (2 * n); \ + pwx->DF[i] = \ + (pwd->DF[i] & 0xc0) ? 0 : k < n ? pwt->DF[k] : pws->DF[k - n]; \ + } while (0); +MSA_FN_DF(vshf_df) +#undef MSA_DO +#undef MSA_LOOP_COND +#undef MSA_FN_DF + +void helper_msa_sldi_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t n) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + + msa_sld_df(df, pwd, pws, n); +} + +void helper_msa_splati_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t n) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + + msa_splat_df(df, pwd, pws, n); +} + +void helper_msa_copy_s_df(CPUMIPSState *env, uint32_t df, uint32_t rd, + uint32_t ws, uint32_t n) +{ + n %= DF_ELEMENTS(df); + + switch (df) { + case DF_BYTE: + env->active_tc.gpr[rd] = (int8_t)env->active_fpu.fpr[ws].wr.b[n]; + break; + case DF_HALF: + env->active_tc.gpr[rd] = (int16_t)env->active_fpu.fpr[ws].wr.h[n]; + break; + case DF_WORD: + env->active_tc.gpr[rd] = (int32_t)env->active_fpu.fpr[ws].wr.w[n]; + break; +#ifdef TARGET_MIPS64 + case DF_DOUBLE: + env->active_tc.gpr[rd] = (int64_t)env->active_fpu.fpr[ws].wr.d[n]; + break; +#endif + default: + assert(0); + } +} + +void helper_msa_copy_u_df(CPUMIPSState *env, uint32_t df, uint32_t rd, + uint32_t ws, uint32_t n) +{ + n %= DF_ELEMENTS(df); + + switch (df) { + case DF_BYTE: + env->active_tc.gpr[rd] = (uint8_t)env->active_fpu.fpr[ws].wr.b[n]; + break; + case DF_HALF: + env->active_tc.gpr[rd] = (uint16_t)env->active_fpu.fpr[ws].wr.h[n]; + break; + case DF_WORD: + env->active_tc.gpr[rd] = (uint32_t)env->active_fpu.fpr[ws].wr.w[n]; + break; +#ifdef TARGET_MIPS64 + case DF_DOUBLE: + env->active_tc.gpr[rd] = (uint64_t)env->active_fpu.fpr[ws].wr.d[n]; + break; +#endif + default: + assert(0); + } +} + +void helper_msa_insert_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t rs_num, uint32_t n) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + target_ulong rs = env->active_tc.gpr[rs_num]; + + switch (df) { + case DF_BYTE: + pwd->b[n] = (int8_t)rs; + break; + case DF_HALF: + pwd->h[n] = (int16_t)rs; + break; + case DF_WORD: + pwd->w[n] = (int32_t)rs; + break; + case DF_DOUBLE: + pwd->d[n] = (int64_t)rs; + break; + default: + assert(0); + } +} + +void helper_msa_insve_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t n) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + + switch (df) { + case DF_BYTE: + pwd->b[n] = (int8_t)pws->b[0]; + break; + case DF_HALF: + pwd->h[n] = (int16_t)pws->h[0]; + break; + case DF_WORD: + pwd->w[n] = (int32_t)pws->w[0]; + break; + case DF_DOUBLE: + pwd->d[n] = (int64_t)pws->d[0]; + break; + default: + assert(0); + } +} + +void helper_msa_ctcmsa(CPUMIPSState *env, target_ulong elm, uint32_t cd) +{ + switch (cd) { + case 0: + break; + case 1: + env->active_tc.msacsr = (int32_t)elm & MSACSR_MASK; + /* set float_status rounding mode */ + set_float_rounding_mode( + ieee_rm[(env->active_tc.msacsr & MSACSR_RM_MASK) >> MSACSR_RM], + &env->active_tc.msa_fp_status); + /* set float_status flush modes */ + set_flush_to_zero( + (env->active_tc.msacsr & MSACSR_FS_MASK) != 0 ? 1 : 0, + &env->active_tc.msa_fp_status); + set_flush_inputs_to_zero( + (env->active_tc.msacsr & MSACSR_FS_MASK) != 0 ? 1 : 0, + &env->active_tc.msa_fp_status); + /* check exception */ + if ((GET_FP_ENABLE(env->active_tc.msacsr) | FP_UNIMPLEMENTED) + & GET_FP_CAUSE(env->active_tc.msacsr)) { + helper_raise_exception(env, EXCP_MSAFPE); + } + break; + } +} + +target_ulong helper_msa_cfcmsa(CPUMIPSState *env, uint32_t cs) +{ + switch (cs) { + case 0: + return env->msair; + case 1: + return env->active_tc.msacsr & MSACSR_MASK; + } + return 0; +} + +void helper_msa_move_v(CPUMIPSState *env, uint32_t wd, uint32_t ws) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + + msa_move_v(pwd, pws); +} + +static inline int64_t msa_pcnt_df(uint32_t df, int64_t arg) +{ + uint64_t x; + + x = UNSIGNED(arg, df); + + x = (x & 0x5555555555555555ULL) + ((x >> 1) & 0x5555555555555555ULL); + x = (x & 0x3333333333333333ULL) + ((x >> 2) & 0x3333333333333333ULL); + x = (x & 0x0F0F0F0F0F0F0F0FULL) + ((x >> 4) & 0x0F0F0F0F0F0F0F0FULL); + x = (x & 0x00FF00FF00FF00FFULL) + ((x >> 8) & 0x00FF00FF00FF00FFULL); + x = (x & 0x0000FFFF0000FFFFULL) + ((x >> 16) & 0x0000FFFF0000FFFFULL); + x = (x & 0x00000000FFFFFFFFULL) + ((x >> 32)); + + return x; +} + +static inline int64_t msa_nlzc_df(uint32_t df, int64_t arg) +{ + uint64_t x, y; + int n, c; + + x = UNSIGNED(arg, df); + n = DF_BITS(df); + c = DF_BITS(df) / 2; + + do { + y = x >> c; + if (y != 0) { + n = n - c; + x = y; + } + c = c >> 1; + } while (c != 0); + + return n - x; +} + +static inline int64_t msa_nloc_df(uint32_t df, int64_t arg) +{ + return msa_nlzc_df(df, UNSIGNED((~arg), df)); +} + +void helper_msa_fill_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t rs) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + uint32_t i; + + switch (df) { + case DF_BYTE: + for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) { + pwd->b[i] = (int8_t)env->active_tc.gpr[rs]; + } + break; + case DF_HALF: + for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) { + pwd->h[i] = (int16_t)env->active_tc.gpr[rs]; + } + break; + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + pwd->w[i] = (int32_t)env->active_tc.gpr[rs]; + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + pwd->d[i] = (int64_t)env->active_tc.gpr[rs]; + } + break; + default: + assert(0); + } +} + +#define MSA_UNOP_DF(func) \ +void helper_msa_ ## func ## _df(CPUMIPSState *env, uint32_t df, \ + uint32_t wd, uint32_t ws) \ +{ \ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); \ + wr_t *pws = &(env->active_fpu.fpr[ws].wr); \ + uint32_t i; \ + \ + switch (df) { \ + case DF_BYTE: \ + for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) { \ + pwd->b[i] = msa_ ## func ## _df(df, pws->b[i]); \ + } \ + break; \ + case DF_HALF: \ + for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) { \ + pwd->h[i] = msa_ ## func ## _df(df, pws->h[i]); \ + } \ + break; \ + case DF_WORD: \ + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { \ + pwd->w[i] = msa_ ## func ## _df(df, pws->w[i]); \ + } \ + break; \ + case DF_DOUBLE: \ + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { \ + pwd->d[i] = msa_ ## func ## _df(df, pws->d[i]); \ + } \ + break; \ + default: \ + assert(0); \ + } \ +} + +MSA_UNOP_DF(nlzc) +MSA_UNOP_DF(nloc) +MSA_UNOP_DF(pcnt) +#undef MSA_UNOP_DF + +#define FLOAT_ONE32 make_float32(0x3f8 << 20) +#define FLOAT_ONE64 make_float64(0x3ffULL << 52) + +#define FLOAT_SNAN16 (float16_default_nan ^ 0x0220) + /* 0x7c20 */ +#define FLOAT_SNAN32 (float32_default_nan ^ 0x00400020) + /* 0x7f800020 */ +#define FLOAT_SNAN64 (float64_default_nan ^ 0x0008000000000020ULL) + /* 0x7ff0000000000020 */ + +static inline void clear_msacsr_cause(CPUMIPSState *env) +{ + SET_FP_CAUSE(env->active_tc.msacsr, 0); +} + +static inline void check_msacsr_cause(CPUMIPSState *env) +{ + if ((GET_FP_CAUSE(env->active_tc.msacsr) & + (GET_FP_ENABLE(env->active_tc.msacsr) | FP_UNIMPLEMENTED)) == 0) { + UPDATE_FP_FLAGS(env->active_tc.msacsr, + GET_FP_CAUSE(env->active_tc.msacsr)); + } else { + helper_raise_exception(env, EXCP_MSAFPE); + } +} + +/* Flush-to-zero use cases for update_msacsr() */ +#define CLEAR_FS_UNDERFLOW 1 +#define CLEAR_IS_INEXACT 2 +#define RECIPROCAL_INEXACT 4 + +static inline int update_msacsr(CPUMIPSState *env, int action, int denormal) +{ + int ieee_ex; + + int c; + int cause; + int enable; + + ieee_ex = get_float_exception_flags(&env->active_tc.msa_fp_status); + + /* QEMU softfloat does not signal all underflow cases */ + if (denormal) { + ieee_ex |= float_flag_underflow; + } + + c = ieee_ex_to_mips(ieee_ex); + enable = GET_FP_ENABLE(env->active_tc.msacsr) | FP_UNIMPLEMENTED; + + /* Set Inexact (I) when flushing inputs to zero */ + if ((ieee_ex & float_flag_input_denormal) && + (env->active_tc.msacsr & MSACSR_FS_MASK) != 0) { + if (action & CLEAR_IS_INEXACT) { + c &= ~FP_INEXACT; + } else { + c |= FP_INEXACT; + } + } + + /* Set Inexact (I) and Underflow (U) when flushing outputs to zero */ + if ((ieee_ex & float_flag_output_denormal) && + (env->active_tc.msacsr & MSACSR_FS_MASK) != 0) { + c |= FP_INEXACT; + if (action & CLEAR_FS_UNDERFLOW) { + c &= ~FP_UNDERFLOW; + } else { + c |= FP_UNDERFLOW; + } + } + + /* Set Inexact (I) when Overflow (O) is not enabled */ + if ((c & FP_OVERFLOW) != 0 && (enable & FP_OVERFLOW) == 0) { + c |= FP_INEXACT; + } + + /* Clear Exact Underflow when Underflow (U) is not enabled */ + if ((c & FP_UNDERFLOW) != 0 && (enable & FP_UNDERFLOW) == 0 && + (c & FP_INEXACT) == 0) { + c &= ~FP_UNDERFLOW; + } + + /* Reciprocal operations set only Inexact when valid and not + divide by zero */ + if ((action & RECIPROCAL_INEXACT) && + (c & (FP_INVALID | FP_DIV0)) == 0) { + c = FP_INEXACT; + } + + cause = c & enable; /* all current enabled exceptions */ + + if (cause == 0) { + /* No enabled exception, update the MSACSR Cause + with all current exceptions */ + SET_FP_CAUSE(env->active_tc.msacsr, + (GET_FP_CAUSE(env->active_tc.msacsr) | c)); + } else { + /* Current exceptions are enabled */ + if ((env->active_tc.msacsr & MSACSR_NX_MASK) == 0) { + /* Exception(s) will trap, update MSACSR Cause + with all enabled exceptions */ + SET_FP_CAUSE(env->active_tc.msacsr, + (GET_FP_CAUSE(env->active_tc.msacsr) | c)); + } + } + + return c; +} + +static inline int get_enabled_exceptions(const CPUMIPSState *env, int c) +{ + int enable = GET_FP_ENABLE(env->active_tc.msacsr) | FP_UNIMPLEMENTED; + return c & enable; +} + +static inline float16 float16_from_float32(int32 a, flag ieee STATUS_PARAM) +{ + float16 f_val; + + f_val = float32_to_float16((float32)a, ieee STATUS_VAR); + f_val = float16_maybe_silence_nan(f_val); + + return a < 0 ? (f_val | (1 << 15)) : f_val; +} + +static inline float32 float32_from_float64(int64 a STATUS_PARAM) +{ + float32 f_val; + + f_val = float64_to_float32((float64)a STATUS_VAR); + f_val = float32_maybe_silence_nan(f_val); + + return a < 0 ? (f_val | (1 << 31)) : f_val; +} + +static inline float32 float32_from_float16(int16_t a, flag ieee STATUS_PARAM) +{ + float32 f_val; + + f_val = float16_to_float32((float16)a, ieee STATUS_VAR); + f_val = float32_maybe_silence_nan(f_val); + + return a < 0 ? (f_val | (1 << 31)) : f_val; +} + +static inline float64 float64_from_float32(int32 a STATUS_PARAM) +{ + float64 f_val; + + f_val = float32_to_float64((float64)a STATUS_VAR); + f_val = float64_maybe_silence_nan(f_val); + + return a < 0 ? (f_val | (1ULL << 63)) : f_val; +} + +static inline float32 float32_from_q16(int16_t a STATUS_PARAM) +{ + float32 f_val; + + /* conversion as integer and scaling */ + f_val = int32_to_float32(a STATUS_VAR); + f_val = float32_scalbn(f_val, -15 STATUS_VAR); + + return f_val; +} + +static inline float64 float64_from_q32(int32 a STATUS_PARAM) +{ + float64 f_val; + + /* conversion as integer and scaling */ + f_val = int32_to_float64(a STATUS_VAR); + f_val = float64_scalbn(f_val, -31 STATUS_VAR); + + return f_val; +} + +static inline int16_t float32_to_q16(float32 a STATUS_PARAM) +{ + int32 q_val; + int32 q_min = 0xffff8000; + int32 q_max = 0x00007fff; + + int ieee_ex; + + if (float32_is_any_nan(a)) { + float_raise(float_flag_invalid STATUS_VAR); + return 0; + } + + /* scaling */ + a = float32_scalbn(a, 15 STATUS_VAR); + + ieee_ex = get_float_exception_flags(status); + set_float_exception_flags(ieee_ex & (~float_flag_underflow) + STATUS_VAR); + + if (ieee_ex & float_flag_overflow) { + float_raise(float_flag_inexact STATUS_VAR); + return (int32)a < 0 ? q_min : q_max; + } + + /* conversion to int */ + q_val = float32_to_int32(a STATUS_VAR); + + ieee_ex = get_float_exception_flags(status); + set_float_exception_flags(ieee_ex & (~float_flag_underflow) + STATUS_VAR); + + if (ieee_ex & float_flag_invalid) { + set_float_exception_flags(ieee_ex & (~float_flag_invalid) + STATUS_VAR); + float_raise(float_flag_overflow | float_flag_inexact STATUS_VAR); + return (int32)a < 0 ? q_min : q_max; + } + + if (q_val < q_min) { + float_raise(float_flag_overflow | float_flag_inexact STATUS_VAR); + return (int16_t)q_min; + } + + if (q_max < q_val) { + float_raise(float_flag_overflow | float_flag_inexact STATUS_VAR); + return (int16_t)q_max; + } + + return (int16_t)q_val; +} + +static inline int32 float64_to_q32(float64 a STATUS_PARAM) +{ + int64 q_val; + int64 q_min = 0xffffffff80000000LL; + int64 q_max = 0x000000007fffffffLL; + + int ieee_ex; + + if (float64_is_any_nan(a)) { + float_raise(float_flag_invalid STATUS_VAR); + return 0; + } + + /* scaling */ + a = float64_scalbn(a, 31 STATUS_VAR); + + ieee_ex = get_float_exception_flags(status); + set_float_exception_flags(ieee_ex & (~float_flag_underflow) + STATUS_VAR); + + if (ieee_ex & float_flag_overflow) { + float_raise(float_flag_inexact STATUS_VAR); + return (int64)a < 0 ? q_min : q_max; + } + + /* conversion to integer */ + q_val = float64_to_int64(a STATUS_VAR); + + ieee_ex = get_float_exception_flags(status); + set_float_exception_flags(ieee_ex & (~float_flag_underflow) + STATUS_VAR); + + if (ieee_ex & float_flag_invalid) { + set_float_exception_flags(ieee_ex & (~float_flag_invalid) + STATUS_VAR); + float_raise(float_flag_overflow | float_flag_inexact STATUS_VAR); + return (int64)a < 0 ? q_min : q_max; + } + + if (q_val < q_min) { + float_raise(float_flag_overflow | float_flag_inexact STATUS_VAR); + return (int32)q_min; + } + + if (q_max < q_val) { + float_raise(float_flag_overflow | float_flag_inexact STATUS_VAR); + return (int32)q_max; + } + + return (int32)q_val; +} + +#define MSA_FLOAT_COND(DEST, OP, ARG1, ARG2, BITS, QUIET) \ + do { \ + int c; \ + int64_t cond; \ + set_float_exception_flags(0, &env->active_tc.msa_fp_status); \ + if (!QUIET) { \ + cond = float ## BITS ## _ ## OP(ARG1, ARG2, \ + &env->active_tc.msa_fp_status); \ + } else { \ + cond = float ## BITS ## _ ## OP ## _quiet(ARG1, ARG2, \ + &env->active_tc.msa_fp_status); \ + } \ + DEST = cond ? M_MAX_UINT(BITS) : 0; \ + c = update_msacsr(env, CLEAR_IS_INEXACT, 0); \ + \ + if (get_enabled_exceptions(env, c)) { \ + DEST = ((FLOAT_SNAN ## BITS >> 6) << 6) | c; \ + } \ + } while (0) + +#define MSA_FLOAT_AF(DEST, ARG1, ARG2, BITS, QUIET) \ + do { \ + MSA_FLOAT_COND(DEST, eq, ARG1, ARG2, BITS, QUIET); \ + if ((DEST & M_MAX_UINT(BITS)) == M_MAX_UINT(BITS)) { \ + DEST = 0; \ + } \ + } while (0) + +#define MSA_FLOAT_UEQ(DEST, ARG1, ARG2, BITS, QUIET) \ + do { \ + MSA_FLOAT_COND(DEST, unordered, ARG1, ARG2, BITS, QUIET); \ + if (DEST == 0) { \ + MSA_FLOAT_COND(DEST, eq, ARG1, ARG2, BITS, QUIET); \ + } \ + } while (0) + +#define MSA_FLOAT_NE(DEST, ARG1, ARG2, BITS, QUIET) \ + do { \ + MSA_FLOAT_COND(DEST, lt, ARG1, ARG2, BITS, QUIET); \ + if (DEST == 0) { \ + MSA_FLOAT_COND(DEST, lt, ARG2, ARG1, BITS, QUIET); \ + } \ + } while (0) + +#define MSA_FLOAT_UNE(DEST, ARG1, ARG2, BITS, QUIET) \ + do { \ + MSA_FLOAT_COND(DEST, unordered, ARG1, ARG2, BITS, QUIET); \ + if (DEST == 0) { \ + MSA_FLOAT_COND(DEST, lt, ARG1, ARG2, BITS, QUIET); \ + if (DEST == 0) { \ + MSA_FLOAT_COND(DEST, lt, ARG2, ARG1, BITS, QUIET); \ + } \ + } \ + } while (0) + +#define MSA_FLOAT_ULE(DEST, ARG1, ARG2, BITS, QUIET) \ + do { \ + MSA_FLOAT_COND(DEST, unordered, ARG1, ARG2, BITS, QUIET); \ + if (DEST == 0) { \ + MSA_FLOAT_COND(DEST, le, ARG1, ARG2, BITS, QUIET); \ + } \ + } while (0) + +#define MSA_FLOAT_ULT(DEST, ARG1, ARG2, BITS, QUIET) \ + do { \ + MSA_FLOAT_COND(DEST, unordered, ARG1, ARG2, BITS, QUIET); \ + if (DEST == 0) { \ + MSA_FLOAT_COND(DEST, lt, ARG1, ARG2, BITS, QUIET); \ + } \ + } while (0) + +#define MSA_FLOAT_OR(DEST, ARG1, ARG2, BITS, QUIET) \ + do { \ + MSA_FLOAT_COND(DEST, le, ARG1, ARG2, BITS, QUIET); \ + if (DEST == 0) { \ + MSA_FLOAT_COND(DEST, le, ARG2, ARG1, BITS, QUIET); \ + } \ + } while (0) + +static inline void compare_af(CPUMIPSState *env, wr_t *pwd, wr_t *pws, + wr_t *pwt, uint32_t df, int quiet) +{ + wr_t wx, *pwx = &wx; + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_AF(pwx->w[i], pws->w[i], pwt->w[i], 32, quiet); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_AF(pwx->d[i], pws->d[i], pwt->d[i], 64, quiet); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +static inline void compare_un(CPUMIPSState *env, wr_t *pwd, wr_t *pws, + wr_t *pwt, uint32_t df, int quiet) +{ + wr_t wx, *pwx = &wx; + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_COND(pwx->w[i], unordered, pws->w[i], pwt->w[i], 32, + quiet); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_COND(pwx->d[i], unordered, pws->d[i], pwt->d[i], 64, + quiet); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +static inline void compare_eq(CPUMIPSState *env, wr_t *pwd, wr_t *pws, + wr_t *pwt, uint32_t df, int quiet) +{ + wr_t wx, *pwx = &wx; + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_COND(pwx->w[i], eq, pws->w[i], pwt->w[i], 32, quiet); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_COND(pwx->d[i], eq, pws->d[i], pwt->d[i], 64, quiet); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +static inline void compare_ueq(CPUMIPSState *env, wr_t *pwd, wr_t *pws, + wr_t *pwt, uint32_t df, int quiet) +{ + wr_t wx, *pwx = &wx; + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_UEQ(pwx->w[i], pws->w[i], pwt->w[i], 32, quiet); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_UEQ(pwx->d[i], pws->d[i], pwt->d[i], 64, quiet); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +static inline void compare_lt(CPUMIPSState *env, wr_t *pwd, wr_t *pws, + wr_t *pwt, uint32_t df, int quiet) +{ + wr_t wx, *pwx = &wx; + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_COND(pwx->w[i], lt, pws->w[i], pwt->w[i], 32, quiet); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_COND(pwx->d[i], lt, pws->d[i], pwt->d[i], 64, quiet); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +static inline void compare_ult(CPUMIPSState *env, wr_t *pwd, wr_t *pws, + wr_t *pwt, uint32_t df, int quiet) +{ + wr_t wx, *pwx = &wx; + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_ULT(pwx->w[i], pws->w[i], pwt->w[i], 32, quiet); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_ULT(pwx->d[i], pws->d[i], pwt->d[i], 64, quiet); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +static inline void compare_le(CPUMIPSState *env, wr_t *pwd, wr_t *pws, + wr_t *pwt, uint32_t df, int quiet) +{ + wr_t wx, *pwx = &wx; + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_COND(pwx->w[i], le, pws->w[i], pwt->w[i], 32, quiet); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_COND(pwx->d[i], le, pws->d[i], pwt->d[i], 64, quiet); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +static inline void compare_ule(CPUMIPSState *env, wr_t *pwd, wr_t *pws, + wr_t *pwt, uint32_t df, int quiet) +{ + wr_t wx, *pwx = &wx; + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_ULE(pwx->w[i], pws->w[i], pwt->w[i], 32, quiet); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_ULE(pwx->d[i], pws->d[i], pwt->d[i], 64, quiet); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +static inline void compare_or(CPUMIPSState *env, wr_t *pwd, wr_t *pws, + wr_t *pwt, uint32_t df, int quiet) +{ + wr_t wx, *pwx = &wx; + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_OR(pwx->w[i], pws->w[i], pwt->w[i], 32, quiet); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_OR(pwx->d[i], pws->d[i], pwt->d[i], 64, quiet); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +static inline void compare_une(CPUMIPSState *env, wr_t *pwd, wr_t *pws, + wr_t *pwt, uint32_t df, int quiet) +{ + wr_t wx, *pwx = &wx; + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_UNE(pwx->w[i], pws->w[i], pwt->w[i], 32, quiet); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_UNE(pwx->d[i], pws->d[i], pwt->d[i], 64, quiet); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +static inline void compare_ne(CPUMIPSState *env, wr_t *pwd, wr_t *pws, + wr_t *pwt, uint32_t df, int quiet) { + wr_t wx, *pwx = &wx; + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_NE(pwx->w[i], pws->w[i], pwt->w[i], 32, quiet); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_NE(pwx->d[i], pws->d[i], pwt->d[i], 64, quiet); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +void helper_msa_fcaf_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_af(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fcun_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_un(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fceq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_eq(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fcueq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_ueq(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fclt_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_lt(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fcult_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_ult(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fcle_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_le(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fcule_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_ule(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fsaf_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_af(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fsun_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_un(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fseq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_eq(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fsueq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_ueq(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fslt_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_lt(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fsult_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_ult(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fsle_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_le(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fsule_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_ule(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fcor_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_or(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fcune_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_une(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fcne_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_ne(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fsor_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_or(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fsune_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_une(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fsne_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + compare_ne(env, pwd, pws, pwt, df, 0); +} + +#define float16_is_zero(ARG) 0 +#define float16_is_zero_or_denormal(ARG) 0 + +#define IS_DENORMAL(ARG, BITS) \ + (!float ## BITS ## _is_zero(ARG) \ + && float ## BITS ## _is_zero_or_denormal(ARG)) + +#define MSA_FLOAT_BINOP(DEST, OP, ARG1, ARG2, BITS) \ + do { \ + int c; \ + \ + set_float_exception_flags(0, &env->active_tc.msa_fp_status); \ + DEST = float ## BITS ## _ ## OP(ARG1, ARG2, \ + &env->active_tc.msa_fp_status); \ + c = update_msacsr(env, 0, IS_DENORMAL(DEST, BITS)); \ + \ + if (get_enabled_exceptions(env, c)) { \ + DEST = ((FLOAT_SNAN ## BITS >> 6) << 6) | c; \ + } \ + } while (0) + +void helper_msa_fadd_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_BINOP(pwx->w[i], add, pws->w[i], pwt->w[i], 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_BINOP(pwx->d[i], add, pws->d[i], pwt->d[i], 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + msa_move_v(pwd, pwx); +} + +void helper_msa_fsub_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_BINOP(pwx->w[i], sub, pws->w[i], pwt->w[i], 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_BINOP(pwx->d[i], sub, pws->d[i], pwt->d[i], 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + msa_move_v(pwd, pwx); +} + +void helper_msa_fmul_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_BINOP(pwx->w[i], mul, pws->w[i], pwt->w[i], 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_BINOP(pwx->d[i], mul, pws->d[i], pwt->d[i], 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +void helper_msa_fdiv_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_BINOP(pwx->w[i], div, pws->w[i], pwt->w[i], 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_BINOP(pwx->d[i], div, pws->d[i], pwt->d[i], 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +#define MSA_FLOAT_MULADD(DEST, ARG1, ARG2, ARG3, NEGATE, BITS) \ + do { \ + int c; \ + \ + set_float_exception_flags(0, &env->active_tc.msa_fp_status); \ + DEST = float ## BITS ## _muladd(ARG2, ARG3, ARG1, NEGATE, \ + &env->active_tc.msa_fp_status); \ + c = update_msacsr(env, 0, IS_DENORMAL(DEST, BITS)); \ + \ + if (get_enabled_exceptions(env, c)) { \ + DEST = ((FLOAT_SNAN ## BITS >> 6) << 6) | c; \ + } \ + } while (0) + +void helper_msa_fmadd_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_MULADD(pwx->w[i], pwd->w[i], + pws->w[i], pwt->w[i], 0, 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_MULADD(pwx->d[i], pwd->d[i], + pws->d[i], pwt->d[i], 0, 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +void helper_msa_fmsub_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_MULADD(pwx->w[i], pwd->w[i], + pws->w[i], pwt->w[i], + float_muladd_negate_product, 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_MULADD(pwx->d[i], pwd->d[i], + pws->d[i], pwt->d[i], + float_muladd_negate_product, 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +void helper_msa_fexp2_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_BINOP(pwx->w[i], scalbn, pws->w[i], + pwt->w[i] > 0x200 ? 0x200 : + pwt->w[i] < -0x200 ? -0x200 : pwt->w[i], + 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_BINOP(pwx->d[i], scalbn, pws->d[i], + pwt->d[i] > 0x1000 ? 0x1000 : + pwt->d[i] < -0x1000 ? -0x1000 : pwt->d[i], + 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +#define MSA_FLOAT_UNOP(DEST, OP, ARG, BITS) \ + do { \ + int c; \ + \ + set_float_exception_flags(0, &env->active_tc.msa_fp_status); \ + DEST = float ## BITS ## _ ## OP(ARG, &env->active_tc.msa_fp_status);\ + c = update_msacsr(env, 0, IS_DENORMAL(DEST, BITS)); \ + \ + if (get_enabled_exceptions(env, c)) { \ + DEST = ((FLOAT_SNAN ## BITS >> 6) << 6) | c; \ + } \ + } while (0) + +void helper_msa_fexdo_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + uint32_t i; + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + /* Half precision floats come in two formats: standard + IEEE and "ARM" format. The latter gains extra exponent + range by omitting the NaN/Inf encodings. */ + flag ieee = 1; + + MSA_FLOAT_BINOP(Lh(pwx, i), from_float32, pws->w[i], ieee, 16); + MSA_FLOAT_BINOP(Rh(pwx, i), from_float32, pwt->w[i], ieee, 16); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_UNOP(Lw(pwx, i), from_float64, pws->d[i], 32); + MSA_FLOAT_UNOP(Rw(pwx, i), from_float64, pwt->d[i], 32); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + msa_move_v(pwd, pwx); +} + +#define MSA_FLOAT_UNOP_XD(DEST, OP, ARG, BITS, XBITS) \ + do { \ + int c; \ + \ + set_float_exception_flags(0, &env->active_tc.msa_fp_status); \ + DEST = float ## BITS ## _ ## OP(ARG, &env->active_tc.msa_fp_status);\ + c = update_msacsr(env, CLEAR_FS_UNDERFLOW, 0); \ + \ + if (get_enabled_exceptions(env, c)) { \ + DEST = ((FLOAT_SNAN ## XBITS >> 6) << 6) | c; \ + } \ + } while (0) + +void helper_msa_ftq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_UNOP_XD(Lh(pwx, i), to_q16, pws->w[i], 32, 16); + MSA_FLOAT_UNOP_XD(Rh(pwx, i), to_q16, pwt->w[i], 32, 16); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_UNOP_XD(Lw(pwx, i), to_q32, pws->d[i], 64, 32); + MSA_FLOAT_UNOP_XD(Rw(pwx, i), to_q32, pwt->d[i], 64, 32); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +#define NUMBER_QNAN_PAIR(ARG1, ARG2, BITS) \ + !float ## BITS ## _is_any_nan(ARG1) \ + && float ## BITS ## _is_quiet_nan(ARG2) + +#define MSA_FLOAT_MAXOP(DEST, OP, ARG1, ARG2, BITS) \ + do { \ + int c; \ + \ + set_float_exception_flags(0, &env->active_tc.msa_fp_status); \ + DEST = float ## BITS ## _ ## OP(ARG1, ARG2, \ + &env->active_tc.msa_fp_status); \ + c = update_msacsr(env, 0, 0); \ + \ + if (get_enabled_exceptions(env, c)) { \ + DEST = ((FLOAT_SNAN ## BITS >> 6) << 6) | c; \ + } \ + } while (0) + +#define FMAXMIN_A(F, G, X, _S, _T, BITS) \ + do { \ + uint## BITS ##_t S = _S, T = _T; \ + uint## BITS ##_t as, at, xs, xt, xd; \ + if (NUMBER_QNAN_PAIR(S, T, BITS)) { \ + T = S; \ + } \ + else if (NUMBER_QNAN_PAIR(T, S, BITS)) { \ + S = T; \ + } \ + as = float## BITS ##_abs(S); \ + at = float## BITS ##_abs(T); \ + MSA_FLOAT_MAXOP(xs, F, S, T, BITS); \ + MSA_FLOAT_MAXOP(xt, G, S, T, BITS); \ + MSA_FLOAT_MAXOP(xd, F, as, at, BITS); \ + X = (as == at || xd == float## BITS ##_abs(xs)) ? xs : xt; \ + } while (0) + +void helper_msa_fmin_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + if (NUMBER_QNAN_PAIR(pws->w[i], pwt->w[i], 32)) { + MSA_FLOAT_MAXOP(pwx->w[i], min, pws->w[i], pws->w[i], 32); + } else if (NUMBER_QNAN_PAIR(pwt->w[i], pws->w[i], 32)) { + MSA_FLOAT_MAXOP(pwx->w[i], min, pwt->w[i], pwt->w[i], 32); + } else { + MSA_FLOAT_MAXOP(pwx->w[i], min, pws->w[i], pwt->w[i], 32); + } + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + if (NUMBER_QNAN_PAIR(pws->d[i], pwt->d[i], 64)) { + MSA_FLOAT_MAXOP(pwx->d[i], min, pws->d[i], pws->d[i], 64); + } else if (NUMBER_QNAN_PAIR(pwt->d[i], pws->d[i], 64)) { + MSA_FLOAT_MAXOP(pwx->d[i], min, pwt->d[i], pwt->d[i], 64); + } else { + MSA_FLOAT_MAXOP(pwx->d[i], min, pws->d[i], pwt->d[i], 64); + } + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +void helper_msa_fmin_a_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + FMAXMIN_A(min, max, pwx->w[i], pws->w[i], pwt->w[i], 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + FMAXMIN_A(min, max, pwx->d[i], pws->d[i], pwt->d[i], 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +void helper_msa_fmax_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + if (NUMBER_QNAN_PAIR(pws->w[i], pwt->w[i], 32)) { + MSA_FLOAT_MAXOP(pwx->w[i], max, pws->w[i], pws->w[i], 32); + } else if (NUMBER_QNAN_PAIR(pwt->w[i], pws->w[i], 32)) { + MSA_FLOAT_MAXOP(pwx->w[i], max, pwt->w[i], pwt->w[i], 32); + } else { + MSA_FLOAT_MAXOP(pwx->w[i], max, pws->w[i], pwt->w[i], 32); + } + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + if (NUMBER_QNAN_PAIR(pws->d[i], pwt->d[i], 64)) { + MSA_FLOAT_MAXOP(pwx->d[i], max, pws->d[i], pws->d[i], 64); + } else if (NUMBER_QNAN_PAIR(pwt->d[i], pws->d[i], 64)) { + MSA_FLOAT_MAXOP(pwx->d[i], max, pwt->d[i], pwt->d[i], 64); + } else { + MSA_FLOAT_MAXOP(pwx->d[i], max, pws->d[i], pwt->d[i], 64); + } + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +void helper_msa_fmax_a_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws, uint32_t wt) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + wr_t *pwt = &(env->active_fpu.fpr[wt].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + FMAXMIN_A(max, min, pwx->w[i], pws->w[i], pwt->w[i], 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + FMAXMIN_A(max, min, pwx->d[i], pws->d[i], pwt->d[i], 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +void helper_msa_fclass_df(CPUMIPSState *env, uint32_t df, + uint32_t wd, uint32_t ws) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + if (df == DF_WORD) { + pwd->w[0] = helper_float_class_s(pws->w[0]); + pwd->w[1] = helper_float_class_s(pws->w[1]); + pwd->w[2] = helper_float_class_s(pws->w[2]); + pwd->w[3] = helper_float_class_s(pws->w[3]); + } else { + pwd->d[0] = helper_float_class_d(pws->d[0]); + pwd->d[1] = helper_float_class_d(pws->d[1]); + } +} + +#define MSA_FLOAT_UNOP0(DEST, OP, ARG, BITS) \ + do { \ + int c; \ + \ + set_float_exception_flags(0, &env->active_tc.msa_fp_status); \ + DEST = float ## BITS ## _ ## OP(ARG, &env->active_tc.msa_fp_status);\ + c = update_msacsr(env, CLEAR_FS_UNDERFLOW, 0); \ + \ + if (get_enabled_exceptions(env, c)) { \ + DEST = ((FLOAT_SNAN ## BITS >> 6) << 6) | c; \ + } else if (float ## BITS ## _is_any_nan(ARG)) { \ + DEST = 0; \ + } \ + } while (0) + +void helper_msa_ftrunc_s_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_UNOP0(pwx->w[i], to_int32_round_to_zero, pws->w[i], 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_UNOP0(pwx->d[i], to_int64_round_to_zero, pws->d[i], 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +void helper_msa_ftrunc_u_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_UNOP0(pwx->w[i], to_uint32_round_to_zero, pws->w[i], 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_UNOP0(pwx->d[i], to_uint64_round_to_zero, pws->d[i], 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +void helper_msa_fsqrt_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_UNOP(pwx->w[i], sqrt, pws->w[i], 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_UNOP(pwx->d[i], sqrt, pws->d[i], 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +#define MSA_FLOAT_RECIPROCAL(DEST, ARG, BITS) \ + do { \ + int c; \ + \ + set_float_exception_flags(0, &env->active_tc.msa_fp_status); \ + DEST = float ## BITS ## _ ## div(FLOAT_ONE ## BITS, ARG, \ + &env->active_tc.msa_fp_status); \ + c = update_msacsr(env, float ## BITS ## _is_infinity(ARG) || \ + float ## BITS ## _is_quiet_nan(DEST) ? \ + 0 : RECIPROCAL_INEXACT, \ + IS_DENORMAL(DEST, BITS)); \ + \ + if (get_enabled_exceptions(env, c)) { \ + DEST = ((FLOAT_SNAN ## BITS >> 6) << 6) | c; \ + } \ + } while (0) + +void helper_msa_frsqrt_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_RECIPROCAL(pwx->w[i], float32_sqrt(pws->w[i], + &env->active_tc.msa_fp_status), 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_RECIPROCAL(pwx->d[i], float64_sqrt(pws->d[i], + &env->active_tc.msa_fp_status), 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +void helper_msa_frcp_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_RECIPROCAL(pwx->w[i], pws->w[i], 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_RECIPROCAL(pwx->d[i], pws->d[i], 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +void helper_msa_frint_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_UNOP(pwx->w[i], round_to_int, pws->w[i], 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_UNOP(pwx->d[i], round_to_int, pws->d[i], 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +#define MSA_FLOAT_LOGB(DEST, ARG, BITS) \ + do { \ + int c; \ + \ + set_float_exception_flags(0, &env->active_tc.msa_fp_status); \ + set_float_rounding_mode(float_round_down, \ + &env->active_tc.msa_fp_status); \ + DEST = float ## BITS ## _ ## log2(ARG, \ + &env->active_tc.msa_fp_status); \ + DEST = float ## BITS ## _ ## round_to_int(DEST, \ + &env->active_tc.msa_fp_status); \ + set_float_rounding_mode(ieee_rm[(env->active_tc.msacsr & \ + MSACSR_RM_MASK) >> MSACSR_RM], \ + &env->active_tc.msa_fp_status); \ + \ + set_float_exception_flags( \ + get_float_exception_flags(&env->active_tc.msa_fp_status) \ + & (~float_flag_inexact), \ + &env->active_tc.msa_fp_status); \ + \ + c = update_msacsr(env, 0, IS_DENORMAL(DEST, BITS)); \ + \ + if (get_enabled_exceptions(env, c)) { \ + DEST = ((FLOAT_SNAN ## BITS >> 6) << 6) | c; \ + } \ + } while (0) + +void helper_msa_flog2_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_LOGB(pwx->w[i], pws->w[i], 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_LOGB(pwx->d[i], pws->d[i], 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +void helper_msa_fexupl_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + uint32_t i; + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + /* Half precision floats come in two formats: standard + IEEE and "ARM" format. The latter gains extra exponent + range by omitting the NaN/Inf encodings. */ + flag ieee = 1; + + MSA_FLOAT_BINOP(pwx->w[i], from_float16, Lh(pws, i), ieee, 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_UNOP(pwx->d[i], from_float32, Lw(pws, i), 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + msa_move_v(pwd, pwx); +} + +void helper_msa_fexupr_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + uint32_t i; + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + /* Half precision floats come in two formats: standard + IEEE and "ARM" format. The latter gains extra exponent + range by omitting the NaN/Inf encodings. */ + flag ieee = 1; + + MSA_FLOAT_BINOP(pwx->w[i], from_float16, Rh(pws, i), ieee, 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_UNOP(pwx->d[i], from_float32, Rw(pws, i), 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + msa_move_v(pwd, pwx); +} + +void helper_msa_ffql_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + uint32_t i; + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_UNOP(pwx->w[i], from_q16, Lh(pws, i), 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_UNOP(pwx->d[i], from_q32, Lw(pws, i), 64); + } + break; + default: + assert(0); + } + + msa_move_v(pwd, pwx); +} + +void helper_msa_ffqr_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + uint32_t i; + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_UNOP(pwx->w[i], from_q16, Rh(pws, i), 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_UNOP(pwx->d[i], from_q32, Rw(pws, i), 64); + } + break; + default: + assert(0); + } + + msa_move_v(pwd, pwx); +} + +void helper_msa_ftint_s_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_UNOP0(pwx->w[i], to_int32, pws->w[i], 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_UNOP0(pwx->d[i], to_int64, pws->d[i], 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +void helper_msa_ftint_u_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_UNOP0(pwx->w[i], to_uint32, pws->w[i], 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_UNOP0(pwx->d[i], to_uint64, pws->d[i], 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +#define float32_from_int32 int32_to_float32 +#define float32_from_uint32 uint32_to_float32 + +#define float64_from_int64 int64_to_float64 +#define float64_from_uint64 uint64_to_float64 + +void helper_msa_ffint_s_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_UNOP(pwx->w[i], from_int32, pws->w[i], 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_UNOP(pwx->d[i], from_int64, pws->d[i], 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} + +void helper_msa_ffint_u_df(CPUMIPSState *env, uint32_t df, uint32_t wd, + uint32_t ws) +{ + wr_t wx, *pwx = &wx; + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + wr_t *pws = &(env->active_fpu.fpr[ws].wr); + uint32_t i; + + clear_msacsr_cause(env); + + switch (df) { + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + MSA_FLOAT_UNOP(pwx->w[i], from_uint32, pws->w[i], 32); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + MSA_FLOAT_UNOP(pwx->d[i], from_uint64, pws->d[i], 64); + } + break; + default: + assert(0); + } + + check_msacsr_cause(env); + + msa_move_v(pwd, pwx); +} diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c index 9ec548cc02..638c9f9dfb 100644 --- a/target-mips/op_helper.c +++ b/target-mips/op_helper.c @@ -90,10 +90,10 @@ static inline type do_##name(CPUMIPSState *env, target_ulong addr, \ } \ } #endif +HELPER_LD(lbu, ldub, uint8_t) +HELPER_LD(lhu, lduw, uint16_t) HELPER_LD(lw, ldl, int32_t) -#ifdef TARGET_MIPS64 HELPER_LD(ld, ldq, int64_t) -#endif #undef HELPER_LD #if defined(CONFIG_USER_ONLY) @@ -118,10 +118,9 @@ static inline void do_##name(CPUMIPSState *env, target_ulong addr, \ } #endif HELPER_ST(sb, stb, uint8_t) +HELPER_ST(sh, stw, uint16_t) HELPER_ST(sw, stl, uint32_t) -#ifdef TARGET_MIPS64 HELPER_ST(sd, stq, uint64_t) -#endif #undef HELPER_ST target_ulong helper_clo (target_ulong arg1) @@ -959,14 +958,14 @@ target_ulong helper_dmfc0_watchlo(CPUMIPSState *env, uint32_t sel) void helper_mtc0_index(CPUMIPSState *env, target_ulong arg1) { - int num = 1; - unsigned int tmp = env->tlb->nb_tlb; - - do { - tmp >>= 1; - num <<= 1; - } while (tmp); - env->CP0_Index = (env->CP0_Index & 0x80000000) | (arg1 & (num - 1)); + uint32_t index_p = env->CP0_Index & 0x80000000; + uint32_t tlb_index = arg1 & 0x7fffffff; + if (tlb_index < env->tlb->nb_tlb) { + if (env->insn_flags & ISA_MIPS32R6) { + index_p |= arg1 & 0x80000000; + } + env->CP0_Index = index_p | tlb_index; + } } void helper_mtc0_mvpcontrol(CPUMIPSState *env, target_ulong arg1) @@ -1099,8 +1098,17 @@ void helper_mtc0_entrylo0(CPUMIPSState *env, target_ulong arg1) { /* Large physaddr (PABITS) not implemented */ /* 1k pages not implemented */ - env->CP0_EntryLo0 = arg1 & 0x3FFFFFFF; + target_ulong rxi = arg1 & (env->CP0_PageGrain & (3u << CP0PG_XIE)); + env->CP0_EntryLo0 = (arg1 & 0x3FFFFFFF) | (rxi << (CP0EnLo_XI - 30)); +} + +#if defined(TARGET_MIPS64) +void helper_dmtc0_entrylo0(CPUMIPSState *env, uint64_t arg1) +{ + uint64_t rxi = arg1 & ((env->CP0_PageGrain & (3ull << CP0PG_XIE)) << 32); + env->CP0_EntryLo0 = (arg1 & 0x3FFFFFFF) | rxi; } +#endif void helper_mtc0_tcstatus(CPUMIPSState *env, target_ulong arg1) { @@ -1266,9 +1274,18 @@ void helper_mtc0_entrylo1(CPUMIPSState *env, target_ulong arg1) { /* Large physaddr (PABITS) not implemented */ /* 1k pages not implemented */ - env->CP0_EntryLo1 = arg1 & 0x3FFFFFFF; + target_ulong rxi = arg1 & (env->CP0_PageGrain & (3u << CP0PG_XIE)); + env->CP0_EntryLo1 = (arg1 & 0x3FFFFFFF) | (rxi << (CP0EnLo_XI - 30)); } +#if defined(TARGET_MIPS64) +void helper_dmtc0_entrylo1(CPUMIPSState *env, uint64_t arg1) +{ + uint64_t rxi = arg1 & ((env->CP0_PageGrain & (3ull << CP0PG_XIE)) << 32); + env->CP0_EntryLo1 = (arg1 & 0x3FFFFFFF) | rxi; +} +#endif + void helper_mtc0_context(CPUMIPSState *env, target_ulong arg1) { env->CP0_Context = (env->CP0_Context & 0x007FFFFF) | (arg1 & ~0x007FFFFF); @@ -1276,8 +1293,13 @@ void helper_mtc0_context(CPUMIPSState *env, target_ulong arg1) void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1) { - /* 1k pages not implemented */ - env->CP0_PageMask = arg1 & (0x1FFFFFFF & (TARGET_PAGE_MASK << 1)); + uint64_t mask = arg1 >> (TARGET_PAGE_BITS + 1); + if (!(env->insn_flags & ISA_MIPS32R6) || (arg1 == ~0) || + (mask == 0x0000 || mask == 0x0003 || mask == 0x000F || + mask == 0x003F || mask == 0x00FF || mask == 0x03FF || + mask == 0x0FFF || mask == 0x3FFF || mask == 0xFFFF)) { + env->CP0_PageMask = arg1 & (0x1FFFFFFF & (TARGET_PAGE_MASK << 1)); + } } void helper_mtc0_pagegrain(CPUMIPSState *env, target_ulong arg1) @@ -1285,12 +1307,19 @@ void helper_mtc0_pagegrain(CPUMIPSState *env, target_ulong arg1) /* SmartMIPS not implemented */ /* Large physaddr (PABITS) not implemented */ /* 1k pages not implemented */ - env->CP0_PageGrain = 0; + env->CP0_PageGrain = (arg1 & env->CP0_PageGrain_rw_bitmask) | + (env->CP0_PageGrain & ~env->CP0_PageGrain_rw_bitmask); } void helper_mtc0_wired(CPUMIPSState *env, target_ulong arg1) { - env->CP0_Wired = arg1 % env->tlb->nb_tlb; + if (env->insn_flags & ISA_MIPS32R6) { + if (arg1 < env->tlb->nb_tlb) { + env->CP0_Wired = arg1; + } + } else { + env->CP0_Wired = arg1 % env->tlb->nb_tlb; + } } void helper_mtc0_srsconf0(CPUMIPSState *env, target_ulong arg1) @@ -1342,14 +1371,28 @@ void helper_mtc0_count(CPUMIPSState *env, target_ulong arg1) void helper_mtc0_entryhi(CPUMIPSState *env, target_ulong arg1) { - target_ulong old, val; + target_ulong old, val, mask; + mask = (TARGET_PAGE_MASK << 1) | 0xFF; + if (((env->CP0_Config4 >> CP0C4_IE) & 0x3) >= 2) { + mask |= 1 << CP0EnHi_EHINV; + } /* 1k pages not implemented */ - val = arg1 & ((TARGET_PAGE_MASK << 1) | 0xFF); #if defined(TARGET_MIPS64) - val &= env->SEGMask; + if (env->insn_flags & ISA_MIPS32R6) { + int entryhi_r = extract64(arg1, 62, 2); + int config0_at = extract32(env->CP0_Config0, 13, 2); + bool no_supervisor = (env->CP0_Status_rw_bitmask & 0x8) == 0; + if ((entryhi_r == 2) || + (entryhi_r == 1 && (no_supervisor || config0_at == 1))) { + /* skip EntryHi.R field if new value is reserved */ + mask &= ~(0x3ull << 62); + } + } + mask &= env->SEGMask; #endif old = env->CP0_EntryHi; + val = (arg1 & mask) | (old & ~mask); env->CP0_EntryHi = val; if (env->CP0_Config3 & (1 << CP0C3_MT)) { sync_c0_entryhi(env, env->current_tc); @@ -1379,6 +1422,13 @@ void helper_mtc0_status(CPUMIPSState *env, target_ulong arg1) uint32_t val, old; uint32_t mask = env->CP0_Status_rw_bitmask; + if (env->insn_flags & ISA_MIPS32R6) { + if (extract32(env->CP0_Status, CP0St_KSU, 2) == 0x3) { + mask &= ~(3 << CP0St_KSU); + } + mask &= ~(0x00180000 & arg1); + } + val = arg1 & mask; old = env->CP0_Status; env->CP0_Status = (env->CP0_Status & ~mask) | val; @@ -1434,6 +1484,9 @@ static void mtc0_cause(CPUMIPSState *cpu, target_ulong arg1) if (cpu->insn_flags & ISA_MIPS32R2) { mask |= 1 << CP0Ca_DC; } + if (cpu->insn_flags & ISA_MIPS32R6) { + mask &= ~((1 << CP0Ca_WP) & arg1); + } cpu->CP0_Cause = (cpu->CP0_Cause & ~mask) | (arg1 & mask); @@ -1535,6 +1588,7 @@ void helper_mtc0_config5(CPUMIPSState *env, target_ulong arg1) { env->CP0_Config5 = (env->CP0_Config5 & (~env->CP0_Config5_rw_bitmask)) | (arg1 & env->CP0_Config5_rw_bitmask); + compute_hflags(env); } void helper_mtc0_lladdr(CPUMIPSState *env, target_ulong arg1) @@ -1839,6 +1893,11 @@ static void r4k_fill_tlb(CPUMIPSState *env, int idx) /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */ tlb = &env->tlb->mmu.r4k.tlb[idx]; + if (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) { + tlb->EHINV = 1; + return; + } + tlb->EHINV = 0; tlb->VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1); #if defined(TARGET_MIPS64) tlb->VPN &= env->SEGMask; @@ -1849,13 +1908,42 @@ static void r4k_fill_tlb(CPUMIPSState *env, int idx) tlb->V0 = (env->CP0_EntryLo0 & 2) != 0; tlb->D0 = (env->CP0_EntryLo0 & 4) != 0; tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7; + tlb->XI0 = (env->CP0_EntryLo0 >> CP0EnLo_XI) & 1; + tlb->RI0 = (env->CP0_EntryLo0 >> CP0EnLo_RI) & 1; tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12; tlb->V1 = (env->CP0_EntryLo1 & 2) != 0; tlb->D1 = (env->CP0_EntryLo1 & 4) != 0; tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7; + tlb->XI1 = (env->CP0_EntryLo1 >> CP0EnLo_XI) & 1; + tlb->RI1 = (env->CP0_EntryLo1 >> CP0EnLo_RI) & 1; tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12; } +void r4k_helper_tlbinv(CPUMIPSState *env) +{ + int idx; + r4k_tlb_t *tlb; + uint8_t ASID = env->CP0_EntryHi & 0xFF; + + for (idx = 0; idx < env->tlb->nb_tlb; idx++) { + tlb = &env->tlb->mmu.r4k.tlb[idx]; + if (!tlb->G && tlb->ASID == ASID) { + tlb->EHINV = 1; + } + } + cpu_mips_tlb_flush(env, 1); +} + +void r4k_helper_tlbinvf(CPUMIPSState *env) +{ + int idx; + + for (idx = 0; idx < env->tlb->nb_tlb; idx++) { + env->tlb->mmu.r4k.tlb[idx].EHINV = 1; + } + cpu_mips_tlb_flush(env, 1); +} + void r4k_helper_tlbwi(CPUMIPSState *env) { r4k_tlb_t *tlb; @@ -1917,7 +2005,7 @@ void r4k_helper_tlbp(CPUMIPSState *env) tag &= env->SEGMask; #endif /* Check ASID, virtual page number & size */ - if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) { + if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag && !tlb->EHINV) { /* TLB match */ env->CP0_Index = i; break; @@ -1961,12 +2049,23 @@ void r4k_helper_tlbr(CPUMIPSState *env) r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb); - env->CP0_EntryHi = tlb->VPN | tlb->ASID; - env->CP0_PageMask = tlb->PageMask; - env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) | + if (tlb->EHINV) { + env->CP0_EntryHi = 1 << CP0EnHi_EHINV; + env->CP0_PageMask = 0; + env->CP0_EntryLo0 = 0; + env->CP0_EntryLo1 = 0; + } else { + env->CP0_EntryHi = tlb->VPN | tlb->ASID; + env->CP0_PageMask = tlb->PageMask; + env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) | + ((target_ulong)tlb->RI0 << CP0EnLo_RI) | + ((target_ulong)tlb->XI0 << CP0EnLo_XI) | (tlb->C0 << 3) | (tlb->PFN[0] >> 6); - env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) | + env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) | + ((target_ulong)tlb->RI1 << CP0EnLo_RI) | + ((target_ulong)tlb->XI1 << CP0EnLo_XI) | (tlb->C1 << 3) | (tlb->PFN[1] >> 6); + } } void helper_tlbwi(CPUMIPSState *env) @@ -1989,6 +2088,16 @@ void helper_tlbr(CPUMIPSState *env) env->tlb->helper_tlbr(env); } +void helper_tlbinv(CPUMIPSState *env) +{ + env->tlb->helper_tlbinv(env); +} + +void helper_tlbinvf(CPUMIPSState *env) +{ + env->tlb->helper_tlbinvf(env); +} + /* Specials */ target_ulong helper_di(CPUMIPSState *env) { @@ -2160,13 +2269,26 @@ void helper_wait(CPUMIPSState *env) #if !defined(CONFIG_USER_ONLY) void mips_cpu_do_unaligned_access(CPUState *cs, vaddr addr, - int is_write, int is_user, uintptr_t retaddr) + int access_type, int is_user, + uintptr_t retaddr) { MIPSCPU *cpu = MIPS_CPU(cs); CPUMIPSState *env = &cpu->env; + int error_code = 0; + int excp; env->CP0_BadVAddr = addr; - do_raise_exception(env, (is_write == 1) ? EXCP_AdES : EXCP_AdEL, retaddr); + + if (access_type == MMU_DATA_STORE) { + excp = EXCP_AdES; + } else { + excp = EXCP_AdEL; + if (access_type == MMU_INST_FETCH) { + error_code |= EXCP_INST_NOTAVAIL; + } + } + + do_raise_exception_err(env, excp, error_code, retaddr); } void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx, @@ -2217,7 +2339,7 @@ void mips_cpu_unassigned_access(CPUState *cs, hwaddr addr, #define FP_TO_INT64_OVERFLOW 0x7fffffffffffffffULL /* convert MIPS rounding mode in FCR31 to IEEE library */ -static unsigned int ieee_rm[] = { +unsigned int ieee_rm[] = { float_round_nearest_even, float_round_to_zero, float_round_up, @@ -2300,8 +2422,9 @@ void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt) } break; case 25: - if (arg1 & 0xffffff00) + if ((env->insn_flags & ISA_MIPS32R6) || (arg1 & 0xffffff00)) { return; + } env->active_fpu.fcr31 = (env->active_fpu.fcr31 & 0x017fffff) | ((arg1 & 0xfe) << 24) | ((arg1 & 0x1) << 23); break; @@ -2317,9 +2440,13 @@ void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt) ((arg1 & 0x4) << 22); break; case 31: - if (arg1 & 0x007c0000) - return; - env->active_fpu.fcr31 = arg1; + if (env->insn_flags & ISA_MIPS32R6) { + uint32_t mask = 0xfefc0000; + env->active_fpu.fcr31 = (arg1 & ~mask) | + (env->active_fpu.fcr31 & mask); + } else if (!(arg1 & 0x007c0000)) { + env->active_fpu.fcr31 = arg1; + } break; default: return; @@ -2333,7 +2460,7 @@ void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt) do_raise_exception(env, EXCP_FPE, GETPC()); } -static inline int ieee_ex_to_mips(int xcpt) +int ieee_ex_to_mips(int xcpt) { int ret = 0; if (xcpt) { @@ -3498,3 +3625,80 @@ FOP_CONDN_S(sune, (float32_unordered(fst1, fst0, &env->active_fpu.fp_status) || float32_lt(fst0, fst1, &env->active_fpu.fp_status))) FOP_CONDN_S(sne, (float32_lt(fst1, fst0, &env->active_fpu.fp_status) || float32_lt(fst0, fst1, &env->active_fpu.fp_status))) + +/* MSA */ +/* Data format min and max values */ +#define DF_BITS(df) (1 << ((df) + 3)) + +/* Element-by-element access macros */ +#define DF_ELEMENTS(df) (MSA_WRLEN / DF_BITS(df)) + +void helper_msa_ld_df(CPUMIPSState *env, uint32_t df, uint32_t wd, uint32_t rs, + int32_t s10) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + target_ulong addr = env->active_tc.gpr[rs] + (s10 << df); + int i; + + switch (df) { + case DF_BYTE: + for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) { + pwd->b[i] = do_lbu(env, addr + (i << DF_BYTE), + env->hflags & MIPS_HFLAG_KSU); + } + break; + case DF_HALF: + for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) { + pwd->h[i] = do_lhu(env, addr + (i << DF_HALF), + env->hflags & MIPS_HFLAG_KSU); + } + break; + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + pwd->w[i] = do_lw(env, addr + (i << DF_WORD), + env->hflags & MIPS_HFLAG_KSU); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + pwd->d[i] = do_ld(env, addr + (i << DF_DOUBLE), + env->hflags & MIPS_HFLAG_KSU); + } + break; + } +} + +void helper_msa_st_df(CPUMIPSState *env, uint32_t df, uint32_t wd, uint32_t rs, + int32_t s10) +{ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); + target_ulong addr = env->active_tc.gpr[rs] + (s10 << df); + int i; + + switch (df) { + case DF_BYTE: + for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) { + do_sb(env, addr + (i << DF_BYTE), pwd->b[i], + env->hflags & MIPS_HFLAG_KSU); + } + break; + case DF_HALF: + for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) { + do_sh(env, addr + (i << DF_HALF), pwd->h[i], + env->hflags & MIPS_HFLAG_KSU); + } + break; + case DF_WORD: + for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { + do_sw(env, addr + (i << DF_WORD), pwd->w[i], + env->hflags & MIPS_HFLAG_KSU); + } + break; + case DF_DOUBLE: + for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { + do_sd(env, addr + (i << DF_DOUBLE), pwd->d[i], + env->hflags & MIPS_HFLAG_KSU); + } + break; + } +} diff --git a/target-mips/translate.c b/target-mips/translate.c index 446eb8a20f..f0b8e6ffe4 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -137,6 +137,8 @@ enum { OPC_JIALC = (0x3E << 26), /* MDMX ASE specific */ OPC_MDMX = (0x1E << 26), + /* MSA ASE, same as MDMX */ + OPC_MSA = OPC_MDMX, /* Cache and prefetch */ OPC_CACHE = (0x2F << 26), OPC_PREF = (0x33 << 26), @@ -896,6 +898,8 @@ enum { enum { OPC_TLBR = 0x01 | OPC_C0, OPC_TLBWI = 0x02 | OPC_C0, + OPC_TLBINV = 0x03 | OPC_C0, + OPC_TLBINVF = 0x04 | OPC_C0, OPC_TLBWR = 0x06 | OPC_C0, OPC_TLBP = 0x08 | OPC_C0, OPC_RFE = 0x10 | OPC_C0, @@ -932,6 +936,8 @@ enum { OPC_BC1 = (0x08 << 21) | OPC_CP1, /* bc */ OPC_BC1ANY2 = (0x09 << 21) | OPC_CP1, OPC_BC1ANY4 = (0x0A << 21) | OPC_CP1, + OPC_BZ_V = (0x0B << 21) | OPC_CP1, + OPC_BNZ_V = (0x0F << 21) | OPC_CP1, OPC_S_FMT = (FMT_S << 21) | OPC_CP1, OPC_D_FMT = (FMT_D << 21) | OPC_CP1, OPC_E_FMT = (FMT_E << 21) | OPC_CP1, @@ -941,6 +947,14 @@ enum { OPC_PS_FMT = (FMT_PS << 21) | OPC_CP1, OPC_BC1EQZ = (0x09 << 21) | OPC_CP1, OPC_BC1NEZ = (0x0D << 21) | OPC_CP1, + OPC_BZ_B = (0x18 << 21) | OPC_CP1, + OPC_BZ_H = (0x19 << 21) | OPC_CP1, + OPC_BZ_W = (0x1A << 21) | OPC_CP1, + OPC_BZ_D = (0x1B << 21) | OPC_CP1, + OPC_BNZ_B = (0x1C << 21) | OPC_CP1, + OPC_BNZ_H = (0x1D << 21) | OPC_CP1, + OPC_BNZ_W = (0x1E << 21) | OPC_CP1, + OPC_BNZ_D = (0x1F << 21) | OPC_CP1, }; #define MASK_CP1_FUNC(op) MASK_CP1(op) | (op & 0x3F) @@ -1101,6 +1115,239 @@ enum { OPC_NMSUB_PS= 0x3E | OPC_CP3, }; +/* MSA Opcodes */ +#define MASK_MSA_MINOR(op) (MASK_OP_MAJOR(op) | (op & 0x3F)) +enum { + OPC_MSA_I8_00 = 0x00 | OPC_MSA, + OPC_MSA_I8_01 = 0x01 | OPC_MSA, + OPC_MSA_I8_02 = 0x02 | OPC_MSA, + OPC_MSA_I5_06 = 0x06 | OPC_MSA, + OPC_MSA_I5_07 = 0x07 | OPC_MSA, + OPC_MSA_BIT_09 = 0x09 | OPC_MSA, + OPC_MSA_BIT_0A = 0x0A | OPC_MSA, + OPC_MSA_3R_0D = 0x0D | OPC_MSA, + OPC_MSA_3R_0E = 0x0E | OPC_MSA, + OPC_MSA_3R_0F = 0x0F | OPC_MSA, + OPC_MSA_3R_10 = 0x10 | OPC_MSA, + OPC_MSA_3R_11 = 0x11 | OPC_MSA, + OPC_MSA_3R_12 = 0x12 | OPC_MSA, + OPC_MSA_3R_13 = 0x13 | OPC_MSA, + OPC_MSA_3R_14 = 0x14 | OPC_MSA, + OPC_MSA_3R_15 = 0x15 | OPC_MSA, + OPC_MSA_ELM = 0x19 | OPC_MSA, + OPC_MSA_3RF_1A = 0x1A | OPC_MSA, + OPC_MSA_3RF_1B = 0x1B | OPC_MSA, + OPC_MSA_3RF_1C = 0x1C | OPC_MSA, + OPC_MSA_VEC = 0x1E | OPC_MSA, + + /* MI10 instruction */ + OPC_LD_B = (0x20) | OPC_MSA, + OPC_LD_H = (0x21) | OPC_MSA, + OPC_LD_W = (0x22) | OPC_MSA, + OPC_LD_D = (0x23) | OPC_MSA, + OPC_ST_B = (0x24) | OPC_MSA, + OPC_ST_H = (0x25) | OPC_MSA, + OPC_ST_W = (0x26) | OPC_MSA, + OPC_ST_D = (0x27) | OPC_MSA, +}; + +enum { + /* I5 instruction df(bits 22..21) = _b, _h, _w, _d */ + OPC_ADDVI_df = (0x0 << 23) | OPC_MSA_I5_06, + OPC_CEQI_df = (0x0 << 23) | OPC_MSA_I5_07, + OPC_SUBVI_df = (0x1 << 23) | OPC_MSA_I5_06, + OPC_MAXI_S_df = (0x2 << 23) | OPC_MSA_I5_06, + OPC_CLTI_S_df = (0x2 << 23) | OPC_MSA_I5_07, + OPC_MAXI_U_df = (0x3 << 23) | OPC_MSA_I5_06, + OPC_CLTI_U_df = (0x3 << 23) | OPC_MSA_I5_07, + OPC_MINI_S_df = (0x4 << 23) | OPC_MSA_I5_06, + OPC_CLEI_S_df = (0x4 << 23) | OPC_MSA_I5_07, + OPC_MINI_U_df = (0x5 << 23) | OPC_MSA_I5_06, + OPC_CLEI_U_df = (0x5 << 23) | OPC_MSA_I5_07, + OPC_LDI_df = (0x6 << 23) | OPC_MSA_I5_07, + + /* I8 instruction */ + OPC_ANDI_B = (0x0 << 24) | OPC_MSA_I8_00, + OPC_BMNZI_B = (0x0 << 24) | OPC_MSA_I8_01, + OPC_SHF_B = (0x0 << 24) | OPC_MSA_I8_02, + OPC_ORI_B = (0x1 << 24) | OPC_MSA_I8_00, + OPC_BMZI_B = (0x1 << 24) | OPC_MSA_I8_01, + OPC_SHF_H = (0x1 << 24) | OPC_MSA_I8_02, + OPC_NORI_B = (0x2 << 24) | OPC_MSA_I8_00, + OPC_BSELI_B = (0x2 << 24) | OPC_MSA_I8_01, + OPC_SHF_W = (0x2 << 24) | OPC_MSA_I8_02, + OPC_XORI_B = (0x3 << 24) | OPC_MSA_I8_00, + + /* VEC/2R/2RF instruction */ + OPC_AND_V = (0x00 << 21) | OPC_MSA_VEC, + OPC_OR_V = (0x01 << 21) | OPC_MSA_VEC, + OPC_NOR_V = (0x02 << 21) | OPC_MSA_VEC, + OPC_XOR_V = (0x03 << 21) | OPC_MSA_VEC, + OPC_BMNZ_V = (0x04 << 21) | OPC_MSA_VEC, + OPC_BMZ_V = (0x05 << 21) | OPC_MSA_VEC, + OPC_BSEL_V = (0x06 << 21) | OPC_MSA_VEC, + + OPC_MSA_2R = (0x18 << 21) | OPC_MSA_VEC, + OPC_MSA_2RF = (0x19 << 21) | OPC_MSA_VEC, + + /* 2R instruction df(bits 17..16) = _b, _h, _w, _d */ + OPC_FILL_df = (0x00 << 18) | OPC_MSA_2R, + OPC_PCNT_df = (0x01 << 18) | OPC_MSA_2R, + OPC_NLOC_df = (0x02 << 18) | OPC_MSA_2R, + OPC_NLZC_df = (0x03 << 18) | OPC_MSA_2R, + + /* 2RF instruction df(bit 16) = _w, _d */ + OPC_FCLASS_df = (0x00 << 17) | OPC_MSA_2RF, + OPC_FTRUNC_S_df = (0x01 << 17) | OPC_MSA_2RF, + OPC_FTRUNC_U_df = (0x02 << 17) | OPC_MSA_2RF, + OPC_FSQRT_df = (0x03 << 17) | OPC_MSA_2RF, + OPC_FRSQRT_df = (0x04 << 17) | OPC_MSA_2RF, + OPC_FRCP_df = (0x05 << 17) | OPC_MSA_2RF, + OPC_FRINT_df = (0x06 << 17) | OPC_MSA_2RF, + OPC_FLOG2_df = (0x07 << 17) | OPC_MSA_2RF, + OPC_FEXUPL_df = (0x08 << 17) | OPC_MSA_2RF, + OPC_FEXUPR_df = (0x09 << 17) | OPC_MSA_2RF, + OPC_FFQL_df = (0x0A << 17) | OPC_MSA_2RF, + OPC_FFQR_df = (0x0B << 17) | OPC_MSA_2RF, + OPC_FTINT_S_df = (0x0C << 17) | OPC_MSA_2RF, + OPC_FTINT_U_df = (0x0D << 17) | OPC_MSA_2RF, + OPC_FFINT_S_df = (0x0E << 17) | OPC_MSA_2RF, + OPC_FFINT_U_df = (0x0F << 17) | OPC_MSA_2RF, + + /* 3R instruction df(bits 22..21) = _b, _h, _w, d */ + OPC_SLL_df = (0x0 << 23) | OPC_MSA_3R_0D, + OPC_ADDV_df = (0x0 << 23) | OPC_MSA_3R_0E, + OPC_CEQ_df = (0x0 << 23) | OPC_MSA_3R_0F, + OPC_ADD_A_df = (0x0 << 23) | OPC_MSA_3R_10, + OPC_SUBS_S_df = (0x0 << 23) | OPC_MSA_3R_11, + OPC_MULV_df = (0x0 << 23) | OPC_MSA_3R_12, + OPC_DOTP_S_df = (0x0 << 23) | OPC_MSA_3R_13, + OPC_SLD_df = (0x0 << 23) | OPC_MSA_3R_14, + OPC_VSHF_df = (0x0 << 23) | OPC_MSA_3R_15, + OPC_SRA_df = (0x1 << 23) | OPC_MSA_3R_0D, + OPC_SUBV_df = (0x1 << 23) | OPC_MSA_3R_0E, + OPC_ADDS_A_df = (0x1 << 23) | OPC_MSA_3R_10, + OPC_SUBS_U_df = (0x1 << 23) | OPC_MSA_3R_11, + OPC_MADDV_df = (0x1 << 23) | OPC_MSA_3R_12, + OPC_DOTP_U_df = (0x1 << 23) | OPC_MSA_3R_13, + OPC_SPLAT_df = (0x1 << 23) | OPC_MSA_3R_14, + OPC_SRAR_df = (0x1 << 23) | OPC_MSA_3R_15, + OPC_SRL_df = (0x2 << 23) | OPC_MSA_3R_0D, + OPC_MAX_S_df = (0x2 << 23) | OPC_MSA_3R_0E, + OPC_CLT_S_df = (0x2 << 23) | OPC_MSA_3R_0F, + OPC_ADDS_S_df = (0x2 << 23) | OPC_MSA_3R_10, + OPC_SUBSUS_U_df = (0x2 << 23) | OPC_MSA_3R_11, + OPC_MSUBV_df = (0x2 << 23) | OPC_MSA_3R_12, + OPC_DPADD_S_df = (0x2 << 23) | OPC_MSA_3R_13, + OPC_PCKEV_df = (0x2 << 23) | OPC_MSA_3R_14, + OPC_SRLR_df = (0x2 << 23) | OPC_MSA_3R_15, + OPC_BCLR_df = (0x3 << 23) | OPC_MSA_3R_0D, + OPC_MAX_U_df = (0x3 << 23) | OPC_MSA_3R_0E, + OPC_CLT_U_df = (0x3 << 23) | OPC_MSA_3R_0F, + OPC_ADDS_U_df = (0x3 << 23) | OPC_MSA_3R_10, + OPC_SUBSUU_S_df = (0x3 << 23) | OPC_MSA_3R_11, + OPC_DPADD_U_df = (0x3 << 23) | OPC_MSA_3R_13, + OPC_PCKOD_df = (0x3 << 23) | OPC_MSA_3R_14, + OPC_BSET_df = (0x4 << 23) | OPC_MSA_3R_0D, + OPC_MIN_S_df = (0x4 << 23) | OPC_MSA_3R_0E, + OPC_CLE_S_df = (0x4 << 23) | OPC_MSA_3R_0F, + OPC_AVE_S_df = (0x4 << 23) | OPC_MSA_3R_10, + OPC_ASUB_S_df = (0x4 << 23) | OPC_MSA_3R_11, + OPC_DIV_S_df = (0x4 << 23) | OPC_MSA_3R_12, + OPC_DPSUB_S_df = (0x4 << 23) | OPC_MSA_3R_13, + OPC_ILVL_df = (0x4 << 23) | OPC_MSA_3R_14, + OPC_HADD_S_df = (0x4 << 23) | OPC_MSA_3R_15, + OPC_BNEG_df = (0x5 << 23) | OPC_MSA_3R_0D, + OPC_MIN_U_df = (0x5 << 23) | OPC_MSA_3R_0E, + OPC_CLE_U_df = (0x5 << 23) | OPC_MSA_3R_0F, + OPC_AVE_U_df = (0x5 << 23) | OPC_MSA_3R_10, + OPC_ASUB_U_df = (0x5 << 23) | OPC_MSA_3R_11, + OPC_DIV_U_df = (0x5 << 23) | OPC_MSA_3R_12, + OPC_DPSUB_U_df = (0x5 << 23) | OPC_MSA_3R_13, + OPC_ILVR_df = (0x5 << 23) | OPC_MSA_3R_14, + OPC_HADD_U_df = (0x5 << 23) | OPC_MSA_3R_15, + OPC_BINSL_df = (0x6 << 23) | OPC_MSA_3R_0D, + OPC_MAX_A_df = (0x6 << 23) | OPC_MSA_3R_0E, + OPC_AVER_S_df = (0x6 << 23) | OPC_MSA_3R_10, + OPC_MOD_S_df = (0x6 << 23) | OPC_MSA_3R_12, + OPC_ILVEV_df = (0x6 << 23) | OPC_MSA_3R_14, + OPC_HSUB_S_df = (0x6 << 23) | OPC_MSA_3R_15, + OPC_BINSR_df = (0x7 << 23) | OPC_MSA_3R_0D, + OPC_MIN_A_df = (0x7 << 23) | OPC_MSA_3R_0E, + OPC_AVER_U_df = (0x7 << 23) | OPC_MSA_3R_10, + OPC_MOD_U_df = (0x7 << 23) | OPC_MSA_3R_12, + OPC_ILVOD_df = (0x7 << 23) | OPC_MSA_3R_14, + OPC_HSUB_U_df = (0x7 << 23) | OPC_MSA_3R_15, + + /* ELM instructions df(bits 21..16) = _b, _h, _w, _d */ + OPC_SLDI_df = (0x0 << 22) | (0x00 << 16) | OPC_MSA_ELM, + OPC_CTCMSA = (0x0 << 22) | (0x3E << 16) | OPC_MSA_ELM, + OPC_SPLATI_df = (0x1 << 22) | (0x00 << 16) | OPC_MSA_ELM, + OPC_CFCMSA = (0x1 << 22) | (0x3E << 16) | OPC_MSA_ELM, + OPC_COPY_S_df = (0x2 << 22) | (0x00 << 16) | OPC_MSA_ELM, + OPC_MOVE_V = (0x2 << 22) | (0x3E << 16) | OPC_MSA_ELM, + OPC_COPY_U_df = (0x3 << 22) | (0x00 << 16) | OPC_MSA_ELM, + OPC_INSERT_df = (0x4 << 22) | (0x00 << 16) | OPC_MSA_ELM, + OPC_INSVE_df = (0x5 << 22) | (0x00 << 16) | OPC_MSA_ELM, + + /* 3RF instruction _df(bit 21) = _w, _d */ + OPC_FCAF_df = (0x0 << 22) | OPC_MSA_3RF_1A, + OPC_FADD_df = (0x0 << 22) | OPC_MSA_3RF_1B, + OPC_FCUN_df = (0x1 << 22) | OPC_MSA_3RF_1A, + OPC_FSUB_df = (0x1 << 22) | OPC_MSA_3RF_1B, + OPC_FCOR_df = (0x1 << 22) | OPC_MSA_3RF_1C, + OPC_FCEQ_df = (0x2 << 22) | OPC_MSA_3RF_1A, + OPC_FMUL_df = (0x2 << 22) | OPC_MSA_3RF_1B, + OPC_FCUNE_df = (0x2 << 22) | OPC_MSA_3RF_1C, + OPC_FCUEQ_df = (0x3 << 22) | OPC_MSA_3RF_1A, + OPC_FDIV_df = (0x3 << 22) | OPC_MSA_3RF_1B, + OPC_FCNE_df = (0x3 << 22) | OPC_MSA_3RF_1C, + OPC_FCLT_df = (0x4 << 22) | OPC_MSA_3RF_1A, + OPC_FMADD_df = (0x4 << 22) | OPC_MSA_3RF_1B, + OPC_MUL_Q_df = (0x4 << 22) | OPC_MSA_3RF_1C, + OPC_FCULT_df = (0x5 << 22) | OPC_MSA_3RF_1A, + OPC_FMSUB_df = (0x5 << 22) | OPC_MSA_3RF_1B, + OPC_MADD_Q_df = (0x5 << 22) | OPC_MSA_3RF_1C, + OPC_FCLE_df = (0x6 << 22) | OPC_MSA_3RF_1A, + OPC_MSUB_Q_df = (0x6 << 22) | OPC_MSA_3RF_1C, + OPC_FCULE_df = (0x7 << 22) | OPC_MSA_3RF_1A, + OPC_FEXP2_df = (0x7 << 22) | OPC_MSA_3RF_1B, + OPC_FSAF_df = (0x8 << 22) | OPC_MSA_3RF_1A, + OPC_FEXDO_df = (0x8 << 22) | OPC_MSA_3RF_1B, + OPC_FSUN_df = (0x9 << 22) | OPC_MSA_3RF_1A, + OPC_FSOR_df = (0x9 << 22) | OPC_MSA_3RF_1C, + OPC_FSEQ_df = (0xA << 22) | OPC_MSA_3RF_1A, + OPC_FTQ_df = (0xA << 22) | OPC_MSA_3RF_1B, + OPC_FSUNE_df = (0xA << 22) | OPC_MSA_3RF_1C, + OPC_FSUEQ_df = (0xB << 22) | OPC_MSA_3RF_1A, + OPC_FSNE_df = (0xB << 22) | OPC_MSA_3RF_1C, + OPC_FSLT_df = (0xC << 22) | OPC_MSA_3RF_1A, + OPC_FMIN_df = (0xC << 22) | OPC_MSA_3RF_1B, + OPC_MULR_Q_df = (0xC << 22) | OPC_MSA_3RF_1C, + OPC_FSULT_df = (0xD << 22) | OPC_MSA_3RF_1A, + OPC_FMIN_A_df = (0xD << 22) | OPC_MSA_3RF_1B, + OPC_MADDR_Q_df = (0xD << 22) | OPC_MSA_3RF_1C, + OPC_FSLE_df = (0xE << 22) | OPC_MSA_3RF_1A, + OPC_FMAX_df = (0xE << 22) | OPC_MSA_3RF_1B, + OPC_MSUBR_Q_df = (0xE << 22) | OPC_MSA_3RF_1C, + OPC_FSULE_df = (0xF << 22) | OPC_MSA_3RF_1A, + OPC_FMAX_A_df = (0xF << 22) | OPC_MSA_3RF_1B, + + /* BIT instruction df(bits 22..16) = _B _H _W _D */ + OPC_SLLI_df = (0x0 << 23) | OPC_MSA_BIT_09, + OPC_SAT_S_df = (0x0 << 23) | OPC_MSA_BIT_0A, + OPC_SRAI_df = (0x1 << 23) | OPC_MSA_BIT_09, + OPC_SAT_U_df = (0x1 << 23) | OPC_MSA_BIT_0A, + OPC_SRLI_df = (0x2 << 23) | OPC_MSA_BIT_09, + OPC_SRARI_df = (0x2 << 23) | OPC_MSA_BIT_0A, + OPC_BCLRI_df = (0x3 << 23) | OPC_MSA_BIT_09, + OPC_SRLRI_df = (0x3 << 23) | OPC_MSA_BIT_0A, + OPC_BSETI_df = (0x4 << 23) | OPC_MSA_BIT_09, + OPC_BNEGI_df = (0x5 << 23) | OPC_MSA_BIT_09, + OPC_BINSLI_df = (0x6 << 23) | OPC_MSA_BIT_09, + OPC_BINSRI_df = (0x7 << 23) | OPC_MSA_BIT_09, +}; + /* global register indices */ static TCGv_ptr cpu_env; static TCGv cpu_gpr[32], cpu_PC; @@ -1109,6 +1356,7 @@ static TCGv cpu_dspctrl, btarget, bcond; static TCGv_i32 hflags; static TCGv_i32 fpu_fcr0, fpu_fcr31; static TCGv_i64 fpu_f64[32]; +static TCGv_i64 msa_wr_d[64]; static uint32_t gen_opc_hflags[OPC_BUF_SIZE]; static target_ulong gen_opc_btarget[OPC_BUF_SIZE]; @@ -1170,6 +1418,11 @@ typedef struct DisasContext { int bstate; target_ulong btarget; bool ulri; + int kscrexist; + bool rxi; + int ie; + bool bi; + bool bp; } DisasContext; enum { @@ -1202,6 +1455,25 @@ static const char * const fregnames[] = { "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", }; +static const char * const msaregnames[] = { + "w0.d0", "w0.d1", "w1.d0", "w1.d1", + "w2.d0", "w2.d1", "w3.d0", "w3.d1", + "w4.d0", "w4.d1", "w5.d0", "w5.d1", + "w6.d0", "w6.d1", "w7.d0", "w7.d1", + "w8.d0", "w8.d1", "w9.d0", "w9.d1", + "w10.d0", "w10.d1", "w11.d0", "w11.d1", + "w12.d0", "w12.d1", "w13.d0", "w13.d1", + "w14.d0", "w14.d1", "w15.d0", "w15.d1", + "w16.d0", "w16.d1", "w17.d0", "w17.d1", + "w18.d0", "w18.d1", "w19.d0", "w19.d1", + "w20.d0", "w20.d1", "w21.d0", "w21.d1", + "w22.d0", "w22.d1", "w23.d0", "w23.d1", + "w24.d0", "w24.d1", "w25.d0", "w25.d1", + "w26.d0", "w26.d1", "w27.d0", "w27.d1", + "w28.d0", "w28.d1", "w29.d0", "w29.d1", + "w30.d0", "w30.d1", "w31.d0", "w31.d1", +}; + #define MIPS_DEBUG(fmt, ...) \ do { \ if (MIPS_DEBUG_DISAS) { \ @@ -4129,7 +4401,8 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, if (ctx->hflags & MIPS_HFLAG_BMASK) { #ifdef MIPS_DEBUG_DISAS - LOG_DISAS("Branch in delay slot at PC 0x" TARGET_FMT_lx "\n", ctx->pc); + LOG_DISAS("Branch in delay / forbidden slot at PC 0x" + TARGET_FMT_lx "\n", ctx->pc); #endif generate_exception(ctx, EXCP_RI); goto out; @@ -4567,6 +4840,22 @@ static inline void gen_mtc0_store64 (TCGv arg, target_ulong off) tcg_gen_st_tl(arg, cpu_env, off); } +static inline void gen_mfc0_unimplemented(DisasContext *ctx, TCGv arg) +{ + if (ctx->insn_flags & ISA_MIPS32R6) { + tcg_gen_movi_tl(arg, 0); + } else { + tcg_gen_movi_tl(arg, ~0); + } +} + +#define CP0_CHECK(c) \ + do { \ + if (!(c)) { \ + goto cp0_unimplemented; \ + } \ + } while (0) + static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) { const char *rn = "invalid"; @@ -4582,124 +4871,143 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "Index"; break; case 1: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_mvpcontrol(arg, cpu_env); rn = "MVPControl"; break; case 2: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_mvpconf0(arg, cpu_env); rn = "MVPConf0"; break; case 3: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_mvpconf1(arg, cpu_env); rn = "MVPConf1"; break; default: - goto die; + goto cp0_unimplemented; } break; case 1: switch (sel) { case 0: + CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6)); gen_helper_mfc0_random(arg, cpu_env); rn = "Random"; break; case 1: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEControl)); rn = "VPEControl"; break; case 2: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf0)); rn = "VPEConf0"; break; case 3: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf1)); rn = "VPEConf1"; break; case 4: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_YQMask)); rn = "YQMask"; break; case 5: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_VPESchedule)); rn = "VPESchedule"; break; case 6: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_VPEScheFBack)); rn = "VPEScheFBack"; break; case 7: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEOpt)); rn = "VPEOpt"; break; default: - goto die; + goto cp0_unimplemented; } break; case 2: switch (sel) { case 0: tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryLo0)); +#if defined(TARGET_MIPS64) + if (ctx->rxi) { + TCGv tmp = tcg_temp_new(); + tcg_gen_andi_tl(tmp, arg, (3ull << 62)); + tcg_gen_shri_tl(tmp, tmp, 32); + tcg_gen_or_tl(arg, arg, tmp); + tcg_temp_free(tmp); + } +#endif tcg_gen_ext32s_tl(arg, arg); rn = "EntryLo0"; break; case 1: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_tcstatus(arg, cpu_env); rn = "TCStatus"; break; case 2: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_tcbind(arg, cpu_env); rn = "TCBind"; break; case 3: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_tcrestart(arg, cpu_env); rn = "TCRestart"; break; case 4: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_tchalt(arg, cpu_env); rn = "TCHalt"; break; case 5: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_tccontext(arg, cpu_env); rn = "TCContext"; break; case 6: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_tcschedule(arg, cpu_env); rn = "TCSchedule"; break; case 7: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_tcschefback(arg, cpu_env); rn = "TCScheFBack"; break; default: - goto die; + goto cp0_unimplemented; } break; case 3: switch (sel) { case 0: tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryLo1)); +#if defined(TARGET_MIPS64) + if (ctx->rxi) { + TCGv tmp = tcg_temp_new(); + tcg_gen_andi_tl(tmp, arg, (3ull << 62)); + tcg_gen_shri_tl(tmp, tmp, 32); + tcg_gen_or_tl(arg, arg, tmp); + tcg_temp_free(tmp); + } +#endif tcg_gen_ext32s_tl(arg, arg); rn = "EntryLo1"; break; default: - goto die; + goto cp0_unimplemented; } break; case 4: @@ -4712,20 +5020,16 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 1: // gen_helper_mfc0_contextconfig(arg); /* SmartMIPS ASE */ rn = "ContextConfig"; - goto die; + goto cp0_unimplemented; // break; case 2: - if (ctx->ulri) { - tcg_gen_ld32s_tl(arg, cpu_env, - offsetof(CPUMIPSState, - active_tc.CP0_UserLocal)); - rn = "UserLocal"; - } else { - tcg_gen_movi_tl(arg, 0); - } + CP0_CHECK(ctx->ulri); + tcg_gen_ld32s_tl(arg, cpu_env, + offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); + rn = "UserLocal"; break; default: - goto die; + goto cp0_unimplemented; } break; case 5: @@ -4740,7 +5044,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "PageGrain"; break; default: - goto die; + goto cp0_unimplemented; } break; case 6: @@ -4775,7 +5079,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "SRSConf4"; break; default: - goto die; + goto cp0_unimplemented; } break; case 7: @@ -4786,7 +5090,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "HWREna"; break; default: - goto die; + goto cp0_unimplemented; } break; case 8: @@ -4796,9 +5100,19 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) tcg_gen_ext32s_tl(arg, arg); rn = "BadVAddr"; break; + case 1: + CP0_CHECK(ctx->bi); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstr)); + rn = "BadInstr"; + break; + case 2: + CP0_CHECK(ctx->bp); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstrP)); + rn = "BadInstrP"; + break; default: - goto die; - } + goto cp0_unimplemented; + } break; case 9: switch (sel) { @@ -4817,7 +5131,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; /* 6,7 are implementation dependent */ default: - goto die; + goto cp0_unimplemented; } break; case 10: @@ -4828,7 +5142,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EntryHi"; break; default: - goto die; + goto cp0_unimplemented; } break; case 11: @@ -4839,7 +5153,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; /* 6,7 are implementation dependent */ default: - goto die; + goto cp0_unimplemented; } break; case 12: @@ -4864,7 +5178,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "SRSMap"; break; default: - goto die; + goto cp0_unimplemented; } break; case 13: @@ -4874,7 +5188,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "Cause"; break; default: - goto die; + goto cp0_unimplemented; } break; case 14: @@ -4885,7 +5199,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EPC"; break; default: - goto die; + goto cp0_unimplemented; } break; case 15: @@ -4900,7 +5214,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EBase"; break; default: - goto die; + goto cp0_unimplemented; } break; case 16: @@ -4939,7 +5253,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "Config7"; break; default: - goto die; + goto cp0_unimplemented; } break; case 17: @@ -4949,7 +5263,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "LLAddr"; break; default: - goto die; + goto cp0_unimplemented; } break; case 18: @@ -4959,7 +5273,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "WatchLo"; break; default: - goto die; + goto cp0_unimplemented; } break; case 19: @@ -4969,7 +5283,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "WatchHi"; break; default: - goto die; + goto cp0_unimplemented; } break; case 20: @@ -4983,18 +5297,19 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; #endif default: - goto die; + goto cp0_unimplemented; } break; case 21: /* Officially reserved, but sel 0 is used for R1x000 framemask */ + CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6)); switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Framemask)); rn = "Framemask"; break; default: - goto die; + goto cp0_unimplemented; } break; case 22: @@ -5024,7 +5339,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "TraceBPC"; // break; default: - goto die; + goto cp0_unimplemented; } break; case 24: @@ -5036,7 +5351,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "DEPC"; break; default: - goto die; + goto cp0_unimplemented; } break; case 25: @@ -5074,7 +5389,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "Performance7"; // break; default: - goto die; + goto cp0_unimplemented; } break; case 26: @@ -5088,7 +5403,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "CacheErr"; break; default: - goto die; + goto cp0_unimplemented; } break; case 28: @@ -5108,7 +5423,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "DataLo"; break; default: - goto die; + goto cp0_unimplemented; } break; case 29: @@ -5128,7 +5443,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "DataHi"; break; default: - goto die; + goto cp0_unimplemented; } break; case 30: @@ -5139,7 +5454,7 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "ErrorEPC"; break; default: - goto die; + goto cp0_unimplemented; } break; case 31: @@ -5149,20 +5464,27 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DESAVE)); rn = "DESAVE"; break; + case 2 ... 7: + CP0_CHECK(ctx->kscrexist & (1 << sel)); + tcg_gen_ld_tl(arg, cpu_env, + offsetof(CPUMIPSState, CP0_KScratch[sel-2])); + tcg_gen_ext32s_tl(arg, arg); + rn = "KScratch"; + break; default: - goto die; + goto cp0_unimplemented; } break; default: - goto die; + goto cp0_unimplemented; } (void)rn; /* avoid a compiler warning */ LOG_DISAS("mfc0 %s (reg %d sel %d)\n", rn, reg, sel); return; -die: +cp0_unimplemented: LOG_DISAS("mfc0 %s (reg %d sel %d)\n", rn, reg, sel); - generate_exception(ctx, EXCP_RI); + gen_mfc0_unimplemented(ctx, arg); } static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) @@ -5183,22 +5505,22 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "Index"; break; case 1: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_mvpcontrol(cpu_env, arg); rn = "MVPControl"; break; case 2: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); /* ignored */ rn = "MVPConf0"; break; case 3: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); /* ignored */ rn = "MVPConf1"; break; default: - goto die; + goto cp0_unimplemented; } break; case 1: @@ -5208,42 +5530,42 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "Random"; break; case 1: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_vpecontrol(cpu_env, arg); rn = "VPEControl"; break; case 2: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_vpeconf0(cpu_env, arg); rn = "VPEConf0"; break; case 3: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_vpeconf1(cpu_env, arg); rn = "VPEConf1"; break; case 4: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_yqmask(cpu_env, arg); rn = "YQMask"; break; case 5: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mtc0_store64(arg, offsetof(CPUMIPSState, CP0_VPESchedule)); rn = "VPESchedule"; break; case 6: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mtc0_store64(arg, offsetof(CPUMIPSState, CP0_VPEScheFBack)); rn = "VPEScheFBack"; break; case 7: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_vpeopt(cpu_env, arg); rn = "VPEOpt"; break; default: - goto die; + goto cp0_unimplemented; } break; case 2: @@ -5253,42 +5575,42 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EntryLo0"; break; case 1: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcstatus(cpu_env, arg); rn = "TCStatus"; break; case 2: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcbind(cpu_env, arg); rn = "TCBind"; break; case 3: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcrestart(cpu_env, arg); rn = "TCRestart"; break; case 4: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tchalt(cpu_env, arg); rn = "TCHalt"; break; case 5: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tccontext(cpu_env, arg); rn = "TCContext"; break; case 6: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcschedule(cpu_env, arg); rn = "TCSchedule"; break; case 7: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcschefback(cpu_env, arg); rn = "TCScheFBack"; break; default: - goto die; + goto cp0_unimplemented; } break; case 3: @@ -5298,7 +5620,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EntryLo1"; break; default: - goto die; + goto cp0_unimplemented; } break; case 4: @@ -5310,17 +5632,16 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 1: // gen_helper_mtc0_contextconfig(cpu_env, arg); /* SmartMIPS ASE */ rn = "ContextConfig"; - goto die; + goto cp0_unimplemented; // break; case 2: - if (ctx->ulri) { - tcg_gen_st_tl(arg, cpu_env, - offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); - rn = "UserLocal"; - } + CP0_CHECK(ctx->ulri); + tcg_gen_st_tl(arg, cpu_env, + offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); + rn = "UserLocal"; break; default: - goto die; + goto cp0_unimplemented; } break; case 5: @@ -5335,7 +5656,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "PageGrain"; break; default: - goto die; + goto cp0_unimplemented; } break; case 6: @@ -5370,7 +5691,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "SRSConf4"; break; default: - goto die; + goto cp0_unimplemented; } break; case 7: @@ -5382,12 +5703,26 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "HWREna"; break; default: - goto die; + goto cp0_unimplemented; } break; case 8: - /* ignored */ - rn = "BadVAddr"; + switch (sel) { + case 0: + /* ignored */ + rn = "BadVAddr"; + break; + case 1: + /* ignored */ + rn = "BadInstr"; + break; + case 2: + /* ignored */ + rn = "BadInstrP"; + break; + default: + goto cp0_unimplemented; + } break; case 9: switch (sel) { @@ -5397,7 +5732,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; /* 6,7 are implementation dependent */ default: - goto die; + goto cp0_unimplemented; } break; case 10: @@ -5407,7 +5742,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EntryHi"; break; default: - goto die; + goto cp0_unimplemented; } break; case 11: @@ -5418,7 +5753,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; /* 6,7 are implementation dependent */ default: - goto die; + goto cp0_unimplemented; } break; case 12: @@ -5453,7 +5788,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "SRSMap"; break; default: - goto die; + goto cp0_unimplemented; } break; case 13: @@ -5464,7 +5799,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "Cause"; break; default: - goto die; + goto cp0_unimplemented; } break; case 14: @@ -5474,7 +5809,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EPC"; break; default: - goto die; + goto cp0_unimplemented; } break; case 15: @@ -5489,7 +5824,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EBase"; break; default: - goto die; + goto cp0_unimplemented; } break; case 16: @@ -5536,7 +5871,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; default: rn = "Invalid config selector"; - goto die; + goto cp0_unimplemented; } break; case 17: @@ -5546,7 +5881,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "LLAddr"; break; default: - goto die; + goto cp0_unimplemented; } break; case 18: @@ -5556,7 +5891,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "WatchLo"; break; default: - goto die; + goto cp0_unimplemented; } break; case 19: @@ -5566,7 +5901,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "WatchHi"; break; default: - goto die; + goto cp0_unimplemented; } break; case 20: @@ -5579,18 +5914,19 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; #endif default: - goto die; + goto cp0_unimplemented; } break; case 21: /* Officially reserved, but sel 0 is used for R1x000 framemask */ + CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6)); switch (sel) { case 0: gen_helper_mtc0_framemask(cpu_env, arg); rn = "Framemask"; break; default: - goto die; + goto cp0_unimplemented; } break; case 22: @@ -5633,7 +5969,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "TraceBPC"; // break; default: - goto die; + goto cp0_unimplemented; } break; case 24: @@ -5644,7 +5980,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "DEPC"; break; default: - goto die; + goto cp0_unimplemented; } break; case 25: @@ -5682,7 +6018,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "Performance7"; // break; default: - goto die; + goto cp0_unimplemented; } break; case 26: @@ -5696,7 +6032,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "CacheErr"; break; default: - goto die; + goto cp0_unimplemented; } break; case 28: @@ -5716,7 +6052,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "DataLo"; break; default: - goto die; + goto cp0_unimplemented; } break; case 29: @@ -5737,7 +6073,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; default: rn = "invalid sel"; - goto die; + goto cp0_unimplemented; } break; case 30: @@ -5747,7 +6083,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "ErrorEPC"; break; default: - goto die; + goto cp0_unimplemented; } break; case 31: @@ -5757,14 +6093,20 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_DESAVE)); rn = "DESAVE"; break; + case 2 ... 7: + CP0_CHECK(ctx->kscrexist & (1 << sel)); + tcg_gen_st_tl(arg, cpu_env, + offsetof(CPUMIPSState, CP0_KScratch[sel-2])); + rn = "KScratch"; + break; default: - goto die; + goto cp0_unimplemented; } /* Stop translation as we may have switched the execution mode */ ctx->bstate = BS_STOP; break; default: - goto die; + goto cp0_unimplemented; } (void)rn; /* avoid a compiler warning */ LOG_DISAS("mtc0 %s (reg %d sel %d)\n", rn, reg, sel); @@ -5775,9 +6117,8 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) } return; -die: +cp0_unimplemented: LOG_DISAS("mtc0 %s (reg %d sel %d)\n", rn, reg, sel); - generate_exception(ctx, EXCP_RI); } #if defined(TARGET_MIPS64) @@ -5796,67 +6137,68 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "Index"; break; case 1: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_mvpcontrol(arg, cpu_env); rn = "MVPControl"; break; case 2: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_mvpconf0(arg, cpu_env); rn = "MVPConf0"; break; case 3: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_mvpconf1(arg, cpu_env); rn = "MVPConf1"; break; default: - goto die; + goto cp0_unimplemented; } break; case 1: switch (sel) { case 0: + CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6)); gen_helper_mfc0_random(arg, cpu_env); rn = "Random"; break; case 1: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEControl)); rn = "VPEControl"; break; case 2: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf0)); rn = "VPEConf0"; break; case 3: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf1)); rn = "VPEConf1"; break; case 4: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_YQMask)); rn = "YQMask"; break; case 5: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPESchedule)); rn = "VPESchedule"; break; case 6: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPEScheFBack)); rn = "VPEScheFBack"; break; case 7: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEOpt)); rn = "VPEOpt"; break; default: - goto die; + goto cp0_unimplemented; } break; case 2: @@ -5866,42 +6208,42 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EntryLo0"; break; case 1: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_tcstatus(arg, cpu_env); rn = "TCStatus"; break; case 2: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_tcbind(arg, cpu_env); rn = "TCBind"; break; case 3: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_dmfc0_tcrestart(arg, cpu_env); rn = "TCRestart"; break; case 4: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_dmfc0_tchalt(arg, cpu_env); rn = "TCHalt"; break; case 5: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_dmfc0_tccontext(arg, cpu_env); rn = "TCContext"; break; case 6: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_dmfc0_tcschedule(arg, cpu_env); rn = "TCSchedule"; break; case 7: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_dmfc0_tcschefback(arg, cpu_env); rn = "TCScheFBack"; break; default: - goto die; + goto cp0_unimplemented; } break; case 3: @@ -5911,7 +6253,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EntryLo1"; break; default: - goto die; + goto cp0_unimplemented; } break; case 4: @@ -5923,19 +6265,16 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 1: // gen_helper_dmfc0_contextconfig(arg); /* SmartMIPS ASE */ rn = "ContextConfig"; - goto die; + goto cp0_unimplemented; // break; case 2: - if (ctx->ulri) { - tcg_gen_ld_tl(arg, cpu_env, - offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); - rn = "UserLocal"; - } else { - tcg_gen_movi_tl(arg, 0); - } + CP0_CHECK(ctx->ulri); + tcg_gen_ld_tl(arg, cpu_env, + offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); + rn = "UserLocal"; break; default: - goto die; + goto cp0_unimplemented; } break; case 5: @@ -5950,7 +6289,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "PageGrain"; break; default: - goto die; + goto cp0_unimplemented; } break; case 6: @@ -5985,7 +6324,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "SRSConf4"; break; default: - goto die; + goto cp0_unimplemented; } break; case 7: @@ -5996,7 +6335,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "HWREna"; break; default: - goto die; + goto cp0_unimplemented; } break; case 8: @@ -6005,8 +6344,18 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr)); rn = "BadVAddr"; break; + case 1: + CP0_CHECK(ctx->bi); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstr)); + rn = "BadInstr"; + break; + case 2: + CP0_CHECK(ctx->bp); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstrP)); + rn = "BadInstrP"; + break; default: - goto die; + goto cp0_unimplemented; } break; case 9: @@ -6026,7 +6375,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; /* 6,7 are implementation dependent */ default: - goto die; + goto cp0_unimplemented; } break; case 10: @@ -6036,7 +6385,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EntryHi"; break; default: - goto die; + goto cp0_unimplemented; } break; case 11: @@ -6047,7 +6396,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; /* 6,7 are implementation dependent */ default: - goto die; + goto cp0_unimplemented; } break; case 12: @@ -6072,7 +6421,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "SRSMap"; break; default: - goto die; + goto cp0_unimplemented; } break; case 13: @@ -6082,7 +6431,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "Cause"; break; default: - goto die; + goto cp0_unimplemented; } break; case 14: @@ -6092,7 +6441,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EPC"; break; default: - goto die; + goto cp0_unimplemented; } break; case 15: @@ -6107,7 +6456,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EBase"; break; default: - goto die; + goto cp0_unimplemented; } break; case 16: @@ -6128,6 +6477,14 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config3)); rn = "Config3"; break; + case 4: + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config4)); + rn = "Config4"; + break; + case 5: + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config5)); + rn = "Config5"; + break; /* 6,7 are implementation dependent */ case 6: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config6)); @@ -6138,7 +6495,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "Config7"; break; default: - goto die; + goto cp0_unimplemented; } break; case 17: @@ -6148,7 +6505,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "LLAddr"; break; default: - goto die; + goto cp0_unimplemented; } break; case 18: @@ -6158,7 +6515,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "WatchLo"; break; default: - goto die; + goto cp0_unimplemented; } break; case 19: @@ -6168,7 +6525,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "WatchHi"; break; default: - goto die; + goto cp0_unimplemented; } break; case 20: @@ -6179,18 +6536,19 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "XContext"; break; default: - goto die; + goto cp0_unimplemented; } break; case 21: /* Officially reserved, but sel 0 is used for R1x000 framemask */ + CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6)); switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Framemask)); rn = "Framemask"; break; default: - goto die; + goto cp0_unimplemented; } break; case 22: @@ -6220,7 +6578,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "TraceBPC"; // break; default: - goto die; + goto cp0_unimplemented; } break; case 24: @@ -6231,7 +6589,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "DEPC"; break; default: - goto die; + goto cp0_unimplemented; } break; case 25: @@ -6269,7 +6627,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "Performance7"; // break; default: - goto die; + goto cp0_unimplemented; } break; case 26: @@ -6284,7 +6642,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "CacheErr"; break; default: - goto die; + goto cp0_unimplemented; } break; case 28: @@ -6304,7 +6662,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "DataLo"; break; default: - goto die; + goto cp0_unimplemented; } break; case 29: @@ -6324,7 +6682,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "DataHi"; break; default: - goto die; + goto cp0_unimplemented; } break; case 30: @@ -6334,7 +6692,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "ErrorEPC"; break; default: - goto die; + goto cp0_unimplemented; } break; case 31: @@ -6344,20 +6702,26 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DESAVE)); rn = "DESAVE"; break; + case 2 ... 7: + CP0_CHECK(ctx->kscrexist & (1 << sel)); + tcg_gen_ld_tl(arg, cpu_env, + offsetof(CPUMIPSState, CP0_KScratch[sel-2])); + rn = "KScratch"; + break; default: - goto die; + goto cp0_unimplemented; } break; default: - goto die; + goto cp0_unimplemented; } (void)rn; /* avoid a compiler warning */ LOG_DISAS("dmfc0 %s (reg %d sel %d)\n", rn, reg, sel); return; -die: +cp0_unimplemented: LOG_DISAS("dmfc0 %s (reg %d sel %d)\n", rn, reg, sel); - generate_exception(ctx, EXCP_RI); + gen_mfc0_unimplemented(ctx, arg); } static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) @@ -6378,22 +6742,22 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "Index"; break; case 1: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_mvpcontrol(cpu_env, arg); rn = "MVPControl"; break; case 2: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); /* ignored */ rn = "MVPConf0"; break; case 3: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); /* ignored */ rn = "MVPConf1"; break; default: - goto die; + goto cp0_unimplemented; } break; case 1: @@ -6403,97 +6767,97 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "Random"; break; case 1: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_vpecontrol(cpu_env, arg); rn = "VPEControl"; break; case 2: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_vpeconf0(cpu_env, arg); rn = "VPEConf0"; break; case 3: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_vpeconf1(cpu_env, arg); rn = "VPEConf1"; break; case 4: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_yqmask(cpu_env, arg); rn = "YQMask"; break; case 5: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPESchedule)); rn = "VPESchedule"; break; case 6: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPEScheFBack)); rn = "VPEScheFBack"; break; case 7: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_vpeopt(cpu_env, arg); rn = "VPEOpt"; break; default: - goto die; + goto cp0_unimplemented; } break; case 2: switch (sel) { case 0: - gen_helper_mtc0_entrylo0(cpu_env, arg); + gen_helper_dmtc0_entrylo0(cpu_env, arg); rn = "EntryLo0"; break; case 1: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcstatus(cpu_env, arg); rn = "TCStatus"; break; case 2: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcbind(cpu_env, arg); rn = "TCBind"; break; case 3: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcrestart(cpu_env, arg); rn = "TCRestart"; break; case 4: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tchalt(cpu_env, arg); rn = "TCHalt"; break; case 5: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tccontext(cpu_env, arg); rn = "TCContext"; break; case 6: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcschedule(cpu_env, arg); rn = "TCSchedule"; break; case 7: - check_insn(ctx, ASE_MT); + CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcschefback(cpu_env, arg); rn = "TCScheFBack"; break; default: - goto die; + goto cp0_unimplemented; } break; case 3: switch (sel) { case 0: - gen_helper_mtc0_entrylo1(cpu_env, arg); + gen_helper_dmtc0_entrylo1(cpu_env, arg); rn = "EntryLo1"; break; default: - goto die; + goto cp0_unimplemented; } break; case 4: @@ -6505,17 +6869,16 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 1: // gen_helper_mtc0_contextconfig(cpu_env, arg); /* SmartMIPS ASE */ rn = "ContextConfig"; - goto die; + goto cp0_unimplemented; // break; case 2: - if (ctx->ulri) { - tcg_gen_st_tl(arg, cpu_env, - offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); - rn = "UserLocal"; - } + CP0_CHECK(ctx->ulri); + tcg_gen_st_tl(arg, cpu_env, + offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); + rn = "UserLocal"; break; default: - goto die; + goto cp0_unimplemented; } break; case 5: @@ -6530,7 +6893,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "PageGrain"; break; default: - goto die; + goto cp0_unimplemented; } break; case 6: @@ -6565,7 +6928,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "SRSConf4"; break; default: - goto die; + goto cp0_unimplemented; } break; case 7: @@ -6577,12 +6940,26 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "HWREna"; break; default: - goto die; + goto cp0_unimplemented; } break; case 8: - /* ignored */ - rn = "BadVAddr"; + switch (sel) { + case 0: + /* ignored */ + rn = "BadVAddr"; + break; + case 1: + /* ignored */ + rn = "BadInstr"; + break; + case 2: + /* ignored */ + rn = "BadInstrP"; + break; + default: + goto cp0_unimplemented; + } break; case 9: switch (sel) { @@ -6592,7 +6969,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; /* 6,7 are implementation dependent */ default: - goto die; + goto cp0_unimplemented; } /* Stop translation as we may have switched the execution mode */ ctx->bstate = BS_STOP; @@ -6604,7 +6981,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EntryHi"; break; default: - goto die; + goto cp0_unimplemented; } break; case 11: @@ -6615,7 +6992,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; /* 6,7 are implementation dependent */ default: - goto die; + goto cp0_unimplemented; } /* Stop translation as we may have switched the execution mode */ ctx->bstate = BS_STOP; @@ -6652,7 +7029,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "SRSMap"; break; default: - goto die; + goto cp0_unimplemented; } break; case 13: @@ -6673,7 +7050,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "Cause"; break; default: - goto die; + goto cp0_unimplemented; } break; case 14: @@ -6683,7 +7060,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EPC"; break; default: - goto die; + goto cp0_unimplemented; } break; case 15: @@ -6698,7 +7075,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "EBase"; break; default: - goto die; + goto cp0_unimplemented; } break; case 16: @@ -6723,10 +7100,20 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) /* ignored */ rn = "Config3"; break; + case 4: + /* currently ignored */ + rn = "Config4"; + break; + case 5: + gen_helper_mtc0_config5(cpu_env, arg); + rn = "Config5"; + /* Stop translation as we may have switched the execution mode */ + ctx->bstate = BS_STOP; + break; /* 6,7 are implementation dependent */ default: rn = "Invalid config selector"; - goto die; + goto cp0_unimplemented; } break; case 17: @@ -6736,7 +7123,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "LLAddr"; break; default: - goto die; + goto cp0_unimplemented; } break; case 18: @@ -6746,7 +7133,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "WatchLo"; break; default: - goto die; + goto cp0_unimplemented; } break; case 19: @@ -6756,7 +7143,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "WatchHi"; break; default: - goto die; + goto cp0_unimplemented; } break; case 20: @@ -6767,18 +7154,19 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "XContext"; break; default: - goto die; + goto cp0_unimplemented; } break; case 21: /* Officially reserved, but sel 0 is used for R1x000 framemask */ + CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6)); switch (sel) { case 0: gen_helper_mtc0_framemask(cpu_env, arg); rn = "Framemask"; break; default: - goto die; + goto cp0_unimplemented; } break; case 22: @@ -6819,7 +7207,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "TraceBPC"; // break; default: - goto die; + goto cp0_unimplemented; } break; case 24: @@ -6830,7 +7218,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "DEPC"; break; default: - goto die; + goto cp0_unimplemented; } break; case 25: @@ -6868,7 +7256,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "Performance7"; // break; default: - goto die; + goto cp0_unimplemented; } break; case 26: @@ -6882,7 +7270,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "CacheErr"; break; default: - goto die; + goto cp0_unimplemented; } break; case 28: @@ -6902,7 +7290,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "DataLo"; break; default: - goto die; + goto cp0_unimplemented; } break; case 29: @@ -6923,7 +7311,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) break; default: rn = "invalid sel"; - goto die; + goto cp0_unimplemented; } break; case 30: @@ -6933,7 +7321,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) rn = "ErrorEPC"; break; default: - goto die; + goto cp0_unimplemented; } break; case 31: @@ -6943,14 +7331,20 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_DESAVE)); rn = "DESAVE"; break; + case 2 ... 7: + CP0_CHECK(ctx->kscrexist & (1 << sel)); + tcg_gen_st_tl(arg, cpu_env, + offsetof(CPUMIPSState, CP0_KScratch[sel-2])); + rn = "KScratch"; + break; default: - goto die; + goto cp0_unimplemented; } /* Stop translation as we may have switched the execution mode */ ctx->bstate = BS_STOP; break; default: - goto die; + goto cp0_unimplemented; } (void)rn; /* avoid a compiler warning */ LOG_DISAS("dmtc0 %s (reg %d sel %d)\n", rn, reg, sel); @@ -6961,9 +7355,8 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) } return; -die: +cp0_unimplemented: LOG_DISAS("dmtc0 %s (reg %d sel %d)\n", rn, reg, sel); - generate_exception(ctx, EXCP_RI); } #endif /* TARGET_MIPS64 */ @@ -7362,12 +7755,15 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, break; case 3: /* XXX: For now we support only a single FPU context. */ + save_cpu_state(ctx, 1); { TCGv_i32 fs_tmp = tcg_const_i32(rd); gen_helper_0e2i(ctc1, t0, fs_tmp, rt); tcg_temp_free_i32(fs_tmp); } + /* Stop translation as we may have changed hflags */ + ctx->bstate = BS_STOP; break; /* COP2: Not implemented. */ case 4: @@ -7454,6 +7850,24 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, goto die; gen_helper_tlbwi(cpu_env); break; + case OPC_TLBINV: + opn = "tlbinv"; + if (ctx->ie >= 2) { + if (!env->tlb->helper_tlbinv) { + goto die; + } + gen_helper_tlbinv(cpu_env); + } /* treat as nop if TLBINV not supported */ + break; + case OPC_TLBINVF: + opn = "tlbinvf"; + if (ctx->ie >= 2) { + if (!env->tlb->helper_tlbinvf) { + goto die; + } + gen_helper_tlbinvf(cpu_env); + } /* treat as nop if TLBINV not supported */ + break; case OPC_TLBWR: opn = "tlbwr"; if (!env->tlb->helper_tlbwr) @@ -7475,12 +7889,22 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, case OPC_ERET: opn = "eret"; check_insn(ctx, ISA_MIPS2); + if ((ctx->insn_flags & ISA_MIPS32R6) && + (ctx->hflags & MIPS_HFLAG_BMASK)) { + MIPS_DEBUG("CTI in delay / forbidden slot"); + goto die; + } gen_helper_eret(cpu_env); ctx->bstate = BS_EXCP; break; case OPC_DERET: opn = "deret"; check_insn(ctx, ISA_MIPS32); + if ((ctx->insn_flags & ISA_MIPS32R6) && + (ctx->hflags & MIPS_HFLAG_BMASK)) { + MIPS_DEBUG("CTI in delay / forbidden slot"); + goto die; + } if (!(ctx->hflags & MIPS_HFLAG_DM)) { MIPS_INVAL(opn); generate_exception(ctx, EXCP_RI); @@ -7492,6 +7916,11 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, case OPC_WAIT: opn = "wait"; check_insn(ctx, ISA_MIPS3 | ISA_MIPS32); + if ((ctx->insn_flags & ISA_MIPS32R6) && + (ctx->hflags & MIPS_HFLAG_BMASK)) { + MIPS_DEBUG("CTI in delay / forbidden slot"); + goto die; + } /* If we get an exception, we want to restart at next instruction */ ctx->pc += 4; save_cpu_state(ctx, 1); @@ -7518,6 +7947,12 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op, const char *opn = "cp1 cond branch"; TCGv_i32 t0 = tcg_temp_new_i32(); + if ((ctx->insn_flags & ISA_MIPS32R6) && (ctx->hflags & MIPS_HFLAG_BMASK)) { + MIPS_DEBUG("CTI in delay / forbidden slot"); + generate_exception(ctx, EXCP_RI); + goto out; + } + if (cc != 0) check_insn(ctx, ISA_MIPS4 | ISA_MIPS32); @@ -7634,7 +8069,8 @@ static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op, if (ctx->hflags & MIPS_HFLAG_BMASK) { #ifdef MIPS_DEBUG_DISAS - LOG_DISAS("Branch in delay slot at PC 0x" TARGET_FMT_lx "\n", ctx->pc); + LOG_DISAS("Branch in delay / forbidden slot at PC 0x" TARGET_FMT_lx + "\n", ctx->pc); #endif generate_exception(ctx, EXCP_RI); goto out; @@ -7668,6 +8104,7 @@ static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op, MIPS_DEBUG("%s: cond %02x target " TARGET_FMT_lx, opn, ctx->hflags, btarget); ctx->btarget = btarget; + ctx->hflags |= MIPS_HFLAG_BDS32; out: tcg_temp_free_i64(t0); @@ -7921,12 +8358,15 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) break; case OPC_CTC1: gen_load_gpr(t0, rt); + save_cpu_state(ctx, 1); { TCGv_i32 fs_tmp = tcg_const_i32(fs); gen_helper_0e2i(ctc1, t0, fs_tmp, rt); tcg_temp_free_i32(fs_tmp); } + /* Stop translation as we may have changed hflags */ + ctx->bstate = BS_STOP; opn = "ctc1"; break; #if defined(TARGET_MIPS64) @@ -10088,6 +10528,10 @@ static void gen_branch(DisasContext *ctx, int insn_bytes) save_cpu_state(ctx, 0); /* FIXME: Need to clear can_do_io. */ switch (proc_hflags & MIPS_HFLAG_BMASK_BASE) { + case MIPS_HFLAG_FBNSLOT: + MIPS_DEBUG("forbidden slot"); + gen_goto_tb(ctx, 0, ctx->pc + insn_bytes); + break; case MIPS_HFLAG_B: /* unconditional branch */ MIPS_DEBUG("unconditional branch"); @@ -13170,8 +13614,13 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, check_insn(ctx, ASE_MIPS3D); /* Fall through */ do_cp1branch: - gen_compute_branch1(ctx, mips32_op, - (ctx->opcode >> 18) & 0x7, imm << 1); + if (env->CP0_Config1 & (1 << CP0C1_FP)) { + check_cp1_enabled(ctx); + gen_compute_branch1(ctx, mips32_op, + (ctx->opcode >> 18) & 0x7, imm << 1); + } else { + generate_exception_err(ctx, EXCP_CpU, 1); + } break; case BPOSGE64: case BPOSGE32: @@ -15370,7 +15819,8 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, if (ctx->hflags & MIPS_HFLAG_BMASK) { #ifdef MIPS_DEBUG_DISAS - LOG_DISAS("Branch in delay slot at PC 0x" TARGET_FMT_lx "\n", ctx->pc); + LOG_DISAS("Branch in delay / forbidden slot at PC 0x" TARGET_FMT_lx + "\n", ctx->pc); #endif generate_exception(ctx, EXCP_RI); goto out; @@ -15463,56 +15913,56 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, gen_branch(ctx, 4); } else { /* Conditional compact branch */ - int l1 = gen_new_label(); + int fs = gen_new_label(); save_cpu_state(ctx, 0); switch (opc) { case OPC_BLEZALC: /* OPC_BGEZALC, OPC_BGEUC */ if (rs == 0 && rt != 0) { /* OPC_BLEZALC */ - tcg_gen_brcondi_tl(TCG_COND_LE, t1, 0, l1); + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LE), t1, 0, fs); } else if (rs != 0 && rt != 0 && rs == rt) { /* OPC_BGEZALC */ - tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GE), t1, 0, fs); } else { /* OPC_BGEUC */ - tcg_gen_brcond_tl(TCG_COND_GEU, t0, t1, l1); + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_GEU), t0, t1, fs); } break; case OPC_BGTZALC: /* OPC_BLTZALC, OPC_BLTUC */ if (rs == 0 && rt != 0) { /* OPC_BGTZALC */ - tcg_gen_brcondi_tl(TCG_COND_GT, t1, 0, l1); + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GT), t1, 0, fs); } else if (rs != 0 && rt != 0 && rs == rt) { /* OPC_BLTZALC */ - tcg_gen_brcondi_tl(TCG_COND_LT, t1, 0, l1); + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LT), t1, 0, fs); } else { /* OPC_BLTUC */ - tcg_gen_brcond_tl(TCG_COND_LTU, t0, t1, l1); + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_LTU), t0, t1, fs); } break; case OPC_BLEZC: /* OPC_BGEZC, OPC_BGEC */ if (rs == 0 && rt != 0) { /* OPC_BLEZC */ - tcg_gen_brcondi_tl(TCG_COND_LE, t1, 0, l1); + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LE), t1, 0, fs); } else if (rs != 0 && rt != 0 && rs == rt) { /* OPC_BGEZC */ - tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GE), t1, 0, fs); } else { /* OPC_BGEC */ - tcg_gen_brcond_tl(TCG_COND_GE, t0, t1, l1); + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_GE), t0, t1, fs); } break; case OPC_BGTZC: /* OPC_BLTZC, OPC_BLTC */ if (rs == 0 && rt != 0) { /* OPC_BGTZC */ - tcg_gen_brcondi_tl(TCG_COND_GT, t1, 0, l1); + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GT), t1, 0, fs); } else if (rs != 0 && rt != 0 && rs == rt) { /* OPC_BLTZC */ - tcg_gen_brcondi_tl(TCG_COND_LT, t1, 0, l1); + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LT), t1, 0, fs); } else { /* OPC_BLTC */ - tcg_gen_brcond_tl(TCG_COND_LT, t0, t1, l1); + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_LT), t0, t1, fs); } break; case OPC_BOVC: /* OPC_BEQZALC, OPC_BEQC */ @@ -15541,10 +15991,10 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, tcg_gen_or_tl(t4, t4, input_overflow); if (opc == OPC_BOVC) { /* OPC_BOVC */ - tcg_gen_brcondi_tl(TCG_COND_NE, t4, 0, l1); + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_NE), t4, 0, fs); } else { /* OPC_BNVC */ - tcg_gen_brcondi_tl(TCG_COND_EQ, t4, 0, l1); + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t4, 0, fs); } tcg_temp_free(input_overflow); tcg_temp_free(t4); @@ -15554,27 +16004,27 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, /* OPC_BEQZALC, OPC_BNEZALC */ if (opc == OPC_BEQZALC) { /* OPC_BEQZALC */ - tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1); + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t1, 0, fs); } else { /* OPC_BNEZALC */ - tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_NE), t1, 0, fs); } } else { /* OPC_BEQC, OPC_BNEC */ if (opc == OPC_BEQC) { /* OPC_BEQC */ - tcg_gen_brcond_tl(TCG_COND_EQ, t0, t1, l1); + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_EQ), t0, t1, fs); } else { /* OPC_BNEC */ - tcg_gen_brcond_tl(TCG_COND_NE, t0, t1, l1); + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_NE), t0, t1, fs); } } break; case OPC_BEQZC: - tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t0, 0, fs); break; case OPC_BNEZC: - tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0, l1); + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_NE), t0, 0, fs); break; default: MIPS_INVAL("Compact conditional branch/jump"); @@ -15583,12 +16033,11 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, } /* Generating branch here as compact branches don't have delay slot */ - /* TODO: implement forbidden slot */ - gen_goto_tb(ctx, 1, ctx->pc + 4); - gen_set_label(l1); - gen_goto_tb(ctx, 0, ctx->btarget); + gen_goto_tb(ctx, 1, ctx->btarget); + gen_set_label(fs); + + ctx->hflags |= MIPS_HFLAG_FBNSLOT; MIPS_DEBUG("Compact conditional branch"); - ctx->bstate = BS_BRANCH; } out: @@ -15656,7 +16105,11 @@ static void decode_opc_special_r6(CPUMIPSState *env, DisasContext *ctx) } break; case R6_OPC_SDBBP: - generate_exception(ctx, EXCP_DBp); + if (ctx->hflags & MIPS_HFLAG_SBRI) { + generate_exception(ctx, EXCP_RI); + } else { + generate_exception(ctx, EXCP_DBp); + } break; #if defined(TARGET_MIPS64) case OPC_DLSA: @@ -15802,6 +16255,16 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) op1 = MASK_SPECIAL(ctx->opcode); switch (op1) { case OPC_SLL: /* Shift with immediate */ + if (sa == 5 && rd == 0 && + rs == 0 && rt == 0) { /* PAUSE */ + if ((ctx->insn_flags & ISA_MIPS32R6) && + (ctx->hflags & MIPS_HFLAG_BMASK)) { + MIPS_DEBUG("CTI in delay / forbidden slot"); + generate_exception(ctx, EXCP_RI); + break; + } + } + /* Fallthrough */ case OPC_SRA: gen_shift_imm(ctx, op1, rd, rt, sa); break; @@ -15862,7 +16325,8 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) gen_trap(ctx, op1, rs, rt, -1); break; case OPC_LSA: /* OPC_PMON */ - if (ctx->insn_flags & ISA_MIPS32R6) { + if ((ctx->insn_flags & ISA_MIPS32R6) || + (env->CP0_Config3 & (1 << CP0C3_MSAP))) { decode_opc_special_r6(env, ctx); } else { /* Pmon entry point, also R4010 selsl */ @@ -15960,6 +16424,12 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) break; } break; + case OPC_DLSA: + if ((ctx->insn_flags & ISA_MIPS32R6) || + (env->CP0_Config3 & (1 << CP0C3_MSAP))) { + decode_opc_special_r6(env, ctx); + } + break; #endif default: if (ctx->insn_flags & ISA_MIPS32R6) { @@ -16769,6 +17239,1107 @@ static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx) } } +/* MIPS SIMD Architecture (MSA) */ +static inline int check_msa_access(DisasContext *ctx) +{ + if (unlikely((ctx->hflags & MIPS_HFLAG_FPU) && + !(ctx->hflags & MIPS_HFLAG_F64))) { + generate_exception(ctx, EXCP_RI); + return 0; + } + + if (unlikely(!(ctx->hflags & MIPS_HFLAG_MSA))) { + if (ctx->insn_flags & ASE_MSA) { + generate_exception(ctx, EXCP_MSADIS); + return 0; + } else { + generate_exception(ctx, EXCP_RI); + return 0; + } + } + return 1; +} + +static void gen_check_zero_element(TCGv tresult, uint8_t df, uint8_t wt) +{ + /* generates tcg ops to check if any element is 0 */ + /* Note this function only works with MSA_WRLEN = 128 */ + uint64_t eval_zero_or_big = 0; + uint64_t eval_big = 0; + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + switch (df) { + case DF_BYTE: + eval_zero_or_big = 0x0101010101010101ULL; + eval_big = 0x8080808080808080ULL; + break; + case DF_HALF: + eval_zero_or_big = 0x0001000100010001ULL; + eval_big = 0x8000800080008000ULL; + break; + case DF_WORD: + eval_zero_or_big = 0x0000000100000001ULL; + eval_big = 0x8000000080000000ULL; + break; + case DF_DOUBLE: + eval_zero_or_big = 0x0000000000000001ULL; + eval_big = 0x8000000000000000ULL; + break; + } + tcg_gen_subi_i64(t0, msa_wr_d[wt<<1], eval_zero_or_big); + tcg_gen_andc_i64(t0, t0, msa_wr_d[wt<<1]); + tcg_gen_andi_i64(t0, t0, eval_big); + tcg_gen_subi_i64(t1, msa_wr_d[(wt<<1)+1], eval_zero_or_big); + tcg_gen_andc_i64(t1, t1, msa_wr_d[(wt<<1)+1]); + tcg_gen_andi_i64(t1, t1, eval_big); + tcg_gen_or_i64(t0, t0, t1); + /* if all bits are zero then all elements are not zero */ + /* if some bit is non-zero then some element is zero */ + tcg_gen_setcondi_i64(TCG_COND_NE, t0, t0, 0); + tcg_gen_trunc_i64_tl(tresult, t0); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +} + +static void gen_msa_branch(CPUMIPSState *env, DisasContext *ctx, uint32_t op1) +{ + uint8_t df = (ctx->opcode >> 21) & 0x3; + uint8_t wt = (ctx->opcode >> 16) & 0x1f; + int64_t s16 = (int16_t)ctx->opcode; + + check_msa_access(ctx); + + if (ctx->insn_flags & ISA_MIPS32R6 && ctx->hflags & MIPS_HFLAG_BMASK) { + MIPS_DEBUG("CTI in delay / forbidden slot"); + generate_exception(ctx, EXCP_RI); + return; + } + switch (op1) { + case OPC_BZ_V: + case OPC_BNZ_V: + { + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_or_i64(t0, msa_wr_d[wt<<1], msa_wr_d[(wt<<1)+1]); + tcg_gen_setcondi_i64((op1 == OPC_BZ_V) ? + TCG_COND_EQ : TCG_COND_NE, t0, t0, 0); + tcg_gen_trunc_i64_tl(bcond, t0); + tcg_temp_free_i64(t0); + } + break; + case OPC_BZ_B: + case OPC_BZ_H: + case OPC_BZ_W: + case OPC_BZ_D: + gen_check_zero_element(bcond, df, wt); + break; + case OPC_BNZ_B: + case OPC_BNZ_H: + case OPC_BNZ_W: + case OPC_BNZ_D: + gen_check_zero_element(bcond, df, wt); + tcg_gen_setcondi_tl(TCG_COND_EQ, bcond, bcond, 0); + break; + } + + ctx->btarget = ctx->pc + (s16 << 2) + 4; + + ctx->hflags |= MIPS_HFLAG_BC; + ctx->hflags |= MIPS_HFLAG_BDS32; +} + +static void gen_msa_i8(CPUMIPSState *env, DisasContext *ctx) +{ +#define MASK_MSA_I8(op) (MASK_MSA_MINOR(op) | (op & (0x03 << 24))) + uint8_t i8 = (ctx->opcode >> 16) & 0xff; + uint8_t ws = (ctx->opcode >> 11) & 0x1f; + uint8_t wd = (ctx->opcode >> 6) & 0x1f; + + TCGv_i32 twd = tcg_const_i32(wd); + TCGv_i32 tws = tcg_const_i32(ws); + TCGv_i32 ti8 = tcg_const_i32(i8); + + switch (MASK_MSA_I8(ctx->opcode)) { + case OPC_ANDI_B: + gen_helper_msa_andi_b(cpu_env, twd, tws, ti8); + break; + case OPC_ORI_B: + gen_helper_msa_ori_b(cpu_env, twd, tws, ti8); + break; + case OPC_NORI_B: + gen_helper_msa_nori_b(cpu_env, twd, tws, ti8); + break; + case OPC_XORI_B: + gen_helper_msa_xori_b(cpu_env, twd, tws, ti8); + break; + case OPC_BMNZI_B: + gen_helper_msa_bmnzi_b(cpu_env, twd, tws, ti8); + break; + case OPC_BMZI_B: + gen_helper_msa_bmzi_b(cpu_env, twd, tws, ti8); + break; + case OPC_BSELI_B: + gen_helper_msa_bseli_b(cpu_env, twd, tws, ti8); + break; + case OPC_SHF_B: + case OPC_SHF_H: + case OPC_SHF_W: + { + uint8_t df = (ctx->opcode >> 24) & 0x3; + if (df == DF_DOUBLE) { + generate_exception(ctx, EXCP_RI); + } else { + TCGv_i32 tdf = tcg_const_i32(df); + gen_helper_msa_shf_df(cpu_env, tdf, twd, tws, ti8); + tcg_temp_free_i32(tdf); + } + } + break; + default: + MIPS_INVAL("MSA instruction"); + generate_exception(ctx, EXCP_RI); + break; + } + + tcg_temp_free_i32(twd); + tcg_temp_free_i32(tws); + tcg_temp_free_i32(ti8); +} + +static void gen_msa_i5(CPUMIPSState *env, DisasContext *ctx) +{ +#define MASK_MSA_I5(op) (MASK_MSA_MINOR(op) | (op & (0x7 << 23))) + uint8_t df = (ctx->opcode >> 21) & 0x3; + int8_t s5 = (int8_t) sextract32(ctx->opcode, 16, 5); + uint8_t u5 = (ctx->opcode >> 16) & 0x1f; + uint8_t ws = (ctx->opcode >> 11) & 0x1f; + uint8_t wd = (ctx->opcode >> 6) & 0x1f; + + TCGv_i32 tdf = tcg_const_i32(df); + TCGv_i32 twd = tcg_const_i32(wd); + TCGv_i32 tws = tcg_const_i32(ws); + TCGv_i32 timm = tcg_temp_new_i32(); + tcg_gen_movi_i32(timm, u5); + + switch (MASK_MSA_I5(ctx->opcode)) { + case OPC_ADDVI_df: + gen_helper_msa_addvi_df(cpu_env, tdf, twd, tws, timm); + break; + case OPC_SUBVI_df: + gen_helper_msa_subvi_df(cpu_env, tdf, twd, tws, timm); + break; + case OPC_MAXI_S_df: + tcg_gen_movi_i32(timm, s5); + gen_helper_msa_maxi_s_df(cpu_env, tdf, twd, tws, timm); + break; + case OPC_MAXI_U_df: + gen_helper_msa_maxi_u_df(cpu_env, tdf, twd, tws, timm); + break; + case OPC_MINI_S_df: + tcg_gen_movi_i32(timm, s5); + gen_helper_msa_mini_s_df(cpu_env, tdf, twd, tws, timm); + break; + case OPC_MINI_U_df: + gen_helper_msa_mini_u_df(cpu_env, tdf, twd, tws, timm); + break; + case OPC_CEQI_df: + tcg_gen_movi_i32(timm, s5); + gen_helper_msa_ceqi_df(cpu_env, tdf, twd, tws, timm); + break; + case OPC_CLTI_S_df: + tcg_gen_movi_i32(timm, s5); + gen_helper_msa_clti_s_df(cpu_env, tdf, twd, tws, timm); + break; + case OPC_CLTI_U_df: + gen_helper_msa_clti_u_df(cpu_env, tdf, twd, tws, timm); + break; + case OPC_CLEI_S_df: + tcg_gen_movi_i32(timm, s5); + gen_helper_msa_clei_s_df(cpu_env, tdf, twd, tws, timm); + break; + case OPC_CLEI_U_df: + gen_helper_msa_clei_u_df(cpu_env, tdf, twd, tws, timm); + break; + case OPC_LDI_df: + { + int32_t s10 = sextract32(ctx->opcode, 11, 10); + tcg_gen_movi_i32(timm, s10); + gen_helper_msa_ldi_df(cpu_env, tdf, twd, timm); + } + break; + default: + MIPS_INVAL("MSA instruction"); + generate_exception(ctx, EXCP_RI); + break; + } + + tcg_temp_free_i32(tdf); + tcg_temp_free_i32(twd); + tcg_temp_free_i32(tws); + tcg_temp_free_i32(timm); +} + +static void gen_msa_bit(CPUMIPSState *env, DisasContext *ctx) +{ +#define MASK_MSA_BIT(op) (MASK_MSA_MINOR(op) | (op & (0x7 << 23))) + uint8_t dfm = (ctx->opcode >> 16) & 0x7f; + uint32_t df = 0, m = 0; + uint8_t ws = (ctx->opcode >> 11) & 0x1f; + uint8_t wd = (ctx->opcode >> 6) & 0x1f; + + TCGv_i32 tdf; + TCGv_i32 tm; + TCGv_i32 twd; + TCGv_i32 tws; + + if ((dfm & 0x40) == 0x00) { + m = dfm & 0x3f; + df = DF_DOUBLE; + } else if ((dfm & 0x60) == 0x40) { + m = dfm & 0x1f; + df = DF_WORD; + } else if ((dfm & 0x70) == 0x60) { + m = dfm & 0x0f; + df = DF_HALF; + } else if ((dfm & 0x78) == 0x70) { + m = dfm & 0x7; + df = DF_BYTE; + } else { + generate_exception(ctx, EXCP_RI); + return; + } + + tdf = tcg_const_i32(df); + tm = tcg_const_i32(m); + twd = tcg_const_i32(wd); + tws = tcg_const_i32(ws); + + switch (MASK_MSA_BIT(ctx->opcode)) { + case OPC_SLLI_df: + gen_helper_msa_slli_df(cpu_env, tdf, twd, tws, tm); + break; + case OPC_SRAI_df: + gen_helper_msa_srai_df(cpu_env, tdf, twd, tws, tm); + break; + case OPC_SRLI_df: + gen_helper_msa_srli_df(cpu_env, tdf, twd, tws, tm); + break; + case OPC_BCLRI_df: + gen_helper_msa_bclri_df(cpu_env, tdf, twd, tws, tm); + break; + case OPC_BSETI_df: + gen_helper_msa_bseti_df(cpu_env, tdf, twd, tws, tm); + break; + case OPC_BNEGI_df: + gen_helper_msa_bnegi_df(cpu_env, tdf, twd, tws, tm); + break; + case OPC_BINSLI_df: + gen_helper_msa_binsli_df(cpu_env, tdf, twd, tws, tm); + break; + case OPC_BINSRI_df: + gen_helper_msa_binsri_df(cpu_env, tdf, twd, tws, tm); + break; + case OPC_SAT_S_df: + gen_helper_msa_sat_s_df(cpu_env, tdf, twd, tws, tm); + break; + case OPC_SAT_U_df: + gen_helper_msa_sat_u_df(cpu_env, tdf, twd, tws, tm); + break; + case OPC_SRARI_df: + gen_helper_msa_srari_df(cpu_env, tdf, twd, tws, tm); + break; + case OPC_SRLRI_df: + gen_helper_msa_srlri_df(cpu_env, tdf, twd, tws, tm); + break; + default: + MIPS_INVAL("MSA instruction"); + generate_exception(ctx, EXCP_RI); + break; + } + + tcg_temp_free_i32(tdf); + tcg_temp_free_i32(tm); + tcg_temp_free_i32(twd); + tcg_temp_free_i32(tws); +} + +static void gen_msa_3r(CPUMIPSState *env, DisasContext *ctx) +{ +#define MASK_MSA_3R(op) (MASK_MSA_MINOR(op) | (op & (0x7 << 23))) + uint8_t df = (ctx->opcode >> 21) & 0x3; + uint8_t wt = (ctx->opcode >> 16) & 0x1f; + uint8_t ws = (ctx->opcode >> 11) & 0x1f; + uint8_t wd = (ctx->opcode >> 6) & 0x1f; + + TCGv_i32 tdf = tcg_const_i32(df); + TCGv_i32 twd = tcg_const_i32(wd); + TCGv_i32 tws = tcg_const_i32(ws); + TCGv_i32 twt = tcg_const_i32(wt); + + switch (MASK_MSA_3R(ctx->opcode)) { + case OPC_SLL_df: + gen_helper_msa_sll_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_ADDV_df: + gen_helper_msa_addv_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_CEQ_df: + gen_helper_msa_ceq_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_ADD_A_df: + gen_helper_msa_add_a_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_SUBS_S_df: + gen_helper_msa_subs_s_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_MULV_df: + gen_helper_msa_mulv_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_SLD_df: + gen_helper_msa_sld_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_VSHF_df: + gen_helper_msa_vshf_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_SRA_df: + gen_helper_msa_sra_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_SUBV_df: + gen_helper_msa_subv_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_ADDS_A_df: + gen_helper_msa_adds_a_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_SUBS_U_df: + gen_helper_msa_subs_u_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_MADDV_df: + gen_helper_msa_maddv_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_SPLAT_df: + gen_helper_msa_splat_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_SRAR_df: + gen_helper_msa_srar_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_SRL_df: + gen_helper_msa_srl_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_MAX_S_df: + gen_helper_msa_max_s_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_CLT_S_df: + gen_helper_msa_clt_s_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_ADDS_S_df: + gen_helper_msa_adds_s_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_SUBSUS_U_df: + gen_helper_msa_subsus_u_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_MSUBV_df: + gen_helper_msa_msubv_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_PCKEV_df: + gen_helper_msa_pckev_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_SRLR_df: + gen_helper_msa_srlr_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_BCLR_df: + gen_helper_msa_bclr_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_MAX_U_df: + gen_helper_msa_max_u_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_CLT_U_df: + gen_helper_msa_clt_u_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_ADDS_U_df: + gen_helper_msa_adds_u_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_SUBSUU_S_df: + gen_helper_msa_subsuu_s_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_PCKOD_df: + gen_helper_msa_pckod_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_BSET_df: + gen_helper_msa_bset_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_MIN_S_df: + gen_helper_msa_min_s_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_CLE_S_df: + gen_helper_msa_cle_s_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_AVE_S_df: + gen_helper_msa_ave_s_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_ASUB_S_df: + gen_helper_msa_asub_s_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_DIV_S_df: + gen_helper_msa_div_s_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_ILVL_df: + gen_helper_msa_ilvl_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_BNEG_df: + gen_helper_msa_bneg_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_MIN_U_df: + gen_helper_msa_min_u_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_CLE_U_df: + gen_helper_msa_cle_u_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_AVE_U_df: + gen_helper_msa_ave_u_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_ASUB_U_df: + gen_helper_msa_asub_u_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_DIV_U_df: + gen_helper_msa_div_u_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_ILVR_df: + gen_helper_msa_ilvr_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_BINSL_df: + gen_helper_msa_binsl_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_MAX_A_df: + gen_helper_msa_max_a_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_AVER_S_df: + gen_helper_msa_aver_s_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_MOD_S_df: + gen_helper_msa_mod_s_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_ILVEV_df: + gen_helper_msa_ilvev_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_BINSR_df: + gen_helper_msa_binsr_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_MIN_A_df: + gen_helper_msa_min_a_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_AVER_U_df: + gen_helper_msa_aver_u_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_MOD_U_df: + gen_helper_msa_mod_u_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_ILVOD_df: + gen_helper_msa_ilvod_df(cpu_env, tdf, twd, tws, twt); + break; + + case OPC_DOTP_S_df: + case OPC_DOTP_U_df: + case OPC_DPADD_S_df: + case OPC_DPADD_U_df: + case OPC_DPSUB_S_df: + case OPC_HADD_S_df: + case OPC_DPSUB_U_df: + case OPC_HADD_U_df: + case OPC_HSUB_S_df: + case OPC_HSUB_U_df: + if (df == DF_BYTE) { + generate_exception(ctx, EXCP_RI); + } + switch (MASK_MSA_3R(ctx->opcode)) { + case OPC_DOTP_S_df: + gen_helper_msa_dotp_s_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_DOTP_U_df: + gen_helper_msa_dotp_u_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_DPADD_S_df: + gen_helper_msa_dpadd_s_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_DPADD_U_df: + gen_helper_msa_dpadd_u_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_DPSUB_S_df: + gen_helper_msa_dpsub_s_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_HADD_S_df: + gen_helper_msa_hadd_s_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_DPSUB_U_df: + gen_helper_msa_dpsub_u_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_HADD_U_df: + gen_helper_msa_hadd_u_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_HSUB_S_df: + gen_helper_msa_hsub_s_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_HSUB_U_df: + gen_helper_msa_hsub_u_df(cpu_env, tdf, twd, tws, twt); + break; + } + break; + default: + MIPS_INVAL("MSA instruction"); + generate_exception(ctx, EXCP_RI); + break; + } + tcg_temp_free_i32(twd); + tcg_temp_free_i32(tws); + tcg_temp_free_i32(twt); + tcg_temp_free_i32(tdf); +} + +static void gen_msa_elm_3e(CPUMIPSState *env, DisasContext *ctx) +{ +#define MASK_MSA_ELM_DF3E(op) (MASK_MSA_MINOR(op) | (op & (0x3FF << 16))) + uint8_t source = (ctx->opcode >> 11) & 0x1f; + uint8_t dest = (ctx->opcode >> 6) & 0x1f; + TCGv telm = tcg_temp_new(); + TCGv_i32 tsr = tcg_const_i32(source); + TCGv_i32 tdt = tcg_const_i32(dest); + + switch (MASK_MSA_ELM_DF3E(ctx->opcode)) { + case OPC_CTCMSA: + gen_load_gpr(telm, source); + gen_helper_msa_ctcmsa(cpu_env, telm, tdt); + break; + case OPC_CFCMSA: + gen_helper_msa_cfcmsa(telm, cpu_env, tsr); + gen_store_gpr(telm, dest); + break; + case OPC_MOVE_V: + gen_helper_msa_move_v(cpu_env, tdt, tsr); + break; + default: + MIPS_INVAL("MSA instruction"); + generate_exception(ctx, EXCP_RI); + break; + } + + tcg_temp_free(telm); + tcg_temp_free_i32(tdt); + tcg_temp_free_i32(tsr); +} + +static void gen_msa_elm_df(CPUMIPSState *env, DisasContext *ctx, uint32_t df, + uint32_t n) +{ +#define MASK_MSA_ELM(op) (MASK_MSA_MINOR(op) | (op & (0xf << 22))) + uint8_t ws = (ctx->opcode >> 11) & 0x1f; + uint8_t wd = (ctx->opcode >> 6) & 0x1f; + + TCGv_i32 tws = tcg_const_i32(ws); + TCGv_i32 twd = tcg_const_i32(wd); + TCGv_i32 tn = tcg_const_i32(n); + TCGv_i32 tdf = tcg_const_i32(df); + + switch (MASK_MSA_ELM(ctx->opcode)) { + case OPC_SLDI_df: + gen_helper_msa_sldi_df(cpu_env, tdf, twd, tws, tn); + break; + case OPC_SPLATI_df: + gen_helper_msa_splati_df(cpu_env, tdf, twd, tws, tn); + break; + case OPC_INSVE_df: + gen_helper_msa_insve_df(cpu_env, tdf, twd, tws, tn); + break; + case OPC_COPY_S_df: + case OPC_COPY_U_df: + case OPC_INSERT_df: +#if !defined(TARGET_MIPS64) + /* Double format valid only for MIPS64 */ + if (df == DF_DOUBLE) { + generate_exception(ctx, EXCP_RI); + break; + } +#endif + switch (MASK_MSA_ELM(ctx->opcode)) { + case OPC_COPY_S_df: + gen_helper_msa_copy_s_df(cpu_env, tdf, twd, tws, tn); + break; + case OPC_COPY_U_df: + gen_helper_msa_copy_u_df(cpu_env, tdf, twd, tws, tn); + break; + case OPC_INSERT_df: + gen_helper_msa_insert_df(cpu_env, tdf, twd, tws, tn); + break; + } + break; + default: + MIPS_INVAL("MSA instruction"); + generate_exception(ctx, EXCP_RI); + } + tcg_temp_free_i32(twd); + tcg_temp_free_i32(tws); + tcg_temp_free_i32(tn); + tcg_temp_free_i32(tdf); +} + +static void gen_msa_elm(CPUMIPSState *env, DisasContext *ctx) +{ + uint8_t dfn = (ctx->opcode >> 16) & 0x3f; + uint32_t df = 0, n = 0; + + if ((dfn & 0x30) == 0x00) { + n = dfn & 0x0f; + df = DF_BYTE; + } else if ((dfn & 0x38) == 0x20) { + n = dfn & 0x07; + df = DF_HALF; + } else if ((dfn & 0x3c) == 0x30) { + n = dfn & 0x03; + df = DF_WORD; + } else if ((dfn & 0x3e) == 0x38) { + n = dfn & 0x01; + df = DF_DOUBLE; + } else if (dfn == 0x3E) { + /* CTCMSA, CFCMSA, MOVE.V */ + gen_msa_elm_3e(env, ctx); + return; + } else { + generate_exception(ctx, EXCP_RI); + return; + } + + gen_msa_elm_df(env, ctx, df, n); +} + +static void gen_msa_3rf(CPUMIPSState *env, DisasContext *ctx) +{ +#define MASK_MSA_3RF(op) (MASK_MSA_MINOR(op) | (op & (0xf << 22))) + uint8_t df = (ctx->opcode >> 21) & 0x1; + uint8_t wt = (ctx->opcode >> 16) & 0x1f; + uint8_t ws = (ctx->opcode >> 11) & 0x1f; + uint8_t wd = (ctx->opcode >> 6) & 0x1f; + + TCGv_i32 twd = tcg_const_i32(wd); + TCGv_i32 tws = tcg_const_i32(ws); + TCGv_i32 twt = tcg_const_i32(wt); + TCGv_i32 tdf = tcg_temp_new_i32(); + + /* adjust df value for floating-point instruction */ + tcg_gen_movi_i32(tdf, df + 2); + + switch (MASK_MSA_3RF(ctx->opcode)) { + case OPC_FCAF_df: + gen_helper_msa_fcaf_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FADD_df: + gen_helper_msa_fadd_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FCUN_df: + gen_helper_msa_fcun_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FSUB_df: + gen_helper_msa_fsub_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FCOR_df: + gen_helper_msa_fcor_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FCEQ_df: + gen_helper_msa_fceq_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FMUL_df: + gen_helper_msa_fmul_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FCUNE_df: + gen_helper_msa_fcune_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FCUEQ_df: + gen_helper_msa_fcueq_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FDIV_df: + gen_helper_msa_fdiv_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FCNE_df: + gen_helper_msa_fcne_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FCLT_df: + gen_helper_msa_fclt_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FMADD_df: + gen_helper_msa_fmadd_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_MUL_Q_df: + tcg_gen_movi_i32(tdf, df + 1); + gen_helper_msa_mul_q_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FCULT_df: + gen_helper_msa_fcult_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FMSUB_df: + gen_helper_msa_fmsub_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_MADD_Q_df: + tcg_gen_movi_i32(tdf, df + 1); + gen_helper_msa_madd_q_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FCLE_df: + gen_helper_msa_fcle_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_MSUB_Q_df: + tcg_gen_movi_i32(tdf, df + 1); + gen_helper_msa_msub_q_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FCULE_df: + gen_helper_msa_fcule_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FEXP2_df: + gen_helper_msa_fexp2_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FSAF_df: + gen_helper_msa_fsaf_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FEXDO_df: + gen_helper_msa_fexdo_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FSUN_df: + gen_helper_msa_fsun_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FSOR_df: + gen_helper_msa_fsor_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FSEQ_df: + gen_helper_msa_fseq_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FTQ_df: + gen_helper_msa_ftq_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FSUNE_df: + gen_helper_msa_fsune_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FSUEQ_df: + gen_helper_msa_fsueq_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FSNE_df: + gen_helper_msa_fsne_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FSLT_df: + gen_helper_msa_fslt_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FMIN_df: + gen_helper_msa_fmin_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_MULR_Q_df: + tcg_gen_movi_i32(tdf, df + 1); + gen_helper_msa_mulr_q_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FSULT_df: + gen_helper_msa_fsult_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FMIN_A_df: + gen_helper_msa_fmin_a_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_MADDR_Q_df: + tcg_gen_movi_i32(tdf, df + 1); + gen_helper_msa_maddr_q_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FSLE_df: + gen_helper_msa_fsle_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FMAX_df: + gen_helper_msa_fmax_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_MSUBR_Q_df: + tcg_gen_movi_i32(tdf, df + 1); + gen_helper_msa_msubr_q_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FSULE_df: + gen_helper_msa_fsule_df(cpu_env, tdf, twd, tws, twt); + break; + case OPC_FMAX_A_df: + gen_helper_msa_fmax_a_df(cpu_env, tdf, twd, tws, twt); + break; + default: + MIPS_INVAL("MSA instruction"); + generate_exception(ctx, EXCP_RI); + break; + } + + tcg_temp_free_i32(twd); + tcg_temp_free_i32(tws); + tcg_temp_free_i32(twt); + tcg_temp_free_i32(tdf); +} + +static void gen_msa_2r(CPUMIPSState *env, DisasContext *ctx) +{ +#define MASK_MSA_2R(op) (MASK_MSA_MINOR(op) | (op & (0x1f << 21)) | \ + (op & (0x7 << 18))) + uint8_t wt = (ctx->opcode >> 16) & 0x1f; + uint8_t ws = (ctx->opcode >> 11) & 0x1f; + uint8_t wd = (ctx->opcode >> 6) & 0x1f; + uint8_t df = (ctx->opcode >> 16) & 0x3; + TCGv_i32 twd = tcg_const_i32(wd); + TCGv_i32 tws = tcg_const_i32(ws); + TCGv_i32 twt = tcg_const_i32(wt); + TCGv_i32 tdf = tcg_const_i32(df); + + switch (MASK_MSA_2R(ctx->opcode)) { + case OPC_FILL_df: +#if !defined(TARGET_MIPS64) + /* Double format valid only for MIPS64 */ + if (df == DF_DOUBLE) { + generate_exception(ctx, EXCP_RI); + break; + } +#endif + gen_helper_msa_fill_df(cpu_env, tdf, twd, tws); /* trs */ + break; + case OPC_PCNT_df: + gen_helper_msa_pcnt_df(cpu_env, tdf, twd, tws); + break; + case OPC_NLOC_df: + gen_helper_msa_nloc_df(cpu_env, tdf, twd, tws); + break; + case OPC_NLZC_df: + gen_helper_msa_nlzc_df(cpu_env, tdf, twd, tws); + break; + default: + MIPS_INVAL("MSA instruction"); + generate_exception(ctx, EXCP_RI); + break; + } + + tcg_temp_free_i32(twd); + tcg_temp_free_i32(tws); + tcg_temp_free_i32(twt); + tcg_temp_free_i32(tdf); +} + +static void gen_msa_2rf(CPUMIPSState *env, DisasContext *ctx) +{ +#define MASK_MSA_2RF(op) (MASK_MSA_MINOR(op) | (op & (0x1f << 21)) | \ + (op & (0xf << 17))) + uint8_t wt = (ctx->opcode >> 16) & 0x1f; + uint8_t ws = (ctx->opcode >> 11) & 0x1f; + uint8_t wd = (ctx->opcode >> 6) & 0x1f; + uint8_t df = (ctx->opcode >> 16) & 0x1; + TCGv_i32 twd = tcg_const_i32(wd); + TCGv_i32 tws = tcg_const_i32(ws); + TCGv_i32 twt = tcg_const_i32(wt); + /* adjust df value for floating-point instruction */ + TCGv_i32 tdf = tcg_const_i32(df + 2); + + switch (MASK_MSA_2RF(ctx->opcode)) { + case OPC_FCLASS_df: + gen_helper_msa_fclass_df(cpu_env, tdf, twd, tws); + break; + case OPC_FTRUNC_S_df: + gen_helper_msa_ftrunc_s_df(cpu_env, tdf, twd, tws); + break; + case OPC_FTRUNC_U_df: + gen_helper_msa_ftrunc_u_df(cpu_env, tdf, twd, tws); + break; + case OPC_FSQRT_df: + gen_helper_msa_fsqrt_df(cpu_env, tdf, twd, tws); + break; + case OPC_FRSQRT_df: + gen_helper_msa_frsqrt_df(cpu_env, tdf, twd, tws); + break; + case OPC_FRCP_df: + gen_helper_msa_frcp_df(cpu_env, tdf, twd, tws); + break; + case OPC_FRINT_df: + gen_helper_msa_frint_df(cpu_env, tdf, twd, tws); + break; + case OPC_FLOG2_df: + gen_helper_msa_flog2_df(cpu_env, tdf, twd, tws); + break; + case OPC_FEXUPL_df: + gen_helper_msa_fexupl_df(cpu_env, tdf, twd, tws); + break; + case OPC_FEXUPR_df: + gen_helper_msa_fexupr_df(cpu_env, tdf, twd, tws); + break; + case OPC_FFQL_df: + gen_helper_msa_ffql_df(cpu_env, tdf, twd, tws); + break; + case OPC_FFQR_df: + gen_helper_msa_ffqr_df(cpu_env, tdf, twd, tws); + break; + case OPC_FTINT_S_df: + gen_helper_msa_ftint_s_df(cpu_env, tdf, twd, tws); + break; + case OPC_FTINT_U_df: + gen_helper_msa_ftint_u_df(cpu_env, tdf, twd, tws); + break; + case OPC_FFINT_S_df: + gen_helper_msa_ffint_s_df(cpu_env, tdf, twd, tws); + break; + case OPC_FFINT_U_df: + gen_helper_msa_ffint_u_df(cpu_env, tdf, twd, tws); + break; + } + + tcg_temp_free_i32(twd); + tcg_temp_free_i32(tws); + tcg_temp_free_i32(twt); + tcg_temp_free_i32(tdf); +} + +static void gen_msa_vec_v(CPUMIPSState *env, DisasContext *ctx) +{ +#define MASK_MSA_VEC(op) (MASK_MSA_MINOR(op) | (op & (0x1f << 21))) + uint8_t wt = (ctx->opcode >> 16) & 0x1f; + uint8_t ws = (ctx->opcode >> 11) & 0x1f; + uint8_t wd = (ctx->opcode >> 6) & 0x1f; + TCGv_i32 twd = tcg_const_i32(wd); + TCGv_i32 tws = tcg_const_i32(ws); + TCGv_i32 twt = tcg_const_i32(wt); + + switch (MASK_MSA_VEC(ctx->opcode)) { + case OPC_AND_V: + gen_helper_msa_and_v(cpu_env, twd, tws, twt); + break; + case OPC_OR_V: + gen_helper_msa_or_v(cpu_env, twd, tws, twt); + break; + case OPC_NOR_V: + gen_helper_msa_nor_v(cpu_env, twd, tws, twt); + break; + case OPC_XOR_V: + gen_helper_msa_xor_v(cpu_env, twd, tws, twt); + break; + case OPC_BMNZ_V: + gen_helper_msa_bmnz_v(cpu_env, twd, tws, twt); + break; + case OPC_BMZ_V: + gen_helper_msa_bmz_v(cpu_env, twd, tws, twt); + break; + case OPC_BSEL_V: + gen_helper_msa_bsel_v(cpu_env, twd, tws, twt); + break; + default: + MIPS_INVAL("MSA instruction"); + generate_exception(ctx, EXCP_RI); + break; + } + + tcg_temp_free_i32(twd); + tcg_temp_free_i32(tws); + tcg_temp_free_i32(twt); +} + +static void gen_msa_vec(CPUMIPSState *env, DisasContext *ctx) +{ + switch (MASK_MSA_VEC(ctx->opcode)) { + case OPC_AND_V: + case OPC_OR_V: + case OPC_NOR_V: + case OPC_XOR_V: + case OPC_BMNZ_V: + case OPC_BMZ_V: + case OPC_BSEL_V: + gen_msa_vec_v(env, ctx); + break; + case OPC_MSA_2R: + gen_msa_2r(env, ctx); + break; + case OPC_MSA_2RF: + gen_msa_2rf(env, ctx); + break; + default: + MIPS_INVAL("MSA instruction"); + generate_exception(ctx, EXCP_RI); + break; + } +} + +static void gen_msa(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = ctx->opcode; + check_insn(ctx, ASE_MSA); + check_msa_access(ctx); + + switch (MASK_MSA_MINOR(opcode)) { + case OPC_MSA_I8_00: + case OPC_MSA_I8_01: + case OPC_MSA_I8_02: + gen_msa_i8(env, ctx); + break; + case OPC_MSA_I5_06: + case OPC_MSA_I5_07: + gen_msa_i5(env, ctx); + break; + case OPC_MSA_BIT_09: + case OPC_MSA_BIT_0A: + gen_msa_bit(env, ctx); + break; + case OPC_MSA_3R_0D: + case OPC_MSA_3R_0E: + case OPC_MSA_3R_0F: + case OPC_MSA_3R_10: + case OPC_MSA_3R_11: + case OPC_MSA_3R_12: + case OPC_MSA_3R_13: + case OPC_MSA_3R_14: + case OPC_MSA_3R_15: + gen_msa_3r(env, ctx); + break; + case OPC_MSA_ELM: + gen_msa_elm(env, ctx); + break; + case OPC_MSA_3RF_1A: + case OPC_MSA_3RF_1B: + case OPC_MSA_3RF_1C: + gen_msa_3rf(env, ctx); + break; + case OPC_MSA_VEC: + gen_msa_vec(env, ctx); + break; + case OPC_LD_B: + case OPC_LD_H: + case OPC_LD_W: + case OPC_LD_D: + case OPC_ST_B: + case OPC_ST_H: + case OPC_ST_W: + case OPC_ST_D: + { + int32_t s10 = sextract32(ctx->opcode, 16, 10); + uint8_t rs = (ctx->opcode >> 11) & 0x1f; + uint8_t wd = (ctx->opcode >> 6) & 0x1f; + uint8_t df = (ctx->opcode >> 0) & 0x3; + + TCGv_i32 tdf = tcg_const_i32(df); + TCGv_i32 twd = tcg_const_i32(wd); + TCGv_i32 trs = tcg_const_i32(rs); + TCGv_i32 ts10 = tcg_const_i32(s10); + + switch (MASK_MSA_MINOR(opcode)) { + case OPC_LD_B: + case OPC_LD_H: + case OPC_LD_W: + case OPC_LD_D: + gen_helper_msa_ld_df(cpu_env, tdf, twd, trs, ts10); + break; + case OPC_ST_B: + case OPC_ST_H: + case OPC_ST_W: + case OPC_ST_D: + gen_helper_msa_st_df(cpu_env, tdf, twd, trs, ts10); + break; + } + + tcg_temp_free_i32(twd); + tcg_temp_free_i32(tdf); + tcg_temp_free_i32(trs); + tcg_temp_free_i32(ts10); + } + break; + default: + MIPS_INVAL("MSA instruction"); + generate_exception(ctx, EXCP_RI); + break; + } + +} + static void decode_opc (CPUMIPSState *env, DisasContext *ctx) { int32_t offset; @@ -16779,7 +18350,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) /* make sure instructions are on a word boundary */ if (ctx->pc & 0x3) { env->CP0_BadVAddr = ctx->pc; - generate_exception(ctx, EXCP_AdEL); + generate_exception_err(ctx, EXCP_AdEL, EXCP_INST_NOTAVAIL); return; } @@ -17090,133 +18661,152 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) break; case OPC_CP1: - if (ctx->CP0_Config1 & (1 << CP0C1_FP)) { + op1 = MASK_CP1(ctx->opcode); + + switch (op1) { + case OPC_MFHC1: + case OPC_MTHC1: check_cp1_enabled(ctx); - op1 = MASK_CP1(ctx->opcode); - switch (op1) { - case OPC_MFHC1: - case OPC_MTHC1: - check_insn(ctx, ISA_MIPS32R2); - case OPC_MFC1: - case OPC_CFC1: - case OPC_MTC1: - case OPC_CTC1: - gen_cp1(ctx, op1, rt, rd); - break; + check_insn(ctx, ISA_MIPS32R2); + case OPC_MFC1: + case OPC_CFC1: + case OPC_MTC1: + case OPC_CTC1: + check_cp1_enabled(ctx); + gen_cp1(ctx, op1, rt, rd); + break; #if defined(TARGET_MIPS64) - case OPC_DMFC1: - case OPC_DMTC1: - check_insn(ctx, ISA_MIPS3); - gen_cp1(ctx, op1, rt, rd); - break; + case OPC_DMFC1: + case OPC_DMTC1: + check_cp1_enabled(ctx); + check_insn(ctx, ISA_MIPS3); + gen_cp1(ctx, op1, rt, rd); + break; #endif - case OPC_BC1EQZ: /* OPC_BC1ANY2 */ - if (ctx->insn_flags & ISA_MIPS32R6) { - /* OPC_BC1EQZ */ - gen_compute_branch1_r6(ctx, MASK_CP1(ctx->opcode), - rt, imm << 2); - } else { - /* OPC_BC1ANY2 */ - check_cop1x(ctx); - check_insn(ctx, ASE_MIPS3D); - gen_compute_branch1(ctx, MASK_BC1(ctx->opcode), - (rt >> 2) & 0x7, imm << 2); - } - break; - case OPC_BC1NEZ: - check_insn(ctx, ISA_MIPS32R6); + case OPC_BC1EQZ: /* OPC_BC1ANY2 */ + check_cp1_enabled(ctx); + if (ctx->insn_flags & ISA_MIPS32R6) { + /* OPC_BC1EQZ */ gen_compute_branch1_r6(ctx, MASK_CP1(ctx->opcode), rt, imm << 2); - break; - case OPC_BC1ANY4: - check_insn_opc_removed(ctx, ISA_MIPS32R6); + } else { + /* OPC_BC1ANY2 */ check_cop1x(ctx); check_insn(ctx, ASE_MIPS3D); - /* fall through */ - case OPC_BC1: - check_insn_opc_removed(ctx, ISA_MIPS32R6); gen_compute_branch1(ctx, MASK_BC1(ctx->opcode), (rt >> 2) & 0x7, imm << 2); - break; - case OPC_PS_FMT: - check_insn_opc_removed(ctx, ISA_MIPS32R6); - case OPC_S_FMT: - case OPC_D_FMT: - gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa, - (imm >> 8) & 0x7); - break; - case OPC_W_FMT: - case OPC_L_FMT: - { - int r6_op = ctx->opcode & FOP(0x3f, 0x1f); - if (ctx->insn_flags & ISA_MIPS32R6) { - switch (r6_op) { - case R6_OPC_CMP_AF_S: - case R6_OPC_CMP_UN_S: - case R6_OPC_CMP_EQ_S: - case R6_OPC_CMP_UEQ_S: - case R6_OPC_CMP_LT_S: - case R6_OPC_CMP_ULT_S: - case R6_OPC_CMP_LE_S: - case R6_OPC_CMP_ULE_S: - case R6_OPC_CMP_SAF_S: - case R6_OPC_CMP_SUN_S: - case R6_OPC_CMP_SEQ_S: - case R6_OPC_CMP_SEUQ_S: - case R6_OPC_CMP_SLT_S: - case R6_OPC_CMP_SULT_S: - case R6_OPC_CMP_SLE_S: - case R6_OPC_CMP_SULE_S: - case R6_OPC_CMP_OR_S: - case R6_OPC_CMP_UNE_S: - case R6_OPC_CMP_NE_S: - case R6_OPC_CMP_SOR_S: - case R6_OPC_CMP_SUNE_S: - case R6_OPC_CMP_SNE_S: - gen_r6_cmp_s(ctx, ctx->opcode & 0x1f, rt, rd, sa); - break; - case R6_OPC_CMP_AF_D: - case R6_OPC_CMP_UN_D: - case R6_OPC_CMP_EQ_D: - case R6_OPC_CMP_UEQ_D: - case R6_OPC_CMP_LT_D: - case R6_OPC_CMP_ULT_D: - case R6_OPC_CMP_LE_D: - case R6_OPC_CMP_ULE_D: - case R6_OPC_CMP_SAF_D: - case R6_OPC_CMP_SUN_D: - case R6_OPC_CMP_SEQ_D: - case R6_OPC_CMP_SEUQ_D: - case R6_OPC_CMP_SLT_D: - case R6_OPC_CMP_SULT_D: - case R6_OPC_CMP_SLE_D: - case R6_OPC_CMP_SULE_D: - case R6_OPC_CMP_OR_D: - case R6_OPC_CMP_UNE_D: - case R6_OPC_CMP_NE_D: - case R6_OPC_CMP_SOR_D: - case R6_OPC_CMP_SUNE_D: - case R6_OPC_CMP_SNE_D: - gen_r6_cmp_d(ctx, ctx->opcode & 0x1f, rt, rd, sa); - break; - default: - gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa, - (imm >> 8) & 0x7); - break; - } - } else { + } + break; + case OPC_BC1NEZ: + check_cp1_enabled(ctx); + check_insn(ctx, ISA_MIPS32R6); + gen_compute_branch1_r6(ctx, MASK_CP1(ctx->opcode), + rt, imm << 2); + break; + case OPC_BC1ANY4: + check_cp1_enabled(ctx); + check_insn_opc_removed(ctx, ISA_MIPS32R6); + check_cop1x(ctx); + check_insn(ctx, ASE_MIPS3D); + /* fall through */ + case OPC_BC1: + check_cp1_enabled(ctx); + check_insn_opc_removed(ctx, ISA_MIPS32R6); + gen_compute_branch1(ctx, MASK_BC1(ctx->opcode), + (rt >> 2) & 0x7, imm << 2); + break; + case OPC_PS_FMT: + check_cp1_enabled(ctx); + check_insn_opc_removed(ctx, ISA_MIPS32R6); + case OPC_S_FMT: + case OPC_D_FMT: + check_cp1_enabled(ctx); + gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa, + (imm >> 8) & 0x7); + break; + case OPC_W_FMT: + case OPC_L_FMT: + { + int r6_op = ctx->opcode & FOP(0x3f, 0x1f); + check_cp1_enabled(ctx); + if (ctx->insn_flags & ISA_MIPS32R6) { + switch (r6_op) { + case R6_OPC_CMP_AF_S: + case R6_OPC_CMP_UN_S: + case R6_OPC_CMP_EQ_S: + case R6_OPC_CMP_UEQ_S: + case R6_OPC_CMP_LT_S: + case R6_OPC_CMP_ULT_S: + case R6_OPC_CMP_LE_S: + case R6_OPC_CMP_ULE_S: + case R6_OPC_CMP_SAF_S: + case R6_OPC_CMP_SUN_S: + case R6_OPC_CMP_SEQ_S: + case R6_OPC_CMP_SEUQ_S: + case R6_OPC_CMP_SLT_S: + case R6_OPC_CMP_SULT_S: + case R6_OPC_CMP_SLE_S: + case R6_OPC_CMP_SULE_S: + case R6_OPC_CMP_OR_S: + case R6_OPC_CMP_UNE_S: + case R6_OPC_CMP_NE_S: + case R6_OPC_CMP_SOR_S: + case R6_OPC_CMP_SUNE_S: + case R6_OPC_CMP_SNE_S: + gen_r6_cmp_s(ctx, ctx->opcode & 0x1f, rt, rd, sa); + break; + case R6_OPC_CMP_AF_D: + case R6_OPC_CMP_UN_D: + case R6_OPC_CMP_EQ_D: + case R6_OPC_CMP_UEQ_D: + case R6_OPC_CMP_LT_D: + case R6_OPC_CMP_ULT_D: + case R6_OPC_CMP_LE_D: + case R6_OPC_CMP_ULE_D: + case R6_OPC_CMP_SAF_D: + case R6_OPC_CMP_SUN_D: + case R6_OPC_CMP_SEQ_D: + case R6_OPC_CMP_SEUQ_D: + case R6_OPC_CMP_SLT_D: + case R6_OPC_CMP_SULT_D: + case R6_OPC_CMP_SLE_D: + case R6_OPC_CMP_SULE_D: + case R6_OPC_CMP_OR_D: + case R6_OPC_CMP_UNE_D: + case R6_OPC_CMP_NE_D: + case R6_OPC_CMP_SOR_D: + case R6_OPC_CMP_SUNE_D: + case R6_OPC_CMP_SNE_D: + gen_r6_cmp_d(ctx, ctx->opcode & 0x1f, rt, rd, sa); + break; + default: gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa, (imm >> 8) & 0x7); + break; } - break; - } - default: - MIPS_INVAL("cp1"); - generate_exception (ctx, EXCP_RI); - break; + } else { + gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa, + (imm >> 8) & 0x7); } - } else { - generate_exception_err(ctx, EXCP_CpU, 1); + break; + } + case OPC_BZ_V: + case OPC_BNZ_V: + case OPC_BZ_B: + case OPC_BZ_H: + case OPC_BZ_W: + case OPC_BZ_D: + case OPC_BNZ_B: + case OPC_BNZ_H: + case OPC_BNZ_W: + case OPC_BNZ_D: + check_insn(ctx, ASE_MSA); + gen_msa_branch(env, ctx, op1); + break; + default: + MIPS_INVAL("cp1"); + generate_exception(ctx, EXCP_RI); + break; } break; @@ -17371,9 +18961,9 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx) gen_compute_branch(ctx, op, 4, rs, rt, offset, 4); } break; - case OPC_MDMX: - check_insn(ctx, ASE_MDMX); + case OPC_MSA: /* OPC_MDMX */ /* MDMX: Not implemented. */ + gen_msa(env, ctx); break; case OPC_PCREL: check_insn(ctx, ISA_MIPS32R6); @@ -17400,7 +18990,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, int num_insns; int max_insns; int insn_bytes; - int is_delay; + int is_slot; if (search_pc) qemu_log("search pc %d\n", search_pc); @@ -17414,6 +19004,11 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, ctx.CP0_Config1 = env->CP0_Config1; ctx.tb = tb; ctx.bstate = BS_NONE; + ctx.kscrexist = (env->CP0_Config4 >> CP0C4_KScrExist) & 0xff; + ctx.rxi = (env->CP0_Config3 >> CP0C3_RXI) & 1; + ctx.ie = (env->CP0_Config4 >> CP0C4_IE) & 3; + ctx.bi = (env->CP0_Config3 >> CP0C3_BI) & 1; + ctx.bp = (env->CP0_Config3 >> CP0C3_BP) & 1; /* Restore delay slot state from the tb context. */ ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */ ctx.ulri = env->CP0_Config3 & (1 << CP0C3_ULRI); @@ -17460,7 +19055,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) gen_io_start(); - is_delay = ctx.hflags & MIPS_HFLAG_BMASK; + is_slot = ctx.hflags & MIPS_HFLAG_BMASK; if (!(ctx.hflags & MIPS_HFLAG_M16)) { ctx.opcode = cpu_ldl_code(env, ctx.pc); insn_bytes = 4; @@ -17478,12 +19073,14 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, } if (ctx.hflags & MIPS_HFLAG_BMASK) { - if (!(ctx.hflags & (MIPS_HFLAG_BDS16 | MIPS_HFLAG_BDS32))) { - is_delay = 1; - /* force to generate branch as no delay slot is required */ + if (!(ctx.hflags & (MIPS_HFLAG_BDS16 | MIPS_HFLAG_BDS32 | + MIPS_HFLAG_FBNSLOT))) { + /* force to generate branch as there is neither delay nor + forbidden slot */ + is_slot = 1; } } - if (is_delay) { + if (is_slot) { gen_branch(&ctx, insn_bytes); } ctx.pc += insn_bytes; @@ -17515,7 +19112,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, gen_io_end(); } if (cs->singlestep_enabled && ctx.bstate != BS_BRANCH) { - save_cpu_state(&ctx, ctx.bstate == BS_NONE); + save_cpu_state(&ctx, ctx.bstate != BS_EXCP); gen_helper_0e0i(raise_exception, EXCP_DEBUG); } else { switch (ctx.bstate) { @@ -17688,8 +19285,15 @@ void mips_tcg_init(void) regnames[i]); for (i = 0; i < 32; i++) { - int off = offsetof(CPUMIPSState, active_fpu.fpr[i]); - fpu_f64[i] = tcg_global_mem_new_i64(TCG_AREG0, off, fregnames[i]); + int off = offsetof(CPUMIPSState, active_fpu.fpr[i].wr.d[0]); + msa_wr_d[i * 2] = + tcg_global_mem_new_i64(TCG_AREG0, off, msaregnames[i * 2]); + /* The scalar floating-point unit (FPU) registers are mapped on + * the MSA vector registers. */ + fpu_f64[i] = msa_wr_d[i * 2]; + off = offsetof(CPUMIPSState, active_fpu.fpr[i].wr.d[1]); + msa_wr_d[i * 2 + 1] = + tcg_global_mem_new_i64(TCG_AREG0, off, msaregnames[i * 2 + 1]); } cpu_PC = tcg_global_mem_new(TCG_AREG0, @@ -17796,7 +19400,10 @@ void cpu_state_reset(CPUMIPSState *env) env->CP0_SRSConf3 = env->cpu_model->CP0_SRSConf3; env->CP0_SRSConf4_rw_bitmask = env->cpu_model->CP0_SRSConf4_rw_bitmask; env->CP0_SRSConf4 = env->cpu_model->CP0_SRSConf4; + env->CP0_PageGrain_rw_bitmask = env->cpu_model->CP0_PageGrain_rw_bitmask; + env->CP0_PageGrain = env->cpu_model->CP0_PageGrain; env->active_fpu.fcr0 = env->cpu_model->CP1_fcr0; + env->msair = env->cpu_model->MSAIR; env->insn_flags = env->cpu_model->insn_flags; #if defined(CONFIG_USER_ONLY) @@ -17894,6 +19501,11 @@ void cpu_state_reset(CPUMIPSState *env) env->CP0_Status |= (1 << CP0St_FR); } + /* MSA */ + if (env->CP0_Config3 & (1 << CP0C3_MSAP)) { + msa_reset(env); + } + compute_hflags(env); cs->exception_index = EXCP_NONE; } diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c index 67b7837750..148b394cf0 100644 --- a/target-mips/translate_init.c +++ b/target-mips/translate_init.c @@ -84,6 +84,7 @@ struct mips_def_t { int32_t CP0_TCStatus_rw_bitmask; int32_t CP0_SRSCtl; int32_t CP1_fcr0; + int32_t MSAIR; int32_t SEGBITS; int32_t PABITS; int32_t CP0_SRSConf0_rw_bitmask; @@ -96,6 +97,8 @@ struct mips_def_t { int32_t CP0_SRSConf3; int32_t CP0_SRSConf4_rw_bitmask; int32_t CP0_SRSConf4; + int32_t CP0_PageGrain_rw_bitmask; + int32_t CP0_PageGrain; int insn_flags; enum mips_mmu_types mmu_type; }; @@ -330,7 +333,8 @@ static const mips_def_t mips_defs[] = (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | (1 << CP0C1_CA), .CP0_Config2 = MIPS_CONFIG2, - .CP0_Config3 = MIPS_CONFIG3 | (0 << CP0C3_VInt) | (1 << CP0C3_DSPP), + .CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_DSP2P) | (1 << CP0C3_DSPP) | + (0 << CP0C3_VInt), .CP0_LLAddr_rw_bitmask = 0, .CP0_LLAddr_shift = 4, .SYNCI_Step = 32, @@ -355,7 +359,7 @@ static const mips_def_t mips_defs[] = (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | (1 << CP0C1_CA), .CP0_Config2 = MIPS_CONFIG2, - .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M), + .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) | (1 << CP0C3_MSAP), .CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M), .CP0_Config4_rw_bitmask = 0, .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_UFR), @@ -373,7 +377,7 @@ static const mips_def_t mips_defs[] = (0x93 << FCR0_PRID), .SEGBITS = 32, .PABITS = 32, - .insn_flags = CPU_MIPS32R5 | ASE_MIPS16 | ASE_DSP | ASE_DSPR2, + .insn_flags = CPU_MIPS32R5 | ASE_MIPS16 | ASE_MSA, .mmu_type = MMU_TYPE_R4000, }, #if defined(TARGET_MIPS64) @@ -517,7 +521,7 @@ static const mips_def_t mips_defs[] = }, { /* A generic CPU supporting MIPS64 Release 6 ISA. - FIXME: It does not support all the MIPS64R6 features yet. + FIXME: Support IEEE 754-2008 FP and misaligned memory accesses. Eventually this should be replaced by a real CPU model. */ .name = "MIPS64R6-generic", .CP0_PRid = 0x00010000, @@ -528,12 +532,19 @@ static const mips_def_t mips_defs[] = (2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) | (0 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), .CP0_Config2 = MIPS_CONFIG2, - .CP0_Config3 = MIPS_CONFIG3, + .CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_RXI) | (1 << CP0C3_BP) | + (1 << CP0C3_BI) | (1 << CP0C3_ULRI) | (1U << CP0C3_M), + .CP0_Config4 = MIPS_CONFIG4 | (0xfc << CP0C4_KScrExist) | + (3 << CP0C4_IE) | (1 << CP0C4_M), + .CP0_Config5_rw_bitmask = (1 << CP0C5_SBRI), .CP0_LLAddr_rw_bitmask = 0, .CP0_LLAddr_shift = 0, .SYNCI_Step = 32, .CCRes = 2, .CP0_Status_rw_bitmask = 0x30D8FFFF, + .CP0_PageGrain = (1 << CP0PG_IEC) | (1 << CP0PG_XIE) | + (1U << CP0PG_RIE), + .CP0_PageGrain_rw_bitmask = 0, .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) | (1 << FCR0_S) | (0x00 << FCR0_PRID) | (0x0 << FCR0_REV), @@ -591,7 +602,8 @@ static const mips_def_t mips_defs[] = (2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) | (1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), .CP0_Config2 = MIPS_CONFIG2, - .CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_LPA), + .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) | (1 << CP0C3_DSP2P) | + (1 << CP0C3_DSPP) | (1 << CP0C3_LPA), .CP0_LLAddr_rw_bitmask = 0, .CP0_LLAddr_shift = 0, .SYNCI_Step = 32, @@ -655,6 +667,8 @@ static void r4k_mmu_init (CPUMIPSState *env, const mips_def_t *def) env->tlb->helper_tlbwr = r4k_helper_tlbwr; env->tlb->helper_tlbp = r4k_helper_tlbp; env->tlb->helper_tlbr = r4k_helper_tlbr; + env->tlb->helper_tlbinv = r4k_helper_tlbinv; + env->tlb->helper_tlbinvf = r4k_helper_tlbinvf; } static void mmu_init (CPUMIPSState *env, const mips_def_t *def) @@ -718,3 +732,36 @@ static void mvp_init (CPUMIPSState *env, const mips_def_t *def) (0x0 << CP0MVPC1_PCX) | (0x0 << CP0MVPC1_PCP2) | (0x1 << CP0MVPC1_PCP1); } + +static void msa_reset(CPUMIPSState *env) +{ +#ifdef CONFIG_USER_ONLY + /* MSA access enabled */ + env->CP0_Config5 |= 1 << CP0C5_MSAEn; + env->CP0_Status |= (1 << CP0St_CU1) | (1 << CP0St_FR); +#endif + + /* MSA CSR: + - non-signaling floating point exception mode off (NX bit is 0) + - Cause, Enables, and Flags are all 0 + - round to nearest / ties to even (RM bits are 0) */ + env->active_tc.msacsr = 0; + + /* tininess detected after rounding.*/ + set_float_detect_tininess(float_tininess_after_rounding, + &env->active_tc.msa_fp_status); + + /* clear float_status exception flags */ + set_float_exception_flags(0, &env->active_tc.msa_fp_status); + + /* set float_status rounding mode */ + set_float_rounding_mode(float_round_nearest_even, + &env->active_tc.msa_fp_status); + + /* set float_status flush modes */ + set_flush_to_zero(0, &env->active_tc.msa_fp_status); + set_flush_inputs_to_zero(0, &env->active_tc.msa_fp_status); + + /* clear float_status nan mode */ + set_default_nan_mode(0, &env->active_tc.msa_fp_status); +} diff --git a/target-ppc/cpu-models.c b/target-ppc/cpu-models.c index 52ac6ec156..3f18996bb0 100644 --- a/target-ppc/cpu-models.c +++ b/target-ppc/cpu-models.c @@ -309,6 +309,9 @@ #endif POWERPC_DEF("440-Xilinx", CPU_POWERPC_440_XILINX, 440x5, "PowerPC 440 Xilinx 5") + + POWERPC_DEF("440-Xilinx-w-dfpu", CPU_POWERPC_440_XILINX, 440x5wDFPU, + "PowerPC 440 Xilinx 5 With a Double Prec. FPU") #if defined(TODO) POWERPC_DEF("440A5", CPU_POWERPC_440A5, 440x5, "PowerPC 440 A5") diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 872456171f..068fcb24a2 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -924,7 +924,8 @@ struct ppc_segment_page_sizes { /* The whole PowerPC CPU context */ #define NB_MMU_MODES 3 -#define PPC_CPU_OPCODES_LEN 0x40 +#define PPC_CPU_OPCODES_LEN 0x40 +#define PPC_CPU_INDIRECT_OPCODES_LEN 0x20 struct CPUPPCState { /* First are the most commonly used resources @@ -2007,13 +2008,16 @@ enum { PPC2_ALTIVEC_207 = 0x0000000000004000ULL, /* PowerISA 2.07 Book3s specification */ PPC2_ISA207S = 0x0000000000008000ULL, + /* Double precision floating point conversion for signed integer 64 */ + PPC2_FP_CVT_S64 = 0x0000000000010000ULL, #define PPC_TCG_INSNS2 (PPC2_BOOKE206 | PPC2_VSX | PPC2_PRCNTL | PPC2_DBRX | \ 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 | PPC2_ISA207S | PPC2_DFP) + PPC2_ALTIVEC_207 | PPC2_ISA207S | PPC2_DFP | \ + PPC2_FP_CVT_S64) }; /*****************************************************************************/ diff --git a/target-ppc/fpu_helper.c b/target-ppc/fpu_helper.c index da93d1215a..7f74466f32 100644 --- a/target-ppc/fpu_helper.c +++ b/target-ppc/fpu_helper.c @@ -649,14 +649,10 @@ 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) 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 - -#if defined(TARGET_PPC64) #define FPU_FCFI(op, cvtr, is_single) \ uint64_t helper_##op(CPUPPCState *env, uint64_t arg) \ @@ -678,8 +674,6 @@ FPU_FCFI(fcfids, int64_to_float32, 1) FPU_FCFI(fcfidu, uint64_to_float64, 0) FPU_FCFI(fcfidus, uint64_to_float32, 1) -#endif - static inline uint64_t do_fri(CPUPPCState *env, uint64_t arg, int rounding_mode) { diff --git a/target-ppc/helper.h b/target-ppc/helper.h index 0cfdc8ab8f..210fd97f6a 100644 --- a/target-ppc/helper.h +++ b/target-ppc/helper.h @@ -66,7 +66,6 @@ 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) @@ -75,7 +74,6 @@ 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) DEF_HELPER_2(friz, i64, env, i64) diff --git a/target-ppc/int_helper.c b/target-ppc/int_helper.c index 713d777076..4c2b71c708 100644 --- a/target-ppc/int_helper.c +++ b/target-ppc/int_helper.c @@ -708,7 +708,7 @@ static inline void vcmpbfp_internal(CPUPPCState *env, ppc_avr_t *r, int le_rel = float32_compare_quiet(a->f[i], b->f[i], &env->vec_status); if (le_rel == float_relation_unordered) { r->u32[i] = 0xc0000000; - /* ALL_IN does not need to be updated here. */ + all_in = 1; } else { float32 bneg = float32_chs(b->f[i]); int ge_rel = float32_compare_quiet(a->f[i], bneg, &env->vec_status); @@ -1552,13 +1552,6 @@ void helper_vlogefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) } } -#if defined(HOST_WORDS_BIGENDIAN) -#define LEFT 0 -#define RIGHT 1 -#else -#define LEFT 1 -#define RIGHT 0 -#endif /* The specification says that the results are undefined if all of the * shift counts are not identical. We check to make sure that they are * to conform to what real hardware appears to do. */ @@ -1588,11 +1581,9 @@ void helper_vlogefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) } \ } \ } -VSHIFT(l, LEFT) -VSHIFT(r, RIGHT) +VSHIFT(l, 1) +VSHIFT(r, 0) #undef VSHIFT -#undef LEFT -#undef RIGHT #define VSL(suffix, element, mask) \ void helper_vsl##suffix(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ @@ -2286,25 +2277,25 @@ uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) 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; + cr = (sgna > 0) ? 1 << CRF_GT : 1 << CRF_LT; } 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; + cr = (sgna > 0) ? 1 << CRF_GT : 1 << CRF_LT; } 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; + cr = (sgnb > 0) ? 1 << CRF_GT : 1 << CRF_LT; } } if (unlikely(invalid)) { result.u64[HI_IDX] = result.u64[LO_IDX] = -1; - cr = 1; + cr = 1 << CRF_SO; } else if (overflow) { - cr |= 1; + cr |= 1 << CRF_SO; } else if (zero) { - cr = 2; + cr = 1 << CRF_EQ; } *r = result; @@ -2352,7 +2343,7 @@ void helper_vcipherlast(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) int i; VECTOR_FOR_INORDER_I(i, u8) { - r->AVRB(i) = b->AVRB(i) ^ (AES_Te4[a->AVRB(AES_shifts[i])] & 0xFF); + r->AVRB(i) = b->AVRB(i) ^ (AES_sbox[a->AVRB(AES_shifts[i])]); } } @@ -2381,7 +2372,7 @@ void helper_vncipherlast(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) int i; VECTOR_FOR_INORDER_I(i, u8) { - r->AVRB(i) = b->AVRB(i) ^ (AES_Td4[a->AVRB(AES_ishifts[i])] & 0xFF); + r->AVRB(i) = b->AVRB(i) ^ (AES_isbox[a->AVRB(AES_ishifts[i])]); } } @@ -2556,6 +2547,7 @@ target_ulong helper_dlmzb(CPUPPCState *env, target_ulong high, } i++; } + i = 8; if (update_Rc) { env->crf[0] = 0x2; } diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 9c23c6ba0d..6843fa0b98 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -1782,7 +1782,7 @@ static int kvmppc_find_cpu_dt(char *buf, int buf_len) * format) */ static uint64_t kvmppc_read_int_cpu_dt(const char *propname) { - char buf[PATH_MAX]; + char buf[PATH_MAX], *tmp; union { uint32_t v32; uint64_t v64; @@ -1794,10 +1794,10 @@ static uint64_t kvmppc_read_int_cpu_dt(const char *propname) return -1; } - strncat(buf, "/", sizeof(buf) - strlen(buf)); - strncat(buf, propname, sizeof(buf) - strlen(buf)); + tmp = g_strdup_printf("%s/%s", buf, propname); - f = fopen(buf, "rb"); + f = fopen(tmp, "rb"); + g_free(tmp); if (!f) { return -1; } diff --git a/target-ppc/translate.c b/target-ppc/translate.c index d03daeaa48..910ce56ec1 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -189,6 +189,7 @@ typedef struct DisasContext { uint32_t opcode; uint32_t exception; /* Routine used to access memory */ + bool pr, hv; int mem_idx; int access_type; /* Translation flags */ @@ -643,20 +644,6 @@ 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) @@ -784,7 +771,7 @@ static void gen_isel(DisasContext *ctx) l1 = gen_new_label(); l2 = gen_new_label(); - mask = 1 << (3 - (bi & 0x03)); + mask = 0x08 >> (bi & 0x03); t0 = tcg_temp_new_i32(); tcg_gen_andi_i32(t0, cpu_crf[bi >> 2], mask); tcg_gen_brcondi_i32(TCG_COND_EQ, t0, 0, l1); @@ -1456,25 +1443,25 @@ static void gen_or(DisasContext *ctx) break; #if !defined(CONFIG_USER_ONLY) case 31: - if (ctx->mem_idx > 0) { + if (!ctx->pr) { /* Set process priority to very low */ prio = 1; } break; case 5: - if (ctx->mem_idx > 0) { + if (!ctx->pr) { /* Set process priority to medium-hight */ prio = 5; } break; case 3: - if (ctx->mem_idx > 0) { + if (!ctx->pr) { /* Set process priority to high */ prio = 6; } break; case 7: - if (ctx->mem_idx > 1) { + if (ctx->hv) { /* Set process priority to very high */ prio = 7; } @@ -2287,9 +2274,8 @@ GEN_FLOAT_B(ctiwz, 0x0F, 0x00, 0, PPC_FLOAT); 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); +GEN_FLOAT_B(cfid, 0x0E, 0x1A, 1, PPC2_FP_CVT_S64); /* fcfids */ GEN_FLOAT_B(cfids, 0x0E, 0x1A, 0, PPC2_FP_CVT_ISA206); /* fcfidu */ @@ -2297,14 +2283,13 @@ 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); +GEN_FLOAT_B(ctid, 0x0E, 0x19, 0, PPC2_FP_CVT_S64); /* fctidu */ GEN_FLOAT_B(ctidu, 0x0E, 0x1D, 0, PPC2_FP_CVT_ISA206); /* fctidz */ -GEN_FLOAT_B(ctidz, 0x0F, 0x19, 0, PPC_64B); +GEN_FLOAT_B(ctidz, 0x0F, 0x19, 0, PPC2_FP_CVT_S64); /* fctidu */ GEN_FLOAT_B(ctiduz, 0x0F, 0x1D, 0, PPC2_FP_CVT_ISA206); -#endif /* frin */ GEN_FLOAT_B(rin, 0x08, 0x0C, 1, PPC_FLOAT_EXT); @@ -2903,7 +2888,7 @@ static void gen_lq(DisasContext *ctx) 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)) { + if (!legal_in_user_mode && ctx->pr) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -3026,7 +3011,7 @@ static void gen_std(DisasContext *ctx) 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)) { + if (!legal_in_user_mode && ctx->pr) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -3889,7 +3874,7 @@ static inline void gen_bcond(DisasContext *ctx, int type) if ((bo & 0x10) == 0) { /* Test CR */ uint32_t bi = BI(ctx->opcode); - uint32_t mask = 1 << (3 - (bi & 0x03)); + uint32_t mask = 0x08 >> (bi & 0x03); TCGv_i32 temp = tcg_temp_new_i32(); if (bo & 0x8) { @@ -3971,7 +3956,7 @@ static void glue(gen_, name)(DisasContext *ctx) else \ tcg_gen_mov_i32(t1, cpu_crf[crbB(ctx->opcode) >> 2]); \ tcg_op(t0, t0, t1); \ - bitmask = 1 << (3 - (crbD(ctx->opcode) & 0x03)); \ + bitmask = 0x08 >> (crbD(ctx->opcode) & 0x03); \ tcg_gen_andi_i32(t0, t0, bitmask); \ tcg_gen_andi_i32(t1, cpu_crf[crbD(ctx->opcode) >> 2], ~bitmask); \ tcg_gen_or_i32(cpu_crf[crbD(ctx->opcode) >> 2], t0, t1); \ @@ -4004,14 +3989,14 @@ static void gen_mcrf(DisasContext *ctx) /*** System linkage ***/ -/* rfi (mem_idx only) */ +/* rfi (supervisor only) */ static void gen_rfi(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else /* Restore CPU state */ - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -4028,7 +4013,7 @@ static void gen_rfid(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else /* Restore CPU state */ - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -4044,7 +4029,7 @@ static void gen_hrfid(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else /* Restore CPU state */ - if (unlikely(ctx->mem_idx <= 1)) { + if (unlikely(!ctx->hv)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -4213,7 +4198,7 @@ static void gen_mfmsr(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } @@ -4237,9 +4222,9 @@ static inline void gen_op_mfspr(DisasContext *ctx) uint32_t sprn = SPR(ctx->opcode); #if !defined(CONFIG_USER_ONLY) - if (ctx->mem_idx == 2) + if (ctx->hv) read_cb = ctx->spr_cb[sprn].hea_read; - else if (ctx->mem_idx) + else if (!ctx->pr) read_cb = ctx->spr_cb[sprn].oea_read; else #endif @@ -4317,7 +4302,7 @@ static void gen_mtmsrd(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } @@ -4348,7 +4333,7 @@ static void gen_mtmsr(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } @@ -4388,9 +4373,9 @@ static void gen_mtspr(DisasContext *ctx) uint32_t sprn = SPR(ctx->opcode); #if !defined(CONFIG_USER_ONLY) - if (ctx->mem_idx == 2) + if (ctx->hv) write_cb = ctx->spr_cb[sprn].hea_write; - else if (ctx->mem_idx) + else if (!ctx->pr) write_cb = ctx->spr_cb[sprn].oea_write; else #endif @@ -4437,7 +4422,7 @@ static void gen_dcbi(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else TCGv EA, val; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -4574,7 +4559,7 @@ static void gen_mfsr(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); #else TCGv t0; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } @@ -4591,7 +4576,7 @@ static void gen_mfsrin(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); #else TCGv t0; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } @@ -4610,7 +4595,7 @@ static void gen_mtsr(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); #else TCGv t0; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } @@ -4627,7 +4612,7 @@ static void gen_mtsrin(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); #else TCGv t0; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } @@ -4649,7 +4634,7 @@ static void gen_mfsr_64b(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); #else TCGv t0; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } @@ -4666,7 +4651,7 @@ static void gen_mfsrin_64b(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); #else TCGv t0; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } @@ -4685,7 +4670,7 @@ static void gen_mtsr_64b(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); #else TCGv t0; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } @@ -4702,7 +4687,7 @@ static void gen_mtsrin_64b(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); #else TCGv t0; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } @@ -4720,7 +4705,7 @@ static void gen_slbmte(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } @@ -4734,7 +4719,7 @@ static void gen_slbmfee(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } @@ -4748,7 +4733,7 @@ static void gen_slbmfev(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } @@ -4759,7 +4744,7 @@ static void gen_slbmfev(DisasContext *ctx) #endif /* defined(TARGET_PPC64) */ /*** Lookaside buffer management ***/ -/* Optional & mem_idx only: */ +/* Optional & supervisor only: */ /* tlbia */ static void gen_tlbia(DisasContext *ctx) @@ -4767,7 +4752,7 @@ static void gen_tlbia(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -4781,7 +4766,7 @@ static void gen_tlbiel(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -4795,7 +4780,7 @@ static void gen_tlbie(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -4816,7 +4801,7 @@ static void gen_tlbsync(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -4834,7 +4819,7 @@ static void gen_slbia(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -4848,7 +4833,7 @@ static void gen_slbie(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -5556,7 +5541,7 @@ static void gen_mfrom(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -5572,7 +5557,7 @@ static void gen_tlbld_6xx(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -5586,7 +5571,7 @@ static void gen_tlbli_6xx(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -5602,7 +5587,7 @@ static void gen_tlbld_74xx(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -5616,7 +5601,7 @@ static void gen_tlbli_74xx(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -5639,7 +5624,7 @@ static void gen_cli(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -5660,7 +5645,7 @@ static void gen_mfsri(DisasContext *ctx) int ra = rA(ctx->opcode); int rd = rD(ctx->opcode); TCGv t0; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -5681,7 +5666,7 @@ static void gen_rac(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else TCGv t0; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -5697,7 +5682,7 @@ static void gen_rfsvc(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -5859,7 +5844,7 @@ static void gen_tlbiva(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else TCGv t0; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6092,7 +6077,7 @@ static void gen_mfdcr(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); #else TCGv dcrn; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } @@ -6111,7 +6096,7 @@ static void gen_mtdcr(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); #else TCGv dcrn; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } @@ -6130,7 +6115,7 @@ static void gen_mfdcrx(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } @@ -6149,7 +6134,7 @@ static void gen_mtdcrx(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } @@ -6187,7 +6172,7 @@ static void gen_dccci(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6202,7 +6187,7 @@ static void gen_dcread(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else TCGv EA, val; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6232,7 +6217,7 @@ static void gen_iccci(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6246,7 +6231,7 @@ static void gen_icread(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6254,13 +6239,13 @@ static void gen_icread(DisasContext *ctx) #endif } -/* rfci (mem_idx only) */ +/* rfci (supervisor only) */ static void gen_rfci_40x(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6275,7 +6260,7 @@ static void gen_rfci(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6293,7 +6278,7 @@ static void gen_rfdi(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6309,7 +6294,7 @@ static void gen_rfmci(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6327,7 +6312,7 @@ static void gen_tlbre_40x(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6354,7 +6339,7 @@ static void gen_tlbsx_40x(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else TCGv t0; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6378,7 +6363,7 @@ static void gen_tlbwe_40x(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6406,7 +6391,7 @@ static void gen_tlbre_440(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6435,7 +6420,7 @@ static void gen_tlbsx_440(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else TCGv t0; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6459,7 +6444,7 @@ static void gen_tlbwe_440(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6489,7 +6474,7 @@ static void gen_tlbre_booke206(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6505,7 +6490,7 @@ static void gen_tlbsx_booke206(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else TCGv t0; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6529,7 +6514,7 @@ static void gen_tlbwe_booke206(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6544,7 +6529,7 @@ static void gen_tlbivax_booke206(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else TCGv t0; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6563,7 +6548,7 @@ static void gen_tlbilx_booke206(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else TCGv t0; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6598,7 +6583,7 @@ static void gen_wrtee(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else TCGv t0; - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6620,7 +6605,7 @@ static void gen_wrteei(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(!ctx->mem_idx)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6673,7 +6658,7 @@ static void gen_msgclr(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(ctx->mem_idx == 0)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -6687,7 +6672,7 @@ static void gen_msgsnd(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(ctx->mem_idx == 0)) { + if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -7253,10 +7238,10 @@ GEN_VXFORM_NOA_ENV(vrefp, 5, 4); GEN_VXFORM_NOA_ENV(vrsqrtefp, 5, 5); GEN_VXFORM_NOA_ENV(vexptefp, 5, 6); GEN_VXFORM_NOA_ENV(vlogefp, 5, 7); -GEN_VXFORM_NOA_ENV(vrfim, 5, 8); -GEN_VXFORM_NOA_ENV(vrfin, 5, 9); +GEN_VXFORM_NOA_ENV(vrfim, 5, 11); +GEN_VXFORM_NOA_ENV(vrfin, 5, 8); GEN_VXFORM_NOA_ENV(vrfip, 5, 10); -GEN_VXFORM_NOA_ENV(vrfiz, 5, 11); +GEN_VXFORM_NOA_ENV(vrfiz, 5, 9); #define GEN_VXFORM_SIMM(name, opc2, opc3) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -8221,7 +8206,7 @@ static inline TCGv_ptr gen_fprp_ptr(int reg) } #if defined(TARGET_PPC64) -static void gen_set_cr6_from_fpscr(DisasContext *ctx) +static void gen_set_cr1_from_fpscr(DisasContext *ctx) { TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(tmp, cpu_fpscr); @@ -8229,7 +8214,7 @@ static void gen_set_cr6_from_fpscr(DisasContext *ctx) tcg_temp_free_i32(tmp); } #else -static void gen_set_cr6_from_fpscr(DisasContext *ctx) +static void gen_set_cr1_from_fpscr(DisasContext *ctx) { tcg_gen_shri_tl(cpu_crf[1], cpu_fpscr, 28); } @@ -8249,7 +8234,7 @@ static void gen_##name(DisasContext *ctx) \ rb = gen_fprp_ptr(rB(ctx->opcode)); \ gen_helper_##name(cpu_env, rd, ra, rb); \ if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_cr6_from_fpscr(ctx); \ + gen_set_cr1_from_fpscr(ctx); \ } \ tcg_temp_free_ptr(rd); \ tcg_temp_free_ptr(ra); \ @@ -8307,7 +8292,7 @@ static void gen_##name(DisasContext *ctx) \ u32_2 = tcg_const_i32(u32f2(ctx->opcode)); \ gen_helper_##name(cpu_env, rt, rb, u32_1, u32_2); \ if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_cr6_from_fpscr(ctx); \ + gen_set_cr1_from_fpscr(ctx); \ } \ tcg_temp_free_ptr(rt); \ tcg_temp_free_ptr(rb); \ @@ -8331,7 +8316,7 @@ static void gen_##name(DisasContext *ctx) \ i32 = tcg_const_i32(i32fld(ctx->opcode)); \ gen_helper_##name(cpu_env, rt, ra, rb, i32); \ if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_cr6_from_fpscr(ctx); \ + gen_set_cr1_from_fpscr(ctx); \ } \ tcg_temp_free_ptr(rt); \ tcg_temp_free_ptr(rb); \ @@ -8352,7 +8337,7 @@ static void gen_##name(DisasContext *ctx) \ rb = gen_fprp_ptr(rB(ctx->opcode)); \ gen_helper_##name(cpu_env, rt, rb); \ if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_cr6_from_fpscr(ctx); \ + gen_set_cr1_from_fpscr(ctx); \ } \ tcg_temp_free_ptr(rt); \ tcg_temp_free_ptr(rb); \ @@ -8373,7 +8358,7 @@ static void gen_##name(DisasContext *ctx) \ i32 = tcg_const_i32(i32fld(ctx->opcode)); \ gen_helper_##name(cpu_env, rt, rs, i32); \ if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_cr6_from_fpscr(ctx); \ + gen_set_cr1_from_fpscr(ctx); \ } \ tcg_temp_free_ptr(rt); \ tcg_temp_free_ptr(rs); \ @@ -10091,16 +10076,14 @@ 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(fcfid, 0x3F, 0x0E, 0x1A, 0x001F0000, PPC_NONE, PPC2_FP_CVT_S64), 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(fctid, 0x3F, 0x0E, 0x19, 0x001F0000, PPC_NONE, PPC2_FP_CVT_S64), 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(fctidz, 0x3F, 0x0F, 0x19, 0x001F0000, PPC_NONE, PPC2_FP_CVT_S64), 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), GEN_FLOAT_B(rip, 0x08, 0x0E, 1, PPC_FLOAT_EXT), @@ -10491,10 +10474,10 @@ GEN_VXFORM_NOA(vrefp, 5, 4), GEN_VXFORM_NOA(vrsqrtefp, 5, 5), GEN_VXFORM_NOA(vexptefp, 5, 6), GEN_VXFORM_NOA(vlogefp, 5, 7), -GEN_VXFORM_NOA(vrfim, 5, 8), -GEN_VXFORM_NOA(vrfin, 5, 9), +GEN_VXFORM_NOA(vrfim, 5, 11), +GEN_VXFORM_NOA(vrfin, 5, 8), GEN_VXFORM_NOA(vrfip, 5, 10), -GEN_VXFORM_NOA(vrfiz, 5, 11), +GEN_VXFORM_NOA(vrfiz, 5, 9), #undef GEN_VXFORM_UIMM #define GEN_VXFORM_UIMM(name, opc2, opc3) \ @@ -11302,6 +11285,8 @@ static inline void gen_intermediate_code_internal(PowerPCCPU *cpu, ctx.tb = tb; ctx.exception = POWERPC_EXCP_NONE; ctx.spr_cb = env->spr_cb; + ctx.pr = msr_pr; + ctx.hv = !msr_pr && msr_hv; ctx.mem_idx = env->mmu_idx; ctx.insns_flags = env->insns_flags; ctx.insns_flags2 = env->insns_flags2; diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 33fb4cc9c3..20d58c01db 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -2786,7 +2786,7 @@ static void init_excp_BookE (CPUPPCState *env) env->excp_vectors[POWERPC_EXCP_DTLB] = 0x00000000; env->excp_vectors[POWERPC_EXCP_ITLB] = 0x00000000; env->excp_vectors[POWERPC_EXCP_DEBUG] = 0x00000000; - env->ivor_mask = 0x0000FFE0UL; + env->ivor_mask = 0x0000FFF0UL; env->ivpr_mask = 0xFFFF0000UL; /* Hardware reset vector */ env->hreset_vector = 0xFFFFFFFCUL; @@ -3923,6 +3923,44 @@ POWERPC_FAMILY(440x5)(ObjectClass *oc, void *data) POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; } +POWERPC_FAMILY(440x5wDFPU)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 440x5 with double precision FPU"; + pcc->init_proc = init_proc_440x5; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_FLOAT | PPC_FLOAT_FSQRT | + PPC_FLOAT_STFIWX | + PPC_DCR | PPC_WRTEE | PPC_RFMCI | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_MFTB | + PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | + PPC_440_SPEC; + pcc->insns_flags2 = PPC2_FP_CVT_S64; + pcc->msr_mask = (1ull << MSR_POW) | + (1ull << MSR_CE) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_DWE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_IR) | + (1ull << MSR_DR); + pcc->mmu_model = POWERPC_MMU_BOOKE; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DWE | + POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; +} + static void init_proc_460 (CPUPPCState *env) { /* Time base */ @@ -5010,7 +5048,8 @@ 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 | PPC2_PERM_ISA206; + pcc->insns_flags2 = PPC2_BOOKE206 | PPC2_PRCNTL | PPC2_PERM_ISA206 | \ + PPC2_FP_CVT_S64; pcc->msr_mask = (1ull << MSR_CM) | (1ull << MSR_GS) | (1ull << MSR_UCLE) | @@ -7906,6 +7945,7 @@ POWERPC_FAMILY(970)(ObjectClass *oc, void *data) PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_64B | PPC_ALTIVEC | PPC_SEGMENT_64B | PPC_SLBI; + pcc->insns_flags2 = PPC2_FP_CVT_S64; pcc->msr_mask = (1ull << MSR_SF) | (1ull << MSR_VR) | (1ull << MSR_POW) | @@ -7958,6 +7998,7 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_64B | PPC_SEGMENT_64B | PPC_SLBI; + pcc->insns_flags2 = PPC2_FP_CVT_S64; pcc->msr_mask = (1ull << MSR_SF) | (1ull << MSR_VR) | (1ull << MSR_POW) | @@ -8100,7 +8141,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) 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; + PPC2_FP_TST_ISA206 | PPC2_FP_CVT_S64; pcc->msr_mask = (1ull << MSR_SF) | (1ull << MSR_VR) | (1ull << MSR_VSX) | @@ -8178,7 +8219,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | - PPC2_ISA205 | PPC2_ISA207S; + PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64; pcc->msr_mask = (1ull << MSR_SF) | (1ull << MSR_TM) | (1ull << MSR_VR) | @@ -8432,14 +8473,16 @@ enum { PPC_INDIRECT = 1, /* Indirect opcode table */ }; +#define PPC_OPCODE_MASK 0x3 + static inline int is_indirect_opcode (void *handler) { - return ((uintptr_t)handler & 0x03) == PPC_INDIRECT; + return ((uintptr_t)handler & PPC_OPCODE_MASK) == PPC_INDIRECT; } static inline opc_handler_t **ind_table(void *handler) { - return (opc_handler_t **)((uintptr_t)handler & ~3); + return (opc_handler_t **)((uintptr_t)handler & ~PPC_OPCODE_MASK); } /* Instruction table creation */ @@ -8456,8 +8499,8 @@ static int create_new_table (opc_handler_t **table, unsigned char idx) { opc_handler_t **tmp; - tmp = g_new(opc_handler_t *, 0x20); - fill_new_table(tmp, 0x20); + tmp = g_new(opc_handler_t *, PPC_CPU_INDIRECT_OPCODES_LEN); + fill_new_table(tmp, PPC_CPU_INDIRECT_OPCODES_LEN); table[idx] = (opc_handler_t *)((uintptr_t)tmp | PPC_INDIRECT); return 0; @@ -8584,7 +8627,8 @@ static int test_opcode_table (opc_handler_t **table, int len) table[i] = &invalid_handler; if (table[i] != &invalid_handler) { if (is_indirect_opcode(table[i])) { - tmp = test_opcode_table(ind_table(table[i]), 0x20); + tmp = test_opcode_table(ind_table(table[i]), + PPC_CPU_INDIRECT_OPCODES_LEN); if (tmp == 0) { free(table[i]); table[i] = &invalid_handler; @@ -8602,7 +8646,7 @@ static int test_opcode_table (opc_handler_t **table, int len) static void fix_opcode_tables (opc_handler_t **ppc_opcodes) { - if (test_opcode_table(ppc_opcodes, 0x40) == 0) + if (test_opcode_table(ppc_opcodes, PPC_CPU_OPCODES_LEN) == 0) printf("*** WARNING: no opcode defined !\n"); } @@ -8613,7 +8657,7 @@ static void create_ppc_opcodes(PowerPCCPU *cpu, Error **errp) CPUPPCState *env = &cpu->env; opcode_t *opc; - fill_new_table(env->opcodes, 0x40); + fill_new_table(env->opcodes, PPC_CPU_OPCODES_LEN); for (opc = opcodes; opc < &opcodes[ARRAY_SIZE(opcodes)]; opc++) { if (((opc->handler.type & pcc->insns_flags) != 0) || ((opc->handler.type2 & pcc->insns_flags2) != 0)) { @@ -8639,12 +8683,12 @@ static void dump_ppc_insns (CPUPPCState *env) printf("Instructions set:\n"); /* opc1 is 6 bits long */ - for (opc1 = 0x00; opc1 < 0x40; opc1++) { + for (opc1 = 0x00; opc1 < PPC_CPU_OPCODES_LEN; opc1++) { table = env->opcodes; handler = table[opc1]; if (is_indirect_opcode(handler)) { /* opc2 is 5 bits long */ - for (opc2 = 0; opc2 < 0x20; opc2++) { + for (opc2 = 0; opc2 < PPC_CPU_INDIRECT_OPCODES_LEN; opc2++) { table = env->opcodes; handler = env->opcodes[opc1]; table = ind_table(handler); @@ -8652,7 +8696,8 @@ static void dump_ppc_insns (CPUPPCState *env) if (is_indirect_opcode(handler)) { table = ind_table(handler); /* opc3 is 5 bits long */ - for (opc3 = 0; opc3 < 0x20; opc3++) { + for (opc3 = 0; opc3 < PPC_CPU_INDIRECT_OPCODES_LEN; + opc3++) { handler = table[opc3]; if (handler->handler != &gen_invalid) { /* Special hack to properly dump SPE insns */ @@ -9087,11 +9132,24 @@ static void ppc_cpu_unrealizefn(DeviceState *dev, Error **errp) { PowerPCCPU *cpu = POWERPC_CPU(dev); CPUPPCState *env = &cpu->env; - int i; + opc_handler_t **table; + int i, j; for (i = 0; i < PPC_CPU_OPCODES_LEN; i++) { - if (env->opcodes[i] != &invalid_handler) { - g_free(env->opcodes[i]); + if (env->opcodes[i] == &invalid_handler) { + continue; + } + if (is_indirect_opcode(env->opcodes[i])) { + table = ind_table(env->opcodes[i]); + for (j = 0; j < PPC_CPU_INDIRECT_OPCODES_LEN; j++) { + if (table[j] != &invalid_handler && + is_indirect_opcode(table[j])) { + g_free((opc_handler_t *)((uintptr_t)table[j] & + ~PPC_INDIRECT)); + } + } + g_free((opc_handler_t *)((uintptr_t)env->opcodes[i] & + ~PPC_INDIRECT)); } } } @@ -9137,7 +9195,7 @@ int ppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version) break; } - if (kvm_enabled() && kvmppc_set_compat(cpu, cpu->max_compat) < 0) { + if (kvm_enabled() && kvmppc_set_compat(cpu, cpu->cpu_version) < 0) { error_report("Unable to set compatibility mode in KVM"); ret = -1; } diff --git a/target-s390x/insn-data.def b/target-s390x/insn-data.def index b42ebb6a1a..4d2feb6977 100644 --- a/target-s390x/insn-data.def +++ b/target-s390x/insn-data.def @@ -744,9 +744,9 @@ /* SERVICE CALL LOGICAL PROCESSOR (PV hypercall) */ C(0xb220, SERVC, RRE, Z, r1_o, r2_o, 0, 0, servc, 0) /* SET ADDRESSING MODE */ - /* We only do 64-bit, so accept this as a no-op. - Let SAM24 and SAM31 signal illegal instruction. */ - C(0x010e, SAM64, E, Z, 0, 0, 0, 0, 0, 0) + D(0x010c, SAM24, E, Z, 0, 0, 0, 0, sam, 0, 0) + D(0x010d, SAM31, E, Z, 0, 0, 0, 0, sam, 0, 1) + D(0x010e, SAM64, E, Z, 0, 0, 0, 0, sam, 0, 3) /* SET ADDRESS SPACE CONTROL FAST */ C(0xb279, SACF, S, Z, 0, a2, 0, 0, sacf, 0) /* SET CLOCK */ diff --git a/target-s390x/interrupt.c b/target-s390x/interrupt.c index 23a9114f5a..1404d0afdd 100644 --- a/target-s390x/interrupt.c +++ b/target-s390x/interrupt.c @@ -22,9 +22,7 @@ void s390_sclp_extint(uint32_t parm) kvm_s390_service_interrupt(parm); } else { S390CPU *dummy_cpu = s390_cpu_addr2state(0); - CPUS390XState *env = &dummy_cpu->env; - env->psw.addr += 4; cpu_inject_ext(dummy_cpu, EXT_SERVICE, parm, 0); } } diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index 5b10a255ed..d247471119 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -827,18 +827,18 @@ static int handle_b9(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) return r; } -static int handle_eb(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) +static int handle_eb(S390CPU *cpu, struct kvm_run *run, uint8_t ipbl) { int r = 0; - switch (ipa1) { + switch (ipbl) { case PRIV_EB_SQBS: /* just inject exception */ r = -1; break; default: r = -1; - DPRINTF("KVM: unhandled PRIV: 0xeb%x\n", ipa1); + DPRINTF("KVM: unhandled PRIV: 0xeb%x\n", ipbl); break; } @@ -1039,7 +1039,7 @@ static int handle_instruction(S390CPU *cpu, struct kvm_run *run) r = handle_b9(cpu, run, ipa1); break; case IPA0_EB: - r = handle_eb(cpu, run, ipa1); + r = handle_eb(cpu, run, run->s390_sieic.ipb & 0xff); break; case IPA0_DIAG: r = handle_diag(cpu, run, run->s390_sieic.ipb); @@ -1272,7 +1272,7 @@ void kvm_s390_crw_mchk(void) struct kvm_s390_irq irq = { .type = KVM_S390_MCHK, .u.mchk.cr14 = 1 << 28, - .u.mchk.mcic = 0x00400f1d40330000, + .u.mchk.mcic = 0x00400f1d40330000ULL, }; kvm_s390_floating_interrupt(&irq); } diff --git a/target-s390x/translate.c b/target-s390x/translate.c index 0cb036f667..dbf1993d46 100644 --- a/target-s390x/translate.c +++ b/target-s390x/translate.c @@ -2925,6 +2925,18 @@ static ExitStatus op_sacf(DisasContext *s, DisasOps *o) /* Addressing mode has changed, so end the block. */ return EXIT_PC_STALE; } + +static ExitStatus op_sam(DisasContext *s, DisasOps *o) +{ + int sam = s->insn->data; + TCGv_i64 tsam = tcg_const_i64(sam); + + /* Overwrite PSW_MASK_64 and PSW_MASK_32 */ + tcg_gen_deposit_i64(psw_mask, psw_mask, tsam, 31, 2); + + tcg_temp_free_i64(tsam); + return EXIT_PC_STALE; +} #endif static ExitStatus op_sar(DisasContext *s, DisasOps *o) diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h index 9cf52758c7..ac463f27fe 100644 --- a/target-xtensa/cpu.h +++ b/target-xtensa/cpu.h @@ -266,6 +266,7 @@ typedef enum { INTTYPE_TIMER, INTTYPE_DEBUG, INTTYPE_WRITE_ERR, + INTTYPE_PROFILING, INTTYPE_MAX } interrupt_type; @@ -471,6 +472,12 @@ static inline xtensa_tlb_entry *xtensa_tlb_get_entry(CPUXtensaState *env, env->itlb[wi] + ei; } +static inline uint32_t xtensa_replicate_windowstart(CPUXtensaState *env) +{ + return env->sregs[WINDOW_START] | + (env->sregs[WINDOW_START] << env->config->nareg / 4); +} + /* MMU modes definitions */ #define MMU_MODE0_SUFFIX _ring0 #define MMU_MODE1_SUFFIX _ring1 diff --git a/target-xtensa/import_core.sh b/target-xtensa/import_core.sh new file mode 100755 index 0000000000..73791ec545 --- /dev/null +++ b/target-xtensa/import_core.sh @@ -0,0 +1,53 @@ +#! /bin/bash -e + +OVERLAY="$1" +NAME="$2" +FREQ=40000 +BASE=$(dirname "$0") +TARGET="$BASE"/core-$NAME + +[ $# -ge 2 -a -f "$OVERLAY" ] || { cat <<EOF +Usage: $0 overlay-archive-to-import core-name [frequency-in-KHz] + overlay-archive-to-import: file name of xtensa-config-overlay.tar.gz + to import configuration from. + core-name: QEMU name of the imported core. Must be valid + C identifier. + frequency-in-KHz: core frequency (40MHz if not specified). +EOF +exit +} + +[ $# -ge 3 ] && FREQ="$3" +mkdir -p "$TARGET" +tar -xf "$OVERLAY" -C "$TARGET" --strip-components=1 \ + --xform='s/core/core-isa/' config/core.h +tar -xf "$OVERLAY" -O gdb/xtensa-config.c | \ + sed -n '1,/*\//p;/pc/,/a15/p' > "$TARGET"/gdb-config.c +NUM_REGS=$(grep XTREG "$TARGET"/gdb-config.c | wc -l) + +cat <<EOF > "${TARGET}.c" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/gdbstub.h" +#include "qemu/host-utils.h" + +#include "core-$NAME/core-isa.h" +#include "overlay_tool.h" + +static const XtensaConfig $NAME __attribute__((unused)) = { + .name = "$NAME", + .gdb_regmap = { + .num_regs = $NUM_REGS, + .reg = { +#include "core-$NAME/gdb-config.c" + } + }, + .clock_freq_khz = $FREQ, + DEFAULT_SECTIONS +}; + +REGISTER_CORE($NAME) +EOF + +grep -q core-${NAME}.o "$BASE"/Makefile.objs || \ + echo "obj-y += core-${NAME}.o" >> "$BASE"/Makefile.objs diff --git a/target-xtensa/op_helper.c b/target-xtensa/op_helper.c index dae13866ef..872e5a823b 100644 --- a/target-xtensa/op_helper.c +++ b/target-xtensa/op_helper.c @@ -235,6 +235,12 @@ void HELPER(entry)(CPUXtensaState *env, uint32_t pc, uint32_t s, uint32_t imm) pc, env->sregs[PS]); HELPER(exception_cause)(env, pc, ILLEGAL_INSTRUCTION_CAUSE); } else { + uint32_t windowstart = xtensa_replicate_windowstart(env) >> + (env->sregs[WINDOW_BASE] + 1); + + if (windowstart & ((1 << callinc) - 1)) { + HELPER(window_check)(env, pc, callinc); + } env->regs[(callinc << 2) | (s & 3)] = env->regs[s] - (imm << 3); rotate_window(env, callinc); env->sregs[WINDOW_START] |= diff --git a/target-xtensa/overlay_tool.h b/target-xtensa/overlay_tool.h index 4c0de7f06a..6105d4c8ff 100644 --- a/target-xtensa/overlay_tool.h +++ b/target-xtensa/overlay_tool.h @@ -108,20 +108,27 @@ #define XCHAL_WINDOW_UF12_VECOFS 0x00000140 #endif +#if XCHAL_HAVE_WINDOWED +#define WINDOW_VECTORS \ + [EXC_WINDOW_OVERFLOW4] = XCHAL_WINDOW_OF4_VECOFS + \ + XCHAL_WINDOW_VECTORS_VADDR, \ + [EXC_WINDOW_UNDERFLOW4] = XCHAL_WINDOW_UF4_VECOFS + \ + XCHAL_WINDOW_VECTORS_VADDR, \ + [EXC_WINDOW_OVERFLOW8] = XCHAL_WINDOW_OF8_VECOFS + \ + XCHAL_WINDOW_VECTORS_VADDR, \ + [EXC_WINDOW_UNDERFLOW8] = XCHAL_WINDOW_UF8_VECOFS + \ + XCHAL_WINDOW_VECTORS_VADDR, \ + [EXC_WINDOW_OVERFLOW12] = XCHAL_WINDOW_OF12_VECOFS + \ + XCHAL_WINDOW_VECTORS_VADDR, \ + [EXC_WINDOW_UNDERFLOW12] = XCHAL_WINDOW_UF12_VECOFS + \ + XCHAL_WINDOW_VECTORS_VADDR, +#else +#define WINDOW_VECTORS +#endif + #define EXCEPTION_VECTORS { \ [EXC_RESET] = XCHAL_RESET_VECTOR_VADDR, \ - [EXC_WINDOW_OVERFLOW4] = XCHAL_WINDOW_OF4_VECOFS + \ - XCHAL_WINDOW_VECTORS_VADDR, \ - [EXC_WINDOW_UNDERFLOW4] = XCHAL_WINDOW_UF4_VECOFS + \ - XCHAL_WINDOW_VECTORS_VADDR, \ - [EXC_WINDOW_OVERFLOW8] = XCHAL_WINDOW_OF8_VECOFS + \ - XCHAL_WINDOW_VECTORS_VADDR, \ - [EXC_WINDOW_UNDERFLOW8] = XCHAL_WINDOW_UF8_VECOFS + \ - XCHAL_WINDOW_VECTORS_VADDR, \ - [EXC_WINDOW_OVERFLOW12] = XCHAL_WINDOW_OF12_VECOFS + \ - XCHAL_WINDOW_VECTORS_VADDR, \ - [EXC_WINDOW_UNDERFLOW12] = XCHAL_WINDOW_UF12_VECOFS + \ - XCHAL_WINDOW_VECTORS_VADDR, \ + WINDOW_VECTORS \ [EXC_KERNEL] = XCHAL_KERNEL_VECTOR_VADDR, \ [EXC_USER] = XCHAL_USER_VECTOR_VADDR, \ [EXC_DOUBLE] = XCHAL_DOUBLEEXC_VECTOR_VADDR, \ @@ -163,6 +170,7 @@ #define XTHAL_INTTYPE_TBD1 INTTYPE_DEBUG #define XTHAL_INTTYPE_TBD2 INTTYPE_WRITE_ERR #define XTHAL_INTTYPE_WRITE_ERROR INTTYPE_WRITE_ERR +#define XTHAL_INTTYPE_PROFILING INTTYPE_PROFILING #define INTERRUPT(i) { \ diff --git a/tests/acpi-test-data/rebuild-expected-aml.sh b/tests/acpi-test-data/rebuild-expected-aml.sh index ab98498884..11bf743914 100755 --- a/tests/acpi-test-data/rebuild-expected-aml.sh +++ b/tests/acpi-test-data/rebuild-expected-aml.sh @@ -23,13 +23,13 @@ else exit 1; fi -if [ ! -e "tests/acpi-test" ]; then - echo "Test: acpi-test is required! Run make check before this script." +if [ ! -e "tests/bios-tables-test" ]; then + echo "Test: bios-tables-test is required! Run make check before this script." echo "Run this script from the build directory." exit 1; fi -TEST_ACPI_REBUILD_AML=y QTEST_QEMU_BINARY=$qemu tests/acpi-test +TEST_ACPI_REBUILD_AML=y QTEST_QEMU_BINARY=$qemu tests/bios-tables-test echo "The files were rebuilt and can be added to git." echo "However, if new files were created, please copy them manually" \ diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040 index f1e16c11c7..2b432ad7a1 100755 --- a/tests/qemu-iotests/040 +++ b/tests/qemu-iotests/040 @@ -43,8 +43,7 @@ class ImageCommitTestCase(iotests.QMPTestCase): if event['event'] == 'BLOCK_JOB_COMPLETED': self.assert_qmp(event, 'data/type', 'commit') self.assert_qmp(event, 'data/device', 'drive0') - self.assert_qmp(event, 'data/offset', self.image_len) - self.assert_qmp(event, 'data/len', self.image_len) + self.assert_qmp(event, 'data/offset', event['data']['len']) if need_ready: self.assertTrue(ready, "Expecting BLOCK_JOB_COMPLETED event") completed = True @@ -52,7 +51,6 @@ class ImageCommitTestCase(iotests.QMPTestCase): ready = True self.assert_qmp(event, 'data/type', 'commit') self.assert_qmp(event, 'data/device', 'drive0') - self.assert_qmp(event, 'data/len', self.image_len) self.vm.qmp('block-job-complete', device='drive0') self.assert_no_active_block_jobs() diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index 5dbd4ee91b..59a8f733f7 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -52,8 +52,7 @@ class ImageMirroringTestCase(iotests.QMPTestCase): event = self.cancel_and_wait(drive=drive) self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED') self.assert_qmp(event, 'data/type', 'mirror') - self.assert_qmp(event, 'data/offset', self.image_len) - self.assert_qmp(event, 'data/len', self.image_len) + self.assert_qmp(event, 'data/offset', event['data']['len']) def complete_and_wait(self, drive='drive0', wait_ready=True): '''Complete a block job and wait for it to finish''' @@ -417,7 +416,6 @@ new_state = "1" self.assert_qmp(event, 'data/type', 'mirror') self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/error', 'Input/output error') - self.assert_qmp(event, 'data/len', self.image_len) completed = True self.assert_no_active_block_jobs() @@ -568,7 +566,6 @@ new_state = "1" self.assert_qmp(event, 'data/type', 'mirror') self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/error', 'Input/output error') - self.assert_qmp(event, 'data/len', self.image_len) completed = True self.assert_no_active_block_jobs() diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061 index ab98def6d4..8d37f8a65c 100755 --- a/tests/qemu-iotests/061 +++ b/tests/qemu-iotests/061 @@ -209,6 +209,31 @@ $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" _check_test_img $QEMU_IO -c "read -P 0 0 64M" "$TEST_IMG" | _filter_qemu_io +echo +echo "=== Testing progress report without snapshot ===" +echo +IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 4G +IMGOPTS="compat=1.1" _make_test_img -b "$TEST_IMG.base" 4G +$QEMU_IO -c "write -z 0 64k" \ + -c "write -z 1G 64k" \ + -c "write -z 2G 64k" \ + -c "write -z 3G 64k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG amend -p -o "compat=0.10" "$TEST_IMG" +_check_test_img + +echo +echo "=== Testing progress report with snapshot ===" +echo +IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 4G +IMGOPTS="compat=1.1" _make_test_img -b "$TEST_IMG.base" 4G +$QEMU_IO -c "write -z 0 64k" \ + -c "write -z 1G 64k" \ + -c "write -z 2G 64k" \ + -c "write -z 3G 64k" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG snapshot -c foo "$TEST_IMG" +$QEMU_IMG amend -p -o "compat=0.10" "$TEST_IMG" +_check_test_img + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out index 4ae6472c73..9045544df2 100644 --- a/tests/qemu-iotests/061.out +++ b/tests/qemu-iotests/061.out @@ -390,4 +390,34 @@ wrote 67108864/67108864 bytes at offset 0 No errors were found on the image. read 67108864/67108864 bytes at offset 0 64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Testing progress report without snapshot === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=4294967296 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file='TEST_DIR/t.IMGFMT.base' +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 1073741824 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 2147483648 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 3221225472 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + (0.00/100%)
(12.50/100%)
(25.00/100%)
(37.50/100%)
(50.00/100%)
(62.50/100%)
(75.00/100%)
(87.50/100%)
(100.00/100%)
(100.00/100%) +No errors were found on the image. + +=== Testing progress report with snapshot === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=4294967296 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file='TEST_DIR/t.IMGFMT.base' +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 1073741824 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 2147483648 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 3221225472 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + (0.00/100%)
(6.25/100%)
(12.50/100%)
(18.75/100%)
(25.00/100%)
(31.25/100%)
(37.50/100%)
(43.75/100%)
(50.00/100%)
(56.25/100%)
(62.50/100%)
(68.75/100%)
(75.00/100%)
(81.25/100%)
(87.50/100%)
(93.75/100%)
(100.00/100%)
(100.00/100%) +No errors were found on the image. *** done diff --git a/tests/qemu-iotests/076 b/tests/qemu-iotests/076 index bc47457a85..ed2be3581e 100755 --- a/tests/qemu-iotests/076 +++ b/tests/qemu-iotests/076 @@ -47,29 +47,34 @@ catalog_entries_offset=$((0x20)) nb_sectors_offset=$((0x24)) echo -echo "== Read from a valid (enough) image ==" -_use_sample_img fake.parallels.bz2 +echo "== Read from a valid v1 image ==" +_use_sample_img parallels-v1.bz2 { $QEMU_IO -c "read -P 0x11 0 64k" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir echo echo "== Negative catalog size ==" -_use_sample_img fake.parallels.bz2 +_use_sample_img parallels-v1.bz2 poke_file "$TEST_IMG" "$catalog_entries_offset" "\xff\xff\xff\xff" { $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir echo echo "== Overflow in catalog allocation ==" -_use_sample_img fake.parallels.bz2 +_use_sample_img parallels-v1.bz2 poke_file "$TEST_IMG" "$nb_sectors_offset" "\xff\xff\xff\xff" poke_file "$TEST_IMG" "$catalog_entries_offset" "\x01\x00\x00\x40" { $QEMU_IO -c "read 64M 64M" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir echo echo "== Zero sectors per track ==" -_use_sample_img fake.parallels.bz2 +_use_sample_img parallels-v1.bz2 poke_file "$TEST_IMG" "$tracks_offset" "\x00\x00\x00\x00" { $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +echo +echo "== Read from a valid v2 image ==" +_use_sample_img parallels-v2.bz2 +{ $QEMU_IO -c "read -P 0x11 0 64k" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/076.out b/tests/qemu-iotests/076.out index f7745d8b0d..32ade08565 100644 --- a/tests/qemu-iotests/076.out +++ b/tests/qemu-iotests/076.out @@ -1,18 +1,22 @@ QA output created by 076 -== Read from a valid (enough) image == +== Read from a valid v1 image == read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == Negative catalog size == -qemu-io: can't open device TEST_DIR/fake.parallels: Catalog too large +qemu-io: can't open device TEST_DIR/parallels-v1: Catalog too large no file open, try 'help open' == Overflow in catalog allocation == -qemu-io: can't open device TEST_DIR/fake.parallels: Catalog too large +qemu-io: can't open device TEST_DIR/parallels-v1: Catalog too large no file open, try 'help open' == Zero sectors per track == -qemu-io: can't open device TEST_DIR/fake.parallels: Invalid image: Zero sectors per track +qemu-io: can't open device TEST_DIR/parallels-v1: Invalid image: Zero sectors per track no file open, try 'help open' + +== Read from a valid v2 image == +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) *** done diff --git a/tests/qemu-iotests/084 b/tests/qemu-iotests/084 index 2712c023a9..733018d4a8 100755 --- a/tests/qemu-iotests/084 +++ b/tests/qemu-iotests/084 @@ -66,15 +66,15 @@ stat -c"disk image file size in bytes: %s" "${TEST_IMG}" # check for image size too large # poke max image size, and appropriate blocks_in_image value -echo "Test 1: Maximum size (1024 TB):" -poke_file "$TEST_IMG" "$ds_offset" "\x00\x00\xf0\xff\xff\xff\x03\x00" -poke_file "$TEST_IMG" "$bii_offset" "\xff\xff\xff\x3f" +echo "Test 1: Maximum size (512 TB - 128 MB):" +poke_file "$TEST_IMG" "$ds_offset" "\x00\x00\x00\xf8\xff\xff\x01\x00" +poke_file "$TEST_IMG" "$bii_offset" "\x80\xff\xff\x1f" _img_info echo -echo "Test 2: Size too large (1024TB + 1)" +echo "Test 2: Size too large (512 TB - 128 MB + 64 kB)" # This should be too large (-EINVAL): -poke_file "$TEST_IMG" "$ds_offset" "\x00\x00\xf1\xff\xff\xff\x03\x00" +poke_file "$TEST_IMG" "$ds_offset" "\x00\x00\x01\xf8\xff\xff\x01\x00" _img_info echo @@ -89,9 +89,9 @@ _img_info echo echo "Test 4: Size valid (64M), but Blocks In Image exceeds max allowed" -# Now check the bounds of blocks_in_image - 0x3fffffff should be the max +# Now check the bounds of blocks_in_image - 0x1fffff80 should be the max # value here, and we should get -ENOTSUP -poke_file "$TEST_IMG" "$bii_offset" "\x00\x00\x00\x40" +poke_file "$TEST_IMG" "$bii_offset" "\x81\xff\xff\x1f" _img_info # Finally, 1MB is the only block size supported. Verify that diff --git a/tests/qemu-iotests/084.out b/tests/qemu-iotests/084.out index ea29ae0b9d..5ece8299c8 100644 --- a/tests/qemu-iotests/084.out +++ b/tests/qemu-iotests/084.out @@ -17,17 +17,20 @@ file format: IMGFMT virtual size: 64M (67108864 bytes) cluster_size: 1048576 disk image file size in bytes: 1024 -Test 1: Maximum size (1024 TB): -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Could not open 'TEST_DIR/t.IMGFMT': Invalid argument +Test 1: Maximum size (512 TB - 128 MB): +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 512T (562949819203584 bytes) +cluster_size: 1048576 -Test 2: Size too large (1024TB + 1) -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported VDI image size (size is 0x3fffffff10000, max supported is 0x3fffffff00000) +Test 2: Size too large (512 TB - 128 MB + 64 kB) +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported VDI image size (size is 0x1fffff8010000, max supported is 0x1fffff8000000) Test 3: Size valid (64M), but Blocks In Image too small (63) qemu-img: Could not open 'TEST_DIR/t.IMGFMT': unsupported VDI image (disk size 67108864, image bitmap has room for 66060288) Test 4: Size valid (64M), but Blocks In Image exceeds max allowed -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': unsupported VDI image (too many blocks 1073741824, max is 1073741823) +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': unsupported VDI image (too many blocks 536870785, max is 536870784) Test 5: Valid Image: 64MB, Blocks In Image 64, Block Size 1MB image: TEST_DIR/t.IMGFMT diff --git a/tests/qemu-iotests/097 b/tests/qemu-iotests/097 new file mode 100755 index 0000000000..c7a613b7ee --- /dev/null +++ b/tests/qemu-iotests/097 @@ -0,0 +1,122 @@ +#!/bin/bash +# +# Commit changes into backing chains and empty the top image if the +# backing image is not explicitly specified +# +# 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=mreitz@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 + _rm_test_img "$TEST_IMG.itmd" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.pattern + +# Any format supporting backing files and bdrv_make_empty +_supported_fmt qcow qcow2 +_supported_proto file +_supported_os Linux + + +# Four passes: +# 0: Two-layer backing chain, commit to upper backing file (implicitly) +# (in this case, the top image will be emptied) +# 1: Two-layer backing chain, commit to upper backing file (explicitly) +# (in this case, the top image will implicitly stay unchanged) +# 2: Two-layer backing chain, commit to upper backing file (implicitly with -d) +# (in this case, the top image will explicitly stay unchanged) +# 3: Two-layer backing chain, commit to lower backing file +# (in this case, the top image will implicitly stay unchanged) +# +# 020 already tests committing, so this only tests whether image chains are +# working properly and that all images above the base are emptied; therefore, +# no complicated patterns are necessary +for i in 0 1 2 3; do + +echo +echo "=== Test pass $i ===" +echo + +TEST_IMG="$TEST_IMG.base" _make_test_img 64M +TEST_IMG="$TEST_IMG.itmd" _make_test_img -b "$TEST_IMG.base" 64M +_make_test_img -b "$TEST_IMG.itmd" 64M + +$QEMU_IO -c 'write -P 1 0 192k' "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c 'write -P 2 64k 128k' "$TEST_IMG.itmd" | _filter_qemu_io +$QEMU_IO -c 'write -P 3 128k 64k' "$TEST_IMG" | _filter_qemu_io + +if [ $i -lt 3 ]; then + if [ $i == 0 ]; then + # -b "$TEST_IMG.itmd" should be the default (that is, committing to the + # first backing file in the chain) + $QEMU_IMG commit "$TEST_IMG" + elif [ $i == 1 ]; then + # explicitly specify the commit target (this should imply -d) + $QEMU_IMG commit -b "$TEST_IMG.itmd" "$TEST_IMG" + else + # do not explicitly specify the commit target, but use -d to leave the + # top image unchanged + $QEMU_IMG commit -d "$TEST_IMG" + fi + + # Bottom should be unchanged + $QEMU_IO -c 'read -P 1 0 192k' "$TEST_IMG.base" | _filter_qemu_io + + # Intermediate should contain changes from top + $QEMU_IO -c 'read -P 1 0 64k' "$TEST_IMG.itmd" | _filter_qemu_io + $QEMU_IO -c 'read -P 2 64k 64k' "$TEST_IMG.itmd" | _filter_qemu_io + $QEMU_IO -c 'read -P 3 128k 64k' "$TEST_IMG.itmd" | _filter_qemu_io + + # And in pass 0, the top image should be empty, whereas in both other passes + # it should be unchanged (which is both checked by qemu-img map) +else + $QEMU_IMG commit -b "$TEST_IMG.base" "$TEST_IMG" + + # Bottom should contain all changes + $QEMU_IO -c 'read -P 1 0 64k' "$TEST_IMG.base" | _filter_qemu_io + $QEMU_IO -c 'read -P 2 64k 64k' "$TEST_IMG.base" | _filter_qemu_io + $QEMU_IO -c 'read -P 3 128k 64k' "$TEST_IMG.base" | _filter_qemu_io + + # Both top and intermediate should be unchanged +fi + +$QEMU_IMG map "$TEST_IMG.base" | _filter_qemu_img_map +$QEMU_IMG map "$TEST_IMG.itmd" | _filter_qemu_img_map +$QEMU_IMG map "$TEST_IMG" | _filter_qemu_img_map + +done + + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/097.out b/tests/qemu-iotests/097.out new file mode 100644 index 0000000000..1cb7764fe8 --- /dev/null +++ b/tests/qemu-iotests/097.out @@ -0,0 +1,119 @@ +QA output created by 097 + +=== Test pass 0 === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd' +wrote 196608/196608 bytes at offset 0 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 65536 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 131072 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Image committed. +read 196608/196608 bytes at offset 0 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 131072 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Offset Length File +0 0x30000 TEST_DIR/t.IMGFMT.base +Offset Length File +0 0x10000 TEST_DIR/t.IMGFMT.base +0x10000 0x20000 TEST_DIR/t.IMGFMT.itmd +Offset Length File +0 0x10000 TEST_DIR/t.IMGFMT.base +0x10000 0x20000 TEST_DIR/t.IMGFMT.itmd + +=== Test pass 1 === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd' +wrote 196608/196608 bytes at offset 0 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 65536 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 131072 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Image committed. +read 196608/196608 bytes at offset 0 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 131072 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Offset Length File +0 0x30000 TEST_DIR/t.IMGFMT.base +Offset Length File +0 0x10000 TEST_DIR/t.IMGFMT.base +0x10000 0x20000 TEST_DIR/t.IMGFMT.itmd +Offset Length File +0 0x10000 TEST_DIR/t.IMGFMT.base +0x10000 0x10000 TEST_DIR/t.IMGFMT.itmd +0x20000 0x10000 TEST_DIR/t.IMGFMT + +=== Test pass 2 === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd' +wrote 196608/196608 bytes at offset 0 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 65536 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 131072 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Image committed. +read 196608/196608 bytes at offset 0 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 131072 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Offset Length File +0 0x30000 TEST_DIR/t.IMGFMT.base +Offset Length File +0 0x10000 TEST_DIR/t.IMGFMT.base +0x10000 0x20000 TEST_DIR/t.IMGFMT.itmd +Offset Length File +0 0x10000 TEST_DIR/t.IMGFMT.base +0x10000 0x10000 TEST_DIR/t.IMGFMT.itmd +0x20000 0x10000 TEST_DIR/t.IMGFMT + +=== Test pass 3 === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.itmd' +wrote 196608/196608 bytes at offset 0 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 65536 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 131072 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Image committed. +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 131072 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Offset Length File +0 0x30000 TEST_DIR/t.IMGFMT.base +Offset Length File +0 0x10000 TEST_DIR/t.IMGFMT.base +0x10000 0x20000 TEST_DIR/t.IMGFMT.itmd +Offset Length File +0 0x10000 TEST_DIR/t.IMGFMT.base +0x10000 0x10000 TEST_DIR/t.IMGFMT.itmd +0x20000 0x10000 TEST_DIR/t.IMGFMT +*** done diff --git a/tests/qemu-iotests/098 b/tests/qemu-iotests/098 new file mode 100755 index 0000000000..e2230ad60c --- /dev/null +++ b/tests/qemu-iotests/098 @@ -0,0 +1,82 @@ +#!/bin/bash +# +# Test qcow2's bdrv_make_empty for images without internal snapshots +# +# 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=mreitz@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 + rm -f "$TEST_DIR/blkdebug.conf" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.pattern + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +IMGOPTS="compat=1.1" + +for event in l1_update empty_image_prepare reftable_update refblock_alloc; do + +echo +echo "=== $event ===" +echo + +TEST_IMG="$TEST_IMG.base" _make_test_img 64M +_make_test_img -b "$TEST_IMG.base" 64M + +# Some data that can be leaked when emptying the top image +$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io + +cat > "$TEST_DIR/blkdebug.conf" <<EOF +[inject-error] +event = "$event" +EOF + +$QEMU_IMG commit "blkdebug:$TEST_DIR/blkdebug.conf:$TEST_IMG" 2>&1 \ + | _filter_testdir | _filter_imgfmt + +# There may be errors, but they should be fixed by opening the image +$QEMU_IO -c close "$TEST_IMG" + +_check_test_img + +done + + +rm -f "$TEST_DIR/blkdebug.conf" + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/098.out b/tests/qemu-iotests/098.out new file mode 100644 index 0000000000..586dfc809f --- /dev/null +++ b/tests/qemu-iotests/098.out @@ -0,0 +1,52 @@ +QA output created by 098 + +=== l1_update === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error +No errors were found on the image. + +=== empty_image_prepare === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error +Leaked cluster 4 refcount=1 reference=0 +Leaked cluster 5 refcount=1 reference=0 +Repairing cluster 4 refcount=1 reference=0 +Repairing cluster 5 refcount=1 reference=0 +No errors were found on the image. + +=== reftable_update === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error +ERROR cluster 0 refcount=0 reference=1 +ERROR cluster 1 refcount=0 reference=1 +ERROR cluster 3 refcount=0 reference=1 +Rebuilding refcount structure +Repairing cluster 1 refcount=1 reference=0 +No errors were found on the image. + +=== refblock_alloc === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error +ERROR cluster 0 refcount=0 reference=1 +ERROR cluster 1 refcount=0 reference=1 +ERROR cluster 3 refcount=0 reference=1 +Rebuilding refcount structure +Repairing cluster 1 refcount=1 reference=0 +No errors were found on the image. +*** done diff --git a/tests/qemu-iotests/102 b/tests/qemu-iotests/102 index 34b363f78f..161b1974ce 100755 --- a/tests/qemu-iotests/102 +++ b/tests/qemu-iotests/102 @@ -34,9 +34,10 @@ _cleanup() } trap "_cleanup; exit \$status" 0 1 2 3 15 -# get standard environment, filters and checks +# get standard environment, filters and qemu instance handling . ./common.rc . ./common.filter +. ./common.qemu _supported_fmt qcow2 _supported_proto file @@ -53,11 +54,27 @@ _make_test_img $IMG_SIZE $QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io # Remove data cluster from image (first cluster: image header, second: reftable, # third: refblock, fourth: L1 table, fifth: L2 table) -truncate -s $((5 * 64 * 1024)) "$TEST_IMG" +$QEMU_IMG resize -f raw "$TEST_IMG" $((5 * 64 * 1024)) $QEMU_IO -c map "$TEST_IMG" $QEMU_IMG map "$TEST_IMG" +echo +echo '=== Testing map on an image file truncated outside of qemu ===' +echo + +# Same as above, only now we concurrently truncate and map the image +_make_test_img $IMG_SIZE +$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io + +qemu_comm_method=monitor _launch_qemu -drive if=none,file="$TEST_IMG",id=drv0 + +$QEMU_IMG resize -f raw "$TEST_IMG" $((5 * 64 * 1024)) + +_send_qemu_cmd $QEMU_HANDLE 'qemu-io drv0 map' 'allocated' \ + | sed -e 's/^(qemu).*qemu-io drv0 map...$/(qemu) qemu-io drv0 map/' +_send_qemu_cmd $QEMU_HANDLE 'quit' '' + # success, all done echo '*** done' rm -f $seq.full diff --git a/tests/qemu-iotests/102.out b/tests/qemu-iotests/102.out index e0e9cdc541..eecde16ad5 100644 --- a/tests/qemu-iotests/102.out +++ b/tests/qemu-iotests/102.out @@ -5,6 +5,17 @@ QA output created by 102 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Image resized. [ 0] 128/ 128 sectors allocated at offset 0 bytes (1) Offset Length Mapped to File + +=== Testing map on an image file truncated outside of qemu === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536 +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Image resized. +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io drv0 map +[ 0] 128/ 128 sectors allocated at offset 0 bytes (1) *** done diff --git a/tests/qemu-iotests/107 b/tests/qemu-iotests/107 index cad1cf98a0..9862030469 100755 --- a/tests/qemu-iotests/107 +++ b/tests/qemu-iotests/107 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow2 -_supported_proto file +_supported_proto file nfs _supported_os Linux diff --git a/tests/qemu-iotests/111 b/tests/qemu-iotests/111 new file mode 100755 index 0000000000..6011c94b71 --- /dev/null +++ b/tests/qemu-iotests/111 @@ -0,0 +1,53 @@ +#!/bin/bash +# +# Test case for non-existing backing file when creating a qcow2 image +# and not specifying the size +# +# 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=mreitz@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 qed qcow qcow2 vmdk +_supported_proto file +_supported_os Linux +_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" + +$QEMU_IMG create -f $IMGFMT -b "$TEST_IMG.inexistent" "$TEST_IMG" 2>&1 \ + | _filter_testdir | _filter_imgfmt + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/111.out b/tests/qemu-iotests/111.out new file mode 100644 index 0000000000..683c01a679 --- /dev/null +++ b/tests/qemu-iotests/111.out @@ -0,0 +1,3 @@ +QA output created by 111 +qemu-img: TEST_DIR/t.IMGFMT: Could not open 'TEST_DIR/t.IMGFMT.inexistent': No such file or directory +*** done diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index f69cb6b916..3acdb307f4 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -213,5 +213,12 @@ _filter_img_info() -e "s/archipelago:a/TEST_DIR\//g" } +# filter out offsets and file names from qemu-img map +_filter_qemu_img_map() +{ + sed -e 's/\([0-9a-fx]* *[0-9a-fx]* *\)[0-9a-fx]* */\1/g' \ + -e 's/Mapped to *//' | _filter_testdir | _filter_imgfmt +} + # make sure this script returns success /bin/true diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 9bbd5d3a17..7dfe46940a 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -67,7 +67,7 @@ 058 rw auto quick 059 rw auto quick 060 rw auto quick -061 rw auto quick +061 rw auto 062 rw auto quick 063 rw auto quick 064 rw auto quick @@ -100,6 +100,8 @@ 091 rw auto quick 092 rw auto quick 095 rw auto quick +097 rw auto backing +098 rw auto backing quick 099 rw auto quick 100 rw auto quick 101 rw auto quick @@ -109,3 +111,4 @@ 105 rw auto quick 107 rw auto quick 108 rw auto quick +111 rw auto quick diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 39a4cfcf4d..f57f1548ac 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -267,8 +267,7 @@ class QMPTestCase(unittest.TestCase): self.assert_qmp(event, 'data/device', drive) self.assert_qmp_absent(event, 'data/error') if check_offset: - self.assert_qmp(event, 'data/offset', self.image_len) - self.assert_qmp(event, 'data/len', self.image_len) + self.assert_qmp(event, 'data/offset', event['data']['len']) completed = True self.assert_no_active_block_jobs() diff --git a/tests/qemu-iotests/sample_images/fake.parallels.bz2 b/tests/qemu-iotests/sample_images/fake.parallels.bz2 Binary files differdeleted file mode 100644 index ffb5f13bac..0000000000 --- a/tests/qemu-iotests/sample_images/fake.parallels.bz2 +++ /dev/null diff --git a/tests/qemu-iotests/sample_images/parallels-v1.bz2 b/tests/qemu-iotests/sample_images/parallels-v1.bz2 Binary files differnew file mode 100644 index 0000000000..d1ef142054 --- /dev/null +++ b/tests/qemu-iotests/sample_images/parallels-v1.bz2 diff --git a/tests/qemu-iotests/sample_images/parallels-v2.bz2 b/tests/qemu-iotests/sample_images/parallels-v2.bz2 Binary files differnew file mode 100644 index 0000000000..fd8614d061 --- /dev/null +++ b/tests/qemu-iotests/sample_images/parallels-v2.bz2 diff --git a/tests/tcg/xtensa/Makefile b/tests/tcg/xtensa/Makefile index a70c92be7e..522a63e36b 100644 --- a/tests/tcg/xtensa/Makefile +++ b/tests/tcg/xtensa/Makefile @@ -13,6 +13,7 @@ SIMFLAGS = --xtensa-core=DC_B_232L --exit_with_target_code $(EXTFLAGS) SIMDEBUG = --gdbserve=0 endif +HOST_CC = gcc CC = $(CROSS)gcc AS = $(CROSS)gcc -x assembler-with-cpp LD = $(CROSS)ld @@ -21,7 +22,7 @@ XTENSA_SRC_PATH = $(SRC_PATH)/tests/tcg/xtensa INCLUDE_DIRS = $(XTENSA_SRC_PATH) $(SRC_PATH)/target-xtensa/core-$(CORE) XTENSA_INC = $(addprefix -I,$(INCLUDE_DIRS)) -LDFLAGS = -T$(XTENSA_SRC_PATH)/linker.ld +LDFLAGS = -Tlinker.ld CRT = crt.o vectors.o @@ -59,13 +60,16 @@ TESTCASES += test_windowed.tst all: build +linker.ld: $(XTENSA_SRC_PATH)/linker.ld.S + $(HOST_CC) $(XTENSA_INC) -E -P $< -o $@ + %.o: $(XTENSA_SRC_PATH)/%.c $(CC) $(XTENSA_INC) $(CFLAGS) -c $< -o $@ %.o: $(XTENSA_SRC_PATH)/%.S $(CC) $(XTENSA_INC) $(ASFLAGS) -c $< -o $@ -%.tst: %.o $(XTENSA_SRC_PATH)/macros.inc $(CRT) Makefile +%.tst: %.o linker.ld $(XTENSA_SRC_PATH)/macros.inc $(CRT) Makefile $(LD) $(LDFLAGS) $(NOSTDFLAGS) $(CRT) $< -o $@ build: $(TESTCASES) @@ -85,4 +89,4 @@ host-debug-%.tst: %.tst gdb --args $(SIM) $(SIMFLAGS) ./$< clean: - $(RM) -fr $(TESTCASES) $(CRT) + $(RM) -fr $(TESTCASES) $(CRT) linker.ld diff --git a/tests/tcg/xtensa/linker.ld b/tests/tcg/xtensa/linker.ld deleted file mode 100644 index 4d0b307fd2..0000000000 --- a/tests/tcg/xtensa/linker.ld +++ /dev/null @@ -1,112 +0,0 @@ -OUTPUT_FORMAT("elf32-xtensa-le") -ENTRY(_start) - -__DYNAMIC = 0; - -MEMORY { - ram : ORIGIN = 0xd0000000, LENGTH = 0x08000000 /* 128M */ - rom : ORIGIN = 0xfe000000, LENGTH = 0x00001000 /* 4k */ -} - -SECTIONS -{ - .init : - { - *(.init) - *(.init.*) - } > rom - - .vector : - { - . = 0x00000000; - *(.vector.window_overflow_4) - *(.vector.window_overflow_4.*) - . = 0x00000040; - *(.vector.window_underflow_4) - *(.vector.window_underflow_4.*) - . = 0x00000080; - *(.vector.window_overflow_8) - *(.vector.window_overflow_8.*) - . = 0x000000c0; - *(.vector.window_underflow_8) - *(.vector.window_underflow_8.*) - . = 0x00000100; - *(.vector.window_overflow_12) - *(.vector.window_overflow_12.*) - . = 0x00000140; - *(.vector.window_underflow_12) - *(.vector.window_underflow_12.*) - - . = 0x00000180; - *(.vector.level2) - *(.vector.level2.*) - . = 0x000001c0; - *(.vector.level3) - *(.vector.level3.*) - . = 0x00000200; - *(.vector.level4) - *(.vector.level4.*) - . = 0x00000240; - *(.vector.level5) - *(.vector.level5.*) - . = 0x00000280; - *(.vector.level6) - *(.vector.level6.*) - . = 0x000002c0; - *(.vector.level7) - *(.vector.level7.*) - - . = 0x00000300; - *(.vector.kernel) - *(.vector.kernel.*) - . = 0x00000340; - *(.vector.user) - *(.vector.user.*) - . = 0x000003c0; - *(.vector.double) - *(.vector.double.*) - } > ram - - .text : - { - _ftext = .; - *(.text .stub .text.* .gnu.linkonce.t.* .literal .literal.*) - _etext = .; - } > ram - - .rodata : - { - . = ALIGN(4); - _frodata = .; - *(.rodata .rodata.* .gnu.linkonce.r.*) - *(.rodata1) - _erodata = .; - } > ram - - .data : - { - . = ALIGN(4); - _fdata = .; - *(.data .data.* .gnu.linkonce.d.*) - *(.data1) - _gp = ALIGN(16); - *(.sdata .sdata.* .gnu.linkonce.s.*) - _edata = .; - } > ram - - .bss : - { - . = ALIGN(4); - _fbss = .; - *(.dynsbss) - *(.sbss .sbss.* .gnu.linkonce.sb.*) - *(.scommon) - *(.dynbss) - *(.bss .bss.* .gnu.linkonce.b.*) - *(COMMON) - _ebss = .; - _end = .; - } > ram -} - -PROVIDE(_fstack = ORIGIN(ram) + LENGTH(ram) - 4); diff --git a/tests/tcg/xtensa/linker.ld.S b/tests/tcg/xtensa/linker.ld.S new file mode 100644 index 0000000000..f1e7fa9f8d --- /dev/null +++ b/tests/tcg/xtensa/linker.ld.S @@ -0,0 +1,130 @@ +#include <core-isa.h> + +#if XTENSA_HAVE_BE +OUTPUT_FORMAT("elf32-xtensa-be") +#else +OUTPUT_FORMAT("elf32-xtensa-le") +#endif +ENTRY(_start) + +__DYNAMIC = 0; + +MEMORY { + ram : ORIGIN = XCHAL_VECBASE_RESET_VADDR, LENGTH = 0x08000000 /* 128M */ + rom : ORIGIN = XCHAL_RESET_VECTOR_VADDR, LENGTH = 0x00001000 /* 4k */ +} + +SECTIONS +{ + .init : + { + *(.init) + *(.init.*) + } > rom + + .vector : + { + . = XCHAL_WINDOW_OF4_VECOFS; + *(.vector.window_overflow_4) + *(.vector.window_overflow_4.*) + . = XCHAL_WINDOW_UF4_VECOFS; + *(.vector.window_underflow_4) + *(.vector.window_underflow_4.*) + . = XCHAL_WINDOW_OF8_VECOFS; + *(.vector.window_overflow_8) + *(.vector.window_overflow_8.*) + . = XCHAL_WINDOW_UF8_VECOFS; + *(.vector.window_underflow_8) + *(.vector.window_underflow_8.*) + . = XCHAL_WINDOW_OF12_VECOFS; + *(.vector.window_overflow_12) + *(.vector.window_overflow_12.*) + . = XCHAL_WINDOW_UF12_VECOFS; + *(.vector.window_underflow_12) + *(.vector.window_underflow_12.*) + +#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 2 + . = XCHAL_INTLEVEL2_VECOFS; + *(.vector.level2) + *(.vector.level2.*) +#endif +#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 3 + . = XCHAL_INTLEVEL3_VECOFS; + *(.vector.level3) + *(.vector.level3.*) +#endif +#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 4 + . = XCHAL_INTLEVEL4_VECOFS; + *(.vector.level4) + *(.vector.level4.*) +#endif +#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 5 + . = XCHAL_INTLEVEL5_VECOFS; + *(.vector.level5) + *(.vector.level5.*) +#endif +#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 6 + . = XCHAL_INTLEVEL6_VECOFS; + *(.vector.level6) + *(.vector.level6.*) +#endif +#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 7 + . = XCHAL_INTLEVEL7_VECOFS; + *(.vector.level7) + *(.vector.level7.*) +#endif + + . = XCHAL_KERNEL_VECOFS; + *(.vector.kernel) + *(.vector.kernel.*) + . = XCHAL_USER_VECOFS; + *(.vector.user) + *(.vector.user.*) + . = XCHAL_DOUBLEEXC_VECOFS; + *(.vector.double) + *(.vector.double.*) + } > ram + + .text : + { + _ftext = .; + *(.text .stub .text.* .gnu.linkonce.t.* .literal .literal.*) + _etext = .; + } > ram + + .rodata : + { + . = ALIGN(4); + _frodata = .; + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.rodata1) + _erodata = .; + } > ram + + .data : + { + . = ALIGN(4); + _fdata = .; + *(.data .data.* .gnu.linkonce.d.*) + *(.data1) + _gp = ALIGN(16); + *(.sdata .sdata.* .gnu.linkonce.s.*) + _edata = .; + } > ram + + .bss : + { + . = ALIGN(4); + _fbss = .; + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + _ebss = .; + _end = .; + } > ram +} + +PROVIDE(_fstack = (ORIGIN(ram) & 0xf0000000) + LENGTH(ram) - 16); diff --git a/tests/tcg/xtensa/test_windowed.S b/tests/tcg/xtensa/test_windowed.S index 3de6d3763a..d851e8f43c 100644 --- a/tests/tcg/xtensa/test_windowed.S +++ b/tests/tcg/xtensa/test_windowed.S @@ -299,4 +299,55 @@ test entry entry_test 12 test_end +.macro entry_overflow_test window, free, next_window + set_vector window_overflow_4, 0 + set_vector window_overflow_8, 0 + set_vector window_overflow_12, 0 + set_vector window_overflow_\next_window, 10f + + movi a2, \window + movi a2, \free + movi a2, \next_window + reset_window %(1 | ((1 | (1 << ((\next_window) / 4))) << ((\free) / 4))) + reset_ps + movi a2, 0x4000f | ((\window) << 14) + wsr a2, ps + isync + movi a3, 0x12345678 + j 1f + .align 4 +1: + entry a3, 0x5678 + test_fail + .align 4 +10: + rsr a2, epc1 + movi a3, 1b + assert eq, a2, a3 + movi a2, 2f + wsr a2, epc1 + + rsr a2, windowbase + movi a3, (\free) / 4 + assert eq, a2, a3 + rfwo +2: +.endm + +.macro all_entry_overflow_tests + .irp window, 4, 8, 12 + .irp next_window, 4, 8, 12 + .irp free, 4, 8, 12 + .if \free <= \window + entry_overflow_test \window, \free, \next_window + .endif + .endr + .endr + .endr +.endm + +test entry_overflow + all_entry_overflow_tests +test_end + test_suite_end diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index fdf91e7897..75fedf0977 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -21,6 +21,15 @@ #include <sys/vfs.h> #include <qemu/sockets.h> +/* GLIB version compatibility flags */ +#if !GLIB_CHECK_VERSION(2, 26, 0) +#define G_TIME_SPAN_SECOND (G_GINT64_CONSTANT(1000000)) +#endif + +#if GLIB_CHECK_VERSION(2, 28, 0) +#define HAVE_MONOTONIC_TIME +#endif + #if GLIB_CHECK_VERSION(2, 32, 0) #define HAVE_MUTEX_INIT #define HAVE_COND_INIT @@ -107,6 +116,18 @@ static VhostUserMemory memory; static GMutex *data_mutex; static GCond *data_cond; +static gint64 _get_time(void) +{ +#ifdef HAVE_MONOTONIC_TIME + return g_get_monotonic_time(); +#else + GTimeVal time; + g_get_current_time(&time); + + return time.tv_sec * G_TIME_SPAN_SECOND + time.tv_usec; +#endif +} + static GMutex *_mutex_new(void) { GMutex *mutex; @@ -189,7 +210,7 @@ static void read_guest_mem(void) g_mutex_lock(data_mutex); - end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; + end_time = _get_time() + 5 * G_TIME_SPAN_SECOND; while (!fds_num) { if (!_cond_wait_until(data_cond, data_mutex, end_time)) { /* timeout has passed */ @@ -128,6 +128,7 @@ static inline void gdk_drawable_get_size(GdkWindow *w, gint *ww, gint *wh) #define GDK_KEY_q GDK_q #define GDK_KEY_plus GDK_plus #define GDK_KEY_minus GDK_minus +#define GDK_KEY_Pause GDK_Pause #endif #define HOTKEY_MODIFIERS (GDK_CONTROL_MASK | GDK_MOD1_MASK) @@ -1020,6 +1021,12 @@ static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque) } } +static void gd_accel_switch_vc(void *opaque) +{ + VirtualConsole *vc = opaque; + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE); +} + static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; @@ -1098,7 +1105,7 @@ static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) if (!s->full_screen) { gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); - gtk_widget_set_size_request(s->menu_bar, 0, 0); + gtk_widget_hide(s->menu_bar); if (vc->type == GD_VC_GFX) { gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), @@ -1109,7 +1116,7 @@ static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) } else { gtk_window_unfullscreen(GTK_WINDOW(s->window)); gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s); - gtk_widget_set_size_request(s->menu_bar, -1, -1); + gtk_widget_show(s->menu_bar); s->full_screen = FALSE; if (vc->type == GD_VC_GFX) { vc->gfx.scale_x = 1.0; @@ -1123,6 +1130,12 @@ static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) gd_update_cursor(vc); } +static void gd_accel_full_screen(void *opaque) +{ + GtkDisplayState *s = opaque; + gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item)); +} + static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; @@ -1401,19 +1414,21 @@ static gboolean gd_focus_out_event(GtkWidget *widget, static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc, int idx, GSList *group, GtkWidget *view_menu) { - char path[32]; - - snprintf(path, sizeof(path), "<QEMU>/View/VC%d", idx); - vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label); - group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item)); - gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path); - gtk_accel_map_add_entry(path, GDK_KEY_1 + idx, HOTKEY_MODIFIERS); + gtk_accel_group_connect(s->accel_group, GDK_KEY_1 + idx, + HOTKEY_MODIFIERS, 0, + g_cclosure_new_swap(G_CALLBACK(gd_accel_switch_vc), vc, NULL)); +#if GTK_CHECK_VERSION(3, 8, 0) + gtk_accel_label_set_accel( + GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(vc->menu_item))), + GDK_KEY_1 + idx, HOTKEY_MODIFIERS); +#endif g_signal_connect(vc->menu_item, "activate", G_CALLBACK(gd_menu_switch_vc), s); gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item); + group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item)); return group; } @@ -1605,13 +1620,13 @@ static void gd_connect_signals(GtkDisplayState *s) G_CALLBACK(gd_change_page), s); } -static GtkWidget *gd_create_menu_machine(GtkDisplayState *s, GtkAccelGroup *accel_group) +static GtkWidget *gd_create_menu_machine(GtkDisplayState *s) { GtkWidget *machine_menu; GtkWidget *separator; machine_menu = gtk_menu_new(); - gtk_menu_set_accel_group(GTK_MENU(machine_menu), accel_group); + gtk_menu_set_accel_group(GTK_MENU(machine_menu), s->accel_group); s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause")); gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item); @@ -1692,7 +1707,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, return group; } -static GtkWidget *gd_create_menu_view(GtkDisplayState *s, GtkAccelGroup *accel_group) +static GtkWidget *gd_create_menu_view(GtkDisplayState *s) { GSList *group = NULL; GtkWidget *view_menu; @@ -1701,13 +1716,17 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s, GtkAccelGroup *accel_g int vc; view_menu = gtk_menu_new(); - gtk_menu_set_accel_group(GTK_MENU(view_menu), accel_group); + gtk_menu_set_accel_group(GTK_MENU(view_menu), s->accel_group); s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen")); - gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->full_screen_item), - "<QEMU>/View/Full Screen"); - gtk_accel_map_add_entry("<QEMU>/View/Full Screen", GDK_KEY_f, - HOTKEY_MODIFIERS); + + gtk_accel_group_connect(s->accel_group, GDK_KEY_f, HOTKEY_MODIFIERS, 0, + g_cclosure_new_swap(G_CALLBACK(gd_accel_full_screen), s, NULL)); +#if GTK_CHECK_VERSION(3, 8, 0) + gtk_accel_label_set_accel( + GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->full_screen_item))), + GDK_KEY_f, HOTKEY_MODIFIERS); +#endif gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item); separator = gtk_separator_menu_item_new(); @@ -1783,11 +1802,9 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s, GtkAccelGroup *accel_g static void gd_create_menus(GtkDisplayState *s) { - GtkAccelGroup *accel_group; - - accel_group = gtk_accel_group_new(); - s->machine_menu = gd_create_menu_machine(s, accel_group); - s->view_menu = gd_create_menu_view(s, accel_group); + s->accel_group = gtk_accel_group_new(); + s->machine_menu = gd_create_menu_machine(s); + s->view_menu = gd_create_menu_view(s); s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine")); gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item), @@ -1798,9 +1815,8 @@ static void gd_create_menus(GtkDisplayState *s) gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu); gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item); - g_object_set_data(G_OBJECT(s->window), "accel_group", accel_group); - gtk_window_add_accel_group(GTK_WINDOW(s->window), accel_group); - s->accel_group = accel_group; + g_object_set_data(G_OBJECT(s->window), "accel_group", s->accel_group); + gtk_window_add_accel_group(GTK_WINDOW(s->window), s->accel_group); } static void gd_set_keycode_type(GtkDisplayState *s) @@ -190,6 +190,9 @@ int nb_numa_nodes; int max_numa_nodeid; NodeInfo numa_info[MAX_NODES]; +/* The bytes in qemu_uuid[] are in the order specified by RFC4122, _not_ in the + * little-endian "wire format" described in the SMBIOS 2.6 specification. + */ uint8_t qemu_uuid[16]; bool qemu_uuid_set; @@ -1420,6 +1423,7 @@ static void machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); QEMUMachine *qm = data; + mc->family = qm->family; mc->name = qm->name; mc->alias = qm->alias; mc->desc = qm->desc; @@ -1437,9 +1441,11 @@ static void machine_class_init(ObjectClass *oc, void *data) mc->no_floppy = qm->no_floppy; mc->no_cdrom = qm->no_cdrom; mc->no_sdcard = qm->no_sdcard; + mc->has_dynamic_sysbus = qm->has_dynamic_sysbus; mc->is_default = qm->is_default; mc->default_machine_opts = qm->default_machine_opts; mc->default_boot_order = qm->default_boot_order; + mc->default_display = qm->default_display; mc->compat_props = qm->compat_props; mc->hw_version = qm->hw_version; } @@ -2492,7 +2498,41 @@ static int debugcon_parse(const char *devname) return 0; } -static MachineClass *machine_parse(const char *name) +static gint machine_class_cmp(gconstpointer a, gconstpointer b) +{ + const MachineClass *mc1 = a, *mc2 = b; + int res; + + if (mc1->family == NULL) { + if (mc2->family == NULL) { + /* Compare standalone machine types against each other; they sort + * in increasing order. + */ + return strcmp(object_class_get_name(OBJECT_CLASS(mc1)), + object_class_get_name(OBJECT_CLASS(mc2))); + } + + /* Standalone machine types sort after families. */ + return 1; + } + + if (mc2->family == NULL) { + /* Families sort before standalone machine types. */ + return -1; + } + + /* Families sort between each other alphabetically increasingly. */ + res = strcmp(mc1->family, mc2->family); + if (res != 0) { + return res; + } + + /* Within the same family, machine types sort in decreasing order. */ + return strcmp(object_class_get_name(OBJECT_CLASS(mc2)), + object_class_get_name(OBJECT_CLASS(mc1))); +} + + static MachineClass *machine_parse(const char *name) { MachineClass *mc = NULL; GSList *el, *machines = object_class_get_list(TYPE_MACHINE, false); @@ -2508,6 +2548,7 @@ static MachineClass *machine_parse(const char *name) error_printf("Use -machine help to list supported machines!\n"); } else { printf("Supported machines are:\n"); + machines = g_slist_sort(machines, machine_class_cmp); for (el = machines; el; el = el->next) { MachineClass *mc = el->data; if (mc->alias) { @@ -4189,7 +4230,9 @@ int main(int argc, char **argv, char **envp) /* If no default VGA is requested, the default is "none". */ if (default_vga) { - if (cirrus_vga_available()) { + if (machine_class->default_display) { + vga_model = machine_class->default_display; + } else if (cirrus_vga_available()) { vga_model = "cirrus"; } else if (vga_available()) { vga_model = "std"; |