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 
19 
20 using namespace std;
21 using namespace phosphor::logging;
22 extern const ipmi::sensor::InvObjectIDMap invSensors;
23 
24 //////////////////////////
25 struct esel_section_headers_t {
26 	uint8_t sectionid[2];
27 	uint8_t sectionlength[2];
28 	uint8_t version;
29 	uint8_t subsectiontype;
30 	uint8_t compid;
31 };
32 
33 struct severity_values_t {
34 	uint8_t type;
35 	const char *description;
36 };
37 
38 
39 const std::vector<severity_values_t> g_sev_desc = {
40 	{0x10, "recoverable error"},
41 	{0x20, "predictive error"},
42 	{0x40, "unrecoverable error"},
43 	{0x50, "critical error"},
44 	{0x60, "error from a diagnostic test"},
45 	{0x70, "recovered symptom "},
46 	{0xFF, "Unknown"},
47 };
48 
49 const char* sev_lookup(uint8_t n) {
50 	auto i = std::find_if(std::begin(g_sev_desc), std::end(g_sev_desc),
51 	                      [n](auto p){ return p.type == n || p.type == 0xFF; });
52 	return i->description;
53 }
54 
55 
56 
57 
58 int find_sensor_type_string(uint8_t sensor_number, char **s) {
59 
60 	dbus_interface_t a;
61 	const char *p;
62 	int r;
63 
64 	r = find_openbmc_path(sensor_number, &a);
65 
66 	if ((r < 0) || (a.bus[0] == 0)) {
67 		// Just make a generic message for errors that
68 		// occur on sensors that don't exist
69 		r = asprintf(s, "Unknown Sensor (0x%02x)", sensor_number);
70 	} else {
71 
72 		if ((p = strrchr (a.path, '/')) == NULL) {
73 			p = "/Unknown Sensor";
74 		}
75 
76 		*s = strdup(p+1);
77 	}
78 
79 	return 0;
80 }
81 
82 
83 size_t getfilestream(const char *fn, uint8_t **buffer) {
84 
85 	FILE *fp;
86 	ssize_t size = 0;
87 	int r;
88 
89 	if ((fp = fopen(fn, "rb")) != NULL) {
90 
91 		r = fseek(fp, 0, SEEK_END);
92 		if (r) {
93 			fprintf(stderr,"Fseek failed\n");
94 			goto fclose_fp;
95 		}
96 
97 		size = ftell(fp);
98 		if (size == -1L) {
99 			fprintf(stderr,"Ftell failed for %s\n", strerror(errno));
100 			size = 0;
101 			goto fclose_fp;
102 		}
103 
104 		r = fseek(fp, 0, SEEK_SET);
105 		if (r) {
106 			fprintf(stderr,"Fseek failed\n");
107 			size = 0;
108 			goto fclose_fp;
109 		}
110 
111 		*buffer = new uint8_t [size];
112 
113 		r = fread(*buffer, 1, size, fp);
114 		if ( r != size) {
115 			size = 0;
116 			fprintf(stderr,"Fread failed\n");
117 		}
118 
119 fclose_fp:
120 		fclose(fp);
121 	}
122 
123 	return static_cast<size_t>(size);
124 }
125 
126 
127 const char *create_esel_severity(const uint8_t *buffer) {
128 
129 	uint8_t severity;
130 	// Dive in to the IBM log to find the severity
131 	severity = (0xF0  & buffer[0x4A]);
132 
133 	return sev_lookup(severity);
134 }
135 
136 int create_esel_association(const uint8_t *buffer, std::string& inventoryPath)
137 {
138 	ipmi_add_sel_request_t *p;
139 	uint8_t sensor;
140 
141 	p = ( ipmi_add_sel_request_t *) buffer;
142 
143 	sensor = p->sensornumber;
144 
145 	inventoryPath = {};
146 
147     /*
148      * Search the sensor number to inventory path mapping to figure out the
149      * inventory associated with the ESEL.
150      */
151     for (auto const &iter : invSensors)
152     {
153         if (iter.second.sensorID == sensor)
154         {
155             inventoryPath = iter.first;
156             break;
157         }
158     }
159 
160 	return 0;
161 }
162 
163 
164 
165 int create_esel_description(const uint8_t *buffer, const char *sev, char **message) {
166 
167 
168 	ipmi_add_sel_request_t *p;
169 	char *m;
170 	int r;
171 
172 	p =  ( ipmi_add_sel_request_t *) buffer;
173 
174 	find_sensor_type_string(p->sensornumber,&m);
175 
176 	r = asprintf(message, "A %s has experienced a %s", m, sev );
177 	if (r == -1) {
178 		fprintf(stderr,
179 			"Failed to allocate memory for ESEL description\n");
180 	}
181 
182 	free(m);
183 
184 	return 0;
185 }
186 
187 
188 int send_esel_to_dbus(const char *desc,
189                       const char *sev,
190                       const std::string& inventoryPath,
191                       uint8_t *debug,
192                       size_t debuglen)
193 {
194 
195     // Allocate enough space to represent the data in hex separated by spaces,
196     // to mimic how IPMI would display the data.
197     unique_ptr<char[]> selData(new char[(debuglen*3) + 1]());
198     uint32_t i = 0;
199     for(i = 0; i < debuglen; i++)
200     {
201         sprintf(&selData[i*3], "%02x ", 0xFF & ((char*)debug)[i]);
202     }
203     selData[debuglen*3] = '\0';
204 
205     using error =  sdbusplus::org::open_power::Host::Event::Error::Event;
206     using metadata = org::open_power::Host::Event::Event;
207 
208     report<error>(metadata::ESEL(selData.get()),
209                   metadata::CALLOUT_INVENTORY_PATH(inventoryPath.c_str()));
210 
211     return 0;
212 }
213 
214 
215 void send_esel(uint16_t recordid) {
216 	char *desc;
217 	const char *sev;
218 	uint8_t *buffer = NULL;
219 	const char *path = "/tmp/esel";
220 	ssize_t sz;
221 	int r;
222 	std::string inventoryPath;
223 
224 	sz = getfilestream(path, &buffer);
225 	if (sz == 0) {
226 		printf("Error file does not exist %d\n",__LINE__);
227 		return;
228 	}
229 
230 	sev = create_esel_severity(buffer);
231 	create_esel_association(buffer, inventoryPath);
232 	create_esel_description(buffer, sev, &desc);
233 
234 	r = send_esel_to_dbus(desc, sev, inventoryPath, buffer, sz);
235 	if (r < 0) {
236 		fprintf(stderr, "Failed to send esel to dbus\n");
237 	}
238 
239 	free(desc);
240 	delete[] buffer;
241 
242 	return;
243 }
244