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