1 #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 <org/open_power/OCC/Device/error.hpp> 13 #include <phosphor-logging/elog.hpp> 14 #include <phosphor-logging/log.hpp> 15 16 #include <algorithm> 17 #include <memory> 18 #include <string> 19 20 // #define TRACE_PACKETS 21 22 namespace open_power 23 { 24 namespace occ 25 { 26 27 using namespace phosphor::logging; 28 29 // Trace block of data in hex 30 void dump_hex(const std::vector<std::uint8_t>& data, 31 const unsigned int data_len) 32 { 33 unsigned int dump_length = data.size(); 34 if ((data_len > 0) && (data_len < dump_length)) 35 { 36 dump_length = data_len; 37 } 38 std::string s; 39 for (uint32_t i = 0; i < dump_length; i++) 40 { 41 if (i % 16 == 0) 42 { 43 s += fmt::format("{:04X}: ", i); 44 } 45 else if (i % 4 == 0) 46 { 47 s += " "; 48 } 49 50 s += fmt::format("{:02X}", data.at(i)); 51 52 if ((i % 16 == 15) || (i == (dump_length - 1))) 53 { 54 log<level::INFO>(s.c_str()); 55 s.clear(); 56 } 57 } 58 } 59 60 OccCommand::OccCommand(uint8_t instance, const char* path) : 61 occInstance(instance), path(path), 62 devicePath(OCC_DEV_PATH + std::to_string((this->path.back() - '0') + 1)), 63 activeStatusSignal( 64 utils::getBus(), 65 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 75 void 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 openErrno = errno; 86 log<level::ERR>( 87 fmt::format( 88 "OccCommand::openDevice: open failed (errno={}, path={})", 89 openErrno, devicePath) 90 .c_str()); 91 } 92 else 93 { 94 log<level::DEBUG>("OccCommand::openDevice: open success"); 95 } 96 97 return; 98 } 99 100 void OccCommand::closeDevice() 101 { 102 if (fd >= 0) 103 { 104 log<level::DEBUG>("OccCommand::closeDevice: calling close()"); 105 close(fd); 106 fd = -1; 107 } 108 } 109 110 CmdStatus OccCommand::send(const std::vector<uint8_t>& command, 111 std::vector<uint8_t>& response) 112 { 113 using namespace sdbusplus::org::open_power::OCC::Device::Error; 114 CmdStatus status = CmdStatus::FAILURE; 115 116 response.clear(); 117 118 log<level::DEBUG>("OccCommand::send: calling openDevice()"); 119 openDevice(); 120 121 if (fd < 0) 122 { 123 // OCC is inactive; empty response 124 return CmdStatus::COMM_FAILURE; 125 } 126 127 const uint8_t cmd_type = command[0]; 128 #ifdef TRACE_PACKETS 129 log<level::INFO>( 130 fmt::format("OCC{}: Sending 0x{:02X} command (length={}, {})", 131 occInstance, cmd_type, command.size(), devicePath) 132 .c_str()); 133 dump_hex(command); 134 #else 135 log<level::DEBUG>("OccCommand::send: calling write()"); 136 #endif 137 138 int retries = 1; // Allow a retry if a command fails to get valid response 139 do 140 { 141 auto rc = write(fd, command.data(), command.size()); 142 const int writeErrno = errno; 143 if ((rc < 0) || (rc != (int)command.size())) 144 { 145 log<level::ERR>( 146 fmt::format( 147 "OccCommand::send: write(OCC{}, command:0x{:02X}) failed with errno={} (retries={})", 148 occInstance, cmd_type, writeErrno, retries) 149 .c_str()); 150 status = CmdStatus::COMM_FAILURE; 151 // retry if available 152 continue; 153 } 154 else 155 { 156 log<level::DEBUG>("OccCommand::send: write succeeded"); 157 } 158 159 // Now read the response. This would be the content of occ-sram 160 while (1) 161 { 162 uint8_t data{}; 163 auto len = read(fd, &data, sizeof(data)); 164 const int readErrno = errno; 165 if (len > 0) 166 { 167 response.emplace_back(data); 168 } 169 else if (len < 0 && readErrno == EAGAIN) 170 { 171 // We may have data coming still. 172 // This driver does not need a sleep for a retry. 173 continue; 174 } 175 else if (len == 0) 176 { 177 log<level::DEBUG>("OccCommand::send: read completed"); 178 // We have read all that we can. 179 status = CmdStatus::SUCCESS; 180 break; 181 } 182 else 183 { 184 log<level::ERR>( 185 fmt::format( 186 "OccCommand::send: read(OCC{}, command:0x{:02X}) failed with errno={} (rspSize={}, retries={})", 187 occInstance, cmd_type, readErrno, response.size(), 188 retries) 189 .c_str()); 190 status = CmdStatus::COMM_FAILURE; 191 break; 192 } 193 } 194 if (status != CmdStatus::SUCCESS) 195 { 196 // retry if available 197 continue; 198 } 199 200 if (response.size() > 2) 201 { 202 #ifdef TRACE_PACKETS 203 log<level::INFO>( 204 fmt::format( 205 "OCC{}: Received 0x{:02X} response (length={} w/checksum)", 206 occInstance, cmd_type, response.size()) 207 .c_str()); 208 dump_hex(response, 64); 209 #endif 210 211 // Validate checksum (last 2 bytes of response) 212 const unsigned int csumIndex = response.size() - 2; 213 const uint32_t rspChecksum = (response[csumIndex] << 8) + 214 response[csumIndex + 1]; 215 uint32_t calcChecksum = 0; 216 for (unsigned int index = 0; index < csumIndex; ++index) 217 { 218 calcChecksum += response[index]; 219 } 220 while (calcChecksum > 0xFFFF) 221 { 222 calcChecksum = (calcChecksum & 0xFFFF) + (calcChecksum >> 16); 223 } 224 if (calcChecksum != rspChecksum) 225 { 226 log<level::ERR>( 227 fmt::format("OCC{}: Checksum Mismatch: response " 228 "0x{:04X}, calculated 0x{:04X}", 229 occInstance, rspChecksum, calcChecksum) 230 .c_str()); 231 dump_hex(response); 232 status = CmdStatus::COMM_FAILURE; 233 } 234 else 235 { 236 // Validate response was for the specified command 237 if (command[0] == response[1]) 238 { 239 // Valid response received 240 241 // Strip off 2 byte checksum 242 response.pop_back(); 243 response.pop_back(); 244 break; 245 } 246 else 247 { 248 log<level::ERR>( 249 fmt::format( 250 "OccCommand::send: Response command mismatch " 251 "(sent: " 252 "0x{:02X}, rsp: 0x{:02X}, rsp seq#: 0x{:02X}", 253 command[0], response[1], response[0]) 254 .c_str()); 255 dump_hex(response, 64); 256 } 257 } 258 } 259 else 260 { 261 log<level::ERR>( 262 fmt::format( 263 "OccCommand::send: Invalid OCC{} response length: {}", 264 occInstance, response.size()) 265 .c_str()); 266 status = CmdStatus::FAILURE; 267 dump_hex(response); 268 } 269 270 if (retries > 0) 271 { 272 log<level::ERR>("OccCommand::send: Command will be retried"); 273 response.clear(); 274 } 275 } while (retries-- > 0); 276 277 closeDevice(); 278 279 return status; 280 } 281 282 // Called at OCC Status change signal 283 void OccCommand::activeStatusEvent(sdbusplus::message_t& msg) 284 { 285 std::string statusInterface; 286 std::map<std::string, std::variant<bool>> msgData; 287 msg.read(statusInterface, msgData); 288 289 auto propertyMap = msgData.find("OccActive"); 290 if (propertyMap != msgData.end()) 291 { 292 // Extract the OccActive property 293 if (std::get<bool>(propertyMap->second)) 294 { 295 occActive = true; 296 } 297 else 298 { 299 occActive = false; 300 301 this->closeDevice(); 302 } 303 } 304 return; 305 } 306 307 } // namespace occ 308 } // namespace open_power 309