xref: /openbmc/phosphor-hwmon/sysfs.cpp (revision 431d26a58b89b88492a0dc5c87d3d9e8de49ade1)
1613a5b37SBrad Bishop /**
2613a5b37SBrad Bishop  * Copyright © 2016 IBM Corporation
3613a5b37SBrad Bishop  *
4613a5b37SBrad Bishop  * Licensed under the Apache License, Version 2.0 (the "License");
5613a5b37SBrad Bishop  * you may not use this file except in compliance with the License.
6613a5b37SBrad Bishop  * You may obtain a copy of the License at
7613a5b37SBrad Bishop  *
8613a5b37SBrad Bishop  *     http://www.apache.org/licenses/LICENSE-2.0
9613a5b37SBrad Bishop  *
10613a5b37SBrad Bishop  * Unless required by applicable law or agreed to in writing, software
11613a5b37SBrad Bishop  * distributed under the License is distributed on an "AS IS" BASIS,
12613a5b37SBrad Bishop  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13613a5b37SBrad Bishop  * See the License for the specific language governing permissions and
14613a5b37SBrad Bishop  * limitations under the License.
15613a5b37SBrad Bishop  */
16613a5b37SBrad Bishop #include <cstdlib>
1708379a31SBrad Bishop #include <experimental/filesystem>
1868c43b21SBrad Bishop #include <fstream>
19613a5b37SBrad Bishop #include <memory>
20048ac87fSMatthew Barth #include <phosphor-logging/elog.hpp>
21048ac87fSMatthew Barth #include <phosphor-logging/elog-errors.hpp>
22048ac87fSMatthew Barth #include <xyz/openbmc_project/Control/Device/error.hpp>
234e1f30f1SMatthew Barth #include <xyz/openbmc_project/Sensor/Device/error.hpp>
24613a5b37SBrad Bishop #include "sysfs.hpp"
25613a5b37SBrad Bishop 
26048ac87fSMatthew Barth using namespace phosphor::logging;
27f4bf63adSBrad Bishop using namespace std::string_literals;
2808379a31SBrad Bishop namespace fs = std::experimental::filesystem;
298af8a200SBrandon Wyman 
301e6324faSPatrick Venture namespace sysfs {
311e6324faSPatrick Venture 
32f4bf63adSBrad Bishop static const auto emptyString = ""s;
3308379a31SBrad Bishop static constexpr auto ofRoot = "/sys/firmware/devicetree/base";
34613a5b37SBrad Bishop 
35f4bf63adSBrad Bishop std::string findPhandleMatch(
36f4bf63adSBrad Bishop         const std::string& iochanneldir,
378af8a200SBrandon Wyman         const std::string& phandledir)
38613a5b37SBrad Bishop {
39f4bf63adSBrad Bishop     // TODO: At the moment this method only supports device trees
40f4bf63adSBrad Bishop     // with iio-hwmon nodes with a single sensor.  Typically
41f4bf63adSBrad Bishop     // device trees are defined with all the iio sensors in a
42f4bf63adSBrad Bishop     // single iio-hwmon node so it would be nice to add support
43f4bf63adSBrad Bishop     // for lists of phandles (with variable sized entries) via
44f4bf63adSBrad Bishop     // libfdt or something like that, so that users are not
45f4bf63adSBrad Bishop     // forced into implementing unusual looking device trees
46f4bf63adSBrad Bishop     // with multiple iio-hwmon nodes - one for each sensor.
47f4bf63adSBrad Bishop 
48f4bf63adSBrad Bishop     fs::path ioChannelsPath{iochanneldir};
49f4bf63adSBrad Bishop     ioChannelsPath /= "io-channels";
50f4bf63adSBrad Bishop 
51f4bf63adSBrad Bishop     if (!fs::exists(ioChannelsPath))
52f4bf63adSBrad Bishop     {
53f4bf63adSBrad Bishop         return emptyString;
54f4bf63adSBrad Bishop     }
55f4bf63adSBrad Bishop 
56f4bf63adSBrad Bishop     uint32_t ioChannelsValue;
57f4bf63adSBrad Bishop     std::ifstream ioChannelsFile(ioChannelsPath);
58f4bf63adSBrad Bishop 
59f4bf63adSBrad Bishop     ioChannelsFile.read(
60f4bf63adSBrad Bishop             reinterpret_cast<char*>(&ioChannelsValue),
61f4bf63adSBrad Bishop             sizeof(ioChannelsValue));
62f4bf63adSBrad Bishop 
638af8a200SBrandon Wyman     for (const auto& ofInst : fs::recursive_directory_iterator(phandledir))
64613a5b37SBrad Bishop     {
658af8a200SBrandon Wyman         auto path = ofInst.path();
66f4bf63adSBrad Bishop         if ("phandle" != path.filename())
674eb9858dSBrandon Wyman         {
68f4bf63adSBrad Bishop             continue;
69f4bf63adSBrad Bishop         }
70f4bf63adSBrad Bishop         std::ifstream pHandleFile(path);
714eb9858dSBrandon Wyman         uint32_t pHandleValue;
724eb9858dSBrandon Wyman 
73f4bf63adSBrad Bishop         pHandleFile.read(
74f4bf63adSBrad Bishop                 reinterpret_cast<char*>(&pHandleValue),
754eb9858dSBrandon Wyman                 sizeof(pHandleValue));
764eb9858dSBrandon Wyman 
774eb9858dSBrandon Wyman         if (ioChannelsValue == pHandleValue)
784eb9858dSBrandon Wyman         {
79f4bf63adSBrad Bishop             return path;
808af8a200SBrandon Wyman         }
818af8a200SBrandon Wyman     }
828af8a200SBrandon Wyman 
83f4bf63adSBrad Bishop     return emptyString;
848af8a200SBrandon Wyman }
858af8a200SBrandon Wyman 
86*431d26a5SBrad Bishop std::string findCalloutPath(const std::string& instancePath)
878af8a200SBrandon Wyman {
88*431d26a5SBrad Bishop     // Follow the hwmon instance (/sys/class/hwmon/hwmon<N>)
89*431d26a5SBrad Bishop     // /sys/devices symlink.
90*431d26a5SBrad Bishop     fs::path devPath{instancePath};
91*431d26a5SBrad Bishop     devPath /= "device";
928af8a200SBrandon Wyman 
93*431d26a5SBrad Bishop     try
948af8a200SBrandon Wyman     {
95*431d26a5SBrad Bishop         devPath = fs::canonical(devPath);
96*431d26a5SBrad Bishop     }
97*431d26a5SBrad Bishop     catch (const std::system_error& e)
98*431d26a5SBrad Bishop     {
99*431d26a5SBrad Bishop         return emptyString;
100*431d26a5SBrad Bishop     }
101*431d26a5SBrad Bishop 
102*431d26a5SBrad Bishop     // See if the device is backed by the iio-hwmon driver.
103*431d26a5SBrad Bishop     fs::path p{devPath};
104*431d26a5SBrad Bishop     p /= "driver";
105*431d26a5SBrad Bishop     p = fs::canonical(p);
106*431d26a5SBrad Bishop 
107*431d26a5SBrad Bishop     if (p.filename() != "iio_hwmon")
108*431d26a5SBrad Bishop     {
109*431d26a5SBrad Bishop         // Not backed by iio-hwmon.  The device pointed to
110*431d26a5SBrad Bishop         // is the callout device.
111*431d26a5SBrad Bishop         return devPath;
112*431d26a5SBrad Bishop     }
113*431d26a5SBrad Bishop 
114*431d26a5SBrad Bishop     // Find the DT path to the iio-hwmon platform device.
115*431d26a5SBrad Bishop     fs::path ofDevPath{devPath};
116*431d26a5SBrad Bishop     ofDevPath /= "of_node";
117*431d26a5SBrad Bishop 
118*431d26a5SBrad Bishop     try
119*431d26a5SBrad Bishop     {
120*431d26a5SBrad Bishop         ofDevPath = fs::canonical(ofDevPath);
121*431d26a5SBrad Bishop     }
122*431d26a5SBrad Bishop     catch (const std::system_error& e)
123*431d26a5SBrad Bishop     {
124*431d26a5SBrad Bishop         return emptyString;
125*431d26a5SBrad Bishop     }
126*431d26a5SBrad Bishop 
127*431d26a5SBrad Bishop     // Search /sys/bus/iio/devices for the phandle in io-channels.
128*431d26a5SBrad Bishop     // If a match is found, use the corresponding /sys/devices
129*431d26a5SBrad Bishop     // iio device as the callout device.
130*431d26a5SBrad Bishop     static constexpr auto iioDevices = "/sys/bus/iio/devices";
131*431d26a5SBrad Bishop     for (const auto& iioDev: fs::recursive_directory_iterator(iioDevices))
132*431d26a5SBrad Bishop     {
133*431d26a5SBrad Bishop         p = iioDev.path();
134*431d26a5SBrad Bishop         p /= "of_node";
135*431d26a5SBrad Bishop 
136*431d26a5SBrad Bishop         try
137*431d26a5SBrad Bishop         {
138*431d26a5SBrad Bishop             p = fs::canonical(p);
139*431d26a5SBrad Bishop         }
140*431d26a5SBrad Bishop         catch (const std::system_error& e)
141*431d26a5SBrad Bishop         {
142*431d26a5SBrad Bishop             continue;
143*431d26a5SBrad Bishop         }
144*431d26a5SBrad Bishop 
145*431d26a5SBrad Bishop         auto match = findPhandleMatch(ofDevPath, p);
146*431d26a5SBrad Bishop         auto n = match.rfind('/');
1478af8a200SBrandon Wyman         if (n != std::string::npos)
1488af8a200SBrandon Wyman         {
149*431d26a5SBrad Bishop             // This is the iio device referred to by io-channels.
150*431d26a5SBrad Bishop             // Remove iio:device<N>.
151*431d26a5SBrad Bishop             try
152*431d26a5SBrad Bishop             {
153*431d26a5SBrad Bishop                 return fs::canonical(iioDev).parent_path();
154*431d26a5SBrad Bishop             }
155*431d26a5SBrad Bishop             catch (const std::system_error& e)
156*431d26a5SBrad Bishop             {
157*431d26a5SBrad Bishop                 return emptyString;
158*431d26a5SBrad Bishop             }
1598af8a200SBrandon Wyman         }
1608af8a200SBrandon Wyman     }
1618af8a200SBrandon Wyman 
162*431d26a5SBrad Bishop     return emptyString;
1638af8a200SBrandon Wyman }
1648af8a200SBrandon Wyman 
1658af8a200SBrandon Wyman std::string findHwmon(const std::string& ofNode)
1668af8a200SBrandon Wyman {
1678af8a200SBrandon Wyman     static constexpr auto hwmonRoot = "/sys/class/hwmon";
1688af8a200SBrandon Wyman 
1698af8a200SBrandon Wyman     fs::path fullOfPath{ofRoot};
1708af8a200SBrandon Wyman     fullOfPath /= ofNode;
1718af8a200SBrandon Wyman 
1728af8a200SBrandon Wyman     for (const auto& hwmonInst : fs::directory_iterator(hwmonRoot))
1738af8a200SBrandon Wyman     {
1748af8a200SBrandon Wyman         auto path = hwmonInst.path();
1758af8a200SBrandon Wyman         path /= "of_node";
1764e24ebd6SBrad Bishop 
1774e24ebd6SBrad Bishop         try
1788af8a200SBrandon Wyman         {
1794e24ebd6SBrad Bishop             path = fs::canonical(path);
1808af8a200SBrandon Wyman         }
1814e24ebd6SBrad Bishop         catch (const std::system_error& e)
1828af8a200SBrandon Wyman         {
1834e24ebd6SBrad Bishop             // realpath may encounter ENOENT (Hwmon
1844e24ebd6SBrad Bishop             // instances have a nasty habit of
1854e24ebd6SBrad Bishop             // going away without warning).
1868af8a200SBrandon Wyman             continue;
1878af8a200SBrandon Wyman         }
1888af8a200SBrandon Wyman 
1894e24ebd6SBrad Bishop         if (path == fullOfPath)
1904e24ebd6SBrad Bishop         {
19108379a31SBrad Bishop             return hwmonInst.path();
192613a5b37SBrad Bishop         }
193613a5b37SBrad Bishop 
1944e24ebd6SBrad Bishop         // Try to find HWMON instance via phandle values.
1954e24ebd6SBrad Bishop         // Used for IIO device drivers.
1964e24ebd6SBrad Bishop         auto matchpath = findPhandleMatch(path, fullOfPath);
1974e24ebd6SBrad Bishop         if (!matchpath.empty())
1984e24ebd6SBrad Bishop         {
1994e24ebd6SBrad Bishop             return hwmonInst.path();
2004e24ebd6SBrad Bishop         }
2014e24ebd6SBrad Bishop     }
2024e24ebd6SBrad Bishop 
2034e24ebd6SBrad Bishop     return emptyString;
204613a5b37SBrad Bishop }
205613a5b37SBrad Bishop 
2064db64422SBrad Bishop int readSysfsWithCallout(const std::string& root,
2074db64422SBrad Bishop                          const std::string& instance,
2084db64422SBrad Bishop                          const std::string& type,
2094db64422SBrad Bishop                          const std::string& id,
2103b8e36e2SMatt Spinler                          const std::string& sensor,
2113b8e36e2SMatt Spinler                          bool throwDeviceBusy)
2124db64422SBrad Bishop {
2135ec68abbSBrad Bishop     namespace fs = std::experimental::filesystem;
2145ec68abbSBrad Bishop 
2154db64422SBrad Bishop     int value = 0;
2165ec68abbSBrad Bishop     std::ifstream ifs;
2175ec68abbSBrad Bishop     fs::path instancePath{root};
2185ec68abbSBrad Bishop     instancePath /= instance;
2194db64422SBrad Bishop     std::string fullPath = make_sysfs_path(instancePath,
2204db64422SBrad Bishop                                            type, id, sensor);
2214db64422SBrad Bishop 
2224db64422SBrad Bishop     ifs.exceptions(std::ifstream::failbit
2234db64422SBrad Bishop                    | std::ifstream::badbit
2244db64422SBrad Bishop                    | std::ifstream::eofbit);
2254db64422SBrad Bishop     try
2264db64422SBrad Bishop     {
2274db64422SBrad Bishop         ifs.open(fullPath);
2284db64422SBrad Bishop         ifs >> value;
2294db64422SBrad Bishop     }
2304db64422SBrad Bishop     catch (const std::exception& e)
2314db64422SBrad Bishop     {
2324db64422SBrad Bishop         // Too many GCC bugs (53984, 66145) to do
2334db64422SBrad Bishop         // this the right way...
2344db64422SBrad Bishop 
2354db64422SBrad Bishop         // errno should still reflect the error from the failing open
2364db64422SBrad Bishop         // or read system calls that got us here.
2374db64422SBrad Bishop         auto rc = errno;
238ac8b7c6bSAndrew Geissler 
2393b8e36e2SMatt Spinler         if ((rc == EAGAIN) && throwDeviceBusy)
2403b8e36e2SMatt Spinler         {
2413b8e36e2SMatt Spinler             throw DeviceBusyException(fullPath);
2423b8e36e2SMatt Spinler         }
2433b8e36e2SMatt Spinler 
244ac8b7c6bSAndrew Geissler         // If the directory disappeared then this application should gracefully
245ac8b7c6bSAndrew Geissler         // exit.  There are race conditions between the unloading of a hwmon
246ac8b7c6bSAndrew Geissler         // driver and the stopping of this service by systemd.  To prevent
247ac8b7c6bSAndrew Geissler         // this application from falsely failing in these scenarios, it will
248ac8b7c6bSAndrew Geissler         // simply exit if the directory or file can not be found.  It is up
249ac8b7c6bSAndrew Geissler         // to the user(s) of this provided hwmon object to log the appropriate
250ac8b7c6bSAndrew Geissler         // errors if the object disappears when it should not.
251ac8b7c6bSAndrew Geissler         if (rc == ENOENT)
252ac8b7c6bSAndrew Geissler         {
253ac8b7c6bSAndrew Geissler             exit(0);
254ac8b7c6bSAndrew Geissler         }
2555ec68abbSBrad Bishop         instancePath /= "device";
256*431d26a5SBrad Bishop         auto callOutPath = findCalloutPath(instancePath);
2574e1f30f1SMatthew Barth         using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::Error;
2581e6324faSPatrick Venture 
2591e6324faSPatrick Venture         // this throws a ReadFailure.
2601e6324faSPatrick Venture         elog<ReadFailure>(
2614e1f30f1SMatthew Barth             xyz::openbmc_project::Sensor::Device::
2624e1f30f1SMatthew Barth                 ReadFailure::CALLOUT_ERRNO(rc),
2634e1f30f1SMatthew Barth             xyz::openbmc_project::Sensor::Device::
264*431d26a5SBrad Bishop                 ReadFailure::CALLOUT_DEVICE_PATH(callOutPath.c_str()));
2654db64422SBrad Bishop     }
2664db64422SBrad Bishop 
2674db64422SBrad Bishop     return value;
2684db64422SBrad Bishop }
2694db64422SBrad Bishop 
270048ac87fSMatthew Barth uint64_t writeSysfsWithCallout(const uint64_t& value,
271048ac87fSMatthew Barth                                const std::string& root,
272048ac87fSMatthew Barth                                const std::string& instance,
273048ac87fSMatthew Barth                                const std::string& type,
274048ac87fSMatthew Barth                                const std::string& id,
275048ac87fSMatthew Barth                                const std::string& sensor)
276048ac87fSMatthew Barth {
277048ac87fSMatthew Barth     namespace fs = std::experimental::filesystem;
278048ac87fSMatthew Barth 
279048ac87fSMatthew Barth     std::string valueStr = std::to_string(value);
280048ac87fSMatthew Barth     std::ofstream ofs;
281048ac87fSMatthew Barth     fs::path instancePath{root};
282048ac87fSMatthew Barth     instancePath /= instance;
283048ac87fSMatthew Barth     std::string fullPath = make_sysfs_path(instancePath,
284048ac87fSMatthew Barth                                            type, id, sensor);
285048ac87fSMatthew Barth 
286048ac87fSMatthew Barth     ofs.exceptions(std::ofstream::failbit
287048ac87fSMatthew Barth                    | std::ofstream::badbit
288048ac87fSMatthew Barth                    | std::ofstream::eofbit);
289048ac87fSMatthew Barth     try
290048ac87fSMatthew Barth     {
291048ac87fSMatthew Barth         ofs.open(fullPath);
292048ac87fSMatthew Barth         ofs << valueStr;
293048ac87fSMatthew Barth     }
294048ac87fSMatthew Barth     catch (const std::exception& e)
295048ac87fSMatthew Barth     {
296048ac87fSMatthew Barth         // errno should still reflect the error from the failing open
297048ac87fSMatthew Barth         // or write system calls that got us here.
298048ac87fSMatthew Barth         auto rc = errno;
299048ac87fSMatthew Barth         instancePath /= "device";
300*431d26a5SBrad Bishop         auto callOutPath = findCalloutPath(instancePath);
301048ac87fSMatthew Barth         using namespace sdbusplus::xyz::openbmc_project::Control::Device::Error;
30205711eb0SMarri Devender Rao         report<WriteFailure>(
303048ac87fSMatthew Barth             xyz::openbmc_project::Control::Device::
304048ac87fSMatthew Barth                 WriteFailure::CALLOUT_ERRNO(rc),
305048ac87fSMatthew Barth             xyz::openbmc_project::Control::Device::
306*431d26a5SBrad Bishop                 WriteFailure::CALLOUT_DEVICE_PATH(callOutPath.c_str()));
30705711eb0SMarri Devender Rao 
308048ac87fSMatthew Barth         exit(EXIT_FAILURE);
309048ac87fSMatthew Barth     }
310048ac87fSMatthew Barth 
311048ac87fSMatthew Barth     return value;
312048ac87fSMatthew Barth }
313048ac87fSMatthew Barth 
3141e6324faSPatrick Venture }
315613a5b37SBrad Bishop // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
316