1 #include "create_pel.hpp" 2 3 #include "attributes_info.H" 4 5 #include "util.hpp" 6 7 #include <fcntl.h> 8 #include <fmt/format.h> 9 #include <libekb.H> 10 #include <libphal.H> 11 #include <unistd.h> 12 13 #include <phosphor-logging/elog.hpp> 14 #include <xyz/openbmc_project/Logging/Create/server.hpp> 15 #include <xyz/openbmc_project/Logging/Entry/server.hpp> 16 17 #include <cerrno> 18 #include <cstdio> 19 #include <cstdlib> 20 #include <cstring> 21 #include <map> 22 #include <stdexcept> 23 #include <string> 24 #include <tuple> 25 #include <vector> 26 27 namespace openpower 28 { 29 using namespace phosphor::logging; 30 31 namespace pel 32 { 33 34 constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging"; 35 constexpr auto loggingInterface = "xyz.openbmc_project.Logging.Create"; 36 constexpr auto opLoggingInterface = "org.open_power.Logging.PEL"; 37 38 /** 39 * @brief get SBE special callout information 40 * 41 * This function add the special sbe callout in the user provided 42 * json callout list. includes BMC0002 procedure callout with 43 * high priority and processor callout with medium priority. 44 * 45 * @param[in] procTarget - pdbg processor target 46 * @param[out] jsonCalloutDataList - reference to json callout list 47 */ 48 static void getSBECallout(struct pdbg_target* procTarget, 49 json& jsonCalloutDataList) 50 { 51 using namespace openpower::phal::pdbg; 52 53 json jsonProcedCallout; 54 55 // Add procedure callout 56 jsonProcedCallout["Procedure"] = "BMC0002"; 57 jsonProcedCallout["Priority"] = "H"; 58 jsonCalloutDataList.emplace_back(std::move(jsonProcedCallout)); 59 try 60 { 61 ATTR_LOCATION_CODE_Type locationCode; 62 // Initialize with default data. 63 memset(&locationCode, '\0', sizeof(locationCode)); 64 // Get location code information 65 openpower::phal::pdbg::getLocationCode(procTarget, locationCode); 66 json jsonProcCallout; 67 jsonProcCallout["LocationCode"] = locationCode; 68 jsonProcCallout["Deconfigured"] = false; 69 jsonProcCallout["Guarded"] = false; 70 jsonProcCallout["Priority"] = "M"; 71 jsonCalloutDataList.emplace_back(std::move(jsonProcCallout)); 72 } 73 catch (const std::exception& e) 74 { 75 log<level::ERR>(fmt::format("getLocationCode({}): Exception({})", 76 pdbg_target_path(procTarget), e.what()) 77 .c_str()); 78 } 79 } 80 81 void createErrorPEL(const std::string& event, const json& calloutData, 82 const FFDCData& ffdcData, const Severity severity) 83 { 84 std::map<std::string, std::string> additionalData; 85 auto bus = sdbusplus::bus::new_default(); 86 additionalData.emplace("_PID", std::to_string(getpid())); 87 for (auto& data : ffdcData) 88 { 89 additionalData.emplace(data); 90 } 91 92 try 93 { 94 FFDCFile ffdcFile(calloutData); 95 96 std::vector<std::tuple<sdbusplus::xyz::openbmc_project::Logging:: 97 server::Create::FFDCFormat, 98 uint8_t, uint8_t, sdbusplus::message::unix_fd>> 99 pelCalloutInfo; 100 101 pelCalloutInfo.push_back( 102 std::make_tuple(sdbusplus::xyz::openbmc_project::Logging::server:: 103 Create::FFDCFormat::JSON, 104 static_cast<uint8_t>(0xCA), 105 static_cast<uint8_t>(0x01), ffdcFile.getFileFD())); 106 107 std::string service = 108 util::getService(bus, loggingObjectPath, loggingInterface); 109 auto method = 110 bus.new_method_call(service.c_str(), loggingObjectPath, 111 loggingInterface, "CreateWithFFDCFiles"); 112 auto level = 113 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage( 114 severity); 115 method.append(event, level, additionalData, pelCalloutInfo); 116 auto resp = bus.call(method); 117 } 118 catch (const sdbusplus::exception_t& e) 119 { 120 log<level::ERR>( 121 fmt::format("D-Bus call exception", 122 "OBJPATH={}, INTERFACE={}, event={}, EXCEPTION={}", 123 loggingObjectPath, loggingInterface, event, e.what()) 124 .c_str()); 125 throw std::runtime_error( 126 "Error in invoking D-Bus logging create interface"); 127 } 128 catch (const std::exception& e) 129 { 130 throw e; 131 } 132 } 133 134 uint32_t createSbeErrorPEL(const std::string& event, const sbeError_t& sbeError, 135 const FFDCData& ffdcData, 136 struct pdbg_target* procTarget, 137 const Severity severity) 138 { 139 uint32_t plid = 0; 140 std::map<std::string, std::string> additionalData; 141 auto bus = sdbusplus::bus::new_default(); 142 143 additionalData.emplace("_PID", std::to_string(getpid())); 144 additionalData.emplace("SBE_ERR_MSG", sbeError.what()); 145 146 for (auto& data : ffdcData) 147 { 148 additionalData.emplace(data); 149 } 150 151 std::vector<std::tuple< 152 sdbusplus::xyz::openbmc_project::Logging::server::Create::FFDCFormat, 153 uint8_t, uint8_t, sdbusplus::message::unix_fd>> 154 pelFFDCInfo; 155 156 // get SBE ffdc file descriptor 157 auto fd = sbeError.getFd(); 158 159 // Negative fd value indicates error case or invalid file 160 // No need of special processing , just log error with additional ffdc. 161 if (fd > 0) 162 { 163 // Refer phosphor-logging/extensions/openpower-pels/README.md section 164 // "Self Boot Engine(SBE) First Failure Data Capture(FFDC) Support" 165 // for details of related to createPEL with SBE FFDC information 166 // usin g CreateWithFFDCFiles api. 167 pelFFDCInfo.push_back( 168 std::make_tuple(sdbusplus::xyz::openbmc_project::Logging::server:: 169 Create::FFDCFormat::Custom, 170 static_cast<uint8_t>(0xCB), 171 static_cast<uint8_t>(0x01), sbeError.getFd())); 172 } 173 174 // Workaround : currently sbe_extract_rc hwp procedure based callout 175 // handling is not available. openbmc issue #2917 176 // As per discussion with RAS team adding additional callout for 177 // SBE timeout error case, till this hwp based error handling in place. 178 // Note: PEL needs ffdcFile file till pel creation. This is forced to 179 // define ffdcFile function level scope. 180 std::unique_ptr<FFDCFile> FFDCFilePtr; 181 try 182 { 183 if ((event == "org.open_power.Processor.Error.SbeBootTimeout") && 184 (severity == Severity::Error)) 185 { 186 json jsonCalloutDataList; 187 jsonCalloutDataList = json::array(); 188 getSBECallout(procTarget, jsonCalloutDataList); 189 FFDCFilePtr = std::make_unique<FFDCFile>(jsonCalloutDataList); 190 pelFFDCInfo.push_back(std::make_tuple( 191 sdbusplus::xyz::openbmc_project::Logging::server::Create:: 192 FFDCFormat::JSON, 193 static_cast<uint8_t>(0xCA), static_cast<uint8_t>(0x01), 194 FFDCFilePtr->getFileFD())); 195 } 196 } 197 catch (const std::exception& e) 198 { 199 log<level::ERR>( 200 fmt::format("Skipping SBE special callout due to Exception({})", 201 e.what()) 202 .c_str()); 203 } 204 205 try 206 { 207 std::string service = 208 util::getService(bus, loggingObjectPath, opLoggingInterface); 209 auto method = 210 bus.new_method_call(service.c_str(), loggingObjectPath, 211 opLoggingInterface, "CreatePELWithFFDCFiles"); 212 auto level = 213 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage( 214 severity); 215 method.append(event, level, additionalData, pelFFDCInfo); 216 auto response = bus.call(method); 217 218 // reply will be tuple containing bmc log id, platform log id 219 std::tuple<uint32_t, uint32_t> reply = {0, 0}; 220 221 // parse dbus response into reply 222 response.read(reply); 223 plid = std::get<1>(reply); // platform log id is tuple "second" 224 } 225 catch (const sdbusplus::exception_t& e) 226 { 227 log<level::ERR>(fmt::format("D-Bus call exception", 228 "OBJPATH={}, INTERFACE={}, EXCEPTION={}", 229 loggingObjectPath, loggingInterface, 230 e.what()) 231 .c_str()); 232 throw std::runtime_error( 233 "Error in invoking D-Bus logging create interface"); 234 } 235 catch (const std::exception& e) 236 { 237 throw e; 238 } 239 return plid; 240 } 241 242 void createPEL(const std::string& event, const FFDCData& ffdcData) 243 { 244 std::map<std::string, std::string> additionalData; 245 auto bus = sdbusplus::bus::new_default(); 246 247 additionalData.emplace("_PID", std::to_string(getpid())); 248 for (auto& data : ffdcData) 249 { 250 additionalData.emplace(data); 251 } 252 253 try 254 { 255 std::string service = 256 util::getService(bus, loggingObjectPath, loggingInterface); 257 auto method = bus.new_method_call(service.c_str(), loggingObjectPath, 258 loggingInterface, "Create"); 259 auto level = 260 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage( 261 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level:: 262 Error); 263 method.append(event, level, additionalData); 264 auto resp = bus.call(method); 265 } 266 catch (const sdbusplus::exception_t& e) 267 { 268 log<level::ERR>(fmt::format("sdbusplus D-Bus call exception", 269 "OBJPATH={}, INTERFACE={}, EXCEPTION={}", 270 loggingObjectPath, loggingInterface, 271 e.what()) 272 .c_str()); 273 ; 274 275 throw std::runtime_error( 276 "Error in invoking D-Bus logging create interface"); 277 } 278 catch (const std::exception& e) 279 { 280 log<level::ERR>( 281 fmt::format("D-bus call exception", "EXCEPTION={}", e.what()) 282 .c_str()); 283 throw e; 284 } 285 } 286 287 FFDCFile::FFDCFile(const json& pHALCalloutData) : 288 calloutData(pHALCalloutData.dump()), 289 calloutFile("/tmp/phalPELCalloutsJson.XXXXXX"), fileFD(-1) 290 { 291 prepareFFDCFile(); 292 } 293 294 FFDCFile::~FFDCFile() 295 { 296 removeCalloutFile(); 297 } 298 299 int FFDCFile::getFileFD() const 300 { 301 return fileFD; 302 } 303 304 void FFDCFile::prepareFFDCFile() 305 { 306 createCalloutFile(); 307 writeCalloutData(); 308 setCalloutFileSeekPos(); 309 } 310 311 void FFDCFile::createCalloutFile() 312 { 313 fileFD = mkostemp(const_cast<char*>(calloutFile.c_str()), O_RDWR); 314 315 if (fileFD == -1) 316 { 317 log<level::ERR>(fmt::format("Failed to create phalPELCallouts " 318 "file({}), errorno({}) and errormsg({})", 319 calloutFile, errno, strerror(errno)) 320 .c_str()); 321 throw std::runtime_error("Failed to create phalPELCallouts file"); 322 } 323 } 324 325 void FFDCFile::writeCalloutData() 326 { 327 ssize_t rc = write(fileFD, calloutData.c_str(), calloutData.size()); 328 329 if (rc == -1) 330 { 331 log<level::ERR>(fmt::format("Failed to write phaPELCallout info " 332 "in file({}), errorno({}), errormsg({})", 333 calloutFile, errno, strerror(errno)) 334 .c_str()); 335 throw std::runtime_error("Failed to write phalPELCallouts info"); 336 } 337 else if (rc != static_cast<ssize_t>(calloutData.size())) 338 { 339 log<level::WARNING>(fmt::format("Could not write all phal callout " 340 "info in file({}), written byte({}) " 341 "and total byte({})", 342 calloutFile, rc, calloutData.size()) 343 .c_str()); 344 } 345 } 346 347 void FFDCFile::setCalloutFileSeekPos() 348 { 349 int rc = lseek(fileFD, 0, SEEK_SET); 350 351 if (rc == -1) 352 { 353 log<level::ERR>(fmt::format("Failed to set SEEK_SET for " 354 "phalPELCallouts in file({}), errorno({}) " 355 "and errormsg({})", 356 calloutFile, errno, strerror(errno)) 357 .c_str()); 358 throw std::runtime_error( 359 "Failed to set SEEK_SET for phalPELCallouts file"); 360 } 361 } 362 363 void FFDCFile::removeCalloutFile() 364 { 365 close(fileFD); 366 std::remove(calloutFile.c_str()); 367 } 368 369 } // namespace pel 370 } // namespace openpower 371