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