Skip to content

Commit 26bf74b

Browse files
reijiw-kvmMarc Zyngier
authored andcommitted
KVM: arm64: mixed-width check should be skipped for uninitialized vCPUs
KVM allows userspace to configure either all EL1 32bit or 64bit vCPUs for a guest. At vCPU reset, vcpu_allowed_register_width() checks if the vcpu's register width is consistent with all other vCPUs'. Since the checking is done even against vCPUs that are not initialized (KVM_ARM_VCPU_INIT has not been done) yet, the uninitialized vCPUs are erroneously treated as 64bit vCPU, which causes the function to incorrectly detect a mixed-width VM. Introduce KVM_ARCH_FLAG_EL1_32BIT and KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED bits for kvm->arch.flags. A value of the EL1_32BIT bit indicates that the guest needs to be configured with all 32bit or 64bit vCPUs, and a value of the REG_WIDTH_CONFIGURED bit indicates if a value of the EL1_32BIT bit is valid (already set up). Values in those bits are set at the first KVM_ARM_VCPU_INIT for the guest based on KVM_ARM_VCPU_EL1_32BIT configuration for the vCPU. Check vcpu's register width against those new bits at the vcpu's KVM_ARM_VCPU_INIT (instead of against other vCPUs' register width). Fixes: 66e94d5 ("KVM: arm64: Prevent mixed-width VM creation") Signed-off-by: Reiji Watanabe <reijiw@google.com> Reviewed-by: Oliver Upton <oupton@google.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20220329031924.619453-2-reijiw@google.com
1 parent c707663 commit 26bf74b

3 files changed

Lines changed: 74 additions & 28 deletions

File tree

arch/arm64/include/asm/kvm_emulate.h

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,22 @@ void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr);
4343

4444
void kvm_vcpu_wfi(struct kvm_vcpu *vcpu);
4545

46+
#if defined(__KVM_VHE_HYPERVISOR__) || defined(__KVM_NVHE_HYPERVISOR__)
4647
static __always_inline bool vcpu_el1_is_32bit(struct kvm_vcpu *vcpu)
4748
{
4849
return !(vcpu->arch.hcr_el2 & HCR_RW);
4950
}
51+
#else
52+
static __always_inline bool vcpu_el1_is_32bit(struct kvm_vcpu *vcpu)
53+
{
54+
struct kvm *kvm = vcpu->kvm;
55+
56+
WARN_ON_ONCE(!test_bit(KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED,
57+
&kvm->arch.flags));
58+
59+
return test_bit(KVM_ARCH_FLAG_EL1_32BIT, &kvm->arch.flags);
60+
}
61+
#endif
5062

5163
static inline void vcpu_reset_hcr(struct kvm_vcpu *vcpu)
5264
{
@@ -72,15 +84,14 @@ static inline void vcpu_reset_hcr(struct kvm_vcpu *vcpu)
7284
vcpu->arch.hcr_el2 |= HCR_TVM;
7385
}
7486

75-
if (test_bit(KVM_ARM_VCPU_EL1_32BIT, vcpu->arch.features))
87+
if (vcpu_el1_is_32bit(vcpu))
7688
vcpu->arch.hcr_el2 &= ~HCR_RW;
77-
78-
/*
79-
* TID3: trap feature register accesses that we virtualise.
80-
* For now this is conditional, since no AArch32 feature regs
81-
* are currently virtualised.
82-
*/
83-
if (!vcpu_el1_is_32bit(vcpu))
89+
else
90+
/*
91+
* TID3: trap feature register accesses that we virtualise.
92+
* For now this is conditional, since no AArch32 feature regs
93+
* are currently virtualised.
94+
*/
8495
vcpu->arch.hcr_el2 |= HCR_TID3;
8596

