blob: e9b03a9881ad972705b96ab9a7c2deb14ef15d68 [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
59OccCommand::OccCommand(uint8_t instance, sdbusplus::bus::bus& bus,
60 const char* path) :
61 occInstance(instance),
62 path(path),
63 devicePath(OCC_DEV_PATH + std::to_string((this->path.back() - '0') + 1)),
64 activeStatusSignal(
65 bus, sdbusRule::propertiesChanged(path, "org.open_power.OCC.Status"),
66 std::bind(std::mem_fn(&OccCommand::activeStatusEvent), this,
67 std::placeholders::_1))
68{
69 log<level::DEBUG>(
70 fmt::format("OccCommand::OccCommand(path={}, devicePath={}", this->path,
71 devicePath)
72 .c_str());
73}
74
75void OccCommand::openDevice()
76{
77 using namespace sdbusplus::org::open_power::OCC::Device::Error;
78
79 log<level::DEBUG>(
80 fmt::format("OccCommand::openDevice: calling open {}", devicePath)
81 .c_str());
82 fd = open(devicePath.c_str(), O_RDWR | O_NONBLOCK);
83 if (fd < 0)
84 {
85 const int open_errno = errno;
86 log<level::ERR>(
87 fmt::format(
88 "OccCommand::openDevice: open failed (errno={}, path={})",
89 open_errno, devicePath)
90 .c_str());
91 // This would log and terminate since its not handled.
92 elog<OpenFailure>(
93 phosphor::logging::org::open_power::OCC::Device::OpenFailure::
94 CALLOUT_ERRNO(open_errno),
95 phosphor::logging::org::open_power::OCC::Device::OpenFailure::
96 CALLOUT_DEVICE_PATH(devicePath.c_str()));
97 }
98 else
99 {
100 log<level::DEBUG>("OccCommand::openDevice: open success");
101 }
102
103 return;
104}
105
106void OccCommand::closeDevice()
107{
108 if (fd >= 0)
109 {
110 log<level::DEBUG>("OccCommand::closeDevice: calling close()");
111 close(fd);
112 fd = -1;
113 }
114}
115
116CmdStatus OccCommand::send(const std::vector<uint8_t>& command,
117 std::vector<uint8_t>& response)
118{
119 using namespace sdbusplus::org::open_power::OCC::Device::Error;
120 CmdStatus status = CmdStatus::FAILURE;
121
122 response.clear();
123
124 log<level::DEBUG>("OccCommand::send: calling openDevice()");
125 openDevice();
126
127 if (fd < 0)
128 {
129 // OCC is inactive; empty response
130 return CmdStatus::OPEN_FAILURE;
131 }
132
133#ifdef TRACE_PACKETS
134 uint8_t cmd_type = command[0];
135 log<level::INFO>(
136 fmt::format("OCC{}: Sending 0x{:02X} command (length={}, {})",
137 occInstance, cmd_type, command.size(), devicePath)
138 .c_str());
139 dump_hex(command);
140#else
141 log<level::DEBUG>("OccCommand::send: calling write()");
142#endif
143 auto rc = write(fd, command.data(), command.size());
144 if ((rc < 0) || (rc != (int)command.size()))
145 {
146 const int write_errno = errno;
147 log<level::ERR>("OccCommand::send: write failed");
148 // This would log and terminate since its not handled.
149 elog<WriteFailure>(
150 phosphor::logging::org::open_power::OCC::Device::WriteFailure::
151 CALLOUT_ERRNO(write_errno),
152 phosphor::logging::org::open_power::OCC::Device::WriteFailure::
153 CALLOUT_DEVICE_PATH(devicePath.c_str()));
154 }
155 else
156 {
157 log<level::DEBUG>("OccCommand::send: write succeeded");
158 }
159
160 // Now read the response. This would be the content of occ-sram
161 while (1)
162 {
163 uint8_t data{};
164 auto len = read(fd, &data, sizeof(data));
165 const int read_errno = errno;
166 if (len > 0)
167 {
168 response.emplace_back(data);
169 }
170 else if (len < 0 && read_errno == EAGAIN)
171 {
172 // We may have data coming still.
173 // This driver does not need a sleep for a retry.
174 continue;
175 }
176 else if (len == 0)
177 {
178 log<level::DEBUG>("OccCommand::send: read completed");
179 // We have read all that we can.
180 status = CmdStatus::SUCCESS;
181 break;
182 }
183 else
184 {
185 log<level::ERR>("OccCommand::send: read failed");
186 // This would log and terminate since its not handled.
187 elog<ReadFailure>(
188 phosphor::logging::org::open_power::OCC::Device::ReadFailure::
189 CALLOUT_ERRNO(read_errno),
190 phosphor::logging::org::open_power::OCC::Device::ReadFailure::
191 CALLOUT_DEVICE_PATH(devicePath.c_str()));
192 }
193 }
194
195 if (response.size() > 2)
196 {
197#ifdef TRACE_PACKETS
198 log<level::INFO>(
199 fmt::format(
200 "OCC{}: Received 0x{:02X} response (length={} w/checksum)",
201 occInstance, cmd_type, response.size())
202 .c_str());
203 dump_hex(response, 64);
204#endif
205
206 // Validate checksum (last 2 bytes of response)
207 const unsigned int csumIndex = response.size() - 2;
208 const uint32_t rspChecksum =
209 (response[csumIndex] << 8) + response[csumIndex + 1];
210 uint32_t calcChecksum = 0;
211 for (unsigned int index = 0; index < csumIndex; ++index)
212 {
213 calcChecksum += response[index];
214 }
215 while (calcChecksum > 0xFFFF)
216 {
217 calcChecksum = (calcChecksum & 0xFFFF) + (calcChecksum >> 16);
218 }
219 if (calcChecksum != rspChecksum)
220 {
221 log<level::ERR>(fmt::format("OCC{}: Checksum Mismatch: response "
222 "0x{:04X}, calculated 0x{:04X}",
223 occInstance, rspChecksum, calcChecksum)
224 .c_str());
225 dump_hex(response);
226 status = CmdStatus::INVALID_CHECKSUM;
227 }
228 else
229 {
230 // Strip off 2 byte checksum
231 response.pop_back();
232 response.pop_back();
233 }
234 }
235 else
236 {
237 log<level::ERR>(
238 fmt::format("OccCommand::send: Invalid OCC{} response length: {}",
239 occInstance, response.size())
240 .c_str());
241 status = CmdStatus::FAILURE;
242 dump_hex(response);
243 }
244
245 closeDevice();
246
247 return status;
248}
249
250// Called at OCC Status change signal
251void OccCommand::activeStatusEvent(sdbusplus::message::message& msg)
252{
253 std::string statusInterface;
254 std::map<std::string, std::variant<bool>> msgData;
255 msg.read(statusInterface, msgData);
256
257 auto propertyMap = msgData.find("OccActive");
258 if (propertyMap != msgData.end())
259 {
260 // Extract the OccActive property
261 if (std::get<bool>(propertyMap->second))
262 {
263 occActive = true;
264 }
265 else
266 {
267 occActive = false;
268
269 this->closeDevice();
270 }
271 }
272 return;
273}
274
275} // namespace occ
276} // namespace open_power