blob: 5d693e7b47b63b834c8a392566fcb80fcb6ba551 [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
12#include <algorithm>
13#include <memory>
14#include <org/open_power/OCC/Device/error.hpp>
15#include <phosphor-logging/elog.hpp>
16#include <phosphor-logging/log.hpp>
17#include <string>
18
19//#define TRACE_PACKETS
20
21namespace open_power
22{
23namespace occ
24{
25
26using namespace phosphor::logging;
27
28// Trace block of data in hex
29void dump_hex(const std::vector<std::uint8_t>& data,
30 const unsigned int data_len)
31{
32 unsigned int dump_length = data.size();
33 if ((data_len > 0) && (data_len < dump_length))
34 {
35 dump_length = data_len;
36 }
37 std::string s;
38 for (uint32_t i = 0; i < dump_length; i++)
39 {
40 if (i % 16 == 0)
41 {
42 s += fmt::format("{:04X}: ", i);
43 }
44 else if (i % 4 == 0)
45 {
46 s += " ";
47 }
48
49 s += fmt::format("{:02X}", data.at(i));
50
51 if ((i % 16 == 15) || (i == (dump_length - 1)))
52 {
53 log<level::INFO>(s.c_str());
54 s.clear();
55 }
56 }
57}
58
George Liuf3b75142021-06-10 11:22:50 +080059OccCommand::OccCommand(uint8_t instance, const char* path) :
60 occInstance(instance), path(path),
Chris Caina8857c52021-01-27 11:53:05 -060061 devicePath(OCC_DEV_PATH + std::to_string((this->path.back() - '0') + 1)),
62 activeStatusSignal(
George Liuf3b75142021-06-10 11:22:50 +080063 utils::getBus(),
64 sdbusRule::propertiesChanged(path, "org.open_power.OCC.Status"),
Chris Caina8857c52021-01-27 11:53:05 -060065 std::bind(std::mem_fn(&OccCommand::activeStatusEvent), this,
66 std::placeholders::_1))
67{
68 log<level::DEBUG>(
69 fmt::format("OccCommand::OccCommand(path={}, devicePath={}", this->path,
70 devicePath)
71 .c_str());
72}
73
74void OccCommand::openDevice()
75{
76 using namespace sdbusplus::org::open_power::OCC::Device::Error;
77
78 log<level::DEBUG>(
79 fmt::format("OccCommand::openDevice: calling open {}", devicePath)
80 .c_str());
81 fd = open(devicePath.c_str(), O_RDWR | O_NONBLOCK);
82 if (fd < 0)
83 {
84 const int open_errno = errno;
85 log<level::ERR>(
86 fmt::format(
87 "OccCommand::openDevice: open failed (errno={}, path={})",
88 open_errno, devicePath)
89 .c_str());
90 // This would log and terminate since its not handled.
91 elog<OpenFailure>(
92 phosphor::logging::org::open_power::OCC::Device::OpenFailure::
93 CALLOUT_ERRNO(open_errno),
94 phosphor::logging::org::open_power::OCC::Device::OpenFailure::
95 CALLOUT_DEVICE_PATH(devicePath.c_str()));
96 }
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
129 return CmdStatus::OPEN_FAILURE;
130 }
131
132#ifdef TRACE_PACKETS
133 uint8_t cmd_type = command[0];
134 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());
147 if ((rc < 0) || (rc != (int)command.size()))
Chris Caina8857c52021-01-27 11:53:05 -0600148 {
Chris Cain78e86012021-03-04 16:15:31 -0600149 const int write_errno = errno;
150 log<level::ERR>("OccCommand::send: write failed");
Chris Caina8857c52021-01-27 11:53:05 -0600151 // This would log and terminate since its not handled.
Chris Cain78e86012021-03-04 16:15:31 -0600152 elog<WriteFailure>(
153 phosphor::logging::org::open_power::OCC::Device::WriteFailure::
154 CALLOUT_ERRNO(write_errno),
155 phosphor::logging::org::open_power::OCC::Device::WriteFailure::
Chris Caina8857c52021-01-27 11:53:05 -0600156 CALLOUT_DEVICE_PATH(devicePath.c_str()));
157 }
Chris Cain78e86012021-03-04 16:15:31 -0600158 else
159 {
160 log<level::DEBUG>("OccCommand::send: write succeeded");
161 }
Chris Caina8857c52021-01-27 11:53:05 -0600162
Chris Cain78e86012021-03-04 16:15:31 -0600163 // Now read the response. This would be the content of occ-sram
164 while (1)
165 {
166 uint8_t data{};
167 auto len = read(fd, &data, sizeof(data));
168 const int read_errno = errno;
169 if (len > 0)
170 {
171 response.emplace_back(data);
172 }
173 else if (len < 0 && read_errno == EAGAIN)
174 {
175 // We may have data coming still.
176 // This driver does not need a sleep for a retry.
177 continue;
178 }
179 else if (len == 0)
180 {
181 log<level::DEBUG>("OccCommand::send: read completed");
182 // We have read all that we can.
183 status = CmdStatus::SUCCESS;
184 break;
185 }
186 else
187 {
188 log<level::ERR>("OccCommand::send: read failed");
189 // This would log and terminate since its not handled.
190 elog<ReadFailure>(
191 phosphor::logging::org::open_power::OCC::Device::
192 ReadFailure::CALLOUT_ERRNO(read_errno),
193 phosphor::logging::org::open_power::OCC::Device::
194 ReadFailure::CALLOUT_DEVICE_PATH(devicePath.c_str()));
195 }
196 }
197
198 if (response.size() > 2)
199 {
Chris Caina8857c52021-01-27 11:53:05 -0600200#ifdef TRACE_PACKETS
Chris Cain78e86012021-03-04 16:15:31 -0600201 log<level::INFO>(
202 fmt::format(
203 "OCC{}: Received 0x{:02X} response (length={} w/checksum)",
204 occInstance, cmd_type, response.size())
205 .c_str());
206 dump_hex(response, 64);
Chris Caina8857c52021-01-27 11:53:05 -0600207#endif
208
Chris Cain78e86012021-03-04 16:15:31 -0600209 // Validate checksum (last 2 bytes of response)
210 const unsigned int csumIndex = response.size() - 2;
211 const uint32_t rspChecksum =
212 (response[csumIndex] << 8) + response[csumIndex + 1];
213 uint32_t calcChecksum = 0;
214 for (unsigned int index = 0; index < csumIndex; ++index)
215 {
216 calcChecksum += response[index];
217 }
218 while (calcChecksum > 0xFFFF)
219 {
220 calcChecksum = (calcChecksum & 0xFFFF) + (calcChecksum >> 16);
221 }
222 if (calcChecksum != rspChecksum)
223 {
224 log<level::ERR>(
225 fmt::format("OCC{}: Checksum Mismatch: response "
226 "0x{:04X}, calculated 0x{:04X}",
227 occInstance, rspChecksum, calcChecksum)
228 .c_str());
229 dump_hex(response);
230 status = CmdStatus::INVALID_CHECKSUM;
231 }
232 else
233 {
234 // Validate response was for the specified command
235 if (command[0] == response[1])
236 {
237 // Valid response received
238
239 // Strip off 2 byte checksum
240 response.pop_back();
241 response.pop_back();
242 break;
243 }
244 else
245 {
246 log<level::ERR>(
247 fmt::format(
248 "OccCommand::send: Response command mismatch "
249 "(sent: "
250 "0x{:02X}, rsp: 0x{:02X}, rsp seq#: 0x{:02X}",
251 command[0], response[1], response[0])
252 .c_str());
253 dump_hex(response, 64);
254 }
255 }
Chris Caina8857c52021-01-27 11:53:05 -0600256 }
257 else
258 {
Chris Cain78e86012021-03-04 16:15:31 -0600259 log<level::ERR>(
260 fmt::format(
261 "OccCommand::send: Invalid OCC{} response length: {}",
262 occInstance, response.size())
263 .c_str());
264 status = CmdStatus::FAILURE;
265 dump_hex(response);
Chris Caina8857c52021-01-27 11:53:05 -0600266 }
Chris Cain78e86012021-03-04 16:15:31 -0600267
268 if (retries > 0)
269 {
270 log<level::ERR>("OccCommand::send: Command will be retried");
271 response.clear();
272 }
273
274 } while (retries-- > 0);
Chris Caina8857c52021-01-27 11:53:05 -0600275
276 closeDevice();
277
278 return status;
279}
280
281// Called at OCC Status change signal
282void OccCommand::activeStatusEvent(sdbusplus::message::message& msg)
283{
284 std::string statusInterface;
285 std::map<std::string, std::variant<bool>> msgData;
286 msg.read(statusInterface, msgData);
287
288 auto propertyMap = msgData.find("OccActive");
289 if (propertyMap != msgData.end())
290 {
291 // Extract the OccActive property
292 if (std::get<bool>(propertyMap->second))
293 {
294 occActive = true;
295 }
296 else
297 {
298 occActive = false;
299
300 this->closeDevice();
301 }
302 }
303 return;
304}
305
306} // namespace occ
307} // namespace open_power