ARMv7 KVM 在 linux中的实现

linux-5.6

函数角度

架构无关部分 在 virt
架构相关部分 在 arch/arm/kvm
  • arch/arm/kvm 的出口函数
__init_stage2_translation // 没被 virt 调用
__kvm_arm_vcpu_get_events
__kvm_arm_vcpu_set_events
__kvm_flush_vm_context
__kvm_hyp_init
__kvm_tlb_flush_local_vmid
__kvm_tlb_flush_vmid
__kvm_tlb_flush_vmid_ipa
__kvm_vcpu_run_nvhe
__vcpu_spsr // 没被 virt 调用
handle_exit
kvm_arch_vcpu_ioctl_get_fpu
kvm_arch_vcpu_ioctl_get_regs
kvm_arch_vcpu_ioctl_get_sregs
kvm_arch_vcpu_ioctl_set_fpu
kvm_arch_vcpu_ioctl_set_guest_debug
kvm_arch_vcpu_ioctl_set_regs
kvm_arch_vcpu_ioctl_set_sregs
kvm_arch_vcpu_ioctl_translate
kvm_arm_copy_reg_indices
kvm_arm_get_reg
kvm_arm_num_regs
kvm_arm_set_reg
kvm_arm_vcpu_arch_get_attr
kvm_arm_vcpu_arch_has_attr
kvm_arm_vcpu_arch_set_attr
kvm_coproc_table_init
kvm_inject_vabt
kvm_reset_vcpu
kvm_target_cpu
kvm_vcpu_preferred_target
vgic_v3_cpu_sysregs_uaccess
vgic_v3_has_cpu_sysregs_attr

初始化角度

virt/kvm/arm/arm.c
	module_init(arm_init);
		kvm_init
			...

运行时角度

  • VM entry

// arm 的 kvm 代码(包括架构和非架构相关) 运行在 两个mode
// 有 __hyp_text 标识的 运行在 hyp mode 

vcpu 对应一个 fd ,对应一个 file_operations , 为 kvm_vcpu_fops
static struct file_operations kvm_vcpu_fops = {
  .release        = kvm_vcpu_release,
  .unlocked_ioctl = kvm_vcpu_ioctl,
  .mmap           = kvm_vcpu_mmap,
  .llseek   = noop_llseek,
  KVM_COMPAT(kvm_vcpu_compat_ioctl),
};


kvm_vcpu_ioctl // virt/kvm/kvm_main.c
	switch (ioctl) {
		case KVM_RUN: {
			oldpid = rcu_access_pointer(vcpu->pid);
			kvm_arch_vcpu_ioctl_run(vcpu, vcpu->run); // virt/kvm/arm/arm.c
				kvm_vcpu_first_run_init
					kvm_arm_vcpu_is_finalized
					kvm_timer_enable
					kvm_arm_pmu_v3_enable
				while (ret > 0) {
					...
					/**************************************************************
					* Enter the guest
					* /
					trace_kvm_entry(*vcpu_pc(vcpu));
					guest_enter_irqoff();
					if (has_vhe()) {ret = kvm_vcpu_run_vhe(vcpu);}
					else {ret = kvm_call_hyp_ret(__kvm_vcpu_run_nvhe, vcpu);}
					vcpu->mode = OUTSIDE_GUEST_MODE;
					vcpu->stat.exits++;
					/*
					 * Back from guest
					 *************************************************************/
					...
					guest_exit
					handle_exit
				}
		}
	}


__kvm_vcpu_run_nvhe // __hyp_text
	 /* Jump in the fire! */
	 again:
	 exit_code = __guest_enter(vcpu, host_ctxt); // 该行执行后,进入虚拟机, 下一行就是返回.
	 /* And we're baaack! */


