1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 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;
}
|