11a568650SJolie Ku /**
21a568650SJolie Ku * Copyright © 2019 IBM Corporation
31a568650SJolie Ku *
41a568650SJolie Ku * Licensed under the Apache License, Version 2.0 (the "License");
51a568650SJolie Ku * you may not use this file except in compliance with the License.
61a568650SJolie Ku * You may obtain a copy of the License at
71a568650SJolie Ku *
81a568650SJolie Ku * http://www.apache.org/licenses/LICENSE-2.0
91a568650SJolie Ku *
101a568650SJolie Ku * Unless required by applicable law or agreed to in writing, software
111a568650SJolie Ku * distributed under the License is distributed on an "AS IS" BASIS,
121a568650SJolie Ku * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131a568650SJolie Ku * See the License for the specific language governing permissions and
141a568650SJolie Ku * limitations under the License.
151a568650SJolie Ku */
161a568650SJolie Ku #include "json_parser.hpp"
171a568650SJolie Ku
181a568650SJolie Ku #include "anyof.hpp"
191a568650SJolie Ku #include "fallback.hpp"
201a568650SJolie Ku #include "gpio.hpp"
211a568650SJolie Ku #include "json_config.hpp"
221a568650SJolie Ku #include "sdbusplus.hpp"
231a568650SJolie Ku #include "tach.hpp"
241a568650SJolie Ku
251a568650SJolie Ku #include <nlohmann/json.hpp>
261a568650SJolie Ku #include <phosphor-logging/log.hpp>
271a568650SJolie Ku #include <sdbusplus/bus.hpp>
28a35a890bSMike Capps #include <xyz/openbmc_project/Logging/Create/server.hpp>
29a35a890bSMike Capps #include <xyz/openbmc_project/Logging/Entry/server.hpp>
301a568650SJolie Ku
311a568650SJolie Ku #include <filesystem>
321a568650SJolie Ku #include <fstream>
331a568650SJolie Ku #include <string>
341a568650SJolie Ku
351a568650SJolie Ku namespace phosphor
361a568650SJolie Ku {
371a568650SJolie Ku namespace fan
381a568650SJolie Ku {
391a568650SJolie Ku namespace presence
401a568650SJolie Ku {
411a568650SJolie Ku
421a568650SJolie Ku using json = nlohmann::json;
431a568650SJolie Ku namespace fs = std::filesystem;
441a568650SJolie Ku using namespace phosphor::logging;
451a568650SJolie Ku
461a568650SJolie Ku policies JsonConfig::_policies;
471a568650SJolie Ku const std::map<std::string, methodHandler> JsonConfig::_methods = {
481a568650SJolie Ku {"tach", method::getTach}, {"gpio", method::getGpio}};
491a568650SJolie Ku const std::map<std::string, rpolicyHandler> JsonConfig::_rpolicies = {
501a568650SJolie Ku {"anyof", rpolicy::getAnyof}, {"fallback", rpolicy::getFallback}};
511a568650SJolie Ku
52a35a890bSMike Capps const auto loggingPath = "/xyz/openbmc_project/logging";
53a35a890bSMike Capps const auto loggingCreateIface = "xyz.openbmc_project.Logging.Create";
54a35a890bSMike Capps
JsonConfig(sdbusplus::bus_t & bus)5561b73296SPatrick Williams JsonConfig::JsonConfig(sdbusplus::bus_t& bus) : _bus(bus) {}
561a568650SJolie Ku
start()575b839919SMatthew Barth void JsonConfig::start()
580daedd18SMatt Spinler {
595b839919SMatthew Barth using config = fan::JsonConfig;
605b839919SMatthew Barth
61dfc8c4d0SMatt Spinler if (!_loaded)
62dfc8c4d0SMatt Spinler {
63808d7fe8SMike Capps process(config::load(config::getConfFile(confAppName, confFileName)));
64dfc8c4d0SMatt Spinler
65dfc8c4d0SMatt Spinler _loaded = true;
660daedd18SMatt Spinler
670daedd18SMatt Spinler for (auto& p : _policies)
680daedd18SMatt Spinler {
690daedd18SMatt Spinler p->monitor();
700daedd18SMatt Spinler }
711a568650SJolie Ku }
72dfc8c4d0SMatt Spinler }
731a568650SJolie Ku
get()741a568650SJolie Ku const policies& JsonConfig::get()
751a568650SJolie Ku {
761a568650SJolie Ku return _policies;
771a568650SJolie Ku }
781a568650SJolie Ku
sighupHandler(sdeventplus::source::Signal &,const struct signalfd_siginfo *)79808d7fe8SMike Capps void JsonConfig::sighupHandler(sdeventplus::source::Signal& /*sigSrc*/,
80808d7fe8SMike Capps const struct signalfd_siginfo* /*sigInfo*/)
811a568650SJolie Ku {
821a568650SJolie Ku try
831a568650SJolie Ku {
841a568650SJolie Ku using config = fan::JsonConfig;
851a568650SJolie Ku
869e9f599cSMatt Spinler _reporter.reset();
879e9f599cSMatt Spinler
881a568650SJolie Ku // Load and process the json configuration
89808d7fe8SMike Capps process(config::load(config::getConfFile(confAppName, confFileName)));
901a568650SJolie Ku
911a568650SJolie Ku for (auto& p : _policies)
921a568650SJolie Ku {
931a568650SJolie Ku p->monitor();
941a568650SJolie Ku }
951a568650SJolie Ku log<level::INFO>("Configuration loaded successfully");
961a568650SJolie Ku }
97ddb773b2SPatrick Williams catch (const std::runtime_error& re)
981a568650SJolie Ku {
991a568650SJolie Ku log<level::ERR>("Error loading config, no config changes made",
1001a568650SJolie Ku entry("LOAD_ERROR=%s", re.what()));
1011a568650SJolie Ku }
1021a568650SJolie Ku }
1031a568650SJolie Ku
process(const json & jsonConf)1041a568650SJolie Ku void JsonConfig::process(const json& jsonConf)
1051a568650SJolie Ku {
1061a568650SJolie Ku policies policies;
1071a568650SJolie Ku std::vector<fanPolicy> fans;
1081a568650SJolie Ku // Set the expected number of fan entries
1091a568650SJolie Ku // to be size of the list of fan json config entries
1101a568650SJolie Ku // (Must be done to eliminate vector reallocation of fan references)
1119e9f599cSMatt Spinler fans.reserve(jsonConf.size());
1129e9f599cSMatt Spinler for (auto& member : jsonConf)
1131a568650SJolie Ku {
1141a568650SJolie Ku if (!member.contains("name") || !member.contains("path") ||
1151a568650SJolie Ku !member.contains("methods") || !member.contains("rpolicy"))
1161a568650SJolie Ku {
1171a568650SJolie Ku log<level::ERR>("Missing required fan presence properties",
1181a568650SJolie Ku entry("REQUIRED_PROPERTIES=%s",
1191a568650SJolie Ku "{name, path, methods, rpolicy}"));
1201a568650SJolie Ku throw std::runtime_error(
1211a568650SJolie Ku "Missing required fan presence properties");
1221a568650SJolie Ku }
1231a568650SJolie Ku
1241a568650SJolie Ku // Loop thru the configured methods of presence detection
1251a568650SJolie Ku std::vector<std::unique_ptr<PresenceSensor>> sensors;
1261a568650SJolie Ku for (auto& method : member["methods"].items())
1271a568650SJolie Ku {
1281a568650SJolie Ku if (!method.value().contains("type"))
1291a568650SJolie Ku {
1301a568650SJolie Ku log<level::ERR>(
1311a568650SJolie Ku "Missing required fan presence method type",
1321a568650SJolie Ku entry("FAN_NAME=%s",
1331a568650SJolie Ku member["name"].get<std::string>().c_str()));
1341a568650SJolie Ku throw std::runtime_error(
1351a568650SJolie Ku "Missing required fan presence method type");
1361a568650SJolie Ku }
1371a568650SJolie Ku // The method type of fan presence detection
1381a568650SJolie Ku // (Must have a supported function within the method namespace)
1391a568650SJolie Ku auto type = method.value()["type"].get<std::string>();
1401a568650SJolie Ku std::transform(type.begin(), type.end(), type.begin(), tolower);
1411a568650SJolie Ku auto func = _methods.find(type);
1421a568650SJolie Ku if (func != _methods.end())
1431a568650SJolie Ku {
1441a568650SJolie Ku // Call function for method type
1451a568650SJolie Ku auto sensor = func->second(fans.size(), method.value());
1461a568650SJolie Ku if (sensor)
1471a568650SJolie Ku {
1481a568650SJolie Ku sensors.emplace_back(std::move(sensor));
1491a568650SJolie Ku }
1501a568650SJolie Ku }
1511a568650SJolie Ku else
1521a568650SJolie Ku {
1531a568650SJolie Ku log<level::ERR>(
1541a568650SJolie Ku "Invalid fan presence method type",
1551a568650SJolie Ku entry("FAN_NAME=%s",
1561a568650SJolie Ku member["name"].get<std::string>().c_str()),
1571a568650SJolie Ku entry("METHOD_TYPE=%s", type.c_str()));
1581a568650SJolie Ku throw std::runtime_error("Invalid fan presence method type");
1591a568650SJolie Ku }
1601a568650SJolie Ku }
1619e9f599cSMatt Spinler
1629e9f599cSMatt Spinler // Get the amount of time a fan must be not present before
1639e9f599cSMatt Spinler // creating an error.
1649e9f599cSMatt Spinler std::optional<size_t> timeUntilError;
1659e9f599cSMatt Spinler if (member.contains("fan_missing_error_time"))
1669e9f599cSMatt Spinler {
1679e9f599cSMatt Spinler timeUntilError = member["fan_missing_error_time"].get<size_t>();
1689e9f599cSMatt Spinler }
1699e9f599cSMatt Spinler
170bc4179e9SMatt Spinler std::unique_ptr<EEPROMDevice> eepromDevice;
171bc4179e9SMatt Spinler if (member.contains("eeprom"))
172bc4179e9SMatt Spinler {
173bc4179e9SMatt Spinler const auto& eeprom = member.at("eeprom");
174bc4179e9SMatt Spinler if (!eeprom.contains("bus_address") ||
175bc4179e9SMatt Spinler !eeprom.contains("driver_name") ||
176bc4179e9SMatt Spinler !eeprom.contains("bind_delay_ms"))
177bc4179e9SMatt Spinler {
178bc4179e9SMatt Spinler log<level::ERR>(
179bc4179e9SMatt Spinler "Missing address, driver_name, or bind_delay_ms in eeprom "
180bc4179e9SMatt Spinler "section",
181bc4179e9SMatt Spinler entry("FAN_NAME=%s",
182bc4179e9SMatt Spinler member["name"].get<std::string>().c_str()));
183bc4179e9SMatt Spinler
184bc4179e9SMatt Spinler throw std::runtime_error("Missing address, driver_name, or "
185bc4179e9SMatt Spinler "bind_delay_ms in eeprom section");
186bc4179e9SMatt Spinler }
187bc4179e9SMatt Spinler eepromDevice = std::make_unique<EEPROMDevice>(
188bc4179e9SMatt Spinler eeprom["bus_address"].get<std::string>(),
189bc4179e9SMatt Spinler eeprom["driver_name"].get<std::string>(),
190bc4179e9SMatt Spinler eeprom["bind_delay_ms"].get<size_t>());
191bc4179e9SMatt Spinler }
192bc4179e9SMatt Spinler
19361b73296SPatrick Williams auto fan = std::make_tuple(member["name"], member["path"],
19461b73296SPatrick Williams timeUntilError);
1951a568650SJolie Ku // Create a fan object
1961a568650SJolie Ku fans.emplace_back(std::make_tuple(fan, std::move(sensors)));
1971a568650SJolie Ku
1981a568650SJolie Ku // Add fan presence policy
19961b73296SPatrick Williams auto policy = getPolicy(member["rpolicy"], fans.back(),
20061b73296SPatrick Williams std::move(eepromDevice));
2011a568650SJolie Ku if (policy)
2021a568650SJolie Ku {
2031a568650SJolie Ku policies.emplace_back(std::move(policy));
2041a568650SJolie Ku }
2051a568650SJolie Ku }
2061a568650SJolie Ku
2071a568650SJolie Ku // Success, refresh fans and policies lists
2081a568650SJolie Ku _fans.clear();
2091a568650SJolie Ku _fans.swap(fans);
2101a568650SJolie Ku
2111a568650SJolie Ku _policies.clear();
2121a568650SJolie Ku _policies.swap(policies);
213e8122390SMatt Spinler
214e8122390SMatt Spinler // Create the error reporter class if necessary
2159e9f599cSMatt Spinler if (std::any_of(_fans.begin(), _fans.end(), [](const auto& fan) {
2169e9f599cSMatt Spinler return std::get<std::optional<size_t>>(std::get<Fan>(fan)) !=
2179e9f599cSMatt Spinler std::nullopt;
2189e9f599cSMatt Spinler }))
219e8122390SMatt Spinler {
2209e9f599cSMatt Spinler _reporter = std::make_unique<ErrorReporter>(_bus, _fans);
221e8122390SMatt Spinler }
2221a568650SJolie Ku }
2231a568650SJolie Ku
2241a568650SJolie Ku std::unique_ptr<RedundancyPolicy>
getPolicy(const json & rpolicy,const fanPolicy & fpolicy,std::unique_ptr<EEPROMDevice> eepromDevice)225bc4179e9SMatt Spinler JsonConfig::getPolicy(const json& rpolicy, const fanPolicy& fpolicy,
226bc4179e9SMatt Spinler std::unique_ptr<EEPROMDevice> eepromDevice)
2271a568650SJolie Ku {
2281a568650SJolie Ku if (!rpolicy.contains("type"))
2291a568650SJolie Ku {
2301a568650SJolie Ku log<level::ERR>(
2311a568650SJolie Ku "Missing required fan presence policy type",
2321a568650SJolie Ku entry("FAN_NAME=%s",
2331a568650SJolie Ku std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
2341a568650SJolie Ku entry("REQUIRED_PROPERTIES=%s", "{type}"));
2351a568650SJolie Ku throw std::runtime_error("Missing required fan presence policy type");
2361a568650SJolie Ku }
2371a568650SJolie Ku
2381a568650SJolie Ku // The redundancy policy type for fan presence detection
2391a568650SJolie Ku // (Must have a supported function within the rpolicy namespace)
2401a568650SJolie Ku auto type = rpolicy["type"].get<std::string>();
2411a568650SJolie Ku std::transform(type.begin(), type.end(), type.begin(), tolower);
2421a568650SJolie Ku auto func = _rpolicies.find(type);
2431a568650SJolie Ku if (func != _rpolicies.end())
2441a568650SJolie Ku {
2451a568650SJolie Ku // Call function for redundancy policy type and return the policy
246bc4179e9SMatt Spinler return func->second(fpolicy, std::move(eepromDevice));
2471a568650SJolie Ku }
2481a568650SJolie Ku else
2491a568650SJolie Ku {
2501a568650SJolie Ku log<level::ERR>(
2511a568650SJolie Ku "Invalid fan presence policy type",
2521a568650SJolie Ku entry("FAN_NAME=%s",
2531a568650SJolie Ku std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
2541a568650SJolie Ku entry("RPOLICY_TYPE=%s", type.c_str()));
2551a568650SJolie Ku throw std::runtime_error("Invalid fan presence methods policy type");
2561a568650SJolie Ku }
2571a568650SJolie Ku }
2581a568650SJolie Ku
2591a568650SJolie Ku /**
2601a568650SJolie Ku * Methods of fan presence detection function definitions
2611a568650SJolie Ku */
2621a568650SJolie Ku namespace method
2631a568650SJolie Ku {
2641a568650SJolie Ku // Get a constructed presence sensor for fan presence detection by tach
getTach(size_t fanIndex,const json & method)2651a568650SJolie Ku std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method)
2661a568650SJolie Ku {
2671a568650SJolie Ku if (!method.contains("sensors") || method["sensors"].size() == 0)
2681a568650SJolie Ku {
2691a568650SJolie Ku log<level::ERR>("Missing required tach method properties",
2701a568650SJolie Ku entry("FAN_ENTRY=%d", fanIndex),
2711a568650SJolie Ku entry("REQUIRED_PROPERTIES=%s", "{sensors}"));
2721a568650SJolie Ku throw std::runtime_error("Missing required tach method properties");
2731a568650SJolie Ku }
2741a568650SJolie Ku
2751a568650SJolie Ku std::vector<std::string> sensors;
2761a568650SJolie Ku for (auto& sensor : method["sensors"])
2771a568650SJolie Ku {
2781a568650SJolie Ku sensors.emplace_back(sensor.get<std::string>());
2791a568650SJolie Ku }
2801a568650SJolie Ku
2811a568650SJolie Ku return std::make_unique<PolicyAccess<Tach, JsonConfig>>(fanIndex,
2821a568650SJolie Ku std::move(sensors));
2831a568650SJolie Ku }
2841a568650SJolie Ku
2851a568650SJolie Ku // Get a constructed presence sensor for fan presence detection by gpio
getGpio(size_t fanIndex,const json & method)2861a568650SJolie Ku std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method)
2871a568650SJolie Ku {
2881a568650SJolie Ku if (!method.contains("physpath") || !method.contains("devpath") ||
2891a568650SJolie Ku !method.contains("key"))
2901a568650SJolie Ku {
2911a568650SJolie Ku log<level::ERR>(
2921a568650SJolie Ku "Missing required gpio method properties",
2931a568650SJolie Ku entry("FAN_ENTRY=%d", fanIndex),
2941a568650SJolie Ku entry("REQUIRED_PROPERTIES=%s", "{physpath, devpath, key}"));
2951a568650SJolie Ku throw std::runtime_error("Missing required gpio method properties");
2961a568650SJolie Ku }
2971a568650SJolie Ku
2981a568650SJolie Ku auto physpath = method["physpath"].get<std::string>();
2991a568650SJolie Ku auto devpath = method["devpath"].get<std::string>();
3001a568650SJolie Ku auto key = method["key"].get<unsigned int>();
3011a568650SJolie Ku
302a35a890bSMike Capps try
303a35a890bSMike Capps {
304a35a890bSMike Capps return std::make_unique<PolicyAccess<Gpio, JsonConfig>>(
305a35a890bSMike Capps fanIndex, physpath, devpath, key);
306a35a890bSMike Capps }
307a35a890bSMike Capps catch (const sdbusplus::exception_t& e)
308a35a890bSMike Capps {
309a35a890bSMike Capps namespace sdlogging = sdbusplus::xyz::openbmc_project::Logging::server;
310a35a890bSMike Capps
311a35a890bSMike Capps log<level::ERR>(
312*fbf4703fSPatrick Williams std::format(
313a35a890bSMike Capps "Error creating Gpio device bridge, hardware not detected: {}",
314a35a890bSMike Capps e.what())
315a35a890bSMike Capps .c_str());
316a35a890bSMike Capps
317a35a890bSMike Capps auto severity =
318a35a890bSMike Capps sdlogging::convertForMessage(sdlogging::Entry::Level::Error);
319a35a890bSMike Capps
320a35a890bSMike Capps std::map<std::string, std::string> additionalData{
321a35a890bSMike Capps {"PHYSPATH", physpath},
322a35a890bSMike Capps {"DEVPATH", devpath},
323a35a890bSMike Capps {"FANINDEX", std::to_string(fanIndex)}};
324a35a890bSMike Capps
325a35a890bSMike Capps try
326a35a890bSMike Capps {
327a35a890bSMike Capps util::SDBusPlus::lookupAndCallMethod(
328a35a890bSMike Capps loggingPath, loggingCreateIface, "Create",
329a35a890bSMike Capps "xyz.openbmc_project.Fan.Presence.Error.GPIODeviceUnavailable",
330a35a890bSMike Capps severity, additionalData);
331a35a890bSMike Capps }
332a35a890bSMike Capps catch (const util::DBusError& e)
333a35a890bSMike Capps {
334*fbf4703fSPatrick Williams log<level::ERR>(std::format("Call to create an error log for "
335a35a890bSMike Capps "presence-sensor failure failed: {}",
336a35a890bSMike Capps e.what())
337a35a890bSMike Capps .c_str());
338a35a890bSMike Capps }
339a35a890bSMike Capps
340a35a890bSMike Capps return std::make_unique<PolicyAccess<NullGpio, JsonConfig>>();
341a35a890bSMike Capps }
3421a568650SJolie Ku }
3431a568650SJolie Ku
3441a568650SJolie Ku } // namespace method
3451a568650SJolie Ku
3461a568650SJolie Ku /**
3471a568650SJolie Ku * Redundancy policies for fan presence detection function definitions
3481a568650SJolie Ku */
3491a568650SJolie Ku namespace rpolicy
3501a568650SJolie Ku {
3511a568650SJolie Ku // Get an `Anyof` redundancy policy for the fan
352bc4179e9SMatt Spinler std::unique_ptr<RedundancyPolicy>
getAnyof(const fanPolicy & fan,std::unique_ptr<EEPROMDevice> eepromDevice)353bc4179e9SMatt Spinler getAnyof(const fanPolicy& fan, std::unique_ptr<EEPROMDevice> eepromDevice)
3541a568650SJolie Ku {
3551a568650SJolie Ku std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
3561a568650SJolie Ku for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
3571a568650SJolie Ku {
3581a568650SJolie Ku pSensors.emplace_back(*fanSensor);
3591a568650SJolie Ku }
3601a568650SJolie Ku
361bc4179e9SMatt Spinler return std::make_unique<AnyOf>(std::get<fanPolicyFanPos>(fan), pSensors,
362bc4179e9SMatt Spinler std::move(eepromDevice));
3631a568650SJolie Ku }
3641a568650SJolie Ku
3651a568650SJolie Ku // Get a `Fallback` redundancy policy for the fan
366bc4179e9SMatt Spinler std::unique_ptr<RedundancyPolicy>
getFallback(const fanPolicy & fan,std::unique_ptr<EEPROMDevice> eepromDevice)367bc4179e9SMatt Spinler getFallback(const fanPolicy& fan,
368bc4179e9SMatt Spinler std::unique_ptr<EEPROMDevice> eepromDevice)
3691a568650SJolie Ku {
3701a568650SJolie Ku std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
3711a568650SJolie Ku for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
3721a568650SJolie Ku {
3731a568650SJolie Ku // Place in the order given to fallback correctly
3741a568650SJolie Ku pSensors.emplace_back(*fanSensor);
3751a568650SJolie Ku }
3761a568650SJolie Ku
377bc4179e9SMatt Spinler return std::make_unique<Fallback>(std::get<fanPolicyFanPos>(fan), pSensors,
378bc4179e9SMatt Spinler std::move(eepromDevice));
3791a568650SJolie Ku }
3801a568650SJolie Ku
3811a568650SJolie Ku } // namespace rpolicy
3821a568650SJolie Ku
3831a568650SJolie Ku } // namespace presence
3841a568650SJolie Ku } // namespace fan
3851a568650SJolie Ku } // namespace phosphor
386