Skip to content

Commit 2e8bf0c

Browse files
jingzhangosoupton
authored andcommitted
KVM: arm64: Use arm64_ftr_bits to sanitise ID register writes
Rather than reinventing the wheel in KVM to do ID register sanitisation we can rely on the work already done in the core kernel. Implement a generalized sanitisation of ID registers based on the combination of the arm64_ftr_bits definitions from the core kernel and (optionally) a set of KVM-specific overrides. This all amounts to absolutely nothing for now, but will be used in subsequent changes to realize user-configurable ID registers. Signed-off-by: Jing Zhang <jingzhangos@google.com> Link: https://lore.kernel.org/r/20230609190054.1542113-8-oliver.upton@linux.dev [Oliver: split off from monster patch, rewrote commit description, reworked RAZ handling, return EINVAL to userspace] Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
1 parent 4733414 commit 2e8bf0c

3 files changed

Lines changed: 121 additions & 5 deletions

File tree

arch/arm64/include/asm/cpufeature.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -915,6 +915,7 @@ static inline unsigned int get_vmid_bits(u64 mmfr1)
915915
return 8;
916916
}
917917

918+
s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new, s64 cur);
918919
struct arm64_ftr_reg *get_arm64_ftr_reg(u32 sys_id);
919920

920921
extern struct arm64_ftr_override id_aa64mmfr1_override;

arch/arm64/kernel/cpufeature.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,7 @@ static u64 arm64_ftr_set_value(const struct arm64_ftr_bits *ftrp, s64 reg,
798798
return reg;
799799
}
800800

801-
static s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new,
801+
s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new,
802802
s64 cur)
803803
{
804804
s64 ret = 0;

arch/arm64/kvm/sys_regs.c

Lines changed: 119 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,6 +1201,91 @@ static u8 vcpu_pmuver(const struct kvm_vcpu *vcpu)
12011201
return 0;
12021202
}
12031203

1204+
static s64 kvm_arm64_ftr_safe_value(u32 id, const struct arm64_ftr_bits *ftrp,
1205+
s64 new, s64 cur)
1206+
{
1207+
struct arm64_ftr_bits kvm_ftr = *ftrp;
1208+
1209+
/* Some features have different safe value type in KVM than host features */
1210+
switch (id) {
1211+
case SYS_ID_AA64DFR0_EL1:
1212+
if (kvm_ftr.shift == ID_AA64DFR0_EL1_PMUVer_SHIFT)
1213+
kvm_ftr.type = FTR_LOWER_SAFE;
1214+
break;
1215+
case SYS_ID_DFR0_EL1:
1216+
if (kvm_ftr.shift == ID_DFR0_EL1_PerfMon_SHIFT)
1217+
kvm_ftr.type = FTR_LOWER_SAFE;
1218+
break;
1219+
}
1220+
1221+
return arm64_ftr_safe_value(&kvm_ftr, new, cur);
1222+
}
1223+
1224+
/**
1225+
* arm64_check_features() - Check if a feature register value constitutes
1226+
* a subset of features indicated by the idreg's KVM sanitised limit.
1227+
*
1228+
* This function will check if each feature field of @val is the "safe" value
1229+
* against idreg's KVM sanitised limit return from reset() callback.
1230+
* If a field value in @val is the same as the one in limit, it is always
1231+
* considered the safe value regardless For register fields that are not in
1232+
* writable, only the value in limit is considered the safe value.
1233+
*
1234+
* Return: 0 if all the fields are safe. Otherwise, return negative errno.
1235+
*/
1236+
static int arm64_check_features(struct kvm_vcpu *vcpu,
1237+
const struct sys_reg_desc *rd,
1238+
u64 val)
1239+
{
1240+
const struct arm64_ftr_reg *ftr_reg;
1241+
const struct arm64_ftr_bits *ftrp = NULL;
1242+
u32 id = reg_to_encoding(rd);
1243+
u64 writable_mask = rd->val;
1244+
u64 limit = rd->reset(vcpu, rd);
1245+
u64 mask = 0;
1246+
1247+
/*
1248+
* Hidden and unallocated ID registers may not have a corresponding
1249+
* struct arm64_ftr_reg. Of course, if the register is RAZ we know the
1250+
* only safe value is 0.
1251+
*/
1252+
if (sysreg_visible_as_raz(vcpu, rd))
1253+
return val ? -E2BIG : 0;
1254+
1255+
ftr_reg = get_arm64_ftr_reg(id);
1256+
if (!ftr_reg)
1257+
return -EINVAL;
1258+
1259+
ftrp = ftr_reg->ftr_bits;
1260+
1261+
for (; ftrp && ftrp->width; ftrp++) {
1262+
s64 f_val, f_lim, safe_val;
1263+
u64 ftr_mask;
1264+
1265+
ftr_mask = arm64_ftr_mask(ftrp);
1266+
if ((ftr_mask & writable_mask) != ftr_mask)
1267+
continue;
1268+
1269+
f_val = arm64_ftr_value(ftrp, val);
1270+
f_lim = arm64_ftr_value(ftrp, limit);
1271+
mask |= ftr_mask;
1272+
1273+
if (f_val == f_lim)
1274+
safe_val = f_val;
1275+
else
1276+
safe_val = kvm_arm64_ftr_safe_value(id, ftrp, f_val, f_lim);
1277+
1278+
if (safe_val != f_val)
1279+
return -E2BIG;
1280+
}
1281+
1282+
/* For fields that are not writable, values in limit are the safe values. */
1283+
if ((val & ~mask) != (limit & ~mask))
1284+
return -E2BIG;
1285+
1286+
return 0;
1287+
}
1288+
12041289
static u8 perfmon_to_pmuver(u8 perfmon)
12051290
{
12061291
switch (perfmon) {
@@ -1528,11 +1613,41 @@ static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
15281613
static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
15291614
u64 val)
15301615
{
1531-
/* This is what we mean by invariant: you can't change it. */
1532-
if (val != read_id_reg(vcpu, rd))
1533-
return -EINVAL;
1616+
u32 id = reg_to_encoding(rd);
1617+
int ret;
15341618

1535-
return 0;
1619+
mutex_lock(&vcpu->kvm->arch.config_lock);
1620+
1621+
/*
1622+
* Once the VM has started the ID registers are immutable. Reject any
1623+
* write that does not match the final register value.
1624+
*/
1625+
if (kvm_vm_has_ran_once(vcpu->kvm)) {
1626+
if (val != read_id_reg(vcpu, rd))
1627+
ret = -EBUSY;
1628+
else
1629+
ret = 0;
1630+
1631+
mutex_unlock(&vcpu->kvm->arch.config_lock);
1632+
return ret;
1633+
}
1634+
1635+
ret = arm64_check_features(vcpu, rd, val);
1636+
if (!ret)
1637+
IDREG(vcpu->kvm, id) = val;
1638+
1639+
mutex_unlock(&vcpu->kvm->arch.config_lock);
1640+
1641+
/*
1642+
* arm64_check_features() returns -E2BIG to indicate the register's
1643+
* feature set is a superset of the maximally-allowed register value.
1644+
* While it would be nice to precisely describe this to userspace, the
1645+
* existing UAPI for KVM_SET_ONE_REG has it that invalid register
1646+
* writes return -EINVAL.
1647+
*/
1648+
if (ret == -E2BIG)
1649+
ret = -EINVAL;
1650+
return ret;
15361651
}
15371652

15381653
static int get_raz_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,

0 commit comments

Comments
 (0)