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 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 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 */ 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 */ 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 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 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 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 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 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 283 static inline u64 get_elog_timeout(void) 284 { 285 return (mftb() + secs_to_tb(ERRORLOG_TIMEOUT_INTERVAL)); 286 } 287 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 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 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 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 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 */ 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