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