/* // 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include std::unique_ptr 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 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( "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 commonData; std::shared_ptr 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( "Error sync dir common data with service", phosphor::logging::entry("ERROR=%s", e.what())); return -1; } if (commonData.size() < syncDirCommonSize) { phosphor::logging::log( "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( "Error dataInfo, input is null point"); return -1; } std::shared_ptr bus = getSdBus(); sdbusplus::message_t method = bus->new_method_call( service.c_str(), mdrv2Path, mdrv2Interface, "FindIdIndex"); std::vector 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( "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( "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 mdr2AgentStatus(uint16_t agentId, uint8_t dirVersion) { if (mdrv2 == nullptr) { mdrv2 = std::make_unique(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log( "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(DirDataRequestEnum::dirDataRequested); } else { dataRequest = static_cast(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> mdr2GetDir(uint16_t agentId, uint8_t dirIndex) { std::shared_ptr bus = getSdBus(); std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); if (mdrv2 == nullptr) { mdrv2 = std::make_unique(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log( "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); return ipmi::responseParmOutOfRange(); } ipmi::DbusVariant value = static_cast(0); if (0 != mdrv2->sdplusMdrv2GetProperty("DirectoryEntries", value, service)) { phosphor::logging::log( "Error getting DirEnries"); return ipmi::responseUnspecifiedError(); } if (std::get(value) == 0) { phosphor::logging::log( "Error getting directory entries", phosphor::logging::entry("VALUE=%x", std::get(value))); return ipmi::responseUnspecifiedError(); } if (dirIndex > std::get(value)) { return ipmi::responseParmOutOfRange(); } sdbusplus::message_t method = bus->new_method_call( service.c_str(), mdrv2Path, mdrv2Interface, "GetDirectoryInformation"); method.append(dirIndex); std::vector dataOut; try { sdbusplus::message_t reply = bus->call(method); reply.read(dataOut); } catch (const sdbusplus::exception_t& e) { phosphor::logging::log( "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( "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( "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 mdr2SendDir(uint16_t agentId, uint8_t dirVersion, uint8_t dirIndex, uint8_t returnedEntries, uint8_t remainingEntries, std::vector dataInfo) { if ((static_cast(returnedEntries) * dataInfoSize) != dataInfo.size()) { return ipmi::responseReqDataLenInvalid(); } std::shared_ptr bus = getSdBus(); std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); if (mdrv2 == nullptr) { mdrv2 = std::make_unique(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log( "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); return ipmi::responseParmOutOfRange(); } if ((dirIndex + returnedEntries) > maxDirEntries) { phosphor::logging::log( "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( "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> mdr2GetDataInfo(uint16_t agentId, std::vector dataInfo) { constexpr size_t getDataInfoReqSize = 16; if (dataInfo.size() < getDataInfoReqSize) { return ipmi::responseReqDataLenInvalid(); } std::shared_ptr bus = getSdBus(); std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); if (mdrv2 == nullptr) { mdrv2 = std::make_unique(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log( "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( "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(idIndex)); std::vector res; try { sdbusplus::message_t reply = bus->call(method); reply.read(res); } catch (const sdbusplus::exception_t& e) { phosphor::logging::log( "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( "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> mdr2DataInfoOffer(uint16_t agentId) { std::shared_ptr bus = getSdBus(); std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); if (mdrv2 == nullptr) { mdrv2 = std::make_unique(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log( "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 dataOut; try { sdbusplus::message_t reply = bus->call(method); reply.read(dataOut); } catch (const sdbusplus::exception_t& e) { phosphor::logging::log( "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( "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 mdr2SendDataInfo(uint16_t agentId, std::array dataInfo, uint8_t validFlag, uint32_t dataLength, uint32_t dataVersion, uint32_t timeStamp) { if (dataLength > smbiosTableStorageSize) { phosphor::logging::log( "Requested data length is out of SMBIOS Table storage size."); return ipmi::responseParmOutOfRange(); } std::shared_ptr bus = getSdBus(); std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); if (mdrv2 == nullptr) { mdrv2 = std::make_unique(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log( "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( "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( "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 // data > mdr2GetDataBlock(uint16_t agentId, uint16_t lockHandle, uint32_t xferOffset, uint32_t xferLength) { if (mdrv2 == nullptr) { mdrv2 = std::make_unique(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log( "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( "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); return ipmi::responseParmOutOfRange(); } if (xferOffset >= mdrv2->smbiosDir.dir[idIndex].common.size) { phosphor::logging::log( "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( "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( "Get data block unexpected error."); return ipmi::responseUnspecifiedError(); } if ((xferOffset + outSize) > UINT_MAX - reinterpret_cast(mdrv2->smbiosDir.dir[idIndex].dataStorage)) { phosphor::logging::log( "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( "Get data block failed - invalid checksum"); return ipmi::response(ccOemInvalidChecksum); } std::vector 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(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log( "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( "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); return ipmi::responseParmOutOfRange(); } if (mdrv2->smbiosIsUpdating(idIndex)) { if (xferOffset > UINT_MAX - xferLength) { phosphor::logging::log( "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( "Send data block Invalid offset/length"); return ipmi::responseReqDataLenExceeded(); } if (reinterpret_cast( mdrv2->smbiosDir.dir[idIndex].dataStorage) > UINT_MAX - xferOffset) { phosphor::logging::log( "Offset is out of range"); return ipmi::responseParmOutOfRange(); } uint8_t* destAddr = mdrv2->smbiosDir.dir[idIndex].dataStorage + xferOffset; uint8_t* sourceAddr = reinterpret_cast(mdrv2->area->vPtr); uint32_t calcChecksum = mdrv2->calcChecksum32(sourceAddr, xferLength); if (calcChecksum != checksum) { phosphor::logging::log( "Send data block Invalid checksum"); return ipmi::response(ccOemInvalidChecksum); } else { if (reinterpret_cast(sourceAddr) > UINT_MAX - xferLength) { phosphor::logging::log( "Length is out of range"); return ipmi::responseParmOutOfRange(); } std::copy(sourceAddr, sourceAddr + xferLength, destAddr); } } else { phosphor::logging::log( "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( "Write data from flash error - Open MDRV2 table file failure"); return false; } try { smbiosFile.write(reinterpret_cast(mdrHdr), sizeof(MDRSMBIOSHeader)); smbiosFile.write(reinterpret_cast(data), mdrHdr->dataSize); } catch (const std::ofstream::failure& e) { phosphor::logging::log( "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( "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( "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; 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 mdr2LockData(uint16_t agentId, std::array dataInfo, uint16_t timeout) { if (mdrv2 == nullptr) { mdrv2 = std::make_unique(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log( "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); return ipmi::responseParmOutOfRange(); } std::shared_ptr 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( "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( "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("unlock data"); if (mdrv2 == nullptr) { mdrv2 = std::make_unique(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log( "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( "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); return ipmi::responseParmOutOfRange(); } if (!mdrv2->smbiosUnlock(idIndex)) { phosphor::logging::log( "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 cmd_mdr2_data_start( uint16_t agentId, std::array dataInfo, uint32_t dataLength, uint32_t xferAddress, uint32_t xferLength, uint16_t timeout) { uint16_t session = 0; if (dataLength > smbiosTableStorageSize) { phosphor::logging::log( "Requested data length is out of SMBIOS Table storage size."); return ipmi::responseParmOutOfRange(); } if ((xferLength + xferAddress) > mdriiSMSize) { phosphor::logging::log( "Invalid data address and size"); return ipmi::responseParmOutOfRange(); } std::shared_ptr bus = getSdBus(); std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); if (mdrv2 == nullptr) { mdrv2 = std::make_unique(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log( "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( "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(xferAddress, xferLength); } catch (const std::system_error& e) { mdrv2->smbiosUnlock(idIndex); phosphor::logging::log( "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( "Unable to sync data to service"); return ipmi::responseResponseError(); } } else { phosphor::logging::log( "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(); } int agentIndex = mdrv2->agentLookup(agentId); if (agentIndex == -1) { phosphor::logging::log( "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( "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); return ipmi::responseParmOutOfRange(); } if (!mdrv2->smbiosUnlock(idIndex)) { phosphor::logging::log( "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( "create folder failed for writting smbios file"); } } if (!mdrv2->storeDatatoFlash( &mdr2Smbios, mdrv2->smbiosDir.dir[smbiosDirIndex].dataStorage)) { phosphor::logging::log( "MDR2 Store data to flash failed"); return ipmi::responseDestinationUnavailable(); } bool status = false; std::shared_ptr 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( "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( "Sync data with service failure"); return ipmi::responseUnspecifiedError(); } return ipmi::responseSuccess(); } static void register_netfn_smbiosmdrv2_functions(void) { // MDR V2 Command // ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIIAgentStatus, ipmi::Privilege::Operator, mdr2AgentStatus); // ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIIGetDir, ipmi::Privilege::Operator, mdr2GetDir); // ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIISendDir, ipmi::Privilege::Operator, mdr2SendDir); // ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIIGetDataInfo, ipmi::Privilege::Operator, mdr2GetDataInfo); // ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIISendDataInfoOffer, ipmi::Privilege::Operator, mdr2DataInfoOffer); // ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIISendDataInfo, ipmi::Privilege::Operator, mdr2SendDataInfo); // ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIIGetDataBlock, ipmi::Privilege::Operator, mdr2GetDataBlock); // ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIISendDataBlock, ipmi::Privilege::Operator, mdr2SendDataBlock); // ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIILockData, ipmi::Privilege::Operator, mdr2LockData); // ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIIUnlockData, ipmi::Privilege::Operator, mdr2UnlockData); // ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIIDataStart, ipmi::Privilege::Operator, cmd_mdr2_data_start); // ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, ipmi::intel::app::cmdMdrIIDataDone, ipmi::Privilege::Operator, cmd_mdr2_data_done); }