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