1a6e3b99dSJohn Edward Broadbent #include "util.hpp" 2a6e3b99dSJohn Edward Broadbent 3043af59fSTom Tung #include "estoraged_conf.hpp" 4d32b9667SJohn Wedig #include "getConfig.hpp" 5d32b9667SJohn Wedig 6a6e3b99dSJohn Edward Broadbent #include <linux/fs.h> 7a6e3b99dSJohn Edward Broadbent 8a6e3b99dSJohn Edward Broadbent #include <phosphor-logging/lg2.hpp> 9a6e3b99dSJohn Edward Broadbent #include <stdplus/fd/create.hpp> 10a6e3b99dSJohn Edward Broadbent #include <stdplus/fd/managed.hpp> 11a6e3b99dSJohn Edward Broadbent #include <stdplus/handle/managed.hpp> 12a6e3b99dSJohn Edward Broadbent #include <xyz/openbmc_project/Common/error.hpp> 13a6e3b99dSJohn Edward Broadbent 14d32b9667SJohn Wedig #include <filesystem> 155d799bb9SJohn Edward Broadbent #include <fstream> 165d799bb9SJohn Edward Broadbent #include <iostream> 17043af59fSTom Tung #include <optional> 185d799bb9SJohn Edward Broadbent #include <string> 19a6e3b99dSJohn Edward Broadbent 20a6e3b99dSJohn Edward Broadbent namespace estoraged 21a6e3b99dSJohn Edward Broadbent { 22a6e3b99dSJohn Edward Broadbent namespace util 23a6e3b99dSJohn Edward Broadbent { 24a6e3b99dSJohn Edward Broadbent using ::sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 25a6e3b99dSJohn Edward Broadbent using ::stdplus::fd::ManagedFd; 26a6e3b99dSJohn Edward Broadbent 275d799bb9SJohn Edward Broadbent uint64_t findSizeOfBlockDevice(const std::string& devPath) 28a6e3b99dSJohn Edward Broadbent { 29a6e3b99dSJohn Edward Broadbent ManagedFd fd; 30a6e3b99dSJohn Edward Broadbent uint64_t bytes = 0; 31a6e3b99dSJohn Edward Broadbent try 32a6e3b99dSJohn Edward Broadbent { 33a6e3b99dSJohn Edward Broadbent // open block dev 34a6e3b99dSJohn Edward Broadbent fd = stdplus::fd::open(devPath, stdplus::fd::OpenAccess::ReadOnly); 35a6e3b99dSJohn Edward Broadbent // get block size 36a6e3b99dSJohn Edward Broadbent fd.ioctl(BLKGETSIZE64, &bytes); 37a6e3b99dSJohn Edward Broadbent } 38a6e3b99dSJohn Edward Broadbent catch (...) 39a6e3b99dSJohn Edward Broadbent { 40a6e3b99dSJohn Edward Broadbent lg2::error("erase unable to open blockdev", "REDFISH_MESSAGE_ID", 41a6e3b99dSJohn Edward Broadbent std::string("OpenBMC.0.1.DriveEraseFailure"), 42a6e3b99dSJohn Edward Broadbent "REDFISH_MESSAGE_ARGS", std::to_string(fd.get())); 43a6e3b99dSJohn Edward Broadbent throw InternalFailure(); 44a6e3b99dSJohn Edward Broadbent } 45a6e3b99dSJohn Edward Broadbent return bytes; 46a6e3b99dSJohn Edward Broadbent } 47a6e3b99dSJohn Edward Broadbent 485d799bb9SJohn Edward Broadbent uint8_t findPredictedMediaLifeLeftPercent(const std::string& sysfsPath) 495d799bb9SJohn Edward Broadbent { 505d799bb9SJohn Edward Broadbent // The eMMC spec defines two estimates for the life span of the device 515d799bb9SJohn Edward Broadbent // in the extended CSD field 269 and 268, named estimate A and estimate B. 525d799bb9SJohn Edward Broadbent // Linux exposes the values in the /life_time node. 535d799bb9SJohn Edward Broadbent // estimate A is for A type memory 545d799bb9SJohn Edward Broadbent // estimate B is for B type memory 555d799bb9SJohn Edward Broadbent // 565d799bb9SJohn Edward Broadbent // the estimate are encoded as such 575d799bb9SJohn Edward Broadbent // 0x01 <=> 0% - 10% device life time used 585d799bb9SJohn Edward Broadbent // 0x02 <=> 10% -20% device life time used 595d799bb9SJohn Edward Broadbent // ... 605d799bb9SJohn Edward Broadbent // 0x0A <=> 90% - 100% device life time used 615d799bb9SJohn Edward Broadbent // 0x0B <=> Exceeded its maximum estimated device life time 625d799bb9SJohn Edward Broadbent 635d799bb9SJohn Edward Broadbent uint16_t estA = 0, estB = 0; 645d799bb9SJohn Edward Broadbent std::ifstream lifeTimeFile; 655d799bb9SJohn Edward Broadbent try 665d799bb9SJohn Edward Broadbent { 675d799bb9SJohn Edward Broadbent lifeTimeFile.open(sysfsPath + "/life_time", std::ios_base::in); 685d799bb9SJohn Edward Broadbent lifeTimeFile >> std::hex >> estA; 695d799bb9SJohn Edward Broadbent lifeTimeFile >> std::hex >> estB; 705d799bb9SJohn Edward Broadbent if ((estA == 0) || (estA > 11) || (estB == 0) || (estB > 11)) 715d799bb9SJohn Edward Broadbent { 725d799bb9SJohn Edward Broadbent throw InternalFailure(); 735d799bb9SJohn Edward Broadbent } 745d799bb9SJohn Edward Broadbent } 755d799bb9SJohn Edward Broadbent catch (...) 765d799bb9SJohn Edward Broadbent { 775d799bb9SJohn Edward Broadbent lg2::error("Unable to read sysfs", "REDFISH_MESSAGE_ID", 785d799bb9SJohn Edward Broadbent std::string("OpenBMC.0.1.DriveEraseFailure")); 795d799bb9SJohn Edward Broadbent lifeTimeFile.close(); 805d799bb9SJohn Edward Broadbent return 255; 815d799bb9SJohn Edward Broadbent } 825d799bb9SJohn Edward Broadbent lifeTimeFile.close(); 835d799bb9SJohn Edward Broadbent // we are returning lowest LifeLeftPercent 845d799bb9SJohn Edward Broadbent uint8_t maxLifeUsed = 0; 855d799bb9SJohn Edward Broadbent if (estA > estB) 865d799bb9SJohn Edward Broadbent { 875d799bb9SJohn Edward Broadbent maxLifeUsed = estA; 885d799bb9SJohn Edward Broadbent } 895d799bb9SJohn Edward Broadbent else 905d799bb9SJohn Edward Broadbent { 915d799bb9SJohn Edward Broadbent maxLifeUsed = estB; 925d799bb9SJohn Edward Broadbent } 935d799bb9SJohn Edward Broadbent 945d799bb9SJohn Edward Broadbent return static_cast<uint8_t>(11 - maxLifeUsed) * 10; 955d799bb9SJohn Edward Broadbent } 965d799bb9SJohn Edward Broadbent 97b4838308SJohn Wedig std::string getPartNumber(const std::filesystem::path& sysfsPath) 98b4838308SJohn Wedig { 99b4838308SJohn Wedig std::ifstream partNameFile; 100b4838308SJohn Wedig std::string partName; 101b4838308SJohn Wedig try 102b4838308SJohn Wedig { 103b4838308SJohn Wedig std::filesystem::path namePath(sysfsPath); 104b4838308SJohn Wedig namePath /= "name"; 105b4838308SJohn Wedig partNameFile.open(namePath, std::ios_base::in); 106b4838308SJohn Wedig partNameFile >> partName; 107b4838308SJohn Wedig } 108b4838308SJohn Wedig catch (...) 109b4838308SJohn Wedig { 110b4838308SJohn Wedig lg2::error("Unable to read sysfs", "REDFISH_MESSAGE_ID", 111b4838308SJohn Wedig std::string("OpenBMC.0.1.PartNumberFailure")); 112b4838308SJohn Wedig } 113b4838308SJohn Wedig partNameFile.close(); 114b4838308SJohn Wedig if (partName.empty()) 115b4838308SJohn Wedig { 116b4838308SJohn Wedig partName = "unknown"; 117b4838308SJohn Wedig } 118b4838308SJohn Wedig 119b4838308SJohn Wedig return partName; 120b4838308SJohn Wedig } 121b4838308SJohn Wedig 122b4838308SJohn Wedig std::string getSerialNumber(const std::filesystem::path& sysfsPath) 123b4838308SJohn Wedig { 124b4838308SJohn Wedig std::ifstream serialNumberFile; 125b4838308SJohn Wedig std::string serialNumber; 126b4838308SJohn Wedig try 127b4838308SJohn Wedig { 128b4838308SJohn Wedig std::filesystem::path serialPath(sysfsPath); 129b4838308SJohn Wedig serialPath /= "serial"; 130b4838308SJohn Wedig serialNumberFile.open(serialPath, std::ios_base::in); 131b4838308SJohn Wedig serialNumberFile >> serialNumber; 132b4838308SJohn Wedig } 133b4838308SJohn Wedig catch (...) 134b4838308SJohn Wedig { 135b4838308SJohn Wedig lg2::error("Unable to read sysfs", "REDFISH_MESSAGE_ID", 136b4838308SJohn Wedig std::string("OpenBMC.0.1.SerialNumberFailure")); 137b4838308SJohn Wedig } 138b4838308SJohn Wedig serialNumberFile.close(); 139b4838308SJohn Wedig if (serialNumber.empty()) 140b4838308SJohn Wedig { 141b4838308SJohn Wedig serialNumber = "unknown"; 142b4838308SJohn Wedig } 143b4838308SJohn Wedig 144b4838308SJohn Wedig return serialNumber; 145b4838308SJohn Wedig } 146b4838308SJohn Wedig 147043af59fSTom Tung std::optional<DeviceInfo> findDevice(const StorageData& data, 148043af59fSTom Tung const std::filesystem::path& searchDir) 149d32b9667SJohn Wedig { 150d32b9667SJohn Wedig /* Check what type of storage device this is. */ 151d32b9667SJohn Wedig estoraged::BasicVariantType typeVariant; 152d32b9667SJohn Wedig try 153d32b9667SJohn Wedig { 154d32b9667SJohn Wedig /* The EntityManager config should have this property set. */ 155d32b9667SJohn Wedig typeVariant = data.at("Type"); 156d32b9667SJohn Wedig } 157d32b9667SJohn Wedig catch (const boost::container::out_of_range& e) 158d32b9667SJohn Wedig { 159d32b9667SJohn Wedig lg2::error("Could not read device type", "REDFISH_MESSAGE_ID", 160d32b9667SJohn Wedig std::string("OpenBMC.0.1.FindDeviceFail")); 161043af59fSTom Tung return std::nullopt; 162d32b9667SJohn Wedig } 163d32b9667SJohn Wedig 16419825057SRahul Kapoor /* Check if location code/ silkscreen name is provided for the drive. */ 165043af59fSTom Tung std::string locationCode; 16619825057SRahul Kapoor auto findLocationCode = data.find("LocationCode"); 16719825057SRahul Kapoor if (findLocationCode != data.end()) 16819825057SRahul Kapoor { 16919825057SRahul Kapoor const std::string* locationCodePtr = 17019825057SRahul Kapoor std::get_if<std::string>(&findLocationCode->second); 17119825057SRahul Kapoor if (locationCodePtr != nullptr) 17219825057SRahul Kapoor { 17319825057SRahul Kapoor locationCode = *locationCodePtr; 17419825057SRahul Kapoor } 17519825057SRahul Kapoor } 17619825057SRahul Kapoor 177043af59fSTom Tung /* Check if EraseMaxGeometry is provided. */ 178043af59fSTom Tung uint64_t eraseMaxGeometry = ERASE_MAX_GEOMETRY; 179043af59fSTom Tung auto findEraseMaxGeometry = data.find("EraseMaxGeometry"); 180043af59fSTom Tung if (findEraseMaxGeometry != data.end()) 181043af59fSTom Tung { 182043af59fSTom Tung const auto* eraseMaxGeometryPtr = 183043af59fSTom Tung std::get_if<uint64_t>(&findEraseMaxGeometry->second); 184043af59fSTom Tung if (eraseMaxGeometryPtr != nullptr) 185043af59fSTom Tung { 186043af59fSTom Tung lg2::info("eStorageD new eraseMaxGeometry found on system"); 187043af59fSTom Tung eraseMaxGeometry = *eraseMaxGeometryPtr; 188043af59fSTom Tung } 189043af59fSTom Tung } 190043af59fSTom Tung 191043af59fSTom Tung /* Check if EraseMinGeometry is provided. */ 192043af59fSTom Tung uint64_t eraseMinGeometry = ERASE_MIN_GEOMETRY; 193043af59fSTom Tung auto findEraseMinGeometry = data.find("EraseMinGeometry"); 194043af59fSTom Tung if (findEraseMinGeometry != data.end()) 195043af59fSTom Tung { 196043af59fSTom Tung const auto* eraseMinGeometryPtr = 197043af59fSTom Tung std::get_if<uint64_t>(&findEraseMinGeometry->second); 198043af59fSTom Tung if (eraseMinGeometryPtr != nullptr) 199043af59fSTom Tung { 200043af59fSTom Tung lg2::info("eStorageD new eraseMinGeometry found on system"); 201043af59fSTom Tung eraseMinGeometry = *eraseMinGeometryPtr; 202043af59fSTom Tung } 203043af59fSTom Tung } 204043af59fSTom Tung 205d32b9667SJohn Wedig /* 206*d7be42bdSJohn Wedig * Determine the drive type to report for this device. Note that we only 207*d7be42bdSJohn Wedig * support eMMC currently, so report an error for any other device types. 208d32b9667SJohn Wedig */ 209*d7be42bdSJohn Wedig std::string deviceType = std::get<std::string>(typeVariant); 210*d7be42bdSJohn Wedig /* drive type to report in the Item.Drive dbus interface */ 211*d7be42bdSJohn Wedig std::string driveType; 212*d7be42bdSJohn Wedig if (deviceType.compare("EmmcDevice") == 0) 213d32b9667SJohn Wedig { 214*d7be42bdSJohn Wedig driveType = "SSD"; 215*d7be42bdSJohn Wedig } 216*d7be42bdSJohn Wedig else 217*d7be42bdSJohn Wedig { 218*d7be42bdSJohn Wedig lg2::error("Unsupported device type {TYPE}", "TYPE", deviceType, 219d32b9667SJohn Wedig "REDFISH_MESSAGE_ID", 220d32b9667SJohn Wedig std::string("OpenBMC.0.1.FindDeviceFail")); 221043af59fSTom Tung return std::nullopt; 222d32b9667SJohn Wedig } 223d32b9667SJohn Wedig 224d32b9667SJohn Wedig /* Look for the eMMC in the specified searchDir directory. */ 22504c28fadSPatrick Williams for (const auto& dirEntry : std::filesystem::directory_iterator{searchDir}) 226d32b9667SJohn Wedig { 227f78215fdSJohn Wedig /* 228f78215fdSJohn Wedig * We will look at the 'type' file to determine if this is an MMC 229f78215fdSJohn Wedig * device. 230f78215fdSJohn Wedig */ 231f78215fdSJohn Wedig std::filesystem::path curPath(dirEntry.path()); 232f78215fdSJohn Wedig curPath /= "device/type"; 233f78215fdSJohn Wedig if (!std::filesystem::exists(curPath)) 234d32b9667SJohn Wedig { 235f78215fdSJohn Wedig /* The 'type' file doesn't exist. This must not be an eMMC. */ 236f78215fdSJohn Wedig continue; 237f78215fdSJohn Wedig } 238f78215fdSJohn Wedig 239f78215fdSJohn Wedig try 240f78215fdSJohn Wedig { 241f78215fdSJohn Wedig std::ifstream typeFile(curPath, std::ios_base::in); 242f78215fdSJohn Wedig std::string devType; 243f78215fdSJohn Wedig typeFile >> devType; 2449be2f0fcSJohn Edward Broadbent if (devType.compare("MMC") == 0 || devType.compare("SD") == 0) 245f78215fdSJohn Wedig { 246f78215fdSJohn Wedig /* Found it. Get the sysfs directory and device file. */ 247f78215fdSJohn Wedig std::filesystem::path deviceName(dirEntry.path().filename()); 248f78215fdSJohn Wedig 249043af59fSTom Tung std::filesystem::path sysfsDir = dirEntry.path(); 250d32b9667SJohn Wedig sysfsDir /= "device"; 251d32b9667SJohn Wedig 252043af59fSTom Tung std::filesystem::path deviceFile = "/dev"; 253f78215fdSJohn Wedig deviceFile /= deviceName; 254d32b9667SJohn Wedig 255043af59fSTom Tung std::string luksName = "luks-" + deviceName.string(); 256*d7be42bdSJohn Wedig return DeviceInfo{ 257*d7be42bdSJohn Wedig deviceFile, sysfsDir, luksName, locationCode, 258*d7be42bdSJohn Wedig eraseMaxGeometry, eraseMinGeometry, driveType}; 259d32b9667SJohn Wedig } 260d32b9667SJohn Wedig } 261f78215fdSJohn Wedig catch (...) 262f78215fdSJohn Wedig { 263f78215fdSJohn Wedig lg2::error("Failed to read device type for {PATH}", "PATH", curPath, 264f78215fdSJohn Wedig "REDFISH_MESSAGE_ID", 265f78215fdSJohn Wedig std::string("OpenBMC.0.1.FindDeviceFail")); 266f78215fdSJohn Wedig /* 267f78215fdSJohn Wedig * We will still continue searching, though. Maybe this wasn't the 268f78215fdSJohn Wedig * device we were looking for, anyway. 269f78215fdSJohn Wedig */ 270f78215fdSJohn Wedig } 271f78215fdSJohn Wedig } 272d32b9667SJohn Wedig 273d32b9667SJohn Wedig /* Device wasn't found. */ 274043af59fSTom Tung return std::nullopt; 275d32b9667SJohn Wedig } 276d32b9667SJohn Wedig 277a6e3b99dSJohn Edward Broadbent } // namespace util 278a6e3b99dSJohn Edward Broadbent 279a6e3b99dSJohn Edward Broadbent } // namespace estoraged 280