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