diff options
author | Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com> | 2014-07-29 09:10:07 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2014-08-05 02:33:52 -0400 |
commit | 0ef95b411e73d8789100d017c02c1329c5055802 (patch) | |
tree | 2438351ab90d8423ffe63bf49e1036a7d83a7950 /arch/powerpc | |
parent | 0869b6fd209bda402576a9a559120ddd4f61198e (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.h | 47 | ||||
-rw-r--r-- | arch/powerpc/include/asm/paca.h | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/Makefile | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal-hmi.c | 188 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal-wrappers.S | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal.c | 35 |
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 */ | ||
519 | enum OpalHMI_Version { | ||
520 | OpalHMIEvt_V1 = 1, | ||
521 | }; | ||
522 | |||
523 | enum 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 | |||
530 | enum OpalHMI_Disposition { | ||
531 | OpalHMI_DISPOSITION_RECOVERED = 0, | ||
532 | OpalHMI_DISPOSITION_NOT_RECOVERED = 1, | ||
533 | }; | ||
534 | |||
535 | enum 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 | |||
550 | struct 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 | |||
516 | enum { | 562 | enum { |
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, | |||
873 | int64_t opal_set_param(uint64_t token, uint32_t param_id, uint64_t buffer, | 919 | int64_t opal_set_param(uint64_t token, uint32_t param_id, uint64_t buffer, |
874 | uint64_t length); | 920 | uint64_t length); |
875 | int64_t opal_sensor_read(uint32_t sensor_hndl, int token, __be32 *sensor_data); | 921 | int64_t opal_sensor_read(uint32_t sensor_hndl, int token, __be32 *sensor_data); |
922 | int64_t opal_handle_hmi(void); | ||
876 | 923 | ||
877 | /* Internal functions */ | 924 | /* Internal functions */ |
878 | extern int early_init_dt_scan_opal(unsigned long node, const char *uname, | 925 | extern 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 @@ | |||
1 | obj-y += setup.o opal-wrappers.o opal.o opal-async.o | 1 | obj-y += setup.o opal-wrappers.o opal.o opal-async.o |
2 | obj-y += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o | 2 | obj-y += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o |
3 | obj-y += rng.o opal-elog.o opal-dump.o opal-sysparam.o opal-sensor.o | 3 | obj-y += rng.o opal-elog.o opal-dump.o opal-sysparam.o opal-sensor.o |
4 | obj-y += opal-msglog.o | 4 | obj-y += opal-msglog.o opal-hmi.o |
5 | 5 | ||
6 | obj-$(CONFIG_SMP) += smp.o subcore.o subcore-asm.o | 6 | obj-$(CONFIG_SMP) += smp.o subcore.o subcore-asm.o |
7 | obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o | 7 | obj-$(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 | |||
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); | ||
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); | |||
244 | OPAL_CALL(opal_sensor_read, OPAL_SENSOR_READ); | 244 | OPAL_CALL(opal_sensor_read, OPAL_SENSOR_READ); |
245 | OPAL_CALL(opal_get_param, OPAL_GET_PARAM); | 245 | OPAL_CALL(opal_get_param, OPAL_GET_PARAM); |
246 | OPAL_CALL(opal_set_param, OPAL_SET_PARAM); | 246 | OPAL_CALL(opal_set_param, OPAL_SET_PARAM); |
247 | OPAL_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. */ |
518 | int opal_hmi_exception_early(struct pt_regs *regs) | 515 | int 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. */ |
525 | int opal_handle_hmi_exception(struct pt_regs *regs) | 533 | int 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 | ||
531 | static uint64_t find_recovery_address(uint64_t nip) | 554 | static uint64_t find_recovery_address(uint64_t nip) |