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 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 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 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 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, 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 */ 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 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