diff mbox series

[v2,03/10] opal/errorlog: Add support to include callout section in error log.

Message ID 160207254747.2097386.5086237208543320593.stgit@jupiter
State New
Headers show
Series Add support to report EEH errors to BMC/FSP (eSEL) | expand

Checks

Context Check Description
snowpatch_ozlabs/apply_patch success Successfully applied on branch master (f901fcafae14d38e29f1cc11440086ee678785d0)
snowpatch_ozlabs/snowpatch_job_snowpatch-skiboot success Test snowpatch/job/snowpatch-skiboot on branch master
snowpatch_ozlabs/snowpatch_job_snowpatch-skiboot-dco success Signed-off-by present

Commit Message

Mahesh Salgaonkar Oct. 7, 2020, 12:09 p.m. UTC
From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>

Allow inclusion of HW callout to eSEL being generated. This will help OPAL
to generate errorlog with fru callout section that includes location code,
part number and serial number of a faulty hardware that may need replacement.

Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
---
Change in v2:
- Avoid use of bitfields in opal_fru_callout structure.
---
 core/errorlog.c    |   49 +++++++++++++++++
 core/pel.c         |  152 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/errorlog.h |   17 ++++++
 include/pel.h      |   71 ++++++++++++++++++++++++
 4 files changed, 287 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/core/errorlog.c b/core/errorlog.c
index 2f7bcce19..0ea248b39 100644
--- a/core/errorlog.c
+++ b/core/errorlog.c
@@ -105,6 +105,55 @@  void log_add_section(struct errorlog *buf, uint32_t tag)
 	buf->user_section_count++;
 }
 
+void log_add_callout_section(struct errorlog *buf, const char *loc_code,
+				const char *part_no, const char *serial_no)
+{
+	int index;
+	struct elog_fru_callout *fru;
+
+	if (!buf) {
+		prerror("ELOG: Cannot add callout section. "
+			"Buffer is invalid\n");
+		return;
+	}
+	index = buf->num_fru_callout;
+	fru = &buf->fru_callout[index];
+
+	if (buf->num_fru_callout >= PEL_MAX_FRU_CALL_OUTS) {
+		prerror("ELOG: Cannot add callout section. "
+			"Maximum limit reached\n");
+		return;
+	}
+
+	if (!loc_code) {
+		prerror("ELOG: Cannot add callout section. "
+			"location code is not specified\n");
+		return;
+	}
+
+	/* Location codes  -- at most 80 chars with null termination */
+	if (strlen(loc_code) >= LOC_CODE_SIZE) {
+		prerror("ELOG: Invalid Size of location code.\n");
+		return;
+	}
+
+	buf->num_fru_callout++;
+	memset(fru->loc_code, 0, LOC_CODE_SIZE);
+	strcpy(fru->loc_code, loc_code);
+
+	/*
+	 * Part number contains a null-terminated ASCII character string.
+	 *	7 ASCII character part number
+	 */
+	if (part_no) {
+		memcpy(fru->part_no, part_no, PEL_FRU_PART_LEN);
+		fru->part_no[7] = '\0';
+	}
+
+	if (serial_no)
+		memcpy(fru->serial_no, serial_no, PEL_FRU_SERIAL_LEN);
+}
+
 void opal_elog_complete(struct errorlog *buf, bool success)
 {
 	if (!success)
diff --git a/core/pel.c b/core/pel.c
index 3a200238e..1be1ab990 100644
--- a/core/pel.c
+++ b/core/pel.c
@@ -107,10 +107,112 @@  static void setrefcode(struct pel_src_section *src, uint16_t src_refcode)
 	memcpy(src->srcstring+4, refcode, 4);
 }
 
