arch/arm64/kvm/hyp/include/hyp/switch.h

Source file repositories/reference/linux-study-clean/arch/arm64/kvm/hyp/include/hyp/switch.h

File Facts

System
Linux kernel
Corpus path
arch/arm64/kvm/hyp/include/hyp/switch.h
Extension
.h
Size
27030 bytes
Lines
989
Domain
Architecture Layer
Bucket
arch/arm64
Inferred role
Architecture Layer: implementation source
Status
source implementation candidate

Why This File Exists

CPU and platform-specific kernel glue: boot entry, traps, syscall entry, interrupts, page tables, context switch, and low-level barriers.

Dependency Surface

Detected Declarations

Annotated Snippet

struct kvm_exception_table_entry {
	int insn, fixup;
};

extern struct kvm_exception_table_entry __start___kvm_ex_table;
extern struct kvm_exception_table_entry __stop___kvm_ex_table;

/* Save the 32-bit only FPSIMD system register state */
static inline void __fpsimd_save_fpexc32(struct kvm_vcpu *vcpu)
{
	if (!vcpu_el1_is_32bit(vcpu))
		return;

	__vcpu_assign_sys_reg(vcpu, FPEXC32_EL2, read_sysreg(fpexc32_el2));
}

static inline void __activate_traps_fpsimd32(struct kvm_vcpu *vcpu)
{
	/*
	 * We are about to set CPTR_EL2.TFP to trap all floating point
	 * register accesses to EL2, however, the ARM ARM clearly states that
	 * traps are only taken to EL2 if the operation would not otherwise
	 * trap to EL1.  Therefore, always make sure that for 32-bit guests,
	 * we set FPEXC.EN to prevent traps to EL1, when setting the TFP bit.
	 * If FP/ASIMD is not implemented, FPEXC is UNDEFINED and any access to
	 * it will cause an exception.
	 */
	if (vcpu_el1_is_32bit(vcpu) && system_supports_fpsimd())
		write_sysreg(1 << 30, fpexc32_el2);
}

static inline void __activate_cptr_traps_nvhe(struct kvm_vcpu *vcpu)
{
	u64 val = CPTR_NVHE_EL2_RES1 | CPTR_EL2_TAM | CPTR_EL2_TTA;

	/*
	 * Always trap SME since it's not supported in KVM.
	 * TSM is RES1 if SME isn't implemented.
	 */
	val |= CPTR_EL2_TSM;

	if (!vcpu_has_sve(vcpu) || !guest_owns_fp_regs())
		val |= CPTR_EL2_TZ;

	if (!guest_owns_fp_regs())
		val |= CPTR_EL2_TFP;

	write_sysreg(val, cptr_el2);
}

static inline void __activate_cptr_traps_vhe(struct kvm_vcpu *vcpu)
{
	/*
	 * With VHE (HCR.E2H == 1), accesses to CPACR_EL1 are routed to
	 * CPTR_EL2. In general, CPACR_EL1 has the same layout as CPTR_EL2,
	 * except for some missing controls, such as TAM.
	 * In this case, CPTR_EL2.TAM has the same position with or without
	 * VHE (HCR.E2H == 1) which allows us to use here the CPTR_EL2.TAM
	 * shift value for trapping the AMU accesses.
	 */
	u64 val = CPTR_EL2_TAM | CPACR_EL1_TTA;
	u64 cptr;

	if (guest_owns_fp_regs()) {
		val |= CPACR_EL1_FPEN;
		if (vcpu_has_sve(vcpu))
			val |= CPACR_EL1_ZEN;
	}

	if (!vcpu_has_nv(vcpu))
		goto write;

	/*
	 * The architecture is a bit crap (what a surprise): an EL2 guest
	 * writing to CPTR_EL2 via CPACR_EL1 can't set any of TCPAC or TTA,
	 * as they are RES0 in the guest's view. To work around it, trap the
	 * sucker using the very same bit it can't set...
	 */
	if (vcpu_el2_e2h_is_set(vcpu) && is_hyp_ctxt(vcpu))
		val |= CPTR_EL2_TCPAC;

	/*
	 * Layer the guest hypervisor's trap configuration on top of our own if
	 * we're in a nested context.
	 */
	if (is_hyp_ctxt(vcpu))
		goto write;

	cptr = vcpu_sanitised_cptr_el2(vcpu);

Annotation

Implementation Notes