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
findSizeOfBlockDevice(const std::string & devPath)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
findPredictedMediaLifeLeftPercent(const std::string & sysfsPath)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
getPartNumber(const std::filesystem::path & sysfsPath)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
getSerialNumber(const std::filesystem::path & sysfsPath)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
findDevice(const StorageData & data,const std::filesystem::path & searchDir)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