Hi,
On 12/20/19 3:05 PM, Mark Rutland wrote:
> When KVM injects an exception into a guest, it generates the CPSR value
> from scratch, configuring CPSR.{M,A,I,T,E}, and setting all other
> bits to zero.
>
> This isn't correct, as the architecture specifies that some CPSR bits
> are (conditionally) cleared or set upon an exception, and others are
> unchanged from the original context.
>
> This patch adds logic to match the architectural behaviour. To make this
> simple to follow/audit/extend, documentation references are provided,
> and bits are configured in order of their layout in SPSR_EL2. This
This patch applies equally well to 32bit KVM, where we have a SPSR register.
> layout can be seen in the diagram on ARM DDI 0487E.a page C5-426.
>
> Signed-off-by: Mark Rutland <[email protected]>
> Cc: Alexandru Elisei <[email protected]>
> Cc: Drew Jones <[email protected]>
> Cc: James Morse <[email protected]>
> Cc: Julien Thierry <[email protected]>
> Cc: Marc Zyngier <[email protected]>
> Cc: Peter Maydell <[email protected]>
> Cc: Suzuki K Poulose <[email protected]>
> Cc: Will Deacon <[email protected]>
> Cc: [email protected]
> ---
> arch/arm/include/asm/kvm_emulate.h | 12 ++++
> arch/arm64/include/asm/ptrace.h | 1 +
> virt/kvm/arm/aarch32.c | 110
> +++++++++++++++++++++++++++++++++----
> 3 files changed, 113 insertions(+), 10 deletions(-)
>
> diff --git a/arch/arm/include/asm/kvm_emulate.h
> b/arch/arm/include/asm/kvm_emulate.h
> index 40002416efec..dee2567661ed 100644
> --- a/arch/arm/include/asm/kvm_emulate.h
> +++ b/arch/arm/include/asm/kvm_emulate.h
> @@ -14,13 +14,25 @@
> #include <asm/cputype.h>
>
> /* arm64 compatibility macros */
> +#define PSR_AA32_MODE_FIQ FIQ_MODE
> +#define PSR_AA32_MODE_SVC SVC_MODE
> #define PSR_AA32_MODE_ABT ABT_MODE
> #define PSR_AA32_MODE_UND UND_MODE
> #define PSR_AA32_T_BIT PSR_T_BIT
> +#define PSR_AA32_F_BIT PSR_F_BIT
> #define PSR_AA32_I_BIT PSR_I_BIT
> #define PSR_AA32_A_BIT PSR_A_BIT
> #define PSR_AA32_E_BIT PSR_E_BIT
> #define PSR_AA32_IT_MASK PSR_IT_MASK
> +#define PSR_AA32_GE_MASK 0x000f0000
> +#define PSR_AA32_PAN_BIT 0x00400000
> +#define PSR_AA32_SSBS_BIT 0x00800000
> +#define PSR_AA32_DIT_BIT 0x01000000
This is actually PSR_J_BIT on arm. Maybe it's worth defining it like that to
make the overlap obvious.
> +#define PSR_AA32_Q_BIT PSR_Q_BIT
> +#define PSR_AA32_V_BIT PSR_V_BIT
> +#define PSR_AA32_C_BIT PSR_C_BIT
> +#define PSR_AA32_Z_BIT PSR_Z_BIT
> +#define PSR_AA32_N_BIT PSR_N_BIT
>
> unsigned long *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num);
>
> diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h
> index fbebb411ae20..bf57308fcd63 100644
> --- a/arch/arm64/include/asm/ptrace.h
> +++ b/arch/arm64/include/asm/ptrace.h
> @@ -62,6 +62,7 @@
> #define PSR_AA32_I_BIT 0x00000080
> #define PSR_AA32_A_BIT 0x00000100
> #define PSR_AA32_E_BIT 0x00000200
> +#define PSR_AA32_PAN_BIT 0x00400000
> #define PSR_AA32_SSBS_BIT 0x00800000
> #define PSR_AA32_DIT_BIT 0x01000000
> #define PSR_AA32_Q_BIT 0x08000000
> diff --git a/virt/kvm/arm/aarch32.c b/virt/kvm/arm/aarch32.c
> index c4c57ba99e90..17bcde5c2451 100644
> --- a/virt/kvm/arm/aarch32.c
> +++ b/virt/kvm/arm/aarch32.c
> @@ -10,6 +10,7 @@
> * Author: Christoffer Dall <[email protected]>
> */
>
> +#include <linux/bits.h>
> #include <linux/kvm_host.h>
> #include <asm/kvm_emulate.h>
> #include <asm/kvm_hyp.h>
> @@ -28,22 +29,111 @@ static const u8 return_offsets[8][2] = {
> [7] = { 4, 4 }, /* FIQ, unused */
> };
>
> +/*
> + * When an exception is taken, most CPSR fields are left unchanged in the
> + * handler. However, some are explicitly overridden (e.g. M[4:0]).
> + *
> + * The SPSR/SPSR_ELx layouts differ, and the below is intended to work with
> + * either format. Note: SPSR.J bit doesn't exist in SPSR_ELx, but this bit
> was
> + * obsoleted by the ARMv7 virtualization extensions and is RES0.
> + *
> + * For the SPSR layout seen from AArch32, see:
> + * - ARM DDI 0406C.d, page B1-1148
> + * - ARM DDI 0487E.a, page G8-6264
> + *
> + * For the SPSR_ELx layout for AArch32 seen from AArch64, see:
> + * - ARM DDI 0487E.a, page C5-426
> + *
> + * Here we manipulate the fields in order of the AArch32 SPSR_ELx layout,
> from
> + * MSB to LSB.
> + */
> +static unsigned long get_except32_cpsr(struct kvm_vcpu *vcpu, u32 mode)
> +{
> + u32 sctlr = vcpu_cp15(vcpu, c1_SCTLR);
> + unsigned long old, new;
> +
> + old = *vcpu_cpsr(vcpu);
> + new = 0;
> +
> + new |= (old & PSR_AA32_N_BIT);
> + new |= (old & PSR_AA32_Z_BIT);
> + new |= (old & PSR_AA32_C_BIT);
> + new |= (old & PSR_AA32_V_BIT);
> + new |= (old & PSR_AA32_Q_BIT);
> +
> + // CPSR.IT[7:0] are set to zero upon any exception
> + // See ARM DDI 0487E.a, section G1.12.3
> + // See ARM DDI 0406C.d, section B1.8.3
> +
> + new |= (old & PSR_AA32_DIT_BIT);
> +
> + // CPSR.SSBS is set to SCTLR.DSSBS upon any exception
> + // See ARM DDI 0487E.a, page G8-6244
> + if (sctlr & BIT(31))
> + new |= PSR_AA32_SSBS_BIT;
> +
> + // CPSR.PAN is unchanged unless overridden by SCTLR.SPAN
> + // See ARM DDI 0487E.a, page G8-6246
> + new |= (old & PSR_AA32_PAN_BIT);
> + if (sctlr & BIT(23))
> + new |= PSR_AA32_PAN_BIT;
PSTATE.PAN is unconditionally set when SCTLR.SPAN is clear.
> +
> + // SS does not exist in AArch32, so ignore
> +
> + // CPSR.IL is set to zero upon any exception
> + // See ARM DDI 0487E.a, page G1-5527
> +
> + new |= (old & PSR_AA32_GE_MASK);
> +
> + // CPSR.IT[7:0] are set to zero upon any exception
> + // See prior comment above
> +
> + // CPSR.E is set to SCTLR.EE upon any exception
> + // See ARM DDI 0487E.a, page G8-6245
> + // See ARM DDI 0406C.d, page B4-1701
> + if (sctlr & BIT(25))
> + new |= PSR_AA32_E_BIT;
> +
> + // CPSR.A is unchanged upon an exception to Undefined, Supervisor
> + // CPSR.A is set upon an exception to other modes
> + // See ARM DDI 0487E.a, pages G1-5515 to G1-5516
> + // See ARM DDI 0406C.d, page B1-1182
> + new |= (old & PSR_AA32_A_BIT);
> + if (mode != PSR_AA32_MODE_UND && mode != PSR_AA32_MODE_SVC)
> + new |= PSR_AA32_A_BIT;
> +
> + // CPSR.I is set upon any exception
> + // See ARM DDI 0487E.a, pages G1-5515 to G1-5516
> + // See ARM DDI 0406C.d, page B1-1182
> + new |= PSR_AA32_I_BIT;
> +
> + // CPSR.F is set upon an exception to FIQ
> + // CPSR.F is unchanged upon an exception to other modes
> + // See ARM DDI 0487E.a, pages G1-5515 to G1-5516
> + // See ARM DDI 0406C.d, page B1-1182
> + new |= (old & PSR_AA32_F_BIT);
> + if (mode == PSR_AA32_MODE_FIQ)
> + new |= PSR_AA32_F_BIT;
> +
> + // CPSR.T is set to SCTLR.TE upon any exception
> + // See ARM DDI 0487E.a, page G8-5514
> + // See ARM DDI 0406C.d, page B1-1181
> + if (sctlr & BIT(30))
> + new |= PSR_AA32_T_BIT;
> +
> + new |= mode;
> +
> + return new;
> +}
> +
> static void prepare_fault32(struct kvm_vcpu *vcpu, u32 mode, u32 vect_offset)
> {
> - unsigned long cpsr;
> unsigned long new_spsr_value = *vcpu_cpsr(vcpu);
> bool is_thumb = (new_spsr_value & PSR_AA32_T_BIT);
> u32 return_offset = return_offsets[vect_offset >> 2][is_thumb];
> u32 sctlr = vcpu_cp15(vcpu, c1_SCTLR);
>
> - cpsr = mode | PSR_AA32_I_BIT;
> -
> - if (sctlr & (1 << 30))
> - cpsr |= PSR_AA32_T_BIT;
> - if (sctlr & (1 << 25))
> - cpsr |= PSR_AA32_E_BIT;
> -
> - *vcpu_cpsr(vcpu) = cpsr;
> + *vcpu_cpsr(vcpu) = get_except32_cpsr(vcpu, mode);
>
> /* Note: These now point to the banked copies */
> vcpu_write_spsr(vcpu, new_spsr_value);
> @@ -84,7 +174,7 @@ static void inject_abt32(struct kvm_vcpu *vcpu, bool
> is_pabt,
> fsr = &vcpu_cp15(vcpu, c5_DFSR);
> }
>
> - prepare_fault32(vcpu, PSR_AA32_MODE_ABT | PSR_AA32_A_BIT, vect_offset);
> + prepare_fault32(vcpu, PSR_AA32_MODE_ABT, vect_offset);
>
> *far = addr;
>
I've also checked that the ARM documentation references match the code.
Thanks,
Alex
_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm