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