arch/arm64/kvm/hyp/nvhe/tlb.c

Source file repositories/reference/linux-study-clean/arch/arm64/kvm/hyp/nvhe/tlb.c

File Facts

System
Linux kernel
Corpus path
arch/arm64/kvm/hyp/nvhe/tlb.c
Extension
.c
Size
6279 bytes
Lines
269
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 tlb_inv_context {
	struct kvm_s2_mmu	*mmu;
	u64			tcr;
	u64			sctlr;
};

static void enter_vmid_context(struct kvm_s2_mmu *mmu,
			       struct tlb_inv_context *cxt,
			       bool nsh)
{
	struct kvm_s2_mmu *host_s2_mmu = &host_mmu.arch.mmu;
	struct kvm_cpu_context *host_ctxt;
	struct kvm_vcpu *vcpu;

	host_ctxt = &this_cpu_ptr(&kvm_host_data)->host_ctxt;
	vcpu = host_ctxt->__hyp_running_vcpu;
	cxt->mmu = NULL;

	/*
	 * We have two requirements:
	 *
	 * - ensure that the page table updates are visible to all
	 *   CPUs, for which a dsb(DOMAIN-st) is what we need, DOMAIN
	 *   being either ish or nsh, depending on the invalidation
	 *   type.
	 *
	 * - complete any speculative page table walk started before
	 *   we trapped to EL2 so that we can mess with the MM
	 *   registers out of context, for which dsb(nsh) is enough
	 *
	 * The composition of these two barriers is a dsb(DOMAIN), and
	 * the 'nsh' parameter tracks the distinction between
	 * Inner-Shareable and Non-Shareable, as specified by the
	 * callers.
	 */
	if (nsh)
		dsb(nsh);
	else
		dsb(ish);

	/*
	 * If we're already in the desired context, then there's nothing to do.
	 */
	if (vcpu) {
		/*
		 * We're in guest context. However, for this to work, this needs
		 * to be called from within __kvm_vcpu_run(), which ensures that
		 * __hyp_running_vcpu is set to the current guest vcpu.
		 */
		if (mmu == vcpu->arch.hw_mmu || WARN_ON(mmu != host_s2_mmu))
			return;

		cxt->mmu = vcpu->arch.hw_mmu;
	} else {
		/* We're in host context. */
		if (mmu == host_s2_mmu)
			return;

		cxt->mmu = host_s2_mmu;
	}

	if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
		u64 val;

		/*
		 * For CPUs that are affected by ARM 1319367, we need to
		 * avoid a Stage-1 walk with the old VMID while we have
		 * the new VMID set in the VTTBR in order to invalidate TLBs.
		 * We're guaranteed that the host S1 MMU is enabled, so
		 * we can simply set the EPD bits to avoid any further
		 * TLB fill. For guests, we ensure that the S1 MMU is
		 * temporarily enabled in the next context.
		 */
		val = cxt->tcr = read_sysreg_el1(SYS_TCR);
		val |= TCR_EPD1_MASK | TCR_EPD0_MASK;
		write_sysreg_el1(val, SYS_TCR);
		isb();

		if (vcpu) {
			val = cxt->sctlr = read_sysreg_el1(SYS_SCTLR);
			if (!(val & SCTLR_ELx_M)) {
				val |= SCTLR_ELx_M;
				write_sysreg_el1(val, SYS_SCTLR);
				isb();
			}
		} else {
			/* The host S1 MMU is always enabled. */
			cxt->sctlr = SCTLR_ELx_M;
		}
	}

Annotation

Implementation Notes