1a6e3b99dSJohn Edward Broadbent #include "util.hpp"
2a6e3b99dSJohn Edward Broadbent
3043af59fSTom Tung #include "estoraged_conf.hpp"
4d32b9667SJohn Wedig #include "getConfig.hpp"
5d32b9667SJohn Wedig
6a6e3b99dSJohn Edward Broadbent #include <linux/fs.h>
7a6e3b99dSJohn Edward Broadbent
8a6e3b99dSJohn Edward Broadbent #include <phosphor-logging/lg2.hpp>
9a6e3b99dSJohn Edward Broadbent #include <stdplus/fd/create.hpp>
10a6e3b99dSJohn Edward Broadbent #include <stdplus/fd/managed.hpp>
11a6e3b99dSJohn Edward Broadbent #include <stdplus/handle/managed.hpp>
12a6e3b99dSJohn Edward Broadbent #include <xyz/openbmc_project/Common/error.hpp>
13a6e3b99dSJohn Edward Broadbent
14d32b9667SJohn Wedig #include <filesystem>
155d799bb9SJohn Edward Broadbent #include <fstream>
165d799bb9SJohn Edward Broadbent #include <iostream>
17043af59fSTom Tung #include <optional>
185d799bb9SJohn Edward Broadbent #include <string>
19a6e3b99dSJohn Edward Broadbent
20a6e3b99dSJohn Edward Broadbent namespace estoraged
21a6e3b99dSJohn Edward Broadbent {
22a6e3b99dSJohn Edward Broadbent namespace util
23a6e3b99dSJohn Edward Broadbent {
24a6e3b99dSJohn Edward Broadbent using ::sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
25a6e3b99dSJohn Edward Broadbent using ::stdplus::fd::ManagedFd;
26a6e3b99dSJohn Edward Broadbent
findSizeOfBlockDevice(const std::string & devPath)275d799bb9SJohn Edward Broadbent uint64_t findSizeOfBlockDevice(const std::string& devPath)
28a6e3b99dSJohn Edward Broadbent {
29a6e3b99dSJohn Edward Broadbent ManagedFd fd;
30a6e3b99dSJohn Edward Broadbent uint64_t bytes = 0;
31a6e3b99dSJohn Edward Broadbent try
32a6e3b99dSJohn Edward Broadbent {
33a6e3b99dSJohn Edward Broadbent // open block dev
34a6e3b99dSJohn Edward Broadbent fd = stdplus::fd::open(devPath, stdplus::fd::OpenAccess::ReadOnly);
35a6e3b99dSJohn Edward Broadbent // get block size
36a6e3b99dSJohn Edward Broadbent fd.ioctl(BLKGETSIZE64, &bytes);
37a6e3b99dSJohn Edward Broadbent }
38a6e3b99dSJohn Edward Broadbent catch (...)
39a6e3b99dSJohn Edward Broadbent {
40a6e3b99dSJohn Edward Broadbent lg2::error("erase unable to open blockdev", "REDFISH_MESSAGE_ID",
41a6e3b99dSJohn Edward Broadbent std::string("OpenBMC.0.1.DriveEraseFailure"),
42a6e3b99dSJohn Edward Broadbent "REDFISH_MESSAGE_ARGS", std::to_string(fd.get()));
43a6e3b99dSJohn Edward Broadbent throw InternalFailure();
44a6e3b99dSJohn Edward Broadbent }
45a6e3b99dSJohn Edward Broadbent return bytes;
46a6e3b99dSJohn Edward Broadbent }
47a6e3b99dSJohn Edward Broadbent
findPredictedMediaLifeLeftPercent(const std::string & sysfsPath)485d799bb9SJohn Edward Broadbent uint8_t findPredictedMediaLifeLeftPercent(const std::string& sysfsPath)
495d799bb9SJohn Edward Broadbent {
505d799bb9SJohn Edward Broadbent // The eMMC spec defines two estimates for the life span of the device
515d799bb9SJohn Edward Broadbent // in the extended CSD field 269 and 268, named estimate A and estimate B.
525d799bb9SJohn Edward Broadbent // Linux exposes the values in the /life_time node.
535d799bb9SJohn Edward Broadbent // estimate A is for A type memory
545d799bb9SJohn Edward Broadbent // estimate B is for B type memory
555d799bb9SJohn Edward Broadbent //
565d799bb9SJohn Edward Broadbent // the estimate are encoded as such
575d799bb9SJohn Edward Broadbent // 0x01 <=> 0% - 10% device life time used
585d799bb9SJohn Edward Broadbent // 0x02 <=> 10% -20% device life time used
595d799bb9SJohn Edward Broadbent // ...
605d799bb9SJohn Edward Broadbent // 0x0A <=> 90% - 100% device life time used
615d799bb9SJohn Edward Broadbent // 0x0B <=> Exceeded its maximum estimated device life time
625d799bb9SJohn Edward Broadbent
635d799bb9SJohn Edward Broadbent uint16_t estA = 0, estB = 0;
645d799bb9SJohn Edward Broadbent std::ifstream lifeTimeFile;
655d799bb9SJohn Edward Broadbent try
665d799bb9SJohn Edward Broadbent {
675d799bb9SJohn Edward Broadbent lifeTimeFile.open(sysfsPath + "/life_time", std::ios_base::in);
685d799bb9SJohn Edward Broadbent lifeTimeFile >> std::hex >> estA;
695d799bb9SJohn Edward Broadbent lifeTimeFile >> std::hex >> estB;
705d799bb9SJohn Edward Broadbent if ((estA == 0) || (estA > 11) || (estB == 0) || (estB > 11))
715d799bb9SJohn Edward Broadbent {
725d799bb9SJohn Edward Broadbent throw InternalFailure();
735d799bb9SJohn Edward Broadbent }
745d799bb9SJohn Edward Broadbent }
755d799bb9SJohn Edward Broadbent catch (...)
765d799bb9SJohn Edward Broadbent {
775d799bb9SJohn Edward Broadbent lg2::error("Unable to read sysfs", "REDFISH_MESSAGE_ID",
785d799bb9SJohn Edward Broadbent std::string("OpenBMC.0.1.DriveEraseFailure"));
795d799bb9SJohn Edward Broadbent lifeTimeFile.close();
805d799bb9SJohn Edward Broadbent return 255;
815d799bb9SJohn Edward Broadbent }
825d799bb9SJohn Edward Broadbent lifeTimeFile.close();
835d799bb9SJohn Edward Broadbent // we are returning lowest LifeLeftPercent
845d799bb9SJohn Edward Broadbent uint8_t maxLifeUsed = 0;
855d799bb9SJohn Edward Broadbent if (estA > estB)
865d799bb9SJohn Edward Broadbent {
875d799bb9SJohn Edward Broadbent maxLifeUsed = estA;
885d799bb9SJohn Edward Broadbent }
895d799bb9SJohn Edward Broadbent else
905d799bb9SJohn Edward Broadbent {
915d799bb9SJohn Edward Broadbent maxLifeUsed = estB;
925d799bb9SJohn Edward Broadbent }
935d799bb9SJohn Edward Broadbent
945d799bb9SJohn Edward Broadbent return static_cast<uint8_t>(11 - maxLifeUsed) * 10;
955d799bb9SJohn Edward Broadbent }
965d799bb9SJohn Edward Broadbent
getPartNumber(const std::filesystem::path & sysfsPath)97b4838308SJohn Wedig std::string getPartNumber(const std::filesystem::path& sysfsPath)
98b4838308SJohn Wedig {
99b4838308SJohn Wedig std::ifstream partNameFile;
100b4838308SJohn Wedig std::string partName;
101b4838308SJohn Wedig try
102b4838308SJohn Wedig {
103b4838308SJohn Wedig std::filesystem::path namePath(sysfsPath);
104b4838308SJohn Wedig namePath /= "name";
105b4838308SJohn Wedig partNameFile.open(namePath, std::ios_base::in);
106b4838308SJohn Wedig partNameFile >> partName;
107b4838308SJohn Wedig }
108b4838308SJohn Wedig catch (...)
109b4838308SJohn Wedig {
110b4838308SJohn Wedig lg2::error("Unable to read sysfs", "REDFISH_MESSAGE_ID",
111b4838308SJohn Wedig std::string("OpenBMC.0.1.PartNumberFailure"));
112b4838308SJohn Wedig }
113b4838308SJohn Wedig partNameFile.close();
114b4838308SJohn Wedig if (partName.empty())
115b4838308SJohn Wedig {
116b4838308SJohn Wedig partName = "unknown";
117b4838308SJohn Wedig }
118b4838308SJohn Wedig
119b4838308SJohn Wedig return partName;
120b4838308SJohn Wedig }
121b4838308SJohn Wedig
getSerialNumber(const std::filesystem::path & sysfsPath)122b4838308SJohn Wedig std::string getSerialNumber(const std::filesystem::path& sysfsPath)
123b4838308SJohn Wedig {
124b4838308SJohn Wedig std::ifstream serialNumberFile;
125b4838308SJohn Wedig std::string serialNumber;
126b4838308SJohn Wedig try
127b4838308SJohn Wedig {
128b4838308SJohn Wedig std::filesystem::path serialPath(sysfsPath);
129b4838308SJohn Wedig serialPath /= "serial";
130b4838308SJohn Wedig serialNumberFile.open(serialPath, std::ios_base::in);
131b4838308SJohn Wedig serialNumberFile >> serialNumber;
132b4838308SJohn Wedig }
133b4838308SJohn Wedig catch (...)
134b4838308SJohn Wedig {
135b4838308SJohn Wedig lg2::error("Unable to read sysfs", "REDFISH_MESSAGE_ID",
136b4838308SJohn Wedig std::string("OpenBMC.0.1.SerialNumberFailure"));
137b4838308SJohn Wedig }
138b4838308SJohn Wedig serialNumberFile.close();
139b4838308SJohn Wedig if (serialNumber.empty())
140b4838308SJohn Wedig {
141b4838308SJohn Wedig serialNumber = "unknown";
142b4838308SJohn Wedig }
143b4838308SJohn Wedig
144b4838308SJohn Wedig return serialNumber;
145b4838308SJohn Wedig }
146b4838308SJohn Wedig
findDevice(const StorageData & data,const std::filesystem::path & searchDir)147043af59fSTom Tung std::optional<DeviceInfo> findDevice(const StorageData& data,
148043af59fSTom Tung const std::filesystem::path& searchDir)
149d32b9667SJohn Wedig {
150d32b9667SJohn Wedig /* Check what type of storage device this is. */
151d32b9667SJohn Wedig estoraged::BasicVariantType typeVariant;
152d32b9667SJohn Wedig try
153d32b9667SJohn Wedig {
154d32b9667SJohn Wedig /* The EntityManager config should have this property set. */
155d32b9667SJohn Wedig typeVariant = data.at("Type");
156d32b9667SJohn Wedig }
157d32b9667SJohn Wedig catch (const boost::container::out_of_range& e)
158d32b9667SJohn Wedig {
159d32b9667SJohn Wedig lg2::error("Could not read device type", "REDFISH_MESSAGE_ID",
160d32b9667SJohn Wedig std::string("OpenBMC.0.1.FindDeviceFail"));
161043af59fSTom Tung return std::nullopt;
162d32b9667SJohn Wedig }
163d32b9667SJohn Wedig
16419825057SRahul Kapoor /* Check if location code/ silkscreen name is provided for the drive. */
165043af59fSTom Tung std::string locationCode;
16619825057SRahul Kapoor auto findLocationCode = data.find("LocationCode");
16719825057SRahul Kapoor if (findLocationCode != data.end())
16819825057SRahul Kapoor {
16919825057SRahul Kapoor const std::string* locationCodePtr =
17019825057SRahul Kapoor std::get_if<std::string>(&findLocationCode->second);
17119825057SRahul Kapoor if (locationCodePtr != nullptr)
17219825057SRahul Kapoor {
17319825057SRahul Kapoor locationCode = *locationCodePtr;
17419825057SRahul Kapoor }
17519825057SRahul Kapoor }
17619825057SRahul Kapoor
177043af59fSTom Tung /* Check if EraseMaxGeometry is provided. */
178043af59fSTom Tung uint64_t eraseMaxGeometry = ERASE_MAX_GEOMETRY;
179043af59fSTom Tung auto findEraseMaxGeometry = data.find("EraseMaxGeometry");
180043af59fSTom Tung if (findEraseMaxGeometry != data.end())
181043af59fSTom Tung {
182043af59fSTom Tung const auto* eraseMaxGeometryPtr =
183043af59fSTom Tung std::get_if<uint64_t>(&findEraseMaxGeometry->second);
184043af59fSTom Tung if (eraseMaxGeometryPtr != nullptr)
185043af59fSTom Tung {
186043af59fSTom Tung lg2::info("eStorageD new eraseMaxGeometry found on system");
187043af59fSTom Tung eraseMaxGeometry = *eraseMaxGeometryPtr;
188043af59fSTom Tung }
189043af59fSTom Tung }
190043af59fSTom Tung
191043af59fSTom Tung /* Check if EraseMinGeometry is provided. */
192043af59fSTom Tung uint64_t eraseMinGeometry = ERASE_MIN_GEOMETRY;
193043af59fSTom Tung auto findEraseMinGeometry = data.find("EraseMinGeometry");
194043af59fSTom Tung if (findEraseMinGeometry != data.end())
195043af59fSTom Tung {
196043af59fSTom Tung const auto* eraseMinGeometryPtr =
197043af59fSTom Tung std::get_if<uint64_t>(&findEraseMinGeometry->second);
198043af59fSTom Tung if (eraseMinGeometryPtr != nullptr)
199043af59fSTom Tung {
200043af59fSTom Tung lg2::info("eStorageD new eraseMinGeometry found on system");
201043af59fSTom Tung eraseMinGeometry = *eraseMinGeometryPtr;
202043af59fSTom Tung }
203043af59fSTom Tung }
204043af59fSTom Tung
205d32b9667SJohn Wedig /*
206*c0d66eb7SJohn Wedig * Determine the drive type and protocol to report for this device. Note
207*c0d66eb7SJohn Wedig * that we only support eMMC currently, so report an error for any other
208*c0d66eb7SJohn Wedig * device types.
209d32b9667SJohn Wedig */
210d7be42bdSJohn Wedig std::string deviceType = std::get<std::string>(typeVariant);
211*c0d66eb7SJohn Wedig /* drive type and protocol to report in the Item.Drive dbus interface */
212d7be42bdSJohn Wedig std::string driveType;
213*c0d66eb7SJohn Wedig std::string driveProtocol;
214d7be42bdSJohn Wedig if (deviceType.compare("EmmcDevice") == 0)
215d32b9667SJohn Wedig {
216d7be42bdSJohn Wedig driveType = "SSD";
217*c0d66eb7SJohn Wedig driveProtocol = "eMMC";
218d7be42bdSJohn Wedig }
219d7be42bdSJohn Wedig else
220d7be42bdSJohn Wedig {
221d7be42bdSJohn Wedig lg2::error("Unsupported device type {TYPE}", "TYPE", deviceType,
222d32b9667SJohn Wedig "REDFISH_MESSAGE_ID",
223d32b9667SJohn Wedig std::string("OpenBMC.0.1.FindDeviceFail"));
224043af59fSTom Tung return std::nullopt;
225d32b9667SJohn Wedig }
226d32b9667SJohn Wedig
227d32b9667SJohn Wedig /* Look for the eMMC in the specified searchDir directory. */
22804c28fadSPatrick Williams for (const auto& dirEntry : std::filesystem::directory_iterator{searchDir})
229d32b9667SJohn Wedig {
230f78215fdSJohn Wedig /*
231f78215fdSJohn Wedig * We will look at the 'type' file to determine if this is an MMC
232f78215fdSJohn Wedig * device.
233f78215fdSJohn Wedig */
234f78215fdSJohn Wedig std::filesystem::path curPath(dirEntry.path());
235f78215fdSJohn Wedig curPath /= "device/type";
236f78215fdSJohn Wedig if (!std::filesystem::exists(curPath))
237d32b9667SJohn Wedig {
238f78215fdSJohn Wedig /* The 'type' file doesn't exist. This must not be an eMMC. */
239f78215fdSJohn Wedig continue;
240f78215fdSJohn Wedig }
241f78215fdSJohn Wedig
242f78215fdSJohn Wedig try
243f78215fdSJohn Wedig {
244f78215fdSJohn Wedig std::ifstream typeFile(curPath, std::ios_base::in);
245f78215fdSJohn Wedig std::string devType;
246f78215fdSJohn Wedig typeFile >> devType;
2479be2f0fcSJohn Edward Broadbent if (devType.compare("MMC") == 0 || devType.compare("SD") == 0)
248f78215fdSJohn Wedig {
249f78215fdSJohn Wedig /* Found it. Get the sysfs directory and device file. */
250f78215fdSJohn Wedig std::filesystem::path deviceName(dirEntry.path().filename());
251f78215fdSJohn Wedig
252043af59fSTom Tung std::filesystem::path sysfsDir = dirEntry.path();
253d32b9667SJohn Wedig sysfsDir /= "device";
254d32b9667SJohn Wedig
255043af59fSTom Tung std::filesystem::path deviceFile = "/dev";
256f78215fdSJohn Wedig deviceFile /= deviceName;
257d32b9667SJohn Wedig
258043af59fSTom Tung std::string luksName = "luks-" + deviceName.string();
259*c0d66eb7SJohn Wedig return DeviceInfo{deviceFile, sysfsDir,
260*c0d66eb7SJohn Wedig luksName, locationCode,
261*c0d66eb7SJohn Wedig eraseMaxGeometry, eraseMinGeometry,
262*c0d66eb7SJohn Wedig driveType, driveProtocol};
263d32b9667SJohn Wedig }
264d32b9667SJohn Wedig }
265f78215fdSJohn Wedig catch (...)
266f78215fdSJohn Wedig {
267f78215fdSJohn Wedig lg2::error("Failed to read device type for {PATH}", "PATH", curPath,
268f78215fdSJohn Wedig "REDFISH_MESSAGE_ID",
269f78215fdSJohn Wedig std::string("OpenBMC.0.1.FindDeviceFail"));
270f78215fdSJohn Wedig /*
271f78215fdSJohn Wedig * We will still continue searching, though. Maybe this wasn't the
272f78215fdSJohn Wedig * device we were looking for, anyway.
273f78215fdSJohn Wedig */
274f78215fdSJohn Wedig }
275f78215fdSJohn Wedig }
276d32b9667SJohn Wedig
277d32b9667SJohn Wedig /* Device wasn't found. */
278043af59fSTom Tung return std::nullopt;
279d32b9667SJohn Wedig }
280d32b9667SJohn Wedig
281a6e3b99dSJohn Edward Broadbent } // namespace util
282a6e3b99dSJohn Edward Broadbent
283a6e3b99dSJohn Edward Broadbent } // namespace estoraged
284