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, 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 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 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 106 void 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 116 CmdStatus 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 251 void 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 277