Skip to content

Commit

Permalink
Merge branch kvm-arm64/aarch32-raz-idregs into kvmarm-master/next
Browse files Browse the repository at this point in the history
* kvm-arm64/aarch32-raz-idregs:
  : .
  : Rework AArch32 ID registers exposed by KVM on AArch64-only
  : systems by treating them as RAZ/WI instead as UNKOWN as
  : architected, which allows them to be trivially migrated
  : between different systems.
  :
  : Patches courtesy of Oliver Upton.
  : .
  KVM: selftests: Add test for AArch32 ID registers
  KVM: arm64: Treat 32bit ID registers as RAZ/WI on 64bit-only system
  KVM: arm64: Add a visibility bit to ignore user writes
  KVM: arm64: Spin off helper for calling visibility hook
  KVM: arm64: Drop raz parameter from read_id_reg()
  KVM: arm64: Remove internal accessor helpers for id regs
  KVM: arm64: Use visibility hook to treat ID regs as RAZ

Signed-off-by: Marc Zyngier <maz@kernel.org>
  • Loading branch information
Marc Zyngier committed Sep 16, 2022
2 parents b90cb10 + 797b845 commit c317c6d
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 86 deletions.
150 changes: 71 additions & 79 deletions arch/arm64/kvm/sys_regs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1063,13 +1063,12 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
}

/* Read a sanitised cpufeature ID register by sys_reg_desc */
static u64 read_id_reg(const struct kvm_vcpu *vcpu,
struct sys_reg_desc const *r, bool raz)
static u64 read_id_reg(const struct kvm_vcpu *vcpu, struct sys_reg_desc const *r)
{
u32 id = reg_to_encoding(r);
u64 val;

if (raz)
if (sysreg_visible_as_raz(vcpu, r))
return 0;

val = read_sanitised_ftr_reg(id);
Expand Down Expand Up @@ -1145,34 +1144,37 @@ static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
return 0;
}

/* cpufeature ID register access trap handlers */

static bool __access_id_reg(struct kvm_vcpu *vcpu,
struct sys_reg_params *p,
const struct sys_reg_desc *r,
bool raz)
static unsigned int aa32_id_visibility(const struct kvm_vcpu *vcpu,
const struct sys_reg_desc *r)
{
if (p->is_write)
return write_to_read_only(vcpu, p, r);
/*
* AArch32 ID registers are UNKNOWN if AArch32 isn't implemented at any
* EL. Promote to RAZ/WI in order to guarantee consistency between
* systems.
*/
if (!kvm_supports_32bit_el0())
return REG_RAZ | REG_USER_WI;

p->regval = read_id_reg(vcpu, r, raz);
return true;
return id_visibility(vcpu, r);
}

static unsigned int raz_visibility(const struct kvm_vcpu *vcpu,
const struct sys_reg_desc *r)
{
return REG_RAZ;
}

/* cpufeature ID register access trap handlers */

static bool access_id_reg(struct kvm_vcpu *vcpu,
struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
bool raz = sysreg_visible_as_raz(vcpu, r);

return __access_id_reg(vcpu, p, r, raz);
}
if (p->is_write)
return write_to_read_only(vcpu, p, r);

static bool access_raz_id_reg(struct kvm_vcpu *vcpu,
struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
return __access_id_reg(vcpu, p, r, true);
p->regval = read_id_reg(vcpu, r);
return true;
}

/* Visibility overrides for SVE-specific control registers */
Expand Down Expand Up @@ -1208,7 +1210,7 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
return -EINVAL;

/* We can only differ with CSV[23], and anything else is an error */
val ^= read_id_reg(vcpu, rd, false);
val ^= read_id_reg(vcpu, rd);
val &= ~((0xFUL << ID_AA64PFR0_CSV2_SHIFT) |
(0xFUL << ID_AA64PFR0_CSV3_SHIFT));
if (val)
Expand All @@ -1227,45 +1229,21 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
* are stored, and for set_id_reg() we don't allow the effective value
* to be changed.
*/
static int __get_id_reg(const struct kvm_vcpu *vcpu,
const struct sys_reg_desc *rd, u64 *val,
bool raz)
{
*val = read_id_reg(vcpu, rd, raz);
return 0;
}

