blob: 9ea9afb817c004470c4956535335f8652b2785a0 [file] [edit]
/*
* SPDX-FileCopyrightText: Copyright (c) 2023-2024 NVIDIA CORPORATION &
* AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#pragma once
#include "config.h"
#include "common/types.hpp"
#include "nsmd/nsmDevice.hpp"
#include "nsmd/socket_handler.hpp"
#include "requester/mctp_endpoint_prober.hpp"
#include "requester/retry_backoff_utils.hpp"
#include <sdbusplus/asio/object_server.hpp>
#include <sdbusplus/bus/match.hpp>
#include <filesystem>
#include <initializer_list>
#include <map>
#include <optional>
#include <vector>
// # define ENABLE_ASSOCIATION_DISCOVERY
namespace mctp
{
using Active = bool;
using DeviceType = uint8_t;
using DeviceRole = uint8_t;
using InstanceNumber = uint8_t;
using MctpMedium = std::string;
using MctpBinding = std::string;
using ConfiguredPath = std::string;
using DiscoveredEIDs =
std::map<eid_t,
std::tuple<uuid_t, DeviceType, InstanceNumber, Active, MctpMedium,
MctpBinding, ConfiguredPath, std::optional<eid_t>>>;
using EidTable =
std::multimap<uuid_t, std::tuple<eid_t, MctpMedium, MctpBinding>>;
using RequesterHandler = requester::Handler<requester::Request>;
class MctpDiscovery
{
public:
MctpDiscovery() = delete;
MctpDiscovery(const MctpDiscovery&) = delete;
MctpDiscovery(MctpDiscovery&&) = delete;
MctpDiscovery& operator=(const MctpDiscovery&) = delete;
MctpDiscovery& operator=(MctpDiscovery&&) = delete;
~MctpDiscovery() = default;
static MctpDiscovery& getInstance()
{
if (!instance)
{
throw std::runtime_error(
"MctpDiscovery instance is not initialized yet");
}
return *instance;
}
static void
initialize(sdbusplus::bus::bus& bus, mctp_socket::Handler& handler,
std::shared_ptr<nsm::NsmMessageHandler> nsmMsgHandler,
EidTable& eidTable, nsm::NsmDeviceTable& nsmDevices,
sdbusplus::asio::object_server& objServer)
{
if (instance)
{
throw std::logic_error(
"Initialize called on an already initialized MctpDiscovery");
}
static MctpDiscovery inst(bus, handler, nsmMsgHandler, eidTable,
nsmDevices, objServer);
instance = &inst;
}
std::shared_ptr<nsm::NsmDevice> getNsmDeviceFromStaticUUID(uuid_t uuid);
std::shared_ptr<nsm::NsmDevice> getNsmDeviceFromEid(eid_t eid);
std::shared_ptr<nsm::NsmDevice>
getNsmDeviceByIdentification(uint8_t deviceType, uint8_t instanceNumber,
uint8_t deviceRole);
nsm::DiscoveryEvents& discoveryEvents(eid_t eid);
requester::Coroutine dumpPingInfoTask(eid_t eid);
static void logProberSummaries()
{
if (instance)
{
instance->prober.logAllSummaries();
}
}
private:
static MctpDiscovery* instance;
/** @brief Constructs the MCTP Discovery object to handle discovery of
* MCTP enabled devices
*
* @param[in] bus - reference to systemd bus
* @param[in] list - initializer list to the MctpDiscoveryHandlerIntf
*/
explicit MctpDiscovery(
sdbusplus::bus::bus& bus, mctp_socket::Handler& handler,
std::shared_ptr<nsm::NsmMessageHandler> nsmMsgHandler,
EidTable& eidTable, nsm::NsmDeviceTable& nsmDevices,
sdbusplus::asio::object_server& objServer);
/** @brief reference to the systemd bus */
sdbusplus::bus::bus& bus;
mctp_socket::Handler& handler;
std::shared_ptr<nsm::NsmMessageHandler> nsmMsgHandler;
EidTable& eidTable;
nsm::NsmDeviceTable& nsmDevices;
sdbusplus::asio::object_server& objServer;
// Dedicated prober to handle ping/query with backoff
requester::MctpEndpointProber prober;
/** @brief Used to watch for new MCTP endpoints */
sdbusplus::bus::match_t mctpEndpointAddedSignal;
/** @brief Used to watch for the removed MCTP endpoints */
sdbusplus::bus::match_t mctpEndpointRemovedSignal;
/** @brief map of Queue to store the pending property change signal from
* mctp service */
std::map<std::string, std::queue<sdbusplus::message::message>>
mctpQueuedSignals;
std::map<std::string, std::coroutine_handle<>> deviceStateChangeTaskHandles;
requester::Coroutine deviceStateChangeTask(const std::string path);
requester::Coroutine readMctpProperties(const std::string& objPath,
MctpInfos& mctpInfos);
void discoverEndpoints(sdbusplus::message::message& msg);
requester::Coroutine
handleDiscoverEndpoints(sdbusplus::message::message& msg,
MctpInfos& mctpInfos);
/**
* @brief matcher rule for property changes of
* xyz.openbmc_project.Object.Enable dbus object
*/
std::map<std::string, sdbusplus::bus::match_t> enableMatches;
/** @brief handler for mctpEndpointRemovedSignal */
void cleanEndpoints(sdbusplus::message::message& msg);
requester::Coroutine handleCleanEndpoints(sdbusplus::message::message& msg,
MctpInfos& mctpInfos);
requester::Coroutine
handleRefreshEndpoints(sdbusplus::message::message& msg,
MctpInfos& mctpInfos);
/**
* @brief A callback for propertiesChanges signal enabled matches matcher
* rule to invoke registered handlers (online/offline mctp endpoint)
*/
void refreshEndpoints(sdbusplus::message::message& msg);
/** @brief Process the D-Bus MCTP endpoint info and prepare data to be used
* for NSM discovery.
*
* @param[in] interfaces - MCTP D-Bus information
* @param[out] mctpInfos - MCTP info for NSM discovery
*/
void populateMctpInfo(const dbus::InterfaceMap& interfaces,
const std::string& objPath, MctpInfos& mctpInfos);
static constexpr uint8_t mctpTypeVDM = 0x7e;
/** @brief MCTP endpoint interface name */
const std::string mctpEndpointIntfName{"xyz.openbmc_project.MCTP.Endpoint"};
const std::string mctpBindingIntfName{"xyz.openbmc_project.MCTP.Binding"};
const std::string associationIntfName{
"xyz.openbmc_project.Association.Definitions"};
/** @brief UUID interface name */
static constexpr std::string_view uuidEndpointIntfName{
"xyz.openbmc_project.Common.UUID"};
/** @brief Unix Socket interface name */
static constexpr std::string_view unixSocketIntfName{
"xyz.openbmc_project.Common.UnixSocket"};
static constexpr std::string_view codeConstructEndpointIntfName{
"au.com.codeconstruct.MCTP.Endpoint1"};
std::queue<MctpInfos> queuedMctpInfos;
std::map<std::string, MctpInfo> cachedMctpInfoByPath;
std::map<eid_t, std::queue<MctpInfo>> perEidQueuedMctpInfos;
std::map<eid_t, std::coroutine_handle<>> perEidDiscoverNsmDeviceTaskHandle;
std::map<eid_t, std::shared_ptr<nsm::DiscoveryEvents>>
perEidDiscoveryEvents;
std::map<uint8_t, std::map<uint16_t, std::shared_ptr<nsm::NsmDevice>>>
deviceMap;
DiscoveredEIDs discoveredEIDs;
/** @brief Helper function to invoke registered handlers
*
* @param[in] mctpInfos - information of discovered MCTP endpoints
*/
void handleMctpEndpoints(const MctpInfos& mctpInfos);
requester::Coroutine
SendRecvNsmMsg(eid_t eid, Request& request,
std::shared_ptr<const nsm_msg>& responseMsg,
size_t* responseLen);
// Discovery methods
bool insertIntoEidTableifNotExist(
uuid_t uuid, const std::tuple<eid_t, MctpMedium, MctpBinding>& value);
void discoverNsmDevice(const MctpInfos& mctpInfos);
requester::Coroutine discoverNsmDeviceTask(eid_t eid);
requester::Coroutine coSetdeviceStateOnlineTask(const MctpInfos& mctpInfos);
requester::Coroutine
coSetdeviceStateOfflineTask(const MctpInfos& mctpInfos);
// Discovery helper methods
requester::Coroutine ping(eid_t eid);
requester::Coroutine
getQueryDeviceIdentification(eid_t eid, uint8_t& deviceIdentification,
uint8_t& deviceInstance);
void discoverAndUpdateNsmDeviceTask(
std::shared_ptr<nsm::NsmDevice> nsmDevice);
requester::Coroutine
updateNsmDeviceTask(std::shared_ptr<nsm::NsmDevice> nsmDevice);
// NsmDevice Creation methods
std::shared_ptr<nsm::NsmDevice>
findOrCreateNsmDevice(uint8_t deviceType, uint8_t deviceRole,
uint8_t instanceNumber, std::string remapPropName,
std::vector<std::string>& remapPropValues);
template <typename T>
bool containsValue(
const T& value,
const std::variant<std::vector<uint8_t>, std::vector<uuid_t>>&
remapPropValues) const;
std::shared_ptr<nsm::NsmDevice> mapNsmDeviceUsingEid(
eid_t eid, uuid_t mctpUuid, uint8_t deviceType, uint8_t instanceNumber,
std::string associatedPath, bool active, MctpMedium mctpMedium,
MctpBinding mctpBinding, std::optional<eid_t> localEid);
int mapMctpEIDForNsmDevice(std::shared_ptr<nsm::NsmDevice> nsmDevice);
void handleMctpStateTransition(const std::string objPath);
requester::Coroutine findConfiguredAssociations(
[[maybe_unused]] const std::string& objPath,
[[maybe_unused]] ConfiguredPath& configuredPath);
};
} // namespace mctp