1 /* Copyright 2013-2017 IBM Corp.
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12 * implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /* This file contains the front end for OPAL error logging. It is used
18 * to construct a struct errorlog representing the event/error to be
19 * logged which is then passed to the platform specific backend to log
20 * the actual errors.
21 */
22 #include <skiboot.h>
23 #include <lock.h>
24 #include <errorlog.h>
25 #include <pool.h>
26
27 /*
28 * Maximum number buffers that are pre-allocated
29 * to hold elogs that are reported on Sapphire and
30 * PowerNV.
31 */
32 #define ELOG_WRITE_MAX_RECORD 64
33 /* Platform log id as per the spec */
34 static uint32_t sapphire_elog_id = 0xB0000000;
35
36 /* Reserved for future use */
37 /* static uint32_t powernv_elog_id = 0xB1000000; */
38
39 /* Pool to allocate elog messages from */
40 static struct pool elog_pool;
41 static struct lock elog_lock = LOCK_UNLOCKED;
42
43 static bool elog_available = false;
44
get_write_buffer(int opal_event_severity)45 static struct errorlog *get_write_buffer(int opal_event_severity)
46 {
47 struct errorlog *buf;
48
49 if (!elog_available)
50 return NULL;
51
52 lock(&elog_lock);
53 if (opal_event_severity == OPAL_ERROR_PANIC)
54 buf = pool_get(&elog_pool, POOL_HIGH);
55 else
56 buf = pool_get(&elog_pool, POOL_NORMAL);
57
58 unlock(&elog_lock);
59 return buf;
60 }
61
62 /* Reporting of error via struct errorlog */
opal_elog_create(struct opal_err_info * e_info,uint32_t tag)63 struct errorlog *opal_elog_create(struct opal_err_info *e_info, uint32_t tag)
64 {
65 struct errorlog *buf;
66
67 buf = get_write_buffer(e_info->sev);
68 if (buf) {
69 buf->error_event_type = e_info->err_type;
70 buf->component_id = e_info->cmp_id;
71 buf->subsystem_id = e_info->subsystem;
72 buf->event_severity = e_info->sev;
73 buf->event_subtype = e_info->event_subtype;
74 buf->reason_code = e_info->reason_code;
75 buf->elog_origin = ORG_SAPPHIRE;
76
77 lock(&elog_lock);
78 buf->plid = ++sapphire_elog_id;
79 unlock(&elog_lock);
80
81 /* Initialise the first user dump section */
82 log_add_section(buf, tag);
83 }
84
85 return buf;
86 }
87
88 /* Add a new user data section to an existing error log */
log_add_section(struct errorlog * buf,uint32_t tag)89 void log_add_section(struct errorlog *buf, uint32_t tag)
90 {
91 size_t size = sizeof(struct elog_user_data_section) - 1;
92 struct elog_user_data_section *tmp;
93
94 if (!buf) {
95 prerror("ELOG: Cannot add user data section. "
96 "Buffer is invalid\n");
97 return;
98 }
99
100 if ((buf->user_section_size + size) > OPAL_LOG_MAX_DUMP) {
101 prerror("ELOG: Size of dump data overruns buffer\n");
102 return;
103 }
104
105 tmp = (struct elog_user_data_section *)(buf->user_data_dump +
106 buf->user_section_size);
107 /* Use DESC if no other tag provided */
108 tmp->tag = tag ? tag : 0x44455343;
109 tmp->size = size;
110
111 buf->user_section_size += tmp->size;
112 buf->user_section_count++;
113 }
114
opal_elog_complete(struct errorlog * buf,bool success)115 void opal_elog_complete(struct errorlog *buf, bool success)
116 {
117 if (!success)
118 printf("Unable to log error\n");
119
120 lock(&elog_lock);
121 pool_free_object(&elog_pool, buf);
122 unlock(&elog_lock);
123 }
124
log_commit(struct errorlog * elog)125 void log_commit(struct errorlog *elog)
126 {
127 int rc;
128
129 if (!elog)
130 return;
131
132 if (platform.elog_commit) {
133 rc = platform.elog_commit(elog);
134 if (rc)
135 prerror("ELOG: Platform commit error %d\n", rc);
136
137 return;
138 }
139
140 opal_elog_complete(elog, false);
141 }
142
log_append_data(struct errorlog * buf,unsigned char * data,uint16_t size)143 void log_append_data(struct errorlog *buf, unsigned char *data, uint16_t size)
144 {
145 struct elog_user_data_section *section;
146 uint8_t n_sections;
147 char *buffer;
148
149 if (!buf) {
150 prerror("ELOG: Cannot update user data. Buffer is invalid\n");
151 return;
152 }
153
154 if ((buf->user_section_size + size) > OPAL_LOG_MAX_DUMP) {
155 prerror("ELOG: Size of dump data overruns buffer\n");
156 return;
157 }
158
159 /* Step through user sections to find latest dump section */
160 buffer = buf->user_data_dump;
161 n_sections = buf->user_section_count;
162 if (!n_sections) {
163 prerror("ELOG: User section invalid\n");
164 return;
165 }
166
167 while (--n_sections) {
168 section = (struct elog_user_data_section *)buffer;
169 buffer += section->size;
170 }
171
172 section = (struct elog_user_data_section *)buffer;
173 buffer += section->size;
174 memcpy(buffer, data, size);
175 section->size += size;
176 buf->user_section_size += size;
177 }
178
log_append_msg(struct errorlog * buf,const char * fmt,...)179 void log_append_msg(struct errorlog *buf, const char *fmt, ...)
180 {
181 char err_msg[250];
182 va_list list;
183
184 if (!buf) {
185 prerror("Tried to append log to NULL buffer\n");
186 return;
187 }
188
189 va_start(list, fmt);
190 vsnprintf(err_msg, sizeof(err_msg), fmt, list);
191 va_end(list);
192
193 /* Log the error on to Sapphire console */
194 prerror("%s", err_msg);
195
196 log_append_data(buf, err_msg, strlen(err_msg));
197 }
198
log_simple_error(struct opal_err_info * e_info,const char * fmt,...)199 uint32_t log_simple_error(struct opal_err_info *e_info, const char *fmt, ...)
200 {
201 struct errorlog *buf;
202 va_list list;
203 char err_msg[250];
204
205 va_start(list, fmt);
206 vsnprintf(err_msg, sizeof(err_msg), fmt, list);
207 va_end(list);
208
209 /* Log the error on to Sapphire console */
210 prerror("%s", err_msg);
211
212 buf = opal_elog_create(e_info, 0);
213 if (buf == NULL) {
214 prerror("ELOG: Error getting buffer to log error\n");
215 return -1;
216 }
217
218 log_append_data(buf, err_msg, strlen(err_msg));
219 log_commit(buf);
220
221 return buf->plid;
222 }
223
elog_init(void)224 int elog_init(void)
225 {
226 /* Pre-allocate memory for records */
227 if (pool_init(&elog_pool, sizeof(struct errorlog),
228 ELOG_WRITE_MAX_RECORD, 1))
229 return OPAL_RESOURCE;
230
231 elog_available = true;
232 return 0;
233 }
234