162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2021 Google LLC
462306a36Sopenharmony_ci * Author: Fuad Tabba <tabba@google.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/kvm_host.h>
862306a36Sopenharmony_ci#include <linux/mm.h>
962306a36Sopenharmony_ci#include <nvhe/fixed_config.h>
1062306a36Sopenharmony_ci#include <nvhe/mem_protect.h>
1162306a36Sopenharmony_ci#include <nvhe/memory.h>
1262306a36Sopenharmony_ci#include <nvhe/pkvm.h>
1362306a36Sopenharmony_ci#include <nvhe/trap_handler.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/* Used by icache_is_vpipt(). */
1662306a36Sopenharmony_ciunsigned long __icache_flags;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/* Used by kvm_get_vttbr(). */
1962306a36Sopenharmony_ciunsigned int kvm_arm_vmid_bits;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * Set trap register values based on features in ID_AA64PFR0.
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_cistatic void pvm_init_traps_aa64pfr0(struct kvm_vcpu *vcpu)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	const u64 feature_ids = pvm_read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
2762306a36Sopenharmony_ci	u64 hcr_set = HCR_RW;
2862306a36Sopenharmony_ci	u64 hcr_clear = 0;
2962306a36Sopenharmony_ci	u64 cptr_set = 0;
3062306a36Sopenharmony_ci	u64 cptr_clear = 0;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	/* Protected KVM does not support AArch32 guests. */
3362306a36Sopenharmony_ci	BUILD_BUG_ON(FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_EL0),
3462306a36Sopenharmony_ci		PVM_ID_AA64PFR0_RESTRICT_UNSIGNED) != ID_AA64PFR0_EL1_ELx_64BIT_ONLY);
3562306a36Sopenharmony_ci	BUILD_BUG_ON(FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_EL1),
3662306a36Sopenharmony_ci		PVM_ID_AA64PFR0_RESTRICT_UNSIGNED) != ID_AA64PFR0_EL1_ELx_64BIT_ONLY);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	/*
3962306a36Sopenharmony_ci	 * Linux guests assume support for floating-point and Advanced SIMD. Do
4062306a36Sopenharmony_ci	 * not change the trapping behavior for these from the KVM default.
4162306a36Sopenharmony_ci	 */
4262306a36Sopenharmony_ci	BUILD_BUG_ON(!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_FP),
4362306a36Sopenharmony_ci				PVM_ID_AA64PFR0_ALLOW));
4462306a36Sopenharmony_ci	BUILD_BUG_ON(!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AdvSIMD),
4562306a36Sopenharmony_ci				PVM_ID_AA64PFR0_ALLOW));
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	if (has_hvhe())
4862306a36Sopenharmony_ci		hcr_set |= HCR_E2H;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	/* Trap RAS unless all current versions are supported */
5162306a36Sopenharmony_ci	if (FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_RAS), feature_ids) <
5262306a36Sopenharmony_ci	    ID_AA64PFR0_EL1_RAS_V1P1) {
5362306a36Sopenharmony_ci		hcr_set |= HCR_TERR | HCR_TEA;
5462306a36Sopenharmony_ci		hcr_clear |= HCR_FIEN;
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	/* Trap AMU */
5862306a36Sopenharmony_ci	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU), feature_ids)) {
5962306a36Sopenharmony_ci		hcr_clear |= HCR_AMVOFFEN;
6062306a36Sopenharmony_ci		cptr_set |= CPTR_EL2_TAM;
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	/* Trap SVE */
6462306a36Sopenharmony_ci	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE), feature_ids)) {
6562306a36Sopenharmony_ci		if (has_hvhe())
6662306a36Sopenharmony_ci			cptr_clear |= CPACR_EL1_ZEN_EL0EN | CPACR_EL1_ZEN_EL1EN;
6762306a36Sopenharmony_ci		else
6862306a36Sopenharmony_ci			cptr_set |= CPTR_EL2_TZ;
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	vcpu->arch.hcr_el2 |= hcr_set;
7262306a36Sopenharmony_ci	vcpu->arch.hcr_el2 &= ~hcr_clear;
7362306a36Sopenharmony_ci	vcpu->arch.cptr_el2 |= cptr_set;
7462306a36Sopenharmony_ci	vcpu->arch.cptr_el2 &= ~cptr_clear;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/*
7862306a36Sopenharmony_ci * Set trap register values based on features in ID_AA64PFR1.
7962306a36Sopenharmony_ci */
8062306a36Sopenharmony_cistatic void pvm_init_traps_aa64pfr1(struct kvm_vcpu *vcpu)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	const u64 feature_ids = pvm_read_id_reg(vcpu, SYS_ID_AA64PFR1_EL1);
8362306a36Sopenharmony_ci	u64 hcr_set = 0;
8462306a36Sopenharmony_ci	u64 hcr_clear = 0;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/* Memory Tagging: Trap and Treat as Untagged if not supported. */
8762306a36Sopenharmony_ci	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTE), feature_ids)) {
8862306a36Sopenharmony_ci		hcr_set |= HCR_TID5;
8962306a36Sopenharmony_ci		hcr_clear |= HCR_DCT | HCR_ATA;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	vcpu->arch.hcr_el2 |= hcr_set;
9362306a36Sopenharmony_ci	vcpu->arch.hcr_el2 &= ~hcr_clear;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/*
9762306a36Sopenharmony_ci * Set trap register values based on features in ID_AA64DFR0.
9862306a36Sopenharmony_ci */
9962306a36Sopenharmony_cistatic void pvm_init_traps_aa64dfr0(struct kvm_vcpu *vcpu)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	const u64 feature_ids = pvm_read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
10262306a36Sopenharmony_ci	u64 mdcr_set = 0;
10362306a36Sopenharmony_ci	u64 mdcr_clear = 0;
10462306a36Sopenharmony_ci	u64 cptr_set = 0;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* Trap/constrain PMU */
10762306a36Sopenharmony_ci	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer), feature_ids)) {
10862306a36Sopenharmony_ci		mdcr_set |= MDCR_EL2_TPM | MDCR_EL2_TPMCR;
10962306a36Sopenharmony_ci		mdcr_clear |= MDCR_EL2_HPME | MDCR_EL2_MTPME |
11062306a36Sopenharmony_ci			      MDCR_EL2_HPMN_MASK;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	/* Trap Debug */
11462306a36Sopenharmony_ci	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), feature_ids))
11562306a36Sopenharmony_ci		mdcr_set |= MDCR_EL2_TDRA | MDCR_EL2_TDA | MDCR_EL2_TDE;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/* Trap OS Double Lock */
11862306a36Sopenharmony_ci	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DoubleLock), feature_ids))
11962306a36Sopenharmony_ci		mdcr_set |= MDCR_EL2_TDOSA;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/* Trap SPE */
12262306a36Sopenharmony_ci	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer), feature_ids)) {
12362306a36Sopenharmony_ci		mdcr_set |= MDCR_EL2_TPMS;
12462306a36Sopenharmony_ci		mdcr_clear |= MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	/* Trap Trace Filter */
12862306a36Sopenharmony_ci	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_TraceFilt), feature_ids))
12962306a36Sopenharmony_ci		mdcr_set |= MDCR_EL2_TTRF;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* Trap Trace */
13262306a36Sopenharmony_ci	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_TraceVer), feature_ids)) {
13362306a36Sopenharmony_ci		if (has_hvhe())
13462306a36Sopenharmony_ci			cptr_set |= CPACR_EL1_TTA;
13562306a36Sopenharmony_ci		else
13662306a36Sopenharmony_ci			cptr_set |= CPTR_EL2_TTA;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	vcpu->arch.mdcr_el2 |= mdcr_set;
14062306a36Sopenharmony_ci	vcpu->arch.mdcr_el2 &= ~mdcr_clear;
14162306a36Sopenharmony_ci	vcpu->arch.cptr_el2 |= cptr_set;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci/*
14562306a36Sopenharmony_ci * Set trap register values based on features in ID_AA64MMFR0.
14662306a36Sopenharmony_ci */
14762306a36Sopenharmony_cistatic void pvm_init_traps_aa64mmfr0(struct kvm_vcpu *vcpu)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	const u64 feature_ids = pvm_read_id_reg(vcpu, SYS_ID_AA64MMFR0_EL1);
15062306a36Sopenharmony_ci	u64 mdcr_set = 0;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	/* Trap Debug Communications Channel registers */
15362306a36Sopenharmony_ci	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_FGT), feature_ids))
15462306a36Sopenharmony_ci		mdcr_set |= MDCR_EL2_TDCC;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	vcpu->arch.mdcr_el2 |= mdcr_set;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/*
16062306a36Sopenharmony_ci * Set trap register values based on features in ID_AA64MMFR1.
16162306a36Sopenharmony_ci */
16262306a36Sopenharmony_cistatic void pvm_init_traps_aa64mmfr1(struct kvm_vcpu *vcpu)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	const u64 feature_ids = pvm_read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
16562306a36Sopenharmony_ci	u64 hcr_set = 0;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* Trap LOR */
16862306a36Sopenharmony_ci	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR1_EL1_LO), feature_ids))
16962306a36Sopenharmony_ci		hcr_set |= HCR_TLOR;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	vcpu->arch.hcr_el2 |= hcr_set;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci/*
17562306a36Sopenharmony_ci * Set baseline trap register values.
17662306a36Sopenharmony_ci */
17762306a36Sopenharmony_cistatic void pvm_init_trap_regs(struct kvm_vcpu *vcpu)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	const u64 hcr_trap_feat_regs = HCR_TID3;
18062306a36Sopenharmony_ci	const u64 hcr_trap_impdef = HCR_TACR | HCR_TIDCP | HCR_TID1;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/*
18362306a36Sopenharmony_ci	 * Always trap:
18462306a36Sopenharmony_ci	 * - Feature id registers: to control features exposed to guests
18562306a36Sopenharmony_ci	 * - Implementation-defined features
18662306a36Sopenharmony_ci	 */
18762306a36Sopenharmony_ci	vcpu->arch.hcr_el2 |= hcr_trap_feat_regs | hcr_trap_impdef;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* Clear res0 and set res1 bits to trap potential new features. */
19062306a36Sopenharmony_ci	vcpu->arch.hcr_el2 &= ~(HCR_RES0);
19162306a36Sopenharmony_ci	vcpu->arch.mdcr_el2 &= ~(MDCR_EL2_RES0);
19262306a36Sopenharmony_ci	if (!has_hvhe()) {
19362306a36Sopenharmony_ci		vcpu->arch.cptr_el2 |= CPTR_NVHE_EL2_RES1;
19462306a36Sopenharmony_ci		vcpu->arch.cptr_el2 &= ~(CPTR_NVHE_EL2_RES0);
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci/*
19962306a36Sopenharmony_ci * Initialize trap register values for protected VMs.
20062306a36Sopenharmony_ci */
20162306a36Sopenharmony_civoid __pkvm_vcpu_init_traps(struct kvm_vcpu *vcpu)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	pvm_init_trap_regs(vcpu);
20462306a36Sopenharmony_ci	pvm_init_traps_aa64pfr0(vcpu);
20562306a36Sopenharmony_ci	pvm_init_traps_aa64pfr1(vcpu);
20662306a36Sopenharmony_ci	pvm_init_traps_aa64dfr0(vcpu);
20762306a36Sopenharmony_ci	pvm_init_traps_aa64mmfr0(vcpu);
20862306a36Sopenharmony_ci	pvm_init_traps_aa64mmfr1(vcpu);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci/*
21262306a36Sopenharmony_ci * Start the VM table handle at the offset defined instead of at 0.
21362306a36Sopenharmony_ci * Mainly for sanity checking and debugging.
21462306a36Sopenharmony_ci */
21562306a36Sopenharmony_ci#define HANDLE_OFFSET 0x1000
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic unsigned int vm_handle_to_idx(pkvm_handle_t handle)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	return handle - HANDLE_OFFSET;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic pkvm_handle_t idx_to_vm_handle(unsigned int idx)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	return idx + HANDLE_OFFSET;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci/*
22862306a36Sopenharmony_ci * Spinlock for protecting state related to the VM table. Protects writes
22962306a36Sopenharmony_ci * to 'vm_table' and 'nr_table_entries' as well as reads and writes to
23062306a36Sopenharmony_ci * 'last_hyp_vcpu_lookup'.
23162306a36Sopenharmony_ci */
23262306a36Sopenharmony_cistatic DEFINE_HYP_SPINLOCK(vm_table_lock);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/*
23562306a36Sopenharmony_ci * The table of VM entries for protected VMs in hyp.
23662306a36Sopenharmony_ci * Allocated at hyp initialization and setup.
23762306a36Sopenharmony_ci */
23862306a36Sopenharmony_cistatic struct pkvm_hyp_vm **vm_table;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_civoid pkvm_hyp_vm_table_init(void *tbl)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	WARN_ON(vm_table);
24362306a36Sopenharmony_ci	vm_table = tbl;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci/*
24762306a36Sopenharmony_ci * Return the hyp vm structure corresponding to the handle.
24862306a36Sopenharmony_ci */
24962306a36Sopenharmony_cistatic struct pkvm_hyp_vm *get_vm_by_handle(pkvm_handle_t handle)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	unsigned int idx = vm_handle_to_idx(handle);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	if (unlikely(idx >= KVM_MAX_PVMS))
25462306a36Sopenharmony_ci		return NULL;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return vm_table[idx];
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistruct pkvm_hyp_vcpu *pkvm_load_hyp_vcpu(pkvm_handle_t handle,
26062306a36Sopenharmony_ci					 unsigned int vcpu_idx)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct pkvm_hyp_vcpu *hyp_vcpu = NULL;
26362306a36Sopenharmony_ci	struct pkvm_hyp_vm *hyp_vm;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	hyp_spin_lock(&vm_table_lock);
26662306a36Sopenharmony_ci	hyp_vm = get_vm_by_handle(handle);
26762306a36Sopenharmony_ci	if (!hyp_vm || hyp_vm->nr_vcpus <= vcpu_idx)
26862306a36Sopenharmony_ci		goto unlock;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	hyp_vcpu = hyp_vm->vcpus[vcpu_idx];
27162306a36Sopenharmony_ci	hyp_page_ref_inc(hyp_virt_to_page(hyp_vm));
27262306a36Sopenharmony_ciunlock:
27362306a36Sopenharmony_ci	hyp_spin_unlock(&vm_table_lock);
27462306a36Sopenharmony_ci	return hyp_vcpu;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_civoid pkvm_put_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	struct pkvm_hyp_vm *hyp_vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	hyp_spin_lock(&vm_table_lock);
28262306a36Sopenharmony_ci	hyp_page_ref_dec(hyp_virt_to_page(hyp_vm));
28362306a36Sopenharmony_ci	hyp_spin_unlock(&vm_table_lock);
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic void unpin_host_vcpu(struct kvm_vcpu *host_vcpu)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	if (host_vcpu)
28962306a36Sopenharmony_ci		hyp_unpin_shared_mem(host_vcpu, host_vcpu + 1);
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic void unpin_host_vcpus(struct pkvm_hyp_vcpu *hyp_vcpus[],
29362306a36Sopenharmony_ci			     unsigned int nr_vcpus)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	int i;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	for (i = 0; i < nr_vcpus; i++)
29862306a36Sopenharmony_ci		unpin_host_vcpu(hyp_vcpus[i]->host_vcpu);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic void init_pkvm_hyp_vm(struct kvm *host_kvm, struct pkvm_hyp_vm *hyp_vm,
30262306a36Sopenharmony_ci			     unsigned int nr_vcpus)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	hyp_vm->host_kvm = host_kvm;
30562306a36Sopenharmony_ci	hyp_vm->kvm.created_vcpus = nr_vcpus;
30662306a36Sopenharmony_ci	hyp_vm->kvm.arch.vtcr = host_mmu.arch.vtcr;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic int init_pkvm_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu,
31062306a36Sopenharmony_ci			      struct pkvm_hyp_vm *hyp_vm,
31162306a36Sopenharmony_ci			      struct kvm_vcpu *host_vcpu,
31262306a36Sopenharmony_ci			      unsigned int vcpu_idx)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	int ret = 0;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	if (hyp_pin_shared_mem(host_vcpu, host_vcpu + 1))
31762306a36Sopenharmony_ci		return -EBUSY;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (host_vcpu->vcpu_idx != vcpu_idx) {
32062306a36Sopenharmony_ci		ret = -EINVAL;
32162306a36Sopenharmony_ci		goto done;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	hyp_vcpu->host_vcpu = host_vcpu;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	hyp_vcpu->vcpu.kvm = &hyp_vm->kvm;
32762306a36Sopenharmony_ci	hyp_vcpu->vcpu.vcpu_id = READ_ONCE(host_vcpu->vcpu_id);
32862306a36Sopenharmony_ci	hyp_vcpu->vcpu.vcpu_idx = vcpu_idx;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	hyp_vcpu->vcpu.arch.hw_mmu = &hyp_vm->kvm.arch.mmu;
33162306a36Sopenharmony_ci	hyp_vcpu->vcpu.arch.cflags = READ_ONCE(host_vcpu->arch.cflags);
33262306a36Sopenharmony_cidone:
33362306a36Sopenharmony_ci	if (ret)
33462306a36Sopenharmony_ci		unpin_host_vcpu(host_vcpu);
33562306a36Sopenharmony_ci	return ret;
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic int find_free_vm_table_entry(struct kvm *host_kvm)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	int i;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	for (i = 0; i < KVM_MAX_PVMS; ++i) {
34362306a36Sopenharmony_ci		if (!vm_table[i])
34462306a36Sopenharmony_ci			return i;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	return -ENOMEM;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci/*
35162306a36Sopenharmony_ci * Allocate a VM table entry and insert a pointer to the new vm.
35262306a36Sopenharmony_ci *
35362306a36Sopenharmony_ci * Return a unique handle to the protected VM on success,
35462306a36Sopenharmony_ci * negative error code on failure.
35562306a36Sopenharmony_ci */
35662306a36Sopenharmony_cistatic pkvm_handle_t insert_vm_table_entry(struct kvm *host_kvm,
35762306a36Sopenharmony_ci					   struct pkvm_hyp_vm *hyp_vm)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct kvm_s2_mmu *mmu = &hyp_vm->kvm.arch.mmu;
36062306a36Sopenharmony_ci	int idx;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	hyp_assert_lock_held(&vm_table_lock);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/*
36562306a36Sopenharmony_ci	 * Initializing protected state might have failed, yet a malicious
36662306a36Sopenharmony_ci	 * host could trigger this function. Thus, ensure that 'vm_table'
36762306a36Sopenharmony_ci	 * exists.
36862306a36Sopenharmony_ci	 */
36962306a36Sopenharmony_ci	if (unlikely(!vm_table))
37062306a36Sopenharmony_ci		return -EINVAL;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	idx = find_free_vm_table_entry(host_kvm);
37362306a36Sopenharmony_ci	if (idx < 0)
37462306a36Sopenharmony_ci		return idx;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	hyp_vm->kvm.arch.pkvm.handle = idx_to_vm_handle(idx);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	/* VMID 0 is reserved for the host */
37962306a36Sopenharmony_ci	atomic64_set(&mmu->vmid.id, idx + 1);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	mmu->arch = &hyp_vm->kvm.arch;
38262306a36Sopenharmony_ci	mmu->pgt = &hyp_vm->pgt;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	vm_table[idx] = hyp_vm;
38562306a36Sopenharmony_ci	return hyp_vm->kvm.arch.pkvm.handle;
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci/*
38962306a36Sopenharmony_ci * Deallocate and remove the VM table entry corresponding to the handle.
39062306a36Sopenharmony_ci */
39162306a36Sopenharmony_cistatic void remove_vm_table_entry(pkvm_handle_t handle)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	hyp_assert_lock_held(&vm_table_lock);
39462306a36Sopenharmony_ci	vm_table[vm_handle_to_idx(handle)] = NULL;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic size_t pkvm_get_hyp_vm_size(unsigned int nr_vcpus)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	return size_add(sizeof(struct pkvm_hyp_vm),
40062306a36Sopenharmony_ci		size_mul(sizeof(struct pkvm_hyp_vcpu *), nr_vcpus));
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic void *map_donated_memory_noclear(unsigned long host_va, size_t size)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	void *va = (void *)kern_hyp_va(host_va);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if (!PAGE_ALIGNED(va))
40862306a36Sopenharmony_ci		return NULL;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	if (__pkvm_host_donate_hyp(hyp_virt_to_pfn(va),
41162306a36Sopenharmony_ci				   PAGE_ALIGN(size) >> PAGE_SHIFT))
41262306a36Sopenharmony_ci		return NULL;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	return va;
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic void *map_donated_memory(unsigned long host_va, size_t size)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	void *va = map_donated_memory_noclear(host_va, size);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	if (va)
42262306a36Sopenharmony_ci		memset(va, 0, size);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	return va;
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic void __unmap_donated_memory(void *va, size_t size)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	WARN_ON(__pkvm_hyp_donate_host(hyp_virt_to_pfn(va),
43062306a36Sopenharmony_ci				       PAGE_ALIGN(size) >> PAGE_SHIFT));
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic void unmap_donated_memory(void *va, size_t size)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	if (!va)
43662306a36Sopenharmony_ci		return;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	memset(va, 0, size);
43962306a36Sopenharmony_ci	__unmap_donated_memory(va, size);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic void unmap_donated_memory_noclear(void *va, size_t size)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	if (!va)
44562306a36Sopenharmony_ci		return;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	__unmap_donated_memory(va, size);
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci/*
45162306a36Sopenharmony_ci * Initialize the hypervisor copy of the protected VM state using the
45262306a36Sopenharmony_ci * memory donated by the host.
45362306a36Sopenharmony_ci *
45462306a36Sopenharmony_ci * Unmaps the donated memory from the host at stage 2.
45562306a36Sopenharmony_ci *
45662306a36Sopenharmony_ci * host_kvm: A pointer to the host's struct kvm.
45762306a36Sopenharmony_ci * vm_hva: The host va of the area being donated for the VM state.
45862306a36Sopenharmony_ci *	   Must be page aligned.
45962306a36Sopenharmony_ci * pgd_hva: The host va of the area being donated for the stage-2 PGD for
46062306a36Sopenharmony_ci *	    the VM. Must be page aligned. Its size is implied by the VM's
46162306a36Sopenharmony_ci *	    VTCR.
46262306a36Sopenharmony_ci *
46362306a36Sopenharmony_ci * Return a unique handle to the protected VM on success,
46462306a36Sopenharmony_ci * negative error code on failure.
46562306a36Sopenharmony_ci */
46662306a36Sopenharmony_ciint __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva,
46762306a36Sopenharmony_ci		   unsigned long pgd_hva)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	struct pkvm_hyp_vm *hyp_vm = NULL;
47062306a36Sopenharmony_ci	size_t vm_size, pgd_size;
47162306a36Sopenharmony_ci	unsigned int nr_vcpus;
47262306a36Sopenharmony_ci	void *pgd = NULL;
47362306a36Sopenharmony_ci	int ret;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	ret = hyp_pin_shared_mem(host_kvm, host_kvm + 1);
47662306a36Sopenharmony_ci	if (ret)
47762306a36Sopenharmony_ci		return ret;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	nr_vcpus = READ_ONCE(host_kvm->created_vcpus);
48062306a36Sopenharmony_ci	if (nr_vcpus < 1) {
48162306a36Sopenharmony_ci		ret = -EINVAL;
48262306a36Sopenharmony_ci		goto err_unpin_kvm;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	vm_size = pkvm_get_hyp_vm_size(nr_vcpus);
48662306a36Sopenharmony_ci	pgd_size = kvm_pgtable_stage2_pgd_size(host_mmu.arch.vtcr);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	ret = -ENOMEM;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	hyp_vm = map_donated_memory(vm_hva, vm_size);
49162306a36Sopenharmony_ci	if (!hyp_vm)
49262306a36Sopenharmony_ci		goto err_remove_mappings;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	pgd = map_donated_memory_noclear(pgd_hva, pgd_size);
49562306a36Sopenharmony_ci	if (!pgd)
49662306a36Sopenharmony_ci		goto err_remove_mappings;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	init_pkvm_hyp_vm(host_kvm, hyp_vm, nr_vcpus);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	hyp_spin_lock(&vm_table_lock);
50162306a36Sopenharmony_ci	ret = insert_vm_table_entry(host_kvm, hyp_vm);
50262306a36Sopenharmony_ci	if (ret < 0)
50362306a36Sopenharmony_ci		goto err_unlock;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	ret = kvm_guest_prepare_stage2(hyp_vm, pgd);
50662306a36Sopenharmony_ci	if (ret)
50762306a36Sopenharmony_ci		goto err_remove_vm_table_entry;
50862306a36Sopenharmony_ci	hyp_spin_unlock(&vm_table_lock);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	return hyp_vm->kvm.arch.pkvm.handle;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cierr_remove_vm_table_entry:
51362306a36Sopenharmony_ci	remove_vm_table_entry(hyp_vm->kvm.arch.pkvm.handle);
51462306a36Sopenharmony_cierr_unlock:
51562306a36Sopenharmony_ci	hyp_spin_unlock(&vm_table_lock);
51662306a36Sopenharmony_cierr_remove_mappings:
51762306a36Sopenharmony_ci	unmap_donated_memory(hyp_vm, vm_size);
51862306a36Sopenharmony_ci	unmap_donated_memory(pgd, pgd_size);
51962306a36Sopenharmony_cierr_unpin_kvm:
52062306a36Sopenharmony_ci	hyp_unpin_shared_mem(host_kvm, host_kvm + 1);
52162306a36Sopenharmony_ci	return ret;
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci/*
52562306a36Sopenharmony_ci * Initialize the hypervisor copy of the protected vCPU state using the
52662306a36Sopenharmony_ci * memory donated by the host.
52762306a36Sopenharmony_ci *
52862306a36Sopenharmony_ci * handle: The handle for the protected vm.
52962306a36Sopenharmony_ci * host_vcpu: A pointer to the corresponding host vcpu.
53062306a36Sopenharmony_ci * vcpu_hva: The host va of the area being donated for the vcpu state.
53162306a36Sopenharmony_ci *	     Must be page aligned. The size of the area must be equal to
53262306a36Sopenharmony_ci *	     the page-aligned size of 'struct pkvm_hyp_vcpu'.
53362306a36Sopenharmony_ci * Return 0 on success, negative error code on failure.
53462306a36Sopenharmony_ci */
53562306a36Sopenharmony_ciint __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu,
53662306a36Sopenharmony_ci		     unsigned long vcpu_hva)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct pkvm_hyp_vcpu *hyp_vcpu;
53962306a36Sopenharmony_ci	struct pkvm_hyp_vm *hyp_vm;
54062306a36Sopenharmony_ci	unsigned int idx;
54162306a36Sopenharmony_ci	int ret;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	hyp_vcpu = map_donated_memory(vcpu_hva, sizeof(*hyp_vcpu));
54462306a36Sopenharmony_ci	if (!hyp_vcpu)
54562306a36Sopenharmony_ci		return -ENOMEM;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	hyp_spin_lock(&vm_table_lock);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	hyp_vm = get_vm_by_handle(handle);
55062306a36Sopenharmony_ci	if (!hyp_vm) {
55162306a36Sopenharmony_ci		ret = -ENOENT;
55262306a36Sopenharmony_ci		goto unlock;
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	idx = hyp_vm->nr_vcpus;
55662306a36Sopenharmony_ci	if (idx >= hyp_vm->kvm.created_vcpus) {
55762306a36Sopenharmony_ci		ret = -EINVAL;
55862306a36Sopenharmony_ci		goto unlock;
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	ret = init_pkvm_hyp_vcpu(hyp_vcpu, hyp_vm, host_vcpu, idx);
56262306a36Sopenharmony_ci	if (ret)
56362306a36Sopenharmony_ci		goto unlock;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	hyp_vm->vcpus[idx] = hyp_vcpu;
56662306a36Sopenharmony_ci	hyp_vm->nr_vcpus++;
56762306a36Sopenharmony_ciunlock:
56862306a36Sopenharmony_ci	hyp_spin_unlock(&vm_table_lock);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if (ret)
57162306a36Sopenharmony_ci		unmap_donated_memory(hyp_vcpu, sizeof(*hyp_vcpu));
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	return ret;
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cistatic void
57762306a36Sopenharmony_citeardown_donated_memory(struct kvm_hyp_memcache *mc, void *addr, size_t size)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	size = PAGE_ALIGN(size);
58062306a36Sopenharmony_ci	memset(addr, 0, size);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	for (void *start = addr; start < addr + size; start += PAGE_SIZE)
58362306a36Sopenharmony_ci		push_hyp_memcache(mc, start, hyp_virt_to_phys);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	unmap_donated_memory_noclear(addr, size);
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ciint __pkvm_teardown_vm(pkvm_handle_t handle)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	struct kvm_hyp_memcache *mc;
59162306a36Sopenharmony_ci	struct pkvm_hyp_vm *hyp_vm;
59262306a36Sopenharmony_ci	struct kvm *host_kvm;
59362306a36Sopenharmony_ci	unsigned int idx;
59462306a36Sopenharmony_ci	size_t vm_size;
59562306a36Sopenharmony_ci	int err;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	hyp_spin_lock(&vm_table_lock);
59862306a36Sopenharmony_ci	hyp_vm = get_vm_by_handle(handle);
59962306a36Sopenharmony_ci	if (!hyp_vm) {
60062306a36Sopenharmony_ci		err = -ENOENT;
60162306a36Sopenharmony_ci		goto err_unlock;
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	if (WARN_ON(hyp_page_count(hyp_vm))) {
60562306a36Sopenharmony_ci		err = -EBUSY;
60662306a36Sopenharmony_ci		goto err_unlock;
60762306a36Sopenharmony_ci	}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	host_kvm = hyp_vm->host_kvm;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	/* Ensure the VMID is clean before it can be reallocated */
61262306a36Sopenharmony_ci	__kvm_tlb_flush_vmid(&hyp_vm->kvm.arch.mmu);
61362306a36Sopenharmony_ci	remove_vm_table_entry(handle);
61462306a36Sopenharmony_ci	hyp_spin_unlock(&vm_table_lock);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	/* Reclaim guest pages (including page-table pages) */
61762306a36Sopenharmony_ci	mc = &host_kvm->arch.pkvm.teardown_mc;
61862306a36Sopenharmony_ci	reclaim_guest_pages(hyp_vm, mc);
61962306a36Sopenharmony_ci	unpin_host_vcpus(hyp_vm->vcpus, hyp_vm->nr_vcpus);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	/* Push the metadata pages to the teardown memcache */
62262306a36Sopenharmony_ci	for (idx = 0; idx < hyp_vm->nr_vcpus; ++idx) {
62362306a36Sopenharmony_ci		struct pkvm_hyp_vcpu *hyp_vcpu = hyp_vm->vcpus[idx];
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci		teardown_donated_memory(mc, hyp_vcpu, sizeof(*hyp_vcpu));
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	vm_size = pkvm_get_hyp_vm_size(hyp_vm->kvm.created_vcpus);
62962306a36Sopenharmony_ci	teardown_donated_memory(mc, hyp_vm, vm_size);
63062306a36Sopenharmony_ci	hyp_unpin_shared_mem(host_kvm, host_kvm + 1);
63162306a36Sopenharmony_ci	return 0;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_cierr_unlock:
63462306a36Sopenharmony_ci	hyp_spin_unlock(&vm_table_lock);
63562306a36Sopenharmony_ci	return err;
63662306a36Sopenharmony_ci}
637