aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/core/pel.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/core/pel.c')
-rw-r--r--roms/skiboot/core/pel.c279
1 files changed, 279 insertions, 0 deletions
diff --git a/roms/skiboot/core/pel.c b/roms/skiboot/core/pel.c
new file mode 100644
index 000000000..ec13e5590
--- /dev/null
+++ b/roms/skiboot/core/pel.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Platform Error Log (PEL) generation
+ *
+ * Copyright 2014-2016 IBM Corp
+ */
+
+#include <string.h>
+#include <errorlog.h>
+#include <device.h>
+#include <fsp.h>
+#include <pel.h>
+#include <rtc.h>
+
+/* Create MTMS section for sapphire log */
+static void create_mtms_section(struct errorlog *elog_data,
+ char *pel_buffer, int *pel_offset)
+{
+ const struct dt_property *p;
+
+ struct opal_mtms_section *mtms = (struct opal_mtms_section *)
+ (pel_buffer + *pel_offset);
+
+ mtms->v6header.id = cpu_to_be16(ELOG_SID_MACHINE_TYPE);
+ mtms->v6header.length = cpu_to_be16(MTMS_SECTION_SIZE);
+ mtms->v6header.version = OPAL_EXT_HRD_VER;
+ mtms->v6header.subtype = 0;
+ mtms->v6header.component_id = cpu_to_be16(elog_data->component_id);
+
+ memset(mtms->model, 0x00, sizeof(mtms->model));
+ memcpy(mtms->model, dt_prop_get(dt_root, "model"), OPAL_SYS_MODEL_LEN);
+
+ memset(mtms->serial_no, 0x00, sizeof(mtms->serial_no));
+ p = dt_find_property(dt_root, "system-id");
+ if (p)
+ memcpy(mtms->serial_no, p->prop, OPAL_SYS_SERIAL_LEN);
+ else
+ memset(mtms->serial_no, 0, OPAL_SYS_SERIAL_LEN);
+
+ *pel_offset += MTMS_SECTION_SIZE;
+}
+
+/* Create extended header section */
+static void create_extended_header_section(struct errorlog *elog_data,
+ char *pel_buffer, int *pel_offset)
+{
+ const char *opalmodel = NULL;
+ const struct dt_property *p;
+ uint64_t extd_time;
+ uint32_t extd_date;
+
+ struct opal_extended_header_section *extdhdr =
+ (struct opal_extended_header_section *)
+ (pel_buffer + *pel_offset);
+
+ extdhdr->v6header.id = cpu_to_be16(ELOG_SID_EXTENDED_HEADER);
+ extdhdr->v6header.length = cpu_to_be16(EXTENDED_HEADER_SECTION_SIZE);
+ extdhdr->v6header.version = OPAL_EXT_HRD_VER;
+ extdhdr->v6header.subtype = 0;
+ extdhdr->v6header.component_id = cpu_to_be16(elog_data->component_id);
+
+ memset(extdhdr->model, 0x00, sizeof(extdhdr->model));
+ opalmodel = dt_prop_get(dt_root, "model");
+ memcpy(extdhdr->model, opalmodel, OPAL_SYS_MODEL_LEN);
+
+ memset(extdhdr->serial_no, 0x00, sizeof(extdhdr->serial_no));
+ p = dt_find_property(dt_root, "system-id");
+ if (p)
+ memcpy(extdhdr->serial_no, p->prop, OPAL_SYS_SERIAL_LEN);
+ else
+ memset(extdhdr->serial_no, 0, OPAL_SYS_SERIAL_LEN);
+
+ memset(extdhdr->opal_release_version, 0x00,
+ sizeof(extdhdr->opal_release_version));
+ memset(extdhdr->opal_subsys_version, 0x00,
+ sizeof(extdhdr->opal_subsys_version));
+
+ rtc_cache_get_datetime(&extd_date, &extd_time);
+ extdhdr->extended_header_date = cpu_to_be32(extd_date);
+ extdhdr->extended_header_time = cpu_to_be32(extd_time >> 32);
+ extdhdr->opal_symid_len = 0;
+
+ *pel_offset += EXTENDED_HEADER_SECTION_SIZE;
+}
+
+/* set src type */
+static void settype(struct opal_src_section *src, uint8_t src_type)
+{
+ char type[4];
+ snprintf(type, sizeof(type), "%02X", src_type);
+ memcpy(src->srcstring, type, 2);
+}
+
+/* set SRC subsystem type */
+static void setsubsys(struct opal_src_section *src, uint8_t src_subsys)
+{
+ char subsys[4];
+ snprintf(subsys, sizeof(subsys), "%02X", src_subsys);
+ memcpy(src->srcstring+2, subsys, 2);
+}
+
+/* Ser reason code of SRC */
+static void setrefcode(struct opal_src_section *src, uint16_t src_refcode)
+{
+ char refcode[8];
+ snprintf(refcode, sizeof(refcode), "%04X", src_refcode);
+ memcpy(src->srcstring+4, refcode, 4);
+}
+
+/* Create SRC section of OPAL log */
+static void create_src_section(struct errorlog *elog_data,
+ char *pel_buffer, int *pel_offset)
+{
+ struct opal_src_section *src = (struct opal_src_section *)
+ (pel_buffer + *pel_offset);
+
+ src->v6header.id = cpu_to_be16(ELOG_SID_PRIMARY_SRC);
+ src->v6header.length = cpu_to_be16(SRC_SECTION_SIZE);
+ src->v6header.version = OPAL_ELOG_VERSION;
+ src->v6header.subtype = OPAL_ELOG_SST;
+ src->v6header.component_id = cpu_to_be16(elog_data->component_id);
+
+ src->version = OPAL_SRC_SEC_VER;
+ src->flags = 0;
+ src->wordcount = OPAL_SRC_MAX_WORD_COUNT;
+ src->srclength = cpu_to_be16(SRC_LENGTH);
+ settype(src, OPAL_SRC_TYPE_ERROR);
+ setsubsys(src, OPAL_FAILING_SUBSYSTEM);
+ setrefcode(src, elog_data->reason_code);
+ memset(src->hexwords, 0 , (8 * 4));
+ src->hexwords[0] = cpu_to_be32(OPAL_SRC_FORMAT);
+ src->hexwords[4] = cpu_to_be32(elog_data->additional_info[0]);
+ src->hexwords[5] = cpu_to_be32(elog_data->additional_info[1]);
+ 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;
+}
+
+/* Create user header section */
+static void create_user_header_section(struct errorlog *elog_data,
+ char *pel_buffer, int *pel_offset)
+{
+ struct opal_user_header_section *usrhdr =
+ (struct opal_user_header_section *)
+ (pel_buffer + *pel_offset);
+
+ usrhdr->v6header.id = cpu_to_be16(ELOG_SID_USER_HEADER);
+ usrhdr->v6header.length = cpu_to_be16(USER_HEADER_SECTION_SIZE);
+ usrhdr->v6header.version = OPAL_ELOG_VERSION;
+ usrhdr->v6header.subtype = OPAL_ELOG_SST;
+ usrhdr->v6header.component_id = cpu_to_be16(elog_data->component_id);
+
+ usrhdr->subsystem_id = elog_data->subsystem_id;
+ usrhdr->event_scope = 0;
+ usrhdr->event_severity = elog_data->event_severity;
+ usrhdr->event_type = elog_data->event_subtype;
+
+ if (elog_data->elog_origin == ORG_SAPPHIRE)
+ usrhdr->action_flags = cpu_to_be16(ERRL_ACTION_REPORT);
+ else
+ usrhdr->action_flags = cpu_to_be16(ERRL_ACTION_NONE);
+
+ *pel_offset += USER_HEADER_SECTION_SIZE;
+}
+
+/* Create private header section */
+static void create_private_header_section(struct errorlog *elog_data,
+ char *pel_buffer, int *pel_offset)
+{
+ uint64_t ctime;
+ uint32_t cdate;
+ struct opal_private_header_section *privhdr =
+ (struct opal_private_header_section *)
+ pel_buffer;
+
+ privhdr->v6header.id = cpu_to_be16(ELOG_SID_PRIVATE_HEADER);
+ privhdr->v6header.length = cpu_to_be16(PRIVATE_HEADER_SECTION_SIZE);
+ privhdr->v6header.version = OPAL_ELOG_VERSION;
+ privhdr->v6header.subtype = OPAL_ELOG_SST;
+ privhdr->v6header.component_id = cpu_to_be16(elog_data->component_id);
+ privhdr->plid = cpu_to_be32(elog_data->plid);
+
+ rtc_cache_get_datetime(&cdate, &ctime);
+ privhdr->create_date = cpu_to_be32(cdate);
+ privhdr->create_time = cpu_to_be32(ctime >> 32);
+ privhdr->section_count = 5;
+
+ privhdr->creator_subid_hi = 0x00;
+ privhdr->creator_subid_lo = 0x00;
+
+ if (elog_data->elog_origin == ORG_SAPPHIRE)
+ privhdr->creator_id = OPAL_CID_SAPPHIRE;
+ else
+ privhdr->creator_id = OPAL_CID_POWERNV;
+
+ privhdr->log_entry_id = cpu_to_be32(elog_data->plid); /*entry id is updated by FSP*/
+
+ *pel_offset += PRIVATE_HEADER_SECTION_SIZE;
+}
+
+static void create_user_defined_section(struct errorlog *elog_data,
+ char *pel_buffer, int *pel_offset)
+{
+ char *dump = (char *)pel_buffer + *pel_offset;
+ char *opal_buf = (char *)elog_data->user_data_dump;
+ struct opal_user_section *usrhdr;
+ struct elog_user_data_section *opal_usr_data;
+ struct opal_private_header_section *privhdr =
+ (struct opal_private_header_section *)pel_buffer;
+ int i;
+
+ for (i = 0; i < elog_data->user_section_count; i++) {
+
+ usrhdr = (struct opal_user_section *)dump;
+ opal_usr_data = (struct elog_user_data_section *)opal_buf;
+
+ usrhdr->v6header.id = cpu_to_be16(ELOG_SID_USER_DEFINED);
+ usrhdr->v6header.length = cpu_to_be16(
+ sizeof(struct opal_v6_header) +
+ be16_to_cpu(opal_usr_data->size));
+ usrhdr->v6header.version = OPAL_ELOG_VERSION;
+ usrhdr->v6header.subtype = OPAL_ELOG_SST;
+ usrhdr->v6header.component_id = cpu_to_be16(elog_data->component_id);
+
+ memcpy(usrhdr->dump, opal_buf, be16_to_cpu(opal_usr_data->size));
+ *pel_offset += be16_to_cpu(usrhdr->v6header.length);
+ dump += be16_to_cpu(usrhdr->v6header.length);
+ opal_buf += be16_to_cpu(opal_usr_data->size);
+ privhdr->section_count++;
+ }
+}
+
+static size_t pel_user_section_size(struct errorlog *elog_data)
+{
+ int i;
+ size_t total = 0;
+ char *opal_buf = (char *)elog_data->user_data_dump;
+ struct elog_user_data_section *opal_usr_data;
+
+ for (i = 0; i < elog_data->user_section_count; i++) {
+ u16 s;
+
+ opal_usr_data = (struct elog_user_data_section *)opal_buf;
+ s = be16_to_cpu(opal_usr_data->size);
+ total += sizeof(struct opal_v6_header) + s;
+ opal_buf += s;
+ }
+
+ return total;
+}
+
+size_t pel_size(struct errorlog *elog_data)
+{
+ return PEL_MIN_SIZE + pel_user_section_size(elog_data);
+}
+
+/* Converts an OPAL errorlog into a PEL formatted log */
+int create_pel_log(struct errorlog *elog_data, char *pel_buffer,
+ size_t pel_buffer_size)
+{
+ int pel_offset = 0;
+
+ if (pel_buffer_size < pel_size(elog_data)) {
+ prerror("PEL buffer too small to create record\n");
+ return 0;
+ }
+
+ memset(pel_buffer, 0, pel_buffer_size);
+
+ create_private_header_section(elog_data, pel_buffer, &pel_offset);
+ create_user_header_section(elog_data, pel_buffer, &pel_offset);
+ create_src_section(elog_data, pel_buffer, &pel_offset);
+ create_extended_header_section(elog_data, pel_buffer, &pel_offset);
+ create_mtms_section(elog_data, pel_buffer, &pel_offset);
+ if (elog_data->user_section_count)
+ create_user_defined_section(elog_data, pel_buffer, &pel_offset);
+
+ return pel_offset;
+}