Skip to content

Commit c5ed021

Browse files
Suraj Jitindar Singhshaoyingxu
authored andcommitted
KVM: arm64: Update id_reg limit value based on per vcpu flags
There are multiple features the availability of which is enabled/disabled and tracked on a per vcpu level in vcpu->arch.flagset e.g. sve, ptrauth, and pmu. While the vm wide value of the id regs which represent the availability of these features is stored in the id_regs kvm struct their value needs to be manipulated on a per vcpu basis. This is done at read time in kvm_arm_read_id_reg(). The value of these per vcpu flags needs to be factored in when calculating the id_reg limit value in check_features() as otherwise we can run into the following scenario. [ running on cpu which supports sve ] 1. AA64PFR0.SVE set in id_reg by kvm_arm_init_id_regs() (cpu supports it and so is set in value returned from read_sanitised_ftr_reg()) 2. vcpus created without sve feature enabled 3. vmm reads AA64PFR0 and attempts to write the same value back (writing the same value back is allowed) 4. write fails in check_features() as limit has AA64PFR0.SVE set however it is not set in the value being written and although a lower value is allowed for this feature it is not in the mask of bits which can be modified and so much match exactly. Thus add a step in check_features() to update the limit returned from id_reg->reset() with the per vcpu features which may have been enabled/disabled at vcpu creation time after the id_regs were initialised. Split this update into a new function named kvm_arm_update_id_reg() so it can be called from check_features() as well as kvm_arm_read_id_reg() to dedup code. Note: Processing of the DFR0 and AA64DFR0 registers remains in kvm_arm_read_id_reg() as the value of these cannot be modified based on vcpu feature flags, all changes to these registers must go through the id_regs mechanism. Also add a check for RAZ (read a zero) registers to arm64_check_features() as even though the host may support a non-zero value for these, it is only valid to set them to zero from userspace. Finally return -EINVAL from set_id_reg() rather than -E2BIG to remain consistent with the UAPI. Signed-off-by: Suraj Jitindar Singh <surajjs@amazon.com>
1 parent abe7ccc commit c5ed021

File tree

1 file changed

+28
-4
lines changed

1 file changed

+28
-4
lines changed

arch/arm64/kvm/sys_regs.c

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
*/
4141

4242
static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, u64 val);
43+
static u64 kvm_arm_update_id_reg(const struct kvm_vcpu *vcpu, u32 id, u64 val);
4344
static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding);
4445
static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
4546

@@ -1143,9 +1144,14 @@ static int arm64_check_features(struct kvm_vcpu *vcpu,
11431144
u64 limit = 0;
11441145
u64 mask = 0;
11451146

1147+
/* If the register is RAZ we know the only safe value is 0. */
1148+
if (sysreg_visible_as_raz(vcpu, rd))
1149+
return val ? -E2BIG : 0;
1150+
11461151
/* For hidden and unallocated idregs without reset, only val = 0 is allowed. */
11471152
if (rd->reset) {
11481153
limit = rd->reset(vcpu, rd);
1154+
limit = kvm_arm_update_id_reg(vcpu, id, limit);
11491155
ftr_reg = get_arm64_ftr_reg(id);
11501156
if (!ftr_reg)
11511157
return -EINVAL;
@@ -1252,10 +1258,8 @@ static u64 general_read_kvm_sanitised_reg(struct kvm_vcpu *vcpu, const struct sy
12521258
return read_sanitised_ftr_reg(reg_to_encoding(rd));
12531259
}
12541260

1255-
static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
1261+
static u64 kvm_arm_update_id_reg(const struct kvm_vcpu *vcpu, u32 encoding, u64 val)
12561262
{
1257-
u64 val = IDREG(vcpu->kvm, encoding);
1258-
12591263
switch (encoding) {
12601264
case SYS_ID_AA64PFR0_EL1:
12611265
if (!vcpu_has_sve(vcpu))
@@ -1285,6 +1289,16 @@ static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
12851289
if (!cpus_have_final_cap(ARM64_HAS_WFXT))
12861290
val &= ~ARM64_FEATURE_MASK(ID_AA64ISAR2_EL1_WFxT);
12871291
break;
1292+
}
1293+
1294+
return val;
1295+
}
1296+
1297+
static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
1298+
{
1299+
u64 val = IDREG(vcpu->kvm, encoding);
1300+
1301+
switch (encoding) {
12881302
case SYS_ID_AA64DFR0_EL1:
12891303
/* Set PMUver to the required version */
12901304
val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer);
@@ -1298,7 +1312,7 @@ static u64 kvm_arm_read_id_reg(const struct kvm_vcpu *vcpu, u32 encoding)
12981312
break;
12991313
}
13001314

1301-
return val;
1315+
return kvm_arm_update_id_reg(vcpu, encoding, val);
13021316
}
13031317

13041318
/* Read a sanitised cpufeature ID register by sys_reg_desc */
@@ -1551,6 +1565,16 @@ static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
15511565
if (!ret)
15521566
IDREG(vcpu->kvm, id) = val;
15531567

1568+
/*
1569+
* arm64_check_features() returns -E2BIG to indicate the register's
1570+
* feature set is a superset of the maximally-allowed register value.
1571+
* While it would be nice to precisely describe this to userspace, the
1572+
* existing UAPI for KVM_SET_ONE_REG has it that invalid register
1573+
* writes return -EINVAL.
1574+
*/
1575+
if (ret == -E2BIG)
1576+
ret = -EINVAL;
1577+
15541578
return ret;
15551579
}
15561580

0 commit comments

Comments
 (0)