blob: d8eec1c1d63ce8c8580bd97e7f931f2b0e504c45 [file] [log] [blame]
Chris Caina8857c52021-01-27 11:53:05 -06001#include "config.h"
2
3#include "occ_command.hpp"
4
5#include "elog-errors.hpp"
6
7#include <errno.h>
8#include <fcntl.h>
9#include <fmt/core.h>
10#include <unistd.h>
11
Chris Caina8857c52021-01-27 11:53:05 -060012#include <org/open_power/OCC/Device/error.hpp>
13#include <phosphor-logging/elog.hpp>
14#include <phosphor-logging/log.hpp>
George Liub5ca1012021-09-10 12:53:11 +080015
16#include <algorithm>
17#include <memory>
Chris Caina8857c52021-01-27 11:53:05 -060018#include <string>
19
Chris Cainf9fd1e52022-10-04 13:39:24 -050020// #define TRACE_PACKETS
Chris Caina8857c52021-01-27 11:53:05 -060021
Eddie Jamesd4af6522022-11-09 08:39:50 -060022std::ostream& operator<<(std::ostream& os, open_power::occ::SysPwrMode spm)
23{
24 return os << static_cast<int>(spm);
25}
26
Chris Caina8857c52021-01-27 11:53:05 -060027namespace open_power
28{
29namespace occ
30{
31
32using namespace phosphor::logging;
33
34// Trace block of data in hex
35void dump_hex(const std::vector<std::uint8_t>& data,
36 const unsigned int data_len)
37{
38 unsigned int dump_length = data.size();
39 if ((data_len > 0) && (data_len < dump_length))
40 {
41 dump_length = data_len;
42 }
43 std::string s;
44 for (uint32_t i = 0; i < dump_length; i++)
45 {
46 if (i % 16 == 0)
47 {
48 s += fmt::format("{:04X}: ", i);
49 }
50 else if (i % 4 == 0)
51 {
52 s += " ";
53 }
54
55 s += fmt::format("{:02X}", data.at(i));
56
57 if ((i % 16 == 15) || (i == (dump_length - 1)))
58 {
59 log<level::INFO>(s.c_str());
60 s.clear();
61 }
62 }
63}
64
George Liuf3b75142021-06-10 11:22:50 +080065OccCommand::OccCommand(uint8_t instance, const char* path) :
66 occInstance(instance), path(path),
Chris Caina8857c52021-01-27 11:53:05 -060067 devicePath(OCC_DEV_PATH + std::to_string((this->path.back() - '0') + 1)),
68 activeStatusSignal(
George Liuf3b75142021-06-10 11:22:50 +080069 utils::getBus(),
70 sdbusRule::propertiesChanged(path, "org.open_power.OCC.Status"),
Chris Caina8857c52021-01-27 11:53:05 -060071 std::bind(std::mem_fn(&OccCommand::activeStatusEvent), this,
72 std::placeholders::_1))
73{
74 log<level::DEBUG>(
75 fmt::format("OccCommand::OccCommand(path={}, devicePath={}", this->path,
76 devicePath)
77 .c_str());
78}
79
80void OccCommand::openDevice()
81{
82 using namespace sdbusplus::org::open_power::OCC::Device::Error;
83
84 log<level::DEBUG>(
85 fmt::format("OccCommand::openDevice: calling open {}", devicePath)
86 .c_str());
87 fd = open(devicePath.c_str(), O_RDWR | O_NONBLOCK);
88 if (fd < 0)
89 {
Chris Cainc567dc82022-04-01 15:09:17 -050090 const int openErrno = errno;
Chris Caina8857c52021-01-27 11:53:05 -060091 log<level::ERR>(
92 fmt::format(
93 "OccCommand::openDevice: open failed (errno={}, path={})",
Chris Cainc567dc82022-04-01 15:09:17 -050094 openErrno, devicePath)
Chris Caina8857c52021-01-27 11:53:05 -060095 .c_str());
Chris Caina8857c52021-01-27 11:53:05 -060096 }
97 else
98 {
99 log<level::DEBUG>("OccCommand::openDevice: open success");
100 }
101
102 return;
103}
104
105void OccCommand::closeDevice()
106{
107 if (fd >= 0)
108 {
109 log<level::DEBUG>("OccCommand::closeDevice: calling close()");
110 close(fd);
111 fd = -1;
112 }
113}
114
115CmdStatus OccCommand::send(const std::vector<uint8_t>& command,
116 std::vector<uint8_t>& response)
117{
118 using namespace sdbusplus::org::open_power::OCC::Device::Error;
119 CmdStatus status = CmdStatus::FAILURE;
120
121 response.clear();
122
123 log<level::DEBUG>("OccCommand::send: calling openDevice()");
124 openDevice();
125
126 if (fd < 0)
127 {
128 // OCC is inactive; empty response
Chris Cainc567dc82022-04-01 15:09:17 -0500129 return CmdStatus::COMM_FAILURE;
Chris Caina8857c52021-01-27 11:53:05 -0600130 }
131
Chris Cainc567dc82022-04-01 15:09:17 -0500132 const uint8_t cmd_type = command[0];
Chris Caina8857c52021-01-27 11:53:05 -0600133#ifdef TRACE_PACKETS
Chris Caina8857c52021-01-27 11:53:05 -0600134 log<level::INFO>(
135 fmt::format("OCC{}: Sending 0x{:02X} command (length={}, {})",
136 occInstance, cmd_type, command.size(), devicePath)
137 .c_str());
138 dump_hex(command);
139#else
140 log<level::DEBUG>("OccCommand::send: calling write()");
141#endif
Chris Caina8857c52021-01-27 11:53:05 -0600142
Chris Cain78e86012021-03-04 16:15:31 -0600143 int retries = 1; // Allow a retry if a command fails to get valid response
144 do
Chris Caina8857c52021-01-27 11:53:05 -0600145 {
Chris Cain78e86012021-03-04 16:15:31 -0600146 auto rc = write(fd, command.data(), command.size());
Chris Cainc567dc82022-04-01 15:09:17 -0500147 const int writeErrno = errno;
Chris Cain78e86012021-03-04 16:15:31 -0600148 if ((rc < 0) || (rc != (int)command.size()))
Chris Caina8857c52021-01-27 11:53:05 -0600149 {
Chris Cainc567dc82022-04-01 15:09:17 -0500150 log<level::ERR>(
151 fmt::format(
152 "OccCommand::send: write(OCC{}, command:0x{:02X}) failed with errno={} (retries={})",
153 occInstance, cmd_type, writeErrno, retries)
154 .c_str());
155 status = CmdStatus::COMM_FAILURE;
156 // retry if available
157 continue;
Chris Caina8857c52021-01-27 11:53:05 -0600158 }
Chris Cain78e86012021-03-04 16:15:31 -0600159 else
160 {
161 log<level::DEBUG>("OccCommand::send: write succeeded");
162 }
Chris Caina8857c52021-01-27 11:53:05 -0600163
Chris Cain78e86012021-03-04 16:15:31 -0600164 // Now read the response. This would be the content of occ-sram
165 while (1)
166 {
167 uint8_t data{};
168 auto len = read(fd, &data, sizeof(data));
Chris Cainc567dc82022-04-01 15:09:17 -0500169 const int readErrno = errno;
Chris Cain78e86012021-03-04 16:15:31 -0600170 if (len > 0)
171 {
172 response.emplace_back(data);
173 }
Chris Cainc567dc82022-04-01 15:09:17 -0500174 else if (len < 0 && readErrno == EAGAIN)
Chris Cain78e86012021-03-04 16:15:31 -0600175 {
176 // We may have data coming still.
177 // This driver does not need a sleep for a retry.
178 continue;
179 }
180 else if (len == 0)
181 {
182 log<level::DEBUG>("OccCommand::send: read completed");
183 // We have read all that we can.
184 status = CmdStatus::SUCCESS;
185 break;
186 }
187 else
188 {
Chris Cainc567dc82022-04-01 15:09:17 -0500189 log<level::ERR>(
190 fmt::format(
191 "OccCommand::send: read(OCC{}, command:0x{:02X}) failed with errno={} (rspSize={}, retries={})",
192 occInstance, cmd_type, readErrno, response.size(),
193 retries)
194 .c_str());
195 status = CmdStatus::COMM_FAILURE;
196 break;
Chris Cain78e86012021-03-04 16:15:31 -0600197 }
198 }
Chris Cainc567dc82022-04-01 15:09:17 -0500199 if (status != CmdStatus::SUCCESS)
200 {
201 // retry if available
202 continue;
203 }
Chris Cain78e86012021-03-04 16:15:31 -0600204
205 if (response.size() > 2)
206 {
Chris Caina8857c52021-01-27 11:53:05 -0600207#ifdef TRACE_PACKETS
Chris Cain78e86012021-03-04 16:15:31 -0600208 log<level::INFO>(
209 fmt::format(
210 "OCC{}: Received 0x{:02X} response (length={} w/checksum)",
211 occInstance, cmd_type, response.size())
212 .c_str());
213 dump_hex(response, 64);
Chris Caina8857c52021-01-27 11:53:05 -0600214#endif
215
Chris Cain78e86012021-03-04 16:15:31 -0600216 // Validate checksum (last 2 bytes of response)
217 const unsigned int csumIndex = response.size() - 2;
218 const uint32_t rspChecksum =
219 (response[csumIndex] << 8) + response[csumIndex + 1];
220 uint32_t calcChecksum = 0;
221 for (unsigned int index = 0; index < csumIndex; ++index)
222 {
223 calcChecksum += response[index];
224 }
225 while (calcChecksum > 0xFFFF)
226 {
227 calcChecksum = (calcChecksum & 0xFFFF) + (calcChecksum >> 16);
228 }
229 if (calcChecksum != rspChecksum)
230 {
231 log<level::ERR>(
232 fmt::format("OCC{}: Checksum Mismatch: response "
233 "0x{:04X}, calculated 0x{:04X}",
234 occInstance, rspChecksum, calcChecksum)
235 .c_str());
236 dump_hex(response);
Chris Cainc567dc82022-04-01 15:09:17 -0500237 status = CmdStatus::COMM_FAILURE;
Chris Cain78e86012021-03-04 16:15:31 -0600238 }
239 else
240 {
241 // Validate response was for the specified command
242 if (command[0] == response[1])
243 {
244 // Valid response received
245
246 // Strip off 2 byte checksum
247 response.pop_back();
248 response.pop_back();
249 break;
250 }
251 else
252 {
253 log<level::ERR>(
254 fmt::format(
255 "OccCommand::send: Response command mismatch "
256 "(sent: "
257 "0x{:02X}, rsp: 0x{:02X}, rsp seq#: 0x{:02X}",
258 command[0], response[1], response[0])
259 .c_str());
260 dump_hex(response, 64);
261 }
262 }
Chris Caina8857c52021-01-27 11:53:05 -0600263 }
264 else
265 {
Chris Cain78e86012021-03-04 16:15:31 -0600266 log<level::ERR>(
267 fmt::format(
268 "OccCommand::send: Invalid OCC{} response length: {}",
269 occInstance, response.size())
270 .c_str());
271 status = CmdStatus::FAILURE;
272 dump_hex(response);
Chris Caina8857c52021-01-27 11:53:05 -0600273 }
Chris Cain78e86012021-03-04 16:15:31 -0600274
275 if (retries > 0)
276 {
277 log<level::ERR>("OccCommand::send: Command will be retried");
278 response.clear();
279 }
Chris Cain78e86012021-03-04 16:15:31 -0600280 } while (retries-- > 0);
Chris Caina8857c52021-01-27 11:53:05 -0600281
282 closeDevice();
283
284 return status;
285}
286
287// Called at OCC Status change signal
Patrick Williamsaf408082022-07-22 19:26:54 -0500288void OccCommand::activeStatusEvent(sdbusplus::message_t& msg)
Chris Caina8857c52021-01-27 11:53:05 -0600289{
290 std::string statusInterface;
291 std::map<std::string, std::variant<bool>> msgData;
292 msg.read(statusInterface, msgData);
293
294 auto propertyMap = msgData.find("OccActive");
295 if (propertyMap != msgData.end())
296 {
297 // Extract the OccActive property
298 if (std::get<bool>(propertyMap->second))
299 {
300 occActive = true;
301 }
302 else
303 {
304 occActive = false;
305
306 this->closeDevice();
307 }
308 }
309 return;
310}
311
312} // namespace occ
313} // namespace open_power