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