xref: /openbmc/estoraged/src/estoraged.cpp (revision 30bfe8efd252bee31f919d61bbe94af6619fb39b)
1 
2 #include "estoraged.hpp"
3 
4 #include "cryptErase.hpp"
5 #include "cryptsetupInterface.hpp"
6 #include "estoraged_conf.hpp"
7 #include "pattern.hpp"
8 #include "sanitize.hpp"
9 #include "verifyDriveGeometry.hpp"
10 #include "zero.hpp"
11 
12 #include <libcryptsetup.h>
13 #include <linux/mmc/core.h>
14 #include <linux/mmc/ioctl.h>
15 #include <linux/mmc/mmc.h>
16 #include <openssl/rand.h>
17 #include <sys/ioctl.h>
18 
19 #include <phosphor-logging/lg2.hpp>
20 #include <sdbusplus/asio/object_server.hpp>
21 #include <stdplus/fd/create.hpp>
22 #include <stdplus/fd/managed.hpp>
23 #include <xyz/openbmc_project/Common/error.hpp>
24 
25 #include <cstdlib>
26 #include <filesystem>
27 #include <iostream>
28 #include <string>
29 #include <string_view>
30 #include <utility>
31 #include <vector>
32 
33 namespace estoraged
34 {
35 
36 using Association = std::tuple<std::string, std::string, std::string>;
37 using sdbusplus::asio::PropertyPermission;
38 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
39 using sdbusplus::xyz::openbmc_project::Common::Error::UnsupportedRequest;
40 using sdbusplus::xyz::openbmc_project::Inventory::Item::server::Drive;
41 using sdbusplus::xyz::openbmc_project::Inventory::Item::server::Volume;
42 
EStoraged(std::unique_ptr<stdplus::Fd> fd,sdbusplus::asio::object_server & server,const std::string & configPath,const std::string & devPath,const std::string & luksName,uint64_t size,uint8_t lifeTime,const std::string & partNumber,const std::string & serialNumber,const std::string & locationCode,uint64_t eraseMaxGeometry,uint64_t eraseMinGeometry,const std::string & driveType,const std::string & driveProtocol,std::unique_ptr<CryptsetupInterface> cryptInterface,std::unique_ptr<FilesystemInterface> fsInterface)43 EStoraged::EStoraged(
44     std::unique_ptr<stdplus::Fd> fd, sdbusplus::asio::object_server& server,
45     const std::string& configPath, const std::string& devPath,
46     const std::string& luksName, uint64_t size, uint8_t lifeTime,
47     const std::string& partNumber, const std::string& serialNumber,
48     const std::string& locationCode, uint64_t eraseMaxGeometry,
49     uint64_t eraseMinGeometry, const std::string& driveType,
50     const std::string& driveProtocol,
51     std::unique_ptr<CryptsetupInterface> cryptInterface,
52     std::unique_ptr<FilesystemInterface> fsInterface) :
53     devPath(devPath), containerName(luksName),
54     mountPoint("/mnt/" + luksName + "_fs"), eraseMaxGeometry(eraseMaxGeometry),
55     eraseMinGeometry(eraseMinGeometry), cryptIface(std::move(cryptInterface)),
56     fsIface(std::move(fsInterface)),
57     cryptDevicePath(cryptIface->cryptGetDir() + "/" + luksName),
58     objectServer(server)
59 {
60     try
61     {
62         changeHsTimingIfNeeded(fd.get(), devPath, partNumber);
63         lg2::info("Change HS_TIMING for {DEV} with {PARTNUMBER}", "DEV",
64                   devPath, "PARTNUMBER", partNumber);
65     }
66     catch (const HsModeError& e)
67     {
68         lg2::error(e.what());
69     }
70 
71     try
72     {
73         enableBackgroundOperation(std::move(fd), devPath);
74     }
75     catch (const BkopsError& e)
76     {
77         lg2::error("Failed to enable background operation for {PATH}: {ERROR}",
78                    "PATH", devPath, "ERROR", e.what());
79     }
80 
81     /* Get the filename of the device (without "/dev/"). */
82     std::string deviceName = std::filesystem::path(devPath).filename().string();
83     /* DBus object path */
84     std::string objectPath =
85         "/xyz/openbmc_project/inventory/storage/" + deviceName;
86 
87     /* Add Volume interface. */
88     volumeInterface = objectServer.add_interface(
89         objectPath, "xyz.openbmc_project.Inventory.Item.Volume");
90     volumeInterface->register_method(
91         "FormatLuks", [this](const std::vector<uint8_t>& password,
92                              Volume::FilesystemType type) {
93             this->formatLuks(password, type);
94         });
95     volumeInterface->register_method(
96         "Erase",
97         [this](Volume::EraseMethod eraseType) { this->erase(eraseType); });
98     volumeInterface->register_method("Lock", [this]() { this->lock(); });
99     volumeInterface->register_method(
100         "Unlock",
101         [this](std::vector<uint8_t>& password) { this->unlock(password); });
102     volumeInterface->register_method(
103         "ChangePassword", [this](const std::vector<uint8_t>& oldPassword,
104                                  const std::vector<uint8_t>& newPassword) {
105             this->changePassword(oldPassword, newPassword);
106         });
107     volumeInterface->register_property_r(
108         "Locked", lockedProperty, sdbusplus::vtable::property_::emits_change,
109         [this](bool& value) {
110             value = this->isLocked();
111             return value;
112         });
113 
114     /* Add Drive interface. */
115     driveInterface = objectServer.add_interface(
116         objectPath, "xyz.openbmc_project.Inventory.Item.Drive");
117     driveInterface->register_property("Capacity", size);
118     /* The lifetime property is read/write only for testing purposes. */
119     driveInterface->register_property("PredictedMediaLifeLeftPercent", lifeTime,
120                                       PropertyPermission::readWrite);
121     driveInterface->register_property(
122         "Type",
123         "xyz.openbmc_project.Inventory.Item.Drive.DriveType." + driveType);
124     driveInterface->register_property(
125         "Protocol", "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol." +
126                         driveProtocol);
127     /* This registers the Locked property for the Drives interface.
128      * Now it is the same as the volume Locked property */
129     driveInterface->register_property_r(
130         "Locked", lockedProperty, sdbusplus::vtable::property_::emits_change,
131         [this](bool& value) {
132             value = this->isLocked();
133             return value;
134         });
135 
136     driveInterface->register_property_r(
137         "EncryptionStatus", encryptionStatus,
138         sdbusplus::vtable::property_::emits_change,
139         [this](Drive::DriveEncryptionState& value) {
140             value = this->findEncryptionStatus();
141             return value;
142         });
143 
144     embeddedLocationInterface = objectServer.add_interface(
145         objectPath, "xyz.openbmc_project.Inventory.Connector.Embedded");
146 
147     if (!locationCode.empty())
148     {
149         locationCodeInterface = objectServer.add_interface(
150             objectPath, "xyz.openbmc_project.Inventory.Decorator.LocationCode");
151         locationCodeInterface->register_property("LocationCode", locationCode);
152         locationCodeInterface->initialize();
153     }
154 
155     /* Add Asset interface. */
156     assetInterface = objectServer.add_interface(
157         objectPath, "xyz.openbmc_project.Inventory.Decorator.Asset");
158     assetInterface->register_property("PartNumber", partNumber);
159     assetInterface->register_property("SerialNumber", serialNumber);
160 
161     volumeInterface->initialize();
162     driveInterface->initialize();
163     embeddedLocationInterface->initialize();
164     assetInterface->initialize();
165 
166     /* Set up the association between chassis and drive. */
167     association = objectServer.add_interface(
168         objectPath, "xyz.openbmc_project.Association.Definitions");
169 
170     std::vector<Association> associations;
171     associations.emplace_back("chassis", "drive",
172                               std::filesystem::path(configPath).parent_path());
173     association->register_property("Associations", associations);
174     association->initialize();
175 }
176 
~EStoraged()177 EStoraged::~EStoraged()
178 {
179     objectServer.remove_interface(volumeInterface);
180     objectServer.remove_interface(driveInterface);
181     objectServer.remove_interface(embeddedLocationInterface);
182     objectServer.remove_interface(assetInterface);
183     objectServer.remove_interface(association);
184 
185     if (locationCodeInterface != nullptr)
186     {
187         objectServer.remove_interface(locationCodeInterface);
188     }
189 }
190 
formatLuks(const std::vector<uint8_t> & password,Volume::FilesystemType type)191 void EStoraged::formatLuks(const std::vector<uint8_t>& password,
192                            Volume::FilesystemType type)
193 {
194     std::string msg = "OpenBMC.0.1.DriveFormat";
195     lg2::info("Starting format", "REDFISH_MESSAGE_ID", msg);
196 
197     if (type != Volume::FilesystemType::ext4)
198     {
199         lg2::error("Only ext4 filesystems are supported currently",
200                    "REDFISH_MESSAGE_ID", std::string("OpenBMC.0.1.FormatFail"));
201         throw UnsupportedRequest();
202     }
203 
204     formatLuksDev(password);
205     activateLuksDev(password);
206 
207     createFilesystem();
208     mountFilesystem();
209 }
210 
erase(Volume::EraseMethod inEraseMethod)211 void EStoraged::erase(Volume::EraseMethod inEraseMethod)
212 {
213     std::cerr << "Erasing encrypted eMMC" << std::endl;
214     lg2::info("Starting erase", "REDFISH_MESSAGE_ID",
215               std::string("OpenBMC.0.1.DriveErase"));
216     switch (inEraseMethod)
217     {
218         case Volume::EraseMethod::CryptoErase:
219         {
220             CryptErase myCryptErase(devPath);
221             myCryptErase.doErase();
222             break;
223         }
224         case Volume::EraseMethod::VerifyGeometry:
225         {
226             VerifyDriveGeometry myVerifyGeometry(devPath);
227             myVerifyGeometry.geometryOkay(eraseMaxGeometry, eraseMinGeometry);
228             break;
229         }
230         case Volume::EraseMethod::LogicalOverWrite:
231         {
232             Pattern myErasePattern(devPath);
233             myErasePattern.writePattern();
234             break;
235         }
236         case Volume::EraseMethod::LogicalVerify:
237         {
238             Pattern myErasePattern(devPath);
239             myErasePattern.verifyPattern();
240             break;
241         }
242         case Volume::EraseMethod::VendorSanitize:
243         {
244             Sanitize mySanitize(devPath);
245             mySanitize.doSanitize();
246             break;
247         }
248         case Volume::EraseMethod::ZeroOverWrite:
249         {
250             Zero myZero(devPath);
251             myZero.writeZero();
252             break;
253         }
254         case Volume::EraseMethod::ZeroVerify:
255         {
256             Zero myZero(devPath);
257             myZero.verifyZero();
258             break;
259         }
260         case Volume::EraseMethod::SecuredLocked:
261         {
262             if (!isLocked())
263             {
264                 lock();
265             }
266             // TODO: implement hardware locking
267             // Until that is done, we can lock using eStoraged::lock()
268             break;
269         }
270     }
271 }
272 
lock()273 void EStoraged::lock()
274 {
275     std::string msg = "OpenBMC.0.1.DriveLock";
276     lg2::info("Starting lock", "REDFISH_MESSAGE_ID", msg);
277 
278     unmountFilesystem();
279     deactivateLuksDev();
280 }
281 
unlock(std::vector<uint8_t> password)282 void EStoraged::unlock(std::vector<uint8_t> password)
283 {
284     std::string msg = "OpenBMC.0.1.DriveUnlock";
285     lg2::info("Starting unlock", "REDFISH_MESSAGE_ID", msg);
286 
287     activateLuksDev(std::move(password));
288     mountFilesystem();
289 }
290 
changePassword(const std::vector<uint8_t> & oldPassword,const std::vector<uint8_t> & newPassword)291 void EStoraged::changePassword(const std::vector<uint8_t>& oldPassword,
292                                const std::vector<uint8_t>& newPassword)
293 {
294     lg2::info("Starting change password", "REDFISH_MESSAGE_ID",
295               std::string("OpenBMC.0.1.DrivePasswordChanged"));
296 
297     CryptHandle cryptHandle = loadLuksHeader();
298 
299     int retval = cryptIface->cryptKeyslotChangeByPassphrase(
300         cryptHandle.get(), CRYPT_ANY_SLOT, CRYPT_ANY_SLOT,
301         reinterpret_cast<const char*>(oldPassword.data()), oldPassword.size(),
302         reinterpret_cast<const char*>(newPassword.data()), newPassword.size());
303     if (retval < 0)
304     {
305         lg2::error("Failed to change password", "REDFISH_MESSAGE_ID",
306                    std::string("OpenBMC.0.1.DrivePasswordChangeFail"));
307         throw InternalFailure();
308     }
309 
310     lg2::info("Successfully changed password for {DEV}", "DEV", devPath,
311               "REDFISH_MESSAGE_ID",
312               std::string("OpenBMC.0.1.DrivePasswordChangeSuccess"));
313 }
314 
isLocked() const315 bool EStoraged::isLocked() const
316 {
317     /*
318      * Check if the mapped virtual device exists. If it exists, the LUKS volume
319      * is unlocked.
320      */
321     try
322     {
323         std::filesystem::path mappedDevicePath(cryptDevicePath);
324         return (std::filesystem::exists(mappedDevicePath) == false);
325     }
326     catch (const std::exception& e)
327     {
328         lg2::error("Failed to query locked status: {EXCEPT}", "EXCEPT",
329                    e.what(), "REDFISH_MESSAGE_ID",
330                    std::string("OpenBMC.0.1.IsLockedFail"));
331         /* If we couldn't query the filesystem path, assume unlocked. */
332         return false;
333     }
334 }
335 
getMountPoint() const336 std::string_view EStoraged::getMountPoint() const
337 {
338     return mountPoint;
339 }
340 
formatLuksDev(std::vector<uint8_t> password)341 void EStoraged::formatLuksDev(std::vector<uint8_t> password)
342 {
343     lg2::info("Formatting device {DEV}", "DEV", devPath, "REDFISH_MESSAGE_ID",
344               std::string("OpenBMC.0.1.FormatLuksDev"));
345 
346     /* Generate the volume key. */
347     const std::size_t keySize = 64;
348     std::vector<uint8_t> volumeKey(keySize);
349     if (RAND_bytes(volumeKey.data(), keySize) != 1)
350     {
351         lg2::error("Failed to create volume key", "REDFISH_MESSAGE_ID",
352                    std::string("OpenBMC.0.1.FormatLuksDevFail"));
353         throw InternalFailure();
354     }
355 
356     /* Create the handle. */
357     CryptHandle cryptHandle(devPath);
358 
359     /* Format the LUKS encrypted device. */
360     int retval = cryptIface->cryptFormat(
361         cryptHandle.get(), CRYPT_LUKS2, "aes", "xts-plain64", nullptr,
362         reinterpret_cast<const char*>(volumeKey.data()), volumeKey.size(),
363         nullptr);
364     if (retval < 0)
365     {
366         lg2::error("Failed to format encrypted device: {RETVAL}", "RETVAL",
367                    retval, "REDFISH_MESSAGE_ID",
368                    std::string("OpenBMC.0.1.FormatLuksDevFail"));
369         throw InternalFailure();
370     }
371 
372     /* Set the password. */
373     retval = cryptIface->cryptKeyslotAddByVolumeKey(
374         cryptHandle.get(), CRYPT_ANY_SLOT, nullptr, 0,
375         reinterpret_cast<const char*>(password.data()), password.size());
376 
377     if (retval < 0)
378     {
379         lg2::error("Failed to set encryption password", "REDFISH_MESSAGE_ID",
380                    std::string("OpenBMC.0.1.FormatLuksDevFail"));
381         throw InternalFailure();
382     }
383 
384     lg2::info("Encrypted device {DEV} successfully formatted", "DEV", devPath,
385               "REDFISH_MESSAGE_ID",
386               std::string("OpenBMC.0.1.FormatLuksDevSuccess"));
387 }
388 
loadLuksHeader()389 CryptHandle EStoraged::loadLuksHeader()
390 {
391     CryptHandle cryptHandle(devPath);
392 
393     int retval = cryptIface->cryptLoad(cryptHandle.get(), CRYPT_LUKS2, nullptr);
394     if (retval < 0)
395     {
396         lg2::error("Failed to load LUKS header: {RETVAL}", "RETVAL", retval,
397                    "REDFISH_MESSAGE_ID",
398                    std::string("OpenBMC.0.1.ActivateLuksDevFail"));
399         throw InternalFailure();
400     }
401     return cryptHandle;
402 }
403 
findEncryptionStatus()404 Drive::DriveEncryptionState EStoraged::findEncryptionStatus()
405 {
406     try
407     {
408         loadLuksHeader();
409         return Drive::DriveEncryptionState::Encrypted;
410     }
411     catch (...)
412     {
413         return Drive::DriveEncryptionState::Unencrypted;
414     }
415 }
416 
activateLuksDev(std::vector<uint8_t> password)417 void EStoraged::activateLuksDev(std::vector<uint8_t> password)
418 {
419     lg2::info("Activating LUKS dev {DEV}", "DEV", devPath, "REDFISH_MESSAGE_ID",
420               std::string("OpenBMC.0.1.ActivateLuksDev"));
421 
422     /* Create the handle. */
423     CryptHandle cryptHandle = loadLuksHeader();
424 
425     int retval = cryptIface->cryptActivateByPassphrase(
426         cryptHandle.get(), containerName.c_str(), CRYPT_ANY_SLOT,
427         reinterpret_cast<const char*>(password.data()), password.size(),
428         CRYPT_ACTIVATE_ALLOW_DISCARDS);
429 
430     if (retval < 0)
431     {
432         lg2::error("Failed to activate LUKS dev: {RETVAL}", "RETVAL", retval,
433                    "REDFISH_MESSAGE_ID",
434                    std::string("OpenBMC.0.1.ActivateLuksDevFail"));
435         throw InternalFailure();
436     }
437 
438     lg2::info("Successfully activated LUKS dev {DEV}", "DEV", devPath,
439               "REDFISH_MESSAGE_ID",
440               std::string("OpenBMC.0.1.ActivateLuksDevSuccess"));
441 }
442 
createFilesystem()443 void EStoraged::createFilesystem()
444 {
445     /* Run the command to create the filesystem. */
446     int retval = fsIface->runMkfs(cryptDevicePath, {"-E", "discard"});
447     if (retval != 0)
448     {
449         lg2::error("Failed to create filesystem: {RETVAL}", "RETVAL", retval,
450                    "REDFISH_MESSAGE_ID",
451                    std::string("OpenBMC.0.1.CreateFilesystemFail"));
452         throw InternalFailure();
453     }
454     lg2::info("Successfully created filesystem for {CONTAINER}", "CONTAINER",
455               cryptDevicePath, "REDFISH_MESSAGE_ID",
456               std::string("OpenBMC.0.1.CreateFilesystemSuccess"));
457 }
458 
mountFilesystem()459 void EStoraged::mountFilesystem()
460 {
461     /*
462      * Before mounting, run fsck to check for and resolve any filesystem errors.
463      */
464     int retval = fsIface->runFsck(cryptDevicePath, "-t ext4 -p");
465     if (retval != 0)
466     {
467         lg2::error("The fsck command failed: {RETVAL}", "RETVAL", retval,
468                    "REDFISH_MESSAGE_ID",
469                    std::string("OpenBMC.0.1.FixFilesystemFail"));
470         /* We'll still try to mount the filesystem, though. */
471     }
472 
473     /*
474      * Create directory for the filesystem, if it's not already present. It
475      * might already exist if, for example, the BMC reboots after creating the
476      * directory.
477      */
478     if (!fsIface->directoryExists(std::filesystem::path(mountPoint)))
479     {
480         bool success =
481             fsIface->createDirectory(std::filesystem::path(mountPoint));
482         if (!success)
483         {
484             lg2::error("Failed to create mount point: {DIR}", "DIR", mountPoint,
485                        "REDFISH_MESSAGE_ID",
486                        std::string("OpenBMC.0.1.MountFilesystemFail"));
487             throw InternalFailure();
488         }
489     }
490 
491     /* Run the command to mount the filesystem. */
492     retval = fsIface->doMount(cryptDevicePath.c_str(), mountPoint.c_str(),
493                               "ext4", 0, nullptr);
494     if (retval != 0)
495     {
496         lg2::error("Failed to mount filesystem: {RETVAL}", "RETVAL", retval,
497                    "REDFISH_MESSAGE_ID",
498                    std::string("OpenBMC.0.1.MountFilesystemFail"));
499         bool removeSuccess =
500             fsIface->removeDirectory(std::filesystem::path(mountPoint));
501         if (!removeSuccess)
502         {
503             lg2::error("Failed to remove mount point: {DIR}", "DIR", mountPoint,
504                        "REDFISH_MESSAGE_ID",
505                        std::string("OpenBMC.0.1.MountFilesystemFail"));
506         }
507         throw InternalFailure();
508     }
509 
510     lg2::info("Successfully mounted filesystem at {DIR}", "DIR", mountPoint,
511               "REDFISH_MESSAGE_ID",
512               std::string("OpenBMC.0.1.MountFilesystemSuccess"));
513 }
514 
unmountFilesystem()515 void EStoraged::unmountFilesystem()
516 {
517     int retval = fsIface->doUnmount(mountPoint.c_str());
518     if (retval != 0)
519     {
520         lg2::error("Failed to unmount filesystem: {RETVAL}", "RETVAL", retval,
521                    "REDFISH_MESSAGE_ID",
522                    std::string("OpenBMC.0.1.UnmountFilesystemFail"));
523         throw InternalFailure();
524     }
525 
526     /* Remove the mount point. */
527     bool success = fsIface->removeDirectory(std::filesystem::path(mountPoint));
528     if (!success)
529     {
530         lg2::error("Failed to remove mount point {DIR}", "DIR", mountPoint,
531                    "REDFISH_MESSAGE_ID",
532                    std::string("OpenBMC.0.1.UnmountFilesystemFail"));
533         throw InternalFailure();
534     }
535 
536     lg2::info("Successfully unmounted filesystem at {DIR}", "DIR", mountPoint,
537               "REDFISH_MESSAGE_ID",
538               std::string("OpenBMC.0.1.MountFilesystemSuccess"));
539 }
540 
deactivateLuksDev()541 void EStoraged::deactivateLuksDev()
542 {
543     lg2::info("Deactivating LUKS device {DEV}", "DEV", devPath,
544               "REDFISH_MESSAGE_ID",
545               std::string("OpenBMC.0.1.DeactivateLuksDev"));
546 
547     int retval = cryptIface->cryptDeactivate(nullptr, containerName.c_str());
548     if (retval < 0)
549     {
550         lg2::error("Failed to deactivate crypt device: {RETVAL}", "RETVAL",
551                    retval, "REDFISH_MESSAGE_ID",
552                    std::string("OpenBMC.0.1.DeactivateLuksDevFail"));
553         throw InternalFailure();
554     }
555 
556     lg2::info("Successfully deactivated LUKS device {DEV}", "DEV", devPath,
557               "REDFISH_MESSAGE_ID",
558               std::string("OpenBMC.0.1.DeactivateLuksDevSuccess"));
559 }
560 
getCryptDevicePath() const561 std::string_view EStoraged::getCryptDevicePath() const
562 {
563     return cryptDevicePath;
564 }
565 
enableBackgroundOperation(std::unique_ptr<stdplus::Fd> fd,std::string_view devPath)566 bool EStoraged::enableBackgroundOperation(std::unique_ptr<stdplus::Fd> fd,
567                                           std::string_view devPath)
568 {
569     struct mmc_ioc_cmd idata{};
570     memset(&idata, 0, sizeof(idata));
571     // Extended Device Specific Data. Contains information about the Device
572     // capabilities and selected modes.
573     std::array<uint8_t, /*EXT_CSD*/ 512> extCsd{};
574     idata.write_flag = 0;
575     idata.opcode = MMC_SEND_EXT_CSD;
576     idata.arg = 0;
577     idata.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
578     idata.blksz = extCsd.size();
579     idata.blocks = 1;
580     mmc_ioc_cmd_set_data(idata, extCsd.data());
581     if (fd->ioctl(MMC_IOC_CMD, &idata) != 0)
582     {
583         throw BkopsIoctlFailure(devPath,
584                                 "Failed to get Extended Device Specific Data");
585     }
586 
587     if ((extCsd[EXT_CSD_BKOPS_SUPPORT] & 0x1) == 0)
588     {
589         lg2::info("BKOPS is not supported for {DEV}", "DEV", devPath);
590         return false;
591     }
592     lg2::info("BKOPS is supported for {DEV}", "DEV", devPath);
593 
594     if ((extCsd[EXT_CSD_BKOPS_EN] &
595          (EXT_CSD_MANUAL_BKOPS_MASK | EXT_CSD_AUTO_BKOPS_MASK)) != 0)
596     {
597         lg2::info("BKOPS is already enabled for {DEV}: Mode: {MODE}", "DEV",
598                   devPath, "MODE", extCsd[EXT_CSD_BKOPS_EN]);
599         return false;
600     }
601 
602     // Clear the input data.
603     memset(&idata, 0, sizeof(idata));
604     idata.write_flag = 1;
605     idata.opcode = MMC_SWITCH;
606     idata.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (EXT_CSD_BKOPS_EN << 16) |
607                 (EXT_CSD_MANUAL_BKOPS_MASK << 8) | EXT_CSD_CMD_SET_NORMAL;
608     idata.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
609     if (fd->ioctl(MMC_IOC_CMD, &idata) != 0)
610     {
611         throw BkopsEnableFailure(devPath);
612     }
613 
614     lg2::info("Successfully enable BKOPS for {DEV}", "DEV", devPath);
615     return true;
616 }
617 
changeHsTiming(stdplus::Fd * fd,std::string_view devPath)618 bool EStoraged::changeHsTiming(stdplus::Fd* fd, std::string_view devPath)
619 {
620     if (fd == nullptr)
621     {
622         return false;
623     }
624     struct mmc_ioc_cmd cmd = {};
625     cmd.write_flag = 1;
626     cmd.opcode = MMC_SWITCH;
627     cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (EXT_CSD_HS_TIMING << 16) |
628               (EXT_CSD_TIMING_HS400 << 8);
629     cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
630     if (fd->ioctl(MMC_IOC_CMD, &cmd) != 0)
631     {
632         throw HsModeError(devPath);
633     }
634     return true;
635 }
636 
changeHsTimingIfNeeded(stdplus::Fd * fd,std::string_view devPath,std::string_view partNumber)637 bool EStoraged::changeHsTimingIfNeeded(
638     stdplus::Fd* fd, std::string_view devPath, std::string_view partNumber)
639 {
640     if (fd == nullptr)
641     {
642         return false;
643     }
644     if (std::ranges::contains(highSpeedMMC, partNumber))
645     {
646         return changeHsTiming(fd, devPath);
647     }
648     return false;
649 }
650 
651 } // namespace estoraged
652