blob: 1dde2a44878a7f028b68a5580848f239f6416066 [file] [log] [blame]
Patrick Venture0b02be92018-08-31 11:55:55 -07001#include "elog-errors.hpp"
2#include "error-HostEvent.hpp"
Patrick Venture46470a32018-09-07 19:26:25 -07003#include "sensorhandler.hpp"
4#include "storagehandler.hpp"
Patrick Venture0b02be92018-08-31 11:55:55 -07005#include "types.hpp"
6
Patrick Venture46470a32018-09-07 19:26:25 -07007#include <host-ipmid/ipmid-api.h>
Patrick Venture0b02be92018-08-31 11:55:55 -07008#include <mapper.h>
Patrick Venture0b02be92018-08-31 11:55:55 -07009#include <systemd/sd-bus.h>
10
11#include <algorithm>
Chris Austen41a4b312015-10-25 03:45:42 -050012#include <cstdlib>
13#include <cstring>
14#include <fstream>
15#include <iostream>
Chris Austen41a4b312015-10-25 03:45:42 -050016#include <memory>
Saqib Khand33a4af2017-02-20 15:23:27 -060017#include <phosphor-logging/elog.hpp>
Patrick Venture0b02be92018-08-31 11:55:55 -070018#include <vector>
19#include <xyz/openbmc_project/Logging/Entry/server.hpp>
20
Chris Austen41a4b312015-10-25 03:45:42 -050021using namespace std;
Adriana Kobylak2efb3e72017-02-06 21:43:59 -060022using namespace phosphor::logging;
Deepak Kodihalli3d230482018-04-03 07:00:45 -050023using namespace sdbusplus::xyz::openbmc_project::Logging::server;
Tom Josephe19540e2019-02-04 14:06:58 +053024extern const ipmi::sensor::InvObjectIDMap invSensors;
Chris Austen41a4b312015-10-25 03:45:42 -050025
Chris Austen41a4b312015-10-25 03:45:42 -050026//////////////////////////
Patrick Venture0b02be92018-08-31 11:55:55 -070027struct esel_section_headers_t
28{
Nagaraju Goruganti2fa0e702018-04-16 05:25:21 -050029 uint8_t sectionid[2];
30 uint8_t sectionlength[2];
31 uint8_t version;
32 uint8_t subsectiontype;
33 uint8_t compid;
Chris Austen41a4b312015-10-25 03:45:42 -050034};
35
Tom Josephe19540e2019-02-04 14:06:58 +053036struct severity_values_t
37{
38 uint8_t type;
39 Entry::Level level;
40};
41
42const std::vector<severity_values_t> g_sev_desc = {
43 {0x10, Entry::Level::Warning}, // recoverable error
44 {0x20, Entry::Level::Warning}, // predictive error
45 // TODO via github issue 3066 : map level
46 // below to Level::Unrecoverable
47 {0x40, Entry::Level::Error}, // unrecoverable error
48 // TODO via github issue 3066 : map level below
49 // to Level::Critical
50 {0x50, Entry::Level::Error}, // critical error
51 {0x60, Entry::Level::Error}, // error from a diagnostic test
52 {0x70, Entry::Level::Warning}, // recoverable symptom
53 {0xFF, Entry::Level::Error}, // unknown error
54};
55
56Entry::Level sev_lookup(uint8_t n)
57{
58 auto i =
59 std::find_if(std::begin(g_sev_desc), std::end(g_sev_desc),
60 [n](auto p) { return p.type == n || p.type == 0xFF; });
61 return i->level;
62}
63
64int find_sensor_type_string(uint8_t sensor_number, char** s)
65{
66
67 dbus_interface_t a;
68 int r;
69
70 r = find_openbmc_path(sensor_number, &a);
71
72 if ((r < 0) || (a.bus[0] == 0))
73 {
74 // Just make a generic message for errors that
75 // occur on sensors that don't exist
76 r = asprintf(s, "Unknown Sensor (0x%02x)", sensor_number);
77 }
78 else
79 {
80 const char* p;
81
82 if ((p = strrchr(a.path, '/')) == NULL)
83 {
84 p = "/Unknown Sensor";
85 }
86
87 *s = strdup(p + 1);
88 }
89
90 return 0;
91}
92
93size_t getfilestream(const char* fn, uint8_t** buffer)
94{
95
96 FILE* fp;
97 ssize_t size = 0;
98 int r;
99
100 if ((fp = fopen(fn, "rb")) != NULL)
101 {
102
103 r = fseek(fp, 0, SEEK_END);
104 if (r)
105 {
106 log<level::ERR>("Fseek failed");
107 goto fclose_fp;
108 }
109
110 size = ftell(fp);
111 if (size == -1L)
112 {
113 log<level::ERR>("Ftell failed", entry("ERROR=%s", strerror(errno)));
114 size = 0;
115 goto fclose_fp;
116 }
117
118 r = fseek(fp, 0, SEEK_SET);
119 if (r)
120 {
121 log<level::ERR>("Fseek failed");
122 size = 0;
123 goto fclose_fp;
124 }
125
126 *buffer = new uint8_t[size];
127
128 r = fread(*buffer, 1, size, fp);
129 if (r != size)
130 {
131 size = 0;
132 log<level::ERR>("Fread failed\n");
133 }
134
135 fclose_fp:
136 fclose(fp);
137 }
138
139 return static_cast<size_t>(size);
140}
141
142Entry::Level create_esel_severity(const uint8_t* buffer)
143{
144
145 uint8_t severity;
146 // Dive in to the IBM log to find the severity
147 severity = (0xF0 & buffer[0x4A]);
148
149 return sev_lookup(severity);
150}
151
152int create_esel_association(const uint8_t* buffer, std::string& inventoryPath)
153{
154 auto p = reinterpret_cast<const ipmi_add_sel_request_t*>(buffer);
155
156 uint8_t sensor = p->sensornumber;
157
158 inventoryPath = {};
159
160 /*
161 * Search the sensor number to inventory path mapping to figure out the
162 * inventory associated with the ESEL.
163 */
164 auto found = std::find_if(invSensors.begin(), invSensors.end(),
165 [&sensor](const auto& iter) {
166 return (iter.second.sensorID == sensor);
167 });
168 if (found != invSensors.end())
169 {
170 inventoryPath = found->first;
171 }
172
173 return 0;
174}
175
176int create_esel_description(const uint8_t* buffer, Entry::Level level,
177 char** message)
178{
179 char* m;
180 int r;
181
182 auto p = reinterpret_cast<const ipmi_add_sel_request_t*>(buffer);
183
184 find_sensor_type_string(p->sensornumber, &m);
185
186 r = asprintf(message, "A %s has experienced an error of level %d", m,
187 static_cast<uint32_t>(level));
188 if (r == -1)
189 {
190 log<level::ERR>("Failed to allocate memory for ESEL description");
191 }
192
193 free(m);
194
195 return 0;
196}
197
198int send_esel_to_dbus(const char* desc, Entry::Level level,
199 const std::string& inventoryPath, uint8_t* debug,
200 size_t debuglen)
201{
202
203 // Allocate enough space to represent the data in hex separated by spaces,
204 // to mimic how IPMI would display the data.
205 unique_ptr<char[]> selData(new char[(debuglen * 3) + 1]());
206 uint32_t i = 0;
207 for (i = 0; i < debuglen; i++)
208 {
209 sprintf(&selData[i * 3], "%02x ", 0xFF & ((char*)debug)[i]);
210 }
211 selData[debuglen * 3] = '\0';
212
213 using error = sdbusplus::org::open_power::Host::Error::Event;
214 using metadata = org::open_power::Host::Event;
215
216 report<error>(level, metadata::ESEL(selData.get()),
217 metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
218
219 return 0;
220}
221
222void send_esel(uint16_t recordid)
223{
224 char* desc;
225 uint8_t* buffer = NULL;
226 const char* path = "/tmp/esel";
227 ssize_t sz;
228 int r;
229 std::string inventoryPath;
230
231 sz = getfilestream(path, &buffer);
232 if (sz == 0)
233 {
234 log<level::ERR>("Error file does not exist",
235 entry("FILENAME=%s", path));
236 return;
237 }
238
239 auto sev = create_esel_severity(buffer);
240 create_esel_association(buffer, inventoryPath);
241 create_esel_description(buffer, sev, &desc);
242
243 r = send_esel_to_dbus(desc, sev, inventoryPath, buffer, sz);
244 if (r < 0)
245 {
246 log<level::ERR>("Failed to send esel to dbus");
247 }
248
249 free(desc);
250 delete[] buffer;
251
252 return;
253}
254
Tom Josephb647d5b2017-10-31 17:25:33 +0530255std::string readESEL(const char* fileName)
256{
257 std::string content;
258 std::ifstream handle(fileName);
259
260 if (handle.fail())
261 {
262 log<level::ERR>("Failed to open eSEL", entry("FILENAME=%s", fileName));
263 return content;
264 }
265
266 handle.seekg(0, std::ios::end);
267 content.resize(handle.tellg());
268 handle.seekg(0, std::ios::beg);
269 handle.read(&content[0], content.size());
270 handle.close();
271
272 return content;
273}
274
275void createProcedureLogEntry(uint8_t procedureNum)
276{
277 // Read the eSEL data from the file.
278 static constexpr auto eSELFile = "/tmp/esel";
279 auto eSELData = readESEL(eSELFile);
280
281 // Each byte in eSEL is formatted as %02x with a space between bytes and
282 // insert '/0' at the end of the character array.
283 static constexpr auto byteSeparator = 3;
Patrick Venture0b02be92018-08-31 11:55:55 -0700284 std::unique_ptr<char[]> data(
285 new char[(eSELData.size() * byteSeparator) + 1]());
Tom Josephb647d5b2017-10-31 17:25:33 +0530286
287 for (size_t i = 0; i < eSELData.size(); i++)
288 {
289 sprintf(&data[i * byteSeparator], "%02x ", eSELData[i]);
290 }
291 data[eSELData.size() * byteSeparator] = '\0';
292
Patrick Venture0b02be92018-08-31 11:55:55 -0700293 using error = sdbusplus::org::open_power::Host::Error::MaintenanceProcedure;
Tom Josephb647d5b2017-10-31 17:25:33 +0530294 using metadata = org::open_power::Host::MaintenanceProcedure;
295
296 report<error>(metadata::ESEL(data.get()),
297 metadata::PROCEDURE(static_cast<uint32_t>(procedureNum)));
298}