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 <boost/algorithm/string.hpp> 18 #include <boost/container/flat_map.hpp> 19 #include <chrono> 20 #include <cmath> 21 #include <commandutils.hpp> 22 #include <iostream> 23 #include <ipmi_to_redfish_hooks.hpp> 24 #include <ipmid/api.hpp> 25 #include <ipmid/utils.hpp> 26 #include <phosphor-logging/log.hpp> 27 #include <sdbusplus/bus.hpp> 28 #include <sdrutils.hpp> 29 #include <sensorcommands.hpp> 30 #include <sensorutils.hpp> 31 #include <storagecommands.hpp> 32 #include <string> 33 34 namespace ipmi 35 { 36 using ManagedObjectType = 37 std::map<sdbusplus::message::object_path, 38 std::map<std::string, std::map<std::string, DbusVariant>>>; 39 40 using SensorMap = std::map<std::string, std::map<std::string, DbusVariant>>; 41 42 static constexpr int sensorListUpdatePeriod = 10; 43 static constexpr int sensorMapUpdatePeriod = 2; 44 45 constexpr size_t maxSDRTotalSize = 46 76; // Largest SDR Record Size (type 01) + SDR Overheader Size 47 constexpr static const uint32_t noTimestamp = 0xFFFFFFFF; 48 49 static uint16_t sdrReservationID; 50 static uint32_t sdrLastAdd = noTimestamp; 51 static uint32_t sdrLastRemove = noTimestamp; 52 53 SensorSubTree sensorTree; 54 static boost::container::flat_map<std::string, ManagedObjectType> SensorCache; 55 56 // Specify the comparison required to sort and find char* map objects 57 struct CmpStr 58 { 59 bool operator()(const char *a, const char *b) const 60 { 61 return std::strcmp(a, b) < 0; 62 } 63 }; 64 const static boost::container::flat_map<const char *, SensorUnits, CmpStr> 65 sensorUnits{{{"temperature", SensorUnits::degreesC}, 66 {"voltage", SensorUnits::volts}, 67 {"current", SensorUnits::amps}, 68 {"fan_tach", SensorUnits::rpm}, 69 {"power", SensorUnits::watts}}}; 70 71 void registerSensorFunctions() __attribute__((constructor)); 72 73 static sdbusplus::bus::match::match sensorAdded( 74 *getSdBus(), 75 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/" 76 "sensors/'", 77 [](sdbusplus::message::message &m) { 78 sensorTree.clear(); 79 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>( 80 std::chrono::system_clock::now().time_since_epoch()) 81 .count(); 82 }); 83 84 static sdbusplus::bus::match::match sensorRemoved( 85 *getSdBus(), 86 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/" 87 "sensors/'", 88 [](sdbusplus::message::message &m) { 89 sensorTree.clear(); 90 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>( 91 std::chrono::system_clock::now().time_since_epoch()) 92 .count(); 93 }); 94 95 // this keeps track of deassertions for sensor event status command. A 96 // deasertion can only happen if an assertion was seen first. 97 static boost::container::flat_map< 98 std::string, boost::container::flat_map<std::string, std::optional<bool>>> 99 thresholdDeassertMap; 100 101 static sdbusplus::bus::match::match thresholdChanged( 102 *getSdBus(), 103 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus." 104 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'", 105 [](sdbusplus::message::message &m) { 106 boost::container::flat_map<std::string, std::variant<bool, double>> 107 values; 108 m.read(std::string(), values); 109 110 auto findAssert = 111 std::find_if(values.begin(), values.end(), [](const auto &pair) { 112 return pair.first.find("Alarm") != std::string::npos; 113 }); 114 if (findAssert != values.end()) 115 { 116 auto ptr = std::get_if<bool>(&(findAssert->second)); 117 if (ptr == nullptr) 118 { 119 phosphor::logging::log<phosphor::logging::level::ERR>( 120 "thresholdChanged: Assert non bool"); 121 return; 122 } 123 if (*ptr) 124 { 125 phosphor::logging::log<phosphor::logging::level::INFO>( 126 "thresholdChanged: Assert", 127 phosphor::logging::entry("SENSOR=%s", m.get_path())); 128 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr; 129 } 130 else 131 { 132 auto &value = 133 thresholdDeassertMap[m.get_path()][findAssert->first]; 134 if (value) 135 { 136 phosphor::logging::log<phosphor::logging::level::INFO>( 137 "thresholdChanged: deassert", 138 phosphor::logging::entry("SENSOR=%s", m.get_path())); 139 value = *ptr; 140 } 141 } 142 } 143 }); 144 145 static void getSensorMaxMin(const SensorMap &sensorMap, double &max, 146 double &min) 147 { 148 max = 127; 149 min = -128; 150 151 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value"); 152 auto critical = 153 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 154 auto warning = 155 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 156 157 if (sensorObject != sensorMap.end()) 158 { 159 auto maxMap = sensorObject->second.find("MaxValue"); 160 auto minMap = sensorObject->second.find("MinValue"); 161 162 if (maxMap != sensorObject->second.end()) 163 { 164 max = std::visit(VariantToDoubleVisitor(), maxMap->second); 165 } 166 if (minMap != sensorObject->second.end()) 167 { 168 min = std::visit(VariantToDoubleVisitor(), minMap->second); 169 } 170 } 171 if (critical != sensorMap.end()) 172 { 173 auto lower = critical->second.find("CriticalLow"); 174 auto upper = critical->second.find("CriticalHigh"); 175 if (lower != critical->second.end()) 176 { 177 double value = std::visit(VariantToDoubleVisitor(), lower->second); 178 min = std::min(value, min); 179 } 180 if (upper != critical->second.end()) 181 { 182 double value = std::visit(VariantToDoubleVisitor(), upper->second); 183 max = std::max(value, max); 184 } 185 } 186 if (warning != sensorMap.end()) 187 { 188 189 auto lower = warning->second.find("WarningLow"); 190 auto upper = warning->second.find("WarningHigh"); 191 if (lower != warning->second.end()) 192 { 193 double value = std::visit(VariantToDoubleVisitor(), lower->second); 194 min = std::min(value, min); 195 } 196 if (upper != warning->second.end()) 197 { 198 double value = std::visit(VariantToDoubleVisitor(), upper->second); 199 max = std::max(value, max); 200 } 201 } 202 } 203 204 static bool getSensorMap(std::string sensorConnection, std::string sensorPath, 205 SensorMap &sensorMap) 206 { 207 static boost::container::flat_map< 208 std::string, std::chrono::time_point<std::chrono::steady_clock>> 209 updateTimeMap; 210 211 auto updateFind = updateTimeMap.find(sensorConnection); 212 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>(); 213 if (updateFind != updateTimeMap.end()) 214 { 215 lastUpdate = updateFind->second; 216 } 217 218 auto now = std::chrono::steady_clock::now(); 219 220 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate) 221 .count() > sensorMapUpdatePeriod) 222 { 223 updateTimeMap[sensorConnection] = now; 224 225 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 226 auto managedObj = dbus->new_method_call( 227 sensorConnection.c_str(), "/", "org.freedesktop.DBus.ObjectManager", 228 "GetManagedObjects"); 229 230 ManagedObjectType managedObjects; 231 try 232 { 233 auto reply = dbus->call(managedObj); 234 reply.read(managedObjects); 235 } 236 catch (sdbusplus::exception_t &) 237 { 238 phosphor::logging::log<phosphor::logging::level::ERR>( 239 "Error getting managed objects from connection", 240 phosphor::logging::entry("CONNECTION=%s", 241 sensorConnection.c_str())); 242 return false; 243 } 244 245 SensorCache[sensorConnection] = managedObjects; 246 } 247 auto connection = SensorCache.find(sensorConnection); 248 if (connection == SensorCache.end()) 249 { 250 return false; 251 } 252 auto path = connection->second.find(sensorPath); 253 if (path == connection->second.end()) 254 { 255 return false; 256 } 257 sensorMap = path->second; 258 259 return true; 260 } 261 262 /* sensor commands */ 263 ipmi_ret_t ipmiSensorWildcardHandler(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 264 ipmi_request_t request, 265 ipmi_response_t response, 266 ipmi_data_len_t dataLen, 267 ipmi_context_t context) 268 { 269 *dataLen = 0; 270 printCommand(+netfn, +cmd); 271 return IPMI_CC_INVALID; 272 } 273 274 namespace meHealth 275 { 276 constexpr const char *busname = "xyz.openbmc_project.NodeManagerProxy"; 277 constexpr const char *path = "/xyz/openbmc_project/status/me"; 278 constexpr const char *interface = "xyz.openbmc_project.SetHealth"; 279 constexpr const char *method = "SetHealth"; 280 constexpr const char *critical = "critical"; 281 constexpr const char *warning = "warning"; 282 constexpr const char *ok = "ok"; 283 } // namespace meHealth 284 285 static void setMeStatus(uint8_t eventData2, uint8_t eventData3, bool disable) 286 { 287 constexpr const std::array<uint8_t, 10> critical = { 288 0x1, 0x2, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xD, 0xE}; 289 constexpr const std::array<uint8_t, 5> warning = {0x3, 0xA, 0x13, 0x19, 290 0x1A}; 291 292 std::string state; 293 if (std::find(critical.begin(), critical.end(), eventData2) != 294 critical.end()) 295 { 296 state = meHealth::critical; 297 } 298 // special case 0x3 as we only care about a few states 299 else if (eventData2 == 0x3) 300 { 301 if (eventData3 <= 0x2) 302 { 303 state = meHealth::warning; 304 } 305 else 306 { 307 return; 308 } 309 } 310 else if (std::find(warning.begin(), warning.end(), eventData2) != 311 warning.end()) 312 { 313 state = meHealth::warning; 314 } 315 else 316 { 317 return; 318 } 319 if (disable) 320 { 321 state = meHealth::ok; 322 } 323 324 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 325 auto setHealth = 326 dbus->new_method_call(meHealth::busname, meHealth::path, 327 meHealth::interface, meHealth::method); 328 setHealth.append(std::to_string(static_cast<size_t>(eventData2)), state); 329 try 330 { 331 dbus->call(setHealth); 332 } 333 catch (sdbusplus::exception_t &) 334 { 335 phosphor::logging::log<phosphor::logging::level::ERR>( 336 "Failed to set ME Health"); 337 } 338 } 339 340 ipmi::RspType<> ipmiSenPlatformEvent(ipmi::message::Payload &p) 341 { 342 constexpr const uint8_t meId = 0x2C; 343 constexpr const uint8_t meSensorNum = 0x17; 344 constexpr const uint8_t disabled = 0x80; 345 346 uint8_t generatorID = 0; 347 uint8_t evmRev = 0; 348 uint8_t sensorType = 0; 349 uint8_t sensorNum = 0; 350 uint8_t eventType = 0; 351 uint8_t eventData1 = 0; 352 std::optional<uint8_t> eventData2 = 0; 353 std::optional<uint8_t> eventData3 = 0; 354 355 // todo: This check is supposed to be based on the incoming channel. 356 // e.g. system channel will provide upto 8 bytes including generator 357 // ID, but ipmb channel will provide only up to 7 bytes without the 358 // generator ID. 359 // Support for this check is coming in future patches, so for now just base 360 // it on if the first byte is the EvMRev (0x04). 361 if (p.size() && p.data()[0] == 0x04) 362 { 363 p.unpack(evmRev, sensorType, sensorNum, eventType, eventData1, 364 eventData2, eventData3); 365 // todo: the generator ID for this channel is supposed to come from the 366 // IPMB requesters slave address. Support for this is coming in future 367 // patches, so for now just assume it is coming from the ME (0x2C). 368 generatorID = 0x2C; 369 } 370 else 371 { 372 p.unpack(generatorID, evmRev, sensorType, sensorNum, eventType, 373 eventData1, eventData2, eventData3); 374 } 375 if (!p.fullyUnpacked()) 376 { 377 return ipmi::responseReqDataLenInvalid(); 378 } 379 380 // Send this request to the Redfish hooks to log it as a Redfish message 381 // instead. There is no need to add it to the SEL, so just return success. 382 intel_oem::ipmi::sel::checkRedfishHooks( 383 generatorID, evmRev, sensorType, sensorNum, eventType, eventData1, 384 eventData2.value_or(0xFF), eventData3.value_or(0xFF)); 385 386 if (generatorID == meId && sensorNum == meSensorNum && eventData2 && 387 eventData3) 388 { 389 setMeStatus(*eventData2, *eventData3, (eventType & disabled)); 390 } 391 392 return ipmi::responseSuccess(); 393 } 394 395 ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>> 396 ipmiSenGetSensorReading(uint8_t sensnum) 397 { 398 std::string connection; 399 std::string path; 400 401 auto status = getSensorConnection(sensnum, connection, path); 402 if (status) 403 { 404 return ipmi::response(status); 405 } 406 407 SensorMap sensorMap; 408 if (!getSensorMap(connection, path, sensorMap)) 409 { 410 return ipmi::responseResponseError(); 411 } 412 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value"); 413 414 if (sensorObject == sensorMap.end() || 415 sensorObject->second.find("Value") == sensorObject->second.end()) 416 { 417 return ipmi::responseResponseError(); 418 } 419 auto &valueVariant = sensorObject->second["Value"]; 420 double reading = std::visit(VariantToDoubleVisitor(), valueVariant); 421 422 double max = 0; 423 double min = 0; 424 getSensorMaxMin(sensorMap, max, min); 425 426 int16_t mValue = 0; 427 int16_t bValue = 0; 428 int8_t rExp = 0; 429 int8_t bExp = 0; 430 bool bSigned = false; 431 432 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 433 { 434 return ipmi::responseResponseError(); 435 } 436 437 uint8_t value = 438 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned); 439 uint8_t operation = 440 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable); 441 operation |= 442 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable); 443 444 uint8_t thresholds = 0; 445 446 auto warningObject = 447 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 448 if (warningObject != sensorMap.end()) 449 { 450 auto alarmHigh = warningObject->second.find("WarningAlarmHigh"); 451 auto alarmLow = warningObject->second.find("WarningAlarmLow"); 452 if (alarmHigh != warningObject->second.end()) 453 { 454 if (std::get<bool>(alarmHigh->second)) 455 { 456 thresholds |= static_cast<uint8_t>( 457 IPMISensorReadingByte3::upperNonCritical); 458 } 459 } 460 if (alarmLow != warningObject->second.end()) 461 { 462 if (std::get<bool>(alarmLow->second)) 463 { 464 thresholds |= static_cast<uint8_t>( 465 IPMISensorReadingByte3::lowerNonCritical); 466 } 467 } 468 } 469 470 auto criticalObject = 471 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 472 if (criticalObject != sensorMap.end()) 473 { 474 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh"); 475 auto alarmLow = criticalObject->second.find("CriticalAlarmLow"); 476 if (alarmHigh != criticalObject->second.end()) 477 { 478 if (std::get<bool>(alarmHigh->second)) 479 { 480 thresholds |= 481 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical); 482 } 483 } 484 if (alarmLow != criticalObject->second.end()) 485 { 486 if (std::get<bool>(alarmLow->second)) 487 { 488 thresholds |= 489 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical); 490 } 491 } 492 } 493 494 // no discrete as of today so optional byte is never returned 495 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt); 496 } 497 498 /** @brief implements the Set Sensor threshold command 499 * @param sensorNumber - sensor number 500 * @param lowerNonCriticalThreshMask 501 * @param lowerCriticalThreshMask 502 * @param lowerNonRecovThreshMask 503 * @param upperNonCriticalThreshMask 504 * @param upperCriticalThreshMask 505 * @param upperNonRecovThreshMask 506 * @param reserved 507 * @param lowerNonCritical - lower non-critical threshold 508 * @param lowerCritical - Lower critical threshold 509 * @param lowerNonRecoverable - Lower non recovarable threshold 510 * @param upperNonCritical - Upper non-critical threshold 511 * @param upperCritical - Upper critical 512 * @param upperNonRecoverable - Upper Non-recoverable 513 * 514 * @returns IPMI completion code 515 */ 516 ipmi::RspType<> ipmiSenSetSensorThresholds( 517 uint8_t sensorNum, bool lowerNonCriticalThreshMask, 518 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask, 519 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask, 520 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical, 521 uint8_t lowerCritical, uint8_t lowerNonRecoverable, 522 uint8_t upperNonCritical, uint8_t upperCritical, 523 uint8_t upperNonRecoverable) 524 { 525 constexpr uint8_t thresholdMask = 0xFF; 526 527 if (reserved) 528 { 529 return ipmi::responseInvalidFieldRequest(); 530 } 531 532 // lower nc and upper nc not suppported on any sensor 533 if (lowerNonRecovThreshMask || upperNonRecovThreshMask) 534 { 535 return ipmi::responseInvalidFieldRequest(); 536 } 537 538 // if none of the threshold mask are set, nothing to do 539 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask | 540 lowerNonRecovThreshMask | upperNonCriticalThreshMask | 541 upperCriticalThreshMask | upperNonRecovThreshMask)) 542 { 543 return ipmi::responseSuccess(); 544 } 545 546 std::string connection; 547 std::string path; 548 549 ipmi::Cc status = getSensorConnection(sensorNum, connection, path); 550 if (status) 551 { 552 return ipmi::response(status); 553 } 554 SensorMap sensorMap; 555 if (!getSensorMap(connection, path, sensorMap)) 556 { 557 return ipmi::responseResponseError(); 558 } 559 560 double max = 0; 561 double min = 0; 562 getSensorMaxMin(sensorMap, max, min); 563 564 int16_t mValue = 0; 565 int16_t bValue = 0; 566 int8_t rExp = 0; 567 int8_t bExp = 0; 568 bool bSigned = false; 569 570 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 571 { 572 return ipmi::responseResponseError(); 573 } 574 575 // store a vector of property name, value to set, and interface 576 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet; 577 578 // define the indexes of the tuple 579 constexpr uint8_t propertyName = 0; 580 constexpr uint8_t thresholdValue = 1; 581 constexpr uint8_t interface = 2; 582 // verifiy all needed fields are present 583 if (lowerCriticalThreshMask || upperCriticalThreshMask) 584 { 585 auto findThreshold = 586 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 587 if (findThreshold == sensorMap.end()) 588 { 589 return ipmi::responseInvalidFieldRequest(); 590 } 591 if (lowerCriticalThreshMask) 592 { 593 auto findLower = findThreshold->second.find("CriticalLow"); 594 if (findLower == findThreshold->second.end()) 595 { 596 return ipmi::responseInvalidFieldRequest(); 597 } 598 thresholdsToSet.emplace_back("CriticalLow", lowerCritical, 599 findThreshold->first); 600 } 601 if (upperCriticalThreshMask) 602 { 603 auto findUpper = findThreshold->second.find("CriticalHigh"); 604 if (findUpper == findThreshold->second.end()) 605 { 606 return ipmi::responseInvalidFieldRequest(); 607 } 608 thresholdsToSet.emplace_back("CriticalHigh", upperCritical, 609 findThreshold->first); 610 } 611 } 612 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask) 613 { 614 auto findThreshold = 615 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 616 if (findThreshold == sensorMap.end()) 617 { 618 return ipmi::responseInvalidFieldRequest(); 619 } 620 if (lowerNonCriticalThreshMask) 621 { 622 auto findLower = findThreshold->second.find("WarningLow"); 623 if (findLower == findThreshold->second.end()) 624 { 625 return ipmi::responseInvalidFieldRequest(); 626 } 627 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical, 628 findThreshold->first); 629 } 630 if (upperNonCriticalThreshMask) 631 { 632 auto findUpper = findThreshold->second.find("WarningHigh"); 633 if (findUpper == findThreshold->second.end()) 634 { 635 return ipmi::responseInvalidFieldRequest(); 636 } 637 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical, 638 findThreshold->first); 639 } 640 } 641 for (const auto &property : thresholdsToSet) 642 { 643 // from section 36.3 in the IPMI Spec, assume all linear 644 double valueToSet = ((mValue * std::get<thresholdValue>(property)) + 645 (bValue * std::pow(10, bExp))) * 646 std::pow(10, rExp); 647 setDbusProperty( 648 *getSdBus(), connection, path, std::get<interface>(property), 649 std::get<propertyName>(property), ipmi::Value(valueToSet)); 650 } 651 return ipmi::responseSuccess(); 652 } 653 654 IPMIThresholds getIPMIThresholds(const SensorMap &sensorMap) 655 { 656 IPMIThresholds resp; 657 auto warningInterface = 658 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 659 auto criticalInterface = 660 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 661 662 if ((warningInterface != sensorMap.end()) || 663 (criticalInterface != sensorMap.end())) 664 { 665 auto sensorPair = sensorMap.find("xyz.openbmc_project.Sensor.Value"); 666 667 if (sensorPair == sensorMap.end()) 668 { 669 // should not have been able to find a sensor not implementing 670 // the sensor object 671 throw std::runtime_error("Invalid sensor map"); 672 } 673 674 double max = 0; 675 double min = 0; 676 getSensorMaxMin(sensorMap, max, min); 677 678 int16_t mValue = 0; 679 int16_t bValue = 0; 680 int8_t rExp = 0; 681 int8_t bExp = 0; 682 bool bSigned = false; 683 684 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 685 { 686 throw std::runtime_error("Invalid sensor atrributes"); 687 } 688 if (warningInterface != sensorMap.end()) 689 { 690 auto &warningMap = warningInterface->second; 691 692 auto warningHigh = warningMap.find("WarningHigh"); 693 auto warningLow = warningMap.find("WarningLow"); 694 695 if (warningHigh != warningMap.end()) 696 { 697 698 double value = 699 std::visit(VariantToDoubleVisitor(), warningHigh->second); 700 resp.warningHigh = scaleIPMIValueFromDouble( 701 value, mValue, rExp, bValue, bExp, bSigned); 702 } 703 if (warningLow != warningMap.end()) 704 { 705 double value = 706 std::visit(VariantToDoubleVisitor(), warningLow->second); 707 resp.warningLow = scaleIPMIValueFromDouble( 708 value, mValue, rExp, bValue, bExp, bSigned); 709 } 710 } 711 if (criticalInterface != sensorMap.end()) 712 { 713 auto &criticalMap = criticalInterface->second; 714 715 auto criticalHigh = criticalMap.find("CriticalHigh"); 716 auto criticalLow = criticalMap.find("CriticalLow"); 717 718 if (criticalHigh != criticalMap.end()) 719 { 720 double value = 721 std::visit(VariantToDoubleVisitor(), criticalHigh->second); 722 resp.criticalHigh = scaleIPMIValueFromDouble( 723 value, mValue, rExp, bValue, bExp, bSigned); 724 } 725 if (criticalLow != criticalMap.end()) 726 { 727 double value = 728 std::visit(VariantToDoubleVisitor(), criticalLow->second); 729 resp.criticalLow = scaleIPMIValueFromDouble( 730 value, mValue, rExp, bValue, bExp, bSigned); 731 } 732 } 733 } 734 return resp; 735 } 736 737 ipmi::RspType<uint8_t, // readable 738 uint8_t, // lowerNCrit 739 uint8_t, // lowerCrit 740 uint8_t, // lowerNrecoverable 741 uint8_t, // upperNC 742 uint8_t, // upperCrit 743 uint8_t> // upperNRecoverable 744 ipmiSenGetSensorThresholds(uint8_t sensorNumber) 745 { 746 std::string connection; 747 std::string path; 748 749 auto status = getSensorConnection(sensorNumber, connection, path); 750 if (status) 751 { 752 return ipmi::response(status); 753 } 754 755 SensorMap sensorMap; 756 if (!getSensorMap(connection, path, sensorMap)) 757 { 758 return ipmi::responseResponseError(); 759 } 760 761 IPMIThresholds thresholdData; 762 try 763 { 764 thresholdData = getIPMIThresholds(sensorMap); 765 } 766 catch (std::exception &) 767 { 768 return ipmi::responseResponseError(); 769 } 770 771 uint8_t readable = 0; 772 uint8_t lowerNC = 0; 773 uint8_t lowerCritical = 0; 774 uint8_t lowerNonRecoverable = 0; 775 uint8_t upperNC = 0; 776 uint8_t upperCritical = 0; 777 uint8_t upperNonRecoverable = 0; 778 779 if (thresholdData.warningHigh) 780 { 781 readable |= 782 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical); 783 upperNC = *thresholdData.warningHigh; 784 } 785 if (thresholdData.warningLow) 786 { 787 readable |= 788 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical); 789 lowerNC = *thresholdData.warningLow; 790 } 791 792 if (thresholdData.criticalHigh) 793 { 794 readable |= 795 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical); 796 upperCritical = *thresholdData.criticalHigh; 797 } 798 if (thresholdData.criticalLow) 799 { 800 readable |= 801 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical); 802 lowerCritical = *thresholdData.criticalLow; 803 } 804 805 return ipmi::responseSuccess(readable, lowerNC, lowerCritical, 806 lowerNonRecoverable, upperNC, upperCritical, 807 upperNonRecoverable); 808 } 809 810 /** @brief implements the get Sensor event enable command 811 * @param sensorNumber - sensor number 812 * 813 * @returns IPMI completion code plus response data 814 * - enabled - Sensor Event messages 815 * - assertionEnabledLsb - Assertion event messages 816 * - assertionEnabledMsb - Assertion event messages 817 * - deassertionEnabledLsb - Deassertion event messages 818 * - deassertionEnabledMsb - Deassertion event messages 819 */ 820 821 ipmi::RspType<uint8_t, // enabled 822 uint8_t, // assertionEnabledLsb 823 uint8_t, // assertionEnabledMsb 824 uint8_t, // deassertionEnabledLsb 825 uint8_t> // deassertionEnabledMsb 826 ipmiSenGetSensorEventEnable(uint8_t sensorNum) 827 { 828 std::string connection; 829 std::string path; 830 831 uint8_t enabled; 832 uint8_t assertionEnabledLsb; 833 uint8_t assertionEnabledMsb; 834 uint8_t deassertionEnabledLsb; 835 uint8_t deassertionEnabledMsb; 836 837 auto status = getSensorConnection(sensorNum, connection, path); 838 if (status) 839 { 840 return ipmi::response(status); 841 } 842 843 SensorMap sensorMap; 844 if (!getSensorMap(connection, path, sensorMap)) 845 { 846 return ipmi::responseResponseError(); 847 } 848 849 auto warningInterface = 850 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 851 auto criticalInterface = 852 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 853 if ((warningInterface != sensorMap.end()) || 854 (criticalInterface != sensorMap.end())) 855 { 856 enabled = static_cast<uint8_t>( 857 IPMISensorEventEnableByte2::sensorScanningEnable); 858 if (warningInterface != sensorMap.end()) 859 { 860 auto &warningMap = warningInterface->second; 861 862 auto warningHigh = warningMap.find("WarningHigh"); 863 auto warningLow = warningMap.find("WarningLow"); 864 if (warningHigh != warningMap.end()) 865 { 866 assertionEnabledLsb |= static_cast<uint8_t>( 867 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh); 868 deassertionEnabledLsb |= static_cast<uint8_t>( 869 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow); 870 } 871 if (warningLow != warningMap.end()) 872 { 873 assertionEnabledLsb |= static_cast<uint8_t>( 874 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow); 875 deassertionEnabledLsb |= static_cast<uint8_t>( 876 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh); 877 } 878 } 879 if (criticalInterface != sensorMap.end()) 880 { 881 auto &criticalMap = criticalInterface->second; 882 883 auto criticalHigh = criticalMap.find("CriticalHigh"); 884 auto criticalLow = criticalMap.find("CriticalLow"); 885 886 if (criticalHigh != criticalMap.end()) 887 { 888 assertionEnabledMsb |= static_cast<uint8_t>( 889 IPMISensorEventEnableThresholds::upperCriticalGoingHigh); 890 deassertionEnabledMsb |= static_cast<uint8_t>( 891 IPMISensorEventEnableThresholds::upperCriticalGoingLow); 892 } 893 if (criticalLow != criticalMap.end()) 894 { 895 assertionEnabledLsb |= static_cast<uint8_t>( 896 IPMISensorEventEnableThresholds::lowerCriticalGoingLow); 897 deassertionEnabledLsb |= static_cast<uint8_t>( 898 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh); 899 } 900 } 901 } 902 903 return ipmi::responseSuccess(enabled, assertionEnabledLsb, 904 assertionEnabledMsb, deassertionEnabledLsb, 905 deassertionEnabledMsb); 906 } 907 908 ipmi_ret_t ipmiSenGetSensorEventStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 909 ipmi_request_t request, 910 ipmi_response_t response, 911 ipmi_data_len_t dataLen, 912 ipmi_context_t context) 913 { 914 if (*dataLen != 1) 915 { 916 *dataLen = 0; 917 return IPMI_CC_REQ_DATA_LEN_INVALID; 918 } 919 *dataLen = 0; // default to 0 in case of an error 920 921 uint8_t sensnum = *(static_cast<uint8_t *>(request)); 922 923 std::string connection; 924 std::string path; 925 926 auto status = getSensorConnection(sensnum, connection, path); 927 if (status) 928 { 929 return status; 930 } 931 932 SensorMap sensorMap; 933 if (!getSensorMap(connection, path, sensorMap)) 934 { 935 return IPMI_CC_RESPONSE_ERROR; 936 } 937 938 auto warningInterface = 939 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 940 auto criticalInterface = 941 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 942 943 // zero out response buff 944 auto responseClear = static_cast<uint8_t *>(response); 945 std::fill(responseClear, responseClear + sizeof(SensorEventStatusResp), 0); 946 auto resp = static_cast<SensorEventStatusResp *>(response); 947 resp->enabled = 948 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable); 949 950 std::optional<bool> criticalDeassertHigh = 951 thresholdDeassertMap[path]["CriticalAlarmHigh"]; 952 std::optional<bool> criticalDeassertLow = 953 thresholdDeassertMap[path]["CriticalAlarmLow"]; 954 std::optional<bool> warningDeassertHigh = 955 thresholdDeassertMap[path]["WarningAlarmHigh"]; 956 std::optional<bool> warningDeassertLow = 957 thresholdDeassertMap[path]["WarningAlarmLow"]; 958 959 if (criticalDeassertHigh && !*criticalDeassertHigh) 960 { 961 resp->deassertionsMSB |= static_cast<uint8_t>( 962 IPMISensorEventEnableThresholds::upperCriticalGoingHigh); 963 } 964 if (criticalDeassertLow && !*criticalDeassertLow) 965 { 966 resp->deassertionsMSB |= static_cast<uint8_t>( 967 IPMISensorEventEnableThresholds::upperCriticalGoingLow); 968 } 969 if (warningDeassertHigh && !*warningDeassertHigh) 970 { 971 resp->deassertionsLSB |= static_cast<uint8_t>( 972 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh); 973 } 974 if (warningDeassertLow && !*warningDeassertLow) 975 { 976 resp->deassertionsLSB |= static_cast<uint8_t>( 977 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh); 978 } 979 980 if ((warningInterface != sensorMap.end()) || 981 (criticalInterface != sensorMap.end())) 982 { 983 resp->enabled = static_cast<uint8_t>( 984 IPMISensorEventEnableByte2::eventMessagesEnable); 985 if (warningInterface != sensorMap.end()) 986 { 987 auto &warningMap = warningInterface->second; 988 989 auto warningHigh = warningMap.find("WarningAlarmHigh"); 990 auto warningLow = warningMap.find("WarningAlarmLow"); 991 auto warningHighAlarm = false; 992 auto warningLowAlarm = false; 993 994 if (warningHigh != warningMap.end()) 995 { 996 warningHighAlarm = std::get<bool>(warningHigh->second); 997 } 998 if (warningLow != warningMap.end()) 999 { 1000 warningLowAlarm = std::get<bool>(warningLow->second); 1001 } 1002 if (warningHighAlarm) 1003 { 1004 resp->assertionsLSB |= static_cast<uint8_t>( 1005 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh); 1006 } 1007 if (warningLowAlarm) 1008 { 1009 resp->assertionsLSB |= 1; // lower nc going low 1010 } 1011 } 1012 if (criticalInterface != sensorMap.end()) 1013 { 1014 auto &criticalMap = criticalInterface->second; 1015 1016 auto criticalHigh = criticalMap.find("CriticalAlarmHigh"); 1017 auto criticalLow = criticalMap.find("CriticalAlarmLow"); 1018 auto criticalHighAlarm = false; 1019 auto criticalLowAlarm = false; 1020 1021 if (criticalHigh != criticalMap.end()) 1022 { 1023 criticalHighAlarm = std::get<bool>(criticalHigh->second); 1024 } 1025 if (criticalLow != criticalMap.end()) 1026 { 1027 criticalLowAlarm = std::get<bool>(criticalLow->second); 1028 } 1029 if (criticalHighAlarm) 1030 { 1031 resp->assertionsMSB |= static_cast<uint8_t>( 1032 IPMISensorEventEnableThresholds::upperCriticalGoingHigh); 1033 } 1034 if (criticalLowAlarm) 1035 { 1036 resp->assertionsLSB |= static_cast<uint8_t>( 1037 IPMISensorEventEnableThresholds::lowerCriticalGoingLow); 1038 } 1039 } 1040 *dataLen = sizeof(SensorEventStatusResp); 1041 } 1042 1043 // no thresholds enabled, don't need assertionMSB 1044 else 1045 { 1046 *dataLen = sizeof(SensorEventStatusResp) - 1; 1047 } 1048 1049 return IPMI_CC_OK; 1050 } 1051 1052 /* end sensor commands */ 1053 1054 /* storage commands */ 1055 1056 ipmi::RspType<uint8_t, // sdr version 1057 uint16_t, // record count 1058 uint16_t, // free space 1059 uint32_t, // most recent addition 1060 uint32_t, // most recent erase 1061 uint8_t // operationSupport 1062 > 1063 ipmiStorageGetSDRRepositoryInfo(void) 1064 { 1065 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF; 1066 if (sensorTree.empty() && !getSensorSubtree(sensorTree)) 1067 { 1068 return ipmi::responseResponseError(); 1069 } 1070 1071 size_t fruCount = 0; 1072 ipmi::Cc ret = ipmi::storage::getFruSdrCount(fruCount); 1073 if (ret != ipmi::ccSuccess) 1074 { 1075 return ipmi::response(ret); 1076 } 1077 1078 uint16_t recordCount = 1079 sensorTree.size() + fruCount + ipmi::storage::type12Count; 1080 1081 uint8_t operationSupport = static_cast<uint8_t>( 1082 SdrRepositoryInfoOps::overflow); // write not supported 1083 1084 operationSupport |= 1085 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported); 1086 operationSupport |= static_cast<uint8_t>( 1087 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported); 1088 return ipmi::responseSuccess(ipmiSdrVersion, recordCount, 1089 unspecifiedFreeSpace, sdrLastAdd, 1090 sdrLastRemove, operationSupport); 1091 } 1092 1093 /** @brief implements the get SDR allocation info command 1094 * 1095 * @returns IPMI completion code plus response data 1096 * - allocUnits - Number of possible allocation units 1097 * - allocUnitSize - Allocation unit size in bytes. 1098 * - allocUnitFree - Number of free allocation units 1099 * - allocUnitLargestFree - Largest free block in allocation units 1100 * - maxRecordSize - Maximum record size in allocation units. 1101 */ 1102 ipmi::RspType<uint16_t, // allocUnits 1103 uint16_t, // allocUnitSize 1104 uint16_t, // allocUnitFree 1105 uint16_t, // allocUnitLargestFree 1106 uint8_t // maxRecordSize 1107 > 1108 ipmiStorageGetSDRAllocationInfo() 1109 { 1110 // 0000h unspecified number of alloc units 1111 constexpr uint16_t allocUnits = 0; 1112 1113 constexpr uint16_t allocUnitFree = 0; 1114 constexpr uint16_t allocUnitLargestFree = 0; 1115 // only allow one block at a time 1116 constexpr uint8_t maxRecordSize = 1; 1117 1118 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree, 1119 allocUnitLargestFree, maxRecordSize); 1120 } 1121 1122 /** @brief implements the reserve SDR command 1123 * @returns IPMI completion code plus response data 1124 * - sdrReservationID 1125 */ 1126 ipmi::RspType<uint16_t> ipmiStorageReserveSDR() 1127 { 1128 sdrReservationID++; 1129 if (sdrReservationID == 0) 1130 { 1131 sdrReservationID++; 1132 } 1133 1134 return ipmi::responseSuccess(sdrReservationID); 1135 } 1136 1137 ipmi::RspType<uint16_t, // next record ID 1138 std::vector<uint8_t> // payload 1139 > 1140 ipmiStorageGetSDR(uint16_t reservationID, uint16_t recordID, uint8_t offset, 1141 uint8_t bytesToRead) 1142 { 1143 constexpr uint16_t lastRecordIndex = 0xFFFF; 1144 1145 // reservation required for partial reads with non zero offset into 1146 // record 1147 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset) 1148 { 1149 return ipmi::responseInvalidReservationId(); 1150 } 1151 1152 if (sensorTree.empty() && !getSensorSubtree(sensorTree)) 1153 { 1154 return ipmi::responseResponseError(); 1155 } 1156 1157 size_t fruCount = 0; 1158 ipmi::Cc ret = ipmi::storage::getFruSdrCount(fruCount); 1159 if (ret != ipmi::ccSuccess) 1160 { 1161 return ipmi::response(ret); 1162 } 1163 1164 size_t lastRecord = 1165 sensorTree.size() + fruCount + ipmi::storage::type12Count - 1; 1166 if (recordID == lastRecordIndex) 1167 { 1168 recordID = lastRecord; 1169 } 1170 if (recordID > lastRecord) 1171 { 1172 return ipmi::responseInvalidFieldRequest(); 1173 } 1174 1175 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF; 1176 1177 if (recordID >= sensorTree.size()) 1178 { 1179 std::vector<uint8_t> recordData; 1180 size_t fruIndex = recordID - sensorTree.size(); 1181 if (fruIndex >= fruCount) 1182 { 1183 // handle type 12 hardcoded records 1184 size_t type12Index = fruIndex - fruCount; 1185 if (type12Index >= ipmi::storage::type12Count || 1186 offset > sizeof(Type12Record)) 1187 { 1188 return ipmi::responseInvalidFieldRequest(); 1189 } 1190 std::vector<uint8_t> record = 1191 ipmi::storage::getType12SDRs(type12Index, recordID); 1192 if (record.size() < (offset + bytesToRead)) 1193 { 1194 bytesToRead = record.size() - offset; 1195 } 1196 1197 recordData.insert(recordData.end(), record.begin() + offset, 1198 record.begin() + offset + bytesToRead); 1199 } 1200 else 1201 { 1202 // handle fru records 1203 get_sdr::SensorDataFruRecord data; 1204 if (offset > sizeof(data)) 1205 { 1206 return ipmi::responseInvalidFieldRequest(); 1207 } 1208 ret = ipmi::storage::getFruSdrs(fruIndex, data); 1209 if (ret != IPMI_CC_OK) 1210 { 1211 return ipmi::response(ret); 1212 } 1213 data.header.record_id_msb = recordID << 8; 1214 data.header.record_id_lsb = recordID & 0xFF; 1215 if (sizeof(data) < (offset + bytesToRead)) 1216 { 1217 bytesToRead = sizeof(data) - offset; 1218 } 1219 1220 uint8_t *respStart = reinterpret_cast<uint8_t *>(&data) + offset; 1221 recordData.insert(recordData.end(), respStart, 1222 respStart + bytesToRead); 1223 } 1224 1225 return ipmi::responseSuccess(nextRecordId, recordData); 1226 } 1227 1228 std::string connection; 1229 std::string path; 1230 uint16_t sensorIndex = recordID; 1231 for (const auto &sensor : sensorTree) 1232 { 1233 if (sensorIndex-- == 0) 1234 { 1235 if (!sensor.second.size()) 1236 { 1237 return ipmi::responseResponseError(); 1238 } 1239 connection = sensor.second.begin()->first; 1240 path = sensor.first; 1241 break; 1242 } 1243 } 1244 1245 SensorMap sensorMap; 1246 if (!getSensorMap(connection, path, sensorMap)) 1247 { 1248 return ipmi::responseResponseError(); 1249 } 1250 uint8_t sensornumber = (recordID & 0xFF); 1251 get_sdr::SensorDataFullRecord record = {0}; 1252 1253 record.header.record_id_msb = recordID << 8; 1254 record.header.record_id_lsb = recordID & 0xFF; 1255 record.header.sdr_version = ipmiSdrVersion; 1256 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD; 1257 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) - 1258 sizeof(get_sdr::SensorDataRecordHeader); 1259 record.key.owner_id = 0x20; 1260 record.key.owner_lun = 0x0; 1261 record.key.sensor_number = sensornumber; 1262 1263 record.body.entity_id = 0x0; 1264 record.body.entity_instance = 0x01; 1265 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis 1266 record.body.sensor_type = getSensorTypeFromPath(path); 1267 std::string type = getSensorTypeStringFromPath(path); 1268 auto typeCstr = type.c_str(); 1269 auto findUnits = sensorUnits.find(typeCstr); 1270 if (findUnits != sensorUnits.end()) 1271 { 1272 record.body.sensor_units_2_base = 1273 static_cast<uint8_t>(findUnits->second); 1274 } // else default 0x0 unspecified 1275 1276 record.body.event_reading_type = getSensorEventTypeFromPath(path); 1277 1278 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value"); 1279 if (sensorObject == sensorMap.end()) 1280 { 1281 return ipmi::responseResponseError(); 1282 } 1283 1284 auto maxObject = sensorObject->second.find("MaxValue"); 1285 auto minObject = sensorObject->second.find("MinValue"); 1286 double max = 128; 1287 double min = -127; 1288 if (maxObject != sensorObject->second.end()) 1289 { 1290 max = std::visit(VariantToDoubleVisitor(), maxObject->second); 1291 } 1292 1293 if (minObject != sensorObject->second.end()) 1294 { 1295 min = std::visit(VariantToDoubleVisitor(), minObject->second); 1296 } 1297 1298 int16_t mValue = 0; 1299 int8_t rExp = 0; 1300 int16_t bValue = 0; 1301 int8_t bExp = 0; 1302 bool bSigned = false; 1303 1304 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 1305 { 1306 return ipmi::responseResponseError(); 1307 } 1308 1309 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4 1310 record.body.m_lsb = mValue & 0xFF; 1311 1312 // move the smallest bit of the MSB into place (bit 9) 1313 // the MSbs are bits 7:8 in m_msb_and_tolerance 1314 uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0; 1315 1316 // assign the negative 1317 if (mValue < 0) 1318 { 1319 mMsb |= (1 << 7); 1320 } 1321 record.body.m_msb_and_tolerance = mMsb; 1322 1323 record.body.b_lsb = bValue & 0xFF; 1324 1325 // move the smallest bit of the MSB into place 1326 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb 1327 uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0; 1328 1329 // assign the negative 1330 if (bValue < 0) 1331 { 1332 bMsb |= (1 << 7); 1333 } 1334 record.body.b_msb_and_accuracy_lsb = bMsb; 1335 1336 record.body.r_b_exponents = bExp & 0x7; 1337 if (bExp < 0) 1338 { 1339 record.body.r_b_exponents |= 1 << 3; 1340 } 1341 record.body.r_b_exponents = (rExp & 0x7) << 4; 1342 if (rExp < 0) 1343 { 1344 record.body.r_b_exponents |= 1 << 7; 1345 } 1346 1347 // todo fill out rest of units 1348 if (bSigned) 1349 { 1350 record.body.sensor_units_1 = 1 << 7; 1351 } 1352 1353 // populate sensor name from path 1354 std::string name; 1355 size_t nameStart = path.rfind("/"); 1356 if (nameStart != std::string::npos) 1357 { 1358 name = path.substr(nameStart + 1, std::string::npos - nameStart); 1359 } 1360 1361 std::replace(name.begin(), name.end(), '_', ' '); 1362 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH) 1363 { 1364 // try to not truncate by replacing common words 1365 constexpr std::array<std::pair<const char *, const char *>, 2> 1366 replaceWords = {std::make_pair("Output", "Out"), 1367 std::make_pair("Input", "In")}; 1368 for (const auto &[find, replace] : replaceWords) 1369 { 1370 boost::replace_all(name, find, replace); 1371 } 1372 1373 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH); 1374 } 1375 record.body.id_string_info = name.size(); 1376 std::strncpy(record.body.id_string, name.c_str(), 1377 sizeof(record.body.id_string)); 1378 1379 IPMIThresholds thresholdData; 1380 try 1381 { 1382 thresholdData = getIPMIThresholds(sensorMap); 1383 } 1384 catch (std::exception &) 1385 { 1386 return ipmi::responseResponseError(); 1387 } 1388 1389 if (thresholdData.criticalHigh) 1390 { 1391 record.body.upper_critical_threshold = *thresholdData.criticalHigh; 1392 record.body.supported_deassertions[1] |= static_cast<uint8_t>( 1393 IPMISensorEventEnableThresholds::upperCriticalGoingHigh); 1394 record.body.supported_assertions[1] |= static_cast<uint8_t>( 1395 IPMISensorEventEnableThresholds::upperCriticalGoingHigh); 1396 record.body.discrete_reading_setting_mask[0] |= 1397 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical); 1398 } 1399 if (thresholdData.warningHigh) 1400 { 1401 record.body.upper_noncritical_threshold = *thresholdData.warningHigh; 1402 record.body.supported_deassertions[0] |= static_cast<uint8_t>( 1403 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh); 1404 record.body.supported_assertions[0] |= static_cast<uint8_t>( 1405 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh); 1406 record.body.discrete_reading_setting_mask[0] |= 1407 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical); 1408 } 1409 if (thresholdData.criticalLow) 1410 { 1411 record.body.lower_critical_threshold = *thresholdData.criticalLow; 1412 record.body.supported_deassertions[0] |= static_cast<uint8_t>( 1413 IPMISensorEventEnableThresholds::lowerCriticalGoingLow); 1414 record.body.supported_assertions[0] |= static_cast<uint8_t>( 1415 IPMISensorEventEnableThresholds::lowerCriticalGoingLow); 1416 record.body.discrete_reading_setting_mask[0] |= 1417 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical); 1418 } 1419 if (thresholdData.warningLow) 1420 { 1421 record.body.lower_noncritical_threshold = *thresholdData.warningLow; 1422 record.body.supported_deassertions[0] |= static_cast<uint8_t>( 1423 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow); 1424 record.body.supported_assertions[0] |= static_cast<uint8_t>( 1425 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow); 1426 record.body.discrete_reading_setting_mask[0] |= 1427 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical); 1428 } 1429 1430 // everything that is readable is setable 1431 record.body.discrete_reading_setting_mask[1] = 1432 record.body.discrete_reading_setting_mask[0]; 1433 1434 if (sizeof(get_sdr::SensorDataFullRecord) < (offset + bytesToRead)) 1435 { 1436 bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - offset; 1437 } 1438 1439 uint8_t *respStart = reinterpret_cast<uint8_t *>(&record) + offset; 1440 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead); 1441 1442 return ipmi::responseSuccess(nextRecordId, recordData); 1443 } 1444 /* end storage commands */ 1445 1446 void registerSensorFunctions() 1447 { 1448 // get firmware version information 1449 ipmiPrintAndRegister(NETFUN_SENSOR, IPMI_CMD_WILDCARD, nullptr, 1450 ipmiSensorWildcardHandler, PRIVILEGE_USER); 1451 1452 // <Get Sensor Type> 1453 ipmiPrintAndRegister( 1454 NETFUN_SENSOR, 1455 static_cast<ipmi_cmd_t>(IPMINetfnSensorCmds::ipmiCmdGetSensorType), 1456 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_USER); 1457 1458 // <Set Sensor Reading and Event Status> 1459 ipmiPrintAndRegister( 1460 NETFUN_SENSOR, 1461 static_cast<ipmi_cmd_t>( 1462 IPMINetfnSensorCmds::ipmiCmdSetSensorReadingAndEventStatus), 1463 nullptr, ipmiSensorWildcardHandler, PRIVILEGE_OPERATOR); 1464 1465 // <Platform Event> 1466 ipmi::registerHandler( 1467 ipmi::prioOemBase, ipmi::netFnSensor, 1468 static_cast<ipmi::Cmd>(ipmi::sensor_event::cmdPlatformEvent), 1469 ipmi::Privilege::Operator, ipmiSenPlatformEvent); 1470 1471 // <Get Sensor Reading> 1472 ipmi::registerHandler( 1473 ipmi::prioOemBase, NETFUN_SENSOR, 1474 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetSensorReading), 1475 ipmi::Privilege::User, ipmiSenGetSensorReading); 1476 1477 // <Get Sensor Threshold> 1478 ipmi::registerHandler( 1479 ipmi::prioOemBase, NETFUN_SENSOR, 1480 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetSensorThreshold), 1481 ipmi::Privilege::User, ipmiSenGetSensorThresholds); 1482 1483 // <Set Sensor Threshold> 1484 ipmi::registerHandler( 1485 ipmi::prioOemBase, NETFUN_SENSOR, 1486 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdSetSensorThreshold), 1487 ipmi::Privilege::Operator, ipmiSenSetSensorThresholds); 1488 1489 // <Get Sensor Event Enable> 1490 ipmi::registerHandler(ipmi::prioOemBase, NETFUN_SENSOR, 1491 static_cast<ipmi::Cmd>( 1492 IPMINetfnSensorCmds::ipmiCmdGetSensorEventEnable), 1493 ipmi::Privilege::User, ipmiSenGetSensorEventEnable); 1494 1495 // <Get Sensor Event Status> 1496 ipmiPrintAndRegister(NETFUN_SENSOR, 1497 static_cast<ipmi_cmd_t>( 1498 IPMINetfnSensorCmds::ipmiCmdGetSensorEventStatus), 1499 nullptr, ipmiSenGetSensorEventStatus, PRIVILEGE_USER); 1500 1501 // register all storage commands for both Sensor and Storage command 1502 // versions 1503 1504 // <Get SDR Repository Info> 1505 ipmi::registerHandler( 1506 ipmi::prioOemBase, NETFUN_STORAGE, 1507 static_cast<ipmi::Cmd>(IPMINetfnStorageCmds::ipmiCmdGetRepositoryInfo), 1508 ipmi::Privilege::User, ipmiStorageGetSDRRepositoryInfo); 1509 1510 // <Get SDR Allocation Info> 1511 ipmi::registerHandler( 1512 ipmi::prioOemBase, NETFUN_STORAGE, 1513 static_cast<ipmi::Cmd>( 1514 IPMINetfnStorageCmds::ipmiCmdGetSDRAllocationInfo), 1515 ipmi::Privilege::User, ipmiStorageGetSDRAllocationInfo); 1516 1517 // <Reserve SDR Repo> 1518 ipmi::registerHandler(ipmi::prioOemBase, NETFUN_SENSOR, 1519 static_cast<ipmi::Cmd>( 1520 IPMINetfnSensorCmds::ipmiCmdReserveDeviceSDRRepo), 1521 ipmi::Privilege::User, ipmiStorageReserveSDR); 1522 1523 ipmi::registerHandler( 1524 ipmi::prioOemBase, NETFUN_STORAGE, 1525 static_cast<ipmi::Cmd>(IPMINetfnStorageCmds::ipmiCmdReserveSDR), 1526 ipmi::Privilege::User, ipmiStorageReserveSDR); 1527 1528 // <Get Sdr> 1529 ipmi::registerHandler( 1530 ipmi::prioOemBase, NETFUN_SENSOR, 1531 static_cast<ipmi::Cmd>(IPMINetfnSensorCmds::ipmiCmdGetDeviceSDR), 1532 ipmi::Privilege::User, ipmiStorageGetSDR); 1533 1534 ipmi::registerHandler( 1535 ipmi::prioOemBase, NETFUN_STORAGE, 1536 static_cast<ipmi::Cmd>(IPMINetfnStorageCmds::ipmiCmdGetSDR), 1537 ipmi::Privilege::User, ipmiStorageGetSDR); 1538 1539 return; 1540 } 1541 } // namespace ipmi 1542