1 /* 2 // Copyright (c) 2017 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 17 #include "dbus-sdr/sensorcommands.hpp" 18 19 #include "dbus-sdr/sdrutils.hpp" 20 #include "dbus-sdr/sensorutils.hpp" 21 #include "dbus-sdr/storagecommands.hpp" 22 23 #include <algorithm> 24 #include <array> 25 #include <boost/algorithm/string.hpp> 26 #include <boost/container/flat_map.hpp> 27 #include <chrono> 28 #include <cmath> 29 #include <cstring> 30 #include <iostream> 31 #include <ipmid/api.hpp> 32 #include <ipmid/types.hpp> 33 #include <ipmid/utils.hpp> 34 #include <map> 35 #include <memory> 36 #include <optional> 37 #include <phosphor-logging/log.hpp> 38 #include <sdbusplus/bus.hpp> 39 #include <stdexcept> 40 #include <string> 41 #include <utility> 42 #include <variant> 43 44 namespace ipmi 45 { 46 static constexpr int sensorMapUpdatePeriod = 10; 47 static constexpr int sensorMapSdrUpdatePeriod = 60; 48 49 constexpr size_t maxSDRTotalSize = 50 76; // Largest SDR Record Size (type 01) + SDR Overheader Size 51 constexpr static const uint32_t noTimestamp = 0xFFFFFFFF; 52 53 static uint16_t sdrReservationID; 54 static uint32_t sdrLastAdd = noTimestamp; 55 static uint32_t sdrLastRemove = noTimestamp; 56 static constexpr size_t lastRecordIndex = 0xFFFF; 57 static constexpr int GENERAL_ERROR = -1; 58 59 static boost::container::flat_map<std::string, ObjectValueTree> SensorCache; 60 61 // Specify the comparison required to sort and find char* map objects 62 struct CmpStr 63 { 64 bool operator()(const char* a, const char* b) const 65 { 66 return std::strcmp(a, b) < 0; 67 } 68 }; 69 const static boost::container::flat_map<const char*, SensorUnits, CmpStr> 70 sensorUnits{{{"temperature", SensorUnits::degreesC}, 71 {"voltage", SensorUnits::volts}, 72 {"current", SensorUnits::amps}, 73 {"fan_tach", SensorUnits::rpm}, 74 {"power", SensorUnits::watts}}}; 75 76 void registerSensorFunctions() __attribute__((constructor)); 77 78 static sdbusplus::bus::match::match sensorAdded( 79 *getSdBus(), 80 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/" 81 "sensors/'", 82 [](sdbusplus::message::message& m) { 83 getSensorTree().clear(); 84 sdrLastAdd = std::chrono::duration_cast<std::chrono::seconds>( 85 std::chrono::system_clock::now().time_since_epoch()) 86 .count(); 87 }); 88 89 static sdbusplus::bus::match::match sensorRemoved( 90 *getSdBus(), 91 "type='signal',member='InterfacesRemoved',arg0path='/xyz/openbmc_project/" 92 "sensors/'", 93 [](sdbusplus::message::message& m) { 94 getSensorTree().clear(); 95 sdrLastRemove = std::chrono::duration_cast<std::chrono::seconds>( 96 std::chrono::system_clock::now().time_since_epoch()) 97 .count(); 98 }); 99 100 // this keeps track of deassertions for sensor event status command. A 101 // deasertion can only happen if an assertion was seen first. 102 static boost::container::flat_map< 103 std::string, boost::container::flat_map<std::string, std::optional<bool>>> 104 thresholdDeassertMap; 105 106 static sdbusplus::bus::match::match thresholdChanged( 107 *getSdBus(), 108 "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus." 109 "Properties',arg0namespace='xyz.openbmc_project.Sensor.Threshold'", 110 [](sdbusplus::message::message& m) { 111 boost::container::flat_map<std::string, std::variant<bool, double>> 112 values; 113 m.read(std::string(), values); 114 115 auto findAssert = 116 std::find_if(values.begin(), values.end(), [](const auto& pair) { 117 return pair.first.find("Alarm") != std::string::npos; 118 }); 119 if (findAssert != values.end()) 120 { 121 auto ptr = std::get_if<bool>(&(findAssert->second)); 122 if (ptr == nullptr) 123 { 124 phosphor::logging::log<phosphor::logging::level::ERR>( 125 "thresholdChanged: Assert non bool"); 126 return; 127 } 128 if (*ptr) 129 { 130 phosphor::logging::log<phosphor::logging::level::INFO>( 131 "thresholdChanged: Assert", 132 phosphor::logging::entry("SENSOR=%s", m.get_path())); 133 thresholdDeassertMap[m.get_path()][findAssert->first] = *ptr; 134 } 135 else 136 { 137 auto& value = 138 thresholdDeassertMap[m.get_path()][findAssert->first]; 139 if (value) 140 { 141 phosphor::logging::log<phosphor::logging::level::INFO>( 142 "thresholdChanged: deassert", 143 phosphor::logging::entry("SENSOR=%s", m.get_path())); 144 value = *ptr; 145 } 146 } 147 } 148 }); 149 150 namespace sensor 151 { 152 static constexpr const char* vrInterface = 153 "xyz.openbmc_project.Control.VoltageRegulatorMode"; 154 static constexpr const char* sensorInterface = 155 "xyz.openbmc_project.Sensor.Value"; 156 } // namespace sensor 157 158 static void getSensorMaxMin(const DbusInterfaceMap& sensorMap, double& max, 159 double& min) 160 { 161 max = 127; 162 min = -128; 163 164 auto sensorObject = sensorMap.find(sensor::sensorInterface); 165 auto critical = 166 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 167 auto warning = 168 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 169 170 if (sensorObject != sensorMap.end()) 171 { 172 auto maxMap = sensorObject->second.find("MaxValue"); 173 auto minMap = sensorObject->second.find("MinValue"); 174 175 if (maxMap != sensorObject->second.end()) 176 { 177 max = std::visit(VariantToDoubleVisitor(), maxMap->second); 178 } 179 if (minMap != sensorObject->second.end()) 180 { 181 min = std::visit(VariantToDoubleVisitor(), minMap->second); 182 } 183 } 184 if (critical != sensorMap.end()) 185 { 186 auto lower = critical->second.find("CriticalLow"); 187 auto upper = critical->second.find("CriticalHigh"); 188 if (lower != critical->second.end()) 189 { 190 double value = std::visit(VariantToDoubleVisitor(), lower->second); 191 min = std::min(value, min); 192 } 193 if (upper != critical->second.end()) 194 { 195 double value = std::visit(VariantToDoubleVisitor(), upper->second); 196 max = std::max(value, max); 197 } 198 } 199 if (warning != sensorMap.end()) 200 { 201 202 auto lower = warning->second.find("WarningLow"); 203 auto upper = warning->second.find("WarningHigh"); 204 if (lower != warning->second.end()) 205 { 206 double value = std::visit(VariantToDoubleVisitor(), lower->second); 207 min = std::min(value, min); 208 } 209 if (upper != warning->second.end()) 210 { 211 double value = std::visit(VariantToDoubleVisitor(), upper->second); 212 max = std::max(value, max); 213 } 214 } 215 } 216 217 static bool getSensorMap(ipmi::Context::ptr ctx, std::string sensorConnection, 218 std::string sensorPath, DbusInterfaceMap& sensorMap, 219 int updatePeriod = sensorMapUpdatePeriod) 220 { 221 static boost::container::flat_map< 222 std::string, std::chrono::time_point<std::chrono::steady_clock>> 223 updateTimeMap; 224 225 auto updateFind = updateTimeMap.find(sensorConnection); 226 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>(); 227 if (updateFind != updateTimeMap.end()) 228 { 229 lastUpdate = updateFind->second; 230 } 231 232 auto now = std::chrono::steady_clock::now(); 233 234 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate) 235 .count() > updatePeriod) 236 { 237 ObjectValueTree managedObjects; 238 boost::system::error_code ec = getManagedObjects( 239 ctx, sensorConnection.c_str(), "/", managedObjects); 240 if (ec) 241 { 242 phosphor::logging::log<phosphor::logging::level::ERR>( 243 "GetMangagedObjects for getSensorMap failed", 244 phosphor::logging::entry("ERROR=%s", ec.message().c_str())); 245 246 return false; 247 } 248 249 SensorCache[sensorConnection] = managedObjects; 250 // Update time after finish building the map which allow the 251 // data to be cached for updatePeriod plus the build time. 252 updateTimeMap[sensorConnection] = std::chrono::steady_clock::now(); 253 } 254 auto connection = SensorCache.find(sensorConnection); 255 if (connection == SensorCache.end()) 256 { 257 return false; 258 } 259 auto path = connection->second.find(sensorPath); 260 if (path == connection->second.end()) 261 { 262 return false; 263 } 264 sensorMap = path->second; 265 266 return true; 267 } 268 269 namespace sensor 270 { 271 // Calculate VR Mode from input IPMI discrete event bytes 272 static std::optional<std::string> 273 calculateVRMode(uint15_t assertOffset, 274 const ipmi::DbusInterfaceMap::mapped_type& VRObject) 275 { 276 // get VR mode profiles from Supported Interface 277 auto supportedProperty = VRObject.find("Supported"); 278 if (supportedProperty == VRObject.end() || 279 VRObject.find("Selected") == VRObject.end()) 280 { 281 phosphor::logging::log<phosphor::logging::level::ERR>( 282 "Missing the required Supported and Selected properties"); 283 return std::nullopt; 284 } 285 286 const auto profilesPtr = 287 std::get_if<std::vector<std::string>>(&supportedProperty->second); 288 289 if (profilesPtr == nullptr) 290 { 291 phosphor::logging::log<phosphor::logging::level::ERR>( 292 "property is not array of string"); 293 return std::nullopt; 294 } 295 296 // interpret IPMI cmd bits into profiles' index 297 long unsigned int index = 0; 298 // only one bit should be set and the highest bit should not be used. 299 if (assertOffset == 0 || assertOffset == (1u << 15) || 300 (assertOffset & (assertOffset - 1))) 301 { 302 phosphor::logging::log<phosphor::logging::level::ERR>( 303 "IPMI cmd format incorrect", 304 305 phosphor::logging::entry("BYTES=%#02x", 306 static_cast<uint16_t>(assertOffset))); 307 return std::nullopt; 308 } 309 310 while (assertOffset != 1) 311 { 312 assertOffset >>= 1; 313 index++; 314 } 315 316 if (index >= profilesPtr->size()) 317 { 318 phosphor::logging::log<phosphor::logging::level::ERR>( 319 "profile index out of boundary"); 320 return std::nullopt; 321 } 322 323 return profilesPtr->at(index); 324 } 325 326 // Calculate sensor value from IPMI reading byte 327 static std::optional<double> 328 calculateValue(uint8_t reading, const ipmi::DbusInterfaceMap& sensorMap, 329 const ipmi::DbusInterfaceMap::mapped_type& valueObject) 330 { 331 if (valueObject.find("Value") == valueObject.end()) 332 { 333 phosphor::logging::log<phosphor::logging::level::ERR>( 334 "Missing the required Value property"); 335 return std::nullopt; 336 } 337 338 double max = 0; 339 double min = 0; 340 getSensorMaxMin(sensorMap, max, min); 341 342 int16_t mValue = 0; 343 int16_t bValue = 0; 344 int8_t rExp = 0; 345 int8_t bExp = 0; 346 bool bSigned = false; 347 348 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 349 { 350 return std::nullopt; 351 } 352 353 double value = bSigned ? ((int8_t)reading) : reading; 354 355 value *= ((double)mValue); 356 value += ((double)bValue) * std::pow(10.0, bExp); 357 value *= std::pow(10.0, rExp); 358 359 return value; 360 } 361 362 } // namespace sensor 363 364 ipmi::RspType<> ipmiSenPlatformEvent(uint8_t generatorID, uint8_t evmRev, 365 uint8_t sensorType, uint8_t sensorNum, 366 uint8_t eventType, uint8_t eventData1, 367 std::optional<uint8_t> eventData2, 368 std::optional<uint8_t> eventData3) 369 { 370 return ipmi::responseSuccess(); 371 } 372 373 ipmi::RspType<> ipmiSetSensorReading(ipmi::Context::ptr ctx, 374 uint8_t sensorNumber, uint8_t operation, 375 uint8_t reading, uint15_t assertOffset, 376 bool resvd1, uint15_t deassertOffset, 377 bool resvd2, uint8_t eventData1, 378 uint8_t eventData2, uint8_t eventData3) 379 { 380 std::string connection; 381 std::string path; 382 ipmi::Cc status = getSensorConnection(ctx, sensorNumber, connection, path); 383 if (status) 384 { 385 return ipmi::response(status); 386 } 387 388 DbusInterfaceMap sensorMap; 389 if (!getSensorMap(ctx, connection, path, sensorMap)) 390 { 391 return ipmi::responseResponseError(); 392 } 393 394 // we can tell the sensor type by its interface type 395 auto sensorObject = sensorMap.find(sensor::sensorInterface); 396 if (sensorObject != sensorMap.end()) 397 { 398 auto value = 399 sensor::calculateValue(reading, sensorMap, sensorObject->second); 400 if (!value) 401 { 402 return ipmi::responseResponseError(); 403 } 404 405 if constexpr (debug) 406 { 407 phosphor::logging::log<phosphor::logging::level::INFO>( 408 "IPMI SET_SENSOR", 409 phosphor::logging::entry("SENSOR_NUM=%d", sensorNumber), 410 phosphor::logging::entry("BYTE=%u", (unsigned int)reading), 411 phosphor::logging::entry("VALUE=%f", *value)); 412 } 413 414 boost::system::error_code ec = 415 setDbusProperty(ctx, connection, path, sensor::sensorInterface, 416 "Value", ipmi::Value(*value)); 417 418 // setDbusProperty intended to resolve dbus exception/rc within the 419 // function but failed to achieve that. Catch SdBusError in the ipmi 420 // callback functions for now (e.g. ipmiSetSensorReading). 421 if (ec) 422 { 423 using namespace phosphor::logging; 424 log<level::ERR>("Failed to set property", 425 entry("PROPERTY=%s", "Value"), 426 entry("PATH=%s", path.c_str()), 427 entry("INTERFACE=%s", sensor::sensorInterface), 428 entry("WHAT=%s", ec.message().c_str())); 429 return ipmi::responseResponseError(); 430 } 431 return ipmi::responseSuccess(); 432 } 433 434 sensorObject = sensorMap.find(sensor::vrInterface); 435 if (sensorObject != sensorMap.end()) 436 { 437 // VR sensors are treated as a special case and we will not check the 438 // write permission for VR sensors, since they always deemed writable 439 // and permission table are not applied to VR sensors. 440 auto vrMode = 441 sensor::calculateVRMode(assertOffset, sensorObject->second); 442 if (!vrMode) 443 { 444 return ipmi::responseResponseError(); 445 } 446 boost::system::error_code ec = setDbusProperty( 447 ctx, connection, path, sensor::vrInterface, "Selected", *vrMode); 448 // setDbusProperty intended to resolve dbus exception/rc within the 449 // function but failed to achieve that. Catch SdBusError in the ipmi 450 // callback functions for now (e.g. ipmiSetSensorReading). 451 if (ec) 452 { 453 using namespace phosphor::logging; 454 log<level::ERR>("Failed to set property", 455 entry("PROPERTY=%s", "Selected"), 456 entry("PATH=%s", path.c_str()), 457 entry("INTERFACE=%s", sensor::sensorInterface), 458 entry("WHAT=%s", ec.message().c_str())); 459 return ipmi::responseResponseError(); 460 } 461 return ipmi::responseSuccess(); 462 } 463 464 phosphor::logging::log<phosphor::logging::level::ERR>( 465 "unknown sensor type", 466 phosphor::logging::entry("PATH=%s", path.c_str())); 467 return ipmi::responseResponseError(); 468 } 469 470 ipmi::RspType<uint8_t, uint8_t, uint8_t, std::optional<uint8_t>> 471 ipmiSenGetSensorReading(ipmi::Context::ptr ctx, uint8_t sensnum) 472 { 473 std::string connection; 474 std::string path; 475 476 auto status = getSensorConnection(ctx, sensnum, connection, path); 477 if (status) 478 { 479 return ipmi::response(status); 480 } 481 482 DbusInterfaceMap sensorMap; 483 if (!getSensorMap(ctx, connection, path, sensorMap)) 484 { 485 return ipmi::responseResponseError(); 486 } 487 auto sensorObject = sensorMap.find(sensor::sensorInterface); 488 489 if (sensorObject == sensorMap.end() || 490 sensorObject->second.find("Value") == sensorObject->second.end()) 491 { 492 return ipmi::responseResponseError(); 493 } 494 auto& valueVariant = sensorObject->second["Value"]; 495 double reading = std::visit(VariantToDoubleVisitor(), valueVariant); 496 497 double max = 0; 498 double min = 0; 499 getSensorMaxMin(sensorMap, max, min); 500 501 int16_t mValue = 0; 502 int16_t bValue = 0; 503 int8_t rExp = 0; 504 int8_t bExp = 0; 505 bool bSigned = false; 506 507 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 508 { 509 return ipmi::responseResponseError(); 510 } 511 512 uint8_t value = 513 scaleIPMIValueFromDouble(reading, mValue, rExp, bValue, bExp, bSigned); 514 uint8_t operation = 515 static_cast<uint8_t>(IPMISensorReadingByte2::sensorScanningEnable); 516 operation |= 517 static_cast<uint8_t>(IPMISensorReadingByte2::eventMessagesEnable); 518 bool notReading = std::isnan(reading); 519 520 if (!notReading) 521 { 522 auto availableObject = 523 sensorMap.find("xyz.openbmc_project.State.Decorator.Availability"); 524 if (availableObject != sensorMap.end()) 525 { 526 auto findAvailable = availableObject->second.find("Available"); 527 if (findAvailable != availableObject->second.end()) 528 { 529 bool* available = std::get_if<bool>(&(findAvailable->second)); 530 if (available && !(*available)) 531 { 532 notReading = true; 533 } 534 } 535 } 536 } 537 538 if (notReading) 539 { 540 operation |= static_cast<uint8_t>( 541 IPMISensorReadingByte2::readingStateUnavailable); 542 } 543 544 if constexpr (details::enableInstrumentation) 545 { 546 int byteValue; 547 if (bSigned) 548 { 549 byteValue = static_cast<int>(static_cast<int8_t>(value)); 550 } 551 else 552 { 553 byteValue = static_cast<int>(static_cast<uint8_t>(value)); 554 } 555 556 // Keep stats on the reading just obtained, even if it is "NaN" 557 if (details::sdrStatsTable.updateReading(sensnum, reading, byteValue)) 558 { 559 // This is the first reading, show the coefficients 560 double step = (max - min) / 255.0; 561 std::cerr << "IPMI sensor " 562 << details::sdrStatsTable.getName(sensnum) 563 << ": Range min=" << min << " max=" << max 564 << ", step=" << step 565 << ", Coefficients mValue=" << static_cast<int>(mValue) 566 << " rExp=" << static_cast<int>(rExp) 567 << " bValue=" << static_cast<int>(bValue) 568 << " bExp=" << static_cast<int>(bExp) 569 << " bSigned=" << static_cast<int>(bSigned) << "\n"; 570 } 571 } 572 573 uint8_t thresholds = 0; 574 575 auto warningObject = 576 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 577 if (warningObject != sensorMap.end()) 578 { 579 auto alarmHigh = warningObject->second.find("WarningAlarmHigh"); 580 auto alarmLow = warningObject->second.find("WarningAlarmLow"); 581 if (alarmHigh != warningObject->second.end()) 582 { 583 if (std::get<bool>(alarmHigh->second)) 584 { 585 thresholds |= static_cast<uint8_t>( 586 IPMISensorReadingByte3::upperNonCritical); 587 } 588 } 589 if (alarmLow != warningObject->second.end()) 590 { 591 if (std::get<bool>(alarmLow->second)) 592 { 593 thresholds |= static_cast<uint8_t>( 594 IPMISensorReadingByte3::lowerNonCritical); 595 } 596 } 597 } 598 599 auto criticalObject = 600 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 601 if (criticalObject != sensorMap.end()) 602 { 603 auto alarmHigh = criticalObject->second.find("CriticalAlarmHigh"); 604 auto alarmLow = criticalObject->second.find("CriticalAlarmLow"); 605 if (alarmHigh != criticalObject->second.end()) 606 { 607 if (std::get<bool>(alarmHigh->second)) 608 { 609 thresholds |= 610 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical); 611 } 612 } 613 if (alarmLow != criticalObject->second.end()) 614 { 615 if (std::get<bool>(alarmLow->second)) 616 { 617 thresholds |= 618 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical); 619 } 620 } 621 } 622 623 // no discrete as of today so optional byte is never returned 624 return ipmi::responseSuccess(value, operation, thresholds, std::nullopt); 625 } 626 627 /** @brief implements the Set Sensor threshold command 628 * @param sensorNumber - sensor number 629 * @param lowerNonCriticalThreshMask 630 * @param lowerCriticalThreshMask 631 * @param lowerNonRecovThreshMask 632 * @param upperNonCriticalThreshMask 633 * @param upperCriticalThreshMask 634 * @param upperNonRecovThreshMask 635 * @param reserved 636 * @param lowerNonCritical - lower non-critical threshold 637 * @param lowerCritical - Lower critical threshold 638 * @param lowerNonRecoverable - Lower non recovarable threshold 639 * @param upperNonCritical - Upper non-critical threshold 640 * @param upperCritical - Upper critical 641 * @param upperNonRecoverable - Upper Non-recoverable 642 * 643 * @returns IPMI completion code 644 */ 645 ipmi::RspType<> ipmiSenSetSensorThresholds( 646 ipmi::Context::ptr ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask, 647 bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask, 648 bool upperNonCriticalThreshMask, bool upperCriticalThreshMask, 649 bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical, 650 uint8_t lowerCritical, uint8_t lowerNonRecoverable, 651 uint8_t upperNonCritical, uint8_t upperCritical, 652 uint8_t upperNonRecoverable) 653 { 654 if (reserved) 655 { 656 return ipmi::responseInvalidFieldRequest(); 657 } 658 659 // lower nc and upper nc not suppported on any sensor 660 if (lowerNonRecovThreshMask || upperNonRecovThreshMask) 661 { 662 return ipmi::responseInvalidFieldRequest(); 663 } 664 665 // if none of the threshold mask are set, nothing to do 666 if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask | 667 lowerNonRecovThreshMask | upperNonCriticalThreshMask | 668 upperCriticalThreshMask | upperNonRecovThreshMask)) 669 { 670 return ipmi::responseSuccess(); 671 } 672 673 std::string connection; 674 std::string path; 675 676 ipmi::Cc status = getSensorConnection(ctx, sensorNum, connection, path); 677 if (status) 678 { 679 return ipmi::response(status); 680 } 681 DbusInterfaceMap sensorMap; 682 if (!getSensorMap(ctx, connection, path, sensorMap)) 683 { 684 return ipmi::responseResponseError(); 685 } 686 687 double max = 0; 688 double min = 0; 689 getSensorMaxMin(sensorMap, max, min); 690 691 int16_t mValue = 0; 692 int16_t bValue = 0; 693 int8_t rExp = 0; 694 int8_t bExp = 0; 695 bool bSigned = false; 696 697 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 698 { 699 return ipmi::responseResponseError(); 700 } 701 702 // store a vector of property name, value to set, and interface 703 std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet; 704 705 // define the indexes of the tuple 706 constexpr uint8_t propertyName = 0; 707 constexpr uint8_t thresholdValue = 1; 708 constexpr uint8_t interface = 2; 709 // verifiy all needed fields are present 710 if (lowerCriticalThreshMask || upperCriticalThreshMask) 711 { 712 auto findThreshold = 713 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 714 if (findThreshold == sensorMap.end()) 715 { 716 return ipmi::responseInvalidFieldRequest(); 717 } 718 if (lowerCriticalThreshMask) 719 { 720 auto findLower = findThreshold->second.find("CriticalLow"); 721 if (findLower == findThreshold->second.end()) 722 { 723 return ipmi::responseInvalidFieldRequest(); 724 } 725 thresholdsToSet.emplace_back("CriticalLow", lowerCritical, 726 findThreshold->first); 727 } 728 if (upperCriticalThreshMask) 729 { 730 auto findUpper = findThreshold->second.find("CriticalHigh"); 731 if (findUpper == findThreshold->second.end()) 732 { 733 return ipmi::responseInvalidFieldRequest(); 734 } 735 thresholdsToSet.emplace_back("CriticalHigh", upperCritical, 736 findThreshold->first); 737 } 738 } 739 if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask) 740 { 741 auto findThreshold = 742 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 743 if (findThreshold == sensorMap.end()) 744 { 745 return ipmi::responseInvalidFieldRequest(); 746 } 747 if (lowerNonCriticalThreshMask) 748 { 749 auto findLower = findThreshold->second.find("WarningLow"); 750 if (findLower == findThreshold->second.end()) 751 { 752 return ipmi::responseInvalidFieldRequest(); 753 } 754 thresholdsToSet.emplace_back("WarningLow", lowerNonCritical, 755 findThreshold->first); 756 } 757 if (upperNonCriticalThreshMask) 758 { 759 auto findUpper = findThreshold->second.find("WarningHigh"); 760 if (findUpper == findThreshold->second.end()) 761 { 762 return ipmi::responseInvalidFieldRequest(); 763 } 764 thresholdsToSet.emplace_back("WarningHigh", upperNonCritical, 765 findThreshold->first); 766 } 767 } 768 for (const auto& property : thresholdsToSet) 769 { 770 // from section 36.3 in the IPMI Spec, assume all linear 771 double valueToSet = ((mValue * std::get<thresholdValue>(property)) + 772 (bValue * std::pow(10.0, bExp))) * 773 std::pow(10.0, rExp); 774 setDbusProperty( 775 *getSdBus(), connection, path, std::get<interface>(property), 776 std::get<propertyName>(property), ipmi::Value(valueToSet)); 777 } 778 return ipmi::responseSuccess(); 779 } 780 781 IPMIThresholds getIPMIThresholds(const DbusInterfaceMap& sensorMap) 782 { 783 IPMIThresholds resp; 784 auto warningInterface = 785 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 786 auto criticalInterface = 787 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 788 789 if ((warningInterface != sensorMap.end()) || 790 (criticalInterface != sensorMap.end())) 791 { 792 auto sensorPair = sensorMap.find(sensor::sensorInterface); 793 794 if (sensorPair == sensorMap.end()) 795 { 796 // should not have been able to find a sensor not implementing 797 // the sensor object 798 throw std::runtime_error("Invalid sensor map"); 799 } 800 801 double max = 0; 802 double min = 0; 803 getSensorMaxMin(sensorMap, max, min); 804 805 int16_t mValue = 0; 806 int16_t bValue = 0; 807 int8_t rExp = 0; 808 int8_t bExp = 0; 809 bool bSigned = false; 810 811 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 812 { 813 throw std::runtime_error("Invalid sensor atrributes"); 814 } 815 if (warningInterface != sensorMap.end()) 816 { 817 auto& warningMap = warningInterface->second; 818 819 auto warningHigh = warningMap.find("WarningHigh"); 820 auto warningLow = warningMap.find("WarningLow"); 821 822 if (warningHigh != warningMap.end()) 823 { 824 825 double value = 826 std::visit(VariantToDoubleVisitor(), warningHigh->second); 827 resp.warningHigh = scaleIPMIValueFromDouble( 828 value, mValue, rExp, bValue, bExp, bSigned); 829 } 830 if (warningLow != warningMap.end()) 831 { 832 double value = 833 std::visit(VariantToDoubleVisitor(), warningLow->second); 834 resp.warningLow = scaleIPMIValueFromDouble( 835 value, mValue, rExp, bValue, bExp, bSigned); 836 } 837 } 838 if (criticalInterface != sensorMap.end()) 839 { 840 auto& criticalMap = criticalInterface->second; 841 842 auto criticalHigh = criticalMap.find("CriticalHigh"); 843 auto criticalLow = criticalMap.find("CriticalLow"); 844 845 if (criticalHigh != criticalMap.end()) 846 { 847 double value = 848 std::visit(VariantToDoubleVisitor(), criticalHigh->second); 849 resp.criticalHigh = scaleIPMIValueFromDouble( 850 value, mValue, rExp, bValue, bExp, bSigned); 851 } 852 if (criticalLow != criticalMap.end()) 853 { 854 double value = 855 std::visit(VariantToDoubleVisitor(), criticalLow->second); 856 resp.criticalLow = scaleIPMIValueFromDouble( 857 value, mValue, rExp, bValue, bExp, bSigned); 858 } 859 } 860 } 861 return resp; 862 } 863 864 ipmi::RspType<uint8_t, // readable 865 uint8_t, // lowerNCrit 866 uint8_t, // lowerCrit 867 uint8_t, // lowerNrecoverable 868 uint8_t, // upperNC 869 uint8_t, // upperCrit 870 uint8_t> // upperNRecoverable 871 ipmiSenGetSensorThresholds(ipmi::Context::ptr ctx, uint8_t sensorNumber) 872 { 873 std::string connection; 874 std::string path; 875 876 auto status = getSensorConnection(ctx, sensorNumber, connection, path); 877 if (status) 878 { 879 return ipmi::response(status); 880 } 881 882 DbusInterfaceMap sensorMap; 883 if (!getSensorMap(ctx, connection, path, sensorMap)) 884 { 885 return ipmi::responseResponseError(); 886 } 887 888 IPMIThresholds thresholdData; 889 try 890 { 891 thresholdData = getIPMIThresholds(sensorMap); 892 } 893 catch (std::exception&) 894 { 895 return ipmi::responseResponseError(); 896 } 897 898 uint8_t readable = 0; 899 uint8_t lowerNC = 0; 900 uint8_t lowerCritical = 0; 901 uint8_t lowerNonRecoverable = 0; 902 uint8_t upperNC = 0; 903 uint8_t upperCritical = 0; 904 uint8_t upperNonRecoverable = 0; 905 906 if (thresholdData.warningHigh) 907 { 908 readable |= 909 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperNonCritical); 910 upperNC = *thresholdData.warningHigh; 911 } 912 if (thresholdData.warningLow) 913 { 914 readable |= 915 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerNonCritical); 916 lowerNC = *thresholdData.warningLow; 917 } 918 919 if (thresholdData.criticalHigh) 920 { 921 readable |= 922 1 << static_cast<uint8_t>(IPMIThresholdRespBits::upperCritical); 923 upperCritical = *thresholdData.criticalHigh; 924 } 925 if (thresholdData.criticalLow) 926 { 927 readable |= 928 1 << static_cast<uint8_t>(IPMIThresholdRespBits::lowerCritical); 929 lowerCritical = *thresholdData.criticalLow; 930 } 931 932 return ipmi::responseSuccess(readable, lowerNC, lowerCritical, 933 lowerNonRecoverable, upperNC, upperCritical, 934 upperNonRecoverable); 935 } 936 937 /** @brief implements the get Sensor event enable command 938 * @param sensorNumber - sensor number 939 * 940 * @returns IPMI completion code plus response data 941 * - enabled - Sensor Event messages 942 * - assertionEnabledLsb - Assertion event messages 943 * - assertionEnabledMsb - Assertion event messages 944 * - deassertionEnabledLsb - Deassertion event messages 945 * - deassertionEnabledMsb - Deassertion event messages 946 */ 947 948 ipmi::RspType<uint8_t, // enabled 949 uint8_t, // assertionEnabledLsb 950 uint8_t, // assertionEnabledMsb 951 uint8_t, // deassertionEnabledLsb 952 uint8_t> // deassertionEnabledMsb 953 ipmiSenGetSensorEventEnable(ipmi::Context::ptr ctx, uint8_t sensorNum) 954 { 955 std::string connection; 956 std::string path; 957 958 uint8_t enabled = 0; 959 uint8_t assertionEnabledLsb = 0; 960 uint8_t assertionEnabledMsb = 0; 961 uint8_t deassertionEnabledLsb = 0; 962 uint8_t deassertionEnabledMsb = 0; 963 964 auto status = getSensorConnection(ctx, sensorNum, connection, path); 965 if (status) 966 { 967 return ipmi::response(status); 968 } 969 970 DbusInterfaceMap sensorMap; 971 if (!getSensorMap(ctx, connection, path, sensorMap)) 972 { 973 return ipmi::responseResponseError(); 974 } 975 976 auto warningInterface = 977 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 978 auto criticalInterface = 979 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 980 if ((warningInterface != sensorMap.end()) || 981 (criticalInterface != sensorMap.end())) 982 { 983 enabled = static_cast<uint8_t>( 984 IPMISensorEventEnableByte2::sensorScanningEnable); 985 if (warningInterface != sensorMap.end()) 986 { 987 auto& warningMap = warningInterface->second; 988 989 auto warningHigh = warningMap.find("WarningHigh"); 990 auto warningLow = warningMap.find("WarningLow"); 991 if (warningHigh != warningMap.end()) 992 { 993 assertionEnabledLsb |= static_cast<uint8_t>( 994 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh); 995 deassertionEnabledLsb |= static_cast<uint8_t>( 996 IPMISensorEventEnableThresholds::upperNonCriticalGoingLow); 997 } 998 if (warningLow != warningMap.end()) 999 { 1000 assertionEnabledLsb |= static_cast<uint8_t>( 1001 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow); 1002 deassertionEnabledLsb |= static_cast<uint8_t>( 1003 IPMISensorEventEnableThresholds::lowerNonCriticalGoingHigh); 1004 } 1005 } 1006 if (criticalInterface != sensorMap.end()) 1007 { 1008 auto& criticalMap = criticalInterface->second; 1009 1010 auto criticalHigh = criticalMap.find("CriticalHigh"); 1011 auto criticalLow = criticalMap.find("CriticalLow"); 1012 1013 if (criticalHigh != criticalMap.end()) 1014 { 1015 assertionEnabledMsb |= static_cast<uint8_t>( 1016 IPMISensorEventEnableThresholds::upperCriticalGoingHigh); 1017 deassertionEnabledMsb |= static_cast<uint8_t>( 1018 IPMISensorEventEnableThresholds::upperCriticalGoingLow); 1019 } 1020 if (criticalLow != criticalMap.end()) 1021 { 1022 assertionEnabledLsb |= static_cast<uint8_t>( 1023 IPMISensorEventEnableThresholds::lowerCriticalGoingLow); 1024 deassertionEnabledLsb |= static_cast<uint8_t>( 1025 IPMISensorEventEnableThresholds::lowerCriticalGoingHigh); 1026 } 1027 } 1028 } 1029 1030 return ipmi::responseSuccess(enabled, assertionEnabledLsb, 1031 assertionEnabledMsb, deassertionEnabledLsb, 1032 deassertionEnabledMsb); 1033 } 1034 1035 /** @brief implements the get Sensor event status command 1036 * @param sensorNumber - sensor number, FFh = reserved 1037 * 1038 * @returns IPMI completion code plus response data 1039 * - sensorEventStatus - Sensor Event messages state 1040 * - assertions - Assertion event messages 1041 * - deassertions - Deassertion event messages 1042 */ 1043 ipmi::RspType<uint8_t, // sensorEventStatus 1044 std::bitset<16>, // assertions 1045 std::bitset<16> // deassertion 1046 > 1047 ipmiSenGetSensorEventStatus(ipmi::Context::ptr ctx, uint8_t sensorNum) 1048 { 1049 if (sensorNum == reservedSensorNumber) 1050 { 1051 return ipmi::responseInvalidFieldRequest(); 1052 } 1053 1054 std::string connection; 1055 std::string path; 1056 auto status = getSensorConnection(ctx, sensorNum, connection, path); 1057 if (status) 1058 { 1059 phosphor::logging::log<phosphor::logging::level::ERR>( 1060 "ipmiSenGetSensorEventStatus: Sensor connection Error", 1061 phosphor::logging::entry("SENSOR=%d", sensorNum)); 1062 return ipmi::response(status); 1063 } 1064 1065 DbusInterfaceMap sensorMap; 1066 if (!getSensorMap(ctx, connection, path, sensorMap)) 1067 { 1068 phosphor::logging::log<phosphor::logging::level::ERR>( 1069 "ipmiSenGetSensorEventStatus: Sensor Mapping Error", 1070 phosphor::logging::entry("SENSOR=%s", path.c_str())); 1071 return ipmi::responseResponseError(); 1072 } 1073 auto warningInterface = 1074 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 1075 auto criticalInterface = 1076 sensorMap.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 1077 1078 uint8_t sensorEventStatus = 1079 static_cast<uint8_t>(IPMISensorEventEnableByte2::sensorScanningEnable); 1080 1081 std::optional<bool> criticalDeassertHigh = 1082 thresholdDeassertMap[path]["CriticalAlarmHigh"]; 1083 std::optional<bool> criticalDeassertLow = 1084 thresholdDeassertMap[path]["CriticalAlarmLow"]; 1085 std::optional<bool> warningDeassertHigh = 1086 thresholdDeassertMap[path]["WarningAlarmHigh"]; 1087 std::optional<bool> warningDeassertLow = 1088 thresholdDeassertMap[path]["WarningAlarmLow"]; 1089 1090 std::bitset<16> assertions = 0; 1091 std::bitset<16> deassertions = 0; 1092 1093 if (criticalDeassertHigh && !*criticalDeassertHigh) 1094 { 1095 deassertions.set(static_cast<size_t>( 1096 IPMIGetSensorEventEnableThresholds::upperCriticalGoingHigh)); 1097 } 1098 if (criticalDeassertLow && !*criticalDeassertLow) 1099 { 1100 deassertions.set(static_cast<size_t>( 1101 IPMIGetSensorEventEnableThresholds::upperCriticalGoingLow)); 1102 } 1103 if (warningDeassertHigh && !*warningDeassertHigh) 1104 { 1105 deassertions.set(static_cast<size_t>( 1106 IPMIGetSensorEventEnableThresholds::upperNonCriticalGoingHigh)); 1107 } 1108 if (warningDeassertLow && !*warningDeassertLow) 1109 { 1110 deassertions.set(static_cast<size_t>( 1111 IPMIGetSensorEventEnableThresholds::lowerNonCriticalGoingHigh)); 1112 } 1113 if ((warningInterface != sensorMap.end()) || 1114 (criticalInterface != sensorMap.end())) 1115 { 1116 sensorEventStatus = static_cast<size_t>( 1117 IPMISensorEventEnableByte2::eventMessagesEnable); 1118 if (warningInterface != sensorMap.end()) 1119 { 1120 auto& warningMap = warningInterface->second; 1121 1122 auto warningHigh = warningMap.find("WarningAlarmHigh"); 1123 auto warningLow = warningMap.find("WarningAlarmLow"); 1124 auto warningHighAlarm = false; 1125 auto warningLowAlarm = false; 1126 1127 if (warningHigh != warningMap.end()) 1128 { 1129 warningHighAlarm = std::get<bool>(warningHigh->second); 1130 } 1131 if (warningLow != warningMap.end()) 1132 { 1133 warningLowAlarm = std::get<bool>(warningLow->second); 1134 } 1135 if (warningHighAlarm) 1136 { 1137 assertions.set( 1138 static_cast<size_t>(IPMIGetSensorEventEnableThresholds:: 1139 upperNonCriticalGoingHigh)); 1140 } 1141 if (warningLowAlarm) 1142 { 1143 assertions.set( 1144 static_cast<size_t>(IPMIGetSensorEventEnableThresholds:: 1145 lowerNonCriticalGoingLow)); 1146 } 1147 } 1148 if (criticalInterface != sensorMap.end()) 1149 { 1150 auto& criticalMap = criticalInterface->second; 1151 1152 auto criticalHigh = criticalMap.find("CriticalAlarmHigh"); 1153 auto criticalLow = criticalMap.find("CriticalAlarmLow"); 1154 auto criticalHighAlarm = false; 1155 auto criticalLowAlarm = false; 1156 1157 if (criticalHigh != criticalMap.end()) 1158 { 1159 criticalHighAlarm = std::get<bool>(criticalHigh->second); 1160 } 1161 if (criticalLow != criticalMap.end()) 1162 { 1163 criticalLowAlarm = std::get<bool>(criticalLow->second); 1164 } 1165 if (criticalHighAlarm) 1166 { 1167 assertions.set( 1168 static_cast<size_t>(IPMIGetSensorEventEnableThresholds:: 1169 upperCriticalGoingHigh)); 1170 } 1171 if (criticalLowAlarm) 1172 { 1173 assertions.set(static_cast<size_t>( 1174 IPMIGetSensorEventEnableThresholds::lowerCriticalGoingLow)); 1175 } 1176 } 1177 } 1178 1179 return ipmi::responseSuccess(sensorEventStatus, assertions, deassertions); 1180 } 1181 1182 static int getSensorDataRecord(ipmi::Context::ptr ctx, 1183 std::vector<uint8_t>& recordData, 1184 uint16_t recordID) 1185 { 1186 auto& sensorTree = getSensorTree(); 1187 size_t fruCount = 0; 1188 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount); 1189 if (ret != ipmi::ccSuccess) 1190 { 1191 phosphor::logging::log<phosphor::logging::level::ERR>( 1192 "getSensorDataRecord: getFruSdrCount error"); 1193 return GENERAL_ERROR; 1194 } 1195 1196 size_t lastRecord = 1197 sensorTree.size() + fruCount + ipmi::storage::type12Count - 1; 1198 if (recordID == lastRecordIndex) 1199 { 1200 recordID = lastRecord; 1201 } 1202 if (recordID > lastRecord) 1203 { 1204 phosphor::logging::log<phosphor::logging::level::ERR>( 1205 "getSensorDataRecord: recordID > lastRecord error"); 1206 return GENERAL_ERROR; 1207 } 1208 1209 if (recordID >= sensorTree.size()) 1210 { 1211 size_t fruIndex = recordID - sensorTree.size(); 1212 if (fruIndex >= fruCount) 1213 { 1214 // handle type 12 hardcoded records 1215 size_t type12Index = fruIndex - fruCount; 1216 if (type12Index >= ipmi::storage::type12Count) 1217 { 1218 phosphor::logging::log<phosphor::logging::level::ERR>( 1219 "getSensorDataRecord: type12Index error"); 1220 return GENERAL_ERROR; 1221 } 1222 recordData = ipmi::storage::getType12SDRs(type12Index, recordID); 1223 } 1224 else 1225 { 1226 // handle fru records 1227 get_sdr::SensorDataFruRecord data; 1228 ret = ipmi::storage::getFruSdrs(ctx, fruIndex, data); 1229 if (ret != IPMI_CC_OK) 1230 { 1231 return GENERAL_ERROR; 1232 } 1233 data.header.record_id_msb = recordID >> 8; 1234 data.header.record_id_lsb = recordID & 0xFF; 1235 recordData.insert(recordData.end(), 1236 reinterpret_cast<uint8_t*>(&data), 1237 reinterpret_cast<uint8_t*>(&data) + sizeof(data)); 1238 } 1239 1240 return 0; 1241 } 1242 1243 std::string connection; 1244 std::string path; 1245 auto status = getSensorConnection(ctx, recordID, connection, path); 1246 if (status) 1247 { 1248 phosphor::logging::log<phosphor::logging::level::ERR>( 1249 "getSensorDataRecord: getSensorConnection error"); 1250 return GENERAL_ERROR; 1251 } 1252 DbusInterfaceMap sensorMap; 1253 if (!getSensorMap(ctx, connection, path, sensorMap, sensorMapUpdatePeriod)) 1254 { 1255 phosphor::logging::log<phosphor::logging::level::ERR>( 1256 "getSensorDataRecord: getSensorMap error"); 1257 return GENERAL_ERROR; 1258 } 1259 uint16_t sensorNum = getSensorNumberFromPath(path); 1260 if (sensorNum == invalidSensorNumber) 1261 { 1262 phosphor::logging::log<phosphor::logging::level::ERR>( 1263 "getSensorDataRecord: invalidSensorNumber"); 1264 return GENERAL_ERROR; 1265 } 1266 uint8_t sensornumber = static_cast<uint8_t>(sensorNum); 1267 uint8_t lun = static_cast<uint8_t>(sensorNum >> 8); 1268 1269 get_sdr::SensorDataFullRecord record = {0}; 1270 1271 get_sdr::header::set_record_id( 1272 recordID, reinterpret_cast<get_sdr::SensorDataRecordHeader*>(&record)); 1273 1274 record.header.sdr_version = ipmiSdrVersion; 1275 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD; 1276 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) - 1277 sizeof(get_sdr::SensorDataRecordHeader); 1278 record.key.owner_id = 0x20; 1279 record.key.owner_lun = lun; 1280 record.key.sensor_number = sensornumber; 1281 1282 record.body.sensor_capabilities = 0x68; // auto rearm - todo hysteresis 1283 record.body.sensor_type = getSensorTypeFromPath(path); 1284 std::string type = getSensorTypeStringFromPath(path); 1285 auto typeCstr = type.c_str(); 1286 auto findUnits = sensorUnits.find(typeCstr); 1287 if (findUnits != sensorUnits.end()) 1288 { 1289 record.body.sensor_units_2_base = 1290 static_cast<uint8_t>(findUnits->second); 1291 } // else default 0x0 unspecified 1292 1293 record.body.event_reading_type = getSensorEventTypeFromPath(path); 1294 1295 auto sensorObject = sensorMap.find(sensor::sensorInterface); 1296 if (sensorObject == sensorMap.end()) 1297 { 1298 phosphor::logging::log<phosphor::logging::level::ERR>( 1299 "getSensorDataRecord: sensorObject error"); 1300 return GENERAL_ERROR; 1301 } 1302 1303 uint8_t entityId = 0; 1304 uint8_t entityInstance = 0x01; 1305 1306 // follow the association chain to get the parent board's entityid and 1307 // entityInstance 1308 updateIpmiFromAssociation(path, sensorMap, entityId, entityInstance); 1309 1310 record.body.entity_id = entityId; 1311 record.body.entity_instance = entityInstance; 1312 1313 auto maxObject = sensorObject->second.find("MaxValue"); 1314 auto minObject = sensorObject->second.find("MinValue"); 1315 1316 // If min and/or max are left unpopulated, 1317 // then default to what a signed byte would be, namely (-128,127) range. 1318 auto max = static_cast<double>(std::numeric_limits<int8_t>::max()); 1319 auto min = static_cast<double>(std::numeric_limits<int8_t>::lowest()); 1320 if (maxObject != sensorObject->second.end()) 1321 { 1322 max = std::visit(VariantToDoubleVisitor(), maxObject->second); 1323 } 1324 1325 if (minObject != sensorObject->second.end()) 1326 { 1327 min = std::visit(VariantToDoubleVisitor(), minObject->second); 1328 } 1329 1330 int16_t mValue = 0; 1331 int8_t rExp = 0; 1332 int16_t bValue = 0; 1333 int8_t bExp = 0; 1334 bool bSigned = false; 1335 1336 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned)) 1337 { 1338 phosphor::logging::log<phosphor::logging::level::ERR>( 1339 "getSensorDataRecord: getSensorAttributes error"); 1340 return GENERAL_ERROR; 1341 } 1342 1343 // The record.body is a struct SensorDataFullRecordBody 1344 // from sensorhandler.hpp in phosphor-ipmi-host. 1345 // The meaning of these bits appears to come from 1346 // table 43.1 of the IPMI spec. 1347 // The above 5 sensor attributes are stuffed in as follows: 1348 // Byte 21 = AA000000 = analog interpretation, 10 signed, 00 unsigned 1349 // Byte 22-24 are for other purposes 1350 // Byte 25 = MMMMMMMM = LSB of M 1351 // Byte 26 = MMTTTTTT = MSB of M (signed), and Tolerance 1352 // Byte 27 = BBBBBBBB = LSB of B 1353 // Byte 28 = BBAAAAAA = MSB of B (signed), and LSB of Accuracy 1354 // Byte 29 = AAAAEE00 = MSB of Accuracy, exponent of Accuracy 1355 // Byte 30 = RRRRBBBB = rExp (signed), bExp (signed) 1356 1357 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4 1358 record.body.m_lsb = mValue & 0xFF; 1359 1360 uint8_t mBitSign = (mValue < 0) ? 1 : 0; 1361 uint8_t mBitNine = (mValue & 0x0100) >> 8; 1362 1363 // move the smallest bit of the MSB into place (bit 9) 1364 // the MSbs are bits 7:8 in m_msb_and_tolerance 1365 record.body.m_msb_and_tolerance = (mBitSign << 7) | (mBitNine << 6); 1366 1367 record.body.b_lsb = bValue & 0xFF; 1368 1369 uint8_t bBitSign = (bValue < 0) ? 1 : 0; 1370 uint8_t bBitNine = (bValue & 0x0100) >> 8; 1371 1372 // move the smallest bit of the MSB into place (bit 9) 1373 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb 1374 record.body.b_msb_and_accuracy_lsb = (bBitSign << 7) | (bBitNine << 6); 1375 1376 uint8_t rExpSign = (rExp < 0) ? 1 : 0; 1377 uint8_t rExpBits = rExp & 0x07; 1378 1379 uint8_t bExpSign = (bExp < 0) ? 1 : 0; 1380 uint8_t bExpBits = bExp & 0x07; 1381 1382 // move rExp and bExp into place 1383 record.body.r_b_exponents = 1384 (rExpSign << 7) | (rExpBits << 4) | (bExpSign << 3) | bExpBits; 1385 1386 // Set the analog reading byte interpretation accordingly 1387 record.body.sensor_units_1 = (bSigned ? 1 : 0) << 7; 1388 1389 // TODO(): Perhaps care about Tolerance, Accuracy, and so on 1390 // These seem redundant, but derivable from the above 5 attributes 1391 // Original comment said "todo fill out rest of units" 1392 1393 // populate sensor name from path 1394 std::string name; 1395 size_t nameStart = path.rfind("/"); 1396 if (nameStart != std::string::npos) 1397 { 1398 name = path.substr(nameStart + 1, std::string::npos - nameStart); 1399 } 1400 1401 std::replace(name.begin(), name.end(), '_', ' '); 1402 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH) 1403 { 1404 // try to not truncate by replacing common words 1405 constexpr std::array<std::pair<const char*, const char*>, 2> 1406 replaceWords = {std::make_pair("Output", "Out"), 1407 std::make_pair("Input", "In")}; 1408 for (const auto& [find, replace] : replaceWords) 1409 { 1410 boost::replace_all(name, find, replace); 1411 } 1412 1413 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH); 1414 } 1415 record.body.id_string_info = name.size(); 1416 std::strncpy(record.body.id_string, name.c_str(), 1417 sizeof(record.body.id_string)); 1418 1419 // Remember the sensor name, as determined for this sensor number 1420 details::sdrStatsTable.updateName(sensornumber, name); 1421 1422 IPMIThresholds thresholdData; 1423 try 1424 { 1425 thresholdData = getIPMIThresholds(sensorMap); 1426 } 1427 catch (std::exception&) 1428 { 1429 phosphor::logging::log<phosphor::logging::level::ERR>( 1430 "getSensorDataRecord: getIPMIThresholds error"); 1431 return GENERAL_ERROR; 1432 } 1433 1434 if (thresholdData.criticalHigh) 1435 { 1436 record.body.upper_critical_threshold = *thresholdData.criticalHigh; 1437 record.body.supported_deassertions[1] |= static_cast<uint8_t>( 1438 IPMISensorEventEnableThresholds::criticalThreshold); 1439 record.body.supported_deassertions[1] |= static_cast<uint8_t>( 1440 IPMISensorEventEnableThresholds::upperCriticalGoingHigh); 1441 record.body.supported_assertions[1] |= static_cast<uint8_t>( 1442 IPMISensorEventEnableThresholds::upperCriticalGoingHigh); 1443 record.body.discrete_reading_setting_mask[0] |= 1444 static_cast<uint8_t>(IPMISensorReadingByte3::upperCritical); 1445 } 1446 if (thresholdData.warningHigh) 1447 { 1448 record.body.upper_noncritical_threshold = *thresholdData.warningHigh; 1449 record.body.supported_deassertions[1] |= static_cast<uint8_t>( 1450 IPMISensorEventEnableThresholds::nonCriticalThreshold); 1451 record.body.supported_deassertions[0] |= static_cast<uint8_t>( 1452 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh); 1453 record.body.supported_assertions[0] |= static_cast<uint8_t>( 1454 IPMISensorEventEnableThresholds::upperNonCriticalGoingHigh); 1455 record.body.discrete_reading_setting_mask[0] |= 1456 static_cast<uint8_t>(IPMISensorReadingByte3::upperNonCritical); 1457 } 1458 if (thresholdData.criticalLow) 1459 { 1460 record.body.lower_critical_threshold = *thresholdData.criticalLow; 1461 record.body.supported_assertions[1] |= static_cast<uint8_t>( 1462 IPMISensorEventEnableThresholds::criticalThreshold); 1463 record.body.supported_deassertions[0] |= static_cast<uint8_t>( 1464 IPMISensorEventEnableThresholds::lowerCriticalGoingLow); 1465 record.body.supported_assertions[0] |= static_cast<uint8_t>( 1466 IPMISensorEventEnableThresholds::lowerCriticalGoingLow); 1467 record.body.discrete_reading_setting_mask[0] |= 1468 static_cast<uint8_t>(IPMISensorReadingByte3::lowerCritical); 1469 } 1470 if (thresholdData.warningLow) 1471 { 1472 record.body.lower_noncritical_threshold = *thresholdData.warningLow; 1473 record.body.supported_assertions[1] |= static_cast<uint8_t>( 1474 IPMISensorEventEnableThresholds::nonCriticalThreshold); 1475 record.body.supported_deassertions[0] |= static_cast<uint8_t>( 1476 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow); 1477 record.body.supported_assertions[0] |= static_cast<uint8_t>( 1478 IPMISensorEventEnableThresholds::lowerNonCriticalGoingLow); 1479 record.body.discrete_reading_setting_mask[0] |= 1480 static_cast<uint8_t>(IPMISensorReadingByte3::lowerNonCritical); 1481 } 1482 1483 // everything that is readable is setable 1484 record.body.discrete_reading_setting_mask[1] = 1485 record.body.discrete_reading_setting_mask[0]; 1486 recordData.insert(recordData.end(), reinterpret_cast<uint8_t*>(&record), 1487 reinterpret_cast<uint8_t*>(&record) + sizeof(record)); 1488 return 0; 1489 } 1490 1491 /** @brief implements the get SDR Info command 1492 * @param count - Operation 1493 * 1494 * @returns IPMI completion code plus response data 1495 * - sdrCount - sensor/SDR count 1496 * - lunsAndDynamicPopulation - static/Dynamic sensor population flag 1497 */ 1498 static ipmi::RspType<uint8_t, // respcount 1499 uint8_t, // dynamic population flags 1500 uint32_t // last time a sensor was added 1501 > 1502 ipmiSensorGetDeviceSdrInfo(ipmi::Context::ptr ctx, 1503 std::optional<uint8_t> count) 1504 { 1505 auto& sensorTree = getSensorTree(); 1506 uint8_t sdrCount = 0; 1507 uint16_t recordID = 0; 1508 std::vector<uint8_t> record; 1509 // Sensors are dynamically allocated, and there is at least one LUN 1510 uint8_t lunsAndDynamicPopulation = 0x80; 1511 constexpr uint8_t getSdrCount = 0x01; 1512 constexpr uint8_t getSensorCount = 0x00; 1513 1514 if (!getSensorSubtree(sensorTree) || sensorTree.empty()) 1515 { 1516 return ipmi::responseResponseError(); 1517 } 1518 uint16_t numSensors = sensorTree.size(); 1519 if (count.value_or(0) == getSdrCount) 1520 { 1521 // Count the number of Type 1 SDR entries assigned to the LUN 1522 while (!getSensorDataRecord(ctx, record, recordID++)) 1523 { 1524 get_sdr::SensorDataRecordHeader* hdr = 1525 reinterpret_cast<get_sdr::SensorDataRecordHeader*>( 1526 record.data()); 1527 if (hdr && hdr->record_type == get_sdr::SENSOR_DATA_FULL_RECORD) 1528 { 1529 get_sdr::SensorDataFullRecord* recordData = 1530 reinterpret_cast<get_sdr::SensorDataFullRecord*>( 1531 record.data()); 1532 if (ctx->lun == recordData->key.owner_lun) 1533 { 1534 sdrCount++; 1535 } 1536 } 1537 } 1538 } 1539 else if (count.value_or(0) == getSensorCount) 1540 { 1541 // Return the number of sensors attached to the LUN 1542 if ((ctx->lun == 0) && (numSensors > 0)) 1543 { 1544 sdrCount = 1545 (numSensors > maxSensorsPerLUN) ? maxSensorsPerLUN : numSensors; 1546 } 1547 else if ((ctx->lun == 1) && (numSensors > maxSensorsPerLUN)) 1548 { 1549 sdrCount = (numSensors > (2 * maxSensorsPerLUN)) 1550 ? maxSensorsPerLUN 1551 : (numSensors - maxSensorsPerLUN) & maxSensorsPerLUN; 1552 } 1553 else if (ctx->lun == 3) 1554 { 1555 if (numSensors <= maxIPMISensors) 1556 { 1557 sdrCount = 1558 (numSensors - (2 * maxSensorsPerLUN)) & maxSensorsPerLUN; 1559 } 1560 else 1561 { 1562 // error 1563 throw std::out_of_range( 1564 "Maximum number of IPMI sensors exceeded."); 1565 } 1566 } 1567 } 1568 else 1569 { 1570 return ipmi::responseInvalidFieldRequest(); 1571 } 1572 1573 // Get Sensor count. This returns the number of sensors 1574 if (numSensors > 0) 1575 { 1576 lunsAndDynamicPopulation |= 1; 1577 } 1578 if (numSensors > maxSensorsPerLUN) 1579 { 1580 lunsAndDynamicPopulation |= 2; 1581 } 1582 if (numSensors >= (maxSensorsPerLUN * 2)) 1583 { 1584 lunsAndDynamicPopulation |= 8; 1585 } 1586 if (numSensors > maxIPMISensors) 1587 { 1588 // error 1589 throw std::out_of_range("Maximum number of IPMI sensors exceeded."); 1590 } 1591 1592 return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation, 1593 sdrLastAdd); 1594 } 1595 1596 /* end sensor commands */ 1597 1598 /* storage commands */ 1599 1600 ipmi::RspType<uint8_t, // sdr version 1601 uint16_t, // record count 1602 uint16_t, // free space 1603 uint32_t, // most recent addition 1604 uint32_t, // most recent erase 1605 uint8_t // operationSupport 1606 > 1607 ipmiStorageGetSDRRepositoryInfo(ipmi::Context::ptr ctx) 1608 { 1609 auto& sensorTree = getSensorTree(); 1610 constexpr const uint16_t unspecifiedFreeSpace = 0xFFFF; 1611 if (!getSensorSubtree(sensorTree) && sensorTree.empty()) 1612 { 1613 return ipmi::responseResponseError(); 1614 } 1615 1616 size_t fruCount = 0; 1617 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount); 1618 if (ret != ipmi::ccSuccess) 1619 { 1620 return ipmi::response(ret); 1621 } 1622 1623 uint16_t recordCount = 1624 sensorTree.size() + fruCount + ipmi::storage::type12Count; 1625 1626 uint8_t operationSupport = static_cast<uint8_t>( 1627 SdrRepositoryInfoOps::overflow); // write not supported 1628 1629 operationSupport |= 1630 static_cast<uint8_t>(SdrRepositoryInfoOps::allocCommandSupported); 1631 operationSupport |= static_cast<uint8_t>( 1632 SdrRepositoryInfoOps::reserveSDRRepositoryCommandSupported); 1633 return ipmi::responseSuccess(ipmiSdrVersion, recordCount, 1634 unspecifiedFreeSpace, sdrLastAdd, 1635 sdrLastRemove, operationSupport); 1636 } 1637 1638 /** @brief implements the get SDR allocation info command 1639 * 1640 * @returns IPMI completion code plus response data 1641 * - allocUnits - Number of possible allocation units 1642 * - allocUnitSize - Allocation unit size in bytes. 1643 * - allocUnitFree - Number of free allocation units 1644 * - allocUnitLargestFree - Largest free block in allocation units 1645 * - maxRecordSize - Maximum record size in allocation units. 1646 */ 1647 ipmi::RspType<uint16_t, // allocUnits 1648 uint16_t, // allocUnitSize 1649 uint16_t, // allocUnitFree 1650 uint16_t, // allocUnitLargestFree 1651 uint8_t // maxRecordSize 1652 > 1653 ipmiStorageGetSDRAllocationInfo() 1654 { 1655 // 0000h unspecified number of alloc units 1656 constexpr uint16_t allocUnits = 0; 1657 1658 constexpr uint16_t allocUnitFree = 0; 1659 constexpr uint16_t allocUnitLargestFree = 0; 1660 // only allow one block at a time 1661 constexpr uint8_t maxRecordSize = 1; 1662 1663 return ipmi::responseSuccess(allocUnits, maxSDRTotalSize, allocUnitFree, 1664 allocUnitLargestFree, maxRecordSize); 1665 } 1666 1667 /** @brief implements the reserve SDR command 1668 * @returns IPMI completion code plus response data 1669 * - sdrReservationID 1670 */ 1671 ipmi::RspType<uint16_t> ipmiStorageReserveSDR() 1672 { 1673 sdrReservationID++; 1674 if (sdrReservationID == 0) 1675 { 1676 sdrReservationID++; 1677 } 1678 1679 return ipmi::responseSuccess(sdrReservationID); 1680 } 1681 1682 ipmi::RspType<uint16_t, // next record ID 1683 std::vector<uint8_t> // payload 1684 > 1685 ipmiStorageGetSDR(ipmi::Context::ptr ctx, uint16_t reservationID, 1686 uint16_t recordID, uint8_t offset, uint8_t bytesToRead) 1687 { 1688 size_t fruCount = 0; 1689 // reservation required for partial reads with non zero offset into 1690 // record 1691 if ((sdrReservationID == 0 || reservationID != sdrReservationID) && offset) 1692 { 1693 phosphor::logging::log<phosphor::logging::level::ERR>( 1694 "ipmiStorageGetSDR: responseInvalidReservationId"); 1695 return ipmi::responseInvalidReservationId(); 1696 } 1697 ipmi::Cc ret = ipmi::storage::getFruSdrCount(ctx, fruCount); 1698 if (ret != ipmi::ccSuccess) 1699 { 1700 phosphor::logging::log<phosphor::logging::level::ERR>( 1701 "ipmiStorageGetSDR: getFruSdrCount error"); 1702 return ipmi::response(ret); 1703 } 1704 1705 auto& sensorTree = getSensorTree(); 1706 size_t lastRecord = 1707 sensorTree.size() + fruCount + ipmi::storage::type12Count - 1; 1708 uint16_t nextRecordId = lastRecord > recordID ? recordID + 1 : 0XFFFF; 1709 1710 if (!getSensorSubtree(sensorTree) && sensorTree.empty()) 1711 { 1712 phosphor::logging::log<phosphor::logging::level::ERR>( 1713 "ipmiStorageGetSDR: getSensorSubtree error"); 1714 return ipmi::responseResponseError(); 1715 } 1716 1717 std::vector<uint8_t> record; 1718 if (getSensorDataRecord(ctx, record, recordID)) 1719 { 1720 phosphor::logging::log<phosphor::logging::level::ERR>( 1721 "ipmiStorageGetSDR: fail to get SDR"); 1722 return ipmi::responseInvalidFieldRequest(); 1723 } 1724 get_sdr::SensorDataRecordHeader* hdr = 1725 reinterpret_cast<get_sdr::SensorDataRecordHeader*>(record.data()); 1726 if (!hdr) 1727 { 1728 phosphor::logging::log<phosphor::logging::level::ERR>( 1729 "ipmiStorageGetSDR: record header is null"); 1730 return ipmi::responseSuccess(nextRecordId, record); 1731 } 1732 1733 size_t sdrLength = 1734 sizeof(get_sdr::SensorDataRecordHeader) + hdr->record_length; 1735 if (sdrLength < (offset + bytesToRead)) 1736 { 1737 bytesToRead = sdrLength - offset; 1738 } 1739 1740 uint8_t* respStart = reinterpret_cast<uint8_t*>(hdr) + offset; 1741 if (!respStart) 1742 { 1743 phosphor::logging::log<phosphor::logging::level::ERR>( 1744 "ipmiStorageGetSDR: record is null"); 1745 return ipmi::responseSuccess(nextRecordId, record); 1746 } 1747 1748 std::vector<uint8_t> recordData(respStart, respStart + bytesToRead); 1749 1750 return ipmi::responseSuccess(nextRecordId, recordData); 1751 } 1752 /* end storage commands */ 1753 1754 void registerSensorFunctions() 1755 { 1756 // <Platform Event> 1757 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1758 ipmi::sensor_event::cmdPlatformEvent, 1759 ipmi::Privilege::Operator, ipmiSenPlatformEvent); 1760 1761 #ifdef FEATURE_DYNAMIC_SENSORS_WRITE 1762 // <Set Sensor Reading and Event Status> 1763 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1764 ipmi::sensor_event::cmdSetSensorReadingAndEvtSts, 1765 ipmi::Privilege::Operator, ipmiSetSensorReading); 1766 #endif 1767 1768 // <Get Sensor Reading> 1769 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1770 ipmi::sensor_event::cmdGetSensorReading, 1771 ipmi::Privilege::User, ipmiSenGetSensorReading); 1772 1773 // <Get Sensor Threshold> 1774 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1775 ipmi::sensor_event::cmdGetSensorThreshold, 1776 ipmi::Privilege::User, ipmiSenGetSensorThresholds); 1777 1778 // <Set Sensor Threshold> 1779 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1780 ipmi::sensor_event::cmdSetSensorThreshold, 1781 ipmi::Privilege::Operator, 1782 ipmiSenSetSensorThresholds); 1783 1784 // <Get Sensor Event Enable> 1785 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1786 ipmi::sensor_event::cmdGetSensorEventEnable, 1787 ipmi::Privilege::User, ipmiSenGetSensorEventEnable); 1788 1789 // <Get Sensor Event Status> 1790 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1791 ipmi::sensor_event::cmdGetSensorEventStatus, 1792 ipmi::Privilege::User, ipmiSenGetSensorEventStatus); 1793 1794 // register all storage commands for both Sensor and Storage command 1795 // versions 1796 1797 // <Get SDR Repository Info> 1798 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1799 ipmi::storage::cmdGetSdrRepositoryInfo, 1800 ipmi::Privilege::User, 1801 ipmiStorageGetSDRRepositoryInfo); 1802 1803 // <Get Device SDR Info> 1804 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1805 ipmi::sensor_event::cmdGetDeviceSdrInfo, 1806 ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo); 1807 1808 // <Get SDR Allocation Info> 1809 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1810 ipmi::storage::cmdGetSdrRepositoryAllocInfo, 1811 ipmi::Privilege::User, 1812 ipmiStorageGetSDRAllocationInfo); 1813 1814 // <Reserve SDR Repo> 1815 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1816 ipmi::sensor_event::cmdReserveDeviceSdrRepository, 1817 ipmi::Privilege::User, ipmiStorageReserveSDR); 1818 1819 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1820 ipmi::storage::cmdReserveSdrRepository, 1821 ipmi::Privilege::User, ipmiStorageReserveSDR); 1822 1823 // <Get Sdr> 1824 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor, 1825 ipmi::sensor_event::cmdGetDeviceSdr, 1826 ipmi::Privilege::User, ipmiStorageGetSDR); 1827 1828 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1829 ipmi::storage::cmdGetSdr, ipmi::Privilege::User, 1830 ipmiStorageGetSDR); 1831 } 1832 } // namespace ipmi 1833