diff mbox series

[2/5] opal/errorlog: Add support to include callout section in error log.

Message ID 159599165147.67334.10462493500128619298.stgit@jupiter
State Changes Requested
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 (abe4c4799ffee4be12674ad59fc0bc521b0724f3)
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 J Salgaonkar July 29, 2020, 3 a.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>
---
 core/errorlog.c    |   41 ++++++++++++++
 core/pel.c         |  148 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/errorlog.h |   17 ++++++
 include/pel.h      |   69 ++++++++++++++++++++++++
 4 files changed, 273 insertions(+), 2 deletions(-)

Comments

Oliver O'Halloran July 29, 2020, 7:12 a.m. UTC | #1
On Wed, Jul 29, 2020 at 1:00 PM Mahesh Salgaonkar <mahesh@linux.ibm.com> wrote:
>
> 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>
> ---
>  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];
>
>
Mahesh J Salgaonkar July 29, 2020, 11:41 a.m. UTC | #2
On 2020-07-29 17:12:14 Wed, Oliver O'Halloran wrote:
> On Wed, Jul 29, 2020 at 1:00 PM Mahesh Salgaonkar <mahesh@linux.ibm.com> wrote:
> >
> > 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>
> > ---
> >  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/include/pel.h b/include/pel.h
> > index 252d27e2e..e2e816036 100644
> > --- a/include/pel.h
> > +++ b/include/pel.h
[...]
> > @@ -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...

All the srtuctures defined in this file (pel.h) is as per the PEL
(Platform Error Log) format and we use it as is for generating PEL error
logs for FSP/BMC.

> 
> > +
> > +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.

Agree. Will fix it.

Thanks for your review.
-Mahesh.
Oliver O'Halloran July 29, 2020, 11:57 a.m. UTC | #3
On Wed, Jul 29, 2020 at 9:41 PM Mahesh J Salgaonkar
<mahesh@linux.ibm.com> wrote:
>
> On 2020-07-29 17:12:14 Wed, Oliver O'Halloran wrote:
> > Are any of these structures actually defined by opal? they seem like
> > FSP junk that we just happen to be using...
>
> All the srtuctures defined in this file (pel.h) is as per the PEL
> (Platform Error Log) format and we use it as is for generating PEL error
> logs for FSP/BMC.

Right, don't use opal_* prefixes for definitions unless they're
actually part of an external facing API that OPAL defines. I realise
there's already some code in here that breaks that rule, but I'd
prefer that was fixed rather than continuing the trend.
Mahesh J Salgaonkar July 29, 2020, 12:41 p.m. UTC | #4
On 2020-07-29 21:57:12 Wed, Oliver O'Halloran wrote:
> On Wed, Jul 29, 2020 at 9:41 PM Mahesh J Salgaonkar
> <mahesh@linux.ibm.com> wrote:
> >
> > On 2020-07-29 17:12:14 Wed, Oliver O'Halloran wrote:
> > > Are any of these structures actually defined by opal? they seem like
> > > FSP junk that we just happen to be using...
> >
> > All the srtuctures defined in this file (pel.h) is as per the PEL
> > (Platform Error Log) format and we use it as is for generating PEL error
> > logs for FSP/BMC.
> 
> Right, don't use opal_* prefixes for definitions unless they're
> actually part of an external facing API that OPAL defines. I realise
> there's already some code in here that breaks that rule, but I'd
> prefer that was fixed rather than continuing the trend.

Maybe I can change all that to pel_* as a separate patch. Let me see if
I can fix that as part of this patch series.

Thanks,
-Mahesh.
diff mbox series

Patch

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];