1 #include <endian.h> 2 #include <errno.h> 3 #include <fcntl.h> 4 #include <poll.h> 5 #include <unistd.h> 6 7 #include <array> 8 #include <file.hpp> 9 #include <sbe_chipOp_handler.hpp> 10 namespace openpower 11 { 12 namespace sbe 13 { 14 namespace internal 15 { 16 17 constexpr uint16_t MAGIC_CODE = 0xC0DE; 18 constexpr auto SBE_OPERATION_SUCCESSFUL = 0; 19 constexpr auto LENGTH_OF_DISTANCE_HEADER_IN_WORDS = 0x1; 20 constexpr auto LENGTH_OF_RESP_HEADER_IN_WORDS = 0x2; 21 constexpr auto DISTANCE_TO_RESP_CODE = 0x1; 22 constexpr auto MAX_FFDC_LEN_IN_WORDS = 5120; 23 constexpr auto WORD_SIZE = 4; 24 constexpr auto MAGIC_CODE_BITS = 16; writeToFifo(const char * devPath,const sbe_word_t * cmdBuffer,size_t cmdBufLen,size_t respBufLen)25 std::vector<sbe_word_t> writeToFifo(const char* devPath, 26 const sbe_word_t* cmdBuffer, 27 size_t cmdBufLen, size_t respBufLen) 28 { 29 ssize_t len = 0; 30 std::vector<sbe_word_t> response; 31 std::ostringstream errMsg; 32 33 // Open the device and obtain the file descriptor associated with it. 34 FileDescriptor fileFd(devPath, (O_RDWR | O_NONBLOCK)); 35 36 // Wait for FIFO device and perform write operation 37 struct pollfd poll_fd = {}; 38 poll_fd.fd = fileFd(); 39 poll_fd.events = POLLOUT | POLLERR; 40 41 int rc = 0; 42 if ((rc = poll(&poll_fd, 1, -1)) < 0) 43 { 44 // TODO:use elog infrastructure 45 errMsg << "Waiting for FIFO device:" << devPath << "to write failed" 46 << "rc=" << rc << "errno=" << errno; 47 throw std::runtime_error(errMsg.str().c_str()); 48 } 49 if (poll_fd.revents & POLLERR) 50 { 51 // TODO:use elog infrastructure 52 errMsg << "POLLERR while waiting for writeable FIFO,errno:" << errno; 53 throw std::runtime_error(errMsg.str().c_str()); 54 } 55 auto bytesToWrite = (cmdBufLen * WORD_SIZE); 56 // Perform the write operation 57 len = write(fileFd(), cmdBuffer, bytesToWrite); 58 if (len < 0) 59 { 60 // TODO:use elog infrastructure 61 errMsg << "Failed to write to FIFO device:" << devPath 62 << " Length " 63 "returned= " 64 << len << " errno=" << errno; 65 throw std::runtime_error(errMsg.str().c_str()); 66 } 67 // Wait for FIFO device and perform read operation 68 poll_fd.fd = fileFd(); 69 poll_fd.events = POLLIN | POLLERR; 70 if ((rc = poll(&poll_fd, 1, -1) < 0)) 71 { 72 // TODO:use elog infrastructure 73 errMsg << "Waiting for FIFO device:" << devPath << "to read failed" 74 << " rc=" << rc << " and errno=" << errno; 75 throw std::runtime_error(errMsg.str().c_str()); 76 } 77 if (poll_fd.revents & POLLERR) 78 { 79 // TODO:use elog infrastructure 80 errMsg << "POLLERR while waiting for readable FIFO,errno:" << errno; 81 throw std::runtime_error(errMsg.str().c_str()); 82 } 83 // Derive the total read length which should include the FFDC, which SBE 84 // returns in case of failure. 85 size_t totalReadLen = respBufLen + MAX_FFDC_LEN_IN_WORDS; 86 // Create a temporary buffer 87 std::vector<sbe_word_t> buffer(totalReadLen); 88 89 ssize_t bytesToRead = (totalReadLen * WORD_SIZE); 90 len = read(fileFd(), buffer.data(), bytesToRead); 91 if (len < 0) 92 { 93 // TODO:use elog infrastructure 94 errMsg << "Failed to read the FIFO device:" << devPath 95 << "bytes read =" << len << " errno=" << errno; 96 throw std::runtime_error(errMsg.str().c_str()); 97 } 98 99 // Extract the valid number of words read. 100 for (auto i = 0; i < (len / WORD_SIZE); ++i) 101 { 102 response.push_back(be32toh(buffer[i])); 103 } 104 105 // Closing of the file descriptor will be handled when the FileDescriptor 106 // object will go out of scope. 107 return response; 108 } 109 parseResponse(std::vector<sbe_word_t> & sbeDataBuf)110 void parseResponse(std::vector<sbe_word_t>& sbeDataBuf) 111 { 112 // Number of 32-bit words obtained from the SBE 113 size_t lengthObtained = sbeDataBuf.size(); 114 115 // Fetch the SBE header and SBE chiop primary and secondary status 116 // Last value in the buffer will have the offset for the SBE header 117 size_t distanceToStatusHeader = sbeDataBuf[sbeDataBuf.size() - 1]; 118 119 if (lengthObtained < distanceToStatusHeader) 120 { 121 // TODO:use elog infrastructure 122 std::ostringstream errMsg; 123 errMsg << "Distance to SBE status header value " 124 << distanceToStatusHeader 125 << " is greater then total length of " 126 "response buffer " 127 << lengthObtained; 128 throw std::runtime_error(errMsg.str().c_str()); 129 } 130 131 // Fetch the response header contents 132 auto iter = sbeDataBuf.begin(); 133 std::advance(iter, (lengthObtained - distanceToStatusHeader)); 134 135 // First header word will have 2 bytes of MAGIC CODE followed by 136 // Command class and command type 137 //| MAGIC BYTES:0xCODE | COMMAND-CLASS | COMMAND-TYPE| 138 sbe_word_t l_magicCode = (*iter >> MAGIC_CODE_BITS); 139 140 // Fetch the primary and secondary response code 141 std::advance(iter, DISTANCE_TO_RESP_CODE); 142 auto l_priSecResp = *iter; 143 144 // Validate the magic code obtained in the response 145 if (l_magicCode != MAGIC_CODE) 146 { 147 // TODO:use elog infrastructure 148 std::ostringstream errMsg; 149 errMsg << "Invalid MAGIC keyword in the response header (" 150 << l_magicCode << "),expected keyword " << MAGIC_CODE; 151 throw std::runtime_error(errMsg.str().c_str()); 152 } 153 154 // Validate the Primary and Secondary response value 155 if (l_priSecResp != SBE_OPERATION_SUCCESSFUL) 156 { 157 // Extract the SBE FFDC and throw it to the caller 158 size_t ffdcLen = 159 (distanceToStatusHeader - LENGTH_OF_RESP_HEADER_IN_WORDS - 160 LENGTH_OF_DISTANCE_HEADER_IN_WORDS); 161 if (ffdcLen) 162 { 163 std::vector<sbe_word_t> ffdcData(ffdcLen); 164 // Fetch the offset of FFDC data 165 auto ffdcOffset = (lengthObtained - distanceToStatusHeader) + 166 LENGTH_OF_RESP_HEADER_IN_WORDS; 167 std::copy_n((sbeDataBuf.begin() + ffdcOffset), ffdcLen, 168 ffdcData.begin()); 169 } 170 171 // TODO:use elog infrastructure to return the SBE and Hardware procedure 172 // FFDC container back to the caller. 173 std::ostringstream errMsg; 174 errMsg << "Chip operation failed with SBE response code:" 175 << l_priSecResp 176 << ".Length of FFDC data of obtained:" << ffdcLen; 177 throw std::runtime_error(errMsg.str().c_str()); 178 } 179 180 // In case of success, remove the response header content and send only the 181 // data.Response header will be towards the end of the buffer. 182 auto respLen = (lengthObtained - distanceToStatusHeader); 183 iter = sbeDataBuf.begin(); 184 std::advance(iter, respLen); 185 sbeDataBuf.erase(iter, sbeDataBuf.end()); 186 } 187 188 } // namespace internal 189 } // namespace sbe 190 } // namespace openpower 191