s390/nmi: do register validation as early as possible
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>
This commit is contained in:
parent
6c81511ca1
commit
3037a52f98
|
@ -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)
|
||||
|
|
|
@ -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__
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue