diff options
114 files changed, 4649 insertions, 5155 deletions
diff --git a/Makefile b/Makefile index 3c8f19c964..7d0c8ec7cc 100644 --- a/Makefile +++ b/Makefile @@ -45,8 +45,8 @@ endif endif GENERATED_HEADERS = config-host.h qemu-options.def -GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h -GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c +GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h qapi-event.h +GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c qapi-event.c GENERATED_HEADERS += trace/generated-events.h GENERATED_SOURCES += trace/generated-events.c @@ -202,7 +202,7 @@ Makefile: $(version-obj-y) $(version-lobj-y) # Build libraries libqemustub.a: $(stub-obj-y) -libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o +libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o qapi-event.o block-modules = $(foreach o,$(block-obj-m),"$(basename $(subst /,-,$o))",) NULL util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)' @@ -246,18 +246,27 @@ $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py) $(gen-out-type) -o qga/qapi-generated -p "qga-" -i $<, \ " GEN $@") +qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \ + $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \ + $(SRC_PATH)/qapi-event.json + qapi-types.c qapi-types.h :\ -$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py) +$(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py) $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py \ $(gen-out-type) -o "." -b -i $<, \ " GEN $@") qapi-visit.c qapi-visit.h :\ -$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py) +$(qapi-modules) $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py) $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py \ $(gen-out-type) -o "." -b -i $<, \ " GEN $@") +qapi-event.c qapi-event.h :\ +$(qapi-modules) $(SRC_PATH)/scripts/qapi-event.py $(qapi-py) + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py \ + $(gen-out-type) -o "." -b -i $<, \ + " GEN $@") qmp-commands.h qmp-marshal.c :\ -$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py) +$(qapi-modules) $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py) $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \ $(gen-out-type) -o "." -m -i $<, \ " GEN $@") diff --git a/Makefile.objs b/Makefile.objs index b897e1dc34..1f76cea569 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -12,7 +12,7 @@ block-obj-y += main-loop.o iohandler.o qemu-timer.o block-obj-$(CONFIG_POSIX) += aio-posix.o block-obj-$(CONFIG_WIN32) += aio-win32.o block-obj-y += block/ -block-obj-y += qapi-types.o qapi-visit.o +block-obj-y += qapi-types.o qapi-visit.o qapi-event.o block-obj-y += qemu-io-cmds.o block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o diff --git a/backends/baum.c b/backends/baum.c index 759003f623..796512d387 100644 --- a/backends/baum.c +++ b/backends/baum.c @@ -574,7 +574,7 @@ CharDriverState *chr_baum_init(void) int tty; baum = g_malloc0(sizeof(BaumDriverState)); - baum->chr = chr = g_malloc0(sizeof(CharDriverState)); + baum->chr = chr = qemu_chr_alloc(); chr->opaque = baum; chr->chr_write = baum_write; diff --git a/backends/msmouse.c b/backends/msmouse.c index c0dbfcdd6b..650a5314d4 100644 --- a/backends/msmouse.c +++ b/backends/msmouse.c @@ -67,7 +67,7 @@ CharDriverState *qemu_chr_open_msmouse(void) { CharDriverState *chr; - chr = g_malloc0(sizeof(CharDriverState)); + chr = qemu_chr_alloc(); chr->chr_write = msmouse_chr_write; chr->chr_close = msmouse_chr_close; chr->explicit_be_open = true; diff --git a/balloon.c b/balloon.c index e321f2c688..b70da4fa29 100644 --- a/balloon.c +++ b/balloon.c @@ -81,19 +81,6 @@ static int qemu_balloon_status(BalloonInfo *info) return 1; } -void qemu_balloon_changed(int64_t actual) -{ - QObject *data; - - data = qobject_from_jsonf("{ 'actual': %" PRId64 " }", - actual); - - monitor_protocol_event(QEVENT_BALLOON_CHANGE, data); - - qobject_decref(data); -} - - BalloonInfo *qmp_query_balloon(Error **errp) { BalloonInfo *info; diff --git a/block.c b/block.c index ff44e76c87..e6f9b71e11 100644 --- a/block.c +++ b/block.c @@ -24,7 +24,6 @@ #include "config-host.h" #include "qemu-common.h" #include "trace.h" -#include "monitor/monitor.h" #include "block/block_int.h" #include "block/blockjob.h" #include "qemu/module.h" @@ -35,6 +34,7 @@ #include "block/qapi.h" #include "qmp-commands.h" #include "qemu/timer.h" +#include "qapi-event.h" #ifdef CONFIG_BSD #include <sys/types.h> @@ -2132,47 +2132,6 @@ void bdrv_set_dev_ops(BlockDriverState *bs, const BlockDevOps *ops, bs->dev_opaque = opaque; } -void bdrv_emit_qmp_error_event(const BlockDriverState *bdrv, - enum MonitorEvent ev, - BlockErrorAction action, bool is_read) -{ - QObject *data; - const char *action_str; - - switch (action) { - case BDRV_ACTION_REPORT: - action_str = "report"; - break; - case BDRV_ACTION_IGNORE: - action_str = "ignore"; - break; - case BDRV_ACTION_STOP: - action_str = "stop"; - break; - default: - abort(); - } - - data = qobject_from_jsonf("{ 'device': %s, 'action': %s, 'operation': %s }", - bdrv->device_name, - action_str, - is_read ? "read" : "write"); - monitor_protocol_event(ev, data); - - qobject_decref(data); -} - -static void bdrv_emit_qmp_eject_event(BlockDriverState *bs, bool ejected) -{ - QObject *data; - - data = qobject_from_jsonf("{ 'device': %s, 'tray-open': %i }", - bdrv_get_device_name(bs), ejected); - monitor_protocol_event(QEVENT_DEVICE_TRAY_MOVED, data); - - qobject_decref(data); -} - static void bdrv_dev_change_media_cb(BlockDriverState *bs, bool load) { if (bs->dev_ops && bs->dev_ops->change_media_cb) { @@ -2180,11 +2139,13 @@ static void bdrv_dev_change_media_cb(BlockDriverState *bs, bool load) bs->dev_ops->change_media_cb(bs->dev_opaque, load); if (tray_was_closed) { /* tray open */ - bdrv_emit_qmp_eject_event(bs, true); + qapi_event_send_device_tray_moved(bdrv_get_device_name(bs), + true, &error_abort); } if (load) { /* tray close */ - bdrv_emit_qmp_eject_event(bs, false); + qapi_event_send_device_tray_moved(bdrv_get_device_name(bs), + false, &error_abort); } } } @@ -3606,13 +3567,14 @@ BlockErrorAction bdrv_get_error_action(BlockDriverState *bs, bool is_read, int e switch (on_err) { case BLOCKDEV_ON_ERROR_ENOSPC: - return (error == ENOSPC) ? BDRV_ACTION_STOP : BDRV_ACTION_REPORT; + return (error == ENOSPC) ? + BLOCK_ERROR_ACTION_STOP : BLOCK_ERROR_ACTION_REPORT; case BLOCKDEV_ON_ERROR_STOP: - return BDRV_ACTION_STOP; + return BLOCK_ERROR_ACTION_STOP; case BLOCKDEV_ON_ERROR_REPORT: - return BDRV_ACTION_REPORT; + return BLOCK_ERROR_ACTION_REPORT; case BLOCKDEV_ON_ERROR_IGNORE: - return BDRV_ACTION_IGNORE; + return BLOCK_ERROR_ACTION_IGNORE; default: abort(); } @@ -3627,7 +3589,7 @@ void bdrv_error_action(BlockDriverState *bs, BlockErrorAction action, { assert(error >= 0); - if (action == BDRV_ACTION_STOP) { + if (action == BLOCK_ERROR_ACTION_STOP) { /* First set the iostatus, so that "info block" returns an iostatus * that matches the events raised so far (an additional error iostatus * is fine, but not a lost one). @@ -3643,10 +3605,16 @@ void bdrv_error_action(BlockDriverState *bs, BlockErrorAction action, * also ensures that the STOP/RESUME pair of events is emitted. */ qemu_system_vmstop_request_prepare(); - bdrv_emit_qmp_error_event(bs, QEVENT_BLOCK_IO_ERROR, action, is_read); + qapi_event_send_block_io_error(bdrv_get_device_name(bs), + is_read ? IO_OPERATION_TYPE_READ : + IO_OPERATION_TYPE_WRITE, + action, &error_abort); qemu_system_vmstop_request(RUN_STATE_IO_ERROR); } else { - bdrv_emit_qmp_error_event(bs, QEVENT_BLOCK_IO_ERROR, action, is_read); + qapi_event_send_block_io_error(bdrv_get_device_name(bs), + is_read ? IO_OPERATION_TYPE_READ : + IO_OPERATION_TYPE_WRITE, + action, &error_abort); } } @@ -5216,7 +5184,8 @@ void bdrv_eject(BlockDriverState *bs, bool eject_flag) } if (bs->device_name[0] != '\0') { - bdrv_emit_qmp_eject_event(bs, eject_flag); + qapi_event_send_device_tray_moved(bdrv_get_device_name(bs), + eject_flag, &error_abort); } } diff --git a/block/backup.c b/block/backup.c index 15a2e55e8e..7978ae2e50 100644 --- a/block/backup.c +++ b/block/backup.c @@ -325,7 +325,7 @@ static void coroutine_fn backup_run(void *opaque) /* Depending on error action, fail now or retry cluster */ BlockErrorAction action = backup_error_action(job, error_is_read, -ret); - if (action == BDRV_ACTION_REPORT) { + if (action == BLOCK_ERROR_ACTION_REPORT) { break; } else { start--; diff --git a/block/mirror.c b/block/mirror.c index 94c8661777..301a04de8e 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -118,7 +118,7 @@ static void mirror_write_complete(void *opaque, int ret) bdrv_set_dirty(source, op->sector_num, op->nb_sectors); action = mirror_error_action(s, false, -ret); - if (action == BDRV_ACTION_REPORT && s->ret >= 0) { + if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) { s->ret = ret; } } @@ -135,7 +135,7 @@ static void mirror_read_complete(void *opaque, int ret) bdrv_set_dirty(source, op->sector_num, op->nb_sectors); action = mirror_error_action(s, true, -ret); - if (action == BDRV_ACTION_REPORT && s->ret >= 0) { + if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) { s->ret = ret; } @@ -415,7 +415,8 @@ static void coroutine_fn mirror_run(void *opaque) trace_mirror_before_flush(s); ret = bdrv_flush(s->target); if (ret < 0) { - if (mirror_error_action(s, false, -ret) == BDRV_ACTION_REPORT) { + if (mirror_error_action(s, false, -ret) == + BLOCK_ERROR_ACTION_REPORT) { goto immediate_exit; } } else { @@ -426,7 +427,7 @@ static void coroutine_fn mirror_run(void *opaque) */ s->common.offset = end * BDRV_SECTOR_SIZE; if (!s->synced) { - block_job_ready(&s->common); + block_job_event_ready(&s->common); s->synced = true; } diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 9507aef847..cc6cf743d6 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -27,6 +27,7 @@ #include "block/qcow2.h" #include "qemu/range.h" #include "qapi/qmp/types.h" +#include "qapi-event.h" static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size); static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, @@ -1807,7 +1808,6 @@ int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, } else if (ret > 0) { int metadata_ol_bitnr = ffs(ret) - 1; char *message; - QObject *data; assert(metadata_ol_bitnr < QCOW2_OL_MAX_BITNR); @@ -1816,12 +1816,14 @@ int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, metadata_ol_names[metadata_ol_bitnr]); message = g_strdup_printf("Prevented %s overwrite", metadata_ol_names[metadata_ol_bitnr]); - data = qobject_from_jsonf("{ 'device': %s, 'msg': %s, 'offset': %" - PRId64 ", 'size': %" PRId64 " }", bs->device_name, message, - offset, size); - monitor_protocol_event(QEVENT_BLOCK_IMAGE_CORRUPTED, data); + qapi_event_send_block_image_corrupted(bdrv_get_device_name(bs), + message, + true, + offset, + true, + size, + &error_abort); g_free(message); - qobject_decref(data); qcow2_mark_corrupt(bs); bs->drv = NULL; /* make BDS unusable */ diff --git a/block/quorum.c b/block/quorum.c index 426077a520..86802d306b 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -17,6 +17,7 @@ #include <gnutls/crypto.h> #include "block/block_int.h" #include "qapi/qmp/qjson.h" +#include "qapi-event.h" #define HASH_LENGTH 32 @@ -198,32 +199,22 @@ static QuorumAIOCB *quorum_aio_get(BDRVQuorumState *s, static void quorum_report_bad(QuorumAIOCB *acb, char *node_name, int ret) { - QObject *data; - assert(node_name); - data = qobject_from_jsonf("{ 'node-name': %s" - ", 'sector-num': %" PRId64 - ", 'sectors-count': %d }", - node_name, acb->sector_num, acb->nb_sectors); + const char *msg = NULL; if (ret < 0) { - QDict *dict = qobject_to_qdict(data); - qdict_put(dict, "error", qstring_from_str(strerror(-ret))); + msg = strerror(-ret); } - monitor_protocol_event(QEVENT_QUORUM_REPORT_BAD, data); - qobject_decref(data); + qapi_event_send_quorum_report_bad(!!msg, msg, node_name, + acb->sector_num, acb->nb_sectors, &error_abort); } static void quorum_report_failure(QuorumAIOCB *acb) { - QObject *data; const char *reference = acb->common.bs->device_name[0] ? acb->common.bs->device_name : acb->common.bs->node_name; - data = qobject_from_jsonf("{ 'reference': %s" - ", 'sector-num': %" PRId64 - ", 'sectors-count': %d }", - reference, acb->sector_num, acb->nb_sectors); - monitor_protocol_event(QEVENT_QUORUM_FAILURE, data); - qobject_decref(data); + + qapi_event_send_quorum_failure(reference, acb->sector_num, + acb->nb_sectors, &error_abort); } static int quorum_vote_error(QuorumAIOCB *acb); diff --git a/block/stream.c b/block/stream.c index 91d18a2db7..043340994d 100644 --- a/block/stream.c +++ b/block/stream.c @@ -159,14 +159,14 @@ wait: BlockErrorAction action = block_job_error_action(&s->common, s->common.bs, s->on_error, true, -ret); - if (action == BDRV_ACTION_STOP) { + if (action == BLOCK_ERROR_ACTION_STOP) { n = 0; continue; } if (error == 0) { error = ret; } - if (action == BDRV_ACTION_REPORT) { + if (action == BLOCK_ERROR_ACTION_REPORT) { break; } } diff --git a/blockdev.c b/blockdev.c index 9b0f8ac45e..03ab153d01 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1847,23 +1847,21 @@ void qmp_block_resize(bool has_device, const char *device, static void block_job_cb(void *opaque, int ret) { BlockDriverState *bs = opaque; - QObject *obj; + const char *msg = NULL; trace_block_job_cb(bs, bs->job, ret); assert(bs->job); - obj = qobject_from_block_job(bs->job); + if (ret < 0) { - QDict *dict = qobject_to_qdict(obj); - qdict_put(dict, "error", qstring_from_str(strerror(-ret))); + msg = strerror(-ret); } if (block_job_is_cancelled(bs->job)) { - monitor_protocol_event(QEVENT_BLOCK_JOB_CANCELLED, obj); + block_job_event_cancelled(bs->job); } else { - monitor_protocol_event(QEVENT_BLOCK_JOB_COMPLETED, obj); + block_job_event_completed(bs->job, msg); } - qobject_decref(obj); bdrv_put_ref_bh_schedule(bs); } diff --git a/blockjob.c b/blockjob.c index 7d84ca1d6c..4da86cdfcd 100644 --- a/blockjob.c +++ b/blockjob.c @@ -26,7 +26,6 @@ #include "config-host.h" #include "qemu-common.h" #include "trace.h" -#include "monitor/monitor.h" #include "block/block.h" #include "block/blockjob.h" #include "block/block_int.h" @@ -34,6 +33,7 @@ #include "block/coroutine.h" #include "qmp-commands.h" #include "qemu/timer.h" +#include "qapi-event.h" void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs, int64_t speed, BlockDriverCompletionFunc *cb, @@ -232,26 +232,31 @@ static void block_job_iostatus_set_err(BlockJob *job, int error) } } +void block_job_event_cancelled(BlockJob *job) +{ + qapi_event_send_block_job_cancelled(job->driver->job_type, + bdrv_get_device_name(job->bs), + job->len, + job->offset, + job->speed, + &error_abort); +} -QObject *qobject_from_block_job(BlockJob *job) +void block_job_event_completed(BlockJob *job, const char *msg) { - return qobject_from_jsonf("{ 'type': %s," - "'device': %s," - "'len': %" PRId64 "," - "'offset': %" PRId64 "," - "'speed': %" PRId64 " }", - BlockJobType_lookup[job->driver->job_type], - bdrv_get_device_name(job->bs), - job->len, - job->offset, - job->speed); + qapi_event_send_block_job_completed(job->driver->job_type, + bdrv_get_device_name(job->bs), + job->len, + job->offset, + job->speed, + !!msg, + msg, + &error_abort); } -void block_job_ready(BlockJob *job) +void block_job_event_ready(BlockJob *job) { - QObject *data = qobject_from_block_job(job); - monitor_protocol_event(QEVENT_BLOCK_JOB_READY, data); - qobject_decref(data); + qapi_event_send_block_job_ready(bdrv_get_device_name(job->bs), &error_abort); } BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs, @@ -262,22 +267,26 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs, switch (on_err) { case BLOCKDEV_ON_ERROR_ENOSPC: - action = (error == ENOSPC) ? BDRV_ACTION_STOP : BDRV_ACTION_REPORT; + action = (error == ENOSPC) ? + BLOCK_ERROR_ACTION_STOP : BLOCK_ERROR_ACTION_REPORT; break; case BLOCKDEV_ON_ERROR_STOP: - action = BDRV_ACTION_STOP; + action = BLOCK_ERROR_ACTION_STOP; break; case BLOCKDEV_ON_ERROR_REPORT: - action = BDRV_ACTION_REPORT; + action = BLOCK_ERROR_ACTION_REPORT; break; case BLOCKDEV_ON_ERROR_IGNORE: - action = BDRV_ACTION_IGNORE; + action = BLOCK_ERROR_ACTION_IGNORE; break; default: abort(); } - bdrv_emit_qmp_error_event(job->bs, QEVENT_BLOCK_JOB_ERROR, action, is_read); - if (action == BDRV_ACTION_STOP) { + qapi_event_send_block_job_error(bdrv_get_device_name(bs), + is_read ? IO_OPERATION_TYPE_READ : + IO_OPERATION_TYPE_WRITE, + action, &error_abort); + if (action == BLOCK_ERROR_ACTION_STOP) { block_job_pause(job); block_job_iostatus_set_err(job, error); if (bs != job->bs) { diff --git a/configure b/configure index 998964e6c0..9c1471e70f 100755 --- a/configure +++ b/configure @@ -4747,6 +4747,8 @@ elif test "$ARCH" = "s390x" ; then QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/s390 $QEMU_INCLUDES" elif test "$ARCH" = "x86_64" -o "$ARCH" = "x32" ; then QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/i386 $QEMU_INCLUDES" +elif test "$ARCH" = "ppc64" ; then + QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/ppc $QEMU_INCLUDES" else QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/\$(ARCH) $QEMU_INCLUDES" fi diff --git a/cpus.c b/cpus.c index 06da4e7665..5e7f2cf3cf 100644 --- a/cpus.c +++ b/cpus.c @@ -26,6 +26,7 @@ #include "config-host.h" #include "monitor/monitor.h" +#include "qapi/qmp/qerror.h" #include "sysemu/sysemu.h" #include "exec/gdbstub.h" #include "sysemu/dma.h" @@ -38,6 +39,7 @@ #include "qemu/main-loop.h" #include "qemu/bitmap.h" #include "qemu/seqlock.h" +#include "qapi-event.h" #ifndef _WIN32 #include "qemu/compatfd.h" @@ -530,7 +532,7 @@ static int do_vm_stop(RunState state) pause_all_vcpus(); runstate_set(state); vm_state_notify(0, state); - monitor_protocol_event(QEVENT_STOP, NULL); + qapi_event_send_stop(&error_abort); } bdrv_drain_all(); diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index dea0d505a7..3a0c99e1da 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -215,6 +215,24 @@ An example command is: 'data': { 'arg1': 'str', '*arg2': 'str' }, 'returns': 'str' } +=== Events === + +Events are defined with the keyword 'event'. When 'data' is also specified, +additional info will be carried on. Finally there will be C API generated +in qapi-event.h; when called by QEMU code, a message with timestamp will +be emitted on the wire. If timestamp is -1, it means failure to retrieve host +time. + +An example event is: + +{ 'event': 'EVENT_C', + 'data': { '*a': 'int', 'b': 'str' } } + +Resulting in this JSON object: + +{ "event": "EVENT_C", + "data": { "b": "test string" }, + "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } == Code generation == diff --git a/docs/qmp/qmp-events.txt b/docs/qmp/qmp-events.txt deleted file mode 100644 index 22fea580a9..0000000000 --- a/docs/qmp/qmp-events.txt +++ /dev/null @@ -1,551 +0,0 @@ - QEMU Machine Protocol Events - ============================ - -ACPI_DEVICE_OST ---------------- - -Emitted when guest executes ACPI _OST method. - - - data: ACPIOSTInfo type as described in qapi-schema.json - -{ "event": "ACPI_DEVICE_OST", - "data": { "device": "d1", "slot": "0", "slot-type": "DIMM", "source": 1, "status": 0 } } - -BALLOON_CHANGE --------------- - -Emitted when the guest changes the actual BALLOON level. This -value is equivalent to the 'actual' field return by the -'query-balloon' command - -Data: - -- "actual": actual level of the guest memory balloon in bytes (json-number) - -Example: - -{ "event": "BALLOON_CHANGE", - "data": { "actual": 944766976 }, - "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } - -BLOCK_IMAGE_CORRUPTED ---------------------- - -Emitted when a disk image is being marked corrupt. - -Data: - -- "device": Device name (json-string) -- "msg": Informative message (e.g., reason for the corruption) (json-string) -- "offset": If the corruption resulted from an image access, this is the access - offset into the image (json-int) -- "size": If the corruption resulted from an image access, this is the access - size (json-int) - -Example: - -{ "event": "BLOCK_IMAGE_CORRUPTED", - "data": { "device": "ide0-hd0", - "msg": "Prevented active L1 table overwrite", "offset": 196608, - "size": 65536 }, - "timestamp": { "seconds": 1378126126, "microseconds": 966463 } } - -BLOCK_IO_ERROR --------------- - -Emitted when a disk I/O error occurs. - -Data: - -- "device": device name (json-string) -- "operation": I/O operation (json-string, "read" or "write") -- "action": action that has been taken, it's one of the following (json-string): - "ignore": error has been ignored - "report": error has been reported to the device - "stop": the VM is going to stop because of the error - -Example: - -{ "event": "BLOCK_IO_ERROR", - "data": { "device": "ide0-hd1", - "operation": "write", - "action": "stop" }, - "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } - -Note: If action is "stop", a STOP event will eventually follow the -BLOCK_IO_ERROR event. - -BLOCK_JOB_CANCELLED -------------------- - -Emitted when a block job has been cancelled. - -Data: - -- "type": Job type (json-string; "stream" for image streaming - "commit" for block commit) -- "device": Device name (json-string) -- "len": Maximum progress value (json-int) -- "offset": Current progress value (json-int) - On success this is equal to len. - On failure this is less than len. -- "speed": Rate limit, bytes per second (json-int) - -Example: - -{ "event": "BLOCK_JOB_CANCELLED", - "data": { "type": "stream", "device": "virtio-disk0", - "len": 10737418240, "offset": 134217728, - "speed": 0 }, - "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } - -BLOCK_JOB_COMPLETED -------------------- - -Emitted when a block job has completed. - -Data: - -- "type": Job type (json-string; "stream" for image streaming - "commit" for block commit) -- "device": Device name (json-string) -- "len": Maximum progress value (json-int) -- "offset": Current progress value (json-int) - On success this is equal to len. - On failure this is less than len. -- "speed": Rate limit, bytes per second (json-int) -- "error": Error message (json-string, optional) - Only present on failure. This field contains a human-readable - error message. There are no semantics other than that streaming - has failed and clients should not try to interpret the error - string. - -Example: - -{ "event": "BLOCK_JOB_COMPLETED", - "data": { "type": "stream", "device": "virtio-disk0", - "len": 10737418240, "offset": 10737418240, - "speed": 0 }, - "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } - -BLOCK_JOB_ERROR ---------------- - -Emitted when a block job encounters an error. - -Data: - -- "device": device name (json-string) -- "operation": I/O operation (json-string, "read" or "write") -- "action": action that has been taken, it's one of the following (json-string): - "ignore": error has been ignored, the job may fail later - "report": error will be reported and the job canceled - "stop": error caused job to be paused - -Example: - -{ "event": "BLOCK_JOB_ERROR", - "data": { "device": "ide0-hd1", - "operation": "write", - "action": "stop" }, - "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } - -BLOCK_JOB_READY ---------------- - -Emitted when a block job is ready to complete. - -Data: - -- "device": device name (json-string) - -Example: - -{ "event": "BLOCK_JOB_READY", - "data": { "device": "ide0-hd1" }, - "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } - -Note: The "ready to complete" status is always reset by a BLOCK_JOB_ERROR -event. - -DEVICE_DELETED --------------- - -Emitted whenever the device removal completion is acknowledged -by the guest. -At this point, it's safe to reuse the specified device ID. -Device removal can be initiated by the guest or by HMP/QMP commands. - -Data: - -- "device": device name (json-string, optional) -- "path": device path (json-string) - -{ "event": "DEVICE_DELETED", - "data": { "device": "virtio-net-pci-0", - "path": "/machine/peripheral/virtio-net-pci-0" }, - "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } - -DEVICE_TRAY_MOVED ------------------ - -It's emitted whenever the tray of a removable device is moved by the guest -or by HMP/QMP commands. - -Data: - -- "device": device name (json-string) -- "tray-open": true if the tray has been opened or false if it has been closed - (json-bool) - -{ "event": "DEVICE_TRAY_MOVED", - "data": { "device": "ide1-cd0", - "tray-open": true - }, - "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } - -GUEST_PANICKED --------------- - -Emitted when guest OS panic is detected. - -Data: - -- "action": Action that has been taken (json-string, currently always "pause"). - -Example: - -{ "event": "GUEST_PANICKED", - "data": { "action": "pause" } } - -NIC_RX_FILTER_CHANGED ---------------------- - -The event is emitted once until the query command is executed, -the first event will always be emitted. - -Data: - -- "name": net client name (json-string) -- "path": device path (json-string) - -{ "event": "NIC_RX_FILTER_CHANGED", - "data": { "name": "vnet0", - "path": "/machine/peripheral/vnet0/virtio-backend" }, - "timestamp": { "seconds": 1368697518, "microseconds": 326866 } } -} - -QUORUM_FAILURE --------------- - -Emitted by the Quorum block driver if it fails to establish a quorum. - -Data: - -- "reference": device name if defined else node name. -- "sector-num": Number of the first sector of the failed read operation. -- "sector-count": Failed read operation sector count. - -Example: - -{ "event": "QUORUM_FAILURE", - "data": { "reference": "usr1", "sector-num": 345435, "sector-count": 5 }, - "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } - -QUORUM_REPORT_BAD ------------------ - -Emitted to report a corruption of a Quorum file. - -Data: - -- "error": Error message (json-string, optional) - Only present on failure. This field contains a human-readable - error message. There are no semantics other than that the - block layer reported an error and clients should not try to - interpret the error string. -- "node-name": The graph node name of the block driver state. -- "sector-num": Number of the first sector of the failed read operation. -- "sector-count": Failed read operation sector count. - -Example: - -{ "event": "QUORUM_REPORT_BAD", - "data": { "node-name": "1.raw", "sector-num": 345435, "sector-count": 5 }, - "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } - -RESET ------ - -Emitted when the Virtual Machine is reseted. - -Data: None. - -Example: - -{ "event": "RESET", - "timestamp": { "seconds": 1267041653, "microseconds": 9518 } } - -RESUME ------- - -Emitted when the Virtual Machine resumes execution. - -Data: None. - -Example: - -{ "event": "RESUME", - "timestamp": { "seconds": 1271770767, "microseconds": 582542 } } - -RTC_CHANGE ----------- - -Emitted when the guest changes the RTC time. - -Data: - -- "offset": Offset between base RTC clock (as specified by -rtc base), and -new RTC clock value (json-number) - -Example: - -{ "event": "RTC_CHANGE", - "data": { "offset": 78 }, - "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } - -SHUTDOWN --------- - -Emitted when the Virtual Machine is powered down. - -Data: None. - -Example: - -{ "event": "SHUTDOWN", - "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } - -Note: If the command-line option "-no-shutdown" has been specified, a STOP -event will eventually follow the SHUTDOWN event. - -SPICE_CONNECTED, SPICE_DISCONNECTED ------------------------------------ - -Emitted when a SPICE client connects or disconnects. - -Data: - -- "server": Server information (json-object) - - "host": IP address (json-string) - - "port": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") -- "client": Client information (json-object) - - "host": IP address (json-string) - - "port": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - -Example: - -{ "timestamp": {"seconds": 1290688046, "microseconds": 388707}, - "event": "SPICE_CONNECTED", - "data": { - "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"}, - "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"} -}} - -SPICE_INITIALIZED ------------------ - -Emitted after initial handshake and authentication takes place (if any) -and the SPICE channel is up'n'running - -Data: - -- "server": Server information (json-object) - - "host": IP address (json-string) - - "port": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - - "auth": authentication method (json-string, optional) -- "client": Client information (json-object) - - "host": IP address (json-string) - - "port": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - - "connection-id": spice connection id. All channels with the same id - belong to the same spice session (json-int) - - "channel-type": channel type. "1" is the main control channel, filter for - this one if you want track spice sessions only (json-int) - - "channel-id": channel id. Usually "0", might be different needed when - multiple channels of the same type exist, such as multiple - display channels in a multihead setup (json-int) - - "tls": whevener the channel is encrypted (json-bool) - -Example: - -{ "timestamp": {"seconds": 1290688046, "microseconds": 417172}, - "event": "SPICE_INITIALIZED", - "data": {"server": {"auth": "spice", "port": "5921", - "family": "ipv4", "host": "127.0.0.1"}, - "client": {"port": "49004", "family": "ipv4", "channel-type": 3, - "connection-id": 1804289383, "host": "127.0.0.1", - "channel-id": 0, "tls": true} -}} - -STOP ----- - -Emitted when the Virtual Machine is stopped. - -Data: None. - -Example: - -{ "event": "STOP", - "timestamp": { "seconds": 1267041730, "microseconds": 281295 } } - -SUSPEND -------- - -Emitted when guest enters S3 state. - -Data: None. - -Example: - -{ "event": "SUSPEND", - "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } - -SUSPEND_DISK ------------- - -Emitted when the guest makes a request to enter S4 state. - -Data: None. - -Example: - -{ "event": "SUSPEND_DISK", - "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } - -Note: QEMU shuts down when entering S4 state. - -VNC_CONNECTED -------------- - -Emitted when a VNC client establishes a connection. - -Data: - -- "server": Server information (json-object) - - "host": IP address (json-string) - - "service": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - - "auth": authentication method (json-string, optional) -- "client": Client information (json-object) - - "host": IP address (json-string) - - "service": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - -Example: - -{ "event": "VNC_CONNECTED", - "data": { - "server": { "auth": "sasl", "family": "ipv4", - "service": "5901", "host": "0.0.0.0" }, - "client": { "family": "ipv4", "service": "58425", - "host": "127.0.0.1" } }, - "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } - - -Note: This event is emitted before any authentication takes place, thus -the authentication ID is not provided. - -VNC_DISCONNECTED ----------------- - -Emitted when the connection is closed. - -Data: - -- "server": Server information (json-object) - - "host": IP address (json-string) - - "service": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - - "auth": authentication method (json-string, optional) -- "client": Client information (json-object) - - "host": IP address (json-string) - - "service": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - - "x509_dname": TLS dname (json-string, optional) - - "sasl_username": SASL username (json-string, optional) - -Example: - -{ "event": "VNC_DISCONNECTED", - "data": { - "server": { "auth": "sasl", "family": "ipv4", - "service": "5901", "host": "0.0.0.0" }, - "client": { "family": "ipv4", "service": "58425", - "host": "127.0.0.1", "sasl_username": "luiz" } }, - "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } - -VNC_INITIALIZED ---------------- - -Emitted after authentication takes place (if any) and the VNC session is -made active. - -Data: - -- "server": Server information (json-object) - - "host": IP address (json-string) - - "service": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - - "auth": authentication method (json-string, optional) -- "client": Client information (json-object) - - "host": IP address (json-string) - - "service": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - - "x509_dname": TLS dname (json-string, optional) - - "sasl_username": SASL username (json-string, optional) - -Example: - -{ "event": "VNC_INITIALIZED", - "data": { - "server": { "auth": "sasl", "family": "ipv4", - "service": "5901", "host": "0.0.0.0"}, - "client": { "family": "ipv4", "service": "46089", - "host": "127.0.0.1", "sasl_username": "luiz" } }, - "timestamp": { "seconds": 1263475302, "microseconds": 150772 } } - -WAKEUP ------- - -Emitted when the guest has woken up from S3 and is running. - -Data: None. - -Example: - -{ "event": "WAKEUP", - "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } - -WATCHDOG --------- - -Emitted when the watchdog device's timer is expired. - -Data: - -- "action": Action that has been taken, it's one of the following (json-string): - "reset", "shutdown", "poweroff", "pause", "debug", or "none" - -Example: - -{ "event": "WATCHDOG", - "data": { "action": "reset" }, - "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } - -Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is -followed respectively by the RESET, SHUTDOWN, or STOP events. diff --git a/exec.c b/exec.c index 1ca7baca0b..c8494051a6 100644 --- a/exec.c +++ b/exec.c @@ -50,7 +50,6 @@ #include "exec/memory-internal.h" #include "exec/ram_addr.h" -#include "qemu/cache-utils.h" #include "qemu/range.h" diff --git a/fpu/softfloat-macros.h b/fpu/softfloat-macros.h index 9b095456c5..0dcda93f72 100644 --- a/fpu/softfloat-macros.h +++ b/fpu/softfloat-macros.h @@ -55,7 +55,7 @@ these four paragraphs for those parts of this code that are retained. | The result is stored in the location pointed to by `zPtr'. *----------------------------------------------------------------------------*/ -INLINE void shift32RightJamming(uint32_t a, int_fast16_t count, uint32_t *zPtr) +static inline void shift32RightJamming(uint32_t a, int_fast16_t count, uint32_t *zPtr) { uint32_t z; @@ -81,7 +81,7 @@ INLINE void shift32RightJamming(uint32_t a, int_fast16_t count, uint32_t *zPtr) | The result is stored in the location pointed to by `zPtr'. *----------------------------------------------------------------------------*/ -INLINE void shift64RightJamming(uint64_t a, int_fast16_t count, uint64_t *zPtr) +static inline void shift64RightJamming(uint64_t a, int_fast16_t count, uint64_t *zPtr) { uint64_t z; @@ -115,7 +115,7 @@ INLINE void shift64RightJamming(uint64_t a, int_fast16_t count, uint64_t *zPtr) | described above, and is returned at the location pointed to by `z1Ptr'.) *----------------------------------------------------------------------------*/ -INLINE void +static inline void shift64ExtraRightJamming( uint64_t a0, uint64_t a1, int_fast16_t count, uint64_t *z0Ptr, uint64_t *z1Ptr) { @@ -152,7 +152,7 @@ INLINE void | which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'. *----------------------------------------------------------------------------*/ -INLINE void +static inline void shift128Right( uint64_t a0, uint64_t a1, int_fast16_t count, uint64_t *z0Ptr, uint64_t *z1Ptr) { @@ -187,7 +187,7 @@ INLINE void | the locations pointed to by `z0Ptr' and `z1Ptr'. *----------------------------------------------------------------------------*/ -INLINE void +static inline void shift128RightJamming( uint64_t a0, uint64_t a1, int_fast16_t count, uint64_t *z0Ptr, uint64_t *z1Ptr) { @@ -238,7 +238,7 @@ INLINE void | `z2Ptr'.) *----------------------------------------------------------------------------*/ -INLINE void +static inline void shift128ExtraRightJamming( uint64_t a0, uint64_t a1, @@ -296,7 +296,7 @@ INLINE void | pieces which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'. *----------------------------------------------------------------------------*/ -INLINE void +static inline void shortShift128Left( uint64_t a0, uint64_t a1, int_fast16_t count, uint64_t *z0Ptr, uint64_t *z1Ptr) { @@ -315,7 +315,7 @@ INLINE void | `z1Ptr', and `z2Ptr'. *----------------------------------------------------------------------------*/ -INLINE void +static inline void shortShift192Left( uint64_t a0, uint64_t a1, @@ -350,7 +350,7 @@ INLINE void | are stored at the locations pointed to by `z0Ptr' and `z1Ptr'. *----------------------------------------------------------------------------*/ -INLINE void +static inline void add128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1, uint64_t *z0Ptr, uint64_t *z1Ptr ) { @@ -370,7 +370,7 @@ INLINE void | `z1Ptr', and `z2Ptr'. *----------------------------------------------------------------------------*/ -INLINE void +static inline void add192( uint64_t a0, uint64_t a1, @@ -408,7 +408,7 @@ INLINE void | `z1Ptr'. *----------------------------------------------------------------------------*/ -INLINE void +static inline void sub128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1, uint64_t *z0Ptr, uint64_t *z1Ptr ) { @@ -426,7 +426,7 @@ INLINE void | pointed to by `z0Ptr', `z1Ptr', and `z2Ptr'. *----------------------------------------------------------------------------*/ -INLINE void +static inline void sub192( uint64_t a0, uint64_t a1, @@ -462,7 +462,7 @@ INLINE void | `z0Ptr' and `z1Ptr'. *----------------------------------------------------------------------------*/ -INLINE void mul64To128( uint64_t a, uint64_t b, uint64_t *z0Ptr, uint64_t *z1Ptr ) +static inline void mul64To128( uint64_t a, uint64_t b, uint64_t *z0Ptr, uint64_t *z1Ptr ) { uint32_t aHigh, aLow, bHigh, bLow; uint64_t z0, zMiddleA, zMiddleB, z1; @@ -492,7 +492,7 @@ INLINE void mul64To128( uint64_t a, uint64_t b, uint64_t *z0Ptr, uint64_t *z1Ptr | `z2Ptr'. *----------------------------------------------------------------------------*/ -INLINE void +static inline void mul128By64To192( uint64_t a0, uint64_t a1, @@ -520,7 +520,7 @@ INLINE void | the locations pointed to by `z0Ptr', `z1Ptr', `z2Ptr', and `z3Ptr'. *----------------------------------------------------------------------------*/ -INLINE void +static inline void mul128To256( uint64_t a0, uint64_t a1, @@ -702,7 +702,7 @@ static int8 countLeadingZeros64( uint64_t a ) | Otherwise, returns 0. *----------------------------------------------------------------------------*/ -INLINE flag eq128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1 ) +static inline flag eq128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1 ) { return ( a0 == b0 ) && ( a1 == b1 ); @@ -715,7 +715,7 @@ INLINE flag eq128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1 ) | Otherwise, returns 0. *----------------------------------------------------------------------------*/ -INLINE flag le128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1 ) +static inline flag le128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1 ) { return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 <= b1 ) ); @@ -728,7 +728,7 @@ INLINE flag le128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1 ) | returns 0. *----------------------------------------------------------------------------*/ -INLINE flag lt128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1 ) +static inline flag lt128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1 ) { return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 < b1 ) ); @@ -741,7 +741,7 @@ INLINE flag lt128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1 ) | Otherwise, returns 0. *----------------------------------------------------------------------------*/ -INLINE flag ne128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1 ) +static inline flag ne128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1 ) { return ( a0 != b0 ) || ( a1 != b1 ); diff --git a/fpu/softfloat.c b/fpu/softfloat.c index e00a6fbca6..9274ebf101 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -66,7 +66,7 @@ these four paragraphs for those parts of this code that are retained. | Returns the fraction bits of the half-precision floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE uint32_t extractFloat16Frac(float16 a) +static inline uint32_t extractFloat16Frac(float16 a) { return float16_val(a) & 0x3ff; } @@ -75,7 +75,7 @@ INLINE uint32_t extractFloat16Frac(float16 a) | Returns the exponent bits of the half-precision floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE int_fast16_t extractFloat16Exp(float16 a) +static inline int_fast16_t extractFloat16Exp(float16 a) { return (float16_val(a) >> 10) & 0x1f; } @@ -84,7 +84,7 @@ INLINE int_fast16_t extractFloat16Exp(float16 a) | Returns the sign bit of the single-precision floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE flag extractFloat16Sign(float16 a) +static inline flag extractFloat16Sign(float16 a) { return float16_val(a)>>15; } @@ -255,7 +255,7 @@ static int64 roundAndPackUint64(flag zSign, uint64_t absZ0, | Returns the fraction bits of the single-precision floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE uint32_t extractFloat32Frac( float32 a ) +static inline uint32_t extractFloat32Frac( float32 a ) { return float32_val(a) & 0x007FFFFF; @@ -266,7 +266,7 @@ INLINE uint32_t extractFloat32Frac( float32 a ) | Returns the exponent bits of the single-precision floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE int_fast16_t extractFloat32Exp(float32 a) +static inline int_fast16_t extractFloat32Exp(float32 a) { return ( float32_val(a)>>23 ) & 0xFF; @@ -277,7 +277,7 @@ INLINE int_fast16_t extractFloat32Exp(float32 a) | Returns the sign bit of the single-precision floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE flag extractFloat32Sign( float32 a ) +static inline flag extractFloat32Sign( float32 a ) { return float32_val(a)>>31; @@ -328,7 +328,7 @@ static void | significand. *----------------------------------------------------------------------------*/ -INLINE float32 packFloat32(flag zSign, int_fast16_t zExp, uint32_t zSig) +static inline float32 packFloat32(flag zSign, int_fast16_t zExp, uint32_t zSig) { return make_float32( @@ -440,7 +440,7 @@ static float32 | Returns the fraction bits of the double-precision floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE uint64_t extractFloat64Frac( float64 a ) +static inline uint64_t extractFloat64Frac( float64 a ) { return float64_val(a) & LIT64( 0x000FFFFFFFFFFFFF ); @@ -451,7 +451,7 @@ INLINE uint64_t extractFloat64Frac( float64 a ) | Returns the exponent bits of the double-precision floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE int_fast16_t extractFloat64Exp(float64 a) +static inline int_fast16_t extractFloat64Exp(float64 a) { return ( float64_val(a)>>52 ) & 0x7FF; @@ -462,7 +462,7 @@ INLINE int_fast16_t extractFloat64Exp(float64 a) | Returns the sign bit of the double-precision floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE flag extractFloat64Sign( float64 a ) +static inline flag extractFloat64Sign( float64 a ) { return float64_val(a)>>63; @@ -513,7 +513,7 @@ static void | significand. *----------------------------------------------------------------------------*/ -INLINE float64 packFloat64(flag zSign, int_fast16_t zExp, uint64_t zSig) +static inline float64 packFloat64(flag zSign, int_fast16_t zExp, uint64_t zSig) { return make_float64( @@ -625,7 +625,7 @@ static float64 | value `a'. *----------------------------------------------------------------------------*/ -INLINE uint64_t extractFloatx80Frac( floatx80 a ) +static inline uint64_t extractFloatx80Frac( floatx80 a ) { return a.low; @@ -637,7 +637,7 @@ INLINE uint64_t extractFloatx80Frac( floatx80 a ) | value `a'. *----------------------------------------------------------------------------*/ -INLINE int32 extractFloatx80Exp( floatx80 a ) +static inline int32 extractFloatx80Exp( floatx80 a ) { return a.high & 0x7FFF; @@ -649,7 +649,7 @@ INLINE int32 extractFloatx80Exp( floatx80 a ) | `a'. *----------------------------------------------------------------------------*/ -INLINE flag extractFloatx80Sign( floatx80 a ) +static inline flag extractFloatx80Sign( floatx80 a ) { return a.high>>15; @@ -679,7 +679,7 @@ static void | extended double-precision floating-point value, returning the result. *----------------------------------------------------------------------------*/ -INLINE floatx80 packFloatx80( flag zSign, int32 zExp, uint64_t zSig ) +static inline floatx80 packFloatx80( flag zSign, int32 zExp, uint64_t zSig ) { floatx80 z; @@ -921,7 +921,7 @@ static floatx80 | floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE uint64_t extractFloat128Frac1( float128 a ) +static inline uint64_t extractFloat128Frac1( float128 a ) { return a.low; @@ -933,7 +933,7 @@ INLINE uint64_t extractFloat128Frac1( float128 a ) | floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE uint64_t extractFloat128Frac0( float128 a ) +static inline uint64_t extractFloat128Frac0( float128 a ) { return a.high & LIT64( 0x0000FFFFFFFFFFFF ); @@ -945,7 +945,7 @@ INLINE uint64_t extractFloat128Frac0( float128 a ) | `a'. *----------------------------------------------------------------------------*/ -INLINE int32 extractFloat128Exp( float128 a ) +static inline int32 extractFloat128Exp( float128 a ) { return ( a.high>>48 ) & 0x7FFF; @@ -956,7 +956,7 @@ INLINE int32 extractFloat128Exp( float128 a ) | Returns the sign bit of the quadruple-precision floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE flag extractFloat128Sign( float128 a ) +static inline flag extractFloat128Sign( float128 a ) { return a.high>>63; @@ -1017,7 +1017,7 @@ static void | significand. *----------------------------------------------------------------------------*/ -INLINE float128 +static inline float128 packFloat128( flag zSign, int32 zExp, uint64_t zSig0, uint64_t zSig1 ) { float128 z; @@ -7088,7 +7088,7 @@ uint64_t float64_to_uint64_round_to_zero (float64 a STATUS_PARAM) } #define COMPARE(s, nan_exp) \ -INLINE int float ## s ## _compare_internal( float ## s a, float ## s b, \ +static inline int float ## s ## _compare_internal( float ## s a, float ## s b, \ int is_quiet STATUS_PARAM ) \ { \ flag aSign, bSign; \ @@ -7140,7 +7140,7 @@ int float ## s ## _compare_quiet( float ## s a, float ## s b STATUS_PARAM ) \ COMPARE(32, 0xff) COMPARE(64, 0x7ff) -INLINE int floatx80_compare_internal( floatx80 a, floatx80 b, +static inline int floatx80_compare_internal( floatx80 a, floatx80 b, int is_quiet STATUS_PARAM ) { flag aSign, bSign; @@ -7186,7 +7186,7 @@ int floatx80_compare_quiet( floatx80 a, floatx80 b STATUS_PARAM ) return floatx80_compare_internal(a, b, 1 STATUS_VAR); } -INLINE int float128_compare_internal( float128 a, float128 b, +static inline int float128_compare_internal( float128 a, float128 b, int is_quiet STATUS_PARAM ) { flag aSign, bSign; @@ -7242,7 +7242,7 @@ int float128_compare_quiet( float128 a, float128 b STATUS_PARAM ) * semantics provided by many CPUs which predate that specification. */ #define MINMAX(s) \ -INLINE float ## s float ## s ## _minmax(float ## s a, float ## s b, \ +static inline float ## s float ## s ## _minmax(float ## s a, float ## s b, \ int ismin, int isieee STATUS_PARAM) \ { \ flag aSign, bSign; \ diff --git a/hmp.c b/hmp.c index 41006f5eef..dc3d2795d2 100644 --- a/hmp.c +++ b/hmp.c @@ -465,7 +465,8 @@ void hmp_info_vnc(Monitor *mon, const QDict *qdict) for (client = info->clients; client; client = client->next) { monitor_printf(mon, "Client:\n"); monitor_printf(mon, " address: %s:%s\n", - client->value->host, client->value->service); + client->value->base->host, + client->value->base->service); monitor_printf(mon, " x509_dname: %s\n", client->value->x509_dname ? client->value->x509_dname : "none"); @@ -513,7 +514,7 @@ void hmp_info_spice(Monitor *mon, const QDict *qdict) for (chan = info->channels; chan; chan = chan->next) { monitor_printf(mon, "Channel:\n"); monitor_printf(mon, " address: %s:%s%s\n", - chan->value->host, chan->value->port, + chan->value->base->host, chan->value->base->port, chan->value->tls ? " [tls]" : ""); monitor_printf(mon, " session: %" PRId64 "\n", chan->value->connection_id); @@ -1692,7 +1693,7 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict) ov = string_output_visitor_new(false); visit_type_uint16List(string_output_get_visitor(ov), &m->value->host_nodes, NULL, NULL); - monitor_printf(mon, "memory device %d\n", i); + monitor_printf(mon, "memory backend: %d\n", i); monitor_printf(mon, " size: %" PRId64 "\n", m->value->size); monitor_printf(mon, " merge: %s\n", m->value->merge ? "true" : "false"); diff --git a/hw/acpi/core.c b/hw/acpi/core.c index 79414b44c7..a7368fb242 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -22,11 +22,11 @@ #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/acpi/acpi.h" -#include "monitor/monitor.h" #include "qemu/config-file.h" #include "qapi/opts-visitor.h" #include "qapi/dealloc-visitor.h" #include "qapi-visit.h" +#include "qapi-event.h" struct acpi_table_header { uint16_t _length; /* our length, not actual part of the hdr */ @@ -550,7 +550,7 @@ static void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val) break; default: if (sus_typ == ar->pm1.cnt.s4_val) { /* S4 request */ - monitor_protocol_event(QEVENT_SUSPEND_DISK, NULL); + qapi_event_send_suspend_disk(&error_abort); qemu_system_shutdown_request(); } break; diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c index de4ddc204f..38ca415f64 100644 --- a/hw/acpi/memory_hotplug.c +++ b/hw/acpi/memory_hotplug.c @@ -3,10 +3,7 @@ #include "hw/mem/pc-dimm.h" #include "hw/boards.h" #include "trace.h" -#include "qapi-visit.h" -#include "monitor/monitor.h" -#include "qapi/dealloc-visitor.h" -#include "qapi/qmp-output-visitor.h" +#include "qapi-event.h" static ACPIOSTInfo *acpi_memory_device_status(int slot, MemStatus *mdev) { @@ -39,29 +36,6 @@ void acpi_memory_ospm_status(MemHotplugState *mem_st, ACPIOSTInfoList ***list) } } -static void acpi_memory_ost_mon_event(const MemHotplugState *mem_st) -{ - Visitor *v; - QObject *out_info; - QapiDeallocVisitor *md; - QmpOutputVisitor *mo = qmp_output_visitor_new(); - MemStatus *mdev = &mem_st->devs[mem_st->selector]; - ACPIOSTInfo *info = acpi_memory_device_status(mem_st->selector, mdev); - - v = qmp_output_get_visitor(mo); - visit_type_ACPIOSTInfo(v, &info, "unused", NULL); - - out_info = qmp_output_get_qobject(mo); - monitor_protocol_event(QEVENT_ACPI_OST, out_info); - qobject_decref(out_info); - - qmp_output_visitor_cleanup(mo); - md = qapi_dealloc_visitor_new(); - v = qapi_dealloc_get_visitor(md); - visit_type_ACPIOSTInfo(v, &info, "unused", NULL); - qapi_dealloc_visitor_cleanup(md); -} - static uint64_t acpi_memory_hotplug_read(void *opaque, hwaddr addr, unsigned int size) { @@ -115,6 +89,7 @@ static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data, { MemHotplugState *mem_st = opaque; MemStatus *mdev; + ACPIOSTInfo *info; if (!mem_st->dev_count) { return; @@ -147,7 +122,10 @@ static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data, mdev->ost_status = data; trace_mhp_acpi_write_ost_status(mem_st->selector, mdev->ost_status); /* TODO: implement memory removal on guest signal */ - acpi_memory_ost_mon_event(mem_st); + + info = acpi_memory_device_status(mem_st->selector, mdev); + qapi_event_send_acpi_device_ost(info, &error_abort); + qapi_free_ACPIOSTInfo(info); break; case 0x14: mdev = &mem_st->devs[mem_st->selector]; diff --git a/hw/audio/fmopl.c b/hw/audio/fmopl.c index 290a224edc..adcef2d3b9 100644 --- a/hw/audio/fmopl.c +++ b/hw/audio/fmopl.c @@ -30,7 +30,6 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#define INLINE static inline #define HAS_YM3812 1 #include <stdio.h> @@ -247,7 +246,7 @@ static INT32 feedback2; /* connect for SLOT 2 */ /* --------------------- subroutines --------------------- */ -INLINE int Limit( int val, int max, int min ) { +static inline int Limit( int val, int max, int min ) { if ( val > max ) val = max; else if ( val < min ) @@ -257,7 +256,7 @@ INLINE int Limit( int val, int max, int min ) { } /* status set and IRQ handling */ -INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag) +static inline void OPL_STATUS_SET(FM_OPL *OPL,int flag) { /* set status flag */ OPL->status |= flag; @@ -273,7 +272,7 @@ INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag) } /* status reset and IRQ handling */ -INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag) +static inline void OPL_STATUS_RESET(FM_OPL *OPL,int flag) { /* reset status flag */ OPL->status &=~flag; @@ -289,7 +288,7 @@ INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag) } /* IRQ mask set */ -INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag) +static inline void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag) { OPL->statusmask = flag; /* IRQ handling check */ @@ -298,7 +297,7 @@ INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag) } /* ----- key on ----- */ -INLINE void OPL_KEYON(OPL_SLOT *SLOT) +static inline void OPL_KEYON(OPL_SLOT *SLOT) { /* sin wave restart */ SLOT->Cnt = 0; @@ -309,7 +308,7 @@ INLINE void OPL_KEYON(OPL_SLOT *SLOT) SLOT->eve = EG_AED; } /* ----- key off ----- */ -INLINE void OPL_KEYOFF(OPL_SLOT *SLOT) +static inline void OPL_KEYOFF(OPL_SLOT *SLOT) { if( SLOT->evm > ENV_MOD_RR) { @@ -325,7 +324,7 @@ INLINE void OPL_KEYOFF(OPL_SLOT *SLOT) /* ---------- calcrate Envelope Generator & Phase Generator ---------- */ /* return : envelope output */ -INLINE UINT32 OPL_CALC_SLOT( OPL_SLOT *SLOT ) +static inline UINT32 OPL_CALC_SLOT( OPL_SLOT *SLOT ) { /* calcrate envelope generator */ if( (SLOT->evc+=SLOT->evs) >= SLOT->eve ) @@ -371,7 +370,7 @@ static void set_algorithm( OPL_CH *CH) } /* ---------- frequency counter for operater update ---------- */ -INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT) +static inline void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT) { int ksr; @@ -391,7 +390,7 @@ INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT) } /* set multi,am,vib,EG-TYP,KSR,mul */ -INLINE void set_mul(FM_OPL *OPL,int slot,int v) +static inline void set_mul(FM_OPL *OPL,int slot,int v) { OPL_CH *CH = &OPL->P_CH[slot/2]; OPL_SLOT *SLOT = &CH->SLOT[slot&1]; @@ -405,7 +404,7 @@ INLINE void set_mul(FM_OPL *OPL,int slot,int v) } /* set ksl & tl */ -INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v) +static inline void set_ksl_tl(FM_OPL *OPL,int slot,int v) { OPL_CH *CH = &OPL->P_CH[slot/2]; OPL_SLOT *SLOT = &CH->SLOT[slot&1]; @@ -421,7 +420,7 @@ INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v) } /* set attack rate & decay rate */ -INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v) +static inline void set_ar_dr(FM_OPL *OPL,int slot,int v) { OPL_CH *CH = &OPL->P_CH[slot/2]; OPL_SLOT *SLOT = &CH->SLOT[slot&1]; @@ -438,7 +437,7 @@ INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v) } /* set sustain level & release rate */ -INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v) +static inline void set_sl_rr(FM_OPL *OPL,int slot,int v) { OPL_CH *CH = &OPL->P_CH[slot/2]; OPL_SLOT *SLOT = &CH->SLOT[slot&1]; @@ -455,7 +454,7 @@ INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v) /* operator output calcrator */ #define OP_OUT(slot,env,con) slot->wavetable[((slot->Cnt+con)/(0x1000000/SIN_ENT))&(SIN_ENT-1)][env] /* ---------- calcrate one of channel ---------- */ -INLINE void OPL_CALC_CH( OPL_CH *CH ) +static inline void OPL_CALC_CH( OPL_CH *CH ) { UINT32 env_out; OPL_SLOT *SLOT; @@ -500,7 +499,7 @@ INLINE void OPL_CALC_CH( OPL_CH *CH ) /* ---------- calcrate rhythm block ---------- */ #define WHITE_NOISE_db 6.0 -INLINE void OPL_CALC_RH( OPL_CH *CH ) +static inline void OPL_CALC_RH( OPL_CH *CH ) { UINT32 env_tam,env_sd,env_top,env_hh; int whitenoise = (rand()&1)*(WHITE_NOISE_db/EG_STEP); @@ -716,7 +715,7 @@ static void OPLCloseTable( void ) } /* CSM Key Control */ -INLINE void CSMKeyControll(OPL_CH *CH) +static inline void CSMKeyControll(OPL_CH *CH) { OPL_SLOT *slot1 = &CH->SLOT[SLOT1]; OPL_SLOT *slot2 = &CH->SLOT[SLOT2]; diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 85aa8715ba..08562ea390 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -56,17 +56,17 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, BlockErrorAction action = bdrv_get_error_action(req->dev->bs, is_read, error); VirtIOBlock *s = req->dev; - if (action == BDRV_ACTION_STOP) { + if (action == BLOCK_ERROR_ACTION_STOP) { req->next = s->rq; s->rq = req; - } else if (action == BDRV_ACTION_REPORT) { + } else if (action == BLOCK_ERROR_ACTION_REPORT) { virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); bdrv_acct_done(s->bs, &req->acct); g_free(req); } bdrv_error_action(s->bs, action, is_read, error); - return action != BDRV_ACTION_IGNORE; + return action != BLOCK_ERROR_ACTION_IGNORE; } static void virtio_blk_rw_complete(void *opaque, int ret) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index b9cd4fc814..d1eba3cc3d 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -32,9 +32,9 @@ #include "qapi/qmp/qerror.h" #include "qapi/visitor.h" #include "qapi/qmp/qjson.h" -#include "monitor/monitor.h" #include "hw/hotplug.h" #include "hw/boards.h" +#include "qapi-event.h" int qdev_hotplug = 0; static bool qdev_hot_added = false; @@ -972,7 +972,6 @@ static void device_unparent(Object *obj) { DeviceState *dev = DEVICE(obj); BusState *bus; - QObject *event_data; bool have_realized = dev->realized; if (dev->realized) { @@ -992,14 +991,7 @@ static void device_unparent(Object *obj) if (have_realized) { gchar *path = object_get_canonical_path(OBJECT(dev)); - if (dev->id) { - event_data = qobject_from_jsonf("{ 'device': %s, 'path': %s }", - dev->id, path); - } else { - event_data = qobject_from_jsonf("{ 'path': %s }", path); - } - monitor_protocol_event(QEVENT_DEVICE_DELETED, event_data); - qobject_decref(event_data); + qapi_event_send_device_deleted(!!dev->id, dev->id, path, &error_abort); g_free(path); } } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 67eb45089e..2cf22b1293 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1643,11 +1643,58 @@ pc_machine_get_hotplug_memory_region_size(Object *obj, Visitor *v, void *opaque, visit_type_int(v, &value, name, errp); } +static void pc_machine_get_max_ram_below_4g(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + uint64_t value = pcms->max_ram_below_4g; + + visit_type_size(v, &value, name, errp); +} + +static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + Error *error = NULL; + uint64_t value; + + visit_type_size(v, &value, name, &error); + if (error) { + error_propagate(errp, error); + return; + } + if (value > (1ULL << 32)) { + error_set(&error, ERROR_CLASS_GENERIC_ERROR, + "Machine option 'max-ram-below-4g=%"PRIu64 + "' expects size less than or equal to 4G", value); + error_propagate(errp, error); + return; + } + + if (value < (1ULL << 20)) { + error_report("Warning: small max_ram_below_4g(%"PRIu64 + ") less than 1M. BIOS may not work..", + value); + } + + pcms->max_ram_below_4g = value; +} + static void pc_machine_initfn(Object *obj) { + PCMachineState *pcms = PC_MACHINE(obj); + object_property_add(obj, PC_MACHINE_MEMHP_REGION_SIZE, "int", pc_machine_get_hotplug_memory_region_size, NULL, NULL, NULL, NULL); + pcms->max_ram_below_4g = 1ULL << 32; /* 4G */ + object_property_add(obj, PC_MACHINE_MAX_RAM_BELOW_4G, "size", + pc_machine_get_max_ram_below_4g, + pc_machine_set_max_ram_below_4g, + NULL, NULL, NULL); } static void pc_machine_class_init(ObjectClass *oc, void *data) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 3e7524b961..47546b72ae 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -48,6 +48,7 @@ #include "exec/address-spaces.h" #include "hw/acpi/acpi.h" #include "cpu.h" +#include "qemu/error-report.h" #ifdef CONFIG_XEN # include <xen/hvm/hvm_info_table.h> #endif @@ -98,21 +99,7 @@ static void pc_init1(MachineState *machine, DeviceState *icc_bridge; FWCfgState *fw_cfg = NULL; PcGuestInfo *guest_info; - - if (xen_enabled() && xen_hvm_init(&ram_memory) != 0) { - fprintf(stderr, "xen hardware virtual machine initialisation failed\n"); - exit(1); - } - - icc_bridge = qdev_create(NULL, TYPE_ICC_BRIDGE); - object_property_add_child(qdev_get_machine(), "icc-bridge", - OBJECT(icc_bridge), NULL); - - pc_cpus_init(machine->cpu_model, icc_bridge); - - if (kvm_enabled() && kvmclock_enabled) { - kvmclock_create(); - } + ram_addr_t lowmem; /* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory). * If it doesn't, we need to split it in chunks below and above 4G. @@ -122,7 +109,25 @@ static void pc_init1(MachineState *machine, * breaking migration. */ if (machine->ram_size >= 0xe0000000) { - ram_addr_t lowmem = gigabyte_align ? 0xc0000000 : 0xe0000000; + lowmem = gigabyte_align ? 0xc0000000 : 0xe0000000; + } else { + lowmem = 0xe0000000; + } + + /* Handle the machine opt max-ram-below-4g. It is basicly doing + * min(qemu limit, user limit). + */ + if (lowmem > pc_machine->max_ram_below_4g) { + lowmem = pc_machine->max_ram_below_4g; + if (machine->ram_size - lowmem > lowmem && + lowmem & ((1ULL << 30) - 1)) { + error_report("Warning: Large machine and max_ram_below_4g(%"PRIu64 + ") not a multiple of 1G; possible bad performance.", + pc_machine->max_ram_below_4g); + } + } + + if (machine->ram_size >= lowmem) { above_4g_mem_size = machine->ram_size - lowmem; below_4g_mem_size = lowmem; } else { @@ -130,6 +135,22 @@ static void pc_init1(MachineState *machine, below_4g_mem_size = machine->ram_size; } + if (xen_enabled() && xen_hvm_init(&below_4g_mem_size, &above_4g_mem_size, + &ram_memory) != 0) { + fprintf(stderr, "xen hardware virtual machine initialisation failed\n"); + exit(1); + } + + icc_bridge = qdev_create(NULL, TYPE_ICC_BRIDGE); + object_property_add_child(qdev_get_machine(), "icc-bridge", + OBJECT(icc_bridge), NULL); + + pc_cpus_init(machine->cpu_model, icc_bridge); + + if (kvm_enabled() && kvmclock_enabled) { + kvmclock_create(); + } + if (pci_enabled) { pci_memory = g_new(MemoryRegion, 1); memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index aa71332ee1..155db99f63 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -44,6 +44,7 @@ #include "hw/ide/ahci.h" #include "hw/usb.h" #include "hw/cpu/icc_bus.h" +#include "qemu/error-report.h" /* ICH9 AHCI has 6 ports */ #define MAX_SATA_PORTS 6 @@ -85,20 +86,7 @@ static void pc_q35_init(MachineState *machine) PCIDevice *ahci; DeviceState *icc_bridge; PcGuestInfo *guest_info; - - if (xen_enabled() && xen_hvm_init(&ram_memory) != 0) { - fprintf(stderr, "xen hardware virtual machine initialisation failed\n"); - exit(1); - } - - icc_bridge = qdev_create(NULL, TYPE_ICC_BRIDGE); - object_property_add_child(qdev_get_machine(), "icc-bridge", - OBJECT(icc_bridge), NULL); - - pc_cpus_init(machine->cpu_model, icc_bridge); - pc_acpi_init("q35-acpi-dsdt.aml"); - - kvmclock_create(); + ram_addr_t lowmem; /* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory * and 256 Mbytes for PCI Express Enhanced Configuration Access Mapping @@ -110,7 +98,25 @@ static void pc_q35_init(MachineState *machine) * breaking migration. */ if (machine->ram_size >= 0xb0000000) { - ram_addr_t lowmem = gigabyte_align ? 0x80000000 : 0xb0000000; + lowmem = gigabyte_align ? 0x80000000 : 0xb0000000; + } else { + lowmem = 0xb0000000; + } + + /* Handle the machine opt max-ram-below-4g. It is basicly doing + * min(qemu limit, user limit). + */ + if (lowmem > pc_machine->max_ram_below_4g) { + lowmem = pc_machine->max_ram_below_4g; + if (machine->ram_size - lowmem > lowmem && + lowmem & ((1ULL << 30) - 1)) { + error_report("Warning: Large machine and max_ram_below_4g(%"PRIu64 + ") not a multiple of 1G; possible bad performance.", + pc_machine->max_ram_below_4g); + } + } + + if (machine->ram_size >= lowmem) { above_4g_mem_size = machine->ram_size - lowmem; below_4g_mem_size = lowmem; } else { @@ -118,6 +124,21 @@ static void pc_q35_init(MachineState *machine) below_4g_mem_size = machine->ram_size; } + if (xen_enabled() && xen_hvm_init(&below_4g_mem_size, &above_4g_mem_size, + &ram_memory) != 0) { + fprintf(stderr, "xen hardware virtual machine initialisation failed\n"); + exit(1); + } + + icc_bridge = qdev_create(NULL, TYPE_ICC_BRIDGE); + object_property_add_child(qdev_get_machine(), "icc-bridge", + OBJECT(icc_bridge), NULL); + + pc_cpus_init(machine->cpu_model, icc_bridge); + pc_acpi_init("q35-acpi-dsdt.aml"); + + kvmclock_create(); + /* pci enabled */ if (pci_enabled) { pci_memory = g_new(MemoryRegion, 1); @@ -388,7 +409,7 @@ static QEMUMachine pc_q35_machine_v1_4 = { .name = "pc-q35-1.4", .init = pc_q35_init_1_4, .compat_props = (GlobalProperty[]) { - PC_COMPAT_1_4, + PC_Q35_COMPAT_1_4, { /* end of list */ } }, }; diff --git a/hw/ide/core.c b/hw/ide/core.c index 1cac5f53dc..3a38f1e599 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -596,10 +596,10 @@ static int ide_handle_rw_error(IDEState *s, int error, int op) bool is_read = (op & BM_STATUS_RETRY_READ) != 0; BlockErrorAction action = bdrv_get_error_action(s->bs, is_read, error); - if (action == BDRV_ACTION_STOP) { + if (action == BLOCK_ERROR_ACTION_STOP) { s->bus->dma->ops->set_unit(s->bus->dma, s->unit); s->bus->error_status = op; - } else if (action == BDRV_ACTION_REPORT) { + } else if (action == BLOCK_ERROR_ACTION_REPORT) { if (op & BM_STATUS_DMA_RETRY) { dma_buf_commit(s); ide_dma_error(s); @@ -608,7 +608,7 @@ static int ide_handle_rw_error(IDEState *s, int error, int op) } } bdrv_error_action(s->bs, action, is_read, error); - return action != BDRV_ACTION_IGNORE; + return action != BLOCK_ERROR_ACTION_IGNORE; } void ide_dma_cb(void *opaque, int ret) diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c index 5377feed69..994f8af8e6 100644 --- a/hw/misc/pvpanic.c +++ b/hw/misc/pvpanic.c @@ -14,12 +14,12 @@ #include "qapi/qmp/qobject.h" #include "qapi/qmp/qjson.h" -#include "monitor/monitor.h" #include "sysemu/sysemu.h" #include "qemu/log.h" #include "hw/nvram/fw_cfg.h" #include "hw/i386/pc.h" +#include "qapi-event.h" /* The bit of supported pv event */ #define PVPANIC_F_PANICKED 0 @@ -31,15 +31,6 @@ #define ISA_PVPANIC_DEVICE(obj) \ OBJECT_CHECK(PVPanicState, (obj), TYPE_ISA_PVPANIC_DEVICE) -static void panicked_mon_event(const char *action) -{ - QObject *data; - - data = qobject_from_jsonf("{ 'action': %s }", action); - monitor_protocol_event(QEVENT_GUEST_PANICKED, data); - qobject_decref(data); -} - static void handle_event(int event) { static bool logged; @@ -50,7 +41,7 @@ static void handle_event(int event) } if (event & PVPANIC_PANICKED) { - panicked_mon_event("pause"); + qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, &error_abort); vm_stop(RUN_STATE_GUEST_PANICKED); return; } diff --git a/hw/net/e1000.c b/hw/net/e1000.c index 57bdffde08..0fc29a0ae3 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -175,6 +175,8 @@ e1000_link_down(E1000State *s) { s->mac_reg[STATUS] &= ~E1000_STATUS_LU; s->phy_reg[PHY_STATUS] &= ~MII_SR_LINK_STATUS; + s->phy_reg[PHY_STATUS] &= ~MII_SR_AUTONEG_COMPLETE; + s->phy_reg[PHY_LP_ABILITY] &= ~MII_LPAR_LPACK; } static void @@ -197,23 +199,11 @@ set_phy_ctrl(E1000State *s, int index, uint16_t val) } if ((val & MII_CR_AUTO_NEG_EN) && (val & MII_CR_RESTART_AUTO_NEG)) { e1000_link_down(s); - s->phy_reg[PHY_STATUS] &= ~MII_SR_AUTONEG_COMPLETE; DBGOUT(PHY, "Start link auto negotiation\n"); timer_mod(s->autoneg_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500); } } -static void -e1000_autoneg_timer(void *opaque) -{ - E1000State *s = opaque; - if (!qemu_get_queue(s->nic)->link_down) { - e1000_link_up(s); - } - s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; - DBGOUT(PHY, "Auto negotiation is completed\n"); -} - static void (*phyreg_writeops[])(E1000State *, int, uint16_t) = { [PHY_CTRL] = set_phy_ctrl, }; @@ -227,7 +217,8 @@ static const char phy_regcap[0x20] = { [PHY_CTRL] = PHY_RW, [PHY_1000T_CTRL] = PHY_RW, [PHY_LP_ABILITY] = PHY_R, [PHY_1000T_STATUS] = PHY_R, [PHY_AUTONEG_ADV] = PHY_RW, [M88E1000_RX_ERR_CNTR] = PHY_R, - [PHY_ID2] = PHY_R, [M88E1000_PHY_SPEC_STATUS] = PHY_R + [PHY_ID2] = PHY_R, [M88E1000_PHY_SPEC_STATUS] = PHY_R, + [PHY_AUTONEG_EXP] = PHY_R, }; /* PHY_ID2 documented in 8254x_GBe_SDM.pdf, pp. 250 */ @@ -344,6 +335,19 @@ set_ics(E1000State *s, int index, uint32_t val) set_interrupt_cause(s, 0, val | s->mac_reg[ICR]); } +static void +e1000_autoneg_timer(void *opaque) +{ + E1000State *s = opaque; + if (!qemu_get_queue(s->nic)->link_down) { + e1000_link_up(s); + s->phy_reg[PHY_LP_ABILITY] |= MII_LPAR_LPACK; + s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; + DBGOUT(PHY, "Auto negotiation is completed\n"); + set_ics(s, 0, E1000_ICS_LSC); /* signal link status change to guest */ + } +} + static int rxbufsize(uint32_t v) { @@ -844,6 +848,14 @@ receive_filter(E1000State *s, const uint8_t *buf, int size) return 0; } +static bool +have_autoneg(E1000State *s) +{ + return (s->compat_flags & E1000_FLAG_AUTONEG) && + (s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN) && + (s->phy_reg[PHY_CTRL] & MII_CR_RESTART_AUTO_NEG); +} + static void e1000_set_link_status(NetClientState *nc) { @@ -853,7 +865,14 @@ e1000_set_link_status(NetClientState *nc) if (nc->link_down) { e1000_link_down(s); } else { - e1000_link_up(s); + if (have_autoneg(s) && + !(s->phy_reg[PHY_STATUS] & MII_SR_AUTONEG_COMPLETE)) { + /* emulate auto-negotiation if supported */ + timer_mod(s->autoneg_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500); + } else { + e1000_link_up(s); + } } if (s->mac_reg[STATUS] != old_status) @@ -1279,19 +1298,13 @@ static void e1000_pre_save(void *opaque) e1000_mit_timer(s); } - if (!(s->compat_flags & E1000_FLAG_AUTONEG)) { - return; - } - /* - * If link is down and auto-negotiation is ongoing, complete - * auto-negotiation immediately. This allows is to look at - * MII_SR_AUTONEG_COMPLETE to infer link status on load. + * If link is down and auto-negotiation is supported and ongoing, + * complete auto-negotiation immediately. This allows us to look + * at MII_SR_AUTONEG_COMPLETE to infer link status on load. */ - if (nc->link_down && - s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN && - s->phy_reg[PHY_CTRL] & MII_CR_RESTART_AUTO_NEG) { - s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; + if (nc->link_down && have_autoneg(s)) { + s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; } } @@ -1313,15 +1326,11 @@ static int e1000_post_load(void *opaque, int version_id) * Alternatively, restart link negotiation if it was in progress. */ nc->link_down = (s->mac_reg[STATUS] & E1000_STATUS_LU) == 0; - if (!(s->compat_flags & E1000_FLAG_AUTONEG)) { - return 0; - } - - if (s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN && - s->phy_reg[PHY_CTRL] & MII_CR_RESTART_AUTO_NEG && + if (have_autoneg(s) && !(s->phy_reg[PHY_STATUS] & MII_SR_AUTONEG_COMPLETE)) { nc->link_down = false; - timer_mod(s->autoneg_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500); + timer_mod(s->autoneg_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500); } return 0; diff --git a/hw/net/e1000_regs.h b/hw/net/e1000_regs.h index 13ac6713d4..60b96aaf13 100644 --- a/hw/net/e1000_regs.h +++ b/hw/net/e1000_regs.h @@ -384,6 +384,9 @@ #define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ #define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ +/* PHY Link Partner Ability Register */ +#define MII_LPAR_LPACK 0x4000 /* Acked by link partner */ + /* Interrupt Cause Read */ #define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */ #define E1000_ICR_TXQE 0x00000002 /* Transmit Queue empty */ diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index d8588f3808..00b5e07ddd 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -22,7 +22,7 @@ #include "net/vhost_net.h" #include "hw/virtio/virtio-bus.h" #include "qapi/qmp/qjson.h" -#include "monitor/monitor.h" +#include "qapi-event.h" #define VIRTIO_NET_VM_VERSION 11 @@ -199,19 +199,12 @@ static void virtio_net_set_link_status(NetClientState *nc) static void rxfilter_notify(NetClientState *nc) { - QObject *event_data; VirtIONet *n = qemu_get_nic_opaque(nc); if (nc->rxfilter_notify_enabled) { gchar *path = object_get_canonical_path(OBJECT(n->qdev)); - if (n->netclient_name) { - event_data = qobject_from_jsonf("{ 'name': %s, 'path': %s }", - n->netclient_name, path); - } else { - event_data = qobject_from_jsonf("{ 'path': %s }", path); - } - monitor_protocol_event(QEVENT_NIC_RX_FILTER_CHANGED, event_data); - qobject_decref(event_data); + qapi_event_send_nic_rx_filter_changed(!!n->netclient_name, + n->netclient_name, path, &error_abort); g_free(path); /* disable event notification to avoid events flooding */ diff --git a/hw/pci-bridge/ioh3420.c b/hw/pci-bridge/ioh3420.c index f4e17ac41a..7cd87fcbb4 100644 --- a/hw/pci-bridge/ioh3420.c +++ b/hw/pci-bridge/ioh3420.c @@ -180,6 +180,12 @@ PCIESlot *ioh3420_init(PCIBus *bus, int devfn, bool multifunction, return PCIE_SLOT(d); } +static Property ioh3420_props[] = { + DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present, + QEMU_PCIE_SLTCAP_PCP_BITNR, true), + DEFINE_PROP_END_OF_LIST() +}; + static const VMStateDescription vmstate_ioh3420 = { .name = "ioh-3240-express-root-port", .version_id = 1, @@ -210,6 +216,7 @@ static void ioh3420_class_init(ObjectClass *klass, void *data) dc->desc = "Intel IOH device id 3420 PCIE Root Port"; dc->reset = ioh3420_reset; dc->vmsd = &vmstate_ioh3420; + dc->props = ioh3420_props; } static const TypeInfo ioh3420_info = { diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c index 8f22f93f8e..51f20d7467 100644 --- a/hw/pci-bridge/xio3130_downstream.c +++ b/hw/pci-bridge/xio3130_downstream.c @@ -147,6 +147,12 @@ PCIESlot *xio3130_downstream_init(PCIBus *bus, int devfn, bool multifunction, return PCIE_SLOT(d); } +static Property xio3130_downstream_props[] = { + DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present, + QEMU_PCIE_SLTCAP_PCP_BITNR, true), + DEFINE_PROP_END_OF_LIST() +}; + static const VMStateDescription vmstate_xio3130_downstream = { .name = "xio3130-express-downstream-port", .version_id = 1, @@ -177,6 +183,7 @@ static void xio3130_downstream_class_init(ObjectClass *klass, void *data) dc->desc = "TI X3130 Downstream Port of PCI Express Switch"; dc->reset = xio3130_downstream_reset; dc->vmsd = &vmstate_xio3130_downstream; + dc->props = xio3130_downstream_props; } static const TypeInfo xio3130_downstream_info = { diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 02cde6f96c..a123c01ef1 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -224,7 +224,7 @@ static void pcie_cap_slot_hotplug_common(PCIDevice *hotplug_dev, *exp_cap = hotplug_dev->config + hotplug_dev->exp.exp_cap; uint16_t sltsta = pci_get_word(*exp_cap + PCI_EXP_SLTSTA); - PCIE_DEV_PRINTF(PCI_DEVICE(dev), "hotplug state: %d\n", state); + PCIE_DEV_PRINTF(PCI_DEVICE(dev), "hotplug state: 0x%x\n", sltsta); if (sltsta & PCI_EXP_SLTSTA_EIS) { /* the slot is electromechanically locked. * This error is propagated up to qdev and then to HMP/QMP. @@ -258,7 +258,8 @@ void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDS); - pcie_cap_slot_event(PCI_DEVICE(hotplug_dev), PCI_EXP_HP_EV_PDC); + pcie_cap_slot_event(PCI_DEVICE(hotplug_dev), + PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP); } void pcie_cap_slot_hot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, @@ -268,10 +269,7 @@ void pcie_cap_slot_hot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, pcie_cap_slot_hotplug_common(PCI_DEVICE(hotplug_dev), dev, &exp_cap, errp); - object_unparent(OBJECT(dev)); - pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_PDS); - pcie_cap_slot_event(PCI_DEVICE(hotplug_dev), PCI_EXP_HP_EV_PDC); + pcie_cap_slot_push_attention_button(PCI_DEVICE(hotplug_dev)); } /* pci express slot for pci express root/downstream port @@ -294,6 +292,15 @@ void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot) PCI_EXP_SLTCAP_AIP | PCI_EXP_SLTCAP_ABP); + if (dev->cap_present & QEMU_PCIE_SLTCAP_PCP) { + pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCAP, + PCI_EXP_SLTCAP_PCP); + pci_word_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PCC); + pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PCC); + } + pci_word_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PIC | PCI_EXP_SLTCTL_AIC); @@ -327,6 +334,10 @@ void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot) void pcie_cap_slot_reset(PCIDevice *dev) { uint8_t *exp_cap = dev->config + dev->exp.exp_cap; + uint8_t port_type = pcie_cap_get_type(dev); + + assert(port_type == PCI_EXP_TYPE_DOWNSTREAM || + port_type == PCI_EXP_TYPE_ROOT_PORT); PCIE_DEV_PRINTF(dev, "reset\n"); @@ -339,9 +350,25 @@ void pcie_cap_slot_reset(PCIDevice *dev) PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE); pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_PIC_OFF | PCI_EXP_SLTCTL_AIC_OFF); + if (dev->cap_present & QEMU_PCIE_SLTCAP_PCP) { + /* Downstream ports enforce device number 0. */ + bool populated = pci_bridge_get_sec_bus(PCI_BRIDGE(dev))->devices[0]; + uint16_t pic; + + if (populated) { + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PCC); + } else { + pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PCC); + } + + pic = populated ? PCI_EXP_SLTCTL_PIC_ON : PCI_EXP_SLTCTL_PIC_OFF; + pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTCTL, pic); + } + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_EIS |/* on reset, the lock is released */ @@ -352,6 +379,11 @@ void pcie_cap_slot_reset(PCIDevice *dev) hotplug_event_update_event_status(dev); } +static void pcie_unplug_device(PCIBus *bus, PCIDevice *dev, void *opaque) +{ + object_unparent(OBJECT(dev)); +} + void pcie_cap_slot_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len) { @@ -376,6 +408,22 @@ void pcie_cap_slot_write_config(PCIDevice *dev, sltsta); } + /* + * If the slot is polulated, power indicator is off and power + * controller is off, it is safe to detach the devices. + */ + if ((sltsta & PCI_EXP_SLTSTA_PDS) && (val & PCI_EXP_SLTCTL_PCC) && + ((val & PCI_EXP_SLTCTL_PIC_OFF) == PCI_EXP_SLTCTL_PIC_OFF)) { + PCIBus *sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(dev)); + pci_for_each_device(sec_bus, pci_bus_num(sec_bus), + pcie_unplug_device, NULL); + + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDS); + pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDC); + } + hotplug_event_notify(dev); /* diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index ea4a2b2698..8d08539baa 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -32,6 +32,7 @@ #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" +#include "qapi-event.h" #include <libfdt.h> @@ -93,7 +94,7 @@ static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr, tm.tm_sec = rtas_ld(args, 5); /* Just generate a monitor event for the change */ - rtc_change_mon_event(&tm); + qapi_event_send_rtc_change(qemu_timedate_diff(&tm), &error_abort); spapr->rtc_offset = qemu_timedate_diff(&tm); rtas_st(rets, 0, RTAS_OUT_SUCCESS); diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index a529ad24c7..d47ecd6ab4 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -419,7 +419,7 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error) SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); BlockErrorAction action = bdrv_get_error_action(s->qdev.conf.bs, is_read, error); - if (action == BDRV_ACTION_REPORT) { + if (action == BLOCK_ERROR_ACTION_REPORT) { switch (error) { case ENOMEDIUM: scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); @@ -439,10 +439,10 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error) } } bdrv_error_action(s->qdev.conf.bs, action, is_read, error); - if (action == BDRV_ACTION_STOP) { + if (action == BLOCK_ERROR_ACTION_STOP) { scsi_req_retry(&r->req); } - return action != BDRV_ACTION_IGNORE; + return action != BLOCK_ERROR_ACTION_IGNORE; } static void scsi_write_complete(void * opaque, int ret) diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 1201f90db5..05002bf9d6 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -26,6 +26,7 @@ #include "sysemu/sysemu.h" #include "hw/timer/mc146818rtc.h" #include "qapi/visitor.h" +#include "qapi-event.h" #ifdef TARGET_I386 #include "hw/i386/apic.h" @@ -530,7 +531,7 @@ static void rtc_set_time(RTCState *s) s->base_rtc = mktimegm(&tm); s->last_update = qemu_clock_get_ns(rtc_clock); - rtc_change_mon_event(&tm); + qapi_event_send_rtc_change(qemu_timedate_diff(&tm), &error_abort); } static void rtc_set_cmos(RTCState *s, const struct tm *tm) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index c1b1aad6cf..e55fe1cc7e 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -20,6 +20,7 @@ #include <linux/vhost.h> #include "exec/address-spaces.h" #include "hw/virtio/virtio-bus.h" +#include "migration/migration.h" static void vhost_dev_sync_region(struct vhost_dev *dev, MemoryRegionSection *section, @@ -304,7 +305,9 @@ static int vhost_verify_ring_mappings(struct vhost_dev *dev, uint64_t size) { int i; - for (i = 0; i < dev->nvqs; ++i) { + int r = 0; + + for (i = 0; !r && i < dev->nvqs; ++i) { struct vhost_virtqueue *vq = dev->vqs + i; hwaddr l; void *p; @@ -316,15 +319,15 @@ static int vhost_verify_ring_mappings(struct vhost_dev *dev, p = cpu_physical_memory_map(vq->ring_phys, &l, 1); if (!p || l != vq->ring_size) { fprintf(stderr, "Unable to map ring buffer for ring %d\n", i); - return -ENOMEM; + r = -ENOMEM; } if (p != vq->ring) { fprintf(stderr, "Ring buffer relocated for ring %d\n", i); - return -EBUSY; + r = -EBUSY; } cpu_physical_memory_unmap(p, l, 0, 0); } - return 0; + return r; } static struct vhost_memory_region *vhost_dev_find_reg(struct vhost_dev *dev, @@ -854,6 +857,12 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, .eventfd_del = vhost_eventfd_del, .priority = 10 }; + hdev->migration_blocker = NULL; + if (!(hdev->features & (0x1 << VHOST_F_LOG_ALL))) { + error_setg(&hdev->migration_blocker, + "Migration disabled: vhost lacks VHOST_F_LOG_ALL feature."); + migrate_add_blocker(hdev->migration_blocker); + } hdev->mem = g_malloc0(offsetof(struct vhost_memory, regions)); hdev->n_mem_sections = 0; hdev->mem_sections = NULL; @@ -882,6 +891,10 @@ void vhost_dev_cleanup(struct vhost_dev *hdev) vhost_virtqueue_cleanup(hdev->vqs + i); } memory_listener_unregister(&hdev->memory_listener); + if (hdev->migration_blocker) { + migrate_del_blocker(hdev->migration_blocker); + error_free(hdev->migration_blocker); + } g_free(hdev->mem); g_free(hdev->mem_sections); hdev->vhost_ops->vhost_backend_cleanup(hdev); diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 22cd52edee..2a2e58a297 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -24,6 +24,7 @@ #include "sysemu/kvm.h" #include "exec/address-spaces.h" #include "qapi/visitor.h" +#include "qapi-event.h" #if defined(__linux__) #include <sys/mman.h> @@ -289,8 +290,9 @@ static void virtio_balloon_set_config(VirtIODevice *vdev, memcpy(&config, config_data, sizeof(struct virtio_balloon_config)); dev->actual = le32_to_cpu(config.actual); if (dev->actual != oldactual) { - qemu_balloon_changed(ram_size - - ((ram_addr_t) dev->actual << VIRTIO_BALLOON_PFN_SHIFT)); + qapi_event_send_balloon_change(ram_size - + ((ram_addr_t) dev->actual << VIRTIO_BALLOON_PFN_SHIFT), + &error_abort); } } diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index ce97514b69..57e1e6141e 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -976,6 +976,8 @@ static void virtio_pci_device_plugged(DeviceState *d) if (proxy->nvectors && msix_init_exclusive_bar(&proxy->pci_dev, proxy->nvectors, 1)) { + error_report("unable to init msix vectors to %" PRIu32, + proxy->nvectors); proxy->nvectors = 0; } diff --git a/hw/watchdog/watchdog.c b/hw/watchdog/watchdog.c index f28161b2d6..4aebd34924 100644 --- a/hw/watchdog/watchdog.c +++ b/hw/watchdog/watchdog.c @@ -24,9 +24,9 @@ #include "qemu/config-file.h" #include "qemu/queue.h" #include "qapi/qmp/types.h" -#include "monitor/monitor.h" #include "sysemu/sysemu.h" #include "sysemu/watchdog.h" +#include "qapi-event.h" /* Possible values for action parameter. */ #define WDT_RESET 1 /* Hard reset. */ @@ -101,15 +101,6 @@ int select_watchdog_action(const char *p) return 0; } -static void watchdog_mon_event(const char *action) -{ - QObject *data; - - data = qobject_from_jsonf("{ 'action': %s }", action); - monitor_protocol_event(QEVENT_WATCHDOG, data); - qobject_decref(data); -} - /* This actually performs the "action" once a watchdog has expired, * ie. reboot, shutdown, exit, etc. */ @@ -117,31 +108,31 @@ void watchdog_perform_action(void) { switch(watchdog_action) { case WDT_RESET: /* same as 'system_reset' in monitor */ - watchdog_mon_event("reset"); + qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_RESET, &error_abort); qemu_system_reset_request(); break; case WDT_SHUTDOWN: /* same as 'system_powerdown' in monitor */ - watchdog_mon_event("shutdown"); + qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_SHUTDOWN, &error_abort); qemu_system_powerdown_request(); break; case WDT_POWEROFF: /* same as 'quit' command in monitor */ - watchdog_mon_event("poweroff"); + qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_POWEROFF, &error_abort); exit(0); case WDT_PAUSE: /* same as 'stop' command in monitor */ - watchdog_mon_event("pause"); + qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_PAUSE, &error_abort); vm_stop(RUN_STATE_WATCHDOG); break; case WDT_DEBUG: - watchdog_mon_event("debug"); + qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_DEBUG, &error_abort); fprintf(stderr, "watchdog: timer fired\n"); break; case WDT_NONE: - watchdog_mon_event("none"); + qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_NONE, &error_abort); break; } } diff --git a/include/block/block.h b/include/block/block.h index f15b99b00b..d0baf4fb83 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -150,10 +150,6 @@ typedef enum { #define BDRV_BLOCK_ALLOCATED 0x10 #define BDRV_BLOCK_OFFSET_MASK BDRV_SECTOR_MASK -typedef enum { - BDRV_ACTION_REPORT, BDRV_ACTION_IGNORE, BDRV_ACTION_STOP -} BlockErrorAction; - typedef QSIMPLEQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue; typedef struct BDRVReopenState { diff --git a/include/block/block_int.h b/include/block/block_int.h index 7aa2213f77..715c761fad 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -425,9 +425,6 @@ void bdrv_attach_aio_context(BlockDriverState *bs, #ifdef _WIN32 int is_windows_drive(const char *filename); #endif -void bdrv_emit_qmp_error_event(const BlockDriverState *bdrv, - enum MonitorEvent ev, - BlockErrorAction action, bool is_read); /** * stream_start: diff --git a/include/block/blockjob.h b/include/block/blockjob.h index c0a787530b..e443987ea8 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -217,12 +217,21 @@ void block_job_pause(BlockJob *job); void block_job_resume(BlockJob *job); /** - * qobject_from_block_job: + * block_job_event_cancle: * @job: The job whose information is requested. * - * Return a QDict corresponding to @job's query-block-jobs entry. + * Send a BLOCK_JOB_CANCELLED event for the specified job. */ -QObject *qobject_from_block_job(BlockJob *job); +void block_job_event_cancelled(BlockJob *job); + +/** + * block_job_ready: + * @job: The job which is now ready to complete. + * @msg: Error message. Only present on failure. + * + * Send a BLOCK_JOB_COMPLETED event for the specified job. + */ +void block_job_event_completed(BlockJob *job, const char *msg); /** * block_job_ready: @@ -230,7 +239,7 @@ QObject *qobject_from_block_job(BlockJob *job); * * Send a BLOCK_JOB_READY event for the specified job. */ -void block_job_ready(BlockJob *job); +void block_job_event_ready(BlockJob *job); /** * block_job_is_paused: diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 3d62d9c464..5e5d86ec46 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -224,7 +224,7 @@ static inline void tb_set_jmp_target1(uintptr_t jmp_addr, uintptr_t addr) /* no need to flush icache explicitly */ } #elif defined(_ARCH_PPC) -void ppc_tb_set_jmp_target(unsigned long jmp_addr, unsigned long addr); +void ppc_tb_set_jmp_target(uintptr_t jmp_addr, uintptr_t addr); #define tb_set_jmp_target1 ppc_tb_set_jmp_target #elif defined(__i386__) || defined(__x86_64__) static inline void tb_set_jmp_target1(uintptr_t jmp_addr, uintptr_t addr) diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index 4b3090ca74..77177c5317 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -63,7 +63,6 @@ typedef uint64_t uint64; typedef int64_t int64; #define LIT64( a ) a##LL -#define INLINE static inline #define STATUS_PARAM , float_status *status #define STATUS(field) status->field @@ -181,59 +180,59 @@ typedef struct float_status { flag default_nan_mode; } float_status; -INLINE void set_float_detect_tininess(int val STATUS_PARAM) +static inline void set_float_detect_tininess(int val STATUS_PARAM) { STATUS(float_detect_tininess) = val; } -INLINE void set_float_rounding_mode(int val STATUS_PARAM) +static inline void set_float_rounding_mode(int val STATUS_PARAM) { STATUS(float_rounding_mode) = val; } -INLINE void set_float_exception_flags(int val STATUS_PARAM) +static inline void set_float_exception_flags(int val STATUS_PARAM) { STATUS(float_exception_flags) = val; } -INLINE void set_floatx80_rounding_precision(int val STATUS_PARAM) +static inline void set_floatx80_rounding_precision(int val STATUS_PARAM) { STATUS(floatx80_rounding_precision) = val; } -INLINE void set_flush_to_zero(flag val STATUS_PARAM) +static inline void set_flush_to_zero(flag val STATUS_PARAM) { STATUS(flush_to_zero) = val; } -INLINE void set_flush_inputs_to_zero(flag val STATUS_PARAM) +static inline void set_flush_inputs_to_zero(flag val STATUS_PARAM) { STATUS(flush_inputs_to_zero) = val; } -INLINE void set_default_nan_mode(flag val STATUS_PARAM) +static inline void set_default_nan_mode(flag val STATUS_PARAM) { STATUS(default_nan_mode) = val; } -INLINE int get_float_detect_tininess(float_status *status) +static inline int get_float_detect_tininess(float_status *status) { return STATUS(float_detect_tininess); } -INLINE int get_float_rounding_mode(float_status *status) +static inline int get_float_rounding_mode(float_status *status) { return STATUS(float_rounding_mode); } -INLINE int get_float_exception_flags(float_status *status) +static inline int get_float_exception_flags(float_status *status) { return STATUS(float_exception_flags); } -INLINE int get_floatx80_rounding_precision(float_status *status) +static inline int get_floatx80_rounding_precision(float_status *status) { return STATUS(floatx80_rounding_precision); } -INLINE flag get_flush_to_zero(float_status *status) +static inline flag get_flush_to_zero(float_status *status) { return STATUS(flush_to_zero); } -INLINE flag get_flush_inputs_to_zero(float_status *status) +static inline flag get_flush_inputs_to_zero(float_status *status) { return STATUS(flush_inputs_to_zero); } -INLINE flag get_default_nan_mode(float_status *status) +static inline flag get_default_nan_mode(float_status *status) { return STATUS(default_nan_mode); } @@ -284,22 +283,22 @@ float128 int64_to_float128(int64_t STATUS_PARAM); float128 uint64_to_float128(uint64_t STATUS_PARAM); /* We provide the int16 versions for symmetry of API with float-to-int */ -INLINE float32 int16_to_float32(int16_t v STATUS_PARAM) +static inline float32 int16_to_float32(int16_t v STATUS_PARAM) { return int32_to_float32(v STATUS_VAR); } -INLINE float32 uint16_to_float32(uint16_t v STATUS_PARAM) +static inline float32 uint16_to_float32(uint16_t v STATUS_PARAM) { return uint32_to_float32(v STATUS_VAR); } -INLINE float64 int16_to_float64(int16_t v STATUS_PARAM) +static inline float64 int16_to_float64(int16_t v STATUS_PARAM) { return int32_to_float64(v STATUS_VAR); } -INLINE float64 uint16_to_float64(uint16_t v STATUS_PARAM) +static inline float64 uint16_to_float64(uint16_t v STATUS_PARAM) { return uint32_to_float64(v STATUS_VAR); } @@ -319,7 +318,7 @@ int float16_is_quiet_nan( float16 ); int float16_is_signaling_nan( float16 ); float16 float16_maybe_silence_nan( float16 ); -INLINE int float16_is_any_nan(float16 a) +static inline int float16_is_any_nan(float16 a) { return ((float16_val(a) & ~0x8000) > 0x7c00); } @@ -380,7 +379,7 @@ int float32_is_signaling_nan( float32 ); float32 float32_maybe_silence_nan( float32 ); float32 float32_scalbn( float32, int STATUS_PARAM ); -INLINE float32 float32_abs(float32 a) +static inline float32 float32_abs(float32 a) { /* Note that abs does *not* handle NaN specially, nor does * it flush denormal inputs to zero. @@ -388,7 +387,7 @@ INLINE float32 float32_abs(float32 a) return make_float32(float32_val(a) & 0x7fffffff); } -INLINE float32 float32_chs(float32 a) +static inline float32 float32_chs(float32 a) { /* Note that chs does *not* handle NaN specially, nor does * it flush denormal inputs to zero. @@ -396,32 +395,32 @@ INLINE float32 float32_chs(float32 a) return make_float32(float32_val(a) ^ 0x80000000); } -INLINE int float32_is_infinity(float32 a) +static inline int float32_is_infinity(float32 a) { return (float32_val(a) & 0x7fffffff) == 0x7f800000; } -INLINE int float32_is_neg(float32 a) +static inline int float32_is_neg(float32 a) { return float32_val(a) >> 31; } -INLINE int float32_is_zero(float32 a) +static inline int float32_is_zero(float32 a) { return (float32_val(a) & 0x7fffffff) == 0; } -INLINE int float32_is_any_nan(float32 a) +static inline int float32_is_any_nan(float32 a) { return ((float32_val(a) & ~(1 << 31)) > 0x7f800000UL); } -INLINE int float32_is_zero_or_denormal(float32 a) +static inline int float32_is_zero_or_denormal(float32 a) { return (float32_val(a) & 0x7f800000) == 0; } -INLINE float32 float32_set_sign(float32 a, int sign) +static inline float32 float32_set_sign(float32 a, int sign) { return make_float32((float32_val(a) & 0x7fffffff) | (sign << 31)); } @@ -490,7 +489,7 @@ int float64_is_signaling_nan( float64 ); float64 float64_maybe_silence_nan( float64 ); float64 float64_scalbn( float64, int STATUS_PARAM ); -INLINE float64 float64_abs(float64 a) +static inline float64 float64_abs(float64 a) { /* Note that abs does *not* handle NaN specially, nor does * it flush denormal inputs to zero. @@ -498,7 +497,7 @@ INLINE float64 float64_abs(float64 a) return make_float64(float64_val(a) & 0x7fffffffffffffffLL); } -INLINE float64 float64_chs(float64 a) +static inline float64 float64_chs(float64 a) { /* Note that chs does *not* handle NaN specially, nor does * it flush denormal inputs to zero. @@ -506,32 +505,32 @@ INLINE float64 float64_chs(float64 a) return make_float64(float64_val(a) ^ 0x8000000000000000LL); } -INLINE int float64_is_infinity(float64 a) +static inline int float64_is_infinity(float64 a) { return (float64_val(a) & 0x7fffffffffffffffLL ) == 0x7ff0000000000000LL; } -INLINE int float64_is_neg(float64 a) +static inline int float64_is_neg(float64 a) { return float64_val(a) >> 63; } -INLINE int float64_is_zero(float64 a) +static inline int float64_is_zero(float64 a) { return (float64_val(a) & 0x7fffffffffffffffLL) == 0; } -INLINE int float64_is_any_nan(float64 a) +static inline int float64_is_any_nan(float64 a) { return ((float64_val(a) & ~(1ULL << 63)) > 0x7ff0000000000000ULL); } -INLINE int float64_is_zero_or_denormal(float64 a) +static inline int float64_is_zero_or_denormal(float64 a) { return (float64_val(a) & 0x7ff0000000000000LL) == 0; } -INLINE float64 float64_set_sign(float64 a, int sign) +static inline float64 float64_set_sign(float64 a, int sign) { return make_float64((float64_val(a) & 0x7fffffffffffffffULL) | ((int64_t)sign << 63)); @@ -585,39 +584,39 @@ int floatx80_is_signaling_nan( floatx80 ); floatx80 floatx80_maybe_silence_nan( floatx80 ); floatx80 floatx80_scalbn( floatx80, int STATUS_PARAM ); -INLINE floatx80 floatx80_abs(floatx80 a) +static inline floatx80 floatx80_abs(floatx80 a) { a.high &= 0x7fff; return a; } -INLINE floatx80 floatx80_chs(floatx80 a) +static inline floatx80 floatx80_chs(floatx80 a) { a.high ^= 0x8000; return a; } -INLINE int floatx80_is_infinity(floatx80 a) +static inline int floatx80_is_infinity(floatx80 a) { return (a.high & 0x7fff) == 0x7fff && a.low == 0x8000000000000000LL; } -INLINE int floatx80_is_neg(floatx80 a) +static inline int floatx80_is_neg(floatx80 a) { return a.high >> 15; } -INLINE int floatx80_is_zero(floatx80 a) +static inline int floatx80_is_zero(floatx80 a) { return (a.high & 0x7fff) == 0 && a.low == 0; } -INLINE int floatx80_is_zero_or_denormal(floatx80 a) +static inline int floatx80_is_zero_or_denormal(floatx80 a) { return (a.high & 0x7fff) == 0; } -INLINE int floatx80_is_any_nan(floatx80 a) +static inline int floatx80_is_any_nan(floatx80 a) { return ((a.high & 0x7fff) == 0x7fff) && (a.low<<1); } @@ -670,39 +669,39 @@ int float128_is_signaling_nan( float128 ); float128 float128_maybe_silence_nan( float128 ); float128 float128_scalbn( float128, int STATUS_PARAM ); -INLINE float128 float128_abs(float128 a) +static inline float128 float128_abs(float128 a) { a.high &= 0x7fffffffffffffffLL; return a; } -INLINE float128 float128_chs(float128 a) +static inline float128 float128_chs(float128 a) { a.high ^= 0x8000000000000000LL; return a; } -INLINE int float128_is_infinity(float128 a) +static inline int float128_is_infinity(float128 a) { return (a.high & 0x7fffffffffffffffLL) == 0x7fff000000000000LL && a.low == 0; } -INLINE int float128_is_neg(float128 a) +static inline int float128_is_neg(float128 a) { return a.high >> 63; } -INLINE int float128_is_zero(float128 a) +static inline int float128_is_zero(float128 a) { return (a.high & 0x7fffffffffffffffLL) == 0 && a.low == 0; } -INLINE int float128_is_zero_or_denormal(float128 a) +static inline int float128_is_zero_or_denormal(float128 a) { return (a.high & 0x7fff000000000000LL) == 0; } -INLINE int float128_is_any_nan(float128 a) +static inline int float128_is_any_nan(float128 a) { return ((a.high >> 48) & 0x7fff) == 0x7fff && ((a.low != 0) || ((a.high & 0xffffffffffffLL) != 0)); diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 19f78ea336..486e98feac 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -33,10 +33,13 @@ struct PCMachineState { MemoryRegion hotplug_memory; HotplugHandler *acpi_dev; + + uint64_t max_ram_below_4g; }; #define PC_MACHINE_ACPI_DEVICE_PROP "acpi-device" #define PC_MACHINE_MEMHP_REGION_SIZE "hotplug-memory-region-size" +#define PC_MACHINE_MAX_RAM_BELOW_4G "max-ram-below-4g" /** * PCMachineClass: @@ -297,8 +300,16 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); .driver = "ICH9-LPC",\ .property = "memory-hotplug-support",\ .value = "off",\ + },{\ + .driver = "xio3130-downstream",\ + .property = COMPAT_PROP_PCP,\ + .value = "off",\ + },{\ + .driver = "ioh3420",\ + .property = COMPAT_PROP_PCP,\ + .value = "off",\ } - + #define PC_Q35_COMPAT_1_7 \ PC_COMPAT_1_7, \ PC_Q35_COMPAT_2_0, \ diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 8c25ae5d1d..c352c7b3ad 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -158,6 +158,9 @@ enum { QEMU_PCI_CAP_SHPC = (1 << QEMU_PCI_SHPC_BITNR), #define QEMU_PCI_SLOTID_BITNR 6 QEMU_PCI_CAP_SLOTID = (1 << QEMU_PCI_SLOTID_BITNR), + /* PCI Express capability - Power Controller Present */ +#define QEMU_PCIE_SLTCAP_PCP_BITNR 7 + QEMU_PCIE_SLTCAP_PCP = (1 << QEMU_PCIE_SLTCAP_PCP_BITNR), }; #define TYPE_PCI_DEVICE "pci-device" diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index b0bf7e3ce1..7fe81f31ef 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -76,6 +76,8 @@ struct PCIExpressDevice { PCIEAERLog aer_log; }; +#define COMPAT_PROP_PCP "power_controller_present" + /* PCI express capability helper functions */ int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port); int pcie_endpoint_cap_init(PCIDevice *dev, uint8_t offset); diff --git a/include/hw/pci/pcie_regs.h b/include/hw/pci/pcie_regs.h index 4d123d9fcc..652d9fc58c 100644 --- a/include/hw/pci/pcie_regs.h +++ b/include/hw/pci/pcie_regs.h @@ -57,6 +57,8 @@ #define PCI_EXP_SLTCTL_PIC_SHIFT (ffs(PCI_EXP_SLTCTL_PIC) - 1) #define PCI_EXP_SLTCTL_PIC_OFF \ (PCI_EXP_SLTCTL_IND_OFF << PCI_EXP_SLTCTL_PIC_SHIFT) +#define PCI_EXP_SLTCTL_PIC_ON \ + (PCI_EXP_SLTCTL_IND_ON << PCI_EXP_SLTCTL_PIC_SHIFT) #define PCI_EXP_SLTCTL_SUPPORTED \ (PCI_EXP_SLTCTL_ABPE | \ diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 33028ec8c2..d5593d1620 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -45,6 +45,7 @@ struct vhost_dev { bool log_enabled; vhost_log_chunk_t *log; unsigned long long log_size; + Error *migration_blocker; bool force; bool memory_changed; hwaddr mem_changed_start_addr; diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h index 85fda3dee4..f71f2d8963 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -37,10 +37,11 @@ void xen_cmos_set_s3_resume(void *opaque, int irq, int level); qemu_irq *xen_interrupt_controller_init(void); int xen_init(MachineClass *mc); -int xen_hvm_init(MemoryRegion **ram_memory); void xenstore_store_pv_console_info(int i, struct CharDriverState *chr); #if defined(NEED_CPU_H) && !defined(CONFIG_USER_ONLY) +int xen_hvm_init(ram_addr_t *below_4g_mem_size, ram_addr_t *above_4g_mem_size, + MemoryRegion **ram_memory); void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, struct MemoryRegion *mr); void xen_modified_memory(ram_addr_t start, ram_addr_t length); diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 97696ea693..3d6929d6cd 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -19,49 +19,8 @@ extern Monitor *default_mon; /* flags for monitor commands */ #define MONITOR_CMD_ASYNC 0x0001 -/* QMP events */ -typedef enum MonitorEvent { - QEVENT_SHUTDOWN, - QEVENT_RESET, - QEVENT_POWERDOWN, - QEVENT_STOP, - QEVENT_RESUME, - QEVENT_VNC_CONNECTED, - QEVENT_VNC_INITIALIZED, - QEVENT_VNC_DISCONNECTED, - QEVENT_BLOCK_IO_ERROR, - QEVENT_RTC_CHANGE, - QEVENT_WATCHDOG, - QEVENT_SPICE_CONNECTED, - QEVENT_SPICE_INITIALIZED, - QEVENT_SPICE_DISCONNECTED, - QEVENT_BLOCK_JOB_COMPLETED, - QEVENT_BLOCK_JOB_CANCELLED, - QEVENT_BLOCK_JOB_ERROR, - QEVENT_BLOCK_JOB_READY, - QEVENT_DEVICE_DELETED, - QEVENT_DEVICE_TRAY_MOVED, - QEVENT_NIC_RX_FILTER_CHANGED, - QEVENT_SUSPEND, - QEVENT_SUSPEND_DISK, - QEVENT_WAKEUP, - QEVENT_BALLOON_CHANGE, - QEVENT_SPICE_MIGRATE_COMPLETED, - QEVENT_GUEST_PANICKED, - QEVENT_BLOCK_IMAGE_CORRUPTED, - QEVENT_QUORUM_FAILURE, - QEVENT_QUORUM_REPORT_BAD, - QEVENT_ACPI_OST, - - /* Add to 'monitor_event_names' array in monitor.c when - * defining new events here */ - - QEVENT_MAX, -} MonitorEvent; - int monitor_cur_is_qmp(void); -void monitor_protocol_event(MonitorEvent event, QObject *data); void monitor_init(CharDriverState *chr, int flags); int monitor_suspend(Monitor *mon); diff --git a/include/qapi/qmp-event.h b/include/qapi/qmp-event.h new file mode 100644 index 0000000000..8a8ffb5718 --- /dev/null +++ b/include/qapi/qmp-event.h @@ -0,0 +1,27 @@ +/* + * QMP Event related + * + * Copyright (c) 2014 Wenchao Xia + * + * Authors: + * Wenchao Xia <wenchaoqemu@gmail.com> + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef QMP_EVENT_H +#define QMP_EVENT_H + +#include "qapi/error.h" +#include "qapi/qmp/qdict.h" + +typedef void (*QMPEventFuncEmit)(unsigned event, QDict *dict, Error **errp); + +void qmp_event_set_func_emit(QMPEventFuncEmit emit); + +QMPEventFuncEmit qmp_event_get_func_emit(void); + +QDict *qmp_event_build_dict(const char *event_name); +#endif diff --git a/include/qemu/cache-utils.h b/include/qemu/cache-utils.h deleted file mode 100644 index 211245bea0..0000000000 --- a/include/qemu/cache-utils.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef QEMU_CACHE_UTILS_H -#define QEMU_CACHE_UTILS_H - -#if defined(_ARCH_PPC) - -#include <stdint.h> /* uintptr_t */ - -struct qemu_cache_conf { - unsigned long dcache_bsize; - unsigned long icache_bsize; -}; - -extern struct qemu_cache_conf qemu_cache_conf; - -void qemu_cache_utils_init(void); - -/* mildly adjusted code from tcg-dyngen.c */ -static inline void flush_icache_range(uintptr_t start, uintptr_t stop) -{ - unsigned long p, start1, stop1; - unsigned long dsize = qemu_cache_conf.dcache_bsize; - unsigned long isize = qemu_cache_conf.icache_bsize; - - start1 = start & ~(dsize - 1); - stop1 = (stop + dsize - 1) & ~(dsize - 1); - for (p = start1; p < stop1; p += dsize) { - asm volatile ("dcbst 0,%0" : : "r"(p) : "memory"); - } - asm volatile ("sync" : : : "memory"); - - start &= start & ~(isize - 1); - stop1 = (stop + isize - 1) & ~(isize - 1); - for (p = start1; p < stop1; p += isize) { - asm volatile ("icbi 0,%0" : : "r"(p) : "memory"); - } - asm volatile ("sync" : : : "memory"); - asm volatile ("isync" : : : "memory"); -} - -#else -#define qemu_cache_utils_init() do { } while (0) -#endif - -#endif /* QEMU_CACHE_UTILS_H */ diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 6d35c1bcba..8480d523f0 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -251,18 +251,6 @@ unsigned long qemu_getauxval(unsigned long type); static inline unsigned long qemu_getauxval(unsigned long type) { return 0; } #endif -/** - * qemu_init_auxval: - * @envp: the third argument to main - * - * If supported and required, locate the auxiliary vector at program startup. - */ -#if defined(CONFIG_GETAUXVAL) || !defined(__linux__) -static inline void qemu_init_auxval(char **envp) { } -#else -void qemu_init_auxval(char **envp); -#endif - void qemu_set_tty_echo(int fd, bool echo); void os_mem_prealloc(int fd, char *area, size_t sz); diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index 45588d7d58..fdbb196787 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -29,6 +29,7 @@ int inet_aton(const char *cp, struct in_addr *ia); #include "qemu/option.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" +#include "qapi-types.h" extern QemuOptsList socket_optslist; @@ -60,7 +61,7 @@ int inet_nonblocking_connect(const char *str, void *opaque, Error **errp); int inet_dgram_opts(QemuOpts *opts, Error **errp); -const char *inet_strfamily(int family); +NetworkAddressFamily inet_netfamily(int family); int unix_listen_opts(QemuOpts *opts, Error **errp); int unix_listen(const char *path, char *ostr, int olen, Error **errp); diff --git a/include/sysemu/balloon.h b/include/sysemu/balloon.h index bd9d39505d..0345e01efb 100644 --- a/include/sysemu/balloon.h +++ b/include/sysemu/balloon.h @@ -24,6 +24,4 @@ int qemu_add_balloon_handler(QEMUBalloonEvent *event_func, QEMUBalloonStatus *stat_func, void *opaque); void qemu_remove_balloon_handler(void *opaque); -void qemu_balloon_changed(int64_t actual); - #endif diff --git a/include/sysemu/char.h b/include/sysemu/char.h index 672ed39a31..c8b15f9729 100644 --- a/include/sysemu/char.h +++ b/include/sysemu/char.h @@ -54,6 +54,7 @@ typedef struct { typedef void IOEventHandler(void *opaque, int event); struct CharDriverState { + QemuMutex chr_write_lock; void (*init)(struct CharDriverState *s); int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len); int (*chr_sync_read)(struct CharDriverState *s, @@ -89,6 +90,15 @@ struct CharDriverState { }; /** + * @qemu_chr_alloc: + * + * Allocate and initialize a new CharDriverState. + * + * Returns: a newly allocated CharDriverState. + */ +CharDriverState *qemu_chr_alloc(void); + +/** * @qemu_chr_new_from_opts: * * Create a new character backend from a QemuOpts list. @@ -155,6 +165,7 @@ void qemu_chr_fe_event(CharDriverState *s, int event); * @qemu_chr_fe_printf: * * Write to a character backend using a printf style interface. + * This function is thread-safe. * * @fmt see #printf */ @@ -167,8 +178,9 @@ int qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond, /** * @qemu_chr_fe_write: * - * Write data to a character backend from the front end. This function will - * send data from the front end to the back end. + * Write data to a character backend from the front end. This function + * will send data from the front end to the back end. This function + * is thread-safe. * * @buf the data * @len the number of bytes to send @@ -183,7 +195,7 @@ int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len); * Write data to a character backend from the front end. This function will * send data from the front end to the back end. Unlike @qemu_chr_fe_write, * this function will block if the back end cannot consume all of the data - * attempted to be written. + * attempted to be written. This function is thread-safe. * * @buf the data * @len the number of bytes to send @@ -207,7 +219,7 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len); /** * @qemu_chr_fe_ioctl: * - * Issue a device specific ioctl to a backend. + * Issue a device specific ioctl to a backend. This function is thread-safe. * * @cmd see CHR_IOCTL_* * @arg the data associated with @cmd diff --git a/include/sysemu/os-posix.h b/include/sysemu/os-posix.h index 25d0b2a73f..f1315213f1 100644 --- a/include/sysemu/os-posix.h +++ b/include/sysemu/os-posix.h @@ -26,6 +26,8 @@ #ifndef QEMU_OS_POSIX_H #define QEMU_OS_POSIX_H +#include <sys/time.h> + void os_set_line_buffering(void); void os_set_proc_name(const char *s); void os_setup_signal_handling(void); diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 6b4cc133c5..285c45baf2 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -202,8 +202,6 @@ void do_usb_add(Monitor *mon, const QDict *qdict); void do_usb_del(Monitor *mon, const QDict *qdict); void usb_info(Monitor *mon, const QDict *qdict); -void rtc_change_mon_event(struct tm *tm); - void add_boot_device_path(int32_t bootindex, DeviceState *dev, const char *suffix); char *get_boot_devices_list(size_t *size, bool ignore_suffixes); diff --git a/linux-user/main.c b/linux-user/main.c index a87c6f7ed4..df1bb0e758 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -28,7 +28,6 @@ #include "qemu.h" #include "qemu-common.h" -#include "qemu/cache-utils.h" #include "cpu.h" #include "tcg.h" #include "qemu/timer.h" @@ -3829,9 +3828,6 @@ int main(int argc, char **argv, char **envp) module_call_init(MODULE_INIT_QOM); - qemu_init_auxval(envp); - qemu_cache_utils_init(); - if ((envlist = envlist_create()) == NULL) { (void) fprintf(stderr, "Unable to allocate envlist\n"); exit(1); diff --git a/monitor.c b/monitor.c index c7f879713e..a8ab600c88 100644 --- a/monitor.c +++ b/monitor.c @@ -71,6 +71,8 @@ #include "hmp.h" #include "qemu/thread.h" #include "block/qapi.h" +#include "qapi/qmp-event.h" +#include "qapi-event.h" /* for pic/irq_info */ #if defined(TARGET_SPARC) @@ -179,23 +181,28 @@ typedef struct MonitorControl { * throttling is calculated globally, rather than per-Monitor * instance. */ -typedef struct MonitorEventState { - MonitorEvent event; /* Event being tracked */ - int64_t rate; /* Period over which to throttle. 0 to disable */ - int64_t last; /* Time at which event was last emitted */ +typedef struct MonitorQAPIEventState { + QAPIEvent event; /* Event being tracked */ + int64_t rate; /* Minimum time (in ns) between two events */ + int64_t last; /* QEMU_CLOCK_REALTIME value at last emission */ QEMUTimer *timer; /* Timer for handling delayed events */ QObject *data; /* Event pending delayed dispatch */ -} MonitorEventState; +} MonitorQAPIEventState; struct Monitor { CharDriverState *chr; - int mux_out; int reset_seen; int flags; int suspend_cnt; bool skip_flush; + + QemuMutex out_lock; QString *outbuf; - guint watch; + guint out_watch; + + /* Read under either BQL or out_lock, written with BQL+out_lock. */ + int mux_out; + ReadLineState *rs; MonitorControl *mc; CPUState *mon_cpu; @@ -210,6 +217,9 @@ struct Monitor { /* QMP checker flags */ #define QMP_ACCEPT_UNKNOWNS 1 +/* Protects mon_list, monitor_event_state. */ +static QemuMutex monitor_lock; + static QLIST_HEAD(mon_list, Monitor) mon_list; static QLIST_HEAD(mon_fdsets, MonFdset) mon_fdsets; static int mon_refcount; @@ -268,17 +278,22 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, } } +static void monitor_flush_locked(Monitor *mon); + static gboolean monitor_unblocked(GIOChannel *chan, GIOCondition cond, void *opaque) { Monitor *mon = opaque; - mon->watch = 0; - monitor_flush(mon); + qemu_mutex_lock(&mon->out_lock); + mon->out_watch = 0; + monitor_flush_locked(mon); + qemu_mutex_unlock(&mon->out_lock); return FALSE; } -void monitor_flush(Monitor *mon) +/* Called with mon->out_lock held. */ +static void monitor_flush_locked(Monitor *mon) { int rc; size_t len; @@ -305,18 +320,26 @@ void monitor_flush(Monitor *mon) QDECREF(mon->outbuf); mon->outbuf = tmp; } - if (mon->watch == 0) { - mon->watch = qemu_chr_fe_add_watch(mon->chr, G_IO_OUT, - monitor_unblocked, mon); + if (mon->out_watch == 0) { + mon->out_watch = qemu_chr_fe_add_watch(mon->chr, G_IO_OUT, + monitor_unblocked, mon); } } } +void monitor_flush(Monitor *mon) +{ + qemu_mutex_lock(&mon->out_lock); + monitor_flush_locked(mon); + qemu_mutex_unlock(&mon->out_lock); +} + /* flush at every end of line */ static void monitor_puts(Monitor *mon, const char *str) { char c; + qemu_mutex_lock(&mon->out_lock); for(;;) { c = *str++; if (c == '\0') @@ -326,9 +349,10 @@ static void monitor_puts(Monitor *mon, const char *str) } qstring_append_chr(mon->outbuf, c); if (c == '\n') { - monitor_flush(mon); + monitor_flush_locked(mon); } } + qemu_mutex_unlock(&mon->out_lock); } void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) @@ -439,66 +463,14 @@ static void monitor_protocol_emitter(Monitor *mon, QObject *data) QDECREF(qmp); } -static void timestamp_put(QDict *qdict) -{ - int err; - QObject *obj; - qemu_timeval tv; - err = qemu_gettimeofday(&tv); - if (err < 0) - return; - - obj = qobject_from_jsonf("{ 'seconds': %" PRId64 ", " - "'microseconds': %" PRId64 " }", - (int64_t) tv.tv_sec, (int64_t) tv.tv_usec); - qdict_put_obj(qdict, "timestamp", obj); -} - - -static const char *monitor_event_names[] = { - [QEVENT_SHUTDOWN] = "SHUTDOWN", - [QEVENT_RESET] = "RESET", - [QEVENT_POWERDOWN] = "POWERDOWN", - [QEVENT_STOP] = "STOP", - [QEVENT_RESUME] = "RESUME", - [QEVENT_VNC_CONNECTED] = "VNC_CONNECTED", - [QEVENT_VNC_INITIALIZED] = "VNC_INITIALIZED", - [QEVENT_VNC_DISCONNECTED] = "VNC_DISCONNECTED", - [QEVENT_BLOCK_IO_ERROR] = "BLOCK_IO_ERROR", - [QEVENT_RTC_CHANGE] = "RTC_CHANGE", - [QEVENT_WATCHDOG] = "WATCHDOG", - [QEVENT_SPICE_CONNECTED] = "SPICE_CONNECTED", - [QEVENT_SPICE_INITIALIZED] = "SPICE_INITIALIZED", - [QEVENT_SPICE_DISCONNECTED] = "SPICE_DISCONNECTED", - [QEVENT_BLOCK_JOB_COMPLETED] = "BLOCK_JOB_COMPLETED", - [QEVENT_BLOCK_JOB_CANCELLED] = "BLOCK_JOB_CANCELLED", - [QEVENT_BLOCK_JOB_ERROR] = "BLOCK_JOB_ERROR", - [QEVENT_BLOCK_JOB_READY] = "BLOCK_JOB_READY", - [QEVENT_DEVICE_DELETED] = "DEVICE_DELETED", - [QEVENT_DEVICE_TRAY_MOVED] = "DEVICE_TRAY_MOVED", - [QEVENT_NIC_RX_FILTER_CHANGED] = "NIC_RX_FILTER_CHANGED", - [QEVENT_SUSPEND] = "SUSPEND", - [QEVENT_SUSPEND_DISK] = "SUSPEND_DISK", - [QEVENT_WAKEUP] = "WAKEUP", - [QEVENT_BALLOON_CHANGE] = "BALLOON_CHANGE", - [QEVENT_SPICE_MIGRATE_COMPLETED] = "SPICE_MIGRATE_COMPLETED", - [QEVENT_GUEST_PANICKED] = "GUEST_PANICKED", - [QEVENT_BLOCK_IMAGE_CORRUPTED] = "BLOCK_IMAGE_CORRUPTED", - [QEVENT_QUORUM_FAILURE] = "QUORUM_FAILURE", - [QEVENT_QUORUM_REPORT_BAD] = "QUORUM_REPORT_BAD", - [QEVENT_ACPI_OST] = "ACPI_DEVICE_OST", -}; -QEMU_BUILD_BUG_ON(ARRAY_SIZE(monitor_event_names) != QEVENT_MAX) - -static MonitorEventState monitor_event_state[QEVENT_MAX]; +static MonitorQAPIEventState monitor_qapi_event_state[QAPI_EVENT_MAX]; /* - * Emits the event to every monitor instance + * Emits the event to every monitor instance, @event is only used for trace + * Called with monitor_lock held. */ -static void -monitor_protocol_event_emit(MonitorEvent event, - QObject *data) +static void monitor_qapi_event_emit(QAPIEvent event, QObject *data) { Monitor *mon; @@ -510,20 +482,18 @@ monitor_protocol_event_emit(MonitorEvent event, } } - /* * Queue a new event for emission to Monitor instances, * applying any rate limiting if required. */ static void -monitor_protocol_event_queue(MonitorEvent event, - QObject *data) +monitor_qapi_event_queue(QAPIEvent event, QDict *data, Error **errp) { - MonitorEventState *evstate; + MonitorQAPIEventState *evstate; + assert(event < QAPI_EVENT_MAX); int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - assert(event < QEVENT_MAX); - evstate = &(monitor_event_state[event]); + evstate = &(monitor_qapi_event_state[event]); trace_monitor_protocol_event_queue(event, data, evstate->rate, @@ -531,8 +501,9 @@ monitor_protocol_event_queue(MonitorEvent event, now); /* Rate limit of 0 indicates no throttling */ + qemu_mutex_lock(&monitor_lock); if (!evstate->rate) { - monitor_protocol_event_emit(event, data); + monitor_qapi_event_emit(event, QOBJECT(data)); evstate->last = now; } else { int64_t delta = now - evstate->last; @@ -548,39 +519,39 @@ monitor_protocol_event_queue(MonitorEvent event, int64_t then = evstate->last + evstate->rate; timer_mod_ns(evstate->timer, then); } - evstate->data = data; + evstate->data = QOBJECT(data); qobject_incref(evstate->data); } else { - monitor_protocol_event_emit(event, data); + monitor_qapi_event_emit(event, QOBJECT(data)); evstate->last = now; } } + qemu_mutex_unlock(&monitor_lock); } - /* * The callback invoked by QemuTimer when a delayed * event is ready to be emitted */ -static void monitor_protocol_event_handler(void *opaque) +static void monitor_qapi_event_handler(void *opaque) { - MonitorEventState *evstate = opaque; + MonitorQAPIEventState *evstate = opaque; int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - trace_monitor_protocol_event_handler(evstate->event, evstate->data, evstate->last, now); + qemu_mutex_lock(&monitor_lock); if (evstate->data) { - monitor_protocol_event_emit(evstate->event, evstate->data); + monitor_qapi_event_emit(evstate->event, evstate->data); qobject_decref(evstate->data); evstate->data = NULL; } evstate->last = now; + qemu_mutex_unlock(&monitor_lock); } - /* * @event: the event ID to be limited * @rate: the rate limit in milliseconds @@ -590,65 +561,35 @@ static void monitor_protocol_event_handler(void *opaque) * milliseconds */ static void -monitor_protocol_event_throttle(MonitorEvent event, - int64_t rate) +monitor_qapi_event_throttle(QAPIEvent event, int64_t rate) { - MonitorEventState *evstate; - assert(event < QEVENT_MAX); + MonitorQAPIEventState *evstate; + assert(event < QAPI_EVENT_MAX); - evstate = &(monitor_event_state[event]); + evstate = &(monitor_qapi_event_state[event]); trace_monitor_protocol_event_throttle(event, rate); evstate->event = event; evstate->rate = rate * SCALE_MS; - evstate->timer = timer_new(QEMU_CLOCK_REALTIME, - SCALE_MS, - monitor_protocol_event_handler, - evstate); evstate->last = 0; evstate->data = NULL; + evstate->timer = timer_new(QEMU_CLOCK_REALTIME, + SCALE_MS, + monitor_qapi_event_handler, + evstate); } - -/* Global, one-time initializer to configure the rate limiting - * and initialize state */ -static void monitor_protocol_event_init(void) +static void monitor_qapi_event_init(void) { - /* Limit RTC & BALLOON events to 1 per second */ - monitor_protocol_event_throttle(QEVENT_RTC_CHANGE, 1000); - monitor_protocol_event_throttle(QEVENT_BALLOON_CHANGE, 1000); - monitor_protocol_event_throttle(QEVENT_WATCHDOG, 1000); + /* Limit guest-triggerable events to 1 per second */ + monitor_qapi_event_throttle(QAPI_EVENT_RTC_CHANGE, 1000); + monitor_qapi_event_throttle(QAPI_EVENT_WATCHDOG, 1000); + monitor_qapi_event_throttle(QAPI_EVENT_BALLOON_CHANGE, 1000); /* limit the rate of quorum events to avoid hammering the management */ - monitor_protocol_event_throttle(QEVENT_QUORUM_REPORT_BAD, 1000); - monitor_protocol_event_throttle(QEVENT_QUORUM_FAILURE, 1000); -} - -/** - * monitor_protocol_event(): Generate a Monitor event - * - * Event-specific data can be emitted through the (optional) 'data' parameter. - */ -void monitor_protocol_event(MonitorEvent event, QObject *data) -{ - QDict *qmp; - const char *event_name; - - assert(event < QEVENT_MAX); - - event_name = monitor_event_names[event]; - assert(event_name != NULL); - - qmp = qdict_new(); - timestamp_put(qmp); - qdict_put(qmp, "event", qstring_from_str(event_name)); - if (data) { - qobject_incref(data); - qdict_put_obj(qmp, "data", data); - } + monitor_qapi_event_throttle(QAPI_EVENT_QUORUM_REPORT_BAD, 1000); + monitor_qapi_event_throttle(QAPI_EVENT_QUORUM_FAILURE, 1000); - trace_monitor_protocol_event(event, event_name, qmp); - monitor_protocol_event_queue(event, QOBJECT(qmp)); - QDECREF(qmp); + qmp_event_set_func_emit(monitor_qapi_event_queue); } static int do_qmp_capabilities(Monitor *mon, const QDict *params, @@ -667,6 +608,7 @@ static void handle_user_command(Monitor *mon, const char *cmdline); static void monitor_data_init(Monitor *mon) { memset(mon, 0, sizeof(Monitor)); + qemu_mutex_init(&mon->out_lock); mon->outbuf = qstring_new(); /* Use *mon_cmds by default. */ mon->cmd_table = mon_cmds; @@ -675,6 +617,7 @@ static void monitor_data_init(Monitor *mon) static void monitor_data_destroy(Monitor *mon) { QDECREF(mon->outbuf); + qemu_mutex_destroy(&mon->out_lock); } char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, @@ -702,11 +645,13 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, handle_user_command(&hmp, command_line); cur_mon = old_mon; + qemu_mutex_lock(&hmp.out_lock); if (qstring_get_length(hmp.outbuf) > 0) { output = g_strdup(qstring_get_str(hmp.outbuf)); } else { output = g_strdup(""); } + qemu_mutex_unlock(&hmp.out_lock); out: monitor_data_destroy(&hmp); @@ -1045,10 +990,10 @@ CommandInfoList *qmp_query_commands(Error **errp) EventInfoList *qmp_query_events(Error **errp) { EventInfoList *info, *ev_list = NULL; - MonitorEvent e; + QAPIEvent e; - for (e = 0 ; e < QEVENT_MAX ; e++) { - const char *event_name = monitor_event_names[e]; + for (e = 0 ; e < QAPI_EVENT_MAX ; e++) { + const char *event_name = QAPIEvent_lookup[e]; assert(event_name != NULL); info = g_malloc0(sizeof(*info)); info->value = g_malloc0(sizeof(*info->value)); @@ -2968,7 +2913,7 @@ static mon_cmd_t info_cmds[] = { .name = "memdev", .args_type = "", .params = "", - .help = "show the memory device", + .help = "show memory backends", .mhandler.cmd = hmp_info_memdev, }, { @@ -5266,7 +5211,9 @@ static void monitor_event(void *opaque, int event) switch (event) { case CHR_EVENT_MUX_IN: + qemu_mutex_lock(&mon->out_lock); mon->mux_out = 0; + qemu_mutex_unlock(&mon->out_lock); if (mon->reset_seen) { readline_restart(mon->rs); monitor_resume(mon); @@ -5286,7 +5233,9 @@ static void monitor_event(void *opaque, int event) } else { mon->suspend_cnt++; } + qemu_mutex_lock(&mon->out_lock); mon->mux_out = 1; + qemu_mutex_unlock(&mon->out_lock); break; case CHR_EVENT_OPENED: @@ -5351,13 +5300,18 @@ static void monitor_readline_flush(void *opaque) monitor_flush(opaque); } +static void __attribute__((constructor)) monitor_lock_init(void) +{ + qemu_mutex_init(&monitor_lock); +} + void monitor_init(CharDriverState *chr, int flags) { static int is_first_init = 1; Monitor *mon; if (is_first_init) { - monitor_protocol_event_init(); + monitor_qapi_event_init(); sortcmdlist(); is_first_init = 0; } @@ -5388,7 +5342,10 @@ void monitor_init(CharDriverState *chr, int flags) monitor_event, mon); } + qemu_mutex_lock(&monitor_lock); QLIST_INSERT_HEAD(&mon_list, mon, entry); + qemu_mutex_unlock(&monitor_lock); + if (!default_mon || (flags & MONITOR_IS_DEFAULT)) default_mon = mon; } diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img index f6223e77c2..1c7f7640fc 100644 --- a/pc-bios/s390-ccw.img +++ b/pc-bios/s390-ccw.img Binary files differdiff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index 53a460df84..5ee3fcb38f 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -58,6 +58,52 @@ struct mbr { /* Scratch space */ static uint8_t sec[SECTOR_SIZE] __attribute__((__aligned__(SECTOR_SIZE))); +typedef struct ResetInfo { + uint32_t ipl_mask; + uint32_t ipl_addr; + uint32_t ipl_continue; +} ResetInfo; + +ResetInfo save; + +static void jump_to_IPL_2(void) +{ + ResetInfo *current = 0; + + void (*ipl)(void) = (void *) (uint64_t) current->ipl_continue; + debug_print_addr("set IPL addr to", ipl); + + /* Ensure the guest output starts fresh */ + sclp_print("\n"); + + *current = save; + ipl(); /* should not return */ +} + +static void jump_to_IPL_code(uint64_t address) +{ + /* + * The IPL PSW is at address 0. We also must not overwrite the + * content of non-BIOS memory after we loaded the guest, so we + * save the original content and restore it in jump_to_IPL_2. + */ + ResetInfo *current = 0; + + save = *current; + current->ipl_addr = (uint32_t) (uint64_t) &jump_to_IPL_2; + current->ipl_continue = address & 0x7fffffff; + + /* + * HACK ALERT. + * We use the load normal reset to keep r15 unchanged. jump_to_IPL_2 + * can then use r15 as its stack pointer. + */ + asm volatile("lghi 1,1\n\t" + "diag 1,1,0x308\n\t" + : : : "1", "memory"); + virtio_panic("\n! IPL returns !\n"); +} + /* Check for ZIPL magic. Returns 0 if not matched. */ static int zipl_magic(uint8_t *ptr) { @@ -72,10 +118,26 @@ static int zipl_magic(uint8_t *ptr) return 1; } +#define FREE_SPACE_FILLER '\xAA' + +static inline bool unused_space(const void *p, unsigned int size) +{ + int i; + const unsigned char *m = p; + + for (i = 0; i < size; i++) { + if (m[i] != FREE_SPACE_FILLER) { + return false; + } + } + return true; +} + static int zipl_load_segment(struct component_entry *entry) { const int max_entries = (SECTOR_SIZE / sizeof(struct scsi_blockptr)); struct scsi_blockptr *bprs = (void*)sec; + const int bprs_size = sizeof(sec); uint64_t blockno; long address; int i; @@ -87,6 +149,7 @@ static int zipl_load_segment(struct component_entry *entry) debug_print_int("addr", address); do { + memset(bprs, FREE_SPACE_FILLER, bprs_size); if (virtio_read(blockno, (uint8_t *)bprs)) { debug_print_int("failed reading bprs at", blockno); goto fail; @@ -104,6 +167,16 @@ static int zipl_load_segment(struct component_entry *entry) if (i == (max_entries - 1)) break; + if (bprs[i].blockct == 0 && unused_space(&bprs[i + 1], + sizeof(struct scsi_blockptr))) { + /* This is a "continue" pointer. + * This ptr is the last one in the current script section. + * I.e. the next ptr must point to the unused memory area. + * The blockno is not zero, so the upper loop must continue + * reading next section of BPRS. + */ + break; + } address = virtio_load_direct(cur_desc[0], cur_desc[1], 0, (void*)address); if (address == -1) @@ -123,7 +196,6 @@ static int zipl_run(struct scsi_blockptr *pte) { struct component_header *header; struct component_entry *entry; - void (*ipl)(void); uint8_t tmp_sec[SECTOR_SIZE]; virtio_read(pte->blockno, tmp_sec); @@ -157,14 +229,8 @@ static int zipl_run(struct scsi_blockptr *pte) goto fail; } - /* Ensure the guest output starts fresh */ - sclp_print("\n"); - - /* And run the OS! */ - ipl = (void*)(entry->load_address & 0x7fffffff); - debug_print_addr("set IPL addr to", ipl); /* should not return */ - ipl(); + jump_to_IPL_code(entry->load_address); return 0; diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c index a46914dd68..bbb3c4f36d 100644 --- a/pc-bios/s390-ccw/virtio.c +++ b/pc-bios/s390-ccw/virtio.c @@ -240,7 +240,7 @@ unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, { u8 status; int sec = rec_list1; - int sec_num = (((rec_list2 >> 32)+ 1) & 0xffff); + int sec_num = ((rec_list2 >> 32) & 0xffff) + 1; int sec_len = rec_list2 >> 48; ulong addr = (ulong)load_addr; diff --git a/qapi-event.json b/qapi-event.json new file mode 100644 index 0000000000..e7a47f9927 --- /dev/null +++ b/qapi-event.json @@ -0,0 +1,318 @@ +## +# @SHUTDOWN +# +# Emitted when the virtual machine has shutdown, possibly indicating that QEMU +# is about about to exit. +# +# Note: If the command-line option "-no-shutdown" has been specified, qemu will +# not exit, and a STOP event will eventually follow the SHUTDOWN event +# +# Since: 0.12.0 +## +{ 'event': 'SHUTDOWN' } + +## +# @POWERDOWN +# +# Emitted when the virtual machine is powered down through the power control +# system, such as via ACPI. +# +# Since: 0.12.0 +## +{ 'event': 'POWERDOWN' } + +## +# @RESET +# +# Emitted when the virtual machine is reset +# +# Since: 0.12.0 +## +{ 'event': 'RESET' } + +## +# @STOP +# +# Emitted when the virtual machine is stopped +# +# Since: 0.12.0 +## +{ 'event': 'STOP' } + +## +# @RESUME +# +# Emitted when the virtual machine resumes execution +# +# Since: 0.12.0 +## +{ 'event': 'RESUME' } + +## +# @SUSPEND +# +# Emitted when guest enters a hardware suspension state, for example, S3 state, +# which is sometimes called standby state +# +# Since: 1.1 +## +{ 'event': 'SUSPEND' } + +## +# @SUSPEND_DISK +# +# Emitted when guest enters a hardware suspension state with data saved on +# disk, for example, S4 state, which is sometimes called hibernate state +# +# Note: QEMU shuts down (similar to event @SHUTDOWN) when entering this state +# +# Since: 1.2 +## +{ 'event': 'SUSPEND_DISK' } + +## +# @WAKEUP +# +# Emitted when the guest has woken up from suspend state and is running +# +# Since: 1.1 +## +{ 'event': 'WAKEUP' } + +## +# @RTC_CHANGE +# +# Emitted when the guest changes the RTC time. +# +# @offset: offset between base RTC clock (as specified by -rtc base), and +# new RTC clock value +# +# Since: 0.13.0 +## +{ 'event': 'RTC_CHANGE', + 'data': { 'offset': 'int' } } + +## +# @WATCHDOG +# +# Emitted when the watchdog device's timer is expired +# +# @action: action that has been taken +# +# Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is +# followed respectively by the RESET, SHUTDOWN, or STOP events +# +# Since: 0.13.0 +## +{ 'event': 'WATCHDOG', + 'data': { 'action': 'WatchdogExpirationAction' } } + +## +# @DEVICE_DELETED +# +# Emitted whenever the device removal completion is acknowledged by the guest. +# At this point, it's safe to reuse the specified device ID. Device removal can +# be initiated by the guest or by HMP/QMP commands. +# +# @device: #optional, device name +# +# @path: device path +# +# Since: 1.5 +## +{ 'event': 'DEVICE_DELETED', + 'data': { '*device': 'str', 'path': 'str' } } + +## +# @NIC_RX_FILTER_CHANGED +# +# Emitted once until the 'query-rx-filter' command is executed, the first event +# will always be emitted +# +# @name: #optional, net client name +# +# @path: device path +# +# Since: 1.6 +## +{ 'event': 'NIC_RX_FILTER_CHANGED', + 'data': { '*name': 'str', 'path': 'str' } } + +## +# @VNC_CONNECTED +# +# Emitted when a VNC client establishes a connection +# +# @server: server information +# +# @client: client information +# +# Note: This event is emitted before any authentication takes place, thus +# the authentication ID is not provided +# +# Since: 0.13.0 +## +{ 'event': 'VNC_CONNECTED', + 'data': { 'server': 'VncServerInfo', + 'client': 'VncBasicInfo' } } + +## +# @VNC_INITIALIZED +# +# Emitted after authentication takes place (if any) and the VNC session is +# made active +# +# @server: server information +# +# @client: client information +# +# Since: 0.13.0 +## +{ 'event': 'VNC_INITIALIZED', + 'data': { 'server': 'VncServerInfo', + 'client': 'VncClientInfo' } } + +## +# @VNC_DISCONNECTED +# +# Emitted when the connection is closed +# +# @server: server information +# +# @client: client information +# +# Since: 0.13.0 +## +{ 'event': 'VNC_DISCONNECTED', + 'data': { 'server': 'VncServerInfo', + 'client': 'VncClientInfo' } } + +## +# @SPICE_CONNECTED +# +# Emitted when a SPICE client establishes a connection +# +# @server: server information +# +# @client: client information +# +# Since: 0.14.0 +## +{ 'event': 'SPICE_CONNECTED', + 'data': { 'server': 'SpiceBasicInfo', + 'client': 'SpiceBasicInfo' } } + +## +# @SPICE_INITIALIZED +# +# Emitted after initial handshake and authentication takes place (if any) +# and the SPICE channel is up and running +# +# @server: server information +# +# @client: client information +# +# Since: 0.14.0 +## +{ 'event': 'SPICE_INITIALIZED', + 'data': { 'server': 'SpiceServerInfo', + 'client': 'SpiceChannel' } } + +## +# @SPICE_DISCONNECTED +# +# Emitted when the SPICE connection is closed +# +# @server: server information +# +# @client: client information +# +# Since: 0.14.0 +## +{ 'event': 'SPICE_DISCONNECTED', + 'data': { 'server': 'SpiceBasicInfo', + 'client': 'SpiceBasicInfo' } } + +## +# @SPICE_MIGRATE_COMPLETED +# +# Emitted when SPICE migration has completed +# +# Since: 1.3 +## +{ 'event': 'SPICE_MIGRATE_COMPLETED' } + +## +# @ACPI_DEVICE_OST +# +# Emitted when guest executes ACPI _OST method. +# +# Since: 2.1 +# +# @info: ACPIOSTInfo type as described in qapi-schema.json +## +{ 'event': 'ACPI_DEVICE_OST', + 'data': { 'info': 'ACPIOSTInfo' } } + +## +# @BALLOON_CHANGE +# +# Emitted when the guest changes the actual BALLOON level. This value is +# equivalent to the @actual field return by the 'query-balloon' command +# +# @actual: actual level of the guest memory balloon in bytes +# +# Since: 1.2 +## +{ 'event': 'BALLOON_CHANGE', + 'data': { 'actual': 'int' } } + +## +# @GUEST_PANICKED +# +# Emitted when guest OS panic is detected +# +# @action: action that has been taken, currently always "pause" +# +# Since: 1.5 +## +{ 'event': 'GUEST_PANICKED', + 'data': { 'action': 'GuestPanicAction' } } + +## +# @QUORUM_FAILURE +# +# Emitted by the Quorum block driver if it fails to establish a quorum +# +# @reference: device name if defined else node name +# +# @sector-num: number of the first sector of the failed read operation +# +# @sector-count: failed read operation sector count +# +# Since: 2.0 +## +{ 'event': 'QUORUM_FAILURE', + 'data': { 'reference': 'str', 'sector-num': 'int', 'sector-count': 'int' } } + +## +# @QUORUM_REPORT_BAD +# +# Emitted to report a corruption of a Quorum file +# +# @error: #optional, error message. Only present on failure. This field +# contains a human-readable error message. There are no semantics other +# than that the block layer reported an error and clients should not +# try to interpret the error string. +# +# @node-name: the graph node name of the block driver state +# +# @sector-num: number of the first sector of the failed read operation +# +# @sector-count: failed read operation sector count +# +# Since: 2.0 +## +{ 'event': 'QUORUM_REPORT_BAD', + 'data': { '*error': 'str', 'node-name': 'str', + 'sector-num': 'int', 'sector-count': 'int' } } diff --git a/qapi-schema.json b/qapi-schema.json index 98350048f6..e7727a1153 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -630,21 +630,59 @@ { 'command': 'query-iothreads', 'returns': ['IOThreadInfo'] } ## -# @VncClientInfo: +# @NetworkAddressFamily # -# Information about a connected VNC client. +# The network address family +# +# @ipv4: IPV4 family +# +# @ipv6: IPV6 family +# +# @unix: unix socket # -# @host: The host name of the client. QEMU tries to resolve this to a DNS name -# when possible. +# @unknown: otherwise +# +# Since: 2.1 +## +{ 'enum': 'NetworkAddressFamily', + 'data': [ 'ipv4', 'ipv6', 'unix', 'unknown' ] } + +## +# @VncBasicInfo +# +# The basic information for vnc network connection +# +# @host: IP address +# +# @service: The service name of vnc port. This may depend on the host system's +# service database so symbolic names should not be relied on. +# +# @family: address family +# +# Since: 2.1 +## +{ 'type': 'VncBasicInfo', + 'data': { 'host': 'str', + 'service': 'str', + 'family': 'NetworkAddressFamily' } } + +## +# @VncServerInfo # -# @family: 'ipv6' if the client is connected via IPv6 and TCP -# 'ipv4' if the client is connected via IPv4 and TCP -# 'unix' if the client is connected via a unix domain socket -# 'unknown' otherwise +# The network connection information for server # -# @service: The service name of the client's port. This may depends on the -# host system's service database so symbolic names should not be -# relied on. +# @auth: #optional, authentication method +# +# Since: 2.1 +## +{ 'type': 'VncServerInfo', + 'base': 'VncBasicInfo', + 'data': { '*auth': 'str' } } + +## +# @VncClientInfo: +# +# Information about a connected VNC client. # # @x509_dname: #optional If x509 authentication is in use, the Distinguished # Name of the client. @@ -655,8 +693,8 @@ # Since: 0.14.0 ## { 'type': 'VncClientInfo', - 'data': {'host': 'str', 'family': 'str', 'service': 'str', - '*x509_dname': 'str', '*sasl_username': 'str'} } + 'base': 'VncBasicInfo', + 'data': { '*x509_dname' : 'str', '*sasl_username': 'str' } } ## # @VncInfo: @@ -695,7 +733,8 @@ # Since: 0.14.0 ## { 'type': 'VncInfo', - 'data': {'enabled': 'bool', '*host': 'str', '*family': 'str', + 'data': {'enabled': 'bool', '*host': 'str', + '*family': 'NetworkAddressFamily', '*service': 'str', '*auth': 'str', '*clients': ['VncClientInfo']} } ## @@ -710,19 +749,40 @@ { 'command': 'query-vnc', 'returns': 'VncInfo' } ## -# @SpiceChannel +# @SpiceBasicInfo # -# Information about a SPICE client channel. +# The basic information for SPICE network connection +# +# @host: IP address +# +# @port: port number +# +# @family: address family +# +# Since: 2.1 +## +{ 'type': 'SpiceBasicInfo', + 'data': { 'host': 'str', + 'port': 'str', + 'family': 'NetworkAddressFamily' } } + +## +# @SpiceServerInfo # -# @host: The host name of the client. QEMU tries to resolve this to a DNS name -# when possible. +# Information about a SPICE server # -# @family: 'ipv6' if the client is connected via IPv6 and TCP -# 'ipv4' if the client is connected via IPv4 and TCP -# 'unix' if the client is connected via a unix domain socket -# 'unknown' otherwise +# @auth: #optional, authentication method # -# @port: The client's port number. +# Since: 2.1 +## +{ 'type': 'SpiceServerInfo', + 'base': 'SpiceBasicInfo', + 'data': { '*auth': 'str' } } + +## +# @SpiceChannel +# +# Information about a SPICE client channel. # # @connection-id: SPICE connection id number. All channels with the same id # belong to the same SPICE session. @@ -740,8 +800,8 @@ # Since: 0.14.0 ## { 'type': 'SpiceChannel', - 'data': {'host': 'str', 'family': 'str', 'port': 'str', - 'connection-id': 'int', 'channel-type': 'int', 'channel-id': 'int', + 'base': 'SpiceBasicInfo', + 'data': {'connection-id': 'int', 'channel-type': 'int', 'channel-id': 'int', 'tls': 'bool'} } ## @@ -3158,19 +3218,19 @@ ## # @Memdev: # -# Information of memory device +# Information about memory backend # -# @size: memory device size +# @size: memory backend size # # @merge: enables or disables memory merge support # -# @dump: includes memory device's memory in a core dump or not +# @dump: includes memory backend's memory in a core dump or not # # @prealloc: enables or disables memory preallocation # # @host-nodes: host nodes for its memory policy # -# @policy: memory policy of memory device +# @policy: memory policy of memory backend # # Since: 2.1 ## @@ -3187,13 +3247,15 @@ ## # @query-memdev: # -# Returns information for all memory devices. +# Returns information for all memory backends. # # Returns: a list of @Memdev. # # Since: 2.1 ## { 'command': 'query-memdev', 'returns': ['Memdev'] } + +## # @PCDIMMDeviceInfo: # # PCDIMMDevice state information @@ -3286,3 +3348,55 @@ # Since: 2.1 ## { 'command': 'query-acpi-ospm-status', 'returns': ['ACPIOSTInfo'] } + +## +# @WatchdogExpirationAction +# +# An enumeration of the actions taken when the watchdog device's timer is +# expired +# +# @reset: system resets +# +# @shutdown: system shutdown, note that it is similar to @powerdown, which +# tries to set to system status and notify guest +# +# @poweroff: system poweroff, the emulator program exits +# +# @pause: system pauses, similar to @stop +# +# @debug: system enters debug state +# +# @none: nothing is done +# +# Since: 2.1 +## +{ 'enum': 'WatchdogExpirationAction', + 'data': [ 'reset', 'shutdown', 'poweroff', 'pause', 'debug', 'none' ] } + +## +# @IoOperationType +# +# An enumeration of the I/O operation types +# +# @read: read operation +# +# @write: write operation +# +# Since: 2.1 +## +{ 'enum': 'IoOperationType', + 'data': [ 'read', 'write' ] } + +## +# @GuestPanicAction +# +# An enumeration of the actions taken when guest OS panic is detected +# +# @pause: system pauses +# +# Since: 2.1 +## +{ 'enum': 'GuestPanicAction', + 'data': [ 'pause' ] } + +{ 'include': 'qapi-event.json' } diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs index 1f9c97342c..d14b769cff 100644 --- a/qapi/Makefile.objs +++ b/qapi/Makefile.objs @@ -3,3 +3,4 @@ util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o util-obj-y += string-input-visitor.o string-output-visitor.o util-obj-y += opts-visitor.o +util-obj-y += qmp-event.o diff --git a/qapi/block-core.json b/qapi/block-core.json index 7215e48130..af6b436540 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1410,3 +1410,153 @@ ## { 'command': 'blockdev-add', 'data': { 'options': 'BlockdevOptions' } } + +## +# @BlockErrorAction +# +# An enumeration of action that has been taken when a DISK I/O occurs +# +# @ignore: error has been ignored +# +# @report: error has been reported to the device +# +# @stop: error caused VM to be stopped +# +# Since: 2.1 +## +{ 'enum': 'BlockErrorAction', + 'data': [ 'ignore', 'report', 'stop' ] } + + +## +# @BLOCK_IMAGE_CORRUPTED +# +# Emitted when a disk image is being marked corrupt +# +# @device: device name +# +# @msg: informative message for human consumption, such as the kind of +# corruption being detected +# +# @offset: #optional, if the corruption resulted from an image access, this is +# the access offset into the image +# +# @size: #optional, if the corruption resulted from an image access, this is +# the access size +# +# Since: 1.7 +## +{ 'event': 'BLOCK_IMAGE_CORRUPTED', + 'data': { 'device' : 'str', + 'msg' : 'str', + '*offset': 'int', + '*size' : 'int' } } + +## +# @BLOCK_IO_ERROR +# +# Emitted when a disk I/O error occurs +# +# @device: device name +# +# @operation: I/O operation +# +# @action: action that has been taken +# +# Note: If action is "stop", a STOP event will eventually follow the +# BLOCK_IO_ERROR event +# +# Since: 0.13.0 +## +{ 'event': 'BLOCK_IO_ERROR', + 'data': { 'device': 'str', 'operation': 'IoOperationType', + 'action': 'BlockErrorAction' } } + +## +# @BLOCK_JOB_COMPLETED +# +# Emitted when a block job has completed +# +# @type: job type +# +# @device: device name +# +# @len: maximum progress value +# +# @offset: current progress value. On success this is equal to len. +# On failure this is less than len +# +# @speed: rate limit, bytes per second +# +# @error: #optional, error message. Only present on failure. This field +# contains a human-readable error message. There are no semantics +# other than that streaming has failed and clients should not try to +# interpret the error string +# +# Since: 1.1 +## +{ 'event': 'BLOCK_JOB_COMPLETED', + 'data': { 'type' : 'BlockJobType', + 'device': 'str', + 'len' : 'int', + 'offset': 'int', + 'speed' : 'int', + '*error': 'str' } } + +## +# @BLOCK_JOB_CANCELLED +# +# Emitted when a block job has been cancelled +# +# @type: job type +# +# @device: device name +# +# @len: maximum progress value +# +# @offset: current progress value. On success this is equal to len. +# On failure this is less than len +# +# @speed: rate limit, bytes per second +# +# Since: 1.1 +## +{ 'event': 'BLOCK_JOB_CANCELLED', + 'data': { 'type' : 'BlockJobType', + 'device': 'str', + 'len' : 'int', + 'offset': 'int', + 'speed' : 'int' } } + +## +# @BLOCK_JOB_ERROR +# +# Emitted when a block job encounters an error +# +# @device: device name +# +# @operation: I/O operation +# +# @action: action that has been taken +# +# Since: 1.3 +## +{ 'event': 'BLOCK_JOB_ERROR', + 'data': { 'device' : 'str', + 'operation': 'IoOperationType', + 'action' : 'BlockdevOnError' } } + +## +# @BLOCK_JOB_READY +# +# Emitted when a block job is ready to complete +# +# @device: device name +# +# Note: The "ready to complete" status is always reset by a @BLOCK_JOB_ERROR +# event +# +# Since: 1.3 +## +{ 'event': 'BLOCK_JOB_READY', + 'data': { 'device': 'str' } } diff --git a/qapi/block.json b/qapi/block.json index 61c463ab05..e3134657b6 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -164,3 +164,17 @@ ## { 'command': 'nbd-server-stop' } +## +# @DEVICE_TRAY_MOVED +# +# Emitted whenever the tray of a removable device is moved by the guest or by +# HMP/QMP commands +# +# @device: device name +# +# @tray-open: true if the tray has been opened or false if it has been closed +# +# Since: 1.1 +## +{ 'event': 'DEVICE_TRAY_MOVED', + 'data': { 'device': 'str', 'tray-open': 'bool' } } diff --git a/qapi/qmp-event.c b/qapi/qmp-event.c new file mode 100644 index 0000000000..0d1ce0bd18 --- /dev/null +++ b/qapi/qmp-event.c @@ -0,0 +1,74 @@ +/* + * QMP Event related + * + * Copyright (c) 2014 Wenchao Xia + * + * Authors: + * Wenchao Xia <wenchaoqemu@gmail.com> + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include <inttypes.h> + +#include "qemu-common.h" +#include "qapi/qmp-event.h" +#include "qapi/qmp/qstring.h" +#include "qapi/qmp/qjson.h" + +#ifdef _WIN32 +#include "sysemu/os-win32.h" +#endif + +#ifdef CONFIG_POSIX +#include "sysemu/os-posix.h" +#endif + +static QMPEventFuncEmit qmp_emit; + +void qmp_event_set_func_emit(QMPEventFuncEmit emit) +{ + qmp_emit = emit; +} + +QMPEventFuncEmit qmp_event_get_func_emit(void) +{ + return qmp_emit; +} + +static void timestamp_put(QDict *qdict) +{ + int err; + QObject *obj; + qemu_timeval tv; + int64_t sec, usec; + + err = qemu_gettimeofday(&tv); + if (err < 0) { + /* Put -1 to indicate failure of getting host time */ + sec = -1; + usec = -1; + } else { + sec = tv.tv_sec; + usec = tv.tv_usec; + } + + obj = qobject_from_jsonf("{ 'seconds': %" PRId64 ", " + "'microseconds': %" PRId64 " }", + sec, usec); + qdict_put_obj(qdict, "timestamp", obj); +} + +/* + * Build a QDict, then fill event name and time stamp, caller should free the + * QDict after usage. + */ +QDict *qmp_event_build_dict(const char *event_name) +{ + QDict *dict = qdict_new(); + qdict_put(dict, "event", qstring_from_str(event_name)); + timestamp_put(dict); + return dict; +} diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c index e9aca3bfdc..1ab8574585 100644 --- a/qapi/string-output-visitor.c +++ b/qapi/string-output-visitor.c @@ -98,7 +98,7 @@ static void format_string(StringOutputVisitor *sov, Range *r, bool next, { if (r->end - r->begin > 1) { if (human) { - g_string_append_printf(sov->string, "0x%" PRIx64 "-%" PRIx64, + g_string_append_printf(sov->string, "0x%" PRIx64 "-0x%" PRIx64, r->begin, r->end - 1); } else { diff --git a/qemu-char.c b/qemu-char.c index e4eb985b57..2e50a1093e 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -91,6 +91,12 @@ static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs = QTAILQ_HEAD_INITIALIZER(chardevs); +CharDriverState *qemu_chr_alloc(void) +{ + CharDriverState *chr = g_malloc0(sizeof(CharDriverState)); + return chr; +} + void qemu_chr_be_event(CharDriverState *s, int event) { /* Keep track if the char device is open */ @@ -115,7 +121,12 @@ void qemu_chr_be_generic_open(CharDriverState *s) int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len) { - return s->chr_write(s, buf, len); + int ret; + + qemu_mutex_lock(&s->chr_write_lock); + ret = s->chr_write(s, buf, len); + qemu_mutex_unlock(&s->chr_write_lock); + return ret; } int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len) @@ -123,6 +134,7 @@ int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len) int offset = 0; int res; + qemu_mutex_lock(&s->chr_write_lock); while (offset < len) { do { res = s->chr_write(s, buf + offset, len - offset); @@ -131,17 +143,17 @@ int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len) } } while (res == -1 && errno == EAGAIN); - if (res == 0) { + if (res <= 0) { break; } - if (res < 0) { - return res; - } - offset += res; } + qemu_mutex_unlock(&s->chr_write_lock); + if (res < 0) { + return res; + } return offset; } @@ -204,7 +216,7 @@ void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len) int qemu_chr_fe_get_msgfd(CharDriverState *s) { int fd; - return (qemu_chr_fe_get_msgfds(s, &fd, 1) >= 0) ? fd : -1; + return (qemu_chr_fe_get_msgfds(s, &fd, 1) == 1) ? fd : -1; } int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, int len) @@ -282,7 +294,7 @@ static CharDriverState *qemu_chr_open_null(void) { CharDriverState *chr; - chr = g_malloc0(sizeof(CharDriverState)); + chr = qemu_chr_alloc(); chr->chr_write = null_chr_write; chr->explicit_be_open = true; return chr; @@ -309,17 +321,20 @@ typedef struct { int prod[MAX_MUX]; int cons[MAX_MUX]; int timestamps; + + /* Protected by the CharDriverState chr_write_lock. */ int linestart; int64_t timestamps_start; } MuxDriver; +/* Called with chr_write_lock held. */ static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { MuxDriver *d = chr->opaque; int ret; if (!d->timestamps) { - ret = d->drv->chr_write(d->drv, buf, len); + ret = qemu_chr_fe_write(d->drv, buf, len); } else { int i; @@ -341,10 +356,10 @@ static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len) (secs / 60) % 60, secs % 60, (int)(ti % 1000)); - d->drv->chr_write(d->drv, (uint8_t *)buf1, strlen(buf1)); + qemu_chr_fe_write(d->drv, (uint8_t *)buf1, strlen(buf1)); d->linestart = 0; } - ret += d->drv->chr_write(d->drv, buf+i, 1); + ret += qemu_chr_fe_write(d->drv, buf+i, 1); if (buf[i] == '\n') { d->linestart = 1; } @@ -379,13 +394,13 @@ static void mux_print_help(CharDriverState *chr) "\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r", term_escape_char); } - chr->chr_write(chr, (uint8_t *)cbuf, strlen(cbuf)); + qemu_chr_fe_write(chr, (uint8_t *)cbuf, strlen(cbuf)); for (i = 0; mux_help[i] != NULL; i++) { for (j=0; mux_help[i][j] != '\0'; j++) { if (mux_help[i][j] == '%') - chr->chr_write(chr, (uint8_t *)ebuf, strlen(ebuf)); + qemu_chr_fe_write(chr, (uint8_t *)ebuf, strlen(ebuf)); else - chr->chr_write(chr, (uint8_t *)&mux_help[i][j], 1); + qemu_chr_fe_write(chr, (uint8_t *)&mux_help[i][j], 1); } } } @@ -410,7 +425,7 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) case 'x': { const char *term = "QEMU: Terminated\n\r"; - chr->chr_write(chr,(uint8_t *)term,strlen(term)); + qemu_chr_fe_write(chr, (uint8_t *)term, strlen(term)); exit(0); break; } @@ -570,7 +585,7 @@ static CharDriverState *qemu_chr_open_mux(CharDriverState *drv) CharDriverState *chr; MuxDriver *d; - chr = g_malloc0(sizeof(CharDriverState)); + chr = qemu_chr_alloc(); d = g_malloc0(sizeof(MuxDriver)); chr->opaque = d; @@ -859,6 +874,7 @@ typedef struct FDCharDriver { QTAILQ_ENTRY(FDCharDriver) node; } FDCharDriver; +/* Called with chr_write_lock held. */ static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { FDCharDriver *s = chr->opaque; @@ -945,7 +961,7 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out) CharDriverState *chr; FDCharDriver *s; - chr = g_malloc0(sizeof(CharDriverState)); + chr = qemu_chr_alloc(); s = g_malloc0(sizeof(FDCharDriver)); s->fd_in = io_channel_from_fd(fd_in); s->fd_out = io_channel_from_fd(fd_out); @@ -1058,12 +1074,14 @@ static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) typedef struct { GIOChannel *fd; - int connected; int read_bytes; + + /* Protected by the CharDriverState chr_write_lock. */ + int connected; guint timer_tag; } PtyCharDriver; -static void pty_chr_update_read_handler(CharDriverState *chr); +static void pty_chr_update_read_handler_locked(CharDriverState *chr); static void pty_chr_state(CharDriverState *chr, int connected); static gboolean pty_chr_timer(gpointer opaque) @@ -1071,14 +1089,17 @@ static gboolean pty_chr_timer(gpointer opaque) struct CharDriverState *chr = opaque; PtyCharDriver *s = chr->opaque; + qemu_mutex_lock(&chr->chr_write_lock); s->timer_tag = 0; if (!s->connected) { /* Next poll ... */ - pty_chr_update_read_handler(chr); + pty_chr_update_read_handler_locked(chr); } + qemu_mutex_unlock(&chr->chr_write_lock); return FALSE; } +/* Called with chr_write_lock held. */ static void pty_chr_rearm_timer(CharDriverState *chr, int ms) { PtyCharDriver *s = chr->opaque; @@ -1095,13 +1116,38 @@ static void pty_chr_rearm_timer(CharDriverState *chr, int ms) } } +/* Called with chr_write_lock held. */ +static void pty_chr_update_read_handler_locked(CharDriverState *chr) +{ + PtyCharDriver *s = chr->opaque; + GPollFD pfd; + + pfd.fd = g_io_channel_unix_get_fd(s->fd); + pfd.events = G_IO_OUT; + pfd.revents = 0; + g_poll(&pfd, 1, 0); + if (pfd.revents & G_IO_HUP) { + pty_chr_state(chr, 0); + } else { + pty_chr_state(chr, 1); + } +} + +static void pty_chr_update_read_handler(CharDriverState *chr) +{ + qemu_mutex_lock(&chr->chr_write_lock); + pty_chr_update_read_handler_locked(chr); + qemu_mutex_unlock(&chr->chr_write_lock); +} + +/* Called with chr_write_lock held. */ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { PtyCharDriver *s = chr->opaque; if (!s->connected) { /* guest sends data, check for (re-)connect */ - pty_chr_update_read_handler(chr); + pty_chr_update_read_handler_locked(chr); return 0; } return io_channel_send(s->fd, buf, len); @@ -1147,22 +1193,7 @@ static gboolean pty_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) return TRUE; } -static void pty_chr_update_read_handler(CharDriverState *chr) -{ - PtyCharDriver *s = chr->opaque; - GPollFD pfd; - - pfd.fd = g_io_channel_unix_get_fd(s->fd); - pfd.events = G_IO_OUT; - pfd.revents = 0; - g_poll(&pfd, 1, 0); - if (pfd.revents & G_IO_HUP) { - pty_chr_state(chr, 0); - } else { - pty_chr_state(chr, 1); - } -} - +/* Called with chr_write_lock held. */ static void pty_chr_state(CharDriverState *chr, int connected) { PtyCharDriver *s = chr->opaque; @@ -1222,7 +1253,7 @@ static CharDriverState *qemu_chr_open_pty(const char *id, close(slave_fd); - chr = g_malloc0(sizeof(CharDriverState)); + chr = qemu_chr_alloc(); chr->filename = g_strdup_printf("pty:%s", pty_name); ret->pty = g_strdup(pty_name); @@ -1584,7 +1615,7 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd) drv->fd = fd; drv->mode = IEEE1284_MODE_COMPAT; - chr = g_malloc0(sizeof(CharDriverState)); + chr = qemu_chr_alloc(); chr->chr_write = null_chr_write; chr->chr_ioctl = pp_ioctl; chr->chr_close = pp_close; @@ -1639,7 +1670,7 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd) { CharDriverState *chr; - chr = g_malloc0(sizeof(CharDriverState)); + chr = qemu_chr_alloc(); chr->opaque = (void *)(intptr_t)fd; chr->chr_write = null_chr_write; chr->chr_ioctl = pp_ioctl; @@ -1653,9 +1684,12 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd) typedef struct { int max_size; HANDLE hcom, hrecv, hsend; - OVERLAPPED orecv, osend; + OVERLAPPED orecv; BOOL fpipe; DWORD len; + + /* Protected by the CharDriverState chr_write_lock. */ + OVERLAPPED osend; } WinCharState; typedef struct { @@ -1765,6 +1799,7 @@ static int win_chr_init(CharDriverState *chr, const char *filename) return -1; } +/* Called with chr_write_lock held. */ static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1) { WinCharState *s = chr->opaque; @@ -1863,7 +1898,7 @@ static CharDriverState *qemu_chr_open_win_path(const char *filename) CharDriverState *chr; WinCharState *s; - chr = g_malloc0(sizeof(CharDriverState)); + chr = qemu_chr_alloc(); s = g_malloc0(sizeof(WinCharState)); chr->opaque = s; chr->chr_write = win_chr_write; @@ -1962,7 +1997,7 @@ static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts) CharDriverState *chr; WinCharState *s; - chr = g_malloc0(sizeof(CharDriverState)); + chr = qemu_chr_alloc(); s = g_malloc0(sizeof(WinCharState)); chr->opaque = s; chr->chr_write = win_chr_write; @@ -1981,7 +2016,7 @@ static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out) CharDriverState *chr; WinCharState *s; - chr = g_malloc0(sizeof(CharDriverState)); + chr = qemu_chr_alloc(); s = g_malloc0(sizeof(WinCharState)); s->hcom = fd_out; chr->opaque = s; @@ -2137,7 +2172,7 @@ static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) DWORD dwMode; int is_console = 0; - chr = g_malloc0(sizeof(CharDriverState)); + chr = qemu_chr_alloc(); stdio = g_malloc0(sizeof(WinStdioCharState)); stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE); @@ -2207,6 +2242,7 @@ typedef struct { int max_size; } NetCharDriver; +/* Called with chr_write_lock held. */ static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { NetCharDriver *s = chr->opaque; @@ -2299,7 +2335,7 @@ static CharDriverState *qemu_chr_open_udp_fd(int fd) CharDriverState *chr = NULL; NetCharDriver *s = NULL; - chr = g_malloc0(sizeof(CharDriverState)); + chr = qemu_chr_alloc(); s = g_malloc0(sizeof(NetCharDriver)); s->fd = fd; @@ -2397,6 +2433,7 @@ static int unix_send_msgfds(CharDriverState *chr, const uint8_t *buf, int len) } #endif +/* Called with chr_write_lock held. */ static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { TCPCharDriver *s = chr->opaque; @@ -2481,8 +2518,15 @@ static int tcp_get_msgfds(CharDriverState *chr, int *fds, int num) int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num; if (to_copy) { + int i; + memcpy(fds, s->read_msgfds, to_copy * sizeof(int)); + /* Close unused fds */ + for (i = to_copy; i < s->read_msgfds_num; i++) { + close(s->read_msgfds[i]); + } + g_free(s->read_msgfds); s->read_msgfds = 0; s->read_msgfds_num = 0; @@ -2850,7 +2894,7 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, return NULL; } - chr = g_malloc0(sizeof(CharDriverState)); + chr = qemu_chr_alloc(); s = g_malloc0(sizeof(TCPCharDriver)); s->connected = 0; @@ -2994,6 +3038,7 @@ static size_t ringbuf_count(const CharDriverState *chr) return d->prod - d->cons; } +/* Called with chr_write_lock held. */ static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { RingBufCharDriver *d = chr->opaque; @@ -3018,9 +3063,11 @@ static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len) RingBufCharDriver *d = chr->opaque; int i; + qemu_mutex_lock(&chr->chr_write_lock); for (i = 0; i < len && d->cons != d->prod; i++) { buf[i] = d->cbuf[d->cons++ & (d->size - 1)]; } + qemu_mutex_unlock(&chr->chr_write_lock); return i; } @@ -3040,7 +3087,7 @@ static CharDriverState *qemu_chr_open_ringbuf(ChardevRingbuf *opts, CharDriverState *chr; RingBufCharDriver *d; - chr = g_malloc0(sizeof(CharDriverState)); + chr = qemu_chr_alloc(); d = g_malloc(sizeof(*d)); d->size = opts->has_size ? opts->size : 65536; diff --git a/qobject/json-lexer.c b/qobject/json-lexer.c index 440df60392..b19623e229 100644 --- a/qobject/json-lexer.c +++ b/qobject/json-lexer.c @@ -138,8 +138,8 @@ static const uint8_t json_lexer[][256] = { ['n'] = IN_SQ_STRING, ['r'] = IN_SQ_STRING, ['t'] = IN_SQ_STRING, - ['/'] = IN_DQ_STRING, - ['\\'] = IN_DQ_STRING, + ['/'] = IN_SQ_STRING, + ['\\'] = IN_SQ_STRING, ['\''] = IN_SQ_STRING, ['\"'] = IN_SQ_STRING, ['u'] = IN_SQ_UCODE0, diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index 7d93d01ed2..053ba85b5f 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -29,9 +29,7 @@ def type_visitor(name): def generate_command_decl(name, args, ret_type): arglist="" for argname, argtype, optional, structured in parse_args(args): - argtype = c_type(argtype) - if argtype == "char *": - argtype = "const char *" + argtype = c_type(argtype, is_param=True) if optional: arglist += "bool has_%s, " % c_var(argname) arglist += "%s %s, " % (argtype, c_var(argname)) @@ -104,7 +102,7 @@ def gen_visitor_input_vars_decl(args): bool has_%(argname)s = false; ''', argname=c_var(argname)) - if c_type(argtype).endswith("*"): + if is_c_ptr(argtype): ret += mcgen(''' %(argtype)s %(argname)s = NULL; ''', @@ -229,7 +227,7 @@ def gen_marshal_input(name, args, ret_type, middle_mode): ''') if ret_type: - if c_type(ret_type).endswith("*"): + if is_c_ptr(ret_type): retval = " %s retval = NULL;" % c_type(ret_type) else: retval = " %s retval;" % c_type(ret_type) diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py new file mode 100644 index 0000000000..3a1cd61914 --- /dev/null +++ b/scripts/qapi-event.py @@ -0,0 +1,369 @@ +# +# QAPI event generator +# +# Copyright (c) 2014 Wenchao Xia +# +# Authors: +# Wenchao Xia <wenchaoqemu@gmail.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. + +from ordereddict import OrderedDict +from qapi import * +import sys +import os +import getopt +import errno + +def _generate_event_api_name(event_name, params): + api_name = "void qapi_event_send_%s(" % c_fun(event_name).lower(); + l = len(api_name) + + if params: + for argname, argentry, optional, structured in parse_args(params): + if optional: + api_name += "bool has_%s,\n" % c_var(argname) + api_name += "".ljust(l) + + if argentry == "str": + api_name += "const " + api_name += "%s %s,\n" % (c_type(argentry), c_var(argname)) + api_name += "".ljust(l) + + api_name += "Error **errp)" + return api_name; + + +# Following are the core functions that generate C APIs to emit event. + +def generate_event_declaration(api_name): + return mcgen(''' + +%(api_name)s; +''', + api_name = api_name) + +def generate_event_implement(api_name, event_name, params): + # step 1: declare any variables + ret = mcgen(""" + +%(api_name)s +{ + QDict *qmp; + Error *local_err = NULL; + QMPEventFuncEmit emit; +""", + api_name = api_name) + + if params: + ret += mcgen(""" + QmpOutputVisitor *qov; + Visitor *v; + QObject *obj; + +""") + + # step 2: check emit function, create a dict + ret += mcgen(""" + emit = qmp_event_get_func_emit(); + if (!emit) { + return; + } + + qmp = qmp_event_build_dict("%(event_name)s"); + +""", + event_name = event_name) + + # step 3: visit the params if params != None + if params: + ret += mcgen(""" + qov = qmp_output_visitor_new(); + g_assert(qov); + + v = qmp_output_get_visitor(qov); + g_assert(v); + + /* Fake visit, as if all members are under a structure */ + visit_start_struct(v, NULL, "", "%(event_name)s", 0, &local_err); + if (local_err) { + goto clean; + } + +""", + event_name = event_name) + + for argname, argentry, optional, structured in parse_args(params): + if optional: + ret += mcgen(""" + if (has_%(var)s) { +""", + var = c_var(argname)) + push_indent() + + if argentry == "str": + var_type = "(char **)" + else: + var_type = "" + + ret += mcgen(""" + visit_type_%(type)s(v, %(var_type)s&%(var)s, "%(name)s", &local_err); + if (local_err) { + goto clean; + } +""", + var_type = var_type, + var = c_var(argname), + type = type_name(argentry), + name = argname) + + if optional: + pop_indent() + ret += mcgen(""" + } +""") + + ret += mcgen(""" + + visit_end_struct(v, &local_err); + if (local_err) { + goto clean; + } + + obj = qmp_output_get_qobject(qov); + g_assert(obj != NULL); + + qdict_put_obj(qmp, "data", obj); +""") + + # step 4: call qmp event api + ret += mcgen(""" + emit(%(event_enum_value)s, qmp, &local_err); + +""", + event_enum_value = event_enum_value) + + # step 5: clean up + if params: + ret += mcgen(""" + clean: + qmp_output_visitor_cleanup(qov); +""") + ret += mcgen(""" + error_propagate(errp, local_err); + QDECREF(qmp); +} +""") + + return ret + + +# Following are the functions that generate an enum type for all defined +# events, similar to qapi-types.py. Here we already have enum name and +# values which were generated before and recorded in event_enum_*. It also +# works around the issue that "import qapi-types" can't work. + +def generate_event_enum_decl(event_enum_name, event_enum_values): + lookup_decl = mcgen(''' + +extern const char *%(event_enum_name)s_lookup[]; +''', + event_enum_name = event_enum_name) + + enum_decl = mcgen(''' +typedef enum %(event_enum_name)s +{ +''', + event_enum_name = event_enum_name) + + # append automatically generated _MAX value + enum_max_value = generate_enum_full_value(event_enum_name, "MAX") + enum_values = event_enum_values + [ enum_max_value ] + + i = 0 + for value in enum_values: + enum_decl += mcgen(''' + %(value)s = %(i)d, +''', + value = value, + i = i) + i += 1 + + enum_decl += mcgen(''' +} %(event_enum_name)s; +''', + event_enum_name = event_enum_name) + + return lookup_decl + enum_decl + +def generate_event_enum_lookup(event_enum_name, event_enum_strings): + ret = mcgen(''' + +const char *%(event_enum_name)s_lookup[] = { +''', + event_enum_name = event_enum_name) + + i = 0 + for string in event_enum_strings: + ret += mcgen(''' + "%(string)s", +''', + string = string) + + ret += mcgen(''' + NULL, +}; +''') + return ret + + +# Start the real job + +try: + opts, args = getopt.gnu_getopt(sys.argv[1:], "chbp:i:o:", + ["source", "header", "builtins", "prefix=", + "input-file=", "output-dir="]) +except getopt.GetoptError, err: + print str(err) + sys.exit(1) + +input_file = "" +output_dir = "" +prefix = "" +c_file = 'qapi-event.c' +h_file = 'qapi-event.h' + +do_c = False +do_h = False +do_builtins = False + +for o, a in opts: + if o in ("-p", "--prefix"): + prefix = a + elif o in ("-i", "--input-file"): + input_file = a + elif o in ("-o", "--output-dir"): + output_dir = a + "/" + elif o in ("-c", "--source"): + do_c = True + elif o in ("-h", "--header"): + do_h = True + elif o in ("-b", "--builtins"): + do_builtins = True + +if not do_c and not do_h: + do_c = True + do_h = True + +c_file = output_dir + prefix + c_file +h_file = output_dir + prefix + h_file + +try: + os.makedirs(output_dir) +except os.error, e: + if e.errno != errno.EEXIST: + raise + +def maybe_open(really, name, opt): + if really: + return open(name, opt) + else: + import StringIO + return StringIO.StringIO() + +fdef = maybe_open(do_c, c_file, 'w') +fdecl = maybe_open(do_h, h_file, 'w') + +fdef.write(mcgen(''' +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* + * schema-defined QAPI event functions + * + * Copyright (c) 2014 Wenchao Xia + * + * Authors: + * Wenchao Xia <wenchaoqemu@gmail.com> + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu-common.h" +#include "%(header)s" +#include "%(prefix)sqapi-visit.h" +#include "qapi/qmp-output-visitor.h" +#include "qapi/qmp-event.h" + +''', + prefix=prefix, header=basename(h_file))) + +fdecl.write(mcgen(''' +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* + * schema-defined QAPI event functions + * + * Copyright (c) 2014 Wenchao Xia + * + * Authors: + * Wenchao Xia <wenchaoqemu@gmail.com> + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef %(guard)s +#define %(guard)s + +#include "qapi/error.h" +#include "qapi/qmp/qdict.h" +#include "%(prefix)sqapi-types.h" + +''', + prefix=prefix, guard=guardname(h_file))) + +exprs = parse_schema(input_file) + +event_enum_name = prefix.upper().replace('-', '_') + "QAPIEvent" +event_enum_values = [] +event_enum_strings = [] + +for expr in exprs: + if expr.has_key('event'): + event_name = expr['event'] + params = expr.get('data') + if params and len(params) == 0: + params = None + + api_name = _generate_event_api_name(event_name, params) + ret = generate_event_declaration(api_name) + fdecl.write(ret) + + # We need an enum value per event + event_enum_value = generate_enum_full_value(event_enum_name, + event_name) + ret = generate_event_implement(api_name, event_name, params) + fdef.write(ret) + + # Record it, and generate enum later + event_enum_values.append(event_enum_value) + event_enum_strings.append(event_name) + +ret = generate_event_enum_decl(event_enum_name, event_enum_values) +fdecl.write(ret) +ret = generate_event_enum_lookup(event_enum_name, event_enum_strings) +fdef.write(ret) + +fdecl.write(''' +#endif +''') + +fdecl.flush() +fdecl.close() + +fdef.flush() +fdef.close() diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 06a79f1631..c12969721a 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -77,7 +77,7 @@ static void visit_type_%(full_name)s_field_%(c_name)s(Visitor *m, %(name)s **obj ret += mcgen(''' -static void visit_type_%(full_name)s_fields(Visitor *m, %(name)s ** obj, Error **errp) +static void visit_type_%(full_name)s_fields(Visitor *m, %(name)s **obj, Error **errp) { Error *err = NULL; ''', @@ -186,7 +186,7 @@ def generate_visit_struct(expr): ret += mcgen(''' -void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp) +void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp) { ''', name=name) @@ -201,7 +201,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** def generate_visit_list(name, members): return mcgen(''' -void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp) +void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp) { Error *err = NULL; GenericList *i, **prev; @@ -230,7 +230,7 @@ out: def generate_visit_enum(name, members): return mcgen(''' -void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **errp) +void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp) { visit_type_enum(m, (int *)obj, %(name)s_lookup, "%(name)s", name, errp); } @@ -240,7 +240,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **e def generate_visit_anon_union(name, members): ret = mcgen(''' -void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp) +void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp) { Error *err = NULL; @@ -327,7 +327,7 @@ def generate_visit_union(expr): ret += mcgen(''' -void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp) +void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp) { Error *err = NULL; @@ -399,13 +399,13 @@ def generate_declaration(name, members, genlist=True, builtin_type=False): if not builtin_type: ret += mcgen(''' -void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp); +void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp); ''', name=name) if genlist: ret += mcgen(''' -void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp); +void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp); ''', name=name) @@ -415,7 +415,7 @@ def generate_enum_declaration(name, members, genlist=True): ret = "" if genlist: ret += mcgen(''' -void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp); +void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp); ''', name=name) @@ -424,7 +424,7 @@ void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, def generate_decl_enum(name, members, genlist=True): return mcgen(''' -void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **errp); +void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp); ''', name=name) diff --git a/scripts/qapi.py b/scripts/qapi.py index 86e96089af..54b97cb48e 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -248,6 +248,16 @@ def discriminator_find_enum_define(expr): return find_enum(discriminator_type) +def check_event(expr, expr_info): + params = expr.get('data') + if params: + for argname, argentry, optional, structured in parse_args(params): + if structured: + raise QAPIExprError(expr_info, + "Nested structure define in event is not " + "supported now, event '%s', argname '%s'" + % (expr['event'], argname)) + def check_union(expr, expr_info): name = expr['union'] base = expr.get('base') @@ -311,6 +321,8 @@ def check_exprs(schema): expr = expr_elem['expr'] if expr.has_key('union'): check_union(expr, expr_elem['info']) + if expr.has_key('event'): + check_event(expr, expr_elem['info']) def parse_schema(input_file): try: @@ -470,9 +482,17 @@ def find_enum(name): def is_enum(name): return find_enum(name) != None -def c_type(name): +eatspace = '\033EATSPACE.' + +# A special suffix is added in c_type() for pointer types, and it's +# stripped in mcgen(). So please notice this when you check the return +# value of c_type() outside mcgen(). +def c_type(name, is_param=False): if name == 'str': - return 'char *' + if is_param: + return 'const char *' + eatspace + return 'char *' + eatspace + elif name == 'int': return 'int64_t' elif (name == 'int8' or name == 'int16' or name == 'int32' or @@ -486,15 +506,19 @@ def c_type(name): elif name == 'number': return 'double' elif type(name) == list: - return '%s *' % c_list_type(name[0]) + return '%s *%s' % (c_list_type(name[0]), eatspace) elif is_enum(name): return name elif name == None or len(name) == 0: return 'void' elif name == name.upper(): - return '%sEvent *' % camel_case(name) + return '%sEvent *%s' % (camel_case(name), eatspace) else: - return '%s *' % name + return '%s *%s' % (name, eatspace) + +def is_c_ptr(name): + suffix = "*" + eatspace + return c_type(name).endswith(suffix) def genindent(count): ret = "" @@ -519,7 +543,8 @@ def cgen(code, **kwds): return '\n'.join(lines) % kwds + '\n' def mcgen(code, **kwds): - return cgen('\n'.join(code.split('\n')[1:-1]), **kwds) + raw = cgen('\n'.join(code.split('\n')[1:-1]), **kwds) + return re.sub(re.escape(eatspace) + ' *', '', raw) def basename(filename): return filename.split("/")[-1] diff --git a/spice-qemu-char.c b/spice-qemu-char.c index 6624559952..4518a4d97d 100644 --- a/spice-qemu-char.c +++ b/spice-qemu-char.c @@ -268,7 +268,7 @@ static CharDriverState *chr_open(const char *subtype, CharDriverState *chr; SpiceCharDriver *s; - chr = g_malloc0(sizeof(CharDriverState)); + chr = qemu_chr_alloc(); s = g_malloc0(sizeof(SpiceCharDriver)); s->chr = chr; s->active = false; diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index 997d68d5b9..528e1617b3 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -1,5 +1,6 @@ stub-obj-y += arch-query-cpu-def.o stub-obj-y += bdrv-commit-all.o +stub-obj-y += chr-baum-init.o stub-obj-y += chr-msmouse.o stub-obj-y += clock-warp.o stub-obj-y += cpu-get-clock.o @@ -19,11 +20,11 @@ stub-obj-y += machine-init-done.o stub-obj-y += migr-blocker.o stub-obj-y += mon-is-qmp.o stub-obj-y += mon-printf.o -stub-obj-y += mon-protocol-event.o stub-obj-y += mon-set-error.o stub-obj-y += monitor-init.o stub-obj-y += notify-event.o stub-obj-y += pci-drive-hot-add.o +stub-obj-$(CONFIG_SPICE) += qemu-chr-open-spice.o stub-obj-y += qtest.o stub-obj-y += reset.o stub-obj-y += runstate-check.o diff --git a/stubs/chr-baum-init.c b/stubs/chr-baum-init.c new file mode 100644 index 0000000000..f5cc6ce1f8 --- /dev/null +++ b/stubs/chr-baum-init.c @@ -0,0 +1,7 @@ +#include "qemu-common.h" +#include "sysemu/char.h" + +CharDriverState *chr_baum_init(void) +{ + return NULL; +} diff --git a/stubs/mon-protocol-event.c b/stubs/mon-protocol-event.c deleted file mode 100644 index 0946e94724..0000000000 --- a/stubs/mon-protocol-event.c +++ /dev/null @@ -1,6 +0,0 @@ -#include "qemu-common.h" -#include "monitor/monitor.h" - -void monitor_protocol_event(MonitorEvent event, QObject *data) -{ -} diff --git a/stubs/qemu-chr-open-spice.c b/stubs/qemu-chr-open-spice.c new file mode 100644 index 0000000000..f1c4849d9c --- /dev/null +++ b/stubs/qemu-chr-open-spice.c @@ -0,0 +1,14 @@ +#include "qemu-common.h" +#include "ui/qemu-spice.h" + +CharDriverState *qemu_chr_open_spice_vmc(const char *type) +{ + return NULL; +} + +#if SPICE_SERVER_VERSION >= 0x000c02 +CharDriverState *qemu_chr_open_spice_port(const char *name) +{ + return NULL; +} +#endif diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index a1a4cc2cab..a6e587b589 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -39,6 +39,7 @@ #include "monitor/monitor.h" #include "exec/gdbstub.h" #include "trace.h" +#include "qapi-event.h" /* #define DEBUG_KVM */ @@ -1029,12 +1030,8 @@ static bool is_special_wait_psw(CPUState *cs) static void guest_panicked(void) { - QObject *data; - - data = qobject_from_jsonf("{ 'action': %s }", "pause"); - monitor_protocol_event(QEVENT_GUEST_PANICKED, data); - qobject_decref(data); - + qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, + &error_abort); vm_stop(RUN_STATE_GUEST_PANICKED); } diff --git a/tcg/ppc/tcg-target.c b/tcg/ppc/tcg-target.c index 436b65bffb..c83fd9fdc0 100644 --- a/tcg/ppc/tcg-target.c +++ b/tcg/ppc/tcg-target.c @@ -24,27 +24,71 @@ #include "tcg-be-ldst.h" -static tcg_insn_unit *tb_ret_addr; - #if defined _CALL_DARWIN || defined __APPLE__ #define TCG_TARGET_CALL_DARWIN #endif +#ifdef _CALL_SYSV +# define TCG_TARGET_CALL_ALIGN_ARGS 1 +#endif -#ifdef TCG_TARGET_CALL_DARWIN -#define LINKAGE_AREA_SIZE 24 -#define LR_OFFSET 8 -#elif defined _CALL_AIX -#define LINKAGE_AREA_SIZE 52 -#define LR_OFFSET 8 +/* For some memory operations, we need a scratch that isn't R0. For the AIX + calling convention, we can re-use the TOC register since we'll be reloading + it at every call. Otherwise R12 will do nicely as neither a call-saved + register nor a parameter register. */ +#ifdef _CALL_AIX +# define TCG_REG_TMP1 TCG_REG_R2 #else -#define LINKAGE_AREA_SIZE 8 -#define LR_OFFSET 4 +# define TCG_REG_TMP1 TCG_REG_R12 #endif +/* For the 64-bit target, we don't like the 5 insn sequence needed to build + full 64-bit addresses. Better to have a base register to which we can + apply a 32-bit displacement. + + There are generally three items of interest: + (1) helper functions in the main executable, + (2) TranslationBlock data structures, + (3) the return address in the epilogue. + + For user-only, we USE_STATIC_CODE_GEN_BUFFER, so the code_gen_buffer + will be inside the main executable, and thus near enough to make a + pointer to the epilogue be within 2GB of all helper functions. + + For softmmu, we'll let the kernel choose the address of code_gen_buffer, + and odds are it'll be somewhere close to the main malloc arena, and so + a pointer to the epilogue will be within 2GB of the TranslationBlocks. + + For --enable-pie, everything will be kinda near everything else, + somewhere in high memory. + + Thus we choose to keep the return address in a call-saved register. */ +#define TCG_REG_RA TCG_REG_R31 +#define USE_REG_RA (TCG_TARGET_REG_BITS == 64) + +/* Shorthand for size of a pointer. Avoid promotion to unsigned. */ +#define SZP ((int)sizeof(void *)) + +/* Shorthand for size of a register. */ +#define SZR (TCG_TARGET_REG_BITS / 8) + +#define TCG_CT_CONST_S16 0x100 +#define TCG_CT_CONST_U16 0x200 +#define TCG_CT_CONST_S32 0x400 +#define TCG_CT_CONST_U32 0x800 +#define TCG_CT_CONST_ZERO 0x1000 +#define TCG_CT_CONST_MONE 0x2000 + +static tcg_insn_unit *tb_ret_addr; + #ifndef GUEST_BASE #define GUEST_BASE 0 #endif +#include "elf.h" +static bool have_isa_2_06; +#define HAVE_ISA_2_06 have_isa_2_06 +#define HAVE_ISEL have_isa_2_06 + #ifdef CONFIG_USE_GUEST_BASE #define TCG_GUEST_BASE_REG 30 #else @@ -89,7 +133,7 @@ static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { #endif static const int tcg_target_reg_alloc_order[] = { - TCG_REG_R14, + TCG_REG_R14, /* call saved registers */ TCG_REG_R15, TCG_REG_R16, TCG_REG_R17, @@ -99,32 +143,26 @@ static const int tcg_target_reg_alloc_order[] = { TCG_REG_R21, TCG_REG_R22, TCG_REG_R23, + TCG_REG_R24, + TCG_REG_R25, + TCG_REG_R26, + TCG_REG_R27, TCG_REG_R28, TCG_REG_R29, TCG_REG_R30, TCG_REG_R31, -#ifdef TCG_TARGET_CALL_DARWIN - TCG_REG_R2, -#endif - TCG_REG_R3, - TCG_REG_R4, - TCG_REG_R5, - TCG_REG_R6, - TCG_REG_R7, - TCG_REG_R8, - TCG_REG_R9, - TCG_REG_R10, -#ifndef TCG_TARGET_CALL_DARWIN + TCG_REG_R12, /* call clobbered, non-arguments */ TCG_REG_R11, -#endif - TCG_REG_R12, -#ifndef _CALL_SYSV + TCG_REG_R2, TCG_REG_R13, -#endif - TCG_REG_R24, - TCG_REG_R25, - TCG_REG_R26, - TCG_REG_R27 + TCG_REG_R10, /* call clobbered, arguments */ + TCG_REG_R9, + TCG_REG_R8, + TCG_REG_R7, + TCG_REG_R6, + TCG_REG_R5, + TCG_REG_R4, + TCG_REG_R3, }; static const int tcg_target_call_iarg_regs[] = { @@ -138,7 +176,7 @@ static const int tcg_target_call_iarg_regs[] = { TCG_REG_R10 }; -static const int tcg_target_call_oarg_regs[2] = { +static const int tcg_target_call_oarg_regs[] = { TCG_REG_R3, TCG_REG_R4 }; @@ -146,10 +184,6 @@ static const int tcg_target_call_oarg_regs[2] = { static const int tcg_target_callee_save_regs[] = { #ifdef TCG_TARGET_CALL_DARWIN TCG_REG_R11, - TCG_REG_R13, -#endif -#ifdef _CALL_AIX - TCG_REG_R13, #endif TCG_REG_R14, TCG_REG_R15, @@ -173,7 +207,7 @@ static const int tcg_target_callee_save_regs[] = { static inline bool in_range_b(tcg_target_long target) { - return target == sextract32(target, 0, 26); + return target == sextract64(target, 0, 26); } static uint32_t reloc_pc24_val(tcg_insn_unit *pc, tcg_insn_unit *target) @@ -200,6 +234,18 @@ static void reloc_pc14(tcg_insn_unit *pc, tcg_insn_unit *target) *pc = (*pc & ~0xfffc) | reloc_pc14_val(pc, target); } +static inline void tcg_out_b_noaddr(TCGContext *s, int insn) +{ + unsigned retrans = *s->code_ptr & 0x3fffffc; + tcg_out32(s, insn | retrans); +} + +static inline void tcg_out_bc_noaddr(TCGContext *s, int insn) +{ + unsigned retrans = *s->code_ptr & 0xfffc; + tcg_out32(s, insn | retrans); +} + static void patch_reloc(tcg_insn_unit *code_ptr, int type, intptr_t value, intptr_t addend) { @@ -233,59 +279,43 @@ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) ct->ct |= TCG_CT_REG; tcg_regset_set32(ct->u.regs, 0, 0xffffffff); break; -#ifdef CONFIG_SOFTMMU case 'L': /* qemu_ld constraint */ ct->ct |= TCG_CT_REG; tcg_regset_set32(ct->u.regs, 0, 0xffffffff); tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3); +#ifdef CONFIG_SOFTMMU tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4); tcg_regset_reset_reg(ct->u.regs, TCG_REG_R5); -#if TARGET_LONG_BITS == 64 - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R6); -#ifdef TCG_TARGET_CALL_ALIGN_ARGS - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R7); -#endif #endif break; - case 'K': /* qemu_st[8..32] constraint */ + case 'S': /* qemu_st constraint */ ct->ct |= TCG_CT_REG; tcg_regset_set32(ct->u.regs, 0, 0xffffffff); tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3); +#ifdef CONFIG_SOFTMMU tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4); tcg_regset_reset_reg(ct->u.regs, TCG_REG_R5); tcg_regset_reset_reg(ct->u.regs, TCG_REG_R6); -#if TARGET_LONG_BITS == 64 - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R7); -#ifdef TCG_TARGET_CALL_ALIGN_ARGS - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R8); -#endif #endif break; - case 'M': /* qemu_st64 constraint */ - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xffffffff); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R5); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R6); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R7); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R8); -#ifdef TCG_TARGET_CALL_ALIGN_ARGS - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R9); -#endif + case 'I': + ct->ct |= TCG_CT_CONST_S16; break; -#else - case 'L': - case 'K': - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xffffffff); + case 'J': + ct->ct |= TCG_CT_CONST_U16; break; case 'M': - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xffffffff); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3); + ct->ct |= TCG_CT_CONST_MONE; + break; + case 'T': + ct->ct |= TCG_CT_CONST_S32; + break; + case 'U': + ct->ct |= TCG_CT_CONST_U32; + break; + case 'Z': + ct->ct |= TCG_CT_CONST_ZERO; break; -#endif default: return -1; } @@ -298,48 +328,86 @@ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) static int tcg_target_const_match(tcg_target_long val, TCGType type, const TCGArgConstraint *arg_ct) { - int ct; + int ct = arg_ct->ct; + if (ct & TCG_CT_CONST) { + return 1; + } + + /* The only 32-bit constraint we use aside from + TCG_CT_CONST is TCG_CT_CONST_S16. */ + if (type == TCG_TYPE_I32) { + val = (int32_t)val; + } - ct = arg_ct->ct; - if (ct & TCG_CT_CONST) + if ((ct & TCG_CT_CONST_S16) && val == (int16_t)val) { + return 1; + } else if ((ct & TCG_CT_CONST_U16) && val == (uint16_t)val) { + return 1; + } else if ((ct & TCG_CT_CONST_S32) && val == (int32_t)val) { + return 1; + } else if ((ct & TCG_CT_CONST_U32) && val == (uint32_t)val) { + return 1; + } else if ((ct & TCG_CT_CONST_ZERO) && val == 0) { return 1; + } else if ((ct & TCG_CT_CONST_MONE) && val == -1) { + return 1; + } return 0; } #define OPCD(opc) ((opc)<<26) -#define XO31(opc) (OPCD(31)|((opc)<<1)) #define XO19(opc) (OPCD(19)|((opc)<<1)) - -#define B OPCD(18) -#define BC OPCD(16) -#define LBZ OPCD(34) -#define LHZ OPCD(40) -#define LHA OPCD(42) -#define LWZ OPCD(32) -#define STB OPCD(38) -#define STH OPCD(44) -#define STW OPCD(36) - -#define ADDIC OPCD(12) -#define ADDI OPCD(14) -#define ADDIS OPCD(15) -#define ORI OPCD(24) -#define ORIS OPCD(25) -#define XORI OPCD(26) -#define XORIS OPCD(27) -#define ANDI OPCD(28) -#define ANDIS OPCD(29) -#define MULLI OPCD( 7) -#define CMPLI OPCD(10) -#define CMPI OPCD(11) +#define MD30(opc) (OPCD(30)|((opc)<<2)) +#define MDS30(opc) (OPCD(30)|((opc)<<1)) +#define XO31(opc) (OPCD(31)|((opc)<<1)) +#define XO58(opc) (OPCD(58)|(opc)) +#define XO62(opc) (OPCD(62)|(opc)) + +#define B OPCD( 18) +#define BC OPCD( 16) +#define LBZ OPCD( 34) +#define LHZ OPCD( 40) +#define LHA OPCD( 42) +#define LWZ OPCD( 32) +#define STB OPCD( 38) +#define STH OPCD( 44) +#define STW OPCD( 36) + +#define STD XO62( 0) +#define STDU XO62( 1) +#define STDX XO31(149) + +#define LD XO58( 0) +#define LDX XO31( 21) +#define LDU XO58( 1) +#define LWA XO58( 2) +#define LWAX XO31(341) + +#define ADDIC OPCD( 12) +#define ADDI OPCD( 14) +#define ADDIS OPCD( 15) +#define ORI OPCD( 24) +#define ORIS OPCD( 25) +#define XORI OPCD( 26) +#define XORIS OPCD( 27) +#define ANDI OPCD( 28) +#define ANDIS OPCD( 29) +#define MULLI OPCD( 7) +#define CMPLI OPCD( 10) +#define CMPI OPCD( 11) #define SUBFIC OPCD( 8) -#define LWZU OPCD(33) -#define STWU OPCD(37) +#define LWZU OPCD( 33) +#define STWU OPCD( 37) + +#define RLWIMI OPCD( 20) +#define RLWINM OPCD( 21) +#define RLWNM OPCD( 23) -#define RLWIMI OPCD(20) -#define RLWINM OPCD(21) -#define RLWNM OPCD(23) +#define RLDICL MD30( 0) +#define RLDICR MD30( 1) +#define RLDIMI MD30( 3) +#define RLDCL MDS30( 8) #define BCLR XO19( 16) #define BCCTR XO19(528) @@ -351,16 +419,22 @@ static int tcg_target_const_match(tcg_target_long val, TCGType type, #define EXTSB XO31(954) #define EXTSH XO31(922) +#define EXTSW XO31(986) #define ADD XO31(266) #define ADDE XO31(138) +#define ADDME XO31(234) +#define ADDZE XO31(202) #define ADDC XO31( 10) #define AND XO31( 28) #define SUBF XO31( 40) #define SUBFC XO31( 8) #define SUBFE XO31(136) +#define SUBFME XO31(232) +#define SUBFZE XO31(200) #define OR XO31(444) #define XOR XO31(316) #define MULLW XO31(235) +#define MULHW XO31( 75) #define MULHWU XO31( 11) #define DIVW XO31(491) #define DIVWU XO31(459) @@ -368,21 +442,31 @@ static int tcg_target_const_match(tcg_target_long val, TCGType type, #define CMPL XO31( 32) #define LHBRX XO31(790) #define LWBRX XO31(534) +#define LDBRX XO31(532) #define STHBRX XO31(918) #define STWBRX XO31(662) +#define STDBRX XO31(660) #define MFSPR XO31(339) #define MTSPR XO31(467) #define SRAWI XO31(824) #define NEG XO31(104) #define MFCR XO31( 19) -#define CNTLZW XO31( 26) +#define MFOCRF (MFCR | (1u << 20)) #define NOR XO31(124) +#define CNTLZW XO31( 26) +#define CNTLZD XO31( 58) #define ANDC XO31( 60) #define ORC XO31(412) #define EQV XO31(284) #define NAND XO31(476) #define ISEL XO31( 15) +#define MULLD XO31(233) +#define MULHD XO31( 73) +#define MULHDU XO31( 9) +#define DIVD XO31(489) +#define DIVDU XO31(457) + #define LBZX XO31( 87) #define LHZX XO31(279) #define LHAX XO31(343) @@ -391,7 +475,7 @@ static int tcg_target_const_match(tcg_target_long val, TCGType type, #define STHX XO31(407) #define STWX XO31(151) -#define SPR(a,b) ((((a)<<5)|(b))<<11) +#define SPR(a, b) ((((a)<<5)|(b))<<11) #define LR SPR(8, 0) #define CTR SPR(9, 0) @@ -399,8 +483,15 @@ static int tcg_target_const_match(tcg_target_long val, TCGType type, #define SRW XO31(536) #define SRAW XO31(792) -#define TW XO31(4) -#define TRAP (TW | TO (31)) +#define SLD XO31( 27) +#define SRD XO31(539) +#define SRAD XO31(794) +#define SRADI XO31(413<<1) + +#define TW XO31( 4) +#define TRAP (TW | TO(31)) + +#define NOP ORI /* ori 0,0,0 */ #define RT(r) ((r)<<21) #define RS(r) ((r)<<21) @@ -411,21 +502,26 @@ static int tcg_target_const_match(tcg_target_long val, TCGType type, #define MB(b) ((b)<<6) #define ME(e) ((e)<<1) #define BO(o) ((o)<<21) +#define MB64(b) ((b)<<5) +#define FXM(b) (1 << (19 - (b))) #define LK 1 -#define TAB(t,a,b) (RT(t) | RA(a) | RB(b)) -#define SAB(s,a,b) (RS(s) | RA(a) | RB(b)) +#define TAB(t, a, b) (RT(t) | RA(a) | RB(b)) +#define SAB(s, a, b) (RS(s) | RA(a) | RB(b)) +#define TAI(s, a, i) (RT(s) | RA(a) | ((i) & 0xffff)) +#define SAI(s, a, i) (RS(s) | RA(a) | ((i) & 0xffff)) #define BF(n) ((n)<<23) #define BI(n, c) (((c)+((n)*4))<<16) #define BT(n, c) (((c)+((n)*4))<<21) #define BA(n, c) (((c)+((n)*4))<<16) #define BB(n, c) (((c)+((n)*4))<<11) +#define BC_(n, c) (((c)+((n)*4))<<6) -#define BO_COND_TRUE BO (12) -#define BO_COND_FALSE BO (4) -#define BO_ALWAYS BO (20) +#define BO_COND_TRUE BO(12) +#define BO_COND_FALSE BO( 4) +#define BO_ALWAYS BO(20) enum { CR_LT, @@ -435,557 +531,348 @@ enum { }; static const uint32_t tcg_to_bc[] = { - [TCG_COND_EQ] = BC | BI (7, CR_EQ) | BO_COND_TRUE, - [TCG_COND_NE] = BC | BI (7, CR_EQ) | BO_COND_FALSE, - [TCG_COND_LT] = BC | BI (7, CR_LT) | BO_COND_TRUE, - [TCG_COND_GE] = BC | BI (7, CR_LT) | BO_COND_FALSE, - [TCG_COND_LE] = BC | BI (7, CR_GT) | BO_COND_FALSE, - [TCG_COND_GT] = BC | BI (7, CR_GT) | BO_COND_TRUE, - [TCG_COND_LTU] = BC | BI (7, CR_LT) | BO_COND_TRUE, - [TCG_COND_GEU] = BC | BI (7, CR_LT) | BO_COND_FALSE, - [TCG_COND_LEU] = BC | BI (7, CR_GT) | BO_COND_FALSE, - [TCG_COND_GTU] = BC | BI (7, CR_GT) | BO_COND_TRUE, + [TCG_COND_EQ] = BC | BI(7, CR_EQ) | BO_COND_TRUE, + [TCG_COND_NE] = BC | BI(7, CR_EQ) | BO_COND_FALSE, + [TCG_COND_LT] = BC | BI(7, CR_LT) | BO_COND_TRUE, + [TCG_COND_GE] = BC | BI(7, CR_LT) | BO_COND_FALSE, + [TCG_COND_LE] = BC | BI(7, CR_GT) | BO_COND_FALSE, + [TCG_COND_GT] = BC | BI(7, CR_GT) | BO_COND_TRUE, + [TCG_COND_LTU] = BC | BI(7, CR_LT) | BO_COND_TRUE, + [TCG_COND_GEU] = BC | BI(7, CR_LT) | BO_COND_FALSE, + [TCG_COND_LEU] = BC | BI(7, CR_GT) | BO_COND_FALSE, + [TCG_COND_GTU] = BC | BI(7, CR_GT) | BO_COND_TRUE, }; +/* The low bit here is set if the RA and RB fields must be inverted. */ +static const uint32_t tcg_to_isel[] = { + [TCG_COND_EQ] = ISEL | BC_(7, CR_EQ), + [TCG_COND_NE] = ISEL | BC_(7, CR_EQ) | 1, + [TCG_COND_LT] = ISEL | BC_(7, CR_LT), + [TCG_COND_GE] = ISEL | BC_(7, CR_LT) | 1, + [TCG_COND_LE] = ISEL | BC_(7, CR_GT) | 1, + [TCG_COND_GT] = ISEL | BC_(7, CR_GT), + [TCG_COND_LTU] = ISEL | BC_(7, CR_LT), + [TCG_COND_GEU] = ISEL | BC_(7, CR_LT) | 1, + [TCG_COND_LEU] = ISEL | BC_(7, CR_GT) | 1, + [TCG_COND_GTU] = ISEL | BC_(7, CR_GT), +}; + +static void tcg_out_mem_long(TCGContext *s, int opi, int opx, TCGReg rt, + TCGReg base, tcg_target_long offset); + static void tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) { + tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); if (ret != arg) { tcg_out32(s, OR | SAB(arg, ret, arg)); } } -static void tcg_out_movi(TCGContext *s, TCGType type, - TCGReg ret, tcg_target_long arg) +static inline void tcg_out_rld(TCGContext *s, int op, TCGReg ra, TCGReg rs, + int sh, int mb) { - if (arg == (int16_t) arg) - tcg_out32 (s, ADDI | RT (ret) | RA (0) | (arg & 0xffff)); - else { - tcg_out32 (s, ADDIS | RT (ret) | RA (0) | ((arg >> 16) & 0xffff)); - if (arg & 0xffff) - tcg_out32 (s, ORI | RS (ret) | RA (ret) | (arg & 0xffff)); - } + assert(TCG_TARGET_REG_BITS == 64); + sh = SH(sh & 0x1f) | (((sh >> 5) & 1) << 1); + mb = MB64((mb >> 5) | ((mb << 1) & 0x3f)); + tcg_out32(s, op | RA(ra) | RS(rs) | sh | mb); } -static void tcg_out_ldst (TCGContext *s, int ret, int addr, - int offset, int op1, int op2) +static inline void tcg_out_rlw(TCGContext *s, int op, TCGReg ra, TCGReg rs, + int sh, int mb, int me) { - if (offset == (int16_t) offset) - tcg_out32 (s, op1 | RT (ret) | RA (addr) | (offset & 0xffff)); - else { - tcg_out_movi (s, TCG_TYPE_I32, 0, offset); - tcg_out32 (s, op2 | RT (ret) | RA (addr) | RB (0)); - } + tcg_out32(s, op | RA(ra) | RS(rs) | SH(sh) | MB(mb) | ME(me)); } -static void tcg_out_b(TCGContext *s, int mask, tcg_insn_unit *target) +static inline void tcg_out_ext32u(TCGContext *s, TCGReg dst, TCGReg src) { - ptrdiff_t disp = tcg_pcrel_diff(s, target); - if (in_range_b(disp)) { - tcg_out32(s, B | (disp & 0x3fffffc) | mask); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R0, (uintptr_t)target); - tcg_out32(s, MTSPR | RS(TCG_REG_R0) | CTR); - tcg_out32(s, BCCTR | BO_ALWAYS | mask); - } + tcg_out_rld(s, RLDICL, dst, src, 0, 32); } -static void tcg_out_call1(TCGContext *s, tcg_insn_unit *target, int lk) +static inline void tcg_out_shli32(TCGContext *s, TCGReg dst, TCGReg src, int c) { -#ifdef _CALL_AIX - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, (uintptr_t)target); - tcg_out32(s, LWZ | RT(TCG_REG_R0) | RA(reg)); - tcg_out32(s, MTSPR | RA(TCG_REG_R0) | CTR); - tcg_out32(s, LWZ | RT(TCG_REG_R2) | RA(reg) | 4); - tcg_out32(s, BCCTR | BO_ALWAYS | lk); -#else - tcg_out_b(s, lk, target); -#endif + tcg_out_rlw(s, RLWINM, dst, src, c, 0, 31 - c); } -static void tcg_out_call(TCGContext *s, tcg_insn_unit *target) +static inline void tcg_out_shli64(TCGContext *s, TCGReg dst, TCGReg src, int c) { - tcg_out_call1(s, target, LK); + tcg_out_rld(s, RLDICR, dst, src, c, 63 - c); } -#if defined(CONFIG_SOFTMMU) - -static void add_qemu_ldst_label (TCGContext *s, - bool is_ld, - TCGMemOp opc, - int data_reg, - int data_reg2, - int addrlo_reg, - int addrhi_reg, - int mem_index, - tcg_insn_unit *raddr, - tcg_insn_unit *label_ptr) +static inline void tcg_out_shri32(TCGContext *s, TCGReg dst, TCGReg src, int c) { - TCGLabelQemuLdst *label = new_ldst_label(s); - - label->is_ld = is_ld; - label->opc = opc; - label->datalo_reg = data_reg; - label->datahi_reg = data_reg2; - label->addrlo_reg = addrlo_reg; - label->addrhi_reg = addrhi_reg; - label->mem_index = mem_index; - label->raddr = raddr; - label->label_ptr[0] = label_ptr; + tcg_out_rlw(s, RLWINM, dst, src, 32 - c, c, 31); } -/* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, - * int mmu_idx, uintptr_t ra) - */ -static void * const qemu_ld_helpers[16] = { - [MO_UB] = helper_ret_ldub_mmu, - [MO_LEUW] = helper_le_lduw_mmu, - [MO_LEUL] = helper_le_ldul_mmu, - [MO_LEQ] = helper_le_ldq_mmu, - [MO_BEUW] = helper_be_lduw_mmu, - [MO_BEUL] = helper_be_ldul_mmu, - [MO_BEQ] = helper_be_ldq_mmu, -}; - -/* helper signature: helper_ret_st_mmu(CPUState *env, target_ulong addr, - * uintxx_t val, int mmu_idx, uintptr_t ra) - */ -static void * const qemu_st_helpers[16] = { - [MO_UB] = helper_ret_stb_mmu, - [MO_LEUW] = helper_le_stw_mmu, - [MO_LEUL] = helper_le_stl_mmu, - [MO_LEQ] = helper_le_stq_mmu, - [MO_BEUW] = helper_be_stw_mmu, - [MO_BEUL] = helper_be_stl_mmu, - [MO_BEQ] = helper_be_stq_mmu, -}; - -static tcg_insn_unit *ld_trampolines[16]; -static tcg_insn_unit *st_trampolines[16]; - -/* Perform the TLB load and compare. Branches to the slow path, placing the - address of the branch in *LABEL_PTR. Loads the addend of the TLB into R0. - Clobbers R1 and R2. */ - -static void tcg_out_tlb_check(TCGContext *s, TCGReg r0, TCGReg r1, TCGReg r2, - TCGReg addrlo, TCGReg addrhi, TCGMemOp s_bits, - int mem_index, int is_load, - tcg_insn_unit **label_ptr) +static inline void tcg_out_shri64(TCGContext *s, TCGReg dst, TCGReg src, int c) { - int cmp_off = - (is_load - ? offsetof(CPUArchState, tlb_table[mem_index][0].addr_read) - : offsetof(CPUArchState, tlb_table[mem_index][0].addr_write)); - int add_off = offsetof(CPUArchState, tlb_table[mem_index][0].addend); - tcg_insn_unit retranst; - TCGReg base = TCG_AREG0; - - /* Extract the page index, shifted into place for tlb index. */ - tcg_out32(s, (RLWINM - | RA(r0) - | RS(addrlo) - | SH(32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS)) - | MB(32 - (CPU_TLB_BITS + CPU_TLB_ENTRY_BITS)) - | ME(31 - CPU_TLB_ENTRY_BITS))); - - /* Compensate for very large offsets. */ - if (add_off >= 0x8000) { - /* Most target env are smaller than 32k; none are larger than 64k. - Simplify the logic here merely to offset by 0x7ff0, giving us a - range just shy of 64k. Check this assumption. */ - QEMU_BUILD_BUG_ON(offsetof(CPUArchState, - tlb_table[NB_MMU_MODES - 1][1]) - > 0x7ff0 + 0x7fff); - tcg_out32(s, ADDI | RT(r1) | RA(base) | 0x7ff0); - base = r1; - cmp_off -= 0x7ff0; - add_off -= 0x7ff0; - } - - /* Clear the non-page, non-alignment bits from the address. */ - tcg_out32(s, (RLWINM - | RA(r2) - | RS(addrlo) - | SH(0) - | MB((32 - s_bits) & 31) - | ME(31 - TARGET_PAGE_BITS))); - - tcg_out32(s, ADD | RT(r0) | RA(r0) | RB(base)); - base = r0; - - /* Load the tlb comparator. */ - tcg_out32(s, LWZ | RT(r1) | RA(base) | (cmp_off & 0xffff)); - - tcg_out32(s, CMP | BF(7) | RA(r2) | RB(r1)); - - if (TARGET_LONG_BITS == 64) { - tcg_out32(s, LWZ | RT(r1) | RA(base) | ((cmp_off + 4) & 0xffff)); - } - - /* Load the tlb addend for use on the fast path. - Do this asap to minimize load delay. */ - tcg_out32(s, LWZ | RT(r0) | RA(base) | (add_off & 0xffff)); + tcg_out_rld(s, RLDICL, dst, src, 64 - c, c); +} - if (TARGET_LONG_BITS == 64) { - tcg_out32(s, CMP | BF(6) | RA(addrhi) | RB(r1)); - tcg_out32(s, CRAND | BT(7, CR_EQ) | BA(6, CR_EQ) | BB(7, CR_EQ)); +static void tcg_out_movi32(TCGContext *s, TCGReg ret, int32_t arg) +{ + if (arg == (int16_t) arg) { + tcg_out32(s, ADDI | TAI(ret, 0, arg)); + } else { + tcg_out32(s, ADDIS | TAI(ret, 0, arg >> 16)); + if (arg & 0xffff) { + tcg_out32(s, ORI | SAI(ret, ret, arg)); + } } - - /* Use a conditional branch-and-link so that we load a pointer to - somewhere within the current opcode, for passing on to the helper. - This address cannot be used for a tail call, but it's shorter - than forming an address from scratch. */ - *label_ptr = s->code_ptr; - retranst = *s->code_ptr & 0xfffc; - tcg_out32(s, BC | BI(7, CR_EQ) | retranst | BO_COND_FALSE | LK); } -#endif -static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is64) +static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg ret, + tcg_target_long arg) { - TCGReg addrlo, datalo, datahi, rbase, addrhi __attribute__((unused)); - TCGMemOp opc, bswap; -#ifdef CONFIG_SOFTMMU - int mem_index; - tcg_insn_unit *label_ptr; -#endif - - datalo = *args++; - datahi = (is64 ? *args++ : 0); - addrlo = *args++; - addrhi = (TARGET_LONG_BITS == 64 ? *args++ : 0); - opc = *args++; - bswap = opc & MO_BSWAP; + tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); + if (type == TCG_TYPE_I32 || arg == (int32_t)arg) { + tcg_out_movi32(s, ret, arg); + } else if (arg == (uint32_t)arg && !(arg & 0x8000)) { + tcg_out32(s, ADDI | TAI(ret, 0, arg)); + tcg_out32(s, ORIS | SAI(ret, ret, arg >> 16)); + } else { + int32_t high; -#ifdef CONFIG_SOFTMMU - mem_index = *args; - tcg_out_tlb_check(s, TCG_REG_R3, TCG_REG_R4, TCG_REG_R0, addrlo, - addrhi, opc & MO_SIZE, mem_index, 0, &label_ptr); - rbase = TCG_REG_R3; -#else /* !CONFIG_SOFTMMU */ - rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0; -#endif + if (USE_REG_RA) { + intptr_t diff = arg - (intptr_t)tb_ret_addr; + if (diff == (int32_t)diff) { + tcg_out_mem_long(s, ADDI, ADD, ret, TCG_REG_RA, diff); + return; + } + } - switch (opc & MO_SSIZE) { - default: - case MO_UB: - tcg_out32(s, LBZX | TAB(datalo, rbase, addrlo)); - break; - case MO_SB: - tcg_out32(s, LBZX | TAB(datalo, rbase, addrlo)); - tcg_out32(s, EXTSB | RA(datalo) | RS(datalo)); - break; - case MO_UW: - tcg_out32(s, (bswap ? LHBRX : LHZX) | TAB(datalo, rbase, addrlo)); - break; - case MO_SW: - if (bswap) { - tcg_out32(s, LHBRX | TAB(datalo, rbase, addrlo)); - tcg_out32(s, EXTSH | RA(datalo) | RS(datalo)); - } else { - tcg_out32(s, LHAX | TAB(datalo, rbase, addrlo)); + high = arg >> 31 >> 1; + tcg_out_movi32(s, ret, high); + if (high) { + tcg_out_shli64(s, ret, ret, 32); } - break; - case MO_UL: - tcg_out32(s, (bswap ? LWBRX : LWZX) | TAB(datalo, rbase, addrlo)); - break; - case MO_Q: - if (bswap) { - tcg_out32(s, ADDI | RT(TCG_REG_R0) | RA(addrlo) | 4); - tcg_out32(s, LWBRX | TAB(datalo, rbase, addrlo)); - tcg_out32(s, LWBRX | TAB(datahi, rbase, TCG_REG_R0)); - } else if (rbase != 0) { - tcg_out32(s, ADDI | RT(TCG_REG_R0) | RA(addrlo) | 4); - tcg_out32(s, LWZX | TAB(datahi, rbase, addrlo)); - tcg_out32(s, LWZX | TAB(datalo, rbase, TCG_REG_R0)); - } else if (addrlo == datahi) { - tcg_out32(s, LWZ | RT(datalo) | RA(addrlo) | 4); - tcg_out32(s, LWZ | RT(datahi) | RA(addrlo)); - } else { - tcg_out32(s, LWZ | RT(datahi) | RA(addrlo)); - tcg_out32(s, LWZ | RT(datalo) | RA(addrlo) | 4); + if (arg & 0xffff0000) { + tcg_out32(s, ORIS | SAI(ret, ret, arg >> 16)); + } + if (arg & 0xffff) { + tcg_out32(s, ORI | SAI(ret, ret, arg)); } - break; } -#ifdef CONFIG_SOFTMMU - add_qemu_ldst_label(s, true, opc, datalo, datahi, addrlo, - addrhi, mem_index, s->code_ptr, label_ptr); -#endif } -static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64) +static bool mask_operand(uint32_t c, int *mb, int *me) { - TCGReg addrlo, datalo, datahi, rbase, addrhi __attribute__((unused)); - TCGMemOp opc, bswap, s_bits; -#ifdef CONFIG_SOFTMMU - int mem_index; - tcg_insn_unit *label_ptr; -#endif + uint32_t lsb, test; + + /* Accept a bit pattern like: + 0....01....1 + 1....10....0 + 0..01..10..0 + Keep track of the transitions. */ + if (c == 0 || c == -1) { + return false; + } + test = c; + lsb = test & -test; + test += lsb; + if (test & (test - 1)) { + return false; + } - datalo = *args++; - datahi = (is64 ? *args++ : 0); - addrlo = *args++; - addrhi = (TARGET_LONG_BITS == 64 ? *args++ : 0); - opc = *args++; - bswap = opc & MO_BSWAP; - s_bits = opc & MO_SIZE; + *me = clz32(lsb); + *mb = test ? clz32(test & -test) + 1 : 0; + return true; +} -#ifdef CONFIG_SOFTMMU - mem_index = *args; - tcg_out_tlb_check(s, TCG_REG_R3, TCG_REG_R4, TCG_REG_R0, addrlo, - addrhi, s_bits, mem_index, 0, &label_ptr); - rbase = TCG_REG_R3; -#else /* !CONFIG_SOFTMMU */ - rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0; -#endif +static bool mask64_operand(uint64_t c, int *mb, int *me) +{ + uint64_t lsb; - switch (s_bits) { - case MO_8: - tcg_out32(s, STBX | SAB(datalo, rbase, addrlo)); - break; - case MO_16: - tcg_out32(s, (bswap ? STHBRX : STHX) | SAB(datalo, rbase, addrlo)); - break; - case MO_32: - default: - tcg_out32(s, (bswap ? STWBRX : STWX) | SAB(datalo, rbase, addrlo)); - break; - case MO_64: - if (bswap) { - tcg_out32(s, ADDI | RT(TCG_REG_R0) | RA(addrlo) | 4); - tcg_out32(s, STWBRX | SAB(datalo, rbase, addrlo)); - tcg_out32(s, STWBRX | SAB(datahi, rbase, TCG_REG_R0)); - } else if (rbase != 0) { - tcg_out32(s, ADDI | RT(TCG_REG_R0) | RA(addrlo) | 4); - tcg_out32(s, STWX | SAB(datahi, rbase, addrlo)); - tcg_out32(s, STWX | SAB(datalo, rbase, TCG_REG_R0)); - } else { - tcg_out32(s, STW | RS(datahi) | RA(addrlo)); - tcg_out32(s, STW | RS(datalo) | RA(addrlo) | 4); - } - break; + if (c == 0) { + return false; } -#ifdef CONFIG_SOFTMMU - add_qemu_ldst_label(s, false, opc, datalo, datahi, addrlo, addrhi, - mem_index, s->code_ptr, label_ptr); -#endif + lsb = c & -c; + /* Accept 1..10..0. */ + if (c == -lsb) { + *mb = 0; + *me = clz64(lsb); + return true; + } + /* Accept 0..01..1. */ + if (lsb == 1 && (c & (c + 1)) == 0) { + *mb = clz64(c + 1) + 1; + *me = 63; + return true; + } + return false; } -#if defined(CONFIG_SOFTMMU) -static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +static void tcg_out_andi32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c) { - TCGReg ir, datalo, datahi; - TCGMemOp opc = l->opc; + int mb, me; - reloc_pc14(l->label_ptr[0], s->code_ptr); - - ir = TCG_REG_R4; - if (TARGET_LONG_BITS == 32) { - tcg_out_mov(s, TCG_TYPE_I32, ir++, l->addrlo_reg); + if ((c & 0xffff) == c) { + tcg_out32(s, ANDI | SAI(src, dst, c)); + return; + } else if ((c & 0xffff0000) == c) { + tcg_out32(s, ANDIS | SAI(src, dst, c >> 16)); + return; + } else if (mask_operand(c, &mb, &me)) { + tcg_out_rlw(s, RLWINM, dst, src, 0, mb, me); } else { -#ifdef TCG_TARGET_CALL_ALIGN_ARGS - ir |= 1; -#endif - tcg_out_mov(s, TCG_TYPE_I32, ir++, l->addrhi_reg); - tcg_out_mov(s, TCG_TYPE_I32, ir++, l->addrlo_reg); + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R0, c); + tcg_out32(s, AND | SAB(src, dst, TCG_REG_R0)); } - tcg_out_movi(s, TCG_TYPE_I32, ir++, l->mem_index); - tcg_out32(s, MFSPR | RT(ir++) | LR); - tcg_out_b(s, LK, ld_trampolines[opc & ~MO_SIGN]); +} - datalo = l->datalo_reg; - switch (opc & MO_SSIZE) { - case MO_SB: - tcg_out32(s, EXTSB | RA(datalo) | RS(TCG_REG_R3)); - break; - case MO_SW: - tcg_out32(s, EXTSH | RA(datalo) | RS(TCG_REG_R3)); - break; - default: - tcg_out_mov(s, TCG_TYPE_I32, datalo, TCG_REG_R3); - break; - case MO_Q: - datahi = l->datahi_reg; - if (datalo != TCG_REG_R3) { - tcg_out_mov(s, TCG_TYPE_I32, datalo, TCG_REG_R4); - tcg_out_mov(s, TCG_TYPE_I32, datahi, TCG_REG_R3); - } else if (datahi != TCG_REG_R4) { - tcg_out_mov(s, TCG_TYPE_I32, datahi, TCG_REG_R3); - tcg_out_mov(s, TCG_TYPE_I32, datalo, TCG_REG_R4); +static void tcg_out_andi64(TCGContext *s, TCGReg dst, TCGReg src, uint64_t c) +{ + int mb, me; + + assert(TCG_TARGET_REG_BITS == 64); + if ((c & 0xffff) == c) { + tcg_out32(s, ANDI | SAI(src, dst, c)); + return; + } else if ((c & 0xffff0000) == c) { + tcg_out32(s, ANDIS | SAI(src, dst, c >> 16)); + return; + } else if (mask64_operand(c, &mb, &me)) { + if (mb == 0) { + tcg_out_rld(s, RLDICR, dst, src, 0, me); } else { - tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R0, TCG_REG_R4); - tcg_out_mov(s, TCG_TYPE_I32, datahi, TCG_REG_R3); - tcg_out_mov(s, TCG_TYPE_I32, datalo, TCG_REG_R0); + tcg_out_rld(s, RLDICL, dst, src, 0, mb); } - break; + } else { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R0, c); + tcg_out32(s, AND | SAB(src, dst, TCG_REG_R0)); } - tcg_out_b(s, 0, l->raddr); } -static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +static void tcg_out_zori32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c, + int op_lo, int op_hi) { - TCGReg ir, datalo; - TCGMemOp opc = l->opc; - - reloc_pc14(l->label_ptr[0], s->code_ptr); - - ir = TCG_REG_R4; - if (TARGET_LONG_BITS == 32) { - tcg_out_mov (s, TCG_TYPE_I32, ir++, l->addrlo_reg); - } else { -#ifdef TCG_TARGET_CALL_ALIGN_ARGS - ir |= 1; -#endif - tcg_out_mov (s, TCG_TYPE_I32, ir++, l->addrhi_reg); - tcg_out_mov (s, TCG_TYPE_I32, ir++, l->addrlo_reg); + if (c >> 16) { + tcg_out32(s, op_hi | SAI(src, dst, c >> 16)); + src = dst; } - - datalo = l->datalo_reg; - switch (opc & MO_SIZE) { - case MO_8: - tcg_out32(s, (RLWINM | RA (ir) | RS (datalo) - | SH (0) | MB (24) | ME (31))); - break; - case MO_16: - tcg_out32(s, (RLWINM | RA (ir) | RS (datalo) - | SH (0) | MB (16) | ME (31))); - break; - default: - tcg_out_mov(s, TCG_TYPE_I32, ir, datalo); - break; - case MO_64: -#ifdef TCG_TARGET_CALL_ALIGN_ARGS - ir |= 1; -#endif - tcg_out_mov(s, TCG_TYPE_I32, ir++, l->datahi_reg); - tcg_out_mov(s, TCG_TYPE_I32, ir, datalo); - break; + if (c & 0xffff) { + tcg_out32(s, op_lo | SAI(src, dst, c)); + src = dst; } - ir++; - - tcg_out_movi(s, TCG_TYPE_I32, ir++, l->mem_index); - tcg_out32(s, MFSPR | RT(ir++) | LR); - tcg_out_b(s, LK, st_trampolines[opc]); - tcg_out_b(s, 0, l->raddr); } -#endif -#ifdef CONFIG_SOFTMMU -static void emit_ldst_trampoline(TCGContext *s, tcg_insn_unit *ptr) +static void tcg_out_ori32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c) { - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_R3, TCG_AREG0); - tcg_out_call1(s, ptr, 0); + tcg_out_zori32(s, dst, src, c, ORI, ORIS); } -#endif -static void tcg_target_qemu_prologue (TCGContext *s) +static void tcg_out_xori32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c) { - int i, frame_size; - - frame_size = 0 - + LINKAGE_AREA_SIZE - + TCG_STATIC_CALL_ARGS_SIZE - + ARRAY_SIZE (tcg_target_callee_save_regs) * 4 - + CPU_TEMP_BUF_NLONGS * sizeof(long) - ; - frame_size = (frame_size + 15) & ~15; - - tcg_set_frame(s, TCG_REG_CALL_STACK, frame_size - - CPU_TEMP_BUF_NLONGS * sizeof(long), - CPU_TEMP_BUF_NLONGS * sizeof(long)); + tcg_out_zori32(s, dst, src, c, XORI, XORIS); +} -#ifdef _CALL_AIX - { - uintptr_t addr; +static void tcg_out_b(TCGContext *s, int mask, tcg_insn_unit *target) +{ + ptrdiff_t disp = tcg_pcrel_diff(s, target); + if (in_range_b(disp)) { + tcg_out32(s, B | (disp & 0x3fffffc) | mask); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R0, (uintptr_t)target); + tcg_out32(s, MTSPR | RS(TCG_REG_R0) | CTR); + tcg_out32(s, BCCTR | BO_ALWAYS | mask); + } +} - /* First emit adhoc function descriptor */ - addr = (uintptr_t)s->code_ptr + 12; - tcg_out32(s, addr); /* entry point */ - tcg_out32(s, 0); /* toc */ - tcg_out32(s, 0); /* environment pointer */ +static void tcg_out_mem_long(TCGContext *s, int opi, int opx, TCGReg rt, + TCGReg base, tcg_target_long offset) +{ + tcg_target_long orig = offset, l0, l1, extra = 0, align = 0; + bool is_store = false; + TCGReg rs = TCG_REG_TMP1; + + switch (opi) { + case LD: case LWA: + align = 3; + /* FALLTHRU */ + default: + if (rt != TCG_REG_R0) { + rs = rt; + break; + } + break; + case STD: + align = 3; + /* FALLTHRU */ + case STB: case STH: case STW: + is_store = true; + break; } -#endif - tcg_out32 (s, MFSPR | RT (0) | LR); - tcg_out32 (s, STWU | RS (1) | RA (1) | (-frame_size & 0xffff)); - for (i = 0; i < ARRAY_SIZE (tcg_target_callee_save_regs); ++i) - tcg_out32 (s, (STW - | RS (tcg_target_callee_save_regs[i]) - | RA (1) - | (i * 4 + LINKAGE_AREA_SIZE + TCG_STATIC_CALL_ARGS_SIZE) - ) - ); - tcg_out32 (s, STW | RS (0) | RA (1) | (frame_size + LR_OFFSET)); -#ifdef CONFIG_USE_GUEST_BASE - if (GUEST_BASE) { - tcg_out_movi (s, TCG_TYPE_I32, TCG_GUEST_BASE_REG, GUEST_BASE); - tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + /* For unaligned, or very large offsets, use the indexed form. */ + if (offset & align || offset != (int32_t)offset) { + tcg_debug_assert(rs != base && (!is_store || rs != rt)); + tcg_out_movi(s, TCG_TYPE_PTR, rs, orig); + tcg_out32(s, opx | TAB(rt, base, rs)); + return; } -#endif - tcg_out_mov (s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); - tcg_out32 (s, MTSPR | RS (tcg_target_call_iarg_regs[1]) | CTR); - tcg_out32 (s, BCCTR | BO_ALWAYS); - tb_ret_addr = s->code_ptr; - - for (i = 0; i < ARRAY_SIZE (tcg_target_callee_save_regs); ++i) - tcg_out32 (s, (LWZ - | RT (tcg_target_callee_save_regs[i]) - | RA (1) - | (i * 4 + LINKAGE_AREA_SIZE + TCG_STATIC_CALL_ARGS_SIZE) - ) - ); - tcg_out32 (s, LWZ | RT (0) | RA (1) | (frame_size + LR_OFFSET)); - tcg_out32 (s, MTSPR | RS (0) | LR); - tcg_out32 (s, ADDI | RT (1) | RA (1) | frame_size); - tcg_out32 (s, BCLR | BO_ALWAYS); + l0 = (int16_t)offset; + offset = (offset - l0) >> 16; + l1 = (int16_t)offset; -#ifdef CONFIG_SOFTMMU - for (i = 0; i < 16; ++i) { - if (qemu_ld_helpers[i]) { - ld_trampolines[i] = s->code_ptr; - emit_ldst_trampoline(s, qemu_ld_helpers[i]); - } - if (qemu_st_helpers[i]) { - st_trampolines[i] = s->code_ptr; - emit_ldst_trampoline(s, qemu_st_helpers[i]); - } + if (l1 < 0 && orig >= 0) { + extra = 0x4000; + l1 = (int16_t)(offset - 0x4000); + } + if (l1) { + tcg_out32(s, ADDIS | TAI(rs, base, l1)); + base = rs; + } + if (extra) { + tcg_out32(s, ADDIS | TAI(rs, base, extra)); + base = rs; + } + if (opi != ADDI || base != rt || l0 != 0) { + tcg_out32(s, opi | TAI(rt, base, l0)); } -#endif } -static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, - intptr_t arg2) +static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, + TCGReg arg1, intptr_t arg2) { - tcg_out_ldst (s, ret, arg1, arg2, LWZ, LWZX); -} + int opi, opx; -static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, TCGReg arg1, - intptr_t arg2) -{ - tcg_out_ldst (s, arg, arg1, arg2, STW, STWX); + assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); + if (type == TCG_TYPE_I32) { + opi = LWZ, opx = LWZX; + } else { + opi = LD, opx = LDX; + } + tcg_out_mem_long(s, opi, opx, ret, arg1, arg2); } -static void ppc_addi (TCGContext *s, int rt, int ra, tcg_target_long si) +static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, + TCGReg arg1, intptr_t arg2) { - if (!si && rt == ra) - return; + int opi, opx; - if (si == (int16_t) si) - tcg_out32 (s, ADDI | RT (rt) | RA (ra) | (si & 0xffff)); - else { - uint16_t h = ((si >> 16) & 0xffff) + ((uint16_t) si >> 15); - tcg_out32 (s, ADDIS | RT (rt) | RA (ra) | h); - tcg_out32 (s, ADDI | RT (rt) | RA (rt) | (si & 0xffff)); + assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); + if (type == TCG_TYPE_I32) { + opi = STW, opx = STWX; + } else { + opi = STD, opx = STDX; } + tcg_out_mem_long(s, opi, opx, arg, arg1, arg2); } -static void tcg_out_cmp (TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, - int const_arg2, int cr) +static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, + int const_arg2, int cr, TCGType type) { int imm; uint32_t op; + tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); + + /* Simplify the comparisons below wrt CMPI. */ + if (type == TCG_TYPE_I32) { + arg2 = (int32_t)arg2; + } + switch (cond) { case TCG_COND_EQ: case TCG_COND_NE: @@ -994,8 +881,7 @@ static void tcg_out_cmp (TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, op = CMPI; imm = 1; break; - } - else if ((uint16_t) arg2 == arg2) { + } else if ((uint16_t) arg2 == arg2) { op = CMPLI; imm = 1; break; @@ -1036,148 +922,144 @@ static void tcg_out_cmp (TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, break; default: - tcg_abort (); + tcg_abort(); } - op |= BF (cr); + op |= BF(cr) | ((type == TCG_TYPE_I64) << 21); - if (imm) - tcg_out32 (s, op | RA (arg1) | (arg2 & 0xffff)); - else { + if (imm) { + tcg_out32(s, op | RA(arg1) | (arg2 & 0xffff)); + } else { if (const_arg2) { - tcg_out_movi (s, TCG_TYPE_I32, 0, arg2); - tcg_out32 (s, op | RA (arg1) | RB (0)); + tcg_out_movi(s, type, TCG_REG_R0, arg2); + arg2 = TCG_REG_R0; } - else - tcg_out32 (s, op | RA (arg1) | RB (arg2)); + tcg_out32(s, op | RA(arg1) | RB(arg2)); } - } -static void tcg_out_bc(TCGContext *s, int bc, int label_index) +static void tcg_out_setcond_eq0(TCGContext *s, TCGType type, + TCGReg dst, TCGReg src) { - TCGLabel *l = &s->labels[label_index]; - - if (l->has_value) { - tcg_out32(s, bc | reloc_pc14_val(s->code_ptr, l->u.value_ptr)); + if (type == TCG_TYPE_I32) { + tcg_out32(s, CNTLZW | RS(src) | RA(dst)); + tcg_out_shri32(s, dst, dst, 5); } else { - /* Thanks to Andrzej Zaborowski */ - tcg_insn_unit retrans = *s->code_ptr & 0xfffc; - tcg_out_reloc(s, s->code_ptr, R_PPC_REL14, label_index, 0); - tcg_out32(s, bc | retrans); + tcg_out32(s, CNTLZD | RS(src) | RA(dst)); + tcg_out_shri64(s, dst, dst, 6); } } -static void tcg_out_cr7eq_from_cond (TCGContext *s, const TCGArg *args, - const int *const_args) +static void tcg_out_setcond_ne0(TCGContext *s, TCGReg dst, TCGReg src) { - TCGCond cond = args[4]; - int op; - struct { int bit1; int bit2; int cond2; } bits[] = { - [TCG_COND_LT ] = { CR_LT, CR_LT, TCG_COND_LT }, - [TCG_COND_LE ] = { CR_LT, CR_GT, TCG_COND_LT }, - [TCG_COND_GT ] = { CR_GT, CR_GT, TCG_COND_GT }, - [TCG_COND_GE ] = { CR_GT, CR_LT, TCG_COND_GT }, - [TCG_COND_LTU] = { CR_LT, CR_LT, TCG_COND_LTU }, - [TCG_COND_LEU] = { CR_LT, CR_GT, TCG_COND_LTU }, - [TCG_COND_GTU] = { CR_GT, CR_GT, TCG_COND_GTU }, - [TCG_COND_GEU] = { CR_GT, CR_LT, TCG_COND_GTU }, - }, *b = &bits[cond]; + /* X != 0 implies X + -1 generates a carry. Extra addition + trickery means: R = X-1 + ~X + C = X-1 + (-X+1) + C = C. */ + if (dst != src) { + tcg_out32(s, ADDIC | TAI(dst, src, -1)); + tcg_out32(s, SUBFE | TAB(dst, dst, src)); + } else { + tcg_out32(s, ADDIC | TAI(TCG_REG_R0, src, -1)); + tcg_out32(s, SUBFE | TAB(dst, TCG_REG_R0, src)); + } +} - switch (cond) { - case TCG_COND_EQ: - case TCG_COND_NE: - op = (cond == TCG_COND_EQ) ? CRAND : CRNAND; - tcg_out_cmp (s, cond, args[0], args[2], const_args[2], 6); - tcg_out_cmp (s, cond, args[1], args[3], const_args[3], 7); - tcg_out32 (s, op | BT (7, CR_EQ) | BA (6, CR_EQ) | BB (7, CR_EQ)); - break; - case TCG_COND_LT: - case TCG_COND_LE: - case TCG_COND_GT: - case TCG_COND_GE: - case TCG_COND_LTU: - case TCG_COND_LEU: - case TCG_COND_GTU: - case TCG_COND_GEU: - op = (b->bit1 != b->bit2) ? CRANDC : CRAND; - tcg_out_cmp (s, b->cond2, args[1], args[3], const_args[3], 5); - tcg_out_cmp (s, tcg_unsigned_cond (cond), args[0], args[2], - const_args[2], 7); - tcg_out32 (s, op | BT (7, CR_EQ) | BA (5, CR_EQ) | BB (7, b->bit2)); - tcg_out32 (s, CROR | BT (7, CR_EQ) | BA (5, b->bit1) | BB (7, CR_EQ)); - break; - default: - tcg_abort(); +static TCGReg tcg_gen_setcond_xor(TCGContext *s, TCGReg arg1, TCGArg arg2, + bool const_arg2) +{ + if (const_arg2) { + if ((uint32_t)arg2 == arg2) { + tcg_out_xori32(s, TCG_REG_R0, arg1, arg2); + } else { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R0, arg2); + tcg_out32(s, XOR | SAB(arg1, TCG_REG_R0, TCG_REG_R0)); + } + } else { + tcg_out32(s, XOR | SAB(arg1, TCG_REG_R0, arg2)); } + return TCG_REG_R0; } -static void tcg_out_setcond (TCGContext *s, TCGCond cond, TCGArg arg0, - TCGArg arg1, TCGArg arg2, int const_arg2) +static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGArg arg0, TCGArg arg1, TCGArg arg2, + int const_arg2) { - int crop, sh, arg; + int crop, sh; - switch (cond) { - case TCG_COND_EQ: - if (const_arg2) { - if (!arg2) { - arg = arg1; - } - else { - arg = 0; - if ((uint16_t) arg2 == arg2) { - tcg_out32 (s, XORI | RS (arg1) | RA (0) | arg2); - } - else { - tcg_out_movi (s, TCG_TYPE_I32, 0, arg2); - tcg_out32 (s, XOR | SAB (arg1, 0, 0)); - } - } - } - else { - arg = 0; - tcg_out32 (s, XOR | SAB (arg1, 0, arg2)); - } - tcg_out32 (s, CNTLZW | RS (arg) | RA (0)); - tcg_out32 (s, (RLWINM - | RA (arg0) - | RS (0) - | SH (27) - | MB (5) - | ME (31) - ) - ); - break; + assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); - case TCG_COND_NE: - if (const_arg2) { - if (!arg2) { - arg = arg1; + /* Ignore high bits of a potential constant arg2. */ + if (type == TCG_TYPE_I32) { + arg2 = (uint32_t)arg2; + } + + /* Handle common and trivial cases before handling anything else. */ + if (arg2 == 0) { + switch (cond) { + case TCG_COND_EQ: + tcg_out_setcond_eq0(s, type, arg0, arg1); + return; + case TCG_COND_NE: + if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) { + tcg_out_ext32u(s, TCG_REG_R0, arg1); + arg1 = TCG_REG_R0; } - else { - arg = 0; - if ((uint16_t) arg2 == arg2) { - tcg_out32 (s, XORI | RS (arg1) | RA (0) | arg2); - } - else { - tcg_out_movi (s, TCG_TYPE_I32, 0, arg2); - tcg_out32 (s, XOR | SAB (arg1, 0, 0)); - } + tcg_out_setcond_ne0(s, arg0, arg1); + return; + case TCG_COND_GE: + tcg_out32(s, NOR | SAB(arg1, arg0, arg1)); + arg1 = arg0; + /* FALLTHRU */ + case TCG_COND_LT: + /* Extract the sign bit. */ + if (type == TCG_TYPE_I32) { + tcg_out_shri32(s, arg0, arg1, 31); + } else { + tcg_out_shri64(s, arg0, arg1, 63); } + return; + default: + break; } - else { - arg = 0; - tcg_out32 (s, XOR | SAB (arg1, 0, arg2)); - } + } + + /* If we have ISEL, we can implement everything with 3 or 4 insns. + All other cases below are also at least 3 insns, so speed up the + code generator by not considering them and always using ISEL. */ + if (HAVE_ISEL) { + int isel, tab; + + tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); - if (arg == arg1 && arg1 == arg0) { - tcg_out32 (s, ADDIC | RT (0) | RA (arg) | 0xffff); - tcg_out32 (s, SUBFE | TAB (arg0, 0, arg)); + isel = tcg_to_isel[cond]; + + tcg_out_movi(s, type, arg0, 1); + if (isel & 1) { + /* arg0 = (bc ? 0 : 1) */ + tab = TAB(arg0, 0, arg0); + isel &= ~1; + } else { + /* arg0 = (bc ? 1 : 0) */ + tcg_out_movi(s, type, TCG_REG_R0, 0); + tab = TAB(arg0, arg0, TCG_REG_R0); } - else { - tcg_out32 (s, ADDIC | RT (arg0) | RA (arg) | 0xffff); - tcg_out32 (s, SUBFE | TAB (arg0, arg0, arg)); + tcg_out32(s, isel | tab); + return; + } + + switch (cond) { + case TCG_COND_EQ: + arg1 = tcg_gen_setcond_xor(s, arg1, arg2, const_arg2); + tcg_out_setcond_eq0(s, type, arg0, arg1); + return; + + case TCG_COND_NE: + arg1 = tcg_gen_setcond_xor(s, arg1, arg2, const_arg2); + /* Discard the high bits only once, rather than both inputs. */ + if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) { + tcg_out_ext32u(s, TCG_REG_R0, arg1); + arg1 = TCG_REG_R0; } - break; + tcg_out_setcond_ne0(s, arg0, arg1); + return; case TCG_COND_GT: case TCG_COND_GTU: @@ -1194,176 +1076,788 @@ static void tcg_out_setcond (TCGContext *s, TCGCond cond, TCGArg arg0, case TCG_COND_GE: case TCG_COND_GEU: sh = 31; - crop = CRNOR | BT (7, CR_EQ) | BA (7, CR_LT) | BB (7, CR_LT); + crop = CRNOR | BT(7, CR_EQ) | BA(7, CR_LT) | BB(7, CR_LT); goto crtest; case TCG_COND_LE: case TCG_COND_LEU: sh = 31; - crop = CRNOR | BT (7, CR_EQ) | BA (7, CR_GT) | BB (7, CR_GT); + crop = CRNOR | BT(7, CR_EQ) | BA(7, CR_GT) | BB(7, CR_GT); crtest: - tcg_out_cmp (s, cond, arg1, arg2, const_arg2, 7); - if (crop) tcg_out32 (s, crop); - tcg_out32 (s, MFCR | RT (0)); - tcg_out32 (s, (RLWINM - | RA (arg0) - | RS (0) - | SH (sh) - | MB (31) - | ME (31) - ) - ); + tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); + if (crop) { + tcg_out32(s, crop); + } + tcg_out32(s, MFOCRF | RT(TCG_REG_R0) | FXM(7)); + tcg_out_rlw(s, RLWINM, arg0, TCG_REG_R0, sh, 31, 31); break; default: - tcg_abort (); + tcg_abort(); } } -static void tcg_out_setcond2 (TCGContext *s, const TCGArg *args, - const int *const_args) +static void tcg_out_bc(TCGContext *s, int bc, int label_index) { - tcg_out_cr7eq_from_cond (s, args + 1, const_args + 1); - tcg_out32 (s, MFCR | RT (0)); - tcg_out32 (s, (RLWINM - | RA (args[0]) - | RS (0) - | SH (31) - | MB (31) - | ME (31) - ) - ); + TCGLabel *l = &s->labels[label_index]; + + if (l->has_value) { + tcg_out32(s, bc | reloc_pc14_val(s->code_ptr, l->u.value_ptr)); + } else { + tcg_out_reloc(s, s->code_ptr, R_PPC_REL14, label_index, 0); + tcg_out_bc_noaddr(s, bc); + } } -static void tcg_out_movcond (TCGContext *s, TCGCond cond, - TCGArg dest, - TCGArg c1, TCGArg c2, - TCGArg v1, TCGArg v2, - int const_c2) +static void tcg_out_brcond(TCGContext *s, TCGCond cond, + TCGArg arg1, TCGArg arg2, int const_arg2, + int label_index, TCGType type) { - tcg_out_cmp (s, cond, c1, c2, const_c2, 7); + tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); + tcg_out_bc(s, tcg_to_bc[cond], label_index); +} + +static void tcg_out_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGArg dest, TCGArg c1, TCGArg c2, TCGArg v1, + TCGArg v2, bool const_c2) +{ + /* If for some reason both inputs are zero, don't produce bad code. */ + if (v1 == 0 && v2 == 0) { + tcg_out_movi(s, type, dest, 0); + return; + } + + tcg_out_cmp(s, cond, c1, c2, const_c2, 7, type); + + if (HAVE_ISEL) { + int isel = tcg_to_isel[cond]; - if (1) { - /* At least here on 7747A bit twiddling hacks are outperformed - by jumpy code (the testing was not scientific) */ + /* Swap the V operands if the operation indicates inversion. */ + if (isel & 1) { + int t = v1; + v1 = v2; + v2 = t; + isel &= ~1; + } + /* V1 == 0 is handled by isel; V2 == 0 must be handled by hand. */ + if (v2 == 0) { + tcg_out_movi(s, type, TCG_REG_R0, 0); + } + tcg_out32(s, isel | TAB(dest, v1, v2)); + } else { if (dest == v2) { - cond = tcg_invert_cond (cond); + cond = tcg_invert_cond(cond); v2 = v1; - } - else { - if (dest != v1) { - tcg_out_mov (s, TCG_TYPE_I32, dest, v1); + } else if (dest != v1) { + if (v1 == 0) { + tcg_out_movi(s, type, dest, 0); + } else { + tcg_out_mov(s, type, dest, v1); } } /* Branch forward over one insn */ - tcg_out32 (s, tcg_to_bc[cond] | 8); - tcg_out_mov (s, TCG_TYPE_I32, dest, v2); + tcg_out32(s, tcg_to_bc[cond] | 8); + if (v2 == 0) { + tcg_out_movi(s, type, dest, 0); + } else { + tcg_out_mov(s, type, dest, v2); + } } - else { - /* isel version, "if (1)" above should be replaced once a way - to figure out availability of isel on the underlying - hardware is found */ - int tab, bc; +} - switch (cond) { - case TCG_COND_EQ: - tab = TAB (dest, v1, v2); - bc = CR_EQ; - break; - case TCG_COND_NE: - tab = TAB (dest, v2, v1); - bc = CR_EQ; - break; - case TCG_COND_LTU: - case TCG_COND_LT: - tab = TAB (dest, v1, v2); - bc = CR_LT; - break; - case TCG_COND_GEU: - case TCG_COND_GE: - tab = TAB (dest, v2, v1); - bc = CR_LT; - break; - case TCG_COND_LEU: - case TCG_COND_LE: - tab = TAB (dest, v2, v1); - bc = CR_GT; - break; - case TCG_COND_GTU: - case TCG_COND_GT: - tab = TAB (dest, v1, v2); - bc = CR_GT; - break; - default: - tcg_abort (); - } - tcg_out32 (s, ISEL | tab | ((bc + 28) << 6)); +static void tcg_out_cmp2(TCGContext *s, const TCGArg *args, + const int *const_args) +{ + static const struct { uint8_t bit1, bit2; } bits[] = { + [TCG_COND_LT ] = { CR_LT, CR_LT }, + [TCG_COND_LE ] = { CR_LT, CR_GT }, + [TCG_COND_GT ] = { CR_GT, CR_GT }, + [TCG_COND_GE ] = { CR_GT, CR_LT }, + [TCG_COND_LTU] = { CR_LT, CR_LT }, + [TCG_COND_LEU] = { CR_LT, CR_GT }, + [TCG_COND_GTU] = { CR_GT, CR_GT }, + [TCG_COND_GEU] = { CR_GT, CR_LT }, + }; + + TCGCond cond = args[4], cond2; + TCGArg al, ah, bl, bh; + int blconst, bhconst; + int op, bit1, bit2; + + al = args[0]; + ah = args[1]; + bl = args[2]; + bh = args[3]; + blconst = const_args[2]; + bhconst = const_args[3]; + + switch (cond) { + case TCG_COND_EQ: + op = CRAND; + goto do_equality; + case TCG_COND_NE: + op = CRNAND; + do_equality: + tcg_out_cmp(s, cond, al, bl, blconst, 6, TCG_TYPE_I32); + tcg_out_cmp(s, cond, ah, bh, bhconst, 7, TCG_TYPE_I32); + tcg_out32(s, op | BT(7, CR_EQ) | BA(6, CR_EQ) | BB(7, CR_EQ)); + break; + + case TCG_COND_LT: + case TCG_COND_LE: + case TCG_COND_GT: + case TCG_COND_GE: + case TCG_COND_LTU: + case TCG_COND_LEU: + case TCG_COND_GTU: + case TCG_COND_GEU: + bit1 = bits[cond].bit1; + bit2 = bits[cond].bit2; + op = (bit1 != bit2 ? CRANDC : CRAND); + cond2 = tcg_unsigned_cond(cond); + + tcg_out_cmp(s, cond, ah, bh, bhconst, 6, TCG_TYPE_I32); + tcg_out_cmp(s, cond2, al, bl, blconst, 7, TCG_TYPE_I32); + tcg_out32(s, op | BT(7, CR_EQ) | BA(6, CR_EQ) | BB(7, bit2)); + tcg_out32(s, CROR | BT(7, CR_EQ) | BA(6, bit1) | BB(7, CR_EQ)); + break; + + default: + tcg_abort(); } } -static void tcg_out_brcond (TCGContext *s, TCGCond cond, - TCGArg arg1, TCGArg arg2, int const_arg2, - int label_index) +static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, + const int *const_args) { - tcg_out_cmp (s, cond, arg1, arg2, const_arg2, 7); - tcg_out_bc (s, tcg_to_bc[cond], label_index); + tcg_out_cmp2(s, args + 1, const_args + 1); + tcg_out32(s, MFOCRF | RT(TCG_REG_R0) | FXM(7)); + tcg_out_rlw(s, RLWINM, args[0], TCG_REG_R0, 31, 31, 31); } -/* XXX: we implement it at the target level to avoid having to - handle cross basic blocks temporaries */ static void tcg_out_brcond2 (TCGContext *s, const TCGArg *args, const int *const_args) { - tcg_out_cr7eq_from_cond (s, args, const_args); - tcg_out_bc (s, (BC | BI (7, CR_EQ) | BO_COND_TRUE), args[5]); + tcg_out_cmp2(s, args, const_args); + tcg_out_bc(s, BC | BI(7, CR_EQ) | BO_COND_TRUE, args[5]); +} + +void ppc_tb_set_jmp_target(uintptr_t jmp_addr, uintptr_t addr) +{ + TCGContext s; + + s.code_buf = s.code_ptr = (tcg_insn_unit *)jmp_addr; + tcg_out_b(&s, 0, (tcg_insn_unit *)addr); + flush_icache_range(jmp_addr, jmp_addr + tcg_current_code_size(&s)); +} + +static void tcg_out_call(TCGContext *s, tcg_insn_unit *target) +{ +#ifdef _CALL_AIX + /* Look through the descriptor. If the branch is in range, and we + don't have to spend too much effort on building the toc. */ + void *tgt = ((void **)target)[0]; + uintptr_t toc = ((uintptr_t *)target)[1]; + intptr_t diff = tcg_pcrel_diff(s, tgt); + + if (in_range_b(diff) && toc == (uint32_t)toc) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP1, toc); + tcg_out_b(s, LK, tgt); + } else { + /* Fold the low bits of the constant into the addresses below. */ + intptr_t arg = (intptr_t)target; + int ofs = (int16_t)arg; + + if (ofs + 8 < 0x8000) { + arg -= ofs; + } else { + ofs = 0; + } + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP1, arg); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R0, TCG_REG_TMP1, ofs); + tcg_out32(s, MTSPR | RA(TCG_REG_R0) | CTR); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R2, TCG_REG_TMP1, ofs + SZP); + tcg_out32(s, BCCTR | BO_ALWAYS | LK); + } +#elif defined(_CALL_ELF) && _CALL_ELF == 2 + intptr_t diff; + + /* In the ELFv2 ABI, we have to set up r12 to contain the destination + address, which the callee uses to compute its TOC address. */ + /* FIXME: when the branch is in range, we could avoid r12 load if we + knew that the destination uses the same TOC, and what its local + entry point offset is. */ + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R12, (intptr_t)target); + + diff = tcg_pcrel_diff(s, target); + if (in_range_b(diff)) { + tcg_out_b(s, LK, target); + } else { + tcg_out32(s, MTSPR | RS(TCG_REG_R12) | CTR); + tcg_out32(s, BCCTR | BO_ALWAYS | LK); + } +#else + tcg_out_b(s, LK, target); +#endif +} + +static const uint32_t qemu_ldx_opc[16] = { + [MO_UB] = LBZX, + [MO_UW] = LHZX, + [MO_UL] = LWZX, + [MO_Q] = LDX, + [MO_SW] = LHAX, + [MO_SL] = LWAX, + [MO_BSWAP | MO_UB] = LBZX, + [MO_BSWAP | MO_UW] = LHBRX, + [MO_BSWAP | MO_UL] = LWBRX, + [MO_BSWAP | MO_Q] = LDBRX, +}; + +static const uint32_t qemu_stx_opc[16] = { + [MO_UB] = STBX, + [MO_UW] = STHX, + [MO_UL] = STWX, + [MO_Q] = STDX, + [MO_BSWAP | MO_UB] = STBX, + [MO_BSWAP | MO_UW] = STHBRX, + [MO_BSWAP | MO_UL] = STWBRX, + [MO_BSWAP | MO_Q] = STDBRX, +}; + +static const uint32_t qemu_exts_opc[4] = { + EXTSB, EXTSH, EXTSW, 0 +}; + +#if defined (CONFIG_SOFTMMU) +/* helper signature: helper_ld_mmu(CPUState *env, target_ulong addr, + * int mmu_idx, uintptr_t ra) + */ +static void * const qemu_ld_helpers[16] = { + [MO_UB] = helper_ret_ldub_mmu, + [MO_LEUW] = helper_le_lduw_mmu, + [MO_LEUL] = helper_le_ldul_mmu, + [MO_LEQ] = helper_le_ldq_mmu, + [MO_BEUW] = helper_be_lduw_mmu, + [MO_BEUL] = helper_be_ldul_mmu, + [MO_BEQ] = helper_be_ldq_mmu, +}; + +/* helper signature: helper_st_mmu(CPUState *env, target_ulong addr, + * uintxx_t val, int mmu_idx, uintptr_t ra) + */ +static void * const qemu_st_helpers[16] = { + [MO_UB] = helper_ret_stb_mmu, + [MO_LEUW] = helper_le_stw_mmu, + [MO_LEUL] = helper_le_stl_mmu, + [MO_LEQ] = helper_le_stq_mmu, + [MO_BEUW] = helper_be_stw_mmu, + [MO_BEUL] = helper_be_stl_mmu, + [MO_BEQ] = helper_be_stq_mmu, +}; + +/* Perform the TLB load and compare. Places the result of the comparison + in CR7, loads the addend of the TLB into R3, and returns the register + containing the guest address (zero-extended into R4). Clobbers R0 and R2. */ + +static TCGReg tcg_out_tlb_read(TCGContext *s, TCGMemOp s_bits, + TCGReg addrlo, TCGReg addrhi, + int mem_index, bool is_read) +{ + int cmp_off + = (is_read + ? offsetof(CPUArchState, tlb_table[mem_index][0].addr_read) + : offsetof(CPUArchState, tlb_table[mem_index][0].addr_write)); + int add_off = offsetof(CPUArchState, tlb_table[mem_index][0].addend); + TCGReg base = TCG_AREG0; + + /* Extract the page index, shifted into place for tlb index. */ + if (TCG_TARGET_REG_BITS == 64) { + if (TARGET_LONG_BITS == 32) { + /* Zero-extend the address into a place helpful for further use. */ + tcg_out_ext32u(s, TCG_REG_R4, addrlo); + addrlo = TCG_REG_R4; + } else { + tcg_out_rld(s, RLDICL, TCG_REG_R3, addrlo, + 64 - TARGET_PAGE_BITS, 64 - CPU_TLB_BITS); + } + } + + /* Compensate for very large offsets. */ + if (add_off >= 0x8000) { + /* Most target env are smaller than 32k; none are larger than 64k. + Simplify the logic here merely to offset by 0x7ff0, giving us a + range just shy of 64k. Check this assumption. */ + QEMU_BUILD_BUG_ON(offsetof(CPUArchState, + tlb_table[NB_MMU_MODES - 1][1]) + > 0x7ff0 + 0x7fff); + tcg_out32(s, ADDI | TAI(TCG_REG_TMP1, base, 0x7ff0)); + base = TCG_REG_TMP1; + cmp_off -= 0x7ff0; + add_off -= 0x7ff0; + } + + /* Extraction and shifting, part 2. */ + if (TCG_TARGET_REG_BITS == 32 || TARGET_LONG_BITS == 32) { + tcg_out_rlw(s, RLWINM, TCG_REG_R3, addrlo, + 32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS), + 32 - (CPU_TLB_BITS + CPU_TLB_ENTRY_BITS), + 31 - CPU_TLB_ENTRY_BITS); + } else { + tcg_out_shli64(s, TCG_REG_R3, TCG_REG_R3, CPU_TLB_ENTRY_BITS); + } + + tcg_out32(s, ADD | TAB(TCG_REG_R3, TCG_REG_R3, base)); + + /* Load the tlb comparator. */ + if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { + tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_R4, TCG_REG_R3, cmp_off); + tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_TMP1, TCG_REG_R3, cmp_off + 4); + } else { + tcg_out_ld(s, TCG_TYPE_TL, TCG_REG_TMP1, TCG_REG_R3, cmp_off); + } + + /* Load the TLB addend for use on the fast path. Do this asap + to minimize any load use delay. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R3, TCG_REG_R3, add_off); + + /* Clear the non-page, non-alignment bits from the address. */ + if (TCG_TARGET_REG_BITS == 32 || TARGET_LONG_BITS == 32) { + tcg_out_rlw(s, RLWINM, TCG_REG_R0, addrlo, 0, + (32 - s_bits) & 31, 31 - TARGET_PAGE_BITS); + } else if (!s_bits) { + tcg_out_rld(s, RLDICR, TCG_REG_R0, addrlo, + 0, 63 - TARGET_PAGE_BITS); + } else { + tcg_out_rld(s, RLDICL, TCG_REG_R0, addrlo, + 64 - TARGET_PAGE_BITS, TARGET_PAGE_BITS - s_bits); + tcg_out_rld(s, RLDICL, TCG_REG_R0, TCG_REG_R0, TARGET_PAGE_BITS, 0); + } + + if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { + tcg_out_cmp(s, TCG_COND_EQ, TCG_REG_R0, TCG_REG_TMP1, + 0, 7, TCG_TYPE_I32); + tcg_out_cmp(s, TCG_COND_EQ, addrhi, TCG_REG_R4, 0, 6, TCG_TYPE_I32); + tcg_out32(s, CRAND | BT(7, CR_EQ) | BA(6, CR_EQ) | BB(7, CR_EQ)); + } else { + tcg_out_cmp(s, TCG_COND_EQ, TCG_REG_R0, TCG_REG_TMP1, + 0, 7, TCG_TYPE_TL); + } + + return addrlo; +} + +/* Record the context of a call to the out of line helper code for the slow + path for a load or store, so that we can later generate the correct + helper code. */ +static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOp opc, + TCGReg datalo_reg, TCGReg datahi_reg, + TCGReg addrlo_reg, TCGReg addrhi_reg, + int mem_index, tcg_insn_unit *raddr, + tcg_insn_unit *lptr) +{ + TCGLabelQemuLdst *label = new_ldst_label(s); + + label->is_ld = is_ld; + label->opc = opc; + label->datalo_reg = datalo_reg; + label->datahi_reg = datahi_reg; + label->addrlo_reg = addrlo_reg; + label->addrhi_reg = addrhi_reg; + label->mem_index = mem_index; + label->raddr = raddr; + label->label_ptr[0] = lptr; +} + +static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) +{ + TCGMemOp opc = lb->opc; + TCGReg hi, lo, arg = TCG_REG_R3; + + reloc_pc14(lb->label_ptr[0], s->code_ptr); + + tcg_out_mov(s, TCG_TYPE_PTR, arg++, TCG_AREG0); + + lo = lb->addrlo_reg; + hi = lb->addrhi_reg; + if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { +#ifdef TCG_TARGET_CALL_ALIGN_ARGS + arg |= 1; +#endif + tcg_out_mov(s, TCG_TYPE_I32, arg++, hi); + tcg_out_mov(s, TCG_TYPE_I32, arg++, lo); + } else { + /* If the address needed to be zero-extended, we'll have already + placed it in R4. The only remaining case is 64-bit guest. */ + tcg_out_mov(s, TCG_TYPE_TL, arg++, lo); + } + + tcg_out_movi(s, TCG_TYPE_I32, arg++, lb->mem_index); + tcg_out32(s, MFSPR | RT(arg) | LR); + + tcg_out_call(s, qemu_ld_helpers[opc & ~MO_SIGN]); + + lo = lb->datalo_reg; + hi = lb->datahi_reg; + if (TCG_TARGET_REG_BITS == 32 && (opc & MO_SIZE) == MO_64) { + tcg_out_mov(s, TCG_TYPE_I32, lo, TCG_REG_R4); + tcg_out_mov(s, TCG_TYPE_I32, hi, TCG_REG_R3); + } else if (opc & MO_SIGN) { + uint32_t insn = qemu_exts_opc[opc & MO_SIZE]; + tcg_out32(s, insn | RA(lo) | RS(TCG_REG_R3)); + } else { + tcg_out_mov(s, TCG_TYPE_REG, lo, TCG_REG_R3); + } + + tcg_out_b(s, 0, lb->raddr); +} + +static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) +{ + TCGMemOp opc = lb->opc; + TCGMemOp s_bits = opc & MO_SIZE; + TCGReg hi, lo, arg = TCG_REG_R3; + + reloc_pc14(lb->label_ptr[0], s->code_ptr); + + tcg_out_mov(s, TCG_TYPE_PTR, arg++, TCG_AREG0); + + lo = lb->addrlo_reg; + hi = lb->addrhi_reg; + if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { +#ifdef TCG_TARGET_CALL_ALIGN_ARGS + arg |= 1; +#endif + tcg_out_mov(s, TCG_TYPE_I32, arg++, hi); + tcg_out_mov(s, TCG_TYPE_I32, arg++, lo); + } else { + /* If the address needed to be zero-extended, we'll have already + placed it in R4. The only remaining case is 64-bit guest. */ + tcg_out_mov(s, TCG_TYPE_TL, arg++, lo); + } + + lo = lb->datalo_reg; + hi = lb->datahi_reg; + if (TCG_TARGET_REG_BITS == 32) { + switch (s_bits) { + case MO_64: +#ifdef TCG_TARGET_CALL_ALIGN_ARGS + arg |= 1; +#endif + tcg_out_mov(s, TCG_TYPE_I32, arg++, hi); + /* FALLTHRU */ + case MO_32: + tcg_out_mov(s, TCG_TYPE_I32, arg++, lo); + break; + default: + tcg_out_rlw(s, RLWINM, arg++, lo, 0, 32 - (8 << s_bits), 31); + break; + } + } else { + if (s_bits == MO_64) { + tcg_out_mov(s, TCG_TYPE_I64, arg++, lo); + } else { + tcg_out_rld(s, RLDICL, arg++, lo, 0, 64 - (8 << s_bits)); + } + } + + tcg_out_movi(s, TCG_TYPE_I32, arg++, lb->mem_index); + tcg_out32(s, MFSPR | RT(arg) | LR); + + tcg_out_call(s, qemu_st_helpers[opc]); + + tcg_out_b(s, 0, lb->raddr); +} +#endif /* SOFTMMU */ + +static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) +{ + TCGReg datalo, datahi, addrlo, rbase; + TCGReg addrhi __attribute__((unused)); + TCGMemOp opc, s_bits; +#ifdef CONFIG_SOFTMMU + int mem_index; + tcg_insn_unit *label_ptr; +#endif + + datalo = *args++; + datahi = (TCG_TARGET_REG_BITS == 32 && is_64 ? *args++ : 0); + addrlo = *args++; + addrhi = (TCG_TARGET_REG_BITS < TARGET_LONG_BITS ? *args++ : 0); + opc = *args++; + s_bits = opc & MO_SIZE; + +#ifdef CONFIG_SOFTMMU + mem_index = *args; + addrlo = tcg_out_tlb_read(s, s_bits, addrlo, addrhi, mem_index, true); + + /* Load a pointer into the current opcode w/conditional branch-link. */ + label_ptr = s->code_ptr; + tcg_out_bc_noaddr(s, BC | BI(7, CR_EQ) | BO_COND_FALSE | LK); + + rbase = TCG_REG_R3; +#else /* !CONFIG_SOFTMMU */ + rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0; + if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { + tcg_out_ext32u(s, TCG_REG_TMP1, addrlo); + addrlo = TCG_REG_TMP1; + } +#endif + + if (TCG_TARGET_REG_BITS == 32 && s_bits == MO_64) { + if (opc & MO_BSWAP) { + tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); + tcg_out32(s, LWBRX | TAB(datalo, rbase, addrlo)); + tcg_out32(s, LWBRX | TAB(datahi, rbase, TCG_REG_R0)); + } else if (rbase != 0) { + tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); + tcg_out32(s, LWZX | TAB(datahi, rbase, addrlo)); + tcg_out32(s, LWZX | TAB(datalo, rbase, TCG_REG_R0)); + } else if (addrlo == datahi) { + tcg_out32(s, LWZ | TAI(datalo, addrlo, 4)); + tcg_out32(s, LWZ | TAI(datahi, addrlo, 0)); + } else { + tcg_out32(s, LWZ | TAI(datahi, addrlo, 0)); + tcg_out32(s, LWZ | TAI(datalo, addrlo, 4)); + } + } else { + uint32_t insn = qemu_ldx_opc[opc]; + if (!HAVE_ISA_2_06 && insn == LDBRX) { + tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); + tcg_out32(s, LWBRX | TAB(datalo, rbase, addrlo)); + tcg_out32(s, LWBRX | TAB(TCG_REG_R0, rbase, TCG_REG_R0)); + tcg_out_rld(s, RLDIMI, datalo, TCG_REG_R0, 32, 0); + } else if (insn) { + tcg_out32(s, insn | TAB(datalo, rbase, addrlo)); + } else { + insn = qemu_ldx_opc[opc & (MO_SIZE | MO_BSWAP)]; + tcg_out32(s, insn | TAB(datalo, rbase, addrlo)); + insn = qemu_exts_opc[s_bits]; + tcg_out32(s, insn | RA(datalo) | RS(datalo)); + } + } + +#ifdef CONFIG_SOFTMMU + add_qemu_ldst_label(s, true, opc, datalo, datahi, addrlo, addrhi, + mem_index, s->code_ptr, label_ptr); +#endif } -void ppc_tb_set_jmp_target (unsigned long jmp_addr, unsigned long addr) +static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) { - uint32_t *ptr; - long disp = addr - jmp_addr; - unsigned long patch_size; - - ptr = (uint32_t *)jmp_addr; - - if ((disp << 6) >> 6 != disp) { - ptr[0] = 0x3c000000 | (addr >> 16); /* lis 0,addr@ha */ - ptr[1] = 0x60000000 | (addr & 0xffff); /* la 0,addr@l(0) */ - ptr[2] = 0x7c0903a6; /* mtctr 0 */ - ptr[3] = 0x4e800420; /* brctr */ - patch_size = 16; + TCGReg datalo, datahi, addrlo, rbase; + TCGReg addrhi __attribute__((unused)); + TCGMemOp opc, s_bits; +#ifdef CONFIG_SOFTMMU + int mem_index; + tcg_insn_unit *label_ptr; +#endif + + datalo = *args++; + datahi = (TCG_TARGET_REG_BITS == 32 && is_64 ? *args++ : 0); + addrlo = *args++; + addrhi = (TCG_TARGET_REG_BITS < TARGET_LONG_BITS ? *args++ : 0); + opc = *args++; + s_bits = opc & MO_SIZE; + +#ifdef CONFIG_SOFTMMU + mem_index = *args; + addrlo = tcg_out_tlb_read(s, s_bits, addrlo, addrhi, mem_index, false); + + /* Load a pointer into the current opcode w/conditional branch-link. */ + label_ptr = s->code_ptr; + tcg_out_bc_noaddr(s, BC | BI(7, CR_EQ) | BO_COND_FALSE | LK); + + rbase = TCG_REG_R3; +#else /* !CONFIG_SOFTMMU */ + rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0; + if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { + tcg_out_ext32u(s, TCG_REG_TMP1, addrlo); + addrlo = TCG_REG_TMP1; + } +#endif + + if (TCG_TARGET_REG_BITS == 32 && s_bits == MO_64) { + if (opc & MO_BSWAP) { + tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); + tcg_out32(s, STWBRX | SAB(datalo, rbase, addrlo)); + tcg_out32(s, STWBRX | SAB(datahi, rbase, TCG_REG_R0)); + } else if (rbase != 0) { + tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4)); + tcg_out32(s, STWX | SAB(datahi, rbase, addrlo)); + tcg_out32(s, STWX | SAB(datalo, rbase, TCG_REG_R0)); + } else { + tcg_out32(s, STW | TAI(datahi, addrlo, 0)); + tcg_out32(s, STW | TAI(datalo, addrlo, 4)); + } } else { - /* patch the branch destination */ - if (disp != 16) { - *ptr = 0x48000000 | (disp & 0x03fffffc); /* b disp */ - patch_size = 4; + uint32_t insn = qemu_stx_opc[opc]; + if (!HAVE_ISA_2_06 && insn == STDBRX) { + tcg_out32(s, STWBRX | SAB(datalo, rbase, addrlo)); + tcg_out32(s, ADDI | TAI(TCG_REG_TMP1, addrlo, 4)); + tcg_out_shri64(s, TCG_REG_R0, datalo, 32); + tcg_out32(s, STWBRX | SAB(TCG_REG_R0, rbase, TCG_REG_TMP1)); } else { - ptr[0] = 0x60000000; /* nop */ - ptr[1] = 0x60000000; - ptr[2] = 0x60000000; - ptr[3] = 0x60000000; - patch_size = 16; + tcg_out32(s, insn | SAB(datalo, rbase, addrlo)); } } - /* flush icache */ - flush_icache_range(jmp_addr, jmp_addr + patch_size); + +#ifdef CONFIG_SOFTMMU + add_qemu_ldst_label(s, false, opc, datalo, datahi, addrlo, addrhi, + mem_index, s->code_ptr, label_ptr); +#endif +} + +/* Parameters for function call generation, used in tcg.c. */ +#define TCG_TARGET_STACK_ALIGN 16 +#define TCG_TARGET_EXTEND_ARGS 1 + +#ifdef _CALL_AIX +# define LINK_AREA_SIZE (6 * SZR) +# define LR_OFFSET (1 * SZR) +# define TCG_TARGET_CALL_STACK_OFFSET (LINK_AREA_SIZE + 8 * SZR) +#elif TCG_TARGET_REG_BITS == 64 +# if defined(_CALL_ELF) && _CALL_ELF == 2 +# define LINK_AREA_SIZE (4 * SZR) +# define LR_OFFSET (1 * SZR) +# endif +#else /* TCG_TARGET_REG_BITS == 32 */ +# if defined(_CALL_SYSV) +# define LINK_AREA_SIZE (2 * SZR) +# define LR_OFFSET (1 * SZR) +# elif defined(TCG_TARGET_CALL_DARWIN) +# define LINK_AREA_SIZE 24 +# define LR_OFFSET 8 +# endif +#endif +#ifndef LR_OFFSET +# error "Unhandled abi" +#endif +#ifndef TCG_TARGET_CALL_STACK_OFFSET +# define TCG_TARGET_CALL_STACK_OFFSET LINK_AREA_SIZE +#endif + +#define CPU_TEMP_BUF_SIZE (CPU_TEMP_BUF_NLONGS * (int)sizeof(long)) +#define REG_SAVE_SIZE ((int)ARRAY_SIZE(tcg_target_callee_save_regs) * SZR) + +#define FRAME_SIZE ((TCG_TARGET_CALL_STACK_OFFSET \ + + TCG_STATIC_CALL_ARGS_SIZE \ + + CPU_TEMP_BUF_SIZE \ + + REG_SAVE_SIZE \ + + TCG_TARGET_STACK_ALIGN - 1) \ + & -TCG_TARGET_STACK_ALIGN) + +#define REG_SAVE_BOT (FRAME_SIZE - REG_SAVE_SIZE) + +static void tcg_target_qemu_prologue(TCGContext *s) +{ + int i; + +#ifdef _CALL_AIX + void **desc = (void **)s->code_ptr; + desc[0] = desc + 2; /* entry point */ + desc[1] = 0; /* environment pointer */ + s->code_ptr = (void *)(desc + 2); /* skip over descriptor */ +#endif + + tcg_set_frame(s, TCG_REG_CALL_STACK, REG_SAVE_BOT - CPU_TEMP_BUF_SIZE, + CPU_TEMP_BUF_SIZE); + + /* Prologue */ + tcg_out32(s, MFSPR | RT(TCG_REG_R0) | LR); + tcg_out32(s, (SZR == 8 ? STDU : STWU) + | SAI(TCG_REG_R1, TCG_REG_R1, -FRAME_SIZE)); + + for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); ++i) { + tcg_out_st(s, TCG_TYPE_REG, tcg_target_callee_save_regs[i], + TCG_REG_R1, REG_SAVE_BOT + i * SZR); + } + tcg_out_st(s, TCG_TYPE_PTR, TCG_REG_R0, TCG_REG_R1, FRAME_SIZE+LR_OFFSET); + +#ifdef CONFIG_USE_GUEST_BASE + if (GUEST_BASE) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, GUEST_BASE); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + } +#endif + + tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); + tcg_out32(s, MTSPR | RS(tcg_target_call_iarg_regs[1]) | CTR); + + if (USE_REG_RA) { +#ifdef _CALL_AIX + /* Make the caller load the value as the TOC into R2. */ + tb_ret_addr = s->code_ptr + 2; + desc[1] = tb_ret_addr; + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_RA, TCG_REG_R2); + tcg_out32(s, BCCTR | BO_ALWAYS); +#elif defined(_CALL_ELF) && _CALL_ELF == 2 + /* Compute from the incoming R12 value. */ + tb_ret_addr = s->code_ptr + 2; + tcg_out32(s, ADDI | TAI(TCG_REG_RA, TCG_REG_R12, + tcg_ptr_byte_diff(tb_ret_addr, s->code_buf))); + tcg_out32(s, BCCTR | BO_ALWAYS); +#else + /* Reserve max 5 insns for the constant load. */ + tb_ret_addr = s->code_ptr + 6; + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RA, (intptr_t)tb_ret_addr); + tcg_out32(s, BCCTR | BO_ALWAYS); + while (s->code_ptr < tb_ret_addr) { + tcg_out32(s, NOP); + } +#endif + } else { + tcg_out32(s, BCCTR | BO_ALWAYS); + tb_ret_addr = s->code_ptr; + } + + /* Epilogue */ + assert(tb_ret_addr == s->code_ptr); + + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R0, TCG_REG_R1, FRAME_SIZE+LR_OFFSET); + for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); ++i) { + tcg_out_ld(s, TCG_TYPE_REG, tcg_target_callee_save_regs[i], + TCG_REG_R1, REG_SAVE_BOT + i * SZR); + } + tcg_out32(s, MTSPR | RS(TCG_REG_R0) | LR); + tcg_out32(s, ADDI | TAI(TCG_REG_R1, TCG_REG_R1, FRAME_SIZE)); + tcg_out32(s, BCLR | BO_ALWAYS); } static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, const int *const_args) { + TCGArg a0, a1, a2; + int c; + switch (opc) { case INDEX_op_exit_tb: - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R3, args[0]); + if (USE_REG_RA) { + ptrdiff_t disp = tcg_pcrel_diff(s, tb_ret_addr); + + /* If we can use a direct branch, otherwise use the value in RA. + Note that the direct branch is always forward. If it's in + range now, it'll still be in range after the movi. Don't + bother about the 20 bytes where the test here fails but it + would succeed below. */ + if (!in_range_b(disp)) { + tcg_out32(s, MTSPR | RS(TCG_REG_RA) | CTR); + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R3, args[0]); + tcg_out32(s, BCCTR | BO_ALWAYS); + break; + } + } + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R3, args[0]); tcg_out_b(s, 0, tb_ret_addr); break; case INDEX_op_goto_tb: if (s->tb_jmp_offset) { - /* direct jump method */ + /* Direct jump method. */ s->tb_jmp_offset[args[0]] = tcg_current_code_size(s); - s->code_ptr += 4; + s->code_ptr += 7; } else { - tcg_abort (); + /* Indirect jump method. */ + tcg_abort(); } s->tb_next_offset[args[0]] = tcg_current_code_size(s); break; @@ -1374,451 +1868,519 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, if (l->has_value) { tcg_out_b(s, 0, l->u.value_ptr); } else { - /* Thanks to Andrzej Zaborowski */ - tcg_insn_unit retrans = *s->code_ptr & 0x3fffffc; tcg_out_reloc(s, s->code_ptr, R_PPC_REL24, args[0], 0); - tcg_out32(s, B | retrans); + tcg_out_b_noaddr(s, B); } } break; case INDEX_op_ld8u_i32: - tcg_out_ldst (s, args[0], args[1], args[2], LBZ, LBZX); + case INDEX_op_ld8u_i64: + tcg_out_mem_long(s, LBZ, LBZX, args[0], args[1], args[2]); break; case INDEX_op_ld8s_i32: - tcg_out_ldst (s, args[0], args[1], args[2], LBZ, LBZX); - tcg_out32 (s, EXTSB | RS (args[0]) | RA (args[0])); + case INDEX_op_ld8s_i64: + tcg_out_mem_long(s, LBZ, LBZX, args[0], args[1], args[2]); + tcg_out32(s, EXTSB | RS(args[0]) | RA(args[0])); break; case INDEX_op_ld16u_i32: - tcg_out_ldst (s, args[0], args[1], args[2], LHZ, LHZX); + case INDEX_op_ld16u_i64: + tcg_out_mem_long(s, LHZ, LHZX, args[0], args[1], args[2]); break; case INDEX_op_ld16s_i32: - tcg_out_ldst (s, args[0], args[1], args[2], LHA, LHAX); + case INDEX_op_ld16s_i64: + tcg_out_mem_long(s, LHA, LHAX, args[0], args[1], args[2]); break; case INDEX_op_ld_i32: - tcg_out_ldst (s, args[0], args[1], args[2], LWZ, LWZX); + case INDEX_op_ld32u_i64: + tcg_out_mem_long(s, LWZ, LWZX, args[0], args[1], args[2]); + break; + case INDEX_op_ld32s_i64: + tcg_out_mem_long(s, LWA, LWAX, args[0], args[1], args[2]); + break; + case INDEX_op_ld_i64: + tcg_out_mem_long(s, LD, LDX, args[0], args[1], args[2]); break; case INDEX_op_st8_i32: - tcg_out_ldst (s, args[0], args[1], args[2], STB, STBX); + case INDEX_op_st8_i64: + tcg_out_mem_long(s, STB, STBX, args[0], args[1], args[2]); break; case INDEX_op_st16_i32: - tcg_out_ldst (s, args[0], args[1], args[2], STH, STHX); + case INDEX_op_st16_i64: + tcg_out_mem_long(s, STH, STHX, args[0], args[1], args[2]); break; case INDEX_op_st_i32: - tcg_out_ldst (s, args[0], args[1], args[2], STW, STWX); + case INDEX_op_st32_i64: + tcg_out_mem_long(s, STW, STWX, args[0], args[1], args[2]); + break; + case INDEX_op_st_i64: + tcg_out_mem_long(s, STD, STDX, args[0], args[1], args[2]); break; case INDEX_op_add_i32: - if (const_args[2]) - ppc_addi (s, args[0], args[1], args[2]); - else - tcg_out32 (s, ADD | TAB (args[0], args[1], args[2])); + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + do_addi_32: + tcg_out_mem_long(s, ADDI, ADD, a0, a1, (int32_t)a2); + } else { + tcg_out32(s, ADD | TAB(a0, a1, a2)); + } break; case INDEX_op_sub_i32: - if (const_args[2]) - ppc_addi (s, args[0], args[1], -args[2]); - else - tcg_out32 (s, SUBF | TAB (args[0], args[2], args[1])); + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[1]) { + if (const_args[2]) { + tcg_out_movi(s, TCG_TYPE_I32, a0, a1 - a2); + } else { + tcg_out32(s, SUBFIC | TAI(a0, a2, a1)); + } + } else if (const_args[2]) { + a2 = -a2; + goto do_addi_32; + } else { + tcg_out32(s, SUBF | TAB(a0, a2, a1)); + } break; case INDEX_op_and_i32: + a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { - uint32_t c; - - c = args[2]; - - if (!c) { - tcg_out_movi (s, TCG_TYPE_I32, args[0], 0); - break; - } -#ifdef __PPU__ - uint32_t t, n; - int mb, me; - - n = c ^ -(c & 1); - t = n + (n & -n); - - if ((t & (t - 1)) == 0) { - int lzc, tzc; - - if ((c & 0x80000001) == 0x80000001) { - lzc = clz32 (n); - tzc = ctz32 (n); - - mb = 32 - tzc; - me = lzc - 1; - } - else { - lzc = clz32 (c); - tzc = ctz32 (c); - - mb = lzc; - me = 31 - tzc; - } - - tcg_out32 (s, (RLWINM - | RA (args[0]) - | RS (args[1]) - | SH (0) - | MB (mb) - | ME (me) - ) - ); - } - else -#endif /* !__PPU__ */ - { - if ((c & 0xffff) == c) - tcg_out32 (s, ANDI | RS (args[1]) | RA (args[0]) | c); - else if ((c & 0xffff0000) == c) - tcg_out32 (s, ANDIS | RS (args[1]) | RA (args[0]) - | ((c >> 16) & 0xffff)); - else { - tcg_out_movi (s, TCG_TYPE_I32, 0, c); - tcg_out32 (s, AND | SAB (args[1], args[0], 0)); - } - } + tcg_out_andi32(s, a0, a1, a2); + } else { + tcg_out32(s, AND | SAB(a1, a0, a2)); } - else - tcg_out32 (s, AND | SAB (args[1], args[0], args[2])); break; + case INDEX_op_and_i64: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + tcg_out_andi64(s, a0, a1, a2); + } else { + tcg_out32(s, AND | SAB(a1, a0, a2)); + } + break; + case INDEX_op_or_i64: case INDEX_op_or_i32: + a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { - if (args[2] & 0xffff) { - tcg_out32 (s, ORI | RS (args[1]) | RA (args[0]) - | (args[2] & 0xffff)); - if (args[2] >> 16) - tcg_out32 (s, ORIS | RS (args[0]) | RA (args[0]) - | ((args[2] >> 16) & 0xffff)); - } - else { - tcg_out32 (s, ORIS | RS (args[1]) | RA (args[0]) - | ((args[2] >> 16) & 0xffff)); - } + tcg_out_ori32(s, a0, a1, a2); + } else { + tcg_out32(s, OR | SAB(a1, a0, a2)); } - else - tcg_out32 (s, OR | SAB (args[1], args[0], args[2])); break; + case INDEX_op_xor_i64: case INDEX_op_xor_i32: + a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { - if ((args[2] & 0xffff) == args[2]) - tcg_out32 (s, XORI | RS (args[1]) | RA (args[0]) - | (args[2] & 0xffff)); - else if ((args[2] & 0xffff0000) == args[2]) - tcg_out32 (s, XORIS | RS (args[1]) | RA (args[0]) - | ((args[2] >> 16) & 0xffff)); - else { - tcg_out_movi (s, TCG_TYPE_I32, 0, args[2]); - tcg_out32 (s, XOR | SAB (args[1], args[0], 0)); - } + tcg_out_xori32(s, a0, a1, a2); + } else { + tcg_out32(s, XOR | SAB(a1, a0, a2)); } - else - tcg_out32 (s, XOR | SAB (args[1], args[0], args[2])); break; case INDEX_op_andc_i32: - tcg_out32 (s, ANDC | SAB (args[1], args[0], args[2])); + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + tcg_out_andi32(s, a0, a1, ~a2); + } else { + tcg_out32(s, ANDC | SAB(a1, a0, a2)); + } + break; + case INDEX_op_andc_i64: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + tcg_out_andi64(s, a0, a1, ~a2); + } else { + tcg_out32(s, ANDC | SAB(a1, a0, a2)); + } break; case INDEX_op_orc_i32: - tcg_out32 (s, ORC | SAB (args[1], args[0], args[2])); + if (const_args[2]) { + tcg_out_ori32(s, args[0], args[1], ~args[2]); + break; + } + /* FALLTHRU */ + case INDEX_op_orc_i64: + tcg_out32(s, ORC | SAB(args[1], args[0], args[2])); break; case INDEX_op_eqv_i32: - tcg_out32 (s, EQV | SAB (args[1], args[0], args[2])); + if (const_args[2]) { + tcg_out_xori32(s, args[0], args[1], ~args[2]); + break; + } + /* FALLTHRU */ + case INDEX_op_eqv_i64: + tcg_out32(s, EQV | SAB(args[1], args[0], args[2])); break; case INDEX_op_nand_i32: - tcg_out32 (s, NAND | SAB (args[1], args[0], args[2])); + case INDEX_op_nand_i64: + tcg_out32(s, NAND | SAB(args[1], args[0], args[2])); break; case INDEX_op_nor_i32: - tcg_out32 (s, NOR | SAB (args[1], args[0], args[2])); + case INDEX_op_nor_i64: + tcg_out32(s, NOR | SAB(args[1], args[0], args[2])); break; case INDEX_op_mul_i32: + a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { - if (args[2] == (int16_t) args[2]) - tcg_out32 (s, MULLI | RT (args[0]) | RA (args[1]) - | (args[2] & 0xffff)); - else { - tcg_out_movi (s, TCG_TYPE_I32, 0, args[2]); - tcg_out32 (s, MULLW | TAB (args[0], args[1], 0)); - } + tcg_out32(s, MULLI | TAI(a0, a1, a2)); + } else { + tcg_out32(s, MULLW | TAB(a0, a1, a2)); } - else - tcg_out32 (s, MULLW | TAB (args[0], args[1], args[2])); break; case INDEX_op_div_i32: - tcg_out32 (s, DIVW | TAB (args[0], args[1], args[2])); + tcg_out32(s, DIVW | TAB(args[0], args[1], args[2])); break; case INDEX_op_divu_i32: - tcg_out32 (s, DIVWU | TAB (args[0], args[1], args[2])); - break; - - case INDEX_op_mulu2_i32: - if (args[0] == args[2] || args[0] == args[3]) { - tcg_out32 (s, MULLW | TAB (0, args[2], args[3])); - tcg_out32 (s, MULHWU | TAB (args[1], args[2], args[3])); - tcg_out_mov (s, TCG_TYPE_I32, args[0], 0); - } - else { - tcg_out32 (s, MULLW | TAB (args[0], args[2], args[3])); - tcg_out32 (s, MULHWU | TAB (args[1], args[2], args[3])); - } + tcg_out32(s, DIVWU | TAB(args[0], args[1], args[2])); break; case INDEX_op_shl_i32: if (const_args[2]) { - tcg_out32 (s, (RLWINM - | RA (args[0]) - | RS (args[1]) - | SH (args[2]) - | MB (0) - | ME (31 - args[2]) - ) - ); - } - else - tcg_out32 (s, SLW | SAB (args[1], args[0], args[2])); + tcg_out_shli32(s, args[0], args[1], args[2]); + } else { + tcg_out32(s, SLW | SAB(args[1], args[0], args[2])); + } break; case INDEX_op_shr_i32: if (const_args[2]) { - tcg_out32 (s, (RLWINM - | RA (args[0]) - | RS (args[1]) - | SH (32 - args[2]) - | MB (args[2]) - | ME (31) - ) - ); - } - else - tcg_out32 (s, SRW | SAB (args[1], args[0], args[2])); + tcg_out_shri32(s, args[0], args[1], args[2]); + } else { + tcg_out32(s, SRW | SAB(args[1], args[0], args[2])); + } break; case INDEX_op_sar_i32: - if (const_args[2]) - tcg_out32 (s, SRAWI | RS (args[1]) | RA (args[0]) | SH (args[2])); - else - tcg_out32 (s, SRAW | SAB (args[1], args[0], args[2])); + if (const_args[2]) { + tcg_out32(s, SRAWI | RS(args[1]) | RA(args[0]) | SH(args[2])); + } else { + tcg_out32(s, SRAW | SAB(args[1], args[0], args[2])); + } break; case INDEX_op_rotl_i32: - { - int op = 0 - | RA (args[0]) - | RS (args[1]) - | MB (0) - | ME (31) - | (const_args[2] ? RLWINM | SH (args[2]) - : RLWNM | RB (args[2])) - ; - tcg_out32 (s, op); + if (const_args[2]) { + tcg_out_rlw(s, RLWINM, args[0], args[1], args[2], 0, 31); + } else { + tcg_out32(s, RLWNM | SAB(args[1], args[0], args[2]) + | MB(0) | ME(31)); } break; case INDEX_op_rotr_i32: if (const_args[2]) { - if (!args[2]) { - tcg_out_mov (s, TCG_TYPE_I32, args[0], args[1]); - } - else { - tcg_out32 (s, RLWINM - | RA (args[0]) - | RS (args[1]) - | SH (32 - args[2]) - | MB (0) - | ME (31) - ); - } - } - else { - tcg_out32 (s, SUBFIC | RT (0) | RA (args[2]) | 32); - tcg_out32 (s, RLWNM - | RA (args[0]) - | RS (args[1]) - | RB (0) - | MB (0) - | ME (31) - ); - } - break; - - case INDEX_op_add2_i32: - if (args[0] == args[3] || args[0] == args[5]) { - tcg_out32 (s, ADDC | TAB (0, args[2], args[4])); - tcg_out32 (s, ADDE | TAB (args[1], args[3], args[5])); - tcg_out_mov (s, TCG_TYPE_I32, args[0], 0); - } - else { - tcg_out32 (s, ADDC | TAB (args[0], args[2], args[4])); - tcg_out32 (s, ADDE | TAB (args[1], args[3], args[5])); - } - break; - case INDEX_op_sub2_i32: - if (args[0] == args[3] || args[0] == args[5]) { - tcg_out32 (s, SUBFC | TAB (0, args[4], args[2])); - tcg_out32 (s, SUBFE | TAB (args[1], args[5], args[3])); - tcg_out_mov (s, TCG_TYPE_I32, args[0], 0); - } - else { - tcg_out32 (s, SUBFC | TAB (args[0], args[4], args[2])); - tcg_out32 (s, SUBFE | TAB (args[1], args[5], args[3])); + tcg_out_rlw(s, RLWINM, args[0], args[1], 32 - args[2], 0, 31); + } else { + tcg_out32(s, SUBFIC | TAI(TCG_REG_R0, args[2], 32)); + tcg_out32(s, RLWNM | SAB(args[1], args[0], TCG_REG_R0) + | MB(0) | ME(31)); } break; case INDEX_op_brcond_i32: - /* - args[0] = r0 - args[1] = r1 - args[2] = cond - args[3] = r1 is const - args[4] = label_index - */ - tcg_out_brcond (s, args[2], args[0], args[1], const_args[1], args[3]); + tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], + args[3], TCG_TYPE_I32); + break; + case INDEX_op_brcond_i64: + tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], + args[3], TCG_TYPE_I64); break; case INDEX_op_brcond2_i32: tcg_out_brcond2(s, args, const_args); break; case INDEX_op_neg_i32: - tcg_out32 (s, NEG | RT (args[0]) | RA (args[1])); + case INDEX_op_neg_i64: + tcg_out32(s, NEG | RT(args[0]) | RA(args[1])); break; case INDEX_op_not_i32: - tcg_out32 (s, NOR | SAB (args[1], args[0], args[1])); + case INDEX_op_not_i64: + tcg_out32(s, NOR | SAB(args[1], args[0], args[1])); + break; + + case INDEX_op_add_i64: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + do_addi_64: + tcg_out_mem_long(s, ADDI, ADD, a0, a1, a2); + } else { + tcg_out32(s, ADD | TAB(a0, a1, a2)); + } + break; + case INDEX_op_sub_i64: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[1]) { + if (const_args[2]) { + tcg_out_movi(s, TCG_TYPE_I64, a0, a1 - a2); + } else { + tcg_out32(s, SUBFIC | TAI(a0, a2, a1)); + } + } else if (const_args[2]) { + a2 = -a2; + goto do_addi_64; + } else { + tcg_out32(s, SUBF | TAB(a0, a2, a1)); + } + break; + + case INDEX_op_shl_i64: + if (const_args[2]) { + tcg_out_shli64(s, args[0], args[1], args[2]); + } else { + tcg_out32(s, SLD | SAB(args[1], args[0], args[2])); + } + break; + case INDEX_op_shr_i64: + if (const_args[2]) { + tcg_out_shri64(s, args[0], args[1], args[2]); + } else { + tcg_out32(s, SRD | SAB(args[1], args[0], args[2])); + } + break; + case INDEX_op_sar_i64: + if (const_args[2]) { + int sh = SH(args[2] & 0x1f) | (((args[2] >> 5) & 1) << 1); + tcg_out32(s, SRADI | RA(args[0]) | RS(args[1]) | sh); + } else { + tcg_out32(s, SRAD | SAB(args[1], args[0], args[2])); + } + break; + case INDEX_op_rotl_i64: + if (const_args[2]) { + tcg_out_rld(s, RLDICL, args[0], args[1], args[2], 0); + } else { + tcg_out32(s, RLDCL | SAB(args[1], args[0], args[2]) | MB64(0)); + } + break; + case INDEX_op_rotr_i64: + if (const_args[2]) { + tcg_out_rld(s, RLDICL, args[0], args[1], 64 - args[2], 0); + } else { + tcg_out32(s, SUBFIC | TAI(TCG_REG_R0, args[2], 64)); + tcg_out32(s, RLDCL | SAB(args[1], args[0], TCG_REG_R0) | MB64(0)); + } + break; + + case INDEX_op_mul_i64: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + tcg_out32(s, MULLI | TAI(a0, a1, a2)); + } else { + tcg_out32(s, MULLD | TAB(a0, a1, a2)); + } + break; + case INDEX_op_div_i64: + tcg_out32(s, DIVD | TAB(args[0], args[1], args[2])); + break; + case INDEX_op_divu_i64: + tcg_out32(s, DIVDU | TAB(args[0], args[1], args[2])); break; case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, args, 0); + tcg_out_qemu_ld(s, args, false); break; case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, args, 1); + tcg_out_qemu_ld(s, args, true); break; case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, args, 0); + tcg_out_qemu_st(s, args, false); break; case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, args, 1); + tcg_out_qemu_st(s, args, true); break; case INDEX_op_ext8s_i32: - tcg_out32 (s, EXTSB | RS (args[1]) | RA (args[0])); - break; - case INDEX_op_ext8u_i32: - tcg_out32 (s, RLWINM - | RA (args[0]) - | RS (args[1]) - | SH (0) - | MB (24) - | ME (31) - ); - break; + case INDEX_op_ext8s_i64: + c = EXTSB; + goto gen_ext; case INDEX_op_ext16s_i32: - tcg_out32 (s, EXTSH | RS (args[1]) | RA (args[0])); - break; - case INDEX_op_ext16u_i32: - tcg_out32 (s, RLWINM - | RA (args[0]) - | RS (args[1]) - | SH (0) - | MB (16) - | ME (31) - ); + case INDEX_op_ext16s_i64: + c = EXTSH; + goto gen_ext; + case INDEX_op_ext32s_i64: + c = EXTSW; + goto gen_ext; + gen_ext: + tcg_out32(s, c | RS(args[1]) | RA(args[0])); break; case INDEX_op_setcond_i32: - tcg_out_setcond (s, args[3], args[0], args[1], args[2], const_args[2]); + tcg_out_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], args[2], + const_args[2]); + break; + case INDEX_op_setcond_i64: + tcg_out_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1], args[2], + const_args[2]); break; case INDEX_op_setcond2_i32: - tcg_out_setcond2 (s, args, const_args); + tcg_out_setcond2(s, args, const_args); break; case INDEX_op_bswap16_i32: - /* Stolen from gcc's builtin_bswap16 */ - + case INDEX_op_bswap16_i64: + a0 = args[0], a1 = args[1]; /* a1 = abcd */ - - /* r0 = (a1 << 8) & 0xff00 # 00d0 */ - tcg_out32 (s, RLWINM - | RA (0) - | RS (args[1]) - | SH (8) - | MB (16) - | ME (23) - ); - - /* a0 = rotate_left (a1, 24) & 0xff # 000c */ - tcg_out32 (s, RLWINM - | RA (args[0]) - | RS (args[1]) - | SH (24) - | MB (24) - | ME (31) - ); - - /* a0 = a0 | r0 # 00dc */ - tcg_out32 (s, OR | SAB (0, args[0], args[0])); + if (a0 != a1) { + /* a0 = (a1 r<< 24) & 0xff # 000c */ + tcg_out_rlw(s, RLWINM, a0, a1, 24, 24, 31); + /* a0 = (a0 & ~0xff00) | (a1 r<< 8) & 0xff00 # 00dc */ + tcg_out_rlw(s, RLWIMI, a0, a1, 8, 16, 23); + } else { + /* r0 = (a1 r<< 8) & 0xff00 # 00d0 */ + tcg_out_rlw(s, RLWINM, TCG_REG_R0, a1, 8, 16, 23); + /* a0 = (a1 r<< 24) & 0xff # 000c */ + tcg_out_rlw(s, RLWINM, a0, a1, 24, 24, 31); + /* a0 = a0 | r0 # 00dc */ + tcg_out32(s, OR | SAB(TCG_REG_R0, a0, a0)); + } break; case INDEX_op_bswap32_i32: + case INDEX_op_bswap32_i64: /* Stolen from gcc's builtin_bswap32 */ - { - int a0 = args[0]; - - /* a1 = args[1] # abcd */ + a1 = args[1]; + a0 = args[0] == a1 ? TCG_REG_R0 : args[0]; + + /* a1 = args[1] # abcd */ + /* a0 = rotate_left (a1, 8) # bcda */ + tcg_out_rlw(s, RLWINM, a0, a1, 8, 0, 31); + /* a0 = (a0 & ~0xff000000) | ((a1 r<< 24) & 0xff000000) # dcda */ + tcg_out_rlw(s, RLWIMI, a0, a1, 24, 0, 7); + /* a0 = (a0 & ~0x0000ff00) | ((a1 r<< 24) & 0x0000ff00) # dcba */ + tcg_out_rlw(s, RLWIMI, a0, a1, 24, 16, 23); + + if (a0 == TCG_REG_R0) { + tcg_out_mov(s, TCG_TYPE_REG, args[0], a0); + } + break; - if (a0 == args[1]) { - a0 = 0; - } + case INDEX_op_bswap64_i64: + a0 = args[0], a1 = args[1], a2 = TCG_REG_R0; + if (a0 == a1) { + a0 = TCG_REG_R0; + a2 = a1; + } - /* a0 = rotate_left (a1, 8) # bcda */ - tcg_out32 (s, RLWINM - | RA (a0) - | RS (args[1]) - | SH (8) - | MB (0) - | ME (31) - ); - - /* a0 = (a0 & ~0xff000000) | ((a1 << 24) & 0xff000000) # dcda */ - tcg_out32 (s, RLWIMI - | RA (a0) - | RS (args[1]) - | SH (24) - | MB (0) - | ME (7) - ); - - /* a0 = (a0 & ~0x0000ff00) | ((a1 << 24) & 0x0000ff00) # dcba */ - tcg_out32 (s, RLWIMI - | RA (a0) - | RS (args[1]) - | SH (24) - | MB (16) - | ME (23) - ); - - if (!a0) { - tcg_out_mov (s, TCG_TYPE_I32, args[0], a0); - } + /* a1 = # abcd efgh */ + /* a0 = rl32(a1, 8) # 0000 fghe */ + tcg_out_rlw(s, RLWINM, a0, a1, 8, 0, 31); + /* a0 = dep(a0, rl32(a1, 24), 0xff000000) # 0000 hghe */ + tcg_out_rlw(s, RLWIMI, a0, a1, 24, 0, 7); + /* a0 = dep(a0, rl32(a1, 24), 0x0000ff00) # 0000 hgfe */ + tcg_out_rlw(s, RLWIMI, a0, a1, 24, 16, 23); + + /* a0 = rl64(a0, 32) # hgfe 0000 */ + /* a2 = rl64(a1, 32) # efgh abcd */ + tcg_out_rld(s, RLDICL, a0, a0, 32, 0); + tcg_out_rld(s, RLDICL, a2, a1, 32, 0); + + /* a0 = dep(a0, rl32(a2, 8), 0xffffffff) # hgfe bcda */ + tcg_out_rlw(s, RLWIMI, a0, a2, 8, 0, 31); + /* a0 = dep(a0, rl32(a2, 24), 0xff000000) # hgfe dcda */ + tcg_out_rlw(s, RLWIMI, a0, a2, 24, 0, 7); + /* a0 = dep(a0, rl32(a2, 24), 0x0000ff00) # hgfe dcba */ + tcg_out_rlw(s, RLWIMI, a0, a2, 24, 16, 23); + + if (a0 == 0) { + tcg_out_mov(s, TCG_TYPE_REG, args[0], a0); } break; case INDEX_op_deposit_i32: - tcg_out32 (s, RLWIMI - | RA (args[0]) - | RS (args[2]) - | SH (args[3]) - | MB (32 - args[3] - args[4]) - | ME (31 - args[3]) - ); + if (const_args[2]) { + uint32_t mask = ((2u << (args[4] - 1)) - 1) << args[3]; + tcg_out_andi32(s, args[0], args[0], ~mask); + } else { + tcg_out_rlw(s, RLWIMI, args[0], args[2], args[3], + 32 - args[3] - args[4], 31 - args[3]); + } + break; + case INDEX_op_deposit_i64: + if (const_args[2]) { + uint64_t mask = ((2ull << (args[4] - 1)) - 1) << args[3]; + tcg_out_andi64(s, args[0], args[0], ~mask); + } else { + tcg_out_rld(s, RLDIMI, args[0], args[2], args[3], + 64 - args[3] - args[4]); + } break; case INDEX_op_movcond_i32: - tcg_out_movcond (s, args[5], args[0], - args[1], args[2], - args[3], args[4], - const_args[2]); + tcg_out_movcond(s, TCG_TYPE_I32, args[5], args[0], args[1], args[2], + args[3], args[4], const_args[2]); + break; + case INDEX_op_movcond_i64: + tcg_out_movcond(s, TCG_TYPE_I64, args[5], args[0], args[1], args[2], + args[3], args[4], const_args[2]); + break; + +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_add2_i64: +#else + case INDEX_op_add2_i32: +#endif + /* Note that the CA bit is defined based on the word size of the + environment. So in 64-bit mode it's always carry-out of bit 63. + The fallback code using deposit works just as well for 32-bit. */ + a0 = args[0], a1 = args[1]; + if (a0 == args[3] || (!const_args[5] && a0 == args[5])) { + a0 = TCG_REG_R0; + } + if (const_args[4]) { + tcg_out32(s, ADDIC | TAI(a0, args[2], args[4])); + } else { + tcg_out32(s, ADDC | TAB(a0, args[2], args[4])); + } + if (const_args[5]) { + tcg_out32(s, (args[5] ? ADDME : ADDZE) | RT(a1) | RA(args[3])); + } else { + tcg_out32(s, ADDE | TAB(a1, args[3], args[5])); + } + if (a0 != args[0]) { + tcg_out_mov(s, TCG_TYPE_REG, args[0], a0); + } break; - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ - case INDEX_op_call: /* Always emitted via tcg_out_call. */ +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_sub2_i64: +#else + case INDEX_op_sub2_i32: +#endif + a0 = args[0], a1 = args[1]; + if (a0 == args[5] || (!const_args[3] && a0 == args[3])) { + a0 = TCG_REG_R0; + } + if (const_args[2]) { + tcg_out32(s, SUBFIC | TAI(a0, args[4], args[2])); + } else { + tcg_out32(s, SUBFC | TAB(a0, args[4], args[2])); + } + if (const_args[3]) { + tcg_out32(s, (args[3] ? SUBFME : SUBFZE) | RT(a1) | RA(args[5])); + } else { + tcg_out32(s, SUBFE | TAB(a1, args[5], args[3])); + } + if (a0 != args[0]) { + tcg_out_mov(s, TCG_TYPE_REG, args[0], a0); + } + break; + + case INDEX_op_muluh_i32: + tcg_out32(s, MULHWU | TAB(args[0], args[1], args[2])); + break; + case INDEX_op_mulsh_i32: + tcg_out32(s, MULHW | TAB(args[0], args[1], args[2])); + break; + case INDEX_op_muluh_i64: + tcg_out32(s, MULHDU | TAB(args[0], args[1], args[2])); + break; + case INDEX_op_mulsh_i64: + tcg_out32(s, MULHD | TAB(args[0], args[1], args[2])); + break; + + case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ + case INDEX_op_mov_i64: + case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ + case INDEX_op_movi_i64: + case INDEX_op_call: /* Always emitted via tcg_out_call. */ default: tcg_abort(); } @@ -1834,79 +2396,146 @@ static const TCGTargetOpDef ppc_op_defs[] = { { INDEX_op_ld16u_i32, { "r", "r" } }, { INDEX_op_ld16s_i32, { "r", "r" } }, { INDEX_op_ld_i32, { "r", "r" } }, + { INDEX_op_st8_i32, { "r", "r" } }, { INDEX_op_st16_i32, { "r", "r" } }, { INDEX_op_st_i32, { "r", "r" } }, { INDEX_op_add_i32, { "r", "r", "ri" } }, - { INDEX_op_mul_i32, { "r", "r", "ri" } }, + { INDEX_op_mul_i32, { "r", "r", "rI" } }, { INDEX_op_div_i32, { "r", "r", "r" } }, { INDEX_op_divu_i32, { "r", "r", "r" } }, - { INDEX_op_mulu2_i32, { "r", "r", "r", "r" } }, - { INDEX_op_sub_i32, { "r", "r", "ri" } }, + { INDEX_op_sub_i32, { "r", "rI", "ri" } }, { INDEX_op_and_i32, { "r", "r", "ri" } }, { INDEX_op_or_i32, { "r", "r", "ri" } }, { INDEX_op_xor_i32, { "r", "r", "ri" } }, + { INDEX_op_andc_i32, { "r", "r", "ri" } }, + { INDEX_op_orc_i32, { "r", "r", "ri" } }, + { INDEX_op_eqv_i32, { "r", "r", "ri" } }, + { INDEX_op_nand_i32, { "r", "r", "r" } }, + { INDEX_op_nor_i32, { "r", "r", "r" } }, { INDEX_op_shl_i32, { "r", "r", "ri" } }, { INDEX_op_shr_i32, { "r", "r", "ri" } }, { INDEX_op_sar_i32, { "r", "r", "ri" } }, - { INDEX_op_rotl_i32, { "r", "r", "ri" } }, { INDEX_op_rotr_i32, { "r", "r", "ri" } }, - { INDEX_op_brcond_i32, { "r", "ri" } }, - - { INDEX_op_add2_i32, { "r", "r", "r", "r", "r", "r" } }, - { INDEX_op_sub2_i32, { "r", "r", "r", "r", "r", "r" } }, - { INDEX_op_brcond2_i32, { "r", "r", "r", "r" } }, - { INDEX_op_neg_i32, { "r", "r" } }, { INDEX_op_not_i32, { "r", "r" } }, + { INDEX_op_ext8s_i32, { "r", "r" } }, + { INDEX_op_ext16s_i32, { "r", "r" } }, + { INDEX_op_bswap16_i32, { "r", "r" } }, + { INDEX_op_bswap32_i32, { "r", "r" } }, - { INDEX_op_andc_i32, { "r", "r", "r" } }, - { INDEX_op_orc_i32, { "r", "r", "r" } }, - { INDEX_op_eqv_i32, { "r", "r", "r" } }, - { INDEX_op_nand_i32, { "r", "r", "r" } }, - { INDEX_op_nor_i32, { "r", "r", "r" } }, - + { INDEX_op_brcond_i32, { "r", "ri" } }, { INDEX_op_setcond_i32, { "r", "r", "ri" } }, + { INDEX_op_movcond_i32, { "r", "r", "ri", "rZ", "rZ" } }, + + { INDEX_op_deposit_i32, { "r", "0", "rZ" } }, + + { INDEX_op_muluh_i32, { "r", "r", "r" } }, + { INDEX_op_mulsh_i32, { "r", "r", "r" } }, + +#if TCG_TARGET_REG_BITS == 64 + { INDEX_op_ld8u_i64, { "r", "r" } }, + { INDEX_op_ld8s_i64, { "r", "r" } }, + { INDEX_op_ld16u_i64, { "r", "r" } }, + { INDEX_op_ld16s_i64, { "r", "r" } }, + { INDEX_op_ld32u_i64, { "r", "r" } }, + { INDEX_op_ld32s_i64, { "r", "r" } }, + { INDEX_op_ld_i64, { "r", "r" } }, + + { INDEX_op_st8_i64, { "r", "r" } }, + { INDEX_op_st16_i64, { "r", "r" } }, + { INDEX_op_st32_i64, { "r", "r" } }, + { INDEX_op_st_i64, { "r", "r" } }, + + { INDEX_op_add_i64, { "r", "r", "rT" } }, + { INDEX_op_sub_i64, { "r", "rI", "rT" } }, + { INDEX_op_and_i64, { "r", "r", "ri" } }, + { INDEX_op_or_i64, { "r", "r", "rU" } }, + { INDEX_op_xor_i64, { "r", "r", "rU" } }, + { INDEX_op_andc_i64, { "r", "r", "ri" } }, + { INDEX_op_orc_i64, { "r", "r", "r" } }, + { INDEX_op_eqv_i64, { "r", "r", "r" } }, + { INDEX_op_nand_i64, { "r", "r", "r" } }, + { INDEX_op_nor_i64, { "r", "r", "r" } }, + + { INDEX_op_shl_i64, { "r", "r", "ri" } }, + { INDEX_op_shr_i64, { "r", "r", "ri" } }, + { INDEX_op_sar_i64, { "r", "r", "ri" } }, + { INDEX_op_rotl_i64, { "r", "r", "ri" } }, + { INDEX_op_rotr_i64, { "r", "r", "ri" } }, + + { INDEX_op_mul_i64, { "r", "r", "rI" } }, + { INDEX_op_div_i64, { "r", "r", "r" } }, + { INDEX_op_divu_i64, { "r", "r", "r" } }, + + { INDEX_op_neg_i64, { "r", "r" } }, + { INDEX_op_not_i64, { "r", "r" } }, + { INDEX_op_ext8s_i64, { "r", "r" } }, + { INDEX_op_ext16s_i64, { "r", "r" } }, + { INDEX_op_ext32s_i64, { "r", "r" } }, + { INDEX_op_bswap16_i64, { "r", "r" } }, + { INDEX_op_bswap32_i64, { "r", "r" } }, + { INDEX_op_bswap64_i64, { "r", "r" } }, + + { INDEX_op_brcond_i64, { "r", "ri" } }, + { INDEX_op_setcond_i64, { "r", "r", "ri" } }, + { INDEX_op_movcond_i64, { "r", "r", "ri", "rZ", "rZ" } }, + + { INDEX_op_deposit_i64, { "r", "0", "rZ" } }, + + { INDEX_op_mulsh_i64, { "r", "r", "r" } }, + { INDEX_op_muluh_i64, { "r", "r", "r" } }, +#endif + +#if TCG_TARGET_REG_BITS == 32 + { INDEX_op_brcond2_i32, { "r", "r", "ri", "ri" } }, { INDEX_op_setcond2_i32, { "r", "r", "r", "ri", "ri" } }, +#endif - { INDEX_op_bswap16_i32, { "r", "r" } }, - { INDEX_op_bswap32_i32, { "r", "r" } }, +#if TCG_TARGET_REG_BITS == 64 + { INDEX_op_add2_i64, { "r", "r", "r", "r", "rI", "rZM" } }, + { INDEX_op_sub2_i64, { "r", "r", "rI", "rZM", "r", "r" } }, +#else + { INDEX_op_add2_i32, { "r", "r", "r", "r", "rI", "rZM" } }, + { INDEX_op_sub2_i32, { "r", "r", "rI", "rZM", "r", "r" } }, +#endif -#if TARGET_LONG_BITS == 32 +#if TCG_TARGET_REG_BITS == 64 + { INDEX_op_qemu_ld_i32, { "r", "L" } }, + { INDEX_op_qemu_st_i32, { "S", "S" } }, + { INDEX_op_qemu_ld_i64, { "r", "L" } }, + { INDEX_op_qemu_st_i64, { "S", "S" } }, +#elif TARGET_LONG_BITS == 32 { INDEX_op_qemu_ld_i32, { "r", "L" } }, + { INDEX_op_qemu_st_i32, { "S", "S" } }, { INDEX_op_qemu_ld_i64, { "L", "L", "L" } }, - { INDEX_op_qemu_st_i32, { "K", "K" } }, - { INDEX_op_qemu_st_i64, { "M", "M", "M" } }, + { INDEX_op_qemu_st_i64, { "S", "S", "S" } }, #else { INDEX_op_qemu_ld_i32, { "r", "L", "L" } }, + { INDEX_op_qemu_st_i32, { "S", "S", "S" } }, { INDEX_op_qemu_ld_i64, { "L", "L", "L", "L" } }, - { INDEX_op_qemu_st_i32, { "K", "K", "K" } }, - { INDEX_op_qemu_st_i64, { "M", "M", "M", "M" } }, + { INDEX_op_qemu_st_i64, { "S", "S", "S", "S" } }, #endif - { INDEX_op_ext8s_i32, { "r", "r" } }, - { INDEX_op_ext8u_i32, { "r", "r" } }, - { INDEX_op_ext16s_i32, { "r", "r" } }, - { INDEX_op_ext16u_i32, { "r", "r" } }, - - { INDEX_op_deposit_i32, { "r", "0", "r" } }, - { INDEX_op_movcond_i32, { "r", "r", "ri", "r", "r" } }, - { -1 }, }; static void tcg_target_init(TCGContext *s) { + unsigned long hwcap = qemu_getauxval(AT_HWCAP); + if (hwcap & PPC_FEATURE_ARCH_2_06) { + have_isa_2_06 = true; + } + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffffffff); tcg_regset_set32(tcg_target_call_clobber_regs, 0, (1 << TCG_REG_R0) | -#ifdef TCG_TARGET_CALL_DARWIN (1 << TCG_REG_R2) | -#endif (1 << TCG_REG_R3) | (1 << TCG_REG_R4) | (1 << TCG_REG_R5) | @@ -1916,18 +2545,173 @@ static void tcg_target_init(TCGContext *s) (1 << TCG_REG_R9) | (1 << TCG_REG_R10) | (1 << TCG_REG_R11) | - (1 << TCG_REG_R12) - ); + (1 << TCG_REG_R12)); tcg_regset_clear(s->reserved_regs); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R1); -#ifndef TCG_TARGET_CALL_DARWIN - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R2); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); /* tcg temp */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R1); /* stack pointer */ +#if defined(_CALL_SYSV) + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R2); /* toc pointer */ #endif -#ifdef _CALL_SYSV - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R13); +#if defined(_CALL_SYSV) || TCG_TARGET_REG_BITS == 64 + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R13); /* thread pointer */ #endif + tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP1); /* mem temp */ + if (USE_REG_RA) { + tcg_regset_set_reg(s->reserved_regs, TCG_REG_RA); /* return addr */ + } tcg_add_target_add_op_defs(ppc_op_defs); } + +#ifdef __ELF__ +typedef struct { + DebugFrameCIE cie; + DebugFrameFDEHeader fde; + uint8_t fde_def_cfa[4]; + uint8_t fde_reg_ofs[ARRAY_SIZE(tcg_target_callee_save_regs) * 2 + 3]; +} DebugFrame; + +/* We're expecting a 2 byte uleb128 encoded value. */ +QEMU_BUILD_BUG_ON(FRAME_SIZE >= (1 << 14)); + +#if TCG_TARGET_REG_BITS == 64 +# define ELF_HOST_MACHINE EM_PPC64 +#else +# define ELF_HOST_MACHINE EM_PPC +#endif + +static DebugFrame debug_frame = { + .cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ + .cie.id = -1, + .cie.version = 1, + .cie.code_align = 1, + .cie.data_align = (-SZR & 0x7f), /* sleb128 -SZR */ + .cie.return_column = 65, + + /* Total FDE size does not include the "len" member. */ + .fde.len = sizeof(DebugFrame) - offsetof(DebugFrame, fde.cie_offset), + + .fde_def_cfa = { + 12, TCG_REG_R1, /* DW_CFA_def_cfa r1, ... */ + (FRAME_SIZE & 0x7f) | 0x80, /* ... uleb128 FRAME_SIZE */ + (FRAME_SIZE >> 7) + }, + .fde_reg_ofs = { + /* DW_CFA_offset_extended_sf, lr, LR_OFFSET */ + 0x11, 65, (LR_OFFSET / -SZR) & 0x7f, + } +}; + +void tcg_register_jit(void *buf, size_t buf_size) +{ + uint8_t *p = &debug_frame.fde_reg_ofs[3]; + int i; + + for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); ++i, p += 2) { + p[0] = 0x80 + tcg_target_callee_save_regs[i]; + p[1] = (FRAME_SIZE - (REG_SAVE_BOT + i * SZR)) / SZR; + } + + debug_frame.fde.func_start = (uintptr_t)buf; + debug_frame.fde.func_len = buf_size; + + tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); +} +#endif /* __ELF__ */ + +static size_t dcache_bsize = 16; +static size_t icache_bsize = 16; + +void flush_icache_range(uintptr_t start, uintptr_t stop) +{ + uintptr_t p, start1, stop1; + size_t dsize = dcache_bsize; + size_t isize = icache_bsize; + + start1 = start & ~(dsize - 1); + stop1 = (stop + dsize - 1) & ~(dsize - 1); + for (p = start1; p < stop1; p += dsize) { + asm volatile ("dcbst 0,%0" : : "r"(p) : "memory"); + } + asm volatile ("sync" : : : "memory"); + + start &= start & ~(isize - 1); + stop1 = (stop + isize - 1) & ~(isize - 1); + for (p = start1; p < stop1; p += isize) { + asm volatile ("icbi 0,%0" : : "r"(p) : "memory"); + } + asm volatile ("sync" : : : "memory"); + asm volatile ("isync" : : : "memory"); +} + +#if defined _AIX +#include <sys/systemcfg.h> + +static void __attribute__((constructor)) tcg_cache_init(void) +{ + icache_bsize = _system_configuration.icache_line; + dcache_bsize = _system_configuration.dcache_line; +} + +#elif defined __linux__ +static void __attribute__((constructor)) tcg_cache_init(void) +{ + unsigned long dsize = qemu_getauxval(AT_DCACHEBSIZE); + unsigned long isize = qemu_getauxval(AT_ICACHEBSIZE); + + if (dsize == 0 || isize == 0) { + if (dsize == 0) { + fprintf(stderr, "getauxval AT_DCACHEBSIZE failed\n"); + } + if (isize == 0) { + fprintf(stderr, "getauxval AT_ICACHEBSIZE failed\n"); + } + exit(1); + } + dcache_bsize = dsize; + icache_bsize = isize; +} + +#elif defined __APPLE__ +#include <stdio.h> +#include <sys/types.h> +#include <sys/sysctl.h> + +static void __attribute__((constructor)) tcg_cache_init(void) +{ + size_t len; + unsigned cacheline; + int name[2] = { CTL_HW, HW_CACHELINE }; + + len = sizeof(cacheline); + if (sysctl(name, 2, &cacheline, &len, NULL, 0)) { + perror("sysctl CTL_HW HW_CACHELINE failed"); + exit(1); + } + dcache_bsize = cacheline; + icache_bsize = cacheline; +} + +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/sysctl.h> + +static void __attribute__((constructor)) tcg_cache_init(void) +{ + size_t len = 4; + unsigned cacheline; + + if (sysctlbyname ("machdep.cacheline_size", &cacheline, &len, NULL, 0)) { + fprintf(stderr, "sysctlbyname machdep.cacheline_size failed: %s\n", + strerror(errno)); + exit(1); + } + dcache_bsize = cacheline; + icache_bsize = cacheline; +} +#endif diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index 05069ae2d8..32ac4424db 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -21,60 +21,35 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef TCG_TARGET_PPC -#define TCG_TARGET_PPC 1 +#ifndef TCG_TARGET_PPC64 +#define TCG_TARGET_PPC64 1 + +#ifdef _ARCH_PPC64 +# define TCG_TARGET_REG_BITS 64 +#else +# define TCG_TARGET_REG_BITS 32 +#endif #define TCG_TARGET_NB_REGS 32 #define TCG_TARGET_INSN_UNIT_SIZE 4 typedef enum { - TCG_REG_R0 = 0, - TCG_REG_R1, - TCG_REG_R2, - TCG_REG_R3, - TCG_REG_R4, - TCG_REG_R5, - TCG_REG_R6, - TCG_REG_R7, - TCG_REG_R8, - TCG_REG_R9, - TCG_REG_R10, - TCG_REG_R11, - TCG_REG_R12, - TCG_REG_R13, - TCG_REG_R14, - TCG_REG_R15, - TCG_REG_R16, - TCG_REG_R17, - TCG_REG_R18, - TCG_REG_R19, - TCG_REG_R20, - TCG_REG_R21, - TCG_REG_R22, - TCG_REG_R23, - TCG_REG_R24, - TCG_REG_R25, - TCG_REG_R26, - TCG_REG_R27, - TCG_REG_R28, - TCG_REG_R29, - TCG_REG_R30, - TCG_REG_R31 + TCG_REG_R0, TCG_REG_R1, TCG_REG_R2, TCG_REG_R3, + TCG_REG_R4, TCG_REG_R5, TCG_REG_R6, TCG_REG_R7, + TCG_REG_R8, TCG_REG_R9, TCG_REG_R10, TCG_REG_R11, + TCG_REG_R12, TCG_REG_R13, TCG_REG_R14, TCG_REG_R15, + TCG_REG_R16, TCG_REG_R17, TCG_REG_R18, TCG_REG_R19, + TCG_REG_R20, TCG_REG_R21, TCG_REG_R22, TCG_REG_R23, + TCG_REG_R24, TCG_REG_R25, TCG_REG_R26, TCG_REG_R27, + TCG_REG_R28, TCG_REG_R29, TCG_REG_R30, TCG_REG_R31, + + TCG_REG_CALL_STACK = TCG_REG_R1, + TCG_AREG0 = TCG_REG_R27 } TCGReg; -/* used for function call generation */ -#define TCG_REG_CALL_STACK TCG_REG_R1 -#define TCG_TARGET_STACK_ALIGN 16 -#if defined _CALL_DARWIN || defined __APPLE__ -#define TCG_TARGET_CALL_STACK_OFFSET 24 -#elif defined _CALL_AIX -#define TCG_TARGET_CALL_STACK_OFFSET 52 -#elif defined _CALL_SYSV -#define TCG_TARGET_CALL_ALIGN_ARGS 1 -#define TCG_TARGET_CALL_STACK_OFFSET 8 -#else -#error Unsupported system -#endif +/* optional instructions automatically implemented */ +#define TCG_TARGET_HAS_ext8u_i32 0 /* andi */ +#define TCG_TARGET_HAS_ext16u_i32 0 /* optional instructions */ #define TCG_TARGET_HAS_div_i32 1 @@ -82,8 +57,6 @@ typedef enum { #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_ext8s_i32 1 #define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_ext8u_i32 1 -#define TCG_TARGET_HAS_ext16u_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 @@ -95,15 +68,44 @@ typedef enum { #define TCG_TARGET_HAS_nor_i32 1 #define TCG_TARGET_HAS_deposit_i32 1 #define TCG_TARGET_HAS_movcond_i32 1 -#define TCG_TARGET_HAS_mulu2_i32 1 +#define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_muls2_i32 0 -#define TCG_TARGET_HAS_muluh_i32 0 -#define TCG_TARGET_HAS_mulsh_i32 0 +#define TCG_TARGET_HAS_muluh_i32 1 +#define TCG_TARGET_HAS_mulsh_i32 1 -#define TCG_AREG0 TCG_REG_R27 +#if TCG_TARGET_REG_BITS == 64 +#define TCG_TARGET_HAS_add2_i32 0 +#define TCG_TARGET_HAS_sub2_i32 0 +#define TCG_TARGET_HAS_trunc_shr_i32 0 +#define TCG_TARGET_HAS_div_i64 1 +#define TCG_TARGET_HAS_rem_i64 0 +#define TCG_TARGET_HAS_rot_i64 1 +#define TCG_TARGET_HAS_ext8s_i64 1 +#define TCG_TARGET_HAS_ext16s_i64 1 +#define TCG_TARGET_HAS_ext32s_i64 1 +#define TCG_TARGET_HAS_ext8u_i64 0 +#define TCG_TARGET_HAS_ext16u_i64 0 +#define TCG_TARGET_HAS_ext32u_i64 0 +#define TCG_TARGET_HAS_bswap16_i64 1 +#define TCG_TARGET_HAS_bswap32_i64 1 +#define TCG_TARGET_HAS_bswap64_i64 1 +#define TCG_TARGET_HAS_not_i64 1 +#define TCG_TARGET_HAS_neg_i64 1 +#define TCG_TARGET_HAS_andc_i64 1 +#define TCG_TARGET_HAS_orc_i64 1 +#define TCG_TARGET_HAS_eqv_i64 1 +#define TCG_TARGET_HAS_nand_i64 1 +#define TCG_TARGET_HAS_nor_i64 1 +#define TCG_TARGET_HAS_deposit_i64 1 +#define TCG_TARGET_HAS_movcond_i64 1 +#define TCG_TARGET_HAS_add2_i64 1 +#define TCG_TARGET_HAS_sub2_i64 1 +#define TCG_TARGET_HAS_mulu2_i64 0 +#define TCG_TARGET_HAS_muls2_i64 0 +#define TCG_TARGET_HAS_muluh_i64 1 +#define TCG_TARGET_HAS_mulsh_i64 1 +#endif -#define tcg_qemu_tb_exec(env, tb_ptr) \ - ((uintptr_t __attribute__ ((longcall)) \ - (*)(void *, void *))tcg_ctx.code_gen_prologue)(env, tb_ptr) +void flush_icache_range(uintptr_t start, uintptr_t stop); #endif diff --git a/tcg/ppc64/tcg-target.c b/tcg/ppc64/tcg-target.c deleted file mode 100644 index c90ddcd03a..0000000000 --- a/tcg/ppc64/tcg-target.c +++ /dev/null @@ -1,2186 +0,0 @@ -/* - * Tiny Code Generator for QEMU - * - * Copyright (c) 2008 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "tcg-be-ldst.h" - -#define TCG_CT_CONST_S16 0x100 -#define TCG_CT_CONST_U16 0x200 -#define TCG_CT_CONST_S32 0x400 -#define TCG_CT_CONST_U32 0x800 -#define TCG_CT_CONST_ZERO 0x1000 -#define TCG_CT_CONST_MONE 0x2000 - -static tcg_insn_unit *tb_ret_addr; - -#if TARGET_LONG_BITS == 32 -#define LD_ADDR LWZ -#define CMP_L 0 -#else -#define LD_ADDR LD -#define CMP_L (1<<21) -#endif - -#ifndef GUEST_BASE -#define GUEST_BASE 0 -#endif - -#include "elf.h" -static bool have_isa_2_06; -#define HAVE_ISA_2_06 have_isa_2_06 -#define HAVE_ISEL have_isa_2_06 - -#ifdef CONFIG_USE_GUEST_BASE -#define TCG_GUEST_BASE_REG 30 -#else -#define TCG_GUEST_BASE_REG 0 -#endif - -#ifndef NDEBUG -static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { - "r0", - "r1", - "r2", - "r3", - "r4", - "r5", - "r6", - "r7", - "r8", - "r9", - "r10", - "r11", - "r12", - "r13", - "r14", - "r15", - "r16", - "r17", - "r18", - "r19", - "r20", - "r21", - "r22", - "r23", - "r24", - "r25", - "r26", - "r27", - "r28", - "r29", - "r30", - "r31" -}; -#endif - -static const int tcg_target_reg_alloc_order[] = { - TCG_REG_R14, /* call saved registers */ - TCG_REG_R15, - TCG_REG_R16, - TCG_REG_R17, - TCG_REG_R18, - TCG_REG_R19, - TCG_REG_R20, - TCG_REG_R21, - TCG_REG_R22, - TCG_REG_R23, - TCG_REG_R24, - TCG_REG_R25, - TCG_REG_R26, - TCG_REG_R27, - TCG_REG_R28, - TCG_REG_R29, - TCG_REG_R30, - TCG_REG_R31, - TCG_REG_R12, /* call clobbered, non-arguments */ - TCG_REG_R11, - TCG_REG_R10, /* call clobbered, arguments */ - TCG_REG_R9, - TCG_REG_R8, - TCG_REG_R7, - TCG_REG_R6, - TCG_REG_R5, - TCG_REG_R4, - TCG_REG_R3, -}; - -static const int tcg_target_call_iarg_regs[] = { - TCG_REG_R3, - TCG_REG_R4, - TCG_REG_R5, - TCG_REG_R6, - TCG_REG_R7, - TCG_REG_R8, - TCG_REG_R9, - TCG_REG_R10 -}; - -static const int tcg_target_call_oarg_regs[] = { - TCG_REG_R3 -}; - -static const int tcg_target_callee_save_regs[] = { -#ifdef __APPLE__ - TCG_REG_R11, -#endif - TCG_REG_R14, - TCG_REG_R15, - TCG_REG_R16, - TCG_REG_R17, - TCG_REG_R18, - TCG_REG_R19, - TCG_REG_R20, - TCG_REG_R21, - TCG_REG_R22, - TCG_REG_R23, - TCG_REG_R24, - TCG_REG_R25, - TCG_REG_R26, - TCG_REG_R27, /* currently used for the global env */ - TCG_REG_R28, - TCG_REG_R29, - TCG_REG_R30, - TCG_REG_R31 -}; - -static inline bool in_range_b(tcg_target_long target) -{ - return target == sextract64(target, 0, 26); -} - -static uint32_t reloc_pc24_val(tcg_insn_unit *pc, tcg_insn_unit *target) -{ - ptrdiff_t disp = tcg_ptr_byte_diff(target, pc); - assert(in_range_b(disp)); - return disp & 0x3fffffc; -} - -static void reloc_pc24(tcg_insn_unit *pc, tcg_insn_unit *target) -{ - *pc = (*pc & ~0x3fffffc) | reloc_pc24_val(pc, target); -} - -static uint16_t reloc_pc14_val(tcg_insn_unit *pc, tcg_insn_unit *target) -{ - ptrdiff_t disp = tcg_ptr_byte_diff(target, pc); - assert(disp == (int16_t) disp); - return disp & 0xfffc; -} - -static void reloc_pc14(tcg_insn_unit *pc, tcg_insn_unit *target) -{ - *pc = (*pc & ~0xfffc) | reloc_pc14_val(pc, target); -} - -static inline void tcg_out_b_noaddr(TCGContext *s, int insn) -{ - unsigned retrans = *s->code_ptr & 0x3fffffc; - tcg_out32(s, insn | retrans); -} - -static inline void tcg_out_bc_noaddr(TCGContext *s, int insn) -{ - unsigned retrans = *s->code_ptr & 0xfffc; - tcg_out32(s, insn | retrans); -} - -static void patch_reloc(tcg_insn_unit *code_ptr, int type, - intptr_t value, intptr_t addend) -{ - tcg_insn_unit *target = (tcg_insn_unit *)value; - - assert(addend == 0); - switch (type) { - case R_PPC_REL14: - reloc_pc14(code_ptr, target); - break; - case R_PPC_REL24: - reloc_pc24(code_ptr, target); - break; - default: - tcg_abort(); - } -} - -/* parse target specific constraints */ -static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) -{ - const char *ct_str; - - ct_str = *pct_str; - switch (ct_str[0]) { - case 'A': case 'B': case 'C': case 'D': - ct->ct |= TCG_CT_REG; - tcg_regset_set_reg(ct->u.regs, 3 + ct_str[0] - 'A'); - break; - case 'r': - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xffffffff); - break; - case 'L': /* qemu_ld constraint */ - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xffffffff); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3); -#ifdef CONFIG_SOFTMMU - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R5); -#endif - break; - case 'S': /* qemu_st constraint */ - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xffffffff); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3); -#ifdef CONFIG_SOFTMMU - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R5); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R6); -#endif - break; - case 'I': - ct->ct |= TCG_CT_CONST_S16; - break; - case 'J': - ct->ct |= TCG_CT_CONST_U16; - break; - case 'M': - ct->ct |= TCG_CT_CONST_MONE; - break; - case 'T': - ct->ct |= TCG_CT_CONST_S32; - break; - case 'U': - ct->ct |= TCG_CT_CONST_U32; - break; - case 'Z': - ct->ct |= TCG_CT_CONST_ZERO; - break; - default: - return -1; - } - ct_str++; - *pct_str = ct_str; - return 0; -} - -/* test if a constant matches the constraint */ -static int tcg_target_const_match(tcg_target_long val, TCGType type, - const TCGArgConstraint *arg_ct) -{ - int ct = arg_ct->ct; - if (ct & TCG_CT_CONST) { - return 1; - } - - /* The only 32-bit constraint we use aside from - TCG_CT_CONST is TCG_CT_CONST_S16. */ - if (type == TCG_TYPE_I32) { - val = (int32_t)val; - } - - if ((ct & TCG_CT_CONST_S16) && val == (int16_t)val) { - return 1; - } else if ((ct & TCG_CT_CONST_U16) && val == (uint16_t)val) { - return 1; - } else if ((ct & TCG_CT_CONST_S32) && val == (int32_t)val) { - return 1; - } else if ((ct & TCG_CT_CONST_U32) && val == (uint32_t)val) { - return 1; - } else if ((ct & TCG_CT_CONST_ZERO) && val == 0) { - return 1; - } else if ((ct & TCG_CT_CONST_MONE) && val == -1) { - return 1; - } - return 0; -} - -#define OPCD(opc) ((opc)<<26) -#define XO19(opc) (OPCD(19)|((opc)<<1)) -#define MD30(opc) (OPCD(30)|((opc)<<2)) -#define MDS30(opc) (OPCD(30)|((opc)<<1)) -#define XO31(opc) (OPCD(31)|((opc)<<1)) -#define XO58(opc) (OPCD(58)|(opc)) -#define XO62(opc) (OPCD(62)|(opc)) - -#define B OPCD( 18) -#define BC OPCD( 16) -#define LBZ OPCD( 34) -#define LHZ OPCD( 40) -#define LHA OPCD( 42) -#define LWZ OPCD( 32) -#define STB OPCD( 38) -#define STH OPCD( 44) -#define STW OPCD( 36) - -#define STD XO62( 0) -#define STDU XO62( 1) -#define STDX XO31(149) - -#define LD XO58( 0) -#define LDX XO31( 21) -#define LDU XO58( 1) -#define LWA XO58( 2) -#define LWAX XO31(341) - -#define ADDIC OPCD( 12) -#define ADDI OPCD( 14) -#define ADDIS OPCD( 15) -#define ORI OPCD( 24) -#define ORIS OPCD( 25) -#define XORI OPCD( 26) -#define XORIS OPCD( 27) -#define ANDI OPCD( 28) -#define ANDIS OPCD( 29) -#define MULLI OPCD( 7) -#define CMPLI OPCD( 10) -#define CMPI OPCD( 11) -#define SUBFIC OPCD( 8) - -#define LWZU OPCD( 33) -#define STWU OPCD( 37) - -#define RLWIMI OPCD( 20) -#define RLWINM OPCD( 21) -#define RLWNM OPCD( 23) - -#define RLDICL MD30( 0) -#define RLDICR MD30( 1) -#define RLDIMI MD30( 3) -#define RLDCL MDS30( 8) - -#define BCLR XO19( 16) -#define BCCTR XO19(528) -#define CRAND XO19(257) -#define CRANDC XO19(129) -#define CRNAND XO19(225) -#define CROR XO19(449) -#define CRNOR XO19( 33) - -#define EXTSB XO31(954) -#define EXTSH XO31(922) -#define EXTSW XO31(986) -#define ADD XO31(266) -#define ADDE XO31(138) -#define ADDME XO31(234) -#define ADDZE XO31(202) -#define ADDC XO31( 10) -#define AND XO31( 28) -#define SUBF XO31( 40) -#define SUBFC XO31( 8) -#define SUBFE XO31(136) -#define SUBFME XO31(232) -#define SUBFZE XO31(200) -#define OR XO31(444) -#define XOR XO31(316) -#define MULLW XO31(235) -#define MULHWU XO31( 11) -#define DIVW XO31(491) -#define DIVWU XO31(459) -#define CMP XO31( 0) -#define CMPL XO31( 32) -#define LHBRX XO31(790) -#define LWBRX XO31(534) -#define LDBRX XO31(532) -#define STHBRX XO31(918) -#define STWBRX XO31(662) -#define STDBRX XO31(660) -#define MFSPR XO31(339) -#define MTSPR XO31(467) -#define SRAWI XO31(824) -#define NEG XO31(104) -#define MFCR XO31( 19) -#define MFOCRF (MFCR | (1u << 20)) -#define NOR XO31(124) -#define CNTLZW XO31( 26) -#define CNTLZD XO31( 58) -#define ANDC XO31( 60) -#define ORC XO31(412) -#define EQV XO31(284) -#define NAND XO31(476) -#define ISEL XO31( 15) - -#define MULLD XO31(233) -#define MULHD XO31( 73) -#define MULHDU XO31( 9) -#define DIVD XO31(489) -#define DIVDU XO31(457) - -#define LBZX XO31( 87) -#define LHZX XO31(279) -#define LHAX XO31(343) -#define LWZX XO31( 23) -#define STBX XO31(215) -#define STHX XO31(407) -#define STWX XO31(151) - -#define SPR(a, b) ((((a)<<5)|(b))<<11) -#define LR SPR(8, 0) -#define CTR SPR(9, 0) - -#define SLW XO31( 24) -#define SRW XO31(536) -#define SRAW XO31(792) - -#define SLD XO31( 27) -#define SRD XO31(539) -#define SRAD XO31(794) -#define SRADI XO31(413<<1) - -#define TW XO31( 4) -#define TRAP (TW | TO(31)) - -#define RT(r) ((r)<<21) -#define RS(r) ((r)<<21) -#define RA(r) ((r)<<16) -#define RB(r) ((r)<<11) -#define TO(t) ((t)<<21) -#define SH(s) ((s)<<11) -#define MB(b) ((b)<<6) -#define ME(e) ((e)<<1) -#define BO(o) ((o)<<21) -#define MB64(b) ((b)<<5) -#define FXM(b) (1 << (19 - (b))) - -#define LK 1 - -#define TAB(t, a, b) (RT(t) | RA(a) | RB(b)) -#define SAB(s, a, b) (RS(s) | RA(a) | RB(b)) -#define TAI(s, a, i) (RT(s) | RA(a) | ((i) & 0xffff)) -#define SAI(s, a, i) (RS(s) | RA(a) | ((i) & 0xffff)) - -#define BF(n) ((n)<<23) -#define BI(n, c) (((c)+((n)*4))<<16) -#define BT(n, c) (((c)+((n)*4))<<21) -#define BA(n, c) (((c)+((n)*4))<<16) -#define BB(n, c) (((c)+((n)*4))<<11) -#define BC_(n, c) (((c)+((n)*4))<<6) - -#define BO_COND_TRUE BO(12) -#define BO_COND_FALSE BO( 4) -#define BO_ALWAYS BO(20) - -enum { - CR_LT, - CR_GT, - CR_EQ, - CR_SO -}; - -static const uint32_t tcg_to_bc[] = { - [TCG_COND_EQ] = BC | BI(7, CR_EQ) | BO_COND_TRUE, - [TCG_COND_NE] = BC | BI(7, CR_EQ) | BO_COND_FALSE, - [TCG_COND_LT] = BC | BI(7, CR_LT) | BO_COND_TRUE, - [TCG_COND_GE] = BC | BI(7, CR_LT) | BO_COND_FALSE, - [TCG_COND_LE] = BC | BI(7, CR_GT) | BO_COND_FALSE, - [TCG_COND_GT] = BC | BI(7, CR_GT) | BO_COND_TRUE, - [TCG_COND_LTU] = BC | BI(7, CR_LT) | BO_COND_TRUE, - [TCG_COND_GEU] = BC | BI(7, CR_LT) | BO_COND_FALSE, - [TCG_COND_LEU] = BC | BI(7, CR_GT) | BO_COND_FALSE, - [TCG_COND_GTU] = BC | BI(7, CR_GT) | BO_COND_TRUE, -}; - -/* The low bit here is set if the RA and RB fields must be inverted. */ -static const uint32_t tcg_to_isel[] = { - [TCG_COND_EQ] = ISEL | BC_(7, CR_EQ), - [TCG_COND_NE] = ISEL | BC_(7, CR_EQ) | 1, - [TCG_COND_LT] = ISEL | BC_(7, CR_LT), - [TCG_COND_GE] = ISEL | BC_(7, CR_LT) | 1, - [TCG_COND_LE] = ISEL | BC_(7, CR_GT) | 1, - [TCG_COND_GT] = ISEL | BC_(7, CR_GT), - [TCG_COND_LTU] = ISEL | BC_(7, CR_LT), - [TCG_COND_GEU] = ISEL | BC_(7, CR_LT) | 1, - [TCG_COND_LEU] = ISEL | BC_(7, CR_GT) | 1, - [TCG_COND_GTU] = ISEL | BC_(7, CR_GT), -}; - -static inline void tcg_out_mov(TCGContext *s, TCGType type, - TCGReg ret, TCGReg arg) -{ - if (ret != arg) { - tcg_out32(s, OR | SAB(arg, ret, arg)); - } -} - -static inline void tcg_out_rld(TCGContext *s, int op, TCGReg ra, TCGReg rs, - int sh, int mb) -{ - sh = SH(sh & 0x1f) | (((sh >> 5) & 1) << 1); - mb = MB64((mb >> 5) | ((mb << 1) & 0x3f)); - tcg_out32(s, op | RA(ra) | RS(rs) | sh | mb); -} - -static inline void tcg_out_rlw(TCGContext *s, int op, TCGReg ra, TCGReg rs, - int sh, int mb, int me) -{ - tcg_out32(s, op | RA(ra) | RS(rs) | SH(sh) | MB(mb) | ME(me)); -} - -static inline void tcg_out_ext32u(TCGContext *s, TCGReg dst, TCGReg src) -{ - tcg_out_rld(s, RLDICL, dst, src, 0, 32); -} - -static inline void tcg_out_shli64(TCGContext *s, TCGReg dst, TCGReg src, int c) -{ - tcg_out_rld(s, RLDICR, dst, src, c, 63 - c); -} - -static inline void tcg_out_shri64(TCGContext *s, TCGReg dst, TCGReg src, int c) -{ - tcg_out_rld(s, RLDICL, dst, src, 64 - c, c); -} - -static void tcg_out_movi32(TCGContext *s, TCGReg ret, int32_t arg) -{ - if (arg == (int16_t) arg) { - tcg_out32(s, ADDI | TAI(ret, 0, arg)); - } else { - tcg_out32(s, ADDIS | TAI(ret, 0, arg >> 16)); - if (arg & 0xffff) { - tcg_out32(s, ORI | SAI(ret, ret, arg)); - } - } -} - -static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg ret, - tcg_target_long arg) -{ - if (type == TCG_TYPE_I32 || arg == (int32_t)arg) { - tcg_out_movi32(s, ret, arg); - } else if (arg == (uint32_t)arg && !(arg & 0x8000)) { - tcg_out32(s, ADDI | TAI(ret, 0, arg)); - tcg_out32(s, ORIS | SAI(ret, ret, arg >> 16)); - } else { - int32_t high = arg >> 32; - tcg_out_movi32(s, ret, high); - if (high) { - tcg_out_shli64(s, ret, ret, 32); - } - if (arg & 0xffff0000) { - tcg_out32(s, ORIS | SAI(ret, ret, arg >> 16)); - } - if (arg & 0xffff) { - tcg_out32(s, ORI | SAI(ret, ret, arg)); - } - } -} - -static bool mask_operand(uint32_t c, int *mb, int *me) -{ - uint32_t lsb, test; - - /* Accept a bit pattern like: - 0....01....1 - 1....10....0 - 0..01..10..0 - Keep track of the transitions. */ - if (c == 0 || c == -1) { - return false; - } - test = c; - lsb = test & -test; - test += lsb; - if (test & (test - 1)) { - return false; - } - - *me = clz32(lsb); - *mb = test ? clz32(test & -test) + 1 : 0; - return true; -} - -static bool mask64_operand(uint64_t c, int *mb, int *me) -{ - uint64_t lsb; - - if (c == 0) { - return false; - } - - lsb = c & -c; - /* Accept 1..10..0. */ - if (c == -lsb) { - *mb = 0; - *me = clz64(lsb); - return true; - } - /* Accept 0..01..1. */ - if (lsb == 1 && (c & (c + 1)) == 0) { - *mb = clz64(c + 1) + 1; - *me = 63; - return true; - } - return false; -} - -static void tcg_out_andi32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c) -{ - int mb, me; - - if ((c & 0xffff) == c) { - tcg_out32(s, ANDI | SAI(src, dst, c)); - return; - } else if ((c & 0xffff0000) == c) { - tcg_out32(s, ANDIS | SAI(src, dst, c >> 16)); - return; - } else if (mask_operand(c, &mb, &me)) { - tcg_out_rlw(s, RLWINM, dst, src, 0, mb, me); - } else { - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R0, c); - tcg_out32(s, AND | SAB(src, dst, TCG_REG_R0)); - } -} - -static void tcg_out_andi64(TCGContext *s, TCGReg dst, TCGReg src, uint64_t c) -{ - int mb, me; - - if ((c & 0xffff) == c) { - tcg_out32(s, ANDI | SAI(src, dst, c)); - return; - } else if ((c & 0xffff0000) == c) { - tcg_out32(s, ANDIS | SAI(src, dst, c >> 16)); - return; - } else if (mask64_operand(c, &mb, &me)) { - if (mb == 0) { - tcg_out_rld(s, RLDICR, dst, src, 0, me); - } else { - tcg_out_rld(s, RLDICL, dst, src, 0, mb); - } - } else { - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R0, c); - tcg_out32(s, AND | SAB(src, dst, TCG_REG_R0)); - } -} - -static void tcg_out_zori32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c, - int op_lo, int op_hi) -{ - if (c >> 16) { - tcg_out32(s, op_hi | SAI(src, dst, c >> 16)); - src = dst; - } - if (c & 0xffff) { - tcg_out32(s, op_lo | SAI(src, dst, c)); - src = dst; - } -} - -static void tcg_out_ori32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c) -{ - tcg_out_zori32(s, dst, src, c, ORI, ORIS); -} - -static void tcg_out_xori32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c) -{ - tcg_out_zori32(s, dst, src, c, XORI, XORIS); -} - -static void tcg_out_b(TCGContext *s, int mask, tcg_insn_unit *target) -{ - ptrdiff_t disp = tcg_pcrel_diff(s, target); - if (in_range_b(disp)) { - tcg_out32(s, B | (disp & 0x3fffffc) | mask); - } else { - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R0, (uintptr_t)target); - tcg_out32(s, MTSPR | RS(TCG_REG_R0) | CTR); - tcg_out32(s, BCCTR | BO_ALWAYS | mask); - } -} - -static void tcg_out_call(TCGContext *s, tcg_insn_unit *target) -{ -#ifdef __APPLE__ - tcg_out_b(s, LK, target); -#else - /* Look through the descriptor. If the branch is in range, and we - don't have to spend too much effort on building the toc. */ - void *tgt = ((void **)target)[0]; - uintptr_t toc = ((uintptr_t *)target)[1]; - intptr_t diff = tcg_pcrel_diff(s, tgt); - - if (in_range_b(diff) && toc == (uint32_t)toc) { - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R2, toc); - tcg_out_b(s, LK, tgt); - } else { - /* Fold the low bits of the constant into the addresses below. */ - intptr_t arg = (intptr_t)target; - int ofs = (int16_t)arg; - - if (ofs + 8 < 0x8000) { - arg -= ofs; - } else { - ofs = 0; - } - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R2, arg); - tcg_out32(s, LD | TAI(TCG_REG_R0, TCG_REG_R2, ofs)); - tcg_out32(s, MTSPR | RA(TCG_REG_R0) | CTR); - tcg_out32(s, LD | TAI(TCG_REG_R2, TCG_REG_R2, ofs + 8)); - tcg_out32(s, BCCTR | BO_ALWAYS | LK); - } -#endif -} - -static void tcg_out_mem_long(TCGContext *s, int opi, int opx, TCGReg rt, - TCGReg base, tcg_target_long offset) -{ - tcg_target_long orig = offset, l0, l1, extra = 0, align = 0; - TCGReg rs = TCG_REG_R2; - - assert(rt != TCG_REG_R2 && base != TCG_REG_R2); - - switch (opi) { - case LD: case LWA: - align = 3; - /* FALLTHRU */ - default: - if (rt != TCG_REG_R0) { - rs = rt; - } - break; - case STD: - align = 3; - break; - case STB: case STH: case STW: - break; - } - - /* For unaligned, or very large offsets, use the indexed form. */ - if (offset & align || offset != (int32_t)offset) { - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R2, orig); - tcg_out32(s, opx | TAB(rt, base, TCG_REG_R2)); - return; - } - - l0 = (int16_t)offset; - offset = (offset - l0) >> 16; - l1 = (int16_t)offset; - - if (l1 < 0 && orig >= 0) { - extra = 0x4000; - l1 = (int16_t)(offset - 0x4000); - } - if (l1) { - tcg_out32(s, ADDIS | TAI(rs, base, l1)); - base = rs; - } - if (extra) { - tcg_out32(s, ADDIS | TAI(rs, base, extra)); - base = rs; - } - if (opi != ADDI || base != rt || l0 != 0) { - tcg_out32(s, opi | TAI(rt, base, l0)); - } -} - -static const uint32_t qemu_ldx_opc[16] = { - [MO_UB] = LBZX, - [MO_UW] = LHZX, - [MO_UL] = LWZX, - [MO_Q] = LDX, - [MO_SW] = LHAX, - [MO_SL] = LWAX, - [MO_BSWAP | MO_UB] = LBZX, - [MO_BSWAP | MO_UW] = LHBRX, - [MO_BSWAP | MO_UL] = LWBRX, - [MO_BSWAP | MO_Q] = LDBRX, -}; - -static const uint32_t qemu_stx_opc[16] = { - [MO_UB] = STBX, - [MO_UW] = STHX, - [MO_UL] = STWX, - [MO_Q] = STDX, - [MO_BSWAP | MO_UB] = STBX, - [MO_BSWAP | MO_UW] = STHBRX, - [MO_BSWAP | MO_UL] = STWBRX, - [MO_BSWAP | MO_Q] = STDBRX, -}; - -static const uint32_t qemu_exts_opc[4] = { - EXTSB, EXTSH, EXTSW, 0 -}; - -#if defined (CONFIG_SOFTMMU) -/* helper signature: helper_ld_mmu(CPUState *env, target_ulong addr, - * int mmu_idx, uintptr_t ra) - */ -static void * const qemu_ld_helpers[16] = { - [MO_UB] = helper_ret_ldub_mmu, - [MO_LEUW] = helper_le_lduw_mmu, - [MO_LEUL] = helper_le_ldul_mmu, - [MO_LEQ] = helper_le_ldq_mmu, - [MO_BEUW] = helper_be_lduw_mmu, - [MO_BEUL] = helper_be_ldul_mmu, - [MO_BEQ] = helper_be_ldq_mmu, -}; - -/* helper signature: helper_st_mmu(CPUState *env, target_ulong addr, - * uintxx_t val, int mmu_idx, uintptr_t ra) - */ -static void * const qemu_st_helpers[16] = { - [MO_UB] = helper_ret_stb_mmu, - [MO_LEUW] = helper_le_stw_mmu, - [MO_LEUL] = helper_le_stl_mmu, - [MO_LEQ] = helper_le_stq_mmu, - [MO_BEUW] = helper_be_stw_mmu, - [MO_BEUL] = helper_be_stl_mmu, - [MO_BEQ] = helper_be_stq_mmu, -}; - -/* Perform the TLB load and compare. Places the result of the comparison - in CR7, loads the addend of the TLB into R3, and returns the register - containing the guest address (zero-extended into R4). Clobbers R0 and R2. */ - -static TCGReg tcg_out_tlb_read(TCGContext *s, TCGMemOp s_bits, TCGReg addr_reg, - int mem_index, bool is_read) -{ - int cmp_off - = (is_read - ? offsetof(CPUArchState, tlb_table[mem_index][0].addr_read) - : offsetof(CPUArchState, tlb_table[mem_index][0].addr_write)); - int add_off = offsetof(CPUArchState, tlb_table[mem_index][0].addend); - TCGReg base = TCG_AREG0; - - /* Extract the page index, shifted into place for tlb index. */ - if (TARGET_LONG_BITS == 32) { - /* Zero-extend the address into a place helpful for further use. */ - tcg_out_ext32u(s, TCG_REG_R4, addr_reg); - addr_reg = TCG_REG_R4; - } else { - tcg_out_rld(s, RLDICL, TCG_REG_R3, addr_reg, - 64 - TARGET_PAGE_BITS, 64 - CPU_TLB_BITS); - } - - /* Compensate for very large offsets. */ - if (add_off >= 0x8000) { - /* Most target env are smaller than 32k; none are larger than 64k. - Simplify the logic here merely to offset by 0x7ff0, giving us a - range just shy of 64k. Check this assumption. */ - QEMU_BUILD_BUG_ON(offsetof(CPUArchState, - tlb_table[NB_MMU_MODES - 1][1]) - > 0x7ff0 + 0x7fff); - tcg_out32(s, ADDI | TAI(TCG_REG_R2, base, 0x7ff0)); - base = TCG_REG_R2; - cmp_off -= 0x7ff0; - add_off -= 0x7ff0; - } - - /* Extraction and shifting, part 2. */ - if (TARGET_LONG_BITS == 32) { - tcg_out_rlw(s, RLWINM, TCG_REG_R3, addr_reg, - 32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS), - 32 - (CPU_TLB_BITS + CPU_TLB_ENTRY_BITS), - 31 - CPU_TLB_ENTRY_BITS); - } else { - tcg_out_shli64(s, TCG_REG_R3, TCG_REG_R3, CPU_TLB_ENTRY_BITS); - } - - tcg_out32(s, ADD | TAB(TCG_REG_R3, TCG_REG_R3, base)); - - /* Load the tlb comparator. */ - tcg_out32(s, LD_ADDR | TAI(TCG_REG_R2, TCG_REG_R3, cmp_off)); - - /* Load the TLB addend for use on the fast path. Do this asap - to minimize any load use delay. */ - tcg_out32(s, LD | TAI(TCG_REG_R3, TCG_REG_R3, add_off)); - - /* Clear the non-page, non-alignment bits from the address. */ - if (TARGET_LONG_BITS == 32) { - tcg_out_rlw(s, RLWINM, TCG_REG_R0, addr_reg, 0, - (32 - s_bits) & 31, 31 - TARGET_PAGE_BITS); - } else if (!s_bits) { - tcg_out_rld(s, RLDICR, TCG_REG_R0, addr_reg, 0, 63 - TARGET_PAGE_BITS); - } else { - tcg_out_rld(s, RLDICL, TCG_REG_R0, addr_reg, - 64 - TARGET_PAGE_BITS, TARGET_PAGE_BITS - s_bits); - tcg_out_rld(s, RLDICL, TCG_REG_R0, TCG_REG_R0, TARGET_PAGE_BITS, 0); - } - - tcg_out32(s, CMP | BF(7) | RA(TCG_REG_R0) | RB(TCG_REG_R2) | CMP_L); - - return addr_reg; -} - -/* Record the context of a call to the out of line helper code for the slow - path for a load or store, so that we can later generate the correct - helper code. */ -static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOp opc, - int data_reg, int addr_reg, int mem_index, - tcg_insn_unit *raddr, tcg_insn_unit *label_ptr) -{ - TCGLabelQemuLdst *label = new_ldst_label(s); - - label->is_ld = is_ld; - label->opc = opc; - label->datalo_reg = data_reg; - label->addrlo_reg = addr_reg; - label->mem_index = mem_index; - label->raddr = raddr; - label->label_ptr[0] = label_ptr; -} - -static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) -{ - TCGMemOp opc = lb->opc; - - reloc_pc14(lb->label_ptr[0], s->code_ptr); - - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_R3, TCG_AREG0); - - /* If the address needed to be zero-extended, we'll have already - placed it in R4. The only remaining case is 64-bit guest. */ - tcg_out_mov(s, TCG_TYPE_I64, TCG_REG_R4, lb->addrlo_reg); - - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R5, lb->mem_index); - tcg_out32(s, MFSPR | RT(TCG_REG_R6) | LR); - - tcg_out_call(s, qemu_ld_helpers[opc & ~MO_SIGN]); - - if (opc & MO_SIGN) { - uint32_t insn = qemu_exts_opc[opc & MO_SIZE]; - tcg_out32(s, insn | RA(lb->datalo_reg) | RS(TCG_REG_R3)); - } else { - tcg_out_mov(s, TCG_TYPE_I64, lb->datalo_reg, TCG_REG_R3); - } - - tcg_out_b(s, 0, lb->raddr); -} - -static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) -{ - TCGMemOp opc = lb->opc; - TCGMemOp s_bits = opc & MO_SIZE; - - reloc_pc14(lb->label_ptr[0], s->code_ptr); - - tcg_out_mov(s, TCG_TYPE_I64, TCG_REG_R3, TCG_AREG0); - - /* If the address needed to be zero-extended, we'll have already - placed it in R4. The only remaining case is 64-bit guest. */ - tcg_out_mov(s, TCG_TYPE_I64, TCG_REG_R4, lb->addrlo_reg); - - tcg_out_rld(s, RLDICL, TCG_REG_R5, lb->datalo_reg, - 0, 64 - (1 << (3 + s_bits))); - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R6, lb->mem_index); - tcg_out32(s, MFSPR | RT(TCG_REG_R7) | LR); - - tcg_out_call(s, qemu_st_helpers[opc]); - - tcg_out_b(s, 0, lb->raddr); -} -#endif /* SOFTMMU */ - -static void tcg_out_qemu_ld(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - TCGMemOp opc, int mem_index) -{ - TCGReg rbase; - uint32_t insn; - TCGMemOp s_bits = opc & MO_SIZE; -#ifdef CONFIG_SOFTMMU - tcg_insn_unit *label_ptr; -#endif - -#ifdef CONFIG_SOFTMMU - addr_reg = tcg_out_tlb_read(s, s_bits, addr_reg, mem_index, true); - - /* Load a pointer into the current opcode w/conditional branch-link. */ - label_ptr = s->code_ptr; - tcg_out_bc_noaddr(s, BC | BI(7, CR_EQ) | BO_COND_FALSE | LK); - - rbase = TCG_REG_R3; -#else /* !CONFIG_SOFTMMU */ - rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0; - if (TARGET_LONG_BITS == 32) { - tcg_out_ext32u(s, TCG_REG_R2, addr_reg); - addr_reg = TCG_REG_R2; - } -#endif - - insn = qemu_ldx_opc[opc]; - if (!HAVE_ISA_2_06 && insn == LDBRX) { - tcg_out32(s, ADDI | TAI(TCG_REG_R0, addr_reg, 4)); - tcg_out32(s, LWBRX | TAB(data_reg, rbase, addr_reg)); - tcg_out32(s, LWBRX | TAB(TCG_REG_R0, rbase, TCG_REG_R0)); - tcg_out_rld(s, RLDIMI, data_reg, TCG_REG_R0, 32, 0); - } else if (insn) { - tcg_out32(s, insn | TAB(data_reg, rbase, addr_reg)); - } else { - insn = qemu_ldx_opc[opc & (MO_SIZE | MO_BSWAP)]; - tcg_out32(s, insn | TAB(data_reg, rbase, addr_reg)); - insn = qemu_exts_opc[s_bits]; - tcg_out32(s, insn | RA(data_reg) | RS(data_reg)); - } - -#ifdef CONFIG_SOFTMMU - add_qemu_ldst_label(s, true, opc, data_reg, addr_reg, mem_index, - s->code_ptr, label_ptr); -#endif -} - -static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - TCGMemOp opc, int mem_index) -{ - TCGReg rbase; - uint32_t insn; -#ifdef CONFIG_SOFTMMU - tcg_insn_unit *label_ptr; -#endif - -#ifdef CONFIG_SOFTMMU - addr_reg = tcg_out_tlb_read(s, opc & MO_SIZE, addr_reg, mem_index, false); - - /* Load a pointer into the current opcode w/conditional branch-link. */ - label_ptr = s->code_ptr; - tcg_out_bc_noaddr(s, BC | BI(7, CR_EQ) | BO_COND_FALSE | LK); - - rbase = TCG_REG_R3; -#else /* !CONFIG_SOFTMMU */ - rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0; - if (TARGET_LONG_BITS == 32) { - tcg_out_ext32u(s, TCG_REG_R2, addr_reg); - addr_reg = TCG_REG_R2; - } -#endif - - insn = qemu_stx_opc[opc]; - if (!HAVE_ISA_2_06 && insn == STDBRX) { - tcg_out32(s, STWBRX | SAB(data_reg, rbase, addr_reg)); - tcg_out32(s, ADDI | TAI(TCG_REG_R2, addr_reg, 4)); - tcg_out_shri64(s, TCG_REG_R0, data_reg, 32); - tcg_out32(s, STWBRX | SAB(TCG_REG_R0, rbase, TCG_REG_R2)); - } else { - tcg_out32(s, insn | SAB(data_reg, rbase, addr_reg)); - } - -#ifdef CONFIG_SOFTMMU - add_qemu_ldst_label(s, false, opc, data_reg, addr_reg, mem_index, - s->code_ptr, label_ptr); -#endif -} - -#define FRAME_SIZE ((int) \ - ((8 /* back chain */ \ - + 8 /* CR */ \ - + 8 /* LR */ \ - + 8 /* compiler doubleword */ \ - + 8 /* link editor doubleword */ \ - + 8 /* TOC save area */ \ - + TCG_STATIC_CALL_ARGS_SIZE \ - + CPU_TEMP_BUF_NLONGS * sizeof(long) \ - + ARRAY_SIZE(tcg_target_callee_save_regs) * 8 \ - + 15) & ~15)) - -#define REG_SAVE_BOT (FRAME_SIZE - ARRAY_SIZE(tcg_target_callee_save_regs) * 8) - -static void tcg_target_qemu_prologue(TCGContext *s) -{ - int i; - - tcg_set_frame(s, TCG_REG_CALL_STACK, - REG_SAVE_BOT - CPU_TEMP_BUF_NLONGS * sizeof(long), - CPU_TEMP_BUF_NLONGS * sizeof(long)); - -#ifndef __APPLE__ - /* First emit adhoc function descriptor */ - tcg_out64(s, (uint64_t)s->code_ptr + 24); /* entry point */ - tcg_out64(s, 0); /* toc */ - tcg_out64(s, 0); /* environment pointer */ -#endif - - /* Prologue */ - tcg_out32(s, MFSPR | RT(TCG_REG_R0) | LR); - tcg_out32(s, STDU | SAI(TCG_REG_R1, TCG_REG_R1, -FRAME_SIZE)); - for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); ++i) { - tcg_out32(s, STD | SAI(tcg_target_callee_save_regs[i], 1, - REG_SAVE_BOT + i * 8)); - } - tcg_out32(s, STD | SAI(TCG_REG_R0, TCG_REG_R1, FRAME_SIZE + 16)); - -#ifdef CONFIG_USE_GUEST_BASE - if (GUEST_BASE) { - tcg_out_movi(s, TCG_TYPE_I64, TCG_GUEST_BASE_REG, GUEST_BASE); - tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); - } -#endif - - tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); - tcg_out32(s, MTSPR | RS(tcg_target_call_iarg_regs[1]) | CTR); - tcg_out32(s, BCCTR | BO_ALWAYS); - - /* Epilogue */ - tb_ret_addr = s->code_ptr; - - for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); ++i) { - tcg_out32(s, LD | TAI(tcg_target_callee_save_regs[i], TCG_REG_R1, - REG_SAVE_BOT + i * 8)); - } - tcg_out32(s, LD | TAI(TCG_REG_R0, TCG_REG_R1, FRAME_SIZE + 16)); - tcg_out32(s, MTSPR | RS(TCG_REG_R0) | LR); - tcg_out32(s, ADDI | TAI(TCG_REG_R1, TCG_REG_R1, FRAME_SIZE)); - tcg_out32(s, BCLR | BO_ALWAYS); -} - -static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg ret, - TCGReg arg1, intptr_t arg2) -{ - int opi, opx; - - if (type == TCG_TYPE_I32) { - opi = LWZ, opx = LWZX; - } else { - opi = LD, opx = LDX; - } - tcg_out_mem_long(s, opi, opx, ret, arg1, arg2); -} - -static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, - TCGReg arg1, intptr_t arg2) -{ - int opi, opx; - - if (type == TCG_TYPE_I32) { - opi = STW, opx = STWX; - } else { - opi = STD, opx = STDX; - } - tcg_out_mem_long(s, opi, opx, arg, arg1, arg2); -} - -static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, - int const_arg2, int cr, TCGType type) -{ - int imm; - uint32_t op; - - /* Simplify the comparisons below wrt CMPI. */ - if (type == TCG_TYPE_I32) { - arg2 = (int32_t)arg2; - } - - switch (cond) { - case TCG_COND_EQ: - case TCG_COND_NE: - if (const_arg2) { - if ((int16_t) arg2 == arg2) { - op = CMPI; - imm = 1; - break; - } else if ((uint16_t) arg2 == arg2) { - op = CMPLI; - imm = 1; - break; - } - } - op = CMPL; - imm = 0; - break; - - case TCG_COND_LT: - case TCG_COND_GE: - case TCG_COND_LE: - case TCG_COND_GT: - if (const_arg2) { - if ((int16_t) arg2 == arg2) { - op = CMPI; - imm = 1; - break; - } - } - op = CMP; - imm = 0; - break; - - case TCG_COND_LTU: - case TCG_COND_GEU: - case TCG_COND_LEU: - case TCG_COND_GTU: - if (const_arg2) { - if ((uint16_t) arg2 == arg2) { - op = CMPLI; - imm = 1; - break; - } - } - op = CMPL; - imm = 0; - break; - - default: - tcg_abort(); - } - op |= BF(cr) | ((type == TCG_TYPE_I64) << 21); - - if (imm) { - tcg_out32(s, op | RA(arg1) | (arg2 & 0xffff)); - } else { - if (const_arg2) { - tcg_out_movi(s, type, TCG_REG_R0, arg2); - arg2 = TCG_REG_R0; - } - tcg_out32(s, op | RA(arg1) | RB(arg2)); - } -} - -static void tcg_out_setcond_eq0(TCGContext *s, TCGType type, - TCGReg dst, TCGReg src) -{ - tcg_out32(s, (type == TCG_TYPE_I64 ? CNTLZD : CNTLZW) | RS(src) | RA(dst)); - tcg_out_shri64(s, dst, dst, type == TCG_TYPE_I64 ? 6 : 5); -} - -static void tcg_out_setcond_ne0(TCGContext *s, TCGReg dst, TCGReg src) -{ - /* X != 0 implies X + -1 generates a carry. Extra addition - trickery means: R = X-1 + ~X + C = X-1 + (-X+1) + C = C. */ - if (dst != src) { - tcg_out32(s, ADDIC | TAI(dst, src, -1)); - tcg_out32(s, SUBFE | TAB(dst, dst, src)); - } else { - tcg_out32(s, ADDIC | TAI(TCG_REG_R0, src, -1)); - tcg_out32(s, SUBFE | TAB(dst, TCG_REG_R0, src)); - } -} - -static TCGReg tcg_gen_setcond_xor(TCGContext *s, TCGReg arg1, TCGArg arg2, - bool const_arg2) -{ - if (const_arg2) { - if ((uint32_t)arg2 == arg2) { - tcg_out_xori32(s, TCG_REG_R0, arg1, arg2); - } else { - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R0, arg2); - tcg_out32(s, XOR | SAB(arg1, TCG_REG_R0, TCG_REG_R0)); - } - } else { - tcg_out32(s, XOR | SAB(arg1, TCG_REG_R0, arg2)); - } - return TCG_REG_R0; -} - -static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, - TCGArg arg0, TCGArg arg1, TCGArg arg2, - int const_arg2) -{ - int crop, sh; - - /* Ignore high bits of a potential constant arg2. */ - if (type == TCG_TYPE_I32) { - arg2 = (uint32_t)arg2; - } - - /* Handle common and trivial cases before handling anything else. */ - if (arg2 == 0) { - switch (cond) { - case TCG_COND_EQ: - tcg_out_setcond_eq0(s, type, arg0, arg1); - return; - case TCG_COND_NE: - if (type == TCG_TYPE_I32) { - tcg_out_ext32u(s, TCG_REG_R0, arg1); - arg1 = TCG_REG_R0; - } - tcg_out_setcond_ne0(s, arg0, arg1); - return; - case TCG_COND_GE: - tcg_out32(s, NOR | SAB(arg1, arg0, arg1)); - arg1 = arg0; - /* FALLTHRU */ - case TCG_COND_LT: - /* Extract the sign bit. */ - tcg_out_rld(s, RLDICL, arg0, arg1, - type == TCG_TYPE_I64 ? 1 : 33, 63); - return; - default: - break; - } - } - - /* If we have ISEL, we can implement everything with 3 or 4 insns. - All other cases below are also at least 3 insns, so speed up the - code generator by not considering them and always using ISEL. */ - if (HAVE_ISEL) { - int isel, tab; - - tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); - - isel = tcg_to_isel[cond]; - - tcg_out_movi(s, type, arg0, 1); - if (isel & 1) { - /* arg0 = (bc ? 0 : 1) */ - tab = TAB(arg0, 0, arg0); - isel &= ~1; - } else { - /* arg0 = (bc ? 1 : 0) */ - tcg_out_movi(s, type, TCG_REG_R0, 0); - tab = TAB(arg0, arg0, TCG_REG_R0); - } - tcg_out32(s, isel | tab); - return; - } - - switch (cond) { - case TCG_COND_EQ: - arg1 = tcg_gen_setcond_xor(s, arg1, arg2, const_arg2); - tcg_out_setcond_eq0(s, type, arg0, arg1); - return; - - case TCG_COND_NE: - arg1 = tcg_gen_setcond_xor(s, arg1, arg2, const_arg2); - /* Discard the high bits only once, rather than both inputs. */ - if (type == TCG_TYPE_I32) { - tcg_out_ext32u(s, TCG_REG_R0, arg1); - arg1 = TCG_REG_R0; - } - tcg_out_setcond_ne0(s, arg0, arg1); - return; - - case TCG_COND_GT: - case TCG_COND_GTU: - sh = 30; - crop = 0; - goto crtest; - - case TCG_COND_LT: - case TCG_COND_LTU: - sh = 29; - crop = 0; - goto crtest; - - case TCG_COND_GE: - case TCG_COND_GEU: - sh = 31; - crop = CRNOR | BT(7, CR_EQ) | BA(7, CR_LT) | BB(7, CR_LT); - goto crtest; - - case TCG_COND_LE: - case TCG_COND_LEU: - sh = 31; - crop = CRNOR | BT(7, CR_EQ) | BA(7, CR_GT) | BB(7, CR_GT); - crtest: - tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); - if (crop) { - tcg_out32(s, crop); - } - tcg_out32(s, MFOCRF | RT(TCG_REG_R0) | FXM(7)); - tcg_out_rlw(s, RLWINM, arg0, TCG_REG_R0, sh, 31, 31); - break; - - default: - tcg_abort(); - } -} - -static void tcg_out_bc(TCGContext *s, int bc, int label_index) -{ - TCGLabel *l = &s->labels[label_index]; - - if (l->has_value) { - tcg_out32(s, bc | reloc_pc14_val(s->code_ptr, l->u.value_ptr)); - } else { - tcg_out_reloc(s, s->code_ptr, R_PPC_REL14, label_index, 0); - tcg_out_bc_noaddr(s, bc); - } -} - -static void tcg_out_brcond(TCGContext *s, TCGCond cond, - TCGArg arg1, TCGArg arg2, int const_arg2, - int label_index, TCGType type) -{ - tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); - tcg_out_bc(s, tcg_to_bc[cond], label_index); -} - -static void tcg_out_movcond(TCGContext *s, TCGType type, TCGCond cond, - TCGArg dest, TCGArg c1, TCGArg c2, TCGArg v1, - TCGArg v2, bool const_c2) -{ - /* If for some reason both inputs are zero, don't produce bad code. */ - if (v1 == 0 && v2 == 0) { - tcg_out_movi(s, type, dest, 0); - return; - } - - tcg_out_cmp(s, cond, c1, c2, const_c2, 7, type); - - if (HAVE_ISEL) { - int isel = tcg_to_isel[cond]; - - /* Swap the V operands if the operation indicates inversion. */ - if (isel & 1) { - int t = v1; - v1 = v2; - v2 = t; - isel &= ~1; - } - /* V1 == 0 is handled by isel; V2 == 0 must be handled by hand. */ - if (v2 == 0) { - tcg_out_movi(s, type, TCG_REG_R0, 0); - } - tcg_out32(s, isel | TAB(dest, v1, v2)); - } else { - if (dest == v2) { - cond = tcg_invert_cond(cond); - v2 = v1; - } else if (dest != v1) { - if (v1 == 0) { - tcg_out_movi(s, type, dest, 0); - } else { - tcg_out_mov(s, type, dest, v1); - } - } - /* Branch forward over one insn */ - tcg_out32(s, tcg_to_bc[cond] | 8); - if (v2 == 0) { - tcg_out_movi(s, type, dest, 0); - } else { - tcg_out_mov(s, type, dest, v2); - } - } -} - -void ppc_tb_set_jmp_target(uintptr_t jmp_addr, uintptr_t addr) -{ - TCGContext s; - - s.code_buf = s.code_ptr = (tcg_insn_unit *)jmp_addr; - tcg_out_b(&s, 0, (tcg_insn_unit *)addr); - flush_icache_range(jmp_addr, jmp_addr + tcg_current_code_size(&s)); -} - -static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, - const int *const_args) -{ - TCGArg a0, a1, a2; - int c; - - switch (opc) { - case INDEX_op_exit_tb: - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R3, args[0]); - tcg_out_b(s, 0, tb_ret_addr); - break; - case INDEX_op_goto_tb: - if (s->tb_jmp_offset) { - /* Direct jump method. */ - s->tb_jmp_offset[args[0]] = tcg_current_code_size(s); - s->code_ptr += 7; - } else { - /* Indirect jump method. */ - tcg_abort(); - } - s->tb_next_offset[args[0]] = tcg_current_code_size(s); - break; - case INDEX_op_br: - { - TCGLabel *l = &s->labels[args[0]]; - - if (l->has_value) { - tcg_out_b(s, 0, l->u.value_ptr); - } else { - tcg_out_reloc(s, s->code_ptr, R_PPC_REL24, args[0], 0); - tcg_out_b_noaddr(s, B); - } - } - break; - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - tcg_out_mem_long(s, LBZ, LBZX, args[0], args[1], args[2]); - break; - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - tcg_out_mem_long(s, LBZ, LBZX, args[0], args[1], args[2]); - tcg_out32(s, EXTSB | RS(args[0]) | RA(args[0])); - break; - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - tcg_out_mem_long(s, LHZ, LHZX, args[0], args[1], args[2]); - break; - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - tcg_out_mem_long(s, LHA, LHAX, args[0], args[1], args[2]); - break; - case INDEX_op_ld_i32: - case INDEX_op_ld32u_i64: - tcg_out_mem_long(s, LWZ, LWZX, args[0], args[1], args[2]); - break; - case INDEX_op_ld32s_i64: - tcg_out_mem_long(s, LWA, LWAX, args[0], args[1], args[2]); - break; - case INDEX_op_ld_i64: - tcg_out_mem_long(s, LD, LDX, args[0], args[1], args[2]); - break; - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - tcg_out_mem_long(s, STB, STBX, args[0], args[1], args[2]); - break; - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - tcg_out_mem_long(s, STH, STHX, args[0], args[1], args[2]); - break; - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - tcg_out_mem_long(s, STW, STWX, args[0], args[1], args[2]); - break; - case INDEX_op_st_i64: - tcg_out_mem_long(s, STD, STDX, args[0], args[1], args[2]); - break; - - case INDEX_op_add_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - do_addi_32: - tcg_out_mem_long(s, ADDI, ADD, a0, a1, (int32_t)a2); - } else { - tcg_out32(s, ADD | TAB(a0, a1, a2)); - } - break; - case INDEX_op_sub_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[1]) { - if (const_args[2]) { - tcg_out_movi(s, TCG_TYPE_I32, a0, a1 - a2); - } else { - tcg_out32(s, SUBFIC | TAI(a0, a2, a1)); - } - } else if (const_args[2]) { - a2 = -a2; - goto do_addi_32; - } else { - tcg_out32(s, SUBF | TAB(a0, a2, a1)); - } - break; - - case INDEX_op_and_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_andi32(s, a0, a1, a2); - } else { - tcg_out32(s, AND | SAB(a1, a0, a2)); - } - break; - case INDEX_op_and_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_andi64(s, a0, a1, a2); - } else { - tcg_out32(s, AND | SAB(a1, a0, a2)); - } - break; - case INDEX_op_or_i64: - case INDEX_op_or_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_ori32(s, a0, a1, a2); - } else { - tcg_out32(s, OR | SAB(a1, a0, a2)); - } - break; - case INDEX_op_xor_i64: - case INDEX_op_xor_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_xori32(s, a0, a1, a2); - } else { - tcg_out32(s, XOR | SAB(a1, a0, a2)); - } - break; - case INDEX_op_andc_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_andi32(s, a0, a1, ~a2); - } else { - tcg_out32(s, ANDC | SAB(a1, a0, a2)); - } - break; - case INDEX_op_andc_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_andi64(s, a0, a1, ~a2); - } else { - tcg_out32(s, ANDC | SAB(a1, a0, a2)); - } - break; - case INDEX_op_orc_i32: - if (const_args[2]) { - tcg_out_ori32(s, args[0], args[1], ~args[2]); - break; - } - /* FALLTHRU */ - case INDEX_op_orc_i64: - tcg_out32(s, ORC | SAB(args[1], args[0], args[2])); - break; - case INDEX_op_eqv_i32: - if (const_args[2]) { - tcg_out_xori32(s, args[0], args[1], ~args[2]); - break; - } - /* FALLTHRU */ - case INDEX_op_eqv_i64: - tcg_out32(s, EQV | SAB(args[1], args[0], args[2])); - break; - case INDEX_op_nand_i32: - case INDEX_op_nand_i64: - tcg_out32(s, NAND | SAB(args[1], args[0], args[2])); - break; - case INDEX_op_nor_i32: - case INDEX_op_nor_i64: - tcg_out32(s, NOR | SAB(args[1], args[0], args[2])); - break; - - case INDEX_op_mul_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out32(s, MULLI | TAI(a0, a1, a2)); - } else { - tcg_out32(s, MULLW | TAB(a0, a1, a2)); - } - break; - - case INDEX_op_div_i32: - tcg_out32(s, DIVW | TAB(args[0], args[1], args[2])); - break; - - case INDEX_op_divu_i32: - tcg_out32(s, DIVWU | TAB(args[0], args[1], args[2])); - break; - - case INDEX_op_shl_i32: - if (const_args[2]) { - tcg_out_rlw(s, RLWINM, args[0], args[1], args[2], 0, 31 - args[2]); - } else { - tcg_out32(s, SLW | SAB(args[1], args[0], args[2])); - } - break; - case INDEX_op_shr_i32: - if (const_args[2]) { - tcg_out_rlw(s, RLWINM, args[0], args[1], 32 - args[2], args[2], 31); - } else { - tcg_out32(s, SRW | SAB(args[1], args[0], args[2])); - } - break; - case INDEX_op_sar_i32: - if (const_args[2]) { - tcg_out32(s, SRAWI | RS(args[1]) | RA(args[0]) | SH(args[2])); - } else { - tcg_out32(s, SRAW | SAB(args[1], args[0], args[2])); - } - break; - case INDEX_op_rotl_i32: - if (const_args[2]) { - tcg_out_rlw(s, RLWINM, args[0], args[1], args[2], 0, 31); - } else { - tcg_out32(s, RLWNM | SAB(args[1], args[0], args[2]) - | MB(0) | ME(31)); - } - break; - case INDEX_op_rotr_i32: - if (const_args[2]) { - tcg_out_rlw(s, RLWINM, args[0], args[1], 32 - args[2], 0, 31); - } else { - tcg_out32(s, SUBFIC | TAI(TCG_REG_R0, args[2], 32)); - tcg_out32(s, RLWNM | SAB(args[1], args[0], TCG_REG_R0) - | MB(0) | ME(31)); - } - break; - - case INDEX_op_brcond_i32: - tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], - args[3], TCG_TYPE_I32); - break; - - case INDEX_op_brcond_i64: - tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], - args[3], TCG_TYPE_I64); - break; - - case INDEX_op_neg_i32: - case INDEX_op_neg_i64: - tcg_out32(s, NEG | RT(args[0]) | RA(args[1])); - break; - - case INDEX_op_not_i32: - case INDEX_op_not_i64: - tcg_out32(s, NOR | SAB(args[1], args[0], args[1])); - break; - - case INDEX_op_add_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - do_addi_64: - tcg_out_mem_long(s, ADDI, ADD, a0, a1, a2); - } else { - tcg_out32(s, ADD | TAB(a0, a1, a2)); - } - break; - case INDEX_op_sub_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[1]) { - if (const_args[2]) { - tcg_out_movi(s, TCG_TYPE_I64, a0, a1 - a2); - } else { - tcg_out32(s, SUBFIC | TAI(a0, a2, a1)); - } - } else if (const_args[2]) { - a2 = -a2; - goto do_addi_64; - } else { - tcg_out32(s, SUBF | TAB(a0, a2, a1)); - } - break; - - case INDEX_op_shl_i64: - if (const_args[2]) { - tcg_out_shli64(s, args[0], args[1], args[2]); - } else { - tcg_out32(s, SLD | SAB(args[1], args[0], args[2])); - } - break; - case INDEX_op_shr_i64: - if (const_args[2]) { - tcg_out_shri64(s, args[0], args[1], args[2]); - } else { - tcg_out32(s, SRD | SAB(args[1], args[0], args[2])); - } - break; - case INDEX_op_sar_i64: - if (const_args[2]) { - int sh = SH(args[2] & 0x1f) | (((args[2] >> 5) & 1) << 1); - tcg_out32(s, SRADI | RA(args[0]) | RS(args[1]) | sh); - } else { - tcg_out32(s, SRAD | SAB(args[1], args[0], args[2])); - } - break; - case INDEX_op_rotl_i64: - if (const_args[2]) { - tcg_out_rld(s, RLDICL, args[0], args[1], args[2], 0); - } else { - tcg_out32(s, RLDCL | SAB(args[1], args[0], args[2]) | MB64(0)); - } - break; - case INDEX_op_rotr_i64: - if (const_args[2]) { - tcg_out_rld(s, RLDICL, args[0], args[1], 64 - args[2], 0); - } else { - tcg_out32(s, SUBFIC | TAI(TCG_REG_R0, args[2], 64)); - tcg_out32(s, RLDCL | SAB(args[1], args[0], TCG_REG_R0) | MB64(0)); - } - break; - - case INDEX_op_mul_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out32(s, MULLI | TAI(a0, a1, a2)); - } else { - tcg_out32(s, MULLD | TAB(a0, a1, a2)); - } - break; - case INDEX_op_div_i64: - tcg_out32(s, DIVD | TAB(args[0], args[1], args[2])); - break; - case INDEX_op_divu_i64: - tcg_out32(s, DIVDU | TAB(args[0], args[1], args[2])); - break; - - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, args[0], args[1], args[2], args[3]); - break; - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, args[0], args[1], args[2], args[3]); - break; - - case INDEX_op_ext8s_i32: - case INDEX_op_ext8s_i64: - c = EXTSB; - goto gen_ext; - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - c = EXTSH; - goto gen_ext; - case INDEX_op_ext32s_i64: - c = EXTSW; - goto gen_ext; - gen_ext: - tcg_out32(s, c | RS(args[1]) | RA(args[0])); - break; - - case INDEX_op_setcond_i32: - tcg_out_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], args[2], - const_args[2]); - break; - case INDEX_op_setcond_i64: - tcg_out_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1], args[2], - const_args[2]); - break; - - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: - a0 = args[0], a1 = args[1]; - /* a1 = abcd */ - if (a0 != a1) { - /* a0 = (a1 r<< 24) & 0xff # 000c */ - tcg_out_rlw(s, RLWINM, a0, a1, 24, 24, 31); - /* a0 = (a0 & ~0xff00) | (a1 r<< 8) & 0xff00 # 00dc */ - tcg_out_rlw(s, RLWIMI, a0, a1, 8, 16, 23); - } else { - /* r0 = (a1 r<< 8) & 0xff00 # 00d0 */ - tcg_out_rlw(s, RLWINM, TCG_REG_R0, a1, 8, 16, 23); - /* a0 = (a1 r<< 24) & 0xff # 000c */ - tcg_out_rlw(s, RLWINM, a0, a1, 24, 24, 31); - /* a0 = a0 | r0 # 00dc */ - tcg_out32(s, OR | SAB(TCG_REG_R0, a0, a0)); - } - break; - - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: - /* Stolen from gcc's builtin_bswap32 */ - a1 = args[1]; - a0 = args[0] == a1 ? TCG_REG_R0 : args[0]; - - /* a1 = args[1] # abcd */ - /* a0 = rotate_left (a1, 8) # bcda */ - tcg_out_rlw(s, RLWINM, a0, a1, 8, 0, 31); - /* a0 = (a0 & ~0xff000000) | ((a1 r<< 24) & 0xff000000) # dcda */ - tcg_out_rlw(s, RLWIMI, a0, a1, 24, 0, 7); - /* a0 = (a0 & ~0x0000ff00) | ((a1 r<< 24) & 0x0000ff00) # dcba */ - tcg_out_rlw(s, RLWIMI, a0, a1, 24, 16, 23); - - if (a0 == TCG_REG_R0) { - tcg_out_mov(s, TCG_TYPE_I64, args[0], a0); - } - break; - - case INDEX_op_bswap64_i64: - a0 = args[0], a1 = args[1], a2 = TCG_REG_R0; - if (a0 == a1) { - a0 = TCG_REG_R0; - a2 = a1; - } - - /* a1 = # abcd efgh */ - /* a0 = rl32(a1, 8) # 0000 fghe */ - tcg_out_rlw(s, RLWINM, a0, a1, 8, 0, 31); - /* a0 = dep(a0, rl32(a1, 24), 0xff000000) # 0000 hghe */ - tcg_out_rlw(s, RLWIMI, a0, a1, 24, 0, 7); - /* a0 = dep(a0, rl32(a1, 24), 0x0000ff00) # 0000 hgfe */ - tcg_out_rlw(s, RLWIMI, a0, a1, 24, 16, 23); - - /* a0 = rl64(a0, 32) # hgfe 0000 */ - /* a2 = rl64(a1, 32) # efgh abcd */ - tcg_out_rld(s, RLDICL, a0, a0, 32, 0); - tcg_out_rld(s, RLDICL, a2, a1, 32, 0); - - /* a0 = dep(a0, rl32(a2, 8), 0xffffffff) # hgfe bcda */ - tcg_out_rlw(s, RLWIMI, a0, a2, 8, 0, 31); - /* a0 = dep(a0, rl32(a2, 24), 0xff000000) # hgfe dcda */ - tcg_out_rlw(s, RLWIMI, a0, a2, 24, 0, 7); - /* a0 = dep(a0, rl32(a2, 24), 0x0000ff00) # hgfe dcba */ - tcg_out_rlw(s, RLWIMI, a0, a2, 24, 16, 23); - - if (a0 == 0) { - tcg_out_mov(s, TCG_TYPE_I64, args[0], a0); - } - break; - - case INDEX_op_deposit_i32: - if (const_args[2]) { - uint32_t mask = ((2u << (args[4] - 1)) - 1) << args[3]; - tcg_out_andi32(s, args[0], args[0], ~mask); - } else { - tcg_out_rlw(s, RLWIMI, args[0], args[2], args[3], - 32 - args[3] - args[4], 31 - args[3]); - } - break; - case INDEX_op_deposit_i64: - if (const_args[2]) { - uint64_t mask = ((2ull << (args[4] - 1)) - 1) << args[3]; - tcg_out_andi64(s, args[0], args[0], ~mask); - } else { - tcg_out_rld(s, RLDIMI, args[0], args[2], args[3], - 64 - args[3] - args[4]); - } - break; - - case INDEX_op_movcond_i32: - tcg_out_movcond(s, TCG_TYPE_I32, args[5], args[0], args[1], args[2], - args[3], args[4], const_args[2]); - break; - case INDEX_op_movcond_i64: - tcg_out_movcond(s, TCG_TYPE_I64, args[5], args[0], args[1], args[2], - args[3], args[4], const_args[2]); - break; - - case INDEX_op_add2_i64: - /* Note that the CA bit is defined based on the word size of the - environment. So in 64-bit mode it's always carry-out of bit 63. - The fallback code using deposit works just as well for 32-bit. */ - a0 = args[0], a1 = args[1]; - if (a0 == args[3] || (!const_args[5] && a0 == args[5])) { - a0 = TCG_REG_R0; - } - if (const_args[4]) { - tcg_out32(s, ADDIC | TAI(a0, args[2], args[4])); - } else { - tcg_out32(s, ADDC | TAB(a0, args[2], args[4])); - } - if (const_args[5]) { - tcg_out32(s, (args[5] ? ADDME : ADDZE) | RT(a1) | RA(args[3])); - } else { - tcg_out32(s, ADDE | TAB(a1, args[3], args[5])); - } - if (a0 != args[0]) { - tcg_out_mov(s, TCG_TYPE_I64, args[0], a0); - } - break; - - case INDEX_op_sub2_i64: - a0 = args[0], a1 = args[1]; - if (a0 == args[5] || (!const_args[4] && a0 == args[4])) { - a0 = TCG_REG_R0; - } - if (const_args[2]) { - tcg_out32(s, SUBFIC | TAI(a0, args[3], args[2])); - } else { - tcg_out32(s, SUBFC | TAB(a0, args[3], args[2])); - } - if (const_args[4]) { - tcg_out32(s, (args[4] ? SUBFME : SUBFZE) | RT(a1) | RA(args[5])); - } else { - tcg_out32(s, SUBFE | TAB(a1, args[5], args[4])); - } - if (a0 != args[0]) { - tcg_out_mov(s, TCG_TYPE_I64, args[0], a0); - } - break; - - case INDEX_op_muluh_i64: - tcg_out32(s, MULHDU | TAB(args[0], args[1], args[2])); - break; - case INDEX_op_mulsh_i64: - tcg_out32(s, MULHD | TAB(args[0], args[1], args[2])); - break; - - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_mov_i64: - case INDEX_op_movi_i32: /* Always emitted via tcg_out_movi. */ - case INDEX_op_movi_i64: - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - default: - tcg_abort(); - } -} - -static const TCGTargetOpDef ppc_op_defs[] = { - { INDEX_op_exit_tb, { } }, - { INDEX_op_goto_tb, { } }, - { INDEX_op_br, { } }, - - { INDEX_op_ld8u_i32, { "r", "r" } }, - { INDEX_op_ld8s_i32, { "r", "r" } }, - { INDEX_op_ld16u_i32, { "r", "r" } }, - { INDEX_op_ld16s_i32, { "r", "r" } }, - { INDEX_op_ld_i32, { "r", "r" } }, - { INDEX_op_ld_i64, { "r", "r" } }, - { INDEX_op_st8_i32, { "r", "r" } }, - { INDEX_op_st8_i64, { "r", "r" } }, - { INDEX_op_st16_i32, { "r", "r" } }, - { INDEX_op_st16_i64, { "r", "r" } }, - { INDEX_op_st_i32, { "r", "r" } }, - { INDEX_op_st_i64, { "r", "r" } }, - { INDEX_op_st32_i64, { "r", "r" } }, - - { INDEX_op_ld8u_i64, { "r", "r" } }, - { INDEX_op_ld8s_i64, { "r", "r" } }, - { INDEX_op_ld16u_i64, { "r", "r" } }, - { INDEX_op_ld16s_i64, { "r", "r" } }, - { INDEX_op_ld32u_i64, { "r", "r" } }, - { INDEX_op_ld32s_i64, { "r", "r" } }, - - { INDEX_op_add_i32, { "r", "r", "ri" } }, - { INDEX_op_mul_i32, { "r", "r", "rI" } }, - { INDEX_op_div_i32, { "r", "r", "r" } }, - { INDEX_op_divu_i32, { "r", "r", "r" } }, - { INDEX_op_sub_i32, { "r", "rI", "ri" } }, - { INDEX_op_and_i32, { "r", "r", "ri" } }, - { INDEX_op_or_i32, { "r", "r", "ri" } }, - { INDEX_op_xor_i32, { "r", "r", "ri" } }, - { INDEX_op_andc_i32, { "r", "r", "ri" } }, - { INDEX_op_orc_i32, { "r", "r", "ri" } }, - { INDEX_op_eqv_i32, { "r", "r", "ri" } }, - { INDEX_op_nand_i32, { "r", "r", "r" } }, - { INDEX_op_nor_i32, { "r", "r", "r" } }, - - { INDEX_op_shl_i32, { "r", "r", "ri" } }, - { INDEX_op_shr_i32, { "r", "r", "ri" } }, - { INDEX_op_sar_i32, { "r", "r", "ri" } }, - { INDEX_op_rotl_i32, { "r", "r", "ri" } }, - { INDEX_op_rotr_i32, { "r", "r", "ri" } }, - - { INDEX_op_brcond_i32, { "r", "ri" } }, - { INDEX_op_brcond_i64, { "r", "ri" } }, - - { INDEX_op_neg_i32, { "r", "r" } }, - { INDEX_op_not_i32, { "r", "r" } }, - - { INDEX_op_add_i64, { "r", "r", "rT" } }, - { INDEX_op_sub_i64, { "r", "rI", "rT" } }, - { INDEX_op_and_i64, { "r", "r", "ri" } }, - { INDEX_op_or_i64, { "r", "r", "rU" } }, - { INDEX_op_xor_i64, { "r", "r", "rU" } }, - { INDEX_op_andc_i64, { "r", "r", "ri" } }, - { INDEX_op_orc_i64, { "r", "r", "r" } }, - { INDEX_op_eqv_i64, { "r", "r", "r" } }, - { INDEX_op_nand_i64, { "r", "r", "r" } }, - { INDEX_op_nor_i64, { "r", "r", "r" } }, - - { INDEX_op_shl_i64, { "r", "r", "ri" } }, - { INDEX_op_shr_i64, { "r", "r", "ri" } }, - { INDEX_op_sar_i64, { "r", "r", "ri" } }, - { INDEX_op_rotl_i64, { "r", "r", "ri" } }, - { INDEX_op_rotr_i64, { "r", "r", "ri" } }, - - { INDEX_op_mul_i64, { "r", "r", "rI" } }, - { INDEX_op_div_i64, { "r", "r", "r" } }, - { INDEX_op_divu_i64, { "r", "r", "r" } }, - - { INDEX_op_neg_i64, { "r", "r" } }, - { INDEX_op_not_i64, { "r", "r" } }, - - { INDEX_op_qemu_ld_i32, { "r", "L" } }, - { INDEX_op_qemu_ld_i64, { "r", "L" } }, - { INDEX_op_qemu_st_i32, { "S", "S" } }, - { INDEX_op_qemu_st_i64, { "S", "S" } }, - - { INDEX_op_ext8s_i32, { "r", "r" } }, - { INDEX_op_ext16s_i32, { "r", "r" } }, - { INDEX_op_ext8s_i64, { "r", "r" } }, - { INDEX_op_ext16s_i64, { "r", "r" } }, - { INDEX_op_ext32s_i64, { "r", "r" } }, - - { INDEX_op_setcond_i32, { "r", "r", "ri" } }, - { INDEX_op_setcond_i64, { "r", "r", "ri" } }, - { INDEX_op_movcond_i32, { "r", "r", "ri", "rZ", "rZ" } }, - { INDEX_op_movcond_i64, { "r", "r", "ri", "rZ", "rZ" } }, - - { INDEX_op_bswap16_i32, { "r", "r" } }, - { INDEX_op_bswap16_i64, { "r", "r" } }, - { INDEX_op_bswap32_i32, { "r", "r" } }, - { INDEX_op_bswap32_i64, { "r", "r" } }, - { INDEX_op_bswap64_i64, { "r", "r" } }, - - { INDEX_op_deposit_i32, { "r", "0", "rZ" } }, - { INDEX_op_deposit_i64, { "r", "0", "rZ" } }, - - { INDEX_op_add2_i64, { "r", "r", "r", "r", "rI", "rZM" } }, - { INDEX_op_sub2_i64, { "r", "r", "rI", "r", "rZM", "r" } }, - { INDEX_op_mulsh_i64, { "r", "r", "r" } }, - { INDEX_op_muluh_i64, { "r", "r", "r" } }, - - { -1 }, -}; - -static void tcg_target_init(TCGContext *s) -{ - unsigned long hwcap = qemu_getauxval(AT_HWCAP); - if (hwcap & PPC_FEATURE_ARCH_2_06) { - have_isa_2_06 = true; - } - - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffffffff); - tcg_regset_set32(tcg_target_call_clobber_regs, 0, - (1 << TCG_REG_R0) | - (1 << TCG_REG_R2) | - (1 << TCG_REG_R3) | - (1 << TCG_REG_R4) | - (1 << TCG_REG_R5) | - (1 << TCG_REG_R6) | - (1 << TCG_REG_R7) | - (1 << TCG_REG_R8) | - (1 << TCG_REG_R9) | - (1 << TCG_REG_R10) | - (1 << TCG_REG_R11) | - (1 << TCG_REG_R12)); - - tcg_regset_clear(s->reserved_regs); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); /* tcg temp */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R1); /* stack pointer */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R2); /* mem temp */ -#ifdef __APPLE__ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R11); /* ??? */ -#endif - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R13); /* thread pointer */ - - tcg_add_target_add_op_defs(ppc_op_defs); -} - -typedef struct { - DebugFrameCIE cie; - DebugFrameFDEHeader fde; - uint8_t fde_def_cfa[4]; - uint8_t fde_reg_ofs[ARRAY_SIZE(tcg_target_callee_save_regs) * 2 + 3]; -} DebugFrame; - -/* We're expecting a 2 byte uleb128 encoded value. */ -QEMU_BUILD_BUG_ON(FRAME_SIZE >= (1 << 14)); - -#define ELF_HOST_MACHINE EM_PPC64 - -static DebugFrame debug_frame = { - .cie.len = sizeof(DebugFrameCIE)-4, /* length after .len member */ - .cie.id = -1, - .cie.version = 1, - .cie.code_align = 1, - .cie.data_align = 0x78, /* sleb128 -8 */ - .cie.return_column = 65, - - /* Total FDE size does not include the "len" member. */ - .fde.len = sizeof(DebugFrame) - offsetof(DebugFrame, fde.cie_offset), - - .fde_def_cfa = { - 12, 1, /* DW_CFA_def_cfa r1, ... */ - (FRAME_SIZE & 0x7f) | 0x80, /* ... uleb128 FRAME_SIZE */ - (FRAME_SIZE >> 7) - }, - .fde_reg_ofs = { - 0x11, 65, 0x7e, /* DW_CFA_offset_extended_sf, lr, 16 */ - } -}; - -void tcg_register_jit(void *buf, size_t buf_size) -{ - uint8_t *p = &debug_frame.fde_reg_ofs[3]; - int i; - - for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); ++i, p += 2) { - p[0] = 0x80 + tcg_target_callee_save_regs[i]; - p[1] = (FRAME_SIZE - (REG_SAVE_BOT + i * 8)) / 8; - } - - debug_frame.fde.func_start = (tcg_target_long) buf; - debug_frame.fde.func_len = buf_size; - - tcg_register_jit_int(buf, buf_size, &debug_frame, sizeof(debug_frame)); -} diff --git a/tcg/ppc64/tcg-target.h b/tcg/ppc64/tcg-target.h deleted file mode 100644 index f2360c869a..0000000000 --- a/tcg/ppc64/tcg-target.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Tiny Code Generator for QEMU - * - * Copyright (c) 2008 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef TCG_TARGET_PPC64 -#define TCG_TARGET_PPC64 1 - -#define TCG_TARGET_NB_REGS 32 -#define TCG_TARGET_INSN_UNIT_SIZE 4 - -typedef enum { - TCG_REG_R0 = 0, - TCG_REG_R1, - TCG_REG_R2, - TCG_REG_R3, - TCG_REG_R4, - TCG_REG_R5, - TCG_REG_R6, - TCG_REG_R7, - TCG_REG_R8, - TCG_REG_R9, - TCG_REG_R10, - TCG_REG_R11, - TCG_REG_R12, - TCG_REG_R13, - TCG_REG_R14, - TCG_REG_R15, - TCG_REG_R16, - TCG_REG_R17, - TCG_REG_R18, - TCG_REG_R19, - TCG_REG_R20, - TCG_REG_R21, - TCG_REG_R22, - TCG_REG_R23, - TCG_REG_R24, - TCG_REG_R25, - TCG_REG_R26, - TCG_REG_R27, - TCG_REG_R28, - TCG_REG_R29, - TCG_REG_R30, - TCG_REG_R31 -} TCGReg; - -/* used for function call generation */ -#define TCG_REG_CALL_STACK TCG_REG_R1 -#define TCG_TARGET_STACK_ALIGN 16 -#define TCG_TARGET_CALL_STACK_OFFSET 48 - -/* optional instructions automatically implemented */ -#define TCG_TARGET_HAS_ext8u_i32 0 /* andi */ -#define TCG_TARGET_HAS_ext16u_i32 0 -#define TCG_TARGET_HAS_ext8u_i64 0 -#define TCG_TARGET_HAS_ext16u_i64 0 -#define TCG_TARGET_HAS_ext32u_i64 0 - -/* optional instructions */ -#define TCG_TARGET_HAS_div_i32 1 -#define TCG_TARGET_HAS_rem_i32 0 -#define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_bswap16_i32 1 -#define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_neg_i32 1 -#define TCG_TARGET_HAS_andc_i32 1 -#define TCG_TARGET_HAS_orc_i32 1 -#define TCG_TARGET_HAS_eqv_i32 1 -#define TCG_TARGET_HAS_nand_i32 1 -#define TCG_TARGET_HAS_nor_i32 1 -#define TCG_TARGET_HAS_deposit_i32 1 -#define TCG_TARGET_HAS_movcond_i32 1 -#define TCG_TARGET_HAS_add2_i32 0 -#define TCG_TARGET_HAS_sub2_i32 0 -#define TCG_TARGET_HAS_mulu2_i32 0 -#define TCG_TARGET_HAS_muls2_i32 0 -#define TCG_TARGET_HAS_muluh_i32 0 -#define TCG_TARGET_HAS_mulsh_i32 0 -#define TCG_TARGET_HAS_trunc_shr_i32 0 - -#define TCG_TARGET_HAS_div_i64 1 -#define TCG_TARGET_HAS_rem_i64 0 -#define TCG_TARGET_HAS_rot_i64 1 -#define TCG_TARGET_HAS_ext8s_i64 1 -#define TCG_TARGET_HAS_ext16s_i64 1 -#define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_bswap16_i64 1 -#define TCG_TARGET_HAS_bswap32_i64 1 -#define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_neg_i64 1 -#define TCG_TARGET_HAS_andc_i64 1 -#define TCG_TARGET_HAS_orc_i64 1 -#define TCG_TARGET_HAS_eqv_i64 1 -#define TCG_TARGET_HAS_nand_i64 1 -#define TCG_TARGET_HAS_nor_i64 1 -#define TCG_TARGET_HAS_deposit_i64 1 -#define TCG_TARGET_HAS_movcond_i64 1 -#define TCG_TARGET_HAS_add2_i64 1 -#define TCG_TARGET_HAS_sub2_i64 1 -#define TCG_TARGET_HAS_mulu2_i64 0 -#define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_muluh_i64 1 -#define TCG_TARGET_HAS_mulsh_i64 1 - -#define TCG_AREG0 TCG_REG_R27 - -#define TCG_TARGET_EXTEND_ARGS 1 - -#endif diff --git a/tcg/tcg.c b/tcg/tcg.c index dae4b7cb00..815eec1a78 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -37,7 +37,6 @@ #endif #include "qemu-common.h" -#include "qemu/cache-utils.h" #include "qemu/host-utils.h" #include "qemu/timer.h" diff --git a/tests/Makefile b/tests/Makefile index 4caf7deb89..7e53d0d618 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -27,6 +27,8 @@ check-unit-y += tests/test-string-input-visitor$(EXESUF) gcov-files-test-string-input-visitor-y = qapi/string-input-visitor.c check-unit-y += tests/test-string-output-visitor$(EXESUF) gcov-files-test-string-output-visitor-y = qapi/string-output-visitor.c +check-unit-y += tests/test-qmp-event$(EXESUF) +gcov-files-test-qmp-event-y += qapi/qmp-event.c check-unit-y += tests/test-opts-visitor$(EXESUF) gcov-files-test-opts-visitor-y = qapi/opts-visitor.c check-unit-y += tests/test-coroutine$(EXESUF) @@ -199,9 +201,10 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \ include-simple.json include-relpath.json include-format-err.json \ include-non-file.json include-no-file.json include-before-err.json \ include-nested-err.json include-self-cycle.json include-cycle.json \ - include-repetition.json) + include-repetition.json event-nest-struct.json) -GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h +GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \ + tests/test-qmp-commands.h tests/test-qapi-event.h test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \ tests/check-qlist.o tests/check-qfloat.o tests/check-qjson.o \ @@ -210,9 +213,10 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \ tests/test-qmp-input-visitor.o tests/test-qmp-input-strict.o \ tests/test-qmp-commands.o tests/test-visitor-serialization.o \ tests/test-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \ - tests/test-opts-visitor.o + tests/test-opts-visitor.o tests/test-qmp-event.o -test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o +test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \ + tests/test-qapi-event.o $(test-obj-y): QEMU_INCLUDES += -Itests QEMU_CFLAGS += -I$(SRC_PATH)/tests @@ -264,9 +268,15 @@ $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-com $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \ $(gen-out-type) -o tests -p "test-" -i $<, \ " GEN $@") +tests/test-qapi-event.c tests/test-qapi-event.h :\ +$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-event.py + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py \ + $(gen-out-type) -o tests -p "test-" -i $<, \ + " GEN $@") tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a +tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a diff --git a/tests/check-qjson.c b/tests/check-qjson.c index 4e74548109..95497a037e 100644 --- a/tests/check-qjson.c +++ b/tests/check-qjson.c @@ -45,6 +45,13 @@ static void escaped_string(void) { "\"single byte utf-8 \\u0020\"", "single byte utf-8 ", .skip = 1 }, { "\"double byte utf-8 \\u00A2\"", "double byte utf-8 \xc2\xa2" }, { "\"triple byte utf-8 \\u20AC\"", "triple byte utf-8 \xe2\x82\xac" }, + { "'\\b'", "\b", .skip = 1 }, + { "'\\f'", "\f", .skip = 1 }, + { "'\\n'", "\n", .skip = 1 }, + { "'\\r'", "\r", .skip = 1 }, + { "'\\t'", "\t", .skip = 1 }, + { "'\\/'", "/", .skip = 1 }, + { "'\\\\'", "\\", .skip = 1 }, {} }; diff --git a/tests/qapi-schema/event-nest-struct.err b/tests/qapi-schema/event-nest-struct.err new file mode 100644 index 0000000000..e4a0faac9c --- /dev/null +++ b/tests/qapi-schema/event-nest-struct.err @@ -0,0 +1 @@ +tests/qapi-schema/event-nest-struct.json:1: Nested structure define in event is not supported now, event 'EVENT_A', argname 'a' diff --git a/tests/qapi-schema/event-nest-struct.exit b/tests/qapi-schema/event-nest-struct.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/event-nest-struct.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/event-nest-struct.json b/tests/qapi-schema/event-nest-struct.json new file mode 100644 index 0000000000..ee6f3ecb6f --- /dev/null +++ b/tests/qapi-schema/event-nest-struct.json @@ -0,0 +1,2 @@ +{ 'event': 'EVENT_A', + 'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } } diff --git a/tests/qapi-schema/event-nest-struct.out b/tests/qapi-schema/event-nest-struct.out new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/qapi-schema/event-nest-struct.out diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 818c06dc75..ab4d3d96b6 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -89,3 +89,15 @@ '*u16' : [ 'uint16' ], '*i64x': 'int' , '*u64x': 'uint64' } } + +# testing event +{ 'type': 'EventStructOne', + 'data': { 'struct1': 'UserDefOne', 'string': 'str', '*enum2': 'EnumOne' } } + +{ 'event': 'EVENT_A' } +{ 'event': 'EVENT_B', + 'data': { } } +{ 'event': 'EVENT_C', + 'data': { '*a': 'int', '*b': 'UserDefOne', 'c': 'str' } } +{ 'event': 'EVENT_D', + 'data': { 'a' : 'EventStructOne', 'b' : 'str', '*c': 'str', '*enum3': 'EnumOne' } } diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 6cd03f31c3..95e989925b 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -15,7 +15,12 @@ OrderedDict([('command', 'user_def_cmd1'), ('data', OrderedDict([('ud1a', 'UserDefOne')]))]), OrderedDict([('command', 'user_def_cmd2'), ('data', OrderedDict([('ud1a', 'UserDefOne'), ('*ud1b', 'UserDefOne')])), ('returns', 'UserDefTwo')]), OrderedDict([('command', 'user_def_cmd3'), ('data', OrderedDict([('a', 'int'), ('*b', 'int')])), ('returns', 'int')]), - OrderedDict([('type', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))])] + OrderedDict([('type', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))]), + OrderedDict([('type', 'EventStructOne'), ('data', OrderedDict([('struct1', 'UserDefOne'), ('string', 'str'), ('*enum2', 'EnumOne')]))]), + OrderedDict([('event', 'EVENT_A')]), + OrderedDict([('event', 'EVENT_B'), ('data', OrderedDict())]), + OrderedDict([('event', 'EVENT_C'), ('data', OrderedDict([('*a', 'int'), ('*b', 'UserDefOne'), ('c', 'str')]))]), + OrderedDict([('event', 'EVENT_D'), ('data', OrderedDict([('a', 'EventStructOne'), ('b', 'str'), ('*c', 'str'), ('*enum3', 'EnumOne')]))])] [{'enum_name': 'EnumOne', 'enum_values': ['value1', 'value2', 'value3']}, {'enum_name': 'UserDefUnionKind', 'enum_values': None}, {'enum_name': 'UserDefAnonUnionKind', 'enum_values': None}, @@ -28,4 +33,5 @@ OrderedDict([('type', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]), OrderedDict([('type', 'UserDefB'), ('data', OrderedDict([('integer', 'int')]))]), OrderedDict([('type', 'UserDefUnionBase'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]), - OrderedDict([('type', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))])] + OrderedDict([('type', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))]), + OrderedDict([('type', 'EventStructOne'), ('data', OrderedDict([('struct1', 'UserDefOne'), ('string', 'str'), ('*enum2', 'EnumOne')]))])] diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c new file mode 100644 index 0000000000..cb1e4418ec --- /dev/null +++ b/tests/test-qmp-event.c @@ -0,0 +1,265 @@ +/* + * qapi event unit-tests. + * + * Copyright (c) 2014 Wenchao Xia + * + * Authors: + * Wenchao Xia <wenchaoqemu@gmail.com> + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include <glib.h> +#include <stdarg.h> + +#include "qemu-common.h" +#include "test-qapi-types.h" +#include "test-qapi-visit.h" +#include "test-qapi-event.h" +#include "qapi/qmp/types.h" +#include "qapi/qmp/qint.h" +#include "qapi/qmp/qobject.h" +#include "qapi/qmp-event.h" + +typedef struct TestEventData { + QDict *expect; +} TestEventData; + +typedef struct QDictCmpData { + QDict *expect; + bool result; +} QDictCmpData; + +TestEventData *test_event_data; +static CompatGMutex test_event_lock; + +/* Only compares bool, int, string */ +static +void qdict_cmp_do_simple(const char *key, QObject *obj1, void *opaque) + +{ + QObject *obj2; + QDictCmpData d_new, *d = opaque; + + if (!d->result) { + return; + } + + obj2 = qdict_get(d->expect, key); + if (!obj2) { + d->result = false; + return; + } + + if (qobject_type(obj1) != qobject_type(obj2)) { + d->result = false; + return; + } + + switch (qobject_type(obj1)) { + case QTYPE_QBOOL: + d->result = (qbool_get_int(qobject_to_qbool(obj1)) == + qbool_get_int(qobject_to_qbool(obj2))); + return; + case QTYPE_QINT: + d->result = (qint_get_int(qobject_to_qint(obj1)) == + qint_get_int(qobject_to_qint(obj2))); + return; + case QTYPE_QSTRING: + d->result = g_strcmp0(qstring_get_str(qobject_to_qstring(obj1)), + qstring_get_str(qobject_to_qstring(obj2))) == 0; + return; + case QTYPE_QDICT: + d_new.expect = qobject_to_qdict(obj2); + d_new.result = true; + qdict_iter(qobject_to_qdict(obj1), qdict_cmp_do_simple, &d_new); + d->result = d_new.result; + return; + default: + abort(); + } +} + +static bool qdict_cmp_simple(QDict *a, QDict *b) +{ + QDictCmpData d; + + d.expect = b; + d.result = true; + qdict_iter(a, qdict_cmp_do_simple, &d); + return d.result; +} + +/* This function is hooked as final emit function, which can verify the + correctness. */ +static void event_test_emit(TEST_QAPIEvent event, QDict *d, Error **errp) +{ + QObject *obj; + QDict *t; + int64_t s, ms; + + /* Verify that we have timestamp, then remove it to compare other fields */ + obj = qdict_get(d, "timestamp"); + g_assert(obj); + t = qobject_to_qdict(obj); + g_assert(t); + obj = qdict_get(t, "seconds"); + g_assert(obj && qobject_type(obj) == QTYPE_QINT); + s = qint_get_int(qobject_to_qint(obj)); + obj = qdict_get(t, "microseconds"); + g_assert(obj && qobject_type(obj) == QTYPE_QINT); + ms = qint_get_int(qobject_to_qint(obj)); + if (s == -1) { + g_assert(ms == -1); + } else { + g_assert(ms >= 0 && ms <= 999999); + } + g_assert(qdict_size(t) == 2); + + qdict_del(d, "timestamp"); + + g_assert(qdict_cmp_simple(d, test_event_data->expect)); + +} + +static void event_prepare(TestEventData *data, + const void *unused) +{ + /* Global variable test_event_data was used to pass the expectation, so + test cases can't be executed at same time. */ + g_mutex_lock(&test_event_lock); + + data->expect = qdict_new(); + test_event_data = data; +} + +static void event_teardown(TestEventData *data, + const void *unused) +{ + QDECREF(data->expect); + test_event_data = NULL; + + g_mutex_unlock(&test_event_lock); +} + +static void event_test_add(const char *testpath, + void (*test_func)(TestEventData *data, + const void *user_data)) +{ + g_test_add(testpath, TestEventData, NULL, event_prepare, test_func, + event_teardown); +} + + +/* Test cases */ + +static void test_event_a(TestEventData *data, + const void *unused) +{ + QDict *d; + d = data->expect; + qdict_put(d, "event", qstring_from_str("EVENT_A")); + qapi_event_send_event_a(&error_abort); +} + +static void test_event_b(TestEventData *data, + const void *unused) +{ + QDict *d; + d = data->expect; + qdict_put(d, "event", qstring_from_str("EVENT_B")); + qapi_event_send_event_b(&error_abort); +} + +static void test_event_c(TestEventData *data, + const void *unused) +{ + QDict *d, *d_data, *d_b; + + UserDefOne b; + UserDefZero z; + z.integer = 2; + b.base = &z; + b.string = g_strdup("test1"); + b.has_enum1 = false; + + d_b = qdict_new(); + qdict_put(d_b, "integer", qint_from_int(2)); + qdict_put(d_b, "string", qstring_from_str("test1")); + + d_data = qdict_new(); + qdict_put(d_data, "a", qint_from_int(1)); + qdict_put(d_data, "b", d_b); + qdict_put(d_data, "c", qstring_from_str("test2")); + + d = data->expect; + qdict_put(d, "event", qstring_from_str("EVENT_C")); + qdict_put(d, "data", d_data); + + qapi_event_send_event_c(true, 1, true, &b, "test2", &error_abort); + + g_free(b.string); +} + +/* Complex type */ +static void test_event_d(TestEventData *data, + const void *unused) +{ + UserDefOne struct1; + EventStructOne a; + UserDefZero z; + QDict *d, *d_data, *d_a, *d_struct1; + + z.integer = 2; + struct1.base = &z; + struct1.string = g_strdup("test1"); + struct1.has_enum1 = true; + struct1.enum1 = ENUM_ONE_VALUE1; + + a.struct1 = &struct1; + a.string = g_strdup("test2"); + a.has_enum2 = true; + a.enum2 = ENUM_ONE_VALUE2; + + d_struct1 = qdict_new(); + qdict_put(d_struct1, "integer", qint_from_int(2)); + qdict_put(d_struct1, "string", qstring_from_str("test1")); + qdict_put(d_struct1, "enum1", qstring_from_str("value1")); + + d_a = qdict_new(); + qdict_put(d_a, "struct1", d_struct1); + qdict_put(d_a, "string", qstring_from_str("test2")); + qdict_put(d_a, "enum2", qstring_from_str("value2")); + + d_data = qdict_new(); + qdict_put(d_data, "a", d_a); + qdict_put(d_data, "b", qstring_from_str("test3")); + qdict_put(d_data, "enum3", qstring_from_str("value3")); + + d = data->expect; + qdict_put(d, "event", qstring_from_str("EVENT_D")); + qdict_put(d, "data", d_data); + + qapi_event_send_event_d(&a, "test3", false, NULL, true, ENUM_ONE_VALUE3, + &error_abort); + + g_free(struct1.string); + g_free(a.string); +} + +int main(int argc, char **argv) +{ + qmp_event_set_func_emit(event_test_emit); + + g_test_init(&argc, &argv, NULL); + + event_test_add("/event/event_a", test_event_a); + event_test_add("/event/event_b", test_event_b); + event_test_add("/event/event_c", test_event_c); + event_test_add("/event/event_d", test_event_d); + g_test_run(); + + return 0; +} diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index 7c826b49e5..2af2381a1d 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -8,17 +8,30 @@ * */ +#define QEMU_GLIB_COMPAT_H +#include <glib.h> + #include "libqtest.h" #include "qemu/option.h" #include "sysemu/char.h" #include "sysemu/sysemu.h" -#include <glib.h> #include <linux/vhost.h> #include <sys/mman.h> #include <sys/vfs.h> #include <qemu/sockets.h> +/* GLIB version compatibility flags */ +#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 +#define HAVE_THREAD_NEW +#endif + #define QEMU_CMD_ACCEL " -machine accel=tcg" #define QEMU_CMD_MEM " -m 512 -object memory-backend-file,id=mem,size=512M,"\ "mem-path=%s,share=on -numa node,memdev=mem" @@ -95,8 +108,93 @@ static VhostUserMsg m __attribute__ ((unused)); int fds_num = 0, fds[VHOST_MEMORY_MAX_NREGIONS]; static VhostUserMemory memory; -static GMutex data_mutex; -static GCond data_cond; +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; + +#ifdef HAVE_MUTEX_INIT + mutex = g_new(GMutex, 1); + g_mutex_init(mutex); +#else + mutex = g_mutex_new(); +#endif + + return mutex; +} + +static void _mutex_free(GMutex *mutex) +{ +#ifdef HAVE_MUTEX_INIT + g_mutex_clear(mutex); + g_free(mutex); +#else + g_mutex_free(mutex); +#endif +} + +static GCond *_cond_new(void) +{ + GCond *cond; + +#ifdef HAVE_COND_INIT + cond = g_new(GCond, 1); + g_cond_init(cond); +#else + cond = g_cond_new(); +#endif + + return cond; +} + +static gboolean _cond_wait_until(GCond *cond, GMutex *mutex, gint64 end_time) +{ + gboolean ret = FALSE; +#ifdef HAVE_COND_INIT + ret = g_cond_wait_until(cond, mutex, end_time); +#else + GTimeVal time = { end_time / G_TIME_SPAN_SECOND, + end_time % G_TIME_SPAN_SECOND }; + ret = g_cond_timed_wait(cond, mutex, &time); +#endif + return ret; +} + +static void _cond_free(GCond *cond) +{ +#ifdef HAVE_COND_INIT + g_cond_clear(cond); + g_free(cond); +#else + g_cond_free(cond); +#endif +} + +static GThread *_thread_new(const gchar *name, GThreadFunc func, gpointer data) +{ + GThread *thread = NULL; + GError *error = NULL; +#ifdef HAVE_THREAD_NEW + thread = g_thread_try_new(name, func, data, &error); +#else + thread = g_thread_create(func, data, TRUE, &error); +#endif + return thread; +} static void read_guest_mem(void) { @@ -104,11 +202,11 @@ static void read_guest_mem(void) gint64 end_time; int i, j; - g_mutex_lock(&data_mutex); + 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 (!g_cond_wait_until(&data_cond, &data_mutex, end_time)) { + if (!_cond_wait_until(data_cond, data_mutex, end_time)) { /* timeout has passed */ g_assert(fds_num); break; @@ -143,7 +241,7 @@ static void read_guest_mem(void) } g_assert_cmpint(1, ==, 1); - g_mutex_unlock(&data_mutex); + g_mutex_unlock(data_mutex); } static void *thread_function(void *data) @@ -171,6 +269,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) return; } + g_mutex_lock(data_mutex); memcpy(p, buf, VHOST_USER_HDR_SIZE); if (msg.size) { @@ -203,8 +302,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) fds_num = qemu_chr_fe_get_msgfds(chr, fds, sizeof(fds) / sizeof(int)); /* signal the test that it can continue */ - g_cond_signal(&data_cond); - g_mutex_unlock(&data_mutex); + g_cond_signal(data_cond); break; case VHOST_USER_SET_VRING_KICK: @@ -221,6 +319,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) default: break; } + g_mutex_unlock(data_mutex); } static const char *init_hugepagefs(void) @@ -285,10 +384,9 @@ int main(int argc, char **argv) qemu_chr_add_handlers(chr, chr_can_read, chr_read, NULL, chr); /* run the main loop thread so the chardev may operate */ - g_mutex_init(&data_mutex); - g_cond_init(&data_cond); - g_mutex_lock(&data_mutex); - g_thread_new(NULL, thread_function, NULL); + data_mutex = _mutex_new(); + data_cond = _cond_new(); + _thread_new(NULL, thread_function, NULL); qemu_cmd = g_strdup_printf(QEMU_CMD, hugefs, socket_path); s = qtest_start(qemu_cmd); @@ -305,8 +403,8 @@ int main(int argc, char **argv) /* cleanup */ unlink(socket_path); g_free(socket_path); - g_cond_clear(&data_cond); - g_mutex_clear(&data_mutex); + _cond_free(data_cond); + _mutex_free(data_mutex); return ret; } diff --git a/ui/console.c b/ui/console.c index 7dc4c1414c..ab8454903c 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1821,7 +1821,7 @@ static CharDriverState *text_console_init(ChardevVC *vc) unsigned width = 0; unsigned height = 0; - chr = g_malloc0(sizeof(CharDriverState)); + chr = qemu_chr_alloc(); if (vc->has_width) { width = vc->width; diff --git a/ui/spice-core.c b/ui/spice-core.c index d10818a925..70df4460e4 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -35,9 +35,9 @@ #include "qapi/qmp/qjson.h" #include "qemu/notify.h" #include "migration/migration.h" -#include "monitor/monitor.h" #include "hw/hw.h" #include "ui/spice-display.h" +#include "qapi-event.h" /* core bits */ @@ -174,39 +174,34 @@ static void channel_list_del(SpiceChannelEventInfo *info) } } -static void add_addr_info(QDict *dict, struct sockaddr *addr, int len) +static void add_addr_info(SpiceBasicInfo *info, struct sockaddr *addr, int len) { char host[NI_MAXHOST], port[NI_MAXSERV]; - const char *family; getnameinfo(addr, len, host, sizeof(host), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); - family = inet_strfamily(addr->sa_family); - qdict_put(dict, "host", qstring_from_str(host)); - qdict_put(dict, "port", qstring_from_str(port)); - qdict_put(dict, "family", qstring_from_str(family)); + info->host = g_strdup(host); + info->port = g_strdup(port); + info->family = inet_netfamily(addr->sa_family); } -static void add_channel_info(QDict *dict, SpiceChannelEventInfo *info) +static void add_channel_info(SpiceChannel *sc, SpiceChannelEventInfo *info) { int tls = info->flags & SPICE_CHANNEL_EVENT_FLAG_TLS; - qdict_put(dict, "connection-id", qint_from_int(info->connection_id)); - qdict_put(dict, "channel-type", qint_from_int(info->type)); - qdict_put(dict, "channel-id", qint_from_int(info->id)); - qdict_put(dict, "tls", qbool_from_int(tls)); + sc->connection_id = info->connection_id; + sc->channel_type = info->type; + sc->channel_id = info->id; + sc->tls = !!tls; } static void channel_event(int event, SpiceChannelEventInfo *info) { - static const int qevent[] = { - [ SPICE_CHANNEL_EVENT_CONNECTED ] = QEVENT_SPICE_CONNECTED, - [ SPICE_CHANNEL_EVENT_INITIALIZED ] = QEVENT_SPICE_INITIALIZED, - [ SPICE_CHANNEL_EVENT_DISCONNECTED ] = QEVENT_SPICE_DISCONNECTED, - }; - QDict *server, *client; - QObject *data; + SpiceServerInfo *server = g_malloc0(sizeof(*server)); + SpiceChannel *client = g_malloc0(sizeof(*client)); + server->base = g_malloc0(sizeof(*server->base)); + client->base = g_malloc0(sizeof(*client->base)); /* * Spice server might have called us from spice worker thread @@ -222,36 +217,43 @@ static void channel_event(int event, SpiceChannelEventInfo *info) qemu_mutex_lock_iothread(); } - client = qdict_new(); - server = qdict_new(); - if (info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) { - add_addr_info(client, (struct sockaddr *)&info->paddr_ext, + add_addr_info(client->base, (struct sockaddr *)&info->paddr_ext, info->plen_ext); - add_addr_info(server, (struct sockaddr *)&info->laddr_ext, + add_addr_info(server->base, (struct sockaddr *)&info->laddr_ext, info->llen_ext); } else { error_report("spice: %s, extended address is expected", __func__); } - if (event == SPICE_CHANNEL_EVENT_INITIALIZED) { - qdict_put(server, "auth", qstring_from_str(auth)); + switch (event) { + case SPICE_CHANNEL_EVENT_CONNECTED: + qapi_event_send_spice_connected(server->base, client->base, &error_abort); + break; + case SPICE_CHANNEL_EVENT_INITIALIZED: + if (auth) { + server->has_auth = true; + server->auth = g_strdup(auth); + } add_channel_info(client, info); channel_list_add(info); - } - if (event == SPICE_CHANNEL_EVENT_DISCONNECTED) { + qapi_event_send_spice_initialized(server, client, &error_abort); + break; + case SPICE_CHANNEL_EVENT_DISCONNECTED: channel_list_del(info); + qapi_event_send_spice_disconnected(server->base, client->base, &error_abort); + break; + default: + break; } - data = qobject_from_jsonf("{ 'client': %p, 'server': %p }", - QOBJECT(client), QOBJECT(server)); - monitor_protocol_event(qevent[event], data); - qobject_decref(data); - if (need_lock) { qemu_mutex_unlock_iothread(); } + + qapi_free_SpiceServerInfo(server); + qapi_free_SpiceChannel(client); } static SpiceCoreInterface core_interface = { @@ -305,7 +307,7 @@ static void migrate_connect_complete_cb(SpiceMigrateInstance *sin) static void migrate_end_complete_cb(SpiceMigrateInstance *sin) { - monitor_protocol_event(QEVENT_SPICE_MIGRATE_COMPLETED, NULL); + qapi_event_send_spice_migrate_completed(&error_abort); spice_migration_completed = true; } @@ -391,15 +393,16 @@ static SpiceChannelList *qmp_query_spice_channels(void) chan = g_malloc0(sizeof(*chan)); chan->value = g_malloc0(sizeof(*chan->value)); + chan->value->base = g_malloc0(sizeof(*chan->value->base)); paddr = (struct sockaddr *)&item->info->paddr_ext; plen = item->info->plen_ext; getnameinfo(paddr, plen, host, sizeof(host), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); - chan->value->host = g_strdup(host); - chan->value->port = g_strdup(port); - chan->value->family = g_strdup(inet_strfamily(paddr->sa_family)); + chan->value->base->host = g_strdup(host); + chan->value->base->port = g_strdup(port); + chan->value->base->family = inet_netfamily(paddr->sa_family); chan->value->connection_id = item->info->connection_id; chan->value->channel_type = item->info->type; diff --git a/ui/vnc.c b/ui/vnc.c index aac93f0e17..14a86c36ce 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -35,6 +35,7 @@ #include "qmp-commands.h" #include "qemu/osdep.h" #include "ui/input.h" +#include "qapi-event.h" #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT #define VNC_REFRESH_INTERVAL_INC 50 @@ -124,9 +125,10 @@ char *vnc_socket_remote_addr(const char *format, int fd) { return addr_to_string(format, &sa, salen); } -static int put_addr_qdict(QDict *qdict, struct sockaddr_storage *sa, - socklen_t salen) +static VncBasicInfo *vnc_basic_info_get(struct sockaddr_storage *sa, + socklen_t salen) { + VncBasicInfo *info; char host[NI_MAXHOST]; char serv[NI_MAXSERV]; int err; @@ -137,40 +139,40 @@ static int put_addr_qdict(QDict *qdict, struct sockaddr_storage *sa, NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { VNC_DEBUG("Cannot resolve address %d: %s\n", err, gai_strerror(err)); - return -1; + return NULL; } - qdict_put(qdict, "host", qstring_from_str(host)); - qdict_put(qdict, "service", qstring_from_str(serv)); - qdict_put(qdict, "family",qstring_from_str(inet_strfamily(sa->ss_family))); - - return 0; + info = g_malloc0(sizeof(VncBasicInfo)); + info->host = g_strdup(host); + info->service = g_strdup(serv); + info->family = inet_netfamily(sa->ss_family); + return info; } -static int vnc_server_addr_put(QDict *qdict, int fd) +static VncBasicInfo *vnc_basic_info_get_from_server_addr(int fd) { struct sockaddr_storage sa; socklen_t salen; salen = sizeof(sa); if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0) { - return -1; + return NULL; } - return put_addr_qdict(qdict, &sa, salen); + return vnc_basic_info_get(&sa, salen); } -static int vnc_qdict_remote_addr(QDict *qdict, int fd) +static VncBasicInfo *vnc_basic_info_get_from_remote_addr(int fd) { struct sockaddr_storage sa; socklen_t salen; salen = sizeof(sa); if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0) { - return -1; + return NULL; } - return put_addr_qdict(qdict, &sa, salen); + return vnc_basic_info_get(&sa, salen); } static const char *vnc_auth_name(VncDisplay *vd) { @@ -224,81 +226,82 @@ static const char *vnc_auth_name(VncDisplay *vd) { return "unknown"; } -static int vnc_server_info_put(QDict *qdict) +static VncServerInfo *vnc_server_info_get(void) { - if (vnc_server_addr_put(qdict, vnc_display->lsock) < 0) { - return -1; + VncServerInfo *info; + VncBasicInfo *bi = vnc_basic_info_get_from_server_addr(vnc_display->lsock); + if (!bi) { + return NULL; } - qdict_put(qdict, "auth", qstring_from_str(vnc_auth_name(vnc_display))); - return 0; + info = g_malloc(sizeof(*info)); + info->base = bi; + info->has_auth = true; + info->auth = g_strdup(vnc_auth_name(vnc_display)); + return info; } static void vnc_client_cache_auth(VncState *client) { -#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) - QDict *qdict; -#endif - if (!client->info) { return; } -#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) - qdict = qobject_to_qdict(client->info); -#endif - #ifdef CONFIG_VNC_TLS if (client->tls.session && client->tls.dname) { - qdict_put(qdict, "x509_dname", qstring_from_str(client->tls.dname)); + client->info->has_x509_dname = true; + client->info->x509_dname = g_strdup(client->tls.dname); } #endif #ifdef CONFIG_VNC_SASL if (client->sasl.conn && client->sasl.username) { - qdict_put(qdict, "sasl_username", - qstring_from_str(client->sasl.username)); + client->info->has_sasl_username = true; + client->info->sasl_username = g_strdup(client->sasl.username); } #endif } static void vnc_client_cache_addr(VncState *client) { - QDict *qdict; + VncBasicInfo *bi = vnc_basic_info_get_from_remote_addr(client->csock); - qdict = qdict_new(); - if (vnc_qdict_remote_addr(qdict, client->csock) < 0) { - QDECREF(qdict); - /* XXX: how to report the error? */ - return; + if (bi) { + client->info = g_malloc0(sizeof(*client->info)); + client->info->base = bi; } - - client->info = QOBJECT(qdict); } -static void vnc_qmp_event(VncState *vs, MonitorEvent event) +static void vnc_qmp_event(VncState *vs, QAPIEvent event) { - QDict *server; - QObject *data; + VncServerInfo *si; if (!vs->info) { return; } + g_assert(vs->info->base); - server = qdict_new(); - if (vnc_server_info_put(server) < 0) { - QDECREF(server); + si = vnc_server_info_get(); + if (!si) { return; } - data = qobject_from_jsonf("{ 'client': %p, 'server': %p }", - vs->info, QOBJECT(server)); - - monitor_protocol_event(event, data); + switch (event) { + case QAPI_EVENT_VNC_CONNECTED: + qapi_event_send_vnc_connected(si, vs->info->base, &error_abort); + break; + case QAPI_EVENT_VNC_INITIALIZED: + qapi_event_send_vnc_initialized(si, vs->info, &error_abort); + break; + case QAPI_EVENT_VNC_DISCONNECTED: + qapi_event_send_vnc_disconnected(si, vs->info, &error_abort); + break; + default: + break; + } - qobject_incref(vs->info); - qobject_decref(data); + qapi_free_VncServerInfo(si); } static VncClientInfo *qmp_query_vnc_client(const VncState *client) @@ -321,9 +324,10 @@ static VncClientInfo *qmp_query_vnc_client(const VncState *client) } info = g_malloc0(sizeof(*info)); - info->host = g_strdup(host); - info->service = g_strdup(serv); - info->family = g_strdup(inet_strfamily(sa.ss_family)); + info->base = g_malloc0(sizeof(*info->base)); + info->base->host = g_strdup(host); + info->base->service = g_strdup(serv); + info->base->family = inet_netfamily(sa.ss_family); #ifdef CONFIG_VNC_TLS if (client->tls.session && client->tls.dname) { @@ -398,7 +402,7 @@ VncInfo *qmp_query_vnc(Error **errp) info->service = g_strdup(serv); info->has_family = true; - info->family = g_strdup(inet_strfamily(sa.ss_family)); + info->family = inet_netfamily(sa.ss_family); info->has_auth = true; info->auth = g_strdup(vnc_auth_name(vnc_display)); @@ -1039,7 +1043,7 @@ void vnc_disconnect_finish(VncState *vs) vnc_jobs_join(vs); /* Wait encoding jobs */ vnc_lock_output(vs); - vnc_qmp_event(vs, QEVENT_VNC_DISCONNECTED); + vnc_qmp_event(vs, QAPI_EVENT_VNC_DISCONNECTED); buffer_free(&vs->input); buffer_free(&vs->output); @@ -1048,7 +1052,7 @@ void vnc_disconnect_finish(VncState *vs) buffer_free(&vs->ws_output); #endif /* CONFIG_VNC_WS */ - qobject_decref(vs->info); + qapi_free_VncClientInfo(vs->info); vnc_zlib_clear(vs); vnc_tight_clear(vs); @@ -2323,7 +2327,7 @@ static int protocol_client_init(VncState *vs, uint8_t *data, size_t len) vnc_flush(vs); vnc_client_cache_auth(vs); - vnc_qmp_event(vs, QEVENT_VNC_INITIALIZED); + vnc_qmp_event(vs, QAPI_EVENT_VNC_INITIALIZED); vnc_read_when(vs, protocol_client_msg, 1); @@ -2846,7 +2850,7 @@ static void vnc_connect(VncDisplay *vd, int csock, } vnc_client_cache_addr(vs); - vnc_qmp_event(vs, QEVENT_VNC_CONNECTED); + vnc_qmp_event(vs, QAPI_EVENT_VNC_CONNECTED); vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING); vs->vd = vd; diff --git a/ui/vnc.h b/ui/vnc.h index 8da81b8d6e..07af9f73de 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -31,7 +31,6 @@ #include "qemu/queue.h" #include "qemu/thread.h" #include "ui/console.h" -#include "monitor/monitor.h" #include "audio/audio.h" #include "qemu/bitmap.h" #include <zlib.h> @@ -40,6 +39,7 @@ #include "keymaps.h" #include "vnc-palette.h" #include "vnc-enc-zrle.h" +#include "qapi-types.h" // #define _VNC_DEBUG 1 @@ -292,7 +292,7 @@ struct VncState bool websocket; #endif /* CONFIG_VNC_WS */ - QObject *info; + VncClientInfo *info; Buffer output; Buffer input; diff --git a/util/Makefile.objs b/util/Makefile.objs index df83b629a0..6b3c83b0eb 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -1,7 +1,7 @@ util-obj-y = osdep.o cutils.o unicode.o qemu-timer-common.o util-obj-$(CONFIG_WIN32) += oslib-win32.o qemu-thread-win32.o event_notifier-win32.o util-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o event_notifier-posix.o qemu-openpty.o -util-obj-y += envlist.o path.o host-utils.o cache-utils.o module.o +util-obj-y += envlist.o path.o host-utils.o module.o util-obj-y += bitmap.o bitops.o hbitmap.o util-obj-y += fifo8.o util-obj-y += acl.o diff --git a/util/cache-utils.c b/util/cache-utils.c deleted file mode 100644 index 047003008b..0000000000 --- a/util/cache-utils.c +++ /dev/null @@ -1,86 +0,0 @@ -#include "qemu-common.h" -#include "qemu/cache-utils.h" - -#if defined(_ARCH_PPC) -struct qemu_cache_conf qemu_cache_conf = { - .dcache_bsize = 16, - .icache_bsize = 16 -}; - -#if defined _AIX -#include <sys/systemcfg.h> - -void qemu_cache_utils_init(void) -{ - qemu_cache_conf.icache_bsize = _system_configuration.icache_line; - qemu_cache_conf.dcache_bsize = _system_configuration.dcache_line; -} - -#elif defined __linux__ -#include "qemu/osdep.h" -#include "elf.h" - -void qemu_cache_utils_init(void) -{ - unsigned long dsize = qemu_getauxval(AT_DCACHEBSIZE); - unsigned long isize = qemu_getauxval(AT_ICACHEBSIZE); - - if (dsize == 0 || isize == 0) { - if (dsize == 0) { - fprintf(stderr, "getauxval AT_DCACHEBSIZE failed\n"); - } - if (isize == 0) { - fprintf(stderr, "getauxval AT_ICACHEBSIZE failed\n"); - } - exit(1); - - } - qemu_cache_conf.dcache_bsize = dsize; - qemu_cache_conf.icache_bsize = isize; -} - -#elif defined __APPLE__ -#include <stdio.h> -#include <sys/types.h> -#include <sys/sysctl.h> - -void qemu_cache_utils_init(void) -{ - size_t len; - unsigned cacheline; - int name[2] = { CTL_HW, HW_CACHELINE }; - - len = sizeof(cacheline); - if (sysctl(name, 2, &cacheline, &len, NULL, 0)) { - perror("sysctl CTL_HW HW_CACHELINE failed"); - } else { - qemu_cache_conf.dcache_bsize = cacheline; - qemu_cache_conf.icache_bsize = cacheline; - } -} - -#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/sysctl.h> - -void qemu_cache_utils_init(void) -{ - size_t len = 4; - unsigned cacheline; - - if (sysctlbyname ("machdep.cacheline_size", &cacheline, &len, NULL, 0)) { - fprintf(stderr, "sysctlbyname machdep.cacheline_size failed: %s\n", - strerror(errno)); - exit(1); - } - - qemu_cache_conf.dcache_bsize = cacheline; - qemu_cache_conf.icache_bsize = cacheline; -} -#endif - -#endif /* _ARCH_PPC */ diff --git a/util/getauxval.c b/util/getauxval.c index 476c883b32..25f48e5456 100644 --- a/util/getauxval.c +++ b/util/getauxval.c @@ -48,24 +48,51 @@ typedef struct { static const ElfW_auxv_t *auxv; -void qemu_init_auxval(char **envp) +static const ElfW_auxv_t *qemu_init_auxval(void) { - /* The auxiliary vector is located just beyond the initial environment. */ - while (*envp++ != NULL) { - continue; + ElfW_auxv_t *a; + ssize_t size = 512, r, ofs; + int fd; + + /* Allocate some initial storage. Make sure the first entry is set + to end-of-list, so that we've got a valid list in case of error. */ + auxv = a = g_malloc(size); + a[0].a_type = 0; + a[0].a_val = 0; + + fd = open("/proc/self/auxv", O_RDONLY); + if (fd < 0) { + return a; + } + + /* Read the first SIZE bytes. Hopefully, this covers everything. */ + r = read(fd, a, size); + + if (r == size) { + /* Continue to expand until we do get a partial read. */ + do { + ofs = size; + size *= 2; + auxv = a = g_realloc(a, size); + r = read(fd, (char *)a + ofs, ofs); + } while (r == ofs); } - auxv = (const ElfW_auxv_t *)envp; + + close(fd); + return a; } unsigned long qemu_getauxval(unsigned long type) { - /* If we were able to find the auxiliary vector, use it. */ - if (auxv) { - const ElfW_auxv_t *a; - for (a = auxv; a->a_type != 0; a++) { - if (a->a_type == type) { - return a->a_val; - } + const ElfW_auxv_t *a = auxv; + + if (unlikely(a == NULL)) { + a = qemu_init_auxval(); + } + + for (; a->a_type != 0; a++) { + if (a->a_type == type) { + return a->a_val; } } diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index a4a1e9d300..74cf0786e5 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -92,14 +92,14 @@ static void inet_setport(struct addrinfo *e, int port) } } -const char *inet_strfamily(int family) +NetworkAddressFamily inet_netfamily(int family) { switch (family) { - case PF_INET6: return "ipv6"; - case PF_INET: return "ipv4"; - case PF_UNIX: return "unix"; + case PF_INET6: return NETWORK_ADDRESS_FAMILY_IPV6; + case PF_INET: return NETWORK_ADDRESS_FAMILY_IPV4; + case PF_UNIX: return NETWORK_ADDRESS_FAMILY_UNIX; } - return "unknown"; + return NETWORK_ADDRESS_FAMILY_UNKNOWN; } int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp) diff --git a/vl.c b/vl.c index 32887559be..a1686ef6c4 100644 --- a/vl.c +++ b/vl.c @@ -82,7 +82,6 @@ int main(int argc, char **argv) #include "qemu/timer.h" #include "sysemu/char.h" #include "qemu/bitmap.h" -#include "qemu/cache-utils.h" #include "sysemu/blockdev.h" #include "hw/block/block.h" #include "migration/block.h" @@ -117,6 +116,8 @@ int main(int argc, char **argv) #include "ui/qemu-spice.h" #include "qapi/string-input-visitor.h" #include "qapi/opts-visitor.h" +#include "qom/object_interfaces.h" +#include "qapi-event.h" #define DEFAULT_RAM_SIZE 128 @@ -381,6 +382,10 @@ static QemuOptsList qemu_machine_opts = { .name = "kvm-type", .type = QEMU_OPT_STRING, .help = "Specifies the KVM virtualization mode (HV, PR)", + },{ + .name = PC_MACHINE_MAX_RAM_BELOW_4G, + .type = QEMU_OPT_SIZE, + .help = "maximum ram below the 4G boundary (32bit boundary)", }, { /* End of list */ } }, @@ -734,7 +739,7 @@ void vm_start(void) * the STOP event. */ if (runstate_is_running()) { - monitor_protocol_event(QEVENT_STOP, NULL); + qapi_event_send_stop(&error_abort); } else { cpu_enable_ticks(); runstate_set(RUN_STATE_RUNNING); @@ -742,7 +747,7 @@ void vm_start(void) resume_all_vcpus(); } - monitor_protocol_event(QEVENT_RESUME, NULL); + qapi_event_send_resume(&error_abort); } @@ -786,15 +791,6 @@ int qemu_timedate_diff(struct tm *tm) return seconds - time(NULL); } -void rtc_change_mon_event(struct tm *tm) -{ - QObject *data; - - data = qobject_from_jsonf("{ 'offset': %d }", qemu_timedate_diff(tm)); - monitor_protocol_event(QEVENT_RTC_CHANGE, data); - qobject_decref(data); -} - static void configure_rtc_date_offset(const char *startdate, int legacy) { time_t rtc_start_date; @@ -1846,7 +1842,7 @@ void qemu_system_reset(bool report) qemu_devices_reset(); } if (report) { - monitor_protocol_event(QEVENT_RESET, NULL); + qapi_event_send_reset(&error_abort); } cpu_synchronize_all_post_reset(); } @@ -1867,7 +1863,7 @@ static void qemu_system_suspend(void) pause_all_vcpus(); notifier_list_notify(&suspend_notifiers, NULL); runstate_set(RUN_STATE_SUSPENDED); - monitor_protocol_event(QEVENT_SUSPEND, NULL); + qapi_event_send_suspend(&error_abort); } void qemu_system_suspend_request(void) @@ -1930,7 +1926,7 @@ void qemu_system_shutdown_request(void) static void qemu_system_powerdown(void) { - monitor_protocol_event(QEVENT_POWERDOWN, NULL); + qapi_event_send_powerdown(&error_abort); notifier_list_notify(&powerdown_notifiers, NULL); } @@ -1962,7 +1958,7 @@ static bool main_loop_should_exit(void) } if (qemu_shutdown_requested()) { qemu_kill_report(); - monitor_protocol_event(QEVENT_SHUTDOWN, NULL); + qapi_event_send_shutdown(&error_abort); if (no_shutdown) { vm_stop(RUN_STATE_SHUTDOWN); } else { @@ -1985,7 +1981,7 @@ static bool main_loop_should_exit(void) notifier_list_notify(&wakeup_notifiers, &wakeup_reason); wakeup_reason = QEMU_WAKEUP_REASON_NONE; resume_all_vcpus(); - monitor_protocol_event(QEVENT_WAKEUP, NULL); + qapi_event_send_wakeup(&error_abort); } if (qemu_powerdown_requested()) { qemu_system_powerdown(); @@ -2975,9 +2971,6 @@ int main(int argc, char **argv, char **envp) rtc_clock = QEMU_CLOCK_HOST; - qemu_init_auxval(envp); - qemu_cache_utils_init(); - QLIST_INIT (&vm_change_state_head); os_setup_early_signal_handling(); diff --git a/xen-hvm-stub.c b/xen-hvm-stub.c index 4eb27b5f2b..2d98696e72 100644 --- a/xen-hvm-stub.c +++ b/xen-hvm-stub.c @@ -51,7 +51,8 @@ void xen_modified_memory(ram_addr_t start, ram_addr_t length) { } -int xen_hvm_init(MemoryRegion **ram_memory) +int xen_hvm_init(ram_addr_t *below_4g_mem_size, ram_addr_t *above_4g_mem_size, + MemoryRegion **ram_memory) { return 0; } diff --git a/xen-hvm.c b/xen-hvm.c index aac38efb09..bafdf1283f 100644 --- a/xen-hvm.c +++ b/xen-hvm.c @@ -155,30 +155,43 @@ qemu_irq *xen_interrupt_controller_init(void) /* Memory Ops */ -static void xen_ram_init(ram_addr_t ram_size, MemoryRegion **ram_memory_p) +static void xen_ram_init(ram_addr_t *below_4g_mem_size, + ram_addr_t *above_4g_mem_size, + ram_addr_t ram_size, MemoryRegion **ram_memory_p) { MemoryRegion *sysmem = get_system_memory(); - ram_addr_t below_4g_mem_size, above_4g_mem_size = 0; ram_addr_t block_len; + uint64_t user_lowmem = object_property_get_int(qdev_get_machine(), + PC_MACHINE_MAX_RAM_BELOW_4G, + &error_abort); - block_len = ram_size; - if (ram_size >= HVM_BELOW_4G_RAM_END) { - /* Xen does not allocate the memory continuously, and keep a hole at - * HVM_BELOW_4G_MMIO_START of HVM_BELOW_4G_MMIO_LENGTH + /* Handle the machine opt max-ram-below-4g. It is basicly doing + * min(xen limit, user limit). + */ + if (HVM_BELOW_4G_RAM_END <= user_lowmem) { + user_lowmem = HVM_BELOW_4G_RAM_END; + } + + if (ram_size >= user_lowmem) { + *above_4g_mem_size = ram_size - user_lowmem; + *below_4g_mem_size = user_lowmem; + } else { + *above_4g_mem_size = 0; + *below_4g_mem_size = ram_size; + } + if (!*above_4g_mem_size) { + block_len = ram_size; + } else { + /* + * Xen does not allocate the memory continuously, it keeps a + * hole of the size computed above or passed in. */ - block_len += HVM_BELOW_4G_MMIO_LENGTH; + block_len = (1ULL << 32) + *above_4g_mem_size; } memory_region_init_ram(&ram_memory, NULL, "xen.ram", block_len); *ram_memory_p = &ram_memory; vmstate_register_ram_global(&ram_memory); - if (ram_size >= HVM_BELOW_4G_RAM_END) { - above_4g_mem_size = ram_size - HVM_BELOW_4G_RAM_END; - below_4g_mem_size = HVM_BELOW_4G_RAM_END; - } else { - below_4g_mem_size = ram_size; - } - memory_region_init_alias(&ram_640k, NULL, "xen.ram.640k", &ram_memory, 0, 0xa0000); memory_region_add_subregion(sysmem, 0, &ram_640k); @@ -189,12 +202,13 @@ static void xen_ram_init(ram_addr_t ram_size, MemoryRegion **ram_memory_p) * the Options ROM, so it is registered here as RAM. */ memory_region_init_alias(&ram_lo, NULL, "xen.ram.lo", - &ram_memory, 0xc0000, below_4g_mem_size - 0xc0000); + &ram_memory, 0xc0000, + *below_4g_mem_size - 0xc0000); memory_region_add_subregion(sysmem, 0xc0000, &ram_lo); - if (above_4g_mem_size > 0) { + if (*above_4g_mem_size > 0) { memory_region_init_alias(&ram_hi, NULL, "xen.ram.hi", &ram_memory, 0x100000000ULL, - above_4g_mem_size); + *above_4g_mem_size); memory_region_add_subregion(sysmem, 0x100000000ULL, &ram_hi); } } @@ -958,7 +972,8 @@ static void xen_wakeup_notifier(Notifier *notifier, void *data) xc_set_hvm_param(xen_xc, xen_domid, HVM_PARAM_ACPI_S_STATE, 0); } -int xen_hvm_init(MemoryRegion **ram_memory) +int xen_hvm_init(ram_addr_t *below_4g_mem_size, ram_addr_t *above_4g_mem_size, + MemoryRegion **ram_memory) { int i, rc; unsigned long ioreq_pfn; @@ -1036,7 +1051,7 @@ int xen_hvm_init(MemoryRegion **ram_memory) /* Init RAM management */ xen_map_cache_init(xen_phys_offset_to_gaddr, state); - xen_ram_init(ram_size, ram_memory); + xen_ram_init(below_4g_mem_size, above_4g_mem_size, ram_size, ram_memory); qemu_add_vm_change_state_handler(xen_hvm_change_state_handler, state); |