1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation 4 #pragma once 5 6 #include "bmcweb_config.h" 7 8 #include "app.hpp" 9 #include "async_resp.hpp" 10 #include "dbus_utility.hpp" 11 #include "error_messages.hpp" 12 #include "generated/enums/memory.hpp" 13 #include "generated/enums/resource.hpp" 14 #include "http_request.hpp" 15 #include "led.hpp" 16 #include "logging.hpp" 17 #include "query.hpp" 18 #include "registries/privilege_registry.hpp" 19 #include "utils/collection.hpp" 20 #include "utils/dbus_utils.hpp" 21 #include "utils/hex_utils.hpp" 22 #include "utils/json_utils.hpp" 23 24 #include <asm-generic/errno.h> 25 26 #include <boost/beast/http/verb.hpp> 27 #include <boost/system/error_code.hpp> 28 #include <boost/url/format.hpp> 29 #include <nlohmann/json.hpp> 30 #include <sdbusplus/message/native_types.hpp> 31 #include <sdbusplus/unpack_properties.hpp> 32 33 #include <array> 34 #include <cstddef> 35 #include <cstdint> 36 #include <functional> 37 #include <memory> 38 #include <optional> 39 #include <string> 40 #include <string_view> 41 #include <utility> 42 #include <vector> 43 44 namespace redfish 45 { 46 47 inline std::string translateMemoryTypeToRedfish(const std::string& memoryType) 48 { 49 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR") 50 { 51 return "DDR"; 52 } 53 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR2") 54 { 55 return "DDR2"; 56 } 57 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR3") 58 { 59 return "DDR3"; 60 } 61 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR4") 62 { 63 return "DDR4"; 64 } 65 if (memoryType == 66 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR4E_SDRAM") 67 { 68 return "DDR4E_SDRAM"; 69 } 70 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR5") 71 { 72 return "DDR5"; 73 } 74 if (memoryType == 75 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.LPDDR4_SDRAM") 76 { 77 return "LPDDR4_SDRAM"; 78 } 79 if (memoryType == 80 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.LPDDR3_SDRAM") 81 { 82 return "LPDDR3_SDRAM"; 83 } 84 if (memoryType == 85 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR2_SDRAM_FB_DIMM") 86 { 87 return "DDR2_SDRAM_FB_DIMM"; 88 } 89 if (memoryType == 90 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR2_SDRAM_FB_DIMM_PROB") 91 { 92 return "DDR2_SDRAM_FB_DIMM_PROBE"; 93 } 94 if (memoryType == 95 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR_SGRAM") 96 { 97 return "DDR_SGRAM"; 98 } 99 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.ROM") 100 { 101 return "ROM"; 102 } 103 if (memoryType == 104 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.SDRAM") 105 { 106 return "SDRAM"; 107 } 108 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.EDO") 109 { 110 return "EDO"; 111 } 112 if (memoryType == 113 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.FastPageMode") 114 { 115 return "FastPageMode"; 116 } 117 if (memoryType == 118 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.PipelinedNibble") 119 { 120 return "PipelinedNibble"; 121 } 122 if (memoryType == 123 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.Logical") 124 { 125 return "Logical"; 126 } 127 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.HBM") 128 { 129 return "HBM"; 130 } 131 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.HBM2") 132 { 133 return "HBM2"; 134 } 135 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.HBM3") 136 { 137 return "HBM3"; 138 } 139 // This is values like Other or Unknown 140 // Also D-Bus values: 141 // DRAM 142 // EDRAM 143 // VRAM 144 // SRAM 145 // RAM 146 // FLASH 147 // EEPROM 148 // FEPROM 149 // EPROM 150 // CDRAM 151 // ThreeDRAM 152 // RDRAM 153 // FBD2 154 // LPDDR_SDRAM 155 // LPDDR2_SDRAM 156 // LPDDR5_SDRAM 157 return ""; 158 } 159 160 inline void dimmPropToHex(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 161 const char* key, const uint16_t* value, 162 const nlohmann::json::json_pointer& jsonPtr) 163 { 164 if (value == nullptr) 165 { 166 return; 167 } 168 asyncResp->res.jsonValue[jsonPtr][key] = "0x" + intToHexString(*value, 4); 169 } 170 171 inline void getPersistentMemoryProperties( 172 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 173 const dbus::utility::DBusPropertiesMap& properties, 174 const nlohmann::json::json_pointer& jsonPtr) 175 { 176 const uint16_t* moduleManufacturerID = nullptr; 177 const uint16_t* moduleProductID = nullptr; 178 const uint16_t* subsystemVendorID = nullptr; 179 const uint16_t* subsystemDeviceID = nullptr; 180 const uint64_t* volatileRegionSizeLimitInKiB = nullptr; 181 const uint64_t* pmRegionSizeLimitInKiB = nullptr; 182 const uint64_t* volatileSizeInKiB = nullptr; 183 const uint64_t* pmSizeInKiB = nullptr; 184 const uint64_t* cacheSizeInKB = nullptr; 185 const uint64_t* voltaileRegionMaxSizeInKib = nullptr; 186 const uint64_t* pmRegionMaxSizeInKiB = nullptr; 187 const uint64_t* allocationIncrementInKiB = nullptr; 188 const uint64_t* allocationAlignmentInKiB = nullptr; 189 const uint64_t* volatileRegionNumberLimit = nullptr; 190 const uint64_t* pmRegionNumberLimit = nullptr; 191 const uint64_t* spareDeviceCount = nullptr; 192 const bool* isSpareDeviceInUse = nullptr; 193 const bool* isRankSpareEnabled = nullptr; 194 const std::vector<uint32_t>* maxAveragePowerLimitmW = nullptr; 195 const bool* configurationLocked = nullptr; 196 const std::string* allowedMemoryModes = nullptr; 197 const std::string* memoryMedia = nullptr; 198 const bool* configurationLockCapable = nullptr; 199 const bool* dataLockCapable = nullptr; 200 const bool* passphraseCapable = nullptr; 201 const uint64_t* maxPassphraseCount = nullptr; 202 const uint64_t* passphraseLockLimit = nullptr; 203 204 const bool success = sdbusplus::unpackPropertiesNoThrow( 205 dbus_utils::UnpackErrorPrinter(), properties, "ModuleManufacturerID", 206 moduleManufacturerID, "ModuleProductID", moduleProductID, 207 "SubsystemVendorID", subsystemVendorID, "SubsystemDeviceID", 208 subsystemDeviceID, "VolatileRegionSizeLimitInKiB", 209 volatileRegionSizeLimitInKiB, "PmRegionSizeLimitInKiB", 210 pmRegionSizeLimitInKiB, "VolatileSizeInKiB", volatileSizeInKiB, 211 "PmSizeInKiB", pmSizeInKiB, "CacheSizeInKB", cacheSizeInKB, 212 "VoltaileRegionMaxSizeInKib", voltaileRegionMaxSizeInKib, 213 "PmRegionMaxSizeInKiB", pmRegionMaxSizeInKiB, 214 "AllocationIncrementInKiB", allocationIncrementInKiB, 215 "AllocationAlignmentInKiB", allocationAlignmentInKiB, 216 "VolatileRegionNumberLimit", volatileRegionNumberLimit, 217 "PmRegionNumberLimit", pmRegionNumberLimit, "SpareDeviceCount", 218 spareDeviceCount, "IsSpareDeviceInUse", isSpareDeviceInUse, 219 "IsRankSpareEnabled", isRankSpareEnabled, "MaxAveragePowerLimitmW", 220 maxAveragePowerLimitmW, "ConfigurationLocked", configurationLocked, 221 "AllowedMemoryModes", allowedMemoryModes, "MemoryMedia", memoryMedia, 222 "ConfigurationLockCapable", configurationLockCapable, "DataLockCapable", 223 dataLockCapable, "PassphraseCapable", passphraseCapable, 224 "MaxPassphraseCount", maxPassphraseCount, "PassphraseLockLimit", 225 passphraseLockLimit); 226 227 if (!success) 228 { 229 messages::internalError(asyncResp->res); 230 return; 231 } 232 233 dimmPropToHex(asyncResp, "ModuleManufacturerID", moduleManufacturerID, 234 jsonPtr); 235 dimmPropToHex(asyncResp, "ModuleProductID", moduleProductID, jsonPtr); 236 dimmPropToHex(asyncResp, "MemorySubsystemControllerManufacturerID", 237 subsystemVendorID, jsonPtr); 238 dimmPropToHex(asyncResp, "MemorySubsystemControllerProductID", 239 subsystemDeviceID, jsonPtr); 240 241 if (volatileRegionSizeLimitInKiB != nullptr) 242 { 243 asyncResp->res.jsonValue[jsonPtr]["VolatileRegionSizeLimitMiB"] = 244 (*volatileRegionSizeLimitInKiB) >> 10; 245 } 246 247 if (pmRegionSizeLimitInKiB != nullptr) 248 { 249 asyncResp->res.jsonValue[jsonPtr]["PersistentRegionSizeLimitMiB"] = 250 (*pmRegionSizeLimitInKiB) >> 10; 251 } 252 253 if (volatileSizeInKiB != nullptr) 254 { 255 asyncResp->res.jsonValue[jsonPtr]["VolatileSizeMiB"] = 256 (*volatileSizeInKiB) >> 10; 257 } 258 259 if (pmSizeInKiB != nullptr) 260 { 261 asyncResp->res.jsonValue[jsonPtr]["NonVolatileSizeMiB"] = 262 (*pmSizeInKiB) >> 10; 263 } 264 265 if (cacheSizeInKB != nullptr) 266 { 267 asyncResp->res.jsonValue[jsonPtr]["CacheSizeMiB"] = 268 (*cacheSizeInKB >> 10); 269 } 270 271 if (voltaileRegionMaxSizeInKib != nullptr) 272 { 273 asyncResp->res.jsonValue[jsonPtr]["VolatileRegionSizeMaxMiB"] = 274 (*voltaileRegionMaxSizeInKib) >> 10; 275 } 276 277 if (pmRegionMaxSizeInKiB != nullptr) 278 { 279 asyncResp->res.jsonValue[jsonPtr]["PersistentRegionSizeMaxMiB"] = 280 (*pmRegionMaxSizeInKiB) >> 10; 281 } 282 283 if (allocationIncrementInKiB != nullptr) 284 { 285 asyncResp->res.jsonValue[jsonPtr]["AllocationIncrementMiB"] = 286 (*allocationIncrementInKiB) >> 10; 287 } 288 289 if (allocationAlignmentInKiB != nullptr) 290 { 291 asyncResp->res.jsonValue[jsonPtr]["AllocationAlignmentMiB"] = 292 (*allocationAlignmentInKiB) >> 10; 293 } 294 295 if (volatileRegionNumberLimit != nullptr) 296 { 297 asyncResp->res.jsonValue[jsonPtr]["VolatileRegionNumberLimit"] = 298 *volatileRegionNumberLimit; 299 } 300 301 if (pmRegionNumberLimit != nullptr) 302 { 303 asyncResp->res.jsonValue[jsonPtr]["PersistentRegionNumberLimit"] = 304 *pmRegionNumberLimit; 305 } 306 307 if (spareDeviceCount != nullptr) 308 { 309 asyncResp->res.jsonValue[jsonPtr]["SpareDeviceCount"] = 310 *spareDeviceCount; 311 } 312 313 if (isSpareDeviceInUse != nullptr) 314 { 315 asyncResp->res.jsonValue[jsonPtr]["IsSpareDeviceEnabled"] = 316 *isSpareDeviceInUse; 317 } 318 319 if (isRankSpareEnabled != nullptr) 320 { 321 asyncResp->res.jsonValue[jsonPtr]["IsRankSpareEnabled"] = 322 *isRankSpareEnabled; 323 } 324 325 if (maxAveragePowerLimitmW != nullptr) 326 { 327 asyncResp->res.jsonValue[jsonPtr]["MaxTDPMilliWatts"] = 328 *maxAveragePowerLimitmW; 329 } 330 331 if (configurationLocked != nullptr) 332 { 333 asyncResp->res.jsonValue[jsonPtr]["ConfigurationLocked"] = 334 *configurationLocked; 335 } 336 337 if (allowedMemoryModes != nullptr) 338 { 339 constexpr const std::array<const char*, 3> values{"Volatile", "PMEM", 340 "Block"}; 341 342 for (const char* v : values) 343 { 344 if (allowedMemoryModes->ends_with(v)) 345 { 346 asyncResp->res.jsonValue[jsonPtr]["OperatingMemoryModes"] 347 .push_back(v); 348 break; 349 } 350 } 351 } 352 353 if (memoryMedia != nullptr) 354 { 355 constexpr const std::array<const char*, 3> values{"DRAM", "NAND", 356 "Intel3DXPoint"}; 357 358 for (const char* v : values) 359 { 360 if (memoryMedia->ends_with(v)) 361 { 362 asyncResp->res.jsonValue[jsonPtr]["MemoryMedia"].push_back(v); 363 break; 364 } 365 } 366 } 367 368 if (configurationLockCapable != nullptr) 369 { 370 asyncResp->res.jsonValue[jsonPtr]["SecurityCapabilities"] 371 ["ConfigurationLockCapable"] = 372 *configurationLockCapable; 373 } 374 375 if (dataLockCapable != nullptr) 376 { 377 asyncResp->res 378 .jsonValue[jsonPtr]["SecurityCapabilities"]["DataLockCapable"] = 379 *dataLockCapable; 380 } 381 382 if (passphraseCapable != nullptr) 383 { 384 asyncResp->res 385 .jsonValue[jsonPtr]["SecurityCapabilities"]["PassphraseCapable"] = 386 *passphraseCapable; 387 } 388 389 if (maxPassphraseCount != nullptr) 390 { 391 asyncResp->res 392 .jsonValue[jsonPtr]["SecurityCapabilities"]["MaxPassphraseCount"] = 393 *maxPassphraseCount; 394 } 395 396 if (passphraseLockLimit != nullptr) 397 { 398 asyncResp->res 399 .jsonValue[jsonPtr]["SecurityCapabilities"]["PassphraseLockLimit"] = 400 *passphraseLockLimit; 401 } 402 } 403 404 inline void assembleDimmProperties( 405 std::string_view dimmId, 406 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 407 const dbus::utility::DBusPropertiesMap& properties, 408 const nlohmann::json::json_pointer& jsonPtr) 409 { 410 asyncResp->res.jsonValue[jsonPtr]["Id"] = dimmId; 411 asyncResp->res.jsonValue[jsonPtr]["Name"] = "DIMM Slot"; 412 asyncResp->res.jsonValue[jsonPtr]["Status"]["State"] = 413 resource::State::Enabled; 414 asyncResp->res.jsonValue[jsonPtr]["Status"]["Health"] = 415 resource::Health::OK; 416 417 const uint16_t* memoryDataWidth = nullptr; 418 const size_t* memorySizeInKB = nullptr; 419 const std::string* partNumber = nullptr; 420 const std::string* serialNumber = nullptr; 421 const std::string* manufacturer = nullptr; 422 const uint16_t* revisionCode = nullptr; 423 const bool* present = nullptr; 424 const uint16_t* memoryTotalWidth = nullptr; 425 const std::string* ecc = nullptr; 426 const std::string* formFactor = nullptr; 427 const std::vector<uint16_t>* allowedSpeedsMT = nullptr; 428 const size_t* memoryAttributes = nullptr; 429 const uint16_t* memoryConfiguredSpeedInMhz = nullptr; 430 const std::string* memoryType = nullptr; 431 const std::uint8_t* channel = nullptr; 432 const std::uint8_t* memoryController = nullptr; 433 const std::uint8_t* slot = nullptr; 434 const std::uint8_t* socket = nullptr; 435 const std::string* sparePartNumber = nullptr; 436 const std::string* model = nullptr; 437 const std::string* locationCode = nullptr; 438 const bool* functional = nullptr; 439 440 const bool success = sdbusplus::unpackPropertiesNoThrow( 441 dbus_utils::UnpackErrorPrinter(), properties, "MemoryDataWidth", 442 memoryDataWidth, "MemorySizeInKB", memorySizeInKB, "PartNumber", 443 partNumber, "SerialNumber", serialNumber, "Manufacturer", manufacturer, 444 "RevisionCode", revisionCode, "Present", present, "MemoryTotalWidth", 445 memoryTotalWidth, "ECC", ecc, "FormFactor", formFactor, 446 "AllowedSpeedsMT", allowedSpeedsMT, "MemoryAttributes", 447 memoryAttributes, "MemoryConfiguredSpeedInMhz", 448 memoryConfiguredSpeedInMhz, "MemoryType", memoryType, "Channel", 449 channel, "MemoryController", memoryController, "Slot", slot, "Socket", 450 socket, "SparePartNumber", sparePartNumber, "Model", model, 451 "LocationCode", locationCode, "Functional", functional); 452 453 if (!success) 454 { 455 messages::internalError(asyncResp->res); 456 return; 457 } 458 459 if (memoryDataWidth != nullptr) 460 { 461 asyncResp->res.jsonValue[jsonPtr]["DataWidthBits"] = *memoryDataWidth; 462 } 463 464 if (memorySizeInKB != nullptr) 465 { 466 asyncResp->res.jsonValue[jsonPtr]["CapacityMiB"] = 467 (*memorySizeInKB >> 10); 468 } 469 470 if (partNumber != nullptr) 471 { 472 asyncResp->res.jsonValue[jsonPtr]["PartNumber"] = *partNumber; 473 } 474 475 if (serialNumber != nullptr) 476 { 477 asyncResp->res.jsonValue[jsonPtr]["SerialNumber"] = *serialNumber; 478 } 479 480 if (manufacturer != nullptr) 481 { 482 asyncResp->res.jsonValue[jsonPtr]["Manufacturer"] = *manufacturer; 483 } 484 485 if (revisionCode != nullptr) 486 { 487 asyncResp->res.jsonValue[jsonPtr]["FirmwareRevision"] = 488 std::to_string(*revisionCode); 489 } 490 491 if (present != nullptr && !*present) 492 { 493 asyncResp->res.jsonValue[jsonPtr]["Status"]["State"] = 494 resource::State::Absent; 495 } 496 497 if (functional != nullptr) 498 { 499 if (!*functional) 500 { 501 asyncResp->res.jsonValue[jsonPtr]["Status"]["Health"] = 502 resource::Health::Critical; 503 } 504 } 505 506 if (memoryTotalWidth != nullptr) 507 { 508 asyncResp->res.jsonValue[jsonPtr]["BusWidthBits"] = *memoryTotalWidth; 509 } 510 511 if (ecc != nullptr) 512 { 513 constexpr const std::array<const char*, 4> values{ 514 "NoECC", "SingleBitECC", "MultiBitECC", "AddressParity"}; 515 516 for (const char* v : values) 517 { 518 if (ecc->ends_with(v)) 519 { 520 asyncResp->res.jsonValue[jsonPtr]["ErrorCorrection"] = v; 521 break; 522 } 523 } 524 } 525 526 if (formFactor != nullptr) 527 { 528 constexpr const std::array<const char*, 11> values{ 529 "RDIMM", "UDIMM", "SO_DIMM", "LRDIMM", 530 "Mini_RDIMM", "Mini_UDIMM", "SO_RDIMM_72b", "SO_UDIMM_72b", 531 "SO_DIMM_16b", "SO_DIMM_32b", "Die"}; 532 533 for (const char* v : values) 534 { 535 if (formFactor->ends_with(v)) 536 { 537 asyncResp->res.jsonValue[jsonPtr]["BaseModuleType"] = v; 538 break; 539 } 540 } 541 } 542 543 if (allowedSpeedsMT != nullptr) 544 { 545 nlohmann::json& jValue = 546 asyncResp->res.jsonValue[jsonPtr]["AllowedSpeedsMHz"]; 547 jValue = nlohmann::json::array(); 548 for (uint16_t subVal : *allowedSpeedsMT) 549 { 550 jValue.push_back(subVal); 551 } 552 } 553 554 if (memoryAttributes != nullptr) 555 { 556 asyncResp->res.jsonValue[jsonPtr]["RankCount"] = *memoryAttributes; 557 } 558 559 if (memoryConfiguredSpeedInMhz != nullptr) 560 { 561 asyncResp->res.jsonValue[jsonPtr]["OperatingSpeedMhz"] = 562 *memoryConfiguredSpeedInMhz; 563 } 564 565 if (memoryType != nullptr) 566 { 567 std::string memoryDeviceType = 568 translateMemoryTypeToRedfish(*memoryType); 569 // Values like "Unknown" or "Other" will return empty 570 // so just leave off 571 if (!memoryDeviceType.empty()) 572 { 573 asyncResp->res.jsonValue[jsonPtr]["MemoryDeviceType"] = 574 memoryDeviceType; 575 } 576 if (memoryType->find("DDR") != std::string::npos) 577 { 578 asyncResp->res.jsonValue[jsonPtr]["MemoryType"] = 579 memory::MemoryType::DRAM; 580 } 581 else if (memoryType->ends_with("Logical")) 582 { 583 asyncResp->res.jsonValue[jsonPtr]["MemoryType"] = 584 memory::MemoryType::IntelOptane; 585 } 586 } 587 588 if (channel != nullptr) 589 { 590 asyncResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Channel"] = 591 *channel; 592 } 593 594 if (memoryController != nullptr) 595 { 596 asyncResp->res 597 .jsonValue[jsonPtr]["MemoryLocation"]["MemoryController"] = 598 *memoryController; 599 } 600 601 if (slot != nullptr) 602 { 603 asyncResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Slot"] = *slot; 604 } 605 606 if (socket != nullptr) 607 { 608 asyncResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Socket"] = *socket; 609 } 610 611 if (sparePartNumber != nullptr) 612 { 613 asyncResp->res.jsonValue[jsonPtr]["SparePartNumber"] = *sparePartNumber; 614 } 615 616 if (model != nullptr) 617 { 618 asyncResp->res.jsonValue[jsonPtr]["Model"] = *model; 619 } 620 621 if (locationCode != nullptr) 622 { 623 asyncResp->res 624 .jsonValue[jsonPtr]["Location"]["PartLocation"]["ServiceLabel"] = 625 *locationCode; 626 } 627 628 getPersistentMemoryProperties(asyncResp, properties, jsonPtr); 629 } 630 631 inline void getDimmDataByService( 632 std::shared_ptr<bmcweb::AsyncResp> asyncResp, const std::string& dimmId, 633 const std::string& service, const std::string& objPath) 634 { 635 BMCWEB_LOG_DEBUG("Get available system components."); 636 dbus::utility::getAllProperties( 637 service, objPath, "", 638 [dimmId, asyncResp{std::move(asyncResp)}]( 639 const boost::system::error_code& ec, 640 const dbus::utility::DBusPropertiesMap& properties) { 641 if (ec) 642 { 643 BMCWEB_LOG_DEBUG("DBUS response error"); 644 messages::internalError(asyncResp->res); 645 return; 646 } 647 assembleDimmProperties(dimmId, asyncResp, properties, 648 ""_json_pointer); 649 }); 650 } 651 652 inline void assembleDimmPartitionData( 653 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 654 const dbus::utility::DBusPropertiesMap& properties, 655 const nlohmann::json::json_pointer& regionPtr) 656 { 657 const std::string* memoryClassification = nullptr; 658 const uint64_t* offsetInKiB = nullptr; 659 const std::string* partitionId = nullptr; 660 const bool* passphraseState = nullptr; 661 const uint64_t* sizeInKiB = nullptr; 662 663 const bool success = sdbusplus::unpackPropertiesNoThrow( 664 dbus_utils::UnpackErrorPrinter(), properties, "MemoryClassification", 665 memoryClassification, "OffsetInKiB", offsetInKiB, "PartitionId", 666 partitionId, "PassphraseState", passphraseState, "SizeInKiB", 667 sizeInKiB); 668 669 if (!success) 670 { 671 messages::internalError(asyncResp->res); 672 return; 673 } 674 675 nlohmann::json::object_t partition; 676 677 if (memoryClassification != nullptr) 678 { 679 partition["MemoryClassification"] = *memoryClassification; 680 } 681 682 if (offsetInKiB != nullptr) 683 { 684 partition["OffsetMiB"] = (*offsetInKiB >> 10); 685 } 686 687 if (partitionId != nullptr) 688 { 689 partition["RegionId"] = *partitionId; 690 } 691 692 if (passphraseState != nullptr) 693 { 694 partition["PassphraseEnabled"] = *passphraseState; 695 } 696 697 if (sizeInKiB != nullptr) 698 { 699 partition["SizeMiB"] = (*sizeInKiB >> 10); 700 } 701 702 asyncResp->res.jsonValue[regionPtr].emplace_back(std::move(partition)); 703 } 704 705 inline void getDimmPartitionData(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 706 const std::string& service, 707 const std::string& path) 708 { 709 dbus::utility::getAllProperties( 710 service, path, 711 "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition", 712 [asyncResp{std::move(asyncResp)}]( 713 const boost::system::error_code& ec, 714 const dbus::utility::DBusPropertiesMap& properties) { 715 if (ec) 716 { 717 BMCWEB_LOG_DEBUG("DBUS response error"); 718 messages::internalError(asyncResp->res); 719 720 return; 721 } 722 nlohmann::json::json_pointer regionPtr = "/Regions"_json_pointer; 723 assembleDimmPartitionData(asyncResp, properties, regionPtr); 724 } 725 726 ); 727 } 728 729 inline void afterGetDimmData( 730 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 731 const std::string& dimmId, const boost::system::error_code& ec, 732 const dbus::utility::MapperGetSubTreeResponse& subtree) 733 { 734 if (ec) 735 { 736 if (ec.value() != EBADR) 737 { 738 BMCWEB_LOG_ERROR("DBUS response error: {}", ec.value()); 739 messages::internalError(asyncResp->res); 740 } 741 return; 742 } 743 744 bool found = false; 745 for (const auto& [objectPath, serviceMap] : subtree) 746 { 747 sdbusplus::message::object_path path(objectPath); 748 749 bool dimmInterface = false; 750 bool associationInterface = false; 751 /* Note: Multiple D-Bus objects can provide details for the Memory 752 * object: 1) Dimm is the primary object 2) Additional partitions could 753 * exist per Dimm. Only consider the object found if the Dimm is found. 754 */ 755 for (const auto& [serviceName, interfaceList] : serviceMap) 756 { 757 for (const auto& interface : interfaceList) 758 { 759 if (interface == "xyz.openbmc_project.Inventory.Item.Dimm" && 760 path.filename() == dimmId) 761 { 762 // Found the single Dimm 763 getDimmDataByService(asyncResp, dimmId, serviceName, 764 objectPath); 765 dimmInterface = true; 766 found = true; 767 } 768 else if (interface == 769 "xyz.openbmc_project.Association.Definitions") 770 { 771 /* Object has associations. If this object is also a Dimm 772 * then the association might provide the LED state 773 * information. After all interfaces for this object have 774 * been checked the LED information will be gathered if the 775 * object was a Dimm 776 */ 777 associationInterface = true; 778 } 779 else if ( 780 interface == 781 "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition" && 782 path.parent_path().filename() == dimmId) 783 { 784 // partitions are separate as there can be multiple per 785 // device, i.e. 786 // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition1 787 // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition2 788 getDimmPartitionData(asyncResp, serviceName, objectPath); 789 } 790 } 791 } 792 793 /* If a Dimm has an Association check if it has a LED */ 794 if (associationInterface && dimmInterface) 795 { 796 getLocationIndicatorActive(asyncResp, objectPath); 797 } 798 } 799 800 if (!found) 801 { 802 // Dimm object not found 803 messages::resourceNotFound(asyncResp->res, "Memory", dimmId); 804 return; 805 } 806 // Set @odata only if object is found 807 asyncResp->res.jsonValue["@odata.type"] = "#Memory.v1_11_0.Memory"; 808 asyncResp->res.jsonValue["@odata.id"] = 809 boost::urls::format("/redfish/v1/Systems/{}/Memory/{}", 810 BMCWEB_REDFISH_SYSTEM_URI_NAME, dimmId); 811 } 812 813 inline void getDimmData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 814 const std::string& dimmId) 815 { 816 BMCWEB_LOG_DEBUG("Get dimm path for {}", dimmId); 817 constexpr std::array<std::string_view, 2> interfaces = { 818 "xyz.openbmc_project.Inventory.Item.Dimm", 819 "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition"}; 820 821 dbus::utility::getSubTree( 822 "/xyz/openbmc_project/inventory", 0, interfaces, 823 [asyncResp, 824 dimmId](const boost::system::error_code& ec, 825 const dbus::utility::MapperGetSubTreeResponse& subtree) { 826 afterGetDimmData(asyncResp, dimmId, ec, subtree); 827 }); 828 } 829 830 inline void handleSetDimmData( 831 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 832 bool locationIndicatorActive, const std::string& dimmPath) 833 { 834 setLocationIndicatorActive(asyncResp, dimmPath, locationIndicatorActive); 835 } 836 837 inline void afterGetValidDimmPath( 838 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 839 const std::string& dimmId, const boost::system::error_code& ec, 840 const dbus::utility::MapperGetSubTreePathsResponse& subtree, 841 const std::function<void(const std::string& dimmPath)>& callback) 842 { 843 if (ec) 844 { 845 if (ec.value() == EBADR) 846 { 847 /* Need to report error for PATCH */ 848 BMCWEB_LOG_WARNING("Dimm not found in inventory"); 849 messages::resourceNotFound(asyncResp->res, "Memory", dimmId); 850 } 851 else 852 { 853 BMCWEB_LOG_ERROR("DBUS response error: {}", ec.value()); 854 messages::internalError(asyncResp->res); 855 } 856 return; 857 } 858 859 for (const auto& objectPath : subtree) 860 { 861 // Ignore any objects which don't end with our desired dimm name 862 sdbusplus::message::object_path path(objectPath); 863 if (path.filename() == dimmId) 864 { 865 callback(path); 866 return; 867 } 868 } 869 870 // Object not found 871 messages::resourceNotFound(asyncResp->res, "Memory", dimmId); 872 } 873 874 inline void getValidDimmPath( 875 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 876 const std::string& dimmId, 877 std::function<void(const std::string& dimmPath)>&& callback) 878 { 879 BMCWEB_LOG_DEBUG("Get dimm path for {}", dimmId); 880 constexpr std::array<std::string_view, 1> interfaces = { 881 "xyz.openbmc_project.Inventory.Item.Dimm"}; 882 883 dbus::utility::getSubTreePaths( 884 "/xyz/openbmc_project/inventory", 0, interfaces, 885 [asyncResp, dimmId, callback{std::move(callback)}]( 886 const boost::system::error_code& ec, 887 const dbus::utility::MapperGetSubTreePathsResponse& subtree) { 888 afterGetValidDimmPath(asyncResp, dimmId, ec, subtree, callback); 889 }); 890 } 891 892 inline void handleMemoryPatch( 893 App& app, const crow::Request& req, 894 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 895 const std::string& systemName, const std::string& dimmId) 896 { 897 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 898 { 899 return; 900 } 901 902 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 903 { 904 // Option currently returns no systems. TBD 905 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 906 systemName); 907 return; 908 } 909 910 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 911 { 912 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 913 systemName); 914 return; 915 } 916 917 std::optional<bool> locationIndicatorActive; 918 if (!json_util::readJsonPatch( // 919 req, asyncResp->res, // 920 "LocationIndicatorActive", locationIndicatorActive // 921 )) 922 { 923 return; 924 } 925 926 if (locationIndicatorActive) 927 { 928 getValidDimmPath(asyncResp, dimmId, 929 std::bind_front(handleSetDimmData, asyncResp, 930 *locationIndicatorActive)); 931 } 932 } 933 934 inline void handleMemoryGet(App& app, const crow::Request& req, 935 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 936 const std::string& systemName, 937 const std::string& dimmId) 938 { 939 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 940 { 941 return; 942 } 943 944 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 945 { 946 // Option currently returns no systems. TBD 947 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 948 systemName); 949 return; 950 } 951 952 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 953 { 954 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 955 systemName); 956 return; 957 } 958 959 getDimmData(asyncResp, dimmId); 960 } 961 962 inline void requestRoutesMemoryCollection(App& app) 963 { 964 /** 965 * Functions triggers appropriate requests on DBus 966 */ 967 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/") 968 .privileges(redfish::privileges::getMemoryCollection) 969 .methods(boost::beast::http::verb::get)( 970 [&app](const crow::Request& req, 971 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 972 const std::string& systemName) { 973 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 974 { 975 return; 976 } 977 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 978 { 979 // Option currently returns no systems. TBD 980 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 981 systemName); 982 return; 983 } 984 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 985 { 986 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 987 systemName); 988 return; 989 } 990 991 asyncResp->res.jsonValue["@odata.type"] = 992 "#MemoryCollection.MemoryCollection"; 993 asyncResp->res.jsonValue["Name"] = "Memory Module Collection"; 994 asyncResp->res.jsonValue["@odata.id"] = 995 boost::urls::format("/redfish/v1/Systems/{}/Memory", 996 BMCWEB_REDFISH_SYSTEM_URI_NAME); 997 998 constexpr std::array<std::string_view, 1> interfaces{ 999 "xyz.openbmc_project.Inventory.Item.Dimm"}; 1000 collection_util::getCollectionMembers( 1001 asyncResp, 1002 boost::urls::format("/redfish/v1/Systems/{}/Memory", 1003 BMCWEB_REDFISH_SYSTEM_URI_NAME), 1004 interfaces, "/xyz/openbmc_project/inventory"); 1005 }); 1006 } 1007 1008 inline void requestRoutesMemory(App& app) 1009 { 1010 /** 1011 * Functions triggers appropriate requests on DBus 1012 */ 1013 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/<str>/") 1014 .privileges(redfish::privileges::getMemory) 1015 .methods(boost::beast::http::verb::get)( 1016 std::bind_front(handleMemoryGet, std::ref(app))); 1017 1018 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/<str>/") 1019 .privileges(redfish::privileges::patchMemory) 1020 .methods(boost::beast::http::verb::patch)( 1021 std::bind_front(handleMemoryPatch, std::ref(app))); 1022 } 1023 1024 } // namespace redfish 1025