/* * Allocate IPMI message: * For normal event, allocate memory using ipmi_mkmsg and for PANIC * event, use pre-allocated buffer. */ static struct ipmi_msg *ipmi_sel_alloc_msg(struct errorlog *elog_buf) { struct ipmi_msg *msg = NULL; if (elog_buf->event_severity == OPAL_ERROR_PANIC) { /* Called before initialization completes */ if (ipmi_sel_panic_msg.msg == NULL) { ipmi_sel_init(); /* Try to allocate IPMI message */ if (ipmi_sel_panic_msg.msg == NULL) return NULL; } if (ipmi_sel_panic_msg.busy == true) return NULL; lock(&ipmi_sel_panic_msg.lock); msg = ipmi_sel_panic_msg.msg; ipmi_sel_panic_msg.busy = true; unlock(&ipmi_sel_panic_msg.lock); ipmi_init_msg(msg, IPMI_DEFAULT_INTERFACE, IPMI_RESERVE_SEL, ipmi_elog_poll, elog_buf, IPMI_MAX_REQ_SIZE, 2); } else { msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_RESERVE_SEL, ipmi_elog_poll, elog_buf, NULL, IPMI_MAX_REQ_SIZE, 2); } return msg; }
/* Log SEL event with eSEL record ID */ static void ipmi_log_sel_event(struct ipmi_msg *msg, uint8_t event_severity, uint16_t esel_record_id) { /* Fill required SEL event fields */ ipmi_update_sel_record(event_severity, esel_record_id); /* Fill IPMI message */ ipmi_init_msg(msg, IPMI_DEFAULT_INTERFACE, IPMI_ADD_SEL_EVENT, ipmi_log_sel_event_complete, NULL, sizeof(struct sel_record), 2); /* Copy SEL data */ memcpy(msg->data, &sel_record, sizeof(struct sel_record)); msg->error = ipmi_log_sel_event_error; ipmi_queue_msg_head(msg); }
static int fru_add(u8 *buf, int size) { int len; char short_version[MAX_STR_LEN + 1]; struct common_header common_hdr; struct product_info info = { .manufacturer = (char *) "IBM", .product = (char *) "skiboot", .part_no = (char *) "", .serial_no = (char *) "", .asset_tag = (char *) "", }; if (size < sizeof(common_hdr)) return OPAL_PARAMETER; /* We currently only support adding the version number at the * product information offset. We choose an offset of 64 bytes * because that's what the standard recommends. */ common_hdr.version = 1; common_hdr.internal_offset = 0; common_hdr.chassis_offset = 0; common_hdr.board_offset = 0; common_hdr.product_offset = 64/8; common_hdr.multirecord_offset = 0; common_hdr.pad = 0; common_hdr.checksum = fru_checksum((u8 *) &common_hdr, sizeof(common_hdr) - 1); memcpy(buf, &common_hdr, sizeof(common_hdr)); info.version = short_version; if (!strncmp(version, "skiboot-", 8)) strncpy(info.version, &version[8], MAX_STR_LEN + 1); else strncpy(info.version, version, MAX_STR_LEN + 1); if (info.version[MAX_STR_LEN] != '\0') info.version[MAX_STR_LEN - 1] = '+'; info.version[MAX_STR_LEN] = '\0'; len = fru_fill_product_info(&buf[64], &info, size - 64); if (len < 0) return OPAL_PARAMETER; return len + 64; } static void fru_write_complete(struct ipmi_msg *msg) { u8 write_count = msg->data[0]; u16 offset; msg->data[WRITE_INDEX] += write_count; msg->data[REMAINING] -= write_count; if (msg->data[REMAINING] == 0) goto out; offset = msg->data[WRITE_INDEX]; ipmi_init_msg(msg, IPMI_DEFAULT_INTERFACE, IPMI_WRITE_FRU, fru_write_complete, NULL, MIN(msg->data[REMAINING] + 3, IPMI_MAX_REQ_SIZE), 2); memmove(&msg->data[3], &msg->data[offset + 3], msg->req_size - 3); msg->data[0] = fru_dev_id; /* FRU Device ID */ msg->data[1] = offset & 0xff; /* Offset LSB */ msg->data[2] = (offset >> 8) & 0xff; /* Offset MSB */ ipmi_queue_msg(msg); return; out: ipmi_free_msg(msg); }
/* Goes through the required steps to add a complete eSEL: * * 1. Get a reservation * 2. Add eSEL header * 3. Partially add data to the SEL * * Because a reservation is needed we need to ensure eSEL's are added * as a single transaction as concurrent/interleaved adds would cancel * the reservation. We guarantee this by always adding our messages to * the head of the transmission queue, blocking any other messages * being sent until we have completed sending this message. * * There is still a very small chance that we will accidentally * interleave a message if there is another one waiting at the head of * the ipmi queue and another cpu calls the ipmi poller before we * complete. However this should just cause a resevation cancelled * error which we have to deal with anyway (eg. because there may be a * SEL erase in progress) so it shouldn't cause any problems. */ static void ipmi_elog_poll(struct ipmi_msg *msg) { static bool first = false; static char pel_buf[IPMI_MAX_PEL_SIZE]; static size_t pel_size; static size_t esel_size; static int esel_index = 0; int pel_index; static unsigned int reservation_id = 0; static unsigned int record_id = 0; struct errorlog *elog_buf = (struct errorlog *) msg->user_data; size_t req_size; if (bmc_platform->sw->ipmi_oem_partial_add_esel == 0) { prlog(PR_WARNING, "Dropped eSEL: BMC code is buggy/missing\n"); return; } ipmi_init_esel_record(); if (msg->cmd == IPMI_CMD(IPMI_RESERVE_SEL)) { first = true; reservation_id = msg->data[0]; reservation_id |= msg->data[1] << 8; if (!reservation_id) { /* * According to specification we should never * get here, but just in case we do we cancel * sending the message. */ prerror("Invalid reservation id"); opal_elog_complete(elog_buf, false); ipmi_sel_free_msg(msg); return; } pel_size = create_pel_log(elog_buf, pel_buf, IPMI_MAX_PEL_SIZE); esel_size = pel_size + sizeof(struct sel_record); esel_index = 0; record_id = 0; } else { record_id = msg->data[0]; record_id |= msg->data[1] << 8; } /* Start or continue the IPMI_PARTIAL_ADD_SEL */ if (esel_index >= esel_size) { /* * We're all done. Invalidate the resevation id to * ensure we get an error if we cut in on another eSEL * message. */ reservation_id = 0; esel_index = 0; /* Log SEL event and free ipmi message */ ipmi_log_sel_event(msg, elog_buf->event_severity, record_id); opal_elog_complete(elog_buf, true); return; } if ((esel_size - esel_index) <= (IPMI_MAX_REQ_SIZE - ESEL_HDR_SIZE)) { /* Last data to send */ msg->data[6] = 1; req_size = esel_size - esel_index + ESEL_HDR_SIZE; } else { msg->data[6] = 0; req_size = IPMI_MAX_REQ_SIZE; } ipmi_init_msg(msg, IPMI_DEFAULT_INTERFACE, bmc_platform->sw->ipmi_oem_partial_add_esel, ipmi_elog_poll, elog_buf, req_size, 2); msg->data[0] = reservation_id & 0xff; msg->data[1] = (reservation_id >> 8) & 0xff; msg->data[2] = record_id & 0xff; msg->data[3] = (record_id >> 8) & 0xff; msg->data[4] = esel_index & 0xff; msg->data[5] = (esel_index >> 8) & 0xff; if (first) { first = false; memcpy(&msg->data[ESEL_HDR_SIZE], &sel_record, sizeof(struct sel_record)); esel_index = sizeof(struct sel_record); msg->req_size = esel_index + ESEL_HDR_SIZE; } else { pel_index = esel_index - sizeof(struct sel_record); memcpy(&msg->data[ESEL_HDR_SIZE], &pel_buf[pel_index], msg->req_size - ESEL_HDR_SIZE); esel_index += msg->req_size - ESEL_HDR_SIZE; } ipmi_queue_msg_head(msg); return; }