initial commit
Add initial code from phosphor-ipmi-flash/tools that was not specific to
firmware update over ipmi-blobs.
Change-Id: I360537a7392347fe989397a699f6a712bc36e62c
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/src/ipmiblob/ipmi_handler.cpp b/src/ipmiblob/ipmi_handler.cpp
new file mode 100644
index 0000000..9278338
--- /dev/null
+++ b/src/ipmiblob/ipmi_handler.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * 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 "ipmi_handler.hpp"
+
+#include "ipmi_errors.hpp"
+
+#include <fcntl.h>
+#include <linux/ipmi.h>
+#include <linux/ipmi_msgdefs.h>
+#include <sys/ioctl.h>
+
+#include <array>
+#include <cstdint>
+#include <cstring>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace host_tool
+{
+
+void IpmiHandler::open()
+{
+ const int device = 0;
+ const std::vector<std::string> formats = {"/dev/ipmi", "/dev/ipmi/",
+ "/dev/ipmidev/"};
+
+ for (const auto& format : formats)
+ {
+ std::ostringstream path;
+ path << format << device;
+
+ fd = sys->open(path.str().c_str(), O_RDWR);
+ if (fd < 0)
+ {
+ continue;
+ }
+ break;
+ }
+
+ if (fd < 0)
+ {
+ throw IpmiException("Unable to open any ipmi devices");
+ }
+}
+
+std::vector<std::uint8_t>
+ IpmiHandler::sendPacket(std::vector<std::uint8_t>& data)
+{
+ if (fd < 0)
+ {
+ open();
+ }
+
+ constexpr int ipmiOEMNetFn = 46;
+ constexpr int ipmiOEMLun = 0;
+ /* /openbmc/phosphor-host-ipmid/blob/master/host-ipmid/oemopenbmc.hpp */
+ constexpr int ipmiOEMBlobCmd = 128;
+ constexpr int fifteenMs = 15 * 1000;
+ constexpr int ipmiReadTimeout = fifteenMs;
+ constexpr int ipmiResponseBufferLen = IPMI_MAX_MSG_LENGTH;
+ constexpr int ipmiOk = 0;
+
+ /* We have a handle to the IPMI device. */
+ std::array<std::uint8_t, ipmiResponseBufferLen> responseBuffer;
+
+ /* Build address. */
+ struct ipmi_system_interface_addr systemAddress;
+ systemAddress.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+ systemAddress.channel = IPMI_BMC_CHANNEL;
+ systemAddress.lun = ipmiOEMLun;
+
+ /* Build request. */
+ struct ipmi_req request;
+ std::memset(&request, 0, sizeof(request));
+ request.addr = reinterpret_cast<unsigned char*>(&systemAddress);
+ request.addr_len = sizeof(systemAddress);
+ request.msgid = sequence++;
+ request.msg.data = reinterpret_cast<unsigned char*>(data.data());
+ request.msg.data_len = data.size();
+ request.msg.netfn = ipmiOEMNetFn;
+ request.msg.cmd = ipmiOEMBlobCmd;
+
+ struct ipmi_recv reply;
+ reply.addr = reinterpret_cast<unsigned char*>(&systemAddress);
+ reply.addr_len = sizeof(systemAddress);
+ reply.msg.data = reinterpret_cast<unsigned char*>(responseBuffer.data());
+ reply.msg.data_len = responseBuffer.size();
+
+ /* Try to send request. */
+ int rc = sys->ioctl(fd, IPMICTL_SEND_COMMAND, &request);
+ if (rc < 0)
+ {
+ throw IpmiException("Unable to send IPMI request.");
+ }
+
+ /* Could use sdeventplus, but for only one type of event is it worth it? */
+ struct pollfd pfd;
+ pfd.fd = fd;
+ pfd.events = POLLIN;
+
+ do
+ {
+ rc = sys->poll(&pfd, 1, ipmiReadTimeout);
+ if (rc < 0)
+ {
+ if (errno == EINTR)
+ {
+ continue;
+ }
+ throw IpmiException("Error occurred.");
+ }
+ else if (rc == 0)
+ {
+ throw IpmiException("Timeout waiting for reply.");
+ }
+
+ /* Yay, happy case! */
+ rc = sys->ioctl(fd, IPMICTL_RECEIVE_MSG_TRUNC, &reply);
+ if (rc < 0)
+ {
+ throw IpmiException("Unable to read reply.");
+ }
+
+ if (request.msgid != reply.msgid)
+ {
+ std::fprintf(stderr, "Received wrong message, trying again.\n");
+ }
+ } while (request.msgid != reply.msgid);
+
+ if (responseBuffer[0] != ipmiOk)
+ {
+ throw IpmiException(static_cast<int>(responseBuffer[0]));
+ }
+
+ std::vector<std::uint8_t> returning;
+ auto dataLen = reply.msg.data_len - 1;
+
+ returning.insert(returning.begin(), responseBuffer.begin() + 1,
+ responseBuffer.begin() + dataLen + 1);
+
+ for (const auto& byte : returning)
+ {
+ std::fprintf(stderr, "0x%02x ", byte);
+ }
+ std::fprintf(stderr, "\n");
+
+ return returning;
+}
+
+} // namespace host_tool