Skip to content

Instantly share code, notes, and snippets.

@moriyoshi
Created March 9, 2010 06:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save moriyoshi/326285 to your computer and use it in GitHub Desktop.
Save moriyoshi/326285 to your computer and use it in GitHub Desktop.
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/kvm.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
struct memrgn {
size_t size;
void *addr;
};
struct vm_ctx {
int fd;
struct memrgn phys_mem;
};
struct vcpu_ctx {
int fd;
int id;
struct kvm_run *stat;
size_t stat_sz;
};
static int kvm_fd;
static int create_vm(struct vm_ctx *ctx, size_t phys_mem_size)
{
ctx->phys_mem.addr = (void*)-1;
/* Create a new virtual machine */
ctx->fd = ioctl(kvm_fd, KVM_CREATE_VM, (void*)0);
if (ctx->fd < 0)
goto fail;
/* Set TSS address right after the physical memory region */
if (ioctl(ctx->fd, KVM_SET_TSS_ADDR, phys_mem_size) < 0)
goto fail;
/* Allocate a memory region for the VM's physical memory */
{
void* addr = mmap(0, phys_mem_size, PROT_EXEC | PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (addr == (void*)-1)
goto fail;
ctx->phys_mem.size = phys_mem_size;
ctx->phys_mem.addr = addr;
}
/* Actually assign the allocated region to VM */
{
struct kvm_userspace_memory_region mem = {
.slot = 1,
.guest_phys_addr = 0x0000000000000000L,
.memory_size = ctx->phys_mem.size,
.userspace_addr = (unsigned long)ctx->phys_mem.addr,
.flags = 0
};
if (ioctl(ctx->fd, KVM_SET_USER_MEMORY_REGION, &mem) < 0)
goto fail;
}
return 0;
fail:
if (ctx->fd)
close(ctx->fd);
if (ctx->phys_mem.addr != (void*)-1)
munmap(ctx->phys_mem.addr, ctx->phys_mem.size);
return errno;
}
static void destroy_vm(struct vm_ctx* ctx)
{
if (ctx->fd)
close(ctx->fd);
if (ctx->phys_mem.addr != (void*)-1)
munmap(ctx->phys_mem.addr, ctx->phys_mem.size);
}
static int create_vcpu(struct vcpu_ctx *ctx, struct vm_ctx *vm_ctx, int slot_id)
{
int retval = 0;
ctx->id = slot_id;
ctx->stat = (struct kvm_run*)-1;
ctx->fd = ioctl(vm_ctx->fd, KVM_CREATE_VCPU, slot_id);
if (ctx->fd < 0) {
retval = 1;
ctx->fd = -1;
goto fail;
}
{
void *addr;
/* Retrieve the size of per-VCPU state structure */
int mmap_size = ioctl(kvm_fd, KVM_GET_VCPU_MMAP_SIZE, (void*)0);
if (mmap_size < 0)
goto fail;
ctx->stat_sz = (size_t)mmap_size;
/* mmap the per-VCPU state structure onto our process space */
addr = mmap(0, ctx->stat_sz, PROT_READ | PROT_WRITE, MAP_SHARED,
ctx->fd, 0);
if (addr == (void*)-1)
goto fail;
ctx->stat = addr;
}
return 0;
fail:
if (ctx->fd != -1)
close(ctx->fd);
if (ctx->stat != (void*)-1)
munmap(ctx->stat, ctx->stat_sz);
return errno;
}
static void destroy_vcpu(struct vcpu_ctx *ctx)
{
if (ctx->fd != -1)
close(ctx->fd);
if (ctx->stat != (void*)-1)
munmap(ctx->stat, ctx->stat_sz);
}
static int vcpu_get_regs(struct vcpu_ctx *ctx, struct kvm_regs *regs, struct kvm_sregs *sregs)
{
if (ioctl(ctx->fd, KVM_GET_REGS, regs) < 0)
return errno;
if (ioctl(ctx->fd, KVM_GET_SREGS, sregs) < 0)
return errno;
return 0;
}
static int vcpu_set_regs(struct vcpu_ctx *ctx, const struct kvm_regs *regs, const struct kvm_sregs *sregs)
{
if (ioctl(ctx->fd, KVM_SET_REGS, regs) < 0)
return errno;
if (ioctl(ctx->fd, KVM_SET_SREGS, sregs) < 0)
return errno;
return 0;
}
static int vcpu_run(struct vcpu_ctx *ctx)
{
return ioctl(ctx->fd, KVM_RUN, 0);
}
int main()
{
int retval = 0, err = 0;
struct vm_ctx vm_ctx = { -1 };
struct vcpu_ctx vcpu_ctx = { -1 };
kvm_fd = open("/dev/kvm", O_RDWR);
if (kvm_fd == -1) {
perror("Failed to access to KVM interface");
retval = 1;
goto out;
}
if (ioctl(kvm_fd, KVM_GET_API_VERSION, (void*)0) != KVM_API_VERSION) {
perror("KVM module version mismatched");
retval = 1;
goto out;
}
err = create_vm(&vm_ctx, 0x100000); // 1MB
if (err) {
fprintf(stderr, "Failed to create a new VM: %s", strerror(err));
retval = 1;
goto out;
}
err = create_vcpu(&vcpu_ctx, &vm_ctx, 0);
if (err) {
fprintf(stderr, "Failed to create a new VCPU: %s", strerror(err));
retval = 1;
goto out;
}
{
static const char instr[] = {
0xb8, 0x00, 0x00, /* mov $0, %ax */
0xba, 0x00, 0x01, /* mov $0x100, %dx */
0xef, /* out %ax, (%dx) */
0x40, /* inc %ax */
0x83, 0xf8, 0x64, /* cmp 100, %eax */
0x7c, 0xf9, /* jl -7 */
0xeb, 0xfe /* jmp -2 */
};
memcpy((char*)vm_ctx.phys_mem.addr + 0xffff0, instr, sizeof(instr));
}
for (;;) {
err = vcpu_run(&vcpu_ctx);
if (err) {
fprintf(stderr, "Failed to run VCPU: %s", strerror(err));
retval = 1;
goto out;
}
if (vcpu_ctx.stat->exit_reason == KVM_EXIT_IO) {
if (vcpu_ctx.stat->io.direction == KVM_EXIT_IO_OUT) {
if (vcpu_ctx.stat->io.port == 0x100) {
printf("%d\n", *(unsigned short*)((char*)vcpu_ctx.stat + vcpu_ctx.stat->io.data_offset));
}
}
}
}
out:
if (vcpu_ctx.fd != -1)
destroy_vcpu(&vcpu_ctx);
if (vm_ctx.fd != -1)
destroy_vm(&vm_ctx);
if (kvm_fd != -1)
close(kvm_fd);
return retval;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment