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