设为首页 收藏本站
查看: 1552|回复: 0

[经验分享] kvm

[复制链接]

尚未签到

发表于 2015-4-10 12:10:32 | 显示全部楼层 |阅读模式
  kvm_dev_ioctl_create_vm->kvm = kvm_create_vm(type);产生新的vm
               ->生成相应的设备文件:r = anon_inode_getfd("kvm-vm", &kvm_vm_fops, kvm, O_RDWR)
  kvm_create_vm->kvm_arch_init_vm->hardware_enable_all()-〉
  
  
  最初的是在 __init vmx_init 中,这块代码是实际驱动模块的 开始
  1.->kvm_init->kvm_arch_init
  ->kvm_arch_hardware_setup()->kvm_x86_ops->hardware_setup()->static struct kvm_x86_ops vmx_x86_ops 是进行操作运行的->hardware_setup 在这个函数中存在很多的标志位,需要注意以后会在哪里用到
  ->setup_vmcs_config()设置 vmcs_config
  ->alloc_kvm_area()为每个cpu配置 vmcs,这里要注意的是vmcs对应的是虚拟地址(这里我觉得应该要注意点,内核中使用的应该也是 内核的虚拟地址 ),根据 vmcs_config设置 相应的 vmcs
  -> kvm_mmu_module_init();
  ->kvm_set_mmio_spte_mask();
  ->kvm_timer_init()上面的函数还没有分析
  ->register_cpu_notifier(&kvm_cpu_notifier);应该是对每个CPU多会调用这个函数,还没分析,应该是对事件进行处理
  ->r = misc_register(&kvm_dev);注册kvm_dev这个设备,该设备对应的操作时 ->kvm_dev_ioctl()这个函数用来调用 kvm_dev_ioctl_create_vm生成新的vm
  下面是kvm_dev对应的设备操作等



static struct file_operations kvm_chardev_ops = {
.unlocked_ioctl = kvm_dev_ioctl,
.compat_ioctl   = kvm_dev_ioctl,//这是外界 qemu和kvm内部的接口
.llseek= noop_llseek,
};
static struct miscdevice kvm_dev = {
KVM_MINOR,
"kvm",
&kvm_chardev_ops,//操作
};

  2.处理ept相关的内容
  开启或者关闭ept,这里使用的标志位是在 hardware_setup函数内赋值的。调用 kvm_enable_tdp()
  
  当用户调用 ioctl(,KVM_CREATE_VM)时生成新的vm时调用顺序:
  kvm_dev_ioctl->kvm_dev_ioctl_create_vm ->kvm_create_vm(type);
  ->kvm_arch_alloc_vm();
  ->kvm_arch_init_vm          
                              ->hardware_enable_all()对于每个CPU调用->hardware_enable_nolock
                              ->kvm->mm = current->mm这理可以看处kvm使用的是当前进程的内存区间
  
  ->生成相应的设备文件:r = anon_inode_getfd("kvm-vm", &kvm_vm_fops, kvm, O_RDWR)  kvm-vm 这个设备文件就是要进行vcpu创建等工作的。
  kvm_vm_fops



static struct file_operations kvm_vm_fops = {
2308         .release        = kvm_vm_release,
2309         .unlocked_ioctl = kvm_vm_ioctl,//这里是用来操作建立vcpu等工作的
2310 #ifdef CONFIG_COMPAT
2311         .compat_ioctl   = kvm_vm_compat_ioctl,
2312 #endif
2313         .mmap           = kvm_vm_mmap,
2314         .llseek         = noop_llseek,
2315 };
  
  用户调用ioctl("kvm-v",KVM_CREATE_VCPU)生成新的cpu时调用下面函数:
  kvm_vm_ioctl_create_vcpu(struct kvm *kvm, u32 id)->vcpu = kvm_arch_vcpu_create(kvm, id);(这个id是vcpu的id,不是实际cpu的id)
  ->kvm_vcpu *vmx_create_vcpu
  ->cpu=get_cpu()
  ->vmx_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
  将相应的vcpu对应的vmcs于id为cpu的核心进行匹配设置cpu per_cpu(current_vmcs, cpu)域,完成vmcs激活等,并且更新相应的vcpu对应的vmcs中的host域,vmx = to_vmx(vcpu);
  
  ->vmx_vcpu_setup:
  这个函数设置相应的vmcs域。Sets up the vmcs for emulated real mode.
  ->vmx_vcpu_put
  


DSC0000.gif DSC0001.gif


