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