s390/nmi: do register validation as early as possible
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Thu, 12 Oct 2017 11:24:48 +0000 (13:24 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Thu, 19 Oct 2017 15:07:40 +0000 (17:07 +0200)
The validation of the CPU registers in the machine check handler is
currently split into two parts. The first part is done at the start
of the low level mcck_int_handler function, this includes the CPU
timer register and the general purpose registers.
The second part is done a bit later in s390_do_machine_check for all
the other registers, including the control registers, floating pointer
control, vector or floating pointer registers, the access registers,
the guarded storage registers, the TOD programmable registers and the
clock comparator.

This is working fine to far but in theory a future extensions could
cause the C code to use registers that are not validated yet. A better
approach is to validate all CPU registers in "safe" assembler code
before any C function is called.

Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/include/asm/ctl_reg.h
arch/s390/include/asm/nmi.h
arch/s390/kernel/asm-offsets.c
arch/s390/kernel/entry.S
arch/s390/kernel/nmi.c

index 0095a40..4d4f35f 100644 (file)
@@ -9,6 +9,8 @@
 
 #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)
index ed41c42..7472bf3 100644 (file)
@@ -25,6 +25,9 @@
 #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__
 
index 3d42f91..1f33c01 100644 (file)
@@ -13,6 +13,7 @@
 #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
@@ -158,6 +159,7 @@ int main(void)
        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);
@@ -193,6 +195,9 @@ int main(void)
        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);
index 21900e1..9887d3e 100644 (file)
@@ -12,6 +12,7 @@
 #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>
@@ -948,15 +949,56 @@ load_fpu_regs:
  */
 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
@@ -972,9 +1014,13 @@ ENTRY(mcck_int_handler)
        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)
index 7f67796..3f3cda4 100644 (file)
@@ -184,19 +184,16 @@ void s390_handle_mcck(void)
 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) {
                /*
@@ -207,18 +204,13 @@ static int notrace s390_validate_registers(union mci mci, int umode)
                        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) {
                /*
@@ -226,7 +218,6 @@ static int notrace s390_validate_registers(union mci mci, int umode)
                 * 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();
@@ -240,72 +231,29 @@ static int notrace s390_validate_registers(union mci mci, int umode)
                 * 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.
@@ -313,55 +261,41 @@ static int notrace s390_validate_registers(union mci mci, int umode)
                 */
                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
@@ -460,7 +394,7 @@ void notrace s390_do_machine_check(struct pt_regs *regs)
                        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.