129e9e385SMatthew Barth /** 229e9e385SMatthew Barth * Copyright © 2020 IBM Corporation 329e9e385SMatthew Barth * 429e9e385SMatthew Barth * Licensed under the Apache License, Version 2.0 (the "License"); 529e9e385SMatthew Barth * you may not use this file except in compliance with the License. 629e9e385SMatthew Barth * You may obtain a copy of the License at 729e9e385SMatthew Barth * 829e9e385SMatthew Barth * http://www.apache.org/licenses/LICENSE-2.0 929e9e385SMatthew Barth * 1029e9e385SMatthew Barth * Unless required by applicable law or agreed to in writing, software 1129e9e385SMatthew Barth * distributed under the License is distributed on an "AS IS" BASIS, 1229e9e385SMatthew Barth * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1329e9e385SMatthew Barth * See the License for the specific language governing permissions and 1429e9e385SMatthew Barth * limitations under the License. 1529e9e385SMatthew Barth */ 1629e9e385SMatthew Barth 1729e9e385SMatthew Barth #include "manager.hpp" 1829e9e385SMatthew Barth 19e0c6a2d9SShawn McCarney #include "chassis.hpp" 20e0c6a2d9SShawn McCarney #include "config_file_parser.hpp" 21e0c6a2d9SShawn McCarney #include "exception_utils.hpp" 22e0c6a2d9SShawn McCarney #include "rule.hpp" 23bbc7c583SMatthew Barth #include "utility.hpp" 24bbc7c583SMatthew Barth 25*589c181aSShawn McCarney #include <algorithm> 26f2bcf1f9SMatthew Barth #include <chrono> 27e0c6a2d9SShawn McCarney #include <exception> 28*589c181aSShawn McCarney #include <functional> 29*589c181aSShawn McCarney #include <map> 30e0c6a2d9SShawn McCarney #include <tuple> 31e0c6a2d9SShawn McCarney #include <utility> 32250d0a98SMatthew Barth #include <variant> 33f2bcf1f9SMatthew Barth 3484807b96SShawn McCarney namespace phosphor::power::regulators 3529e9e385SMatthew Barth { 3629e9e385SMatthew Barth 37e0c6a2d9SShawn McCarney namespace fs = std::filesystem; 38e0c6a2d9SShawn McCarney 39*589c181aSShawn McCarney constexpr auto busName = "xyz.openbmc_project.Power.Regulators"; 40*589c181aSShawn McCarney constexpr auto managerObjPath = "/xyz/openbmc_project/power/regulators/manager"; 41*589c181aSShawn McCarney constexpr auto compatibleIntf = 42*589c181aSShawn McCarney "xyz.openbmc_project.Configuration.IBMCompatibleSystem"; 43*589c181aSShawn McCarney constexpr auto compatibleNamesProp = "Names"; 44*589c181aSShawn McCarney 45*589c181aSShawn McCarney /** 46*589c181aSShawn McCarney * Default configuration file name. This is used when the system does not 47*589c181aSShawn McCarney * implement the D-Bus compatible interface. 48*589c181aSShawn McCarney */ 49*589c181aSShawn McCarney constexpr auto defaultConfigFileName = "config.json"; 50*589c181aSShawn McCarney 51e0c6a2d9SShawn McCarney /** 52e0c6a2d9SShawn McCarney * Standard configuration file directory. This directory is part of the 53e0c6a2d9SShawn McCarney * firmware install image. It contains the standard version of the config file. 54e0c6a2d9SShawn McCarney */ 55e0c6a2d9SShawn McCarney const fs::path standardConfigFileDir{"/usr/share/phosphor-regulators"}; 56e0c6a2d9SShawn McCarney 57e0c6a2d9SShawn McCarney /** 58e0c6a2d9SShawn McCarney * Test configuration file directory. This directory can contain a test version 59e0c6a2d9SShawn McCarney * of the config file. The test version will override the standard version. 60e0c6a2d9SShawn McCarney */ 61e0c6a2d9SShawn McCarney const fs::path testConfigFileDir{"/etc/phosphor-regulators"}; 62e0c6a2d9SShawn McCarney 63f2bcf1f9SMatthew Barth Manager::Manager(sdbusplus::bus::bus& bus, const sdeventplus::Event& event) : 64*589c181aSShawn McCarney ManagerObject{bus, managerObjPath, true}, bus{bus}, eventLoop{event}, 65*589c181aSShawn McCarney services{bus} 6629e9e385SMatthew Barth { 67*589c181aSShawn McCarney // Subscribe to D-Bus interfacesAdded signal from Entity Manager. This 68*589c181aSShawn McCarney // notifies us if the compatible interface becomes available later. 69*589c181aSShawn McCarney std::string matchStr = sdbusplus::bus::match::rules::interfacesAdded() + 70*589c181aSShawn McCarney sdbusplus::bus::match::rules::sender( 71*589c181aSShawn McCarney "xyz.openbmc_project.EntityManager"); 72250d0a98SMatthew Barth std::unique_ptr<sdbusplus::server::match::match> matchPtr = 73250d0a98SMatthew Barth std::make_unique<sdbusplus::server::match::match>( 74*589c181aSShawn McCarney bus, matchStr, 75*589c181aSShawn McCarney std::bind(&Manager::interfacesAddedHandler, this, 76250d0a98SMatthew Barth std::placeholders::_1)); 77250d0a98SMatthew Barth signals.emplace_back(std::move(matchPtr)); 78250d0a98SMatthew Barth 79*589c181aSShawn McCarney // Try to find compatible system types using D-Bus compatible interface. 80*589c181aSShawn McCarney // Note that it might not be supported on this system, or the service that 81*589c181aSShawn McCarney // provides the interface might not be running yet. 82*589c181aSShawn McCarney findCompatibleSystemTypes(); 8384807b96SShawn McCarney 84*589c181aSShawn McCarney // Try to find and load the JSON configuration file 85e0c6a2d9SShawn McCarney loadConfigFile(); 8629e9e385SMatthew Barth 8729e9e385SMatthew Barth // Obtain dbus service name 8829e9e385SMatthew Barth bus.request_name(busName); 8929e9e385SMatthew Barth } 9029e9e385SMatthew Barth 9129e9e385SMatthew Barth void Manager::configure() 9229e9e385SMatthew Barth { 936345c6c5SShawn McCarney // Verify System object exists; this means config file has been loaded 946345c6c5SShawn McCarney if (system) 956345c6c5SShawn McCarney { 966345c6c5SShawn McCarney // Configure the regulator devices in the system 9723243f84SBob King system->configure(services); 986345c6c5SShawn McCarney } 996345c6c5SShawn McCarney else 1006345c6c5SShawn McCarney { 101b464c8bdSShawn McCarney services.getJournal().logError("Unable to configure regulator devices: " 102b464c8bdSShawn McCarney "Configuration file not loaded"); 1036345c6c5SShawn McCarney // TODO: Log error 1046345c6c5SShawn McCarney } 1056345c6c5SShawn McCarney 10629e9e385SMatthew Barth // TODO Configuration errors that should halt poweron, 10729e9e385SMatthew Barth // throw InternalFailure exception (or similar) to 10829e9e385SMatthew Barth // fail the call(busctl) to this method 10929e9e385SMatthew Barth } 11029e9e385SMatthew Barth 111*589c181aSShawn McCarney void Manager::interfacesAddedHandler(sdbusplus::message::message& msg) 112*589c181aSShawn McCarney { 113*589c181aSShawn McCarney // Verify message is valid 114*589c181aSShawn McCarney if (!msg) 115*589c181aSShawn McCarney { 116*589c181aSShawn McCarney return; 117*589c181aSShawn McCarney } 118*589c181aSShawn McCarney 119*589c181aSShawn McCarney try 120*589c181aSShawn McCarney { 121*589c181aSShawn McCarney // Read object path for object that was created or had interface added 122*589c181aSShawn McCarney sdbusplus::message::object_path objPath; 123*589c181aSShawn McCarney msg.read(objPath); 124*589c181aSShawn McCarney 125*589c181aSShawn McCarney // Read the dictionary whose keys are interface names and whose values 126*589c181aSShawn McCarney // are dictionaries containing the interface property names and values 127*589c181aSShawn McCarney std::map<std::string, 128*589c181aSShawn McCarney std::map<std::string, std::variant<std::vector<std::string>>>> 129*589c181aSShawn McCarney intfProp; 130*589c181aSShawn McCarney msg.read(intfProp); 131*589c181aSShawn McCarney 132*589c181aSShawn McCarney // Find the compatible interface, if present 133*589c181aSShawn McCarney auto itIntf = intfProp.find(compatibleIntf); 134*589c181aSShawn McCarney if (itIntf != intfProp.cend()) 135*589c181aSShawn McCarney { 136*589c181aSShawn McCarney // Find the Names property of the compatible interface, if present 137*589c181aSShawn McCarney auto itProp = itIntf->second.find(compatibleNamesProp); 138*589c181aSShawn McCarney if (itProp != itIntf->second.cend()) 139*589c181aSShawn McCarney { 140*589c181aSShawn McCarney // Get value of Names property 141*589c181aSShawn McCarney auto propValue = std::get<0>(itProp->second); 142*589c181aSShawn McCarney if (!propValue.empty()) 143*589c181aSShawn McCarney { 144*589c181aSShawn McCarney // Store list of compatible system types 145*589c181aSShawn McCarney compatibleSystemTypes = propValue; 146*589c181aSShawn McCarney 147*589c181aSShawn McCarney // Find and load JSON config file based on system types 148*589c181aSShawn McCarney loadConfigFile(); 149*589c181aSShawn McCarney } 150*589c181aSShawn McCarney } 151*589c181aSShawn McCarney } 152*589c181aSShawn McCarney } 153*589c181aSShawn McCarney catch (const std::exception&) 154*589c181aSShawn McCarney { 155*589c181aSShawn McCarney // Error trying to read interfacesAdded message. One possible cause 156*589c181aSShawn McCarney // could be a property whose value is not a std::vector<std::string>. 157*589c181aSShawn McCarney } 158*589c181aSShawn McCarney } 159*589c181aSShawn McCarney 1605b19ea51SShawn McCarney void Manager::monitor(bool enable) 16129e9e385SMatthew Barth { 16229e9e385SMatthew Barth if (enable) 16329e9e385SMatthew Barth { 1645b19ea51SShawn McCarney /* Temporarily comment out until monitoring is supported. 165f2bcf1f9SMatthew Barth Timer timer(eventLoop, std::bind(&Manager::timerExpired, this)); 166f2bcf1f9SMatthew Barth // Set timer as a repeating 1sec timer 167f2bcf1f9SMatthew Barth timer.restart(std::chrono::milliseconds(1000)); 168f2bcf1f9SMatthew Barth timers.emplace_back(std::move(timer)); 1695b19ea51SShawn McCarney */ 17029e9e385SMatthew Barth } 17129e9e385SMatthew Barth else 17229e9e385SMatthew Barth { 1735b19ea51SShawn McCarney /* Temporarily comment out until monitoring is supported. 174f2bcf1f9SMatthew Barth // Delete all timers to disable monitoring 175f2bcf1f9SMatthew Barth timers.clear(); 1766345c6c5SShawn McCarney */ 1775b19ea51SShawn McCarney 1785b19ea51SShawn McCarney // Verify System object exists; this means config file has been loaded 1795b19ea51SShawn McCarney if (system) 1805b19ea51SShawn McCarney { 1815b19ea51SShawn McCarney // Close the regulator devices in the system. Monitoring is 1825b19ea51SShawn McCarney // normally disabled because the system is being powered off. The 1835b19ea51SShawn McCarney // devices should be closed in case hardware is removed or replaced 1845b19ea51SShawn McCarney // while the system is at standby. 185d692d6dfSBob King system->closeDevices(services); 1865b19ea51SShawn McCarney } 1875b19ea51SShawn McCarney } 18829e9e385SMatthew Barth } 18929e9e385SMatthew Barth 190*589c181aSShawn McCarney void Manager::sighupHandler(sdeventplus::source::Signal& /*sigSrc*/, 191*589c181aSShawn McCarney const struct signalfd_siginfo* /*sigInfo*/) 192*589c181aSShawn McCarney { 193*589c181aSShawn McCarney // Reload the JSON configuration file 194*589c181aSShawn McCarney loadConfigFile(); 195*589c181aSShawn McCarney } 196*589c181aSShawn McCarney 197f2bcf1f9SMatthew Barth void Manager::timerExpired() 198f2bcf1f9SMatthew Barth { 199f2bcf1f9SMatthew Barth // TODO Analyze, refresh sensor status, and 200f2bcf1f9SMatthew Barth // collect/update telemetry for each regulator 201f2bcf1f9SMatthew Barth } 202f2bcf1f9SMatthew Barth 203*589c181aSShawn McCarney void Manager::findCompatibleSystemTypes() 2047cbc5536SMatthew Barth { 205bbc7c583SMatthew Barth using namespace phosphor::power::util; 206bbc7c583SMatthew Barth 207bbc7c583SMatthew Barth try 208bbc7c583SMatthew Barth { 209*589c181aSShawn McCarney // Query object mapper for object paths that implement the compatible 210*589c181aSShawn McCarney // interface. Returns a map of object paths to a map of services names 211*589c181aSShawn McCarney // to their interfaces. 212*589c181aSShawn McCarney DbusSubtree subTree = getSubTree(bus, "/xyz/openbmc_project/inventory", 213*589c181aSShawn McCarney compatibleIntf, 0); 214*589c181aSShawn McCarney 215*589c181aSShawn McCarney // Get the first object path 216*589c181aSShawn McCarney auto objectIt = subTree.cbegin(); 217*589c181aSShawn McCarney if (objectIt != subTree.cend()) 218*589c181aSShawn McCarney { 219*589c181aSShawn McCarney std::string objPath = objectIt->first; 220*589c181aSShawn McCarney 221*589c181aSShawn McCarney // Get the first service name 222*589c181aSShawn McCarney auto serviceIt = objectIt->second.cbegin(); 223*589c181aSShawn McCarney if (serviceIt != objectIt->second.cend()) 224*589c181aSShawn McCarney { 225*589c181aSShawn McCarney std::string service = serviceIt->first; 226bbc7c583SMatthew Barth if (!service.empty()) 227bbc7c583SMatthew Barth { 228*589c181aSShawn McCarney // Get compatible system types property value 229*589c181aSShawn McCarney getProperty(compatibleIntf, compatibleNamesProp, objPath, 230*589c181aSShawn McCarney service, bus, compatibleSystemTypes); 231bbc7c583SMatthew Barth } 232bbc7c583SMatthew Barth } 233*589c181aSShawn McCarney } 234*589c181aSShawn McCarney } 235*589c181aSShawn McCarney catch (const std::exception&) 236bbc7c583SMatthew Barth { 237*589c181aSShawn McCarney // Compatible system types information is not available. The current 238*589c181aSShawn McCarney // system might not support the interface, or the service that 239*589c181aSShawn McCarney // implements the interface might not be running yet. 240bbc7c583SMatthew Barth } 241bbc7c583SMatthew Barth } 242bbc7c583SMatthew Barth 243e0c6a2d9SShawn McCarney fs::path Manager::findConfigFile() 244e0c6a2d9SShawn McCarney { 245*589c181aSShawn McCarney // Build list of possible base file names 246*589c181aSShawn McCarney std::vector<std::string> fileNames{}; 247*589c181aSShawn McCarney 248*589c181aSShawn McCarney // Add possible file names based on compatible system types (if any) 249*589c181aSShawn McCarney for (const std::string& systemType : compatibleSystemTypes) 250*589c181aSShawn McCarney { 251*589c181aSShawn McCarney // Replace all spaces and commas in system type name with underscores 252*589c181aSShawn McCarney std::string fileName{systemType}; 253*589c181aSShawn McCarney std::replace(fileName.begin(), fileName.end(), ' ', '_'); 254*589c181aSShawn McCarney std::replace(fileName.begin(), fileName.end(), ',', '_'); 255*589c181aSShawn McCarney 256*589c181aSShawn McCarney // Append .json suffix and add to list 257*589c181aSShawn McCarney fileName.append(".json"); 258*589c181aSShawn McCarney fileNames.emplace_back(fileName); 259*589c181aSShawn McCarney } 260*589c181aSShawn McCarney 261*589c181aSShawn McCarney // Add default file name for systems that don't use compatible interface 262*589c181aSShawn McCarney fileNames.emplace_back(defaultConfigFileName); 263*589c181aSShawn McCarney 264*589c181aSShawn McCarney // Look for a config file with one of the possible base names 265*589c181aSShawn McCarney for (const std::string& fileName : fileNames) 266*589c181aSShawn McCarney { 267*589c181aSShawn McCarney // Check if file exists in test directory 268e0c6a2d9SShawn McCarney fs::path pathName{testConfigFileDir / fileName}; 269*589c181aSShawn McCarney if (fs::exists(pathName)) 270e0c6a2d9SShawn McCarney { 271*589c181aSShawn McCarney return pathName; 272*589c181aSShawn McCarney } 273*589c181aSShawn McCarney 274*589c181aSShawn McCarney // Check if file exists in standard directory 275e0c6a2d9SShawn McCarney pathName = standardConfigFileDir / fileName; 276*589c181aSShawn McCarney if (fs::exists(pathName)) 277e0c6a2d9SShawn McCarney { 278*589c181aSShawn McCarney return pathName; 279e0c6a2d9SShawn McCarney } 280e0c6a2d9SShawn McCarney } 281e0c6a2d9SShawn McCarney 282*589c181aSShawn McCarney // No config file found; return empty path 283*589c181aSShawn McCarney return fs::path{}; 284e0c6a2d9SShawn McCarney } 285e0c6a2d9SShawn McCarney 286e0c6a2d9SShawn McCarney void Manager::loadConfigFile() 287e0c6a2d9SShawn McCarney { 288e0c6a2d9SShawn McCarney try 289e0c6a2d9SShawn McCarney { 290e0c6a2d9SShawn McCarney // Find the absolute path to the config file 291e0c6a2d9SShawn McCarney fs::path pathName = findConfigFile(); 292*589c181aSShawn McCarney if (!pathName.empty()) 293*589c181aSShawn McCarney { 294e0c6a2d9SShawn McCarney // Log info message in journal; config file path is important 295b464c8bdSShawn McCarney services.getJournal().logInfo("Loading configuration file " + 296b464c8bdSShawn McCarney pathName.string()); 297e0c6a2d9SShawn McCarney 298e0c6a2d9SShawn McCarney // Parse the config file 299e0c6a2d9SShawn McCarney std::vector<std::unique_ptr<Rule>> rules{}; 300e0c6a2d9SShawn McCarney std::vector<std::unique_ptr<Chassis>> chassis{}; 301e0c6a2d9SShawn McCarney std::tie(rules, chassis) = config_file_parser::parse(pathName); 302e0c6a2d9SShawn McCarney 303*589c181aSShawn McCarney // Store config file information in a new System object. The old 304*589c181aSShawn McCarney // System object, if any, is automatically deleted. 305*589c181aSShawn McCarney system = 306*589c181aSShawn McCarney std::make_unique<System>(std::move(rules), std::move(chassis)); 307*589c181aSShawn McCarney } 308e0c6a2d9SShawn McCarney } 309e0c6a2d9SShawn McCarney catch (const std::exception& e) 310e0c6a2d9SShawn McCarney { 311e0c6a2d9SShawn McCarney // Log error messages in journal 312b464c8bdSShawn McCarney services.getJournal().logError(exception_utils::getMessages(e)); 313b464c8bdSShawn McCarney services.getJournal().logError("Unable to load configuration file"); 314e0c6a2d9SShawn McCarney 315e0c6a2d9SShawn McCarney // TODO: Create error log entry 316e0c6a2d9SShawn McCarney } 317e0c6a2d9SShawn McCarney } 318e0c6a2d9SShawn McCarney 31984807b96SShawn McCarney } // namespace phosphor::power::regulators 320