Merge branch 'for-linus-4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/mason...
[deliverable/linux.git] / arch / powerpc / platforms / powernv / opal-hmi.c
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 #include <asm/machdep.h>
32
33 static int opal_hmi_handler_nb_init;
34 struct OpalHmiEvtNode {
35 struct list_head list;
36 struct OpalHMIEvent hmi_evt;
37 };
38
39 struct xstop_reason {
40 uint32_t xstop_reason;
41 const char *unit_failed;
42 const char *description;
43 };
44
45 static LIST_HEAD(opal_hmi_evt_list);
46 static DEFINE_SPINLOCK(opal_hmi_evt_lock);
47
48 static void print_core_checkstop_reason(const char *level,
49 struct OpalHMIEvent *hmi_evt)
50 {
51 int i;
52 static const struct xstop_reason xstop_reason[] = {
53 { CORE_CHECKSTOP_IFU_REGFILE, "IFU",
54 "RegFile core check stop" },
55 { CORE_CHECKSTOP_IFU_LOGIC, "IFU", "Logic core check stop" },
56 { CORE_CHECKSTOP_PC_DURING_RECOV, "PC",
57 "Core checkstop during recovery" },
58 { CORE_CHECKSTOP_ISU_REGFILE, "ISU",
59 "RegFile core check stop (mapper error)" },
60 { CORE_CHECKSTOP_ISU_LOGIC, "ISU", "Logic core check stop" },
61 { CORE_CHECKSTOP_FXU_LOGIC, "FXU", "Logic core check stop" },
62 { CORE_CHECKSTOP_VSU_LOGIC, "VSU", "Logic core check stop" },
63 { CORE_CHECKSTOP_PC_RECOV_IN_MAINT_MODE, "PC",
64 "Recovery in maintenance mode" },
65 { CORE_CHECKSTOP_LSU_REGFILE, "LSU",
66 "RegFile core check stop" },
67 { CORE_CHECKSTOP_PC_FWD_PROGRESS, "PC",
68 "Forward Progress Error" },
69 { CORE_CHECKSTOP_LSU_LOGIC, "LSU", "Logic core check stop" },
70 { CORE_CHECKSTOP_PC_LOGIC, "PC", "Logic core check stop" },
71 { CORE_CHECKSTOP_PC_HYP_RESOURCE, "PC",
72 "Hypervisor Resource error - core check stop" },
73 { CORE_CHECKSTOP_PC_HANG_RECOV_FAILED, "PC",
74 "Hang Recovery Failed (core check stop)" },
75 { CORE_CHECKSTOP_PC_AMBI_HANG_DETECTED, "PC",
76 "Ambiguous Hang Detected (unknown source)" },
77 { CORE_CHECKSTOP_PC_DEBUG_TRIG_ERR_INJ, "PC",
78 "Debug Trigger Error inject" },
79 { CORE_CHECKSTOP_PC_SPRD_HYP_ERR_INJ, "PC",
80 "Hypervisor check stop via SPRC/SPRD" },
81 };
82
83 /* Validity check */
84 if (!hmi_evt->u.xstop_error.xstop_reason) {
85 printk("%s Unknown Core check stop.\n", level);
86 return;
87 }
88
89 printk("%s CPU PIR: %08x\n", level,
90 be32_to_cpu(hmi_evt->u.xstop_error.u.pir));
91 for (i = 0; i < ARRAY_SIZE(xstop_reason); i++)
92 if (be32_to_cpu(hmi_evt->u.xstop_error.xstop_reason) &
93 xstop_reason[i].xstop_reason)
94 printk("%s [Unit: %-3s] %s\n", level,
95 xstop_reason[i].unit_failed,
96 xstop_reason[i].description);
97 }
98
99 static void print_nx_checkstop_reason(const char *level,
100 struct OpalHMIEvent *hmi_evt)
101 {
102 int i;
103 static const struct xstop_reason xstop_reason[] = {
104 { NX_CHECKSTOP_SHM_INVAL_STATE_ERR, "DMA & Engine",
105 "SHM invalid state error" },
106 { NX_CHECKSTOP_DMA_INVAL_STATE_ERR_1, "DMA & Engine",
107 "DMA invalid state error bit 15" },
108 { NX_CHECKSTOP_DMA_INVAL_STATE_ERR_2, "DMA & Engine",
109 "DMA invalid state error bit 16" },
110 { NX_CHECKSTOP_DMA_CH0_INVAL_STATE_ERR, "DMA & Engine",
111 "Channel 0 invalid state error" },
112 { NX_CHECKSTOP_DMA_CH1_INVAL_STATE_ERR, "DMA & Engine",
113 "Channel 1 invalid state error" },
114 { NX_CHECKSTOP_DMA_CH2_INVAL_STATE_ERR, "DMA & Engine",
115 "Channel 2 invalid state error" },
116 { NX_CHECKSTOP_DMA_CH3_INVAL_STATE_ERR, "DMA & Engine",
117 "Channel 3 invalid state error" },
118 { NX_CHECKSTOP_DMA_CH4_INVAL_STATE_ERR, "DMA & Engine",
119 "Channel 4 invalid state error" },
120 { NX_CHECKSTOP_DMA_CH5_INVAL_STATE_ERR, "DMA & Engine",
121 "Channel 5 invalid state error" },
122 { NX_CHECKSTOP_DMA_CH6_INVAL_STATE_ERR, "DMA & Engine",
123 "Channel 6 invalid state error" },
124 { NX_CHECKSTOP_DMA_CH7_INVAL_STATE_ERR, "DMA & Engine",
125 "Channel 7 invalid state error" },
126 { NX_CHECKSTOP_DMA_CRB_UE, "DMA & Engine",
127 "UE error on CRB(CSB address, CCB)" },
128 { NX_CHECKSTOP_DMA_CRB_SUE, "DMA & Engine",
129 "SUE error on CRB(CSB address, CCB)" },
130 { NX_CHECKSTOP_PBI_ISN_UE, "PowerBus Interface",
131 "CRB Kill ISN received while holding ISN with UE error" },
132 };
133
134 /* Validity check */
135 if (!hmi_evt->u.xstop_error.xstop_reason) {
136 printk("%s Unknown NX check stop.\n", level);
137 return;
138 }
139
140 printk("%s NX checkstop on CHIP ID: %x\n", level,
141 be32_to_cpu(hmi_evt->u.xstop_error.u.chip_id));
142 for (i = 0; i < ARRAY_SIZE(xstop_reason); i++)
143 if (be32_to_cpu(hmi_evt->u.xstop_error.xstop_reason) &
144 xstop_reason[i].xstop_reason)
145 printk("%s [Unit: %-3s] %s\n", level,
146 xstop_reason[i].unit_failed,
147 xstop_reason[i].description);
148 }
149
150 static void print_checkstop_reason(const char *level,
151 struct OpalHMIEvent *hmi_evt)
152 {
153 uint8_t type = hmi_evt->u.xstop_error.xstop_type;
154 switch (type) {
155 case CHECKSTOP_TYPE_CORE:
156 print_core_checkstop_reason(level, hmi_evt);
157 break;
158 case CHECKSTOP_TYPE_NX:
159 print_nx_checkstop_reason(level, hmi_evt);
160 break;
161 default:
162 printk("%s Unknown Malfunction Alert of type %d\n",
163 level, type);
164 break;
165 }
166 }
167
168 static void print_hmi_event_info(struct OpalHMIEvent *hmi_evt)
169 {
170 const char *level, *sevstr, *error_info;
171 static const char *hmi_error_types[] = {
172 "Malfunction Alert",
173 "Processor Recovery done",
174 "Processor recovery occurred again",
175 "Processor recovery occurred for masked error",
176 "Timer facility experienced an error",
177 "TFMR SPR is corrupted",
178 "UPS (Uniterrupted Power System) Overflow indication",
179 "An XSCOM operation failure",
180 "An XSCOM operation completed",
181 "SCOM has set a reserved FIR bit to cause recovery",
182 "Debug trigger has set a reserved FIR bit to cause recovery",
183 "A hypervisor resource error occurred"
184 };
185
186 /* Print things out */
187 if (hmi_evt->version < OpalHMIEvt_V1) {
188 pr_err("HMI Interrupt, Unknown event version %d !\n",
189 hmi_evt->version);
190 return;
191 }
192 switch (hmi_evt->severity) {
193 case OpalHMI_SEV_NO_ERROR:
194 level = KERN_INFO;
195 sevstr = "Harmless";
196 break;
197 case OpalHMI_SEV_WARNING:
198 level = KERN_WARNING;
199 sevstr = "";
200 break;
201 case OpalHMI_SEV_ERROR_SYNC:
202 level = KERN_ERR;
203 sevstr = "Severe";
204 break;
205 case OpalHMI_SEV_FATAL:
206 default:
207 level = KERN_ERR;
208 sevstr = "Fatal";
209 break;
210 }
211
212 printk("%s%s Hypervisor Maintenance interrupt [%s]\n",
213 level, sevstr,
214 hmi_evt->disposition == OpalHMI_DISPOSITION_RECOVERED ?
215 "Recovered" : "Not recovered");
216 error_info = hmi_evt->type < ARRAY_SIZE(hmi_error_types) ?
217 hmi_error_types[hmi_evt->type]
218 : "Unknown";
219 printk("%s Error detail: %s\n", level, error_info);
220 printk("%s HMER: %016llx\n", level, be64_to_cpu(hmi_evt->hmer));
221 if ((hmi_evt->type == OpalHMI_ERROR_TFAC) ||
222 (hmi_evt->type == OpalHMI_ERROR_TFMR_PARITY))
223 printk("%s TFMR: %016llx\n", level,
224 be64_to_cpu(hmi_evt->tfmr));
225
226 if (hmi_evt->version < OpalHMIEvt_V2)
227 return;
228
229 /* OpalHMIEvt_V2 and above provides reason for malfunction alert. */
230 if (hmi_evt->type == OpalHMI_ERROR_MALFUNC_ALERT)
231 print_checkstop_reason(level, hmi_evt);
232 }
233
234 static void hmi_event_handler(struct work_struct *work)
235 {
236 unsigned long flags;
237 struct OpalHMIEvent *hmi_evt;
238 struct OpalHmiEvtNode *msg_node;
239 uint8_t disposition;
240 struct opal_msg msg;
241 int unrecoverable = 0;
242
243 spin_lock_irqsave(&opal_hmi_evt_lock, flags);
244 while (!list_empty(&opal_hmi_evt_list)) {
245 msg_node = list_entry(opal_hmi_evt_list.next,
246 struct OpalHmiEvtNode, list);
247 list_del(&msg_node->list);
248 spin_unlock_irqrestore(&opal_hmi_evt_lock, flags);
249
250 hmi_evt = (struct OpalHMIEvent *) &msg_node->hmi_evt;
251 print_hmi_event_info(hmi_evt);
252 disposition = hmi_evt->disposition;
253 kfree(msg_node);
254
255 /*
256 * Check if HMI event has been recovered or not. If not
257 * then kernel can't continue, we need to panic.
258 * But before we do that, display all the HMI event
259 * available on the list and set unrecoverable flag to 1.
260 */
261 if (disposition != OpalHMI_DISPOSITION_RECOVERED)
262 unrecoverable = 1;
263
264 spin_lock_irqsave(&opal_hmi_evt_lock, flags);
265 }
266 spin_unlock_irqrestore(&opal_hmi_evt_lock, flags);
267
268 if (unrecoverable) {
269 int ret;
270
271 /* Pull all HMI events from OPAL before we panic. */
272 while (opal_get_msg(__pa(&msg), sizeof(msg)) == OPAL_SUCCESS) {
273 u32 type;
274
275 type = be32_to_cpu(msg.msg_type);
276
277 /* skip if not HMI event */
278 if (type != OPAL_MSG_HMI_EVT)
279 continue;
280
281 /* HMI event info starts from param[0] */
282 hmi_evt = (struct OpalHMIEvent *)&msg.params[0];
283 print_hmi_event_info(hmi_evt);
284 }
285
286 /*
287 * Unrecoverable HMI exception. We need to inform BMC/OCC
288 * about this error so that it can collect relevant data
289 * for error analysis before rebooting.
290 */
291 ret = opal_cec_reboot2(OPAL_REBOOT_PLATFORM_ERROR,
292 "Unrecoverable HMI exception");
293 if (ret == OPAL_UNSUPPORTED) {
294 pr_emerg("Reboot type %d not supported\n",
295 OPAL_REBOOT_PLATFORM_ERROR);
296 }
297
298 /*
299 * Fall through and panic if opal_cec_reboot2() returns
300 * OPAL_UNSUPPORTED.
301 */
302 panic("Unrecoverable HMI exception");
303 }
304 }
305
306 static DECLARE_WORK(hmi_event_work, hmi_event_handler);
307 /*
308 * opal_handle_hmi_event - notifier handler that queues up HMI events
309 * to be preocessed later.
310 */
311 static int opal_handle_hmi_event(struct notifier_block *nb,
312 unsigned long msg_type, void *msg)
313 {
314 unsigned long flags;
315 struct OpalHMIEvent *hmi_evt;
316 struct opal_msg *hmi_msg = msg;
317 struct OpalHmiEvtNode *msg_node;
318
319 /* Sanity Checks */
320 if (msg_type != OPAL_MSG_HMI_EVT)
321 return 0;
322
323 /* HMI event info starts from param[0] */
324 hmi_evt = (struct OpalHMIEvent *)&hmi_msg->params[0];
325
326 /* Delay the logging of HMI events to workqueue. */
327 msg_node = kzalloc(sizeof(*msg_node), GFP_ATOMIC);
328 if (!msg_node) {
329 pr_err("HMI: out of memory, Opal message event not handled\n");
330 return -ENOMEM;
331 }
332 memcpy(&msg_node->hmi_evt, hmi_evt, sizeof(struct OpalHMIEvent));
333
334 spin_lock_irqsave(&opal_hmi_evt_lock, flags);
335 list_add(&msg_node->list, &opal_hmi_evt_list);
336 spin_unlock_irqrestore(&opal_hmi_evt_lock, flags);
337
338 schedule_work(&hmi_event_work);
339 return 0;
340 }
341
342 static struct notifier_block opal_hmi_handler_nb = {
343 .notifier_call = opal_handle_hmi_event,
344 .next = NULL,
345 .priority = 0,
346 };
347
348 int __init opal_hmi_handler_init(void)
349 {
350 int ret;
351
352 if (!opal_hmi_handler_nb_init) {
353 ret = opal_message_notifier_register(
354 OPAL_MSG_HMI_EVT, &opal_hmi_handler_nb);
355 if (ret) {
356 pr_err("%s: Can't register OPAL event notifier (%d)\n",
357 __func__, ret);
358 return ret;
359 }
360 opal_hmi_handler_nb_init = 1;
361 }
362 return 0;
363 }
This page took 0.03876 seconds and 6 git commands to generate.