xref: /openbmc/phosphor-logging/extensions/openpower-pels/json_utils.cpp (revision 40fb54935ce7367636a7156039396ee91cc4d5e2)
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