1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright 2019 IBM Corporation
3
4 #include "json_utils.hpp"
5
6 #include "paths.hpp"
7
8 #include <stdio.h>
9
10 #include <nlohmann/json.hpp>
11
12 #include <cstring>
13 #include <filesystem>
14 #include <fstream>
15 #include <optional>
16 #include <string>
17
18 namespace openpower
19 {
20 namespace pels
21 {
22
escapeJSON(const std::string & input)23 std::string escapeJSON(const std::string& input)
24 {
25 std::string output;
26 output.reserve(input.length());
27
28 for (const auto c : input)
29 {
30 switch (c)
31 {
32 case '"':
33 output += "\\\"";
34 break;
35 case '/':
36 output += "\\/";
37 break;
38 case '\b':
39 output += "\\b";
40 break;
41 case '\f':
42 output += "\\f";
43 break;
44 case '\n':
45 output += "\\n";
46 break;
47 case '\r':
48 output += "\\r";
49 break;
50 case '\t':
51 output += "\\t";
52 break;
53 case '\\':
54 output += "\\\\";
55 break;
56 default:
57 output += c;
58 break;
59 }
60 }
61
62 return output;
63 }
dumpHex(const void * data,size_t size,size_t indentCount,bool toJson)64 std::unique_ptr<char[]> dumpHex(const void* data, size_t size,
65 size_t indentCount, bool toJson)
66 {
67 const int symbolSize = 100;
68 std::string jsonIndent(indentLevel * indentCount, 0x20);
69 if (toJson)
70 {
71 jsonIndent.append("\"");
72 }
73 std::unique_ptr<char[]> buffer{new char[std::max(70, 10 * (int)size)]()};
74 char* symbol = (char*)calloc(symbolSize, sizeof(char));
75 char* byteCount = (char*)calloc(11, sizeof(char));
76 char ascii[17];
77 size_t i, j;
78 ascii[16] = '\0';
79 for (i = 0; i < size; ++i)
80 {
81 if (i % 16 == 0)
82 {
83 if (!toJson)
84 {
85 snprintf(byteCount, 11, "%08X ", static_cast<uint32_t>(i));
86 strcat(buffer.get(), byteCount);
87 }
88 strcat(buffer.get(), jsonIndent.c_str());
89 }
90 snprintf(symbol, symbolSize, "%02X ", ((unsigned char*)data)[i]);
91 strcat(buffer.get(), symbol);
92 memset(symbol, 0, strlen(symbol));
93 if (((unsigned char*)data)[i] >= ' ' &&
94 ((unsigned char*)data)[i] <= '~')
95 {
96 ascii[i % 16] = ((unsigned char*)data)[i];
97 }
98 else
99 {
100 ascii[i % 16] = '.';
101 }
102 if ((i + 1) % 8 == 0 || i + 1 == size)
103 {
104 std::string asciiString(ascii);
105 if (toJson)
106 {
107 asciiString = escapeJSON(asciiString);
108 }
109 strcat(buffer.get(), " ");
110 if ((i + 1) % 16 == 0)
111 {
112 if (i + 1 != size && toJson)
113 {
114 snprintf(symbol, symbolSize, "| %s\",\n",
115 asciiString.c_str());
116 }
117 else if (toJson)
118 {
119 snprintf(symbol, symbolSize, "| %s\"\n",
120 asciiString.c_str());
121 }
122 else
123 {
124 snprintf(symbol, symbolSize, "| %s\n",
125 asciiString.c_str());
126 }
127 strcat(buffer.get(), symbol);
128 memset(symbol, 0, strlen(symbol));
129 }
130 else if (i + 1 == size)
131 {
132 ascii[(i + 1) % 16] = '\0';
133 if ((i + 1) % 16 <= 8)
134 {
135 strcat(buffer.get(), " ");
136 }
137 for (j = (i + 1) % 16; j < 16; ++j)
138 {
139 strcat(buffer.get(), " ");
140 }
141 std::string asciiString2(ascii);
142 if (toJson)
143 {
144 asciiString2 = escapeJSON(asciiString2);
145 snprintf(symbol, symbolSize, "| %s\"\n",
146 asciiString2.c_str());
147 }
148 else
149 {
150 snprintf(symbol, symbolSize, "| %s\n",
151 asciiString2.c_str());
152 }
153
154 strcat(buffer.get(), symbol);
155 memset(symbol, 0, strlen(symbol));
156 }
157 }
158 }
159 free(byteCount);
160 free(symbol);
161 return buffer;
162 }
163
jsonInsert(std::string & jsonStr,const std::string & fieldName,const std::string & fieldValue,uint8_t indentCount)164 void jsonInsert(std::string& jsonStr, const std::string& fieldName,
165 const std::string& fieldValue, uint8_t indentCount)
166 {
167 const int8_t spacesToAppend =
168 colAlign - (indentCount * indentLevel) - fieldName.length() - 3;
169 const std::string jsonIndent(indentCount * indentLevel, 0x20);
170 jsonStr.append(jsonIndent + "\"" + fieldName + "\":");
171 if (spacesToAppend >= 0)
172 {
173 jsonStr.append(spacesToAppend, 0x20);
174 }
175 else
176 {
177 jsonStr.append(1, 0x20);
178 }
179 jsonStr.append("\"" + fieldValue + "\",\n");
180 }
181
jsonInsertArray(std::string & jsonStr,const std::string & fieldName,const std::vector<std::string> & values,uint8_t indentCount)182 void jsonInsertArray(std::string& jsonStr, const std::string& fieldName,
183 const std::vector<std::string>& values,
184 uint8_t indentCount)
185 {
186 const std::string jsonIndent(indentCount * indentLevel, 0x20);
187 if (!values.empty())
188 {
189 jsonStr.append(jsonIndent + "\"" + fieldName + "\": [\n");
190 for (size_t i = 0; i < values.size(); i++)
191 {
192 jsonStr.append(colAlign, 0x20);
193 if (i == values.size() - 1)
194 {
195 jsonStr.append("\"" + values[i] + "\"\n");
196 }
197 else
198 {
199 jsonStr.append("\"" + values[i] + "\",\n");
200 }
201 }
202 jsonStr.append(jsonIndent + "],\n");
203 }
204 else
205 {
206 const int8_t spacesToAppend =
207 colAlign - (indentCount * indentLevel) - fieldName.length() - 3;
208 jsonStr.append(jsonIndent + "\"" + fieldName + "\":");
209 if (spacesToAppend > 0)
210 {
211 jsonStr.append(spacesToAppend, 0x20);
212 }
213 else
214 {
215 jsonStr.append(1, 0x20);
216 }
217 jsonStr.append("[],\n");
218 }
219 }
220
trimEnd(std::string s)221 std::string trimEnd(std::string s)
222 {
223 const char* t = " \t\n\r\f\v";
224 if (s.find_last_not_of(t) != std::string::npos)
225 {
226 s.erase(s.find_last_not_of(t) + 1);
227 }
228 return s;
229 }
230
231 /**
232 * @brief Lookup the component ID in a JSON file named
233 * after the creator ID.
234 *
235 * Keeps a cache of the JSON it reads to live throughout
236 * the peltool call as the JSON can be reused across
237 * PEL sections or even across PELs.
238 *
239 * @param[in] compID - The component ID
240 * @param[in] creatorID - The creator ID for the PEL
241 * @return optional<string> - The comp name, or std::nullopt
242 */
lookupComponentName(uint16_t compID,char creatorID)243 static std::optional<std::string> lookupComponentName(uint16_t compID,
244 char creatorID)
245 {
246 static std::map<char, nlohmann::json> jsonCache;
247 nlohmann::json jsonData;
248 nlohmann::json* jsonPtr = &jsonData;
249 std::filesystem::path filename{
250 std::string{creatorID} + "_component_ids.json"};
251 filename = getPELReadOnlyDataPath() / filename;
252
253 auto jsonIt = jsonCache.find(creatorID);
254 if (jsonIt != jsonCache.end())
255 {
256 jsonPtr = &(jsonIt->second);
257 }
258 else
259 {
260 std::error_code ec;
261 if (!std::filesystem::exists(filename, ec))
262 {
263 return std::nullopt;
264 }
265
266 std::ifstream file{filename};
267 if (!file)
268 {
269 return std::nullopt;
270 }
271
272 jsonData = nlohmann::json::parse(file, nullptr, false);
273 if (jsonData.is_discarded())
274 {
275 return std::nullopt;
276 }
277
278 jsonCache.emplace(creatorID, jsonData);
279 }
280
281 auto id = getNumberString("%04X", compID);
282
283 auto it = jsonPtr->find(id);
284 if (it == jsonPtr->end())
285 {
286 return std::nullopt;
287 }
288
289 return it->get<std::string>();
290 }
291
292 /**
293 * @brief Convert the component ID to a 2 character string
294 * if both bytes are nonzero
295 *
296 * e.g. 0x4552 -> "ER"
297 *
298 * @param[in] compID - The component ID
299 * @return optional<string> - The two character string, or std::nullopt.
300 */
convertCompIDToChars(uint16_t compID)301 static std::optional<std::string> convertCompIDToChars(uint16_t compID)
302 {
303 uint8_t first = (compID >> 8) & 0xFF;
304 uint8_t second = compID & 0xFF;
305 if ((first != 0) && (second != 0))
306 {
307 std::string id{static_cast<char>(first)};
308 id += static_cast<char>(second);
309 return id;
310 }
311
312 return std::nullopt;
313 }
314
getComponentName(uint16_t compID,uint8_t creatorID)315 std::string getComponentName(uint16_t compID, uint8_t creatorID)
316 {
317 // See if there's a JSON file with the names
318 auto name = lookupComponentName(compID, creatorID);
319
320 // If PHYP, convert to ASCII
321 if (!name && ('H' == creatorID))
322 {
323 name = convertCompIDToChars(compID);
324 }
325
326 if (!name)
327 {
328 name = getNumberString("0x%04X", compID);
329 }
330
331 return *name;
332 }
333
334 } // namespace pels
335 } // namespace openpower
336