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