#include #include #include #include #include #include #include #include namespace openpower { namespace sbe { namespace internal { constexpr uint16_t MAGIC_CODE = 0xC0DE; constexpr auto SBE_OPERATION_SUCCESSFUL = 0; constexpr auto LENGTH_OF_DISTANCE_HEADER_IN_WORDS = 0x1; constexpr auto LENGTH_OF_RESP_HEADER_IN_WORDS = 0x2; constexpr auto DISTANCE_TO_RESP_CODE = 0x1; constexpr auto MAX_FFDC_LEN_IN_WORDS = 5120; constexpr auto WORD_SIZE = 4; constexpr auto MAGIC_CODE_BITS = 16; std::vector writeToFifo(const char* devPath, const sbe_word_t* cmdBuffer, size_t cmdBufLen, size_t respBufLen) { ssize_t len = 0; std::vector response; std::ostringstream errMsg; // Open the device and obtain the file descriptor associated with it. FileDescriptor fileFd(devPath, (O_RDWR | O_NONBLOCK)); // Wait for FIFO device and perform write operation struct pollfd poll_fd = {}; poll_fd.fd = fileFd(); poll_fd.events = POLLOUT | POLLERR; int rc = 0; if ((rc = poll(&poll_fd, 1, -1)) < 0) { // TODO:use elog infrastructure errMsg << "Waiting for FIFO device:" << devPath << "to write failed" << "rc=" << rc << "errno=" << errno; throw std::runtime_error(errMsg.str().c_str()); } if (poll_fd.revents & POLLERR) { // TODO:use elog infrastructure errMsg << "POLLERR while waiting for writeable FIFO,errno:" << errno; throw std::runtime_error(errMsg.str().c_str()); } auto bytesToWrite = (cmdBufLen * WORD_SIZE); // Perform the write operation len = write(fileFd(), cmdBuffer, bytesToWrite); if (len < 0) { // TODO:use elog infrastructure errMsg << "Failed to write to FIFO device:" << devPath << " Length " "returned= " << len << " errno=" << errno; throw std::runtime_error(errMsg.str().c_str()); } // Wait for FIFO device and perform read operation poll_fd.fd = fileFd(); poll_fd.events = POLLIN | POLLERR; if ((rc = poll(&poll_fd, 1, -1) < 0)) { // TODO:use elog infrastructure errMsg << "Waiting for FIFO device:" << devPath << "to read failed" << " rc=" << rc << " and errno=" << errno; throw std::runtime_error(errMsg.str().c_str()); } if (poll_fd.revents & POLLERR) { // TODO:use elog infrastructure errMsg << "POLLERR while waiting for readable FIFO,errno:" << errno; throw std::runtime_error(errMsg.str().c_str()); } // Derive the total read length which should include the FFDC, which SBE // returns in case of failure. size_t totalReadLen = respBufLen + MAX_FFDC_LEN_IN_WORDS; // Create a temporary buffer std::vector buffer(totalReadLen); ssize_t bytesToRead = (totalReadLen * WORD_SIZE); len = read(fileFd(), buffer.data(), bytesToRead); if (len < 0) { // TODO:use elog infrastructure errMsg << "Failed to read the FIFO device:" << devPath << "bytes read =" << len << " errno=" << errno; throw std::runtime_error(errMsg.str().c_str()); } // Extract the valid number of words read. for (auto i = 0; i < (len / WORD_SIZE); ++i) { response.push_back(be32toh(buffer[i])); } // Closing of the file descriptor will be handled when the FileDescriptor // object will go out of scope. return response; } void parseResponse(std::vector& sbeDataBuf) { // Number of 32-bit words obtained from the SBE size_t lengthObtained = sbeDataBuf.size(); // Fetch the SBE header and SBE chiop primary and secondary status // Last value in the buffer will have the offset for the SBE header size_t distanceToStatusHeader = sbeDataBuf[sbeDataBuf.size() - 1]; if (lengthObtained < distanceToStatusHeader) { // TODO:use elog infrastructure std::ostringstream errMsg; errMsg << "Distance to SBE status header value " << distanceToStatusHeader << " is greater then total length of " "response buffer " << lengthObtained; throw std::runtime_error(errMsg.str().c_str()); } // Fetch the response header contents auto iter = sbeDataBuf.begin(); std::advance(iter, (lengthObtained - distanceToStatusHeader)); // First header word will have 2 bytes of MAGIC CODE followed by // Command class and command type //| MAGIC BYTES:0xCODE | COMMAND-CLASS | COMMAND-TYPE| sbe_word_t l_magicCode = (*iter >> MAGIC_CODE_BITS); // Fetch the primary and secondary response code std::advance(iter, DISTANCE_TO_RESP_CODE); auto l_priSecResp = *iter; // Validate the magic code obtained in the response if (l_magicCode != MAGIC_CODE) { // TODO:use elog infrastructure std::ostringstream errMsg; errMsg << "Invalid MAGIC keyword in the response header (" << l_magicCode << "),expected keyword " << MAGIC_CODE; throw std::runtime_error(errMsg.str().c_str()); } // Validate the Primary and Secondary response value if (l_priSecResp != SBE_OPERATION_SUCCESSFUL) { // Extract the SBE FFDC and throw it to the caller size_t ffdcLen = (distanceToStatusHeader - LENGTH_OF_RESP_HEADER_IN_WORDS - LENGTH_OF_DISTANCE_HEADER_IN_WORDS); if (ffdcLen) { std::vector ffdcData(ffdcLen); // Fetch the offset of FFDC data auto ffdcOffset = (lengthObtained - distanceToStatusHeader) + LENGTH_OF_RESP_HEADER_IN_WORDS; std::copy_n((sbeDataBuf.begin() + ffdcOffset), ffdcLen, ffdcData.begin()); } // TODO:use elog infrastructure to return the SBE and Hardware procedure // FFDC container back to the caller. std::ostringstream errMsg; errMsg << "Chip operation failed with SBE response code:" << l_priSecResp << ".Length of FFDC data of obtained:" << ffdcLen; throw std::runtime_error(errMsg.str().c_str()); } // In case of success, remove the response header content and send only the // data.Response header will be towards the end of the buffer. auto respLen = (lengthObtained - distanceToStatusHeader); iter = sbeDataBuf.begin(); std::advance(iter, respLen); sbeDataBuf.erase(iter, sbeDataBuf.end()); } } // namespace internal } // namespace sbe } // namespace openpower