+static int create_fru_identity(struct elog_fru_callout *elog_fru,
+					char *pel_buffer, int pel_offset)
+{
+	int data_len = 0;
+	struct pel_fru_identity *identity;
+
+	identity = (struct pel_fru_identity *)(pel_buffer + pel_offset);
+
+	if (!strlen(elog_fru->part_no) && !strlen(elog_fru->serial_no))
+		return 0;
+
+	identity->id = PEL_FRU_IDENT_ID;
+	identity->flags = PEL_FRU_IDENT_FLAG_NORMAL;
+
+	if (strlen(elog_fru->part_no)) {
+		identity->flags |= PEL_FRU_IDENT_FLAG_PART;
+		memset(identity->fru_data, 0, PEL_FRU_PART_LEN);
+		memcpy(identity->fru_data, elog_fru->part_no,
+							PEL_FRU_PART_LEN);
+		data_len = PEL_FRU_PART_LEN;
+	}
+
+	if (strlen(elog_fru->serial_no)) {
+		identity->flags |= PEL_FRU_IDENT_FLAG_SERIAL;
+		memset(identity->fru_data + data_len, 0, PEL_FRU_SERIAL_LEN);
+		memcpy(identity->fru_data + data_len, elog_fru->serial_no,
+							PEL_FRU_SERIAL_LEN);
+		data_len += PEL_FRU_SERIAL_LEN;
+	}
+	identity->length = FRU_IDENTITY_HEADER_SIZE + data_len;
+
+	return identity->length;
+}
+
+/*
+ * The FRU Call-out section is added as additional/optional sub section.
+ * Each additional sub section starts with sub section header followed by
+ * data. The sub section header contains section id, flags and size of the
+ * sub section including header size. A variable number of FRU Call-outs are
+ * possible up to the maximum of 10. Each FRU Callout structure starts on a
+ * word boundary.
+ */
+static int create_fru_callout_section(struct errorlog *elog_data,
+					char *pel_buffer, int pel_offset)
+{
+	int len;
+	struct pel_fru_callout_section *fru;
+	struct pel_fru_callout *fru_callout;
+	int i, fru_count;
+
+	fru = (struct pel_fru_callout_section *)(pel_buffer + pel_offset);
+	fru->header.id = PEL_SRC_SUB_SECTION_ID;
+	fru->header.flags = 0;
+	/*
+	 * Initialize SRC sub section size with sub section header size.
+	 * SRC sub section size is expressed in # of words (4 byte fields)
+	 * SRC sub section size will be updated for each FRU call out.
+	 */
+	fru->header.length += SRC_SUBSECTION_HEADER_SIZE / 4;
+	pel_offset += SRC_SUBSECTION_HEADER_SIZE;
+	fru_callout = (struct pel_fru_callout *)(pel_buffer + pel_offset);
+
+	/* Add FRU Call-outs */
+	fru_count = MIN(PEL_MAX_FRU_CALL_OUTS, elog_data->num_fru_callout);
+	for (i = 0; i < fru_count; i++) {
+		struct elog_fru_callout *elog_fru = &elog_data->fru_callout[i];
+
+		/* Set FRU callout type */
+		fru_callout->type_flags |= PEL_FRU_TYPE_NORMAL;
+		fru_callout->priority = PEL_FRU_PRIO_MEDIUM;
+		/* Size of loc-code including NULL terminator. */
+		len = strlen(elog_fru->loc_code) + 1;
+
+		/* Length of Location Code field - must be a multiple of 4. */
+		len = ALIGN_UP(len, 4);
+		memcpy(fru_callout->loc_code, elog_fru->loc_code, len);
+		fru_callout->loc_code_len = len;
+		fru_callout->length = FRU_CALLOUT_SECTION_SIZE +
+						fru_callout->loc_code_len;
+
+		pel_offset += fru_callout->length;
+		len = create_fru_identity(elog_fru, pel_buffer, pel_offset);
+		if (len) {
+			/* Set FRU callout flag */
+			fru_callout->type_flags |=
+					PEL_FRU_FLAG_IDENTITY_INCLUDED;
+		}
+		fru_callout->length += len;
+		pel_offset += len;
+
+		/*
+		 * SRC sub section size is expressed in # of words
+		 * (4 byte fields)
+		 */
+		fru->header.length += fru_callout->length / 4;
+		fru_callout =
+			(struct pel_fru_callout *)(pel_buffer + pel_offset);
+	}
+	return fru->header.length * 4;
+}
+
 /* Create SRC section of OPAL log */
 static void create_src_section(struct errorlog *elog_data,
 					char *pel_buffer, int *pel_offset)
 {
+	int len;
 	struct pel_src_section *src = (struct pel_src_section *)
 						(pel_buffer + *pel_offset);
 
@@ -134,6 +236,16 @@  static void create_src_section(struct errorlog *elog_data,
 	src->hexwords[6] = cpu_to_be32(elog_data->additional_info[2]);
 	src->hexwords[7] = cpu_to_be32(elog_data->additional_info[3]);
 	*pel_offset += SRC_SECTION_SIZE;
+
+	if (!elog_data->num_fru_callout)
+		return;
+
+	/* Fill up src sub section header */
+	len = create_fru_callout_section(elog_data, pel_buffer, *pel_offset);
+	src->srclength += len;
+	src->v6header.length += len;
+	src->flags |= PEL_SRC_FLAG_SUB_SECTION_PRESENT;
+	*pel_offset += len;
 }
 
 /* Create user header section */
@@ -252,9 +364,47 @@  static size_t pel_user_section_size(struct errorlog *elog_data)
 	return total;
 }
 
