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