blob: cf1b3841eba8add24d3fa615b5e07f18b47660c8 [file] [log] [blame]
Brad Bishop613a5b32017-01-05 20:58:13 -05001/**
2 * Copyright © 2016 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Brad Bishop8b574a72017-08-25 16:17:19 -040016#include <cerrno>
Brad Bishop613a5b32017-01-05 20:58:13 -050017#include <cstdlib>
Brad Bishop08379a32017-03-06 21:28:46 -050018#include <experimental/filesystem>
Brad Bishop68c43b22017-08-28 16:24:00 -040019#include <fstream>
Brad Bishop613a5b32017-01-05 20:58:13 -050020#include <memory>
Matthew Barth048ac872017-03-09 14:36:08 -060021#include <phosphor-logging/elog.hpp>
22#include <phosphor-logging/elog-errors.hpp>
23#include <xyz/openbmc_project/Control/Device/error.hpp>
Matthew Barth4e1f30f2017-03-21 16:13:27 -050024#include <xyz/openbmc_project/Sensor/Device/error.hpp>
Brad Bishop613a5b32017-01-05 20:58:13 -050025#include "sysfs.hpp"
Brad Bishop613a5b32017-01-05 20:58:13 -050026
Matthew Barth048ac872017-03-09 14:36:08 -060027using namespace phosphor::logging;
Brad Bishopf4bf63a2017-08-28 15:39:19 -040028using namespace std::string_literals;
Brandon Wyman8af8a202017-05-31 18:26:30 -050029namespace fs = std::experimental::filesystem;
Matthew Barth048ac872017-03-09 14:36:08 -060030
Patrick Venture1e6324f2017-06-01 14:07:05 -070031namespace sysfs {
32
Brad Bishopf4bf63a2017-08-28 15:39:19 -040033static const auto emptyString = ""s;
Brandon Wyman8af8a202017-05-31 18:26:30 -050034static constexpr auto ofRoot = "/sys/firmware/devicetree/base";
35
Brad Bishopf4bf63a2017-08-28 15:39:19 -040036std::string findPhandleMatch(
37 const std::string& iochanneldir,
38 const std::string& phandledir)
Brad Bishop613a5b32017-01-05 20:58:13 -050039{
Brad Bishopf4bf63a2017-08-28 15:39:19 -040040 // TODO: At the moment this method only supports device trees
41 // with iio-hwmon nodes with a single sensor. Typically
42 // device trees are defined with all the iio sensors in a
43 // single iio-hwmon node so it would be nice to add support
44 // for lists of phandles (with variable sized entries) via
45 // libfdt or something like that, so that users are not
46 // forced into implementing unusual looking device trees
47 // with multiple iio-hwmon nodes - one for each sensor.
48
49 fs::path ioChannelsPath{iochanneldir};
50 ioChannelsPath /= "io-channels";
51
52 if (!fs::exists(ioChannelsPath))
53 {
54 return emptyString;
55 }
56
57 uint32_t ioChannelsValue;
58 std::ifstream ioChannelsFile(ioChannelsPath);
59
60 ioChannelsFile.read(
61 reinterpret_cast<char*>(&ioChannelsValue),
62 sizeof(ioChannelsValue));
63
Brandon Wyman8af8a202017-05-31 18:26:30 -050064 for (const auto& ofInst : fs::recursive_directory_iterator(phandledir))
Brad Bishop613a5b32017-01-05 20:58:13 -050065 {
Brandon Wyman8af8a202017-05-31 18:26:30 -050066 auto path = ofInst.path();
Brad Bishopf4bf63a2017-08-28 15:39:19 -040067 if ("phandle" != path.filename())
Brad Bishop613a5b32017-01-05 20:58:13 -050068 {
Brad Bishopf4bf63a2017-08-28 15:39:19 -040069 continue;
70 }
71 std::ifstream pHandleFile(path);
72 uint32_t pHandleValue;
Brandon Wyman4eb98582017-05-24 14:24:00 -050073
Brad Bishopf4bf63a2017-08-28 15:39:19 -040074 pHandleFile.read(
75 reinterpret_cast<char*>(&pHandleValue),
76 sizeof(pHandleValue));
Brandon Wyman4eb98582017-05-24 14:24:00 -050077
Brad Bishopf4bf63a2017-08-28 15:39:19 -040078 if (ioChannelsValue == pHandleValue)
79 {
80 return path;
Brandon Wyman8af8a202017-05-31 18:26:30 -050081 }
82 }
83
Brad Bishopf4bf63a2017-08-28 15:39:19 -040084 return emptyString;
Brandon Wyman8af8a202017-05-31 18:26:30 -050085}
86
Brad Bishop431d26a2017-08-25 09:47:58 -040087std::string findCalloutPath(const std::string& instancePath)
Brandon Wyman8af8a202017-05-31 18:26:30 -050088{
Brad Bishop431d26a2017-08-25 09:47:58 -040089 // Follow the hwmon instance (/sys/class/hwmon/hwmon<N>)
90 // /sys/devices symlink.
91 fs::path devPath{instancePath};
92 devPath /= "device";
Brandon Wyman8af8a202017-05-31 18:26:30 -050093
Brad Bishop431d26a2017-08-25 09:47:58 -040094 try
Brandon Wyman8af8a202017-05-31 18:26:30 -050095 {
Brad Bishop431d26a2017-08-25 09:47:58 -040096 devPath = fs::canonical(devPath);
97 }
98 catch (const std::system_error& e)
99 {
100 return emptyString;
101 }
102
103 // See if the device is backed by the iio-hwmon driver.
104 fs::path p{devPath};
105 p /= "driver";
106 p = fs::canonical(p);
107
108 if (p.filename() != "iio_hwmon")
109 {
110 // Not backed by iio-hwmon. The device pointed to
111 // is the callout device.
112 return devPath;
113 }
114
115 // Find the DT path to the iio-hwmon platform device.
116 fs::path ofDevPath{devPath};
117 ofDevPath /= "of_node";
118
119 try
120 {
121 ofDevPath = fs::canonical(ofDevPath);
122 }
123 catch (const std::system_error& e)
124 {
125 return emptyString;
126 }
127
128 // Search /sys/bus/iio/devices for the phandle in io-channels.
129 // If a match is found, use the corresponding /sys/devices
130 // iio device as the callout device.
131 static constexpr auto iioDevices = "/sys/bus/iio/devices";
132 for (const auto& iioDev: fs::recursive_directory_iterator(iioDevices))
133 {
134 p = iioDev.path();
135 p /= "of_node";
136
137 try
138 {
139 p = fs::canonical(p);
140 }
141 catch (const std::system_error& e)
142 {
143 continue;
144 }
145
146 auto match = findPhandleMatch(ofDevPath, p);
147 auto n = match.rfind('/');
Brandon Wyman8af8a202017-05-31 18:26:30 -0500148 if (n != std::string::npos)
149 {
Brad Bishop431d26a2017-08-25 09:47:58 -0400150 // This is the iio device referred to by io-channels.
151 // Remove iio:device<N>.
152 try
153 {
154 return fs::canonical(iioDev).parent_path();
155 }
156 catch (const std::system_error& e)
157 {
158 return emptyString;
159 }
Brandon Wyman8af8a202017-05-31 18:26:30 -0500160 }
161 }
162
Brad Bishop431d26a2017-08-25 09:47:58 -0400163 return emptyString;
Brandon Wyman8af8a202017-05-31 18:26:30 -0500164}
165
166std::string findHwmon(const std::string& ofNode)
167{
168 static constexpr auto hwmonRoot = "/sys/class/hwmon";
169
170 fs::path fullOfPath{ofRoot};
171 fullOfPath /= ofNode;
172
173 for (const auto& hwmonInst : fs::directory_iterator(hwmonRoot))
174 {
175 auto path = hwmonInst.path();
176 path /= "of_node";
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400177
178 try
Brandon Wyman8af8a202017-05-31 18:26:30 -0500179 {
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400180 path = fs::canonical(path);
181 }
182 catch (const std::system_error& e)
183 {
184 // realpath may encounter ENOENT (Hwmon
185 // instances have a nasty habit of
186 // going away without warning).
187 continue;
Brad Bishop613a5b32017-01-05 20:58:13 -0500188 }
189
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400190 if (path == fullOfPath)
191 {
192 return hwmonInst.path();
193 }
194
195 // Try to find HWMON instance via phandle values.
196 // Used for IIO device drivers.
197 auto matchpath = findPhandleMatch(path, fullOfPath);
198 if (!matchpath.empty())
199 {
200 return hwmonInst.path();
201 }
Brad Bishop613a5b32017-01-05 20:58:13 -0500202 }
203
Brad Bishop4e24ebd2017-08-28 16:19:16 -0400204 return emptyString;
Brad Bishop613a5b32017-01-05 20:58:13 -0500205}
206
Brad Bishop4db64422017-02-16 11:33:32 -0500207int readSysfsWithCallout(const std::string& root,
208 const std::string& instance,
209 const std::string& type,
210 const std::string& id,
Matt Spinler3b8e36e2017-07-28 10:44:45 -0500211 const std::string& sensor,
212 bool throwDeviceBusy)
Brad Bishop4db64422017-02-16 11:33:32 -0500213{
Brad Bishop5ec68ab2017-03-27 13:41:02 -0400214 namespace fs = std::experimental::filesystem;
215
Brad Bishop4db64422017-02-16 11:33:32 -0500216 int value = 0;
Brad Bishop5ec68ab2017-03-27 13:41:02 -0400217 std::ifstream ifs;
218 fs::path instancePath{root};
219 instancePath /= instance;
Brad Bishop4db64422017-02-16 11:33:32 -0500220 std::string fullPath = make_sysfs_path(instancePath,
221 type, id, sensor);
Brad Bishop4db64422017-02-16 11:33:32 -0500222
223 ifs.exceptions(std::ifstream::failbit
224 | std::ifstream::badbit
225 | std::ifstream::eofbit);
226 try
227 {
228 ifs.open(fullPath);
229 ifs >> value;
230 }
231 catch (const std::exception& e)
232 {
233 // Too many GCC bugs (53984, 66145) to do
234 // this the right way...
Brad Bishop4db64422017-02-16 11:33:32 -0500235
236 // errno should still reflect the error from the failing open
237 // or read system calls that got us here.
238 auto rc = errno;
Andrew Geisslerac8b7c62017-06-13 16:15:30 -0500239
Matt Spinler3b8e36e2017-07-28 10:44:45 -0500240 if ((rc == EAGAIN) && throwDeviceBusy)
241 {
242 throw DeviceBusyException(fullPath);
243 }
244
Andrew Geisslerac8b7c62017-06-13 16:15:30 -0500245 // If the directory disappeared then this application should gracefully
246 // exit. There are race conditions between the unloading of a hwmon
247 // driver and the stopping of this service by systemd. To prevent
248 // this application from falsely failing in these scenarios, it will
249 // simply exit if the directory or file can not be found. It is up
250 // to the user(s) of this provided hwmon object to log the appropriate
251 // errors if the object disappears when it should not.
252 if (rc == ENOENT)
253 {
254 exit(0);
255 }
Brad Bishop5ec68ab2017-03-27 13:41:02 -0400256 instancePath /= "device";
Brad Bishop431d26a2017-08-25 09:47:58 -0400257 auto callOutPath = findCalloutPath(instancePath);
Matthew Barth4e1f30f2017-03-21 16:13:27 -0500258 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::Error;
Patrick Venture1e6324f2017-06-01 14:07:05 -0700259
260 // this throws a ReadFailure.
261 elog<ReadFailure>(
Marri Devender Rao05711eb2017-04-15 06:47:11 -0500262 xyz::openbmc_project::Sensor::Device::
263 ReadFailure::CALLOUT_ERRNO(rc),
264 xyz::openbmc_project::Sensor::Device::
Brad Bishop431d26a2017-08-25 09:47:58 -0400265 ReadFailure::CALLOUT_DEVICE_PATH(callOutPath.c_str()));
Brad Bishop4db64422017-02-16 11:33:32 -0500266 }
267
268 return value;
269}
270
Matthew Barth048ac872017-03-09 14:36:08 -0600271uint64_t writeSysfsWithCallout(const uint64_t& value,
272 const std::string& root,
273 const std::string& instance,
274 const std::string& type,
275 const std::string& id,
276 const std::string& sensor)
277{
278 namespace fs = std::experimental::filesystem;
279
280 std::string valueStr = std::to_string(value);
281 std::ofstream ofs;
282 fs::path instancePath{root};
283 instancePath /= instance;
284 std::string fullPath = make_sysfs_path(instancePath,
285 type, id, sensor);
286
287 ofs.exceptions(std::ofstream::failbit
288 | std::ofstream::badbit
289 | std::ofstream::eofbit);
290 try
291 {
292 ofs.open(fullPath);
293 ofs << valueStr;
294 }
295 catch (const std::exception& e)
296 {
297 // errno should still reflect the error from the failing open
298 // or write system calls that got us here.
299 auto rc = errno;
300 instancePath /= "device";
Brad Bishop431d26a2017-08-25 09:47:58 -0400301 auto callOutPath = findCalloutPath(instancePath);
Matthew Barth048ac872017-03-09 14:36:08 -0600302 using namespace sdbusplus::xyz::openbmc_project::Control::Device::Error;
Marri Devender Rao05711eb2017-04-15 06:47:11 -0500303 report<WriteFailure>(
304 xyz::openbmc_project::Control::Device::
305 WriteFailure::CALLOUT_ERRNO(rc),
306 xyz::openbmc_project::Control::Device::
Brad Bishop431d26a2017-08-25 09:47:58 -0400307 WriteFailure::CALLOUT_DEVICE_PATH(callOutPath.c_str()));
Marri Devender Rao05711eb2017-04-15 06:47:11 -0500308
Matthew Barth048ac872017-03-09 14:36:08 -0600309 exit(EXIT_FAILURE);
310 }
311
312 return value;
313}
314
Brad Bishop8b574a72017-08-25 16:17:19 -0400315namespace hwmonio
316{
317
318HwmonIO::HwmonIO(const std::string& path) : p(path)
319{
320
321}
322
323uint32_t HwmonIO::read(
324 const std::string& type,
325 const std::string& id,
326 const std::string& sensor) const
327{
328 uint32_t val;
329 std::ifstream ifs;
330 auto fullPath = sysfs::make_sysfs_path(
331 p, type, id, sensor);
332
333 ifs.exceptions(
334 std::ifstream::failbit |
335 std::ifstream::badbit |
336 std::ifstream::eofbit);
337 try
338 {
339 ifs.open(fullPath);
340 ifs >> val;
341 }
342 catch (const std::exception& e)
343 {
344 auto rc = errno;
345
346 if (rc == ENOENT)
347 {
348 // If the directory disappeared then this application should
349 // gracefully exit. There are race conditions between the
350 // unloading of a hwmon driver and the stopping of this service
351 // by systemd. To prevent this application from falsely failing
352 // in these scenarios, it will simply exit if the directory or
353 // file can not be found. It is up to the user(s) of this
354 // provided hwmon object to log the appropriate errors if the
355 // object disappears when it should not.
356 exit(0);
357 }
358
359 if (rc)
360 {
361 // Work around GCC bugs 53984 and 66145 for callers by
362 // explicitly raising system_error here.
363 throw std::system_error(rc, std::generic_category());
364 }
365
366 throw;
367 }
368 return val;
369}
370
371void HwmonIO::write(
372 uint32_t val,
373 const std::string& type,
374 const std::string& id,
375 const std::string& sensor) const
376{
377 std::ofstream ofs;
378 auto fullPath = sysfs::make_sysfs_path(
379 p, type, id, sensor);
380
381 ofs.exceptions(
382 std::ofstream::failbit |
383 std::ofstream::badbit |
384 std::ofstream::eofbit);
385
386 // See comments in the read method for an explanation of the odd exception
387 // handling behavior here.
388
389 try
390 {
391 ofs.open(fullPath);
392 ofs << val;
393 }
394 catch (const std::exception& e)
395 {
396 auto rc = errno;
397
398 if (rc == ENOENT)
399 {
400 exit(0);
401 }
402
403 if (rc)
404 {
405 throw std::system_error(rc, std::generic_category());
406 }
407
408 throw;
409 }
410}
411
412std::string HwmonIO::path() const
413{
414 return p;
415}
416
417} // namespace hwmonio
Patrick Venture1e6324f2017-06-01 14:07:05 -0700418}
Brad Bishop613a5b32017-01-05 20:58:13 -0500419// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4