+static size_t pel_fru_callout_section_size(struct errorlog *elog_data)
+{
+	int i, fru_count;
+	size_t total = 0;
+
+	if (!elog_data->num_fru_callout)
+		return 0;
+
+	total += SRC_SUBSECTION_HEADER_SIZE;
+	fru_count = MIN(PEL_MAX_FRU_CALL_OUTS, elog_data->num_fru_callout);
+	for (i = 0; i < fru_count; i++) {
+		size_t len;
+
+		total += FRU_CALLOUT_SECTION_SIZE;
+		/* Size of loc-code including NULL terminator. */
+		len = strlen(elog_data->fru_callout[i].loc_code) + 1;
+		len = ALIGN_UP(len, 4);
+		total += len;
+
+		/* Calculate size for FRU identity if present */
+		if (!strlen(elog_data->fru_callout[i].part_no)
+			&& !strlen(elog_data->fru_callout[i].serial_no))
+			continue;
+
+		if (strlen(elog_data->fru_callout[i].part_no))
+			total += PEL_FRU_PART_LEN;
+
+		if (strlen(elog_data->fru_callout[i].serial_no))
+			total += PEL_FRU_SERIAL_LEN;
+
+		total += FRU_IDENTITY_HEADER_SIZE;
+	}
+	return total;
+}
+
 size_t pel_size(struct errorlog *elog_data)
 {
-	return PEL_MIN_SIZE + pel_user_section_size(elog_data);
+	size_t len;
+
+	len = PEL_MIN_SIZE + pel_user_section_size(elog_data);
+	return len + pel_fru_callout_section_size(elog_data);
 }
 
 /* Converts an OPAL errorlog into a PEL formatted log */
diff --git a/include/errorlog.h b/include/errorlog.h
index 74fe8b1fd..a9c3250e8 100644
--- a/include/errorlog.h
+++ b/include/errorlog.h
@@ -89,6 +89,12 @@ 
 /* Max user dump size is 14K	*/
 #define OPAL_LOG_MAX_DUMP	14336
 
+#define PEL_FRU_PART_LEN	8
+#define PEL_FRU_SERIAL_LEN	12
+
+/* Maximum possible FRU Call-outs in PEL errorlog */
+#define PEL_MAX_FRU_CALL_OUTS	10
+
 /* Origin of error, elog_origin */
 #define ORG_SAPPHIRE	1
 #define ORG_POWERNV	2
@@ -101,6 +107,13 @@  struct elog_user_data_section {
 	char data_dump[1];
 } __packed;
 
+
+struct elog_fru_callout {
+	char loc_code[LOC_CODE_SIZE];
+	char part_no[PEL_FRU_PART_LEN];
+	char serial_no[PEL_FRU_SERIAL_LEN];
+};
+
 /*
  * All the information regarding an error/event to be reported
  * needs to populate this structure using pre-defined interfaces
@@ -125,6 +138,8 @@  struct errorlog {
 	uint32_t log_size;
 	uint64_t elog_timeout;
 	bool service_req;
+	uint16_t num_fru_callout;
+	struct elog_fru_callout fru_callout[PEL_MAX_FRU_CALL_OUTS];
 
 	char user_data_dump[OPAL_LOG_MAX_DUMP];
 	struct list_node link;
@@ -347,6 +362,8 @@  uint32_t log_simple_error(struct opal_err_info *e_info,
 struct errorlog *opal_elog_create(struct opal_err_info *e_info,
 				  uint32_t tag) __warn_unused_result;
 void log_add_section(struct errorlog *buf, uint32_t tag);
+void log_add_callout_section(struct errorlog *buf, const char *loc_code,
+				const char *part_no, const char *serial_no);
 void log_append_data(struct errorlog *buf, unsigned char *data, uint16_t size);
 void log_append_msg(struct errorlog *buf,
 		const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
diff --git a/include/pel.h b/include/pel.h
index fdc30366a..968bf2779 100644
--- a/include/pel.h
+++ b/include/pel.h
@@ -12,12 +12,14 @@ 
 #define PRIVATE_HEADER_SECTION_SIZE		48
 #define USER_HEADER_SECTION_SIZE		24
 #define SRC_SECTION_SIZE			80
-#define SRC_SUBSECTION_SIZE			 4
+#define SRC_SUBSECTION_HEADER_SIZE		 4
 #define SRC_LENGTH				72
 #define PEL_MAX_SRC_BYTES			32
 #define EXTENDED_HEADER_SECTION_SIZE		76
 #define MTMS_SECTION_SIZE			28
 #define IO_EVENT_SECTION_SIZE			16
+#define FRU_CALLOUT_SECTION_SIZE		4
+#define FRU_IDENTITY_HEADER_SIZE		4
 
 #define PEL_ELOG_VERSION		1
 #define PEL_ELOG_SST			0
@@ -36,6 +38,8 @@ 
 #define PEL_SRC_SEC_VER	0x02
 #define PEL_EXT_HRD_VER	0x01
 
+#define PEL_SRC_FLAG_SUB_SECTION_PRESENT	0x01
+
 /* Error log reporting action */
 #define ERRL_ACTION_REPORT     0x2000
 #define ERRL_ACTION_SERVICE    0x8000
