xref: /openbmc/openpower-sbe-interface/sbe_chipOp_handler.cpp (revision da79c9ca20745373e25b442ea4c722e6eda996e3)
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