blob: fc976512b4341c8f4b6d8a1d51721f0ab0617caa [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 <ekb/hwpf/fapi2/include/target_types.H>
#include <libekb.H>
#include <phosphor-logging/log.hpp>
#include <format>
#include <new>
namespace openpower
{
namespace pels
{
namespace sbe
{
constexpr uint32_t sbeMaxFfdcPackets = 20;
constexpr uint16_t p10FfdcMagicCode = 0xFFDC;
constexpr uint16_t pozFfdcMagicCode = 0xFBAD;
constexpr uint16_t p10FfdcSkipWords = 2;
constexpr uint16_t pozFfdcSkipWords = 3;
struct p10FfdcHeader
{
uint32_t magic_bytes:16;
uint32_t lengthinWords:16;
uint32_t seqId:16;
uint32_t cmdClass:8;
uint32_t cmd:8;
uint32_t fapiRc;
} __attribute__((packed));
struct pozFfdcHeader
{
uint32_t magicByte:16;
uint32_t lengthinWords:16;
uint32_t seqId:16;
uint32_t cmdClass:8;
uint32_t cmd:8;
uint32_t slid:16;
uint32_t severity:8;
uint32_t chipId:8;
uint32_t fapiRc;
} __attribute__((packed));
using namespace phosphor::logging;
SbeFFDC::SbeFFDC(const AdditionalData& aData, const PelFFDC& files) :
ffdcType(FFDC_TYPE_NONE), chipType(fapi2::TARGET_TYPE_PROC_CHIP)
{
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
{
chipPos = (std::stoi(src6.value()) & 0xFFFF0000) >> 16;
}
catch (const std::exception& err)
{
log<level::ERR>(
std::format("Conversion failure errormsg({})", err.what()).c_str());
return;
}
auto type = aData.getValue("CHIP_TYPE");
if (type != std::nullopt)
{
try
{
chipType = std::stoi(type.value());
}
catch (const std::exception& err)
{
log<level::ERR>(
std::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>(
std::format("SBE FFDC file fd:({}), parsing started", fd).c_str());
uint32_t ffdcBufOffset = 0;
uint32_t pktCount = 0;
// get SBE FFDC data.
auto ffdcData = util::readFD(fd);
if (ffdcData.empty())
{
log<level::ERR>(
std::format("Empty SBE FFDC file fd:({}), skipping", fd).c_str());
return;
}
while ((ffdcBufOffset < ffdcData.size()) && (sbeMaxFfdcPackets != pktCount))
{
sbeFfdcPacketType ffdcPkt;
// Next un-extracted FFDC Packet
uint16_t magicBytes =
*(reinterpret_cast<uint16_t*>(ffdcData.data() + ffdcBufOffset));
magicBytes = ntohs(magicBytes);
uint32_t pktLenWords = 0;
uint16_t lenWords = 0;
if (magicBytes == p10FfdcMagicCode)
{
p10FfdcHeader* ffdc = reinterpret_cast<p10FfdcHeader*>(
ffdcData.data() + ffdcBufOffset);
lenWords = ntohs(ffdc->lengthinWords);
auto fapiRc = ntohl(ffdc->fapiRc);
auto msg =
std::format("P10 FFDC magic: {} length in words:{} Fapirc:{}",
magicBytes, lenWords, fapiRc);
log<level::INFO>(msg.c_str());
ffdcPkt.fapiRc = fapiRc;
// Not interested in the first 2 words (these are not ffdc)
pktLenWords = lenWords - p10FfdcSkipWords;
ffdcPkt.ffdcLengthInWords = pktLenWords;
if (pktLenWords)
{
ffdcPkt.ffdcData = new uint32_t[pktLenWords];
memcpy(ffdcPkt.ffdcData,
((reinterpret_cast<uint32_t*>(ffdc)) + p10FfdcSkipWords),
(pktLenWords * sizeof(uint32_t)));
}
else
{
log<level::ERR>("FFDC packet size is zero skipping");
return;
}
pktCount++;
}
else if (magicBytes == pozFfdcMagicCode)
{
pozFfdcHeader* ffdc = reinterpret_cast<pozFfdcHeader*>(
ffdcData.data() + ffdcBufOffset);
lenWords = ntohs(ffdc->lengthinWords);
auto fapiRc = ntohl(ffdc->fapiRc);
auto msg =
std::format("P0Z FFDC magic: {} length in words:{} Fapirc:{}",
magicBytes, lenWords, fapiRc);
log<level::INFO>(msg.c_str());
ffdcPkt.fapiRc = fapiRc;
// Not interested in the first 3 words (these are not ffdc)
pktLenWords = lenWords - pozFfdcSkipWords;
ffdcPkt.ffdcLengthInWords = pktLenWords;
if (pktLenWords)
{
ffdcPkt.ffdcData = new uint32_t[pktLenWords];
memcpy(ffdcPkt.ffdcData,
((reinterpret_cast<uint32_t*>(ffdc)) + pozFfdcSkipWords),
(pktLenWords * sizeof(uint32_t)));
}
else
{
log<level::ERR>("FFDC packet size is zero skipping");
return;
}
}
else
{
log<level::ERR>("Invalid FFDC magic code in Header: 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);
}
if (pktCount == sbeMaxFfdcPackets)
{
log<level::ERR>(std::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, chipPos, chipType);
}
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.emplace_back(ffdcFile.getPath(), pf.fd);
// 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.emplace_back(pelDataFile.getPath(), pdf.fd);
}
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