static void vmx_vcpu_put(struct kvm_vcpu *vcpu)
1604 {
1605         __vmx_load_host_state(to_vmx(vcpu));//这里是安需要重新加载host的寄存器的值,为什么要重新加载呢?
1606         if (!vmm_exclusive) {//这里是标志着什么呢?
1607                 __loaded_vmcs_clear(to_vmx(vcpu)->loaded_vmcs);
1608                 vcpu->cpu = -1;
1609                 kvm_cpu_vmxoff();
1610         }
1611 }
View Code   注意如果想要退出虚拟模式,可以使用VMM运行VMXOFF指令.
  
  ->kvm_vcpu_init
  会设置vcpu->run = page_address(page);这个结构在io中会很有用,这个函数主要是初始化vcpu的数据
  ->kvm_arch_vcpu_init



if (!irqchip_in_kernel(kvm) || kvm_vcpu_is_bsp(vcpu))//第一个判断是kvm中的kvm->arch.vpic是否被为空,如果为空的话执行6624
6624                 vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
6625         else
6626                 vcpu->arch.mp_state = KVM_MP_STATE_UNINITIALIZED;
6627

下面和上面的判断一样
if (irqchip_in_kernel(kvm)) {
6642                 r = kvm_create_lapic(vcpu);//不为空的话为 vcpu->arch.apic = apic;设置新值没理解为什么要这样作.
6643                 if (r < 0)
6644                         goto fail_mmu_destroy;
6645         } else
6646                 static_key_slow_inc(&kvm_no_apic_vcpu);//如果为空的话,增加kvm_no_apic_vcpu的值
  
  

初始化vcpu->arch里面的结构,并且会调用kvm_mmu_create(在上篇关于内存的地方有讲到)
                                                ->vmx_vcpu_put(&vmx->vcpu);(在函数vmx_create_vcpu中被调用)



  ->kvm_arch_vcpu_setup(vcpu)
  ->r = vcpu_load(vcpu);
  ->cpu=get_cpu();得到当前的cpu号.
  ->kvm_arch__vcpu_load(vcpu, cpu);
  ->vmx_vcpu_load(struct kvm_vcpu *vcpu, int cpu)(这里为什么还要调用一次这个函数,在vmx_create_vcpu中已经调用了这个函数完成了.vmcs于cpu的匹配??)
  ->下面的很大一部分是处理的tsc相关的内容(时钟吧)
  ->kvm_vcpu_reset
  重置了一些vcpu.arch中的内容,例如clock
  ->kvm_x86_ops->vcpu_reset(vcpu);
  ->vcpu_put(vcpu);和上面一样最终调用的是vmx_vcpu_put
  

                           ->create_vcpu_fd(struct kvm_vcpu *vcpu)
完成在用户空间定义一个kvm-vcpu的文件,利用下面的将该vcpu于该文件以及kvm_vcpu_fops结合起来。 anon_inode_getfd("kvm-vcpu", &kvm_vcpu_fops, vcpu, O_RDWR);

  
  

                     
  

kvm_vcpu_ioctl会按照参数调用下面的函数
->kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)这里2个参数需要注意下,最后的参数可能在io 时候用到
       ->__vcpu_run(vcpu)  
          ->vcpu_enter_guest(struct kvm_vcpu *vcpu)
              ->作一些必要的检测主要是检测requset,需要注入中断时注入终端,这里以后还在分析
              ->r = kvm_mmu_reload(vcpu);这里是加载相应的内存页吧.//在上篇内存中有介绍
              ->kvm_x86_ops->prepare_guest_switch(vcpu);这里主要是保存的host的状态位
              ->kvm_load_guest_xcr0(vcpu);这里还没分析好
  
              ->kvm_guest_enter(void)这里主要是设计到rcu的
              ->kvm_x86_ops->run(vcpu);(vmx_vcpu_run也就是这个函数)
                ->vmx_vcpu_run(struct kvm_vcpu *vcpu)
下面是具体进入的代码:


