summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile.am6
-rw-r--r--configure.ac2
-rw-r--r--flash-ipmi.cpp39
-rw-r--r--flash-ipmi.hpp54
-rw-r--r--ipmi.cpp40
-rw-r--r--ipmi.hpp18
-rw-r--r--main.cpp22
-rw-r--r--test/Makefile.am18
-rw-r--r--test/ipmi_starttransfer_unittest.cpp61
-rw-r--r--test/updater_mock.hpp13
11 files changed, 271 insertions, 3 deletions
diff --git a/.gitignore b/.gitignore
index 80f3f36..81cb1b9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,4 @@ x86_64-libtool
*_unittest
*_unittest.log
*_unittest.trs
+test-suite.log
diff --git a/Makefile.am b/Makefile.am
index 05f493e..0b76d38 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,7 +2,9 @@ AM_DEFAULT_SOURCE_EXT = .cpp
libflashcmdsdir = ${libdir}/ipmid-providers
libflashcmds_LTLIBRARIES = libflashcmds.la
-libflashcmds_la_SOURCES = main.cpp
+libflashcmds_la_SOURCES = main.cpp \
+ flash-ipmi.cpp \
+ ipmi.cpp
libflashcmds_la_LDFLAGS = $(SYSTEMD_LIBS) \
$(PHOSPHOR_DBUS_INTERFACES_LIBS) \
@@ -12,4 +14,4 @@ libflashcmds_la_CXXFLAGS = $(SYSTEMD_CFLAGS) \
$(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
$(PHOSPHOR_LOGGING_CFLAGS)
-SUBDIRS = .
+SUBDIRS = . test
diff --git a/configure.ac b/configure.ac
index 0add2b8..0fe7382 100644
--- a/configure.ac
+++ b/configure.ac
@@ -69,5 +69,5 @@ AS_IF([test "x$enable_oe_sdk" == "xyes"],
)
# Create configured output
-AC_CONFIG_FILES([Makefile])
+AC_CONFIG_FILES([Makefile test/Makefile])
AC_OUTPUT
diff --git a/flash-ipmi.cpp b/flash-ipmi.cpp
new file mode 100644
index 0000000..4d05044
--- /dev/null
+++ b/flash-ipmi.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 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 "flash-ipmi.hpp"
+
+void FlashUpdate::abortEverything()
+{
+ return;
+}
+
+bool FlashUpdate::openEverything()
+{
+ return true;
+}
+
+/* Prepare to receive a BMC image and then a signature. */
+bool FlashUpdate::start(uint32_t)
+{
+ /* TODO: Validate request->length */
+
+ /* Close out and delete everything. */
+ abortEverything();
+
+ /* Start over! */
+ return openEverything();
+}
diff --git a/flash-ipmi.hpp b/flash-ipmi.hpp
index 43cafe1..deef582 100644
--- a/flash-ipmi.hpp
+++ b/flash-ipmi.hpp
@@ -1,5 +1,10 @@
#pragma once
+#include "host-ipmid/ipmid-api.h"
+
+/* Clearer way to represent the subcommand size. */
+#define SUBCMD_SZ sizeof(uint8_t)
+
/*
* flashStartTransfer -- starts file upload.
* flashDataBlock -- adds data to image file.
@@ -55,3 +60,52 @@ enum FlashSubCmds
flashHashExtData = 12,
flashMapRegionLpc = 13,
};
+
+/*
+ * StartTransfer expects a basic structure providing some information.
+ */
+struct StartTx
+{
+ uint8_t cmd;
+ uint32_t length; /* Maximum image length is 4GiB (little-endian) */
+} __attribute__((packed));
+
+class UpdateInterface
+{
+ public:
+ virtual ~UpdateInterface() = default;
+
+ virtual bool start(uint32_t length) = 0;
+};
+
+class FlashUpdate : public UpdateInterface
+{
+ public:
+ FlashUpdate() = default;
+ ~FlashUpdate() = default;
+ FlashUpdate(const FlashUpdate&) = default;
+ FlashUpdate& operator=(const FlashUpdate&) = default;
+ FlashUpdate(FlashUpdate&&) = default;
+ FlashUpdate& operator=(FlashUpdate&&) = default;
+
+ /**
+ * Prepare to receive a BMC image and then a signature.
+ *
+ * @param[in] length - the size of the flash image.
+ * @return true on success, false otherwise.
+ */
+ bool start(uint32_t length) override;
+
+ private:
+ /**
+ * Tries to close out and delete anything staged.
+ */
+ void abortEverything();
+
+ /**
+ * Open all staged file handles you expect to use.
+ *
+ * @return false on failure.
+ */
+ bool openEverything();
+};
diff --git a/ipmi.cpp b/ipmi.cpp
new file mode 100644
index 0000000..65a719a
--- /dev/null
+++ b/ipmi.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017 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 "flash-ipmi.hpp"
+#include "ipmi.hpp"
+
+ipmi_ret_t startTransfer(UpdateInterface* updater, const uint8_t* reqBuf,
+ uint8_t* replyBuf, size_t* dataLen)
+{
+ /* Validate the request buffer. */
+ if (sizeof(struct StartTx) > (*dataLen))
+ {
+ return IPMI_CC_INVALID;
+ }
+
+ auto request = reinterpret_cast<const struct StartTx*>(reqBuf);
+
+ if (!updater->start(request->length))
+ {
+ return IPMI_CC_INVALID;
+ }
+
+ /* We were successful and set the response byte to 0. */
+ replyBuf[0] = 0x00;
+ (*dataLen) = 1;
+ return IPMI_CC_OK;
+}
diff --git a/ipmi.hpp b/ipmi.hpp
new file mode 100644
index 0000000..3b622b0
--- /dev/null
+++ b/ipmi.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "host-ipmid/ipmid-api.h"
+
+#include "flash-ipmi.hpp"
+
+/**
+ * Prepare to receive a BMC image and then a signature.
+ *
+ * @param[in] updater - Pointer to Updater object.
+ * @param[in] reqBuf - the IPMI packet.
+ * @param[in] replyBuf - Pointer to buffer for any response.
+ * @param[in,out] dataLen - Initially reqBuf length, set to replyBuf
+ * length when done.
+ * @return corresponding IPMI return code.
+ */
+ipmi_ret_t startTransfer(UpdateInterface* updater, const uint8_t* reqBuf,
+ uint8_t* replyBuf, size_t* dataLen);
diff --git a/main.cpp b/main.cpp
index 8a6bac1..a3288ef 100644
--- a/main.cpp
+++ b/main.cpp
@@ -14,10 +14,13 @@
* limitations under the License.
*/
+#include <memory>
+
#include "host-ipmid/ipmid-api.h"
#include "host-ipmid/oemrouter.hpp"
#include "flash-ipmi.hpp"
+#include "ipmi.hpp"
/* TODO: Once OEM IPMI number placement is settled, point to that. */
namespace oem
@@ -29,6 +32,9 @@ constexpr int flashOverBTCmd = 127;
} // namespace google
} // namespace oem
+/* We have one instance of the FlashUpdate object tracking commands. */
+std::unique_ptr<FlashUpdate> flashUpdateSingleton;
+
static ipmi_ret_t flashControl(ipmi_cmd_t cmd, const uint8_t* reqBuf,
uint8_t* replyCmdBuf, size_t* dataLen)
{
@@ -38,6 +44,20 @@ static ipmi_ret_t flashControl(ipmi_cmd_t cmd, const uint8_t* reqBuf,
return IPMI_CC_INVALID;
}
+ uint8_t subCmd = reqBuf[0];
+
+ /* TODO: This could be cleaner to just have a function pointer table, may
+ * transition in later patchset.
+ */
+ switch (subCmd)
+ {
+ case FlashSubCmds::flashStartTransfer:
+ return startTransfer(flashUpdateSingleton.get(), reqBuf,
+ replyCmdBuf, dataLen);
+ default:
+ return IPMI_CC_INVALID;
+ }
+
return IPMI_CC_INVALID;
}
@@ -58,6 +78,8 @@ void setupGlobalOemFlashControl() __attribute__((constructor));
void setupGlobalOemFlashControl()
{
+ flashUpdateSingleton = std::make_unique<FlashUpdate>();
+
#ifdef ENABLE_GOOGLE
oem::Router* router = oem::mutableRouter();
diff --git a/test/Makefile.am b/test/Makefile.am
new file mode 100644
index 0000000..7da78d8
--- /dev/null
+++ b/test/Makefile.am
@@ -0,0 +1,18 @@
+AM_CPPFLAGS = -I$(top_srcdir)/ \
+ $(GTEST_CFLAGS) \
+ $(GMOCK_CFLAGS)
+AM_CXXFLAGS = \
+ $(GTEST_MAIN_CFLAGS)
+AM_LDFLAGS = \
+ $(GMOCK_LIBS) \
+ $(GTEST_MAIN_LIBS) \
+ $(OESDK_TESTCASE_FLAGS)
+
+# Run all 'check' test programs
+check_PROGRAMS = \
+ ipmi_starttransfer_unittest
+
+TESTS = $(check_PROGRAMS)
+
+ipmi_starttransfer_unittest_SOURCES = ipmi_starttransfer_unittest.cpp
+ipmi_starttransfer_unittest_LDADD = $(top_builddir)/ipmi.o
diff --git a/test/ipmi_starttransfer_unittest.cpp b/test/ipmi_starttransfer_unittest.cpp
new file mode 100644
index 0000000..448287b
--- /dev/null
+++ b/test/ipmi_starttransfer_unittest.cpp
@@ -0,0 +1,61 @@
+#include "flash-ipmi.hpp"
+#include "ipmi.hpp"
+
+#include "updater_mock.hpp"
+
+#include <cstring>
+#include <gtest/gtest.h>
+
+using ::testing::NotNull;
+using ::testing::Return;
+using ::testing::StrictMock;
+
+// ipmid.hpp isn't installed where we can grab it and this value is per BMC
+// SoC.
+#define MAX_IPMI_BUFFER 64
+#define THIRTYTWO_MIB 33554432
+
+TEST(IpmiStartTransferTest, InvalidRequestLengthReturnsFailure)
+{
+ // Verify that the request is sanity checked w.r.t length.
+
+ StrictMock<UpdaterMock> updater;
+
+ size_t dataLen;
+ uint8_t request[MAX_IPMI_BUFFER] = {0};
+ uint8_t reply[MAX_IPMI_BUFFER] = {0};
+
+ struct StartTx tx;
+ tx.cmd = FlashSubCmds::flashStartTransfer;
+ tx.length = THIRTYTWO_MIB;
+ std::memcpy(request, &tx, sizeof(tx));
+
+ dataLen = sizeof(tx) - 1; // It's too small to be a valid packet.
+
+ EXPECT_EQ(IPMI_CC_INVALID,
+ startTransfer(&updater, request, reply, &dataLen));
+}
+
+TEST(IpmiStartTransferTest, ValidRequestBoringCase)
+{
+ // Verify that if the request is valid it calls into the flash updater.
+
+ StrictMock<UpdaterMock> updater;
+
+ size_t dataLen;
+ uint8_t request[MAX_IPMI_BUFFER] = {0};
+ uint8_t reply[MAX_IPMI_BUFFER] = {0};
+
+ struct StartTx tx;
+ tx.cmd = FlashSubCmds::flashStartTransfer;
+ tx.length = THIRTYTWO_MIB;
+ std::memcpy(request, &tx, sizeof(tx));
+
+ dataLen = sizeof(tx);
+
+ EXPECT_CALL(updater, start(THIRTYTWO_MIB)).WillOnce(Return(true));
+
+ EXPECT_EQ(IPMI_CC_OK, startTransfer(&updater, request, reply, &dataLen));
+ EXPECT_EQ(sizeof(uint8_t), dataLen);
+ EXPECT_EQ(0, reply[0]);
+}
diff --git a/test/updater_mock.hpp b/test/updater_mock.hpp
new file mode 100644
index 0000000..3c2b5ab
--- /dev/null
+++ b/test/updater_mock.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "flash-ipmi.hpp"
+
+class UpdaterMock : public UpdateInterface
+{
+ public:
+ virtual ~UpdaterMock() = default;
+
+ MOCK_METHOD1(start, bool(uint32_t));
+};
OpenPOWER on IntegriCloud