/* // 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 <limits.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <commandutils.hpp> #include <ipmid/api.hpp> #include <ipmid/utils.hpp> #include <oemcommands.hpp> #include <phosphor-logging/log.hpp> #include <sdbusplus/message/types.hpp> #include <smbiosmdrv2handler.hpp> #include <xyz/openbmc_project/Common/error.hpp> #include <cstdint> #include <fstream> #include <string> #include <vector> std::unique_ptr<MDRV2> mdrv2 = nullptr; static constexpr const uint8_t ccOemInvalidChecksum = 0x85; static constexpr size_t dataInfoSize = 16; static constexpr const uint8_t ccStorageLeak = 0xC4; static void register_netfn_smbiosmdrv2_functions() __attribute__((constructor)); 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, ipmi::DbusVariant& value, const std::string& service) { std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); sdbusplus::message_t method = bus->new_method_call(service.c_str(), mdrv2Path, dbusProperties, "Get"); method.append(mdrv2Interface, name); sdbusplus::message_t reply = bus->call(method); try { sdbusplus::message_t reply = bus->call(method); reply.read(value); } catch (const sdbusplus::exception_t& e) { phosphor::logging::log<phosphor::logging::level::ERR>( "Error get property, sdbusplus call failed", phosphor::logging::entry("ERROR=%s", e.what())); return -1; } return 0; } int MDRV2::syncDirCommonData(uint8_t idIndex, uint32_t size, const std::string& service) { std::vector<uint32_t> commonData; std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); sdbusplus::message_t method = bus->new_method_call(service.c_str(), mdrv2Path, mdrv2Interface, "SynchronizeDirectoryCommonData"); method.append(idIndex, size); try { sdbusplus::message_t reply = bus->call(method); reply.read(commonData); } catch (const sdbusplus::exception_t& e) { phosphor::logging::log<phosphor::logging::level::ERR>( "Error sync dir common data with service", phosphor::logging::entry("ERROR=%s", e.what())); 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; } std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); sdbusplus::message_t 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_t reply = bus->call(method); reply.read(idIndex); } catch (const sdbusplus::exception_t& e) { phosphor::logging::log<phosphor::logging::level::ERR>( "Error find id index", phosphor::logging::entry("ERROR=%s", e.what()), 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; } /** @brief implements mdr2 agent status command * @param agentId * @param dirVersion * * @returns IPMI completion code plus response data * - mdrVersion * - agentVersion * - dirVersion * - dirEntries * - dataRequest */ ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t> mdr2AgentStatus(uint16_t agentId, uint8_t dirVersion) { if (mdrv2 == nullptr) { mdrv2 = std::make_unique<MDRV2>(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log<phosphor::logging::level::ERR>( "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); return ipmi::responseParmOutOfRange(); } constexpr uint8_t mdrVersion = mdr2Version; constexpr uint8_t agentVersion = smbiosAgentVersion; uint8_t dirVersionResp = mdrv2->smbiosDir.dirVersion; uint8_t dirEntries = mdrv2->smbiosDir.dirEntries; uint8_t dataRequest; if (mdrv2->smbiosDir.remoteDirVersion != dirVersion) { mdrv2->smbiosDir.remoteDirVersion = dirVersion; dataRequest = static_cast<uint8_t>(DirDataRequestEnum::dirDataRequested); } else { dataRequest = static_cast<uint8_t>(DirDataRequestEnum::dirDataNotRequested); } return ipmi::responseSuccess(mdrVersion, agentVersion, dirVersionResp, dirEntries, dataRequest); } /** @brief implements mdr2 get directory command * @param agentId * @param dirIndex * @returns IPMI completion code plus response data * - dataOut */ ipmi::RspType<std::vector<uint8_t>> mdr2GetDir(uint16_t agentId, uint8_t dirIndex) { std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); if (mdrv2 == nullptr) { mdrv2 = std::make_unique<MDRV2>(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log<phosphor::logging::level::ERR>( "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); return ipmi::responseParmOutOfRange(); } ipmi::DbusVariant value = static_cast<uint8_t>(0); if (0 != mdrv2->sdplusMdrv2GetProperty("DirectoryEntries", value, service)) { phosphor::logging::log<phosphor::logging::level::ERR>( "Error getting DirEnries"); return ipmi::responseUnspecifiedError(); } if (std::get<uint8_t>(value) == 0) { phosphor::logging::log<phosphor::logging::level::ERR>( "Error getting directory entries", phosphor::logging::entry("VALUE=%x", std::get<uint8_t>(value))); return ipmi::responseUnspecifiedError(); } if (dirIndex > std::get<uint8_t>(value)) { return ipmi::responseParmOutOfRange(); } sdbusplus::message_t method = bus->new_method_call( service.c_str(), mdrv2Path, mdrv2Interface, "GetDirectoryInformation"); method.append(dirIndex); std::vector<uint8_t> dataOut; try { sdbusplus::message_t reply = bus->call(method); reply.read(dataOut); } catch (const sdbusplus::exception_t& e) { phosphor::logging::log<phosphor::logging::level::ERR>( "Error get dir", phosphor::logging::entry("ERROR=%s", e.what()), phosphor::logging::entry("SERVICE=%s", service.c_str()), phosphor::logging::entry("PATH=%s", mdrv2Path)); return ipmi::responseResponseError(); } constexpr size_t getDirRespSize = 6; if (dataOut.size() < getDirRespSize) { phosphor::logging::log<phosphor::logging::level::ERR>( "Error get dir, response length invalid"); return ipmi::responseUnspecifiedError(); } if (dataOut.size() > 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"); return ipmi::responseResponseError(); } return ipmi::responseSuccess(dataOut); } /** @brief implements mdr2 send directory info command * @param agentId * @param dirVersion * @param dirIndex * @param returnedEntries * @param remainingEntries * @param dataInfo * dataInfo is 32 Bytes in size and contains below parameters * - dataInfo, size, dataSetSize, dataVersion, timestamp * * @returns IPMI completion code plus response data * - bool */ ipmi::RspType<bool> mdr2SendDir(uint16_t agentId, uint8_t dirVersion, uint8_t dirIndex, uint8_t returnedEntries, uint8_t remainingEntries, std::vector<uint8_t> dataInfo) { if ((static_cast<size_t>(returnedEntries) * dataInfoSize) != dataInfo.size()) { return ipmi::responseReqDataLenInvalid(); } std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); if (mdrv2 == nullptr) { mdrv2 = std::make_unique<MDRV2>(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log<phosphor::logging::level::ERR>( "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); return ipmi::responseParmOutOfRange(); } if ((dirIndex + returnedEntries) > maxDirEntries) { phosphor::logging::log<phosphor::logging::level::ERR>( "Too many directory entries"); return ipmi::response(ccStorageLeak); } sdbusplus::message_t method = bus->new_method_call( service.c_str(), mdrv2Path, mdrv2Interface, "SendDirectoryInformation"); method.append(dirVersion, dirIndex, returnedEntries, remainingEntries, dataInfo); bool terminate = false; try { sdbusplus::message_t reply = bus->call(method); reply.read(terminate); } catch (const sdbusplus::exception_t& e) { phosphor::logging::log<phosphor::logging::level::ERR>( "Error send dir", phosphor::logging::entry("ERROR=%s", e.what()), phosphor::logging::entry("SERVICE=%s", service.c_str()), phosphor::logging::entry("PATH=%s", mdrv2Path)); return ipmi::responseResponseError(); } return ipmi::responseSuccess(terminate); } /** @brief implements mdr2 get data info command * @param agentId * @param dataInfo * * @returns IPMI completion code plus response data * - response - mdrVersion, data info, validFlag, * dataLength, dataVersion, timeStamp */ ipmi::RspType<std::vector<uint8_t>> mdr2GetDataInfo(uint16_t agentId, std::vector<uint8_t> dataInfo) { constexpr size_t getDataInfoReqSize = 16; if (dataInfo.size() < getDataInfoReqSize) { return ipmi::responseReqDataLenInvalid(); } std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); if (mdrv2 == nullptr) { mdrv2 = std::make_unique<MDRV2>(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log<phosphor::logging::level::ERR>( "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); return ipmi::responseParmOutOfRange(); } int idIndex = mdrv2->findDataId(dataInfo.data(), dataInfo.size(), service); if ((idIndex < 0) || (idIndex >= maxDirEntries)) { phosphor::logging::log<phosphor::logging::level::ERR>( "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); return ipmi::responseParmOutOfRange(); } sdbusplus::message_t method = bus->new_method_call( service.c_str(), mdrv2Path, mdrv2Interface, "GetDataInformation"); method.append(static_cast<uint8_t>(idIndex)); std::vector<uint8_t> res; try { sdbusplus::message_t reply = bus->call(method); reply.read(res); } catch (const sdbusplus::exception_t& e) { phosphor::logging::log<phosphor::logging::level::ERR>( "Error get data info", phosphor::logging::entry("ERROR=%s", e.what()), phosphor::logging::entry("SERVICE=%s", service.c_str()), phosphor::logging::entry("PATH=%s", mdrv2Path)); return ipmi::responseResponseError(); } if (res.size() != sizeof(MDRiiGetDataInfoResponse)) { phosphor::logging::log<phosphor::logging::level::ERR>( "Get data info response length not invalid"); return ipmi::responseResponseError(); } return ipmi::responseSuccess(res); } /** @brief implements mdr2 data info offer command * @param agentId - Offer a agent ID to get the "Data Set ID" * * @returns IPMI completion code plus response data * - dataOut - data Set Id */ ipmi::RspType<std::vector<uint8_t>> mdr2DataInfoOffer(uint16_t agentId) { std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); if (mdrv2 == nullptr) { mdrv2 = std::make_unique<MDRV2>(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log<phosphor::logging::level::ERR>( "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); return ipmi::responseParmOutOfRange(); } sdbusplus::message_t method = bus->new_method_call( service.c_str(), mdrv2Path, mdrv2Interface, "GetDataOffer"); std::vector<uint8_t> dataOut; try { sdbusplus::message_t reply = bus->call(method); reply.read(dataOut); } catch (const sdbusplus::exception_t& e) { phosphor::logging::log<phosphor::logging::level::ERR>( "Error send data info offer", phosphor::logging::entry("ERROR=%s", e.what()), phosphor::logging::entry("SERVICE=%s", service.c_str()), phosphor::logging::entry("PATH=%s", mdrv2Path)); return ipmi::responseResponseError(); } constexpr size_t respInfoSize = 16; if (dataOut.size() != respInfoSize) { phosphor::logging::log<phosphor::logging::level::ERR>( "Error send data info offer, return length invalid"); return ipmi::responseUnspecifiedError(); } return ipmi::responseSuccess(dataOut); } /** @brief implements mdr2 send data info command * @param agentId * @param dataInfo * @param validFlag * @param dataLength * @param dataVersion * @param timeStamp * * @returns IPMI completion code plus response data * - bool */ ipmi::RspType<bool> mdr2SendDataInfo(uint16_t agentId, std::array<uint8_t, dataInfoSize> dataInfo, uint8_t validFlag, uint32_t dataLength, uint32_t dataVersion, uint32_t timeStamp) { if (dataLength > smbiosTableStorageSize) { phosphor::logging::log<phosphor::logging::level::ERR>( "Requested data length is out of SMBIOS Table storage size."); return ipmi::responseParmOutOfRange(); } std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); if (mdrv2 == nullptr) { mdrv2 = std::make_unique<MDRV2>(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log<phosphor::logging::level::ERR>( "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); return ipmi::responseParmOutOfRange(); } int idIndex = mdrv2->findDataId(dataInfo.data(), dataInfo.size(), service); if ((idIndex < 0) || (idIndex >= maxDirEntries)) { phosphor::logging::log<phosphor::logging::level::ERR>( "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); return ipmi::responseParmOutOfRange(); } sdbusplus::message_t method = bus->new_method_call( service.c_str(), mdrv2Path, mdrv2Interface, "SendDataInformation"); method.append((uint8_t)idIndex, validFlag, dataLength, dataVersion, timeStamp); bool entryChanged = true; try { sdbusplus::message_t reply = bus->call(method); reply.read(entryChanged); } catch (const sdbusplus::exception_t& e) { phosphor::logging::log<phosphor::logging::level::ERR>( "Error send data info", phosphor::logging::entry("ERROR=%s", e.what()), phosphor::logging::entry("SERVICE=%s", service.c_str()), phosphor::logging::entry("PATH=%s", mdrv2Path)); return ipmi::responseResponseError(); } return ipmi::responseSuccess(entryChanged); } /** @brief This command is MDR related get data block command. @param - agentId @param - lockHandle @param - xferOffset @param - xferLength @return on success - xferLength - checksum - data **/ ipmi::RspType<uint32_t, // xferLength uint32_t, // Checksum std::vector<uint8_t> // data > mdr2GetDataBlock(uint16_t agentId, uint16_t lockHandle, uint32_t xferOffset, uint32_t xferLength) { if (mdrv2 == nullptr) { mdrv2 = std::make_unique<MDRV2>(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log<phosphor::logging::level::ERR>( "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); return ipmi::responseParmOutOfRange(); } int idIndex = mdrv2->findLockHandle(lockHandle); if ((idIndex < 0) || (idIndex >= maxDirEntries)) { phosphor::logging::log<phosphor::logging::level::ERR>( "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); return ipmi::responseParmOutOfRange(); } if (xferOffset >= mdrv2->smbiosDir.dir[idIndex].common.size) { phosphor::logging::log<phosphor::logging::level::ERR>( "Offset is outside of range."); return ipmi::responseParmOutOfRange(); } size_t outSize = (xferLength > mdrv2->smbiosDir.dir[idIndex].xferSize) ? mdrv2->smbiosDir.dir[idIndex].xferSize : xferLength; if (outSize > UINT_MAX - xferOffset) { phosphor::logging::log<phosphor::logging::level::ERR>( "Out size and offset are out of range"); return ipmi::responseParmOutOfRange(); } if ((xferOffset + outSize) > mdrv2->smbiosDir.dir[idIndex].common.size) { outSize = mdrv2->smbiosDir.dir[idIndex].common.size - xferOffset; } uint32_t respXferLength = outSize; if (respXferLength > xferLength) { phosphor::logging::log<phosphor::logging::level::ERR>( "Get data block unexpected error."); return ipmi::responseUnspecifiedError(); } if ((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::responseParmOutOfRange(); } uint32_t u32Checksum = mdrv2->calcChecksum32( mdrv2->smbiosDir.dir[idIndex].dataStorage + xferOffset, outSize); if (u32Checksum == invalidChecksum) { phosphor::logging::log<phosphor::logging::level::ERR>( "Get data block failed - invalid checksum"); return ipmi::response(ccOemInvalidChecksum); } std::vector<uint8_t> data(outSize); std::copy(&mdrv2->smbiosDir.dir[idIndex].dataStorage[xferOffset], &mdrv2->smbiosDir.dir[idIndex].dataStorage[xferOffset + outSize], data.begin()); return ipmi::responseSuccess(respXferLength, u32Checksum, data); } /** @brief implements mdr2 send data block command * @param agentId * @param lockHandle * @param xferOffset * @param xferLength * @param checksum * * @returns IPMI completion code */ ipmi::RspType<> mdr2SendDataBlock(uint16_t agentId, uint16_t lockHandle, uint32_t xferOffset, uint32_t xferLength, uint32_t checksum) { if (mdrv2 == nullptr) { mdrv2 = std::make_unique<MDRV2>(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log<phosphor::logging::level::ERR>( "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); return ipmi::responseParmOutOfRange(); } int idIndex = mdrv2->findLockHandle(lockHandle); if ((idIndex < 0) || (idIndex >= maxDirEntries)) { phosphor::logging::log<phosphor::logging::level::ERR>( "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); return ipmi::responseParmOutOfRange(); } if (mdrv2->smbiosIsUpdating(idIndex)) { if (xferOffset > UINT_MAX - xferLength) { phosphor::logging::log<phosphor::logging::level::ERR>( "Offset and length are out of range"); return ipmi::responseParmOutOfRange(); } if (((xferOffset + xferLength) > mdrv2->smbiosDir.dir[idIndex].maxDataSize) || ((xferOffset + xferLength) > mdrv2->smbiosDir.dir[idIndex].common.dataSetSize)) { phosphor::logging::log<phosphor::logging::level::ERR>( "Send data block Invalid offset/length"); return ipmi::responseReqDataLenExceeded(); } if (reinterpret_cast<size_t>( mdrv2->smbiosDir.dir[idIndex].dataStorage) > UINT_MAX - xferOffset) { phosphor::logging::log<phosphor::logging::level::ERR>( "Offset is out of range"); return ipmi::responseParmOutOfRange(); } uint8_t* destAddr = mdrv2->smbiosDir.dir[idIndex].dataStorage + xferOffset; uint8_t* sourceAddr = reinterpret_cast<uint8_t*>(mdrv2->area->vPtr); uint32_t calcChecksum = mdrv2->calcChecksum32(sourceAddr, xferLength); if (calcChecksum != checksum) { phosphor::logging::log<phosphor::logging::level::ERR>( "Send data block Invalid checksum"); return ipmi::response(ccOemInvalidChecksum); } else { if (reinterpret_cast<size_t>(sourceAddr) > UINT_MAX - xferLength) { phosphor::logging::log<phosphor::logging::level::ERR>( "Length is out of range"); return ipmi::responseParmOutOfRange(); } std::copy(sourceAddr, sourceAddr + xferLength, destAddr); } } else { phosphor::logging::log<phosphor::logging::level::ERR>( "Send data block failed, other data is updating"); return ipmi::responseDestinationUnavailable(); } return ipmi::responseSuccess(); } 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 (const std::ofstream::failure& e) { phosphor::logging::log<phosphor::logging::level::ERR>( "Write data from flash error - write data error", phosphor::logging::entry("ERROR=%s", e.what())); 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 = false; 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); } /** @brief implements mdr2 lock data command * @param agentId * @param dataInfo * @param timeout * * @returns IPMI completion code plus response data * - mdr2Version * - session * - dataLength * - xferAddress * - xferLength */ ipmi::RspType<uint8_t, // mdr2Version uint16_t, // session uint32_t, // dataLength uint32_t, // xferAddress uint32_t // xferLength > mdr2LockData(uint16_t agentId, std::array<uint8_t, dataInfoSize> dataInfo, uint16_t timeout) { if (mdrv2 == nullptr) { mdrv2 = std::make_unique<MDRV2>(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log<phosphor::logging::level::ERR>( "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); return ipmi::responseParmOutOfRange(); } std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); int idIndex = mdrv2->findDataId(dataInfo.data(), dataInfo.size(), service); if ((idIndex < 0) || (idIndex >= maxDirEntries)) { phosphor::logging::log<phosphor::logging::level::ERR>( "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); return ipmi::responseParmOutOfRange(); } uint16_t session = 0; if (!mdrv2->smbiosTryLock(0, idIndex, &session, timeout)) { phosphor::logging::log<phosphor::logging::level::ERR>( "Lock Data failed - cannot lock idIndex"); return ipmi::responseCommandNotAvailable(); } uint32_t dataLength = mdrv2->smbiosDir.dir[idIndex].common.size; uint32_t xferAddress = mdrv2->smbiosDir.dir[idIndex].xferBuff; uint32_t xferLength = mdrv2->smbiosDir.dir[idIndex].xferSize; return ipmi::responseSuccess(mdr2Version, session, dataLength, xferAddress, xferLength); } /** @brief implements mdr2 unlock data command * @param agentId * @param lockHandle * * @returns IPMI completion code */ ipmi::RspType<> mdr2UnlockData(uint16_t agentId, uint16_t lockHandle) { phosphor::logging::log<phosphor::logging::level::ERR>("unlock data"); if (mdrv2 == nullptr) { mdrv2 = std::make_unique<MDRV2>(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log<phosphor::logging::level::ERR>( "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); return ipmi::responseParmOutOfRange(); } int idIndex = mdrv2->findLockHandle(lockHandle); if ((idIndex < 0) || (idIndex >= maxDirEntries)) { phosphor::logging::log<phosphor::logging::level::ERR>( "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); return ipmi::responseParmOutOfRange(); } if (!mdrv2->smbiosUnlock(idIndex)) { phosphor::logging::log<phosphor::logging::level::ERR>( "Unlock Data failed - cannot unlock idIndex"); return ipmi::responseCommandNotAvailable(); } return ipmi::responseSuccess(); } /** @brief This command is executed after POST BIOS to get the session info. @param - agentId, dataInfo, dataLength, xferAddress, xferLength, timeout. @return xferStartAck and session on success. **/ ipmi::RspType<uint8_t, uint16_t> cmd_mdr2_data_start(uint16_t agentId, std::array<uint8_t, 16> dataInfo, uint32_t dataLength, uint32_t xferAddress, uint32_t xferLength, uint16_t timeout) { uint16_t session = 0; if (dataLength > smbiosTableStorageSize) { phosphor::logging::log<phosphor::logging::level::ERR>( "Requested data length is out of SMBIOS Table storage size."); return ipmi::responseParmOutOfRange(); } if ((xferLength + xferAddress) > mdriiSMSize) { phosphor::logging::log<phosphor::logging::level::ERR>( "Invalid data address and size"); return ipmi::responseParmOutOfRange(); } std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); if (mdrv2 == nullptr) { mdrv2 = std::make_unique<MDRV2>(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log<phosphor::logging::level::ERR>( "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); return ipmi::responseParmOutOfRange(); } int idIndex = mdrv2->findDataId(dataInfo.data(), dataInfo.size(), service); if ((idIndex < 0) || (idIndex >= maxDirEntries)) { phosphor::logging::log<phosphor::logging::level::ERR>( "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); return ipmi::responseParmOutOfRange(); } if (mdrv2->smbiosTryLock(1, idIndex, &session, timeout)) { try { mdrv2->area = std::make_unique<SharedMemoryArea>(xferAddress, xferLength); } catch (const std::system_error& e) { mdrv2->smbiosUnlock(idIndex); phosphor::logging::log<phosphor::logging::level::ERR>( "Unable to access share memory", phosphor::logging::entry("ERROR=%s", e.what())); return ipmi::responseUnspecifiedError(); } mdrv2->smbiosDir.dir[idIndex].common.size = 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::responseResponseError(); } } else { phosphor::logging::log<phosphor::logging::level::ERR>( "Canot lock smbios"); return ipmi::responseUnspecifiedError(); } static constexpr uint8_t xferStartAck = 1; return ipmi::responseSuccess(xferStartAck, session); } /** @brief This command is executed to close the session. @param - agentId, lockHandle. @return completion code on success. **/ ipmi::RspType<> cmd_mdr2_data_done(uint16_t agentId, uint16_t lockHandle) { if (mdrv2 == nullptr) { mdrv2 = std::make_unique<MDRV2>(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log<phosphor::logging::level::ERR>( "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); return ipmi::responseParmOutOfRange(); } int idIndex = mdrv2->findLockHandle(lockHandle); if ((idIndex < 0) || (idIndex >= maxDirEntries)) { phosphor::logging::log<phosphor::logging::level::ERR>( "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); return ipmi::responseParmOutOfRange(); } if (!mdrv2->smbiosUnlock(idIndex)) { phosphor::logging::log<phosphor::logging::level::ERR>( "Send data done failed - cannot unlock idIndex"); return ipmi::responseDestinationUnavailable(); } 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::responseDestinationUnavailable(); } bool status = false; std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); sdbusplus::message_t method = bus->new_method_call( service.c_str(), mdrv2Path, mdrv2Interface, "AgentSynchronizeData"); try { sdbusplus::message_t reply = bus->call(method); reply.read(status); } catch (const sdbusplus::exception_t& e) { phosphor::logging::log<phosphor::logging::level::ERR>( "Error Sync data with service", phosphor::logging::entry("ERROR=%s", e.what()), phosphor::logging::entry("SERVICE=%s", service.c_str()), phosphor::logging::entry("PATH=%s", mdrv2Path)); return ipmi::responseResponseError(); } if (!status) { phosphor::logging::log<phosphor::logging::level::ERR>( "Sync data with service failure"); return ipmi::responseUnspecifiedError(); } return ipmi::responseSuccess(); } static void register_netfn_smbiosmdrv2_functions(void) { // MDR V2 Command // <Get MDRII Status Command> ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIIAgentStatus, ipmi::Privilege::Operator, mdr2AgentStatus); // <Get MDRII Directory Command> ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIIGetDir, ipmi::Privilege::Operator, mdr2GetDir); // <Send MDRII Directory Command> ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIISendDir, ipmi::Privilege::Operator, mdr2SendDir); // <Get MDRII Data Info Command> ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIIGetDataInfo, ipmi::Privilege::Operator, mdr2GetDataInfo); // <Send MDRII Info Offer> ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIISendDataInfoOffer, ipmi::Privilege::Operator, mdr2DataInfoOffer); // <Send MDRII Data Info> ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIISendDataInfo, ipmi::Privilege::Operator, mdr2SendDataInfo); // <Get MDRII Data Block Command> ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIIGetDataBlock, ipmi::Privilege::Operator, mdr2GetDataBlock); // <Send MDRII Data Block> ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIISendDataBlock, ipmi::Privilege::Operator, mdr2SendDataBlock); // <Lock MDRII Data Command> ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIILockData, ipmi::Privilege::Operator, mdr2LockData); // <Unlock MDRII Data Command> ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIIUnlockData, ipmi::Privilege::Operator, mdr2UnlockData); // <Send MDRII Data Start> ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIIDataStart, ipmi::Privilege::Operator, cmd_mdr2_data_start); // <Send MDRII Data Done> ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIIDataDone, ipmi::Privilege::Operator, cmd_mdr2_data_done); }