KVM is surprisingly simple (until you get to drivers, of course)
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#include <linux/kvm.h>
#include <termios.h>
#define MEM_AMT 1024 * 64 /* 64KiB */
#define ERR(x) do { perror(x); exit(-1); } while (0)
int main(int argc, char *argv[])
{
if (argc < 2) return printf("usage: %s <rom>\n", argv[0]);
struct termios termios;
if (tcgetattr(0, &termios) < 0) ERR("tcgetattr");
struct termios new_termios = termios;
new_termios.c_lflag &= (~ICANON & ~ECHO);
if (tcsetattr(0, 0, &new_termios) < 0) ERR("tcsetattr");
int kvm = open("/dev/kvm", O_RDWR);
if (!kvm) ERR("open kvm");
int vm = ioctl(kvm, KVM_CREATE_VM, 0);
if (!vm) ERR("create vm");
struct kvm_userspace_memory_region mem =
{
.slot = 0,
.flags = 0,
.memory_size = MEM_AMT,
.userspace_addr = (uint64_t)mmap(NULL, MEM_AMT, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0),
.guest_phys_addr = 0
};
if (!mem.userspace_addr) ERR("malloc");
if (ioctl(vm, KVM_SET_USER_MEMORY_REGION, &mem)) ERR("set memory region");
FILE *f = fopen(argv[1], "rb");
if (!f) ERR("fopen rom");
uint8_t *w = (uint8_t *)mem.userspace_addr;
size_t read;
while ((read = fread(w, 1, 8192, f)) > 0)
{
if (read < 0) ERR("fread rom");
w += read;
}
fclose(f);
int vcpu = ioctl(vm, KVM_CREATE_VCPU, 0);
if (!vcpu) ERR("create vcpu");
struct kvm_sregs sregs;
if (ioctl(vcpu, KVM_GET_SREGS, &sregs)) ERR("get sregs");
sregs.cs.selector = sregs.cs.base = sregs.ds.selector = sregs.ds.base = sregs.es.selector = sregs.es.base = sregs.fs.selector = sregs.fs.base = sregs.gs.selector = sregs.gs.base = sregs.ss.selector = sregs.ss.base = sregs.fs.selector = sregs.fs.base = 0;
if (ioctl(vcpu, KVM_SET_SREGS, &sregs)) ERR("set sregs");
struct kvm_regs regs;
if (ioctl(vcpu, KVM_GET_REGS, ®s)) ERR("get regs");
regs.rip = 0;
regs.rflags = 2;
if (ioctl(vcpu, KVM_SET_REGS, ®s)) ERR("set regs");
struct kvm_run *run = mmap(NULL, ioctl(kvm, KVM_GET_VCPU_MMAP_SIZE, 0), PROT_READ | PROT_WRITE, MAP_SHARED, vcpu, 0);
if (!run) ERR("kvm run map");
char c;
int ret = 0;
for (;;)
{
if (ioctl(vcpu, KVM_RUN, 0)) ERR("run vcpu 0");
switch (run->exit_reason)
{
case KVM_EXIT_SHUTDOWN: goto end;
case KVM_EXIT_IO:
{
uint8_t *data = (uint8_t *)((uint64_t)run + run->io.data_offset);
if (run->io.direction == KVM_EXIT_IO_OUT) putchar(*data);
else *data = c;
}
break;
case KVM_EXIT_HLT: c = getchar(); if (c == '\n') c = '\r'; break;
default: printf("unknown exit reason %d\n", run->exit_reason); ret = -1; goto end;
}
}
end:
tcsetattr(0, 0, &termios);
return ret;
}
and a port of BootBASIC that causes this post editor to crash :(
TOODLES.