aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHuang Ying <ying.huang@intel.com>2010-05-18 02:35:21 -0400
committerLen Brown <len.brown@intel.com>2010-05-19 22:41:31 -0400
commita08f82d08053fb6e3aa3635c2c26456d96337c8b (patch)
treeed68e8951610b66c6971cdb1fc446eb9e53e0422
parentd334a49113a4a33109fd24e46073280ecd1bea0d (diff)
ACPI, APEI, Error Record Serialization Table (ERST) support
ERST is a way provided by APEI to save and retrieve hardware error record to and from some simple persistent storage (such as flash). The Linux kernel support implementation is quite simple and workable in NMI context. So it can be used to save hardware error record into flash in hardware error exception or NMI handler, where other more complex persistent storage such as disk is not usable. After saving hardware error records via ERST in hardware error exception or NMI handler, the error records can be retrieved and logged into disk or network after a clean reboot. For more information about ERST, please refer to ACPI Specification version 4.0, section 17.4. This patch incorporate fixes from Jin Dongming. Signed-off-by: Huang Ying <ying.huang@intel.com> Signed-off-by: Andi Kleen <ak@linux.intel.com> CC: Jin Dongming <jin.dongming@np.css.fujitsu.com> Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r--Documentation/kernel-parameters.txt4
-rw-r--r--drivers/acpi/apei/Makefile2
-rw-r--r--drivers/acpi/apei/erst.c855
-rw-r--r--include/acpi/apei.h21
4 files changed, 881 insertions, 1 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 47a0e1ba5e64..42d77735fd45 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -750,6 +750,10 @@ and is between 256 and 4096 characters. It is defined in the file
750 Default value is 0. 750 Default value is 0.
751 Value can be changed at runtime via /selinux/enforce. 751 Value can be changed at runtime via /selinux/enforce.
752 752
753 erst_disable [ACPI]
754 Disable Error Record Serialization Table (ERST)
755 support.
756
753 ether= [HW,NET] Ethernet cards parameters 757 ether= [HW,NET] Ethernet cards parameters
754 This option is obsoleted by the "netdev=" option, which 758 This option is obsoleted by the "netdev=" option, which
755 has equivalent usage. See its documentation for details. 759 has equivalent usage. See its documentation for details.
diff --git a/drivers/acpi/apei/Makefile b/drivers/acpi/apei/Makefile
index 41c61db4c51c..b13b03a17789 100644
--- a/drivers/acpi/apei/Makefile
+++ b/drivers/acpi/apei/Makefile
@@ -2,4 +2,4 @@ obj-$(CONFIG_ACPI_APEI) += apei.o
2obj-$(CONFIG_ACPI_APEI_GHES) += ghes.o 2obj-$(CONFIG_ACPI_APEI_GHES) += ghes.o
3obj-$(CONFIG_ACPI_APEI_EINJ) += einj.o 3obj-$(CONFIG_ACPI_APEI_EINJ) += einj.o
4 4
5apei-y := apei-base.o hest.o cper.o 5apei-y := apei-base.o hest.o cper.o erst.o
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
new file mode 100644
index 000000000000..2ebc39115507
--- /dev/null
+++ b/drivers/acpi/apei/erst.c
@@ -0,0 +1,855 @@
1/*
2 * APEI Error Record Serialization Table support
3 *
4 * ERST is a way provided by APEI to save and retrieve hardware error
5 * infomation to and from a persistent store.
6 *
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>
36#include <acpi/apei.h>
37
38#include "apei-internal.h"
39
40#define ERST_PFX "ERST: "
41
42/* ERST command status */
43#define ERST_STATUS_SUCCESS 0x0
44#define ERST_STATUS_NOT_ENOUGH_SPACE 0x1
45#define ERST_STATUS_HARDWARE_NOT_AVAILABLE 0x2
46#define ERST_STATUS_FAILED 0x3
47#define ERST_STATUS_RECORD_STORE_EMPTY 0x4
48#define ERST_STATUS_RECORD_NOT_FOUND 0x5
49
50#define ERST_TAB_ENTRY(tab) \
51 ((struct acpi_whea_header *)((char *)(tab) + \
52 sizeof(struct acpi_table_erst)))
53
54#define SPIN_UNIT 100 /* 100ns */
55/* Firmware should respond within 1 miliseconds */
56#define FIRMWARE_TIMEOUT (1 * NSEC_PER_MSEC)
57#define FIRMWARE_MAX_STALL 50 /* 50us */
58
59int erst_disable;
60EXPORT_SYMBOL_GPL(erst_disable);
61
62static struct acpi_table_erst *erst_tab;
63
64/* ERST Error Log Address Range atrributes */
65#define ERST_RANGE_RESERVED 0x0001
66#define ERST_RANGE_NVRAM 0x0002
67#define ERST_RANGE_SLOW 0x0004
68
69/*
70 * ERST Error Log Address Range, used as buffer for reading/writing
71 * error records.
72 */
73static struct erst_erange {
74 u64 base;
75 u64 size;
76 void __iomem *vaddr;
77 u32 attr;
78} erst_erange;
79
80/*
81 * Prevent ERST interpreter to run simultaneously, because the
82 * corresponding firmware implementation may not work properly when
83 * invoked simultaneously.
84 *
85 * It is used to provide exclusive accessing for ERST Error Log
86 * Address Range too.
87 */
88static DEFINE_SPINLOCK(erst_lock);
89
90static inline int erst_errno(int command_status)
91{
92 switch (command_status) {
93 case ERST_STATUS_SUCCESS:
94 return 0;
95 case ERST_STATUS_HARDWARE_NOT_AVAILABLE:
96 return -ENODEV;
97 case ERST_STATUS_NOT_ENOUGH_SPACE:
98 return -ENOSPC;
99 case ERST_STATUS_RECORD_STORE_EMPTY:
100 case ERST_STATUS_RECORD_NOT_FOUND:
101 return -ENOENT;
102 default:
103 return -EINVAL;
104 }
105}
106
107static int erst_timedout(u64 *t, u64 spin_unit)
108{
109 if ((s64)*t < spin_unit) {
110 pr_warning(FW_WARN ERST_PFX
111 "Firmware does not respond in time\n");
112 return 1;
113 }
114 *t -= spin_unit;
115 ndelay(spin_unit);
116 touch_nmi_watchdog();
117 return 0;
118}
119
120static int erst_exec_load_var1(struct apei_exec_context *ctx,
121 struct acpi_whea_header *entry)
122{
123 return __apei_exec_read_register(entry, &ctx->var1);
124}
125
126static int erst_exec_load_var2(struct apei_exec_context *ctx,
127 struct acpi_whea_header *entry)
128{
129 return __apei_exec_read_register(entry, &ctx->var2);
130}
131
132static int erst_exec_store_var1(struct apei_exec_context *ctx,
133 struct acpi_whea_header *entry)
134{
135 return __apei_exec_write_register(entry, ctx->var1);
136}
137
138static int erst_exec_add(struct apei_exec_context *ctx,
139 struct acpi_whea_header *entry)
140{
141 ctx->var1 += ctx->var2;
142 return 0;
143}
144
145static int erst_exec_subtract(struct apei_exec_context *ctx,
146 struct acpi_whea_header *entry)
147{
148 ctx->var1 -= ctx->var2;
149 return 0;
150}
151
152static int erst_exec_add_value(struct apei_exec_context *ctx,
153 struct acpi_whea_header *entry)
154{
155 int rc;
156 u64 val;
157
158 rc = __apei_exec_read_register(entry, &val);
159 if (rc)
160 return rc;
161 val += ctx->value;
162 rc = __apei_exec_write_register(entry, val);
163 return rc;
164}
165
166static int erst_exec_subtract_value(struct apei_exec_context *ctx,
167 struct acpi_whea_header *entry)
168{
169 int rc;
170 u64 val;
171
172 rc = __apei_exec_read_register(entry, &val);
173 if (rc)
174 return rc;
175 val -= ctx->value;
176 rc = __apei_exec_write_register(entry, val);
177 return rc;
178}
179
180static int erst_exec_stall(struct apei_exec_context *ctx,
181 struct acpi_whea_header *entry)
182{
183 u64 stall_time;
184
185 if (ctx->value > FIRMWARE_MAX_STALL) {
186 if (!in_nmi())
187 pr_warning(FW_WARN ERST_PFX
188 "Too long stall time for stall instruction: %llx.\n",
189 ctx->value);
190 stall_time = FIRMWARE_MAX_STALL;
191 } else
192 stall_time = ctx->value;
193 udelay(stall_time);
194 return 0;
195}
196
197static int erst_exec_stall_while_true(struct apei_exec_context *ctx,
198 struct acpi_whea_header *entry)
199{
200 int rc;
201 u64 val;
202 u64 timeout = FIRMWARE_TIMEOUT;
203 u64 stall_time;
204
205 if (ctx->var1 > FIRMWARE_MAX_STALL) {
206 if (!in_nmi())
207 pr_warning(FW_WARN ERST_PFX
208 "Too long stall time for stall while true instruction: %llx.\n",
209 ctx->var1);
210 stall_time = FIRMWARE_MAX_STALL;
211 } else
212 stall_time = ctx->var1;
213
214 for (;;) {
215 rc = __apei_exec_read_register(entry, &val);
216 if (rc)
217 return rc;
218 if (val != ctx->value)
219 break;
220 if (erst_timedout(&timeout, stall_time * NSEC_PER_USEC))
221 return -EIO;
222 }
223 return 0;
224}
225
226static int erst_exec_skip_next_instruction_if_true(
227 struct apei_exec_context *ctx,
228 struct acpi_whea_header *entry)
229{
230 int rc;
231 u64 val;
232
233 rc = __apei_exec_read_register(entry, &val);
234 if (rc)
235 return rc;
236 if (val == ctx->value) {
237 ctx->ip += 2;
238 return APEI_EXEC_SET_IP;
239 }
240
241 return 0;
242}
243
244static int erst_exec_goto(struct apei_exec_context *ctx,
245 struct acpi_whea_header *entry)
246{
247 ctx->ip = ctx->value;
248 return APEI_EXEC_SET_IP;
249}
250
251static int erst_exec_set_src_address_base(struct apei_exec_context *ctx,
252 struct acpi_whea_header *entry)
253{
254 return __apei_exec_read_register(entry, &ctx->src_base);
255}
256
257static int erst_exec_set_dst_address_base(struct apei_exec_context *ctx,
258 struct acpi_whea_header *entry)
259{
260 return __apei_exec_read_register(entry, &ctx->dst_base);
261}
262
263static int erst_exec_move_data(struct apei_exec_context *ctx,
264 struct acpi_whea_header *entry)
265{
266 int rc;
267 u64 offset;
268
269 rc = __apei_exec_read_register(entry, &offset);
270 if (rc)
271 return rc;
272 memmove((void *)ctx->dst_base + offset,
273 (void *)ctx->src_base + offset,
274 ctx->var2);
275
276 return 0;
277}
278
279static struct apei_exec_ins_type erst_ins_type[] = {
280 [ACPI_ERST_READ_REGISTER] = {
281 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
282 .run = apei_exec_read_register,
283 },
284 [ACPI_ERST_READ_REGISTER_VALUE] = {
285 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
286 .run = apei_exec_read_register_value,
287 },
288 [ACPI_ERST_WRITE_REGISTER] = {
289 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
290 .run = apei_exec_write_register,
291 },
292 [ACPI_ERST_WRITE_REGISTER_VALUE] = {
293 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
294 .run = apei_exec_write_register_value,
295 },
296 [ACPI_ERST_NOOP] = {
297 .flags = 0,
298 .run = apei_exec_noop,
299 },
300 [ACPI_ERST_LOAD_VAR1] = {
301 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
302 .run = erst_exec_load_var1,
303 },
304 [ACPI_ERST_LOAD_VAR2] = {
305 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
306 .run = erst_exec_load_var2,
307 },
308 [ACPI_ERST_STORE_VAR1] = {
309 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
310 .run = erst_exec_store_var1,
311 },
312 [ACPI_ERST_ADD] = {
313 .flags = 0,
314 .run = erst_exec_add,
315 },
316 [ACPI_ERST_SUBTRACT] = {
317 .flags = 0,
318 .run = erst_exec_subtract,
319 },
320 [ACPI_ERST_ADD_VALUE] = {
321 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
322 .run = erst_exec_add_value,
323 },
324 [ACPI_ERST_SUBTRACT_VALUE] = {
325 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
326 .run = erst_exec_subtract_value,
327 },
328 [ACPI_ERST_STALL] = {
329 .flags = 0,
330 .run = erst_exec_stall,
331 },
332 [ACPI_ERST_STALL_WHILE_TRUE] = {
333 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
334 .run = erst_exec_stall_while_true,
335 },
336 [ACPI_ERST_SKIP_NEXT_IF_TRUE] = {
337 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
338 .run = erst_exec_skip_next_instruction_if_true,
339 },
340 [ACPI_ERST_GOTO] = {
341 .flags = 0,
342 .run = erst_exec_goto,
343 },
344 [ACPI_ERST_SET_SRC_ADDRESS_BASE] = {
345 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
346 .run = erst_exec_set_src_address_base,
347 },
348 [ACPI_ERST_SET_DST_ADDRESS_BASE] = {
349 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
350 .run = erst_exec_set_dst_address_base,
351 },
352 [ACPI_ERST_MOVE_DATA] = {
353 .flags = APEI_EXEC_INS_ACCESS_REGISTER,
354 .run = erst_exec_move_data,
355 },
356};
357
358static inline void erst_exec_ctx_init(struct apei_exec_context *ctx)
359{
360 apei_exec_ctx_init(ctx, erst_ins_type, ARRAY_SIZE(erst_ins_type),
361 ERST_TAB_ENTRY(erst_tab), erst_tab->entries);
362}
363
364static int erst_get_erange(struct erst_erange *range)
365{
366 struct apei_exec_context ctx;
367 int rc;
368
369 erst_exec_ctx_init(&ctx);
370 rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_RANGE);
371 if (rc)
372 return rc;
373 range->base = apei_exec_ctx_get_output(&ctx);
374 rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_LENGTH);
375 if (rc)
376 return rc;
377 range->size = apei_exec_ctx_get_output(&ctx);
378 rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_ATTRIBUTES);
379 if (rc)
380 return rc;
381 range->attr = apei_exec_ctx_get_output(&ctx);
382
383 return 0;
384}
385
386static ssize_t __erst_get_record_count(void)
387{
388 struct apei_exec_context ctx;
389 int rc;
390
391 erst_exec_ctx_init(&ctx);
392 rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_COUNT);
393 if (rc)
394 return rc;
395 return apei_exec_ctx_get_output(&ctx);
396}
397
398ssize_t erst_get_record_count(void)
399{
400 ssize_t count;
401 unsigned long flags;
402
403 if (erst_disable)
404 return -ENODEV;
405
406 spin_lock_irqsave(&erst_lock, flags);
407 count = __erst_get_record_count();
408 spin_unlock_irqrestore(&erst_lock, flags);
409
410 return count;
411}
412EXPORT_SYMBOL_GPL(erst_get_record_count);
413
414static int __erst_get_next_record_id(u64 *record_id)
415{
416 struct apei_exec_context ctx;
417 int rc;
418
419 erst_exec_ctx_init(&ctx);
420 rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_ID);
421 if (rc)
422 return rc;
423 *record_id = apei_exec_ctx_get_output(&ctx);
424
425 return 0;
426}
427
428/*
429 * Get the record ID of an existing error record on the persistent
430 * storage. If there is no error record on the persistent storage, the
431 * returned record_id is APEI_ERST_INVALID_RECORD_ID.
432 */
433int erst_get_next_record_id(u64 *record_id)
434{
435 int rc;
436 unsigned long flags;
437
438 if (erst_disable)
439 return -ENODEV;
440
441 spin_lock_irqsave(&erst_lock, flags);
442 rc = __erst_get_next_record_id(record_id);
443 spin_unlock_irqrestore(&erst_lock, flags);
444
445 return rc;
446}
447EXPORT_SYMBOL_GPL(erst_get_next_record_id);
448
449static int __erst_write_to_storage(u64 offset)
450{
451 struct apei_exec_context ctx;
452 u64 timeout = FIRMWARE_TIMEOUT;
453 u64 val;
454 int rc;
455
456 erst_exec_ctx_init(&ctx);
457 rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_WRITE);
458 if (rc)
459 return rc;
460 apei_exec_ctx_set_input(&ctx, offset);
461 rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET);
462 if (rc)
463 return rc;
464 rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);
465 if (rc)
466 return rc;
467 for (;;) {
468 rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);
469 if (rc)
470 return rc;
471 val = apei_exec_ctx_get_output(&ctx);
472 if (!val)
473 break;
474 if (erst_timedout(&timeout, SPIN_UNIT))
475 return -EIO;
476 }
477 rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);
478 if (rc)
479 return rc;
480 val = apei_exec_ctx_get_output(&ctx);
481 rc = apei_exec_run(&ctx, ACPI_ERST_END);
482 if (rc)
483 return rc;
484
485 return erst_errno(val);
486}
487
488static int __erst_read_from_storage(u64 record_id, u64 offset)
489{
490 struct apei_exec_context ctx;
491 u64 timeout = FIRMWARE_TIMEOUT;
492 u64 val;
493 int rc;
494
495 erst_exec_ctx_init(&ctx);
496 rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_READ);
497 if (rc)
498 return rc;
499 apei_exec_ctx_set_input(&ctx, offset);
500 rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET);
501 if (rc)
502 return rc;
503 apei_exec_ctx_set_input(&ctx, record_id);
504 rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID);
505 if (rc)
506 return rc;
507 rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);
508 if (rc)
509 return rc;
510 for (;;) {
511 rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);
512 if (rc)
513 return rc;
514 val = apei_exec_ctx_get_output(&ctx);
515 if (!val)
516 break;
517 if (erst_timedout(&timeout, SPIN_UNIT))
518 return -EIO;
519 };
520 rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);
521 if (rc)
522 return rc;
523 val = apei_exec_ctx_get_output(&ctx);
524 rc = apei_exec_run(&ctx, ACPI_ERST_END);
525 if (rc)
526 return rc;
527
528 return erst_errno(val);
529}
530
531static int __erst_clear_from_storage(u64 record_id)
532{
533 struct apei_exec_context ctx;
534 u64 timeout = FIRMWARE_TIMEOUT;
535 u64 val;
536 int rc;
537
538 erst_exec_ctx_init(&ctx);
539 rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_CLEAR);
540 if (rc)
541 return rc;
542 apei_exec_ctx_set_input(&ctx, record_id);
543 rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID);
544 if (rc)
545 return rc;
546 rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);
547 if (rc)
548 return rc;
549 for (;;) {
550 rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);
551 if (rc)
552 return rc;
553 val = apei_exec_ctx_get_output(&ctx);
554 if (!val)
555 break;
556 if (erst_timedout(&timeout, SPIN_UNIT))
557 return -EIO;
558 }
559 rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);
560 if (rc)
561 return rc;
562 val = apei_exec_ctx_get_output(&ctx);
563 rc = apei_exec_run(&ctx, ACPI_ERST_END);
564 if (rc)
565 return rc;
566
567 return erst_errno(val);
568}
569
570/* NVRAM ERST Error Log Address Range is not supported yet */
571static void pr_unimpl_nvram(void)
572{
573 if (printk_ratelimit())
574 pr_warning(ERST_PFX
575 "NVRAM ERST Log Address Range is not implemented yet\n");
576}
577
578static int __erst_write_to_nvram(const struct cper_record_header *record)
579{
580 /* do not print message, because printk is not safe for NMI */
581 return -ENOSYS;
582}
583
584static int __erst_read_to_erange_from_nvram(u64 record_id, u64 *offset)
585{
586 pr_unimpl_nvram();
587 return -ENOSYS;
588}
589
590static int __erst_clear_from_nvram(u64 record_id)
591{
592 pr_unimpl_nvram();
593 return -ENOSYS;
594}
595
596int erst_write(const struct cper_record_header *record)
597{
598 int rc;
599 unsigned long flags;
600 struct cper_record_header *rcd_erange;
601
602 if (erst_disable)
603 return -ENODEV;
604
605 if (memcmp(record->signature, CPER_SIG_RECORD, CPER_SIG_SIZE))
606 return -EINVAL;
607
608 if (erst_erange.attr & ERST_RANGE_NVRAM) {
609 if (!spin_trylock_irqsave(&erst_lock, flags))
610 return -EBUSY;
611 rc = __erst_write_to_nvram(record);
612 spin_unlock_irqrestore(&erst_lock, flags);
613 return rc;
614 }
615
616 if (record->record_length > erst_erange.size)
617 return -EINVAL;
618
619 if (!spin_trylock_irqsave(&erst_lock, flags))
620 return -EBUSY;
621 memcpy(erst_erange.vaddr, record, record->record_length);
622 rcd_erange = erst_erange.vaddr;
623 /* signature for serialization system */
624 memcpy(&rcd_erange->persistence_information, "ER", 2);
625
626 rc = __erst_write_to_storage(0);
627 spin_unlock_irqrestore(&erst_lock, flags);
628
629 return rc;
630}
631EXPORT_SYMBOL_GPL(erst_write);
632
633static int __erst_read_to_erange(u64 record_id, u64 *offset)
634{
635 int rc;
636
637 if (erst_erange.attr & ERST_RANGE_NVRAM)
638 return __erst_read_to_erange_from_nvram(
639 record_id, offset);
640
641 rc = __erst_read_from_storage(record_id, 0);
642 if (rc)
643 return rc;
644 *offset = 0;
645
646 return 0;
647}
648
649static ssize_t __erst_read(u64 record_id, struct cper_record_header *record,
650 size_t buflen)
651{
652 int rc;
653 u64 offset, len = 0;
654 struct cper_record_header *rcd_tmp;
655
656 rc = __erst_read_to_erange(record_id, &offset);
657 if (rc)
658 return rc;
659 rcd_tmp = erst_erange.vaddr + offset;
660 len = rcd_tmp->record_length;
661 if (len <= buflen)
662 memcpy(record, rcd_tmp, len);
663
664 return len;
665}
666
667/*
668 * If return value > buflen, the buffer size is not big enough,
669 * else if return value < 0, something goes wrong,
670 * else everything is OK, and return value is record length
671 */
672ssize_t erst_read(u64 record_id, struct cper_record_header *record,
673 size_t buflen)
674{
675 ssize_t len;
676 unsigned long flags;
677
678 if (erst_disable)
679 return -ENODEV;
680
681 spin_lock_irqsave(&erst_lock, flags);
682 len = __erst_read(record_id, record, buflen);
683 spin_unlock_irqrestore(&erst_lock, flags);
684 return len;
685}
686EXPORT_SYMBOL_GPL(erst_read);
687
688/*
689 * If return value > buflen, the buffer size is not big enough,
690 * else if return value = 0, there is no more record to read,
691 * else if return value < 0, something goes wrong,
692 * else everything is OK, and return value is record length
693 */
694ssize_t erst_read_next(struct cper_record_header *record, size_t buflen)
695{
696 int rc;
697 ssize_t len;
698 unsigned long flags;
699 u64 record_id;
700
701 if (erst_disable)
702 return -ENODEV;
703
704 spin_lock_irqsave(&erst_lock, flags);
705 rc = __erst_get_next_record_id(&record_id);
706 if (rc) {
707 spin_unlock_irqrestore(&erst_lock, flags);
708 return rc;
709 }
710 /* no more record */
711 if (record_id == APEI_ERST_INVALID_RECORD_ID) {
712 spin_unlock_irqrestore(&erst_lock, flags);
713 return 0;
714 }
715
716 len = __erst_read(record_id, record, buflen);
717 spin_unlock_irqrestore(&erst_lock, flags);
718
719 return len;
720}
721EXPORT_SYMBOL_GPL(erst_read_next);
722
723int erst_clear(u64 record_id)
724{
725 int rc;
726 unsigned long flags;
727
728 if (erst_disable)
729 return -ENODEV;
730
731 spin_lock_irqsave(&erst_lock, flags);
732 if (erst_erange.attr & ERST_RANGE_NVRAM)
733 rc = __erst_clear_from_nvram(record_id);
734 else
735 rc = __erst_clear_from_storage(record_id);
736 spin_unlock_irqrestore(&erst_lock, flags);
737
738 return rc;
739}
740EXPORT_SYMBOL_GPL(erst_clear);
741
742static int __init setup_erst_disable(char *str)
743{
744 erst_disable = 1;
745 return 0;
746}
747
748__setup("erst_disable", setup_erst_disable);
749
750static int erst_check_table(struct acpi_table_erst *erst_tab)
751{
752 if (erst_tab->header_length != sizeof(struct acpi_table_erst))
753 return -EINVAL;
754 if (erst_tab->header.length < sizeof(struct acpi_table_erst))
755 return -EINVAL;
756 if (erst_tab->entries !=
757 (erst_tab->header.length - sizeof(struct acpi_table_erst)) /
758 sizeof(struct acpi_erst_entry))
759 return -EINVAL;
760
761 return 0;
762}
763
764static int __init erst_init(void)
765{
766 int rc = 0;
767 acpi_status status;
768 struct apei_exec_context ctx;
769 struct apei_resources erst_resources;
770 struct resource *r;
771
772 if (acpi_disabled)
773 goto err;
774
775 if (erst_disable) {
776 pr_info(ERST_PFX
777 "Error Record Serialization Table (ERST) support is disabled.\n");
778 goto err;
779 }
780
781 status = acpi_get_table(ACPI_SIG_ERST, 0,
782 (struct acpi_table_header **)&erst_tab);
783 if (status == AE_NOT_FOUND) {
784 pr_err(ERST_PFX "Table is not found!\n");
785 goto err;
786 } else if (ACPI_FAILURE(status)) {
787 const char *msg = acpi_format_exception(status);
788 pr_err(ERST_PFX "Failed to get table, %s\n", msg);
789 rc = -EINVAL;
790 goto err;
791 }
792
793 rc = erst_check_table(erst_tab);
794 if (rc) {
795 pr_err(FW_BUG ERST_PFX "ERST table is invalid\n");
796 goto err;
797 }
798
799 apei_resources_init(&erst_resources);
800 erst_exec_ctx_init(&ctx);
801 rc = apei_exec_collect_resources(&ctx, &erst_resources);
802 if (rc)
803 goto err_fini;
804 rc = apei_resources_request(&erst_resources, "APEI ERST");
805 if (rc)
806 goto err_fini;
807 rc = apei_exec_pre_map_gars(&ctx);
808 if (rc)
809 goto err_release;
810 rc = erst_get_erange(&erst_erange);
811 if (rc) {
812 if (rc == -ENODEV)
813 pr_info(ERST_PFX
814 "The corresponding hardware device or firmware implementation "
815 "is not available.\n");
816 else
817 pr_err(ERST_PFX
818 "Failed to get Error Log Address Range.\n");
819 goto err_unmap_reg;
820 }
821
822 r = request_mem_region(erst_erange.base, erst_erange.size, "APEI ERST");
823 if (!r) {
824 pr_err(ERST_PFX
825 "Can not request iomem region <0x%16llx-0x%16llx> for ERST.\n",
826 (unsigned long long)erst_erange.base,
827 (unsigned long long)erst_erange.base + erst_erange.size);
828 rc = -EIO;
829 goto err_unmap_reg;
830 }
831 rc = -ENOMEM;
832 erst_erange.vaddr = ioremap_cache(erst_erange.base,
833 erst_erange.size);
834 if (!erst_erange.vaddr)
835 goto err_release_erange;
836
837 pr_info(ERST_PFX
838 "Error Record Serialization Table (ERST) support is initialized.\n");
839
840 return 0;
841
842err_release_erange:
843 release_mem_region(erst_erange.base, erst_erange.size);
844err_unmap_reg:
845 apei_exec_post_unmap_gars(&ctx);
846err_release:
847 apei_resources_release(&erst_resources);
848err_fini:
849 apei_resources_fini(&erst_resources);
850err:
851 erst_disable = 1;
852 return rc;
853}
854
855device_initcall(erst_init);
diff --git a/include/acpi/apei.h b/include/acpi/apei.h
index 631a1ad2d108..b3365025ff8d 100644
--- a/include/acpi/apei.h
+++ b/include/acpi/apei.h
@@ -5,9 +5,30 @@
5#ifndef ACPI_APEI_H 5#ifndef ACPI_APEI_H
6#define ACPI_APEI_H 6#define ACPI_APEI_H
7 7
8#include <linux/acpi.h>
9#include <linux/cper.h>
10#include <asm/ioctls.h>
11
12#define APEI_ERST_INVALID_RECORD_ID 0xffffffffffffffffULL
13
14#define APEI_ERST_CLEAR_RECORD _IOW('E', 1, u64)
15#define APEI_ERST_GET_RECORD_COUNT _IOR('E', 2, u32)
16
17#ifdef __KERNEL__
18
8extern int hest_disable; 19extern int hest_disable;
20extern int erst_disable;
9 21
10typedef int (*apei_hest_func_t)(struct acpi_hest_header *hest_hdr, void *data); 22typedef int (*apei_hest_func_t)(struct acpi_hest_header *hest_hdr, void *data);
11int apei_hest_parse(apei_hest_func_t func, void *data); 23int apei_hest_parse(apei_hest_func_t func, void *data);
12 24
25int erst_write(const struct cper_record_header *record);
26ssize_t erst_get_record_count(void);
27int erst_get_next_record_id(u64 *record_id);
28ssize_t erst_read(u64 record_id, struct cper_record_header *record,
29 size_t buflen);
30ssize_t erst_read_next(struct cper_record_header *record, size_t buflen);
31int erst_clear(u64 record_id);
32
33#endif
13#endif 34#endif