* [PATCH 1/7] KVM: arm64: Rename is_id_reg() to imply VM scope
2024-05-02 23:35 [PATCH 0/7] KVM: arm64: Don't clobber CLIDR and MPIDR across vCPU reset Oliver Upton
@ 2024-05-02 23:35 ` Oliver Upton
2024-05-13 13:24 ` Sebastian Ott
2024-05-02 23:35 ` [PATCH 2/7] KVM: arm64: Reset VM feature ID regs from kvm_reset_sys_regs() Oliver Upton
` (6 subsequent siblings)
7 siblings, 1 reply; 12+ messages in thread
From: Oliver Upton @ 2024-05-02 23:35 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu, kvm,
Oliver Upton
The naming of some of the feature ID checks is ambiguous. Rephrase the
is_id_reg() helper to make its purpose slightly clearer.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
arch/arm64/kvm/sys_regs.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index c9f4f387155f..51a6f91607e5 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1570,9 +1570,10 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu, const struct sys_reg_desc *r
/*
* Return true if the register's (Op0, Op1, CRn, CRm, Op2) is
- * (3, 0, 0, crm, op2), where 1<=crm<8, 0<=op2<8.
+ * (3, 0, 0, crm, op2), where 1<=crm<8, 0<=op2<8, which is the range of ID
+ * registers KVM maintains on a per-VM basis.
*/
-static inline bool is_id_reg(u32 id)
+static inline bool is_vm_ftr_id_reg(u32 id)
{
return (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&
sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 1 &&
@@ -3521,7 +3522,7 @@ static void kvm_reset_id_regs(struct kvm_vcpu *vcpu)
lockdep_assert_held(&kvm->arch.config_lock);
/* Initialize all idregs */
- while (is_id_reg(id)) {
+ while (is_vm_ftr_id_reg(id)) {
IDREG(kvm, id) = idreg->reset(vcpu, idreg);
idreg++;
@@ -3547,7 +3548,7 @@ void kvm_reset_sys_regs(struct kvm_vcpu *vcpu)
for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
const struct sys_reg_desc *r = &sys_reg_descs[i];
- if (is_id_reg(reg_to_encoding(r)))
+ if (is_vm_ftr_id_reg(reg_to_encoding(r)))
continue;
if (r->reset)
@@ -4014,7 +4015,7 @@ int kvm_vm_ioctl_get_reg_writable_masks(struct kvm *kvm, struct reg_mask_range *
* compliant with a given revision of the architecture, but the
* RES0/RES1 definitions allow us to do that.
*/
- if (is_id_reg(encoding)) {
+ if (is_vm_ftr_id_reg(encoding)) {
if (!reg->val ||
(is_aa32_id_reg(encoding) && !kvm_supports_32bit_el0()))
continue;
--
2.45.0.rc1.225.g2a3ae87e7f-goog
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 2/7] KVM: arm64: Reset VM feature ID regs from kvm_reset_sys_regs()
2024-05-02 23:35 [PATCH 0/7] KVM: arm64: Don't clobber CLIDR and MPIDR across vCPU reset Oliver Upton
2024-05-02 23:35 ` [PATCH 1/7] KVM: arm64: Rename is_id_reg() to imply VM scope Oliver Upton
@ 2024-05-02 23:35 ` Oliver Upton
2024-05-13 13:26 ` Sebastian Ott
2024-05-02 23:35 ` [PATCH 3/7] KVM: arm64: Only reset vCPU-scoped feature ID regs once Oliver Upton
` (5 subsequent siblings)
7 siblings, 1 reply; 12+ messages in thread
From: Oliver Upton @ 2024-05-02 23:35 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu, kvm,
Oliver Upton
A subsequent change to KVM will expand the range of feature ID registers
that get special treatment at reset. Fold the existing ones back in to
kvm_reset_sys_regs() to avoid the need for an additional table walk.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
arch/arm64/kvm/sys_regs.c | 27 ++++++++++-----------------
1 file changed, 10 insertions(+), 17 deletions(-)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 51a6f91607e5..bb09ce4bce45 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -3510,26 +3510,16 @@ void kvm_sys_regs_create_debugfs(struct kvm *kvm)
&idregs_debug_fops);
}
-static void kvm_reset_id_regs(struct kvm_vcpu *vcpu)
+static void reset_vm_ftr_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *reg)
{
- const struct sys_reg_desc *idreg = first_idreg;
- u32 id = reg_to_encoding(idreg);
+ u32 id = reg_to_encoding(reg);
struct kvm *kvm = vcpu->kvm;
if (test_bit(KVM_ARCH_FLAG_ID_REGS_INITIALIZED, &kvm->arch.flags))
return;
lockdep_assert_held(&kvm->arch.config_lock);
-
- /* Initialize all idregs */
- while (is_vm_ftr_id_reg(id)) {
- IDREG(kvm, id) = idreg->reset(vcpu, idreg);
-
- idreg++;
- id = reg_to_encoding(idreg);
- }
-
- set_bit(KVM_ARCH_FLAG_ID_REGS_INITIALIZED, &kvm->arch.flags);
+ IDREG(kvm, id) = reg->reset(vcpu, reg);
}
/**
@@ -3541,19 +3531,22 @@ static void kvm_reset_id_regs(struct kvm_vcpu *vcpu)
*/
void kvm_reset_sys_regs(struct kvm_vcpu *vcpu)
{
+ struct kvm *kvm = vcpu->kvm;
unsigned long i;
- kvm_reset_id_regs(vcpu);
-
for (i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
const struct sys_reg_desc *r = &sys_reg_descs[i];
- if (is_vm_ftr_id_reg(reg_to_encoding(r)))
+ if (!r->reset)
continue;
- if (r->reset)
+ if (is_vm_ftr_id_reg(reg_to_encoding(r)))
+ reset_vm_ftr_id_reg(vcpu, r);
+ else
r->reset(vcpu, r);
}
+
+ set_bit(KVM_ARCH_FLAG_ID_REGS_INITIALIZED, &kvm->arch.flags);
}
/**
--
2.45.0.rc1.225.g2a3ae87e7f-goog
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 3/7] KVM: arm64: Only reset vCPU-scoped feature ID regs once
2024-05-02 23:35 [PATCH 0/7] KVM: arm64: Don't clobber CLIDR and MPIDR across vCPU reset Oliver Upton
2024-05-02 23:35 ` [PATCH 1/7] KVM: arm64: Rename is_id_reg() to imply VM scope Oliver Upton
2024-05-02 23:35 ` [PATCH 2/7] KVM: arm64: Reset VM feature ID regs from kvm_reset_sys_regs() Oliver Upton
@ 2024-05-02 23:35 ` Oliver Upton
2024-05-13 13:31 ` Sebastian Ott
2024-05-02 23:35 ` [PATCH 4/7] KVM: selftests: Rename helper in set_id_regs to imply VM scope Oliver Upton
` (4 subsequent siblings)
7 siblings, 1 reply; 12+ messages in thread
From: Oliver Upton @ 2024-05-02 23:35 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu, kvm,
Oliver Upton
The general expecation with feature ID registers is that they're 'reset'
exactly once by KVM for the lifetime of a vCPU/VM, such that any
userspace changes to the CPU features / identity are honored after a
vCPU gets reset (e.g. PSCI_ON).
KVM handles what it calls VM-scoped feature ID registers correctly, but
feature ID registers local to a vCPU (CLIDR_EL1, MPIDR_EL1) get wiped
after every reset. What's especially concerning is that a
potentially-changing MPIDR_EL1 breaks MPIDR compression for indexing
mpidr_data, as the mask of useful bits to build the index could change.
This is absolutely no good. Avoid resetting vCPU feature ID registers
more than once.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
arch/arm64/include/asm/kvm_host.h | 2 ++
arch/arm64/kvm/arm.c | 5 -----
arch/arm64/kvm/sys_regs.c | 32 +++++++++++++++++++++++--------
3 files changed, 26 insertions(+), 13 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 9e8a496fb284..78830318c946 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -1275,6 +1275,8 @@ static inline bool __vcpu_has_feature(const struct kvm_arch *ka, int feature)
#define vcpu_has_feature(v, f) __vcpu_has_feature(&(v)->kvm->arch, (f))
+#define kvm_vcpu_initialized(v) vcpu_get_flag(vcpu, VCPU_INITIALIZED)
+
int kvm_trng_call(struct kvm_vcpu *vcpu);
#ifdef CONFIG_KVM
extern phys_addr_t hyp_mem_base;
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index c4a0a35e02c7..2116181e2315 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -580,11 +580,6 @@ unsigned long kvm_arch_vcpu_get_ip(struct kvm_vcpu *vcpu)
}
#endif
-static int kvm_vcpu_initialized(struct kvm_vcpu *vcpu)
-{
- return vcpu_get_flag(vcpu, VCPU_INITIALIZED);
-}
-
static void kvm_init_mpidr_data(struct kvm *kvm)
{
struct kvm_mpidr_data *data = NULL;
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index bb09ce4bce45..99a485062a62 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1568,6 +1568,14 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu, const struct sys_reg_desc *r
return IDREG(vcpu->kvm, reg_to_encoding(r));
}
+static bool is_feature_id_reg(u32 encoding)
+{
+ return (sys_reg_Op0(encoding) == 3 &&
+ (sys_reg_Op1(encoding) < 2 || sys_reg_Op1(encoding) == 3) &&
+ sys_reg_CRn(encoding) == 0 &&
+ sys_reg_CRm(encoding) <= 7);
+}
+
/*
* Return true if the register's (Op0, Op1, CRn, CRm, Op2) is
* (3, 0, 0, crm, op2), where 1<=crm<8, 0<=op2<8, which is the range of ID
@@ -1580,6 +1588,11 @@ static inline bool is_vm_ftr_id_reg(u32 id)
sys_reg_CRm(id) < 8);
}
+static inline bool is_vcpu_ftr_id_reg(u32 id)
+{
+ return is_feature_id_reg(id) && !is_vm_ftr_id_reg(id);
+}
+
static inline bool is_aa32_id_reg(u32 id)
{
return (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&
@@ -3522,6 +3535,15 @@ static void reset_vm_ftr_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc
IDREG(kvm, id) = reg->reset(vcpu, reg);
}
+static void reset_vcpu_ftr_id_reg(struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *reg)
+{
+ if (kvm_vcpu_initialized(vcpu))
+ return;
+
+ reg->reset(vcpu, reg);
+}
+
/**
* kvm_reset_sys_regs - sets system registers to reset value
* @vcpu: The VCPU pointer
@@ -3542,6 +3564,8 @@ void kvm_reset_sys_regs(struct kvm_vcpu *vcpu)
if (is_vm_ftr_id_reg(reg_to_encoding(r)))
reset_vm_ftr_id_reg(vcpu, r);
+ else if (is_vcpu_ftr_id_reg(reg_to_encoding(r)))
+ reset_vcpu_ftr_id_reg(vcpu, r);
else
r->reset(vcpu, r);
}
@@ -3972,14 +3996,6 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
sys_reg_CRm(r), \
sys_reg_Op2(r))
-static bool is_feature_id_reg(u32 encoding)
-{
- return (sys_reg_Op0(encoding) == 3 &&
- (sys_reg_Op1(encoding) < 2 || sys_reg_Op1(encoding) == 3) &&
- sys_reg_CRn(encoding) == 0 &&
- sys_reg_CRm(encoding) <= 7);
-}
-
int kvm_vm_ioctl_get_reg_writable_masks(struct kvm *kvm, struct reg_mask_range *range)
{
const void *zero_page = page_to_virt(ZERO_PAGE(0));
--
2.45.0.rc1.225.g2a3ae87e7f-goog
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH 3/7] KVM: arm64: Only reset vCPU-scoped feature ID regs once
2024-05-02 23:35 ` [PATCH 3/7] KVM: arm64: Only reset vCPU-scoped feature ID regs once Oliver Upton
@ 2024-05-13 13:31 ` Sebastian Ott
0 siblings, 0 replies; 12+ messages in thread
From: Sebastian Ott @ 2024-05-13 13:31 UTC (permalink / raw)
To: oliver.upton; +Cc: james.morse, kvm, kvmarm, maz, suzuki.poulose, yuzenghui
> The general expecation with feature ID registers is that they're 'reset'
^
expectation
> exactly once by KVM for the lifetime of a vCPU/VM, such that any
> userspace changes to the CPU features / identity are honored after a
> vCPU gets reset (e.g. PSCI_ON).
>
> KVM handles what it calls VM-scoped feature ID registers correctly, but
> feature ID registers local to a vCPU (CLIDR_EL1, MPIDR_EL1) get wiped
> after every reset. What's especially concerning is that a
> potentially-changing MPIDR_EL1 breaks MPIDR compression for indexing
> mpidr_data, as the mask of useful bits to build the index could change.
>
> This is absolutely no good. Avoid resetting vCPU feature ID registers
> more than once.
Reviewed-by: Sebastian Ott <sebott@redhat.com>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 4/7] KVM: selftests: Rename helper in set_id_regs to imply VM scope
2024-05-02 23:35 [PATCH 0/7] KVM: arm64: Don't clobber CLIDR and MPIDR across vCPU reset Oliver Upton
` (2 preceding siblings ...)
2024-05-02 23:35 ` [PATCH 3/7] KVM: arm64: Only reset vCPU-scoped feature ID regs once Oliver Upton
@ 2024-05-02 23:35 ` Oliver Upton
2024-05-02 23:35 ` [PATCH 5/7] KVM: selftests: Store expected register value in set_id_regs Oliver Upton
` (3 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Oliver Upton @ 2024-05-02 23:35 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu, kvm,
Oliver Upton
Prepare for a later change that'll cram in per-vCPU feature ID test
cases by renaming the current test case.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
tools/testing/selftests/kvm/aarch64/set_id_regs.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/kvm/aarch64/set_id_regs.c b/tools/testing/selftests/kvm/aarch64/set_id_regs.c
index 16e2338686c1..3d0ce49f9b78 100644
--- a/tools/testing/selftests/kvm/aarch64/set_id_regs.c
+++ b/tools/testing/selftests/kvm/aarch64/set_id_regs.c
@@ -374,7 +374,7 @@ static void test_reg_set_fail(struct kvm_vcpu *vcpu, uint64_t reg,
TEST_ASSERT_EQ(val, old_val);
}
-static void test_user_set_reg(struct kvm_vcpu *vcpu, bool aarch64_only)
+static void test_vm_ftr_id_regs(struct kvm_vcpu *vcpu, bool aarch64_only)
{
uint64_t masks[KVM_ARM_FEATURE_ID_RANGE_SIZE];
struct reg_mask_range range = {
@@ -476,7 +476,7 @@ int main(void)
ksft_set_plan(ftr_cnt);
- test_user_set_reg(vcpu, aarch64_only);
+ test_vm_ftr_id_regs(vcpu, aarch64_only);
test_guest_reg_read(vcpu);
kvm_vm_free(vm);
--
2.45.0.rc1.225.g2a3ae87e7f-goog
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 5/7] KVM: selftests: Store expected register value in set_id_regs
2024-05-02 23:35 [PATCH 0/7] KVM: arm64: Don't clobber CLIDR and MPIDR across vCPU reset Oliver Upton
` (3 preceding siblings ...)
2024-05-02 23:35 ` [PATCH 4/7] KVM: selftests: Rename helper in set_id_regs to imply VM scope Oliver Upton
@ 2024-05-02 23:35 ` Oliver Upton
2024-05-02 23:35 ` [PATCH 6/7] KVM: arm64: Test that feature ID regs survive a reset Oliver Upton
` (2 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Oliver Upton @ 2024-05-02 23:35 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu, kvm,
Oliver Upton
Rather than comparing against what is returned by the ioctl, store
expected values for the feature ID registers in a table and compare with
that instead.
This will prove useful for subsequent tests involving vCPU reset.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
.../selftests/kvm/aarch64/set_id_regs.c | 27 ++++++++++++-------
1 file changed, 18 insertions(+), 9 deletions(-)
diff --git a/tools/testing/selftests/kvm/aarch64/set_id_regs.c b/tools/testing/selftests/kvm/aarch64/set_id_regs.c
index 3d0ce49f9b78..7c8d33fa2ae6 100644
--- a/tools/testing/selftests/kvm/aarch64/set_id_regs.c
+++ b/tools/testing/selftests/kvm/aarch64/set_id_regs.c
@@ -327,8 +327,8 @@ uint64_t get_invalid_value(const struct reg_ftr_bits *ftr_bits, uint64_t ftr)
return ftr;
}
-static void test_reg_set_success(struct kvm_vcpu *vcpu, uint64_t reg,
- const struct reg_ftr_bits *ftr_bits)
+static uint64_t test_reg_set_success(struct kvm_vcpu *vcpu, uint64_t reg,
+ const struct reg_ftr_bits *ftr_bits)
{
uint8_t shift = ftr_bits->shift;
uint64_t mask = ftr_bits->mask;
@@ -346,6 +346,8 @@ static void test_reg_set_success(struct kvm_vcpu *vcpu, uint64_t reg,
vcpu_set_reg(vcpu, reg, val);
vcpu_get_reg(vcpu, reg, &new_val);
TEST_ASSERT_EQ(new_val, val);
+
+ return new_val;
}
static void test_reg_set_fail(struct kvm_vcpu *vcpu, uint64_t reg,
@@ -374,6 +376,14 @@ static void test_reg_set_fail(struct kvm_vcpu *vcpu, uint64_t reg,
TEST_ASSERT_EQ(val, old_val);
}
+static uint64_t test_reg_vals[KVM_ARM_FEATURE_ID_RANGE_SIZE];
+
+#define encoding_to_range_idx(encoding) \
+ KVM_ARM_FEATURE_ID_RANGE_IDX(sys_reg_Op0(encoding), sys_reg_Op1(encoding), \
+ sys_reg_CRn(encoding), sys_reg_CRm(encoding), \
+ sys_reg_Op2(encoding))
+
+
static void test_vm_ftr_id_regs(struct kvm_vcpu *vcpu, bool aarch64_only)
{
uint64_t masks[KVM_ARM_FEATURE_ID_RANGE_SIZE];
@@ -398,9 +408,7 @@ static void test_vm_ftr_id_regs(struct kvm_vcpu *vcpu, bool aarch64_only)
int idx;
/* Get the index to masks array for the idreg */
- idx = KVM_ARM_FEATURE_ID_RANGE_IDX(sys_reg_Op0(reg_id), sys_reg_Op1(reg_id),
- sys_reg_CRn(reg_id), sys_reg_CRm(reg_id),
- sys_reg_Op2(reg_id));
+ idx = encoding_to_range_idx(reg_id);
for (int j = 0; ftr_bits[j].type != FTR_END; j++) {
/* Skip aarch32 reg on aarch64 only system, since they are RAZ/WI. */
@@ -414,7 +422,9 @@ static void test_vm_ftr_id_regs(struct kvm_vcpu *vcpu, bool aarch64_only)
TEST_ASSERT_EQ(masks[idx] & ftr_bits[j].mask, ftr_bits[j].mask);
test_reg_set_fail(vcpu, reg, &ftr_bits[j]);
- test_reg_set_success(vcpu, reg, &ftr_bits[j]);
+
+ test_reg_vals[idx] = test_reg_set_success(vcpu, reg,
+ &ftr_bits[j]);
ksft_test_result_pass("%s\n", ftr_bits[j].name);
}
@@ -425,7 +435,6 @@ static void test_guest_reg_read(struct kvm_vcpu *vcpu)
{
bool done = false;
struct ucall uc;
- uint64_t val;
while (!done) {
vcpu_run(vcpu);
@@ -436,8 +445,8 @@ static void test_guest_reg_read(struct kvm_vcpu *vcpu)
break;
case UCALL_SYNC:
/* Make sure the written values are seen by guest */
- vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(uc.args[2]), &val);
- TEST_ASSERT_EQ(val, uc.args[3]);
+ TEST_ASSERT_EQ(test_reg_vals[encoding_to_range_idx(uc.args[2])],
+ uc.args[3]);
break;
case UCALL_DONE:
done = true;
--
2.45.0.rc1.225.g2a3ae87e7f-goog
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 6/7] KVM: arm64: Test that feature ID regs survive a reset
2024-05-02 23:35 [PATCH 0/7] KVM: arm64: Don't clobber CLIDR and MPIDR across vCPU reset Oliver Upton
` (4 preceding siblings ...)
2024-05-02 23:35 ` [PATCH 5/7] KVM: selftests: Store expected register value in set_id_regs Oliver Upton
@ 2024-05-02 23:35 ` Oliver Upton
2024-05-02 23:35 ` [PATCH 7/7] KVM: selftests: Test vCPU-scoped feature ID registers Oliver Upton
2024-05-09 17:45 ` [PATCH 0/7] KVM: arm64: Don't clobber CLIDR and MPIDR across vCPU reset Marc Zyngier
7 siblings, 0 replies; 12+ messages in thread
From: Oliver Upton @ 2024-05-02 23:35 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu, kvm,
Oliver Upton
One of the expectations with feature ID registers is that their values
survive a vCPU reset. Start testing that.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
.../selftests/kvm/aarch64/set_id_regs.c | 41 +++++++++++++++----
1 file changed, 33 insertions(+), 8 deletions(-)
diff --git a/tools/testing/selftests/kvm/aarch64/set_id_regs.c b/tools/testing/selftests/kvm/aarch64/set_id_regs.c
index 7c8d33fa2ae6..24b248c78f5d 100644
--- a/tools/testing/selftests/kvm/aarch64/set_id_regs.c
+++ b/tools/testing/selftests/kvm/aarch64/set_id_regs.c
@@ -457,13 +457,36 @@ static void test_guest_reg_read(struct kvm_vcpu *vcpu)
}
}
+static void test_assert_id_reg_unchanged(struct kvm_vcpu *vcpu, uint32_t encoding)
+{
+ size_t idx = encoding_to_range_idx(encoding);
+ uint64_t observed;
+
+ vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(encoding), &observed);
+ TEST_ASSERT_EQ(test_reg_vals[idx], observed);
+}
+
+static void test_reset_preserves_id_regs(struct kvm_vcpu *vcpu)
+{
+ /*
+ * Calls KVM_ARM_VCPU_INIT behind the scenes, which will do an
+ * architectural reset of the vCPU.
+ */
+ aarch64_vcpu_setup(vcpu, NULL);
+
+ for (int i = 0; i < ARRAY_SIZE(test_regs); i++)
+ test_assert_id_reg_unchanged(vcpu, test_regs[i].reg);
+
+ ksft_test_result_pass("%s\n", __func__);
+}
+
int main(void)
{
struct kvm_vcpu *vcpu;
struct kvm_vm *vm;
bool aarch64_only;
uint64_t val, el0;
- int ftr_cnt;
+ int test_cnt;
TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES));
@@ -476,18 +499,20 @@ int main(void)
ksft_print_header();
- ftr_cnt = ARRAY_SIZE(ftr_id_aa64dfr0_el1) + ARRAY_SIZE(ftr_id_dfr0_el1) +
- ARRAY_SIZE(ftr_id_aa64isar0_el1) + ARRAY_SIZE(ftr_id_aa64isar1_el1) +
- ARRAY_SIZE(ftr_id_aa64isar2_el1) + ARRAY_SIZE(ftr_id_aa64pfr0_el1) +
- ARRAY_SIZE(ftr_id_aa64mmfr0_el1) + ARRAY_SIZE(ftr_id_aa64mmfr1_el1) +
- ARRAY_SIZE(ftr_id_aa64mmfr2_el1) + ARRAY_SIZE(ftr_id_aa64zfr0_el1) -
- ARRAY_SIZE(test_regs);
+ test_cnt = ARRAY_SIZE(ftr_id_aa64dfr0_el1) + ARRAY_SIZE(ftr_id_dfr0_el1) +
+ ARRAY_SIZE(ftr_id_aa64isar0_el1) + ARRAY_SIZE(ftr_id_aa64isar1_el1) +
+ ARRAY_SIZE(ftr_id_aa64isar2_el1) + ARRAY_SIZE(ftr_id_aa64pfr0_el1) +
+ ARRAY_SIZE(ftr_id_aa64mmfr0_el1) + ARRAY_SIZE(ftr_id_aa64mmfr1_el1) +
+ ARRAY_SIZE(ftr_id_aa64mmfr2_el1) + ARRAY_SIZE(ftr_id_aa64zfr0_el1) -
+ ARRAY_SIZE(test_regs) + 1;
- ksft_set_plan(ftr_cnt);
+ ksft_set_plan(test_cnt);
test_vm_ftr_id_regs(vcpu, aarch64_only);
test_guest_reg_read(vcpu);
+ test_reset_preserves_id_regs(vcpu);
+
kvm_vm_free(vm);
ksft_finished();
--
2.45.0.rc1.225.g2a3ae87e7f-goog
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 7/7] KVM: selftests: Test vCPU-scoped feature ID registers
2024-05-02 23:35 [PATCH 0/7] KVM: arm64: Don't clobber CLIDR and MPIDR across vCPU reset Oliver Upton
` (5 preceding siblings ...)
2024-05-02 23:35 ` [PATCH 6/7] KVM: arm64: Test that feature ID regs survive a reset Oliver Upton
@ 2024-05-02 23:35 ` Oliver Upton
2024-05-09 17:45 ` [PATCH 0/7] KVM: arm64: Don't clobber CLIDR and MPIDR across vCPU reset Marc Zyngier
7 siblings, 0 replies; 12+ messages in thread
From: Oliver Upton @ 2024-05-02 23:35 UTC (permalink / raw)
To: kvmarm
Cc: Marc Zyngier, James Morse, Suzuki K Poulose, Zenghui Yu, kvm,
Oliver Upton
Test that CLIDR_EL1 and MPIDR_EL1 are modifiable from userspace and that
the values are preserved across a vCPU reset like the other feature ID
registers.
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
.../selftests/kvm/aarch64/set_id_regs.c | 53 ++++++++++++++++++-
1 file changed, 52 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/kvm/aarch64/set_id_regs.c b/tools/testing/selftests/kvm/aarch64/set_id_regs.c
index 24b248c78f5d..a7de39fa2a0a 100644
--- a/tools/testing/selftests/kvm/aarch64/set_id_regs.c
+++ b/tools/testing/selftests/kvm/aarch64/set_id_regs.c
@@ -457,6 +457,53 @@ static void test_guest_reg_read(struct kvm_vcpu *vcpu)
}
}
+/* Politely lifted from arch/arm64/include/asm/cache.h */
+/* Ctypen, bits[3(n - 1) + 2 : 3(n - 1)], for n = 1 to 7 */
+#define CLIDR_CTYPE_SHIFT(level) (3 * (level - 1))
+#define CLIDR_CTYPE_MASK(level) (7 << CLIDR_CTYPE_SHIFT(level))
+#define CLIDR_CTYPE(clidr, level) \
+ (((clidr) & CLIDR_CTYPE_MASK(level)) >> CLIDR_CTYPE_SHIFT(level))
+
+static void test_clidr(struct kvm_vcpu *vcpu)
+{
+ uint64_t clidr;
+ int level;
+
+ vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_CLIDR_EL1), &clidr);
+
+ /* find the first empty level in the cache hierarchy */
+ for (level = 1; level < 7; level++) {
+ if (!CLIDR_CTYPE(clidr, level))
+ break;
+ }
+
+ /*
+ * If you have a mind-boggling 7 levels of cache, congratulations, you
+ * get to fix this.
+ */
+ TEST_ASSERT(level <= 7, "can't find an empty level in cache hierarchy");
+
+ /* stick in a unified cache level */
+ clidr |= BIT(2) << CLIDR_CTYPE_SHIFT(level);
+
+ vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_CLIDR_EL1), clidr);
+ test_reg_vals[encoding_to_range_idx(SYS_CLIDR_EL1)] = clidr;
+}
+
+static void test_vcpu_ftr_id_regs(struct kvm_vcpu *vcpu)
+{
+ u64 val;
+
+ test_clidr(vcpu);
+
+ vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MPIDR_EL1), &val);
+ val++;
+ vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MPIDR_EL1), val);
+
+ test_reg_vals[encoding_to_range_idx(SYS_MPIDR_EL1)] = val;
+ ksft_test_result_pass("%s\n", __func__);
+}
+
static void test_assert_id_reg_unchanged(struct kvm_vcpu *vcpu, uint32_t encoding)
{
size_t idx = encoding_to_range_idx(encoding);
@@ -477,6 +524,8 @@ static void test_reset_preserves_id_regs(struct kvm_vcpu *vcpu)
for (int i = 0; i < ARRAY_SIZE(test_regs); i++)
test_assert_id_reg_unchanged(vcpu, test_regs[i].reg);
+ test_assert_id_reg_unchanged(vcpu, SYS_CLIDR_EL1);
+
ksft_test_result_pass("%s\n", __func__);
}
@@ -504,11 +553,13 @@ int main(void)
ARRAY_SIZE(ftr_id_aa64isar2_el1) + ARRAY_SIZE(ftr_id_aa64pfr0_el1) +
ARRAY_SIZE(ftr_id_aa64mmfr0_el1) + ARRAY_SIZE(ftr_id_aa64mmfr1_el1) +
ARRAY_SIZE(ftr_id_aa64mmfr2_el1) + ARRAY_SIZE(ftr_id_aa64zfr0_el1) -
- ARRAY_SIZE(test_regs) + 1;
+ ARRAY_SIZE(test_regs) + 2;
ksft_set_plan(test_cnt);
test_vm_ftr_id_regs(vcpu, aarch64_only);
+ test_vcpu_ftr_id_regs(vcpu);
+
test_guest_reg_read(vcpu);
test_reset_preserves_id_regs(vcpu);
--
2.45.0.rc1.225.g2a3ae87e7f-goog
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH 0/7] KVM: arm64: Don't clobber CLIDR and MPIDR across vCPU reset
2024-05-02 23:35 [PATCH 0/7] KVM: arm64: Don't clobber CLIDR and MPIDR across vCPU reset Oliver Upton
` (6 preceding siblings ...)
2024-05-02 23:35 ` [PATCH 7/7] KVM: selftests: Test vCPU-scoped feature ID registers Oliver Upton
@ 2024-05-09 17:45 ` Marc Zyngier
7 siblings, 0 replies; 12+ messages in thread
From: Marc Zyngier @ 2024-05-09 17:45 UTC (permalink / raw)
To: kvmarm, Oliver Upton; +Cc: James Morse, Suzuki K Poulose, Zenghui Yu, kvm
On Thu, 02 May 2024 23:35:22 +0000, Oliver Upton wrote:
> When I was reviewing Sebastian's CTR_EL0 series it occurred to me that
> our handling of feature ID registers local to a vCPU is quite poor.
>
> For VM-wide feature ID registers we ensure they get initialized once for
> the lifetime of a VM. On the other hand, vCPU-local feature ID registers
> get re-initialized on every vCPU reset, potentially clobbering the
> values userspace set up.
>
> [...]
Applied to next, thanks!
[1/7] KVM: arm64: Rename is_id_reg() to imply VM scope
commit: 592efc606b549692c7ba6c8f232c4e6028d0382c
[2/7] KVM: arm64: Reset VM feature ID regs from kvm_reset_sys_regs()
commit: 44cbe80b7616702b0a7443853feff2459a599b33
[3/7] KVM: arm64: Only reset vCPU-scoped feature ID regs once
commit: e016333745c70c960e02b4a9b123c807669d2b22
[4/7] KVM: selftests: Rename helper in set_id_regs to imply VM scope
commit: 41ee9b33e94a2457e936f0cc7423005902f36b67
[5/7] KVM: selftests: Store expected register value in set_id_regs
commit: 46247a317f403e52d51928f0e1b675cffbd1046c
[6/7] KVM: arm64: Test that feature ID regs survive a reset
commit: 07eabd8a528f511f6bbef3b5cbe5d9f90c5bb4ea
[7/7] KVM: selftests: Test vCPU-scoped feature ID registers
commit: 606af8293cd8b962ad7cc51326bfd974c2fa1f91
Cheers,
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply [flat|nested] 12+ messages in thread