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