static int __set_id_reg(const struct kvm_vcpu *vcpu,
const struct sys_reg_desc *rd, u64 val,
bool raz)
{
/* This is what we mean by invariant: you can't change it. */
if (val != read_id_reg(vcpu, rd, raz))
return -EINVAL;

return 0;
}

static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
u64 *val)
{
bool raz = sysreg_visible_as_raz(vcpu, rd);

return __get_id_reg(vcpu, rd, val, raz);
*val = read_id_reg(vcpu, rd);
return 0;
}

static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
u64 val)
{
bool raz = sysreg_visible_as_raz(vcpu, rd);

return __set_id_reg(vcpu, rd, val, raz);
}
/* This is what we mean by invariant: you can't change it. */
if (val != read_id_reg(vcpu, rd))
return -EINVAL;

static int set_raz_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
u64 val)
{
return __set_id_reg(vcpu, rd, val, true);
return 0;
}

static int get_raz_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
Expand Down Expand Up @@ -1367,16 +1345,26 @@ static unsigned int mte_visibility(const struct kvm_vcpu *vcpu,
.visibility = id_visibility, \
}

/* sys_reg_desc initialiser for known cpufeature ID registers */
#define AA32_ID_SANITISED(name) { \
SYS_DESC(SYS_##name), \
.access = access_id_reg, \
.get_user = get_id_reg, \
.set_user = set_id_reg, \
.visibility = aa32_id_visibility, \
}

/*
* sys_reg_desc initialiser for architecturally unallocated cpufeature ID
* register with encoding Op0=3, Op1=0, CRn=0, CRm=crm, Op2=op2
* (1 <= crm < 8, 0 <= Op2 < 8).
*/
#define ID_UNALLOCATED(crm, op2) { \
Op0(3), Op1(0), CRn(0), CRm(crm), Op2(op2), \
.access = access_raz_id_reg, \
.get_user = get_raz_reg, \
.set_user = set_raz_id_reg, \
.access = access_id_reg, \
.get_user = get_id_reg, \
.set_user = set_id_reg, \
.visibility = raz_visibility \
}

/*
Expand All @@ -1386,9 +1374,10 @@ static unsigned int mte_visibility(const struct kvm_vcpu *vcpu,
*/
#define ID_HIDDEN(name) { \
SYS_DESC(SYS_##name), \
.access = access_raz_id_reg, \
.get_user = get_raz_reg, \
.set_user = set_raz_id_reg, \
.access = access_id_reg, \
.get_user = get_id_reg, \
.set_user = set_id_reg, \
.visibility = raz_visibility, \
}

/*
Expand Down Expand Up @@ -1452,33 +1441,33 @@ static const struct sys_reg_desc sys_reg_descs[] = {

/* AArch64 mappings of the AArch32 ID registers */
/* CRm=1 */
ID_SANITISED(ID_PFR0_EL1),
ID_SANITISED(ID_PFR1_EL1),
ID_SANITISED(ID_DFR0_EL1),
AA32_ID_SANITISED(ID_PFR0_EL1),
AA32_ID_SANITISED(ID_PFR1_EL1),
AA32_ID_SANITISED(ID_DFR0_EL1),
ID_HIDDEN(ID_AFR0_EL1),
ID_SANITISED(ID_MMFR0_EL1),
ID_SANITISED(ID_MMFR1_EL1),
ID_SANITISED(ID_MMFR2_EL1),
ID_SANITISED(ID_MMFR3_EL1),
AA32_ID_SANITISED(ID_MMFR0_EL1),
AA32_ID_SANITISED(ID_MMFR1_EL1),
AA32_ID_SANITISED(ID_MMFR2_EL1),
AA32_ID_SANITISED(ID_MMFR3_EL1),

