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) 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 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level:: 115 Error); 116 method.append(event, level, additionalData, pelCalloutInfo); 117 auto resp = bus.call(method); 118 } 119 catch (const sdbusplus::exception::exception& e) 120 { 121 log<level::ERR>( 122 fmt::format("D-Bus call exception", 123 "OBJPATH={}, INTERFACE={}, event={}, EXCEPTION={}", 124 loggingObjectPath, loggingInterface, event, e.what()) 125 .c_str()); 126 throw std::runtime_error( 127 "Error in invoking D-Bus logging create interface"); 128 } 129 catch (const std::exception& e) 130 { 131 throw e; 132 } 133 } 134 135 uint32_t createSbeErrorPEL(const std::string& event, const sbeError_t& sbeError, 136 const FFDCData& ffdcData, 137 struct pdbg_target* procTarget, 138 const Severity severity) 139 { 140 uint32_t plid = 0; 141 std::map<std::string, std::string> additionalData; 142 auto bus = sdbusplus::bus::new_default(); 143 144 additionalData.emplace("_PID", std::to_string(getpid())); 145 additionalData.emplace("SBE_ERR_MSG", sbeError.what()); 146 147 for (auto& data : ffdcData) 148 { 149 additionalData.emplace(data); 150 } 151 152 std::vector<std::tuple< 153 sdbusplus::xyz::openbmc_project::Logging::server::Create::FFDCFormat, 154 uint8_t, uint8_t, sdbusplus::message::unix_fd>> 155 pelFFDCInfo; 156 157 // get SBE ffdc file descriptor 158 auto fd = sbeError.getFd(); 159 160 // Negative fd value indicates error case or invalid file 161 // No need of special processing , just log error with additional ffdc. 162 if (fd > 0) 163 { 164 // Refer phosphor-logging/extensions/openpower-pels/README.md section 165 // "Self Boot Engine(SBE) First Failure Data Capture(FFDC) Support" 166 // for details of related to createPEL with SBE FFDC information 167 // usin g CreateWithFFDCFiles api. 168 pelFFDCInfo.push_back( 169 std::make_tuple(sdbusplus::xyz::openbmc_project::Logging::server:: 170 Create::FFDCFormat::Custom, 171 static_cast<uint8_t>(0xCB), 172 static_cast<uint8_t>(0x01), sbeError.getFd())); 173 } 174 175 // Workaround : currently sbe_extract_rc hwp procedure based callout 176 // handling is not available. openbmc issue #2917 177 // As per discussion with RAS team adding additional callout for 178 // SBE timeout error case, till this hwp based error handling in place. 179 // Note: PEL needs ffdcFile file till pel creation. This is forced to 180 // define ffdcFile function level scope. 181 std::unique_ptr<FFDCFile> FFDCFilePtr; 182 try 183 { 184 if ((event == "org.open_power.Processor.Error.SbeBootTimeout") && 185 (severity == Severity::Error)) 186 { 187 json jsonCalloutDataList; 188 jsonCalloutDataList = json::array(); 189 getSBECallout(procTarget, jsonCalloutDataList); 190 FFDCFilePtr = std::make_unique<FFDCFile>(jsonCalloutDataList); 191 pelFFDCInfo.push_back(std::make_tuple( 192 sdbusplus::xyz::openbmc_project::Logging::server::Create:: 193 FFDCFormat::JSON, 194 static_cast<uint8_t>(0xCA), static_cast<uint8_t>(0x01), 195 FFDCFilePtr->getFileFD())); 196 } 197 } 198 catch (const std::exception& e) 199 { 200 log<level::ERR>( 201 fmt::format("Skipping SBE special callout due to Exception({})", 202 e.what()) 203 .c_str()); 204 } 205 206 try 207 { 208 std::string service = 209 util::getService(bus, loggingObjectPath, opLoggingInterface); 210 auto method = 211 bus.new_method_call(service.c_str(), loggingObjectPath, 212 opLoggingInterface, "CreatePELWithFFDCFiles"); 213 auto level = 214 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage( 215 severity); 216 method.append(event, level, additionalData, pelFFDCInfo); 217 auto response = bus.call(method); 218 219 // reply will be tuple containing bmc log id, platform log id 220 std::tuple<uint32_t, uint32_t> reply = {0, 0}; 221 222 // parse dbus response into reply 223 response.read(reply); 224 plid = std::get<1>(reply); // platform log id is tuple "second" 225 } 226 catch (const sdbusplus::exception::exception& e) 227 { 228 log<level::ERR>(fmt::format("D-Bus call exception", 229 "OBJPATH={}, INTERFACE={}, EXCEPTION={}", 230 loggingObjectPath, loggingInterface, 231 e.what()) 232 .c_str()); 233 throw std::runtime_error( 234 "Error in invoking D-Bus logging create interface"); 235 } 236 catch (const std::exception& e) 237 { 238 throw e; 239 } 240 return plid; 241 } 242 243 void createPEL(const std::string& event, const FFDCData& ffdcData) 244 { 245 std::map<std::string, std::string> additionalData; 246 auto bus = sdbusplus::bus::new_default(); 247 248 additionalData.emplace("_PID", std::to_string(getpid())); 249 for (auto& data : ffdcData) 250 { 251 additionalData.emplace(data); 252 } 253 254 try 255 { 256 std::string service = 257 util::getService(bus, loggingObjectPath, loggingInterface); 258 auto method = bus.new_method_call(service.c_str(), loggingObjectPath, 259 loggingInterface, "Create"); 260 auto level = 261 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage( 262 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level:: 263 Error); 264 method.append(event, level, additionalData); 265 auto resp = bus.call(method); 266 } 267 catch (const sdbusplus::exception::exception& e) 268 { 269 log<level::ERR>(fmt::format("sdbusplus D-Bus call exception", 270 "OBJPATH={}, INTERFACE={}, EXCEPTION={}", 271 loggingObjectPath, loggingInterface, 272 e.what()) 273 .c_str()); 274 ; 275 276 throw std::runtime_error( 277 "Error in invoking D-Bus logging create interface"); 278 } 279 catch (const std::exception& e) 280 { 281 log<level::ERR>( 282 fmt::format("D-bus call exception", "EXCEPTION={}", e.what()) 283 .c_str()); 284 throw e; 285 } 286 } 287 288 FFDCFile::FFDCFile(const json& pHALCalloutData) : 289 calloutData(pHALCalloutData.dump()), 290 calloutFile("/tmp/phalPELCalloutsJson.XXXXXX"), fileFD(-1) 291 { 292 prepareFFDCFile(); 293 } 294 295 FFDCFile::~FFDCFile() 296 { 297 removeCalloutFile(); 298 } 299 300 int FFDCFile::getFileFD() const 301 { 302 return fileFD; 303 } 304 305 void FFDCFile::prepareFFDCFile() 306 { 307 createCalloutFile(); 308 writeCalloutData(); 309 setCalloutFileSeekPos(); 310 } 311 312 void FFDCFile::createCalloutFile() 313 { 314 fileFD = mkostemp(const_cast<char*>(calloutFile.c_str()), O_RDWR); 315 316 if (fileFD == -1) 317 { 318 log<level::ERR>(fmt::format("Failed to create phalPELCallouts " 319 "file({}), errorno({}) and errormsg({})", 320 calloutFile, errno, strerror(errno)) 321 .c_str()); 322 throw std::runtime_error("Failed to create phalPELCallouts file"); 323 } 324 } 325 326 void FFDCFile::writeCalloutData() 327 { 328 ssize_t rc = write(fileFD, calloutData.c_str(), calloutData.size()); 329 330 if (rc == -1) 331 { 332 log<level::ERR>(fmt::format("Failed to write phaPELCallout info " 333 "in file({}), errorno({}), errormsg({})", 334 calloutFile, errno, strerror(errno)) 335 .c_str()); 336 throw std::runtime_error("Failed to write phalPELCallouts info"); 337 } 338 else if (rc != static_cast<ssize_t>(calloutData.size())) 339 { 340 log<level::WARNING>(fmt::format("Could not write all phal callout " 341 "info in file({}), written byte({}) " 342 "and total byte({})", 343 calloutFile, rc, calloutData.size()) 344 .c_str()); 345 } 346 } 347 348 void FFDCFile::setCalloutFileSeekPos() 349 { 350 int rc = lseek(fileFD, 0, SEEK_SET); 351 352 if (rc == -1) 353 { 354 log<level::ERR>(fmt::format("Failed to set SEEK_SET for " 355 "phalPELCallouts in file({}), errorno({}) " 356 "and errormsg({})", 357 calloutFile, errno, strerror(errno)) 358 .c_str()); 359 throw std::runtime_error( 360 "Failed to set SEEK_SET for phalPELCallouts file"); 361 } 362 } 363 364 void FFDCFile::removeCalloutFile() 365 { 366 close(fileFD); 367 std::remove(calloutFile.c_str()); 368 } 369 370 } // namespace pel 371 } // namespace openpower 372