[Skiboot] [PATCH 2/5] opal/errorlog: Add support to include callout section in error log.
Mahesh Salgaonkar
mahesh at linux.ibm.com
Wed Jul 29 13:00:51 AEST 2020
From: Mahesh Salgaonkar <mahesh at 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 at linux.vnet.ibm.com>
---
core/errorlog.c | 41 ++++++++++++++
core/pel.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++
include/errorlog.h | 17 ++++++
include/pel.h | 69 ++++++++++++++++++++++++
4 files changed, 273 insertions(+), 2 deletions(-)
diff --git a/core/errorlog.c b/core/errorlog.c
index 2f7bcce19..bd2679531 100644
--- a/core/errorlog.c
+++ b/core/errorlog.c
@@ -105,6 +105,47 @@ 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 = buf->num_fru_callout;
+ struct elog_fru_callout *fru = &buf->fru_callout[index];
+
+ if (!buf) {
+ prerror("ELOG: Cannot add callout section. "
+ "Buffer is invalid\n");
+ return;
+ }
+
+ if (buf->num_fru_callout >= OPAL_MAX_FRU_CALL_OUTS) {
+ prerror("ELOG: Cannot add callout section. "
+ "Maximum limit reached\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, OPAL_FRU_PART_LEN);
+ fru->part_no[7] = '\0';
+ }
+
+ if (serial_no)
+ memcpy(fru->serial_no, serial_no, OPAL_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 4b2656346..4c0b9db2d 100644
--- a/core/pel.c
+++ b/core/pel.c
@@ -107,10 +107,108 @@ static void setrefcode(struct opal_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 opal_fru_identity *identity;
+
+ identity = (struct opal_fru_identity *)(pel_buffer + pel_offset);
+
+ if (!strlen(elog_fru->part_no) && !strlen(elog_fru->serial_no))
+ return 0;
+
+ identity->id = OPAL_FRU_IDENT_ID;
+ identity->flags = OPAL_FRU_IDENT_FLAG_NORMAL;
+
+ if (strlen(elog_fru->part_no)) {
+ identity->flags |= OPAL_FRU_IDENT_FLAG_PART;
+ memset(identity->fru_data, 0, OPAL_FRU_PART_LEN);
+ memcpy(identity->fru_data, elog_fru->part_no,
+ OPAL_FRU_PART_LEN);
+ data_len = OPAL_FRU_PART_LEN;
+ }
+
+ if (strlen(elog_fru->serial_no)) {
+ identity->flags |= OPAL_FRU_IDENT_FLAG_SERIAL;
+ memset(identity->fru_data + data_len, 0, OPAL_FRU_SERIAL_LEN);
+ memcpy(identity->fru_data + data_len, elog_fru->serial_no,
+ OPAL_FRU_SERIAL_LEN);
+ data_len += OPAL_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 opal_fru_callout_section *fru;
+ struct opal_fru_callout *fru_callout;
+ int i, fru_count;
+
+ fru = (struct opal_fru_callout_section *)(pel_buffer + pel_offset);
+ fru->header.id = OPAL_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 opal_fru_callout *)(pel_buffer + pel_offset);
+
+ /* Add FRU Call-outs */
+ fru_count = MIN(OPAL_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];
+
+ fru_callout->type = OPAL_FRU_TYPE_NORMAL;
+ fru_callout->priority = OPAL_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)
+ fru_callout->flags = OPAL_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 opal_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 opal_src_section *src = (struct opal_src_section *)
(pel_buffer + *pel_offset);
@@ -134,6 +232,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 |= OPAL_SRC_FLAG_SUB_SECTION_PRESENT;
+ *pel_offset += len;
}
/* Create user header section */
@@ -252,9 +360,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(OPAL_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 += OPAL_FRU_PART_LEN;
+
+ if (strlen(elog_data->fru_callout[i].serial_no))
+ total += OPAL_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..24d12c4d8 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 OPAL_FRU_PART_LEN 8
+#define OPAL_FRU_SERIAL_LEN 12
+
+/* Maximum possible FRU Call-outs */
+#define OPAL_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[OPAL_FRU_PART_LEN];
+ char serial_no[OPAL_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[OPAL_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 252d27e2e..e2e816036 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 OPAL_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 OPAL_ELOG_VERSION 1
#define OPAL_ELOG_SST 0
@@ -36,6 +38,8 @@
#define OPAL_SRC_SEC_VER 0x02
#define OPAL_EXT_HRD_VER 0x01
+#define OPAL_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 opal_v6_header {
/* opal_srctype */
#define OPAL_SRC_TYPE_ERROR 0xBB
+/* opal SRC subsection id */
+#define OPAL_SRC_SUB_SECTION_ID 0xC0
+
+/* FRU callout Type */
+#define OPAL_FRU_TYPE_NORMAL 0x2
+/* FRU callout flags */
+#define OPAL_FRU_FLAG_IDENTITY_INCLUDED 0x8
+#define OPAL_FRU_FLAG_MRU_INCLUDED 0x4
+/* FRU callout priority */
+#define OPAL_FRU_PRIO_HIGH 'H'
+#define OPAL_FRU_PRIO_MEDIUM 'M'
+#define OPAL_FRU_PRIO_MEDIUM_GROUP_A 'A'
+#define OPAL_FRU_PRIO_MEDIUM_GROUP_B 'B'
+#define OPAL_FRU_PRIO_MEDIUM_GROUP_C 'C'
+#define OPAL_FRU_PRIO_LOW 'L'
+
+/* FRU identity srtucture id */
+#define OPAL_FRU_IDENT_ID 0x4944 /* ID */
+/* FRU identity flags bits 0-3 */
+#define OPAL_FRU_IDENT_FLAG_NORMAL 0x10 /* "normal" hardware FRU */
+#define OPAL_FRU_IDENT_FLAG_CODE 0x20 /* code FRU */
+#define OPAL_FRU_IDENT_FLAG_CONFIG 0x30 /* configuration error */
+#define OPAL_FRU_IDENT_FLAG_MAINT 0x40 /* Maintenance Procedure req */
+#define OPAL_FRU_IDENT_FLAG_EXT 0x90 /* External FRU */
+#define OPAL_FRU_IDENT_FLAG_EXT_CODE 0xa0 /* External code FRU */
+#define OPAL_FRU_IDENT_FLAG_TOOL 0xb0 /* Tool FRU */
+#define OPAL_FRU_IDENT_FLAG_SYMBOL 0xc0 /* Symbolic FRU */
+/* FRU identity flags bits 4-7 */
+#define OPAL_FRU_IDENT_FLAG_PART 0x08 /* FRU Part Number supplied */
+#define OPAL_FRU_IDENT_FLAG_CCIN 0x04 /* CCIN supplied */
+#define OPAL_FRU_IDENT_FLAG_SERIAL 0x01 /* FRU serial number supplied */
#define OPAL_CID_SAPPHIRE 'K' /* creator ID for sapphire log */
#define OPAL_CID_POWERNV 'P' /* creator ID for powernv log */
@@ -129,6 +164,38 @@ struct opal_src_section {
char srcstring[OPAL_MAX_SRC_BYTES];
} __packed;
+struct opal_src_sub_section {
+ uint8_t id; /* SRC sub section id */
+ uint8_t flags;
+ uint16_t length; /* in # of words (4 byte fields) */
+};
+
+struct opal_fru_callout_section {
+ struct opal_src_sub_section header;
+};
+
+struct opal_fru_callout {
+ uint8_t length; /* in bytes */
+ uint8_t type:4;
+ uint8_t flags:4;
+ uint8_t priority;
+ uint8_t loc_code_len;
+ char loc_code[0];
+};
+
+struct opal_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 opal_extended_header_section {
struct opal_v6_header v6header;
char model[OPAL_SYS_MODEL_LEN];
More information about the Skiboot
mailing list