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