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