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
54void 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
70void 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
132int 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
158void 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
168void 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
177enum {
178	ERST_INJECT,
179	ERST_POLL,
180	ERST_CLEAR,
181	ERST_COUNT,
182	ERST_MAX = 255
183};
184
185void 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
199int 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