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