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