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