#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <libgen.h>
#include <assert.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/debug.h>
#include <sys/vmm.h>
#include <sys/vmm_dev.h>
#include <vmmapi.h>
#include "in_guest.h"
enum test_state {
SEEKING_IN,
SEEKING_OUT,
SEEKING_END,
DONE,
};
bool
advance_test_state(const struct vm_exit *vexit, struct vm_entry *ventry,
struct vcpu *vcpu, enum vm_exit_kind kind, enum test_state *state,
const bool have_decodeassist)
{
if (*state != DONE && kind == VEK_REENTR) {
return (true);
}
switch (*state) {
case SEEKING_IN:
if (kind != VEK_UNHANDLED) {
break;
}
if (have_decodeassist) {
if (vexit->exitcode != VM_EXITCODE_INOUT) {
break;
}
const bool match_inb =
vexit_match_inout(vexit, true, 0x55aa, 1, NULL);
if (!match_inb) {
break;
}
ventry_fulfill_inout(vexit, ventry, 0xee);
} else {
if (vexit->exitcode != VM_EXITCODE_INST_EMUL) {
break;
}
const bool match_inb =
vexit->u.inst_emul.num_valid >= 2 &&
vexit->u.inst_emul.inst[0] == 0xf3 &&
vexit->u.inst_emul.inst[1] == 0x6c;
if (!match_inb) {
break;
}
int err = vm_set_register(vcpu, 0, vexit->rip + 2);
if (err != 0) {
test_fail_errno(err, "failed to set %rip");
}
}
*state = SEEKING_OUT;
return (true);
case SEEKING_OUT:
if (kind != VEK_UNHANDLED) {
break;
}
if (have_decodeassist) {
if (vexit->exitcode != VM_EXITCODE_INOUT) {
break;
}
const bool match_outb =
vexit_match_inout(vexit, false, 0x55aa, 1, NULL);
if (!match_outb) {
break;
}
ventry_fulfill_inout(vexit, ventry, 0);
} else {
if (vexit->exitcode != VM_EXITCODE_INST_EMUL) {
break;
}
const bool match_outb =
vexit->u.inst_emul.num_valid >= 2 &&
vexit->u.inst_emul.inst[0] == 0xf3 &&
vexit->u.inst_emul.inst[1] == 0x6e;
if (!match_outb) {
break;
}
int err = vm_set_register(vcpu, 0, vexit->rip + 2);
if (err != 0) {
test_fail_errno(err, "failed to set %rip");
}
}
*state = SEEKING_END;
return (true);
case SEEKING_END:
if (kind == VEK_TEST_PASS) {
*state = DONE;
return (true);
}
break;
case DONE:
break;
}
return (false);
}
int
main(int argc, char *argv[])
{
const char *test_suite_name = basename(argv[0]);
struct vmctx *ctx = NULL;
struct vcpu *vcpu;
int err;
ctx = test_initialize(test_suite_name);
uint_t regs[4];
do_cpuid(0x8000000a, regs);
const bool have_decodeassist = (regs[3] & (1 << 7)) != 0;
if ((vcpu = vm_vcpu_open(ctx, 0)) == NULL) {
test_fail_errno(errno, "Could not open vcpu0");
}
err = test_setup_vcpu(vcpu, MEM_LOC_PAYLOAD, MEM_LOC_STACK);
if (err != 0) {
test_fail_errno(err, "Could not initialize vcpu0");
}
struct vm_entry ventry = { 0 };
struct vm_exit vexit = { 0 };
enum test_state state = SEEKING_IN;
do {
const enum vm_exit_kind kind =
test_run_vcpu(vcpu, &ventry, &vexit);
const bool exit_ok = advance_test_state(&vexit, &ventry, vcpu,
kind, &state, have_decodeassist);
if (!exit_ok) {
test_fail_vmexit(&vexit);
break;
}
} while (state != DONE);
test_pass();
}