Blob Blame History Raw
From: QingFeng Hao <haoqf@linux.vnet.ibm.com>
Subject: s390/kvm: fix detection of guest machine checks
Patch-mainline: v4.14-rc7
Git-commit: 0a5e2ec2647737907d267c09dc9a25fab1468865
References: FATE#324256,LTC#160417,bsc#1066327

Summary:     kvm: Robust Machine Check Handling
Description: A machine check is caused by a machine malfunction and not by data
             or instructions. The machine check could happen on CPU, I/O,
             storage etc.
             There are several kinds of machine checks, e.g. System Damage(SD),
             Instruction Processing Damage(IPD), Delayed Access Exception(DAE),
             Channel Report Pending(CRW), External Damage(ED).
             In general, they are of two types: exigent condition and
             repressible condition. The 64 bits Machine-Check-Interruption
             Code(MCIC) in lowcore is set to indicate the current machine
             check's type with the validity bits when a machine check happens.
             Meanwhile, the machine check handler set in lowcore is called to
             handle it. Through the validity bits in MCIC, the program can
             determine if the machine check could be recovered.
             The present machine check handler can handle almost all of the
             machine checks and validate the registers to recover the machine
             by the validity bits in MCIC. It can also inject the CRW machine
             check request from QEMU to the guest.
             VS1522 improves it to inject most of the host supported machine
             checks to the guest if they happen during the guest's running
             and the necessary validity bits to recover are set. Exceptions are
             the SD and Timer Facility Damage. The original handling is kept
             and they will not be injected into the guest.
             Additionally, this line item reinjects the DAE machine check
             if it happens during the guest's running instead of damage host
             because DAE could be caused by the program's using an improper
             procedure to update the DAT tables.
             The program will not inject the machine check for device hotplug
             such as CRW into the guest. For machine checks happening during
             the nested guest's(guest-3) running, guest-2 will reinject them
             into guest-3.

Upstream-Description:

             s390/kvm: fix detection of guest machine checks

             The new detection code for guest machine checks added a check based
             on %r11 to .Lcleanup_sie to distinguish between normal asynchronous
             interrupts and machine checks. But the funtion is called from the
             program check handler as well with an undefined value in %r11.

             The effect is that all program exceptions pointing to the SIE instruction
             will set the CIF_MCCK_GUEST bit. The bit stays set for the CPU until the
              next machine check comes in which will incorrectly be interpreted as a
             guest machine check.

             The simplest fix is to stop using .Lcleanup_sie in the program check
             handler and duplicate a few instructions.

             Fixes: c929500d7a5a ("s390/nmi: s390: New low level handling for machine check happening in guest")
             Cc: <stable@vger.kernel.org> # v4.13+
             Reviewed-by: Christian Borntraeger <borntraeger@de.ibm.com>
             Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>


Signed-off-by: QingFeng Hao <haoqf@linux.vnet.ibm.com>
Acked-by: Hannes Reinecke <hare@suse.com>
---
 arch/s390/kernel/entry.S |    7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

--- a/arch/s390/kernel/entry.S
+++ b/arch/s390/kernel/entry.S
@@ -508,12 +508,15 @@ ENTRY(pgm_check_handler)
 	tmhh	%r8,0x0001		# test problem state bit
 	jnz	2f			# -> fault in user space
 #if IS_ENABLED(CONFIG_KVM)
-	# cleanup critical section for sie64a
+	# cleanup critical section for program checks in sie64a
 	lgr	%r14,%r9
 	slg	%r14,BASED(.Lsie_critical_start)
 	clg	%r14,BASED(.Lsie_critical_length)
 	jhe	0f
-	brasl	%r14,.Lcleanup_sie
+	lg	%r14,__SF_EMPTY(%r15)		# get control block pointer
+	ni	__SIE_PROG0C+3(%r14),0xfe	# no longer in SIE
+	lctlg	%c1,%c1,__LC_USER_ASCE		# load primary asce
+	larl	%r9,sie_exit			# skip forward to sie_exit
 #endif
 0:	tmhh	%r8,0x4000		# PER bit set in old PSW ?
 	jnz	1f			# -> enabled, can't be a double fault