OSDN Git Service

s390/mcck: move register validation to C code
authorAlexander Gordeev <agordeev@linux.ibm.com>
Fri, 18 Jun 2021 06:17:18 +0000 (08:17 +0200)
committerVasily Gorbik <gor@linux.ibm.com>
Mon, 5 Jul 2021 10:44:23 +0000 (12:44 +0200)
This update partially reverts commit 3037a52f9846 ("s390/nmi:
do register validation as early as possible").

Storage error checks and control registers validation are left
in the assembler code, since correct ASCEs and page tables are
required to enable DAT - which is done before the C handler is
entered.

System damage, kernel instruction address and PSW MWP checks
are left in the assembler code as well, since there is no way
to proceed if one of these checks is failed.

The getcpu vdso syscall reads CPU number from the programmable
field of the TOD clock. Disregard the TOD programmable register
validity bit and load the CPU number into the TOD programmable
field unconditionally.

Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
Reviewed-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
arch/s390/include/asm/ctl_reg.h
arch/s390/kernel/asm-offsets.c
arch/s390/kernel/entry.S
arch/s390/kernel/nmi.c

index ed5efbb..adc0179 100644 (file)
@@ -21,8 +21,6 @@
 #define CR0_INTERRUPT_KEY_SUBMASK      BIT(63 - 57)
 #define CR0_MEASUREMENT_ALERT_SUBMASK  BIT(63 - 58)
 
-#define CR2_GUARDED_STORAGE            BIT(63 - 59)
-
 #define CR14_UNUSED_32                 BIT(63 - 32)
 #define CR14_UNUSED_33                 BIT(63 - 33)
 #define CR14_CHANNEL_REPORT_SUBMASK    BIT(63 - 35)
index f53605a..77ff213 100644 (file)
@@ -14,8 +14,6 @@
 #include <linux/pgtable.h>
 #include <asm/idle.h>
 #include <asm/gmap.h>
-#include <asm/nmi.h>
-#include <asm/setup.h>
 #include <asm/stacktrace.h>
 
 int main(void)
@@ -108,7 +106,6 @@ 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);
@@ -145,9 +142,6 @@ 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 8f72a8f..5a2f70c 100644 (file)
@@ -14,7 +14,6 @@
 #include <asm/alternative-asm.h>
 #include <asm/processor.h>
 #include <asm/cache.h>
-#include <asm/ctl_reg.h>
 #include <asm/dwarf.h>
 #include <asm/errno.h>
 #include <asm/ptrace.h>
@@ -506,8 +505,6 @@ ENTRY(mcck_int_handler)
        BPOFF
        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
        lmg     %r8,%r9,__LC_MCK_OLD_PSW
@@ -518,41 +515,7 @@ ENTRY(mcck_int_handler)
        la      %r14,4095
        lctlg   %c0,%c15,__LC_CREGS_SAVE_AREA-4095(%r14) # validate ctl regs
        ptlb
-       lg      %r11,__LC_MCESAD-4095(%r14) # 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
+       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
index fdb5d23..20f8e18 100644 (file)
@@ -189,12 +189,16 @@ void noinstr s390_handle_mcck(void)
  * returns 0 if all required registers are available
  * returns 1 otherwise
  */
-static int notrace s390_check_registers(union mci mci, int umode)
+static int notrace s390_validate_registers(union mci mci, int umode)
 {
+       struct mcesa *mcesa;
+       void *fpt_save_area;
        union ctlreg2 cr2;
        int kill_task;
+       u64 zero;
 
        kill_task = 0;
+       zero = 0;
 
        if (!mci.gr) {
                /*
@@ -217,35 +221,89 @@ static int notrace s390_check_registers(union mci mci, int umode)
                if (!test_cpu_flag(CIF_FPU))
                        kill_task = 1;
        }
+       fpt_save_area = &S390_lowcore.floating_pt_save_area;
        if (!mci.fc) {
                /*
                 * Floating point control register can't be restored.
                 * 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.
+                * registers loaded it is terminated. Otherwise the
+                * FPC is just validated.
                 */
                if (S390_lowcore.fpu_flags & KERNEL_FPC)
                        s390_handle_damage();
+               asm volatile(
+                       "       lfpc    %0\n"
+                       :
+                       : "Q" (zero));
                if (!test_cpu_flag(CIF_FPU))
                        kill_task = 1;
+       } else {
+               asm volatile(
+                       "       lfpc    %0\n"
+                       :
+                       : "Q" (S390_lowcore.fpt_creg_save_area));
        }
 
-       if (MACHINE_HAS_VX) {
+       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 (!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.
+                        * loaded it is terminated. Otherwise just validate
+                        * the registers.
                         */
                        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);
        }
-       /* Check if access registers are valid */
+       /* Validate access registers */
+       asm volatile(
+               "       lam     0,15,0(%0)\n"
+               :
+               : "a" (&S390_lowcore.access_regs_save_area)
+               : "memory");
        if (!mci.ar) {
                /*
                 * Access registers have unknown contents.
@@ -253,7 +311,7 @@ static int notrace s390_check_registers(union mci mci, int umode)
                 */
                kill_task = 1;
        }
-       /* Check guarded storage registers */
+       /* Validate guarded storage registers */
        cr2.val = S390_lowcore.cregs_save_area[2];
        if (cr2.gse) {
                if (!mci.gs) {
@@ -263,15 +321,26 @@ static int notrace s390_check_registers(union mci mci, int umode)
                         * It has to be terminated.
                         */
                        kill_task = 1;
+               } else {
+                       load_gs_cb((struct gs_cb *)mcesa->guarded_storage_save_area);
                }
        }
+       /*
+        * The getcpu vdso syscall reads CPU number from the programmable
+        * field of the TOD clock. Disregard the TOD programmable register
+        * validity bit and load the CPU number into the TOD programmable
+        * field unconditionally.
+        */
+       set_tod_programmable_field(raw_smp_processor_id());
+       /* Validate clock comparator register */
+       set_clock_comparator(S390_lowcore.clock_comparator);
 
        if (!mci.ms || !mci.pm || !mci.ia)
                kill_task = 1;
 
        return kill_task;
 }
-NOKPROBE_SYMBOL(s390_check_registers);
+NOKPROBE_SYMBOL(s390_validate_registers);
 
 /*
  * Backup the guest's machine check info to its description block
@@ -369,7 +438,7 @@ int notrace s390_do_machine_check(struct pt_regs *regs)
                        s390_handle_damage();
                }
        }
-       if (s390_check_registers(mci, user_mode(regs))) {
+       if (s390_validate_registers(mci, user_mode(regs))) {
                /*
                 * Couldn't restore all register contents for the
                 * user space process -> mark task for termination.