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 "dbus-sdr/sensorcommands.hpp" 18 19 #include "dbus-sdr/sdrutils.hpp" 20 #include "dbus-sdr/sensorutils.hpp" 21 #include "dbus-sdr/storagecommands.hpp" 22 23 #include <algorithm> 24 #include <array> 25 #include <boost/algorithm/string.hpp> 26 #include <boost/container/flat_map.hpp> 27 #include <chrono> 28 #include <cmath> 29 #include <cstring> 30 #include <iostream> 31 #include <ipmid/api.hpp> 32 #include <ipmid/types.hpp> 33 #include <ipmid/utils.hpp> 34 #include <map> 35 #include <memory> 36 #include <optional> 37 #include <phosphor-logging/log.hpp> 38 #include <sdbusplus/bus.hpp> 39 #include <stdexcept> 40 #include <string> 41 #include <utility> 42 #include <variant> 43 44 #ifdef FEATURE_HYBRID_SENSORS 45 46 #include "sensordatahandler.hpp" 47 namespace ipmi 48 { 49 namespace sensor 50 { 51 extern const IdInfoMap sensors; 52 } // namespace sensor 53 } // namespace ipmi 54 #endif 55 56 constexpr std::array<const char*, 7> suffixes = { 57 "_Output_Voltage", "_Input_Voltage", "_Output_Current", "_Input_Current", 58 "_Output_Power", "_Input_Power", "_Temperature"}; 59 namespace ipmi 60 { 61 62 using phosphor::logging::entry; 63 using phosphor::logging::level; 64 using phosphor::logging::log; 65 66 static constexpr int sensorMapUpdatePeriod = 10; 67 static constexpr int sensorMapSdrUpdatePeriod = 60; 68 69 // BMC I2C address is generally at 0x20 70 static constexpr uint8_t bmcI2CAddr = 0x20; 71 72 constexpr size_t maxSDRTotalSize = 73 76; // Largest SDR Record Size (type 01) + SDR Overheader Size 74 constexpr static const uint32_t noTimestamp = 0xFFFFFFFF; 75 76 static uint16_t sdrReservationID; 77 static uint32_t sdrLastAdd = noTimestamp; 78 static uint32_t sdrLastRemove = noTimestamp; 79 static constexpr size_t lastRecordIndex = 0xFFFF; 80 static constexpr int GENERAL_ERROR = -1; 81 82 static boost::container::flat_map<std::string, ObjectValueTree> SensorCache; 83 84 // Specify the comparison required to sort and find char* map objects 85 struct CmpStr 86 { 87 bool operator()(const char* a, const char* b) const 88 { 89 return std::strcmp(a, b) < 0; 90 } 91 }; 92 const static boost::container::flat_map<const char*, SensorUnits, CmpStr> 93 sensorUnits{{{"temperature", SensorUnits::degreesC}, 94 {"voltage", SensorUnits::volts}, 95 {"current", SensorUnits::amps}, 96 {"fan_tach", SensorUnits::rpm}, 97 {"power", SensorUnits::watts}}}; 98 99 void registerSensorFunctions() __attribute__((constructor)); 100 101 static sdbusplus::bus::match::match sensorAdded( 102 *getSdBus(), 103 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/" 104 "sensors/'", 105 [](sdbusplus::message::message& m) { 106 getSensorTree().clear(); 107 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>( 108 std::chrono::system_clock::now().time_since_epoch()) 109 .count(); 110 }); 111 112 static sdbusplus::bus::match::match sensorRemoved( 113 *getSdBus(), 114 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/" 115 "sensors/'", 116 [](sdbusplus::message::message& m) { 117 getSensorTree().clear(); 118 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>( 119 std::chrono::system_clock::now().time_since_epoch()) 120 .count(); 121 }); 122 123 // this keeps track of deassertions for sensor event status command. A 124 // deasertion can only happen if an assertion was seen first. 125 static boost::container::flat_map< 126 std::string, boost::container::flat_map<std::string, std::optional<bool>>> 127 thresholdDeassertMap; 128 129 static sdbusplus::bus::match::match thresholdChanged( 130 *getSdBus(), 131 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus." 132 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'", 133 [](sdbusplus::message::message& m) { 134 boost::container::flat_map<std::string, std::variant<bool, double>> 135 values; 136 m.read(std::string(), values); 137 138 auto findAssert = 139 std::find_if(values.begin(), values.end(), [](const auto& pair) { 140 return pair.first.find("Alarm") != std::string::npos; 141 }); 142 if (findAssert != values.end()) 143 { 144 auto ptr = std::get_if<bool>(&(findAssert->second)); 145 if (ptr == nullptr) 146 { 147 phosphor::logging::log<phosphor::logging::level::ERR>( 148 "thresholdChanged: Assert non bool"); 149 return; 150 } 151 if (*ptr) 152 { 153 phosphor::logging::log<phosphor::logging::level::INFO>( 154 "thresholdChanged: Assert", 155 phosphor::logging::entry("SENSOR=%s", m.get_path())); 156 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr; 157 } 158 else 159 { 160 auto& value = 161 thresholdDeassertMap[m.get_path()][findAssert->first]; 162 if (value) 163 { 164 phosphor::logging::log<phosphor::logging::level::INFO>( 165 "thresholdChanged: deassert", 166 phosphor::logging::entry("SENSOR=%s", m.get_path())); 167 value = *ptr; 168 } 169 } 170 } 171 }); 172 173 namespace sensor 174 { 175 static constexpr const char* vrInterface = 176 "xyz.openbmc_project.Control.VoltageRegulatorMode"; 177 static constexpr const char* sensorInterface = 178 "xyz.openbmc_project.Sensor.Value"; 179 } // namespace sensor 180 181 static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max, 182 double& min) 183 { 184 max = 127; 185 min = -128; 186 187 auto sensorObject = sensorMap.find(sensor::sensorInterface); 188 auto critical = 189 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 190 auto warning = 191 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 192 193 if (sensorObject != sensorMap.end()) 194 { 195 auto maxMap = sensorObject->second.find("MaxValue"); 196 auto minMap = sensorObject->second.find("MinValue"); 197 198 if (maxMap != sensorObject->second.end()) 199 { 200 max = std::visit(VariantToDoubleVisitor(), maxMap->second); 201 } 202 if (minMap != sensorObject->second.end()) 203 { 204 min = std::visit(VariantToDoubleVisitor(), minMap->second); 205 } 206 } 207 if (critical != sensorMap.end()) 208 { 209 auto lower = critical->second.find("CriticalLow"); 210 auto upper = critical->second.find("CriticalHigh"); 211 if (lower != critical->second.end()) 212 { 213 double value = std::visit(VariantToDoubleVisitor(), lower->second); 214 min = std::min(value, min); 215 } 216 if (upper != critical->second.end()) 217 { 218 double value = std::visit(VariantToDoubleVisitor(), upper->second); 219 max = std::max(value, max); 220 } 221 } 222 if (warning != sensorMap.end()) 223 { 224 225 auto lower = warning->second.find("WarningLow"); 226 auto upper = warning->second.find("WarningHigh"); 227 if (lower != warning->second.end()) 228 { 229 double value = std::visit(VariantToDoubleVisitor(), lower->second); 230 min = std::min(value, min); 231 } 232 if (upper != warning->second.end()) 233 { 234 double value = std::visit(VariantToDoubleVisitor(), upper->second); 235 max = std::max(value, max); 236 } 237 } 238 } 239 240 static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection, 241 std::string sensorPath, DbusInterfaceMap& sensorMap, 242 int updatePeriod = sensorMapUpdatePeriod) 243 { 244 #ifdef FEATURE_HYBRID_SENSORS 245 if (auto sensor = findStaticSensor(sensorPath); 246 sensor != ipmi::sensor::sensors.end() && 247 getSensorEventTypeFromPath(sensorPath) != 248 static_cast<uint8_t>(SensorEventTypeCodes::threshold)) 249 { 250 // If the incoming sensor is a discrete sensor, it might fail in 251 // getManagedObjects(), return true, and use its own getFunc to get 252 // value. 253 return true; 254 } 255 #endif 256 257 static boost::container::flat_map< 258 std::string, std::chrono::time_point<std::chrono::steady_clock>> 259 updateTimeMap; 260 261 auto updateFind = updateTimeMap.find(sensorConnection); 262 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>(); 263 if (updateFind != updateTimeMap.end()) 264 { 265 lastUpdate = updateFind->second; 266 } 267 268 auto now = std::chrono::steady_clock::now(); 269 270 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate) 271 .count() > updatePeriod) 272 { 273 ObjectValueTree managedObjects; 274 boost::system::error_code ec = getManagedObjects( 275 ctx, sensorConnection.c_str(), "/", managedObjects); 276 if (ec) 277 { 278 phosphor::logging::log<phosphor::logging::level::ERR>( 279 "GetMangagedObjects for getSensorMap failed", 280 phosphor::logging::entry("ERROR=%s", ec.message().c_str())); 281 282 return false; 283 } 284 285 SensorCache[sensorConnection] = managedObjects; 286 // Update time after finish building the map which allow the 287 // data to be cached for updatePeriod plus the build time. 288 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now(); 289 } 290 auto connection = SensorCache.find(sensorConnection); 291 if (connection == SensorCache.end()) 292 { 293 return false; 294 } 295 auto path = connection->second.find(sensorPath); 296 if (path == connection->second.end()) 297 { 298 return false; 299 } 300 sensorMap = path->second; 301 302 return true; 303 } 304 305 namespace sensor 306 { 307 // Read VR profiles from sensor(daemon) interface 308 static std::optional<std::vector<std::string>> 309 getSupportedVrProfiles(const ipmi::DbusInterfaceMap::mapped_type& object) 310 { 311 // get VR mode profiles from Supported Interface 312 auto supportedProperty = object.find("Supported"); 313 if (supportedProperty == object.end() || 314 object.find("Selected") == object.end()) 315 { 316 phosphor::logging::log<phosphor::logging::level::ERR>( 317 "Missing the required Supported and Selected properties"); 318 return std::nullopt; 319 } 320 321 const auto profilesPtr = 322 std::get_if<std::vector<std::string>>(&supportedProperty->second); 323 324 if (profilesPtr == nullptr) 325 { 326 phosphor::logging::log<phosphor::logging::level::ERR>( 327 "property is not array of string"); 328 return std::nullopt; 329 } 330 return *profilesPtr; 331 } 332 333 // Calculate VR Mode from input IPMI discrete event bytes 334 static std::optional<std::string> 335 calculateVRMode(uint15_t assertOffset, 336 const ipmi::DbusInterfaceMap::mapped_type& VRObject) 337 { 338 // get VR mode profiles from Supported Interface 339 auto profiles = getSupportedVrProfiles(VRObject); 340 if (!profiles) 341 { 342 return std::nullopt; 343 } 344 345 // interpret IPMI cmd bits into profiles' index 346 long unsigned int index = 0; 347 // only one bit should be set and the highest bit should not be used. 348 if (assertOffset == 0 || assertOffset == (1u << 15) || 349 (assertOffset & (assertOffset - 1))) 350 { 351 phosphor::logging::log<phosphor::logging::level::ERR>( 352 "IPMI cmd format incorrect", 353 354 phosphor::logging::entry("BYTES=%#02x", 355 static_cast<uint16_t>(assertOffset))); 356 return std::nullopt; 357 } 358 359 while (assertOffset != 1) 360 { 361 assertOffset >>= 1; 362 index++; 363 } 364 365 if (index >= profiles->size()) 366 { 367 phosphor::logging::log<phosphor::logging::level::ERR>( 368 "profile index out of boundary"); 369 return std::nullopt; 370 } 371 372 return profiles->at(index); 373 } 374 375 // Calculate sensor value from IPMI reading byte 376 static std::optional<double> 377 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap, 378 const ipmi::DbusInterfaceMap::mapped_type& valueObject) 379 { 380 if (valueObject.find("Value") == valueObject.end()) 381 { 382 phosphor::logging::log<phosphor::logging::level::ERR>( 383 "Missing the required Value property"); 384 return std::nullopt; 385 } 386 387 double max = 0; 388 double min = 0; 389 getSensorMaxMin(sensorMap, max, min); 390 391 int16_t mValue = 0; 392 int16_t bValue = 0; 393 int8_t rExp = 0; 394 int8_t bExp = 0; 395 bool bSigned = false; 396 397 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 398 { 399 return std::nullopt; 400 } 401 402 double value = bSigned ? ((int8_t)reading) : reading; 403 404 value *= ((double)mValue); 405 value += ((double)bValue) * std::pow(10.0, bExp); 406 value *= std::pow(10.0, rExp); 407 408 return value; 409 } 410 411 // Extract file name from sensor path as the sensors SDR ID. Simplify the name 412 // if it is too long. 413 std::string parseSdrIdFromPath(const std::string& path) 414 { 415 std::string name; 416 size_t nameStart = path.rfind("/"); 417 if (nameStart != std::string::npos) 418 { 419 name = path.substr(nameStart + 1, std::string::npos - nameStart); 420 } 421 422 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH) 423 { 424 // try to not truncate by replacing common words 425 for (const auto& suffix : suffixes) 426 { 427 if (boost::ends_with(name, suffix)) 428 { 429 boost::replace_all(name, suffix, ""); 430 break; 431 } 432 } 433 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH); 434 } 435 std::replace(name.begin(), name.end(), '_', ' '); 436 return name; 437 } 438 439 bool getVrEventStatus(ipmi::Context::ptr ctx, const std::string& connection, 440 const std::string& path, 441 const ipmi::DbusInterfaceMap::mapped_type& object, 442 std::bitset<16>& assertions) 443 { 444 auto profiles = sensor::getSupportedVrProfiles(object); 445 if (!profiles) 446 { 447 return false; 448 } 449 ipmi::Value modeVariant; 450 451 auto ec = getDbusProperty(ctx, connection, path, sensor::vrInterface, 452 "Selected", modeVariant); 453 if (ec) 454 { 455 log<level::ERR>("Failed to get property", 456 entry("PROPERTY=%s", "Selected"), 457 entry("PATH=%s", path.c_str()), 458 entry("INTERFACE=%s", sensor::sensorInterface), 459 entry("WHAT=%s", ec.message().c_str())); 460 return false; 461 } 462 463 auto mode = std::get_if<std::string>(&modeVariant); 464 if (mode == nullptr) 465 { 466 log<level::ERR>("property is not a string", 467 entry("PROPERTY=%s", "Selected"), 468 entry("PATH=%s", path.c_str()), 469 entry("INTERFACE=%s", sensor::sensorInterface)); 470 return false; 471 } 472 473 auto itr = std::find(profiles->begin(), profiles->end(), *mode); 474 if (itr == profiles->end()) 475 { 476 using namespace phosphor::logging; 477 log<level::ERR>("VR mode doesn't match any of its profiles", 478 entry("PATH=%s", path.c_str())); 479 return false; 480 } 481 std::size_t index = 482 static_cast<std::size_t>(std::distance(profiles->begin(), itr)); 483 484 // map index to reponse event assertion bit. 485 if (index < 8) 486 { 487 assertions.set(1u << index); 488 } 489 else if (index < 15) 490 { 491 assertions.set(1u << (index - 8)); 492 } 493 else 494 { 495 log<level::ERR>("VR profile index reaches max assertion bit", 496 entry("PATH=%s", path.c_str()), 497 entry("INDEX=%uz", index)); 498 return false; 499 } 500 if constexpr (debug) 501 { 502 std::cerr << "VR sensor " << sensor::parseSdrIdFromPath(path) 503 << " mode is: [" << index << "] " << *mode << std::endl; 504 } 505 return true; 506 } 507 } // namespace sensor 508 509 ipmi::RspType<> ipmiSenPlatformEvent(uint8_t generatorID, uint8_t evmRev, 510 uint8_t sensorType, uint8_t sensorNum, 511 uint8_t eventType, uint8_t eventData1, 512 std::optional<uint8_t> eventData2, 513 std::optional<uint8_t> eventData3) 514 { 515 return ipmi::responseSuccess(); 516 } 517 518 ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx, 519 uint8_t sensorNumber, uint8_t operation, 520 uint8_t reading, uint15_t assertOffset, 521 bool resvd1, uint15_t deassertOffset, 522 bool resvd2, uint8_t eventData1, 523 uint8_t eventData2, uint8_t eventData3) 524 { 525 std::string connection; 526 std::string path; 527 std::vector<std::string> interfaces; 528 529 ipmi::Cc status = 530 getSensorConnection(ctx, sensorNumber, connection, path, &interfaces); 531 if (status) 532 { 533 return ipmi::response(status); 534 } 535 536 // we can tell the sensor type by its interface type 537 if (std::find(interfaces.begin(), interfaces.end(), 538 sensor::sensorInterface) != interfaces.end()) 539 { 540 DbusInterfaceMap sensorMap; 541 if (!getSensorMap(ctx, connection, path, sensorMap)) 542 { 543 return ipmi::responseResponseError(); 544 } 545 auto sensorObject = sensorMap.find(sensor::sensorInterface); 546 if (sensorObject == sensorMap.end()) 547 { 548 return ipmi::responseResponseError(); 549 } 550 551 // Only allow external SetSensor if write permission granted 552 if (!details::sdrWriteTable.getWritePermission(sensorNumber)) 553 { 554 return ipmi::responseResponseError(); 555 } 556 557 auto value = 558 sensor::calculateValue(reading, sensorMap, sensorObject->second); 559 if (!value) 560 { 561 return ipmi::responseResponseError(); 562 } 563 564 if constexpr (debug) 565 { 566 phosphor::logging::log<phosphor::logging::level::INFO>( 567 "IPMI SET_SENSOR", 568 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber), 569 phosphor::logging::entry("BYTE=%u", (unsigned int)reading), 570 phosphor::logging::entry("VALUE=%f", *value)); 571 } 572 573 boost::system::error_code ec = 574 setDbusProperty(ctx, connection, path, sensor::sensorInterface, 575 "Value", ipmi::Value(*value)); 576 577 // setDbusProperty intended to resolve dbus exception/rc within the 578 // function but failed to achieve that. Catch exception in the ipmi 579 // callback functions for now (e.g. ipmiSetSensorReading). 580 if (ec) 581 { 582 using namespace phosphor::logging; 583 log<level::ERR>("Failed to set property", 584 entry("PROPERTY=%s", "Value"), 585 entry("PATH=%s", path.c_str()), 586 entry("INTERFACE=%s", sensor::sensorInterface), 587 entry("WHAT=%s", ec.message().c_str())); 588 return ipmi::responseResponseError(); 589 } 590 return ipmi::responseSuccess(); 591 } 592 593 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) != 594 interfaces.end()) 595 { 596 DbusInterfaceMap sensorMap; 597 if (!getSensorMap(ctx, connection, path, sensorMap)) 598 { 599 return ipmi::responseResponseError(); 600 } 601 auto sensorObject = sensorMap.find(sensor::vrInterface); 602 if (sensorObject == sensorMap.end()) 603 { 604 return ipmi::responseResponseError(); 605 } 606 607 // VR sensors are treated as a special case and we will not check the 608 // write permission for VR sensors, since they always deemed writable 609 // and permission table are not applied to VR sensors. 610 auto vrMode = 611 sensor::calculateVRMode(assertOffset, sensorObject->second); 612 if (!vrMode) 613 { 614 return ipmi::responseResponseError(); 615 } 616 boost::system::error_code ec = setDbusProperty( 617 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode); 618 // setDbusProperty intended to resolve dbus exception/rc within the 619 // function but failed to achieve that. Catch exception in the ipmi 620 // callback functions for now (e.g. ipmiSetSensorReading). 621 if (ec) 622 { 623 using namespace phosphor::logging; 624 log<level::ERR>("Failed to set property", 625 entry("PROPERTY=%s", "Selected"), 626 entry("PATH=%s", path.c_str()), 627 entry("INTERFACE=%s", sensor::sensorInterface), 628 entry("WHAT=%s", ec.message().c_str())); 629 return ipmi::responseResponseError(); 630 } 631 return ipmi::responseSuccess(); 632 } 633 634 phosphor::logging::log<phosphor::logging::level::ERR>( 635 "unknown sensor type", 636 phosphor::logging::entry("PATH=%s", path.c_str())); 637 return ipmi::responseResponseError(); 638 } 639 640 ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>> 641 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum) 642 { 643 std::string connection; 644 std::string path; 645 646 if (sensnum == reservedSensorNumber) 647 { 648 return ipmi::responseInvalidFieldRequest(); 649 } 650 651 auto status = getSensorConnection(ctx, sensnum, connection, path); 652 if (status) 653 { 654 return ipmi::response(status); 655 } 656 657 #ifdef FEATURE_HYBRID_SENSORS 658 if (auto sensor = findStaticSensor(path); 659 sensor != ipmi::sensor::sensors.end() && 660 getSensorEventTypeFromPath(path) != 661 static_cast<uint8_t>(SensorEventTypeCodes::threshold)) 662 { 663 if (ipmi::sensor::Mutability::Read != 664 (sensor->second.mutability & ipmi::sensor::Mutability::Read)) 665 { 666 return ipmi::responseIllegalCommand(); 667 } 668 669 uint8_t operation; 670 try 671 { 672 ipmi::sensor::GetSensorResponse getResponse = 673 sensor->second.getFunc(sensor->second); 674 675 if (getResponse.readingOrStateUnavailable) 676 { 677 operation |= static_cast<uint8_t>( 678 IPMISensorReadingByte2::readingStateUnavailable); 679 } 680 if (getResponse.scanningEnabled) 681 { 682 operation |= static_cast<uint8_t>( 683 IPMISensorReadingByte2::sensorScanningEnable); 684 } 685 if (getResponse.allEventMessagesEnabled) 686 { 687 operation |= static_cast<uint8_t>( 688 IPMISensorReadingByte2::eventMessagesEnable); 689 } 690 return ipmi::responseSuccess( 691 getResponse.reading, operation, 692 getResponse.thresholdLevelsStates, 693 getResponse.discreteReadingSensorStates); 694 } 695 catch (const std::exception& e) 696 { 697 operation |= static_cast<uint8_t>( 698 IPMISensorReadingByte2::readingStateUnavailable); 699 return ipmi::responseSuccess(0, operation, 0, std::nullopt); 700 } 701 } 702 #endif 703 704 DbusInterfaceMap sensorMap; 705 if (!getSensorMap(ctx, connection, path, sensorMap)) 706 { 707 return ipmi::responseResponseError(); 708 } 709 auto sensorObject = sensorMap.find(sensor::sensorInterface); 710 711 if (sensorObject == sensorMap.end() || 712 sensorObject->second.find("Value") == sensorObject->second.end()) 713 { 714 return ipmi::responseResponseError(); 715 } 716 auto& valueVariant = sensorObject->second["Value"]; 717 double reading = std::visit(VariantToDoubleVisitor(), valueVariant); 718 719 double max = 0; 720 double min = 0; 721 getSensorMaxMin(sensorMap, max, min); 722 723 int16_t mValue = 0; 724 int16_t bValue = 0; 725 int8_t rExp = 0; 726 int8_t bExp = 0; 727 bool bSigned = false; 728 729 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 730 { 731 return ipmi::responseResponseError(); 732 } 733 734 uint8_t value = 735 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned); 736 uint8_t operation = 737 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable); 738 operation |= 739 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable); 740 bool notReading = std::isnan(reading); 741 742 if (!notReading) 743 { 744 auto availableObject = 745 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability"); 746 if (availableObject != sensorMap.end()) 747 { 748 auto findAvailable = availableObject->second.find("Available"); 749 if (findAvailable != availableObject->second.end()) 750 { 751 bool* available = std::get_if<bool>(&(findAvailable->second)); 752 if (available && !(*available)) 753 { 754 notReading = true; 755 } 756 } 757 } 758 } 759 760 if (notReading) 761 { 762 operation |= static_cast<uint8_t>( 763 IPMISensorReadingByte2::readingStateUnavailable); 764 } 765 766 if constexpr (details::enableInstrumentation) 767 { 768 int byteValue; 769 if (bSigned) 770 { 771 byteValue = static_cast<int>(static_cast<int8_t>(value)); 772 } 773 else 774 { 775 byteValue = static_cast<int>(static_cast<uint8_t>(value)); 776 } 777 778 // Keep stats on the reading just obtained, even if it is "NaN" 779 if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue)) 780 { 781 // This is the first reading, show the coefficients 782 double step = (max - min) / 255.0; 783 std::cerr << "IPMI sensor " 784 << details::sdrStatsTable.getName(sensnum) 785 << ": Range min=" << min << " max=" << max 786 << ", step=" << step 787 << ", Coefficients mValue=" << static_cast<int>(mValue) 788 << " rExp=" << static_cast<int>(rExp) 789 << " bValue=" << static_cast<int>(bValue) 790 << " bExp=" << static_cast<int>(bExp) 791 << " bSigned=" << static_cast<int>(bSigned) << "\n"; 792 } 793 } 794 795 uint8_t thresholds = 0; 796 797 auto warningObject = 798 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 799 if (warningObject != sensorMap.end()) 800 { 801 auto alarmHigh = warningObject->second.find("WarningAlarmHigh"); 802 auto alarmLow = warningObject->second.find("WarningAlarmLow"); 803 if (alarmHigh != warningObject->second.end()) 804 { 805 if (std::get<bool>(alarmHigh->second)) 806 { 807 thresholds |= static_cast<uint8_t>( 808 IPMISensorReadingByte3::upperNonCritical); 809 } 810 } 811 if (alarmLow != warningObject->second.end()) 812 { 813 if (std::get<bool>(alarmLow->second)) 814 { 815 thresholds |= static_cast<uint8_t>( 816 IPMISensorReadingByte3::lowerNonCritical); 817 } 818 } 819 } 820 821 auto criticalObject = 822 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 823 if (criticalObject != sensorMap.end()) 824 { 825 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh"); 826 auto alarmLow = criticalObject->second.find("CriticalAlarmLow"); 827 if (alarmHigh != criticalObject->second.end()) 828 { 829 if (std::get<bool>(alarmHigh->second)) 830 { 831 thresholds |= 832 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical); 833 } 834 } 835 if (alarmLow != criticalObject->second.end()) 836 { 837 if (std::get<bool>(alarmLow->second)) 838 { 839 thresholds |= 840 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical); 841 } 842 } 843 } 844 845 // no discrete as of today so optional byte is never returned 846 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt); 847 } 848 849 /** @brief implements the Set Sensor threshold command 850 * @param sensorNumber - sensor number 851 * @param lowerNonCriticalThreshMask 852 * @param lowerCriticalThreshMask 853 * @param lowerNonRecovThreshMask 854 * @param upperNonCriticalThreshMask 855 * @param upperCriticalThreshMask 856 * @param upperNonRecovThreshMask 857 * @param reserved 858 * @param lowerNonCritical - lower non-critical threshold 859 * @param lowerCritical - Lower critical threshold 860 * @param lowerNonRecoverable - Lower non recovarable threshold 861 * @param upperNonCritical - Upper non-critical threshold 862 * @param upperCritical - Upper critical 863 * @param upperNonRecoverable - Upper Non-recoverable 864 * 865 * @returns IPMI completion code 866 */ 867 ipmi::RspType<> ipmiSenSetSensorThresholds( 868 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask, 869 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask, 870 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask, 871 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical, 872 uint8_t lowerCritical, uint8_t lowerNonRecoverable, 873 uint8_t upperNonCritical, uint8_t upperCritical, 874 uint8_t upperNonRecoverable) 875 { 876 if (sensorNum == reservedSensorNumber || reserved) 877 { 878 return ipmi::responseInvalidFieldRequest(); 879 } 880 881 // lower nc and upper nc not suppported on any sensor 882 if (lowerNonRecovThreshMask || upperNonRecovThreshMask) 883 { 884 return ipmi::responseInvalidFieldRequest(); 885 } 886 887 // if none of the threshold mask are set, nothing to do 888 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask | 889 lowerNonRecovThreshMask | upperNonCriticalThreshMask | 890 upperCriticalThreshMask | upperNonRecovThreshMask)) 891 { 892 return ipmi::responseSuccess(); 893 } 894 895 std::string connection; 896 std::string path; 897 898 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path); 899 if (status) 900 { 901 return ipmi::response(status); 902 } 903 DbusInterfaceMap sensorMap; 904 if (!getSensorMap(ctx, connection, path, sensorMap)) 905 { 906 return ipmi::responseResponseError(); 907 } 908 909 double max = 0; 910 double min = 0; 911 getSensorMaxMin(sensorMap, max, min); 912 913 int16_t mValue = 0; 914 int16_t bValue = 0; 915 int8_t rExp = 0; 916 int8_t bExp = 0; 917 bool bSigned = false; 918 919 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 920 { 921 return ipmi::responseResponseError(); 922 } 923 924 // store a vector of property name, value to set, and interface 925 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet; 926 927 // define the indexes of the tuple 928 constexpr uint8_t propertyName = 0; 929 constexpr uint8_t thresholdValue = 1; 930 constexpr uint8_t interface = 2; 931 // verifiy all needed fields are present 932 if (lowerCriticalThreshMask || upperCriticalThreshMask) 933 { 934 auto findThreshold = 935 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 936 if (findThreshold == sensorMap.end()) 937 { 938 return ipmi::responseInvalidFieldRequest(); 939 } 940 if (lowerCriticalThreshMask) 941 { 942 auto findLower = findThreshold->second.find("CriticalLow"); 943 if (findLower == findThreshold->second.end()) 944 { 945 return ipmi::responseInvalidFieldRequest(); 946 } 947 thresholdsToSet.emplace_back("CriticalLow", lowerCritical, 948 findThreshold->first); 949 } 950 if (upperCriticalThreshMask) 951 { 952 auto findUpper = findThreshold->second.find("CriticalHigh"); 953 if (findUpper == findThreshold->second.end()) 954 { 955 return ipmi::responseInvalidFieldRequest(); 956 } 957 thresholdsToSet.emplace_back("CriticalHigh", upperCritical, 958 findThreshold->first); 959 } 960 } 961 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask) 962 { 963 auto findThreshold = 964 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 965 if (findThreshold == sensorMap.end()) 966 { 967 return ipmi::responseInvalidFieldRequest(); 968 } 969 if (lowerNonCriticalThreshMask) 970 { 971 auto findLower = findThreshold->second.find("WarningLow"); 972 if (findLower == findThreshold->second.end()) 973 { 974 return ipmi::responseInvalidFieldRequest(); 975 } 976 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical, 977 findThreshold->first); 978 } 979 if (upperNonCriticalThreshMask) 980 { 981 auto findUpper = findThreshold->second.find("WarningHigh"); 982 if (findUpper == findThreshold->second.end()) 983 { 984 return ipmi::responseInvalidFieldRequest(); 985 } 986 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical, 987 findThreshold->first); 988 } 989 } 990 for (const auto& property : thresholdsToSet) 991 { 992 // from section 36.3 in the IPMI Spec, assume all linear 993 double valueToSet = ((mValue * std::get<thresholdValue>(property)) + 994 (bValue * std::pow(10.0, bExp))) * 995 std::pow(10.0, rExp); 996 setDbusProperty( 997 *getSdBus(), connection, path, std::get<interface>(property), 998 std::get<propertyName>(property), ipmi::Value(valueToSet)); 999 } 1000 return ipmi::responseSuccess(); 1001 } 1002 1003 IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap) 1004 { 1005 IPMIThresholds resp; 1006 auto warningInterface = 1007 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 1008 auto criticalInterface = 1009 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 1010 1011 if ((warningInterface != sensorMap.end()) || 1012 (criticalInterface != sensorMap.end())) 1013 { 1014 auto sensorPair = sensorMap.find(sensor::sensorInterface); 1015 1016 if (sensorPair == sensorMap.end()) 1017 { 1018 // should not have been able to find a sensor not implementing 1019 // the sensor object 1020 throw std::runtime_error("Invalid sensor map"); 1021 } 1022 1023 double max = 0; 1024 double min = 0; 1025 getSensorMaxMin(sensorMap, max, min); 1026 1027 int16_t mValue = 0; 1028 int16_t bValue = 0; 1029 int8_t rExp = 0; 1030 int8_t bExp = 0; 1031 bool bSigned = false; 1032 1033 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 1034 { 1035 throw std::runtime_error("Invalid sensor atrributes"); 1036 } 1037 if (warningInterface != sensorMap.end()) 1038 { 1039 auto& warningMap = warningInterface->second; 1040 1041 auto warningHigh = warningMap.find("WarningHigh"); 1042 auto warningLow = warningMap.find("WarningLow"); 1043 1044 if (warningHigh != warningMap.end()) 1045 { 1046 1047 double value = 1048 std::visit(VariantToDoubleVisitor(), warningHigh->second); 1049 resp.warningHigh = scaleIPMIValueFromDouble( 1050 value, mValue, rExp, bValue, bExp, bSigned); 1051 } 1052 if (warningLow != warningMap.end()) 1053 { 1054 double value = 1055 std::visit(VariantToDoubleVisitor(), warningLow->second); 1056 resp.warningLow = scaleIPMIValueFromDouble( 1057 value, mValue, rExp, bValue, bExp, bSigned); 1058 } 1059 } 1060 if (criticalInterface != sensorMap.end()) 1061 { 1062 auto& criticalMap = criticalInterface->second; 1063 1064 auto criticalHigh = criticalMap.find("CriticalHigh"); 1065 auto criticalLow = criticalMap.find("CriticalLow"); 1066 1067 if (criticalHigh != criticalMap.end()) 1068 { 1069 double value = 1070 std::visit(VariantToDoubleVisitor(), criticalHigh->second); 1071 resp.criticalHigh = scaleIPMIValueFromDouble( 1072 value, mValue, rExp, bValue, bExp, bSigned); 1073 } 1074 if (criticalLow != criticalMap.end()) 1075 { 1076 double value = 1077 std::visit(VariantToDoubleVisitor(), criticalLow->second); 1078 resp.criticalLow = scaleIPMIValueFromDouble( 1079 value, mValue, rExp, bValue, bExp, bSigned); 1080 } 1081 } 1082 } 1083 return resp; 1084 } 1085 1086 ipmi::RspType<uint8_t, // readable 1087 uint8_t, // lowerNCrit 1088 uint8_t, // lowerCrit 1089 uint8_t, // lowerNrecoverable 1090 uint8_t, // upperNC 1091 uint8_t, // upperCrit 1092 uint8_t> // upperNRecoverable 1093 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber) 1094 { 1095 std::string connection; 1096 std::string path; 1097 1098 if (sensorNumber == reservedSensorNumber) 1099 { 1100 return ipmi::responseInvalidFieldRequest(); 1101 } 1102 1103 auto status = getSensorConnection(ctx, sensorNumber, connection, path); 1104 if (status) 1105 { 1106 return ipmi::response(status); 1107 } 1108 1109 DbusInterfaceMap sensorMap; 1110 if (!getSensorMap(ctx, connection, path, sensorMap)) 1111 { 1112 return ipmi::responseResponseError(); 1113 } 1114 1115 IPMIThresholds thresholdData; 1116 try 1117 { 1118 thresholdData = getIPMIThresholds(sensorMap); 1119 } 1120 catch (const std::exception&) 1121 { 1122 return ipmi::responseResponseError(); 1123 } 1124 1125 uint8_t readable = 0; 1126 uint8_t lowerNC = 0; 1127 uint8_t lowerCritical = 0; 1128 uint8_t lowerNonRecoverable = 0; 1129 uint8_t upperNC = 0; 1130 uint8_t upperCritical = 0; 1131 uint8_t upperNonRecoverable = 0; 1132 1133 if (thresholdData.warningHigh) 1134 { 1135 readable |= 1136 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical); 1137 upperNC = *thresholdData.warningHigh; 1138 } 1139 if (thresholdData.warningLow) 1140 { 1141 readable |= 1142 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical); 1143 lowerNC = *thresholdData.warningLow; 1144 } 1145 1146 if (thresholdData.criticalHigh) 1147 { 1148 readable |= 1149 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical); 1150 upperCritical = *thresholdData.criticalHigh; 1151 } 1152 if (thresholdData.criticalLow) 1153 { 1154 readable |= 1155 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical); 1156 lowerCritical = *thresholdData.criticalLow; 1157 } 1158 1159 return ipmi::responseSuccess(readable, lowerNC, lowerCritical, 1160 lowerNonRecoverable, upperNC, upperCritical, 1161 upperNonRecoverable); 1162 } 1163 1164 /** @brief implements the get Sensor event enable command 1165 * @param sensorNumber - sensor number 1166 * 1167 * @returns IPMI completion code plus response data 1168 * - enabled - Sensor Event messages 1169 * - assertionEnabledLsb - Assertion event messages 1170 * - assertionEnabledMsb - Assertion event messages 1171 * - deassertionEnabledLsb - Deassertion event messages 1172 * - deassertionEnabledMsb - Deassertion event messages 1173 */ 1174 1175 ipmi::RspType<uint8_t, // enabled 1176 uint8_t, // assertionEnabledLsb 1177 uint8_t, // assertionEnabledMsb 1178 uint8_t, // deassertionEnabledLsb 1179 uint8_t> // deassertionEnabledMsb 1180 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum) 1181 { 1182 std::string connection; 1183 std::string path; 1184 1185 uint8_t enabled = 0; 1186 uint8_t assertionEnabledLsb = 0; 1187 uint8_t assertionEnabledMsb = 0; 1188 uint8_t deassertionEnabledLsb = 0; 1189 uint8_t deassertionEnabledMsb = 0; 1190 1191 if (sensorNum == reservedSensorNumber) 1192 { 1193 return ipmi::responseInvalidFieldRequest(); 1194 } 1195 1196 auto status = getSensorConnection(ctx, sensorNum, connection, path); 1197 if (status) 1198 { 1199 return ipmi::response(status); 1200 } 1201 1202 #ifdef FEATURE_HYBRID_SENSORS 1203 if (auto sensor = findStaticSensor(path); 1204 sensor != ipmi::sensor::sensors.end() && 1205 getSensorEventTypeFromPath(path) != 1206 static_cast<uint8_t>(SensorEventTypeCodes::threshold)) 1207 { 1208 enabled = static_cast<uint8_t>( 1209 IPMISensorEventEnableByte2::sensorScanningEnable); 1210 uint16_t assertionEnabled = 0; 1211 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin() 1212 ->second.begin() 1213 ->second.second) 1214 { 1215 assertionEnabled |= (1 << offsetValMap.first); 1216 } 1217 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF)); 1218 assertionEnabledMsb = 1219 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF)); 1220 1221 return ipmi::responseSuccess(enabled, assertionEnabledLsb, 1222 assertionEnabledMsb, deassertionEnabledLsb, 1223 deassertionEnabledMsb); 1224 } 1225 #endif 1226 1227 DbusInterfaceMap sensorMap; 1228 if (!getSensorMap(ctx, connection, path, sensorMap)) 1229 { 1230 return ipmi::responseResponseError(); 1231 } 1232 1233 auto warningInterface = 1234 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 1235 auto criticalInterface = 1236 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 1237 if ((warningInterface != sensorMap.end()) || 1238 (criticalInterface != sensorMap.end())) 1239 { 1240 enabled = static_cast<uint8_t>( 1241 IPMISensorEventEnableByte2::sensorScanningEnable); 1242 if (warningInterface != sensorMap.end()) 1243 { 1244 auto& warningMap = warningInterface->second; 1245 1246 auto warningHigh = warningMap.find("WarningHigh"); 1247 auto warningLow = warningMap.find("WarningLow"); 1248 if (warningHigh != warningMap.end()) 1249 { 1250 assertionEnabledLsb |= static_cast<uint8_t>( 1251 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh); 1252 deassertionEnabledLsb |= static_cast<uint8_t>( 1253 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow); 1254 } 1255 if (warningLow != warningMap.end()) 1256 { 1257 assertionEnabledLsb |= static_cast<uint8_t>( 1258 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow); 1259 deassertionEnabledLsb |= static_cast<uint8_t>( 1260 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh); 1261 } 1262 } 1263 if (criticalInterface != sensorMap.end()) 1264 { 1265 auto& criticalMap = criticalInterface->second; 1266 1267 auto criticalHigh = criticalMap.find("CriticalHigh"); 1268 auto criticalLow = criticalMap.find("CriticalLow"); 1269 1270 if (criticalHigh != criticalMap.end()) 1271 { 1272 assertionEnabledMsb |= static_cast<uint8_t>( 1273 IPMISensorEventEnableThresholds::upperCriticalGoingHigh); 1274 deassertionEnabledMsb |= static_cast<uint8_t>( 1275 IPMISensorEventEnableThresholds::upperCriticalGoingLow); 1276 } 1277 if (criticalLow != criticalMap.end()) 1278 { 1279 assertionEnabledLsb |= static_cast<uint8_t>( 1280 IPMISensorEventEnableThresholds::lowerCriticalGoingLow); 1281 deassertionEnabledLsb |= static_cast<uint8_t>( 1282 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh); 1283 } 1284 } 1285 } 1286 1287 return ipmi::responseSuccess(enabled, assertionEnabledLsb, 1288 assertionEnabledMsb, deassertionEnabledLsb, 1289 deassertionEnabledMsb); 1290 } 1291 1292 /** @brief implements the get Sensor event status command 1293 * @param sensorNumber - sensor number, FFh = reserved 1294 * 1295 * @returns IPMI completion code plus response data 1296 * - sensorEventStatus - Sensor Event messages state 1297 * - assertions - Assertion event messages 1298 * - deassertions - Deassertion event messages 1299 */ 1300 ipmi::RspType<uint8_t, // sensorEventStatus 1301 std::bitset<16>, // assertions 1302 std::bitset<16> // deassertion 1303 > 1304 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum) 1305 { 1306 if (sensorNum == reservedSensorNumber) 1307 { 1308 return ipmi::responseInvalidFieldRequest(); 1309 } 1310 1311 std::string connection; 1312 std::string path; 1313 auto status = getSensorConnection(ctx, sensorNum, connection, path); 1314 if (status) 1315 { 1316 phosphor::logging::log<phosphor::logging::level::ERR>( 1317 "ipmiSenGetSensorEventStatus: Sensor connection Error", 1318 phosphor::logging::entry("SENSOR=%d", sensorNum)); 1319 return ipmi::response(status); 1320 } 1321 1322 #ifdef FEATURE_HYBRID_SENSORS 1323 if (auto sensor = findStaticSensor(path); 1324 sensor != ipmi::sensor::sensors.end() && 1325 getSensorEventTypeFromPath(path) != 1326 static_cast<uint8_t>(SensorEventTypeCodes::threshold)) 1327 { 1328 auto response = ipmi::sensor::get::mapDbusToAssertion( 1329 sensor->second, path, sensor->second.sensorInterface); 1330 std::bitset<16> assertions; 1331 // deassertions are not used. 1332 std::bitset<16> deassertions = 0; 1333 uint8_t sensorEventStatus; 1334 if (response.readingOrStateUnavailable) 1335 { 1336 sensorEventStatus |= static_cast<uint8_t>( 1337 IPMISensorReadingByte2::readingStateUnavailable); 1338 } 1339 if (response.scanningEnabled) 1340 { 1341 sensorEventStatus |= static_cast<uint8_t>( 1342 IPMISensorReadingByte2::sensorScanningEnable); 1343 } 1344 if (response.allEventMessagesEnabled) 1345 { 1346 sensorEventStatus |= static_cast<uint8_t>( 1347 IPMISensorReadingByte2::eventMessagesEnable); 1348 } 1349 assertions |= response.discreteReadingSensorStates << 8; 1350 assertions |= response.thresholdLevelsStates; 1351 return ipmi::responseSuccess(sensorEventStatus, assertions, 1352 deassertions); 1353 } 1354 #endif 1355 1356 DbusInterfaceMap sensorMap; 1357 if (!getSensorMap(ctx, connection, path, sensorMap)) 1358 { 1359 phosphor::logging::log<phosphor::logging::level::ERR>( 1360 "ipmiSenGetSensorEventStatus: Sensor Mapping Error", 1361 phosphor::logging::entry("SENSOR=%s", path.c_str())); 1362 return ipmi::responseResponseError(); 1363 } 1364 1365 uint8_t sensorEventStatus = 1366 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable); 1367 std::bitset<16> assertions = 0; 1368 std::bitset<16> deassertions = 0; 1369 1370 // handle VR typed sensor 1371 auto vrInterface = sensorMap.find(sensor::vrInterface); 1372 if (vrInterface != sensorMap.end()) 1373 { 1374 if (!sensor::getVrEventStatus(ctx, connection, path, 1375 vrInterface->second, assertions)) 1376 { 1377 return ipmi::responseResponseError(); 1378 } 1379 1380 // both Event Message and Sensor Scanning are disable for VR. 1381 sensorEventStatus = 0; 1382 return ipmi::responseSuccess(sensorEventStatus, assertions, 1383 deassertions); 1384 } 1385 1386 auto warningInterface = 1387 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 1388 auto criticalInterface = 1389 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 1390 1391 std::optional<bool> criticalDeassertHigh = 1392 thresholdDeassertMap[path]["CriticalAlarmHigh"]; 1393 std::optional<bool> criticalDeassertLow = 1394 thresholdDeassertMap[path]["CriticalAlarmLow"]; 1395 std::optional<bool> warningDeassertHigh = 1396 thresholdDeassertMap[path]["WarningAlarmHigh"]; 1397 std::optional<bool> warningDeassertLow = 1398 thresholdDeassertMap[path]["WarningAlarmLow"]; 1399 1400 if (criticalDeassertHigh && !*criticalDeassertHigh) 1401 { 1402 deassertions.set(static_cast<size_t>( 1403 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh)); 1404 } 1405 if (criticalDeassertLow && !*criticalDeassertLow) 1406 { 1407 deassertions.set(static_cast<size_t>( 1408 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow)); 1409 } 1410 if (warningDeassertHigh && !*warningDeassertHigh) 1411 { 1412 deassertions.set(static_cast<size_t>( 1413 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh)); 1414 } 1415 if (warningDeassertLow && !*warningDeassertLow) 1416 { 1417 deassertions.set(static_cast<size_t>( 1418 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh)); 1419 } 1420 if ((warningInterface != sensorMap.end()) || 1421 (criticalInterface != sensorMap.end())) 1422 { 1423 sensorEventStatus = static_cast<size_t>( 1424 IPMISensorEventEnableByte2::eventMessagesEnable); 1425 if (warningInterface != sensorMap.end()) 1426 { 1427 auto& warningMap = warningInterface->second; 1428 1429 auto warningHigh = warningMap.find("WarningAlarmHigh"); 1430 auto warningLow = warningMap.find("WarningAlarmLow"); 1431 auto warningHighAlarm = false; 1432 auto warningLowAlarm = false; 1433 1434 if (warningHigh != warningMap.end()) 1435 { 1436 warningHighAlarm = std::get<bool>(warningHigh->second); 1437 } 1438 if (warningLow != warningMap.end()) 1439 { 1440 warningLowAlarm = std::get<bool>(warningLow->second); 1441 } 1442 if (warningHighAlarm) 1443 { 1444 assertions.set( 1445 static_cast<size_t>(IPMIGetSensorEventEnableThresholds:: 1446 upperNonCriticalGoingHigh)); 1447 } 1448 if (warningLowAlarm) 1449 { 1450 assertions.set( 1451 static_cast<size_t>(IPMIGetSensorEventEnableThresholds:: 1452 lowerNonCriticalGoingLow)); 1453 } 1454 } 1455 if (criticalInterface != sensorMap.end()) 1456 { 1457 auto& criticalMap = criticalInterface->second; 1458 1459 auto criticalHigh = criticalMap.find("CriticalAlarmHigh"); 1460 auto criticalLow = criticalMap.find("CriticalAlarmLow"); 1461 auto criticalHighAlarm = false; 1462 auto criticalLowAlarm = false; 1463 1464 if (criticalHigh != criticalMap.end()) 1465 { 1466 criticalHighAlarm = std::get<bool>(criticalHigh->second); 1467 } 1468 if (criticalLow != criticalMap.end()) 1469 { 1470 criticalLowAlarm = std::get<bool>(criticalLow->second); 1471 } 1472 if (criticalHighAlarm) 1473 { 1474 assertions.set( 1475 static_cast<size_t>(IPMIGetSensorEventEnableThresholds:: 1476 upperCriticalGoingHigh)); 1477 } 1478 if (criticalLowAlarm) 1479 { 1480 assertions.set(static_cast<size_t>( 1481 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow)); 1482 } 1483 } 1484 } 1485 1486 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions); 1487 } 1488 1489 // Construct a type 1 SDR for threshold sensor. 1490 void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID, 1491 get_sdr::SensorDataFullRecord& record) 1492 { 1493 get_sdr::header::set_record_id( 1494 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record)); 1495 1496 uint8_t sensornumber = static_cast<uint8_t>(sensorNum); 1497 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8); 1498 1499 record.header.sdr_version = ipmiSdrVersion; 1500 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD; 1501 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) - 1502 sizeof(get_sdr::SensorDataRecordHeader); 1503 record.key.owner_id = bmcI2CAddr; 1504 record.key.owner_lun = lun; 1505 record.key.sensor_number = sensornumber; 1506 } 1507 bool constructSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum, 1508 uint16_t recordID, const std::string& service, 1509 const std::string& path, 1510 get_sdr::SensorDataFullRecord& record) 1511 { 1512 uint8_t sensornumber = static_cast<uint8_t>(sensorNum); 1513 constructSensorSdrHeaderKey(sensorNum, recordID, record); 1514 1515 DbusInterfaceMap sensorMap; 1516 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod)) 1517 { 1518 phosphor::logging::log<phosphor::logging::level::ERR>( 1519 "Failed to update sensor map for threshold sensor", 1520 phosphor::logging::entry("SERVICE=%s", service.c_str()), 1521 phosphor::logging::entry("PATH=%s", path.c_str())); 1522 return false; 1523 } 1524 1525 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis 1526 record.body.sensor_type = getSensorTypeFromPath(path); 1527 std::string type = getSensorTypeStringFromPath(path); 1528 auto typeCstr = type.c_str(); 1529 auto findUnits = sensorUnits.find(typeCstr); 1530 if (findUnits != sensorUnits.end()) 1531 { 1532 record.body.sensor_units_2_base = 1533 static_cast<uint8_t>(findUnits->second); 1534 } // else default 0x0 unspecified 1535 1536 record.body.event_reading_type = getSensorEventTypeFromPath(path); 1537 1538 auto sensorObject = sensorMap.find(sensor::sensorInterface); 1539 if (sensorObject == sensorMap.end()) 1540 { 1541 phosphor::logging::log<phosphor::logging::level::ERR>( 1542 "getSensorDataRecord: sensorObject error"); 1543 return false; 1544 } 1545 1546 uint8_t entityId = 0; 1547 uint8_t entityInstance = 0x01; 1548 1549 // follow the association chain to get the parent board's entityid and 1550 // entityInstance 1551 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance); 1552 1553 record.body.entity_id = entityId; 1554 record.body.entity_instance = entityInstance; 1555 1556 double max = 0; 1557 double min = 0; 1558 getSensorMaxMin(sensorMap, max, min); 1559 1560 int16_t mValue = 0; 1561 int8_t rExp = 0; 1562 int16_t bValue = 0; 1563 int8_t bExp = 0; 1564 bool bSigned = false; 1565 1566 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 1567 { 1568 phosphor::logging::log<phosphor::logging::level::ERR>( 1569 "getSensorDataRecord: getSensorAttributes error"); 1570 return false; 1571 } 1572 1573 // The record.body is a struct SensorDataFullRecordBody 1574 // from sensorhandler.hpp in phosphor-ipmi-host. 1575 // The meaning of these bits appears to come from 1576 // table 43.1 of the IPMI spec. 1577 // The above 5 sensor attributes are stuffed in as follows: 1578 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned 1579 // Byte 22-24 are for other purposes 1580 // Byte 25 = MMMMMMMM = LSB of M 1581 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance 1582 // Byte 27 = BBBBBBBB = LSB of B 1583 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy 1584 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy 1585 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed) 1586 1587 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4 1588 record.body.m_lsb = mValue & 0xFF; 1589 1590 uint8_t mBitSign = (mValue < 0) ? 1 : 0; 1591 uint8_t mBitNine = (mValue & 0x0100) >> 8; 1592 1593 // move the smallest bit of the MSB into place (bit 9) 1594 // the MSbs are bits 7:8 in m_msb_and_tolerance 1595 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6); 1596 1597 record.body.b_lsb = bValue & 0xFF; 1598 1599 uint8_t bBitSign = (bValue < 0) ? 1 : 0; 1600 uint8_t bBitNine = (bValue & 0x0100) >> 8; 1601 1602 // move the smallest bit of the MSB into place (bit 9) 1603 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb 1604 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6); 1605 1606 uint8_t rExpSign = (rExp < 0) ? 1 : 0; 1607 uint8_t rExpBits = rExp & 0x07; 1608 1609 uint8_t bExpSign = (bExp < 0) ? 1 : 0; 1610 uint8_t bExpBits = bExp & 0x07; 1611 1612 // move rExp and bExp into place 1613 record.body.r_b_exponents = 1614 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits; 1615 1616 // Set the analog reading byte interpretation accordingly 1617 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7; 1618 1619 // TODO(): Perhaps care about Tolerance, Accuracy, and so on 1620 // These seem redundant, but derivable from the above 5 attributes 1621 // Original comment said "todo fill out rest of units" 1622 1623 // populate sensor name from path 1624 auto name = sensor::parseSdrIdFromPath(path); 1625 record.body.id_string_info = name.size(); 1626 std::strncpy(record.body.id_string, name.c_str(), 1627 sizeof(record.body.id_string)); 1628 1629 // Remember the sensor name, as determined for this sensor number 1630 details::sdrStatsTable.updateName(sensornumber, name); 1631 1632 bool sensorSettable = false; 1633 auto mutability = 1634 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability"); 1635 if (mutability != sensorMap.end()) 1636 { 1637 sensorSettable = 1638 mappedVariant<bool>(mutability->second, "Mutable", false); 1639 } 1640 get_sdr::body::init_settable_state(sensorSettable, &record.body); 1641 1642 // Grant write permission to sensors deemed externally settable 1643 details::sdrWriteTable.setWritePermission(sensornumber, sensorSettable); 1644 1645 IPMIThresholds thresholdData; 1646 try 1647 { 1648 thresholdData = getIPMIThresholds(sensorMap); 1649 } 1650 catch (const std::exception&) 1651 { 1652 phosphor::logging::log<phosphor::logging::level::ERR>( 1653 "getSensorDataRecord: getIPMIThresholds error"); 1654 return false; 1655 } 1656 1657 if (thresholdData.criticalHigh) 1658 { 1659 record.body.upper_critical_threshold = *thresholdData.criticalHigh; 1660 record.body.supported_deassertions[1] |= static_cast<uint8_t>( 1661 IPMISensorEventEnableThresholds::criticalThreshold); 1662 record.body.supported_deassertions[1] |= static_cast<uint8_t>( 1663 IPMISensorEventEnableThresholds::upperCriticalGoingHigh); 1664 record.body.supported_assertions[1] |= static_cast<uint8_t>( 1665 IPMISensorEventEnableThresholds::upperCriticalGoingHigh); 1666 record.body.discrete_reading_setting_mask[0] |= 1667 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical); 1668 } 1669 if (thresholdData.warningHigh) 1670 { 1671 record.body.upper_noncritical_threshold = *thresholdData.warningHigh; 1672 record.body.supported_deassertions[1] |= static_cast<uint8_t>( 1673 IPMISensorEventEnableThresholds::nonCriticalThreshold); 1674 record.body.supported_deassertions[0] |= static_cast<uint8_t>( 1675 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh); 1676 record.body.supported_assertions[0] |= static_cast<uint8_t>( 1677 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh); 1678 record.body.discrete_reading_setting_mask[0] |= 1679 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical); 1680 } 1681 if (thresholdData.criticalLow) 1682 { 1683 record.body.lower_critical_threshold = *thresholdData.criticalLow; 1684 record.body.supported_assertions[1] |= static_cast<uint8_t>( 1685 IPMISensorEventEnableThresholds::criticalThreshold); 1686 record.body.supported_deassertions[0] |= static_cast<uint8_t>( 1687 IPMISensorEventEnableThresholds::lowerCriticalGoingLow); 1688 record.body.supported_assertions[0] |= static_cast<uint8_t>( 1689 IPMISensorEventEnableThresholds::lowerCriticalGoingLow); 1690 record.body.discrete_reading_setting_mask[0] |= 1691 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical); 1692 } 1693 if (thresholdData.warningLow) 1694 { 1695 record.body.lower_noncritical_threshold = *thresholdData.warningLow; 1696 record.body.supported_assertions[1] |= static_cast<uint8_t>( 1697 IPMISensorEventEnableThresholds::nonCriticalThreshold); 1698 record.body.supported_deassertions[0] |= static_cast<uint8_t>( 1699 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow); 1700 record.body.supported_assertions[0] |= static_cast<uint8_t>( 1701 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow); 1702 record.body.discrete_reading_setting_mask[0] |= 1703 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical); 1704 } 1705 1706 // everything that is readable is setable 1707 record.body.discrete_reading_setting_mask[1] = 1708 record.body.discrete_reading_setting_mask[0]; 1709 return true; 1710 } 1711 1712 #ifdef FEATURE_HYBRID_SENSORS 1713 // Construct a type 1 SDR for discrete Sensor typed sensor. 1714 void constructStaticSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum, 1715 uint16_t recordID, 1716 ipmi::sensor::IdInfoMap::const_iterator sensor, 1717 get_sdr::SensorDataFullRecord& record) 1718 { 1719 constructSensorSdrHeaderKey(sensorNum, recordID, record); 1720 1721 record.body.entity_id = sensor->second.entityType; 1722 record.body.sensor_type = sensor->second.sensorType; 1723 record.body.event_reading_type = sensor->second.sensorReadingType; 1724 record.body.entity_instance = sensor->second.instance; 1725 if (ipmi::sensor::Mutability::Write == 1726 (sensor->second.mutability & ipmi::sensor::Mutability::Write)) 1727 { 1728 get_sdr::body::init_settable_state(true, &(record.body)); 1729 } 1730 1731 auto id_string = sensor->second.sensorName; 1732 1733 if (id_string.empty()) 1734 { 1735 id_string = sensor->second.sensorNameFunc(sensor->second); 1736 } 1737 1738 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH) 1739 { 1740 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH, 1741 &(record.body)); 1742 } 1743 else 1744 { 1745 get_sdr::body::set_id_strlen(id_string.length(), &(record.body)); 1746 } 1747 std::strncpy(record.body.id_string, id_string.c_str(), 1748 get_sdr::body::get_id_strlen(&(record.body))); 1749 } 1750 #endif 1751 1752 // Construct type 3 SDR header and key (for VR and other discrete sensors) 1753 void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID, 1754 get_sdr::SensorDataEventRecord& record) 1755 { 1756 uint8_t sensornumber = static_cast<uint8_t>(sensorNum); 1757 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8); 1758 1759 get_sdr::header::set_record_id( 1760 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record)); 1761 1762 record.header.sdr_version = ipmiSdrVersion; 1763 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD; 1764 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) - 1765 sizeof(get_sdr::SensorDataRecordHeader); 1766 record.key.owner_id = bmcI2CAddr; 1767 record.key.owner_lun = lun; 1768 record.key.sensor_number = sensornumber; 1769 1770 record.body.entity_id = 0x00; 1771 record.body.entity_instance = 0x01; 1772 } 1773 1774 // Construct a type 3 SDR for VR typed sensor(daemon). 1775 bool constructVrSdr(ipmi::Context::ptr ctx, uint16_t sensorNum, 1776 uint16_t recordID, const std::string& service, 1777 const std::string& path, 1778 get_sdr::SensorDataEventRecord& record) 1779 { 1780 uint8_t sensornumber = static_cast<uint8_t>(sensorNum); 1781 constructEventSdrHeaderKey(sensorNum, recordID, record); 1782 1783 DbusInterfaceMap sensorMap; 1784 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod)) 1785 { 1786 phosphor::logging::log<phosphor::logging::level::ERR>( 1787 "Failed to update sensor map for VR sensor", 1788 phosphor::logging::entry("SERVICE=%s", service.c_str()), 1789 phosphor::logging::entry("PATH=%s", path.c_str())); 1790 return false; 1791 } 1792 // follow the association chain to get the parent board's entityid and 1793 // entityInstance 1794 updateIpmiFromAssociation(path, sensorMap, record.body.entity_id, 1795 record.body.entity_instance); 1796 1797 // Sensor type is hardcoded as a module/board type instead of parsing from 1798 // sensor path. This is because VR control is allocated in an independent 1799 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by 1800 // types. 1801 static constexpr const uint8_t module_board_type = 0x15; 1802 record.body.sensor_type = module_board_type; 1803 record.body.event_reading_type = 0x00; 1804 1805 record.body.sensor_record_sharing_1 = 0x00; 1806 record.body.sensor_record_sharing_2 = 0x00; 1807 1808 // populate sensor name from path 1809 auto name = sensor::parseSdrIdFromPath(path); 1810 int nameSize = std::min(name.size(), sizeof(record.body.id_string)); 1811 record.body.id_string_info = nameSize; 1812 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string)); 1813 std::memcpy(record.body.id_string, name.c_str(), nameSize); 1814 1815 // Remember the sensor name, as determined for this sensor number 1816 details::sdrStatsTable.updateName(sensornumber, name); 1817 1818 return true; 1819 } 1820 1821 static int 1822 getSensorDataRecord(ipmi::Context::ptr ctx, 1823 std::vector<uint8_t>& recordData, uint16_t recordID, 1824 uint8_t readBytes = std::numeric_limits<uint8_t>::max()) 1825 { 1826 size_t fruCount = 0; 1827 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount); 1828 if (ret != ipmi::ccSuccess) 1829 { 1830 phosphor::logging::log<phosphor::logging::level::ERR>( 1831 "getSensorDataRecord: getFruSdrCount error"); 1832 return GENERAL_ERROR; 1833 } 1834 1835 auto& sensorTree = getSensorTree(); 1836 size_t lastRecord = 1837 sensorTree.size() + fruCount + ipmi::storage::type12Count + -1; 1838 if (recordID == lastRecordIndex) 1839 { 1840 recordID = lastRecord; 1841 } 1842 if (recordID > lastRecord) 1843 { 1844 phosphor::logging::log<phosphor::logging::level::ERR>( 1845 "getSensorDataRecord: recordID > lastRecord error"); 1846 return GENERAL_ERROR; 1847 } 1848 1849 if (recordID >= sensorTree.size()) 1850 { 1851 size_t fruIndex = recordID - sensorTree.size(); 1852 1853 if (fruIndex >= fruCount) 1854 { 1855 // handle type 12 hardcoded records 1856 size_t type12Index = fruIndex - fruCount; 1857 if (type12Index >= ipmi::storage::type12Count) 1858 { 1859 phosphor::logging::log<phosphor::logging::level::ERR>( 1860 "getSensorDataRecord: type12Index error"); 1861 return GENERAL_ERROR; 1862 } 1863 recordData = ipmi::storage::getType12SDRs(type12Index, recordID); 1864 } 1865 else 1866 { 1867 // handle fru records 1868 get_sdr::SensorDataFruRecord data; 1869 ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data); 1870 if (ret != IPMI_CC_OK) 1871 { 1872 return GENERAL_ERROR; 1873 } 1874 data.header.record_id_msb = recordID >> 8; 1875 data.header.record_id_lsb = recordID & 0xFF; 1876 recordData.insert(recordData.end(), (uint8_t*)&data, 1877 ((uint8_t*)&data) + sizeof(data)); 1878 } 1879 1880 return 0; 1881 } 1882 1883 std::string connection; 1884 std::string path; 1885 std::vector<std::string> interfaces; 1886 1887 auto status = 1888 getSensorConnection(ctx, recordID, connection, path, &interfaces); 1889 if (status) 1890 { 1891 phosphor::logging::log<phosphor::logging::level::ERR>( 1892 "getSensorDataRecord: getSensorConnection error"); 1893 return GENERAL_ERROR; 1894 } 1895 uint16_t sensorNum = getSensorNumberFromPath(path); 1896 if (sensorNum == invalidSensorNumber) 1897 { 1898 phosphor::logging::log<phosphor::logging::level::ERR>( 1899 "getSensorDataRecord: invalidSensorNumber"); 1900 return GENERAL_ERROR; 1901 } 1902 1903 // Construct full record (SDR type 1) for the threshold sensors 1904 if (std::find(interfaces.begin(), interfaces.end(), 1905 sensor::sensorInterface) != interfaces.end()) 1906 { 1907 get_sdr::SensorDataFullRecord record = {0}; 1908 1909 // If the request doesn't read SDR body, construct only header and key 1910 // part to avoid additional DBus transaction. 1911 if (readBytes <= sizeof(record.header) + sizeof(record.key)) 1912 { 1913 constructSensorSdrHeaderKey(sensorNum, recordID, record); 1914 } 1915 else if (!constructSensorSdr(ctx, sensorNum, recordID, connection, path, 1916 record)) 1917 { 1918 return GENERAL_ERROR; 1919 } 1920 1921 recordData.insert(recordData.end(), (uint8_t*)&record, 1922 ((uint8_t*)&record) + sizeof(record)); 1923 1924 return 0; 1925 } 1926 1927 #ifdef FEATURE_HYBRID_SENSORS 1928 if (auto sensor = findStaticSensor(path); 1929 sensor != ipmi::sensor::sensors.end() && 1930 getSensorEventTypeFromPath(path) != 1931 static_cast<uint8_t>(SensorEventTypeCodes::threshold)) 1932 { 1933 get_sdr::SensorDataFullRecord record = {0}; 1934 1935 // If the request doesn't read SDR body, construct only header and key 1936 // part to avoid additional DBus transaction. 1937 if (readBytes <= sizeof(record.header) + sizeof(record.key)) 1938 { 1939 constructSensorSdrHeaderKey(sensorNum, recordID, record); 1940 } 1941 else 1942 { 1943 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record); 1944 } 1945 1946 recordData.insert(recordData.end(), (uint8_t*)&record, 1947 ((uint8_t*)&record) + sizeof(record)); 1948 1949 return 0; 1950 } 1951 #endif 1952 1953 // Contruct SDR type 3 record for VR sensor (daemon) 1954 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) != 1955 interfaces.end()) 1956 { 1957 get_sdr::SensorDataEventRecord record = {0}; 1958 1959 // If the request doesn't read SDR body, construct only header and key 1960 // part to avoid additional DBus transaction. 1961 if (readBytes <= sizeof(record.header) + sizeof(record.key)) 1962 { 1963 constructEventSdrHeaderKey(sensorNum, recordID, record); 1964 } 1965 else if (!constructVrSdr(ctx, sensorNum, recordID, connection, path, 1966 record)) 1967 { 1968 return GENERAL_ERROR; 1969 } 1970 recordData.insert(recordData.end(), (uint8_t*)&record, 1971 ((uint8_t*)&record) + sizeof(record)); 1972 } 1973 1974 return 0; 1975 } 1976 1977 /** @brief implements the get SDR Info command 1978 * @param count - Operation 1979 * 1980 * @returns IPMI completion code plus response data 1981 * - sdrCount - sensor/SDR count 1982 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag 1983 */ 1984 static ipmi::RspType<uint8_t, // respcount 1985 uint8_t, // dynamic population flags 1986 uint32_t // last time a sensor was added 1987 > 1988 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx, 1989 std::optional<uint8_t> count) 1990 { 1991 auto& sensorTree = getSensorTree(); 1992 uint8_t sdrCount = 0; 1993 uint16_t recordID = 0; 1994 std::vector<uint8_t> record; 1995 // Sensors are dynamically allocated, and there is at least one LUN 1996 uint8_t lunsAndDynamicPopulation = 0x80; 1997 constexpr uint8_t getSdrCount = 0x01; 1998 constexpr uint8_t getSensorCount = 0x00; 1999 2000 if (!getSensorSubtree(sensorTree) || sensorTree.empty()) 2001 { 2002 return ipmi::responseResponseError(); 2003 } 2004 uint16_t numSensors = sensorTree.size(); 2005 if (count.value_or(0) == getSdrCount) 2006 { 2007 // Count the number of Type 1 SDR entries assigned to the LUN 2008 while (!getSensorDataRecord(ctx, record, recordID++)) 2009 { 2010 get_sdr::SensorDataRecordHeader* hdr = 2011 reinterpret_cast<get_sdr::SensorDataRecordHeader*>( 2012 record.data()); 2013 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD) 2014 { 2015 get_sdr::SensorDataFullRecord* recordData = 2016 reinterpret_cast<get_sdr::SensorDataFullRecord*>( 2017 record.data()); 2018 if (ctx->lun == recordData->key.owner_lun) 2019 { 2020 sdrCount++; 2021 } 2022 } 2023 } 2024 } 2025 else if (count.value_or(0) == getSensorCount) 2026 { 2027 // Return the number of sensors attached to the LUN 2028 if ((ctx->lun == 0) && (numSensors > 0)) 2029 { 2030 sdrCount = 2031 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors; 2032 } 2033 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN)) 2034 { 2035 sdrCount = (numSensors > (2 * maxSensorsPerLUN)) 2036 ? maxSensorsPerLUN 2037 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN; 2038 } 2039 else if (ctx->lun == 3) 2040 { 2041 if (numSensors <= maxIPMISensors) 2042 { 2043 sdrCount = 2044 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN; 2045 } 2046 else 2047 { 2048 // error 2049 throw std::out_of_range( 2050 "Maximum number of IPMI sensors exceeded."); 2051 } 2052 } 2053 } 2054 else 2055 { 2056 return ipmi::responseInvalidFieldRequest(); 2057 } 2058 2059 // Get Sensor count. This returns the number of sensors 2060 if (numSensors > 0) 2061 { 2062 lunsAndDynamicPopulation |= 1; 2063 } 2064 if (numSensors > maxSensorsPerLUN) 2065 { 2066 lunsAndDynamicPopulation |= 2; 2067 } 2068 if (numSensors >= (maxSensorsPerLUN * 2)) 2069 { 2070 lunsAndDynamicPopulation |= 8; 2071 } 2072 if (numSensors > maxIPMISensors) 2073 { 2074 // error 2075 throw std::out_of_range("Maximum number of IPMI sensors exceeded."); 2076 } 2077 2078 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation, 2079 sdrLastAdd); 2080 } 2081 2082 /* end sensor commands */ 2083 2084 /* storage commands */ 2085 2086 ipmi::RspType<uint8_t, // sdr version 2087 uint16_t, // record count 2088 uint16_t, // free space 2089 uint32_t, // most recent addition 2090 uint32_t, // most recent erase 2091 uint8_t // operationSupport 2092 > 2093 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx) 2094 { 2095 auto& sensorTree = getSensorTree(); 2096 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF; 2097 if (!getSensorSubtree(sensorTree) && sensorTree.empty()) 2098 { 2099 return ipmi::responseResponseError(); 2100 } 2101 2102 size_t fruCount = 0; 2103 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount); 2104 if (ret != ipmi::ccSuccess) 2105 { 2106 return ipmi::response(ret); 2107 } 2108 2109 uint16_t recordCount = 2110 sensorTree.size() + fruCount + ipmi::storage::type12Count; 2111 2112 uint8_t operationSupport = static_cast<uint8_t>( 2113 SdrRepositoryInfoOps::overflow); // write not supported 2114 2115 operationSupport |= 2116 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported); 2117 operationSupport |= static_cast<uint8_t>( 2118 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported); 2119 return ipmi::responseSuccess(ipmiSdrVersion, recordCount, 2120 unspecifiedFreeSpace, sdrLastAdd, 2121 sdrLastRemove, operationSupport); 2122 } 2123 2124 /** @brief implements the get SDR allocation info command 2125 * 2126 * @returns IPMI completion code plus response data 2127 * - allocUnits - Number of possible allocation units 2128 * - allocUnitSize - Allocation unit size in bytes. 2129 * - allocUnitFree - Number of free allocation units 2130 * - allocUnitLargestFree - Largest free block in allocation units 2131 * - maxRecordSize - Maximum record size in allocation units. 2132 */ 2133 ipmi::RspType<uint16_t, // allocUnits 2134 uint16_t, // allocUnitSize 2135 uint16_t, // allocUnitFree 2136 uint16_t, // allocUnitLargestFree 2137 uint8_t // maxRecordSize 2138 > 2139 ipmiStorageGetSDRAllocationInfo() 2140 { 2141 // 0000h unspecified number of alloc units 2142 constexpr uint16_t allocUnits = 0; 2143 2144 constexpr uint16_t allocUnitFree = 0; 2145 constexpr uint16_t allocUnitLargestFree = 0; 2146 // only allow one block at a time 2147 constexpr uint8_t maxRecordSize = 1; 2148 2149 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree, 2150 allocUnitLargestFree, maxRecordSize); 2151 } 2152 2153 /** @brief implements the reserve SDR command 2154 * @returns IPMI completion code plus response data 2155 * - sdrReservationID 2156 */ 2157 ipmi::RspType<uint16_t> ipmiStorageReserveSDR() 2158 { 2159 sdrReservationID++; 2160 if (sdrReservationID == 0) 2161 { 2162 sdrReservationID++; 2163 } 2164 2165 return ipmi::responseSuccess(sdrReservationID); 2166 } 2167 2168 ipmi::RspType<uint16_t, // next record ID 2169 std::vector<uint8_t> // payload 2170 > 2171 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID, 2172 uint16_t recordID, uint8_t offset, uint8_t bytesToRead) 2173 { 2174 size_t fruCount = 0; 2175 // reservation required for partial reads with non zero offset into 2176 // record 2177 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset) 2178 { 2179 phosphor::logging::log<phosphor::logging::level::ERR>( 2180 "ipmiStorageGetSDR: responseInvalidReservationId"); 2181 return ipmi::responseInvalidReservationId(); 2182 } 2183 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount); 2184 if (ret != ipmi::ccSuccess) 2185 { 2186 phosphor::logging::log<phosphor::logging::level::ERR>( 2187 "ipmiStorageGetSDR: getFruSdrCount error"); 2188 return ipmi::response(ret); 2189 } 2190 2191 auto& sensorTree = getSensorTree(); 2192 size_t lastRecord = 2193 sensorTree.size() + fruCount + ipmi::storage::type12Count - 1; 2194 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF; 2195 2196 if (!getSensorSubtree(sensorTree) && sensorTree.empty()) 2197 { 2198 phosphor::logging::log<phosphor::logging::level::ERR>( 2199 "ipmiStorageGetSDR: getSensorSubtree error"); 2200 return ipmi::responseResponseError(); 2201 } 2202 2203 std::vector<uint8_t> record; 2204 if (getSensorDataRecord(ctx, record, recordID, offset + bytesToRead)) 2205 { 2206 phosphor::logging::log<phosphor::logging::level::ERR>( 2207 "ipmiStorageGetSDR: fail to get SDR"); 2208 return ipmi::responseInvalidFieldRequest(); 2209 } 2210 get_sdr::SensorDataRecordHeader* hdr = 2211 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data()); 2212 if (!hdr) 2213 { 2214 phosphor::logging::log<phosphor::logging::level::ERR>( 2215 "ipmiStorageGetSDR: record header is null"); 2216 return ipmi::responseSuccess(nextRecordId, record); 2217 } 2218 2219 size_t sdrLength = 2220 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length; 2221 if (sdrLength < (offset + bytesToRead)) 2222 { 2223 bytesToRead = sdrLength - offset; 2224 } 2225 2226 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset; 2227 if (!respStart) 2228 { 2229 phosphor::logging::log<phosphor::logging::level::ERR>( 2230 "ipmiStorageGetSDR: record is null"); 2231 return ipmi::responseSuccess(nextRecordId, record); 2232 } 2233 2234 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead); 2235 2236 return ipmi::responseSuccess(nextRecordId, recordData); 2237 } 2238 /* end storage commands */ 2239 2240 void registerSensorFunctions() 2241 { 2242 // <Platform Event> 2243 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2244 ipmi::sensor_event::cmdPlatformEvent, 2245 ipmi::Privilege::Operator, ipmiSenPlatformEvent); 2246 2247 // <Set Sensor Reading and Event Status> 2248 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2249 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts, 2250 ipmi::Privilege::Operator, ipmiSetSensorReading); 2251 2252 // <Get Sensor Reading> 2253 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2254 ipmi::sensor_event::cmdGetSensorReading, 2255 ipmi::Privilege::User, ipmiSenGetSensorReading); 2256 2257 // <Get Sensor Threshold> 2258 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2259 ipmi::sensor_event::cmdGetSensorThreshold, 2260 ipmi::Privilege::User, ipmiSenGetSensorThresholds); 2261 2262 // <Set Sensor Threshold> 2263 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2264 ipmi::sensor_event::cmdSetSensorThreshold, 2265 ipmi::Privilege::Operator, 2266 ipmiSenSetSensorThresholds); 2267 2268 // <Get Sensor Event Enable> 2269 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2270 ipmi::sensor_event::cmdGetSensorEventEnable, 2271 ipmi::Privilege::User, ipmiSenGetSensorEventEnable); 2272 2273 // <Get Sensor Event Status> 2274 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2275 ipmi::sensor_event::cmdGetSensorEventStatus, 2276 ipmi::Privilege::User, ipmiSenGetSensorEventStatus); 2277 2278 // register all storage commands for both Sensor and Storage command 2279 // versions 2280 2281 // <Get SDR Repository Info> 2282 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 2283 ipmi::storage::cmdGetSdrRepositoryInfo, 2284 ipmi::Privilege::User, 2285 ipmiStorageGetSDRRepositoryInfo); 2286 2287 // <Get Device SDR Info> 2288 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2289 ipmi::sensor_event::cmdGetDeviceSdrInfo, 2290 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo); 2291 2292 // <Get SDR Allocation Info> 2293 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 2294 ipmi::storage::cmdGetSdrRepositoryAllocInfo, 2295 ipmi::Privilege::User, 2296 ipmiStorageGetSDRAllocationInfo); 2297 2298 // <Reserve SDR Repo> 2299 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2300 ipmi::sensor_event::cmdReserveDeviceSdrRepository, 2301 ipmi::Privilege::User, ipmiStorageReserveSDR); 2302 2303 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 2304 ipmi::storage::cmdReserveSdrRepository, 2305 ipmi::Privilege::User, ipmiStorageReserveSDR); 2306 2307 // <Get Sdr> 2308 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2309 ipmi::sensor_event::cmdGetDeviceSdr, 2310 ipmi::Privilege::User, ipmiStorageGetSDR); 2311 2312 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 2313 ipmi::storage::cmdGetSdr, ipmi::Privilege::User, 2314 ipmiStorageGetSDR); 2315 } 2316 } // namespace ipmi 2317