Learning KVM - implement your own kernel
主要参考的文章是david942j@217的这篇博客和这篇介绍。
在做abyss这个题目的时候涉及到了很多hypervisor, kvm的内容。虚拟化这一层一般也是我们在逃逸的最后一层了。
Basic Intro
KVM的全称是Kernel Base Virtual Machine,基于内核的虚拟机,是基于x86架构的免费的开源的全虚拟化解决方案。云计算的发展让虚拟化技术变得火热,KVM作为一种结构简单的方案,使得很多云服务提供者使用其作为hypervisor,包括RedHat和Ubuntu等等。
每次说到Hypervisor可能会有点模棱两可,这里把hyperv说一下好了。称作虚拟机监视器,virtual machine monitor,缩写VMM。
首先我们有一个host,即主体机器。在host运行hypervisor,hypervisor提供虚拟的平台给客体操作系统guest,负责管理guest的执行。这些guest共享虚拟化后的硬件资源。
总的来说,hypervisor是运行在服务器和操作系统之间的中间软件层,允许多个操作系统共享硬件。
hypervisor的类型主要分为三种:
- (1)直接运行在系统硬件上,称作“裸机”型
- (2)运行在传统操作系统上,称作“托管(宿主)”型。调用资源时通过vm内核->hypervisor->主机内核,性能比较差。
- (3)运行在传统操作系统上,创建独立的虚拟化容器,指向底层托管操作系统,称作操作系统虚拟化。
KVM提供分隔的独立的运行环境,KVM创建的每一个虚拟机都都有自己的虚拟化硬件,包括网卡,硬盘,图形适配器等等。
我们接下来的学习目标:
- 如何使用KVM来执行简单的汇编代码
- 通过KVM运行内核的关键点
需要的环境:
- 开启了KVM
Get started
我们现在的位置在Host宿主机。想要和kvm交互需要通过ioctl,这个在内核我们已经见过很多次了。
Create a vm
通过kvm创建vm的7个步骤如下:
- 通过open打开/dev/kvm
kvmfd=open("/dev/kvm", O_RDWR|O_CLOEXEC)
- 通过ioctl创建vm
vmfd=ioctl(kvmfd, KVM_CREATE_VM, 0)
- 为vm guest设置内存范围
ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, ®ion)
- 为vm创建一个虚拟cpu
vcpufd=ioctl(vmfd, KVM_CREATE_VCPU, 0)
vcpu可以有多个 - 给vcpu分配内存
vcpu_size=ioctl(kvmfd, KVM_GET_VCPU_MMAP_SIZE, NULL)
run=(struct kvm_run*)mmap(NULL, mmap_size, PROT_READ|PROT_WRITE, MAP_SHARED, vcpufd, 0)
- 把汇编代码放入到用户空间,设置vcpu的寄存器的值,例如rip等等
- 运行vm并根据退出状态做好处理
while(1) { ioctl(vcpufd, KVM_RUN, 0); ... }
丰富一下结构图:
Create a vcpu
- 通过ioctl vmfd创建vcpu拿到vcpufd
- 通过ioctl kvmfd拿到vcpu的大小并mmap这么大的空间给vcpu
- 设置vcpu的regs和sregs寄存器
Excute 16-bit assembled code
就是一个while 1的大循环。通过ioctl vcpufd run来运行,直到遇到需要退出vm的指令如hlt
out
等。需要对退出的原因进行处理,是结束运行还是IO中断还是出了问题shutdown等等。其中比较重要的是IO,in
和out
指令会触发KVM_EXIT_IO。这要是hypervisor和host通信的手段。
需要注意的是现在没有页表所以不能运行32位/64位程序,现在处于实模式(Real Mode),只能运行16-bit的汇编代码。
也就是读写端口。我们现在还没有内核,自然也就没有syscall之类的,现在也没有hypercall。
目前的效果:
|