/* CRm=2 */
ID_SANITISED(ID_ISAR0_EL1),
ID_SANITISED(ID_ISAR1_EL1),
ID_SANITISED(ID_ISAR2_EL1),
ID_SANITISED(ID_ISAR3_EL1),
ID_SANITISED(ID_ISAR4_EL1),
ID_SANITISED(ID_ISAR5_EL1),
ID_SANITISED(ID_MMFR4_EL1),
ID_SANITISED(ID_ISAR6_EL1),
AA32_ID_SANITISED(ID_ISAR0_EL1),
AA32_ID_SANITISED(ID_ISAR1_EL1),
AA32_ID_SANITISED(ID_ISAR2_EL1),
AA32_ID_SANITISED(ID_ISAR3_EL1),
AA32_ID_SANITISED(ID_ISAR4_EL1),
AA32_ID_SANITISED(ID_ISAR5_EL1),
AA32_ID_SANITISED(ID_MMFR4_EL1),
AA32_ID_SANITISED(ID_ISAR6_EL1),

/* CRm=3 */
ID_SANITISED(MVFR0_EL1),
ID_SANITISED(MVFR1_EL1),
ID_SANITISED(MVFR2_EL1),
AA32_ID_SANITISED(MVFR0_EL1),
AA32_ID_SANITISED(MVFR1_EL1),
AA32_ID_SANITISED(MVFR2_EL1),
ID_UNALLOCATED(3,3),
ID_SANITISED(ID_PFR2_EL1),
AA32_ID_SANITISED(ID_PFR2_EL1),
ID_HIDDEN(ID_DFR1_EL1),
ID_SANITISED(ID_MMFR5_EL1),
AA32_ID_SANITISED(ID_MMFR5_EL1),
ID_UNALLOCATED(3,7),

/* AArch64 ID registers */
Expand Down Expand Up @@ -2809,6 +2798,9 @@ int kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
if (!r)
return -ENOENT;

if (sysreg_user_write_ignore(vcpu, r))
return 0;

if (r->set_user) {
ret = (r->set_user)(vcpu, r, val);
} else {
Expand Down
24 changes: 17 additions & 7 deletions arch/arm64/kvm/sys_regs.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ struct sys_reg_desc {

#define REG_HIDDEN (1 << 0) /* hidden from userspace and guest */
#define REG_RAZ (1 << 1) /* RAZ from userspace and guest */
#define REG_USER_WI (1 << 2) /* WI from userspace only */

static __printf(2, 3)
inline void print_sys_reg_msg(const struct sys_reg_params *p,
Expand Down Expand Up @@ -136,22 +137,31 @@ static inline void reset_val(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r
__vcpu_sys_reg(vcpu, r->reg) = r->val;
}

static inline bool sysreg_hidden(const struct kvm_vcpu *vcpu,
const struct sys_reg_desc *r)
static inline unsigned int sysreg_visibility(const struct kvm_vcpu *vcpu,
const struct sys_reg_desc *r)
{
if (likely(!r->visibility))
return false;
return 0;

return r->visibility(vcpu, r) & REG_HIDDEN;
return r->visibility(vcpu, r);
}

static inline bool sysreg_hidden(const struct kvm_vcpu *vcpu,
const struct sys_reg_desc *r)
{
return sysreg_visibility(vcpu, r) & REG_HIDDEN;
}

static inline bool sysreg_visible_as_raz(const struct kvm_vcpu *vcpu,
const struct sys_reg_desc *r)
{
if (likely(!r->visibility))
return false;
return sysreg_visibility(vcpu, r) & REG_RAZ;
}

return r->visibility(vcpu, r) & REG_RAZ;
static inline bool sysreg_user_write_ignore(const struct kvm_vcpu *vcpu,
const struct sys_reg_desc *r)
{
return sysreg_visibility(vcpu, r) & REG_USER_WI;
}

static inline int cmp_sys_reg(const struct sys_reg_desc *i1,
Expand Down
1 change: 1 addition & 0 deletions tools/testing/selftests/kvm/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
/aarch64/aarch32_id_regs
/aarch64/arch_timer
/aarch64/debug-exceptions
/aarch64/get-reg-list
Expand Down
1 change: 1 addition & 0 deletions tools/testing/selftests/kvm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ TEST_GEN_PROGS_x86_64 += system_counter_offset_test
# Compiled outputs used by test targets
TEST_GEN_PROGS_EXTENDED_x86_64 += x86_64/nx_huge_pages_test

TEST_GEN_PROGS_aarch64 += aarch64/aarch32_id_regs
TEST_GEN_PROGS_aarch64 += aarch64/arch_timer
TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions
TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list
Expand Down
Loading

0 comments on commit c317c6d

Please sign in to comment.