|  | /* | 
|  | // Copyright (c) 2018 Intel Corporation | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include <boost/bimap.hpp> | 
|  | #include <boost/container/flat_map.hpp> | 
|  | #include <ipmid/api.hpp> | 
|  | #include <ipmid/types.hpp> | 
|  | #include <phosphor-logging/log.hpp> | 
|  | #include <sdbusplus/bus/match.hpp> | 
|  |  | 
|  | #include <cstdio> | 
|  | #include <cstring> | 
|  | #include <exception> | 
|  | #include <filesystem> | 
|  | #include <map> | 
|  | #include <optional> | 
|  | #include <string> | 
|  | #include <unordered_set> | 
|  | #include <vector> | 
|  |  | 
|  | #pragma once | 
|  |  | 
|  | static constexpr bool debug = false; | 
|  |  | 
|  | struct CmpStrVersion | 
|  | { | 
|  | bool operator()(std::string a, std::string b) const | 
|  | { | 
|  | return strverscmp(a.c_str(), b.c_str()) < 0; | 
|  | } | 
|  | }; | 
|  |  | 
|  | using SensorSubTree = boost::container::flat_map< | 
|  | std::string, | 
|  | boost::container::flat_map<std::string, std::vector<std::string>>, | 
|  | CmpStrVersion>; | 
|  |  | 
|  | using SensorNumMap = boost::bimap<int, std::string>; | 
|  |  | 
|  | static constexpr uint16_t maxSensorsPerLUN = 255; | 
|  | static constexpr uint16_t maxIPMISensors = (maxSensorsPerLUN * 3); | 
|  | static constexpr uint16_t lun1Sensor0 = 0x100; | 
|  | static constexpr uint16_t lun3Sensor0 = 0x300; | 
|  | static constexpr uint16_t invalidSensorNumber = 0xFFFF; | 
|  | static constexpr uint8_t reservedSensorNumber = 0xFF; | 
|  |  | 
|  | namespace details | 
|  | { | 
|  | // Enable/disable the logging of stats instrumentation | 
|  | static constexpr bool enableInstrumentation = false; | 
|  |  | 
|  | class IPMIStatsEntry | 
|  | { | 
|  | private: | 
|  | int numReadings = 0; | 
|  | int numMissings = 0; | 
|  | int numStreakRead = 0; | 
|  | int numStreakMiss = 0; | 
|  | double minValue = 0.0; | 
|  | double maxValue = 0.0; | 
|  | std::string sensorName; | 
|  |  | 
|  | public: | 
|  | const std::string& getName(void) const | 
|  | { | 
|  | return sensorName; | 
|  | } | 
|  |  | 
|  | void updateName(std::string_view name) | 
|  | { | 
|  | sensorName = name; | 
|  | } | 
|  |  | 
|  | // Returns true if this is the first successful reading | 
|  | // This is so the caller can log the coefficients used | 
|  | bool updateReading(double reading, int raw) | 
|  | { | 
|  | if constexpr (!enableInstrumentation) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool first = ((numReadings == 0) && (numMissings == 0)); | 
|  |  | 
|  | // Sensors can use "nan" to indicate unavailable reading | 
|  | if (!(std::isfinite(reading))) | 
|  | { | 
|  | // Only show this if beginning a new streak | 
|  | if (numStreakMiss == 0) | 
|  | { | 
|  | lg2::error( | 
|  | "IPMI sensor {NAME}: Missing reading, byte={BYTE}, Reading " | 
|  | "counts good={GOOD} miss={MISS}, Prior good streak={STREAK}", | 
|  | "NAME", sensorName, "BYTE", raw, "GOOD", numReadings, | 
|  | "MISS", numMissings, "STREAK", numStreakRead); | 
|  | } | 
|  |  | 
|  | numStreakRead = 0; | 
|  | ++numMissings; | 
|  | ++numStreakMiss; | 
|  |  | 
|  | return first; | 
|  | } | 
|  |  | 
|  | // Only show this if beginning a new streak and not the first time | 
|  | if ((numStreakRead == 0) && (numReadings != 0)) | 
|  | { | 
|  | lg2::error( | 
|  | "IPMI sensor {NAME}: Recovered reading, value={VALUE} byte={BYTE}" | 
|  | ", Reading counts good={GOOD} miss={MISS}, Prior miss " | 
|  | "streak={STREAK}", | 
|  | "NAME", sensorName, "VALUE", reading, "BYTE", raw, "GOOD", | 
|  | numReadings, "MISS", numMissings, "STREAK", numStreakMiss); | 
|  | } | 
|  |  | 
|  | // Initialize min/max if the first successful reading | 
|  | if (numReadings == 0) | 
|  | { | 
|  | lg2::error( | 
|  | "IPMI sensor {NAME}: First reading, value={VALUE} byte={BYTE}", | 
|  | "NAME", sensorName, "VALUE", reading, "BYTE", raw); | 
|  |  | 
|  | minValue = reading; | 
|  | maxValue = reading; | 
|  | } | 
|  |  | 
|  | numStreakMiss = 0; | 
|  | ++numReadings; | 
|  | ++numStreakRead; | 
|  |  | 
|  | // Only provide subsequent output if new min/max established | 
|  | if (reading < minValue) | 
|  | { | 
|  | lg2::error( | 
|  | "IPMI sensor {NAME}: Lowest reading, value={VALUE} byte={BYTE}", | 
|  | "NAME", sensorName, "VALUE", reading, "BYTE", raw); | 
|  |  | 
|  | minValue = reading; | 
|  | } | 
|  |  | 
|  | if (reading > maxValue) | 
|  | { | 
|  | lg2::error( | 
|  | "IPMI sensor {NAME}: Highest reading, value={VALUE} byte={BYTE}", | 
|  | "NAME", sensorName, "VALUE", reading, "BYTE", raw); | 
|  |  | 
|  | maxValue = reading; | 
|  | } | 
|  |  | 
|  | return first; | 
|  | } | 
|  | }; | 
|  |  | 
|  | class IPMIStatsTable | 
|  | { | 
|  | private: | 
|  | std::vector<IPMIStatsEntry> entries; | 
|  |  | 
|  | private: | 
|  | void padEntries(size_t index) | 
|  | { | 
|  | char hexbuf[16]; | 
|  |  | 
|  | // Pad vector until entries[index] becomes a valid index | 
|  | while (entries.size() <= index) | 
|  | { | 
|  | // As name not known yet, use human-readable hex as name | 
|  | IPMIStatsEntry newEntry; | 
|  | sprintf(hexbuf, "0x%02zX", entries.size()); | 
|  | newEntry.updateName(hexbuf); | 
|  |  | 
|  | entries.push_back(std::move(newEntry)); | 
|  | } | 
|  | } | 
|  |  | 
|  | public: | 
|  | void wipeTable(void) | 
|  | { | 
|  | entries.clear(); | 
|  | } | 
|  |  | 
|  | const std::string& getName(size_t index) | 
|  | { | 
|  | padEntries(index); | 
|  | return entries[index].getName(); | 
|  | } | 
|  |  | 
|  | void updateName(size_t index, std::string_view name) | 
|  | { | 
|  | padEntries(index); | 
|  | entries[index].updateName(name); | 
|  | } | 
|  |  | 
|  | bool updateReading(size_t index, double reading, int raw) | 
|  | { | 
|  | padEntries(index); | 
|  | return entries[index].updateReading(reading, raw); | 
|  | } | 
|  | }; | 
|  |  | 
|  | class IPMIWriteEntry | 
|  | { | 
|  | private: | 
|  | bool writePermission = false; | 
|  |  | 
|  | public: | 
|  | bool getWritePermission(void) const | 
|  | { | 
|  | return writePermission; | 
|  | } | 
|  |  | 
|  | void setWritePermission(bool permission) | 
|  | { | 
|  | writePermission = permission; | 
|  | } | 
|  | }; | 
|  |  | 
|  | class IPMIWriteTable | 
|  | { | 
|  | private: | 
|  | std::vector<IPMIWriteEntry> entries; | 
|  |  | 
|  | private: | 
|  | void padEntries(size_t index) | 
|  | { | 
|  | // Pad vector until entries[index] becomes a valid index | 
|  | if (entries.size() <= index) | 
|  | { | 
|  | entries.resize(index + 1); | 
|  | } | 
|  | } | 
|  |  | 
|  | public: | 
|  | void wipeTable(void) | 
|  | { | 
|  | entries.clear(); | 
|  | } | 
|  |  | 
|  | bool getWritePermission(size_t index) | 
|  | { | 
|  | padEntries(index); | 
|  | return entries[index].getWritePermission(); | 
|  | } | 
|  |  | 
|  | void setWritePermission(size_t index, bool permission) | 
|  | { | 
|  | padEntries(index); | 
|  | entries[index].setWritePermission(permission); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Store information for threshold sensors and they are not used by VR | 
|  | // sensors. These objects are global singletons, used from a variety of places. | 
|  | inline IPMIStatsTable sdrStatsTable; | 
|  | inline IPMIWriteTable sdrWriteTable; | 
|  |  | 
|  | /** | 
|  | * Search ObjectMapper for sensors and update them to subtree. | 
|  | * | 
|  | * The function will search for sensors under either | 
|  | * /xyz/openbmc_project/sensors or /xyz/openbmc_project/extsensors. It will | 
|  | * optionally search VR typed sensors under /xyz/openbmc_project/vr | 
|  | * | 
|  | * @return the updated amount of times any of "sensors" or "extsensors" sensor | 
|  | * paths updated successfully, previous amount if all failed. The "vr" | 
|  | * sensor path is optional, and does not participate in the return value. | 
|  | */ | 
|  | uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree); | 
|  |  | 
|  | bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap); | 
|  | } // namespace details | 
|  |  | 
|  | bool getSensorSubtree(SensorSubTree& subtree); | 
|  |  | 
|  | #ifdef FEATURE_HYBRID_SENSORS | 
|  | ipmi::sensor::IdInfoMap::const_iterator findStaticSensor( | 
|  | const std::string& path); | 
|  | #endif | 
|  |  | 
|  | struct CmpStr | 
|  | { | 
|  | bool operator()(const char* a, const char* b) const | 
|  | { | 
|  | return std::strcmp(a, b) < 0; | 
|  | } | 
|  | }; | 
|  |  | 
|  | static constexpr size_t sensorTypeCodes = 0; | 
|  | static constexpr size_t sensorEventTypeCodes = 1; | 
|  |  | 
|  | enum class SensorTypeCodes : uint8_t | 
|  | { | 
|  | reserved = 0x00, | 
|  | temperature = 0x01, | 
|  | voltage = 0x02, | 
|  | current = 0x03, | 
|  | fan = 0x04, | 
|  | physicalSecurity = 0x5, | 
|  | processor = 0x07, | 
|  | powerUnit = 0x09, | 
|  | other = 0x0b, | 
|  | memory = 0x0c, | 
|  | buttons = 0x14, | 
|  | watchdog2 = 0x23, | 
|  | entity = 0x25, | 
|  | oemC0 = 0xc0, | 
|  | }; | 
|  |  | 
|  | enum class SensorEventTypeCodes : uint8_t | 
|  | { | 
|  | unspecified = 0x00, | 
|  | threshold = 0x01, | 
|  | sensorSpecified = 0x6f | 
|  | }; | 
|  |  | 
|  | extern boost::container::flat_map< | 
|  | const char*, std::pair<SensorTypeCodes, SensorEventTypeCodes>, CmpStr> | 
|  | sensorTypes; | 
|  |  | 
|  | std::string getSensorTypeStringFromPath(const std::string& path); | 
|  |  | 
|  | uint8_t getSensorTypeFromPath(const std::string& path); | 
|  |  | 
|  | uint16_t getSensorNumberFromPath(const std::string& path); | 
|  |  | 
|  | uint8_t getSensorEventTypeFromPath(const std::string& path); | 
|  |  | 
|  | std::string getPathFromSensorNumber(uint16_t sensorNum); | 
|  |  | 
|  | namespace ipmi | 
|  | { | 
|  | std::optional<std::map<std::string, std::vector<std::string>>> | 
|  | getObjectInterfaces(const char* path); | 
|  |  | 
|  | std::map<std::string, Value> getEntityManagerProperties(const char* path, | 
|  | const char* interface); | 
|  |  | 
|  | std::optional<std::unordered_set<std::string>>& getIpmiDecoratorPaths( | 
|  | const std::optional<ipmi::Context::ptr>& ctx); | 
|  |  | 
|  | const std::string* getSensorConfigurationInterface( | 
|  | const std::map<std::string, std::vector<std::string>>& | 
|  | sensorInterfacesResponse); | 
|  |  | 
|  | void updateIpmiFromAssociation( | 
|  | const std::string& path, | 
|  | const std::unordered_set<std::string>& ipmiDecoratorPaths, | 
|  | const DbusInterfaceMap& sensorMap, uint8_t& entityId, | 
|  | uint8_t& entityInstance); | 
|  | } // namespace ipmi |