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