aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Kurz <gkurz@linux.vnet.ibm.com>2014-04-04 03:35:13 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-04-06 20:33:12 -0400
commita08a53ea4c97940fe83fea3eab27618ac0fb5ed1 (patch)
treea6d61d06dbf2929477e066d14eceff2ea7a5ac31
parentc59c015b6aa5bc18d01c8e482149086cafd7332a (diff)
powerpc/le: Enable RTAS events support
The current kernel code assumes big endian and parses RTAS events all wrong. The most visible effect is that we cannot honor EPOW events, meaning, for example, we cannot shut down a guest properly from the hypervisor. This new patch is largely inspired by Nathan's work: we get rid of all the bit fields in the RTAS event structures (even the unused ones, for consistency). We also introduce endian safe accessors for the fields used by the kernel (trivial rtas_error_type() accessor added for consistency). Cc: Nathan Fontenot <nfont@linux.vnet.ibm.com> Signed-off-by: Greg Kurz <gkurz@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r--arch/powerpc/include/asm/rtas.h127
-rw-r--r--arch/powerpc/kernel/rtas.c15
-rw-r--r--arch/powerpc/kernel/rtasd.c24
-rw-r--r--arch/powerpc/platforms/pseries/io_event_irq.c6
-rw-r--r--arch/powerpc/platforms/pseries/ras.c17
5 files changed, 128 insertions, 61 deletions
diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h
index a0e1add01ef5..b390f55b0df1 100644
--- a/arch/powerpc/include/asm/rtas.h
+++ b/arch/powerpc/include/asm/rtas.h
@@ -150,19 +150,53 @@ struct rtas_suspend_me_data {
150#define RTAS_VECTOR_EXTERNAL_INTERRUPT 0x500 150#define RTAS_VECTOR_EXTERNAL_INTERRUPT 0x500
151 151
152struct rtas_error_log { 152struct rtas_error_log {
153 unsigned long version:8; /* Architectural version */ 153 /* Byte 0 */
154 unsigned long severity:3; /* Severity level of error */ 154 uint8_t byte0; /* Architectural version */
155 unsigned long disposition:2; /* Degree of recovery */ 155
156 unsigned long extended:1; /* extended log present? */ 156 /* Byte 1 */
157 unsigned long /* reserved */ :2; /* Reserved for future use */ 157 uint8_t byte1;
158 unsigned long initiator:4; /* Initiator of event */ 158 /* XXXXXXXX
159 unsigned long target:4; /* Target of failed operation */ 159 * XXX 3: Severity level of error
160 unsigned long type:8; /* General event or error*/ 160 * XX 2: Degree of recovery
161 unsigned long extended_log_length:32; /* length in bytes */ 161 * X 1: Extended log present?
162 unsigned char buffer[1]; /* Start of extended log */ 162 * XX 2: Reserved
163 */
164
165 /* Byte 2 */
166 uint8_t byte2;
167 /* XXXXXXXX
168 * XXXX 4: Initiator of event
169 * XXXX 4: Target of failed operation
170 */
171 uint8_t byte3; /* General event or error*/
172 __be32 extended_log_length; /* length in bytes */
173 unsigned char buffer[1]; /* Start of extended log */
163 /* Variable length. */ 174 /* Variable length. */
164}; 175};
165 176
177static inline uint8_t rtas_error_severity(const struct rtas_error_log *elog)
178{
179 return (elog->byte1 & 0xE0) >> 5;
180}
181
182static inline uint8_t rtas_error_disposition(const struct rtas_error_log *elog)
183{
184 return (elog->byte1 & 0x18) >> 3;
185}
186
187static inline uint8_t rtas_error_extended(const struct rtas_error_log *elog)
188{
189 return (elog->byte1 & 0x04) >> 2;
190}
191
192#define rtas_error_type(x) ((x)->byte3)
193
194static inline
195uint32_t rtas_error_extended_log_length(const struct rtas_error_log *elog)
196{
197 return be32_to_cpu(elog->extended_log_length);
198}
199
166#define RTAS_V6EXT_LOG_FORMAT_EVENT_LOG 14 200#define RTAS_V6EXT_LOG_FORMAT_EVENT_LOG 14
167 201
168#define RTAS_V6EXT_COMPANY_ID_IBM (('I' << 24) | ('B' << 16) | ('M' << 8)) 202#define RTAS_V6EXT_COMPANY_ID_IBM (('I' << 24) | ('B' << 16) | ('M' << 8))
@@ -172,32 +206,35 @@ struct rtas_error_log {
172 */ 206 */
173struct rtas_ext_event_log_v6 { 207struct rtas_ext_event_log_v6 {
174 /* Byte 0 */ 208 /* Byte 0 */
175 uint32_t log_valid:1; /* 1:Log valid */ 209 uint8_t byte0;
176 uint32_t unrecoverable_error:1; /* 1:Unrecoverable error */ 210 /* XXXXXXXX
177 uint32_t recoverable_error:1; /* 1:recoverable (correctable */ 211 * X 1: Log valid
178 /* or successfully retried) */ 212 * X 1: Unrecoverable error
179 uint32_t degraded_operation:1; /* 1:Unrecoverable err, bypassed*/ 213 * X 1: Recoverable (correctable or successfully retried)
180 /* - degraded operation (e.g. */ 214 * X 1: Bypassed unrecoverable error (degraded operation)
181 /* CPU or mem taken off-line) */ 215 * X 1: Predictive error
182 uint32_t predictive_error:1; 216 * X 1: "New" log (always 1 for data returned from RTAS)
183 uint32_t new_log:1; /* 1:"New" log (Always 1 for */ 217 * X 1: Big Endian
184 /* data returned from RTAS */ 218 * X 1: Reserved
185 uint32_t big_endian:1; /* 1: Big endian */ 219 */
186 uint32_t :1; /* reserved */ 220
187 /* Byte 1 */ 221 /* Byte 1 */
188 uint32_t :8; /* reserved */ 222 uint8_t byte1; /* reserved */
223
189 /* Byte 2 */ 224 /* Byte 2 */
190 uint32_t powerpc_format:1; /* Set to 1 (indicating log is */ 225 uint8_t byte2;
191 /* in PowerPC format */ 226 /* XXXXXXXX
192 uint32_t :3; /* reserved */ 227 * X 1: Set to 1 (indicating log is in PowerPC format)
193 uint32_t log_format:4; /* Log format indicator. Define */ 228 * XXX 3: Reserved
194 /* format used for byte 12-2047 */ 229 * XXXX 4: Log format used for bytes 12-2047
230 */
231
195 /* Byte 3 */ 232 /* Byte 3 */
196 uint32_t :8; /* reserved */ 233 uint8_t byte3; /* reserved */
197 /* Byte 4-11 */ 234 /* Byte 4-11 */
198 uint8_t reserved[8]; /* reserved */ 235 uint8_t reserved[8]; /* reserved */
199 /* Byte 12-15 */ 236 /* Byte 12-15 */
200 uint32_t company_id; /* Company ID of the company */ 237 __be32 company_id; /* Company ID of the company */
201 /* that defines the format for */ 238 /* that defines the format for */
202 /* the vendor specific log type */ 239 /* the vendor specific log type */
203 /* Byte 16-end of log */ 240 /* Byte 16-end of log */
@@ -205,6 +242,18 @@ struct rtas_ext_event_log_v6 {
205 /* Variable length. */ 242 /* Variable length. */
206}; 243};
207 244
245static
246inline uint8_t rtas_ext_event_log_format(struct rtas_ext_event_log_v6 *ext_log)
247{
248 return ext_log->byte2 & 0x0F;
249}
250
251static
252inline uint32_t rtas_ext_event_company_id(struct rtas_ext_event_log_v6 *ext_log)
253{
254 return be32_to_cpu(ext_log->company_id);
255}
256
208/* pSeries event log format */ 257/* pSeries event log format */
209 258
210/* Two bytes ASCII section IDs */ 259/* Two bytes ASCII section IDs */
@@ -227,14 +276,26 @@ struct rtas_ext_event_log_v6 {
227 276
228/* Vendor specific Platform Event Log Format, Version 6, section header */ 277/* Vendor specific Platform Event Log Format, Version 6, section header */
229struct pseries_errorlog { 278struct pseries_errorlog {
230 uint16_t id; /* 0x00 2-byte ASCII section ID */ 279 __be16 id; /* 0x00 2-byte ASCII section ID */
231 uint16_t length; /* 0x02 Section length in bytes */ 280 __be16 length; /* 0x02 Section length in bytes */
232 uint8_t version; /* 0x04 Section version */ 281 uint8_t version; /* 0x04 Section version */
233 uint8_t subtype; /* 0x05 Section subtype */ 282 uint8_t subtype; /* 0x05 Section subtype */
234 uint16_t creator_component; /* 0x06 Creator component ID */ 283 __be16 creator_component; /* 0x06 Creator component ID */
235 uint8_t data[]; /* 0x08 Start of section data */ 284 uint8_t data[]; /* 0x08 Start of section data */
236}; 285};
237 286
287static
288inline uint16_t pseries_errorlog_id(struct pseries_errorlog *sect)
289{
290 return be16_to_cpu(sect->id);
291}
292
293static
294inline uint16_t pseries_errorlog_length(struct pseries_errorlog *sect)
295{
296 return be16_to_cpu(sect->length);
297}
298
238struct pseries_errorlog *get_pseries_errorlog(struct rtas_error_log *log, 299struct pseries_errorlog *get_pseries_errorlog(struct rtas_error_log *log,
239 uint16_t section_id); 300 uint16_t section_id);
240 301
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
index f386296ff378..8cd5ed049b5d 100644
--- a/arch/powerpc/kernel/rtas.c
+++ b/arch/powerpc/kernel/rtas.c
@@ -993,21 +993,24 @@ struct pseries_errorlog *get_pseries_errorlog(struct rtas_error_log *log,
993 (struct rtas_ext_event_log_v6 *)log->buffer; 993 (struct rtas_ext_event_log_v6 *)log->buffer;
994 struct pseries_errorlog *sect; 994 struct pseries_errorlog *sect;
995 unsigned char *p, *log_end; 995 unsigned char *p, *log_end;
996 uint32_t ext_log_length = rtas_error_extended_log_length(log);
997 uint8_t log_format = rtas_ext_event_log_format(ext_log);
998 uint32_t company_id = rtas_ext_event_company_id(ext_log);
996 999
997 /* Check that we understand the format */ 1000 /* Check that we understand the format */
998 if (log->extended_log_length < sizeof(struct rtas_ext_event_log_v6) || 1001 if (ext_log_length < sizeof(struct rtas_ext_event_log_v6) ||
999 ext_log->log_format != RTAS_V6EXT_LOG_FORMAT_EVENT_LOG || 1002 log_format != RTAS_V6EXT_LOG_FORMAT_EVENT_LOG ||
1000 ext_log->company_id != RTAS_V6EXT_COMPANY_ID_IBM) 1003 company_id != RTAS_V6EXT_COMPANY_ID_IBM)
1001 return NULL; 1004 return NULL;
1002 1005
1003 log_end = log->buffer + log->extended_log_length; 1006 log_end = log->buffer + ext_log_length;
1004 p = ext_log->vendor_log; 1007 p = ext_log->vendor_log;
1005 1008
1006 while (p < log_end) { 1009 while (p < log_end) {
1007 sect = (struct pseries_errorlog *)p; 1010 sect = (struct pseries_errorlog *)p;
1008 if (sect->id == section_id) 1011 if (pseries_errorlog_id(sect) == section_id)
1009 return sect; 1012 return sect;
1010 p += sect->length; 1013 p += pseries_errorlog_length(sect);
1011 } 1014 }
1012 1015
1013 return NULL; 1016 return NULL;
diff --git a/arch/powerpc/kernel/rtasd.c b/arch/powerpc/kernel/rtasd.c
index 1130c53ad652..e736387fee6a 100644
--- a/arch/powerpc/kernel/rtasd.c
+++ b/arch/powerpc/kernel/rtasd.c
@@ -150,8 +150,8 @@ static void printk_log_rtas(char *buf, int len)
150 struct rtas_error_log *errlog = (struct rtas_error_log *)buf; 150 struct rtas_error_log *errlog = (struct rtas_error_log *)buf;
151 151
152 printk(RTAS_DEBUG "event: %d, Type: %s, Severity: %d\n", 152 printk(RTAS_DEBUG "event: %d, Type: %s, Severity: %d\n",
153 error_log_cnt, rtas_event_type(errlog->type), 153 error_log_cnt, rtas_event_type(rtas_error_type(errlog)),
154 errlog->severity); 154 rtas_error_severity(errlog));
155 } 155 }
156} 156}
157 157
@@ -159,14 +159,16 @@ static int log_rtas_len(char * buf)
159{ 159{
160 int len; 160 int len;
161 struct rtas_error_log *err; 161 struct rtas_error_log *err;
162 uint32_t extended_log_length;
162 163
163 /* rtas fixed header */ 164 /* rtas fixed header */
164 len = 8; 165 len = 8;
165 err = (struct rtas_error_log *)buf; 166 err = (struct rtas_error_log *)buf;
166 if (err->extended && err->extended_log_length) { 167 extended_log_length = rtas_error_extended_log_length(err);
168 if (rtas_error_extended(err) && extended_log_length) {
167 169
168 /* extended header */ 170 /* extended header */
169 len += err->extended_log_length; 171 len += extended_log_length;
170 } 172 }
171 173
172 if (rtas_error_log_max == 0) 174 if (rtas_error_log_max == 0)
@@ -293,15 +295,13 @@ void prrn_schedule_update(u32 scope)
293 295
294static void handle_rtas_event(const struct rtas_error_log *log) 296static void handle_rtas_event(const struct rtas_error_log *log)
295{ 297{
296 if (log->type == RTAS_TYPE_PRRN) { 298 if (rtas_error_type(log) != RTAS_TYPE_PRRN || !prrn_is_enabled())
297 /* For PRRN Events the extended log length is used to denote 299 return;
298 * the scope for calling rtas update-nodes.
299 */
300 if (prrn_is_enabled())
301 prrn_schedule_update(log->extended_log_length);
302 }
303 300
304 return; 301 /* For PRRN Events the extended log length is used to denote
302 * the scope for calling rtas update-nodes.
303 */
304 prrn_schedule_update(rtas_error_extended_log_length(log));
305} 305}
306 306
307#else 307#else
diff --git a/arch/powerpc/platforms/pseries/io_event_irq.c b/arch/powerpc/platforms/pseries/io_event_irq.c
index 5ea88d1541f7..0240c4ff878a 100644
--- a/arch/powerpc/platforms/pseries/io_event_irq.c
+++ b/arch/powerpc/platforms/pseries/io_event_irq.c
@@ -82,9 +82,9 @@ static struct pseries_io_event * ioei_find_event(struct rtas_error_log *elog)
82 * RTAS_TYPE_IO only exists in extended event log version 6 or later. 82 * RTAS_TYPE_IO only exists in extended event log version 6 or later.
83 * No need to check event log version. 83 * No need to check event log version.
84 */ 84 */
85 if (unlikely(elog->type != RTAS_TYPE_IO)) { 85 if (unlikely(rtas_error_type(elog) != RTAS_TYPE_IO)) {
86 printk_once(KERN_WARNING "io_event_irq: Unexpected event type %d", 86 printk_once(KERN_WARNING"io_event_irq: Unexpected event type %d",
87 elog->type); 87 rtas_error_type(elog));
88 return NULL; 88 return NULL;
89 } 89 }
90 90
diff --git a/arch/powerpc/platforms/pseries/ras.c b/arch/powerpc/platforms/pseries/ras.c
index 721c0586b284..9c5778e6ed4b 100644
--- a/arch/powerpc/platforms/pseries/ras.c
+++ b/arch/powerpc/platforms/pseries/ras.c
@@ -236,7 +236,8 @@ static irqreturn_t ras_error_interrupt(int irq, void *dev_id)
236 236
237 rtas_elog = (struct rtas_error_log *)ras_log_buf; 237 rtas_elog = (struct rtas_error_log *)ras_log_buf;
238 238
239 if ((status == 0) && (rtas_elog->severity >= RTAS_SEVERITY_ERROR_SYNC)) 239 if (status == 0 &&
240 rtas_error_severity(rtas_elog) >= RTAS_SEVERITY_ERROR_SYNC)
240 fatal = 1; 241 fatal = 1;
241 else 242 else
242 fatal = 0; 243 fatal = 0;
@@ -300,13 +301,14 @@ static struct rtas_error_log *fwnmi_get_errinfo(struct pt_regs *regs)
300 301
301 /* If it isn't an extended log we can use the per cpu 64bit buffer */ 302 /* If it isn't an extended log we can use the per cpu 64bit buffer */
302 h = (struct rtas_error_log *)&savep[1]; 303 h = (struct rtas_error_log *)&savep[1];
303 if (!h->extended) { 304 if (!rtas_error_extended(h)) {
304 memcpy(&__get_cpu_var(mce_data_buf), h, sizeof(__u64)); 305 memcpy(&__get_cpu_var(mce_data_buf), h, sizeof(__u64));
305 errhdr = (struct rtas_error_log *)&__get_cpu_var(mce_data_buf); 306 errhdr = (struct rtas_error_log *)&__get_cpu_var(mce_data_buf);
306 } else { 307 } else {
307 int len; 308 int len, error_log_length;
308 309
309 len = max_t(int, 8+h->extended_log_length, RTAS_ERROR_LOG_MAX); 310 error_log_length = 8 + rtas_error_extended_log_length(h);
311 len = max_t(int, error_log_length, RTAS_ERROR_LOG_MAX);
310 memset(global_mce_data_buf, 0, RTAS_ERROR_LOG_MAX); 312 memset(global_mce_data_buf, 0, RTAS_ERROR_LOG_MAX);
311 memcpy(global_mce_data_buf, h, len); 313 memcpy(global_mce_data_buf, h, len);
312 errhdr = (struct rtas_error_log *)global_mce_data_buf; 314 errhdr = (struct rtas_error_log *)global_mce_data_buf;
@@ -350,23 +352,24 @@ int pSeries_system_reset_exception(struct pt_regs *regs)
350static int recover_mce(struct pt_regs *regs, struct rtas_error_log *err) 352static int recover_mce(struct pt_regs *regs, struct rtas_error_log *err)
351{ 353{
352 int recovered = 0; 354 int recovered = 0;
355 int disposition = rtas_error_disposition(err);
353 356
354 if (!(regs->msr & MSR_RI)) { 357 if (!(regs->msr & MSR_RI)) {
355 /* If MSR_RI isn't set, we cannot recover */ 358 /* If MSR_RI isn't set, we cannot recover */
356 recovered = 0; 359 recovered = 0;
357 360
358 } else if (err->disposition == RTAS_DISP_FULLY_RECOVERED) { 361 } else if (disposition == RTAS_DISP_FULLY_RECOVERED) {
359 /* Platform corrected itself */ 362 /* Platform corrected itself */
360 recovered = 1; 363 recovered = 1;
361 364
362 } else if (err->disposition == RTAS_DISP_LIMITED_RECOVERY) { 365 } else if (disposition == RTAS_DISP_LIMITED_RECOVERY) {
363 /* Platform corrected itself but could be degraded */ 366 /* Platform corrected itself but could be degraded */
364 printk(KERN_ERR "MCE: limited recovery, system may " 367 printk(KERN_ERR "MCE: limited recovery, system may "
365 "be degraded\n"); 368 "be degraded\n");
366 recovered = 1; 369 recovered = 1;
367 370
368 } else if (user_mode(regs) && !is_global_init(current) && 371 } else if (user_mode(regs) && !is_global_init(current) &&
369 err->severity == RTAS_SEVERITY_ERROR_SYNC) { 372 rtas_error_severity(err) == RTAS_SEVERITY_ERROR_SYNC) {
370 373
371 /* 374 /*
372 * If we received a synchronous error when in userspace 375 * If we received a synchronous error when in userspace