ia64/xen-unstable

changeset 10679:7c6c400f5a7d

[IA64] Add mca.c

Signed-off-by: Akio Takebe <takebe_akio@jp.fujitsu.com>
Signed-off-by: Alex Williamson <alex.williamson@hp.com>
author awilliam@xenbuild.aw
date Thu Jul 06 11:57:05 2006 -0600 (2006-07-06)
parents ac6f34b44e3f
children 80dbb6c5862b
files xen/arch/ia64/linux-xen/README.origin xen/arch/ia64/linux-xen/mca.c
line diff
     1.1 --- a/xen/arch/ia64/linux-xen/README.origin	Thu Jul 06 11:06:06 2006 -0600
     1.2 +++ b/xen/arch/ia64/linux-xen/README.origin	Thu Jul 06 11:57:05 2006 -0600
     1.3 @@ -11,6 +11,7 @@ entry.S			-> linux/arch/ia64/kernel/entr
     1.4  head.S			-> linux/arch/ia64/kernel/head.S
     1.5  hpsim_ssc.h		-> linux/arch/ia64/hp/sim/hpsim_ssc.h
     1.6  irq_ia64.c		-> linux/arch/ia64/kernel/irq_ia64.c
     1.7 +mca.c			-> linux/arch/ia64/kernel/mca.c
     1.8  mca_asm.S		-> linux/arch/ia64/kernel/mca_asm.S
     1.9  minstate.h		-> linux/arch/ia64/kernel/minstate.h
    1.10  mm_contig.c		-> linux/arch/ia64/mm/contig.c
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/xen/arch/ia64/linux-xen/mca.c	Thu Jul 06 11:57:05 2006 -0600
     2.3 @@ -0,0 +1,1470 @@
     2.4 +/*
     2.5 + * File:	mca.c
     2.6 + * Purpose:	Generic MCA handling layer
     2.7 + *
     2.8 + * Updated for latest kernel
     2.9 + * Copyright (C) 2003 Hewlett-Packard Co
    2.10 + *	David Mosberger-Tang <davidm@hpl.hp.com>
    2.11 + *
    2.12 + * Copyright (C) 2002 Dell Inc.
    2.13 + * Copyright (C) Matt Domsch (Matt_Domsch@dell.com)
    2.14 + *
    2.15 + * Copyright (C) 2002 Intel
    2.16 + * Copyright (C) Jenna Hall (jenna.s.hall@intel.com)
    2.17 + *
    2.18 + * Copyright (C) 2001 Intel
    2.19 + * Copyright (C) Fred Lewis (frederick.v.lewis@intel.com)
    2.20 + *
    2.21 + * Copyright (C) 2000 Intel
    2.22 + * Copyright (C) Chuck Fleckenstein (cfleck@co.intel.com)
    2.23 + *
    2.24 + * Copyright (C) 1999, 2004 Silicon Graphics, Inc.
    2.25 + * Copyright (C) Vijay Chander(vijay@engr.sgi.com)
    2.26 + *
    2.27 + * 03/04/15 D. Mosberger Added INIT backtrace support.
    2.28 + * 02/03/25 M. Domsch	GUID cleanups
    2.29 + *
    2.30 + * 02/01/04 J. Hall	Aligned MCA stack to 16 bytes, added platform vs. CPU
    2.31 + *			error flag, set SAL default return values, changed
    2.32 + *			error record structure to linked list, added init call
    2.33 + *			to sal_get_state_info_size().
    2.34 + *
    2.35 + * 01/01/03 F. Lewis    Added setup of CMCI and CPEI IRQs, logging of corrected
    2.36 + *                      platform errors, completed code for logging of
    2.37 + *                      corrected & uncorrected machine check errors, and
    2.38 + *                      updated for conformance with Nov. 2000 revision of the
    2.39 + *                      SAL 3.0 spec.
    2.40 + * 00/03/29 C. Fleckenstein  Fixed PAL/SAL update issues, began MCA bug fixes, logging issues,
    2.41 + *                           added min save state dump, added INIT handler.
    2.42 + *
    2.43 + * 2003-12-08 Keith Owens <kaos@sgi.com>
    2.44 + *            smp_call_function() must not be called from interrupt context (can
    2.45 + *            deadlock on tasklist_lock).  Use keventd to call smp_call_function().
    2.46 + *
    2.47 + * 2004-02-01 Keith Owens <kaos@sgi.com>
    2.48 + *            Avoid deadlock when using printk() for MCA and INIT records.
    2.49 + *            Delete all record printing code, moved to salinfo_decode in user space.
    2.50 + *            Mark variables and functions static where possible.
    2.51 + *            Delete dead variables and functions.
    2.52 + *            Reorder to remove the need for forward declarations and to consolidate
    2.53 + *            related code.
    2.54 + */
    2.55 +#include <linux/config.h>
    2.56 +#include <linux/types.h>
    2.57 +#include <linux/init.h>
    2.58 +#include <linux/sched.h>
    2.59 +#include <linux/interrupt.h>
    2.60 +#include <linux/irq.h>
    2.61 +#include <linux/kallsyms.h>
    2.62 +#include <linux/smp_lock.h>
    2.63 +#include <linux/bootmem.h>
    2.64 +#include <linux/acpi.h>
    2.65 +#include <linux/timer.h>
    2.66 +#include <linux/module.h>
    2.67 +#include <linux/kernel.h>
    2.68 +#include <linux/smp.h>
    2.69 +#include <linux/workqueue.h>
    2.70 +
    2.71 +#include <asm/delay.h>
    2.72 +#include <asm/machvec.h>
    2.73 +#include <asm/meminit.h>
    2.74 +#include <asm/page.h>
    2.75 +#include <asm/ptrace.h>
    2.76 +#include <asm/system.h>
    2.77 +#include <asm/sal.h>
    2.78 +#include <asm/mca.h>
    2.79 +
    2.80 +#include <asm/irq.h>
    2.81 +#include <asm/hw_irq.h>
    2.82 +
    2.83 +#if defined(IA64_MCA_DEBUG_INFO)
    2.84 +# define IA64_MCA_DEBUG(fmt...)	printk(fmt)
    2.85 +#else
    2.86 +# define IA64_MCA_DEBUG(fmt...)
    2.87 +#endif
    2.88 +
    2.89 +/* Used by mca_asm.S */
    2.90 +ia64_mca_sal_to_os_state_t	ia64_sal_to_os_handoff_state;
    2.91 +ia64_mca_os_to_sal_state_t	ia64_os_to_sal_handoff_state;
    2.92 +u64				ia64_mca_serialize;
    2.93 +DEFINE_PER_CPU(u64, ia64_mca_data); /* == __per_cpu_mca[smp_processor_id()] */
    2.94 +DEFINE_PER_CPU(u64, ia64_mca_per_cpu_pte); /* PTE to map per-CPU area */
    2.95 +DEFINE_PER_CPU(u64, ia64_mca_pal_pte);	    /* PTE to map PAL code */
    2.96 +DEFINE_PER_CPU(u64, ia64_mca_pal_base);    /* vaddr PAL code granule */
    2.97 +
    2.98 +unsigned long __per_cpu_mca[NR_CPUS];
    2.99 +
   2.100 +/* In mca_asm.S */
   2.101 +extern void			ia64_monarch_init_handler (void);
   2.102 +extern void			ia64_slave_init_handler (void);
   2.103 +
   2.104 +static ia64_mc_info_t		ia64_mc_info;
   2.105 +
   2.106 +#define MAX_CPE_POLL_INTERVAL (15*60*HZ) /* 15 minutes */
   2.107 +#define MIN_CPE_POLL_INTERVAL (2*60*HZ)  /* 2 minutes */
   2.108 +#define CMC_POLL_INTERVAL     (1*60*HZ)  /* 1 minute */
   2.109 +#define CPE_HISTORY_LENGTH    5
   2.110 +#define CMC_HISTORY_LENGTH    5
   2.111 +
   2.112 +static struct timer_list cpe_poll_timer;
   2.113 +static struct timer_list cmc_poll_timer;
   2.114 +/*
   2.115 + * This variable tells whether we are currently in polling mode.
   2.116 + * Start with this in the wrong state so we won't play w/ timers
   2.117 + * before the system is ready.
   2.118 + */
   2.119 +static int cmc_polling_enabled = 1;
   2.120 +
   2.121 +/*
   2.122 + * Clearing this variable prevents CPE polling from getting activated
   2.123 + * in mca_late_init.  Use it if your system doesn't provide a CPEI,
   2.124 + * but encounters problems retrieving CPE logs.  This should only be
   2.125 + * necessary for debugging.
   2.126 + */
   2.127 +static int cpe_poll_enabled = 1;
   2.128 +
   2.129 +extern void salinfo_log_wakeup(int type, u8 *buffer, u64 size, int irqsafe);
   2.130 +
   2.131 +static int mca_init;
   2.132 +
   2.133 +/*
   2.134 + * IA64_MCA log support
   2.135 + */
   2.136 +#define IA64_MAX_LOGS		2	/* Double-buffering for nested MCAs */
   2.137 +#define IA64_MAX_LOG_TYPES      4   /* MCA, INIT, CMC, CPE */
   2.138 +
   2.139 +typedef struct ia64_state_log_s
   2.140 +{
   2.141 +	spinlock_t	isl_lock;
   2.142 +	int		isl_index;
   2.143 +	unsigned long	isl_count;
   2.144 +	ia64_err_rec_t  *isl_log[IA64_MAX_LOGS]; /* need space to store header + error log */
   2.145 +} ia64_state_log_t;
   2.146 +
   2.147 +static ia64_state_log_t ia64_state_log[IA64_MAX_LOG_TYPES];
   2.148 +
   2.149 +#define IA64_LOG_ALLOCATE(it, size) \
   2.150 +	{ia64_state_log[it].isl_log[IA64_LOG_CURR_INDEX(it)] = \
   2.151 +		(ia64_err_rec_t *)alloc_bootmem(size); \
   2.152 +	ia64_state_log[it].isl_log[IA64_LOG_NEXT_INDEX(it)] = \
   2.153 +		(ia64_err_rec_t *)alloc_bootmem(size);}
   2.154 +#define IA64_LOG_LOCK_INIT(it) spin_lock_init(&ia64_state_log[it].isl_lock)
   2.155 +#define IA64_LOG_LOCK(it)      spin_lock_irqsave(&ia64_state_log[it].isl_lock, s)
   2.156 +#define IA64_LOG_UNLOCK(it)    spin_unlock_irqrestore(&ia64_state_log[it].isl_lock,s)
   2.157 +#define IA64_LOG_NEXT_INDEX(it)    ia64_state_log[it].isl_index
   2.158 +#define IA64_LOG_CURR_INDEX(it)    1 - ia64_state_log[it].isl_index
   2.159 +#define IA64_LOG_INDEX_INC(it) \
   2.160 +    {ia64_state_log[it].isl_index = 1 - ia64_state_log[it].isl_index; \
   2.161 +    ia64_state_log[it].isl_count++;}
   2.162 +#define IA64_LOG_INDEX_DEC(it) \
   2.163 +    ia64_state_log[it].isl_index = 1 - ia64_state_log[it].isl_index
   2.164 +#define IA64_LOG_NEXT_BUFFER(it)   (void *)((ia64_state_log[it].isl_log[IA64_LOG_NEXT_INDEX(it)]))
   2.165 +#define IA64_LOG_CURR_BUFFER(it)   (void *)((ia64_state_log[it].isl_log[IA64_LOG_CURR_INDEX(it)]))
   2.166 +#define IA64_LOG_COUNT(it)         ia64_state_log[it].isl_count
   2.167 +
   2.168 +/*
   2.169 + * ia64_log_init
   2.170 + *	Reset the OS ia64 log buffer
   2.171 + * Inputs   :   info_type   (SAL_INFO_TYPE_{MCA,INIT,CMC,CPE})
   2.172 + * Outputs	:	None
   2.173 + */
   2.174 +static void
   2.175 +ia64_log_init(int sal_info_type)
   2.176 +{
   2.177 +	u64	max_size = 0;
   2.178 +
   2.179 +	IA64_LOG_NEXT_INDEX(sal_info_type) = 0;
   2.180 +	IA64_LOG_LOCK_INIT(sal_info_type);
   2.181 +
   2.182 +	// SAL will tell us the maximum size of any error record of this type
   2.183 +	max_size = ia64_sal_get_state_info_size(sal_info_type);
   2.184 +	if (!max_size)
   2.185 +		/* alloc_bootmem() doesn't like zero-sized allocations! */
   2.186 +		return;
   2.187 +
   2.188 +	// set up OS data structures to hold error info
   2.189 +	IA64_LOG_ALLOCATE(sal_info_type, max_size);
   2.190 +	memset(IA64_LOG_CURR_BUFFER(sal_info_type), 0, max_size);
   2.191 +	memset(IA64_LOG_NEXT_BUFFER(sal_info_type), 0, max_size);
   2.192 +}
   2.193 +
   2.194 +/*
   2.195 + * ia64_log_get
   2.196 + *
   2.197 + *	Get the current MCA log from SAL and copy it into the OS log buffer.
   2.198 + *
   2.199 + *  Inputs  :   info_type   (SAL_INFO_TYPE_{MCA,INIT,CMC,CPE})
   2.200 + *              irq_safe    whether you can use printk at this point
   2.201 + *  Outputs :   size        (total record length)
   2.202 + *              *buffer     (ptr to error record)
   2.203 + *
   2.204 + */
   2.205 +static u64
   2.206 +ia64_log_get(int sal_info_type, u8 **buffer, int irq_safe)
   2.207 +{
   2.208 +	sal_log_record_header_t     *log_buffer;
   2.209 +	u64                         total_len = 0;
   2.210 +	int                         s;
   2.211 +
   2.212 +	IA64_LOG_LOCK(sal_info_type);
   2.213 +
   2.214 +	/* Get the process state information */
   2.215 +	log_buffer = IA64_LOG_NEXT_BUFFER(sal_info_type);
   2.216 +
   2.217 +	total_len = ia64_sal_get_state_info(sal_info_type, (u64 *)log_buffer);
   2.218 +
   2.219 +	if (total_len) {
   2.220 +		IA64_LOG_INDEX_INC(sal_info_type);
   2.221 +		IA64_LOG_UNLOCK(sal_info_type);
   2.222 +		if (irq_safe) {
   2.223 +			IA64_MCA_DEBUG("%s: SAL error record type %d retrieved. "
   2.224 +				       "Record length = %ld\n", __FUNCTION__, sal_info_type, total_len);
   2.225 +		}
   2.226 +		*buffer = (u8 *) log_buffer;
   2.227 +		return total_len;
   2.228 +	} else {
   2.229 +		IA64_LOG_UNLOCK(sal_info_type);
   2.230 +		return 0;
   2.231 +	}
   2.232 +}
   2.233 +
   2.234 +/*
   2.235 + *  ia64_mca_log_sal_error_record
   2.236 + *
   2.237 + *  This function retrieves a specified error record type from SAL
   2.238 + *  and wakes up any processes waiting for error records.
   2.239 + *
   2.240 + *  Inputs  :   sal_info_type   (Type of error record MCA/CMC/CPE/INIT)
   2.241 + */
   2.242 +static void
   2.243 +ia64_mca_log_sal_error_record(int sal_info_type)
   2.244 +{
   2.245 +	u8 *buffer;
   2.246 +	sal_log_record_header_t *rh;
   2.247 +	u64 size;
   2.248 +	int irq_safe = sal_info_type != SAL_INFO_TYPE_MCA && sal_info_type != SAL_INFO_TYPE_INIT;
   2.249 +#ifdef IA64_MCA_DEBUG_INFO
   2.250 +	static const char * const rec_name[] = { "MCA", "INIT", "CMC", "CPE" };
   2.251 +#endif
   2.252 +
   2.253 +	size = ia64_log_get(sal_info_type, &buffer, irq_safe);
   2.254 +	if (!size)
   2.255 +		return;
   2.256 +
   2.257 +	salinfo_log_wakeup(sal_info_type, buffer, size, irq_safe);
   2.258 +
   2.259 +	if (irq_safe)
   2.260 +		IA64_MCA_DEBUG("CPU %d: SAL log contains %s error record\n",
   2.261 +			smp_processor_id(),
   2.262 +			sal_info_type < ARRAY_SIZE(rec_name) ? rec_name[sal_info_type] : "UNKNOWN");
   2.263 +
   2.264 +	/* Clear logs from corrected errors in case there's no user-level logger */
   2.265 +	rh = (sal_log_record_header_t *)buffer;
   2.266 +	if (rh->severity == sal_log_severity_corrected)
   2.267 +		ia64_sal_clear_state_info(sal_info_type);
   2.268 +}
   2.269 +
   2.270 +/*
   2.271 + * platform dependent error handling
   2.272 + */
   2.273 +#ifndef PLATFORM_MCA_HANDLERS
   2.274 +
   2.275 +#ifdef CONFIG_ACPI
   2.276 +
   2.277 +int cpe_vector = -1;
   2.278 +
   2.279 +static irqreturn_t
   2.280 +ia64_mca_cpe_int_handler (int cpe_irq, void *arg, struct pt_regs *ptregs)
   2.281 +{
   2.282 +	static unsigned long	cpe_history[CPE_HISTORY_LENGTH];
   2.283 +	static int		index;
   2.284 +	static DEFINE_SPINLOCK(cpe_history_lock);
   2.285 +
   2.286 +	IA64_MCA_DEBUG("%s: received interrupt vector = %#x on CPU %d\n",
   2.287 +		       __FUNCTION__, cpe_irq, smp_processor_id());
   2.288 +
   2.289 +	/* SAL spec states this should run w/ interrupts enabled */
   2.290 +	local_irq_enable();
   2.291 +
   2.292 +	/* Get the CPE error record and log it */
   2.293 +	ia64_mca_log_sal_error_record(SAL_INFO_TYPE_CPE);
   2.294 +
   2.295 +	spin_lock(&cpe_history_lock);
   2.296 +	if (!cpe_poll_enabled && cpe_vector >= 0) {
   2.297 +
   2.298 +		int i, count = 1; /* we know 1 happened now */
   2.299 +		unsigned long now = jiffies;
   2.300 +
   2.301 +		for (i = 0; i < CPE_HISTORY_LENGTH; i++) {
   2.302 +			if (now - cpe_history[i] <= HZ)
   2.303 +				count++;
   2.304 +		}
   2.305 +
   2.306 +		IA64_MCA_DEBUG(KERN_INFO "CPE threshold %d/%d\n", count, CPE_HISTORY_LENGTH);
   2.307 +		if (count >= CPE_HISTORY_LENGTH) {
   2.308 +
   2.309 +			cpe_poll_enabled = 1;
   2.310 +			spin_unlock(&cpe_history_lock);
   2.311 +			disable_irq_nosync(local_vector_to_irq(IA64_CPE_VECTOR));
   2.312 +
   2.313 +			/*
   2.314 +			 * Corrected errors will still be corrected, but
   2.315 +			 * make sure there's a log somewhere that indicates
   2.316 +			 * something is generating more than we can handle.
   2.317 +			 */
   2.318 +			printk(KERN_WARNING "WARNING: Switching to polling CPE handler; error records may be lost\n");
   2.319 +
   2.320 +			mod_timer(&cpe_poll_timer, jiffies + MIN_CPE_POLL_INTERVAL);
   2.321 +
   2.322 +			/* lock already released, get out now */
   2.323 +			return IRQ_HANDLED;
   2.324 +		} else {
   2.325 +			cpe_history[index++] = now;
   2.326 +			if (index == CPE_HISTORY_LENGTH)
   2.327 +				index = 0;
   2.328 +		}
   2.329 +	}
   2.330 +	spin_unlock(&cpe_history_lock);
   2.331 +	return IRQ_HANDLED;
   2.332 +}
   2.333 +
   2.334 +#endif /* CONFIG_ACPI */
   2.335 +
   2.336 +static void
   2.337 +show_min_state (pal_min_state_area_t *minstate)
   2.338 +{
   2.339 +	u64 iip = minstate->pmsa_iip + ((struct ia64_psr *)(&minstate->pmsa_ipsr))->ri;
   2.340 +	u64 xip = minstate->pmsa_xip + ((struct ia64_psr *)(&minstate->pmsa_xpsr))->ri;
   2.341 +
   2.342 +	printk("NaT bits\t%016lx\n", minstate->pmsa_nat_bits);
   2.343 +	printk("pr\t\t%016lx\n", minstate->pmsa_pr);
   2.344 +	printk("b0\t\t%016lx ", minstate->pmsa_br0); print_symbol("%s\n", minstate->pmsa_br0);
   2.345 +	printk("ar.rsc\t\t%016lx\n", minstate->pmsa_rsc);
   2.346 +	printk("cr.iip\t\t%016lx ", iip); print_symbol("%s\n", iip);
   2.347 +	printk("cr.ipsr\t\t%016lx\n", minstate->pmsa_ipsr);
   2.348 +	printk("cr.ifs\t\t%016lx\n", minstate->pmsa_ifs);
   2.349 +	printk("xip\t\t%016lx ", xip); print_symbol("%s\n", xip);
   2.350 +	printk("xpsr\t\t%016lx\n", minstate->pmsa_xpsr);
   2.351 +	printk("xfs\t\t%016lx\n", minstate->pmsa_xfs);
   2.352 +	printk("b1\t\t%016lx ", minstate->pmsa_br1);
   2.353 +	print_symbol("%s\n", minstate->pmsa_br1);
   2.354 +
   2.355 +	printk("\nstatic registers r0-r15:\n");
   2.356 +	printk(" r0- 3 %016lx %016lx %016lx %016lx\n",
   2.357 +	       0UL, minstate->pmsa_gr[0], minstate->pmsa_gr[1], minstate->pmsa_gr[2]);
   2.358 +	printk(" r4- 7 %016lx %016lx %016lx %016lx\n",
   2.359 +	       minstate->pmsa_gr[3], minstate->pmsa_gr[4],
   2.360 +	       minstate->pmsa_gr[5], minstate->pmsa_gr[6]);
   2.361 +	printk(" r8-11 %016lx %016lx %016lx %016lx\n",
   2.362 +	       minstate->pmsa_gr[7], minstate->pmsa_gr[8],
   2.363 +	       minstate->pmsa_gr[9], minstate->pmsa_gr[10]);
   2.364 +	printk("r12-15 %016lx %016lx %016lx %016lx\n",
   2.365 +	       minstate->pmsa_gr[11], minstate->pmsa_gr[12],
   2.366 +	       minstate->pmsa_gr[13], minstate->pmsa_gr[14]);
   2.367 +
   2.368 +	printk("\nbank 0:\n");
   2.369 +	printk("r16-19 %016lx %016lx %016lx %016lx\n",
   2.370 +	       minstate->pmsa_bank0_gr[0], minstate->pmsa_bank0_gr[1],
   2.371 +	       minstate->pmsa_bank0_gr[2], minstate->pmsa_bank0_gr[3]);
   2.372 +	printk("r20-23 %016lx %016lx %016lx %016lx\n",
   2.373 +	       minstate->pmsa_bank0_gr[4], minstate->pmsa_bank0_gr[5],
   2.374 +	       minstate->pmsa_bank0_gr[6], minstate->pmsa_bank0_gr[7]);
   2.375 +	printk("r24-27 %016lx %016lx %016lx %016lx\n",
   2.376 +	       minstate->pmsa_bank0_gr[8], minstate->pmsa_bank0_gr[9],
   2.377 +	       minstate->pmsa_bank0_gr[10], minstate->pmsa_bank0_gr[11]);
   2.378 +	printk("r28-31 %016lx %016lx %016lx %016lx\n",
   2.379 +	       minstate->pmsa_bank0_gr[12], minstate->pmsa_bank0_gr[13],
   2.380 +	       minstate->pmsa_bank0_gr[14], minstate->pmsa_bank0_gr[15]);
   2.381 +
   2.382 +	printk("\nbank 1:\n");
   2.383 +	printk("r16-19 %016lx %016lx %016lx %016lx\n",
   2.384 +	       minstate->pmsa_bank1_gr[0], minstate->pmsa_bank1_gr[1],
   2.385 +	       minstate->pmsa_bank1_gr[2], minstate->pmsa_bank1_gr[3]);
   2.386 +	printk("r20-23 %016lx %016lx %016lx %016lx\n",
   2.387 +	       minstate->pmsa_bank1_gr[4], minstate->pmsa_bank1_gr[5],
   2.388 +	       minstate->pmsa_bank1_gr[6], minstate->pmsa_bank1_gr[7]);
   2.389 +	printk("r24-27 %016lx %016lx %016lx %016lx\n",
   2.390 +	       minstate->pmsa_bank1_gr[8], minstate->pmsa_bank1_gr[9],
   2.391 +	       minstate->pmsa_bank1_gr[10], minstate->pmsa_bank1_gr[11]);
   2.392 +	printk("r28-31 %016lx %016lx %016lx %016lx\n",
   2.393 +	       minstate->pmsa_bank1_gr[12], minstate->pmsa_bank1_gr[13],
   2.394 +	       minstate->pmsa_bank1_gr[14], minstate->pmsa_bank1_gr[15]);
   2.395 +}
   2.396 +
   2.397 +static void
   2.398 +fetch_min_state (pal_min_state_area_t *ms, struct pt_regs *pt, struct switch_stack *sw)
   2.399 +{
   2.400 +	u64 *dst_banked, *src_banked, bit, shift, nat_bits;
   2.401 +	int i;
   2.402 +
   2.403 +	/*
   2.404 +	 * First, update the pt-regs and switch-stack structures with the contents stored
   2.405 +	 * in the min-state area:
   2.406 +	 */
   2.407 +	if (((struct ia64_psr *) &ms->pmsa_ipsr)->ic == 0) {
   2.408 +		pt->cr_ipsr = ms->pmsa_xpsr;
   2.409 +		pt->cr_iip = ms->pmsa_xip;
   2.410 +		pt->cr_ifs = ms->pmsa_xfs;
   2.411 +	} else {
   2.412 +		pt->cr_ipsr = ms->pmsa_ipsr;
   2.413 +		pt->cr_iip = ms->pmsa_iip;
   2.414 +		pt->cr_ifs = ms->pmsa_ifs;
   2.415 +	}
   2.416 +	pt->ar_rsc = ms->pmsa_rsc;
   2.417 +	pt->pr = ms->pmsa_pr;
   2.418 +	pt->r1 = ms->pmsa_gr[0];
   2.419 +	pt->r2 = ms->pmsa_gr[1];
   2.420 +	pt->r3 = ms->pmsa_gr[2];
   2.421 +	sw->r4 = ms->pmsa_gr[3];
   2.422 +	sw->r5 = ms->pmsa_gr[4];
   2.423 +	sw->r6 = ms->pmsa_gr[5];
   2.424 +	sw->r7 = ms->pmsa_gr[6];
   2.425 +	pt->r8 = ms->pmsa_gr[7];
   2.426 +	pt->r9 = ms->pmsa_gr[8];
   2.427 +	pt->r10 = ms->pmsa_gr[9];
   2.428 +	pt->r11 = ms->pmsa_gr[10];
   2.429 +	pt->r12 = ms->pmsa_gr[11];
   2.430 +	pt->r13 = ms->pmsa_gr[12];
   2.431 +	pt->r14 = ms->pmsa_gr[13];
   2.432 +	pt->r15 = ms->pmsa_gr[14];
   2.433 +	dst_banked = &pt->r16;		/* r16-r31 are contiguous in struct pt_regs */
   2.434 +	src_banked = ms->pmsa_bank1_gr;
   2.435 +	for (i = 0; i < 16; ++i)
   2.436 +		dst_banked[i] = src_banked[i];
   2.437 +	pt->b0 = ms->pmsa_br0;
   2.438 +	sw->b1 = ms->pmsa_br1;
   2.439 +
   2.440 +	/* construct the NaT bits for the pt-regs structure: */
   2.441 +#	define PUT_NAT_BIT(dst, addr)					\
   2.442 +	do {								\
   2.443 +		bit = nat_bits & 1; nat_bits >>= 1;			\
   2.444 +		shift = ((unsigned long) addr >> 3) & 0x3f;		\
   2.445 +		dst = ((dst) & ~(1UL << shift)) | (bit << shift);	\
   2.446 +	} while (0)
   2.447 +
   2.448 +	/* Rotate the saved NaT bits such that bit 0 corresponds to pmsa_gr[0]: */
   2.449 +	shift = ((unsigned long) &ms->pmsa_gr[0] >> 3) & 0x3f;
   2.450 +	nat_bits = (ms->pmsa_nat_bits >> shift) | (ms->pmsa_nat_bits << (64 - shift));
   2.451 +
   2.452 +	PUT_NAT_BIT(sw->caller_unat, &pt->r1);
   2.453 +	PUT_NAT_BIT(sw->caller_unat, &pt->r2);
   2.454 +	PUT_NAT_BIT(sw->caller_unat, &pt->r3);
   2.455 +	PUT_NAT_BIT(sw->ar_unat, &sw->r4);
   2.456 +	PUT_NAT_BIT(sw->ar_unat, &sw->r5);
   2.457 +	PUT_NAT_BIT(sw->ar_unat, &sw->r6);
   2.458 +	PUT_NAT_BIT(sw->ar_unat, &sw->r7);
   2.459 +	PUT_NAT_BIT(sw->caller_unat, &pt->r8);	PUT_NAT_BIT(sw->caller_unat, &pt->r9);
   2.460 +	PUT_NAT_BIT(sw->caller_unat, &pt->r10);	PUT_NAT_BIT(sw->caller_unat, &pt->r11);
   2.461 +	PUT_NAT_BIT(sw->caller_unat, &pt->r12);	PUT_NAT_BIT(sw->caller_unat, &pt->r13);
   2.462 +	PUT_NAT_BIT(sw->caller_unat, &pt->r14);	PUT_NAT_BIT(sw->caller_unat, &pt->r15);
   2.463 +	nat_bits >>= 16;	/* skip over bank0 NaT bits */
   2.464 +	PUT_NAT_BIT(sw->caller_unat, &pt->r16);	PUT_NAT_BIT(sw->caller_unat, &pt->r17);
   2.465 +	PUT_NAT_BIT(sw->caller_unat, &pt->r18);	PUT_NAT_BIT(sw->caller_unat, &pt->r19);
   2.466 +	PUT_NAT_BIT(sw->caller_unat, &pt->r20);	PUT_NAT_BIT(sw->caller_unat, &pt->r21);
   2.467 +	PUT_NAT_BIT(sw->caller_unat, &pt->r22);	PUT_NAT_BIT(sw->caller_unat, &pt->r23);
   2.468 +	PUT_NAT_BIT(sw->caller_unat, &pt->r24);	PUT_NAT_BIT(sw->caller_unat, &pt->r25);
   2.469 +	PUT_NAT_BIT(sw->caller_unat, &pt->r26);	PUT_NAT_BIT(sw->caller_unat, &pt->r27);
   2.470 +	PUT_NAT_BIT(sw->caller_unat, &pt->r28);	PUT_NAT_BIT(sw->caller_unat, &pt->r29);
   2.471 +	PUT_NAT_BIT(sw->caller_unat, &pt->r30);	PUT_NAT_BIT(sw->caller_unat, &pt->r31);
   2.472 +}
   2.473 +
   2.474 +static void
   2.475 +init_handler_platform (pal_min_state_area_t *ms,
   2.476 +		       struct pt_regs *pt, struct switch_stack *sw)
   2.477 +{
   2.478 +	struct unw_frame_info info;
   2.479 +
   2.480 +	/* if a kernel debugger is available call it here else just dump the registers */
   2.481 +
   2.482 +	/*
   2.483 +	 * Wait for a bit.  On some machines (e.g., HP's zx2000 and zx6000, INIT can be
   2.484 +	 * generated via the BMC's command-line interface, but since the console is on the
   2.485 +	 * same serial line, the user will need some time to switch out of the BMC before
   2.486 +	 * the dump begins.
   2.487 +	 */
   2.488 +	printk("Delaying for 5 seconds...\n");
   2.489 +	udelay(5*1000000);
   2.490 +	show_min_state(ms);
   2.491 +
   2.492 +	printk("Backtrace of current task (pid %d, %s)\n", current->pid, current->comm);
   2.493 +	fetch_min_state(ms, pt, sw);
   2.494 +	unw_init_from_interruption(&info, current, pt, sw);
   2.495 +	ia64_do_show_stack(&info, NULL);
   2.496 +
   2.497 +#ifdef CONFIG_SMP
   2.498 +	/* read_trylock() would be handy... */
   2.499 +	if (!tasklist_lock.write_lock)
   2.500 +		read_lock(&tasklist_lock);
   2.501 +#endif
   2.502 +	{
   2.503 +		struct task_struct *g, *t;
   2.504 +		do_each_thread (g, t) {
   2.505 +			if (t == current)
   2.506 +				continue;
   2.507 +
   2.508 +			printk("\nBacktrace of pid %d (%s)\n", t->pid, t->comm);
   2.509 +			show_stack(t, NULL);
   2.510 +		} while_each_thread (g, t);
   2.511 +	}
   2.512 +#ifdef CONFIG_SMP
   2.513 +	if (!tasklist_lock.write_lock)
   2.514 +		read_unlock(&tasklist_lock);
   2.515 +#endif
   2.516 +
   2.517 +	printk("\nINIT dump complete.  Please reboot now.\n");
   2.518 +	while (1);			/* hang city if no debugger */
   2.519 +}
   2.520 +
   2.521 +#ifdef CONFIG_ACPI
   2.522 +/*
   2.523 + * ia64_mca_register_cpev
   2.524 + *
   2.525 + *  Register the corrected platform error vector with SAL.
   2.526 + *
   2.527 + *  Inputs
   2.528 + *      cpev        Corrected Platform Error Vector number
   2.529 + *
   2.530 + *  Outputs
   2.531 + *      None
   2.532 + */
   2.533 +static void
   2.534 +ia64_mca_register_cpev (int cpev)
   2.535 +{
   2.536 +	/* Register the CPE interrupt vector with SAL */
   2.537 +	struct ia64_sal_retval isrv;
   2.538 +
   2.539 +	isrv = ia64_sal_mc_set_params(SAL_MC_PARAM_CPE_INT, SAL_MC_PARAM_MECHANISM_INT, cpev, 0, 0);
   2.540 +	if (isrv.status) {
   2.541 +		printk(KERN_ERR "Failed to register Corrected Platform "
   2.542 +		       "Error interrupt vector with SAL (status %ld)\n", isrv.status);
   2.543 +		return;
   2.544 +	}
   2.545 +
   2.546 +	IA64_MCA_DEBUG("%s: corrected platform error "
   2.547 +		       "vector %#x registered\n", __FUNCTION__, cpev);
   2.548 +}
   2.549 +#endif /* CONFIG_ACPI */
   2.550 +
   2.551 +#endif /* PLATFORM_MCA_HANDLERS */
   2.552 +
   2.553 +/*
   2.554 + * ia64_mca_cmc_vector_setup
   2.555 + *
   2.556 + *  Setup the corrected machine check vector register in the processor.
   2.557 + *  (The interrupt is masked on boot. ia64_mca_late_init unmask this.)
   2.558 + *  This function is invoked on a per-processor basis.
   2.559 + *
   2.560 + * Inputs
   2.561 + *      None
   2.562 + *
   2.563 + * Outputs
   2.564 + *	None
   2.565 + */
   2.566 +void
   2.567 +ia64_mca_cmc_vector_setup (void)
   2.568 +{
   2.569 +	cmcv_reg_t	cmcv;
   2.570 +
   2.571 +	cmcv.cmcv_regval	= 0;
   2.572 +	cmcv.cmcv_mask		= 1;        /* Mask/disable interrupt at first */
   2.573 +	cmcv.cmcv_vector	= IA64_CMC_VECTOR;
   2.574 +	ia64_setreg(_IA64_REG_CR_CMCV, cmcv.cmcv_regval);
   2.575 +
   2.576 +	IA64_MCA_DEBUG("%s: CPU %d corrected "
   2.577 +		       "machine check vector %#x registered.\n",
   2.578 +		       __FUNCTION__, smp_processor_id(), IA64_CMC_VECTOR);
   2.579 +
   2.580 +	IA64_MCA_DEBUG("%s: CPU %d CMCV = %#016lx\n",
   2.581 +		       __FUNCTION__, smp_processor_id(), ia64_getreg(_IA64_REG_CR_CMCV));
   2.582 +}
   2.583 +
   2.584 +/*
   2.585 + * ia64_mca_cmc_vector_disable
   2.586 + *
   2.587 + *  Mask the corrected machine check vector register in the processor.
   2.588 + *  This function is invoked on a per-processor basis.
   2.589 + *
   2.590 + * Inputs
   2.591 + *      dummy(unused)
   2.592 + *
   2.593 + * Outputs
   2.594 + *	None
   2.595 + */
   2.596 +static void
   2.597 +ia64_mca_cmc_vector_disable (void *dummy)
   2.598 +{
   2.599 +	cmcv_reg_t	cmcv;
   2.600 +
   2.601 +	cmcv.cmcv_regval = ia64_getreg(_IA64_REG_CR_CMCV);
   2.602 +
   2.603 +	cmcv.cmcv_mask = 1; /* Mask/disable interrupt */
   2.604 +	ia64_setreg(_IA64_REG_CR_CMCV, cmcv.cmcv_regval);
   2.605 +
   2.606 +	IA64_MCA_DEBUG("%s: CPU %d corrected "
   2.607 +		       "machine check vector %#x disabled.\n",
   2.608 +		       __FUNCTION__, smp_processor_id(), cmcv.cmcv_vector);
   2.609 +}
   2.610 +
   2.611 +/*
   2.612 + * ia64_mca_cmc_vector_enable
   2.613 + *
   2.614 + *  Unmask the corrected machine check vector register in the processor.
   2.615 + *  This function is invoked on a per-processor basis.
   2.616 + *
   2.617 + * Inputs
   2.618 + *      dummy(unused)
   2.619 + *
   2.620 + * Outputs
   2.621 + *	None
   2.622 + */
   2.623 +static void
   2.624 +ia64_mca_cmc_vector_enable (void *dummy)
   2.625 +{
   2.626 +	cmcv_reg_t	cmcv;
   2.627 +
   2.628 +	cmcv.cmcv_regval = ia64_getreg(_IA64_REG_CR_CMCV);
   2.629 +
   2.630 +	cmcv.cmcv_mask = 0; /* Unmask/enable interrupt */
   2.631 +	ia64_setreg(_IA64_REG_CR_CMCV, cmcv.cmcv_regval);
   2.632 +
   2.633 +	IA64_MCA_DEBUG("%s: CPU %d corrected "
   2.634 +		       "machine check vector %#x enabled.\n",
   2.635 +		       __FUNCTION__, smp_processor_id(), cmcv.cmcv_vector);
   2.636 +}
   2.637 +
   2.638 +/*
   2.639 + * ia64_mca_cmc_vector_disable_keventd
   2.640 + *
   2.641 + * Called via keventd (smp_call_function() is not safe in interrupt context) to
   2.642 + * disable the cmc interrupt vector.
   2.643 + */
   2.644 +static void
   2.645 +ia64_mca_cmc_vector_disable_keventd(void *unused)
   2.646 +{
   2.647 +	on_each_cpu(ia64_mca_cmc_vector_disable, NULL, 1, 0);
   2.648 +}
   2.649 +
   2.650 +/*
   2.651 + * ia64_mca_cmc_vector_enable_keventd
   2.652 + *
   2.653 + * Called via keventd (smp_call_function() is not safe in interrupt context) to
   2.654 + * enable the cmc interrupt vector.
   2.655 + */
   2.656 +static void
   2.657 +ia64_mca_cmc_vector_enable_keventd(void *unused)
   2.658 +{
   2.659 +	on_each_cpu(ia64_mca_cmc_vector_enable, NULL, 1, 0);
   2.660 +}
   2.661 +
   2.662 +/*
   2.663 + * ia64_mca_wakeup_ipi_wait
   2.664 + *
   2.665 + *	Wait for the inter-cpu interrupt to be sent by the
   2.666 + *	monarch processor once it is done with handling the
   2.667 + *	MCA.
   2.668 + *
   2.669 + *  Inputs  :   None
   2.670 + *  Outputs :   None
   2.671 + */
   2.672 +static void
   2.673 +ia64_mca_wakeup_ipi_wait(void)
   2.674 +{
   2.675 +	int	irr_num = (IA64_MCA_WAKEUP_VECTOR >> 6);
   2.676 +	int	irr_bit = (IA64_MCA_WAKEUP_VECTOR & 0x3f);
   2.677 +	u64	irr = 0;
   2.678 +
   2.679 +	do {
   2.680 +		switch(irr_num) {
   2.681 +		      case 0:
   2.682 +			irr = ia64_getreg(_IA64_REG_CR_IRR0);
   2.683 +			break;
   2.684 +		      case 1:
   2.685 +			irr = ia64_getreg(_IA64_REG_CR_IRR1);
   2.686 +			break;
   2.687 +		      case 2:
   2.688 +			irr = ia64_getreg(_IA64_REG_CR_IRR2);
   2.689 +			break;
   2.690 +		      case 3:
   2.691 +			irr = ia64_getreg(_IA64_REG_CR_IRR3);
   2.692 +			break;
   2.693 +		}
   2.694 +		cpu_relax();
   2.695 +	} while (!(irr & (1UL << irr_bit))) ;
   2.696 +}
   2.697 +
   2.698 +/*
   2.699 + * ia64_mca_wakeup
   2.700 + *
   2.701 + *	Send an inter-cpu interrupt to wake-up a particular cpu
   2.702 + *	and mark that cpu to be out of rendez.
   2.703 + *
   2.704 + *  Inputs  :   cpuid
   2.705 + *  Outputs :   None
   2.706 + */
   2.707 +static void
   2.708 +ia64_mca_wakeup(int cpu)
   2.709 +{
   2.710 +	platform_send_ipi(cpu, IA64_MCA_WAKEUP_VECTOR, IA64_IPI_DM_INT, 0);
   2.711 +	ia64_mc_info.imi_rendez_checkin[cpu] = IA64_MCA_RENDEZ_CHECKIN_NOTDONE;
   2.712 +
   2.713 +}
   2.714 +
   2.715 +/*
   2.716 + * ia64_mca_wakeup_all
   2.717 + *
   2.718 + *	Wakeup all the cpus which have rendez'ed previously.
   2.719 + *
   2.720 + *  Inputs  :   None
   2.721 + *  Outputs :   None
   2.722 + */
   2.723 +static void
   2.724 +ia64_mca_wakeup_all(void)
   2.725 +{
   2.726 +	int cpu;
   2.727 +
   2.728 +	/* Clear the Rendez checkin flag for all cpus */
   2.729 +	for(cpu = 0; cpu < NR_CPUS; cpu++) {
   2.730 +		if (!cpu_online(cpu))
   2.731 +			continue;
   2.732 +		if (ia64_mc_info.imi_rendez_checkin[cpu] == IA64_MCA_RENDEZ_CHECKIN_DONE)
   2.733 +			ia64_mca_wakeup(cpu);
   2.734 +	}
   2.735 +
   2.736 +}
   2.737 +
   2.738 +/*
   2.739 + * ia64_mca_rendez_interrupt_handler
   2.740 + *
   2.741 + *	This is handler used to put slave processors into spinloop
   2.742 + *	while the monarch processor does the mca handling and later
   2.743 + *	wake each slave up once the monarch is done.
   2.744 + *
   2.745 + *  Inputs  :   None
   2.746 + *  Outputs :   None
   2.747 + */
   2.748 +static irqreturn_t
   2.749 +ia64_mca_rendez_int_handler(int rendez_irq, void *arg, struct pt_regs *ptregs)
   2.750 +{
   2.751 +	unsigned long flags;
   2.752 +	int cpu = smp_processor_id();
   2.753 +
   2.754 +	/* Mask all interrupts */
   2.755 +	local_irq_save(flags);
   2.756 +
   2.757 +	ia64_mc_info.imi_rendez_checkin[cpu] = IA64_MCA_RENDEZ_CHECKIN_DONE;
   2.758 +	/* Register with the SAL monarch that the slave has
   2.759 +	 * reached SAL
   2.760 +	 */
   2.761 +	ia64_sal_mc_rendez();
   2.762 +
   2.763 +	/* Wait for the wakeup IPI from the monarch
   2.764 +	 * This waiting is done by polling on the wakeup-interrupt
   2.765 +	 * vector bit in the processor's IRRs
   2.766 +	 */
   2.767 +	ia64_mca_wakeup_ipi_wait();
   2.768 +
   2.769 +	/* Enable all interrupts */
   2.770 +	local_irq_restore(flags);
   2.771 +	return IRQ_HANDLED;
   2.772 +}
   2.773 +
   2.774 +/*
   2.775 + * ia64_mca_wakeup_int_handler
   2.776 + *
   2.777 + *	The interrupt handler for processing the inter-cpu interrupt to the
   2.778 + *	slave cpu which was spinning in the rendez loop.
   2.779 + *	Since this spinning is done by turning off the interrupts and
   2.780 + *	polling on the wakeup-interrupt bit in the IRR, there is
   2.781 + *	nothing useful to be done in the handler.
   2.782 + *
   2.783 + *  Inputs  :   wakeup_irq  (Wakeup-interrupt bit)
   2.784 + *	arg		(Interrupt handler specific argument)
   2.785 + *	ptregs		(Exception frame at the time of the interrupt)
   2.786 + *  Outputs :   None
   2.787 + *
   2.788 + */
   2.789 +static irqreturn_t
   2.790 +ia64_mca_wakeup_int_handler(int wakeup_irq, void *arg, struct pt_regs *ptregs)
   2.791 +{
   2.792 +	return IRQ_HANDLED;
   2.793 +}
   2.794 +
   2.795 +/*
   2.796 + * ia64_return_to_sal_check
   2.797 + *
   2.798 + *	This is function called before going back from the OS_MCA handler
   2.799 + *	to the OS_MCA dispatch code which finally takes the control back
   2.800 + *	to the SAL.
   2.801 + *	The main purpose of this routine is to setup the OS_MCA to SAL
   2.802 + *	return state which can be used by the OS_MCA dispatch code
   2.803 + *	just before going back to SAL.
   2.804 + *
   2.805 + *  Inputs  :   None
   2.806 + *  Outputs :   None
   2.807 + */
   2.808 +
   2.809 +static void
   2.810 +ia64_return_to_sal_check(int recover)
   2.811 +{
   2.812 +
   2.813 +	/* Copy over some relevant stuff from the sal_to_os_mca_handoff
   2.814 +	 * so that it can be used at the time of os_mca_to_sal_handoff
   2.815 +	 */
   2.816 +	ia64_os_to_sal_handoff_state.imots_sal_gp =
   2.817 +		ia64_sal_to_os_handoff_state.imsto_sal_gp;
   2.818 +
   2.819 +	ia64_os_to_sal_handoff_state.imots_sal_check_ra =
   2.820 +		ia64_sal_to_os_handoff_state.imsto_sal_check_ra;
   2.821 +
   2.822 +	if (recover)
   2.823 +		ia64_os_to_sal_handoff_state.imots_os_status = IA64_MCA_CORRECTED;
   2.824 +	else
   2.825 +		ia64_os_to_sal_handoff_state.imots_os_status = IA64_MCA_COLD_BOOT;
   2.826 +
   2.827 +	/* Default = tell SAL to return to same context */
   2.828 +	ia64_os_to_sal_handoff_state.imots_context = IA64_MCA_SAME_CONTEXT;
   2.829 +
   2.830 +	ia64_os_to_sal_handoff_state.imots_new_min_state =
   2.831 +		(u64 *)ia64_sal_to_os_handoff_state.pal_min_state;
   2.832 +
   2.833 +}
   2.834 +
   2.835 +/* Function pointer for extra MCA recovery */
   2.836 +int (*ia64_mca_ucmc_extension)
   2.837 +	(void*,ia64_mca_sal_to_os_state_t*,ia64_mca_os_to_sal_state_t*)
   2.838 +	= NULL;
   2.839 +
   2.840 +int
   2.841 +ia64_reg_MCA_extension(void *fn)
   2.842 +{
   2.843 +	if (ia64_mca_ucmc_extension)
   2.844 +		return 1;
   2.845 +
   2.846 +	ia64_mca_ucmc_extension = fn;
   2.847 +	return 0;
   2.848 +}
   2.849 +
   2.850 +void
   2.851 +ia64_unreg_MCA_extension(void)
   2.852 +{
   2.853 +	if (ia64_mca_ucmc_extension)
   2.854 +		ia64_mca_ucmc_extension = NULL;
   2.855 +}
   2.856 +
   2.857 +EXPORT_SYMBOL(ia64_reg_MCA_extension);
   2.858 +EXPORT_SYMBOL(ia64_unreg_MCA_extension);
   2.859 +
   2.860 +/*
   2.861 + * ia64_mca_ucmc_handler
   2.862 + *
   2.863 + *	This is uncorrectable machine check handler called from OS_MCA
   2.864 + *	dispatch code which is in turn called from SAL_CHECK().
   2.865 + *	This is the place where the core of OS MCA handling is done.
   2.866 + *	Right now the logs are extracted and displayed in a well-defined
   2.867 + *	format. This handler code is supposed to be run only on the
   2.868 + *	monarch processor. Once the monarch is done with MCA handling
   2.869 + *	further MCA logging is enabled by clearing logs.
   2.870 + *	Monarch also has the duty of sending wakeup-IPIs to pull the
   2.871 + *	slave processors out of rendezvous spinloop.
   2.872 + *
   2.873 + *  Inputs  :   None
   2.874 + *  Outputs :   None
   2.875 + */
   2.876 +void
   2.877 +ia64_mca_ucmc_handler(void)
   2.878 +{
   2.879 +	pal_processor_state_info_t *psp = (pal_processor_state_info_t *)
   2.880 +		&ia64_sal_to_os_handoff_state.proc_state_param;
   2.881 +	int recover; 
   2.882 +
   2.883 +	/* Get the MCA error record and log it */
   2.884 +	ia64_mca_log_sal_error_record(SAL_INFO_TYPE_MCA);
   2.885 +
   2.886 +	/* TLB error is only exist in this SAL error record */
   2.887 +	recover = (psp->tc && !(psp->cc || psp->bc || psp->rc || psp->uc))
   2.888 +	/* other error recovery */
   2.889 +	   || (ia64_mca_ucmc_extension 
   2.890 +		&& ia64_mca_ucmc_extension(
   2.891 +			IA64_LOG_CURR_BUFFER(SAL_INFO_TYPE_MCA),
   2.892 +			&ia64_sal_to_os_handoff_state,
   2.893 +			&ia64_os_to_sal_handoff_state)); 
   2.894 +
   2.895 +	if (recover) {
   2.896 +		sal_log_record_header_t *rh = IA64_LOG_CURR_BUFFER(SAL_INFO_TYPE_MCA);
   2.897 +		rh->severity = sal_log_severity_corrected;
   2.898 +		ia64_sal_clear_state_info(SAL_INFO_TYPE_MCA);
   2.899 +	}
   2.900 +	/*
   2.901 +	 *  Wakeup all the processors which are spinning in the rendezvous
   2.902 +	 *  loop.
   2.903 +	 */
   2.904 +	ia64_mca_wakeup_all();
   2.905 +
   2.906 +	/* Return to SAL */
   2.907 +	ia64_return_to_sal_check(recover);
   2.908 +}
   2.909 +
   2.910 +static DECLARE_WORK(cmc_disable_work, ia64_mca_cmc_vector_disable_keventd, NULL);
   2.911 +static DECLARE_WORK(cmc_enable_work, ia64_mca_cmc_vector_enable_keventd, NULL);
   2.912 +
   2.913 +/*
   2.914 + * ia64_mca_cmc_int_handler
   2.915 + *
   2.916 + *  This is corrected machine check interrupt handler.
   2.917 + *	Right now the logs are extracted and displayed in a well-defined
   2.918 + *	format.
   2.919 + *
   2.920 + * Inputs
   2.921 + *      interrupt number
   2.922 + *      client data arg ptr
   2.923 + *      saved registers ptr
   2.924 + *
   2.925 + * Outputs
   2.926 + *	None
   2.927 + */
   2.928 +static irqreturn_t
   2.929 +ia64_mca_cmc_int_handler(int cmc_irq, void *arg, struct pt_regs *ptregs)
   2.930 +{
   2.931 +	static unsigned long	cmc_history[CMC_HISTORY_LENGTH];
   2.932 +	static int		index;
   2.933 +	static DEFINE_SPINLOCK(cmc_history_lock);
   2.934 +
   2.935 +	IA64_MCA_DEBUG("%s: received interrupt vector = %#x on CPU %d\n",
   2.936 +		       __FUNCTION__, cmc_irq, smp_processor_id());
   2.937 +
   2.938 +	/* SAL spec states this should run w/ interrupts enabled */
   2.939 +	local_irq_enable();
   2.940 +
   2.941 +	/* Get the CMC error record and log it */
   2.942 +	ia64_mca_log_sal_error_record(SAL_INFO_TYPE_CMC);
   2.943 +
   2.944 +	spin_lock(&cmc_history_lock);
   2.945 +	if (!cmc_polling_enabled) {
   2.946 +		int i, count = 1; /* we know 1 happened now */
   2.947 +		unsigned long now = jiffies;
   2.948 +
   2.949 +		for (i = 0; i < CMC_HISTORY_LENGTH; i++) {
   2.950 +			if (now - cmc_history[i] <= HZ)
   2.951 +				count++;
   2.952 +		}
   2.953 +
   2.954 +		IA64_MCA_DEBUG(KERN_INFO "CMC threshold %d/%d\n", count, CMC_HISTORY_LENGTH);
   2.955 +		if (count >= CMC_HISTORY_LENGTH) {
   2.956 +
   2.957 +			cmc_polling_enabled = 1;
   2.958 +			spin_unlock(&cmc_history_lock);
   2.959 +			schedule_work(&cmc_disable_work);
   2.960 +
   2.961 +			/*
   2.962 +			 * Corrected errors will still be corrected, but
   2.963 +			 * make sure there's a log somewhere that indicates
   2.964 +			 * something is generating more than we can handle.
   2.965 +			 */
   2.966 +			printk(KERN_WARNING "WARNING: Switching to polling CMC handler; error records may be lost\n");
   2.967 +
   2.968 +			mod_timer(&cmc_poll_timer, jiffies + CMC_POLL_INTERVAL);
   2.969 +
   2.970 +			/* lock already released, get out now */
   2.971 +			return IRQ_HANDLED;
   2.972 +		} else {
   2.973 +			cmc_history[index++] = now;
   2.974 +			if (index == CMC_HISTORY_LENGTH)
   2.975 +				index = 0;
   2.976 +		}
   2.977 +	}
   2.978 +	spin_unlock(&cmc_history_lock);
   2.979 +	return IRQ_HANDLED;
   2.980 +}
   2.981 +
   2.982 +/*
   2.983 + *  ia64_mca_cmc_int_caller
   2.984 + *
   2.985 + * 	Triggered by sw interrupt from CMC polling routine.  Calls
   2.986 + * 	real interrupt handler and either triggers a sw interrupt
   2.987 + * 	on the next cpu or does cleanup at the end.
   2.988 + *
   2.989 + * Inputs
   2.990 + *	interrupt number
   2.991 + *	client data arg ptr
   2.992 + *	saved registers ptr
   2.993 + * Outputs
   2.994 + * 	handled
   2.995 + */
   2.996 +static irqreturn_t
   2.997 +ia64_mca_cmc_int_caller(int cmc_irq, void *arg, struct pt_regs *ptregs)
   2.998 +{
   2.999 +	static int start_count = -1;
  2.1000 +	unsigned int cpuid;
  2.1001 +
  2.1002 +	cpuid = smp_processor_id();
  2.1003 +
  2.1004 +	/* If first cpu, update count */
  2.1005 +	if (start_count == -1)
  2.1006 +		start_count = IA64_LOG_COUNT(SAL_INFO_TYPE_CMC);
  2.1007 +
  2.1008 +	ia64_mca_cmc_int_handler(cmc_irq, arg, ptregs);
  2.1009 +
  2.1010 +	for (++cpuid ; cpuid < NR_CPUS && !cpu_online(cpuid) ; cpuid++);
  2.1011 +
  2.1012 +	if (cpuid < NR_CPUS) {
  2.1013 +		platform_send_ipi(cpuid, IA64_CMCP_VECTOR, IA64_IPI_DM_INT, 0);
  2.1014 +	} else {
  2.1015 +		/* If no log record, switch out of polling mode */
  2.1016 +		if (start_count == IA64_LOG_COUNT(SAL_INFO_TYPE_CMC)) {
  2.1017 +
  2.1018 +			printk(KERN_WARNING "Returning to interrupt driven CMC handler\n");
  2.1019 +			schedule_work(&cmc_enable_work);
  2.1020 +			cmc_polling_enabled = 0;
  2.1021 +
  2.1022 +		} else {
  2.1023 +
  2.1024 +			mod_timer(&cmc_poll_timer, jiffies + CMC_POLL_INTERVAL);
  2.1025 +		}
  2.1026 +
  2.1027 +		start_count = -1;
  2.1028 +	}
  2.1029 +
  2.1030 +	return IRQ_HANDLED;
  2.1031 +}
  2.1032 +
  2.1033 +/*
  2.1034 + *  ia64_mca_cmc_poll
  2.1035 + *
  2.1036 + *	Poll for Corrected Machine Checks (CMCs)
  2.1037 + *
  2.1038 + * Inputs   :   dummy(unused)
  2.1039 + * Outputs  :   None
  2.1040 + *
  2.1041 + */
  2.1042 +static void
  2.1043 +ia64_mca_cmc_poll (unsigned long dummy)
  2.1044 +{
  2.1045 +	/* Trigger a CMC interrupt cascade  */
  2.1046 +	platform_send_ipi(first_cpu(cpu_online_map), IA64_CMCP_VECTOR, IA64_IPI_DM_INT, 0);
  2.1047 +}
  2.1048 +
  2.1049 +/*
  2.1050 + *  ia64_mca_cpe_int_caller
  2.1051 + *
  2.1052 + * 	Triggered by sw interrupt from CPE polling routine.  Calls
  2.1053 + * 	real interrupt handler and either triggers a sw interrupt
  2.1054 + * 	on the next cpu or does cleanup at the end.
  2.1055 + *
  2.1056 + * Inputs
  2.1057 + *	interrupt number
  2.1058 + *	client data arg ptr
  2.1059 + *	saved registers ptr
  2.1060 + * Outputs
  2.1061 + * 	handled
  2.1062 + */
  2.1063 +#ifdef CONFIG_ACPI
  2.1064 +
  2.1065 +static irqreturn_t
  2.1066 +ia64_mca_cpe_int_caller(int cpe_irq, void *arg, struct pt_regs *ptregs)
  2.1067 +{
  2.1068 +	static int start_count = -1;
  2.1069 +	static int poll_time = MIN_CPE_POLL_INTERVAL;
  2.1070 +	unsigned int cpuid;
  2.1071 +
  2.1072 +	cpuid = smp_processor_id();
  2.1073 +
  2.1074 +	/* If first cpu, update count */
  2.1075 +	if (start_count == -1)
  2.1076 +		start_count = IA64_LOG_COUNT(SAL_INFO_TYPE_CPE);
  2.1077 +
  2.1078 +	ia64_mca_cpe_int_handler(cpe_irq, arg, ptregs);
  2.1079 +
  2.1080 +	for (++cpuid ; cpuid < NR_CPUS && !cpu_online(cpuid) ; cpuid++);
  2.1081 +
  2.1082 +	if (cpuid < NR_CPUS) {
  2.1083 +		platform_send_ipi(cpuid, IA64_CPEP_VECTOR, IA64_IPI_DM_INT, 0);
  2.1084 +	} else {
  2.1085 +		/*
  2.1086 +		 * If a log was recorded, increase our polling frequency,
  2.1087 +		 * otherwise, backoff or return to interrupt mode.
  2.1088 +		 */
  2.1089 +		if (start_count != IA64_LOG_COUNT(SAL_INFO_TYPE_CPE)) {
  2.1090 +			poll_time = max(MIN_CPE_POLL_INTERVAL, poll_time / 2);
  2.1091 +		} else if (cpe_vector < 0) {
  2.1092 +			poll_time = min(MAX_CPE_POLL_INTERVAL, poll_time * 2);
  2.1093 +		} else {
  2.1094 +			poll_time = MIN_CPE_POLL_INTERVAL;
  2.1095 +
  2.1096 +			printk(KERN_WARNING "Returning to interrupt driven CPE handler\n");
  2.1097 +			enable_irq(local_vector_to_irq(IA64_CPE_VECTOR));
  2.1098 +			cpe_poll_enabled = 0;
  2.1099 +		}
  2.1100 +
  2.1101 +		if (cpe_poll_enabled)
  2.1102 +			mod_timer(&cpe_poll_timer, jiffies + poll_time);
  2.1103 +		start_count = -1;
  2.1104 +	}
  2.1105 +
  2.1106 +	return IRQ_HANDLED;
  2.1107 +}
  2.1108 +
  2.1109 +/*
  2.1110 + *  ia64_mca_cpe_poll
  2.1111 + *
  2.1112 + *	Poll for Corrected Platform Errors (CPEs), trigger interrupt
  2.1113 + *	on first cpu, from there it will trickle through all the cpus.
  2.1114 + *
  2.1115 + * Inputs   :   dummy(unused)
  2.1116 + * Outputs  :   None
  2.1117 + *
  2.1118 + */
  2.1119 +static void
  2.1120 +ia64_mca_cpe_poll (unsigned long dummy)
  2.1121 +{
  2.1122 +	/* Trigger a CPE interrupt cascade  */
  2.1123 +	platform_send_ipi(first_cpu(cpu_online_map), IA64_CPEP_VECTOR, IA64_IPI_DM_INT, 0);
  2.1124 +}
  2.1125 +
  2.1126 +#endif /* CONFIG_ACPI */
  2.1127 +
  2.1128 +/*
  2.1129 + * C portion of the OS INIT handler
  2.1130 + *
  2.1131 + * Called from ia64_monarch_init_handler
  2.1132 + *
  2.1133 + * Inputs: pointer to pt_regs where processor info was saved.
  2.1134 + *
  2.1135 + * Returns:
  2.1136 + *   0 if SAL must warm boot the System
  2.1137 + *   1 if SAL must return to interrupted context using PAL_MC_RESUME
  2.1138 + *
  2.1139 + */
  2.1140 +void
  2.1141 +ia64_init_handler (struct pt_regs *pt, struct switch_stack *sw)
  2.1142 +{
  2.1143 +	pal_min_state_area_t *ms;
  2.1144 +
  2.1145 +	oops_in_progress = 1;	/* avoid deadlock in printk, but it makes recovery dodgy */
  2.1146 +	console_loglevel = 15;	/* make sure printks make it to console */
  2.1147 +
  2.1148 +	printk(KERN_INFO "Entered OS INIT handler. PSP=%lx\n",
  2.1149 +		ia64_sal_to_os_handoff_state.proc_state_param);
  2.1150 +
  2.1151 +	/*
  2.1152 +	 * Address of minstate area provided by PAL is physical,
  2.1153 +	 * uncacheable (bit 63 set). Convert to Linux virtual
  2.1154 +	 * address in region 6.
  2.1155 +	 */
  2.1156 +	ms = (pal_min_state_area_t *)(ia64_sal_to_os_handoff_state.pal_min_state | (6ul<<61));
  2.1157 +
  2.1158 +	init_handler_platform(ms, pt, sw);	/* call platform specific routines */
  2.1159 +}
  2.1160 +
  2.1161 +static int __init
  2.1162 +ia64_mca_disable_cpe_polling(char *str)
  2.1163 +{
  2.1164 +	cpe_poll_enabled = 0;
  2.1165 +	return 1;
  2.1166 +}
  2.1167 +
  2.1168 +__setup("disable_cpe_poll", ia64_mca_disable_cpe_polling);
  2.1169 +
  2.1170 +static struct irqaction cmci_irqaction = {
  2.1171 +	.handler =	ia64_mca_cmc_int_handler,
  2.1172 +	.flags =	SA_INTERRUPT,
  2.1173 +	.name =		"cmc_hndlr"
  2.1174 +};
  2.1175 +
  2.1176 +static struct irqaction cmcp_irqaction = {
  2.1177 +	.handler =	ia64_mca_cmc_int_caller,
  2.1178 +	.flags =	SA_INTERRUPT,
  2.1179 +	.name =		"cmc_poll"
  2.1180 +};
  2.1181 +
  2.1182 +static struct irqaction mca_rdzv_irqaction = {
  2.1183 +	.handler =	ia64_mca_rendez_int_handler,
  2.1184 +	.flags =	SA_INTERRUPT,
  2.1185 +	.name =		"mca_rdzv"
  2.1186 +};
  2.1187 +
  2.1188 +static struct irqaction mca_wkup_irqaction = {
  2.1189 +	.handler =	ia64_mca_wakeup_int_handler,
  2.1190 +	.flags =	SA_INTERRUPT,
  2.1191 +	.name =		"mca_wkup"
  2.1192 +};
  2.1193 +
  2.1194 +#ifdef CONFIG_ACPI
  2.1195 +static struct irqaction mca_cpe_irqaction = {
  2.1196 +	.handler =	ia64_mca_cpe_int_handler,
  2.1197 +	.flags =	SA_INTERRUPT,
  2.1198 +	.name =		"cpe_hndlr"
  2.1199 +};
  2.1200 +
  2.1201 +static struct irqaction mca_cpep_irqaction = {
  2.1202 +	.handler =	ia64_mca_cpe_int_caller,
  2.1203 +	.flags =	SA_INTERRUPT,
  2.1204 +	.name =		"cpe_poll"
  2.1205 +};
  2.1206 +#endif /* CONFIG_ACPI */
  2.1207 +
  2.1208 +/* Do per-CPU MCA-related initialization.  */
  2.1209 +
  2.1210 +void __devinit
  2.1211 +ia64_mca_cpu_init(void *cpu_data)
  2.1212 +{
  2.1213 +	void *pal_vaddr;
  2.1214 +
  2.1215 +	if (smp_processor_id() == 0) {
  2.1216 +		void *mca_data;
  2.1217 +		int cpu;
  2.1218 +
  2.1219 +		mca_data = alloc_bootmem(sizeof(struct ia64_mca_cpu)
  2.1220 +					 * NR_CPUS);
  2.1221 +		for (cpu = 0; cpu < NR_CPUS; cpu++) {
  2.1222 +			__per_cpu_mca[cpu] = __pa(mca_data);
  2.1223 +			mca_data += sizeof(struct ia64_mca_cpu);
  2.1224 +		}
  2.1225 +	}
  2.1226 +
  2.1227 +        /*
  2.1228 +         * The MCA info structure was allocated earlier and its
  2.1229 +         * physical address saved in __per_cpu_mca[cpu].  Copy that
  2.1230 +         * address * to ia64_mca_data so we can access it as a per-CPU
  2.1231 +         * variable.
  2.1232 +         */
  2.1233 +	__get_cpu_var(ia64_mca_data) = __per_cpu_mca[smp_processor_id()];
  2.1234 +
  2.1235 +	/*
  2.1236 +	 * Stash away a copy of the PTE needed to map the per-CPU page.
  2.1237 +	 * We may need it during MCA recovery.
  2.1238 +	 */
  2.1239 +	__get_cpu_var(ia64_mca_per_cpu_pte) =
  2.1240 +		pte_val(mk_pte_phys(__pa(cpu_data), PAGE_KERNEL));
  2.1241 +
  2.1242 +        /*
  2.1243 +         * Also, stash away a copy of the PAL address and the PTE
  2.1244 +         * needed to map it.
  2.1245 +         */
  2.1246 +        pal_vaddr = efi_get_pal_addr();
  2.1247 +	if (!pal_vaddr)
  2.1248 +		return;
  2.1249 +	__get_cpu_var(ia64_mca_pal_base) =
  2.1250 +		GRANULEROUNDDOWN((unsigned long) pal_vaddr);
  2.1251 +	__get_cpu_var(ia64_mca_pal_pte) = pte_val(mk_pte_phys(__pa(pal_vaddr),
  2.1252 +							      PAGE_KERNEL));
  2.1253 +}
  2.1254 +
  2.1255 +/*
  2.1256 + * ia64_mca_init
  2.1257 + *
  2.1258 + *  Do all the system level mca specific initialization.
  2.1259 + *
  2.1260 + *	1. Register spinloop and wakeup request interrupt vectors
  2.1261 + *
  2.1262 + *	2. Register OS_MCA handler entry point
  2.1263 + *
  2.1264 + *	3. Register OS_INIT handler entry point
  2.1265 + *
  2.1266 + *  4. Initialize MCA/CMC/INIT related log buffers maintained by the OS.
  2.1267 + *
  2.1268 + *  Note that this initialization is done very early before some kernel
  2.1269 + *  services are available.
  2.1270 + *
  2.1271 + *  Inputs  :   None
  2.1272 + *
  2.1273 + *  Outputs :   None
  2.1274 + */
  2.1275 +void __init
  2.1276 +ia64_mca_init(void)
  2.1277 +{
  2.1278 +	ia64_fptr_t *mon_init_ptr = (ia64_fptr_t *)ia64_monarch_init_handler;
  2.1279 +	ia64_fptr_t *slave_init_ptr = (ia64_fptr_t *)ia64_slave_init_handler;
  2.1280 +	ia64_fptr_t *mca_hldlr_ptr = (ia64_fptr_t *)ia64_os_mca_dispatch;
  2.1281 +	int i;
  2.1282 +	s64 rc;
  2.1283 +	struct ia64_sal_retval isrv;
  2.1284 +	u64 timeout = IA64_MCA_RENDEZ_TIMEOUT;	/* platform specific */
  2.1285 +
  2.1286 +	IA64_MCA_DEBUG("%s: begin\n", __FUNCTION__);
  2.1287 +
  2.1288 +	/* Clear the Rendez checkin flag for all cpus */
  2.1289 +	for(i = 0 ; i < NR_CPUS; i++)
  2.1290 +		ia64_mc_info.imi_rendez_checkin[i] = IA64_MCA_RENDEZ_CHECKIN_NOTDONE;
  2.1291 +
  2.1292 +	/*
  2.1293 +	 * Register the rendezvous spinloop and wakeup mechanism with SAL
  2.1294 +	 */
  2.1295 +
  2.1296 +	/* Register the rendezvous interrupt vector with SAL */
  2.1297 +	while (1) {
  2.1298 +		isrv = ia64_sal_mc_set_params(SAL_MC_PARAM_RENDEZ_INT,
  2.1299 +					      SAL_MC_PARAM_MECHANISM_INT,
  2.1300 +					      IA64_MCA_RENDEZ_VECTOR,
  2.1301 +					      timeout,
  2.1302 +					      SAL_MC_PARAM_RZ_ALWAYS);
  2.1303 +		rc = isrv.status;
  2.1304 +		if (rc == 0)
  2.1305 +			break;
  2.1306 +		if (rc == -2) {
  2.1307 +			printk(KERN_INFO "Increasing MCA rendezvous timeout from "
  2.1308 +				"%ld to %ld milliseconds\n", timeout, isrv.v0);
  2.1309 +			timeout = isrv.v0;
  2.1310 +			continue;
  2.1311 +		}
  2.1312 +		printk(KERN_ERR "Failed to register rendezvous interrupt "
  2.1313 +		       "with SAL (status %ld)\n", rc);
  2.1314 +		return;
  2.1315 +	}
  2.1316 +
  2.1317 +	/* Register the wakeup interrupt vector with SAL */
  2.1318 +	isrv = ia64_sal_mc_set_params(SAL_MC_PARAM_RENDEZ_WAKEUP,
  2.1319 +				      SAL_MC_PARAM_MECHANISM_INT,
  2.1320 +				      IA64_MCA_WAKEUP_VECTOR,
  2.1321 +				      0, 0);
  2.1322 +	rc = isrv.status;
  2.1323 +	if (rc) {
  2.1324 +		printk(KERN_ERR "Failed to register wakeup interrupt with SAL "
  2.1325 +		       "(status %ld)\n", rc);
  2.1326 +		return;
  2.1327 +	}
  2.1328 +
  2.1329 +	IA64_MCA_DEBUG("%s: registered MCA rendezvous spinloop and wakeup mech.\n", __FUNCTION__);
  2.1330 +
  2.1331 +	ia64_mc_info.imi_mca_handler        = ia64_tpa(mca_hldlr_ptr->fp);
  2.1332 +	/*
  2.1333 +	 * XXX - disable SAL checksum by setting size to 0; should be
  2.1334 +	 *	ia64_tpa(ia64_os_mca_dispatch_end) - ia64_tpa(ia64_os_mca_dispatch);
  2.1335 +	 */
  2.1336 +	ia64_mc_info.imi_mca_handler_size	= 0;
  2.1337 +
  2.1338 +	/* Register the os mca handler with SAL */
  2.1339 +	if ((rc = ia64_sal_set_vectors(SAL_VECTOR_OS_MCA,
  2.1340 +				       ia64_mc_info.imi_mca_handler,
  2.1341 +				       ia64_tpa(mca_hldlr_ptr->gp),
  2.1342 +				       ia64_mc_info.imi_mca_handler_size,
  2.1343 +				       0, 0, 0)))
  2.1344 +	{
  2.1345 +		printk(KERN_ERR "Failed to register OS MCA handler with SAL "
  2.1346 +		       "(status %ld)\n", rc);
  2.1347 +		return;
  2.1348 +	}
  2.1349 +
  2.1350 +	IA64_MCA_DEBUG("%s: registered OS MCA handler with SAL at 0x%lx, gp = 0x%lx\n", __FUNCTION__,
  2.1351 +		       ia64_mc_info.imi_mca_handler, ia64_tpa(mca_hldlr_ptr->gp));
  2.1352 +
  2.1353 +	/*
  2.1354 +	 * XXX - disable SAL checksum by setting size to 0, should be
  2.1355 +	 * size of the actual init handler in mca_asm.S.
  2.1356 +	 */
  2.1357 +	ia64_mc_info.imi_monarch_init_handler		= ia64_tpa(mon_init_ptr->fp);
  2.1358 +	ia64_mc_info.imi_monarch_init_handler_size	= 0;
  2.1359 +	ia64_mc_info.imi_slave_init_handler		= ia64_tpa(slave_init_ptr->fp);
  2.1360 +	ia64_mc_info.imi_slave_init_handler_size	= 0;
  2.1361 +
  2.1362 +	IA64_MCA_DEBUG("%s: OS INIT handler at %lx\n", __FUNCTION__,
  2.1363 +		       ia64_mc_info.imi_monarch_init_handler);
  2.1364 +
  2.1365 +	/* Register the os init handler with SAL */
  2.1366 +	if ((rc = ia64_sal_set_vectors(SAL_VECTOR_OS_INIT,
  2.1367 +				       ia64_mc_info.imi_monarch_init_handler,
  2.1368 +				       ia64_tpa(ia64_getreg(_IA64_REG_GP)),
  2.1369 +				       ia64_mc_info.imi_monarch_init_handler_size,
  2.1370 +				       ia64_mc_info.imi_slave_init_handler,
  2.1371 +				       ia64_tpa(ia64_getreg(_IA64_REG_GP)),
  2.1372 +				       ia64_mc_info.imi_slave_init_handler_size)))
  2.1373 +	{
  2.1374 +		printk(KERN_ERR "Failed to register m/s INIT handlers with SAL "
  2.1375 +		       "(status %ld)\n", rc);
  2.1376 +		return;
  2.1377 +	}
  2.1378 +
  2.1379 +	IA64_MCA_DEBUG("%s: registered OS INIT handler with SAL\n", __FUNCTION__);
  2.1380 +
  2.1381 +	/*
  2.1382 +	 *  Configure the CMCI/P vector and handler. Interrupts for CMC are
  2.1383 +	 *  per-processor, so AP CMC interrupts are setup in smp_callin() (smpboot.c).
  2.1384 +	 */
  2.1385 +	register_percpu_irq(IA64_CMC_VECTOR, &cmci_irqaction);
  2.1386 +	register_percpu_irq(IA64_CMCP_VECTOR, &cmcp_irqaction);
  2.1387 +	ia64_mca_cmc_vector_setup();       /* Setup vector on BSP */
  2.1388 +
  2.1389 +	/* Setup the MCA rendezvous interrupt vector */
  2.1390 +	register_percpu_irq(IA64_MCA_RENDEZ_VECTOR, &mca_rdzv_irqaction);
  2.1391 +
  2.1392 +	/* Setup the MCA wakeup interrupt vector */
  2.1393 +	register_percpu_irq(IA64_MCA_WAKEUP_VECTOR, &mca_wkup_irqaction);
  2.1394 +
  2.1395 +#ifdef CONFIG_ACPI
  2.1396 +	/* Setup the CPEI/P handler */
  2.1397 +	register_percpu_irq(IA64_CPEP_VECTOR, &mca_cpep_irqaction);
  2.1398 +#endif
  2.1399 +
  2.1400 +	/* Initialize the areas set aside by the OS to buffer the
  2.1401 +	 * platform/processor error states for MCA/INIT/CMC
  2.1402 +	 * handling.
  2.1403 +	 */
  2.1404 +	ia64_log_init(SAL_INFO_TYPE_MCA);
  2.1405 +	ia64_log_init(SAL_INFO_TYPE_INIT);
  2.1406 +	ia64_log_init(SAL_INFO_TYPE_CMC);
  2.1407 +	ia64_log_init(SAL_INFO_TYPE_CPE);
  2.1408 +
  2.1409 +	mca_init = 1;
  2.1410 +	printk(KERN_INFO "MCA related initialization done\n");
  2.1411 +}
  2.1412 +
  2.1413 +/*
  2.1414 + * ia64_mca_late_init
  2.1415 + *
  2.1416 + *	Opportunity to setup things that require initialization later
  2.1417 + *	than ia64_mca_init.  Setup a timer to poll for CPEs if the
  2.1418 + *	platform doesn't support an interrupt driven mechanism.
  2.1419 + *
  2.1420 + *  Inputs  :   None
  2.1421 + *  Outputs :   Status
  2.1422 + */
  2.1423 +static int __init
  2.1424 +ia64_mca_late_init(void)
  2.1425 +{
  2.1426 +	if (!mca_init)
  2.1427 +		return 0;
  2.1428 +
  2.1429 +	/* Setup the CMCI/P vector and handler */
  2.1430 +	init_timer(&cmc_poll_timer);
  2.1431 +	cmc_poll_timer.function = ia64_mca_cmc_poll;
  2.1432 +
  2.1433 +	/* Unmask/enable the vector */
  2.1434 +	cmc_polling_enabled = 0;
  2.1435 +	schedule_work(&cmc_enable_work);
  2.1436 +
  2.1437 +	IA64_MCA_DEBUG("%s: CMCI/P setup and enabled.\n", __FUNCTION__);
  2.1438 +
  2.1439 +#ifdef CONFIG_ACPI
  2.1440 +	/* Setup the CPEI/P vector and handler */
  2.1441 +	cpe_vector = acpi_request_vector(ACPI_INTERRUPT_CPEI);
  2.1442 +	init_timer(&cpe_poll_timer);
  2.1443 +	cpe_poll_timer.function = ia64_mca_cpe_poll;
  2.1444 +
  2.1445 +	{
  2.1446 +		irq_desc_t *desc;
  2.1447 +		unsigned int irq;
  2.1448 +
  2.1449 +		if (cpe_vector >= 0) {
  2.1450 +			/* If platform supports CPEI, enable the irq. */
  2.1451 +			cpe_poll_enabled = 0;
  2.1452 +			for (irq = 0; irq < NR_IRQS; ++irq)
  2.1453 +				if (irq_to_vector(irq) == cpe_vector) {
  2.1454 +					desc = irq_descp(irq);
  2.1455 +					desc->status |= IRQ_PER_CPU;
  2.1456 +					setup_irq(irq, &mca_cpe_irqaction);
  2.1457 +				}
  2.1458 +			ia64_mca_register_cpev(cpe_vector);
  2.1459 +			IA64_MCA_DEBUG("%s: CPEI/P setup and enabled.\n", __FUNCTION__);
  2.1460 +		} else {
  2.1461 +			/* If platform doesn't support CPEI, get the timer going. */
  2.1462 +			if (cpe_poll_enabled) {
  2.1463 +				ia64_mca_cpe_poll(0UL);
  2.1464 +				IA64_MCA_DEBUG("%s: CPEP setup and enabled.\n", __FUNCTION__);
  2.1465 +			}
  2.1466 +		}
  2.1467 +	}
  2.1468 +#endif
  2.1469 +
  2.1470 +	return 0;
  2.1471 +}
  2.1472 +
  2.1473 +device_initcall(ia64_mca_late_init);