0X00 Preface
本系列笔记为学习虚拟化,KVM的读书笔记,目的为通过阅读kvmtool源码笔记并通过KVM API实现款能够运行img镜像的最小的KVM程序。
0X01 初始化命令
kvmtool对不懂的debug场景有不同的参数分配,因此本次只从run命令开始初次往后阅读。kvmltool的对运行命令使用结构体进行统一地初始化,代码位于kvm-cmd.c
中
struct cmd_struct kvm_commands[] = {
{"pause", kvm_cmd_pause, kvm_pause_help, 0},
{"resume", kvm_cmd_resume, kvm_resume_help, 0},
{"debug", kvm_cmd_debug, kvm_debug_help, 0},
{"balloon", kvm_cmd_balloon, kvm_balloon_help, 0},
{"list", kvm_cmd_list, kvm_list_help, 0},
{"version", kvm_cmd_version, NULL, 0},
{"--version", kvm_cmd_version, NULL, 0},
{"stop", kvm_cmd_stop, kvm_stop_help, 0},
{"stat", kvm_cmd_stat, kvm_stat_help, 0},
{"help", kvm_cmd_help, NULL, 0},
{"setup", kvm_cmd_setup, kvm_setup_help, 0},
{"run", kvm_cmd_run, kvm_run_help, 0},
{"sandbox", kvm_cmd_sandbox, kvm_run_help, 0},
{NULL, NULL, NULL, 0},
};
设定一个运行img的命令行
sudo ./lkvm run --kernel bzImage --initrd initramfs-busybox-x86.cpio.gz
其中kernel文件bzImage以及Linux文件系统的制作可以参考https://mgalgs.io/2015/05/16/how-to-build-a-custom-linux-kernel-for-qemu-2015-edition.html,从run命令进行跟读,即kvm_cmd_run
。KVM开头进行结构体初始化,整个代码如下
struct kvm {
struct kvm_arch arch;
struct kvm_config cfg;
int sys_fd; /* /dev/kvm的文件句柄 */
int vm_fd; /* KVM API New VM的句柄 */
timer_t timerid; /* Posix timer for interrupts */
int nrcpus; /* CPU的个数 */
struct kvm_cpu **cpus;
u32 mem_slots; /* for KVM_SET_USER_MEMORY_REGION */
u64 ram_size; /* Guest memory size, in bytes */
void *ram_start;
u64 ram_pagesize;
struct mutex mem_banks_lock; /* 通过线程启动CPU函数的时候作为CPU线程锁参数*/
struct list_head mem_banks;
bool nmi_disabled;
bool msix_needs_devid;
const char *vmlinux;
struct disk_image **disks;
int nr_disks;
int vm_state; /* KVM的状态:Pause/Run */
#ifdef KVM_BRLOCK_DEBUG
pthread_rwlock_t brlock_sem;
#endif
};
对结构体拆分分析。参数arch所代表的结构体kvm_arch
存储了虚拟地址的各项参数,包括GVA(Guest Virtual address) 经过malloc之后的起始地址以及kernel和initrd系统文件加载地址参数。参数cfg所代表的结构体kvm_config则包含了许多虚拟化必备的参数,其总代码为
struct kvm_config {
struct kvm_config_arch arch;
struct disk_image_params disk_image[MAX_DISK_IMAGES];
struct vfio_device_params *vfio_devices;
u64 ram_addr; /* Guest memory physical base address, in bytes */
u64 ram_size; /* Guest memory size, in bytes */
u8 num_net_devices;
...
const char *kernel_cmdline;
const char *kernel_filename;
const char *initrd_filename;
...
};
本文主要条运行最小Linux必要的几个参数分析。
u64 ram_addr
,u64 ram_size
: 为启动KVM必要的内存起始位置,其存储了启动VM的起始内存地址以及内存的大小const char *kernel_cmdline
: 启动VM默认附加命令const char *kernel_filename
,const char *initrd_filename
: kernel以及initrd文件地址
struct list_head mem_banks
的定义则是借用双向链表模拟VM的缺页换页的操作。 struct kvm_cpu **cpus
顾名思义就是VM的CPU函数,使用两个指针用于模拟多核CPU,kvm_cpu结构体为
struct kvm_cpu {
pthread_t thread; /* CPU线程运行后的句柄 */
unsigned long cpu_id;
unsigned long riscv_xlen;
unsigned long riscv_isa;
unsigned long riscv_timebase;
struct kvm *kvm;
int vcpu_fd;
struct kvm_run *kvm_run;
struct kvm_cpu_task *task;
u8 is_running;
u8 paused;
u8 needs_nmi;
struct kvm_coalesced_mmio_ring *ring;
};
struct kvm *kvm
: 每一个CPU的线程都需要调用KVM的API,而KVM API调用后的结果存储在struct kvm内。因此将外部的struct kvm的指针地址也存储在cpu结构体中方便调用KVM API的内容int vcpu_fd
: 存储函数ioctl(vcpu->kvm->vm_fd, KVM_CREATE_VCPU, cpu_id)
返回的文件句柄struct kvm_run *kvm_run
: KVM_RUN 接口的各项参数,即负责虚拟机运行的参数调整struct kvm_cpu_task *task
: 用于保存正在运行的CPU任务u8 is_running
: 保存CPU运行的状态,同时用于结束CPU线程和运行时的各种操作
0x02 KVM_CMD_RUN_INIT
在kvm_cmd_run
这个函数了包含了初始化并运行直到结束后退出VM的所有过程,整个函数只有三个子函数,分别是
kvm_cmd_run_init
: 包含了初始化一个VM的所有过程,包括初始化启动命令,初始化内存,将kernel内核加载到指定的ram起始地址等kvm_cmd_run_work
,kvm_cmd_run_exit
: 使用pthread启动CPU函数,同事也包含暂停并推出一个VM,并清理运行函数的栈空间
其次,kvmtool使用了全局函数栈作为启动VM辅助方法,全局栈函数位于头文件include/kvm/util-init.h
中,以后面的int型为优先级运行基础函数
#define core_init(cb) __init_list_add(cb, 0)
#define base_init(cb) __init_list_add(cb, 2)
#define dev_base_init(cb) __init_list_add(cb, 4)
#define dev_init(cb) __init_list_add(cb, 5)
#define virtio_dev_init(cb) __init_list_add(cb, 6)
#define firmware_init(cb) __init_list_add(cb, 7)
#define late_init(cb) __init_list_add(cb, 9)
运行VM加载的函数顺序如图
至此为kvmtool使用的kvm结构体,以及整个VM的运行时候的大致流程图,并制作基础KVM最小运行镜像,参考项目https://github.com/christasa/trivial-kvm/tree/6bce6168baf659bc6e262194b856fb69b7b524e8
0XFF Reference
- https://github.com/kvmtool/kvmtool
- Inside the Linux Virtualization Principle and Implementation