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