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