blob: 6a4f9a809642c22c0c524bc377e1e1080e614a22 [file] [log] [blame]
#include "config.h"
#include "ecc_manager.hpp"
#include <phosphor-logging/elog-errors.hpp>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
using namespace phosphor::logging;
namespace phosphor
{
namespace memory
{
static constexpr const char ECC_FILE[] = "/etc/ecc/maxlog.conf";
static constexpr const auto RESET_COUNT = "1";
static constexpr const char CLOSE_EDAC_REPORT[] = "off";
auto retries = 3;
static constexpr auto delay = std::chrono::milliseconds{100};
static constexpr auto interval = 1000000;
static constexpr uint16_t selBMCGenID = 0x0020;
void ECC::init()
{
namespace fs = std::filesystem;
if (fs::exists(sysfsRootPath))
{
try
{
resetCounter();
getMaxLogValue();
}
catch (const std::system_error& e)
{
log<level::INFO>(
"Logging failing sysfs file",
phosphor::logging::entry("FILE=%s", sysfsRootPath));
}
}
_bus.request_name(BUSNAME);
}
std::string ECC::getValue(std::string fullPath)
{
std::string val;
std::ifstream ifs;
while (true)
{
try
{
if (!ifs.is_open())
ifs.open(fullPath);
ifs.clear();
ifs.seekg(0);
ifs >> val;
}
catch (const std::exception& e)
{
--retries;
std::this_thread::sleep_for(delay);
continue;
}
break;
}
ifs.close();
return val;
}
void ECC::writeValue(std::string fullPath, std::string value)
{
std::ofstream ofs;
while (true)
{
try
{
if (!ofs.is_open())
ofs.open(fullPath);
ofs.clear();
ofs.seekp(0);
ofs << value;
ofs.flush();
}
catch (const std::exception& e)
{
--retries;
std::this_thread::sleep_for(delay);
continue;
}
break;
}
ofs.close();
}
void ECC::run()
{
init();
std::function<void()> callback(std::bind(&ECC::read, this));
try
{
_timer.restart(std::chrono::microseconds(interval));
_bus.attach_event(_event.get(), SD_EVENT_PRIORITY_IMPORTANT);
_event.loop();
}
catch (const std::exception& e)
{
log<level::ERR>("Error in sysfs polling loop",
entry("ERROR=%s", e.what()));
throw;
}
}
void ECC::checkEccLogFull(int64_t ceCount, int64_t ueCount)
{
std::string errorMsg = "ECC error(memory error logging limit reached)";
std::vector<uint8_t> eccLogFullEventData{0x05, 0xff, 0xfe};
bool assert = true;
auto total = ceCount + ueCount;
bool isReached = false;
if (total == 0)
{
// someone reset edac report from driver
// so clear all parameter
EccInterface::ceCount(ceCount);
EccInterface::ueCount(ueCount);
previousCeCounter = 0;
previousUeCounter = 0;
EccInterface::isLoggingLimitReached(isReached);
}
else if (total >= maxECCLog)
{
// add SEL log
addSELLog(errorMsg, OBJPATH, eccLogFullEventData, assert, selBMCGenID);
isReached = true;
EccInterface::isLoggingLimitReached(isReached);
controlEDACReport(CLOSE_EDAC_REPORT);
// set ECC state
EccInterface::state(MemoryECC::ECCStatus::LogFull);
}
}
int ECC::checkCeCount()
{
std::string item = "ce_count";
std::string errorMsg = "ECC error(correctable)";
int64_t value = 0;
std::string fullPath = sysfsRootPath;
fullPath.append(item);
value = std::stoi(getValue(fullPath));
std::vector<uint8_t> eccCeEventData{0x00, 0xff, 0xfe};
bool assert = true;
while (previousCeCounter < value)
{
previousCeCounter++;
// add phosphor-logging log
EccInterface::ceCount(previousCeCounter);
// add SEL log
addSELLog(errorMsg, OBJPATH, eccCeEventData, assert, selBMCGenID);
// set ECC state
EccInterface::state(MemoryECC::ECCStatus::CE);
}
return value;
}
int ECC::checkUeCount()
{
std::string item = "ue_count";
std::string errorMsg = "ECC error(uncorrectable)";
int64_t value = 0;
std::string fullPath = sysfsRootPath;
fullPath.append(item);
value = std::stoi(getValue(fullPath));
std::vector<uint8_t> eccUeEventData{0x01, 0xff, 0xfe};
bool assert = true;
while (previousUeCounter < value)
{
previousUeCounter++;
// add phosphor-logging log
EccInterface::ueCount(previousUeCounter);
// add SEL log
addSELLog(errorMsg, OBJPATH, eccUeEventData, assert, selBMCGenID);
// set ECC state
EccInterface::state(MemoryECC::ECCStatus::UE);
}
return value;
}
void ECC::resetCounter()
{
std::string item = "reset_counters";
std::string fullPath = sysfsRootPath;
fullPath.append(item);
writeValue(fullPath, RESET_COUNT);
}
void ECC::read()
{
int64_t ceCount = 0;
int64_t ueCount = 0;
ceCount = checkCeCount();
ueCount = checkUeCount();
checkEccLogFull(ceCount, ueCount);
}
void ECC::controlEDACReport(std::string op)
{
writeValue(sysfsEDACReportPath, op);
}
// get max log from file
void ECC::getMaxLogValue()
{
maxECCLog = std::stoi(getValue(ECC_FILE));
}
void ECC::addSELLog(std::string message, std::string path,
std::vector<uint8_t> selData, bool assert, uint16_t genId)
{
// sdbusplus::bus_t bus = sdbusplus::bus::new_default();
auto selCall = _bus.new_method_call(
"xyz.openbmc_project.Logging.IPMI", "/xyz/openbmc_project/Logging/IPMI",
"xyz.openbmc_project.Logging.IPMI", "IpmiSelAdd");
selCall.append(message, path, selData, assert, genId);
auto selReply = _bus.call(selCall);
if (selReply.is_method_error())
{
log<level::ERR>("add SEL log error\n");
}
}
} // namespace memory
} // namespace phosphor