1 #include "create_pel.hpp" 2 3 #include "util.hpp" 4 5 #include <fcntl.h> 6 #include <fmt/format.h> 7 #include <libekb.H> 8 #include <unistd.h> 9 10 #include <phosphor-logging/elog.hpp> 11 #include <xyz/openbmc_project/Logging/Create/server.hpp> 12 #include <xyz/openbmc_project/Logging/Entry/server.hpp> 13 14 #include <cerrno> 15 #include <cstdio> 16 #include <cstdlib> 17 #include <cstring> 18 #include <map> 19 #include <stdexcept> 20 #include <string> 21 #include <tuple> 22 #include <vector> 23 24 namespace openpower 25 { 26 using namespace phosphor::logging; 27 28 namespace pel 29 { 30 31 constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging"; 32 constexpr auto loggingInterface = "xyz.openbmc_project.Logging.Create"; 33 constexpr auto opLoggingInterface = "org.open_power.Logging.PEL"; 34 35 void createErrorPEL(const std::string& event, const json& calloutData, 36 const FFDCData& ffdcData) 37 { 38 std::map<std::string, std::string> additionalData; 39 auto bus = sdbusplus::bus::new_default(); 40 additionalData.emplace("_PID", std::to_string(getpid())); 41 for (auto& data : ffdcData) 42 { 43 additionalData.emplace(data); 44 } 45 46 try 47 { 48 FFDCFile ffdcFile(calloutData); 49 50 std::vector<std::tuple<sdbusplus::xyz::openbmc_project::Logging:: 51 server::Create::FFDCFormat, 52 uint8_t, uint8_t, sdbusplus::message::unix_fd>> 53 pelCalloutInfo; 54 55 pelCalloutInfo.push_back( 56 std::make_tuple(sdbusplus::xyz::openbmc_project::Logging::server:: 57 Create::FFDCFormat::JSON, 58 static_cast<uint8_t>(0xCA), 59 static_cast<uint8_t>(0x01), ffdcFile.getFileFD())); 60 61 std::string service = 62 util::getService(bus, loggingObjectPath, loggingInterface); 63 auto method = 64 bus.new_method_call(service.c_str(), loggingObjectPath, 65 loggingInterface, "CreateWithFFDCFiles"); 66 auto level = 67 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage( 68 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level:: 69 Error); 70 method.append(event, level, additionalData, pelCalloutInfo); 71 auto resp = bus.call(method); 72 } 73 catch (const sdbusplus::exception::exception& e) 74 { 75 log<level::ERR>( 76 fmt::format("D-Bus call exception", 77 "OBJPATH={}, INTERFACE={}, event={}, EXCEPTION={}", 78 loggingObjectPath, loggingInterface, event, e.what()) 79 .c_str()); 80 throw std::runtime_error( 81 "Error in invoking D-Bus logging create interface"); 82 } 83 catch (const std::exception& e) 84 { 85 throw e; 86 } 87 } 88 89 uint32_t createSbeErrorPEL(const std::string& event, const sbeError_t& sbeError, 90 const FFDCData& ffdcData) 91 { 92 uint32_t plid = 0; 93 std::map<std::string, std::string> additionalData; 94 auto bus = sdbusplus::bus::new_default(); 95 96 additionalData.emplace("_PID", std::to_string(getpid())); 97 additionalData.emplace("SBE_ERR_MSG", sbeError.what()); 98 99 for (auto& data : ffdcData) 100 { 101 additionalData.emplace(data); 102 } 103 104 std::vector<std::tuple< 105 sdbusplus::xyz::openbmc_project::Logging::server::Create::FFDCFormat, 106 uint8_t, uint8_t, sdbusplus::message::unix_fd>> 107 pelFFDCInfo; 108 109 // get SBE ffdc file descriptor 110 auto fd = sbeError.getFd(); 111 112 // Negative fd value indicates error case or invalid file 113 // No need of special processing , just log error with additional ffdc. 114 if (fd > 0) 115 { 116 // Refer phosphor-logging/extensions/openpower-pels/README.md section 117 // "Self Boot Engine(SBE) First Failure Data Capture(FFDC) Support" 118 // for details of related to createPEL with SBE FFDC information 119 // usin g CreateWithFFDCFiles api. 120 pelFFDCInfo.push_back( 121 std::make_tuple(sdbusplus::xyz::openbmc_project::Logging::server:: 122 Create::FFDCFormat::Custom, 123 static_cast<uint8_t>(0xCB), 124 static_cast<uint8_t>(0x01), sbeError.getFd())); 125 } 126 try 127 { 128 std::string service = 129 util::getService(bus, loggingObjectPath, opLoggingInterface); 130 auto method = 131 bus.new_method_call(service.c_str(), loggingObjectPath, 132 opLoggingInterface, "CreatePELWithFFDCFiles"); 133 auto level = 134 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage( 135 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level:: 136 Error); 137 method.append(event, level, additionalData, pelFFDCInfo); 138 auto response = bus.call(method); 139 140 // reply will be tuple containing bmc log id, platform log id 141 std::tuple<uint32_t, uint32_t> reply = {0, 0}; 142 143 // parse dbus response into reply 144 response.read(reply); 145 plid = std::get<1>(reply); // platform log id is tuple "second" 146 } 147 catch (const sdbusplus::exception::exception& e) 148 { 149 log<level::ERR>(fmt::format("D-Bus call exception", 150 "OBJPATH={}, INTERFACE={}, EXCEPTION={}", 151 loggingObjectPath, loggingInterface, 152 e.what()) 153 .c_str()); 154 throw std::runtime_error( 155 "Error in invoking D-Bus logging create interface"); 156 } 157 catch (const std::exception& e) 158 { 159 throw e; 160 } 161 162 return plid; 163 } 164 165 void createPEL(const std::string& event, const FFDCData& ffdcData) 166 { 167 std::map<std::string, std::string> additionalData; 168 auto bus = sdbusplus::bus::new_default(); 169 170 additionalData.emplace("_PID", std::to_string(getpid())); 171 for (auto& data : ffdcData) 172 { 173 additionalData.emplace(data); 174 } 175 176 try 177 { 178 std::string service = 179 util::getService(bus, loggingObjectPath, loggingInterface); 180 auto method = bus.new_method_call(service.c_str(), loggingObjectPath, 181 loggingInterface, "Create"); 182 auto level = 183 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage( 184 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level:: 185 Error); 186 method.append(event, level, additionalData); 187 auto resp = bus.call(method); 188 } 189 catch (const sdbusplus::exception::exception& e) 190 { 191 log<level::ERR>(fmt::format("sdbusplus D-Bus call exception", 192 "OBJPATH={}, INTERFACE={}, EXCEPTION={}", 193 loggingObjectPath, loggingInterface, 194 e.what()) 195 .c_str()); 196 ; 197 198 throw std::runtime_error( 199 "Error in invoking D-Bus logging create interface"); 200 } 201 catch (const std::exception& e) 202 { 203 log<level::ERR>( 204 fmt::format("D-bus call exception", "EXCEPTION={}", e.what()) 205 .c_str()); 206 throw e; 207 } 208 } 209 210 FFDCFile::FFDCFile(const json& pHALCalloutData) : 211 calloutData(pHALCalloutData.dump()), 212 calloutFile("/tmp/phalPELCalloutsJson.XXXXXX"), fileFD(-1) 213 { 214 prepareFFDCFile(); 215 } 216 217 FFDCFile::~FFDCFile() 218 { 219 removeCalloutFile(); 220 } 221 222 int FFDCFile::getFileFD() const 223 { 224 return fileFD; 225 } 226 227 void FFDCFile::prepareFFDCFile() 228 { 229 createCalloutFile(); 230 writeCalloutData(); 231 setCalloutFileSeekPos(); 232 } 233 234 void FFDCFile::createCalloutFile() 235 { 236 fileFD = mkostemp(const_cast<char*>(calloutFile.c_str()), O_RDWR); 237 238 if (fileFD == -1) 239 { 240 log<level::ERR>(fmt::format("Failed to create phalPELCallouts " 241 "file({}), errorno({}) and errormsg({})", 242 calloutFile, errno, strerror(errno)) 243 .c_str()); 244 throw std::runtime_error("Failed to create phalPELCallouts file"); 245 } 246 } 247 248 void FFDCFile::writeCalloutData() 249 { 250 ssize_t rc = write(fileFD, calloutData.c_str(), calloutData.size()); 251 252 if (rc == -1) 253 { 254 log<level::ERR>(fmt::format("Failed to write phaPELCallout info " 255 "in file({}), errorno({}), errormsg({})", 256 calloutFile, errno, strerror(errno)) 257 .c_str()); 258 throw std::runtime_error("Failed to write phalPELCallouts info"); 259 } 260 else if (rc != static_cast<ssize_t>(calloutData.size())) 261 { 262 log<level::WARNING>(fmt::format("Could not write all phal callout " 263 "info in file({}), written byte({}) " 264 "and total byte({})", 265 calloutFile, rc, calloutData.size()) 266 .c_str()); 267 } 268 } 269 270 void FFDCFile::setCalloutFileSeekPos() 271 { 272 int rc = lseek(fileFD, 0, SEEK_SET); 273 274 if (rc == -1) 275 { 276 log<level::ERR>(fmt::format("Failed to set SEEK_SET for " 277 "phalPELCallouts in file({}), errorno({}) " 278 "and errormsg({})", 279 calloutFile, errno, strerror(errno)) 280 .c_str()); 281 throw std::runtime_error( 282 "Failed to set SEEK_SET for phalPELCallouts file"); 283 } 284 } 285 286 void FFDCFile::removeCalloutFile() 287 { 288 close(fileFD); 289 std::remove(calloutFile.c_str()); 290 } 291 292 } // namespace pel 293 } // namespace openpower 294