예제 #1
0
/*
 * 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;
}
예제 #2
0
/* 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);
}
예제 #3
0
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);
}
예제 #4
0
/* 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;
}