Add OEM command to request flash size
The BMC flash is `/dev/mtd0` and the size can found in
`/sys/class/mtd/mtd0/size`. This OEM command will request that
information with MTDINFO.
Tested:
Successfully requested the flash size information with ipmitool.
```
cat /sys/class/mtd/mtd0/size
67108864
```
67108864 / 1024 / 1024 = 64MB flash
```
ipmitool raw 0x2e 0x32 0x79 0x2b 0x00 0x09
79 2b 00 09 00 00 00 04
```
Output in little endian.
0x04000000 -> 67108864
Change-Id: Iec1b33503d1166a42ceef4b8491e5c19c3a077fe
Signed-off-by: Willy Tu <wltu@google.com>
diff --git a/Makefile.am b/Makefile.am
index 34e60bd..5016449 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -44,6 +44,7 @@
cable.cpp \
cpld.cpp \
eth.cpp \
+ flash_size.cpp \
psu.cpp \
pcie_i2c.cpp \
entity_name.cpp \
diff --git a/README.md b/README.md
index 4e79158..25ae8e0 100644
--- a/README.md
+++ b/README.md
@@ -198,3 +198,20 @@
|--------|------|----
|0x00|0x08|Subcommand
+
+### GetFlashSize - SubCommand 0x09
+
+Request the physical size of the BMC flash.
+
+Request
+
+|Byte(s) |Value |Data
+|--------|------|----
+|0x00|0x09|Subcommand
+
+Response
+
+|Byte(s) |Value |Data
+|--------|------|----
+|0x00|0x09|Subcommand
+|0x01...0x04|Flash size|Flash size
diff --git a/commands.hpp b/commands.hpp
index 19632ef..d9efb4f 100644
--- a/commands.hpp
+++ b/commands.hpp
@@ -25,6 +25,8 @@
SysMachineName = 7,
// Arm for psu reset on host shutdown
SysPsuHardResetOnShutdown = 8,
+ // The Sys get flash size command
+ SysGetFlashSize = 9,
};
} // namespace ipmi
diff --git a/flash_size.cpp b/flash_size.cpp
new file mode 100644
index 0000000..069b1b1
--- /dev/null
+++ b/flash_size.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2021 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_size.hpp"
+
+#include "commands.hpp"
+#include "errors.hpp"
+#include "handler.hpp"
+
+#include <cstdint>
+#include <cstring>
+#include <string>
+#include <vector>
+
+namespace google
+{
+namespace ipmi
+{
+
+struct GetFlashSizeRequest
+{
+ uint8_t subcommand;
+} __attribute__((packed));
+
+struct GetFlashSizeReply
+{
+ uint8_t subcommand;
+ uint32_t flashSize;
+} __attribute__((packed));
+
+ipmi_ret_t getFlashSize(const uint8_t* reqBuf, uint8_t* replyBuf,
+ size_t* dataLen, HandlerInterface* handler)
+{
+ struct GetFlashSizeRequest request;
+
+ if ((*dataLen) < sizeof(request))
+ {
+ std::fprintf(stderr, "Invalid command length: %u\n",
+ static_cast<uint32_t>(*dataLen));
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ std::memcpy(&request, &reqBuf[0], sizeof(request));
+ uint32_t flashSize;
+ try
+ {
+ flashSize = handler->getFlashSize();
+ }
+ catch (const IpmiException& e)
+ {
+ return e.getIpmiError();
+ }
+
+ auto reply = reinterpret_cast<struct GetFlashSizeReply*>(&replyBuf[0]);
+ reply->subcommand = SysGetFlashSize;
+ reply->flashSize = htole32(flashSize);
+
+ (*dataLen) = sizeof(struct GetFlashSizeReply);
+ return IPMI_CC_OK;
+}
+} // namespace ipmi
+} // namespace google
diff --git a/flash_size.hpp b/flash_size.hpp
new file mode 100644
index 0000000..9cec240
--- /dev/null
+++ b/flash_size.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "handler.hpp"
+
+#include <ipmid/api.h>
+
+namespace google
+{
+namespace ipmi
+{
+
+ipmi_ret_t getFlashSize(const uint8_t* reqBuf, uint8_t* replyBuf,
+ size_t* dataLen, HandlerInterface* handler);
+
+} // namespace ipmi
+} // namespace google
diff --git a/handler.cpp b/handler.cpp
index 4f1bc56..ef48842 100644
--- a/handler.cpp
+++ b/handler.cpp
@@ -20,7 +20,12 @@
#include "handler_impl.hpp"
#include "util.hpp"
+#include <fcntl.h>
#include <ipmid/api.h>
+#include <mtd/mtd-abi.h>
+#include <mtd/mtd-user.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
#include <cinttypes>
#include <cstdio>
@@ -213,6 +218,20 @@
ofs.close();
}
+uint32_t Handler::getFlashSize()
+{
+ mtd_info_t info;
+ int fd = open("/dev/mtd0", O_RDONLY);
+ int err = ioctl(fd, MEMGETINFO, &info);
+ close(fd);
+
+ if (err)
+ {
+ throw IpmiException(IPMI_CC_UNSPECIFIED_ERROR);
+ }
+ return info.size;
+}
+
std::string Handler::getEntityName(std::uint8_t id, std::uint8_t instance)
{
// Check if we support this Entity ID.
diff --git a/handler.hpp b/handler.hpp
index 2db83df..1a2cbae 100644
--- a/handler.hpp
+++ b/handler.hpp
@@ -74,6 +74,14 @@
std::uint8_t instance) = 0;
/**
+ * Return the flash size of bmc chip.
+ *
+ * @return the flash size of bmc chip
+ * @throw IpmiException on failure.
+ */
+ virtual uint32_t getFlashSize() = 0;
+
+ /**
* Return the name of the machine, parsed from release information.
*
* @return the machine name
diff --git a/handler_impl.hpp b/handler_impl.hpp
index f1ea491..677efa2 100644
--- a/handler_impl.hpp
+++ b/handler_impl.hpp
@@ -30,6 +30,7 @@
void psuResetDelay(std::uint32_t delay) const override;
void psuResetOnShutdown() const override;
std::string getEntityName(std::uint8_t id, std::uint8_t instance) override;
+ uint32_t getFlashSize() override;
std::string getMachineName() override;
void buildI2cPcieMapping() override;
size_t getI2cPcieMappingSize() const override;
diff --git a/ipmi.cpp b/ipmi.cpp
index cdf3b64..2b71315 100644
--- a/ipmi.cpp
+++ b/ipmi.cpp
@@ -21,6 +21,7 @@
#include "cpld.hpp"
#include "entity_name.hpp"
#include "eth.hpp"
+#include "flash_size.hpp"
#include "handler.hpp"
#include "machine_name.hpp"
#include "pcie_i2c.hpp"
@@ -69,6 +70,8 @@
case SysPsuHardResetOnShutdown:
return psuHardResetOnShutdown(reqBuf, replyCmdBuf, dataLen,
handler);
+ case SysGetFlashSize:
+ return getFlashSize(reqBuf, replyCmdBuf, dataLen, handler);
default:
std::fprintf(stderr, "Invalid subcommand: 0x%x\n", reqBuf[0]);
return IPMI_CC_INVALID;
diff --git a/test/Makefile.am b/test/Makefile.am
index d10f4ba..c97f480 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -48,3 +48,7 @@
check_PROGRAMS += pcie_unittest
pcie_unittest_SOURCES = pcie_unittest.cpp
pcie_unittest_LDADD = $(top_builddir)/libsyscmds_common.la
+
+check_PROGRAMS += flash_unittest
+flash_unittest_SOURCES = flash_unittest.cpp
+flash_unittest_LDADD = $(top_builddir)/libsyscmds_common.la
diff --git a/test/flash_unittest.cpp b/test/flash_unittest.cpp
new file mode 100644
index 0000000..36f5d4a
--- /dev/null
+++ b/test/flash_unittest.cpp
@@ -0,0 +1,52 @@
+#include "commands.hpp"
+#include "flash_size.hpp"
+#include "handler_mock.hpp"
+
+#include <cstdint>
+#include <cstring>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#define MAX_IPMI_BUFFER 64
+
+using ::testing::Return;
+
+namespace google
+{
+namespace ipmi
+{
+
+TEST(FlashSizeCommandTest, InvalidCommandLength)
+{
+ // GetFlashSizeRequest is one byte, let's send 0.
+ std::vector<std::uint8_t> request = {};
+ size_t dataLen = request.size();
+ std::uint8_t reply[MAX_IPMI_BUFFER];
+
+ HandlerMock hMock;
+ EXPECT_EQ(IPMI_CC_REQ_DATA_LEN_INVALID,
+ getFlashSize(request.data(), reply, &dataLen, &hMock));
+}
+
+TEST(FlashSizeCommandTest, ValidRequest)
+{
+ std::vector<std::uint8_t> request = {SysOEMCommands::SysGetFlashSize};
+ size_t dataLen = request.size();
+ std::uint8_t reply[MAX_IPMI_BUFFER];
+ uint32_t flashSize = 5422312; // 0x52BCE8
+
+ HandlerMock hMock;
+ EXPECT_CALL(hMock, getFlashSize()).WillOnce(Return(flashSize));
+ EXPECT_EQ(IPMI_CC_OK,
+ getFlashSize(request.data(), reply, &dataLen, &hMock));
+ EXPECT_EQ(dataLen, 5);
+ EXPECT_EQ(reply[4], 0);
+ EXPECT_EQ(reply[3], 0x52);
+ EXPECT_EQ(reply[2], 0xBC);
+ EXPECT_EQ(reply[1], 0xE8);
+}
+
+} // namespace ipmi
+} // namespace google
diff --git a/test/handler_mock.hpp b/test/handler_mock.hpp
index e20886a..d539f7b 100644
--- a/test/handler_mock.hpp
+++ b/test/handler_mock.hpp
@@ -27,6 +27,7 @@
std::uint8_t>(unsigned int));
MOCK_CONST_METHOD1(psuResetDelay, void(std::uint32_t));
MOCK_CONST_METHOD0(psuResetOnShutdown, void());
+ MOCK_METHOD0(getFlashSize, uint32_t());
MOCK_METHOD2(getEntityName, std::string(std::uint8_t, std::uint8_t));
MOCK_METHOD0(getMachineName, std::string());
MOCK_METHOD0(buildI2cPcieMapping, void());