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