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