blob: 7d02693d4d9ae077c8c2e3c355499441464ea23f [file] [log] [blame]
/**
* Copyright © 2021 IBM Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
extern "C" {
#include <libpdbg.h>
}
#include "fapi_data_process.hpp"
#include "pel.hpp"
#include "sbe_ffdc_handler.hpp"
#include "temporary_file.hpp"
#include <ekb/hwpf/fapi2/include/return_code_defs.H>
#include <fmt/format.h>
#include <libekb.H>
#include <new>
#include <phosphor-logging/log.hpp>
namespace openpower
{
namespace pels
{
namespace sbe
{
using namespace phosphor::logging;
SbeFFDC::SbeFFDC(const AdditionalData& aData, const PelFFDC& files) :
ffdcType(FFDC_TYPE_NONE)
{
log<level::INFO>("SBE FFDC processing requested");
// SRC6 field in the additional data contains Processor position
// associated to the SBE FFDC
//"[0:15] chip position"
auto src6 = aData.getValue("SRC6");
if (src6 == std::nullopt)
{
log<level::ERR>("Fail to extract SRC6 data: failing to get proc index");
return;
}
try
{
procPos = (std::stoi(src6.value()) & 0xFFFF0000) >> 16;
}
catch (const std::exception& err)
{
log<level::ERR>(
fmt::format("Conversion failure errormsg({})", err.what()).c_str());
return;
}
if (files.empty())
{
log<level::INFO>("SbeFFDC : No files found, skipping ffdc processing");
return;
}
for (const auto& file : files)
{
if ((file.format == UserDataFormat::custom) &&
(file.subType == sbeFFDCSubType))
{
// Process SBE file.
parse(file.fd);
}
}
}
void SbeFFDC::parse(int fd)
{
log<level::INFO>(
fmt::format("SBE FFDC file fd:({}), parsing started", fd).c_str());
uint32_t ffdcBufOffset = 0;
uint32_t pktCount = 0;
sbeFfdcPacketType ffdcPkt;
// get SBE FFDC data.
auto ffdcData = util::readFD(fd);
if (ffdcData.empty())
{
log<level::ERR>(
fmt::format("Empty SBE FFDC file fd:({}), skipping", fd).c_str());
return;
}
while ((ffdcBufOffset < ffdcData.size()) && (sbeMaxFfdcPackets != pktCount))
{
// Next un-extracted FFDC Packet
fapiFfdcBufType* ffdc =
reinterpret_cast<fapiFfdcBufType*>(ffdcData.data() + ffdcBufOffset);
auto magicBytes = ntohs(ffdc->magic_bytes);
auto lenWords = ntohs(ffdc->lengthinWords);
auto fapiRc = ntohl(ffdc->fapiRc);
auto msg = fmt::format("FFDC magic: {} length in words:{} Fapirc:{}",
magicBytes, lenWords, fapiRc);
log<level::INFO>(msg.c_str());
if (magicBytes != ffdcMagicCode)
{
log<level::ERR>("Invalid FFDC magic code in Header: Skipping ");
return;
}
ffdcPkt.fapiRc = fapiRc;
// Not interested in the first 2 words (these are not ffdc)
auto pktLenWords = lenWords - (2 * ffdcPkgOneWord);
ffdcPkt.ffdcLengthInWords = pktLenWords;
if (pktLenWords)
{
// Memory freeing will be taking care by ffdcPkt structure
// destructor
ffdcPkt.ffdcData = new uint32_t[pktLenWords];
memcpy(ffdcPkt.ffdcData,
((reinterpret_cast<uint32_t*>(ffdc)) +
(2 * ffdcPkgOneWord)), // skip first 2 words
(pktLenWords * sizeof(uint32_t)));
}
else
{
log<level::ERR>("FFDC packet size is zero skipping");
return;
}
// SBE FFDC processing is not required for SBE Plat errors RCs.
// Plat errors processing is driven by SBE provided user data
// plugins, which need to link with PEL tool infrastructure.
if (ffdcPkt.fapiRc != fapi2::FAPI2_RC_PLAT_ERR_SEE_DATA)
{
process(ffdcPkt);
}
else
{
log<level::INFO>("SBE FFDC: Internal FFDC packet");
}
// Update Buffer offset in Bytes
ffdcBufOffset += lenWords * sizeof(uint32_t);
++pktCount;
}
if (pktCount == sbeMaxFfdcPackets)
{
log<level::ERR>(fmt::format("Received more than the limit of ({})"
" FFDC packets, processing only ({})",
sbeMaxFfdcPackets, pktCount)
.c_str());
}
}
void SbeFFDC::process(const sbeFfdcPacketType& ffdcPkt)
{
using json = nlohmann::json;
// formated FFDC data structure after FFDC packet processing
FFDC ffdc;
if (!pdbg_targets_init(NULL))
{
log<level::ERR>("pdbg_targets_init failed, skipping ffdc processing");
return;
}
if (libekb_init())
{
log<level::ERR>("libekb_init failed, skipping ffdc processing");
return;
}
try
{
// libekb provided wrapper function to convert SBE FFDC
// in to known ffdc structure.
libekb_get_sbe_ffdc(ffdc, ffdcPkt, procPos);
}
catch (...)
{
log<level::ERR>("libekb_get_sbe_ffdc failed, skipping ffdc processing");
return;
}
// update FFDC type class membeir for hwp specific packet
// Assumption SBE FFDC contains only one hwp FFDC packet.
ffdcType = ffdc.ffdc_type;
// To store callouts details in json format as per pel expectation.
json pelJSONFmtCalloutDataList;
pelJSONFmtCalloutDataList = json::array();
// To store other user data from FFDC.
openpower::pels::phal::FFDCData ffdcUserData;
// Get FFDC and required info to include in PEL
openpower::pels::phal::convertFAPItoPELformat(
ffdc, pelJSONFmtCalloutDataList, ffdcUserData);
// Get callout information and sore in to file.
auto calloutData = pelJSONFmtCalloutDataList.dump();
util::TemporaryFile ffdcFile(calloutData.c_str(), calloutData.size());
// Create json callout type pel FFDC file structre.
PelFFDCfile pf;
pf.format = openpower::pels::UserDataFormat::json;
pf.subType = openpower::pels::jsonCalloutSubtype;
pf.version = 0x01;
pf.fd = ffdcFile.getFd();
ffdcFiles.push_back(pf);
// save the file path to delete the file after usage.
paths.push_back(ffdcFile.getPath());
// Format ffdc user data and create new file.
std::string data;
for (auto& d : ffdcUserData)
{
data += d.first + " = " + d.second + "\n";
}
util::TemporaryFile pelDataFile(data.c_str(), data.size());
PelFFDCfile pdf;
pdf.format = openpower::pels::UserDataFormat::text;
pdf.version = 0x01;
pdf.fd = pelDataFile.getFd();
pdf.subType = 0;
ffdcFiles.push_back(pdf);
paths.push_back(pelDataFile.getPath());
}
std::optional<LogSeverity> SbeFFDC::getSeverity()
{
if (ffdcType == FFDC_TYPE_SPARE_CLOCK_INFO)
{
log<level::INFO>(
"Found spare clock error, changing severity to informational");
return LogSeverity::Informational;
}
return std::nullopt;
}
} // namespace sbe
} // namespace pels
} // namespace openpower