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