#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <libgen.h>
#include <sys/stat.h>
#include <errno.h>
#include <err.h>
#include <assert.h>
#include <sys/sysmacros.h>
#include <stdbool.h>
#include <sys/vmm.h>
#include <sys/vmm_dev.h>
#include <sys/vmm_data.h>
#include <vmmapi.h>
#include "common.h"
static void
should_eq_u64(const char *field_name, uint64_t a, uint64_t b)
{
if (a != b) {
errx(EXIT_FAILURE, "unexpected %s %" PRIu64 " != %" PRIu64,
field_name, a, b);
}
}
static void
check_inval_field(int vmfd, uint32_t ident, uint64_t val)
{
struct vdi_field_entry_v1 field = {
.vfe_ident = ident,
.vfe_value = val,
};
struct vm_data_xfer vdx = {
.vdx_class = VDC_VMM_ARCH,
.vdx_version = 1,
.vdx_len = sizeof (field),
.vdx_data = &field,
};
if (ioctl(vmfd, VM_DATA_WRITE, &vdx) == 0) {
err(EXIT_FAILURE, "vmm_data_write should have failed");
}
int err = errno;
if (err != EINVAL) {
errx(EXIT_FAILURE, "expected EINVAL errno, got %d", err);
}
}
static void
do_data_write(int vmfd, struct vm_data_xfer *vdx)
{
if (ioctl(vmfd, VM_DATA_WRITE, vdx) != 0) {
err(EXIT_FAILURE, "valid vmm_data_write failed");
}
if (vdx->vdx_result_len != vdx->vdx_len) {
errx(EXIT_FAILURE, "unexpected vdx_result_len %u != %u",
vdx->vdx_len, vdx->vdx_result_len);
}
}
static void
do_data_read(int vmfd, struct vm_data_xfer *vdx)
{
if (ioctl(vmfd, VM_DATA_READ, vdx) != 0) {
err(EXIT_FAILURE, "valid vmm_data_read failed");
}
if (vdx->vdx_result_len != vdx->vdx_len) {
errx(EXIT_FAILURE, "unexpected vdx_result_len %u != %u",
vdx->vdx_len, vdx->vdx_result_len);
}
}
int
main(int argc, char *argv[])
{
const char *suite_name = basename(argv[0]);
struct vmctx *ctx;
struct vcpu *vcpu;
ctx = create_test_vm(suite_name);
if (ctx == NULL) {
errx(EXIT_FAILURE, "could not open test VM");
}
if ((vcpu = vm_vcpu_open(ctx, 0)) == NULL) {
err(EXIT_FAILURE, "Could not open vcpu0");
}
if (vm_activate_cpu(vcpu) != 0) {
err(EXIT_FAILURE, "could not activate vcpu0");
}
const int vmfd = vm_get_device_fd(ctx);
if (ioctl(vmfd, VM_PAUSE, 0) != 0) {
err(EXIT_FAILURE, "VM_PAUSE failed");
}
struct vdi_field_entry_v1 fields[4] = {
{ .vfe_ident = VAI_PEND_NMI },
{ .vfe_ident = VAI_PEND_EXTINT },
{ .vfe_ident = VAI_PEND_EXCP },
{ .vfe_ident = VAI_PEND_INTINFO },
};
struct vm_data_xfer vdx = {
.vdx_class = VDC_VMM_ARCH,
.vdx_version = 1,
.vdx_flags = VDX_FLAG_READ_COPYIN,
.vdx_len = sizeof (fields),
.vdx_data = &fields,
};
do_data_read(vmfd, &vdx);
should_eq_u64("VAI_PEND_NMI", fields[0].vfe_value, 0);
should_eq_u64("VAI_PEND_EXTINT", fields[1].vfe_value, 0);
should_eq_u64("VAI_PEND_EXCP", fields[2].vfe_value, 0);
should_eq_u64("VAI_PEND_INTINFO", fields[3].vfe_value, 0);
fields[0].vfe_value = 1;
fields[1].vfe_value = 1;
fields[2].vfe_value = VM_INTINFO_VALID | VM_INTINFO_HWEXCP | IDT_GP;
fields[3].vfe_value = VM_INTINFO_VALID | VM_INTINFO_SWINTR | 0x80;
do_data_write(vmfd, &vdx);
fields[0].vfe_ident = VAI_PEND_INTINFO;
fields[1].vfe_ident = VAI_PEND_EXCP;
fields[2].vfe_ident = VAI_PEND_EXTINT;
fields[3].vfe_ident = VAI_PEND_NMI;
do_data_read(vmfd, &vdx);
should_eq_u64("VAI_PEND_INTINFO", fields[0].vfe_value,
VM_INTINFO_VALID | VM_INTINFO_SWINTR | 0x80);
should_eq_u64("VAI_PEND_EXCP", fields[1].vfe_value,
VM_INTINFO_VALID | VM_INTINFO_HWEXCP | IDT_GP);
should_eq_u64("VAI_PEND_EXTINT", fields[2].vfe_value, 1);
should_eq_u64("VAI_PEND_NMI", fields[3].vfe_value, 1);
check_inval_field(vmfd, VAI_PEND_INTINFO,
VM_INTINFO_VALID | VM_INTINFO_NMI | 0xd);
check_inval_field(vmfd, VAI_PEND_INTINFO,
VM_INTINFO_VALID | VM_INTINFO_HWEXCP | 0x40);
check_inval_field(vmfd, VAI_PEND_EXCP,
VM_INTINFO_VALID | VM_INTINFO_SWINTR | 0xd);
fields[0].vfe_value = 0;
fields[1].vfe_value = 0;
fields[2].vfe_value = 0;
fields[3].vfe_value = 0;
do_data_write(vmfd, &vdx);
do_data_read(vmfd, &vdx);
should_eq_u64("VAI_PEND_INTINFO", fields[0].vfe_value, 0);
should_eq_u64("VAI_PEND_EXCP", fields[1].vfe_value, 0);
should_eq_u64("VAI_PEND_EXTINT", fields[2].vfe_value, 0);
should_eq_u64("VAI_PEND_NMI", fields[3].vfe_value, 0);
vm_vcpu_close(vcpu);
vm_destroy(ctx);
(void) printf("%s\tPASS\n", suite_name);
return (EXIT_SUCCESS);
}