aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlistair Popple <alistair@popple.id.au>2014-11-13 17:16:05 +1100
committerStewart Smith <stewart@linux.vnet.ibm.com>2014-12-02 18:43:26 +1100
commitdd173e4beaf1da11db3bcfb2ccdfe8a623f26e53 (patch)
treef9524e195ecb3ee70032a204089260bc61706588
parentf8a0bb99bb6879124f153a161339bbfdef2049bf (diff)
downloadskiboot-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>
-rw-r--r--external/read_esel.sh55
-rw-r--r--hw/ipmi/Makefile.inc2
-rw-r--r--hw/ipmi/ipmi-sel.c150
-rw-r--r--include/ipmi.h8
-rw-r--r--platforms/astbmc/common.c2
-rw-r--r--platforms/astbmc/palmetto.c2
6 files changed, 218 insertions, 1 deletions
diff --git a/external/read_esel.sh b/external/read_esel.sh
new file mode 100644
index 0000000..492fbce
--- /dev/null
+++ b/external/read_esel.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+# 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.
+set -e
+
+BMC_HOST=$1
+RECORD_ID=$2
+
+BMC_USER=admin
+BMC_PASS=admin
+
+if [ -z "$BMC_HOST" -o -z "$RECORD_ID" ]; then
+ echo "Usage: $0 <bmc hostname> <record id>"
+ echo "Example: $0 bmc 0xa > pel.bin"
+ echo ''
+ echo 'Record ids can be found using ipmitool with the "sel list" command. Records with'
+ echo 'a description of "OEM record df" contain extended SEL information (in PEL'
+ echo 'format) which can be extracted with this tool.'
+ exit -1
+fi
+
+# Convert a number into 2 hex-bytes in little-endian order
+function conv_le {
+ echo $(for i in $(printf %04x $1 | grep -o .. | tac); do echo -n "0x$i "; done)
+}
+
+function conv_native {
+ echo -n "0x${2}${1}"
+}
+
+record=$(conv_le $2)
+offset=0
+progress=0
+
+while [ $progress = 0 ]; do
+ result=$(ipmitool -H ${BMC_HOST} -I lan -U ${BMC_USER} -P ${BMC_PASS} raw 0x32 0xf1 ${record} $(conv_le ${offset}))
+ len=$(conv_native $(echo ${result} | cut -d " " -f 1-2))
+ progress=$(($(echo ${result} | cut -d " " -f 3)))
+ data="$data "$(echo -n ${result} | cut -d " " -f 6-)
+ offset=$(($offset + ${#data}/3))
+done
+
+echo -n ${data} | cut -d " " -f 1-$(($len)) | xxd -r -p
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;
+}
diff --git a/include/ipmi.h b/include/ipmi.h
index 38770ab..50de293 100644
--- a/include/ipmi.h
+++ b/include/ipmi.h
@@ -83,15 +83,19 @@
#define IPMI_NETFN_CHASSIS 0x00
#define IPMI_NETFN_STORAGE 0x0a
#define IPMI_NETFN_APP 0x06
+#define IPMI_NETFN_OEM 0x32
#define IPMI_WRITE_FRU IPMI_CODE(IPMI_NETFN_STORAGE, 0x12)
#define IPMI_GET_SEL_INFO IPMI_CODE(IPMI_NETFN_STORAGE, 0x40)
+#define IPMI_RESERVE_SEL IPMI_CODE(IPMI_NETFN_STORAGE, 0x42)
#define IPMI_GET_SEL_TIME IPMI_CODE(IPMI_NETFN_STORAGE, 0x48)
#define IPMI_SET_SEL_TIME IPMI_CODE(IPMI_NETFN_STORAGE, 0x49)
#define IPMI_CHASSIS_CONTROL IPMI_CODE(IPMI_NETFN_CHASSIS, 0x02)
#define IPMI_SET_POWER_STATE IPMI_CODE(IPMI_NETFN_APP, 0x06)
#define IPMI_GET_POWER_STATE IPMI_CODE(IPMI_NETFN_APP, 0x07)
+#define IPMI_PARTIAL_ADD_ESEL IPMI_CODE(IPMI_NETFN_OEM, 0xf0)
+
/*
* IPMI response codes.
*/
@@ -187,4 +191,8 @@ void ipmi_opal_init(void);
/* Populate fru data */
void ipmi_fru_init(uint8_t fru_dev_id);
+/* Commit an error log to the bmc using the OEM add eSEL commands */
+struct errorlog;
+int ipmi_elog_commit(struct errorlog *elog_buf);
+
#endif
diff --git a/platforms/astbmc/common.c b/platforms/astbmc/common.c
index d6b5b07..26f90f5 100644
--- a/platforms/astbmc/common.c
+++ b/platforms/astbmc/common.c
@@ -24,6 +24,7 @@
#include <ast.h>
#include <ipmi.h>
#include <bt.h>
+#include <errorlog.h>
#include "astbmc.h"
@@ -53,6 +54,7 @@ void astbmc_init(void)
ipmi_rtc_init();
ipmi_opal_init();
ipmi_fru_init(0x01);
+ elog_init();
/* As soon as IPMI is up, inform BMC we are in "S0" */
ipmi_set_power_state(IPMI_PWR_SYS_S0_WORKING, IPMI_PWR_NOCHANGE);
diff --git a/platforms/astbmc/palmetto.c b/platforms/astbmc/palmetto.c
index 99eb962..9c8be9f 100644
--- a/platforms/astbmc/palmetto.c
+++ b/platforms/astbmc/palmetto.c
@@ -19,6 +19,7 @@
#include <device.h>
#include <console.h>
#include <chip.h>
+#include <ipmi.h>
#include "astbmc.h"
@@ -49,4 +50,5 @@ DECLARE_PLATFORM(palmetto) = {
.external_irq = astbmc_ext_irq,
.cec_power_down = astbmc_ipmi_power_down,
.cec_reboot = astbmc_ipmi_reboot,
+ .elog_commit = ipmi_elog_commit,
};