blob: 8ff8c32fef5846e20ec6a62cc3c662df01424ba8 [file] [log] [blame]
Huang Yinga08f82d2010-05-18 14:35:21 +08001/*
2 * APEI Error Record Serialization Table support
3 *
4 * ERST is a way provided by APEI to save and retrieve hardware error
Lucas De Marchi58f87ed2010-09-07 12:49:45 -04005 * information to and from a persistent store.
Huang Yinga08f82d2010-05-18 14:35:21 +08006 *
7 * For more information about ERST, please refer to ACPI Specification
8 * version 4.0, section 17.4.
9 *
10 * Copyright 2010 Intel Corp.
11 * Author: Huang Ying <ying.huang@intel.com>
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License version
15 * 2 as published by the Free Software Foundation.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27#include <linux/kernel.h>
28#include <linux/module.h>
29#include <linux/init.h>
30#include <linux/delay.h>
31#include <linux/io.h>
32#include <linux/acpi.h>
33#include <linux/uaccess.h>
34#include <linux/cper.h>
35#include <linux/nmi.h>
Thomas Gleixner0a7992c2010-08-11 14:17:29 -070036#include <linux/hardirq.h>
Huang Yinga08f82d2010-05-18 14:35:21 +080037#include <acpi/apei.h>
38
39#include "apei-internal.h"
40
41#define ERST_PFX "ERST: "
42
43/* ERST command status */
44#define ERST_STATUS_SUCCESS 0x0
45#define ERST_STATUS_NOT_ENOUGH_SPACE 0x1
46#define ERST_STATUS_HARDWARE_NOT_AVAILABLE 0x2
47#define ERST_STATUS_FAILED 0x3
48#define ERST_STATUS_RECORD_STORE_EMPTY 0x4
49#define ERST_STATUS_RECORD_NOT_FOUND 0x5
50
51#define ERST_TAB_ENTRY(tab) \
52 ((struct acpi_whea_header *)((char *)(tab) + \
53 sizeof(struct acpi_table_erst)))
54
55#define SPIN_UNIT 100 /* 100ns */
Stefan Weile8a8b252011-01-02 15:12:42 +010056/* Firmware should respond within 1 milliseconds */
Huang Yinga08f82d2010-05-18 14:35:21 +080057#define FIRMWARE_TIMEOUT (1 * NSEC_PER_MSEC)
58#define FIRMWARE_MAX_STALL 50 /* 50us */
59
60int erst_disable;
61EXPORT_SYMBOL_GPL(erst_disable);
62
63static struct acpi_table_erst *erst_tab;
64
65/* ERST Error Log Address Range atrributes */
66#define ERST_RANGE_RESERVED 0x0001
67#define ERST_RANGE_NVRAM 0x0002
68#define ERST_RANGE_SLOW 0x0004
69
70/*
71 * ERST Error Log Address Range, used as buffer for reading/writing
72 * error records.
73 */
74static struct erst_erange {
75 u64 base;
76 u64 size;
77 void __iomem *vaddr;
78 u32 attr;
79} erst_erange;
80
81/*
82 * Prevent ERST interpreter to run simultaneously, because the
83 * corresponding firmware implementation may not work properly when
84 * invoked simultaneously.
85 *
86 * It is used to provide exclusive accessing for ERST Error Log
87 * Address Range too.
88 */
Huang Ying3b38bb52010-12-02 10:40:53 +080089static DEFINE_RAW_SPINLOCK(erst_lock);
Huang Yinga08f82d2010-05-18 14:35:21 +080090
91static inline int erst_errno(int command_status)
92{
93 switch (command_status) {
94 case ERST_STATUS_SUCCESS:
95 return 0;
96 case ERST_STATUS_HARDWARE_NOT_AVAILABLE:
97 return -ENODEV;
98 case ERST_STATUS_NOT_ENOUGH_SPACE:
99 return -ENOSPC;
100 case ERST_STATUS_RECORD_STORE_EMPTY:
101 case ERST_STATUS_RECORD_NOT_FOUND:
102 return -ENOENT;
103 default:
104 return -EINVAL;
105 }
106}
107
108static int erst_timedout(u64 *t, u64 spin_unit)
109{
110 if ((s64)*t < spin_unit) {
111 pr_warning(FW_WARN ERST_PFX
112 "Firmware does not respond in time\n");
113 return 1;
114 }
115 *t -= spin_unit;
116 ndelay(spin_unit);
117 touch_nmi_watchdog();
118 return 0;
119}
120
121static int erst_exec_load_var1(struct apei_exec_context *ctx,
122 struct acpi_whea_header *entry)
123{
124 return __apei_exec_read_register(entry, &ctx->var1);
125}
126
127static int erst_exec_load_var2(struct apei_exec_context *ctx,
128 struct acpi_whea_header *entry)
129{
130 return __apei_exec_read_register(entry, &ctx->var2);
131}
132
133static int erst_exec_store_var1(struct apei_exec_context *ctx,
134 struct acpi_whea_header *entry)
135{
136 return __apei_exec_write_register(entry, ctx->var1);
137}
138
139static int erst_exec_add(struct apei_exec_context *ctx,
140 struct acpi_whea_header *entry)
141{
142 ctx->var1 += ctx->var2;
143 return 0;
144}
145
146static int erst_exec_subtract(struct apei_exec_context *ctx,
147 struct acpi_whea_header *entry)
148{
149 ctx->var1 -= ctx->var2;
150 return 0;
151}
152
153static int erst_exec_add_value(struct apei_exec_context *ctx,
154 struct acpi_whea_header *entry)
155{
156 int rc;
157 u64 val;
158
159 rc = __apei_exec_read_register(entry, &val);
160 if (rc)
161 return rc;
162 val += ctx->value;
163 rc = __apei_exec_write_register(entry, val);
164 return rc;
165}
166
167static int erst_exec_subtract_value(struct apei_exec_context *ctx,
168 struct acpi_whea_header *entry)
169{
170 int rc;
171 u64 val;
172
173 rc = __apei_exec_read_register(entry, &val);
174 if (rc)
175 return rc;
176 val -= ctx->value;
177 rc = __apei_exec_write_register(entry, val);
178 return rc;
179}
180
181static int erst_exec_stall(struct apei_exec_context *ctx,
182 struct acpi_whea_header *entry)
183{
184 u64 stall_time;
185
186 if (ctx->value > FIRMWARE_MAX_STALL) {
187 if (!in_nmi())
188 pr_warning(FW_WARN ERST_PFX
189 "Too long stall time for stall instruction: %llx.\n",
190 ctx->value);
191 stall_time = FIRMWARE_MAX_STALL;
192 } else
193 stall_time = ctx->value;
194 udelay(stall_time);
195 return 0;
196}
197
198static int erst_exec_stall_while_true(struct apei_exec_context *ctx,
199 struct acpi_whea_header *entry)
200{
201 int rc;
202 u64 val;
203 u64 timeout = FIRMWARE_TIMEOUT;
204 u64 stall_time;
205
206 if (ctx->var1 > FIRMWARE_MAX_STALL) {
207 if (!in_nmi())
208 pr_warning(FW_WARN ERST_PFX
209 "Too long stall time for stall while true instruction: %llx.\n",
210 ctx->var1);
211 stall_time = FIRMWARE_MAX_STALL;
212 } else
213 stall_time = ctx->var1;
214
215 for (;;) {
216 rc = __apei_exec_read_register(entry, &val);
217 if (rc)
218 return rc;
219 if (val != ctx->value)
220 break;
221 if (erst_timedout(&timeout, stall_time * NSEC_PER_USEC))
222 return -EIO;
223 }
224 return 0;
225}
226
227static int erst_exec_skip_next_instruction_if_true(
228 struct apei_exec_context *ctx,
229 struct acpi_whea_header *entry)
230{
231 int rc;
232 u64 val;
233
234 rc = __apei_exec_read_register(entry, &val);
235 if (rc)
236 return rc;
237 if (val == ctx->value) {
238 ctx->ip += 2;
239 return APEI_EXEC_SET_IP;
240 }
241
242 return 0;
243}
244
245static int erst_exec_goto(struct apei_exec_context *ctx,
246 struct acpi_whea_header *entry)
247{
248 ctx->ip = ctx->value;
249 return APEI_EXEC_SET_IP;
250}
251
252static int erst_exec_set_src_address_base(struct apei_exec_context *ctx,
253 struct acpi_whea_header *entry)
254{
255 return __apei_exec_read_register(entry, &ctx->src_base);
256}
257
258static int erst_exec_set_dst_address_base(struct apei_exec_context *ctx,
259 struct acpi_whea_header *entry)
260{
261 return __apei_exec_read_register(entry, &ctx->dst_base);
262}
263
264static int erst_exec_move_data(struct apei_exec_context *ctx,
265 struct acpi_whea_header *entry)
266{
267 int rc;
268 u64 offset;
Huang Ying0bbba382010-09-29 19:53:55 +0800269 void *src, *dst;
270
271 /* ioremap does not work in interrupt context */
272 if (in_interrupt()) {
273 pr_warning(ERST_PFX
274 "MOVE_DATA can not be used in interrupt context");
275 return -EBUSY;
276 }
Huang Yinga08f82d2010-05-18 14:35:21 +0800277
278 rc = __apei_exec_read_register(entry, &offset);
279 if (rc)
280 return rc;
Huang Ying0bbba382010-09-29 19:53:55 +0800281
282 src = ioremap(ctx->src_base + offset, ctx->var2);
283 if (!src)
284 return -ENOMEM;
285 dst = ioremap(ctx->dst_base + offset, ctx->var2);
286 if (!dst)
287 return -ENOMEM;
288
289 memmove(dst, src, ctx->var2);
290
291 iounmap(src);
292 iounmap(dst);
Huang Yinga08f82d2010-05-18 14:35:21 +0800293
294 return 0;
295}
296
297static struct apei_exec_ins_type erst_ins_type[] = {
298 [ACPI_ERST_READ_REGISTER] = {
299 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
300 .run = apei_exec_read_register,
301 },
302 [ACPI_ERST_READ_REGISTER_VALUE] = {
303 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
304 .run = apei_exec_read_register_value,
305 },
306 [ACPI_ERST_WRITE_REGISTER] = {
307 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
308 .run = apei_exec_write_register,
309 },
310 [ACPI_ERST_WRITE_REGISTER_VALUE] = {
311 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
312 .run = apei_exec_write_register_value,
313 },
314 [ACPI_ERST_NOOP] = {
315 .flags = 0,
316 .run = apei_exec_noop,
317 },
318 [ACPI_ERST_LOAD_VAR1] = {
319 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
320 .run = erst_exec_load_var1,
321 },
322 [ACPI_ERST_LOAD_VAR2] = {
323 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
324 .run = erst_exec_load_var2,
325 },
326 [ACPI_ERST_STORE_VAR1] = {
327 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
328 .run = erst_exec_store_var1,
329 },
330 [ACPI_ERST_ADD] = {
331 .flags = 0,
332 .run = erst_exec_add,
333 },
334 [ACPI_ERST_SUBTRACT] = {
335 .flags = 0,
336 .run = erst_exec_subtract,
337 },
338 [ACPI_ERST_ADD_VALUE] = {
339 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
340 .run = erst_exec_add_value,
341 },
342 [ACPI_ERST_SUBTRACT_VALUE] = {
343 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
344 .run = erst_exec_subtract_value,
345 },
346 [ACPI_ERST_STALL] = {
347 .flags = 0,
348 .run = erst_exec_stall,
349 },
350 [ACPI_ERST_STALL_WHILE_TRUE] = {
351 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
352 .run = erst_exec_stall_while_true,
353 },
354 [ACPI_ERST_SKIP_NEXT_IF_TRUE] = {
355 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
356 .run = erst_exec_skip_next_instruction_if_true,
357 },
358 [ACPI_ERST_GOTO] = {
359 .flags = 0,
360 .run = erst_exec_goto,
361 },
362 [ACPI_ERST_SET_SRC_ADDRESS_BASE] = {
363 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
364 .run = erst_exec_set_src_address_base,
365 },
366 [ACPI_ERST_SET_DST_ADDRESS_BASE] = {
367 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
368 .run = erst_exec_set_dst_address_base,
369 },
370 [ACPI_ERST_MOVE_DATA] = {
371 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
372 .run = erst_exec_move_data,
373 },
374};
375
376static inline void erst_exec_ctx_init(struct apei_exec_context *ctx)
377{
378 apei_exec_ctx_init(ctx, erst_ins_type, ARRAY_SIZE(erst_ins_type),
379 ERST_TAB_ENTRY(erst_tab), erst_tab->entries);
380}
381
382static int erst_get_erange(struct erst_erange *range)
383{
384 struct apei_exec_context ctx;
385 int rc;
386
387 erst_exec_ctx_init(&ctx);
388 rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_RANGE);
389 if (rc)
390 return rc;
391 range->base = apei_exec_ctx_get_output(&ctx);
392 rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_LENGTH);
393 if (rc)
394 return rc;
395 range->size = apei_exec_ctx_get_output(&ctx);
396 rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_ATTRIBUTES);
397 if (rc)
398 return rc;
399 range->attr = apei_exec_ctx_get_output(&ctx);
400
401 return 0;
402}
403
404static ssize_t __erst_get_record_count(void)
405{
406 struct apei_exec_context ctx;
407 int rc;
408
409 erst_exec_ctx_init(&ctx);
410 rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_COUNT);
411 if (rc)
412 return rc;
413 return apei_exec_ctx_get_output(&ctx);
414}
415
416ssize_t erst_get_record_count(void)
417{
418 ssize_t count;
419 unsigned long flags;
420
421 if (erst_disable)
422 return -ENODEV;
423
Huang Ying3b38bb52010-12-02 10:40:53 +0800424 raw_spin_lock_irqsave(&erst_lock, flags);
Huang Yinga08f82d2010-05-18 14:35:21 +0800425 count = __erst_get_record_count();
Huang Ying3b38bb52010-12-02 10:40:53 +0800426 raw_spin_unlock_irqrestore(&erst_lock, flags);
Huang Yinga08f82d2010-05-18 14:35:21 +0800427
428 return count;
429}
430EXPORT_SYMBOL_GPL(erst_get_record_count);
431
Huang Ying885b9762011-02-21 13:54:41 +0800432#define ERST_RECORD_ID_CACHE_SIZE_MIN 16
433#define ERST_RECORD_ID_CACHE_SIZE_MAX 1024
434
435struct erst_record_id_cache {
436 struct mutex lock;
437 u64 *entries;
438 int len;
439 int size;
440 int refcount;
441};
442
443static struct erst_record_id_cache erst_record_id_cache = {
444 .lock = __MUTEX_INITIALIZER(erst_record_id_cache.lock),
445 .refcount = 0,
446};
447
Huang Yinga08f82d2010-05-18 14:35:21 +0800448static int __erst_get_next_record_id(u64 *record_id)
449{
450 struct apei_exec_context ctx;
451 int rc;
452
453 erst_exec_ctx_init(&ctx);
454 rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_ID);
455 if (rc)
456 return rc;
457 *record_id = apei_exec_ctx_get_output(&ctx);
458
459 return 0;
460}
461
Huang Ying885b9762011-02-21 13:54:41 +0800462int erst_get_record_id_begin(int *pos)
463{
464 int rc;
465
466 if (erst_disable)
467 return -ENODEV;
468
469 rc = mutex_lock_interruptible(&erst_record_id_cache.lock);
470 if (rc)
471 return rc;
472 erst_record_id_cache.refcount++;
473 mutex_unlock(&erst_record_id_cache.lock);
474
475 *pos = 0;
476
477 return 0;
478}
479EXPORT_SYMBOL_GPL(erst_get_record_id_begin);
480
481/* erst_record_id_cache.lock must be held by caller */
482static int __erst_record_id_cache_add_one(void)
483{
484 u64 id, prev_id, first_id;
485 int i, rc;
486 u64 *entries;
487 unsigned long flags;
488
489 id = prev_id = first_id = APEI_ERST_INVALID_RECORD_ID;
490retry:
491 raw_spin_lock_irqsave(&erst_lock, flags);
492 rc = __erst_get_next_record_id(&id);
493 raw_spin_unlock_irqrestore(&erst_lock, flags);
494 if (rc == -ENOENT)
495 return 0;
496 if (rc)
497 return rc;
498 if (id == APEI_ERST_INVALID_RECORD_ID)
499 return 0;
500 /* can not skip current ID, or loop back to first ID */
501 if (id == prev_id || id == first_id)
502 return 0;
503 if (first_id == APEI_ERST_INVALID_RECORD_ID)
504 first_id = id;
505 prev_id = id;
506
507 entries = erst_record_id_cache.entries;
508 for (i = 0; i < erst_record_id_cache.len; i++) {
509 if (entries[i] == id)
510 break;
511 }
512 /* record id already in cache, try next */
513 if (i < erst_record_id_cache.len)
514 goto retry;
515 if (erst_record_id_cache.len >= erst_record_id_cache.size) {
516 int new_size, alloc_size;
517 u64 *new_entries;
518
519 new_size = erst_record_id_cache.size * 2;
520 new_size = clamp_val(new_size, ERST_RECORD_ID_CACHE_SIZE_MIN,
521 ERST_RECORD_ID_CACHE_SIZE_MAX);
522 if (new_size <= erst_record_id_cache.size) {
523 if (printk_ratelimit())
524 pr_warning(FW_WARN ERST_PFX
525 "too many record ID!\n");
526 return 0;
527 }
528 alloc_size = new_size * sizeof(entries[0]);
529 if (alloc_size < PAGE_SIZE)
530 new_entries = kmalloc(alloc_size, GFP_KERNEL);
531 else
532 new_entries = vmalloc(alloc_size);
533 if (!new_entries)
534 return -ENOMEM;
535 memcpy(new_entries, entries,
536 erst_record_id_cache.len * sizeof(entries[0]));
537 if (erst_record_id_cache.size < PAGE_SIZE)
538 kfree(entries);
539 else
540 vfree(entries);
541 erst_record_id_cache.entries = entries = new_entries;
542 erst_record_id_cache.size = new_size;
543 }
544 entries[i] = id;
545 erst_record_id_cache.len++;
546
547 return 1;
548}
549
Huang Yinga08f82d2010-05-18 14:35:21 +0800550/*
551 * Get the record ID of an existing error record on the persistent
552 * storage. If there is no error record on the persistent storage, the
553 * returned record_id is APEI_ERST_INVALID_RECORD_ID.
554 */
Huang Ying885b9762011-02-21 13:54:41 +0800555int erst_get_record_id_next(int *pos, u64 *record_id)
Huang Yinga08f82d2010-05-18 14:35:21 +0800556{
Huang Ying885b9762011-02-21 13:54:41 +0800557 int rc = 0;
558 u64 *entries;
Huang Yinga08f82d2010-05-18 14:35:21 +0800559
560 if (erst_disable)
561 return -ENODEV;
562
Huang Ying885b9762011-02-21 13:54:41 +0800563 /* must be enclosed by erst_get_record_id_begin/end */
564 BUG_ON(!erst_record_id_cache.refcount);
565 BUG_ON(*pos < 0 || *pos > erst_record_id_cache.len);
566
567 mutex_lock(&erst_record_id_cache.lock);
568 entries = erst_record_id_cache.entries;
569 for (; *pos < erst_record_id_cache.len; (*pos)++)
570 if (entries[*pos] != APEI_ERST_INVALID_RECORD_ID)
571 break;
572 /* found next record id in cache */
573 if (*pos < erst_record_id_cache.len) {
574 *record_id = entries[*pos];
575 (*pos)++;
576 goto out_unlock;
577 }
578
579 /* Try to add one more record ID to cache */
580 rc = __erst_record_id_cache_add_one();
581 if (rc < 0)
582 goto out_unlock;
583 /* successfully add one new ID */
584 if (rc == 1) {
585 *record_id = erst_record_id_cache.entries[*pos];
586 (*pos)++;
587 rc = 0;
588 } else {
589 *pos = -1;
590 *record_id = APEI_ERST_INVALID_RECORD_ID;
591 }
592out_unlock:
593 mutex_unlock(&erst_record_id_cache.lock);
Huang Yinga08f82d2010-05-18 14:35:21 +0800594
595 return rc;
596}
Huang Ying885b9762011-02-21 13:54:41 +0800597EXPORT_SYMBOL_GPL(erst_get_record_id_next);
598
599/* erst_record_id_cache.lock must be held by caller */
600static void __erst_record_id_cache_compact(void)
601{
602 int i, wpos = 0;
603 u64 *entries;
604
605 if (erst_record_id_cache.refcount)
606 return;
607
608 entries = erst_record_id_cache.entries;
609 for (i = 0; i < erst_record_id_cache.len; i++) {
610 if (entries[i] == APEI_ERST_INVALID_RECORD_ID)
611 continue;
612 if (wpos != i)
613 memcpy(&entries[wpos], &entries[i], sizeof(entries[i]));
614 wpos++;
615 }
616 erst_record_id_cache.len = wpos;
617}
618
619void erst_get_record_id_end(void)
620{
621 /*
622 * erst_disable != 0 should be detected by invoker via the
623 * return value of erst_get_record_id_begin/next, so this
624 * function should not be called for erst_disable != 0.
625 */
626 BUG_ON(erst_disable);
627
628 mutex_lock(&erst_record_id_cache.lock);
629 erst_record_id_cache.refcount--;
630 BUG_ON(erst_record_id_cache.refcount < 0);
631 __erst_record_id_cache_compact();
632 mutex_unlock(&erst_record_id_cache.lock);
633}
634EXPORT_SYMBOL_GPL(erst_get_record_id_end);
Huang Yinga08f82d2010-05-18 14:35:21 +0800635
636static int __erst_write_to_storage(u64 offset)
637{
638 struct apei_exec_context ctx;
639 u64 timeout = FIRMWARE_TIMEOUT;
640 u64 val;
641 int rc;
642
643 erst_exec_ctx_init(&ctx);
644 rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_WRITE);
645 if (rc)
646 return rc;
647 apei_exec_ctx_set_input(&ctx, offset);
648 rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET);
649 if (rc)
650 return rc;
651 rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);
652 if (rc)
653 return rc;
654 for (;;) {
655 rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);
656 if (rc)
657 return rc;
658 val = apei_exec_ctx_get_output(&ctx);
659 if (!val)
660 break;
661 if (erst_timedout(&timeout, SPIN_UNIT))
662 return -EIO;
663 }
664 rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);
665 if (rc)
666 return rc;
667 val = apei_exec_ctx_get_output(&ctx);
668 rc = apei_exec_run(&ctx, ACPI_ERST_END);
669 if (rc)
670 return rc;
671
672 return erst_errno(val);
673}
674
675static int __erst_read_from_storage(u64 record_id, u64 offset)
676{
677 struct apei_exec_context ctx;
678 u64 timeout = FIRMWARE_TIMEOUT;
679 u64 val;
680 int rc;
681
682 erst_exec_ctx_init(&ctx);
683 rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_READ);
684 if (rc)
685 return rc;
686 apei_exec_ctx_set_input(&ctx, offset);
687 rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET);
688 if (rc)
689 return rc;
690 apei_exec_ctx_set_input(&ctx, record_id);
691 rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID);
692 if (rc)
693 return rc;
694 rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);
695 if (rc)
696 return rc;
697 for (;;) {
698 rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);
699 if (rc)
700 return rc;
701 val = apei_exec_ctx_get_output(&ctx);
702 if (!val)
703 break;
704 if (erst_timedout(&timeout, SPIN_UNIT))
705 return -EIO;
706 };
707 rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);
708 if (rc)
709 return rc;
710 val = apei_exec_ctx_get_output(&ctx);
711 rc = apei_exec_run(&ctx, ACPI_ERST_END);
712 if (rc)
713 return rc;
714
715 return erst_errno(val);
716}
717
718static int __erst_clear_from_storage(u64 record_id)
719{
720 struct apei_exec_context ctx;
721 u64 timeout = FIRMWARE_TIMEOUT;
722 u64 val;
723 int rc;
724
725 erst_exec_ctx_init(&ctx);
726 rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_CLEAR);
727 if (rc)
728 return rc;
729 apei_exec_ctx_set_input(&ctx, record_id);
730 rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID);
731 if (rc)
732 return rc;
733 rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);
734 if (rc)
735 return rc;
736 for (;;) {
737 rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);
738 if (rc)
739 return rc;
740 val = apei_exec_ctx_get_output(&ctx);
741 if (!val)
742 break;
743 if (erst_timedout(&timeout, SPIN_UNIT))
744 return -EIO;
745 }
746 rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);
747 if (rc)
748 return rc;
749 val = apei_exec_ctx_get_output(&ctx);
750 rc = apei_exec_run(&ctx, ACPI_ERST_END);
751 if (rc)
752 return rc;
753
754 return erst_errno(val);
755}
756
757/* NVRAM ERST Error Log Address Range is not supported yet */
758static void pr_unimpl_nvram(void)
759{
760 if (printk_ratelimit())
761 pr_warning(ERST_PFX
762 "NVRAM ERST Log Address Range is not implemented yet\n");
763}
764
765static int __erst_write_to_nvram(const struct cper_record_header *record)
766{
767 /* do not print message, because printk is not safe for NMI */
768 return -ENOSYS;
769}
770
771static int __erst_read_to_erange_from_nvram(u64 record_id, u64 *offset)
772{
773 pr_unimpl_nvram();
774 return -ENOSYS;
775}
776
777static int __erst_clear_from_nvram(u64 record_id)
778{
779 pr_unimpl_nvram();
780 return -ENOSYS;
781}
782
783int erst_write(const struct cper_record_header *record)
784{
785 int rc;
786 unsigned long flags;
787 struct cper_record_header *rcd_erange;
788
789 if (erst_disable)
790 return -ENODEV;
791
792 if (memcmp(record->signature, CPER_SIG_RECORD, CPER_SIG_SIZE))
793 return -EINVAL;
794
795 if (erst_erange.attr & ERST_RANGE_NVRAM) {
Huang Ying3b38bb52010-12-02 10:40:53 +0800796 if (!raw_spin_trylock_irqsave(&erst_lock, flags))
Huang Yinga08f82d2010-05-18 14:35:21 +0800797 return -EBUSY;
798 rc = __erst_write_to_nvram(record);
Huang Ying3b38bb52010-12-02 10:40:53 +0800799 raw_spin_unlock_irqrestore(&erst_lock, flags);
Huang Yinga08f82d2010-05-18 14:35:21 +0800800 return rc;
801 }
802
803 if (record->record_length > erst_erange.size)
804 return -EINVAL;
805
Huang Ying3b38bb52010-12-02 10:40:53 +0800806 if (!raw_spin_trylock_irqsave(&erst_lock, flags))
Huang Yinga08f82d2010-05-18 14:35:21 +0800807 return -EBUSY;
808 memcpy(erst_erange.vaddr, record, record->record_length);
809 rcd_erange = erst_erange.vaddr;
810 /* signature for serialization system */
811 memcpy(&rcd_erange->persistence_information, "ER", 2);
812
813 rc = __erst_write_to_storage(0);
Huang Ying3b38bb52010-12-02 10:40:53 +0800814 raw_spin_unlock_irqrestore(&erst_lock, flags);
Huang Yinga08f82d2010-05-18 14:35:21 +0800815
816 return rc;
817}
818EXPORT_SYMBOL_GPL(erst_write);
819
820static int __erst_read_to_erange(u64 record_id, u64 *offset)
821{
822 int rc;
823
824 if (erst_erange.attr & ERST_RANGE_NVRAM)
825 return __erst_read_to_erange_from_nvram(
826 record_id, offset);
827
828 rc = __erst_read_from_storage(record_id, 0);
829 if (rc)
830 return rc;
831 *offset = 0;
832
833 return 0;
834}
835
836static ssize_t __erst_read(u64 record_id, struct cper_record_header *record,
837 size_t buflen)
838{
839 int rc;
840 u64 offset, len = 0;
841 struct cper_record_header *rcd_tmp;
842
843 rc = __erst_read_to_erange(record_id, &offset);
844 if (rc)
845 return rc;
846 rcd_tmp = erst_erange.vaddr + offset;
847 len = rcd_tmp->record_length;
848 if (len <= buflen)
849 memcpy(record, rcd_tmp, len);
850
851 return len;
852}
853
854/*
855 * If return value > buflen, the buffer size is not big enough,
856 * else if return value < 0, something goes wrong,
857 * else everything is OK, and return value is record length
858 */
859ssize_t erst_read(u64 record_id, struct cper_record_header *record,
860 size_t buflen)
861{
862 ssize_t len;
863 unsigned long flags;
864
865 if (erst_disable)
866 return -ENODEV;
867
Huang Ying3b38bb52010-12-02 10:40:53 +0800868 raw_spin_lock_irqsave(&erst_lock, flags);
Huang Yinga08f82d2010-05-18 14:35:21 +0800869 len = __erst_read(record_id, record, buflen);
Huang Ying3b38bb52010-12-02 10:40:53 +0800870 raw_spin_unlock_irqrestore(&erst_lock, flags);
Huang Yinga08f82d2010-05-18 14:35:21 +0800871 return len;
872}
873EXPORT_SYMBOL_GPL(erst_read);
874
Huang Yinga08f82d2010-05-18 14:35:21 +0800875int erst_clear(u64 record_id)
876{
Huang Ying885b9762011-02-21 13:54:41 +0800877 int rc, i;
Huang Yinga08f82d2010-05-18 14:35:21 +0800878 unsigned long flags;
Huang Ying885b9762011-02-21 13:54:41 +0800879 u64 *entries;
Huang Yinga08f82d2010-05-18 14:35:21 +0800880
881 if (erst_disable)
882 return -ENODEV;
883
Huang Ying885b9762011-02-21 13:54:41 +0800884 rc = mutex_lock_interruptible(&erst_record_id_cache.lock);
885 if (rc)
886 return rc;
Huang Ying3b38bb52010-12-02 10:40:53 +0800887 raw_spin_lock_irqsave(&erst_lock, flags);
Huang Yinga08f82d2010-05-18 14:35:21 +0800888 if (erst_erange.attr & ERST_RANGE_NVRAM)
889 rc = __erst_clear_from_nvram(record_id);
890 else
891 rc = __erst_clear_from_storage(record_id);
Huang Ying3b38bb52010-12-02 10:40:53 +0800892 raw_spin_unlock_irqrestore(&erst_lock, flags);
Huang Ying885b9762011-02-21 13:54:41 +0800893 if (rc)
894 goto out;
895 entries = erst_record_id_cache.entries;
896 for (i = 0; i < erst_record_id_cache.len; i++) {
897 if (entries[i] == record_id)
898 entries[i] = APEI_ERST_INVALID_RECORD_ID;
899 }
900 __erst_record_id_cache_compact();
901out:
902 mutex_unlock(&erst_record_id_cache.lock);
Huang Yinga08f82d2010-05-18 14:35:21 +0800903 return rc;
904}
905EXPORT_SYMBOL_GPL(erst_clear);
906
907static int __init setup_erst_disable(char *str)
908{
909 erst_disable = 1;
910 return 0;
911}
912
913__setup("erst_disable", setup_erst_disable);
914
915static int erst_check_table(struct acpi_table_erst *erst_tab)
916{
Huang Ying3a78f962010-09-29 19:53:51 +0800917 if ((erst_tab->header_length !=
918 (sizeof(struct acpi_table_erst) - sizeof(erst_tab->header)))
919 && (erst_tab->header_length != sizeof(struct acpi_table_einj)))
Huang Yinga08f82d2010-05-18 14:35:21 +0800920 return -EINVAL;
921 if (erst_tab->header.length < sizeof(struct acpi_table_erst))
922 return -EINVAL;
923 if (erst_tab->entries !=
924 (erst_tab->header.length - sizeof(struct acpi_table_erst)) /
925 sizeof(struct acpi_erst_entry))
926 return -EINVAL;
927
928 return 0;
929}
930
931static int __init erst_init(void)
932{
933 int rc = 0;
934 acpi_status status;
935 struct apei_exec_context ctx;
936 struct apei_resources erst_resources;
937 struct resource *r;
938
939 if (acpi_disabled)
940 goto err;
941
942 if (erst_disable) {
943 pr_info(ERST_PFX
944 "Error Record Serialization Table (ERST) support is disabled.\n");
945 goto err;
946 }
947
948 status = acpi_get_table(ACPI_SIG_ERST, 0,
949 (struct acpi_table_header **)&erst_tab);
950 if (status == AE_NOT_FOUND) {
Daniel J Blueman980533b2010-07-01 23:27:11 +0100951 pr_info(ERST_PFX "Table is not found!\n");
Huang Yinga08f82d2010-05-18 14:35:21 +0800952 goto err;
953 } else if (ACPI_FAILURE(status)) {
954 const char *msg = acpi_format_exception(status);
955 pr_err(ERST_PFX "Failed to get table, %s\n", msg);
956 rc = -EINVAL;
957 goto err;
958 }
959
960 rc = erst_check_table(erst_tab);
961 if (rc) {
962 pr_err(FW_BUG ERST_PFX "ERST table is invalid\n");
963 goto err;
964 }
965
966 apei_resources_init(&erst_resources);
967 erst_exec_ctx_init(&ctx);
968 rc = apei_exec_collect_resources(&ctx, &erst_resources);
969 if (rc)
970 goto err_fini;
971 rc = apei_resources_request(&erst_resources, "APEI ERST");
972 if (rc)
973 goto err_fini;
974 rc = apei_exec_pre_map_gars(&ctx);
975 if (rc)
976 goto err_release;
977 rc = erst_get_erange(&erst_erange);
978 if (rc) {
979 if (rc == -ENODEV)
980 pr_info(ERST_PFX
981 "The corresponding hardware device or firmware implementation "
982 "is not available.\n");
983 else
984 pr_err(ERST_PFX
985 "Failed to get Error Log Address Range.\n");
986 goto err_unmap_reg;
987 }
988
989 r = request_mem_region(erst_erange.base, erst_erange.size, "APEI ERST");
990 if (!r) {
991 pr_err(ERST_PFX
992 "Can not request iomem region <0x%16llx-0x%16llx> for ERST.\n",
993 (unsigned long long)erst_erange.base,
994 (unsigned long long)erst_erange.base + erst_erange.size);
995 rc = -EIO;
996 goto err_unmap_reg;
997 }
998 rc = -ENOMEM;
999 erst_erange.vaddr = ioremap_cache(erst_erange.base,
1000 erst_erange.size);
1001 if (!erst_erange.vaddr)
1002 goto err_release_erange;
1003
1004 pr_info(ERST_PFX
1005 "Error Record Serialization Table (ERST) support is initialized.\n");
1006
1007 return 0;
1008
1009err_release_erange:
1010 release_mem_region(erst_erange.base, erst_erange.size);
1011err_unmap_reg:
1012 apei_exec_post_unmap_gars(&ctx);
1013err_release:
1014 apei_resources_release(&erst_resources);
1015err_fini:
1016 apei_resources_fini(&erst_resources);
1017err:
1018 erst_disable = 1;
1019 return rc;
1020}
1021
1022device_initcall(erst_init);