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