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