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