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 auto status = getSensorConnection(ctx, sensnum, connection, path); 647 if (status) 648 { 649 return ipmi::response(status); 650 } 651 652 #ifdef FEATURE_HYBRID_SENSORS 653 if (auto sensor = findStaticSensor(path); 654 sensor != ipmi::sensor::sensors.end() && 655 getSensorEventTypeFromPath(path) != 656 static_cast<uint8_t>(SensorEventTypeCodes::threshold)) 657 { 658 if (ipmi::sensor::Mutability::Read != 659 (sensor->second.mutability & ipmi::sensor::Mutability::Read)) 660 { 661 return ipmi::responseIllegalCommand(); 662 } 663 664 uint8_t operation; 665 try 666 { 667 ipmi::sensor::GetSensorResponse getResponse = 668 sensor->second.getFunc(sensor->second); 669 670 if (getResponse.readingOrStateUnavailable) 671 { 672 operation |= static_cast<uint8_t>( 673 IPMISensorReadingByte2::readingStateUnavailable); 674 } 675 if (getResponse.scanningEnabled) 676 { 677 operation |= static_cast<uint8_t>( 678 IPMISensorReadingByte2::sensorScanningEnable); 679 } 680 if (getResponse.allEventMessagesEnabled) 681 { 682 operation |= static_cast<uint8_t>( 683 IPMISensorReadingByte2::eventMessagesEnable); 684 } 685 return ipmi::responseSuccess( 686 getResponse.reading, operation, 687 getResponse.thresholdLevelsStates, 688 getResponse.discreteReadingSensorStates); 689 } 690 catch (const std::exception& e) 691 { 692 operation |= static_cast<uint8_t>( 693 IPMISensorReadingByte2::readingStateUnavailable); 694 return ipmi::responseSuccess(0, operation, 0, std::nullopt); 695 } 696 } 697 #endif 698 699 DbusInterfaceMap sensorMap; 700 if (!getSensorMap(ctx, connection, path, sensorMap)) 701 { 702 return ipmi::responseResponseError(); 703 } 704 auto sensorObject = sensorMap.find(sensor::sensorInterface); 705 706 if (sensorObject == sensorMap.end() || 707 sensorObject->second.find("Value") == sensorObject->second.end()) 708 { 709 return ipmi::responseResponseError(); 710 } 711 auto& valueVariant = sensorObject->second["Value"]; 712 double reading = std::visit(VariantToDoubleVisitor(), valueVariant); 713 714 double max = 0; 715 double min = 0; 716 getSensorMaxMin(sensorMap, max, min); 717 718 int16_t mValue = 0; 719 int16_t bValue = 0; 720 int8_t rExp = 0; 721 int8_t bExp = 0; 722 bool bSigned = false; 723 724 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 725 { 726 return ipmi::responseResponseError(); 727 } 728 729 uint8_t value = 730 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned); 731 uint8_t operation = 732 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable); 733 operation |= 734 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable); 735 bool notReading = std::isnan(reading); 736 737 if (!notReading) 738 { 739 auto availableObject = 740 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability"); 741 if (availableObject != sensorMap.end()) 742 { 743 auto findAvailable = availableObject->second.find("Available"); 744 if (findAvailable != availableObject->second.end()) 745 { 746 bool* available = std::get_if<bool>(&(findAvailable->second)); 747 if (available && !(*available)) 748 { 749 notReading = true; 750 } 751 } 752 } 753 } 754 755 if (notReading) 756 { 757 operation |= static_cast<uint8_t>( 758 IPMISensorReadingByte2::readingStateUnavailable); 759 } 760 761 if constexpr (details::enableInstrumentation) 762 { 763 int byteValue; 764 if (bSigned) 765 { 766 byteValue = static_cast<int>(static_cast<int8_t>(value)); 767 } 768 else 769 { 770 byteValue = static_cast<int>(static_cast<uint8_t>(value)); 771 } 772 773 // Keep stats on the reading just obtained, even if it is "NaN" 774 if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue)) 775 { 776 // This is the first reading, show the coefficients 777 double step = (max - min) / 255.0; 778 std::cerr << "IPMI sensor " 779 << details::sdrStatsTable.getName(sensnum) 780 << ": Range min=" << min << " max=" << max 781 << ", step=" << step 782 << ", Coefficients mValue=" << static_cast<int>(mValue) 783 << " rExp=" << static_cast<int>(rExp) 784 << " bValue=" << static_cast<int>(bValue) 785 << " bExp=" << static_cast<int>(bExp) 786 << " bSigned=" << static_cast<int>(bSigned) << "\n"; 787 } 788 } 789 790 uint8_t thresholds = 0; 791 792 auto warningObject = 793 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 794 if (warningObject != sensorMap.end()) 795 { 796 auto alarmHigh = warningObject->second.find("WarningAlarmHigh"); 797 auto alarmLow = warningObject->second.find("WarningAlarmLow"); 798 if (alarmHigh != warningObject->second.end()) 799 { 800 if (std::get<bool>(alarmHigh->second)) 801 { 802 thresholds |= static_cast<uint8_t>( 803 IPMISensorReadingByte3::upperNonCritical); 804 } 805 } 806 if (alarmLow != warningObject->second.end()) 807 { 808 if (std::get<bool>(alarmLow->second)) 809 { 810 thresholds |= static_cast<uint8_t>( 811 IPMISensorReadingByte3::lowerNonCritical); 812 } 813 } 814 } 815 816 auto criticalObject = 817 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 818 if (criticalObject != sensorMap.end()) 819 { 820 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh"); 821 auto alarmLow = criticalObject->second.find("CriticalAlarmLow"); 822 if (alarmHigh != criticalObject->second.end()) 823 { 824 if (std::get<bool>(alarmHigh->second)) 825 { 826 thresholds |= 827 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical); 828 } 829 } 830 if (alarmLow != criticalObject->second.end()) 831 { 832 if (std::get<bool>(alarmLow->second)) 833 { 834 thresholds |= 835 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical); 836 } 837 } 838 } 839 840 // no discrete as of today so optional byte is never returned 841 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt); 842 } 843 844 /** @brief implements the Set Sensor threshold command 845 * @param sensorNumber - sensor number 846 * @param lowerNonCriticalThreshMask 847 * @param lowerCriticalThreshMask 848 * @param lowerNonRecovThreshMask 849 * @param upperNonCriticalThreshMask 850 * @param upperCriticalThreshMask 851 * @param upperNonRecovThreshMask 852 * @param reserved 853 * @param lowerNonCritical - lower non-critical threshold 854 * @param lowerCritical - Lower critical threshold 855 * @param lowerNonRecoverable - Lower non recovarable threshold 856 * @param upperNonCritical - Upper non-critical threshold 857 * @param upperCritical - Upper critical 858 * @param upperNonRecoverable - Upper Non-recoverable 859 * 860 * @returns IPMI completion code 861 */ 862 ipmi::RspType<> ipmiSenSetSensorThresholds( 863 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask, 864 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask, 865 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask, 866 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical, 867 uint8_t lowerCritical, uint8_t lowerNonRecoverable, 868 uint8_t upperNonCritical, uint8_t upperCritical, 869 uint8_t upperNonRecoverable) 870 { 871 if (reserved) 872 { 873 return ipmi::responseInvalidFieldRequest(); 874 } 875 876 // lower nc and upper nc not suppported on any sensor 877 if (lowerNonRecovThreshMask || upperNonRecovThreshMask) 878 { 879 return ipmi::responseInvalidFieldRequest(); 880 } 881 882 // if none of the threshold mask are set, nothing to do 883 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask | 884 lowerNonRecovThreshMask | upperNonCriticalThreshMask | 885 upperCriticalThreshMask | upperNonRecovThreshMask)) 886 { 887 return ipmi::responseSuccess(); 888 } 889 890 std::string connection; 891 std::string path; 892 893 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path); 894 if (status) 895 { 896 return ipmi::response(status); 897 } 898 DbusInterfaceMap sensorMap; 899 if (!getSensorMap(ctx, connection, path, sensorMap)) 900 { 901 return ipmi::responseResponseError(); 902 } 903 904 double max = 0; 905 double min = 0; 906 getSensorMaxMin(sensorMap, max, min); 907 908 int16_t mValue = 0; 909 int16_t bValue = 0; 910 int8_t rExp = 0; 911 int8_t bExp = 0; 912 bool bSigned = false; 913 914 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 915 { 916 return ipmi::responseResponseError(); 917 } 918 919 // store a vector of property name, value to set, and interface 920 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet; 921 922 // define the indexes of the tuple 923 constexpr uint8_t propertyName = 0; 924 constexpr uint8_t thresholdValue = 1; 925 constexpr uint8_t interface = 2; 926 // verifiy all needed fields are present 927 if (lowerCriticalThreshMask || upperCriticalThreshMask) 928 { 929 auto findThreshold = 930 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 931 if (findThreshold == sensorMap.end()) 932 { 933 return ipmi::responseInvalidFieldRequest(); 934 } 935 if (lowerCriticalThreshMask) 936 { 937 auto findLower = findThreshold->second.find("CriticalLow"); 938 if (findLower == findThreshold->second.end()) 939 { 940 return ipmi::responseInvalidFieldRequest(); 941 } 942 thresholdsToSet.emplace_back("CriticalLow", lowerCritical, 943 findThreshold->first); 944 } 945 if (upperCriticalThreshMask) 946 { 947 auto findUpper = findThreshold->second.find("CriticalHigh"); 948 if (findUpper == findThreshold->second.end()) 949 { 950 return ipmi::responseInvalidFieldRequest(); 951 } 952 thresholdsToSet.emplace_back("CriticalHigh", upperCritical, 953 findThreshold->first); 954 } 955 } 956 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask) 957 { 958 auto findThreshold = 959 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 960 if (findThreshold == sensorMap.end()) 961 { 962 return ipmi::responseInvalidFieldRequest(); 963 } 964 if (lowerNonCriticalThreshMask) 965 { 966 auto findLower = findThreshold->second.find("WarningLow"); 967 if (findLower == findThreshold->second.end()) 968 { 969 return ipmi::responseInvalidFieldRequest(); 970 } 971 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical, 972 findThreshold->first); 973 } 974 if (upperNonCriticalThreshMask) 975 { 976 auto findUpper = findThreshold->second.find("WarningHigh"); 977 if (findUpper == findThreshold->second.end()) 978 { 979 return ipmi::responseInvalidFieldRequest(); 980 } 981 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical, 982 findThreshold->first); 983 } 984 } 985 for (const auto& property : thresholdsToSet) 986 { 987 // from section 36.3 in the IPMI Spec, assume all linear 988 double valueToSet = ((mValue * std::get<thresholdValue>(property)) + 989 (bValue * std::pow(10.0, bExp))) * 990 std::pow(10.0, rExp); 991 setDbusProperty( 992 *getSdBus(), connection, path, std::get<interface>(property), 993 std::get<propertyName>(property), ipmi::Value(valueToSet)); 994 } 995 return ipmi::responseSuccess(); 996 } 997 998 IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap) 999 { 1000 IPMIThresholds resp; 1001 auto warningInterface = 1002 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 1003 auto criticalInterface = 1004 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 1005 1006 if ((warningInterface != sensorMap.end()) || 1007 (criticalInterface != sensorMap.end())) 1008 { 1009 auto sensorPair = sensorMap.find(sensor::sensorInterface); 1010 1011 if (sensorPair == sensorMap.end()) 1012 { 1013 // should not have been able to find a sensor not implementing 1014 // the sensor object 1015 throw std::runtime_error("Invalid sensor map"); 1016 } 1017 1018 double max = 0; 1019 double min = 0; 1020 getSensorMaxMin(sensorMap, max, min); 1021 1022 int16_t mValue = 0; 1023 int16_t bValue = 0; 1024 int8_t rExp = 0; 1025 int8_t bExp = 0; 1026 bool bSigned = false; 1027 1028 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 1029 { 1030 throw std::runtime_error("Invalid sensor atrributes"); 1031 } 1032 if (warningInterface != sensorMap.end()) 1033 { 1034 auto& warningMap = warningInterface->second; 1035 1036 auto warningHigh = warningMap.find("WarningHigh"); 1037 auto warningLow = warningMap.find("WarningLow"); 1038 1039 if (warningHigh != warningMap.end()) 1040 { 1041 1042 double value = 1043 std::visit(VariantToDoubleVisitor(), warningHigh->second); 1044 resp.warningHigh = scaleIPMIValueFromDouble( 1045 value, mValue, rExp, bValue, bExp, bSigned); 1046 } 1047 if (warningLow != warningMap.end()) 1048 { 1049 double value = 1050 std::visit(VariantToDoubleVisitor(), warningLow->second); 1051 resp.warningLow = scaleIPMIValueFromDouble( 1052 value, mValue, rExp, bValue, bExp, bSigned); 1053 } 1054 } 1055 if (criticalInterface != sensorMap.end()) 1056 { 1057 auto& criticalMap = criticalInterface->second; 1058 1059 auto criticalHigh = criticalMap.find("CriticalHigh"); 1060 auto criticalLow = criticalMap.find("CriticalLow"); 1061 1062 if (criticalHigh != criticalMap.end()) 1063 { 1064 double value = 1065 std::visit(VariantToDoubleVisitor(), criticalHigh->second); 1066 resp.criticalHigh = scaleIPMIValueFromDouble( 1067 value, mValue, rExp, bValue, bExp, bSigned); 1068 } 1069 if (criticalLow != criticalMap.end()) 1070 { 1071 double value = 1072 std::visit(VariantToDoubleVisitor(), criticalLow->second); 1073 resp.criticalLow = scaleIPMIValueFromDouble( 1074 value, mValue, rExp, bValue, bExp, bSigned); 1075 } 1076 } 1077 } 1078 return resp; 1079 } 1080 1081 ipmi::RspType<uint8_t, // readable 1082 uint8_t, // lowerNCrit 1083 uint8_t, // lowerCrit 1084 uint8_t, // lowerNrecoverable 1085 uint8_t, // upperNC 1086 uint8_t, // upperCrit 1087 uint8_t> // upperNRecoverable 1088 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber) 1089 { 1090 std::string connection; 1091 std::string path; 1092 1093 auto status = getSensorConnection(ctx, sensorNumber, connection, path); 1094 if (status) 1095 { 1096 return ipmi::response(status); 1097 } 1098 1099 DbusInterfaceMap sensorMap; 1100 if (!getSensorMap(ctx, connection, path, sensorMap)) 1101 { 1102 return ipmi::responseResponseError(); 1103 } 1104 1105 IPMIThresholds thresholdData; 1106 try 1107 { 1108 thresholdData = getIPMIThresholds(sensorMap); 1109 } 1110 catch (const std::exception&) 1111 { 1112 return ipmi::responseResponseError(); 1113 } 1114 1115 uint8_t readable = 0; 1116 uint8_t lowerNC = 0; 1117 uint8_t lowerCritical = 0; 1118 uint8_t lowerNonRecoverable = 0; 1119 uint8_t upperNC = 0; 1120 uint8_t upperCritical = 0; 1121 uint8_t upperNonRecoverable = 0; 1122 1123 if (thresholdData.warningHigh) 1124 { 1125 readable |= 1126 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical); 1127 upperNC = *thresholdData.warningHigh; 1128 } 1129 if (thresholdData.warningLow) 1130 { 1131 readable |= 1132 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical); 1133 lowerNC = *thresholdData.warningLow; 1134 } 1135 1136 if (thresholdData.criticalHigh) 1137 { 1138 readable |= 1139 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical); 1140 upperCritical = *thresholdData.criticalHigh; 1141 } 1142 if (thresholdData.criticalLow) 1143 { 1144 readable |= 1145 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical); 1146 lowerCritical = *thresholdData.criticalLow; 1147 } 1148 1149 return ipmi::responseSuccess(readable, lowerNC, lowerCritical, 1150 lowerNonRecoverable, upperNC, upperCritical, 1151 upperNonRecoverable); 1152 } 1153 1154 /** @brief implements the get Sensor event enable command 1155 * @param sensorNumber - sensor number 1156 * 1157 * @returns IPMI completion code plus response data 1158 * - enabled - Sensor Event messages 1159 * - assertionEnabledLsb - Assertion event messages 1160 * - assertionEnabledMsb - Assertion event messages 1161 * - deassertionEnabledLsb - Deassertion event messages 1162 * - deassertionEnabledMsb - Deassertion event messages 1163 */ 1164 1165 ipmi::RspType<uint8_t, // enabled 1166 uint8_t, // assertionEnabledLsb 1167 uint8_t, // assertionEnabledMsb 1168 uint8_t, // deassertionEnabledLsb 1169 uint8_t> // deassertionEnabledMsb 1170 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum) 1171 { 1172 std::string connection; 1173 std::string path; 1174 1175 uint8_t enabled = 0; 1176 uint8_t assertionEnabledLsb = 0; 1177 uint8_t assertionEnabledMsb = 0; 1178 uint8_t deassertionEnabledLsb = 0; 1179 uint8_t deassertionEnabledMsb = 0; 1180 1181 auto status = getSensorConnection(ctx, sensorNum, connection, path); 1182 if (status) 1183 { 1184 return ipmi::response(status); 1185 } 1186 1187 #ifdef FEATURE_HYBRID_SENSORS 1188 if (auto sensor = findStaticSensor(path); 1189 sensor != ipmi::sensor::sensors.end() && 1190 getSensorEventTypeFromPath(path) != 1191 static_cast<uint8_t>(SensorEventTypeCodes::threshold)) 1192 { 1193 enabled = static_cast<uint8_t>( 1194 IPMISensorEventEnableByte2::sensorScanningEnable); 1195 uint16_t assertionEnabled = 0; 1196 for (auto& offsetValMap : sensor->second.propertyInterfaces.begin() 1197 ->second.begin() 1198 ->second.second) 1199 { 1200 assertionEnabled |= (1 << offsetValMap.first); 1201 } 1202 assertionEnabledLsb = static_cast<uint8_t>((assertionEnabled & 0xFF)); 1203 assertionEnabledMsb = 1204 static_cast<uint8_t>(((assertionEnabled >> 8) & 0xFF)); 1205 1206 return ipmi::responseSuccess(enabled, assertionEnabledLsb, 1207 assertionEnabledMsb, deassertionEnabledLsb, 1208 deassertionEnabledMsb); 1209 } 1210 #endif 1211 1212 DbusInterfaceMap sensorMap; 1213 if (!getSensorMap(ctx, connection, path, sensorMap)) 1214 { 1215 return ipmi::responseResponseError(); 1216 } 1217 1218 auto warningInterface = 1219 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 1220 auto criticalInterface = 1221 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 1222 if ((warningInterface != sensorMap.end()) || 1223 (criticalInterface != sensorMap.end())) 1224 { 1225 enabled = static_cast<uint8_t>( 1226 IPMISensorEventEnableByte2::sensorScanningEnable); 1227 if (warningInterface != sensorMap.end()) 1228 { 1229 auto& warningMap = warningInterface->second; 1230 1231 auto warningHigh = warningMap.find("WarningHigh"); 1232 auto warningLow = warningMap.find("WarningLow"); 1233 if (warningHigh != warningMap.end()) 1234 { 1235 assertionEnabledLsb |= static_cast<uint8_t>( 1236 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh); 1237 deassertionEnabledLsb |= static_cast<uint8_t>( 1238 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow); 1239 } 1240 if (warningLow != warningMap.end()) 1241 { 1242 assertionEnabledLsb |= static_cast<uint8_t>( 1243 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow); 1244 deassertionEnabledLsb |= static_cast<uint8_t>( 1245 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh); 1246 } 1247 } 1248 if (criticalInterface != sensorMap.end()) 1249 { 1250 auto& criticalMap = criticalInterface->second; 1251 1252 auto criticalHigh = criticalMap.find("CriticalHigh"); 1253 auto criticalLow = criticalMap.find("CriticalLow"); 1254 1255 if (criticalHigh != criticalMap.end()) 1256 { 1257 assertionEnabledMsb |= static_cast<uint8_t>( 1258 IPMISensorEventEnableThresholds::upperCriticalGoingHigh); 1259 deassertionEnabledMsb |= static_cast<uint8_t>( 1260 IPMISensorEventEnableThresholds::upperCriticalGoingLow); 1261 } 1262 if (criticalLow != criticalMap.end()) 1263 { 1264 assertionEnabledLsb |= static_cast<uint8_t>( 1265 IPMISensorEventEnableThresholds::lowerCriticalGoingLow); 1266 deassertionEnabledLsb |= static_cast<uint8_t>( 1267 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh); 1268 } 1269 } 1270 } 1271 1272 return ipmi::responseSuccess(enabled, assertionEnabledLsb, 1273 assertionEnabledMsb, deassertionEnabledLsb, 1274 deassertionEnabledMsb); 1275 } 1276 1277 /** @brief implements the get Sensor event status command 1278 * @param sensorNumber - sensor number, FFh = reserved 1279 * 1280 * @returns IPMI completion code plus response data 1281 * - sensorEventStatus - Sensor Event messages state 1282 * - assertions - Assertion event messages 1283 * - deassertions - Deassertion event messages 1284 */ 1285 ipmi::RspType<uint8_t, // sensorEventStatus 1286 std::bitset<16>, // assertions 1287 std::bitset<16> // deassertion 1288 > 1289 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum) 1290 { 1291 if (sensorNum == reservedSensorNumber) 1292 { 1293 return ipmi::responseInvalidFieldRequest(); 1294 } 1295 1296 std::string connection; 1297 std::string path; 1298 auto status = getSensorConnection(ctx, sensorNum, connection, path); 1299 if (status) 1300 { 1301 phosphor::logging::log<phosphor::logging::level::ERR>( 1302 "ipmiSenGetSensorEventStatus: Sensor connection Error", 1303 phosphor::logging::entry("SENSOR=%d", sensorNum)); 1304 return ipmi::response(status); 1305 } 1306 1307 #ifdef FEATURE_HYBRID_SENSORS 1308 if (auto sensor = findStaticSensor(path); 1309 sensor != ipmi::sensor::sensors.end() && 1310 getSensorEventTypeFromPath(path) != 1311 static_cast<uint8_t>(SensorEventTypeCodes::threshold)) 1312 { 1313 auto response = ipmi::sensor::get::mapDbusToAssertion( 1314 sensor->second, path, sensor->second.sensorInterface); 1315 std::bitset<16> assertions; 1316 // deassertions are not used. 1317 std::bitset<16> deassertions = 0; 1318 uint8_t sensorEventStatus; 1319 if (response.readingOrStateUnavailable) 1320 { 1321 sensorEventStatus |= static_cast<uint8_t>( 1322 IPMISensorReadingByte2::readingStateUnavailable); 1323 } 1324 if (response.scanningEnabled) 1325 { 1326 sensorEventStatus |= static_cast<uint8_t>( 1327 IPMISensorReadingByte2::sensorScanningEnable); 1328 } 1329 if (response.allEventMessagesEnabled) 1330 { 1331 sensorEventStatus |= static_cast<uint8_t>( 1332 IPMISensorReadingByte2::eventMessagesEnable); 1333 } 1334 assertions |= response.discreteReadingSensorStates << 8; 1335 assertions |= response.thresholdLevelsStates; 1336 return ipmi::responseSuccess(sensorEventStatus, assertions, 1337 deassertions); 1338 } 1339 #endif 1340 1341 DbusInterfaceMap sensorMap; 1342 if (!getSensorMap(ctx, connection, path, sensorMap)) 1343 { 1344 phosphor::logging::log<phosphor::logging::level::ERR>( 1345 "ipmiSenGetSensorEventStatus: Sensor Mapping Error", 1346 phosphor::logging::entry("SENSOR=%s", path.c_str())); 1347 return ipmi::responseResponseError(); 1348 } 1349 1350 uint8_t sensorEventStatus = 1351 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable); 1352 std::bitset<16> assertions = 0; 1353 std::bitset<16> deassertions = 0; 1354 1355 // handle VR typed sensor 1356 auto vrInterface = sensorMap.find(sensor::vrInterface); 1357 if (vrInterface != sensorMap.end()) 1358 { 1359 if (!sensor::getVrEventStatus(ctx, connection, path, 1360 vrInterface->second, assertions)) 1361 { 1362 return ipmi::responseResponseError(); 1363 } 1364 1365 // both Event Message and Sensor Scanning are disable for VR. 1366 sensorEventStatus = 0; 1367 return ipmi::responseSuccess(sensorEventStatus, assertions, 1368 deassertions); 1369 } 1370 1371 auto warningInterface = 1372 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 1373 auto criticalInterface = 1374 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 1375 1376 std::optional<bool> criticalDeassertHigh = 1377 thresholdDeassertMap[path]["CriticalAlarmHigh"]; 1378 std::optional<bool> criticalDeassertLow = 1379 thresholdDeassertMap[path]["CriticalAlarmLow"]; 1380 std::optional<bool> warningDeassertHigh = 1381 thresholdDeassertMap[path]["WarningAlarmHigh"]; 1382 std::optional<bool> warningDeassertLow = 1383 thresholdDeassertMap[path]["WarningAlarmLow"]; 1384 1385 if (criticalDeassertHigh && !*criticalDeassertHigh) 1386 { 1387 deassertions.set(static_cast<size_t>( 1388 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh)); 1389 } 1390 if (criticalDeassertLow && !*criticalDeassertLow) 1391 { 1392 deassertions.set(static_cast<size_t>( 1393 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow)); 1394 } 1395 if (warningDeassertHigh && !*warningDeassertHigh) 1396 { 1397 deassertions.set(static_cast<size_t>( 1398 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh)); 1399 } 1400 if (warningDeassertLow && !*warningDeassertLow) 1401 { 1402 deassertions.set(static_cast<size_t>( 1403 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh)); 1404 } 1405 if ((warningInterface != sensorMap.end()) || 1406 (criticalInterface != sensorMap.end())) 1407 { 1408 sensorEventStatus = static_cast<size_t>( 1409 IPMISensorEventEnableByte2::eventMessagesEnable); 1410 if (warningInterface != sensorMap.end()) 1411 { 1412 auto& warningMap = warningInterface->second; 1413 1414 auto warningHigh = warningMap.find("WarningAlarmHigh"); 1415 auto warningLow = warningMap.find("WarningAlarmLow"); 1416 auto warningHighAlarm = false; 1417 auto warningLowAlarm = false; 1418 1419 if (warningHigh != warningMap.end()) 1420 { 1421 warningHighAlarm = std::get<bool>(warningHigh->second); 1422 } 1423 if (warningLow != warningMap.end()) 1424 { 1425 warningLowAlarm = std::get<bool>(warningLow->second); 1426 } 1427 if (warningHighAlarm) 1428 { 1429 assertions.set( 1430 static_cast<size_t>(IPMIGetSensorEventEnableThresholds:: 1431 upperNonCriticalGoingHigh)); 1432 } 1433 if (warningLowAlarm) 1434 { 1435 assertions.set( 1436 static_cast<size_t>(IPMIGetSensorEventEnableThresholds:: 1437 lowerNonCriticalGoingLow)); 1438 } 1439 } 1440 if (criticalInterface != sensorMap.end()) 1441 { 1442 auto& criticalMap = criticalInterface->second; 1443 1444 auto criticalHigh = criticalMap.find("CriticalAlarmHigh"); 1445 auto criticalLow = criticalMap.find("CriticalAlarmLow"); 1446 auto criticalHighAlarm = false; 1447 auto criticalLowAlarm = false; 1448 1449 if (criticalHigh != criticalMap.end()) 1450 { 1451 criticalHighAlarm = std::get<bool>(criticalHigh->second); 1452 } 1453 if (criticalLow != criticalMap.end()) 1454 { 1455 criticalLowAlarm = std::get<bool>(criticalLow->second); 1456 } 1457 if (criticalHighAlarm) 1458 { 1459 assertions.set( 1460 static_cast<size_t>(IPMIGetSensorEventEnableThresholds:: 1461 upperCriticalGoingHigh)); 1462 } 1463 if (criticalLowAlarm) 1464 { 1465 assertions.set(static_cast<size_t>( 1466 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow)); 1467 } 1468 } 1469 } 1470 1471 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions); 1472 } 1473 1474 // Construct a type 1 SDR for threshold sensor. 1475 void constructSensorSdrHeaderKey(uint16_t sensorNum, uint16_t recordID, 1476 get_sdr::SensorDataFullRecord& record) 1477 { 1478 get_sdr::header::set_record_id( 1479 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record)); 1480 1481 uint8_t sensornumber = static_cast<uint8_t>(sensorNum); 1482 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8); 1483 1484 record.header.sdr_version = ipmiSdrVersion; 1485 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD; 1486 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) - 1487 sizeof(get_sdr::SensorDataRecordHeader); 1488 record.key.owner_id = bmcI2CAddr; 1489 record.key.owner_lun = lun; 1490 record.key.sensor_number = sensornumber; 1491 } 1492 bool constructSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum, 1493 uint16_t recordID, const std::string& service, 1494 const std::string& path, 1495 get_sdr::SensorDataFullRecord& record) 1496 { 1497 uint8_t sensornumber = static_cast<uint8_t>(sensorNum); 1498 constructSensorSdrHeaderKey(sensorNum, recordID, record); 1499 1500 DbusInterfaceMap sensorMap; 1501 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod)) 1502 { 1503 phosphor::logging::log<phosphor::logging::level::ERR>( 1504 "Failed to update sensor map for threshold sensor", 1505 phosphor::logging::entry("SERVICE=%s", service.c_str()), 1506 phosphor::logging::entry("PATH=%s", path.c_str())); 1507 return false; 1508 } 1509 1510 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis 1511 record.body.sensor_type = getSensorTypeFromPath(path); 1512 std::string type = getSensorTypeStringFromPath(path); 1513 auto typeCstr = type.c_str(); 1514 auto findUnits = sensorUnits.find(typeCstr); 1515 if (findUnits != sensorUnits.end()) 1516 { 1517 record.body.sensor_units_2_base = 1518 static_cast<uint8_t>(findUnits->second); 1519 } // else default 0x0 unspecified 1520 1521 record.body.event_reading_type = getSensorEventTypeFromPath(path); 1522 1523 auto sensorObject = sensorMap.find(sensor::sensorInterface); 1524 if (sensorObject == sensorMap.end()) 1525 { 1526 phosphor::logging::log<phosphor::logging::level::ERR>( 1527 "getSensorDataRecord: sensorObject error"); 1528 return false; 1529 } 1530 1531 uint8_t entityId = 0; 1532 uint8_t entityInstance = 0x01; 1533 1534 // follow the association chain to get the parent board's entityid and 1535 // entityInstance 1536 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance); 1537 1538 record.body.entity_id = entityId; 1539 record.body.entity_instance = entityInstance; 1540 1541 double max = 0; 1542 double min = 0; 1543 getSensorMaxMin(sensorMap, max, min); 1544 1545 int16_t mValue = 0; 1546 int8_t rExp = 0; 1547 int16_t bValue = 0; 1548 int8_t bExp = 0; 1549 bool bSigned = false; 1550 1551 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 1552 { 1553 phosphor::logging::log<phosphor::logging::level::ERR>( 1554 "getSensorDataRecord: getSensorAttributes error"); 1555 return false; 1556 } 1557 1558 // The record.body is a struct SensorDataFullRecordBody 1559 // from sensorhandler.hpp in phosphor-ipmi-host. 1560 // The meaning of these bits appears to come from 1561 // table 43.1 of the IPMI spec. 1562 // The above 5 sensor attributes are stuffed in as follows: 1563 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned 1564 // Byte 22-24 are for other purposes 1565 // Byte 25 = MMMMMMMM = LSB of M 1566 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance 1567 // Byte 27 = BBBBBBBB = LSB of B 1568 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy 1569 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy 1570 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed) 1571 1572 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4 1573 record.body.m_lsb = mValue & 0xFF; 1574 1575 uint8_t mBitSign = (mValue < 0) ? 1 : 0; 1576 uint8_t mBitNine = (mValue & 0x0100) >> 8; 1577 1578 // move the smallest bit of the MSB into place (bit 9) 1579 // the MSbs are bits 7:8 in m_msb_and_tolerance 1580 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6); 1581 1582 record.body.b_lsb = bValue & 0xFF; 1583 1584 uint8_t bBitSign = (bValue < 0) ? 1 : 0; 1585 uint8_t bBitNine = (bValue & 0x0100) >> 8; 1586 1587 // move the smallest bit of the MSB into place (bit 9) 1588 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb 1589 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6); 1590 1591 uint8_t rExpSign = (rExp < 0) ? 1 : 0; 1592 uint8_t rExpBits = rExp & 0x07; 1593 1594 uint8_t bExpSign = (bExp < 0) ? 1 : 0; 1595 uint8_t bExpBits = bExp & 0x07; 1596 1597 // move rExp and bExp into place 1598 record.body.r_b_exponents = 1599 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits; 1600 1601 // Set the analog reading byte interpretation accordingly 1602 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7; 1603 1604 // TODO(): Perhaps care about Tolerance, Accuracy, and so on 1605 // These seem redundant, but derivable from the above 5 attributes 1606 // Original comment said "todo fill out rest of units" 1607 1608 // populate sensor name from path 1609 auto name = sensor::parseSdrIdFromPath(path); 1610 record.body.id_string_info = name.size(); 1611 std::strncpy(record.body.id_string, name.c_str(), 1612 sizeof(record.body.id_string)); 1613 1614 // Remember the sensor name, as determined for this sensor number 1615 details::sdrStatsTable.updateName(sensornumber, name); 1616 1617 bool sensorSettable = false; 1618 auto mutability = 1619 sensorMap.find("xyz.openbmc_project.Sensor.ValueMutability"); 1620 if (mutability != sensorMap.end()) 1621 { 1622 sensorSettable = 1623 mappedVariant<bool>(mutability->second, "Mutable", false); 1624 } 1625 get_sdr::body::init_settable_state(sensorSettable, &record.body); 1626 1627 // Grant write permission to sensors deemed externally settable 1628 details::sdrWriteTable.setWritePermission(sensornumber, sensorSettable); 1629 1630 IPMIThresholds thresholdData; 1631 try 1632 { 1633 thresholdData = getIPMIThresholds(sensorMap); 1634 } 1635 catch (const std::exception&) 1636 { 1637 phosphor::logging::log<phosphor::logging::level::ERR>( 1638 "getSensorDataRecord: getIPMIThresholds error"); 1639 return false; 1640 } 1641 1642 if (thresholdData.criticalHigh) 1643 { 1644 record.body.upper_critical_threshold = *thresholdData.criticalHigh; 1645 record.body.supported_deassertions[1] |= static_cast<uint8_t>( 1646 IPMISensorEventEnableThresholds::criticalThreshold); 1647 record.body.supported_deassertions[1] |= static_cast<uint8_t>( 1648 IPMISensorEventEnableThresholds::upperCriticalGoingHigh); 1649 record.body.supported_assertions[1] |= static_cast<uint8_t>( 1650 IPMISensorEventEnableThresholds::upperCriticalGoingHigh); 1651 record.body.discrete_reading_setting_mask[0] |= 1652 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical); 1653 } 1654 if (thresholdData.warningHigh) 1655 { 1656 record.body.upper_noncritical_threshold = *thresholdData.warningHigh; 1657 record.body.supported_deassertions[1] |= static_cast<uint8_t>( 1658 IPMISensorEventEnableThresholds::nonCriticalThreshold); 1659 record.body.supported_deassertions[0] |= static_cast<uint8_t>( 1660 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh); 1661 record.body.supported_assertions[0] |= static_cast<uint8_t>( 1662 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh); 1663 record.body.discrete_reading_setting_mask[0] |= 1664 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical); 1665 } 1666 if (thresholdData.criticalLow) 1667 { 1668 record.body.lower_critical_threshold = *thresholdData.criticalLow; 1669 record.body.supported_assertions[1] |= static_cast<uint8_t>( 1670 IPMISensorEventEnableThresholds::criticalThreshold); 1671 record.body.supported_deassertions[0] |= static_cast<uint8_t>( 1672 IPMISensorEventEnableThresholds::lowerCriticalGoingLow); 1673 record.body.supported_assertions[0] |= static_cast<uint8_t>( 1674 IPMISensorEventEnableThresholds::lowerCriticalGoingLow); 1675 record.body.discrete_reading_setting_mask[0] |= 1676 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical); 1677 } 1678 if (thresholdData.warningLow) 1679 { 1680 record.body.lower_noncritical_threshold = *thresholdData.warningLow; 1681 record.body.supported_assertions[1] |= static_cast<uint8_t>( 1682 IPMISensorEventEnableThresholds::nonCriticalThreshold); 1683 record.body.supported_deassertions[0] |= static_cast<uint8_t>( 1684 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow); 1685 record.body.supported_assertions[0] |= static_cast<uint8_t>( 1686 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow); 1687 record.body.discrete_reading_setting_mask[0] |= 1688 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical); 1689 } 1690 1691 // everything that is readable is setable 1692 record.body.discrete_reading_setting_mask[1] = 1693 record.body.discrete_reading_setting_mask[0]; 1694 return true; 1695 } 1696 1697 #ifdef FEATURE_HYBRID_SENSORS 1698 // Construct a type 1 SDR for discrete Sensor typed sensor. 1699 void constructStaticSensorSdr(ipmi::Context::ptr ctx, uint16_t sensorNum, 1700 uint16_t recordID, 1701 ipmi::sensor::IdInfoMap::const_iterator sensor, 1702 get_sdr::SensorDataFullRecord& record) 1703 { 1704 constructSensorSdrHeaderKey(sensorNum, recordID, record); 1705 1706 record.body.entity_id = sensor->second.entityType; 1707 record.body.sensor_type = sensor->second.sensorType; 1708 record.body.event_reading_type = sensor->second.sensorReadingType; 1709 record.body.entity_instance = sensor->second.instance; 1710 if (ipmi::sensor::Mutability::Write == 1711 (sensor->second.mutability & ipmi::sensor::Mutability::Write)) 1712 { 1713 get_sdr::body::init_settable_state(true, &(record.body)); 1714 } 1715 1716 auto id_string = sensor->second.sensorName; 1717 1718 if (id_string.empty()) 1719 { 1720 id_string = sensor->second.sensorNameFunc(sensor->second); 1721 } 1722 1723 if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH) 1724 { 1725 get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH, 1726 &(record.body)); 1727 } 1728 else 1729 { 1730 get_sdr::body::set_id_strlen(id_string.length(), &(record.body)); 1731 } 1732 std::strncpy(record.body.id_string, id_string.c_str(), 1733 get_sdr::body::get_id_strlen(&(record.body))); 1734 } 1735 #endif 1736 1737 // Construct type 3 SDR header and key (for VR and other discrete sensors) 1738 void constructEventSdrHeaderKey(uint16_t sensorNum, uint16_t recordID, 1739 get_sdr::SensorDataEventRecord& record) 1740 { 1741 uint8_t sensornumber = static_cast<uint8_t>(sensorNum); 1742 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8); 1743 1744 get_sdr::header::set_record_id( 1745 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record)); 1746 1747 record.header.sdr_version = ipmiSdrVersion; 1748 record.header.record_type = get_sdr::SENSOR_DATA_EVENT_RECORD; 1749 record.header.record_length = sizeof(get_sdr::SensorDataEventRecord) - 1750 sizeof(get_sdr::SensorDataRecordHeader); 1751 record.key.owner_id = bmcI2CAddr; 1752 record.key.owner_lun = lun; 1753 record.key.sensor_number = sensornumber; 1754 1755 record.body.entity_id = 0x00; 1756 record.body.entity_instance = 0x01; 1757 } 1758 1759 // Construct a type 3 SDR for VR typed sensor(daemon). 1760 bool constructVrSdr(ipmi::Context::ptr ctx, uint16_t sensorNum, 1761 uint16_t recordID, const std::string& service, 1762 const std::string& path, 1763 get_sdr::SensorDataEventRecord& record) 1764 { 1765 uint8_t sensornumber = static_cast<uint8_t>(sensorNum); 1766 constructEventSdrHeaderKey(sensorNum, recordID, record); 1767 1768 DbusInterfaceMap sensorMap; 1769 if (!getSensorMap(ctx, service, path, sensorMap, sensorMapSdrUpdatePeriod)) 1770 { 1771 phosphor::logging::log<phosphor::logging::level::ERR>( 1772 "Failed to update sensor map for VR sensor", 1773 phosphor::logging::entry("SERVICE=%s", service.c_str()), 1774 phosphor::logging::entry("PATH=%s", path.c_str())); 1775 return false; 1776 } 1777 // follow the association chain to get the parent board's entityid and 1778 // entityInstance 1779 updateIpmiFromAssociation(path, sensorMap, record.body.entity_id, 1780 record.body.entity_instance); 1781 1782 // Sensor type is hardcoded as a module/board type instead of parsing from 1783 // sensor path. This is because VR control is allocated in an independent 1784 // path(/xyz/openbmc_project/vr/profile/...) which is not categorized by 1785 // types. 1786 static constexpr const uint8_t module_board_type = 0x15; 1787 record.body.sensor_type = module_board_type; 1788 record.body.event_reading_type = 0x00; 1789 1790 record.body.sensor_record_sharing_1 = 0x00; 1791 record.body.sensor_record_sharing_2 = 0x00; 1792 1793 // populate sensor name from path 1794 auto name = sensor::parseSdrIdFromPath(path); 1795 int nameSize = std::min(name.size(), sizeof(record.body.id_string)); 1796 record.body.id_string_info = nameSize; 1797 std::memset(record.body.id_string, 0x00, sizeof(record.body.id_string)); 1798 std::memcpy(record.body.id_string, name.c_str(), nameSize); 1799 1800 // Remember the sensor name, as determined for this sensor number 1801 details::sdrStatsTable.updateName(sensornumber, name); 1802 1803 return true; 1804 } 1805 1806 static int 1807 getSensorDataRecord(ipmi::Context::ptr ctx, 1808 std::vector<uint8_t>& recordData, uint16_t recordID, 1809 uint8_t readBytes = std::numeric_limits<uint8_t>::max()) 1810 { 1811 size_t fruCount = 0; 1812 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount); 1813 if (ret != ipmi::ccSuccess) 1814 { 1815 phosphor::logging::log<phosphor::logging::level::ERR>( 1816 "getSensorDataRecord: getFruSdrCount error"); 1817 return GENERAL_ERROR; 1818 } 1819 1820 auto& sensorTree = getSensorTree(); 1821 size_t lastRecord = 1822 sensorTree.size() + fruCount + ipmi::storage::type12Count + -1; 1823 if (recordID == lastRecordIndex) 1824 { 1825 recordID = lastRecord; 1826 } 1827 if (recordID > lastRecord) 1828 { 1829 phosphor::logging::log<phosphor::logging::level::ERR>( 1830 "getSensorDataRecord: recordID > lastRecord error"); 1831 return GENERAL_ERROR; 1832 } 1833 1834 if (recordID >= sensorTree.size()) 1835 { 1836 size_t fruIndex = recordID - sensorTree.size(); 1837 1838 if (fruIndex >= fruCount) 1839 { 1840 // handle type 12 hardcoded records 1841 size_t type12Index = fruIndex - fruCount; 1842 if (type12Index >= ipmi::storage::type12Count) 1843 { 1844 phosphor::logging::log<phosphor::logging::level::ERR>( 1845 "getSensorDataRecord: type12Index error"); 1846 return GENERAL_ERROR; 1847 } 1848 recordData = ipmi::storage::getType12SDRs(type12Index, recordID); 1849 } 1850 else 1851 { 1852 // handle fru records 1853 get_sdr::SensorDataFruRecord data; 1854 ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data); 1855 if (ret != IPMI_CC_OK) 1856 { 1857 return GENERAL_ERROR; 1858 } 1859 data.header.record_id_msb = recordID >> 8; 1860 data.header.record_id_lsb = recordID & 0xFF; 1861 recordData.insert(recordData.end(), (uint8_t*)&data, 1862 ((uint8_t*)&data) + sizeof(data)); 1863 } 1864 1865 return 0; 1866 } 1867 1868 std::string connection; 1869 std::string path; 1870 std::vector<std::string> interfaces; 1871 1872 auto status = 1873 getSensorConnection(ctx, recordID, connection, path, &interfaces); 1874 if (status) 1875 { 1876 phosphor::logging::log<phosphor::logging::level::ERR>( 1877 "getSensorDataRecord: getSensorConnection error"); 1878 return GENERAL_ERROR; 1879 } 1880 uint16_t sensorNum = getSensorNumberFromPath(path); 1881 if (sensorNum == invalidSensorNumber) 1882 { 1883 phosphor::logging::log<phosphor::logging::level::ERR>( 1884 "getSensorDataRecord: invalidSensorNumber"); 1885 return GENERAL_ERROR; 1886 } 1887 1888 // Construct full record (SDR type 1) for the threshold sensors 1889 if (std::find(interfaces.begin(), interfaces.end(), 1890 sensor::sensorInterface) != interfaces.end()) 1891 { 1892 get_sdr::SensorDataFullRecord record = {0}; 1893 1894 // If the request doesn't read SDR body, construct only header and key 1895 // part to avoid additional DBus transaction. 1896 if (readBytes <= sizeof(record.header) + sizeof(record.key)) 1897 { 1898 constructSensorSdrHeaderKey(sensorNum, recordID, record); 1899 } 1900 else if (!constructSensorSdr(ctx, sensorNum, recordID, connection, path, 1901 record)) 1902 { 1903 return GENERAL_ERROR; 1904 } 1905 1906 recordData.insert(recordData.end(), (uint8_t*)&record, 1907 ((uint8_t*)&record) + sizeof(record)); 1908 1909 return 0; 1910 } 1911 1912 #ifdef FEATURE_HYBRID_SENSORS 1913 if (auto sensor = findStaticSensor(path); 1914 sensor != ipmi::sensor::sensors.end() && 1915 getSensorEventTypeFromPath(path) != 1916 static_cast<uint8_t>(SensorEventTypeCodes::threshold)) 1917 { 1918 get_sdr::SensorDataFullRecord record = {0}; 1919 1920 // If the request doesn't read SDR body, construct only header and key 1921 // part to avoid additional DBus transaction. 1922 if (readBytes <= sizeof(record.header) + sizeof(record.key)) 1923 { 1924 constructSensorSdrHeaderKey(sensorNum, recordID, record); 1925 } 1926 else 1927 { 1928 constructStaticSensorSdr(ctx, sensorNum, recordID, sensor, record); 1929 } 1930 1931 recordData.insert(recordData.end(), (uint8_t*)&record, 1932 ((uint8_t*)&record) + sizeof(record)); 1933 1934 return 0; 1935 } 1936 #endif 1937 1938 // Contruct SDR type 3 record for VR sensor (daemon) 1939 if (std::find(interfaces.begin(), interfaces.end(), sensor::vrInterface) != 1940 interfaces.end()) 1941 { 1942 get_sdr::SensorDataEventRecord record = {0}; 1943 1944 // If the request doesn't read SDR body, construct only header and key 1945 // part to avoid additional DBus transaction. 1946 if (readBytes <= sizeof(record.header) + sizeof(record.key)) 1947 { 1948 constructEventSdrHeaderKey(sensorNum, recordID, record); 1949 } 1950 else if (!constructVrSdr(ctx, sensorNum, recordID, connection, path, 1951 record)) 1952 { 1953 return GENERAL_ERROR; 1954 } 1955 recordData.insert(recordData.end(), (uint8_t*)&record, 1956 ((uint8_t*)&record) + sizeof(record)); 1957 } 1958 1959 return 0; 1960 } 1961 1962 /** @brief implements the get SDR Info command 1963 * @param count - Operation 1964 * 1965 * @returns IPMI completion code plus response data 1966 * - sdrCount - sensor/SDR count 1967 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag 1968 */ 1969 static ipmi::RspType<uint8_t, // respcount 1970 uint8_t, // dynamic population flags 1971 uint32_t // last time a sensor was added 1972 > 1973 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx, 1974 std::optional<uint8_t> count) 1975 { 1976 auto& sensorTree = getSensorTree(); 1977 uint8_t sdrCount = 0; 1978 uint16_t recordID = 0; 1979 std::vector<uint8_t> record; 1980 // Sensors are dynamically allocated, and there is at least one LUN 1981 uint8_t lunsAndDynamicPopulation = 0x80; 1982 constexpr uint8_t getSdrCount = 0x01; 1983 constexpr uint8_t getSensorCount = 0x00; 1984 1985 if (!getSensorSubtree(sensorTree) || sensorTree.empty()) 1986 { 1987 return ipmi::responseResponseError(); 1988 } 1989 uint16_t numSensors = sensorTree.size(); 1990 if (count.value_or(0) == getSdrCount) 1991 { 1992 // Count the number of Type 1 SDR entries assigned to the LUN 1993 while (!getSensorDataRecord(ctx, record, recordID++)) 1994 { 1995 get_sdr::SensorDataRecordHeader* hdr = 1996 reinterpret_cast<get_sdr::SensorDataRecordHeader*>( 1997 record.data()); 1998 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD) 1999 { 2000 get_sdr::SensorDataFullRecord* recordData = 2001 reinterpret_cast<get_sdr::SensorDataFullRecord*>( 2002 record.data()); 2003 if (ctx->lun == recordData->key.owner_lun) 2004 { 2005 sdrCount++; 2006 } 2007 } 2008 } 2009 } 2010 else if (count.value_or(0) == getSensorCount) 2011 { 2012 // Return the number of sensors attached to the LUN 2013 if ((ctx->lun == 0) && (numSensors > 0)) 2014 { 2015 sdrCount = 2016 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors; 2017 } 2018 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN)) 2019 { 2020 sdrCount = (numSensors > (2 * maxSensorsPerLUN)) 2021 ? maxSensorsPerLUN 2022 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN; 2023 } 2024 else if (ctx->lun == 3) 2025 { 2026 if (numSensors <= maxIPMISensors) 2027 { 2028 sdrCount = 2029 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN; 2030 } 2031 else 2032 { 2033 // error 2034 throw std::out_of_range( 2035 "Maximum number of IPMI sensors exceeded."); 2036 } 2037 } 2038 } 2039 else 2040 { 2041 return ipmi::responseInvalidFieldRequest(); 2042 } 2043 2044 // Get Sensor count. This returns the number of sensors 2045 if (numSensors > 0) 2046 { 2047 lunsAndDynamicPopulation |= 1; 2048 } 2049 if (numSensors > maxSensorsPerLUN) 2050 { 2051 lunsAndDynamicPopulation |= 2; 2052 } 2053 if (numSensors >= (maxSensorsPerLUN * 2)) 2054 { 2055 lunsAndDynamicPopulation |= 8; 2056 } 2057 if (numSensors > maxIPMISensors) 2058 { 2059 // error 2060 throw std::out_of_range("Maximum number of IPMI sensors exceeded."); 2061 } 2062 2063 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation, 2064 sdrLastAdd); 2065 } 2066 2067 /* end sensor commands */ 2068 2069 /* storage commands */ 2070 2071 ipmi::RspType<uint8_t, // sdr version 2072 uint16_t, // record count 2073 uint16_t, // free space 2074 uint32_t, // most recent addition 2075 uint32_t, // most recent erase 2076 uint8_t // operationSupport 2077 > 2078 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx) 2079 { 2080 auto& sensorTree = getSensorTree(); 2081 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF; 2082 if (!getSensorSubtree(sensorTree) && sensorTree.empty()) 2083 { 2084 return ipmi::responseResponseError(); 2085 } 2086 2087 size_t fruCount = 0; 2088 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount); 2089 if (ret != ipmi::ccSuccess) 2090 { 2091 return ipmi::response(ret); 2092 } 2093 2094 uint16_t recordCount = 2095 sensorTree.size() + fruCount + ipmi::storage::type12Count; 2096 2097 uint8_t operationSupport = static_cast<uint8_t>( 2098 SdrRepositoryInfoOps::overflow); // write not supported 2099 2100 operationSupport |= 2101 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported); 2102 operationSupport |= static_cast<uint8_t>( 2103 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported); 2104 return ipmi::responseSuccess(ipmiSdrVersion, recordCount, 2105 unspecifiedFreeSpace, sdrLastAdd, 2106 sdrLastRemove, operationSupport); 2107 } 2108 2109 /** @brief implements the get SDR allocation info command 2110 * 2111 * @returns IPMI completion code plus response data 2112 * - allocUnits - Number of possible allocation units 2113 * - allocUnitSize - Allocation unit size in bytes. 2114 * - allocUnitFree - Number of free allocation units 2115 * - allocUnitLargestFree - Largest free block in allocation units 2116 * - maxRecordSize - Maximum record size in allocation units. 2117 */ 2118 ipmi::RspType<uint16_t, // allocUnits 2119 uint16_t, // allocUnitSize 2120 uint16_t, // allocUnitFree 2121 uint16_t, // allocUnitLargestFree 2122 uint8_t // maxRecordSize 2123 > 2124 ipmiStorageGetSDRAllocationInfo() 2125 { 2126 // 0000h unspecified number of alloc units 2127 constexpr uint16_t allocUnits = 0; 2128 2129 constexpr uint16_t allocUnitFree = 0; 2130 constexpr uint16_t allocUnitLargestFree = 0; 2131 // only allow one block at a time 2132 constexpr uint8_t maxRecordSize = 1; 2133 2134 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree, 2135 allocUnitLargestFree, maxRecordSize); 2136 } 2137 2138 /** @brief implements the reserve SDR command 2139 * @returns IPMI completion code plus response data 2140 * - sdrReservationID 2141 */ 2142 ipmi::RspType<uint16_t> ipmiStorageReserveSDR() 2143 { 2144 sdrReservationID++; 2145 if (sdrReservationID == 0) 2146 { 2147 sdrReservationID++; 2148 } 2149 2150 return ipmi::responseSuccess(sdrReservationID); 2151 } 2152 2153 ipmi::RspType<uint16_t, // next record ID 2154 std::vector<uint8_t> // payload 2155 > 2156 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID, 2157 uint16_t recordID, uint8_t offset, uint8_t bytesToRead) 2158 { 2159 size_t fruCount = 0; 2160 // reservation required for partial reads with non zero offset into 2161 // record 2162 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset) 2163 { 2164 phosphor::logging::log<phosphor::logging::level::ERR>( 2165 "ipmiStorageGetSDR: responseInvalidReservationId"); 2166 return ipmi::responseInvalidReservationId(); 2167 } 2168 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount); 2169 if (ret != ipmi::ccSuccess) 2170 { 2171 phosphor::logging::log<phosphor::logging::level::ERR>( 2172 "ipmiStorageGetSDR: getFruSdrCount error"); 2173 return ipmi::response(ret); 2174 } 2175 2176 auto& sensorTree = getSensorTree(); 2177 size_t lastRecord = 2178 sensorTree.size() + fruCount + ipmi::storage::type12Count - 1; 2179 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF; 2180 2181 if (!getSensorSubtree(sensorTree) && sensorTree.empty()) 2182 { 2183 phosphor::logging::log<phosphor::logging::level::ERR>( 2184 "ipmiStorageGetSDR: getSensorSubtree error"); 2185 return ipmi::responseResponseError(); 2186 } 2187 2188 std::vector<uint8_t> record; 2189 if (getSensorDataRecord(ctx, record, recordID, offset + bytesToRead)) 2190 { 2191 phosphor::logging::log<phosphor::logging::level::ERR>( 2192 "ipmiStorageGetSDR: fail to get SDR"); 2193 return ipmi::responseInvalidFieldRequest(); 2194 } 2195 get_sdr::SensorDataRecordHeader* hdr = 2196 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data()); 2197 if (!hdr) 2198 { 2199 phosphor::logging::log<phosphor::logging::level::ERR>( 2200 "ipmiStorageGetSDR: record header is null"); 2201 return ipmi::responseSuccess(nextRecordId, record); 2202 } 2203 2204 size_t sdrLength = 2205 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length; 2206 if (sdrLength < (offset + bytesToRead)) 2207 { 2208 bytesToRead = sdrLength - offset; 2209 } 2210 2211 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset; 2212 if (!respStart) 2213 { 2214 phosphor::logging::log<phosphor::logging::level::ERR>( 2215 "ipmiStorageGetSDR: record is null"); 2216 return ipmi::responseSuccess(nextRecordId, record); 2217 } 2218 2219 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead); 2220 2221 return ipmi::responseSuccess(nextRecordId, recordData); 2222 } 2223 /* end storage commands */ 2224 2225 void registerSensorFunctions() 2226 { 2227 // <Platform Event> 2228 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2229 ipmi::sensor_event::cmdPlatformEvent, 2230 ipmi::Privilege::Operator, ipmiSenPlatformEvent); 2231 2232 // <Set Sensor Reading and Event Status> 2233 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2234 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts, 2235 ipmi::Privilege::Operator, ipmiSetSensorReading); 2236 2237 // <Get Sensor Reading> 2238 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2239 ipmi::sensor_event::cmdGetSensorReading, 2240 ipmi::Privilege::User, ipmiSenGetSensorReading); 2241 2242 // <Get Sensor Threshold> 2243 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2244 ipmi::sensor_event::cmdGetSensorThreshold, 2245 ipmi::Privilege::User, ipmiSenGetSensorThresholds); 2246 2247 // <Set Sensor Threshold> 2248 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2249 ipmi::sensor_event::cmdSetSensorThreshold, 2250 ipmi::Privilege::Operator, 2251 ipmiSenSetSensorThresholds); 2252 2253 // <Get Sensor Event Enable> 2254 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2255 ipmi::sensor_event::cmdGetSensorEventEnable, 2256 ipmi::Privilege::User, ipmiSenGetSensorEventEnable); 2257 2258 // <Get Sensor Event Status> 2259 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2260 ipmi::sensor_event::cmdGetSensorEventStatus, 2261 ipmi::Privilege::User, ipmiSenGetSensorEventStatus); 2262 2263 // register all storage commands for both Sensor and Storage command 2264 // versions 2265 2266 // <Get SDR Repository Info> 2267 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 2268 ipmi::storage::cmdGetSdrRepositoryInfo, 2269 ipmi::Privilege::User, 2270 ipmiStorageGetSDRRepositoryInfo); 2271 2272 // <Get Device SDR Info> 2273 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2274 ipmi::sensor_event::cmdGetDeviceSdrInfo, 2275 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo); 2276 2277 // <Get SDR Allocation Info> 2278 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 2279 ipmi::storage::cmdGetSdrRepositoryAllocInfo, 2280 ipmi::Privilege::User, 2281 ipmiStorageGetSDRAllocationInfo); 2282 2283 // <Reserve SDR Repo> 2284 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2285 ipmi::sensor_event::cmdReserveDeviceSdrRepository, 2286 ipmi::Privilege::User, ipmiStorageReserveSDR); 2287 2288 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 2289 ipmi::storage::cmdReserveSdrRepository, 2290 ipmi::Privilege::User, ipmiStorageReserveSDR); 2291 2292 // <Get Sdr> 2293 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 2294 ipmi::sensor_event::cmdGetDeviceSdr, 2295 ipmi::Privilege::User, ipmiStorageGetSDR); 2296 2297 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 2298 ipmi::storage::cmdGetSdr, ipmi::Privilege::User, 2299 ipmiStorageGetSDR); 2300 } 2301 } // namespace ipmi 2302