1 #include "elog-errors.hpp"
2 #include "error-HostEvent.hpp"
3 #include "sensorhandler.hpp"
4 #include "storagehandler.hpp"
5 #include "types.hpp"
6 
7 #include <host-ipmid/ipmid-api.h>
8 #include <mapper.h>
9 #include <systemd/sd-bus.h>
10 
11 #include <algorithm>
12 #include <cstdlib>
13 #include <cstring>
14 #include <fstream>
15 #include <iostream>
16 #include <memory>
17 #include <phosphor-logging/elog.hpp>
18 #include <vector>
19 #include <xyz/openbmc_project/Logging/Entry/server.hpp>
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 {
29     uint8_t sectionid[2];
30     uint8_t sectionlength[2];
31     uint8_t version;
32     uint8_t subsectiontype;
33     uint8_t compid;
34 };
35 
36 struct severity_values_t
37 {
38     uint8_t type;
39     Entry::Level level;
40 };
41 
42 const 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 
56 Entry::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 
64 int 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 
93 size_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 
142 Entry::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 
152 int 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 
176 int 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 
198 int 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 
222 void 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 
255 std::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 
275 void 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;
284     std::unique_ptr<char[]> data(
285         new char[(eSELData.size() * byteSeparator) + 1]());
286 
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 
293     using error = sdbusplus::org::open_power::Host::Error::MaintenanceProcedure;
294     using metadata = org::open_power::Host::MaintenanceProcedure;
295 
296     report<error>(metadata::ESEL(data.get()),
297                   metadata::PROCEDURE(static_cast<uint32_t>(procedureNum)));
298 }
299