diff options
author | Alistair Popple <alistair@popple.id.au> | 2014-11-13 17:16:05 +1100 |
---|---|---|
committer | Stewart Smith <stewart@linux.vnet.ibm.com> | 2014-12-02 18:43:26 +1100 |
commit | dd173e4beaf1da11db3bcfb2ccdfe8a623f26e53 (patch) | |
tree | f9524e195ecb3ee70032a204089260bc61706588 /hw/ipmi | |
parent | f8a0bb99bb6879124f153a161339bbfdef2049bf (diff) | |
download | skiboot-dd173e4beaf1da11db3bcfb2ccdfe8a623f26e53.zip skiboot-dd173e4beaf1da11db3bcfb2ccdfe8a623f26e53.tar.gz skiboot-dd173e4beaf1da11db3bcfb2ccdfe8a623f26e53.tar.bz2 |
esel: Add a logging backend for bmc based machines
This patch adds a backend for bmc based machines running AMI firmware
supporting the OEM extended sel commands. Errors are logged in pel
format to the bmc.
Signed-off-by: Alistair Popple <alistair@popple.id.au>
Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Diffstat (limited to 'hw/ipmi')
-rw-r--r-- | hw/ipmi/Makefile.inc | 2 | ||||
-rw-r--r-- | hw/ipmi/ipmi-sel.c | 150 |
2 files changed, 151 insertions, 1 deletions
diff --git a/hw/ipmi/Makefile.inc b/hw/ipmi/Makefile.inc index 2d9f41f..02670d7 100644 --- a/hw/ipmi/Makefile.inc +++ b/hw/ipmi/Makefile.inc @@ -1,5 +1,5 @@ SUBDIRS += hw/ipmi -IPMI_OBJS = ipmi-rtc.o ipmi-power.o ipmi-opal.o ipmi-fru.o +IPMI_OBJS = ipmi-rtc.o ipmi-power.o ipmi-opal.o ipmi-fru.o ipmi-sel.o IPMI = hw/ipmi/built-in.o $(IPMI): $(IPMI_OBJS:%=hw/ipmi/%) diff --git a/hw/ipmi/ipmi-sel.c b/hw/ipmi/ipmi-sel.c new file mode 100644 index 0000000..d6f7f47 --- /dev/null +++ b/hw/ipmi/ipmi-sel.c @@ -0,0 +1,150 @@ +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <ipmi.h> +#include <device.h> +#include <opal.h> +#include <lock.h> +#include <errorlog.h> +#include <pel.h> + +/* As far as I can tell the size of PEL record is unbounded (due to + * the possible presence of the user defined section). We chose this + * size because it's what FSP uses, but we could probably reduce + * it. */ +#define MAX_PEL_SIZE 0x10000 + +#define ESEL_HDR_SIZE 7 + +static void ipmi_elog_error(struct ipmi_msg *msg) +{ + if (msg->cc == IPMI_LOST_ARBITRATION_ERR) + /* Retry due to SEL erase */ + ipmi_queue_msg(msg); + else + opal_elog_complete(msg->user_data, false); + + ipmi_free_msg(msg); +} + +/* Goes through the required steps to add a complete eSEL: + * + * 1. Get a reservation + * 2. 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 char pel_buf[MAX_PEL_SIZE]; + static size_t pel_size; + static int index = 0; + static unsigned int reservation_id = 0; + static unsigned int record_id = 0; + struct errorlog *elog_buf = (struct errorlog *) msg->user_data; + + if (msg->cmd == IPMI_CMD(IPMI_RESERVE_SEL)) { + 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_free_msg(msg); + return; + } + + pel_size = create_pel_log(elog_buf, pel_buf, MAX_PEL_SIZE); + 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 (index >= pel_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; + index = 0; + opal_elog_complete(elog_buf, true); + ipmi_free_msg(msg); + return; + } + + msg->cmd = IPMI_CMD(IPMI_PARTIAL_ADD_ESEL); + msg->netfn = IPMI_NETFN(IPMI_PARTIAL_ADD_ESEL) << 2; + msg->resp_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] = index & 0xff; + msg->data[5] = (index >> 8) & 0xff; + + if ((pel_size - index) < (IPMI_MAX_REQ_SIZE - ESEL_HDR_SIZE)) { + /* Last data to send */ + msg->data[6] = 1; + msg->req_size = pel_size - index + ESEL_HDR_SIZE; + } else { + msg->data[6] = 0; + msg->req_size = IPMI_MAX_REQ_SIZE; + } + + memcpy(&msg->data[ESEL_HDR_SIZE], &pel_buf[index], msg->req_size - ESEL_HDR_SIZE); + index += msg->req_size - ESEL_HDR_SIZE; + + ipmi_queue_msg_head(msg); + return; +} + +int ipmi_elog_commit(struct errorlog *elog_buf) +{ + struct ipmi_msg *msg; + + /* We pass a large request size in to mkmsg so that we have a + * large enough allocation to reuse the message to pass the + * PEL data via a series of partial add commands. */ + msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_RESERVE_SEL, ipmi_elog_poll, + elog_buf, NULL, IPMI_MAX_REQ_SIZE, 2); + if (!msg) { + opal_elog_complete(elog_buf, false); + return OPAL_RESOURCE; + } + msg->error = ipmi_elog_error; + + msg->req_size = 0; + ipmi_queue_msg(msg); + + return 0; +} |