Initial commit
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..700c821
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,34 @@
+CFLAGS := -g -Wall
+LDFLAGS += -lpthread
+
+OBJ := aer-inject.o util.o aer.tab.o lex.yy.o
+GENSRC := aer.tab.c aer.tab.h lex.yy.c
+SRC := aer-inject.c util.c
+CLEAN := ${OBJ} ${GENSRC} aer-inject .gdb_history .depend
+DISTCLEAN := .depend .gdb_history
+
+.PHONY: clean depend
+
+aer-inject: ${OBJ}
+
+lex.yy.c: aer.lex aer.tab.h
+	flex aer.lex
+
+aer.tab.c aer.tab.h: aer.y
+	bison -d aer.y
+
+clean:
+	rm -f ${CLEAN}
+
+distclean: clean
+	rm -f ${DISTCLEAN} *~
+
+depend: .depend
+
+.depend: ${SRC} ${GENSRC}
+	${CC} -MM -DDEPS_RUN -I. ${SRC} ${GENSRC} > .depend.X && \
+		mv .depend.X .depend
+
+Makefile: .depend
+
+include .depend
diff --git a/README b/README
new file mode 100644
index 0000000..44195f8
--- /dev/null
+++ b/README
@@ -0,0 +1,39 @@
+aer-inject allows to inject PCIE AER errors on the software level into
+a running Linux kernel. This is intended for validation of the PCIE
+driver error recovery handler and PCIE AER core handler.
+
+Syntax:
+
+aer-inject aer-file
+
+See SPEC for the input language
+
+Some simple tests are in test/*.
+
+Requires a new Linux kernel with PCIE AER error injection patches.
+The PCIE AER error injection patches are currently available from
+git://git.kernel.org/pub/scm/linux/kernel/git/xxx.git
+
+
+Authors:
+
+Huang Ying <ying.huang@intel.com>
+
+Basic design, some code and document are based on Andi Kleen's
+mce-inject.
+
+Copyright 2009 by Intel Corporation
+   aer-inject is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2.
+
+   aer-inject is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should find a copy of v2 of the GNU General Public License
+   somewhere on your Linux system; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+   USA
+
diff --git a/SPEC b/SPEC
new file mode 100644
index 0000000..00bade3
--- /dev/null
+++ b/SPEC
@@ -0,0 +1,52 @@
+
+aer-inject allows to inject PCIE AER errors in a running kernel.
+
+Has to run as root. /dev/aer_inject has to exist.
+
+aer-inject aer-file
+aer-inject < aer-file
+
+Requires a kernel with PCIE AER error injection support. The injection
+only happens on the software level and does not simulate full PCIE AER
+handling on the platform level.
+
+The PCIE AER error to be injected are described in an input language
+which reflects PCIE AER related registers quite straighforward.
+
+See the PCI Express Base Specification section 7.10 for details on
+PCIE AER related registers.
+
+The keywords are case-insensitive
+
+The error always starts with:
+
+AER
+
+The description of error follows:
+
+BUS number DEV number FN number
+
+These specify the PCI device or port via PCI bus number, dev number
+and function number
+
+COR_STATUS {RCVR|BAD_TLP|BAD_DLLP|REP_ROLL|REP_TIMER|number}
+
+UNCOR_STATUS {TRAIN|DLP|POISON_TLP|FCP|COMP_TIME|COMP_ABORT|UNX_COMP|
+	      RX_OVER|MALF_TLP|ECRC|UNSUP|number}
+
+HEADER_LOG number number number number
+
+These specify the corrected and uncorrected error types to be
+injected and corresponding TLP header log.
+
+multiple fields can be on a line.
+
+number can be hex/octal/decimal in the usual C format.
+
+Multiple errors can be in a single file, each new one starts with
+"AER".
+
+For all missing fields reasonable default values are filled in
+(hopefully).
+
+Comments start with # until the end of the line.
diff --git a/aer-inject.c b/aer-inject.c
new file mode 100644
index 0000000..1bcfc54
--- /dev/null
+++ b/aer-inject.c
@@ -0,0 +1,37 @@
+/*
+ * Inject PCIE AER error into Linux kernel for testing
+ *
+ * Copyright 2009 Intel Corporation.
+ *     Huang Ying <ying.huang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "aer.h"
+#include "util.h"
+
+#define AER_DEV "/dev/aer_inject"
+
+void init_aer(struct aer_error_inj *aerr)
+{
+	memset(aerr, 0, sizeof(struct aer_error_inj));
+}
+
+void submit_aer(struct aer_error_inj *err)
+{
+	int fd, ret;
+
+	fd = open(AER_DEV, O_WRONLY);
+	ERROR_EXIT_ON(fd <= 0, "Failed to open device file: %s", AER_DEV);
+	ret = write(fd, err, sizeof(struct aer_error_inj));
+	ERROR_EXIT_ON(ret != sizeof(struct aer_error_inj), "Failed to write");
+	close(fd);
+}
diff --git a/aer.h b/aer.h
new file mode 100644
index 0000000..2061a93
--- /dev/null
+++ b/aer.h
@@ -0,0 +1,43 @@
+#ifndef AER_H
+#define AER_H
+
+struct aer_error_inj
+{
+	int8_t bus;
+	int8_t dev;
+	int8_t fn;
+	int32_t uncor_status;
+	int32_t cor_status;
+	int32_t header_log0;
+	int32_t header_log1;
+	int32_t header_log2;
+	int32_t header_log3;
+};
+
+#define  PCI_ERR_UNC_TRAIN	0x00000001	/* Training */
+#define  PCI_ERR_UNC_DLP	0x00000010	/* Data Link Protocol */
+#define  PCI_ERR_UNC_POISON_TLP	0x00001000	/* Poisoned TLP */
+#define  PCI_ERR_UNC_FCP	0x00002000	/* Flow Control Protocol */
+#define  PCI_ERR_UNC_COMP_TIME	0x00004000	/* Completion Timeout */
+#define  PCI_ERR_UNC_COMP_ABORT	0x00008000	/* Completer Abort */
+#define  PCI_ERR_UNC_UNX_COMP	0x00010000	/* Unexpected Completion */
+#define  PCI_ERR_UNC_RX_OVER	0x00020000	/* Receiver Overflow */
+#define  PCI_ERR_UNC_MALF_TLP	0x00040000	/* Malformed TLP */
+#define  PCI_ERR_UNC_ECRC	0x00080000	/* ECRC Error Status */
+#define  PCI_ERR_UNC_UNSUP	0x00100000	/* Unsupported Request */
+#define  PCI_ERR_COR_RCVR	0x00000001	/* Receiver Error Status */
+#define  PCI_ERR_COR_BAD_TLP	0x00000040	/* Bad TLP Status */
+#define  PCI_ERR_COR_BAD_DLLP	0x00000080	/* Bad DLLP Status */
+#define  PCI_ERR_COR_REP_ROLL	0x00000100	/* REPLAY_NUM Rollover */
+#define  PCI_ERR_COR_REP_TIMER	0x00001000	/* Replay Timer Timeout */
+
+extern void init_aer(struct aer_error_inj *err);
+extern void submit_aer(struct aer_error_inj *err);
+
+extern char *filename;
+extern int yylineno;
+extern void yyerror(char const *msg, ...);
+extern int yylex(void);
+extern int yyparse(void);
+
+#endif
diff --git a/aer.lex b/aer.lex
new file mode 100644
index 0000000..b3c6a30
--- /dev/null
+++ b/aer.lex
@@ -0,0 +1,126 @@
+/*
+ * Scanner for the PCIE-AER grammar.
+ *
+ * Copyright (c) 2009 by Intel Corp.
+ *   Author: Huang Ying <ying.huang@intel.com>
+ *
+ * Based on mce.lex of mce-inject, which is written by Andi Kleen
+ * <andi.kleen@intel.com>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ */
+
+%{
+#define _GNU_SOURCE 1
+#include <stdlib.h>
+#include <string.h>
+
+#include "aer.h"
+#include "aer.tab.h"
+#include "util.h"
+
+int yylineno;
+
+static int lookup_symbol(const char *);
+%}
+
+%option nounput
+
+%%
+
+#.*\n			/* comment */;
+\n			++yylineno;
+0x[0-9a-fA-F]+ 		|
+0[0-7]+			|
+[0-9]+			yylval = strtoull(yytext, NULL, 0); return NUMBER;
+[:{}<>]			return yytext[0];
+[_a-zA-Z][_a-zA-Z0-9]*	return lookup_symbol(yytext);
+[ \t]+			/* white space */;
+.			yyerror("Unrecognized character '%s'", yytext);
+
+%%
+
+/* Keyword handling */
+
+static struct key {
+	const char *name;
+	int tok;
+	int32_t val;
+} keys[] = {
+#define KEY(x) { #x, x }
+#define KEYVAL(x,v) { #x, x, v }
+	KEY(AER),
+	KEY(BUS),
+	KEY(DEV),
+	KEY(FN),
+	KEY(UNCOR_STATUS),
+	KEY(COR_STATUS),
+	KEY(HEADER_LOG),
+	KEYVAL(TRAIN,PCI_ERR_UNC_TRAIN),
+	KEYVAL(DLP, PCI_ERR_UNC_DLP),
+	KEYVAL(POISON_TLP, PCI_ERR_UNC_POISON_TLP),
+	KEYVAL(FCP, PCI_ERR_UNC_FCP),
+	KEYVAL(COMP_TIME, PCI_ERR_UNC_COMP_TIME),
+	KEYVAL(COMP_ABORT, PCI_ERR_UNC_COMP_ABORT),
+	KEYVAL(UNX_COMP, PCI_ERR_UNC_UNX_COMP),
+	KEYVAL(RX_OVER, PCI_ERR_UNC_RX_OVER),
+	KEYVAL(MALF_TLP, PCI_ERR_UNC_MALF_TLP),
+	KEYVAL(ECRC, PCI_ERR_UNC_ECRC),
+	KEYVAL(UNSUP, PCI_ERR_UNC_UNSUP),
+	KEYVAL(RCVR, PCI_ERR_COR_RCVR),
+	KEYVAL(BAD_TLP, PCI_ERR_COR_BAD_TLP),
+	KEYVAL(BAD_DLLP, PCI_ERR_COR_BAD_DLLP),
+	KEYVAL(REP_ROLL, PCI_ERR_COR_REP_ROLL),
+	KEYVAL(REP_TIMER, PCI_ERR_COR_REP_TIMER),
+};
+
+static int cmp_key(const void *av, const void *bv)
+{
+	const struct key *a = av;
+	const struct key *b = bv;
+	return strcasecmp(a->name, b->name);
+}
+
+static int lookup_symbol(const char *name)
+{
+	struct key *k;
+	struct key key;
+	key.name = name;
+	k = bsearch(&key, keys, ARRAY_SIZE(keys), sizeof(struct key), cmp_key);
+	if (k != NULL) {
+		yylval = k->val;
+		return k->tok;
+	}
+	return SYMBOL;
+}
+
+static void init_lex(void)
+{
+	qsort(keys, ARRAY_SIZE(keys), sizeof(struct key), cmp_key);
+}
+
+static char **argv;
+char *filename = "<stdin>";
+
+int yywrap(void)
+{
+	if (*argv == NULL)
+		return 1;
+	filename = *argv;
+	yyin = fopen(filename, "r");
+	ERROR_EXIT_ON(!yyin, "Can not open: %s", filename);
+	argv++;
+	return 0;
+}
+
+int main(int ac, char **av)
+{
+	init_lex();
+	argv = ++av;
+	if (*argv)
+		yywrap();
+	return yyparse();
+}
diff --git a/aer.y b/aer.y
new file mode 100644
index 0000000..80b002e
--- /dev/null
+++ b/aer.y
@@ -0,0 +1,91 @@
+/*
+ * Grammar for the PCIE-AER injection.
+ *
+ * Copyright (c) 2009 by Intel Corp.
+ *   Author: Huang Ying <ying.huang@intel.com>
+ *
+ * Based on mce.y of mce-inject, which is written by Andi Kleen
+ * <andi.kleen@intel.com>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ */
+
+%{
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "aer.h"
+
+static struct aer_error_inj aerr;
+
+static void init(void);
+
+%}
+
+%token AER BUS DEV FN UNCOR_STATUS COR_STATUS HEADER_LOG
+%token TRAIN DLP POISON_TLP FCP COMP_TIME COMP_ABORT UNX_COMP RX_OVER MALF_TLP
+%token ECRC UNSUP
+%token RCVR BAD_TLP BAD_DLLP REP_ROLL REP_TIMER
+%token NUMBER SYMBOL
+
+%%
+
+input: /* empty */
+	| input aer_start aer { submit_aer(&aerr); }
+	;
+
+aer_start: AER		{ init(); }
+	;
+
+aer: aer_term
+	| aer aer_term
+	;
+
+aer_term: UNCOR_STATUS uncor_status_list	{ aerr.uncor_status = $2; }
+	| COR_STATUS cor_status_list		{ aerr.cor_status = $2; }
+	| BUS NUMBER DEV NUMBER FN NUMBER	{ aerr.bus = $2;
+						  aerr.dev = $4;
+						  aerr.fn = $6; }
+	| HEADER_LOG NUMBER NUMBER NUMBER NUMBER { aerr.header_log0 = $2;
+						   aerr.header_log1 = $3;
+						   aerr.header_log2 = $4;
+						   aerr.header_log3 = $5; }
+	;
+
+uncor_status_list: /* empty */			{ $$ = 0; }
+	| uncor_status_list uncor_status	{ $$ = $1 | $2; }
+	;
+
+uncor_status: TRAIN | DLP | POISON_TLP | FCP | COMP_TIME | COMP_ABORT
+	| UNX_COMP | RX_OVER | MALF_TLP | ECRC | UNSUP | NUMBER
+	;
+
+cor_status_list: /* empty */			{ $$ = 0; }
+	| cor_status_list cor_status		{ $$ = $1 | $2; }
+	;
+
+cor_status: RCVR | BAD_TLP | BAD_DLLP | REP_ROLL | REP_TIMER | NUMBER
+	;
+
+%% 
+
+static void init(void)
+{
+	init_aer(&aerr);
+}
+
+void yyerror(char const *msg, ...)
+{
+	va_list ap;
+	va_start(ap, msg);
+	fprintf(stderr, "%s:%d: ", filename, yylineno);
+	vfprintf(stderr, msg, ap);
+	fputc('\n', stderr);
+	va_end(ap);
+	exit(1);
+}
diff --git a/test/aer1 b/test/aer1
new file mode 100644
index 0000000..c960175
--- /dev/null
+++ b/test/aer1
@@ -0,0 +1,4 @@
+AER
+BUS 0 DEV 0 FN 2
+COR_STATUS BAD_TLP
+HEADER_LOG 0 1 2 3
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..b5d80da
--- /dev/null
+++ b/util.c
@@ -0,0 +1,34 @@
+/*
+ * Some utility functions
+ *
+ * Copyright (C) Intel Corp., 2009
+ *     Author: Huang Ying <ying.huang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "util.h"
+
+void error_exit(char *fmt, ...)
+{
+	va_list ap;
+
+	fprintf(stderr, "Error: ");
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	if (errno)
+		fprintf(stderr, ", %s\n", strerror(errno));
+	else
+		fprintf(stderr, "\n");
+	exit(-1);
+}
+
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..9ed7cd9
--- /dev/null
+++ b/util.h
@@ -0,0 +1,19 @@
+#ifndef UTIL_H
+#define UTIL_H
+
+void error_exit(char *fmt, ...);
+
+#define ERROR_EXIT(fmt, x...)					\
+	do {							\
+		error_exit(fmt, ## x);				\
+	} while (0)
+
+#define ERROR_EXIT_ON(check, fmt, x...)				\
+	do {							\
+		if (check)					\
+			error_exit(fmt, ## x);			\
+	} while (0)
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*(x)))
+
+#endif