Add erase sanitize
Sanitize uses the eMMC firmware to erase all blocks.
Tested:
$ cat /dev/urandom > /dev/mmcblk0
$ time busctl call xyz.openbmc_project.eStoraged.mmcblk0 \
/xyz/openbmc_project/inventory/storage/mmcblk0 \
xyz.openbmc_project.Inventory.Item.Volume Erase s \
xyz.openbmc_project.Inventory.Item.Volume.EraseMethod.VendorSanitize \
--timeout=1200
real 0m1.793s
user 0m0.021s
sys 0m0.009s
root@ytbaz20-nfd01:/# cat /dev/mmcblk0 | hexdump
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
Signed-off-by: John Edward Broadbent <jebr@google.com>
Change-Id: I31bc21c0b6d31cbba0db752d94a93eb004dbbde6
diff --git a/.clang-tidy b/.clang-tidy
index b5f7462..0c9cc0b 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -296,5 +296,5 @@
- { key: readability-identifier-naming.ParameterCase, value: camelBack }
- { key: readability-identifier-naming.NamespaceCase, value: lower_case }
- { key: readability-identifier-naming.StructCase, value: CamelCase }
- - { key: cppcoreguidelines-macro-usage.AllowedRegexp, value: ((ERASE_MIN_GEOMETRY)|(ERASE_MAX_GEOMETRY)) }
+ - { key: cppcoreguidelines-macro-usage.AllowedRegexp, value: ((ERASE_MIN_GEOMETRY)|(ERASE_MAX_GEOMETRY)|(twiddleextCsdEraseGroupDef)) }
diff --git a/include/sanitize.hpp b/include/sanitize.hpp
new file mode 100644
index 0000000..4ab73bf
--- /dev/null
+++ b/include/sanitize.hpp
@@ -0,0 +1,125 @@
+#pragma once
+
+#include "erase.hpp"
+
+#include <linux/mmc/ioctl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include <stdplus/fd/managed.hpp>
+#include <util.hpp>
+
+#include <array>
+#include <cstddef>
+#include <span>
+#include <string_view>
+
+namespace estoraged
+{
+
+using stdplus::fd::ManagedFd;
+
+class IOCTLWrapperInterface
+{
+ public:
+ /** @brief Wrapper around ioctl
+ * @details Used for mocking purposes.
+ *
+ * @param[in] devPath - File name of block device
+ * @param[in] request - Device-dependent request code
+ * @param[in] mmc_ioc_cmd - eMMC cmd
+ */
+ virtual int doIoctl(std::string_view devPath, unsigned long request,
+ struct mmc_ioc_cmd data) = 0;
+
+ /** @brief Wrapper around ioctl
+ * @details Used for mocking purposes.
+ *
+ * @param[in] devPath - File name of block device
+ * @param[in] request - Device-dependent request code
+ * @param[in] mmc_io_mutli_cmd - many eMMC cmd
+ */
+ virtual int doIoctlMulti(std::string_view devPath, unsigned long request,
+ struct mmc_io_multi_cmd_erase data) = 0;
+
+ virtual ~IOCTLWrapperInterface() = default;
+ IOCTLWrapperInterface() = default;
+ IOCTLWrapperInterface(const IOCTLWrapperInterface&) = delete;
+ IOCTLWrapperInterface& operator=(const IOCTLWrapperInterface&) = delete;
+
+ IOCTLWrapperInterface(IOCTLWrapperInterface&&) = delete;
+ IOCTLWrapperInterface& operator=(IOCTLWrapperInterface&&) = delete;
+};
+
+// mockIOCTLWapper also inherits from IOCTLWrapperInterface
+class IOCTLWrapperImpl : public IOCTLWrapperInterface
+{
+ public:
+ int doIoctl(std::string_view devPath, unsigned long request,
+ struct mmc_ioc_cmd data) override;
+ int doIoctlMulti(std::string_view devPath, unsigned long request,
+ struct mmc_io_multi_cmd_erase data) override;
+ ~IOCTLWrapperImpl() override = default;
+ IOCTLWrapperImpl() = default;
+
+ IOCTLWrapperImpl(const IOCTLWrapperImpl&) = delete;
+ IOCTLWrapperImpl& operator=(const IOCTLWrapperImpl&) = delete;
+
+ IOCTLWrapperImpl(IOCTLWrapperImpl&&) = delete;
+ IOCTLWrapperImpl& operator=(IOCTLWrapperImpl&&) = delete;
+};
+
+class Sanitize : public Erase
+{
+ public:
+ /** @breif Creates a sanitize erase object
+ *
+ * @param[in] inDevPath - the linux device path for the block device.
+ * @param[in] IOCTLWrapper - This is a ioctl wrapper, it can be used for
+ * testing
+ */
+ Sanitize(std::string_view inDevPath,
+ std::unique_ptr<IOCTLWrapperInterface> inIOCTL =
+ std::make_unique<IOCTLWrapperImpl>()) :
+ Erase(inDevPath),
+ ioctlWrapper(std::move(inIOCTL))
+ {}
+
+ /** @brief sanitize the drive, using eMMC specifed erase commands
+ *
+ * param[in] driveSize - size of the drive in bytes
+ */
+ void doSanitize(uint64_t driveSize);
+
+ /** @brief sanitize the drive, using eMMC specifed erase commands
+ * This function uses the built in utils to call sanitize
+ */
+ void doSanitize()
+ {
+ doSanitize(util::Util::findSizeOfBlockDevice(devPath));
+ }
+
+ private:
+ /* Wrapper for ioctl*/
+ std::unique_ptr<IOCTLWrapperInterface> ioctlWrapper;
+
+ /** @brief uses the eMMC defined sanitize command, it is not the same as
+ * vendor_sanitize */
+ void emmcSanitize();
+
+ /** @brief uses the eMMC defined erase command
+ *
+ * param[in] driveSize - size of the drive in bytes
+ */
+ void emmcErase(uint64_t driveSize);
+};
+
+// can't use the real mmc_ioc_multi_cmd b/c of zero length array
+// see uapi/linux/mmc/ioctl.h
+struct mmc_io_multi_cmd_erase
+{
+ uint64_t num_of_cmds;
+ struct mmc_ioc_cmd cmds[3]; // NOLINT (c arrays usage)
+};
+
+} // namespace estoraged
diff --git a/src/erase/meson.build b/src/erase/meson.build
index 233fc59..57329c2 100644
--- a/src/erase/meson.build
+++ b/src/erase/meson.build
@@ -3,6 +3,7 @@
'verifyDriveGeometry.cpp',
'pattern.cpp',
'cryptoErase.cpp',
+ 'sanitize.cpp',
'zero.cpp',
include_directories : eStoraged_headers,
implicit_include_directories: false,
diff --git a/src/erase/sanitize.cpp b/src/erase/sanitize.cpp
new file mode 100644
index 0000000..26e39fb
--- /dev/null
+++ b/src/erase/sanitize.cpp
@@ -0,0 +1,145 @@
+#include "sanitize.hpp"
+
+#include "estoraged_conf.hpp"
+
+#include <linux/mmc/ioctl.h>
+#include <sys/ioctl.h>
+
+#include <phosphor-logging/lg2.hpp>
+#include <stdplus/fd/create.hpp>
+#include <stdplus/fd/managed.hpp>
+#include <stdplus/handle/managed.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+#include <array>
+#include <cstddef>
+#include <span>
+#include <string>
+#include <string_view>
+
+namespace
+{
+
+constexpr uint32_t mmcSwitch = 6;
+constexpr uint32_t mmcSendExtCsd = 8;
+constexpr uint32_t mmcSwitchModeWriteByte = 0x03;
+constexpr uint32_t extCsdSanitizeStart = 165;
+constexpr uint32_t extCsdCmdSetNormal = (1 << 0);
+
+constexpr uint32_t mmcRspPresent = (1 << 0);
+constexpr uint32_t mmcRspCrc = (1 << 2);
+constexpr uint32_t mmcRspBusy = (1 << 3);
+constexpr uint32_t mmcRspOpcode = (1 << 4);
+
+constexpr uint32_t mmcCmdAc = (0 << 5);
+constexpr uint32_t mmcCmdAdtc = (1 << 5);
+
+constexpr uint32_t mmcRspSpiS1 = (1 << 7);
+constexpr uint32_t mmcRspSpiBusy = (1 << 10);
+
+constexpr uint32_t mmcRspSpiR1 = (mmcRspSpiS1);
+constexpr uint32_t mmcRspSpiR1B = (mmcRspSpiS1 | mmcRspSpiBusy);
+
+constexpr uint32_t mmcRspR1B =
+ (mmcRspPresent | mmcRspCrc | mmcRspOpcode | mmcRspBusy);
+
+constexpr uint32_t mmcRspR1 = (mmcRspPresent | mmcRspCrc | mmcRspOpcode);
+
+constexpr uint32_t mmcEraseGroupStart = 35;
+constexpr uint32_t mmcEraseGroupEnd = 36;
+constexpr uint32_t mmcErase = 38;
+} // namespace
+
+namespace estoraged
+{
+
+using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+using stdplus::fd::ManagedFd;
+
+void Sanitize::doSanitize(uint64_t driveSize)
+{
+ try
+ {
+ emmcErase(driveSize);
+ emmcSanitize();
+ }
+ catch (...)
+ {
+ lg2::error("eStorageD erase sanitize failure", "REDFISH_MESSAGE_ID",
+ std::string("eStorageD.1.0.EraseFailure"));
+ throw InternalFailure();
+ }
+ lg2::info("eStorageD successfully erase sanitize", "REDFISH_MESSAGE_ID",
+ std::string("eStorageD.1.0.EraseSuccessful"));
+}
+
+void Sanitize::emmcErase(uint64_t driveSize)
+{
+
+ uint64_t sectorSize = 0x200; // default value see eMMC spec 6.6.34.
+ // NOTE: 0x200 is only valid for eMMC greater
+ // then 2 GB
+ struct mmc_io_multi_cmd_erase eraseCmd = {};
+
+ eraseCmd.num_of_cmds = 3;
+ eraseCmd.cmds[0].opcode = mmcEraseGroupStart;
+ eraseCmd.cmds[0].arg = 0;
+ eraseCmd.cmds[0].flags = mmcRspSpiR1 | mmcRspR1 | mmcCmdAc;
+ eraseCmd.cmds[0].write_flag = 1;
+
+ eraseCmd.cmds[1].opcode = mmcEraseGroupEnd;
+ eraseCmd.cmds[1].arg = (driveSize / sectorSize) - 1;
+ eraseCmd.cmds[1].flags = mmcRspSpiR1 | mmcRspR1 | mmcCmdAc;
+ eraseCmd.cmds[1].write_flag = 1;
+
+ /* Send Erase Command */
+ eraseCmd.cmds[2].opcode = mmcErase;
+ eraseCmd.cmds[2].arg = 0x00000000;
+ eraseCmd.cmds[2].cmd_timeout_ms = 0x0FFFFFFF;
+ eraseCmd.cmds[2].flags = mmcRspSpiR1B | mmcRspR1B | mmcCmdAc;
+ eraseCmd.cmds[2].write_flag = 1;
+
+ if (ioctlWrapper->doIoctlMulti(devPath, MMC_IOC_MULTI_CMD, eraseCmd) != 0)
+ {
+ throw InternalFailure();
+ }
+}
+
+void Sanitize::emmcSanitize()
+{
+
+ struct mmc_ioc_cmd idata = {};
+ idata.write_flag = 1;
+ idata.opcode = mmcSwitch;
+ idata.arg = (mmcSwitchModeWriteByte << 24) | (extCsdSanitizeStart << 16) |
+ (1 << 8) | extCsdCmdSetNormal;
+ idata.flags = mmcRspSpiR1B | mmcRspR1B | mmcCmdAc;
+
+ // make the eMMC sanitize ioctl
+ if (ioctlWrapper->doIoctl(devPath, MMC_IOC_CMD, idata) != 0)
+ {
+ throw InternalFailure();
+ }
+}
+
+int IOCTLWrapperImpl::doIoctl(std::string_view devPath, unsigned long request,
+ struct mmc_ioc_cmd data)
+
+{
+ ManagedFd fd = stdplus::fd::open(std::string(devPath).c_str(),
+ stdplus::fd::OpenAccess::ReadOnly);
+
+ return fd.ioctl(request, static_cast<void*>(&data));
+}
+
+int IOCTLWrapperImpl::doIoctlMulti(std::string_view devPath,
+ unsigned long request,
+ struct mmc_io_multi_cmd_erase data)
+{
+ ManagedFd fd = stdplus::fd::open(std::string(devPath).c_str(),
+ stdplus::fd::OpenAccess::ReadOnly);
+
+ return fd.ioctl(request, static_cast<void*>(&data));
+}
+
+} // namespace estoraged
diff --git a/src/estoraged.cpp b/src/estoraged.cpp
index 6285079..f6176e4 100644
--- a/src/estoraged.cpp
+++ b/src/estoraged.cpp
@@ -4,6 +4,7 @@
#include "cryptErase.hpp"
#include "cryptsetupInterface.hpp"
#include "pattern.hpp"
+#include "sanitize.hpp"
#include "verifyDriveGeometry.hpp"
#include "zero.hpp"
@@ -86,6 +87,8 @@
}
case EraseMethod::VendorSanitize:
{
+ Sanitize mySanitize(devPath);
+ mySanitize.doSanitize();
break;
}
case EraseMethod::ZeroOverWrite:
diff --git a/src/test/erase/sanitize_test.cpp b/src/test/erase/sanitize_test.cpp
new file mode 100644
index 0000000..4e18148
--- /dev/null
+++ b/src/test/erase/sanitize_test.cpp
@@ -0,0 +1,72 @@
+#include "estoraged_conf.hpp"
+#include "sanitize.hpp"
+
+#include <sys/ioctl.h>
+
+#include <stdplus/fd/managed.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+#include <string>
+#include <string_view>
+
+#include <gmock/gmock-matchers.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace estoraged_test
+{
+
+using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+using ::testing::_;
+using ::testing::Return;
+
+class IOCTLWrapperMock : public estoraged::IOCTLWrapperInterface
+{
+ public:
+ MOCK_METHOD(int, doIoctl,
+ (std::string_view devPath, unsigned long request,
+ struct mmc_ioc_cmd idata),
+ (override));
+
+ MOCK_METHOD(int, doIoctlMulti,
+ (std::string_view devPath, unsigned long request,
+ struct estoraged::mmc_io_multi_cmd_erase),
+ (override));
+};
+
+// mock ioctl returns 0, and everything passes
+TEST(Sanitize, Successful)
+{
+ std::unique_ptr<IOCTLWrapperMock> mockIOCTL =
+ std::make_unique<IOCTLWrapperMock>();
+ IOCTLWrapperMock* mockPtr = mockIOCTL.get();
+ estoraged::Sanitize goodSanitize("/dev/null", std::move(mockIOCTL));
+ EXPECT_CALL(*mockPtr, doIoctl(_, _, _)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*mockPtr, doIoctlMulti(_, _, _)).WillRepeatedly(Return(0));
+ EXPECT_NO_THROW(goodSanitize.doSanitize(52428800));
+}
+
+// doIoctlMulti returns -1, and Sanitize::emmcErase throws InternalFailure
+TEST(Sanitize, Sanitize_emmcErase)
+{
+ std::unique_ptr<IOCTLWrapperMock> mockIOCTL =
+ std::make_unique<IOCTLWrapperMock>();
+ IOCTLWrapperMock* mockPtr = mockIOCTL.get();
+ estoraged::Sanitize emptySanitize("", std::move(mockIOCTL));
+ EXPECT_CALL(*mockPtr, doIoctlMulti(_, _, _)).WillRepeatedly(Return(-1));
+ EXPECT_THROW(emptySanitize.doSanitize(4000000000), InternalFailure);
+}
+
+// mock ioctl returns 1, and emmcSanitize throws
+TEST(Sanitize, Sanitize_emmcSanitize)
+{
+ std::unique_ptr<IOCTLWrapperMock> mockIOCTL =
+ std::make_unique<IOCTLWrapperMock>();
+ IOCTLWrapperMock* mockPtr = mockIOCTL.get();
+ estoraged::Sanitize ioctlSanitize("/dev/null", std::move(mockIOCTL));
+ EXPECT_CALL(*mockPtr, doIoctl(_, _, _)).WillRepeatedly(Return(1));
+ EXPECT_CALL(*mockPtr, doIoctlMulti(_, _, _)).WillRepeatedly(Return(0));
+ EXPECT_THROW(ioctlSanitize.doSanitize(4000000000), InternalFailure);
+}
+
+} // namespace estoraged_test
diff --git a/src/test/meson.build b/src/test/meson.build
index b684d24..29cdda4 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -6,6 +6,7 @@
'erase/pattern_test',
'erase/zero_test',
'erase/crypto_test',
+ 'erase/sanitize_test',
'estoraged_test',
]