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 <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 namespace pldm 24 { 25 namespace responder 26 { 27 28 using namespace sdbusplus::xyz::openbmc_project::Logging::server; 29 30 namespace detail 31 { 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 std::cerr << "Unable to open PEL file " << pelFileName << "\n"; 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 = 105 pldm::utils::DBusHandler().getService(logObjPath, 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); 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 std::cerr << "GetPEL D-Bus call failed, PEL id = 0x" << std::hex 118 << fileHandle << ", error = " << e.what() << "\n"; 119 return PLDM_ERROR; 120 } 121 122 return PLDM_SUCCESS; 123 } 124 125 int PelHandler::read(uint32_t offset, uint32_t& length, Response& response, 126 oem_platform::Handler* /*oemPlatformHandler*/) 127 { 128 static constexpr auto logObjPath = "/xyz/openbmc_project/logging"; 129 static constexpr auto logInterface = "org.open_power.Logging.PEL"; 130 auto& bus = pldm::utils::DBusHandler::getBus(); 131 132 try 133 { 134 auto service = 135 pldm::utils::DBusHandler().getService(logObjPath, logInterface); 136 auto method = bus.new_method_call(service.c_str(), logObjPath, 137 logInterface, "GetPEL"); 138 method.append(fileHandle); 139 auto reply = bus.call(method); 140 sdbusplus::message::unix_fd fd{}; 141 reply.read(fd); 142 143 off_t fileSize = lseek(fd, 0, SEEK_END); 144 if (fileSize == -1) 145 { 146 std::cerr << "file seek failed"; 147 return PLDM_ERROR; 148 } 149 if (offset >= fileSize) 150 { 151 std::cerr << "Offset exceeds file size, OFFSET=" << offset 152 << " FILE_SIZE=" << fileSize << std::endl; 153 return PLDM_DATA_OUT_OF_RANGE; 154 } 155 if (offset + length > fileSize) 156 { 157 length = fileSize - offset; 158 } 159 auto rc = lseek(fd, offset, SEEK_SET); 160 if (rc == -1) 161 { 162 std::cerr << "file seek failed"; 163 return PLDM_ERROR; 164 } 165 size_t currSize = response.size(); 166 response.resize(currSize + length); 167 auto filePos = reinterpret_cast<char*>(response.data()); 168 filePos += currSize; 169 rc = ::read(fd, filePos, length); 170 if (rc == -1) 171 { 172 std::cerr << "file read failed"; 173 return PLDM_ERROR; 174 } 175 if (rc != length) 176 { 177 std::cerr << "mismatch between number of characters to read and " 178 << "the length read, LENGTH=" << length << " COUNT=" << rc 179 << std::endl; 180 return PLDM_ERROR; 181 } 182 } 183 catch (const std::exception& e) 184 { 185 std::cerr << "GetPEL D-Bus call failed on PEL ID 0x" << std::hex 186 << fileHandle << ", error = " << e.what() << "\n"; 187 return PLDM_ERROR; 188 } 189 return PLDM_SUCCESS; 190 } 191 192 int PelHandler::writeFromMemory(uint32_t offset, uint32_t length, 193 uint64_t address, 194 oem_platform::Handler* /*oemPlatformHandler*/) 195 { 196 char tmpFile[] = "/tmp/pel.XXXXXX"; 197 int fd = mkstemp(tmpFile); 198 if (fd == -1) 199 { 200 std::cerr << "failed to create a temporary pel, ERROR=" << errno 201 << "\n"; 202 return PLDM_ERROR; 203 } 204 close(fd); 205 fs::path path(tmpFile); 206 207 auto rc = transferFileData(path, false, offset, length, address); 208 if (rc == PLDM_SUCCESS) 209 { 210 rc = storePel(path.string()); 211 } 212 return rc; 213 } 214 215 int PelHandler::fileAck(uint8_t /*fileStatus*/) 216 { 217 static constexpr auto logObjPath = "/xyz/openbmc_project/logging"; 218 static constexpr auto logInterface = "org.open_power.Logging.PEL"; 219 auto& bus = pldm::utils::DBusHandler::getBus(); 220 221 try 222 { 223 auto service = 224 pldm::utils::DBusHandler().getService(logObjPath, logInterface); 225 auto method = bus.new_method_call(service.c_str(), logObjPath, 226 logInterface, "HostAck"); 227 method.append(fileHandle); 228 bus.call_noreply(method); 229 } 230 catch (const std::exception& e) 231 { 232 std::cerr << "HostAck D-Bus call failed on PEL ID 0x" << std::hex 233 << fileHandle << ", error = " << e.what() << "\n"; 234 return PLDM_ERROR; 235 } 236 237 return PLDM_SUCCESS; 238 } 239 240 int PelHandler::storePel(std::string&& pelFileName) 241 { 242 static constexpr auto logObjPath = "/xyz/openbmc_project/logging"; 243 static constexpr auto logInterface = "xyz.openbmc_project.Logging.Create"; 244 245 auto& bus = pldm::utils::DBusHandler::getBus(); 246 247 try 248 { 249 auto service = 250 pldm::utils::DBusHandler().getService(logObjPath, logInterface); 251 using namespace sdbusplus::xyz::openbmc_project::Logging::server; 252 std::map<std::string, std::string> addlData{}; 253 auto severity = 254 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage( 255 detail::getEntryLevelFromPEL(pelFileName)); 256 addlData.emplace("RAWPEL", std::move(pelFileName)); 257 258 auto method = bus.new_method_call(service.c_str(), logObjPath, 259 logInterface, "Create"); 260 method.append("xyz.openbmc_project.Host.Error.Event", severity, 261 addlData); 262 bus.call_noreply(method); 263 } 264 catch (const std::exception& e) 265 { 266 std::cerr << "failed to make a d-bus call to PEL daemon, ERROR=" 267 << e.what() << "\n"; 268 return PLDM_ERROR; 269 } 270 271 return PLDM_SUCCESS; 272 } 273 274 int PelHandler::write(const char* buffer, uint32_t offset, uint32_t& length, 275 oem_platform::Handler* /*oemPlatformHandler*/) 276 { 277 int rc = PLDM_SUCCESS; 278 279 if (offset > 0) 280 { 281 std::cerr << "Offset is non zero \n"; 282 return PLDM_ERROR; 283 } 284 285 char tmpFile[] = "/tmp/pel.XXXXXX"; 286 auto fd = mkstemp(tmpFile); 287 if (fd == -1) 288 { 289 std::cerr << "failed to create a temporary pel, ERROR=" << errno 290 << "\n"; 291 return PLDM_ERROR; 292 } 293 294 size_t written = 0; 295 do 296 { 297 if ((rc = ::write(fd, buffer, length - written)) == -1) 298 { 299 break; 300 } 301 written += rc; 302 buffer += rc; 303 } while (rc && written < length); 304 close(fd); 305 306 if (rc == -1) 307 { 308 std::cerr << "file write failed, ERROR=" << errno 309 << ", LENGTH=" << length << ", OFFSET=" << offset << "\n"; 310 fs::remove(tmpFile); 311 return PLDM_ERROR; 312 } 313 314 if (written == length) 315 { 316 fs::path path(tmpFile); 317 rc = storePel(path.string()); 318 if (rc != PLDM_SUCCESS) 319 { 320 std::cerr << "save PEL failed, ERROR = " << rc 321 << "tmpFile = " << tmpFile << "\n"; 322 } 323 } 324 325 return rc; 326 } 327 328 } // namespace responder 329 } // namespace pldm 330