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