Created
March 9, 2010 06:15
-
-
Save moriyoshi/326285 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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