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

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
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -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)
+0 −6
Original line number Diff line number Diff line
@@ -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);
+1 −38
Original line number Diff line number Diff line
@@ -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
+77 −8
Original line number Diff line number Diff line
@@ -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));
	}

	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.
			 * 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;
		}
	}
	/* Check if access registers are valid */
		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)\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.