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 '{FILE}'", "FILE", pelFileName); 86 } 87 } 88 89 return Entry::Level::Error; 90 } 91 } // namespace detail 92 93 int PelHandler::readIntoMemory(uint32_t offset, uint32_t length, 94 uint64_t address, 95 oem_platform::Handler* /*oemPlatformHandler*/) 96 { 97 static constexpr auto logObjPath = "/xyz/openbmc_project/logging"; 98 static constexpr auto logInterface = "org.open_power.Logging.PEL"; 99 100 auto& bus = pldm::utils::DBusHandler::getBus(); 101 102 try 103 { 104 auto service = pldm::utils::DBusHandler().getService(logObjPath, 105 logInterface); 106 auto method = bus.new_method_call(service.c_str(), logObjPath, 107 logInterface, "GetPEL"); 108 method.append(fileHandle); 109 auto reply = bus.call(method, dbusTimeout); 110 sdbusplus::message::unix_fd fd{}; 111 reply.read(fd); 112 auto rc = transferFileData(fd, true, offset, length, address); 113 return rc; 114 } 115 catch (const std::exception& e) 116 { 117 error( 118 "Failed to get PEL D-Bus call for PEL ID '{FILE_HANDLE}', error - {ERROR}", 119 "FILE_HANDLE", lg2::hex, fileHandle, "ERROR", e); 120 return PLDM_ERROR; 121 } 122 123 return PLDM_SUCCESS; 124 } 125 126 int PelHandler::read(uint32_t offset, uint32_t& length, Response& response, 127 oem_platform::Handler* /*oemPlatformHandler*/) 128 { 129 static constexpr auto logObjPath = "/xyz/openbmc_project/logging"; 130 static constexpr auto logInterface = "org.open_power.Logging.PEL"; 131 auto& bus = pldm::utils::DBusHandler::getBus(); 132 133 try 134 { 135 auto service = pldm::utils::DBusHandler().getService(logObjPath, 136 logInterface); 137 auto method = bus.new_method_call(service.c_str(), logObjPath, 138 logInterface, "GetPEL"); 139 method.append(fileHandle); 140 auto reply = bus.call(method, dbusTimeout); 141 sdbusplus::message::unix_fd fd{}; 142 reply.read(fd); 143 144 off_t fileSize = lseek(fd, 0, SEEK_END); 145 if (fileSize == -1) 146 { 147 error("File lseek failed"); 148 return PLDM_ERROR; 149 } 150 if (offset >= fileSize) 151 { 152 error( 153 "Offset '{OFFSET}' exceeds file size {SIZE}, file handle {FILE_HANDLE}", 154 "OFFSET", offset, "SIZE", fileSize, "FILE_HANDLE", fileHandle); 155 return PLDM_DATA_OUT_OF_RANGE; 156 } 157 if (offset + length > fileSize) 158 { 159 length = fileSize - offset; 160 } 161 auto rc = lseek(fd, offset, SEEK_SET); 162 if (rc == -1) 163 { 164 error("Failed to do file lseek at offset '{OFFSET}'", "OFFSET", 165 offset); 166 return PLDM_ERROR; 167 } 168 size_t currSize = response.size(); 169 response.resize(currSize + length); 170 auto filePos = reinterpret_cast<char*>(response.data()); 171 filePos += currSize; 172 rc = ::read(fd, filePos, length); 173 if (rc == -1) 174 { 175 error( 176 "Failed to do file read of length '{LENGTH}' at offset '{OFFSET}'", 177 "LENGTH", length, "OFFSET", filePos); 178 return PLDM_ERROR; 179 } 180 if (rc != length) 181 { 182 error( 183 "Mismatch between number of characters to read and the read length '{LENGTH}' and count '{RC}'", 184 "LENGTH", length, "RC", rc); 185 return PLDM_ERROR; 186 } 187 } 188 catch (const std::exception& e) 189 { 190 error( 191 "Failed to get PEL D-Bus call on PEL ID {FILE_HANDLE}, error - {ERROR}", 192 "FILE_HANDLE", lg2::hex, fileHandle, "ERROR", e); 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 number - {ERROR_NUM}", 207 "ERROR_NUM", errno); 208 return PLDM_ERROR; 209 } 210 close(fd); 211 fs::path path(tmpFile); 212 213 auto rc = transferFileData(path, false, offset, length, address); 214 if (rc == PLDM_SUCCESS) 215 { 216 rc = storePel(path.string()); 217 } 218 return rc; 219 } 220 221 int PelHandler::fileAck(uint8_t fileStatus) 222 { 223 static constexpr auto logObjPath = "/xyz/openbmc_project/logging"; 224 static constexpr auto logInterface = "org.open_power.Logging.PEL"; 225 static std::string service; 226 auto& bus = pldm::utils::DBusHandler::getBus(); 227 228 if (service.empty()) 229 { 230 try 231 { 232 service = pldm::utils::DBusHandler().getService(logObjPath, 233 logInterface); 234 } 235 catch (const sdbusplus::exception_t& e) 236 { 237 error( 238 "Failed to do mapper call when trying to find logging service " 239 "to ack PEL ID '{FILE_HANDLE}', error - {ERROR}", 240 "FILE_HANDLE", lg2::hex, fileHandle, "ERROR", e); 241 return PLDM_ERROR; 242 } 243 } 244 245 if (fileStatus == PLDM_SUCCESS) 246 { 247 try 248 { 249 auto method = bus.new_method_call(service.c_str(), logObjPath, 250 logInterface, "HostAck"); 251 method.append(fileHandle); 252 bus.call_noreply(method, dbusTimeout); 253 } 254 catch (const std::exception& e) 255 { 256 error( 257 "Failure in HostReject ack D-Bus call on PEL ID '{FILE_HANDLE}', error - {ERROR}", 258 "FILE_HANDLE", lg2::hex, fileHandle, "ERROR", e); 259 return PLDM_ERROR; 260 } 261 } 262 else 263 { 264 PEL::RejectionReason reason{}; 265 if (fileStatus == PLDM_FULL_FILE_DISCARDED) 266 { 267 reason = PEL::RejectionReason::HostFull; 268 } 269 else if (fileStatus == PLDM_ERROR_FILE_DISCARDED) 270 { 271 reason = PEL::RejectionReason::BadPEL; 272 } 273 else 274 { 275 error( 276 "Invalid file status '{STATUS}' in PEL file ack response for PEL '{FILE_HANDLE}'", 277 "STATUS", lg2::hex, fileStatus, "FILE_HANDLE", lg2::hex, 278 fileHandle); 279 return PLDM_ERROR; 280 } 281 282 try 283 { 284 auto method = bus.new_method_call(service.c_str(), logObjPath, 285 logInterface, "HostReject"); 286 method.append(fileHandle, reason); 287 bus.call_noreply(method, dbusTimeout); 288 } 289 catch (const std::exception& e) 290 { 291 error("Failure in HostReject D-Bus call on PEL ID '{FILE_HANDLE}', " 292 "error - {ERROR}, status - {STATUS}", 293 "FILE_HANDLE", lg2::hex, fileHandle, "ERROR", e, "STATUS", 294 lg2::hex, fileStatus); 295 return PLDM_ERROR; 296 } 297 } 298 299 return PLDM_SUCCESS; 300 } 301 302 int PelHandler::storePel(std::string&& pelFileName) 303 { 304 static constexpr auto logObjPath = "/xyz/openbmc_project/logging"; 305 static constexpr auto logInterface = "xyz.openbmc_project.Logging.Create"; 306 307 auto& bus = pldm::utils::DBusHandler::getBus(); 308 309 try 310 { 311 auto service = pldm::utils::DBusHandler().getService(logObjPath, 312 logInterface); 313 using namespace sdbusplus::xyz::openbmc_project::Logging::server; 314 std::map<std::string, std::string> addlData{}; 315 auto severity = 316 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage( 317 detail::getEntryLevelFromPEL(pelFileName)); 318 addlData.emplace("RAWPEL", std::move(pelFileName)); 319 320 auto method = bus.new_method_call(service.c_str(), logObjPath, 321 logInterface, "Create"); 322 method.append("xyz.openbmc_project.Host.Error.Event", severity, 323 addlData); 324 bus.call_noreply(method, dbusTimeout); 325 } 326 catch (const std::exception& e) 327 { 328 error( 329 "Failed to make a d-bus call to PEL daemon, PEL file name '{FILE}', ERROR - {ERROR}", 330 "FILE", pelFileName, "ERROR", e); 331 return PLDM_ERROR; 332 } 333 334 return PLDM_SUCCESS; 335 } 336 337 int PelHandler::write(const char* buffer, uint32_t offset, uint32_t& length, 338 oem_platform::Handler* /*oemPlatformHandler*/) 339 { 340 int rc = PLDM_SUCCESS; 341 342 if (offset > 0) 343 { 344 error("Offset '{OFFSET}' is non zero", "OFFSET", offset); 345 return PLDM_ERROR; 346 } 347 348 char tmpFile[] = "/tmp/pel.XXXXXX"; 349 auto fd = mkstemp(tmpFile); 350 if (fd == -1) 351 { 352 error("Failed to create a temporary PEL, error number - {ERROR_NUM}", 353 "ERROR_NUM", errno); 354 return PLDM_ERROR; 355 } 356 357 size_t written = 0; 358 do 359 { 360 if ((rc = ::write(fd, buffer, length - written)) == -1) 361 { 362 break; 363 } 364 written += rc; 365 buffer += rc; 366 } while (rc && written < length); 367 close(fd); 368 369 if (rc == -1) 370 { 371 error( 372 "Failed to do file write of length '{LENGTH}' at offset '{OFFSET}', error number - {ERROR_NUM}", 373 "LENGTH", length, "OFFSET", offset, "ERROR_NUM", errno); 374 fs::remove(tmpFile); 375 return PLDM_ERROR; 376 } 377 378 if (written == length) 379 { 380 fs::path path(tmpFile); 381 rc = storePel(path.string()); 382 if (rc != PLDM_SUCCESS) 383 { 384 error( 385 "Failed to save PEL in temp file '{FILE}', response code '{RC}'", 386 "RC", rc, "FILE", tmpFile); 387 } 388 } 389 390 return rc; 391 } 392 393 } // namespace responder 394 } // namespace pldm 395