#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <dev/virtual/nvmm/nvmm.h>
#include <dev/virtual/nvmm/nvmm_ioctl.h>
int main(void) {
    struct nvmm_ioc_capability c;
    struct nvmm_ioc_machine_create mc;
    struct nvmm_ioc_hva_map hm;
    struct nvmm_ioc_gpa_map gm;
    struct nvmm_ioc_vcpu_create vc;
    struct nvmm_ioc_vcpu_setstate ss;
    struct nvmm_comm_page *comm;
    int fd = open("/dev/nvmm", O_RDONLY|O_CLOEXEC);
    if (fd < 0) { perror("open"); return 1; }
    memset(&c, 0, sizeof(c));
    if (ioctl(fd, NVMM_IOC_CAPABILITY, &c)) { perror("cap"); return 1; }
    printf("cap ok v%u\n", c.cap.version);
    memset(&mc, 0, sizeof(mc));
    if (ioctl(fd, NVMM_IOC_MACHINE_CREATE, &mc)) { perror("machcreate"); return 1; }
    memset(&hm, 0, sizeof(hm));
    hm.machid = mc.machid; hm.hva = 0x10000000UL; hm.size = 0x200000;
    if (ioctl(fd, NVMM_IOC_HVA_MAP, &hm)) { perror("hvamap"); return 1; }
    memset(&gm, 0, sizeof(gm));
    gm.machid = mc.machid; gm.hva = 0x10000000UL; gm.gpa = 0; gm.size = 0x200000;
    gm.prot = PROT_READ|PROT_WRITE|PROT_EXEC;
    if (ioctl(fd, NVMM_IOC_GPA_MAP, &gm)) { perror("gpamap"); return 1; }
    memset(&vc, 0, sizeof(vc));
    vc.machid = mc.machid; vc.cpuid = 0; vc.comm = (void *)0x30000000UL;
    if (ioctl(fd, NVMM_IOC_VCPU_CREATE, &vc)) { perror("vcpucreate"); return 1; }
    comm = (struct nvmm_comm_page *)vc.comm;
    printf("vcpu ok comm=%p\n", comm);
    memcpy((void *)0x10000000UL + 0x1000, (uint8_t[]){0xEB,0xFE}, 2);
    struct nvmm_x64_state *st = &comm->state;
    memset(st, 0, sizeof(*st));
    for (int i = 0; i < NVMM_X64_NSEG; i++) {
        st->segs[i].limit = 0xFFFF; st->segs[i].attrib.type = 3; st->segs[i].attrib.p = 1;
    }
    st->segs[NVMM_X64_SEG_GDT].attrib.type = 2;
    st->segs[NVMM_X64_SEG_IDT].attrib.type = 2;
    st->segs[NVMM_X64_SEG_TR].attrib.type = 3; st->segs[NVMM_X64_SEG_TR].attrib.s = 0;
    st->segs[NVMM_X64_SEG_LDT].attrib.s = 0;
    st->gprs[NVMM_X64_GPR_RFLAGS] = 2;
    st->gprs[NVMM_X64_GPR_RIP] = 0x1000;
    st->gprs[NVMM_X64_GPR_RSP] = 0x7000;
    st->crs[NVMM_X64_CR_CR0] = 0x10;
    st->crs[NVMM_X64_CR_XCR0] = 1;
    st->drs[NVMM_X64_DR_DR6] = 0xFFFF0FF0;
    st->drs[NVMM_X64_DR_DR7] = 0x400;
    comm->state_wanted = NVMM_X64_STATE_ALL;
    memset(&ss, 0, sizeof(ss));
    ss.machid = mc.machid; ss.cpuid = 0;
    if (ioctl(fd, NVMM_IOC_VCPU_SETSTATE, &ss)) { perror("setstate"); return 1; }
    printf("setstate ok\n");
    return 0;
}
