1 #include "config.h" 2 3 #include "file_io_type_pel.hpp" 4 5 #include "common/utils.hpp" 6 #include "xyz/openbmc_project/Common/error.hpp" 7 8 #include <libpldm/base.h> 9 #include <libpldm/file_io.h> 10 #include <stdint.h> 11 #include <systemd/sd-bus.h> 12 #include <unistd.h> 13 14 #include <phosphor-logging/lg2.hpp> 15 #include <sdbusplus/server.hpp> 16 #include <xyz/openbmc_project/Logging/Entry/server.hpp> 17 18 #include <exception> 19 #include <filesystem> 20 #include <fstream> 21 #include <iostream> 22 #include <vector> 23 24 PHOSPHOR_LOG2_USING; 25 26 namespace pldm 27 { 28 namespace responder 29 { 30 using namespace sdbusplus::xyz::openbmc_project::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); 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); 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}", 156 "OFFSET", offset, "FILE_SIZE", fileSize); 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 auto& bus = pldm::utils::DBusHandler::getBus(); 224 225 try 226 { 227 auto service = pldm::utils::DBusHandler().getService(logObjPath, 228 logInterface); 229 auto method = bus.new_method_call(service.c_str(), logObjPath, 230 logInterface, "HostAck"); 231 method.append(fileHandle); 232 bus.call_noreply(method); 233 } 234 catch (const std::exception& e) 235 { 236 error( 237 "HostAck D-Bus call failed on PEL ID 0x{FILE_HANDLE}, error ={ERR_EXCEP}", 238 "FILE_HANDLE", lg2::hex, fileHandle, "ERR_EXCEP", e.what()); 239 return PLDM_ERROR; 240 } 241 242 return PLDM_SUCCESS; 243 } 244 245 int PelHandler::storePel(std::string&& pelFileName) 246 { 247 static constexpr auto logObjPath = "/xyz/openbmc_project/logging"; 248 static constexpr auto logInterface = "xyz.openbmc_project.Logging.Create"; 249 250 auto& bus = pldm::utils::DBusHandler::getBus(); 251 252 try 253 { 254 auto service = pldm::utils::DBusHandler().getService(logObjPath, 255 logInterface); 256 using namespace sdbusplus::xyz::openbmc_project::Logging::server; 257 std::map<std::string, std::string> addlData{}; 258 auto severity = 259 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage( 260 detail::getEntryLevelFromPEL(pelFileName)); 261 addlData.emplace("RAWPEL", std::move(pelFileName)); 262 263 auto method = bus.new_method_call(service.c_str(), logObjPath, 264 logInterface, "Create"); 265 method.append("xyz.openbmc_project.Host.Error.Event", severity, 266 addlData); 267 bus.call_noreply(method); 268 } 269 catch (const std::exception& e) 270 { 271 error("failed to make a d-bus call to PEL daemon, ERROR={ERR_EXCEP}", 272 "ERR_EXCEP", e.what()); 273 return PLDM_ERROR; 274 } 275 276 return PLDM_SUCCESS; 277 } 278 279 int PelHandler::write(const char* buffer, uint32_t offset, uint32_t& length, 280 oem_platform::Handler* /*oemPlatformHandler*/) 281 { 282 int rc = PLDM_SUCCESS; 283 284 if (offset > 0) 285 { 286 error("Offset is non zero"); 287 return PLDM_ERROR; 288 } 289 290 char tmpFile[] = "/tmp/pel.XXXXXX"; 291 auto fd = mkstemp(tmpFile); 292 if (fd == -1) 293 { 294 error("failed to create a temporary pel, ERROR={ERR}", "ERR", errno); 295 return PLDM_ERROR; 296 } 297 298 size_t written = 0; 299 do 300 { 301 if ((rc = ::write(fd, buffer, length - written)) == -1) 302 { 303 break; 304 } 305 written += rc; 306 buffer += rc; 307 } while (rc && written < length); 308 close(fd); 309 310 if (rc == -1) 311 { 312 error("file write failed, ERROR={ERR}, LENGTH={LEN}, OFFSET={OFFSET}", 313 "ERR", errno, "LEN", length, "OFFSET", offset); 314 fs::remove(tmpFile); 315 return PLDM_ERROR; 316 } 317 318 if (written == length) 319 { 320 fs::path path(tmpFile); 321 rc = storePel(path.string()); 322 if (rc != PLDM_SUCCESS) 323 { 324 error("save PEL failed, ERROR = {RC} tmpFile = {TMP_FILE}", "RC", 325 rc, "TMP_FILE", tmpFile); 326 } 327 } 328 329 return rc; 330 } 331 332 } // namespace responder 333 } // namespace pldm 334