mca.c 4.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5
/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
6
 * Copyright (c) 2000-2006 Silicon Graphics, Inc.  All Rights Reserved.
L
Linus Torvalds 已提交
7 8 9 10 11 12
 */

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/vmalloc.h>
J
Jes Sorensen 已提交
13
#include <linux/mutex.h>
L
Linus Torvalds 已提交
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
#include <asm/mca.h>
#include <asm/sal.h>
#include <asm/sn/sn_sal.h>

/*
 * Interval for calling SAL to poll for errors that do NOT cause error
 * interrupts. SAL will raise a CPEI if any errors are present that
 * need to be logged.
 */
#define CPEI_INTERVAL	(5*HZ)

struct timer_list sn_cpei_timer;
void sn_init_cpei_timer(void);

/* Printing oemdata from mca uses data that is not passed through SAL, it is
 * global.  Only one user at a time.
 */
J
Jes Sorensen 已提交
31
static DEFINE_MUTEX(sn_oemdata_mutex);
L
Linus Torvalds 已提交
32 33 34 35 36 37 38 39 40
static u8 **sn_oemdata;
static u64 *sn_oemdata_size, sn_oemdata_bufsize;

/*
 * print_hook
 *
 * This function is the callback routine that SAL calls to log error
 * info for platform errors.  buf is appended to sn_oemdata, resizing as
 * required.
41 42 43 44 45
 * Note: this is a SAL to OS callback, running under the same rules as the SAL
 * code.  SAL calls are run with preempt disabled so this routine must not
 * sleep.  vmalloc can sleep so print_hook cannot resize the output buffer
 * itself, instead it must set the required size and return to let the caller
 * resize the buffer then redrive the SAL call.
L
Linus Torvalds 已提交
46 47 48 49 50 51 52 53 54 55
 */
static int print_hook(const char *fmt, ...)
{
	char buf[400];
	int len;
	va_list args;
	va_start(args, fmt);
	vsnprintf(buf, sizeof(buf), fmt, args);
	va_end(args);
	len = strlen(buf);
56 57
	if (*sn_oemdata_size + len <= sn_oemdata_bufsize)
		memcpy(*sn_oemdata + *sn_oemdata_size, buf, len);
L
Linus Torvalds 已提交
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
	*sn_oemdata_size += len;
	return 0;
}

static void sn_cpei_handler(int irq, void *devid, struct pt_regs *regs)
{
	/*
	 * this function's sole purpose is to call SAL when we receive
	 * a CE interrupt from SHUB or when the timer routine decides
	 * we need to call SAL to check for CEs.
	 */

	/* CALL SAL_LOG_CE */

	ia64_sn_plat_cpei_handler();
}

static void sn_cpei_timer_handler(unsigned long dummy)
{
	sn_cpei_handler(-1, NULL, NULL);
	mod_timer(&sn_cpei_timer, jiffies + CPEI_INTERVAL);
}

void sn_init_cpei_timer(void)
{
	init_timer(&sn_cpei_timer);
	sn_cpei_timer.expires = jiffies + CPEI_INTERVAL;
	sn_cpei_timer.function = sn_cpei_timer_handler;
	add_timer(&sn_cpei_timer);
}

static int
sn_platform_plat_specific_err_print(const u8 * sect_header, u8 ** oemdata,
				    u64 * oemdata_size)
{
J
Jes Sorensen 已提交
93
	mutex_lock(&sn_oemdata_mutex);
L
Linus Torvalds 已提交
94 95 96
	sn_oemdata = oemdata;
	sn_oemdata_size = oemdata_size;
	sn_oemdata_bufsize = 0;
97 98 99 100 101 102 103 104 105 106 107 108 109 110
	*sn_oemdata_size = PAGE_SIZE;	/* first guess at how much data will be generated */
	while (*sn_oemdata_size > sn_oemdata_bufsize) {
		u8 *newbuf = vmalloc(*sn_oemdata_size);
		if (!newbuf) {
			printk(KERN_ERR "%s: unable to extend sn_oemdata\n",
			       __FUNCTION__);
			return 1;
		}
		vfree(*sn_oemdata);
		*sn_oemdata = newbuf;
		sn_oemdata_bufsize = *sn_oemdata_size;
		*sn_oemdata_size = 0;
		ia64_sn_plat_specific_err_print(print_hook, (char *)sect_header);
	}
J
Jes Sorensen 已提交
111
	mutex_unlock(&sn_oemdata_mutex);
L
Linus Torvalds 已提交
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
	return 0;
}

/* Callback when userspace salinfo wants to decode oem data via the platform
 * kernel and/or prom.
 */
int sn_salinfo_platform_oemdata(const u8 *sect_header, u8 **oemdata, u64 *oemdata_size)
{
	efi_guid_t guid = *(efi_guid_t *)sect_header;
	int valid = 0;
	*oemdata_size = 0;
	vfree(*oemdata);
	*oemdata = NULL;
	if (efi_guidcmp(guid, SAL_PLAT_SPECIFIC_ERR_SECT_GUID) == 0) {
		sal_log_plat_specific_err_info_t *psei = (sal_log_plat_specific_err_info_t *)sect_header;
		valid = psei->valid.oem_data;
	} else if (efi_guidcmp(guid, SAL_PLAT_MEM_DEV_ERR_SECT_GUID) == 0) {
		sal_log_mem_dev_err_info_t *mdei = (sal_log_mem_dev_err_info_t *)sect_header;
		valid = mdei->valid.oem_data;
	}
	if (valid)
		return sn_platform_plat_specific_err_print(sect_header, oemdata, oemdata_size);
	else
		return 0;
}

static int __init sn_salinfo_init(void)
{
140 141
	if (ia64_platform_is("sn2"))
		salinfo_platform_oemdata = &sn_salinfo_platform_oemdata;
L
Linus Torvalds 已提交
142 143 144 145
	return 0;
}

module_init(sn_salinfo_init)