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 class IPMIWriteEntry 213 { 214 private: 215 bool writePermission = false; 216 217 public: 218 bool getWritePermission(void) const 219 { 220 return writePermission; 221 } 222 223 void setWritePermission(bool permission) 224 { 225 writePermission = permission; 226 } 227 }; 228 229 class IPMIWriteTable 230 { 231 private: 232 std::vector<IPMIWriteEntry> entries; 233 234 private: 235 void padEntries(size_t index) 236 { 237 // Pad vector until entries[index] becomes a valid index 238 if (entries.size() <= index) 239 { 240 entries.resize(index + 1); 241 } 242 } 243 244 public: 245 void wipeTable(void) 246 { 247 entries.clear(); 248 } 249 250 bool getWritePermission(size_t index) 251 { 252 padEntries(index); 253 return entries[index].getWritePermission(); 254 } 255 256 void setWritePermission(size_t index, bool permission) 257 { 258 padEntries(index); 259 entries[index].setWritePermission(permission); 260 } 261 }; 262 263 // Store information for threshold sensors and they are not used by VR 264 // sensors. These objects are global singletons, used from a variety of places. 265 inline IPMIStatsTable sdrStatsTable; 266 inline IPMIWriteTable sdrWriteTable; 267 268 /** 269 * Search ObjectMapper for sensors and update them to subtree. 270 * 271 * The function will search for sensors under either 272 * /xyz/openbmc_project/sensors or /xyz/openbmc_project/extsensors. It will 273 * optionally search VR typed sensors under /xyz/openbmc_project/vr 274 * 275 * @return the updated amount of times any of "sensors" or "extsensors" sensor 276 * paths updated successfully, previous amount if all failed. The "vr" 277 * sensor path is optional, and does not participate in the return value. 278 */ 279 uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree); 280 281 bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap); 282 } // namespace details 283 284 bool getSensorSubtree(SensorSubTree& subtree); 285 286 #ifdef FEATURE_HYBRID_SENSORS 287 ipmi::sensor::IdInfoMap::const_iterator 288 findStaticSensor(const std::string& path); 289 #endif 290 291 struct CmpStr 292 { 293 bool operator()(const char* a, const char* b) const 294 { 295 return std::strcmp(a, b) < 0; 296 } 297 }; 298 299 static constexpr size_t sensorTypeCodes = 0; 300 static constexpr size_t sensorEventTypeCodes = 1; 301 302 enum class SensorTypeCodes : uint8_t 303 { 304 reserved = 0x0, 305 temperature = 0x1, 306 voltage = 0x2, 307 current = 0x3, 308 fan = 0x4, 309 other = 0xB, 310 memory = 0x0c, 311 power_unit = 0x09, 312 buttons = 0x14, 313 watchdog2 = 0x23, 314 }; 315 316 enum class SensorEventTypeCodes : uint8_t 317 { 318 unspecified = 0x00, 319 threshold = 0x01, 320 sensorSpecified = 0x6f 321 }; 322 323 const static boost::container::flat_map< 324 const char*, std::pair<SensorTypeCodes, SensorEventTypeCodes>, CmpStr> 325 sensorTypes{ 326 {{"temperature", std::make_pair(SensorTypeCodes::temperature, 327 SensorEventTypeCodes::threshold)}, 328 {"voltage", std::make_pair(SensorTypeCodes::voltage, 329 SensorEventTypeCodes::threshold)}, 330 {"current", std::make_pair(SensorTypeCodes::current, 331 SensorEventTypeCodes::threshold)}, 332 {"fan_tach", std::make_pair(SensorTypeCodes::fan, 333 SensorEventTypeCodes::threshold)}, 334 {"fan_pwm", std::make_pair(SensorTypeCodes::fan, 335 SensorEventTypeCodes::threshold)}, 336 {"power", std::make_pair(SensorTypeCodes::other, 337 SensorEventTypeCodes::threshold)}, 338 {"memory", std::make_pair(SensorTypeCodes::memory, 339 SensorEventTypeCodes::sensorSpecified)}, 340 {"state", std::make_pair(SensorTypeCodes::power_unit, 341 SensorEventTypeCodes::sensorSpecified)}, 342 {"buttons", std::make_pair(SensorTypeCodes::buttons, 343 SensorEventTypeCodes::sensorSpecified)}, 344 {"watchdog", std::make_pair(SensorTypeCodes::watchdog2, 345 SensorEventTypeCodes::sensorSpecified)}}}; 346 347 std::string getSensorTypeStringFromPath(const std::string& path); 348 349 uint8_t getSensorTypeFromPath(const std::string& path); 350 351 uint16_t getSensorNumberFromPath(const std::string& path); 352 353 uint8_t getSensorEventTypeFromPath(const std::string& path); 354 355 std::string getPathFromSensorNumber(uint16_t sensorNum); 356 357 namespace ipmi 358 { 359 std::map<std::string, std::vector<std::string>> 360 getObjectInterfaces(const char* path); 361 362 std::map<std::string, Value> getEntityManagerProperties(const char* path, 363 const char* interface); 364 365 const std::string* getSensorConfigurationInterface( 366 const std::map<std::string, std::vector<std::string>>& 367 sensorInterfacesResponse); 368 369 void updateIpmiFromAssociation(const std::string& path, 370 const DbusInterfaceMap& sensorMap, 371 uint8_t& entityId, uint8_t& entityInstance); 372 } // namespace ipmi 373