186 lines
5.2 KiB
C
186 lines
5.2 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (C) 2021 Google LLC
|
|
* Author: Fuad Tabba <tabba@google.com>
|
|
*/
|
|
|
|
#include <linux/kvm_host.h>
|
|
#include <linux/mm.h>
|
|
#include <nvhe/fixed_config.h>
|
|
#include <nvhe/trap_handler.h>
|
|
|
|
/*
|
|
* Set trap register values based on features in ID_AA64PFR0.
|
|
*/
|
|
static void pvm_init_traps_aa64pfr0(struct kvm_vcpu *vcpu)
|
|
{
|
|
const u64 feature_ids = pvm_read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
|
|
u64 hcr_set = HCR_RW;
|
|
u64 hcr_clear = 0;
|
|
u64 cptr_set = 0;
|
|
|
|
/* Protected KVM does not support AArch32 guests. */
|
|
BUILD_BUG_ON(FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_EL0),
|
|
PVM_ID_AA64PFR0_RESTRICT_UNSIGNED) != ID_AA64PFR0_EL1_ELx_64BIT_ONLY);
|
|
BUILD_BUG_ON(FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_EL1),
|
|
PVM_ID_AA64PFR0_RESTRICT_UNSIGNED) != ID_AA64PFR0_EL1_ELx_64BIT_ONLY);
|
|
|
|
/*
|
|
* Linux guests assume support for floating-point and Advanced SIMD. Do
|
|
* not change the trapping behavior for these from the KVM default.
|
|
*/
|
|
BUILD_BUG_ON(!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_FP),
|
|
PVM_ID_AA64PFR0_ALLOW));
|
|
BUILD_BUG_ON(!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AdvSIMD),
|
|
PVM_ID_AA64PFR0_ALLOW));
|
|
|
|
/* Trap RAS unless all current versions are supported */
|
|
if (FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_RAS), feature_ids) <
|
|
ID_AA64PFR0_EL1_RAS_V1P1) {
|
|
hcr_set |= HCR_TERR | HCR_TEA;
|
|
hcr_clear |= HCR_FIEN;
|
|
}
|
|
|
|
/* Trap AMU */
|
|
if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_AMU), feature_ids)) {
|
|
hcr_clear |= HCR_AMVOFFEN;
|
|
cptr_set |= CPTR_EL2_TAM;
|
|
}
|
|
|
|
/* Trap SVE */
|
|
if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1_SVE), feature_ids))
|
|
cptr_set |= CPTR_EL2_TZ;
|
|
|
|
vcpu->arch.hcr_el2 |= hcr_set;
|
|
vcpu->arch.hcr_el2 &= ~hcr_clear;
|
|
vcpu->arch.cptr_el2 |= cptr_set;
|
|
}
|
|
|
|
/*
|
|
* Set trap register values based on features in ID_AA64PFR1.
|
|
*/
|
|
static void pvm_init_traps_aa64pfr1(struct kvm_vcpu *vcpu)
|
|
{
|
|
const u64 feature_ids = pvm_read_id_reg(vcpu, SYS_ID_AA64PFR1_EL1);
|
|
u64 hcr_set = 0;
|
|
u64 hcr_clear = 0;
|
|
|
|
/* Memory Tagging: Trap and Treat as Untagged if not supported. */
|
|
if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTE), feature_ids)) {
|
|
hcr_set |= HCR_TID5;
|
|
hcr_clear |= HCR_DCT | HCR_ATA;
|
|
}
|
|
|
|
vcpu->arch.hcr_el2 |= hcr_set;
|
|
vcpu->arch.hcr_el2 &= ~hcr_clear;
|
|
}
|
|
|
|
/*
|
|
* Set trap register values based on features in ID_AA64DFR0.
|
|
*/
|
|
static void pvm_init_traps_aa64dfr0(struct kvm_vcpu *vcpu)
|
|
{
|
|
const u64 feature_ids = pvm_read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
|
|
u64 mdcr_set = 0;
|
|
u64 mdcr_clear = 0;
|
|
u64 cptr_set = 0;
|
|
|
|
/* Trap/constrain PMU */
|
|
if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer), feature_ids)) {
|
|
mdcr_set |= MDCR_EL2_TPM | MDCR_EL2_TPMCR;
|
|
mdcr_clear |= MDCR_EL2_HPME | MDCR_EL2_MTPME |
|
|
MDCR_EL2_HPMN_MASK;
|
|
}
|
|
|
|
/* Trap Debug */
|
|
if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DebugVer), feature_ids))
|
|
mdcr_set |= MDCR_EL2_TDRA | MDCR_EL2_TDA | MDCR_EL2_TDE;
|
|
|
|
/* Trap OS Double Lock */
|
|
if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_DoubleLock), feature_ids))
|
|
mdcr_set |= MDCR_EL2_TDOSA;
|
|
|
|
/* Trap SPE */
|
|
if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMSVer), feature_ids)) {
|
|
mdcr_set |= MDCR_EL2_TPMS;
|
|
mdcr_clear |= MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT;
|
|
}
|
|
|
|
/* Trap Trace Filter */
|
|
if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_TraceFilt), feature_ids))
|
|
mdcr_set |= MDCR_EL2_TTRF;
|
|
|
|
/* Trap Trace */
|
|
if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_TraceVer), feature_ids))
|
|
cptr_set |= CPTR_EL2_TTA;
|
|
|
|
vcpu->arch.mdcr_el2 |= mdcr_set;
|
|
vcpu->arch.mdcr_el2 &= ~mdcr_clear;
|
|
vcpu->arch.cptr_el2 |= cptr_set;
|
|
}
|
|
|
|
/*
|
|
* Set trap register values based on features in ID_AA64MMFR0.
|
|
*/
|
|
static void pvm_init_traps_aa64mmfr0(struct kvm_vcpu *vcpu)
|
|
{
|
|
const u64 feature_ids = pvm_read_id_reg(vcpu, SYS_ID_AA64MMFR0_EL1);
|
|
u64 mdcr_set = 0;
|
|
|
|
/* Trap Debug Communications Channel registers */
|
|
if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_EL1_FGT), feature_ids))
|
|
mdcr_set |= MDCR_EL2_TDCC;
|
|
|
|
vcpu->arch.mdcr_el2 |= mdcr_set;
|
|
}
|
|
|
|
/*
|
|
* Set trap register values based on features in ID_AA64MMFR1.
|
|
*/
|
|
static void pvm_init_traps_aa64mmfr1(struct kvm_vcpu *vcpu)
|
|
{
|
|
const u64 feature_ids = pvm_read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
|
|
u64 hcr_set = 0;
|
|
|
|
/* Trap LOR */
|
|
if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR1_EL1_LO), feature_ids))
|
|
hcr_set |= HCR_TLOR;
|
|
|
|
vcpu->arch.hcr_el2 |= hcr_set;
|
|
}
|
|
|
|
/*
|
|
* Set baseline trap register values.
|
|
*/
|
|
static void pvm_init_trap_regs(struct kvm_vcpu *vcpu)
|
|
{
|
|
const u64 hcr_trap_feat_regs = HCR_TID3;
|
|
const u64 hcr_trap_impdef = HCR_TACR | HCR_TIDCP | HCR_TID1;
|
|
|
|
/*
|
|
* Always trap:
|
|
* - Feature id registers: to control features exposed to guests
|
|
* - Implementation-defined features
|
|
*/
|
|
vcpu->arch.hcr_el2 |= hcr_trap_feat_regs | hcr_trap_impdef;
|
|
|
|
/* Clear res0 and set res1 bits to trap potential new features. */
|
|
vcpu->arch.hcr_el2 &= ~(HCR_RES0);
|
|
vcpu->arch.mdcr_el2 &= ~(MDCR_EL2_RES0);
|
|
vcpu->arch.cptr_el2 |= CPTR_NVHE_EL2_RES1;
|
|
vcpu->arch.cptr_el2 &= ~(CPTR_NVHE_EL2_RES0);
|
|
}
|
|
|
|
/*
|
|
* Initialize trap register values for protected VMs.
|
|
*/
|
|
void __pkvm_vcpu_init_traps(struct kvm_vcpu *vcpu)
|
|
{
|
|
pvm_init_trap_regs(vcpu);
|
|
pvm_init_traps_aa64pfr0(vcpu);
|
|
pvm_init_traps_aa64pfr1(vcpu);
|
|
pvm_init_traps_aa64dfr0(vcpu);
|
|
pvm_init_traps_aa64mmfr0(vcpu);
|
|
pvm_init_traps_aa64mmfr1(vcpu);
|
|
}
|