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