⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mce.c

📁 xen虚拟机源代码安装包
💻 C
字号:
/* * mce.c - x86 Machine Check Exception Reporting * (c) 2002 Alan Cox <alan@redhat.com>, Dave Jones <davej@codemonkey.org.uk> */#include <xen/init.h>#include <xen/types.h>#include <xen/kernel.h>#include <xen/config.h>#include <xen/smp.h>#include <xen/errno.h>#include <asm/processor.h> #include <asm/system.h>#include "mce.h"#include "x86_mca.h"int mce_disabled = 0;unsigned int nr_mce_banks;EXPORT_SYMBOL_GPL(nr_mce_banks);	/* non-fatal.o *//* XXX For now a fixed array is used. Later this should be changed * to a dynamic allocated array with the size calculated in relation * to physical cpus present in the machine. * The more physical cpus are available, the more entries you need. */#define MAX_MCINFO	10struct mc_machine_notify {	struct mc_info mc;	uint32_t fetch_idx;	uint32_t valid;};struct mc_machine {	/* Array structure used for collecting machine check error telemetry. */	struct mc_info mc[MAX_MCINFO];	/* We handle multiple machine check reports lockless by	 * iterating through the array using the producer/consumer concept.	 */	/* Producer array index to fill with machine check error data.	 * Index must be increased atomically. */	uint32_t error_idx;	/* Consumer array index to fetch machine check error data from.	 * Index must be increased atomically. */	uint32_t fetch_idx;	/* Integer array holding the indeces of the mc array that allows         * a Dom0 to notify a DomU to re-fetch the same machine check error         * data. The notification and refetch also uses its own 	 * producer/consumer mechanism, because Dom0 may decide to not report	 * every error to the impacted DomU.	 */	struct mc_machine_notify notify[MAX_MCINFO];	/* Array index to get fetch_idx from.	 * Index must be increased atomically. */	uint32_t notifyproducer_idx;	uint32_t notifyconsumer_idx;};/* Global variable with machine check information. */struct mc_machine mc_data;/* Handle unconfigured int18 (should never happen) */static void unexpected_machine_check(struct cpu_user_regs *regs, long error_code){		printk(XENLOG_ERR "CPU#%d: Unexpected int18 (Machine Check).\n",		smp_processor_id());}/* Call the installed machine check handler for this CPU setup. */void (*machine_check_vector)(struct cpu_user_regs *regs, long error_code) = unexpected_machine_check;/* Init machine check callback handler * It is used to collect additional information provided by newer * CPU families/models without the need to duplicate the whole handler. * This avoids having many handlers doing almost nearly the same and each * with its own tweaks ands bugs. */int (*mc_callback_bank_extended)(struct mc_info *, uint16_t, uint64_t) = NULL;static void amd_mcheck_init(struct cpuinfo_x86 *ci){	switch (ci->x86) {	case 6:		amd_k7_mcheck_init(ci);		break;	case 0xf:		amd_k8_mcheck_init(ci);		break;	case 0x10:		amd_f10_mcheck_init(ci);		break;	default:		/* Assume that machine check support is available.		 * The minimum provided support is at least the K8. */		amd_k8_mcheck_init(ci);	}}/* This has to be run for each processor */void mcheck_init(struct cpuinfo_x86 *c){	if (mce_disabled == 1) {		printk(XENLOG_INFO "MCE support disabled by bootparam\n");		return;	}	if (!cpu_has(c, X86_FEATURE_MCE)) {		printk(XENLOG_INFO "CPU%i: No machine check support available\n",			smp_processor_id());		return;	}	memset(&mc_data, 0, sizeof(struct mc_machine));	switch (c->x86_vendor) {	case X86_VENDOR_AMD:		amd_mcheck_init(c);		break;	case X86_VENDOR_INTEL:#ifndef CONFIG_X86_64		if (c->x86==5)			intel_p5_mcheck_init(c);		if (c->x86==6)			intel_p6_mcheck_init(c);#endif		if (c->x86==15)			intel_p4_mcheck_init(c);		break;#ifndef CONFIG_X86_64	case X86_VENDOR_CENTAUR:		if (c->x86==5)			winchip_mcheck_init(c);		break;#endif	default:		break;	}}static void __init mcheck_disable(char *str){	mce_disabled = 1;}static void __init mcheck_enable(char *str){	mce_disabled = -1;}custom_param("nomce", mcheck_disable);custom_param("mce", mcheck_enable);#include <xen/guest_access.h>#include <asm/traps.h>struct mc_info *x86_mcinfo_getptr(void){	struct mc_info *mi;	uint32_t entry, next;	for (;;) {		entry = mc_data.error_idx;		smp_rmb();		next = entry + 1;		if (cmpxchg(&mc_data.error_idx, entry, next) == entry)			break;	}	mi = &(mc_data.mc[(entry % MAX_MCINFO)]);	BUG_ON(mc_data.error_idx < mc_data.fetch_idx);	return mi;}static int x86_mcinfo_matches_guest(const struct mc_info *mi,			const struct domain *d, const struct vcpu *v){	struct mcinfo_common *mic;	struct mcinfo_global *mig;	x86_mcinfo_lookup(mic, mi, MC_TYPE_GLOBAL);	mig = (struct mcinfo_global *)mic;	if (mig == NULL)		return 0;	if (d->domain_id != mig->mc_domid)		return 0;	if (v->vcpu_id != mig->mc_vcpuid)		return 0;	return 1;}#define x86_mcinfo_mcdata(idx) (mc_data.mc[(idx % MAX_MCINFO)])static struct mc_info *x86_mcinfo_getfetchptr(uint32_t *fetch_idx,				const struct domain *d, const struct vcpu *v){	struct mc_info *mi;	/* This function is called from the fetch hypercall with	 * the mc_lock spinlock held. Thus, no need for locking here.	 */	mi = &(x86_mcinfo_mcdata(mc_data.fetch_idx));	if ((d != dom0) && !x86_mcinfo_matches_guest(mi, d, v)) {		/* Bogus domU command detected. */		*fetch_idx = 0;		return NULL;	}	*fetch_idx = mc_data.fetch_idx;	mc_data.fetch_idx++;	BUG_ON(mc_data.fetch_idx > mc_data.error_idx);	return mi;}static void x86_mcinfo_marknotified(struct xen_mc_notifydomain *mc_notifydomain){	struct mc_machine_notify *mn;	struct mcinfo_common *mic = NULL;	struct mcinfo_global *mig;	struct domain *d;	int i;	/* This function is called from the notifier hypercall with	 * the mc_notify_lock spinlock held. Thus, no need for locking here.	 */	/* First invalidate entries for guests that disappeared after	 * notification (e.g. shutdown/crash). This step prevents the	 * notification array from filling up with stalling/leaking entries.	 */	for (i = mc_data.notifyconsumer_idx; i < mc_data.notifyproducer_idx; i++) {		mn = &(mc_data.notify[(i % MAX_MCINFO)]);		x86_mcinfo_lookup(mic, &mn->mc, MC_TYPE_GLOBAL);		BUG_ON(mic == NULL);		mig = (struct mcinfo_global *)mic;		d = get_domain_by_id(mig->mc_domid);		if (d == NULL) {			/* Domain does not exist. */			mn->valid = 0;		}		if ((!mn->valid) && (i == mc_data.notifyconsumer_idx))			mc_data.notifyconsumer_idx++;	}	/* Now put in the error telemetry. Since all error data fetchable	 * by domUs are uncorrectable errors, they are very important.	 * So we dump them before overriding them. When a guest takes that long,	 * then we can assume something bad already happened (crash, hang, etc.)	 */	mn = &(mc_data.notify[(mc_data.notifyproducer_idx % MAX_MCINFO)]);	if (mn->valid) {		struct mcinfo_common *mic = NULL;		struct mcinfo_global *mig;		/* To not loose the information, we dump it. */		x86_mcinfo_lookup(mic, &mn->mc, MC_TYPE_GLOBAL);		BUG_ON(mic == NULL);		mig = (struct mcinfo_global *)mic;		printk(XENLOG_WARNING "Domain ID %u was notified by Dom0 to "			"fetch machine check error telemetry. But Domain ID "			"did not do that in time.\n",			mig->mc_domid);		x86_mcinfo_dump(&mn->mc);	}	memcpy(&mn->mc, &(x86_mcinfo_mcdata(mc_notifydomain->fetch_idx)),		sizeof(struct mc_info));	mn->fetch_idx = mc_notifydomain->fetch_idx;	mn->valid = 1;	mc_data.notifyproducer_idx++;	/* By design there can never be more notifies than machine check errors.	 * If that ever happens, then we hit a bug. */	BUG_ON(mc_data.notifyproducer_idx > mc_data.fetch_idx);	BUG_ON(mc_data.notifyconsumer_idx > mc_data.notifyproducer_idx);}static struct mc_info *x86_mcinfo_getnotifiedptr(uint32_t *fetch_idx,				const struct domain *d, const struct vcpu *v){	struct mc_machine_notify *mn = NULL;	uint32_t i;	int found;	/* This function is called from the fetch hypercall with	 * the mc_notify_lock spinlock held. Thus, no need for locking here.	 */	/* The notifier data is filled in the order guests get notified, but	 * guests may fetch them in a different order. That's why we need	 * the game with valid/invalid entries. */	found = 0;	for (i = mc_data.notifyconsumer_idx; i < mc_data.notifyproducer_idx; i++) {		mn = &(mc_data.notify[(i % MAX_MCINFO)]);		if (!mn->valid) {			if (i == mc_data.notifyconsumer_idx)				mc_data.notifyconsumer_idx++;			continue;		}		if (x86_mcinfo_matches_guest(&mn->mc, d, v)) {			found = 1;			break;		}	}	if (!found) {		/* This domain has never been notified. This must be		 * a bogus domU command. */		*fetch_idx = 0;		return NULL;	}	BUG_ON(mn == NULL);	*fetch_idx = mn->fetch_idx;	mn->valid = 0;	BUG_ON(mc_data.notifyconsumer_idx > mc_data.notifyproducer_idx);	return &mn->mc;}void x86_mcinfo_clear(struct mc_info *mi){	memset(mi, 0, sizeof(struct mc_info));	x86_mcinfo_nentries(mi) = 0;}int x86_mcinfo_add(struct mc_info *mi, void *mcinfo){	int i;	unsigned long end1, end2;	struct mcinfo_common *mic, *mic_base, *mic_index;	mic = (struct mcinfo_common *)mcinfo;	mic_index = mic_base = x86_mcinfo_first(mi);	/* go to first free entry */	for (i = 0; i < x86_mcinfo_nentries(mi); i++) {		mic_index = x86_mcinfo_next(mic_index);	}	/* check if there is enough size */	end1 = (unsigned long)((uint8_t *)mic_base + sizeof(struct mc_info));	end2 = (unsigned long)((uint8_t *)mic_index + mic->size);	if (end1 < end2)		return -ENOSPC; /* No space. Can't add entry. */	/* there's enough space. add entry. */	memcpy(mic_index, mic, mic->size);	x86_mcinfo_nentries(mi)++;	return 0;}/* Dump machine check information in a format, * mcelog can parse. This is used only when * Dom0 does not take the notification. */void x86_mcinfo_dump(struct mc_info *mi){	struct mcinfo_common *mic = NULL;	struct mcinfo_global *mc_global;	struct mcinfo_bank *mc_bank;	/* first print the global info */	x86_mcinfo_lookup(mic, mi, MC_TYPE_GLOBAL);	if (mic == NULL)		return;	mc_global = (struct mcinfo_global *)mic;	if (mc_global->mc_flags & MC_FLAG_UNCORRECTABLE) {		printk(XENLOG_WARNING			"CPU%d: Machine Check Exception: %16"PRIx64"\n",			mc_global->mc_coreid, mc_global->mc_gstatus);	} else {		printk(XENLOG_WARNING "MCE: The hardware reports a non "			"fatal, correctable incident occured on "			"CPU %d.\n",			mc_global->mc_coreid);	}	/* then the bank information */	x86_mcinfo_lookup(mic, mi, MC_TYPE_BANK); /* finds the first entry */	do {		if (mic == NULL)			return;		if (mic->type != MC_TYPE_BANK)			continue;		mc_bank = (struct mcinfo_bank *)mic;			printk(XENLOG_WARNING "Bank %d: %16"PRIx64,			mc_bank->mc_bank,			mc_bank->mc_status);		if (mc_bank->mc_status & MCi_STATUS_MISCV)			printk("[%16"PRIx64"]", mc_bank->mc_misc);		if (mc_bank->mc_status & MCi_STATUS_ADDRV)			printk(" at %16"PRIx64, mc_bank->mc_addr);		printk("\n");		mic = x86_mcinfo_next(mic); /* next entry */		if ((mic == NULL) || (mic->size == 0))			break;	} while (1);}/* Machine Check Architecture Hypercall */long do_mca(XEN_GUEST_HANDLE(xen_mc_t) u_xen_mc){	long ret = 0;	struct xen_mc curop, *op = &curop;	struct vcpu *v = current;	struct domain *domU;	struct xen_mc_fetch *mc_fetch;	struct xen_mc_notifydomain *mc_notifydomain;	struct mc_info *mi;	uint32_t flags;	uint32_t fetch_idx;        uint16_t vcpuid;	/* Use a different lock for the notify hypercall in order to allow	 * a DomU to fetch mc data while Dom0 notifies another DomU. */	static DEFINE_SPINLOCK(mc_lock);	static DEFINE_SPINLOCK(mc_notify_lock);	if ( copy_from_guest(op, u_xen_mc, 1) )		return -EFAULT;	if ( op->interface_version != XEN_MCA_INTERFACE_VERSION )		return -EACCES;	switch ( op->cmd ) {	case XEN_MC_fetch:		/* This hypercall is for any domain */		mc_fetch = &op->u.mc_fetch;		switch (mc_fetch->flags) {		case XEN_MC_CORRECTABLE:			/* But polling mode is Dom0 only, because			 * correctable errors are reported to Dom0 only */			if ( !IS_PRIV(v->domain) )				return -EPERM;			break;		case XEN_MC_TRAP:			break;		default:			return -EFAULT;		}		flags = XEN_MC_OK;		spin_lock(&mc_lock);		if ( IS_PRIV(v->domain) ) {			/* this must be Dom0. So a notify hypercall			 * can't have happened before. */			mi = x86_mcinfo_getfetchptr(&fetch_idx, dom0, v);		} else {			/* Hypercall comes from an unprivileged domain */			domU = v->domain;			if (guest_has_trap_callback(dom0, 0, TRAP_machine_check)) {				/* Dom0 must have notified this DomU before				 * via the notify hypercall. */				mi = x86_mcinfo_getnotifiedptr(&fetch_idx, domU, v);			} else {				/* Xen notified the DomU. */				mi = x86_mcinfo_getfetchptr(&fetch_idx, domU, v);			}		}		if (mi) {			memcpy(&mc_fetch->mc_info, mi,				sizeof(struct mc_info));		} else {			/* There is no data for a bogus DomU command. */			flags |= XEN_MC_NODATA;			memset(&mc_fetch->mc_info, 0, sizeof(struct mc_info));		}		mc_fetch->flags = flags;		mc_fetch->fetch_idx = fetch_idx;		if ( copy_to_guest(u_xen_mc, op, 1) )			ret = -EFAULT;		spin_unlock(&mc_lock);		break;	case XEN_MC_notifydomain:		/* This hypercall is for Dom0 only */		if ( !IS_PRIV(v->domain) )			return -EPERM;		spin_lock(&mc_notify_lock);		mc_notifydomain = &op->u.mc_notifydomain;		domU = get_domain_by_id(mc_notifydomain->mc_domid);		vcpuid = mc_notifydomain->mc_vcpuid;		if ((domU == NULL) || (domU == dom0)) {			/* It's not possible to notify a non-existent domain			 * or the dom0. */			spin_unlock(&mc_notify_lock);			return -EACCES;		}		if (vcpuid >= MAX_VIRT_CPUS) {			/* It's not possible to notify a vcpu, Xen can't			 * assign to a domain. */			spin_unlock(&mc_notify_lock);			return -EACCES;		}		mc_notifydomain->flags = XEN_MC_OK;		mi = &(x86_mcinfo_mcdata(mc_notifydomain->fetch_idx));		if (!x86_mcinfo_matches_guest(mi, domU, domU->vcpu[vcpuid])) {			/* The error telemetry is not for the guest, Dom0			 * wants to notify. */			mc_notifydomain->flags |= XEN_MC_NOMATCH;		} else if ( guest_has_trap_callback(domU, vcpuid,						TRAP_machine_check) )		{			/* Send notification */			if ( send_guest_trap(domU, vcpuid, TRAP_machine_check) )				mc_notifydomain->flags |= XEN_MC_NOTDELIVERED;		} else			mc_notifydomain->flags |= XEN_MC_CANNOTHANDLE;#ifdef DEBUG		/* sanity check - these two flags are mutually exclusive */		if ((flags & XEN_MC_CANNOTHANDLE) && (flags & XEN_MC_NOTDELIVERED))			BUG();#endif		if ( copy_to_guest(u_xen_mc, op, 1) )			ret = -EFAULT;		if (ret == 0) {			x86_mcinfo_marknotified(mc_notifydomain);		}		spin_unlock(&mc_notify_lock);		break;	}	return ret;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -