tools: starting burn_my_bmc host utility
burn_my_bmc is a host-run utility that in cooperation with the
phosphor-ipmi-flash library, attempts to send a BMC firmware image and
signature file to the BMC and trigger verification of said image.
The program's current design and use were tailored towards the legacy
OpenBMC image and not UBI. Therefore, changes to support the UBI
process will get addressed as it all takes shape.
The overall process is:
1) Attempts to send firmware image over an interface.
2) Attempts to send signature file contents over an interface*.
3) Triggers a verification step.
4) Reboots the BMC**.
* The only interface in the initial version here is the blocktransfer
interface. It's technically also possibly KCS. It's sending the data
over the same communications channel as the normal IPMI packets. A
later patchset will enable sending the data bytes over an LPC memory
region or the PCI P2A region.
** The 4th step is done by a separate call to the 'reboot' command.
The 'reboot' and 'ping' commands will come in a later patchset.
Change-Id: I62d725274e56c55ca414fa6c2a3eab6c500066ed
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/tools/libs/ipmitool b/tools/libs/ipmitool
new file mode 160000
index 0000000..232773d
--- /dev/null
+++ b/tools/libs/ipmitool
@@ -0,0 +1 @@
+Subproject commit 232773d171a8f5a929d02744e952bbfbe87c1b8e
diff --git a/tools/libs/ipmitool.c b/tools/libs/ipmitool.c
new file mode 100644
index 0000000..3b64a35
--- /dev/null
+++ b/tools/libs/ipmitool.c
@@ -0,0 +1,244 @@
+/*
+Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+Redistribution of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+Redistribution in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+Neither the name of Sun Microsystems, Inc. or the names of
+contributors may be used to endorse or promote products derived
+from this software without specific prior written permission.
+
+This software is provided "AS IS," without a warranty of any kind.
+ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
+SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
+FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
+SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
+OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
+PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
+LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+#include <ipmitool/ipmi_intf.h>
+#include <ipmitool/ipmi_mc.h>
+#include <ipmitool/log.h>
+
+/* Duplicate this into our memory space. Nothing in our code path calls this
+ * in a critical path.
+ */
+static IPMI_OEM sel_iana = IPMI_OEM_UNKNOWN;
+
+const struct valstr completion_code_vals[] = {
+ {0x00, "Command completed normally"},
+ {0xc0, "Node busy"},
+ {0xc1, "Invalid command"},
+ {0xc2, "Invalid command on LUN"},
+ {0xc3, "Timeout"},
+ {0xc4, "Out of space"},
+ {0xc5, "Reservation cancelled or invalid"},
+ {0xc6, "Request data truncated"},
+ {0xc7, "Request data length invalid"},
+ {0xc8, "Request data field length limit exceeded"},
+ {0xc9, "Parameter out of range"},
+ {0xca, "Cannot return number of requested data bytes"},
+ {0xcb, "Requested sensor, data, or record not found"},
+ {0xcc, "Invalid data field in request"},
+ {0xcd, "Command illegal for specified sensor or record type"},
+ {0xce, "Command response could not be provided"},
+ {0xcf, "Cannot execute duplicated request"},
+ {0xd0, "SDR Repository in update mode"},
+ {0xd1, "Device firmeware in update mode"},
+ {0xd2, "BMC initialization in progress"},
+ {0xd3, "Destination unavailable"},
+ {0xd4, "Insufficient privilege level"},
+ {0xd5, "Command not supported in present state"},
+ {0xd6, "Cannot execute command, command disabled"},
+ {0xff, "Unspecified error"},
+ {0x00, NULL}};
+
+const char* val2str(uint16_t val, const struct valstr* vs)
+{
+ static char un_str[32];
+ int i;
+
+ for (i = 0; vs[i].str != NULL; i++)
+ {
+ if (vs[i].val == val)
+ return vs[i].str;
+ }
+
+ memset(un_str, 0, 32);
+ snprintf(un_str, 32, "Unknown (0x%02X)", val);
+
+ return un_str;
+}
+
+void ipmi_intf_session_set_timeout(struct ipmi_intf* intf, uint32_t timeout)
+{
+ intf->ssn_params.timeout = timeout;
+}
+
+void ipmi_intf_session_set_retry(struct ipmi_intf* intf, int retry)
+{
+ intf->ssn_params.retry = retry;
+}
+
+/* Nullify the methods we don't care about. */
+void lprintf(int level, const char* format, ...)
+{
+ return;
+}
+void lperror(int level, const char* format, ...)
+{
+ return;
+}
+
+int verbose = 0;
+
+const char* buf2str_extended(const uint8_t* buf, int len, const char* sep)
+{
+ static char str[BUF2STR_MAXIMUM_OUTPUT_SIZE];
+ char* cur;
+ int i;
+ int sz;
+ int left;
+ int sep_len;
+
+ if (buf == NULL)
+ {
+ snprintf(str, sizeof(str), "<NULL>");
+ return (const char*)str;
+ }
+ cur = str;
+ left = sizeof(str);
+ if (sep)
+ {
+ sep_len = strlen(sep);
+ }
+ else
+ {
+ sep_len = 0;
+ }
+ for (i = 0; i < len; i++)
+ {
+ /* may return more than 2, depending on locale */
+ sz = snprintf(cur, left, "%2.2x", buf[i]);
+ if (sz >= left)
+ {
+ /* buffer overflow, truncate */
+ break;
+ }
+ cur += sz;
+ left -= sz;
+ /* do not write separator after last byte */
+ if (sep && i != (len - 1))
+ {
+ if (sep_len >= left)
+ {
+ break;
+ }
+ strncpy(cur, sep, left - sz);
+ cur += sep_len;
+ left -= sep_len;
+ }
+ }
+ *cur = '\0';
+
+ return (const char*)str;
+}
+
+const char* buf2str(const uint8_t* buf, int len)
+{
+ return buf2str_extended(buf, len, NULL);
+}
+
+uint8_t ipmi_csum(uint8_t* d, int s)
+{
+ uint8_t c = 0;
+ for (; s > 0; s--, d++)
+ c += *d;
+ return -c;
+}
+
+void printbuf(const uint8_t* buf, int len, const char* desc)
+{
+ int i;
+
+ if (len <= 0)
+ return;
+
+ if (verbose < 1)
+ return;
+
+ fprintf(stderr, "%s (%d bytes)\n", desc, len);
+ for (i = 0; i < len; i++)
+ {
+ if (((i % 16) == 0) && (i != 0))
+ fprintf(stderr, "\n");
+ fprintf(stderr, " %2.2x", buf[i]);
+ }
+ fprintf(stderr, "\n");
+}
+
+IPMI_OEM
+ipmi_get_oem(struct ipmi_intf* intf)
+{
+ /* Execute a Get Device ID command to determine the OEM */
+ struct ipmi_rs* rsp;
+ struct ipmi_rq req;
+ struct ipm_devid_rsp* devid;
+
+ if (intf->fd == 0)
+ {
+ if (sel_iana != IPMI_OEM_UNKNOWN)
+ {
+ return sel_iana;
+ }
+ return IPMI_OEM_UNKNOWN;
+ }
+
+ /*
+ * Return the cached manufacturer id if the device is open and
+ * we got an identified OEM owner. Otherwise just attempt to read
+ * it.
+ */
+ if (intf->opened && intf->manufacturer_id != IPMI_OEM_UNKNOWN)
+ {
+ return intf->manufacturer_id;
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_APP;
+ req.msg.cmd = BMC_GET_DEVICE_ID;
+ req.msg.data_len = 0;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL)
+ {
+ lprintf(LOG_ERR, "Get Device ID command failed");
+ return IPMI_OEM_UNKNOWN;
+ }
+ if (rsp->ccode > 0)
+ {
+ lprintf(LOG_ERR, "Get Device ID command failed: %#x %s", rsp->ccode,
+ val2str(rsp->ccode, completion_code_vals));
+ return IPMI_OEM_UNKNOWN;
+ }
+
+ devid = (struct ipm_devid_rsp*)rsp->data;
+
+ lprintf(LOG_DEBUG, "Iana: %u",
+ IPM_DEV_MANUFACTURER_ID(devid->manufacturer_id));
+
+ return IPM_DEV_MANUFACTURER_ID(devid->manufacturer_id);
+}
diff --git a/tools/libs/ipmitoolintf.c b/tools/libs/ipmitoolintf.c
new file mode 100644
index 0000000..b68d454
--- /dev/null
+++ b/tools/libs/ipmitoolintf.c
@@ -0,0 +1,65 @@
+#include "ipmitoolintf.h"
+
+#include <ipmitool/ipmi_intf.h>
+#include <string.h>
+
+extern struct ipmi_intf ipmi_open_intf;
+
+int ipmiSendCommand(const uint8_t* bytes, int length, struct IpmiResponse* resp)
+{
+ struct ipmi_intf* intf = &ipmi_open_intf;
+
+ /* The length needs to be at least two bytes [netfn][cmd] */
+ if (length < 2)
+ {
+ return -1;
+ }
+
+ uint8_t data[MAX_PIPELINE_BANDWIDTH];
+ struct ipmi_rq request;
+ memset(&data[0], 0, sizeof(data));
+ memset(&request, 0, sizeof(request));
+
+ ipmi_intf_session_set_timeout(intf, 15);
+ /* The retry here isn't used for the normal BT interface comms it appears.
+ * Only references found in sol and serial, and lan.
+ */
+ ipmi_intf_session_set_retry(intf, 1);
+
+ request.msg.netfn = bytes[0];
+ request.msg.lun = 0x00;
+ request.msg.cmd = bytes[1];
+ request.msg.data = &data[0];
+
+ /* Can you fit the request in the buffer? */
+ if ((length - 2) > sizeof(data))
+ {
+ return -1;
+ }
+
+ /* Skip beyond netfn and command. */
+ memcpy(request.msg.data, &bytes[2], length - 2);
+ request.msg.data_len = length - 2;
+
+ /* Actually send the command and check for a response. */
+ struct ipmi_rs* response = intf->sendrecv(intf, &request);
+ if (!response)
+ {
+ return -1;
+ }
+
+ /* If the caller wanted the response back. */
+ if (resp)
+ {
+ resp->ccode = response->ccode;
+ if (response->data_len <= sizeof(resp->data))
+ {
+ memcpy(resp->data, response->data, response->data_len);
+ resp->dataLen = response->data_len;
+ return 0;
+ }
+ /* TODO: deal with truncation... */
+ }
+
+ return 0;
+}
diff --git a/tools/libs/ipmitoolintf.h b/tools/libs/ipmitoolintf.h
new file mode 100644
index 0000000..c4994fd
--- /dev/null
+++ b/tools/libs/ipmitoolintf.h
@@ -0,0 +1,28 @@
+#pragma once
+
+/* The Aspeed AST2400 & AST2500 have 64 bytes of SRAM as the FIFO for each
+ * direction, of which 2 bytes are reserved for len and for seq by upper layer
+ * ipmi driver.
+ */
+#define MAX_PIPELINE_BANDWIDTH 62
+#define IPMI_BUF_SIZE 1024
+
+#include <stdint.h>
+
+struct IpmiResponse
+{
+ uint8_t ccode;
+ uint8_t data[IPMI_BUF_SIZE];
+ int dataLen;
+};
+
+/**
+ * Call into the ipmitool source to send the IPMI packet.
+ *
+ * @param[in] bytes - the IPMI packet contents.
+ * @param[in] length - the number of bytes.
+ * @param[in,out] resp - a pointer to write the response.
+ * @return 0 on success.
+ */
+int ipmiSendCommand(const uint8_t* bytes, int length,
+ struct IpmiResponse* resp);