#include <linux/const.h>
 
+#define CR2_GUARDED_STORAGE            _BITUL(63 - 59)
+
 #define CR14_CHANNEL_REPORT_SUBMASK    _BITUL(63 - 35)
 #define CR14_RECOVERY_SUBMASK          _BITUL(63 - 36)
 #define CR14_DEGRADATION_SUBMASK       _BITUL(63 - 37)
 
 #define MCCK_CODE_CPU_TIMER_VALID      _BITUL(63 - 46)
 #define MCCK_CODE_PSW_MWP_VALID                _BITUL(63 - 20)
 #define MCCK_CODE_PSW_IA_VALID         _BITUL(63 - 23)
+#define MCCK_CODE_CR_VALID             _BITUL(63 - 29)
+#define MCCK_CODE_GS_VALID             _BITUL(63 - 36)
+#define MCCK_CODE_FC_VALID             _BITUL(63 - 43)
 
 #ifndef __ASSEMBLY__
 
 
 #include <asm/vdso.h>
 #include <asm/pgtable.h>
 #include <asm/gmap.h>
+#include <asm/nmi.h>
 
 /*
  * Make sure that the compiler is new enough. We want a compiler that
        OFFSET(__LC_LAST_UPDATE_CLOCK, lowcore, last_update_clock);
        OFFSET(__LC_INT_CLOCK, lowcore, int_clock);
        OFFSET(__LC_MCCK_CLOCK, lowcore, mcck_clock);
+       OFFSET(__LC_CLOCK_COMPARATOR, lowcore, clock_comparator);
        OFFSET(__LC_BOOT_CLOCK, lowcore, boot_clock);
        OFFSET(__LC_CURRENT, lowcore, current_task);
        OFFSET(__LC_KERNEL_STACK, lowcore, kernel_stack);
        OFFSET(__LC_CREGS_SAVE_AREA, lowcore, cregs_save_area);
        OFFSET(__LC_PGM_TDB, lowcore, pgm_tdb);
        BLANK();
+       /* extended machine check save area */
+       OFFSET(__MCESA_GS_SAVE_AREA, mcesa, guarded_storage_save_area);
+       BLANK();
        /* gmap/sie offsets */
        OFFSET(__GMAP_ASCE, gmap, asce);
        OFFSET(__SIE_PROG0C, kvm_s390_sie_block, prog0c);
 
 #include <linux/linkage.h>
 #include <asm/processor.h>
 #include <asm/cache.h>
+#include <asm/ctl_reg.h>
 #include <asm/errno.h>
 #include <asm/ptrace.h>
 #include <asm/thread_info.h>
  */
 ENTRY(mcck_int_handler)
        STCK    __LC_MCCK_CLOCK
-       la      %r1,4095                # revalidate r1
-       spt     __LC_CPU_TIMER_SAVE_AREA-4095(%r1)      # revalidate cpu timer
-       lmg     %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r1)# revalidate gprs
+       la      %r1,4095                # validate r1
+       spt     __LC_CPU_TIMER_SAVE_AREA-4095(%r1)      # validate cpu timer
+       sckc    __LC_CLOCK_COMPARATOR                   # validate comparator
+       lam     %a0,%a15,__LC_AREGS_SAVE_AREA-4095(%r1) # validate acrs
+       lmg     %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r1)# validate gprs
        lg      %r12,__LC_CURRENT
        larl    %r13,cleanup_critical
        lmg     %r8,%r9,__LC_MCK_OLD_PSW
        TSTMSK  __LC_MCCK_CODE,MCCK_CODE_SYSTEM_DAMAGE
        jo      .Lmcck_panic            # yes -> rest of mcck code invalid
