| #ifndef THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_PROCESSOR_H_ | |
| #define THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_PROCESSOR_H_ | |
| /* | |
| // Copyright (c) 2018 Intel Corporation | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| */ | |
| #include <systemd/sd-bus.h> | |
| #include <algorithm> | |
| #include <array> | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <functional> | |
| #include <limits> | |
| #include <map> | |
| #include <memory> | |
| #include <optional> | |
| #include <string> | |
| #include <string_view> | |
| #include <system_error> // NOLINT | |
| #include <tuple> | |
| #include <unordered_map> | |
| #include <utility> | |
| #include <variant> | |
| #include <vector> | |
| #include "absl/strings/match.h" | |
| #include "absl/strings/numbers.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "boost/container/flat_map.hpp" // NOLINT | |
| #include "boost/system/error_code.hpp" // NOLINT | |
| #include "app.hpp" | |
| #include "http_request.hpp" | |
| #include "logging.hpp" | |
| #include "utility.hpp" | |
| #include "async_resp.hpp" | |
| #include "dbus_utility.hpp" | |
| #include "human_sort.hpp" | |
| #include "error_messages.hpp" | |
| #include "query.hpp" | |
| #include "registries/privilege_registry.hpp" | |
| #include "collection.hpp" | |
| #include "dbus_utils.hpp" | |
| #include "hex_utils.hpp" | |
| #include "json_utils.hpp" | |
| #include "location_utils.hpp" | |
| #include "system_utils.hpp" | |
| #include "external_storer.hpp" | |
| #include "health.hpp" | |
| #include <nlohmann/json.hpp> | |
| #include "managed_store.hpp" | |
| #include "managed_store_types.hpp" | |
| #include "sdbusplus/message.hpp" | |
| #include "sdbusplus/message/native_types.hpp" | |
| #include "sdbusplus/unpack_properties.hpp" | |
| namespace redfish { | |
| using resourceIdToSubtreeRespMapType = | |
| std::unordered_map<std::string, | |
| std::pair<std::string, dbus::utility::MapperServiceMap>>; | |
| using ManagedObjectByServiceMap = | |
| std::unordered_map<std::string, dbus::utility::ManagedObjectType>; | |
| constexpr std::array<std::string_view, 2> processorInterfaces = { | |
| "xyz.openbmc_project.Inventory.Item.Cpu", | |
| "xyz.openbmc_project.Inventory.Item.Accelerator"}; | |
| constexpr std::array<std::string_view, 1> subProcessorCoreInterfaces = { | |
| "xyz.openbmc_project.Inventory.Item.CpuCore"}; | |
| constexpr std::array<std::string_view, 1> subProcessorThreadInterfaces = { | |
| "xyz.openbmc_project.Inventory.Item.CpuThread"}; | |
| inline void getSubProcessorThreadCollectionWithExpand( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const nlohmann::json::json_pointer& jsonPtr, uint8_t expandLevel, | |
| const std::string& systemName, const std::string& processorId, | |
| const std::string& coreId, const std::string& corePath, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree, | |
| const ManagedObjectByServiceMap& managedObjectsMap); | |
| inline void getSubProcessorCoreCollectionWithExpand( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const nlohmann::json::json_pointer& jsonPtr, uint8_t expandLevel, | |
| const std::string& systemName, const std::string& processorId, | |
| const std::string& cpuPath, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree, | |
| const ManagedObjectByServiceMap& managedObjectsMap); | |
| inline void getProcessorCollectionWithExpand( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree, | |
| const ManagedObjectByServiceMap& managedObjectsMap, uint8_t expandLevel, | |
| const std::string& systempath); | |
| inline void handleGetCpuCoreThreadMetrics( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const nlohmann::json::json_pointer& jsonPtr, const std::string& systemName, | |
| const std::string& cpuArg, const std::string& coreArg, | |
| const std::string& threadArg); | |
| inline void handleGetCpuMetrics( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const nlohmann::json::json_pointer& jsonPtr, const std::string& systemName, | |
| const std::string& cpuArg); | |
| inline bool isValidSystemServicePair(const std::string& systemName, | |
| const std::string& service); | |
| /** | |
| * @brief Returns a collection of dbus interfaces applicable to SubProcessor's | |
| * subtree. | |
| * | |
| * @param[in] expandLevel Requested expand level. | |
| * | |
| * @return Vector of strings capturing dbus interface names. | |
| */ | |
| inline std::vector<std::string> getInterfacesForSubProcessorSubtree( | |
| uint8_t expandLevel) { | |
| std::vector<std::string> subProcessorSubtreeInterfaces = { | |
| subProcessorCoreInterfaces.begin(), subProcessorCoreInterfaces.end()}; | |
| if (expandLevel > 0) { | |
| subProcessorSubtreeInterfaces.insert(subProcessorSubtreeInterfaces.end(), | |
| subProcessorThreadInterfaces.begin(), | |
| subProcessorThreadInterfaces.end()); | |
| return subProcessorSubtreeInterfaces; | |
| } | |
| return subProcessorSubtreeInterfaces; | |
| } | |
| /** | |
| * @brief Check if the given system name and service name are a valid pair. | |
| * | |
| * @param[in] systemName The name of the system. | |
| * @param[in] service The name of the service. | |
| * | |
| * @return True if the pair is valid, false otherwise. | |
| */ | |
| inline bool isValidSystemServicePair(const std::string& systemName, | |
| const std::string& service) { | |
| static const std::unordered_map<std::string, std::string> | |
| validSystemServices = {{"system1", "xyz.openbmc_project.pldm0"}, | |
| {"system2", "xyz.openbmc_project.pldm1"}, | |
| {"system", "xyz.openbmc_project.pldm"}}; | |
| auto it = validSystemServices.find(systemName); | |
| return (it != validSystemServices.end() && it->second == service); | |
| } | |
| /** | |
| * | |
| * @param[in,out] aResp Async HTTP response. | |
| * @param[in] processorPath The D-Bus path of the processor. | |
| * @param[in] jsonPtr json pointer to index the response fields. | |
| * @param[in] systemName system name | |
| */ | |
| inline void getProcessorMaxTDP(const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& processorPath, | |
| const nlohmann::json::json_pointer& jsonPtr, | |
| const std::string& systemName) { | |
| constexpr std::array<std::string_view, 1> interfaces = { | |
| "xyz.openbmc_project.Effecter.Value"}; | |
| managedStore::ManagedObjectStoreContext pldmContext(aResp); | |
| // Get all PLDM effecters using the ManagedObjectStore | |
| managedStore::GetManagedObjectStore()->getSubTree( | |
| "/xyz/openbmc_project/pldm", 0, interfaces, pldmContext, | |
| [aResp, processorPath, jsonPtr, systemName]( | |
| const boost::system::error_code& ec, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { | |
| if (ec) { | |
| BMCWEB_LOG_ERROR << "DBUS response error for pldm effecters: " << ec; | |
| // It's not a fatal error if PLDM isn't present, so just return. | |
| return; | |
| } | |
| // Iterate through the PLDM services (CN0, CN1, etc.) | |
| for (const auto& [effecterPath, serviceMap] : subtree) { | |
| if (serviceMap.empty()) { | |
| continue; | |
| } | |
| // 2. Inner loop iterates over the key-value pairs in 'serviceMap'. | |
| for (const auto& servicePair : serviceMap) { | |
| // 3. We only use '.first' to get the service name (the key). | |
| const std::string& service = servicePair.first; | |
| BMCWEB_LOG_DEBUG << "Service Name: " << service | |
| << "Effecter Path: " << effecterPath; | |
| // Inverted condition to skip invalid pairs first | |
| if (!isValidSystemServicePair(systemName, service)) { | |
| continue; | |
| } | |
| // The effecter object path looks like: | |
| // /xyz/openbmc_project/pldm/4/effecter/power/SOC_TDP_4. | |
| // This object implements the | |
| // xyz.openbmc_project.Effecter.Value interface, which | |
| // stores the configured MaxTDPWatts field. The object name | |
| // can be same for both pldm services on muti CN systems | |
| if (absl::StrContains(effecterPath, "SOC_TDP")) { | |
| // Get the MaxTDPWatts value | |
| managedStore::ManagedObjectStoreContext valueContext(aResp); | |
| dbus_utils::getProperty<double>( | |
| service, effecterPath, "xyz.openbmc_project.Effecter.Value", | |
| "Value", valueContext, | |
| [aResp, jsonPtr, effecterPath]( | |
| const boost::system::error_code& ec3, | |
| const double& value) { | |
| if (ec3) { | |
| BMCWEB_LOG_ERROR | |
| << "DBUS response error for effecterPath: " | |
| << effecterPath << " ,Error: " << ec3.message(); | |
| return; | |
| } | |
| aResp->res.jsonValue[jsonPtr]["MaxTDPWatts"] = value; | |
| }); | |
| return; | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| /** | |
| * @brief Returns a collection of dbus interfaces applicable to Processor's | |
| * subtree. | |
| * | |
| * @param[in] expandLevel Requested expand level. | |
| * | |
| * @return Vector of strings capturing dbus interface names. | |
| */ | |
| inline std::vector<std::string> getInterfacesForProcessorSubtree( | |
| uint8_t expandLevel) { | |
| if (expandLevel > 0) { | |
| std::vector<std::string> processorSubtreeInterfaces = | |
| getInterfacesForSubProcessorSubtree(expandLevel - 1); | |
| processorSubtreeInterfaces.insert(processorSubtreeInterfaces.end(), | |
| processorInterfaces.begin(), | |
| processorInterfaces.end()); | |
| return processorSubtreeInterfaces; | |
| } | |
| return {processorInterfaces.begin(), processorInterfaces.end()}; | |
| } | |
| /** | |
| * @brief Fill out uuid info of a processor by | |
| * requesting data from the given D-Bus object. | |
| * | |
| * @param[in,out] aResp Async HTTP response. | |
| * @param[in] objPath D-Bus object to query. | |
| * @param[in] interface D-Bus interface to query. | |
| * @param[in] jsonPtr json pointer to index the response fields | |
| * @param[in] managedObjects Dbus objects with interfaces and properties. | |
| */ | |
| inline void getProcessorUUID( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, const std::string& objPath, | |
| const std::string& interface, const nlohmann::json::json_pointer& jsonPtr, | |
| const dbus::utility::ManagedObjectType& managedObjects) { | |
| BMCWEB_LOG_DEBUG << "Get Processor UUID"; | |
| dbus::utility::DBusPropertiesMap properties; | |
| dbus_utils::getPropertiesFromManagedObjects(managedObjects, objPath, | |
| interface, properties); | |
| const std::string* property = nullptr; | |
| const bool success = sdbusplus::unpackPropertiesNoThrow( | |
| dbus_utils::UnpackErrorPrinter(), properties, "UUID", property); | |
| if (!success) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| if (property != nullptr) { | |
| aResp->res.jsonValue[jsonPtr]["UUID"] = *property; | |
| } | |
| } | |
| /** | |
| * @brief Populates Cpu information using given dbus properties. | |
| * | |
| * @param[in] aResp Async HTTP response. | |
| * @param[in] jsonPtr json pointer to index the response fields. | |
| * @param[in] dbusProperties DBusPropertiesMap to parse for Cpu data. | |
| * | |
| * @return void | |
| */ | |
| inline void getCpuDataFromDbusProperties( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const nlohmann::json::json_pointer& jsonPtr, | |
| const dbus::utility::DBusPropertiesMap& dbusProperties) { | |
| BMCWEB_LOG_DEBUG << "Get CPU resources from dbus properties."; | |
| // Set the default value of state | |
| aResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Enabled"; | |
| aResp->res.jsonValue[jsonPtr]["Status"]["Health"] = "OK"; | |
| for (const auto& property : dbusProperties) { | |
| if (property.first == "Present") { | |
| const bool* cpuPresent = std::get_if<bool>(&property.second); | |
| if (cpuPresent == nullptr) { | |
| // Important property not in desired type | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| if (!*cpuPresent) { | |
| // Slot is not populated | |
| aResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Absent"; | |
| } | |
| } else if (property.first == "Functional") { | |
| const bool* cpuFunctional = std::get_if<bool>(&property.second); | |
| if (cpuFunctional == nullptr) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| if (!*cpuFunctional) { | |
| aResp->res.jsonValue[jsonPtr]["Status"]["Health"] = "Critical"; | |
| } | |
| } else if (property.first == "CoreCount") { | |
| const uint16_t* coresCount = std::get_if<uint16_t>(&property.second); | |
| if (coresCount == nullptr) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| aResp->res.jsonValue[jsonPtr]["TotalCores"] = *coresCount; | |
| } else if (property.first == "MaxSpeedInMhz") { | |
| const uint32_t* value = std::get_if<uint32_t>(&property.second); | |
| if (value != nullptr) { | |
| aResp->res.jsonValue[jsonPtr]["MaxSpeedMHz"] = *value; | |
| } | |
| } else if (property.first == "Socket") { | |
| const std::string* value = std::get_if<std::string>(&property.second); | |
| if (value != nullptr) { | |
| aResp->res.jsonValue[jsonPtr]["Socket"] = *value; | |
| } | |
| } else if (property.first == "ThreadCount") { | |
| const uint16_t* value = std::get_if<uint16_t>(&property.second); | |
| if (value != nullptr) { | |
| aResp->res.jsonValue[jsonPtr]["TotalThreads"] = *value; | |
| } | |
| } else if (property.first == "CoreEnable") { | |
| const uint16_t* value = std::get_if<uint16_t>(&property.second); | |
| if (value != nullptr) { | |
| aResp->res.jsonValue[jsonPtr]["TotalEnabledCores"] = *value; | |
| } | |
| } else if (property.first == "EffectiveFamily") { | |
| const uint16_t* value = std::get_if<uint16_t>(&property.second); | |
| if (value != nullptr && *value != 2) { | |
| aResp->res.jsonValue[jsonPtr]["ProcessorId"]["EffectiveFamily"] = | |
| "0x" + intToHexString(*value, 4); | |
| } | |
| } else if (property.first == "EffectiveModel") { | |
| const uint16_t* value = std::get_if<uint16_t>(&property.second); | |
| if (value == nullptr) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| if (*value != 0) { | |
| aResp->res.jsonValue[jsonPtr]["ProcessorId"]["EffectiveModel"] = | |
| "0x" + intToHexString(*value, 4); | |
| } | |
| } else if (property.first == "Id") { | |
| const uint64_t* value = std::get_if<uint64_t>(&property.second); | |
| if (value != nullptr && *value != 0) { | |
| aResp->res | |
| .jsonValue[jsonPtr]["ProcessorId"]["IdentificationRegisters"] = | |
| "0x" + intToHexString(*value, 16); | |
| } | |
| } else if (property.first == "Microcode") { | |
| const uint32_t* value = std::get_if<uint32_t>(&property.second); | |
| if (value == nullptr) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| if (*value != 0) { | |
| aResp->res.jsonValue[jsonPtr]["ProcessorId"]["MicrocodeInfo"] = | |
| "0x" + intToHexString(*value, 8); | |
| } | |
| } else if (property.first == "Step") { | |
| const uint16_t* value = std::get_if<uint16_t>(&property.second); | |
| if (value == nullptr) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| if (*value != std::numeric_limits<uint16_t>::max()) { | |
| aResp->res.jsonValue[jsonPtr]["ProcessorId"]["Step"] = | |
| "0x" + intToHexString(*value, 4); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * @brief Populates Cpu information using given dbus Managed Objects. | |
| * | |
| * @param[in] aResp Async HTTP response. | |
| * @param[in] cpuId Cpu identifier. | |
| * @param[in] objPath Cpu dbus object path. | |
| * @param[in] interface Cpu dbus interface name. | |
| * @param[in] jsonPtr json pointer to index the response fields. | |
| * @param[in] dbusData Dbus Managed Objects to parse for Cpu data. | |
| * | |
| * @return void | |
| */ | |
| inline void getCpuData(const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& cpuId, const std::string& objPath, | |
| const std::string& interface, | |
| const nlohmann::json::json_pointer& jsonPtr, | |
| const dbus::utility::ManagedObjectType& dbusData) { | |
| BMCWEB_LOG_DEBUG << "Get Cpu data from managed objects for object " | |
| << objPath; | |
| aResp->res.jsonValue[jsonPtr]["Id"] = cpuId; | |
| aResp->res.jsonValue[jsonPtr]["Name"] = "Processor"; | |
| aResp->res.jsonValue[jsonPtr]["ProcessorType"] = "CPU"; | |
| bool slotPresent = false; | |
| std::string corePath = objPath + "/core"; | |
| size_t totalCores = 0; | |
| for (const auto& object : dbusData) { | |
| dbus::utility::DBusPropertiesMap properties; | |
| if (object.first.str == objPath) { | |
| dbus_utils::getPropertiesFromManagedObjects(dbusData, objPath, interface, | |
| properties); | |
| getCpuDataFromDbusProperties(aResp, jsonPtr, properties); | |
| #ifdef HEALTH_POPULATE | |
| auto health = std::make_shared<HealthPopulate>( | |
| aResp, "/redfish/v1/Systems/system/Processors/" + cpuId, objPath, | |
| "Processor", cpuId); | |
| #endif /* HEALTH_POPULATE */ | |
| } else if (object.first.str.starts_with(corePath)) { | |
| dbus_utils::getPropertiesFromManagedObjects( | |
| dbusData, object.first.str, "xyz.openbmc_project.Inventory.Item", | |
| properties); | |
| for (const auto& property : properties) { | |
| if (property.first != "Present") { | |
| continue; | |
| } | |
| const bool* present = std::get_if<bool>(&property.second); | |
| if (present != nullptr && *present) { | |
| slotPresent = true; | |
| totalCores++; | |
| } | |
| } | |
| } | |
| } | |
| // In getCpuDataFromDbusProperties(), state and health are set based on the | |
| // present and functional status. If core count is zero, then it has a | |
| // higher precedence. | |
| if (slotPresent) { | |
| if (totalCores == 0) { | |
| // Slot is not populated, set status end return | |
| aResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Absent"; | |
| aResp->res.jsonValue[jsonPtr]["Status"]["Health"] = "OK"; | |
| } | |
| aResp->res.jsonValue[jsonPtr]["TotalCores"] = totalCores; | |
| } | |
| } | |
| /** | |
| * @brief Populates Cpu asset information using given dbus Managed Objects. | |
| * | |
| * @param[in] aResp Async HTTP response. | |
| * @param[in] objPath Cpu dbus object path. | |
| * @param[in] interface Cpu dbus interface name. | |
| * @param[in] jsonPtr json pointer to index the response fields. | |
| * @param[in] managedObjects Dbus Managed Objects to parse for Cpu data. | |
| * | |
| * @return void | |
| */ | |
| inline void getCpuAssetData( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, const std::string& objPath, | |
| const std::string& interface, const nlohmann::json::json_pointer& jsonPtr, | |
| const dbus::utility::ManagedObjectType& managedObjects) { | |
| BMCWEB_LOG_DEBUG << "Get Cpu Asset Data"; | |
| const std::string* serialNumber = nullptr; | |
| const std::string* model = nullptr; | |
| const std::string* manufacturer = nullptr; | |
| const std::string* partNumber = nullptr; | |
| const std::string* sparePartNumber = nullptr; | |
| dbus::utility::DBusPropertiesMap properties; | |
| dbus_utils::getPropertiesFromManagedObjects(managedObjects, objPath, | |
| interface, properties); | |
| const bool success = sdbusplus::unpackPropertiesNoThrow( | |
| dbus_utils::UnpackErrorPrinter(), properties, "SerialNumber", | |
| serialNumber, "Model", model, "Manufacturer", manufacturer, "PartNumber", | |
| partNumber, "SparePartNumber", sparePartNumber); | |
| if (!success) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| if (serialNumber != nullptr && !serialNumber->empty()) { | |
| aResp->res.jsonValue[jsonPtr]["SerialNumber"] = *serialNumber; | |
| } | |
| if ((model != nullptr) && !model->empty()) { | |
| aResp->res.jsonValue[jsonPtr]["Model"] = *model; | |
| } | |
| if (manufacturer != nullptr) { | |
| aResp->res.jsonValue[jsonPtr]["Manufacturer"] = *manufacturer; | |
| // Otherwise would be unexpected. | |
| if (manufacturer->find("Intel") != std::string::npos) { | |
| aResp->res.jsonValue[jsonPtr]["ProcessorArchitecture"] = "x86"; | |
| aResp->res.jsonValue[jsonPtr]["InstructionSet"] = "x86-64"; | |
| } else if (manufacturer->find("IBM") != std::string::npos) { | |
| aResp->res.jsonValue[jsonPtr]["ProcessorArchitecture"] = "Power"; | |
| aResp->res.jsonValue[jsonPtr]["InstructionSet"] = "PowerISA"; | |
| } | |
| } | |
| if (partNumber != nullptr) { | |
| aResp->res.jsonValue[jsonPtr]["PartNumber"] = *partNumber; | |
| } | |
| if (sparePartNumber != nullptr && !sparePartNumber->empty()) { | |
| aResp->res.jsonValue[jsonPtr]["SparePartNumber"] = *sparePartNumber; | |
| } | |
| } | |
| /** | |
| * @brief Populates Cpu revision data using given dbus Managed Objects. | |
| * | |
| * @param[in] aResp Async HTTP response. | |
| * @param[in] objPath Cpu dbus object path. | |
| * @param[in] interface Cpu dbus interface name. | |
| * @param[in] jsonPtr json pointer to index the response fields. | |
| * @param[in] managedObjects Dbus Managed Objects to parse for Cpu data. | |
| * | |
| * @return void | |
| */ | |
| inline void getCpuRevisionData( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, const std::string& objPath, | |
| const std::string& interface, const nlohmann::json::json_pointer& jsonPtr, | |
| const dbus::utility::ManagedObjectType& managedObjects) { | |
| BMCWEB_LOG_DEBUG << "Get Cpu Revision Data"; | |
| dbus::utility::DBusPropertiesMap properties; | |
| dbus_utils::getPropertiesFromManagedObjects(managedObjects, objPath, | |
| interface, properties); | |
| const std::string* version = nullptr; | |
| const bool success = sdbusplus::unpackPropertiesNoThrow( | |
| dbus_utils::UnpackErrorPrinter(), properties, "Version", version); | |
| if (!success) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| if (version != nullptr) { | |
| aResp->res.jsonValue[jsonPtr]["Version"] = *version; | |
| } | |
| } | |
| /** | |
| * @brief Populates accelerator data using given dbus Managed Objects. | |
| * | |
| * @param[in] aResp Async HTTP response. | |
| * @param[in] acclrtrId Accelerator identifier. | |
| * @param[in] objPath Accelerator dbus object path. | |
| * @param[in] interface Accelerator dbus interface name. | |
| * @param[in] jsonPtr json pointer to index the response fields. | |
| * @param[in] managedObjects Dbus Managed Objects to parse. | |
| * | |
| * @return void | |
| */ | |
| inline void getAcceleratorData( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& acclrtrId, const std::string& objPath, | |
| const std::string& interface, const nlohmann::json::json_pointer& jsonPtr, | |
| const dbus::utility::ManagedObjectType& managedObjects) { | |
| BMCWEB_LOG_DEBUG << "Get available system Accelerator resources by service."; | |
| const bool* functional = nullptr; | |
| const bool* present = nullptr; | |
| dbus::utility::DBusPropertiesMap properties; | |
| dbus_utils::getPropertiesFromManagedObjects(managedObjects, objPath, | |
| interface, properties); | |
| const bool success = sdbusplus::unpackPropertiesNoThrow( | |
| dbus_utils::UnpackErrorPrinter(), properties, "Functional", functional, | |
| "Present", present); | |
| if (!success) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| std::string state = "Enabled"; | |
| std::string health = "OK"; | |
| if (present != nullptr && !*present) { | |
| state = "Absent"; | |
| } | |
| if (functional != nullptr && !*functional) { | |
| if (state == "Enabled") { | |
| health = "Critical"; | |
| } | |
| } | |
| aResp->res.jsonValue[jsonPtr]["Id"] = acclrtrId; | |
| aResp->res.jsonValue[jsonPtr]["Name"] = "Processor"; | |
| aResp->res.jsonValue[jsonPtr]["Status"]["State"] = state; | |
| aResp->res.jsonValue[jsonPtr]["Status"]["Health"] = health; | |
| aResp->res.jsonValue[jsonPtr]["ProcessorType"] = "Accelerator"; | |
| #ifdef HEALTH_POPULATE | |
| auto healthObj = std::make_shared<HealthPopulate>( | |
| aResp, "/redfish/v1/Systems/system/Processors/" + acclrtrId, objPath, | |
| "Processor", acclrtrId); | |
| #endif /* HEALTH_POPULATE */ | |
| } | |
| // OperatingConfig D-Bus Types | |
| using TurboProfileProperty = std::vector<std::tuple<uint32_t, size_t>>; | |
| using BaseSpeedPrioritySettingsProperty = | |
| std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>; | |
| // uint32_t and size_t may or may not be the same type, requiring a dedup'd | |
| // variant | |
| /** | |
| * Fill out the HighSpeedCoreIDs in a Processor resource from the given | |
| * OperatingConfig D-Bus property. | |
| * | |
| * @param[in,out] aResp Async HTTP response. | |
| * @param[in] jsonPtr json pointer to index the response fields | |
| * @param[in] baseSpeedSettings Full list of base speed priority groups, | |
| * to use to determine the list of high | |
| * speed cores. | |
| */ | |
| inline void highSpeedCoreIdsHandler( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const nlohmann::json::json_pointer& jsonPtr, | |
| const BaseSpeedPrioritySettingsProperty& baseSpeedSettings) { | |
| // The D-Bus property does not indicate which bucket is the "high | |
| // priority" group, so let's discern that by looking for the one with | |
| // highest base frequency. | |
| auto highPriorityGroup = baseSpeedSettings.cend(); | |
| uint32_t highestBaseSpeed = 0; | |
| for (auto it = baseSpeedSettings.cbegin(); it != baseSpeedSettings.cend(); | |
| ++it) { | |
| const uint32_t baseFreq = std::get<uint32_t>(*it); | |
| if (baseFreq > highestBaseSpeed) { | |
| highestBaseSpeed = baseFreq; | |
| highPriorityGroup = it; | |
| } | |
| } | |
| nlohmann::json& jsonCoreIds = | |
| aResp->res.jsonValue[jsonPtr]["HighSpeedCoreIDs"]; | |
| jsonCoreIds = nlohmann::json::array(); | |
| // There may not be any entries in the D-Bus property, so only populate | |
| // if there was actually something there. | |
| if (highPriorityGroup != baseSpeedSettings.cend()) { | |
| jsonCoreIds = std::get<std::vector<uint32_t>>(*highPriorityGroup); | |
| } | |
| } | |
| /** | |
| * Fill out OperatingConfig related items in a Processor resource by requesting | |
| * data from the given D-Bus object. | |
| * | |
| * @param[in,out] aResp Async HTTP response. | |
| * @param[in] cpuId CPU D-Bus name. | |
| * @param[in] objPath D-Bus object to query. | |
| * @param[in] interface D-Bus interface to query. | |
| * @param[in] jsonPtr json pointer to index the response fields | |
| * @param[in] managedObjects Dbus objects with interfaces and properties. | |
| */ | |
| inline void getCpuConfigData( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, const std::string& cpuId, | |
| const std::string& objPath, const std::string& interface, | |
| const nlohmann::json::json_pointer& jsonPtr, | |
| const dbus::utility::ManagedObjectType& managedObjects) { | |
| BMCWEB_LOG_INFO << "Getting CPU operating configs for " << cpuId; | |
| nlohmann::json& json = aResp->res.jsonValue; | |
| const sdbusplus::message::object_path* appliedConfig = nullptr; | |
| const bool* baseSpeedPriorityEnabled = nullptr; | |
| const BaseSpeedPrioritySettingsProperty* baseSpeedList = nullptr; | |
| dbus::utility::DBusPropertiesMap properties; | |
| dbus_utils::getPropertiesFromManagedObjects(managedObjects, objPath, | |
| interface, properties); | |
| const bool success = sdbusplus::unpackPropertiesNoThrow( | |
| dbus_utils::UnpackErrorPrinter(), properties, "AppliedConfig", | |
| appliedConfig, "BaseSpeedPriorityEnabled", baseSpeedPriorityEnabled); | |
| if (!success) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| if (appliedConfig != nullptr) { | |
| const std::string& dbusPath = appliedConfig->str; | |
| std::string uri = | |
| "/redfish/v1/Systems/system/Processors/" + cpuId + "/OperatingConfigs"; | |
| nlohmann::json::object_t operatingConfig; | |
| operatingConfig["@odata.id"] = uri; | |
| json[jsonPtr]["OperatingConfigs"] = std::move(operatingConfig); | |
| // Reuse the D-Bus config object name for the Redfish | |
| // URI | |
| size_t baseNamePos = dbusPath.rfind('/'); | |
| if (baseNamePos == std::string::npos || | |
| baseNamePos == (dbusPath.size() - 1)) { | |
| // If the AppliedConfig was somehow not a valid path, | |
| // skip adding any more properties, since everything | |
| // else is tied to this applied config. | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| uri += '/'; | |
| uri += dbusPath.substr(baseNamePos + 1); | |
| nlohmann::json::object_t appliedOperatingConfig; | |
| appliedOperatingConfig["@odata.id"] = uri; | |
| json[jsonPtr]["AppliedOperatingConfig"] = std::move(appliedOperatingConfig); | |
| // Once we found the current applied config, read the base freq core ids | |
| // out of Operating config. | |
| dbus::utility::DBusPropertiesMap config; | |
| dbus_utils::getPropertiesFromManagedObjects( | |
| managedObjects, dbusPath, | |
| "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig", config); | |
| const bool parseConfigSuccess = sdbusplus::unpackPropertiesNoThrow( | |
| dbus_utils::UnpackErrorPrinter(), config, "BaseSpeedPrioritySettings", | |
| baseSpeedList); | |
| if (!parseConfigSuccess) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| if (baseSpeedList != nullptr) { | |
| highSpeedCoreIdsHandler(aResp, jsonPtr, *baseSpeedList); | |
| } | |
| } | |
| if (baseSpeedPriorityEnabled != nullptr) { | |
| json[jsonPtr]["BaseSpeedPriorityState"] = | |
| *baseSpeedPriorityEnabled ? "Enabled" : "Disabled"; | |
| } | |
| } | |
| /** | |
| * Populate the unique identifier in a Processor resource by requesting data | |
| * from the given D-Bus object. | |
| * | |
| * @param[in,out] aResp Async HTTP response. | |
| * @param[in] objPath D-Bus object to query. | |
| * @param[in] interface D-Bus interface to query. | |
| * @param[in] jsonPtr json pointer to index the response fields | |
| * @param[in] managedObjects Dbus objects with interfaces and properties. | |
| */ | |
| inline void getCpuUniqueId( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& objectPath, const std::string& interface, | |
| const nlohmann::json::json_pointer& jsonPtr, | |
| const dbus::utility::ManagedObjectType& managedObjects) { | |
| BMCWEB_LOG_DEBUG << "Get CPU UniqueIdentifier"; | |
| dbus::utility::DBusPropertiesMap properties; | |
| dbus_utils::getPropertiesFromManagedObjects(managedObjects, objectPath, | |
| interface, properties); | |
| const std::string* id = nullptr; | |
| const bool success = sdbusplus::unpackPropertiesNoThrow( | |
| dbus_utils::UnpackErrorPrinter(), properties, "UniqueIdentifier", id); | |
| if (!success) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| if (id != nullptr) { | |
| aResp->res | |
| .jsonValue[jsonPtr]["ProcessorId"]["ProtectedIdentificationNumber"] = | |
| *id; | |
| } | |
| } | |
| inline void getCpuChassisAssociation( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& processorId, const std::string& objectPath, | |
| const nlohmann::json::json_pointer& jsonPtr) { | |
| BMCWEB_LOG_DEBUG << "Get CPU -- Chassis association"; | |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); | |
| dbus_utils::getProperty<std::vector<std::string>>( | |
| "xyz.openbmc_project.ObjectMapper", objectPath + "/chassis", | |
| "xyz.openbmc_project.Association", "endpoints", requestContext, | |
| [asyncResp{asyncResp}, jsonPtr, processorId]( | |
| const boost::system::error_code ec, | |
| const std::vector<std::string>& chassisList) { | |
| if (ec) { | |
| return; | |
| } | |
| if (chassisList.empty()) { | |
| return; | |
| } | |
| if (chassisList.size() > 1) { | |
| BMCWEB_LOG_DEBUG << processorId | |
| << " is associated with mutliple chassis"; | |
| return; | |
| } | |
| sdbusplus::message::object_path chassisPath(chassisList[0]); | |
| std::string chassisName = chassisPath.filename(); | |
| if (chassisName.empty()) { | |
| BMCWEB_LOG_ERROR << "filename() is empty in " << chassisPath.str; | |
| return; | |
| } | |
| asyncResp->res.jsonValue[jsonPtr]["Links"]["Chassis"] = { | |
| {"@odata.id", "/redfish/v1/Chassis/" + chassisName}}; | |
| }); | |
| } | |
| /** | |
| * @brief Fill out the links to associated memory of a processor | |
| * by looking up its D-Bus association. | |
| * | |
| * @param[in,out] aResp Async HTTP response. | |
| * @param[in] objPath D-Bus object to query. | |
| */ | |
| inline void getMemoryLinks(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& objectPath, | |
| const nlohmann::json::json_pointer& jsonPtr) { | |
| BMCWEB_LOG_DEBUG << "Get Processor -- Memory association"; | |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); | |
| dbus_utils::getProperty<std::vector<std::string>>( | |
| "xyz.openbmc_project.ObjectMapper", objectPath + "/memory", | |
| "xyz.openbmc_project.Association", "endpoints", requestContext, | |
| [asyncResp, jsonPtr](const boost::system::error_code ec, | |
| const std::vector<std::string>& memoryList) { | |
| if (ec) { | |
| return; | |
| } | |
| if (memoryList.empty()) { | |
| return; | |
| } | |
| nlohmann::json::array_t memoryLinks; | |
| for (const std::string& memory : memoryList) { | |
| sdbusplus::message::object_path memoryPath(memory); | |
| const std::string memoryName = memoryPath.filename(); | |
| if (memoryName.empty()) { | |
| BMCWEB_LOG_ERROR << "filename() is empty in " << memoryPath.str; | |
| return; | |
| } | |
| nlohmann::json::object_t memoryObject; | |
| memoryObject["@odata.id"] = | |
| "/redfish/v1/Systems/system/Memory/" + memoryName; | |
| memoryLinks.emplace_back(memoryObject); | |
| } | |
| asyncResp->res.jsonValue[jsonPtr]["Links"]["Memory"] = | |
| std::move(memoryLinks); | |
| }); | |
| } | |
| /** | |
| * Find the D-Bus object representing the requested Processor, and call the | |
| * handler with the results. If matching object is not found, add 404 error to | |
| * response and don't call the handler. | |
| * | |
| * @param[in,out] resp Async HTTP response. | |
| * @param[in] processorId Redfish Processor Id. | |
| * @param[in] handler Callback to continue processing request upon | |
| * successfully finding object. | |
| */ | |
| template <typename Handler> | |
| inline void getProcessorObject(const std::shared_ptr<bmcweb::AsyncResp>& resp, | |
| const std::string& processorId, | |
| const std::string& systemPath, | |
| Handler&& handler) { | |
| BMCWEB_LOG_DEBUG << "Get available system processor resources."; | |
| const bool multiHost = !systemPath.empty(); | |
| // Default systemName is system and change if system is multi-host | |
| std::string systemName = "system"; | |
| // The subtree to query for Dimms changes if single or multi-host | |
| std::string subtreeRoot = "/xyz/openbmc_project/inventory"; | |
| if (multiHost) { | |
| systemName = sdbusplus::message::object_path(systemPath).filename(); | |
| subtreeRoot = systemPath; | |
| } | |
| managedStore::ManagedObjectStoreContext context(resp); | |
| managedStore::GetManagedObjectStore()->getSubTree( | |
| subtreeRoot, 0, processorInterfaces, context, | |
| [resp{resp}, processorId, multiHost, systemPath, | |
| handler = std::forward<Handler>(handler), context]( | |
| const boost::system::error_code& ec, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree) mutable { | |
| if (ec) { | |
| BMCWEB_LOG_ERROR << "DBUS response error: " << ec.value(); | |
| messages::internalError(resp->res); | |
| return; | |
| } | |
| std::vector<std::string> serviceNames = | |
| dbus_utils::getServiceNamesFromSubtree(subtree); | |
| if (serviceNames.empty()) { | |
| BMCWEB_LOG_ERROR << "Service list is empty"; | |
| messages::internalError(resp->res); | |
| return; | |
| } | |
| sdbusplus::message::object_path path("/xyz/openbmc_project/inventory"); | |
| dbus_utils::getManagedObjectsInEachService( | |
| std::move(serviceNames), path, context, | |
| [resp{std::move(resp)}, subtree, processorId, multiHost, systemPath, | |
| handler = std::forward<Handler>(handler)]( | |
| const ManagedObjectByServiceMap& managedObjectsByService) { | |
| if (managedObjectsByService.empty()) { | |
| BMCWEB_LOG_ERROR | |
| << "GetManagedObjects does not return any object."; | |
| messages::internalError(resp->res); | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG << "Looping through subtree"; | |
| for (const auto& [objectPath, serviceMap] : subtree) { | |
| BMCWEB_LOG_DEBUG << objectPath; | |
| if (multiHost && !objectPath.starts_with(systemPath)) { | |
| continue; | |
| } | |
| // Ignore any objects which don't end with our desired cpu name | |
| if (!objectPath.ends_with(processorId)) { | |
| continue; | |
| } | |
| // Filter out objects that don't have the CPU-specific | |
| // interfaces to make sure we can return 404 on non-CPUs | |
| // (e.g. /redfish/../Processors/dimm0) | |
| if (!dbus_utils::findInterfacesInServiceMap( | |
| serviceMap, processorInterfaces)) { | |
| continue; | |
| } | |
| handler(objectPath, subtree, managedObjectsByService); | |
| return; | |
| } | |
| messages::resourceNotFound(resp->res, "Processor", processorId); | |
| }); | |
| }); | |
| } | |
| inline void getProcessorState( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& service, const std::string& objPath, | |
| const nlohmann::json::json_pointer& jsonPtr) { | |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); | |
| dbus_utils::getProperty<bool>( | |
| service, objPath, "xyz.openbmc_project.Inventory.Item", "Present", | |
| requestContext, | |
| [asyncResp, objPath, jsonPtr](const boost::system::error_code& ec, | |
| bool present) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "get presence failed for Processor State " | |
| << objPath << " with error " << ec; | |
| if (ec.value() != EBADR) { | |
| messages::internalError(asyncResp->res); | |
| } | |
| return; | |
| } | |
| asyncResp->res.jsonValue[jsonPtr]["Status"]["State"] = | |
| present ? "Enabled" : "Absent"; | |
| }); | |
| } | |
| inline void getProcessorStateData( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName, const std::string& processorId, | |
| const std::string& procStateId, | |
| const nlohmann::json::json_pointer& jsonPtr) { | |
| BMCWEB_LOG_DEBUG << "Processor State Id: " << procStateId; | |
| asyncResp->res.jsonValue[jsonPtr]["@odata.id"] = crow::utility::urlFromPieces( | |
| "redfish", "v1", "Systems", systemName, "Processors", processorId, "Oem", | |
| "Google", "ProcessorStates", procStateId); | |
| asyncResp->res.jsonValue[jsonPtr]["@odata.type"] = | |
| "#GoogleProcessorState.GoogleProcessorState"; | |
| asyncResp->res.jsonValue[jsonPtr]["Id"] = procStateId; | |
| asyncResp->res.jsonValue[jsonPtr]["Name"] = "Processor"; | |
| getProcessorState(asyncResp, "xyz.openbmc_project.EntityManager", | |
| absl::StrCat("/xyz/openbmc_project/inventory/", systemName, | |
| "/processorstate/", procStateId), | |
| jsonPtr); | |
| } | |
| inline void handleGetProcessorState( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName, const std::string& processorId, | |
| const std::string& procStateId) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| getProcessorStateData(asyncResp, systemName, processorId, procStateId, | |
| ""_json_pointer); | |
| } | |
| using Mapper = dbus::utility::MapperGetSubTreePathsResponse; | |
| inline void getProcessorStateLinksOrExpand( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& processorId, const nlohmann::json::json_pointer& jsonPtr, | |
| const std::string& systemName, uint8_t expandLevel, | |
| const boost::system::error_code& ec, const Mapper& procStatePaths) { | |
| BMCWEB_LOG_DEBUG << "Get Processor State URL"; | |
| if (ec) { | |
| BMCWEB_LOG_ERROR | |
| << "ProcessorState Collection GetSubTreePaths handler Dbus error " | |
| << ec; | |
| return; | |
| } | |
| nlohmann::json::array_t procStateLinks; | |
| size_t stateCount = 0; | |
| for (const std::string& procState : procStatePaths) { | |
| std::string procStateId = | |
| sdbusplus::message::object_path(procState).filename(); | |
| nlohmann::json::json_pointer processorStatePtr = | |
| jsonPtr / "Oem" / "Google" / "ProcessorStates" / stateCount++; | |
| if (expandLevel > 0) { | |
| getProcessorStateData(aResp, systemName, processorId, procStateId, | |
| processorStatePtr); | |
| } else { | |
| nlohmann::json::object_t procStateObj; | |
| aResp->res.jsonValue[processorStatePtr]["@odata.id"] = | |
| crow::utility::urlFromPieces( | |
| "redfish", "v1", "Systems", systemName, "Processors", processorId, | |
| "Oem", "Google", "ProcessorStates", procStateId); | |
| } | |
| } | |
| } | |
| inline void getProcessorData( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const nlohmann::json::json_pointer& jsonPtr, uint8_t expandLevel, | |
| const std::string& processorId, const std::string& systemPath, | |
| const std::string& objectPath, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree, | |
| const ManagedObjectByServiceMap& managedObjectsMap) { | |
| BMCWEB_LOG_DEBUG << "Get processor data."; | |
| const bool multiHost = !systemPath.empty(); | |
| // Default systemName is system and change if system is multi-host | |
| std::string systemName = "system"; | |
| if (multiHost) { | |
| systemName = sdbusplus::message::object_path(systemPath).filename(); | |
| } | |
| aResp->res.addHeader( | |
| boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); | |
| aResp->res.jsonValue[jsonPtr]["@odata.type"] = "#Processor.v1_11_0.Processor"; | |
| aResp->res.jsonValue[jsonPtr]["@odata.id"] = crow::utility::urlFromPieces( | |
| "redfish", "v1", "Systems", systemName, "Processors", processorId); | |
| // Get service to interface map for cpu | |
| dbus::utility::MapperServiceMap serviceMap; | |
| for (const auto& [parsedObjectPath, parsedServiceMap] : subtree) { | |
| if (objectPath == parsedObjectPath) { | |
| serviceMap = parsedServiceMap; | |
| break; | |
| } | |
| } | |
| for (const auto& [serviceName, interfaceList] : serviceMap) { | |
| auto iter = managedObjectsMap.find(serviceName); | |
| if (iter == managedObjectsMap.end()) { | |
| BMCWEB_LOG_DEBUG << "Cpu data not available for serviceName " | |
| << serviceName; | |
| continue; | |
| } | |
| BMCWEB_LOG_DEBUG << "Populate processor data from dbus interfaces."; | |
| for (const auto& interface : interfaceList) { | |
| if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset") { | |
| getCpuAssetData(aResp, objectPath, interface, jsonPtr, iter->second); | |
| } else if (interface == | |
| "xyz.openbmc_project.Inventory.Decorator.Revision") { | |
| getCpuRevisionData(aResp, objectPath, interface, jsonPtr, iter->second); | |
| } else if (interface == "xyz.openbmc_project.Inventory.Item.Cpu") { | |
| getCpuData(aResp, processorId, objectPath, interface, jsonPtr, | |
| iter->second); | |
| } else if (interface == | |
| "xyz.openbmc_project.Inventory.Item.Accelerator") { | |
| getAcceleratorData(aResp, processorId, objectPath, interface, jsonPtr, | |
| iter->second); | |
| } else if (interface == | |
| "xyz.openbmc_project.Control.Processor." | |
| "CurrentOperatingConfig") { | |
| getCpuConfigData(aResp, processorId, objectPath, interface, jsonPtr, | |
| iter->second); | |
| } else if (interface == | |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode") { | |
| location_util::getLocationCodeFromManagedObject( | |
| aResp, objectPath, interface, jsonPtr / "Location", iter->second); | |
| location_util::getPartLocationContext(aResp, jsonPtr / "Location", | |
| objectPath + "/chassis"); | |
| } else if (interface == "xyz.openbmc_project.Common.UUID") { | |
| getProcessorUUID(aResp, objectPath, interface, jsonPtr, iter->second); | |
| } else if (interface == | |
| "xyz.openbmc_project.Inventory.Decorator.UniqueIdentifier") { | |
| getCpuUniqueId(aResp, objectPath, interface, jsonPtr, iter->second); | |
| } else { | |
| std::optional<std::string> locationType = | |
| location_util::getLocationType(interface); | |
| if (locationType == std::nullopt) { | |
| BMCWEB_LOG_DEBUG << "getLocationType for Processor failed for " | |
| << interface; | |
| continue; | |
| } | |
| aResp->res | |
| .jsonValue[jsonPtr]["Location"]["PartLocation"]["LocationType"] = | |
| *locationType; | |
| } | |
| } | |
| } | |
| getProcessorMaxTDP(aResp, objectPath, jsonPtr, systemName); | |
| getCpuChassisAssociation(aResp, processorId, objectPath, jsonPtr); | |
| getMemoryLinks(aResp, objectPath, jsonPtr); | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| constexpr std::array<std::string_view, 1> procStateIntf = { | |
| "xyz.openbmc_project.Inventory.Item.ProcessorState"}; | |
| dbus_utils::getSubTreePaths( | |
| "/xyz/openbmc_project/inventory", 0, procStateIntf, context, | |
| std::bind_front(getProcessorStateLinksOrExpand, aResp, processorId, | |
| jsonPtr, systemName, expandLevel)); | |
| if (expandLevel > 0) { | |
| nlohmann::json::json_pointer subProcessorPtr = jsonPtr / "SubProcessors"; | |
| getSubProcessorCoreCollectionWithExpand( | |
| aResp, subProcessorPtr, expandLevel - 1, systemName, processorId, | |
| objectPath, subtree, managedObjectsMap); | |
| } else { | |
| aResp->res.jsonValue[jsonPtr]["SubProcessors"]["@odata.id"] = | |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", systemName, | |
| "Processors", processorId, | |
| "SubProcessors"); | |
| } | |
| if (!multiHost) { | |
| if (expandLevel > 0) { | |
| nlohmann::json::json_pointer metricsPtr = jsonPtr / "Metrics"; | |
| handleGetCpuMetrics(aResp, metricsPtr, systemName, processorId); | |
| } else { | |
| // Inserting the "Metrics" JSON stanza | |
| std::string metricsUrl = aResp->res.jsonValue[jsonPtr]["@odata.id"]; | |
| absl::StrAppend(&metricsUrl, "/ProcessorMetrics"); | |
| nlohmann::json::object_t metricsObj; | |
| metricsObj["@odata.id"] = std::move(metricsUrl); | |
| aResp->res.jsonValue[jsonPtr]["Metrics"] = std::move(metricsObj); | |
| } | |
| } | |
| } | |
| template <typename Handler> | |
| inline void getProcessorPaths(const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& systemPath, | |
| const std::string& processorId, | |
| Handler&& handler) { | |
| std::array<std::string_view, 1> interfaces{ | |
| "xyz.openbmc_project.Inventory.Item.Cpu"}; | |
| std::string subtreeRoot = "/xyz/openbmc_project/inventory"; | |
| if (!systemPath.empty()) { | |
| subtreeRoot = systemPath; | |
| } | |
| managedStore::ManagedObjectStoreContext requestContext(aResp); | |
| managedStore::GetManagedObjectStore()->getSubTreePaths( | |
| subtreeRoot, 0, interfaces, requestContext, | |
| [processorId, aResp{aResp}, handler{std::forward<Handler>(handler)}]( | |
| const boost::system::error_code ec, | |
| const std::vector<std::string>& subTreePaths) { | |
| if (ec) { | |
| handler(ec, ""); | |
| return; | |
| } | |
| for (const std::string& cpuPath : subTreePaths) { | |
| if (sdbusplus::message::object_path(cpuPath).filename() != | |
| processorId) { | |
| continue; | |
| } | |
| handler(ec, cpuPath); | |
| return; | |
| } | |
| // Set an error code since valid cpu path is not found | |
| handler(boost::system::errc::make_error_code( | |
| boost::system::errc::no_such_file_or_directory), | |
| ""); | |
| }); | |
| } | |
| template <typename Handler> | |
| inline void getSubProcessorCorePaths( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& systemPath, const std::string& processorId, | |
| const std::string& coreId, Handler&& handler) { | |
| std::array<std::string_view, 1> interfaces{ | |
| "xyz.openbmc_project.Inventory.Item.Cpu"}; | |
| std::string subtreeRoot = "/xyz/openbmc_project/inventory"; | |
| if (!systemPath.empty()) { | |
| subtreeRoot = systemPath; | |
| } | |
| managedStore::ManagedObjectStoreContext requestContext(aResp); | |
| managedStore::GetManagedObjectStore()->getSubTreePaths( | |
| subtreeRoot, 0, interfaces, requestContext, | |
| [processorId, coreId, aResp{aResp}, | |
| handler{std::forward<Handler>(handler)}, subtreeRoot, | |
| requestContext](const boost::system::error_code ec, | |
| const std::vector<std::string>& subTreeCpuPaths) { | |
| if (ec) { | |
| handler(ec, ""); | |
| return; | |
| } | |
| for (const std::string& cpuPath : subTreeCpuPaths) { | |
| if (sdbusplus::message::object_path(cpuPath).filename() != | |
| processorId) { | |
| continue; | |
| } | |
| std::array<std::string_view, 1> interfaces{ | |
| "xyz.openbmc_project.Inventory.Item.CpuCore"}; | |
| managedStore::GetManagedObjectStore()->getSubTreePaths( | |
| subtreeRoot, 0, interfaces, requestContext, | |
| [processorId, coreId, aResp{aResp}, handler]( | |
| const boost::system::error_code ec2, | |
| const std::vector<std::string>& subTreeCorePaths) { | |
| if (ec2) { | |
| handler(ec2, ""); | |
| return; | |
| } | |
| for (const std::string& corePath : subTreeCorePaths) { | |
| if (sdbusplus::message::object_path(corePath).filename() != | |
| coreId) { | |
| continue; | |
| } | |
| handler(ec2, corePath); | |
| return; | |
| } | |
| // Set an error code since valid processor core path is not | |
| // found | |
| handler(boost::system::errc::make_error_code( | |
| boost::system::errc::no_such_file_or_directory), | |
| ""); | |
| }); | |
| return; | |
| } | |
| // Set an error code since valid processor cpu path is not found | |
| handler(boost::system::errc::make_error_code( | |
| boost::system::errc::no_such_file_or_directory), | |
| ""); | |
| }); | |
| } | |
| inline void getCoreThreadData( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const nlohmann::json::json_pointer& jsonPtr, | |
| [[maybe_unused]] const uint8_t expandLevel, const std::string& systemName, | |
| const std::string& processorId, const std::string& coreId, | |
| const std::string& threadId, const std::string& threadPath, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree, | |
| const ManagedObjectByServiceMap& managedObjectsMap) { | |
| aResp->res.jsonValue[jsonPtr]["@odata.type"] = "#Processor.v1_11_0.Processor"; | |
| aResp->res.jsonValue[jsonPtr]["@odata.id"] = crow::utility::urlFromPieces( | |
| "redfish", "v1", "Systems", systemName, "Processors", processorId, | |
| "SubProcessors", coreId, "SubProcessors", threadId); | |
| aResp->res.jsonValue[jsonPtr]["Name"] = "SubProcessor"; | |
| aResp->res.jsonValue[jsonPtr]["ProcessorType"] = "Thread"; | |
| aResp->res.jsonValue[jsonPtr]["Id"] = threadId; | |
| aResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Enabled"; | |
| aResp->res.jsonValue[jsonPtr]["Status"]["Health"] = "OK"; | |
| bool present = false; | |
| bool functional = false; | |
| dbus::utility::MapperServiceMap serviceMap; | |
| for (const auto& [parsedObjectPath, parsedServiceMap] : subtree) { | |
| if (threadPath == parsedObjectPath) { | |
| serviceMap = parsedServiceMap; | |
| break; | |
| } | |
| } | |
| for (const auto& [serviceName, interfaceList] : serviceMap) { | |
| auto iter = managedObjectsMap.find(serviceName); | |
| if (iter == managedObjectsMap.end()) { | |
| BMCWEB_LOG_DEBUG << "Thread data not available for serviceName " | |
| << serviceName; | |
| continue; | |
| } | |
| for (const auto& interface : interfaceList) { | |
| dbus::utility::DBusPropertiesMap properties; | |
| if (interface == | |
| "xyz.openbmc_project.State." | |
| "Decorator.OperationalStatus") { | |
| const bool* propertyFunctional = nullptr; | |
| dbus_utils::getPropertiesFromManagedObjects(iter->second, threadPath, | |
| interface, properties); | |
| const bool success = sdbusplus::unpackPropertiesNoThrow( | |
| dbus_utils::UnpackErrorPrinter(), properties, "Functional", | |
| propertyFunctional); | |
| if (!success || propertyFunctional == nullptr) { | |
| continue; | |
| } | |
| functional = *propertyFunctional; | |
| } else if (interface == "xyz.openbmc_project.Inventory.Item") { | |
| const bool* propertyPresent = nullptr; | |
| const std::string* propertyPrettyName = nullptr; | |
| dbus_utils::getPropertiesFromManagedObjects(iter->second, threadPath, | |
| interface, properties); | |
| const bool success = sdbusplus::unpackPropertiesNoThrow( | |
| dbus_utils::UnpackErrorPrinter(), properties, "Present", | |
| propertyPresent, "PrettyName", propertyPrettyName); | |
| if (!success) { | |
| continue; | |
| } | |
| if (propertyPresent != nullptr) { | |
| present = *propertyPresent; | |
| } | |
| if (propertyPrettyName != nullptr) { | |
| aResp->res.jsonValue[jsonPtr]["Name"] = *propertyPrettyName; | |
| } | |
| } else if (interface == "xyz.openbmc_project.Inventory.Item.CpuThread") { | |
| const uint32_t* propertyMicrocode = nullptr; | |
| dbus_utils::getPropertiesFromManagedObjects(iter->second, threadPath, | |
| interface, properties); | |
| const bool success = sdbusplus::unpackPropertiesNoThrow( | |
| dbus_utils::UnpackErrorPrinter(), properties, "Microcode", | |
| propertyMicrocode); | |
| if (!success) { | |
| continue; | |
| } | |
| if (propertyMicrocode != nullptr) { | |
| aResp->res.jsonValue[jsonPtr]["ProcessorId"]["MicrocodeInfo"] = | |
| "0x" + intToHexString(*propertyMicrocode, 8); | |
| } | |
| } | |
| } | |
| } | |
| if (!present) { | |
| aResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Absent"; | |
| } | |
| if (!functional) { | |
| aResp->res.jsonValue[jsonPtr]["Status"]["Health"] = "Critical"; | |
| } | |
| if (systemName == "system") { | |
| if (expandLevel > 0) { | |
| nlohmann::json::json_pointer metricsPtr = jsonPtr / "Metrics"; | |
| handleGetCpuCoreThreadMetrics(aResp, metricsPtr, systemName, processorId, | |
| coreId, threadId); | |
| } else { | |
| // Inserting the "Metrics" JSON stanza | |
| std::string metricsUrl = aResp->res.jsonValue[jsonPtr]["@odata.id"]; | |
| absl::StrAppend(&metricsUrl, "/ProcessorMetrics"); | |
| nlohmann::json::object_t metricsObj; | |
| metricsObj["@odata.id"] = std::move(metricsUrl); | |
| aResp->res.jsonValue[jsonPtr]["Metrics"] = std::move(metricsObj); | |
| } | |
| } | |
| } | |
| inline void getSubProcessorThreadData( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& systemName, const std::string& processorId, | |
| const std::string& coreId, const std::string& threadId, | |
| const boost::system::error_code ec, const std::string& corePath) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error, ec: " << ec.value(); | |
| // No sub processor thread objects found by mapper | |
| if (ec.value() == boost::system::errc::io_error) { | |
| messages::resourceNotFound(aResp->res, "#Processor.v1_11_0.Processor", | |
| coreId); | |
| return; | |
| } | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| aResp->res.addHeader( | |
| boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| managedStore::GetManagedObjectStore()->getSubTree( | |
| corePath, 0, subProcessorThreadInterfaces, context, | |
| [aResp{aResp}, systemName, processorId, coreId, threadId, context]( | |
| const boost::system::error_code& ec2, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { | |
| if (ec2) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error, ec2: " << ec2.value(); | |
| // No sub processor core objects found by mapper | |
| if (ec2.value() == boost::system::errc::io_error) { | |
| messages::resourceNotFound(aResp->res, | |
| "#Processor.v1_11_0.Processor", coreId); | |
| return; | |
| } | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| std::vector<std::string> serviceNames = | |
| dbus_utils::getServiceNamesFromSubtree(subtree); | |
| sdbusplus::message::object_path path("/xyz/openbmc_project/inventory"); | |
| dbus_utils::getManagedObjectsInEachService( | |
| std::move(serviceNames), path, context, | |
| [aResp{aResp}, subtree, systemName, processorId, coreId, threadId]( | |
| const ManagedObjectByServiceMap& managedObjectsByService) { | |
| if (managedObjectsByService.empty()) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| for (const auto& [objectPath, serviceMap] : subtree) { | |
| if (sdbusplus::message::object_path(objectPath).filename() != | |
| threadId) { | |
| continue; | |
| } | |
| getCoreThreadData(aResp, ""_json_pointer, 0, systemName, | |
| processorId, coreId, threadId, objectPath, | |
| subtree, managedObjectsByService); | |
| return; | |
| } | |
| // Object not found | |
| messages::resourceNotFound( | |
| aResp->res, "#Processor.v1_11_0.Processor", threadId); | |
| }); | |
| }); | |
| } | |
| inline void getSubProcessorThreadMembers( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& systemName, const std::string& processorId, | |
| const std::string& coreId, const boost::system::error_code ec, | |
| const std::string& corePath) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error, ec: " << ec.value(); | |
| // No sub processor core objects found by mapper | |
| if (ec.value() == boost::system::errc::io_error) { | |
| messages::resourceNotFound(aResp->res, "#Processor.v1_11_0.Processor", | |
| coreId); | |
| return; | |
| } | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| aResp->res.addHeader(boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/ProcessorCollection/" | |
| "ProcessorCollection.json>; rel=describedby"); | |
| aResp->res.jsonValue["@odata.type"] = | |
| "#ProcessorCollection.ProcessorCollection"; | |
| aResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces( | |
| "redfish", "v1", "Systems", systemName, "Processors", processorId, | |
| "SubProcessors", coreId, "SubProcessors"); | |
| aResp->res.jsonValue["Name"] = "SubProcessor Collection"; | |
| const boost::urls::url& subProcessorsPath = crow::utility::urlFromPieces( | |
| "redfish", "v1", "Systems", systemName, "Processors", processorId, | |
| "SubProcessors", coreId, "SubProcessors"); | |
| constexpr std::array<std::string_view, 1> interfaces{ | |
| "xyz.openbmc_project.Inventory.Item.CpuThread"}; | |
| collection_util::getCollectionMembers(aResp, subProcessorsPath, interfaces, | |
| corePath.c_str()); | |
| } | |
| inline void getCpuCoreData( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const nlohmann::json::json_pointer& jsonPtr, uint8_t expandLevel, | |
| const std::string& systemName, const std::string& processorId, | |
| const std::string& coreId, const std::string& corePath, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree, | |
| const ManagedObjectByServiceMap& managedObjectsMap) { | |
| aResp->res.jsonValue[jsonPtr]["@odata.type"] = "#Processor.v1_11_0.Processor"; | |
| aResp->res.jsonValue[jsonPtr]["@odata.id"] = crow::utility::urlFromPieces( | |
| "redfish", "v1", "Systems", systemName, "Processors", processorId, | |
| "SubProcessors", coreId); | |
| aResp->res.jsonValue[jsonPtr]["Name"] = "SubProcessor"; | |
| aResp->res.jsonValue[jsonPtr]["ProcessorType"] = "Core"; | |
| aResp->res.jsonValue[jsonPtr]["Id"] = coreId; | |
| if (expandLevel > 0) { | |
| nlohmann::json::json_pointer subProcessorPtr = jsonPtr / "SubProcessors"; | |
| getSubProcessorThreadCollectionWithExpand( | |
| aResp, subProcessorPtr, expandLevel - 1, systemName, processorId, | |
| coreId, corePath, subtree, managedObjectsMap); | |
| } else { | |
| aResp->res.jsonValue[jsonPtr]["SubProcessors"]["@odata.id"] = | |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", systemName, | |
| "Processors", processorId, "SubProcessors", | |
| coreId, "SubProcessors"); | |
| } | |
| aResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Enabled"; | |
| aResp->res.jsonValue[jsonPtr]["Status"]["Health"] = "OK"; | |
| bool present = false; | |
| bool functional = false; | |
| dbus::utility::MapperServiceMap serviceMap; | |
| for (const auto& [parsedObjectPath, parsedServiceMap] : subtree) { | |
| if (corePath == parsedObjectPath) { | |
| serviceMap = parsedServiceMap; | |
| break; | |
| } | |
| } | |
| for (const auto& [serviceName, interfaceList] : serviceMap) { | |
| auto iter = managedObjectsMap.find(serviceName); | |
| if (iter == managedObjectsMap.end()) { | |
| BMCWEB_LOG_DEBUG << "Cpu data not available for serviceName " | |
| << serviceName; | |
| continue; | |
| } | |
| for (const auto& interface : interfaceList) { | |
| dbus::utility::DBusPropertiesMap properties; | |
| if (interface == | |
| "xyz.openbmc_project.State." | |
| "Decorator.OperationalStatus") { | |
| const bool* propertyFunctional = nullptr; | |
| dbus_utils::getPropertiesFromManagedObjects(iter->second, corePath, | |
| interface, properties); | |
| const bool success = sdbusplus::unpackPropertiesNoThrow( | |
| dbus_utils::UnpackErrorPrinter(), properties, "Functional", | |
| propertyFunctional); | |
| if (!success) { | |
| continue; | |
| } | |
| if (propertyFunctional != nullptr) { | |
| functional = *propertyFunctional; | |
| } | |
| } else if (interface == "xyz.openbmc_project.Inventory.Item") { | |
| const bool* propertyPresent = nullptr; | |
| const std::string* propertyPrettyName = nullptr; | |
| dbus_utils::getPropertiesFromManagedObjects(iter->second, corePath, | |
| interface, properties); | |
| const bool success = sdbusplus::unpackPropertiesNoThrow( | |
| dbus_utils::UnpackErrorPrinter(), properties, "Present", | |
| propertyPresent, "PrettyName", propertyPrettyName); | |
| if (!success) { | |
| continue; | |
| } | |
| if (propertyPresent != nullptr) { | |
| present = *propertyPresent; | |
| } | |
| if (propertyPrettyName != nullptr) { | |
| aResp->res.jsonValue[jsonPtr]["Name"] = *propertyPrettyName; | |
| } | |
| } else if (interface == "xyz.openbmc_project.Inventory.Item.CpuCore") { | |
| const uint32_t* propertyMicrocode = nullptr; | |
| dbus_utils::getPropertiesFromManagedObjects(iter->second, corePath, | |
| interface, properties); | |
| const bool success = sdbusplus::unpackPropertiesNoThrow( | |
| dbus_utils::UnpackErrorPrinter(), properties, "Microcode", | |
| propertyMicrocode); | |
| if (!success) { | |
| continue; | |
| } | |
| if (propertyMicrocode != nullptr) { | |
| aResp->res.jsonValue[jsonPtr]["ProcessorId"]["MicrocodeInfo"] = | |
| "0x" + intToHexString(*propertyMicrocode, 8); | |
| } | |
| } | |
| } | |
| } | |
| if (!present) { | |
| aResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Absent"; | |
| } | |
| if (!functional) { | |
| aResp->res.jsonValue[jsonPtr]["Status"]["Health"] = "Critical"; | |
| } | |
| // The "Metrics" stanza is deliberately omitted at the Core layer | |
| } | |
| inline void getSubProcessorCoreData( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& systemName, const std::string& processorId, | |
| const std::string& coreId, const boost::system::error_code ec, | |
| const std::string& cpuPath) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error, ec: " << ec.value(); | |
| // No processor objects found by mapper | |
| if (ec.value() == boost::system::errc::io_error) { | |
| messages::resourceNotFound(aResp->res, "#Processor.v1_11_0.Processor", | |
| processorId); | |
| return; | |
| } | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| aResp->res.addHeader( | |
| boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); | |
| std::array<std::string_view, 1> interfaces{ | |
| "xyz.openbmc_project.Inventory.Item.CpuCore"}; | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| managedStore::GetManagedObjectStore()->getSubTree( | |
| cpuPath, 0, interfaces, context, | |
| [aResp{aResp}, systemName, processorId, coreId, context]( | |
| const boost::system::error_code& ec2, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { | |
| if (ec2) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error, ec2: " << ec2.value(); | |
| // No processor objects found by mapper | |
| if (ec2.value() == boost::system::errc::io_error) { | |
| messages::resourceNotFound( | |
| aResp->res, "#Processor.v1_11_0.Processor", processorId); | |
| return; | |
| } | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| std::vector<std::string> serviceNames = | |
| dbus_utils::getServiceNamesFromSubtree(subtree); | |
| sdbusplus::message::object_path path("/xyz/openbmc_project/inventory"); | |
| dbus_utils::getManagedObjectsInEachService( | |
| std::move(serviceNames), path, context, | |
| [aResp{aResp}, subtree, systemName, processorId, | |
| coreId](const ManagedObjectByServiceMap& managedObjectsByService) { | |
| if (managedObjectsByService.empty()) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| for (const auto& [objectPath, serviceMap] : subtree) { | |
| if (sdbusplus::message::object_path(objectPath).filename() != | |
| coreId) { | |
| continue; | |
| } | |
| getCpuCoreData(aResp, ""_json_pointer, 0, systemName, | |
| processorId, coreId, objectPath, subtree, | |
| managedObjectsByService); | |
| return; | |
| } | |
| // Object not found | |
| messages::resourceNotFound( | |
| aResp->res, "#Processor.v1_11_0.Processor", coreId); | |
| }); | |
| }); | |
| } | |
| inline void getSubProcessorCoreMembers( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& systemName, const std::string& processorId, | |
| const boost::system::error_code ec, const std::string& cpuPath) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error, ec: " << ec.value(); | |
| // No processor objects found by mapper | |
| if (ec.value() == boost::system::errc::io_error) { | |
| messages::resourceNotFound(aResp->res, "#Processor.v1_11_0.Processor", | |
| processorId); | |
| return; | |
| } | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| aResp->res.addHeader(boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/ProcessorCollection/" | |
| "ProcessorCollection.json>; rel=describedby"); | |
| aResp->res.jsonValue["@odata.type"] = | |
| "#ProcessorCollection.ProcessorCollection"; | |
| aResp->res.jsonValue["@odata.id"] = | |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", systemName, | |
| "Processors", processorId, "SubProcessors"); | |
| aResp->res.jsonValue["Name"] = "SubProcessor Collection"; | |
| const boost::urls::url& subProcessorsPath = | |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", systemName, | |
| "Processors", processorId, "SubProcessors"); | |
| constexpr std::array<std::string_view, 1> interfaces{ | |
| "xyz.openbmc_project.Inventory.Item.CpuCore"}; | |
| collection_util::getCollectionMembers(aResp, subProcessorsPath, interfaces, | |
| cpuPath.c_str()); | |
| } | |
| inline void getSubProcessorThreadCollectionWithExpand( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const nlohmann::json::json_pointer& jsonPtr, uint8_t expandLevel, | |
| const std::string& systemName, const std::string& processorId, | |
| const std::string& coreId, const std::string& corePath, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree, | |
| const ManagedObjectByServiceMap& managedObjectsMap) { | |
| aResp->res.jsonValue[jsonPtr]["Name"] = "SubProcessor Collection"; | |
| aResp->res.jsonValue[jsonPtr]["@odata.type"] = | |
| "#ProcessorCollection.ProcessorCollection"; | |
| aResp->res.jsonValue[jsonPtr]["@odata.id"] = crow::utility::urlFromPieces( | |
| "redfish", "v1", "Systems", systemName, "Processors", processorId, | |
| "SubProcessors", coreId, "SubProcessors"); | |
| // Maps thread ID to threadObjectPath | |
| std::map<std::string, std::string, AlphanumLess<std::string>> | |
| threadIdToObjectPath; | |
| for (const auto& [objectPath, serviceMap] : subtree) { | |
| if (!objectPath.starts_with(corePath) || | |
| !dbus_utils::findInterfacesInServiceMap(serviceMap, | |
| subProcessorThreadInterfaces)) { | |
| continue; | |
| } | |
| sdbusplus::message::object_path threadPath(objectPath); | |
| const std::string& threadId = threadPath.filename(); | |
| threadIdToObjectPath.insert({threadId, threadPath}); | |
| } | |
| BMCWEB_LOG_DEBUG << "Core path: " << corePath | |
| << " thread count: " << threadIdToObjectPath.size(); | |
| if (threadIdToObjectPath.empty()) { | |
| BMCWEB_LOG_WARNING << "D-Bus CPU thread count was somehow empty"; | |
| aResp->res.jsonValue[jsonPtr]["Members@odata.count"] = 0; | |
| aResp->res.jsonValue[jsonPtr]["Members"] = nlohmann::json::array(); | |
| return; | |
| } | |
| size_t threadMemberCount = 0; | |
| for (const auto& [threadId, _] : threadIdToObjectPath) { | |
| nlohmann::json::json_pointer threadMemberPtr = | |
| jsonPtr / "Members" / threadMemberCount; | |
| if (expandLevel > 0) { | |
| getCoreThreadData(aResp, threadMemberPtr, expandLevel - 1, systemName, | |
| processorId, coreId, threadId, | |
| threadIdToObjectPath[threadId], subtree, | |
| managedObjectsMap); | |
| } else { | |
| aResp->res.jsonValue[threadMemberPtr]["@odata.id"] = | |
| crow::utility::urlFromPieces( | |
| "redfish", "v1", "Systems", systemName, "Processors", processorId, | |
| "SubProcessors", coreId, "SubProcessors", threadId); | |
| } | |
| threadMemberCount++; | |
| } | |
| aResp->res.jsonValue[jsonPtr]["Members@odata.count"] = threadMemberCount; | |
| } | |
| inline void getSubProcessorCoreCollectionWithExpand( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const nlohmann::json::json_pointer& jsonPtr, uint8_t expandLevel, | |
| const std::string& systemName, const std::string& processorId, | |
| const std::string& cpuPath, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree, | |
| const ManagedObjectByServiceMap& managedObjectsMap) { | |
| aResp->res.jsonValue[jsonPtr]["@odata.type"] = | |
| "#ProcessorCollection.ProcessorCollection"; | |
| aResp->res.jsonValue[jsonPtr]["Name"] = "SubProcessor Collection"; | |
| aResp->res.jsonValue[jsonPtr]["@odata.id"] = | |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", systemName, | |
| "Processors", processorId, "SubProcessors"); | |
| // Maps core ID to coreObjectPath | |
| std::map<std::string, std::string, AlphanumLess<std::string>> | |
| coreIdToObjectPath; | |
| for (const auto& [objectPath, serviceMap] : subtree) { | |
| if (!objectPath.starts_with(cpuPath) || | |
| !dbus_utils::findInterfacesInServiceMap(serviceMap, | |
| subProcessorCoreInterfaces)) { | |
| continue; | |
| } | |
| sdbusplus::message::object_path corePath(objectPath); | |
| const std::string& coreId = corePath.filename(); | |
| coreIdToObjectPath.insert({coreId, objectPath}); | |
| } | |
| if (coreIdToObjectPath.empty()) { | |
| BMCWEB_LOG_WARNING << "D-Bus CPU core count was somehow empty"; | |
| aResp->res.jsonValue[jsonPtr]["Members@odata.count"] = 0; | |
| aResp->res.jsonValue[jsonPtr]["Members"] = nlohmann::json::array(); | |
| return; | |
| } | |
| size_t coreMemberCount = 0; | |
| for (const auto& [coreId, _] : coreIdToObjectPath) { | |
| nlohmann::json::json_pointer coreMemberPtr = | |
| jsonPtr / "Members" / coreMemberCount; | |
| if (expandLevel > 0) { | |
| getCpuCoreData(aResp, coreMemberPtr, expandLevel - 1, systemName, | |
| processorId, coreId, coreIdToObjectPath[coreId], subtree, | |
| managedObjectsMap); | |
| } else { | |
| aResp->res.jsonValue[coreMemberPtr]["@odata.id"] = | |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", systemName, | |
| "Processors", processorId, | |
| "SubProcessors", coreId); | |
| } | |
| coreMemberCount++; | |
| } | |
| aResp->res.jsonValue[jsonPtr]["Members@odata.count"] = coreMemberCount; | |
| } | |
| inline void getProcessorCollectionWithExpand( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree, | |
| const ManagedObjectByServiceMap& managedObjectsMap, uint8_t expandLevel, | |
| const std::string& systemPath) { | |
| BMCWEB_LOG_DEBUG << "Get processor collection with expand."; | |
| const bool multiHost = !systemPath.empty(); | |
| // Maps cpu ID to cpuObjectPath. | |
| std::map<std::string, std::string, AlphanumLess<std::string>> | |
| cpuNameToObjectPath; | |
| for (const auto& [objectPath, serviceMap] : subtree) { | |
| if (!dbus_utils::findInterfacesInServiceMap(serviceMap, | |
| processorInterfaces)) { | |
| continue; | |
| } | |
| sdbusplus::message::object_path cpuPath(objectPath); | |
| // For multi-host systems, the object path of the cpu must be a | |
| // descendant of the systemPath. (e.g. if systemPath = /a/b, then | |
| // objectPath must be some pattern like /a/b/..) | |
| if (multiHost && !objectPath.starts_with(systemPath)) { | |
| continue; | |
| } | |
| const std::string& cpuId = cpuPath.filename(); | |
| cpuNameToObjectPath.insert({cpuId, cpuPath}); | |
| } | |
| // If no cpus found, return | |
| if (cpuNameToObjectPath.empty()) { | |
| BMCWEB_LOG_WARNING << "D-Bus CPU count was somehow empty"; | |
| aResp->res.jsonValue["Members@odata.count"] = 0; | |
| aResp->res.jsonValue["Members"] = nlohmann::json::array(); | |
| return; | |
| } | |
| size_t cpuMemberCount = 0; | |
| for (const auto& [cpu, _] : cpuNameToObjectPath) { | |
| nlohmann::json::json_pointer cpuMemberPtr = | |
| "/Members"_json_pointer / cpuMemberCount; | |
| getProcessorData(aResp, cpuMemberPtr, expandLevel - 1, cpu, systemPath, | |
| cpuNameToObjectPath[cpu], subtree, managedObjectsMap); | |
| cpuMemberCount++; | |
| } | |
| aResp->res.jsonValue["Members@odata.count"] = cpuMemberCount; | |
| aResp->res.jsonValue["Name"] = "Processor Collection"; | |
| } | |
| /** | |
| * Request all the properties for the given D-Bus object and fill out the | |
| * related entries in the Redfish OperatingConfig response. | |
| * | |
| * @param[in,out] aResp Async HTTP response. | |
| * @param[in] service D-Bus service name to query. | |
| * @param[in] objPath D-Bus object to query. | |
| */ | |
| inline void getOperatingConfigData( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, const std::string& service, | |
| const std::string& objPath) { | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| managedStore::GetManagedObjectStore()->getAllProperties( | |
| service, objPath, | |
| "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig", context, | |
| [aResp](const boost::system::error_code& ec, | |
| const dbus::utility::DBusPropertiesMap& properties) { | |
| if (ec) { | |
| BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " << ec.message(); | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| const size_t* availableCoreCount = nullptr; | |
| const uint32_t* baseSpeed = nullptr; | |
| const uint32_t* maxJunctionTemperature = nullptr; | |
| const uint32_t* maxSpeed = nullptr; | |
| const uint32_t* powerLimit = nullptr; | |
| const TurboProfileProperty* turboProfile = nullptr; | |
| const BaseSpeedPrioritySettingsProperty* baseSpeedPrioritySettings = | |
| nullptr; | |
| const bool success = sdbusplus::unpackPropertiesNoThrow( | |
| dbus_utils::UnpackErrorPrinter(), properties, "AvailableCoreCount", | |
| availableCoreCount, "BaseSpeed", baseSpeed, | |
| "MaxJunctionTemperature", maxJunctionTemperature, "MaxSpeed", | |
| maxSpeed, "PowerLimit", powerLimit, "TurboProfile", turboProfile, | |
| "BaseSpeedPrioritySettings", baseSpeedPrioritySettings); | |
| if (!success) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| nlohmann::json& json = aResp->res.jsonValue; | |
| if (availableCoreCount != nullptr) { | |
| json["TotalAvailableCoreCount"] = *availableCoreCount; | |
| } | |
| if (baseSpeed != nullptr) { | |
| json["BaseSpeedMHz"] = *baseSpeed; | |
| } | |
| if (maxJunctionTemperature != nullptr) { | |
| json["MaxJunctionTemperatureCelsius"] = *maxJunctionTemperature; | |
| } | |
| if (maxSpeed != nullptr) { | |
| json["MaxSpeedMHz"] = *maxSpeed; | |
| } | |
| if (powerLimit != nullptr) { | |
| json["TDPWatts"] = *powerLimit; | |
| } | |
| if (turboProfile != nullptr) { | |
| nlohmann::json& turboArray = json["TurboProfile"]; | |
| turboArray = nlohmann::json::array(); | |
| for (const auto& [turboSpeed, coreCount] : *turboProfile) { | |
| nlohmann::json::object_t turbo; | |
| turbo["ActiveCoreCount"] = coreCount; | |
| turbo["MaxSpeedMHz"] = turboSpeed; | |
| turboArray.emplace_back(std::move(turbo)); | |
| } | |
| } | |
| if (baseSpeedPrioritySettings != nullptr) { | |
| nlohmann::json& baseSpeedArray = json["BaseSpeedPrioritySettings"]; | |
| baseSpeedArray = nlohmann::json::array(); | |
| for (const auto& [baseSpeedMhz, coreList] : | |
| *baseSpeedPrioritySettings) { | |
| nlohmann::json::object_t speed; | |
| speed["CoreCount"] = coreList.size(); | |
| speed["CoreIDs"] = coreList; | |
| speed["BaseSpeedMHz"] = baseSpeedMhz; | |
| baseSpeedArray.emplace_back(std::move(speed)); | |
| } | |
| } | |
| }); | |
| } | |
| /** | |
| * Handle the D-Bus response from attempting to set the CPU's AppliedConfig | |
| * property. Main task is to translate error messages into Redfish errors. | |
| * | |
| * @param[in,out] resp HTTP response. | |
| * @param[in] setPropVal Value which we attempted to set. | |
| * @param[in] ec D-Bus response error code. | |
| * @param[in] msg D-Bus response message. | |
| */ | |
| inline void handleAppliedConfigResponse( | |
| const std::shared_ptr<bmcweb::AsyncResp>& resp, | |
| const std::string& setPropVal, const boost::system::error_code& ec, | |
| const sdbusplus::message_t& msg) { | |
| if (!ec) { | |
| BMCWEB_LOG_DEBUG << "Set Property succeeded"; | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG << "Set Property failed: " << ec; | |
| const sd_bus_error* dbusError = msg.get_error(); | |
| if (dbusError == nullptr) { | |
| messages::internalError(resp->res); | |
| return; | |
| } | |
| // The asio error code doesn't know about our custom errors, so we have to | |
| // parse the error string. Some of these D-Bus -> Redfish translations are a | |
| // stretch, but it's good to try to communicate something vaguely useful. | |
| if (strcmp(dbusError->name, | |
| "xyz.openbmc_project.Common.Error.InvalidArgument") == 0) { | |
| // Service did not like the object_path we tried to set. | |
| messages::propertyValueIncorrect( | |
| resp->res, "AppliedOperatingConfig/@odata.id", setPropVal); | |
| } else if (strcmp(dbusError->name, | |
| "xyz.openbmc_project.Common.Error.NotAllowed") == 0) { | |
| // Service indicates we can never change the config for this processor. | |
| messages::propertyNotWritable(resp->res, "AppliedOperatingConfig"); | |
| } else if (strcmp(dbusError->name, | |
| "xyz.openbmc_project.Common.Error.Unavailable") == 0) { | |
| // Service indicates the config cannot be changed right now, but maybe | |
| // in a different system state. | |
| messages::resourceInStandby(resp->res); | |
| } else { | |
| messages::internalError(resp->res); | |
| } | |
| } | |
| /** | |
| * Handle the PATCH operation of the AppliedOperatingConfig property. Do basic | |
| * validation of the input data, and then set the D-Bus property. | |
| * | |
| * @param[in,out] resp Async HTTP response. | |
| * @param[in] processorId Processor's Id. | |
| * @param[in] appliedConfigUri New property value to apply. | |
| * @param[in] cpuObjectPath Path of CPU object to modify. | |
| * @param[in] serviceMap Service map for CPU object. | |
| */ | |
| inline void patchAppliedOperatingConfig( | |
| const std::shared_ptr<bmcweb::AsyncResp>& resp, | |
| const std::string& processorId, const std::string& appliedConfigUri, | |
| const std::string& cpuObjectPath, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree, | |
| [[maybe_unused]] const ManagedObjectByServiceMap& managedObjectsMap) { | |
| // Get Service map for the object path. | |
| dbus::utility::MapperServiceMap serviceMap; | |
| for (const auto& [parsedObjectPath, parsedServiceMap] : subtree) { | |
| if (cpuObjectPath == parsedObjectPath) { | |
| serviceMap = parsedServiceMap; | |
| break; | |
| } | |
| } | |
| // Check that the property even exists by checking for the interface | |
| const std::string* controlService = nullptr; | |
| for (const auto& [serviceName, interfaceList] : serviceMap) { | |
| if (std::find(interfaceList.begin(), interfaceList.end(), | |
| "xyz.openbmc_project.Control.Processor." | |
| "CurrentOperatingConfig") != interfaceList.end()) { | |
| controlService = &serviceName; | |
| break; | |
| } | |
| } | |
| if (controlService == nullptr) { | |
| messages::internalError(resp->res); | |
| return; | |
| } | |
| // Check that the config URI is a child of the cpu URI being patched. | |
| std::string expectedPrefix("/redfish/v1/Systems/system/Processors/"); | |
| expectedPrefix += processorId; | |
| expectedPrefix += "/OperatingConfigs/"; | |
| if (!appliedConfigUri.starts_with(expectedPrefix) || | |
| expectedPrefix.size() == appliedConfigUri.size()) { | |
| messages::propertyValueIncorrect( | |
| resp->res, "AppliedOperatingConfig/@odata.id", appliedConfigUri); | |
| return; | |
| } | |
| // Generate the D-Bus path of the OperatingConfig object, by assuming it's a | |
| // direct child of the CPU object. | |
| // Strip the expectedPrefix from the config URI to get the "filename", and | |
| // append to the CPU's path. | |
| std::string configBaseName = appliedConfigUri.substr(expectedPrefix.size()); | |
| sdbusplus::message::object_path configPath(cpuObjectPath); | |
| configPath /= configBaseName; | |
| BMCWEB_LOG_INFO << "Setting config to " << configPath.str; | |
| // Set the property, with handler to check error responses | |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( | |
| resp->strand_, | |
| [resp, appliedConfigUri](const boost::system::error_code& ec, | |
| const sdbusplus::message_t& msg) { | |
| handleAppliedConfigResponse(resp, appliedConfigUri, ec, msg); | |
| }, | |
| *controlService, cpuObjectPath, "org.freedesktop.DBus.Properties", "Set", | |
| "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig", | |
| "AppliedConfig", dbus::utility::DbusVariantType(std::move(configPath))); | |
| } | |
| inline void handleSubProcessorThreadHead( | |
| crow::App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& /* systemName */, const std::string& /* processorId */, | |
| const std::string& /* coreId */, const std::string& /* threadId */) { | |
| if (!redfish::setUpRedfishRoute(app, req, aResp)) { | |
| return; | |
| } | |
| aResp->res.addHeader( | |
| boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); | |
| } | |
| inline void handleSubProcessorThreadCollectionHead( | |
| crow::App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& /* systemName */, const std::string& /* processorId */, | |
| const std::string& /* coreId */) { | |
| if (!redfish::setUpRedfishRoute(app, req, aResp)) { | |
| return; | |
| } | |
| aResp->res.addHeader(boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/ProcessorCollection/" | |
| "ProcessorCollection.json>; rel=describedby"); | |
| } | |
| inline void handleSubProcessorCoreHead( | |
| crow::App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& /* systemName */, const std::string& /* processorId */, | |
| const std::string& /* coreId */) { | |
| if (!redfish::setUpRedfishRoute(app, req, aResp)) { | |
| return; | |
| } | |
| aResp->res.addHeader( | |
| boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); | |
| } | |
| inline void handleSubProcessorCoreCollectionHead( | |
| crow::App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& /* systemName */, const std::string& /* processorId */) { | |
| if (!redfish::setUpRedfishRoute(app, req, aResp)) { | |
| return; | |
| } | |
| aResp->res.addHeader(boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/ProcessorCollection/" | |
| "ProcessorCollection.json>; rel=describedby"); | |
| } | |
| inline void handleProcessorHead(crow::App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& /* systemName */, | |
| const std::string& /* processorId */) { | |
| if (!redfish::setUpRedfishRoute(app, req, aResp)) { | |
| return; | |
| } | |
| aResp->res.addHeader( | |
| boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); | |
| } | |
| inline void handleProcessorCollectionHead( | |
| crow::App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& /* systemName */) { | |
| if (!redfish::setUpRedfishRoute(app, req, aResp)) { | |
| return; | |
| } | |
| aResp->res.addHeader(boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/ProcessorCollection/" | |
| "ProcessorCollection.json>; rel=describedby"); | |
| } | |
| inline void parseSubtreeForProcessorCollection( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree, | |
| const query_param::Query& delegated, const std::string& systemPath) { | |
| const bool multiHost = !systemPath.empty(); | |
| // Default systemName is system and change if system is multi-host | |
| std::string systemName = "system"; | |
| if (multiHost) { | |
| systemName = sdbusplus::message::object_path(systemPath).filename(); | |
| } | |
| if (delegated.expandLevel == 0 || | |
| delegated.expandType == query_param::ExpandType::None) { | |
| BMCWEB_LOG_DEBUG << "Use default processor expand handler"; | |
| collection_util::getCollectionMembersFromSubtree( | |
| asyncResp, | |
| boost::urls::url("/redfish/v1/Systems/" + systemName + "/Processors"), | |
| processorInterfaces, subtree); | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG << "Use efficient processor expand handler"; | |
| std::vector<std::string> serviceNames = | |
| dbus_utils::getServiceNamesFromSubtree(subtree); | |
| if (serviceNames.empty()) { | |
| BMCWEB_LOG_DEBUG << "Can't retrieve service names from subtree"; | |
| asyncResp->res.jsonValue["Members@odata.count"] = 0; | |
| asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); | |
| return; | |
| } | |
| managedStore::ManagedObjectStoreContext context(asyncResp); | |
| // Get managed objects from each service in subtree to proactively | |
| // fetch all dbus data needed to serve requests for subordinate | |
| // resources. | |
| sdbusplus::message::object_path path("/xyz/openbmc_project/inventory"); | |
| dbus_utils::getManagedObjectsInEachService( | |
| std::move(serviceNames), path, context, | |
| [asyncResp{asyncResp}, subtree, delegated, | |
| systemPath](const ManagedObjectByServiceMap& managedObjectsByService) { | |
| if (managedObjectsByService.empty()) { | |
| BMCWEB_LOG_DEBUG << "Cannot get managed objects."; | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| getProcessorCollectionWithExpand(asyncResp, subtree, | |
| managedObjectsByService, | |
| delegated.expandLevel, systemPath); | |
| }); | |
| } | |
| inline void handleProcessorCollection( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const query_param::Query& delegated, const std::string& systemPath) { | |
| const bool multiHost = !systemPath.empty(); | |
| // Default systemName is system and change if system is multi-host | |
| std::string systemName = "system"; | |
| // The subtree to query for Dimms changes if single or multi-host | |
| std::string subtreeRoot = "/xyz/openbmc_project/inventory"; | |
| if (multiHost) { | |
| systemName = sdbusplus::message::object_path(systemPath).filename(); | |
| subtreeRoot = systemPath; | |
| } | |
| asyncResp->res.addHeader(boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/ProcessorCollection/" | |
| "ProcessorCollection.json>; rel=describedby"); | |
| asyncResp->res.jsonValue["@odata.type"] = | |
| "#ProcessorCollection.ProcessorCollection"; | |
| asyncResp->res.jsonValue["Name"] = "Processor Collection"; | |
| asyncResp->res.jsonValue["@odata.id"] = | |
| "/redfish/v1/Systems/" + systemName + "/Processors"; | |
| // Get dbus interfaces based on expand level. | |
| auto interfaces = getInterfacesForProcessorSubtree(delegated.expandLevel); | |
| if (interfaces.empty()) { | |
| BMCWEB_LOG_ERROR << "Can't get interface list for expand level " | |
| << delegated.expandLevel; | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| // Get subtree information to serve requests for both processor | |
| // collection and, if expand is requested, for all requested subordinate | |
| // resources. | |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); | |
| managedStore::GetManagedObjectStore()->getSubTree( | |
| subtreeRoot, 0, interfaces, requestContext, | |
| [asyncResp{asyncResp}, delegated, systemPath]( | |
| const boost::system::error_code& ec, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { | |
| if (ec == boost::system::errc::io_error) { | |
| asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); | |
| asyncResp->res.jsonValue["Members@odata.count"] = 0; | |
| return; | |
| } | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec.value(); | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| parseSubtreeForProcessorCollection(asyncResp, subtree, delegated, | |
| systemPath); | |
| }); | |
| } | |
| inline void handleOperatingConfigCollectionGet( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& cpuName) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| asyncResp->res.jsonValue["@odata.type"] = | |
| "#OperatingConfigCollection.OperatingConfigCollection"; | |
| asyncResp->res.jsonValue["@odata.id"] = | |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", "system", | |
| "Processors", cpuName, "OperatingConfigs"); | |
| asyncResp->res.jsonValue["Name"] = "Operating Config Collection"; | |
| // First find the matching CPU object so we know how to | |
| // constrain our search for related Config objects. | |
| const std::array<std::string_view, 1> interfaces = { | |
| "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"}; | |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); | |
| managedStore::GetManagedObjectStore()->getSubTreePaths( | |
| "/xyz/openbmc_project/inventory", 0, interfaces, requestContext, | |
| [asyncResp, cpuName]( | |
| const boost::system::error_code& ec, | |
| const dbus::utility::MapperGetSubTreePathsResponse& objects) { | |
| if (ec) { | |
| BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " << ec.message(); | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| for (const std::string& object : objects) { | |
| if (!object.ends_with(cpuName)) { | |
| continue; | |
| } | |
| // Not expected that there will be multiple matching | |
| // CPU objects, but if there are just use the first | |
| // one. | |
| // Use the common search routine to construct the | |
| // Collection of all Config objects under this CPU. | |
| constexpr std::array<std::string_view, 1> interface{ | |
| "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}; | |
| collection_util::getCollectionMembers( | |
| asyncResp, | |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", "system", | |
| "Processors", cpuName, | |
| "OperatingConfigs"), | |
| interface, object.c_str()); | |
| return; | |
| } | |
| }); | |
| } | |
| inline void requestRoutesOperatingConfigCollection(App& app) { | |
| BMCWEB_ROUTE(app, | |
| "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/") | |
| .privileges(redfish::privileges::getOperatingConfigCollection) | |
| .methods(boost::beast::http::verb::get)( | |
| std::bind_front(handleOperatingConfigCollectionGet, std::ref(app))); | |
| } | |
| inline void handleOperatingConfigGet( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& cpuName, const std::string& configName) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| // Ask for all objects implementing OperatingConfig so we can search | |
| // for one with a matching name | |
| constexpr std::array<std::string_view, 1> interfaces = { | |
| "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}; | |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); | |
| managedStore::GetManagedObjectStore()->getSubTree( | |
| "/xyz/openbmc_project/inventory", 0, interfaces, requestContext, | |
| [asyncResp, cpuName, configName]( | |
| const boost::system::error_code& ec, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { | |
| if (ec) { | |
| BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " << ec.message(); | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| const std::string expectedEnding = cpuName + '/' + configName; | |
| for (const auto& [objectPath, serviceMap] : subtree) { | |
| // Ignore any configs without matching cpuX/configY | |
| if (!objectPath.ends_with(expectedEnding) || serviceMap.empty()) { | |
| continue; | |
| } | |
| nlohmann::json& json = asyncResp->res.jsonValue; | |
| json["@odata.type"] = "#OperatingConfig.v1_0_0.OperatingConfig"; | |
| json["@odata.id"] = crow::utility::urlFromPieces( | |
| "redfish", "v1", "Systems", "system", "Processors", cpuName, | |
| "OperatingConfigs", configName); | |
| json["Name"] = "Processor Profile"; | |
| json["Id"] = configName; | |
| // Just use the first implementation of the object - not | |
| // expected that there would be multiple matching | |
| // services | |
| getOperatingConfigData(asyncResp, serviceMap.begin()->first, | |
| objectPath); | |
| return; | |
| } | |
| messages::resourceNotFound(asyncResp->res, "OperatingConfig", | |
| configName); | |
| }); | |
| } | |
| inline void requestRoutesOperatingConfig(App& app) { | |
| BMCWEB_ROUTE( | |
| app, | |
| "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/<str>/") | |
| .privileges(redfish::privileges::getOperatingConfig) | |
| .methods(boost::beast::http::verb::get)( | |
| std::bind_front(handleOperatingConfigGet, std::ref(app))); | |
| } | |
| inline int countProcessors() { | |
| // TODO(): This is a STUB | |
| // For now, this is OK, since it is only used for syntax validation. | |
| // However, when and if writing (POST, PATCH, etc.) is allowed, | |
| // we will need to have better bounds-checking here, | |
| // otherwise it will be trivial for a writer to DoS all the storage. | |
| return 16; | |
| } | |
| inline int countCores(int cpuIndex) { | |
| // TODO(): This is a STUB, see above comment | |
| (void)cpuIndex; | |
| return 256; | |
| } | |
| inline int countThreads(int cpuIndex, int coreIndex) { | |
| // TODO(): This is a STUB, see above comment | |
| (void)cpuIndex; | |
| (void)coreIndex; | |
| return 16; | |
| } | |
| inline int prefixStrToInt(const std::string& prefix, const std::string& str) { | |
| size_t lenPrefix = prefix.size(); | |
| size_t lenStr = str.size(); | |
| // Content following prefix must be within 1 to 9 characters in length | |
| if (lenStr < (lenPrefix + 1)) { | |
| BMCWEB_LOG_ERROR << "Index number too short"; | |
| return -1; | |
| } | |
| if (lenStr > (lenPrefix + 9)) { | |
| // This length limit prevents 32-bit rollover attacks | |
| BMCWEB_LOG_ERROR << "Index number too long"; | |
| return -1; | |
| } | |
| if (str.substr(0, lenPrefix) != prefix) { | |
| // Prefix does not match | |
| BMCWEB_LOG_ERROR << "Index number wrong prefix"; | |
| return -1; | |
| } | |
| std::string numText = str.substr(lenPrefix); | |
| size_t lenText = numText.size(); | |
| size_t lenCheck = std::strlen(numText.c_str()); | |
| if (lenText != lenCheck) { | |
| // This comparison prevents 0x00 string truncation attacks | |
| BMCWEB_LOG_ERROR << "Index number wrong length"; | |
| return -1; | |
| } | |
| int64_t value = 0; | |
| if (!absl::SimpleAtoi(numText, &value)) { | |
| BMCWEB_LOG_ERROR << "Index number wrong content:" << numText; | |
| return -1; | |
| } | |
| int result = static_cast<int>(value); | |
| return result; | |
| } | |
| inline int extractIndexFromArg(const std::string& arg) { | |
| const std::string index = arg.substr(arg.find_last_not_of("0123456789") + 1); | |
| if (index.empty()) { | |
| BMCWEB_LOG_ERROR << "Unable to extract index from arg"; | |
| return -1; | |
| } | |
| int64_t value = 0; | |
| if (!absl::SimpleAtoi(index, &value)) { | |
| BMCWEB_LOG_ERROR << "Index number from arg invalid content:" << index; | |
| return -1; | |
| } | |
| return static_cast<int>(value); | |
| } | |
| inline int cpuArgToIndex(const std::string& cpuArg) { | |
| int bound = countProcessors(); | |
| int num = extractIndexFromArg(cpuArg); | |
| if (num >= bound) { | |
| BMCWEB_LOG_ERROR << "Processor index out of range"; | |
| return -1; | |
| } | |
| return num; | |
| } | |
| inline int coreArgToIndex(const std::string& coreArg, int cpuIndex) { | |
| int bound = countCores(cpuIndex); | |
| int num = prefixStrToInt("core", coreArg); | |
| if (num >= bound) { | |
| BMCWEB_LOG_ERROR << "Subprocessor core index out of range"; | |
| return -1; | |
| } | |
| return num; | |
| } | |
| inline int threadArgToIndex(const std::string& threadArg, int cpuIndex, | |
| int coreIndex) { | |
| int bound = countThreads(cpuIndex, coreIndex); | |
| int num = prefixStrToInt("thread", threadArg); | |
| if (num >= bound) { | |
| BMCWEB_LOG_ERROR << "Subprocessor thread index out of range"; | |
| return -1; | |
| } | |
| return num; | |
| } | |
| inline int validateCpu(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& cpuArg) { | |
| int cpuIndex = cpuArgToIndex(cpuArg); | |
| if (cpuIndex < 0) { | |
| messages::resourceNotFound(asyncResp->res, "Processor", cpuArg); | |
| } | |
| return cpuIndex; | |
| } | |
| inline int validateCpuCore(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& cpuArg, | |
| const std::string& coreArg) { | |
| int cpuIndex = validateCpu(asyncResp, cpuArg); | |
| if (cpuIndex < 0) { | |
| return cpuIndex; | |
| } | |
| int coreIndex = coreArgToIndex(coreArg, cpuIndex); | |
| if (coreIndex < 0) { | |
| messages::resourceNotFound(asyncResp->res, "Core", coreArg); | |
| } | |
| return coreIndex; | |
| } | |
| inline int validateCpuCoreThread( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& cpuArg, const std::string& coreArg, | |
| const std::string& threadArg) { | |
| int cpuIndex = validateCpu(asyncResp, cpuArg); | |
| if (cpuIndex < 0) { | |
| return cpuIndex; | |
| } | |
| int coreIndex = validateCpuCore(asyncResp, cpuArg, coreArg); | |
| if (coreIndex < 0) { | |
| return coreIndex; | |
| } | |
| int threadIndex = threadArgToIndex(threadArg, cpuIndex, coreIndex); | |
| if (threadIndex < 0) { | |
| messages::resourceNotFound(asyncResp->res, "Thread", threadArg); | |
| } | |
| return threadIndex; | |
| } | |
| // Creates a generic ExternalStorer hook, should be 2 directories above | |
| // Each hook contains instances, each instance contains entries | |
| // Omit the leading "/redfish/v1" from pathBase, and no trailing slash | |
| inline external_storer::Hook makeExternalStorerHook( | |
| const std::string& pathBase) { | |
| const std::string emptyString; | |
| const std::vector<std::string> emptyList; | |
| return {pathBase, emptyString, emptyList, emptyList}; | |
| } | |
| // Creates a generic ExternalStorer instance within the given hook | |
| // The content will be minimal, just enough to create filename on disk | |
| // Returns true if successful | |
| inline bool makeExternalStorerInstance( | |
| const std::shared_ptr<external_storer::Hook>& hook, const std::string& id) { | |
| // External storer NEVER sends dbus calls, so you will never leave this | |
| // thread. | |
| auto respGet = std::make_shared<bmcweb::AsyncResp>(nullptr); | |
| hook->handleGetInstance(respGet, id); | |
| if (respGet->res.result() == boost::beast::http::status::ok) { | |
| // Already exists, nothing more needs to be done | |
| return true; | |
| } | |
| boost::beast::http::request<boost::beast::http::string_body> upBody; | |
| std::error_code ec; | |
| // Create instance, with given ID, which will become filename on disk | |
| auto upJson = nlohmann::json::object(); | |
| upJson["Id"] = id; | |
| // Must supply 4th argument to avoid throwing exceptions | |
| upBody.body() = | |
| upJson.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace); | |
| crow::Request reqCreate{upBody, ec}; | |
| // External storer NEVER sends dbus calls, so you will never leave this | |
| // thread. | |
| auto respCreate = std::make_shared<bmcweb::AsyncResp>(nullptr); | |
| hook->handleCreateInstance(reqCreate, respCreate); | |
| if (respCreate->res.result() != boost::beast::http::status::created) { | |
| BMCWEB_LOG_ERROR << "Problem creating ExternalStorer instance " << id; | |
| return false; | |
| } | |
| return true; | |
| } | |
| // Remembers toplevel Processors hook, there is only one per systemName | |
| // Each instance in it represents a CPU and is named like cpuArg | |
| // Each instance contains only one entry, "ProcessorMetrics" | |
| inline std::shared_ptr<external_storer::Hook> rememberProcessorHook( | |
| const std::string& systemName, const std::string& cpuArg) { | |
| if (systemName != "system") { | |
| return nullptr; | |
| } | |
| static std::shared_ptr<external_storer::Hook> hookProcessor = nullptr; | |
| if (!hookProcessor) { | |
| // If not already remembered by static variable, create hook | |
| hookProcessor = std::make_shared<external_storer::Hook>( | |
| makeExternalStorerHook("Systems/system/Processors")); | |
| } | |
| if (!(makeExternalStorerInstance(hookProcessor, cpuArg))) { | |
| BMCWEB_LOG_ERROR << "Problem remembering hook for " << cpuArg; | |
| return nullptr; | |
| } | |
| return hookProcessor; | |
| } | |
| // Remembers lowest-level SubProcessors hook, scoped by systemName | |
| // There is one hook for each unique (System, CPU, thread) tuple | |
| // Each instance in it represents a thread and is named like threadArg | |
| // Each instance contains only one entry, "ProcessorMetrics" | |
| inline std::shared_ptr<external_storer::Hook> rememberThreadHook( | |
| const std::string& systemName, const std::string& cpuArg, | |
| const std::string& coreArg, const std::string& threadArg) { | |
| if (systemName != "system") { | |
| return nullptr; | |
| } | |
| static std::map<std::string, | |
| std::map<std::string, std::shared_ptr<external_storer::Hook>>> | |
| hookMaps; | |
| auto findCpu = hookMaps.find(cpuArg); | |
| if (findCpu == hookMaps.end()) { | |
| hookMaps[cpuArg] = {}; | |
| findCpu = hookMaps.find(cpuArg); | |
| } | |
| auto findCore = findCpu->second.find(coreArg); | |
| if (findCore == findCpu->second.end()) { | |
| (findCpu->second)[coreArg] = {}; | |
| findCore = findCpu->second.find(coreArg); | |
| } | |
| std::shared_ptr<external_storer::Hook> hookThread = findCore->second; | |
| if (!hookThread) { | |
| std::string path = "Systems/system/Processors/" + cpuArg + | |
| "/SubProcessors/" + coreArg + "/SubProcessors"; | |
| hookThread = | |
| std::make_shared<external_storer::Hook>(makeExternalStorerHook(path)); | |
| findCore->second = hookThread; | |
| } | |
| if (!(makeExternalStorerInstance(hookThread, threadArg))) { | |
| BMCWEB_LOG_ERROR << "Problem remembering hook for " << threadArg; | |
| return nullptr; | |
| } | |
| return hookThread; | |
| } | |
| inline nlohmann::json fakeProcessorMetrics(const std::string& systemName, | |
| const std::string& cpu, | |
| const std::string& core, | |
| const std::string& thread) { | |
| nlohmann::json::object_t metricsObj; | |
| metricsObj["Id"] = "Metrics"; | |
| metricsObj["Name"] = "Processor Metrics"; | |
| // The different levels have slightly different default content | |
| if (!thread.empty()) { | |
| // Thread level | |
| metricsObj["CorrectableCoreErrorCount"] = 0; | |
| metricsObj["UncorrectableCoreErrorCount"] = 0; | |
| } | |
| metricsObj["CorrectableOtherErrorCount"] = 0; | |
| metricsObj["UncorrectableOtherErrorCount"] = 0; | |
| // ExternalStorer would normally apply this as invariant if successful | |
| // The caller already applies "@odata.type" as invariant no matter what | |
| std::string url = | |
| absl::StrCat("/redfish/v1/Systems/", systemName, "/Processors/", cpu); | |
| if (!core.empty()) { | |
| absl::StrAppend(&url, "/SubProcessors/", core); | |
| } | |
| if (!thread.empty()) { | |
| absl::StrAppend(&url, "/SubProcessors/", thread); | |
| } | |
| absl::StrAppend(&url, "/ProcessorMetrics"); | |
| metricsObj["@odata.id"] = std::move(url); | |
| return metricsObj; | |
| } | |
| inline void handleGetCpuMetrics( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const nlohmann::json::json_pointer& jsonPtr, const std::string& systemName, | |
| const std::string& cpuArg) { | |
| if (validateCpu(asyncResp, cpuArg) < 0) { | |
| // Error reply already composed | |
| return; | |
| } | |
| std::shared_ptr<external_storer::Hook> hook = | |
| rememberProcessorHook(systemName, cpuArg); | |
| if (!hook) { | |
| messages::resourceNotFound(asyncResp->res, "Processor", cpuArg); | |
| return; | |
| } | |
| std::string emptyString; | |
| hook->handleGetEntry(asyncResp, jsonPtr, cpuArg, emptyString, | |
| "ProcessorMetrics"); | |
| if (asyncResp->res.result() == boost::beast::http::status::not_found) { | |
| // Fake up a synthetic response to replace only the 404 error | |
| asyncResp->res.jsonValue[jsonPtr] = | |
| fakeProcessorMetrics(systemName, cpuArg, "", ""); | |
| asyncResp->res.result(boost::beast::http::status::ok); | |
| } | |
| if (asyncResp->res.result() != boost::beast::http::status::ok) { | |
| // Error reply already composed | |
| return; | |
| } | |
| // Apply invariants, to override whatever might have been in the file | |
| asyncResp->res.jsonValue[jsonPtr]["@odata.type"] = | |
| "#ProcessorMetrics.v1_5_0.ProcessorMetrics"; | |
| } | |
| inline void handleGetCpuCoreThreadMetrics( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const nlohmann::json::json_pointer& jsonPtr, const std::string& systemName, | |
| const std::string& cpuArg, const std::string& coreArg, | |
| const std::string& threadArg) { | |
| if (validateCpuCoreThread(asyncResp, cpuArg, coreArg, threadArg) < 0) { | |
| return; | |
| } | |
| std::shared_ptr<external_storer::Hook> hook = | |
| rememberThreadHook(systemName, cpuArg, coreArg, threadArg); | |
| if (!hook) { | |
| messages::resourceNotFound(asyncResp->res, "Thread", threadArg); | |
| return; | |
| } | |
| std::string emptyString; | |
| hook->handleGetEntry(asyncResp, jsonPtr, threadArg, emptyString, | |
| "ProcessorMetrics"); | |
| if (asyncResp->res.result() == boost::beast::http::status::not_found) { | |
| // Fake up a synthetic response to replace only the 404 error | |
| asyncResp->res.jsonValue[jsonPtr] = | |
| fakeProcessorMetrics(systemName, cpuArg, coreArg, threadArg); | |
| asyncResp->res.result(boost::beast::http::status::ok); | |
| } | |
| if (asyncResp->res.result() != boost::beast::http::status::ok) { | |
| // Error reply already composed | |
| return; | |
| } | |
| // Apply invariants, to override whatever might have been in the file | |
| asyncResp->res.jsonValue[jsonPtr]["@odata.type"] = | |
| "#ProcessorMetrics.v1_5_0.ProcessorMetrics"; | |
| } | |
| // The glue layers are already handled by these functions elsewhere: | |
| // Processors = requestRoutesProcessorCollection() | |
| // Processors/cpuX = requestRoutesProcessor() | |
| // Processors/cpuX/SubProcessors = requestRoutesSubProcessorCoreCollection() | |
| // Processors/cpuX/SubProcessors/coreY = requestRoutesSubProcessorCore() | |
| // Processors/cpuX/SubProcessors/coreY/SubProcessors = | |
| // requestRoutesSubProcessorThreadCollection() | |
| // Processors/cpuX/SubProcessors/coreY/SubProcessors/threadZ = | |
| // requestRoutesSubProcessorThread() | |
| // The "ProcessorMetrics" endpoints are below those glue layers: | |
| // Processors/cpuX/ProcessorMetrics = handled here | |
| // Processors/cpuX/SubProcessors/coreY/ProcessorMetrics = intentionally | |
| // omitted | |
| // Processors/cpuX/SubProcessors/coreY/SubProcessors/threadZ/ProcessorMetrics | |
| // = handled here | |
| inline void requestRoutesProcessorMetricsViaExternalStorer(App& app) { | |
| BMCWEB_ROUTE(app, | |
| "/redfish/v1/Systems/system/Processors/<str>/ProcessorMetrics/") | |
| .privileges(redfish::privileges::getProcessor) | |
| .methods(boost::beast::http::verb::get)( | |
| [&app](const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& cpuArg) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| handleGetCpuMetrics(asyncResp, ""_json_pointer, "system", cpuArg); | |
| }); | |
| BMCWEB_ROUTE(app, | |
| "/redfish/v1/Systems/system/Processors/<str>/SubProcessors/" | |
| "<str>/SubProcessors/<str>/ProcessorMetrics/") | |
| .privileges(redfish::privileges::getProcessor) | |
| .methods(boost::beast::http::verb::get)( | |
| [&app](const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& cpuArg, const std::string& coreArg, | |
| const std::string& threadArg) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| handleGetCpuCoreThreadMetrics(asyncResp, ""_json_pointer, | |
| "system", cpuArg, coreArg, | |
| threadArg); | |
| }); | |
| // TODO(): Implement corresponding PATCH methods | |
| // The POST method is not correct to use here, | |
| // as the result location is already well-known name "ProcessorMetrics". | |
| // and from the user point of view it always exists, | |
| // so the creation operation is not the correct operation. | |
| // The PUT method also might not be correct to use here, | |
| // as it would fully replace all content at the result location, | |
| // but the expectation is to only selectively replace some content. | |
| } | |
| inline void requestRoutesProcessorCollection(App& app) { | |
| /** | |
| * Functions triggers appropriate requests on DBus | |
| */ | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/") | |
| .privileges(redfish::privileges::headProcessorCollection) | |
| .methods(boost::beast::http::verb::head)( | |
| std::bind_front(handleProcessorCollectionHead, std::ref(app))); | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/") | |
| .privileges(redfish::privileges::getProcessorCollection) | |
| .methods(boost::beast::http::verb::get)( | |
| [&app](const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName) { | |
| query_param::Query delegated; | |
| query_param::QueryCapabilities capabilities = { | |
| #ifdef EFFICIENT_EXPAND_PROCESSORS_ENABLED | |
| .canDelegateExpandLevel = 6, | |
| #else | |
| .canDelegateExpandLevel = 0, | |
| #endif | |
| }; | |
| if (!redfish::setUpRedfishRouteWithDelegation( | |
| app, req, asyncResp, delegated, capabilities)) { | |
| return; | |
| } | |
| redfish::system_utils::getSystemInformation( | |
| asyncResp, systemName, | |
| [delegated](const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemPath) { | |
| handleProcessorCollection(asyncResp, delegated, systemPath); | |
| }); | |
| }); | |
| } | |
| inline void getProcessorStateCollection( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName, const std::string& processorId) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| constexpr std::array<std::string_view, 1> procStateIntf = { | |
| "xyz.openbmc_project.Inventory.Item.ProcessorState"}; | |
| collection_util::getCollectionMembers( | |
| asyncResp, | |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", systemName, | |
| "Processors", processorId, "Oem", "Google", | |
| "ProcessorStates"), | |
| procStateIntf); | |
| } | |
| inline void handleProcessorGet( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName, const std::string& processorId) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| redfish::system_utils::getSystemInformation( | |
| asyncResp, systemName, | |
| [processorId](const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemPath) { | |
| getProcessorObject( | |
| asyncResp, processorId, systemPath, | |
| std::bind_front(getProcessorData, asyncResp, ""_json_pointer, 0, | |
| processorId, systemPath)); | |
| }); | |
| } | |
| inline void handleProcessorPatch( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName, const std::string& processorId) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| if (systemName != "system") { | |
| messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); | |
| return; | |
| } | |
| std::optional<nlohmann::json> appliedConfigJson; | |
| if (!json_util::readJsonPatch(req, asyncResp->res, "AppliedOperatingConfig", | |
| appliedConfigJson)) { | |
| return; | |
| } | |
| if (appliedConfigJson) { | |
| std::string appliedConfigUri; | |
| if (!json_util::readJson(*appliedConfigJson, asyncResp->res, "@odata.id", | |
| appliedConfigUri)) { | |
| return; | |
| } | |
| redfish::system_utils::getSystemInformation( | |
| asyncResp, systemName, | |
| [processorId, appliedConfigUri]( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemPath) { | |
| // Check for 404 and find matching D-Bus object, then run | |
| // property patch handlers if that all succeeds. | |
| getProcessorObject( | |
| asyncResp, processorId, systemPath, | |
| std::bind_front(patchAppliedOperatingConfig, asyncResp, | |
| processorId, appliedConfigUri)); | |
| }); | |
| } | |
| } | |
| inline void requestRoutesProcessor(App& app) { | |
| /** | |
| * Functions triggers appropriate requests on DBus | |
| */ | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") | |
| .privileges(redfish::privileges::headProcessor) | |
| .methods(boost::beast::http::verb::head)( | |
| std::bind_front(handleProcessorHead, std::ref(app))); | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") | |
| .privileges(redfish::privileges::getProcessor) | |
| .methods(boost::beast::http::verb::get)( | |
| std::bind_front(handleProcessorGet, std::ref(app))); | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") | |
| .privileges(redfish::privileges::patchProcessor) | |
| .methods(boost::beast::http::verb::patch)( | |
| std::bind_front(handleProcessorPatch, std::ref(app))); | |
| BMCWEB_ROUTE( | |
| app, | |
| "/redfish/v1/Systems/<str>/Processors/<str>/Oem/Google/ProcessorStates/") | |
| .privileges(redfish::privileges::getProcessor) | |
| .methods(boost::beast::http::verb::get)( | |
| std::bind_front(getProcessorStateCollection, std::ref(app))); | |
| BMCWEB_ROUTE(app, | |
| "/redfish/v1/Systems/<str>/Processors/<str>/Oem/Google/" | |
| "ProcessorStates/<str>/") | |
| .privileges(redfish::privileges::getProcessor) | |
| .methods(boost::beast::http::verb::get)( | |
| std::bind_front(handleGetProcessorState, std::ref(app))); | |
| } | |
| inline void requestRoutesSubProcessorCoreCollection(App& app) { | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/SubProcessors/") | |
| .privileges(redfish::privileges::headProcessorCollection) | |
| .methods(boost::beast::http::verb::head)( | |
| std::bind_front(handleSubProcessorCoreCollectionHead, std::ref(app))); | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/SubProcessors/") | |
| .privileges(redfish::privileges::getProcessorCollection) | |
| .methods(boost::beast::http::verb::get)( | |
| [&app](const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName, | |
| const std::string& processorId) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG | |
| << "Get available system sub processor core members."; | |
| redfish::system_utils::getSystemInformation( | |
| asyncResp, systemName, | |
| [processorId, systemName]( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemPath) { | |
| getProcessorPaths( | |
| asyncResp, systemPath, processorId, | |
| std::bind_front(getSubProcessorCoreMembers, asyncResp, | |
| systemName, processorId)); | |
| }); | |
| }); | |
| } | |
| inline void requestRoutesSubProcessorCore(App& app) { | |
| BMCWEB_ROUTE( | |
| app, "/redfish/v1/Systems/<str>/Processors/<str>/SubProcessors/<str>/") | |
| .privileges(redfish::privileges::headProcessor) | |
| .methods(boost::beast::http::verb::head)( | |
| std::bind_front(handleSubProcessorCoreHead, std::ref(app))); | |
| BMCWEB_ROUTE( | |
| app, "/redfish/v1/Systems/<str>/Processors/<str>/SubProcessors/<str>/") | |
| .privileges(redfish::privileges::getProcessor) | |
| .methods(boost::beast::http::verb::get)( | |
| [&app](const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName, const std::string& processorId, | |
| const std::string& coreId) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG | |
| << "Get available system sub processor core resources."; | |
| redfish::system_utils::getSystemInformation( | |
| asyncResp, systemName, | |
| [processorId, coreId, systemName]( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemPath) { | |
| getProcessorPaths( | |
| asyncResp, systemPath, processorId, | |
| std::bind_front(getSubProcessorCoreData, asyncResp, | |
| systemName, processorId, coreId)); | |
| }); | |
| }); | |
| } | |
| inline void requestRoutesSubProcessorThreadCollection(App& app) { | |
| BMCWEB_ROUTE(app, | |
| "/redfish/v1/Systems/<str>/Processors/<str>/SubProcessors/<str>/" | |
| "SubProcessors/") | |
| .privileges(redfish::privileges::headProcessorCollection) | |
| .methods(boost::beast::http::verb::head)(std::bind_front( | |
| handleSubProcessorThreadCollectionHead, std::ref(app))); | |
| BMCWEB_ROUTE(app, | |
| "/redfish/v1/Systems/<str>/Processors/<str>/SubProcessors/<str>/" | |
| "SubProcessors/") | |
| .privileges(redfish::privileges::getProcessorCollection) | |
| .methods(boost::beast::http::verb::get)( | |
| [&app](const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName, const std::string& processorId, | |
| const std::string& coreId) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| redfish::system_utils::getSystemInformation( | |
| asyncResp, systemName, | |
| [processorId, coreId, systemName]( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemPath) { | |
| getSubProcessorCorePaths( | |
| asyncResp, systemPath, processorId, coreId, | |
| std::bind_front(getSubProcessorThreadMembers, asyncResp, | |
| systemName, processorId, coreId)); | |
| }); | |
| }); | |
| } | |
| inline void requestRoutesSubProcessorThread(App& app) { | |
| BMCWEB_ROUTE(app, | |
| "/redfish/v1/Systems/<str>/Processors/<str>/SubProcessors/<str>/" | |
| "SubProcessors/<str>/") | |
| .privileges(redfish::privileges::headProcessor) | |
| .methods(boost::beast::http::verb::head)( | |
| std::bind_front(handleSubProcessorThreadHead, std::ref(app))); | |
| BMCWEB_ROUTE(app, | |
| "/redfish/v1/Systems/<str>/Processors/<str>/SubProcessors/<str>/" | |
| "SubProcessors/<str>/") | |
| .privileges(redfish::privileges::getProcessor) | |
| .methods(boost::beast::http::verb::get)( | |
| [&app](const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName, const std::string& processorId, | |
| const std::string& coreId, const std::string& threadId) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| redfish::system_utils::getSystemInformation( | |
| asyncResp, systemName, | |
| [processorId, coreId, threadId, systemName]( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemPath) { | |
| getSubProcessorCorePaths( | |
| asyncResp, systemPath, processorId, coreId, | |
| std::bind_front(getSubProcessorThreadData, asyncResp, | |
| systemName, processorId, coreId, | |
| threadId)); | |
| }); | |
| }); | |
| } | |
| } // namespace redfish | |
| #endif // THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_PROCESSOR_H_ |