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