From 4b7a6bf402bd064605c287eecadc493ccf2d4897 Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Tue, 21 Jul 2015 11:18:45 +0100 Subject: [PATCH] target-arm: kvm: Differentiate registers based on write-back levels Some registers like the CNTVCT register should only be written to the kernel as part of machine initialization or on vmload operations, but never during runtime, as this can potentially make time go backwards or create inconsistent time observations between VCPUs. Introduce a list of registers that should not be written back at runtime and check this list on syncing the register state to the KVM state. Signed-off-by: Christoffer Dall Message-id: 1437046488-10773-1-git-send-email-christoffer.dall@linaro.org [PMM: tweaked a few comments, added the new argument to the stub write_list_to_kvmstate() in target-arm/kvm-stub.c] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target-arm/kvm-stub.c | 2 +- target-arm/kvm.c | 6 +++++- target-arm/kvm32.c | 30 +++++++++++++++++++++++++++++- target-arm/kvm64.c | 30 +++++++++++++++++++++++++++++- target-arm/kvm_arm.h | 12 +++++++++++- target-arm/machine.c | 2 +- 6 files changed, 76 insertions(+), 6 deletions(-) diff --git a/target-arm/kvm-stub.c b/target-arm/kvm-stub.c index cd1849f72cb..db2edc2c4cc 100644 --- a/target-arm/kvm-stub.c +++ b/target-arm/kvm-stub.c @@ -17,7 +17,7 @@ bool write_kvmstate_to_list(ARMCPU *cpu) abort(); } -bool write_list_to_kvmstate(ARMCPU *cpu) +bool write_list_to_kvmstate(ARMCPU *cpu, int level) { abort(); } diff --git a/target-arm/kvm.c b/target-arm/kvm.c index 548bfd768de..b278542085d 100644 --- a/target-arm/kvm.c +++ b/target-arm/kvm.c @@ -409,7 +409,7 @@ bool write_kvmstate_to_list(ARMCPU *cpu) return ok; } -bool write_list_to_kvmstate(ARMCPU *cpu) +bool write_list_to_kvmstate(ARMCPU *cpu, int level) { CPUState *cs = CPU(cpu); int i; @@ -421,6 +421,10 @@ bool write_list_to_kvmstate(ARMCPU *cpu) uint32_t v32; int ret; + if (kvm_arm_cpreg_level(regidx) > level) { + continue; + } + r.id = regidx; switch (regidx & KVM_REG_SIZE_MASK) { case KVM_REG_SIZE_U32: diff --git a/target-arm/kvm32.c b/target-arm/kvm32.c index d7e7d6877f8..421ce0ea0d0 100644 --- a/target-arm/kvm32.c +++ b/target-arm/kvm32.c @@ -153,6 +153,34 @@ bool kvm_arm_reg_syncs_via_cpreg_list(uint64_t regidx) } } +typedef struct CPRegStateLevel { + uint64_t regidx; + int level; +} CPRegStateLevel; + +/* All coprocessor registers not listed in the following table are assumed to + * be of the level KVM_PUT_RUNTIME_STATE. If a register should be written less + * often, you must add it to this table with a state of either + * KVM_PUT_RESET_STATE or KVM_PUT_FULL_STATE. + */ +static const CPRegStateLevel non_runtime_cpregs[] = { + { KVM_REG_ARM_TIMER_CNT, KVM_PUT_FULL_STATE }, +}; + +int kvm_arm_cpreg_level(uint64_t regidx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(non_runtime_cpregs); i++) { + const CPRegStateLevel *l = &non_runtime_cpregs[i]; + if (l->regidx == regidx) { + return l->level; + } + } + + return KVM_PUT_RUNTIME_STATE; +} + #define ARM_MPIDR_HWID_BITMASK 0xFFFFFF #define ARM_CPU_ID_MPIDR 0, 0, 0, 5 @@ -367,7 +395,7 @@ int kvm_arch_put_registers(CPUState *cs, int level) * managed to update the CPUARMState with, and only allowing those * to be written back up into the kernel). */ - if (!write_list_to_kvmstate(cpu)) { + if (!write_list_to_kvmstate(cpu, level)) { return EINVAL; } diff --git a/target-arm/kvm64.c b/target-arm/kvm64.c index ac34f514987..bd60889d12e 100644 --- a/target-arm/kvm64.c +++ b/target-arm/kvm64.c @@ -139,6 +139,34 @@ bool kvm_arm_reg_syncs_via_cpreg_list(uint64_t regidx) } } +typedef struct CPRegStateLevel { + uint64_t regidx; + int level; +} CPRegStateLevel; + +/* All system registers not listed in the following table are assumed to be + * of the level KVM_PUT_RUNTIME_STATE. If a register should be written less + * often, you must add it to this table with a state of either + * KVM_PUT_RESET_STATE or KVM_PUT_FULL_STATE. + */ +static const CPRegStateLevel non_runtime_cpregs[] = { + { KVM_REG_ARM_TIMER_CNT, KVM_PUT_FULL_STATE }, +}; + +int kvm_arm_cpreg_level(uint64_t regidx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(non_runtime_cpregs); i++) { + const CPRegStateLevel *l = &non_runtime_cpregs[i]; + if (l->regidx == regidx) { + return l->level; + } + } + + return KVM_PUT_RUNTIME_STATE; +} + #define AARCH64_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \ KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x)) @@ -280,7 +308,7 @@ int kvm_arch_put_registers(CPUState *cs, int level) return ret; } - if (!write_list_to_kvmstate(cpu)) { + if (!write_list_to_kvmstate(cpu, level)) { return EINVAL; } diff --git a/target-arm/kvm_arm.h b/target-arm/kvm_arm.h index 5abd5916d19..7912d7433d9 100644 --- a/target-arm/kvm_arm.h +++ b/target-arm/kvm_arm.h @@ -68,9 +68,19 @@ int kvm_arm_init_cpreg_list(ARMCPU *cpu); */ bool kvm_arm_reg_syncs_via_cpreg_list(uint64_t regidx); +/** + * kvm_arm_cpreg_level + * regidx: KVM register index + * + * Return the level of this coprocessor/system register. Return value is + * either KVM_PUT_RUNTIME_STATE, KVM_PUT_RESET_STATE, or KVM_PUT_FULL_STATE. + */ +int kvm_arm_cpreg_level(uint64_t regidx); + /** * write_list_to_kvmstate: * @cpu: ARMCPU + * @level: the state level to sync * * For each register listed in the ARMCPU cpreg_indexes list, write * its value from the cpreg_values list into the kernel (via ioctl). @@ -83,7 +93,7 @@ bool kvm_arm_reg_syncs_via_cpreg_list(uint64_t regidx); * Note that we do not stop early on failure -- we will attempt * writing all registers in the list. */ -bool write_list_to_kvmstate(ARMCPU *cpu); +bool write_list_to_kvmstate(ARMCPU *cpu, int level); /** * write_kvmstate_to_list: diff --git a/target-arm/machine.c b/target-arm/machine.c index 9eb51dfddd5..32adfe792e3 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -251,7 +251,7 @@ static int cpu_post_load(void *opaque, int version_id) } if (kvm_enabled()) { - if (!write_list_to_kvmstate(cpu)) { + if (!write_list_to_kvmstate(cpu, KVM_PUT_FULL_STATE)) { return -1; } /* Note that it's OK for the TCG side not to know about -- 2.39.5