1 /* Copyright 2013-2016 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
18 /*
19 * This code will enable generation and pushing of error log from Sapphire
20 * to FSP.
21 * Critical events from Sapphire that needs to be reported will be pushed
22 * on to FSP after converting the error log to Platform Error Log(PEL) format.
23 * This is termed as write action to FSP.
24 */
25
26 #include <cpu.h>
27 #include <errno.h>
28 #include <fsp.h>
29 #include <fsp-elog.h>
30 #include <lock.h>
31 #include <opal-api.h>
32 #include <pel.h>
33 #include <pool.h>
34 #include <skiboot.h>
35 #include <timebase.h>
36
37 static LIST_HEAD(elog_write_to_fsp_pending);
38 static LIST_HEAD(elog_write_to_host_pending);
39 static LIST_HEAD(elog_write_to_host_processed);
40
41 static struct lock elog_write_lock = LOCK_UNLOCKED;
42 static struct lock elog_panic_write_lock = LOCK_UNLOCKED;
43 static struct lock elog_write_to_host_lock = LOCK_UNLOCKED;
44
45 #define ELOG_WRITE_TO_FSP_BUFFER_SIZE 0x00004000
46 /* Log buffer to copy OPAL log for write to FSP. */
47 static void *elog_write_to_fsp_buffer;
48
49 #define ELOG_PANIC_WRITE_BUFFER_SIZE 0x00004000
50 static void *elog_panic_write_buffer;
51
52 #define ELOG_WRITE_TO_HOST_BUFFER_SIZE 0x00004000
53 static void *elog_write_to_host_buffer;
54
55 static uint32_t elog_write_retries;
56
57 /* Manipulate this only with write_lock held */
58 static uint32_t elog_plid_fsp_commit = -1;
59 static enum elog_head_state elog_write_to_host_head_state = ELOG_STATE_NONE;
60
61 /* Need forward declaration because of circular dependency */
62 static int opal_send_elog_to_fsp(void);
63
remove_elog_head_entry(void)64 static void remove_elog_head_entry(void)
65 {
66 struct errorlog *head, *entry;
67
68 lock(&elog_write_lock);
69 if (!list_empty(&elog_write_to_fsp_pending)) {
70 head = list_top(&elog_write_to_fsp_pending,
71 struct errorlog, link);
72 if (head->plid == elog_plid_fsp_commit) {
73 entry = list_pop(&elog_write_to_fsp_pending,
74 struct errorlog, link);
75 opal_elog_complete(entry,
76 elog_write_retries < MAX_RETRIES);
77 /* Reset the counter */
78 elog_plid_fsp_commit = -1;
79 }
80 }
81
82 elog_write_retries = 0;
83 unlock(&elog_write_lock);
84 }
85
opal_fsp_write_complete(struct fsp_msg * read_msg)86 static void opal_fsp_write_complete(struct fsp_msg *read_msg)
87 {
88 uint8_t val;
89
90 val = (read_msg->resp->word1 >> 8) & 0xff;
91 fsp_freemsg(read_msg);
92
93 switch (val) {
94 case FSP_STATUS_SUCCESS:
95 remove_elog_head_entry();
96 break;
97 default:
98 if (elog_write_retries++ >= MAX_RETRIES) {
99 remove_elog_head_entry();
100 prerror("ELOG: Error in writing to FSP (0x%x)!\n", val);
101 }
102
103 break;
104 }
105
106 if (opal_send_elog_to_fsp() != OPAL_SUCCESS)
107 prerror("ELOG: Error sending elog to FSP !\n");
108 }
109
110 /* Write PEL format hex dump of the log to FSP */
fsp_opal_elog_write(size_t opal_elog_size)111 static int64_t fsp_opal_elog_write(size_t opal_elog_size)
112 {
113 struct fsp_msg *elog_msg;
114
115 elog_msg = fsp_mkmsg(FSP_CMD_CREATE_ERRLOG, 3, opal_elog_size,
116 0, PSI_DMA_ERRLOG_WRITE_BUF);
117 if (!elog_msg) {
118 prerror("ELOG: Failed to create message for WRITE to FSP\n");
119 return OPAL_INTERNAL_ERROR;
120 }
121
122 if (fsp_queue_msg(elog_msg, opal_fsp_write_complete)) {
123 fsp_freemsg(elog_msg);
124 elog_msg = NULL;
125 prerror("FSP: Error queueing elog update\n");
126 return OPAL_INTERNAL_ERROR;
127 }
128
129 return OPAL_SUCCESS;
130 }
131
132 /* This should be called with elog_write_to_host_lock lock */
fsp_elog_write_set_head_state(enum elog_head_state state)133 static inline void fsp_elog_write_set_head_state(enum elog_head_state state)
134 {
135 elog_set_head_state(true, state);
136 elog_write_to_host_head_state = state;
137 }
138
opal_elog_info(uint64_t * opal_elog_id,uint64_t * opal_elog_size)139 bool opal_elog_info(uint64_t *opal_elog_id, uint64_t *opal_elog_size)
140 {
141 struct errorlog *head;
142 bool rc = false;
143
144 lock(&elog_write_to_host_lock);
145 if (elog_write_to_host_head_state == ELOG_STATE_FETCHED_DATA) {
146 head = list_top(&elog_write_to_host_pending,
147 struct errorlog, link);
148 if (!head) {
149 /**
150 * @fwts-label ElogListInconsistent
151 * @fwts-advice Bug in interaction between FSP and
152 * OPAL. The state maintained by OPAL didn't match
153 * what the FSP sent.
154 */
155 prlog(PR_ERR,
156 "%s: Inconsistent internal list state !\n",
157 __func__);
158 fsp_elog_write_set_head_state(ELOG_STATE_NONE);
159 } else {
160 *opal_elog_id = head->plid;
161 *opal_elog_size = head->log_size;
162 fsp_elog_write_set_head_state(ELOG_STATE_HOST_INFO);
163 rc = true;
164 }
165 }
166
167 unlock(&elog_write_to_host_lock);
168 return rc;
169 }
170
opal_commit_elog_in_host(void)171 static void opal_commit_elog_in_host(void)
172 {
173 struct errorlog *buf;
174
175 lock(&elog_write_to_host_lock);
176 if (!list_empty(&elog_write_to_host_pending) &&
177 (elog_write_to_host_head_state == ELOG_STATE_NONE)) {
178 buf = list_top(&elog_write_to_host_pending,
179 struct errorlog, link);
180 buf->log_size = create_pel_log(buf,
181 (char *)elog_write_to_host_buffer,
182 ELOG_WRITE_TO_HOST_BUFFER_SIZE);
183 fsp_elog_write_set_head_state(ELOG_STATE_FETCHED_DATA);
184 }
185
186 unlock(&elog_write_to_host_lock);
187 }
188
opal_elog_read(uint64_t * buffer,uint64_t opal_elog_size,uint64_t opal_elog_id)189 bool opal_elog_read(uint64_t *buffer, uint64_t opal_elog_size,
190 uint64_t opal_elog_id)
191 {
192 struct errorlog *log_data;
193 bool rc = false;
194
195 lock(&elog_write_to_host_lock);
196 if (elog_write_to_host_head_state == ELOG_STATE_HOST_INFO) {
197 log_data = list_top(&elog_write_to_host_pending,
198 struct errorlog, link);
199 if (!log_data) {
200 fsp_elog_write_set_head_state(ELOG_STATE_NONE);
201 unlock(&elog_write_to_host_lock);
202 return rc;
203 }
204
205 if ((opal_elog_id != log_data->plid) &&
206 (opal_elog_size != log_data->log_size)) {
207 unlock(&elog_write_to_host_lock);
208 return rc;
209 }
210
211 memcpy((void *)buffer, elog_write_to_host_buffer,
212 opal_elog_size);
213 list_del(&log_data->link);
214 list_add(&elog_write_to_host_processed, &log_data->link);
215 fsp_elog_write_set_head_state(ELOG_STATE_NONE);
216 rc = true;
217 }
218
219 unlock(&elog_write_to_host_lock);
220 opal_commit_elog_in_host();
221 return rc;
222 }
223
opal_elog_ack(uint64_t ack_id)224 bool opal_elog_ack(uint64_t ack_id)
225 {
226 bool rc = false;
227 struct errorlog *log_data;
228 struct errorlog *record, *next_record;
229
230 lock(&elog_write_to_host_lock);
231 if (!list_empty(&elog_write_to_host_processed)) {
232 list_for_each_safe(&elog_write_to_host_processed, record,
233 next_record, link) {
234 if (record->plid != ack_id)
235 continue;
236
237 list_del(&record->link);
238 opal_elog_complete(record, true);
239 rc = true;
240 }
241 }
242
243 if ((!rc) && (!list_empty(&elog_write_to_host_pending))) {
244 log_data = list_top(&elog_write_to_host_pending,
245 struct errorlog, link);
246 if (ack_id == log_data->plid)
247 fsp_elog_write_set_head_state(ELOG_STATE_NONE);
248
249 list_for_each_safe(&elog_write_to_host_pending, record,
250 next_record, link) {
251 if (record->plid != ack_id)
252 continue;
253
254 list_del(&record->link);
255 opal_elog_complete(record, true);
256 rc = true;
257 unlock(&elog_write_to_host_lock);
258 opal_commit_elog_in_host();
259 return rc;
260 }
261 }
262
263 unlock(&elog_write_to_host_lock);
264 return rc;
265 }
266
opal_resend_pending_logs(void)267 void opal_resend_pending_logs(void)
268 {
269 struct errorlog *record;
270
271 lock(&elog_write_to_host_lock);
272 while (!list_empty(&elog_write_to_host_processed)) {
273 record = list_pop(&elog_write_to_host_processed,
274 struct errorlog, link);
275 list_add_tail(&elog_write_to_host_pending, &record->link);
276 }
277
278 fsp_elog_write_set_head_state(ELOG_STATE_NONE);
279 unlock(&elog_write_to_host_lock);
280 opal_commit_elog_in_host();
281 }
282
get_elog_timeout(void)283 static inline u64 get_elog_timeout(void)
284 {
285 return (mftb() + secs_to_tb(ERRORLOG_TIMEOUT_INTERVAL));
286 }
287
opal_send_elog_to_fsp(void)288 static int opal_send_elog_to_fsp(void)
289 {
290 struct errorlog *head;
291 int rc = OPAL_SUCCESS;
292
293 /*
294 * Convert entry to PEL and push it down to FSP.
295 * Then we wait for the ack from FSP.
296 */
297 lock(&elog_write_lock);
298 if (!list_empty(&elog_write_to_fsp_pending)) {
299 head = list_top(&elog_write_to_fsp_pending,
300 struct errorlog, link);
301 /* Error needs to be committed, update the time out value */
302 head->elog_timeout = get_elog_timeout();
303
304 elog_plid_fsp_commit = head->plid;
305 head->log_size = create_pel_log(head,
306 (char *)elog_write_to_fsp_buffer,
307 ELOG_WRITE_TO_FSP_BUFFER_SIZE);
308 rc = fsp_opal_elog_write(head->log_size);
309 unlock(&elog_write_lock);
310 return rc;
311 }
312
313 unlock(&elog_write_lock);
314 return rc;
315 }
316
opal_push_logs_sync_to_fsp(struct errorlog * buf)317 static int opal_push_logs_sync_to_fsp(struct errorlog *buf)
318 {
319 struct fsp_msg *elog_msg;
320 int opal_elog_size = 0;
321 int rc = OPAL_SUCCESS;
322
323 lock(&elog_panic_write_lock);
324
325 /* Error needs to be committed, update the time out value */
326 buf->elog_timeout = get_elog_timeout();
327
328 opal_elog_size = create_pel_log(buf,
329 (char *)elog_panic_write_buffer,
330 ELOG_PANIC_WRITE_BUFFER_SIZE);
331
332 elog_msg = fsp_mkmsg(FSP_CMD_CREATE_ERRLOG, 3, opal_elog_size,
333 0, PSI_DMA_ELOG_PANIC_WRITE_BUF);
334 if (!elog_msg) {
335 prerror("ELOG: PLID: 0x%x Failed to create message for WRITE "
336 "to FSP\n", buf->plid);
337 unlock(&elog_panic_write_lock);
338 opal_elog_complete(buf, false);
339 return OPAL_INTERNAL_ERROR;
340 }
341
342 if (fsp_sync_msg(elog_msg, false)) {
343 fsp_freemsg(elog_msg);
344 rc = OPAL_INTERNAL_ERROR;
345 } else {
346 rc = (elog_msg->resp->word1 >> 8) & 0xff;
347 fsp_freemsg(elog_msg);
348 }
349
350 unlock(&elog_panic_write_lock);
351 if (rc != OPAL_SUCCESS)
352 opal_elog_complete(buf, false);
353 else
354 opal_elog_complete(buf, true);
355
356 return rc;
357 }
358
elog_fsp_commit(struct errorlog * buf)359 int elog_fsp_commit(struct errorlog *buf)
360 {
361 int rc = OPAL_SUCCESS;
362
363 if (buf->event_severity == OPAL_ERROR_PANIC) {
364 rc = opal_push_logs_sync_to_fsp(buf);
365 return rc;
366 }
367
368 lock(&elog_write_lock);
369 if (list_empty(&elog_write_to_fsp_pending)) {
370 list_add_tail(&elog_write_to_fsp_pending, &buf->link);
371 unlock(&elog_write_lock);
372 rc = opal_send_elog_to_fsp();
373 return rc;
374 }
375
376 list_add_tail(&elog_write_to_fsp_pending, &buf->link);
377 unlock(&elog_write_lock);
378 return rc;
379 }
380
elog_append_write_to_host(struct errorlog * buf)381 static void elog_append_write_to_host(struct errorlog *buf)
382 {
383 lock(&elog_write_to_host_lock);
384 if (list_empty(&elog_write_to_host_pending)) {
385 list_add(&elog_write_to_host_pending, &buf->link);
386 unlock(&elog_write_to_host_lock);
387 opal_commit_elog_in_host();
388 } else {
389 list_add_tail(&elog_write_to_host_pending, &buf->link);
390 unlock(&elog_write_to_host_lock);
391 }
392 }
393
elog_timeout_poll(void * data __unused)394 static void elog_timeout_poll(void *data __unused)
395 {
396 uint64_t now;
397 struct errorlog *head, *entry;
398
399 lock(&elog_write_lock);
400 if (list_empty(&elog_write_to_fsp_pending)) {
401 unlock(&elog_write_lock);
402 return;
403 }
404
405 head = list_top(&elog_write_to_fsp_pending, struct errorlog, link);
406 now = mftb();
407 if ((tb_compare(now, head->elog_timeout) == TB_AAFTERB) ||
408 (tb_compare(now, head->elog_timeout) == TB_AEQUALB)) {
409 entry = list_pop(&elog_write_to_fsp_pending,
410 struct errorlog, link);
411 unlock(&elog_write_lock);
412 elog_append_write_to_host(entry);
413 } else {
414 unlock(&elog_write_lock);
415 }
416 }
417
418 /* FSP elog init function */
fsp_elog_write_init(void)419 void fsp_elog_write_init(void)
420 {
421 if (!fsp_present())
422 return;
423
424 elog_panic_write_buffer = memalign(TCE_PSIZE,
425 ELOG_PANIC_WRITE_BUFFER_SIZE);
426 if (!elog_panic_write_buffer) {
427 prerror("FSP: could not allocate ELOG_PANIC_WRITE_BUFFER!\n");
428 return;
429 }
430
431 elog_write_to_fsp_buffer = memalign(TCE_PSIZE,
432 ELOG_WRITE_TO_FSP_BUFFER_SIZE);
433 if (!elog_write_to_fsp_buffer) {
434 prerror("FSP: could not allocate ELOG_WRITE_BUFFER!\n");
435 return;
436 }
437
438 elog_write_to_host_buffer = memalign(TCE_PSIZE,
439 ELOG_WRITE_TO_HOST_BUFFER_SIZE);
440 if (!elog_write_to_host_buffer) {
441 prerror("FSP: could not allocate ELOG_WRITE_TO_HOST_BUFFER!\n");
442 return;
443 }
444
445 /* Map TCEs */
446 fsp_tce_map(PSI_DMA_ELOG_PANIC_WRITE_BUF, elog_panic_write_buffer,
447 PSI_DMA_ELOG_PANIC_WRITE_BUF_SZ);
448
449 fsp_tce_map(PSI_DMA_ERRLOG_WRITE_BUF, elog_write_to_fsp_buffer,
450 PSI_DMA_ERRLOG_WRITE_BUF_SZ);
451
452 elog_init();
453
454 /* Add a poller */
455 opal_add_poller(elog_timeout_poll, NULL);
456 }
457