// arch/arm/kvm/hyp/entry.S
/* int __guest_enter(struct kvm_vcpu *vcpu, struct kvm_cpu_context *host) */
ENTRY(__guest_enter)
	@ Save host registers
	add	r1, r1, #(USR_REGS_OFFSET + S_R4)
	stm	r1!, {r4-r12}
	str	lr, [r1, #4]	@ Skip SP_usr (already saved)

	@ Restore guest registers
	add	r0, r0,  #(VCPU_GUEST_CTXT + USR_REGS_OFFSET + S_R0)
	ldr	lr, [r0, #S_LR]
	ldm	r0, {r0-r12}

	clrex
	eret // 该指令后进入 虚拟机
ENDPROC(__guest_enter)


  • VM exit
我们知道返回时的第一条指令

__guest_enter 会进入,那什么时候返回呢?不知道


ENTRY(__guest_exit)
	/*
	 * return convention:
	 * guest r0, r1, r2 saved on the stack
	 * r0: vcpu pointer
	 * r1: exception code
	 */

	add	r2, r0, #(VCPU_GUEST_CTXT + USR_REGS_OFFSET + S_R3)
	stm	r2!, {r3-r12}
	str	lr, [r2, #4]
	add	r2, r0, #(VCPU_GUEST_CTXT + USR_REGS_OFFSET + S_R0)
	pop	{r3, r4, r5}		@ r0, r1, r2
	stm	r2, {r3-r5}

	ldr	r0, [r0, #VCPU_HOST_CTXT]
	add	r0, r0, #(USR_REGS_OFFSET + S_R4)
	ldm	r0!, {r4-r12}
	ldr	lr, [r0, #4]

	mov	r0, r1
	mrs	r1, SPSR
	mrs	r2, ELR_hyp
	mrc	p15, 4, r3, c5, c2, 0	@ HSR

	/*
	 * Force loads and stores to complete before unmasking aborts
	 * and forcing the delivery of the exception. This gives us a
	 * single instruction window, which the handler will try to
	 * match.
	 */
	dsb	sy
	cpsie	a

	.global	abort_guest_exit_start
abort_guest_exit_start:

	isb

	.global	abort_guest_exit_end
abort_guest_exit_end:

	/*
	 * If we took an abort, r0[31] will be set, and cmp will set
	 * the N bit in PSTATE.
	 */
	cmp	r0, #0
	msrmi	SPSR_cxsf, r1
	msrmi	ELR_hyp, r2
	mcrmi	p15, 4, r3, c5, c2, 0	@ HSR

	bx	lr
ENDPROC(__guest_exit)

arch/arm/kvm/hyp/hyp-entry.S 
guest_trap: // hyp_hvc
	load_vcpu r0			@ Load VCPU pointer to r0

#ifdef CONFIG_VFPv3
	@ Check for a VFP access
	lsr	r1, r1, #HSR_EC_SHIFT
	cmp	r1, #HSR_EC_CP_0_13
	beq	__vfp_guest_restore
#endif

	mov	r1, #ARM_EXCEPTION_HVC
	b	__guest_exit

hyp_irq:
	push	{r0, r1, r2}
	mov	r1, #ARM_EXCEPTION_IRQ
	load_vcpu r0			@ Load VCPU pointer to r0
	b	__guest_exit
arch/arm/kvm/hyp/hyp-entry.S
	.align 5
__kvm_hyp_vector:
	.global __kvm_hyp_vector

	@ Hyp-mode exception vector
	W(b)	hyp_reset
	W(b)	hyp_undef
	W(b)	hyp_svc
	W(b)	hyp_pabt
	W(b)	hyp_dabt
	W(b)	hyp_hvc
	W(b)	hyp_irq
	W(b)	hyp_fiq
.macro vect_br val, targ
ARM(	eor	sp, sp, #\val	)
ARM(	tst	sp, #7		)
ARM(	eorne	sp, sp, #\val	)

THUMB(	cmp	r1, #\val	)
THUMB(	popeq	{r1, r2}	)

	beq	\targ
.endm

	vect_br	0, hyp_fiq
	vect_br	1, hyp_irq
	vect_br	2, hyp_hvc
	vect_br	3, hyp_dabt
	vect_br	4, hyp_pabt
	vect_br	5, hyp_svc
	vect_br	6, hyp_undef
	vect_br	7, hyp_reset
#endif

.macro invalid_vector label, cause
	.align
\label:	mov	r0, #\cause
	b	__hyp_panic
.endm

	invalid_vector	hyp_reset	ARM_EXCEPTION_RESET
	invalid_vector	hyp_undef	ARM_EXCEPTION_UNDEFINED
	invalid_vector	hyp_svc		ARM_EXCEPTION_SOFTWARE
	invalid_vector	hyp_pabt	ARM_EXCEPTION_PREF_ABORT
	invalid_vector	hyp_fiq		ARM_EXCEPTION_FIQ

实现了以下三个,其他五个没实现
hyp_dabt
hyp_hvc
hyp_irq





上一篇:移植openssh到arm-linux


下一篇:常见的关键词选取方法有哪些?