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/file_io.h> 8 #include <stdint.h> 9 #include <systemd/sd-bus.h> 10 #include <unistd.h> 11 12 #include <phosphor-logging/lg2.hpp> 13 #include <sdbusplus/server.hpp> 14 #include <xyz/openbmc_project/Logging/Entry/server.hpp> 15 16 #include <exception> 17 #include <filesystem> 18 #include <fstream> 19 #include <iostream> 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 30 namespace detail 31 { 32 /** 33 * @brief Finds the Entry::Level value for the severity of the PEL 34 * passed in. 35 * 36 * The severity byte is at offset 10 in the User Header section, 37 * which is always after the 48 byte Private Header section. 38 * 39 * @param[in] pelFileName - The file containing the PEL 40 * 41 * @return Entry::Level - The severity value for the Entry 42 */ 43 Entry::Level getEntryLevelFromPEL(const std::string& pelFileName) 44 { 45 const std::map<uint8_t, Entry::Level> severityMap{ 46 {0x00, Entry::Level::Informational}, // Informational event 47 {0x10, Entry::Level::Warning}, // Recoverable error 48 {0x20, Entry::Level::Warning}, // Predictive error 49 {0x40, Entry::Level::Error}, // Unrecoverable error 50 {0x50, Entry::Level::Error}, // Critical error 51 {0x60, Entry::Level::Error}, // Error from a diagnostic test 52 {0x70, Entry::Level::Warning} // Recoverable symptom 53 }; 54 55 const size_t severityOffset = 0x3A; 56 57 size_t size = 0; 58 if (fs::exists(pelFileName)) 59 { 60 size = fs::file_size(pelFileName); 61 } 62 63 if (size > severityOffset) 64 { 65 std::ifstream pel{pelFileName}; 66 if (pel.good()) 67 { 68 pel.seekg(severityOffset); 69 70 uint8_t sev; 71 pel.read(reinterpret_cast<char*>(&sev), 1); 72 73 // Get the type 74 sev = sev & 0xF0; 75 76 auto entry = severityMap.find(sev); 77 if (entry != severityMap.end()) 78 { 79 return entry->second; 80 } 81 } 82 else 83 { 84 error("Unable to open PEL file {PEL_FILE_NAME}", "PEL_FILE_NAME", 85 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( 110 method, 111 std::chrono::duration_cast<microsec>(sec(DBUS_TIMEOUT)).count()); 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( 143 method, 144 std::chrono::duration_cast<microsec>(sec(DBUS_TIMEOUT)).count()); 145 sdbusplus::message::unix_fd fd{}; 146 reply.read(fd); 147 148 off_t fileSize = lseek(fd, 0, SEEK_END); 149 if (fileSize == -1) 150 { 151 error("file seek failed"); 152 return PLDM_ERROR; 153 } 154 if (offset >= fileSize) 155 { 156 error( 157 "Offset exceeds file size, OFFSET={OFFSET} FILE_SIZE={FILE_SIZE} FILE_HANDLE{FILE_HANDLE}", 158 "OFFSET", offset, "FILE_SIZE", fileSize, "FILE_HANDLE", 159 fileHandle); 160 return PLDM_DATA_OUT_OF_RANGE; 161 } 162 if (offset + length > fileSize) 163 { 164 length = fileSize - offset; 165 } 166 auto rc = lseek(fd, offset, SEEK_SET); 167 if (rc == -1) 168 { 169 error("file seek failed"); 170 return PLDM_ERROR; 171 } 172 size_t currSize = response.size(); 173 response.resize(currSize + length); 174 auto filePos = reinterpret_cast<char*>(response.data()); 175 filePos += currSize; 176 rc = ::read(fd, filePos, length); 177 if (rc == -1) 178 { 179 error("file read failed"); 180 return PLDM_ERROR; 181 } 182 if (rc != length) 183 { 184 error( 185 "mismatch between number of characters to read and the length read, LENGTH={LEN} COUNT={CNT}", 186 "LEN", length, "CNT", rc); 187 return PLDM_ERROR; 188 } 189 } 190 catch (const std::exception& e) 191 { 192 error( 193 "GetPEL D-Bus call failed on PEL ID 0x{FILE_HANDLE}, error ={ERR_EXCEP}", 194 "FILE_HANDLE", lg2::hex, fileHandle, "ERR_EXCEP", e.what()); 195 return PLDM_ERROR; 196 } 197 return PLDM_SUCCESS; 198 } 199 200 int PelHandler::writeFromMemory(uint32_t offset, uint32_t length, 201 uint64_t address, 202 oem_platform::Handler* /*oemPlatformHandler*/) 203 { 204 char tmpFile[] = "/tmp/pel.XXXXXX"; 205 int fd = mkstemp(tmpFile); 206 if (fd == -1) 207 { 208 error("failed to create a temporary pel, ERROR={ERR}", "ERR", errno); 209 return PLDM_ERROR; 210 } 211 close(fd); 212 fs::path path(tmpFile); 213 214 auto rc = transferFileData(path, false, offset, length, address); 215 if (rc == PLDM_SUCCESS) 216 { 217 rc = storePel(path.string()); 218 } 219 return rc; 220 } 221 222 int PelHandler::fileAck(uint8_t /*fileStatus*/) 223 { 224 static constexpr auto logObjPath = "/xyz/openbmc_project/logging"; 225 static constexpr auto logInterface = "org.open_power.Logging.PEL"; 226 auto& bus = pldm::utils::DBusHandler::getBus(); 227 228 try 229 { 230 auto service = pldm::utils::DBusHandler().getService(logObjPath, 231 logInterface); 232 auto method = bus.new_method_call(service.c_str(), logObjPath, 233 logInterface, "HostAck"); 234 method.append(fileHandle); 235 bus.call_noreply(method); 236 } 237 catch (const std::exception& e) 238 { 239 error( 240 "HostAck D-Bus call failed on PEL ID 0x{FILE_HANDLE}, error ={ERR_EXCEP}", 241 "FILE_HANDLE", lg2::hex, fileHandle, "ERR_EXCEP", e.what()); 242 return PLDM_ERROR; 243 } 244 245 return PLDM_SUCCESS; 246 } 247 248 int PelHandler::storePel(std::string&& pelFileName) 249 { 250 static constexpr auto logObjPath = "/xyz/openbmc_project/logging"; 251 static constexpr auto logInterface = "xyz.openbmc_project.Logging.Create"; 252 253 auto& bus = pldm::utils::DBusHandler::getBus(); 254 255 try 256 { 257 auto service = pldm::utils::DBusHandler().getService(logObjPath, 258 logInterface); 259 using namespace sdbusplus::xyz::openbmc_project::Logging::server; 260 std::map<std::string, std::string> addlData{}; 261 auto severity = 262 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage( 263 detail::getEntryLevelFromPEL(pelFileName)); 264 addlData.emplace("RAWPEL", std::move(pelFileName)); 265 266 auto method = bus.new_method_call(service.c_str(), logObjPath, 267 logInterface, "Create"); 268 method.append("xyz.openbmc_project.Host.Error.Event", severity, 269 addlData); 270 bus.call_noreply(method); 271 } 272 catch (const std::exception& e) 273 { 274 error( 275 "failed to make a d-bus call to PEL daemon, PEL_FILE_NAME={PEL_FILE_NAME), ERROR={ERR_EXCEP}", 276 "PEL_FILE_NAME", pelFileName, "ERR_EXCEP", e.what()); 277 return PLDM_ERROR; 278 } 279 280 return PLDM_SUCCESS; 281 } 282 283 int PelHandler::write(const char* buffer, uint32_t offset, uint32_t& length, 284 oem_platform::Handler* /*oemPlatformHandler*/) 285 { 286 int rc = PLDM_SUCCESS; 287 288 if (offset > 0) 289 { 290 error("Offset is non zero"); 291 return PLDM_ERROR; 292 } 293 294 char tmpFile[] = "/tmp/pel.XXXXXX"; 295 auto fd = mkstemp(tmpFile); 296 if (fd == -1) 297 { 298 error("failed to create a temporary pel, ERROR={ERR}", "ERR", errno); 299 return PLDM_ERROR; 300 } 301 302 size_t written = 0; 303 do 304 { 305 if ((rc = ::write(fd, buffer, length - written)) == -1) 306 { 307 break; 308 } 309 written += rc; 310 buffer += rc; 311 } while (rc && written < length); 312 close(fd); 313 314 if (rc == -1) 315 { 316 error("file write failed, ERROR={ERR}, LENGTH={LEN}, OFFSET={OFFSET}", 317 "ERR", errno, "LEN", length, "OFFSET", offset); 318 fs::remove(tmpFile); 319 return PLDM_ERROR; 320 } 321 322 if (written == length) 323 { 324 fs::path path(tmpFile); 325 rc = storePel(path.string()); 326 if (rc != PLDM_SUCCESS) 327 { 328 error("save PEL failed, ERROR = {RC} tmpFile = {TMP_FILE}", "RC", 329 rc, "TMP_FILE", tmpFile); 330 } 331 } 332 333 return rc; 334 } 335 336 } // namespace responder 337 } // namespace pldm 338