Commit c317c6d2 authored by Marc Zyngier's avatar Marc Zyngier

Merge branch kvm-arm64/aarch32-raz-idregs into kvmarm-master/next

* 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: default avatarMarc Zyngier <maz@kernel.org>
parents b90cb105 797b8451
......@@ -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);
......@@ -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 */
......@@ -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)
......@@ -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,
......@@ -1367,6 +1345,15 @@ 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
......@@ -1374,9 +1361,10 @@ static unsigned int mte_visibility(const struct kvm_vcpu *vcpu,
*/
#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 \
}
/*
......@@ -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, \
}
/*
......@@ -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 */
......@@ -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 {
......
......@@ -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,
......@@ -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,
......
# SPDX-License-Identifier: GPL-2.0-only
/aarch64/aarch32_id_regs
/aarch64/arch_timer
/aarch64/debug-exceptions
/aarch64/get-reg-list
......
......@@ -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
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* aarch32_id_regs - Test for ID register behavior on AArch64-only systems
*
* Copyright (c) 2022 Google LLC.
*
* Test that KVM handles the AArch64 views of the AArch32 ID registers as RAZ
* and WI from userspace.
*/
#include <stdint.h>
#include "kvm_util.h"
#include "processor.h"
#include "test_util.h"
#define BAD_ID_REG_VAL 0x1badc0deul
#define GUEST_ASSERT_REG_RAZ(reg) GUEST_ASSERT_EQ(read_sysreg_s(reg), 0)
static void guest_main(void)
{
GUEST_ASSERT_REG_RAZ(SYS_ID_PFR0_EL1);
GUEST_ASSERT_REG_RAZ(SYS_ID_PFR1_EL1);
GUEST_ASSERT_REG_RAZ(SYS_ID_DFR0_EL1);
GUEST_ASSERT_REG_RAZ(SYS_ID_AFR0_EL1);
GUEST_ASSERT_REG_RAZ(SYS_ID_MMFR0_EL1);
GUEST_ASSERT_REG_RAZ(SYS_ID_MMFR1_EL1);
GUEST_ASSERT_REG_RAZ(SYS_ID_MMFR2_EL1);
GUEST_ASSERT_REG_RAZ(SYS_ID_MMFR3_EL1);
GUEST_ASSERT_REG_RAZ(SYS_ID_ISAR0_EL1);
GUEST_ASSERT_REG_RAZ(SYS_ID_ISAR1_EL1);
GUEST_ASSERT_REG_RAZ(SYS_ID_ISAR2_EL1);
GUEST_ASSERT_REG_RAZ(SYS_ID_ISAR3_EL1);
GUEST_ASSERT_REG_RAZ(SYS_ID_ISAR4_EL1);
GUEST_ASSERT_REG_RAZ(SYS_ID_ISAR5_EL1);
GUEST_ASSERT_REG_RAZ(SYS_ID_MMFR4_EL1);
GUEST_ASSERT_REG_RAZ(SYS_ID_ISAR6_EL1);
GUEST_ASSERT_REG_RAZ(SYS_MVFR0_EL1);
GUEST_ASSERT_REG_RAZ(SYS_MVFR1_EL1);
GUEST_ASSERT_REG_RAZ(SYS_MVFR2_EL1);
GUEST_ASSERT_REG_RAZ(sys_reg(3, 0, 0, 3, 3));
GUEST_ASSERT_REG_RAZ(SYS_ID_PFR2_EL1);
GUEST_ASSERT_REG_RAZ(SYS_ID_DFR1_EL1);
GUEST_ASSERT_REG_RAZ(SYS_ID_MMFR5_EL1);
GUEST_ASSERT_REG_RAZ(sys_reg(3, 0, 0, 3, 7));
GUEST_DONE();
}
static void test_guest_raz(struct kvm_vcpu *vcpu)
{
struct ucall uc;
vcpu_run(vcpu);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
REPORT_GUEST_ASSERT(uc);
break;
case UCALL_DONE:
break;
default:
TEST_FAIL("Unexpected ucall: %lu", uc.cmd);
}
}
static uint64_t raz_wi_reg_ids[] = {
KVM_ARM64_SYS_REG(SYS_ID_PFR0_EL1),
KVM_ARM64_SYS_REG(SYS_ID_PFR1_EL1),
KVM_ARM64_SYS_REG(SYS_ID_DFR0_EL1),
KVM_ARM64_SYS_REG(SYS_ID_MMFR0_EL1),
KVM_ARM64_SYS_REG(SYS_ID_MMFR1_EL1),
KVM_ARM64_SYS_REG(SYS_ID_MMFR2_EL1),
KVM_ARM64_SYS_REG(SYS_ID_MMFR3_EL1),
KVM_ARM64_SYS_REG(SYS_ID_ISAR0_EL1),
KVM_ARM64_SYS_REG(SYS_ID_ISAR1_EL1),
KVM_ARM64_SYS_REG(SYS_ID_ISAR2_EL1),
KVM_ARM64_SYS_REG(SYS_ID_ISAR3_EL1),
KVM_ARM64_SYS_REG(SYS_ID_ISAR4_EL1),
KVM_ARM64_SYS_REG(SYS_ID_ISAR5_EL1),
KVM_ARM64_SYS_REG(SYS_ID_MMFR4_EL1),
KVM_ARM64_SYS_REG(SYS_ID_ISAR6_EL1),
KVM_ARM64_SYS_REG(SYS_MVFR0_EL1),
KVM_ARM64_SYS_REG(SYS_MVFR1_EL1),
KVM_ARM64_SYS_REG(SYS_MVFR2_EL1),
KVM_ARM64_SYS_REG(SYS_ID_PFR2_EL1),
KVM_ARM64_SYS_REG(SYS_ID_MMFR5_EL1),
};
static void test_user_raz_wi(struct kvm_vcpu *vcpu)
{
int i;
for (i = 0; i < ARRAY_SIZE(raz_wi_reg_ids); i++) {
uint64_t reg_id = raz_wi_reg_ids[i];
uint64_t val;
vcpu_get_reg(vcpu, reg_id, &val);
ASSERT_EQ(val, 0);
/*
* Expect the ioctl to succeed with no effect on the register
* value.
*/
vcpu_set_reg(vcpu, reg_id, BAD_ID_REG_VAL);
vcpu_get_reg(vcpu, reg_id, &val);
ASSERT_EQ(val, 0);
}
}
static uint64_t raz_invariant_reg_ids[] = {
KVM_ARM64_SYS_REG(SYS_ID_AFR0_EL1),
KVM_ARM64_SYS_REG(sys_reg(3, 0, 0, 3, 3)),
KVM_ARM64_SYS_REG(SYS_ID_DFR1_EL1),
KVM_ARM64_SYS_REG(sys_reg(3, 0, 0, 3, 7)),
};
static void test_user_raz_invariant(struct kvm_vcpu *vcpu)
{
int i, r;
for (i = 0; i < ARRAY_SIZE(raz_invariant_reg_ids); i++) {
uint64_t reg_id = raz_invariant_reg_ids[i];
uint64_t val;
vcpu_get_reg(vcpu, reg_id, &val);
ASSERT_EQ(val, 0);
r = __vcpu_set_reg(vcpu, reg_id, BAD_ID_REG_VAL);
TEST_ASSERT(r < 0 && errno == EINVAL,
"unexpected KVM_SET_ONE_REG error: r=%d, errno=%d", r, errno);
vcpu_get_reg(vcpu, reg_id, &val);
ASSERT_EQ(val, 0);
}
}
static bool vcpu_aarch64_only(struct kvm_vcpu *vcpu)
{
uint64_t val, el0;
vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR0_EL1), &val);
el0 = (val & ARM64_FEATURE_MASK(ID_AA64PFR0_EL0)) >> ID_AA64PFR0_EL0_SHIFT;
return el0 == ID_AA64PFR0_ELx_64BIT_ONLY;
}
int main(void)
{
struct kvm_vcpu *vcpu;
struct kvm_vm *vm;
vm = vm_create_with_one_vcpu(&vcpu, guest_main);
TEST_REQUIRE(vcpu_aarch64_only(vcpu));
ucall_init(vm, NULL);
test_user_raz_wi(vcpu);
test_user_raz_invariant(vcpu);
test_guest_raz(vcpu);
ucall_uninit(vm);
kvm_vm_free(vm);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment