1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 17 #include <boost/algorithm/string.hpp> 18 #include <boost/bimap.hpp> 19 #include <boost/container/flat_map.hpp> 20 #include <cstdio> 21 #include <cstring> 22 #include <exception> 23 #include <filesystem> 24 #include <ipmid/api.hpp> 25 #include <ipmid/types.hpp> 26 #include <map> 27 #include <phosphor-logging/log.hpp> 28 #include <sdbusplus/bus/match.hpp> 29 #include <string> 30 #include <vector> 31 32 #pragma once 33 34 static constexpr bool debug = false; 35 36 struct CmpStrVersion 37 { 38 bool operator()(std::string a, std::string b) const 39 { 40 return strverscmp(a.c_str(), b.c_str()) < 0; 41 } 42 }; 43 44 using SensorSubTree = boost::container::flat_map< 45 std::string, 46 boost::container::flat_map<std::string, std::vector<std::string>>, 47 CmpStrVersion>; 48 49 using SensorNumMap = boost::bimap<int, std::string>; 50 51 static constexpr uint16_t maxSensorsPerLUN = 255; 52 static constexpr uint16_t maxIPMISensors = (maxSensorsPerLUN * 3); 53 static constexpr uint16_t lun1Sensor0 = 0x100; 54 static constexpr uint16_t lun3Sensor0 = 0x300; 55 static constexpr uint16_t invalidSensorNumber = 0xFFFF; 56 static constexpr uint8_t reservedSensorNumber = 0xFF; 57 58 namespace details 59 { 60 // Enable/disable the logging of stats instrumentation 61 static constexpr bool enableInstrumentation = false; 62 63 class IPMIStatsEntry 64 { 65 private: 66 int numReadings = 0; 67 int numMissings = 0; 68 int numStreakRead = 0; 69 int numStreakMiss = 0; 70 double minValue = 0.0; 71 double maxValue = 0.0; 72 std::string sensorName; 73 74 public: 75 const std::string& getName(void) const 76 { 77 return sensorName; 78 } 79 80 void updateName(std::string_view name) 81 { 82 sensorName = name; 83 } 84 85 // Returns true if this is the first successful reading 86 // This is so the caller can log the coefficients used 87 bool updateReading(double reading, int raw) 88 { 89 if constexpr (!enableInstrumentation) 90 { 91 return false; 92 } 93 94 bool first = ((numReadings == 0) && (numMissings == 0)); 95 96 // Sensors can use "nan" to indicate unavailable reading 97 if (!(std::isfinite(reading))) 98 { 99 // Only show this if beginning a new streak 100 if (numStreakMiss == 0) 101 { 102 std::cerr << "IPMI sensor " << sensorName 103 << ": Missing reading, byte=" << raw 104 << ", Reading counts good=" << numReadings 105 << " miss=" << numMissings 106 << ", Prior good streak=" << numStreakRead << "\n"; 107 } 108 109 numStreakRead = 0; 110 ++numMissings; 111 ++numStreakMiss; 112 113 return first; 114 } 115 116 // Only show this if beginning a new streak and not the first time 117 if ((numStreakRead == 0) && (numReadings != 0)) 118 { 119 std::cerr << "IPMI sensor " << sensorName 120 << ": Recovered reading, value=" << reading 121 << " byte=" << raw 122 << ", Reading counts good=" << numReadings 123 << " miss=" << numMissings 124 << ", Prior miss streak=" << numStreakMiss << "\n"; 125 } 126 127 // Initialize min/max if the first successful reading 128 if (numReadings == 0) 129 { 130 std::cerr << "IPMI sensor " << sensorName 131 << ": First reading, value=" << reading << " byte=" << raw 132 << "\n"; 133 134 minValue = reading; 135 maxValue = reading; 136 } 137 138 numStreakMiss = 0; 139 ++numReadings; 140 ++numStreakRead; 141 142 // Only provide subsequent output if new min/max established 143 if (reading < minValue) 144 { 145 std::cerr << "IPMI sensor " << sensorName 146 << ": Lowest reading, value=" << reading 147 << " byte=" << raw << "\n"; 148 149 minValue = reading; 150 } 151 152 if (reading > maxValue) 153 { 154 std::cerr << "IPMI sensor " << sensorName 155 << ": Highest reading, value=" << reading 156 << " byte=" << raw << "\n"; 157 158 maxValue = reading; 159 } 160 161 return first; 162 } 163 }; 164 165 class IPMIStatsTable 166 { 167 private: 168 std::vector<IPMIStatsEntry> entries; 169 170 private: 171 void padEntries(size_t index) 172 { 173 char hexbuf[16]; 174 175 // Pad vector until entries[index] becomes a valid index 176 while (entries.size() <= index) 177 { 178 // As name not known yet, use human-readable hex as name 179 IPMIStatsEntry newEntry; 180 sprintf(hexbuf, "0x%02zX", entries.size()); 181 newEntry.updateName(hexbuf); 182 183 entries.push_back(std::move(newEntry)); 184 } 185 } 186 187 public: 188 void wipeTable(void) 189 { 190 entries.clear(); 191 } 192 193 const std::string& getName(size_t index) 194 { 195 padEntries(index); 196 return entries[index].getName(); 197 } 198 199 void updateName(size_t index, std::string_view name) 200 { 201 padEntries(index); 202 entries[index].updateName(name); 203 } 204 205 bool updateReading(size_t index, double reading, int raw) 206 { 207 padEntries(index); 208 return entries[index].updateReading(reading, raw); 209 } 210 }; 211 212 // Store information for threshold sensors and they are not used by VR 213 // sensors. These objects are global singletons, used from a variety of places. 214 inline IPMIStatsTable sdrStatsTable; 215 216 /** 217 * Search ObjectMapper for sensors and update them to subtree. 218 * 219 * The function will search for sensors under either 220 * /xyz/openbmc_project/sensors or /xyz/openbmc_project/extsensors. It will 221 * optionally search VR typed sensors under /xyz/openbmc_project/vr 222 * 223 * @return the updated amount of times any of "sensors" or "extsensors" sensor 224 * paths updated successfully, previous amount if all failed. The "vr" 225 * sensor path is optional, and does not participate in the return value. 226 */ 227 uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree); 228 229 bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap); 230 } // namespace details 231 232 bool getSensorSubtree(SensorSubTree& subtree); 233 234 #ifdef FEATURE_HYBRID_SENSORS 235 ipmi::sensor::IdInfoMap::const_iterator 236 findStaticSensor(const std::string& path); 237 #endif 238 239 struct CmpStr 240 { 241 bool operator()(const char* a, const char* b) const 242 { 243 return std::strcmp(a, b) < 0; 244 } 245 }; 246 247 static constexpr size_t sensorTypeCodes = 0; 248 static constexpr size_t sensorEventTypeCodes = 1; 249 250 enum class SensorTypeCodes : uint8_t 251 { 252 reserved = 0x0, 253 temperature = 0x1, 254 voltage = 0x2, 255 current = 0x3, 256 fan = 0x4, 257 other = 0xB, 258 memory = 0x0c, 259 power_unit = 0x09, 260 buttons = 0x14, 261 watchdog2 = 0x23, 262 }; 263 264 enum class SensorEventTypeCodes : uint8_t 265 { 266 unspecified = 0x00, 267 threshold = 0x01, 268 sensorSpecified = 0x6f 269 }; 270 271 const static boost::container::flat_map< 272 const char*, std::pair<SensorTypeCodes, SensorEventTypeCodes>, CmpStr> 273 sensorTypes{ 274 {{"temperature", std::make_pair(SensorTypeCodes::temperature, 275 SensorEventTypeCodes::threshold)}, 276 {"voltage", std::make_pair(SensorTypeCodes::voltage, 277 SensorEventTypeCodes::threshold)}, 278 {"current", std::make_pair(SensorTypeCodes::current, 279 SensorEventTypeCodes::threshold)}, 280 {"fan_tach", std::make_pair(SensorTypeCodes::fan, 281 SensorEventTypeCodes::threshold)}, 282 {"fan_pwm", std::make_pair(SensorTypeCodes::fan, 283 SensorEventTypeCodes::threshold)}, 284 {"power", std::make_pair(SensorTypeCodes::other, 285 SensorEventTypeCodes::threshold)}, 286 {"memory", std::make_pair(SensorTypeCodes::memory, 287 SensorEventTypeCodes::sensorSpecified)}, 288 {"state", std::make_pair(SensorTypeCodes::power_unit, 289 SensorEventTypeCodes::sensorSpecified)}, 290 {"buttons", std::make_pair(SensorTypeCodes::buttons, 291 SensorEventTypeCodes::sensorSpecified)}, 292 {"watchdog", std::make_pair(SensorTypeCodes::watchdog2, 293 SensorEventTypeCodes::sensorSpecified)}}}; 294 295 std::string getSensorTypeStringFromPath(const std::string& path); 296 297 uint8_t getSensorTypeFromPath(const std::string& path); 298 299 uint16_t getSensorNumberFromPath(const std::string& path); 300 301 uint8_t getSensorEventTypeFromPath(const std::string& path); 302 303 std::string getPathFromSensorNumber(uint16_t sensorNum); 304 305 namespace ipmi 306 { 307 std::map<std::string, std::vector<std::string>> 308 getObjectInterfaces(const char* path); 309 310 std::map<std::string, Value> getEntityManagerProperties(const char* path, 311 const char* interface); 312 313 const std::string* getSensorConfigurationInterface( 314 const std::map<std::string, std::vector<std::string>>& 315 sensorInterfacesResponse); 316 317 void updateIpmiFromAssociation(const std::string& path, 318 const DbusInterfaceMap& sensorMap, 319 uint8_t& entityId, uint8_t& entityInstance); 320 } // namespace ipmi 321