今天制作一个简单的kvm模型,并制作个简单的kernel用于执行,主要是了解kvm的io虚拟化,用于学你kvm的工作过程。
kvm是一个经过修改的qemu,kvm利用硬件虚拟化技术为qemu提供加速,主要有cpu虚拟化和内存虚拟化。qemu为kvm提供io的虚拟化
一下就是kvm中几个重要的函数,并且一会儿会用到
kvm_context_t kvm_init(struct kvm_callbacks *callbacks,
void *opaque);
int kvm_create(kvm_context_t kvm,
unsigned long phys_mem_bytes,
void **phys_mem);
int kvm_create_vcpu(kvm_context_t kvm,
int slot);
void *kvm_create_phys_mem(kvm_context_t kvm,
unsigned long phys_start,
unsigned long len,
int log,
int writable);
int kvm_run(kvm_context_t kvm,
int vcpu);
首先创建一个KVM进程,需要利用kvm_init函数,函数的第一个参数就是kvm_callbacks这是一个回调函数,就是当虚拟机执行了一些特权指令或者io指令是将会执行回调函数中内容。如果是io指令将会退出客户模式进入用户模式的qemu中模拟,并调用相应的函数。函数具体声明在libkvm.h
代码
之后调用kvm_create(),函数第二个参数是RAM大小和加载的程序地址。此时会为虚拟机分配一个vcpu。这个函数调用时并不会分配内存
调用kvm_create_vcpu()创建虚拟cpu。个数0-65。此函数由某线程调用添加vcpu的个数
kvm_create_phys_mem()是为虚拟机分配内存有几种方法之一
之后调用kvm_run启动vcpu和虚拟机,这个函数并不会返回,除非遇到1、io处理程序没有处理成功2、出现了一个kvm和虚拟机都无法处理的情况。
模拟kvm代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <libkvm.h>
#ifndef __RUNTIME_H__
#define __RUNTIME_H__
// port to use for general purpose output
#define IO_PORT_PSEUDO_SERIAL 0xf1
#endif /* __RUNTIME_H_ */
/* callback definitions as shown in Listing 2 go here */
static int my_inb(void *opaque, int16_t addr, uint8_t *data)
{ puts ("inb"); return 0; }
static int my_inw(void *opaque, uint16_t addr, uint16_t *data)
{ puts ("inw"); return 0; }
static int my_inl(void *opaque, uint16_t addr, uint32_t *data)
{ puts ("inl"); return 0; }
static int my_outb(void *opaque, uint16_t addr, uint8_t data)
{
if (addr == IO_PORT_PSEUDO_SERIAL)
if (isprint(data) || data == '\n')
putchar(data);
else
putchar('.');
else
printf("outb: %x, %d\n", addr, data);
fflush (NULL);
return 0;
}
static int my_outw(void *opaque, uint16_t addr, uint16_t data)
{ puts ("outw"); return 0; }
static int my_outl (void *opaque, uint16_t addr, uint32_t data)
{ puts ("outl"); return 0; }
static int my_pre_kvm_run(void *opaque, int vcpu)
{ return 0; }
static struct kvm_callbacks my_callbacks = {
.inb = my_inb,
.inw = my_inw,
.inl = my_inl,
.outb = my_outb,
.outw = my_outw,
.outl = my_outl,
.pre_kvm_run = my_pre_kvm_run,
};
void load_file(void *mem, const char *filename)
{
int fd;
int nr;
fd = open(filename, O_RDONLY);
if (fd == -1) {
fprintf(stderr, "Cannot open %s", filename);
perror("open");
exit(1);
}
while ((nr = read(fd, mem, 4096)) != -1 && nr != 0)
mem += nr;
if (nr == -1) {
perror("read");
exit(1);
}
close(fd);
}
#define MEMORY_SIZE (0x1000000) /* 16 Mb */
#define FIRST_VCPU (0)
int main(int argc, char *argv[])
{
kvm_context_t kvm;
void *memory_area;
/* Second argument is an opaque, we don't use it yet */
kvm = kvm_init(&my_callbacks, NULL);
if (!kvm) {
fprintf(stderr, "KVM init failed");
exit(1);
}
if (kvm_create(kvm, MEMORY_SIZE, &memory_area) != 0) {
fprintf(stderr, "VM creation failed");
exit(1);
}
#ifndef KVM_VERSION_LESS_THAN_65
if (kvm_create_vcpu(kvm, FIRST_VCPU)) {
fprintf(stderr, "VCPU creation failed");
exit(1);
}
#endif
memory_area = kvm_create_phys_mem(kvm, 0, MEMORY_SIZE, 0, 1);
load_file(memory_area + 0xf0000, argv[1]);
kvm_run(kvm, FIRST_VCPU);
return 0;
}
连接器代码
OUTPUT_FORMAT(binary)
SECTIONS {
. = 0;
.text : { *(.init) *(.text) }
. = ALIGN(4K);
.data : { *(.data) }
. = ALIGN(16);
.bss : { *(.bss) }
. = ALIGN(4K);
.edata = .;
}
制作镜像代码:
#ifndef __RUNTIME_H__
#define __RUNTIME_H__
// port to use for general purpose output
#define IO_PORT_PSEUDO_SERIAL 0xf1
#endif /* __RUNTIME_H_ */
.code16
start:
mov $0x48,%al // H
outb %al,$IO_PORT_PSEUDO_SERIAL
mov $0x65,%al // e
outb %al,$IO_PORT_PSEUDO_SERIAL
mov $0x6c,%al // l
outb %al,$IO_PORT_PSEUDO_SERIAL
mov $0x6c,%al // l
outb %al,$IO_PORT_PSEUDO_SERIAL
mov $0x6f,%al // o
outb %al,$IO_PORT_PSEUDO_SERIAL
mov $0x0a,%al // new_line
outb %al,$IO_PORT_PSEUDO_SERIAL
hlt // halt the processor
. = 0xfff0
ljmp $0xf000, $start
Makefile
# If KVM was compiled from sources and you have errors about
# missing asm/kvm*.h files, copy them from
# kvm-XX/kernel/include/asm/* to {prefix}/include/asm/
CC=gcc
KERNEL16_CFLAGS=-nostdlib -ffreestanding -Wl,-T,kernel16.lds
all: launcher kernel2
launcher: launcher.o
$(CC) launcher.o /usr/lib/libkvm.a -o launcher
launcher.o:
kernel2: kernel2.S
$(CC) $(KERNEL16_CFLAGS) kernel2.S -o kernel1
clean:
rm *.o launcher kernel1
制作内核
gcc -nostdlib -Wl,-T,kernel16.lds kernel1.S -o kernel1
执行
./launcher kernel1