1 /*
2 * Error Record Serialization Table(ERST) is used to save and retrieve hardware
3 * error information to and from a persistent store, such as flash or NVRAM.
4 *
5 * This test case is used to test ERST operation including read/write/clean.
6 * To be sure of loading erst-dbg module before executing this test.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public
10 * License as published by the Free Software Foundation; version 2.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should find a copy of v2 of the GNU General Public License somewhere
18 * on your Linux system; if not, write to the Free Software Foundation,
19 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 * Copyright (C) 2011, Intel Corp.
22 * Author: Chen Gong <gong.chen@intel.com>
23 *
24 * Original written by Huang Ying <ying.huang@intel.com>
25 * Updated by Chen Gong <gong.chen@intel.com>
26 *
27 */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stdarg.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <sys/ioctl.h>
36
37 #include "cper.h"
38
39 #define ERST_DEV "/dev/erst_dbg"
40
41 #define APEI_ERST_CLEAR_RECORD _IOW('E', 1, u64)
42 #define APEI_ERST_GET_RECORD_COUNT _IOR('E', 2, u32)
43
44 #define CPER_CREATOR_LINUX \
45 LGUID(0x94DB0E05, 0xEE60, 0x42D8, 0x91, 0xA5, 0xC6, 0xC0, \
46 0x02, 0x41, 0x6C, 0x6A)
47
48 #define ERROR_EXIT_ON(check, fmt, x...) \
49 do { \
50 if (check) \
51 error_exit(fmt, ## x); \
52 } while (0)
53
error_exit(char * fmt,...)54 void error_exit(char *fmt, ...)
55 {
56 va_list ap;
57
58 fprintf(stderr, "Error: ");
59 va_start(ap, fmt);
60 vfprintf(stderr, fmt, ap);
61 va_end(ap);
62
63 if (errno)
64 fprintf(stderr, ", errno: %d (%s)\n", errno, strerror(errno));
65 else
66 fprintf(stderr, "\n");
67 exit(-1);
68 }
69
inject(int fd,u64 record_id)70 void inject(int fd, u64 record_id)
71 {
72 int rc;
73 unsigned int len;
74 struct cper_record_header *rcd_hdr;
75 struct cper_section_descriptor *sec_hdr;
76 struct cper_sec_mem_err *mem_err;
77
78 len = sizeof(*rcd_hdr) + sizeof(*sec_hdr) + sizeof(*mem_err);
79 printf("sizes: %lu, %lu, %lu\n", sizeof(*rcd_hdr), sizeof(*sec_hdr),
80 sizeof(*mem_err));
81 rcd_hdr = malloc(len);
82 ERROR_EXIT_ON(!rcd_hdr, "Can not alloc mem");
83
84 #define LE 0
85
86 sec_hdr = (void *)(rcd_hdr + 1);
87 mem_err = (void *)(sec_hdr + 1);
88
89 memset(rcd_hdr, 0, sizeof(*rcd_hdr));
90 #if 0
91 memcpy(rcd_hdr->signature, "REPC", 4);
92 #else
93 memcpy(rcd_hdr->signature, "CPER", 4);
94 #endif
95 rcd_hdr->revision = 0x0100;
96 rcd_hdr->signature_end = 0xffffffff;
97 rcd_hdr->error_severity = CPER_SER_FATAL;
98 rcd_hdr->validation_bits = 0;
99 rcd_hdr->creator_id = CPER_CREATOR_LINUX;
100 rcd_hdr->notification_type = CPER_NOTIFY_NMI;
101 rcd_hdr->section_count = 1;
102 rcd_hdr->record_length = len;
103 rcd_hdr->record_id = record_id;
104 #if LE
105 memcpy(&rcd_hdr->persistence_information, "RE", 2);
106 #else
107 memcpy(&rcd_hdr->persistence_information, "ER", 2);
108 #endif
109
110 memset(sec_hdr, 0, sizeof(*sec_hdr));
111 sec_hdr->section_offset = (void *)mem_err - (void *)rcd_hdr;
112 sec_hdr->section_length = sizeof(*mem_err);
113 sec_hdr->revision = 0x0100;
114 sec_hdr->validation_bits = 0;
115 sec_hdr->flags = 0;
116 sec_hdr->section_type = CPER_SEC_PLATFORM_MEM;
117 sec_hdr->section_severity = CPER_SER_FATAL;
118
119 memset(mem_err, 0, sizeof(*mem_err));
120 mem_err->validation_bits = 0x6;
121 mem_err->physical_addr = 0x2000;
122 mem_err->physical_addr_mask = ~0xfffULL;
123
124 rc = write(fd, rcd_hdr, len);
125 ERROR_EXIT_ON(rc != len, "Error inject: %d", rc);
126
127 free(rcd_hdr);
128 }
129
130 #define POLL_BUF_SIZ (1024 * 1024)
131
poll(int fd)132 int poll(int fd)
133 {
134 int rc;
135 struct cper_record_header *rcd_hdr;
136 struct cper_section_descriptor *sec_hdr;
137 struct cper_sec_mem_err *mem_err;
138
139 rcd_hdr = malloc(POLL_BUF_SIZ);
140 ERROR_EXIT_ON(!rcd_hdr, "Can not alloc mem");
141
142 rc = read(fd, rcd_hdr, POLL_BUF_SIZ);
143 ERROR_EXIT_ON(rc < 0, "Error poll: %d", rc);
144
145 sec_hdr = (void *)(rcd_hdr + 1);
146 mem_err = (void *)(sec_hdr + 1);
147
148 printf("rc: %d\n", rc);
149
150 printf("rcd sig: %4s\n", rcd_hdr->signature);
151 printf("rcd id: 0x%llx\n", rcd_hdr->record_id);
152
153 free(rcd_hdr);
154
155 return rc;
156 }
157
clear(int fd,u64 record_id)158 void clear(int fd, u64 record_id)
159 {
160 int rc;
161
162 printf("clear an error record: id = 0x%llx\n", record_id);
163
164 rc = ioctl(fd, APEI_ERST_CLEAR_RECORD, &record_id);
165 ERROR_EXIT_ON(rc, "Error clear: %d", rc);
166 }
167
get_record_count(int fd,u32 * record_count)168 void get_record_count(int fd, u32 *record_count)
169 {
170 int rc;
171 rc = ioctl(fd, APEI_ERST_GET_RECORD_COUNT, record_count);
172 ERROR_EXIT_ON(rc, "Error get record count: %d", rc);
173
174 printf("total error record count: %u\n", *record_count);
175 }
176
177 enum {
178 ERST_INJECT,
179 ERST_POLL,
180 ERST_CLEAR,
181 ERST_COUNT,
182 ERST_MAX = 255
183 };
184
usage()185 void usage()
186 {
187 printf("Usage: ./erst-inject [option] <id>\n");
188 printf("PAY ATTENTION, <id> is hexadecimal.\n");
189 printf("\tp\treturn all error records in the ERST\n");
190 printf("\ti\twrite an error record to be persisted into one item with <id>\n");
191 printf("\tc\tclean specific error record with <id>\n");
192 printf("\tn\treturn error records count in the ERST\n");
193 printf("\nExample:\t ./erst-inject -p\n");
194 printf("\t\t ./erst-inject -i 0x1234567\n");
195 printf("\t\t ./erst-inject -c 5050\n");
196 printf("\t\t ./erst-inject -n\n");
197 }
198
main(int argc,char * argv[])199 int main(int argc, char *argv[])
200 {
201 int fd;
202 int todo = ERST_MAX;
203 int opt;
204 u64 record_id = 0x12345678;
205 u32 record_count;
206
207 if (argc == 1) {
208 usage();
209 exit(0);
210 }
211
212 while ((opt = getopt(argc, argv, "pi:c:n")) != -1) {
213 switch (opt) {
214 case 'p':
215 todo = ERST_POLL;
216 break;
217 case 'i':
218 todo = ERST_INJECT;
219 record_id = strtoull(optarg, NULL, 16);
220 break;
221 case 'c':
222 todo = ERST_CLEAR;
223 record_id = strtoull(optarg, NULL, 16);
224 break;
225 case 'n':
226 todo = ERST_COUNT;
227 break;
228 }
229 }
230
231 fd = open(ERST_DEV, O_RDWR);
232 ERROR_EXIT_ON(fd < 0, "Can not open dev file");
233
234 switch (todo) {
235 case ERST_INJECT:
236 inject(fd, record_id);
237 break;
238 case ERST_POLL:
239 while (poll(fd));
240 break;
241 case ERST_CLEAR:
242 clear(fd, record_id);
243 break;
244 case ERST_COUNT:
245 get_record_count(fd, &record_count);
246 break;
247 case ERST_MAX:
248 usage();
249 break;
250 }
251
252 close(fd);
253
254 return 0;
255 }
256