linux虚拟化之KVM

linux虚拟化之KVM

Posted by lwk on August 7, 2020

本文主要介绍linux虚拟化中的kvm部分。也是linux虚拟化三部马车里的最后一部。

image 当qemu执行kvm_vcpu_ioctl()时,进入内核态函数。具体切换至内核态是通过ioctl系统调用执行的。

image image ioctl系统调用sys_ioctl->ksys_ioctl->do_vfs_ioctl->vfs_ioctl->unlocked_ioctl->kvm_vcpu_ioctl

image image image #define KVMIO 0xAE

image image

kvm_vcpu_ioctl()第二个参数是int类型的,即KVM_RUN是个int,在qemu代码中KVM_RUN是个宏,看下这个宏具体是多少。

#define KVM_RUN _IO(0xAE, 0x80)

#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)

#define _IO(0xAE,0x80) _IOC(0,(0xAE),(0x80),0)

_IOC也是个宏

#define _IOC(dir,type,nr,size) \

(((dir) « _IOC_DIRSHIFT) \
((type) << _IOC_TYPESHIFT) | \

((nr)   << _IOC_NRSHIFT) | \

((size) << _IOC_SIZESHIFT))

将参数带入展开如下:

#define _IOC(0,0xAE,0x80,0) \

(((0) « _IOC_DIRSHIFT) \
((0xAE) << _IOC_TYPESHIFT) | \

((0x80)   << _IOC_NRSHIFT) |\

((0) << _IOC_SIZESHIFT))

接下来计算下_IOC_DIRSHIFT, _IOC_TYPESHIFT, _IOC_NRSHIFT,_IOC_SIZESHIFT

#define _IOC_NRSHIFT 0

#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS)

#define _IOC_NRBITS 8

#define _IOC_TYPEBITS 8

_IOC_TYPESHIFT = 0 + 8 = 8

_IOC_SIZESHIFT =_IOC_NRSHIFT+_IOC_NRBITS+_IOC_TYPEBITS = 0 + 8 + 8 = 16

_IOC_DIRSHIFT = _IOC_SIZESHIFT +_IOC_SIZEBITS = 16 + 14 = 30

#define _IOC(0,0xAE,0x80,0) \

(((0) « 30) \
((0xAE) << 8) | \

((0x80)   << 0) | \

((0) << 16))
#define _IOC(0,0xAE,0x80,0) ((0)«30) ((0xAE) « 8) ((0x80) « 0) ((0) « 16)

因此,最后得到KVM_RUN为0xAE80(十进制是44672)

#define _IOC(0,0xAE,0x80,0) 0xAE80

image 其中,type=0xAE80

KVM逻辑: image image

image image image

vcpu_enter_guest()函数核心逻辑是

image image image image kvm_x86_ops是个struct型指针。

image

从上面结构体可以看到,所有成员函数都是函数指针。

真正实现回调函数在arch/x86/kvm/vmx/vmx.c【注意:vmx是针对Intel VT-x,svm是针对AMD】 image

因此,kvm_x86_ops->run(vcpu)实际调用的函数是vmx_vcpu_run image image

image 这段汇编指令主要实现进入客户机模式以及从客户机模式退出。依赖于Intel的硬件虚拟化技术(AMD的参考svm.c的svm_vcpu_run())

下面截图是测试环境qemu进程的stack情况 image 总结下qemu进入通过ioctl进入内核态和guest进行通信的逻辑图: image