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 25415094c1SShawn McCarney #include <xyz/openbmc_project/Common/error.hpp> 26415094c1SShawn McCarney 27589c181aSShawn McCarney #include <algorithm> 28f2bcf1f9SMatthew Barth #include <chrono> 29e0c6a2d9SShawn McCarney #include <exception> 30589c181aSShawn McCarney #include <functional> 31589c181aSShawn McCarney #include <map> 328acaf547SShawn McCarney #include <thread> 33e0c6a2d9SShawn McCarney #include <tuple> 34e0c6a2d9SShawn McCarney #include <utility> 35250d0a98SMatthew Barth #include <variant> 36f2bcf1f9SMatthew Barth 3784807b96SShawn McCarney namespace phosphor::power::regulators 3829e9e385SMatthew Barth { 3929e9e385SMatthew Barth 40e0c6a2d9SShawn McCarney namespace fs = std::filesystem; 41e0c6a2d9SShawn McCarney 42589c181aSShawn McCarney constexpr auto busName = "xyz.openbmc_project.Power.Regulators"; 43589c181aSShawn McCarney constexpr auto managerObjPath = "/xyz/openbmc_project/power/regulators/manager"; 44589c181aSShawn McCarney constexpr auto compatibleIntf = 45589c181aSShawn McCarney "xyz.openbmc_project.Configuration.IBMCompatibleSystem"; 46589c181aSShawn McCarney constexpr auto compatibleNamesProp = "Names"; 478acaf547SShawn McCarney constexpr std::chrono::minutes maxTimeToWaitForCompatTypes{5}; 48589c181aSShawn McCarney 49589c181aSShawn McCarney /** 50589c181aSShawn McCarney * Default configuration file name. This is used when the system does not 51589c181aSShawn McCarney * implement the D-Bus compatible interface. 52589c181aSShawn McCarney */ 53589c181aSShawn McCarney constexpr auto defaultConfigFileName = "config.json"; 54589c181aSShawn McCarney 55e0c6a2d9SShawn McCarney /** 56e0c6a2d9SShawn McCarney * Standard configuration file directory. This directory is part of the 57e0c6a2d9SShawn McCarney * firmware install image. It contains the standard version of the config file. 58e0c6a2d9SShawn McCarney */ 59e0c6a2d9SShawn McCarney const fs::path standardConfigFileDir{"/usr/share/phosphor-regulators"}; 60e0c6a2d9SShawn McCarney 61e0c6a2d9SShawn McCarney /** 62e0c6a2d9SShawn McCarney * Test configuration file directory. This directory can contain a test version 63e0c6a2d9SShawn McCarney * of the config file. The test version will override the standard version. 64e0c6a2d9SShawn McCarney */ 65e0c6a2d9SShawn McCarney const fs::path testConfigFileDir{"/etc/phosphor-regulators"}; 66e0c6a2d9SShawn McCarney 67f2bcf1f9SMatthew Barth Manager::Manager(sdbusplus::bus::bus& bus, const sdeventplus::Event& event) : 68589c181aSShawn McCarney ManagerObject{bus, managerObjPath, true}, bus{bus}, eventLoop{event}, 69589c181aSShawn McCarney services{bus} 7029e9e385SMatthew Barth { 71589c181aSShawn McCarney // Subscribe to D-Bus interfacesAdded signal from Entity Manager. This 72589c181aSShawn McCarney // notifies us if the compatible interface becomes available later. 73589c181aSShawn McCarney std::string matchStr = sdbusplus::bus::match::rules::interfacesAdded() + 74589c181aSShawn McCarney sdbusplus::bus::match::rules::sender( 75589c181aSShawn McCarney "xyz.openbmc_project.EntityManager"); 76250d0a98SMatthew Barth std::unique_ptr<sdbusplus::server::match::match> matchPtr = 77250d0a98SMatthew Barth std::make_unique<sdbusplus::server::match::match>( 78589c181aSShawn McCarney bus, matchStr, 79589c181aSShawn McCarney std::bind(&Manager::interfacesAddedHandler, this, 80250d0a98SMatthew Barth std::placeholders::_1)); 81250d0a98SMatthew Barth signals.emplace_back(std::move(matchPtr)); 82250d0a98SMatthew Barth 83589c181aSShawn McCarney // Try to find compatible system types using D-Bus compatible interface. 84589c181aSShawn McCarney // Note that it might not be supported on this system, or the service that 85589c181aSShawn McCarney // provides the interface might not be running yet. 86589c181aSShawn McCarney findCompatibleSystemTypes(); 8784807b96SShawn McCarney 88589c181aSShawn McCarney // Try to find and load the JSON configuration file 89e0c6a2d9SShawn McCarney loadConfigFile(); 9029e9e385SMatthew Barth 9129e9e385SMatthew Barth // Obtain dbus service name 9229e9e385SMatthew Barth bus.request_name(busName); 9329e9e385SMatthew Barth } 9429e9e385SMatthew Barth 9529e9e385SMatthew Barth void Manager::configure() 9629e9e385SMatthew Barth { 979bd94d36SShawn McCarney // Clear any cached data or error history related to hardware devices 989bd94d36SShawn McCarney clearHardwareData(); 999bd94d36SShawn McCarney 1008acaf547SShawn McCarney // Wait until the config file has been loaded or hit max wait time 1018acaf547SShawn McCarney waitUntilConfigFileLoaded(); 1028acaf547SShawn McCarney 1038acaf547SShawn McCarney // Verify config file has been loaded and System object is valid 1048acaf547SShawn McCarney if (isConfigFileLoaded()) 1056345c6c5SShawn McCarney { 1066345c6c5SShawn McCarney // Configure the regulator devices in the system 10723243f84SBob King system->configure(services); 1086345c6c5SShawn McCarney } 1096345c6c5SShawn McCarney else 1106345c6c5SShawn McCarney { 111415094c1SShawn McCarney // Write error message to journal 112b464c8bdSShawn McCarney services.getJournal().logError("Unable to configure regulator devices: " 113b464c8bdSShawn McCarney "Configuration file not loaded"); 1146345c6c5SShawn McCarney 115415094c1SShawn McCarney // Log critical error since regulators could not be configured. Could 116415094c1SShawn McCarney // cause hardware damage if default regulator settings are very wrong. 117415094c1SShawn McCarney services.getErrorLogging().logConfigFileError(Entry::Level::Critical, 118415094c1SShawn McCarney services.getJournal()); 119415094c1SShawn McCarney 120415094c1SShawn McCarney // Throw InternalFailure to propogate error status to D-Bus client 121415094c1SShawn McCarney throw sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure{}; 122415094c1SShawn McCarney } 12329e9e385SMatthew Barth } 12429e9e385SMatthew Barth 125589c181aSShawn McCarney void Manager::interfacesAddedHandler(sdbusplus::message::message& msg) 126589c181aSShawn McCarney { 127589c181aSShawn McCarney // Verify message is valid 128589c181aSShawn McCarney if (!msg) 129589c181aSShawn McCarney { 130589c181aSShawn McCarney return; 131589c181aSShawn McCarney } 132589c181aSShawn McCarney 133589c181aSShawn McCarney try 134589c181aSShawn McCarney { 135589c181aSShawn McCarney // Read object path for object that was created or had interface added 136589c181aSShawn McCarney sdbusplus::message::object_path objPath; 137589c181aSShawn McCarney msg.read(objPath); 138589c181aSShawn McCarney 139589c181aSShawn McCarney // Read the dictionary whose keys are interface names and whose values 140589c181aSShawn McCarney // are dictionaries containing the interface property names and values 141589c181aSShawn McCarney std::map<std::string, 142589c181aSShawn McCarney std::map<std::string, std::variant<std::vector<std::string>>>> 143589c181aSShawn McCarney intfProp; 144589c181aSShawn McCarney msg.read(intfProp); 145589c181aSShawn McCarney 146589c181aSShawn McCarney // Find the compatible interface, if present 147589c181aSShawn McCarney auto itIntf = intfProp.find(compatibleIntf); 148589c181aSShawn McCarney if (itIntf != intfProp.cend()) 149589c181aSShawn McCarney { 150589c181aSShawn McCarney // Find the Names property of the compatible interface, if present 151589c181aSShawn McCarney auto itProp = itIntf->second.find(compatibleNamesProp); 152589c181aSShawn McCarney if (itProp != itIntf->second.cend()) 153589c181aSShawn McCarney { 154589c181aSShawn McCarney // Get value of Names property 155589c181aSShawn McCarney auto propValue = std::get<0>(itProp->second); 156589c181aSShawn McCarney if (!propValue.empty()) 157589c181aSShawn McCarney { 158589c181aSShawn McCarney // Store list of compatible system types 159589c181aSShawn McCarney compatibleSystemTypes = propValue; 160589c181aSShawn McCarney 161589c181aSShawn McCarney // Find and load JSON config file based on system types 162589c181aSShawn McCarney loadConfigFile(); 163589c181aSShawn McCarney } 164589c181aSShawn McCarney } 165589c181aSShawn McCarney } 166589c181aSShawn McCarney } 167589c181aSShawn McCarney catch (const std::exception&) 168589c181aSShawn McCarney { 169589c181aSShawn McCarney // Error trying to read interfacesAdded message. One possible cause 170589c181aSShawn McCarney // could be a property whose value is not a std::vector<std::string>. 171589c181aSShawn McCarney } 172589c181aSShawn McCarney } 173589c181aSShawn McCarney 1745b19ea51SShawn McCarney void Manager::monitor(bool enable) 17529e9e385SMatthew Barth { 17629e9e385SMatthew Barth if (enable) 17729e9e385SMatthew Barth { 1785b19ea51SShawn McCarney /* Temporarily comment out until monitoring is supported. 179f2bcf1f9SMatthew Barth Timer timer(eventLoop, std::bind(&Manager::timerExpired, this)); 180f2bcf1f9SMatthew Barth // Set timer as a repeating 1sec timer 181f2bcf1f9SMatthew Barth timer.restart(std::chrono::milliseconds(1000)); 182f2bcf1f9SMatthew Barth timers.emplace_back(std::move(timer)); 1835b19ea51SShawn McCarney */ 18429e9e385SMatthew Barth } 18529e9e385SMatthew Barth else 18629e9e385SMatthew Barth { 1875b19ea51SShawn McCarney /* Temporarily comment out until monitoring is supported. 188f2bcf1f9SMatthew Barth // Delete all timers to disable monitoring 189f2bcf1f9SMatthew Barth timers.clear(); 1906345c6c5SShawn McCarney */ 1915b19ea51SShawn McCarney 1928acaf547SShawn McCarney // Verify config file has been loaded and System object is valid 1938acaf547SShawn McCarney if (isConfigFileLoaded()) 1945b19ea51SShawn McCarney { 1955b19ea51SShawn McCarney // Close the regulator devices in the system. Monitoring is 1965b19ea51SShawn McCarney // normally disabled because the system is being powered off. The 1975b19ea51SShawn McCarney // devices should be closed in case hardware is removed or replaced 1989bd94d36SShawn McCarney // while the system is powered off. 199d692d6dfSBob King system->closeDevices(services); 2005b19ea51SShawn McCarney } 2015b19ea51SShawn McCarney } 20229e9e385SMatthew Barth } 20329e9e385SMatthew Barth 204589c181aSShawn McCarney void Manager::sighupHandler(sdeventplus::source::Signal& /*sigSrc*/, 205589c181aSShawn McCarney const struct signalfd_siginfo* /*sigInfo*/) 206589c181aSShawn McCarney { 207589c181aSShawn McCarney // Reload the JSON configuration file 208589c181aSShawn McCarney loadConfigFile(); 209589c181aSShawn McCarney } 210589c181aSShawn McCarney 211f2bcf1f9SMatthew Barth void Manager::timerExpired() 212f2bcf1f9SMatthew Barth { 213f2bcf1f9SMatthew Barth // TODO Analyze, refresh sensor status, and 214f2bcf1f9SMatthew Barth // collect/update telemetry for each regulator 215f2bcf1f9SMatthew Barth } 216f2bcf1f9SMatthew Barth 2179bd94d36SShawn McCarney void Manager::clearHardwareData() 2189bd94d36SShawn McCarney { 2194e0402cbSShawn McCarney // Clear any cached hardware presence data and VPD values 2209bd94d36SShawn McCarney services.getPresenceService().clearCache(); 2214e0402cbSShawn McCarney services.getVPD().clearCache(); 2229bd94d36SShawn McCarney 2238acaf547SShawn McCarney // Verify config file has been loaded and System object is valid 2248acaf547SShawn McCarney if (isConfigFileLoaded()) 2259bd94d36SShawn McCarney { 2269bd94d36SShawn McCarney // Clear any cached hardware data in the System object 2279bd94d36SShawn McCarney system->clearCache(); 2289bd94d36SShawn McCarney 229*ce540f3cSShawn McCarney // Clear error history related to hardware devices in the System object 230*ce540f3cSShawn McCarney system->clearErrorHistory(); 231*ce540f3cSShawn McCarney } 2329bd94d36SShawn McCarney } 2339bd94d36SShawn McCarney 234589c181aSShawn McCarney void Manager::findCompatibleSystemTypes() 2357cbc5536SMatthew Barth { 236bbc7c583SMatthew Barth using namespace phosphor::power::util; 237bbc7c583SMatthew Barth 238bbc7c583SMatthew Barth try 239bbc7c583SMatthew Barth { 240589c181aSShawn McCarney // Query object mapper for object paths that implement the compatible 241589c181aSShawn McCarney // interface. Returns a map of object paths to a map of services names 242589c181aSShawn McCarney // to their interfaces. 243589c181aSShawn McCarney DbusSubtree subTree = getSubTree(bus, "/xyz/openbmc_project/inventory", 244589c181aSShawn McCarney compatibleIntf, 0); 245589c181aSShawn McCarney 246589c181aSShawn McCarney // Get the first object path 247589c181aSShawn McCarney auto objectIt = subTree.cbegin(); 248589c181aSShawn McCarney if (objectIt != subTree.cend()) 249589c181aSShawn McCarney { 250589c181aSShawn McCarney std::string objPath = objectIt->first; 251589c181aSShawn McCarney 252589c181aSShawn McCarney // Get the first service name 253589c181aSShawn McCarney auto serviceIt = objectIt->second.cbegin(); 254589c181aSShawn McCarney if (serviceIt != objectIt->second.cend()) 255589c181aSShawn McCarney { 256589c181aSShawn McCarney std::string service = serviceIt->first; 257bbc7c583SMatthew Barth if (!service.empty()) 258bbc7c583SMatthew Barth { 259589c181aSShawn McCarney // Get compatible system types property value 260589c181aSShawn McCarney getProperty(compatibleIntf, compatibleNamesProp, objPath, 261589c181aSShawn McCarney service, bus, compatibleSystemTypes); 262bbc7c583SMatthew Barth } 263bbc7c583SMatthew Barth } 264589c181aSShawn McCarney } 265589c181aSShawn McCarney } 266589c181aSShawn McCarney catch (const std::exception&) 267bbc7c583SMatthew Barth { 268589c181aSShawn McCarney // Compatible system types information is not available. The current 269589c181aSShawn McCarney // system might not support the interface, or the service that 270589c181aSShawn McCarney // implements the interface might not be running yet. 271bbc7c583SMatthew Barth } 272bbc7c583SMatthew Barth } 273bbc7c583SMatthew Barth 274e0c6a2d9SShawn McCarney fs::path Manager::findConfigFile() 275e0c6a2d9SShawn McCarney { 276589c181aSShawn McCarney // Build list of possible base file names 277589c181aSShawn McCarney std::vector<std::string> fileNames{}; 278589c181aSShawn McCarney 279589c181aSShawn McCarney // Add possible file names based on compatible system types (if any) 280589c181aSShawn McCarney for (const std::string& systemType : compatibleSystemTypes) 281589c181aSShawn McCarney { 282589c181aSShawn McCarney // Replace all spaces and commas in system type name with underscores 283589c181aSShawn McCarney std::string fileName{systemType}; 284589c181aSShawn McCarney std::replace(fileName.begin(), fileName.end(), ' ', '_'); 285589c181aSShawn McCarney std::replace(fileName.begin(), fileName.end(), ',', '_'); 286589c181aSShawn McCarney 287589c181aSShawn McCarney // Append .json suffix and add to list 288589c181aSShawn McCarney fileName.append(".json"); 289589c181aSShawn McCarney fileNames.emplace_back(fileName); 290589c181aSShawn McCarney } 291589c181aSShawn McCarney 292589c181aSShawn McCarney // Add default file name for systems that don't use compatible interface 293589c181aSShawn McCarney fileNames.emplace_back(defaultConfigFileName); 294589c181aSShawn McCarney 295589c181aSShawn McCarney // Look for a config file with one of the possible base names 296589c181aSShawn McCarney for (const std::string& fileName : fileNames) 297589c181aSShawn McCarney { 298589c181aSShawn McCarney // Check if file exists in test directory 299e0c6a2d9SShawn McCarney fs::path pathName{testConfigFileDir / fileName}; 300589c181aSShawn McCarney if (fs::exists(pathName)) 301e0c6a2d9SShawn McCarney { 302589c181aSShawn McCarney return pathName; 303589c181aSShawn McCarney } 304589c181aSShawn McCarney 305589c181aSShawn McCarney // Check if file exists in standard directory 306e0c6a2d9SShawn McCarney pathName = standardConfigFileDir / fileName; 307589c181aSShawn McCarney if (fs::exists(pathName)) 308e0c6a2d9SShawn McCarney { 309589c181aSShawn McCarney return pathName; 310e0c6a2d9SShawn McCarney } 311e0c6a2d9SShawn McCarney } 312e0c6a2d9SShawn McCarney 313589c181aSShawn McCarney // No config file found; return empty path 314589c181aSShawn McCarney return fs::path{}; 315e0c6a2d9SShawn McCarney } 316e0c6a2d9SShawn McCarney 317e0c6a2d9SShawn McCarney void Manager::loadConfigFile() 318e0c6a2d9SShawn McCarney { 319e0c6a2d9SShawn McCarney try 320e0c6a2d9SShawn McCarney { 321e0c6a2d9SShawn McCarney // Find the absolute path to the config file 322e0c6a2d9SShawn McCarney fs::path pathName = findConfigFile(); 323589c181aSShawn McCarney if (!pathName.empty()) 324589c181aSShawn McCarney { 325e0c6a2d9SShawn McCarney // Log info message in journal; config file path is important 326b464c8bdSShawn McCarney services.getJournal().logInfo("Loading configuration file " + 327b464c8bdSShawn McCarney pathName.string()); 328e0c6a2d9SShawn McCarney 329e0c6a2d9SShawn McCarney // Parse the config file 330e0c6a2d9SShawn McCarney std::vector<std::unique_ptr<Rule>> rules{}; 331e0c6a2d9SShawn McCarney std::vector<std::unique_ptr<Chassis>> chassis{}; 332e0c6a2d9SShawn McCarney std::tie(rules, chassis) = config_file_parser::parse(pathName); 333e0c6a2d9SShawn McCarney 334589c181aSShawn McCarney // Store config file information in a new System object. The old 335589c181aSShawn McCarney // System object, if any, is automatically deleted. 336589c181aSShawn McCarney system = 337589c181aSShawn McCarney std::make_unique<System>(std::move(rules), std::move(chassis)); 338589c181aSShawn McCarney } 339e0c6a2d9SShawn McCarney } 340e0c6a2d9SShawn McCarney catch (const std::exception& e) 341e0c6a2d9SShawn McCarney { 342e0c6a2d9SShawn McCarney // Log error messages in journal 343b464c8bdSShawn McCarney services.getJournal().logError(exception_utils::getMessages(e)); 344b464c8bdSShawn McCarney services.getJournal().logError("Unable to load configuration file"); 345e0c6a2d9SShawn McCarney 346415094c1SShawn McCarney // Log error 347415094c1SShawn McCarney services.getErrorLogging().logConfigFileError(Entry::Level::Error, 348415094c1SShawn McCarney services.getJournal()); 349e0c6a2d9SShawn McCarney } 350e0c6a2d9SShawn McCarney } 351e0c6a2d9SShawn McCarney 3528acaf547SShawn McCarney void Manager::waitUntilConfigFileLoaded() 3538acaf547SShawn McCarney { 3548acaf547SShawn McCarney // If config file not loaded and list of compatible system types is empty 3558acaf547SShawn McCarney if (!isConfigFileLoaded() && compatibleSystemTypes.empty()) 3568acaf547SShawn McCarney { 3578acaf547SShawn McCarney // Loop until compatible system types found or waited max amount of time 3588acaf547SShawn McCarney auto start = std::chrono::system_clock::now(); 3598acaf547SShawn McCarney std::chrono::system_clock::duration timeWaited{0}; 3608acaf547SShawn McCarney while (compatibleSystemTypes.empty() && 3618acaf547SShawn McCarney (timeWaited <= maxTimeToWaitForCompatTypes)) 3628acaf547SShawn McCarney { 3638acaf547SShawn McCarney // Try to find list of compatible system types 3648acaf547SShawn McCarney findCompatibleSystemTypes(); 3658acaf547SShawn McCarney if (!compatibleSystemTypes.empty()) 3668acaf547SShawn McCarney { 3678acaf547SShawn McCarney // Compatible system types found; try to load config file 3688acaf547SShawn McCarney loadConfigFile(); 3698acaf547SShawn McCarney } 3708acaf547SShawn McCarney else 3718acaf547SShawn McCarney { 3728acaf547SShawn McCarney // Sleep 5 seconds 3738acaf547SShawn McCarney using namespace std::chrono_literals; 3748acaf547SShawn McCarney std::this_thread::sleep_for(5s); 3758acaf547SShawn McCarney } 3768acaf547SShawn McCarney timeWaited = std::chrono::system_clock::now() - start; 3778acaf547SShawn McCarney } 3788acaf547SShawn McCarney } 3798acaf547SShawn McCarney } 3808acaf547SShawn McCarney 38184807b96SShawn McCarney } // namespace phosphor::power::regulators 382