little-qemu&guest 中的 虚拟机内存角度分析 - 进行中

之前在 文章 中描述了 7个ioctl,现在着重分析 其中3个,这3个与虚拟机内存相关

KVM_CREATE_VM
KVM_GET_VCPU_MMAP_SIZE
KVM_SET_USER_MEMORY_REGION
  • 文章 中描述了 ARMv7的虚拟化支持中的 内存虚拟化支持 Second-stage of translation

基于 两点 对 linux-5.6 ARMv7 KVM 内存虚拟化 实现的探究

猜想 : 实现了 Second-stage of translation

正常访问流程
	GVA->GPA  利用 MMU  访问 guest OS 中的 页表
	GPA->HPA  利用 Second-stage of translation 机制  访问 hostOS中的专门为虚拟机做的 页表

猜想正确
	在 guest entry 的过程中 // 进入和退出的过程都在 __kvm_vcpu_run_nvhe  中
		__kvm_vcpu_run_nvhe 调用了 __activate_vm
	在 entery exit 的过程中
		__kvm_vcpu_run_nvhe 调用了 __deactivate_vm

__activate_vm
	write_sysreg(kvm_get_vttbr(kvm), VTTBR); // Second-stage of translation 相关的寄存器 : VTTBR
	write_sysreg(vcpu->arch.midr, VPIDR);
__deactivate_vm
	write_sysreg(0, VTTBR);
	write_sysreg(read_sysreg(MIDR), VPIDR);

参考 https://blog.csdn.net/u011011827/article/details/120471549
当 虚拟机 添加内存的时候 , 会有 GPA 与 HVA的对应关系
但是 实际访存时, 会由 MMU 访问 VTTBR  完成 GPA(IPA) -> HPA(PA) 的转换
所以,按道理, 虚拟机添加内存的时候 , 应该会改变 VTTBR 对应的 页表
而 VTTBR 是 host linux 管理的, 所以
虚拟机进程添加内存的时候,会建立 IPA(GPA) 与 HVA 的对应关系 memslot
当 guest 运行异常时 陷入 PL2后转入PL1调用user_mem_fault(解析memslot)建立 IPA 与 GPA 的对应关系(页表)
	页表是以 fault_ipa 为 源 做出了 pte 的地址
		 以 pfn 为 源  做出了 pte 的内容

ARMv7内存虚拟化代码关键点详解

页表基址寄存器

根据 文章 中的 “内存访存流程伪代码” 我们可以看到 需要关注几个寄存器

host 中的 
	HTTBR
	TTBR0/TTBR1
	VTTBR
guest 中的
	 TTBR0/TTBR1	  
  • host 中的 HTTBR
kvm_dev_ioctl_create_vm
	kvm_create_vm
		hardware_enable_all
			hardware_enable_nolock
				kvm_arch_hardware_enable
					_kvm_arch_hardware_enable
						cpu_hyp_reinit
							cpu_hyp_reset
								__hyp_reset_vectors
									mov     r0, #HVC_RESET_VECTORS
									__HVC(0)
										__kvm_call_hyp
											hvc     #0 // 进入 __kvm_hyp_init
											bx      lr					
									ret     lr


__kvm_hyp_init // 
	@ Hyp-mode exception vector
	W(b)    __do_hyp_init

__do_hyp_init
	// 将 r1 中的值 填充到 协处理器寄存器
	@ Set HVBAR to point to the HYP vectors
	mcr p15, 4, r1, c12, c0, 0  @ HVBAR
	
	// 将 r2 r3 中的值 填充到 协处理器寄存器
	@ Set the HTTBR to point to the hypervisor PGD pointer passed
	mcrr    p15, 4, rr_lo_hi(r2, r3), c2
  • host 中的 VTTBR
KVM_CREATE_VM
	kvm_dev_ioctl_create_vm
		kvm_dev_ioctl_create_vm
			kvm_create_vm
				kvm_arch_init_vm
					kvm_alloc_stage2_pgd
						pgd_t *pgd;
						pgd = alloc_pages_exact(stage2_pgd_size(kvm), GFP_KERNEL | __GFP_ZERO);
						kvm->arch.pgd = pgd;
						kvm->arch.pgd_phys = pgd_phys;

KVM_RUN
	...
		__kvm_vcpu_run_nvhe 
			__activate_vm
				write_sysreg(kvm_get_vttbr(kvm), VTTBR);
					baddr = kvm->arch.pgd_phys;
					vmid_field = (u64)vmid->vmid << VTTBR_VMID_SHIFT;
					return kvm_phys_to_vttbr(baddr) | vmid_field;
	
guest 访存流程 (时间角度)
想象中是这样子的流程
1. guest 内存相关 的初始化 (HTTBR 与 VTTBR 的设置) // 参考 "host 中的 HTTBR" 和 "host 中的 VTTBR"
2. guest os 开始运行
3. guest os 读 A 地址
4. 陷入 host PL2 异常 // host os 开始运行
	4.1 异常退出
	4.2 做相应的动作
	4.3 注入中断
	4.4 guest enter // guest os 开始 运行
5. guest os 陷入 guest PL1 异常
6. guest PL1 异常处理
	6.1 保存现场
	6.2 做相应的动作
	6.2 恢复现场,恢复 pc 为访存时的pc
7. guest os 读 A 地址 // 和 3 是同一个动作
8. guest os 获取 A 地址的值

注意 : 4.26.2 是 我们关注的重点

  • 初始化
arm_init
	kvm_init
		init_subsystems
			_kvm_arch_hardware_enable
				cpu_hyp_reinit
					cpu_hyp_reset
						__hyp_reset_vectors
							mov     r0, #HVC_RESET_VECTORS
							__HVC(0)
								__kvm_call_hyp
									hvc     #0
									bx      lr					
							ret     lr
					cpu_init_hyp_mode
						__cpu_init_hyp_mode(pgd_ptr, hyp_stack_ptr, vector_ptr); // 设置最终的异常向量表
						__cpu_init_stage2
							kvm_call_hyp(__init_stage2_translation);
								--- 进入 hyp mode 的异常向量表
									__kvm_hyp_vector_ic_inv:W(add)  sp, sp, #1      /* HVC            2 */
									decode_vectors
										blx     lr                      @ Call the HYP function
											__init_stage2_translation

おすすめ

転載: blog.csdn.net/u011011827/article/details/120864037