xref: /openbmc/estoraged/src/util.cpp (revision 19825057)
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                 std::string& locationCode)
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 false;
162     }
163 
164     /* Check if location code/ silkscreen name is provided for the drive. */
165     auto findLocationCode = data.find("LocationCode");
166     if (findLocationCode != data.end())
167     {
168         const std::string* locationCodePtr =
169             std::get_if<std::string>(&findLocationCode->second);
170         if (locationCodePtr != nullptr)
171         {
172             locationCode = *locationCodePtr;
173         }
174     }
175 
176     /*
177      * Currently, we only support eMMC, so report an error for any other device
178      * types.
179      */
180     std::string type = std::get<std::string>(typeVariant);
181     if (type.compare("EmmcDevice") != 0)
182     {
183         lg2::error("Unsupported device type {TYPE}", "TYPE", type,
184                    "REDFISH_MESSAGE_ID",
185                    std::string("OpenBMC.0.1.FindDeviceFail"));
186         return false;
187     }
188 
189     /* Look for the eMMC in the specified searchDir directory. */
190     for (const auto& dirEntry : std::filesystem::directory_iterator{searchDir})
191     {
192         /*
193          * We will look at the 'type' file to determine if this is an MMC
194          * device.
195          */
196         std::filesystem::path curPath(dirEntry.path());
197         curPath /= "device/type";
198         if (!std::filesystem::exists(curPath))
199         {
200             /* The 'type' file doesn't exist. This must not be an eMMC. */
201             continue;
202         }
203 
204         try
205         {
206             std::ifstream typeFile(curPath, std::ios_base::in);
207             std::string devType;
208             typeFile >> devType;
209             if (devType.compare("MMC") == 0 || devType.compare("SD") == 0)
210             {
211                 /* Found it. Get the sysfs directory and device file. */
212                 std::filesystem::path deviceName(dirEntry.path().filename());
213 
214                 sysfsDir = dirEntry.path();
215                 sysfsDir /= "device";
216 
217                 deviceFile = "/dev";
218                 deviceFile /= deviceName;
219 
220                 luksName = "luks-" + deviceName.string();
221                 return true;
222             }
223         }
224         catch (...)
225         {
226             lg2::error("Failed to read device type for {PATH}", "PATH", curPath,
227                        "REDFISH_MESSAGE_ID",
228                        std::string("OpenBMC.0.1.FindDeviceFail"));
229             /*
230              * We will still continue searching, though. Maybe this wasn't the
231              * device we were looking for, anyway.
232              */
233         }
234     }
235 
236     /* Device wasn't found. */
237     return false;
238 }
239 
240 } // namespace util
241 
242 } // namespace estoraged
243