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