2 * OPAL hypervisor Maintenance interrupt handling support in PowreNV.
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.
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.
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/>.
17 * Copyright 2014 IBM Corporation
18 * Author: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
23 #include <linux/kernel.h>
24 #include <linux/init.h>
27 #include <linux/slab.h>
30 #include <asm/cputable.h>
32 static int opal_hmi_handler_nb_init;
33 struct OpalHmiEvtNode {
34 struct list_head list;
35 struct OpalHMIEvent hmi_evt;
37 static LIST_HEAD(opal_hmi_evt_list);
38 static DEFINE_SPINLOCK(opal_hmi_evt_lock);
40 static void print_hmi_event_info(struct OpalHMIEvent *hmi_evt)
42 const char *level, *sevstr, *error_info;
43 static const char *hmi_error_types[] = {
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"
58 /* Print things out */
59 if (hmi_evt->version != OpalHMIEvt_V1) {
60 pr_err("HMI Interrupt, Unknown event version %d !\n",
64 switch (hmi_evt->severity) {
65 case OpalHMI_SEV_NO_ERROR:
69 case OpalHMI_SEV_WARNING:
73 case OpalHMI_SEV_ERROR_SYNC:
77 case OpalHMI_SEV_FATAL:
84 printk("%s%s Hypervisor Maintenance interrupt [%s]\n",
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]
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));
99 static void hmi_event_handler(struct work_struct *work)
102 struct OpalHMIEvent *hmi_evt;
103 struct OpalHmiEvtNode *msg_node;
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);
113 hmi_evt = (struct OpalHMIEvent *) &msg_node->hmi_evt;
114 print_hmi_event_info(hmi_evt);
115 disposition = hmi_evt->disposition;
119 * Check if HMI event has been recovered or not. If not
120 * then we can't continue, invoke panic.
122 if (disposition != OpalHMI_DISPOSITION_RECOVERED)
123 panic("Unrecoverable HMI exception");
125 spin_lock_irqsave(&opal_hmi_evt_lock, flags);
127 spin_unlock_irqrestore(&opal_hmi_evt_lock, flags);
130 static DECLARE_WORK(hmi_event_work, hmi_event_handler);
132 * opal_handle_hmi_event - notifier handler that queues up HMI events
133 * to be preocessed later.
135 static int opal_handle_hmi_event(struct notifier_block *nb,
136 unsigned long msg_type, void *msg)
139 struct OpalHMIEvent *hmi_evt;
140 struct opal_msg *hmi_msg = msg;
141 struct OpalHmiEvtNode *msg_node;
144 if (msg_type != OPAL_MSG_HMI_EVT)
147 /* HMI event info starts from param[0] */
148 hmi_evt = (struct OpalHMIEvent *)&hmi_msg->params[0];
150 /* Delay the logging of HMI events to workqueue. */
151 msg_node = kzalloc(sizeof(*msg_node), GFP_ATOMIC);
153 pr_err("HMI: out of memory, Opal message event not handled\n");
156 memcpy(&msg_node->hmi_evt, hmi_evt, sizeof(struct OpalHMIEvent));
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);
162 schedule_work(&hmi_event_work);
166 static struct notifier_block opal_hmi_handler_nb = {
167 .notifier_call = opal_handle_hmi_event,
172 static int __init opal_hmi_handler_init(void)
176 if (!opal_hmi_handler_nb_init) {
177 ret = opal_message_notifier_register(
178 OPAL_MSG_HMI_EVT, &opal_hmi_handler_nb);
180 pr_err("%s: Can't register OPAL event notifier (%d)\n",
184 opal_hmi_handler_nb_init = 1;
188 subsys_initcall(opal_hmi_handler_init);