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 = colAlign - (indentCount * indentLevel) -
180                                   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 = colAlign - (indentCount * indentLevel) -
219                                       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  */
lookupComponentName(uint16_t compID,char creatorID)255 static std::optional<std::string> lookupComponentName(uint16_t compID,
256                                                       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{std::string{creatorID} +
262                                    "_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