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