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