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 <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 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 += 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 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 fmt::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 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 105 void 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 115 CmdStatus 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 142 auto rc = write(fd, command.data(), command.size()); 143 if ((rc < 0) || (rc != (int)command.size())) 144 { 145 const int write_errno = errno; 146 log<level::ERR>("OccCommand::send: write failed"); 147 // This would log and terminate since its not handled. 148 elog<WriteFailure>( 149 phosphor::logging::org::open_power::OCC::Device::WriteFailure:: 150 CALLOUT_ERRNO(write_errno), 151 phosphor::logging::org::open_power::OCC::Device::WriteFailure:: 152 CALLOUT_DEVICE_PATH(devicePath.c_str())); 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 read_errno = errno; 165 if (len > 0) 166 { 167 response.emplace_back(data); 168 } 169 else if (len < 0 && read_errno == 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>("OccCommand::send: read failed"); 185 // This would log and terminate since its not handled. 186 elog<ReadFailure>( 187 phosphor::logging::org::open_power::OCC::Device::ReadFailure:: 188 CALLOUT_ERRNO(read_errno), 189 phosphor::logging::org::open_power::OCC::Device::ReadFailure:: 190 CALLOUT_DEVICE_PATH(devicePath.c_str())); 191 } 192 } 193 194 if (response.size() > 2) 195 { 196 #ifdef TRACE_PACKETS 197 log<level::INFO>( 198 fmt::format( 199 "OCC{}: Received 0x{:02X} response (length={} w/checksum)", 200 occInstance, cmd_type, response.size()) 201 .c_str()); 202 dump_hex(response, 64); 203 #endif 204 205 // Validate checksum (last 2 bytes of response) 206 const unsigned int csumIndex = response.size() - 2; 207 const uint32_t rspChecksum = 208 (response[csumIndex] << 8) + response[csumIndex + 1]; 209 uint32_t calcChecksum = 0; 210 for (unsigned int index = 0; index < csumIndex; ++index) 211 { 212 calcChecksum += response[index]; 213 } 214 while (calcChecksum > 0xFFFF) 215 { 216 calcChecksum = (calcChecksum & 0xFFFF) + (calcChecksum >> 16); 217 } 218 if (calcChecksum != rspChecksum) 219 { 220 log<level::ERR>(fmt::format("OCC{}: Checksum Mismatch: response " 221 "0x{:04X}, calculated 0x{:04X}", 222 occInstance, rspChecksum, calcChecksum) 223 .c_str()); 224 dump_hex(response); 225 status = CmdStatus::INVALID_CHECKSUM; 226 } 227 else 228 { 229 // Strip off 2 byte checksum 230 response.pop_back(); 231 response.pop_back(); 232 } 233 } 234 else 235 { 236 log<level::ERR>( 237 fmt::format("OccCommand::send: Invalid OCC{} response length: {}", 238 occInstance, response.size()) 239 .c_str()); 240 status = CmdStatus::FAILURE; 241 dump_hex(response); 242 } 243 244 closeDevice(); 245 246 return status; 247 } 248 249 // Called at OCC Status change signal 250 void OccCommand::activeStatusEvent(sdbusplus::message::message& msg) 251 { 252 std::string statusInterface; 253 std::map<std::string, std::variant<bool>> msgData; 254 msg.read(statusInterface, msgData); 255 256 auto propertyMap = msgData.find("OccActive"); 257 if (propertyMap != msgData.end()) 258 { 259 // Extract the OccActive property 260 if (std::get<bool>(propertyMap->second)) 261 { 262 occActive = true; 263 } 264 else 265 { 266 occActive = false; 267 268 this->closeDevice(); 269 } 270 } 271 return; 272 } 273 274 } // namespace occ 275 } // namespace open_power 276