#include <asm/kvm_emulate.h>
 #include <asm/esr.h>
 
-#define PSTATE_FAULT_BITS_64   (PSR_MODE_EL1h | PSR_A_BIT | PSR_F_BIT | \
-                                PSR_I_BIT | PSR_D_BIT)
-
 #define CURRENT_EL_SP_EL0_VECTOR       0x0
 #define CURRENT_EL_SP_ELx_VECTOR       0x200
 #define LOWER_EL_AArch64_VECTOR                0x400
        return vcpu_read_sys_reg(vcpu, VBAR_EL1) + exc_offset + type;
 }
 
+/*
+ * When an exception is taken, most PSTATE fields are left unchanged in the
+ * handler. However, some are explicitly overridden (e.g. M[4:0]). Luckily all
+ * of the inherited bits have the same position in the AArch64/AArch32 SPSR_ELx
+ * layouts, so we don't need to shuffle these for exceptions from AArch32 EL0.
+ *
+ * For the SPSR_ELx layout for AArch64, see ARM DDI 0487E.a page C5-429.
+ * For the SPSR_ELx layout for AArch32, see ARM DDI 0487E.a page C5-426.
+ *
+ * Here we manipulate the fields in order of the AArch64 SPSR_ELx layout, from
+ * MSB to LSB.
+ */
+static unsigned long get_except64_pstate(struct kvm_vcpu *vcpu)
+{
+       unsigned long sctlr = vcpu_read_sys_reg(vcpu, SCTLR_EL1);
+       unsigned long old, new;
+
+       old = *vcpu_cpsr(vcpu);
+       new = 0;
+
+       new |= (old & PSR_N_BIT);
+       new |= (old & PSR_Z_BIT);
+       new |= (old & PSR_C_BIT);
+       new |= (old & PSR_V_BIT);
+
+       // TODO: TCO (if/when ARMv8.5-MemTag is exposed to guests)
+
+       new |= (old & PSR_DIT_BIT);
+
+       // PSTATE.UAO is set to zero upon any exception to AArch64
+       // See ARM DDI 0487E.a, page D5-2579.
+
+       // PSTATE.PAN is unchanged unless SCTLR_ELx.SPAN == 0b0
+       // SCTLR_ELx.SPAN is RES1 when ARMv8.1-PAN is not implemented
+       // See ARM DDI 0487E.a, page D5-2578.
+       new |= (old & PSR_PAN_BIT);
+       if (!(sctlr & SCTLR_EL1_SPAN))
+               new |= PSR_PAN_BIT;
+
+       // PSTATE.SS is set to zero upon any exception to AArch64
+       // See ARM DDI 0487E.a, page D2-2452.
+
+       // PSTATE.IL is set to zero upon any exception to AArch64
+       // See ARM DDI 0487E.a, page D1-2306.
+
+       // PSTATE.SSBS is set to SCTLR_ELx.DSSBS upon any exception to AArch64
+       // See ARM DDI 0487E.a, page D13-3258
+       if (sctlr & SCTLR_ELx_DSSBS)
+               new |= PSR_SSBS_BIT;
+
+       // PSTATE.BTYPE is set to zero upon any exception to AArch64
+       // See ARM DDI 0487E.a, pages D1-2293 to D1-2294.
+
+       new |= PSR_D_BIT;
+       new |= PSR_A_BIT;
+       new |= PSR_I_BIT;
+       new |= PSR_F_BIT;
+
+       new |= PSR_MODE_EL1h;
+
+       return new;
+}
+
 static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr)
 {
        unsigned long cpsr = *vcpu_cpsr(vcpu);
        vcpu_write_elr_el1(vcpu, *vcpu_pc(vcpu));
        *vcpu_pc(vcpu) = get_except_vector(vcpu, except_type_sync);
 
-       *vcpu_cpsr(vcpu) = PSTATE_FAULT_BITS_64;
+       *vcpu_cpsr(vcpu) = get_except64_pstate(vcpu);
        vcpu_write_spsr(vcpu, cpsr);
 
        vcpu_write_sys_reg(vcpu, addr, FAR_EL1);
        vcpu_write_elr_el1(vcpu, *vcpu_pc(vcpu));
        *vcpu_pc(vcpu) = get_except_vector(vcpu, except_type_sync);
 
-       *vcpu_cpsr(vcpu) = PSTATE_FAULT_BITS_64;
+       *vcpu_cpsr(vcpu) = get_except64_pstate(vcpu);
        vcpu_write_spsr(vcpu, cpsr);
 
        /*