/* Store host registers */
6316                 "push %%" _ASM_DX "; push %%" _ASM_BP ";"//下面要用到这些寄存器所有需要保存
6317                 "push %%" _ASM_CX " \n\t" /* placeholder for guest rcx */
6318                 "push %%" _ASM_CX " \n\t"
6319                 "cmp %%" _ASM_SP ", %c[host_rsp](%0) \n\t"
6320                 "je 1f \n\t"
6321                 "mov %%" _ASM_SP ", %c[host_rsp](%0) \n\t"
6322                 __ex(ASM_VMX_VMWRITE_RSP_RDX) "\n\t"
6323                 "1: \n\t"
6324                 /* Reload cr2 if changed */
6325                 "mov %c[cr2](%0), %%" _ASM_AX " \n\t"
6326                 "mov %%cr2, %%" _ASM_DX " \n\t"
6327                 "cmp %%" _ASM_AX ", %%" _ASM_DX " \n\t"
6328                 "je 2f \n\t"
6329                 "mov %%" _ASM_AX", %%cr2 \n\t"
6330                 "2: \n\t"
6331                 /* Check if vmlaunch of vmresume is needed */
6332                 "cmpl $0, %c[launched](%0) \n\t"//%c对应的vmx这个结构,而lauched对应的是lauched这个结构的偏移,下面有讲到
6333                 /* Load guest registers.  Don't clobber flags. */
6334                 "mov %c[rax](%0), %%" _ASM_AX " \n\t"//用vmx中的寄存器的值填充到当前的寄存器中
6335                 "mov %c[rbx](%0), %%" _ASM_BX " \n\t"
6336                 "mov %c[rdx](%0), %%" _ASM_DX " \n\t"
6337                 "mov %c[rsi](%0), %%" _ASM_SI " \n\t"
6338                 "mov %c[rdi](%0), %%" _ASM_DI " \n\t"
6339                 "mov %c[rbp](%0), %%" _ASM_BP " \n\t"
6340 #ifdef CONFIG_X86_64
6341                 "mov %c[r8](%0),  %%r8  \n\t"
6342                 "mov %c[r9](%0),  %%r9  \n\t"
6343                 "mov %c[r10](%0), %%r10 \n\t"
6344                 "mov %c[r11](%0), %%r11 \n\t"
6345                 "mov %c[r12](%0), %%r12 \n\t"
6346                 "mov %c[r13](%0), %%r13 \n\t"
6347                 "mov %c[r14](%0), %%r14 \n\t"
6348                 "mov %c[r15](%0), %%r15 \n\t"
6349 #endif
6350                 "mov %c[rcx](%0), %%" _ASM_CX " \n\t" /* kills %0 (ecx) */
6351
6352                 /* Enter guest mode */
6353                 "jne 1f \n\t"
6354                 __ex(ASM_VMX_VMLAUNCH) "\n\t"
6355                 "jmp 2f \n\t"
6356                 "1: " __ex(ASM_VMX_VMRESUME) "\n\t"
6357                 "2: "//从上面开始就已经进入到了GUEST模式中
6358                 /* Save guest registers, load host registers, keep flags */
6359                 "mov %0, %c[wordsize](%%" _ASM_SP ") \n\t"//从这里开始退出了GUSET模式,有进入了host模式
6360                 "pop %0 \n\t"
6361                 "mov %%" _ASM_AX ", %c[rax](%0) \n\t"
6362                 "mov %%" _ASM_BX ", %c[rbx](%0) \n\t"
6363                 __ASM_SIZE(pop) " %c[rcx](%0) \n\t"
6364                 "mov %%" _ASM_DX ", %c[rdx](%0) \n\t"
6365                 "mov %%" _ASM_SI ", %c[rsi](%0) \n\t"
6366                 "mov %%" _ASM_DI ", %c[rdi](%0) \n\t"
6367                 "mov %%" _ASM_BP ", %c[rbp](%0) \n\t"
6368 #ifdef CONFIG_X86_64
6369                 "mov %%r8,  %c[r8](%0) \n\t"
6370                 "mov %%r9,  %c[r9](%0) \n\t"
6371                 "mov %%r10, %c[r10](%0) \n\t"
6372                 "mov %%r11, %c[r11](%0) \n\t"
6373                 "mov %%r12, %c[r12](%0) \n\t"
6374                 "mov %%r13, %c[r13](%0) \n\t"
6375                 "mov %%r14, %c[r14](%0) \n\t"
6376                 "mov %%r15, %c[r15](%0) \n\t"
6377 #endif
6378                 "mov %%cr2, %%" _ASM_AX "   \n\t"
6379                 "mov %%" _ASM_AX ", %c[cr2](%0) \n\t"
6380
6381                 "pop  %%" _ASM_BP "; pop  %%" _ASM_DX " \n\t"
6382                 "setbe %c[fail](%0) \n\t"
6383                 ".pushsection .rodata \n\t"
6384                 ".global vmx_return \n\t"
6385                 "vmx_return: " _ASM_PTR " 2b \n\t"
6386                 ".popsection"
6387               : : "c"(vmx), "d"((unsigned long)HOST_RSP),
6388                 [launched]"i"(offsetof(struct vcpu_vmx, __launched)),
6389                 [fail]"i"(offsetof(struct vcpu_vmx, fail)),
6390                 [host_rsp]"i"(offsetof(struct vcpu_vmx, host_rsp)),
6391                 [rax]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RAX])),
6392                 [rbx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RBX])),
6393                 [rcx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RCX])),
6394                 [rdx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RDX])),
6395                 [rsi]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RSI])),
6396                 [rdi]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RDI])),
6397                 [rbp]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RBP])),
6398 #ifdef CONFIG_X86_64
6399                 [r8]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R8])),
6400                 [r9]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R9])),
6401                 [r10]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R10])),
6402                 [r11]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R11])),
6403                 [r12]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R12])),
6404                 [r13]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R13])),
6405                 [r14]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R14])),
6406                 [r15]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R15])),
6407 #endif
6408                 [cr2]"i"(offsetof(struct vcpu_vmx, vcpu.arch.cr2)),
6409                 [wordsize]"i"(sizeof(ulong))
6410               : "cc", "memory"
6411 #ifdef CONFIG_X86_64
6412                 , "rax", "rbx", "rdi", "rsi"
6413                 , "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
6414 #else
6415                 , "eax", "ebx", "edi", "esi"
6416 #endif
6417               );



  下面的很大一部分代码都是更行vcpu的相关域主要是有关中断处理的部分.而对应的(下面3个函数是vmx_vcpu_run中的函数)
  ->vmx_complete_atomic_exit(vmx);

