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