aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/apei/erst.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/apei/erst.c')
-rw-r--r--drivers/acpi/apei/erst.c136
1 files changed, 136 insertions, 0 deletions
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
index cf6db6b7662a..c02005abce43 100644
--- a/drivers/acpi/apei/erst.c
+++ b/drivers/acpi/apei/erst.c
@@ -34,6 +34,7 @@
34#include <linux/cper.h> 34#include <linux/cper.h>
35#include <linux/nmi.h> 35#include <linux/nmi.h>
36#include <linux/hardirq.h> 36#include <linux/hardirq.h>
37#include <linux/pstore.h>
37#include <acpi/apei.h> 38#include <acpi/apei.h>
38 39
39#include "apei-internal.h" 40#include "apei-internal.h"
@@ -781,6 +782,128 @@ static int erst_check_table(struct acpi_table_erst *erst_tab)
781 return 0; 782 return 0;
782} 783}
783 784
785static size_t erst_reader(u64 *id, enum pstore_type_id *type,
786 struct timespec *time);
787static u64 erst_writer(enum pstore_type_id type, size_t size);
788
789static struct pstore_info erst_info = {
790 .owner = THIS_MODULE,
791 .name = "erst",
792 .read = erst_reader,
793 .write = erst_writer,
794 .erase = erst_clear
795};
796
797#define CPER_CREATOR_PSTORE \
798 UUID_LE(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c, \
799 0x64, 0x90, 0xb8, 0x9d)
800#define CPER_SECTION_TYPE_DMESG \
801 UUID_LE(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54, \
802 0x94, 0x19, 0xeb, 0x12)
803#define CPER_SECTION_TYPE_MCE \
804 UUID_LE(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96, \
805 0x04, 0x4a, 0x38, 0xfc)
806
807struct cper_pstore_record {
808 struct cper_record_header hdr;
809 struct cper_section_descriptor sec_hdr;
810 char data[];
811} __packed;
812
813static size_t erst_reader(u64 *id, enum pstore_type_id *type,
814 struct timespec *time)
815{
816 int rc;
817 ssize_t len;
818 unsigned long flags;
819 u64 record_id;
820 struct cper_pstore_record *rcd = (struct cper_pstore_record *)
821 (erst_info.buf - sizeof(*rcd));
822
823 if (erst_disable)
824 return -ENODEV;
825
826 raw_spin_lock_irqsave(&erst_lock, flags);
827skip:
828 rc = __erst_get_next_record_id(&record_id);
829 if (rc) {
830 raw_spin_unlock_irqrestore(&erst_lock, flags);
831 return rc;
832 }
833 /* no more record */
834 if (record_id == APEI_ERST_INVALID_RECORD_ID) {
835 raw_spin_unlock_irqrestore(&erst_lock, flags);
836 return 0;
837 }
838
839 len = __erst_read(record_id, &rcd->hdr, sizeof(*rcd) +
840 erst_erange.size);
841 if (uuid_le_cmp(rcd->hdr.creator_id, CPER_CREATOR_PSTORE) != 0)
842 goto skip;
843 raw_spin_unlock_irqrestore(&erst_lock, flags);
844
845 *id = record_id;
846 if (uuid_le_cmp(rcd->sec_hdr.section_type,
847 CPER_SECTION_TYPE_DMESG) == 0)
848 *type = PSTORE_TYPE_DMESG;
849 else if (uuid_le_cmp(rcd->sec_hdr.section_type,
850 CPER_SECTION_TYPE_MCE) == 0)
851 *type = PSTORE_TYPE_MCE;
852 else
853 *type = PSTORE_TYPE_UNKNOWN;
854
855 if (rcd->hdr.validation_bits & CPER_VALID_TIMESTAMP)
856 time->tv_sec = rcd->hdr.timestamp;
857 else
858 time->tv_sec = 0;
859 time->tv_nsec = 0;
860
861 return len - sizeof(*rcd);
862}
863
864static u64 erst_writer(enum pstore_type_id type, size_t size)
865{
866 struct cper_pstore_record *rcd = (struct cper_pstore_record *)
867 (erst_info.buf - sizeof(*rcd));
868
869 memset(rcd, 0, sizeof(*rcd));
870 memcpy(rcd->hdr.signature, CPER_SIG_RECORD, CPER_SIG_SIZE);
871 rcd->hdr.revision = CPER_RECORD_REV;
872 rcd->hdr.signature_end = CPER_SIG_END;
873 rcd->hdr.section_count = 1;
874 rcd->hdr.error_severity = CPER_SEV_FATAL;
875 /* timestamp valid. platform_id, partition_id are invalid */
876 rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP;
877 rcd->hdr.timestamp = get_seconds();
878 rcd->hdr.record_length = sizeof(*rcd) + size;
879 rcd->hdr.creator_id = CPER_CREATOR_PSTORE;
880 rcd->hdr.notification_type = CPER_NOTIFY_MCE;
881 rcd->hdr.record_id = cper_next_record_id();
882 rcd->hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR;
883
884 rcd->sec_hdr.section_offset = sizeof(*rcd);
885 rcd->sec_hdr.section_length = size;
886 rcd->sec_hdr.revision = CPER_SEC_REV;
887 /* fru_id and fru_text is invalid */
888 rcd->sec_hdr.validation_bits = 0;
889 rcd->sec_hdr.flags = CPER_SEC_PRIMARY;
890 switch (type) {
891 case PSTORE_TYPE_DMESG:
892 rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG;
893 break;
894 case PSTORE_TYPE_MCE:
895 rcd->sec_hdr.section_type = CPER_SECTION_TYPE_MCE;
896 break;
897 default:
898 return -EINVAL;
899 }
900 rcd->sec_hdr.section_severity = CPER_SEV_FATAL;
901
902 erst_write(&rcd->hdr);
903
904 return rcd->hdr.record_id;
905}
906
784static int __init erst_init(void) 907static int __init erst_init(void)
785{ 908{
786 int rc = 0; 909 int rc = 0;
@@ -788,6 +911,7 @@ static int __init erst_init(void)
788 struct apei_exec_context ctx; 911 struct apei_exec_context ctx;
789 struct apei_resources erst_resources; 912 struct apei_resources erst_resources;
790 struct resource *r; 913 struct resource *r;
914 char *buf;
791 915
792 if (acpi_disabled) 916 if (acpi_disabled)
793 goto err; 917 goto err;
@@ -854,6 +978,18 @@ static int __init erst_init(void)
854 if (!erst_erange.vaddr) 978 if (!erst_erange.vaddr)
855 goto err_release_erange; 979 goto err_release_erange;
856 980
981 buf = kmalloc(erst_erange.size, GFP_KERNEL);
982 mutex_init(&erst_info.buf_mutex);
983 if (buf) {
984 erst_info.buf = buf + sizeof(struct cper_pstore_record);
985 erst_info.bufsize = erst_erange.size -
986 sizeof(struct cper_pstore_record);
987 if (pstore_register(&erst_info)) {
988 pr_info(ERST_PFX "Could not register with persistent store\n");
989 kfree(buf);
990 }
991 }
992
857 pr_info(ERST_PFX 993 pr_info(ERST_PFX
858 "Error Record Serialization Table (ERST) support is initialized.\n"); 994 "Error Record Serialization Table (ERST) support is initialized.\n");
859 995