[Skiboot] [PATCH 2/5] opal/errorlog: Add support to include callout section in error log.
Oliver O'Halloran
oohall at gmail.com
Wed Jul 29 17:12:14 AEST 2020
On Wed, Jul 29, 2020 at 1:00 PM Mahesh Salgaonkar <mahesh at linux.ibm.com> wrote:
>
> 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) */
> +};
Are any of these structures actually defined by opal? they seem like
FSP junk that we just happen to be using...
> +
> +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;
The layout of bitfields is endian dependent. don't use them unless you
absolutely have to.
> + 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