@@ -71,6 +75,37 @@  struct pel_v6_header {
 
 /* opal_srctype */
 #define OPAL_SRC_TYPE_ERROR 0xBB
+/* pel SRC subsection id */
+#define PEL_SRC_SUB_SECTION_ID	0xC0
+
+/* FRU callout Type */
+#define PEL_FRU_TYPE_NORMAL			0x20
+/* FRU callout flags */
+#define PEL_FRU_FLAG_IDENTITY_INCLUDED		0x08
+#define PEL_FRU_FLAG_MRU_INCLUDED		0x04
+/* FRU callout priority */
+#define PEL_FRU_PRIO_HIGH		'H'
+#define PEL_FRU_PRIO_MEDIUM		'M'
+#define PEL_FRU_PRIO_MEDIUM_GROUP_A	'A'
+#define PEL_FRU_PRIO_MEDIUM_GROUP_B	'B'
+#define PEL_FRU_PRIO_MEDIUM_GROUP_C	'C'
+#define PEL_FRU_PRIO_LOW		'L'
+
+/* FRU identity srtucture id */
+#define PEL_FRU_IDENT_ID		0x4944	/* ID */
+/* FRU identity flags bits 0-3 */
+#define PEL_FRU_IDENT_FLAG_NORMAL	0x10	/* "normal" hardware FRU */
+#define PEL_FRU_IDENT_FLAG_CODE	0x20	/* code FRU */
+#define PEL_FRU_IDENT_FLAG_CONFIG	0x30	/* configuration error */
+#define PEL_FRU_IDENT_FLAG_MAINT	0x40	/* Maintenance Procedure req */
+#define PEL_FRU_IDENT_FLAG_EXT		0x90	/* External  FRU */
+#define PEL_FRU_IDENT_FLAG_EXT_CODE	0xa0	/* External code FRU */
+#define PEL_FRU_IDENT_FLAG_TOOL	0xb0	/* Tool FRU */
+#define PEL_FRU_IDENT_FLAG_SYMBOL	0xc0	/* Symbolic  FRU */
+/* FRU identity flags bits 4-7 */
+#define PEL_FRU_IDENT_FLAG_PART	0x08	/* FRU Part Number supplied */
+#define PEL_FRU_IDENT_FLAG_CCIN	0x04	/* CCIN supplied */
+#define PEL_FRU_IDENT_FLAG_SERIAL	0x01	/* FRU serial number supplied */
 
 #define PEL_CID_SAPPHIRE	'K'	/* creator ID for sapphire log */
 #define PEL_CID_POWERNV	'P'	/* creator ID for powernv log */
@@ -129,6 +164,40 @@  struct pel_src_section {
 	char		srcstring[PEL_MAX_SRC_BYTES];
 } __packed;
 
+struct pel_src_sub_section {
+	uint8_t		id;		/* SRC sub section id */
+	uint8_t		flags;
+	uint16_t	length;		/* in # of words (4 byte fields) */
+};
+
+struct pel_fru_callout_section {
+	struct pel_src_sub_section	header;
+};
+
+struct pel_fru_callout {
+	uint8_t		length;	/* in bytes */
+	/* First 4 bits are FRU callout type */
+	/* Second 4 bits are the FRU callout flags */
+	uint8_t		type_flags;
+	uint8_t		flags:4;
+	uint8_t		priority;
+	uint8_t		loc_code_len;
+	char		loc_code[0];
+};
+
+struct pel_fru_identity {
+	uint16_t	id;
+	uint8_t		length;
+	uint8_t		flags;
+	/*
+	 * Depending on flags[4-7] bits fru_data will contain:
+	 *	part number 8 bytes (7 ASCII character + 1 null-terminator)
+	 *	CCIN number 4 bytes (not a null-terminated string)
+	 *	FRU serial number 12 bytes (not a null-terminated string)
+	 */
+	char		fru_data[0];
+};
+
 struct pel_extended_header_section {
 	struct	pel_v6_header v6header;
 	char	model[PEL_SYS_MODEL_LEN];