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