1/* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (c) 2000-2006 Silicon Graphics, Inc. All Rights Reserved. 7 */ 8 9#include <linux/types.h> 10#include <linux/kernel.h> 11#include <linux/timer.h> 12#include <linux/vmalloc.h> 13#include <linux/mutex.h> 14#include <asm/mca.h> 15#include <asm/sal.h> 16#include <asm/sn/sn_sal.h> 17 18/* 19 * Interval for calling SAL to poll for errors that do NOT cause error 20 * interrupts. SAL will raise a CPEI if any errors are present that 21 * need to be logged. 22 */ 23#define CPEI_INTERVAL (5*HZ) 24 25struct timer_list sn_cpei_timer; 26void sn_init_cpei_timer(void); 27 28/* Printing oemdata from mca uses data that is not passed through SAL, it is 29 * global. Only one user at a time. 30 */ 31static DEFINE_MUTEX(sn_oemdata_mutex); 32static u8 **sn_oemdata; 33static u64 *sn_oemdata_size, sn_oemdata_bufsize; 34 35/* 36 * print_hook 37 * 38 * This function is the callback routine that SAL calls to log error 39 * info for platform errors. buf is appended to sn_oemdata, resizing as 40 * required. 41 * Note: this is a SAL to OS callback, running under the same rules as the SAL 42 * code. SAL calls are run with preempt disabled so this routine must not 43 * sleep. vmalloc can sleep so print_hook cannot resize the output buffer 44 * itself, instead it must set the required size and return to let the caller 45 * resize the buffer then redrive the SAL call. 46 */ 47static int print_hook(const char *fmt, ...) 48{ 49 char buf[400]; 50 int len; 51 va_list args; 52 va_start(args, fmt); 53 vsnprintf(buf, sizeof(buf), fmt, args); 54 va_end(args); 55 len = strlen(buf); 56 if (*sn_oemdata_size + len <= sn_oemdata_bufsize) 57 memcpy(*sn_oemdata + *sn_oemdata_size, buf, len); 58 *sn_oemdata_size += len; 59 return 0; 60} 61 62static void sn_cpei_handler(int irq, void *devid, struct pt_regs *regs) 63{ 64 /* 65 * this function's sole purpose is to call SAL when we receive 66 * a CE interrupt from SHUB or when the timer routine decides 67 * we need to call SAL to check for CEs. 68 */ 69 70 /* CALL SAL_LOG_CE */ 71 72 ia64_sn_plat_cpei_handler(); 73} 74 75static void sn_cpei_timer_handler(unsigned long dummy) 76{ 77 sn_cpei_handler(-1, NULL, NULL); 78 mod_timer(&sn_cpei_timer, jiffies + CPEI_INTERVAL); 79} 80 81void sn_init_cpei_timer(void) 82{ 83 init_timer(&sn_cpei_timer); 84 sn_cpei_timer.expires = jiffies + CPEI_INTERVAL; 85 sn_cpei_timer.function = sn_cpei_timer_handler; 86 add_timer(&sn_cpei_timer); 87} 88 89static int 90sn_platform_plat_specific_err_print(const u8 * sect_header, u8 ** oemdata, 91 u64 * oemdata_size) 92{ 93 mutex_lock(&sn_oemdata_mutex); 94 sn_oemdata = oemdata; 95 sn_oemdata_size = oemdata_size; 96 sn_oemdata_bufsize = 0; 97 *sn_oemdata_size = PAGE_SIZE; /* first guess at how much data will be generated */ 98 while (*sn_oemdata_size > sn_oemdata_bufsize) { 99 u8 *newbuf = vmalloc(*sn_oemdata_size); 100 if (!newbuf) { 101 mutex_unlock(&sn_oemdata_mutex); 102 printk(KERN_ERR "%s: unable to extend sn_oemdata\n", 103 __func__); 104 return 1; 105 } 106 vfree(*sn_oemdata); 107 *sn_oemdata = newbuf; 108 sn_oemdata_bufsize = *sn_oemdata_size; 109 *sn_oemdata_size = 0; 110 ia64_sn_plat_specific_err_print(print_hook, (char *)sect_header); 111 } 112 mutex_unlock(&sn_oemdata_mutex); 113 return 0; 114} 115 116/* Callback when userspace salinfo wants to decode oem data via the platform 117 * kernel and/or prom. 118 */ 119int sn_salinfo_platform_oemdata(const u8 *sect_header, u8 **oemdata, u64 *oemdata_size) 120{ 121 efi_guid_t guid = *(efi_guid_t *)sect_header; 122 int valid = 0; 123 *oemdata_size = 0; 124 vfree(*oemdata); 125 *oemdata = NULL; 126 if (efi_guidcmp(guid, SAL_PLAT_SPECIFIC_ERR_SECT_GUID) == 0) { 127 sal_log_plat_specific_err_info_t *psei = (sal_log_plat_specific_err_info_t *)sect_header; 128 valid = psei->valid.oem_data; 129 } else if (efi_guidcmp(guid, SAL_PLAT_MEM_DEV_ERR_SECT_GUID) == 0) { 130 sal_log_mem_dev_err_info_t *mdei = (sal_log_mem_dev_err_info_t *)sect_header; 131 valid = mdei->valid.oem_data; 132 } 133 if (valid) 134 return sn_platform_plat_specific_err_print(sect_header, oemdata, oemdata_size); 135 else 136 return 0; 137} 138 139static int __init sn_salinfo_init(void) 140{ 141 if (ia64_platform_is("sn2")) 142 salinfo_platform_oemdata = &sn_salinfo_platform_oemdata; 143 return 0; 144} 145 146module_init(sn_salinfo_init) 147