-       lghi    %r14,__LC_CPU_TIMER_SAVE_AREA
+       TSTMSK  __LC_MCCK_CODE,MCCK_CODE_CR_VALID
+       jno     .Lmcck_panic            # control registers invalid -> panic
+       la      %r14,4095
+       lctlg   %c0,%c15,__LC_CREGS_SAVE_AREA-4095(%r14) # validate ctl regs
+       ptlb
+       lg      %r11,__LC_MCESAD        # extended machine check save area
+       nill    %r11,0xfc00             # MCESA_ORIGIN_MASK
+       TSTMSK  __LC_CREGS_SAVE_AREA+16-4095(%r14),CR2_GUARDED_STORAGE
+       jno     0f
+       TSTMSK  __LC_MCCK_CODE,MCCK_CODE_GS_VALID
+       jno     0f
+       .insn    rxy,0xe3000000004d,0,__MCESA_GS_SAVE_AREA(%r11) # LGSC
+0:     l       %r14,__LC_FP_CREG_SAVE_AREA-4095(%r14)
+       TSTMSK  __LC_MCCK_CODE,MCCK_CODE_FC_VALID
+       jo      0f
+       sr      %r14,%r14
+0:     sfpc    %r14
+       TSTMSK  __LC_MACHINE_FLAGS,MACHINE_FLAG_VX
+       jo      0f
+       lghi    %r14,__LC_FPREGS_SAVE_AREA
+       ld      %f0,0(%r14)
+       ld      %f1,8(%r14)
+       ld      %f2,16(%r14)
+       ld      %f3,24(%r14)
+       ld      %f4,32(%r14)
+       ld      %f5,40(%r14)
+       ld      %f6,48(%r14)
+       ld      %f7,56(%r14)
+       ld      %f8,64(%r14)
+       ld      %f9,72(%r14)
+       ld      %f10,80(%r14)
+       ld      %f11,88(%r14)
+       ld      %f12,96(%r14)
+       ld      %f13,104(%r14)
+       ld      %f14,112(%r14)
+       ld      %f15,120(%r14)
+       j       1f
+0:     VLM     %v0,%v15,0,%r11
+       VLM     %v16,%v31,256,%r11
+1:     lghi    %r14,__LC_CPU_TIMER_SAVE_AREA
        mvc     __LC_MCCK_ENTER_TIMER(8),0(%r14)
        TSTMSK  __LC_MCCK_CODE,MCCK_CODE_CPU_TIMER_VALID
        jo      3f
        la      %r14,__LC_LAST_UPDATE_TIMER
 2:     spt     0(%r14)
        mvc     __LC_MCCK_ENTER_TIMER(8),0(%r14)
-3:     TSTMSK  __LC_MCCK_CODE,(MCCK_CODE_PSW_MWP_VALID|MCCK_CODE_PSW_IA_VALID)
-       jno     .Lmcck_panic            # no -> skip cleanup critical
-       SWITCH_ASYNC __LC_GPREGS_SAVE_AREA+64,__LC_MCCK_ENTER_TIMER
+3:     TSTMSK  __LC_MCCK_CODE,MCCK_CODE_PSW_MWP_VALID
+       jno     .Lmcck_panic
+       tmhh    %r8,0x0001              # interrupting from user ?
+       jnz     4f
+       TSTMSK  __LC_MCCK_CODE,MCCK_CODE_PSW_IA_VALID
+       jno     .Lmcck_panic
+4:     SWITCH_ASYNC __LC_GPREGS_SAVE_AREA+64,__LC_MCCK_ENTER_TIMER
 .Lmcck_skip:
        lghi    %r14,__LC_GPREGS_SAVE_AREA+64
        stmg    %r0,%r7,__PT_R0(%r11)
 
 EXPORT_SYMBOL_GPL(s390_handle_mcck);
 
 /*
- * returns 0 if all registers could be validated
+ * returns 0 if all required registers are available
  * returns 1 otherwise
  */
