blob: 3a19268a8cd4146b3b80344babaa23e57a923903 [file] [log] [blame]
#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"
#ifdef UNIT_TEST_BUILD
#include "test/g3/mock_managed_store.hpp" // NOLINT
#endif
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& 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& 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& 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& 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));
// TODO(edward): Support SubProcessors and ProcessorMetrics for Multi-Host
// as use-cases arise
if (!multiHost) {
if (expandLevel > 0) {
nlohmann::json::json_pointer subProcessorPtr = jsonPtr / "SubProcessors";
getSubProcessorCoreCollectionWithExpand(
aResp, subProcessorPtr, expandLevel - 1, processorId, objectPath,
subtree, managedObjectsMap);
} else {
aResp->res.jsonValue[jsonPtr]["SubProcessors"]["@odata.id"] =
crow::utility::urlFromPieces("redfish", "v1", "Systems", "system",
"Processors", processorId,
"SubProcessors");
}
if (expandLevel > 0) {
nlohmann::json::json_pointer metricsPtr = jsonPtr / "Metrics";
handleGetCpuMetrics(aResp, metricsPtr, processorId);
} else {
// Inserting the "Metrics" JSON stanza
std::string metricsUrl = aResp->res.jsonValue[jsonPtr]["@odata.id"];
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& processorId,
Handler&& handler) {
std::array<std::string_view, 1> interfaces{
"xyz.openbmc_project.Inventory.Item.Cpu"};
managedStore::ManagedObjectStoreContext requestContext(aResp);
managedStore::GetManagedObjectStore()->getSubTreePaths(
"/xyz/openbmc_project/inventory", 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& processorId, const std::string& coreId,
Handler&& handler) {
std::array<std::string_view, 1> interfaces{
"xyz.openbmc_project.Inventory.Item.Cpu"};
managedStore::ManagedObjectStoreContext requestContext(aResp);
managedStore::GetManagedObjectStore()->getSubTreePaths(
"/xyz/openbmc_project/inventory", 0, interfaces, requestContext,
[processorId, coreId, aResp{aResp},
handler{std::forward<Handler>(handler)},
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(
"/xyz/openbmc_project/inventory", 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& 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", "system", "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 (expandLevel > 0) {
nlohmann::json::json_pointer metricsPtr = jsonPtr / "Metrics";
handleGetCpuCoreThreadMetrics(aResp, metricsPtr, processorId, coreId,
threadId);
} else {
// Inserting the "Metrics" JSON stanza
std::string metricsUrl = aResp->res.jsonValue[jsonPtr]["@odata.id"];
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& 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}, 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, 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, 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& 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", "system", "Processors", processorId,
"SubProcessors", coreId, "SubProcessors");
aResp->res.jsonValue["Name"] = "SubProcessor Collection";
const boost::urls::url& subProcessorsPath = crow::utility::urlFromPieces(
"redfish", "v1", "Systems", "system", "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& 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", "system", "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, processorId, coreId, corePath,
subtree, managedObjectsMap);
} else {
aResp->res.jsonValue[jsonPtr]["SubProcessors"]["@odata.id"] =
crow::utility::urlFromPieces("redfish", "v1", "Systems", "system",
"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& 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}, 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, 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, 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& 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", "system",
"Processors", processorId, "SubProcessors");
aResp->res.jsonValue["Name"] = "SubProcessor Collection";
const boost::urls::url& subProcessorsPath =
crow::utility::urlFromPieces("redfish", "v1", "Systems", "system",
"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& 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", "system", "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, processorId,
coreId, threadId, threadIdToObjectPath[threadId],
subtree, managedObjectsMap);
} else {
aResp->res.jsonValue[threadMemberPtr]["@odata.id"] =
crow::utility::urlFromPieces(
"redfish", "v1", "Systems", "system", "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& 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", "system",
"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, processorId, coreId,
coreIdToObjectPath[coreId], subtree, managedObjectsMap);
} else {
aResp->res.jsonValue[coreMemberPtr]["@odata.id"] =
crow::utility::urlFromPieces("redfish", "v1", "Systems", "system",
"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
// 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& cpuArg) {
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
// There is one hook for each unique (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& cpuArg, const std::string& coreArg,
const std::string& threadArg) {
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& 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 = "/redfish/v1/Systems/system/Processors/";
url += cpu;
if (!core.empty()) {
url += "/SubProcessors/";
url += core;
}
if (!thread.empty()) {
url += "/SubProcessors/";
url += thread;
}
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& cpuArg) {
if (validateCpu(asyncResp, cpuArg) < 0) {
// Error reply already composed
return;
}
std::shared_ptr<external_storer::Hook> hook = rememberProcessorHook(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(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& 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(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(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, 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, 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;
}
if (systemName != "system") {
messages::resourceNotFound(asyncResp->res, "ComputerSystem",
systemName);
return;
}
BMCWEB_LOG_DEBUG
<< "Get available system sub processor core members.";
getProcessorPaths(asyncResp, processorId,
std::bind_front(getSubProcessorCoreMembers,
asyncResp, 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;
}
if (systemName != "system") {
messages::resourceNotFound(asyncResp->res, "ComputerSystem",
systemName);
return;
}
BMCWEB_LOG_DEBUG
<< "Get available system sub processor core resources.";
getProcessorPaths(asyncResp, processorId,
std::bind_front(getSubProcessorCoreData,
asyncResp, 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;
}
if (systemName != "system") {
messages::resourceNotFound(asyncResp->res, "ComputerSystem",
systemName);
return;
}
getSubProcessorCorePaths(
asyncResp, processorId, coreId,
std::bind_front(getSubProcessorThreadMembers, asyncResp,
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;
}
if (systemName != "system") {
messages::resourceNotFound(asyncResp->res, "ComputerSystem",
systemName);
return;
}
getSubProcessorCorePaths(
asyncResp, processorId, coreId,
std::bind_front(getSubProcessorThreadData, asyncResp,
processorId, coreId, threadId));
});
}
} // namespace redfish
#endif // THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_PROCESSOR_H_