Commit 5fa2ea07 authored by Alexander Gordeev's avatar Alexander Gordeev Committed by Vasily Gorbik

s390/mcck: move register validation to C code

This update partially reverts commit 3037a52f ("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: default avatarAlexander Gordeev <agordeev@linux.ibm.com>
Reviewed-by: default avatarHeiko Carstens <hca@linux.ibm.com>
Signed-off-by: default avatarHeiko Carstens <hca@linux.ibm.com>
Signed-off-by: default avatarVasily Gorbik <gor@linux.ibm.com>
parent 9f744abb
...@@ -21,8 +21,6 @@ ...@@ -21,8 +21,6 @@
#define CR0_INTERRUPT_KEY_SUBMASK BIT(63 - 57) #define CR0_INTERRUPT_KEY_SUBMASK BIT(63 - 57)
#define CR0_MEASUREMENT_ALERT_SUBMASK BIT(63 - 58) #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_32 BIT(63 - 32)
#define CR14_UNUSED_33 BIT(63 - 33) #define CR14_UNUSED_33 BIT(63 - 33)
#define CR14_CHANNEL_REPORT_SUBMASK BIT(63 - 35) #define CR14_CHANNEL_REPORT_SUBMASK BIT(63 - 35)
......
...@@ -14,8 +14,6 @@ ...@@ -14,8 +14,6 @@
#include <linux/pgtable.h> #include <linux/pgtable.h>
#include <asm/idle.h> #include <asm/idle.h>
#include <asm/gmap.h> #include <asm/gmap.h>
#include <asm/nmi.h>
#include <asm/setup.h>
#include <asm/stacktrace.h> #include <asm/stacktrace.h>
int main(void) int main(void)
...@@ -108,7 +106,6 @@ int main(void) ...@@ -108,7 +106,6 @@ int main(void)
OFFSET(__LC_LAST_UPDATE_CLOCK, lowcore, last_update_clock); OFFSET(__LC_LAST_UPDATE_CLOCK, lowcore, last_update_clock);
OFFSET(__LC_INT_CLOCK, lowcore, int_clock); OFFSET(__LC_INT_CLOCK, lowcore, int_clock);
OFFSET(__LC_MCCK_CLOCK, lowcore, mcck_clock); OFFSET(__LC_MCCK_CLOCK, lowcore, mcck_clock);
OFFSET(__LC_CLOCK_COMPARATOR, lowcore, clock_comparator);
OFFSET(__LC_BOOT_CLOCK, lowcore, boot_clock); OFFSET(__LC_BOOT_CLOCK, lowcore, boot_clock);
OFFSET(__LC_CURRENT, lowcore, current_task); OFFSET(__LC_CURRENT, lowcore, current_task);
OFFSET(__LC_KERNEL_STACK, lowcore, kernel_stack); OFFSET(__LC_KERNEL_STACK, lowcore, kernel_stack);
...@@ -145,9 +142,6 @@ int main(void) ...@@ -145,9 +142,6 @@ int main(void)
OFFSET(__LC_CREGS_SAVE_AREA, lowcore, cregs_save_area); OFFSET(__LC_CREGS_SAVE_AREA, lowcore, cregs_save_area);
OFFSET(__LC_PGM_TDB, lowcore, pgm_tdb); OFFSET(__LC_PGM_TDB, lowcore, pgm_tdb);
BLANK(); BLANK();
/* extended machine check save area */
OFFSET(__MCESA_GS_SAVE_AREA, mcesa, guarded_storage_save_area);
BLANK();
/* gmap/sie offsets */ /* gmap/sie offsets */
OFFSET(__GMAP_ASCE, gmap, asce); OFFSET(__GMAP_ASCE, gmap, asce);
OFFSET(__SIE_PROG0C, kvm_s390_sie_block, prog0c); OFFSET(__SIE_PROG0C, kvm_s390_sie_block, prog0c);
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include <asm/alternative-asm.h> #include <asm/alternative-asm.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/cache.h> #include <asm/cache.h>
#include <asm/ctl_reg.h>
#include <asm/dwarf.h> #include <asm/dwarf.h>
#include <asm/errno.h> #include <asm/errno.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
...@@ -506,8 +505,6 @@ ENTRY(mcck_int_handler) ...@@ -506,8 +505,6 @@ ENTRY(mcck_int_handler)
BPOFF BPOFF
la %r1,4095 # validate r1 la %r1,4095 # validate r1
spt __LC_CPU_TIMER_SAVE_AREA-4095(%r1) # validate cpu timer 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 lmg %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r1)# validate gprs
lg %r12,__LC_CURRENT lg %r12,__LC_CURRENT
lmg %r8,%r9,__LC_MCK_OLD_PSW lmg %r8,%r9,__LC_MCK_OLD_PSW
...@@ -518,41 +515,7 @@ ENTRY(mcck_int_handler) ...@@ -518,41 +515,7 @@ ENTRY(mcck_int_handler)
la %r14,4095 la %r14,4095
lctlg %c0,%c15,__LC_CREGS_SAVE_AREA-4095(%r14) # validate ctl regs lctlg %c0,%c15,__LC_CREGS_SAVE_AREA-4095(%r14) # validate ctl regs
ptlb ptlb
lg %r11,__LC_MCESAD-4095(%r14) # extended machine check save area lghi %r14,__LC_CPU_TIMER_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) mvc __LC_MCCK_ENTER_TIMER(8),0(%r14)
TSTMSK __LC_MCCK_CODE,MCCK_CODE_CPU_TIMER_VALID TSTMSK __LC_MCCK_CODE,MCCK_CODE_CPU_TIMER_VALID
jo 3f jo 3f
......
...@@ -189,12 +189,16 @@ void noinstr s390_handle_mcck(void) ...@@ -189,12 +189,16 @@ void noinstr s390_handle_mcck(void)
* returns 0 if all required registers are available * returns 0 if all required registers are available
* returns 1 otherwise * 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; union ctlreg2 cr2;
int kill_task; int kill_task;
u64 zero;
kill_task = 0; kill_task = 0;
zero = 0;
if (!mci.gr) { if (!mci.gr) {
/* /*
...@@ -217,35 +221,89 @@ static int notrace s390_check_registers(union mci mci, int umode) ...@@ -217,35 +221,89 @@ static int notrace s390_check_registers(union mci mci, int umode)
if (!test_cpu_flag(CIF_FPU)) if (!test_cpu_flag(CIF_FPU))
kill_task = 1; kill_task = 1;
} }
fpt_save_area = &S390_lowcore.floating_pt_save_area;
if (!mci.fc) { if (!mci.fc) {
/* /*
* Floating point control register can't be restored. * Floating point control register can't be restored.
* If the kernel currently uses the floating pointer * If the kernel currently uses the floating pointer
* registers and needs the FPC register the system is * registers and needs the FPC register the system is
* stopped. If the process has its floating pointer * 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) if (S390_lowcore.fpu_flags & KERNEL_FPC)
s390_handle_damage(); s390_handle_damage();
asm volatile(
" lfpc %0\n"
:
: "Q" (zero));
if (!test_cpu_flag(CIF_FPU)) if (!test_cpu_flag(CIF_FPU))
kill_task = 1; 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) { if (!mci.vr) {
/* /*
* Vector registers can't be restored. If the kernel * Vector registers can't be restored. If the kernel
* currently uses vector registers the system is * currently uses vector registers the system is
* stopped. If the process has its vector registers * 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) if (S390_lowcore.fpu_flags & KERNEL_VXR)
s390_handle_damage(); s390_handle_damage();
if (!test_cpu_flag(CIF_FPU)) if (!test_cpu_flag(CIF_FPU))
kill_task = 1; 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) { if (!mci.ar) {
/* /*
* Access registers have unknown contents. * Access registers have unknown contents.
...@@ -253,7 +311,7 @@ static int notrace s390_check_registers(union mci mci, int umode) ...@@ -253,7 +311,7 @@ static int notrace s390_check_registers(union mci mci, int umode)
*/ */
kill_task = 1; kill_task = 1;
} }
/* Check guarded storage registers */ /* Validate guarded storage registers */
cr2.val = S390_lowcore.cregs_save_area[2]; cr2.val = S390_lowcore.cregs_save_area[2];
if (cr2.gse) { if (cr2.gse) {
if (!mci.gs) { if (!mci.gs) {
...@@ -263,15 +321,26 @@ static int notrace s390_check_registers(union mci mci, int umode) ...@@ -263,15 +321,26 @@ static int notrace s390_check_registers(union mci mci, int umode)
* It has to be terminated. * It has to be terminated.
*/ */
kill_task = 1; 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) if (!mci.ms || !mci.pm || !mci.ia)
kill_task = 1; kill_task = 1;
return kill_task; return kill_task;
} }
NOKPROBE_SYMBOL(s390_check_registers); NOKPROBE_SYMBOL(s390_validate_registers);
/* /*
* Backup the guest's machine check info to its description block * 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) ...@@ -369,7 +438,7 @@ int notrace s390_do_machine_check(struct pt_regs *regs)
s390_handle_damage(); 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 * Couldn't restore all register contents for the
* user space process -> mark task for termination. * user space process -> mark task for termination.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment