1 #include "config.h" 2 3 #include "file_io_type_pel.hpp" 4 5 #include "libpldm/base.h" 6 #include "oem/ibm/libpldm/file_io.h" 7 8 #include "common/utils.hpp" 9 #include "xyz/openbmc_project/Common/error.hpp" 10 11 #include <stdint.h> 12 #include <systemd/sd-bus.h> 13 #include <unistd.h> 14 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 namespace pldm 25 { 26 namespace responder 27 { 28 29 using namespace sdbusplus::xyz::openbmc_project::Logging::server; 30 31 namespace detail 32 { 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 std::cerr << "Unable to open PEL file " << pelFileName << "\n"; 87 } 88 } 89 90 return Entry::Level::Error; 91 } 92 } // namespace detail 93 94 int PelHandler::readIntoMemory(uint32_t offset, uint32_t& length, 95 uint64_t address, 96 oem_platform::Handler* /*oemPlatformHandler*/) 97 { 98 static constexpr auto logObjPath = "/xyz/openbmc_project/logging"; 99 static constexpr auto logInterface = "org.open_power.Logging.PEL"; 100 101 auto& bus = pldm::utils::DBusHandler::getBus(); 102 103 try 104 { 105 auto service = 106 pldm::utils::DBusHandler().getService(logObjPath, logInterface); 107 auto method = bus.new_method_call(service.c_str(), logObjPath, 108 logInterface, "GetPEL"); 109 method.append(fileHandle); 110 auto reply = bus.call(method); 111 sdbusplus::message::unix_fd fd{}; 112 reply.read(fd); 113 auto rc = transferFileData(fd, true, offset, length, address); 114 return rc; 115 } 116 catch (const std::exception& e) 117 { 118 std::cerr << "GetPEL D-Bus call failed, PEL id = " << fileHandle 119 << ", error = " << e.what() << "\n"; 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 = 136 pldm::utils::DBusHandler().getService(logObjPath, 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); 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 std::cerr << "file seek failed"; 148 return PLDM_ERROR; 149 } 150 if (offset >= fileSize) 151 { 152 std::cerr << "Offset exceeds file size, OFFSET=" << offset 153 << " FILE_SIZE=" << fileSize << std::endl; 154 return PLDM_DATA_OUT_OF_RANGE; 155 } 156 if (offset + length > fileSize) 157 { 158 length = fileSize - offset; 159 } 160 auto rc = lseek(fd, offset, SEEK_SET); 161 if (rc == -1) 162 { 163 std::cerr << "file seek failed"; 164 return PLDM_ERROR; 165 } 166 size_t currSize = response.size(); 167 response.resize(currSize + length); 168 auto filePos = reinterpret_cast<char*>(response.data()); 169 filePos += currSize; 170 rc = ::read(fd, filePos, length); 171 if (rc == -1) 172 { 173 std::cerr << "file read failed"; 174 return PLDM_ERROR; 175 } 176 if (rc != length) 177 { 178 std::cerr << "mismatch between number of characters to read and " 179 << "the length read, LENGTH=" << length << " COUNT=" << rc 180 << std::endl; 181 return PLDM_ERROR; 182 } 183 } 184 catch (const std::exception& e) 185 { 186 std::cerr << "GetPEL D-Bus call failed"; 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"; 233 return PLDM_ERROR; 234 } 235 236 return PLDM_SUCCESS; 237 } 238 239 int PelHandler::storePel(std::string&& pelFileName) 240 { 241 static constexpr auto logObjPath = "/xyz/openbmc_project/logging"; 242 static constexpr auto logInterface = "xyz.openbmc_project.Logging.Create"; 243 244 auto& bus = pldm::utils::DBusHandler::getBus(); 245 246 try 247 { 248 auto service = 249 pldm::utils::DBusHandler().getService(logObjPath, logInterface); 250 using namespace sdbusplus::xyz::openbmc_project::Logging::server; 251 std::map<std::string, std::string> addlData{}; 252 auto severity = 253 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage( 254 detail::getEntryLevelFromPEL(pelFileName)); 255 addlData.emplace("RAWPEL", std::move(pelFileName)); 256 257 auto method = bus.new_method_call(service.c_str(), logObjPath, 258 logInterface, "Create"); 259 method.append("xyz.openbmc_project.Host.Error.Event", severity, 260 addlData); 261 bus.call_noreply(method); 262 } 263 catch (const std::exception& e) 264 { 265 std::cerr << "failed to make a d-bus call to PEL daemon, ERROR=" 266 << e.what() << "\n"; 267 return PLDM_ERROR; 268 } 269 270 return PLDM_SUCCESS; 271 } 272 273 int PelHandler::write(const char* buffer, uint32_t offset, uint32_t& length, 274 oem_platform::Handler* /*oemPlatformHandler*/) 275 { 276 int rc = PLDM_SUCCESS; 277 278 if (offset > 0) 279 { 280 std::cerr << "Offset is non zero \n"; 281 return PLDM_ERROR; 282 } 283 284 char tmpFile[] = "/tmp/pel.XXXXXX"; 285 auto fd = mkstemp(tmpFile); 286 if (fd == -1) 287 { 288 std::cerr << "failed to create a temporary pel, ERROR=" << errno 289 << "\n"; 290 return PLDM_ERROR; 291 } 292 293 size_t written = 0; 294 do 295 { 296 if ((rc = ::write(fd, buffer, length - written)) == -1) 297 { 298 break; 299 } 300 written += rc; 301 buffer += rc; 302 } while (rc && written < length); 303 close(fd); 304 305 if (rc == -1) 306 { 307 std::cerr << "file write failed, ERROR=" << errno 308 << ", LENGTH=" << length << ", OFFSET=" << offset << "\n"; 309 fs::remove(tmpFile); 310 return PLDM_ERROR; 311 } 312 313 if (written == length) 314 { 315 fs::path path(tmpFile); 316 rc = storePel(path.string()); 317 if (rc != PLDM_SUCCESS) 318 { 319 std::cerr << "save PEL failed, ERROR = " << rc 320 << "tmpFile = " << tmpFile << "\n"; 321 } 322 } 323 324 return rc; 325 } 326 327 } // namespace responder 328 } // namespace pldm 329