8697
if (cpus_have_const_cap(ARM64_MISMATCHED_CACHE_TYPE) ||

arch/arm64/include/asm/kvm_host.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,16 @@ struct kvm_arch {
127127
#define KVM_ARCH_FLAG_MTE_ENABLED 1
128128
/* At least one vCPU has ran in the VM */
129129
#define KVM_ARCH_FLAG_HAS_RAN_ONCE 2
130+
/*
131+
* The following two bits are used to indicate the guest's EL1
132+
* register width configuration. A value of KVM_ARCH_FLAG_EL1_32BIT
133+
* bit is valid only when KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED is set.
134+
* Otherwise, the guest's EL1 register width has not yet been
135+
* determined yet.
136+
*/
137+
#define KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED 3
138+
#define KVM_ARCH_FLAG_EL1_32BIT 4
139+
130140
unsigned long flags;
131141

132142
/*

arch/arm64/kvm/reset.c

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -181,27 +181,51 @@ static int kvm_vcpu_enable_ptrauth(struct kvm_vcpu *vcpu)
181181
return 0;
182182
}
183183

184-
static bool vcpu_allowed_register_width(struct kvm_vcpu *vcpu)
184+
/**
185+
* kvm_set_vm_width() - set the register width for the guest
186+
* @vcpu: Pointer to the vcpu being configured
187+
*
188+
* Set both KVM_ARCH_FLAG_EL1_32BIT and KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED
189+
* in the VM flags based on the vcpu's requested register width, the HW
190+
* capabilities and other options (such as MTE).
191+
* When REG_WIDTH_CONFIGURED is already set, the vcpu settings must be
192+
* consistent with the value of the FLAG_EL1_32BIT bit in the flags.
193+
*
194+
* Return: 0 on success, negative error code on failure.
195+
*/
196+
static int kvm_set_vm_width(struct kvm_vcpu *vcpu)
185197
{
186-
struct kvm_vcpu *tmp;
198+
struct kvm *kvm = vcpu->kvm;
187199
bool is32bit;
188-
unsigned long i;
189200

190201
is32bit = vcpu_has_feature(vcpu, KVM_ARM_VCPU_EL1_32BIT);
202+
203+
lockdep_assert_held(&kvm->lock);
204+
205+
if (test_bit(KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED, &kvm->arch.flags)) {
206+
/*
207+
* The guest's register width is already configured.
208+
* Make sure that the vcpu is consistent with it.
209+
*/
210+
if (is32bit == test_bit(KVM_ARCH_FLAG_EL1_32BIT, &kvm->arch.flags))
211+
return 0;
212+
213+
return -EINVAL;
214+
}
215+
191216
if (!cpus_have_const_cap(ARM64_HAS_32BIT_EL1) && is32bit)
192-
return false;
217+
return -EINVAL;
193218

194219
/* MTE is incompatible with AArch32 */
195-
if (kvm_has_mte(vcpu->kvm) && is32bit)
196-
return false;
220+
if (kvm_has_mte(kvm) && is32bit)
221+
return -EINVAL;
197222

198-
/* Check that the vcpus are either all 32bit or all 64bit */
199-
kvm_for_each_vcpu(i, tmp, vcpu->kvm) {
200-
if (vcpu_has_feature(tmp, KVM_ARM_VCPU_EL1_32BIT) != is32bit)
201-
return false;
202-
}
223+
if (is32bit)
224+
set_bit(KVM_ARCH_FLAG_EL1_32BIT, &kvm->arch.flags);
203225

204-
return true;
226+
set_bit(KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED, &kvm->arch.flags);
227+
228+
return 0;
205229
}
206230

207231
/**
@@ -230,10 +254,16 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
230254
u32 pstate;
231255

232256
mutex_lock(&vcpu->kvm->lock);
233-
reset_state = vcpu->arch.reset_state;
234-
WRITE_ONCE(vcpu->arch.reset_state.reset, false);
257+
ret = kvm_set_vm_width(vcpu);
258+
if (!ret) {
259+
reset_state = vcpu->arch.reset_state;
260+
WRITE_ONCE(vcpu->arch.reset_state.reset, false);
261+
}
235262
mutex_unlock(&vcpu->kvm->lock);
236263

264+
if (ret)
265+
return ret;
266+
237267
/* Reset PMU outside of the non-preemptible section */
238268
kvm_pmu_vcpu_reset(vcpu);
239269

@@ -260,14 +290,9 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
260290
}
261291
}
262292

263-
if (!vcpu_allowed_register_width(vcpu)) {
264-
ret = -EINVAL;
265-
goto out;
266-
}
267-
268293
switch (vcpu->arch.target) {
269294
default:
270-
if (test_bit(KVM_ARM_VCPU_EL1_32BIT, vcpu->arch.features)) {
295+
if (vcpu_el1_is_32bit(vcpu)) {
271296
pstate = VCPU_RESET_PSTATE_SVC;
272297
} else {
273298
pstate = VCPU_RESET_PSTATE_EL1;

0 commit comments

Comments
 (0)