6463                                                   ->vmx_recover_nmi_blocking(vmx);
6464                                                   ->vmx_complete_interrupts(vmx);
则可以处理的是将相应的中断或者其他信息读入到vmx中.
               ->kvm_guest_exit();调用这个函数不知道有什么用处
               ->
kvm_x86_ops->handle_exit()调用vmx_handle_exit处理相应的vcpu的中断
  



->long kvm_arch_vcpu_ioctl(struct file *filp,unsigned int ioctl, unsigned long arg)



2044         case KVM_SET_FPU: {
2045                 fpu = memdup_user(argp, sizeof(*fpu));
2046                 if (IS_ERR(fpu)) {
2047                         r = PTR_ERR(fpu);
2048                         fpu = NULL;
2049                         goto out;
2050                 }
2051                 r = kvm_arch_vcpu_ioctl_set_fpu(vcpu, fpu);
2052                 break;
2053         }
2054         default:
2055                 r = kvm_arch_vcpu_ioctl(filp, ioctl, arg);//default的情况调用这个函数,而这个函数处理qemu注入中断的情况
2056         }
2057 out:
上面这个函数在kvm_vcpu_ioctl中被默认的调用,也就是不符合kvm_vcpu_ioctl中指定参数的情况下调用,但是这个函数非常重要,用于在用户空间的qemu对向vcpu注入中断

    

     
              
              
              
  
  
  

  
  
  
  
  一篇关于内核中内存的文章,
  http://blog.iyunv.com/ctthuangcheng/article/details/8915146
  首先,内核代码所访问的地址都是虚拟地址,因为CPU指令接收的就是虚拟地址(地址映射对于CPU指令是透明的)。但是,建立地址映射时,内核在页表里面填写的内容却是物理地址,因为地址映射的目标就是要得到物理地址。
  上面的话摘自博客
  

smp_rmb();这个函数完成的是内存屏障的作用,也就是保证对在 LFENCE 指令前面发出的所有加载指令执行序列化操作.保证在lfence前进行的读写完成,这里会停止流水线

pic_irqchip(kvm)返回kvm->arch.vpic;
irqchip_in_kernel()返回kvm->arch.vpic是否被初始化
get_cpu()禁止抢断,并且要返回当前的cpu号
put_cpu()设置可以抢断
指令:
  VMCALL          --- 调用虚拟机监视器(VM Monitor)
  VMCLEAR        --- 清空虚拟机控制结构(VMCS)
  VMLAUNCH      --- 初始化(Lauch)虚拟机
  VMRESUME      --- 重新进入先前已经初始化(Resume)的虚拟机
  VMPTRLD        --- 加载指向VMCS的指针
  VMPSRT          --- 存储指向VMCS的指针
  VMREAD          --- 读取虚拟机控制结构(VMCS)中的域值
  VMWRITE        --- 写入虚拟机控制结构(VMCS)中的域值
  VMXOFF          --- 退出VMX root操作状态
  VMXON           --- 进入VMX root操作状态      
  
  关于MP 初始化的文章http://blog.iyunv.com/zenny_chen/article/details/6060253
  关于APIC的文章http://blog.iyunv.com/kendyhj9999/article/details/8927709
  需要查看进入linux模块的参数如何传递???????????????????????????

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-55659-1-1.html 上篇帖子: kvm分析笔记(1):代码结构分析 下篇帖子: 从KVM角度对MIDlet生命周期的剖析
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表