blob: 057efa08744f8938b422328725422bc76d4c5f32 [file] [log] [blame] [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.
*/
#include "config.h"
#include "cmd_helper.hpp"
#include "requester/mctp.h"
#include "xyz/openbmc_project/Common/error.hpp"
#include <linux/mctp.h>
#include <systemd/sd-bus.h>
#include <sdbusplus/server.hpp>
#include <xyz/openbmc_project/Logging/Entry/server.hpp>
#include <exception>
using namespace utils;
namespace nsmtool
{
namespace helper
{
/*
* Initialize the socket, send nsm command & recieve response from socket
*
*/
int mctpSockSendRecv(const std::vector<uint8_t>& requestMsg,
std::vector<uint8_t>& responseMsg, bool verbose)
{
const char devPath[] = "\0mctp-pcie-mux";
int returnCode = 0;
int sockFd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (-1 == sockFd)
{
returnCode = -errno;
std::cerr << "Failed to create the socket : RC = " << sockFd << "\n";
return returnCode;
}
Logger(verbose, "Success in creating the socket : RC = ", sockFd);
struct sockaddr_un addr{};
addr.sun_family = AF_UNIX;
memcpy(addr.sun_path, devPath, sizeof(devPath) - 1);
CustomFD socketFd(sockFd);
int result = connect(socketFd(), reinterpret_cast<struct sockaddr*>(&addr),
sizeof(devPath) + sizeof(addr.sun_family) - 1);
if (-1 == result)
{
returnCode = -errno;
std::cerr << "Failed to connect to socket : RC = " << returnCode
<< "\n";
return returnCode;
}
Logger(verbose, "Success in connecting to socket : RC = ", returnCode);
auto mctpMsgType = MCTP_MSG_TYPE_PCI_VDM;
result = write(socketFd(), &mctpMsgType, sizeof(mctpMsgType));
if (-1 == result)
{
returnCode = -errno;
std::cerr << "Failed to send message type as VDM to mctp : RC = "
<< returnCode << "\n";
return returnCode;
}
Logger(verbose, "Success in sending message type as VDM to mctp : RC = ",
returnCode);
result = send(socketFd(), requestMsg.data(), requestMsg.size(), 0);
if (-1 == result)
{
returnCode = -errno;
std::cerr << "Write to socket failure : RC = " << returnCode << "\n";
return returnCode;
}
Logger(verbose, "Write to socket successful : RC = ", result);
// Read the response from socket
ssize_t peekedLength = recv(socketFd(), nullptr, 0, MSG_TRUNC | MSG_PEEK);
if (0 == peekedLength)
{
std::cerr << "Socket is closed : peekedLength = " << peekedLength
<< "\n";
return returnCode;
}
else if (peekedLength <= -1)
{
returnCode = -errno;
std::cerr << "recv() system call failed : RC = " << returnCode << "\n";
return returnCode;
}
else
{
auto reqhdr = reinterpret_cast<const nsm_msg_hdr*>(&requestMsg[2]);
do
{
auto peekedLength = recv(socketFd(), nullptr, 0,
MSG_PEEK | MSG_TRUNC);
if (-1 == peekedLength)
{
returnCode = -errno;
std::cerr << "Failed to recv message length : RC = "
<< returnCode << "\n";
return returnCode;
}
responseMsg.resize(peekedLength);
auto recvDataLength =
recv(socketFd(), reinterpret_cast<void*>(responseMsg.data()),
peekedLength, 0);
auto resphdr =
reinterpret_cast<const nsm_msg_hdr*>(&responseMsg[2]);
if (recvDataLength == peekedLength &&
resphdr->instance_id == reqhdr->instance_id &&
resphdr->request == 0)
{
Logger(verbose, "Total length:", recvDataLength);
break;
}
else if (recvDataLength != peekedLength)
{
std::cerr << "Failure to read response length packet: length = "
<< recvDataLength << "\n";
return returnCode;
}
} while (1);
}
returnCode = shutdown(socketFd(), SHUT_RDWR);
if (-1 == returnCode)
{
returnCode = -errno;
std::cerr << "Failed to shutdown the socket : RC = " << returnCode
<< "\n";
return returnCode;
}
Logger(verbose, "Shutdown Socket successful : RC = ", returnCode);
return NSM_SW_SUCCESS;
}
/*
* Initialize the socket, send nsm command & recieve response from socket with
* in-kernel MCTP
*
*/
int inKernelMctpSockSendRecv(const std::vector<uint8_t>& requestMsg,
std::vector<uint8_t>& responseMsg, bool verbose)
{
int returnCode = 0;
int sockFd = socket(AF_MCTP, SOCK_DGRAM, 0);
if (-1 == sockFd)
{
returnCode = -errno;
std::cerr << "Failed to create the socket : RC = " << sockFd << "\n";
return returnCode;
}
Logger(verbose, "Success in creating the socket : RC = ", sockFd);
CustomFD socketFd(sockFd);
struct sockaddr_mctp addr;
memset(&addr, 0, sizeof(addr));
addr.smctp_family = AF_MCTP;
addr.smctp_network = MCTP_NET_ANY;
addr.smctp_addr.s_addr = requestMsg[1];
addr.smctp_tag = MCTP_TAG_OWNER;
addr.smctp_type = requestMsg[2];
int result = sendto(socketFd(), &requestMsg[3], requestMsg.size() - 3, 0,
(struct sockaddr*)&addr, sizeof(addr));
if (-1 == result)
{
returnCode = -errno;
std::cerr << "Failed to send message type as VDM to mctp : RC = "
<< returnCode << "\n";
return returnCode;
}
Logger(verbose, "Success in sending message type as VDM to mctp : RC = ",
returnCode);
// Read the response from socket
ssize_t peekedLength = recv(socketFd(), nullptr, 0, MSG_TRUNC | MSG_PEEK);
if (0 == peekedLength)
{
std::cerr << "Socket is closed : peekedLength = " << peekedLength
<< "\n";
return returnCode;
}
else if (peekedLength <= -1)
{
returnCode = -errno;
std::cerr << "recv() system call failed : RC = " << returnCode << "\n";
return returnCode;
}
else
{
auto reqhdr = reinterpret_cast<const nsm_msg_hdr*>(&requestMsg[3]);
do
{
auto peekedLength = recv(socketFd(), nullptr, 0,
MSG_PEEK | MSG_TRUNC);
if (-1 == peekedLength)
{
returnCode = -errno;
std::cerr << "Failed to recv message length : RC = "
<< returnCode << "\n";
return returnCode;
}
responseMsg.resize(peekedLength);
struct sockaddr_mctp addr;
socklen_t addrlen;
addrlen = sizeof(addr);
memset(&addr, 0, sizeof(addr));
ssize_t recvDataLength = recvfrom(
socketFd(), reinterpret_cast<void*>(responseMsg.data()),
peekedLength, 0, (struct sockaddr*)&addr, &addrlen);
auto resphdr =
reinterpret_cast<const nsm_msg_hdr*>(responseMsg.data());
if (recvDataLength == peekedLength &&
resphdr->instance_id == reqhdr->instance_id &&
resphdr->request == 0)
{
return NSM_SW_SUCCESS;
}
else if (recvDataLength != peekedLength)
{
std::cerr << "Failure to read response length packet: length = "
<< recvDataLength << "\n";
return returnCode;
}
} while (1);
}
}
// parser for bitField variable
void parseBitfieldVar(ordered_json& res, const std::string& key,
const bitfield8_t* value, uint8_t size)
{
for (uint8_t i = 0; i < size; i++)
{
if (value[i].bits.bit0)
{
res[key].push_back((i * 8) + 0);
}
if (value[i].bits.bit1)
{
res[key].push_back((i * 8) + 1);
}
if (value[i].bits.bit2)
{
res[key].push_back((i * 8) + 2);
}
if (value[i].bits.bit3)
{
res[key].push_back((i * 8) + 3);
}
if (value[i].bits.bit4)
{
res[key].push_back((i * 8) + 4);
}
if (value[i].bits.bit5)
{
res[key].push_back((i * 8) + 5);
}
if (value[i].bits.bit6)
{
res[key].push_back((i * 8) + 6);
}
if (value[i].bits.bit7)
{
res[key].push_back((i * 8) + 7);
}
}
}
void CommandInterface::exec()
{
instanceId = 0;
auto [rc, requestMsgs] = createRequestMsgs();
if (rc != NSM_SW_SUCCESS)
{
std::cerr << "Failed to encode request message for " << nsmType << ":"
<< commandName << " rc = " << rc << "\n";
return;
}
for (auto& msg : requestMsgs)
{
requestQueue.push_back(std::move(msg));
}
if (requestQueue.empty())
{
std::cerr << "No request messages to send for " << nsmType << ":"
<< commandName << "\n";
return;
}
while (!requestQueue.empty())
{
std::vector<uint8_t> request = std::move(requestQueue.front());
requestQueue.pop_front();
std::vector<uint8_t> response;
int rc2 = nsmSendRecv(request, response);
if (rc2 != NSM_SW_SUCCESS)
{
std::cerr << "nsmSendRecv: Failed to receive RC = " << rc2 << "\n";
return;
}
auto responsePtr = reinterpret_cast<struct nsm_msg*>(response.data());
parseResponseMsg(responsePtr, response.size());
}
}
std::tuple<int, int, std::vector<uint8_t>>
CommandInterface::getMctpSockInfo(uint8_t remoteEID)
{
std::set<dbus::Service> mctpCtrlServices;
int type = 0;
int protocol = 0;
std::vector<uint8_t> address{};
auto& bus = utils::DBusHandler::getBus();
const auto mctpEndpointIntfName{"xyz.openbmc_project.MCTP.Endpoint"};
const auto unixSocketIntfName{"xyz.openbmc_project.Common.UnixSocket"};
try
{
const dbus::Interfaces ifaceList{"xyz.openbmc_project.MCTP.Endpoint"};
auto getSubTreeResponse = utils::DBusHandler().getSubtree(
"/au/com/codeconstruct/mctp1", 0, ifaceList);
for (const auto& [objPath, mapperServiceMap] : getSubTreeResponse)
{
for (const auto& [serviceName, interfaces] : mapperServiceMap)
{
dbus::ObjectValueTree objects{};
auto method = bus.new_method_call(
serviceName.c_str(), "/au/com/codeconstruct/mctp1",
"org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
auto reply = bus.call(method);
reply.read(objects);
for (const auto& [objectPath, interfaces] : objects)
{
if (interfaces.contains(mctpEndpointIntfName))
{
const auto& mctpProperties =
interfaces.at(mctpEndpointIntfName);
auto eid = std::get<size_t>(mctpProperties.at("EID"));
if (remoteEID == eid)
{
if (interfaces.contains(unixSocketIntfName))
{
const auto& properties =
interfaces.at(unixSocketIntfName);
type = std::get<size_t>(properties.at("Type"));
protocol =
std::get<size_t>(properties.at("Protocol"));
address = std::get<std::vector<uint8_t>>(
properties.at("Address"));
if (address.empty() || !type)
{
address.clear();
return {0, 0, address};
}
else
{
return {type, protocol, address};
}
}
}
}
}
}
}
}
catch (const std::exception& e)
{
std::cerr << e.what() << "\n";
address.clear();
return {0, 0, address};
}
return {type, protocol, address};
}
int CommandInterface::nsmSendRecv(std::vector<uint8_t>& requestMsg,
std::vector<uint8_t>& responseMsg)
{
bool mctpVerbose = verbose;
// By default enable request/response msgs for nsmtool raw commands.
if (CommandInterface::nsmType == "raw")
{
mctpVerbose = true;
}
if (mctpVerbose)
{
std::cout << "nsmtool: ";
printBuffer(Tx, requestMsg, MCTP_MSG_TAG_REQ, mctpEid);
}
if (mctpEid != NSM_ENTITY_ID)
{
#ifdef MCTP_IN_KERNEL
std::vector<uint8_t> reqMsg{MCTP_MSG_TAG_REQ, mctpEid,
MCTP_MSG_TYPE_PCI_VDM};
reqMsg.insert(reqMsg.end(), requestMsg.begin(), requestMsg.end());
inKernelMctpSockSendRecv(reqMsg, responseMsg, mctpVerbose);
#else
auto [type, protocol, sockAddress] = getMctpSockInfo(mctpEid);
if (sockAddress.empty())
{
std::cerr << "nsmtool: Remote MCTP endpoint not found"
<< "\n";
return -1;
}
int rc = 0;
int sockFd = socket(AF_UNIX, type, protocol);
if (-1 == sockFd)
{
rc = -errno;
std::cerr << "Failed to create the socket : RC = " << sockFd
<< "\n";
return rc;
}
Logger(verbose, "Success in creating the socket : RC = ", sockFd);
CustomFD socketFd(sockFd);
struct sockaddr_un addr{};
addr.sun_family = AF_UNIX;
memcpy(addr.sun_path, sockAddress.data(), sockAddress.size());
rc = connect(sockFd, reinterpret_cast<struct sockaddr*>(&addr),
sockAddress.size() + sizeof(addr.sun_family));
if (-1 == rc)
{
rc = -errno;
std::cerr << "Failed to connect to socket : RC = " << rc << "\n";
return rc;
}
Logger(verbose, "Success in connecting to socket : RC = ", rc);
auto mctpMsgType = MCTP_MSG_TYPE_PCI_VDM;
rc = write(socketFd(), &mctpMsgType, sizeof(mctpMsgType));
if (-1 == rc)
{
rc = -errno;
std::cerr
<< "Failed to send message type as VDM to mctp demux daemon: RC = "
<< rc << "\n";
return rc;
}
Logger(
verbose,
"Success in sending message type as VDM to mctp demux daemon : RC = ",
rc);
uint8_t* responseMessage = nullptr;
size_t responseMessageSize{};
rc = nsm_send_recv(mctpEid, sockFd, requestMsg.data(),
requestMsg.size(), &responseMessage,
&responseMessageSize);
if (rc < 0)
{
std::cerr << "nsm_send_recv() failed RC = " << rc << "\n";
}
responseMsg.resize(responseMessageSize);
memcpy(responseMsg.data(), responseMessage, responseMsg.size());
free(responseMessage);
#endif
if (verbose)
{
std::cout << "nsmtool: ";
printBuffer(Rx, responseMsg, MCTP_TAG_NSM, mctpEid);
}
}
else
{
requestMsg.insert(requestMsg.begin(), MCTP_MSG_TAG_REQ);
requestMsg.insert(requestMsg.begin(), mctpEid);
requestMsg.insert(requestMsg.begin(), MCTP_MSG_TYPE_PCI_VDM);
#ifdef MCTP_IN_KERNEL
inKernelMctpSockSendRecv(requestMsg, responseMsg, mctpVerbose);
#else
mctpSockSendRecv(requestMsg, responseMsg, mctpVerbose);
responseMsg.erase(responseMsg.begin(),
responseMsg.begin() + 2 /* skip the mctp header */);
#endif
if (verbose)
{
std::cout << "nsmtool: ";
printBuffer(Rx, responseMsg, MCTP_TAG_NSM, mctpEid);
}
}
return NSM_SW_SUCCESS;
}
} // namespace helper
} // namespace nsmtool