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 fs::remove(path); 213 return rc; 214 } 215 216 int PelHandler::fileAck(uint8_t /*fileStatus*/) 217 { 218 static constexpr auto logObjPath = "/xyz/openbmc_project/logging"; 219 static constexpr auto logInterface = "org.open_power.Logging.PEL"; 220 auto& bus = pldm::utils::DBusHandler::getBus(); 221 222 try 223 { 224 auto service = 225 pldm::utils::DBusHandler().getService(logObjPath, logInterface); 226 auto method = bus.new_method_call(service.c_str(), logObjPath, 227 logInterface, "HostAck"); 228 method.append(fileHandle); 229 bus.call_noreply(method); 230 } 231 catch (const std::exception& e) 232 { 233 std::cerr << "HostAck D-Bus call failed"; 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 } // namespace responder 275 } // namespace pldm 276