/* * The initial machine check extended save area for the boot CPU. * It will be replaced on the boot CPU reinit with an allocated * structure. The structure is required for machine check happening * early in the boot process.
*/ staticstruct mcesa boot_mcesa __aligned(MCESA_MAX_SIZE);
void __init nmi_alloc_mcesa_early(u64 *mcesad)
{ if (!nmi_needs_mcesa()) return;
*mcesad = __pa(&boot_mcesa); if (cpu_has_gs())
*mcesad |= ilog2(MCESA_MAX_SIZE);
}
int nmi_alloc_mcesa(u64 *mcesad)
{ unsignedlong size; void *origin;
*mcesad = 0; if (!nmi_needs_mcesa()) return 0;
size = cpu_has_gs() ? MCESA_MAX_SIZE : MCESA_MIN_SIZE;
origin = kmalloc(size, GFP_KERNEL); if (!origin) return -ENOMEM; /* The pointer is stored with mcesa_bits ORed in */
kmemleak_not_leak(origin);
*mcesad = __pa(origin); if (cpu_has_gs())
*mcesad |= ilog2(MCESA_MAX_SIZE); return 0;
}
/* * Disable low address protection and make machine check new PSW a * disabled wait PSW. Any additional machine check cannot be handled.
*/
local_ctl_store(0, &cr0.reg);
cr0_new = cr0;
cr0_new.lap = 0;
local_ctl_load(0, &cr0_new.reg);
psw_save = lc->mcck_new_psw;
psw_bits(lc->mcck_new_psw).io = 0;
psw_bits(lc->mcck_new_psw).ext = 0;
psw_bits(lc->mcck_new_psw).wait = 1;
nmi_print_info();
/* * Restore machine check new PSW and control register 0 to original * values. This makes possible system dump analysis easier.
*/
lc->mcck_new_psw = psw_save;
local_ctl_load(0, &cr0.reg);
disabled_wait(); while (1);
}
NOKPROBE_SYMBOL(s390_handle_damage);
/* * Main machine check handler function. Will be called with interrupts disabled * and machine checks enabled.
*/ void s390_handle_mcck(void)
{ struct mcck_struct mcck; unsignedlong mflags;
/* * Disable machine checks and get the current state of accumulated * machine checks. Afterwards delete the old state and enable machine * checks again.
*/
local_mcck_save(mflags);
mcck = *this_cpu_ptr(&cpu_mcck);
memset(this_cpu_ptr(&cpu_mcck), 0, sizeof(mcck));
local_mcck_restore(mflags);
if (mcck.channel_report)
crw_handle_channel_report(); /* * A warning may remain for a prolonged period on the bare iron. * (actually until the machine is powered off, or the problem is gone) * So we just stop listening for the WARNING MCH and avoid continuously * being interrupted. One caveat is however, that we must do this per * processor and cannot use the smp version of ctl_clear_bit(). * On VM we only get one interrupt per virtally presented machinecheck. * Though one suffices, we may get one interrupt per (virtual) cpu.
*/ if (mcck.warning) { /* WARNING pending ? */ staticint mchchk_wng_posted = 0;
/* Use single cpu clear, as we cannot handle smp here. */
local_ctl_clear_bit(14, CR14_WARNING_SUBMASK_BIT); if (xchg(&mchchk_wng_posted, 1) == 0)
kill_cad_pid(SIGPWR, 1);
} if (mcck.stp_queue)
stp_queue_work(); if (mcck.kill_task) {
printk(KERN_EMERG "mcck: Terminating task because of machine " "malfunction (code 0x%016lx).\n", mcck.mcck_code);
printk(KERN_EMERG "mcck: task: %s, pid: %d.\n",
current->comm, current->pid); if (is_global_init(current))
panic("mcck: Attempting to kill init!\n");
do_send_sig_info(SIGKILL, SEND_SIG_PRIV, current, PIDTYPE_PID);
}
}
/** * nmi_registers_valid - verify if registers are valid * @mci: machine check interruption code * * Inspect a machine check interruption code and verify if all required * registers are valid. For some registers the corresponding validity bit is * ignored and the registers are set to the expected value. * Returns true if all registers are valid, otherwise false.
*/ staticbool notrace nmi_registers_valid(union mci mci)
{ union ctlreg2 cr2;
/* * The getcpu vdso syscall reads the 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()); /* * Set the clock comparator register to the next expected value.
*/
set_clock_comparator(get_lowcore()->clock_comparator); if (!mci.gr || !mci.fp || !mci.fc) returnfalse; /* * The vector validity must only be checked if not running a * KVM guest. For KVM guests the machine check is forwarded by * KVM and it is the responsibility of the guest to take * appropriate actions. The host vector or FPU values have been * saved by KVM and will be restored by KVM.
*/ if (!mci.vr && !test_cpu_flag(CIF_MCCK_GUEST)) returnfalse; if (!mci.ar) returnfalse; /* * Two cases for guarded storage registers: * - machine check in kernel or userspace * - machine check while running SIE (KVM guest) * For kernel or userspace the userspace values of guarded storage * control can not be recreated, the process must be terminated. * For SIE the guest values of guarded storage can not be recreated. * This is either due to a bug or due to GS being disabled in the * guest. The guest will be notified by KVM code and the guests machine * check handling must take care of this. The host values are saved by * KVM and are not affected.
*/
cr2.reg = get_lowcore()->cregs_save_area[2]; if (cr2.gse && !mci.gs && !test_cpu_flag(CIF_MCCK_GUEST)) returnfalse; if (!mci.ms || !mci.pm || !mci.ia) returnfalse; returntrue;
}
NOKPROBE_SYMBOL(nmi_registers_valid);
/* * Backup the guest's machine check info to its description block
*/ staticvoid notrace s390_backup_mcck_info(struct pt_regs *regs)
{ struct mcck_volatile_info *mcck_backup; struct sie_page *sie_page;
/* r14 contains the sie block, which was set in sie64a */ struct kvm_s390_sie_block *sie_block = phys_to_virt(regs->gprs[14]);
/* * Nullifying exigent condition, therefore we might * retry this instruction.
*/
spin_lock(&ipd_lock);
tmp = get_tod_clock(); if (((tmp - last_ipd) >> 12) < MAX_IPD_TIME)
ipd_count++; else
ipd_count = 1;
last_ipd = tmp; if (ipd_count == MAX_IPD_COUNT)
s390_handle_damage();
spin_unlock(&ipd_lock);
} else { /* Processing damage -> stopping machine */
s390_handle_damage();
}
} if (!nmi_registers_valid(mci)) { if (!user_mode(regs))
s390_handle_damage(); /* * Couldn't restore all register contents for the * user space process -> mark task for termination.
*/
mcck->kill_task = 1;
mcck->mcck_code = mci.val;
mcck_pending = 1;
}
/* * Backup the machine check's info if it happens when the guest * is running.
*/ if (test_cpu_flag(CIF_MCCK_GUEST))
s390_backup_mcck_info(regs);
if (mci.cd) { /* Timing facility damage */
s390_handle_damage();
} if (mci.ed && mci.ec) { /* External damage */ if (lc->external_damage_code & (1U << ED_STP_SYNC))
mcck->stp_queue |= stp_sync_check(); if (lc->external_damage_code & (1U << ED_STP_ISLAND))
mcck->stp_queue |= stp_island_check();
mcck_pending = 1;
} /* * Reinject storage related machine checks into the guest if they * happen when the guest is running.
*/ if (!test_cpu_flag(CIF_MCCK_GUEST)) { /* Storage error uncorrected */ if (mci.se)
s390_handle_damage(); /* Storage key-error uncorrected */ if (mci.ke)
s390_handle_damage(); /* Storage degradation */ if (mci.ds && mci.fa)
s390_handle_damage();
} if (mci.cp) { /* Channel report word pending */
mcck->channel_report = 1;
mcck_pending = 1;
} if (mci.w) { /* Warning pending */
mcck->warning = 1;
mcck_pending = 1;
}
/* * If there are only Channel Report Pending and External Damage * machine checks, they will not be reinjected into the guest * because they refer to host conditions only.
*/
mcck_dam_code = (mci.val & MCIC_SUBCLASS_MASK); if (test_cpu_flag(CIF_MCCK_GUEST) &&
(mcck_dam_code & MCCK_CODE_NO_GUEST) != mcck_dam_code) { /* Set exit reason code for host's later handling */
*((long *)(regs->gprs[15] + __SF_SIE_REASON)) = -EINTR;
}
clear_cpu_flag(CIF_MCCK_GUEST);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.