[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