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