xref: /openbmc/linux/arch/arm64/kvm/hyp/nvhe/pkvm.c (revision bbb774d9)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2021 Google LLC
4  * Author: Fuad Tabba <tabba@google.com>
5  */
6 
7 #include <linux/kvm_host.h>
8 #include <linux/mm.h>
9 #include <nvhe/fixed_config.h>
10 #include <nvhe/trap_handler.h>
11 
12 /*
13  * Set trap register values based on features in ID_AA64PFR0.
14  */
15 static void pvm_init_traps_aa64pfr0(struct kvm_vcpu *vcpu)
16 {
17 	const u64 feature_ids = pvm_read_id_reg(vcpu, SYS_ID_AA64PFR0_EL1);
18 	u64 hcr_set = HCR_RW;
19 	u64 hcr_clear = 0;
20 	u64 cptr_set = 0;
21 
22 	/* Protected KVM does not support AArch32 guests. */
23 	BUILD_BUG_ON(FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL0),
24 		PVM_ID_AA64PFR0_RESTRICT_UNSIGNED) != ID_AA64PFR0_ELx_64BIT_ONLY);
25 	BUILD_BUG_ON(FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_EL1),
26 		PVM_ID_AA64PFR0_RESTRICT_UNSIGNED) != ID_AA64PFR0_ELx_64BIT_ONLY);
27 
28 	/*
29 	 * Linux guests assume support for floating-point and Advanced SIMD. Do
30 	 * not change the trapping behavior for these from the KVM default.
31 	 */
32 	BUILD_BUG_ON(!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_FP),
33 				PVM_ID_AA64PFR0_ALLOW));
34 	BUILD_BUG_ON(!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_ASIMD),
35 				PVM_ID_AA64PFR0_ALLOW));
36 
37 	/* Trap RAS unless all current versions are supported */
38 	if (FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_RAS), feature_ids) <
39 	    ID_AA64PFR0_RAS_V1P1) {
40 		hcr_set |= HCR_TERR | HCR_TEA;
41 		hcr_clear |= HCR_FIEN;
42 	}
43 
44 	/* Trap AMU */
45 	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_AMU), feature_ids)) {
46 		hcr_clear |= HCR_AMVOFFEN;
47 		cptr_set |= CPTR_EL2_TAM;
48 	}
49 
50 	/* Trap SVE */
51 	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR0_SVE), feature_ids))
52 		cptr_set |= CPTR_EL2_TZ;
53 
54 	vcpu->arch.hcr_el2 |= hcr_set;
55 	vcpu->arch.hcr_el2 &= ~hcr_clear;
56 	vcpu->arch.cptr_el2 |= cptr_set;
57 }
58 
59 /*
60  * Set trap register values based on features in ID_AA64PFR1.
61  */
62 static void pvm_init_traps_aa64pfr1(struct kvm_vcpu *vcpu)
63 {
64 	const u64 feature_ids = pvm_read_id_reg(vcpu, SYS_ID_AA64PFR1_EL1);
65 	u64 hcr_set = 0;
66 	u64 hcr_clear = 0;
67 
68 	/* Memory Tagging: Trap and Treat as Untagged if not supported. */
69 	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64PFR1_MTE), feature_ids)) {
70 		hcr_set |= HCR_TID5;
71 		hcr_clear |= HCR_DCT | HCR_ATA;
72 	}
73 
74 	vcpu->arch.hcr_el2 |= hcr_set;
75 	vcpu->arch.hcr_el2 &= ~hcr_clear;
76 }
77 
78 /*
79  * Set trap register values based on features in ID_AA64DFR0.
80  */
81 static void pvm_init_traps_aa64dfr0(struct kvm_vcpu *vcpu)
82 {
83 	const u64 feature_ids = pvm_read_id_reg(vcpu, SYS_ID_AA64DFR0_EL1);
84 	u64 mdcr_set = 0;
85 	u64 mdcr_clear = 0;
86 	u64 cptr_set = 0;
87 
88 	/* Trap/constrain PMU */
89 	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_PMUVER), feature_ids)) {
90 		mdcr_set |= MDCR_EL2_TPM | MDCR_EL2_TPMCR;
91 		mdcr_clear |= MDCR_EL2_HPME | MDCR_EL2_MTPME |
92 			      MDCR_EL2_HPMN_MASK;
93 	}
94 
95 	/* Trap Debug */
96 	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER), feature_ids))
97 		mdcr_set |= MDCR_EL2_TDRA | MDCR_EL2_TDA | MDCR_EL2_TDE;
98 
99 	/* Trap OS Double Lock */
100 	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_DOUBLELOCK), feature_ids))
101 		mdcr_set |= MDCR_EL2_TDOSA;
102 
103 	/* Trap SPE */
104 	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_PMSVER), feature_ids)) {
105 		mdcr_set |= MDCR_EL2_TPMS;
106 		mdcr_clear |= MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT;
107 	}
108 
109 	/* Trap Trace Filter */
110 	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_TRACE_FILT), feature_ids))
111 		mdcr_set |= MDCR_EL2_TTRF;
112 
113 	/* Trap Trace */
114 	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_TRACEVER), feature_ids))
115 		cptr_set |= CPTR_EL2_TTA;
116 
117 	vcpu->arch.mdcr_el2 |= mdcr_set;
118 	vcpu->arch.mdcr_el2 &= ~mdcr_clear;
119 	vcpu->arch.cptr_el2 |= cptr_set;
120 }
121 
122 /*
123  * Set trap register values based on features in ID_AA64MMFR0.
124  */
125 static void pvm_init_traps_aa64mmfr0(struct kvm_vcpu *vcpu)
126 {
127 	const u64 feature_ids = pvm_read_id_reg(vcpu, SYS_ID_AA64MMFR0_EL1);
128 	u64 mdcr_set = 0;
129 
130 	/* Trap Debug Communications Channel registers */
131 	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR0_FGT), feature_ids))
132 		mdcr_set |= MDCR_EL2_TDCC;
133 
134 	vcpu->arch.mdcr_el2 |= mdcr_set;
135 }
136 
137 /*
138  * Set trap register values based on features in ID_AA64MMFR1.
139  */
140 static void pvm_init_traps_aa64mmfr1(struct kvm_vcpu *vcpu)
141 {
142 	const u64 feature_ids = pvm_read_id_reg(vcpu, SYS_ID_AA64MMFR1_EL1);
143 	u64 hcr_set = 0;
144 
145 	/* Trap LOR */
146 	if (!FIELD_GET(ARM64_FEATURE_MASK(ID_AA64MMFR1_LOR), feature_ids))
147 		hcr_set |= HCR_TLOR;
148 
149 	vcpu->arch.hcr_el2 |= hcr_set;
150 }
151 
152 /*
153  * Set baseline trap register values.
154  */
155 static void pvm_init_trap_regs(struct kvm_vcpu *vcpu)
156 {
157 	const u64 hcr_trap_feat_regs = HCR_TID3;
158 	const u64 hcr_trap_impdef = HCR_TACR | HCR_TIDCP | HCR_TID1;
159 
160 	/*
161 	 * Always trap:
162 	 * - Feature id registers: to control features exposed to guests
163 	 * - Implementation-defined features
164 	 */
165 	vcpu->arch.hcr_el2 |= hcr_trap_feat_regs | hcr_trap_impdef;
166 
167 	/* Clear res0 and set res1 bits to trap potential new features. */
168 	vcpu->arch.hcr_el2 &= ~(HCR_RES0);
169 	vcpu->arch.mdcr_el2 &= ~(MDCR_EL2_RES0);
170 	vcpu->arch.cptr_el2 |= CPTR_NVHE_EL2_RES1;
171 	vcpu->arch.cptr_el2 &= ~(CPTR_NVHE_EL2_RES0);
172 }
173 
174 /*
175  * Initialize trap register values for protected VMs.
176  */
177 void __pkvm_vcpu_init_traps(struct kvm_vcpu *vcpu)
178 {
179 	pvm_init_trap_regs(vcpu);
180 	pvm_init_traps_aa64pfr0(vcpu);
181 	pvm_init_traps_aa64pfr1(vcpu);
182 	pvm_init_traps_aa64dfr0(vcpu);
183 	pvm_init_traps_aa64mmfr0(vcpu);
184 	pvm_init_traps_aa64mmfr1(vcpu);
185 }
186