| From ffdc7345535fa7c69b7fa8f4a46f8179801764bd Mon Sep 17 00:00:00 2001 |
| From: Mark Rutland <mark.rutland@arm.com> |
| Date: Wed, 8 Jan 2020 13:43:24 +0000 |
| Subject: [PATCH] KVM: arm/arm64: Correct AArch32 SPSR on exception entry |
| |
| commit 1cfbb484de158e378e8971ac40f3082e53ecca55 upstream. |
| |
| Confusingly, there are three SPSR layouts that a kernel may need to deal |
| with: |
| |
| (1) An AArch64 SPSR_ELx view of an AArch64 pstate |
| (2) An AArch64 SPSR_ELx view of an AArch32 pstate |
| (3) An AArch32 SPSR_* view of an AArch32 pstate |
| |
| When the KVM AArch32 support code deals with SPSR_{EL2,HYP}, it's either |
| dealing with #2 or #3 consistently. On arm64 the PSR_AA32_* definitions |
| match the AArch64 SPSR_ELx view, and on arm the PSR_AA32_* definitions |
| match the AArch32 SPSR_* view. |
| |
| However, when we inject an exception into an AArch32 guest, we have to |
| synthesize the AArch32 SPSR_* that the guest will see. Thus, an AArch64 |
| host needs to synthesize layout #3 from layout #2. |
| |
| This patch adds a new host_spsr_to_spsr32() helper for this, and makes |
| use of it in the KVM AArch32 support code. For arm64 we need to shuffle |
| the DIT bit around, and remove the SS bit, while for arm we can use the |
| value as-is. |
| |
| I've open-coded the bit manipulation for now to avoid having to rework |
| the existing PSR_* definitions into PSR64_AA32_* and PSR32_AA32_* |
| definitions. I hope to perform a more thorough refactoring in future so |
| that we can handle pstate view manipulation more consistently across the |
| kernel tree. |
| |
| Signed-off-by: Mark Rutland <mark.rutland@arm.com> |
| Signed-off-by: Marc Zyngier <maz@kernel.org> |
| Reviewed-by: Alexandru Elisei <alexandru.elisei@arm.com> |
| Cc: stable@vger.kernel.org |
| Link: https://lore.kernel.org/r/20200108134324.46500-4-mark.rutland@arm.com |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/arch/arm/include/asm/kvm_emulate.h b/arch/arm/include/asm/kvm_emulate.h |
| index 995ab1fac9fc..ca3ca44dd029 100644 |
| --- a/arch/arm/include/asm/kvm_emulate.h |
| +++ b/arch/arm/include/asm/kvm_emulate.h |
| @@ -53,6 +53,11 @@ static inline void vcpu_write_spsr(struct kvm_vcpu *vcpu, unsigned long v) |
| *__vcpu_spsr(vcpu) = v; |
| } |
| |
| +static inline unsigned long host_spsr_to_spsr32(unsigned long spsr) |
| +{ |
| + return spsr; |
| +} |
| + |
| static inline unsigned long vcpu_get_reg(struct kvm_vcpu *vcpu, |
| u8 reg_num) |
| { |
| diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h |
| index 034dadec7168..bb665043a322 100644 |
| --- a/arch/arm64/include/asm/kvm_emulate.h |
| +++ b/arch/arm64/include/asm/kvm_emulate.h |
| @@ -204,6 +204,38 @@ static inline void vcpu_write_spsr(struct kvm_vcpu *vcpu, unsigned long v) |
| vcpu_gp_regs(vcpu)->spsr[KVM_SPSR_EL1] = v; |
| } |
| |
| +/* |
| + * The layout of SPSR for an AArch32 state is different when observed from an |
| + * AArch64 SPSR_ELx or an AArch32 SPSR_*. This function generates the AArch32 |
| + * view given an AArch64 view. |
| + * |
| + * In ARM DDI 0487E.a see: |
| + * |
| + * - The AArch64 view (SPSR_EL2) in section C5.2.18, page C5-426 |
| + * - The AArch32 view (SPSR_abt) in section G8.2.126, page G8-6256 |
| + * - The AArch32 view (SPSR_und) in section G8.2.132, page G8-6280 |
| + * |
| + * Which show the following differences: |
| + * |
| + * | Bit | AA64 | AA32 | Notes | |
| + * +-----+------+------+-----------------------------| |
| + * | 24 | DIT | J | J is RES0 in ARMv8 | |
| + * | 21 | SS | DIT | SS doesn't exist in AArch32 | |
| + * |
| + * ... and all other bits are (currently) common. |
| + */ |
| +static inline unsigned long host_spsr_to_spsr32(unsigned long spsr) |
| +{ |
| + const unsigned long overlap = BIT(24) | BIT(21); |
| + unsigned long dit = !!(spsr & PSR_AA32_DIT_BIT); |
| + |
| + spsr &= ~overlap; |
| + |
| + spsr |= dit << 21; |
| + |
| + return spsr; |
| +} |
| + |
| static inline bool vcpu_mode_priv(const struct kvm_vcpu *vcpu) |
| { |
| u32 mode; |
| diff --git a/virt/kvm/arm/aarch32.c b/virt/kvm/arm/aarch32.c |
| index 773cf1439081..631d397ac81b 100644 |
| --- a/virt/kvm/arm/aarch32.c |
| +++ b/virt/kvm/arm/aarch32.c |
| @@ -129,15 +129,15 @@ static unsigned long get_except32_cpsr(struct kvm_vcpu *vcpu, u32 mode) |
| |
| static void prepare_fault32(struct kvm_vcpu *vcpu, u32 mode, u32 vect_offset) |
| { |
| - unsigned long new_spsr_value = *vcpu_cpsr(vcpu); |
| - bool is_thumb = (new_spsr_value & PSR_AA32_T_BIT); |
| + unsigned long spsr = *vcpu_cpsr(vcpu); |
| + bool is_thumb = (spsr & PSR_AA32_T_BIT); |
| u32 return_offset = return_offsets[vect_offset >> 2][is_thumb]; |
| u32 sctlr = vcpu_cp15(vcpu, c1_SCTLR); |
| |
| *vcpu_cpsr(vcpu) = get_except32_cpsr(vcpu, mode); |
| |
| /* Note: These now point to the banked copies */ |
| - vcpu_write_spsr(vcpu, new_spsr_value); |
| + vcpu_write_spsr(vcpu, host_spsr_to_spsr32(spsr)); |
| *vcpu_reg32(vcpu, 14) = *vcpu_pc(vcpu) + return_offset; |
| |
| /* Branch to exception vector */ |
| -- |
| 2.7.4 |
| |