diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/core/errorlog.c | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/core/errorlog.c')
-rw-r--r-- | roms/skiboot/core/errorlog.c | 223 |
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; +} |