1 /* 2 * OPAL hypervisor Maintenance interrupt handling support in PowreNV. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; If not, see <http://www.gnu.org/licenses/>. 16 * 17 * Copyright 2014 IBM Corporation 18 * Author: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com> 19 */ 20 21 #undef DEBUG 22 23 #include <linux/kernel.h> 24 #include <linux/init.h> 25 #include <linux/of.h> 26 #include <linux/mm.h> 27 #include <linux/slab.h> 28 29 #include <asm/opal.h> 30 #include <asm/cputable.h> 31 32 static int opal_hmi_handler_nb_init; 33 struct OpalHmiEvtNode { 34 struct list_head list; 35 struct OpalHMIEvent hmi_evt; 36 }; 37 static LIST_HEAD(opal_hmi_evt_list); 38 static DEFINE_SPINLOCK(opal_hmi_evt_lock); 39 40 static void print_hmi_event_info(struct OpalHMIEvent *hmi_evt) 41 { 42 const char *level, *sevstr, *error_info; 43 static const char *hmi_error_types[] = { 44 "Malfunction Alert", 45 "Processor Recovery done", 46 "Processor recovery occurred again", 47 "Processor recovery occurred for masked error", 48 "Timer facility experienced an error", 49 "TFMR SPR is corrupted", 50 "UPS (Uniterrupted Power System) Overflow indication", 51 "An XSCOM operation failure", 52 "An XSCOM operation completed", 53 "SCOM has set a reserved FIR bit to cause recovery", 54 "Debug trigger has set a reserved FIR bit to cause recovery", 55 "A hypervisor resource error occurred" 56 }; 57 58 /* Print things out */ 59 if (hmi_evt->version != OpalHMIEvt_V1) { 60 pr_err("HMI Interrupt, Unknown event version %d !\n", 61 hmi_evt->version); 62 return; 63 } 64 switch (hmi_evt->severity) { 65 case OpalHMI_SEV_NO_ERROR: 66 level = KERN_INFO; 67 sevstr = "Harmless"; 68 break; 69 case OpalHMI_SEV_WARNING: 70 level = KERN_WARNING; 71 sevstr = ""; 72 break; 73 case OpalHMI_SEV_ERROR_SYNC: 74 level = KERN_ERR; 75 sevstr = "Severe"; 76 break; 77 case OpalHMI_SEV_FATAL: 78 default: 79 level = KERN_ERR; 80 sevstr = "Fatal"; 81 break; 82 } 83 84 printk("%s%s Hypervisor Maintenance interrupt [%s]\n", 85 level, sevstr, 86 hmi_evt->disposition == OpalHMI_DISPOSITION_RECOVERED ? 87 "Recovered" : "Not recovered"); 88 error_info = hmi_evt->type < ARRAY_SIZE(hmi_error_types) ? 89 hmi_error_types[hmi_evt->type] 90 : "Unknown"; 91 printk("%s Error detail: %s\n", level, error_info); 92 printk("%s HMER: %016llx\n", level, be64_to_cpu(hmi_evt->hmer)); 93 if ((hmi_evt->type == OpalHMI_ERROR_TFAC) || 94 (hmi_evt->type == OpalHMI_ERROR_TFMR_PARITY)) 95 printk("%s TFMR: %016llx\n", level, 96 be64_to_cpu(hmi_evt->tfmr)); 97 } 98 99 static void hmi_event_handler(struct work_struct *work) 100 { 101 unsigned long flags; 102 struct OpalHMIEvent *hmi_evt; 103 struct OpalHmiEvtNode *msg_node; 104 uint8_t disposition; 105 106 spin_lock_irqsave(&opal_hmi_evt_lock, flags); 107 while (!list_empty(&opal_hmi_evt_list)) { 108 msg_node = list_entry(opal_hmi_evt_list.next, 109 struct OpalHmiEvtNode, list); 110 list_del(&msg_node->list); 111 spin_unlock_irqrestore(&opal_hmi_evt_lock, flags); 112 113 hmi_evt = (struct OpalHMIEvent *) &msg_node->hmi_evt; 114 print_hmi_event_info(hmi_evt); 115 disposition = hmi_evt->disposition; 116 kfree(msg_node); 117 118 /* 119 * Check if HMI event has been recovered or not. If not 120 * then we can't continue, invoke panic. 121 */ 122 if (disposition != OpalHMI_DISPOSITION_RECOVERED) 123 panic("Unrecoverable HMI exception"); 124 125 spin_lock_irqsave(&opal_hmi_evt_lock, flags); 126 } 127 spin_unlock_irqrestore(&opal_hmi_evt_lock, flags); 128 } 129 130 static DECLARE_WORK(hmi_event_work, hmi_event_handler); 131 /* 132 * opal_handle_hmi_event - notifier handler that queues up HMI events 133 * to be preocessed later. 134 */ 135 static int opal_handle_hmi_event(struct notifier_block *nb, 136 unsigned long msg_type, void *msg) 137 { 138 unsigned long flags; 139 struct OpalHMIEvent *hmi_evt; 140 struct opal_msg *hmi_msg = msg; 141 struct OpalHmiEvtNode *msg_node; 142 143 /* Sanity Checks */ 144 if (msg_type != OPAL_MSG_HMI_EVT) 145 return 0; 146 147 /* HMI event info starts from param[0] */ 148 hmi_evt = (struct OpalHMIEvent *)&hmi_msg->params[0]; 149 150 /* Delay the logging of HMI events to workqueue. */ 151 msg_node = kzalloc(sizeof(*msg_node), GFP_ATOMIC); 152 if (!msg_node) { 153 pr_err("HMI: out of memory, Opal message event not handled\n"); 154 return -ENOMEM; 155 } 156 memcpy(&msg_node->hmi_evt, hmi_evt, sizeof(struct OpalHMIEvent)); 157 158 spin_lock_irqsave(&opal_hmi_evt_lock, flags); 159 list_add(&msg_node->list, &opal_hmi_evt_list); 160 spin_unlock_irqrestore(&opal_hmi_evt_lock, flags); 161 162 schedule_work(&hmi_event_work); 163 return 0; 164 } 165 166 static struct notifier_block opal_hmi_handler_nb = { 167 .notifier_call = opal_handle_hmi_event, 168 .next = NULL, 169 .priority = 0, 170 }; 171 172 static int __init opal_hmi_handler_init(void) 173 { 174 int ret; 175 176 if (!opal_hmi_handler_nb_init) { 177 ret = opal_message_notifier_register( 178 OPAL_MSG_HMI_EVT, &opal_hmi_handler_nb); 179 if (ret) { 180 pr_err("%s: Can't register OPAL event notifier (%d)\n", 181 __func__, ret); 182 return ret; 183 } 184 opal_hmi_handler_nb_init = 1; 185 } 186 return 0; 187 } 188 subsys_initcall(opal_hmi_handler_init); 189