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