-static int notrace s390_validate_registers(union mci mci, int umode)
+static int notrace s390_check_registers(union mci mci, int umode)
 {
        union ctlreg2 cr2;
        int kill_task;
-       u64 zero;
        void *fpt_save_area;
-       struct mcesa *mcesa;
 
        kill_task = 0;
-       zero = 0;
 
        if (!mci.gr) {
                /*
                        s390_handle_damage();
                kill_task = 1;
        }
-       /* Validate control registers */
+       /* Check control registers */
        if (!mci.cr) {
                /*
                 * Control registers have unknown contents.
                 * Can't recover and therefore stopping machine.
                 */
                s390_handle_damage();
-       } else {
-               asm volatile(
-                       "       lctlg   0,15,0(%0)\n"
-                       "       ptlb\n"
-                       : : "a" (&S390_lowcore.cregs_save_area) : "memory");
        }
        if (!mci.fp) {
                /*
                 * kernel currently uses floating point registers the
                 * system is stopped. If the process has its floating
                 * pointer registers loaded it is terminated.
-                * Otherwise just revalidate the registers.
                 */
                if (S390_lowcore.fpu_flags & KERNEL_VXR_V0V7)
                        s390_handle_damage();
                 * If the kernel currently uses the floating pointer
                 * registers and needs the FPC register the system is
                 * stopped. If the process has its floating pointer
-                * registers loaded it is terminated. Otherwiese the
-                * FPC is just revalidated.
+                * registers loaded it is terminated.
                 */
                if (S390_lowcore.fpu_flags & KERNEL_FPC)
                        s390_handle_damage();
-               asm volatile("lfpc %0" : : "Q" (zero));
                if (!test_cpu_flag(CIF_FPU))
                        kill_task = 1;
-       } else {
-               asm volatile("lfpc %0"
-                            : : "Q" (S390_lowcore.fpt_creg_save_area));
        }
 
-       mcesa = (struct mcesa *)(S390_lowcore.mcesad & MCESA_ORIGIN_MASK);
-       if (!MACHINE_HAS_VX) {
-               /* Validate floating point registers */
-               asm volatile(
-                       "       ld      0,0(%0)\n"
-                       "       ld      1,8(%0)\n"
-                       "       ld      2,16(%0)\n"
-                       "       ld      3,24(%0)\n"
-                       "       ld      4,32(%0)\n"
-                       "       ld      5,40(%0)\n"
-                       "       ld      6,48(%0)\n"
-                       "       ld      7,56(%0)\n"
-                       "       ld      8,64(%0)\n"
-                       "       ld      9,72(%0)\n"
-                       "       ld      10,80(%0)\n"
-                       "       ld      11,88(%0)\n"
-                       "       ld      12,96(%0)\n"
-                       "       ld      13,104(%0)\n"
-                       "       ld      14,112(%0)\n"
-                       "       ld      15,120(%0)\n"
-                       : : "a" (fpt_save_area) : "memory");
-       } else {
-               /* Validate vector registers */
-               union ctlreg0 cr0;
-
+       if (MACHINE_HAS_VX) {
                if (!mci.vr) {
                        /*
                         * Vector registers can't be restored. If the kernel
                         * currently uses vector registers the system is
                         * stopped. If the process has its vector registers
-                        * loaded it is terminated. Otherwise just revalidate
-                        * the registers.
+                        * loaded it is terminated.
                         */
                        if (S390_lowcore.fpu_flags & KERNEL_VXR)
                                s390_handle_damage();
                        if (!test_cpu_flag(CIF_FPU))
                                kill_task = 1;
                }
-               cr0.val = S390_lowcore.cregs_save_area[0];
-               cr0.afp = cr0.vx = 1;
-               __ctl_load(cr0.val, 0, 0);
-               asm volatile(
-                       "       la      1,%0\n"
-                       "       .word   0xe70f,0x1000,0x0036\n" /* vlm 0,15,0(1) */
-                       "       .word   0xe70f,0x1100,0x0c36\n" /* vlm 16,31,256(1) */
-                       : : "Q" (*(struct vx_array *) mcesa->vector_save_area)
-                       : "1");
-               __ctl_load(S390_lowcore.cregs_save_area[0], 0, 0);
        }
-       /* Validate access registers */
-       asm volatile(
-               "       lam     0,15,0(%0)"
-               : : "a" (&S390_lowcore.access_regs_save_area));
+       /* Check if access registers are valid */
        if (!mci.ar) {
                /*
                 * Access registers have unknown contents.
                 */
                kill_task = 1;
        }
-       /* Validate guarded storage registers */
+       /* Check guarded storage registers */
        cr2.val = S390_lowcore.cregs_save_area[2];
        if (cr2.gse) {
-               if (!mci.gs)
+               if (!mci.gs) {
                        /*
                         * Guarded storage register can't be restored and
                         * the current processes uses guarded storage.
                         * It has to be terminated.
                         */
                        kill_task = 1;
-               else
-                       load_gs_cb((struct gs_cb *)
-                                  mcesa->guarded_storage_save_area);
+               }
        }
-       /*
-        * We don't even try to validate the TOD register, since we simply
-        * can't write something sensible into that register.
-        */
-       /*
-        * See if we can validate the TOD programmable register with its
-        * old contents (should be zero) otherwise set it to zero.
-        */
-       if (!mci.pr)
-               asm volatile(
-                       "       sr      0,0\n"
-                       "       sckpf"
-                       : : : "0", "cc");
-       else
-               asm volatile(
-                       "       l       0,%0\n"
-                       "       sckpf"
-                       : : "Q" (S390_lowcore.tod_progreg_save_area)
-                       : "0", "cc");
-       /* Validate clock comparator register */
-       set_clock_comparator(S390_lowcore.clock_comparator);
        /* Check if old PSW is valid */
-       if (!mci.wp)
+       if (!mci.wp) {
                /*
                 * Can't tell if we come from user or kernel mode
                 * -> stopping machine.
                 */
                s390_handle_damage();
+       }
+       /* Check for invalid kernel instruction address */
+       if (!mci.ia && !umode) {
+               /*
+                * The instruction address got lost while running
+                * in the kernel -> stopping machine.
+                */
+               s390_handle_damage();
+       }
 
        if (!mci.ms || !mci.pm || !mci.ia)
                kill_task = 1;
 
        return kill_task;
 }
-NOKPROBE_SYMBOL(s390_validate_registers);
+NOKPROBE_SYMBOL(s390_check_registers);
 
 /*
  * Backup the guest's machine check info to its description block
                        s390_handle_damage();
                }
        }
-       if (s390_validate_registers(mci, user_mode(regs))) {
+       if (s390_check_registers(mci, user_mode(regs))) {
                /*
                 * Couldn't restore all register contents for the
                 * user space process -> mark task for termination.