/* // 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 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, sdbusplus::message::variant &value, const std::string &service) { std::shared_ptr bus = getSdBus(); 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 &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::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 &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::message 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::message reply = bus->call(method); reply.read(idIndex); } catch (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(); } std::variant value = 0; if (0 != mdrv2->sdplusMdrv2GetProperty("DirectoryEntries", value, service)) { phosphor::logging::log( "Error getting DirEnries"); return ipmi::responseUnspecifiedError(); } if (dirIndex > std::get(value)) { return ipmi::responseParmOutOfRange(); } sdbusplus::message::message method = bus->new_method_call( service.c_str(), mdrv2Path, mdrv2Interface, "GetDirectoryInformation"); method.append(dirIndex); std::vector dataOut; try { sdbusplus::message::message reply = bus->call(method); reply.read(dataOut); } catch (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); } ipmi::RspType mdr2SendDir(uint16_t agentId, uint8_t dirVersion, uint8_t dirIndex, uint8_t returnedEntries, uint8_t remainingEntries, std::array dataInfo, uint32_t size, uint32_t dataSetSize, uint32_t dataVersion, uint32_t timestamp) { 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::message method = bus->new_method_call( service.c_str(), mdrv2Path, mdrv2Interface, "SendDirectoryInformation"); method.append(dirVersion, dirIndex, returnedEntries, remainingEntries, dataInfo, size, dataSetSize, dataVersion, timestamp); bool terminate = false; try { sdbusplus::message::message reply = bus->call(method); reply.read(terminate); } catch (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); } 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(request); auto dataOut = reinterpret_cast(response); std::vector res; if (*data_len < sizeof(MDRiiGetDataInfoRequest)) { *data_len = 0; return IPMI_CC_REQ_DATA_LEN_INVALID; } *data_len = 0; std::shared_ptr bus = getSdBus(); std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); if (mdrv2 == nullptr) { mdrv2 = std::make_unique(); } int agentIndex = mdrv2->agentLookup(requestData->agentId); if (agentIndex == -1) { phosphor::logging::log( "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( "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 &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_CC_RESPONSE_ERROR; } if (res.size() != sizeof(MDRiiGetDataInfoResponse)) { phosphor::logging::log( "Get data info response length not invalid"); return IPMI_CC_UNSPECIFIED_ERROR; } *data_len = static_cast(res.size()); std::copy(&res[0], &res[*data_len], dataOut); return IPMI_CC_OK; } /** @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::message method = bus->new_method_call( service.c_str(), mdrv2Path, mdrv2Interface, "GetDataOffer"); std::vector dataOut; try { sdbusplus::message::message reply = bus->call(method); reply.read(dataOut); } catch (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); } 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(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( "Requested data length is out of SMBIOS Table storage size."); return IPMI_CC_PARM_OUT_OF_RANGE; } std::shared_ptr bus = getSdBus(); std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); if (mdrv2 == nullptr) { mdrv2 = std::make_unique(); } int agentIndex = mdrv2->agentLookup(requestData->agentId); if (agentIndex == -1) { phosphor::logging::log( "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( "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 &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_CC_RESPONSE_ERROR; } *data_len = 1; if (entryChanged) { *(static_cast(response)) = 1; } else { *(static_cast(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(request); auto responseData = reinterpret_cast(response); std::tuple> res; std::vector resData; uint8_t status = 1; if (*data_len != sizeof(MDRiiGetDataBlockRequest)) { *data_len = 0; return IPMI_CC_REQ_DATA_LEN_INVALID; } *data_len = 0; if (mdrv2 == nullptr) { mdrv2 = std::make_unique(); } int agentIndex = mdrv2->agentLookup(requestData->agentId); if (agentIndex == -1) { phosphor::logging::log( "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( "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( "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( "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( "Get data block unexpected error."); return IPMI_CC_UNSPECIFIED_ERROR; } if ((requestData->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_CC_PARM_OUT_OF_RANGE; } uint32_t u32Checksum = mdrv2->calcChecksum32( mdrv2->smbiosDir.dir[idIndex].dataStorage + requestData->xferOffset, outSize); if (u32Checksum == invalidChecksum) { phosphor::logging::log( "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( "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; } /** @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 (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; 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 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(), sizeof(dataInfo), 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(), sizeof(dataInfo), 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::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 &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, NETFUN_INTEL_APP_OEM, IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_AGENT_STATUS, ipmi::Privilege::Operator, mdr2AgentStatus); // ipmi::registerHandler(ipmi::prioOemBase, NETFUN_INTEL_APP_OEM, IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_GET_DIR, ipmi::Privilege::Operator, mdr2GetDir); // ipmi::registerHandler(ipmi::prioOemBase, NETFUN_INTEL_APP_OEM, IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_SEND_DIR, ipmi::Privilege::Operator, mdr2SendDir); // 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); // ipmi::registerHandler( ipmi::prioOemBase, NETFUN_INTEL_APP_OEM, IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_SEND_DATA_INFO_OFFER, ipmi::Privilege::Operator, mdr2DataInfoOffer); // 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); // 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); // ipmi::registerHandler(ipmi::prioOemBase, NETFUN_INTEL_APP_OEM, IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_SEND_DATA_BLOCK, ipmi::Privilege::Operator, mdr2SendDataBlock); // ipmi::registerHandler(ipmi::prioOemBase, NETFUN_INTEL_APP_OEM, IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_LOCK_DATA, ipmi::Privilege::Operator, mdr2LockData); // ipmi::registerHandler(ipmi::prioOemBase, NETFUN_INTEL_APP_OEM, IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_UNLOCK_DATA, ipmi::Privilege::Operator, mdr2UnlockData); // ipmi::registerHandler(ipmi::prioOemBase, NETFUN_INTEL_APP_OEM, IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_DATA_START, ipmi::Privilege::Operator, cmd_mdr2_data_start); // ipmi::registerHandler(ipmi::prioOemBase, NETFUN_INTEL_APP_OEM, IPMI_NETFN_INTEL_OEM_APP_CMD::MDRII_DATA_DONE, ipmi::Privilege::Operator, cmd_mdr2_data_done); }