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