aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/core/errorlog.c
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/core/errorlog.c
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/core/errorlog.c')
-rw-r--r--roms/skiboot/core/errorlog.c223
1 files changed, 223 insertions, 0 deletions
diff --git a/roms/skiboot/core/errorlog.c b/roms/skiboot/core/errorlog.c
new file mode 100644
index 000000000..f64ac3f23
--- /dev/null
+++ b/roms/skiboot/core/errorlog.c
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* This file contains the front end for OPAL error logging. It is used
+ * to construct a struct errorlog representing the event/error to be
+ * logged which is then passed to the platform specific backend to log
+ * the actual errors.
+ *
+ * Copyright 2013-2017 IBM Corp.
+ */
+
+#include <skiboot.h>
+#include <lock.h>
+#include <errorlog.h>
+#include <pool.h>
+
+/*
+ * Maximum number buffers that are pre-allocated
+ * to hold elogs that are reported on Sapphire and
+ * PowerNV.
+ */
+#define ELOG_WRITE_MAX_RECORD 64
+/* Platform log id as per the spec */
+static uint32_t sapphire_elog_id = 0xB0000000;
+
+/* Reserved for future use */
+/* static uint32_t powernv_elog_id = 0xB1000000; */
+
+/* Pool to allocate elog messages from */
+static struct pool elog_pool;
+static struct lock elog_lock = LOCK_UNLOCKED;
+
+static bool elog_available = false;
+
+static struct errorlog *get_write_buffer(int opal_event_severity)
+{
+ struct errorlog *buf;
+
+ if (!elog_available)
+ return NULL;
+
+ lock(&elog_lock);
+ if (opal_event_severity == OPAL_ERROR_PANIC)
+ buf = pool_get(&elog_pool, POOL_HIGH);
+ else
+ buf = pool_get(&elog_pool, POOL_NORMAL);
+
+ unlock(&elog_lock);
+ return buf;
+}
+
+/* Reporting of error via struct errorlog */
+struct errorlog *opal_elog_create(struct opal_err_info *e_info, uint32_t tag)
+{
+ struct errorlog *buf;
+
+ buf = get_write_buffer(e_info->sev);
+ if (buf) {
+ buf->error_event_type = e_info->err_type;
+ buf->component_id = e_info->cmp_id;
+ buf->subsystem_id = e_info->subsystem;
+ buf->event_severity = e_info->sev;
+ buf->event_subtype = e_info->event_subtype;
+ buf->reason_code = e_info->reason_code;
+ buf->elog_origin = ORG_SAPPHIRE;
+
+ lock(&elog_lock);
+ buf->plid = ++sapphire_elog_id;
+ unlock(&elog_lock);
+
+ /* Initialise the first user dump section */
+ log_add_section(buf, tag);
+ }
+
+ return buf;
+}
+
+/* Add a new user data section to an existing error log */
+void log_add_section(struct errorlog *buf, uint32_t tag)
+{
+ size_t size = sizeof(struct elog_user_data_section) - 1;
+ struct elog_user_data_section *tmp;
+
+ if (!buf) {
+ prerror("ELOG: Cannot add user data section. "
+ "Buffer is invalid\n");
+ return;
+ }
+
+ if ((buf->user_section_size + size) > OPAL_LOG_MAX_DUMP) {
+ prerror("ELOG: Size of dump data overruns buffer\n");
+ return;
+ }
+
+ tmp = (struct elog_user_data_section *)(buf->user_data_dump +
+ buf->user_section_size);
+ /* Use DESC if no other tag provided */
+ tmp->tag = tag ? cpu_to_be32(tag) : cpu_to_be32(OPAL_ELOG_SEC_DESC);
+ tmp->size = cpu_to_be16(size);
+
+ buf->user_section_size += size;
+ buf->user_section_count++;
+}
+
+void opal_elog_complete(struct errorlog *buf, bool success)
+{
+ if (!success)
+ printf("Unable to log error\n");
+
+ lock(&elog_lock);
+ pool_free_object(&elog_pool, buf);
+ unlock(&elog_lock);
+}
+
+void log_commit(struct errorlog *elog)
+{
+ int rc;
+
+ if (!elog)
+ return;
+
+ if (platform.elog_commit) {
+ rc = platform.elog_commit(elog);
+ if (rc)
+ prerror("ELOG: Platform commit error %d\n", rc);
+
+ return;
+ }
+
+ opal_elog_complete(elog, false);
+}
+
+void log_append_data(struct errorlog *buf, unsigned char *data, uint16_t size)
+{
+ struct elog_user_data_section *section;
+ uint8_t n_sections;
+ char *buffer;
+ uint16_t ssize;
+
+ if (!buf) {
+ prerror("ELOG: Cannot update user data. Buffer is invalid\n");
+ return;
+ }
+
+ if ((buf->user_section_size + size) > OPAL_LOG_MAX_DUMP) {
+ prerror("ELOG: Size of dump data overruns buffer\n");
+ return;
+ }
+
+ /* Step through user sections to find latest dump section */
+ buffer = buf->user_data_dump;
+ n_sections = buf->user_section_count;
+ if (!n_sections) {
+ prerror("ELOG: User section invalid\n");
+ return;
+ }
+
+ while (--n_sections) {
+ section = (struct elog_user_data_section *)buffer;
+ buffer += be16_to_cpu(section->size);
+ }
+
+ section = (struct elog_user_data_section *)buffer;
+ ssize = be16_to_cpu(section->size);
+ buffer += ssize;
+ memcpy(buffer, data, size);
+ section->size = cpu_to_be16(ssize + size);
+ buf->user_section_size += size;
+}
+
+void log_append_msg(struct errorlog *buf, const char *fmt, ...)
+{
+ char err_msg[250];
+ va_list list;
+
+ if (!buf) {
+ prerror("Tried to append log to NULL buffer\n");
+ return;
+ }
+
+ va_start(list, fmt);
+ vsnprintf(err_msg, sizeof(err_msg), fmt, list);
+ va_end(list);
+
+ /* Log the error on to Sapphire console */
+ prerror("%s", err_msg);
+
+ log_append_data(buf, err_msg, strlen(err_msg));
+}
+
+uint32_t log_simple_error(struct opal_err_info *e_info, const char *fmt, ...)
+{
+ struct errorlog *buf;
+ va_list list;
+ char err_msg[250];
+
+ va_start(list, fmt);
+ vsnprintf(err_msg, sizeof(err_msg), fmt, list);
+ va_end(list);
+
+ /* Log the error on to Sapphire console */
+ prerror("%s", err_msg);
+
+ buf = opal_elog_create(e_info, 0);
+ if (buf == NULL) {
+ prerror("ELOG: Error getting buffer to log error\n");
+ return -1;
+ }
+
+ log_append_data(buf, err_msg, strlen(err_msg));
+ log_commit(buf);
+
+ return buf->plid;
+}
+
+int elog_init(void)
+{
+ /* Pre-allocate memory for records */
+ if (pool_init(&elog_pool, sizeof(struct errorlog),
+ ELOG_WRITE_MAX_RECORD, 1))
+ return OPAL_RESOURCE;
+
+ elog_available = true;
+ return 0;
+}