1 #include "util.hpp" 2 3 #include "getConfig.hpp" 4 5 #include <linux/fs.h> 6 7 #include <phosphor-logging/lg2.hpp> 8 #include <stdplus/fd/create.hpp> 9 #include <stdplus/fd/managed.hpp> 10 #include <stdplus/handle/managed.hpp> 11 #include <xyz/openbmc_project/Common/error.hpp> 12 13 #include <filesystem> 14 #include <fstream> 15 #include <iostream> 16 #include <string> 17 18 namespace estoraged 19 { 20 namespace util 21 { 22 using ::sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 23 using ::stdplus::fd::ManagedFd; 24 25 uint64_t findSizeOfBlockDevice(const std::string& devPath) 26 { 27 ManagedFd fd; 28 uint64_t bytes = 0; 29 try 30 { 31 // open block dev 32 fd = stdplus::fd::open(devPath, stdplus::fd::OpenAccess::ReadOnly); 33 // get block size 34 fd.ioctl(BLKGETSIZE64, &bytes); 35 } 36 catch (...) 37 { 38 lg2::error("erase unable to open blockdev", "REDFISH_MESSAGE_ID", 39 std::string("OpenBMC.0.1.DriveEraseFailure"), 40 "REDFISH_MESSAGE_ARGS", std::to_string(fd.get())); 41 throw InternalFailure(); 42 } 43 return bytes; 44 } 45 46 uint8_t findPredictedMediaLifeLeftPercent(const std::string& sysfsPath) 47 { 48 // The eMMC spec defines two estimates for the life span of the device 49 // in the extended CSD field 269 and 268, named estimate A and estimate B. 50 // Linux exposes the values in the /life_time node. 51 // estimate A is for A type memory 52 // estimate B is for B type memory 53 // 54 // the estimate are encoded as such 55 // 0x01 <=> 0% - 10% device life time used 56 // 0x02 <=> 10% -20% device life time used 57 // ... 58 // 0x0A <=> 90% - 100% device life time used 59 // 0x0B <=> Exceeded its maximum estimated device life time 60 61 uint16_t estA = 0, estB = 0; 62 std::ifstream lifeTimeFile; 63 try 64 { 65 lifeTimeFile.open(sysfsPath + "/life_time", std::ios_base::in); 66 lifeTimeFile >> std::hex >> estA; 67 lifeTimeFile >> std::hex >> estB; 68 if ((estA == 0) || (estA > 11) || (estB == 0) || (estB > 11)) 69 { 70 throw InternalFailure(); 71 } 72 } 73 catch (...) 74 { 75 lg2::error("Unable to read sysfs", "REDFISH_MESSAGE_ID", 76 std::string("OpenBMC.0.1.DriveEraseFailure")); 77 lifeTimeFile.close(); 78 return 255; 79 } 80 lifeTimeFile.close(); 81 // we are returning lowest LifeLeftPercent 82 uint8_t maxLifeUsed = 0; 83 if (estA > estB) 84 { 85 maxLifeUsed = estA; 86 } 87 else 88 { 89 maxLifeUsed = estB; 90 } 91 92 return static_cast<uint8_t>(11 - maxLifeUsed) * 10; 93 } 94 95 std::string getPartNumber(const std::filesystem::path& sysfsPath) 96 { 97 std::ifstream partNameFile; 98 std::string partName; 99 try 100 { 101 std::filesystem::path namePath(sysfsPath); 102 namePath /= "name"; 103 partNameFile.open(namePath, std::ios_base::in); 104 partNameFile >> partName; 105 } 106 catch (...) 107 { 108 lg2::error("Unable to read sysfs", "REDFISH_MESSAGE_ID", 109 std::string("OpenBMC.0.1.PartNumberFailure")); 110 } 111 partNameFile.close(); 112 if (partName.empty()) 113 { 114 partName = "unknown"; 115 } 116 117 return partName; 118 } 119 120 std::string getSerialNumber(const std::filesystem::path& sysfsPath) 121 { 122 std::ifstream serialNumberFile; 123 std::string serialNumber; 124 try 125 { 126 std::filesystem::path serialPath(sysfsPath); 127 serialPath /= "serial"; 128 serialNumberFile.open(serialPath, std::ios_base::in); 129 serialNumberFile >> serialNumber; 130 } 131 catch (...) 132 { 133 lg2::error("Unable to read sysfs", "REDFISH_MESSAGE_ID", 134 std::string("OpenBMC.0.1.SerialNumberFailure")); 135 } 136 serialNumberFile.close(); 137 if (serialNumber.empty()) 138 { 139 serialNumber = "unknown"; 140 } 141 142 return serialNumber; 143 } 144 145 bool findDevice(const StorageData& data, const std::filesystem::path& searchDir, 146 std::filesystem::path& deviceFile, 147 std::filesystem::path& sysfsDir, std::string& luksName) 148 { 149 /* Check what type of storage device this is. */ 150 estoraged::BasicVariantType typeVariant; 151 try 152 { 153 /* The EntityManager config should have this property set. */ 154 typeVariant = data.at("Type"); 155 } 156 catch (const boost::container::out_of_range& e) 157 { 158 lg2::error("Could not read device type", "REDFISH_MESSAGE_ID", 159 std::string("OpenBMC.0.1.FindDeviceFail")); 160 return false; 161 } 162 163 /* 164 * Currently, we only support eMMC, so report an error for any other device 165 * types. 166 */ 167 std::string type = std::get<std::string>(typeVariant); 168 if (type.compare("EmmcDevice") != 0) 169 { 170 lg2::error("Unsupported device type {TYPE}", "TYPE", type, 171 "REDFISH_MESSAGE_ID", 172 std::string("OpenBMC.0.1.FindDeviceFail")); 173 return false; 174 } 175 176 /* Look for the eMMC in the specified searchDir directory. */ 177 for (auto const& dirEntry : std::filesystem::directory_iterator{searchDir}) 178 { 179 /* 180 * We will look at the 'type' file to determine if this is an MMC 181 * device. 182 */ 183 std::filesystem::path curPath(dirEntry.path()); 184 curPath /= "device/type"; 185 if (!std::filesystem::exists(curPath)) 186 { 187 /* The 'type' file doesn't exist. This must not be an eMMC. */ 188 continue; 189 } 190 191 try 192 { 193 std::ifstream typeFile(curPath, std::ios_base::in); 194 std::string devType; 195 typeFile >> devType; 196 if (devType.compare("MMC") == 0 || devType.compare("SD") == 0) 197 { 198 /* Found it. Get the sysfs directory and device file. */ 199 std::filesystem::path deviceName(dirEntry.path().filename()); 200 201 sysfsDir = dirEntry.path(); 202 sysfsDir /= "device"; 203 204 deviceFile = "/dev"; 205 deviceFile /= deviceName; 206 207 luksName = "luks-" + deviceName.string(); 208 return true; 209 } 210 } 211 catch (...) 212 { 213 lg2::error("Failed to read device type for {PATH}", "PATH", curPath, 214 "REDFISH_MESSAGE_ID", 215 std::string("OpenBMC.0.1.FindDeviceFail")); 216 /* 217 * We will still continue searching, though. Maybe this wasn't the 218 * device we were looking for, anyway. 219 */ 220 } 221 } 222 223 /* Device wasn't found. */ 224 return false; 225 } 226 227 } // namespace util 228 229 } // namespace estoraged 230