aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--accel/stubs/Makefile.objs1
-rw-r--r--accel/stubs/hvf-stub.c31
-rw-r--r--block.c75
-rw-r--r--block/commit.c8
-rw-r--r--block/io.c164
-rw-r--r--block/qcow2.c51
-rw-r--r--block/qcow2.h3
-rw-r--r--block/replication.c6
-rw-r--r--blockdev.c11
-rw-r--r--blockjob.c22
-rwxr-xr-xconfigure38
-rw-r--r--cpus.c86
-rw-r--r--hmp.c6
-rw-r--r--hw/block/nvme.c349
-rw-r--r--hw/block/trace-events93
-rw-r--r--hw/i386/acpi-build.c19
-rw-r--r--hw/intc/apic.c12
-rw-r--r--hw/net/e1000.c92
-rw-r--r--hw/net/e1000e.c4
-rw-r--r--hw/net/e1000e_core.c16
-rw-r--r--hw/net/e1000e_core.h2
-rw-r--r--hw/net/e1000x_common.h2
-rw-r--r--hw/net/eepro100.c32
-rw-r--r--hw/net/ftgmac100.c2
-rw-r--r--hw/net/lan9118.c3
-rw-r--r--hw/net/ne2000.c4
-rw-r--r--hw/net/opencores_eth.c3
-rw-r--r--hw/net/pcnet.c22
-rw-r--r--hw/net/rtl8139.c2
-rw-r--r--hw/net/sungem.c5
-rw-r--r--hw/net/sunhme.c25
-rw-r--r--hw/tpm/tpm_emulator.c17
-rw-r--r--hw/tpm/tpm_tis.c130
-rw-r--r--hw/tpm/tpm_util.c7
-rw-r--r--hw/tpm/tpm_util.h7
-rw-r--r--include/block/block.h15
-rw-r--r--include/block/block_int.h6
-rw-r--r--include/hw/acpi/acpi-defs.h7
-rw-r--r--include/hw/i386/apic.h1
-rw-r--r--include/net/net.h5
-rw-r--r--include/net/slirp.h2
-rw-r--r--include/qemu/typedefs.h1
-rw-r--r--include/qom/cpu.h2
-rw-r--r--include/sysemu/hvf.h107
-rw-r--r--net/net.c40
-rw-r--r--net/slirp.c34
-rw-r--r--qapi/block-core.json4
-rw-r--r--qemu-doc.texi67
-rw-r--r--qemu-io-cmds.c3
-rw-r--r--qemu-options.hx43
-rw-r--r--target/i386/Makefile.objs1
-rw-r--r--target/i386/cpu-qom.h4
-rw-r--r--target/i386/cpu.c80
-rw-r--r--target/i386/cpu.h101
-rw-r--r--target/i386/hvf/Makefile.objs2
-rw-r--r--target/i386/hvf/README.md7
-rw-r--r--target/i386/hvf/hvf-i386.h48
-rw-r--r--target/i386/hvf/hvf.c959
-rw-r--r--target/i386/hvf/panic.h45
-rw-r--r--target/i386/hvf/vmcs.h374
-rw-r--r--target/i386/hvf/vmx.h222
-rw-r--r--target/i386/hvf/x86.c186
-rw-r--r--target/i386/hvf/x86.h400
-rw-r--r--target/i386/hvf/x86_cpuid.c166
-rw-r--r--target/i386/hvf/x86_decode.c2186
-rw-r--r--target/i386/hvf/x86_decode.h323
-rw-r--r--target/i386/hvf/x86_descr.c125
-rw-r--r--target/i386/hvf/x86_descr.h58
-rw-r--r--target/i386/hvf/x86_emu.c1483
-rw-r--r--target/i386/hvf/x86_emu.h49
-rw-r--r--target/i386/hvf/x86_flags.c315
-rw-r--r--target/i386/hvf/x86_flags.h80
-rw-r--r--target/i386/hvf/x86_mmu.c272
-rw-r--r--target/i386/hvf/x86_mmu.h43
-rw-r--r--target/i386/hvf/x86_task.c191
-rw-r--r--target/i386/hvf/x86_task.h18
-rw-r--r--target/i386/hvf/x86hvf.c465
-rw-r--r--target/i386/hvf/x86hvf.h39
-rw-r--r--target/i386/kvm.c2
-rw-r--r--tests/Makefile.include2
-rwxr-xr-xtests/qemu-iotests/1974
-rw-r--r--tests/qemu-iotests/common.filter3
-rw-r--r--tests/test-bdrv-drain.c651
-rw-r--r--vl.c86
84 files changed, 10004 insertions, 673 deletions
diff --git a/accel/stubs/Makefile.objs b/accel/stubs/Makefile.objs
index c071abaf4e..779343b0c0 100644
--- a/accel/stubs/Makefile.objs
+++ b/accel/stubs/Makefile.objs
@@ -1,3 +1,4 @@
obj-$(call lnot,$(CONFIG_HAX)) += hax-stub.o
+obj-$(call lnot,$(CONFIG_HVF)) += hvf-stub.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
obj-$(call lnot,$(CONFIG_TCG)) += tcg-stub.o
diff --git a/accel/stubs/hvf-stub.c b/accel/stubs/hvf-stub.c
new file mode 100644
index 0000000000..a79f9fc36f
--- /dev/null
+++ b/accel/stubs/hvf-stub.c
@@ -0,0 +1,31 @@
+/*
+ * QEMU HVF support
+ *
+ * Copyright 2017 Red Hat, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2 or later, as published by the Free Software Foundation,
+ * and may be copied, distributed, and modified under those terms.
+ *
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "sysemu/hvf.h"
+
+int hvf_init_vcpu(CPUState *cpu)
+{
+ return -ENOSYS;
+}
+
+int hvf_vcpu_exec(CPUState *cpu)
+{
+ return -ENOSYS;
+}
+
+void hvf_vcpu_destroy(CPUState *cpu)
+{
+}
diff --git a/block.c b/block.c
index 1c37ce4554..a8da4f2b25 100644
--- a/block.c
+++ b/block.c
@@ -822,6 +822,18 @@ static void bdrv_child_cb_drained_end(BdrvChild *child)
bdrv_drained_end(bs);
}
+static void bdrv_child_cb_attach(BdrvChild *child)
+{
+ BlockDriverState *bs = child->opaque;
+ bdrv_apply_subtree_drain(child, bs);
+}
+
+static void bdrv_child_cb_detach(BdrvChild *child)
+{
+ BlockDriverState *bs = child->opaque;
+ bdrv_unapply_subtree_drain(child, bs);
+}
+
static int bdrv_child_cb_inactivate(BdrvChild *child)
{
BlockDriverState *bs = child->opaque;
@@ -889,6 +901,8 @@ const BdrvChildRole child_file = {
.inherit_options = bdrv_inherited_options,
.drained_begin = bdrv_child_cb_drained_begin,
.drained_end = bdrv_child_cb_drained_end,
+ .attach = bdrv_child_cb_attach,
+ .detach = bdrv_child_cb_detach,
.inactivate = bdrv_child_cb_inactivate,
};
@@ -911,6 +925,8 @@ const BdrvChildRole child_format = {
.inherit_options = bdrv_inherited_fmt_options,
.drained_begin = bdrv_child_cb_drained_begin,
.drained_end = bdrv_child_cb_drained_end,
+ .attach = bdrv_child_cb_attach,
+ .detach = bdrv_child_cb_detach,
.inactivate = bdrv_child_cb_inactivate,
};
@@ -953,6 +969,8 @@ static void bdrv_backing_attach(BdrvChild *c)
parent->backing_blocker);
bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET,
parent->backing_blocker);
+
+ bdrv_child_cb_attach(c);
}
static void bdrv_backing_detach(BdrvChild *c)
@@ -963,6 +981,8 @@ static void bdrv_backing_detach(BdrvChild *c)
bdrv_op_unblock_all(c->bs, parent->backing_blocker);
error_free(parent->backing_blocker);
parent->backing_blocker = NULL;
+
+ bdrv_child_cb_detach(c);
}
/*
@@ -1924,6 +1944,8 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
assert(role == &child_backing || role == &child_file);
if (!backing) {
+ int flags = bdrv_reopen_get_flags(reopen_queue, bs);
+
/* Apart from the modifications below, the same permissions are
* forwarded and left alone as for filters */
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
@@ -1936,7 +1958,9 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
/* bs->file always needs to be consistent because of the metadata. We
* can never allow other users to resize or write to it. */
- perm |= BLK_PERM_CONSISTENT_READ;
+ if (!(flags & BDRV_O_NO_IO)) {
+ perm |= BLK_PERM_CONSISTENT_READ;
+ }
shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
} else {
/* We want consistent read from backing files if the parent needs it.
@@ -1968,17 +1992,23 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
BlockDriverState *new_bs)
{
BlockDriverState *old_bs = child->bs;
+ int i;
if (old_bs && new_bs) {
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
}
if (old_bs) {
- if (old_bs->quiesce_counter && child->role->drained_end) {
- child->role->drained_end(child);
- }
+ /* Detach first so that the recursive drain sections coming from @child
+ * are already gone and we only end the drain sections that came from
+ * elsewhere. */
if (child->role->detach) {
child->role->detach(child);
}
+ if (old_bs->quiesce_counter && child->role->drained_end) {
+ for (i = 0; i < old_bs->quiesce_counter; i++) {
+ child->role->drained_end(child);
+ }
+ }
QLIST_REMOVE(child, next_parent);
}
@@ -1987,9 +2017,14 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
if (new_bs) {
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
if (new_bs->quiesce_counter && child->role->drained_begin) {
- child->role->drained_begin(child);
+ for (i = 0; i < new_bs->quiesce_counter; i++) {
+ child->role->drained_begin(child);
+ }
}
+ /* Attach only after starting new drained sections, so that recursive
+ * drain sections coming from @child don't get an extra .drained_begin
+ * callback. */
if (child->role->attach) {
child->role->attach(child);
}
@@ -2731,6 +2766,7 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
* returns a pointer to bs_queue, which is either the newly allocated
* bs_queue, or the existing bs_queue being used.
*
+ * bs must be drained between bdrv_reopen_queue() and bdrv_reopen_multiple().
*/
static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
BlockDriverState *bs,
@@ -2746,6 +2782,11 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
BdrvChild *child;
QDict *old_options, *explicit_options;
+ /* Make sure that the caller remembered to use a drained section. This is
+ * important to avoid graph changes between the recursive queuing here and
+ * bdrv_reopen_multiple(). */
+ assert(bs->quiesce_counter > 0);
+
if (bs_queue == NULL) {
bs_queue = g_new0(BlockReopenQueue, 1);
QSIMPLEQ_INIT(bs_queue);
@@ -2870,6 +2911,8 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
* If all devices prepare successfully, then the changes are committed
* to all devices.
*
+ * All affected nodes must be drained between bdrv_reopen_queue() and
+ * bdrv_reopen_multiple().
*/
int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp)
{
@@ -2879,11 +2922,8 @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
assert(bs_queue != NULL);
- aio_context_release(ctx);
- bdrv_drain_all_begin();
- aio_context_acquire(ctx);
-
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
+ assert(bs_entry->state.bs->quiesce_counter > 0);
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
error_propagate(errp, local_err);
goto cleanup;
@@ -2912,8 +2952,6 @@ cleanup:
}
g_free(bs_queue);
- bdrv_drain_all_end();
-
return ret;
}
@@ -2923,12 +2961,18 @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp)
{
int ret = -1;
Error *local_err = NULL;
- BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
+ BlockReopenQueue *queue;
+ bdrv_subtree_drained_begin(bs);
+
+ queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
}
+
+ bdrv_subtree_drained_end(bs);
+
return ret;
}
@@ -4601,10 +4645,11 @@ void bdrv_img_create(const char *filename, const char *fmt,
back_flags = flags;
back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
+ backing_options = qdict_new();
if (backing_fmt) {
- backing_options = qdict_new();
qdict_put_str(backing_options, "driver", backing_fmt);
}
+ qdict_put_bool(backing_options, BDRV_OPT_FORCE_SHARE, true);
bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
&local_err);
@@ -4754,7 +4799,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
AioContext *ctx = bdrv_get_aio_context(bs);
aio_disable_external(ctx);
- bdrv_parent_drained_begin(bs);
+ bdrv_parent_drained_begin(bs, NULL);
bdrv_drain(bs); /* ensure there are no in-flight requests */
while (aio_poll(ctx, false)) {
@@ -4768,7 +4813,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
*/
aio_context_acquire(new_context);
bdrv_attach_aio_context(bs, new_context);
- bdrv_parent_drained_end(bs);
+ bdrv_parent_drained_end(bs, NULL);
aio_enable_external(ctx);
aio_context_release(new_context);
}
diff --git a/block/commit.c b/block/commit.c
index c5327551ce..bb6c904704 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -277,7 +277,6 @@ void commit_start(const char *job_id, BlockDriverState *bs,
const char *filter_node_name, Error **errp)
{
CommitBlockJob *s;
- BlockReopenQueue *reopen_queue = NULL;
int orig_base_flags;
BlockDriverState *iter;
BlockDriverState *commit_top_bs = NULL;
@@ -299,12 +298,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
/* convert base to r/w, if necessary */
orig_base_flags = bdrv_get_flags(base);
if (!(orig_base_flags & BDRV_O_RDWR)) {
- reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL,
- orig_base_flags | BDRV_O_RDWR);
- }
-
- if (reopen_queue) {
- bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err);
+ bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
goto fail;
diff --git a/block/io.c b/block/io.c
index 6773926fc1..7ea402352e 100644
--- a/block/io.c
+++ b/block/io.c
@@ -40,22 +40,28 @@
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
int64_t offset, int bytes, BdrvRequestFlags flags);
-void bdrv_parent_drained_begin(BlockDriverState *bs)
+void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore)
{
BdrvChild *c, *next;
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
+ if (c == ignore) {
+ continue;
+ }
if (c->role->drained_begin) {
c->role->drained_begin(c);
}
}
}
-void bdrv_parent_drained_end(BlockDriverState *bs)
+void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
{
BdrvChild *c, *next;
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
+ if (c == ignore) {
+ continue;
+ }
if (c->role->drained_end) {
c->role->drained_end(c);
}
@@ -134,29 +140,13 @@ void bdrv_disable_copy_on_read(BlockDriverState *bs)
assert(old >= 1);
}
-/* Check if any requests are in-flight (including throttled requests) */
-bool bdrv_requests_pending(BlockDriverState *bs)
-{
- BdrvChild *child;
-
- if (atomic_read(&bs->in_flight)) {
- return true;
- }
-
- QLIST_FOREACH(child, &bs->children, next) {
- if (bdrv_requests_pending(child->bs)) {
- return true;
- }
- }
-
- return false;
-}
-
typedef struct {
Coroutine *co;
BlockDriverState *bs;
bool done;
bool begin;
+ bool recursive;
+ BdrvChild *parent;
} BdrvCoDrainData;
static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
@@ -175,8 +165,10 @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
bdrv_wakeup(bs);
}
-static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
+/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
+static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, bool recursive)
{
+ BdrvChild *child, *tmp;
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) ||
@@ -187,16 +179,19 @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data);
bdrv_coroutine_enter(bs, data.co);
BDRV_POLL_WHILE(bs, !data.done);
+
+ if (recursive) {
+ QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
+ bdrv_drain_invoke(child->bs, begin, true);
+ }
+ }
}
-static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
+static bool bdrv_drain_recurse(BlockDriverState *bs)
{
BdrvChild *child, *tmp;
bool waited;
- /* Ensure any pending metadata writes are submitted to bs->file. */
- bdrv_drain_invoke(bs, begin);
-
/* Wait for drained requests to finish */
waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0);
@@ -215,7 +210,7 @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
*/
bdrv_ref(bs);
}
- waited |= bdrv_drain_recurse(bs, begin);
+ waited |= bdrv_drain_recurse(bs);
if (in_main_loop) {
bdrv_unref(bs);
}
@@ -224,6 +219,11 @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
return waited;
}
+static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
+ BdrvChild *parent);
+static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
+ BdrvChild *parent);
+
static void bdrv_co_drain_bh_cb(void *opaque)
{
BdrvCoDrainData *data = opaque;
@@ -232,9 +232,9 @@ static void bdrv_co_drain_bh_cb(void *opaque)
bdrv_dec_in_flight(bs);
if (data->begin) {
- bdrv_drained_begin(bs);
+ bdrv_do_drained_begin(bs, data->recursive, data->parent);
} else {
- bdrv_drained_end(bs);
+ bdrv_do_drained_end(bs, data->recursive, data->parent);
}
data->done = true;
@@ -242,7 +242,8 @@ static void bdrv_co_drain_bh_cb(void *opaque)
}
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
- bool begin)
+ bool begin, bool recursive,
+ BdrvChild *parent)
{
BdrvCoDrainData data;
@@ -256,6 +257,8 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
.bs = bs,
.done = false,
.begin = begin,
+ .recursive = recursive,
+ .parent = parent,
};
bdrv_inc_in_flight(bs);
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs),
@@ -267,35 +270,97 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
assert(data.done);
}
-void bdrv_drained_begin(BlockDriverState *bs)
+void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
+ BdrvChild *parent)
{
+ BdrvChild *child, *next;
+
if (qemu_in_coroutine()) {
- bdrv_co_yield_to_drain(bs, true);
+ bdrv_co_yield_to_drain(bs, true, recursive, parent);
return;
}
+ /* Stop things in parent-to-child order */
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
aio_disable_external(bdrv_get_aio_context(bs));
- bdrv_parent_drained_begin(bs);
}
- bdrv_drain_recurse(bs, true);
+ bdrv_parent_drained_begin(bs, parent);
+ bdrv_drain_invoke(bs, true, false);
+ bdrv_drain_recurse(bs);
+
+ if (recursive) {
+ bs->recursive_quiesce_counter++;
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
+ bdrv_do_drained_begin(child->bs, true, child);
+ }
+ }
}
-void bdrv_drained_end(BlockDriverState *bs)
+void bdrv_drained_begin(BlockDriverState *bs)
{
+ bdrv_do_drained_begin(bs, false, NULL);
+}
+
+void bdrv_subtree_drained_begin(BlockDriverState *bs)
+{
+ bdrv_do_drained_begin(bs, true, NULL);
+}
+
+void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
+ BdrvChild *parent)
+{
+ BdrvChild *child, *next;
+ int old_quiesce_counter;
+
if (qemu_in_coroutine()) {
- bdrv_co_yield_to_drain(bs, false);
+ bdrv_co_yield_to_drain(bs, false, recursive, parent);
return;
}
assert(bs->quiesce_counter > 0);
- if (atomic_fetch_dec(&bs->quiesce_counter) > 1) {
- return;
+ old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter);
+
+ /* Re-enable things in child-to-parent order */
+ bdrv_drain_invoke(bs, false, false);
+ bdrv_parent_drained_end(bs, parent);
+ if (old_quiesce_counter == 1) {
+ aio_enable_external(bdrv_get_aio_context(bs));
}
- bdrv_parent_drained_end(bs);
- bdrv_drain_recurse(bs, false);
- aio_enable_external(bdrv_get_aio_context(bs));
+ if (recursive) {
+ bs->recursive_quiesce_counter--;
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
+ bdrv_do_drained_end(child->bs, true, child);
+ }
+ }
+}
+
+void bdrv_drained_end(BlockDriverState *bs)
+{
+ bdrv_do_drained_end(bs, false, NULL);
+}
+
+void bdrv_subtree_drained_end(BlockDriverState *bs)
+{
+ bdrv_do_drained_end(bs, true, NULL);
+}
+
+void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
+{
+ int i;
+
+ for (i = 0; i < new_parent->recursive_quiesce_counter; i++) {
+ bdrv_do_drained_begin(child->bs, true, child);
+ }
+}
+
+void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
+{
+ int i;
+
+ for (i = 0; i < old_parent->recursive_quiesce_counter; i++) {
+ bdrv_do_drained_end(child->bs, true, child);
+ }
}
/*
@@ -342,14 +407,20 @@ void bdrv_drain_all_begin(void)
BdrvNextIterator it;
GSList *aio_ctxs = NULL, *ctx;
- block_job_pause_all();
+ /* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread
+ * or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on
+ * nodes in several different AioContexts, so make sure we're in the main
+ * context. */
+ assert(qemu_get_current_aio_context() == qemu_get_aio_context());
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
AioContext *aio_context = bdrv_get_aio_context(bs);
+ /* Stop things in parent-to-child order */
aio_context_acquire(aio_context);
- bdrv_parent_drained_begin(bs);
aio_disable_external(aio_context);
+ bdrv_parent_drained_begin(bs, NULL);
+ bdrv_drain_invoke(bs, true, true);
aio_context_release(aio_context);
if (!g_slist_find(aio_ctxs, aio_context)) {
@@ -372,7 +443,7 @@ void bdrv_drain_all_begin(void)
aio_context_acquire(aio_context);
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
if (aio_context == bdrv_get_aio_context(bs)) {
- waited |= bdrv_drain_recurse(bs, true);
+ waited |= bdrv_drain_recurse(bs);
}
}
aio_context_release(aio_context);
@@ -390,14 +461,13 @@ void bdrv_drain_all_end(void)
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
AioContext *aio_context = bdrv_get_aio_context(bs);
+ /* Re-enable things in child-to-parent order */
aio_context_acquire(aio_context);
+ bdrv_drain_invoke(bs, false, true);
+ bdrv_parent_drained_end(bs, NULL);
aio_enable_external(aio_context);
- bdrv_parent_drained_end(bs);
- bdrv_drain_recurse(bs, false);
aio_context_release(aio_context);
}
-
- block_job_resume_all();
}
void bdrv_drain_all(void)
diff --git a/block/qcow2.c b/block/qcow2.c
index 1914a940e5..4348b2c0c5 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1672,34 +1672,12 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
return status;
}
-/* handle reading after the end of the backing file */
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
- int64_t offset, int bytes)
-{
- uint64_t bs_size = bs->total_sectors * BDRV_SECTOR_SIZE;
- int n1;
-
- if ((offset + bytes) <= bs_size) {
- return bytes;
- }
-
- if (offset >= bs_size) {
- n1 = 0;
- } else {
- n1 = bs_size - offset;
- }
-
- qemu_iovec_memset(qiov, n1, 0, bytes - n1);
-
- return n1;
-}
-
static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, QEMUIOVector *qiov,
int flags)
{
BDRVQcow2State *s = bs->opaque;
- int offset_in_cluster, n1;
+ int offset_in_cluster;
int ret;
unsigned int cur_bytes; /* number of bytes in current iteration */
uint64_t cluster_offset = 0;
@@ -1734,26 +1712,13 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
case QCOW2_CLUSTER_UNALLOCATED:
if (bs->backing) {
- /* read from the base image */
- n1 = qcow2_backing_read1(bs->backing->bs, &hd_qiov,
- offset, cur_bytes);
- if (n1 > 0) {
- QEMUIOVector local_qiov;
-
- qemu_iovec_init(&local_qiov, hd_qiov.niov);
- qemu_iovec_concat(&local_qiov, &hd_qiov, 0, n1);
-
- BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
- qemu_co_mutex_unlock(&s->lock);
- ret = bdrv_co_preadv(bs->backing, offset, n1,
- &local_qiov, 0);
- qemu_co_mutex_lock(&s->lock);
-
- qemu_iovec_destroy(&local_qiov);
-
- if (ret < 0) {
- goto fail;
- }
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
+ qemu_co_mutex_unlock(&s->lock);
+ ret = bdrv_co_preadv(bs->backing, offset, cur_bytes,
+ &hd_qiov, 0);
+ qemu_co_mutex_lock(&s->lock);
+ if (ret < 0) {
+ goto fail;
}
} else {
/* Note: in this case, no need to wait */
diff --git a/block/qcow2.h b/block/qcow2.h
index 6f0ff15dd0..46c8cf44ec 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -528,9 +528,6 @@ uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset)
}
/* qcow2.c functions */
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
- int64_t sector_num, int nb_sectors);
-
int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size,
int refcount_order, bool generous_increase,
uint64_t *refblock_count);
diff --git a/block/replication.c b/block/replication.c
index e41e293d2b..b1ea3caa4b 100644
--- a/block/replication.c
+++ b/block/replication.c
@@ -394,6 +394,9 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
new_secondary_flags = s->orig_secondary_flags;
}
+ bdrv_subtree_drained_begin(s->hidden_disk->bs);
+ bdrv_subtree_drained_begin(s->secondary_disk->bs);
+
if (orig_hidden_flags != new_hidden_flags) {
reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, NULL,
new_hidden_flags);
@@ -409,6 +412,9 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
reopen_queue, &local_err);
error_propagate(errp, local_err);
}
+
+ bdrv_subtree_drained_end(s->hidden_disk->bs);
+ bdrv_subtree_drained_end(s->secondary_disk->bs);
}
static void backup_job_cleanup(BlockDriverState *bs)
diff --git a/blockdev.c b/blockdev.c
index 9c3a430cfb..29d569a24e 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -735,10 +735,6 @@ QemuOptsList qemu_legacy_drive_opts = {
.type = QEMU_OPT_STRING,
.help = "chs translation (auto, lba, none)",
},{
- .name = "boot",
- .type = QEMU_OPT_BOOL,
- .help = "(deprecated, ignored)",
- },{
.name = "addr",
.type = QEMU_OPT_STRING,
.help = "pci address (virtio only)",
@@ -873,13 +869,6 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
goto fail;
}
- /* Deprecated option boot=[on|off] */
- if (qemu_opt_get(legacy_opts, "boot") != NULL) {
- fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be "
- "ignored. Future versions will reject this parameter. Please "
- "update your scripts.\n");
- }
-
/* Other deprecated options */
if (!qtest_enabled()) {
for (i = 0; i < ARRAY_SIZE(deprecated); i++) {
diff --git a/blockjob.c b/blockjob.c
index 6173e4728c..f5cea84e73 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -234,26 +234,23 @@ static char *child_job_get_parent_desc(BdrvChild *c)
job->id);
}
-static const BdrvChildRole child_job = {
- .get_parent_desc = child_job_get_parent_desc,
- .stay_at_node = true,
-};
-
-static void block_job_drained_begin(void *opaque)
+static void child_job_drained_begin(BdrvChild *c)
{
- BlockJob *job = opaque;
+ BlockJob *job = c->opaque;
block_job_pause(job);
}
-static void block_job_drained_end(void *opaque)
+static void child_job_drained_end(BdrvChild *c)
{
- BlockJob *job = opaque;
+ BlockJob *job = c->opaque;
block_job_resume(job);
}
-static const BlockDevOps block_job_dev_ops = {
- .drained_begin = block_job_drained_begin,
- .drained_end = block_job_drained_end,
+static const BdrvChildRole child_job = {
+ .get_parent_desc = child_job_get_parent_desc,
+ .drained_begin = child_job_drained_begin,
+ .drained_end = child_job_drained_end,
+ .stay_at_node = true,
};
void block_job_remove_all_bdrv(BlockJob *job)
@@ -715,7 +712,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
bs->job = job;
- blk_set_dev_ops(blk, &block_job_dev_ops, job);
bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
QLIST_INSERT_HEAD(&block_jobs, job, job_list);
diff --git a/configure b/configure
index 100309c33f..56f9716609 100755
--- a/configure
+++ b/configure
@@ -211,6 +211,17 @@ supported_xen_target() {
return 1
}
+supported_hvf_target() {
+ test "$hvf" = "yes" || return 1
+ glob "$1" "*-softmmu" || return 1
+ case "${1%-softmmu}" in
+ x86_64)
+ return 0
+ ;;
+ esac
+ return 1
+}
+
supported_target() {
case "$1" in
*-softmmu)
@@ -236,6 +247,7 @@ supported_target() {
supported_kvm_target "$1" && return 0
supported_xen_target "$1" && return 0
supported_hax_target "$1" && return 0
+ supported_hvf_target "$1" && return 0
print_error "TCG disabled, but hardware accelerator not available for '$target'"
return 1
}
@@ -325,6 +337,7 @@ vhost_vsock="no"
vhost_user=""
kvm="no"
hax="no"
+hvf="no"
rdma=""
gprof="no"
debug_tcg="no"
@@ -741,6 +754,7 @@ Darwin)
bsd="yes"
darwin="yes"
hax="yes"
+ hvf="yes"
LDFLAGS_SHARED="-bundle -undefined dynamic_lookup"
if [ "$cpu" = "x86_64" ] ; then
QEMU_CFLAGS="-arch x86_64 $QEMU_CFLAGS"
@@ -1036,6 +1050,10 @@ for opt do
;;
--enable-hax) hax="yes"
;;
+ --disable-hvf) hvf="no"
+ ;;
+ --enable-hvf) hvf="yes"
+ ;;
--disable-tcg-interpreter) tcg_interpreter="no"
;;
--enable-tcg-interpreter) tcg_interpreter="yes"
@@ -1529,6 +1547,7 @@ disabled with --disable-FEATURE, default is enabled if available:
bluez bluez stack connectivity
kvm KVM acceleration support
hax HAX acceleration support
+ hvf Hypervisor.framework acceleration support
rdma RDMA-based migration support
vde support for vde network
netmap support for netmap network
@@ -5056,6 +5075,21 @@ fi
#################################################
+# Check to see if we have the Hypervisor framework
+if [ "$darwin" == "yes" ] ; then
+ cat > $TMPC << EOF
+#include <Hypervisor/hv.h>
+int main() { return 0;}
+EOF
+ if ! compile_object ""; then
+ hvf='no'
+ else
+ hvf='yes'
+ LDFLAGS="-framework Hypervisor $LDFLAGS"
+ fi
+fi
+
+#################################################
# Sparc implicitly links with --relax, which is
# incompatible with -r, so --no-relax should be
# given. It does no harm to give it on other
@@ -5530,6 +5564,7 @@ echo "ATTR/XATTR support $attr"
echo "Install blobs $blobs"
echo "KVM support $kvm"
echo "HAX support $hax"
+echo "HVF support $hvf"
echo "TCG support $tcg"
if test "$tcg" = "yes" ; then
echo "TCG debug enabled $debug_tcg"
@@ -6602,6 +6637,9 @@ fi
if supported_hax_target $target; then
echo "CONFIG_HAX=y" >> $config_target_mak
fi
+if supported_hvf_target $target; then
+ echo "CONFIG_HVF=y" >> $config_target_mak
+fi
if test "$target_bigendian" = "yes" ; then
echo "TARGET_WORDS_BIGENDIAN=y" >> $config_target_mak
fi
diff --git a/cpus.c b/cpus.c
index 83700c1716..e8139de534 100644
--- a/cpus.c
+++ b/cpus.c
@@ -37,6 +37,7 @@
#include "sysemu/hw_accel.h"
#include "sysemu/kvm.h"
#include "sysemu/hax.h"
+#include "sysemu/hvf.h"
#include "qmp-commands.h"
#include "exec/exec-all.h"
@@ -900,6 +901,10 @@ void cpu_synchronize_all_states(void)
CPU_FOREACH(cpu) {
cpu_synchronize_state(cpu);
+ /* TODO: move to cpu_synchronize_state() */
+ if (hvf_enabled()) {
+ hvf_cpu_synchronize_state(cpu);
+ }
}
}
@@ -909,6 +914,10 @@ void cpu_synchronize_all_post_reset(void)
CPU_FOREACH(cpu) {
cpu_synchronize_post_reset(cpu);
+ /* TODO: move to cpu_synchronize_post_reset() */
+ if (hvf_enabled()) {
+ hvf_cpu_synchronize_post_reset(cpu);
+ }
}
}
@@ -918,6 +927,10 @@ void cpu_synchronize_all_post_init(void)
CPU_FOREACH(cpu) {
cpu_synchronize_post_init(cpu);
+ /* TODO: move to cpu_synchronize_post_init() */
+ if (hvf_enabled()) {
+ hvf_cpu_synchronize_post_init(cpu);
+ }
}
}
@@ -1107,6 +1120,14 @@ static void qemu_kvm_wait_io_event(CPUState *cpu)
qemu_wait_io_event_common(cpu);
}
+static void qemu_hvf_wait_io_event(CPUState *cpu)
+{
+ while (cpu_thread_is_idle(cpu)) {
+ qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
+ }
+ qemu_wait_io_event_common(cpu);
+}
+
static void *qemu_kvm_cpu_thread_fn(void *arg)
{
CPUState *cpu = arg;
@@ -1444,6 +1465,48 @@ static void *qemu_hax_cpu_thread_fn(void *arg)
return NULL;
}
+/* The HVF-specific vCPU thread function. This one should only run when the host
+ * CPU supports the VMX "unrestricted guest" feature. */
+static void *qemu_hvf_cpu_thread_fn(void *arg)
+{
+ CPUState *cpu = arg;
+
+ int r;
+
+ assert(hvf_enabled());
+
+ rcu_register_thread();
+
+ qemu_mutex_lock_iothread();
+ qemu_thread_get_self(cpu->thread);
+
+ cpu->thread_id = qemu_get_thread_id();
+ cpu->can_do_io = 1;
+ current_cpu = cpu;
+
+ hvf_init_vcpu(cpu);
+
+ /* signal CPU creation */
+ cpu->created = true;
+ qemu_cond_signal(&qemu_cpu_cond);
+
+ do {
+ if (cpu_can_run(cpu)) {
+ r = hvf_vcpu_exec(cpu);
+ if (r == EXCP_DEBUG) {
+ cpu_handle_guest_debug(cpu);
+ }
+ }
+ qemu_hvf_wait_io_event(cpu);
+ } while (!cpu->unplug || cpu_can_run(cpu));
+
+ hvf_vcpu_destroy(cpu);
+ cpu->created = false;
+ qemu_cond_signal(&qemu_cpu_cond);
+ qemu_mutex_unlock_iothread();
+ return NULL;
+}
+
#ifdef _WIN32
static void CALLBACK dummy_apc_func(ULONG_PTR unused)
{
@@ -1761,6 +1824,27 @@ static void qemu_kvm_start_vcpu(CPUState *cpu)
}
}
+static void qemu_hvf_start_vcpu(CPUState *cpu)
+{
+ char thread_name[VCPU_THREAD_NAME_SIZE];
+
+ /* HVF currently does not support TCG, and only runs in
+ * unrestricted-guest mode. */
+ assert(hvf_enabled());
+
+ cpu->thread = g_malloc0(sizeof(QemuThread));
+ cpu->halt_cond = g_malloc0(sizeof(QemuCond));
+ qemu_cond_init(cpu->halt_cond);
+
+ snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/HVF",
+ cpu->cpu_index);
+ qemu_thread_create(cpu->thread, thread_name, qemu_hvf_cpu_thread_fn,
+ cpu, QEMU_THREAD_JOINABLE);
+ while (!cpu->created) {
+ qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
+ }
+}
+
static void qemu_dummy_start_vcpu(CPUState *cpu)
{
char thread_name[VCPU_THREAD_NAME_SIZE];
@@ -1795,6 +1879,8 @@ void qemu_init_vcpu(CPUState *cpu)
qemu_kvm_start_vcpu(cpu);
} else if (hax_enabled()) {
qemu_hax_start_vcpu(cpu);
+ } else if (hvf_enabled()) {
+ qemu_hvf_start_vcpu(cpu);
} else if (tcg_enabled()) {
qemu_tcg_init_vcpu(cpu);
} else {
diff --git a/hmp.c b/hmp.c
index 35a7041824..2d72f94193 100644
--- a/hmp.c
+++ b/hmp.c
@@ -2318,7 +2318,6 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
{
BlockBackend *blk;
BlockBackend *local_blk = NULL;
- AioContext *aio_context;
const char* device = qdict_get_str(qdict, "device");
const char* command = qdict_get_str(qdict, "command");
Error *err = NULL;
@@ -2338,9 +2337,6 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
}
}
- aio_context = blk_get_aio_context(blk);
- aio_context_acquire(aio_context);
-
/*
* Notably absent: Proper permission management. This is sad, but it seems
* almost impossible to achieve without changing the semantics and thereby
@@ -2368,8 +2364,6 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
*/
qemuio_command(blk, command);
- aio_context_release(aio_context);
-
fail:
blk_unref(local_blk);
hmp_handle_error(mon, &err);
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
index e529e88e4e..1ac356d3a5 100644
--- a/hw/block/nvme.c
+++ b/hw/block/nvme.c
@@ -34,8 +34,17 @@
#include "qapi/visitor.h"
#include "sysemu/block-backend.h"
+#include "qemu/log.h"
+#include "trace.h"
#include "nvme.h"
+#define NVME_GUEST_ERR(trace, fmt, ...) \
+ do { \
+ (trace_##trace)(__VA_ARGS__); \
+ qemu_log_mask(LOG_GUEST_ERROR, #trace \
+ " in %s: " fmt "\n", __func__, ## __VA_ARGS__); \
+ } while (0)
+
static void nvme_process_sq(void *opaque);
static void nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size)
@@ -86,10 +95,14 @@ static void nvme_isr_notify(NvmeCtrl *n, NvmeCQueue *cq)
{
if (cq->irq_enabled) {
if (msix_enabled(&(n->parent_obj))) {
+ trace_nvme_irq_msix(cq->vector);
msix_notify(&(n->parent_obj), cq->vector);
} else {
+ trace_nvme_irq_pin();
pci_irq_pulse(&n->parent_obj);
}
+ } else {
+ trace_nvme_irq_masked();
}
}
@@ -100,7 +113,8 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
trans_len = MIN(len, trans_len);
int num_prps = (len >> n->page_bits) + 1;
- if (!prp1) {
+ if (unlikely(!prp1)) {
+ trace_nvme_err_invalid_prp();
return NVME_INVALID_FIELD | NVME_DNR;
} else if (n->cmbsz && prp1 >= n->ctrl_mem.addr &&
prp1 < n->ctrl_mem.addr + int128_get64(n->ctrl_mem.size)) {
@@ -113,7 +127,8 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
}
len -= trans_len;
if (len) {
- if (!prp2) {
+ if (unlikely(!prp2)) {
+ trace_nvme_err_invalid_prp2_missing();
goto unmap;
}
if (len > n->page_size) {
@@ -128,7 +143,8 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
uint64_t prp_ent = le64_to_cpu(prp_list[i]);
if (i == n->max_prp_ents - 1 && len > n->page_size) {
- if (!prp_ent || prp_ent & (n->page_size - 1)) {
+ if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
+ trace_nvme_err_invalid_prplist_ent(prp_ent);
goto unmap;
}
@@ -140,7 +156,8 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
prp_ent = le64_to_cpu(prp_list[i]);
}
- if (!prp_ent || prp_ent & (n->page_size - 1)) {
+ if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
+ trace_nvme_err_invalid_prplist_ent(prp_ent);
goto unmap;
}
@@ -154,7 +171,8 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
i++;
}
} else {
- if (prp2 & (n->page_size - 1)) {
+ if (unlikely(prp2 & (n->page_size - 1))) {
+ trace_nvme_err_invalid_prp2_align(prp2);
goto unmap;
}
if (qsg->nsg) {
@@ -178,16 +196,20 @@ static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
QEMUIOVector iov;
uint16_t status = NVME_SUCCESS;
+ trace_nvme_dma_read(prp1, prp2);
+
if (nvme_map_prp(&qsg, &iov, prp1, prp2, len, n)) {
return NVME_INVALID_FIELD | NVME_DNR;
}
if (qsg.nsg > 0) {
- if (dma_buf_read(ptr, len, &qsg)) {
+ if (unlikely(dma_buf_read(ptr, len, &qsg))) {
+ trace_nvme_err_invalid_dma();
status = NVME_INVALID_FIELD | NVME_DNR;
}
qemu_sglist_destroy(&qsg);
} else {
- if (qemu_iovec_to_buf(&iov, 0, ptr, len) != len) {
+ if (unlikely(qemu_iovec_to_buf(&iov, 0, ptr, len) != len)) {
+ trace_nvme_err_invalid_dma();
status = NVME_INVALID_FIELD | NVME_DNR;
}
qemu_iovec_destroy(&iov);
@@ -273,7 +295,8 @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
uint32_t aio_nlb = nlb << (data_shift - BDRV_SECTOR_BITS);
- if (slba + nlb > ns->id_ns.nsze) {
+ if (unlikely(slba + nlb > ns->id_ns.nsze)) {
+ trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
return NVME_LBA_RANGE | NVME_DNR;
}
@@ -301,8 +324,11 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0;
enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ;
- if ((slba + nlb) > ns->id_ns.nsze) {
+ trace_nvme_rw(is_write ? "write" : "read", nlb, data_size, slba);
+
+ if (unlikely((slba + nlb) > ns->id_ns.nsze)) {
block_acct_invalid(blk_get_stats(n->conf.blk), acct);
+ trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
return NVME_LBA_RANGE | NVME_DNR;
}
@@ -336,7 +362,8 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
NvmeNamespace *ns;
uint32_t nsid = le32_to_cpu(cmd->nsid);
- if (nsid == 0 || nsid > n->num_namespaces) {
+ if (unlikely(nsid == 0 || nsid > n->num_namespaces)) {
+ trace_nvme_err_invalid_ns(nsid, n->num_namespaces);
return NVME_INVALID_NSID | NVME_DNR;
}
@@ -350,6 +377,7 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
case NVME_CMD_READ:
return nvme_rw(n, ns, cmd, req);
default:
+ trace_nvme_err_invalid_opc(cmd->opcode);
return NVME_INVALID_OPCODE | NVME_DNR;
}
}
@@ -373,10 +401,13 @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeCmd *cmd)
NvmeCQueue *cq;
uint16_t qid = le16_to_cpu(c->qid);
- if (!qid || nvme_check_sqid(n, qid)) {
+ if (unlikely(!qid || nvme_check_sqid(n, qid))) {
+ trace_nvme_err_invalid_del_sq(qid);
return NVME_INVALID_QID | NVME_DNR;
}
+ trace_nvme_del_sq(qid);
+
sq = n->sq[qid];
while (!QTAILQ_EMPTY(&sq->out_req_list)) {
req = QTAILQ_FIRST(&sq->out_req_list);
@@ -439,19 +470,26 @@ static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeCmd *cmd)
uint16_t qflags = le16_to_cpu(c->sq_flags);
uint64_t prp1 = le64_to_cpu(c->prp1);
- if (!cqid || nvme_check_cqid(n, cqid)) {
+ trace_nvme_create_sq(prp1, sqid, cqid, qsize, qflags);
+
+ if (unlikely(!cqid || nvme_check_cqid(n, cqid))) {
+ trace_nvme_err_invalid_create_sq_cqid(cqid);
return NVME_INVALID_CQID | NVME_DNR;
}
- if (!sqid || !nvme_check_sqid(n, sqid)) {
+ if (unlikely(!sqid || !nvme_check_sqid(n, sqid))) {
+ trace_nvme_err_invalid_create_sq_sqid(sqid);
return NVME_INVALID_QID | NVME_DNR;
}
- if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) {
+ if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) {
+ trace_nvme_err_invalid_create_sq_size(qsize);
return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR;
}
- if (!prp1 || prp1 & (n->page_size - 1)) {
+ if (unlikely(!prp1 || prp1 & (n->page_size - 1))) {
+ trace_nvme_err_invalid_create_sq_addr(prp1);
return NVME_INVALID_FIELD | NVME_DNR;
}
- if (!(NVME_SQ_FLAGS_PC(qflags))) {
+ if (unlikely(!(NVME_SQ_FLAGS_PC(qflags)))) {
+ trace_nvme_err_invalid_create_sq_qflags(NVME_SQ_FLAGS_PC(qflags));
return NVME_INVALID_FIELD | NVME_DNR;
}
sq = g_malloc0(sizeof(*sq));
@@ -476,14 +514,17 @@ static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeCmd *cmd)
NvmeCQueue *cq;
uint16_t qid = le16_to_cpu(c->qid);
- if (!qid || nvme_check_cqid(n, qid)) {
+ if (unlikely(!qid || nvme_check_cqid(n, qid))) {
+ trace_nvme_err_invalid_del_cq_cqid(qid);
return NVME_INVALID_CQID | NVME_DNR;
}
cq = n->cq[qid];
- if (!QTAILQ_EMPTY(&cq->sq_list)) {
+ if (unlikely(!QTAILQ_EMPTY(&cq->sq_list))) {
+ trace_nvme_err_invalid_del_cq_notempty(qid);
return NVME_INVALID_QUEUE_DEL;
}
+ trace_nvme_del_cq(qid);
nvme_free_cq(cq, n);
return NVME_SUCCESS;
}
@@ -516,19 +557,27 @@ static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeCmd *cmd)
uint16_t qflags = le16_to_cpu(c->cq_flags);
uint64_t prp1 = le64_to_cpu(c->prp1);
- if (!cqid || !nvme_check_cqid(n, cqid)) {
+ trace_nvme_create_cq(prp1, cqid, vector, qsize, qflags,
+ NVME_CQ_FLAGS_IEN(qflags) != 0);
+
+ if (unlikely(!cqid || !nvme_check_cqid(n, cqid))) {
+ trace_nvme_err_invalid_create_cq_cqid(cqid);
return NVME_INVALID_CQID | NVME_DNR;
}
- if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) {
+ if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) {
+ trace_nvme_err_invalid_create_cq_size(qsize);
return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR;
}
- if (!prp1) {
+ if (unlikely(!prp1)) {
+ trace_nvme_err_invalid_create_cq_addr(prp1);
return NVME_INVALID_FIELD | NVME_DNR;
}
- if (vector > n->num_queues) {
+ if (unlikely(vector > n->num_queues)) {
+ trace_nvme_err_invalid_create_cq_vector(vector);
return NVME_INVALID_IRQ_VECTOR | NVME_DNR;
}
- if (!(NVME_CQ_FLAGS_PC(qflags))) {
+ if (unlikely(!(NVME_CQ_FLAGS_PC(qflags)))) {
+ trace_nvme_err_invalid_create_cq_qflags(NVME_CQ_FLAGS_PC(qflags));
return NVME_INVALID_FIELD | NVME_DNR;
}
@@ -543,6 +592,8 @@ static uint16_t nvme_identify_ctrl(NvmeCtrl *n, NvmeIdentify *c)
uint64_t prp1 = le64_to_cpu(c->prp1);
uint64_t prp2 = le64_to_cpu(c->prp2);
+ trace_nvme_identify_ctrl();
+
return nvme_dma_read_prp(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl),
prp1, prp2);
}
@@ -554,11 +605,15 @@ static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeIdentify *c)
uint64_t prp1 = le64_to_cpu(c->prp1);
uint64_t prp2 = le64_to_cpu(c->prp2);
- if (nsid == 0 || nsid > n->num_namespaces) {
+ trace_nvme_identify_ns(nsid);
+
+ if (unlikely(nsid == 0 || nsid > n->num_namespaces)) {
+ trace_nvme_err_invalid_ns(nsid, n->num_namespaces);
return NVME_INVALID_NSID | NVME_DNR;
}
ns = &n->namespaces[nsid - 1];
+
return nvme_dma_read_prp(n, (uint8_t *)&ns->id_ns, sizeof(ns->id_ns),
prp1, prp2);
}
@@ -573,6 +628,8 @@ static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeIdentify *c)
uint16_t ret;
int i, j = 0;
+ trace_nvme_identify_nslist(min_nsid);
+
list = g_malloc0(data_len);
for (i = 0; i < n->num_namespaces; i++) {
if (i < min_nsid) {
@@ -601,6 +658,7 @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd)
case 0x02:
return nvme_identify_nslist(n, c);
default:
+ trace_nvme_err_invalid_identify_cns(le32_to_cpu(c->cns));
return NVME_INVALID_FIELD | NVME_DNR;
}
}
@@ -613,11 +671,14 @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
switch (dw10) {
case NVME_VOLATILE_WRITE_CACHE:
result = blk_enable_write_cache(n->conf.blk);
+ trace_nvme_getfeat_vwcache(result ? "enabled" : "disabled");
break;
case NVME_NUMBER_OF_QUEUES:
result = cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
+ trace_nvme_getfeat_numq(result);
break;
default:
+ trace_nvme_err_invalid_getfeat(dw10);
return NVME_INVALID_FIELD | NVME_DNR;
}
@@ -635,10 +696,14 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
blk_set_enable_write_cache(n->conf.blk, dw11 & 1);
break;
case NVME_NUMBER_OF_QUEUES:
+ trace_nvme_setfeat_numq((dw11 & 0xFFFF) + 1,
+ ((dw11 >> 16) & 0xFFFF) + 1,
+ n->num_queues - 1, n->num_queues - 1);
req->cqe.result =
cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
break;
default:
+ trace_nvme_err_invalid_setfeat(dw10);
return NVME_INVALID_FIELD | NVME_DNR;
}
return NVME_SUCCESS;
@@ -662,6 +727,7 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
case NVME_ADM_CMD_GET_FEATURES:
return nvme_get_feature(n, cmd, req);
default:
+ trace_nvme_err_invalid_admin_opc(cmd->opcode);
return NVME_INVALID_OPCODE | NVME_DNR;
}
}
@@ -721,15 +787,78 @@ static int nvme_start_ctrl(NvmeCtrl *n)
uint32_t page_bits = NVME_CC_MPS(n->bar.cc) + 12;
uint32_t page_size = 1 << page_bits;
- if (n->cq[0] || n->sq[0] || !n->bar.asq || !n->bar.acq ||
- n->bar.asq & (page_size - 1) || n->bar.acq & (page_size - 1) ||
- NVME_CC_MPS(n->bar.cc) < NVME_CAP_MPSMIN(n->bar.cap) ||
- NVME_CC_MPS(n->bar.cc) > NVME_CAP_MPSMAX(n->bar.cap) ||
- NVME_CC_IOCQES(n->bar.cc) < NVME_CTRL_CQES_MIN(n->id_ctrl.cqes) ||
- NVME_CC_IOCQES(n->bar.cc) > NVME_CTRL_CQES_MAX(n->id_ctrl.cqes) ||
- NVME_CC_IOSQES(n->bar.cc) < NVME_CTRL_SQES_MIN(n->id_ctrl.sqes) ||
- NVME_CC_IOSQES(n->bar.cc) > NVME_CTRL_SQES_MAX(n->id_ctrl.sqes) ||
- !NVME_AQA_ASQS(n->bar.aqa) || !NVME_AQA_ACQS(n->bar.aqa)) {
+ if (unlikely(n->cq[0])) {
+ trace_nvme_err_startfail_cq();
+ return -1;
+ }
+ if (unlikely(n->sq[0])) {
+ trace_nvme_err_startfail_sq();
+ return -1;
+ }
+ if (unlikely(!n->bar.asq)) {
+ trace_nvme_err_startfail_nbarasq();
+ return -1;
+ }
+ if (unlikely(!n->bar.acq)) {
+ trace_nvme_err_startfail_nbaracq();
+ return -1;
+ }
+ if (unlikely(n->bar.asq & (page_size - 1))) {
+ trace_nvme_err_startfail_asq_misaligned(n->bar.asq);
+ return -1;
+ }
+ if (unlikely(n->bar.acq & (page_size - 1))) {
+ trace_nvme_err_startfail_acq_misaligned(n->bar.acq);
+ return -1;
+ }
+ if (unlikely(NVME_CC_MPS(n->bar.cc) <
+ NVME_CAP_MPSMIN(n->bar.cap))) {
+ trace_nvme_err_startfail_page_too_small(
+ NVME_CC_MPS(n->bar.cc),
+ NVME_CAP_MPSMIN(n->bar.cap));
+ return -1;
+ }
+ if (unlikely(NVME_CC_MPS(n->bar.cc) >
+ NVME_CAP_MPSMAX(n->bar.cap))) {
+ trace_nvme_err_startfail_page_too_large(
+ NVME_CC_MPS(n->bar.cc),
+ NVME_CAP_MPSMAX(n->bar.cap));
+ return -1;
+ }
+ if (unlikely(NVME_CC_IOCQES(n->bar.cc) <
+ NVME_CTRL_CQES_MIN(n->id_ctrl.cqes))) {
+ trace_nvme_err_startfail_cqent_too_small(
+ NVME_CC_IOCQES(n->bar.cc),
+ NVME_CTRL_CQES_MIN(n->bar.cap));
+ return -1;
+ }
+ if (unlikely(NVME_CC_IOCQES(n->bar.cc) >
+ NVME_CTRL_CQES_MAX(n->id_ctrl.cqes))) {
+ trace_nvme_err_startfail_cqent_too_large(
+ NVME_CC_IOCQES(n->bar.cc),
+ NVME_CTRL_CQES_MAX(n->bar.cap));
+ return -1;
+ }
+ if (unlikely(NVME_CC_IOSQES(n->bar.cc) <
+ NVME_CTRL_SQES_MIN(n->id_ctrl.sqes))) {
+ trace_nvme_err_startfail_sqent_too_small(
+ NVME_CC_IOSQES(n->bar.cc),
+ NVME_CTRL_SQES_MIN(n->bar.cap));
+ return -1;
+ }
+ if (unlikely(NVME_CC_IOSQES(n->bar.cc) >
+ NVME_CTRL_SQES_MAX(n->id_ctrl.sqes))) {
+ trace_nvme_err_startfail_sqent_too_large(
+ NVME_CC_IOSQES(n->bar.cc),
+ NVME_CTRL_SQES_MAX(n->bar.cap));
+ return -1;
+ }
+ if (unlikely(!NVME_AQA_ASQS(n->bar.aqa))) {
+ trace_nvme_err_startfail_asqent_sz_zero();
+ return -1;
+ }
+ if (unlikely(!NVME_AQA_ACQS(n->bar.aqa))) {
+ trace_nvme_err_startfail_acqent_sz_zero();
return -1;
}
@@ -749,16 +878,48 @@ static int nvme_start_ctrl(NvmeCtrl *n)
static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
unsigned size)
{
+ if (unlikely(offset & (sizeof(uint32_t) - 1))) {
+ NVME_GUEST_ERR(nvme_ub_mmiowr_misaligned32,
+ "MMIO write not 32-bit aligned,"
+ " offset=0x%"PRIx64"", offset);
+ /* should be ignored, fall through for now */
+ }
+
+ if (unlikely(size < sizeof(uint32_t))) {
+ NVME_GUEST_ERR(nvme_ub_mmiowr_toosmall,
+ "MMIO write smaller than 32-bits,"
+ " offset=0x%"PRIx64", size=%u",
+ offset, size);
+ /* should be ignored, fall through for now */
+ }
+
switch (offset) {
- case 0xc:
+ case 0xc: /* INTMS */
+ if (unlikely(msix_enabled(&(n->parent_obj)))) {
+ NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix,
+ "undefined access to interrupt mask set"
+ " when MSI-X is enabled");
+ /* should be ignored, fall through for now */
+ }
n->bar.intms |= data & 0xffffffff;
n->bar.intmc = n->bar.intms;
+ trace_nvme_mmio_intm_set(data & 0xffffffff,
+ n->bar.intmc);
break;
- case 0x10:
+ case 0x10: /* INTMC */
+ if (unlikely(msix_enabled(&(n->parent_obj)))) {
+ NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix,
+ "undefined access to interrupt mask clr"
+ " when MSI-X is enabled");
+ /* should be ignored, fall through for now */
+ }
n->bar.intms &= ~(data & 0xffffffff);
n->bar.intmc = n->bar.intms;
+ trace_nvme_mmio_intm_clr(data & 0xffffffff,
+ n->bar.intmc);
break;
- case 0x14:
+ case 0x14: /* CC */
+ trace_nvme_mmio_cfg(data & 0xffffffff);
/* Windows first sends data, then sends enable bit */
if (!NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc) &&
!NVME_CC_SHN(data) && !NVME_CC_SHN(n->bar.cc))
@@ -768,40 +929,82 @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
if (NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc)) {
n->bar.cc = data;
- if (nvme_start_ctrl(n)) {
+ if (unlikely(nvme_start_ctrl(n))) {
+ trace_nvme_err_startfail();
n->bar.csts = NVME_CSTS_FAILED;
} else {
+ trace_nvme_mmio_start_success();
n->bar.csts = NVME_CSTS_READY;
}
} else if (!NVME_CC_EN(data) && NVME_CC_EN(n->bar.cc)) {
+ trace_nvme_mmio_stopped();
nvme_clear_ctrl(n);
n->bar.csts &= ~NVME_CSTS_READY;
}
if (NVME_CC_SHN(data) && !(NVME_CC_SHN(n->bar.cc))) {
- nvme_clear_ctrl(n);
- n->bar.cc = data;
- n->bar.csts |= NVME_CSTS_SHST_COMPLETE;
+ trace_nvme_mmio_shutdown_set();
+ nvme_clear_ctrl(n);
+ n->bar.cc = data;
+ n->bar.csts |= NVME_CSTS_SHST_COMPLETE;
} else if (!NVME_CC_SHN(data) && NVME_CC_SHN(n->bar.cc)) {
- n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE;
- n->bar.cc = data;
+ trace_nvme_mmio_shutdown_cleared();
+ n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE;
+ n->bar.cc = data;
+ }
+ break;
+ case 0x1C: /* CSTS */
+ if (data & (1 << 4)) {
+ NVME_GUEST_ERR(nvme_ub_mmiowr_ssreset_w1c_unsupported,
+ "attempted to W1C CSTS.NSSRO"
+ " but CAP.NSSRS is zero (not supported)");
+ } else if (data != 0) {
+ NVME_GUEST_ERR(nvme_ub_mmiowr_ro_csts,
+ "attempted to set a read only bit"
+ " of controller status");
+ }
+ break;
+ case 0x20: /* NSSR */
+ if (data == 0x4E564D65) {
+ trace_nvme_ub_mmiowr_ssreset_unsupported();
+ } else {
+ /* The spec says that writes of other values have no effect */
+ return;
}
break;
- case 0x24:
+ case 0x24: /* AQA */
n->bar.aqa = data & 0xffffffff;
+ trace_nvme_mmio_aqattr(data & 0xffffffff);
break;
- case 0x28:
+ case 0x28: /* ASQ */
n->bar.asq = data;
+ trace_nvme_mmio_asqaddr(data);
break;
- case 0x2c:
+ case 0x2c: /* ASQ hi */
n->bar.asq |= data << 32;
+ trace_nvme_mmio_asqaddr_hi(data, n->bar.asq);
break;
- case 0x30:
+ case 0x30: /* ACQ */
+ trace_nvme_mmio_acqaddr(data);
n->bar.acq = data;
break;
- case 0x34:
+ case 0x34: /* ACQ hi */
n->bar.acq |= data << 32;
+ trace_nvme_mmio_acqaddr_hi(data, n->bar.acq);
break;
+ case 0x38: /* CMBLOC */
+ NVME_GUEST_ERR(nvme_ub_mmiowr_cmbloc_reserved,
+ "invalid write to reserved CMBLOC"
+ " when CMBSZ is zero, ignored");
+ return;
+ case 0x3C: /* CMBSZ */
+ NVME_GUEST_ERR(nvme_ub_mmiowr_cmbsz_readonly,
+ "invalid write to read only CMBSZ, ignored");
+ return;
default:
+ NVME_GUEST_ERR(nvme_ub_mmiowr_invalid,
+ "invalid MMIO write,"
+ " offset=0x%"PRIx64", data=%"PRIx64"",
+ offset, data);
break;
}
}
@@ -812,9 +1015,26 @@ static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size)
uint8_t *ptr = (uint8_t *)&n->bar;
uint64_t val = 0;
+ if (unlikely(addr & (sizeof(uint32_t) - 1))) {
+ NVME_GUEST_ERR(nvme_ub_mmiord_misaligned32,
+ "MMIO read not 32-bit aligned,"
+ " offset=0x%"PRIx64"", addr);
+ /* should RAZ, fall through for now */
+ } else if (unlikely(size < sizeof(uint32_t))) {
+ NVME_GUEST_ERR(nvme_ub_mmiord_toosmall,
+ "MMIO read smaller than 32-bits,"
+ " offset=0x%"PRIx64"", addr);
+ /* should RAZ, fall through for now */
+ }
+
if (addr < sizeof(n->bar)) {
memcpy(&val, ptr + addr, size);
+ } else {
+ NVME_GUEST_ERR(nvme_ub_mmiord_invalid_ofs,
+ "MMIO read beyond last register,"
+ " offset=0x%"PRIx64", returning 0", addr);
}
+
return val;
}
@@ -822,22 +1042,36 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val)
{
uint32_t qid;
- if (addr & ((1 << 2) - 1)) {
+ if (unlikely(addr & ((1 << 2) - 1))) {
+ NVME_GUEST_ERR(nvme_ub_db_wr_misaligned,
+ "doorbell write not 32-bit aligned,"
+ " offset=0x%"PRIx64", ignoring", addr);
return;
}
if (((addr - 0x1000) >> 2) & 1) {
+ /* Completion queue doorbell write */
+
uint16_t new_head = val & 0xffff;
int start_sqs;
NvmeCQueue *cq;
qid = (addr - (0x1000 + (1 << 2))) >> 3;
- if (nvme_check_cqid(n, qid)) {
+ if (unlikely(nvme_check_cqid(n, qid))) {
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cq,
+ "completion queue doorbell write"
+ " for nonexistent queue,"
+ " sqid=%"PRIu32", ignoring", qid);
return;
}
cq = n->cq[qid];
- if (new_head >= cq->size) {
+ if (unlikely(new_head >= cq->size)) {
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cqhead,
+ "completion queue doorbell write value"
+ " beyond queue size, sqid=%"PRIu32","
+ " new_head=%"PRIu16", ignoring",
+ qid, new_head);
return;
}
@@ -855,16 +1089,27 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val)
nvme_isr_notify(n, cq);
}
} else {
+ /* Submission queue doorbell write */
+
uint16_t new_tail = val & 0xffff;
NvmeSQueue *sq;
qid = (addr - 0x1000) >> 3;
- if (nvme_check_sqid(n, qid)) {
+ if (unlikely(nvme_check_sqid(n, qid))) {
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sq,
+ "submission queue doorbell write"
+ " for nonexistent queue,"
+ " sqid=%"PRIu32", ignoring", qid);
return;
}
sq = n->sq[qid];
- if (new_tail >= sq->size) {
+ if (unlikely(new_tail >= sq->size)) {
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sqtail,
+ "submission queue doorbell write value"
+ " beyond queue size, sqid=%"PRIu32","
+ " new_tail=%"PRIu16", ignoring",
+ qid, new_tail);
return;
}
diff --git a/hw/block/trace-events b/hw/block/trace-events
index 962a3bfa24..5acd495207 100644
--- a/hw/block/trace-events
+++ b/hw/block/trace-events
@@ -11,6 +11,99 @@ virtio_blk_submit_multireq(void *vdev, void *mrb, int start, int num_reqs, uint6
hd_geometry_lchs_guess(void *blk, int cyls, int heads, int secs) "blk %p LCHS %d %d %d"
hd_geometry_guess(void *blk, uint32_t cyls, uint32_t heads, uint32_t secs, int trans) "blk %p CHS %u %u %u trans %d"
+# hw/block/nvme.c
+# nvme traces for successful events
+nvme_irq_msix(uint32_t vector) "raising MSI-X IRQ vector %u"
+nvme_irq_pin(void) "pulsing IRQ pin"
+nvme_irq_masked(void) "IRQ is masked"
+nvme_dma_read(uint64_t prp1, uint64_t prp2) "DMA read, prp1=0x%"PRIx64" prp2=0x%"PRIx64""
+nvme_rw(char const *verb, uint32_t blk_count, uint64_t byte_count, uint64_t lba) "%s %"PRIu32" blocks (%"PRIu64" bytes) from LBA %"PRIu64""
+nvme_create_sq(uint64_t addr, uint16_t sqid, uint16_t cqid, uint16_t qsize, uint16_t qflags) "create submission queue, addr=0x%"PRIx64", sqid=%"PRIu16", cqid=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16""
+nvme_create_cq(uint64_t addr, uint16_t cqid, uint16_t vector, uint16_t size, uint16_t qflags, int ien) "create completion queue, addr=0x%"PRIx64", cqid=%"PRIu16", vector=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16", ien=%d"
+nvme_del_sq(uint16_t qid) "deleting submission queue sqid=%"PRIu16""
+nvme_del_cq(uint16_t cqid) "deleted completion queue, sqid=%"PRIu16""
+nvme_identify_ctrl(void) "identify controller"
+nvme_identify_ns(uint16_t ns) "identify namespace, nsid=%"PRIu16""
+nvme_identify_nslist(uint16_t ns) "identify namespace list, nsid=%"PRIu16""
+nvme_getfeat_vwcache(char const* result) "get feature volatile write cache, result=%s"
+nvme_getfeat_numq(int result) "get feature number of queues, result=%d"
+nvme_setfeat_numq(int reqcq, int reqsq, int gotcq, int gotsq) "requested cq_count=%d sq_count=%d, responding with cq_count=%d sq_count=%d"
+nvme_mmio_intm_set(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask set, data=0x%"PRIx64", new_mask=0x%"PRIx64""
+nvme_mmio_intm_clr(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask clr, data=0x%"PRIx64", new_mask=0x%"PRIx64""
+nvme_mmio_cfg(uint64_t data) "wrote MMIO, config controller config=0x%"PRIx64""
+nvme_mmio_aqattr(uint64_t data) "wrote MMIO, admin queue attributes=0x%"PRIx64""
+nvme_mmio_asqaddr(uint64_t data) "wrote MMIO, admin submission queue address=0x%"PRIx64""
+nvme_mmio_acqaddr(uint64_t data) "wrote MMIO, admin completion queue address=0x%"PRIx64""
+nvme_mmio_asqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin submission queue high half=0x%"PRIx64", new_address=0x%"PRIx64""
+nvme_mmio_acqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin completion queue high half=0x%"PRIx64", new_address=0x%"PRIx64""
+nvme_mmio_start_success(void) "setting controller enable bit succeeded"
+nvme_mmio_stopped(void) "cleared controller enable bit"
+nvme_mmio_shutdown_set(void) "shutdown bit set"
+nvme_mmio_shutdown_cleared(void) "shutdown bit cleared"
+
+# nvme traces for error conditions
+nvme_err_invalid_dma(void) "PRP/SGL is too small for transfer size"
+nvme_err_invalid_prplist_ent(uint64_t prplist) "PRP list entry is null or not page aligned: 0x%"PRIx64""
+nvme_err_invalid_prp2_align(uint64_t prp2) "PRP2 is not page aligned: 0x%"PRIx64""
+nvme_err_invalid_prp2_missing(void) "PRP2 is null and more data to be transferred"
+nvme_err_invalid_field(void) "invalid field"
+nvme_err_invalid_prp(void) "invalid PRP"
+nvme_err_invalid_sgl(void) "invalid SGL"
+nvme_err_invalid_ns(uint32_t ns, uint32_t limit) "invalid namespace %u not within 1-%u"
+nvme_err_invalid_opc(uint8_t opc) "invalid opcode 0x%"PRIx8""
+nvme_err_invalid_admin_opc(uint8_t opc) "invalid admin opcode 0x%"PRIx8""
+nvme_err_invalid_lba_range(uint64_t start, uint64_t len, uint64_t limit) "Invalid LBA start=%"PRIu64" len=%"PRIu64" limit=%"PRIu64""
+nvme_err_invalid_del_sq(uint16_t qid) "invalid submission queue deletion, sid=%"PRIu16""
+nvme_err_invalid_create_sq_cqid(uint16_t cqid) "failed creating submission queue, invalid cqid=%"PRIu16""
+nvme_err_invalid_create_sq_sqid(uint16_t sqid) "failed creating submission queue, invalid sqid=%"PRIu16""
+nvme_err_invalid_create_sq_size(uint16_t qsize) "failed creating submission queue, invalid qsize=%"PRIu16""
+nvme_err_invalid_create_sq_addr(uint64_t addr) "failed creating submission queue, addr=0x%"PRIx64""
+nvme_err_invalid_create_sq_qflags(uint16_t qflags) "failed creating submission queue, qflags=%"PRIu16""
+nvme_err_invalid_del_cq_cqid(uint16_t cqid) "failed deleting completion queue, cqid=%"PRIu16""
+nvme_err_invalid_del_cq_notempty(uint16_t cqid) "failed deleting completion queue, it is not empty, cqid=%"PRIu16""
+nvme_err_invalid_create_cq_cqid(uint16_t cqid) "failed creating completion queue, cqid=%"PRIu16""
+nvme_err_invalid_create_cq_size(uint16_t size) "failed creating completion queue, size=%"PRIu16""
+nvme_err_invalid_create_cq_addr(uint64_t addr) "failed creating completion queue, addr=0x%"PRIx64""
+nvme_err_invalid_create_cq_vector(uint16_t vector) "failed creating completion queue, vector=%"PRIu16""
+nvme_err_invalid_create_cq_qflags(uint16_t qflags) "failed creating completion queue, qflags=%"PRIu16""
+nvme_err_invalid_identify_cns(uint16_t cns) "identify, invalid cns=0x%"PRIx16""
+nvme_err_invalid_getfeat(int dw10) "invalid get features, dw10=0x%"PRIx32""
+nvme_err_invalid_setfeat(uint32_t dw10) "invalid set features, dw10=0x%"PRIx32""
+nvme_err_startfail_cq(void) "nvme_start_ctrl failed because there are non-admin completion queues"
+nvme_err_startfail_sq(void) "nvme_start_ctrl failed because there are non-admin submission queues"
+nvme_err_startfail_nbarasq(void) "nvme_start_ctrl failed because the admin submission queue address is null"
+nvme_err_startfail_nbaracq(void) "nvme_start_ctrl failed because the admin completion queue address is null"
+nvme_err_startfail_asq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin submission queue address is misaligned: 0x%"PRIx64""
+nvme_err_startfail_acq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin completion queue address is misaligned: 0x%"PRIx64""
+nvme_err_startfail_page_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too small: log2size=%u, min=%u"
+nvme_err_startfail_page_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too large: log2size=%u, max=%u"
+nvme_err_startfail_cqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too small: log2size=%u, min=%u"
+nvme_err_startfail_cqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too large: log2size=%u, max=%u"
+nvme_err_startfail_sqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too small: log2size=%u, min=%u"
+nvme_err_startfail_sqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too large: log2size=%u, max=%u"
+nvme_err_startfail_asqent_sz_zero(void) "nvme_start_ctrl failed because the admin submission queue size is zero"
+nvme_err_startfail_acqent_sz_zero(void) "nvme_start_ctrl failed because the admin completion queue size is zero"
+nvme_err_startfail(void) "setting controller enable bit failed"
+
+# Traces for undefined behavior
+nvme_ub_mmiowr_misaligned32(uint64_t offset) "MMIO write not 32-bit aligned, offset=0x%"PRIx64""
+nvme_ub_mmiowr_toosmall(uint64_t offset, unsigned size) "MMIO write smaller than 32 bits, offset=0x%"PRIx64", size=%u"
+nvme_ub_mmiowr_intmask_with_msix(void) "undefined access to interrupt mask set when MSI-X is enabled"
+nvme_ub_mmiowr_ro_csts(void) "attempted to set a read only bit of controller status"
+nvme_ub_mmiowr_ssreset_w1c_unsupported(void) "attempted to W1C CSTS.NSSRO but CAP.NSSRS is zero (not supported)"
+nvme_ub_mmiowr_ssreset_unsupported(void) "attempted NVM subsystem reset but CAP.NSSRS is zero (not supported)"
+nvme_ub_mmiowr_cmbloc_reserved(void) "invalid write to reserved CMBLOC when CMBSZ is zero, ignored"
+nvme_ub_mmiowr_cmbsz_readonly(void) "invalid write to read only CMBSZ, ignored"
+nvme_ub_mmiowr_invalid(uint64_t offset, uint64_t data) "invalid MMIO write, offset=0x%"PRIx64", data=0x%"PRIx64""
+nvme_ub_mmiord_misaligned32(uint64_t offset) "MMIO read not 32-bit aligned, offset=0x%"PRIx64""
+nvme_ub_mmiord_toosmall(uint64_t offset) "MMIO read smaller than 32-bits, offset=0x%"PRIx64""
+nvme_ub_mmiord_invalid_ofs(uint64_t offset) "MMIO read beyond last register, offset=0x%"PRIx64", returning 0"
+nvme_ub_db_wr_misaligned(uint64_t offset) "doorbell write not 32-bit aligned, offset=0x%"PRIx64", ignoring"
+nvme_ub_db_wr_invalid_cq(uint32_t qid) "completion queue doorbell write for nonexistent queue, cqid=%"PRIu32", ignoring"
+nvme_ub_db_wr_invalid_cqhead(uint32_t qid, uint16_t new_head) "completion queue doorbell write value beyond queue size, cqid=%"PRIu32", new_head=%"PRIu16", ignoring"
+nvme_ub_db_wr_invalid_sq(uint32_t qid) "submission queue doorbell write for nonexistent queue, sqid=%"PRIu32", ignoring"
+nvme_ub_db_wr_invalid_sqtail(uint32_t qid, uint16_t new_tail) "submission queue doorbell write value beyond queue size, sqid=%"PRIu32", new_head=%"PRIu16", ignoring"
+
# hw/block/xen_disk.c
xen_disk_alloc(char *name) "%s"
xen_disk_init(char *name) "%s"
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 5a6dee081c..18b939e469 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -2274,16 +2274,25 @@ build_tpm_tcpa(GArray *table_data, BIOSLinker *linker, GArray *tcpalog)
}
static void
-build_tpm2(GArray *table_data, BIOSLinker *linker)
+build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog)
{
- Acpi20TPM2 *tpm2_ptr;
-
- tpm2_ptr = acpi_data_push(table_data, sizeof *tpm2_ptr);
+ Acpi20TPM2 *tpm2_ptr = acpi_data_push(table_data, sizeof *tpm2_ptr);
+ unsigned log_addr_size = sizeof(tpm2_ptr->log_area_start_address);
+ unsigned log_addr_offset =
+ (char *)&tpm2_ptr->log_area_start_address - table_data->data;
tpm2_ptr->platform_class = cpu_to_le16(TPM2_ACPI_CLASS_CLIENT);
if (TPM_IS_TIS(tpm_find())) {
tpm2_ptr->control_area_address = cpu_to_le64(0);
tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_MMIO);
+
+ tpm2_ptr->log_area_minimum_length =
+ cpu_to_le32(TPM_LOG_AREA_MINIMUM_SIZE);
+
+ /* log area start address to be filled by Guest linker */
+ bios_linker_loader_add_pointer(linker,
+ ACPI_BUILD_TABLE_FILE, log_addr_offset, log_addr_size,
+ ACPI_BUILD_TPMLOG_FILE, 0);
} else {
g_warn_if_reached();
}
@@ -2695,7 +2704,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
if (misc.tpm_version == TPM_VERSION_2_0) {
acpi_add_table(table_offsets, tables_blob);
- build_tpm2(tables_blob, tables->linker);
+ build_tpm2(tables_blob, tables->linker, tables->tcpalog);
}
}
if (pcms->numa_nodes) {
diff --git a/hw/intc/apic.c b/hw/intc/apic.c
index fe15fb6024..6fda52b86c 100644
--- a/hw/intc/apic.c
+++ b/hw/intc/apic.c
@@ -305,6 +305,18 @@ static void apic_set_tpr(APICCommonState *s, uint8_t val)
}
}
+int apic_get_highest_priority_irr(DeviceState *dev)
+{
+ APICCommonState *s;
+
+ if (!dev) {
+ /* no interrupts */
+ return -1;
+ }
+ s = APIC_COMMON(dev);
+ return get_highest_priority_int(s->irr);
+}
+
static uint8_t apic_get_tpr(APICCommonState *s)
{
apic_sync_vapic(s, SYNC_FROM_VAPIC);
diff --git a/hw/net/e1000.c b/hw/net/e1000.c
index 05a00cba31..804ec08721 100644
--- a/hw/net/e1000.c
+++ b/hw/net/e1000.c
@@ -98,7 +98,10 @@ typedef struct E1000State_st {
unsigned char data[0x10000];
uint16_t size;
unsigned char vlan_needed;
+ unsigned char sum_needed;
+ bool cptse;
e1000x_txd_props props;
+ e1000x_txd_props tso_props;
uint16_t tso_frames;
} tx;
@@ -539,35 +542,37 @@ xmit_seg(E1000State *s)
uint16_t len;
unsigned int frames = s->tx.tso_frames, css, sofar;
struct e1000_tx *tp = &s->tx;
+ struct e1000x_txd_props *props = tp->cptse ? &tp->tso_props : &tp->props;
- if (tp->props.tse && tp->props.cptse) {
- css = tp->props.ipcss;
+ if (tp->cptse) {
+ css = props->ipcss;
DBGOUT(TXSUM, "frames %d size %d ipcss %d\n",
frames, tp->size, css);
- if (tp->props.ip) { /* IPv4 */
+ if (props->ip) { /* IPv4 */
stw_be_p(tp->data+css+2, tp->size - css);
stw_be_p(tp->data+css+4,
lduw_be_p(tp->data + css + 4) + frames);
} else { /* IPv6 */
stw_be_p(tp->data+css+4, tp->size - css);
}
- css = tp->props.tucss;
+ css = props->tucss;
len = tp->size - css;
- DBGOUT(TXSUM, "tcp %d tucss %d len %d\n", tp->props.tcp, css, len);
- if (tp->props.tcp) {
- sofar = frames * tp->props.mss;
+ DBGOUT(TXSUM, "tcp %d tucss %d len %d\n", props->tcp, css, len);
+ if (props->tcp) {
+ sofar = frames * props->mss;
stl_be_p(tp->data+css+4, ldl_be_p(tp->data+css+4)+sofar); /* seq */
- if (tp->props.paylen - sofar > tp->props.mss) {
+ if (props->paylen - sofar > props->mss) {
tp->data[css + 13] &= ~9; /* PSH, FIN */
} else if (frames) {
e1000x_inc_reg_if_not_full(s->mac_reg, TSCTC);
}
- } else /* UDP */
+ } else { /* UDP */
stw_be_p(tp->data+css+4, len);
- if (tp->props.sum_needed & E1000_TXD_POPTS_TXSM) {
+ }
+ if (tp->sum_needed & E1000_TXD_POPTS_TXSM) {
unsigned int phsum;
// add pseudo-header length before checksum calculation
- void *sp = tp->data + tp->props.tucso;
+ void *sp = tp->data + props->tucso;
phsum = lduw_be_p(sp) + len;
phsum = (phsum >> 16) + (phsum & 0xffff);
@@ -576,13 +581,11 @@ xmit_seg(E1000State *s)
tp->tso_frames++;
}
- if (tp->props.sum_needed & E1000_TXD_POPTS_TXSM) {
- putsum(tp->data, tp->size, tp->props.tucso,
- tp->props.tucss, tp->props.tucse);
+ if (tp->sum_needed & E1000_TXD_POPTS_TXSM) {
+ putsum(tp->data, tp->size, props->tucso, props->tucss, props->tucse);
}
- if (tp->props.sum_needed & E1000_TXD_POPTS_IXSM) {
- putsum(tp->data, tp->size, tp->props.ipcso,
- tp->props.ipcss, tp->props.ipcse);
+ if (tp->sum_needed & E1000_TXD_POPTS_IXSM) {
+ putsum(tp->data, tp->size, props->ipcso, props->ipcss, props->ipcse);
}
if (tp->vlan_needed) {
memmove(tp->vlan, tp->data, 4);
@@ -614,27 +617,27 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp)
s->mit_ide |= (txd_lower & E1000_TXD_CMD_IDE);
if (dtype == E1000_TXD_CMD_DEXT) { /* context descriptor */
- e1000x_read_tx_ctx_descr(xp, &tp->props);
- tp->tso_frames = 0;
- if (tp->props.tucso == 0) { /* this is probably wrong */
- DBGOUT(TXSUM, "TCP/UDP: cso 0!\n");
- tp->props.tucso = tp->props.tucss + (tp->props.tcp ? 16 : 6);
+ if (le32_to_cpu(xp->cmd_and_length) & E1000_TXD_CMD_TSE) {
+ e1000x_read_tx_ctx_descr(xp, &tp->tso_props);
+ tp->tso_frames = 0;
+ } else {
+ e1000x_read_tx_ctx_descr(xp, &tp->props);
}
return;
} else if (dtype == (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D)) {
// data descriptor
if (tp->size == 0) {
- tp->props.sum_needed = le32_to_cpu(dp->upper.data) >> 8;
+ tp->sum_needed = le32_to_cpu(dp->upper.data) >> 8;
}
- tp->props.cptse = (txd_lower & E1000_TXD_CMD_TSE) ? 1 : 0;
+ tp->cptse = (txd_lower & E1000_TXD_CMD_TSE) ? 1 : 0;
} else {
// legacy descriptor
- tp->props.cptse = 0;
+ tp->cptse = 0;
}
if (e1000x_vlan_enabled(s->mac_reg) &&
e1000x_is_vlan_txd(txd_lower) &&
- (tp->props.cptse || txd_lower & E1000_TXD_CMD_EOP)) {
+ (tp->cptse || txd_lower & E1000_TXD_CMD_EOP)) {
tp->vlan_needed = 1;
stw_be_p(tp->vlan_header,
le16_to_cpu(s->mac_reg[VET]));
@@ -643,8 +646,8 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp)
}
addr = le64_to_cpu(dp->buffer_addr);
- if (tp->props.tse && tp->props.cptse) {
- msh = tp->props.hdr_len + tp->props.mss;
+ if (tp->cptse) {
+ msh = tp->tso_props.hdr_len + tp->tso_props.mss;
do {
bytes = split_size;
if (tp->size + bytes > msh)
@@ -653,21 +656,19 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp)
bytes = MIN(sizeof(tp->data) - tp->size, bytes);
pci_dma_read(d, addr, tp->data + tp->size, bytes);
sz = tp->size + bytes;
- if (sz >= tp->props.hdr_len && tp->size < tp->props.hdr_len) {
- memmove(tp->header, tp->data, tp->props.hdr_len);
+ if (sz >= tp->tso_props.hdr_len
+ && tp->size < tp->tso_props.hdr_len) {
+ memmove(tp->header, tp->data, tp->tso_props.hdr_len);
}
tp->size = sz;
addr += bytes;
if (sz == msh) {
xmit_seg(s);
- memmove(tp->data, tp->header, tp->props.hdr_len);
- tp->size = tp->props.hdr_len;
+ memmove(tp->data, tp->header, tp->tso_props.hdr_len);
+ tp->size = tp->tso_props.hdr_len;
}
split_size -= bytes;
} while (bytes && split_size);
- } else if (!tp->props.tse && tp->props.cptse) {
- // context descriptor TSE is not set, while data descriptor TSE is set
- DBGOUT(TXERR, "TCP segmentation error\n");
} else {
split_size = MIN(sizeof(tp->data) - tp->size, split_size);
pci_dma_read(d, addr, tp->data + tp->size, split_size);
@@ -676,14 +677,14 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp)
if (!(txd_lower & E1000_TXD_CMD_EOP))
return;
- if (!(tp->props.tse && tp->props.cptse && tp->size < tp->props.hdr_len)) {
+ if (!(tp->cptse && tp->size < tp->tso_props.hdr_len)) {
xmit_seg(s);
}
tp->tso_frames = 0;
- tp->props.sum_needed = 0;
+ tp->sum_needed = 0;
tp->vlan_needed = 0;
tp->size = 0;
- tp->props.cptse = 0;
+ tp->cptse = 0;
}
static uint32_t
@@ -1435,7 +1436,7 @@ static const VMStateDescription vmstate_e1000_full_mac_state = {
static const VMStateDescription vmstate_e1000 = {
.name = "e1000",
- .version_id = 2,
+ .version_id = 3,
.minimum_version_id = 1,
.pre_save = e1000_pre_save,
.post_load = e1000_post_load,
@@ -1461,7 +1462,7 @@ static const VMStateDescription vmstate_e1000 = {
VMSTATE_UINT16(tx.props.mss, E1000State),
VMSTATE_UINT16(tx.size, E1000State),
VMSTATE_UINT16(tx.tso_frames, E1000State),
- VMSTATE_UINT8(tx.props.sum_needed, E1000State),
+ VMSTATE_UINT8(tx.sum_needed, E1000State),
VMSTATE_INT8(tx.props.ip, E1000State),
VMSTATE_INT8(tx.props.tcp, E1000State),
VMSTATE_BUFFER(tx.header, E1000State),
@@ -1508,6 +1509,17 @@ static const VMStateDescription vmstate_e1000 = {
VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, RA, 32),
VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, MTA, 128),
VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, VFTA, 128),
+ VMSTATE_UINT8_V(tx.tso_props.ipcss, E1000State, 3),
+ VMSTATE_UINT8_V(tx.tso_props.ipcso, E1000State, 3),
+ VMSTATE_UINT16_V(tx.tso_props.ipcse, E1000State, 3),
+ VMSTATE_UINT8_V(tx.tso_props.tucss, E1000State, 3),
+ VMSTATE_UINT8_V(tx.tso_props.tucso, E1000State, 3),
+ VMSTATE_UINT16_V(tx.tso_props.tucse, E1000State, 3),
+ VMSTATE_UINT32_V(tx.tso_props.paylen, E1000State, 3),
+ VMSTATE_UINT8_V(tx.tso_props.hdr_len, E1000State, 3),
+ VMSTATE_UINT16_V(tx.tso_props.mss, E1000State, 3),
+ VMSTATE_INT8_V(tx.tso_props.ip, E1000State, 3),
+ VMSTATE_INT8_V(tx.tso_props.tcp, E1000State, 3),
VMSTATE_END_OF_LIST()
},
.subsections = (const VMStateDescription*[]) {
diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c
index f1af279e8d..191398a3d5 100644
--- a/hw/net/e1000e.c
+++ b/hw/net/e1000e.c
@@ -556,7 +556,7 @@ static const VMStateDescription e1000e_vmstate_tx = {
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
- VMSTATE_UINT8(props.sum_needed, struct e1000e_tx),
+ VMSTATE_UINT8(sum_needed, struct e1000e_tx),
VMSTATE_UINT8(props.ipcss, struct e1000e_tx),
VMSTATE_UINT8(props.ipcso, struct e1000e_tx),
VMSTATE_UINT16(props.ipcse, struct e1000e_tx),
@@ -569,7 +569,7 @@ static const VMStateDescription e1000e_vmstate_tx = {
VMSTATE_INT8(props.ip, struct e1000e_tx),
VMSTATE_INT8(props.tcp, struct e1000e_tx),
VMSTATE_BOOL(props.tse, struct e1000e_tx),
- VMSTATE_BOOL(props.cptse, struct e1000e_tx),
+ VMSTATE_BOOL(cptse, struct e1000e_tx),
VMSTATE_BOOL(skip_cp, struct e1000e_tx),
VMSTATE_END_OF_LIST()
}
diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c
index 43a8d89955..c93c4661ed 100644
--- a/hw/net/e1000e_core.c
+++ b/hw/net/e1000e_core.c
@@ -632,18 +632,18 @@ e1000e_rss_parse_packet(E1000ECore *core,
static void
e1000e_setup_tx_offloads(E1000ECore *core, struct e1000e_tx *tx)
{
- if (tx->props.tse && tx->props.cptse) {
+ if (tx->props.tse && tx->cptse) {
net_tx_pkt_build_vheader(tx->tx_pkt, true, true, tx->props.mss);
net_tx_pkt_update_ip_checksums(tx->tx_pkt);
e1000x_inc_reg_if_not_full(core->mac, TSCTC);
return;
}
- if (tx->props.sum_needed & E1000_TXD_POPTS_TXSM) {
+ if (tx->sum_needed & E1000_TXD_POPTS_TXSM) {
net_tx_pkt_build_vheader(tx->tx_pkt, false, true, 0);
}
- if (tx->props.sum_needed & E1000_TXD_POPTS_IXSM) {
+ if (tx->sum_needed & E1000_TXD_POPTS_IXSM) {
net_tx_pkt_update_ip_hdr_checksum(tx->tx_pkt);
}
}
@@ -715,13 +715,13 @@ e1000e_process_tx_desc(E1000ECore *core,
return;
} else if (dtype == (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D)) {
/* data descriptor */
- tx->props.sum_needed = le32_to_cpu(dp->upper.data) >> 8;
- tx->props.cptse = (txd_lower & E1000_TXD_CMD_TSE) ? 1 : 0;
+ tx->sum_needed = le32_to_cpu(dp->upper.data) >> 8;
+ tx->cptse = (txd_lower & E1000_TXD_CMD_TSE) ? 1 : 0;
e1000e_process_ts_option(core, dp);
} else {
/* legacy descriptor */
e1000e_process_ts_option(core, dp);
- tx->props.cptse = 0;
+ tx->cptse = 0;
}
addr = le64_to_cpu(dp->buffer_addr);
@@ -747,8 +747,8 @@ e1000e_process_tx_desc(E1000ECore *core,
tx->skip_cp = false;
net_tx_pkt_reset(tx->tx_pkt);
- tx->props.sum_needed = 0;
- tx->props.cptse = 0;
+ tx->sum_needed = 0;
+ tx->cptse = 0;
}
}
diff --git a/hw/net/e1000e_core.h b/hw/net/e1000e_core.h
index 1ff6978ca1..7d8ff41890 100644
--- a/hw/net/e1000e_core.h
+++ b/hw/net/e1000e_core.h
@@ -71,6 +71,8 @@ struct E1000Core {
e1000x_txd_props props;
bool skip_cp;
+ unsigned char sum_needed;
+ bool cptse;
struct NetTxPkt *tx_pkt;
} tx[E1000E_NUM_QUEUES];
diff --git a/hw/net/e1000x_common.h b/hw/net/e1000x_common.h
index 3072ce9d50..0268884e72 100644
--- a/hw/net/e1000x_common.h
+++ b/hw/net/e1000x_common.h
@@ -193,7 +193,6 @@ void e1000x_update_regs_on_autoneg_done(uint32_t *mac, uint16_t *phy);
void e1000x_increase_size_stats(uint32_t *mac, const int *size_regs, int size);
typedef struct e1000x_txd_props {
- unsigned char sum_needed;
uint8_t ipcss;
uint8_t ipcso;
uint16_t ipcse;
@@ -206,7 +205,6 @@ typedef struct e1000x_txd_props {
int8_t ip;
int8_t tcp;
bool tse;
- bool cptse;
} e1000x_txd_props;
void e1000x_read_tx_ctx_descr(struct e1000_context_desc *d,
diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
index 1c0def555b..a07a63247e 100644
--- a/hw/net/eepro100.c
+++ b/hw/net/eepro100.c
@@ -44,6 +44,7 @@
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "net/net.h"
+#include "net/eth.h"
#include "hw/nvram/eeprom93xx.h"
#include "sysemu/sysemu.h"
#include "sysemu/dma.h"
@@ -323,32 +324,8 @@ static const uint16_t eepro100_mdi_mask[] = {
0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
};
-#define POLYNOMIAL 0x04c11db6
-
static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s);
-/* From FreeBSD (locally modified). */
-static unsigned e100_compute_mcast_idx(const uint8_t *ep)
-{
- uint32_t crc;
- int carry, i, j;
- uint8_t b;
-
- crc = 0xffffffff;
- for (i = 0; i < 6; i++) {
- b = *ep++;
- for (j = 0; j < 8; j++) {
- carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
- crc <<= 1;
- b >>= 1;
- if (carry) {
- crc = ((crc ^ POLYNOMIAL) | carry);
- }
- }
- }
- return (crc & BITS(7, 2)) >> 2;
-}
-
/* Read a 16 bit control/status (CSR) register. */
static uint16_t e100_read_reg2(EEPRO100State *s, E100RegisterOffset addr)
{
@@ -845,7 +822,8 @@ static void set_multicast_list(EEPRO100State *s)
uint8_t multicast_addr[6];
pci_dma_read(&s->dev, s->cb_address + 10 + i, multicast_addr, 6);
TRACE(OTHER, logout("multicast entry %s\n", nic_dump(multicast_addr, 6)));
- unsigned mcast_idx = e100_compute_mcast_idx(multicast_addr);
+ unsigned mcast_idx = (net_crc32(multicast_addr, ETH_ALEN) &
+ BITS(7, 2)) >> 2;
assert(mcast_idx < 64);
s->mult[mcast_idx >> 3] |= (1 << (mcast_idx & 7));
}
@@ -1681,7 +1659,7 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
if (s->configuration[21] & BIT(3)) {
/* Multicast all bit is set, receive all multicast frames. */
} else {
- unsigned mcast_idx = e100_compute_mcast_idx(buf);
+ unsigned mcast_idx = (net_crc32(buf, ETH_ALEN) & BITS(7, 2)) >> 2;
assert(mcast_idx < 64);
if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
/* Multicast frame is allowed in hash table. */
@@ -1701,7 +1679,7 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
rfd_status |= 0x0004;
} else if (s->configuration[20] & BIT(6)) {
/* Multiple IA bit set. */
- unsigned mcast_idx = compute_mcast_idx(buf);
+ unsigned mcast_idx = net_crc32(buf, ETH_ALEN) >> 26;
assert(mcast_idx < 64);
if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
TRACE(RXTX, logout("%p accepted, multiple IA bit set\n", s));
diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c
index 3c36ab9cec..704f452067 100644
--- a/hw/net/ftgmac100.c
+++ b/hw/net/ftgmac100.c
@@ -762,7 +762,7 @@ static int ftgmac100_filter(FTGMAC100State *s, const uint8_t *buf, size_t len)
}
/* TODO: this does not seem to work for ftgmac100 */
- mcast_idx = compute_mcast_idx(buf);
+ mcast_idx = net_crc32(buf, ETH_ALEN) >> 26;
if (!(s->math[mcast_idx / 32] & (1 << (mcast_idx % 32)))) {
return 0;
}
diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c
index 3db8937cac..b9032dac59 100644
--- a/hw/net/lan9118.c
+++ b/hw/net/lan9118.c
@@ -13,6 +13,7 @@
#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "net/net.h"
+#include "net/eth.h"
#include "hw/devices.h"
#include "sysemu/sysemu.h"
#include "hw/ptimer.h"
@@ -504,7 +505,7 @@ static int lan9118_filter(lan9118_state *s, const uint8_t *addr)
}
} else {
/* Hash matching */
- hash = compute_mcast_idx(addr);
+ hash = net_crc32(addr, ETH_ALEN) >> 26;
if (hash & 0x20) {
return (s->mac_hashh >> (hash & 0x1f)) & 1;
} else {
diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c
index 6874c8c6b9..687ef84aac 100644
--- a/hw/net/ne2000.c
+++ b/hw/net/ne2000.c
@@ -23,6 +23,8 @@
*/
#include "qemu/osdep.h"
#include "hw/pci/pci.h"
+#include "net/net.h"
+#include "net/eth.h"
#include "ne2000.h"
#include "hw/loader.h"
#include "sysemu/sysemu.h"
@@ -199,7 +201,7 @@ ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
/* multicast */
if (!(s->rxcr & 0x08))
return size;
- mcast_idx = compute_mcast_idx(buf);
+ mcast_idx = net_crc32(buf, ETH_ALEN) >> 26;
if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))))
return size;
} else if (s->mem[0] == buf[0] &&
diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c
index 268d6a7892..d42b79c08c 100644
--- a/hw/net/opencores_eth.c
+++ b/hw/net/opencores_eth.c
@@ -36,6 +36,7 @@
#include "hw/net/mii.h"
#include "hw/sysbus.h"
#include "net/net.h"
+#include "net/eth.h"
#include "sysemu/sysemu.h"
#include "trace.h"
@@ -373,7 +374,7 @@ static ssize_t open_eth_receive(NetClientState *nc,
if (memcmp(buf, bcast_addr, sizeof(bcast_addr)) == 0) {
miss = GET_REGBIT(s, MODER, BRO);
} else if ((buf[0] & 0x1) || GET_REGBIT(s, MODER, IAM)) {
- unsigned mcast_idx = compute_mcast_idx(buf);
+ unsigned mcast_idx = net_crc32(buf, ETH_ALEN) >> 26;
miss = !(s->regs[HASH0 + mcast_idx / 32] &
(1 << (mcast_idx % 32)));
trace_open_eth_receive_mcast(
diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c
index 654455355f..39d5d93525 100644
--- a/hw/net/pcnet.c
+++ b/hw/net/pcnet.c
@@ -38,6 +38,7 @@
#include "qemu/osdep.h"
#include "hw/qdev.h"
#include "net/net.h"
+#include "net/eth.h"
#include "qemu/timer.h"
#include "qemu/sockets.h"
#include "sysemu/sysemu.h"
@@ -522,25 +523,6 @@ static inline void pcnet_rmd_store(PCNetState *s, struct pcnet_RMD *rmd,
be16_to_cpu(hdr->ether_type)); \
} while (0)
-#define MULTICAST_FILTER_LEN 8
-
-static inline uint32_t lnc_mchash(const uint8_t *ether_addr)
-{
-#define LNC_POLYNOMIAL 0xEDB88320UL
- uint32_t crc = 0xFFFFFFFF;
- int idx, bit;
- uint8_t data;
-
- for (idx = 0; idx < 6; idx++) {
- for (data = *ether_addr++, bit = 0; bit < MULTICAST_FILTER_LEN; bit++) {
- crc = (crc >> 1) ^ (((crc ^ data) & 1) ? LNC_POLYNOMIAL : 0);
- data >>= 1;
- }
- }
- return crc;
-#undef LNC_POLYNOMIAL
-}
-
#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff])
/* generated using the AUTODIN II polynomial
@@ -656,7 +638,7 @@ static inline int ladr_match(PCNetState *s, const uint8_t *buf, int size)
s->csr[10] & 0xff, s->csr[10] >> 8,
s->csr[11] & 0xff, s->csr[11] >> 8
};
- int index = lnc_mchash(hdr->ether_dhost) >> 26;
+ int index = net_crc32_le(hdr->ether_dhost, ETH_ALEN) >> 26;
return !!(ladr[index >> 3] & (1 << (index & 7)));
}
return 0;
diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c
index a6b2a9f7a4..1cc95b8cba 100644
--- a/hw/net/rtl8139.c
+++ b/hw/net/rtl8139.c
@@ -882,7 +882,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t
return size;
}
- int mcast_idx = compute_mcast_idx(buf);
+ int mcast_idx = net_crc32(buf, ETH_ALEN) >> 26;
if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))))
{
diff --git a/hw/net/sungem.c b/hw/net/sungem.c
index 6aa8d1117b..60f1e479f3 100644
--- a/hw/net/sungem.c
+++ b/hw/net/sungem.c
@@ -11,12 +11,11 @@
#include "hw/pci/pci.h"
#include "qemu/log.h"
#include "net/net.h"
+#include "net/eth.h"
#include "net/checksum.h"
#include "hw/net/mii.h"
#include "sysemu/sysemu.h"
#include "trace.h"
-/* For crc32 */
-#include <zlib.h>
#define TYPE_SUNGEM "sungem"
@@ -595,7 +594,7 @@ static ssize_t sungem_receive(NetClientState *nc, const uint8_t *buf,
}
/* Get MAC crc */
- mac_crc = crc32(~0, buf, 6);
+ mac_crc = net_crc32_le(buf, ETH_ALEN);
/* Packet isn't for me ? */
rx_cond = sungem_check_rx_mac(s, buf, mac_crc);
diff --git a/hw/net/sunhme.c b/hw/net/sunhme.c
index b1efa1b88d..7558fca8f9 100644
--- a/hw/net/sunhme.c
+++ b/hw/net/sunhme.c
@@ -698,29 +698,6 @@ static inline void sunhme_set_rx_ring_nr(SunHMEState *s, int i)
s->erxregs[HME_ERXI_RING >> 2] = ring;
}
-#define POLYNOMIAL_LE 0xedb88320
-static uint32_t sunhme_crc32_le(const uint8_t *p, int len)
-{
- uint32_t crc;
- int carry, i, j;
- uint8_t b;
-
- crc = 0xffffffff;
- for (i = 0; i < len; i++) {
- b = *p++;
- for (j = 0; j < 8; j++) {
- carry = (crc & 0x1) ^ (b & 0x01);
- crc >>= 1;
- b >>= 1;
- if (carry) {
- crc = crc ^ POLYNOMIAL_LE;
- }
- }
- }
-
- return crc;
-}
-
#define MIN_BUF_SIZE 60
static ssize_t sunhme_receive(NetClientState *nc, const uint8_t *buf,
@@ -761,7 +738,7 @@ static ssize_t sunhme_receive(NetClientState *nc, const uint8_t *buf,
trace_sunhme_rx_filter_bcast_match();
} else if (s->macregs[HME_MACI_RXCFG >> 2] & HME_MAC_RXCFG_HENABLE) {
/* Didn't match local address, check hash filter */
- int mcast_idx = sunhme_crc32_le(buf, 6) >> 26;
+ int mcast_idx = net_crc32_le(buf, ETH_ALEN) >> 26;
if (!(s->macregs[(HME_MACI_HASHTAB0 >> 2) - (mcast_idx >> 4)] &
(1 << (mcast_idx & 0xf)))) {
/* Didn't match hash filter */
diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c
index 38b6f175a1..35c78de5a9 100644
--- a/hw/tpm/tpm_emulator.c
+++ b/hw/tpm/tpm_emulator.c
@@ -72,6 +72,9 @@ typedef struct TPMEmulator {
Error *migration_blocker;
QemuMutex mutex;
+
+ unsigned int established_flag:1;
+ unsigned int established_flag_cached:1;
} TPMEmulator;
@@ -349,16 +352,22 @@ static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
ptm_est est;
- DPRINTF("%s", __func__);
+ if (tpm_emu->established_flag_cached) {
+ return tpm_emu->established_flag;
+ }
+
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_TPMESTABLISHED, &est,
0, sizeof(est)) < 0) {
error_report("tpm-emulator: Could not get the TPM established flag: %s",
strerror(errno));
return false;
}
- DPRINTF("established flag: %0x", est.u.resp.bit);
+ DPRINTF("got established flag: %0x", est.u.resp.bit);
+
+ tpm_emu->established_flag_cached = 1;
+ tpm_emu->established_flag = (est.u.resp.bit != 0);
- return (est.u.resp.bit != 0);
+ return tpm_emu->established_flag;
}
static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb,
@@ -389,6 +398,8 @@ static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb,
return -1;
}
+ tpm_emu->established_flag_cached = 0;
+
return 0;
}
diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c
index b8e811b086..561384cd86 100644
--- a/hw/tpm/tpm_tis.c
+++ b/hw/tpm/tpm_tis.c
@@ -48,11 +48,6 @@ typedef enum {
TPM_TIS_STATE_RECEPTION,
} TPMTISState;
-typedef struct TPMSizedBuffer {
- uint32_t size;
- uint8_t *buffer;
-} TPMSizedBuffer;
-
/* locality data -- all fields are persisted */
typedef struct TPMLocality {
TPMTISState state;
@@ -61,19 +56,14 @@ typedef struct TPMLocality {
uint32_t iface_id;
uint32_t inte;
uint32_t ints;
-
- uint16_t w_offset;
- uint16_t r_offset;
- TPMSizedBuffer w_buffer;
- TPMSizedBuffer r_buffer;
} TPMLocality;
typedef struct TPMState {
ISADevice busdev;
MemoryRegion mmio;
- uint32_t offset;
- uint8_t buf[TPM_TIS_BUFFER_MAX];
+ unsigned char buffer[TPM_TIS_BUFFER_MAX];
+ uint16_t rw_offset;
uint8_t active_locty;
uint8_t aborting_locty;
@@ -215,23 +205,19 @@ static uint8_t tpm_tis_locality_from_addr(hwaddr addr)
return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7);
}
-static uint32_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb)
-{
- return tpm_cmd_get_size(sb->buffer);
-}
-
-static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
+static void tpm_tis_show_buffer(const unsigned char *buffer,
+ size_t buffer_size, const char *string)
{
#ifdef DEBUG_TIS
uint32_t len, i;
- len = tpm_tis_get_size_from_buffer(sb);
+ len = MIN(tpm_cmd_get_size(buffer), buffer_size);
DPRINTF("tpm_tis: %s length = %d\n", string, len);
for (i = 0; i < len; i++) {
if (i && !(i % 16)) {
DPRINTF("\n");
}
- DPRINTF("%.2X ", sb->buffer[i]);
+ DPRINTF("%.2X ", buffer[i]);
}
DPRINTF("\n");
#endif
@@ -261,22 +247,21 @@ static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags)
*/
static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
{
- TPMLocality *locty_data = &s->loc[locty];
-
- tpm_tis_show_buffer(&s->loc[locty].w_buffer, "tpm_tis: To TPM");
+ tpm_tis_show_buffer(s->buffer, s->be_buffer_size,
+ "tpm_tis: To TPM");
/*
- * w_offset serves as length indicator for length of data;
+ * rw_offset serves as length indicator for length of data;
* it's reset when the response comes back
*/
s->loc[locty].state = TPM_TIS_STATE_EXECUTION;
s->cmd = (TPMBackendCmd) {
.locty = locty,
- .in = locty_data->w_buffer.buffer,
- .in_len = locty_data->w_offset,
- .out = locty_data->r_buffer.buffer,
- .out_len = locty_data->r_buffer.size
+ .in = s->buffer,
+ .in_len = s->rw_offset,
+ .out = s->buffer,
+ .out_len = s->be_buffer_size,
};
tpm_backend_deliver_request(s->be_driver, &s->cmd);
@@ -356,8 +341,7 @@ static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
/* abort -- this function switches the locality */
static void tpm_tis_abort(TPMState *s, uint8_t locty)
{
- s->loc[locty].r_offset = 0;
- s->loc[locty].w_offset = 0;
+ s->rw_offset = 0;
DPRINTF("tpm_tis: tis_abort: new active locality is %d\n", s->next_locty);
@@ -424,10 +408,10 @@ static void tpm_tis_request_completed(TPMIf *ti)
tpm_tis_sts_set(&s->loc[locty],
TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE);
s->loc[locty].state = TPM_TIS_STATE_COMPLETION;
- s->loc[locty].r_offset = 0;
- s->loc[locty].w_offset = 0;
+ s->rw_offset = 0;
- tpm_tis_show_buffer(&s->loc[locty].r_buffer, "tpm_tis: From TPM");
+ tpm_tis_show_buffer(s->buffer, s->be_buffer_size,
+ "tpm_tis: From TPM");
if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) {
tpm_tis_abort(s, locty);
@@ -446,16 +430,17 @@ static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
uint16_t len;
if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
- len = tpm_tis_get_size_from_buffer(&s->loc[locty].r_buffer);
+ len = MIN(tpm_cmd_get_size(&s->buffer),
+ s->be_buffer_size);
- ret = s->loc[locty].r_buffer.buffer[s->loc[locty].r_offset++];
- if (s->loc[locty].r_offset >= len) {
+ ret = s->buffer[s->rw_offset++];
+ if (s->rw_offset >= len) {
/* got last byte */
tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
}
DPRINTF("tpm_tis: tpm_tis_data_read byte 0x%02x [%d]\n",
- ret, s->loc[locty].r_offset - 1);
+ ret, s->rw_offset - 1);
}
return ret;
@@ -490,27 +475,15 @@ static void tpm_tis_dump_state(void *opaque, hwaddr addr)
(int)tpm_tis_mmio_read(opaque, base + regs[idx], 4));
}
- DPRINTF("tpm_tis: read offset : %d\n"
+ DPRINTF("tpm_tis: r/w offset : %d\n"
"tpm_tis: result buffer : ",
- s->loc[locty].r_offset);
+ s->rw_offset);
for (idx = 0;
- idx < tpm_tis_get_size_from_buffer(&s->loc[locty].r_buffer);
+ idx < MIN(tpm_cmd_get_size(&s->buffer), s->be_buffer_size);
idx++) {
DPRINTF("%c%02x%s",
- s->loc[locty].r_offset == idx ? '>' : ' ',
- s->loc[locty].r_buffer.buffer[idx],
- ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : "");
- }
- DPRINTF("\n"
- "tpm_tis: write offset : %d\n"
- "tpm_tis: request buffer: ",
- s->loc[locty].w_offset);
- for (idx = 0;
- idx < tpm_tis_get_size_from_buffer(&s->loc[locty].w_buffer);
- idx++) {
- DPRINTF("%c%02x%s",
- s->loc[locty].w_offset == idx ? '>' : ' ',
- s->loc[locty].w_buffer.buffer[idx],
+ s->rw_offset == idx ? '>' : ' ',
+ s->buffer[idx],
((idx & 0xf) == 0xf) ? "\ntpm_tis: " : "");
}
DPRINTF("\n");
@@ -572,11 +545,11 @@ static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
if (s->active_locty == locty) {
if ((s->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) {
val = TPM_TIS_BURST_COUNT(
- tpm_tis_get_size_from_buffer(&s->loc[locty].r_buffer)
- - s->loc[locty].r_offset) | s->loc[locty].sts;
+ MIN(tpm_cmd_get_size(&s->buffer),
+ s->be_buffer_size)
+ - s->rw_offset) | s->loc[locty].sts;
} else {
- avail = s->loc[locty].w_buffer.size
- - s->loc[locty].w_offset;
+ avail = s->be_buffer_size - s->rw_offset;
/*
* byte-sized reads should not return 0x00 for 0x100
* available bytes.
@@ -840,8 +813,7 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
switch (s->loc[locty].state) {
case TPM_TIS_STATE_READY:
- s->loc[locty].w_offset = 0;
- s->loc[locty].r_offset = 0;
+ s->rw_offset = 0;
break;
case TPM_TIS_STATE_IDLE:
@@ -859,8 +831,7 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
break;
case TPM_TIS_STATE_COMPLETION:
- s->loc[locty].w_offset = 0;
- s->loc[locty].r_offset = 0;
+ s->rw_offset = 0;
/* shortcut to ready state with C/R set */
s->loc[locty].state = TPM_TIS_STATE_READY;
if (!(s->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) {
@@ -886,7 +857,7 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
} else if (val == TPM_TIS_STS_RESPONSE_RETRY) {
switch (s->loc[locty].state) {
case TPM_TIS_STATE_COMPLETION:
- s->loc[locty].r_offset = 0;
+ s->rw_offset = 0;
tpm_tis_sts_set(&s->loc[locty],
TPM_TIS_STS_VALID|
TPM_TIS_STS_DATA_AVAILABLE);
@@ -924,9 +895,9 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
}
while ((s->loc[locty].sts & TPM_TIS_STS_EXPECT) && size > 0) {
- if (s->loc[locty].w_offset < s->loc[locty].w_buffer.size) {
- s->loc[locty].w_buffer.
- buffer[s->loc[locty].w_offset++] = (uint8_t)val;
+ if (s->rw_offset < s->be_buffer_size) {
+ s->buffer[s->rw_offset++] =
+ (uint8_t)val;
val >>= 8;
size--;
} else {
@@ -935,13 +906,13 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
}
/* check for complete packet */
- if (s->loc[locty].w_offset > 5 &&
+ if (s->rw_offset > 5 &&
(s->loc[locty].sts & TPM_TIS_STS_EXPECT)) {
/* we have a packet length - see if we have all of it */
bool need_irq = !(s->loc[locty].sts & TPM_TIS_STS_VALID);
- len = tpm_tis_get_size_from_buffer(&s->loc[locty].w_buffer);
- if (len > s->loc[locty].w_offset) {
+ len = tpm_cmd_get_size(&s->buffer);
+ if (len > s->rw_offset) {
tpm_tis_sts_set(&s->loc[locty],
TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID);
} else {
@@ -974,20 +945,11 @@ static const MemoryRegionOps tpm_tis_memory_ops = {
},
};
-static int tpm_tis_do_startup_tpm(TPMState *s, uint32_t buffersize)
+static int tpm_tis_do_startup_tpm(TPMState *s, size_t buffersize)
{
return tpm_backend_startup_tpm(s->be_driver, buffersize);
}
-static void tpm_tis_realloc_buffer(TPMSizedBuffer *sb,
- size_t wanted_size)
-{
- if (sb->size != wanted_size) {
- sb->buffer = g_realloc(sb->buffer, wanted_size);
- sb->size = wanted_size;
- }
-}
-
/*
* Get the TPMVersion of the backend device being used
*/
@@ -1012,7 +974,8 @@ static void tpm_tis_reset(DeviceState *dev)
int c;
s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver);
- s->be_buffer_size = tpm_backend_get_buffer_size(s->be_driver);
+ s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver),
+ TPM_TIS_BUFFER_MAX);
tpm_backend_reset(s->be_driver);
@@ -1038,13 +1001,10 @@ static void tpm_tis_reset(DeviceState *dev)
s->loc[c].ints = 0;
s->loc[c].state = TPM_TIS_STATE_IDLE;
- s->loc[c].w_offset = 0;
- tpm_tis_realloc_buffer(&s->loc[c].w_buffer, s->be_buffer_size);
- s->loc[c].r_offset = 0;
- tpm_tis_realloc_buffer(&s->loc[c].r_buffer, s->be_buffer_size);
+ s->rw_offset = 0;
}
- tpm_tis_do_startup_tpm(s, 0);
+ tpm_tis_do_startup_tpm(s, s->be_buffer_size);
}
static const VMStateDescription vmstate_tpm_tis = {
diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c
index 17cafbe6b3..747075e244 100644
--- a/hw/tpm/tpm_util.c
+++ b/hw/tpm/tpm_util.c
@@ -355,3 +355,10 @@ int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
return 0;
}
+
+void tpm_sized_buffer_reset(TPMSizedBuffer *tsb)
+{
+ g_free(tsb->buffer);
+ tsb->buffer = NULL;
+ tsb->size = 0;
+}
diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h
index 2393b6bc0e..19b28474ae 100644
--- a/hw/tpm/tpm_util.h
+++ b/hw/tpm/tpm_util.h
@@ -42,4 +42,11 @@ int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
#define DEFINE_PROP_TPMBE(_n, _s, _f) \
DEFINE_PROP(_n, _s, _f, qdev_prop_tpm, TPMBackend *)
+typedef struct TPMSizedBuffer {
+ uint32_t size;
+ uint8_t *buffer;
+} TPMSizedBuffer;
+
+void tpm_sized_buffer_reset(TPMSizedBuffer *tsb);
+
#endif /* TPM_TPM_UTIL_H */
diff --git a/include/block/block.h b/include/block/block.h
index c05cac57e5..9b12774ddf 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -585,7 +585,7 @@ void bdrv_io_unplug(BlockDriverState *bs);
* Begin a quiesced section of all users of @bs. This is part of
* bdrv_drained_begin.
*/
-void bdrv_parent_drained_begin(BlockDriverState *bs);
+void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore);
/**
* bdrv_parent_drained_end:
@@ -593,7 +593,7 @@ void bdrv_parent_drained_begin(BlockDriverState *bs);
* End a quiesced section of all users of @bs. This is part of
* bdrv_drained_end.
*/
-void bdrv_parent_drained_end(BlockDriverState *bs);
+void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);
/**
* bdrv_drained_begin:
@@ -608,12 +608,23 @@ void bdrv_parent_drained_end(BlockDriverState *bs);
void bdrv_drained_begin(BlockDriverState *bs);
/**
+ * Like bdrv_drained_begin, but recursively begins a quiesced section for
+ * exclusive access to all child nodes as well.
+ */
+void bdrv_subtree_drained_begin(BlockDriverState *bs);
+
+/**
* bdrv_drained_end:
*
* End a quiescent section started by bdrv_drained_begin().
*/
void bdrv_drained_end(BlockDriverState *bs);
+/**
+ * End a quiescent section started by bdrv_subtree_drained_begin().
+ */
+void bdrv_subtree_drained_end(BlockDriverState *bs);
+
void bdrv_add_child(BlockDriverState *parent, BlockDriverState *child,
Error **errp);
void bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp);
diff --git a/include/block/block_int.h b/include/block/block_int.h
index a5482775ec..29cafa4236 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -717,6 +717,8 @@ struct BlockDriverState {
/* Accessed with atomic ops. */
int quiesce_counter;
+ int recursive_quiesce_counter;
+
unsigned int write_gen; /* Current data generation */
/* Protected by reqs_lock. */
@@ -768,6 +770,9 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
BdrvRequestFlags flags);
+void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent);
+void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent);
+
int get_tmp_filename(char *filename, int size);
BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size,
const char *filename);
@@ -1045,7 +1050,6 @@ bool blk_dev_is_tray_open(BlockBackend *blk);
bool blk_dev_is_medium_locked(BlockBackend *blk);
void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes);
-bool bdrv_requests_pending(BlockDriverState *bs);
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out);
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in);
diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h
index 72be675dd6..80c8099a23 100644
--- a/include/hw/acpi/acpi-defs.h
+++ b/include/hw/acpi/acpi-defs.h
@@ -558,8 +558,8 @@ typedef struct Acpi20Tcpa Acpi20Tcpa;
/*
* TPM2
*
- * Following Level 00, Rev 00.37 of specs:
- * http://www.trustedcomputinggroup.org/resources/tcg_acpi_specification
+ * Following Version 1.2, Revision 8 of specs:
+ * https://trustedcomputinggroup.org/tcg-acpi-specification/
*/
struct Acpi20TPM2 {
ACPI_TABLE_HEADER_DEF
@@ -567,6 +567,9 @@ struct Acpi20TPM2 {
uint16_t reserved;
uint64_t control_area_address;
uint32_t start_method;
+ uint8_t start_method_params[12];
+ uint32_t log_area_minimum_length;
+ uint64_t log_area_start_address;
} QEMU_PACKED;
typedef struct Acpi20TPM2 Acpi20TPM2;
diff --git a/include/hw/i386/apic.h b/include/hw/i386/apic.h
index ea48ea9389..a9f6c0aa33 100644
--- a/include/hw/i386/apic.h
+++ b/include/hw/i386/apic.h
@@ -20,6 +20,7 @@ void apic_init_reset(DeviceState *s);
void apic_sipi(DeviceState *s);
void apic_poll_irq(DeviceState *d);
void apic_designate_bsp(DeviceState *d, bool bsp);
+int apic_get_highest_priority_irr(DeviceState *dev);
/* pc.c */
DeviceState *cpu_get_current_apic(void);
diff --git a/include/net/net.h b/include/net/net.h
index 1c55a93588..4afac1a9dd 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -227,7 +227,10 @@ NetClientState *net_hub_port_find(int hub_id);
void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd);
-#define POLYNOMIAL 0x04c11db6
+#define POLYNOMIAL_BE 0x04c11db6
+#define POLYNOMIAL_LE 0xedb88320
+uint32_t net_crc32(const uint8_t *p, int len);
+uint32_t net_crc32_le(const uint8_t *p, int len);
unsigned compute_mcast_idx(const uint8_t *ep);
#define vmstate_offset_macaddr(_state, _field) \
diff --git a/include/net/slirp.h b/include/net/slirp.h
index 64b795cda9..0c98e463db 100644
--- a/include/net/slirp.h
+++ b/include/net/slirp.h
@@ -36,8 +36,6 @@ void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict);
int net_slirp_redir(const char *redir_str);
-int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret);
-
int net_slirp_smb(const char *exported_dir);
void hmp_info_usernet(Monitor *mon, const QDict *qdict);
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
index 3dbc69b1e9..9bd7a834ba 100644
--- a/include/qemu/typedefs.h
+++ b/include/qemu/typedefs.h
@@ -36,6 +36,7 @@ typedef struct FWCfgIoState FWCfgIoState;
typedef struct FWCfgMemState FWCfgMemState;
typedef struct FWCfgState FWCfgState;
typedef struct HCIInfo HCIInfo;
+typedef struct HVFX86EmulatorState HVFX86EmulatorState;
typedef struct I2CBus I2CBus;
typedef struct I2SCodec I2SCodec;
typedef struct ISABus ISABus;
diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index c2fa151228..93bd546879 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -423,6 +423,8 @@ struct CPUState {
* unnecessary flushes.
*/
uint16_t pending_tlb_flush;
+
+ int hvf_fd;
};
QTAILQ_HEAD(CPUTailQ, CPUState);
diff --git a/include/sysemu/hvf.h b/include/sysemu/hvf.h
new file mode 100644
index 0000000000..e4e43f6468
--- /dev/null
+++ b/include/sysemu/hvf.h
@@ -0,0 +1,107 @@
+/*
+ * QEMU Hypervisor.framework (HVF) support
+ *
+ * Copyright Google Inc., 2017
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+/* header to be included in non-HVF-specific code */
+#ifndef _HVF_H
+#define _HVF_H
+
+#include "config-host.h"
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/bitops.h"
+#include "exec/memory.h"
+#include "sysemu/accel.h"
+
+extern int hvf_disabled;
+#ifdef CONFIG_HVF
+#include <Hypervisor/hv.h>
+#include <Hypervisor/hv_vmx.h>
+#include <Hypervisor/hv_error.h>
+#include "target/i386/cpu.h"
+#include "hw/hw.h"
+uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx,
+ int reg);
+#define hvf_enabled() !hvf_disabled
+#else
+#define hvf_enabled() 0
+#define hvf_get_supported_cpuid(func, idx, reg) 0
+#endif
+
+/* hvf_slot flags */
+#define HVF_SLOT_LOG (1 << 0)
+
+typedef struct hvf_slot {
+ uint64_t start;
+ uint64_t size;
+ uint8_t *mem;
+ int slot_id;
+ uint32_t flags;
+ MemoryRegion *region;
+} hvf_slot;
+
+typedef struct hvf_vcpu_caps {
+ uint64_t vmx_cap_pinbased;
+ uint64_t vmx_cap_procbased;
+ uint64_t vmx_cap_procbased2;
+ uint64_t vmx_cap_entry;
+ uint64_t vmx_cap_exit;
+ uint64_t vmx_cap_preemption_timer;
+} hvf_vcpu_caps;
+
+typedef struct HVFState {
+ AccelState parent;
+ hvf_slot slots[32];
+ int num_slots;
+
+ hvf_vcpu_caps *hvf_caps;
+} HVFState;
+extern HVFState *hvf_state;
+
+void hvf_set_phys_mem(MemoryRegionSection *, bool);
+void hvf_handle_io(CPUArchState *, uint16_t, void *,
+ int, int, int);
+hvf_slot *hvf_find_overlap_slot(uint64_t, uint64_t);
+
+/* Disable HVF if |disable| is 1, otherwise, enable it iff it is supported by
+ * the host CPU. Use hvf_enabled() after this to get the result. */
+void hvf_disable(int disable);
+
+/* Returns non-0 if the host CPU supports the VMX "unrestricted guest" feature
+ * which allows the virtual CPU to directly run in "real mode". If true, this
+ * allows QEMU to run several vCPU threads in parallel (see cpus.c). Otherwise,
+ * only a a single TCG thread can run, and it will call HVF to run the current
+ * instructions, except in case of "real mode" (paging disabled, typically at
+ * boot time), or MMIO operations. */
+
+int hvf_sync_vcpus(void);
+
+int hvf_init_vcpu(CPUState *);
+int hvf_vcpu_exec(CPUState *);
+int hvf_smp_cpu_exec(CPUState *);
+void hvf_cpu_synchronize_state(CPUState *);
+void hvf_cpu_synchronize_post_reset(CPUState *);
+void hvf_cpu_synchronize_post_init(CPUState *);
+void _hvf_cpu_synchronize_post_init(CPUState *, run_on_cpu_data);
+
+void hvf_vcpu_destroy(CPUState *);
+void hvf_raise_event(CPUState *);
+/* void hvf_reset_vcpu_state(void *opaque); */
+void hvf_reset_vcpu(CPUState *);
+void vmx_update_tpr(CPUState *);
+void update_apic_tpr(CPUState *);
+int hvf_put_registers(CPUState *);
+void vmx_clear_int_window_exiting(CPUState *cpu);
+
+#define TYPE_HVF_ACCEL ACCEL_CLASS_NAME("hvf")
+
+#define HVF_STATE(obj) \
+ OBJECT_CHECK(HVFState, (obj), TYPE_HVF_ACCEL)
+
+#endif
diff --git a/net/net.c b/net/net.c
index 39ef546708..2b81c93193 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1565,13 +1565,6 @@ int net_init_clients(void)
int net_client_parse(QemuOptsList *opts_list, const char *optarg)
{
-#if defined(CONFIG_SLIRP)
- int ret;
- if (net_slirp_parse_legacy(opts_list, optarg, &ret)) {
- return ret;
- }
-#endif
-
if (!qemu_opts_parse_noisily(opts_list, optarg, true)) {
return -1;
}
@@ -1581,25 +1574,48 @@ int net_client_parse(QemuOptsList *opts_list, const char *optarg)
/* From FreeBSD */
/* XXX: optimize */
-unsigned compute_mcast_idx(const uint8_t *ep)
+uint32_t net_crc32(const uint8_t *p, int len)
{
uint32_t crc;
int carry, i, j;
uint8_t b;
crc = 0xffffffff;
- for (i = 0; i < 6; i++) {
- b = *ep++;
+ for (i = 0; i < len; i++) {
+ b = *p++;
for (j = 0; j < 8; j++) {
carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
crc <<= 1;
b >>= 1;
if (carry) {
- crc = ((crc ^ POLYNOMIAL) | carry);
+ crc = ((crc ^ POLYNOMIAL_BE) | carry);
}
}
}
- return crc >> 26;
+
+ return crc;
+}
+
+uint32_t net_crc32_le(const uint8_t *p, int len)
+{
+ uint32_t crc;
+ int carry, i, j;
+ uint8_t b;
+
+ crc = 0xffffffff;
+ for (i = 0; i < len; i++) {
+ b = *p++;
+ for (j = 0; j < 8; j++) {
+ carry = (crc & 0x1) ^ (b & 0x01);
+ crc >>= 1;
+ b >>= 1;
+ if (carry) {
+ crc ^= POLYNOMIAL_LE;
+ }
+ }
+ }
+
+ return crc;
}
QemuOptsList qemu_netdev_opts = {
diff --git a/net/slirp.c b/net/slirp.c
index 318a26e892..cb8ca2312f 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -956,37 +956,3 @@ int net_init_slirp(const Netdev *netdev, const char *name,
return ret;
}
-
-int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret)
-{
- if (strcmp(opts_list->name, "net") != 0 ||
- strncmp(optarg, "channel,", strlen("channel,")) != 0) {
- return 0;
- }
-
- error_report("The '-net channel' option is deprecated. "
- "Please use '-netdev user,guestfwd=...' instead.");
-
- /* handle legacy -net channel,port:chr */
- optarg += strlen("channel,");
-
- if (QTAILQ_EMPTY(&slirp_stacks)) {
- struct slirp_config_str *config;
-
- config = g_malloc(sizeof(*config));
- pstrcpy(config->str, sizeof(config->str), optarg);
- config->flags = SLIRP_CFG_LEGACY;
- config->next = slirp_configs;
- slirp_configs = config;
- *ret = 0;
- } else {
- Error *err = NULL;
- *ret = slirp_guestfwd(QTAILQ_FIRST(&slirp_stacks), optarg, 1, &err);
- if (*ret < 0) {
- error_report_err(err);
- }
- }
-
- return 1;
-}
-
diff --git a/qapi/block-core.json b/qapi/block-core.json
index a8cdbc300b..e94a6881b2 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -3918,6 +3918,10 @@
# does not support all kinds of operations, all kinds of children, nor
# all block drivers.
#
+# FIXME Removing children from a quorum node means introducing gaps in the
+# child indices. This cannot be represented in the 'children' list of
+# BlockdevOptionsQuorum, as returned by .bdrv_refresh_filename().
+#
# Warning: The data in a new quorum child MUST be consistent with that of
# the rest of the array.
#
diff --git a/qemu-doc.texi b/qemu-doc.texi
index 90bea7331d..8d0c809ad5 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -2589,12 +2589,6 @@ deprecated.
@section System emulator command line arguments
-@subsection -drive boot=on|off (since 1.3.0)
-
-The ``boot=on|off'' option to the ``-drive'' argument is
-ignored. Applications should use the ``bootindex=N'' parameter
-to set an absolute ordering between devices instead.
-
@subsection -tdf (since 1.3.0)
The ``-tdf'' argument is ignored. The behaviour implemented
@@ -2648,32 +2642,36 @@ combined with ``-vnc tls-creds=tls0'
@subsection -tftp (since 2.6.0)
-The ``-tftp /some/dir'' argument is now a synonym for setting
-the ``-netdev user,tftp=/some/dir' argument. The new syntax
-allows different settings to be provided per NIC.
+The ``-tftp /some/dir'' argument is replaced by
+``-netdev user,id=x,tftp=/some/dir'', either accompanied with
+``-device ...,netdev=x'' (for pluggable NICs) or ``-net nic,netdev=x''
+(for embedded NICs). The new syntax allows different settings to be
+provided per NIC.
@subsection -bootp (since 2.6.0)
-The ``-bootp /some/file'' argument is now a synonym for setting
-the ``-netdev user,bootp=/some/file' argument. The new syntax
-allows different settings to be provided per NIC.
+The ``-bootp /some/file'' argument is replaced by
+``-netdev user,id=x,bootp=/some/file'', either accompanied with
+``-device ...,netdev=x'' (for pluggable NICs) or ``-net nic,netdev=x''
+(for embedded NICs). The new syntax allows different settings to be
+provided per NIC.
@subsection -redir (since 2.6.0)
-The ``-redir ARGS'' argument is now a synonym for setting
-the ``-netdev user,hostfwd=ARGS'' argument instead. The new
-syntax allows different settings to be provided per NIC.
+The ``-redir [tcp|udp]:hostport:[guestaddr]:guestport'' argument is
+replaced by ``-netdev
+user,id=x,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport'',
+either accompanied with ``-device ...,netdev=x'' (for pluggable NICs) or
+``-net nic,netdev=x'' (for embedded NICs). The new syntax allows different
+settings to be provided per NIC.
@subsection -smb (since 2.6.0)
-The ``-smb /some/dir'' argument is now a synonym for setting
-the ``-netdev user,smb=/some/dir'' argument instead. The new
-syntax allows different settings to be provided per NIC.
-
-@subsection -net channel (since 2.6.0)
-
-The ``--net channel,ARGS'' argument is now a synonym for setting
-the ``-netdev user,guestfwd=ARGS'' argument instead.
+The ``-smb /some/dir'' argument is replaced by
+``-netdev user,id=x,smb=/some/dir'', either accompanied with
+``-device ...,netdev=x'' (for pluggable NICs) or ``-net nic,netdev=x''
+(for embedded NICs). The new syntax allows different settings to be
+provided per NIC.
@subsection -net vlan (since 2.9.0)
@@ -2686,20 +2684,27 @@ longer be directly supported in QEMU.
The ``-drive if=scsi'' argument is replaced by the the
``-device BUS-TYPE'' argument combined with ``-drive if=none''.
+@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0)
+
+The drive geometry arguments are replaced by the the geometry arguments
+that can be specified with the ``-device'' parameter.
+
+@subsection -drive serial=... (since 2.10.0)
+
+The drive serial argument is replaced by the the serial argument
+that can be specified with the ``-device'' parameter.
+
+@subsection -drive addr=... (since 2.10.0)
+
+The drive addr argument is replaced by the the addr argument
+that can be specified with the ``-device'' parameter.
+
@subsection -net dump (since 2.10.0)
The ``--net dump'' argument is now replaced with the
``-object filter-dump'' argument which works in combination
with the modern ``-netdev`` backends instead.
-@subsection -hdachs (since 2.10.0)
-
-The ``-hdachs'' argument is now a synonym for setting
-the ``cyls'', ``heads'', ``secs'', and ``trans'' properties
-on the ``ide-hd'' device using the ``-device'' argument.
-The new syntax allows different settings to be provided
-per disk.
-
@subsection -usbdevice (since 2.10.0)
The ``-usbdevice DEV'' argument is now a synonym for setting
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
index de8e3de726..a6a70fc3dc 100644
--- a/qemu-io-cmds.c
+++ b/qemu-io-cmds.c
@@ -2013,8 +2013,11 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
opts = qopts ? qemu_opts_to_qdict(qopts, NULL) : NULL;
qemu_opts_reset(&reopen_opts);
+ bdrv_subtree_drained_begin(bs);
brq = bdrv_reopen_queue(NULL, bs, opts, flags);
bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err);
+ bdrv_subtree_drained_end(bs);
+
if (local_err) {
error_report_err(local_err);
} else {
diff --git a/qemu-options.hx b/qemu-options.hx
index 94647e21e3..678181c599 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -31,7 +31,7 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \
"-machine [type=]name[,prop[=value][,...]]\n"
" selects emulated machine ('-machine help' for list)\n"
" property accel=accel1[:accel2[:...]] selects accelerator\n"
- " supported accelerators are kvm, xen, hax or tcg (default: tcg)\n"
+ " supported accelerators are kvm, xen, hax, hvf or tcg (default: tcg)\n"
" kernel_irqchip=on|off|split controls accelerated irqchip support (default=off)\n"
" vmport=on|off|auto controls emulation of vmport (default: auto)\n"
" kvm_shadow_mem=size of KVM shadow MMU in bytes\n"
@@ -66,7 +66,7 @@ Supported machine properties are:
@table @option
@item accel=@var{accels1}[:@var{accels2}[:...]]
This is used to enable an accelerator. Depending on the target architecture,
-kvm, xen, hax or tcg can be available. By default, tcg is used. If there is
+kvm, xen, hax, hvf or tcg can be available. By default, tcg is used. If there is
more than one accelerator specified, the next one is used if the previous one
fails to initialize.
@item kernel_irqchip=on|off
@@ -126,13 +126,13 @@ ETEXI
DEF("accel", HAS_ARG, QEMU_OPTION_accel,
"-accel [accel=]accelerator[,thread=single|multi]\n"
- " select accelerator (kvm, xen, hax or tcg; use 'help' for a list)\n"
- " thread=single|multi (enable multi-threaded TCG)\n", QEMU_ARCH_ALL)
+ " select accelerator (kvm, xen, hax, hvf or tcg; use 'help' for a list)\n"
+ " thread=single|multi (enable multi-threaded TCG)", QEMU_ARCH_ALL)
STEXI
@item -accel @var{name}[,prop=@var{value}[,...]]
@findex -accel
This is used to enable an accelerator. Depending on the target architecture,
-kvm, xen, hax or tcg can be available. By default, tcg is used. If there is
+kvm, xen, hax, hvf or tcg can be available. By default, tcg is used. If there is
more than one accelerator specified, the next one is used if the previous one
fails to initialize.
@table @option
@@ -846,8 +846,8 @@ of available connectors of a given interface type.
@item media=@var{media}
This option defines the type of the media: disk or cdrom.
@item cyls=@var{c},heads=@var{h},secs=@var{s}[,trans=@var{t}]
-These options have the same definition as they have in @option{-hdachs}.
-These parameters are deprecated, use the corresponding parameters
+Force disk physical geometry and the optional BIOS translation (trans=none or
+lba). These parameters are deprecated, use the corresponding parameters
of @code{-device} instead.
@item snapshot=@var{snapshot}
@var{snapshot} is "on" or "off" and controls snapshot mode for the given drive
@@ -1027,21 +1027,6 @@ the raw disk image you use is not written back. You can however force
the write back by pressing @key{C-a s} (@pxref{disk_images}).
ETEXI
-DEF("hdachs", HAS_ARG, QEMU_OPTION_hdachs, \
- "-hdachs c,h,s[,t]\n" \
- " force hard disk 0 physical geometry and the optional BIOS\n" \
- " translation (t=none or lba) (usually QEMU can guess them)\n",
- QEMU_ARCH_ALL)
-STEXI
-@item -hdachs @var{c},@var{h},@var{s},[,@var{t}]
-@findex -hdachs
-Force hard disk 0 physical geometry (1 <= @var{c} <= 16383, 1 <=
-@var{h} <= 16, 1 <= @var{s} <= 63) and optionally force the BIOS
-translation mode (@var{t}=none, lba or auto). Usually QEMU can guess
-all those parameters. This option is deprecated, please use
-@code{-device ide-hd,cyls=c,heads=h,secs=s,...} instead.
-ETEXI
-
DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev,
"-fsdev fsdriver,id=id[,path=path,][security_model={mapped-xattr|mapped-file|passthrough|none}]\n"
" [,writeout=immediate][,readonly][,socket=socket|sock_fd=sock_fd][,fmode=fmode][,dmode=dmode]\n"
@@ -2035,9 +2020,10 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev,
"-netdev hubport,id=str,hubid=n\n"
" configure a hub port on QEMU VLAN 'n'\n", QEMU_ARCH_ALL)
DEF("net", HAS_ARG, QEMU_OPTION_net,
- "-net nic[,vlan=n][,macaddr=mac][,model=type][,name=str][,addr=str][,vectors=v]\n"
- " old way to create a new NIC and connect it to VLAN 'n'\n"
- " (use the '-device devtype,netdev=str' option if possible instead)\n"
+ "-net nic[,vlan=n][,netdev=nd][,macaddr=mac][,model=type][,name=str][,addr=str][,vectors=v]\n"
+ " configure or create an on-board (or machine default) NIC and\n"
+ " connect it either to VLAN 'n' or the netdev 'nd' (for pluggable\n"
+ " NICs please use '-device devtype,netdev=nd' instead)\n"
"-net dump[,vlan=n][,file=f][,len=n]\n"
" dump traffic on vlan 'n' to file 'f' (max n bytes per packet)\n"
"-net none use it alone to have zero network devices. If no -net option\n"
@@ -2058,10 +2044,11 @@ DEF("net", HAS_ARG, QEMU_OPTION_net,
" old way to initialize a host network interface\n"
" (use the -netdev option if possible instead)\n", QEMU_ARCH_ALL)
STEXI
-@item -net nic[,vlan=@var{n}][,macaddr=@var{mac}][,model=@var{type}] [,name=@var{name}][,addr=@var{addr}][,vectors=@var{v}]
+@item -net nic[,vlan=@var{n}][,netdev=@var{nd}][,macaddr=@var{mac}][,model=@var{type}] [,name=@var{name}][,addr=@var{addr}][,vectors=@var{v}]
@findex -net
-Create a new Network Interface Card and connect it to VLAN @var{n} (@var{n}
-= 0 is the default). The NIC is an e1000 by default on the PC
+Configure or create an on-board (or machine default) Network Interface Card
+(NIC) and connect it either to VLAN @var{n} (@var{n} = 0 is the default), or
+to the netdev @var{nd}. The NIC is an e1000 by default on the PC
target. Optionally, the MAC address can be changed to @var{mac}, the
device address set to @var{addr} (PCI cards only),
and a @var{name} can be assigned for use in monitor commands.
diff --git a/target/i386/Makefile.objs b/target/i386/Makefile.objs
index 6a26e9d9f0..44103a693b 100644
--- a/target/i386/Makefile.objs
+++ b/target/i386/Makefile.objs
@@ -12,4 +12,5 @@ obj-$(CONFIG_HAX) += hax-all.o hax-mem.o hax-windows.o
endif
ifdef CONFIG_DARWIN
obj-$(CONFIG_HAX) += hax-all.o hax-mem.o hax-darwin.o
+obj-$(CONFIG_HVF) += hvf/
endif
diff --git a/target/i386/cpu-qom.h b/target/i386/cpu-qom.h
index c2205e6077..22f95eb3a4 100644
--- a/target/i386/cpu-qom.h
+++ b/target/i386/cpu-qom.h
@@ -47,7 +47,7 @@ typedef struct X86CPUDefinition X86CPUDefinition;
/**
* X86CPUClass:
* @cpu_def: CPU model definition
- * @kvm_required: Whether CPU model requires KVM to be enabled.
+ * @host_cpuid_required: Whether CPU model requires cpuid from host.
* @ordering: Ordering on the "-cpu help" CPU model list.
* @migration_safe: See CpuDefinitionInfo::migration_safe
* @static_model: See CpuDefinitionInfo::static
@@ -66,7 +66,7 @@ typedef struct X86CPUClass {
*/
X86CPUDefinition *cpu_def;
- bool kvm_required;
+ bool host_cpuid_required;
int ordering;
bool migration_safe;
bool static_model;
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index b069eafcc6..3818d72831 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -22,6 +22,7 @@
#include "cpu.h"
#include "exec/exec-all.h"
#include "sysemu/kvm.h"
+#include "sysemu/hvf.h"
#include "sysemu/cpus.h"
#include "kvm_i386.h"
@@ -615,6 +616,11 @@ static uint32_t xsave_area_size(uint64_t mask)
return ret;
}
+static inline bool accel_uses_host_cpuid(void)
+{
+ return kvm_enabled() || hvf_enabled();
+}
+
static inline uint64_t x86_cpu_xsave_components(X86CPU *cpu)
{
return ((uint64_t)cpu->env.features[FEAT_XSAVE_COMP_HI]) << 32 |
@@ -1686,10 +1692,15 @@ static void max_x86_cpu_initfn(Object *obj)
*/
cpu->max_features = true;
- if (kvm_enabled()) {
+ if (accel_uses_host_cpuid()) {
char vendor[CPUID_VENDOR_SZ + 1] = { 0 };
char model_id[CPUID_MODEL_ID_SZ + 1] = { 0 };
int family, model, stepping;
+ X86CPUDefinition host_cpudef = { };
+ uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
+
+ host_cpuid(0x0, 0, &eax, &ebx, &ecx, &edx);
+ x86_cpu_vendor_words2str(host_cpudef.vendor, ebx, edx, ecx);
host_vendor_fms(vendor, &family, &model, &stepping);
@@ -1703,12 +1714,21 @@ static void max_x86_cpu_initfn(Object *obj)
object_property_set_str(OBJECT(cpu), model_id, "model-id",
&error_abort);
- env->cpuid_min_level =
- kvm_arch_get_supported_cpuid(s, 0x0, 0, R_EAX);
- env->cpuid_min_xlevel =
- kvm_arch_get_supported_cpuid(s, 0x80000000, 0, R_EAX);
- env->cpuid_min_xlevel2 =
- kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX);
+ if (kvm_enabled()) {
+ env->cpuid_min_level =
+ kvm_arch_get_supported_cpuid(s, 0x0, 0, R_EAX);
+ env->cpuid_min_xlevel =
+ kvm_arch_get_supported_cpuid(s, 0x80000000, 0, R_EAX);
+ env->cpuid_min_xlevel2 =
+ kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX);
+ } else {
+ env->cpuid_min_level =
+ hvf_get_supported_cpuid(0x0, 0, R_EAX);
+ env->cpuid_min_xlevel =
+ hvf_get_supported_cpuid(0x80000000, 0, R_EAX);
+ env->cpuid_min_xlevel2 =
+ hvf_get_supported_cpuid(0xC0000000, 0, R_EAX);
+ }
if (lmce_supported()) {
object_property_set_bool(OBJECT(cpu), true, "lmce", &error_abort);
@@ -1734,18 +1754,21 @@ static const TypeInfo max_x86_cpu_type_info = {
.class_init = max_x86_cpu_class_init,
};
-#ifdef CONFIG_KVM
-
+#if defined(CONFIG_KVM) || defined(CONFIG_HVF)
static void host_x86_cpu_class_init(ObjectClass *oc, void *data)
{
X86CPUClass *xcc = X86_CPU_CLASS(oc);
- xcc->kvm_required = true;
+ xcc->host_cpuid_required = true;
xcc->ordering = 8;
- xcc->model_description =
- "KVM processor with all supported host features "
- "(only available in KVM mode)";
+ if (kvm_enabled()) {
+ xcc->model_description =
+ "KVM processor with all supported host features ";
+ } else if (hvf_enabled()) {
+ xcc->model_description =
+ "HVF processor with all supported host features ";
+ }
}
static const TypeInfo host_x86_cpu_type_info = {
@@ -1767,7 +1790,7 @@ static void report_unavailable_features(FeatureWord w, uint32_t mask)
assert(reg);
warn_report("%s doesn't support requested feature: "
"CPUID.%02XH:%s%s%s [bit %d]",
- kvm_enabled() ? "host" : "TCG",
+ accel_uses_host_cpuid() ? "host" : "TCG",
f->cpuid_eax, reg,
f->feat_names[i] ? "." : "",
f->feat_names[i] ? f->feat_names[i] : "", i);
@@ -2218,7 +2241,7 @@ static void x86_cpu_class_check_missing_features(X86CPUClass *xcc,
Error *err = NULL;
strList **next = missing_feats;
- if (xcc->kvm_required && !kvm_enabled()) {
+ if (xcc->host_cpuid_required && !accel_uses_host_cpuid()) {
strList *new = g_new0(strList, 1);
new->value = g_strdup("kvm");
*missing_feats = new;
@@ -2380,6 +2403,10 @@ static uint32_t x86_cpu_get_supported_feature_word(FeatureWord w,
r = kvm_arch_get_supported_cpuid(kvm_state, wi->cpuid_eax,
wi->cpuid_ecx,
wi->cpuid_reg);
+ } else if (hvf_enabled()) {
+ r = hvf_get_supported_cpuid(wi->cpuid_eax,
+ wi->cpuid_ecx,
+ wi->cpuid_reg);
} else if (tcg_enabled()) {
r = wi->tcg_features;
} else {
@@ -2439,6 +2466,7 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp)
}
/* Special cases not set in the X86CPUDefinition structs: */
+ /* TODO: in-kernel irqchip for hvf */
if (kvm_enabled()) {
if (!kvm_irqchip_in_kernel()) {
x86_cpu_change_kvm_default("x2apic", "off");
@@ -2459,7 +2487,7 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp)
* when doing cross vendor migration
*/
vendor = def->vendor;
- if (kvm_enabled()) {
+ if (accel_uses_host_cpuid()) {
uint32_t ebx = 0, ecx = 0, edx = 0;
host_cpuid(0, 0, NULL, &ebx, &ecx, &edx);
x86_cpu_vendor_words2str(host_vendor, ebx, edx, ecx);
@@ -2910,6 +2938,11 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
*ebx = kvm_arch_get_supported_cpuid(s, 0xA, count, R_EBX);
*ecx = kvm_arch_get_supported_cpuid(s, 0xA, count, R_ECX);
*edx = kvm_arch_get_supported_cpuid(s, 0xA, count, R_EDX);
+ } else if (hvf_enabled() && cpu->enable_pmu) {
+ *eax = hvf_get_supported_cpuid(0xA, count, R_EAX);
+ *ebx = hvf_get_supported_cpuid(0xA, count, R_EBX);
+ *ecx = hvf_get_supported_cpuid(0xA, count, R_ECX);
+ *edx = hvf_get_supported_cpuid(0xA, count, R_EDX);
} else {
*eax = 0;
*ebx = 0;
@@ -3252,6 +3285,9 @@ static void x86_cpu_reset(CPUState *s)
memset(env->mtrr_var, 0, sizeof(env->mtrr_var));
memset(env->mtrr_fixed, 0, sizeof(env->mtrr_fixed));
+ env->interrupt_injected = -1;
+ env->exception_injected = -1;
+ env->nmi_injected = false;
#if !defined(CONFIG_USER_ONLY)
/* We hard-wire the BSP to the first CPU. */
apic_designate_bsp(cpu->apic_state, s->cpu_index == 0);
@@ -3261,6 +3297,9 @@ static void x86_cpu_reset(CPUState *s)
if (kvm_enabled()) {
kvm_arch_reset_vcpu(cpu);
}
+ else if (hvf_enabled()) {
+ hvf_reset_vcpu(s);
+ }
#endif
}
@@ -3300,6 +3339,7 @@ APICCommonClass *apic_get_class(void)
{
const char *apic_type = "apic";
+ /* TODO: in-kernel irqchip for hvf */
if (kvm_apic_in_kernel()) {
apic_type = "kvm-apic";
} else if (xen_enabled()) {
@@ -3613,7 +3653,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
Error *local_err = NULL;
static bool ht_warned;
- if (xcc->kvm_required && !kvm_enabled()) {
+ if (xcc->host_cpuid_required && !accel_uses_host_cpuid()) {
char *name = x86_cpu_class_get_model_name(xcc);
error_setg(&local_err, "CPU model '%s' requires KVM", name);
g_free(name);
@@ -3635,7 +3675,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
x86_cpu_report_filtered_features(cpu);
if (cpu->enforce_cpuid) {
error_setg(&local_err,
- kvm_enabled() ?
+ accel_uses_host_cpuid() ?
"Host doesn't support requested features" :
"TCG doesn't support requested features");
goto out;
@@ -3658,7 +3698,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
* consumer AMD devices but nothing else.
*/
if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) {
- if (kvm_enabled()) {
+ if (accel_uses_host_cpuid()) {
uint32_t host_phys_bits = x86_host_phys_bits();
static bool warned;
@@ -4272,7 +4312,7 @@ static void x86_cpu_register_types(void)
}
type_register_static(&max_x86_cpu_type_info);
type_register_static(&x86_base_cpu_type_info);
-#ifdef CONFIG_KVM
+#if defined(CONFIG_KVM) || defined(CONFIG_HVF)
type_register_static(&host_x86_cpu_type_info);
#endif
}
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index d605cc6ccb..62c4742703 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -30,6 +30,8 @@
#define TARGET_LONG_BITS 32
#endif
+#include "exec/cpu-defs.h"
+
/* The x86 has a strong memory model with some store-after-load re-ordering */
#define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD)
@@ -50,48 +52,64 @@
#define CPUArchState struct CPUX86State
-#include "exec/cpu-defs.h"
-
#ifdef CONFIG_TCG
#include "fpu/softfloat.h"
#endif
-#define R_EAX 0
-#define R_ECX 1
-#define R_EDX 2
-#define R_EBX 3
-#define R_ESP 4
-#define R_EBP 5
-#define R_ESI 6
-#define R_EDI 7
-
-#define R_AL 0
-#define R_CL 1
-#define R_DL 2
-#define R_BL 3
-#define R_AH 4
-#define R_CH 5
-#define R_DH 6
-#define R_BH 7
-
-#define R_ES 0
-#define R_CS 1
-#define R_SS 2
-#define R_DS 3
-#define R_FS 4
-#define R_GS 5
+enum {
+ R_EAX = 0,
+ R_ECX = 1,
+ R_EDX = 2,
+ R_EBX = 3,
+ R_ESP = 4,
+ R_EBP = 5,
+ R_ESI = 6,
+ R_EDI = 7,
+ R_R8 = 8,
+ R_R9 = 9,
+ R_R10 = 10,
+ R_R11 = 11,
+ R_R12 = 12,
+ R_R13 = 13,
+ R_R14 = 14,
+ R_R15 = 15,
+
+ R_AL = 0,
+ R_CL = 1,
+ R_DL = 2,
+ R_BL = 3,
+ R_AH = 4,
+ R_CH = 5,
+ R_DH = 6,
+ R_BH = 7,
+};
+
+typedef enum X86Seg {
+ R_ES = 0,
+ R_CS = 1,
+ R_SS = 2,
+ R_DS = 3,
+ R_FS = 4,
+ R_GS = 5,
+ R_LDTR = 6,
+ R_TR = 7,
+} X86Seg;
/* segment descriptor fields */
-#define DESC_G_MASK (1 << 23)
+#define DESC_G_SHIFT 23
+#define DESC_G_MASK (1 << DESC_G_SHIFT)
#define DESC_B_SHIFT 22
#define DESC_B_MASK (1 << DESC_B_SHIFT)
#define DESC_L_SHIFT 21 /* x86_64 only : 64 bit code segment */
#define DESC_L_MASK (1 << DESC_L_SHIFT)
-#define DESC_AVL_MASK (1 << 20)
-#define DESC_P_MASK (1 << 15)
+#define DESC_AVL_SHIFT 20
+#define DESC_AVL_MASK (1 << DESC_AVL_SHIFT)
+#define DESC_P_SHIFT 15
+#define DESC_P_MASK (1 << DESC_P_SHIFT)
#define DESC_DPL_SHIFT 13
#define DESC_DPL_MASK (3 << DESC_DPL_SHIFT)
-#define DESC_S_MASK (1 << 12)
+#define DESC_S_SHIFT 12
+#define DESC_S_MASK (1 << DESC_S_SHIFT)
#define DESC_TYPE_SHIFT 8
#define DESC_TYPE_MASK (15 << DESC_TYPE_SHIFT)
#define DESC_A_MASK (1 << 8)
@@ -631,6 +649,7 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS];
#define CPUID_7_0_EBX_AVX512BW (1U << 30) /* AVX-512 Byte and Word Instructions */
#define CPUID_7_0_EBX_AVX512VL (1U << 31) /* AVX-512 Vector Length Extensions */
+#define CPUID_7_0_ECX_AVX512BMI (1U << 1)
#define CPUID_7_0_ECX_VBMI (1U << 1) /* AVX-512 Vector Byte Manipulation Instrs */
#define CPUID_7_0_ECX_UMIP (1U << 2)
#define CPUID_7_0_ECX_PKU (1U << 3)
@@ -812,6 +831,20 @@ typedef struct SegmentCache {
float64 _d_##n[(bits)/64]; \
}
+typedef union {
+ uint8_t _b[16];
+ uint16_t _w[8];
+ uint32_t _l[4];
+ uint64_t _q[2];
+} XMMReg;
+
+typedef union {
+ uint8_t _b[32];
+ uint16_t _w[16];
+ uint32_t _l[8];
+ uint64_t _q[4];
+} YMMReg;
+
typedef MMREG_UNION(ZMMReg, 512) ZMMReg;
typedef MMREG_UNION(MMXReg, 64) MMXReg;
@@ -1047,7 +1080,11 @@ typedef struct CPUX86State {
ZMMReg xmm_t0;
MMXReg mmx_t0;
+ XMMReg ymmh_regs[CPU_NB_REGS];
+
uint64_t opmask_regs[NB_OPMASK_REGS];
+ YMMReg zmmh_regs[CPU_NB_REGS];
+ ZMMReg hi16_zmm_regs[CPU_NB_REGS];
/* sysenter registers */
uint32_t sysenter_cs;
@@ -1172,11 +1209,15 @@ typedef struct CPUX86State {
int32_t interrupt_injected;
uint8_t soft_interrupt;
uint8_t has_error_code;
+ uint32_t ins_len;
uint32_t sipi_vector;
bool tsc_valid;
int64_t tsc_khz;
int64_t user_tsc_khz; /* for sanity check only */
void *kvm_xsave_buf;
+#if defined(CONFIG_HVF)
+ HVFX86EmulatorState *hvf_emul;
+#endif
uint64_t mcg_cap;
uint64_t mcg_ctl;
diff --git a/target/i386/hvf/Makefile.objs b/target/i386/hvf/Makefile.objs
new file mode 100644
index 0000000000..927b86bc67
--- /dev/null
+++ b/target/i386/hvf/Makefile.objs
@@ -0,0 +1,2 @@
+obj-y += hvf.o
+obj-y += x86.o x86_cpuid.o x86_decode.o x86_descr.o x86_emu.o x86_flags.o x86_mmu.o x86hvf.o x86_task.o
diff --git a/target/i386/hvf/README.md b/target/i386/hvf/README.md
new file mode 100644
index 0000000000..0d27a0d52b
--- /dev/null
+++ b/target/i386/hvf/README.md
@@ -0,0 +1,7 @@
+# OS X Hypervisor.framework support in QEMU
+
+These sources (and ../hvf-all.c) are adapted from Veertu Inc's vdhh (Veertu Desktop Hosted Hypervisor) (last known location: https://github.com/veertuinc/vdhh) with some minor changes, the most significant of which were:
+
+1. Adapt to our current QEMU's `CPUState` structure and `address_space_rw` API; many struct members have been moved around (emulated x86 state, kvm_xsave_buf) due to historical differences + QEMU needing to handle more emulation targets.
+2. Removal of `apic_page` and hyperv-related functionality.
+3. More relaxed use of `qemu_mutex_lock_iothread`.
diff --git a/target/i386/hvf/hvf-i386.h b/target/i386/hvf/hvf-i386.h
new file mode 100644
index 0000000000..2232501552
--- /dev/null
+++ b/target/i386/hvf/hvf-i386.h
@@ -0,0 +1,48 @@
+/*
+ * QEMU Hypervisor.framework (HVF) support
+ *
+ * Copyright 2017 Google Inc
+ *
+ * Adapted from target-i386/hax-i386.h:
+ * Copyright (c) 2011 Intel Corporation
+ * Written by:
+ * Jiang Yunhong<yunhong.jiang@intel.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef _HVF_I386_H
+#define _HVF_I386_H
+
+#include "sysemu/hvf.h"
+#include "cpu.h"
+#include "x86.h"
+
+#define HVF_MAX_VCPU 0x10
+#define MAX_VM_ID 0x40
+#define MAX_VCPU_ID 0x40
+
+extern struct hvf_state hvf_global;
+
+struct hvf_vm {
+ int id;
+ struct hvf_vcpu_state *vcpus[HVF_MAX_VCPU];
+};
+
+struct hvf_state {
+ uint32_t version;
+ struct hvf_vm *vm;
+ uint64_t mem_quota;
+};
+
+#ifdef NEED_CPU_H
+/* Functions exported to host specific mode */
+
+/* Host specific functions */
+int hvf_inject_interrupt(CPUArchState *env, int vector);
+int hvf_vcpu_run(struct hvf_vcpu_state *vcpu);
+#endif
+
+#endif
diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c
new file mode 100644
index 0000000000..010866ed22
--- /dev/null
+++ b/target/i386/hvf/hvf.c
@@ -0,0 +1,959 @@
+/* Copyright 2008 IBM Corporation
+ * 2008 Red Hat, Inc.
+ * Copyright 2011 Intel Corporation
+ * Copyright 2016 Veertu, Inc.
+ * Copyright 2017 The Android Open Source Project
+ *
+ * QEMU Hypervisor.framework support
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/error-report.h"
+
+#include "sysemu/hvf.h"
+#include "hvf-i386.h"
+#include "vmcs.h"
+#include "vmx.h"
+#include "x86.h"
+#include "x86_descr.h"
+#include "x86_mmu.h"
+#include "x86_decode.h"
+#include "x86_emu.h"
+#include "x86_task.h"
+#include "x86hvf.h"
+
+#include <Hypervisor/hv.h>
+#include <Hypervisor/hv_vmx.h>
+
+#include "exec/address-spaces.h"
+#include "exec/exec-all.h"
+#include "exec/ioport.h"
+#include "hw/i386/apic_internal.h"
+#include "hw/boards.h"
+#include "qemu/main-loop.h"
+#include "strings.h"
+#include "sysemu/accel.h"
+#include "sysemu/sysemu.h"
+#include "target/i386/cpu.h"
+
+pthread_rwlock_t mem_lock = PTHREAD_RWLOCK_INITIALIZER;
+HVFState *hvf_state;
+int hvf_disabled = 1;
+
+static void assert_hvf_ok(hv_return_t ret)
+{
+ if (ret == HV_SUCCESS) {
+ return;
+ }
+
+ switch (ret) {
+ case HV_ERROR:
+ error_report("Error: HV_ERROR\n");
+ break;
+ case HV_BUSY:
+ error_report("Error: HV_BUSY\n");
+ break;
+ case HV_BAD_ARGUMENT:
+ error_report("Error: HV_BAD_ARGUMENT\n");
+ break;
+ case HV_NO_RESOURCES:
+ error_report("Error: HV_NO_RESOURCES\n");
+ break;
+ case HV_NO_DEVICE:
+ error_report("Error: HV_NO_DEVICE\n");
+ break;
+ case HV_UNSUPPORTED:
+ error_report("Error: HV_UNSUPPORTED\n");
+ break;
+ default:
+ error_report("Unknown Error\n");
+ }
+
+ abort();
+}
+
+/* Memory slots */
+hvf_slot *hvf_find_overlap_slot(uint64_t start, uint64_t end)
+{
+ hvf_slot *slot;
+ int x;
+ for (x = 0; x < hvf_state->num_slots; ++x) {
+ slot = &hvf_state->slots[x];
+ if (slot->size && start < (slot->start + slot->size) &&
+ end > slot->start) {
+ return slot;
+ }
+ }
+ return NULL;
+}
+
+struct mac_slot {
+ int present;
+ uint64_t size;
+ uint64_t gpa_start;
+ uint64_t gva;
+};
+
+struct mac_slot mac_slots[32];
+#define ALIGN(x, y) (((x) + (y) - 1) & ~((y) - 1))
+
+static int do_hvf_set_memory(hvf_slot *slot)
+{
+ struct mac_slot *macslot;
+ hv_memory_flags_t flags;
+ hv_return_t ret;
+
+ macslot = &mac_slots[slot->slot_id];
+
+ if (macslot->present) {
+ if (macslot->size != slot->size) {
+ macslot->present = 0;
+ ret = hv_vm_unmap(macslot->gpa_start, macslot->size);
+ assert_hvf_ok(ret);
+ }
+ }
+
+ if (!slot->size) {
+ return 0;
+ }
+
+ flags = HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC;
+
+ macslot->present = 1;
+ macslot->gpa_start = slot->start;
+ macslot->size = slot->size;
+ ret = hv_vm_map((hv_uvaddr_t)slot->mem, slot->start, slot->size, flags);
+ assert_hvf_ok(ret);
+ return 0;
+}
+
+void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
+{
+ hvf_slot *mem;
+ MemoryRegion *area = section->mr;
+
+ if (!memory_region_is_ram(area)) {
+ return;
+ }
+
+ mem = hvf_find_overlap_slot(
+ section->offset_within_address_space,
+ section->offset_within_address_space + int128_get64(section->size));
+
+ if (mem && add) {
+ if (mem->size == int128_get64(section->size) &&
+ mem->start == section->offset_within_address_space &&
+ mem->mem == (memory_region_get_ram_ptr(area) +
+ section->offset_within_region)) {
+ return; /* Same region was attempted to register, go away. */
+ }
+ }
+
+ /* Region needs to be reset. set the size to 0 and remap it. */
+ if (mem) {
+ mem->size = 0;
+ if (do_hvf_set_memory(mem)) {
+ error_report("Failed to reset overlapping slot\n");
+ abort();
+ }
+ }
+
+ if (!add) {
+ return;
+ }
+
+ /* Now make a new slot. */
+ int x;
+
+ for (x = 0; x < hvf_state->num_slots; ++x) {
+ mem = &hvf_state->slots[x];
+ if (!mem->size) {
+ break;
+ }
+ }
+
+ if (x == hvf_state->num_slots) {
+ error_report("No free slots\n");
+ abort();
+ }
+
+ mem->size = int128_get64(section->size);
+ mem->mem = memory_region_get_ram_ptr(area) + section->offset_within_region;
+ mem->start = section->offset_within_address_space;
+ mem->region = area;
+
+ if (do_hvf_set_memory(mem)) {
+ error_report("Error registering new memory slot\n");
+ abort();
+ }
+}
+
+void vmx_update_tpr(CPUState *cpu)
+{
+ /* TODO: need integrate APIC handling */
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ int tpr = cpu_get_apic_tpr(x86_cpu->apic_state) << 4;
+ int irr = apic_get_highest_priority_irr(x86_cpu->apic_state);
+
+ wreg(cpu->hvf_fd, HV_X86_TPR, tpr);
+ if (irr == -1) {
+ wvmcs(cpu->hvf_fd, VMCS_TPR_THRESHOLD, 0);
+ } else {
+ wvmcs(cpu->hvf_fd, VMCS_TPR_THRESHOLD, (irr > tpr) ? tpr >> 4 :
+ irr >> 4);
+ }
+}
+
+void update_apic_tpr(CPUState *cpu)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ int tpr = rreg(cpu->hvf_fd, HV_X86_TPR) >> 4;
+ cpu_set_apic_tpr(x86_cpu->apic_state, tpr);
+}
+
+#define VECTORING_INFO_VECTOR_MASK 0xff
+
+static void hvf_handle_interrupt(CPUState * cpu, int mask)
+{
+ cpu->interrupt_request |= mask;
+ if (!qemu_cpu_is_self(cpu)) {
+ qemu_cpu_kick(cpu);
+ }
+}
+
+void hvf_handle_io(CPUArchState *env, uint16_t port, void *buffer,
+ int direction, int size, int count)
+{
+ int i;
+ uint8_t *ptr = buffer;
+
+ for (i = 0; i < count; i++) {
+ address_space_rw(&address_space_io, port, MEMTXATTRS_UNSPECIFIED,
+ ptr, size,
+ direction);
+ ptr += size;
+ }
+}
+
+/* TODO: synchronize vcpu state */
+static void do_hvf_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
+{
+ CPUState *cpu_state = cpu;
+ if (cpu_state->vcpu_dirty == 0) {
+ hvf_get_registers(cpu_state);
+ }
+
+ cpu_state->vcpu_dirty = 1;
+}
+
+void hvf_cpu_synchronize_state(CPUState *cpu_state)
+{
+ if (cpu_state->vcpu_dirty == 0) {
+ run_on_cpu(cpu_state, do_hvf_cpu_synchronize_state, RUN_ON_CPU_NULL);
+ }
+}
+
+static void do_hvf_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg)
+{
+ CPUState *cpu_state = cpu;
+ hvf_put_registers(cpu_state);
+ cpu_state->vcpu_dirty = false;
+}
+
+void hvf_cpu_synchronize_post_reset(CPUState *cpu_state)
+{
+ run_on_cpu(cpu_state, do_hvf_cpu_synchronize_post_reset, RUN_ON_CPU_NULL);
+}
+
+void _hvf_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg)
+{
+ CPUState *cpu_state = cpu;
+ hvf_put_registers(cpu_state);
+ cpu_state->vcpu_dirty = false;
+}
+
+void hvf_cpu_synchronize_post_init(CPUState *cpu_state)
+{
+ run_on_cpu(cpu_state, _hvf_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
+}
+
+static bool ept_emulation_fault(hvf_slot *slot, uint64_t gpa, uint64_t ept_qual)
+{
+ int read, write;
+
+ /* EPT fault on an instruction fetch doesn't make sense here */
+ if (ept_qual & EPT_VIOLATION_INST_FETCH) {
+ return false;
+ }
+
+ /* EPT fault must be a read fault or a write fault */
+ read = ept_qual & EPT_VIOLATION_DATA_READ ? 1 : 0;
+ write = ept_qual & EPT_VIOLATION_DATA_WRITE ? 1 : 0;
+ if ((read | write) == 0) {
+ return false;
+ }
+
+ if (write && slot) {
+ if (slot->flags & HVF_SLOT_LOG) {
+ memory_region_set_dirty(slot->region, gpa - slot->start, 1);
+ hv_vm_protect((hv_gpaddr_t)slot->start, (size_t)slot->size,
+ HV_MEMORY_READ | HV_MEMORY_WRITE);
+ }
+ }
+
+ /*
+ * The EPT violation must have been caused by accessing a
+ * guest-physical address that is a translation of a guest-linear
+ * address.
+ */
+ if ((ept_qual & EPT_VIOLATION_GLA_VALID) == 0 ||
+ (ept_qual & EPT_VIOLATION_XLAT_VALID) == 0) {
+ return false;
+ }
+
+ return !slot;
+}
+
+static void hvf_set_dirty_tracking(MemoryRegionSection *section, bool on)
+{
+ hvf_slot *slot;
+
+ slot = hvf_find_overlap_slot(
+ section->offset_within_address_space,
+ section->offset_within_address_space + int128_get64(section->size));
+
+ /* protect region against writes; begin tracking it */
+ if (on) {
+ slot->flags |= HVF_SLOT_LOG;
+ hv_vm_protect((hv_gpaddr_t)slot->start, (size_t)slot->size,
+ HV_MEMORY_READ);
+ /* stop tracking region*/
+ } else {
+ slot->flags &= ~HVF_SLOT_LOG;
+ hv_vm_protect((hv_gpaddr_t)slot->start, (size_t)slot->size,
+ HV_MEMORY_READ | HV_MEMORY_WRITE);
+ }
+}
+
+static void hvf_log_start(MemoryListener *listener,
+ MemoryRegionSection *section, int old, int new)
+{
+ if (old != 0) {
+ return;
+ }
+
+ hvf_set_dirty_tracking(section, 1);
+}
+
+static void hvf_log_stop(MemoryListener *listener,
+ MemoryRegionSection *section, int old, int new)
+{
+ if (new != 0) {
+ return;
+ }
+
+ hvf_set_dirty_tracking(section, 0);
+}
+
+static void hvf_log_sync(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ /*
+ * sync of dirty pages is handled elsewhere; just make sure we keep
+ * tracking the region.
+ */
+ hvf_set_dirty_tracking(section, 1);
+}
+
+static void hvf_region_add(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ hvf_set_phys_mem(section, true);
+}
+
+static void hvf_region_del(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ hvf_set_phys_mem(section, false);
+}
+
+static MemoryListener hvf_memory_listener = {
+ .priority = 10,
+ .region_add = hvf_region_add,
+ .region_del = hvf_region_del,
+ .log_start = hvf_log_start,
+ .log_stop = hvf_log_stop,
+ .log_sync = hvf_log_sync,
+};
+
+void hvf_reset_vcpu(CPUState *cpu) {
+
+ /* TODO: this shouldn't be needed; there is already a call to
+ * cpu_synchronize_all_post_reset in vl.c
+ */
+ wvmcs(cpu->hvf_fd, VMCS_ENTRY_CTLS, 0);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_IA32_EFER, 0);
+ macvm_set_cr0(cpu->hvf_fd, 0x60000010);
+
+ wvmcs(cpu->hvf_fd, VMCS_CR4_MASK, CR4_VMXE_MASK);
+ wvmcs(cpu->hvf_fd, VMCS_CR4_SHADOW, 0x0);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_CR4, CR4_VMXE_MASK);
+
+ /* set VMCS guest state fields */
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_CS_SELECTOR, 0xf000);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_CS_LIMIT, 0xffff);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_CS_ACCESS_RIGHTS, 0x9b);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_CS_BASE, 0xffff0000);
+
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_DS_SELECTOR, 0);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_DS_LIMIT, 0xffff);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_DS_ACCESS_RIGHTS, 0x93);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_DS_BASE, 0);
+
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_ES_SELECTOR, 0);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_ES_LIMIT, 0xffff);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_ES_ACCESS_RIGHTS, 0x93);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_ES_BASE, 0);
+
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_FS_SELECTOR, 0);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_FS_LIMIT, 0xffff);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_FS_ACCESS_RIGHTS, 0x93);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_FS_BASE, 0);
+
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_GS_SELECTOR, 0);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_GS_LIMIT, 0xffff);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_GS_ACCESS_RIGHTS, 0x93);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_GS_BASE, 0);
+
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_SS_SELECTOR, 0);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_SS_LIMIT, 0xffff);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_SS_ACCESS_RIGHTS, 0x93);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_SS_BASE, 0);
+
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_SELECTOR, 0);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_LIMIT, 0);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_ACCESS_RIGHTS, 0x10000);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_BASE, 0);
+
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_TR_SELECTOR, 0);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_TR_LIMIT, 0);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_TR_ACCESS_RIGHTS, 0x83);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_TR_BASE, 0);
+
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_GDTR_LIMIT, 0);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_GDTR_BASE, 0);
+
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_IDTR_LIMIT, 0);
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_IDTR_BASE, 0);
+
+ /*wvmcs(cpu->hvf_fd, VMCS_GUEST_CR2, 0x0);*/
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_CR3, 0x0);
+
+ wreg(cpu->hvf_fd, HV_X86_RIP, 0xfff0);
+ wreg(cpu->hvf_fd, HV_X86_RDX, 0x623);
+ wreg(cpu->hvf_fd, HV_X86_RFLAGS, 0x2);
+ wreg(cpu->hvf_fd, HV_X86_RSP, 0x0);
+ wreg(cpu->hvf_fd, HV_X86_RAX, 0x0);
+ wreg(cpu->hvf_fd, HV_X86_RBX, 0x0);
+ wreg(cpu->hvf_fd, HV_X86_RCX, 0x0);
+ wreg(cpu->hvf_fd, HV_X86_RSI, 0x0);
+ wreg(cpu->hvf_fd, HV_X86_RDI, 0x0);
+ wreg(cpu->hvf_fd, HV_X86_RBP, 0x0);
+
+ for (int i = 0; i < 8; i++) {
+ wreg(cpu->hvf_fd, HV_X86_R8 + i, 0x0);
+ }
+
+ hv_vm_sync_tsc(0);
+ cpu->halted = 0;
+ hv_vcpu_invalidate_tlb(cpu->hvf_fd);
+ hv_vcpu_flush(cpu->hvf_fd);
+}
+
+void hvf_vcpu_destroy(CPUState *cpu)
+{
+ hv_return_t ret = hv_vcpu_destroy((hv_vcpuid_t)cpu->hvf_fd);
+ assert_hvf_ok(ret);
+}
+
+static void dummy_signal(int sig)
+{
+}
+
+int hvf_init_vcpu(CPUState *cpu)
+{
+
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+ int r;
+
+ /* init cpu signals */
+ sigset_t set;
+ struct sigaction sigact;
+
+ memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_handler = dummy_signal;
+ sigaction(SIG_IPI, &sigact, NULL);
+
+ pthread_sigmask(SIG_BLOCK, NULL, &set);
+ sigdelset(&set, SIG_IPI);
+
+ init_emu();
+ init_decoder();
+
+ hvf_state->hvf_caps = g_new0(struct hvf_vcpu_caps, 1);
+ env->hvf_emul = g_new0(HVFX86EmulatorState, 1);
+
+ r = hv_vcpu_create((hv_vcpuid_t *)&cpu->hvf_fd, HV_VCPU_DEFAULT);
+ cpu->vcpu_dirty = 1;
+ assert_hvf_ok(r);
+
+ if (hv_vmx_read_capability(HV_VMX_CAP_PINBASED,
+ &hvf_state->hvf_caps->vmx_cap_pinbased)) {
+ abort();
+ }
+ if (hv_vmx_read_capability(HV_VMX_CAP_PROCBASED,
+ &hvf_state->hvf_caps->vmx_cap_procbased)) {
+ abort();
+ }
+ if (hv_vmx_read_capability(HV_VMX_CAP_PROCBASED2,
+ &hvf_state->hvf_caps->vmx_cap_procbased2)) {
+ abort();
+ }
+ if (hv_vmx_read_capability(HV_VMX_CAP_ENTRY,
+ &hvf_state->hvf_caps->vmx_cap_entry)) {
+ abort();
+ }
+
+ /* set VMCS control fields */
+ wvmcs(cpu->hvf_fd, VMCS_PIN_BASED_CTLS,
+ cap2ctrl(hvf_state->hvf_caps->vmx_cap_pinbased,
+ VMCS_PIN_BASED_CTLS_EXTINT |
+ VMCS_PIN_BASED_CTLS_NMI |
+ VMCS_PIN_BASED_CTLS_VNMI));
+ wvmcs(cpu->hvf_fd, VMCS_PRI_PROC_BASED_CTLS,
+ cap2ctrl(hvf_state->hvf_caps->vmx_cap_procbased,
+ VMCS_PRI_PROC_BASED_CTLS_HLT |
+ VMCS_PRI_PROC_BASED_CTLS_MWAIT |
+ VMCS_PRI_PROC_BASED_CTLS_TSC_OFFSET |
+ VMCS_PRI_PROC_BASED_CTLS_TPR_SHADOW) |
+ VMCS_PRI_PROC_BASED_CTLS_SEC_CONTROL);
+ wvmcs(cpu->hvf_fd, VMCS_SEC_PROC_BASED_CTLS,
+ cap2ctrl(hvf_state->hvf_caps->vmx_cap_procbased2,
+ VMCS_PRI_PROC_BASED2_CTLS_APIC_ACCESSES));
+
+ wvmcs(cpu->hvf_fd, VMCS_ENTRY_CTLS, cap2ctrl(hvf_state->hvf_caps->vmx_cap_entry,
+ 0));
+ wvmcs(cpu->hvf_fd, VMCS_EXCEPTION_BITMAP, 0); /* Double fault */
+
+ wvmcs(cpu->hvf_fd, VMCS_TPR_THRESHOLD, 0);
+
+ hvf_reset_vcpu(cpu);
+
+ x86cpu = X86_CPU(cpu);
+ x86cpu->env.kvm_xsave_buf = qemu_memalign(4096, 4096);
+
+ hv_vcpu_enable_native_msr(cpu->hvf_fd, MSR_STAR, 1);
+ hv_vcpu_enable_native_msr(cpu->hvf_fd, MSR_LSTAR, 1);
+ hv_vcpu_enable_native_msr(cpu->hvf_fd, MSR_CSTAR, 1);
+ hv_vcpu_enable_native_msr(cpu->hvf_fd, MSR_FMASK, 1);
+ hv_vcpu_enable_native_msr(cpu->hvf_fd, MSR_FSBASE, 1);
+ hv_vcpu_enable_native_msr(cpu->hvf_fd, MSR_GSBASE, 1);
+ hv_vcpu_enable_native_msr(cpu->hvf_fd, MSR_KERNELGSBASE, 1);
+ hv_vcpu_enable_native_msr(cpu->hvf_fd, MSR_TSC_AUX, 1);
+ /*hv_vcpu_enable_native_msr(cpu->hvf_fd, MSR_IA32_TSC, 1);*/
+ hv_vcpu_enable_native_msr(cpu->hvf_fd, MSR_IA32_SYSENTER_CS, 1);
+ hv_vcpu_enable_native_msr(cpu->hvf_fd, MSR_IA32_SYSENTER_EIP, 1);
+ hv_vcpu_enable_native_msr(cpu->hvf_fd, MSR_IA32_SYSENTER_ESP, 1);
+
+ return 0;
+}
+
+void hvf_disable(int shouldDisable)
+{
+ hvf_disabled = shouldDisable;
+}
+
+static void hvf_store_events(CPUState *cpu, uint32_t ins_len, uint64_t idtvec_info)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+
+ env->exception_injected = -1;
+ env->interrupt_injected = -1;
+ env->nmi_injected = false;
+ if (idtvec_info & VMCS_IDT_VEC_VALID) {
+ switch (idtvec_info & VMCS_IDT_VEC_TYPE) {
+ case VMCS_IDT_VEC_HWINTR:
+ case VMCS_IDT_VEC_SWINTR:
+ env->interrupt_injected = idtvec_info & VMCS_IDT_VEC_VECNUM;
+ break;
+ case VMCS_IDT_VEC_NMI:
+ env->nmi_injected = true;
+ break;
+ case VMCS_IDT_VEC_HWEXCEPTION:
+ case VMCS_IDT_VEC_SWEXCEPTION:
+ env->exception_injected = idtvec_info & VMCS_IDT_VEC_VECNUM;
+ break;
+ case VMCS_IDT_VEC_PRIV_SWEXCEPTION:
+ default:
+ abort();
+ }
+ if ((idtvec_info & VMCS_IDT_VEC_TYPE) == VMCS_IDT_VEC_SWEXCEPTION ||
+ (idtvec_info & VMCS_IDT_VEC_TYPE) == VMCS_IDT_VEC_SWINTR) {
+ env->ins_len = ins_len;
+ }
+ if (idtvec_info & VMCS_INTR_DEL_ERRCODE) {
+ env->has_error_code = true;
+ env->error_code = rvmcs(cpu->hvf_fd, VMCS_IDT_VECTORING_ERROR);
+ }
+ }
+ if ((rvmcs(cpu->hvf_fd, VMCS_GUEST_INTERRUPTIBILITY) &
+ VMCS_INTERRUPTIBILITY_NMI_BLOCKING)) {
+ env->hflags2 |= HF2_NMI_MASK;
+ } else {
+ env->hflags2 &= ~HF2_NMI_MASK;
+ }
+ if (rvmcs(cpu->hvf_fd, VMCS_GUEST_INTERRUPTIBILITY) &
+ (VMCS_INTERRUPTIBILITY_STI_BLOCKING |
+ VMCS_INTERRUPTIBILITY_MOVSS_BLOCKING)) {
+ env->hflags |= HF_INHIBIT_IRQ_MASK;
+ } else {
+ env->hflags &= ~HF_INHIBIT_IRQ_MASK;
+ }
+}
+
+int hvf_vcpu_exec(CPUState *cpu)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ int ret = 0;
+ uint64_t rip = 0;
+
+ cpu->halted = 0;
+
+ if (hvf_process_events(cpu)) {
+ return EXCP_HLT;
+ }
+
+ do {
+ if (cpu->vcpu_dirty) {
+ hvf_put_registers(cpu);
+ cpu->vcpu_dirty = false;
+ }
+
+ if (hvf_inject_interrupts(cpu)) {
+ return EXCP_INTERRUPT;
+ }
+ vmx_update_tpr(cpu);
+
+ qemu_mutex_unlock_iothread();
+ if (!cpu_is_bsp(X86_CPU(cpu)) && cpu->halted) {
+ qemu_mutex_lock_iothread();
+ return EXCP_HLT;
+ }
+
+ hv_return_t r = hv_vcpu_run(cpu->hvf_fd);
+ assert_hvf_ok(r);
+
+ /* handle VMEXIT */
+ uint64_t exit_reason = rvmcs(cpu->hvf_fd, VMCS_EXIT_REASON);
+ uint64_t exit_qual = rvmcs(cpu->hvf_fd, VMCS_EXIT_QUALIFICATION);
+ uint32_t ins_len = (uint32_t)rvmcs(cpu->hvf_fd,
+ VMCS_EXIT_INSTRUCTION_LENGTH);
+
+ uint64_t idtvec_info = rvmcs(cpu->hvf_fd, VMCS_IDT_VECTORING_INFO);
+
+ hvf_store_events(cpu, ins_len, idtvec_info);
+ rip = rreg(cpu->hvf_fd, HV_X86_RIP);
+ RFLAGS(env) = rreg(cpu->hvf_fd, HV_X86_RFLAGS);
+ env->eflags = RFLAGS(env);
+
+ qemu_mutex_lock_iothread();
+
+ update_apic_tpr(cpu);
+ current_cpu = cpu;
+
+ ret = 0;
+ switch (exit_reason) {
+ case EXIT_REASON_HLT: {
+ macvm_set_rip(cpu, rip + ins_len);
+ if (!((cpu->interrupt_request & CPU_INTERRUPT_HARD) &&
+ (EFLAGS(env) & IF_MASK))
+ && !(cpu->interrupt_request & CPU_INTERRUPT_NMI) &&
+ !(idtvec_info & VMCS_IDT_VEC_VALID)) {
+ cpu->halted = 1;
+ ret = EXCP_HLT;
+ }
+ ret = EXCP_INTERRUPT;
+ break;
+ }
+ case EXIT_REASON_MWAIT: {
+ ret = EXCP_INTERRUPT;
+ break;
+ }
+ /* Need to check if MMIO or unmmaped fault */
+ case EXIT_REASON_EPT_FAULT:
+ {
+ hvf_slot *slot;
+ uint64_t gpa = rvmcs(cpu->hvf_fd, VMCS_GUEST_PHYSICAL_ADDRESS);
+
+ if (((idtvec_info & VMCS_IDT_VEC_VALID) == 0) &&
+ ((exit_qual & EXIT_QUAL_NMIUDTI) != 0)) {
+ vmx_set_nmi_blocking(cpu);
+ }
+
+ slot = hvf_find_overlap_slot(gpa, gpa);
+ /* mmio */
+ if (ept_emulation_fault(slot, gpa, exit_qual)) {
+ struct x86_decode decode;
+
+ load_regs(cpu);
+ env->hvf_emul->fetch_rip = rip;
+
+ decode_instruction(env, &decode);
+ exec_instruction(env, &decode);
+ store_regs(cpu);
+ break;
+ }
+ break;
+ }
+ case EXIT_REASON_INOUT:
+ {
+ uint32_t in = (exit_qual & 8) != 0;
+ uint32_t size = (exit_qual & 7) + 1;
+ uint32_t string = (exit_qual & 16) != 0;
+ uint32_t port = exit_qual >> 16;
+ /*uint32_t rep = (exit_qual & 0x20) != 0;*/
+
+ if (!string && in) {
+ uint64_t val = 0;
+ load_regs(cpu);
+ hvf_handle_io(env, port, &val, 0, size, 1);
+ if (size == 1) {
+ AL(env) = val;
+ } else if (size == 2) {
+ AX(env) = val;
+ } else if (size == 4) {
+ RAX(env) = (uint32_t)val;
+ } else {
+ RAX(env) = (uint64_t)val;
+ }
+ RIP(env) += ins_len;
+ store_regs(cpu);
+ break;
+ } else if (!string && !in) {
+ RAX(env) = rreg(cpu->hvf_fd, HV_X86_RAX);
+ hvf_handle_io(env, port, &RAX(env), 1, size, 1);
+ macvm_set_rip(cpu, rip + ins_len);
+ break;
+ }
+ struct x86_decode decode;
+
+ load_regs(cpu);
+ env->hvf_emul->fetch_rip = rip;
+
+ decode_instruction(env, &decode);
+ assert(ins_len == decode.len);
+ exec_instruction(env, &decode);
+ store_regs(cpu);
+
+ break;
+ }
+ case EXIT_REASON_CPUID: {
+ uint32_t rax = (uint32_t)rreg(cpu->hvf_fd, HV_X86_RAX);
+ uint32_t rbx = (uint32_t)rreg(cpu->hvf_fd, HV_X86_RBX);
+ uint32_t rcx = (uint32_t)rreg(cpu->hvf_fd, HV_X86_RCX);
+ uint32_t rdx = (uint32_t)rreg(cpu->hvf_fd, HV_X86_RDX);
+
+ cpu_x86_cpuid(env, rax, rcx, &rax, &rbx, &rcx, &rdx);
+
+ wreg(cpu->hvf_fd, HV_X86_RAX, rax);
+ wreg(cpu->hvf_fd, HV_X86_RBX, rbx);
+ wreg(cpu->hvf_fd, HV_X86_RCX, rcx);
+ wreg(cpu->hvf_fd, HV_X86_RDX, rdx);
+
+ macvm_set_rip(cpu, rip + ins_len);
+ break;
+ }
+ case EXIT_REASON_XSETBV: {
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ uint32_t eax = (uint32_t)rreg(cpu->hvf_fd, HV_X86_RAX);
+ uint32_t ecx = (uint32_t)rreg(cpu->hvf_fd, HV_X86_RCX);
+ uint32_t edx = (uint32_t)rreg(cpu->hvf_fd, HV_X86_RDX);
+
+ if (ecx) {
+ macvm_set_rip(cpu, rip + ins_len);
+ break;
+ }
+ env->xcr0 = ((uint64_t)edx << 32) | eax;
+ wreg(cpu->hvf_fd, HV_X86_XCR0, env->xcr0 | 1);
+ macvm_set_rip(cpu, rip + ins_len);
+ break;
+ }
+ case EXIT_REASON_INTR_WINDOW:
+ vmx_clear_int_window_exiting(cpu);
+ ret = EXCP_INTERRUPT;
+ break;
+ case EXIT_REASON_NMI_WINDOW:
+ vmx_clear_nmi_window_exiting(cpu);
+ ret = EXCP_INTERRUPT;
+ break;
+ case EXIT_REASON_EXT_INTR:
+ /* force exit and allow io handling */
+ ret = EXCP_INTERRUPT;
+ break;
+ case EXIT_REASON_RDMSR:
+ case EXIT_REASON_WRMSR:
+ {
+ load_regs(cpu);
+ if (exit_reason == EXIT_REASON_RDMSR) {
+ simulate_rdmsr(cpu);
+ } else {
+ simulate_wrmsr(cpu);
+ }
+ RIP(env) += rvmcs(cpu->hvf_fd, VMCS_EXIT_INSTRUCTION_LENGTH);
+ store_regs(cpu);
+ break;
+ }
+ case EXIT_REASON_CR_ACCESS: {
+ int cr;
+ int reg;
+
+ load_regs(cpu);
+ cr = exit_qual & 15;
+ reg = (exit_qual >> 8) & 15;
+
+ switch (cr) {
+ case 0x0: {
+ macvm_set_cr0(cpu->hvf_fd, RRX(env, reg));
+ break;
+ }
+ case 4: {
+ macvm_set_cr4(cpu->hvf_fd, RRX(env, reg));
+ break;
+ }
+ case 8: {
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ if (exit_qual & 0x10) {
+ RRX(env, reg) = cpu_get_apic_tpr(x86_cpu->apic_state);
+ } else {
+ int tpr = RRX(env, reg);
+ cpu_set_apic_tpr(x86_cpu->apic_state, tpr);
+ ret = EXCP_INTERRUPT;
+ }
+ break;
+ }
+ default:
+ error_report("Unrecognized CR %d\n", cr);
+ abort();
+ }
+ RIP(env) += ins_len;
+ store_regs(cpu);
+ break;
+ }
+ case EXIT_REASON_APIC_ACCESS: { /* TODO */
+ struct x86_decode decode;
+
+ load_regs(cpu);
+ env->hvf_emul->fetch_rip = rip;
+
+ decode_instruction(env, &decode);
+ exec_instruction(env, &decode);
+ store_regs(cpu);
+ break;
+ }
+ case EXIT_REASON_TPR: {
+ ret = 1;
+ break;
+ }
+ case EXIT_REASON_TASK_SWITCH: {
+ uint64_t vinfo = rvmcs(cpu->hvf_fd, VMCS_IDT_VECTORING_INFO);
+ x68_segment_selector sel = {.sel = exit_qual & 0xffff};
+ vmx_handle_task_switch(cpu, sel, (exit_qual >> 30) & 0x3,
+ vinfo & VMCS_INTR_VALID, vinfo & VECTORING_INFO_VECTOR_MASK, vinfo
+ & VMCS_INTR_T_MASK);
+ break;
+ }
+ case EXIT_REASON_TRIPLE_FAULT: {
+ qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+ ret = EXCP_INTERRUPT;
+ break;
+ }
+ case EXIT_REASON_RDPMC:
+ wreg(cpu->hvf_fd, HV_X86_RAX, 0);
+ wreg(cpu->hvf_fd, HV_X86_RDX, 0);
+ macvm_set_rip(cpu, rip + ins_len);
+ break;
+ case VMX_REASON_VMCALL:
+ env->exception_injected = EXCP0D_GPF;
+ env->has_error_code = true;
+ env->error_code = 0;
+ break;
+ default:
+ error_report("%llx: unhandled exit %llx\n", rip, exit_reason);
+ }
+ } while (ret == 0);
+
+ return ret;
+}
+
+static bool hvf_allowed;
+
+static int hvf_accel_init(MachineState *ms)
+{
+ int x;
+ hv_return_t ret;
+ HVFState *s;
+
+ hvf_disable(0);
+ ret = hv_vm_create(HV_VM_DEFAULT);
+ assert_hvf_ok(ret);
+
+ s = g_new0(HVFState, 1);
+
+ s->num_slots = 32;
+ for (x = 0; x < s->num_slots; ++x) {
+ s->slots[x].size = 0;
+ s->slots[x].slot_id = x;
+ }
+
+ hvf_state = s;
+ cpu_interrupt_handler = hvf_handle_interrupt;
+ memory_listener_register(&hvf_memory_listener, &address_space_memory);
+ return 0;
+}
+
+static void hvf_accel_class_init(ObjectClass *oc, void *data)
+{
+ AccelClass *ac = ACCEL_CLASS(oc);
+ ac->name = "HVF";
+ ac->init_machine = hvf_accel_init;
+ ac->allowed = &hvf_allowed;
+}
+
+static const TypeInfo hvf_accel_type = {
+ .name = TYPE_HVF_ACCEL,
+ .parent = TYPE_ACCEL,
+ .class_init = hvf_accel_class_init,
+};
+
+static void hvf_type_init(void)
+{
+ type_register_static(&hvf_accel_type);
+}
+
+type_init(hvf_type_init);
diff --git a/target/i386/hvf/panic.h b/target/i386/hvf/panic.h
new file mode 100644
index 0000000000..411ef43a5b
--- /dev/null
+++ b/target/i386/hvf/panic.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 Veertu Inc,
+ * Copyright (C) 2017 Google Inc,
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef HVF_PANIC_H
+#define HVF_PANIC_H
+
+#define VM_PANIC(x) {\
+ printf("%s\n", x); \
+ abort(); \
+}
+
+#define VM_PANIC_ON(x) {\
+ if (x) { \
+ printf("%s\n", #x); \
+ abort(); \
+ } \
+}
+
+#define VM_PANIC_EX(...) {\
+ printf(__VA_ARGS__); \
+ abort(); \
+}
+
+#define VM_PANIC_ON_EX(x, ...) {\
+ if (x) { \
+ printf(__VA_ARGS__); \
+ abort(); \
+ } \
+}
+
+#endif
diff --git a/target/i386/hvf/vmcs.h b/target/i386/hvf/vmcs.h
new file mode 100644
index 0000000000..2a8c0424a5
--- /dev/null
+++ b/target/i386/hvf/vmcs.h
@@ -0,0 +1,374 @@
+/*-
+ * Copyright (c) 2011 NetApp, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _VMCS_H_
+#define _VMCS_H_
+
+#include <Hypervisor/hv.h>
+#include <Hypervisor/hv_vmx.h>
+
+#define VMCS_INITIAL 0xffffffffffffffff
+
+#define VMCS_IDENT(encoding) ((encoding) | 0x80000000)
+/*
+ * VMCS field encodings from Appendix H, Intel Architecture Manual Vol3B.
+ */
+#define VMCS_INVALID_ENCODING 0xffffffff
+
+/* 16-bit control fields */
+#define VMCS_VPID 0x00000000
+#define VMCS_PIR_VECTOR 0x00000002
+
+/* 16-bit guest-state fields */
+#define VMCS_GUEST_ES_SELECTOR 0x00000800
+#define VMCS_GUEST_CS_SELECTOR 0x00000802
+#define VMCS_GUEST_SS_SELECTOR 0x00000804
+#define VMCS_GUEST_DS_SELECTOR 0x00000806
+#define VMCS_GUEST_FS_SELECTOR 0x00000808
+#define VMCS_GUEST_GS_SELECTOR 0x0000080A
+#define VMCS_GUEST_LDTR_SELECTOR 0x0000080C
+#define VMCS_GUEST_TR_SELECTOR 0x0000080E
+#define VMCS_GUEST_INTR_STATUS 0x00000810
+
+/* 16-bit host-state fields */
+#define VMCS_HOST_ES_SELECTOR 0x00000C00
+#define VMCS_HOST_CS_SELECTOR 0x00000C02
+#define VMCS_HOST_SS_SELECTOR 0x00000C04
+#define VMCS_HOST_DS_SELECTOR 0x00000C06
+#define VMCS_HOST_FS_SELECTOR 0x00000C08
+#define VMCS_HOST_GS_SELECTOR 0x00000C0A
+#define VMCS_HOST_TR_SELECTOR 0x00000C0C
+
+/* 64-bit control fields */
+#define VMCS_IO_BITMAP_A 0x00002000
+#define VMCS_IO_BITMAP_B 0x00002002
+#define VMCS_MSR_BITMAP 0x00002004
+#define VMCS_EXIT_MSR_STORE 0x00002006
+#define VMCS_EXIT_MSR_LOAD 0x00002008
+#define VMCS_ENTRY_MSR_LOAD 0x0000200A
+#define VMCS_EXECUTIVE_VMCS 0x0000200C
+#define VMCS_TSC_OFFSET 0x00002010
+#define VMCS_VIRTUAL_APIC 0x00002012
+#define VMCS_APIC_ACCESS 0x00002014
+#define VMCS_PIR_DESC 0x00002016
+#define VMCS_EPTP 0x0000201A
+#define VMCS_EOI_EXIT0 0x0000201C
+#define VMCS_EOI_EXIT1 0x0000201E
+#define VMCS_EOI_EXIT2 0x00002020
+#define VMCS_EOI_EXIT3 0x00002022
+#define VMCS_EOI_EXIT(vector) (VMCS_EOI_EXIT0 + ((vector) / 64) * 2)
+
+/* 64-bit read-only fields */
+#define VMCS_GUEST_PHYSICAL_ADDRESS 0x00002400
+
+/* 64-bit guest-state fields */
+#define VMCS_LINK_POINTER 0x00002800
+#define VMCS_GUEST_IA32_DEBUGCTL 0x00002802
+#define VMCS_GUEST_IA32_PAT 0x00002804
+#define VMCS_GUEST_IA32_EFER 0x00002806
+#define VMCS_GUEST_IA32_PERF_GLOBAL_CTRL 0x00002808
+#define VMCS_GUEST_PDPTE0 0x0000280A
+#define VMCS_GUEST_PDPTE1 0x0000280C
+#define VMCS_GUEST_PDPTE2 0x0000280E
+#define VMCS_GUEST_PDPTE3 0x00002810
+
+/* 64-bit host-state fields */
+#define VMCS_HOST_IA32_PAT 0x00002C00
+#define VMCS_HOST_IA32_EFER 0x00002C02
+#define VMCS_HOST_IA32_PERF_GLOBAL_CTRL 0x00002C04
+
+/* 32-bit control fields */
+#define VMCS_PIN_BASED_CTLS 0x00004000
+#define VMCS_PRI_PROC_BASED_CTLS 0x00004002
+#define VMCS_EXCEPTION_BITMAP 0x00004004
+#define VMCS_PF_ERROR_MASK 0x00004006
+#define VMCS_PF_ERROR_MATCH 0x00004008
+#define VMCS_CR3_TARGET_COUNT 0x0000400A
+#define VMCS_EXIT_CTLS 0x0000400C
+#define VMCS_EXIT_MSR_STORE_COUNT 0x0000400E
+#define VMCS_EXIT_MSR_LOAD_COUNT 0x00004010
+#define VMCS_ENTRY_CTLS 0x00004012
+#define VMCS_ENTRY_MSR_LOAD_COUNT 0x00004014
+#define VMCS_ENTRY_INTR_INFO 0x00004016
+#define VMCS_ENTRY_EXCEPTION_ERROR 0x00004018
+#define VMCS_ENTRY_INST_LENGTH 0x0000401A
+#define VMCS_TPR_THRESHOLD 0x0000401C
+#define VMCS_SEC_PROC_BASED_CTLS 0x0000401E
+#define VMCS_PLE_GAP 0x00004020
+#define VMCS_PLE_WINDOW 0x00004022
+
+/* 32-bit read-only data fields */
+#define VMCS_INSTRUCTION_ERROR 0x00004400
+#define VMCS_EXIT_REASON 0x00004402
+#define VMCS_EXIT_INTR_INFO 0x00004404
+#define VMCS_EXIT_INTR_ERRCODE 0x00004406
+#define VMCS_IDT_VECTORING_INFO 0x00004408
+#define VMCS_IDT_VECTORING_ERROR 0x0000440A
+#define VMCS_EXIT_INSTRUCTION_LENGTH 0x0000440C
+#define VMCS_EXIT_INSTRUCTION_INFO 0x0000440E
+
+/* 32-bit guest-state fields */
+#define VMCS_GUEST_ES_LIMIT 0x00004800
+#define VMCS_GUEST_CS_LIMIT 0x00004802
+#define VMCS_GUEST_SS_LIMIT 0x00004804
+#define VMCS_GUEST_DS_LIMIT 0x00004806
+#define VMCS_GUEST_FS_LIMIT 0x00004808
+#define VMCS_GUEST_GS_LIMIT 0x0000480A
+#define VMCS_GUEST_LDTR_LIMIT 0x0000480C
+#define VMCS_GUEST_TR_LIMIT 0x0000480E
+#define VMCS_GUEST_GDTR_LIMIT 0x00004810
+#define VMCS_GUEST_IDTR_LIMIT 0x00004812
+#define VMCS_GUEST_ES_ACCESS_RIGHTS 0x00004814
+#define VMCS_GUEST_CS_ACCESS_RIGHTS 0x00004816
+#define VMCS_GUEST_SS_ACCESS_RIGHTS 0x00004818
+#define VMCS_GUEST_DS_ACCESS_RIGHTS 0x0000481A
+#define VMCS_GUEST_FS_ACCESS_RIGHTS 0x0000481C
+#define VMCS_GUEST_GS_ACCESS_RIGHTS 0x0000481E
+#define VMCS_GUEST_LDTR_ACCESS_RIGHTS 0x00004820
+#define VMCS_GUEST_TR_ACCESS_RIGHTS 0x00004822
+#define VMCS_GUEST_INTERRUPTIBILITY 0x00004824
+#define VMCS_GUEST_ACTIVITY 0x00004826
+#define VMCS_GUEST_SMBASE 0x00004828
+#define VMCS_GUEST_IA32_SYSENTER_CS 0x0000482A
+#define VMCS_PREEMPTION_TIMER_VALUE 0x0000482E
+
+/* 32-bit host state fields */
+#define VMCS_HOST_IA32_SYSENTER_CS 0x00004C00
+
+/* Natural Width control fields */
+#define VMCS_CR0_MASK 0x00006000
+#define VMCS_CR4_MASK 0x00006002
+#define VMCS_CR0_SHADOW 0x00006004
+#define VMCS_CR4_SHADOW 0x00006006
+#define VMCS_CR3_TARGET0 0x00006008
+#define VMCS_CR3_TARGET1 0x0000600A
+#define VMCS_CR3_TARGET2 0x0000600C
+#define VMCS_CR3_TARGET3 0x0000600E
+
+/* Natural Width read-only fields */
+#define VMCS_EXIT_QUALIFICATION 0x00006400
+#define VMCS_IO_RCX 0x00006402
+#define VMCS_IO_RSI 0x00006404
+#define VMCS_IO_RDI 0x00006406
+#define VMCS_IO_RIP 0x00006408
+#define VMCS_GUEST_LINEAR_ADDRESS 0x0000640A
+
+/* Natural Width guest-state fields */
+#define VMCS_GUEST_CR0 0x00006800
+#define VMCS_GUEST_CR3 0x00006802
+#define VMCS_GUEST_CR4 0x00006804
+#define VMCS_GUEST_ES_BASE 0x00006806
+#define VMCS_GUEST_CS_BASE 0x00006808
+#define VMCS_GUEST_SS_BASE 0x0000680A
+#define VMCS_GUEST_DS_BASE 0x0000680C
+#define VMCS_GUEST_FS_BASE 0x0000680E
+#define VMCS_GUEST_GS_BASE 0x00006810
+#define VMCS_GUEST_LDTR_BASE 0x00006812
+#define VMCS_GUEST_TR_BASE 0x00006814
+#define VMCS_GUEST_GDTR_BASE 0x00006816
+#define VMCS_GUEST_IDTR_BASE 0x00006818
+#define VMCS_GUEST_DR7 0x0000681A
+#define VMCS_GUEST_RSP 0x0000681C
+#define VMCS_GUEST_RIP 0x0000681E
+#define VMCS_GUEST_RFLAGS 0x00006820
+#define VMCS_GUEST_PENDING_DBG_EXCEPTIONS 0x00006822
+#define VMCS_GUEST_IA32_SYSENTER_ESP 0x00006824
+#define VMCS_GUEST_IA32_SYSENTER_EIP 0x00006826
+
+/* Natural Width host-state fields */
+#define VMCS_HOST_CR0 0x00006C00
+#define VMCS_HOST_CR3 0x00006C02
+#define VMCS_HOST_CR4 0x00006C04
+#define VMCS_HOST_FS_BASE 0x00006C06
+#define VMCS_HOST_GS_BASE 0x00006C08
+#define VMCS_HOST_TR_BASE 0x00006C0A
+#define VMCS_HOST_GDTR_BASE 0x00006C0C
+#define VMCS_HOST_IDTR_BASE 0x00006C0E
+#define VMCS_HOST_IA32_SYSENTER_ESP 0x00006C10
+#define VMCS_HOST_IA32_SYSENTER_EIP 0x00006C12
+#define VMCS_HOST_RSP 0x00006C14
+#define VMCS_HOST_RIP 0x00006c16
+
+/*
+ * VM instruction error numbers
+ */
+#define VMRESUME_WITH_NON_LAUNCHED_VMCS 5
+
+/*
+ * VMCS exit reasons
+ */
+#define EXIT_REASON_EXCEPTION 0
+#define EXIT_REASON_EXT_INTR 1
+#define EXIT_REASON_TRIPLE_FAULT 2
+#define EXIT_REASON_INIT 3
+#define EXIT_REASON_SIPI 4
+#define EXIT_REASON_IO_SMI 5
+#define EXIT_REASON_SMI 6
+#define EXIT_REASON_INTR_WINDOW 7
+#define EXIT_REASON_NMI_WINDOW 8
+#define EXIT_REASON_TASK_SWITCH 9
+#define EXIT_REASON_CPUID 10
+#define EXIT_REASON_GETSEC 11
+#define EXIT_REASON_HLT 12
+#define EXIT_REASON_INVD 13
+#define EXIT_REASON_INVLPG 14
+#define EXIT_REASON_RDPMC 15
+#define EXIT_REASON_RDTSC 16
+#define EXIT_REASON_RSM 17
+#define EXIT_REASON_VMCALL 18
+#define EXIT_REASON_VMCLEAR 19
+#define EXIT_REASON_VMLAUNCH 20
+#define EXIT_REASON_VMPTRLD 21
+#define EXIT_REASON_VMPTRST 22
+#define EXIT_REASON_VMREAD 23
+#define EXIT_REASON_VMRESUME 24
+#define EXIT_REASON_VMWRITE 25
+#define EXIT_REASON_VMXOFF 26
+#define EXIT_REASON_VMXON 27
+#define EXIT_REASON_CR_ACCESS 28
+#define EXIT_REASON_DR_ACCESS 29
+#define EXIT_REASON_INOUT 30
+#define EXIT_REASON_RDMSR 31
+#define EXIT_REASON_WRMSR 32
+#define EXIT_REASON_INVAL_VMCS 33
+#define EXIT_REASON_INVAL_MSR 34
+#define EXIT_REASON_MWAIT 36
+#define EXIT_REASON_MTF 37
+#define EXIT_REASON_MONITOR 39
+#define EXIT_REASON_PAUSE 40
+#define EXIT_REASON_MCE_DURING_ENTR 41
+#define EXIT_REASON_TPR 43
+#define EXIT_REASON_APIC_ACCESS 44
+#define EXIT_REASON_VIRTUALIZED_EOI 45
+#define EXIT_REASON_GDTR_IDTR 46
+#define EXIT_REASON_LDTR_TR 47
+#define EXIT_REASON_EPT_FAULT 48
+#define EXIT_REASON_EPT_MISCONFIG 49
+#define EXIT_REASON_INVEPT 50
+#define EXIT_REASON_RDTSCP 51
+#define EXIT_REASON_VMX_PREEMPT 52
+#define EXIT_REASON_INVVPID 53
+#define EXIT_REASON_WBINVD 54
+#define EXIT_REASON_XSETBV 55
+#define EXIT_REASON_APIC_WRITE 56
+
+/*
+ * NMI unblocking due to IRET.
+ *
+ * Applies to VM-exits due to hardware exception or EPT fault.
+ */
+#define EXIT_QUAL_NMIUDTI (1 << 12)
+/*
+ * VMCS interrupt information fields
+ */
+#define VMCS_INTR_VALID (1U << 31)
+#define VMCS_INTR_T_MASK 0x700 /* Interruption-info type */
+#define VMCS_INTR_T_HWINTR (0 << 8)
+#define VMCS_INTR_T_NMI (2 << 8)
+#define VMCS_INTR_T_HWEXCEPTION (3 << 8)
+#define VMCS_INTR_T_SWINTR (4 << 8)
+#define VMCS_INTR_T_PRIV_SWEXCEPTION (5 << 8)
+#define VMCS_INTR_T_SWEXCEPTION (6 << 8)
+#define VMCS_INTR_DEL_ERRCODE (1 << 11)
+
+/*
+ * VMCS IDT-Vectoring information fields
+ */
+#define VMCS_IDT_VEC_VECNUM 0xFF
+#define VMCS_IDT_VEC_VALID (1U << 31)
+#define VMCS_IDT_VEC_TYPE 0x700
+#define VMCS_IDT_VEC_ERRCODE_VALID (1U << 11)
+#define VMCS_IDT_VEC_HWINTR (0 << 8)
+#define VMCS_IDT_VEC_NMI (2 << 8)
+#define VMCS_IDT_VEC_HWEXCEPTION (3 << 8)
+#define VMCS_IDT_VEC_SWINTR (4 << 8)
+#define VMCS_IDT_VEC_PRIV_SWEXCEPTION (5 << 8)
+#define VMCS_IDT_VEC_SWEXCEPTION (6 << 8)
+
+/*
+ * VMCS Guest interruptibility field
+ */
+#define VMCS_INTERRUPTIBILITY_STI_BLOCKING (1 << 0)
+#define VMCS_INTERRUPTIBILITY_MOVSS_BLOCKING (1 << 1)
+#define VMCS_INTERRUPTIBILITY_SMI_BLOCKING (1 << 2)
+#define VMCS_INTERRUPTIBILITY_NMI_BLOCKING (1 << 3)
+
+/*
+ * Exit qualification for EXIT_REASON_INVAL_VMCS
+ */
+#define EXIT_QUAL_NMI_WHILE_STI_BLOCKING 3
+
+/*
+ * Exit qualification for EPT violation
+ */
+#define EPT_VIOLATION_DATA_READ (1UL << 0)
+#define EPT_VIOLATION_DATA_WRITE (1UL << 1)
+#define EPT_VIOLATION_INST_FETCH (1UL << 2)
+#define EPT_VIOLATION_GPA_READABLE (1UL << 3)
+#define EPT_VIOLATION_GPA_WRITEABLE (1UL << 4)
+#define EPT_VIOLATION_GPA_EXECUTABLE (1UL << 5)
+#define EPT_VIOLATION_GLA_VALID (1UL << 7)
+#define EPT_VIOLATION_XLAT_VALID (1UL << 8)
+
+/*
+ * Exit qualification for APIC-access VM exit
+ */
+#define APIC_ACCESS_OFFSET(qual) ((qual) & 0xFFF)
+#define APIC_ACCESS_TYPE(qual) (((qual) >> 12) & 0xF)
+
+/*
+ * Exit qualification for APIC-write VM exit
+ */
+#define APIC_WRITE_OFFSET(qual) ((qual) & 0xFFF)
+
+#define VMCS_PIN_BASED_CTLS_EXTINT (1 << 0)
+#define VMCS_PIN_BASED_CTLS_NMI (1 << 3)
+#define VMCS_PIN_BASED_CTLS_VNMI (1 << 5)
+
+#define VMCS_PRI_PROC_BASED_CTLS_INT_WINDOW_EXITING (1 << 2)
+#define VMCS_PRI_PROC_BASED_CTLS_TSC_OFFSET (1 << 3)
+#define VMCS_PRI_PROC_BASED_CTLS_HLT (1 << 7)
+#define VMCS_PRI_PROC_BASED_CTLS_MWAIT (1 << 10)
+#define VMCS_PRI_PROC_BASED_CTLS_TSC (1 << 12)
+#define VMCS_PRI_PROC_BASED_CTLS_CR8_LOAD (1 << 19)
+#define VMCS_PRI_PROC_BASED_CTLS_CR8_STORE (1 << 20)
+#define VMCS_PRI_PROC_BASED_CTLS_TPR_SHADOW (1 << 21)
+#define VMCS_PRI_PROC_BASED_CTLS_NMI_WINDOW_EXITING (1 << 22)
+#define VMCS_PRI_PROC_BASED_CTLS_SEC_CONTROL (1 << 31)
+
+#define VMCS_PRI_PROC_BASED2_CTLS_APIC_ACCESSES (1 << 0)
+#define VMCS_PRI_PROC_BASED2_CTLS_X2APIC (1 << 4)
+
+enum task_switch_reason {
+ TSR_CALL,
+ TSR_IRET,
+ TSR_JMP,
+ TSR_IDT_GATE, /* task gate in IDT */
+};
+
+#endif
diff --git a/target/i386/hvf/vmx.h b/target/i386/hvf/vmx.h
new file mode 100644
index 0000000000..9dfcd2f2eb
--- /dev/null
+++ b/target/i386/hvf/vmx.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2016 Veertu Inc,
+ * Copyright (C) 2017 Google Inc,
+ * Based on Veertu vddh/vmm/vmx.h
+ *
+ * Interfaces to Hypervisor.framework to read/write X86 registers and VMCS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef VMX_H
+#define VMX_H
+
+#include <stdint.h>
+#include <Hypervisor/hv.h>
+#include <Hypervisor/hv_vmx.h>
+#include "vmcs.h"
+#include "cpu.h"
+#include "x86.h"
+
+#include "exec/address-spaces.h"
+
+static inline uint64_t rreg(hv_vcpuid_t vcpu, hv_x86_reg_t reg)
+{
+ uint64_t v;
+
+ if (hv_vcpu_read_register(vcpu, reg, &v)) {
+ abort();
+ }
+
+ return v;
+}
+
+/* write GPR */
+static inline void wreg(hv_vcpuid_t vcpu, hv_x86_reg_t reg, uint64_t v)
+{
+ if (hv_vcpu_write_register(vcpu, reg, v)) {
+ abort();
+ }
+}
+
+/* read VMCS field */
+static inline uint64_t rvmcs(hv_vcpuid_t vcpu, uint32_t field)
+{
+ uint64_t v;
+
+ hv_vmx_vcpu_read_vmcs(vcpu, field, &v);
+
+ return v;
+}
+
+/* write VMCS field */
+static inline void wvmcs(hv_vcpuid_t vcpu, uint32_t field, uint64_t v)
+{
+ hv_vmx_vcpu_write_vmcs(vcpu, field, v);
+}
+
+/* desired control word constrained by hardware/hypervisor capabilities */
+static inline uint64_t cap2ctrl(uint64_t cap, uint64_t ctrl)
+{
+ return (ctrl | (cap & 0xffffffff)) & (cap >> 32);
+}
+
+#define VM_ENTRY_GUEST_LMA (1LL << 9)
+
+#define AR_TYPE_ACCESSES_MASK 1
+#define AR_TYPE_READABLE_MASK (1 << 1)
+#define AR_TYPE_WRITEABLE_MASK (1 << 2)
+#define AR_TYPE_CODE_MASK (1 << 3)
+#define AR_TYPE_MASK 0x0f
+#define AR_TYPE_BUSY_64_TSS 11
+#define AR_TYPE_BUSY_32_TSS 11
+#define AR_TYPE_BUSY_16_TSS 3
+#define AR_TYPE_LDT 2
+
+static void enter_long_mode(hv_vcpuid_t vcpu, uint64_t cr0, uint64_t efer)
+{
+ uint64_t entry_ctls;
+
+ efer |= MSR_EFER_LMA;
+ wvmcs(vcpu, VMCS_GUEST_IA32_EFER, efer);
+ entry_ctls = rvmcs(vcpu, VMCS_ENTRY_CTLS);
+ wvmcs(vcpu, VMCS_ENTRY_CTLS, rvmcs(vcpu, VMCS_ENTRY_CTLS) |
+ VM_ENTRY_GUEST_LMA);
+
+ uint64_t guest_tr_ar = rvmcs(vcpu, VMCS_GUEST_TR_ACCESS_RIGHTS);
+ if ((efer & MSR_EFER_LME) &&
+ (guest_tr_ar & AR_TYPE_MASK) != AR_TYPE_BUSY_64_TSS) {
+ wvmcs(vcpu, VMCS_GUEST_TR_ACCESS_RIGHTS,
+ (guest_tr_ar & ~AR_TYPE_MASK) | AR_TYPE_BUSY_64_TSS);
+ }
+}
+
+static void exit_long_mode(hv_vcpuid_t vcpu, uint64_t cr0, uint64_t efer)
+{
+ uint64_t entry_ctls;
+
+ entry_ctls = rvmcs(vcpu, VMCS_ENTRY_CTLS);
+ wvmcs(vcpu, VMCS_ENTRY_CTLS, entry_ctls & ~VM_ENTRY_GUEST_LMA);
+
+ efer &= ~MSR_EFER_LMA;
+ wvmcs(vcpu, VMCS_GUEST_IA32_EFER, efer);
+}
+
+static inline void macvm_set_cr0(hv_vcpuid_t vcpu, uint64_t cr0)
+{
+ int i;
+ uint64_t pdpte[4] = {0, 0, 0, 0};
+ uint64_t efer = rvmcs(vcpu, VMCS_GUEST_IA32_EFER);
+ uint64_t old_cr0 = rvmcs(vcpu, VMCS_GUEST_CR0);
+
+ if ((cr0 & CR0_PG) && (rvmcs(vcpu, VMCS_GUEST_CR4) & CR4_PAE) &&
+ !(efer & MSR_EFER_LME)) {
+ address_space_rw(&address_space_memory,
+ rvmcs(vcpu, VMCS_GUEST_CR3) & ~0x1f,
+ MEMTXATTRS_UNSPECIFIED,
+ (uint8_t *)pdpte, 32, 0);
+ }
+
+ for (i = 0; i < 4; i++) {
+ wvmcs(vcpu, VMCS_GUEST_PDPTE0 + i * 2, pdpte[i]);
+ }
+
+ wvmcs(vcpu, VMCS_CR0_MASK, CR0_CD | CR0_NE | CR0_PG);
+ wvmcs(vcpu, VMCS_CR0_SHADOW, cr0);
+
+ cr0 &= ~CR0_CD;
+ wvmcs(vcpu, VMCS_GUEST_CR0, cr0 | CR0_NE | CR0_ET);
+
+ if (efer & MSR_EFER_LME) {
+ if (!(old_cr0 & CR0_PG) && (cr0 & CR0_PG)) {
+ enter_long_mode(vcpu, cr0, efer);
+ }
+ if (/*(old_cr0 & CR0_PG) &&*/ !(cr0 & CR0_PG)) {
+ exit_long_mode(vcpu, cr0, efer);
+ }
+ }
+
+ hv_vcpu_invalidate_tlb(vcpu);
+ hv_vcpu_flush(vcpu);
+}
+
+static inline void macvm_set_cr4(hv_vcpuid_t vcpu, uint64_t cr4)
+{
+ uint64_t guest_cr4 = cr4 | CR4_VMXE;
+
+ wvmcs(vcpu, VMCS_GUEST_CR4, guest_cr4);
+ wvmcs(vcpu, VMCS_CR4_SHADOW, cr4);
+
+ hv_vcpu_invalidate_tlb(vcpu);
+ hv_vcpu_flush(vcpu);
+}
+
+static inline void macvm_set_rip(CPUState *cpu, uint64_t rip)
+{
+ uint64_t val;
+
+ /* BUG, should take considering overlap.. */
+ wreg(cpu->hvf_fd, HV_X86_RIP, rip);
+
+ /* after moving forward in rip, we need to clean INTERRUPTABILITY */
+ val = rvmcs(cpu->hvf_fd, VMCS_GUEST_INTERRUPTIBILITY);
+ if (val & (VMCS_INTERRUPTIBILITY_STI_BLOCKING |
+ VMCS_INTERRUPTIBILITY_MOVSS_BLOCKING)) {
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_INTERRUPTIBILITY,
+ val & ~(VMCS_INTERRUPTIBILITY_STI_BLOCKING |
+ VMCS_INTERRUPTIBILITY_MOVSS_BLOCKING));
+ }
+}
+
+static inline void vmx_clear_nmi_blocking(CPUState *cpu)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+
+ env->hflags2 &= ~HF2_NMI_MASK;
+ uint32_t gi = (uint32_t) rvmcs(cpu->hvf_fd, VMCS_GUEST_INTERRUPTIBILITY);
+ gi &= ~VMCS_INTERRUPTIBILITY_NMI_BLOCKING;
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_INTERRUPTIBILITY, gi);
+}
+
+static inline void vmx_set_nmi_blocking(CPUState *cpu)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+
+ env->hflags2 |= HF2_NMI_MASK;
+ uint32_t gi = (uint32_t)rvmcs(cpu->hvf_fd, VMCS_GUEST_INTERRUPTIBILITY);
+ gi |= VMCS_INTERRUPTIBILITY_NMI_BLOCKING;
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_INTERRUPTIBILITY, gi);
+}
+
+static inline void vmx_set_nmi_window_exiting(CPUState *cpu)
+{
+ uint64_t val;
+ val = rvmcs(cpu->hvf_fd, VMCS_PRI_PROC_BASED_CTLS);
+ wvmcs(cpu->hvf_fd, VMCS_PRI_PROC_BASED_CTLS, val |
+ VMCS_PRI_PROC_BASED_CTLS_NMI_WINDOW_EXITING);
+
+}
+
+static inline void vmx_clear_nmi_window_exiting(CPUState *cpu)
+{
+
+ uint64_t val;
+ val = rvmcs(cpu->hvf_fd, VMCS_PRI_PROC_BASED_CTLS);
+ wvmcs(cpu->hvf_fd, VMCS_PRI_PROC_BASED_CTLS, val &
+ ~VMCS_PRI_PROC_BASED_CTLS_NMI_WINDOW_EXITING);
+}
+
+#endif
diff --git a/target/i386/hvf/x86.c b/target/i386/hvf/x86.c
new file mode 100644
index 0000000000..3afcedc7fc
--- /dev/null
+++ b/target/i386/hvf/x86.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2016 Veertu Inc,
+ * Copyright (C) 2017 Google Inc,
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+
+#include "cpu.h"
+#include "qemu-common.h"
+#include "x86_decode.h"
+#include "x86_emu.h"
+#include "vmcs.h"
+#include "vmx.h"
+#include "x86_mmu.h"
+#include "x86_descr.h"
+
+/* static uint32_t x86_segment_access_rights(struct x86_segment_descriptor *var)
+{
+ uint32_t ar;
+
+ if (!var->p) {
+ ar = 1 << 16;
+ return ar;
+ }
+
+ ar = var->type & 15;
+ ar |= (var->s & 1) << 4;
+ ar |= (var->dpl & 3) << 5;
+ ar |= (var->p & 1) << 7;
+ ar |= (var->avl & 1) << 12;
+ ar |= (var->l & 1) << 13;
+ ar |= (var->db & 1) << 14;
+ ar |= (var->g & 1) << 15;
+ return ar;
+}*/
+
+bool x86_read_segment_descriptor(struct CPUState *cpu,
+ struct x86_segment_descriptor *desc,
+ x68_segment_selector sel)
+{
+ target_ulong base;
+ uint32_t limit;
+
+ memset(desc, 0, sizeof(*desc));
+
+ /* valid gdt descriptors start from index 1 */
+ if (!sel.index && GDT_SEL == sel.ti) {
+ return false;
+ }
+
+ if (GDT_SEL == sel.ti) {
+ base = rvmcs(cpu->hvf_fd, VMCS_GUEST_GDTR_BASE);
+ limit = rvmcs(cpu->hvf_fd, VMCS_GUEST_GDTR_LIMIT);
+ } else {
+ base = rvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_BASE);
+ limit = rvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_LIMIT);
+ }
+
+ if (sel.index * 8 >= limit) {
+ return false;
+ }
+
+ vmx_read_mem(cpu, desc, base + sel.index * 8, sizeof(*desc));
+ return true;
+}
+
+bool x86_write_segment_descriptor(struct CPUState *cpu,
+ struct x86_segment_descriptor *desc,
+ x68_segment_selector sel)
+{
+ target_ulong base;
+ uint32_t limit;
+
+ if (GDT_SEL == sel.ti) {
+ base = rvmcs(cpu->hvf_fd, VMCS_GUEST_GDTR_BASE);
+ limit = rvmcs(cpu->hvf_fd, VMCS_GUEST_GDTR_LIMIT);
+ } else {
+ base = rvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_BASE);
+ limit = rvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_LIMIT);
+ }
+
+ if (sel.index * 8 >= limit) {
+ printf("%s: gdt limit\n", __func__);
+ return false;
+ }
+ vmx_write_mem(cpu, base + sel.index * 8, desc, sizeof(*desc));
+ return true;
+}
+
+bool x86_read_call_gate(struct CPUState *cpu, struct x86_call_gate *idt_desc,
+ int gate)
+{
+ target_ulong base = rvmcs(cpu->hvf_fd, VMCS_GUEST_IDTR_BASE);
+ uint32_t limit = rvmcs(cpu->hvf_fd, VMCS_GUEST_IDTR_LIMIT);
+
+ memset(idt_desc, 0, sizeof(*idt_desc));
+ if (gate * 8 >= limit) {
+ printf("%s: idt limit\n", __func__);
+ return false;
+ }
+
+ vmx_read_mem(cpu, idt_desc, base + gate * 8, sizeof(*idt_desc));
+ return true;
+}
+
+bool x86_is_protected(struct CPUState *cpu)
+{
+ uint64_t cr0 = rvmcs(cpu->hvf_fd, VMCS_GUEST_CR0);
+ return cr0 & CR0_PE;
+}
+
+bool x86_is_real(struct CPUState *cpu)
+{
+ return !x86_is_protected(cpu);
+}
+
+bool x86_is_v8086(struct CPUState *cpu)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ return x86_is_protected(cpu) && (RFLAGS(env) & RFLAGS_VM);
+}
+
+bool x86_is_long_mode(struct CPUState *cpu)
+{
+ return rvmcs(cpu->hvf_fd, VMCS_GUEST_IA32_EFER) & MSR_EFER_LMA;
+}
+
+bool x86_is_long64_mode(struct CPUState *cpu)
+{
+ struct vmx_segment desc;
+ vmx_read_segment_descriptor(cpu, &desc, R_CS);
+
+ return x86_is_long_mode(cpu) && ((desc.ar >> 13) & 1);
+}
+
+bool x86_is_paging_mode(struct CPUState *cpu)
+{
+ uint64_t cr0 = rvmcs(cpu->hvf_fd, VMCS_GUEST_CR0);
+ return cr0 & CR0_PG;
+}
+
+bool x86_is_pae_enabled(struct CPUState *cpu)
+{
+ uint64_t cr4 = rvmcs(cpu->hvf_fd, VMCS_GUEST_CR4);
+ return cr4 & CR4_PAE;
+}
+
+target_ulong linear_addr(struct CPUState *cpu, target_ulong addr, X86Seg seg)
+{
+ return vmx_read_segment_base(cpu, seg) + addr;
+}
+
+target_ulong linear_addr_size(struct CPUState *cpu, target_ulong addr, int size,
+ X86Seg seg)
+{
+ switch (size) {
+ case 2:
+ addr = (uint16_t)addr;
+ break;
+ case 4:
+ addr = (uint32_t)addr;
+ break;
+ default:
+ break;
+ }
+ return linear_addr(cpu, addr, seg);
+}
+
+target_ulong linear_rip(struct CPUState *cpu, target_ulong rip)
+{
+ return linear_addr(cpu, rip, R_CS);
+}
diff --git a/target/i386/hvf/x86.h b/target/i386/hvf/x86.h
new file mode 100644
index 0000000000..103ec0976c
--- /dev/null
+++ b/target/i386/hvf/x86.h
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2016 Veertu Inc,
+ * Copyright (C) 2017 Veertu Inc,
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HVF_X86_H
+#define HVF_X86_H 1
+
+typedef struct x86_register {
+ union {
+ struct {
+ uint64_t rrx; /* full 64 bit */
+ };
+ struct {
+ uint32_t erx; /* low 32 bit part */
+ uint32_t hi32_unused1;
+ };
+ struct {
+ uint16_t rx; /* low 16 bit part */
+ uint16_t hi16_unused1;
+ uint32_t hi32_unused2;
+ };
+ struct {
+ uint8_t lx; /* low 8 bit part */
+ uint8_t hx; /* high 8 bit */
+ uint16_t hi16_unused2;
+ uint32_t hi32_unused3;
+ };
+ };
+} __attribute__ ((__packed__)) x86_register;
+
+typedef enum x86_rflags {
+ RFLAGS_CF = (1L << 0),
+ RFLAGS_PF = (1L << 2),
+ RFLAGS_AF = (1L << 4),
+ RFLAGS_ZF = (1L << 6),
+ RFLAGS_SF = (1L << 7),
+ RFLAGS_TF = (1L << 8),
+ RFLAGS_IF = (1L << 9),
+ RFLAGS_DF = (1L << 10),
+ RFLAGS_OF = (1L << 11),
+ RFLAGS_IOPL = (3L << 12),
+ RFLAGS_NT = (1L << 14),
+ RFLAGS_RF = (1L << 16),
+ RFLAGS_VM = (1L << 17),
+ RFLAGS_AC = (1L << 18),
+ RFLAGS_VIF = (1L << 19),
+ RFLAGS_VIP = (1L << 20),
+ RFLAGS_ID = (1L << 21),
+} x86_rflags;
+
+/* rflags register */
+typedef struct x86_reg_flags {
+ union {
+ struct {
+ uint64_t rflags;
+ };
+ struct {
+ uint32_t eflags;
+ uint32_t hi32_unused1;
+ };
+ struct {
+ uint32_t cf:1;
+ uint32_t unused1:1;
+ uint32_t pf:1;
+ uint32_t unused2:1;
+ uint32_t af:1;
+ uint32_t unused3:1;
+ uint32_t zf:1;
+ uint32_t sf:1;
+ uint32_t tf:1;
+ uint32_t ief:1;
+ uint32_t df:1;
+ uint32_t of:1;
+ uint32_t iopl:2;
+ uint32_t nt:1;
+ uint32_t unused4:1;
+ uint32_t rf:1;
+ uint32_t vm:1;
+ uint32_t ac:1;
+ uint32_t vif:1;
+ uint32_t vip:1;
+ uint32_t id:1;
+ uint32_t unused5:10;
+ uint32_t hi32_unused2;
+ };
+ };
+} __attribute__ ((__packed__)) x86_reg_flags;
+
+typedef enum x86_reg_cr0 {
+ CR0_PE = (1L << 0),
+ CR0_MP = (1L << 1),
+ CR0_EM = (1L << 2),
+ CR0_TS = (1L << 3),
+ CR0_ET = (1L << 4),
+ CR0_NE = (1L << 5),
+ CR0_WP = (1L << 16),
+ CR0_AM = (1L << 18),
+ CR0_NW = (1L << 29),
+ CR0_CD = (1L << 30),
+ CR0_PG = (1L << 31),
+} x86_reg_cr0;
+
+typedef enum x86_reg_cr4 {
+ CR4_VME = (1L << 0),
+ CR4_PVI = (1L << 1),
+ CR4_TSD = (1L << 2),
+ CR4_DE = (1L << 3),
+ CR4_PSE = (1L << 4),
+ CR4_PAE = (1L << 5),
+ CR4_MSE = (1L << 6),
+ CR4_PGE = (1L << 7),
+ CR4_PCE = (1L << 8),
+ CR4_OSFXSR = (1L << 9),
+ CR4_OSXMMEXCPT = (1L << 10),
+ CR4_VMXE = (1L << 13),
+ CR4_SMXE = (1L << 14),
+ CR4_FSGSBASE = (1L << 16),
+ CR4_PCIDE = (1L << 17),
+ CR4_OSXSAVE = (1L << 18),
+ CR4_SMEP = (1L << 20),
+} x86_reg_cr4;
+
+/* 16 bit Task State Segment */
+typedef struct x86_tss_segment16 {
+ uint16_t link;
+ uint16_t sp0;
+ uint16_t ss0;
+ uint32_t sp1;
+ uint16_t ss1;
+ uint32_t sp2;
+ uint16_t ss2;
+ uint16_t ip;
+ uint16_t flags;
+ uint16_t ax;
+ uint16_t cx;
+ uint16_t dx;
+ uint16_t bx;
+ uint16_t sp;
+ uint16_t bp;
+ uint16_t si;
+ uint16_t di;
+ uint16_t es;
+ uint16_t cs;
+ uint16_t ss;
+ uint16_t ds;
+ uint16_t ldtr;
+} __attribute__((packed)) x86_tss_segment16;
+
+/* 32 bit Task State Segment */
+typedef struct x86_tss_segment32 {
+ uint32_t prev_tss;
+ uint32_t esp0;
+ uint32_t ss0;
+ uint32_t esp1;
+ uint32_t ss1;
+ uint32_t esp2;
+ uint32_t ss2;
+ uint32_t cr3;
+ uint32_t eip;
+ uint32_t eflags;
+ uint32_t eax;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t ebx;
+ uint32_t esp;
+ uint32_t ebp;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t es;
+ uint32_t cs;
+ uint32_t ss;
+ uint32_t ds;
+ uint32_t fs;
+ uint32_t gs;
+ uint32_t ldt;
+ uint16_t trap;
+ uint16_t iomap_base;
+} __attribute__ ((__packed__)) x86_tss_segment32;
+
+/* 64 bit Task State Segment */
+typedef struct x86_tss_segment64 {
+ uint32_t unused;
+ uint64_t rsp0;
+ uint64_t rsp1;
+ uint64_t rsp2;
+ uint64_t unused1;
+ uint64_t ist1;
+ uint64_t ist2;
+ uint64_t ist3;
+ uint64_t ist4;
+ uint64_t ist5;
+ uint64_t ist6;
+ uint64_t ist7;
+ uint64_t unused2;
+ uint16_t unused3;
+ uint16_t iomap_base;
+} __attribute__ ((__packed__)) x86_tss_segment64;
+
+/* segment descriptors */
+typedef struct x86_segment_descriptor {
+ uint64_t limit0:16;
+ uint64_t base0:16;
+ uint64_t base1:8;
+ uint64_t type:4;
+ uint64_t s:1;
+ uint64_t dpl:2;
+ uint64_t p:1;
+ uint64_t limit1:4;
+ uint64_t avl:1;
+ uint64_t l:1;
+ uint64_t db:1;
+ uint64_t g:1;
+ uint64_t base2:8;
+} __attribute__ ((__packed__)) x86_segment_descriptor;
+
+static inline uint32_t x86_segment_base(x86_segment_descriptor *desc)
+{
+ return (uint32_t)((desc->base2 << 24) | (desc->base1 << 16) | desc->base0);
+}
+
+static inline void x86_set_segment_base(x86_segment_descriptor *desc,
+ uint32_t base)
+{
+ desc->base2 = base >> 24;
+ desc->base1 = (base >> 16) & 0xff;
+ desc->base0 = base & 0xffff;
+}
+
+static inline uint32_t x86_segment_limit(x86_segment_descriptor *desc)
+{
+ uint32_t limit = (uint32_t)((desc->limit1 << 16) | desc->limit0);
+ if (desc->g) {
+ return (limit << 12) | 0xfff;
+ }
+ return limit;
+}
+
+static inline void x86_set_segment_limit(x86_segment_descriptor *desc,
+ uint32_t limit)
+{
+ desc->limit0 = limit & 0xffff;
+ desc->limit1 = limit >> 16;
+}
+
+typedef struct x86_call_gate {
+ uint64_t offset0:16;
+ uint64_t selector:16;
+ uint64_t param_count:4;
+ uint64_t reserved:3;
+ uint64_t type:4;
+ uint64_t dpl:1;
+ uint64_t p:1;
+ uint64_t offset1:16;
+} __attribute__ ((__packed__)) x86_call_gate;
+
+static inline uint32_t x86_call_gate_offset(x86_call_gate *gate)
+{
+ return (uint32_t)((gate->offset1 << 16) | gate->offset0);
+}
+
+#define LDT_SEL 0
+#define GDT_SEL 1
+
+typedef struct x68_segment_selector {
+ union {
+ uint16_t sel;
+ struct {
+ uint16_t rpl:3;
+ uint16_t ti:1;
+ uint16_t index:12;
+ };
+ };
+} __attribute__ ((__packed__)) x68_segment_selector;
+
+typedef struct lazy_flags {
+ target_ulong result;
+ target_ulong auxbits;
+} lazy_flags;
+
+/* Definition of hvf_x86_state is here */
+struct HVFX86EmulatorState {
+ int interruptable;
+ uint64_t fetch_rip;
+ uint64_t rip;
+ struct x86_register regs[16];
+ struct x86_reg_flags rflags;
+ struct lazy_flags lflags;
+ uint8_t mmio_buf[4096];
+};
+
+/* useful register access macros */
+#define RIP(cpu) (cpu->hvf_emul->rip)
+#define EIP(cpu) ((uint32_t)cpu->hvf_emul->rip)
+#define RFLAGS(cpu) (cpu->hvf_emul->rflags.rflags)
+#define EFLAGS(cpu) (cpu->hvf_emul->rflags.eflags)
+
+#define RRX(cpu, reg) (cpu->hvf_emul->regs[reg].rrx)
+#define RAX(cpu) RRX(cpu, R_EAX)
+#define RCX(cpu) RRX(cpu, R_ECX)
+#define RDX(cpu) RRX(cpu, R_EDX)
+#define RBX(cpu) RRX(cpu, R_EBX)
+#define RSP(cpu) RRX(cpu, R_ESP)
+#define RBP(cpu) RRX(cpu, R_EBP)
+#define RSI(cpu) RRX(cpu, R_ESI)
+#define RDI(cpu) RRX(cpu, R_EDI)
+#define R8(cpu) RRX(cpu, R_R8)
+#define R9(cpu) RRX(cpu, R_R9)
+#define R10(cpu) RRX(cpu, R_R10)
+#define R11(cpu) RRX(cpu, R_R11)
+#define R12(cpu) RRX(cpu, R_R12)
+#define R13(cpu) RRX(cpu, R_R13)
+#define R14(cpu) RRX(cpu, R_R14)
+#define R15(cpu) RRX(cpu, R_R15)
+
+#define ERX(cpu, reg) (cpu->hvf_emul->regs[reg].erx)
+#define EAX(cpu) ERX(cpu, R_EAX)
+#define ECX(cpu) ERX(cpu, R_ECX)
+#define EDX(cpu) ERX(cpu, R_EDX)
+#define EBX(cpu) ERX(cpu, R_EBX)
+#define ESP(cpu) ERX(cpu, R_ESP)
+#define EBP(cpu) ERX(cpu, R_EBP)
+#define ESI(cpu) ERX(cpu, R_ESI)
+#define EDI(cpu) ERX(cpu, R_EDI)
+
+#define RX(cpu, reg) (cpu->hvf_emul->regs[reg].rx)
+#define AX(cpu) RX(cpu, R_EAX)
+#define CX(cpu) RX(cpu, R_ECX)
+#define DX(cpu) RX(cpu, R_EDX)
+#define BP(cpu) RX(cpu, R_EBP)
+#define SP(cpu) RX(cpu, R_ESP)
+#define BX(cpu) RX(cpu, R_EBX)
+#define SI(cpu) RX(cpu, R_ESI)
+#define DI(cpu) RX(cpu, R_EDI)
+
+#define RL(cpu, reg) (cpu->hvf_emul->regs[reg].lx)
+#define AL(cpu) RL(cpu, R_EAX)
+#define CL(cpu) RL(cpu, R_ECX)
+#define DL(cpu) RL(cpu, R_EDX)
+#define BL(cpu) RL(cpu, R_EBX)
+
+#define RH(cpu, reg) (cpu->hvf_emul->regs[reg].hx)
+#define AH(cpu) RH(cpu, R_EAX)
+#define CH(cpu) RH(cpu, R_ECX)
+#define DH(cpu) RH(cpu, R_EDX)
+#define BH(cpu) RH(cpu, R_EBX)
+
+/* deal with GDT/LDT descriptors in memory */
+bool x86_read_segment_descriptor(struct CPUState *cpu,
+ struct x86_segment_descriptor *desc,
+ x68_segment_selector sel);
+bool x86_write_segment_descriptor(struct CPUState *cpu,
+ struct x86_segment_descriptor *desc,
+ x68_segment_selector sel);
+
+bool x86_read_call_gate(struct CPUState *cpu, struct x86_call_gate *idt_desc,
+ int gate);
+
+/* helpers */
+bool x86_is_protected(struct CPUState *cpu);
+bool x86_is_real(struct CPUState *cpu);
+bool x86_is_v8086(struct CPUState *cpu);
+bool x86_is_long_mode(struct CPUState *cpu);
+bool x86_is_long64_mode(struct CPUState *cpu);
+bool x86_is_paging_mode(struct CPUState *cpu);
+bool x86_is_pae_enabled(struct CPUState *cpu);
+
+enum X86Seg;
+target_ulong linear_addr(struct CPUState *cpu, target_ulong addr, enum X86Seg seg);
+target_ulong linear_addr_size(struct CPUState *cpu, target_ulong addr, int size,
+ enum X86Seg seg);
+target_ulong linear_rip(struct CPUState *cpu, target_ulong rip);
+
+static inline uint64_t rdtscp(void)
+{
+ uint64_t tsc;
+ __asm__ __volatile__("rdtscp; " /* serializing read of tsc */
+ "shl $32,%%rdx; " /* shift higher 32 bits stored in rdx up */
+ "or %%rdx,%%rax" /* and or onto rax */
+ : "=a"(tsc) /* output to tsc variable */
+ :
+ : "%rcx", "%rdx"); /* rcx and rdx are clobbered */
+
+ return tsc;
+}
+
+#endif
diff --git a/target/i386/hvf/x86_cpuid.c b/target/i386/hvf/x86_cpuid.c
new file mode 100644
index 0000000000..9874a46e92
--- /dev/null
+++ b/target/i386/hvf/x86_cpuid.c
@@ -0,0 +1,166 @@
+/*
+ * i386 CPUID helper functions
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * cpuid
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "x86.h"
+#include "vmx.h"
+#include "sysemu/hvf.h"
+
+static uint64_t xgetbv(uint32_t xcr)
+{
+ uint32_t eax, edx;
+
+ __asm__ volatile ("xgetbv"
+ : "=a" (eax), "=d" (edx)
+ : "c" (xcr));
+
+ return (((uint64_t)edx) << 32) | eax;
+}
+
+static bool vmx_mpx_supported()
+{
+ uint64_t cap_exit, cap_entry;
+
+ hv_vmx_read_capability(HV_VMX_CAP_ENTRY, &cap_entry);
+ hv_vmx_read_capability(HV_VMX_CAP_EXIT, &cap_exit);
+
+ return ((cap_exit & (1 << 23)) && (cap_entry & (1 << 16)));
+}
+
+uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx,
+ int reg)
+{
+ uint64_t cap;
+ uint32_t eax, ebx, ecx, edx;
+
+ host_cpuid(func, idx, &eax, &ebx, &ecx, &edx);
+
+ switch (func) {
+ case 0:
+ eax = eax < (uint32_t)0xd ? eax : (uint32_t)0xd;
+ break;
+ case 1:
+ edx &= CPUID_FP87 | CPUID_VME | CPUID_DE | CPUID_PSE | CPUID_TSC |
+ CPUID_MSR | CPUID_PAE | CPUID_MCE | CPUID_CX8 | CPUID_APIC |
+ CPUID_SEP | CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV |
+ CPUID_PAT | CPUID_PSE36 | CPUID_CLFLUSH | CPUID_MMX |
+ CPUID_FXSR | CPUID_SSE | CPUID_SSE2 | CPUID_SS;
+ ecx &= CPUID_EXT_SSE3 | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSSE3 |
+ CPUID_EXT_FMA | CPUID_EXT_CX16 | CPUID_EXT_PCID |
+ CPUID_EXT_SSE41 | CPUID_EXT_SSE42 | CPUID_EXT_MOVBE |
+ CPUID_EXT_POPCNT | CPUID_EXT_AES | CPUID_EXT_XSAVE |
+ CPUID_EXT_AVX | CPUID_EXT_F16C | CPUID_EXT_RDRAND;
+ ecx |= CPUID_EXT_HYPERVISOR;
+ break;
+ case 6:
+ eax = CPUID_6_EAX_ARAT;
+ ebx = 0;
+ ecx = 0;
+ edx = 0;
+ break;
+ case 7:
+ if (idx == 0) {
+ ebx &= CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 |
+ CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 |
+ CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 |
+ CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_RTM |
+ CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX |
+ CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_AVX512IFMA |
+ CPUID_7_0_EBX_AVX512F | CPUID_7_0_EBX_AVX512PF |
+ CPUID_7_0_EBX_AVX512ER | CPUID_7_0_EBX_AVX512CD |
+ CPUID_7_0_EBX_CLFLUSHOPT | CPUID_7_0_EBX_CLWB |
+ CPUID_7_0_EBX_AVX512DQ | CPUID_7_0_EBX_SHA_NI |
+ CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512VL |
+ CPUID_7_0_EBX_INVPCID | CPUID_7_0_EBX_MPX;
+
+ if (!vmx_mpx_supported()) {
+ ebx &= ~CPUID_7_0_EBX_MPX;
+ }
+ hv_vmx_read_capability(HV_VMX_CAP_PROCBASED2, &cap);
+ if (!(cap & CPU_BASED2_INVPCID)) {
+ ebx &= ~CPUID_7_0_EBX_INVPCID;
+ }
+
+ ecx &= CPUID_7_0_ECX_AVX512BMI | CPUID_7_0_ECX_AVX512_VPOPCNTDQ;
+ edx &= CPUID_7_0_EDX_AVX512_4VNNIW | CPUID_7_0_EDX_AVX512_4FMAPS;
+ } else {
+ ebx = 0;
+ ecx = 0;
+ edx = 0;
+ }
+ eax = 0;
+ break;
+ case 0xD:
+ if (idx == 0) {
+ uint64_t host_xcr0 = xgetbv(0);
+ uint64_t supp_xcr0 = host_xcr0 & (XSTATE_FP_MASK | XSTATE_SSE_MASK |
+ XSTATE_YMM_MASK | XSTATE_BNDREGS_MASK |
+ XSTATE_BNDCSR_MASK | XSTATE_OPMASK_MASK |
+ XSTATE_ZMM_Hi256_MASK | XSTATE_Hi16_ZMM_MASK);
+ eax &= supp_xcr0;
+ if (!vmx_mpx_supported()) {
+ eax &= ~(XSTATE_BNDREGS_MASK | XSTATE_BNDCSR_MASK);
+ }
+ } else if (idx == 1) {
+ hv_vmx_read_capability(HV_VMX_CAP_PROCBASED2, &cap);
+ eax &= CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XGETBV1;
+ if (!(cap & CPU_BASED2_XSAVES_XRSTORS)) {
+ eax &= ~CPUID_XSAVE_XSAVES;
+ }
+ }
+ break;
+ case 0x80000001:
+ /* LM only if HVF in 64-bit mode */
+ edx &= CPUID_FP87 | CPUID_VME | CPUID_DE | CPUID_PSE | CPUID_TSC |
+ CPUID_MSR | CPUID_PAE | CPUID_MCE | CPUID_CX8 | CPUID_APIC |
+ CPUID_EXT2_SYSCALL | CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV |
+ CPUID_PAT | CPUID_PSE36 | CPUID_EXT2_MMXEXT | CPUID_MMX |
+ CPUID_FXSR | CPUID_EXT2_FXSR | CPUID_EXT2_PDPE1GB | CPUID_EXT2_3DNOWEXT |
+ CPUID_EXT2_3DNOW | CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX;
+ hv_vmx_read_capability(HV_VMX_CAP_PROCBASED, &cap);
+ if (!(cap & CPU_BASED_TSC_OFFSET)) {
+ edx &= ~CPUID_EXT2_RDTSCP;
+ }
+ ecx &= CPUID_EXT3_LAHF_LM | CPUID_EXT3_CMP_LEG | CPUID_EXT3_CR8LEG |
+ CPUID_EXT3_ABM | CPUID_EXT3_SSE4A | CPUID_EXT3_MISALIGNSSE |
+ CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_OSVW | CPUID_EXT3_XOP |
+ CPUID_EXT3_FMA4 | CPUID_EXT3_TBM;
+ break;
+ default:
+ return 0;
+ }
+
+ switch (reg) {
+ case R_EAX:
+ return eax;
+ case R_EBX:
+ return ebx;
+ case R_ECX:
+ return ecx;
+ case R_EDX:
+ return edx;
+ default:
+ return 0;
+ }
+}
diff --git a/target/i386/hvf/x86_decode.c b/target/i386/hvf/x86_decode.c
new file mode 100644
index 0000000000..bf93e8207d
--- /dev/null
+++ b/target/i386/hvf/x86_decode.c
@@ -0,0 +1,2186 @@
+/*
+ * Copyright (C) 2016 Veertu Inc,
+ * Copyright (C) 2017 Google Inc,
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu-common.h"
+#include "panic.h"
+#include "x86_decode.h"
+#include "string.h"
+#include "vmx.h"
+#include "x86_mmu.h"
+#include "x86_descr.h"
+
+#define OPCODE_ESCAPE 0xf
+
+static void decode_invalid(CPUX86State *env, struct x86_decode *decode)
+{
+ printf("%llx: failed to decode instruction ", env->hvf_emul->fetch_rip -
+ decode->len);
+ for (int i = 0; i < decode->opcode_len; i++) {
+ printf("%x ", decode->opcode[i]);
+ }
+ printf("\n");
+ VM_PANIC("decoder failed\n");
+}
+
+uint64_t sign(uint64_t val, int size)
+{
+ switch (size) {
+ case 1:
+ val = (int8_t)val;
+ break;
+ case 2:
+ val = (int16_t)val;
+ break;
+ case 4:
+ val = (int32_t)val;
+ break;
+ case 8:
+ val = (int64_t)val;
+ break;
+ default:
+ VM_PANIC_EX("%s invalid size %d\n", __func__, size);
+ break;
+ }
+ return val;
+}
+
+static inline uint64_t decode_bytes(CPUX86State *env, struct x86_decode *decode,
+ int size)
+{
+ target_ulong val = 0;
+
+ switch (size) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ break;
+ default:
+ VM_PANIC_EX("%s invalid size %d\n", __func__, size);
+ break;
+ }
+ target_ulong va = linear_rip(ENV_GET_CPU(env), RIP(env)) + decode->len;
+ vmx_read_mem(ENV_GET_CPU(env), &val, va, size);
+ decode->len += size;
+
+ return val;
+}
+
+static inline uint8_t decode_byte(CPUX86State *env, struct x86_decode *decode)
+{
+ return (uint8_t)decode_bytes(env, decode, 1);
+}
+
+static inline uint16_t decode_word(CPUX86State *env, struct x86_decode *decode)
+{
+ return (uint16_t)decode_bytes(env, decode, 2);
+}
+
+static inline uint32_t decode_dword(CPUX86State *env, struct x86_decode *decode)
+{
+ return (uint32_t)decode_bytes(env, decode, 4);
+}
+
+static inline uint64_t decode_qword(CPUX86State *env, struct x86_decode *decode)
+{
+ return decode_bytes(env, decode, 8);
+}
+
+static void decode_modrm_rm(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ op->type = X86_VAR_RM;
+}
+
+static void decode_modrm_reg(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ op->type = X86_VAR_REG;
+ op->reg = decode->modrm.reg;
+ op->ptr = get_reg_ref(env, op->reg, decode->rex.r, decode->operand_size);
+}
+
+static void decode_rax(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ op->type = X86_VAR_REG;
+ op->reg = R_EAX;
+ op->ptr = get_reg_ref(env, op->reg, 0, decode->operand_size);
+}
+
+static inline void decode_immediate(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *var, int size)
+{
+ var->type = X86_VAR_IMMEDIATE;
+ var->size = size;
+ switch (size) {
+ case 1:
+ var->val = decode_byte(env, decode);
+ break;
+ case 2:
+ var->val = decode_word(env, decode);
+ break;
+ case 4:
+ var->val = decode_dword(env, decode);
+ break;
+ case 8:
+ var->val = decode_qword(env, decode);
+ break;
+ default:
+ VM_PANIC_EX("bad size %d\n", size);
+ }
+}
+
+static void decode_imm8(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ decode_immediate(env, decode, op, 1);
+ op->type = X86_VAR_IMMEDIATE;
+}
+
+static void decode_imm8_signed(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ decode_immediate(env, decode, op, 1);
+ op->val = sign(op->val, 1);
+ op->type = X86_VAR_IMMEDIATE;
+}
+
+static void decode_imm16(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ decode_immediate(env, decode, op, 2);
+ op->type = X86_VAR_IMMEDIATE;
+}
+
+
+static void decode_imm(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ if (8 == decode->operand_size) {
+ decode_immediate(env, decode, op, 4);
+ op->val = sign(op->val, decode->operand_size);
+ } else {
+ decode_immediate(env, decode, op, decode->operand_size);
+ }
+ op->type = X86_VAR_IMMEDIATE;
+}
+
+static void decode_imm_signed(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ decode_immediate(env, decode, op, decode->operand_size);
+ op->val = sign(op->val, decode->operand_size);
+ op->type = X86_VAR_IMMEDIATE;
+}
+
+static void decode_imm_1(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ op->type = X86_VAR_IMMEDIATE;
+ op->val = 1;
+}
+
+static void decode_imm_0(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ op->type = X86_VAR_IMMEDIATE;
+ op->val = 0;
+}
+
+
+static void decode_pushseg(CPUX86State *env, struct x86_decode *decode)
+{
+ uint8_t op = (decode->opcode_len > 1) ? decode->opcode[1] : decode->opcode[0];
+
+ decode->op[0].type = X86_VAR_REG;
+ switch (op) {
+ case 0xe:
+ decode->op[0].reg = R_CS;
+ break;
+ case 0x16:
+ decode->op[0].reg = R_SS;
+ break;
+ case 0x1e:
+ decode->op[0].reg = R_DS;
+ break;
+ case 0x06:
+ decode->op[0].reg = R_ES;
+ break;
+ case 0xa0:
+ decode->op[0].reg = R_FS;
+ break;
+ case 0xa8:
+ decode->op[0].reg = R_GS;
+ break;
+ }
+}
+
+static void decode_popseg(CPUX86State *env, struct x86_decode *decode)
+{
+ uint8_t op = (decode->opcode_len > 1) ? decode->opcode[1] : decode->opcode[0];
+
+ decode->op[0].type = X86_VAR_REG;
+ switch (op) {
+ case 0xf:
+ decode->op[0].reg = R_CS;
+ break;
+ case 0x17:
+ decode->op[0].reg = R_SS;
+ break;
+ case 0x1f:
+ decode->op[0].reg = R_DS;
+ break;
+ case 0x07:
+ decode->op[0].reg = R_ES;
+ break;
+ case 0xa1:
+ decode->op[0].reg = R_FS;
+ break;
+ case 0xa9:
+ decode->op[0].reg = R_GS;
+ break;
+ }
+}
+
+static void decode_incgroup(CPUX86State *env, struct x86_decode *decode)
+{
+ decode->op[0].type = X86_VAR_REG;
+ decode->op[0].reg = decode->opcode[0] - 0x40;
+ decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
+ decode->operand_size);
+}
+
+static void decode_decgroup(CPUX86State *env, struct x86_decode *decode)
+{
+ decode->op[0].type = X86_VAR_REG;
+ decode->op[0].reg = decode->opcode[0] - 0x48;
+ decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
+ decode->operand_size);
+}
+
+static void decode_incgroup2(CPUX86State *env, struct x86_decode *decode)
+{
+ if (!decode->modrm.reg) {
+ decode->cmd = X86_DECODE_CMD_INC;
+ } else if (1 == decode->modrm.reg) {
+ decode->cmd = X86_DECODE_CMD_DEC;
+ }
+}
+
+static void decode_pushgroup(CPUX86State *env, struct x86_decode *decode)
+{
+ decode->op[0].type = X86_VAR_REG;
+ decode->op[0].reg = decode->opcode[0] - 0x50;
+ decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
+ decode->operand_size);
+}
+
+static void decode_popgroup(CPUX86State *env, struct x86_decode *decode)
+{
+ decode->op[0].type = X86_VAR_REG;
+ decode->op[0].reg = decode->opcode[0] - 0x58;
+ decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
+ decode->operand_size);
+}
+
+static void decode_jxx(CPUX86State *env, struct x86_decode *decode)
+{
+ decode->displacement = decode_bytes(env, decode, decode->operand_size);
+ decode->displacement_size = decode->operand_size;
+}
+
+static void decode_farjmp(CPUX86State *env, struct x86_decode *decode)
+{
+ decode->op[0].type = X86_VAR_IMMEDIATE;
+ decode->op[0].val = decode_bytes(env, decode, decode->operand_size);
+ decode->displacement = decode_word(env, decode);
+}
+
+static void decode_addgroup(CPUX86State *env, struct x86_decode *decode)
+{
+ enum x86_decode_cmd group[] = {
+ X86_DECODE_CMD_ADD,
+ X86_DECODE_CMD_OR,
+ X86_DECODE_CMD_ADC,
+ X86_DECODE_CMD_SBB,
+ X86_DECODE_CMD_AND,
+ X86_DECODE_CMD_SUB,
+ X86_DECODE_CMD_XOR,
+ X86_DECODE_CMD_CMP
+ };
+ decode->cmd = group[decode->modrm.reg];
+}
+
+static void decode_rotgroup(CPUX86State *env, struct x86_decode *decode)
+{
+ enum x86_decode_cmd group[] = {
+ X86_DECODE_CMD_ROL,
+ X86_DECODE_CMD_ROR,
+ X86_DECODE_CMD_RCL,
+ X86_DECODE_CMD_RCR,
+ X86_DECODE_CMD_SHL,
+ X86_DECODE_CMD_SHR,
+ X86_DECODE_CMD_SHL,
+ X86_DECODE_CMD_SAR
+ };
+ decode->cmd = group[decode->modrm.reg];
+}
+
+static void decode_f7group(CPUX86State *env, struct x86_decode *decode)
+{
+ enum x86_decode_cmd group[] = {
+ X86_DECODE_CMD_TST,
+ X86_DECODE_CMD_TST,
+ X86_DECODE_CMD_NOT,
+ X86_DECODE_CMD_NEG,
+ X86_DECODE_CMD_MUL,
+ X86_DECODE_CMD_IMUL_1,
+ X86_DECODE_CMD_DIV,
+ X86_DECODE_CMD_IDIV
+ };
+ decode->cmd = group[decode->modrm.reg];
+ decode_modrm_rm(env, decode, &decode->op[0]);
+
+ switch (decode->modrm.reg) {
+ case 0:
+ case 1:
+ decode_imm(env, decode, &decode->op[1]);
+ break;
+ case 2:
+ break;
+ case 3:
+ decode->op[1].type = X86_VAR_IMMEDIATE;
+ decode->op[1].val = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+static void decode_xchgroup(CPUX86State *env, struct x86_decode *decode)
+{
+ decode->op[0].type = X86_VAR_REG;
+ decode->op[0].reg = decode->opcode[0] - 0x90;
+ decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
+ decode->operand_size);
+}
+
+static void decode_movgroup(CPUX86State *env, struct x86_decode *decode)
+{
+ decode->op[0].type = X86_VAR_REG;
+ decode->op[0].reg = decode->opcode[0] - 0xb8;
+ decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
+ decode->operand_size);
+ decode_immediate(env, decode, &decode->op[1], decode->operand_size);
+}
+
+static void fetch_moffs(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ op->type = X86_VAR_OFFSET;
+ op->ptr = decode_bytes(env, decode, decode->addressing_size);
+}
+
+static void decode_movgroup8(CPUX86State *env, struct x86_decode *decode)
+{
+ decode->op[0].type = X86_VAR_REG;
+ decode->op[0].reg = decode->opcode[0] - 0xb0;
+ decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
+ decode->operand_size);
+ decode_immediate(env, decode, &decode->op[1], decode->operand_size);
+}
+
+static void decode_rcx(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ op->type = X86_VAR_REG;
+ op->reg = R_ECX;
+ op->ptr = get_reg_ref(env, op->reg, decode->rex.b, decode->operand_size);
+}
+
+struct decode_tbl {
+ uint8_t opcode;
+ enum x86_decode_cmd cmd;
+ uint8_t operand_size;
+ bool is_modrm;
+ void (*decode_op1)(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op1);
+ void (*decode_op2)(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op2);
+ void (*decode_op3)(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op3);
+ void (*decode_op4)(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op4);
+ void (*decode_postfix)(CPUX86State *env, struct x86_decode *decode);
+ uint32_t flags_mask;
+};
+
+struct decode_x87_tbl {
+ uint8_t opcode;
+ uint8_t modrm_reg;
+ uint8_t modrm_mod;
+ enum x86_decode_cmd cmd;
+ uint8_t operand_size;
+ bool rev;
+ bool pop;
+ void (*decode_op1)(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op1);
+ void (*decode_op2)(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op2);
+ void (*decode_postfix)(CPUX86State *env, struct x86_decode *decode);
+ uint32_t flags_mask;
+};
+
+struct decode_tbl invl_inst = {0x0, 0, 0, false, NULL, NULL, NULL, NULL,
+ decode_invalid};
+
+struct decode_tbl _decode_tbl1[255];
+struct decode_tbl _decode_tbl2[255];
+struct decode_x87_tbl _decode_tbl3[255];
+
+static void decode_x87_ins(CPUX86State *env, struct x86_decode *decode)
+{
+ struct decode_x87_tbl *decoder;
+
+ decode->is_fpu = true;
+ int mode = decode->modrm.mod == 3 ? 1 : 0;
+ int index = ((decode->opcode[0] & 0xf) << 4) | (mode << 3) |
+ decode->modrm.reg;
+
+ decoder = &_decode_tbl3[index];
+
+ decode->cmd = decoder->cmd;
+ if (decoder->operand_size) {
+ decode->operand_size = decoder->operand_size;
+ }
+ decode->flags_mask = decoder->flags_mask;
+ decode->fpop_stack = decoder->pop;
+ decode->frev = decoder->rev;
+
+ if (decoder->decode_op1) {
+ decoder->decode_op1(env, decode, &decode->op[0]);
+ }
+ if (decoder->decode_op2) {
+ decoder->decode_op2(env, decode, &decode->op[1]);
+ }
+ if (decoder->decode_postfix) {
+ decoder->decode_postfix(env, decode);
+ }
+
+ VM_PANIC_ON_EX(!decode->cmd, "x87 opcode %x %x (%x %x) not decoded\n",
+ decode->opcode[0], decode->modrm.modrm, decoder->modrm_reg,
+ decoder->modrm_mod);
+}
+
+static void decode_ffgroup(CPUX86State *env, struct x86_decode *decode)
+{
+ enum x86_decode_cmd group[] = {
+ X86_DECODE_CMD_INC,
+ X86_DECODE_CMD_DEC,
+ X86_DECODE_CMD_CALL_NEAR_ABS_INDIRECT,
+ X86_DECODE_CMD_CALL_FAR_ABS_INDIRECT,
+ X86_DECODE_CMD_JMP_NEAR_ABS_INDIRECT,
+ X86_DECODE_CMD_JMP_FAR_ABS_INDIRECT,
+ X86_DECODE_CMD_PUSH,
+ X86_DECODE_CMD_INVL,
+ X86_DECODE_CMD_INVL
+ };
+ decode->cmd = group[decode->modrm.reg];
+ if (decode->modrm.reg > 2) {
+ decode->flags_mask = 0;
+ }
+}
+
+static void decode_sldtgroup(CPUX86State *env, struct x86_decode *decode)
+{
+
+ enum x86_decode_cmd group[] = {
+ X86_DECODE_CMD_SLDT,
+ X86_DECODE_CMD_STR,
+ X86_DECODE_CMD_LLDT,
+ X86_DECODE_CMD_LTR,
+ X86_DECODE_CMD_VERR,
+ X86_DECODE_CMD_VERW,
+ X86_DECODE_CMD_INVL,
+ X86_DECODE_CMD_INVL
+ };
+ decode->cmd = group[decode->modrm.reg];
+ printf("%llx: decode_sldtgroup: %d\n", env->hvf_emul->fetch_rip,
+ decode->modrm.reg);
+}
+
+static void decode_lidtgroup(CPUX86State *env, struct x86_decode *decode)
+{
+ enum x86_decode_cmd group[] = {
+ X86_DECODE_CMD_SGDT,
+ X86_DECODE_CMD_SIDT,
+ X86_DECODE_CMD_LGDT,
+ X86_DECODE_CMD_LIDT,
+ X86_DECODE_CMD_SMSW,
+ X86_DECODE_CMD_LMSW,
+ X86_DECODE_CMD_LMSW,
+ X86_DECODE_CMD_INVLPG
+ };
+ decode->cmd = group[decode->modrm.reg];
+ if (0xf9 == decode->modrm.modrm) {
+ decode->opcode[decode->len++] = decode->modrm.modrm;
+ decode->cmd = X86_DECODE_CMD_RDTSCP;
+ }
+}
+
+static void decode_btgroup(CPUX86State *env, struct x86_decode *decode)
+{
+ enum x86_decode_cmd group[] = {
+ X86_DECODE_CMD_INVL,
+ X86_DECODE_CMD_INVL,
+ X86_DECODE_CMD_INVL,
+ X86_DECODE_CMD_INVL,
+ X86_DECODE_CMD_BT,
+ X86_DECODE_CMD_BTS,
+ X86_DECODE_CMD_BTR,
+ X86_DECODE_CMD_BTC
+ };
+ decode->cmd = group[decode->modrm.reg];
+}
+
+static void decode_x87_general(CPUX86State *env, struct x86_decode *decode)
+{
+ decode->is_fpu = true;
+}
+
+static void decode_x87_modrm_floatp(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ op->type = X87_VAR_FLOATP;
+}
+
+static void decode_x87_modrm_intp(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ op->type = X87_VAR_INTP;
+}
+
+static void decode_x87_modrm_bytep(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ op->type = X87_VAR_BYTEP;
+}
+
+static void decode_x87_modrm_st0(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ op->type = X87_VAR_REG;
+ op->reg = 0;
+}
+
+static void decode_decode_x87_modrm_st0(CPUX86State *env,
+ struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ op->type = X87_VAR_REG;
+ op->reg = decode->modrm.modrm & 7;
+}
+
+
+static void decode_aegroup(CPUX86State *env, struct x86_decode *decode)
+{
+ decode->is_fpu = true;
+ switch (decode->modrm.reg) {
+ case 0:
+ decode->cmd = X86_DECODE_CMD_FXSAVE;
+ decode_x87_modrm_bytep(env, decode, &decode->op[0]);
+ break;
+ case 1:
+ decode_x87_modrm_bytep(env, decode, &decode->op[0]);
+ decode->cmd = X86_DECODE_CMD_FXRSTOR;
+ break;
+ case 5:
+ if (decode->modrm.modrm == 0xe8) {
+ decode->cmd = X86_DECODE_CMD_LFENCE;
+ } else {
+ VM_PANIC("xrstor");
+ }
+ break;
+ case 6:
+ VM_PANIC_ON(decode->modrm.modrm != 0xf0);
+ decode->cmd = X86_DECODE_CMD_MFENCE;
+ break;
+ case 7:
+ if (decode->modrm.modrm == 0xf8) {
+ decode->cmd = X86_DECODE_CMD_SFENCE;
+ } else {
+ decode->cmd = X86_DECODE_CMD_CLFLUSH;
+ }
+ break;
+ default:
+ VM_PANIC_EX("0xae: reg %d\n", decode->modrm.reg);
+ break;
+ }
+}
+
+static void decode_bswap(CPUX86State *env, struct x86_decode *decode)
+{
+ decode->op[0].type = X86_VAR_REG;
+ decode->op[0].reg = decode->opcode[1] - 0xc8;
+ decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
+ decode->operand_size);
+}
+
+static void decode_d9_4(CPUX86State *env, struct x86_decode *decode)
+{
+ switch (decode->modrm.modrm) {
+ case 0xe0:
+ /* FCHS */
+ decode->cmd = X86_DECODE_CMD_FCHS;
+ break;
+ case 0xe1:
+ decode->cmd = X86_DECODE_CMD_FABS;
+ break;
+ case 0xe4:
+ VM_PANIC("FTST");
+ break;
+ case 0xe5:
+ /* FXAM */
+ decode->cmd = X86_DECODE_CMD_FXAM;
+ break;
+ default:
+ VM_PANIC("FLDENV");
+ break;
+ }
+}
+
+static void decode_db_4(CPUX86State *env, struct x86_decode *decode)
+{
+ switch (decode->modrm.modrm) {
+ case 0xe0:
+ VM_PANIC_EX("unhandled FNENI: %x %x\n", decode->opcode[0],
+ decode->modrm.modrm);
+ break;
+ case 0xe1:
+ VM_PANIC_EX("unhandled FNDISI: %x %x\n", decode->opcode[0],
+ decode->modrm.modrm);
+ break;
+ case 0xe2:
+ VM_PANIC_EX("unhandled FCLEX: %x %x\n", decode->opcode[0],
+ decode->modrm.modrm);
+ break;
+ case 0xe3:
+ decode->cmd = X86_DECODE_CMD_FNINIT;
+ break;
+ case 0xe4:
+ decode->cmd = X86_DECODE_CMD_FNSETPM;
+ break;
+ default:
+ VM_PANIC_EX("unhandled fpu opcode: %x %x\n", decode->opcode[0],
+ decode->modrm.modrm);
+ break;
+ }
+}
+
+
+#define RFLAGS_MASK_NONE 0
+#define RFLAGS_MASK_OSZAPC (RFLAGS_OF | RFLAGS_SF | RFLAGS_ZF | RFLAGS_AF | \
+ RFLAGS_PF | RFLAGS_CF)
+#define RFLAGS_MASK_LAHF (RFLAGS_SF | RFLAGS_ZF | RFLAGS_AF | RFLAGS_PF | \
+ RFLAGS_CF)
+#define RFLAGS_MASK_CF (RFLAGS_CF)
+#define RFLAGS_MASK_IF (RFLAGS_IF)
+#define RFLAGS_MASK_TF (RFLAGS_TF)
+#define RFLAGS_MASK_DF (RFLAGS_DF)
+#define RFLAGS_MASK_ZF (RFLAGS_ZF)
+
+struct decode_tbl _1op_inst[] = {
+ {0x0, X86_DECODE_CMD_ADD, 1, true, decode_modrm_rm, decode_modrm_reg, NULL,
+ NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x1, X86_DECODE_CMD_ADD, 0, true, decode_modrm_rm, decode_modrm_reg, NULL,
+ NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x2, X86_DECODE_CMD_ADD, 1, true, decode_modrm_reg, decode_modrm_rm, NULL,
+ NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x3, X86_DECODE_CMD_ADD, 0, true, decode_modrm_reg, decode_modrm_rm, NULL,
+ NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x4, X86_DECODE_CMD_ADD, 1, false, decode_rax, decode_imm8, NULL, NULL,
+ NULL, RFLAGS_MASK_OSZAPC},
+ {0x5, X86_DECODE_CMD_ADD, 0, false, decode_rax, decode_imm, NULL, NULL,
+ NULL, RFLAGS_MASK_OSZAPC},
+ {0x6, X86_DECODE_CMD_PUSH_SEG, 0, false, false, NULL, NULL, NULL,
+ decode_pushseg, RFLAGS_MASK_NONE},
+ {0x7, X86_DECODE_CMD_POP_SEG, 0, false, false, NULL, NULL, NULL,
+ decode_popseg, RFLAGS_MASK_NONE},
+ {0x8, X86_DECODE_CMD_OR, 1, true, decode_modrm_rm, decode_modrm_reg, NULL,
+ NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x9, X86_DECODE_CMD_OR, 0, true, decode_modrm_rm, decode_modrm_reg, NULL,
+ NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0xa, X86_DECODE_CMD_OR, 1, true, decode_modrm_reg, decode_modrm_rm, NULL,
+ NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0xb, X86_DECODE_CMD_OR, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0xc, X86_DECODE_CMD_OR, 1, false, decode_rax, decode_imm8,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0xd, X86_DECODE_CMD_OR, 0, false, decode_rax, decode_imm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+
+ {0xe, X86_DECODE_CMD_PUSH_SEG, 0, false, false,
+ NULL, NULL, NULL, decode_pushseg, RFLAGS_MASK_NONE},
+ {0xf, X86_DECODE_CMD_POP_SEG, 0, false, false,
+ NULL, NULL, NULL, decode_popseg, RFLAGS_MASK_NONE},
+
+ {0x10, X86_DECODE_CMD_ADC, 1, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x11, X86_DECODE_CMD_ADC, 0, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x12, X86_DECODE_CMD_ADC, 1, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x13, X86_DECODE_CMD_ADC, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x14, X86_DECODE_CMD_ADC, 1, false, decode_rax, decode_imm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x15, X86_DECODE_CMD_ADC, 0, false, decode_rax, decode_imm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+
+ {0x16, X86_DECODE_CMD_PUSH_SEG, 0, false, false,
+ NULL, NULL, NULL, decode_pushseg, RFLAGS_MASK_NONE},
+ {0x17, X86_DECODE_CMD_POP_SEG, 0, false, false,
+ NULL, NULL, NULL, decode_popseg, RFLAGS_MASK_NONE},
+
+ {0x18, X86_DECODE_CMD_SBB, 1, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x19, X86_DECODE_CMD_SBB, 0, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x1a, X86_DECODE_CMD_SBB, 1, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x1b, X86_DECODE_CMD_SBB, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x1c, X86_DECODE_CMD_SBB, 1, false, decode_rax, decode_imm8,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x1d, X86_DECODE_CMD_SBB, 0, false, decode_rax, decode_imm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+
+ {0x1e, X86_DECODE_CMD_PUSH_SEG, 0, false, false,
+ NULL, NULL, NULL, decode_pushseg, RFLAGS_MASK_NONE},
+ {0x1f, X86_DECODE_CMD_POP_SEG, 0, false, false,
+ NULL, NULL, NULL, decode_popseg, RFLAGS_MASK_NONE},
+
+ {0x20, X86_DECODE_CMD_AND, 1, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x21, X86_DECODE_CMD_AND, 0, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x22, X86_DECODE_CMD_AND, 1, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x23, X86_DECODE_CMD_AND, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x24, X86_DECODE_CMD_AND, 1, false, decode_rax, decode_imm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x25, X86_DECODE_CMD_AND, 0, false, decode_rax, decode_imm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x28, X86_DECODE_CMD_SUB, 1, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x29, X86_DECODE_CMD_SUB, 0, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x2a, X86_DECODE_CMD_SUB, 1, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x2b, X86_DECODE_CMD_SUB, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x2c, X86_DECODE_CMD_SUB, 1, false, decode_rax, decode_imm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x2d, X86_DECODE_CMD_SUB, 0, false, decode_rax, decode_imm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x2f, X86_DECODE_CMD_DAS, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x30, X86_DECODE_CMD_XOR, 1, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x31, X86_DECODE_CMD_XOR, 0, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x32, X86_DECODE_CMD_XOR, 1, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x33, X86_DECODE_CMD_XOR, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x34, X86_DECODE_CMD_XOR, 1, false, decode_rax, decode_imm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x35, X86_DECODE_CMD_XOR, 0, false, decode_rax, decode_imm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+
+ {0x38, X86_DECODE_CMD_CMP, 1, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x39, X86_DECODE_CMD_CMP, 0, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x3a, X86_DECODE_CMD_CMP, 1, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x3b, X86_DECODE_CMD_CMP, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x3c, X86_DECODE_CMD_CMP, 1, false, decode_rax, decode_imm8,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x3d, X86_DECODE_CMD_CMP, 0, false, decode_rax, decode_imm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+
+ {0x3f, X86_DECODE_CMD_AAS, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+
+ {0x40, X86_DECODE_CMD_INC, 0, false,
+ NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC},
+ {0x41, X86_DECODE_CMD_INC, 0, false,
+ NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC},
+ {0x42, X86_DECODE_CMD_INC, 0, false,
+ NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC},
+ {0x43, X86_DECODE_CMD_INC, 0, false,
+ NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC},
+ {0x44, X86_DECODE_CMD_INC, 0, false,
+ NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC},
+ {0x45, X86_DECODE_CMD_INC, 0, false,
+ NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC},
+ {0x46, X86_DECODE_CMD_INC, 0, false,
+ NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC},
+ {0x47, X86_DECODE_CMD_INC, 0, false,
+ NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC},
+
+ {0x48, X86_DECODE_CMD_DEC, 0, false,
+ NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC},
+ {0x49, X86_DECODE_CMD_DEC, 0, false,
+ NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC},
+ {0x4a, X86_DECODE_CMD_DEC, 0, false,
+ NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC},
+ {0x4b, X86_DECODE_CMD_DEC, 0, false,
+ NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC},
+ {0x4c, X86_DECODE_CMD_DEC, 0, false,
+ NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC},
+ {0x4d, X86_DECODE_CMD_DEC, 0, false,
+ NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC},
+ {0x4e, X86_DECODE_CMD_DEC, 0, false,
+ NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC},
+ {0x4f, X86_DECODE_CMD_DEC, 0, false,
+ NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC},
+
+ {0x50, X86_DECODE_CMD_PUSH, 0, false,
+ NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE},
+ {0x51, X86_DECODE_CMD_PUSH, 0, false,
+ NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE},
+ {0x52, X86_DECODE_CMD_PUSH, 0, false,
+ NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE},
+ {0x53, X86_DECODE_CMD_PUSH, 0, false,
+ NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE},
+ {0x54, X86_DECODE_CMD_PUSH, 0, false,
+ NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE},
+ {0x55, X86_DECODE_CMD_PUSH, 0, false,
+ NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE},
+ {0x56, X86_DECODE_CMD_PUSH, 0, false,
+ NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE},
+ {0x57, X86_DECODE_CMD_PUSH, 0, false,
+ NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE},
+
+ {0x58, X86_DECODE_CMD_POP, 0, false,
+ NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE},
+ {0x59, X86_DECODE_CMD_POP, 0, false,
+ NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE},
+ {0x5a, X86_DECODE_CMD_POP, 0, false,
+ NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE},
+ {0x5b, X86_DECODE_CMD_POP, 0, false,
+ NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE},
+ {0x5c, X86_DECODE_CMD_POP, 0, false,
+ NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE},
+ {0x5d, X86_DECODE_CMD_POP, 0, false,
+ NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE},
+ {0x5e, X86_DECODE_CMD_POP, 0, false,
+ NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE},
+ {0x5f, X86_DECODE_CMD_POP, 0, false,
+ NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE},
+
+ {0x60, X86_DECODE_CMD_PUSHA, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x61, X86_DECODE_CMD_POPA, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+
+ {0x68, X86_DECODE_CMD_PUSH, 0, false, decode_imm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x6a, X86_DECODE_CMD_PUSH, 0, false, decode_imm8_signed,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x69, X86_DECODE_CMD_IMUL_3, 0, true, decode_modrm_reg,
+ decode_modrm_rm, decode_imm, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x6b, X86_DECODE_CMD_IMUL_3, 0, true, decode_modrm_reg, decode_modrm_rm,
+ decode_imm8_signed, NULL, NULL, RFLAGS_MASK_OSZAPC},
+
+ {0x6c, X86_DECODE_CMD_INS, 1, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x6d, X86_DECODE_CMD_INS, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x6e, X86_DECODE_CMD_OUTS, 1, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x6f, X86_DECODE_CMD_OUTS, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+
+ {0x70, X86_DECODE_CMD_JXX, 1, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x71, X86_DECODE_CMD_JXX, 1, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x72, X86_DECODE_CMD_JXX, 1, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x73, X86_DECODE_CMD_JXX, 1, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x74, X86_DECODE_CMD_JXX, 1, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x75, X86_DECODE_CMD_JXX, 1, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x76, X86_DECODE_CMD_JXX, 1, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x77, X86_DECODE_CMD_JXX, 1, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x78, X86_DECODE_CMD_JXX, 1, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x79, X86_DECODE_CMD_JXX, 1, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x7a, X86_DECODE_CMD_JXX, 1, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x7b, X86_DECODE_CMD_JXX, 1, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x7c, X86_DECODE_CMD_JXX, 1, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x7d, X86_DECODE_CMD_JXX, 1, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x7e, X86_DECODE_CMD_JXX, 1, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x7f, X86_DECODE_CMD_JXX, 1, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+
+ {0x80, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_imm8,
+ NULL, NULL, decode_addgroup, RFLAGS_MASK_OSZAPC},
+ {0x81, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm,
+ NULL, NULL, decode_addgroup, RFLAGS_MASK_OSZAPC},
+ {0x82, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_imm8,
+ NULL, NULL, decode_addgroup, RFLAGS_MASK_OSZAPC},
+ {0x83, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm8_signed,
+ NULL, NULL, decode_addgroup, RFLAGS_MASK_OSZAPC},
+ {0x84, X86_DECODE_CMD_TST, 1, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x85, X86_DECODE_CMD_TST, 0, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0x86, X86_DECODE_CMD_XCHG, 1, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x87, X86_DECODE_CMD_XCHG, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x88, X86_DECODE_CMD_MOV, 1, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x89, X86_DECODE_CMD_MOV, 0, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x8a, X86_DECODE_CMD_MOV, 1, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x8b, X86_DECODE_CMD_MOV, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x8c, X86_DECODE_CMD_MOV_FROM_SEG, 0, true, decode_modrm_rm,
+ decode_modrm_reg, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x8d, X86_DECODE_CMD_LEA, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x8e, X86_DECODE_CMD_MOV_TO_SEG, 0, true, decode_modrm_reg,
+ decode_modrm_rm, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x8f, X86_DECODE_CMD_POP, 0, true, decode_modrm_rm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+
+ {0x90, X86_DECODE_CMD_NOP, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x91, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax,
+ NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE},
+ {0x92, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax,
+ NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE},
+ {0x93, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax,
+ NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE},
+ {0x94, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax,
+ NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE},
+ {0x95, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax,
+ NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE},
+ {0x96, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax,
+ NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE},
+ {0x97, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax,
+ NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE},
+
+ {0x98, X86_DECODE_CMD_CBW, 0, false, NULL, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x99, X86_DECODE_CMD_CWD, 0, false, NULL, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+
+ {0x9a, X86_DECODE_CMD_CALL_FAR, 0, false, NULL,
+ NULL, NULL, NULL, decode_farjmp, RFLAGS_MASK_NONE},
+
+ {0x9c, X86_DECODE_CMD_PUSHF, 0, false, NULL, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ /*{0x9d, X86_DECODE_CMD_POPF, 0, false, NULL, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_POPF},*/
+ {0x9e, X86_DECODE_CMD_SAHF, 0, false, NULL, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x9f, X86_DECODE_CMD_LAHF, 0, false, NULL, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_LAHF},
+
+ {0xa0, X86_DECODE_CMD_MOV, 1, false, decode_rax, fetch_moffs,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xa1, X86_DECODE_CMD_MOV, 0, false, decode_rax, fetch_moffs,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xa2, X86_DECODE_CMD_MOV, 1, false, fetch_moffs, decode_rax,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xa3, X86_DECODE_CMD_MOV, 0, false, fetch_moffs, decode_rax,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+
+ {0xa4, X86_DECODE_CMD_MOVS, 1, false, NULL, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xa5, X86_DECODE_CMD_MOVS, 0, false, NULL, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xa6, X86_DECODE_CMD_CMPS, 1, false, NULL, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0xa7, X86_DECODE_CMD_CMPS, 0, false, NULL, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0xaa, X86_DECODE_CMD_STOS, 1, false, NULL, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xab, X86_DECODE_CMD_STOS, 0, false, NULL, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xac, X86_DECODE_CMD_LODS, 1, false, NULL, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xad, X86_DECODE_CMD_LODS, 0, false, NULL, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xae, X86_DECODE_CMD_SCAS, 1, false, NULL, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0xaf, X86_DECODE_CMD_SCAS, 0, false, NULL, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+
+ {0xa8, X86_DECODE_CMD_TST, 1, false, decode_rax, decode_imm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0xa9, X86_DECODE_CMD_TST, 0, false, decode_rax, decode_imm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+
+ {0xb0, X86_DECODE_CMD_MOV, 1, false, NULL,
+ NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE},
+ {0xb1, X86_DECODE_CMD_MOV, 1, false, NULL,
+ NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE},
+ {0xb2, X86_DECODE_CMD_MOV, 1, false, NULL,
+ NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE},
+ {0xb3, X86_DECODE_CMD_MOV, 1, false, NULL,
+ NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE},
+ {0xb4, X86_DECODE_CMD_MOV, 1, false, NULL,
+ NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE},
+ {0xb5, X86_DECODE_CMD_MOV, 1, false, NULL,
+ NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE},
+ {0xb6, X86_DECODE_CMD_MOV, 1, false, NULL,
+ NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE},
+ {0xb7, X86_DECODE_CMD_MOV, 1, false, NULL,
+ NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE},
+
+ {0xb8, X86_DECODE_CMD_MOV, 0, false, NULL,
+ NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE},
+ {0xb9, X86_DECODE_CMD_MOV, 0, false, NULL,
+ NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE},
+ {0xba, X86_DECODE_CMD_MOV, 0, false, NULL,
+ NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE},
+ {0xbb, X86_DECODE_CMD_MOV, 0, false, NULL,
+ NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE},
+ {0xbc, X86_DECODE_CMD_MOV, 0, false, NULL,
+ NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE},
+ {0xbd, X86_DECODE_CMD_MOV, 0, false, NULL,
+ NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE},
+ {0xbe, X86_DECODE_CMD_MOV, 0, false, NULL,
+ NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE},
+ {0xbf, X86_DECODE_CMD_MOV, 0, false, NULL,
+ NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE},
+
+ {0xc0, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_imm8,
+ NULL, NULL, decode_rotgroup, RFLAGS_MASK_OSZAPC},
+ {0xc1, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm8,
+ NULL, NULL, decode_rotgroup, RFLAGS_MASK_OSZAPC},
+
+ {0xc2, X86_DECODE_RET_NEAR, 0, false, decode_imm16,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xc3, X86_DECODE_RET_NEAR, 0, false, NULL,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+
+ {0xc4, X86_DECODE_CMD_LES, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xc5, X86_DECODE_CMD_LDS, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+
+ {0xc6, X86_DECODE_CMD_MOV, 1, true, decode_modrm_rm, decode_imm8,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xc7, X86_DECODE_CMD_MOV, 0, true, decode_modrm_rm, decode_imm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+
+ {0xc8, X86_DECODE_CMD_ENTER, 0, false, decode_imm16, decode_imm8,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xc9, X86_DECODE_CMD_LEAVE, 0, false, NULL, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xca, X86_DECODE_RET_FAR, 0, false, decode_imm16, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xcb, X86_DECODE_RET_FAR, 0, false, decode_imm_0, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xcd, X86_DECODE_CMD_INT, 0, false, decode_imm8, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ /*{0xcf, X86_DECODE_CMD_IRET, 0, false, NULL, NULL,
+ NULL, NULL, NULL, RFLAGS_MASK_IRET},*/
+
+ {0xd0, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_imm_1,
+ NULL, NULL, decode_rotgroup, RFLAGS_MASK_OSZAPC},
+ {0xd1, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm_1,
+ NULL, NULL, decode_rotgroup, RFLAGS_MASK_OSZAPC},
+ {0xd2, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_rcx,
+ NULL, NULL, decode_rotgroup, RFLAGS_MASK_OSZAPC},
+ {0xd3, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_rcx,
+ NULL, NULL, decode_rotgroup, RFLAGS_MASK_OSZAPC},
+
+ {0xd4, X86_DECODE_CMD_AAM, 0, false, decode_imm8,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0xd5, X86_DECODE_CMD_AAD, 0, false, decode_imm8,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+
+ {0xd7, X86_DECODE_CMD_XLAT, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+
+ {0xd8, X86_DECODE_CMD_INVL, 0, true, NULL,
+ NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE},
+ {0xd9, X86_DECODE_CMD_INVL, 0, true, NULL,
+ NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE},
+ {0xda, X86_DECODE_CMD_INVL, 0, true, NULL,
+ NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE},
+ {0xdb, X86_DECODE_CMD_INVL, 0, true, NULL,
+ NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE},
+ {0xdc, X86_DECODE_CMD_INVL, 0, true, NULL,
+ NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE},
+ {0xdd, X86_DECODE_CMD_INVL, 0, true, NULL,
+ NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE},
+ {0xde, X86_DECODE_CMD_INVL, 0, true, NULL,
+ NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE},
+ {0xdf, X86_DECODE_CMD_INVL, 0, true, NULL,
+ NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE},
+
+ {0xe0, X86_DECODE_CMD_LOOP, 0, false, decode_imm8_signed,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xe1, X86_DECODE_CMD_LOOP, 0, false, decode_imm8_signed,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xe2, X86_DECODE_CMD_LOOP, 0, false, decode_imm8_signed,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+
+ {0xe3, X86_DECODE_CMD_JCXZ, 1, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+
+ {0xe4, X86_DECODE_CMD_IN, 1, false, decode_imm8,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xe5, X86_DECODE_CMD_IN, 0, false, decode_imm8,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xe6, X86_DECODE_CMD_OUT, 1, false, decode_imm8,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xe7, X86_DECODE_CMD_OUT, 0, false, decode_imm8,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xe8, X86_DECODE_CMD_CALL_NEAR, 0, false, decode_imm_signed,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xe9, X86_DECODE_CMD_JMP_NEAR, 0, false, decode_imm_signed,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xea, X86_DECODE_CMD_JMP_FAR, 0, false,
+ NULL, NULL, NULL, NULL, decode_farjmp, RFLAGS_MASK_NONE},
+ {0xeb, X86_DECODE_CMD_JMP_NEAR, 1, false, decode_imm8_signed,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xec, X86_DECODE_CMD_IN, 1, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xed, X86_DECODE_CMD_IN, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xee, X86_DECODE_CMD_OUT, 1, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xef, X86_DECODE_CMD_OUT, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+
+ {0xf4, X86_DECODE_CMD_HLT, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+
+ {0xf5, X86_DECODE_CMD_CMC, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_CF},
+
+ {0xf6, X86_DECODE_CMD_INVL, 1, true,
+ NULL, NULL, NULL, NULL, decode_f7group, RFLAGS_MASK_OSZAPC},
+ {0xf7, X86_DECODE_CMD_INVL, 0, true,
+ NULL, NULL, NULL, NULL, decode_f7group, RFLAGS_MASK_OSZAPC},
+
+ {0xf8, X86_DECODE_CMD_CLC, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_CF},
+ {0xf9, X86_DECODE_CMD_STC, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_CF},
+
+ {0xfa, X86_DECODE_CMD_CLI, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_IF},
+ {0xfb, X86_DECODE_CMD_STI, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_IF},
+ {0xfc, X86_DECODE_CMD_CLD, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_DF},
+ {0xfd, X86_DECODE_CMD_STD, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_DF},
+ {0xfe, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm,
+ NULL, NULL, NULL, decode_incgroup2, RFLAGS_MASK_OSZAPC},
+ {0xff, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm,
+ NULL, NULL, NULL, decode_ffgroup, RFLAGS_MASK_OSZAPC},
+};
+
+struct decode_tbl _2op_inst[] = {
+ {0x0, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm,
+ NULL, NULL, NULL, decode_sldtgroup, RFLAGS_MASK_NONE},
+ {0x1, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm,
+ NULL, NULL, NULL, decode_lidtgroup, RFLAGS_MASK_NONE},
+ {0x6, X86_DECODE_CMD_CLTS, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_TF},
+ {0x9, X86_DECODE_CMD_WBINVD, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x18, X86_DECODE_CMD_PREFETCH, 0, true,
+ NULL, NULL, NULL, NULL, decode_x87_general, RFLAGS_MASK_NONE},
+ {0x1f, X86_DECODE_CMD_NOP, 0, true, decode_modrm_rm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x20, X86_DECODE_CMD_MOV_FROM_CR, 0, true, decode_modrm_rm,
+ decode_modrm_reg, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x21, X86_DECODE_CMD_MOV_FROM_DR, 0, true, decode_modrm_rm,
+ decode_modrm_reg, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x22, X86_DECODE_CMD_MOV_TO_CR, 0, true, decode_modrm_reg,
+ decode_modrm_rm, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x23, X86_DECODE_CMD_MOV_TO_DR, 0, true, decode_modrm_reg,
+ decode_modrm_rm, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x30, X86_DECODE_CMD_WRMSR, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x31, X86_DECODE_CMD_RDTSC, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x32, X86_DECODE_CMD_RDMSR, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x40, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x41, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x42, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x43, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x44, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x45, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x46, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x47, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x48, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x49, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x4a, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x4b, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x4c, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x4d, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x4e, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x4f, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x77, X86_DECODE_CMD_EMMS, 0, false,
+ NULL, NULL, NULL, NULL, decode_x87_general, RFLAGS_MASK_NONE},
+ {0x82, X86_DECODE_CMD_JXX, 0, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x83, X86_DECODE_CMD_JXX, 0, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x84, X86_DECODE_CMD_JXX, 0, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x85, X86_DECODE_CMD_JXX, 0, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x86, X86_DECODE_CMD_JXX, 0, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x87, X86_DECODE_CMD_JXX, 0, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x88, X86_DECODE_CMD_JXX, 0, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x89, X86_DECODE_CMD_JXX, 0, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x8a, X86_DECODE_CMD_JXX, 0, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x8b, X86_DECODE_CMD_JXX, 0, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x8c, X86_DECODE_CMD_JXX, 0, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x8d, X86_DECODE_CMD_JXX, 0, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x8e, X86_DECODE_CMD_JXX, 0, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x8f, X86_DECODE_CMD_JXX, 0, false,
+ NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE},
+ {0x90, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x91, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x92, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x93, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x94, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x95, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x96, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x97, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x98, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x99, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x9a, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x9b, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x9c, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x9d, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x9e, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0x9f, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+
+ {0xb0, X86_DECODE_CMD_CMPXCHG, 1, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xb1, X86_DECODE_CMD_CMPXCHG, 0, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+
+ {0xb6, X86_DECODE_CMD_MOVZX, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xb7, X86_DECODE_CMD_MOVZX, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xb8, X86_DECODE_CMD_POPCNT, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0xbe, X86_DECODE_CMD_MOVSX, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xbf, X86_DECODE_CMD_MOVSX, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xa0, X86_DECODE_CMD_PUSH_SEG, 0, false, false,
+ NULL, NULL, NULL, decode_pushseg, RFLAGS_MASK_NONE},
+ {0xa1, X86_DECODE_CMD_POP_SEG, 0, false, false,
+ NULL, NULL, NULL, decode_popseg, RFLAGS_MASK_NONE},
+ {0xa2, X86_DECODE_CMD_CPUID, 0, false,
+ NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xa3, X86_DECODE_CMD_BT, 0, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_CF},
+ {0xa4, X86_DECODE_CMD_SHLD, 0, true, decode_modrm_rm, decode_modrm_reg,
+ decode_imm8, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0xa5, X86_DECODE_CMD_SHLD, 0, true, decode_modrm_rm, decode_modrm_reg,
+ decode_rcx, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0xa8, X86_DECODE_CMD_PUSH_SEG, 0, false, false,
+ NULL, NULL, NULL, decode_pushseg, RFLAGS_MASK_NONE},
+ {0xa9, X86_DECODE_CMD_POP_SEG, 0, false, false,
+ NULL, NULL, NULL, decode_popseg, RFLAGS_MASK_NONE},
+ {0xab, X86_DECODE_CMD_BTS, 0, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_CF},
+ {0xac, X86_DECODE_CMD_SHRD, 0, true, decode_modrm_rm, decode_modrm_reg,
+ decode_imm8, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0xad, X86_DECODE_CMD_SHRD, 0, true, decode_modrm_rm, decode_modrm_reg,
+ decode_rcx, NULL, NULL, RFLAGS_MASK_OSZAPC},
+
+ {0xae, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm,
+ NULL, NULL, NULL, decode_aegroup, RFLAGS_MASK_NONE},
+
+ {0xaf, X86_DECODE_CMD_IMUL_2, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0xb2, X86_DECODE_CMD_LSS, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xb3, X86_DECODE_CMD_BTR, 0, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0xba, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm8,
+ NULL, NULL, decode_btgroup, RFLAGS_MASK_OSZAPC},
+ {0xbb, X86_DECODE_CMD_BTC, 0, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0xbc, X86_DECODE_CMD_BSF, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+ {0xbd, X86_DECODE_CMD_BSR, 0, true, decode_modrm_reg, decode_modrm_rm,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+
+ {0xc1, X86_DECODE_CMD_XADD, 0, true, decode_modrm_rm, decode_modrm_reg,
+ NULL, NULL, NULL, RFLAGS_MASK_OSZAPC},
+
+ {0xc7, X86_DECODE_CMD_CMPXCHG8B, 0, true, decode_modrm_rm,
+ NULL, NULL, NULL, NULL, RFLAGS_MASK_ZF},
+
+ {0xc8, X86_DECODE_CMD_BSWAP, 0, false,
+ NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE},
+ {0xc9, X86_DECODE_CMD_BSWAP, 0, false,
+ NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE},
+ {0xca, X86_DECODE_CMD_BSWAP, 0, false,
+ NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE},
+ {0xcb, X86_DECODE_CMD_BSWAP, 0, false,
+ NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE},
+ {0xcc, X86_DECODE_CMD_BSWAP, 0, false,
+ NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE},
+ {0xcd, X86_DECODE_CMD_BSWAP, 0, false,
+ NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE},
+ {0xce, X86_DECODE_CMD_BSWAP, 0, false,
+ NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE},
+ {0xcf, X86_DECODE_CMD_BSWAP, 0, false,
+ NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE},
+};
+
+struct decode_x87_tbl invl_inst_x87 = {0x0, 0, 0, 0, 0, false, false, NULL,
+ NULL, decode_invalid, 0};
+
+struct decode_x87_tbl _x87_inst[] = {
+ {0xd8, 0, 3, X86_DECODE_CMD_FADD, 10, false, false,
+ decode_x87_modrm_st0, decode_decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xd8, 0, 0, X86_DECODE_CMD_FADD, 4, false, false, decode_x87_modrm_st0,
+ decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE},
+ {0xd8, 1, 3, X86_DECODE_CMD_FMUL, 10, false, false, decode_x87_modrm_st0,
+ decode_decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xd8, 1, 0, X86_DECODE_CMD_FMUL, 4, false, false, decode_x87_modrm_st0,
+ decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE},
+ {0xd8, 4, 3, X86_DECODE_CMD_FSUB, 10, false, false, decode_x87_modrm_st0,
+ decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xd8, 4, 0, X86_DECODE_CMD_FSUB, 4, false, false, decode_x87_modrm_st0,
+ decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE},
+ {0xd8, 5, 3, X86_DECODE_CMD_FSUB, 10, true, false, decode_x87_modrm_st0,
+ decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xd8, 5, 0, X86_DECODE_CMD_FSUB, 4, true, false, decode_x87_modrm_st0,
+ decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE},
+ {0xd8, 6, 3, X86_DECODE_CMD_FDIV, 10, false, false, decode_x87_modrm_st0,
+ decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xd8, 6, 0, X86_DECODE_CMD_FDIV, 4, false, false, decode_x87_modrm_st0,
+ decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE},
+ {0xd8, 7, 3, X86_DECODE_CMD_FDIV, 10, true, false, decode_x87_modrm_st0,
+ decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xd8, 7, 0, X86_DECODE_CMD_FDIV, 4, true, false, decode_x87_modrm_st0,
+ decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE},
+
+ {0xd9, 0, 3, X86_DECODE_CMD_FLD, 10, false, false,
+ decode_x87_modrm_st0, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xd9, 0, 0, X86_DECODE_CMD_FLD, 4, false, false,
+ decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xd9, 1, 3, X86_DECODE_CMD_FXCH, 10, false, false, decode_x87_modrm_st0,
+ decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xd9, 1, 0, X86_DECODE_CMD_INVL, 10, false, false,
+ decode_x87_modrm_st0, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xd9, 2, 3, X86_DECODE_CMD_INVL, 10, false, false,
+ decode_x87_modrm_st0, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xd9, 2, 0, X86_DECODE_CMD_FST, 4, false, false,
+ decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xd9, 3, 3, X86_DECODE_CMD_INVL, 10, false, false,
+ decode_x87_modrm_st0, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xd9, 3, 0, X86_DECODE_CMD_FST, 4, false, true,
+ decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xd9, 4, 3, X86_DECODE_CMD_INVL, 10, false, false,
+ decode_x87_modrm_st0, NULL, decode_d9_4, RFLAGS_MASK_NONE},
+ {0xd9, 4, 0, X86_DECODE_CMD_INVL, 4, false, false,
+ decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xd9, 5, 3, X86_DECODE_CMD_FLDxx, 10, false, false, NULL, NULL, NULL,
+ RFLAGS_MASK_NONE},
+ {0xd9, 5, 0, X86_DECODE_CMD_FLDCW, 2, false, false,
+ decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE},
+
+ {0xd9, 7, 3, X86_DECODE_CMD_FNSTCW, 2, false, false,
+ decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xd9, 7, 0, X86_DECODE_CMD_FNSTCW, 2, false, false,
+ decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE},
+
+ {0xda, 0, 3, X86_DECODE_CMD_FCMOV, 10, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xda, 0, 0, X86_DECODE_CMD_FADD, 4, false, false, decode_x87_modrm_st0,
+ decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE},
+ {0xda, 1, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0,
+ decode_decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xda, 1, 0, X86_DECODE_CMD_FMUL, 4, false, false, decode_x87_modrm_st0,
+ decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE},
+ {0xda, 2, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0,
+ decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xda, 3, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0,
+ decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xda, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL,
+ RFLAGS_MASK_NONE},
+ {0xda, 4, 0, X86_DECODE_CMD_FSUB, 4, false, false, decode_x87_modrm_st0,
+ decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE},
+ {0xda, 5, 3, X86_DECODE_CMD_FUCOM, 10, false, true, decode_x87_modrm_st0,
+ decode_decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xda, 5, 0, X86_DECODE_CMD_FSUB, 4, true, false, decode_x87_modrm_st0,
+ decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE},
+ {0xda, 6, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL,
+ RFLAGS_MASK_NONE},
+ {0xda, 6, 0, X86_DECODE_CMD_FDIV, 4, false, false, decode_x87_modrm_st0,
+ decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE},
+ {0xda, 7, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL,
+ RFLAGS_MASK_NONE},
+ {0xda, 7, 0, X86_DECODE_CMD_FDIV, 4, true, false, decode_x87_modrm_st0,
+ decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE},
+
+ {0xdb, 0, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0,
+ decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xdb, 0, 0, X86_DECODE_CMD_FLD, 4, false, false,
+ decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xdb, 1, 3, X86_DECODE_CMD_FCMOV, 10, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xdb, 2, 3, X86_DECODE_CMD_FCMOV, 10, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xdb, 2, 0, X86_DECODE_CMD_FST, 4, false, false,
+ decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xdb, 3, 3, X86_DECODE_CMD_FCMOV, 10, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xdb, 3, 0, X86_DECODE_CMD_FST, 4, false, true,
+ decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xdb, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL,
+ decode_db_4, RFLAGS_MASK_NONE},
+ {0xdb, 4, 0, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL,
+ RFLAGS_MASK_NONE},
+ {0xdb, 5, 3, X86_DECODE_CMD_FUCOMI, 10, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xdb, 5, 0, X86_DECODE_CMD_FLD, 10, false, false,
+ decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xdb, 7, 0, X86_DECODE_CMD_FST, 10, false, true,
+ decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE},
+
+ {0xdc, 0, 3, X86_DECODE_CMD_FADD, 10, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xdc, 0, 0, X86_DECODE_CMD_FADD, 8, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE},
+ {0xdc, 1, 3, X86_DECODE_CMD_FMUL, 10, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xdc, 1, 0, X86_DECODE_CMD_FMUL, 8, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE},
+ {0xdc, 4, 3, X86_DECODE_CMD_FSUB, 10, true, false,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xdc, 4, 0, X86_DECODE_CMD_FSUB, 8, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE},
+ {0xdc, 5, 3, X86_DECODE_CMD_FSUB, 10, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xdc, 5, 0, X86_DECODE_CMD_FSUB, 8, true, false,
+ decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE},
+ {0xdc, 6, 3, X86_DECODE_CMD_FDIV, 10, true, false,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xdc, 6, 0, X86_DECODE_CMD_FDIV, 8, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE},
+ {0xdc, 7, 3, X86_DECODE_CMD_FDIV, 10, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xdc, 7, 0, X86_DECODE_CMD_FDIV, 8, true, false,
+ decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE},
+
+ {0xdd, 0, 0, X86_DECODE_CMD_FLD, 8, false, false,
+ decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xdd, 1, 3, X86_DECODE_CMD_FXCH, 10, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xdd, 2, 3, X86_DECODE_CMD_FST, 10, false, false,
+ decode_x87_modrm_st0, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xdd, 2, 0, X86_DECODE_CMD_FST, 8, false, false,
+ decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xdd, 3, 3, X86_DECODE_CMD_FST, 10, false, true,
+ decode_x87_modrm_st0, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xdd, 3, 0, X86_DECODE_CMD_FST, 8, false, true,
+ decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xdd, 4, 3, X86_DECODE_CMD_FUCOM, 10, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xdd, 4, 0, X86_DECODE_CMD_FRSTOR, 8, false, false,
+ decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xdd, 5, 3, X86_DECODE_CMD_FUCOM, 10, false, true,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xdd, 7, 0, X86_DECODE_CMD_FNSTSW, 0, false, false,
+ decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xdd, 7, 3, X86_DECODE_CMD_FNSTSW, 0, false, false,
+ decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE},
+
+ {0xde, 0, 3, X86_DECODE_CMD_FADD, 10, false, true,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xde, 0, 0, X86_DECODE_CMD_FADD, 2, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE},
+ {0xde, 1, 3, X86_DECODE_CMD_FMUL, 10, false, true,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xde, 1, 0, X86_DECODE_CMD_FMUL, 2, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE},
+ {0xde, 4, 3, X86_DECODE_CMD_FSUB, 10, true, true,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xde, 4, 0, X86_DECODE_CMD_FSUB, 2, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE},
+ {0xde, 5, 3, X86_DECODE_CMD_FSUB, 10, false, true,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xde, 5, 0, X86_DECODE_CMD_FSUB, 2, true, false,
+ decode_x87_modrm_st0, decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE},
+ {0xde, 6, 3, X86_DECODE_CMD_FDIV, 10, true, true,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xde, 6, 0, X86_DECODE_CMD_FDIV, 2, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE},
+ {0xde, 7, 3, X86_DECODE_CMD_FDIV, 10, false, true,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xde, 7, 0, X86_DECODE_CMD_FDIV, 2, true, false,
+ decode_x87_modrm_st0, decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE},
+
+ {0xdf, 0, 0, X86_DECODE_CMD_FLD, 2, false, false,
+ decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xdf, 1, 3, X86_DECODE_CMD_FXCH, 10, false, false,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xdf, 2, 3, X86_DECODE_CMD_FST, 10, false, true,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xdf, 2, 0, X86_DECODE_CMD_FST, 2, false, false,
+ decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xdf, 3, 3, X86_DECODE_CMD_FST, 10, false, true,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xdf, 3, 0, X86_DECODE_CMD_FST, 2, false, true,
+ decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xdf, 4, 3, X86_DECODE_CMD_FNSTSW, 2, false, true,
+ decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xdf, 5, 3, X86_DECODE_CMD_FUCOMI, 10, false, true,
+ decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE},
+ {0xdf, 5, 0, X86_DECODE_CMD_FLD, 8, false, false,
+ decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE},
+ {0xdf, 7, 0, X86_DECODE_CMD_FST, 8, false, true,
+ decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE},
+};
+
+void calc_modrm_operand16(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ target_ulong ptr = 0;
+ X86Seg seg = R_DS;
+
+ if (!decode->modrm.mod && 6 == decode->modrm.rm) {
+ op->ptr = (uint16_t)decode->displacement;
+ goto calc_addr;
+ }
+
+ if (decode->displacement_size) {
+ ptr = sign(decode->displacement, decode->displacement_size);
+ }
+
+ switch (decode->modrm.rm) {
+ case 0:
+ ptr += BX(env) + SI(env);
+ break;
+ case 1:
+ ptr += BX(env) + DI(env);
+ break;
+ case 2:
+ ptr += BP(env) + SI(env);
+ seg = R_SS;
+ break;
+ case 3:
+ ptr += BP(env) + DI(env);
+ seg = R_SS;
+ break;
+ case 4:
+ ptr += SI(env);
+ break;
+ case 5:
+ ptr += DI(env);
+ break;
+ case 6:
+ ptr += BP(env);
+ seg = R_SS;
+ break;
+ case 7:
+ ptr += BX(env);
+ break;
+ }
+calc_addr:
+ if (X86_DECODE_CMD_LEA == decode->cmd) {
+ op->ptr = (uint16_t)ptr;
+ } else {
+ op->ptr = decode_linear_addr(env, decode, (uint16_t)ptr, seg);
+ }
+}
+
+target_ulong get_reg_ref(CPUX86State *env, int reg, int is_extended, int size)
+{
+ target_ulong ptr = 0;
+ int which = 0;
+
+ if (is_extended) {
+ reg |= R_R8;
+ }
+
+
+ switch (size) {
+ case 1:
+ if (is_extended || reg < 4) {
+ which = 1;
+ ptr = (target_ulong)&RL(env, reg);
+ } else {
+ which = 2;
+ ptr = (target_ulong)&RH(env, reg - 4);
+ }
+ break;
+ default:
+ which = 3;
+ ptr = (target_ulong)&RRX(env, reg);
+ break;
+ }
+ return ptr;
+}
+
+target_ulong get_reg_val(CPUX86State *env, int reg, int is_extended, int size)
+{
+ target_ulong val = 0;
+ memcpy(&val, (void *)get_reg_ref(env, reg, is_extended, size), size);
+ return val;
+}
+
+static target_ulong get_sib_val(CPUX86State *env, struct x86_decode *decode,
+ X86Seg *sel)
+{
+ target_ulong base = 0;
+ target_ulong scaled_index = 0;
+ int addr_size = decode->addressing_size;
+ int base_reg = decode->sib.base;
+ int index_reg = decode->sib.index;
+
+ *sel = R_DS;
+
+ if (decode->modrm.mod || base_reg != R_EBP) {
+ if (decode->rex.b) {
+ base_reg |= R_R8;
+ }
+ if (base_reg == R_ESP || base_reg == R_EBP) {
+ *sel = R_SS;
+ }
+ base = get_reg_val(env, decode->sib.base, decode->rex.b, addr_size);
+ }
+
+ if (decode->rex.x) {
+ index_reg |= R_R8;
+ }
+
+ if (index_reg != R_ESP) {
+ scaled_index = get_reg_val(env, index_reg, decode->rex.x, addr_size) <<
+ decode->sib.scale;
+ }
+ return base + scaled_index;
+}
+
+void calc_modrm_operand32(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ X86Seg seg = R_DS;
+ target_ulong ptr = 0;
+ int addr_size = decode->addressing_size;
+
+ if (decode->displacement_size) {
+ ptr = sign(decode->displacement, decode->displacement_size);
+ }
+
+ if (4 == decode->modrm.rm) {
+ ptr += get_sib_val(env, decode, &seg);
+ } else if (!decode->modrm.mod && 5 == decode->modrm.rm) {
+ if (x86_is_long_mode(ENV_GET_CPU(env))) {
+ ptr += RIP(env) + decode->len;
+ } else {
+ ptr = decode->displacement;
+ }
+ } else {
+ if (decode->modrm.rm == R_EBP || decode->modrm.rm == R_ESP) {
+ seg = R_SS;
+ }
+ ptr += get_reg_val(env, decode->modrm.rm, decode->rex.b, addr_size);
+ }
+
+ if (X86_DECODE_CMD_LEA == decode->cmd) {
+ op->ptr = (uint32_t)ptr;
+ } else {
+ op->ptr = decode_linear_addr(env, decode, (uint32_t)ptr, seg);
+ }
+}
+
+void calc_modrm_operand64(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ X86Seg seg = R_DS;
+ int32_t offset = 0;
+ int mod = decode->modrm.mod;
+ int rm = decode->modrm.rm;
+ target_ulong ptr;
+ int src = decode->modrm.rm;
+
+ if (decode->displacement_size) {
+ offset = sign(decode->displacement, decode->displacement_size);
+ }
+
+ if (4 == rm) {
+ ptr = get_sib_val(env, decode, &seg) + offset;
+ } else if (0 == mod && 5 == rm) {
+ ptr = RIP(env) + decode->len + (int32_t) offset;
+ } else {
+ ptr = get_reg_val(env, src, decode->rex.b, 8) + (int64_t) offset;
+ }
+
+ if (X86_DECODE_CMD_LEA == decode->cmd) {
+ op->ptr = ptr;
+ } else {
+ op->ptr = decode_linear_addr(env, decode, ptr, seg);
+ }
+}
+
+
+void calc_modrm_operand(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op)
+{
+ if (3 == decode->modrm.mod) {
+ op->reg = decode->modrm.reg;
+ op->type = X86_VAR_REG;
+ op->ptr = get_reg_ref(env, decode->modrm.rm, decode->rex.b,
+ decode->operand_size);
+ return;
+ }
+
+ switch (decode->addressing_size) {
+ case 2:
+ calc_modrm_operand16(env, decode, op);
+ break;
+ case 4:
+ calc_modrm_operand32(env, decode, op);
+ break;
+ case 8:
+ calc_modrm_operand64(env, decode, op);
+ break;
+ default:
+ VM_PANIC_EX("unsupported address size %d\n", decode->addressing_size);
+ break;
+ }
+}
+
+static void decode_prefix(CPUX86State *env, struct x86_decode *decode)
+{
+ while (1) {
+ uint8_t byte = decode_byte(env, decode);
+ switch (byte) {
+ case PREFIX_LOCK:
+ decode->lock = byte;
+ break;
+ case PREFIX_REPN:
+ case PREFIX_REP:
+ decode->rep = byte;
+ break;
+ case PREFIX_CS_SEG_OVEERIDE:
+ case PREFIX_SS_SEG_OVEERIDE:
+ case PREFIX_DS_SEG_OVEERIDE:
+ case PREFIX_ES_SEG_OVEERIDE:
+ case PREFIX_FS_SEG_OVEERIDE:
+ case PREFIX_GS_SEG_OVEERIDE:
+ decode->segment_override = byte;
+ break;
+ case PREFIX_OP_SIZE_OVERRIDE:
+ decode->op_size_override = byte;
+ break;
+ case PREFIX_ADDR_SIZE_OVERRIDE:
+ decode->addr_size_override = byte;
+ break;
+ case PREFIX_REX ... (PREFIX_REX + 0xf):
+ if (x86_is_long_mode(ENV_GET_CPU(env))) {
+ decode->rex.rex = byte;
+ break;
+ }
+ /* fall through when not in long mode */
+ default:
+ decode->len--;
+ return;
+ }
+ }
+}
+
+void set_addressing_size(CPUX86State *env, struct x86_decode *decode)
+{
+ decode->addressing_size = -1;
+ if (x86_is_real(ENV_GET_CPU(env)) || x86_is_v8086(ENV_GET_CPU(env))) {
+ if (decode->addr_size_override) {
+ decode->addressing_size = 4;
+ } else {
+ decode->addressing_size = 2;
+ }
+ } else if (!x86_is_long_mode(ENV_GET_CPU(env))) {
+ /* protected */
+ struct vmx_segment cs;
+ vmx_read_segment_descriptor(ENV_GET_CPU(env), &cs, R_CS);
+ /* check db */
+ if ((cs.ar >> 14) & 1) {
+ if (decode->addr_size_override) {
+ decode->addressing_size = 2;
+ } else {
+ decode->addressing_size = 4;
+ }
+ } else {
+ if (decode->addr_size_override) {
+ decode->addressing_size = 4;
+ } else {
+ decode->addressing_size = 2;
+ }
+ }
+ } else {
+ /* long */
+ if (decode->addr_size_override) {
+ decode->addressing_size = 4;
+ } else {
+ decode->addressing_size = 8;
+ }
+ }
+}
+
+void set_operand_size(CPUX86State *env, struct x86_decode *decode)
+{
+ decode->operand_size = -1;
+ if (x86_is_real(ENV_GET_CPU(env)) || x86_is_v8086(ENV_GET_CPU(env))) {
+ if (decode->op_size_override) {
+ decode->operand_size = 4;
+ } else {
+ decode->operand_size = 2;
+ }
+ } else if (!x86_is_long_mode(ENV_GET_CPU(env))) {
+ /* protected */
+ struct vmx_segment cs;
+ vmx_read_segment_descriptor(ENV_GET_CPU(env), &cs, R_CS);
+ /* check db */
+ if ((cs.ar >> 14) & 1) {
+ if (decode->op_size_override) {
+ decode->operand_size = 2;
+ } else{
+ decode->operand_size = 4;
+ }
+ } else {
+ if (decode->op_size_override) {
+ decode->operand_size = 4;
+ } else {
+ decode->operand_size = 2;
+ }
+ }
+ } else {
+ /* long */
+ if (decode->op_size_override) {
+ decode->operand_size = 2;
+ } else {
+ decode->operand_size = 4;
+ }
+
+ if (decode->rex.w) {
+ decode->operand_size = 8;
+ }
+ }
+}
+
+static void decode_sib(CPUX86State *env, struct x86_decode *decode)
+{
+ if ((decode->modrm.mod != 3) && (4 == decode->modrm.rm) &&
+ (decode->addressing_size != 2)) {
+ decode->sib.sib = decode_byte(env, decode);
+ decode->sib_present = true;
+ }
+}
+
+/* 16 bit modrm */
+int disp16_tbl[4][8] = {
+ {0, 0, 0, 0, 0, 0, 2, 0},
+ {1, 1, 1, 1, 1, 1, 1, 1},
+ {2, 2, 2, 2, 2, 2, 2, 2},
+ {0, 0, 0, 0, 0, 0, 0, 0}
+};
+
+/* 32/64-bit modrm */
+int disp32_tbl[4][8] = {
+ {0, 0, 0, 0, -1, 4, 0, 0},
+ {1, 1, 1, 1, 1, 1, 1, 1},
+ {4, 4, 4, 4, 4, 4, 4, 4},
+ {0, 0, 0, 0, 0, 0, 0, 0}
+};
+
+static inline void decode_displacement(CPUX86State *env, struct x86_decode *decode)
+{
+ int addressing_size = decode->addressing_size;
+ int mod = decode->modrm.mod;
+ int rm = decode->modrm.rm;
+
+ decode->displacement_size = 0;
+ switch (addressing_size) {
+ case 2:
+ decode->displacement_size = disp16_tbl[mod][rm];
+ if (decode->displacement_size) {
+ decode->displacement = (uint16_t)decode_bytes(env, decode,
+ decode->displacement_size);
+ }
+ break;
+ case 4:
+ case 8:
+ if (-1 == disp32_tbl[mod][rm]) {
+ if (5 == decode->sib.base) {
+ decode->displacement_size = 4;
+ }
+ } else {
+ decode->displacement_size = disp32_tbl[mod][rm];
+ }
+
+ if (decode->displacement_size) {
+ decode->displacement = (uint32_t)decode_bytes(env, decode,
+ decode->displacement_size);
+ }
+ break;
+ }
+}
+
+static inline void decode_modrm(CPUX86State *env, struct x86_decode *decode)
+{
+ decode->modrm.modrm = decode_byte(env, decode);
+ decode->is_modrm = true;
+
+ decode_sib(env, decode);
+ decode_displacement(env, decode);
+}
+
+static inline void decode_opcode_general(CPUX86State *env,
+ struct x86_decode *decode,
+ uint8_t opcode,
+ struct decode_tbl *inst_decoder)
+{
+ decode->cmd = inst_decoder->cmd;
+ if (inst_decoder->operand_size) {
+ decode->operand_size = inst_decoder->operand_size;
+ }
+ decode->flags_mask = inst_decoder->flags_mask;
+
+ if (inst_decoder->is_modrm) {
+ decode_modrm(env, decode);
+ }
+ if (inst_decoder->decode_op1) {
+ inst_decoder->decode_op1(env, decode, &decode->op[0]);
+ }
+ if (inst_decoder->decode_op2) {
+ inst_decoder->decode_op2(env, decode, &decode->op[1]);
+ }
+ if (inst_decoder->decode_op3) {
+ inst_decoder->decode_op3(env, decode, &decode->op[2]);
+ }
+ if (inst_decoder->decode_op4) {
+ inst_decoder->decode_op4(env, decode, &decode->op[3]);
+ }
+ if (inst_decoder->decode_postfix) {
+ inst_decoder->decode_postfix(env, decode);
+ }
+}
+
+static inline void decode_opcode_1(CPUX86State *env, struct x86_decode *decode,
+ uint8_t opcode)
+{
+ struct decode_tbl *inst_decoder = &_decode_tbl1[opcode];
+ decode_opcode_general(env, decode, opcode, inst_decoder);
+}
+
+
+static inline void decode_opcode_2(CPUX86State *env, struct x86_decode *decode,
+ uint8_t opcode)
+{
+ struct decode_tbl *inst_decoder = &_decode_tbl2[opcode];
+ decode_opcode_general(env, decode, opcode, inst_decoder);
+}
+
+static void decode_opcodes(CPUX86State *env, struct x86_decode *decode)
+{
+ uint8_t opcode;
+
+ opcode = decode_byte(env, decode);
+ decode->opcode[decode->opcode_len++] = opcode;
+ if (opcode != OPCODE_ESCAPE) {
+ decode_opcode_1(env, decode, opcode);
+ } else {
+ opcode = decode_byte(env, decode);
+ decode->opcode[decode->opcode_len++] = opcode;
+ decode_opcode_2(env, decode, opcode);
+ }
+}
+
+uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode)
+{
+ memset(decode, 0, sizeof(*decode));
+ decode_prefix(env, decode);
+ set_addressing_size(env, decode);
+ set_operand_size(env, decode);
+
+ decode_opcodes(env, decode);
+
+ return decode->len;
+}
+
+void init_decoder()
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(_decode_tbl2); i++) {
+ memcpy(_decode_tbl1, &invl_inst, sizeof(invl_inst));
+ }
+ for (i = 0; i < ARRAY_SIZE(_decode_tbl2); i++) {
+ memcpy(_decode_tbl2, &invl_inst, sizeof(invl_inst));
+ }
+ for (i = 0; i < ARRAY_SIZE(_decode_tbl3); i++) {
+ memcpy(_decode_tbl3, &invl_inst, sizeof(invl_inst_x87));
+
+ }
+ for (i = 0; i < ARRAY_SIZE(_1op_inst); i++) {
+ _decode_tbl1[_1op_inst[i].opcode] = _1op_inst[i];
+ }
+ for (i = 0; i < ARRAY_SIZE(_2op_inst); i++) {
+ _decode_tbl2[_2op_inst[i].opcode] = _2op_inst[i];
+ }
+ for (i = 0; i < ARRAY_SIZE(_x87_inst); i++) {
+ int index = ((_x87_inst[i].opcode & 0xf) << 4) |
+ ((_x87_inst[i].modrm_mod & 1) << 3) |
+ _x87_inst[i].modrm_reg;
+ _decode_tbl3[index] = _x87_inst[i];
+ }
+}
+
+
+const char *decode_cmd_to_string(enum x86_decode_cmd cmd)
+{
+ static const char *cmds[] = {"INVL", "PUSH", "PUSH_SEG", "POP", "POP_SEG",
+ "MOV", "MOVSX", "MOVZX", "CALL_NEAR", "CALL_NEAR_ABS_INDIRECT",
+ "CALL_FAR_ABS_INDIRECT", "CMD_CALL_FAR", "RET_NEAR", "RET_FAR", "ADD",
+ "OR", "ADC", "SBB", "AND", "SUB", "XOR", "CMP", "INC", "DEC", "TST",
+ "NOT", "NEG", "JMP_NEAR", "JMP_NEAR_ABS_INDIRECT", "JMP_FAR",
+ "JMP_FAR_ABS_INDIRECT", "LEA", "JXX", "JCXZ", "SETXX", "MOV_TO_SEG",
+ "MOV_FROM_SEG", "CLI", "STI", "CLD", "STD", "STC", "CLC", "OUT", "IN",
+ "INS", "OUTS", "LIDT", "SIDT", "LGDT", "SGDT", "SMSW", "LMSW",
+ "RDTSCP", "INVLPG", "MOV_TO_CR", "MOV_FROM_CR", "MOV_TO_DR",
+ "MOV_FROM_DR", "PUSHF", "POPF", "CPUID", "ROL", "ROR", "RCL", "RCR",
+ "SHL", "SAL", "SHR", "SHRD", "SHLD", "SAR", "DIV", "IDIV", "MUL",
+ "IMUL_3", "IMUL_2", "IMUL_1", "MOVS", "CMPS", "SCAS", "LODS", "STOS",
+ "BSWAP", "XCHG", "RDTSC", "RDMSR", "WRMSR", "ENTER", "LEAVE", "BT",
+ "BTS", "BTC", "BTR", "BSF", "BSR", "IRET", "INT", "POPA", "PUSHA",
+ "CWD", "CBW", "DAS", "AAD", "AAM", "AAS", "LOOP", "SLDT", "STR", "LLDT",
+ "LTR", "VERR", "VERW", "SAHF", "LAHF", "WBINVD", "LDS", "LSS", "LES",
+ "LGS", "LFS", "CMC", "XLAT", "NOP", "CMOV", "CLTS", "XADD", "HLT",
+ "CMPXCHG8B", "CMPXCHG", "POPCNT", "FNINIT", "FLD", "FLDxx", "FNSTCW",
+ "FNSTSW", "FNSETPM", "FSAVE", "FRSTOR", "FXSAVE", "FXRSTOR", "FDIV",
+ "FMUL", "FSUB", "FADD", "EMMS", "MFENCE", "SFENCE", "LFENCE",
+ "PREFETCH", "FST", "FABS", "FUCOM", "FUCOMI", "FLDCW",
+ "FXCH", "FCHS", "FCMOV", "FRNDINT", "FXAM", "LAST"};
+ return cmds[cmd];
+}
+
+target_ulong decode_linear_addr(CPUX86State *env, struct x86_decode *decode,
+ target_ulong addr, X86Seg seg)
+{
+ switch (decode->segment_override) {
+ case PREFIX_CS_SEG_OVEERIDE:
+ seg = R_CS;
+ break;
+ case PREFIX_SS_SEG_OVEERIDE:
+ seg = R_SS;
+ break;
+ case PREFIX_DS_SEG_OVEERIDE:
+ seg = R_DS;
+ break;
+ case PREFIX_ES_SEG_OVEERIDE:
+ seg = R_ES;
+ break;
+ case PREFIX_FS_SEG_OVEERIDE:
+ seg = R_FS;
+ break;
+ case PREFIX_GS_SEG_OVEERIDE:
+ seg = R_GS;
+ break;
+ default:
+ break;
+ }
+ return linear_addr_size(ENV_GET_CPU(env), addr, decode->addressing_size, seg);
+}
diff --git a/target/i386/hvf/x86_decode.h b/target/i386/hvf/x86_decode.h
new file mode 100644
index 0000000000..5ab6f31fa5
--- /dev/null
+++ b/target/i386/hvf/x86_decode.h
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2016 Veertu Inc,
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HVF_X86_DECODE_H
+#define HVF_X86_DECODE_H 1
+
+#include "cpu.h"
+#include "x86.h"
+
+typedef enum x86_prefix {
+ /* group 1 */
+ PREFIX_LOCK = 0xf0,
+ PREFIX_REPN = 0xf2,
+ PREFIX_REP = 0xf3,
+ /* group 2 */
+ PREFIX_CS_SEG_OVEERIDE = 0x2e,
+ PREFIX_SS_SEG_OVEERIDE = 0x36,
+ PREFIX_DS_SEG_OVEERIDE = 0x3e,
+ PREFIX_ES_SEG_OVEERIDE = 0x26,
+ PREFIX_FS_SEG_OVEERIDE = 0x64,
+ PREFIX_GS_SEG_OVEERIDE = 0x65,
+ /* group 3 */
+ PREFIX_OP_SIZE_OVERRIDE = 0x66,
+ /* group 4 */
+ PREFIX_ADDR_SIZE_OVERRIDE = 0x67,
+
+ PREFIX_REX = 0x40,
+} x86_prefix;
+
+enum x86_decode_cmd {
+ X86_DECODE_CMD_INVL = 0,
+
+ X86_DECODE_CMD_PUSH,
+ X86_DECODE_CMD_PUSH_SEG,
+ X86_DECODE_CMD_POP,
+ X86_DECODE_CMD_POP_SEG,
+ X86_DECODE_CMD_MOV,
+ X86_DECODE_CMD_MOVSX,
+ X86_DECODE_CMD_MOVZX,
+ X86_DECODE_CMD_CALL_NEAR,
+ X86_DECODE_CMD_CALL_NEAR_ABS_INDIRECT,
+ X86_DECODE_CMD_CALL_FAR_ABS_INDIRECT,
+ X86_DECODE_CMD_CALL_FAR,
+ X86_DECODE_RET_NEAR,
+ X86_DECODE_RET_FAR,
+ X86_DECODE_CMD_ADD,
+ X86_DECODE_CMD_OR,
+ X86_DECODE_CMD_ADC,
+ X86_DECODE_CMD_SBB,
+ X86_DECODE_CMD_AND,
+ X86_DECODE_CMD_SUB,
+ X86_DECODE_CMD_XOR,
+ X86_DECODE_CMD_CMP,
+ X86_DECODE_CMD_INC,
+ X86_DECODE_CMD_DEC,
+ X86_DECODE_CMD_TST,
+ X86_DECODE_CMD_NOT,
+ X86_DECODE_CMD_NEG,
+ X86_DECODE_CMD_JMP_NEAR,
+ X86_DECODE_CMD_JMP_NEAR_ABS_INDIRECT,
+ X86_DECODE_CMD_JMP_FAR,
+ X86_DECODE_CMD_JMP_FAR_ABS_INDIRECT,
+ X86_DECODE_CMD_LEA,
+ X86_DECODE_CMD_JXX,
+ X86_DECODE_CMD_JCXZ,
+ X86_DECODE_CMD_SETXX,
+ X86_DECODE_CMD_MOV_TO_SEG,
+ X86_DECODE_CMD_MOV_FROM_SEG,
+ X86_DECODE_CMD_CLI,
+ X86_DECODE_CMD_STI,
+ X86_DECODE_CMD_CLD,
+ X86_DECODE_CMD_STD,
+ X86_DECODE_CMD_STC,
+ X86_DECODE_CMD_CLC,
+ X86_DECODE_CMD_OUT,
+ X86_DECODE_CMD_IN,
+ X86_DECODE_CMD_INS,
+ X86_DECODE_CMD_OUTS,
+ X86_DECODE_CMD_LIDT,
+ X86_DECODE_CMD_SIDT,
+ X86_DECODE_CMD_LGDT,
+ X86_DECODE_CMD_SGDT,
+ X86_DECODE_CMD_SMSW,
+ X86_DECODE_CMD_LMSW,
+ X86_DECODE_CMD_RDTSCP,
+ X86_DECODE_CMD_INVLPG,
+ X86_DECODE_CMD_MOV_TO_CR,
+ X86_DECODE_CMD_MOV_FROM_CR,
+ X86_DECODE_CMD_MOV_TO_DR,
+ X86_DECODE_CMD_MOV_FROM_DR,
+ X86_DECODE_CMD_PUSHF,
+ X86_DECODE_CMD_POPF,
+ X86_DECODE_CMD_CPUID,
+ X86_DECODE_CMD_ROL,
+ X86_DECODE_CMD_ROR,
+ X86_DECODE_CMD_RCL,
+ X86_DECODE_CMD_RCR,
+ X86_DECODE_CMD_SHL,
+ X86_DECODE_CMD_SAL,
+ X86_DECODE_CMD_SHR,
+ X86_DECODE_CMD_SHRD,
+ X86_DECODE_CMD_SHLD,
+ X86_DECODE_CMD_SAR,
+ X86_DECODE_CMD_DIV,
+ X86_DECODE_CMD_IDIV,
+ X86_DECODE_CMD_MUL,
+ X86_DECODE_CMD_IMUL_3,
+ X86_DECODE_CMD_IMUL_2,
+ X86_DECODE_CMD_IMUL_1,
+ X86_DECODE_CMD_MOVS,
+ X86_DECODE_CMD_CMPS,
+ X86_DECODE_CMD_SCAS,
+ X86_DECODE_CMD_LODS,
+ X86_DECODE_CMD_STOS,
+ X86_DECODE_CMD_BSWAP,
+ X86_DECODE_CMD_XCHG,
+ X86_DECODE_CMD_RDTSC,
+ X86_DECODE_CMD_RDMSR,
+ X86_DECODE_CMD_WRMSR,
+ X86_DECODE_CMD_ENTER,
+ X86_DECODE_CMD_LEAVE,
+ X86_DECODE_CMD_BT,
+ X86_DECODE_CMD_BTS,
+ X86_DECODE_CMD_BTC,
+ X86_DECODE_CMD_BTR,
+ X86_DECODE_CMD_BSF,
+ X86_DECODE_CMD_BSR,
+ X86_DECODE_CMD_IRET,
+ X86_DECODE_CMD_INT,
+ X86_DECODE_CMD_POPA,
+ X86_DECODE_CMD_PUSHA,
+ X86_DECODE_CMD_CWD,
+ X86_DECODE_CMD_CBW,
+ X86_DECODE_CMD_DAS,
+ X86_DECODE_CMD_AAD,
+ X86_DECODE_CMD_AAM,
+ X86_DECODE_CMD_AAS,
+ X86_DECODE_CMD_LOOP,
+ X86_DECODE_CMD_SLDT,
+ X86_DECODE_CMD_STR,
+ X86_DECODE_CMD_LLDT,
+ X86_DECODE_CMD_LTR,
+ X86_DECODE_CMD_VERR,
+ X86_DECODE_CMD_VERW,
+ X86_DECODE_CMD_SAHF,
+ X86_DECODE_CMD_LAHF,
+ X86_DECODE_CMD_WBINVD,
+ X86_DECODE_CMD_LDS,
+ X86_DECODE_CMD_LSS,
+ X86_DECODE_CMD_LES,
+ X86_DECODE_XMD_LGS,
+ X86_DECODE_CMD_LFS,
+ X86_DECODE_CMD_CMC,
+ X86_DECODE_CMD_XLAT,
+ X86_DECODE_CMD_NOP,
+ X86_DECODE_CMD_CMOV,
+ X86_DECODE_CMD_CLTS,
+ X86_DECODE_CMD_XADD,
+ X86_DECODE_CMD_HLT,
+ X86_DECODE_CMD_CMPXCHG8B,
+ X86_DECODE_CMD_CMPXCHG,
+ X86_DECODE_CMD_POPCNT,
+
+ X86_DECODE_CMD_FNINIT,
+ X86_DECODE_CMD_FLD,
+ X86_DECODE_CMD_FLDxx,
+ X86_DECODE_CMD_FNSTCW,
+ X86_DECODE_CMD_FNSTSW,
+ X86_DECODE_CMD_FNSETPM,
+ X86_DECODE_CMD_FSAVE,
+ X86_DECODE_CMD_FRSTOR,
+ X86_DECODE_CMD_FXSAVE,
+ X86_DECODE_CMD_FXRSTOR,
+ X86_DECODE_CMD_FDIV,
+ X86_DECODE_CMD_FMUL,
+ X86_DECODE_CMD_FSUB,
+ X86_DECODE_CMD_FADD,
+ X86_DECODE_CMD_EMMS,
+ X86_DECODE_CMD_MFENCE,
+ X86_DECODE_CMD_SFENCE,
+ X86_DECODE_CMD_LFENCE,
+ X86_DECODE_CMD_PREFETCH,
+ X86_DECODE_CMD_CLFLUSH,
+ X86_DECODE_CMD_FST,
+ X86_DECODE_CMD_FABS,
+ X86_DECODE_CMD_FUCOM,
+ X86_DECODE_CMD_FUCOMI,
+ X86_DECODE_CMD_FLDCW,
+ X86_DECODE_CMD_FXCH,
+ X86_DECODE_CMD_FCHS,
+ X86_DECODE_CMD_FCMOV,
+ X86_DECODE_CMD_FRNDINT,
+ X86_DECODE_CMD_FXAM,
+
+ X86_DECODE_CMD_LAST,
+};
+
+const char *decode_cmd_to_string(enum x86_decode_cmd cmd);
+
+typedef struct x86_modrm {
+ union {
+ uint8_t modrm;
+ struct {
+ uint8_t rm:3;
+ uint8_t reg:3;
+ uint8_t mod:2;
+ };
+ };
+} __attribute__ ((__packed__)) x86_modrm;
+
+typedef struct x86_sib {
+ union {
+ uint8_t sib;
+ struct {
+ uint8_t base:3;
+ uint8_t index:3;
+ uint8_t scale:2;
+ };
+ };
+} __attribute__ ((__packed__)) x86_sib;
+
+typedef struct x86_rex {
+ union {
+ uint8_t rex;
+ struct {
+ uint8_t b:1;
+ uint8_t x:1;
+ uint8_t r:1;
+ uint8_t w:1;
+ uint8_t unused:4;
+ };
+ };
+} __attribute__ ((__packed__)) x86_rex;
+
+typedef enum x86_var_type {
+ X86_VAR_IMMEDIATE,
+ X86_VAR_OFFSET,
+ X86_VAR_REG,
+ X86_VAR_RM,
+
+ /* for floating point computations */
+ X87_VAR_REG,
+ X87_VAR_FLOATP,
+ X87_VAR_INTP,
+ X87_VAR_BYTEP,
+} x86_var_type;
+
+typedef struct x86_decode_op {
+ enum x86_var_type type;
+ int size;
+
+ int reg;
+ target_ulong val;
+
+ target_ulong ptr;
+} x86_decode_op;
+
+typedef struct x86_decode {
+ int len;
+ uint8_t opcode[4];
+ uint8_t opcode_len;
+ enum x86_decode_cmd cmd;
+ int addressing_size;
+ int operand_size;
+ int lock;
+ int rep;
+ int op_size_override;
+ int addr_size_override;
+ int segment_override;
+ int control_change_inst;
+ bool fwait;
+ bool fpop_stack;
+ bool frev;
+
+ uint32_t displacement;
+ uint8_t displacement_size;
+ struct x86_rex rex;
+ bool is_modrm;
+ bool sib_present;
+ struct x86_sib sib;
+ struct x86_modrm modrm;
+ struct x86_decode_op op[4];
+ bool is_fpu;
+ uint32_t flags_mask;
+
+} x86_decode;
+
+uint64_t sign(uint64_t val, int size);
+
+uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode);
+
+target_ulong get_reg_ref(CPUX86State *env, int reg, int is_extended, int size);
+target_ulong get_reg_val(CPUX86State *env, int reg, int is_extended, int size);
+void calc_modrm_operand(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op);
+target_ulong decode_linear_addr(CPUX86State *env, struct x86_decode *decode,
+ target_ulong addr, enum X86Seg seg);
+
+void init_decoder(void);
+void calc_modrm_operand16(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op);
+void calc_modrm_operand32(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op);
+void calc_modrm_operand64(CPUX86State *env, struct x86_decode *decode,
+ struct x86_decode_op *op);
+void set_addressing_size(CPUX86State *env, struct x86_decode *decode);
+void set_operand_size(CPUX86State *env, struct x86_decode *decode);
+
+#endif
diff --git a/target/i386/hvf/x86_descr.c b/target/i386/hvf/x86_descr.c
new file mode 100644
index 0000000000..8c05c34f33
--- /dev/null
+++ b/target/i386/hvf/x86_descr.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2016 Veertu Inc,
+ * Copyright (C) 2017 Google Inc,
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu-common.h"
+#include "vmx.h"
+#include "x86_descr.h"
+
+#define VMX_SEGMENT_FIELD(seg) \
+ [R_##seg] = { \
+ .selector = VMCS_GUEST_##seg##_SELECTOR, \
+ .base = VMCS_GUEST_##seg##_BASE, \
+ .limit = VMCS_GUEST_##seg##_LIMIT, \
+ .ar_bytes = VMCS_GUEST_##seg##_ACCESS_RIGHTS, \
+}
+
+static const struct vmx_segment_field {
+ int selector;
+ int base;
+ int limit;
+ int ar_bytes;
+} vmx_segment_fields[] = {
+ VMX_SEGMENT_FIELD(ES),
+ VMX_SEGMENT_FIELD(CS),
+ VMX_SEGMENT_FIELD(SS),
+ VMX_SEGMENT_FIELD(DS),
+ VMX_SEGMENT_FIELD(FS),
+ VMX_SEGMENT_FIELD(GS),
+ VMX_SEGMENT_FIELD(LDTR),
+ VMX_SEGMENT_FIELD(TR),
+};
+
+uint32_t vmx_read_segment_limit(CPUState *cpu, X86Seg seg)
+{
+ return (uint32_t)rvmcs(cpu->hvf_fd, vmx_segment_fields[seg].limit);
+}
+
+uint32_t vmx_read_segment_ar(CPUState *cpu, X86Seg seg)
+{
+ return (uint32_t)rvmcs(cpu->hvf_fd, vmx_segment_fields[seg].ar_bytes);
+}
+
+uint64_t vmx_read_segment_base(CPUState *cpu, X86Seg seg)
+{
+ return rvmcs(cpu->hvf_fd, vmx_segment_fields[seg].base);
+}
+
+x68_segment_selector vmx_read_segment_selector(CPUState *cpu, X86Seg seg)
+{
+ x68_segment_selector sel;
+ sel.sel = rvmcs(cpu->hvf_fd, vmx_segment_fields[seg].selector);
+ return sel;
+}
+
+void vmx_write_segment_selector(struct CPUState *cpu, x68_segment_selector selector, X86Seg seg)
+{
+ wvmcs(cpu->hvf_fd, vmx_segment_fields[seg].selector, selector.sel);
+}
+
+void vmx_read_segment_descriptor(struct CPUState *cpu, struct vmx_segment *desc, X86Seg seg)
+{
+ desc->sel = rvmcs(cpu->hvf_fd, vmx_segment_fields[seg].selector);
+ desc->base = rvmcs(cpu->hvf_fd, vmx_segment_fields[seg].base);
+ desc->limit = rvmcs(cpu->hvf_fd, vmx_segment_fields[seg].limit);
+ desc->ar = rvmcs(cpu->hvf_fd, vmx_segment_fields[seg].ar_bytes);
+}
+
+void vmx_write_segment_descriptor(CPUState *cpu, struct vmx_segment *desc, X86Seg seg)
+{
+ const struct vmx_segment_field *sf = &vmx_segment_fields[seg];
+
+ wvmcs(cpu->hvf_fd, sf->base, desc->base);
+ wvmcs(cpu->hvf_fd, sf->limit, desc->limit);
+ wvmcs(cpu->hvf_fd, sf->selector, desc->sel);
+ wvmcs(cpu->hvf_fd, sf->ar_bytes, desc->ar);
+}
+
+void x86_segment_descriptor_to_vmx(struct CPUState *cpu, x68_segment_selector selector, struct x86_segment_descriptor *desc, struct vmx_segment *vmx_desc)
+{
+ vmx_desc->sel = selector.sel;
+ vmx_desc->base = x86_segment_base(desc);
+ vmx_desc->limit = x86_segment_limit(desc);
+
+ vmx_desc->ar = (selector.sel ? 0 : 1) << 16 |
+ desc->g << 15 |
+ desc->db << 14 |
+ desc->l << 13 |
+ desc->avl << 12 |
+ desc->p << 7 |
+ desc->dpl << 5 |
+ desc->s << 4 |
+ desc->type;
+}
+
+void vmx_segment_to_x86_descriptor(struct CPUState *cpu, struct vmx_segment *vmx_desc, struct x86_segment_descriptor *desc)
+{
+ x86_set_segment_limit(desc, vmx_desc->limit);
+ x86_set_segment_base(desc, vmx_desc->base);
+
+ desc->type = vmx_desc->ar & 15;
+ desc->s = (vmx_desc->ar >> 4) & 1;
+ desc->dpl = (vmx_desc->ar >> 5) & 3;
+ desc->p = (vmx_desc->ar >> 7) & 1;
+ desc->avl = (vmx_desc->ar >> 12) & 1;
+ desc->l = (vmx_desc->ar >> 13) & 1;
+ desc->db = (vmx_desc->ar >> 14) & 1;
+ desc->g = (vmx_desc->ar >> 15) & 1;
+}
+
diff --git a/target/i386/hvf/x86_descr.h b/target/i386/hvf/x86_descr.h
new file mode 100644
index 0000000000..25a2b1731c
--- /dev/null
+++ b/target/i386/hvf/x86_descr.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 Veertu Inc,
+ * Copyright (C) 2017 Google Inc,
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HVF_X86_DESCR_H
+#define HVF_X86_DESCR_H 1
+
+#include "x86.h"
+
+typedef struct vmx_segment {
+ uint16_t sel;
+ uint64_t base;
+ uint64_t limit;
+ uint64_t ar;
+} vmx_segment;
+
+/* deal with vmstate descriptors */
+void vmx_read_segment_descriptor(struct CPUState *cpu,
+ struct vmx_segment *desc, enum X86Seg seg);
+void vmx_write_segment_descriptor(CPUState *cpu, struct vmx_segment *desc,
+ enum X86Seg seg);
+
+x68_segment_selector vmx_read_segment_selector(struct CPUState *cpu,
+ enum X86Seg seg);
+void vmx_write_segment_selector(struct CPUState *cpu,
+ x68_segment_selector selector,
+ enum X86Seg seg);
+
+uint64_t vmx_read_segment_base(struct CPUState *cpu, enum X86Seg seg);
+void vmx_write_segment_base(struct CPUState *cpu, enum X86Seg seg,
+ uint64_t base);
+
+void x86_segment_descriptor_to_vmx(struct CPUState *cpu,
+ x68_segment_selector selector,
+ struct x86_segment_descriptor *desc,
+ struct vmx_segment *vmx_desc);
+
+uint32_t vmx_read_segment_limit(CPUState *cpu, enum X86Seg seg);
+uint32_t vmx_read_segment_ar(CPUState *cpu, enum X86Seg seg);
+void vmx_segment_to_x86_descriptor(struct CPUState *cpu,
+ struct vmx_segment *vmx_desc,
+ struct x86_segment_descriptor *desc);
+
+#endif
diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c
new file mode 100644
index 0000000000..3ea18edc68
--- /dev/null
+++ b/target/i386/hvf/x86_emu.c
@@ -0,0 +1,1483 @@
+/*
+ * Copyright (C) 2016 Veertu Inc,
+ * Copyright (C) 2017 Google Inc,
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001-2012 The Bochs Project
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA B 02110-1301 USA
+/////////////////////////////////////////////////////////////////////////
+
+#include "qemu/osdep.h"
+#include "panic.h"
+#include "qemu-common.h"
+#include "x86_decode.h"
+#include "x86.h"
+#include "x86_emu.h"
+#include "x86_mmu.h"
+#include "x86_flags.h"
+#include "vmcs.h"
+#include "vmx.h"
+
+void hvf_handle_io(struct CPUState *cpu, uint16_t port, void *data,
+ int direction, int size, uint32_t count);
+
+#define EXEC_2OP_FLAGS_CMD(env, decode, cmd, FLAGS_FUNC, save_res) \
+{ \
+ fetch_operands(env, decode, 2, true, true, false); \
+ switch (decode->operand_size) { \
+ case 1: \
+ { \
+ uint8_t v1 = (uint8_t)decode->op[0].val; \
+ uint8_t v2 = (uint8_t)decode->op[1].val; \
+ uint8_t diff = v1 cmd v2; \
+ if (save_res) { \
+ write_val_ext(env, decode->op[0].ptr, diff, 1); \
+ } \
+ FLAGS_FUNC##8(env, v1, v2, diff); \
+ break; \
+ } \
+ case 2: \
+ { \
+ uint16_t v1 = (uint16_t)decode->op[0].val; \
+ uint16_t v2 = (uint16_t)decode->op[1].val; \
+ uint16_t diff = v1 cmd v2; \
+ if (save_res) { \
+ write_val_ext(env, decode->op[0].ptr, diff, 2); \
+ } \
+ FLAGS_FUNC##16(env, v1, v2, diff); \
+ break; \
+ } \
+ case 4: \
+ { \
+ uint32_t v1 = (uint32_t)decode->op[0].val; \
+ uint32_t v2 = (uint32_t)decode->op[1].val; \
+ uint32_t diff = v1 cmd v2; \
+ if (save_res) { \
+ write_val_ext(env, decode->op[0].ptr, diff, 4); \
+ } \
+ FLAGS_FUNC##32(env, v1, v2, diff); \
+ break; \
+ } \
+ default: \
+ VM_PANIC("bad size\n"); \
+ } \
+} \
+
+target_ulong read_reg(CPUX86State *env, int reg, int size)
+{
+ switch (size) {
+ case 1:
+ return env->hvf_emul->regs[reg].lx;
+ case 2:
+ return env->hvf_emul->regs[reg].rx;
+ case 4:
+ return env->hvf_emul->regs[reg].erx;
+ case 8:
+ return env->hvf_emul->regs[reg].rrx;
+ default:
+ abort();
+ }
+ return 0;
+}
+
+void write_reg(CPUX86State *env, int reg, target_ulong val, int size)
+{
+ switch (size) {
+ case 1:
+ env->hvf_emul->regs[reg].lx = val;
+ break;
+ case 2:
+ env->hvf_emul->regs[reg].rx = val;
+ break;
+ case 4:
+ env->hvf_emul->regs[reg].rrx = (uint32_t)val;
+ break;
+ case 8:
+ env->hvf_emul->regs[reg].rrx = val;
+ break;
+ default:
+ abort();
+ }
+}
+
+target_ulong read_val_from_reg(target_ulong reg_ptr, int size)
+{
+ target_ulong val;
+
+ switch (size) {
+ case 1:
+ val = *(uint8_t *)reg_ptr;
+ break;
+ case 2:
+ val = *(uint16_t *)reg_ptr;
+ break;
+ case 4:
+ val = *(uint32_t *)reg_ptr;
+ break;
+ case 8:
+ val = *(uint64_t *)reg_ptr;
+ break;
+ default:
+ abort();
+ }
+ return val;
+}
+
+void write_val_to_reg(target_ulong reg_ptr, target_ulong val, int size)
+{
+ switch (size) {
+ case 1:
+ *(uint8_t *)reg_ptr = val;
+ break;
+ case 2:
+ *(uint16_t *)reg_ptr = val;
+ break;
+ case 4:
+ *(uint64_t *)reg_ptr = (uint32_t)val;
+ break;
+ case 8:
+ *(uint64_t *)reg_ptr = val;
+ break;
+ default:
+ abort();
+ }
+}
+
+static bool is_host_reg(struct CPUX86State *env, target_ulong ptr)
+{
+ return (ptr - (target_ulong)&env->hvf_emul->regs[0]) < sizeof(env->hvf_emul->regs);
+}
+
+void write_val_ext(struct CPUX86State *env, target_ulong ptr, target_ulong val, int size)
+{
+ if (is_host_reg(env, ptr)) {
+ write_val_to_reg(ptr, val, size);
+ return;
+ }
+ vmx_write_mem(ENV_GET_CPU(env), ptr, &val, size);
+}
+
+uint8_t *read_mmio(struct CPUX86State *env, target_ulong ptr, int bytes)
+{
+ vmx_read_mem(ENV_GET_CPU(env), env->hvf_emul->mmio_buf, ptr, bytes);
+ return env->hvf_emul->mmio_buf;
+}
+
+
+target_ulong read_val_ext(struct CPUX86State *env, target_ulong ptr, int size)
+{
+ target_ulong val;
+ uint8_t *mmio_ptr;
+
+ if (is_host_reg(env, ptr)) {
+ return read_val_from_reg(ptr, size);
+ }
+
+ mmio_ptr = read_mmio(env, ptr, size);
+ switch (size) {
+ case 1:
+ val = *(uint8_t *)mmio_ptr;
+ break;
+ case 2:
+ val = *(uint16_t *)mmio_ptr;
+ break;
+ case 4:
+ val = *(uint32_t *)mmio_ptr;
+ break;
+ case 8:
+ val = *(uint64_t *)mmio_ptr;
+ break;
+ default:
+ VM_PANIC("bad size\n");
+ break;
+ }
+ return val;
+}
+
+static void fetch_operands(struct CPUX86State *env, struct x86_decode *decode,
+ int n, bool val_op0, bool val_op1, bool val_op2)
+{
+ int i;
+ bool calc_val[3] = {val_op0, val_op1, val_op2};
+
+ for (i = 0; i < n; i++) {
+ switch (decode->op[i].type) {
+ case X86_VAR_IMMEDIATE:
+ break;
+ case X86_VAR_REG:
+ VM_PANIC_ON(!decode->op[i].ptr);
+ if (calc_val[i]) {
+ decode->op[i].val = read_val_from_reg(decode->op[i].ptr,
+ decode->operand_size);
+ }
+ break;
+ case X86_VAR_RM:
+ calc_modrm_operand(env, decode, &decode->op[i]);
+ if (calc_val[i]) {
+ decode->op[i].val = read_val_ext(env, decode->op[i].ptr,
+ decode->operand_size);
+ }
+ break;
+ case X86_VAR_OFFSET:
+ decode->op[i].ptr = decode_linear_addr(env, decode,
+ decode->op[i].ptr,
+ R_DS);
+ if (calc_val[i]) {
+ decode->op[i].val = read_val_ext(env, decode->op[i].ptr,
+ decode->operand_size);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void exec_mov(struct CPUX86State *env, struct x86_decode *decode)
+{
+ fetch_operands(env, decode, 2, false, true, false);
+ write_val_ext(env, decode->op[0].ptr, decode->op[1].val,
+ decode->operand_size);
+
+ RIP(env) += decode->len;
+}
+
+static void exec_add(struct CPUX86State *env, struct x86_decode *decode)
+{
+ EXEC_2OP_FLAGS_CMD(env, decode, +, SET_FLAGS_OSZAPC_ADD, true);
+ RIP(env) += decode->len;
+}
+
+static void exec_or(struct CPUX86State *env, struct x86_decode *decode)
+{
+ EXEC_2OP_FLAGS_CMD(env, decode, |, SET_FLAGS_OSZAPC_LOGIC, true);
+ RIP(env) += decode->len;
+}
+
+static void exec_adc(struct CPUX86State *env, struct x86_decode *decode)
+{
+ EXEC_2OP_FLAGS_CMD(env, decode, +get_CF(env)+, SET_FLAGS_OSZAPC_ADD, true);
+ RIP(env) += decode->len;
+}
+
+static void exec_sbb(struct CPUX86State *env, struct x86_decode *decode)
+{
+ EXEC_2OP_FLAGS_CMD(env, decode, -get_CF(env)-, SET_FLAGS_OSZAPC_SUB, true);
+ RIP(env) += decode->len;
+}
+
+static void exec_and(struct CPUX86State *env, struct x86_decode *decode)
+{
+ EXEC_2OP_FLAGS_CMD(env, decode, &, SET_FLAGS_OSZAPC_LOGIC, true);
+ RIP(env) += decode->len;
+}
+
+static void exec_sub(struct CPUX86State *env, struct x86_decode *decode)
+{
+ EXEC_2OP_FLAGS_CMD(env, decode, -, SET_FLAGS_OSZAPC_SUB, true);
+ RIP(env) += decode->len;
+}
+
+static void exec_xor(struct CPUX86State *env, struct x86_decode *decode)
+{
+ EXEC_2OP_FLAGS_CMD(env, decode, ^, SET_FLAGS_OSZAPC_LOGIC, true);
+ RIP(env) += decode->len;
+}
+
+static void exec_neg(struct CPUX86State *env, struct x86_decode *decode)
+{
+ /*EXEC_2OP_FLAGS_CMD(env, decode, -, SET_FLAGS_OSZAPC_SUB, false);*/
+ int32_t val;
+ fetch_operands(env, decode, 2, true, true, false);
+
+ val = 0 - sign(decode->op[1].val, decode->operand_size);
+ write_val_ext(env, decode->op[1].ptr, val, decode->operand_size);
+
+ if (4 == decode->operand_size) {
+ SET_FLAGS_OSZAPC_SUB32(env, 0, 0 - val, val);
+ } else if (2 == decode->operand_size) {
+ SET_FLAGS_OSZAPC_SUB16(env, 0, 0 - val, val);
+ } else if (1 == decode->operand_size) {
+ SET_FLAGS_OSZAPC_SUB8(env, 0, 0 - val, val);
+ } else {
+ VM_PANIC("bad op size\n");
+ }
+
+ /*lflags_to_rflags(env);*/
+ RIP(env) += decode->len;
+}
+
+static void exec_cmp(struct CPUX86State *env, struct x86_decode *decode)
+{
+ EXEC_2OP_FLAGS_CMD(env, decode, -, SET_FLAGS_OSZAPC_SUB, false);
+ RIP(env) += decode->len;
+}
+
+static void exec_inc(struct CPUX86State *env, struct x86_decode *decode)
+{
+ decode->op[1].type = X86_VAR_IMMEDIATE;
+ decode->op[1].val = 0;
+
+ EXEC_2OP_FLAGS_CMD(env, decode, +1+, SET_FLAGS_OSZAP_ADD, true);
+
+ RIP(env) += decode->len;
+}
+
+static void exec_dec(struct CPUX86State *env, struct x86_decode *decode)
+{
+ decode->op[1].type = X86_VAR_IMMEDIATE;
+ decode->op[1].val = 0;
+
+ EXEC_2OP_FLAGS_CMD(env, decode, -1-, SET_FLAGS_OSZAP_SUB, true);
+ RIP(env) += decode->len;
+}
+
+static void exec_tst(struct CPUX86State *env, struct x86_decode *decode)
+{
+ EXEC_2OP_FLAGS_CMD(env, decode, &, SET_FLAGS_OSZAPC_LOGIC, false);
+ RIP(env) += decode->len;
+}
+
+static void exec_not(struct CPUX86State *env, struct x86_decode *decode)
+{
+ fetch_operands(env, decode, 1, true, false, false);
+
+ write_val_ext(env, decode->op[0].ptr, ~decode->op[0].val,
+ decode->operand_size);
+ RIP(env) += decode->len;
+}
+
+void exec_movzx(struct CPUX86State *env, struct x86_decode *decode)
+{
+ int src_op_size;
+ int op_size = decode->operand_size;
+
+ fetch_operands(env, decode, 1, false, false, false);
+
+ if (0xb6 == decode->opcode[1]) {
+ src_op_size = 1;
+ } else {
+ src_op_size = 2;
+ }
+ decode->operand_size = src_op_size;
+ calc_modrm_operand(env, decode, &decode->op[1]);
+ decode->op[1].val = read_val_ext(env, decode->op[1].ptr, src_op_size);
+ write_val_ext(env, decode->op[0].ptr, decode->op[1].val, op_size);
+
+ RIP(env) += decode->len;
+}
+
+static void exec_out(struct CPUX86State *env, struct x86_decode *decode)
+{
+ switch (decode->opcode[0]) {
+ case 0xe6:
+ hvf_handle_io(ENV_GET_CPU(env), decode->op[0].val, &AL(env), 1, 1, 1);
+ break;
+ case 0xe7:
+ hvf_handle_io(ENV_GET_CPU(env), decode->op[0].val, &RAX(env), 1,
+ decode->operand_size, 1);
+ break;
+ case 0xee:
+ hvf_handle_io(ENV_GET_CPU(env), DX(env), &AL(env), 1, 1, 1);
+ break;
+ case 0xef:
+ hvf_handle_io(ENV_GET_CPU(env), DX(env), &RAX(env), 1, decode->operand_size, 1);
+ break;
+ default:
+ VM_PANIC("Bad out opcode\n");
+ break;
+ }
+ RIP(env) += decode->len;
+}
+
+static void exec_in(struct CPUX86State *env, struct x86_decode *decode)
+{
+ target_ulong val = 0;
+ switch (decode->opcode[0]) {
+ case 0xe4:
+ hvf_handle_io(ENV_GET_CPU(env), decode->op[0].val, &AL(env), 0, 1, 1);
+ break;
+ case 0xe5:
+ hvf_handle_io(ENV_GET_CPU(env), decode->op[0].val, &val, 0, decode->operand_size, 1);
+ if (decode->operand_size == 2) {
+ AX(env) = val;
+ } else {
+ RAX(env) = (uint32_t)val;
+ }
+ break;
+ case 0xec:
+ hvf_handle_io(ENV_GET_CPU(env), DX(env), &AL(env), 0, 1, 1);
+ break;
+ case 0xed:
+ hvf_handle_io(ENV_GET_CPU(env), DX(env), &val, 0, decode->operand_size, 1);
+ if (decode->operand_size == 2) {
+ AX(env) = val;
+ } else {
+ RAX(env) = (uint32_t)val;
+ }
+
+ break;
+ default:
+ VM_PANIC("Bad in opcode\n");
+ break;
+ }
+
+ RIP(env) += decode->len;
+}
+
+static inline void string_increment_reg(struct CPUX86State *env, int reg,
+ struct x86_decode *decode)
+{
+ target_ulong val = read_reg(env, reg, decode->addressing_size);
+ if (env->hvf_emul->rflags.df) {
+ val -= decode->operand_size;
+ } else {
+ val += decode->operand_size;
+ }
+ write_reg(env, reg, val, decode->addressing_size);
+}
+
+static inline void string_rep(struct CPUX86State *env, struct x86_decode *decode,
+ void (*func)(struct CPUX86State *env,
+ struct x86_decode *ins), int rep)
+{
+ target_ulong rcx = read_reg(env, R_ECX, decode->addressing_size);
+ while (rcx--) {
+ func(env, decode);
+ write_reg(env, R_ECX, rcx, decode->addressing_size);
+ if ((PREFIX_REP == rep) && !get_ZF(env)) {
+ break;
+ }
+ if ((PREFIX_REPN == rep) && get_ZF(env)) {
+ break;
+ }
+ }
+}
+
+static void exec_ins_single(struct CPUX86State *env, struct x86_decode *decode)
+{
+ target_ulong addr = linear_addr_size(ENV_GET_CPU(env), RDI(env), decode->addressing_size,
+ R_ES);
+
+ hvf_handle_io(ENV_GET_CPU(env), DX(env), env->hvf_emul->mmio_buf, 0,
+ decode->operand_size, 1);
+ vmx_write_mem(ENV_GET_CPU(env), addr, env->hvf_emul->mmio_buf, decode->operand_size);
+
+ string_increment_reg(env, R_EDI, decode);
+}
+
+static void exec_ins(struct CPUX86State *env, struct x86_decode *decode)
+{
+ if (decode->rep) {
+ string_rep(env, decode, exec_ins_single, 0);
+ } else {
+ exec_ins_single(env, decode);
+ }
+
+ RIP(env) += decode->len;
+}
+
+static void exec_outs_single(struct CPUX86State *env, struct x86_decode *decode)
+{
+ target_ulong addr = decode_linear_addr(env, decode, RSI(env), R_DS);
+
+ vmx_read_mem(ENV_GET_CPU(env), env->hvf_emul->mmio_buf, addr, decode->operand_size);
+ hvf_handle_io(ENV_GET_CPU(env), DX(env), env->hvf_emul->mmio_buf, 1,
+ decode->operand_size, 1);
+
+ string_increment_reg(env, R_ESI, decode);
+}
+
+static void exec_outs(struct CPUX86State *env, struct x86_decode *decode)
+{
+ if (decode->rep) {
+ string_rep(env, decode, exec_outs_single, 0);
+ } else {
+ exec_outs_single(env, decode);
+ }
+
+ RIP(env) += decode->len;
+}
+
+static void exec_movs_single(struct CPUX86State *env, struct x86_decode *decode)
+{
+ target_ulong src_addr;
+ target_ulong dst_addr;
+ target_ulong val;
+
+ src_addr = decode_linear_addr(env, decode, RSI(env), R_DS);
+ dst_addr = linear_addr_size(ENV_GET_CPU(env), RDI(env), decode->addressing_size,
+ R_ES);
+
+ val = read_val_ext(env, src_addr, decode->operand_size);
+ write_val_ext(env, dst_addr, val, decode->operand_size);
+
+ string_increment_reg(env, R_ESI, decode);
+ string_increment_reg(env, R_EDI, decode);
+}
+
+static void exec_movs(struct CPUX86State *env, struct x86_decode *decode)
+{
+ if (decode->rep) {
+ string_rep(env, decode, exec_movs_single, 0);
+ } else {
+ exec_movs_single(env, decode);
+ }
+
+ RIP(env) += decode->len;
+}
+
+static void exec_cmps_single(struct CPUX86State *env, struct x86_decode *decode)
+{
+ target_ulong src_addr;
+ target_ulong dst_addr;
+
+ src_addr = decode_linear_addr(env, decode, RSI(env), R_DS);
+ dst_addr = linear_addr_size(ENV_GET_CPU(env), RDI(env), decode->addressing_size,
+ R_ES);
+
+ decode->op[0].type = X86_VAR_IMMEDIATE;
+ decode->op[0].val = read_val_ext(env, src_addr, decode->operand_size);
+ decode->op[1].type = X86_VAR_IMMEDIATE;
+ decode->op[1].val = read_val_ext(env, dst_addr, decode->operand_size);
+
+ EXEC_2OP_FLAGS_CMD(env, decode, -, SET_FLAGS_OSZAPC_SUB, false);
+
+ string_increment_reg(env, R_ESI, decode);
+ string_increment_reg(env, R_EDI, decode);
+}
+
+static void exec_cmps(struct CPUX86State *env, struct x86_decode *decode)
+{
+ if (decode->rep) {
+ string_rep(env, decode, exec_cmps_single, decode->rep);
+ } else {
+ exec_cmps_single(env, decode);
+ }
+ RIP(env) += decode->len;
+}
+
+
+static void exec_stos_single(struct CPUX86State *env, struct x86_decode *decode)
+{
+ target_ulong addr;
+ target_ulong val;
+
+ addr = linear_addr_size(ENV_GET_CPU(env), RDI(env), decode->addressing_size, R_ES);
+ val = read_reg(env, R_EAX, decode->operand_size);
+ vmx_write_mem(ENV_GET_CPU(env), addr, &val, decode->operand_size);
+
+ string_increment_reg(env, R_EDI, decode);
+}
+
+
+static void exec_stos(struct CPUX86State *env, struct x86_decode *decode)
+{
+ if (decode->rep) {
+ string_rep(env, decode, exec_stos_single, 0);
+ } else {
+ exec_stos_single(env, decode);
+ }
+
+ RIP(env) += decode->len;
+}
+
+static void exec_scas_single(struct CPUX86State *env, struct x86_decode *decode)
+{
+ target_ulong addr;
+
+ addr = linear_addr_size(ENV_GET_CPU(env), RDI(env), decode->addressing_size, R_ES);
+ decode->op[1].type = X86_VAR_IMMEDIATE;
+ vmx_read_mem(ENV_GET_CPU(env), &decode->op[1].val, addr, decode->operand_size);
+
+ EXEC_2OP_FLAGS_CMD(env, decode, -, SET_FLAGS_OSZAPC_SUB, false);
+ string_increment_reg(env, R_EDI, decode);
+}
+
+static void exec_scas(struct CPUX86State *env, struct x86_decode *decode)
+{
+ decode->op[0].type = X86_VAR_REG;
+ decode->op[0].reg = R_EAX;
+ if (decode->rep) {
+ string_rep(env, decode, exec_scas_single, decode->rep);
+ } else {
+ exec_scas_single(env, decode);
+ }
+
+ RIP(env) += decode->len;
+}
+
+static void exec_lods_single(struct CPUX86State *env, struct x86_decode *decode)
+{
+ target_ulong addr;
+ target_ulong val = 0;
+
+ addr = decode_linear_addr(env, decode, RSI(env), R_DS);
+ vmx_read_mem(ENV_GET_CPU(env), &val, addr, decode->operand_size);
+ write_reg(env, R_EAX, val, decode->operand_size);
+
+ string_increment_reg(env, R_ESI, decode);
+}
+
+static void exec_lods(struct CPUX86State *env, struct x86_decode *decode)
+{
+ if (decode->rep) {
+ string_rep(env, decode, exec_lods_single, 0);
+ } else {
+ exec_lods_single(env, decode);
+ }
+
+ RIP(env) += decode->len;
+}
+
+#define MSR_IA32_UCODE_REV 0x00000017
+
+void simulate_rdmsr(struct CPUState *cpu)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ uint32_t msr = ECX(env);
+ uint64_t val = 0;
+
+ switch (msr) {
+ case MSR_IA32_TSC:
+ val = rdtscp() + rvmcs(cpu->hvf_fd, VMCS_TSC_OFFSET);
+ break;
+ case MSR_IA32_APICBASE:
+ val = cpu_get_apic_base(X86_CPU(cpu)->apic_state);
+ break;
+ case MSR_IA32_UCODE_REV:
+ val = (0x100000000ULL << 32) | 0x100000000ULL;
+ break;
+ case MSR_EFER:
+ val = rvmcs(cpu->hvf_fd, VMCS_GUEST_IA32_EFER);
+ break;
+ case MSR_FSBASE:
+ val = rvmcs(cpu->hvf_fd, VMCS_GUEST_FS_BASE);
+ break;
+ case MSR_GSBASE:
+ val = rvmcs(cpu->hvf_fd, VMCS_GUEST_GS_BASE);
+ break;
+ case MSR_KERNELGSBASE:
+ val = rvmcs(cpu->hvf_fd, VMCS_HOST_FS_BASE);
+ break;
+ case MSR_STAR:
+ abort();
+ break;
+ case MSR_LSTAR:
+ abort();
+ break;
+ case MSR_CSTAR:
+ abort();
+ break;
+ case MSR_IA32_MISC_ENABLE:
+ val = env->msr_ia32_misc_enable;
+ break;
+ case MSR_MTRRphysBase(0):
+ case MSR_MTRRphysBase(1):
+ case MSR_MTRRphysBase(2):
+ case MSR_MTRRphysBase(3):
+ case MSR_MTRRphysBase(4):
+ case MSR_MTRRphysBase(5):
+ case MSR_MTRRphysBase(6):
+ case MSR_MTRRphysBase(7):
+ val = env->mtrr_var[(ECX(env) - MSR_MTRRphysBase(0)) / 2].base;
+ break;
+ case MSR_MTRRphysMask(0):
+ case MSR_MTRRphysMask(1):
+ case MSR_MTRRphysMask(2):
+ case MSR_MTRRphysMask(3):
+ case MSR_MTRRphysMask(4):
+ case MSR_MTRRphysMask(5):
+ case MSR_MTRRphysMask(6):
+ case MSR_MTRRphysMask(7):
+ val = env->mtrr_var[(ECX(env) - MSR_MTRRphysMask(0)) / 2].mask;
+ break;
+ case MSR_MTRRfix64K_00000:
+ val = env->mtrr_fixed[0];
+ break;
+ case MSR_MTRRfix16K_80000:
+ case MSR_MTRRfix16K_A0000:
+ val = env->mtrr_fixed[ECX(env) - MSR_MTRRfix16K_80000 + 1];
+ break;
+ case MSR_MTRRfix4K_C0000:
+ case MSR_MTRRfix4K_C8000:
+ case MSR_MTRRfix4K_D0000:
+ case MSR_MTRRfix4K_D8000:
+ case MSR_MTRRfix4K_E0000:
+ case MSR_MTRRfix4K_E8000:
+ case MSR_MTRRfix4K_F0000:
+ case MSR_MTRRfix4K_F8000:
+ val = env->mtrr_fixed[ECX(env) - MSR_MTRRfix4K_C0000 + 3];
+ break;
+ case MSR_MTRRdefType:
+ val = env->mtrr_deftype;
+ break;
+ default:
+ /* fprintf(stderr, "%s: unknown msr 0x%x\n", __func__, msr); */
+ val = 0;
+ break;
+ }
+
+ RAX(env) = (uint32_t)val;
+ RDX(env) = (uint32_t)(val >> 32);
+}
+
+static void exec_rdmsr(struct CPUX86State *env, struct x86_decode *decode)
+{
+ simulate_rdmsr(ENV_GET_CPU(env));
+ RIP(env) += decode->len;
+}
+
+void simulate_wrmsr(struct CPUState *cpu)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ uint32_t msr = ECX(env);
+ uint64_t data = ((uint64_t)EDX(env) << 32) | EAX(env);
+
+ switch (msr) {
+ case MSR_IA32_TSC:
+ /* if (!osx_is_sierra())
+ wvmcs(cpu->hvf_fd, VMCS_TSC_OFFSET, data - rdtscp());
+ hv_vm_sync_tsc(data);*/
+ break;
+ case MSR_IA32_APICBASE:
+ cpu_set_apic_base(X86_CPU(cpu)->apic_state, data);
+ break;
+ case MSR_FSBASE:
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_FS_BASE, data);
+ break;
+ case MSR_GSBASE:
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_GS_BASE, data);
+ break;
+ case MSR_KERNELGSBASE:
+ wvmcs(cpu->hvf_fd, VMCS_HOST_FS_BASE, data);
+ break;
+ case MSR_STAR:
+ abort();
+ break;
+ case MSR_LSTAR:
+ abort();
+ break;
+ case MSR_CSTAR:
+ abort();
+ break;
+ case MSR_EFER:
+ /*printf("new efer %llx\n", EFER(cpu));*/
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_IA32_EFER, data);
+ if (data & MSR_EFER_NXE) {
+ hv_vcpu_invalidate_tlb(cpu->hvf_fd);
+ }
+ break;
+ case MSR_MTRRphysBase(0):
+ case MSR_MTRRphysBase(1):
+ case MSR_MTRRphysBase(2):
+ case MSR_MTRRphysBase(3):
+ case MSR_MTRRphysBase(4):
+ case MSR_MTRRphysBase(5):
+ case MSR_MTRRphysBase(6):
+ case MSR_MTRRphysBase(7):
+ env->mtrr_var[(ECX(env) - MSR_MTRRphysBase(0)) / 2].base = data;
+ break;
+ case MSR_MTRRphysMask(0):
+ case MSR_MTRRphysMask(1):
+ case MSR_MTRRphysMask(2):
+ case MSR_MTRRphysMask(3):
+ case MSR_MTRRphysMask(4):
+ case MSR_MTRRphysMask(5):
+ case MSR_MTRRphysMask(6):
+ case MSR_MTRRphysMask(7):
+ env->mtrr_var[(ECX(env) - MSR_MTRRphysMask(0)) / 2].mask = data;
+ break;
+ case MSR_MTRRfix64K_00000:
+ env->mtrr_fixed[ECX(env) - MSR_MTRRfix64K_00000] = data;
+ break;
+ case MSR_MTRRfix16K_80000:
+ case MSR_MTRRfix16K_A0000:
+ env->mtrr_fixed[ECX(env) - MSR_MTRRfix16K_80000 + 1] = data;
+ break;
+ case MSR_MTRRfix4K_C0000:
+ case MSR_MTRRfix4K_C8000:
+ case MSR_MTRRfix4K_D0000:
+ case MSR_MTRRfix4K_D8000:
+ case MSR_MTRRfix4K_E0000:
+ case MSR_MTRRfix4K_E8000:
+ case MSR_MTRRfix4K_F0000:
+ case MSR_MTRRfix4K_F8000:
+ env->mtrr_fixed[ECX(env) - MSR_MTRRfix4K_C0000 + 3] = data;
+ break;
+ case MSR_MTRRdefType:
+ env->mtrr_deftype = data;
+ break;
+ default:
+ break;
+ }
+
+ /* Related to support known hypervisor interface */
+ /* if (g_hypervisor_iface)
+ g_hypervisor_iface->wrmsr_handler(cpu, msr, data);
+
+ printf("write msr %llx\n", RCX(cpu));*/
+}
+
+static void exec_wrmsr(struct CPUX86State *env, struct x86_decode *decode)
+{
+ simulate_wrmsr(ENV_GET_CPU(env));
+ RIP(env) += decode->len;
+}
+
+/*
+ * flag:
+ * 0 - bt, 1 - btc, 2 - bts, 3 - btr
+ */
+static void do_bt(struct CPUX86State *env, struct x86_decode *decode, int flag)
+{
+ int32_t displacement;
+ uint8_t index;
+ bool cf;
+ int mask = (4 == decode->operand_size) ? 0x1f : 0xf;
+
+ VM_PANIC_ON(decode->rex.rex);
+
+ fetch_operands(env, decode, 2, false, true, false);
+ index = decode->op[1].val & mask;
+
+ if (decode->op[0].type != X86_VAR_REG) {
+ if (4 == decode->operand_size) {
+ displacement = ((int32_t) (decode->op[1].val & 0xffffffe0)) / 32;
+ decode->op[0].ptr += 4 * displacement;
+ } else if (2 == decode->operand_size) {
+ displacement = ((int16_t) (decode->op[1].val & 0xfff0)) / 16;
+ decode->op[0].ptr += 2 * displacement;
+ } else {
+ VM_PANIC("bt 64bit\n");
+ }
+ }
+ decode->op[0].val = read_val_ext(env, decode->op[0].ptr,
+ decode->operand_size);
+ cf = (decode->op[0].val >> index) & 0x01;
+
+ switch (flag) {
+ case 0:
+ set_CF(env, cf);
+ return;
+ case 1:
+ decode->op[0].val ^= (1u << index);
+ break;
+ case 2:
+ decode->op[0].val |= (1u << index);
+ break;
+ case 3:
+ decode->op[0].val &= ~(1u << index);
+ break;
+ }
+ write_val_ext(env, decode->op[0].ptr, decode->op[0].val,
+ decode->operand_size);
+ set_CF(env, cf);
+}
+
+static void exec_bt(struct CPUX86State *env, struct x86_decode *decode)
+{
+ do_bt(env, decode, 0);
+ RIP(env) += decode->len;
+}
+
+static void exec_btc(struct CPUX86State *env, struct x86_decode *decode)
+{
+ do_bt(env, decode, 1);
+ RIP(env) += decode->len;
+}
+
+static void exec_btr(struct CPUX86State *env, struct x86_decode *decode)
+{
+ do_bt(env, decode, 3);
+ RIP(env) += decode->len;
+}
+
+static void exec_bts(struct CPUX86State *env, struct x86_decode *decode)
+{
+ do_bt(env, decode, 2);
+ RIP(env) += decode->len;
+}
+
+void exec_shl(struct CPUX86State *env, struct x86_decode *decode)
+{
+ uint8_t count;
+ int of = 0, cf = 0;
+
+ fetch_operands(env, decode, 2, true, true, false);
+
+ count = decode->op[1].val;
+ count &= 0x1f; /* count is masked to 5 bits*/
+ if (!count) {
+ goto exit;
+ }
+
+ switch (decode->operand_size) {
+ case 1:
+ {
+ uint8_t res = 0;
+ if (count <= 8) {
+ res = (decode->op[0].val << count);
+ cf = (decode->op[0].val >> (8 - count)) & 0x1;
+ of = cf ^ (res >> 7);
+ }
+
+ write_val_ext(env, decode->op[0].ptr, res, 1);
+ SET_FLAGS_OSZAPC_LOGIC8(env, 0, 0, res);
+ SET_FLAGS_OxxxxC(env, of, cf);
+ break;
+ }
+ case 2:
+ {
+ uint16_t res = 0;
+
+ /* from bochs */
+ if (count <= 16) {
+ res = (decode->op[0].val << count);
+ cf = (decode->op[0].val >> (16 - count)) & 0x1;
+ of = cf ^ (res >> 15); /* of = cf ^ result15 */
+ }
+
+ write_val_ext(env, decode->op[0].ptr, res, 2);
+ SET_FLAGS_OSZAPC_LOGIC16(env, 0, 0, res);
+ SET_FLAGS_OxxxxC(env, of, cf);
+ break;
+ }
+ case 4:
+ {
+ uint32_t res = decode->op[0].val << count;
+
+ write_val_ext(env, decode->op[0].ptr, res, 4);
+ SET_FLAGS_OSZAPC_LOGIC32(env, 0, 0, res);
+ cf = (decode->op[0].val >> (32 - count)) & 0x1;
+ of = cf ^ (res >> 31); /* of = cf ^ result31 */
+ SET_FLAGS_OxxxxC(env, of, cf);
+ break;
+ }
+ default:
+ abort();
+ }
+
+exit:
+ /* lflags_to_rflags(env); */
+ RIP(env) += decode->len;
+}
+
+void exec_movsx(CPUX86State *env, struct x86_decode *decode)
+{
+ int src_op_size;
+ int op_size = decode->operand_size;
+
+ fetch_operands(env, decode, 2, false, false, false);
+
+ if (0xbe == decode->opcode[1]) {
+ src_op_size = 1;
+ } else {
+ src_op_size = 2;
+ }
+
+ decode->operand_size = src_op_size;
+ calc_modrm_operand(env, decode, &decode->op[1]);
+ decode->op[1].val = sign(read_val_ext(env, decode->op[1].ptr, src_op_size),
+ src_op_size);
+
+ write_val_ext(env, decode->op[0].ptr, decode->op[1].val, op_size);
+
+ RIP(env) += decode->len;
+}
+
+void exec_ror(struct CPUX86State *env, struct x86_decode *decode)
+{
+ uint8_t count;
+
+ fetch_operands(env, decode, 2, true, true, false);
+ count = decode->op[1].val;
+
+ switch (decode->operand_size) {
+ case 1:
+ {
+ uint32_t bit6, bit7;
+ uint8_t res;
+
+ if ((count & 0x07) == 0) {
+ if (count & 0x18) {
+ bit6 = ((uint8_t)decode->op[0].val >> 6) & 1;
+ bit7 = ((uint8_t)decode->op[0].val >> 7) & 1;
+ SET_FLAGS_OxxxxC(env, bit6 ^ bit7, bit7);
+ }
+ } else {
+ count &= 0x7; /* use only bottom 3 bits */
+ res = ((uint8_t)decode->op[0].val >> count) |
+ ((uint8_t)decode->op[0].val << (8 - count));
+ write_val_ext(env, decode->op[0].ptr, res, 1);
+ bit6 = (res >> 6) & 1;
+ bit7 = (res >> 7) & 1;
+ /* set eflags: ROR count affects the following flags: C, O */
+ SET_FLAGS_OxxxxC(env, bit6 ^ bit7, bit7);
+ }
+ break;
+ }
+ case 2:
+ {
+ uint32_t bit14, bit15;
+ uint16_t res;
+
+ if ((count & 0x0f) == 0) {
+ if (count & 0x10) {
+ bit14 = ((uint16_t)decode->op[0].val >> 14) & 1;
+ bit15 = ((uint16_t)decode->op[0].val >> 15) & 1;
+ /* of = result14 ^ result15 */
+ SET_FLAGS_OxxxxC(env, bit14 ^ bit15, bit15);
+ }
+ } else {
+ count &= 0x0f; /* use only 4 LSB's */
+ res = ((uint16_t)decode->op[0].val >> count) |
+ ((uint16_t)decode->op[0].val << (16 - count));
+ write_val_ext(env, decode->op[0].ptr, res, 2);
+
+ bit14 = (res >> 14) & 1;
+ bit15 = (res >> 15) & 1;
+ /* of = result14 ^ result15 */
+ SET_FLAGS_OxxxxC(env, bit14 ^ bit15, bit15);
+ }
+ break;
+ }
+ case 4:
+ {
+ uint32_t bit31, bit30;
+ uint32_t res;
+
+ count &= 0x1f;
+ if (count) {
+ res = ((uint32_t)decode->op[0].val >> count) |
+ ((uint32_t)decode->op[0].val << (32 - count));
+ write_val_ext(env, decode->op[0].ptr, res, 4);
+
+ bit31 = (res >> 31) & 1;
+ bit30 = (res >> 30) & 1;
+ /* of = result30 ^ result31 */
+ SET_FLAGS_OxxxxC(env, bit30 ^ bit31, bit31);
+ }
+ break;
+ }
+ }
+ RIP(env) += decode->len;
+}
+
+void exec_rol(struct CPUX86State *env, struct x86_decode *decode)
+{
+ uint8_t count;
+
+ fetch_operands(env, decode, 2, true, true, false);
+ count = decode->op[1].val;
+
+ switch (decode->operand_size) {
+ case 1:
+ {
+ uint32_t bit0, bit7;
+ uint8_t res;
+
+ if ((count & 0x07) == 0) {
+ if (count & 0x18) {
+ bit0 = ((uint8_t)decode->op[0].val & 1);
+ bit7 = ((uint8_t)decode->op[0].val >> 7);
+ SET_FLAGS_OxxxxC(env, bit0 ^ bit7, bit0);
+ }
+ } else {
+ count &= 0x7; /* use only lowest 3 bits */
+ res = ((uint8_t)decode->op[0].val << count) |
+ ((uint8_t)decode->op[0].val >> (8 - count));
+
+ write_val_ext(env, decode->op[0].ptr, res, 1);
+ /* set eflags:
+ * ROL count affects the following flags: C, O
+ */
+ bit0 = (res & 1);
+ bit7 = (res >> 7);
+ SET_FLAGS_OxxxxC(env, bit0 ^ bit7, bit0);
+ }
+ break;
+ }
+ case 2:
+ {
+ uint32_t bit0, bit15;
+ uint16_t res;
+
+ if ((count & 0x0f) == 0) {
+ if (count & 0x10) {
+ bit0 = ((uint16_t)decode->op[0].val & 0x1);
+ bit15 = ((uint16_t)decode->op[0].val >> 15);
+ /* of = cf ^ result15 */
+ SET_FLAGS_OxxxxC(env, bit0 ^ bit15, bit0);
+ }
+ } else {
+ count &= 0x0f; /* only use bottom 4 bits */
+ res = ((uint16_t)decode->op[0].val << count) |
+ ((uint16_t)decode->op[0].val >> (16 - count));
+
+ write_val_ext(env, decode->op[0].ptr, res, 2);
+ bit0 = (res & 0x1);
+ bit15 = (res >> 15);
+ /* of = cf ^ result15 */
+ SET_FLAGS_OxxxxC(env, bit0 ^ bit15, bit0);
+ }
+ break;
+ }
+ case 4:
+ {
+ uint32_t bit0, bit31;
+ uint32_t res;
+
+ count &= 0x1f;
+ if (count) {
+ res = ((uint32_t)decode->op[0].val << count) |
+ ((uint32_t)decode->op[0].val >> (32 - count));
+
+ write_val_ext(env, decode->op[0].ptr, res, 4);
+ bit0 = (res & 0x1);
+ bit31 = (res >> 31);
+ /* of = cf ^ result31 */
+ SET_FLAGS_OxxxxC(env, bit0 ^ bit31, bit0);
+ }
+ break;
+ }
+ }
+ RIP(env) += decode->len;
+}
+
+
+void exec_rcl(struct CPUX86State *env, struct x86_decode *decode)
+{
+ uint8_t count;
+ int of = 0, cf = 0;
+
+ fetch_operands(env, decode, 2, true, true, false);
+ count = decode->op[1].val & 0x1f;
+
+ switch (decode->operand_size) {
+ case 1:
+ {
+ uint8_t op1_8 = decode->op[0].val;
+ uint8_t res;
+ count %= 9;
+ if (!count) {
+ break;
+ }
+
+ if (1 == count) {
+ res = (op1_8 << 1) | get_CF(env);
+ } else {
+ res = (op1_8 << count) | (get_CF(env) << (count - 1)) |
+ (op1_8 >> (9 - count));
+ }
+
+ write_val_ext(env, decode->op[0].ptr, res, 1);
+
+ cf = (op1_8 >> (8 - count)) & 0x01;
+ of = cf ^ (res >> 7); /* of = cf ^ result7 */
+ SET_FLAGS_OxxxxC(env, of, cf);
+ break;
+ }
+ case 2:
+ {
+ uint16_t res;
+ uint16_t op1_16 = decode->op[0].val;
+
+ count %= 17;
+ if (!count) {
+ break;
+ }
+
+ if (1 == count) {
+ res = (op1_16 << 1) | get_CF(env);
+ } else if (count == 16) {
+ res = (get_CF(env) << 15) | (op1_16 >> 1);
+ } else { /* 2..15 */
+ res = (op1_16 << count) | (get_CF(env) << (count - 1)) |
+ (op1_16 >> (17 - count));
+ }
+
+ write_val_ext(env, decode->op[0].ptr, res, 2);
+
+ cf = (op1_16 >> (16 - count)) & 0x1;
+ of = cf ^ (res >> 15); /* of = cf ^ result15 */
+ SET_FLAGS_OxxxxC(env, of, cf);
+ break;
+ }
+ case 4:
+ {
+ uint32_t res;
+ uint32_t op1_32 = decode->op[0].val;
+
+ if (!count) {
+ break;
+ }
+
+ if (1 == count) {
+ res = (op1_32 << 1) | get_CF(env);
+ } else {
+ res = (op1_32 << count) | (get_CF(env) << (count - 1)) |
+ (op1_32 >> (33 - count));
+ }
+
+ write_val_ext(env, decode->op[0].ptr, res, 4);
+
+ cf = (op1_32 >> (32 - count)) & 0x1;
+ of = cf ^ (res >> 31); /* of = cf ^ result31 */
+ SET_FLAGS_OxxxxC(env, of, cf);
+ break;
+ }
+ }
+ RIP(env) += decode->len;
+}
+
+void exec_rcr(struct CPUX86State *env, struct x86_decode *decode)
+{
+ uint8_t count;
+ int of = 0, cf = 0;
+
+ fetch_operands(env, decode, 2, true, true, false);
+ count = decode->op[1].val & 0x1f;
+
+ switch (decode->operand_size) {
+ case 1:
+ {
+ uint8_t op1_8 = decode->op[0].val;
+ uint8_t res;
+
+ count %= 9;
+ if (!count) {
+ break;
+ }
+ res = (op1_8 >> count) | (get_CF(env) << (8 - count)) |
+ (op1_8 << (9 - count));
+
+ write_val_ext(env, decode->op[0].ptr, res, 1);
+
+ cf = (op1_8 >> (count - 1)) & 0x1;
+ of = (((res << 1) ^ res) >> 7) & 0x1; /* of = result6 ^ result7 */
+ SET_FLAGS_OxxxxC(env, of, cf);
+ break;
+ }
+ case 2:
+ {
+ uint16_t op1_16 = decode->op[0].val;
+ uint16_t res;
+
+ count %= 17;
+ if (!count) {
+ break;
+ }
+ res = (op1_16 >> count) | (get_CF(env) << (16 - count)) |
+ (op1_16 << (17 - count));
+
+ write_val_ext(env, decode->op[0].ptr, res, 2);
+
+ cf = (op1_16 >> (count - 1)) & 0x1;
+ of = ((uint16_t)((res << 1) ^ res) >> 15) & 0x1; /* of = result15 ^
+ result14 */
+ SET_FLAGS_OxxxxC(env, of, cf);
+ break;
+ }
+ case 4:
+ {
+ uint32_t res;
+ uint32_t op1_32 = decode->op[0].val;
+
+ if (!count) {
+ break;
+ }
+
+ if (1 == count) {
+ res = (op1_32 >> 1) | (get_CF(env) << 31);
+ } else {
+ res = (op1_32 >> count) | (get_CF(env) << (32 - count)) |
+ (op1_32 << (33 - count));
+ }
+
+ write_val_ext(env, decode->op[0].ptr, res, 4);
+
+ cf = (op1_32 >> (count - 1)) & 0x1;
+ of = ((res << 1) ^ res) >> 31; /* of = result30 ^ result31 */
+ SET_FLAGS_OxxxxC(env, of, cf);
+ break;
+ }
+ }
+ RIP(env) += decode->len;
+}
+
+static void exec_xchg(struct CPUX86State *env, struct x86_decode *decode)
+{
+ fetch_operands(env, decode, 2, true, true, false);
+
+ write_val_ext(env, decode->op[0].ptr, decode->op[1].val,
+ decode->operand_size);
+ write_val_ext(env, decode->op[1].ptr, decode->op[0].val,
+ decode->operand_size);
+
+ RIP(env) += decode->len;
+}
+
+static void exec_xadd(struct CPUX86State *env, struct x86_decode *decode)
+{
+ EXEC_2OP_FLAGS_CMD(env, decode, +, SET_FLAGS_OSZAPC_ADD, true);
+ write_val_ext(env, decode->op[1].ptr, decode->op[0].val,
+ decode->operand_size);
+
+ RIP(env) += decode->len;
+}
+
+static struct cmd_handler {
+ enum x86_decode_cmd cmd;
+ void (*handler)(struct CPUX86State *env, struct x86_decode *ins);
+} handlers[] = {
+ {X86_DECODE_CMD_INVL, NULL,},
+ {X86_DECODE_CMD_MOV, exec_mov},
+ {X86_DECODE_CMD_ADD, exec_add},
+ {X86_DECODE_CMD_OR, exec_or},
+ {X86_DECODE_CMD_ADC, exec_adc},
+ {X86_DECODE_CMD_SBB, exec_sbb},
+ {X86_DECODE_CMD_AND, exec_and},
+ {X86_DECODE_CMD_SUB, exec_sub},
+ {X86_DECODE_CMD_NEG, exec_neg},
+ {X86_DECODE_CMD_XOR, exec_xor},
+ {X86_DECODE_CMD_CMP, exec_cmp},
+ {X86_DECODE_CMD_INC, exec_inc},
+ {X86_DECODE_CMD_DEC, exec_dec},
+ {X86_DECODE_CMD_TST, exec_tst},
+ {X86_DECODE_CMD_NOT, exec_not},
+ {X86_DECODE_CMD_MOVZX, exec_movzx},
+ {X86_DECODE_CMD_OUT, exec_out},
+ {X86_DECODE_CMD_IN, exec_in},
+ {X86_DECODE_CMD_INS, exec_ins},
+ {X86_DECODE_CMD_OUTS, exec_outs},
+ {X86_DECODE_CMD_RDMSR, exec_rdmsr},
+ {X86_DECODE_CMD_WRMSR, exec_wrmsr},
+ {X86_DECODE_CMD_BT, exec_bt},
+ {X86_DECODE_CMD_BTR, exec_btr},
+ {X86_DECODE_CMD_BTC, exec_btc},
+ {X86_DECODE_CMD_BTS, exec_bts},
+ {X86_DECODE_CMD_SHL, exec_shl},
+ {X86_DECODE_CMD_ROL, exec_rol},
+ {X86_DECODE_CMD_ROR, exec_ror},
+ {X86_DECODE_CMD_RCR, exec_rcr},
+ {X86_DECODE_CMD_RCL, exec_rcl},
+ /*{X86_DECODE_CMD_CPUID, exec_cpuid},*/
+ {X86_DECODE_CMD_MOVS, exec_movs},
+ {X86_DECODE_CMD_CMPS, exec_cmps},
+ {X86_DECODE_CMD_STOS, exec_stos},
+ {X86_DECODE_CMD_SCAS, exec_scas},
+ {X86_DECODE_CMD_LODS, exec_lods},
+ {X86_DECODE_CMD_MOVSX, exec_movsx},
+ {X86_DECODE_CMD_XCHG, exec_xchg},
+ {X86_DECODE_CMD_XADD, exec_xadd},
+};
+
+static struct cmd_handler _cmd_handler[X86_DECODE_CMD_LAST];
+
+static void init_cmd_handler()
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(handlers); i++) {
+ _cmd_handler[handlers[i].cmd] = handlers[i];
+ }
+}
+
+void load_regs(struct CPUState *cpu)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+
+ int i = 0;
+ RRX(env, R_EAX) = rreg(cpu->hvf_fd, HV_X86_RAX);
+ RRX(env, R_EBX) = rreg(cpu->hvf_fd, HV_X86_RBX);
+ RRX(env, R_ECX) = rreg(cpu->hvf_fd, HV_X86_RCX);
+ RRX(env, R_EDX) = rreg(cpu->hvf_fd, HV_X86_RDX);
+ RRX(env, R_ESI) = rreg(cpu->hvf_fd, HV_X86_RSI);
+ RRX(env, R_EDI) = rreg(cpu->hvf_fd, HV_X86_RDI);
+ RRX(env, R_ESP) = rreg(cpu->hvf_fd, HV_X86_RSP);
+ RRX(env, R_EBP) = rreg(cpu->hvf_fd, HV_X86_RBP);
+ for (i = 8; i < 16; i++) {
+ RRX(env, i) = rreg(cpu->hvf_fd, HV_X86_RAX + i);
+ }
+
+ RFLAGS(env) = rreg(cpu->hvf_fd, HV_X86_RFLAGS);
+ rflags_to_lflags(env);
+ RIP(env) = rreg(cpu->hvf_fd, HV_X86_RIP);
+}
+
+void store_regs(struct CPUState *cpu)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+
+ int i = 0;
+ wreg(cpu->hvf_fd, HV_X86_RAX, RAX(env));
+ wreg(cpu->hvf_fd, HV_X86_RBX, RBX(env));
+ wreg(cpu->hvf_fd, HV_X86_RCX, RCX(env));
+ wreg(cpu->hvf_fd, HV_X86_RDX, RDX(env));
+ wreg(cpu->hvf_fd, HV_X86_RSI, RSI(env));
+ wreg(cpu->hvf_fd, HV_X86_RDI, RDI(env));
+ wreg(cpu->hvf_fd, HV_X86_RBP, RBP(env));
+ wreg(cpu->hvf_fd, HV_X86_RSP, RSP(env));
+ for (i = 8; i < 16; i++) {
+ wreg(cpu->hvf_fd, HV_X86_RAX + i, RRX(env, i));
+ }
+
+ lflags_to_rflags(env);
+ wreg(cpu->hvf_fd, HV_X86_RFLAGS, RFLAGS(env));
+ macvm_set_rip(cpu, RIP(env));
+}
+
+bool exec_instruction(struct CPUX86State *env, struct x86_decode *ins)
+{
+ /*if (hvf_vcpu_id(cpu))
+ printf("%d, %llx: exec_instruction %s\n", hvf_vcpu_id(cpu), RIP(cpu),
+ decode_cmd_to_string(ins->cmd));*/
+
+ if (!_cmd_handler[ins->cmd].handler) {
+ printf("Unimplemented handler (%llx) for %d (%x %x) \n", RIP(env),
+ ins->cmd, ins->opcode[0],
+ ins->opcode_len > 1 ? ins->opcode[1] : 0);
+ RIP(env) += ins->len;
+ return true;
+ }
+
+ _cmd_handler[ins->cmd].handler(env, ins);
+ return true;
+}
+
+void init_emu()
+{
+ init_cmd_handler();
+}
diff --git a/target/i386/hvf/x86_emu.h b/target/i386/hvf/x86_emu.h
new file mode 100644
index 0000000000..fbb4832576
--- /dev/null
+++ b/target/i386/hvf/x86_emu.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 Veertu Inc,
+ * Copyright (C) 2017 Google Inc,
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __X86_EMU_H__
+#define __X86_EMU_H__
+
+#include "x86.h"
+#include "x86_decode.h"
+#include "cpu.h"
+
+void init_emu(void);
+bool exec_instruction(struct CPUX86State *env, struct x86_decode *ins);
+
+void load_regs(struct CPUState *cpu);
+void store_regs(struct CPUState *cpu);
+
+void simulate_rdmsr(struct CPUState *cpu);
+void simulate_wrmsr(struct CPUState *cpu);
+
+target_ulong read_reg(CPUX86State *env, int reg, int size);
+void write_reg(CPUX86State *env, int reg, target_ulong val, int size);
+target_ulong read_val_from_reg(target_ulong reg_ptr, int size);
+void write_val_to_reg(target_ulong reg_ptr, target_ulong val, int size);
+void write_val_ext(struct CPUX86State *env, target_ulong ptr, target_ulong val, int size);
+uint8_t *read_mmio(struct CPUX86State *env, target_ulong ptr, int bytes);
+target_ulong read_val_ext(struct CPUX86State *env, target_ulong ptr, int size);
+
+void exec_movzx(struct CPUX86State *env, struct x86_decode *decode);
+void exec_shl(struct CPUX86State *env, struct x86_decode *decode);
+void exec_movsx(struct CPUX86State *env, struct x86_decode *decode);
+void exec_ror(struct CPUX86State *env, struct x86_decode *decode);
+void exec_rol(struct CPUX86State *env, struct x86_decode *decode);
+void exec_rcl(struct CPUX86State *env, struct x86_decode *decode);
+void exec_rcr(struct CPUX86State *env, struct x86_decode *decode);
+#endif
diff --git a/target/i386/hvf/x86_flags.c b/target/i386/hvf/x86_flags.c
new file mode 100644
index 0000000000..ee6d33f861
--- /dev/null
+++ b/target/i386/hvf/x86_flags.c
@@ -0,0 +1,315 @@
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001-2012 The Bochs Project
+// Copyright (C) 2017 Google Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA B 02110-1301 USA
+/////////////////////////////////////////////////////////////////////////
+/*
+ * flags functions
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu-common.h"
+#include "panic.h"
+#include "cpu.h"
+#include "x86_flags.h"
+#include "x86.h"
+
+
+/* this is basically bocsh code */
+
+#define LF_SIGN_BIT 31
+
+#define LF_BIT_SD (0) /* lazy Sign Flag Delta */
+#define LF_BIT_AF (3) /* lazy Adjust flag */
+#define LF_BIT_PDB (8) /* lazy Parity Delta Byte (8 bits) */
+#define LF_BIT_CF (31) /* lazy Carry Flag */
+#define LF_BIT_PO (30) /* lazy Partial Overflow = CF ^ OF */
+
+#define LF_MASK_SD (0x01 << LF_BIT_SD)
+#define LF_MASK_AF (0x01 << LF_BIT_AF)
+#define LF_MASK_PDB (0xFF << LF_BIT_PDB)
+#define LF_MASK_CF (0x01 << LF_BIT_CF)
+#define LF_MASK_PO (0x01 << LF_BIT_PO)
+
+#define ADD_COUT_VEC(op1, op2, result) \
+ (((op1) & (op2)) | (((op1) | (op2)) & (~(result))))
+
+#define SUB_COUT_VEC(op1, op2, result) \
+ (((~(op1)) & (op2)) | (((~(op1)) ^ (op2)) & (result)))
+
+#define GET_ADD_OVERFLOW(op1, op2, result, mask) \
+ ((((op1) ^ (result)) & ((op2) ^ (result))) & (mask))
+
+/* ******************* */
+/* OSZAPC */
+/* ******************* */
+
+/* size, carries, result */
+#define SET_FLAGS_OSZAPC_SIZE(size, lf_carries, lf_result) { \
+ target_ulong temp = ((lf_carries) & (LF_MASK_AF)) | \
+ (((lf_carries) >> (size - 2)) << LF_BIT_PO); \
+ env->hvf_emul->lflags.result = (target_ulong)(int##size##_t)(lf_result); \
+ if ((size) == 32) { \
+ temp = ((lf_carries) & ~(LF_MASK_PDB | LF_MASK_SD)); \
+ } else if ((size) == 16) { \
+ temp = ((lf_carries) & (LF_MASK_AF)) | ((lf_carries) << 16); \
+ } else if ((size) == 8) { \
+ temp = ((lf_carries) & (LF_MASK_AF)) | ((lf_carries) << 24); \
+ } else { \
+ VM_PANIC("unimplemented"); \
+ } \
+ env->hvf_emul->lflags.auxbits = (target_ulong)(uint32_t)temp; \
+}
+
+/* carries, result */
+#define SET_FLAGS_OSZAPC_8(carries, result) \
+ SET_FLAGS_OSZAPC_SIZE(8, carries, result)
+#define SET_FLAGS_OSZAPC_16(carries, result) \
+ SET_FLAGS_OSZAPC_SIZE(16, carries, result)
+#define SET_FLAGS_OSZAPC_32(carries, result) \
+ SET_FLAGS_OSZAPC_SIZE(32, carries, result)
+
+/* ******************* */
+/* OSZAP */
+/* ******************* */
+/* size, carries, result */
+#define SET_FLAGS_OSZAP_SIZE(size, lf_carries, lf_result) { \
+ target_ulong temp = ((lf_carries) & (LF_MASK_AF)) | \
+ (((lf_carries) >> (size - 2)) << LF_BIT_PO); \
+ if ((size) == 32) { \
+ temp = ((lf_carries) & ~(LF_MASK_PDB | LF_MASK_SD)); \
+ } else if ((size) == 16) { \
+ temp = ((lf_carries) & (LF_MASK_AF)) | ((lf_carries) << 16); \
+ } else if ((size) == 8) { \
+ temp = ((lf_carries) & (LF_MASK_AF)) | ((lf_carries) << 24); \
+ } else { \
+ VM_PANIC("unimplemented"); \
+ } \
+ env->hvf_emul->lflags.result = (target_ulong)(int##size##_t)(lf_result); \
+ target_ulong delta_c = (env->hvf_emul->lflags.auxbits ^ temp) & LF_MASK_CF; \
+ delta_c ^= (delta_c >> 1); \
+ env->hvf_emul->lflags.auxbits = (target_ulong)(uint32_t)(temp ^ delta_c); \
+}
+
+/* carries, result */
+#define SET_FLAGS_OSZAP_8(carries, result) \
+ SET_FLAGS_OSZAP_SIZE(8, carries, result)
+#define SET_FLAGS_OSZAP_16(carries, result) \
+ SET_FLAGS_OSZAP_SIZE(16, carries, result)
+#define SET_FLAGS_OSZAP_32(carries, result) \
+ SET_FLAGS_OSZAP_SIZE(32, carries, result)
+
+void SET_FLAGS_OxxxxC(CPUX86State *env, uint32_t new_of, uint32_t new_cf)
+{
+ uint32_t temp_po = new_of ^ new_cf;
+ env->hvf_emul->lflags.auxbits &= ~(LF_MASK_PO | LF_MASK_CF);
+ env->hvf_emul->lflags.auxbits |= (temp_po << LF_BIT_PO) |
+ (new_cf << LF_BIT_CF);
+}
+
+void SET_FLAGS_OSZAPC_SUB32(CPUX86State *env, uint32_t v1, uint32_t v2,
+ uint32_t diff)
+{
+ SET_FLAGS_OSZAPC_32(SUB_COUT_VEC(v1, v2, diff), diff);
+}
+
+void SET_FLAGS_OSZAPC_SUB16(CPUX86State *env, uint16_t v1, uint16_t v2,
+ uint16_t diff)
+{
+ SET_FLAGS_OSZAPC_16(SUB_COUT_VEC(v1, v2, diff), diff);
+}
+
+void SET_FLAGS_OSZAPC_SUB8(CPUX86State *env, uint8_t v1, uint8_t v2,
+ uint8_t diff)
+{
+ SET_FLAGS_OSZAPC_8(SUB_COUT_VEC(v1, v2, diff), diff);
+}
+
+void SET_FLAGS_OSZAPC_ADD32(CPUX86State *env, uint32_t v1, uint32_t v2,
+ uint32_t diff)
+{
+ SET_FLAGS_OSZAPC_32(ADD_COUT_VEC(v1, v2, diff), diff);
+}
+
+void SET_FLAGS_OSZAPC_ADD16(CPUX86State *env, uint16_t v1, uint16_t v2,
+ uint16_t diff)
+{
+ SET_FLAGS_OSZAPC_16(ADD_COUT_VEC(v1, v2, diff), diff);
+}
+
+void SET_FLAGS_OSZAPC_ADD8(CPUX86State *env, uint8_t v1, uint8_t v2,
+ uint8_t diff)
+{
+ SET_FLAGS_OSZAPC_8(ADD_COUT_VEC(v1, v2, diff), diff);
+}
+
+void SET_FLAGS_OSZAP_SUB32(CPUX86State *env, uint32_t v1, uint32_t v2,
+ uint32_t diff)
+{
+ SET_FLAGS_OSZAP_32(SUB_COUT_VEC(v1, v2, diff), diff);
+}
+
+void SET_FLAGS_OSZAP_SUB16(CPUX86State *env, uint16_t v1, uint16_t v2,
+ uint16_t diff)
+{
+ SET_FLAGS_OSZAP_16(SUB_COUT_VEC(v1, v2, diff), diff);
+}
+
+void SET_FLAGS_OSZAP_SUB8(CPUX86State *env, uint8_t v1, uint8_t v2,
+ uint8_t diff)
+{
+ SET_FLAGS_OSZAP_8(SUB_COUT_VEC(v1, v2, diff), diff);
+}
+
+void SET_FLAGS_OSZAP_ADD32(CPUX86State *env, uint32_t v1, uint32_t v2,
+ uint32_t diff)
+{
+ SET_FLAGS_OSZAP_32(ADD_COUT_VEC(v1, v2, diff), diff);
+}
+
+void SET_FLAGS_OSZAP_ADD16(CPUX86State *env, uint16_t v1, uint16_t v2,
+ uint16_t diff)
+{
+ SET_FLAGS_OSZAP_16(ADD_COUT_VEC(v1, v2, diff), diff);
+}
+
+void SET_FLAGS_OSZAP_ADD8(CPUX86State *env, uint8_t v1, uint8_t v2,
+ uint8_t diff)
+{
+ SET_FLAGS_OSZAP_8(ADD_COUT_VEC(v1, v2, diff), diff);
+}
+
+
+void SET_FLAGS_OSZAPC_LOGIC32(CPUX86State *env, uint32_t v1, uint32_t v2,
+ uint32_t diff)
+{
+ SET_FLAGS_OSZAPC_32(0, diff);
+}
+
+void SET_FLAGS_OSZAPC_LOGIC16(CPUX86State *env, uint16_t v1, uint16_t v2,
+ uint16_t diff)
+{
+ SET_FLAGS_OSZAPC_16(0, diff);
+}
+
+void SET_FLAGS_OSZAPC_LOGIC8(CPUX86State *env, uint8_t v1, uint8_t v2,
+ uint8_t diff)
+{
+ SET_FLAGS_OSZAPC_8(0, diff);
+}
+
+bool get_PF(CPUX86State *env)
+{
+ uint32_t temp = (255 & env->hvf_emul->lflags.result);
+ temp = temp ^ (255 & (env->hvf_emul->lflags.auxbits >> LF_BIT_PDB));
+ temp = (temp ^ (temp >> 4)) & 0x0F;
+ return (0x9669U >> temp) & 1;
+}
+
+void set_PF(CPUX86State *env, bool val)
+{
+ uint32_t temp = (255 & env->hvf_emul->lflags.result) ^ (!val);
+ env->hvf_emul->lflags.auxbits &= ~(LF_MASK_PDB);
+ env->hvf_emul->lflags.auxbits |= (temp << LF_BIT_PDB);
+}
+
+bool get_OF(CPUX86State *env)
+{
+ return ((env->hvf_emul->lflags.auxbits + (1U << LF_BIT_PO)) >> LF_BIT_CF) & 1;
+}
+
+bool get_CF(CPUX86State *env)
+{
+ return (env->hvf_emul->lflags.auxbits >> LF_BIT_CF) & 1;
+}
+
+void set_OF(CPUX86State *env, bool val)
+{
+ bool old_cf = get_CF(env);
+ SET_FLAGS_OxxxxC(env, val, old_cf);
+}
+
+void set_CF(CPUX86State *env, bool val)
+{
+ bool old_of = get_OF(env);
+ SET_FLAGS_OxxxxC(env, old_of, val);
+}
+
+bool get_AF(CPUX86State *env)
+{
+ return (env->hvf_emul->lflags.auxbits >> LF_BIT_AF) & 1;
+}
+
+void set_AF(CPUX86State *env, bool val)
+{
+ env->hvf_emul->lflags.auxbits &= ~(LF_MASK_AF);
+ env->hvf_emul->lflags.auxbits |= val << LF_BIT_AF;
+}
+
+bool get_ZF(CPUX86State *env)
+{
+ return !env->hvf_emul->lflags.result;
+}
+
+void set_ZF(CPUX86State *env, bool val)
+{
+ if (val) {
+ env->hvf_emul->lflags.auxbits ^=
+ (((env->hvf_emul->lflags.result >> LF_SIGN_BIT) & 1) << LF_BIT_SD);
+ /* merge the parity bits into the Parity Delta Byte */
+ uint32_t temp_pdb = (255 & env->hvf_emul->lflags.result);
+ env->hvf_emul->lflags.auxbits ^= (temp_pdb << LF_BIT_PDB);
+ /* now zero the .result value */
+ env->hvf_emul->lflags.result = 0;
+ } else {
+ env->hvf_emul->lflags.result |= (1 << 8);
+ }
+}
+
+bool get_SF(CPUX86State *env)
+{
+ return ((env->hvf_emul->lflags.result >> LF_SIGN_BIT) ^
+ (env->hvf_emul->lflags.auxbits >> LF_BIT_SD)) & 1;
+}
+
+void set_SF(CPUX86State *env, bool val)
+{
+ bool temp_sf = get_SF(env);
+ env->hvf_emul->lflags.auxbits ^= (temp_sf ^ val) << LF_BIT_SD;
+}
+
+void lflags_to_rflags(CPUX86State *env)
+{
+ env->hvf_emul->rflags.cf = get_CF(env);
+ env->hvf_emul->rflags.pf = get_PF(env);
+ env->hvf_emul->rflags.af = get_AF(env);
+ env->hvf_emul->rflags.zf = get_ZF(env);
+ env->hvf_emul->rflags.sf = get_SF(env);
+ env->hvf_emul->rflags.of = get_OF(env);
+}
+
+void rflags_to_lflags(CPUX86State *env)
+{
+ env->hvf_emul->lflags.auxbits = env->hvf_emul->lflags.result = 0;
+ set_OF(env, env->hvf_emul->rflags.of);
+ set_SF(env, env->hvf_emul->rflags.sf);
+ set_ZF(env, env->hvf_emul->rflags.zf);
+ set_AF(env, env->hvf_emul->rflags.af);
+ set_PF(env, env->hvf_emul->rflags.pf);
+ set_CF(env, env->hvf_emul->rflags.cf);
+}
diff --git a/target/i386/hvf/x86_flags.h b/target/i386/hvf/x86_flags.h
new file mode 100644
index 0000000000..8942745988
--- /dev/null
+++ b/target/i386/hvf/x86_flags.h
@@ -0,0 +1,80 @@
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001-2012 The Bochs Project
+// Copyright (C) 2017 Google Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA B 02110-1301 USA
+/////////////////////////////////////////////////////////////////////////
+/*
+ * x86 eflags functions
+ */
+#ifndef __X86_FLAGS_H__
+#define __X86_FLAGS_H__
+
+#include "cpu.h"
+void lflags_to_rflags(CPUX86State *env);
+void rflags_to_lflags(CPUX86State *env);
+
+bool get_PF(CPUX86State *env);
+void set_PF(CPUX86State *env, bool val);
+bool get_CF(CPUX86State *env);
+void set_CF(CPUX86State *env, bool val);
+bool get_AF(CPUX86State *env);
+void set_AF(CPUX86State *env, bool val);
+bool get_ZF(CPUX86State *env);
+void set_ZF(CPUX86State *env, bool val);
+bool get_SF(CPUX86State *env);
+void set_SF(CPUX86State *env, bool val);
+bool get_OF(CPUX86State *env);
+void set_OF(CPUX86State *env, bool val);
+
+void SET_FLAGS_OxxxxC(CPUX86State *env, uint32_t new_of, uint32_t new_cf);
+
+void SET_FLAGS_OSZAPC_SUB32(CPUX86State *env, uint32_t v1, uint32_t v2,
+ uint32_t diff);
+void SET_FLAGS_OSZAPC_SUB16(CPUX86State *env, uint16_t v1, uint16_t v2,
+ uint16_t diff);
+void SET_FLAGS_OSZAPC_SUB8(CPUX86State *env, uint8_t v1, uint8_t v2,
+ uint8_t diff);
+
+void SET_FLAGS_OSZAPC_ADD32(CPUX86State *env, uint32_t v1, uint32_t v2,
+ uint32_t diff);
+void SET_FLAGS_OSZAPC_ADD16(CPUX86State *env, uint16_t v1, uint16_t v2,
+ uint16_t diff);
+void SET_FLAGS_OSZAPC_ADD8(CPUX86State *env, uint8_t v1, uint8_t v2,
+ uint8_t diff);
+
+void SET_FLAGS_OSZAP_SUB32(CPUX86State *env, uint32_t v1, uint32_t v2,
+ uint32_t diff);
+void SET_FLAGS_OSZAP_SUB16(CPUX86State *env, uint16_t v1, uint16_t v2,
+ uint16_t diff);
+void SET_FLAGS_OSZAP_SUB8(CPUX86State *env, uint8_t v1, uint8_t v2,
+ uint8_t diff);
+
+void SET_FLAGS_OSZAP_ADD32(CPUX86State *env, uint32_t v1, uint32_t v2,
+ uint32_t diff);
+void SET_FLAGS_OSZAP_ADD16(CPUX86State *env, uint16_t v1, uint16_t v2,
+ uint16_t diff);
+void SET_FLAGS_OSZAP_ADD8(CPUX86State *env, uint8_t v1, uint8_t v2,
+ uint8_t diff);
+
+void SET_FLAGS_OSZAPC_LOGIC32(CPUX86State *env, uint32_t v1, uint32_t v2,
+ uint32_t diff);
+void SET_FLAGS_OSZAPC_LOGIC16(CPUX86State *env, uint16_t v1, uint16_t v2,
+ uint16_t diff);
+void SET_FLAGS_OSZAPC_LOGIC8(CPUX86State *env, uint8_t v1, uint8_t v2,
+ uint8_t diff);
+
+#endif /* __X86_FLAGS_H__ */
diff --git a/target/i386/hvf/x86_mmu.c b/target/i386/hvf/x86_mmu.c
new file mode 100644
index 0000000000..5c1f35acd0
--- /dev/null
+++ b/target/i386/hvf/x86_mmu.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2016 Veertu Inc,
+ * Copyright (C) 2017 Google Inc,
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "qemu/osdep.h"
+#include "panic.h"
+
+#include "qemu-common.h"
+#include "cpu.h"
+#include "x86.h"
+#include "x86_mmu.h"
+#include "string.h"
+#include "vmcs.h"
+#include "vmx.h"
+
+#include "memory.h"
+#include "exec/address-spaces.h"
+
+#define pte_present(pte) (pte & PT_PRESENT)
+#define pte_write_access(pte) (pte & PT_WRITE)
+#define pte_user_access(pte) (pte & PT_USER)
+#define pte_exec_access(pte) (!(pte & PT_NX))
+
+#define pte_large_page(pte) (pte & PT_PS)
+#define pte_global_access(pte) (pte & PT_GLOBAL)
+
+#define PAE_CR3_MASK (~0x1fllu)
+#define LEGACY_CR3_MASK (0xffffffff)
+
+#define LEGACY_PTE_PAGE_MASK (0xffffffffllu << 12)
+#define PAE_PTE_PAGE_MASK ((-1llu << 12) & ((1llu << 52) - 1))
+#define PAE_PTE_LARGE_PAGE_MASK ((-1llu << (21)) & ((1llu << 52) - 1))
+
+struct gpt_translation {
+ target_ulong gva;
+ uint64_t gpa;
+ int err_code;
+ uint64_t pte[5];
+ bool write_access;
+ bool user_access;
+ bool exec_access;
+};
+
+static int gpt_top_level(struct CPUState *cpu, bool pae)
+{
+ if (!pae) {
+ return 2;
+ }
+ if (x86_is_long_mode(cpu)) {
+ return 4;
+ }
+
+ return 3;
+}
+
+static inline int gpt_entry(target_ulong addr, int level, bool pae)
+{
+ int level_shift = pae ? 9 : 10;
+ return (addr >> (level_shift * (level - 1) + 12)) & ((1 << level_shift) - 1);
+}
+
+static inline int pte_size(bool pae)
+{
+ return pae ? 8 : 4;
+}
+
+
+static bool get_pt_entry(struct CPUState *cpu, struct gpt_translation *pt,
+ int level, bool pae)
+{
+ int index;
+ uint64_t pte = 0;
+ uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK;
+ uint64_t gpa = pt->pte[level] & page_mask;
+
+ if (level == 3 && !x86_is_long_mode(cpu)) {
+ gpa = pt->pte[level];
+ }
+
+ index = gpt_entry(pt->gva, level, pae);
+ address_space_rw(&address_space_memory, gpa + index * pte_size(pae),
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)&pte, pte_size(pae), 0);
+
+ pt->pte[level - 1] = pte;
+
+ return true;
+}
+
+/* test page table entry */
+static bool test_pt_entry(struct CPUState *cpu, struct gpt_translation *pt,
+ int level, bool *is_large, bool pae)
+{
+ uint64_t pte = pt->pte[level];
+
+ if (pt->write_access) {
+ pt->err_code |= MMU_PAGE_WT;
+ }
+ if (pt->user_access) {
+ pt->err_code |= MMU_PAGE_US;
+ }
+ if (pt->exec_access) {
+ pt->err_code |= MMU_PAGE_NX;
+ }
+
+ if (!pte_present(pte)) {
+ return false;
+ }
+
+ if (pae && !x86_is_long_mode(cpu) && 2 == level) {
+ goto exit;
+ }
+
+ if (1 == level && pte_large_page(pte)) {
+ pt->err_code |= MMU_PAGE_PT;
+ *is_large = true;
+ }
+ if (!level) {
+ pt->err_code |= MMU_PAGE_PT;
+ }
+
+ uint32_t cr0 = rvmcs(cpu->hvf_fd, VMCS_GUEST_CR0);
+ /* check protection */
+ if (cr0 & CR0_WP) {
+ if (pt->write_access && !pte_write_access(pte)) {
+ return false;
+ }
+ }
+
+ if (pt->user_access && !pte_user_access(pte)) {
+ return false;
+ }
+
+ if (pae && pt->exec_access && !pte_exec_access(pte)) {
+ return false;
+ }
+
+exit:
+ /* TODO: check reserved bits */
+ return true;
+}
+
+static inline uint64_t pse_pte_to_page(uint64_t pte)
+{
+ return ((pte & 0x1fe000) << 19) | (pte & 0xffc00000);
+}
+
+static inline uint64_t large_page_gpa(struct gpt_translation *pt, bool pae)
+{
+ VM_PANIC_ON(!pte_large_page(pt->pte[1]))
+ /* 2Mb large page */
+ if (pae) {
+ return (pt->pte[1] & PAE_PTE_LARGE_PAGE_MASK) | (pt->gva & 0x1fffff);
+ }
+
+ /* 4Mb large page */
+ return pse_pte_to_page(pt->pte[1]) | (pt->gva & 0x3fffff);
+}
+
+
+
+static bool walk_gpt(struct CPUState *cpu, target_ulong addr, int err_code,
+ struct gpt_translation *pt, bool pae)
+{
+ int top_level, level;
+ bool is_large = false;
+ target_ulong cr3 = rvmcs(cpu->hvf_fd, VMCS_GUEST_CR3);
+ uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK;
+
+ memset(pt, 0, sizeof(*pt));
+ top_level = gpt_top_level(cpu, pae);
+
+ pt->pte[top_level] = pae ? (cr3 & PAE_CR3_MASK) : (cr3 & LEGACY_CR3_MASK);
+ pt->gva = addr;
+ pt->user_access = (err_code & MMU_PAGE_US);
+ pt->write_access = (err_code & MMU_PAGE_WT);
+ pt->exec_access = (err_code & MMU_PAGE_NX);
+
+ for (level = top_level; level > 0; level--) {
+ get_pt_entry(cpu, pt, level, pae);
+
+ if (!test_pt_entry(cpu, pt, level - 1, &is_large, pae)) {
+ return false;
+ }
+
+ if (is_large) {
+ break;
+ }
+ }
+
+ if (!is_large) {
+ pt->gpa = (pt->pte[0] & page_mask) | (pt->gva & 0xfff);
+ } else {
+ pt->gpa = large_page_gpa(pt, pae);
+ }
+
+ return true;
+}
+
+
+bool mmu_gva_to_gpa(struct CPUState *cpu, target_ulong gva, uint64_t *gpa)
+{
+ bool res;
+ struct gpt_translation pt;
+ int err_code = 0;
+
+ if (!x86_is_paging_mode(cpu)) {
+ *gpa = gva;
+ return true;
+ }
+
+ res = walk_gpt(cpu, gva, err_code, &pt, x86_is_pae_enabled(cpu));
+ if (res) {
+ *gpa = pt.gpa;
+ return true;
+ }
+
+ return false;
+}
+
+void vmx_write_mem(struct CPUState *cpu, target_ulong gva, void *data, int bytes)
+{
+ uint64_t gpa;
+
+ while (bytes > 0) {
+ /* copy page */
+ int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
+
+ if (!mmu_gva_to_gpa(cpu, gva, &gpa)) {
+ VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva);
+ } else {
+ address_space_rw(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED,
+ data, copy, 1);
+ }
+
+ bytes -= copy;
+ gva += copy;
+ data += copy;
+ }
+}
+
+void vmx_read_mem(struct CPUState *cpu, void *data, target_ulong gva, int bytes)
+{
+ uint64_t gpa;
+
+ while (bytes > 0) {
+ /* copy page */
+ int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
+
+ if (!mmu_gva_to_gpa(cpu, gva, &gpa)) {
+ VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva);
+ }
+ address_space_rw(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED,
+ data, copy, 0);
+
+ bytes -= copy;
+ gva += copy;
+ data += copy;
+ }
+}
diff --git a/target/i386/hvf/x86_mmu.h b/target/i386/hvf/x86_mmu.h
new file mode 100644
index 0000000000..0bd1acc94f
--- /dev/null
+++ b/target/i386/hvf/x86_mmu.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 Veertu Inc,
+ * Copyright (C) 2017 Google Inc,
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __X86_MMU_H__
+#define __X86_MMU_H__
+
+#define PT_PRESENT (1 << 0)
+#define PT_WRITE (1 << 1)
+#define PT_USER (1 << 2)
+#define PT_WT (1 << 3)
+#define PT_CD (1 << 4)
+#define PT_ACCESSED (1 << 5)
+#define PT_DIRTY (1 << 6)
+#define PT_PS (1 << 7)
+#define PT_GLOBAL (1 << 8)
+#define PT_NX (1llu << 63)
+
+/* error codes */
+#define MMU_PAGE_PT (1 << 0)
+#define MMU_PAGE_WT (1 << 1)
+#define MMU_PAGE_US (1 << 2)
+#define MMU_PAGE_NX (1 << 3)
+
+bool mmu_gva_to_gpa(struct CPUState *cpu, target_ulong gva, uint64_t *gpa);
+
+void vmx_write_mem(struct CPUState *cpu, target_ulong gva, void *data, int bytes);
+void vmx_read_mem(struct CPUState *cpu, void *data, target_ulong gva, int bytes);
+
+#endif /* __X86_MMU_H__ */
diff --git a/target/i386/hvf/x86_task.c b/target/i386/hvf/x86_task.c
new file mode 100644
index 0000000000..d7f665f8fa
--- /dev/null
+++ b/target/i386/hvf/x86_task.c
@@ -0,0 +1,191 @@
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+#include "qemu/osdep.h"
+#include "panic.h"
+#include "qemu-common.h"
+#include "qemu/error-report.h"
+
+#include "sysemu/hvf.h"
+#include "hvf-i386.h"
+#include "vmcs.h"
+#include "vmx.h"
+#include "x86.h"
+#include "x86_descr.h"
+#include "x86_mmu.h"
+#include "x86_decode.h"
+#include "x86_emu.h"
+#include "x86_task.h"
+#include "x86hvf.h"
+
+#include <Hypervisor/hv.h>
+#include <Hypervisor/hv_vmx.h>
+
+#include "exec/address-spaces.h"
+#include "exec/exec-all.h"
+#include "exec/ioport.h"
+#include "hw/i386/apic_internal.h"
+#include "hw/boards.h"
+#include "qemu/main-loop.h"
+#include "strings.h"
+#include "sysemu/accel.h"
+#include "sysemu/sysemu.h"
+#include "target/i386/cpu.h"
+
+// TODO: taskswitch handling
+static void save_state_to_tss32(CPUState *cpu, struct x86_tss_segment32 *tss)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+
+ /* CR3 and ldt selector are not saved intentionally */
+ tss->eip = EIP(env);
+ tss->eflags = EFLAGS(env);
+ tss->eax = EAX(env);
+ tss->ecx = ECX(env);
+ tss->edx = EDX(env);
+ tss->ebx = EBX(env);
+ tss->esp = ESP(env);
+ tss->ebp = EBP(env);
+ tss->esi = ESI(env);
+ tss->edi = EDI(env);
+
+ tss->es = vmx_read_segment_selector(cpu, R_ES).sel;
+ tss->cs = vmx_read_segment_selector(cpu, R_CS).sel;
+ tss->ss = vmx_read_segment_selector(cpu, R_SS).sel;
+ tss->ds = vmx_read_segment_selector(cpu, R_DS).sel;
+ tss->fs = vmx_read_segment_selector(cpu, R_FS).sel;
+ tss->gs = vmx_read_segment_selector(cpu, R_GS).sel;
+}
+
+static void load_state_from_tss32(CPUState *cpu, struct x86_tss_segment32 *tss)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+
+ wvmcs(cpu->hvf_fd, VMCS_GUEST_CR3, tss->cr3);
+
+ RIP(env) = tss->eip;
+ EFLAGS(env) = tss->eflags | 2;
+
+ /* General purpose registers */
+ RAX(env) = tss->eax;
+ RCX(env) = tss->ecx;
+ RDX(env) = tss->edx;
+ RBX(env) = tss->ebx;
+ RSP(env) = tss->esp;
+ RBP(env) = tss->ebp;
+ RSI(env) = tss->esi;
+ RDI(env) = tss->edi;
+
+ vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ldt}}, R_LDTR);
+ vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->es}}, R_ES);
+ vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->cs}}, R_CS);
+ vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ss}}, R_SS);
+ vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ds}}, R_DS);
+ vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->fs}}, R_FS);
+ vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->gs}}, R_GS);
+}
+
+static int task_switch_32(CPUState *cpu, x68_segment_selector tss_sel, x68_segment_selector old_tss_sel,
+ uint64_t old_tss_base, struct x86_segment_descriptor *new_desc)
+{
+ struct x86_tss_segment32 tss_seg;
+ uint32_t new_tss_base = x86_segment_base(new_desc);
+ uint32_t eip_offset = offsetof(struct x86_tss_segment32, eip);
+ uint32_t ldt_sel_offset = offsetof(struct x86_tss_segment32, ldt);
+
+ vmx_read_mem(cpu, &tss_seg, old_tss_base, sizeof(tss_seg));
+ save_state_to_tss32(cpu, &tss_seg);
+
+ vmx_write_mem(cpu, old_tss_base + eip_offset, &tss_seg.eip, ldt_sel_offset - eip_offset);
+ vmx_read_mem(cpu, &tss_seg, new_tss_base, sizeof(tss_seg));
+
+ if (old_tss_sel.sel != 0xffff) {
+ tss_seg.prev_tss = old_tss_sel.sel;
+
+ vmx_write_mem(cpu, new_tss_base, &tss_seg.prev_tss, sizeof(tss_seg.prev_tss));
+ }
+ load_state_from_tss32(cpu, &tss_seg);
+ return 0;
+}
+
+void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int reason, bool gate_valid, uint8_t gate, uint64_t gate_type)
+{
+ uint64_t rip = rreg(cpu->hvf_fd, HV_X86_RIP);
+ if (!gate_valid || (gate_type != VMCS_INTR_T_HWEXCEPTION &&
+ gate_type != VMCS_INTR_T_HWINTR &&
+ gate_type != VMCS_INTR_T_NMI)) {
+ int ins_len = rvmcs(cpu->hvf_fd, VMCS_EXIT_INSTRUCTION_LENGTH);
+ macvm_set_rip(cpu, rip + ins_len);
+ return;
+ }
+
+ load_regs(cpu);
+
+ struct x86_segment_descriptor curr_tss_desc, next_tss_desc;
+ int ret;
+ x68_segment_selector old_tss_sel = vmx_read_segment_selector(cpu, R_TR);
+ uint64_t old_tss_base = vmx_read_segment_base(cpu, R_TR);
+ uint32_t desc_limit;
+ struct x86_call_gate task_gate_desc;
+ struct vmx_segment vmx_seg;
+
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+
+ x86_read_segment_descriptor(cpu, &next_tss_desc, tss_sel);
+ x86_read_segment_descriptor(cpu, &curr_tss_desc, old_tss_sel);
+
+ if (reason == TSR_IDT_GATE && gate_valid) {
+ int dpl;
+
+ ret = x86_read_call_gate(cpu, &task_gate_desc, gate);
+
+ dpl = task_gate_desc.dpl;
+ x68_segment_selector cs = vmx_read_segment_selector(cpu, R_CS);
+ if (tss_sel.rpl > dpl || cs.rpl > dpl)
+ ;//DPRINTF("emulate_gp");
+ }
+
+ desc_limit = x86_segment_limit(&next_tss_desc);
+ if (!next_tss_desc.p || ((desc_limit < 0x67 && (next_tss_desc.type & 8)) || desc_limit < 0x2b)) {
+ VM_PANIC("emulate_ts");
+ }
+
+ if (reason == TSR_IRET || reason == TSR_JMP) {
+ curr_tss_desc.type &= ~(1 << 1); /* clear busy flag */
+ x86_write_segment_descriptor(cpu, &curr_tss_desc, old_tss_sel);
+ }
+
+ if (reason == TSR_IRET)
+ EFLAGS(env) &= ~RFLAGS_NT;
+
+ if (reason != TSR_CALL && reason != TSR_IDT_GATE)
+ old_tss_sel.sel = 0xffff;
+
+ if (reason != TSR_IRET) {
+ next_tss_desc.type |= (1 << 1); /* set busy flag */
+ x86_write_segment_descriptor(cpu, &next_tss_desc, tss_sel);
+ }
+
+ if (next_tss_desc.type & 8)
+ ret = task_switch_32(cpu, tss_sel, old_tss_sel, old_tss_base, &next_tss_desc);
+ else
+ //ret = task_switch_16(cpu, tss_sel, old_tss_sel, old_tss_base, &next_tss_desc);
+ VM_PANIC("task_switch_16");
+
+ macvm_set_cr0(cpu->hvf_fd, rvmcs(cpu->hvf_fd, VMCS_GUEST_CR0) | CR0_TS);
+ x86_segment_descriptor_to_vmx(cpu, tss_sel, &next_tss_desc, &vmx_seg);
+ vmx_write_segment_descriptor(cpu, &vmx_seg, R_TR);
+
+ store_regs(cpu);
+
+ hv_vcpu_invalidate_tlb(cpu->hvf_fd);
+ hv_vcpu_flush(cpu->hvf_fd);
+}
diff --git a/target/i386/hvf/x86_task.h b/target/i386/hvf/x86_task.h
new file mode 100644
index 0000000000..4f1b188d2e
--- /dev/null
+++ b/target/i386/hvf/x86_task.h
@@ -0,0 +1,18 @@
+/* This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef HVF_TASK
+#define HVF_TASK
+void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel,
+ int reason, bool gate_valid, uint8_t gate, uint64_t gate_type);
+#endif
diff --git a/target/i386/hvf/x86hvf.c b/target/i386/hvf/x86hvf.c
new file mode 100644
index 0000000000..71c0515073
--- /dev/null
+++ b/target/i386/hvf/x86hvf.c
@@ -0,0 +1,465 @@
+/*
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (C) 2016 Veertu Inc,
+ * Copyright (C) 2017 Google Inc,
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu-common.h"
+#include "x86hvf.h"
+#include "vmx.h"
+#include "vmcs.h"
+#include "cpu.h"
+#include "x86_descr.h"
+#include "x86_decode.h"
+
+#include "hw/i386/apic_internal.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <Hypervisor/hv.h>
+#include <Hypervisor/hv_vmx.h>
+#include <stdint.h>
+
+void hvf_set_segment(struct CPUState *cpu, struct vmx_segment *vmx_seg,
+ SegmentCache *qseg, bool is_tr)
+{
+ vmx_seg->sel = qseg->selector;
+ vmx_seg->base = qseg->base;
+ vmx_seg->limit = qseg->limit;
+
+ if (!qseg->selector && !x86_is_real(cpu) && !is_tr) {
+ /* the TR register is usable after processor reset despite
+ * having a null selector */
+ vmx_seg->ar = 1 << 16;
+ return;
+ }
+ vmx_seg->ar = (qseg->flags >> DESC_TYPE_SHIFT) & 0xf;
+ vmx_seg->ar |= ((qseg->flags >> DESC_G_SHIFT) & 1) << 15;
+ vmx_seg->ar |= ((qseg->flags >> DESC_B_SHIFT) & 1) << 14;
+ vmx_seg->ar |= ((qseg->flags >> DESC_L_SHIFT) & 1) << 13;
+ vmx_seg->ar |= ((qseg->flags >> DESC_AVL_SHIFT) & 1) << 12;
+ vmx_seg->ar |= ((qseg->flags >> DESC_P_SHIFT) & 1) << 7;
+ vmx_seg->ar |= ((qseg->flags >> DESC_DPL_SHIFT) & 3) << 5;
+ vmx_seg->ar |= ((qseg->flags >> DESC_S_SHIFT) & 1) << 4;
+}
+
+void hvf_get_segment(SegmentCache *qseg, struct vmx_segment *vmx_seg)
+{
+ qseg->limit = vmx_seg->limit;
+ qseg->base = vmx_seg->base;
+ qseg->selector = vmx_seg->sel;
+ qseg->flags = ((vmx_seg->ar & 0xf) << DESC_TYPE_SHIFT) |
+ (((vmx_seg->ar >> 4) & 1) << DESC_S_SHIFT) |
+ (((vmx_seg->ar >> 5) & 3) << DESC_DPL_SHIFT) |
+ (((vmx_seg->ar >> 7) & 1) << DESC_P_SHIFT) |
+ (((vmx_seg->ar >> 12) & 1) << DESC_AVL_SHIFT) |
+ (((vmx_seg->ar >> 13) & 1) << DESC_L_SHIFT) |
+ (((vmx_seg->ar >> 14) & 1) << DESC_B_SHIFT) |
+ (((vmx_seg->ar >> 15) & 1) << DESC_G_SHIFT);
+}
+
+void hvf_put_xsave(CPUState *cpu_state)
+{
+
+ struct X86XSaveArea *xsave;
+
+ xsave = X86_CPU(cpu_state)->env.kvm_xsave_buf;
+
+ x86_cpu_xsave_all_areas(X86_CPU(cpu_state), xsave);
+
+ if (hv_vcpu_write_fpstate(cpu_state->hvf_fd, (void*)xsave, 4096)) {
+ abort();
+ }
+}
+
+void hvf_put_segments(CPUState *cpu_state)
+{
+ CPUX86State *env = &X86_CPU(cpu_state)->env;
+ struct vmx_segment seg;
+
+ wvmcs(cpu_state->hvf_fd, VMCS_GUEST_IDTR_LIMIT, env->idt.limit);
+ wvmcs(cpu_state->hvf_fd, VMCS_GUEST_IDTR_BASE, env->idt.base);
+
+ wvmcs(cpu_state->hvf_fd, VMCS_GUEST_GDTR_LIMIT, env->gdt.limit);
+ wvmcs(cpu_state->hvf_fd, VMCS_GUEST_GDTR_BASE, env->gdt.base);
+
+ /* wvmcs(cpu_state->hvf_fd, VMCS_GUEST_CR2, env->cr[2]); */
+ wvmcs(cpu_state->hvf_fd, VMCS_GUEST_CR3, env->cr[3]);
+ vmx_update_tpr(cpu_state);
+ wvmcs(cpu_state->hvf_fd, VMCS_GUEST_IA32_EFER, env->efer);
+
+ macvm_set_cr4(cpu_state->hvf_fd, env->cr[4]);
+ macvm_set_cr0(cpu_state->hvf_fd, env->cr[0]);
+
+ hvf_set_segment(cpu_state, &seg, &env->segs[R_CS], false);
+ vmx_write_segment_descriptor(cpu_state, &seg, R_CS);
+
+ hvf_set_segment(cpu_state, &seg, &env->segs[R_DS], false);
+ vmx_write_segment_descriptor(cpu_state, &seg, R_DS);
+
+ hvf_set_segment(cpu_state, &seg, &env->segs[R_ES], false);
+ vmx_write_segment_descriptor(cpu_state, &seg, R_ES);
+
+ hvf_set_segment(cpu_state, &seg, &env->segs[R_SS], false);
+ vmx_write_segment_descriptor(cpu_state, &seg, R_SS);
+
+ hvf_set_segment(cpu_state, &seg, &env->segs[R_FS], false);
+ vmx_write_segment_descriptor(cpu_state, &seg, R_FS);
+
+ hvf_set_segment(cpu_state, &seg, &env->segs[R_GS], false);
+ vmx_write_segment_descriptor(cpu_state, &seg, R_GS);
+
+ hvf_set_segment(cpu_state, &seg, &env->tr, true);
+ vmx_write_segment_descriptor(cpu_state, &seg, R_TR);
+
+ hvf_set_segment(cpu_state, &seg, &env->ldt, false);
+ vmx_write_segment_descriptor(cpu_state, &seg, R_LDTR);
+
+ hv_vcpu_flush(cpu_state->hvf_fd);
+}
+
+void hvf_put_msrs(CPUState *cpu_state)
+{
+ CPUX86State *env = &X86_CPU(cpu_state)->env;
+
+ hv_vcpu_write_msr(cpu_state->hvf_fd, MSR_IA32_SYSENTER_CS,
+ env->sysenter_cs);
+ hv_vcpu_write_msr(cpu_state->hvf_fd, MSR_IA32_SYSENTER_ESP,
+ env->sysenter_esp);
+ hv_vcpu_write_msr(cpu_state->hvf_fd, MSR_IA32_SYSENTER_EIP,
+ env->sysenter_eip);
+
+ hv_vcpu_write_msr(cpu_state->hvf_fd, MSR_STAR, env->star);
+
+#ifdef TARGET_X86_64
+ hv_vcpu_write_msr(cpu_state->hvf_fd, MSR_CSTAR, env->cstar);
+ hv_vcpu_write_msr(cpu_state->hvf_fd, MSR_KERNELGSBASE, env->kernelgsbase);
+ hv_vcpu_write_msr(cpu_state->hvf_fd, MSR_FMASK, env->fmask);
+ hv_vcpu_write_msr(cpu_state->hvf_fd, MSR_LSTAR, env->lstar);
+#endif
+
+ hv_vcpu_write_msr(cpu_state->hvf_fd, MSR_GSBASE, env->segs[R_GS].base);
+ hv_vcpu_write_msr(cpu_state->hvf_fd, MSR_FSBASE, env->segs[R_FS].base);
+
+ /* if (!osx_is_sierra())
+ wvmcs(cpu_state->hvf_fd, VMCS_TSC_OFFSET, env->tsc - rdtscp());*/
+ hv_vm_sync_tsc(env->tsc);
+}
+
+
+void hvf_get_xsave(CPUState *cpu_state)
+{
+ struct X86XSaveArea *xsave;
+
+ xsave = X86_CPU(cpu_state)->env.kvm_xsave_buf;
+
+ if (hv_vcpu_read_fpstate(cpu_state->hvf_fd, (void*)xsave, 4096)) {
+ abort();
+ }
+
+ x86_cpu_xrstor_all_areas(X86_CPU(cpu_state), xsave);
+}
+
+void hvf_get_segments(CPUState *cpu_state)
+{
+ CPUX86State *env = &X86_CPU(cpu_state)->env;
+
+ struct vmx_segment seg;
+
+ env->interrupt_injected = -1;
+
+ vmx_read_segment_descriptor(cpu_state, &seg, R_CS);
+ hvf_get_segment(&env->segs[R_CS], &seg);
+
+ vmx_read_segment_descriptor(cpu_state, &seg, R_DS);
+ hvf_get_segment(&env->segs[R_DS], &seg);
+
+ vmx_read_segment_descriptor(cpu_state, &seg, R_ES);
+ hvf_get_segment(&env->segs[R_ES], &seg);
+
+ vmx_read_segment_descriptor(cpu_state, &seg, R_FS);
+ hvf_get_segment(&env->segs[R_FS], &seg);
+
+ vmx_read_segment_descriptor(cpu_state, &seg, R_GS);
+ hvf_get_segment(&env->segs[R_GS], &seg);
+
+ vmx_read_segment_descriptor(cpu_state, &seg, R_SS);
+ hvf_get_segment(&env->segs[R_SS], &seg);
+
+ vmx_read_segment_descriptor(cpu_state, &seg, R_TR);
+ hvf_get_segment(&env->tr, &seg);
+
+ vmx_read_segment_descriptor(cpu_state, &seg, R_LDTR);
+ hvf_get_segment(&env->ldt, &seg);
+
+ env->idt.limit = rvmcs(cpu_state->hvf_fd, VMCS_GUEST_IDTR_LIMIT);
+ env->idt.base = rvmcs(cpu_state->hvf_fd, VMCS_GUEST_IDTR_BASE);
+ env->gdt.limit = rvmcs(cpu_state->hvf_fd, VMCS_GUEST_GDTR_LIMIT);
+ env->gdt.base = rvmcs(cpu_state->hvf_fd, VMCS_GUEST_GDTR_BASE);
+
+ env->cr[0] = rvmcs(cpu_state->hvf_fd, VMCS_GUEST_CR0);
+ env->cr[2] = 0;
+ env->cr[3] = rvmcs(cpu_state->hvf_fd, VMCS_GUEST_CR3);
+ env->cr[4] = rvmcs(cpu_state->hvf_fd, VMCS_GUEST_CR4);
+
+ env->efer = rvmcs(cpu_state->hvf_fd, VMCS_GUEST_IA32_EFER);
+}
+
+void hvf_get_msrs(CPUState *cpu_state)
+{
+ CPUX86State *env = &X86_CPU(cpu_state)->env;
+ uint64_t tmp;
+
+ hv_vcpu_read_msr(cpu_state->hvf_fd, MSR_IA32_SYSENTER_CS, &tmp);
+ env->sysenter_cs = tmp;
+
+ hv_vcpu_read_msr(cpu_state->hvf_fd, MSR_IA32_SYSENTER_ESP, &tmp);
+ env->sysenter_esp = tmp;
+
+ hv_vcpu_read_msr(cpu_state->hvf_fd, MSR_IA32_SYSENTER_EIP, &tmp);
+ env->sysenter_eip = tmp;
+
+ hv_vcpu_read_msr(cpu_state->hvf_fd, MSR_STAR, &env->star);
+
+#ifdef TARGET_X86_64
+ hv_vcpu_read_msr(cpu_state->hvf_fd, MSR_CSTAR, &env->cstar);
+ hv_vcpu_read_msr(cpu_state->hvf_fd, MSR_KERNELGSBASE, &env->kernelgsbase);
+ hv_vcpu_read_msr(cpu_state->hvf_fd, MSR_FMASK, &env->fmask);
+ hv_vcpu_read_msr(cpu_state->hvf_fd, MSR_LSTAR, &env->lstar);
+#endif
+
+ hv_vcpu_read_msr(cpu_state->hvf_fd, MSR_IA32_APICBASE, &tmp);
+
+ env->tsc = rdtscp() + rvmcs(cpu_state->hvf_fd, VMCS_TSC_OFFSET);
+}
+
+int hvf_put_registers(CPUState *cpu_state)
+{
+ X86CPU *x86cpu = X86_CPU(cpu_state);
+ CPUX86State *env = &x86cpu->env;
+
+ wreg(cpu_state->hvf_fd, HV_X86_RAX, env->regs[R_EAX]);
+ wreg(cpu_state->hvf_fd, HV_X86_RBX, env->regs[R_EBX]);
+ wreg(cpu_state->hvf_fd, HV_X86_RCX, env->regs[R_ECX]);
+ wreg(cpu_state->hvf_fd, HV_X86_RDX, env->regs[R_EDX]);
+ wreg(cpu_state->hvf_fd, HV_X86_RBP, env->regs[R_EBP]);
+ wreg(cpu_state->hvf_fd, HV_X86_RSP, env->regs[R_ESP]);
+ wreg(cpu_state->hvf_fd, HV_X86_RSI, env->regs[R_ESI]);
+ wreg(cpu_state->hvf_fd, HV_X86_RDI, env->regs[R_EDI]);
+ wreg(cpu_state->hvf_fd, HV_X86_R8, env->regs[8]);
+ wreg(cpu_state->hvf_fd, HV_X86_R9, env->regs[9]);
+ wreg(cpu_state->hvf_fd, HV_X86_R10, env->regs[10]);
+ wreg(cpu_state->hvf_fd, HV_X86_R11, env->regs[11]);
+ wreg(cpu_state->hvf_fd, HV_X86_R12, env->regs[12]);
+ wreg(cpu_state->hvf_fd, HV_X86_R13, env->regs[13]);
+ wreg(cpu_state->hvf_fd, HV_X86_R14, env->regs[14]);
+ wreg(cpu_state->hvf_fd, HV_X86_R15, env->regs[15]);
+ wreg(cpu_state->hvf_fd, HV_X86_RFLAGS, env->eflags);
+ wreg(cpu_state->hvf_fd, HV_X86_RIP, env->eip);
+
+ wreg(cpu_state->hvf_fd, HV_X86_XCR0, env->xcr0);
+
+ hvf_put_xsave(cpu_state);
+
+ hvf_put_segments(cpu_state);
+
+ hvf_put_msrs(cpu_state);
+
+ wreg(cpu_state->hvf_fd, HV_X86_DR0, env->dr[0]);
+ wreg(cpu_state->hvf_fd, HV_X86_DR1, env->dr[1]);
+ wreg(cpu_state->hvf_fd, HV_X86_DR2, env->dr[2]);
+ wreg(cpu_state->hvf_fd, HV_X86_DR3, env->dr[3]);
+ wreg(cpu_state->hvf_fd, HV_X86_DR4, env->dr[4]);
+ wreg(cpu_state->hvf_fd, HV_X86_DR5, env->dr[5]);
+ wreg(cpu_state->hvf_fd, HV_X86_DR6, env->dr[6]);
+ wreg(cpu_state->hvf_fd, HV_X86_DR7, env->dr[7]);
+
+ return 0;
+}
+
+int hvf_get_registers(CPUState *cpu_state)
+{
+ X86CPU *x86cpu = X86_CPU(cpu_state);
+ CPUX86State *env = &x86cpu->env;
+
+
+ env->regs[R_EAX] = rreg(cpu_state->hvf_fd, HV_X86_RAX);
+ env->regs[R_EBX] = rreg(cpu_state->hvf_fd, HV_X86_RBX);
+ env->regs[R_ECX] = rreg(cpu_state->hvf_fd, HV_X86_RCX);
+ env->regs[R_EDX] = rreg(cpu_state->hvf_fd, HV_X86_RDX);
+ env->regs[R_EBP] = rreg(cpu_state->hvf_fd, HV_X86_RBP);
+ env->regs[R_ESP] = rreg(cpu_state->hvf_fd, HV_X86_RSP);
+ env->regs[R_ESI] = rreg(cpu_state->hvf_fd, HV_X86_RSI);
+ env->regs[R_EDI] = rreg(cpu_state->hvf_fd, HV_X86_RDI);
+ env->regs[8] = rreg(cpu_state->hvf_fd, HV_X86_R8);
+ env->regs[9] = rreg(cpu_state->hvf_fd, HV_X86_R9);
+ env->regs[10] = rreg(cpu_state->hvf_fd, HV_X86_R10);
+ env->regs[11] = rreg(cpu_state->hvf_fd, HV_X86_R11);
+ env->regs[12] = rreg(cpu_state->hvf_fd, HV_X86_R12);
+ env->regs[13] = rreg(cpu_state->hvf_fd, HV_X86_R13);
+ env->regs[14] = rreg(cpu_state->hvf_fd, HV_X86_R14);
+ env->regs[15] = rreg(cpu_state->hvf_fd, HV_X86_R15);
+
+ env->eflags = rreg(cpu_state->hvf_fd, HV_X86_RFLAGS);
+ env->eip = rreg(cpu_state->hvf_fd, HV_X86_RIP);
+
+ hvf_get_xsave(cpu_state);
+ env->xcr0 = rreg(cpu_state->hvf_fd, HV_X86_XCR0);
+
+ hvf_get_segments(cpu_state);
+ hvf_get_msrs(cpu_state);
+
+ env->dr[0] = rreg(cpu_state->hvf_fd, HV_X86_DR0);
+ env->dr[1] = rreg(cpu_state->hvf_fd, HV_X86_DR1);
+ env->dr[2] = rreg(cpu_state->hvf_fd, HV_X86_DR2);
+ env->dr[3] = rreg(cpu_state->hvf_fd, HV_X86_DR3);
+ env->dr[4] = rreg(cpu_state->hvf_fd, HV_X86_DR4);
+ env->dr[5] = rreg(cpu_state->hvf_fd, HV_X86_DR5);
+ env->dr[6] = rreg(cpu_state->hvf_fd, HV_X86_DR6);
+ env->dr[7] = rreg(cpu_state->hvf_fd, HV_X86_DR7);
+
+ return 0;
+}
+
+static void vmx_set_int_window_exiting(CPUState *cpu)
+{
+ uint64_t val;
+ val = rvmcs(cpu->hvf_fd, VMCS_PRI_PROC_BASED_CTLS);
+ wvmcs(cpu->hvf_fd, VMCS_PRI_PROC_BASED_CTLS, val |
+ VMCS_PRI_PROC_BASED_CTLS_INT_WINDOW_EXITING);
+}
+
+void vmx_clear_int_window_exiting(CPUState *cpu)
+{
+ uint64_t val;
+ val = rvmcs(cpu->hvf_fd, VMCS_PRI_PROC_BASED_CTLS);
+ wvmcs(cpu->hvf_fd, VMCS_PRI_PROC_BASED_CTLS, val &
+ ~VMCS_PRI_PROC_BASED_CTLS_INT_WINDOW_EXITING);
+}
+
+#define NMI_VEC 2
+
+bool hvf_inject_interrupts(CPUState *cpu_state)
+{
+ X86CPU *x86cpu = X86_CPU(cpu_state);
+ CPUX86State *env = &x86cpu->env;
+
+ uint8_t vector;
+ uint64_t intr_type;
+ bool have_event = true;
+ if (env->interrupt_injected != -1) {
+ vector = env->interrupt_injected;
+ intr_type = VMCS_INTR_T_SWINTR;
+ } else if (env->exception_injected != -1) {
+ vector = env->exception_injected;
+ if (vector == EXCP03_INT3 || vector == EXCP04_INTO) {
+ intr_type = VMCS_INTR_T_SWEXCEPTION;
+ } else {
+ intr_type = VMCS_INTR_T_HWEXCEPTION;
+ }
+ } else if (env->nmi_injected) {
+ vector = NMI_VEC;
+ intr_type = VMCS_INTR_T_NMI;
+ } else {
+ have_event = false;
+ }
+
+ uint64_t info = 0;
+ if (have_event) {
+ info = vector | intr_type | VMCS_INTR_VALID;
+ uint64_t reason = rvmcs(cpu_state->hvf_fd, VMCS_EXIT_REASON);
+ if (env->nmi_injected && reason != EXIT_REASON_TASK_SWITCH) {
+ vmx_clear_nmi_blocking(cpu_state);
+ }
+
+ if (!(env->hflags2 & HF2_NMI_MASK) || intr_type != VMCS_INTR_T_NMI) {
+ info &= ~(1 << 12); /* clear undefined bit */
+ if (intr_type == VMCS_INTR_T_SWINTR ||
+ intr_type == VMCS_INTR_T_SWEXCEPTION) {
+ wvmcs(cpu_state->hvf_fd, VMCS_ENTRY_INST_LENGTH, env->ins_len);
+ }
+
+ if (env->has_error_code) {
+ wvmcs(cpu_state->hvf_fd, VMCS_ENTRY_EXCEPTION_ERROR,
+ env->error_code);
+ }
+ /*printf("reinject %lx err %d\n", info, err);*/
+ wvmcs(cpu_state->hvf_fd, VMCS_ENTRY_INTR_INFO, info);
+ };
+ }
+
+ if (cpu_state->interrupt_request & CPU_INTERRUPT_NMI) {
+ if (!(env->hflags2 & HF2_NMI_MASK) && !(info & VMCS_INTR_VALID)) {
+ cpu_state->interrupt_request &= ~CPU_INTERRUPT_NMI;
+ info = VMCS_INTR_VALID | VMCS_INTR_T_NMI | NMI_VEC;
+ wvmcs(cpu_state->hvf_fd, VMCS_ENTRY_INTR_INFO, info);
+ } else {
+ vmx_set_nmi_window_exiting(cpu_state);
+ }
+ }
+
+ if (!(env->hflags & HF_INHIBIT_IRQ_MASK) &&
+ (cpu_state->interrupt_request & CPU_INTERRUPT_HARD) &&
+ (EFLAGS(env) & IF_MASK) && !(info & VMCS_INTR_VALID)) {
+ int line = cpu_get_pic_interrupt(&x86cpu->env);
+ cpu_state->interrupt_request &= ~CPU_INTERRUPT_HARD;
+ if (line >= 0) {
+ wvmcs(cpu_state->hvf_fd, VMCS_ENTRY_INTR_INFO, line |
+ VMCS_INTR_VALID | VMCS_INTR_T_HWINTR);
+ }
+ }
+ if (cpu_state->interrupt_request & CPU_INTERRUPT_HARD) {
+ vmx_set_int_window_exiting(cpu_state);
+ }
+ return (cpu_state->interrupt_request
+ & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR));
+}
+
+int hvf_process_events(CPUState *cpu_state)
+{
+ X86CPU *cpu = X86_CPU(cpu_state);
+ CPUX86State *env = &cpu->env;
+
+ EFLAGS(env) = rreg(cpu_state->hvf_fd, HV_X86_RFLAGS);
+
+ if (cpu_state->interrupt_request & CPU_INTERRUPT_INIT) {
+ hvf_cpu_synchronize_state(cpu_state);
+ do_cpu_init(cpu);
+ }
+
+ if (cpu_state->interrupt_request & CPU_INTERRUPT_POLL) {
+ cpu_state->interrupt_request &= ~CPU_INTERRUPT_POLL;
+ apic_poll_irq(cpu->apic_state);
+ }
+ if (((cpu_state->interrupt_request & CPU_INTERRUPT_HARD) &&
+ (EFLAGS(env) & IF_MASK)) ||
+ (cpu_state->interrupt_request & CPU_INTERRUPT_NMI)) {
+ cpu_state->halted = 0;
+ }
+ if (cpu_state->interrupt_request & CPU_INTERRUPT_SIPI) {
+ hvf_cpu_synchronize_state(cpu_state);
+ do_cpu_sipi(cpu);
+ }
+ if (cpu_state->interrupt_request & CPU_INTERRUPT_TPR) {
+ cpu_state->interrupt_request &= ~CPU_INTERRUPT_TPR;
+ hvf_cpu_synchronize_state(cpu_state);
+ apic_handle_tpr_access_report(cpu->apic_state, env->eip,
+ env->tpr_access_type);
+ }
+ return cpu_state->halted;
+}
diff --git a/target/i386/hvf/x86hvf.h b/target/i386/hvf/x86hvf.h
new file mode 100644
index 0000000000..79539f7282
--- /dev/null
+++ b/target/i386/hvf/x86hvf.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 Veertu Inc,
+ * Copyright (C) 2017 Google Inc,
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef X86HVF_H
+#define X86HVF_H
+#include "cpu.h"
+#include "x86_descr.h"
+
+int hvf_process_events(CPUState *);
+int hvf_put_registers(CPUState *);
+int hvf_get_registers(CPUState *);
+bool hvf_inject_interrupts(CPUState *);
+void hvf_set_segment(struct CPUState *cpu, struct vmx_segment *vmx_seg,
+ SegmentCache *qseg, bool is_tr);
+void hvf_get_segment(SegmentCache *qseg, struct vmx_segment *vmx_seg);
+void hvf_put_xsave(CPUState *cpu_state);
+void hvf_put_segments(CPUState *cpu_state);
+void hvf_put_msrs(CPUState *cpu_state);
+void hvf_get_xsave(CPUState *cpu_state);
+void hvf_get_msrs(CPUState *cpu_state);
+void vmx_clear_int_window_exiting(CPUState *cpu);
+void hvf_get_segments(CPUState *cpu_state);
+void vmx_update_tpr(CPUState *cpu);
+void hvf_cpu_synchronize_state(CPUState *cpu_state);
+#endif
diff --git a/target/i386/kvm.c b/target/i386/kvm.c
index 351b64f77c..6f69e2fcfd 100644
--- a/target/i386/kvm.c
+++ b/target/i386/kvm.c
@@ -1038,8 +1038,6 @@ void kvm_arch_reset_vcpu(X86CPU *cpu)
{
CPUX86State *env = &cpu->env;
- env->exception_injected = -1;
- env->interrupt_injected = -1;
env->xcr0 = 1;
if (kvm_irqchip_in_kernel()) {
env->mp_state = cpu_is_bsp(cpu) ? KVM_MP_STATE_RUNNABLE :
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 77f8183117..39a4b5359d 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -80,6 +80,7 @@ gcov-files-test-thread-pool-y = thread-pool.c
gcov-files-test-hbitmap-y = util/hbitmap.c
check-unit-y += tests/test-hbitmap$(EXESUF)
gcov-files-test-hbitmap-y = blockjob.c
+check-unit-y += tests/test-bdrv-drain$(EXESUF)
check-unit-y += tests/test-blockjob$(EXESUF)
check-unit-y += tests/test-blockjob-txn$(EXESUF)
check-unit-y += tests/test-x86-cpuid$(EXESUF)
@@ -595,6 +596,7 @@ tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y)
tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
+tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(test-util-obj-y)
tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y)
tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y)
diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197
index 887eb4f496..5e869fe2b7 100755
--- a/tests/qemu-iotests/197
+++ b/tests/qemu-iotests/197
@@ -60,6 +60,10 @@ echo '=== Copy-on-read ==='
echo
# Prep the images
+# VPC rounds image sizes to a specific geometry, force a specific size.
+if [ "$IMGFMT" = "vpc" ]; then
+ IMGOPTS=$(_optstr_add "$IMGOPTS" "force_size")
+fi
_make_test_img 4G
$QEMU_IO -c "write -P 55 3G 1k" "$TEST_IMG" | _filter_qemu_io
IMGPROTO=file IMGFMT=qcow2 IMGOPTS= TEST_IMG_FILE="$TEST_WRAP" \
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
index d9237799e9..f08248bfd9 100644
--- a/tests/qemu-iotests/common.filter
+++ b/tests/qemu-iotests/common.filter
@@ -134,7 +134,8 @@ _filter_img_create()
-e "s# log_size=[0-9]\\+##g" \
-e "s# refcount_bits=[0-9]\\+##g" \
-e "s# key-secret=[a-zA-Z0-9]\\+##g" \
- -e "s# iter-time=[0-9]\\+##g"
+ -e "s# iter-time=[0-9]\\+##g" \
+ -e "s# force_size=\\(on\\|off\\)##g"
}
_filter_img_info()
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
new file mode 100644
index 0000000000..d760e2b243
--- /dev/null
+++ b/tests/test-bdrv-drain.c
@@ -0,0 +1,651 @@
+/*
+ * Block node draining tests
+ *
+ * Copyright (c) 2017 Kevin Wolf <kwolf@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "block/block.h"
+#include "block/blockjob_int.h"
+#include "sysemu/block-backend.h"
+#include "qapi/error.h"
+
+typedef struct BDRVTestState {
+ int drain_count;
+} BDRVTestState;
+
+static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs)
+{
+ BDRVTestState *s = bs->opaque;
+ s->drain_count++;
+}
+
+static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs)
+{
+ BDRVTestState *s = bs->opaque;
+ s->drain_count--;
+}
+
+static void bdrv_test_close(BlockDriverState *bs)
+{
+ BDRVTestState *s = bs->opaque;
+ g_assert_cmpint(s->drain_count, >, 0);
+}
+
+static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs,
+ uint64_t offset, uint64_t bytes,
+ QEMUIOVector *qiov, int flags)
+{
+ /* We want this request to stay until the polling loop in drain waits for
+ * it to complete. We need to sleep a while as bdrv_drain_invoke() comes
+ * first and polls its result, too, but it shouldn't accidentally complete
+ * this request yet. */
+ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000);
+
+ return 0;
+}
+
+static BlockDriver bdrv_test = {
+ .format_name = "test",
+ .instance_size = sizeof(BDRVTestState),
+
+ .bdrv_close = bdrv_test_close,
+ .bdrv_co_preadv = bdrv_test_co_preadv,
+
+ .bdrv_co_drain_begin = bdrv_test_co_drain_begin,
+ .bdrv_co_drain_end = bdrv_test_co_drain_end,
+
+ .bdrv_child_perm = bdrv_format_default_perms,
+};
+
+static void aio_ret_cb(void *opaque, int ret)
+{
+ int *aio_ret = opaque;
+ *aio_ret = ret;
+}
+
+typedef struct CallInCoroutineData {
+ void (*entry)(void);
+ bool done;
+} CallInCoroutineData;
+
+static coroutine_fn void call_in_coroutine_entry(void *opaque)
+{
+ CallInCoroutineData *data = opaque;
+
+ data->entry();
+ data->done = true;
+}
+
+static void call_in_coroutine(void (*entry)(void))
+{
+ Coroutine *co;
+ CallInCoroutineData data = {
+ .entry = entry,
+ .done = false,
+ };
+
+ co = qemu_coroutine_create(call_in_coroutine_entry, &data);
+ qemu_coroutine_enter(co);
+ while (!data.done) {
+ aio_poll(qemu_get_aio_context(), true);
+ }
+}
+
+enum drain_type {
+ BDRV_DRAIN_ALL,
+ BDRV_DRAIN,
+ BDRV_SUBTREE_DRAIN,
+ DRAIN_TYPE_MAX,
+};
+
+static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
+{
+ switch (drain_type) {
+ case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
+ case BDRV_DRAIN: bdrv_drained_begin(bs); break;
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_begin(bs); break;
+ default: g_assert_not_reached();
+ }
+}
+
+static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
+{
+ switch (drain_type) {
+ case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break;
+ case BDRV_DRAIN: bdrv_drained_end(bs); break;
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_end(bs); break;
+ default: g_assert_not_reached();
+ }
+}
+
+static void test_drv_cb_common(enum drain_type drain_type, bool recursive)
+{
+ BlockBackend *blk;
+ BlockDriverState *bs, *backing;
+ BDRVTestState *s, *backing_s;
+ BlockAIOCB *acb;
+ int aio_ret;
+
+ QEMUIOVector qiov;
+ struct iovec iov = {
+ .iov_base = NULL,
+ .iov_len = 0,
+ };
+ qemu_iovec_init_external(&qiov, &iov, 1);
+
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
+ &error_abort);
+ s = bs->opaque;
+ blk_insert_bs(blk, bs, &error_abort);
+
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
+ backing_s = backing->opaque;
+ bdrv_set_backing_hd(bs, backing, &error_abort);
+
+ /* Simple bdrv_drain_all_begin/end pair, check that CBs are called */
+ g_assert_cmpint(s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+
+ do_drain_begin(drain_type, bs);
+
+ g_assert_cmpint(s->drain_count, ==, 1);
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
+
+ do_drain_end(drain_type, bs);
+
+ g_assert_cmpint(s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+
+ /* Now do the same while a request is pending */
+ aio_ret = -EINPROGRESS;
+ acb = blk_aio_preadv(blk, 0, &qiov, 0, aio_ret_cb, &aio_ret);
+ g_assert(acb != NULL);
+ g_assert_cmpint(aio_ret, ==, -EINPROGRESS);
+
+ g_assert_cmpint(s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+
+ do_drain_begin(drain_type, bs);
+
+ g_assert_cmpint(aio_ret, ==, 0);
+ g_assert_cmpint(s->drain_count, ==, 1);
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
+
+ do_drain_end(drain_type, bs);
+
+ g_assert_cmpint(s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+
+ bdrv_unref(backing);
+ bdrv_unref(bs);
+ blk_unref(blk);
+}
+
+static void test_drv_cb_drain_all(void)
+{
+ test_drv_cb_common(BDRV_DRAIN_ALL, true);
+}
+
+static void test_drv_cb_drain(void)
+{
+ test_drv_cb_common(BDRV_DRAIN, false);
+}
+
+static void test_drv_cb_drain_subtree(void)
+{
+ test_drv_cb_common(BDRV_SUBTREE_DRAIN, true);
+}
+
+static void test_drv_cb_co_drain(void)
+{
+ call_in_coroutine(test_drv_cb_drain);
+}
+
+static void test_drv_cb_co_drain_subtree(void)
+{
+ call_in_coroutine(test_drv_cb_drain_subtree);
+}
+
+static void test_quiesce_common(enum drain_type drain_type, bool recursive)
+{
+ BlockBackend *blk;
+ BlockDriverState *bs, *backing;
+
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
+ &error_abort);
+ blk_insert_bs(blk, bs, &error_abort);
+
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
+ bdrv_set_backing_hd(bs, backing, &error_abort);
+
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
+
+ do_drain_begin(drain_type, bs);
+
+ g_assert_cmpint(bs->quiesce_counter, ==, 1);
+ g_assert_cmpint(backing->quiesce_counter, ==, !!recursive);
+
+ do_drain_end(drain_type, bs);
+
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
+
+ bdrv_unref(backing);
+ bdrv_unref(bs);
+ blk_unref(blk);
+}
+
+static void test_quiesce_drain_all(void)
+{
+ // XXX drain_all doesn't quiesce
+ //test_quiesce_common(BDRV_DRAIN_ALL, true);
+}
+
+static void test_quiesce_drain(void)
+{
+ test_quiesce_common(BDRV_DRAIN, false);
+}
+
+static void test_quiesce_drain_subtree(void)
+{
+ test_quiesce_common(BDRV_SUBTREE_DRAIN, true);
+}
+
+static void test_quiesce_co_drain(void)
+{
+ call_in_coroutine(test_quiesce_drain);
+}
+
+static void test_quiesce_co_drain_subtree(void)
+{
+ call_in_coroutine(test_quiesce_drain_subtree);
+}
+
+static void test_nested(void)
+{
+ BlockBackend *blk;
+ BlockDriverState *bs, *backing;
+ BDRVTestState *s, *backing_s;
+ enum drain_type outer, inner;
+
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
+ &error_abort);
+ s = bs->opaque;
+ blk_insert_bs(blk, bs, &error_abort);
+
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
+ backing_s = backing->opaque;
+ bdrv_set_backing_hd(bs, backing, &error_abort);
+
+ for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) {
+ for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) {
+ /* XXX bdrv_drain_all() doesn't increase the quiesce_counter */
+ int bs_quiesce = (outer != BDRV_DRAIN_ALL) +
+ (inner != BDRV_DRAIN_ALL);
+ int backing_quiesce = (outer == BDRV_SUBTREE_DRAIN) +
+ (inner == BDRV_SUBTREE_DRAIN);
+ int backing_cb_cnt = (outer != BDRV_DRAIN) +
+ (inner != BDRV_DRAIN);
+
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
+ g_assert_cmpint(s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+
+ do_drain_begin(outer, bs);
+ do_drain_begin(inner, bs);
+
+ g_assert_cmpint(bs->quiesce_counter, ==, bs_quiesce);
+ g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce);
+ g_assert_cmpint(s->drain_count, ==, 2);
+ g_assert_cmpint(backing_s->drain_count, ==, backing_cb_cnt);
+
+ do_drain_end(inner, bs);
+ do_drain_end(outer, bs);
+
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
+ g_assert_cmpint(s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+ }
+ }
+
+ bdrv_unref(backing);
+ bdrv_unref(bs);
+ blk_unref(blk);
+}
+
+static void test_multiparent(void)
+{
+ BlockBackend *blk_a, *blk_b;
+ BlockDriverState *bs_a, *bs_b, *backing;
+ BDRVTestState *a_s, *b_s, *backing_s;
+
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
+ &error_abort);
+ a_s = bs_a->opaque;
+ blk_insert_bs(blk_a, bs_a, &error_abort);
+
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
+ &error_abort);
+ b_s = bs_b->opaque;
+ blk_insert_bs(blk_b, bs_b, &error_abort);
+
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
+ backing_s = backing->opaque;
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
+
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
+ g_assert_cmpint(a_s->drain_count, ==, 0);
+ g_assert_cmpint(b_s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
+
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
+ g_assert_cmpint(backing->quiesce_counter, ==, 1);
+ g_assert_cmpint(a_s->drain_count, ==, 1);
+ g_assert_cmpint(b_s->drain_count, ==, 1);
+ g_assert_cmpint(backing_s->drain_count, ==, 1);
+
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
+
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 2);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
+ g_assert_cmpint(backing->quiesce_counter, ==, 2);
+ g_assert_cmpint(a_s->drain_count, ==, 2);
+ g_assert_cmpint(b_s->drain_count, ==, 2);
+ g_assert_cmpint(backing_s->drain_count, ==, 2);
+
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
+
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
+ g_assert_cmpint(backing->quiesce_counter, ==, 1);
+ g_assert_cmpint(a_s->drain_count, ==, 1);
+ g_assert_cmpint(b_s->drain_count, ==, 1);
+ g_assert_cmpint(backing_s->drain_count, ==, 1);
+
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
+
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
+ g_assert_cmpint(a_s->drain_count, ==, 0);
+ g_assert_cmpint(b_s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+
+ bdrv_unref(backing);
+ bdrv_unref(bs_a);
+ bdrv_unref(bs_b);
+ blk_unref(blk_a);
+ blk_unref(blk_b);
+}
+
+static void test_graph_change(void)
+{
+ BlockBackend *blk_a, *blk_b;
+ BlockDriverState *bs_a, *bs_b, *backing;
+ BDRVTestState *a_s, *b_s, *backing_s;
+
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
+ &error_abort);
+ a_s = bs_a->opaque;
+ blk_insert_bs(blk_a, bs_a, &error_abort);
+
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
+ &error_abort);
+ b_s = bs_b->opaque;
+ blk_insert_bs(blk_b, bs_b, &error_abort);
+
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
+ backing_s = backing->opaque;
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
+
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
+ g_assert_cmpint(a_s->drain_count, ==, 0);
+ g_assert_cmpint(b_s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
+
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
+ g_assert_cmpint(backing->quiesce_counter, ==, 5);
+ g_assert_cmpint(a_s->drain_count, ==, 5);
+ g_assert_cmpint(b_s->drain_count, ==, 5);
+ g_assert_cmpint(backing_s->drain_count, ==, 5);
+
+ bdrv_set_backing_hd(bs_b, NULL, &error_abort);
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 3);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
+ g_assert_cmpint(backing->quiesce_counter, ==, 3);
+ g_assert_cmpint(a_s->drain_count, ==, 3);
+ g_assert_cmpint(b_s->drain_count, ==, 2);
+ g_assert_cmpint(backing_s->drain_count, ==, 3);
+
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
+ g_assert_cmpint(backing->quiesce_counter, ==, 5);
+ g_assert_cmpint(a_s->drain_count, ==, 5);
+ g_assert_cmpint(b_s->drain_count, ==, 5);
+ g_assert_cmpint(backing_s->drain_count, ==, 5);
+
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
+
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
+ g_assert_cmpint(a_s->drain_count, ==, 0);
+ g_assert_cmpint(b_s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+
+ bdrv_unref(backing);
+ bdrv_unref(bs_a);
+ bdrv_unref(bs_b);
+ blk_unref(blk_a);
+ blk_unref(blk_b);
+}
+
+
+typedef struct TestBlockJob {
+ BlockJob common;
+ bool should_complete;
+} TestBlockJob;
+
+static void test_job_completed(BlockJob *job, void *opaque)
+{
+ block_job_completed(job, 0);
+}
+
+static void coroutine_fn test_job_start(void *opaque)
+{
+ TestBlockJob *s = opaque;
+
+ while (!s->should_complete) {
+ block_job_sleep_ns(&s->common, 100000);
+ }
+
+ block_job_defer_to_main_loop(&s->common, test_job_completed, NULL);
+}
+
+static void test_job_complete(BlockJob *job, Error **errp)
+{
+ TestBlockJob *s = container_of(job, TestBlockJob, common);
+ s->should_complete = true;
+}
+
+BlockJobDriver test_job_driver = {
+ .instance_size = sizeof(TestBlockJob),
+ .start = test_job_start,
+ .complete = test_job_complete,
+};
+
+static void test_blockjob_common(enum drain_type drain_type)
+{
+ BlockBackend *blk_src, *blk_target;
+ BlockDriverState *src, *target;
+ BlockJob *job;
+ int ret;
+
+ src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR,
+ &error_abort);
+ blk_src = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+ blk_insert_bs(blk_src, src, &error_abort);
+
+ target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR,
+ &error_abort);
+ blk_target = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+ blk_insert_bs(blk_target, target, &error_abort);
+
+ job = block_job_create("job0", &test_job_driver, src, 0, BLK_PERM_ALL, 0,
+ 0, NULL, NULL, &error_abort);
+ block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort);
+ block_job_start(job);
+
+ g_assert_cmpint(job->pause_count, ==, 0);
+ g_assert_false(job->paused);
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
+
+ do_drain_begin(drain_type, src);
+
+ if (drain_type == BDRV_DRAIN_ALL) {
+ /* bdrv_drain_all() drains both src and target */
+ g_assert_cmpint(job->pause_count, ==, 2);
+ } else {
+ g_assert_cmpint(job->pause_count, ==, 1);
+ }
+ /* XXX We don't wait until the job is actually paused. Is this okay? */
+ /* g_assert_true(job->paused); */
+ g_assert_false(job->busy); /* The job is paused */
+
+ do_drain_end(drain_type, src);
+
+ g_assert_cmpint(job->pause_count, ==, 0);
+ g_assert_false(job->paused);
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
+
+ do_drain_begin(drain_type, target);
+
+ if (drain_type == BDRV_DRAIN_ALL) {
+ /* bdrv_drain_all() drains both src and target */
+ g_assert_cmpint(job->pause_count, ==, 2);
+ } else {
+ g_assert_cmpint(job->pause_count, ==, 1);
+ }
+ /* XXX We don't wait until the job is actually paused. Is this okay? */
+ /* g_assert_true(job->paused); */
+ g_assert_false(job->busy); /* The job is paused */
+
+ do_drain_end(drain_type, target);
+
+ g_assert_cmpint(job->pause_count, ==, 0);
+ g_assert_false(job->paused);
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
+
+ ret = block_job_complete_sync(job, &error_abort);
+ g_assert_cmpint(ret, ==, 0);
+
+ blk_unref(blk_src);
+ blk_unref(blk_target);
+ bdrv_unref(src);
+ bdrv_unref(target);
+}
+
+static void test_blockjob_drain_all(void)
+{
+ test_blockjob_common(BDRV_DRAIN_ALL);
+}
+
+static void test_blockjob_drain(void)
+{
+ test_blockjob_common(BDRV_DRAIN);
+}
+
+static void test_blockjob_drain_subtree(void)
+{
+ test_blockjob_common(BDRV_SUBTREE_DRAIN);
+}
+
+int main(int argc, char **argv)
+{
+ bdrv_init();
+ qemu_init_main_loop(&error_abort);
+
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
+ g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
+ g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
+ test_drv_cb_drain_subtree);
+
+ // XXX bdrv_drain_all() doesn't work in coroutine context
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain", test_drv_cb_co_drain);
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain_subtree",
+ test_drv_cb_co_drain_subtree);
+
+
+ g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
+ g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
+ g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
+ test_quiesce_drain_subtree);
+
+ // XXX bdrv_drain_all() doesn't work in coroutine context
+ g_test_add_func("/bdrv-drain/quiesce/co/drain", test_quiesce_co_drain);
+ g_test_add_func("/bdrv-drain/quiesce/co/drain_subtree",
+ test_quiesce_co_drain_subtree);
+
+ g_test_add_func("/bdrv-drain/nested", test_nested);
+ g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
+ g_test_add_func("/bdrv-drain/graph-change", test_graph_change);
+
+ g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
+ g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
+ g_test_add_func("/bdrv-drain/blockjob/drain_subtree",
+ test_blockjob_drain_subtree);
+
+ return g_test_run();
+}
diff --git a/vl.c b/vl.c
index d3a5c5d021..444b7507da 100644
--- a/vl.c
+++ b/vl.c
@@ -3052,9 +3052,8 @@ int main(int argc, char **argv, char **envp)
const char *boot_order = NULL;
const char *boot_once = NULL;
DisplayState *ds;
- int cyls, heads, secs, translation;
QemuOpts *opts, *machine_opts;
- QemuOpts *hda_opts = NULL, *icount_opts = NULL, *accel_opts = NULL;
+ QemuOpts *icount_opts = NULL, *accel_opts = NULL;
QemuOptsList *olist;
int optind;
const char *optarg;
@@ -3146,8 +3145,6 @@ int main(int argc, char **argv, char **envp)
cpu_model = NULL;
snapshot = 0;
- cyls = heads = secs = 0;
- translation = BIOS_ATA_TRANSLATION_AUTO;
nb_nics = 0;
@@ -3186,7 +3183,7 @@ int main(int argc, char **argv, char **envp)
if (optind >= argc)
break;
if (argv[optind][0] != '-') {
- hda_opts = drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
+ drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
} else {
const QEMUOption *popt;
@@ -3206,21 +3203,6 @@ int main(int argc, char **argv, char **envp)
cpu_model = optarg;
break;
case QEMU_OPTION_hda:
- {
- char buf[256];
- if (cyls == 0)
- snprintf(buf, sizeof(buf), "%s", HD_OPTS);
- else
- snprintf(buf, sizeof(buf),
- "%s,cyls=%d,heads=%d,secs=%d%s",
- HD_OPTS , cyls, heads, secs,
- translation == BIOS_ATA_TRANSLATION_LBA ?
- ",trans=lba" :
- translation == BIOS_ATA_TRANSLATION_NONE ?
- ",trans=none" : "");
- drive_add(IF_DEFAULT, 0, optarg, buf);
- break;
- }
case QEMU_OPTION_hdb:
case QEMU_OPTION_hdc:
case QEMU_OPTION_hdd:
@@ -3271,70 +3253,6 @@ int main(int argc, char **argv, char **envp)
case QEMU_OPTION_snapshot:
snapshot = 1;
break;
- case QEMU_OPTION_hdachs:
- {
- const char *p;
- p = optarg;
- cyls = strtol(p, (char **)&p, 0);
- if (cyls < 1 || cyls > 16383)
- goto chs_fail;
- if (*p != ',')
- goto chs_fail;
- p++;
- heads = strtol(p, (char **)&p, 0);
- if (heads < 1 || heads > 16)
- goto chs_fail;
- if (*p != ',')
- goto chs_fail;
- p++;
- secs = strtol(p, (char **)&p, 0);
- if (secs < 1 || secs > 63)
- goto chs_fail;
- if (*p == ',') {
- p++;
- if (!strcmp(p, "large")) {
- translation = BIOS_ATA_TRANSLATION_LARGE;
- } else if (!strcmp(p, "rechs")) {
- translation = BIOS_ATA_TRANSLATION_RECHS;
- } else if (!strcmp(p, "none")) {
- translation = BIOS_ATA_TRANSLATION_NONE;
- } else if (!strcmp(p, "lba")) {
- translation = BIOS_ATA_TRANSLATION_LBA;
- } else if (!strcmp(p, "auto")) {
- translation = BIOS_ATA_TRANSLATION_AUTO;
- } else {
- goto chs_fail;
- }
- } else if (*p != '\0') {
- chs_fail:
- error_report("invalid physical CHS format");
- exit(1);
- }
- if (hda_opts != NULL) {
- qemu_opt_set_number(hda_opts, "cyls", cyls,
- &error_abort);
- qemu_opt_set_number(hda_opts, "heads", heads,
- &error_abort);
- qemu_opt_set_number(hda_opts, "secs", secs,
- &error_abort);
- if (translation == BIOS_ATA_TRANSLATION_LARGE) {
- qemu_opt_set(hda_opts, "trans", "large",
- &error_abort);
- } else if (translation == BIOS_ATA_TRANSLATION_RECHS) {
- qemu_opt_set(hda_opts, "trans", "rechs",
- &error_abort);
- } else if (translation == BIOS_ATA_TRANSLATION_LBA) {
- qemu_opt_set(hda_opts, "trans", "lba",
- &error_abort);
- } else if (translation == BIOS_ATA_TRANSLATION_NONE) {
- qemu_opt_set(hda_opts, "trans", "none",
- &error_abort);
- }
- }
- }
- error_report("'-hdachs' is deprecated, please use '-device"
- " ide-hd,cyls=c,heads=h,secs=s,...' instead");
- break;
case QEMU_OPTION_numa:
opts = qemu_opts_parse_noisily(qemu_find_opts("numa"),
optarg, true);