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