1 /* 2 // Copyright (c) 2017 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 "dbus-sdr/sensorcommands.hpp" 18 19 #include "dbus-sdr/sdrutils.hpp" 20 #include "dbus-sdr/sensorutils.hpp" 21 #include "dbus-sdr/storagecommands.hpp" 22 23 #include <algorithm> 24 #include <array> 25 #include <boost/algorithm/string.hpp> 26 #include <boost/container/flat_map.hpp> 27 #include <chrono> 28 #include <cmath> 29 #include <cstring> 30 #include <iostream> 31 #include <ipmid/api.hpp> 32 #include <ipmid/types.hpp> 33 #include <ipmid/utils.hpp> 34 #include <map> 35 #include <memory> 36 #include <optional> 37 #include <phosphor-logging/log.hpp> 38 #include <sdbusplus/bus.hpp> 39 #include <stdexcept> 40 #include <string> 41 #include <utility> 42 #include <variant> 43 44 namespace ipmi 45 { 46 static constexpr int sensorMapUpdatePeriod = 10; 47 static constexpr int sensorMapSdrUpdatePeriod = 60; 48 49 constexpr size_t maxSDRTotalSize = 50 76; // Largest SDR Record Size (type 01) + SDR Overheader Size 51 constexpr static const uint32_t noTimestamp = 0xFFFFFFFF; 52 53 static uint16_t sdrReservationID; 54 static uint32_t sdrLastAdd = noTimestamp; 55 static uint32_t sdrLastRemove = noTimestamp; 56 static constexpr size_t lastRecordIndex = 0xFFFF; 57 static constexpr int GENERAL_ERROR = -1; 58 59 static boost::container::flat_map<std::string, ObjectValueTree> SensorCache; 60 61 // Specify the comparison required to sort and find char* map objects 62 struct CmpStr 63 { 64 bool operator()(const char* a, const char* b) const 65 { 66 return std::strcmp(a, b) < 0; 67 } 68 }; 69 const static boost::container::flat_map<const char*, SensorUnits, CmpStr> 70 sensorUnits{{{"temperature", SensorUnits::degreesC}, 71 {"voltage", SensorUnits::volts}, 72 {"current", SensorUnits::amps}, 73 {"fan_tach", SensorUnits::rpm}, 74 {"power", SensorUnits::watts}}}; 75 76 void registerSensorFunctions() __attribute__((constructor)); 77 78 static sdbusplus::bus::match::match sensorAdded( 79 *getSdBus(), 80 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/" 81 "sensors/'", 82 [](sdbusplus::message::message& m) { 83 getSensorTree().clear(); 84 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>( 85 std::chrono::system_clock::now().time_since_epoch()) 86 .count(); 87 }); 88 89 static sdbusplus::bus::match::match sensorRemoved( 90 *getSdBus(), 91 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/" 92 "sensors/'", 93 [](sdbusplus::message::message& m) { 94 getSensorTree().clear(); 95 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>( 96 std::chrono::system_clock::now().time_since_epoch()) 97 .count(); 98 }); 99 100 // this keeps track of deassertions for sensor event status command. A 101 // deasertion can only happen if an assertion was seen first. 102 static boost::container::flat_map< 103 std::string, boost::container::flat_map<std::string, std::optional<bool>>> 104 thresholdDeassertMap; 105 106 static sdbusplus::bus::match::match thresholdChanged( 107 *getSdBus(), 108 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus." 109 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'", 110 [](sdbusplus::message::message& m) { 111 boost::container::flat_map<std::string, std::variant<bool, double>> 112 values; 113 m.read(std::string(), values); 114 115 auto findAssert = 116 std::find_if(values.begin(), values.end(), [](const auto& pair) { 117 return pair.first.find("Alarm") != std::string::npos; 118 }); 119 if (findAssert != values.end()) 120 { 121 auto ptr = std::get_if<bool>(&(findAssert->second)); 122 if (ptr == nullptr) 123 { 124 phosphor::logging::log<phosphor::logging::level::ERR>( 125 "thresholdChanged: Assert non bool"); 126 return; 127 } 128 if (*ptr) 129 { 130 phosphor::logging::log<phosphor::logging::level::INFO>( 131 "thresholdChanged: Assert", 132 phosphor::logging::entry("SENSOR=%s", m.get_path())); 133 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr; 134 } 135 else 136 { 137 auto& value = 138 thresholdDeassertMap[m.get_path()][findAssert->first]; 139 if (value) 140 { 141 phosphor::logging::log<phosphor::logging::level::INFO>( 142 "thresholdChanged: deassert", 143 phosphor::logging::entry("SENSOR=%s", m.get_path())); 144 value = *ptr; 145 } 146 } 147 } 148 }); 149 150 static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max, 151 double& min) 152 { 153 max = 127; 154 min = -128; 155 156 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value"); 157 auto critical = 158 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 159 auto warning = 160 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 161 162 if (sensorObject != sensorMap.end()) 163 { 164 auto maxMap = sensorObject->second.find("MaxValue"); 165 auto minMap = sensorObject->second.find("MinValue"); 166 167 if (maxMap != sensorObject->second.end()) 168 { 169 max = std::visit(VariantToDoubleVisitor(), maxMap->second); 170 } 171 if (minMap != sensorObject->second.end()) 172 { 173 min = std::visit(VariantToDoubleVisitor(), minMap->second); 174 } 175 } 176 if (critical != sensorMap.end()) 177 { 178 auto lower = critical->second.find("CriticalLow"); 179 auto upper = critical->second.find("CriticalHigh"); 180 if (lower != critical->second.end()) 181 { 182 double value = std::visit(VariantToDoubleVisitor(), lower->second); 183 min = std::min(value, min); 184 } 185 if (upper != critical->second.end()) 186 { 187 double value = std::visit(VariantToDoubleVisitor(), upper->second); 188 max = std::max(value, max); 189 } 190 } 191 if (warning != sensorMap.end()) 192 { 193 194 auto lower = warning->second.find("WarningLow"); 195 auto upper = warning->second.find("WarningHigh"); 196 if (lower != warning->second.end()) 197 { 198 double value = std::visit(VariantToDoubleVisitor(), lower->second); 199 min = std::min(value, min); 200 } 201 if (upper != warning->second.end()) 202 { 203 double value = std::visit(VariantToDoubleVisitor(), upper->second); 204 max = std::max(value, max); 205 } 206 } 207 } 208 209 static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection, 210 std::string sensorPath, DbusInterfaceMap& sensorMap, 211 int updatePeriod = sensorMapUpdatePeriod) 212 { 213 static boost::container::flat_map< 214 std::string, std::chrono::time_point<std::chrono::steady_clock>> 215 updateTimeMap; 216 217 auto updateFind = updateTimeMap.find(sensorConnection); 218 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>(); 219 if (updateFind != updateTimeMap.end()) 220 { 221 lastUpdate = updateFind->second; 222 } 223 224 auto now = std::chrono::steady_clock::now(); 225 226 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate) 227 .count() > updatePeriod) 228 { 229 ObjectValueTree managedObjects; 230 boost::system::error_code ec = getManagedObjects( 231 ctx, sensorConnection.c_str(), "/", managedObjects); 232 if (ec) 233 { 234 phosphor::logging::log<phosphor::logging::level::ERR>( 235 "GetMangagedObjects for getSensorMap failed", 236 phosphor::logging::entry("ERROR=%s", ec.message().c_str())); 237 238 return false; 239 } 240 241 SensorCache[sensorConnection] = managedObjects; 242 // Update time after finish building the map which allow the 243 // data to be cached for updatePeriod plus the build time. 244 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now(); 245 } 246 auto connection = SensorCache.find(sensorConnection); 247 if (connection == SensorCache.end()) 248 { 249 return false; 250 } 251 auto path = connection->second.find(sensorPath); 252 if (path == connection->second.end()) 253 { 254 return false; 255 } 256 sensorMap = path->second; 257 258 return true; 259 } 260 261 ipmi::RspType<> ipmiSenPlatformEvent(uint8_t generatorID, uint8_t evmRev, 262 uint8_t sensorType, uint8_t sensorNum, 263 uint8_t eventType, uint8_t eventData1, 264 std::optional<uint8_t> eventData2, 265 std::optional<uint8_t> eventData3) 266 { 267 return ipmi::responseSuccess(); 268 } 269 270 ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx, 271 uint8_t sensorNumber, uint8_t operation, 272 uint8_t reading, uint15_t assertOffset, 273 bool resvd1, uint15_t deassertOffset, 274 bool resvd2, uint8_t eventData1, 275 uint8_t eventData2, uint8_t eventData3) 276 { 277 std::string connection; 278 std::string path; 279 ipmi::Cc status = getSensorConnection(ctx, sensorNumber, connection, path); 280 if (status) 281 { 282 return ipmi::response(status); 283 } 284 285 DbusInterfaceMap sensorMap; 286 if (!getSensorMap(ctx, connection, path, sensorMap)) 287 { 288 return ipmi::responseResponseError(); 289 } 290 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value"); 291 292 if (sensorObject == sensorMap.end() || 293 sensorObject->second.find("Value") == sensorObject->second.end()) 294 { 295 return ipmi::responseResponseError(); 296 } 297 298 double max = 0; 299 double min = 0; 300 getSensorMaxMin(sensorMap, max, min); 301 302 int16_t mValue = 0; 303 int16_t bValue = 0; 304 int8_t rExp = 0; 305 int8_t bExp = 0; 306 bool bSigned = false; 307 308 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 309 { 310 return ipmi::responseResponseError(); 311 } 312 313 double value = bSigned ? ((int8_t)reading) : reading; 314 315 value *= ((double)mValue); 316 value += ((double)bValue) * std::pow(10.0, bExp); 317 value *= std::pow(10.0, rExp); 318 319 if constexpr (debug) 320 { 321 phosphor::logging::log<phosphor::logging::level::INFO>( 322 "IPMI SET_SENSOR", 323 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber), 324 phosphor::logging::entry("BYTE=%u", (unsigned int)reading), 325 phosphor::logging::entry("VALUE=%f", value)); 326 } 327 328 try 329 { 330 setDbusProperty(ctx, connection, path, 331 "xyz.openbmc_project.Sensor.Value", "Value", 332 ipmi::Value(value)); 333 } 334 // setDbusProperty intended to resolve dbus exception/rc within the 335 // function but failed to achieve that. Catch SdBusError in the ipmi 336 // callback functions for now (e.g. ipmiSetSensorReading). 337 catch (const sdbusplus::exception::SdBusError& e) 338 { 339 using namespace phosphor::logging; 340 log<level::ERR>( 341 "Failed to set property", entry("PROPERTY=%s", "Value"), 342 entry("PATH=%s", path.c_str()), 343 entry("INTERFACE=%s", "xyz.openbmc_project.Sensor.Value"), 344 entry("WHAT=%s", e.what())); 345 return ipmi::responseResponseError(); 346 } 347 return ipmi::responseSuccess(); 348 } 349 350 ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>> 351 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum) 352 { 353 std::string connection; 354 std::string path; 355 356 auto status = getSensorConnection(ctx, sensnum, connection, path); 357 if (status) 358 { 359 return ipmi::response(status); 360 } 361 362 DbusInterfaceMap sensorMap; 363 if (!getSensorMap(ctx, connection, path, sensorMap)) 364 { 365 return ipmi::responseResponseError(); 366 } 367 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value"); 368 369 if (sensorObject == sensorMap.end() || 370 sensorObject->second.find("Value") == sensorObject->second.end()) 371 { 372 return ipmi::responseResponseError(); 373 } 374 auto& valueVariant = sensorObject->second["Value"]; 375 double reading = std::visit(VariantToDoubleVisitor(), valueVariant); 376 377 double max = 0; 378 double min = 0; 379 getSensorMaxMin(sensorMap, max, min); 380 381 int16_t mValue = 0; 382 int16_t bValue = 0; 383 int8_t rExp = 0; 384 int8_t bExp = 0; 385 bool bSigned = false; 386 387 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 388 { 389 return ipmi::responseResponseError(); 390 } 391 392 uint8_t value = 393 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned); 394 uint8_t operation = 395 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable); 396 operation |= 397 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable); 398 bool notReading = std::isnan(reading); 399 400 if (!notReading) 401 { 402 auto availableObject = 403 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability"); 404 if (availableObject != sensorMap.end()) 405 { 406 auto findAvailable = availableObject->second.find("Available"); 407 if (findAvailable != availableObject->second.end()) 408 { 409 bool* available = std::get_if<bool>(&(findAvailable->second)); 410 if (available && !(*available)) 411 { 412 notReading = true; 413 } 414 } 415 } 416 } 417 418 if (notReading) 419 { 420 operation |= static_cast<uint8_t>( 421 IPMISensorReadingByte2::readingStateUnavailable); 422 } 423 424 if constexpr (details::enableInstrumentation) 425 { 426 int byteValue; 427 if (bSigned) 428 { 429 byteValue = static_cast<int>(static_cast<int8_t>(value)); 430 } 431 else 432 { 433 byteValue = static_cast<int>(static_cast<uint8_t>(value)); 434 } 435 436 // Keep stats on the reading just obtained, even if it is "NaN" 437 if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue)) 438 { 439 // This is the first reading, show the coefficients 440 double step = (max - min) / 255.0; 441 std::cerr << "IPMI sensor " 442 << details::sdrStatsTable.getName(sensnum) 443 << ": Range min=" << min << " max=" << max 444 << ", step=" << step 445 << ", Coefficients mValue=" << static_cast<int>(mValue) 446 << " rExp=" << static_cast<int>(rExp) 447 << " bValue=" << static_cast<int>(bValue) 448 << " bExp=" << static_cast<int>(bExp) 449 << " bSigned=" << static_cast<int>(bSigned) << "\n"; 450 } 451 } 452 453 uint8_t thresholds = 0; 454 455 auto warningObject = 456 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 457 if (warningObject != sensorMap.end()) 458 { 459 auto alarmHigh = warningObject->second.find("WarningAlarmHigh"); 460 auto alarmLow = warningObject->second.find("WarningAlarmLow"); 461 if (alarmHigh != warningObject->second.end()) 462 { 463 if (std::get<bool>(alarmHigh->second)) 464 { 465 thresholds |= static_cast<uint8_t>( 466 IPMISensorReadingByte3::upperNonCritical); 467 } 468 } 469 if (alarmLow != warningObject->second.end()) 470 { 471 if (std::get<bool>(alarmLow->second)) 472 { 473 thresholds |= static_cast<uint8_t>( 474 IPMISensorReadingByte3::lowerNonCritical); 475 } 476 } 477 } 478 479 auto criticalObject = 480 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 481 if (criticalObject != sensorMap.end()) 482 { 483 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh"); 484 auto alarmLow = criticalObject->second.find("CriticalAlarmLow"); 485 if (alarmHigh != criticalObject->second.end()) 486 { 487 if (std::get<bool>(alarmHigh->second)) 488 { 489 thresholds |= 490 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical); 491 } 492 } 493 if (alarmLow != criticalObject->second.end()) 494 { 495 if (std::get<bool>(alarmLow->second)) 496 { 497 thresholds |= 498 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical); 499 } 500 } 501 } 502 503 // no discrete as of today so optional byte is never returned 504 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt); 505 } 506 507 /** @brief implements the Set Sensor threshold command 508 * @param sensorNumber - sensor number 509 * @param lowerNonCriticalThreshMask 510 * @param lowerCriticalThreshMask 511 * @param lowerNonRecovThreshMask 512 * @param upperNonCriticalThreshMask 513 * @param upperCriticalThreshMask 514 * @param upperNonRecovThreshMask 515 * @param reserved 516 * @param lowerNonCritical - lower non-critical threshold 517 * @param lowerCritical - Lower critical threshold 518 * @param lowerNonRecoverable - Lower non recovarable threshold 519 * @param upperNonCritical - Upper non-critical threshold 520 * @param upperCritical - Upper critical 521 * @param upperNonRecoverable - Upper Non-recoverable 522 * 523 * @returns IPMI completion code 524 */ 525 ipmi::RspType<> ipmiSenSetSensorThresholds( 526 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask, 527 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask, 528 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask, 529 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical, 530 uint8_t lowerCritical, uint8_t lowerNonRecoverable, 531 uint8_t upperNonCritical, uint8_t upperCritical, 532 uint8_t upperNonRecoverable) 533 { 534 if (reserved) 535 { 536 return ipmi::responseInvalidFieldRequest(); 537 } 538 539 // lower nc and upper nc not suppported on any sensor 540 if (lowerNonRecovThreshMask || upperNonRecovThreshMask) 541 { 542 return ipmi::responseInvalidFieldRequest(); 543 } 544 545 // if none of the threshold mask are set, nothing to do 546 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask | 547 lowerNonRecovThreshMask | upperNonCriticalThreshMask | 548 upperCriticalThreshMask | upperNonRecovThreshMask)) 549 { 550 return ipmi::responseSuccess(); 551 } 552 553 std::string connection; 554 std::string path; 555 556 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path); 557 if (status) 558 { 559 return ipmi::response(status); 560 } 561 DbusInterfaceMap sensorMap; 562 if (!getSensorMap(ctx, connection, path, sensorMap)) 563 { 564 return ipmi::responseResponseError(); 565 } 566 567 double max = 0; 568 double min = 0; 569 getSensorMaxMin(sensorMap, max, min); 570 571 int16_t mValue = 0; 572 int16_t bValue = 0; 573 int8_t rExp = 0; 574 int8_t bExp = 0; 575 bool bSigned = false; 576 577 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 578 { 579 return ipmi::responseResponseError(); 580 } 581 582 // store a vector of property name, value to set, and interface 583 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet; 584 585 // define the indexes of the tuple 586 constexpr uint8_t propertyName = 0; 587 constexpr uint8_t thresholdValue = 1; 588 constexpr uint8_t interface = 2; 589 // verifiy all needed fields are present 590 if (lowerCriticalThreshMask || upperCriticalThreshMask) 591 { 592 auto findThreshold = 593 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 594 if (findThreshold == sensorMap.end()) 595 { 596 return ipmi::responseInvalidFieldRequest(); 597 } 598 if (lowerCriticalThreshMask) 599 { 600 auto findLower = findThreshold->second.find("CriticalLow"); 601 if (findLower == findThreshold->second.end()) 602 { 603 return ipmi::responseInvalidFieldRequest(); 604 } 605 thresholdsToSet.emplace_back("CriticalLow", lowerCritical, 606 findThreshold->first); 607 } 608 if (upperCriticalThreshMask) 609 { 610 auto findUpper = findThreshold->second.find("CriticalHigh"); 611 if (findUpper == findThreshold->second.end()) 612 { 613 return ipmi::responseInvalidFieldRequest(); 614 } 615 thresholdsToSet.emplace_back("CriticalHigh", upperCritical, 616 findThreshold->first); 617 } 618 } 619 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask) 620 { 621 auto findThreshold = 622 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 623 if (findThreshold == sensorMap.end()) 624 { 625 return ipmi::responseInvalidFieldRequest(); 626 } 627 if (lowerNonCriticalThreshMask) 628 { 629 auto findLower = findThreshold->second.find("WarningLow"); 630 if (findLower == findThreshold->second.end()) 631 { 632 return ipmi::responseInvalidFieldRequest(); 633 } 634 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical, 635 findThreshold->first); 636 } 637 if (upperNonCriticalThreshMask) 638 { 639 auto findUpper = findThreshold->second.find("WarningHigh"); 640 if (findUpper == findThreshold->second.end()) 641 { 642 return ipmi::responseInvalidFieldRequest(); 643 } 644 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical, 645 findThreshold->first); 646 } 647 } 648 for (const auto& property : thresholdsToSet) 649 { 650 // from section 36.3 in the IPMI Spec, assume all linear 651 double valueToSet = ((mValue * std::get<thresholdValue>(property)) + 652 (bValue * std::pow(10.0, bExp))) * 653 std::pow(10.0, rExp); 654 setDbusProperty( 655 *getSdBus(), connection, path, std::get<interface>(property), 656 std::get<propertyName>(property), ipmi::Value(valueToSet)); 657 } 658 return ipmi::responseSuccess(); 659 } 660 661 IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap) 662 { 663 IPMIThresholds resp; 664 auto warningInterface = 665 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 666 auto criticalInterface = 667 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 668 669 if ((warningInterface != sensorMap.end()) || 670 (criticalInterface != sensorMap.end())) 671 { 672 auto sensorPair = sensorMap.find("xyz.openbmc_project.Sensor.Value"); 673 674 if (sensorPair == sensorMap.end()) 675 { 676 // should not have been able to find a sensor not implementing 677 // the sensor object 678 throw std::runtime_error("Invalid sensor map"); 679 } 680 681 double max = 0; 682 double min = 0; 683 getSensorMaxMin(sensorMap, max, min); 684 685 int16_t mValue = 0; 686 int16_t bValue = 0; 687 int8_t rExp = 0; 688 int8_t bExp = 0; 689 bool bSigned = false; 690 691 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 692 { 693 throw std::runtime_error("Invalid sensor atrributes"); 694 } 695 if (warningInterface != sensorMap.end()) 696 { 697 auto& warningMap = warningInterface->second; 698 699 auto warningHigh = warningMap.find("WarningHigh"); 700 auto warningLow = warningMap.find("WarningLow"); 701 702 if (warningHigh != warningMap.end()) 703 { 704 705 double value = 706 std::visit(VariantToDoubleVisitor(), warningHigh->second); 707 resp.warningHigh = scaleIPMIValueFromDouble( 708 value, mValue, rExp, bValue, bExp, bSigned); 709 } 710 if (warningLow != warningMap.end()) 711 { 712 double value = 713 std::visit(VariantToDoubleVisitor(), warningLow->second); 714 resp.warningLow = scaleIPMIValueFromDouble( 715 value, mValue, rExp, bValue, bExp, bSigned); 716 } 717 } 718 if (criticalInterface != sensorMap.end()) 719 { 720 auto& criticalMap = criticalInterface->second; 721 722 auto criticalHigh = criticalMap.find("CriticalHigh"); 723 auto criticalLow = criticalMap.find("CriticalLow"); 724 725 if (criticalHigh != criticalMap.end()) 726 { 727 double value = 728 std::visit(VariantToDoubleVisitor(), criticalHigh->second); 729 resp.criticalHigh = scaleIPMIValueFromDouble( 730 value, mValue, rExp, bValue, bExp, bSigned); 731 } 732 if (criticalLow != criticalMap.end()) 733 { 734 double value = 735 std::visit(VariantToDoubleVisitor(), criticalLow->second); 736 resp.criticalLow = scaleIPMIValueFromDouble( 737 value, mValue, rExp, bValue, bExp, bSigned); 738 } 739 } 740 } 741 return resp; 742 } 743 744 ipmi::RspType<uint8_t, // readable 745 uint8_t, // lowerNCrit 746 uint8_t, // lowerCrit 747 uint8_t, // lowerNrecoverable 748 uint8_t, // upperNC 749 uint8_t, // upperCrit 750 uint8_t> // upperNRecoverable 751 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber) 752 { 753 std::string connection; 754 std::string path; 755 756 auto status = getSensorConnection(ctx, sensorNumber, connection, path); 757 if (status) 758 { 759 return ipmi::response(status); 760 } 761 762 DbusInterfaceMap sensorMap; 763 if (!getSensorMap(ctx, connection, path, sensorMap)) 764 { 765 return ipmi::responseResponseError(); 766 } 767 768 IPMIThresholds thresholdData; 769 try 770 { 771 thresholdData = getIPMIThresholds(sensorMap); 772 } 773 catch (std::exception&) 774 { 775 return ipmi::responseResponseError(); 776 } 777 778 uint8_t readable = 0; 779 uint8_t lowerNC = 0; 780 uint8_t lowerCritical = 0; 781 uint8_t lowerNonRecoverable = 0; 782 uint8_t upperNC = 0; 783 uint8_t upperCritical = 0; 784 uint8_t upperNonRecoverable = 0; 785 786 if (thresholdData.warningHigh) 787 { 788 readable |= 789 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical); 790 upperNC = *thresholdData.warningHigh; 791 } 792 if (thresholdData.warningLow) 793 { 794 readable |= 795 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical); 796 lowerNC = *thresholdData.warningLow; 797 } 798 799 if (thresholdData.criticalHigh) 800 { 801 readable |= 802 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical); 803 upperCritical = *thresholdData.criticalHigh; 804 } 805 if (thresholdData.criticalLow) 806 { 807 readable |= 808 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical); 809 lowerCritical = *thresholdData.criticalLow; 810 } 811 812 return ipmi::responseSuccess(readable, lowerNC, lowerCritical, 813 lowerNonRecoverable, upperNC, upperCritical, 814 upperNonRecoverable); 815 } 816 817 /** @brief implements the get Sensor event enable command 818 * @param sensorNumber - sensor number 819 * 820 * @returns IPMI completion code plus response data 821 * - enabled - Sensor Event messages 822 * - assertionEnabledLsb - Assertion event messages 823 * - assertionEnabledMsb - Assertion event messages 824 * - deassertionEnabledLsb - Deassertion event messages 825 * - deassertionEnabledMsb - Deassertion event messages 826 */ 827 828 ipmi::RspType<uint8_t, // enabled 829 uint8_t, // assertionEnabledLsb 830 uint8_t, // assertionEnabledMsb 831 uint8_t, // deassertionEnabledLsb 832 uint8_t> // deassertionEnabledMsb 833 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum) 834 { 835 std::string connection; 836 std::string path; 837 838 uint8_t enabled = 0; 839 uint8_t assertionEnabledLsb = 0; 840 uint8_t assertionEnabledMsb = 0; 841 uint8_t deassertionEnabledLsb = 0; 842 uint8_t deassertionEnabledMsb = 0; 843 844 auto status = getSensorConnection(ctx, sensorNum, connection, path); 845 if (status) 846 { 847 return ipmi::response(status); 848 } 849 850 DbusInterfaceMap sensorMap; 851 if (!getSensorMap(ctx, connection, path, sensorMap)) 852 { 853 return ipmi::responseResponseError(); 854 } 855 856 auto warningInterface = 857 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 858 auto criticalInterface = 859 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 860 if ((warningInterface != sensorMap.end()) || 861 (criticalInterface != sensorMap.end())) 862 { 863 enabled = static_cast<uint8_t>( 864 IPMISensorEventEnableByte2::sensorScanningEnable); 865 if (warningInterface != sensorMap.end()) 866 { 867 auto& warningMap = warningInterface->second; 868 869 auto warningHigh = warningMap.find("WarningHigh"); 870 auto warningLow = warningMap.find("WarningLow"); 871 if (warningHigh != warningMap.end()) 872 { 873 assertionEnabledLsb |= static_cast<uint8_t>( 874 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh); 875 deassertionEnabledLsb |= static_cast<uint8_t>( 876 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow); 877 } 878 if (warningLow != warningMap.end()) 879 { 880 assertionEnabledLsb |= static_cast<uint8_t>( 881 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow); 882 deassertionEnabledLsb |= static_cast<uint8_t>( 883 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh); 884 } 885 } 886 if (criticalInterface != sensorMap.end()) 887 { 888 auto& criticalMap = criticalInterface->second; 889 890 auto criticalHigh = criticalMap.find("CriticalHigh"); 891 auto criticalLow = criticalMap.find("CriticalLow"); 892 893 if (criticalHigh != criticalMap.end()) 894 { 895 assertionEnabledMsb |= static_cast<uint8_t>( 896 IPMISensorEventEnableThresholds::upperCriticalGoingHigh); 897 deassertionEnabledMsb |= static_cast<uint8_t>( 898 IPMISensorEventEnableThresholds::upperCriticalGoingLow); 899 } 900 if (criticalLow != criticalMap.end()) 901 { 902 assertionEnabledLsb |= static_cast<uint8_t>( 903 IPMISensorEventEnableThresholds::lowerCriticalGoingLow); 904 deassertionEnabledLsb |= static_cast<uint8_t>( 905 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh); 906 } 907 } 908 } 909 910 return ipmi::responseSuccess(enabled, assertionEnabledLsb, 911 assertionEnabledMsb, deassertionEnabledLsb, 912 deassertionEnabledMsb); 913 } 914 915 /** @brief implements the get Sensor event status command 916 * @param sensorNumber - sensor number, FFh = reserved 917 * 918 * @returns IPMI completion code plus response data 919 * - sensorEventStatus - Sensor Event messages state 920 * - assertions - Assertion event messages 921 * - deassertions - Deassertion event messages 922 */ 923 ipmi::RspType<uint8_t, // sensorEventStatus 924 std::bitset<16>, // assertions 925 std::bitset<16> // deassertion 926 > 927 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum) 928 { 929 if (sensorNum == reservedSensorNumber) 930 { 931 return ipmi::responseInvalidFieldRequest(); 932 } 933 934 std::string connection; 935 std::string path; 936 auto status = getSensorConnection(ctx, sensorNum, connection, path); 937 if (status) 938 { 939 phosphor::logging::log<phosphor::logging::level::ERR>( 940 "ipmiSenGetSensorEventStatus: Sensor connection Error", 941 phosphor::logging::entry("SENSOR=%d", sensorNum)); 942 return ipmi::response(status); 943 } 944 945 DbusInterfaceMap sensorMap; 946 if (!getSensorMap(ctx, connection, path, sensorMap)) 947 { 948 phosphor::logging::log<phosphor::logging::level::ERR>( 949 "ipmiSenGetSensorEventStatus: Sensor Mapping Error", 950 phosphor::logging::entry("SENSOR=%s", path.c_str())); 951 return ipmi::responseResponseError(); 952 } 953 auto warningInterface = 954 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 955 auto criticalInterface = 956 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 957 958 uint8_t sensorEventStatus = 959 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable); 960 961 std::optional<bool> criticalDeassertHigh = 962 thresholdDeassertMap[path]["CriticalAlarmHigh"]; 963 std::optional<bool> criticalDeassertLow = 964 thresholdDeassertMap[path]["CriticalAlarmLow"]; 965 std::optional<bool> warningDeassertHigh = 966 thresholdDeassertMap[path]["WarningAlarmHigh"]; 967 std::optional<bool> warningDeassertLow = 968 thresholdDeassertMap[path]["WarningAlarmLow"]; 969 970 std::bitset<16> assertions = 0; 971 std::bitset<16> deassertions = 0; 972 973 if (criticalDeassertHigh && !*criticalDeassertHigh) 974 { 975 deassertions.set(static_cast<size_t>( 976 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh)); 977 } 978 if (criticalDeassertLow && !*criticalDeassertLow) 979 { 980 deassertions.set(static_cast<size_t>( 981 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow)); 982 } 983 if (warningDeassertHigh && !*warningDeassertHigh) 984 { 985 deassertions.set(static_cast<size_t>( 986 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh)); 987 } 988 if (warningDeassertLow && !*warningDeassertLow) 989 { 990 deassertions.set(static_cast<size_t>( 991 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh)); 992 } 993 if ((warningInterface != sensorMap.end()) || 994 (criticalInterface != sensorMap.end())) 995 { 996 sensorEventStatus = static_cast<size_t>( 997 IPMISensorEventEnableByte2::eventMessagesEnable); 998 if (warningInterface != sensorMap.end()) 999 { 1000 auto& warningMap = warningInterface->second; 1001 1002 auto warningHigh = warningMap.find("WarningAlarmHigh"); 1003 auto warningLow = warningMap.find("WarningAlarmLow"); 1004 auto warningHighAlarm = false; 1005 auto warningLowAlarm = false; 1006 1007 if (warningHigh != warningMap.end()) 1008 { 1009 warningHighAlarm = std::get<bool>(warningHigh->second); 1010 } 1011 if (warningLow != warningMap.end()) 1012 { 1013 warningLowAlarm = std::get<bool>(warningLow->second); 1014 } 1015 if (warningHighAlarm) 1016 { 1017 assertions.set( 1018 static_cast<size_t>(IPMIGetSensorEventEnableThresholds:: 1019 upperNonCriticalGoingHigh)); 1020 } 1021 if (warningLowAlarm) 1022 { 1023 assertions.set( 1024 static_cast<size_t>(IPMIGetSensorEventEnableThresholds:: 1025 lowerNonCriticalGoingLow)); 1026 } 1027 } 1028 if (criticalInterface != sensorMap.end()) 1029 { 1030 auto& criticalMap = criticalInterface->second; 1031 1032 auto criticalHigh = criticalMap.find("CriticalAlarmHigh"); 1033 auto criticalLow = criticalMap.find("CriticalAlarmLow"); 1034 auto criticalHighAlarm = false; 1035 auto criticalLowAlarm = false; 1036 1037 if (criticalHigh != criticalMap.end()) 1038 { 1039 criticalHighAlarm = std::get<bool>(criticalHigh->second); 1040 } 1041 if (criticalLow != criticalMap.end()) 1042 { 1043 criticalLowAlarm = std::get<bool>(criticalLow->second); 1044 } 1045 if (criticalHighAlarm) 1046 { 1047 assertions.set( 1048 static_cast<size_t>(IPMIGetSensorEventEnableThresholds:: 1049 upperCriticalGoingHigh)); 1050 } 1051 if (criticalLowAlarm) 1052 { 1053 assertions.set(static_cast<size_t>( 1054 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow)); 1055 } 1056 } 1057 } 1058 1059 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions); 1060 } 1061 1062 static int getSensorDataRecord(ipmi::Context::ptr ctx, 1063 std::vector<uint8_t>& recordData, 1064 uint16_t recordID) 1065 { 1066 auto& sensorTree = getSensorTree(); 1067 size_t fruCount = 0; 1068 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount); 1069 if (ret != ipmi::ccSuccess) 1070 { 1071 phosphor::logging::log<phosphor::logging::level::ERR>( 1072 "getSensorDataRecord: getFruSdrCount error"); 1073 return GENERAL_ERROR; 1074 } 1075 1076 size_t lastRecord = 1077 sensorTree.size() + fruCount + ipmi::storage::type12Count - 1; 1078 if (recordID == lastRecordIndex) 1079 { 1080 recordID = lastRecord; 1081 } 1082 if (recordID > lastRecord) 1083 { 1084 phosphor::logging::log<phosphor::logging::level::ERR>( 1085 "getSensorDataRecord: recordID > lastRecord error"); 1086 return GENERAL_ERROR; 1087 } 1088 1089 if (recordID >= sensorTree.size()) 1090 { 1091 size_t fruIndex = recordID - sensorTree.size(); 1092 if (fruIndex >= fruCount) 1093 { 1094 // handle type 12 hardcoded records 1095 size_t type12Index = fruIndex - fruCount; 1096 if (type12Index >= ipmi::storage::type12Count) 1097 { 1098 phosphor::logging::log<phosphor::logging::level::ERR>( 1099 "getSensorDataRecord: type12Index error"); 1100 return GENERAL_ERROR; 1101 } 1102 recordData = ipmi::storage::getType12SDRs(type12Index, recordID); 1103 } 1104 else 1105 { 1106 // handle fru records 1107 get_sdr::SensorDataFruRecord data; 1108 ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data); 1109 if (ret != IPMI_CC_OK) 1110 { 1111 return GENERAL_ERROR; 1112 } 1113 data.header.record_id_msb = recordID >> 8; 1114 data.header.record_id_lsb = recordID & 0xFF; 1115 recordData.insert(recordData.end(), 1116 reinterpret_cast<uint8_t*>(&data), 1117 reinterpret_cast<uint8_t*>(&data) + sizeof(data)); 1118 } 1119 1120 return 0; 1121 } 1122 1123 std::string connection; 1124 std::string path; 1125 auto status = getSensorConnection(ctx, recordID, connection, path); 1126 if (status) 1127 { 1128 phosphor::logging::log<phosphor::logging::level::ERR>( 1129 "getSensorDataRecord: getSensorConnection error"); 1130 return GENERAL_ERROR; 1131 } 1132 DbusInterfaceMap sensorMap; 1133 if (!getSensorMap(ctx, connection, path, sensorMap, sensorMapUpdatePeriod)) 1134 { 1135 phosphor::logging::log<phosphor::logging::level::ERR>( 1136 "getSensorDataRecord: getSensorMap error"); 1137 return GENERAL_ERROR; 1138 } 1139 uint16_t sensorNum = getSensorNumberFromPath(path); 1140 if (sensorNum == invalidSensorNumber) 1141 { 1142 phosphor::logging::log<phosphor::logging::level::ERR>( 1143 "getSensorDataRecord: invalidSensorNumber"); 1144 return GENERAL_ERROR; 1145 } 1146 uint8_t sensornumber = static_cast<uint8_t>(sensorNum); 1147 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8); 1148 1149 get_sdr::SensorDataFullRecord record = {0}; 1150 1151 get_sdr::header::set_record_id( 1152 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record)); 1153 1154 record.header.sdr_version = ipmiSdrVersion; 1155 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD; 1156 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) - 1157 sizeof(get_sdr::SensorDataRecordHeader); 1158 record.key.owner_id = 0x20; 1159 record.key.owner_lun = lun; 1160 record.key.sensor_number = sensornumber; 1161 1162 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis 1163 record.body.sensor_type = getSensorTypeFromPath(path); 1164 std::string type = getSensorTypeStringFromPath(path); 1165 auto typeCstr = type.c_str(); 1166 auto findUnits = sensorUnits.find(typeCstr); 1167 if (findUnits != sensorUnits.end()) 1168 { 1169 record.body.sensor_units_2_base = 1170 static_cast<uint8_t>(findUnits->second); 1171 } // else default 0x0 unspecified 1172 1173 record.body.event_reading_type = getSensorEventTypeFromPath(path); 1174 1175 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value"); 1176 if (sensorObject == sensorMap.end()) 1177 { 1178 phosphor::logging::log<phosphor::logging::level::ERR>( 1179 "getSensorDataRecord: sensorObject error"); 1180 return GENERAL_ERROR; 1181 } 1182 1183 uint8_t entityId = 0; 1184 uint8_t entityInstance = 0x01; 1185 1186 // follow the association chain to get the parent board's entityid and 1187 // entityInstance 1188 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance); 1189 1190 record.body.entity_id = entityId; 1191 record.body.entity_instance = entityInstance; 1192 1193 auto maxObject = sensorObject->second.find("MaxValue"); 1194 auto minObject = sensorObject->second.find("MinValue"); 1195 1196 // If min and/or max are left unpopulated, 1197 // then default to what a signed byte would be, namely (-128,127) range. 1198 auto max = static_cast<double>(std::numeric_limits<int8_t>::max()); 1199 auto min = static_cast<double>(std::numeric_limits<int8_t>::lowest()); 1200 if (maxObject != sensorObject->second.end()) 1201 { 1202 max = std::visit(VariantToDoubleVisitor(), maxObject->second); 1203 } 1204 1205 if (minObject != sensorObject->second.end()) 1206 { 1207 min = std::visit(VariantToDoubleVisitor(), minObject->second); 1208 } 1209 1210 int16_t mValue = 0; 1211 int8_t rExp = 0; 1212 int16_t bValue = 0; 1213 int8_t bExp = 0; 1214 bool bSigned = false; 1215 1216 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 1217 { 1218 phosphor::logging::log<phosphor::logging::level::ERR>( 1219 "getSensorDataRecord: getSensorAttributes error"); 1220 return GENERAL_ERROR; 1221 } 1222 1223 // The record.body is a struct SensorDataFullRecordBody 1224 // from sensorhandler.hpp in phosphor-ipmi-host. 1225 // The meaning of these bits appears to come from 1226 // table 43.1 of the IPMI spec. 1227 // The above 5 sensor attributes are stuffed in as follows: 1228 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned 1229 // Byte 22-24 are for other purposes 1230 // Byte 25 = MMMMMMMM = LSB of M 1231 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance 1232 // Byte 27 = BBBBBBBB = LSB of B 1233 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy 1234 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy 1235 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed) 1236 1237 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4 1238 record.body.m_lsb = mValue & 0xFF; 1239 1240 uint8_t mBitSign = (mValue < 0) ? 1 : 0; 1241 uint8_t mBitNine = (mValue & 0x0100) >> 8; 1242 1243 // move the smallest bit of the MSB into place (bit 9) 1244 // the MSbs are bits 7:8 in m_msb_and_tolerance 1245 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6); 1246 1247 record.body.b_lsb = bValue & 0xFF; 1248 1249 uint8_t bBitSign = (bValue < 0) ? 1 : 0; 1250 uint8_t bBitNine = (bValue & 0x0100) >> 8; 1251 1252 // move the smallest bit of the MSB into place (bit 9) 1253 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb 1254 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6); 1255 1256 uint8_t rExpSign = (rExp < 0) ? 1 : 0; 1257 uint8_t rExpBits = rExp & 0x07; 1258 1259 uint8_t bExpSign = (bExp < 0) ? 1 : 0; 1260 uint8_t bExpBits = bExp & 0x07; 1261 1262 // move rExp and bExp into place 1263 record.body.r_b_exponents = 1264 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits; 1265 1266 // Set the analog reading byte interpretation accordingly 1267 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7; 1268 1269 // TODO(): Perhaps care about Tolerance, Accuracy, and so on 1270 // These seem redundant, but derivable from the above 5 attributes 1271 // Original comment said "todo fill out rest of units" 1272 1273 // populate sensor name from path 1274 std::string name; 1275 size_t nameStart = path.rfind("/"); 1276 if (nameStart != std::string::npos) 1277 { 1278 name = path.substr(nameStart + 1, std::string::npos - nameStart); 1279 } 1280 1281 std::replace(name.begin(), name.end(), '_', ' '); 1282 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH) 1283 { 1284 // try to not truncate by replacing common words 1285 constexpr std::array<std::pair<const char*, const char*>, 2> 1286 replaceWords = {std::make_pair("Output", "Out"), 1287 std::make_pair("Input", "In")}; 1288 for (const auto& [find, replace] : replaceWords) 1289 { 1290 boost::replace_all(name, find, replace); 1291 } 1292 1293 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH); 1294 } 1295 record.body.id_string_info = name.size(); 1296 std::strncpy(record.body.id_string, name.c_str(), 1297 sizeof(record.body.id_string)); 1298 1299 // Remember the sensor name, as determined for this sensor number 1300 details::sdrStatsTable.updateName(sensornumber, name); 1301 1302 IPMIThresholds thresholdData; 1303 try 1304 { 1305 thresholdData = getIPMIThresholds(sensorMap); 1306 } 1307 catch (std::exception&) 1308 { 1309 phosphor::logging::log<phosphor::logging::level::ERR>( 1310 "getSensorDataRecord: getIPMIThresholds error"); 1311 return GENERAL_ERROR; 1312 } 1313 1314 if (thresholdData.criticalHigh) 1315 { 1316 record.body.upper_critical_threshold = *thresholdData.criticalHigh; 1317 record.body.supported_deassertions[1] |= static_cast<uint8_t>( 1318 IPMISensorEventEnableThresholds::criticalThreshold); 1319 record.body.supported_deassertions[1] |= static_cast<uint8_t>( 1320 IPMISensorEventEnableThresholds::upperCriticalGoingHigh); 1321 record.body.supported_assertions[1] |= static_cast<uint8_t>( 1322 IPMISensorEventEnableThresholds::upperCriticalGoingHigh); 1323 record.body.discrete_reading_setting_mask[0] |= 1324 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical); 1325 } 1326 if (thresholdData.warningHigh) 1327 { 1328 record.body.upper_noncritical_threshold = *thresholdData.warningHigh; 1329 record.body.supported_deassertions[1] |= static_cast<uint8_t>( 1330 IPMISensorEventEnableThresholds::nonCriticalThreshold); 1331 record.body.supported_deassertions[0] |= static_cast<uint8_t>( 1332 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh); 1333 record.body.supported_assertions[0] |= static_cast<uint8_t>( 1334 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh); 1335 record.body.discrete_reading_setting_mask[0] |= 1336 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical); 1337 } 1338 if (thresholdData.criticalLow) 1339 { 1340 record.body.lower_critical_threshold = *thresholdData.criticalLow; 1341 record.body.supported_assertions[1] |= static_cast<uint8_t>( 1342 IPMISensorEventEnableThresholds::criticalThreshold); 1343 record.body.supported_deassertions[0] |= static_cast<uint8_t>( 1344 IPMISensorEventEnableThresholds::lowerCriticalGoingLow); 1345 record.body.supported_assertions[0] |= static_cast<uint8_t>( 1346 IPMISensorEventEnableThresholds::lowerCriticalGoingLow); 1347 record.body.discrete_reading_setting_mask[0] |= 1348 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical); 1349 } 1350 if (thresholdData.warningLow) 1351 { 1352 record.body.lower_noncritical_threshold = *thresholdData.warningLow; 1353 record.body.supported_assertions[1] |= static_cast<uint8_t>( 1354 IPMISensorEventEnableThresholds::nonCriticalThreshold); 1355 record.body.supported_deassertions[0] |= static_cast<uint8_t>( 1356 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow); 1357 record.body.supported_assertions[0] |= static_cast<uint8_t>( 1358 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow); 1359 record.body.discrete_reading_setting_mask[0] |= 1360 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical); 1361 } 1362 1363 // everything that is readable is setable 1364 record.body.discrete_reading_setting_mask[1] = 1365 record.body.discrete_reading_setting_mask[0]; 1366 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record), 1367 reinterpret_cast<uint8_t*>(&record) + sizeof(record)); 1368 return 0; 1369 } 1370 1371 /** @brief implements the get SDR Info command 1372 * @param count - Operation 1373 * 1374 * @returns IPMI completion code plus response data 1375 * - sdrCount - sensor/SDR count 1376 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag 1377 */ 1378 static ipmi::RspType<uint8_t, // respcount 1379 uint8_t, // dynamic population flags 1380 uint32_t // last time a sensor was added 1381 > 1382 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx, 1383 std::optional<uint8_t> count) 1384 { 1385 auto& sensorTree = getSensorTree(); 1386 uint8_t sdrCount = 0; 1387 uint16_t recordID = 0; 1388 std::vector<uint8_t> record; 1389 // Sensors are dynamically allocated, and there is at least one LUN 1390 uint8_t lunsAndDynamicPopulation = 0x80; 1391 constexpr uint8_t getSdrCount = 0x01; 1392 constexpr uint8_t getSensorCount = 0x00; 1393 1394 if (!getSensorSubtree(sensorTree) || sensorTree.empty()) 1395 { 1396 return ipmi::responseResponseError(); 1397 } 1398 uint16_t numSensors = sensorTree.size(); 1399 if (count.value_or(0) == getSdrCount) 1400 { 1401 // Count the number of Type 1 SDR entries assigned to the LUN 1402 while (!getSensorDataRecord(ctx, record, recordID++)) 1403 { 1404 get_sdr::SensorDataRecordHeader* hdr = 1405 reinterpret_cast<get_sdr::SensorDataRecordHeader*>( 1406 record.data()); 1407 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD) 1408 { 1409 get_sdr::SensorDataFullRecord* recordData = 1410 reinterpret_cast<get_sdr::SensorDataFullRecord*>( 1411 record.data()); 1412 if (ctx->lun == recordData->key.owner_lun) 1413 { 1414 sdrCount++; 1415 } 1416 } 1417 } 1418 } 1419 else if (count.value_or(0) == getSensorCount) 1420 { 1421 // Return the number of sensors attached to the LUN 1422 if ((ctx->lun == 0) && (numSensors > 0)) 1423 { 1424 sdrCount = 1425 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors; 1426 } 1427 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN)) 1428 { 1429 sdrCount = (numSensors > (2 * maxSensorsPerLUN)) 1430 ? maxSensorsPerLUN 1431 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN; 1432 } 1433 else if (ctx->lun == 3) 1434 { 1435 if (numSensors <= maxIPMISensors) 1436 { 1437 sdrCount = 1438 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN; 1439 } 1440 else 1441 { 1442 // error 1443 throw std::out_of_range( 1444 "Maximum number of IPMI sensors exceeded."); 1445 } 1446 } 1447 } 1448 else 1449 { 1450 return ipmi::responseInvalidFieldRequest(); 1451 } 1452 1453 // Get Sensor count. This returns the number of sensors 1454 if (numSensors > 0) 1455 { 1456 lunsAndDynamicPopulation |= 1; 1457 } 1458 if (numSensors > maxSensorsPerLUN) 1459 { 1460 lunsAndDynamicPopulation |= 2; 1461 } 1462 if (numSensors >= (maxSensorsPerLUN * 2)) 1463 { 1464 lunsAndDynamicPopulation |= 8; 1465 } 1466 if (numSensors > maxIPMISensors) 1467 { 1468 // error 1469 throw std::out_of_range("Maximum number of IPMI sensors exceeded."); 1470 } 1471 1472 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation, 1473 sdrLastAdd); 1474 } 1475 1476 /* end sensor commands */ 1477 1478 /* storage commands */ 1479 1480 ipmi::RspType<uint8_t, // sdr version 1481 uint16_t, // record count 1482 uint16_t, // free space 1483 uint32_t, // most recent addition 1484 uint32_t, // most recent erase 1485 uint8_t // operationSupport 1486 > 1487 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx) 1488 { 1489 auto& sensorTree = getSensorTree(); 1490 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF; 1491 if (!getSensorSubtree(sensorTree) && sensorTree.empty()) 1492 { 1493 return ipmi::responseResponseError(); 1494 } 1495 1496 size_t fruCount = 0; 1497 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount); 1498 if (ret != ipmi::ccSuccess) 1499 { 1500 return ipmi::response(ret); 1501 } 1502 1503 uint16_t recordCount = 1504 sensorTree.size() + fruCount + ipmi::storage::type12Count; 1505 1506 uint8_t operationSupport = static_cast<uint8_t>( 1507 SdrRepositoryInfoOps::overflow); // write not supported 1508 1509 operationSupport |= 1510 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported); 1511 operationSupport |= static_cast<uint8_t>( 1512 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported); 1513 return ipmi::responseSuccess(ipmiSdrVersion, recordCount, 1514 unspecifiedFreeSpace, sdrLastAdd, 1515 sdrLastRemove, operationSupport); 1516 } 1517 1518 /** @brief implements the get SDR allocation info command 1519 * 1520 * @returns IPMI completion code plus response data 1521 * - allocUnits - Number of possible allocation units 1522 * - allocUnitSize - Allocation unit size in bytes. 1523 * - allocUnitFree - Number of free allocation units 1524 * - allocUnitLargestFree - Largest free block in allocation units 1525 * - maxRecordSize - Maximum record size in allocation units. 1526 */ 1527 ipmi::RspType<uint16_t, // allocUnits 1528 uint16_t, // allocUnitSize 1529 uint16_t, // allocUnitFree 1530 uint16_t, // allocUnitLargestFree 1531 uint8_t // maxRecordSize 1532 > 1533 ipmiStorageGetSDRAllocationInfo() 1534 { 1535 // 0000h unspecified number of alloc units 1536 constexpr uint16_t allocUnits = 0; 1537 1538 constexpr uint16_t allocUnitFree = 0; 1539 constexpr uint16_t allocUnitLargestFree = 0; 1540 // only allow one block at a time 1541 constexpr uint8_t maxRecordSize = 1; 1542 1543 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree, 1544 allocUnitLargestFree, maxRecordSize); 1545 } 1546 1547 /** @brief implements the reserve SDR command 1548 * @returns IPMI completion code plus response data 1549 * - sdrReservationID 1550 */ 1551 ipmi::RspType<uint16_t> ipmiStorageReserveSDR() 1552 { 1553 sdrReservationID++; 1554 if (sdrReservationID == 0) 1555 { 1556 sdrReservationID++; 1557 } 1558 1559 return ipmi::responseSuccess(sdrReservationID); 1560 } 1561 1562 ipmi::RspType<uint16_t, // next record ID 1563 std::vector<uint8_t> // payload 1564 > 1565 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID, 1566 uint16_t recordID, uint8_t offset, uint8_t bytesToRead) 1567 { 1568 size_t fruCount = 0; 1569 // reservation required for partial reads with non zero offset into 1570 // record 1571 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset) 1572 { 1573 phosphor::logging::log<phosphor::logging::level::ERR>( 1574 "ipmiStorageGetSDR: responseInvalidReservationId"); 1575 return ipmi::responseInvalidReservationId(); 1576 } 1577 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount); 1578 if (ret != ipmi::ccSuccess) 1579 { 1580 phosphor::logging::log<phosphor::logging::level::ERR>( 1581 "ipmiStorageGetSDR: getFruSdrCount error"); 1582 return ipmi::response(ret); 1583 } 1584 1585 auto& sensorTree = getSensorTree(); 1586 size_t lastRecord = 1587 sensorTree.size() + fruCount + ipmi::storage::type12Count - 1; 1588 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF; 1589 1590 if (!getSensorSubtree(sensorTree) && sensorTree.empty()) 1591 { 1592 phosphor::logging::log<phosphor::logging::level::ERR>( 1593 "ipmiStorageGetSDR: getSensorSubtree error"); 1594 return ipmi::responseResponseError(); 1595 } 1596 1597 std::vector<uint8_t> record; 1598 if (getSensorDataRecord(ctx, record, recordID)) 1599 { 1600 phosphor::logging::log<phosphor::logging::level::ERR>( 1601 "ipmiStorageGetSDR: fail to get SDR"); 1602 return ipmi::responseInvalidFieldRequest(); 1603 } 1604 get_sdr::SensorDataRecordHeader* hdr = 1605 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data()); 1606 if (!hdr) 1607 { 1608 phosphor::logging::log<phosphor::logging::level::ERR>( 1609 "ipmiStorageGetSDR: record header is null"); 1610 return ipmi::responseSuccess(nextRecordId, record); 1611 } 1612 1613 size_t sdrLength = 1614 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length; 1615 if (sdrLength < (offset + bytesToRead)) 1616 { 1617 bytesToRead = sdrLength - offset; 1618 } 1619 1620 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset; 1621 if (!respStart) 1622 { 1623 phosphor::logging::log<phosphor::logging::level::ERR>( 1624 "ipmiStorageGetSDR: record is null"); 1625 return ipmi::responseSuccess(nextRecordId, record); 1626 } 1627 1628 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead); 1629 1630 return ipmi::responseSuccess(nextRecordId, recordData); 1631 } 1632 /* end storage commands */ 1633 1634 void registerSensorFunctions() 1635 { 1636 // <Platform Event> 1637 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1638 ipmi::sensor_event::cmdPlatformEvent, 1639 ipmi::Privilege::Operator, ipmiSenPlatformEvent); 1640 1641 #ifdef FEATURE_DYNAMIC_SENSORS_WRITE 1642 // <Set Sensor Reading and Event Status> 1643 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1644 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts, 1645 ipmi::Privilege::Operator, ipmiSetSensorReading); 1646 #endif 1647 1648 // <Get Sensor Reading> 1649 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1650 ipmi::sensor_event::cmdGetSensorReading, 1651 ipmi::Privilege::User, ipmiSenGetSensorReading); 1652 1653 // <Get Sensor Threshold> 1654 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1655 ipmi::sensor_event::cmdGetSensorThreshold, 1656 ipmi::Privilege::User, ipmiSenGetSensorThresholds); 1657 1658 // <Set Sensor Threshold> 1659 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1660 ipmi::sensor_event::cmdSetSensorThreshold, 1661 ipmi::Privilege::Operator, 1662 ipmiSenSetSensorThresholds); 1663 1664 // <Get Sensor Event Enable> 1665 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1666 ipmi::sensor_event::cmdGetSensorEventEnable, 1667 ipmi::Privilege::User, ipmiSenGetSensorEventEnable); 1668 1669 // <Get Sensor Event Status> 1670 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1671 ipmi::sensor_event::cmdGetSensorEventStatus, 1672 ipmi::Privilege::User, ipmiSenGetSensorEventStatus); 1673 1674 // register all storage commands for both Sensor and Storage command 1675 // versions 1676 1677 // <Get SDR Repository Info> 1678 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1679 ipmi::storage::cmdGetSdrRepositoryInfo, 1680 ipmi::Privilege::User, 1681 ipmiStorageGetSDRRepositoryInfo); 1682 1683 // <Get Device SDR Info> 1684 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1685 ipmi::sensor_event::cmdGetDeviceSdrInfo, 1686 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo); 1687 1688 // <Get SDR Allocation Info> 1689 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1690 ipmi::storage::cmdGetSdrRepositoryAllocInfo, 1691 ipmi::Privilege::User, 1692 ipmiStorageGetSDRAllocationInfo); 1693 1694 // <Reserve SDR Repo> 1695 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1696 ipmi::sensor_event::cmdReserveDeviceSdrRepository, 1697 ipmi::Privilege::User, ipmiStorageReserveSDR); 1698 1699 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1700 ipmi::storage::cmdReserveSdrRepository, 1701 ipmi::Privilege::User, ipmiStorageReserveSDR); 1702 1703 // <Get Sdr> 1704 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1705 ipmi::sensor_event::cmdGetDeviceSdr, 1706 ipmi::Privilege::User, ipmiStorageGetSDR); 1707 1708 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1709 ipmi::storage::cmdGetSdr, ipmi::Privilege::User, 1710 ipmiStorageGetSDR); 1711 } 1712 } // namespace ipmi 1713