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