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 <optional> 28 #include <phosphor-logging/log.hpp> 29 #include <sdbusplus/bus/match.hpp> 30 #include <string> 31 #include <unordered_set> 32 #include <vector> 33 34 #pragma once 35 36 static constexpr bool debug = false; 37 38 struct CmpStrVersion 39 { 40 bool operator()(std::string a, std::string b) const 41 { 42 return strverscmp(a.c_str(), b.c_str()) < 0; 43 } 44 }; 45 46 using SensorSubTree = boost::container::flat_map< 47 std::string, 48 boost::container::flat_map<std::string, std::vector<std::string>>, 49 CmpStrVersion>; 50 51 using SensorNumMap = boost::bimap<int, std::string>; 52 53 static constexpr uint16_t maxSensorsPerLUN = 255; 54 static constexpr uint16_t maxIPMISensors = (maxSensorsPerLUN * 3); 55 static constexpr uint16_t lun1Sensor0 = 0x100; 56 static constexpr uint16_t lun3Sensor0 = 0x300; 57 static constexpr uint16_t invalidSensorNumber = 0xFFFF; 58 static constexpr uint8_t reservedSensorNumber = 0xFF; 59 60 namespace details 61 { 62 // Enable/disable the logging of stats instrumentation 63 static constexpr bool enableInstrumentation = false; 64 65 class IPMIStatsEntry 66 { 67 private: 68 int numReadings = 0; 69 int numMissings = 0; 70 int numStreakRead = 0; 71 int numStreakMiss = 0; 72 double minValue = 0.0; 73 double maxValue = 0.0; 74 std::string sensorName; 75 76 public: 77 const std::string& getName(void) const 78 { 79 return sensorName; 80 } 81 82 void updateName(std::string_view name) 83 { 84 sensorName = name; 85 } 86 87 // Returns true if this is the first successful reading 88 // This is so the caller can log the coefficients used 89 bool updateReading(double reading, int raw) 90 { 91 if constexpr (!enableInstrumentation) 92 { 93 return false; 94 } 95 96 bool first = ((numReadings == 0) && (numMissings == 0)); 97 98 // Sensors can use "nan" to indicate unavailable reading 99 if (!(std::isfinite(reading))) 100 { 101 // Only show this if beginning a new streak 102 if (numStreakMiss == 0) 103 { 104 std::cerr << "IPMI sensor " << sensorName 105 << ": Missing reading, byte=" << raw 106 << ", Reading counts good=" << numReadings 107 << " miss=" << numMissings 108 << ", Prior good streak=" << numStreakRead << "\n"; 109 } 110 111 numStreakRead = 0; 112 ++numMissings; 113 ++numStreakMiss; 114 115 return first; 116 } 117 118 // Only show this if beginning a new streak and not the first time 119 if ((numStreakRead == 0) && (numReadings != 0)) 120 { 121 std::cerr << "IPMI sensor " << sensorName 122 << ": Recovered reading, value=" << reading 123 << " byte=" << raw 124 << ", Reading counts good=" << numReadings 125 << " miss=" << numMissings 126 << ", Prior miss streak=" << numStreakMiss << "\n"; 127 } 128 129 // Initialize min/max if the first successful reading 130 if (numReadings == 0) 131 { 132 std::cerr << "IPMI sensor " << sensorName 133 << ": First reading, value=" << reading << " byte=" << raw 134 << "\n"; 135 136 minValue = reading; 137 maxValue = reading; 138 } 139 140 numStreakMiss = 0; 141 ++numReadings; 142 ++numStreakRead; 143 144 // Only provide subsequent output if new min/max established 145 if (reading < minValue) 146 { 147 std::cerr << "IPMI sensor " << sensorName 148 << ": Lowest reading, value=" << reading 149 << " byte=" << raw << "\n"; 150 151 minValue = reading; 152 } 153 154 if (reading > maxValue) 155 { 156 std::cerr << "IPMI sensor " << sensorName 157 << ": Highest reading, value=" << reading 158 << " byte=" << raw << "\n"; 159 160 maxValue = reading; 161 } 162 163 return first; 164 } 165 }; 166 167 class IPMIStatsTable 168 { 169 private: 170 std::vector<IPMIStatsEntry> entries; 171 172 private: 173 void padEntries(size_t index) 174 { 175 char hexbuf[16]; 176 177 // Pad vector until entries[index] becomes a valid index 178 while (entries.size() <= index) 179 { 180 // As name not known yet, use human-readable hex as name 181 IPMIStatsEntry newEntry; 182 sprintf(hexbuf, "0x%02zX", entries.size()); 183 newEntry.updateName(hexbuf); 184 185 entries.push_back(std::move(newEntry)); 186 } 187 } 188 189 public: 190 void wipeTable(void) 191 { 192 entries.clear(); 193 } 194 195 const std::string& getName(size_t index) 196 { 197 padEntries(index); 198 return entries[index].getName(); 199 } 200 201 void updateName(size_t index, std::string_view name) 202 { 203 padEntries(index); 204 entries[index].updateName(name); 205 } 206 207 bool updateReading(size_t index, double reading, int raw) 208 { 209 padEntries(index); 210 return entries[index].updateReading(reading, raw); 211 } 212 }; 213 214 class IPMIWriteEntry 215 { 216 private: 217 bool writePermission = false; 218 219 public: 220 bool getWritePermission(void) const 221 { 222 return writePermission; 223 } 224 225 void setWritePermission(bool permission) 226 { 227 writePermission = permission; 228 } 229 }; 230 231 class IPMIWriteTable 232 { 233 private: 234 std::vector<IPMIWriteEntry> entries; 235 236 private: 237 void padEntries(size_t index) 238 { 239 // Pad vector until entries[index] becomes a valid index 240 if (entries.size() <= index) 241 { 242 entries.resize(index + 1); 243 } 244 } 245 246 public: 247 void wipeTable(void) 248 { 249 entries.clear(); 250 } 251 252 bool getWritePermission(size_t index) 253 { 254 padEntries(index); 255 return entries[index].getWritePermission(); 256 } 257 258 void setWritePermission(size_t index, bool permission) 259 { 260 padEntries(index); 261 entries[index].setWritePermission(permission); 262 } 263 }; 264 265 // Store information for threshold sensors and they are not used by VR 266 // sensors. These objects are global singletons, used from a variety of places. 267 inline IPMIStatsTable sdrStatsTable; 268 inline IPMIWriteTable sdrWriteTable; 269 270 /** 271 * Search ObjectMapper for sensors and update them to subtree. 272 * 273 * The function will search for sensors under either 274 * /xyz/openbmc_project/sensors or /xyz/openbmc_project/extsensors. It will 275 * optionally search VR typed sensors under /xyz/openbmc_project/vr 276 * 277 * @return the updated amount of times any of "sensors" or "extsensors" sensor 278 * paths updated successfully, previous amount if all failed. The "vr" 279 * sensor path is optional, and does not participate in the return value. 280 */ 281 uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree); 282 283 bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap); 284 } // namespace details 285 286 bool getSensorSubtree(SensorSubTree& subtree); 287 288 #ifdef FEATURE_HYBRID_SENSORS 289 ipmi::sensor::IdInfoMap::const_iterator 290 findStaticSensor(const std::string& path); 291 #endif 292 293 struct CmpStr 294 { 295 bool operator()(const char* a, const char* b) const 296 { 297 return std::strcmp(a, b) < 0; 298 } 299 }; 300 301 static constexpr size_t sensorTypeCodes = 0; 302 static constexpr size_t sensorEventTypeCodes = 1; 303 304 enum class SensorTypeCodes : uint8_t 305 { 306 reserved = 0x00, 307 temperature = 0x01, 308 voltage = 0x02, 309 current = 0x03, 310 fan = 0x04, 311 physical_security = 0x5, 312 processor = 0x07, 313 power_unit = 0x09, 314 other = 0x0b, 315 memory = 0x0c, 316 buttons = 0x14, 317 watchdog2 = 0x23, 318 entity = 0x25, 319 }; 320 321 enum class SensorEventTypeCodes : uint8_t 322 { 323 unspecified = 0x00, 324 threshold = 0x01, 325 sensorSpecified = 0x6f 326 }; 327 328 const static boost::container::flat_map< 329 const char*, std::pair<SensorTypeCodes, SensorEventTypeCodes>, CmpStr> 330 sensorTypes{ 331 {{"temperature", std::make_pair(SensorTypeCodes::temperature, 332 SensorEventTypeCodes::threshold)}, 333 {"voltage", std::make_pair(SensorTypeCodes::voltage, 334 SensorEventTypeCodes::threshold)}, 335 {"current", std::make_pair(SensorTypeCodes::current, 336 SensorEventTypeCodes::threshold)}, 337 {"fan_tach", std::make_pair(SensorTypeCodes::fan, 338 SensorEventTypeCodes::threshold)}, 339 {"fan_pwm", std::make_pair(SensorTypeCodes::fan, 340 SensorEventTypeCodes::threshold)}, 341 {"intrusion", std::make_pair(SensorTypeCodes::physical_security, 342 SensorEventTypeCodes::sensorSpecified)}, 343 {"processor", std::make_pair(SensorTypeCodes::processor, 344 SensorEventTypeCodes::sensorSpecified)}, 345 {"power", std::make_pair(SensorTypeCodes::other, 346 SensorEventTypeCodes::threshold)}, 347 {"memory", std::make_pair(SensorTypeCodes::memory, 348 SensorEventTypeCodes::sensorSpecified)}, 349 {"state", std::make_pair(SensorTypeCodes::power_unit, 350 SensorEventTypeCodes::sensorSpecified)}, 351 {"buttons", std::make_pair(SensorTypeCodes::buttons, 352 SensorEventTypeCodes::sensorSpecified)}, 353 {"watchdog", std::make_pair(SensorTypeCodes::watchdog2, 354 SensorEventTypeCodes::sensorSpecified)}, 355 {"entity", std::make_pair(SensorTypeCodes::entity, 356 SensorEventTypeCodes::sensorSpecified)}}}; 357 std::string getSensorTypeStringFromPath(const std::string& path); 358 359 uint8_t getSensorTypeFromPath(const std::string& path); 360 361 uint16_t getSensorNumberFromPath(const std::string& path); 362 363 uint8_t getSensorEventTypeFromPath(const std::string& path); 364 365 std::string getPathFromSensorNumber(uint16_t sensorNum); 366 367 namespace ipmi 368 { 369 std::map<std::string, std::vector<std::string>> 370 getObjectInterfaces(const char* path); 371 372 std::map<std::string, Value> getEntityManagerProperties(const char* path, 373 const char* interface); 374 375 std::optional<std::unordered_set<std::string>>& 376 getIpmiDecoratorPaths(const std::optional<ipmi::Context::ptr>& ctx); 377 378 const std::string* getSensorConfigurationInterface( 379 const std::map<std::string, std::vector<std::string>>& 380 sensorInterfacesResponse); 381 382 void updateIpmiFromAssociation( 383 const std::string& path, 384 const std::unordered_set<std::string>& ipmiDecoratorPaths, 385 const DbusInterfaceMap& sensorMap, uint8_t& entityId, 386 uint8_t& entityInstance); 387 } // namespace ipmi 388