xref: /openbmc/openpower-occ-control/occ_command.cpp (revision 37abe9be91df2bb173c9642a2740a425904d7921)
1a8857c50SChris Cain #include "config.h"
2a8857c50SChris Cain 
3a8857c50SChris Cain #include "occ_command.hpp"
4a8857c50SChris Cain 
5a8857c50SChris Cain #include <errno.h>
6a8857c50SChris Cain #include <fcntl.h>
7a8857c50SChris Cain #include <unistd.h>
8a8857c50SChris Cain 
9a8857c50SChris Cain #include <org/open_power/OCC/Device/error.hpp>
10*37abe9beSChris Cain #include <phosphor-logging/lg2.hpp>
11b5ca1015SGeorge Liu 
12b5ca1015SGeorge Liu #include <algorithm>
1348002498SPatrick Williams #include <format>
14a8857c50SChris Cain #include <string>
15a8857c50SChris Cain 
16a8857c50SChris Cain // #define TRACE_PACKETS
17a8857c50SChris Cain 
18a8857c50SChris Cain namespace open_power
19a8857c50SChris Cain {
20a8857c50SChris Cain namespace occ
21a8857c50SChris Cain {
22a8857c50SChris Cain 
23a8857c50SChris Cain // Trace block of data in hex
dump_hex(const std::vector<std::uint8_t> & data,const unsigned int data_len)24a8857c50SChris Cain void dump_hex(const std::vector<std::uint8_t>& data,
25a8857c50SChris Cain               const unsigned int data_len)
26a8857c50SChris Cain {
27a8857c50SChris Cain     unsigned int dump_length = data.size();
28a8857c50SChris Cain     if ((data_len > 0) && (data_len < dump_length))
29a8857c50SChris Cain     {
30a8857c50SChris Cain         dump_length = data_len;
31a8857c50SChris Cain     }
32a8857c50SChris Cain     std::string s;
33a8857c50SChris Cain     for (uint32_t i = 0; i < dump_length; i++)
34a8857c50SChris Cain     {
35a8857c50SChris Cain         if (i % 16 == 0)
36a8857c50SChris Cain         {
3748002498SPatrick Williams             s += std::format("{:04X}: ", i);
38a8857c50SChris Cain         }
39a8857c50SChris Cain         else if (i % 4 == 0)
40a8857c50SChris Cain         {
41a8857c50SChris Cain             s += " ";
42a8857c50SChris Cain         }
43a8857c50SChris Cain 
4448002498SPatrick Williams         s += std::format("{:02X}", data.at(i));
45a8857c50SChris Cain 
46a8857c50SChris Cain         if ((i % 16 == 15) || (i == (dump_length - 1)))
47a8857c50SChris Cain         {
48*37abe9beSChris Cain             lg2::info("{STRING}", "STRING", s);
49a8857c50SChris Cain             s.clear();
50a8857c50SChris Cain         }
51a8857c50SChris Cain     }
52a8857c50SChris Cain }
53a8857c50SChris Cain 
OccCommand(uint8_t instance,const char * path)54f3b7514eSGeorge Liu OccCommand::OccCommand(uint8_t instance, const char* path) :
55f3b7514eSGeorge Liu     occInstance(instance), path(path),
56a8857c50SChris Cain     devicePath(OCC_DEV_PATH + std::to_string((this->path.back() - '0') + 1)),
57a8857c50SChris Cain     activeStatusSignal(
58f3b7514eSGeorge Liu         utils::getBus(),
59f3b7514eSGeorge Liu         sdbusRule::propertiesChanged(path, "org.open_power.OCC.Status"),
60a8857c50SChris Cain         std::bind(std::mem_fn(&OccCommand::activeStatusEvent), this,
61a8857c50SChris Cain                   std::placeholders::_1))
62a8857c50SChris Cain {
63*37abe9beSChris Cain     lg2::debug("OccCommand::OccCommand(path={PATH}, devicePath={DEVICE}",
64*37abe9beSChris Cain                "PATH", this->path, "DEVICE", devicePath);
65a8857c50SChris Cain }
66a8857c50SChris Cain 
openDevice()67a8857c50SChris Cain void OccCommand::openDevice()
68a8857c50SChris Cain {
69a8857c50SChris Cain     using namespace sdbusplus::org::open_power::OCC::Device::Error;
70a8857c50SChris Cain 
71*37abe9beSChris Cain     lg2::debug("OccCommand::openDevice: calling open {PATH}", "PATH",
72*37abe9beSChris Cain                devicePath);
73a8857c50SChris Cain     fd = open(devicePath.c_str(), O_RDWR | O_NONBLOCK);
74a8857c50SChris Cain     if (fd < 0)
75a8857c50SChris Cain     {
76c567dc8dSChris Cain         const int openErrno = errno;
77*37abe9beSChris Cain         lg2::error(
78*37abe9beSChris Cain             "OccCommand::openDevice: open failed (errno={ERR}, path={PATH})",
79*37abe9beSChris Cain             "ERR", openErrno, "PATH", devicePath);
80a8857c50SChris Cain     }
81a8857c50SChris Cain     else
82a8857c50SChris Cain     {
83*37abe9beSChris Cain         lg2::debug("OccCommand::openDevice: open success");
84a8857c50SChris Cain     }
85a8857c50SChris Cain 
86a8857c50SChris Cain     return;
87a8857c50SChris Cain }
88a8857c50SChris Cain 
closeDevice()89a8857c50SChris Cain void OccCommand::closeDevice()
90a8857c50SChris Cain {
91a8857c50SChris Cain     if (fd >= 0)
92a8857c50SChris Cain     {
93*37abe9beSChris Cain         lg2::debug("OccCommand::closeDevice: calling close()");
94a8857c50SChris Cain         close(fd);
95a8857c50SChris Cain         fd = -1;
96a8857c50SChris Cain     }
97a8857c50SChris Cain }
98a8857c50SChris Cain 
send(const std::vector<uint8_t> & command,std::vector<uint8_t> & response)99a8857c50SChris Cain CmdStatus OccCommand::send(const std::vector<uint8_t>& command,
100a8857c50SChris Cain                            std::vector<uint8_t>& response)
101a8857c50SChris Cain {
102a8857c50SChris Cain     using namespace sdbusplus::org::open_power::OCC::Device::Error;
103a8857c50SChris Cain     CmdStatus status = CmdStatus::FAILURE;
104a8857c50SChris Cain 
105a8857c50SChris Cain     response.clear();
106a8857c50SChris Cain 
107*37abe9beSChris Cain     lg2::debug("OccCommand::send: calling openDevice()");
108a8857c50SChris Cain     openDevice();
109a8857c50SChris Cain 
110a8857c50SChris Cain     if (fd < 0)
111a8857c50SChris Cain     {
112a8857c50SChris Cain         // OCC is inactive; empty response
113c567dc8dSChris Cain         return CmdStatus::COMM_FAILURE;
114a8857c50SChris Cain     }
115a8857c50SChris Cain 
116c567dc8dSChris Cain     const uint8_t cmd_type = command[0];
117a8857c50SChris Cain #ifdef TRACE_PACKETS
118*37abe9beSChris Cain     lg2::info("OCC{INST}: Sending {CMD} command (length={LEN}, {PATH})", "INST",
119*37abe9beSChris Cain               occInstance, "CMD", lg2::hex, cmd_type, "LEN", command.size(),
120*37abe9beSChris Cain               "PATH", devicePath);
121a8857c50SChris Cain     dump_hex(command);
122a8857c50SChris Cain #else
123*37abe9beSChris Cain     lg2::debug("OccCommand::send: calling write()");
124a8857c50SChris Cain #endif
12578e86012SChris Cain 
12678e86012SChris Cain     int retries = 1; // Allow a retry if a command fails to get valid response
12778e86012SChris Cain     do
12878e86012SChris Cain     {
129a8857c50SChris Cain         auto rc = write(fd, command.data(), command.size());
130c567dc8dSChris Cain         const int writeErrno = errno;
131a8857c50SChris Cain         if ((rc < 0) || (rc != (int)command.size()))
132a8857c50SChris Cain         {
133*37abe9beSChris Cain             lg2::error(
134*37abe9beSChris Cain                 "OccCommand::send: write(OCC{INST}, command:{CMD}) failed with errno={ERR} (retries={RETRIES})",
135*37abe9beSChris Cain                 "INST", occInstance, "CMD", lg2::hex, cmd_type, "ERR",
136*37abe9beSChris Cain                 writeErrno, "RETRIES", retries);
137c567dc8dSChris Cain             status = CmdStatus::COMM_FAILURE;
138c567dc8dSChris Cain             // retry if available
139c567dc8dSChris Cain             continue;
140a8857c50SChris Cain         }
141a8857c50SChris Cain         else
142a8857c50SChris Cain         {
143*37abe9beSChris Cain             lg2::debug("OccCommand::send: write succeeded");
144a8857c50SChris Cain         }
145a8857c50SChris Cain 
146a8857c50SChris Cain         // Now read the response. This would be the content of occ-sram
147a8857c50SChris Cain         while (1)
148a8857c50SChris Cain         {
149a8857c50SChris Cain             uint8_t data{};
150a8857c50SChris Cain             auto len = read(fd, &data, sizeof(data));
151c567dc8dSChris Cain             const int readErrno = errno;
152a8857c50SChris Cain             if (len > 0)
153a8857c50SChris Cain             {
154a8857c50SChris Cain                 response.emplace_back(data);
155a8857c50SChris Cain             }
156c567dc8dSChris Cain             else if (len < 0 && readErrno == EAGAIN)
157a8857c50SChris Cain             {
158a8857c50SChris Cain                 // We may have data coming still.
159a8857c50SChris Cain                 // This driver does not need a sleep for a retry.
160a8857c50SChris Cain                 continue;
161a8857c50SChris Cain             }
162a8857c50SChris Cain             else if (len == 0)
163a8857c50SChris Cain             {
164*37abe9beSChris Cain                 lg2::debug("OccCommand::send: read completed");
165a8857c50SChris Cain                 // We have read all that we can.
166a8857c50SChris Cain                 status = CmdStatus::SUCCESS;
167a8857c50SChris Cain                 break;
168a8857c50SChris Cain             }
169a8857c50SChris Cain             else
170a8857c50SChris Cain             {
171*37abe9beSChris Cain                 lg2::error(
172*37abe9beSChris Cain                     "OccCommand::send: read(OCC{INST}, command:{CMD) failed with errno={ERR} (rspSize={LEN}, retries={RETRIES})",
173*37abe9beSChris Cain                     "INST", occInstance, "CMD", lg2::hex, cmd_type, "ERR",
174*37abe9beSChris Cain                     readErrno, "LEN", response.size(), "RETRIES", retries);
175c567dc8dSChris Cain                 status = CmdStatus::COMM_FAILURE;
176c567dc8dSChris Cain                 break;
177a8857c50SChris Cain             }
178a8857c50SChris Cain         }
179c567dc8dSChris Cain         if (status != CmdStatus::SUCCESS)
180c567dc8dSChris Cain         {
181c567dc8dSChris Cain             // retry if available
182c567dc8dSChris Cain             continue;
183c567dc8dSChris Cain         }
184a8857c50SChris Cain 
185a8857c50SChris Cain         if (response.size() > 2)
186a8857c50SChris Cain         {
187a8857c50SChris Cain #ifdef TRACE_PACKETS
188*37abe9beSChris Cain             lg2::info(
189*37abe9beSChris Cain                 "OCC{INST}: Received {CMD} response (length={LEN} w/checksum)",
190*37abe9beSChris Cain                 "INST", occInstance, "CMD", lg2::hex, cmd_type, "LEN",
191*37abe9beSChris Cain                 response.size());
192a8857c50SChris Cain             dump_hex(response, 64);
193a8857c50SChris Cain #endif
194a8857c50SChris Cain 
195a8857c50SChris Cain             // Validate checksum (last 2 bytes of response)
196a8857c50SChris Cain             const unsigned int csumIndex = response.size() - 2;
197d7542c83SPatrick Williams             const uint32_t rspChecksum =
198d7542c83SPatrick Williams                 (response[csumIndex] << 8) + response[csumIndex + 1];
199358d3966SChris Cain             // OCC checksum is the 2 byte sum of data (ignoring overflow)
200358d3966SChris Cain             uint16_t calcChecksum = 0;
201a8857c50SChris Cain             for (unsigned int index = 0; index < csumIndex; ++index)
202a8857c50SChris Cain             {
203a8857c50SChris Cain                 calcChecksum += response[index];
204a8857c50SChris Cain             }
205a8857c50SChris Cain             if (calcChecksum != rspChecksum)
206a8857c50SChris Cain             {
207*37abe9beSChris Cain                 lg2::error("OCC{INST}: Checksum Mismatch: response "
208*37abe9beSChris Cain                            "{RCVD}, calculated {EXPECT}",
209*37abe9beSChris Cain                            "INST", occInstance, "RCVD", rspChecksum, "EXPECT",
210*37abe9beSChris Cain                            calcChecksum);
211a8857c50SChris Cain                 dump_hex(response);
212c567dc8dSChris Cain                 status = CmdStatus::COMM_FAILURE;
213a8857c50SChris Cain             }
214a8857c50SChris Cain             else
215a8857c50SChris Cain             {
21678e86012SChris Cain                 // Validate response was for the specified command
21778e86012SChris Cain                 if (command[0] == response[1])
21878e86012SChris Cain                 {
21978e86012SChris Cain                     // Valid response received
22078e86012SChris Cain 
221a8857c50SChris Cain                     // Strip off 2 byte checksum
222a8857c50SChris Cain                     response.pop_back();
223a8857c50SChris Cain                     response.pop_back();
22478e86012SChris Cain                     break;
22578e86012SChris Cain                 }
22678e86012SChris Cain                 else
22778e86012SChris Cain                 {
228*37abe9beSChris Cain                     lg2::error("OccCommand::send: Response command mismatch "
22978e86012SChris Cain                                "(sent: "
230*37abe9beSChris Cain                                "{CMD}, rsp: {STATUS}, rsp seq#: {SEQ}",
231*37abe9beSChris Cain                                "CMD", lg2::hex, command[0], "STATUS", lg2::hex,
232*37abe9beSChris Cain                                response[1], "SEQ", lg2::hex, response[0]);
23378e86012SChris Cain                     dump_hex(response, 64);
23478e86012SChris Cain                 }
235a8857c50SChris Cain             }
236a8857c50SChris Cain         }
237a8857c50SChris Cain         else
238a8857c50SChris Cain         {
239*37abe9beSChris Cain             lg2::error(
240*37abe9beSChris Cain                 "OccCommand::send: Invalid OCC{INST} response length: {LEN}",
241*37abe9beSChris Cain                 "INST", occInstance, "LEN", response.size());
242a8857c50SChris Cain             status = CmdStatus::FAILURE;
243a8857c50SChris Cain             dump_hex(response);
244a8857c50SChris Cain         }
245a8857c50SChris Cain 
24678e86012SChris Cain         if (retries > 0)
24778e86012SChris Cain         {
248*37abe9beSChris Cain             lg2::error("OccCommand::send: Command will be retried");
24978e86012SChris Cain             response.clear();
25078e86012SChris Cain         }
25178e86012SChris Cain     } while (retries-- > 0);
25278e86012SChris Cain 
253a8857c50SChris Cain     closeDevice();
254a8857c50SChris Cain 
255a8857c50SChris Cain     return status;
256a8857c50SChris Cain }
257a8857c50SChris Cain 
258a8857c50SChris Cain // Called at OCC Status change signal
activeStatusEvent(sdbusplus::message_t & msg)259af40808fSPatrick Williams void OccCommand::activeStatusEvent(sdbusplus::message_t& msg)
260a8857c50SChris Cain {
261a8857c50SChris Cain     std::string statusInterface;
262a8857c50SChris Cain     std::map<std::string, std::variant<bool>> msgData;
263a8857c50SChris Cain     msg.read(statusInterface, msgData);
264a8857c50SChris Cain 
265a8857c50SChris Cain     auto propertyMap = msgData.find("OccActive");
266a8857c50SChris Cain     if (propertyMap != msgData.end())
267a8857c50SChris Cain     {
268a8857c50SChris Cain         // Extract the OccActive property
269a8857c50SChris Cain         if (std::get<bool>(propertyMap->second))
270a8857c50SChris Cain         {
271a8857c50SChris Cain             occActive = true;
272a8857c50SChris Cain         }
273a8857c50SChris Cain         else
274a8857c50SChris Cain         {
275a8857c50SChris Cain             occActive = false;
276a8857c50SChris Cain 
277a8857c50SChris Cain             this->closeDevice();
278a8857c50SChris Cain         }
279a8857c50SChris Cain     }
280a8857c50SChris Cain     return;
281a8857c50SChris Cain }
282a8857c50SChris Cain 
283a8857c50SChris Cain } // namespace occ
284a8857c50SChris Cain } // namespace open_power
285