aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc
diff options
context:
space:
mode:
authorMahesh Salgaonkar <mahesh@linux.vnet.ibm.com>2014-07-29 09:10:07 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-08-05 02:33:52 -0400
commit0ef95b411e73d8789100d017c02c1329c5055802 (patch)
tree2438351ab90d8423ffe63bf49e1036a7d83a7950 /arch/powerpc
parent0869b6fd209bda402576a9a559120ddd4f61198e (diff)
powerpc/powernv: Invoke opal call to handle hmi.
When we hit the HMI in Linux, invoke opal call to handle/recover from HMI errors in real mode and then in virtual mode during check_irq_replay() invoke opal_poll_events()/opal_do_notifier() to retrieve HMI event from OPAL and act accordingly. Now that we are ready to handle HMI interrupt directly in linux, remove the HMI interrupt registration with firmware. Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc')
-rw-r--r--arch/powerpc/include/asm/opal.h47
-rw-r--r--arch/powerpc/include/asm/paca.h1
-rw-r--r--arch/powerpc/platforms/powernv/Makefile2
-rw-r--r--arch/powerpc/platforms/powernv/opal-hmi.c188
-rw-r--r--arch/powerpc/platforms/powernv/opal-wrappers.S1
-rw-r--r--arch/powerpc/platforms/powernv/opal.c35
6 files changed, 267 insertions, 7 deletions
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index efc16c37b959..b2f8ce1fd0d7 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -148,6 +148,7 @@ struct opal_sg_list {
148#define OPAL_DUMP_RESEND 91 148#define OPAL_DUMP_RESEND 91
149#define OPAL_DUMP_INFO2 94 149#define OPAL_DUMP_INFO2 94
150#define OPAL_PCI_EEH_FREEZE_SET 97 150#define OPAL_PCI_EEH_FREEZE_SET 97
151#define OPAL_HANDLE_HMI 98
151 152
152#ifndef __ASSEMBLY__ 153#ifndef __ASSEMBLY__
153 154
@@ -245,6 +246,7 @@ enum OpalMessageType {
245 OPAL_MSG_MEM_ERR, 246 OPAL_MSG_MEM_ERR,
246 OPAL_MSG_EPOW, 247 OPAL_MSG_EPOW,
247 OPAL_MSG_SHUTDOWN, 248 OPAL_MSG_SHUTDOWN,
249 OPAL_MSG_HMI_EVT,
248 OPAL_MSG_TYPE_MAX, 250 OPAL_MSG_TYPE_MAX,
249}; 251};
250 252
@@ -513,6 +515,50 @@ struct OpalMemoryErrorData {
513 } u; 515 } u;
514}; 516};
515 517
518/* HMI interrupt event */
519enum OpalHMI_Version {
520 OpalHMIEvt_V1 = 1,
521};
522
523enum OpalHMI_Severity {
524 OpalHMI_SEV_NO_ERROR = 0,
525 OpalHMI_SEV_WARNING = 1,
526 OpalHMI_SEV_ERROR_SYNC = 2,
527 OpalHMI_SEV_FATAL = 3,
528};
529
530enum OpalHMI_Disposition {
531 OpalHMI_DISPOSITION_RECOVERED = 0,
532 OpalHMI_DISPOSITION_NOT_RECOVERED = 1,
533};
534
535enum OpalHMI_ErrType {
536 OpalHMI_ERROR_MALFUNC_ALERT = 0,
537 OpalHMI_ERROR_PROC_RECOV_DONE,
538 OpalHMI_ERROR_PROC_RECOV_DONE_AGAIN,
539 OpalHMI_ERROR_PROC_RECOV_MASKED,
540 OpalHMI_ERROR_TFAC,
541 OpalHMI_ERROR_TFMR_PARITY,
542 OpalHMI_ERROR_HA_OVERFLOW_WARN,
543 OpalHMI_ERROR_XSCOM_FAIL,
544 OpalHMI_ERROR_XSCOM_DONE,
545 OpalHMI_ERROR_SCOM_FIR,
546 OpalHMI_ERROR_DEBUG_TRIG_FIR,
547 OpalHMI_ERROR_HYP_RESOURCE,
548};
549
550struct OpalHMIEvent {
551 uint8_t version; /* 0x00 */
552 uint8_t severity; /* 0x01 */
553 uint8_t type; /* 0x02 */
554 uint8_t disposition; /* 0x03 */
555 uint8_t reserved_1[4]; /* 0x04 */
556
557 __be64 hmer;
558 /* TFMR register. Valid only for TFAC and TFMR_PARITY error type. */
559 __be64 tfmr;
560};
561
516enum { 562enum {
517 OPAL_P7IOC_DIAG_TYPE_NONE = 0, 563 OPAL_P7IOC_DIAG_TYPE_NONE = 0,
518 OPAL_P7IOC_DIAG_TYPE_RGC = 1, 564 OPAL_P7IOC_DIAG_TYPE_RGC = 1,
@@ -873,6 +919,7 @@ int64_t opal_get_param(uint64_t token, uint32_t param_id, uint64_t buffer,
873int64_t opal_set_param(uint64_t token, uint32_t param_id, uint64_t buffer, 919int64_t opal_set_param(uint64_t token, uint32_t param_id, uint64_t buffer,
874 uint64_t length); 920 uint64_t length);
875int64_t opal_sensor_read(uint32_t sensor_hndl, int token, __be32 *sensor_data); 921int64_t opal_sensor_read(uint32_t sensor_hndl, int token, __be32 *sensor_data);
922int64_t opal_handle_hmi(void);
876 923
877/* Internal functions */ 924/* Internal functions */
878extern int early_init_dt_scan_opal(unsigned long node, const char *uname, 925extern int early_init_dt_scan_opal(unsigned long node, const char *uname,
diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h
index 5abde4e223bb..a5139ea6910b 100644
--- a/arch/powerpc/include/asm/paca.h
+++ b/arch/powerpc/include/asm/paca.h
@@ -167,6 +167,7 @@ struct paca_struct {
167 * and already using emergency stack. 167 * and already using emergency stack.
168 */ 168 */
169 u16 in_mce; 169 u16 in_mce;
170 u8 hmi_event_available; /* HMI event is available */
170#endif 171#endif
171 172
172 /* Stuff for accurate time accounting */ 173 /* Stuff for accurate time accounting */
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
index 70b758aaaffa..f241accc053d 100644
--- a/arch/powerpc/platforms/powernv/Makefile
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -1,7 +1,7 @@
1obj-y += setup.o opal-wrappers.o opal.o opal-async.o 1obj-y += setup.o opal-wrappers.o opal.o opal-async.o
2obj-y += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o 2obj-y += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o
3obj-y += rng.o opal-elog.o opal-dump.o opal-sysparam.o opal-sensor.o 3obj-y += rng.o opal-elog.o opal-dump.o opal-sysparam.o opal-sensor.o
4obj-y += opal-msglog.o 4obj-y += opal-msglog.o opal-hmi.o
5 5
6obj-$(CONFIG_SMP) += smp.o subcore.o subcore-asm.o 6obj-$(CONFIG_SMP) += smp.o subcore.o subcore-asm.o
7obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o 7obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o
diff --git a/arch/powerpc/platforms/powernv/opal-hmi.c b/arch/powerpc/platforms/powernv/opal-hmi.c
new file mode 100644
index 000000000000..97ac8dc33667
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal-hmi.c
@@ -0,0 +1,188 @@
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
32static int opal_hmi_handler_nb_init;
33struct OpalHmiEvtNode {
34 struct list_head list;
35 struct OpalHMIEvent hmi_evt;
36};
37static LIST_HEAD(opal_hmi_evt_list);
38static DEFINE_SPINLOCK(opal_hmi_evt_lock);
39
40static 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
99static 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
130static 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 */
135static 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
166static struct notifier_block opal_hmi_handler_nb = {
167 .notifier_call = opal_handle_hmi_event,
168 .next = NULL,
169 .priority = 0,
170};
171
172static 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}
188subsys_initcall(opal_hmi_handler_init);
diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S
index 3dda49957158..a328be44880f 100644
--- a/arch/powerpc/platforms/powernv/opal-wrappers.S
+++ b/arch/powerpc/platforms/powernv/opal-wrappers.S
@@ -244,3 +244,4 @@ OPAL_CALL(opal_sync_host_reboot, OPAL_SYNC_HOST_REBOOT);
244OPAL_CALL(opal_sensor_read, OPAL_SENSOR_READ); 244OPAL_CALL(opal_sensor_read, OPAL_SENSOR_READ);
245OPAL_CALL(opal_get_param, OPAL_GET_PARAM); 245OPAL_CALL(opal_get_param, OPAL_GET_PARAM);
246OPAL_CALL(opal_set_param, OPAL_SET_PARAM); 246OPAL_CALL(opal_set_param, OPAL_SET_PARAM);
247OPAL_CALL(opal_handle_hmi, OPAL_HANDLE_HMI);
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index d20d69921376..f0a01a46a57d 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -194,9 +194,6 @@ static int __init opal_register_exception_handlers(void)
194 * fwnmi area at 0x7000 to provide the glue space to OPAL 194 * fwnmi area at 0x7000 to provide the glue space to OPAL
195 */ 195 */
196 glue = 0x7000; 196 glue = 0x7000;
197 opal_register_exception_handler(OPAL_HYPERVISOR_MAINTENANCE_HANDLER,
198 0, glue);
199 glue += 128;
200 opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue); 197 opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue);
201#endif 198#endif
202 199
@@ -517,15 +514,41 @@ int opal_machine_check(struct pt_regs *regs)
517/* Early hmi handler called in real mode. */ 514/* Early hmi handler called in real mode. */
518int opal_hmi_exception_early(struct pt_regs *regs) 515int opal_hmi_exception_early(struct pt_regs *regs)
519{ 516{
520 /* TODO: Call opal hmi handler. */ 517 s64 rc;
518
519 /*
520 * call opal hmi handler. Pass paca address as token.
521 * The return value OPAL_SUCCESS is an indication that there is
522 * an HMI event generated waiting to pull by Linux.
523 */
524 rc = opal_handle_hmi();
525 if (rc == OPAL_SUCCESS) {
526 local_paca->hmi_event_available = 1;
527 return 1;
528 }
521 return 0; 529 return 0;
522} 530}
523 531
524/* HMI exception handler called in virtual mode during check_irq_replay. */ 532/* HMI exception handler called in virtual mode during check_irq_replay. */
525int opal_handle_hmi_exception(struct pt_regs *regs) 533int opal_handle_hmi_exception(struct pt_regs *regs)
526{ 534{
527 /* TODO: Retrive and print HMI event from OPAL. */ 535 s64 rc;
528 return 0; 536 __be64 evt = 0;
537
538 /*
539 * Check if HMI event is available.
540 * if Yes, then call opal_poll_events to pull opal messages and
541 * process them.
542 */
543 if (!local_paca->hmi_event_available)
544 return 0;
545
546 local_paca->hmi_event_available = 0;
547 rc = opal_poll_events(&evt);
548 if (rc == OPAL_SUCCESS && evt)
549 opal_do_notifier(be64_to_cpu(evt));
550
551 return 1;
529} 552}
530 553
531static uint64_t find_recovery_address(uint64_t nip) 554static uint64_t find_recovery_address(uint64_t nip)