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