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