blob: 3fa1f8806a9a9f79f285df309f9343ec3bb0ed43 [file] [log] [blame]
Will Lianga1d42022019-06-13 14:17:12 +08001#include "config.h"
2
3#include "ecc_manager.hpp"
4
5#include <filesystem>
6#include <fstream>
7#include <iostream>
8#include <phosphor-logging/elog-errors.hpp>
9#include <string>
10
11using namespace phosphor::logging;
12
13namespace phosphor
14{
15namespace memory
16{
17static constexpr const char ECC_FILE[] = "/etc/ecc/maxlog.conf";
18static constexpr const auto RESET_COUNT = "1";
19static constexpr const char CLOSE_EDAC_REPORT[] = "off";
20
21auto retries = 3;
22static constexpr auto delay = std::chrono::milliseconds{100};
23static constexpr auto interval = 1000000;
24static constexpr uint16_t selBMCGenID = 0x0020;
25void ECC::init()
26{
27
28 namespace fs = std::filesystem;
29
30 if (fs::exists(sysfsRootPath))
31 {
32 try
33 {
34 resetCounter();
35 getMaxLogValue();
36 }
37 catch (const std::system_error& e)
38 {
39
40 log<level::INFO>(
41 "Logging failing sysfs file",
42 phosphor::logging::entry("FILE=%s", sysfsRootPath));
43 }
44 }
45 _bus.request_name(BUSNAME);
46}
47
48std::string ECC::getValue(std::string fullPath)
49{
50 std::string val;
51 std::ifstream ifs;
52
53 while (true)
54 {
55 try
56 {
57 if (!ifs.is_open())
58 ifs.open(fullPath);
59 ifs.clear();
60 ifs.seekg(0);
61 ifs >> val;
62 }
63 catch (const std::exception& e)
64 {
65 --retries;
66 std::this_thread::sleep_for(delay);
67 continue;
68 }
69 break;
70 }
71
72 ifs.close();
73 return val;
74}
75
76void ECC::writeValue(std::string fullPath, std::string value)
77{
78 std::ofstream ofs;
79 while (true)
80 {
81 try
82 {
83 if (!ofs.is_open())
84 ofs.open(fullPath);
85 ofs.clear();
86 ofs.seekp(0);
87 ofs << value;
88 ofs.flush();
89 }
90 catch (const std::exception& e)
91 {
92 --retries;
93 std::this_thread::sleep_for(delay);
94 continue;
95 }
96 break;
97 }
98 ofs.close();
99}
100
101void ECC::run()
102{
103 init();
104 std::function<void()> callback(std::bind(&ECC::read, this));
105 try
106 {
107 _timer.restart(std::chrono::microseconds(interval));
108
109 _bus.attach_event(_event.get(), SD_EVENT_PRIORITY_IMPORTANT);
110 _event.loop();
111 }
112 catch (const std::exception& e)
113 {
114 log<level::ERR>("Error in sysfs polling loop",
115 entry("ERROR=%s", e.what()));
116 throw;
117 }
118}
119
120void ECC::checkEccLogFull(int64_t ceCount, int64_t ueCount)
121{
122 std::string errorMsg = "ECC error(memory error logging limit reached)";
123 std::vector<uint8_t> eccLogFullEventData{0x05, 0xff, 0xfe};
124 bool assert = true;
125
126 auto total = ceCount + ueCount;
127 bool isReached = false;
128 if (total == 0)
129 {
130 // someone reset edac report from driver
131 // so clear all parameter
132 EccInterface::ceCount(ceCount);
133 EccInterface::ueCount(ueCount);
134 previousCeCounter = 0;
135 previousUeCounter = 0;
136 EccInterface::isLoggingLimitReached(isReached);
137 }
138 else if (total >= maxECCLog)
139 {
140 // add SEL log
141 addSELLog(errorMsg, OBJPATH, eccLogFullEventData, assert, selBMCGenID);
142 isReached = true;
143 EccInterface::isLoggingLimitReached(isReached);
144 controlEDACReport(CLOSE_EDAC_REPORT);
145 // set ECC state
146 EccInterface::state(MemoryECC::ECCStatus::LogFull);
147 }
148}
149
150int ECC::checkCeCount()
151{
152 std::string item = "ce_count";
153 std::string errorMsg = "ECC error(correctable)";
154 int64_t value = 0;
155 std::string fullPath = sysfsRootPath;
156 fullPath.append(item);
157 value = std::stoi(getValue(fullPath));
158 std::vector<uint8_t> eccCeEventData{0x00, 0xff, 0xfe};
159 bool assert = true;
160
161 while (previousCeCounter < value)
162 {
163 previousCeCounter++;
164 // add phosphor-logging log
165 EccInterface::ceCount(previousCeCounter);
166 // add SEL log
167 addSELLog(errorMsg, OBJPATH, eccCeEventData, assert, selBMCGenID);
168 // set ECC state
169 EccInterface::state(MemoryECC::ECCStatus::CE);
170 }
171 return value;
172}
173
174int ECC::checkUeCount()
175{
176 std::string item = "ue_count";
177 std::string errorMsg = "ECC error(uncorrectable)";
178 int64_t value = 0;
179 std::string fullPath = sysfsRootPath;
180 fullPath.append(item);
181 value = std::stoi(getValue(fullPath));
182 std::vector<uint8_t> eccUeEventData{0x01, 0xff, 0xfe};
183 bool assert = true;
184
185 while (previousUeCounter < value)
186 {
187 previousUeCounter++;
188 // add phosphor-logging log
189 EccInterface::ueCount(previousUeCounter);
190 // add SEL log
191 addSELLog(errorMsg, OBJPATH, eccUeEventData, assert, selBMCGenID);
192 // set ECC state
193 EccInterface::state(MemoryECC::ECCStatus::UE);
194 }
195 return value;
196}
197
198void ECC::resetCounter()
199{
200 std::string item = "reset_counters";
201 std::string fullPath = sysfsRootPath;
202 fullPath.append(item);
203 writeValue(fullPath, RESET_COUNT);
204}
205
206void ECC::read()
207{
208 int64_t ceCount = 0;
209 int64_t ueCount = 0;
210 ceCount = checkCeCount();
211 ueCount = checkUeCount();
212 checkEccLogFull(ceCount, ueCount);
213}
214
215void ECC::controlEDACReport(std::string op)
216{
217 writeValue(sysfsEDACReportPath, op);
218}
219
220// get max log from file
221void ECC::getMaxLogValue()
222{
223 maxECCLog = std::stoi(getValue(ECC_FILE));
224}
225
226void ECC::addSELLog(std::string message, std::string path,
227 std::vector<uint8_t> selData, bool assert, uint16_t genId)
228{
229 // sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
230
231 auto selCall = _bus.new_method_call(
232 "xyz.openbmc_project.Logging.IPMI", "/xyz/openbmc_project/Logging/IPMI",
233 "xyz.openbmc_project.Logging.IPMI", "IpmiSelAdd");
234 selCall.append(message, path, selData, assert, genId);
235
236 auto selReply = _bus.call(selCall);
237 if (selReply.is_method_error())
238 {
239 log<level::ERR>("add SEL log error\n");
240 }
241}
242
243} // namespace memory
244} // namespace phosphor