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