xref: /openbmc/openpower-sbe-interface/sbe_chipOp_handler.cpp (revision 0dc2932391d105178de8a49bbc6ae75b4a863f7d)
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     else if (len != bytesToRead)
97     {
98         //TODO:use elog infrastructure
99         errMsg << "Exepcted " << bytesToRead << " bytes to be read from FIFO"
100                " device:" << devPath << ", but bytes read are " << len <<
101                " errno= " << errno;
102         throw std::runtime_error(errMsg.str().c_str());
103     }
104 
105     //Extract the valid number of words read.
106     for (auto i = 0; i < (rc / WORD_SIZE); ++i)
107     {
108         response.push_back(be32toh(buffer[i]));
109     }
110 
111     //Closing of the file descriptor will be handled when the FileDescriptor
112     //object will go out of scope.
113     return response;
114 }
115 
116 void parseResponse(std::vector<sbe_word_t>& sbeDataBuf)
117 {
118     //Number of 32-bit words obtained from the SBE
119     size_t lengthObtained = sbeDataBuf.size();
120 
121     //Fetch the SBE header and SBE chiop primary and secondary status
122     //Last value in the buffer will have the offset for the SBE header
123     size_t distanceToStatusHeader =  sbeDataBuf[sbeDataBuf.size() - 1];
124 
125     if (lengthObtained < distanceToStatusHeader)
126     {
127         //TODO:use elog infrastructure
128         std::ostringstream errMsg;
129         errMsg << "Distance to SBE status header value " <<
130                distanceToStatusHeader << " is greater then total length of "
131                "response buffer " << lengthObtained;
132         throw std::runtime_error(errMsg.str().c_str());
133     }
134 
135     //Fetch the response header contents
136     auto iter = sbeDataBuf.begin();
137     std::advance(iter, (lengthObtained - distanceToStatusHeader));
138 
139     //First header word will have 2 bytes of MAGIC CODE followed by
140     //Command class and command type
141     //|  MAGIC BYTES:0xCODE | COMMAND-CLASS | COMMAND-TYPE|
142     sbe_word_t l_magicCode = (*iter >> MAGIC_CODE_BITS);
143 
144     //Fetch the primary and secondary response code
145     std::advance(iter, DISTANCE_TO_RESP_CODE);
146     auto l_priSecResp = *iter;
147 
148     //Validate the magic code obtained in the response
149     if (l_magicCode != MAGIC_CODE)
150     {
151         //TODO:use elog infrastructure
152         std::ostringstream errMsg;
153         errMsg << "Invalid MAGIC keyword in the response header (" <<
154                l_magicCode << "),expected keyword " << MAGIC_CODE;
155         throw std::runtime_error(errMsg.str().c_str());
156     }
157 
158     //Validate the Primary and Secondary response value
159     if (l_priSecResp != SBE_OPERATION_SUCCESSFUL)
160     {
161         //Extract the SBE FFDC and throw it to the caller
162         size_t ffdcLen = (distanceToStatusHeader -
163                           LENGTH_OF_RESP_HEADER_IN_WORDS -
164                           LENGTH_OF_DISTANCE_HEADER_IN_WORDS);
165         if (ffdcLen)
166         {
167             std::vector<sbe_word_t> ffdcData(ffdcLen);
168             //Fetch the offset of FFDC data
169             auto ffdcOffset = (lengthObtained - distanceToStatusHeader) +
170                               LENGTH_OF_RESP_HEADER_IN_WORDS;
171             std::copy_n((sbeDataBuf.begin() + ffdcOffset), ffdcLen,
172                         ffdcData.begin());
173         }
174 
175         //TODO:use elog infrastructure to return the SBE and Hardware procedure
176         //FFDC container back to the caller.
177         std::ostringstream errMsg;
178         errMsg << "Chip operation failed with SBE response code:" << l_priSecResp
179                << ".Length of FFDC data of obtained:" << ffdcLen;
180         throw std::runtime_error(errMsg.str().c_str());
181     }
182 
183     //In case of success, remove the response header content and send only the
184     //data.Response header will be towards the end of the buffer.
185     auto respLen = (lengthObtained - distanceToStatusHeader);
186     iter = sbeDataBuf.begin();
187     std::advance(iter,respLen);
188     sbeDataBuf.erase(iter, sbeDataBuf.end());
189 }
190 
191 }
192 }
193 }
194