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