Move commands from ipmi-providers
Many commands were in ipmi-providers and need to be moved into the
active development library (intel-ipmi-oem). This copies wholesale
those commands, even though many need to be rewritten to use the new
ipmi providers API.
Tested-by: build and see that the commands are still present even when
the ipmi-providers library is removed
Change-Id: If326f5d7844adeed7da2d3b7a2f1d3eeeea43b29
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
diff --git a/src/bmccontrolservices.cpp b/src/bmccontrolservices.cpp
new file mode 100644
index 0000000..2bebab5
--- /dev/null
+++ b/src/bmccontrolservices.cpp
@@ -0,0 +1,153 @@
+/*
+// 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 "oemcommands.hpp"
+
+#include <openssl/hmac.h>
+
+#include <ipmid/api.hpp>
+#include <ipmid/utils.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+
+void register_netfn_bmc_control_functions() __attribute__((constructor));
+
+enum ipmi_bmc_control_services_return_codes
+{
+ ipmiCCBmcControlInvalidBitMask = 0xCC,
+ ipmiCCBmcControlPasswdInvalid = 0xCD,
+ ipmiCCBmcControlInvalidChannel = 0xD4,
+};
+
+// TODO: Add other services, once they are supported
+static const std::unordered_map<uint8_t, std::string> bmcServices = {
+ {3, "netipmid"},
+ {5, "web"},
+ {6, "ssh"},
+};
+
+static constexpr const char* objectManagerIntf =
+ "org.freedesktop.DBus.ObjectManager";
+static constexpr const char* serviceConfigBasePath =
+ "/xyz/openbmc_project/control/service";
+static constexpr const char* serviceConfigAttrIntf =
+ "xyz.openbmc_project.Control.Service.Attributes";
+static constexpr const char* serviceStateProperty = "State";
+static std::string disableServiceValue = "disabled";
+
+static ipmi_ret_t disableBmcServices(const std::string& objName)
+{
+ std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
+ static std::string serviceCfgMgr{};
+ if (serviceCfgMgr.empty())
+ {
+ try
+ {
+ serviceCfgMgr = ipmi::getService(*dbus, objectManagerIntf,
+ serviceConfigBasePath);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ serviceCfgMgr.clear();
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error: In fetching disabling service manager name");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ }
+ auto path = std::string(serviceConfigBasePath) + "/" + objName;
+ try
+ {
+ ipmi::setDbusProperty(*dbus, serviceCfgMgr, path, serviceConfigAttrIntf,
+ serviceStateProperty,
+ ipmi::Value(disableServiceValue));
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Disabling service",
+ phosphor::logging::entry("PATH=%s", path.c_str()),
+ phosphor::logging::entry("MGR_NAME=%s", serviceCfgMgr.c_str()));
+ return IPMI_CC_OK;
+ }
+ catch (const sdbusplus::exception_t&)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error: Disabling service",
+ phosphor::logging::entry("PATH=%s", path.c_str()),
+ phosphor::logging::entry("MGR_NAME=%s", serviceCfgMgr.c_str()));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+}
+
+static constexpr size_t controlPasswdSize = 32;
+
+ipmi::RspType<> bmcIntelControlServices(
+ ipmi::Context::ptr ctx,
+ const std::array<uint8_t, controlPasswdSize>& passwd, uint8_t stdServices,
+ uint8_t oemServices)
+{
+ // Execute this command only in KCS interface
+ if (ctx->channel != interfaceKCS)
+ {
+ return ipmi::response(ipmiCCBmcControlInvalidChannel);
+ }
+
+ static std::string hashData("Intel 0penBMC");
+ static std::vector<uint8_t> hashedValue = {
+ 0x89, 0x6A, 0xAB, 0x7D, 0xB0, 0x5A, 0x2D, 0x92, 0x41, 0xAD, 0x92,
+ 0xEE, 0xD4, 0x82, 0xDE, 0x62, 0x66, 0x16, 0xC1, 0x08, 0xFD, 0x23,
+ 0xC6, 0xD8, 0x75, 0xB3, 0x52, 0x53, 0x31, 0x3C, 0x7F, 0x69};
+ std::vector<uint8_t> hashedOutput(EVP_MAX_MD_SIZE, 0);
+ unsigned int outputLen = 0;
+ HMAC(EVP_sha256(), passwd.data(), passwd.size(),
+ reinterpret_cast<const uint8_t*>(hashData.c_str()), hashData.length(),
+ &hashedOutput[0], &outputLen);
+ hashedOutput.resize(outputLen);
+
+ if (hashedOutput != hashedValue)
+ {
+ return ipmi::response(ipmiCCBmcControlPasswdInvalid);
+ }
+
+ if (stdServices == 0 && oemServices == 0)
+ {
+ return ipmi::response(ipmiCCBmcControlInvalidBitMask);
+ }
+
+ ipmi_ret_t retVal = IPMI_CC_OK;
+ for (size_t bitIndex = 0; bitIndex < 8; ++bitIndex)
+ {
+ if (stdServices & (1 << bitIndex))
+ {
+ auto it = bmcServices.find(bitIndex);
+ if (it == bmcServices.end())
+ {
+ return ipmi::response(ipmiCCBmcControlInvalidBitMask);
+ }
+ retVal = disableBmcServices(it->second);
+ if (retVal != IPMI_CC_OK)
+ {
+ return ipmi::response(retVal);
+ }
+ }
+ }
+ return ipmi::responseSuccess();
+}
+
+void register_netfn_bmc_control_functions()
+{
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, netfnIntcOEMGeneral,
+ static_cast<ipmi_cmd_t>(
+ IPMINetFnIntelOemGeneralCmds::BmcControlServices),
+ ipmi::Privilege::User, bmcIntelControlServices);
+}
diff --git a/src/bridgingcommands.cpp b/src/bridgingcommands.cpp
new file mode 100644
index 0000000..0c727f6
--- /dev/null
+++ b/src/bridgingcommands.cpp
@@ -0,0 +1,514 @@
+/*
+// 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 <ipmid/api.h>
+
+#include <bridgingcommands.hpp>
+#include <cstring>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <sdbusplus/message.hpp>
+#include <vector>
+
+static constexpr const char *ipmbBus = "xyz.openbmc_project.Ipmi.Channel.Ipmb";
+static constexpr const char *ipmbObj = "/xyz/openbmc_project/Ipmi/Channel/Ipmb";
+static constexpr const char *ipmbIntf = "org.openbmc.Ipmb";
+
+static Bridging bridging;
+
+/**
+ * @brief utils for checksum
+ */
+static bool ipmbChecksumValidate(uint8_t *data, uint8_t length)
+{
+ if (data == nullptr)
+ {
+ return false;
+ }
+
+ uint8_t checksum = 0;
+
+ for (uint8_t idx = 0; idx < length; idx++)
+ {
+ checksum += data[idx];
+ }
+
+ if (0 == checksum)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+static uint8_t ipmbChecksumCompute(uint8_t *data, uint8_t length)
+{
+ if (data == nullptr)
+ {
+ return 0;
+ }
+
+ uint8_t checksum = 0;
+
+ for (uint8_t idx = 0; idx < length; idx++)
+ {
+ checksum += data[idx];
+ }
+
+ checksum = (~checksum) + 1;
+ return checksum;
+}
+
+static inline bool ipmbConnectionHeaderChecksumValidate(ipmbHeader *ipmbHeader)
+{
+ return ipmbChecksumValidate(reinterpret_cast<uint8_t *>(ipmbHeader),
+ ipmbConnectionHeaderLength);
+}
+
+static inline bool ipmbDataChecksumValidate(ipmbHeader *ipmbHeader,
+ uint8_t length)
+{
+ return ipmbChecksumValidate(
+ (reinterpret_cast<uint8_t *>(ipmbHeader) + ipmbConnectionHeaderLength),
+ (length - ipmbConnectionHeaderLength));
+}
+
+static bool isFrameValid(ipmbHeader *frame, uint8_t length)
+{
+ if ((length < ipmbMinFrameLength) || (length > ipmbMaxFrameLength))
+ {
+ return false;
+ }
+
+ if (false == ipmbConnectionHeaderChecksumValidate(frame))
+ {
+ return false;
+ }
+
+ if (false == ipmbDataChecksumValidate(frame, length))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+IpmbRequest::IpmbRequest(const ipmbHeader *ipmbBuffer, size_t bufferLength)
+{
+ address = ipmbBuffer->Header.Req.address;
+ netFn = ipmbNetFnGet(ipmbBuffer->Header.Req.rsNetFnLUN);
+ rsLun = ipmbLunFromNetFnLunGet(ipmbBuffer->Header.Req.rsNetFnLUN);
+ rqSA = ipmbBuffer->Header.Req.rqSA;
+ seq = ipmbSeqGet(ipmbBuffer->Header.Req.rqSeqLUN);
+ rqLun = ipmbLunFromSeqLunGet(ipmbBuffer->Header.Req.rqSeqLUN);
+ cmd = ipmbBuffer->Header.Req.cmd;
+
+ size_t dataLength =
+ bufferLength - (ipmbConnectionHeaderLength +
+ ipmbRequestDataHeaderLength + ipmbChecksumSize);
+
+ if (dataLength > 0)
+ {
+ data.insert(data.end(), ipmbBuffer->Header.Req.data,
+ &ipmbBuffer->Header.Req.data[dataLength]);
+ }
+}
+
+IpmbResponse::IpmbResponse(uint8_t address, uint8_t netFn, uint8_t rqLun,
+ uint8_t rsSA, uint8_t seq, uint8_t rsLun,
+ uint8_t cmd, uint8_t completionCode,
+ std::vector<uint8_t> &inputData) :
+ address(address),
+ netFn(netFn), rqLun(rqLun), rsSA(rsSA), seq(seq), rsLun(rsLun), cmd(cmd),
+ completionCode(completionCode)
+{
+ data.reserve(ipmbMaxDataSize);
+
+ if (inputData.size() > 0)
+ {
+ data = std::move(inputData);
+ }
+}
+
+void IpmbResponse::ipmbToi2cConstruct(uint8_t *buffer, size_t *bufferLength)
+{
+ ipmbHeader *ipmbBuffer = (ipmbHeader *)buffer;
+
+ ipmbBuffer->Header.Resp.address = address;
+ ipmbBuffer->Header.Resp.rqNetFnLUN = ipmbNetFnLunSet(netFn, rqLun);
+ ipmbBuffer->Header.Resp.rsSA = rsSA;
+ ipmbBuffer->Header.Resp.rsSeqLUN = ipmbSeqLunSet(seq, rsLun);
+ ipmbBuffer->Header.Resp.cmd = cmd;
+ ipmbBuffer->Header.Resp.completionCode = completionCode;
+
+ ipmbBuffer->Header.Resp.checksum1 = ipmbChecksumCompute(
+ buffer, ipmbConnectionHeaderLength - ipmbChecksumSize);
+
+ if (data.size() > 0)
+ {
+ std::copy(
+ data.begin(), data.end(),
+ &buffer[ipmbConnectionHeaderLength + ipmbResponseDataHeaderLength]);
+ }
+
+ *bufferLength = data.size() + ipmbResponseDataHeaderLength +
+ ipmbConnectionHeaderLength + ipmbChecksumSize;
+
+ buffer[*bufferLength - ipmbChecksumSize] =
+ ipmbChecksumCompute(&buffer[ipmbChecksum2StartOffset],
+ (ipmbResponseDataHeaderLength + data.size()));
+}
+
+void IpmbRequest::prepareRequest(sdbusplus::message::message &mesg)
+{
+ mesg.append(ipmbMeChannelNum, netFn, rqLun, cmd, data);
+}
+
+Bridging::Bridging() : dbus(ipmid_get_sd_bus_connection())
+{
+}
+
+ipmi_return_codes Bridging::handleIpmbChannel(sSendMessageReq *sendMsgReq,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen)
+{
+ if ((*dataLen < (sizeof(sSendMessageReq) + ipmbMinFrameLength)) ||
+ (*dataLen > (sizeof(sSendMessageReq) + ipmbMaxFrameLength)))
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ auto sendMsgReqData = reinterpret_cast<ipmbHeader *>(sendMsgReq->data);
+
+ // TODO: check privilege lvl. Bridging to ME requires Administrator lvl
+
+ // allow bridging to ME only
+ if (sendMsgReqData->Header.Req.address != ipmbMeSlaveAddress)
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "handleIpmbChannel, IPMB address invalid");
+ *dataLen = 0;
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ // check allowed modes
+ if (sendMsgReq->modeGet() != modeNoTracking &&
+ sendMsgReq->modeGet() != modeTrackRequest)
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "handleIpmbChannel, mode not supported");
+ *dataLen = 0;
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ // check if request contains valid IPMB frame
+ if (!isFrameValid(sendMsgReqData, (*dataLen - sizeof(sSendMessageReq))))
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "handleIpmbChannel, IPMB frame invalid");
+ *dataLen = 0;
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ auto ipmbRequest =
+ IpmbRequest(sendMsgReqData, (*dataLen - sizeof(sSendMessageReq)));
+
+ std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
+ ipmbResponse;
+
+ // send request to IPMB
+ try
+ {
+ auto mesg =
+ dbus.new_method_call(ipmbBus, ipmbObj, ipmbIntf, "sendRequest");
+ ipmbRequest.prepareRequest(mesg);
+ auto ret = dbus.call(mesg);
+ ret.read(ipmbResponse);
+ }
+ catch (sdbusplus::exception::SdBusError &e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "handleIpmbChannel, dbus call exception");
+ *dataLen = 0;
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ std::vector<uint8_t> dataReceived(0);
+ int status = -1;
+ uint8_t netFn = 0, lun = 0, cmd = 0, cc = 0;
+
+ std::tie(status, netFn, lun, cmd, cc, dataReceived) = ipmbResponse;
+
+ auto respReceived =
+ IpmbResponse(ipmbRequest.rqSA, netFn, lun, ipmbRequest.address,
+ ipmbRequest.seq, lun, cmd, cc, dataReceived);
+
+ // check IPMB layer status
+ if (status)
+ {
+ phosphor::logging::log<phosphor::logging::level::WARNING>(
+ "handleIpmbChannel, ipmb returned non zero status");
+ *dataLen = 0;
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ auto sendMsgRes = reinterpret_cast<uint8_t *>(response);
+
+ switch (sendMsgReq->modeGet())
+ {
+ case modeNoTracking:
+ if (responseQueue.size() == responseQueueMaxSize)
+ {
+ *dataLen = 0;
+ return IPMI_CC_BUSY;
+ }
+ responseQueue.insert(responseQueue.end(), std::move(respReceived));
+ *dataLen = 0;
+ return IPMI_CC_OK;
+
+ break;
+ case modeTrackRequest:
+ respReceived.ipmbToi2cConstruct(sendMsgRes, dataLen);
+ return IPMI_CC_OK;
+
+ break;
+ default:
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "handleIpmbChannel, mode not supported");
+ *dataLen = 0;
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ *dataLen = 0;
+ return IPMI_CC_UNSPECIFIED_ERROR;
+}
+
+ipmi_return_codes Bridging::sendMessageHandler(ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen)
+{
+ ipmi_return_codes retCode = IPMI_CC_OK;
+
+ if (*dataLen < sizeof(sSendMessageReq))
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ auto sendMsgReq = reinterpret_cast<sSendMessageReq *>(request);
+
+ // check message fields:
+ // encryption not supported
+ if (sendMsgReq->encryptionGet() != 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "sendMessageHandler, encryption not supported");
+ *dataLen = 0;
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ // authentication not supported
+ if (sendMsgReq->authenticationGet() != 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "sendMessageHandler, authentication not supported");
+ *dataLen = 0;
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ switch (sendMsgReq->channelNumGet())
+ {
+ // we only handle ipmb for now
+ case targetChannelIpmb:
+ case targetChannelOtherLan:
+ retCode = handleIpmbChannel(sendMsgReq, response, dataLen);
+ break;
+ // fall through to default
+ case targetChannelIcmb10:
+ case targetChannelIcmb09:
+ case targetChannelLan:
+ case targetChannelSerialModem:
+ case targetChannelPciSmbus:
+ case targetChannelSmbus10:
+ case targetChannelSmbus20:
+ case targetChannelSystemInterface:
+ default:
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "sendMessageHandler, TargetChannel invalid");
+ *dataLen = 0;
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ return retCode;
+}
+
+ipmi_return_codes Bridging::getMessageHandler(ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen)
+{
+ if (*dataLen != 0)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ auto getMsgRes = reinterpret_cast<sGetMessageRes *>(response);
+ auto getMsgResData = static_cast<uint8_t *>(getMsgRes->data);
+
+ std::memset(getMsgRes, 0, sizeof(sGetMessageRes));
+
+ auto respQueueItem = responseQueue.begin();
+
+ if (respQueueItem == responseQueue.end())
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "getMessageHandler, no data available");
+ *dataLen = 0;
+ return ipmiGetMessageCmdDataNotAvailable;
+ }
+
+ // set message fields
+ getMsgRes->privilegeLvlSet(SYSTEM_INTERFACE);
+ getMsgRes->channelNumSet(targetChannelSystemInterface);
+
+ // construct response
+ respQueueItem->ipmbToi2cConstruct(getMsgResData, dataLen);
+ responseQueue.erase(respQueueItem);
+
+ *dataLen = *dataLen + sizeof(sGetMessageRes);
+ return IPMI_CC_OK;
+}
+
+ipmi_return_codes Bridging::getMessageFlagsHandler(ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen)
+{
+ if (*dataLen != 0)
+ {
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ auto getMsgFlagsRes = reinterpret_cast<sGetMessageFlagsResp *>(response);
+
+ std::memset(getMsgFlagsRes, 0, sizeof(sGetMessageFlagsResp));
+
+ // preserve current (legacy) behaviour
+ getMsgFlagsRes->eventMessageBitSet(1);
+
+ // set message fields
+ if (responseQueue.size() > 0)
+ {
+ getMsgFlagsRes->receiveMessageBitSet(1);
+ }
+ else
+ {
+ getMsgFlagsRes->receiveMessageBitSet(0);
+ }
+
+ *dataLen = sizeof(sGetMessageFlagsResp);
+
+ return IPMI_CC_OK;
+}
+
+ipmi_return_codes Bridging::clearMessageFlagsHandler(ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen)
+{
+ if (*dataLen != sizeof(sClearMessageFlagsReq))
+ {
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ auto clearMsgFlagsReq = reinterpret_cast<sClearMessageFlagsReq *>(request);
+
+ if (clearMsgFlagsReq->receiveMessageBitGet() == 1)
+ {
+ responseQueue.clear();
+ }
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ipmiAppSendMessage(ipmi_netfn_t netFn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t dataLen, ipmi_context_t context)
+{
+ ipmi_ret_t retCode = IPMI_CC_OK;
+ retCode = bridging.sendMessageHandler(request, response, dataLen);
+
+ return retCode;
+}
+
+ipmi_ret_t ipmiAppGetMessage(ipmi_netfn_t netFn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t dataLen, ipmi_context_t context)
+{
+ ipmi_ret_t retCode = IPMI_CC_OK;
+ retCode = bridging.getMessageHandler(request, response, dataLen);
+
+ return retCode;
+}
+
+ipmi_ret_t ipmiAppGetMessageFlags(ipmi_netfn_t netFn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ ipmi_ret_t retCode = IPMI_CC_OK;
+ retCode = bridging.getMessageFlagsHandler(request, response, dataLen);
+
+ return retCode;
+}
+
+ipmi_ret_t ipmiAppClearMessageFlags(ipmi_netfn_t netFn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ ipmi_ret_t retCode = IPMI_CC_OK;
+ retCode = bridging.clearMessageFlagsHandler(request, response, dataLen);
+
+ *dataLen = 0;
+
+ return retCode;
+}
+
+static void register_bridging_functions() __attribute__((constructor));
+static void register_bridging_functions()
+{
+ ipmi_register_callback(
+ NETFUN_APP, Bridging::IpmiAppBridgingCmds::ipmiCmdClearMessageFlags,
+ NULL, ipmiAppClearMessageFlags, PRIVILEGE_USER);
+
+ ipmi_register_callback(
+ NETFUN_APP, Bridging::IpmiAppBridgingCmds::ipmiCmdGetMessageFlags, NULL,
+ ipmiAppGetMessageFlags, PRIVILEGE_USER);
+
+ ipmi_register_callback(NETFUN_APP,
+ Bridging::IpmiAppBridgingCmds::ipmiCmdGetMessage,
+ NULL, ipmiAppGetMessage, PRIVILEGE_USER);
+
+ ipmi_register_callback(NETFUN_APP,
+ Bridging::IpmiAppBridgingCmds::ipmiCmdSendMessage,
+ NULL, ipmiAppSendMessage, PRIVILEGE_USER);
+
+ return;
+}
diff --git a/src/manufacturingcommands.cpp b/src/manufacturingcommands.cpp
new file mode 100644
index 0000000..7bbb970
--- /dev/null
+++ b/src/manufacturingcommands.cpp
@@ -0,0 +1,628 @@
+/*
+// 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 <boost/process/child.hpp>
+#include <ipmid/api.hpp>
+#include <manufacturingcommands.hpp>
+#include <oemcommands.hpp>
+
+namespace ipmi
+{
+
+Manufacturing mtm;
+
+static auto revertTimeOut =
+ std::chrono::duration_cast<std::chrono::microseconds>(
+ std::chrono::seconds(60)); // 1 minute timeout
+
+static constexpr const char* idButtonPath =
+ "/xyz/openbmc_project/Chassis/Buttons/ID0";
+static constexpr const char* idButtonInterface =
+ "xyz.openbmc_project.Chassis.Buttons.ID";
+static constexpr const char* idButtonMemberPressed = "Pressed";
+
+static constexpr const char* callbackMgrService =
+ "xyz.openbmc_project.CallbackManager";
+static constexpr const char* callbackMgrIntf =
+ "xyz.openbmc_project.CallbackManager";
+static constexpr const char* callbackMgrObjPath =
+ "/xyz/openbmc_project/CallbackManager";
+static constexpr const char* retriggerLedUpdate = "RetriggerLEDUpdate";
+
+const static constexpr char* systemDService = "org.freedesktop.systemd1";
+const static constexpr char* systemDObjPath = "/org/freedesktop/systemd1";
+const static constexpr char* systemDMgrIntf =
+ "org.freedesktop.systemd1.Manager";
+const static constexpr char* pidControlService = "phosphor-pid-control.service";
+
+// TODO: Temporary place to test the working code. Will be moved to
+// gpio daemon
+constexpr const char* passthroughPath = "/usr/bin/set-passthrough.sh";
+void disablePassthrough(bool value)
+{
+ boost::process::child c(passthroughPath, value ? "0" : "1");
+ c.wait();
+}
+
+ipmi_ret_t ledStoreAndSet(SmSignalSet signal, std::string setState)
+{
+ LedProperty* ledProp = mtm.findLedProperty(signal);
+ if (ledProp == nullptr)
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+
+ std::string ledName = ledProp->getName();
+ std::string ledService = ledServicePrefix + ledName;
+ std::string ledPath = ledPathPrefix + ledName;
+ ipmi::Value presentState;
+
+ if (false == ledProp->getLock())
+ {
+ if (mtm.getProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
+ "State", &presentState) != 0)
+ {
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ ledProp->setPrevState(std::get<std::string>(presentState));
+ ledProp->setLock(true);
+ if (signal == SmSignalSet::smPowerFaultLed ||
+ signal == SmSignalSet::smSystemReadyLed)
+ {
+ mtm.revertLedCallback = true;
+ }
+ }
+ if (mtm.setProperty(ledService.c_str(), ledPath.c_str(), ledIntf, "State",
+ ledStateStr + setState) != 0)
+ {
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t ledRevert(SmSignalSet signal)
+{
+ LedProperty* ledProp = mtm.findLedProperty(signal);
+ if (ledProp == nullptr)
+ {
+ return IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ if (true == ledProp->getLock())
+ {
+ ledProp->setLock(false);
+ if (signal == SmSignalSet::smPowerFaultLed ||
+ signal == SmSignalSet::smSystemReadyLed)
+ {
+ try
+ {
+ ipmi::method_no_args::callDbusMethod(
+ *getSdBus(), callbackMgrService, callbackMgrObjPath,
+ callbackMgrIntf, retriggerLedUpdate);
+ }
+ catch (sdbusplus::exception_t& e)
+ {
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ mtm.revertLedCallback = false;
+ }
+ else
+ {
+ std::string ledName = ledProp->getName();
+ std::string ledService = ledServicePrefix + ledName;
+ std::string ledPath = ledPathPrefix + ledName;
+ if (mtm.setProperty(ledService.c_str(), ledPath.c_str(), ledIntf,
+ "State", ledProp->getPrevState()) != 0)
+ {
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ }
+ }
+ return IPMI_CC_OK;
+}
+
+void Manufacturing::initData()
+{
+ gpioPaths[(uint8_t)SmSignalGet::smPowerButton] = "Power_Button";
+ gpioPaths[(uint8_t)SmSignalGet::smResetButton] = "Reset_Button";
+ gpioPaths[(uint8_t)SmSignalGet::smIdentifyButton] = "ID_Button";
+ gpioPaths[(uint8_t)SmSignalGet::smFpLcpEnterButton] = "Lcp_Enter_Button";
+ gpioPaths[(uint8_t)SmSignalGet::smFpLcpLeftButton] = "Lcp_Left_Button";
+ gpioPaths[(uint8_t)SmSignalGet::smFpLcpRightButton] = "Lcp_Right_Button";
+ gpioPaths[(uint8_t)SmSignalGet::smNmiButton] = "Nmi_Button";
+
+ ledPropertyList.push_back(
+ LedProperty(SmSignalSet::smPowerFaultLed, "status_amber"));
+ ledPropertyList.push_back(
+ LedProperty(SmSignalSet::smSystemReadyLed, "status_green"));
+ ledPropertyList.push_back(
+ LedProperty(SmSignalSet::smIdentifyLed, "identify"));
+}
+
+void Manufacturing::revertTimerHandler()
+{
+ for (const auto& signal : revertSmSignalGetVector)
+ {
+ mtm.setProperty(gpioService,
+ mtm.getGpioPathForSmSignal((uint8_t)signal), gpioIntf,
+ "Ignore", false);
+ }
+ revertSmSignalGetVector.clear();
+ disablePassthrough(false);
+ if (revertFanPWM)
+ {
+ revertFanPWM = false;
+ disablePidControlService(false);
+ }
+
+ for (const auto& ledProperty : ledPropertyList)
+ {
+ const std::string& ledName = ledProperty.getName();
+ ledRevert(ledProperty.getSignal());
+ }
+}
+
+Manufacturing::Manufacturing() :
+ revertTimer([&](void) { revertTimerHandler(); })
+{
+ initData();
+}
+
+int8_t Manufacturing::getProperty(const char* service, std::string path,
+ const char* interface,
+ std::string propertyName, ipmi::Value* reply)
+{
+ try
+ {
+ *reply = ipmi::getDbusProperty(*getSdBus(), service, path.c_str(),
+ interface, propertyName);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "ERROR: getProperty");
+ return -1;
+ }
+
+ return 0;
+}
+
+int8_t Manufacturing::setProperty(const char* service, std::string path,
+ const char* interface,
+ std::string propertyName, ipmi::Value value)
+{
+ try
+ {
+ ipmi::setDbusProperty(*getSdBus(), service, path.c_str(), interface,
+ propertyName, value);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "ERROR: setProperty");
+ return -1;
+ }
+
+ return 0;
+}
+
+int8_t Manufacturing::disablePidControlService(const bool disable)
+{
+ try
+ {
+ auto dbus = getSdBus();
+ auto method = dbus->new_method_call(systemDService, systemDObjPath,
+ systemDMgrIntf,
+ disable ? "StopUnit" : "StartUnit");
+ method.append(pidControlService, "replace");
+ auto reply = dbus->call(method);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "ERROR: phosphor-pid-control service start or stop failed");
+ return -1;
+ }
+ return 0;
+}
+
+std::tuple<uint8_t, ipmi_ret_t, uint8_t>
+ Manufacturing::proccessSignal(SmSignalGet signal, SmActionGet action)
+{
+ int8_t ret = 0;
+ uint8_t retCode = 0;
+ uint8_t dataLen = 0;
+ uint8_t value = 0;
+ ipmi::Value reply;
+
+ switch (action)
+ {
+ case SmActionGet::sample:
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "case SmActionGet::sample");
+ break;
+ case SmActionGet::ignore:
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "case SmActionGet::ignore");
+ if (std::find(revertSmSignalGetVector.begin(),
+ revertSmSignalGetVector.end(),
+ signal) == revertSmSignalGetVector.end())
+ {
+ // Todo: Needs to be replaced with pass-through of particular
+ // pin
+ disablePassthrough(true);
+ ret = mtm.setProperty(
+ gpioService, mtm.getGpioPathForSmSignal((uint8_t)signal),
+ gpioIntf, "Ignore", true);
+ if (ret < 0)
+ {
+ dataLen = 0;
+ retCode = IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ revertSmSignalGetVector.push_back(signal);
+ revertTimer.start(revertTimeOut);
+ }
+ }
+ break;
+ case SmActionGet::revert:
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "case SmActionGet::revert");
+ auto iter = std::find(revertSmSignalGetVector.begin(),
+ revertSmSignalGetVector.end(), signal);
+ if (iter != revertSmSignalGetVector.end())
+ {
+ ret = mtm.setProperty(
+ gpioService, mtm.getGpioPathForSmSignal((uint8_t)signal),
+ gpioIntf, "Ignore", false);
+ if (ret < 0)
+ {
+ dataLen = 0;
+ retCode = IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ revertSmSignalGetVector.erase(iter);
+ // Todo: Needs to be replaced with pass-through of particular
+ // pin
+ disablePassthrough(true);
+ if (revertSmSignalGetVector.size() == 0)
+ {
+ revertTimer.stop();
+ }
+ }
+ }
+ break;
+
+ default:
+ dataLen = 0;
+ retCode = IPMI_CC_INVALID_FIELD_REQUEST;
+ break;
+ }
+
+ if (ret == 0) // No error happend, cmd will return with gpio value
+ {
+ ret = mtm.getProperty(gpioService,
+ mtm.getGpioPathForSmSignal((uint8_t)signal),
+ gpioIntf, "SampledValue", &reply);
+ if (ret < 0)
+ {
+ dataLen = 0;
+ retCode = IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ else
+ {
+ dataLen = 1;
+ value = std::get<bool>(reply);
+ }
+ }
+
+ return std::make_tuple(dataLen, retCode, value);
+}
+
+ipmi_ret_t ipmi_app_mtm_get_signal(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t data_len,
+ ipmi_context_t context)
+{
+ ipmi_ret_t retCode = IPMI_CC_OK;
+ int8_t ret = 0;
+ GetSmSignalReq* pReq = NULL;
+ GetSmSignalRsp* pRsp = NULL;
+
+ pReq = static_cast<GetSmSignalReq*>(request);
+ pRsp = static_cast<GetSmSignalRsp*>(response);
+
+ ipmi::Value reply;
+
+ if ((*data_len == sizeof(*pReq)) &&
+ (mtm.getAccessLvl() >= MtmLvl::mtmAvailable))
+ {
+ switch (pReq->Signal)
+ {
+ case SmSignalGet::smFanPwmGet:
+ {
+ std::string fullPath =
+ fanPwmPath + std::to_string(pReq->Instance);
+ ret = mtm.getProperty(fanService, fullPath, fanIntf, "Value",
+ &reply);
+ if (ret < 0)
+ {
+ *data_len = 0;
+ retCode = IPMI_CC_INVALID_FIELD_REQUEST;
+ break;
+ }
+ *data_len = 1;
+ pRsp->SigVal = std::get<double>(reply);
+ }
+ break;
+ case SmSignalGet::smFanTachometerGet:
+ {
+ // Full path calculation pattern:
+ // Instance 1 path is
+ // /xyz/openbmc_project/sensors/fan_tach/Fan_1a Instance 2 path
+ // is /xyz/openbmc_project/sensors/fan_tach/Fan_1b Instance 3
+ // path is /xyz/openbmc_project/sensors/fan_tach/Fan_2a
+ // and so on...
+ std::string fullPath = fanTachPathPrefix;
+ std::string fanAb = (pReq->Instance % 2) == 0 ? "b" : "a";
+ if (0 == pReq->Instance)
+ {
+ *data_len = 0;
+ retCode = IPMI_CC_INVALID_FIELD_REQUEST;
+ break;
+ }
+ else if (0 == pReq->Instance / 2)
+ {
+ fullPath += std::string("1") + fanAb;
+ }
+ else
+ {
+ fullPath += std::to_string(pReq->Instance / 2) + fanAb;
+ }
+
+ ret = mtm.getProperty(fanService, fullPath, fanIntf, "Value",
+ &reply);
+ if (ret < 0)
+ {
+ *data_len = 0;
+ retCode = IPMI_CC_INVALID_FIELD_REQUEST;
+ break;
+ }
+
+ uint16_t value = std::get<double>(reply);
+ *data_len = sizeof(*pRsp);
+
+ pRsp->SigVal = FAN_PRESENT | FAN_SENSOR_PRESENT;
+ pRsp->SigVal1 = value & 0x00FF;
+ pRsp->SigVal2 = (value >> 8) & 0xFF;
+ }
+ break;
+ case SmSignalGet::smResetButton: // gpio32
+ case SmSignalGet::smPowerButton: // gpio34
+ case SmSignalGet::smFpLcpEnterButton: // gpio51
+ case SmSignalGet::smFpLcpLeftButton: // gpio52
+ case SmSignalGet::smFpLcpRightButton: // gpio53
+ case SmSignalGet::smNmiButton: // gpio217
+ case SmSignalGet::smIdentifyButton: // gpio218
+ std::tie(*data_len, retCode, pRsp->SigVal) =
+ mtm.proccessSignal(pReq->Signal, pReq->Action);
+ *data_len = sizeof(pRsp->SigVal);
+ break;
+ default:
+ *data_len = 0;
+ retCode = IPMI_CC_INVALID_FIELD_REQUEST;
+ break;
+ }
+ }
+ else
+ {
+ *data_len = 0;
+ retCode = IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ return retCode;
+}
+
+ipmi_ret_t ipmi_app_mtm_set_signal(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t data_len,
+ ipmi_context_t context)
+{
+ uint8_t ret = 0;
+ ipmi_ret_t retCode = IPMI_CC_OK;
+ SetSmSignalReq* pReq = static_cast<SetSmSignalReq*>(request);
+ std::string ledName;
+ /////////////////// Signal to led configuration ////////////////
+ // {SM_SYSTEM_READY_LED, STAT_GRN_LED}, GPIOS4 gpio148
+ // {SM_POWER_FAULT_LED, STAT_AMB_LED}, GPIOS5 gpio149
+ // {SM_IDENTIFY_LED, IDENTIFY_LED}, GPIOS6 gpio150
+ // {SM_SPEAKER, SPEAKER}, GPIOAB0 gpio216
+ /////////////////////////////////////////////////////////////////
+ if ((*data_len == sizeof(*pReq)) &&
+ (mtm.getAccessLvl() >= MtmLvl::mtmAvailable))
+ {
+ switch (pReq->Signal)
+ {
+ case SmSignalSet::smPowerFaultLed:
+ case SmSignalSet::smSystemReadyLed:
+ case SmSignalSet::smIdentifyLed:
+ switch (pReq->Action)
+ {
+ case SmActionSet::forceDeasserted:
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "case SmActionSet::forceDeasserted");
+
+ retCode =
+ ledStoreAndSet(pReq->Signal, std::string("Off"));
+ if (retCode != IPMI_CC_OK)
+ {
+ break;
+ }
+ mtm.revertTimer.start(revertTimeOut);
+ }
+ break;
+ case SmActionSet::forceAsserted:
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "case SmActionSet::forceAsserted");
+
+ retCode =
+ ledStoreAndSet(pReq->Signal, std::string("On"));
+ if (retCode != IPMI_CC_OK)
+ {
+ break;
+ }
+ mtm.revertTimer.start(revertTimeOut);
+ if (SmSignalSet::smPowerFaultLed == pReq->Signal)
+ {
+ // Deassert "system ready"
+ retCode =
+ ledStoreAndSet(SmSignalSet::smSystemReadyLed,
+ std::string("Off"));
+ if (retCode != IPMI_CC_OK)
+ {
+ break;
+ }
+ }
+ else if (SmSignalSet::smSystemReadyLed == pReq->Signal)
+ {
+ // Deassert "fault led"
+ retCode =
+ ledStoreAndSet(SmSignalSet::smPowerFaultLed,
+ std::string("Off"));
+ if (retCode != IPMI_CC_OK)
+ {
+ break;
+ }
+ }
+ }
+ break;
+ case SmActionSet::revert:
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "case SmActionSet::revert");
+ retCode = ledRevert(pReq->Signal);
+ if (retCode != IPMI_CC_OK)
+ {
+ break;
+ }
+ }
+ break;
+ default:
+ {
+ retCode = IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ break;
+ }
+ break;
+ case SmSignalSet::smFanPowerSpeed:
+ {
+ if (((pReq->Action == SmActionSet::forceAsserted) &&
+ (*data_len != sizeof(*pReq)) && (pReq->Value > 100)) ||
+ pReq->Instance == 0)
+ {
+ retCode = IPMI_CC_INVALID_FIELD_REQUEST;
+ break;
+ }
+ uint8_t pwmValue = 0;
+ switch (pReq->Action)
+ {
+ case SmActionSet::revert:
+ {
+ if (mtm.revertFanPWM)
+ {
+ ret = mtm.disablePidControlService(false);
+ if (ret < 0)
+ {
+ retCode = IPMI_CC_UNSPECIFIED_ERROR;
+ break;
+ }
+ mtm.revertFanPWM = false;
+ }
+ }
+ break;
+ case SmActionSet::forceAsserted:
+ {
+ pwmValue = pReq->Value;
+ } // fall-through
+ case SmActionSet::forceDeasserted:
+ {
+ if (!mtm.revertFanPWM)
+ {
+ ret = mtm.disablePidControlService(true);
+ if (ret < 0)
+ {
+ retCode = IPMI_CC_UNSPECIFIED_ERROR;
+ break;
+ }
+ mtm.revertFanPWM = true;
+ }
+ mtm.revertTimer.start(revertTimeOut);
+ std::string fanPwmInstancePath =
+ fanPwmPath + std::to_string(pReq->Instance);
+
+ ret = mtm.setProperty(
+ fanService, fanPwmInstancePath.c_str(), fanIntf,
+ "Value", static_cast<double>(pwmValue));
+ if (ret < 0)
+ {
+ retCode = IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ }
+ break;
+ default:
+ {
+ retCode = IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ break;
+ }
+ }
+ break;
+ default:
+ {
+ retCode = IPMI_CC_INVALID_FIELD_REQUEST;
+ }
+ break;
+ }
+ }
+ else
+ {
+ retCode = IPMI_CC_ILLEGAL_COMMAND;
+ }
+
+ *data_len = 0; // Only CC is return for SetSmSignal cmd
+ return retCode;
+}
+
+} // namespace ipmi
+
+void register_mtm_commands() __attribute__((constructor));
+void register_mtm_commands()
+{
+ ipmi_register_callback(
+ netfnIntcOEMGeneral,
+ static_cast<ipmi_cmd_t>(IPMINetFnIntelOemGeneralCmds::GetSmSignal),
+ NULL, ipmi::ipmi_app_mtm_get_signal, PRIVILEGE_USER);
+
+ ipmi_register_callback(
+ netfnIntcOEMGeneral,
+ static_cast<ipmi_cmd_t>(IPMINetFnIntelOemGeneralCmds::SetSmSignal),
+ NULL, ipmi::ipmi_app_mtm_set_signal, PRIVILEGE_USER);
+
+ return;
+}
diff --git a/src/smbioshandler.cpp b/src/smbioshandler.cpp
new file mode 100644
index 0000000..7510493
--- /dev/null
+++ b/src/smbioshandler.cpp
@@ -0,0 +1,501 @@
+/*
+// 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 <ipmid/api.h>
+
+#include <commandutils.hpp>
+#include <cstdint>
+#include <iostream>
+#include <ipmid/utils.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+#include <smbioshandler.hpp>
+#include <string>
+#include <vector>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+using InternalFailure =
+ sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+
+using level = phosphor::logging::level;
+
+constexpr const char* DBUS_PROPERTIES = "org.freedesktop.DBus.Properties";
+constexpr const char* MDRV1_PATH = "/xyz/openbmc_project/Smbios/MDR_V1";
+constexpr const char* MDRV1_INTERFACE = "xyz.openbmc_project.Smbios.MDR_V1";
+
+static void register_netfn_smbios_functions() __attribute__((constructor));
+static sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
+
+ipmi_ret_t cmd_region_status(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t data_len, ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const RegionStatusRequest*>(request);
+ std::vector<uint8_t> status;
+
+ if (*data_len != sizeof(RegionStatusRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ uint8_t regionId = requestData->regionId - 1;
+ *data_len = 0;
+
+ if (regionId >= maxMDRId)
+ {
+ phosphor::logging::log<level::ERR>("Invalid region");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ std::string service = ipmi::getService(bus, MDRV1_INTERFACE, MDRV1_PATH);
+
+ auto method = bus.new_method_call(service.c_str(), MDRV1_PATH,
+ MDRV1_INTERFACE, "RegionStatus");
+ method.append(regionId);
+ auto reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ phosphor::logging::log<level::ERR>(
+ "Error get region status",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", MDRV1_PATH));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ reply.read(status);
+
+ if (status.size() != sizeof(MDRState))
+ {
+ phosphor::logging::log<level::ERR>(
+ "Error get region status, return length invalid");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ *data_len = static_cast<size_t>(status.size());
+ auto dataOut = reinterpret_cast<uint8_t*>(response);
+ std::copy(&status[0], &status[*data_len], dataOut);
+ return IPMI_CC_OK;
+}
+
+int sdplus_mdrv1_get_property(
+ const std::string& name,
+ sdbusplus::message::variant<uint8_t, uint16_t>& value, std::string& service)
+{
+ auto method = bus.new_method_call(service.c_str(), MDRV1_PATH,
+ DBUS_PROPERTIES, "Get");
+ method.append(MDRV1_INTERFACE, name);
+ auto reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ phosphor::logging::log<level::ERR>(
+ "Error getting property, sdbusplus call failed");
+ return -1;
+ }
+ reply.read(value);
+
+ return 0;
+}
+
+static int set_regionId(uint8_t regionId, std::string& service)
+{
+ auto method = bus.new_method_call(service.c_str(), MDRV1_PATH,
+ DBUS_PROPERTIES, "Set");
+ sdbusplus::message::variant<uint8_t> value{regionId};
+ method.append(MDRV1_INTERFACE, "RegionId", value);
+ auto region = bus.call(method);
+ if (region.is_method_error())
+ {
+ phosphor::logging::log<level::ERR>(
+ "Error setting regionID, sdbusplus call failed");
+ return -1;
+ }
+ return 0;
+}
+
+ipmi_ret_t cmd_region_complete(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t data_len, ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const RegionCompleteRequest*>(request);
+ uint8_t status;
+
+ sdbusplus::message::variant<uint8_t, uint16_t> value;
+
+ if (*data_len != sizeof(RegionCompleteRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ uint8_t regionId = requestData->regionId - 1;
+ *data_len = 0;
+
+ if (regionId >= maxMDRId)
+ {
+ phosphor::logging::log<level::ERR>("Invalid region");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ std::string service = ipmi::getService(bus, MDRV1_INTERFACE, MDRV1_PATH);
+
+ if (set_regionId(regionId, service) < 0)
+ {
+ phosphor::logging::log<level::ERR>("Error setting regionId");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ if (0 > sdplus_mdrv1_get_property("LockPolicy", value, service))
+ {
+ phosphor::logging::log<level::ERR>("Error getting lockPolicy");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ if (regionLockUnlocked ==
+ sdbusplus::message::variant_ns::get<uint8_t>(value))
+ {
+ return IPMI_CC_PARAMETER_NOT_SUPPORT_IN_PRESENT_STATE;
+ }
+
+ if (0 > sdplus_mdrv1_get_property("SessionId", value, service))
+ {
+ phosphor::logging::log<level::ERR>("Error getting sessionId");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ if (requestData->sessionId !=
+ sdbusplus::message::variant_ns::get<uint8_t>(value))
+ {
+ return IPMI_CC_OEM_SET_IN_PROCESS;
+ }
+
+ auto method = bus.new_method_call(service.c_str(), MDRV1_PATH,
+ MDRV1_INTERFACE, "RegionComplete");
+
+ method.append(regionId);
+
+ auto reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ phosphor::logging::log<level::ERR>(
+ "Error set region complete",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", MDRV1_PATH));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ reply.read(status);
+
+ if (status != 0)
+ phosphor::logging::log<level::ERR>(
+ "Error set region complete, unexpected error");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_region_read(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t data_len, ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const RegionReadRequest*>(request);
+ auto responseData = reinterpret_cast<RegionReadResponse*>(response);
+ sdbusplus::message::variant<uint8_t, uint16_t> regUsedVal;
+ sdbusplus::message::variant<uint8_t, uint16_t> lockPolicyVal;
+ std::vector<uint8_t> res;
+
+ if (*data_len < sizeof(RegionReadRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ uint8_t regionId = requestData->regionId - 1;
+
+ *data_len = 0;
+
+ if (regionId >= maxMDRId)
+ {
+ phosphor::logging::log<level::ERR>("Invalid region");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ std::string service = ipmi::getService(bus, MDRV1_INTERFACE, MDRV1_PATH);
+ // TODO to make sure the interface can get correct LockPolicy even
+ // regionId changed by another task.
+ if (set_regionId(regionId, service) < 0)
+ {
+ phosphor::logging::log<level::ERR>("Error setting regionId");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ if (0 > sdplus_mdrv1_get_property("RegionUsed", regUsedVal, service))
+ {
+ phosphor::logging::log<level::ERR>("Error getting regionUsed");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ if (requestData->offset + requestData->length >
+ sdbusplus::message::variant_ns::get<uint16_t>(regUsedVal))
+ {
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ if (0 > sdplus_mdrv1_get_property("LockPolicy", lockPolicyVal, service))
+ {
+ phosphor::logging::log<level::ERR>("Error getting lockPolicy");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ if (regionLockUnlocked !=
+ sdbusplus::message::variant_ns::get<uint8_t>(lockPolicyVal))
+ {
+ return IPMI_CC_PARAMETER_NOT_SUPPORT_IN_PRESENT_STATE;
+ }
+
+ auto method = bus.new_method_call(service.c_str(), MDRV1_PATH,
+ MDRV1_INTERFACE, "RegionRead");
+
+ method.append(regionId, requestData->length, requestData->offset);
+
+ auto reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ phosphor::logging::log<level::ERR>(
+ "Error read region data",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", MDRV1_PATH));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ reply.read(res);
+
+ *data_len = responseData->length = res[0];
+ responseData->updateCount = res[1];
+
+ if ((*data_len == 0) || (*data_len >= 254))
+ {
+ phosphor::logging::log<level::ERR>(
+ "Data length send from service is invalid");
+ *data_len = 0;
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ *data_len += 2 * sizeof(uint8_t);
+ std::copy(&res[2], &res[*data_len], responseData->data);
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_region_write(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t data_len, ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const RegionWriteRequest*>(request);
+ uint8_t regionId = requestData->regionId - 1;
+ std::string res;
+ std::vector<uint8_t> writeData;
+ uint16_t index;
+ uint8_t tmp[255];
+
+ size_t minInputLen = &requestData->data[0] - &requestData->sessionId + 1;
+ if (*data_len < minInputLen)
+ { // this command need at least 6 bytes input
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ sdbusplus::message::variant<uint8_t, uint16_t> value;
+
+ *data_len = 0;
+
+ if (regionId >= maxMDRId)
+ {
+ phosphor::logging::log<level::ERR>("Invalid region");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ std::string service = ipmi::getService(bus, MDRV1_INTERFACE, MDRV1_PATH);
+
+ if (set_regionId(regionId, service) < 0)
+ {
+ phosphor::logging::log<level::ERR>("Error setting regionId");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ if (0 > sdplus_mdrv1_get_property("LockPolicy", value, service))
+ {
+ phosphor::logging::log<level::ERR>("Error getting lockPolicy");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ if (regionLockUnlocked ==
+ sdbusplus::message::variant_ns::get<uint8_t>(value))
+ {
+ return IPMI_CC_PARAMETER_NOT_SUPPORT_IN_PRESENT_STATE;
+ }
+
+ if (0 > sdplus_mdrv1_get_property("SessionId", value, service))
+ {
+ phosphor::logging::log<level::ERR>("Error getting sessionId");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ if (requestData->sessionId !=
+ sdbusplus::message::variant_ns::get<uint8_t>(value))
+ {
+ return IPMI_CC_OEM_SET_IN_PROCESS;
+ }
+
+ std::copy(&(requestData->length), &(requestData->data[requestData->length]),
+ tmp);
+ writeData.push_back(regionId);
+ for (index = 0; index < minInputLen + requestData->length - 2; index++)
+ {
+ writeData.push_back(tmp[index]);
+ }
+
+ auto method = bus.new_method_call(service.c_str(), MDRV1_PATH,
+ MDRV1_INTERFACE, "RegionWrite");
+
+ method.append(writeData);
+
+ auto reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ phosphor::logging::log<level::ERR>(
+ "Error write region data",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", MDRV1_PATH));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ reply.read(res);
+
+ if (res == "NoData")
+ {
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+ else if (res != "Success")
+ {
+ phosphor::logging::log<level::ERR>(
+ "Error write region data, unexpected error");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_region_lock(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t data_len, ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const RegionLockRequest*>(request);
+ uint8_t regionId = requestData->regionId - 1;
+ sdbusplus::message::variant<uint8_t, uint16_t> value;
+ auto res = reinterpret_cast<uint8_t*>(response);
+ uint8_t lockResponse;
+
+ if (*data_len != sizeof(RegionLockRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ if (regionId >= maxMDRId)
+ {
+ phosphor::logging::log<level::ERR>("Invalid region");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ std::string service = ipmi::getService(bus, MDRV1_INTERFACE, MDRV1_PATH);
+
+ if (set_regionId(regionId, service) < 0)
+ {
+ phosphor::logging::log<level::ERR>("Error setting regionId");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ if (0 > sdplus_mdrv1_get_property("LockPolicy", value, service))
+ {
+ phosphor::logging::log<level::ERR>("Error getting lockPolicy");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ if (requestData->lockPolicy == regionLockUnlocked)
+ {
+ if (regionLockUnlocked ==
+ sdbusplus::message::variant_ns::get<uint8_t>(value))
+ {
+ return IPMI_CC_PARAMETER_NOT_SUPPORT_IN_PRESENT_STATE;
+ }
+ }
+ if (regionLockUnlocked !=
+ sdbusplus::message::variant_ns::get<uint8_t>(value))
+ {
+ if (0 > sdplus_mdrv1_get_property("SessionId", value, service))
+ {
+ phosphor::logging::log<level::ERR>("Error getting sessionId");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ if (requestData->sessionId !=
+ sdbusplus::message::variant_ns::get<uint8_t>(value))
+ {
+ if (requestData->lockPolicy != regionLockStrict)
+ {
+ return IPMI_CC_OEM_SET_IN_PROCESS;
+ }
+ }
+ }
+ auto method = bus.new_method_call(service.c_str(), MDRV1_PATH,
+ MDRV1_INTERFACE, "RegionLock");
+
+ method.append(requestData->sessionId, regionId, requestData->lockPolicy,
+ requestData->msTimeout);
+
+ auto reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ phosphor::logging::log<level::ERR>(
+ "Error lock region ",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", MDRV1_PATH));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ reply.read(lockResponse);
+
+ *data_len = sizeof(lockResponse);
+ *res = lockResponse;
+ return IPMI_CC_OK;
+}
+
+static void register_netfn_smbios_functions(void)
+{
+ // MDR V1 Command
+ // <Get MDR Status Command>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDR_STATUS, NULL,
+ cmd_region_status, PRIVILEGE_OPERATOR);
+
+ // <Update Complete Status Command>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDR_COMPLETE, NULL,
+ cmd_region_complete, PRIVILEGE_OPERATOR);
+
+ // <Read MDR Command>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDR_READ, NULL,
+ cmd_region_read, PRIVILEGE_OPERATOR);
+
+ // <Write MDR Command>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDR_WRITE, NULL,
+ cmd_region_write, PRIVILEGE_OPERATOR);
+
+ // <Lock MDR Command>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDR_LOCK, NULL,
+ cmd_region_lock, PRIVILEGE_OPERATOR);
+}
diff --git a/src/smbiosmdrv2.cpp b/src/smbiosmdrv2.cpp
new file mode 100644
index 0000000..93ad412
--- /dev/null
+++ b/src/smbiosmdrv2.cpp
@@ -0,0 +1,1106 @@
+/*
+// 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 <errno.h>
+#include <ipmid/api.h>
+
+#include <commandutils.hpp>
+#include <cstdint>
+#include <ipmid/utils.hpp>
+#include <phosphor-ipmi-host/ipmid.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+#include <smbiosmdrv2.hpp>
+#include <string>
+#include <vector>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+constexpr const char* DBUS_PROPERTIES = "org.freedesktop.DBus.Properties";
+constexpr const char* MDRV2_PATH = "/xyz/openbmc_project/Smbios/MDR_V2";
+constexpr const char* MDRV2_INTERFACE = "xyz.openbmc_project.Smbios.MDR_V2";
+constexpr const int LAST_AGENT_INDEX = -1;
+constexpr const uint16_t LAST_AGENT_ID = 0xFFFF;
+
+static void register_netfn_smbiosmdrv2_functions() __attribute__((constructor));
+static sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
+
+int agentLookup(const uint16_t& agentId, const std::string& service)
+{
+ int agentIndex = -1;
+
+ if (LAST_AGENT_ID == agentId)
+ {
+ return LAST_AGENT_INDEX;
+ }
+
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), MDRV2_PATH, MDRV2_INTERFACE, "AgentLookup");
+ method.append(agentId);
+ sdbusplus::message::message reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error get agent index, sdbusplus call failed");
+ return -1;
+ }
+ reply.read(agentIndex);
+
+ return agentIndex;
+}
+
+int findLockHandle(const uint16_t& lockHandle, const std::string& service)
+{
+ int idIndex = -1;
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), MDRV2_PATH, MDRV2_INTERFACE, "FindLockHandle");
+ method.append(lockHandle);
+
+ sdbusplus::message::message reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error find lock handle",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", MDRV2_PATH));
+ return -1;
+ }
+ reply.read(idIndex);
+
+ return idIndex;
+}
+
+int sdplusMdrv2GetProperty(const std::string& name,
+ sdbusplus::message::variant<uint8_t>& value,
+ const std::string& service)
+{
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), MDRV2_PATH, DBUS_PROPERTIES, "Get");
+ method.append(MDRV2_INTERFACE, name);
+ sdbusplus::message::message reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error get property, sdbusplus call failed");
+ return -1;
+ }
+ reply.read(value);
+
+ return 0;
+}
+
+int findDataId(const uint8_t* dataInfo, const size_t& len,
+ const std::string& service)
+{
+ int idIndex = -1;
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), MDRV2_PATH, MDRV2_INTERFACE, "FindIdIndex");
+ std::vector<uint8_t> info;
+ for (int index = 0; index < len; index++)
+ {
+ info.push_back(dataInfo[index]);
+ }
+ method.append(info);
+
+ sdbusplus::message::message reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error find id index",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", MDRV2_PATH));
+ return -1;
+ }
+ reply.read(idIndex);
+
+ return idIndex;
+}
+
+ipmi_ret_t cmd_mdr2_agent_status(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t data_len,
+ ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const MDRiiGetAgentStatus*>(request);
+ auto dataOut = reinterpret_cast<uint8_t*>(response);
+ std::vector<uint8_t> status;
+
+ if (*data_len != sizeof(MDRiiGetAgentStatus))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ std::string service = ipmi::getService(bus, MDRV2_INTERFACE, MDRV2_PATH);
+
+ int agentIndex = agentLookup(requestData->agentId, service);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), MDRV2_PATH, MDRV2_INTERFACE, "AgentStatus");
+ method.append(requestData->dirVersion);
+ sdbusplus::message::message reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error get agent status",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", MDRV2_PATH));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ reply.read(status);
+
+ if (status.size() != sizeof(MDRiiAgentStatusResponse))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Get agent status response length not valid");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ *data_len = static_cast<size_t>(status.size());
+ std::copy(&status[0], &status[*data_len], dataOut);
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_get_dir(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t data_len, ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const MDRiiGetDirRequest*>(request);
+ auto dataOut = reinterpret_cast<uint8_t*>(response);
+ std::vector<uint8_t> dirInfo;
+
+ if (*data_len != sizeof(MDRiiGetDirRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ std::string service = ipmi::getService(bus, MDRV2_INTERFACE, MDRV2_PATH);
+
+ int agentIndex = agentLookup(requestData->agentId, service);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ sdbusplus::message::variant<uint8_t> value = 0;
+ if (0 != sdplusMdrv2GetProperty("DirEntries", value, service))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error getting DirEnries");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ if (requestData->dirIndex >
+ sdbusplus::message::variant_ns::get<uint8_t>(value))
+ {
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), MDRV2_PATH, MDRV2_INTERFACE, "GetDir");
+
+ method.append(requestData->dirIndex);
+
+ sdbusplus::message::message reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error get dir",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", MDRV2_PATH));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ reply.read(dirInfo);
+
+ if (dirInfo.size() < sizeof(MDRiiGetDirResponse))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error get dir, response length invalid");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ auto responseData = reinterpret_cast<MDRiiGetDirResponse*>(dirInfo.data());
+
+ *data_len = dirInfo.size();
+
+ if (*data_len > MAX_IPMI_BUFFER) // length + completion code should no more
+ // than MAX_IPMI_BUFFER
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Data length send from service is invalid");
+ *data_len = 0;
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ std::copy(&dirInfo[0], &dirInfo[*data_len], dataOut);
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_get_data_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t data_len,
+ ipmi_context_t context)
+{
+ auto requestData =
+ reinterpret_cast<const MDRiiGetDataInfoRequest*>(request);
+ auto dataOut = reinterpret_cast<uint8_t*>(response);
+ std::vector<uint8_t> res;
+
+ if (*data_len < sizeof(MDRiiGetDataInfoRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ std::string service = ipmi::getService(bus, MDRV2_INTERFACE, MDRV2_PATH);
+
+ int agentIndex = agentLookup(requestData->agentId, service);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ int idIndex =
+ findDataId(requestData->dataSetInfo.dataInfo,
+ sizeof(requestData->dataSetInfo.dataInfo), service);
+
+ if ((idIndex < 0) || (idIndex >= maxDirEntries))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), MDRV2_PATH, MDRV2_INTERFACE, "GetDataInfo");
+
+ method.append(idIndex);
+
+ sdbusplus::message::message reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error get data info",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", MDRV2_PATH));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ reply.read(res);
+
+ if (res.size() != sizeof(MDRiiGetDataInfoResponse))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Get data info response length not invalid");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ *data_len = static_cast<size_t>(res.size());
+ std::copy(&res[0], &res[*data_len], dataOut);
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_lock_data(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t data_len, ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const MDRiiLockDataRequest*>(request);
+ auto responseData = reinterpret_cast<MDRiiLockDataResponse*>(response);
+
+ std::tuple<bool, uint8_t, uint16_t, uint32_t, uint32_t, uint32_t> res;
+
+ if (*data_len < sizeof(MDRiiLockDataRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ std::string service = ipmi::getService(bus, MDRV2_INTERFACE, MDRV2_PATH);
+
+ int agentIndex = agentLookup(requestData->agentId, service);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ int idIndex =
+ findDataId(requestData->dataSetInfo.dataInfo,
+ sizeof(requestData->dataSetInfo.dataInfo), service);
+
+ if ((idIndex < 0) || (idIndex >= maxDirEntries))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), MDRV2_PATH, MDRV2_INTERFACE, "LockData");
+
+ method.append((uint8_t)idIndex, requestData->timeout);
+
+ sdbusplus::message::message reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ if (reply.get_errno() == EBUSY)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Lock Data failed - cannot lock idIndex");
+ return IPMI_CC_PARAMETER_NOT_SUPPORT_IN_PRESENT_STATE;
+ }
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error lock data",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", MDRV2_PATH));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ reply.read(res);
+
+ if (std::get<0>(res) == false)
+ {
+ return IPMI_CC_PARAMETER_NOT_SUPPORT_IN_PRESENT_STATE;
+ }
+
+ *data_len = sizeof(MDRiiLockDataResponse);
+
+ responseData->mdrVersion = std::get<1>(res);
+ responseData->lockHandle = std::get<2>(res);
+ responseData->dataLength = std::get<3>(res);
+ responseData->xferAddress = std::get<4>(res);
+ responseData->xferLength = std::get<5>(res);
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_unlock_data(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t data_len,
+ ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const MDRiiUnlockDataRequest*>(request);
+ std::string resStatus;
+
+ if (*data_len != sizeof(MDRiiUnlockDataRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ std::string service = ipmi::getService(bus, MDRV2_INTERFACE, MDRV2_PATH);
+
+ int agentIndex = agentLookup(requestData->agentId, service);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ int idIndex = findLockHandle(requestData->lockHandle, service);
+
+ if ((idIndex < 0) || (idIndex >= maxDirEntries))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), MDRV2_PATH, MDRV2_INTERFACE, "UnLockData");
+ method.append((uint8_t)idIndex);
+
+ sdbusplus::message::message reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ if (reply.get_errno() == EBUSY)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unlock Data failed - cannot unlock idIndex");
+ return IPMI_CC_PARAMETER_NOT_SUPPORT_IN_PRESENT_STATE;
+ }
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error unlock data",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", MDRV2_PATH));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ reply.read(resStatus);
+
+ if (resStatus != "success")
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Agent unlock Invalid lock status.");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_get_data_block(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t data_len,
+ ipmi_context_t context)
+{
+ auto requestData =
+ reinterpret_cast<const MDRiiGetDataBlockRequest*>(request);
+ auto responseData = reinterpret_cast<MDRiiGetDataBlockResponse*>(response);
+ std::tuple<uint8_t, uint32_t, uint32_t, std::vector<uint8_t>> res;
+ std::vector<uint8_t> resData;
+ uint8_t status = 1;
+
+ if (*data_len != sizeof(MDRiiGetDataBlockRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ std::string service = ipmi::getService(bus, MDRV2_INTERFACE, MDRV2_PATH);
+
+ int agentIndex = agentLookup(requestData->agentId, service);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ int idIndex = findLockHandle(requestData->lockHandle, service);
+
+ if ((idIndex < 0) || (idIndex >= maxDirEntries))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), MDRV2_PATH, MDRV2_INTERFACE, "GetDataBlock");
+ method.append((uint8_t)idIndex, requestData->xferOffset,
+ requestData->xferLength);
+
+ sdbusplus::message::message reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error get data block",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", MDRV2_PATH));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ reply.read(res);
+
+ // Get the status of get data block, 0 means succeed
+ status = std::get<0>(res);
+ if (status == 1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Request data offset is outside of range.");
+ return IPMI_CC_CANNOT_RETURN_NUMBER_OF_REQUESTED_DATA_BYTES;
+ }
+ else if (status != 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Get data block unexpected error.");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ responseData->xferLength = std::get<1>(res);
+ if (responseData->xferLength > requestData->xferLength)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Get data block unexpected error.");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ responseData->checksum = std::get<2>(res);
+
+ resData = std::get<3>(res);
+
+ *data_len = sizeof(responseData->xferLength) +
+ sizeof(responseData->checksum) + resData.size();
+
+ if (*data_len > MAX_IPMI_BUFFER) // length + completion code should no more
+ // than MAX_IPMI_BUFFER
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Data length send from service is invalid");
+ *data_len = 0;
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ std::copy(resData.begin(), resData.end(), responseData->data);
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_send_dir(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t data_len, ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const MDRiiSendDirRequest*>(request);
+ std::vector<uint8_t> idVector;
+ bool teminate = false;
+
+ if (*data_len != sizeof(MDRiiSendDirRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ std::string service = ipmi::getService(bus, MDRV2_INTERFACE, MDRV2_PATH);
+
+ int agentIndex = agentLookup(requestData->agentId, service);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ if ((requestData->dirIndex + requestData->returnedEntries) > maxDirEntries)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Too many directory entries");
+ return IPMI_CC_STORGE_LEAK;
+ }
+
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), MDRV2_PATH, MDRV2_INTERFACE, "SendDir");
+ method.append(requestData->dirVersion, requestData->dirIndex,
+ requestData->returnedEntries, requestData->remainingEntries);
+ uint8_t* reqPoint;
+ for (int index = 0; index < requestData->returnedEntries; index++)
+ {
+ reqPoint = (uint8_t*)&(requestData->data[index]);
+ std::copy(reqPoint, sizeof(Mdr2DirEntry) + reqPoint, idVector.data());
+ }
+ method.append(idVector);
+
+ sdbusplus::message::message reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error send dir",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", MDRV2_PATH));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ reply.read(teminate);
+
+ *data_len = 1;
+ if (teminate == false)
+ *(static_cast<uint8_t*>(response)) = 0;
+ else
+ *(static_cast<uint8_t*>(response)) = 1;
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_data_info_offer(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t data_len,
+ ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const MDRiiOfferDataInfo*>(request);
+ auto dataOut = reinterpret_cast<uint8_t*>(response);
+ std::vector<uint8_t> dataInfo;
+
+ if (*data_len != sizeof(MDRiiOfferDataInfo))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ std::string service = ipmi::getService(bus, MDRV2_INTERFACE, MDRV2_PATH);
+
+ int agentIndex = agentLookup(requestData->agentId, service);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), MDRV2_PATH, MDRV2_INTERFACE, "GetDataOffer");
+
+ sdbusplus::message::message reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ if (reply.get_errno() == EBUSY)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Send data info offer failed - not available to update data "
+ "into agent at present");
+ return IPMI_CC_PARAMETER_NOT_SUPPORT_IN_PRESENT_STATE;
+ }
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error send data info offer",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", MDRV2_PATH));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ reply.read(dataInfo);
+ if (dataInfo.size() != sizeof(MDRiiOfferDataInfoResponse))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error send data info offer, return length invalid");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ *data_len = dataInfo.size();
+ std::copy(dataInfo.begin(), dataInfo.end(), dataOut);
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_send_data_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t data_len,
+ ipmi_context_t context)
+{
+ auto requestData =
+ reinterpret_cast<const MDRiiSendDataInfoRequest*>(request);
+ bool entryChanged = true;
+
+ if (*data_len != sizeof(MDRiiSendDataInfoRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ if (requestData->dataLength > smbiosTableStorageSize)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Requested data length is out of SMBIOS Table storage size.");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ std::string service = ipmi::getService(bus, MDRV2_INTERFACE, MDRV2_PATH);
+
+ int agentIndex = agentLookup(requestData->agentId, service);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ int idIndex =
+ findDataId(requestData->dataSetInfo.dataInfo,
+ sizeof(requestData->dataSetInfo.dataInfo), service);
+
+ if ((idIndex < 0) || (idIndex >= maxDirEntries))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), MDRV2_PATH, MDRV2_INTERFACE, "SendDataInfo");
+
+ method.append((uint8_t)idIndex, requestData->validFlag,
+ requestData->dataLength, requestData->dataVersion,
+ requestData->timeStamp);
+
+ sdbusplus::message::message reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error send data info",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", MDRV2_PATH));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ reply.read(entryChanged);
+
+ *data_len = 1;
+
+ if (entryChanged)
+ {
+ *(static_cast<uint8_t*>(response)) = 1;
+ }
+ else
+ {
+ *(static_cast<uint8_t*>(response)) = 0;
+ }
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_data_start(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t data_len, ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const MDRiiDataStartRequest*>(request);
+ auto responseData = reinterpret_cast<MDRiiDataStartResponse*>(response);
+ std::vector<uint8_t> idVector;
+
+ if (*data_len != sizeof(MDRiiDataStartRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ if (requestData->dataLength > smbiosTableStorageSize)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Requested data length is out of SMBIOS Table storage size.");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ if ((requestData->xferLength + requestData->xferAddress) > mdriiSMSize)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid data address and size");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ std::string service = ipmi::getService(bus, MDRV2_INTERFACE, MDRV2_PATH);
+
+ int agentIndex = agentLookup(requestData->agentId, service);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ int idIndex =
+ findDataId(requestData->dataSetInfo.dataInfo,
+ sizeof(requestData->dataSetInfo.dataInfo), service);
+
+ if ((idIndex < 0) || (idIndex >= maxDirEntries))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), MDRV2_PATH, MDRV2_INTERFACE, "DataStart");
+
+ for (uint8_t infoIndex = 0; infoIndex < sizeof(DataIdStruct); infoIndex++)
+ {
+ idVector.push_back(requestData->dataSetInfo.dataInfo[infoIndex]);
+ }
+ method.append((uint8_t)idIndex, idVector, requestData->dataLength,
+ requestData->xferAddress, requestData->xferLength,
+ requestData->timeout);
+
+ sdbusplus::message::message reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ int errNumber = reply.get_errno();
+ if (errNumber == ENOMEM)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Send data start failed - cannot map share memory");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ else if (errNumber == EINVAL)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid data address and size");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+ else
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error Send Data Start",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", MDRV2_PATH));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ }
+ uint8_t xferStartAck = 0;
+ uint16_t sessionHandle = 0;
+ reply.read(xferStartAck, sessionHandle);
+ responseData->sessionHandle = sessionHandle;
+ responseData->xferStartAck = xferStartAck;
+ if (responseData->xferStartAck == 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Send data start unexpected error");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ *data_len = sizeof(MDRiiDataStartResponse);
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_data_done(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t data_len, ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const MDRiiDataDoneRequest*>(request);
+ std::string resStatus;
+
+ if (*data_len != sizeof(MDRiiDataDoneRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ std::string service = ipmi::getService(bus, MDRV2_INTERFACE, MDRV2_PATH);
+
+ int agentIndex = agentLookup(requestData->agentId, service);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ int idIndex = findLockHandle(requestData->lockHandle, service);
+
+ if ((idIndex < 0) || (idIndex >= maxDirEntries))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), MDRV2_PATH, MDRV2_INTERFACE, "DataDone");
+ method.append((uint8_t)idIndex);
+
+ sdbusplus::message::message reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ if (reply.get_errno() == EBUSY)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Send data done failed - cannot unlock idIndex");
+ return IPMI_CC_DESTINATION_UNAVAILABLE;
+ }
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error Send Data done",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", MDRV2_PATH));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ reply.read(resStatus);
+
+ if (resStatus != "success")
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Data done failure.");
+ return IPMI_CC_DESTINATION_UNAVAILABLE;
+ }
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_send_data_block(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t data_len,
+ ipmi_context_t context)
+{
+ auto requestData =
+ reinterpret_cast<const MDRiiSendDataBlockRequest*>(request);
+ std::string resStatus;
+
+ if (*data_len != sizeof(MDRiiSendDataBlockRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ std::string service = ipmi::getService(bus, MDRV2_INTERFACE, MDRV2_PATH);
+
+ int agentIndex = agentLookup(requestData->agentId, service);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ int idIndex = findLockHandle(requestData->lockHandle, service);
+
+ if ((idIndex < 0) || (idIndex >= maxDirEntries))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), MDRV2_PATH, MDRV2_INTERFACE, "SendDataBlock");
+ method.append((uint8_t)idIndex, requestData->xferOffset,
+ requestData->xferLength, requestData->checksum);
+
+ sdbusplus::message::message reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ int errNumber = reply.get_errno();
+ if (errNumber == EINVAL)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Send data block Invalid checksum");
+ return IPMI_CC_OEM_INVALID_CHECKSUM;
+ }
+ else if (errNumber == ENOBUFS)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Send data block Invalid offset/length");
+ return IPMI_CC_REQUEST_DATA_FIELD_LENGTH_LIMIT_EXCEEDED;
+ }
+ else if (errNumber == EBUSY)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Send data block failed, other data is updating");
+ return IPMI_CC_DESTINATION_UNAVAILABLE;
+ }
+ else
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error Send data block",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", MDRV2_PATH));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ }
+ reply.read(resStatus);
+
+ if (resStatus != "success")
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "send data block failure.");
+ return IPMI_CC_DESTINATION_UNAVAILABLE;
+ }
+
+ return IPMI_CC_OK;
+}
+
+static void register_netfn_smbiosmdrv2_functions(void)
+{
+ // MDR V2 Command
+ // <Get MDRII Status Command>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_AGENT_STATUS,
+ NULL, cmd_mdr2_agent_status, PRIVILEGE_OPERATOR);
+
+ // <Get MDRII Directory Command>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_GET_DIR, NULL,
+ cmd_mdr2_get_dir, PRIVILEGE_OPERATOR);
+
+ // <Get MDRII Data Info Command>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_GET_DATA_INFO,
+ NULL, cmd_mdr2_get_data_info, PRIVILEGE_OPERATOR);
+
+ // <Lock MDRII Data Command>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_LOCK_DATA, NULL,
+ cmd_mdr2_lock_data, PRIVILEGE_OPERATOR);
+
+ // <Unlock MDRII Data Command>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_UNLOCK_DATA,
+ NULL, cmd_mdr2_unlock_data, PRIVILEGE_OPERATOR);
+
+ // <Get MDRII Data Block Command>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_GET_DATA_BLOCK,
+ NULL, cmd_mdr2_get_data_block, PRIVILEGE_OPERATOR);
+
+ // <Send MDRII Directory Command>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_SEND_DIR, NULL,
+ cmd_mdr2_send_dir, PRIVILEGE_OPERATOR);
+
+ // <Send MDRII Info Offer>
+ ipmi_register_callback(
+ NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_SEND_DATA_INFO_OFFER, NULL,
+ cmd_mdr2_data_info_offer, PRIVILEGE_OPERATOR);
+
+ // <Send MDRII Data Info>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_SEND_DATA_INFO,
+ NULL, cmd_mdr2_send_data_info, PRIVILEGE_OPERATOR);
+
+ // <Send MDRII Data Start>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_DATA_START, NULL,
+ cmd_mdr2_data_start, PRIVILEGE_OPERATOR);
+
+ // <Send MDRII Data Done>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_DATA_DONE, NULL,
+ cmd_mdr2_data_done, PRIVILEGE_OPERATOR);
+
+ // <Send MDRII Data Block>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_SEND_DATA_BLOCK,
+ NULL, cmd_mdr2_send_data_block, PRIVILEGE_OPERATOR);
+}
diff --git a/src/smbiosmdrv2handler.cpp b/src/smbiosmdrv2handler.cpp
new file mode 100644
index 0000000..da3712a
--- /dev/null
+++ b/src/smbiosmdrv2handler.cpp
@@ -0,0 +1,1444 @@
+/*
+// 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 <errno.h>
+#include <fcntl.h>
+#include <ipmid/api.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <commandutils.hpp>
+#include <cstdint>
+#include <fstream>
+#include <ipmid/utils.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/message/types.hpp>
+#include <smbiosmdrv2handler.hpp>
+#include <string>
+#include <vector>
+#include <xyz/openbmc_project/Common/error.hpp>
+#include <xyz/openbmc_project/Smbios/MDR_V2/error.hpp>
+
+std::unique_ptr<MDRV2> mdrv2 = nullptr;
+
+namespace variant_ns = sdbusplus::message::variant_ns;
+
+static void register_netfn_smbiosmdrv2_functions() __attribute__((constructor));
+static sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
+
+int MDRV2::agentLookup(const uint16_t &agentId)
+{
+ int agentIndex = -1;
+
+ if (lastAgentId == agentId)
+ {
+ return lastAgentIndex;
+ }
+
+ if (agentId == smbiosAgentId)
+ {
+ return firstAgentIndex;
+ }
+
+ return agentIndex;
+}
+
+int MDRV2::sdplusMdrv2GetProperty(const std::string &name,
+ sdbusplus::message::variant<uint8_t> &value,
+ const std::string &service)
+{
+ sdbusplus::message::message method =
+ bus.new_method_call(service.c_str(), mdrv2Path, dbusProperties, "Get");
+ method.append(mdrv2Interface, name);
+
+ sdbusplus::message::message reply = bus.call(method);
+
+ try
+ {
+ sdbusplus::message::message reply = bus.call(method);
+ reply.read(value);
+ }
+ catch (sdbusplus::exception_t &)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error get property, sdbusplus call failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+int MDRV2::syncDirCommonData(uint8_t idIndex, uint32_t size,
+ const std::string &service)
+{
+ std::vector<uint32_t> commonData;
+ sdbusplus::message::message method =
+ bus.new_method_call(service.c_str(), mdrv2Path, mdrv2Interface,
+ "SynchronizeDirectoryCommonData");
+ method.append(idIndex, size);
+
+ try
+ {
+ sdbusplus::message::message reply = bus.call(method);
+ reply.read(commonData);
+ }
+ catch (sdbusplus::exception_t &)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error sync dir common data with service");
+ return -1;
+ }
+
+ if (commonData.size() < syncDirCommonSize)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error sync dir common data - data length invalid");
+ return -1;
+ }
+ smbiosDir.dir[idIndex].common.dataSetSize = commonData.at(0);
+ smbiosDir.dir[idIndex].common.dataVersion = commonData.at(1);
+ smbiosDir.dir[idIndex].common.timestamp = commonData.at(2);
+
+ return 0;
+}
+
+int MDRV2::findDataId(const uint8_t *dataInfo, const size_t &len,
+ const std::string &service)
+{
+ int idIndex = -1;
+
+ if (dataInfo == nullptr)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error dataInfo, input is null point");
+ return -1;
+ }
+
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), mdrv2Path, mdrv2Interface, "FindIdIndex");
+ std::vector<uint8_t> info;
+ info.resize(len);
+ std::copy(dataInfo, dataInfo + len, info.data());
+ method.append(info);
+
+ try
+ {
+ sdbusplus::message::message reply = bus.call(method);
+ reply.read(idIndex);
+ }
+ catch (sdbusplus::exception_t &)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error find id index",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", mdrv2Path));
+ return -1;
+ }
+
+ return idIndex;
+}
+
+uint16_t MDRV2::getSessionHandle(Mdr2DirStruct *dir)
+{
+ if (dir == NULL)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Empty dir point");
+ return 0;
+ }
+ dir->sessionHandle++;
+ if (dir->sessionHandle == 0)
+ {
+ dir->sessionHandle = 1;
+ }
+
+ return dir->sessionHandle;
+}
+
+int MDRV2::findLockHandle(const uint16_t &lockHandle)
+{
+ int idIndex = -1;
+
+ for (int index = 0; index < smbiosDir.dirEntries; index++)
+ {
+ if (lockHandle == smbiosDir.dir[index].lockHandle)
+ {
+ return index;
+ }
+ }
+
+ return idIndex;
+}
+
+bool MDRV2::smbiosIsUpdating(uint8_t index)
+{
+ if (index > maxDirEntries)
+ {
+ return false;
+ }
+ if (smbiosDir.dir[index].stage == MDR2SMBIOSStatusEnum::mdr2Updating)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+uint32_t MDRV2::calcChecksum32(uint8_t *buf, uint32_t len)
+{
+ uint32_t sum = 0;
+
+ if (buf == nullptr)
+ {
+ return invalidChecksum;
+ }
+
+ for (uint32_t index = 0; index < len; index++)
+ {
+ sum += buf[index];
+ }
+
+ return sum;
+}
+
+ipmi_ret_t cmd_mdr2_agent_status(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t data_len,
+ ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const MDRiiGetAgentStatus *>(request);
+ auto responseData = reinterpret_cast<MDRiiAgentStatusResponse *>(response);
+
+ if (*data_len != sizeof(MDRiiGetAgentStatus))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ if (mdrv2 == nullptr)
+ {
+ mdrv2 = std::make_unique<MDRV2>();
+ }
+
+ int agentIndex = mdrv2->agentLookup(requestData->agentId);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ responseData->mdrVersion = mdr2Version;
+ responseData->agentVersion = smbiosAgentVersion;
+ responseData->dirVersion = mdrv2->smbiosDir.dirVersion;
+ responseData->dirEntries = mdrv2->smbiosDir.dirEntries;
+
+ if (mdrv2->smbiosDir.remoteDirVersion != requestData->dirVersion)
+ {
+ mdrv2->smbiosDir.remoteDirVersion = requestData->dirVersion;
+ responseData->dataRequest =
+ static_cast<uint8_t>(DirDataRequestEnum::dirDataRequested);
+ }
+ else
+ {
+ responseData->dataRequest =
+ static_cast<uint8_t>(DirDataRequestEnum::dirDataNotRequested);
+ }
+
+ *data_len = sizeof(MDRiiAgentStatusResponse);
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_get_dir(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t data_len, ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const MDRiiGetDirRequest *>(request);
+ auto dataOut = reinterpret_cast<uint8_t *>(response);
+ std::vector<uint8_t> dirInfo;
+
+ if (*data_len != sizeof(MDRiiGetDirRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ std::string service = ipmi::getService(bus, mdrv2Interface, mdrv2Path);
+
+ if (mdrv2 == nullptr)
+ {
+ mdrv2 = std::make_unique<MDRV2>();
+ }
+
+ int agentIndex = mdrv2->agentLookup(requestData->agentId);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ sdbusplus::message::variant<uint8_t> value = 0;
+ if (0 != mdrv2->sdplusMdrv2GetProperty("DirectoryEntries", value, service))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error getting DirEnries");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ if (requestData->dirIndex > variant_ns::get<uint8_t>(value))
+ {
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), mdrv2Path, mdrv2Interface, "GetDirectoryInformation");
+
+ method.append(requestData->dirIndex);
+
+ try
+ {
+ sdbusplus::message::message reply = bus.call(method);
+ reply.read(dirInfo);
+ }
+ catch (sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
+ InvalidParameter)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error get dir - Invalid parameter");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+ catch (sdbusplus::exception_t &)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error get dir",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", mdrv2Path));
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ if (dirInfo.size() < sizeof(MDRiiGetDirResponse))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error get dir, response length invalid");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ auto responseData = reinterpret_cast<MDRiiGetDirResponse *>(dirInfo.data());
+
+ *data_len = dirInfo.size();
+
+ if (*data_len > MAX_IPMI_BUFFER) // length + completion code should no more
+ // than MAX_IPMI_BUFFER
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Data length send from service is invalid");
+ *data_len = 0;
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ std::copy(&dirInfo[0], &dirInfo[*data_len], dataOut);
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_send_dir(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t data_len, ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const MDRiiSendDirRequest *>(request);
+ std::vector<uint8_t> idVector;
+ bool teminate = false;
+
+ if (*data_len != sizeof(MDRiiSendDirRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ std::string service = ipmi::getService(bus, mdrv2Interface, mdrv2Path);
+
+ if (mdrv2 == nullptr)
+ {
+ mdrv2 = std::make_unique<MDRV2>();
+ }
+
+ int agentIndex = mdrv2->agentLookup(requestData->agentId);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ if ((requestData->dirIndex + requestData->returnedEntries) > maxDirEntries)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Too many directory entries");
+ return IPMI_CC_STORGE_LEAK;
+ }
+
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), mdrv2Path, mdrv2Interface, "SendDirectoryInformation");
+ method.append(requestData->dirVersion, requestData->dirIndex,
+ requestData->returnedEntries, requestData->remainingEntries);
+ uint8_t *reqPoint;
+ for (int index = 0; index < requestData->returnedEntries; index++)
+ {
+ reqPoint = (uint8_t *)&(requestData->data[index]);
+ std::copy(reqPoint, sizeof(Mdr2DirEntry) + reqPoint, idVector.data());
+ }
+ method.append(idVector);
+
+ try
+ {
+ sdbusplus::message::message reply = bus.call(method);
+ reply.read(teminate);
+ }
+ catch (sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
+ InvalidParameter)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error send dir - Invalid parameter");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+ catch (sdbusplus::exception_t &)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error send dir",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", mdrv2Path));
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ *data_len = 1;
+ if (teminate == false)
+ *(static_cast<uint8_t *>(response)) = 0;
+ else
+ *(static_cast<uint8_t *>(response)) = 1;
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_get_data_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t data_len,
+ ipmi_context_t context)
+{
+ auto requestData =
+ reinterpret_cast<const MDRiiGetDataInfoRequest *>(request);
+ auto dataOut = reinterpret_cast<uint8_t *>(response);
+ std::vector<uint8_t> res;
+
+ if (*data_len < sizeof(MDRiiGetDataInfoRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ std::string service = ipmi::getService(bus, mdrv2Interface, mdrv2Path);
+
+ if (mdrv2 == nullptr)
+ {
+ mdrv2 = std::make_unique<MDRV2>();
+ }
+
+ int agentIndex = mdrv2->agentLookup(requestData->agentId);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ int idIndex =
+ mdrv2->findDataId(requestData->dataSetInfo.dataInfo,
+ sizeof(requestData->dataSetInfo.dataInfo), service);
+
+ if ((idIndex < 0) || (idIndex >= maxDirEntries))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), mdrv2Path, mdrv2Interface, "GetDataInformation");
+
+ method.append(idIndex);
+
+ try
+ {
+ sdbusplus::message::message reply = bus.call(method);
+ reply.read(res);
+ }
+ catch (sdbusplus::exception_t &)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error get data info",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", mdrv2Path));
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ if (res.size() != sizeof(MDRiiGetDataInfoResponse))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Get data info response length not invalid");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ *data_len = static_cast<size_t>(res.size());
+ std::copy(&res[0], &res[*data_len], dataOut);
+
+ return IPMI_CC_OK;
+}
+
+// Offer a agent ID to get the "Data Set ID"
+ipmi_ret_t cmd_mdr2_data_info_offer(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t data_len,
+ ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const MDRiiOfferDataInfo *>(request);
+ auto dataOut = reinterpret_cast<uint8_t *>(response);
+ std::vector<uint8_t> dataInfo;
+
+ if (*data_len != sizeof(MDRiiOfferDataInfo))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ std::string service = ipmi::getService(bus, mdrv2Interface, mdrv2Path);
+
+ if (mdrv2 == nullptr)
+ {
+ mdrv2 = std::make_unique<MDRV2>();
+ }
+
+ int agentIndex = mdrv2->agentLookup(requestData->agentId);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), mdrv2Path, mdrv2Interface, "GetDataOffer");
+
+ try
+ {
+ sdbusplus::message::message reply = bus.call(method);
+ reply.read(dataInfo);
+ }
+ catch (
+ sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::UpdateInProgress
+ &)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Send data info offer failed - not available to update data "
+ "into agent at present");
+ return IPMI_CC_PARAMETER_NOT_SUPPORT_IN_PRESENT_STATE;
+ }
+ catch (sdbusplus::exception_t &)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error send data info offer",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", mdrv2Path));
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ if (dataInfo.size() != sizeof(MDRiiOfferDataInfoResponse))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error send data info offer, return length invalid");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ *data_len = dataInfo.size();
+ std::copy(dataInfo.begin(), dataInfo.end(), dataOut);
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_send_data_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t data_len,
+ ipmi_context_t context)
+{
+ auto requestData =
+ reinterpret_cast<const MDRiiSendDataInfoRequest *>(request);
+ bool entryChanged = true;
+
+ if (*data_len != sizeof(MDRiiSendDataInfoRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ if (requestData->dataLength > smbiosTableStorageSize)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Requested data length is out of SMBIOS Table storage size.");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ std::string service = ipmi::getService(bus, mdrv2Interface, mdrv2Path);
+
+ if (mdrv2 == nullptr)
+ {
+ mdrv2 = std::make_unique<MDRV2>();
+ }
+
+ int agentIndex = mdrv2->agentLookup(requestData->agentId);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ int idIndex =
+ mdrv2->findDataId(requestData->dataSetInfo.dataInfo,
+ sizeof(requestData->dataSetInfo.dataInfo), service);
+
+ if ((idIndex < 0) || (idIndex >= maxDirEntries))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), mdrv2Path, mdrv2Interface, "SendDataInformation");
+
+ method.append((uint8_t)idIndex, requestData->validFlag,
+ requestData->dataLength, requestData->dataVersion,
+ requestData->timeStamp);
+
+ try
+ {
+ sdbusplus::message::message reply = bus.call(method);
+ reply.read(entryChanged);
+ }
+ catch (sdbusplus::exception_t &)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error send data info",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", mdrv2Path));
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ *data_len = 1;
+
+ if (entryChanged)
+ {
+ *(static_cast<uint8_t *>(response)) = 1;
+ }
+ else
+ {
+ *(static_cast<uint8_t *>(response)) = 0;
+ }
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_get_data_block(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t data_len,
+ ipmi_context_t context)
+{
+ auto requestData =
+ reinterpret_cast<const MDRiiGetDataBlockRequest *>(request);
+ auto responseData = reinterpret_cast<MDRiiGetDataBlockResponse *>(response);
+ std::tuple<uint8_t, uint32_t, uint32_t, std::vector<uint8_t>> res;
+ std::vector<uint8_t> resData;
+ uint8_t status = 1;
+
+ if (*data_len != sizeof(MDRiiGetDataBlockRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ std::string service = ipmi::getService(bus, mdrv2Interface, mdrv2Path);
+
+ if (mdrv2 == nullptr)
+ {
+ mdrv2 = std::make_unique<MDRV2>();
+ }
+
+ int agentIndex = mdrv2->agentLookup(requestData->agentId);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ int idIndex = mdrv2->findLockHandle(requestData->lockHandle);
+
+ if ((idIndex < 0) || (idIndex >= maxDirEntries))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ if (requestData->xferOffset >= mdrv2->smbiosDir.dir[idIndex].common.size)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Offset is outside of range.");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ size_t outSize =
+ (requestData->xferLength > mdrv2->smbiosDir.dir[idIndex].xferSize)
+ ? mdrv2->smbiosDir.dir[idIndex].xferSize
+ : requestData->xferLength;
+ if (outSize > UINT_MAX - requestData->xferOffset)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Out size and offset are out of range");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+ if ((requestData->xferOffset + outSize) >
+ mdrv2->smbiosDir.dir[idIndex].common.size)
+ {
+ outSize =
+ mdrv2->smbiosDir.dir[idIndex].common.size - requestData->xferOffset;
+ }
+
+ responseData->xferLength = outSize;
+ if (responseData->xferLength > requestData->xferLength)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Get data block unexpected error.");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ if ((requestData->xferOffset + outSize) >
+ UINT_MAX -
+ reinterpret_cast<size_t>(mdrv2->smbiosDir.dir[idIndex].dataStorage))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Input data to calculate checksum is out of range");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ uint32_t u32Checksum = mdrv2->calcChecksum32(
+ mdrv2->smbiosDir.dir[idIndex].dataStorage + requestData->xferOffset,
+ outSize);
+ if (u32Checksum == invalidChecksum)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Get data block failed - invalid checksum");
+ return IPMI_CC_OEM_INVALID_CHECKSUM;
+ }
+ responseData->checksum = u32Checksum;
+
+ *data_len = sizeof(responseData->xferLength) +
+ sizeof(responseData->checksum) + outSize;
+
+ if (*data_len > MAX_IPMI_BUFFER) // length + completion code should no more
+ // than MAX_IPMI_BUFFER
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Data length send from service is invalid");
+ *data_len = 0;
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ std::copy(
+ &mdrv2->smbiosDir.dir[idIndex].dataStorage[requestData->xferOffset],
+ &mdrv2->smbiosDir.dir[idIndex]
+ .dataStorage[requestData->xferOffset + outSize],
+ responseData->data);
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_send_data_block(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t data_len,
+ ipmi_context_t context)
+{
+ auto requestData =
+ reinterpret_cast<const MDRiiSendDataBlockRequest *>(request);
+
+ if (*data_len != sizeof(MDRiiSendDataBlockRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ std::string service = ipmi::getService(bus, mdrv2Interface, mdrv2Path);
+
+ if (mdrv2 == nullptr)
+ {
+ mdrv2 = std::make_unique<MDRV2>();
+ }
+
+ int agentIndex = mdrv2->agentLookup(requestData->agentId);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ int idIndex = mdrv2->findLockHandle(requestData->lockHandle);
+
+ if ((idIndex < 0) || (idIndex >= maxDirEntries))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ if (mdrv2->smbiosIsUpdating(idIndex))
+ {
+ if (requestData->xferOffset > UINT_MAX - requestData->xferLength)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Offset and length are out of range");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+ if (((requestData->xferOffset + requestData->xferLength) >
+ mdrv2->smbiosDir.dir[idIndex].maxDataSize) ||
+ ((requestData->xferOffset + requestData->xferLength) >
+ mdrv2->smbiosDir.dir[idIndex].common.dataSetSize))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Send data block Invalid offset/length");
+ return IPMI_CC_REQUEST_DATA_FIELD_LENGTH_LIMIT_EXCEEDED;
+ }
+ if (reinterpret_cast<size_t>(
+ mdrv2->smbiosDir.dir[idIndex].dataStorage) >
+ UINT_MAX - requestData->xferOffset)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Offset is out of range");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+ uint8_t *destAddr =
+ mdrv2->smbiosDir.dir[idIndex].dataStorage + requestData->xferOffset;
+ uint8_t *sourceAddr = reinterpret_cast<uint8_t *>(mdrv2->area->vPtr);
+ uint32_t calcChecksum =
+ mdrv2->calcChecksum32(sourceAddr, requestData->xferLength);
+ if (calcChecksum != requestData->checksum)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Send data block Invalid checksum");
+ return IPMI_CC_OEM_INVALID_CHECKSUM;
+ }
+ else
+ {
+ if (reinterpret_cast<size_t>(sourceAddr) >
+ UINT_MAX - requestData->xferLength)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Length is out of range");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+ std::copy(sourceAddr, sourceAddr + requestData->xferLength,
+ destAddr);
+ }
+ }
+ else
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Send data block failed, other data is updating");
+ return IPMI_CC_DESTINATION_UNAVAILABLE;
+ }
+
+ return IPMI_CC_OK;
+}
+
+bool MDRV2::storeDatatoFlash(MDRSMBIOSHeader *mdrHdr, uint8_t *data)
+{
+ std::ofstream smbiosFile(mdrType2File,
+ std::ios_base::binary | std::ios_base::trunc);
+ if (!smbiosFile.good())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Write data from flash error - Open MDRV2 table file failure");
+ return false;
+ }
+
+ try
+ {
+ smbiosFile.write(reinterpret_cast<char *>(mdrHdr),
+ sizeof(MDRSMBIOSHeader));
+ smbiosFile.write(reinterpret_cast<char *>(data), mdrHdr->dataSize);
+ }
+ catch (std::ofstream::failure &e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Write data from flash error - write data error");
+ return false;
+ }
+
+ return true;
+}
+
+void SharedMemoryArea::Initialize(uint32_t addr, uint32_t areaSize)
+{
+ int memDriver = 0;
+
+ // open mem driver for the system memory access
+ memDriver = open("/dev/vgasharedmem", O_RDONLY);
+ if (memDriver < 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Cannot access mem driver");
+ throw std::system_error(EIO, std::generic_category());
+ }
+
+ // map the system memory
+ vPtr = mmap(NULL, // where to map to: don't mind
+ areaSize, // how many bytes ?
+ PROT_READ, // want to read and write
+ MAP_SHARED, // no copy on write
+ memDriver, // handle to /dev/mem
+ (physicalAddr & pageMask)); // hopefully the Text-buffer :-)
+
+ close(memDriver);
+ if (vPtr == MAP_FAILED)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Failed to map share memory");
+ throw std::system_error(EIO, std::generic_category());
+ }
+ size = areaSize;
+ physicalAddr = addr;
+}
+
+bool MDRV2::smbiosUnlock(uint8_t index)
+{
+ bool ret;
+ switch (smbiosDir.dir[index].stage)
+ {
+ case MDR2SMBIOSStatusEnum::mdr2Updating:
+ smbiosDir.dir[index].stage = MDR2SMBIOSStatusEnum::mdr2Updated;
+ smbiosDir.dir[index].lock = MDR2DirLockEnum::mdr2DirUnlock;
+
+ timer->stop();
+ smbiosDir.dir[index].lockHandle = 0;
+ ret = true;
+ break;
+
+ case MDR2SMBIOSStatusEnum::mdr2Updated:
+ case MDR2SMBIOSStatusEnum::mdr2Loaded:
+ smbiosDir.dir[index].lock = MDR2DirLockEnum::mdr2DirUnlock;
+
+ timer->stop();
+
+ smbiosDir.dir[index].lockHandle = 0;
+ ret = true;
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+bool MDRV2::smbiosTryLock(uint8_t flag, uint8_t index, uint16_t *session,
+ uint16_t timeout)
+{
+ bool ret = false;
+ uint32_t u32Status = 0;
+
+ if (timeout == 0)
+ {
+ timeout = defaultTimeout;
+ }
+ std::chrono::microseconds usec(timeout * sysClock);
+
+ switch (smbiosDir.dir[index].stage)
+ {
+ case MDR2SMBIOSStatusEnum::mdr2Updating:
+ if (smbiosDir.dir[index].lock != MDR2DirLockEnum::mdr2DirLock)
+ {
+ smbiosDir.dir[index].lock = MDR2DirLockEnum::mdr2DirLock;
+ timer->start(usec);
+ lockIndex = index;
+
+ *session = getSessionHandle(&smbiosDir);
+ smbiosDir.dir[index].lockHandle = *session;
+ ret = true;
+ }
+ break;
+ case MDR2SMBIOSStatusEnum::mdr2Init:
+ if (flag)
+ {
+ smbiosDir.dir[index].stage = MDR2SMBIOSStatusEnum::mdr2Updating;
+ smbiosDir.dir[index].lock = MDR2DirLockEnum::mdr2DirUnlock;
+ timer->start(usec);
+ lockIndex = index;
+
+ *session = getSessionHandle(&smbiosDir);
+ smbiosDir.dir[index].lockHandle = *session;
+ ret = true;
+ }
+ break;
+
+ case MDR2SMBIOSStatusEnum::mdr2Updated:
+ case MDR2SMBIOSStatusEnum::mdr2Loaded:
+ if (smbiosDir.dir[index].lock != MDR2DirLockEnum::mdr2DirLock)
+ {
+ if (flag)
+ {
+ smbiosDir.dir[index].stage =
+ MDR2SMBIOSStatusEnum::mdr2Updating;
+ smbiosDir.dir[index].lock = MDR2DirLockEnum::mdr2DirUnlock;
+ }
+ else
+ {
+ smbiosDir.dir[index].lock = MDR2DirLockEnum::mdr2DirLock;
+ }
+
+ timer->start(usec);
+ lockIndex = index;
+
+ *session = getSessionHandle(&smbiosDir);
+ smbiosDir.dir[index].lockHandle = *session;
+ ret = true;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return ret;
+}
+
+void MDRV2::timeoutHandler()
+{
+ smbiosUnlock(lockIndex);
+ mdrv2->area.reset(nullptr);
+}
+
+ipmi_ret_t cmd_mdr2_lock_data(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t data_len, ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const MDRiiLockDataRequest *>(request);
+ auto responseData = reinterpret_cast<MDRiiLockDataResponse *>(response);
+ uint16_t session = 0;
+
+ std::tuple<bool, uint8_t, uint16_t, uint32_t, uint32_t, uint32_t> res;
+
+ if (*data_len < sizeof(MDRiiLockDataRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ if (mdrv2 == nullptr)
+ {
+ mdrv2 = std::make_unique<MDRV2>();
+ }
+
+ int agentIndex = mdrv2->agentLookup(requestData->agentId);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ std::string service = ipmi::getService(bus, mdrv2Interface, mdrv2Path);
+
+ int idIndex =
+ mdrv2->findDataId(requestData->dataSetInfo.dataInfo,
+ sizeof(requestData->dataSetInfo.dataInfo), service);
+
+ if ((idIndex < 0) || (idIndex >= maxDirEntries))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ if (!mdrv2->smbiosTryLock(0, idIndex, &session, requestData->timeout))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Lock Data failed - cannot lock idIndex");
+ return IPMI_CC_PARAMETER_NOT_SUPPORT_IN_PRESENT_STATE;
+ }
+
+ *data_len = sizeof(MDRiiLockDataResponse);
+
+ responseData->mdrVersion = mdr2Version;
+ responseData->lockHandle = session;
+ responseData->dataLength = mdrv2->smbiosDir.dir[idIndex].common.size;
+ responseData->xferAddress = mdrv2->smbiosDir.dir[idIndex].xferBuff;
+ responseData->xferLength = mdrv2->smbiosDir.dir[idIndex].xferSize;
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_unlock_data(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t data_len,
+ ipmi_context_t context)
+{
+ phosphor::logging::log<phosphor::logging::level::ERR>("unlock data");
+ auto requestData =
+ reinterpret_cast<const MDRiiUnlockDataRequest *>(request);
+
+ if (*data_len != sizeof(MDRiiUnlockDataRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ std::string service = ipmi::getService(bus, mdrv2Interface, mdrv2Path);
+
+ if (mdrv2 == nullptr)
+ {
+ mdrv2 = std::make_unique<MDRV2>();
+ }
+
+ int agentIndex = mdrv2->agentLookup(requestData->agentId);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ int idIndex = mdrv2->findLockHandle(requestData->lockHandle);
+
+ if ((idIndex < 0) || (idIndex >= maxDirEntries))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ if (!mdrv2->smbiosUnlock(idIndex))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unlock Data failed - cannot unlock idIndex");
+ return IPMI_CC_PARAMETER_NOT_SUPPORT_IN_PRESENT_STATE;
+ }
+
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_data_start(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t data_len, ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const MDRiiDataStartRequest *>(request);
+ auto responseData = reinterpret_cast<MDRiiDataStartResponse *>(response);
+ std::vector<uint8_t> idVector;
+ uint16_t session = 0;
+
+ if (*data_len != sizeof(MDRiiDataStartRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ if (requestData->dataLength > smbiosTableStorageSize)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Requested data length is out of SMBIOS Table storage size.");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ if ((requestData->xferLength + requestData->xferAddress) > mdriiSMSize)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid data address and size");
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ std::string service = ipmi::getService(bus, mdrv2Interface, mdrv2Path);
+
+ if (mdrv2 == nullptr)
+ {
+ mdrv2 = std::make_unique<MDRV2>();
+ }
+
+ int agentIndex = mdrv2->agentLookup(requestData->agentId);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ int idIndex =
+ mdrv2->findDataId(requestData->dataSetInfo.dataInfo,
+ sizeof(requestData->dataSetInfo.dataInfo), service);
+
+ if ((idIndex < 0) || (idIndex >= maxDirEntries))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ if (mdrv2->smbiosTryLock(1, idIndex, &session, requestData->timeout))
+ {
+ try
+ {
+ mdrv2->area = std::make_unique<SharedMemoryArea>(
+ requestData->xferAddress, requestData->xferLength);
+ }
+ catch (const std::system_error &e)
+ {
+ mdrv2->smbiosUnlock(idIndex);
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unable to access share memory");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ mdrv2->smbiosDir.dir[idIndex].common.size = requestData->dataLength;
+ mdrv2->smbiosDir.dir[idIndex].lockHandle = session;
+ if (-1 ==
+ mdrv2->syncDirCommonData(
+ idIndex, mdrv2->smbiosDir.dir[idIndex].common.size, service))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unable to sync data to service");
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+ }
+ else
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Canot lock smbios");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ responseData->sessionHandle = session;
+ responseData->xferStartAck = 1;
+
+ *data_len = sizeof(MDRiiDataStartResponse);
+ return IPMI_CC_OK;
+}
+
+ipmi_ret_t cmd_mdr2_data_done(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request, ipmi_response_t response,
+ ipmi_data_len_t data_len, ipmi_context_t context)
+{
+ auto requestData = reinterpret_cast<const MDRiiDataDoneRequest *>(request);
+
+ if (*data_len != sizeof(MDRiiDataDoneRequest))
+ {
+ *data_len = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ *data_len = 0;
+
+ if (mdrv2 == nullptr)
+ {
+ mdrv2 = std::make_unique<MDRV2>();
+ }
+
+ int agentIndex = mdrv2->agentLookup(requestData->agentId);
+ if (agentIndex == -1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Unknown agent id",
+ phosphor::logging::entry("ID=%x", requestData->agentId));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ int idIndex = mdrv2->findLockHandle(requestData->lockHandle);
+
+ if ((idIndex < 0) || (idIndex >= maxDirEntries))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex));
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ if (!mdrv2->smbiosUnlock(idIndex))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Send data done failed - cannot unlock idIndex");
+ return IPMI_CC_DESTINATION_UNAVAILABLE;
+ }
+
+ mdrv2->area.reset(nullptr);
+ MDRSMBIOSHeader mdr2Smbios;
+ mdr2Smbios.mdrType = mdrTypeII;
+ mdr2Smbios.dirVer = mdrv2->smbiosDir.dir[0].common.dataVersion;
+ mdr2Smbios.timestamp = mdrv2->smbiosDir.dir[0].common.timestamp;
+ mdr2Smbios.dataSize = mdrv2->smbiosDir.dir[0].common.size;
+
+ if (access(smbiosPath, 0) == -1)
+ {
+ int flag = mkdir(smbiosPath, S_IRWXU);
+ if (flag != 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "create folder failed for writting smbios file");
+ }
+ }
+ if (!mdrv2->storeDatatoFlash(
+ &mdr2Smbios, mdrv2->smbiosDir.dir[smbiosDirIndex].dataStorage))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "MDR2 Store data to flash failed");
+ return IPMI_CC_DESTINATION_UNAVAILABLE;
+ }
+
+ bool status = false;
+ std::string service = ipmi::getService(bus, mdrv2Interface, mdrv2Path);
+ sdbusplus::message::message method = bus.new_method_call(
+ service.c_str(), mdrv2Path, mdrv2Interface, "AgentSynchronizeData");
+
+ try
+ {
+ sdbusplus::message::message reply = bus.call(method);
+ reply.read(status);
+ }
+ catch (sdbusplus::exception_t &)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error Sync data with service",
+ phosphor::logging::entry("SERVICE=%s", service.c_str()),
+ phosphor::logging::entry("PATH=%s", mdrv2Path));
+ return IPMI_CC_RESPONSE_ERROR;
+ }
+
+ if (!status)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Sync data with service failure");
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ return IPMI_CC_OK;
+}
+
+static void register_netfn_smbiosmdrv2_functions(void)
+{
+ // MDR V2 Command
+ // <Get MDRII Status Command>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_AGENT_STATUS,
+ NULL, cmd_mdr2_agent_status, PRIVILEGE_OPERATOR);
+
+ // <Get MDRII Directory Command>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_GET_DIR, NULL,
+ cmd_mdr2_get_dir, PRIVILEGE_OPERATOR);
+
+ // <Send MDRII Directory Command>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_SEND_DIR, NULL,
+ cmd_mdr2_send_dir, PRIVILEGE_OPERATOR);
+
+ // <Get MDRII Data Info Command>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_GET_DATA_INFO,
+ NULL, cmd_mdr2_get_data_info, PRIVILEGE_OPERATOR);
+
+ // <Send MDRII Info Offer>
+ ipmi_register_callback(
+ NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_SEND_DATA_INFO_OFFER, NULL,
+ cmd_mdr2_data_info_offer, PRIVILEGE_OPERATOR);
+
+ // <Send MDRII Data Info>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_SEND_DATA_INFO,
+ NULL, cmd_mdr2_send_data_info, PRIVILEGE_OPERATOR);
+
+ // <Get MDRII Data Block Command>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_GET_DATA_BLOCK,
+ NULL, cmd_mdr2_get_data_block, PRIVILEGE_OPERATOR);
+
+ // <Send MDRII Data Block>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_SEND_DATA_BLOCK,
+ NULL, cmd_mdr2_send_data_block, PRIVILEGE_OPERATOR);
+
+ // <Lock MDRII Data Command>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_LOCK_DATA, NULL,
+ cmd_mdr2_lock_data, PRIVILEGE_OPERATOR);
+
+ // <Unlock MDRII Data Command>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_UNLOCK_DATA,
+ NULL, cmd_mdr2_unlock_data, PRIVILEGE_OPERATOR);
+
+ // <Send MDRII Data Start>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_DATA_START, NULL,
+ cmd_mdr2_data_start, PRIVILEGE_OPERATOR);
+
+ // <Send MDRII Data Done>
+ ipmi_register_callback(NETFUN_INTEL_APP_OEM,
+ IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_DATA_DONE, NULL,
+ cmd_mdr2_data_done, PRIVILEGE_OPERATOR);
+}