1 #include "file_io_type_pel.hpp" 2 3 #include "common/utils.hpp" 4 #include "xyz/openbmc_project/Common/error.hpp" 5 6 #include <libpldm/base.h> 7 #include <libpldm/oem/ibm/file_io.h> 8 #include <stdint.h> 9 #include <systemd/sd-bus.h> 10 #include <unistd.h> 11 12 #include <org/open_power/Logging/PEL/server.hpp> 13 #include <phosphor-logging/lg2.hpp> 14 #include <sdbusplus/server.hpp> 15 #include <xyz/openbmc_project/Logging/Entry/server.hpp> 16 17 #include <exception> 18 #include <filesystem> 19 #include <fstream> 20 #include <vector> 21 22 PHOSPHOR_LOG2_USING; 23 24 namespace pldm 25 { 26 namespace responder 27 { 28 using namespace sdbusplus::xyz::openbmc_project::Logging::server; 29 using namespace sdbusplus::org::open_power::Logging::server; 30 31 namespace detail 32 { 33 /** 34 * @brief Finds the Entry::Level value for the severity of the PEL 35 * passed in. 36 * 37 * The severity byte is at offset 10 in the User Header section, 38 * which is always after the 48 byte Private Header section. 39 * 40 * @param[in] pelFileName - The file containing the PEL 41 * 42 * @return Entry::Level - The severity value for the Entry 43 */ 44 Entry::Level getEntryLevelFromPEL(const std::string& pelFileName) 45 { 46 const std::map<uint8_t, Entry::Level> severityMap{ 47 {0x00, Entry::Level::Informational}, // Informational event 48 {0x10, Entry::Level::Warning}, // Recoverable error 49 {0x20, Entry::Level::Warning}, // Predictive error 50 {0x40, Entry::Level::Error}, // Unrecoverable error 51 {0x50, Entry::Level::Error}, // Critical error 52 {0x60, Entry::Level::Error}, // Error from a diagnostic test 53 {0x70, Entry::Level::Warning} // Recoverable symptom 54 }; 55 56 const size_t severityOffset = 0x3A; 57 58 size_t size = 0; 59 if (fs::exists(pelFileName)) 60 { 61 size = fs::file_size(pelFileName); 62 } 63 64 if (size > severityOffset) 65 { 66 std::ifstream pel{pelFileName}; 67 if (pel.good()) 68 { 69 pel.seekg(severityOffset); 70 71 uint8_t sev; 72 pel.read(reinterpret_cast<char*>(&sev), 1); 73 74 // Get the type 75 sev = sev & 0xF0; 76 77 auto entry = severityMap.find(sev); 78 if (entry != severityMap.end()) 79 { 80 return entry->second; 81 } 82 } 83 else 84 { 85 error("Unable to open PEL file {PEL_FILE_NAME}", "PEL_FILE_NAME", 86 pelFileName); 87 } 88 } 89 90 return Entry::Level::Error; 91 } 92 } // namespace detail 93 94 int PelHandler::readIntoMemory(uint32_t offset, uint32_t& length, 95 uint64_t address, 96 oem_platform::Handler* /*oemPlatformHandler*/) 97 { 98 static constexpr auto logObjPath = "/xyz/openbmc_project/logging"; 99 static constexpr auto logInterface = "org.open_power.Logging.PEL"; 100 101 auto& bus = pldm::utils::DBusHandler::getBus(); 102 103 try 104 { 105 auto service = pldm::utils::DBusHandler().getService(logObjPath, 106 logInterface); 107 auto method = bus.new_method_call(service.c_str(), logObjPath, 108 logInterface, "GetPEL"); 109 method.append(fileHandle); 110 auto reply = bus.call(method, dbusTimeout); 111 sdbusplus::message::unix_fd fd{}; 112 reply.read(fd); 113 auto rc = transferFileData(fd, true, offset, length, address); 114 return rc; 115 } 116 catch (const std::exception& e) 117 { 118 error( 119 "GetPEL D-Bus call failed, PEL id = 0x{FILE_HANDLE}, error ={ERR_EXCEP}", 120 "FILE_HANDLE", lg2::hex, fileHandle, "ERR_EXCEP", e.what()); 121 return PLDM_ERROR; 122 } 123 124 return PLDM_SUCCESS; 125 } 126 127 int PelHandler::read(uint32_t offset, uint32_t& length, Response& response, 128 oem_platform::Handler* /*oemPlatformHandler*/) 129 { 130 static constexpr auto logObjPath = "/xyz/openbmc_project/logging"; 131 static constexpr auto logInterface = "org.open_power.Logging.PEL"; 132 auto& bus = pldm::utils::DBusHandler::getBus(); 133 134 try 135 { 136 auto service = pldm::utils::DBusHandler().getService(logObjPath, 137 logInterface); 138 auto method = bus.new_method_call(service.c_str(), logObjPath, 139 logInterface, "GetPEL"); 140 method.append(fileHandle); 141 auto reply = bus.call(method, dbusTimeout); 142 sdbusplus::message::unix_fd fd{}; 143 reply.read(fd); 144 145 off_t fileSize = lseek(fd, 0, SEEK_END); 146 if (fileSize == -1) 147 { 148 error("file seek failed"); 149 return PLDM_ERROR; 150 } 151 if (offset >= fileSize) 152 { 153 error( 154 "Offset exceeds file size, OFFSET={OFFSET} FILE_SIZE={FILE_SIZE} FILE_HANDLE{FILE_HANDLE}", 155 "OFFSET", offset, "FILE_SIZE", fileSize, "FILE_HANDLE", 156 fileHandle); 157 return PLDM_DATA_OUT_OF_RANGE; 158 } 159 if (offset + length > fileSize) 160 { 161 length = fileSize - offset; 162 } 163 auto rc = lseek(fd, offset, SEEK_SET); 164 if (rc == -1) 165 { 166 error("file seek failed"); 167 return PLDM_ERROR; 168 } 169 size_t currSize = response.size(); 170 response.resize(currSize + length); 171 auto filePos = reinterpret_cast<char*>(response.data()); 172 filePos += currSize; 173 rc = ::read(fd, filePos, length); 174 if (rc == -1) 175 { 176 error("file read failed"); 177 return PLDM_ERROR; 178 } 179 if (rc != length) 180 { 181 error( 182 "mismatch between number of characters to read and the length read, LENGTH={LEN} COUNT={CNT}", 183 "LEN", length, "CNT", rc); 184 return PLDM_ERROR; 185 } 186 } 187 catch (const std::exception& e) 188 { 189 error( 190 "GetPEL D-Bus call failed on PEL ID 0x{FILE_HANDLE}, error ={ERR_EXCEP}", 191 "FILE_HANDLE", lg2::hex, fileHandle, "ERR_EXCEP", e.what()); 192 return PLDM_ERROR; 193 } 194 return PLDM_SUCCESS; 195 } 196 197 int PelHandler::writeFromMemory(uint32_t offset, uint32_t length, 198 uint64_t address, 199 oem_platform::Handler* /*oemPlatformHandler*/) 200 { 201 char tmpFile[] = "/tmp/pel.XXXXXX"; 202 int fd = mkstemp(tmpFile); 203 if (fd == -1) 204 { 205 error("failed to create a temporary pel, ERROR={ERR}", "ERR", errno); 206 return PLDM_ERROR; 207 } 208 close(fd); 209 fs::path path(tmpFile); 210 211 auto rc = transferFileData(path, false, offset, length, address); 212 if (rc == PLDM_SUCCESS) 213 { 214 rc = storePel(path.string()); 215 } 216 return rc; 217 } 218 219 int PelHandler::fileAck(uint8_t fileStatus) 220 { 221 static constexpr auto logObjPath = "/xyz/openbmc_project/logging"; 222 static constexpr auto logInterface = "org.open_power.Logging.PEL"; 223 static std::string service; 224 auto& bus = pldm::utils::DBusHandler::getBus(); 225 226 if (service.empty()) 227 { 228 try 229 { 230 service = pldm::utils::DBusHandler().getService(logObjPath, 231 logInterface); 232 } 233 catch (const sdbusplus::exception_t& e) 234 { 235 error("Mapper call failed when trying to find logging service " 236 "to ack PEL ID {FILE_HANDLE} error = {ERR_EXCEP}", 237 "FILE_HANDLE", lg2::hex, fileHandle, "ERR_EXCEP", e); 238 return PLDM_ERROR; 239 } 240 } 241 242 if (fileStatus == PLDM_SUCCESS) 243 { 244 try 245 { 246 auto method = bus.new_method_call(service.c_str(), logObjPath, 247 logInterface, "HostAck"); 248 method.append(fileHandle); 249 bus.call_noreply(method, dbusTimeout); 250 } 251 catch (const std::exception& e) 252 { 253 error( 254 "HostAck D-Bus call failed on PEL ID {FILE_HANDLE}, error = {ERR_EXCEP}", 255 "FILE_HANDLE", lg2::hex, fileHandle, "ERR_EXCEP", e); 256 return PLDM_ERROR; 257 } 258 } 259 else 260 { 261 PEL::RejectionReason reason{}; 262 if (fileStatus == PLDM_FULL_FILE_DISCARDED) 263 { 264 reason = PEL::RejectionReason::HostFull; 265 } 266 else if (fileStatus == PLDM_ERROR_FILE_DISCARDED) 267 { 268 reason = PEL::RejectionReason::BadPEL; 269 } 270 else 271 { 272 error( 273 "Invalid file status {STATUS} in PEL file ack response for PEL {FILE_HANDLE}", 274 "STATUS", lg2::hex, fileStatus, "FILE_HANDLE", lg2::hex, 275 fileHandle); 276 return PLDM_ERROR; 277 } 278 279 try 280 { 281 auto method = bus.new_method_call(service.c_str(), logObjPath, 282 logInterface, "HostReject"); 283 method.append(fileHandle, reason); 284 bus.call_noreply(method, dbusTimeout); 285 } 286 catch (const std::exception& e) 287 { 288 error("HostReject D-Bus call failed on PEL ID {FILE_HANDLE}, " 289 "error = {ERR_EXCEP}, status = {STATUS}", 290 "FILE_HANDLE", lg2::hex, fileHandle, "ERR_EXCEP", e, "STATUS", 291 lg2::hex, fileStatus); 292 return PLDM_ERROR; 293 } 294 } 295 296 return PLDM_SUCCESS; 297 } 298 299 int PelHandler::storePel(std::string&& pelFileName) 300 { 301 static constexpr auto logObjPath = "/xyz/openbmc_project/logging"; 302 static constexpr auto logInterface = "xyz.openbmc_project.Logging.Create"; 303 304 auto& bus = pldm::utils::DBusHandler::getBus(); 305 306 try 307 { 308 auto service = pldm::utils::DBusHandler().getService(logObjPath, 309 logInterface); 310 using namespace sdbusplus::xyz::openbmc_project::Logging::server; 311 std::map<std::string, std::string> addlData{}; 312 auto severity = 313 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage( 314 detail::getEntryLevelFromPEL(pelFileName)); 315 addlData.emplace("RAWPEL", std::move(pelFileName)); 316 317 auto method = bus.new_method_call(service.c_str(), logObjPath, 318 logInterface, "Create"); 319 method.append("xyz.openbmc_project.Host.Error.Event", severity, 320 addlData); 321 bus.call_noreply(method, dbusTimeout); 322 } 323 catch (const std::exception& e) 324 { 325 error( 326 "failed to make a d-bus call to PEL daemon, PEL_FILE_NAME={PEL_FILE_NAME), ERROR={ERR_EXCEP}", 327 "PEL_FILE_NAME", pelFileName, "ERR_EXCEP", e.what()); 328 return PLDM_ERROR; 329 } 330 331 return PLDM_SUCCESS; 332 } 333 334 int PelHandler::write(const char* buffer, uint32_t offset, uint32_t& length, 335 oem_platform::Handler* /*oemPlatformHandler*/) 336 { 337 int rc = PLDM_SUCCESS; 338 339 if (offset > 0) 340 { 341 error("Offset is non zero"); 342 return PLDM_ERROR; 343 } 344 345 char tmpFile[] = "/tmp/pel.XXXXXX"; 346 auto fd = mkstemp(tmpFile); 347 if (fd == -1) 348 { 349 error("failed to create a temporary pel, ERROR={ERR}", "ERR", errno); 350 return PLDM_ERROR; 351 } 352 353 size_t written = 0; 354 do 355 { 356 if ((rc = ::write(fd, buffer, length - written)) == -1) 357 { 358 break; 359 } 360 written += rc; 361 buffer += rc; 362 } while (rc && written < length); 363 close(fd); 364 365 if (rc == -1) 366 { 367 error("file write failed, ERROR={ERR}, LENGTH={LEN}, OFFSET={OFFSET}", 368 "ERR", errno, "LEN", length, "OFFSET", offset); 369 fs::remove(tmpFile); 370 return PLDM_ERROR; 371 } 372 373 if (written == length) 374 { 375 fs::path path(tmpFile); 376 rc = storePel(path.string()); 377 if (rc != PLDM_SUCCESS) 378 { 379 error("save PEL failed, ERROR = {RC} tmpFile = {TMP_FILE}", "RC", 380 rc, "TMP_FILE", tmpFile); 381 } 382 } 383 384 return rc; 385 } 386 387 } // namespace responder 388 } // namespace pldm 389