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