xref: /openbmc/estoraged/src/util.cpp (revision 620d1a51)
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